Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(elements|ino-list-item): moved logic from ino-control-item to ino-list-item #1154

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/elements/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -4245,6 +4248,7 @@ declare module "@stencil/core" {
*/
"ino-chip": LocalJSX.InoChip & JSXBase.HTMLAttributes<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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -24,8 +29,6 @@ import {
shadow: false,
})
export class InoControlItem implements ComponentInterface {
private inputEl: HTMLInputElement;

@Element() el!: HTMLInoControlItemElement;

/**
Expand Down Expand Up @@ -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<boolean>) => {
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<void> {
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 (
Expand All @@ -149,17 +170,16 @@ export class InoControlItem implements ComponentInterface {
activated={this.activated}
selected={this.selected}
disabled={this.disabled}
onClick={this.clickHandler}
>
{this.role === 'checkbox' ? (
{this.roleIntern === 'checkbox' ? (
<ino-checkbox
{...controlItemProps}
indeterminate={this.indeterminate}
/>
) : (
<ino-radio {...controlItemProps} />
)}
<span slot={slotPosition}>
<span slot={this.defaultSlot}>
<slot></slot>
</span>
</ino-list-item>
Expand Down
3 changes: 2 additions & 1 deletion packages/elements/src/components/ino-control-item/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
<!-- Auto Generated Below -->


## 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

Expand Down
90 changes: 73 additions & 17 deletions packages/elements/src/components/ino-list-item/ino-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we need this logic? For which use cases?

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<string, boolean> = {
'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 || <slot name="primary" />;
const secondaryContent =
this.secondaryText ||
(secondarySlotHasContent ? <slot name="secondary" /> : null);
(this.hasSecondarySlot() ? <slot name="secondary" /> : null);

const getLeadingSlot = () =>
this.hasLeadingSlot() ? 'leading' : undefined;

return (
<Host>
Expand All @@ -126,9 +182,9 @@ export class ListItem implements ComponentInterface {
{...this.attrs}
>
<span class="mdc-deprecated-list-item__ripple"></span>
{leadingSlotHasContent && (
{this.showLeadingSlot() && (
<span class="mdc-deprecated-list-item__graphic" role="presentation">
<slot name="leading" />
<slot name={getLeadingSlot()} />
</span>
)}
<span class="mdc-deprecated-list-item__text">
Expand All @@ -143,7 +199,7 @@ export class ListItem implements ComponentInterface {
]
: primaryContent}
</span>
{trailingSlotHasContent && (
{this.hasTrailingSlot() && (
<span class="mdc-deprecated-list-item__meta">
<slot name="trailing" />
</span>
Expand Down
12 changes: 6 additions & 6 deletions packages/elements/src/components/ino-list-item/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading