UNPKG

@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

436 lines (411 loc) 14.4 kB
import { withActions } from "@storybook/addon-actions/decorator"; import { html } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; const defaultHeader = { showBackButton: true, title: 'Title', subtitle: 'Subtitle', showCloseButton: true, }; const meta = { title: 'Components/Bottom Sheet', component: 'modus-wc-bottom-sheet', args: { 'custom-class': '', visible: true, 'display-mode': 'default', 'drag-step-threshold': 0.4, header: defaultHeader, }, argTypes: { visible: { control: 'boolean', table: { defaultValue: { summary: 'false' } }, }, 'display-mode': { control: 'select', options: ['default', 'expanded', 'minimized'], table: { defaultValue: { summary: 'default' } }, }, 'drag-step-threshold': { control: { type: 'number', min: 0, max: 1, step: 0.05 }, table: { defaultValue: { summary: '0.4' } }, }, header: { description: 'Configuration for the built-in header layout. Do not set if you use the header slot.', table: { type: { detail: ` Interface: IBottomSheetHeader Properties: - showBackButton (boolean, optional): Whether to show the back button - title (string, optional): The title of the header - subtitle (string, optional): The subtitle of the header - showCloseButton (boolean, optional): Whether to show the dismiss button. Clicking it closes the bottom sheet `, }, }, control: { type: 'object', }, }, }, decorators: [withActions], parameters: { layout: 'padded', actions: { handles: [ 'headerBackClick', 'headerCloseClick', 'displayModeChange', 'sheetVisibilityChange', ], }, docs: { description: { component: ` A bottom sheet that slides up from the bottom of the viewport. It supports three resting display modes — minimized, default, and expanded — controlled by the \`displayMode\` property or the drag handle. The component supports \`header\`, \`content\`, and \`footer\` slots. Alternatively, set the \`header\` prop for the built-in header layout (do not set \`header\` if you use the \`header\` slot). `, }, }, }, }; export default meta; export const Default = { parameters: { docs: { description: { story: 'Default sheet with the built-in header, content, and footer slots. Toggle the `visible` control to open and close it. In a real app, attach the sheet to `<body>` and drive `visible` from a button in your page (see **Triggered By Button**).', }, source: { code: `<style> /* Demo-only frame: the component is position: fixed, which would escape to the page (and overlap other stories) on the docs canvas. 'contain' makes this frame a containing block for the sheet's position: fixed and clips it, so the sheet stays bounded WITHOUT changing its position value (overriding position to absolute caused a render jump). */ .bottom-sheet-demo { border: 1px dashed var(--modus-wc-color-base-content-low-contrast); contain: layout paint; height: 520px; width: 100%; } .bottom-sheet-demo .modus-wc-panel { max-height: 480px; } .modus-wc-bottom-sheet-footer-actions { align-items: center; display: flex; gap: var(--modus-wc-spacing-sm); justify-content: flex-end; width: 100%; } </style> <div class="bottom-sheet-demo"> <modus-wc-bottom-sheet id="app-bottom-sheet" visible style="width: 600px"> <div slot="content">Main content area for forms, lists, or other components.</div> <div slot="footer"> <div class="modus-wc-bottom-sheet-footer-actions"> <modus-wc-button color="tertiary" size="sm" variant="outlined">Cancel</modus-wc-button> <modus-wc-button color="primary" size="sm" variant="filled">Save</modus-wc-button> </div> </div> </modus-wc-bottom-sheet> </div> <script> document.getElementById('app-bottom-sheet').header = { showBackButton: true, title: 'Title', subtitle: 'Subtitle', showCloseButton: true, }; </script>`, }, }, }, render: (args) => { // prettier-ignore return html ` <style> .bottom-sheet-demo { border: 1px dashed var(--modus-wc-color-base-content-low-contrast); contain: layout paint; height: 520px; width: 100%; } .bottom-sheet-demo .modus-wc-panel { max-height: 480px; } .modus-wc-bottom-sheet-footer-actions { align-items: center; display: flex; gap: var(--modus-wc-spacing-sm); justify-content: flex-end; width: 100%; } </style> <div class="bottom-sheet-demo"> <modus-wc-bottom-sheet ?visible="${args.visible}" display-mode="${ifDefined(args['display-mode'])}" drag-step-threshold="${ifDefined(args['drag-step-threshold'])}" custom-class="${ifDefined(args['custom-class'])}" style="width: 600px" .header="${args.header}" > <div slot="content"> <modus-wc-typography hierarchy="p" size="md" label="Main content area for forms, lists, or other components." ></modus-wc-typography> </div> <div slot="footer"> <div class="modus-wc-bottom-sheet-footer-actions"> <modus-wc-button color="tertiary" size="sm" variant="outlined"> Cancel </modus-wc-button> <modus-wc-button color="primary" size="sm" variant="filled"> Save </modus-wc-button> </div> </div> </modus-wc-bottom-sheet> </div> `; }, }; export const TriggeredByButton = { args: { header: { showBackButton: false, title: 'Bottom Sheet Title', subtitle: 'Drag the handle down to minimize or up to expand.', showCloseButton: false, }, }, parameters: { docs: { description: { story: [ 'Open the sheet from a control in your own page.', '', '- Put the **trigger button (and its click handler) in your page markup**.', '- Render `<modus-wc-bottom-sheet>` as a **direct child of `<body>`** so its `position: fixed` anchors it to the window.', '- Open with `sheet.visible = true` (optionally set `sheet.displayMode`); close with `sheet.visible = false`.', '', 'The dashed frame below is demo-only so the sheet stays bounded within this story.', ].join('\n'), }, source: { code: `<!-- 1) Trigger button + handler live in YOUR page --> <modus-wc-button id="open-bottom-sheet" color="primary" variant="filled"> Open bottom sheet </modus-wc-button> <modus-wc-button id="minimize-bottom-sheet" color="tertiary" variant="outlined"> Minimize </modus-wc-button> <!-- 2) Attach the sheet as a direct child of <body> so position: fixed pins it to the bottom of the window --> <modus-wc-bottom-sheet id="app-bottom-sheet"> <div slot="content">Main content area for forms, lists, or other components.</div> <div slot="footer"> <modus-wc-button id="cancel-bottom-sheet" color="tertiary" variant="outlined">Cancel</modus-wc-button> <modus-wc-button id="save-bottom-sheet" color="primary" variant="filled">Save</modus-wc-button> </div> </modus-wc-bottom-sheet> <script> const sheet = document.getElementById('app-bottom-sheet'); sheet.header = { showBackButton: false, title: 'Bottom Sheet Title', subtitle: 'Drag the handle down to minimize or up to expand.', showCloseButton: false, }; // Open from the page button document .getElementById('open-bottom-sheet') .addEventListener('buttonClick', () => { sheet.visible = true; sheet.displayMode = 'default'; }); // Minimize from the page button document .getElementById('minimize-bottom-sheet') .addEventListener('buttonClick', () => { sheet.displayMode = 'minimized'; }); // Close from buttons inside the sheet ['cancel-bottom-sheet', 'save-bottom-sheet'].forEach((id) => document .getElementById(id) .addEventListener('buttonClick', () => (sheet.visible = false)) ); // (Optional) react to state changes sheet.addEventListener('sheetVisibilityChange', (e) => console.log('visible:', e.detail.visible) ); sheet.addEventListener('displayModeChange', (e) => console.log('display mode:', e.detail.displayMode) ); </script>`, }, }, }, render: (args) => { const sheetId = 'demo-triggered-bottom-sheet'; const closeSheet = () => { const sheet = document.getElementById(sheetId); if (sheet) sheet.visible = false; }; const minimizeSheet = () => { const sheet = document.getElementById(sheetId); if (sheet) sheet.displayMode = 'minimized'; }; const restoreSheet = () => { const sheet = document.getElementById(sheetId); if (sheet) { const typed = sheet; typed.visible = true; typed.displayMode = 'default'; } }; // prettier-ignore return html ` <style> /* Demo-only frame: the component is position: fixed, which would escape to the page (and overlap other stories) on the docs canvas. 'contain' makes this frame a containing block for the sheet's position: fixed and clips it, so the sheet stays bounded WITHOUT changing its position value (overriding position to absolute caused a render jump). */ .bottom-sheet-demo { border: 1px dashed var(--modus-wc-color-base-content-low-contrast); contain: layout paint; height: 520px; width: 100%; } .bottom-sheet-demo .modus-wc-panel { max-height: 480px; } .modus-wc-bottom-sheet-footer-actions { align-items: center; display: flex; gap: var(--modus-wc-spacing-sm); justify-content: flex-end; width: 100%; } .modus-wc-bottom-sheet-trigger-actions { display: flex; gap: var(--modus-wc-spacing-sm); padding: var(--modus-wc-spacing-lg); } </style> <div class="bottom-sheet-demo"> <div class="modus-wc-bottom-sheet-trigger-actions"> <modus-wc-button color="primary" size="md" variant="filled" @buttonClick="${restoreSheet}" > <modus-wc-icon name="expand_more" size="sm" decorative></modus-wc-icon> Open bottom sheet </modus-wc-button> <modus-wc-button color="tertiary" size="md" variant="outlined" @buttonClick="${minimizeSheet}" > Minimize </modus-wc-button> </div> <modus-wc-bottom-sheet id="${sheetId}" drag-step-threshold="${ifDefined(args['drag-step-threshold'])}" style="width: 600px" .header="${ifDefined(args.header)}" > <div slot="content"> <modus-wc-typography hierarchy="p" size="md" label="Main content area for forms, lists, or other components." ></modus-wc-typography> </div> <div slot="footer"> <div class="modus-wc-bottom-sheet-footer-actions"> <modus-wc-button color="tertiary" size="sm" variant="outlined" @buttonClick="${closeSheet}" > Cancel </modus-wc-button> <modus-wc-button color="primary" size="sm" variant="filled" @buttonClick="${closeSheet}" > Save </modus-wc-button> </div> </div> </modus-wc-bottom-sheet> </div> `; }, }; export const ContentOnly = { args: { header: undefined, }, parameters: { docs: { description: { story: 'Sheet with only the `content` slot — no header or footer. The drag handle still lets users minimize/expand. As with the other stories, attach the sheet to `<body>` in production and toggle `visible` from your own page control.', }, }, }, render: (args) => { // prettier-ignore return html ` <style> /* Demo-only frame: the component is position: fixed, which would escape to the page (and overlap other stories) on the docs canvas. 'contain' makes this frame a containing block for the sheet's position: fixed and clips it, so the sheet stays bounded WITHOUT changing its position value (overriding position to absolute caused a render jump). */ .bottom-sheet-demo { border: 1px dashed var(--modus-wc-color-base-content-low-contrast); contain: layout paint; height: 520px; width: 100%; } .bottom-sheet-demo .modus-wc-panel { max-height: 480px; } </style> <div class="bottom-sheet-demo"> <modus-wc-bottom-sheet ?visible="${args.visible}" style="width: 600px" .header="${args.header}" > <div slot="content"> <modus-wc-typography hierarchy="p" size="md" label="Bottom sheet with content slot only." ></modus-wc-typography> </div> </modus-wc-bottom-sheet> </div> `; }, };