@windmorph/react 0.1.1 → 0.1.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"sheet-styles.js","names":[],"sources":["../../../src/components/sheet/sheet-styles.ts"],"sourcesContent":["import { reducedMotionFade } from '../../motion/reduced-motion';\nimport { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Bottom sheet values follow m3.material.io and Compose Material 3 (ModalBottomSheet.kt,\n * BottomSheetScaffold.kt, SheetDefaults.kt, SheetBottomTokens): at most 640px wide and\n * centred, with a 72px top margin (56px, and 56px side margins, above 640px), 28px top\n * corners, `surface-container-low` at level 1, a 32 × 4px `on-surface-variant` drag handle\n * with 22px above and below it, and (modal) a 32% scrim fading on the default effects\n * spring. It shows on the default spatial spring and hides on the fast spatial spring: the\n * exit moves the sheet, and position moves on spatial springs (2026-10-13). The exit\n * travels 112% of the sheet's size, so the spring's overshoot (under 10.5% of the\n * distance) settles out of view and the sheet never springs back into the window.\n *\n * Side sheets aren't in Compose; values follow m3.material.io and Material Components\n * Android (`m3_comp_sheet_side_*`): 256px wide, at most 400px; standard `surface` at level 0 with no\n * corners and an `outline-variant` divider on its content edge; modal\n * `surface-container-low` at level 1 with 16px corners on its free edge, over a 32% scrim;\n * detached sheets sit 16px from the edges with 16px corners all round. The header has 24px\n * start padding (16px with a back button) and 12px between its parts; the bottom action\n * row is at least 72px, 16px above and 24px below, start-aligned. They move on the bottom\n * sheet's springs.\n *\n * The sheets move by `translate` on a library-owned wrapper, never on the consumer's panel\n * (§10).\n *\n * The containers' colours, corners and shadows and the drag handle's colour come from the\n * sheets' component tokens (`sheet-tokens.ts`, `--md-comp-sheet-bottom-docked-*` and\n * `--md-comp-sheet-side-*`); each sheet's root carries its set's `data-tokens` and\n * `data-variant` (a detached side sheet also `data-detached`), and the parts read one private\n * variable each. A consumer class (`bg-surface-container`) still wins. Every class is written\n * out in full so Tailwind can find it.\n */\n\nconst scrim = [\n 'fixed inset-0 z-(--md-sys-z-scrim) bg-scrim/32 opacity-100',\n '[transition-property:opacity]',\n '[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n 'starting:opacity-0 data-exiting:opacity-0',\n];\n\n/**\n * Shows on the default spatial spring, hides on the fast spatial spring. Exits translate\n * 112% of the sheet's size, so the spring's overshoot stays off-screen. Under reduced motion\n * the sheet fades in and out where it rests, and settling on a detent is instant; a drag\n * still follows the pointer.\n */\nconst sheetMotion = [\n '[transition-property:translate]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing)]',\n 'data-dragging:[transition-duration:0s]',\n // A move down to a lower detent that isn't a drag's settle (BottomSheet's state): the fast\n // effects spring, no overshoot. Under reduced motion detent changes stay instant.\n 'motion-safe:data-lowering:not-data-exiting:not-data-dragging:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'motion-safe:data-lowering:not-data-exiting:not-data-dragging:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ...reducedMotionFade,\n];\n\n/**\n * The transitions that end a sheet's exit (`usePresence`): the slide, or the fade under\n * reduced motion.\n */\nexport const SHEET_EXIT_PROPERTIES = ['translate', 'opacity'] as const;\n\n/** Variant definitions for {@link BottomSheet}. */\nexport const bottomSheetStyles = tv({\n slots: {\n scrim,\n // The outermost element, which places the sheet and takes the consumer's `className` and\n // `style`. The sheet sits at its bottom (`items-end`), so a consumer inset that stretches\n // it (`top-24` with the library's `bottom-0`) never stretches the sheet.\n container: [\n 'fixed inset-x-0 bottom-0 z-(--md-sys-z-sheet) mx-auto flex w-full max-w-[640px] items-end',\n 'max-h-[calc(100dvh-72px)] min-[641px]:max-h-[calc(100dvh-56px)]',\n 'min-[641px]:max-w-[min(640px,calc(100%-112px))]',\n // Clips below its bottom edge only, where the panel's bleed hangs (see `panel`).\n '[clip-path:inset(-100vh_-100vw_0_-100vw)]',\n ],\n // Library-owned layer that moves the sheet, inside the container, so the motion never\n // animates the outermost element's `translate` (architecture §10 rule 4).\n motion: [\n 'flex w-full',\n 'translate-y-(--m3-sheet-offset) motion-safe:starting:translate-y-full motion-safe:data-exiting:translate-y-[112%]',\n ...sheetMotion,\n ],\n panel: [\n 'relative flex max-h-[calc(100dvh-72px)] w-full flex-col',\n // A bleed in the panel's colour below its bottom edge: when the spring overshoots, the\n // sheet lifts a few pixels off the window's edge and this fills the gap (Compose scales\n // the sheet up for the same reason). The container clips it at rest.\n \"after:pointer-events-none after:absolute after:inset-x-0 after:top-full after:h-[32px] after:[background-color:inherit] after:content-['']\",\n 'rounded-t-[var(--_md-comp-sheet-bottom-docked-container-shape)]',\n 'min-[641px]:max-h-[calc(100dvh-56px)]',\n 'bg-[var(--_md-comp-sheet-bottom-docked-container-color)] text-on-surface outline-none',\n 'shadow-[shadow:var(--_md-comp-sheet-bottom-docked-container-elevation)]',\n // Forced colours drop the container and the shadow: a border on the free edges.\n 'forced-colors:border-x forced-colors:border-t forced-colors:border-solid forced-colors:border-[CanvasText]',\n ],\n handle: [\n 'flex w-full shrink-0 justify-center pt-[22px] pb-[22px] outline-none',\n 'focus-ring-inset rounded-t-[var(--_md-comp-sheet-bottom-docked-container-shape)]',\n ],\n handleBar: [\n 'h-[4px] w-[32px] rounded-corner-extra-large',\n 'bg-[var(--_md-comp-sheet-bottom-docked-drag-handle-color)]',\n 'forced-colors:bg-[CanvasText] forced-colors:forced-color-adjust-none',\n ],\n closeRow: 'flex shrink-0 justify-end ps-[4px] pe-[4px]',\n content: 'min-h-0 flex-1 overflow-y-auto overscroll-contain',\n },\n variants: {\n variant: {\n modal: {},\n // Over the page without a scrim, below every overlay; the page stays usable around it.\n standard: { container: 'z-(--md-sys-z-sticky)' },\n },\n /** Dragging moves the sheet (`swipeable`); without it the handle is only a button. */\n swipeable: {\n true: {\n handle: 'cursor-grab touch-none data-dragging:cursor-grabbing',\n // At its top, the content leaves downward touch moves to the sheet's drag\n // (BottomSheet), and upward ones too while the sheet rests below its top detent\n // (`data-can-rise`), so an upward swipe raises the sheet before the content scrolls.\n content: [\n 'data-at-top:[touch-action:pan-x_pan-down_pinch-zoom]',\n 'data-at-top:data-can-rise:[touch-action:pan-x_pinch-zoom]',\n ],\n },\n false: { handle: 'cursor-pointer' },\n },\n },\n defaultVariants: { variant: 'modal', swipeable: true },\n});\n\n/**\n * The variants of {@link bottomSheetStyles}, for building on its styles. They are the recipe's keys, not\n * component props: their names (`hasIcon`, `isOpen`…) follow the classes and may change.\n */\nexport type BottomSheetStyleProps = VariantProps<typeof bottomSheetStyles>;\n\n/** Variant definitions for {@link SideSheet}. */\nexport const sideSheetStyles = tv({\n slots: {\n scrim,\n // A modal sheet's outermost element, which places it on its `edge` and takes the\n // consumer's `className` and `style`. It clips past that edge only, where a docked\n // panel's bleed hangs (see `compoundVariants`).\n container: 'fixed inset-y-0 z-(--md-sys-z-sheet) flex max-w-full',\n // Library-owned layer that moves a modal sheet, inside the container (architecture §10\n // rule 4); it holds a detached sheet's 16px margins, so the exit clears them. A swipe\n // moves it by `--m3-side-sheet-offset` (physical px, set by SideSheet while dragging and\n // settling); it enters from and leaves past the sheet's edge (`edge`).\n motion: ['flex min-w-0', 'translate-x-(--m3-side-sheet-offset)', ...sheetMotion],\n // 256px by default; a consumer width can't pass the spec's 400px maximum. A fixed\n // maximum (not a percentage) also caps the wrapper's shrink-to-fit width, and `min-w-0`\n // lets the panel shrink with the wrapper in a narrow window.\n panel: [\n 'flex w-[256px] max-w-[400px] min-w-0 flex-col text-on-surface outline-none',\n 'bg-[var(--_md-comp-sheet-side-container-color)]',\n 'shadow-[shadow:var(--_md-comp-sheet-side-container-elevation)]',\n ],\n header:\n 'flex min-h-[72px] shrink-0 items-center gap-[12px] ps-[24px] pe-[12px] pt-[12px] pb-[12px]',\n title: 'min-w-0 flex-1 text-title-large font-brand text-on-surface-variant',\n headerActions: 'ms-auto flex shrink-0 items-center gap-[12px]',\n // m3.material.io: \"Side sheets cannot scroll horizontally.\"\n content:\n 'min-h-0 flex-1 overflow-x-hidden overflow-y-auto overscroll-contain ps-[24px] pe-[24px]',\n footer: [\n 'flex min-h-[72px] shrink-0 flex-wrap items-center justify-start gap-[8px]',\n 'ps-[24px] pe-[24px] pt-[16px] pb-[24px]',\n ],\n // Standard sheets sit in the layout; their width opens and closes (a measured length, so\n // the panel stays on its edge), on the default spatial spring. The measured width is also\n // the maximum, so the spring's overshoot never opens a strip beside the panel.\n standard: [\n 'flex h-full w-[var(--m3-side-sheet-width,auto)] shrink-0 visible data-closed:invisible data-closed:w-0',\n 'max-w-[var(--m3-side-sheet-width,none)]',\n '[transition-property:width,visibility]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration),var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing),linear]',\n ],\n // The panel rides the root's inner edge (the content's side, `edge`), so as the width\n // moves the sheet slides in and out with its edge leading, as M3's reference moves it\n // (2026-10-24); pinned to the window's side, its far side was wiped into view first.\n standardClip: 'flex w-full min-w-0 overflow-hidden',\n standardFrame: 'flex h-full shrink-0',\n },\n variants: {\n variant: {\n modal: {},\n standard: { panel: 'h-full shrink-0' },\n },\n /** The window edge the sheet sits on; `end` is the right in LTR and the left in RTL. */\n edge: {\n end: {\n container: [\n 'end-0',\n '[clip-path:inset(-100vh_0_-100vh_-100vw)] rtl:[clip-path:inset(-100vh_-100vw_-100vh_0)]',\n ],\n motion: [\n 'motion-safe:starting:translate-x-full motion-safe:data-exiting:translate-x-[112%]',\n 'motion-safe:rtl:starting:-translate-x-full motion-safe:rtl:data-exiting:-translate-x-[112%]',\n ],\n standardClip: 'justify-start',\n },\n start: {\n container: [\n 'start-0',\n '[clip-path:inset(-100vh_-100vw_-100vh_0)] rtl:[clip-path:inset(-100vh_0_-100vh_-100vw)]',\n ],\n motion: [\n 'motion-safe:starting:-translate-x-full motion-safe:data-exiting:-translate-x-[112%]',\n 'motion-safe:rtl:starting:translate-x-full motion-safe:rtl:data-exiting:translate-x-[112%]',\n ],\n standardClip: 'justify-end',\n },\n },\n /**\n * A touch or pen swipe can close the modal sheet: the browser keeps vertical scrolling\n * and pinch zoom but never takes a horizontal pan from it (the content too, a scroller,\n * which would give its descendants panning back).\n */\n swipeable: {\n true: {\n panel: '[touch-action:pan-y_pinch-zoom]',\n content: '[touch-action:pan-y_pinch-zoom]',\n },\n false: {},\n },\n detached: {\n true: {\n motion: 'pt-[16px] pb-[16px]',\n standardFrame: 'pt-[16px] pb-[16px] ps-[16px] pe-[16px]',\n panel: 'rounded-[var(--_md-comp-sheet-side-container-shape)]',\n },\n false: {},\n },\n /** A back button leads the header. */\n hasBack: { true: { header: 'ps-[16px]' }, false: {} },\n /** A divider separates the standard sheet from the content beside it. */\n divider: { true: {}, false: {} },\n /** A divider separates the bottom actions from the content. */\n footerDivider: { true: { footer: 'border-t border-outline-variant' }, false: {} },\n },\n compoundVariants: [\n { detached: true, edge: 'end', class: { motion: 'pe-[16px]' } },\n { detached: true, edge: 'start', class: { motion: 'ps-[16px]' } },\n {\n variant: 'modal',\n detached: false,\n class: {\n panel: [\n // A bleed past the anchored edge fills the gap when the spring overshoots (see the\n // bottom sheet's panel).\n \"relative after:pointer-events-none after:absolute after:inset-y-0 after:w-[32px] after:[background-color:inherit] after:content-['']\",\n // Forced colours drop the container and the shadow: a border on the free edge.\n 'forced-colors:border-solid forced-colors:border-[CanvasText]',\n ],\n },\n },\n // The corners and the forced-colours border are on the free edge, the bleed on the other.\n {\n variant: 'modal',\n detached: false,\n edge: 'end',\n class: {\n panel: [\n 'rounded-s-[var(--_md-comp-sheet-side-container-shape)] after:start-full',\n 'forced-colors:border-s',\n ],\n },\n },\n {\n variant: 'modal',\n detached: false,\n edge: 'start',\n class: {\n panel: [\n 'rounded-e-[var(--_md-comp-sheet-side-container-shape)] after:end-full',\n 'forced-colors:border-e',\n ],\n },\n },\n {\n detached: true,\n class: {\n panel: 'forced-colors:border forced-colors:border-solid forced-colors:border-[CanvasText]',\n },\n },\n // The divider is on the content edge, the side away from the window's.\n {\n variant: 'standard',\n detached: false,\n divider: true,\n edge: 'end',\n class: { panel: 'border-s border-outline-variant' },\n },\n {\n variant: 'standard',\n detached: false,\n divider: true,\n edge: 'start',\n class: { panel: 'border-e border-outline-variant' },\n },\n ],\n defaultVariants: {\n variant: 'modal',\n edge: 'end',\n swipeable: false,\n detached: false,\n hasBack: false,\n divider: true,\n footerDivider: false,\n },\n});\n\n/**\n * The variants of {@link sideSheetStyles}, for building on its styles. They are the recipe's keys, not\n * component props: their names (`hasIcon`, `isOpen`…) follow the classes and may change.\n */\nexport type SideSheetStyleProps = VariantProps<typeof sideSheetStyles>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"sheet-styles.js","names":[],"sources":["../../../src/components/sheet/sheet-styles.ts"],"sourcesContent":["import { reducedMotionFade } from '../../motion/reduced-motion';\nimport { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Bottom sheet values follow m3.material.io and Compose Material 3 (ModalBottomSheet.kt,\n * BottomSheetScaffold.kt, SheetDefaults.kt, SheetBottomTokens): at most 640px wide and\n * centred, with a 72px top margin (56px, and 56px side margins, above 640px), 28px top\n * corners, `surface-container-low` at level 1, a 32 × 4px `on-surface-variant` drag handle\n * with 22px above and below it, and (modal) a 32% scrim fading on the default effects\n * spring. It shows on the default spatial spring and hides on the fast spatial spring: the\n * exit moves the sheet, and position moves on spatial springs (2026-10-13). The exit\n * travels 112% of the sheet's size, so the spring's overshoot (under 10.5% of the\n * distance) settles out of view and the sheet never springs back into the window.\n *\n * Side sheets aren't in Compose; values follow m3.material.io and Material Components\n * Android (`m3_comp_sheet_side_*`): 256px wide, at most 400px; standard `surface` at level 0 with no\n * corners and an `outline-variant` divider on its content edge; modal\n * `surface-container-low` at level 1 with 16px corners on its free edge, over a 32% scrim;\n * detached sheets sit 16px from the edges with 16px corners all round. The header has 24px\n * start padding (16px with a back button) and 12px between its parts; the bottom action\n * row is at least 72px, 16px above and 24px below, start-aligned. They move on the bottom\n * sheet's springs.\n *\n * The sheets move by `translate` on a library-owned wrapper, never on the consumer's panel\n * (§10).\n *\n * The containers' colours, corners and shadows and the drag handle's colour come from the\n * sheets' component tokens (`sheet-tokens.ts`, `--md-comp-sheet-bottom-docked-*` and\n * `--md-comp-sheet-side-*`); each sheet's root carries its set's `data-tokens` and\n * `data-variant` (a detached side sheet also `data-detached`), and the parts read one private\n * variable each. A consumer class (`bg-surface-container`) still wins. Every class is written\n * out in full so Tailwind can find it.\n */\n\nconst scrim = [\n 'fixed inset-0 z-(--md-sys-z-scrim) bg-scrim/32 opacity-100',\n '[transition-property:opacity]',\n '[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n 'starting:opacity-0 data-exiting:opacity-0',\n];\n\n/**\n * Shows on the default spatial spring, hides on the fast spatial spring. Exits translate\n * 112% of the sheet's size, so the spring's overshoot stays off-screen. Under reduced motion\n * the sheet fades in and out where it rests, and settling on a detent is instant; a drag\n * still follows the pointer.\n */\nconst sheetMotion = [\n '[transition-property:translate]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing)]',\n 'data-dragging:[transition-duration:0s]',\n // A move down to a lower detent that isn't a drag's settle (BottomSheet's state): the fast\n // effects spring, no overshoot. Under reduced motion detent changes stay instant.\n 'motion-safe:data-lowering:not-data-exiting:not-data-dragging:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'motion-safe:data-lowering:not-data-exiting:not-data-dragging:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ...reducedMotionFade,\n];\n\n/**\n * The transitions that end a sheet's exit (`usePresence`): the slide, or the fade under\n * reduced motion.\n */\nexport const SHEET_EXIT_PROPERTIES = ['translate', 'opacity'] as const;\n\n/** Variant definitions for {@link BottomSheet}. */\nexport const bottomSheetStyles = tv({\n slots: {\n scrim,\n // The outermost element, which places the sheet and takes the consumer's `className` and\n // `style`. The sheet sits at its bottom (`items-end`), so a consumer inset that stretches\n // it (`top-24` with the library's `bottom-0`) never stretches the sheet.\n container: [\n // Lifted above an on-screen keyboard and kept within what it leaves showing\n // (`--m3-sheet-keyboard-inset`, from the visual viewport; 2026-11-07).\n 'fixed inset-x-0 bottom-[var(--m3-sheet-keyboard-inset,0px)] z-(--md-sys-z-sheet) mx-auto flex w-full max-w-[640px] items-end',\n 'max-h-[calc(100dvh_-_72px_-_var(--m3-sheet-keyboard-inset,0px))] min-[641px]:max-h-[calc(100dvh_-_56px_-_var(--m3-sheet-keyboard-inset,0px))]',\n 'min-[641px]:max-w-[min(640px,calc(100%-112px))]',\n // Clips below its bottom edge only, where the panel's bleed hangs (see `panel`).\n '[clip-path:inset(-100vh_-100vw_0_-100vw)]',\n ],\n // Library-owned layer that moves the sheet, inside the container, so the motion never\n // animates the outermost element's `translate` (architecture §10 rule 4).\n motion: [\n 'flex w-full',\n 'translate-y-(--m3-sheet-offset) motion-safe:starting:translate-y-full motion-safe:data-exiting:translate-y-[112%]',\n ...sheetMotion,\n ],\n panel: [\n 'relative flex max-h-[calc(100dvh_-_72px_-_var(--m3-sheet-keyboard-inset,0px))] w-full flex-col',\n // A bleed in the panel's colour below its bottom edge: when the spring overshoots, the\n // sheet lifts a few pixels off the window's edge and this fills the gap (Compose scales\n // the sheet up for the same reason). The container clips it at rest.\n \"after:pointer-events-none after:absolute after:inset-x-0 after:top-full after:h-[32px] after:[background-color:inherit] after:content-['']\",\n 'rounded-t-[var(--_md-comp-sheet-bottom-docked-container-shape)]',\n 'min-[641px]:max-h-[calc(100dvh_-_56px_-_var(--m3-sheet-keyboard-inset,0px))]',\n 'bg-[var(--_md-comp-sheet-bottom-docked-container-color)] text-on-surface outline-none',\n 'shadow-[shadow:var(--_md-comp-sheet-bottom-docked-container-elevation)]',\n // Forced colours drop the container and the shadow: a border on the free edges.\n 'forced-colors:border-x forced-colors:border-t forced-colors:border-solid forced-colors:border-[CanvasText]',\n ],\n handle: [\n 'flex w-full shrink-0 justify-center pt-[22px] pb-[22px] outline-none',\n 'focus-ring-inset rounded-t-[var(--_md-comp-sheet-bottom-docked-container-shape)]',\n ],\n handleBar: [\n 'h-[4px] w-[32px] rounded-corner-extra-large',\n 'bg-[var(--_md-comp-sheet-bottom-docked-drag-handle-color)]',\n 'forced-colors:bg-[CanvasText] forced-colors:forced-color-adjust-none',\n ],\n closeRow: 'flex shrink-0 justify-end ps-[4px] pe-[4px]',\n content: 'min-h-0 flex-1 overflow-y-auto overscroll-contain',\n },\n variants: {\n variant: {\n modal: {},\n // Over the page without a scrim, below every overlay; the page stays usable around it.\n standard: { container: 'z-(--md-sys-z-sticky)' },\n },\n /** Dragging moves the sheet (`swipeable`); without it the handle is only a button. */\n swipeable: {\n true: {\n handle: 'cursor-grab touch-none data-dragging:cursor-grabbing',\n // At its top, the content leaves downward touch moves to the sheet's drag\n // (BottomSheet), and upward ones too while the sheet rests below its top detent\n // (`data-can-rise`), so an upward swipe raises the sheet before the content scrolls.\n content: [\n 'data-at-top:[touch-action:pan-x_pan-down_pinch-zoom]',\n 'data-at-top:data-can-rise:[touch-action:pan-x_pinch-zoom]',\n ],\n },\n false: { handle: 'cursor-pointer' },\n },\n },\n defaultVariants: { variant: 'modal', swipeable: true },\n});\n\n/**\n * The variants of {@link bottomSheetStyles}, for building on its styles. They are the recipe's keys, not\n * component props: their names (`hasIcon`, `isOpen`…) follow the classes and may change.\n */\nexport type BottomSheetStyleProps = VariantProps<typeof bottomSheetStyles>;\n\n/** Variant definitions for {@link SideSheet}. */\nexport const sideSheetStyles = tv({\n slots: {\n scrim,\n // A modal sheet's outermost element, which places it on its `edge` and takes the\n // consumer's `className` and `style`. It clips past that edge only, where a docked\n // panel's bleed hangs (see `compoundVariants`).\n container: 'fixed inset-y-0 z-(--md-sys-z-sheet) flex max-w-full',\n // Library-owned layer that moves a modal sheet, inside the container (architecture §10\n // rule 4); it holds a detached sheet's 16px margins, so the exit clears them. A swipe\n // moves it by `--m3-side-sheet-offset` (physical px, set by SideSheet while dragging and\n // settling); it enters from and leaves past the sheet's edge (`edge`).\n motion: ['flex min-w-0', 'translate-x-(--m3-side-sheet-offset)', ...sheetMotion],\n // 256px by default; a consumer width can't pass the spec's 400px maximum. A fixed\n // maximum (not a percentage) also caps the wrapper's shrink-to-fit width, and `min-w-0`\n // lets the panel shrink with the wrapper in a narrow window.\n panel: [\n 'flex w-[256px] max-w-[400px] min-w-0 flex-col text-on-surface outline-none',\n 'bg-[var(--_md-comp-sheet-side-container-color)]',\n 'shadow-[shadow:var(--_md-comp-sheet-side-container-elevation)]',\n ],\n header:\n 'flex min-h-[72px] shrink-0 items-center gap-[12px] ps-[24px] pe-[12px] pt-[12px] pb-[12px]',\n title: 'min-w-0 flex-1 text-title-large font-brand text-on-surface-variant',\n headerActions: 'ms-auto flex shrink-0 items-center gap-[12px]',\n // m3.material.io: \"Side sheets cannot scroll horizontally.\"\n content:\n 'min-h-0 flex-1 overflow-x-hidden overflow-y-auto overscroll-contain ps-[24px] pe-[24px]',\n footer: [\n 'flex min-h-[72px] shrink-0 flex-wrap items-center justify-start gap-[8px]',\n 'ps-[24px] pe-[24px] pt-[16px] pb-[24px]',\n ],\n // Standard sheets sit in the layout; their width opens and closes (a measured length, so\n // the panel stays on its edge), on the default spatial spring. The measured width is also\n // the maximum, so the spring's overshoot never opens a strip beside the panel.\n standard: [\n 'flex h-full w-[var(--m3-side-sheet-width,auto)] shrink-0 visible data-closed:invisible data-closed:w-0',\n 'max-w-[var(--m3-side-sheet-width,none)]',\n '[transition-property:width,visibility]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration),var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing),linear]',\n ],\n // The panel rides the root's inner edge (the content's side, `edge`), so as the width\n // moves the sheet slides in and out with its edge leading, as M3's reference moves it\n // (2026-10-24); pinned to the window's side, its far side was wiped into view first.\n standardClip: 'flex w-full min-w-0 overflow-hidden',\n standardFrame: 'flex h-full shrink-0',\n },\n variants: {\n variant: {\n modal: {},\n standard: { panel: 'h-full shrink-0' },\n },\n /** The window edge the sheet sits on; `end` is the right in LTR and the left in RTL. */\n edge: {\n end: {\n container: [\n 'end-0',\n '[clip-path:inset(-100vh_0_-100vh_-100vw)] rtl:[clip-path:inset(-100vh_-100vw_-100vh_0)]',\n ],\n motion: [\n 'motion-safe:starting:translate-x-full motion-safe:data-exiting:translate-x-[112%]',\n 'motion-safe:rtl:starting:-translate-x-full motion-safe:rtl:data-exiting:-translate-x-[112%]',\n ],\n standardClip: 'justify-start',\n },\n start: {\n container: [\n 'start-0',\n '[clip-path:inset(-100vh_-100vw_-100vh_0)] rtl:[clip-path:inset(-100vh_0_-100vh_-100vw)]',\n ],\n motion: [\n 'motion-safe:starting:-translate-x-full motion-safe:data-exiting:-translate-x-[112%]',\n 'motion-safe:rtl:starting:translate-x-full motion-safe:rtl:data-exiting:translate-x-[112%]',\n ],\n standardClip: 'justify-end',\n },\n },\n /**\n * A touch or pen swipe can close the modal sheet: the browser keeps vertical scrolling\n * and pinch zoom but never takes a horizontal pan from it (the content too, a scroller,\n * which would give its descendants panning back).\n */\n swipeable: {\n true: {\n panel: '[touch-action:pan-y_pinch-zoom]',\n content: '[touch-action:pan-y_pinch-zoom]',\n },\n false: {},\n },\n detached: {\n true: {\n motion: 'pt-[16px] pb-[16px]',\n standardFrame: 'pt-[16px] pb-[16px] ps-[16px] pe-[16px]',\n panel: 'rounded-[var(--_md-comp-sheet-side-container-shape)]',\n },\n false: {},\n },\n /** A back button leads the header. */\n hasBack: { true: { header: 'ps-[16px]' }, false: {} },\n /** A divider separates the standard sheet from the content beside it. */\n divider: { true: {}, false: {} },\n /** A divider separates the bottom actions from the content. */\n footerDivider: { true: { footer: 'border-t border-outline-variant' }, false: {} },\n },\n compoundVariants: [\n { detached: true, edge: 'end', class: { motion: 'pe-[16px]' } },\n { detached: true, edge: 'start', class: { motion: 'ps-[16px]' } },\n {\n variant: 'modal',\n detached: false,\n class: {\n panel: [\n // A bleed past the anchored edge fills the gap when the spring overshoots (see the\n // bottom sheet's panel).\n \"relative after:pointer-events-none after:absolute after:inset-y-0 after:w-[32px] after:[background-color:inherit] after:content-['']\",\n // Forced colours drop the container and the shadow: a border on the free edge.\n 'forced-colors:border-solid forced-colors:border-[CanvasText]',\n ],\n },\n },\n // The corners and the forced-colours border are on the free edge, the bleed on the other.\n {\n variant: 'modal',\n detached: false,\n edge: 'end',\n class: {\n panel: [\n 'rounded-s-[var(--_md-comp-sheet-side-container-shape)] after:start-full',\n 'forced-colors:border-s',\n ],\n },\n },\n {\n variant: 'modal',\n detached: false,\n edge: 'start',\n class: {\n panel: [\n 'rounded-e-[var(--_md-comp-sheet-side-container-shape)] after:end-full',\n 'forced-colors:border-e',\n ],\n },\n },\n {\n detached: true,\n class: {\n panel: 'forced-colors:border forced-colors:border-solid forced-colors:border-[CanvasText]',\n },\n },\n // The divider is on the content edge, the side away from the window's.\n {\n variant: 'standard',\n detached: false,\n divider: true,\n edge: 'end',\n class: { panel: 'border-s border-outline-variant' },\n },\n {\n variant: 'standard',\n detached: false,\n divider: true,\n edge: 'start',\n class: { panel: 'border-e border-outline-variant' },\n },\n ],\n defaultVariants: {\n variant: 'modal',\n edge: 'end',\n swipeable: false,\n detached: false,\n hasBack: false,\n divider: true,\n footerDivider: false,\n },\n});\n\n/**\n * The variants of {@link sideSheetStyles}, for building on its styles. They are the recipe's keys, not\n * component props: their names (`hasIcon`, `isOpen`…) follow the classes and may change.\n */\nexport type SideSheetStyleProps = VariantProps<typeof sideSheetStyles>;\n"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { useSyncExternalStore } from "react";
3
+ //#region src/components/sheet/use-keyboard-inset.ts
4
+ function subscribe(onChange) {
5
+ const viewport = window.visualViewport;
6
+ if (!viewport) return () => {};
7
+ viewport.addEventListener("resize", onChange);
8
+ viewport.addEventListener("scroll", onChange);
9
+ return () => {
10
+ viewport.removeEventListener("resize", onChange);
11
+ viewport.removeEventListener("scroll", onChange);
12
+ };
13
+ }
14
+ /**
15
+ * How far the on-screen keyboard covers the bottom of the window, in px. Phone browsers lay a
16
+ * keyboard over the page without resizing it (Chrome's default `interactive-widget`), so an
17
+ * element fixed to the window's bottom sits behind it; the visual viewport shrinks instead.
18
+ * A zoomed page shrinks it too, so only an unzoomed one counts (2026-11-07).
19
+ */
20
+ function getInset() {
21
+ const viewport = window.visualViewport;
22
+ if (!viewport || Math.abs(viewport.scale - 1) > .01) return 0;
23
+ return Math.max(0, Math.round(window.innerHeight - viewport.offsetTop - viewport.height));
24
+ }
25
+ const getServerInset = () => 0;
26
+ /** The keyboard's height over the window's bottom edge, followed as it opens and closes. */
27
+ function useKeyboardInset() {
28
+ return useSyncExternalStore(subscribe, getInset, getServerInset);
29
+ }
30
+ //#endregion
31
+ export { useKeyboardInset };
32
+
33
+ //# sourceMappingURL=use-keyboard-inset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-keyboard-inset.js","names":[],"sources":["../../../src/components/sheet/use-keyboard-inset.ts"],"sourcesContent":["'use client';\n\nimport { useSyncExternalStore } from 'react';\n\nfunction subscribe(onChange: () => void) {\n const viewport = window.visualViewport;\n if (!viewport) return () => {};\n viewport.addEventListener('resize', onChange);\n viewport.addEventListener('scroll', onChange);\n return () => {\n viewport.removeEventListener('resize', onChange);\n viewport.removeEventListener('scroll', onChange);\n };\n}\n\n/**\n * How far the on-screen keyboard covers the bottom of the window, in px. Phone browsers lay a\n * keyboard over the page without resizing it (Chrome's default `interactive-widget`), so an\n * element fixed to the window's bottom sits behind it; the visual viewport shrinks instead.\n * A zoomed page shrinks it too, so only an unzoomed one counts (2026-11-07).\n */\nfunction getInset() {\n const viewport = window.visualViewport;\n if (!viewport || Math.abs(viewport.scale - 1) > 0.01) return 0;\n return Math.max(0, Math.round(window.innerHeight - viewport.offsetTop - viewport.height));\n}\n\nconst getServerInset = () => 0;\n\n/** The keyboard's height over the window's bottom edge, followed as it opens and closes. */\nexport function useKeyboardInset(): number {\n return useSyncExternalStore(subscribe, getInset, getServerInset);\n}\n"],"mappings":";;;AAIA,SAAS,UAAU,UAAsB;CACvC,MAAM,WAAW,OAAO;CACxB,IAAI,CAAC,UAAU,aAAa,CAAC;CAC7B,SAAS,iBAAiB,UAAU,QAAQ;CAC5C,SAAS,iBAAiB,UAAU,QAAQ;CAC5C,aAAa;EACX,SAAS,oBAAoB,UAAU,QAAQ;EAC/C,SAAS,oBAAoB,UAAU,QAAQ;CACjD;AACF;;;;;;;AAQA,SAAS,WAAW;CAClB,MAAM,WAAW,OAAO;CACxB,IAAI,CAAC,YAAY,KAAK,IAAI,SAAS,QAAQ,CAAC,IAAI,KAAM,OAAO;CAC7D,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,cAAc,SAAS,YAAY,SAAS,MAAM,CAAC;AAC1F;AAEA,MAAM,uBAAuB;;AAG7B,SAAgB,mBAA2B;CACzC,OAAO,qBAAqB,WAAW,UAAU,cAAc;AACjE"}