diff --git a/etc/lime-elements.api.md b/etc/lime-elements.api.md index 1c496524ed..93b860ff10 100644 --- a/etc/lime-elements.api.md +++ b/etc/lime-elements.api.md @@ -645,6 +645,7 @@ export namespace Components { "commandText"?: string; "disabled": boolean; "hotkey"?: string; + "primaryComponent"?: ListComponent; "showChevron": boolean; } // @internal (undocumented) @@ -2854,6 +2855,7 @@ export namespace JSX { "commandText"?: string; "disabled"?: boolean; "hotkey"?: string; + "primaryComponent"?: ListComponent; "showChevron"?: boolean; } @@ -4191,6 +4193,7 @@ interface MenuItem { items?: Array | ListSeparator> | MenuLoader; // @internal parentItem?: MenuItem; + primaryComponent?: ListComponent; secondaryText?: string; selected?: boolean; text: string; diff --git a/src/components/list-item/list-item.scss b/src/components/list-item/list-item.scss index f2bb3a4f92..61241c31bd 100644 --- a/src/components/list-item/list-item.scss +++ b/src/components/list-item/list-item.scss @@ -67,7 +67,8 @@ limel-list-item[disabled]:not([disabled='false']) { .text, limel-icon, img, - .boolean-input { + .boolean-input, + limel-menu-item-meta { opacity: 0.4; } } diff --git a/src/components/list-item/list-item.tsx b/src/components/list-item/list-item.tsx index 0a23d67979..2219810515 100644 --- a/src/components/list-item/list-item.tsx +++ b/src/components/list-item/list-item.tsx @@ -252,7 +252,7 @@ export class ListItemComponent implements ListItem { const PrimaryComponent: any = primary.name; const props = primary.props || {}; - return ; + return ; }; private renderImage = () => { diff --git a/src/components/list-item/menu-item-meta/menu-item-meta.tsx b/src/components/list-item/menu-item-meta/menu-item-meta.tsx index 1711137d23..4c5f220fe6 100644 --- a/src/components/list-item/menu-item-meta/menu-item-meta.tsx +++ b/src/components/list-item/menu-item-meta/menu-item-meta.tsx @@ -1,5 +1,6 @@ import { Component, Host, Prop, h } from '@stencil/core'; import { normalizeHotkeyString } from '../../../util/hotkeys'; +import { ListComponent } from '../list-item.types'; /** * Meta content for menu list items @@ -47,9 +48,16 @@ export class MenuItemMeta { @Prop() public showChevron = false; + /** + * Optional primary component to render before other meta content + */ + @Prop() + public primaryComponent?: ListComponent; + public render() { return ( + {this.renderPrimaryComponent()} {this.renderCommandText()} {this.renderBadge()} {this.renderChevron()} @@ -80,6 +88,18 @@ export class MenuItemMeta { return ; } + private renderPrimaryComponent() { + const primary = this.primaryComponent; + if (!primary?.name) { + return; + } + + const PrimaryComponent: any = primary.name; + const props = primary.props || {}; + + return ; + } + private renderChevron() { if (!this.showChevron) { return; diff --git a/src/components/menu-list/menu-list-renderer.tsx b/src/components/menu-list/menu-list-renderer.tsx index 8be59c253e..d071949599 100644 --- a/src/components/menu-list/menu-list-renderer.tsx +++ b/src/components/menu-list/menu-list-renderer.tsx @@ -111,18 +111,20 @@ export class MenuListRenderer { !!item.hotkey || !!item.commandText; - const primaryComponent = hasMeta - ? { - name: 'limel-menu-item-meta', - props: { - commandText: item.commandText, - hotkey: item.hotkey, - disabled: !!item.disabled, - badge: item.badge, - showChevron: hasSubMenu, - }, - } - : undefined; + const primaryComponent = + hasMeta || item.primaryComponent + ? { + name: 'limel-menu-item-meta', + props: { + commandText: item.commandText, + hotkey: item.hotkey, + disabled: !!item.disabled, + badge: item.badge, + showChevron: hasSubMenu, + primaryComponent: item.primaryComponent, + }, + } + : undefined; const key = (item as any).id ?? `item-${index}`; const classNames = { diff --git a/src/components/menu/examples/menu-primary-component.tsx b/src/components/menu/examples/menu-primary-component.tsx new file mode 100644 index 0000000000..6437b2970f --- /dev/null +++ b/src/components/menu/examples/menu-primary-component.tsx @@ -0,0 +1,77 @@ +import { LimelMenuCustomEvent, MenuItem } from '@limetech/lime-elements'; +import { Component, h, State } from '@stencil/core'; + +/** + * With a primary component + * + * Menu items can render a custom primary component using the + * `primaryComponent` prop, just like list items. + * + * The component is rendered before the item's text. + */ +@Component({ + tag: 'limel-example-menu-primary-component', + shadow: true, +}) +export class MenuPrimaryComponentExample { + @State() + private lastSelectedItem: string; + + private items: MenuItem[] = [ + { + text: 'Upload file', + secondaryText: 'Completed', + primaryComponent: { + name: 'limel-circular-progress', + props: { + value: 10, + maxValue: 10, + suffix: '%', + displayPercentageColors: true, + size: 'small', + }, + }, + }, + { + text: 'Sync contacts', + secondaryText: 'In progress…', + primaryComponent: { + name: 'limel-circular-progress', + props: { + value: 4, + maxValue: 10, + suffix: '%', + displayPercentageColors: true, + size: 'small', + }, + }, + }, + { + text: 'Generate report', + secondaryText: 'Just started', + primaryComponent: { + name: 'limel-circular-progress', + props: { + value: 1, + maxValue: 10, + suffix: '%', + displayPercentageColors: true, + size: 'small', + }, + }, + }, + ]; + + public render() { + return [ + + + , + , + ]; + } + + private handleSelect = (event: LimelMenuCustomEvent) => { + this.lastSelectedItem = event.detail.text; + }; +} diff --git a/src/components/menu/menu.tsx b/src/components/menu/menu.tsx index d644e0e0ef..9fee5356fa 100644 --- a/src/components/menu/menu.tsx +++ b/src/components/menu/menu.tsx @@ -60,6 +60,7 @@ const DEFAULT_ROOT_BREADCRUMBS_ITEM: BreadcrumbsItem = { * @exampleComponent limel-example-menu-surface-width * @exampleComponent limel-example-menu-separators * @exampleComponent limel-example-menu-icons + * @exampleComponent limel-example-menu-primary-component * @exampleComponent limel-example-menu-badge-icons * @exampleComponent limel-example-menu-grid * @exampleComponent limel-example-menu-secondary-text diff --git a/src/components/menu/menu.types.ts b/src/components/menu/menu.types.ts index 86b73244fb..b2162cd8ba 100644 --- a/src/components/menu/menu.types.ts +++ b/src/components/menu/menu.types.ts @@ -1,6 +1,7 @@ import { ListSeparator } from '../../global/shared-types/separator.types'; import { Icon } from '../../global/shared-types/icon.types'; import { Color } from '../../global/shared-types/color.types'; +import { ListComponent } from '../list-item/list-item.types'; /** * The direction in which the menu should open. @@ -121,6 +122,16 @@ export interface MenuItem { */ badge?: number | string; + /** + * Component used to render additional content in the menu item. + * + * When the menu item is disabled, the `disabled` prop is automatically + * propagated to the primary component. + * + * See `limel-example-menu-primary-component` for usage. + */ + primaryComponent?: ListComponent; + /** * Value of the menu item. */