@trimble-oss/moduswebcomponents
Version:
Modus Web Components is a modern, accessible UI library built with Stencil JS that provides reusable web components following Trimble's Modus design system. This updated version focuses on improved flexibility, enhanced theming options, comprehensive cust
376 lines (370 loc) • 11.5 kB
JavaScript
import { withActions } from "@storybook/addon-actions/decorator";
import { html } from "lit";
import { ifDefined } from "lit/directives/if-defined.js";
import { ref } from "lit/directives/ref.js";
import { createShadowHostClass } from "../../providers/shadow-dom/shadow-host-helper";
const meta = {
title: 'Components/Menu',
component: 'modus-wc-menu',
args: {
orientation: 'vertical',
'selection-mode': 'single',
size: 'md',
},
argTypes: {
orientation: {
control: { type: 'select' },
options: ['horizontal', 'vertical'],
},
'selection-mode': {
control: { type: 'select' },
options: ['single', 'multiple'],
},
size: {
control: { type: 'select' },
options: ['xs', 'sm', 'md', 'lg'],
},
},
decorators: [withActions],
parameters: {
actions: {
handles: ['menuFocusout', 'menuSelectionChange', 'itemSelect'],
},
},
};
export default meta;
export const Default = {
render: (args) => {
// prettier-ignore
return html `
<modus-wc-menu
aria-label="Menu"
?bordered=${args.bordered}
custom-class=${ifDefined(args['custom-class'])}
orientation=${ifDefined(args.orientation)}
selection-mode=${ifDefined(args['selection-mode'])}
size=${ifDefined(args.size)}
>
<modus-wc-menu-item
label="Small"
value="1"
size="sm"
></modus-wc-menu-item>
<modus-wc-menu-item label="Medium" value="2"></modus-wc-menu-item>
<modus-wc-menu-item
label="Large"
value="3"
size="lg"
></modus-wc-menu-item>
<modus-wc-menu-item
label="Bordered"
value="3"
bordered="true"
></modus-wc-menu-item>
<modus-wc-menu-item
label="With Sub-label"
value="3"
sub-label="Sub-label"
></modus-wc-menu-item>
<modus-wc-menu-item
label="Selected"
value="3"
selected="true"
></modus-wc-menu-item>
<modus-wc-menu-item
label="With Start Icon"
value="3"
>
<modus-wc-icon slot="start-icon" name="info"></modus-wc-icon>
</modus-wc-menu-item>
<modus-wc-menu-item
label="Disabled"
value="3"
disabled="true"
></modus-wc-menu-item>
</modus-wc-menu>
`;
},
};
export const MultiSelect = {
args: {
'selection-mode': 'multiple',
},
render: (args) => {
let outputEl;
const handleSelectionChange = (e) => {
if (!outputEl)
return;
const { selectedItems } = e.detail;
outputEl.textContent =
selectedItems.length > 0
? `Selected: ${selectedItems.map((i) => i.getAttribute('value')).join(', ')}`
: 'Selected: none';
};
// prettier-ignore
return html `
<modus-wc-menu
aria-label="Menu"
?bordered=${args.bordered}
custom-class=${ifDefined(args['custom-class'])}
orientation=${ifDefined(args.orientation)}
selection-mode=${ifDefined(args['selection-mode'])}
size=${ifDefined(args.size)}
@menuSelectionChange=${handleSelectionChange}
>
<modus-wc-menu-item
label="Menu Item 1"
value="1"
></modus-wc-menu-item>
<modus-wc-menu-item label="Menu Item 2" value="2"></modus-wc-menu-item>
<modus-wc-menu-item
label="Menu Item 3"
value="3"
></modus-wc-menu-item>
<modus-wc-menu-item
label="Menu Item 4"
value="4"
bordered="true"
></modus-wc-menu-item>
<modus-wc-menu-item
label="Menu Item 5"
value="5"
sub-label="Menu Item 5 Sub-label"
></modus-wc-menu-item>
</modus-wc-menu>
<p ${ref((el) => { outputEl = el; })} style="font-size: 0.875rem; margin-top: 0.5rem; color: var(--modus-wc-color-gray-6);">Selected: none</p>
`;
},
};
export const CustomMenu = {
render: () => {
// prettier-ignore
return html `
<style>
.custom-menu-width {
width: 400px;
}
.custom-flex-row {
display: flex;
align-items: center;
}
.custom-nested-row {
padding-inline-start: 3rem;
}
.custom-justify-end {
margin-left: auto;
}
.green-square {
height: 18px;
width: 18px;
background-color: green;
}
.red-square {
height: 18px;
width: 18px;
background-color: red;
}
</style>
<modus-wc-menu
aria-label="Custom menu"
custom-class="custom-menu-width"
>
<li>
<div class="custom-flex-row">
<modus-wc-icon decorative="true" name="expand_more"></modus-wc-icon>
<modus-wc-button aria-label="Visible button" size="sm" shape="circle" variant="borderless">
<modus-wc-icon aria-label="Visible icon" name="visibility_on"></modus-wc-icon>
</modus-wc-button>
<div>Parent</div>
<div class="custom-justify-end">
<modus-wc-button aria-label="Actions button" size="sm" shape="circle" variant="borderless">
<modus-wc-icon aria-label="Actions icon" name="more_vertical"></modus-wc-icon>
</modus-wc-button>
</div>
</div>
</li>
<li>
<div class="custom-flex-row custom-nested-row">
<modus-wc-button aria-label="Visible button" size="sm" shape="circle" variant="borderless">
<modus-wc-icon aria-label="Visible icon" name="visibility_on"></modus-wc-icon>
</modus-wc-button>
<div class="green-square"></div>
<div>Child</div>
<div class="custom-justify-end">
<modus-wc-button aria-label="Actions button" size="sm" shape="circle" variant="borderless">
<modus-wc-icon aria-label="Actions icon" name="more_vertical"></modus-wc-icon>
</modus-wc-button>
</div>
</div>
</li>
<li>
<div class="custom-flex-row custom-nested-row">
<modus-wc-button aria-label="Visible button" size="sm" shape="circle" variant="borderless">
<modus-wc-icon aria-label="Invisible icon" name="visibility_off"></modus-wc-icon>
</modus-wc-button>
<div class="red-square"></div>
<div>Child</div>
<div class="custom-justify-end">
<modus-wc-button aria-label="Actions button" size="sm" shape="circle" variant="borderless">
<modus-wc-icon aria-label="Actions icon" name="more_vertical"></modus-wc-icon>
</modus-wc-button>
</div>
</div>
</li>
<li>
<div class="custom-flex-row">
<modus-wc-icon decorative="true" name="chevron_right"></modus-wc-icon>
<modus-wc-button aria-label="Visible button" size="sm" shape="circle" variant="borderless">
<modus-wc-icon aria-label="Visible icon" name="visibility_on"></modus-wc-icon>
</modus-wc-button>
<div>Parent</div>
<div class="custom-justify-end">
<modus-wc-button aria-label="Actions button" size="sm" shape="circle" variant="borderless">
<modus-wc-icon aria-label="Actions icon" name="more_vertical"></modus-wc-icon>
</modus-wc-button>
</div>
</div>
</li>
</modus-wc-menu>
`;
},
};
export const CollapsibleMenu = {
render: () => {
return html `
<style>
.menu-width {
width: 400px;
}
</style>
<modus-wc-menu custom-class="menu-width">
<modus-wc-menu-item
label="Charts"
.hasSubmenu=${true}
id="charts-menu"
value="charts"
>
<modus-wc-icon
slot="start-icon"
decorative="true"
name="bar_graph"
></modus-wc-icon>
<modus-wc-menu .isSubMenu=${true} id="charts-submenu">
<modus-wc-menu-item label="Bar Chart" value="bar-chart">
</modus-wc-menu-item>
<modus-wc-menu-item label="Line Chart" value="line-chart">
</modus-wc-menu-item>
</modus-wc-menu>
</modus-wc-menu-item>
<modus-wc-menu-item label="Calendar" value="calendar">
<modus-wc-icon
slot="start-icon"
decorative="true"
name="calendar"
></modus-wc-icon>
</modus-wc-menu-item>
<modus-wc-menu-item
label="Reports"
.hasSubmenu=${true}
id="reports-menu"
value="reports"
>
<modus-wc-icon
slot="start-icon"
decorative="true"
name="master_data"
></modus-wc-icon>
<modus-wc-menu .isSubMenu=${true} id="reports-submenu">
<modus-wc-menu-item label="Monthly Report" value="monthly-report">
</modus-wc-menu-item>
<modus-wc-menu-item label="Annual Report" value="annual-report">
</modus-wc-menu-item>
</modus-wc-menu>
</modus-wc-menu-item>
</modus-wc-menu>
<script>
// Adding this block to show how to set submenu properties via JS
// document.addEventListener('DOMContentLoaded', () => {
// const chartsMenu = document.querySelector('#charts-menu');
// const reportsMenu = document.querySelector('#reports-menu');
// const chartsSubMenu = document.querySelector('#charts-submenu');
// const reportsSubMenu = document.querySelector('#reports-submenu');
// // Set hasSubmenu property for menu items with submenus
// [chartsMenu, reportsMenu].forEach((menuItem) => {
// if (menuItem) {
// menuItem.hasSubmenu = true;
// }
// });
// // Set isSubMenu for all submenu elements
// [chartsSubMenu, reportsSubMenu].forEach((submenu) => {
// if (submenu) {
// submenu.isSubMenu = true;
// }
// });
// });
</script>
`;
},
};
export const ShadowDomParent = {
render: (args) => {
// Create a unique shadow host for menu component
if (!customElements.get('menu-shadow-host')) {
const MenuShadowHost = createShadowHostClass({
componentTag: 'modus-wc-menu',
propsMapper: (v, el) => {
const menuEl = el;
menuEl.ariaLabel = 'Shadow DOM Menu';
menuEl.bordered = Boolean(v.bordered);
menuEl.customClass = v['custom-class'] || '';
menuEl.orientation = v.orientation || 'vertical';
menuEl.size = v.size || 'md';
// Only set innerHTML once on initial creation
if (!el.querySelector('modus-wc-menu-item')) {
el.innerHTML = `
<modus-wc-menu-item
label="Small"
value="1"
size="sm"
></modus-wc-menu-item>
<modus-wc-menu-item label="Medium" value="2"></modus-wc-menu-item>
<modus-wc-menu-item
label="Large"
value="3"
size="lg"
></modus-wc-menu-item>
<modus-wc-menu-item
label="Bordered"
value="3"
bordered="true"
></modus-wc-menu-item>
<modus-wc-menu-item
label="With Sub-label"
value="3"
sub-label="Sub-label"
></modus-wc-menu-item>
<modus-wc-menu-item
label="Selected"
value="3"
selected="true"
></modus-wc-menu-item>
<modus-wc-menu-item
label="With Start Icon"
value="3"
>
<modus-wc-icon slot="start-icon" name="info"></modus-wc-icon>
</modus-wc-menu-item>
<modus-wc-menu-item
label="Disabled"
value="3"
disabled="true"
></modus-wc-menu-item>
`;
}
},
});
customElements.define('menu-shadow-host', MenuShadowHost);
}
return html `<menu-shadow-host .props=${Object.assign({}, args)}></menu-shadow-host>`;
},
};