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

237 lines (231 loc) 9.71 kB
import { html } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; import { createShadowHostClass } from "../../providers/shadow-dom/shadow-host-helper"; import { generateRandomId } from "../utils"; const meta = { title: 'Components/Modal', component: 'modus-wc-modal', args: { backdrop: 'default', 'custom-class': '', fullscreen: false, 'modal-id': 'my_modal_1', position: 'center', 'show-close': true, 'show-fullscreen-toggle': false, }, argTypes: { backdrop: { control: { type: 'select' }, options: ['default', 'static'], }, position: { control: { type: 'select' }, options: ['bottom', 'center', 'top'], }, }, parameters: { layout: 'centered', }, }; export default meta; const illustrativeScript = html ` <script> // This is to illustrate how to implement modal visibility handling // const modalId = document // .querySelector('modus-wc-modal') // .getAttribute('modal-id'); // const handleModalVisibility = (action) => { // const modal = document.getElementById(modalId); // if (modal) { // if (action === 'show') { // modal.showModal(); // } else { // modal.close(); // } // } // }; // const openButton = document.getElementById('open-modal-btn'); // const closeButton = document.getElementById('close-modal-btn'); // openButton.addEventListener('click', () => // handleModalVisibility('show') // ); // closeButton.addEventListener('click', () => // handleModalVisibility('hide') // ); </script> `; export const Default = { render: (args) => { const modalId = `${args['modal-id']}${generateRandomId(4)}}`; const handleModalVisibility = (action) => { const modal = document.getElementById(modalId); if (modal) { if (action === 'show') { modal.showModal(); } else { modal.close(); } } }; // prettier-ignore return html ` <modus-wc-button id="open-modal-btn" @buttonClick=${() => handleModalVisibility('show')}> Open modal </modus-wc-button> <modus-wc-modal aria-label="Example modal" custom-class=${ifDefined(args['custom-class'])} fullscreen=${args.fullscreen} modal-id=${modalId} backdrop=${args.backdrop} position=${args.position} show-close=${args['show-close']} show-fullscreen-toggle=${args['show-fullscreen-toggle']} > <span slot="header">Modal Title</span> <span slot="content"> This is sample modal content. </span> <modus-wc-button slot="footer" id="close-modal-btn" @buttonClick=${() => handleModalVisibility('hide')}> Close </modus-wc-button> </modus-wc-modal> ${illustrativeScript} `; }, }; export const CustomWidthAndHeight = { render: (args) => { const modalId = `${args['modal-id']}${generateRandomId(4)}}`; const handleModalVisibility = (action) => { const modal = document.getElementById(modalId); if (modal) { if (action === 'show') { modal.showModal(); } else { modal.close(); } } }; // prettier-ignore return html ` <style> .expanded-modal .modus-wc-modal-box { width: 80%; max-width: none; height: 60%; max-height: none; } </style> <modus-wc-button id="open-modal-btn" @buttonClick=${() => handleModalVisibility('show')}> Open modal </modus-wc-button> <modus-wc-modal aria-label="Example modal" custom-class="expanded-modal" modal-id=${modalId} backdrop=${ifDefined(args.backdrop)} position=${ifDefined(args.position)} show-close=${ifDefined(args['show-close'])} > <span slot="header">Modal Title</span> <p slot="content">Sample modal content.</p> <modus-wc-button slot="footer" id="close-modal-btn" @buttonClick=${() => handleModalVisibility('hide')}> Close </modus-wc-button> </modus-wc-modal> ${illustrativeScript} `; }, }; export const ShadowDomParent = { render: (args) => { const modalId = `shadow-dom-modal`; const handleModalVisibility = (action) => { var _a; // The dialog lives inside the shadow host's shadowRoot, not in document const host = document.querySelector('modal-shadow-host'); const modal = (_a = host === null || host === void 0 ? void 0 : host.shadowRoot) === null || _a === void 0 ? void 0 : _a.getElementById(modalId); if (modal) { if (action === 'show') modal.showModal(); else modal.close(); } }; if (!customElements.get('modal-shadow-host')) { const ModalShadowHost = createShadowHostClass({ componentTag: 'modus-wc-modal', propsMapper: (v, el) => { const modalEl = el; modalEl.backdrop = v.backdrop; modalEl.customClass = v['custom-class'] || ''; modalEl.fullscreen = Boolean(v.fullscreen); modalEl.modalId = modalId; modalEl.position = v.position; modalEl.showClose = Boolean(v['show-close']); modalEl.showFullscreenToggle = Boolean(v['show-fullscreen-toggle']); if (!el.hasChildNodes()) { el.innerHTML = `<span slot="header">Modal Title</span><span slot="content">This is sample modal content.</span><modus-wc-button slot="footer">Close</modus-wc-button>`; // Wire the footer close button to close the dialog const closeBtn = el.querySelector('modus-wc-button[slot="footer"]'); closeBtn === null || closeBtn === void 0 ? void 0 : closeBtn.addEventListener('buttonClick', () => { const dialog = el.querySelector('dialog'); dialog === null || dialog === void 0 ? void 0 : dialog.close(); }); } }, }); customElements.define('modal-shadow-host', ModalShadowHost); } // prettier-ignore return html ` <modus-wc-button @buttonClick=${() => handleModalVisibility('show')}> Open modal </modus-wc-button> <modal-shadow-host .props=${Object.assign({}, args)}></modal-shadow-host> `; }, }; export const Migration = { parameters: { docs: { description: { story: ` #### Breaking Changes - Modal identification is now required via the \`modal-id\` prop. - 2.0 requires the use of slots for a fully customizable \`header\`, \`content\`, and \`footer\`. Primary and secondary buttons as well as \`header-text\` are no longer built-in. - In 1.0, modals had built-in open/close state management with methods. 2.0 uses the native HTML dialog element with \`modal-id\` to target the dialog with native \`showModal()\` and \`close()\` methods. #### Prop Mapping | 1.0 Prop | 2.0 Prop | Notes | |------------------------------|-------------------------|-----------------------------------------------| | aria-label | aria-label | | | backdrop | backdrop | | | fullscreen | fullscreen | | | header-text | | Not carried over, use \`header\` slot instead | | primary-button-aria-label | | Not carried over, use \`footer\` slot instead | | primary-button-disabled | | Not carried over, use \`footer\` slot instead | | primary-button-text | | Not carried over, use \`footer\` slot instead | | secondary-button-aria-label | | Not carried over, use \`footer\` slot instead | | secondary-button-disabled | | Not carried over, use \`footer\` slot instead | | secondary-button-text | | Not carried over, use \`footer\` slot instead | | show-fullscreen-toggle | show-fullscreen-toggle | | | z-index | | Not carried over, use CSS instead | #### Event Mapping | 1.0 Event | 2.0 Event | Notes | |----------------------|-----------|-----------------------------------------------------------------------------------| | closed | | Not carried over, use dialog \`close()\` event instead | | opened | | Not carried over, use dialog \`showModal()\` event instead | | primaryButtonClick | | Not carried over, handle with events on custom buttons in \`footer\` slot instead | | secondaryButtonClick | | Not carried over, handle with events on custom buttons in \`footer\` slot instead | `, }, }, controls: { disable: true }, canvas: { disable: true }, }, render: () => html `<div></div>`, };