@vielzeug/refine 26.10.6 → 26.10.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/accordion-item.cjs +2 -2
- package/dist/accordion-item.cjs.map +1 -1
- package/dist/accordion-item.js +6 -6
- package/dist/accordion-item.js.map +1 -1
- package/dist/accordion.cjs +2 -2
- package/dist/accordion.cjs.map +1 -1
- package/dist/accordion.js +2 -2
- package/dist/accordion.js.map +1 -1
- package/dist/alert.cjs +2 -2
- package/dist/alert.cjs.map +1 -1
- package/dist/alert.js +2 -2
- package/dist/alert.js.map +1 -1
- package/dist/avatar.cjs +3 -3
- package/dist/avatar.cjs.map +1 -1
- package/dist/avatar.js +2 -2
- package/dist/avatar.js.map +1 -1
- package/dist/badge.cjs +2 -2
- package/dist/badge.cjs.map +1 -1
- package/dist/badge.js +2 -2
- package/dist/badge.js.map +1 -1
- package/dist/box.cjs +2 -2
- package/dist/box.cjs.map +1 -1
- package/dist/box.js +2 -2
- package/dist/box.js.map +1 -1
- package/dist/button-group.cjs +2 -2
- package/dist/button-group.cjs.map +1 -1
- package/dist/button-group.js +2 -2
- package/dist/button-group.js.map +1 -1
- package/dist/button.cjs +9 -9
- package/dist/button.cjs.map +1 -1
- package/dist/button.js +8 -8
- package/dist/button.js.map +1 -1
- package/dist/card.cjs +2 -2
- package/dist/card.cjs.map +1 -1
- package/dist/card.js +2 -2
- package/dist/card.js.map +1 -1
- package/dist/carousel.cjs +6 -6
- package/dist/carousel.cjs.map +1 -1
- package/dist/carousel.js +6 -6
- package/dist/carousel.js.map +1 -1
- package/dist/chat-message.cjs +4 -4
- package/dist/chat-message.cjs.map +1 -1
- package/dist/chat-message.js +4 -4
- package/dist/chat-message.js.map +1 -1
- package/dist/checkbox-group.cjs +1 -1
- package/dist/checkbox-group.cjs.map +1 -1
- package/dist/checkbox-group.js +1 -1
- package/dist/checkbox-group.js.map +1 -1
- package/dist/checkbox.cjs +1 -1
- package/dist/checkbox.cjs.map +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/checkbox.js.map +1 -1
- package/dist/chip.cjs +8 -8
- package/dist/chip.cjs.map +1 -1
- package/dist/chip.js +8 -8
- package/dist/chip.js.map +1 -1
- package/dist/combobox.cjs +8 -8
- package/dist/combobox.cjs.map +1 -1
- package/dist/combobox.js +11 -11
- package/dist/combobox.js.map +1 -1
- package/dist/content/avatar/avatar.d.ts +2 -1
- package/dist/content/avatar/avatar.d.ts.map +1 -1
- package/dist/content/card/card.d.ts +6 -3
- package/dist/content/card/card.d.ts.map +1 -1
- package/dist/content/carousel/carousel.d.ts +5 -3
- package/dist/content/carousel/carousel.d.ts.map +1 -1
- package/dist/content/chat-message/chat-message.d.ts +2 -1
- package/dist/content/chat-message/chat-message.d.ts.map +1 -1
- package/dist/content/copy-command/copy-command.d.ts +1 -1
- package/dist/content/copy-command/copy-command.d.ts.map +1 -1
- package/dist/content/keyboard-key/keyboard-key.d.ts +2 -1
- package/dist/content/keyboard-key/keyboard-key.d.ts.map +1 -1
- package/dist/content/list/list.d.ts +2 -1
- package/dist/content/list/list.d.ts.map +1 -1
- package/dist/content/marquee/marquee.d.ts +3 -2
- package/dist/content/marquee/marquee.d.ts.map +1 -1
- package/dist/content/pagination/pagination.d.ts +1 -1
- package/dist/content/pagination/pagination.d.ts.map +1 -1
- package/dist/content/qr-code/qr-code.d.ts +0 -41
- package/dist/content/qr-code/qr-code.d.ts.map +1 -1
- package/dist/content/separator/separator.d.ts +1 -1
- package/dist/content/separator/separator.d.ts.map +1 -1
- package/dist/content/stepper/step.d.ts +1 -1
- package/dist/content/stepper/step.d.ts.map +1 -1
- package/dist/content/table/table.d.ts +2 -1
- package/dist/content/table/table.d.ts.map +1 -1
- package/dist/content/text/text.d.ts +13 -6
- package/dist/content/text/text.d.ts.map +1 -1
- package/dist/cookie-banner.cjs +1 -1
- package/dist/cookie-banner.cjs.map +1 -1
- package/dist/cookie-banner.js +1 -1
- package/dist/cookie-banner.js.map +1 -1
- package/dist/copy-command.cjs +6 -6
- package/dist/copy-command.cjs.map +1 -1
- package/dist/copy-command.js +7 -7
- package/dist/copy-command.js.map +1 -1
- package/dist/core/checkable.cjs +1 -1
- package/dist/core/checkable.cjs.map +1 -1
- package/dist/core/checkable.d.ts +6 -7
- package/dist/core/checkable.d.ts.map +1 -1
- package/dist/core/checkable.js +1 -1
- package/dist/core/checkable.js.map +1 -1
- package/dist/core/choice-field.cjs +1 -1
- package/dist/core/choice-field.cjs.map +1 -1
- package/dist/core/choice-field.d.ts +6 -6
- package/dist/core/choice-field.d.ts.map +1 -1
- package/dist/core/choice-field.js +1 -1
- package/dist/core/choice-field.js.map +1 -1
- package/dist/core/text-field.cjs +1 -1
- package/dist/core/text-field.cjs.map +1 -1
- package/dist/core/text-field.d.ts +8 -8
- package/dist/core/text-field.d.ts.map +1 -1
- package/dist/core/text-field.js +1 -1
- package/dist/core/text-field.js.map +1 -1
- package/dist/counter.cjs +2 -2
- package/dist/counter.cjs.map +1 -1
- package/dist/counter.js +2 -2
- package/dist/counter.js.map +1 -1
- package/dist/custom-elements.json +5184 -5047
- package/dist/datagrid.cjs +47 -47
- package/dist/datagrid.cjs.map +1 -1
- package/dist/datagrid.js +51 -51
- package/dist/datagrid.js.map +1 -1
- package/dist/date-picker.cjs +9 -9
- package/dist/date-picker.cjs.map +1 -1
- package/dist/date-picker.js +11 -11
- package/dist/date-picker.js.map +1 -1
- package/dist/dialog.cjs +3 -3
- package/dist/dialog.cjs.map +1 -1
- package/dist/dialog.js +3 -3
- package/dist/dialog.js.map +1 -1
- package/dist/disclosure/accordion/accordion.d.ts +4 -2
- package/dist/disclosure/accordion/accordion.d.ts.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.d.ts +1 -1
- package/dist/disclosure/accordion-item/accordion-item.d.ts.map +1 -1
- package/dist/disclosure/tab-item/tab-item.d.ts +1 -1
- package/dist/disclosure/tab-item/tab-item.d.ts.map +1 -1
- package/dist/disclosure/tabs/tabs.d.ts +1 -1
- package/dist/disclosure/tabs/tabs.d.ts.map +1 -1
- package/dist/drawer.cjs +4 -4
- package/dist/drawer.cjs.map +1 -1
- package/dist/drawer.js +2 -2
- package/dist/drawer.js.map +1 -1
- package/dist/feedback/alert/alert.d.ts +2 -1
- package/dist/feedback/alert/alert.d.ts.map +1 -1
- package/dist/feedback/badge/badge.d.ts +7 -4
- package/dist/feedback/badge/badge.d.ts.map +1 -1
- package/dist/feedback/chip/chip.d.ts +7 -5
- package/dist/feedback/chip/chip.d.ts.map +1 -1
- package/dist/grid-item.cjs +2 -2
- package/dist/grid-item.cjs.map +1 -1
- package/dist/grid-item.js +2 -2
- package/dist/grid-item.js.map +1 -1
- package/dist/grid.cjs +2 -2
- package/dist/grid.cjs.map +1 -1
- package/dist/grid.js +2 -2
- package/dist/grid.js.map +1 -1
- package/dist/input.cjs +7 -7
- package/dist/input.cjs.map +1 -1
- package/dist/input.js +8 -8
- package/dist/input.js.map +1 -1
- package/dist/inputs/button/button.d.ts +3 -2
- package/dist/inputs/button/button.d.ts.map +1 -1
- package/dist/inputs/button-group/button-group.d.ts +1 -37
- package/dist/inputs/button-group/button-group.d.ts.map +1 -1
- package/dist/inputs/combobox/combobox.types.d.ts +2 -2
- package/dist/inputs/combobox/combobox.types.d.ts.map +1 -1
- package/dist/inputs/counter/counter.d.ts +3 -2
- package/dist/inputs/counter/counter.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.cjs +1 -1
- package/dist/inputs/datagrid/datagrid-model.cjs.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.d.ts +2 -1
- package/dist/inputs/datagrid/datagrid-model.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.js +1 -1
- package/dist/inputs/datagrid/datagrid-model.js.map +1 -1
- package/dist/inputs/datagrid/datagrid.d.ts +4 -2
- package/dist/inputs/datagrid/datagrid.d.ts.map +1 -1
- package/dist/inputs/date-picker/date-picker.d.ts +3 -2
- package/dist/inputs/date-picker/date-picker.d.ts.map +1 -1
- package/dist/inputs/input/input.d.ts +5 -3
- package/dist/inputs/input/input.d.ts.map +1 -1
- package/dist/inputs/message-composer/message-composer.d.ts +3 -2
- package/dist/inputs/message-composer/message-composer.d.ts.map +1 -1
- package/dist/inputs/number-input/number-input.d.ts +1 -1
- package/dist/inputs/number-input/number-input.d.ts.map +1 -1
- package/dist/inputs/otp-input/otp-input.d.ts +3 -2
- package/dist/inputs/otp-input/otp-input.d.ts.map +1 -1
- package/dist/inputs/query-bar/query-bar.d.ts.map +1 -1
- package/dist/inputs/select/select.d.ts +2 -2
- package/dist/inputs/select/select.d.ts.map +1 -1
- package/dist/inputs/textarea/textarea.d.ts +4 -3
- package/dist/inputs/textarea/textarea.d.ts.map +1 -1
- package/dist/inputs/time-picker/time-picker.d.ts +2 -2
- package/dist/inputs/time-picker/time-picker.d.ts.map +1 -1
- package/dist/keyboard-key.cjs +3 -3
- package/dist/keyboard-key.cjs.map +1 -1
- package/dist/keyboard-key.js +3 -3
- package/dist/keyboard-key.js.map +1 -1
- package/dist/layout/box/box.d.ts +6 -3
- package/dist/layout/box/box.d.ts.map +1 -1
- package/dist/layout/grid/grid.d.ts +11 -6
- package/dist/layout/grid/grid.d.ts.map +1 -1
- package/dist/layout/grid-item/grid-item.d.ts +8 -4
- package/dist/layout/grid-item/grid-item.d.ts.map +1 -1
- package/dist/layout/navbar/navbar.d.ts +4 -2
- package/dist/layout/navbar/navbar.d.ts.map +1 -1
- package/dist/layout/sidebar/sidebar.d.ts +2 -1
- package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
- package/dist/list-item.cjs +1 -1
- package/dist/list-item.cjs.map +1 -1
- package/dist/list-item.js +1 -1
- package/dist/list-item.js.map +1 -1
- package/dist/list.cjs +2 -2
- package/dist/list.cjs.map +1 -1
- package/dist/list.js +2 -2
- package/dist/list.js.map +1 -1
- package/dist/marquee.cjs +3 -3
- package/dist/marquee.cjs.map +1 -1
- package/dist/marquee.js +3 -3
- package/dist/marquee.js.map +1 -1
- package/dist/menu.cjs +3 -3
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.js +3 -3
- package/dist/menu.js.map +1 -1
- package/dist/message-composer.cjs +10 -10
- package/dist/message-composer.cjs.map +1 -1
- package/dist/message-composer.js +7 -7
- package/dist/message-composer.js.map +1 -1
- package/dist/navbar.cjs +10 -10
- package/dist/navbar.cjs.map +1 -1
- package/dist/navbar.js +13 -13
- package/dist/navbar.js.map +1 -1
- package/dist/number-input.cjs +3 -3
- package/dist/number-input.cjs.map +1 -1
- package/dist/number-input.js +4 -4
- package/dist/number-input.js.map +1 -1
- package/dist/otp-input.cjs +4 -4
- package/dist/otp-input.cjs.map +1 -1
- package/dist/otp-input.js +4 -4
- package/dist/otp-input.js.map +1 -1
- package/dist/overlay/dialog/dialog.d.ts +7 -4
- package/dist/overlay/dialog/dialog.d.ts.map +1 -1
- package/dist/overlay/drawer/drawer.d.ts +4 -2
- package/dist/overlay/drawer/drawer.d.ts.map +1 -1
- package/dist/overlay/menu/menu.d.ts +2 -1
- package/dist/overlay/menu/menu.d.ts.map +1 -1
- package/dist/pagination.cjs +9 -9
- package/dist/pagination.cjs.map +1 -1
- package/dist/pagination.js +9 -9
- package/dist/pagination.js.map +1 -1
- package/dist/qr-code.cjs +2 -2
- package/dist/qr-code.cjs.map +1 -1
- package/dist/qr-code.js +2 -2
- package/dist/qr-code.js.map +1 -1
- package/dist/query-bar.cjs +7 -7
- package/dist/query-bar.cjs.map +1 -1
- package/dist/query-bar.js +8 -8
- package/dist/query-bar.js.map +1 -1
- package/dist/radio-group.cjs +1 -1
- package/dist/radio-group.cjs.map +1 -1
- package/dist/radio-group.js +1 -1
- package/dist/radio-group.js.map +1 -1
- package/dist/refine.cjs +123 -123
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +123 -123
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +124 -124
- package/dist/refine.js.map +1 -1
- package/dist/select.cjs +13 -13
- package/dist/select.cjs.map +1 -1
- package/dist/select.js +15 -15
- package/dist/select.js.map +1 -1
- package/dist/separator.cjs +4 -4
- package/dist/separator.cjs.map +1 -1
- package/dist/separator.js +4 -4
- package/dist/separator.js.map +1 -1
- package/dist/shared/prop-bundles.cjs +1 -1
- package/dist/shared/prop-bundles.cjs.map +1 -1
- package/dist/shared/prop-bundles.d.ts +7 -8
- package/dist/shared/prop-bundles.d.ts.map +1 -1
- package/dist/shared/prop-bundles.js +1 -1
- package/dist/shared/prop-bundles.js.map +1 -1
- package/dist/shared/types.cjs +2 -0
- package/dist/shared/types.cjs.map +1 -0
- package/dist/shared/types.d.ts +27 -9
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js +2 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/sidebar.cjs +7 -7
- package/dist/sidebar.cjs.map +1 -1
- package/dist/sidebar.js +4 -4
- package/dist/sidebar.js.map +1 -1
- package/dist/step.cjs +10 -10
- package/dist/step.cjs.map +1 -1
- package/dist/step.js +10 -10
- package/dist/step.js.map +1 -1
- package/dist/tab-item.cjs +4 -4
- package/dist/tab-item.cjs.map +1 -1
- package/dist/tab-item.js +5 -5
- package/dist/tab-item.js.map +1 -1
- package/dist/table.cjs +2 -2
- package/dist/table.cjs.map +1 -1
- package/dist/table.js +2 -2
- package/dist/table.js.map +1 -1
- package/dist/tabs.cjs +2 -2
- package/dist/tabs.cjs.map +1 -1
- package/dist/tabs.js +4 -4
- package/dist/tabs.js.map +1 -1
- package/dist/text.cjs +2 -2
- package/dist/text.cjs.map +1 -1
- package/dist/text.js +2 -2
- package/dist/text.js.map +1 -1
- package/dist/textarea.cjs +7 -7
- package/dist/textarea.cjs.map +1 -1
- package/dist/textarea.js +7 -7
- package/dist/textarea.js.map +1 -1
- package/dist/time-picker.cjs +7 -7
- package/dist/time-picker.cjs.map +1 -1
- package/dist/time-picker.js +7 -7
- package/dist/time-picker.js.map +1 -1
- package/dist/tooltip.cjs +1 -1
- package/dist/tooltip.cjs.map +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/tooltip.js.map +1 -1
- package/package.json +13 -13
package/dist/dialog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.js","names":[],"sources":["../src/overlay/dialog/dialog.ts"],"sourcesContent":["import { define, getHost, html, onCleanup, onEvent, onMounted, prop, ref, useEmit, useSlots } from '@vielzeug/ore';\n\nimport { type OverlayOpenChangeDetail, parseOptionalBool } from '../../core';\nimport { coarsePointerMixin, roundedVariantMixin } from '../../styles';\nimport type { PaddingSize, RoundedSize } from '../../types';\nimport { useDialogControl } from '../shared/use-dialog';\n\nimport componentStyles from './dialog.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\n\ntype DialogSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';\ntype DialogBackdrop = 'opaque' | 'blur' | 'transparent';\ntype DialogElevation = 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\n/** Dialog component properties */\n\nexport type OreDialogEvents = {\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreDialogProps = {\n /** Backdrop style: 'blur' (default): dark overlay + blur; 'opaque': dark overlay only; 'transparent': no overlay */\n backdrop?: DialogBackdrop;\n /** Initial uncontrolled open state. Ignored when `open` is set. */\n 'default-open'?: boolean;\n /** Show a close (×) button in the header */\n dismissible?: boolean;\n /** Panel shadow elevation: defaults to 'xl' */\n elevation?: DialogElevation;\n /**\n * CSS selector for the element inside the dialog that should receive focus when the dialog opens.\n * Defaults to the first focusable element (browser default).\n * @example 'input[name=\"email\"]' | '#confirm-btn'\n */\n 'initial-focus'?: string;\n /** Dialog title shown in the header (used as aria-label when no header slot) */\n label?: string;\n /** Controls the open state of the dialog */\n open?: boolean;\n /** Internal padding size */\n padding?: PaddingSize;\n /** When true, clicking the backdrop does not close the dialog */\n persistent?: boolean;\n /**\n * When true (default), the focus returns to the element that triggered the dialog after it closes.\n * Set to false if you want to manage focus manually.\n */\n 'return-focus'?: boolean;\n /** Border radius */\n rounded?: RoundedSize | '';\n /** Dialog size */\n size?: DialogSize;\n};\n\n/**\n * A modal dialog that traps focus, blocks page interaction, and dismisses on\n * `Escape`. Built on the native `<dialog>` element for correct top-layer stacking\n * and browser-managed accessibility.\n *\n * @element ore-dialog\n *\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} label - Dialog title (also used as aria-label)\n * @attr {string} size - Size: 'sm' | 'md' | 'lg' | 'xl' | 'full'\n * @attr {boolean} dismissible - Show a close (×) button in the header\n * @attr {boolean} persistent - Prevent backdrop-click from closing\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} backdrop - Backdrop style: 'opaque' (default) | 'blur' | 'transparent'\n * @attr {string} elevation - Panel shadow: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'\n * @attr {string} padding - Padding: 'none' | 'sm' | 'md' | 'lg' | 'xl'\n *\n * @fires open-change - Fired when the dialog state changes. detail: { open, reason }\n *\n * @slot - Dialog body content\n * @slot header - Custom header content (replaces the default title + close layout)\n * @slot footer - Action buttons or additional content at the bottom\n *\n * @cssprop --dialog-bg - Panel background color\n * @cssprop --dialog-border-color - Panel border color\n * @cssprop --dialog-radius - Panel border radius\n * @cssprop --dialog-shadow - Panel drop shadow\n * @cssprop --dialog-padding - Padding for header, body, and footer sections\n * @cssprop --dialog-gap - Gap between footer action buttons\n * @cssprop --dialog-backdrop - Backdrop overlay color\n * @cssprop --dialog-max-width - Maximum panel width (overridden by size prop)\n *\n * @part dialog - Dialog root container.\n * @part overlay - Overlay backdrop element.\n * @part panel - Panel container.\n * @part header - Header container.\n * @part title - Title text element.\n * @part close - Close action control.\n * @part body - Body content container.\n * @part footer - Footer container.\n * @example\n * ```html\n * <ore-dialog label=\"Confirm action\" dismissible>\n * <p>Are you sure you want to delete this item?</p>\n * <div slot=\"footer\">\n * <ore-button variant=\"ghost\" id=\"cancel\">Cancel</ore-button>\n * <ore-button color=\"error\" id=\"confirm\">Delete</ore-button>\n * </div>\n * </ore-dialog>\n *\n * <script type=\"module\">\n * import '@vielzeug/refine/dialog';\n * const dialog = document.querySelector('ore-dialog');\n * document.querySelector('#open-btn').addEventListener('click', () => {\n * dialog.setAttribute('open', '');\n * });\n * document.querySelector('#cancel').addEventListener('click', () => {\n * dialog.removeAttribute('open');\n * });\n * </script>\n * ```\n */\n\nexport const DIALOG_TAG = 'ore-dialog' as const;\ndefine<OreDialogProps>(DIALOG_TAG, {\n props: {\n backdrop: prop.string<DialogBackdrop>(),\n 'default-open': prop.bool(false),\n dismissible: prop.bool(false),\n elevation: prop.string<DialogElevation>(),\n 'initial-focus': prop.string(),\n label: prop.string(),\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n padding: prop.string<PaddingSize>(),\n persistent: prop.bool(false),\n 'return-focus': prop.bool(true),\n rounded: prop.string<RoundedSize | ''>(),\n size: prop.oneOf(['sm', 'md', 'lg', 'xl', 'full'] as const, 'md'),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreDialogEvents>();\n const slots = useSlots();\n\n const dialogRef = ref<HTMLDialogElement>();\n const hasHeader = () => slots.has('header').value || !!props.label.value || props.dismissible.value;\n const hasFooter = () => slots.has('footer').value;\n\n const { closeWithAnimation, handleBackdropClick, overlay, requestClose, mount } = useDialogControl({\n defaultOpen: props['default-open'],\n dialogRef,\n getPanelEl: () => dialogRef.value?.querySelector<HTMLElement>('.panel'),\n host: el,\n initialFocus: props['initial-focus'],\n isPersistent: () => Boolean(props.persistent.value),\n onCleanup,\n onEvent,\n onNativeClose: (reason) => emit('open-change', { open: false, reason }),\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n openProp: props.open,\n performClose: () => {\n closeWithAnimation();\n },\n returnFocus: props['return-focus'],\n });\n\n const handleDismiss = () => {\n requestClose('trigger');\n };\n\n onMounted(() => {\n const dialog = dialogRef.value;\n\n if (!dialog) return;\n\n mount();\n onEvent(dialog, 'click', handleBackdropClick);\n\n return () => {\n overlay.dispose();\n };\n });\n\n return html`\n <dialog ref=${dialogRef} class=\"dialog\" part=\"dialog\" aria-label=\"${props.label}\" aria-modal=\"true\">\n <div class=\"overlay\" part=\"overlay\" aria-hidden=\"true\"></div>\n <div class=\"panel\" part=\"panel\" data-size=\"${props.size}\">\n <div class=\"header\" part=\"header\" ?hidden=${() => !hasHeader()}>\n <slot name=\"header\">\n <span class=\"title\" part=\"title\">${props.label}</span>\n </slot>\n <button\n class=\"close\"\n part=\"close\"\n type=\"button\"\n aria-label=\"Close dialog\"\n ?hidden=${() => !props.dismissible.value}\n @click=${handleDismiss}>\n <ore-icon name=\"x\" size=\"16\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n <div class=\"body\" part=\"body\">\n <slot></slot>\n </div>\n <div class=\"footer\" part=\"footer\" ?hidden=${() => !hasFooter()}>\n <slot name=\"footer\"></slot>\n </div>\n </div>\n </dialog>\n `;\n },\n styles: [roundedVariantMixin, coarsePointerMixin, componentStyles],\n});\n"],"mappings":"6eAuHA,IAAa,EAAa,aAC1B,EAAuB,EAAY,CACjC,MAAO,CACL,SAAU,EAAK,OAAuB,EACtC,eAAgB,EAAK,KAAK,EAAK,EAC/B,YAAa,EAAK,KAAK,EAAK,EAC5B,UAAW,EAAK,OAAwB,EACxC,gBAAiB,EAAK,OAAO,EAC7B,MAAO,EAAK,OAAO,EACnB,KAAM,CAAE,QAAS,IAAA,GAAkC,MAAO,CAAkB,EAC5E,QAAS,EAAK,OAAoB,EAClC,WAAY,EAAK,KAAK,EAAK,EAC3B,eAAgB,EAAK,KAAK,EAAI,EAC9B,QAAS,EAAK,OAAyB,EACvC,KAAM,EAAK,MAAM,CAAC,KAAM,KAAM,KAAM,KAAM,MAAM,EAAY,IAAI,CAClE,EACA,MAAM,EAAO,CACX,IAAM,EAAK,EAAQ,EACb,EAAO,EAAyB,EAChC,EAAQ,EAAS,EAEjB,EAAY,EAAuB,EACnC,cAAkB,EAAM,IAAI,QAAQ,CAAC,CAAC,OAAS,CAAC,CAAC,EAAM,MAAM,OAAS,EAAM,YAAY,MACxF,cAAkB,EAAM,IAAI,QAAQ,CAAC,CAAC,MAEtC,CAAE,qBAAoB,sBAAqB,UAAS,eAAc,SAAU,EAAiB,CACjG,YAAa,EAAM,gBACnB,YACA,eAAkB,EAAU,OAAO,cAA2B,QAAQ,EACtE,KAAM,EACN,aAAc,EAAM,iBACpB,iBAAoB,EAAQ,EAAM,WAAW,MAC7C,YACA,UACA,cAAgB,GAAW,EAAK,cAAe,CAAE,KAAM,GAAO,QAAO,CAAC,EACtE,OAAS,GAAW,EAAK,cAAe,CAAE,KAAM,GAAM,QAAO,CAAC,EAC9D,SAAU,EAAM,KAChB,iBAAoB,CAClB,EAAmB,CACrB,EACA,YAAa,EAAM,eACrB,CAAC,EAEK,kBAAsB,CAC1B,EAAa,SAAS,CACxB,EAeA,OAbA,MAAgB,CACd,IAAM,EAAS,EAAU,MAEpB,KAKL,OAHA,EAAM,EACN,EAAQ,EAAQ,QAAS,CAAmB,MAE/B,CACX,EAAQ,QAAQ,CAClB,CACF,CAAC,EAEM,CAAI;oBACK,EAAU,4CAA4C,EAAM,MAAM;;qDAEjC,EAAM,KAAK;0DACJ,CAAC,UAAU,EAAE;;iDAExB,EAAM,MAAM;;;;;;;4BAO/B,CAAC,EAAM,YAAY,MAAM;uBAChC,cAAc;;;;;;;0DAOuB,CAAC,UAAU,EAAE;;;;;KAMvE,EACA,OAAQ,CAAC,EAAqB,EAAoB,CAAe,CACnE,CAAC"}
|
|
1
|
+
{"version":3,"file":"dialog.js","names":[],"sources":["../src/overlay/dialog/dialog.ts"],"sourcesContent":["import { define, getHost, html, onCleanup, onEvent, onMounted, prop, ref, useEmit, useSlots } from '@vielzeug/ore';\n\nimport { type OverlayOpenChangeDetail, parseOptionalBool } from '../../core';\nimport { coarsePointerMixin, roundedVariantMixin } from '../../styles';\nimport { PADDING_SIZES, type PaddingSize, ROUNDED_SIZES } from '../../types';\nimport { useDialogControl } from '../shared/use-dialog';\n\nimport componentStyles from './dialog.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\n\ntype DialogSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';\nconst DIALOG_BACKDROPS = ['opaque', 'blur', 'transparent'] as const;\ntype DialogBackdrop = (typeof DIALOG_BACKDROPS)[number];\nconst DIALOG_ELEVATIONS = ['none', 'sm', 'md', 'lg', 'xl', '2xl'] as const;\ntype DialogElevation = (typeof DIALOG_ELEVATIONS)[number];\nconst DIALOG_ROUNDED = ['', ...ROUNDED_SIZES] as const;\n\n/** Dialog component properties */\n\nexport type OreDialogEvents = {\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreDialogProps = {\n /** Backdrop style: 'blur' (default): dark overlay + blur; 'opaque': dark overlay only; 'transparent': no overlay */\n backdrop?: DialogBackdrop;\n /** Initial uncontrolled open state. Ignored when `open` is set. */\n 'default-open'?: boolean;\n /** Show a close (×) button in the header */\n dismissible?: boolean;\n /** Panel shadow elevation: defaults to 'xl' */\n elevation?: DialogElevation;\n /**\n * CSS selector for the element inside the dialog that should receive focus when the dialog opens.\n * Defaults to the first focusable element (browser default).\n * @example 'input[name=\"email\"]' | '#confirm-btn'\n */\n 'initial-focus'?: string;\n /** Dialog title shown in the header (used as aria-label when no header slot) */\n label?: string;\n /** Controls the open state of the dialog */\n open?: boolean;\n /** Internal padding size */\n padding?: PaddingSize;\n /** When true, clicking the backdrop does not close the dialog */\n persistent?: boolean;\n /**\n * When true (default), the focus returns to the element that triggered the dialog after it closes.\n * Set to false if you want to manage focus manually.\n */\n 'return-focus'?: boolean;\n /** Border radius */\n rounded?: (typeof DIALOG_ROUNDED)[number];\n /** Dialog size */\n size?: DialogSize;\n};\n\n/**\n * A modal dialog that traps focus, blocks page interaction, and dismisses on\n * `Escape`. Built on the native `<dialog>` element for correct top-layer stacking\n * and browser-managed accessibility.\n *\n * @element ore-dialog\n *\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} label - Dialog title (also used as aria-label)\n * @attr {string} size - Size: 'sm' | 'md' | 'lg' | 'xl' | 'full'\n * @attr {boolean} dismissible - Show a close (×) button in the header\n * @attr {boolean} persistent - Prevent backdrop-click from closing\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} backdrop - Backdrop style: 'opaque' (default) | 'blur' | 'transparent'\n * @attr {string} elevation - Panel shadow: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'\n * @attr {string} padding - Padding: 'none' | 'sm' | 'md' | 'lg' | 'xl'\n *\n * @fires open-change - Fired when the dialog state changes. detail: { open, reason }\n *\n * @slot - Dialog body content\n * @slot header - Custom header content (replaces the default title + close layout)\n * @slot footer - Action buttons or additional content at the bottom\n *\n * @cssprop --dialog-bg - Panel background color\n * @cssprop --dialog-border-color - Panel border color\n * @cssprop --dialog-radius - Panel border radius\n * @cssprop --dialog-shadow - Panel drop shadow\n * @cssprop --dialog-padding - Padding for header, body, and footer sections\n * @cssprop --dialog-gap - Gap between footer action buttons\n * @cssprop --dialog-backdrop - Backdrop overlay color\n * @cssprop --dialog-max-width - Maximum panel width (overridden by size prop)\n *\n * @part dialog - Dialog root container.\n * @part overlay - Overlay backdrop element.\n * @part panel - Panel container.\n * @part header - Header container.\n * @part title - Title text element.\n * @part close - Close action control.\n * @part body - Body content container.\n * @part footer - Footer container.\n * @example\n * ```html\n * <ore-dialog label=\"Confirm action\" dismissible>\n * <p>Are you sure you want to delete this item?</p>\n * <div slot=\"footer\">\n * <ore-button variant=\"ghost\" id=\"cancel\">Cancel</ore-button>\n * <ore-button color=\"error\" id=\"confirm\">Delete</ore-button>\n * </div>\n * </ore-dialog>\n *\n * <script type=\"module\">\n * import '@vielzeug/refine/dialog';\n * const dialog = document.querySelector('ore-dialog');\n * document.querySelector('#open-btn').addEventListener('click', () => {\n * dialog.setAttribute('open', '');\n * });\n * document.querySelector('#cancel').addEventListener('click', () => {\n * dialog.removeAttribute('open');\n * });\n * </script>\n * ```\n */\n\nexport const DIALOG_TAG = 'ore-dialog' as const;\ndefine<OreDialogProps>(DIALOG_TAG, {\n props: {\n backdrop: prop.oneOf(DIALOG_BACKDROPS),\n 'default-open': prop.bool(false),\n dismissible: prop.bool(false),\n elevation: prop.oneOf(DIALOG_ELEVATIONS),\n 'initial-focus': prop.string(),\n label: prop.string(),\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n padding: prop.oneOf(PADDING_SIZES),\n persistent: prop.bool(false),\n 'return-focus': prop.bool(true),\n rounded: prop.oneOf(DIALOG_ROUNDED),\n size: prop.oneOf(['sm', 'md', 'lg', 'xl', 'full'] as const, 'md'),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreDialogEvents>();\n const slots = useSlots();\n\n const dialogRef = ref<HTMLDialogElement>();\n const hasHeader = () => slots.has('header').value || !!props.label.value || props.dismissible.value;\n const hasFooter = () => slots.has('footer').value;\n\n const { closeWithAnimation, handleBackdropClick, overlay, requestClose, mount } = useDialogControl({\n defaultOpen: props['default-open'],\n dialogRef,\n getPanelEl: () => dialogRef.value?.querySelector<HTMLElement>('.panel'),\n host: el,\n initialFocus: props['initial-focus'],\n isPersistent: () => Boolean(props.persistent.value),\n onCleanup,\n onEvent,\n onNativeClose: (reason) => emit('open-change', { open: false, reason }),\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n openProp: props.open,\n performClose: () => {\n closeWithAnimation();\n },\n returnFocus: props['return-focus'],\n });\n\n const handleDismiss = () => {\n requestClose('trigger');\n };\n\n onMounted(() => {\n const dialog = dialogRef.value;\n\n if (!dialog) return;\n\n mount();\n onEvent(dialog, 'click', handleBackdropClick);\n\n return () => {\n overlay.dispose();\n };\n });\n\n return html`\n <dialog ref=${dialogRef} class=\"dialog\" part=\"dialog\" aria-label=\"${props.label}\" aria-modal=\"true\">\n <div class=\"overlay\" part=\"overlay\" aria-hidden=\"true\"></div>\n <div class=\"panel\" part=\"panel\" data-size=\"${props.size}\">\n <div class=\"header\" part=\"header\" ?hidden=${() => !hasHeader()}>\n <slot name=\"header\">\n <span class=\"title\" part=\"title\">${props.label}</span>\n </slot>\n <button\n class=\"close\"\n part=\"close\"\n type=\"button\"\n aria-label=\"Close dialog\"\n ?hidden=${() => !props.dismissible.value}\n @click=${handleDismiss}>\n <ore-icon name=\"x\" size=\"16\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n <div class=\"body\" part=\"body\">\n <slot></slot>\n </div>\n <div class=\"footer\" part=\"footer\" ?hidden=${() => !hasFooter()}>\n <slot name=\"footer\"></slot>\n </div>\n </div>\n </dialog>\n `;\n },\n styles: [roundedVariantMixin, coarsePointerMixin, componentStyles],\n});\n"],"mappings":"kjBAYA,IAAM,EAAmB,CAAC,SAAU,OAAQ,aAAa,EAEnD,EAAoB,CAAC,OAAQ,KAAM,KAAM,KAAM,KAAM,KAAK,EAE1D,EAAiB,CAAC,GAAI,GAAG,CAAa,EA0G/B,EAAa,aAC1B,EAAuB,EAAY,CACjC,MAAO,CACL,SAAU,EAAK,MAAM,CAAgB,EACrC,eAAgB,EAAK,KAAK,EAAK,EAC/B,YAAa,EAAK,KAAK,EAAK,EAC5B,UAAW,EAAK,MAAM,CAAiB,EACvC,gBAAiB,EAAK,OAAO,EAC7B,MAAO,EAAK,OAAO,EACnB,KAAM,CAAE,QAAS,IAAA,GAAkC,MAAO,CAAkB,EAC5E,QAAS,EAAK,MAAM,CAAa,EACjC,WAAY,EAAK,KAAK,EAAK,EAC3B,eAAgB,EAAK,KAAK,EAAI,EAC9B,QAAS,EAAK,MAAM,CAAc,EAClC,KAAM,EAAK,MAAM,CAAC,KAAM,KAAM,KAAM,KAAM,MAAM,EAAY,IAAI,CAClE,EACA,MAAM,EAAO,CACX,IAAM,EAAK,EAAQ,EACb,EAAO,EAAyB,EAChC,EAAQ,EAAS,EAEjB,EAAY,EAAuB,EACnC,cAAkB,EAAM,IAAI,QAAQ,CAAC,CAAC,OAAS,CAAC,CAAC,EAAM,MAAM,OAAS,EAAM,YAAY,MACxF,cAAkB,EAAM,IAAI,QAAQ,CAAC,CAAC,MAEtC,CAAE,qBAAoB,sBAAqB,UAAS,eAAc,SAAU,EAAiB,CACjG,YAAa,EAAM,gBACnB,YACA,eAAkB,EAAU,OAAO,cAA2B,QAAQ,EACtE,KAAM,EACN,aAAc,EAAM,iBACpB,iBAAoB,EAAQ,EAAM,WAAW,MAC7C,YACA,UACA,cAAgB,GAAW,EAAK,cAAe,CAAE,KAAM,GAAO,QAAO,CAAC,EACtE,OAAS,GAAW,EAAK,cAAe,CAAE,KAAM,GAAM,QAAO,CAAC,EAC9D,SAAU,EAAM,KAChB,iBAAoB,CAClB,EAAmB,CACrB,EACA,YAAa,EAAM,eACrB,CAAC,EAEK,kBAAsB,CAC1B,EAAa,SAAS,CACxB,EAeA,OAbA,MAAgB,CACd,IAAM,EAAS,EAAU,MAEpB,KAKL,OAHA,EAAM,EACN,EAAQ,EAAQ,QAAS,CAAmB,MAE/B,CACX,EAAQ,QAAQ,CAClB,CACF,CAAC,EAEM,CAAI;oBACK,EAAU,4CAA4C,EAAM,MAAM;;qDAEjC,EAAM,KAAK;0DACJ,CAAC,UAAU,EAAE;;iDAExB,EAAM,MAAM;;;;;;;4BAO/B,CAAC,EAAM,YAAY,MAAM;uBAChC,cAAc;;;;;;;0DAOuB,CAAC,UAAU,EAAE;;;;;KAMvE,EACA,OAAQ,CAAC,EAAqB,EAAoB,CAAe,CACnE,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Readable } from '@vielzeug/ripple';
|
|
2
|
-
import type
|
|
2
|
+
import { type ComponentSize, type VisualVariant } from '../../types';
|
|
3
|
+
declare const ACCORDION_SELECTION_MODES: readonly ["single", "multiple"];
|
|
3
4
|
/** Context provided by ore-accordion to its ore-accordion-item children. */
|
|
4
5
|
export type AccordionContext = {
|
|
5
6
|
selectionMode: Readable<'single' | 'multiple' | undefined>;
|
|
@@ -16,7 +17,7 @@ export type OreAccordionEvents = {
|
|
|
16
17
|
};
|
|
17
18
|
export type OreAccordionProps = {
|
|
18
19
|
/** Selection mode (single = only one opens, multiple = multiple can be open) */
|
|
19
|
-
selectionMode?:
|
|
20
|
+
selectionMode?: (typeof ACCORDION_SELECTION_MODES)[number];
|
|
20
21
|
/** Size for all items (propagated via context) */
|
|
21
22
|
size?: ComponentSize;
|
|
22
23
|
/** Visual variant for all items (propagated via context) */
|
|
@@ -59,4 +60,5 @@ export type OreAccordionProps = {
|
|
|
59
60
|
* ```
|
|
60
61
|
*/
|
|
61
62
|
export declare const ACCORDION_TAG: "ore-accordion";
|
|
63
|
+
export {};
|
|
62
64
|
//# sourceMappingURL=accordion.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accordion.d.ts","sourceRoot":"","sources":["../../../src/disclosure/accordion/accordion.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"accordion.d.ts","sourceRoot":"","sources":["../../../src/disclosure/accordion/accordion.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAAmB,KAAK,aAAa,EAAmB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEvG,QAAA,MAAM,yBAAyB,iCAAkC,CAAC;AAIlE,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,QAAQ,CAAC,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC,CAAC;IAC3D,IAAI,EAAE,QAAQ,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IAC1C,OAAO,EAAE,QAAQ,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;CAC9C,CAAC;AACF,+CAA+C;AAC/C,eAAO,MAAM,aAAa,wDAAsD,CAAC;AAEjF,qCAAqC;AAErC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE;QAAE,YAAY,EAAE,WAAW,CAAA;KAAE,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gFAAgF;IAChF,aAAa,CAAC,EAAE,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;IAC3D,kDAAkD;IAClD,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,eAAO,MAAM,aAAa,EAAG,eAAwB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ComponentSize, type VisualVariant } from '../../types';
|
|
2
2
|
export { ICON_TAG } from '../../content/icon/icon';
|
|
3
3
|
/** Accordion item component properties */
|
|
4
4
|
export type OreAccordionItemEvents = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accordion-item.d.ts","sourceRoot":"","sources":["../../../src/disclosure/accordion-item/accordion-item.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"accordion-item.d.ts","sourceRoot":"","sources":["../../../src/disclosure/accordion-item/accordion-item.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,KAAK,aAAa,EAAmB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAIvG,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,0CAA0C;AAE1C,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC;IACnD,MAAM,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,yCAAyC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB;IAChB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,eAAO,MAAM,kBAAkB,EAAG,oBAA6B,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ComponentSize, type ThemeColor, type VisualVariant } from '../../types';
|
|
2
2
|
export type OreTabItemProps = {
|
|
3
3
|
/** Whether this tab is currently selected (set by ore-tabs) */
|
|
4
4
|
active?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tab-item.d.ts","sourceRoot":"","sources":["../../../src/disclosure/tab-item/tab-item.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"tab-item.d.ts","sourceRoot":"","sources":["../../../src/disclosure/tab-item/tab-item.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,UAAU,EAEf,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAIrB,MAAM,MAAM,eAAe,GAAG;IAC5B,+DAA+D;IAC/D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,uBAAuB;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gDAAgD;IAChD,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,qCAAqC;IACrC,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,YAAY,EAAG,cAAuB,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Readable } from '@vielzeug/ripple';
|
|
2
|
-
import type
|
|
2
|
+
import { type ComponentSize, type ThemeColor, type VisualVariant } from '../../types';
|
|
3
3
|
/** Context provided by ore-tabs to its ore-tab-item and ore-tab-panel children. */
|
|
4
4
|
export type TabsContext = {
|
|
5
5
|
color: Readable<ThemeColor | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../../src/disclosure/tabs/tabs.ts"],"names":[],"mappings":"AAaA,OAAO,EAAY,KAAK,QAAQ,EAAiB,MAAM,kBAAkB,CAAC;AAI1E,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../../src/disclosure/tabs/tabs.ts"],"names":[],"mappings":"AAaA,OAAO,EAAY,KAAK,QAAQ,EAAiB,MAAM,kBAAkB,CAAC;AAI1E,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAmB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAGvG,mFAAmF;AACnF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,QAAQ,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IACxC,OAAO,EAAE,QAAQ,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;IACzC,WAAW,EAAE,QAAQ,CAAC,YAAY,GAAG,UAAU,CAAC,CAAC;IACjD,IAAI,EAAE,QAAQ,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IAC1C,KAAK,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACpC,OAAO,EAAE,QAAQ,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;CAC9C,CAAC;AACF,0CAA0C;AAC1C,eAAO,MAAM,QAAQ,mDAA4C,CAAC;AAElE,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B,kBAAkB;IAClB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,gFAAgF;IAChF,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,wHAAwH;IACxH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACxC,qBAAqB;IACrB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,QAAQ,EAAG,UAAmB,CAAC"}
|
package/dist/drawer.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/theme.css.cjs"),n=require("./core/parse.cjs"),r=require("./icon.cjs"),i=require("./overlay/shared/use-dialog.cjs"),a=require("./overlay/drawer/drawer.css.cjs");let o=require("@vielzeug/ripple"),s=require("@vielzeug/ore"),c=require("@vielzeug/gesture");var l={bottom:{axis:`y`,closingDistance:e=>Math.max(0,e),translate:e=>`translateY(${e}px)`},left:{axis:`x`,closingDistance:e=>Math.max(0,-e),translate:e=>`translateX(${e}px)`},right:{axis:`x`,closingDistance:e=>Math.max(0,e),translate:e=>`translateX(${e}px)`},top:{axis:`y`,closingDistance:e=>Math.max(0,-e),translate:e=>`translateY(${e}px)`}},
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/theme.css.cjs"),n=require("./core/parse.cjs"),r=require("./icon.cjs"),i=require("./overlay/shared/use-dialog.cjs"),a=require("./overlay/drawer/drawer.css.cjs");let o=require("@vielzeug/ripple"),s=require("@vielzeug/ore"),c=require("@vielzeug/gesture");var l=[`sm`,`lg`,`full`],u=[`opaque`,`blur`,`transparent`],d={bottom:{axis:`y`,closingDistance:e=>Math.max(0,e),translate:e=>`translateY(${e}px)`},left:{axis:`x`,closingDistance:e=>Math.max(0,-e),translate:e=>`translateX(${e}px)`},right:{axis:`x`,closingDistance:e=>Math.max(0,e),translate:e=>`translateX(${e}px)`},top:{axis:`y`,closingDistance:e=>Math.max(0,-e),translate:e=>`translateY(${e}px)`}},f=`ore-drawer`;(0,s.define)(f,{props:{backdrop:s.prop.oneOf(u),"default-open":s.prop.bool(!1),dismissible:s.prop.bool(!0),"drag-handle-placement":s.prop.oneOf([`outside`,`inset`],`outside`),"initial-focus":s.prop.string(),label:s.prop.string(),open:{default:void 0,parse:n.parseOptionalBool},persistent:s.prop.bool(!1),placement:s.prop.oneOf([`left`,`right`,`top`,`bottom`],`right`),"return-focus":s.prop.bool(!0),size:s.prop.oneOf(l),title:s.prop.string()},setup(e){let t=(0,s.getHost)(),n=(0,s.useEmit)(),r=(0,s.useSlots)(),a=(0,s.createId)(`drawer-label`),l=(0,s.ref)(),u=(0,s.ref)(),f=(0,o.signal)(!1),p,m,getHeaderText=()=>e.label.value??e.title.value??``,hasHeaderTitle=()=>r.has(`header`).value||!!getHeaderText(),hasHeader=()=>hasHeaderTitle()||e.dismissible.value,hasFooter=()=>r.has(`footer`).value,getPlacement=()=>e.placement.value||`right`,getSwipeConfig=()=>d[getPlacement()],getSnapThreshold=(e,t)=>{let n=t===`x`?e.offsetWidth:e.offsetHeight;return Math.min(96,Math.max(36,n*.18))},shouldCommitSwipeClose=(e,t)=>getSwipeConfig().closingDistance(e)>=t,finalizeSwipeClose=e=>{if(!f.value)return;m&&=(clearTimeout(m),void 0);let t=l.value;if(!t){f.value=!1;return}t.style.transition=`none`,e.style.opacity=`0`,e.style.visibility=`hidden`,t.offsetWidth,v(`swipe`)},startSwipeClose=(e,t,n)=>{if(f.value)return;f.value=!0;let r=t.axis===`x`?e.offsetWidth:e.offsetHeight,i=n>=0?Math.max(n,r):Math.min(n,-r),a=t.translate(i);e.style.transition=`transform 180ms cubic-bezier(0.2, 0.9, 0.2, 1), opacity 180ms ease-out`,e.offsetWidth;let onExitTransitionEnd=t=>{t.target===e&&t.propertyName===`transform`&&(e.removeEventListener(`transitionend`,onExitTransitionEnd),finalizeSwipeClose(e))};e.addEventListener(`transitionend`,onExitTransitionEnd);let o=parseFloat(getComputedStyle(e).transitionDuration)*1e3;m=setTimeout(()=>{e.removeEventListener(`transitionend`,onExitTransitionEnd),finalizeSwipeClose(e)},o+50),e.style.transform=a,e.style.opacity=`0.2`},resetPanelDragStyles=e=>{m&&=(clearTimeout(m),void 0),e.style.transition=``,e.style.transform=``,e.style.opacity=``,e.style.visibility=``,f.value=!1},handleSwipeEnd=({distance:e,reason:t})=>{let n=u.value;if(!n)return;let r=getSnapThreshold(n,getSwipeConfig().axis);if(t===`release`&&shouldCommitSwipeClose(e,r)){startSwipeClose(n,getSwipeConfig(),e);return}resetPanelDragStyles(n)},{closeWithAnimation:h,mount:g,overlay:_,requestClose:v}=i.useDialogControl({beforeOpen:e=>{let t=u.value;t&&resetPanelDragStyles(t)},closeRequestDetail:t=>({placement:e.placement.value??`right`}),defaultOpen:e[`default-open`],dialogRef:l,getPanelEl:()=>u.value,host:t,initialFocus:e[`initial-focus`],isPersistent:()=>!!e.persistent.value,onCleanup:s.onCleanup,onEvent:s.onEvent,onNativeClose:e=>{let t=u.value;t&&e!==`swipe`&&resetPanelDragStyles(t);let r=l.value;r&&(r.style.transition=``),n(`open-change`,{open:!1,reason:e})},onOpen:e=>n(`open-change`,{open:!0,reason:e}),openProp:e.open,performClose:(e,t)=>{t===`swipe`?e.close():h()},returnFocus:e[`return-focus`]}),handleCloseButtonClick=()=>{v(`trigger`)};return(0,s.onMounted)(()=>{let n=l.value;if(!n)return;let r=t;r.show=()=>{_.open(`programmatic`)},r.hide=()=>{_.close(`programmatic`)};let handleBackdropClick=t=>{e.persistent.value||p?.active||f.value||t.target===n&&v(`outsideClick`)};g(),(0,s.onEvent)(n,`click`,handleBackdropClick);let i=u.value?.querySelector(`[part="drag-handle"]`);return i&&(p=(0,c.createPanGesture)(i,{axis:()=>getSwipeConfig().axis,disabled:()=>f.value,onEnd:handleSwipeEnd,onMove:({distance:e})=>{let t=u.value;if(!t)return;let n=getSwipeConfig(),r=getSnapThreshold(t,n.axis),i=n.closingDistance(e),a=Math.min(i/r,1);t.style.transition=`none`,t.style.transform=n.translate(e),t.style.opacity=String(1-a*.4)}})),()=>{p?.dispose(),p=void 0,_.dispose()}}),s.html`
|
|
2
2
|
<dialog
|
|
3
|
-
ref=${
|
|
3
|
+
ref=${l}
|
|
4
4
|
aria-modal="true"
|
|
5
5
|
aria-label="${()=>e.label.value??null}"
|
|
6
6
|
aria-labelledby="${()=>e.label.value?null:a}">
|
|
7
|
-
<div class="panel" part="panel" ref=${
|
|
7
|
+
<div class="panel" part="panel" ref=${u}>
|
|
8
8
|
<div class="drag-handle" part="drag-handle" aria-label="Drag to close" role="button"></div>
|
|
9
9
|
<div class="header" part="header" ?hidden=${()=>!hasHeader()}>
|
|
10
10
|
<span class="header-title" id="${a}" ?hidden=${()=>!hasHeaderTitle()}>
|
|
@@ -28,5 +28,5 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
28
28
|
</div>
|
|
29
29
|
</div>
|
|
30
30
|
</dialog>
|
|
31
|
-
`},styles:[t.forcedColorsMixin,e.coarsePointerMixin,e.reducedMotionMixin,a.default]}),exports.DRAWER_TAG=
|
|
31
|
+
`},styles:[t.forcedColorsMixin,e.coarsePointerMixin,e.reducedMotionMixin,a.default]}),exports.DRAWER_TAG=f,exports.ICON_TAG=r.ICON_TAG;
|
|
32
32
|
//# sourceMappingURL=drawer.cjs.map
|
package/dist/drawer.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawer.cjs","names":[],"sources":["../src/overlay/drawer/drawer.ts"],"sourcesContent":["import { createPanGesture, type PanAxis, type PanGesture, type PanGestureEndDetail } from '@vielzeug/gesture';\nimport {\n createId,\n define,\n getHost,\n html,\n onCleanup,\n onEvent,\n onMounted,\n prop,\n ref,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { signal } from '@vielzeug/ripple';\n\nimport { type OverlayOpenChangeDetail, parseOptionalBool } from '../../core';\n\nimport { coarsePointerMixin, forcedColorsMixin, reducedMotionMixin } from '../../styles';\nimport { useDialogControl } from '../shared/use-dialog';\nimport styles from './drawer.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\n\ntype DrawerPlacement = 'left' | 'right' | 'top' | 'bottom';\ntype DrawerSize = 'sm' | 'lg' | 'full';\ntype DrawerBackdrop = 'opaque' | 'blur' | 'transparent';\ntype DrawerDragHandlePlacement = 'outside' | 'inset';\ntype DrawerSwipeConfig = {\n axis: PanAxis;\n closingDistance: (distance: number) => number;\n translate: (distance: number) => string;\n};\n\nconst drawerSwipeConfig: Record<DrawerPlacement, DrawerSwipeConfig> = {\n bottom: {\n axis: 'y',\n closingDistance: (distance) => Math.max(0, distance),\n translate: (distance) => `translateY(${distance}px)`,\n },\n left: {\n axis: 'x',\n closingDistance: (distance) => Math.max(0, -distance),\n translate: (distance) => `translateX(${distance}px)`,\n },\n right: {\n axis: 'x',\n closingDistance: (distance) => Math.max(0, distance),\n translate: (distance) => `translateX(${distance}px)`,\n },\n top: {\n axis: 'y',\n closingDistance: (distance) => Math.max(0, -distance),\n translate: (distance) => `translateY(${distance}px)`,\n },\n};\n\n/** Element interface exposing the imperative API for `ore-drawer`. */\nexport interface DrawerElement extends HTMLElement, Omit<OreDrawerProps, 'title'> {\n /** Programmatically close the drawer with the exit animation. */\n hide(): void;\n /** Programmatically open the drawer. Equivalent to setting `open`. */\n show(): void;\n}\n\n/** Drawer component properties */\n\nexport type OreDrawerEvents = {\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreDrawerProps = {\n /** Backdrop style: 'opaque' (default), 'blur', or 'transparent' */\n backdrop?: DrawerBackdrop;\n /** Initial uncontrolled open state. Ignored when `open` is set. */\n 'default-open'?: boolean;\n /** Show the close (×) button in the header (default: true) */\n dismissible?: boolean;\n /** Drag handle position used for swipe-to-close gestures */\n 'drag-handle-placement'?: DrawerDragHandlePlacement;\n /**\n * CSS selector for the element inside the drawer that should receive focus on open.\n * Defaults to native dialog focus management (first focusable element).\n * @example '#submit-btn' | 'input[name=\"email\"]'\n */\n 'initial-focus'?: string;\n /**\n * Invisible accessible label for the dialog (`aria-label`).\n * Use when the drawer has no visible title (e.g. image-only content).\n * When omitted, `aria-labelledby` points to the visible header title instead.\n */\n label?: string;\n /** Controlled open state */\n open?: boolean;\n /** When true, backdrop clicks do not close the drawer (default: false) */\n persistent?: boolean;\n /** Side from which the drawer slides in */\n placement?: DrawerPlacement;\n /**\n * When true (default), focus returns to the triggering element after the drawer closes.\n * Set to false to manage focus manually.\n */\n 'return-focus'?: boolean;\n /** Drawer width/height preset */\n size?: DrawerSize;\n /**\n * Visible title text rendered inside the header.\n * Used as the dialog's accessible label via `aria-labelledby` when `label` is not set.\n */\n title?: string;\n};\n\n/**\n * A panel that slides in from any edge of the screen, built on the native `<dialog>` element.\n *\n * @element ore-drawer\n *\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} placement - 'left' | 'right' (default) | 'top' | 'bottom'\n * @attr {string} size - 'sm' | 'lg' | 'full'\n * @attr {string} title - Visible header title text\n * @attr {string} label - Invisible aria-label (for drawers without a visible title)\n * @attr {boolean} dismissible - Show the close (×) button (default: true)\n * @attr {string} drag-handle-placement - 'outside' (default) | 'inset'\n * @attr {string} backdrop - Backdrop style: 'opaque' (default) | 'blur' | 'transparent'\n * @attr {boolean} persistent - Prevent backdrop-click from closing (default: false)\n *\n * @fires open-change - When the drawer state changes. detail: { open, reason }\n *\n * @slot header - Drawer header content\n * @slot - Main body content\n * @slot footer - Drawer footer content\n *\n * @cssprop --drawer-backdrop-bg - Backdrop background color\n * @cssprop --drawer-bg - Panel background color\n * @cssprop --drawer-size - Panel width (horizontal) or height (vertical)\n * @cssprop --drawer-shadow - Panel box-shadow\n * @cssprop --drawer-panel-blur - Panel backdrop blur amount\n *\n * @part drag-handle - Drawer drag handle.\n * @part panel - Panel container.\n * @part header - Header container.\n * @part close-btn - Close button.\n * @part body - Body content container.\n * @part footer - Footer container.\n * @example\n * ```html\n * <!-- With visible title -->\n * <ore-drawer open title=\"Settings\" placement=\"right\">\n * <p>Settings content here.</p>\n * </ore-drawer>\n *\n * <!-- With custom header slot -->\n * <ore-drawer open placement=\"right\">\n * <span slot=\"header\">Settings</span>\n * <p>Settings content here.</p>\n * </ore-drawer>\n * ```\n */\n\nexport const DRAWER_TAG = 'ore-drawer' as const;\ndefine<OreDrawerProps>(DRAWER_TAG, {\n props: {\n backdrop: prop.string<DrawerBackdrop>(),\n 'default-open': prop.bool(false),\n dismissible: prop.bool(true),\n 'drag-handle-placement': prop.oneOf(['outside', 'inset'] as const, 'outside'),\n 'initial-focus': prop.string(),\n label: prop.string(),\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n persistent: prop.bool(false),\n placement: prop.oneOf(['left', 'right', 'top', 'bottom'] as const, 'right'),\n 'return-focus': prop.bool(true),\n size: prop.string<DrawerSize>(),\n title: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreDrawerEvents>();\n const slots = useSlots();\n\n const drawerLabelId = createId('drawer-label');\n const dialogRef = ref<HTMLDialogElement>();\n const panelRef = ref<HTMLDivElement>();\n\n // Drag-to-close state\n const isSwipeClosing = signal(false);\n let swipe: PanGesture | undefined;\n let swipeCloseTimer: ReturnType<typeof setTimeout> | undefined;\n\n const getHeaderText = () => props.label.value ?? props.title.value ?? '';\n const hasHeaderTitle = () => slots.has('header').value || !!getHeaderText();\n\n // Header is visible when there is slot content, a text label, or a close button.\n const hasHeader = () => hasHeaderTitle() || props.dismissible.value;\n const hasFooter = () => slots.has('footer').value;\n\n const getPlacement = (): DrawerPlacement => props.placement.value || 'right';\n\n const getSwipeConfig = (): DrawerSwipeConfig => drawerSwipeConfig[getPlacement()];\n\n const getSnapThreshold = (panel: HTMLElement, axis: PanAxis) => {\n const panelSize = axis === 'x' ? panel.offsetWidth : panel.offsetHeight;\n\n return Math.min(96, Math.max(36, panelSize * 0.18));\n };\n\n const shouldCommitSwipeClose = (distance: number, threshold: number) => {\n return getSwipeConfig().closingDistance(distance) >= threshold;\n };\n\n const finalizeSwipeClose = (panel: HTMLElement) => {\n if (!isSwipeClosing.value) return;\n\n if (swipeCloseTimer) {\n clearTimeout(swipeCloseTimer);\n swipeCloseTimer = undefined;\n }\n\n const dialog = dialogRef.value;\n\n if (!dialog) {\n isSwipeClosing.value = false;\n\n return;\n }\n\n dialog.style.transition = 'none';\n panel.style.opacity = '0';\n panel.style.visibility = 'hidden';\n void dialog.offsetWidth;\n\n requestClose('swipe');\n };\n\n const startSwipeClose = (panel: HTMLElement, swipe: DrawerSwipeConfig, committedDistance: number) => {\n if (isSwipeClosing.value) return;\n\n isSwipeClosing.value = true;\n\n const panelSize = swipe.axis === 'x' ? panel.offsetWidth : panel.offsetHeight;\n // Preserve overshoot so closing continues from the dragged position instead of snapping back.\n const exitDistance =\n committedDistance >= 0 ? Math.max(committedDistance, panelSize) : Math.min(committedDistance, -panelSize);\n const exitTransform = swipe.translate(exitDistance);\n\n // Commit the current drag position first so the magnetic snap continues\n // from the finger instead of jumping back to rest.\n panel.style.transition = 'transform 180ms cubic-bezier(0.2, 0.9, 0.2, 1), opacity 180ms ease-out';\n void panel.offsetWidth;\n\n const onExitTransitionEnd = (ev: TransitionEvent) => {\n if (ev.target !== panel || ev.propertyName !== 'transform') return;\n\n panel.removeEventListener('transitionend', onExitTransitionEnd);\n finalizeSwipeClose(panel);\n };\n\n panel.addEventListener('transitionend', onExitTransitionEnd);\n\n const durationMs = parseFloat(getComputedStyle(panel).transitionDuration) * 1000;\n\n swipeCloseTimer = setTimeout(() => {\n panel.removeEventListener('transitionend', onExitTransitionEnd);\n finalizeSwipeClose(panel);\n }, durationMs + 50);\n\n panel.style.transform = exitTransform;\n panel.style.opacity = '0.2';\n };\n\n const resetPanelDragStyles = (panel: HTMLElement) => {\n if (swipeCloseTimer) {\n clearTimeout(swipeCloseTimer);\n swipeCloseTimer = undefined;\n }\n\n // Re-enable transitions so the snap-back or exit animates.\n panel.style.transition = '';\n panel.style.transform = '';\n panel.style.opacity = '';\n panel.style.visibility = '';\n isSwipeClosing.value = false;\n };\n\n const handleSwipeEnd = ({ distance, reason }: PanGestureEndDetail): void => {\n const panel = panelRef.value;\n\n if (!panel) return;\n\n const threshold = getSnapThreshold(panel, getSwipeConfig().axis);\n\n if (reason === 'release' && shouldCommitSwipeClose(distance, threshold)) {\n startSwipeClose(panel, getSwipeConfig(), distance);\n\n return;\n }\n\n resetPanelDragStyles(panel);\n };\n\n // ────────────────────────────────────────────────────────────────\n // Overlay State Management\n // ────────────────────────────────────────────────────────────────\n\n const { closeWithAnimation, mount, overlay, requestClose } = useDialogControl({\n beforeOpen: (dialog) => {\n // Clear any inline drag styles from a previous swipe-close so the CSS\n // entry animation starts from the correct base state.\n const panel = panelRef.value;\n\n if (panel) resetPanelDragStyles(panel);\n\n void dialog;\n },\n closeRequestDetail: (_reason) => ({ placement: props.placement.value ?? 'right' }),\n defaultOpen: props['default-open'],\n dialogRef,\n getPanelEl: () => panelRef.value,\n host: el,\n initialFocus: props['initial-focus'],\n isPersistent: () => Boolean(props.persistent.value),\n onCleanup,\n onEvent,\n onNativeClose: (reason) => {\n const panelEl = panelRef.value;\n\n // For swipe-close, keep the panel hidden and off-screen until the next\n // open cycle: resetting inline styles during native close can produce\n // a visible frame at the rest position on some browsers.\n if (panelEl && reason !== 'swipe') resetPanelDragStyles(panelEl);\n\n const dialog = dialogRef.value;\n\n if (dialog) dialog.style.transition = '';\n\n emit('open-change', { open: false, reason });\n },\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n openProp: props.open,\n performClose: (dialog, reason) => {\n if (reason === 'swipe') {\n dialog.close();\n } else {\n closeWithAnimation();\n }\n },\n returnFocus: props['return-focus'],\n });\n\n const handleCloseButtonClick = () => {\n requestClose('trigger');\n };\n\n // ────────────────────────────────────────────────────────────────\n // Lifecycle: Setup Drawer Integration\n // ────────────────────────────────────────────────────────────────\n\n onMounted(() => {\n const dialog = dialogRef.value;\n\n if (!dialog) return;\n\n // Expose imperative API\n const drawerEl = el as DrawerElement;\n\n drawerEl.show = () => {\n overlay.open('programmatic');\n };\n\n drawerEl.hide = () => {\n overlay.close('programmatic');\n };\n\n // ────────────────────────────────────────────────────────────\n // Event Handlers: Backdrop Click\n // ────────────────────────────────────────────────────────────\n // `mount()` registers the open-prop watcher and the shared native\n // `close`/`cancel` handling (Escape). Drawer registers only its own\n // backdrop-click handler on top, since swipe-to-dismiss adds guards the\n // shared `handleBackdropClick` doesn't know about.\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (props.persistent.value) return;\n\n if (swipe?.active || isSwipeClosing.value) return;\n\n if (e.target !== dialog) return; // Click inside panel\n\n requestClose('outsideClick');\n };\n\n mount();\n\n onEvent(dialog, 'click', handleBackdropClick);\n\n // Drag-to-close handlers: scoped to the handle element only so interactions\n // with panel content don't accidentally start a drag.\n const panel = panelRef.value;\n const dragHandleEl = panel?.querySelector<HTMLElement>('[part=\"drag-handle\"]');\n\n if (dragHandleEl) {\n swipe = createPanGesture(dragHandleEl, {\n axis: () => getSwipeConfig().axis,\n disabled: () => isSwipeClosing.value,\n onEnd: handleSwipeEnd,\n onMove: ({ distance }) => {\n const currentPanel = panelRef.value;\n\n if (!currentPanel) return;\n\n const swipeConfig = getSwipeConfig();\n const threshold = getSnapThreshold(currentPanel, swipeConfig.axis);\n const closingDistance = swipeConfig.closingDistance(distance);\n const progress = Math.min(closingDistance / threshold, 1);\n\n currentPanel.style.transition = 'none';\n currentPanel.style.transform = swipeConfig.translate(distance);\n currentPanel.style.opacity = String(1 - progress * 0.4);\n },\n });\n }\n\n return () => {\n swipe?.dispose();\n swipe = undefined;\n overlay.dispose();\n };\n });\n\n return html`\n <dialog\n ref=${dialogRef}\n aria-modal=\"true\"\n aria-label=\"${() => props.label.value ?? null}\"\n aria-labelledby=\"${() => (!props.label.value ? drawerLabelId : null)}\">\n <div class=\"panel\" part=\"panel\" ref=${panelRef}>\n <div class=\"drag-handle\" part=\"drag-handle\" aria-label=\"Drag to close\" role=\"button\"></div>\n <div class=\"header\" part=\"header\" ?hidden=${() => !hasHeader()}>\n <span class=\"header-title\" id=\"${drawerLabelId}\" ?hidden=${() => !hasHeaderTitle()}>\n <slot name=\"header\">${() => getHeaderText()}</slot>\n </span>\n <button\n class=\"close-btn\"\n part=\"close-btn\"\n type=\"button\"\n aria-label=\"Close\"\n ?hidden=${() => !props.dismissible.value}\n @click=${handleCloseButtonClick}>\n <ore-icon name=\"x\" size=\"16\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n <div class=\"body\" part=\"body\">\n <slot></slot>\n </div>\n <div class=\"footer\" part=\"footer\" ?hidden=${() => !hasFooter()}>\n <slot name=\"footer\"></slot>\n </div>\n </div>\n </dialog>\n `;\n },\n styles: [forcedColorsMixin, coarsePointerMixin, reducedMotionMixin, styles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"drawer.cjs","names":[],"sources":["../src/overlay/drawer/drawer.ts"],"sourcesContent":["import { createPanGesture, type PanAxis, type PanGesture, type PanGestureEndDetail } from '@vielzeug/gesture';\nimport {\n createId,\n define,\n getHost,\n html,\n onCleanup,\n onEvent,\n onMounted,\n prop,\n ref,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { signal } from '@vielzeug/ripple';\n\nimport { type OverlayOpenChangeDetail, parseOptionalBool } from '../../core';\n\nimport { coarsePointerMixin, forcedColorsMixin, reducedMotionMixin } from '../../styles';\nimport { useDialogControl } from '../shared/use-dialog';\nimport styles from './drawer.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\n\ntype DrawerPlacement = 'left' | 'right' | 'top' | 'bottom';\nconst DRAWER_SIZES = ['sm', 'lg', 'full'] as const;\ntype DrawerSize = (typeof DRAWER_SIZES)[number];\nconst DRAWER_BACKDROPS = ['opaque', 'blur', 'transparent'] as const;\ntype DrawerBackdrop = (typeof DRAWER_BACKDROPS)[number];\ntype DrawerDragHandlePlacement = 'outside' | 'inset';\ntype DrawerSwipeConfig = {\n axis: PanAxis;\n closingDistance: (distance: number) => number;\n translate: (distance: number) => string;\n};\n\nconst drawerSwipeConfig: Record<DrawerPlacement, DrawerSwipeConfig> = {\n bottom: {\n axis: 'y',\n closingDistance: (distance) => Math.max(0, distance),\n translate: (distance) => `translateY(${distance}px)`,\n },\n left: {\n axis: 'x',\n closingDistance: (distance) => Math.max(0, -distance),\n translate: (distance) => `translateX(${distance}px)`,\n },\n right: {\n axis: 'x',\n closingDistance: (distance) => Math.max(0, distance),\n translate: (distance) => `translateX(${distance}px)`,\n },\n top: {\n axis: 'y',\n closingDistance: (distance) => Math.max(0, -distance),\n translate: (distance) => `translateY(${distance}px)`,\n },\n};\n\n/** Element interface exposing the imperative API for `ore-drawer`. */\nexport interface DrawerElement extends HTMLElement, Omit<OreDrawerProps, 'title'> {\n /** Programmatically close the drawer with the exit animation. */\n hide(): void;\n /** Programmatically open the drawer. Equivalent to setting `open`. */\n show(): void;\n}\n\n/** Drawer component properties */\n\nexport type OreDrawerEvents = {\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreDrawerProps = {\n /** Backdrop style: 'opaque' (default), 'blur', or 'transparent' */\n backdrop?: DrawerBackdrop;\n /** Initial uncontrolled open state. Ignored when `open` is set. */\n 'default-open'?: boolean;\n /** Show the close (×) button in the header (default: true) */\n dismissible?: boolean;\n /** Drag handle position used for swipe-to-close gestures */\n 'drag-handle-placement'?: DrawerDragHandlePlacement;\n /**\n * CSS selector for the element inside the drawer that should receive focus on open.\n * Defaults to native dialog focus management (first focusable element).\n * @example '#submit-btn' | 'input[name=\"email\"]'\n */\n 'initial-focus'?: string;\n /**\n * Invisible accessible label for the dialog (`aria-label`).\n * Use when the drawer has no visible title (e.g. image-only content).\n * When omitted, `aria-labelledby` points to the visible header title instead.\n */\n label?: string;\n /** Controlled open state */\n open?: boolean;\n /** When true, backdrop clicks do not close the drawer (default: false) */\n persistent?: boolean;\n /** Side from which the drawer slides in */\n placement?: DrawerPlacement;\n /**\n * When true (default), focus returns to the triggering element after the drawer closes.\n * Set to false to manage focus manually.\n */\n 'return-focus'?: boolean;\n /** Drawer width/height preset */\n size?: DrawerSize;\n /**\n * Visible title text rendered inside the header.\n * Used as the dialog's accessible label via `aria-labelledby` when `label` is not set.\n */\n title?: string;\n};\n\n/**\n * A panel that slides in from any edge of the screen, built on the native `<dialog>` element.\n *\n * @element ore-drawer\n *\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} placement - 'left' | 'right' (default) | 'top' | 'bottom'\n * @attr {string} size - 'sm' | 'lg' | 'full'\n * @attr {string} title - Visible header title text\n * @attr {string} label - Invisible aria-label (for drawers without a visible title)\n * @attr {boolean} dismissible - Show the close (×) button (default: true)\n * @attr {string} drag-handle-placement - 'outside' (default) | 'inset'\n * @attr {string} backdrop - Backdrop style: 'opaque' (default) | 'blur' | 'transparent'\n * @attr {boolean} persistent - Prevent backdrop-click from closing (default: false)\n *\n * @fires open-change - When the drawer state changes. detail: { open, reason }\n *\n * @slot header - Drawer header content\n * @slot - Main body content\n * @slot footer - Drawer footer content\n *\n * @cssprop --drawer-backdrop-bg - Backdrop background color\n * @cssprop --drawer-bg - Panel background color\n * @cssprop --drawer-size - Panel width (horizontal) or height (vertical)\n * @cssprop --drawer-shadow - Panel box-shadow\n * @cssprop --drawer-panel-blur - Panel backdrop blur amount\n *\n * @part drag-handle - Drawer drag handle.\n * @part panel - Panel container.\n * @part header - Header container.\n * @part close-btn - Close button.\n * @part body - Body content container.\n * @part footer - Footer container.\n * @example\n * ```html\n * <!-- With visible title -->\n * <ore-drawer open title=\"Settings\" placement=\"right\">\n * <p>Settings content here.</p>\n * </ore-drawer>\n *\n * <!-- With custom header slot -->\n * <ore-drawer open placement=\"right\">\n * <span slot=\"header\">Settings</span>\n * <p>Settings content here.</p>\n * </ore-drawer>\n * ```\n */\n\nexport const DRAWER_TAG = 'ore-drawer' as const;\ndefine<OreDrawerProps>(DRAWER_TAG, {\n props: {\n backdrop: prop.oneOf(DRAWER_BACKDROPS),\n 'default-open': prop.bool(false),\n dismissible: prop.bool(true),\n 'drag-handle-placement': prop.oneOf(['outside', 'inset'] as const, 'outside'),\n 'initial-focus': prop.string(),\n label: prop.string(),\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n persistent: prop.bool(false),\n placement: prop.oneOf(['left', 'right', 'top', 'bottom'] as const, 'right'),\n 'return-focus': prop.bool(true),\n size: prop.oneOf(DRAWER_SIZES),\n title: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreDrawerEvents>();\n const slots = useSlots();\n\n const drawerLabelId = createId('drawer-label');\n const dialogRef = ref<HTMLDialogElement>();\n const panelRef = ref<HTMLDivElement>();\n\n // Drag-to-close state\n const isSwipeClosing = signal(false);\n let swipe: PanGesture | undefined;\n let swipeCloseTimer: ReturnType<typeof setTimeout> | undefined;\n\n const getHeaderText = () => props.label.value ?? props.title.value ?? '';\n const hasHeaderTitle = () => slots.has('header').value || !!getHeaderText();\n\n // Header is visible when there is slot content, a text label, or a close button.\n const hasHeader = () => hasHeaderTitle() || props.dismissible.value;\n const hasFooter = () => slots.has('footer').value;\n\n const getPlacement = (): DrawerPlacement => props.placement.value || 'right';\n\n const getSwipeConfig = (): DrawerSwipeConfig => drawerSwipeConfig[getPlacement()];\n\n const getSnapThreshold = (panel: HTMLElement, axis: PanAxis) => {\n const panelSize = axis === 'x' ? panel.offsetWidth : panel.offsetHeight;\n\n return Math.min(96, Math.max(36, panelSize * 0.18));\n };\n\n const shouldCommitSwipeClose = (distance: number, threshold: number) => {\n return getSwipeConfig().closingDistance(distance) >= threshold;\n };\n\n const finalizeSwipeClose = (panel: HTMLElement) => {\n if (!isSwipeClosing.value) return;\n\n if (swipeCloseTimer) {\n clearTimeout(swipeCloseTimer);\n swipeCloseTimer = undefined;\n }\n\n const dialog = dialogRef.value;\n\n if (!dialog) {\n isSwipeClosing.value = false;\n\n return;\n }\n\n dialog.style.transition = 'none';\n panel.style.opacity = '0';\n panel.style.visibility = 'hidden';\n void dialog.offsetWidth;\n\n requestClose('swipe');\n };\n\n const startSwipeClose = (panel: HTMLElement, swipe: DrawerSwipeConfig, committedDistance: number) => {\n if (isSwipeClosing.value) return;\n\n isSwipeClosing.value = true;\n\n const panelSize = swipe.axis === 'x' ? panel.offsetWidth : panel.offsetHeight;\n // Preserve overshoot so closing continues from the dragged position instead of snapping back.\n const exitDistance =\n committedDistance >= 0 ? Math.max(committedDistance, panelSize) : Math.min(committedDistance, -panelSize);\n const exitTransform = swipe.translate(exitDistance);\n\n // Commit the current drag position first so the magnetic snap continues\n // from the finger instead of jumping back to rest.\n panel.style.transition = 'transform 180ms cubic-bezier(0.2, 0.9, 0.2, 1), opacity 180ms ease-out';\n void panel.offsetWidth;\n\n const onExitTransitionEnd = (ev: TransitionEvent) => {\n if (ev.target !== panel || ev.propertyName !== 'transform') return;\n\n panel.removeEventListener('transitionend', onExitTransitionEnd);\n finalizeSwipeClose(panel);\n };\n\n panel.addEventListener('transitionend', onExitTransitionEnd);\n\n const durationMs = parseFloat(getComputedStyle(panel).transitionDuration) * 1000;\n\n swipeCloseTimer = setTimeout(() => {\n panel.removeEventListener('transitionend', onExitTransitionEnd);\n finalizeSwipeClose(panel);\n }, durationMs + 50);\n\n panel.style.transform = exitTransform;\n panel.style.opacity = '0.2';\n };\n\n const resetPanelDragStyles = (panel: HTMLElement) => {\n if (swipeCloseTimer) {\n clearTimeout(swipeCloseTimer);\n swipeCloseTimer = undefined;\n }\n\n // Re-enable transitions so the snap-back or exit animates.\n panel.style.transition = '';\n panel.style.transform = '';\n panel.style.opacity = '';\n panel.style.visibility = '';\n isSwipeClosing.value = false;\n };\n\n const handleSwipeEnd = ({ distance, reason }: PanGestureEndDetail): void => {\n const panel = panelRef.value;\n\n if (!panel) return;\n\n const threshold = getSnapThreshold(panel, getSwipeConfig().axis);\n\n if (reason === 'release' && shouldCommitSwipeClose(distance, threshold)) {\n startSwipeClose(panel, getSwipeConfig(), distance);\n\n return;\n }\n\n resetPanelDragStyles(panel);\n };\n\n // ────────────────────────────────────────────────────────────────\n // Overlay State Management\n // ────────────────────────────────────────────────────────────────\n\n const { closeWithAnimation, mount, overlay, requestClose } = useDialogControl({\n beforeOpen: (dialog) => {\n // Clear any inline drag styles from a previous swipe-close so the CSS\n // entry animation starts from the correct base state.\n const panel = panelRef.value;\n\n if (panel) resetPanelDragStyles(panel);\n\n void dialog;\n },\n closeRequestDetail: (_reason) => ({ placement: props.placement.value ?? 'right' }),\n defaultOpen: props['default-open'],\n dialogRef,\n getPanelEl: () => panelRef.value,\n host: el,\n initialFocus: props['initial-focus'],\n isPersistent: () => Boolean(props.persistent.value),\n onCleanup,\n onEvent,\n onNativeClose: (reason) => {\n const panelEl = panelRef.value;\n\n // For swipe-close, keep the panel hidden and off-screen until the next\n // open cycle: resetting inline styles during native close can produce\n // a visible frame at the rest position on some browsers.\n if (panelEl && reason !== 'swipe') resetPanelDragStyles(panelEl);\n\n const dialog = dialogRef.value;\n\n if (dialog) dialog.style.transition = '';\n\n emit('open-change', { open: false, reason });\n },\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n openProp: props.open,\n performClose: (dialog, reason) => {\n if (reason === 'swipe') {\n dialog.close();\n } else {\n closeWithAnimation();\n }\n },\n returnFocus: props['return-focus'],\n });\n\n const handleCloseButtonClick = () => {\n requestClose('trigger');\n };\n\n // ────────────────────────────────────────────────────────────────\n // Lifecycle: Setup Drawer Integration\n // ────────────────────────────────────────────────────────────────\n\n onMounted(() => {\n const dialog = dialogRef.value;\n\n if (!dialog) return;\n\n // Expose imperative API\n const drawerEl = el as DrawerElement;\n\n drawerEl.show = () => {\n overlay.open('programmatic');\n };\n\n drawerEl.hide = () => {\n overlay.close('programmatic');\n };\n\n // ────────────────────────────────────────────────────────────\n // Event Handlers: Backdrop Click\n // ────────────────────────────────────────────────────────────\n // `mount()` registers the open-prop watcher and the shared native\n // `close`/`cancel` handling (Escape). Drawer registers only its own\n // backdrop-click handler on top, since swipe-to-dismiss adds guards the\n // shared `handleBackdropClick` doesn't know about.\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (props.persistent.value) return;\n\n if (swipe?.active || isSwipeClosing.value) return;\n\n if (e.target !== dialog) return; // Click inside panel\n\n requestClose('outsideClick');\n };\n\n mount();\n\n onEvent(dialog, 'click', handleBackdropClick);\n\n // Drag-to-close handlers: scoped to the handle element only so interactions\n // with panel content don't accidentally start a drag.\n const panel = panelRef.value;\n const dragHandleEl = panel?.querySelector<HTMLElement>('[part=\"drag-handle\"]');\n\n if (dragHandleEl) {\n swipe = createPanGesture(dragHandleEl, {\n axis: () => getSwipeConfig().axis,\n disabled: () => isSwipeClosing.value,\n onEnd: handleSwipeEnd,\n onMove: ({ distance }) => {\n const currentPanel = panelRef.value;\n\n if (!currentPanel) return;\n\n const swipeConfig = getSwipeConfig();\n const threshold = getSnapThreshold(currentPanel, swipeConfig.axis);\n const closingDistance = swipeConfig.closingDistance(distance);\n const progress = Math.min(closingDistance / threshold, 1);\n\n currentPanel.style.transition = 'none';\n currentPanel.style.transform = swipeConfig.translate(distance);\n currentPanel.style.opacity = String(1 - progress * 0.4);\n },\n });\n }\n\n return () => {\n swipe?.dispose();\n swipe = undefined;\n overlay.dispose();\n };\n });\n\n return html`\n <dialog\n ref=${dialogRef}\n aria-modal=\"true\"\n aria-label=\"${() => props.label.value ?? null}\"\n aria-labelledby=\"${() => (!props.label.value ? drawerLabelId : null)}\">\n <div class=\"panel\" part=\"panel\" ref=${panelRef}>\n <div class=\"drag-handle\" part=\"drag-handle\" aria-label=\"Drag to close\" role=\"button\"></div>\n <div class=\"header\" part=\"header\" ?hidden=${() => !hasHeader()}>\n <span class=\"header-title\" id=\"${drawerLabelId}\" ?hidden=${() => !hasHeaderTitle()}>\n <slot name=\"header\">${() => getHeaderText()}</slot>\n </span>\n <button\n class=\"close-btn\"\n part=\"close-btn\"\n type=\"button\"\n aria-label=\"Close\"\n ?hidden=${() => !props.dismissible.value}\n @click=${handleCloseButtonClick}>\n <ore-icon name=\"x\" size=\"16\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n <div class=\"body\" part=\"body\">\n <slot></slot>\n </div>\n <div class=\"footer\" part=\"footer\" ?hidden=${() => !hasFooter()}>\n <slot name=\"footer\"></slot>\n </div>\n </div>\n </dialog>\n `;\n },\n styles: [forcedColorsMixin, coarsePointerMixin, reducedMotionMixin, styles],\n});\n"],"mappings":"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"}
|
package/dist/drawer.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{coarsePointerMixin as e,reducedMotionMixin as t}from"./styles/mixins/accessibility.css.js";import{forcedColorsMixin as n}from"./styles/mixins/theme.css.js";import{parseOptionalBool as r}from"./core/parse.js";import{ICON_TAG as i}from"./icon.js";import{useDialogControl as a}from"./overlay/shared/use-dialog.js";import o from"./overlay/drawer/drawer.css.js";import{signal as s}from"@vielzeug/ripple";import{createId as c,define as l,getHost as u,html as d,onCleanup as f,onEvent as p,onMounted as m,prop as h,ref as g,useEmit as _,useSlots as v}from"@vielzeug/ore";import{createPanGesture as y}from"@vielzeug/gesture";var b={bottom:{axis:`y`,closingDistance:e=>Math.max(0,e),translate:e=>`translateY(${e}px)`},left:{axis:`x`,closingDistance:e=>Math.max(0,-e),translate:e=>`translateX(${e}px)`},right:{axis:`x`,closingDistance:e=>Math.max(0,e),translate:e=>`translateX(${e}px)`},top:{axis:`y`,closingDistance:e=>Math.max(0,-e),translate:e=>`translateY(${e}px)`}},
|
|
1
|
+
import{coarsePointerMixin as e,reducedMotionMixin as t}from"./styles/mixins/accessibility.css.js";import{forcedColorsMixin as n}from"./styles/mixins/theme.css.js";import{parseOptionalBool as r}from"./core/parse.js";import{ICON_TAG as i}from"./icon.js";import{useDialogControl as a}from"./overlay/shared/use-dialog.js";import o from"./overlay/drawer/drawer.css.js";import{signal as s}from"@vielzeug/ripple";import{createId as c,define as l,getHost as u,html as d,onCleanup as f,onEvent as p,onMounted as m,prop as h,ref as g,useEmit as _,useSlots as v}from"@vielzeug/ore";import{createPanGesture as y}from"@vielzeug/gesture";var b=[`sm`,`lg`,`full`],x=[`opaque`,`blur`,`transparent`],S={bottom:{axis:`y`,closingDistance:e=>Math.max(0,e),translate:e=>`translateY(${e}px)`},left:{axis:`x`,closingDistance:e=>Math.max(0,-e),translate:e=>`translateX(${e}px)`},right:{axis:`x`,closingDistance:e=>Math.max(0,e),translate:e=>`translateX(${e}px)`},top:{axis:`y`,closingDistance:e=>Math.max(0,-e),translate:e=>`translateY(${e}px)`}},C=`ore-drawer`;l(C,{props:{backdrop:h.oneOf(x),"default-open":h.bool(!1),dismissible:h.bool(!0),"drag-handle-placement":h.oneOf([`outside`,`inset`],`outside`),"initial-focus":h.string(),label:h.string(),open:{default:void 0,parse:r},persistent:h.bool(!1),placement:h.oneOf([`left`,`right`,`top`,`bottom`],`right`),"return-focus":h.bool(!0),size:h.oneOf(b),title:h.string()},setup(e){let t=u(),n=_(),r=v(),i=c(`drawer-label`),o=g(),l=g(),h=s(!1),b,x,getHeaderText=()=>e.label.value??e.title.value??``,hasHeaderTitle=()=>r.has(`header`).value||!!getHeaderText(),hasHeader=()=>hasHeaderTitle()||e.dismissible.value,hasFooter=()=>r.has(`footer`).value,getPlacement=()=>e.placement.value||`right`,getSwipeConfig=()=>S[getPlacement()],getSnapThreshold=(e,t)=>{let n=t===`x`?e.offsetWidth:e.offsetHeight;return Math.min(96,Math.max(36,n*.18))},shouldCommitSwipeClose=(e,t)=>getSwipeConfig().closingDistance(e)>=t,finalizeSwipeClose=e=>{if(!h.value)return;x&&=(clearTimeout(x),void 0);let t=o.value;if(!t){h.value=!1;return}t.style.transition=`none`,e.style.opacity=`0`,e.style.visibility=`hidden`,t.offsetWidth,E(`swipe`)},startSwipeClose=(e,t,n)=>{if(h.value)return;h.value=!0;let r=t.axis===`x`?e.offsetWidth:e.offsetHeight,i=n>=0?Math.max(n,r):Math.min(n,-r),a=t.translate(i);e.style.transition=`transform 180ms cubic-bezier(0.2, 0.9, 0.2, 1), opacity 180ms ease-out`,e.offsetWidth;let onExitTransitionEnd=t=>{t.target===e&&t.propertyName===`transform`&&(e.removeEventListener(`transitionend`,onExitTransitionEnd),finalizeSwipeClose(e))};e.addEventListener(`transitionend`,onExitTransitionEnd);let o=parseFloat(getComputedStyle(e).transitionDuration)*1e3;x=setTimeout(()=>{e.removeEventListener(`transitionend`,onExitTransitionEnd),finalizeSwipeClose(e)},o+50),e.style.transform=a,e.style.opacity=`0.2`},resetPanelDragStyles=e=>{x&&=(clearTimeout(x),void 0),e.style.transition=``,e.style.transform=``,e.style.opacity=``,e.style.visibility=``,h.value=!1},handleSwipeEnd=({distance:e,reason:t})=>{let n=l.value;if(!n)return;let r=getSnapThreshold(n,getSwipeConfig().axis);if(t===`release`&&shouldCommitSwipeClose(e,r)){startSwipeClose(n,getSwipeConfig(),e);return}resetPanelDragStyles(n)},{closeWithAnimation:C,mount:w,overlay:T,requestClose:E}=a({beforeOpen:e=>{let t=l.value;t&&resetPanelDragStyles(t)},closeRequestDetail:t=>({placement:e.placement.value??`right`}),defaultOpen:e[`default-open`],dialogRef:o,getPanelEl:()=>l.value,host:t,initialFocus:e[`initial-focus`],isPersistent:()=>!!e.persistent.value,onCleanup:f,onEvent:p,onNativeClose:e=>{let t=l.value;t&&e!==`swipe`&&resetPanelDragStyles(t);let r=o.value;r&&(r.style.transition=``),n(`open-change`,{open:!1,reason:e})},onOpen:e=>n(`open-change`,{open:!0,reason:e}),openProp:e.open,performClose:(e,t)=>{t===`swipe`?e.close():C()},returnFocus:e[`return-focus`]}),handleCloseButtonClick=()=>{E(`trigger`)};return m(()=>{let n=o.value;if(!n)return;let r=t;r.show=()=>{T.open(`programmatic`)},r.hide=()=>{T.close(`programmatic`)};let handleBackdropClick=t=>{e.persistent.value||b?.active||h.value||t.target===n&&E(`outsideClick`)};w(),p(n,`click`,handleBackdropClick);let i=l.value?.querySelector(`[part="drag-handle"]`);return i&&(b=y(i,{axis:()=>getSwipeConfig().axis,disabled:()=>h.value,onEnd:handleSwipeEnd,onMove:({distance:e})=>{let t=l.value;if(!t)return;let n=getSwipeConfig(),r=getSnapThreshold(t,n.axis),i=n.closingDistance(e),a=Math.min(i/r,1);t.style.transition=`none`,t.style.transform=n.translate(e),t.style.opacity=String(1-a*.4)}})),()=>{b?.dispose(),b=void 0,T.dispose()}}),d`
|
|
2
2
|
<dialog
|
|
3
3
|
ref=${o}
|
|
4
4
|
aria-modal="true"
|
|
@@ -28,5 +28,5 @@ import{coarsePointerMixin as e,reducedMotionMixin as t}from"./styles/mixins/acce
|
|
|
28
28
|
</div>
|
|
29
29
|
</div>
|
|
30
30
|
</dialog>
|
|
31
|
-
`},styles:[n,e,t,o]});export{
|
|
31
|
+
`},styles:[n,e,t,o]});export{C as DRAWER_TAG,i as ICON_TAG};
|
|
32
32
|
//# sourceMappingURL=drawer.js.map
|
package/dist/drawer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawer.js","names":[],"sources":["../src/overlay/drawer/drawer.ts"],"sourcesContent":["import { createPanGesture, type PanAxis, type PanGesture, type PanGestureEndDetail } from '@vielzeug/gesture';\nimport {\n createId,\n define,\n getHost,\n html,\n onCleanup,\n onEvent,\n onMounted,\n prop,\n ref,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { signal } from '@vielzeug/ripple';\n\nimport { type OverlayOpenChangeDetail, parseOptionalBool } from '../../core';\n\nimport { coarsePointerMixin, forcedColorsMixin, reducedMotionMixin } from '../../styles';\nimport { useDialogControl } from '../shared/use-dialog';\nimport styles from './drawer.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\n\ntype DrawerPlacement = 'left' | 'right' | 'top' | 'bottom';\ntype DrawerSize = 'sm' | 'lg' | 'full';\ntype DrawerBackdrop = 'opaque' | 'blur' | 'transparent';\ntype DrawerDragHandlePlacement = 'outside' | 'inset';\ntype DrawerSwipeConfig = {\n axis: PanAxis;\n closingDistance: (distance: number) => number;\n translate: (distance: number) => string;\n};\n\nconst drawerSwipeConfig: Record<DrawerPlacement, DrawerSwipeConfig> = {\n bottom: {\n axis: 'y',\n closingDistance: (distance) => Math.max(0, distance),\n translate: (distance) => `translateY(${distance}px)`,\n },\n left: {\n axis: 'x',\n closingDistance: (distance) => Math.max(0, -distance),\n translate: (distance) => `translateX(${distance}px)`,\n },\n right: {\n axis: 'x',\n closingDistance: (distance) => Math.max(0, distance),\n translate: (distance) => `translateX(${distance}px)`,\n },\n top: {\n axis: 'y',\n closingDistance: (distance) => Math.max(0, -distance),\n translate: (distance) => `translateY(${distance}px)`,\n },\n};\n\n/** Element interface exposing the imperative API for `ore-drawer`. */\nexport interface DrawerElement extends HTMLElement, Omit<OreDrawerProps, 'title'> {\n /** Programmatically close the drawer with the exit animation. */\n hide(): void;\n /** Programmatically open the drawer. Equivalent to setting `open`. */\n show(): void;\n}\n\n/** Drawer component properties */\n\nexport type OreDrawerEvents = {\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreDrawerProps = {\n /** Backdrop style: 'opaque' (default), 'blur', or 'transparent' */\n backdrop?: DrawerBackdrop;\n /** Initial uncontrolled open state. Ignored when `open` is set. */\n 'default-open'?: boolean;\n /** Show the close (×) button in the header (default: true) */\n dismissible?: boolean;\n /** Drag handle position used for swipe-to-close gestures */\n 'drag-handle-placement'?: DrawerDragHandlePlacement;\n /**\n * CSS selector for the element inside the drawer that should receive focus on open.\n * Defaults to native dialog focus management (first focusable element).\n * @example '#submit-btn' | 'input[name=\"email\"]'\n */\n 'initial-focus'?: string;\n /**\n * Invisible accessible label for the dialog (`aria-label`).\n * Use when the drawer has no visible title (e.g. image-only content).\n * When omitted, `aria-labelledby` points to the visible header title instead.\n */\n label?: string;\n /** Controlled open state */\n open?: boolean;\n /** When true, backdrop clicks do not close the drawer (default: false) */\n persistent?: boolean;\n /** Side from which the drawer slides in */\n placement?: DrawerPlacement;\n /**\n * When true (default), focus returns to the triggering element after the drawer closes.\n * Set to false to manage focus manually.\n */\n 'return-focus'?: boolean;\n /** Drawer width/height preset */\n size?: DrawerSize;\n /**\n * Visible title text rendered inside the header.\n * Used as the dialog's accessible label via `aria-labelledby` when `label` is not set.\n */\n title?: string;\n};\n\n/**\n * A panel that slides in from any edge of the screen, built on the native `<dialog>` element.\n *\n * @element ore-drawer\n *\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} placement - 'left' | 'right' (default) | 'top' | 'bottom'\n * @attr {string} size - 'sm' | 'lg' | 'full'\n * @attr {string} title - Visible header title text\n * @attr {string} label - Invisible aria-label (for drawers without a visible title)\n * @attr {boolean} dismissible - Show the close (×) button (default: true)\n * @attr {string} drag-handle-placement - 'outside' (default) | 'inset'\n * @attr {string} backdrop - Backdrop style: 'opaque' (default) | 'blur' | 'transparent'\n * @attr {boolean} persistent - Prevent backdrop-click from closing (default: false)\n *\n * @fires open-change - When the drawer state changes. detail: { open, reason }\n *\n * @slot header - Drawer header content\n * @slot - Main body content\n * @slot footer - Drawer footer content\n *\n * @cssprop --drawer-backdrop-bg - Backdrop background color\n * @cssprop --drawer-bg - Panel background color\n * @cssprop --drawer-size - Panel width (horizontal) or height (vertical)\n * @cssprop --drawer-shadow - Panel box-shadow\n * @cssprop --drawer-panel-blur - Panel backdrop blur amount\n *\n * @part drag-handle - Drawer drag handle.\n * @part panel - Panel container.\n * @part header - Header container.\n * @part close-btn - Close button.\n * @part body - Body content container.\n * @part footer - Footer container.\n * @example\n * ```html\n * <!-- With visible title -->\n * <ore-drawer open title=\"Settings\" placement=\"right\">\n * <p>Settings content here.</p>\n * </ore-drawer>\n *\n * <!-- With custom header slot -->\n * <ore-drawer open placement=\"right\">\n * <span slot=\"header\">Settings</span>\n * <p>Settings content here.</p>\n * </ore-drawer>\n * ```\n */\n\nexport const DRAWER_TAG = 'ore-drawer' as const;\ndefine<OreDrawerProps>(DRAWER_TAG, {\n props: {\n backdrop: prop.string<DrawerBackdrop>(),\n 'default-open': prop.bool(false),\n dismissible: prop.bool(true),\n 'drag-handle-placement': prop.oneOf(['outside', 'inset'] as const, 'outside'),\n 'initial-focus': prop.string(),\n label: prop.string(),\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n persistent: prop.bool(false),\n placement: prop.oneOf(['left', 'right', 'top', 'bottom'] as const, 'right'),\n 'return-focus': prop.bool(true),\n size: prop.string<DrawerSize>(),\n title: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreDrawerEvents>();\n const slots = useSlots();\n\n const drawerLabelId = createId('drawer-label');\n const dialogRef = ref<HTMLDialogElement>();\n const panelRef = ref<HTMLDivElement>();\n\n // Drag-to-close state\n const isSwipeClosing = signal(false);\n let swipe: PanGesture | undefined;\n let swipeCloseTimer: ReturnType<typeof setTimeout> | undefined;\n\n const getHeaderText = () => props.label.value ?? props.title.value ?? '';\n const hasHeaderTitle = () => slots.has('header').value || !!getHeaderText();\n\n // Header is visible when there is slot content, a text label, or a close button.\n const hasHeader = () => hasHeaderTitle() || props.dismissible.value;\n const hasFooter = () => slots.has('footer').value;\n\n const getPlacement = (): DrawerPlacement => props.placement.value || 'right';\n\n const getSwipeConfig = (): DrawerSwipeConfig => drawerSwipeConfig[getPlacement()];\n\n const getSnapThreshold = (panel: HTMLElement, axis: PanAxis) => {\n const panelSize = axis === 'x' ? panel.offsetWidth : panel.offsetHeight;\n\n return Math.min(96, Math.max(36, panelSize * 0.18));\n };\n\n const shouldCommitSwipeClose = (distance: number, threshold: number) => {\n return getSwipeConfig().closingDistance(distance) >= threshold;\n };\n\n const finalizeSwipeClose = (panel: HTMLElement) => {\n if (!isSwipeClosing.value) return;\n\n if (swipeCloseTimer) {\n clearTimeout(swipeCloseTimer);\n swipeCloseTimer = undefined;\n }\n\n const dialog = dialogRef.value;\n\n if (!dialog) {\n isSwipeClosing.value = false;\n\n return;\n }\n\n dialog.style.transition = 'none';\n panel.style.opacity = '0';\n panel.style.visibility = 'hidden';\n void dialog.offsetWidth;\n\n requestClose('swipe');\n };\n\n const startSwipeClose = (panel: HTMLElement, swipe: DrawerSwipeConfig, committedDistance: number) => {\n if (isSwipeClosing.value) return;\n\n isSwipeClosing.value = true;\n\n const panelSize = swipe.axis === 'x' ? panel.offsetWidth : panel.offsetHeight;\n // Preserve overshoot so closing continues from the dragged position instead of snapping back.\n const exitDistance =\n committedDistance >= 0 ? Math.max(committedDistance, panelSize) : Math.min(committedDistance, -panelSize);\n const exitTransform = swipe.translate(exitDistance);\n\n // Commit the current drag position first so the magnetic snap continues\n // from the finger instead of jumping back to rest.\n panel.style.transition = 'transform 180ms cubic-bezier(0.2, 0.9, 0.2, 1), opacity 180ms ease-out';\n void panel.offsetWidth;\n\n const onExitTransitionEnd = (ev: TransitionEvent) => {\n if (ev.target !== panel || ev.propertyName !== 'transform') return;\n\n panel.removeEventListener('transitionend', onExitTransitionEnd);\n finalizeSwipeClose(panel);\n };\n\n panel.addEventListener('transitionend', onExitTransitionEnd);\n\n const durationMs = parseFloat(getComputedStyle(panel).transitionDuration) * 1000;\n\n swipeCloseTimer = setTimeout(() => {\n panel.removeEventListener('transitionend', onExitTransitionEnd);\n finalizeSwipeClose(panel);\n }, durationMs + 50);\n\n panel.style.transform = exitTransform;\n panel.style.opacity = '0.2';\n };\n\n const resetPanelDragStyles = (panel: HTMLElement) => {\n if (swipeCloseTimer) {\n clearTimeout(swipeCloseTimer);\n swipeCloseTimer = undefined;\n }\n\n // Re-enable transitions so the snap-back or exit animates.\n panel.style.transition = '';\n panel.style.transform = '';\n panel.style.opacity = '';\n panel.style.visibility = '';\n isSwipeClosing.value = false;\n };\n\n const handleSwipeEnd = ({ distance, reason }: PanGestureEndDetail): void => {\n const panel = panelRef.value;\n\n if (!panel) return;\n\n const threshold = getSnapThreshold(panel, getSwipeConfig().axis);\n\n if (reason === 'release' && shouldCommitSwipeClose(distance, threshold)) {\n startSwipeClose(panel, getSwipeConfig(), distance);\n\n return;\n }\n\n resetPanelDragStyles(panel);\n };\n\n // ────────────────────────────────────────────────────────────────\n // Overlay State Management\n // ────────────────────────────────────────────────────────────────\n\n const { closeWithAnimation, mount, overlay, requestClose } = useDialogControl({\n beforeOpen: (dialog) => {\n // Clear any inline drag styles from a previous swipe-close so the CSS\n // entry animation starts from the correct base state.\n const panel = panelRef.value;\n\n if (panel) resetPanelDragStyles(panel);\n\n void dialog;\n },\n closeRequestDetail: (_reason) => ({ placement: props.placement.value ?? 'right' }),\n defaultOpen: props['default-open'],\n dialogRef,\n getPanelEl: () => panelRef.value,\n host: el,\n initialFocus: props['initial-focus'],\n isPersistent: () => Boolean(props.persistent.value),\n onCleanup,\n onEvent,\n onNativeClose: (reason) => {\n const panelEl = panelRef.value;\n\n // For swipe-close, keep the panel hidden and off-screen until the next\n // open cycle: resetting inline styles during native close can produce\n // a visible frame at the rest position on some browsers.\n if (panelEl && reason !== 'swipe') resetPanelDragStyles(panelEl);\n\n const dialog = dialogRef.value;\n\n if (dialog) dialog.style.transition = '';\n\n emit('open-change', { open: false, reason });\n },\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n openProp: props.open,\n performClose: (dialog, reason) => {\n if (reason === 'swipe') {\n dialog.close();\n } else {\n closeWithAnimation();\n }\n },\n returnFocus: props['return-focus'],\n });\n\n const handleCloseButtonClick = () => {\n requestClose('trigger');\n };\n\n // ────────────────────────────────────────────────────────────────\n // Lifecycle: Setup Drawer Integration\n // ────────────────────────────────────────────────────────────────\n\n onMounted(() => {\n const dialog = dialogRef.value;\n\n if (!dialog) return;\n\n // Expose imperative API\n const drawerEl = el as DrawerElement;\n\n drawerEl.show = () => {\n overlay.open('programmatic');\n };\n\n drawerEl.hide = () => {\n overlay.close('programmatic');\n };\n\n // ────────────────────────────────────────────────────────────\n // Event Handlers: Backdrop Click\n // ────────────────────────────────────────────────────────────\n // `mount()` registers the open-prop watcher and the shared native\n // `close`/`cancel` handling (Escape). Drawer registers only its own\n // backdrop-click handler on top, since swipe-to-dismiss adds guards the\n // shared `handleBackdropClick` doesn't know about.\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (props.persistent.value) return;\n\n if (swipe?.active || isSwipeClosing.value) return;\n\n if (e.target !== dialog) return; // Click inside panel\n\n requestClose('outsideClick');\n };\n\n mount();\n\n onEvent(dialog, 'click', handleBackdropClick);\n\n // Drag-to-close handlers: scoped to the handle element only so interactions\n // with panel content don't accidentally start a drag.\n const panel = panelRef.value;\n const dragHandleEl = panel?.querySelector<HTMLElement>('[part=\"drag-handle\"]');\n\n if (dragHandleEl) {\n swipe = createPanGesture(dragHandleEl, {\n axis: () => getSwipeConfig().axis,\n disabled: () => isSwipeClosing.value,\n onEnd: handleSwipeEnd,\n onMove: ({ distance }) => {\n const currentPanel = panelRef.value;\n\n if (!currentPanel) return;\n\n const swipeConfig = getSwipeConfig();\n const threshold = getSnapThreshold(currentPanel, swipeConfig.axis);\n const closingDistance = swipeConfig.closingDistance(distance);\n const progress = Math.min(closingDistance / threshold, 1);\n\n currentPanel.style.transition = 'none';\n currentPanel.style.transform = swipeConfig.translate(distance);\n currentPanel.style.opacity = String(1 - progress * 0.4);\n },\n });\n }\n\n return () => {\n swipe?.dispose();\n swipe = undefined;\n overlay.dispose();\n };\n });\n\n return html`\n <dialog\n ref=${dialogRef}\n aria-modal=\"true\"\n aria-label=\"${() => props.label.value ?? null}\"\n aria-labelledby=\"${() => (!props.label.value ? drawerLabelId : null)}\">\n <div class=\"panel\" part=\"panel\" ref=${panelRef}>\n <div class=\"drag-handle\" part=\"drag-handle\" aria-label=\"Drag to close\" role=\"button\"></div>\n <div class=\"header\" part=\"header\" ?hidden=${() => !hasHeader()}>\n <span class=\"header-title\" id=\"${drawerLabelId}\" ?hidden=${() => !hasHeaderTitle()}>\n <slot name=\"header\">${() => getHeaderText()}</slot>\n </span>\n <button\n class=\"close-btn\"\n part=\"close-btn\"\n type=\"button\"\n aria-label=\"Close\"\n ?hidden=${() => !props.dismissible.value}\n @click=${handleCloseButtonClick}>\n <ore-icon name=\"x\" size=\"16\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n <div class=\"body\" part=\"body\">\n <slot></slot>\n </div>\n <div class=\"footer\" part=\"footer\" ?hidden=${() => !hasFooter()}>\n <slot name=\"footer\"></slot>\n </div>\n </div>\n </dialog>\n `;\n },\n styles: [forcedColorsMixin, coarsePointerMixin, reducedMotionMixin, styles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"drawer.js","names":[],"sources":["../src/overlay/drawer/drawer.ts"],"sourcesContent":["import { createPanGesture, type PanAxis, type PanGesture, type PanGestureEndDetail } from '@vielzeug/gesture';\nimport {\n createId,\n define,\n getHost,\n html,\n onCleanup,\n onEvent,\n onMounted,\n prop,\n ref,\n useEmit,\n useSlots,\n} from '@vielzeug/ore';\nimport { signal } from '@vielzeug/ripple';\n\nimport { type OverlayOpenChangeDetail, parseOptionalBool } from '../../core';\n\nimport { coarsePointerMixin, forcedColorsMixin, reducedMotionMixin } from '../../styles';\nimport { useDialogControl } from '../shared/use-dialog';\nimport styles from './drawer.css?inline';\n\nexport { ICON_TAG } from '../../content/icon/icon';\n\ntype DrawerPlacement = 'left' | 'right' | 'top' | 'bottom';\nconst DRAWER_SIZES = ['sm', 'lg', 'full'] as const;\ntype DrawerSize = (typeof DRAWER_SIZES)[number];\nconst DRAWER_BACKDROPS = ['opaque', 'blur', 'transparent'] as const;\ntype DrawerBackdrop = (typeof DRAWER_BACKDROPS)[number];\ntype DrawerDragHandlePlacement = 'outside' | 'inset';\ntype DrawerSwipeConfig = {\n axis: PanAxis;\n closingDistance: (distance: number) => number;\n translate: (distance: number) => string;\n};\n\nconst drawerSwipeConfig: Record<DrawerPlacement, DrawerSwipeConfig> = {\n bottom: {\n axis: 'y',\n closingDistance: (distance) => Math.max(0, distance),\n translate: (distance) => `translateY(${distance}px)`,\n },\n left: {\n axis: 'x',\n closingDistance: (distance) => Math.max(0, -distance),\n translate: (distance) => `translateX(${distance}px)`,\n },\n right: {\n axis: 'x',\n closingDistance: (distance) => Math.max(0, distance),\n translate: (distance) => `translateX(${distance}px)`,\n },\n top: {\n axis: 'y',\n closingDistance: (distance) => Math.max(0, -distance),\n translate: (distance) => `translateY(${distance}px)`,\n },\n};\n\n/** Element interface exposing the imperative API for `ore-drawer`. */\nexport interface DrawerElement extends HTMLElement, Omit<OreDrawerProps, 'title'> {\n /** Programmatically close the drawer with the exit animation. */\n hide(): void;\n /** Programmatically open the drawer. Equivalent to setting `open`. */\n show(): void;\n}\n\n/** Drawer component properties */\n\nexport type OreDrawerEvents = {\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreDrawerProps = {\n /** Backdrop style: 'opaque' (default), 'blur', or 'transparent' */\n backdrop?: DrawerBackdrop;\n /** Initial uncontrolled open state. Ignored when `open` is set. */\n 'default-open'?: boolean;\n /** Show the close (×) button in the header (default: true) */\n dismissible?: boolean;\n /** Drag handle position used for swipe-to-close gestures */\n 'drag-handle-placement'?: DrawerDragHandlePlacement;\n /**\n * CSS selector for the element inside the drawer that should receive focus on open.\n * Defaults to native dialog focus management (first focusable element).\n * @example '#submit-btn' | 'input[name=\"email\"]'\n */\n 'initial-focus'?: string;\n /**\n * Invisible accessible label for the dialog (`aria-label`).\n * Use when the drawer has no visible title (e.g. image-only content).\n * When omitted, `aria-labelledby` points to the visible header title instead.\n */\n label?: string;\n /** Controlled open state */\n open?: boolean;\n /** When true, backdrop clicks do not close the drawer (default: false) */\n persistent?: boolean;\n /** Side from which the drawer slides in */\n placement?: DrawerPlacement;\n /**\n * When true (default), focus returns to the triggering element after the drawer closes.\n * Set to false to manage focus manually.\n */\n 'return-focus'?: boolean;\n /** Drawer width/height preset */\n size?: DrawerSize;\n /**\n * Visible title text rendered inside the header.\n * Used as the dialog's accessible label via `aria-labelledby` when `label` is not set.\n */\n title?: string;\n};\n\n/**\n * A panel that slides in from any edge of the screen, built on the native `<dialog>` element.\n *\n * @element ore-drawer\n *\n * @attr {boolean} open - Controlled open state\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} placement - 'left' | 'right' (default) | 'top' | 'bottom'\n * @attr {string} size - 'sm' | 'lg' | 'full'\n * @attr {string} title - Visible header title text\n * @attr {string} label - Invisible aria-label (for drawers without a visible title)\n * @attr {boolean} dismissible - Show the close (×) button (default: true)\n * @attr {string} drag-handle-placement - 'outside' (default) | 'inset'\n * @attr {string} backdrop - Backdrop style: 'opaque' (default) | 'blur' | 'transparent'\n * @attr {boolean} persistent - Prevent backdrop-click from closing (default: false)\n *\n * @fires open-change - When the drawer state changes. detail: { open, reason }\n *\n * @slot header - Drawer header content\n * @slot - Main body content\n * @slot footer - Drawer footer content\n *\n * @cssprop --drawer-backdrop-bg - Backdrop background color\n * @cssprop --drawer-bg - Panel background color\n * @cssprop --drawer-size - Panel width (horizontal) or height (vertical)\n * @cssprop --drawer-shadow - Panel box-shadow\n * @cssprop --drawer-panel-blur - Panel backdrop blur amount\n *\n * @part drag-handle - Drawer drag handle.\n * @part panel - Panel container.\n * @part header - Header container.\n * @part close-btn - Close button.\n * @part body - Body content container.\n * @part footer - Footer container.\n * @example\n * ```html\n * <!-- With visible title -->\n * <ore-drawer open title=\"Settings\" placement=\"right\">\n * <p>Settings content here.</p>\n * </ore-drawer>\n *\n * <!-- With custom header slot -->\n * <ore-drawer open placement=\"right\">\n * <span slot=\"header\">Settings</span>\n * <p>Settings content here.</p>\n * </ore-drawer>\n * ```\n */\n\nexport const DRAWER_TAG = 'ore-drawer' as const;\ndefine<OreDrawerProps>(DRAWER_TAG, {\n props: {\n backdrop: prop.oneOf(DRAWER_BACKDROPS),\n 'default-open': prop.bool(false),\n dismissible: prop.bool(true),\n 'drag-handle-placement': prop.oneOf(['outside', 'inset'] as const, 'outside'),\n 'initial-focus': prop.string(),\n label: prop.string(),\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n persistent: prop.bool(false),\n placement: prop.oneOf(['left', 'right', 'top', 'bottom'] as const, 'right'),\n 'return-focus': prop.bool(true),\n size: prop.oneOf(DRAWER_SIZES),\n title: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreDrawerEvents>();\n const slots = useSlots();\n\n const drawerLabelId = createId('drawer-label');\n const dialogRef = ref<HTMLDialogElement>();\n const panelRef = ref<HTMLDivElement>();\n\n // Drag-to-close state\n const isSwipeClosing = signal(false);\n let swipe: PanGesture | undefined;\n let swipeCloseTimer: ReturnType<typeof setTimeout> | undefined;\n\n const getHeaderText = () => props.label.value ?? props.title.value ?? '';\n const hasHeaderTitle = () => slots.has('header').value || !!getHeaderText();\n\n // Header is visible when there is slot content, a text label, or a close button.\n const hasHeader = () => hasHeaderTitle() || props.dismissible.value;\n const hasFooter = () => slots.has('footer').value;\n\n const getPlacement = (): DrawerPlacement => props.placement.value || 'right';\n\n const getSwipeConfig = (): DrawerSwipeConfig => drawerSwipeConfig[getPlacement()];\n\n const getSnapThreshold = (panel: HTMLElement, axis: PanAxis) => {\n const panelSize = axis === 'x' ? panel.offsetWidth : panel.offsetHeight;\n\n return Math.min(96, Math.max(36, panelSize * 0.18));\n };\n\n const shouldCommitSwipeClose = (distance: number, threshold: number) => {\n return getSwipeConfig().closingDistance(distance) >= threshold;\n };\n\n const finalizeSwipeClose = (panel: HTMLElement) => {\n if (!isSwipeClosing.value) return;\n\n if (swipeCloseTimer) {\n clearTimeout(swipeCloseTimer);\n swipeCloseTimer = undefined;\n }\n\n const dialog = dialogRef.value;\n\n if (!dialog) {\n isSwipeClosing.value = false;\n\n return;\n }\n\n dialog.style.transition = 'none';\n panel.style.opacity = '0';\n panel.style.visibility = 'hidden';\n void dialog.offsetWidth;\n\n requestClose('swipe');\n };\n\n const startSwipeClose = (panel: HTMLElement, swipe: DrawerSwipeConfig, committedDistance: number) => {\n if (isSwipeClosing.value) return;\n\n isSwipeClosing.value = true;\n\n const panelSize = swipe.axis === 'x' ? panel.offsetWidth : panel.offsetHeight;\n // Preserve overshoot so closing continues from the dragged position instead of snapping back.\n const exitDistance =\n committedDistance >= 0 ? Math.max(committedDistance, panelSize) : Math.min(committedDistance, -panelSize);\n const exitTransform = swipe.translate(exitDistance);\n\n // Commit the current drag position first so the magnetic snap continues\n // from the finger instead of jumping back to rest.\n panel.style.transition = 'transform 180ms cubic-bezier(0.2, 0.9, 0.2, 1), opacity 180ms ease-out';\n void panel.offsetWidth;\n\n const onExitTransitionEnd = (ev: TransitionEvent) => {\n if (ev.target !== panel || ev.propertyName !== 'transform') return;\n\n panel.removeEventListener('transitionend', onExitTransitionEnd);\n finalizeSwipeClose(panel);\n };\n\n panel.addEventListener('transitionend', onExitTransitionEnd);\n\n const durationMs = parseFloat(getComputedStyle(panel).transitionDuration) * 1000;\n\n swipeCloseTimer = setTimeout(() => {\n panel.removeEventListener('transitionend', onExitTransitionEnd);\n finalizeSwipeClose(panel);\n }, durationMs + 50);\n\n panel.style.transform = exitTransform;\n panel.style.opacity = '0.2';\n };\n\n const resetPanelDragStyles = (panel: HTMLElement) => {\n if (swipeCloseTimer) {\n clearTimeout(swipeCloseTimer);\n swipeCloseTimer = undefined;\n }\n\n // Re-enable transitions so the snap-back or exit animates.\n panel.style.transition = '';\n panel.style.transform = '';\n panel.style.opacity = '';\n panel.style.visibility = '';\n isSwipeClosing.value = false;\n };\n\n const handleSwipeEnd = ({ distance, reason }: PanGestureEndDetail): void => {\n const panel = panelRef.value;\n\n if (!panel) return;\n\n const threshold = getSnapThreshold(panel, getSwipeConfig().axis);\n\n if (reason === 'release' && shouldCommitSwipeClose(distance, threshold)) {\n startSwipeClose(panel, getSwipeConfig(), distance);\n\n return;\n }\n\n resetPanelDragStyles(panel);\n };\n\n // ────────────────────────────────────────────────────────────────\n // Overlay State Management\n // ────────────────────────────────────────────────────────────────\n\n const { closeWithAnimation, mount, overlay, requestClose } = useDialogControl({\n beforeOpen: (dialog) => {\n // Clear any inline drag styles from a previous swipe-close so the CSS\n // entry animation starts from the correct base state.\n const panel = panelRef.value;\n\n if (panel) resetPanelDragStyles(panel);\n\n void dialog;\n },\n closeRequestDetail: (_reason) => ({ placement: props.placement.value ?? 'right' }),\n defaultOpen: props['default-open'],\n dialogRef,\n getPanelEl: () => panelRef.value,\n host: el,\n initialFocus: props['initial-focus'],\n isPersistent: () => Boolean(props.persistent.value),\n onCleanup,\n onEvent,\n onNativeClose: (reason) => {\n const panelEl = panelRef.value;\n\n // For swipe-close, keep the panel hidden and off-screen until the next\n // open cycle: resetting inline styles during native close can produce\n // a visible frame at the rest position on some browsers.\n if (panelEl && reason !== 'swipe') resetPanelDragStyles(panelEl);\n\n const dialog = dialogRef.value;\n\n if (dialog) dialog.style.transition = '';\n\n emit('open-change', { open: false, reason });\n },\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n openProp: props.open,\n performClose: (dialog, reason) => {\n if (reason === 'swipe') {\n dialog.close();\n } else {\n closeWithAnimation();\n }\n },\n returnFocus: props['return-focus'],\n });\n\n const handleCloseButtonClick = () => {\n requestClose('trigger');\n };\n\n // ────────────────────────────────────────────────────────────────\n // Lifecycle: Setup Drawer Integration\n // ────────────────────────────────────────────────────────────────\n\n onMounted(() => {\n const dialog = dialogRef.value;\n\n if (!dialog) return;\n\n // Expose imperative API\n const drawerEl = el as DrawerElement;\n\n drawerEl.show = () => {\n overlay.open('programmatic');\n };\n\n drawerEl.hide = () => {\n overlay.close('programmatic');\n };\n\n // ────────────────────────────────────────────────────────────\n // Event Handlers: Backdrop Click\n // ────────────────────────────────────────────────────────────\n // `mount()` registers the open-prop watcher and the shared native\n // `close`/`cancel` handling (Escape). Drawer registers only its own\n // backdrop-click handler on top, since swipe-to-dismiss adds guards the\n // shared `handleBackdropClick` doesn't know about.\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (props.persistent.value) return;\n\n if (swipe?.active || isSwipeClosing.value) return;\n\n if (e.target !== dialog) return; // Click inside panel\n\n requestClose('outsideClick');\n };\n\n mount();\n\n onEvent(dialog, 'click', handleBackdropClick);\n\n // Drag-to-close handlers: scoped to the handle element only so interactions\n // with panel content don't accidentally start a drag.\n const panel = panelRef.value;\n const dragHandleEl = panel?.querySelector<HTMLElement>('[part=\"drag-handle\"]');\n\n if (dragHandleEl) {\n swipe = createPanGesture(dragHandleEl, {\n axis: () => getSwipeConfig().axis,\n disabled: () => isSwipeClosing.value,\n onEnd: handleSwipeEnd,\n onMove: ({ distance }) => {\n const currentPanel = panelRef.value;\n\n if (!currentPanel) return;\n\n const swipeConfig = getSwipeConfig();\n const threshold = getSnapThreshold(currentPanel, swipeConfig.axis);\n const closingDistance = swipeConfig.closingDistance(distance);\n const progress = Math.min(closingDistance / threshold, 1);\n\n currentPanel.style.transition = 'none';\n currentPanel.style.transform = swipeConfig.translate(distance);\n currentPanel.style.opacity = String(1 - progress * 0.4);\n },\n });\n }\n\n return () => {\n swipe?.dispose();\n swipe = undefined;\n overlay.dispose();\n };\n });\n\n return html`\n <dialog\n ref=${dialogRef}\n aria-modal=\"true\"\n aria-label=\"${() => props.label.value ?? null}\"\n aria-labelledby=\"${() => (!props.label.value ? drawerLabelId : null)}\">\n <div class=\"panel\" part=\"panel\" ref=${panelRef}>\n <div class=\"drag-handle\" part=\"drag-handle\" aria-label=\"Drag to close\" role=\"button\"></div>\n <div class=\"header\" part=\"header\" ?hidden=${() => !hasHeader()}>\n <span class=\"header-title\" id=\"${drawerLabelId}\" ?hidden=${() => !hasHeaderTitle()}>\n <slot name=\"header\">${() => getHeaderText()}</slot>\n </span>\n <button\n class=\"close-btn\"\n part=\"close-btn\"\n type=\"button\"\n aria-label=\"Close\"\n ?hidden=${() => !props.dismissible.value}\n @click=${handleCloseButtonClick}>\n <ore-icon name=\"x\" size=\"16\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n </div>\n <div class=\"body\" part=\"body\">\n <slot></slot>\n </div>\n <div class=\"footer\" part=\"footer\" ?hidden=${() => !hasFooter()}>\n <slot name=\"footer\"></slot>\n </div>\n </div>\n </dialog>\n `;\n },\n styles: [forcedColorsMixin, coarsePointerMixin, reducedMotionMixin, styles],\n});\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ComponentSize, RoundedSize, ThemeColor } from '../../types';
|
|
2
|
+
declare const ALERT_VARIANTS: readonly ["solid", "flat", "bordered"];
|
|
2
3
|
export { ICON_TAG } from '../../content/icon/icon';
|
|
3
4
|
export type OreAlertEvents = {
|
|
4
5
|
dismiss: {
|
|
@@ -27,7 +28,7 @@ export type OreAlertProps = {
|
|
|
27
28
|
/** Alert size */
|
|
28
29
|
size?: ComponentSize;
|
|
29
30
|
/** Visual style variant */
|
|
30
|
-
variant?:
|
|
31
|
+
variant?: (typeof ALERT_VARIANTS)[number];
|
|
31
32
|
};
|
|
32
33
|
/**
|
|
33
34
|
* A status/feedback banner with optional heading, icon slot, and dismiss button.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alert.d.ts","sourceRoot":"","sources":["../../../src/feedback/alert/alert.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"alert.d.ts","sourceRoot":"","sources":["../../../src/feedback/alert/alert.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1E,QAAA,MAAM,cAAc,wCAAyC,CAAC;AAI9D,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE;QAAE,aAAa,EAAE,UAAU,CAAA;KAAE,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB;IAClB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,oCAAoC;IACpC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oBAAoB;IACpB,OAAO,CAAC,EAAE,WAAW,GAAG,EAAE,CAAC;IAC3B,iBAAiB;IACjB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;CAC3C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,eAAO,MAAM,SAAS,EAAG,WAAoB,CAAC"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import type { RoundedSize, ThemeColor
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { RoundedSize, ThemeColor } from '../../types';
|
|
2
|
+
declare const BADGE_ANCHORS: readonly ["top-end", "top-start", "bottom-end", "bottom-start"];
|
|
3
|
+
declare const BADGE_SIZES: readonly ["xs", "sm", "md", "lg"];
|
|
4
|
+
declare const BADGE_VARIANTS: readonly ["solid", "flat", "bordered", "outline", "frost"];
|
|
5
|
+
type BadgeVariant = (typeof BADGE_VARIANTS)[number];
|
|
6
|
+
type BadgeSize = (typeof BADGE_SIZES)[number];
|
|
4
7
|
/** Badge component properties */
|
|
5
8
|
export type OreBadgeProps = {
|
|
6
9
|
/**
|
|
@@ -8,7 +11,7 @@ export type OreBadgeProps = {
|
|
|
8
11
|
* and the badge pins to a corner over the slotted content.
|
|
9
12
|
* Value controls which corner: 'top-end' (default) | 'top-start' | 'bottom-end' | 'bottom-start'
|
|
10
13
|
*/
|
|
11
|
-
anchor?:
|
|
14
|
+
anchor?: (typeof BADGE_ANCHORS)[number];
|
|
12
15
|
/** Theme color */
|
|
13
16
|
color?: ThemeColor;
|
|
14
17
|
/** Numeric count to display */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../../../src/feedback/badge/badge.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../../../src/feedback/badge/badge.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG3D,QAAA,MAAM,aAAa,iEAAkE,CAAC;AACtF,QAAA,MAAM,WAAW,mCAAoC,CAAC;AACtD,QAAA,MAAM,cAAc,4DAA6D,CAAC;AAElF,KAAK,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AACpD,KAAK,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9C,iCAAiC;AACjC,MAAM,MAAM,aAAa,GAAG;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;IACxC,kBAAkB;IAClB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,iBAAiB;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,SAAS,EAAG,WAAoB,CAAC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { COMPONENT_SIZES, THEME_COLORS } from '../../types';
|
|
2
2
|
export { ICON_TAG } from '../../content/icon/icon';
|
|
3
|
+
declare const CHIP_ROUNDED: readonly ["", "none", "sm", "md", "lg", "xl", "2xl", "3xl", "full"];
|
|
4
|
+
declare const CHIP_VARIANTS: readonly ["solid", "flat", "bordered", "outline", "ghost", "frost"];
|
|
3
5
|
/** Chip component properties */
|
|
4
6
|
type ChipBaseProps = {
|
|
5
7
|
/** Theme color */
|
|
6
|
-
color?:
|
|
8
|
+
color?: (typeof THEME_COLORS)[number];
|
|
7
9
|
/** Disable interactions */
|
|
8
10
|
disabled?: boolean;
|
|
9
11
|
/** Accessible label (required for icon-only chips) */
|
|
@@ -11,13 +13,13 @@ type ChipBaseProps = {
|
|
|
11
13
|
/** Content layout: 'inline' (default) keeps icon and label on one line; 'stacked' places the icon above a wrapping label */
|
|
12
14
|
layout?: 'inline' | 'stacked';
|
|
13
15
|
/** Border radius override */
|
|
14
|
-
rounded?:
|
|
16
|
+
rounded?: (typeof CHIP_ROUNDED)[number];
|
|
15
17
|
/** Component size */
|
|
16
|
-
size?:
|
|
18
|
+
size?: (typeof COMPONENT_SIZES)[number];
|
|
17
19
|
/** Value associated with this chip: included in emitted event detail */
|
|
18
20
|
value?: string;
|
|
19
21
|
/** Visual style variant */
|
|
20
|
-
variant?:
|
|
22
|
+
variant?: (typeof CHIP_VARIANTS)[number];
|
|
21
23
|
};
|
|
22
24
|
type OreChipMode = 'static' | 'removable' | 'selectable' | 'action';
|
|
23
25
|
/** Read-only presentation chip */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chip.d.ts","sourceRoot":"","sources":["../../../src/feedback/chip/chip.ts"],"names":[],"mappings":"AAUA,OAAO,
|
|
1
|
+
{"version":3,"file":"chip.d.ts","sourceRoot":"","sources":["../../../src/feedback/chip/chip.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,eAAe,EAAiB,YAAY,EAAE,MAAM,aAAa,CAAC;AAM3E,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD,QAAA,MAAM,YAAY,qEAAkC,CAAC;AACrD,QAAA,MAAM,aAAa,qEAAsE,CAAC;AAM1F,gCAAgC;AAChC,KAAK,aAAa,GAAG;IACnB,kBAAkB;IAClB,KAAK,CAAC,EAAE,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;IACtC,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4HAA4H;IAC5H,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC9B,6BAA6B;IAC7B,OAAO,CAAC,EAAE,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;IACxC,qBAAqB;IACrB,IAAI,CAAC,EAAE,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;IACxC,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,OAAO,CAAC,EAAE,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;CAC1C,CAAC;AAEF,KAAK,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEpE,kCAAkC;AAClC,KAAK,eAAe,GAAG;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;CACvC,CAAC;AAEF,0BAA0B;AAC1B,KAAK,kBAAkB,GAAG;IACxB,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;CACzC,CAAC;AAEF,2BAA2B;AAC3B,KAAK,mBAAmB,GAAG;IACzB,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;CAC1C,CAAC;AAEF,6FAA6F;AAC7F,KAAK,eAAe,GAAG;IACrB,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;CACtC,CAAC;AAQF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC9E,KAAK,EAAE;QAAE,aAAa,EAAE,UAAU,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAChE,MAAM,EAAE;QAAE,aAAa,EAAE,UAAU,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,aAAa,GACtC,CAAC,eAAe,GAAG,kBAAkB,GAAG,mBAAmB,GAAG,eAAe,CAAC,CAAC;AAEjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8EG;AACH,eAAO,MAAM,QAAQ,EAAG,UAAmB,CAAC"}
|
package/dist/grid-item.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./layout/grid-item/grid-item.css.cjs");let t=require("@vielzeug/ripple"),n=require("@vielzeug/ore");var r=`ore-grid-item`;(0,n.define)(
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./layout/grid-item/grid-item.css.cjs");let t=require("@vielzeug/ripple"),n=require("@vielzeug/ore");var r=[`start`,`center`,`end`,`stretch`],i=[`1`,`2`,`3`,`4`,`5`,`6`,`7`,`8`,`9`,`10`,`11`,`12`,`full`],a=[`1`,`2`,`3`,`4`,`5`,`6`,`full`],o=`ore-grid-item`;(0,n.define)(o,{props:{align:n.prop.oneOf(r),area:n.prop.string(),col:n.prop.string(),colSpan:n.prop.oneOf(i),justify:n.prop.oneOf(r),row:n.prop.string(),rowSpan:n.prop.oneOf(a)},setup(e){let r=(0,t.computed)(()=>{let t=e.col.value,n=e.colSpan.value;return t||(n===`full`?`1 / -1`:n?`span ${n}`:``)}),i=(0,t.computed)(()=>{let t=e.row.value,n=e.rowSpan.value;return t||(n===`full`?`1 / -1`:n?`span ${n}`:``)});return(0,n.bind)({style:{gridArea:e.area,gridColumn:r,gridRow:i}}),n.html`
|
|
2
2
|
<slot></slot>
|
|
3
|
-
`},styles:[e.default]}),exports.GRID_ITEM_TAG=
|
|
3
|
+
`},styles:[e.default]}),exports.GRID_ITEM_TAG=o;
|
|
4
4
|
//# sourceMappingURL=grid-item.cjs.map
|