@windmorph/react 0.1.1 → 0.2.0

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.
Files changed (61) hide show
  1. package/dist/components/checkbox/checkbox-tokens.d.ts +1 -1
  2. package/dist/components/date-picker/date-picker-styles.d.ts +6 -6
  3. package/dist/components/date-picker/date-picker-styles.js +5 -5
  4. package/dist/components/date-picker/date-picker-styles.js.map +1 -1
  5. package/dist/components/dialog/dialog-styles.js +5 -5
  6. package/dist/components/dialog/dialog-styles.js.map +1 -1
  7. package/dist/components/menu/use-reveal-entering.d.ts +19 -0
  8. package/dist/components/navigation/NavItem.js +1 -1
  9. package/dist/components/navigation/NavItem.js.map +1 -1
  10. package/dist/components/navigation/NavigationRail.d.ts +21 -2
  11. package/dist/components/navigation/NavigationRail.js +18 -4
  12. package/dist/components/navigation/NavigationRail.js.map +1 -1
  13. package/dist/components/navigation/navigation-styles.d.ts +21 -0
  14. package/dist/components/navigation/navigation-styles.js +6 -1
  15. package/dist/components/navigation/navigation-styles.js.map +1 -1
  16. package/dist/components/select/Select.js +12 -1
  17. package/dist/components/select/Select.js.map +1 -1
  18. package/dist/components/select/search-list.js +1 -1
  19. package/dist/components/select/search-list.js.map +1 -1
  20. package/dist/components/sheet/BottomSheet.js +6 -1
  21. package/dist/components/sheet/BottomSheet.js.map +1 -1
  22. package/dist/components/sheet/sheet-styles.js +4 -4
  23. package/dist/components/sheet/sheet-styles.js.map +1 -1
  24. package/dist/components/sheet/use-keyboard-inset.js +33 -0
  25. package/dist/components/sheet/use-keyboard-inset.js.map +1 -0
  26. package/dist/components/snackbar/snackbar-tokens.d.ts +1 -1
  27. package/dist/components/text-field/TextField.js +9 -1
  28. package/dist/components/text-field/TextField.js.map +1 -1
  29. package/dist/components/time-picker/TimeField.d.ts +118 -0
  30. package/dist/components/time-picker/TimeField.js +183 -0
  31. package/dist/components/time-picker/TimeField.js.map +1 -0
  32. package/dist/components/time-picker/TimeFieldDialog.js +70 -0
  33. package/dist/components/time-picker/TimeFieldDialog.js.map +1 -0
  34. package/dist/components/time-picker/TimePicker.js +1 -4
  35. package/dist/components/time-picker/TimePicker.js.map +1 -1
  36. package/dist/components/time-picker/hour-cycle.js +10 -0
  37. package/dist/components/time-picker/hour-cycle.js.map +1 -0
  38. package/dist/components/time-picker/time-format.js +84 -0
  39. package/dist/components/time-picker/time-format.js.map +1 -0
  40. package/dist/composites/button-group/ButtonGroup.d.ts +7 -0
  41. package/dist/composites/button-group/ButtonGroup.js +3 -2
  42. package/dist/composites/button-group/ButtonGroup.js.map +1 -1
  43. package/dist/composites/button-group/button-group-styles.d.ts +15 -3
  44. package/dist/composites/button-group/button-group-styles.js +16 -1
  45. package/dist/composites/button-group/button-group-styles.js.map +1 -1
  46. package/dist/index.d.ts +4 -2
  47. package/dist/index.js +4 -2
  48. package/dist/primitives.d.ts +4 -1
  49. package/dist/primitives.js +4 -1
  50. package/dist/styles.compiled.css +1 -1
  51. package/dist/theme/ThemeProvider.d.ts +6 -0
  52. package/dist/theme/ThemeProvider.js +44 -11
  53. package/dist/theme/ThemeProvider.js.map +1 -1
  54. package/dist/theme/ThemeScript.d.ts +4 -1
  55. package/dist/theme/ThemeScript.js +4 -1
  56. package/dist/theme/ThemeScript.js.map +1 -1
  57. package/dist/theme/context.js +18 -3
  58. package/dist/theme/context.js.map +1 -1
  59. package/dist/utils/closed-by.d.ts +5 -0
  60. package/dist/utils/placement.d.ts +11 -0
  61. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"BottomSheet.js","names":["Overlay"],"sources":["../../../src/components/sheet/BottomSheet.tsx"],"sourcesContent":["'use client';\n\nimport {\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type ComponentPropsWithRef,\n type CSSProperties,\n type HTMLAttributes,\n type KeyboardEvent,\n type PointerEvent,\n type ReactElement,\n type ReactNode,\n type Ref,\n type TransitionEvent,\n} from 'react';\nimport {\n mergeProps,\n useDialog,\n useFocusRing,\n useModalOverlay,\n useObjectRef,\n type AriaDialogProps,\n} from 'react-aria';\nimport { useOverlayTriggerState, type OverlayTriggerState } from 'react-stately';\nimport { Overlay } from '../../primitives/Overlay';\nimport { TriggerContext } from '../../primitives/TriggerContext';\nimport { usePresence } from '../../primitives/use-presence';\nimport { closedByOverlayOptions, type ClosedBy } from '../../utils/closed-by';\nimport { cn } from '../../utils/cn';\nimport { focusFirstInteractive } from '../../utils/focus';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\nimport { useThemeScope } from '../../theme/context';\nimport { IconButton } from '../icon-button/IconButton';\nimport { CloseIcon, CollapseIcon } from './sheet-icons';\nimport { contentGesture, scrolledAbove, type ContentGesture } from './bottom-sheet-gesture';\nimport { DRAG_SLOP, ownsGesture } from './sheet-gesture';\nimport {\n EXIT_DISTANCE,\n releaseMotion,\n releaseVelocity,\n settleTarget,\n type DragSample,\n type ReleaseMotion,\n} from './sheet-settle';\nimport { usePeekInert } from './use-peek-inert';\nimport { SHEET_EXIT_PROPERTIES, bottomSheetStyles } from './sheet-styles';\nimport { SheetTriggerContext } from './SheetTrigger';\n\nexport interface BottomSheetClassNames {\n /**\n * The outermost element, which places the sheet (where `className` and `style` go too):\n * `fixed` to the window's bottom by default, or, for example, `absolute` to a region.\n */\n container?: string;\n /** The dimmed layer behind a modal sheet. */\n scrim?: string;\n /** The sheet's surface: its colour, corners and shadow. */\n panel?: string;\n /** The drag handle's 48px pressable area. */\n handle?: string;\n /** The drag handle's visible bar, inside `handle`. */\n handleBar?: string;\n /** The row holding the close (modal) or collapse (standard) button. */\n closeRow?: string;\n /** The close or collapse icon button. */\n close?: string;\n /** The scrollable content area. */\n content?: string;\n}\n\n/** Words the bottom sheet announces. */\nexport interface BottomSheetLabels {\n /**\n * The drag handle while the sheet is half open or collapsed (it expands the sheet).\n * @default \"Expand sheet\"\n */\n expand: string;\n /** The drag handle and collapse button of a fully open standard sheet. @default \"Collapse sheet\" */\n collapse: string;\n /** The drag handle of a fully open modal sheet (it closes the sheet). @default \"Close sheet\" */\n dismiss: string;\n /** The close button of a modal sheet that fills the window height. @default \"Close\" */\n close: string;\n}\n\nconst DEFAULT_LABELS: BottomSheetLabels = {\n expand: 'Expand sheet',\n collapse: 'Collapse sheet',\n dismiss: 'Close sheet',\n close: 'Close',\n};\n\n/**\n * Where a standard bottom sheet rests: `collapsed` to its `peekHeight`, or `expanded` to its\n * full height (Compose `SheetValue.PartiallyExpanded` and `SheetValue.Expanded`). A sheet whose\n * content is no taller than the peek height is always expanded.\n */\nexport type BottomSheetDetent = 'collapsed' | 'expanded';\n\n/** The interactive strip at the top of a sheet without a drag handle (m3.material.io). */\nconst DRAG_ZONE = 48;\n\ntype Naming =\n | {\n /** Accessible name when there's no visible title. Required without `aria-labelledby`. */\n 'aria-label': string;\n }\n | {\n /** Id of the visible title that names the sheet. Required without `aria-label`. */\n 'aria-labelledby': string;\n };\n\ninterface BottomSheetOwnProps {\n /**\n * `modal` opens over a scrim as a dialog that blocks the page; `standard` sits over the\n * page without a scrim, so both can be used at once, and rests collapsed at `peekHeight`\n * or expanded (`detent`). @default \"modal\"\n */\n variant?: 'modal' | 'standard';\n /** Whether the sheet is open (controlled). */\n open?: boolean;\n /** Whether the sheet starts open when uncontrolled. @default false, or true when standard */\n defaultOpen?: boolean;\n /** Called when the sheet opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /** Show the drag handle. Without it, the top 48px of the sheet still drags. @default true */\n dragHandle?: boolean;\n /**\n * Whether dragging the handle, the top of the sheet or (touch and pen) its content moves\n * the sheet. Without it the handle stays a button (a press, Enter or Space), and Escape,\n * the scrim and the close button work as before.\n * @default true\n */\n swipeable?: boolean;\n /**\n * Open fully instead of to half the window when the content is taller than half the\n * window (modal). @default false\n */\n skipPartiallyExpanded?: boolean;\n /**\n * Height of a collapsed standard sheet, px. While collapsed, the content's top-level parts\n * that don't fit entirely in it are `inert`. @default 56\n */\n peekHeight?: number;\n /**\n * Where a standard sheet rests (controlled): `collapsed` at `peekHeight` or `expanded` to\n * its full height, so the app can raise or lower it (\"can be set by the system or toggled\n * by the user\", m3.material.io). Its handle, a drag, Escape and its collapse button then\n * only call `onDetentChange`. A modal sheet ignores it.\n */\n detent?: BottomSheetDetent;\n /**\n * Where a standard sheet first rests when uncontrolled; `expanded` opens it fully raised\n * (Compose `rememberStandardBottomSheetState(initialValue = SheetValue.Expanded)`). It\n * applies each time the sheet opens. A modal sheet ignores it.\n * @default \"collapsed\"\n */\n defaultDetent?: BottomSheetDetent;\n /**\n * Called when a standard sheet moves to another detent: by its handle (a press, Enter or\n * Space, or a drag), Escape or its collapse button. Not called for a modal sheet.\n */\n onDetentChange?: (detent: BottomSheetDetent) => void;\n /**\n * How the user can close a modal sheet, as HTML's `<dialog closedby>`: `any` (Escape, a\n * press on the scrim or a drag down), `closerequest` (Escape or a drag down) or `none`\n * (only its buttons or the app). @default \"any\"\n */\n closedBy?: ClosedBy;\n /**\n * While the sheet fills the window height, show a close button (modal) or a collapse\n * button (standard) at its top end. @default true\n */\n closeButton?: boolean;\n /** Replaces the close button's icon. */\n closeIcon?: ReactElement;\n /** Replaces the collapse button's icon. */\n collapseIcon?: ReactElement;\n /** Words the sheet announces, with English defaults. */\n labels?: Partial<BottomSheetLabels>;\n children?: ReactNode | ((props: { close: () => void }) => ReactNode);\n /** Classes for each part, by slot. */\n classNames?: BottomSheetClassNames;\n /**\n * Goes with `style` on the outermost element, the container that places the sheet, so a\n * layout class (`absolute`, `fixed`, a transform) moves the whole sheet and never clips or\n * stretches its surface. Style the surface with `classNames.panel`; `ref`, `data-*` and\n * other attributes go on the panel.\n */\n className?: string;\n /** Goes on the outermost element, with `className`. */\n style?: CSSProperties;\n /** Ref to the panel, the sheet's surface (`role=\"dialog\"`). */\n ref?: Ref<HTMLDivElement>;\n}\n\nexport type BottomSheetProps = BottomSheetOwnProps &\n Naming &\n Omit<ComponentPropsWithRef<'div'>, keyof BottomSheetOwnProps | 'aria-label' | 'aria-labelledby'>;\n\n/**\n * M3 bottom sheet. A `modal` sheet slides up over a scrim, opening to half the window when\n * its content is taller (drag the handle up, or press it, to expand), and closes on a drag\n * down, a press on the scrim or Escape, in one press from any height. A\n * `standard` sheet sits over the page without a scrim, collapsed to `peekHeight` until it\n * is expanded, or opening expanded with `defaultDetent=\"expanded\"`; `detent` and\n * `onDetentChange` control it.\n *\n * @example\n * <SheetTrigger>\n * <Button>Share</Button>\n * <BottomSheet aria-label=\"Share\">{({ close }) => <ShareOptions onPick={close} />}</BottomSheet>\n * </SheetTrigger>\n */\nexport function BottomSheet({\n variant = 'modal',\n open,\n defaultOpen,\n onOpenChange,\n ...props\n}: BottomSheetProps) {\n const trigger = useContext(SheetTriggerContext);\n const ownState = useOverlayTriggerState({\n isOpen: open,\n defaultOpen: defaultOpen ?? (variant === 'standard' ? true : undefined),\n onOpenChange,\n });\n const state = (variant === 'modal' ? trigger?.state : undefined) ?? ownState;\n const { isPresent, isExiting, exitProps } = usePresence(state.isOpen, SHEET_EXIT_PROPERTIES);\n if (!isPresent) return null;\n if (variant === 'standard')\n return (\n <BottomSheetPanel\n {...props}\n variant=\"standard\"\n state={state}\n isExiting={isExiting}\n exitProps={exitProps}\n />\n );\n return (\n <Overlay exiting={isExiting}>\n <ModalBottomSheet\n {...props}\n state={state}\n isExiting={isExiting}\n exitProps={exitProps}\n triggerOverlayProps={trigger?.overlayProps}\n />\n </Overlay>\n );\n}\n\nfunction ModalBottomSheet({\n state,\n isExiting,\n triggerOverlayProps,\n closedBy = 'any',\n classNames,\n ref,\n ...props\n}: Omit<PanelProps, 'variant' | 'panelProps'> & { triggerOverlayProps?: { id?: string } }) {\n const panelRef = useObjectRef(ref as Ref<HTMLDivElement>);\n const { modalProps, underlayProps } = useModalOverlay(\n {\n ...closedByOverlayOptions(closedBy, isExiting),\n // The panel handles Escape itself (below), so a nested control's Escape stays its own.\n isKeyboardDismissDisabled: true,\n },\n state,\n panelRef,\n );\n const { dialogProps } = useDialog(props as AriaDialogProps, panelRef);\n // Initial focus goes to the first interactive element (the drag handle, when there is one;\n // M3), as in a Dialog, not to the panel.\n useEffect(() => focusFirstInteractive(panelRef.current), [panelRef]);\n const styles = bottomSheetStyles();\n return (\n <>\n <div\n aria-hidden=\"true\"\n data-exiting={isExiting || undefined}\n className={styles.scrim({ class: classNames?.scrim })}\n />\n <div {...underlayProps} className=\"fixed inset-0 z-(--md-sys-z-sheet)\">\n <BottomSheetPanel\n {...props}\n variant=\"modal\"\n closedBy={closedBy}\n state={state}\n isExiting={isExiting}\n classNames={classNames}\n ref={panelRef}\n panelProps={mergeProps(triggerOverlayProps ?? {}, modalProps, dialogProps)}\n />\n </div>\n </>\n );\n}\n\ntype PanelProps = Omit<BottomSheetProps, 'open' | 'defaultOpen' | 'onOpenChange' | 'variant'> & {\n variant: 'modal' | 'standard';\n state: OverlayTriggerState;\n isExiting: boolean;\n exitProps: ReturnType<typeof usePresence>['exitProps'];\n /** Props from the modal overlay and dialog hooks. */\n panelProps?: HTMLAttributes<HTMLElement>;\n};\n\n/** The sheet itself: the moving wrapper, the panel, its handle and its detents. */\nfunction BottomSheetPanel({\n variant,\n state,\n isExiting,\n exitProps,\n panelProps: overlayPanelProps,\n dragHandle = true,\n swipeable = true,\n skipPartiallyExpanded = false,\n peekHeight = 56,\n detent: detentProp,\n defaultDetent = 'collapsed',\n onDetentChange,\n closedBy = 'any',\n closeButton = true,\n closeIcon,\n collapseIcon,\n labels: labelsProp,\n children,\n className,\n classNames,\n style,\n ref,\n ...rest\n}: PanelProps) {\n const labels = { ...DEFAULT_LABELS, ...labelsProp };\n const standard = variant === 'standard';\n const panelRef = useObjectRef(ref as Ref<HTMLDivElement>);\n const motionRef = useRef<HTMLDivElement>(null);\n const styles = bottomSheetStyles({ variant, swipeable });\n const [height, setHeight] = useState(0);\n const [viewport, setViewport] = useState({ width: 0, height: 0 });\n // The lower detent is half the window (modal) or the peek height (standard); `null` rests\n // on the lower detent when there is one.\n const [ownDetent, setOwnDetent] = useState<'expanded' | 'lower' | null>(\n standard && defaultDetent === 'expanded' ? 'expanded' : null,\n );\n const controlledDetent =\n standard && detentProp !== undefined\n ? detentProp === 'expanded'\n ? 'expanded'\n : 'lower'\n : undefined;\n const detent = controlledDetent ?? ownDetent;\n const [drag, setDrag] = useState<number | null>(null);\n // A drag's settle continues at the speed the pointer let go with, as Compose's\n // `animateTo(target, velocity)` does: the spring starts with that velocity, as a `linear()`\n // easing and duration set on the moving layer until its move ends (2026-10-17; it started\n // from rest). Under reduced motion settling stays instant.\n const { motion: motionScheme } = useThemeScope();\n const [settleMotion, setSettleMotion] = useState<ReleaseMotion | null>(null);\n // A drag let go to close: the offset stays where the pointer left it, so the exit leaves\n // from there (and, under reduced motion, fades there, as the side sheet does). If the\n // sheet is still open after that render, the app refused and the sheet springs back.\n const [dragClosing, setDragClosing] = useState(false);\n if (dragClosing && state.isOpen) {\n setDragClosing(false);\n setDrag(null);\n setSettleMotion(null);\n }\n const contentRef = useRef<HTMLDivElement>(null);\n const pointer = useRef({\n pending: false,\n fromHandle: false,\n /** Started on the content, where a drag only moves the sheet once it picks `drag`. */\n fromContent: false,\n gesture: 'undecided' as ContentGesture,\n x: 0,\n y: 0,\n offset: 0,\n id: 0,\n samples: [] as DragSample[],\n });\n\n // The lower detent: a modal sheet taller than half the window opens to half (Compose's\n // PartiallyExpanded); a standard sheet collapses to its peek height (BottomSheetScaffold).\n const lowerOffset = standard ? height - peekHeight : height - viewport.height / 2;\n const hasLower = standard ? height > peekHeight : !skipPartiallyExpanded && lowerOffset > 0;\n // Where the sheet rests: the lower detent only when it has one.\n const current = detent === 'expanded' || !hasLower ? 'expanded' : 'lower';\n const restingOffset = current === 'lower' ? lowerOffset : 0;\n const offset = drag ?? restingOffset;\n const maxOffset = standard ? Math.max(0, lowerOffset) : height;\n const detentName = current === 'expanded' ? 'expanded' : standard ? 'collapsed' : 'partial';\n\n // Moving down to a lower detent while staying open (Escape, the handle or the collapse\n // button on a full sheet, or the app lowering it) runs on the fast effects spring, which\n // doesn't overshoot, as Compose's `partialExpand()` (`hideMotionSpec`); on the spatial\n // spring the sheet dropped a few pixels below the detent. A drag's settle keeps the\n // spatial spring (Compose's `anchoredDraggableMotion`), and so do the entry and every\n // move up. It's state, rendered with the move, so the transition starts with it.\n const [lowering, setLowering] = useState(false);\n const [previousControlled, setPreviousControlled] = useState(controlledDetent);\n if (controlledDetent !== previousControlled) {\n setPreviousControlled(controlledDetent);\n setLowering(controlledDetent === 'lower');\n setSettleMotion(null);\n }\n\n /** Moves to `next`, telling `onDetentChange` when a standard sheet's detent changes. */\n const setDetent = (next: 'expanded' | 'lower', fromDrag = false) => {\n setLowering(next === 'lower' && !fromDrag);\n if (!fromDrag) setSettleMotion(null);\n if (controlledDetent === undefined) setOwnDetent(next);\n const resting = next === 'lower' && hasLower ? 'lower' : 'expanded';\n if (standard && resting !== current)\n onDetentChange?.(resting === 'expanded' ? 'expanded' : 'collapsed');\n };\n\n useLayoutEffect(() => {\n const element = motionRef.current;\n if (!element) return;\n const measure = () => {\n setHeight(element.offsetHeight);\n setViewport({ width: window.innerWidth, height: window.innerHeight });\n };\n measure();\n const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(measure);\n observer?.observe(element);\n window.addEventListener('resize', measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener('resize', measure);\n };\n }, []);\n\n const close = state.close;\n\n // A modal sheet's last anchor is closed, unless the user may not close it (`closedBy=\"none\"`).\n const closable = !standard && closedBy !== 'none';\n const anchors = [0, ...(hasLower ? [lowerOffset] : []), ...(closable ? [height] : [])];\n\n const settle = (target: number, velocity: number) => {\n const closing = !standard && target >= height - 0.5;\n // A closing sheet leaves to 112% of its height on the fast spatial spring (its exit).\n const end = closing ? height * EXIT_DISTANCE : target;\n setSettleMotion(releaseMotion(motionScheme, closing, velocity, end - (drag ?? restingOffset)));\n if (closing) {\n setDragClosing(true);\n close();\n } else {\n setDrag(null);\n setDetent(target === 0 ? 'expanded' : 'lower', true);\n }\n };\n\n /** The handle's action: expand a lower sheet, collapse a standard one, close a modal one. */\n const cycle = () => {\n if (current === 'lower') setDetent('expanded');\n else if (standard) setDetent('lower');\n else close();\n };\n\n const startPointer = (\n event: PointerEvent<HTMLElement>,\n fromHandle: boolean,\n fromContent = false,\n ) => {\n // A leaving sheet takes no new drag.\n if (event.button !== 0 || isExiting) return;\n setSettleMotion(null);\n pointer.current = {\n pending: true,\n fromHandle,\n fromContent,\n gesture: 'undecided',\n x: event.clientX,\n y: event.clientY,\n offset: restingOffset,\n id: event.pointerId,\n samples: [{ position: event.clientY, time: event.timeStamp }],\n };\n };\n const movePointer = (event: PointerEvent<HTMLElement>) => {\n const start = pointer.current;\n if (!start.pending) return;\n if (start.fromContent && start.gesture !== 'drag') {\n // Once a touch on the content picks scrolling or dragging, it keeps that choice.\n start.gesture = contentGesture(\n event.clientX - start.x,\n event.clientY - start.y,\n scrolledAbove(event.target as Element, contentRef.current!),\n current === 'lower',\n );\n if (start.gesture === 'undecided') return;\n if (start.gesture === 'scroll') {\n start.pending = false;\n return;\n }\n }\n // Without `swipeable` the handle is only a button: a move never drags it.\n if (!swipeable) return;\n start.samples = [...start.samples, { position: event.clientY, time: event.timeStamp }];\n if (drag === null && Math.abs(event.clientY - start.y) <= DRAG_SLOP) return;\n if (drag === null) event.currentTarget.setPointerCapture?.(start.id);\n const raw = start.offset + event.clientY - start.y;\n setDrag(Math.min(maxOffset, Math.max(0, raw)));\n // A drag up from the content that has raised the sheet fully scrolls the content with the\n // rest of the move, as Compose's nested scroll hands it over.\n if (start.fromContent && contentRef.current) contentRef.current.scrollTop = Math.max(0, -raw);\n };\n const endPointer = (event: PointerEvent<HTMLElement>) => {\n const start = pointer.current;\n if (!start.pending) return;\n start.pending = false;\n if (drag === null) {\n // A press on the handle cycles the sheet (Compose's handle click).\n if (start.fromHandle) cycle();\n return;\n }\n // From the last 100ms of moves, so a slow drag ending in a flick reads as a fling.\n const velocity = releaseVelocity([\n ...start.samples,\n { position: event.clientY, time: event.timeStamp },\n ]);\n settle(settleTarget(anchors, start.offset, drag, velocity), velocity);\n };\n const cancelPointer = () => {\n pointer.current.pending = false;\n setSettleMotion(null);\n setDrag(null);\n };\n\n // Without a handle the top 48px of the sheet still drags it (m3.material.io).\n const onZonePointerDown = (event: PointerEvent<HTMLDivElement>) => {\n if (!swipeable) return;\n const top = event.currentTarget.getBoundingClientRect().top;\n if (event.clientY - top <= DRAG_ZONE) startPointer(event, false);\n };\n\n // A touch (or pen) drag down on content scrolled to its top moves the sheet, as the\n // handle does. Mouse drags select text instead, as they always have on the web.\n const onContentPointerDown = (event: PointerEvent<HTMLDivElement>) => {\n const content = contentRef.current;\n const target = event.target as Element;\n pointer.current.fromContent = false;\n if (!swipeable || event.pointerType === 'mouse' || !content?.contains(target)) return;\n if (ownsGesture(target, content)) return;\n startPointer(event, false, true);\n };\n\n // While the content is at its top, the browser only scrolls it for upward and sideways\n // moves (`touch-action: pan-x pan-down`), so a downward one reaches the sheet as pointer\n // events. Browsers without `pan-down` (iOS Safari) are stopped from scrolling or\n // rubber-banding by cancelling the touch moves of a drag.\n useEffect(() => {\n const content = contentRef.current;\n if (!content) return;\n const update = () => content.toggleAttribute('data-at-top', content.scrollTop <= 0);\n const onTouchMove = (event: TouchEvent) => {\n const start = pointer.current;\n if (start.pending && start.fromContent && start.gesture === 'drag' && event.cancelable)\n event.preventDefault();\n };\n update();\n content.addEventListener('scroll', update, { passive: true });\n content.addEventListener('touchmove', onTouchMove, { passive: false });\n return () => {\n content.removeEventListener('scroll', update);\n content.removeEventListener('touchmove', onTouchMove);\n };\n }, []);\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || isExiting) return;\n if (standard) {\n if (current !== 'expanded' || !hasLower) return;\n event.stopPropagation();\n setDetent('lower');\n return;\n }\n // Escape is a close request: it closes a modal sheet in one press, from any detent (WAI-ARIA\n // APG modal dialog; HTML's close request), unless the user may not close it.\n if (closedBy === 'none') return;\n event.stopPropagation();\n close();\n };\n\n const handleRef = useRef<HTMLDivElement>(null);\n usePeekInert(\n panelRef,\n contentRef,\n detentName === 'collapsed',\n peekHeight,\n () => handleRef.current ?? panelRef.current,\n );\n\n // The collapse button leaves once the sheet stops filling the window, so focus moves to the\n // handle (or the panel) first instead of dropping to the page.\n const collapseFromButton = () => {\n (handleRef.current ?? panelRef.current)?.focus({ preventScroll: true });\n setDetent('lower');\n };\n\n const { focusProps: handleFocusProps, isFocusVisible: handleFocusVisible } = useFocusRing();\n const exiting = isExiting || undefined;\n const dragging = (drag !== null && !dragClosing) || undefined;\n const handleLabel =\n current === 'lower' ? labels.expand : standard ? labels.collapse : labels.dismiss;\n // The sheet fills the window up to its top margin: 72px, or 56px above 640px wide.\n const topMargin = viewport.width > 640 ? 56 : 72;\n const fillsWindow =\n current === 'expanded' && viewport.height > 0 && height >= viewport.height - topMargin - 0.5;\n const showCloseButton = closeButton && fillsWindow && (!standard || hasLower);\n\n // A standard sheet is a non-modal dialog: no scrim, no focus trap, the page stays usable.\n const roleProps: HTMLAttributes<HTMLElement> = standard\n ? { role: 'dialog', tabIndex: -1 }\n : (overlayPanelProps ?? {});\n const panelProps = mergeProps(rest, roleProps, { onKeyDown });\n const zone = !dragHandle;\n\n return (\n <div\n style={style}\n className={styles.container({ class: cn(classNames?.container, className) })}\n >\n <div\n {...mergeProps(exitProps, {\n onTransitionEnd: (event: TransitionEvent<HTMLDivElement>) => {\n if (event.target === event.currentTarget && event.propertyName === 'translate')\n setSettleMotion(null);\n },\n })}\n ref={motionRef}\n data-exiting={exiting}\n data-dragging={dragging}\n data-lowering={lowering || undefined}\n data-settling={settleMotion ? '' : undefined}\n style={\n {\n '--m3-sheet-offset': `${offset}px`,\n ...(settleMotion && {\n transitionDuration: `${settleMotion.duration}ms`,\n transitionTimingFunction: settleMotion.easing,\n }),\n } as CSSProperties\n }\n className={styles.motion()}\n >\n <div\n {...panelProps}\n // Content drags listen in the capture phase: pressable content (list items, buttons)\n // stops its pointer events from bubbling.\n onPointerDownCapture={(event) => {\n panelProps.onPointerDownCapture?.(event);\n onContentPointerDown(event);\n }}\n onPointerMoveCapture={(event) => {\n panelProps.onPointerMoveCapture?.(event);\n if (pointer.current.fromContent) movePointer(event);\n }}\n onPointerUpCapture={(event) => {\n panelProps.onPointerUpCapture?.(event);\n if (pointer.current.fromContent) endPointer(event);\n }}\n onPointerCancelCapture={(event) => {\n panelProps.onPointerCancelCapture?.(event);\n if (pointer.current.fromContent) cancelPointer();\n }}\n onPointerDown={(event) => {\n panelProps.onPointerDown?.(event);\n if (zone && !pointer.current.fromContent) onZonePointerDown(event);\n }}\n onPointerMove={(event) => {\n panelProps.onPointerMove?.(event);\n if (zone && !pointer.current.fromContent) movePointer(event);\n }}\n onPointerUp={(event) => {\n panelProps.onPointerUp?.(event);\n if (zone && !pointer.current.fromContent) endPointer(event);\n }}\n onPointerCancel={(event) => {\n panelProps.onPointerCancel?.(event);\n if (zone && !pointer.current.fromContent) cancelPointer();\n }}\n ref={panelRef}\n // The component tokens' hooks (sheet-tokens.ts); `data-variant` is also public.\n data-tokens={mergeDataTokens('md-comp-sheet-bottom-docked', rest)}\n data-variant={variant}\n data-detent={detentName}\n data-exiting={exiting}\n data-dragging={dragging}\n className={styles.panel({ class: classNames?.panel })}\n >\n <TriggerContext value={null}>\n {dragHandle && (\n <div\n {...handleFocusProps}\n ref={handleRef}\n role=\"button\"\n tabIndex={0}\n data-focus-visible={handleFocusVisible || undefined}\n aria-label={handleLabel}\n data-dragging={dragging}\n onPointerDown={(event) => startPointer(event, true)}\n onPointerMove={movePointer}\n onPointerUp={endPointer}\n onPointerCancel={cancelPointer}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n cycle();\n }\n }}\n className={styles.handle({ class: classNames?.handle })}\n >\n <span\n data-slot=\"drag-handle\"\n className={styles.handleBar({ class: classNames?.handleBar })}\n />\n </div>\n )}\n {showCloseButton && (\n <div className={styles.closeRow({ class: classNames?.closeRow })}>\n <IconButton\n variant=\"standard\"\n density={0}\n icon={\n standard ? (collapseIcon ?? <CollapseIcon />) : (closeIcon ?? <CloseIcon />)\n }\n aria-label={standard ? labels.collapse : labels.close}\n onPress={standard ? collapseFromButton : close}\n className={classNames?.close}\n />\n </div>\n )}\n <div\n ref={contentRef}\n data-can-rise={current === 'lower' || undefined}\n className={styles.content({ class: classNames?.content })}\n >\n {typeof children === 'function' ? children({ close }) : children}\n </div>\n </TriggerContext>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"BottomSheet.js","names":["Overlay"],"sources":["../../../src/components/sheet/BottomSheet.tsx"],"sourcesContent":["'use client';\n\nimport {\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type ComponentPropsWithRef,\n type CSSProperties,\n type HTMLAttributes,\n type KeyboardEvent,\n type PointerEvent,\n type ReactElement,\n type ReactNode,\n type Ref,\n type TransitionEvent,\n} from 'react';\nimport {\n mergeProps,\n useDialog,\n useFocusRing,\n useModalOverlay,\n useObjectRef,\n type AriaDialogProps,\n} from 'react-aria';\nimport { useOverlayTriggerState, type OverlayTriggerState } from 'react-stately';\nimport { Overlay } from '../../primitives/Overlay';\nimport { TriggerContext } from '../../primitives/TriggerContext';\nimport { usePresence } from '../../primitives/use-presence';\nimport { closedByOverlayOptions, type ClosedBy } from '../../utils/closed-by';\nimport { cn } from '../../utils/cn';\nimport { focusFirstInteractive } from '../../utils/focus';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\nimport { useThemeScope } from '../../theme/context';\nimport { IconButton } from '../icon-button/IconButton';\nimport { CloseIcon, CollapseIcon } from './sheet-icons';\nimport { contentGesture, scrolledAbove, type ContentGesture } from './bottom-sheet-gesture';\nimport { DRAG_SLOP, ownsGesture } from './sheet-gesture';\nimport {\n EXIT_DISTANCE,\n releaseMotion,\n releaseVelocity,\n settleTarget,\n type DragSample,\n type ReleaseMotion,\n} from './sheet-settle';\nimport { usePeekInert } from './use-peek-inert';\nimport { SHEET_EXIT_PROPERTIES, bottomSheetStyles } from './sheet-styles';\nimport { useKeyboardInset } from './use-keyboard-inset';\nimport { SheetTriggerContext } from './SheetTrigger';\n\nexport interface BottomSheetClassNames {\n /**\n * The outermost element, which places the sheet (where `className` and `style` go too):\n * `fixed` to the window's bottom by default, or, for example, `absolute` to a region.\n */\n container?: string;\n /** The dimmed layer behind a modal sheet. */\n scrim?: string;\n /** The sheet's surface: its colour, corners and shadow. */\n panel?: string;\n /** The drag handle's 48px pressable area. */\n handle?: string;\n /** The drag handle's visible bar, inside `handle`. */\n handleBar?: string;\n /** The row holding the close (modal) or collapse (standard) button. */\n closeRow?: string;\n /** The close or collapse icon button. */\n close?: string;\n /** The scrollable content area. */\n content?: string;\n}\n\n/** Words the bottom sheet announces. */\nexport interface BottomSheetLabels {\n /**\n * The drag handle while the sheet is half open or collapsed (it expands the sheet).\n * @default \"Expand sheet\"\n */\n expand: string;\n /** The drag handle and collapse button of a fully open standard sheet. @default \"Collapse sheet\" */\n collapse: string;\n /** The drag handle of a fully open modal sheet (it closes the sheet). @default \"Close sheet\" */\n dismiss: string;\n /** The close button of a modal sheet that fills the window height. @default \"Close\" */\n close: string;\n}\n\nconst DEFAULT_LABELS: BottomSheetLabels = {\n expand: 'Expand sheet',\n collapse: 'Collapse sheet',\n dismiss: 'Close sheet',\n close: 'Close',\n};\n\n/**\n * Where a standard bottom sheet rests: `collapsed` to its `peekHeight`, or `expanded` to its\n * full height (Compose `SheetValue.PartiallyExpanded` and `SheetValue.Expanded`). A sheet whose\n * content is no taller than the peek height is always expanded.\n */\nexport type BottomSheetDetent = 'collapsed' | 'expanded';\n\n/** The interactive strip at the top of a sheet without a drag handle (m3.material.io). */\nconst DRAG_ZONE = 48;\n\ntype Naming =\n | {\n /** Accessible name when there's no visible title. Required without `aria-labelledby`. */\n 'aria-label': string;\n }\n | {\n /** Id of the visible title that names the sheet. Required without `aria-label`. */\n 'aria-labelledby': string;\n };\n\ninterface BottomSheetOwnProps {\n /**\n * `modal` opens over a scrim as a dialog that blocks the page; `standard` sits over the\n * page without a scrim, so both can be used at once, and rests collapsed at `peekHeight`\n * or expanded (`detent`). @default \"modal\"\n */\n variant?: 'modal' | 'standard';\n /** Whether the sheet is open (controlled). */\n open?: boolean;\n /** Whether the sheet starts open when uncontrolled. @default false, or true when standard */\n defaultOpen?: boolean;\n /** Called when the sheet opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /** Show the drag handle. Without it, the top 48px of the sheet still drags. @default true */\n dragHandle?: boolean;\n /**\n * Whether dragging the handle, the top of the sheet or (touch and pen) its content moves\n * the sheet. Without it the handle stays a button (a press, Enter or Space), and Escape,\n * the scrim and the close button work as before.\n * @default true\n */\n swipeable?: boolean;\n /**\n * Open fully instead of to half the window when the content is taller than half the\n * window (modal). @default false\n */\n skipPartiallyExpanded?: boolean;\n /**\n * Height of a collapsed standard sheet, px. While collapsed, the content's top-level parts\n * that don't fit entirely in it are `inert`. @default 56\n */\n peekHeight?: number;\n /**\n * Where a standard sheet rests (controlled): `collapsed` at `peekHeight` or `expanded` to\n * its full height, so the app can raise or lower it (\"can be set by the system or toggled\n * by the user\", m3.material.io). Its handle, a drag, Escape and its collapse button then\n * only call `onDetentChange`. A modal sheet ignores it.\n */\n detent?: BottomSheetDetent;\n /**\n * Where a standard sheet first rests when uncontrolled; `expanded` opens it fully raised\n * (Compose `rememberStandardBottomSheetState(initialValue = SheetValue.Expanded)`). It\n * applies each time the sheet opens. A modal sheet ignores it.\n * @default \"collapsed\"\n */\n defaultDetent?: BottomSheetDetent;\n /**\n * Called when a standard sheet moves to another detent: by its handle (a press, Enter or\n * Space, or a drag), Escape or its collapse button. Not called for a modal sheet.\n */\n onDetentChange?: (detent: BottomSheetDetent) => void;\n /**\n * How the user can close a modal sheet, as HTML's `<dialog closedby>`: `any` (Escape, a\n * press on the scrim or a drag down), `closerequest` (Escape or a drag down) or `none`\n * (only its buttons or the app). @default \"any\"\n */\n closedBy?: ClosedBy;\n /**\n * While the sheet fills the window height, show a close button (modal) or a collapse\n * button (standard) at its top end. @default true\n */\n closeButton?: boolean;\n /** Replaces the close button's icon. */\n closeIcon?: ReactElement;\n /** Replaces the collapse button's icon. */\n collapseIcon?: ReactElement;\n /** Words the sheet announces, with English defaults. */\n labels?: Partial<BottomSheetLabels>;\n children?: ReactNode | ((props: { close: () => void }) => ReactNode);\n /** Classes for each part, by slot. */\n classNames?: BottomSheetClassNames;\n /**\n * Goes with `style` on the outermost element, the container that places the sheet, so a\n * layout class (`absolute`, `fixed`, a transform) moves the whole sheet and never clips or\n * stretches its surface. Style the surface with `classNames.panel`; `ref`, `data-*` and\n * other attributes go on the panel.\n */\n className?: string;\n /** Goes on the outermost element, with `className`. */\n style?: CSSProperties;\n /** Ref to the panel, the sheet's surface (`role=\"dialog\"`). */\n ref?: Ref<HTMLDivElement>;\n}\n\nexport type BottomSheetProps = BottomSheetOwnProps &\n Naming &\n Omit<ComponentPropsWithRef<'div'>, keyof BottomSheetOwnProps | 'aria-label' | 'aria-labelledby'>;\n\n/**\n * M3 bottom sheet. A `modal` sheet slides up over a scrim, opening to half the window when\n * its content is taller (drag the handle up, or press it, to expand), and closes on a drag\n * down, a press on the scrim or Escape, in one press from any height. A\n * `standard` sheet sits over the page without a scrim, collapsed to `peekHeight` until it\n * is expanded, or opening expanded with `defaultDetent=\"expanded\"`; `detent` and\n * `onDetentChange` control it.\n *\n * @example\n * <SheetTrigger>\n * <Button>Share</Button>\n * <BottomSheet aria-label=\"Share\">{({ close }) => <ShareOptions onPick={close} />}</BottomSheet>\n * </SheetTrigger>\n */\nexport function BottomSheet({\n variant = 'modal',\n open,\n defaultOpen,\n onOpenChange,\n ...props\n}: BottomSheetProps) {\n const trigger = useContext(SheetTriggerContext);\n const ownState = useOverlayTriggerState({\n isOpen: open,\n defaultOpen: defaultOpen ?? (variant === 'standard' ? true : undefined),\n onOpenChange,\n });\n const state = (variant === 'modal' ? trigger?.state : undefined) ?? ownState;\n const { isPresent, isExiting, exitProps } = usePresence(state.isOpen, SHEET_EXIT_PROPERTIES);\n if (!isPresent) return null;\n if (variant === 'standard')\n return (\n <BottomSheetPanel\n {...props}\n variant=\"standard\"\n state={state}\n isExiting={isExiting}\n exitProps={exitProps}\n />\n );\n return (\n <Overlay exiting={isExiting}>\n <ModalBottomSheet\n {...props}\n state={state}\n isExiting={isExiting}\n exitProps={exitProps}\n triggerOverlayProps={trigger?.overlayProps}\n />\n </Overlay>\n );\n}\n\nfunction ModalBottomSheet({\n state,\n isExiting,\n triggerOverlayProps,\n closedBy = 'any',\n classNames,\n ref,\n ...props\n}: Omit<PanelProps, 'variant' | 'panelProps'> & { triggerOverlayProps?: { id?: string } }) {\n const panelRef = useObjectRef(ref as Ref<HTMLDivElement>);\n const { modalProps, underlayProps } = useModalOverlay(\n {\n ...closedByOverlayOptions(closedBy, isExiting),\n // The panel handles Escape itself (below), so a nested control's Escape stays its own.\n isKeyboardDismissDisabled: true,\n },\n state,\n panelRef,\n );\n const { dialogProps } = useDialog(props as AriaDialogProps, panelRef);\n // Initial focus goes to the first interactive element (the drag handle, when there is one;\n // M3), as in a Dialog, not to the panel.\n useEffect(() => focusFirstInteractive(panelRef.current), [panelRef]);\n const styles = bottomSheetStyles();\n return (\n <>\n <div\n aria-hidden=\"true\"\n data-exiting={isExiting || undefined}\n className={styles.scrim({ class: classNames?.scrim })}\n />\n <div {...underlayProps} className=\"fixed inset-0 z-(--md-sys-z-sheet)\">\n <BottomSheetPanel\n {...props}\n variant=\"modal\"\n closedBy={closedBy}\n state={state}\n isExiting={isExiting}\n classNames={classNames}\n ref={panelRef}\n panelProps={mergeProps(triggerOverlayProps ?? {}, modalProps, dialogProps)}\n />\n </div>\n </>\n );\n}\n\ntype PanelProps = Omit<BottomSheetProps, 'open' | 'defaultOpen' | 'onOpenChange' | 'variant'> & {\n variant: 'modal' | 'standard';\n state: OverlayTriggerState;\n isExiting: boolean;\n exitProps: ReturnType<typeof usePresence>['exitProps'];\n /** Props from the modal overlay and dialog hooks. */\n panelProps?: HTMLAttributes<HTMLElement>;\n};\n\n/** The sheet itself: the moving wrapper, the panel, its handle and its detents. */\nfunction BottomSheetPanel({\n variant,\n state,\n isExiting,\n exitProps,\n panelProps: overlayPanelProps,\n dragHandle = true,\n swipeable = true,\n skipPartiallyExpanded = false,\n peekHeight = 56,\n detent: detentProp,\n defaultDetent = 'collapsed',\n onDetentChange,\n closedBy = 'any',\n closeButton = true,\n closeIcon,\n collapseIcon,\n labels: labelsProp,\n children,\n className,\n classNames,\n style,\n ref,\n ...rest\n}: PanelProps) {\n const labels = { ...DEFAULT_LABELS, ...labelsProp };\n const standard = variant === 'standard';\n const panelRef = useObjectRef(ref as Ref<HTMLDivElement>);\n const motionRef = useRef<HTMLDivElement>(null);\n const styles = bottomSheetStyles({ variant, swipeable });\n const [height, setHeight] = useState(0);\n const [viewport, setViewport] = useState({ width: 0, height: 0 });\n // The lower detent is half the window (modal) or the peek height (standard); `null` rests\n // on the lower detent when there is one.\n const [ownDetent, setOwnDetent] = useState<'expanded' | 'lower' | null>(\n standard && defaultDetent === 'expanded' ? 'expanded' : null,\n );\n const controlledDetent =\n standard && detentProp !== undefined\n ? detentProp === 'expanded'\n ? 'expanded'\n : 'lower'\n : undefined;\n const detent = controlledDetent ?? ownDetent;\n const [drag, setDrag] = useState<number | null>(null);\n // A drag's settle continues at the speed the pointer let go with, as Compose's\n // `animateTo(target, velocity)` does: the spring starts with that velocity, as a `linear()`\n // easing and duration set on the moving layer until its move ends (2026-10-17; it started\n // from rest). Under reduced motion settling stays instant.\n const { motion: motionScheme } = useThemeScope();\n const [settleMotion, setSettleMotion] = useState<ReleaseMotion | null>(null);\n // A drag let go to close: the offset stays where the pointer left it, so the exit leaves\n // from there (and, under reduced motion, fades there, as the side sheet does). If the\n // sheet is still open after that render, the app refused and the sheet springs back.\n const [dragClosing, setDragClosing] = useState(false);\n if (dragClosing && state.isOpen) {\n setDragClosing(false);\n setDrag(null);\n setSettleMotion(null);\n }\n const contentRef = useRef<HTMLDivElement>(null);\n const pointer = useRef({\n pending: false,\n fromHandle: false,\n /** Started on the content, where a drag only moves the sheet once it picks `drag`. */\n fromContent: false,\n gesture: 'undecided' as ContentGesture,\n x: 0,\n y: 0,\n offset: 0,\n id: 0,\n samples: [] as DragSample[],\n });\n\n // The lower detent: a modal sheet taller than half the window opens to half (Compose's\n // PartiallyExpanded); a standard sheet collapses to its peek height (BottomSheetScaffold).\n const lowerOffset = standard ? height - peekHeight : height - viewport.height / 2;\n const hasLower = standard ? height > peekHeight : !skipPartiallyExpanded && lowerOffset > 0;\n // Where the sheet rests: the lower detent only when it has one.\n const current = detent === 'expanded' || !hasLower ? 'expanded' : 'lower';\n const restingOffset = current === 'lower' ? lowerOffset : 0;\n const offset = drag ?? restingOffset;\n const maxOffset = standard ? Math.max(0, lowerOffset) : height;\n const detentName = current === 'expanded' ? 'expanded' : standard ? 'collapsed' : 'partial';\n\n // Moving down to a lower detent while staying open (Escape, the handle or the collapse\n // button on a full sheet, or the app lowering it) runs on the fast effects spring, which\n // doesn't overshoot, as Compose's `partialExpand()` (`hideMotionSpec`); on the spatial\n // spring the sheet dropped a few pixels below the detent. A drag's settle keeps the\n // spatial spring (Compose's `anchoredDraggableMotion`), and so do the entry and every\n // move up. It's state, rendered with the move, so the transition starts with it.\n const [lowering, setLowering] = useState(false);\n const [previousControlled, setPreviousControlled] = useState(controlledDetent);\n if (controlledDetent !== previousControlled) {\n setPreviousControlled(controlledDetent);\n setLowering(controlledDetent === 'lower');\n setSettleMotion(null);\n }\n\n /** Moves to `next`, telling `onDetentChange` when a standard sheet's detent changes. */\n const setDetent = (next: 'expanded' | 'lower', fromDrag = false) => {\n setLowering(next === 'lower' && !fromDrag);\n if (!fromDrag) setSettleMotion(null);\n if (controlledDetent === undefined) setOwnDetent(next);\n const resting = next === 'lower' && hasLower ? 'lower' : 'expanded';\n if (standard && resting !== current)\n onDetentChange?.(resting === 'expanded' ? 'expanded' : 'collapsed');\n };\n\n useLayoutEffect(() => {\n const element = motionRef.current;\n if (!element) return;\n const measure = () => {\n setHeight(element.offsetHeight);\n setViewport({ width: window.innerWidth, height: window.innerHeight });\n };\n measure();\n const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(measure);\n observer?.observe(element);\n window.addEventListener('resize', measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener('resize', measure);\n };\n }, []);\n\n const close = state.close;\n\n // A modal sheet's last anchor is closed, unless the user may not close it (`closedBy=\"none\"`).\n const closable = !standard && closedBy !== 'none';\n const anchors = [0, ...(hasLower ? [lowerOffset] : []), ...(closable ? [height] : [])];\n\n const settle = (target: number, velocity: number) => {\n const closing = !standard && target >= height - 0.5;\n // A closing sheet leaves to 112% of its height on the fast spatial spring (its exit).\n const end = closing ? height * EXIT_DISTANCE : target;\n setSettleMotion(releaseMotion(motionScheme, closing, velocity, end - (drag ?? restingOffset)));\n if (closing) {\n setDragClosing(true);\n close();\n } else {\n setDrag(null);\n setDetent(target === 0 ? 'expanded' : 'lower', true);\n }\n };\n\n /** The handle's action: expand a lower sheet, collapse a standard one, close a modal one. */\n const cycle = () => {\n if (current === 'lower') setDetent('expanded');\n else if (standard) setDetent('lower');\n else close();\n };\n\n const startPointer = (\n event: PointerEvent<HTMLElement>,\n fromHandle: boolean,\n fromContent = false,\n ) => {\n // A leaving sheet takes no new drag.\n if (event.button !== 0 || isExiting) return;\n setSettleMotion(null);\n pointer.current = {\n pending: true,\n fromHandle,\n fromContent,\n gesture: 'undecided',\n x: event.clientX,\n y: event.clientY,\n offset: restingOffset,\n id: event.pointerId,\n samples: [{ position: event.clientY, time: event.timeStamp }],\n };\n };\n const movePointer = (event: PointerEvent<HTMLElement>) => {\n const start = pointer.current;\n if (!start.pending) return;\n if (start.fromContent && start.gesture !== 'drag') {\n // Once a touch on the content picks scrolling or dragging, it keeps that choice.\n start.gesture = contentGesture(\n event.clientX - start.x,\n event.clientY - start.y,\n scrolledAbove(event.target as Element, contentRef.current!),\n current === 'lower',\n );\n if (start.gesture === 'undecided') return;\n if (start.gesture === 'scroll') {\n start.pending = false;\n return;\n }\n }\n // Without `swipeable` the handle is only a button: a move never drags it.\n if (!swipeable) return;\n start.samples = [...start.samples, { position: event.clientY, time: event.timeStamp }];\n if (drag === null && Math.abs(event.clientY - start.y) <= DRAG_SLOP) return;\n if (drag === null) event.currentTarget.setPointerCapture?.(start.id);\n const raw = start.offset + event.clientY - start.y;\n setDrag(Math.min(maxOffset, Math.max(0, raw)));\n // A drag up from the content that has raised the sheet fully scrolls the content with the\n // rest of the move, as Compose's nested scroll hands it over.\n if (start.fromContent && contentRef.current) contentRef.current.scrollTop = Math.max(0, -raw);\n };\n const endPointer = (event: PointerEvent<HTMLElement>) => {\n const start = pointer.current;\n if (!start.pending) return;\n start.pending = false;\n if (drag === null) {\n // A press on the handle cycles the sheet (Compose's handle click).\n if (start.fromHandle) cycle();\n return;\n }\n // From the last 100ms of moves, so a slow drag ending in a flick reads as a fling.\n const velocity = releaseVelocity([\n ...start.samples,\n { position: event.clientY, time: event.timeStamp },\n ]);\n settle(settleTarget(anchors, start.offset, drag, velocity), velocity);\n };\n const cancelPointer = () => {\n pointer.current.pending = false;\n setSettleMotion(null);\n setDrag(null);\n };\n\n // Without a handle the top 48px of the sheet still drags it (m3.material.io).\n const onZonePointerDown = (event: PointerEvent<HTMLDivElement>) => {\n if (!swipeable) return;\n const top = event.currentTarget.getBoundingClientRect().top;\n if (event.clientY - top <= DRAG_ZONE) startPointer(event, false);\n };\n\n // A touch (or pen) drag down on content scrolled to its top moves the sheet, as the\n // handle does. Mouse drags select text instead, as they always have on the web.\n const onContentPointerDown = (event: PointerEvent<HTMLDivElement>) => {\n const content = contentRef.current;\n const target = event.target as Element;\n pointer.current.fromContent = false;\n if (!swipeable || event.pointerType === 'mouse' || !content?.contains(target)) return;\n if (ownsGesture(target, content)) return;\n startPointer(event, false, true);\n };\n\n // While the content is at its top, the browser only scrolls it for upward and sideways\n // moves (`touch-action: pan-x pan-down`), so a downward one reaches the sheet as pointer\n // events. Browsers without `pan-down` (iOS Safari) are stopped from scrolling or\n // rubber-banding by cancelling the touch moves of a drag.\n useEffect(() => {\n const content = contentRef.current;\n if (!content) return;\n const update = () => content.toggleAttribute('data-at-top', content.scrollTop <= 0);\n const onTouchMove = (event: TouchEvent) => {\n const start = pointer.current;\n if (start.pending && start.fromContent && start.gesture === 'drag' && event.cancelable)\n event.preventDefault();\n };\n update();\n content.addEventListener('scroll', update, { passive: true });\n content.addEventListener('touchmove', onTouchMove, { passive: false });\n return () => {\n content.removeEventListener('scroll', update);\n content.removeEventListener('touchmove', onTouchMove);\n };\n }, []);\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || isExiting) return;\n if (standard) {\n if (current !== 'expanded' || !hasLower) return;\n event.stopPropagation();\n setDetent('lower');\n return;\n }\n // Escape is a close request: it closes a modal sheet in one press, from any detent (WAI-ARIA\n // APG modal dialog; HTML's close request), unless the user may not close it.\n if (closedBy === 'none') return;\n event.stopPropagation();\n close();\n };\n\n const handleRef = useRef<HTMLDivElement>(null);\n usePeekInert(\n panelRef,\n contentRef,\n detentName === 'collapsed',\n peekHeight,\n () => handleRef.current ?? panelRef.current,\n );\n\n // The collapse button leaves once the sheet stops filling the window, so focus moves to the\n // handle (or the panel) first instead of dropping to the page.\n const collapseFromButton = () => {\n (handleRef.current ?? panelRef.current)?.focus({ preventScroll: true });\n setDetent('lower');\n };\n\n const { focusProps: handleFocusProps, isFocusVisible: handleFocusVisible } = useFocusRing();\n const exiting = isExiting || undefined;\n const dragging = (drag !== null && !dragClosing) || undefined;\n const handleLabel =\n current === 'lower' ? labels.expand : standard ? labels.collapse : labels.dismiss;\n // The sheet fills the window up to its top margin: 72px, or 56px above 640px wide.\n const topMargin = viewport.width > 640 ? 56 : 72;\n const fillsWindow =\n current === 'expanded' && viewport.height > 0 && height >= viewport.height - topMargin - 0.5;\n const showCloseButton = closeButton && fillsWindow && (!standard || hasLower);\n\n // A standard sheet is a non-modal dialog: no scrim, no focus trap, the page stays usable.\n const roleProps: HTMLAttributes<HTMLElement> = standard\n ? { role: 'dialog', tabIndex: -1 }\n : (overlayPanelProps ?? {});\n const panelProps = mergeProps(rest, roleProps, { onKeyDown });\n const zone = !dragHandle;\n const keyboardInset = useKeyboardInset();\n\n return (\n <div\n style={{ '--m3-sheet-keyboard-inset': `${keyboardInset}px`, ...style } as CSSProperties}\n className={styles.container({ class: cn(classNames?.container, className) })}\n >\n <div\n {...mergeProps(exitProps, {\n onTransitionEnd: (event: TransitionEvent<HTMLDivElement>) => {\n if (event.target === event.currentTarget && event.propertyName === 'translate')\n setSettleMotion(null);\n },\n })}\n ref={motionRef}\n data-exiting={exiting}\n data-dragging={dragging}\n data-lowering={lowering || undefined}\n data-settling={settleMotion ? '' : undefined}\n style={\n {\n '--m3-sheet-offset': `${offset}px`,\n ...(settleMotion && {\n transitionDuration: `${settleMotion.duration}ms`,\n transitionTimingFunction: settleMotion.easing,\n }),\n } as CSSProperties\n }\n className={styles.motion()}\n >\n <div\n {...panelProps}\n // Content drags listen in the capture phase: pressable content (list items, buttons)\n // stops its pointer events from bubbling.\n onPointerDownCapture={(event) => {\n panelProps.onPointerDownCapture?.(event);\n onContentPointerDown(event);\n }}\n onPointerMoveCapture={(event) => {\n panelProps.onPointerMoveCapture?.(event);\n if (pointer.current.fromContent) movePointer(event);\n }}\n onPointerUpCapture={(event) => {\n panelProps.onPointerUpCapture?.(event);\n if (pointer.current.fromContent) endPointer(event);\n }}\n onPointerCancelCapture={(event) => {\n panelProps.onPointerCancelCapture?.(event);\n if (pointer.current.fromContent) cancelPointer();\n }}\n onPointerDown={(event) => {\n panelProps.onPointerDown?.(event);\n if (zone && !pointer.current.fromContent) onZonePointerDown(event);\n }}\n onPointerMove={(event) => {\n panelProps.onPointerMove?.(event);\n if (zone && !pointer.current.fromContent) movePointer(event);\n }}\n onPointerUp={(event) => {\n panelProps.onPointerUp?.(event);\n if (zone && !pointer.current.fromContent) endPointer(event);\n }}\n onPointerCancel={(event) => {\n panelProps.onPointerCancel?.(event);\n if (zone && !pointer.current.fromContent) cancelPointer();\n }}\n ref={panelRef}\n // The component tokens' hooks (sheet-tokens.ts); `data-variant` is also public.\n data-tokens={mergeDataTokens('md-comp-sheet-bottom-docked', rest)}\n data-variant={variant}\n data-detent={detentName}\n data-exiting={exiting}\n data-dragging={dragging}\n className={styles.panel({ class: classNames?.panel })}\n >\n <TriggerContext value={null}>\n {dragHandle && (\n <div\n {...handleFocusProps}\n ref={handleRef}\n role=\"button\"\n tabIndex={0}\n data-focus-visible={handleFocusVisible || undefined}\n aria-label={handleLabel}\n data-dragging={dragging}\n onPointerDown={(event) => startPointer(event, true)}\n onPointerMove={movePointer}\n onPointerUp={endPointer}\n onPointerCancel={cancelPointer}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n cycle();\n }\n }}\n className={styles.handle({ class: classNames?.handle })}\n >\n <span\n data-slot=\"drag-handle\"\n className={styles.handleBar({ class: classNames?.handleBar })}\n />\n </div>\n )}\n {showCloseButton && (\n <div className={styles.closeRow({ class: classNames?.closeRow })}>\n <IconButton\n variant=\"standard\"\n density={0}\n icon={\n standard ? (collapseIcon ?? <CollapseIcon />) : (closeIcon ?? <CloseIcon />)\n }\n aria-label={standard ? labels.collapse : labels.close}\n onPress={standard ? collapseFromButton : close}\n className={classNames?.close}\n />\n </div>\n )}\n <div\n ref={contentRef}\n data-can-rise={current === 'lower' || undefined}\n className={styles.content({ class: classNames?.content })}\n >\n {typeof children === 'function' ? children({ close }) : children}\n </div>\n </TriggerContext>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -35,8 +35,8 @@ const bottomSheetStyles = tv({
35
35
  slots: {
36
36
  scrim,
37
37
  container: [
38
- "fixed inset-x-0 bottom-0 z-(--md-sys-z-sheet) mx-auto flex w-full max-w-[640px] items-end",
39
- "max-h-[calc(100dvh-72px)] min-[641px]:max-h-[calc(100dvh-56px)]",
38
+ "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",
39
+ "max-h-[calc(100dvh_-_72px_-_var(--m3-sheet-keyboard-inset,0px))] min-[641px]:max-h-[calc(100dvh_-_56px_-_var(--m3-sheet-keyboard-inset,0px))]",
40
40
  "min-[641px]:max-w-[min(640px,calc(100%-112px))]",
41
41
  "[clip-path:inset(-100vh_-100vw_0_-100vw)]"
42
42
  ],
@@ -46,10 +46,10 @@ const bottomSheetStyles = tv({
46
46
  ...sheetMotion
47
47
  ],
48
48
  panel: [
49
- "relative flex max-h-[calc(100dvh-72px)] w-full flex-col",
49
+ "relative flex max-h-[calc(100dvh_-_72px_-_var(--m3-sheet-keyboard-inset,0px))] w-full flex-col",
50
50
  "after:pointer-events-none after:absolute after:inset-x-0 after:top-full after:h-[32px] after:[background-color:inherit] after:content-['']",
51
51
  "rounded-t-[var(--_md-comp-sheet-bottom-docked-container-shape)]",
52
- "min-[641px]:max-h-[calc(100dvh-56px)]",
52
+ "min-[641px]:max-h-[calc(100dvh_-_56px_-_var(--m3-sheet-keyboard-inset,0px))]",
53
53
  "bg-[var(--_md-comp-sheet-bottom-docked-container-color)] text-on-surface outline-none",
54
54
  "shadow-[shadow:var(--_md-comp-sheet-bottom-docked-container-elevation)]",
55
55
  "forced-colors:border-x forced-colors:border-t forced-colors:border-solid forced-colors:border-[CanvasText]"
@@ -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"}
@@ -2,6 +2,6 @@ import { ComponentTokenSet } from "../../tokens/component.js";
2
2
  import "../../index.js";
3
3
  //#region src/components/snackbar/snackbar-tokens.d.ts
4
4
  /** The tokens of {@link Snackbar}: `--md-comp-snackbar-<part>`. */
5
- export declare const snackbarTokenSet: ComponentTokenSet<"container-color" | "container-elevation" | "icon-color" | "container-shape" | "supporting-text-color" | "action-label-text-color">;
5
+ export declare const snackbarTokenSet: ComponentTokenSet<"icon-color" | "container-color" | "container-elevation" | "container-shape" | "supporting-text-color" | "action-label-text-color">;
6
6
  //#endregion
7
7
  //# sourceMappingURL=snackbar-tokens.d.ts.map
@@ -15,6 +15,14 @@ const DEFAULT_LABELS = {
15
15
  error: "Error",
16
16
  characterCount: (count, max) => `Character count: ${count} of ${max}`
17
17
  };
18
+ /** Input types the browser draws its own format in when empty, which a resting label would cover. */
19
+ const FORMAT_SHOWING_TYPES = /* @__PURE__ */ new Set([
20
+ "date",
21
+ "time",
22
+ "datetime-local",
23
+ "month",
24
+ "week"
25
+ ]);
18
26
  const INTERACTIVE = "input, textarea, button, a, [role=\"button\"]";
19
27
  /**
20
28
  * M3 text field: filled or outlined, with a floating label, leading / trailing icons,
@@ -71,7 +79,7 @@ function TextField(props) {
71
79
  domInputRef
72
80
  ]);
73
81
  const hasLabel = label !== void 0 && label !== null;
74
- const floated = hasLabel && (isFocused || value.length > 0);
82
+ const floated = hasLabel && (isFocused || value.length > 0 || FORMAT_SHOWING_TYPES.has(ariaProps.type ?? ""));
75
83
  const state = disabled ? "disabled" : isInvalid ? isFocused ? "error-focus" : isHovered ? "error-hover" : "error" : isFocused ? "focus" : isHovered ? "hover" : "rest";
76
84
  const resolvedError = typeof errorMessage === "function" ? errorMessage({
77
85
  isInvalid,
@@ -1 +1 @@
1
- {"version":3,"file":"TextField.js","names":[],"sources":["../../../src/components/text-field/TextField.tsx"],"sourcesContent":["'use client';\n\nimport {\n useId,\n useLayoutEffect,\n type AriaAttributes,\n type CSSProperties,\n type MouseEvent,\n type PointerEvent,\n type ReactNode,\n type Ref,\n} from 'react';\nimport {\n mergeProps,\n useFocusRing,\n useHover,\n useObjectRef,\n useTextField,\n type AriaTextFieldOptions,\n type AriaTextFieldProps,\n} from 'react-aria';\nimport { useControlledState } from 'react-stately/useControlledState';\nimport {\n CharacterCounter,\n ErrorText,\n FieldErrorIcon,\n FieldLabel,\n SupportingText,\n} from '../../primitives/Field';\nimport { densityAttribute, type Density } from '../../tokens/density';\nimport { cn } from '../../utils/cn';\nimport { renderedDescription } from '../../utils/rendered-description';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\nimport { pickWidgetAria, splitRootProps, type WrapperRootDOMProps } from '../../utils/root-props';\nimport { textFieldStyles, type TextFieldVariant } from './text-field-styles';\n\nexport interface TextFieldClassNames {\n /** The root element, merged with `className`. */\n root?: string;\n /** The filled or outlined container around the label, input and icons. */\n container?: string;\n /** The floating label. */\n label?: string;\n /** The `<input>` or `<textarea>`. */\n input?: string;\n /** The leading icon's 48px box. */\n leadingIcon?: string;\n /** The trailing icon's 48px box. */\n trailingIcon?: string;\n /** The prefix text before the input. */\n prefix?: string;\n /** The suffix text after the input. */\n suffix?: string;\n /** The helper text below the field. */\n supportingText?: string;\n /** The error message below the field. */\n errorText?: string;\n /** The character counter below the field. */\n counter?: string;\n /** The built-in error icon. */\n errorIcon?: string;\n /** The outlined field's border (a `<fieldset>`, notched for the floated label). */\n outline?: string;\n /** The filled field's active indicator, the line along the container's bottom. */\n activeIndicator?: string;\n}\n\n/** Words the text field announces; override them for other languages. */\nexport interface TextFieldLabels {\n /** The error icon's name (M3: \"Error\"). @default \"Error\" */\n error: string;\n /**\n * What screen readers hear for the character counter (M3 calls it the \"character count\").\n * @default (count, max) => `Character count: ${count} of ${max}`\n */\n characterCount: (count: number, max: number) => string;\n}\n\nconst DEFAULT_LABELS: TextFieldLabels = {\n error: 'Error',\n characterCount: (count, max) => `Character count: ${count} of ${max}`,\n};\n\ntype FieldState = 'disabled' | 'error-focus' | 'error-hover' | 'error' | 'focus' | 'hover' | 'rest';\n\n/** Props React Aria names with an `is` prefix; we use the HTML-style names instead. */\ntype RenamedAriaProps =\n | 'isDisabled'\n | 'isReadOnly'\n | 'isRequired'\n | 'isInvalid'\n | 'label'\n | 'description'\n | 'inputElementType'\n | 'value'\n | 'defaultValue'\n | 'onChange'\n // Left out: the deprecated `validationState` (`invalid` replaces it) and React Aria's\n // `excludeFromTabOrder` (`tabIndex={-1}` covers the rare case).\n | 'validationState'\n | 'excludeFromTabOrder';\n\ninterface TextFieldOwnProps\n extends\n Omit<AriaTextFieldProps<HTMLInputElement>, RenamedAriaProps>,\n Omit<WrapperRootDOMProps, keyof AriaTextFieldProps<HTMLInputElement>>,\n // The attributes that describe the input go to it, with React Aria's `aria-controls`,\n // `aria-haspopup`, `aria-autocomplete` and `aria-activedescendant`: a field that opens its\n // own suggestions can say so.\n Pick<\n AriaAttributes,\n | 'aria-expanded'\n | 'aria-required'\n | 'aria-invalid'\n | 'aria-readonly'\n | 'aria-multiline'\n | 'aria-placeholder'\n > {\n /** The `<input>`'s (or `<textarea>`'s) id, for a `<label for>` or an error summary's link. */\n id?: string;\n /** The container style: a filled surface or an outline. @default \"filled\" */\n variant?: TextFieldVariant;\n /**\n * Web density, `0` to `-3`: each step takes 4px off the container (56px at 0, 44px at -3).\n * Left out, the field takes the density of the nearest `ThemeProvider` or `ThemeScope`. M3\n * asks for density to be the reader's choice rather than a default, so prefer setting it\n * there, from a setting.\n * @default 0\n */\n density?: Density;\n /** The text, when controlled. */\n value?: string;\n /** The initial text, when uncontrolled. @default \"\" */\n defaultValue?: string;\n /** Called with the new text on every change. */\n onChange?: (value: string) => void;\n /**\n * Helper text below the field. Replaced visually by the error message when invalid, and\n * shown in the error colour while the field is invalid.\n */\n supportingText?: ReactNode;\n /**\n * Shows the supporting text only while the field has focus (M3 allows it \"persistently\n * visible or visible only on focus\"). Its space is kept, and screen readers always get it.\n * @default false\n */\n supportingTextOnFocus?: boolean;\n /** Marks the field invalid (in addition to `validate` and native constraints). */\n invalid?: boolean;\n /** A value is required: adds the `*` to the label and native `required` validation. */\n required?: boolean;\n /** Disables the field: it can't be focused or edited. @default false */\n disabled?: boolean;\n /**\n * The value can be read and copied but not changed. M3 styles it as a regular field and\n * asks for it to be \"clearly labeled as read-only\", e.g. in the supporting text. The root\n * gets `data-readonly`.\n * @default false\n */\n readOnly?: boolean;\n /** Decorative icon at the start, in a 48px box. */\n leadingIcon?: ReactNode;\n /**\n * Icon or icon button at the end (e.g. clear, visibility toggle), in a 48px box. It takes\n * the place of the error icon.\n */\n trailingIcon?: ReactNode;\n /**\n * The icon shown at the end while the field is invalid and has no `trailingIcon` (M3\n * strongly recommends one). Defaults to Material Symbols `error`, named by `labels.error`;\n * `false` turns it off.\n */\n errorIcon?: ReactNode | false;\n /** Text before the input, e.g. \"$\". Shown once the label floats. */\n prefix?: ReactNode;\n /**\n * What screen readers hear for the prefix, e.g. \"US dollars\" for \"$\". The prefix is in the\n * input's description either way; without this, its visible text is read.\n */\n prefixLabel?: string;\n /** Text after the input, e.g. \"kg\". Shown once the label floats. */\n suffix?: ReactNode;\n /** What screen readers hear for the suffix, e.g. \"kilograms\" for \"kg\". */\n suffixLabel?: string;\n /**\n * Renders a `<textarea>`. By default it's an M3 text area: a fixed height of `rows` lines\n * that scrolls vertically, which M3 recommends over growing fields on the web.\n * @default false\n */\n multiline?: boolean;\n /**\n * With `multiline`: grow with the content from `rows` up to `maxRows` (M3's multi-line\n * field) instead of keeping a fixed height.\n * @default false\n */\n autoGrow?: boolean;\n /** Visible lines of a multiline field. @default 4, or 1 with autoGrow */\n rows?: number;\n /** With `autoGrow`: lines after which the field scrolls instead of growing. */\n maxRows?: number;\n /** Shows `count/maxLength` below the field. Defaults to `true` when `maxLength` is set. */\n counter?: boolean;\n /** Words the field announces, for other languages. */\n labels?: Partial<TextFieldLabels>;\n /** Classes for the root element, merged with `classNames.root`; consumer classes win. */\n className?: string;\n /** Classes for each part, by slot. */\n classNames?: TextFieldClassNames;\n /** Inline styles for the root element. */\n style?: CSSProperties;\n /** The root element. Use `inputRef` for the input itself. */\n ref?: Ref<HTMLDivElement>;\n /** The `<input>` (or `<textarea>` when multiline), e.g. for form libraries. */\n inputRef?: Ref<HTMLInputElement | HTMLTextAreaElement>;\n}\n\n/** A visible label, or an accessible name for fields without one. */\ntype TextFieldLabel =\n | {\n /** The visible label, which floats above the input once it has focus or a value. */\n label: ReactNode;\n }\n | {\n label?: undefined;\n /** Accessible name when there's no visible label. */\n 'aria-label': string;\n }\n | {\n label?: undefined;\n /** The id of the element that names the field, when there's no visible label. */\n 'aria-labelledby': string;\n };\n\nexport type TextFieldProps = TextFieldOwnProps & TextFieldLabel;\n\nconst INTERACTIVE = 'input, textarea, button, a, [role=\"button\"]';\n\n/**\n * M3 text field: filled or outlined, with a floating label, leading / trailing icons,\n * prefix and suffix, supporting text, error message and icon, character counter, and a\n * multiline form (a fixed-height text area, or a growing field with `autoGrow`). Built on React Aria `useTextField`, so labelling,\n * descriptions, `validate` and native constraints work as in React Aria.\n *\n * `className` and `style` go on the root; `inputRef` reaches the input.\n *\n * @example\n * <TextField label=\"Email\" type=\"email\" required supportingText=\"We never share it.\" />\n * <TextField variant=\"outlined\" label=\"Price\" prefix=\"$\" inputMode=\"decimal\" />\n */\nexport function TextField(props: TextFieldProps) {\n const {\n variant = 'filled',\n density,\n label,\n value: valueProp,\n defaultValue,\n onChange,\n supportingText,\n supportingTextOnFocus = false,\n errorMessage,\n invalid,\n required,\n disabled = false,\n readOnly,\n leadingIcon,\n trailingIcon,\n errorIcon,\n prefix,\n prefixLabel,\n suffix,\n suffixLabel,\n multiline = false,\n autoGrow = false,\n rows = autoGrow ? 1 : 4,\n maxRows,\n counter,\n labels: labelsProp,\n className,\n classNames,\n style,\n ref,\n inputRef,\n ...otherProps\n } = props;\n\n // `data-*` attributes and the root's DOM attributes describe the component, so they go on the\n // root with `className`; everything else (the input's ARIA, attributes and events) goes to\n // the input via React Aria.\n const { root: rootData, rest: ariaProps } = splitRootProps(otherProps, { wrapper: true });\n const labels = { ...DEFAULT_LABELS, ...labelsProp };\n const prefixId = useId();\n const suffixId = useId();\n const counterId = useId();\n\n const [value, setValue] = useControlledState(valueProp, defaultValue ?? '', onChange);\n const domInputRef = useObjectRef(inputRef as Ref<HTMLInputElement>);\n const {\n labelProps,\n inputProps,\n descriptionProps,\n errorMessageProps,\n isInvalid,\n validationErrors,\n validationDetails,\n } = useTextField(\n {\n ...ariaProps,\n label,\n description: supportingText,\n errorMessage,\n value,\n onChange: setValue,\n isInvalid: invalid,\n isRequired: required,\n isDisabled: disabled,\n isReadOnly: readOnly,\n inputElementType: multiline ? 'textarea' : 'input',\n } as AriaTextFieldOptions<'input' | 'textarea'>,\n domInputRef,\n );\n const { focusProps, isFocused } = useFocusRing({ isTextInput: true });\n const { hoverProps, isHovered } = useHover({ isDisabled: disabled });\n\n const growing = multiline && autoGrow;\n // Grow a multiline field with its content, up to maxRows.\n useLayoutEffect(() => {\n const element = domInputRef.current as HTMLTextAreaElement | null;\n if (!growing || !element) return;\n element.style.height = 'auto';\n const lineHeight = Number.parseFloat(getComputedStyle(element).lineHeight) || 24;\n const max = maxRows ? lineHeight * maxRows : Number.POSITIVE_INFINITY;\n element.style.height = `${Math.min(element.scrollHeight, max)}px`;\n element.style.overflowY = element.scrollHeight > max ? 'auto' : 'hidden';\n }, [value, growing, maxRows, domInputRef]);\n\n const hasLabel = label !== undefined && label !== null;\n const floated = hasLabel && (isFocused || value.length > 0);\n const state: FieldState = disabled\n ? 'disabled'\n : isInvalid\n ? isFocused\n ? 'error-focus'\n : isHovered\n ? 'error-hover'\n : 'error'\n : isFocused\n ? 'focus'\n : isHovered\n ? 'hover'\n : 'rest';\n\n const resolvedError =\n typeof errorMessage === 'function'\n ? errorMessage({ isInvalid, validationErrors, validationDetails })\n : (errorMessage ?? validationErrors.join(' '));\n const showError = isInvalid && Boolean(resolvedError);\n const maxLength = ariaProps.maxLength;\n const showCounter = maxLength !== undefined && counter !== false;\n const showErrorIcon = isInvalid && !trailingIcon && errorIcon !== false;\n\n const styles = textFieldStyles({\n variant,\n hasLabel,\n hasLeadingIcon: Boolean(leadingIcon),\n hasTrailingIcon: Boolean(trailingIcon) || showErrorIcon,\n multiline,\n fixedHeight: multiline && !autoGrow,\n supportingTextOnFocus,\n });\n // The affixes and the counter join React Aria's description (supporting text, then error).\n const describedBy =\n [\n prefix ? prefixId : null,\n suffix ? suffixId : null,\n renderedDescription(inputProps['aria-describedby'], [\n !supportingText && descriptionProps.id,\n !showError && errorMessageProps.id,\n ]),\n showCounter ? counterId : null,\n ]\n .filter(Boolean)\n .join(' ') || undefined;\n\n // Pressing the container (not an icon button) focuses the input, like Compose.\n const focusInput = (event: PointerEvent<HTMLDivElement>) => {\n if ((event.target as Element).closest(INTERACTIVE) || disabled) return;\n event.preventDefault();\n // The field is where the press landed: the page never scrolls to it (2026-10-15).\n domInputRef.current?.focus({ preventScroll: true });\n };\n // Chrome's touch adjustment can send a tap's pointer events to the input while the mouse\n // events it emulates, and the click, land on the padding beside it, so the input never\n // takes focus (2026-10-17). A click on the field that leaves the input unfocused focuses it.\n // The mouse never needs it: its press on the padding already focused the input.\n const focusInputOnClick = (event: MouseEvent<HTMLDivElement>) => {\n const target = event.target as Element;\n if (\n (event.nativeEvent as globalThis.PointerEvent).pointerType === 'mouse' ||\n !event.currentTarget.contains(target) ||\n target.closest(INTERACTIVE) ||\n disabled ||\n document.activeElement === domInputRef.current\n ) {\n return;\n }\n domInputRef.current?.focus({ preventScroll: true });\n };\n\n // Part of the accessible name, as M3 asks (\"The accessibility label must include the\n // asterisk\"); the copy in the outline's notch is hidden with the fieldset.\n const requiredMark = required ? ' *' : null;\n const Input = multiline ? 'textarea' : 'input';\n\n return (\n <div\n {...rootData}\n ref={ref}\n style={style}\n className={styles.root({ class: cn(classNames?.root, className) })}\n data-tokens={mergeDataTokens('md-comp-text-field', rootData)}\n data-variant={variant}\n {...densityAttribute(density)}\n data-field-state={state}\n data-focused={isFocused || undefined}\n data-hovered={(isHovered && !disabled) || undefined}\n data-invalid={isInvalid || undefined}\n data-disabled={disabled || undefined}\n data-readonly={readOnly || undefined}\n data-floated={floated || undefined}\n >\n <div\n {...hoverProps}\n onPointerDown={focusInput}\n onClick={focusInputOnClick}\n data-slot=\"container\"\n className={styles.container({ class: classNames?.container })}\n >\n {variant === 'outlined' && (\n <fieldset\n aria-hidden=\"true\"\n data-slot=\"outline\"\n className={styles.outline({ class: classNames?.outline })}\n >\n {hasLabel && (\n <legend className={styles.notch()}>\n <span className={styles.notchClip()}>\n <span className={styles.notchText()}>\n {label}\n {requiredMark}\n </span>\n </span>\n </legend>\n )}\n </fieldset>\n )}\n {leadingIcon && (\n <span\n aria-hidden=\"true\"\n data-slot=\"leading-icon\"\n className={styles.icon({ class: cn(styles.leadingIcon(), classNames?.leadingIcon) })}\n >\n {leadingIcon}\n </span>\n )}\n <div className={styles.field()}>\n {prefix && (\n <span\n id={prefixId}\n className={styles.affix({ class: cn(styles.prefix(), classNames?.prefix) })}\n >\n <Affix text={prefix} label={prefixLabel} />\n </span>\n )}\n <Input\n {...(mergeProps(inputProps, pickWidgetAria(ariaProps), focusProps) as object)}\n aria-describedby={describedBy}\n ref={domInputRef as Ref<never>}\n data-slot=\"input\"\n rows={multiline ? rows : undefined}\n className={styles.input({ class: classNames?.input })}\n />\n {suffix && (\n <span\n id={suffixId}\n className={styles.affix({ class: cn(styles.suffix(), classNames?.suffix) })}\n >\n <Affix text={suffix} label={suffixLabel} />\n </span>\n )}\n </div>\n {trailingIcon ? (\n <span\n data-slot=\"trailing-icon\"\n className={styles.icon({\n class: cn(styles.trailingIcon(), classNames?.trailingIcon),\n })}\n >\n {trailingIcon}\n </span>\n ) : showErrorIcon ? (\n <span className={styles.icon({ class: styles.trailingIcon() })}>\n <FieldErrorIcon\n label={labels.error}\n icon={errorIcon}\n className={styles.errorIcon({ class: classNames?.errorIcon })}\n />\n </span>\n ) : null}\n {hasLabel && (\n <FieldLabel\n {...labelProps}\n data-slot=\"label\"\n className={styles.label({ class: classNames?.label })}\n >\n {label}\n {requiredMark}\n </FieldLabel>\n )}\n <span\n aria-hidden=\"true\"\n data-slot=\"active-indicator\"\n className={styles.indicator({ class: classNames?.activeIndicator })}\n />\n </div>\n {(supportingText || showError || showCounter) && (\n <div className={styles.supporting()}>\n {supportingText && (\n <SupportingText\n {...descriptionProps}\n data-slot=\"supporting-text\"\n className={cn(\n styles.supportingText({\n class: cn(styles.supportingTextColor(), classNames?.supportingText),\n }),\n showError && 'sr-only',\n )}\n >\n {supportingText}\n </SupportingText>\n )}\n {showError && (\n <ErrorText\n {...errorMessageProps}\n className={styles.supportingText({\n supportingTextOnFocus: false,\n class: classNames?.errorText,\n })}\n >\n {resolvedError}\n </ErrorText>\n )}\n {showCounter && (\n <CharacterCounter\n id={counterId}\n count={value.length}\n max={maxLength}\n label={labels.characterCount(value.length, maxLength)}\n className={styles.counter({ class: classNames?.counter })}\n />\n )}\n </div>\n )}\n </div>\n );\n}\n\n/** An affix's visible text, read as its `label` when there is one. */\nfunction Affix({ text, label }: { text: ReactNode; label: string | undefined }) {\n if (label === undefined) return text;\n return (\n <>\n <span aria-hidden=\"true\">{text}</span>\n <span className=\"sr-only\">{label}</span>\n </>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TextField.js","names":[],"sources":["../../../src/components/text-field/TextField.tsx"],"sourcesContent":["'use client';\n\nimport {\n useId,\n useLayoutEffect,\n type AriaAttributes,\n type CSSProperties,\n type MouseEvent,\n type PointerEvent,\n type ReactNode,\n type Ref,\n} from 'react';\nimport {\n mergeProps,\n useFocusRing,\n useHover,\n useObjectRef,\n useTextField,\n type AriaTextFieldOptions,\n type AriaTextFieldProps,\n} from 'react-aria';\nimport { useControlledState } from 'react-stately/useControlledState';\nimport {\n CharacterCounter,\n ErrorText,\n FieldErrorIcon,\n FieldLabel,\n SupportingText,\n} from '../../primitives/Field';\nimport { densityAttribute, type Density } from '../../tokens/density';\nimport { cn } from '../../utils/cn';\nimport { renderedDescription } from '../../utils/rendered-description';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\nimport { pickWidgetAria, splitRootProps, type WrapperRootDOMProps } from '../../utils/root-props';\nimport { textFieldStyles, type TextFieldVariant } from './text-field-styles';\n\nexport interface TextFieldClassNames {\n /** The root element, merged with `className`. */\n root?: string;\n /** The filled or outlined container around the label, input and icons. */\n container?: string;\n /** The floating label. */\n label?: string;\n /** The `<input>` or `<textarea>`. */\n input?: string;\n /** The leading icon's 48px box. */\n leadingIcon?: string;\n /** The trailing icon's 48px box. */\n trailingIcon?: string;\n /** The prefix text before the input. */\n prefix?: string;\n /** The suffix text after the input. */\n suffix?: string;\n /** The helper text below the field. */\n supportingText?: string;\n /** The error message below the field. */\n errorText?: string;\n /** The character counter below the field. */\n counter?: string;\n /** The built-in error icon. */\n errorIcon?: string;\n /** The outlined field's border (a `<fieldset>`, notched for the floated label). */\n outline?: string;\n /** The filled field's active indicator, the line along the container's bottom. */\n activeIndicator?: string;\n}\n\n/** Words the text field announces; override them for other languages. */\nexport interface TextFieldLabels {\n /** The error icon's name (M3: \"Error\"). @default \"Error\" */\n error: string;\n /**\n * What screen readers hear for the character counter (M3 calls it the \"character count\").\n * @default (count, max) => `Character count: ${count} of ${max}`\n */\n characterCount: (count: number, max: number) => string;\n}\n\nconst DEFAULT_LABELS: TextFieldLabels = {\n error: 'Error',\n characterCount: (count, max) => `Character count: ${count} of ${max}`,\n};\n\n/** Input types the browser draws its own format in when empty, which a resting label would cover. */\nconst FORMAT_SHOWING_TYPES = new Set(['date', 'time', 'datetime-local', 'month', 'week']);\n\ntype FieldState = 'disabled' | 'error-focus' | 'error-hover' | 'error' | 'focus' | 'hover' | 'rest';\n\n/** Props React Aria names with an `is` prefix; we use the HTML-style names instead. */\ntype RenamedAriaProps =\n | 'isDisabled'\n | 'isReadOnly'\n | 'isRequired'\n | 'isInvalid'\n | 'label'\n | 'description'\n | 'inputElementType'\n | 'value'\n | 'defaultValue'\n | 'onChange'\n // Left out: the deprecated `validationState` (`invalid` replaces it) and React Aria's\n // `excludeFromTabOrder` (`tabIndex={-1}` covers the rare case).\n | 'validationState'\n | 'excludeFromTabOrder';\n\ninterface TextFieldOwnProps\n extends\n Omit<AriaTextFieldProps<HTMLInputElement>, RenamedAriaProps>,\n Omit<WrapperRootDOMProps, keyof AriaTextFieldProps<HTMLInputElement>>,\n // The attributes that describe the input go to it, with React Aria's `aria-controls`,\n // `aria-haspopup`, `aria-autocomplete` and `aria-activedescendant`: a field that opens its\n // own suggestions can say so.\n Pick<\n AriaAttributes,\n | 'aria-expanded'\n | 'aria-required'\n | 'aria-invalid'\n | 'aria-readonly'\n | 'aria-multiline'\n | 'aria-placeholder'\n > {\n /** The `<input>`'s (or `<textarea>`'s) id, for a `<label for>` or an error summary's link. */\n id?: string;\n /** The container style: a filled surface or an outline. @default \"filled\" */\n variant?: TextFieldVariant;\n /**\n * Web density, `0` to `-3`: each step takes 4px off the container (56px at 0, 44px at -3).\n * Left out, the field takes the density of the nearest `ThemeProvider` or `ThemeScope`. M3\n * asks for density to be the reader's choice rather than a default, so prefer setting it\n * there, from a setting.\n * @default 0\n */\n density?: Density;\n /** The text, when controlled. */\n value?: string;\n /** The initial text, when uncontrolled. @default \"\" */\n defaultValue?: string;\n /** Called with the new text on every change. */\n onChange?: (value: string) => void;\n /**\n * Helper text below the field. Replaced visually by the error message when invalid, and\n * shown in the error colour while the field is invalid.\n */\n supportingText?: ReactNode;\n /**\n * Shows the supporting text only while the field has focus (M3 allows it \"persistently\n * visible or visible only on focus\"). Its space is kept, and screen readers always get it.\n * @default false\n */\n supportingTextOnFocus?: boolean;\n /** Marks the field invalid (in addition to `validate` and native constraints). */\n invalid?: boolean;\n /** A value is required: adds the `*` to the label and native `required` validation. */\n required?: boolean;\n /** Disables the field: it can't be focused or edited. @default false */\n disabled?: boolean;\n /**\n * The value can be read and copied but not changed. M3 styles it as a regular field and\n * asks for it to be \"clearly labeled as read-only\", e.g. in the supporting text. The root\n * gets `data-readonly`.\n * @default false\n */\n readOnly?: boolean;\n /** Decorative icon at the start, in a 48px box. */\n leadingIcon?: ReactNode;\n /**\n * Icon or icon button at the end (e.g. clear, visibility toggle), in a 48px box. It takes\n * the place of the error icon.\n */\n trailingIcon?: ReactNode;\n /**\n * The icon shown at the end while the field is invalid and has no `trailingIcon` (M3\n * strongly recommends one). Defaults to Material Symbols `error`, named by `labels.error`;\n * `false` turns it off.\n */\n errorIcon?: ReactNode | false;\n /** Text before the input, e.g. \"$\". Shown once the label floats. */\n prefix?: ReactNode;\n /**\n * What screen readers hear for the prefix, e.g. \"US dollars\" for \"$\". The prefix is in the\n * input's description either way; without this, its visible text is read.\n */\n prefixLabel?: string;\n /** Text after the input, e.g. \"kg\". Shown once the label floats. */\n suffix?: ReactNode;\n /** What screen readers hear for the suffix, e.g. \"kilograms\" for \"kg\". */\n suffixLabel?: string;\n /**\n * Renders a `<textarea>`. By default it's an M3 text area: a fixed height of `rows` lines\n * that scrolls vertically, which M3 recommends over growing fields on the web.\n * @default false\n */\n multiline?: boolean;\n /**\n * With `multiline`: grow with the content from `rows` up to `maxRows` (M3's multi-line\n * field) instead of keeping a fixed height.\n * @default false\n */\n autoGrow?: boolean;\n /** Visible lines of a multiline field. @default 4, or 1 with autoGrow */\n rows?: number;\n /** With `autoGrow`: lines after which the field scrolls instead of growing. */\n maxRows?: number;\n /** Shows `count/maxLength` below the field. Defaults to `true` when `maxLength` is set. */\n counter?: boolean;\n /** Words the field announces, for other languages. */\n labels?: Partial<TextFieldLabels>;\n /** Classes for the root element, merged with `classNames.root`; consumer classes win. */\n className?: string;\n /** Classes for each part, by slot. */\n classNames?: TextFieldClassNames;\n /** Inline styles for the root element. */\n style?: CSSProperties;\n /** The root element. Use `inputRef` for the input itself. */\n ref?: Ref<HTMLDivElement>;\n /** The `<input>` (or `<textarea>` when multiline), e.g. for form libraries. */\n inputRef?: Ref<HTMLInputElement | HTMLTextAreaElement>;\n}\n\n/** A visible label, or an accessible name for fields without one. */\ntype TextFieldLabel =\n | {\n /** The visible label, which floats above the input once it has focus or a value. */\n label: ReactNode;\n }\n | {\n label?: undefined;\n /** Accessible name when there's no visible label. */\n 'aria-label': string;\n }\n | {\n label?: undefined;\n /** The id of the element that names the field, when there's no visible label. */\n 'aria-labelledby': string;\n };\n\nexport type TextFieldProps = TextFieldOwnProps & TextFieldLabel;\n\nconst INTERACTIVE = 'input, textarea, button, a, [role=\"button\"]';\n\n/**\n * M3 text field: filled or outlined, with a floating label, leading / trailing icons,\n * prefix and suffix, supporting text, error message and icon, character counter, and a\n * multiline form (a fixed-height text area, or a growing field with `autoGrow`). Built on React Aria `useTextField`, so labelling,\n * descriptions, `validate` and native constraints work as in React Aria.\n *\n * `className` and `style` go on the root; `inputRef` reaches the input.\n *\n * @example\n * <TextField label=\"Email\" type=\"email\" required supportingText=\"We never share it.\" />\n * <TextField variant=\"outlined\" label=\"Price\" prefix=\"$\" inputMode=\"decimal\" />\n */\nexport function TextField(props: TextFieldProps) {\n const {\n variant = 'filled',\n density,\n label,\n value: valueProp,\n defaultValue,\n onChange,\n supportingText,\n supportingTextOnFocus = false,\n errorMessage,\n invalid,\n required,\n disabled = false,\n readOnly,\n leadingIcon,\n trailingIcon,\n errorIcon,\n prefix,\n prefixLabel,\n suffix,\n suffixLabel,\n multiline = false,\n autoGrow = false,\n rows = autoGrow ? 1 : 4,\n maxRows,\n counter,\n labels: labelsProp,\n className,\n classNames,\n style,\n ref,\n inputRef,\n ...otherProps\n } = props;\n\n // `data-*` attributes and the root's DOM attributes describe the component, so they go on the\n // root with `className`; everything else (the input's ARIA, attributes and events) goes to\n // the input via React Aria.\n const { root: rootData, rest: ariaProps } = splitRootProps(otherProps, { wrapper: true });\n const labels = { ...DEFAULT_LABELS, ...labelsProp };\n const prefixId = useId();\n const suffixId = useId();\n const counterId = useId();\n\n const [value, setValue] = useControlledState(valueProp, defaultValue ?? '', onChange);\n const domInputRef = useObjectRef(inputRef as Ref<HTMLInputElement>);\n const {\n labelProps,\n inputProps,\n descriptionProps,\n errorMessageProps,\n isInvalid,\n validationErrors,\n validationDetails,\n } = useTextField(\n {\n ...ariaProps,\n label,\n description: supportingText,\n errorMessage,\n value,\n onChange: setValue,\n isInvalid: invalid,\n isRequired: required,\n isDisabled: disabled,\n isReadOnly: readOnly,\n inputElementType: multiline ? 'textarea' : 'input',\n } as AriaTextFieldOptions<'input' | 'textarea'>,\n domInputRef,\n );\n const { focusProps, isFocused } = useFocusRing({ isTextInput: true });\n const { hoverProps, isHovered } = useHover({ isDisabled: disabled });\n\n const growing = multiline && autoGrow;\n // Grow a multiline field with its content, up to maxRows.\n useLayoutEffect(() => {\n const element = domInputRef.current as HTMLTextAreaElement | null;\n if (!growing || !element) return;\n element.style.height = 'auto';\n const lineHeight = Number.parseFloat(getComputedStyle(element).lineHeight) || 24;\n const max = maxRows ? lineHeight * maxRows : Number.POSITIVE_INFINITY;\n element.style.height = `${Math.min(element.scrollHeight, max)}px`;\n element.style.overflowY = element.scrollHeight > max ? 'auto' : 'hidden';\n }, [value, growing, maxRows, domInputRef]);\n\n const hasLabel = label !== undefined && label !== null;\n const floated =\n hasLabel && (isFocused || value.length > 0 || FORMAT_SHOWING_TYPES.has(ariaProps.type ?? ''));\n const state: FieldState = disabled\n ? 'disabled'\n : isInvalid\n ? isFocused\n ? 'error-focus'\n : isHovered\n ? 'error-hover'\n : 'error'\n : isFocused\n ? 'focus'\n : isHovered\n ? 'hover'\n : 'rest';\n\n const resolvedError =\n typeof errorMessage === 'function'\n ? errorMessage({ isInvalid, validationErrors, validationDetails })\n : (errorMessage ?? validationErrors.join(' '));\n const showError = isInvalid && Boolean(resolvedError);\n const maxLength = ariaProps.maxLength;\n const showCounter = maxLength !== undefined && counter !== false;\n const showErrorIcon = isInvalid && !trailingIcon && errorIcon !== false;\n\n const styles = textFieldStyles({\n variant,\n hasLabel,\n hasLeadingIcon: Boolean(leadingIcon),\n hasTrailingIcon: Boolean(trailingIcon) || showErrorIcon,\n multiline,\n fixedHeight: multiline && !autoGrow,\n supportingTextOnFocus,\n });\n // The affixes and the counter join React Aria's description (supporting text, then error).\n const describedBy =\n [\n prefix ? prefixId : null,\n suffix ? suffixId : null,\n renderedDescription(inputProps['aria-describedby'], [\n !supportingText && descriptionProps.id,\n !showError && errorMessageProps.id,\n ]),\n showCounter ? counterId : null,\n ]\n .filter(Boolean)\n .join(' ') || undefined;\n\n // Pressing the container (not an icon button) focuses the input, like Compose.\n const focusInput = (event: PointerEvent<HTMLDivElement>) => {\n if ((event.target as Element).closest(INTERACTIVE) || disabled) return;\n event.preventDefault();\n // The field is where the press landed: the page never scrolls to it (2026-10-15).\n domInputRef.current?.focus({ preventScroll: true });\n };\n // Chrome's touch adjustment can send a tap's pointer events to the input while the mouse\n // events it emulates, and the click, land on the padding beside it, so the input never\n // takes focus (2026-10-17). A click on the field that leaves the input unfocused focuses it.\n // The mouse never needs it: its press on the padding already focused the input.\n const focusInputOnClick = (event: MouseEvent<HTMLDivElement>) => {\n const target = event.target as Element;\n if (\n (event.nativeEvent as globalThis.PointerEvent).pointerType === 'mouse' ||\n !event.currentTarget.contains(target) ||\n target.closest(INTERACTIVE) ||\n disabled ||\n document.activeElement === domInputRef.current\n ) {\n return;\n }\n domInputRef.current?.focus({ preventScroll: true });\n };\n\n // Part of the accessible name, as M3 asks (\"The accessibility label must include the\n // asterisk\"); the copy in the outline's notch is hidden with the fieldset.\n const requiredMark = required ? ' *' : null;\n const Input = multiline ? 'textarea' : 'input';\n\n return (\n <div\n {...rootData}\n ref={ref}\n style={style}\n className={styles.root({ class: cn(classNames?.root, className) })}\n data-tokens={mergeDataTokens('md-comp-text-field', rootData)}\n data-variant={variant}\n {...densityAttribute(density)}\n data-field-state={state}\n data-focused={isFocused || undefined}\n data-hovered={(isHovered && !disabled) || undefined}\n data-invalid={isInvalid || undefined}\n data-disabled={disabled || undefined}\n data-readonly={readOnly || undefined}\n data-floated={floated || undefined}\n >\n <div\n {...hoverProps}\n onPointerDown={focusInput}\n onClick={focusInputOnClick}\n data-slot=\"container\"\n className={styles.container({ class: classNames?.container })}\n >\n {variant === 'outlined' && (\n <fieldset\n aria-hidden=\"true\"\n data-slot=\"outline\"\n className={styles.outline({ class: classNames?.outline })}\n >\n {hasLabel && (\n <legend className={styles.notch()}>\n <span className={styles.notchClip()}>\n <span className={styles.notchText()}>\n {label}\n {requiredMark}\n </span>\n </span>\n </legend>\n )}\n </fieldset>\n )}\n {leadingIcon && (\n <span\n aria-hidden=\"true\"\n data-slot=\"leading-icon\"\n className={styles.icon({ class: cn(styles.leadingIcon(), classNames?.leadingIcon) })}\n >\n {leadingIcon}\n </span>\n )}\n <div className={styles.field()}>\n {prefix && (\n <span\n id={prefixId}\n className={styles.affix({ class: cn(styles.prefix(), classNames?.prefix) })}\n >\n <Affix text={prefix} label={prefixLabel} />\n </span>\n )}\n <Input\n {...(mergeProps(inputProps, pickWidgetAria(ariaProps), focusProps) as object)}\n aria-describedby={describedBy}\n ref={domInputRef as Ref<never>}\n data-slot=\"input\"\n rows={multiline ? rows : undefined}\n className={styles.input({ class: classNames?.input })}\n />\n {suffix && (\n <span\n id={suffixId}\n className={styles.affix({ class: cn(styles.suffix(), classNames?.suffix) })}\n >\n <Affix text={suffix} label={suffixLabel} />\n </span>\n )}\n </div>\n {trailingIcon ? (\n <span\n data-slot=\"trailing-icon\"\n className={styles.icon({\n class: cn(styles.trailingIcon(), classNames?.trailingIcon),\n })}\n >\n {trailingIcon}\n </span>\n ) : showErrorIcon ? (\n <span className={styles.icon({ class: styles.trailingIcon() })}>\n <FieldErrorIcon\n label={labels.error}\n icon={errorIcon}\n className={styles.errorIcon({ class: classNames?.errorIcon })}\n />\n </span>\n ) : null}\n {hasLabel && (\n <FieldLabel\n {...labelProps}\n data-slot=\"label\"\n className={styles.label({ class: classNames?.label })}\n >\n {label}\n {requiredMark}\n </FieldLabel>\n )}\n <span\n aria-hidden=\"true\"\n data-slot=\"active-indicator\"\n className={styles.indicator({ class: classNames?.activeIndicator })}\n />\n </div>\n {(supportingText || showError || showCounter) && (\n <div className={styles.supporting()}>\n {supportingText && (\n <SupportingText\n {...descriptionProps}\n data-slot=\"supporting-text\"\n className={cn(\n styles.supportingText({\n class: cn(styles.supportingTextColor(), classNames?.supportingText),\n }),\n showError && 'sr-only',\n )}\n >\n {supportingText}\n </SupportingText>\n )}\n {showError && (\n <ErrorText\n {...errorMessageProps}\n className={styles.supportingText({\n supportingTextOnFocus: false,\n class: classNames?.errorText,\n })}\n >\n {resolvedError}\n </ErrorText>\n )}\n {showCounter && (\n <CharacterCounter\n id={counterId}\n count={value.length}\n max={maxLength}\n label={labels.characterCount(value.length, maxLength)}\n className={styles.counter({ class: classNames?.counter })}\n />\n )}\n </div>\n )}\n </div>\n );\n}\n\n/** An affix's visible text, read as its `label` when there is one. */\nfunction Affix({ text, label }: { text: ReactNode; label: string | undefined }) {\n if (label === undefined) return text;\n return (\n <>\n <span aria-hidden=\"true\">{text}</span>\n <span className=\"sr-only\">{label}</span>\n </>\n );\n}\n"],"mappings":"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"}