diff --git a/packages/elements/src/components.d.ts b/packages/elements/src/components.d.ts index 44630b7302..4655ff2c9c 100644 --- a/packages/elements/src/components.d.ts +++ b/packages/elements/src/components.d.ts @@ -262,6 +262,7 @@ export namespace Components { "value"?: string; } /** + * @deprecated Use the component `ino-list-item` together with the component `ino-checkbox` or `ino-radio` instead. * A list item component that displays a single instance of choice in a list or menu with a control element (radio-button or checkbox). It functions as a wrapper around the material [list item](https://github.com/material-components/material-components-web/blob/master/packages/mdc-list/) capabilities. * This component is used as child of `ino-list` and `ino-menu` components. * #### Restrictions @@ -1892,6 +1893,7 @@ declare global { new (): HTMLInoChipElement; }; /** + * @deprecated Use the component `ino-list-item` together with the component `ino-checkbox` or `ino-radio` instead. * A list item component that displays a single instance of choice in a list or menu with a control element (radio-button or checkbox). It functions as a wrapper around the material [list item](https://github.com/material-components/material-components-web/blob/master/packages/mdc-list/) capabilities. * This component is used as child of `ino-list` and `ino-menu` components. * #### Restrictions @@ -2646,6 +2648,7 @@ declare namespace LocalJSX { "value"?: string; } /** + * @deprecated Use the component `ino-list-item` together with the component `ino-checkbox` or `ino-radio` instead. * A list item component that displays a single instance of choice in a list or menu with a control element (radio-button or checkbox). It functions as a wrapper around the material [list item](https://github.com/material-components/material-components-web/blob/master/packages/mdc-list/) capabilities. * This component is used as child of `ino-list` and `ino-menu` components. * #### Restrictions @@ -4245,6 +4248,7 @@ declare module "@stencil/core" { */ "ino-chip": LocalJSX.InoChip & JSXBase.HTMLAttributes; /** + * @deprecated Use the component `ino-list-item` together with the component `ino-checkbox` or `ino-radio` instead. * A list item component that displays a single instance of choice in a list or menu with a control element (radio-button or checkbox). It functions as a wrapper around the material [list item](https://github.com/material-components/material-components-web/blob/master/packages/mdc-list/) capabilities. * This component is used as child of `ino-list` and `ino-menu` components. * #### Restrictions diff --git a/packages/elements/src/components/ino-control-item/ino-control-item.tsx b/packages/elements/src/components/ino-control-item/ino-control-item.tsx index 4c152bcf9d..0a76a76478 100644 --- a/packages/elements/src/components/ino-control-item/ino-control-item.tsx +++ b/packages/elements/src/components/ino-control-item/ino-control-item.tsx @@ -6,13 +6,18 @@ import { EventEmitter, Host, Prop, + State, + Watch, h, } from '@stencil/core'; /** + * @deprecated Use the component `ino-list-item` together with the component `ino-checkbox` or `ino-radio` instead. + * * A list item component that displays a single instance of choice in a list or menu with a control element (radio-button or checkbox). It functions as a wrapper around the material [list item](https://github.com/material-components/material-components-web/blob/master/packages/mdc-list/) capabilities. * * This component is used as child of `ino-list` and `ino-menu` components. + * * #### Restrictions * Please note that only text is supported as a trailing element. However, your icons can be placed at the leading position. To do so, use the `trailing`-Property and declare your icon inside of the element * @@ -24,8 +29,6 @@ import { shadow: false, }) export class InoControlItem implements ComponentInterface { - private inputEl: HTMLInputElement; - @Element() el!: HTMLInoControlItemElement; /** @@ -99,46 +102,64 @@ export class InoControlItem implements ComponentInterface { */ @Event() checkedChange!: EventEmitter; + @State() controlSlot: 'leading' | 'trailing' = 'leading'; + + @State() defaultSlot: 'leading' | 'trailing' | undefined = 'trailing'; + + @State() roleIntern: string; + + @Watch('trailing') + onTrailingChanged(): void { + this.updateSlots(); + } + + @Watch('role') + onRoleChanged(): void { + if (this.role !== null) { + this.updateRole(); + } + } + + private updateSlots(): void { + this.controlSlot = this.trailing ? 'trailing' : 'leading'; + this.defaultSlot = + this.el.children.length > 0 + ? this.trailing + ? 'leading' + : 'trailing' + : undefined; + } + + private updateRole(): void { + this.roleIntern = this.role; + this.el.removeAttribute('role'); + } + private changedHandler = (e: CustomEvent) => { e.stopPropagation(); this.checkedChange.emit(e.detail); }; - private clickHandler = (e: MouseEvent) => { - e.stopPropagation(); - if (this.disabled || e.target['tagName'] === 'INO-CHECKBOX') { - return; - } - - this.inputEl.shadowRoot - .querySelector('input') - .dispatchEvent(new CustomEvent('input')); - }; + componentWillLoad(): void | Promise { + this.updateSlots(); + this.updateRole(); + } componentDidLoad() { - if (!['checkbox', 'radio'].includes(this.role)) + if (!['checkbox', 'radio'].includes(this.roleIntern)) console.warn( - `Given role ${this.role}is not valid, fallbacks to role radio`, + `Given role ${this.roleIntern} is not valid, fallbacks to role radio`, ); } render() { - const controlItemPosition = this.trailing ? 'trailing' : 'leading'; - const slotPosition = - this.el.children.length > 0 - ? this.trailing - ? 'leading' - : 'trailing' - : ''; - const controlItemProps = { - slot: controlItemPosition, + slot: this.controlSlot, checked: this.checked, disabled: this.disabled, name: this.name, value: this.value, onCheckedChange: this.changedHandler, - ref: (inputEl) => (this.inputEl = inputEl), }; return ( @@ -149,9 +170,8 @@ export class InoControlItem implements ComponentInterface { activated={this.activated} selected={this.selected} disabled={this.disabled} - onClick={this.clickHandler} > - {this.role === 'checkbox' ? ( + {this.roleIntern === 'checkbox' ? ( )} - + diff --git a/packages/elements/src/components/ino-control-item/readme.md b/packages/elements/src/components/ino-control-item/readme.md index 1ba7e85b22..cb3e86e392 100644 --- a/packages/elements/src/components/ino-control-item/readme.md +++ b/packages/elements/src/components/ino-control-item/readme.md @@ -4,11 +4,12 @@ -## Overview +> **[DEPRECATED]** Use the component `ino-list-item` together with the component `ino-checkbox` or `ino-radio` instead. A list item component that displays a single instance of choice in a list or menu with a control element (radio-button or checkbox). It functions as a wrapper around the material [list item](https://github.com/material-components/material-components-web/blob/master/packages/mdc-list/) capabilities. This component is used as child of `ino-list` and `ino-menu` components. + #### Restrictions Please note that only text is supported as a trailing element. However, your icons can be placed at the leading position. To do so, use the `trailing`-Property and declare your icon inside of the element diff --git a/packages/elements/src/components/ino-list-item/ino-list-item.tsx b/packages/elements/src/components/ino-list-item/ino-list-item.tsx index 74c05ad1e4..95dd4b35f0 100644 --- a/packages/elements/src/components/ino-list-item/ino-list-item.tsx +++ b/packages/elements/src/components/ino-list-item/ino-list-item.tsx @@ -9,16 +9,22 @@ import { Listen, Prop, } from '@stencil/core'; -import classNames from 'classnames'; import { hasSlotContent } from '../../util/component-utils'; import { MDCRipple } from '@material/ripple'; +enum ListItemSlot { + PRIMARY = 'primary', + SECONDARY = 'secondary', + LEADING = 'leading', + TRAILING = 'trailing', +} + /** * A list item component that displays a single instance of choice in a list or menu. It functions as a wrapper around the material [list item](https://github.com/material-components/material-components-web/blob/master/packages/mdc-list/) capabilities. * * This component is used as child of `ino-list` and `ino-menu` components. * - * @slot leading - For the element to be prepended + * @slot leading - For the element to be prepended. It's also the fallback for the default slot. * @slot trailing - For the element to be appended * @slot primary - For the (text) element * @slot secondary - For the secondary text element in a two-lined list @@ -86,36 +92,86 @@ export class ListItem implements ComponentInterface { this.mdcRipple?.destroy(); } - @Listen('keydown') + componentWillRender(): void { + if (this.text && this.hasPrimarySlot()) { + console.warn( + '[ino-list-item] primary slot is used together with the property "text".', + ); + } + if (this.secondaryText && this.hasSecondarySlot()) { + console.warn( + '[ino-list-item] secondary slot is used together with the property "secondary-text".', + ); + } + } + + @Listen('keydown', {}) handleKeyDown(ev: KeyboardEvent) { if (!this.disabled && (ev.code === 'Enter' || ev.code === 'Space')) { this.clickEl.emit(this.el); } } - @Listen('click') - clickHandler() { - if (!this.disabled) { - this.clickEl.emit(this.el); + @Listen('click', {}) + clickHandler(e: MouseEvent) { + if (this.disabled) return; + this.clickEl.emit(this.el); + this.dispatchInputEvent(e); + } + + private dispatchInputEvent(e: MouseEvent): void { + const checkbox = this.el.querySelector('ino-checkbox'); + const radio = this.el.querySelector('ino-radio'); + if (checkbox == null && radio == null) { + return; } + const skip = ['INO-CHECKBOX', 'INO-RADIO'].includes(e.target['tagName']); + if (skip) return; + const ctrl = checkbox || radio; + ctrl.shadowRoot + .querySelector('input') + .dispatchEvent(new CustomEvent('input')); } - render() { - const leadingSlotHasContent = hasSlotContent(this.el, 'leading'); - const trailingSlotHasContent = hasSlotContent(this.el, 'trailing'); - const secondarySlotHasContent = hasSlotContent(this.el, 'secondary'); + private hasDefaultSlot(): boolean { + return this.el.children?.length > 0; + } - const listItemClasses = classNames({ + private hasLeadingSlot(): boolean { + return hasSlotContent(this.el, ListItemSlot.LEADING); + } + + private showLeadingSlot(): boolean { + return this.hasLeadingSlot() || this.hasDefaultSlot(); + } + + private hasTrailingSlot(): boolean { + return hasSlotContent(this.el, ListItemSlot.TRAILING); + } + + private hasPrimarySlot(): boolean { + return hasSlotContent(this.el, ListItemSlot.PRIMARY); + } + + private hasSecondarySlot(): boolean { + return hasSlotContent(this.el, ListItemSlot.SECONDARY); + } + + render() { + const listItemClasses: Record = { 'mdc-deprecated-list-item': true, 'mdc-deprecated-list-item--selected': this.selected, 'mdc-deprecated-list-item--activated': this.activated, 'mdc-deprecated-list-item--disabled': this.disabled, - }); + }; const primaryContent = this.text || ; const secondaryContent = this.secondaryText || - (secondarySlotHasContent ? : null); + (this.hasSecondarySlot() ? : null); + + const getLeadingSlot = () => + this.hasLeadingSlot() ? 'leading' : undefined; return ( @@ -126,9 +182,9 @@ export class ListItem implements ComponentInterface { {...this.attrs} > - {leadingSlotHasContent && ( + {this.showLeadingSlot() && ( - + )} @@ -143,7 +199,7 @@ export class ListItem implements ComponentInterface { ] : primaryContent} - {trailingSlotHasContent && ( + {this.hasTrailingSlot() && ( diff --git a/packages/elements/src/components/ino-list-item/readme.md b/packages/elements/src/components/ino-list-item/readme.md index edfddf5ee7..1f8258c442 100644 --- a/packages/elements/src/components/ino-list-item/readme.md +++ b/packages/elements/src/components/ino-list-item/readme.md @@ -31,12 +31,12 @@ This component is used as child of `ino-list` and `ino-menu` components. ## Slots -| Slot | Description | -| ------------- | -------------------------------------------------- | -| `"leading"` | For the element to be prepended | -| `"primary"` | For the (text) element | -| `"secondary"` | For the secondary text element in a two-lined list | -| `"trailing"` | For the element to be appended | +| Slot | Description | +| ------------- | ----------------------------------------------------------------------------- | +| `"leading"` | For the element to be prepended. It's also the fallback for the default slot. | +| `"primary"` | For the (text) element | +| `"secondary"` | For the secondary text element in a two-lined list | +| `"trailing"` | For the element to be appended | ## CSS Custom Properties diff --git a/packages/landingpage/components/home/component-sample/component-sample.tsx b/packages/landingpage/components/home/component-sample/component-sample.tsx index 1e495726ed..29cdd97f00 100644 --- a/packages/landingpage/components/home/component-sample/component-sample.tsx +++ b/packages/landingpage/components/home/component-sample/component-sample.tsx @@ -2,10 +2,10 @@ import styles from './component-sample.module.scss'; import { InoButton, InoChip, - InoControlItem, InoIcon, InoInput, InoList, + InoListItem, InoRange, InoSegmentButton, InoSegmentGroup, @@ -18,6 +18,7 @@ import Link from 'next/link'; import { MainRoutes } from 'utils/routes'; import useTranslation from 'utils/hooks/useTranslation'; import classNames from 'classnames'; +import { InoCheckbox, InoRadio } from '@inovex.de/elements-react'; enum ChipValues { REACT = 'React', @@ -118,18 +119,24 @@ export default function ComponentSample() { componentCategory="Structure" > - setCheckboxValue(value.detail)} - > - setRadioboxValue(value.detail)} - > + + setCheckboxValue(value.detail)} + > + Checkbox + + + + setRadioboxValue(value.detail)} + > + Radio Button + + diff --git a/packages/storybook/elements-stencil-docs.json b/packages/storybook/elements-stencil-docs.json index fbf2b4d754..b7e25562d7 100644 --- a/packages/storybook/elements-stencil-docs.json +++ b/packages/storybook/elements-stencil-docs.json @@ -1618,10 +1618,14 @@ "fileName": "ino-control-item.tsx", "tag": "ino-control-item", "readme": "# ino-control-item\n\n", - "overview": "A list item component that displays a single instance of choice in a list or menu with a control element (radio-button or checkbox). It functions as a wrapper around the material [list item](https://github.com/material-components/material-components-web/blob/master/packages/mdc-list/) capabilities.\n\nThis component is used as child of `ino-list` and `ino-menu` components.\n#### Restrictions\nPlease note that only text is supported as a trailing element. However, your icons can be placed at the leading position. To do so, use the `trailing`-Property and declare your icon inside of the element", + "overview": "", "usage": {}, - "docs": "A list item component that displays a single instance of choice in a list or menu with a control element (radio-button or checkbox). It functions as a wrapper around the material [list item](https://github.com/material-components/material-components-web/blob/master/packages/mdc-list/) capabilities.\n\nThis component is used as child of `ino-list` and `ino-menu` components.\n#### Restrictions\nPlease note that only text is supported as a trailing element. However, your icons can be placed at the leading position. To do so, use the `trailing`-Property and declare your icon inside of the element", + "docs": "", "docsTags": [ + { + "name": "deprecated", + "text": "Use the component `ino-list-item` together with the component `ino-checkbox` or `ino-radio` instead.\n\nA list item component that displays a single instance of choice in a list or menu with a control element (radio-button or checkbox). It functions as a wrapper around the material [list item](https://github.com/material-components/material-components-web/blob/master/packages/mdc-list/) capabilities.\n\nThis component is used as child of `ino-list` and `ino-menu` components.\n\n#### Restrictions\nPlease note that only text is supported as a trailing element. However, your icons can be placed at the leading position. To do so, use the `trailing`-Property and declare your icon inside of the element" + }, { "name": "slot", "text": "default - Any element" @@ -1641,6 +1645,7 @@ "ino-radio" ] }, + "deprecation": "Use the component `ino-list-item` together with the component `ino-checkbox` or `ino-radio` instead.\n\nA list item component that displays a single instance of choice in a list or menu with a control element (radio-button or checkbox). It functions as a wrapper around the material [list item](https://github.com/material-components/material-components-web/blob/master/packages/mdc-list/) capabilities.\n\nThis component is used as child of `ino-list` and `ino-menu` components.\n\n#### Restrictions\nPlease note that only text is supported as a trailing element. However, your icons can be placed at the leading position. To do so, use the `trailing`-Property and declare your icon inside of the element", "props": [ { "name": "activated", @@ -5977,7 +5982,7 @@ "docsTags": [ { "name": "slot", - "text": "leading - For the element to be prepended" + "text": "leading - For the element to be prepended. It's also the fallback for the default slot." }, { "name": "slot", @@ -6214,7 +6219,7 @@ "slots": [ { "name": "leading", - "docs": "For the element to be prepended" + "docs": "For the element to be prepended. It's also the fallback for the default slot." }, { "name": "primary", diff --git a/packages/storybook/src/stories/ino-list-item/ino-list-item.stories.ts b/packages/storybook/src/stories/ino-list-item/ino-list-item.stories.ts index 90f0664615..db1971b85a 100644 --- a/packages/storybook/src/stories/ino-list-item/ino-list-item.stories.ts +++ b/packages/storybook/src/stories/ino-list-item/ino-list-item.stories.ts @@ -47,12 +47,14 @@ export default { }, } as Meta; -const exampleImg = html` `; +const exampleImg = html` + +`; const template = new TemplateGenerator( 'ino-list-item', @@ -87,6 +89,7 @@ type InoListVariants = Components.InoList & { meta: boolean; checkbox: boolean; radio: boolean; + trailing: boolean; }; const templateGraphicAndMeta = new TemplateGenerator( @@ -95,9 +98,9 @@ const templateGraphicAndMeta = new TemplateGenerator( ${args.avatar && exampleImg} - ${args.checkbox && html` `} - ${args.meta && html`

$10.00

`} - ${args.radio && html` `} + ${args.checkbox && html` `} + ${args.meta && html`

$10.00

`} + ${args.radio && html` `}
`, @@ -119,10 +122,10 @@ MetaData.args = { meta: true }; * You can use a `ino-checkbox` element in the `leading` or `trailing` slot. */ export const WithCheckbox = templateGraphicAndMeta.generatePlaygroundStory(); -WithCheckbox.args = { checkbox: true }; +WithCheckbox.args = { checkbox: true, trailing: true }; /** - * You can use a `ino-checkbox` element in the `leading` or `trailing` slot. + * You can use a `ino-radio` element in the `leading` or `trailing` slot. */ export const WithRadio = templateGraphicAndMeta.generatePlaygroundStory(); WithRadio.args = { radio: true };