@windmorph/react 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/autocomplete/Autocomplete.js +1 -1
- package/dist/components/autocomplete/Autocomplete.js.map +1 -1
- package/dist/components/checkbox/checkbox-tokens.d.ts +1 -1
- package/dist/components/chip/chip-tokens.d.ts +4 -4
- package/dist/components/date-picker/date-picker-styles.d.ts +3 -3
- package/dist/components/date-picker/date-picker-tokens.d.ts +1 -1
- package/dist/components/dialog/dialog-tokens.d.ts +1 -1
- package/dist/components/fab-menu/FabMenu.js +2 -2
- package/dist/components/fab-menu/FabMenu.js.map +1 -1
- package/dist/components/menu/Menu.d.ts +13 -0
- package/dist/components/menu/Menu.js +14 -1
- package/dist/components/menu/Menu.js.map +1 -1
- package/dist/components/menu/menu-tokens.d.ts +1 -1
- package/dist/components/navigation/navigation-styles.js +1 -1
- package/dist/components/navigation/navigation-styles.js.map +1 -1
- package/dist/components/search/search-styles.js +2 -2
- package/dist/components/search/search-styles.js.map +1 -1
- package/dist/components/slider/Slider.js +5 -1
- package/dist/components/slider/Slider.js.map +1 -1
- package/dist/components/snackbar/snackbar-tokens.d.ts +1 -1
- package/dist/components/text-field/text-field-styles.js +2 -2
- package/dist/components/text-field/text-field-styles.js.map +1 -1
- package/dist/components/tooltip/tooltip-tokens.d.ts +1 -1
- package/dist/primitives/use-m3-interaction.js +10 -6
- package/dist/primitives/use-m3-interaction.js.map +1 -1
- package/dist/styles/utilities.css +22 -15
- package/dist/styles.compiled.css +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Menu.js","names":["Overlay"],"sources":["../../../src/components/menu/Menu.tsx"],"sourcesContent":["'use client';\n\nimport {\n cloneElement,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n useSyncExternalStore,\n type CSSProperties,\n type FocusEvent,\n type JSX,\n type Key,\n type ReactElement,\n type ReactNode,\n type Ref,\n type RefObject,\n} from 'react';\nimport {\n DismissButton,\n I18nProvider,\n mergeProps,\n useLocale,\n useMenu,\n useMenuItem,\n useMenuSection,\n useObjectRef,\n usePopover,\n useButton,\n useSubmenuTrigger,\n type AriaMenuItemProps,\n type AriaMenuOptions,\n type AriaMenuProps,\n type AriaPopoverProps,\n type Placement,\n} from 'react-aria';\nimport {\n Item,\n Section,\n useSubmenuTriggerState,\n useTreeState,\n type Node,\n type RootMenuTriggerState,\n type SubmenuTriggerState,\n type TreeState,\n} from 'react-stately';\nimport { localeWithDirection } from '../../primitives/locale-with-direction';\nimport { Overlay } from '../../primitives/Overlay';\nimport { TriggerContext } from '../../primitives/TriggerContext';\nimport { useM3Interaction } from '../../primitives/use-m3-interaction';\nimport { usePresence } from '../../primitives/use-presence';\nimport { densityAttribute, type Density } from '../../tokens/density';\nimport { toAriaPlacement } from '../../utils/placement';\nimport { assertCollectionChildren } from '../../utils/assert-collection-children';\nimport { cn } from '../../utils/cn';\nimport { useTypeaheadDelegate } from '../../utils/typeahead';\nimport { COMPACT_WINDOW, useMediaQuery } from '../date-picker/use-media-query';\nimport { Divider } from '../divider/Divider';\nimport { LazyBottomSheet, usePreloadBottomSheet } from '../sheet/lazy-bottom-sheet';\nimport { BackIcon } from '../sheet/sheet-icons';\nimport { useRevealEntering } from './use-reveal-entering';\nimport { menuOwnWidth, menuStyles, type MenuLayout, type MenuVariant } from './menu-styles';\nimport { MenuTriggerContext, type MenuTriggerContextValue } from './menu-trigger';\n\nexport {\n ContextMenuTrigger,\n MenuTrigger,\n type ContextMenuTriggerProps,\n type MenuTriggerProps,\n} from './menu-trigger';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\nimport { splitRootProps, type RootDOMProps } from '../../utils/root-props';\n\nconst CheckIcon = () => (\n <svg viewBox=\"0 -960 960 960\" fill=\"currentColor\">\n <path d=\"M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z\" />\n </svg>\n);\n\n/** The default submenu arrow (Material Symbols `arrow_right`), mirrored in RTL. */\nconst SubmenuIcon = () => (\n <svg viewBox=\"0 -960 960 960\" fill=\"currentColor\" className=\"rtl:-scale-x-100\">\n <path d=\"M400-280v-400l200 200-200 200Z\" />\n </svg>\n);\n\n/** Props of a {@link MenuItem}; the React `key` identifies it for `onAction` and selection. */\nexport interface MenuItemProps {\n /** The label. */\n children: ReactNode;\n /** Plain-text label for typeahead when `children` is not a string. */\n textValue?: string;\n /** Icon before the label. */\n leadingIcon?: ReactNode;\n /** Icon shown while selected (defaults to a check in selectable menus). */\n selectedIcon?: ReactNode;\n /** Second line of text (supporting text), read as part of the item's name after the label. */\n description?: ReactNode;\n /**\n * A badge before the trailing text or icon (M3 anatomy), such as a `Badge` with a count.\n * It's read after the name, as the item's description.\n */\n badge?: ReactNode;\n /** Keyboard shortcut shown at the end, e.g. \"⌘C\", read as the item's description. */\n shortcut?: string;\n /** Icon at the end (an item with a submenu shows an arrow by default). */\n trailingIcon?: ReactNode;\n /** Accessible name when the label isn't plain text. */\n 'aria-label'?: string;\n /** Classes for the item, merged after `classNames.item`; consumer classes win. */\n className?: string;\n}\n\n/** An item in a `Menu` (a React Stately collection item: identify it with `key`). */\nexport const MenuItem = Item as unknown as (props: MenuItemProps) => JSX.Element;\n\nexport interface MenuGroupProps {\n /** Group label shown above its items. */\n title?: ReactNode;\n /** Accessible name for the group when it has no `title`. */\n 'aria-label'?: string;\n /** `MenuItem`s and `SubmenuTrigger`s. */\n children: ReactElement | ReactElement[];\n}\n\n/**\n * A group of items (a React Stately collection section): set off by dividers in the\n * `divider` layout, its own surface in the `gap` layout.\n */\nexport const MenuGroup = Section as unknown as (props: MenuGroupProps) => JSX.Element;\n\nexport interface SubmenuTriggerProps {\n /** The `MenuItem` that opens the submenu, then the submenu's `Menu`. */\n children: [ReactElement<MenuItemProps>, ReactElement<MenuProps>];\n /** How long (ms) the pointer rests on the item before the submenu opens. @default 200 */\n delay?: number;\n}\n\n/** What a `SubmenuTrigger` adds to its item's collection node. */\ninterface SubmenuItemProps extends MenuItemProps {\n submenu?: ReactElement<MenuProps>;\n submenuDelay?: number;\n /** See {@link CheckableMenuItemProps}. */\n checkable?: boolean;\n}\n\n/**\n * Props of a {@link CheckableMenuItem}, for the library's own menus (not exported from the\n * package): in a selectable menu, `checkable={false}` makes an item a plain action, with the\n * `menuitem` role, no checked state and no check, that closes the menu like any action.\n */\nexport interface CheckableMenuItemProps extends MenuItemProps {\n /** Whether the item can be checked in a selectable menu. @default true */\n checkable?: boolean;\n}\n\n/** A {@link MenuItem} that can opt out of the menu's selection (library-internal). */\nexport const CheckableMenuItem = Item as unknown as (props: CheckableMenuItemProps) => JSX.Element;\n\n/** Only a menu collection reads a `SubmenuTrigger`; rendered on its own it's nothing. */\nfunction SubmenuTriggerElement(): null {\n return null;\n}\n\n/** React Stately builds the item's node from this, with the submenu on its props. */\nSubmenuTriggerElement.getCollectionNode = function* getCollectionNode({\n children: [item, submenu],\n delay,\n}: SubmenuTriggerProps) {\n // `cloneElement` keeps the item's own `key`.\n yield { element: cloneElement(item, { submenu, submenuDelay: delay } as SubmenuItemProps) };\n};\n\n/**\n * Gives a menu item a submenu (M3: \"On web, menus can open submenus\"). It opens beside the\n * item, without covering it, on hover, press, Enter, Space or the arrow key pointing away\n * from the menu (Left in right-to-left), and closes with the opposite arrow or Escape.\n * While it's open, the item shows its active (pressed) layer, and the menu holding focus\n * takes rounder corners than the other one.\n *\n * @example\n * <SubmenuTrigger>\n * <MenuItem key=\"share\">Share</MenuItem>\n * <Menu onAction={(key) => share(key)}>\n * <MenuItem key=\"email\">Email</MenuItem>\n * <MenuItem key=\"link\">Copy link</MenuItem>\n * </Menu>\n * </SubmenuTrigger>\n */\nexport const SubmenuTrigger = SubmenuTriggerElement as unknown as (\n props: SubmenuTriggerProps,\n) => JSX.Element;\n\nexport interface MenuClassNames {\n /**\n * The menu's surface (the `menu` element): its colour, corners, size and the list that\n * scrolls. In a sheet it spans the sheet, without a surface of its own.\n */\n panel?: string;\n /**\n * The bottom sheet the menu opens in (`presentation=\"sheet\"`, or `\"auto\"` on compact\n * windows): the sheet's surface, as its `classNames.panel`.\n */\n sheet?: string;\n /** Each group of items (its own surface in the `gap` layout). */\n group?: string;\n /** Each `MenuGroup`'s heading. */\n heading?: string;\n /** Each item. */\n item?: string;\n /** Each item's leading icon, or the icon marking a selected item. */\n icon?: string;\n /** Each item's `description` line. */\n description?: string;\n /** Each item's keyboard `shortcut`. */\n shortcut?: string;\n /** Each item's trailing icon, including a submenu's arrow. */\n trailingIcon?: string;\n /** The dividers between groups in the `divider` layout. */\n divider?: string;\n}\n\nexport interface MenuProps\n extends\n Omit<\n AriaMenuProps<object>,\n | 'children'\n | 'items'\n | 'onClose'\n | 'autoFocus'\n | 'disallowEmptySelection'\n // The arrow keys always wrap and Escape always closes the menu, as M3's menus do.\n | 'shouldFocusWrap'\n | 'escapeKeyBehavior'\n >,\n Omit<RootDOMProps, keyof AriaMenuProps<object>> {\n /**\n * With `selectionMode`: the last selected item can be deselected, leaving none. `false` keeps\n * at least one item selected. @default true\n */\n deselectable?: boolean;\n /**\n * The keys of items that can't be chosen. They stay focusable, as M3 asks (\"Disabled menu\n * items can receive focus but aren't selectable\").\n */\n disabledKeys?: AriaMenuProps<object>['disabledKeys'];\n /** `MenuItem`s, `MenuGroup`s and `SubmenuTrigger`s. */\n children: ReactNode;\n /** `standard` (surface colours) or `vibrant` (tertiary container). @default \"standard\" */\n variant?: MenuVariant;\n /**\n * How groups are set apart. `divider`: one surface with dividers between groups, which M3\n * recommends on the web and for menus that scroll. `gap`: each group is its own surface,\n * 2px apart; a `gap` menu that has to scroll switches to dividers, since M3 doesn't use\n * gaps in scrolling menus. A submenu takes its parent's layout and variant unless it\n * sets its own.\n * @default \"divider\"\n */\n layout?: MenuLayout;\n /**\n * Web density (M3: \"On web only … Increasing density decreases the top and bottom\n * padding\"): each step down takes 4px off the items' height (44px at 0, 32px at -3) and 2px\n * off the space around dividers. Left out, the menu takes the density of the nearest\n * `ThemeProvider` or `ThemeScope`; a submenu takes its parent menu's. Keep 0 for touch.\n * @default 0\n */\n density?: Density;\n /**\n * Placement relative to the trigger: a side, then optionally the edge to align with\n * (`start` / `end` follow the text direction). A submenu always opens beside its item.\n * @default \"bottom-start\"\n */\n placement?: MenuPlacement;\n /** Gap between the trigger and the menu, in px (a split button's menu sits 4px away). @default 0 */\n offset?: number;\n /**\n * The least space kept between the menu and the window's edges, in px. A split button\n * passes 0, so its menu lines up with the trailing button even at the edge of the screen.\n * @default 12\n */\n containerPadding?: number;\n /**\n * Where the menu opens. `popover`: anchored to its trigger. `sheet`: in a modal bottom\n * sheet. `auto`: a sheet on compact windows (below 600px wide) and a popover on larger\n * ones (M3: \"Consider adapting menus into bottom sheets on small screens\"). Set it on the\n * root menu.\n * @default \"popover\"\n */\n presentation?: MenuPresentation;\n /** Words the menu shows in a sheet. Set them on the root menu. */\n labels?: Partial<MenuLabels>;\n /**\n * Open and close with no motion (M3: \"In dense products, such as on desktop, menus can\n * open instantly to reduce motion\"). Submenus follow their root menu. Popovers only: a menu\n * shown as a bottom sheet (`presentation=\"sheet\"`, or `auto` on a compact window) keeps the\n * sheet's slide, as M3's and Compose's modal bottom sheets always animate; under reduced\n * motion it fades instead.\n * @default false\n */\n instant?: boolean;\n /**\n * Goes with `style` on the menu's outermost element: the box React Aria places against the\n * trigger, or the bottom sheet's outermost element in a sheet. The surface inside keeps\n * its size and its place against the trigger whatever layout class lands here (the box\n * keeps the menu's own width); style it with `classNames.panel`.\n */\n className?: string;\n /** Classes for each part, by slot. */\n classNames?: MenuClassNames;\n /** Goes on the menu's outermost element, with `className`. */\n style?: CSSProperties;\n /** Ref to the menu's list, the `role=\"menu\"` element, in a popover or a sheet. */\n ref?: Ref<HTMLDivElement>;\n}\n\n/** Where a {@link Menu} sits against its trigger: a side, then optionally the edge it aligns with. */\nexport type MenuPlacement =\n | 'top'\n | 'bottom'\n | 'start'\n | 'end'\n | 'top-start'\n | 'top-end'\n | 'bottom-start'\n | 'bottom-end'\n | 'start-top'\n | 'start-bottom'\n | 'end-top'\n | 'end-bottom';\n\n/** Where a {@link Menu} opens. */\nexport type MenuPresentation = 'popover' | 'sheet' | 'auto';\n\n/** Words a {@link Menu} shows. */\nexport interface MenuLabels {\n /**\n * In a sheet, the button above a submenu that goes back to the menu that opened it.\n * @default \"Back\"\n */\n back: string;\n}\n\nconst DEFAULT_LABELS: MenuLabels = { back: 'Back' };\n\n/**\n * M3 Expressive menu: items on one surface with dividers between groups (or on grouped\n * surfaces), standard or vibrant colours, optional single or multiple selection with a\n * selected shape and check, submenus, keyboard navigation and typeahead (React Aria\n * `useMenu`). Must be inside a `MenuTrigger` or `ContextMenuTrigger`; it opens anchored to\n * the trigger in a portal that keeps the surrounding theme and direction.\n *\n * @example\n * <MenuTrigger>\n * <IconButton icon={<MoreIcon />} aria-label=\"More\" />\n * <Menu onAction={(key) => run(key)}>\n * <MenuGroup>\n * <MenuItem key=\"copy\" shortcut=\"⌘C\">Copy</MenuItem>\n * <MenuItem key=\"paste\" shortcut=\"⌘V\">Paste</MenuItem>\n * </MenuGroup>\n * <MenuGroup>\n * <MenuItem key=\"delete\">Delete</MenuItem>\n * </MenuGroup>\n * </Menu>\n * </MenuTrigger>\n */\nexport function Menu({ presentation = 'popover', instant = false, ...props }: MenuProps) {\n const trigger = useContext(MenuTriggerContext);\n if (!trigger) throw new Error('[@windmorph/react] <Menu> must be used inside <MenuTrigger>.');\n const compact = useMediaQuery(COMPACT_WINDOW);\n const presence = usePresence(trigger.state.isOpen);\n const asSheet = presentation === 'sheet' || (presentation === 'auto' && compact);\n usePreloadBottomSheet(trigger.triggerRef, asSheet);\n if (asSheet) return <MenuSheet {...props} trigger={trigger} />;\n // An instant menu leaves at once, without waiting for an exit transition.\n const isPresent = instant ? trigger.state.isOpen : presence.isPresent;\n const isExiting = !instant && presence.isExiting;\n if (!isPresent) return null;\n return (\n <Overlay exiting={isExiting}>\n <MenuPopover\n {...props}\n trigger={trigger}\n instant={instant}\n isExiting={isExiting}\n exitProps={presence.exitProps}\n />\n </Overlay>\n );\n}\n\n/**\n * The menu in a modal bottom sheet (M3: menus can adapt into bottom sheets on small\n * screens). The sheet is a dialog named like the menu; the menu inside keeps its role,\n * keyboard, typeahead and initial focus, and spans the sheet without a surface of its own.\n * The sheet's code loads on first open (or when the trigger is hovered or focused), so a\n * menu that only opens as a popover doesn't carry it.\n */\nfunction MenuSheet({\n trigger,\n layout: _layout,\n placement: _placement,\n offset: _offset,\n variant = 'standard',\n labels,\n className,\n style,\n ...props\n}: Omit<MenuProps, 'presentation' | 'instant'> & { trigger: MenuTriggerContextValue }) {\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n const [activeLevel, setActiveLevel] = useState(0);\n const menuProps = mergeProps(trigger.menuProps, props);\n const label = menuProps['aria-label'];\n const naming =\n label != null\n ? { 'aria-label': label }\n : { 'aria-labelledby': menuProps['aria-labelledby'] ?? '' };\n return (\n <LazyBottomSheet\n {...naming}\n // A menu sheet opens at its own height (up to the window, the list scrolling inside):\n // at half the window, a short phone left its last items off-screen, where the arrow\n // keys could still move focus.\n skipPartiallyExpanded\n open={trigger.state.isOpen}\n onOpenChange={(open) => (open ? trigger.state.open() : trigger.state.close())}\n className={className}\n style={style}\n classNames={props.classNames?.sheet ? { panel: props.classNames.sheet } : undefined}\n >\n <div ref={setContainer} className={menuStyles().sheetContent()}>\n <TriggerContext value={null}>\n <MenuTreeContext\n value={{\n rootState: trigger.state,\n container,\n activeLevel,\n setActiveLevel,\n layout: 'divider',\n variant,\n density: props.density,\n instant: false,\n sheet: true,\n labels: { ...DEFAULT_LABELS, ...labels },\n }}\n >\n <MenuList\n {...menuProps}\n layout=\"divider\"\n variant={variant}\n level={0}\n embedded\n onClose={trigger.state.close}\n autoFocus={trigger.state.focusStrategy ?? 'first'}\n />\n </MenuTreeContext>\n </TriggerContext>\n </div>\n </LazyBottomSheet>\n );\n}\n\n/** Shared by a menu and its submenus. */\ninterface MenuTreeContextValue {\n rootState: RootMenuTriggerState;\n /** The root popover, which submenus portal into (inside its focus scope, never inert). */\n container: HTMLElement | null;\n /** The level of the menu holding focus (0 is the root menu). */\n activeLevel: number;\n setActiveLevel: (level: number) => void;\n /** What submenus inherit. */\n layout: MenuLayout;\n variant: MenuVariant;\n density: MenuProps['density'];\n /** Submenus open and close with no motion too. */\n instant: boolean;\n /**\n * In a sheet, a submenu takes the sheet's place of the menu that opened it, below a back\n * button, instead of opening beside its item.\n */\n sheet: boolean;\n labels: MenuLabels;\n}\n\nconst MenuTreeContext = createContext<MenuTreeContextValue | null>(null);\n\n/** The menu element an item is rendered in (a submenu's parent). */\nconst MenuListContext = createContext<RefObject<HTMLDivElement | null> | null>(null);\n\nconst subscribeToNothing = () => () => {};\n\n/**\n * React Aria resolves `start` / `end` from its locale context, not the DOM `dir`, so an\n * RTL region without an `I18nProvider` would align the menu to the wrong edge. Resolve\n * them to physical sides from the trigger's computed direction instead.\n */\nfunction toPhysicalPlacement(placement: MenuPlacement, direction: string): Placement {\n return toAriaPlacement(placement, direction === 'rtl');\n}\n\ntype ExitProps = ReturnType<typeof usePresence>['exitProps'];\n\nfunction MenuPopover({\n trigger,\n instant,\n isExiting,\n exitProps,\n placement = 'bottom-start',\n offset = 0,\n containerPadding = 12,\n layout = 'divider',\n variant = 'standard',\n labels,\n className,\n style,\n ...props\n}: Omit<MenuProps, 'presentation' | 'instant'> & {\n trigger: MenuTriggerContextValue;\n instant: boolean;\n isExiting: boolean;\n exitProps: ExitProps;\n}) {\n const popoverRef = useRef<HTMLDivElement>(null);\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n const setPopover = useCallback((element: HTMLDivElement | null) => {\n popoverRef.current = element;\n setContainer(element);\n }, []);\n const [activeLevel, setActiveLevel] = useState(0);\n // The DOM is the source of truth for direction; read it like an external store.\n const direction = useSyncExternalStore(\n subscribeToNothing,\n () =>\n trigger.triggerRef.current ? getComputedStyle(trigger.triggerRef.current).direction : 'ltr',\n () => 'ltr',\n );\n // Submenus' arrow keys and placement follow the locale's direction: give it the DOM's.\n const { locale, direction: localeDirection } = useLocale();\n const menuLocale =\n direction === localeDirection\n ? locale\n : localeWithDirection(locale, direction === 'rtl' ? 'rtl' : 'ltr');\n const {\n popoverProps,\n underlayProps,\n placement: actualPlacement,\n } = usePopover(\n {\n triggerRef: trigger.triggerRef,\n popoverRef,\n placement: toPhysicalPlacement(placement, direction),\n offset,\n containerPadding,\n },\n trigger.state,\n );\n const revealRef = useRef<HTMLDivElement>(null);\n const entering = useRevealEntering(actualPlacement, revealRef);\n const close = trigger.state.close;\n // Tab moves on from an open menu and closes it and its submenus (the APG menu button\n // pattern). React Aria's focus scope moves focus past the trigger, or back to it when\n // nothing follows; without the close, the menu stayed open with focus outside it. That\n // scope handles Tab on the document's capture phase and stops it, so this listens on the\n // window's, which comes first.\n useEffect(() => {\n const view = container?.ownerDocument.defaultView;\n if (!container || !view) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Tab' || event.altKey || event.ctrlKey || event.metaKey) return;\n if (container.contains(container.ownerDocument.activeElement)) close();\n };\n view.addEventListener('keydown', onKeyDown, true);\n return () => view.removeEventListener('keydown', onKeyDown, true);\n }, [container, close]);\n const styles = menuStyles();\n return (\n <>\n <div {...underlayProps} className=\"fixed inset-0\" />\n <div\n {...popoverProps}\n ref={setPopover}\n // React Aria's placement wins over a consumer style's and, through the attribute\n // (styles/utilities.css), over consumer inset classes.\n style={{ ...style, ...popoverProps.style }}\n data-overlay-placement=\"\"\n className={styles.popover({ class: cn(menuOwnWidth, className) })}\n >\n <DismissButton onDismiss={trigger.state.close} />\n <div\n {...exitProps}\n ref={revealRef}\n data-entering={entering || undefined}\n data-exiting={isExiting || undefined}\n data-placement={actualPlacement ?? undefined}\n className={menuStyles({ instant }).reveal()}\n >\n <TriggerContext value={null}>\n <I18nProvider locale={menuLocale}>\n <MenuTreeContext\n value={{\n rootState: trigger.state,\n container,\n activeLevel,\n setActiveLevel,\n layout,\n variant,\n density: props.density,\n instant,\n sheet: false,\n labels: { ...DEFAULT_LABELS, ...labels },\n }}\n >\n <MenuList\n {...mergeProps(trigger.menuProps, props)}\n layout={layout}\n variant={variant}\n level={0}\n onClose={trigger.state.close}\n autoFocus={trigger.state.focusStrategy ?? 'first'}\n />\n </MenuTreeContext>\n </I18nProvider>\n </TriggerContext>\n </div>\n <DismissButton onDismiss={trigger.state.close} />\n </div>\n </>\n );\n}\n\ntype Position = 'only' | 'first' | 'middle' | 'last';\ntype Edges = { top: boolean; bottom: boolean };\n\nconst positionOf = (index: number, count: number): Position =>\n count === 1 ? 'only' : index === 0 ? 'first' : index === count - 1 ? 'last' : 'middle';\n\n/** Which of a group's edges have the outer corners (the others have 8px). */\nconst roundEdges = (position: Position): Edges => ({\n top: position === 'only' || position === 'first',\n bottom: position === 'only' || position === 'last',\n});\n\n/** Sections become groups; runs of loose items between them form implicit groups. */\nfunction toGroups(nodes: Node<object>[]): (Node<object> | Node<object>[])[] {\n const groups: (Node<object> | Node<object>[])[] = [];\n let run: Node<object>[] = [];\n for (const node of nodes) {\n if (node.type === 'section') {\n if (run.length) groups.push(run);\n run = [];\n groups.push(node);\n } else {\n run.push(node);\n }\n }\n if (run.length) groups.push(run);\n return groups;\n}\n\ntype MenuListProps = Omit<\n MenuProps,\n 'placement' | 'layout' | 'variant' | 'presentation' | 'instant' | 'className' | 'style'\n> &\n Pick<AriaMenuOptions<object>, 'onClose' | 'autoFocus' | 'onKeyDown' | 'onKeyUp'> & {\n layout: MenuLayout;\n variant: MenuVariant;\n /** 0 for the root menu, then one more per submenu. */\n level: number;\n listRef?: RefObject<HTMLDivElement | null>;\n /** In a sheet: the list spans it, with no surface of its own. */\n embedded?: boolean;\n };\n\n/**\n * Whether the list has had to scroll, from a `ResizeObserver` (so it's measured only when\n * its size changes). Once true it stays true while the menu is open, so a `gap` menu that\n * switches to dividers can't flip back and forth.\n */\nfunction useHasScrolled(ref: RefObject<HTMLElement | null>, enabled: boolean) {\n const [scrolls, setScrolls] = useState(false);\n useLayoutEffect(() => {\n const element = ref.current;\n if (!enabled || scrolls || !element || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(() => {\n if (element.scrollHeight > element.clientHeight + 1) setScrolls(true);\n });\n observer.observe(element);\n return () => observer.disconnect();\n }, [ref, enabled, scrolls]);\n return scrolls;\n}\n\nfunction MenuList({\n deselectable = true,\n variant,\n layout: requestedLayout,\n density,\n level,\n classNames,\n listRef,\n embedded = false,\n ref: forwardedRef,\n ...allProps\n}: MenuListProps) {\n // The menu list is the menu's root (its `ref` and `id` land there): its DOM attributes too.\n const { root, rest: props } = splitRootProps(allProps);\n // MenuItem / MenuGroup are React Stately Item / Section, so children are a collection.\n assertCollectionChildren(\n props.children,\n 'Menu',\n 'MenuItem, MenuGroup and SubmenuTrigger elements',\n );\n const tree = useContext(MenuTreeContext);\n // M3: \"Disabled menu items can receive focus but aren't selectable.\"\n const state = useTreeState({\n ...(props as AriaMenuProps<object>),\n disallowEmptySelection: !deselectable,\n disabledBehavior: 'selection',\n });\n const ownRef = useObjectRef(forwardedRef);\n const ref = listRef ?? ownRef;\n // M3 keyboard table: \"Letters — Focus moves to the next menu item starting with letter\".\n const keyboardDelegate = useTypeaheadDelegate(state, ref);\n const { menuProps } = useMenu(\n // The arrow keys wrap from the last item to the first and back, as M3's menus and the rest\n // of the library's collections do (APG makes it optional).\n { ...(props as AriaMenuOptions<object>), keyboardDelegate, shouldFocusWrap: true },\n state,\n ref,\n );\n const groups = toGroups([...state.collection]);\n const scrolled = useHasScrolled(ref, requestedLayout === 'gap');\n const layout: MenuLayout = scrolled ? 'divider' : requestedLayout;\n const styles = menuStyles({ variant, layout, embedded });\n // While a submenu is open, the menu holding focus is rounder than the others (M3).\n const submenuOpen = Boolean(tree && tree.rootState.expandedKeysStack.length > 0);\n const shape = !submenuOpen ? undefined : tree?.activeLevel === level ? 'active' : 'inactive';\n const setActive = tree?.setActiveLevel;\n\n return (\n <div\n {...mergeProps(root, menuProps, {\n // Focus events bubble through portals: count only focus inside this menu's element.\n onFocus: (event: FocusEvent<HTMLDivElement>) => {\n if (event.currentTarget.contains(event.target)) setActive?.(level);\n },\n })}\n ref={ref}\n {...densityAttribute(density)}\n data-tokens={mergeDataTokens('md-comp-menus', root)}\n data-variant={variant}\n data-layout={layout}\n data-shape={shape}\n className={styles.menu({ class: classNames?.panel })}\n >\n <MenuListContext value={ref}>\n {groups.map((group, index) => {\n const position = positionOf(index, groups.length);\n // In the divider layout the menu is the surface: its first and last items nest in\n // its corners, and dividers go between groups.\n const edges: Edges =\n layout === 'divider'\n ? { top: index === 0, bottom: index === groups.length - 1 }\n : roundEdges(position);\n const groupStyles = menuStyles({ variant, layout, groupPosition: position, morph: true });\n return (\n <GroupWithDivider\n key={Array.isArray(group) ? group[0]!.key : group.key}\n divider={\n layout === 'divider' && index > 0 ? (\n // A menu's divider is a `separator` between its groups (WAI-ARIA menu).\n <Divider\n decorative={false}\n className={styles.divider({ class: classNames?.divider })}\n />\n ) : null\n }\n >\n {Array.isArray(group) ? (\n <div\n role=\"presentation\"\n className={groupStyles.group({ class: classNames?.group })}\n >\n <MenuItems\n nodes={group}\n state={state}\n variant={variant}\n edges={edges}\n classNames={classNames}\n />\n </div>\n ) : (\n <MenuSection\n section={group}\n state={state}\n variant={variant}\n className={groupStyles.group({ class: classNames?.group })}\n headingClassName={groupStyles.heading({ class: classNames?.heading })}\n edges={edges}\n classNames={classNames}\n />\n )}\n </GroupWithDivider>\n );\n })}\n </MenuListContext>\n </div>\n );\n}\n\nfunction GroupWithDivider({ divider, children }: { divider: ReactNode; children: ReactNode }) {\n return (\n <>\n {divider}\n {children}\n </>\n );\n}\n\nfunction MenuSection({\n section,\n state,\n variant,\n className,\n headingClassName,\n edges,\n classNames,\n}: {\n section: Node<object>;\n state: TreeState<object>;\n variant: MenuVariant;\n className: string;\n headingClassName: string;\n edges: Edges;\n classNames?: MenuClassNames;\n}) {\n const { itemProps, headingProps, groupProps } = useMenuSection({\n heading: section.rendered,\n 'aria-label': section['aria-label'],\n });\n return (\n <div {...itemProps} className={className}>\n {section.rendered && (\n <span {...headingProps} className={headingClassName}>\n {section.rendered}\n </span>\n )}\n <div {...groupProps} className=\"flex flex-col\">\n <MenuItems\n nodes={[...section.childNodes]}\n state={state}\n variant={variant}\n // A heading takes the group's top edge, so the first item doesn't touch it.\n edges={{ ...edges, top: !section.rendered && edges.top }}\n classNames={classNames}\n />\n </div>\n </div>\n );\n}\n\n/**\n * Items nest their corners inside the container's: an item on an outer edge gets the\n * outer corner less the 4px padding, and every other corner is 4px.\n */\nfunction MenuItems({\n nodes,\n state,\n variant,\n edges,\n classNames,\n}: {\n nodes: Node<object>[];\n state: TreeState<object>;\n variant: MenuVariant;\n edges: Edges;\n classNames?: MenuClassNames;\n}) {\n return nodes.map((node, index) => {\n const itemProps: MenuItemViewProps = {\n node,\n state,\n variant,\n top: index === 0 && edges.top ? 'outer' : 'plain',\n bottom: index === nodes.length - 1 && edges.bottom ? 'outer' : 'plain',\n classNames,\n };\n return (node.props as SubmenuItemProps).submenu ? (\n <SubmenuItemView key={node.key} {...itemProps} />\n ) : (\n <MenuItemView key={node.key} {...itemProps} />\n );\n });\n}\n\ninterface MenuItemViewProps {\n node: Node<object>;\n state: TreeState<object>;\n variant: MenuVariant;\n top: 'outer' | 'plain';\n bottom: 'outer' | 'plain';\n classNames?: MenuClassNames;\n /** A submenu trigger's props (from `useSubmenuTrigger`) and ref. */\n triggerProps?: Partial<AriaMenuItemProps>;\n itemRef?: RefObject<HTMLDivElement | null>;\n /** Whether the item's submenu is open (its active state). */\n submenuOpen?: boolean;\n}\n\n/** An item that opens a submenu, and the submenu's popover. */\nfunction SubmenuItemView(props: MenuItemViewProps) {\n const tree = useContext(MenuTreeContext)!;\n const parentMenuRef = useContext(MenuListContext)!;\n const { submenu, submenuDelay } = props.node.props as SubmenuItemProps;\n const submenuState = useSubmenuTriggerState({ triggerKey: props.node.key }, tree.rootState);\n const itemRef = useRef<HTMLDivElement>(null);\n const submenuRef = useRef<HTMLDivElement>(null);\n const { submenuTriggerProps, submenuProps, popoverProps } = useSubmenuTrigger(\n {\n parentMenuRef,\n submenuRef,\n delay: submenuDelay,\n isDisabled: props.state.disabledKeys.has(props.node.key),\n },\n submenuState,\n itemRef,\n );\n // In a sheet a submenu replaces its menu, so hovering an item doesn't open it.\n const { isOpen, onHoverChange, ...triggerProps } = submenuTriggerProps;\n // A sheet's submenu hides the item that opened it, so React Aria's focus on the item when\n // the submenu closes (← or Escape) finds it hidden. Once the menu shows again, the item\n // takes focus back from the closed submenu (or the back button), unless every menu closed.\n const wasOpen = useRef(isOpen);\n useLayoutEffect(() => {\n const closed = wasOpen.current && !isOpen;\n wasOpen.current = isOpen;\n const item = itemRef.current;\n if (!closed || !tree.sheet || !tree.rootState.isOpen || !item) return;\n const active = item.ownerDocument.activeElement;\n if (active == null || active === item.ownerDocument.body) item.focus();\n });\n return (\n <>\n <MenuItemView\n {...props}\n triggerProps={tree.sheet ? triggerProps : { ...triggerProps, onHoverChange }}\n itemRef={itemRef}\n submenuOpen={isOpen}\n />\n {tree.sheet ? (\n <SubmenuSheetPanel\n state={submenuState}\n submenuRef={submenuRef}\n menuProps={submenuProps}\n submenu={submenu!}\n title={props.node.rendered}\n />\n ) : (\n <SubmenuPopover\n state={submenuState}\n triggerRef={itemRef}\n submenuRef={submenuRef}\n menuProps={submenuProps}\n popoverProps={popoverProps}\n submenu={submenu!}\n />\n )}\n </>\n );\n}\n\n/**\n * A submenu in a sheet (M3 adapts menus into bottom sheets on small screens, where there's\n * no room beside an item): it takes the place of the menu that opened it, which the sheet\n * hides while it's open, below a back button and its item's label. ← (→ in right-to-left),\n * Escape and the back button go back. It's portalled into the sheet after its menu, so the\n * sheet's focus scope covers it.\n */\nfunction SubmenuSheetPanel({\n state,\n submenuRef,\n menuProps,\n submenu,\n title,\n}: Pick<SubmenuPopoverProps, 'state' | 'submenuRef' | 'menuProps' | 'submenu'> & {\n title: ReactNode;\n}) {\n const tree = useContext(MenuTreeContext)!;\n if (!state.isOpen || !tree.container) return null;\n const { submenuLevel, ...ariaMenuProps } = menuProps;\n const own = submenu.props;\n const variant = own.variant ?? tree.variant;\n const styles = menuStyles({ variant });\n return (\n <Overlay portalContainer={tree.container} disableFocusManagement>\n <div data-submenu-panel=\"\" className={styles.sheetPanel()}>\n <div className={styles.sheetHeader()}>\n <SheetBackButton\n label={tree.labels.back}\n onPress={() => state.close()}\n className={styles.sheetBack()}\n />\n <span className={styles.sheetTitle()}>{title}</span>\n </div>\n <MenuList\n {...mergeProps(ariaMenuProps, own)}\n listRef={submenuRef}\n layout=\"divider\"\n variant={variant}\n density={own.density ?? tree.density}\n level={submenuLevel + 1}\n embedded\n // However it opened (a touch leaves focus on the item, now hidden), focus moves in.\n autoFocus={state.focusStrategy ?? 'first'}\n />\n </div>\n </Overlay>\n );\n}\n\n/** The back button above a sheet's submenu: 48px, with an outline focus ring. */\nfunction SheetBackButton({\n label,\n onPress,\n className,\n}: {\n label: string;\n onPress: () => void;\n className: string;\n}) {\n const ref = useRef<HTMLButtonElement>(null);\n const { buttonProps, isPressed } = useButton({ onPress, 'aria-label': label }, ref);\n const { interactionProps, dataAttributes } = useM3Interaction({ isPressed }, ref);\n return (\n <button\n {...mergeProps(buttonProps, interactionProps)}\n {...dataAttributes}\n ref={ref}\n className={className}\n >\n <BackIcon />\n </button>\n );\n}\n\ninterface SubmenuPopoverProps {\n state: SubmenuTriggerState;\n triggerRef: RefObject<HTMLDivElement | null>;\n submenuRef: RefObject<HTMLDivElement | null>;\n menuProps: ReturnType<typeof useSubmenuTrigger>['submenuProps'];\n popoverProps: ReturnType<typeof useSubmenuTrigger>['popoverProps'];\n submenu: ReactElement<MenuProps>;\n}\n\nfunction SubmenuPopover(props: SubmenuPopoverProps) {\n const tree = useContext(MenuTreeContext)!;\n const presence = usePresence(props.state.isOpen);\n const isPresent = tree.instant ? props.state.isOpen : presence.isPresent;\n const isExiting = !tree.instant && presence.isExiting;\n const { exitProps } = presence;\n if (!isPresent || !tree.container) return null;\n // Inside the root popover: its focus scope and outside-press handling cover the submenu,\n // and the modal root menu doesn't make it inert.\n return (\n <Overlay portalContainer={tree.container} disableFocusManagement exiting={isExiting}>\n <SubmenuPopoverContent {...props} isExiting={isExiting} exitProps={exitProps} />\n </Overlay>\n );\n}\n\nfunction SubmenuPopoverContent({\n state,\n triggerRef,\n submenuRef,\n menuProps,\n popoverProps,\n submenu,\n isExiting,\n exitProps,\n}: SubmenuPopoverProps & { isExiting: boolean; exitProps: ExitProps }) {\n const tree = useContext(MenuTreeContext)!;\n const popoverRef = useRef<HTMLDivElement>(null);\n const { submenuLevel, ...ariaMenuProps } = menuProps;\n const { popoverProps: positionProps, placement } = usePopover(\n {\n ...popoverProps,\n // React Aria handles this as a submenu's popover (dismissable, non-modal).\n ...({ trigger: 'SubmenuTrigger' } as Partial<AriaPopoverProps>),\n triggerRef,\n popoverRef,\n placement: 'end top',\n // Beside the parent menu (the 4px padding around the item and a 2px gap), with its\n // first item level with the item that opened it.\n offset: 6,\n crossOffset: -4,\n },\n state,\n );\n const revealRef = useRef<HTMLDivElement>(null);\n const entering = useRevealEntering(placement, revealRef);\n const { className, style, ...own } = submenu.props;\n const styles = menuStyles();\n return (\n // `w-max` (`menuOwnWidth`): positioned inside the narrower root popover, it would otherwise shrink to fit\n // what's left of that popover's width and wrap every label. Like the root menu's, the\n // submenu's `className` and `style` go on this outermost element.\n <div\n {...positionProps}\n ref={popoverRef}\n style={{ ...style, ...positionProps.style }}\n data-overlay-placement=\"\"\n className={styles.popover({ class: cn(menuOwnWidth, className) })}\n >\n <div\n {...exitProps}\n ref={revealRef}\n data-entering={entering || undefined}\n data-exiting={isExiting || undefined}\n data-placement={placement ?? undefined}\n className={menuStyles({ instant: tree.instant }).reveal()}\n >\n <MenuList\n {...mergeProps(ariaMenuProps, own)}\n listRef={submenuRef}\n layout={own.layout ?? tree.layout}\n variant={own.variant ?? tree.variant}\n density={own.density ?? tree.density}\n level={submenuLevel + 1}\n />\n </div>\n </div>\n );\n}\n\nfunction MenuItemView({\n node,\n state,\n variant,\n top,\n bottom,\n classNames,\n triggerProps,\n itemRef,\n submenuOpen = false,\n}: MenuItemViewProps) {\n const ownRef = useRef<HTMLDivElement>(null);\n const ref = itemRef ?? ownRef;\n const props = node.props as SubmenuItemProps;\n const isDisabled = state.disabledKeys.has(node.key);\n // A plain action in a selectable menu (`checkable={false}`) is never checked.\n const selectable = state.selectionManager.selectionMode !== 'none' && props.checkable !== false;\n const plainInSelectable = state.selectionManager.selectionMode !== 'none' && !selectable;\n const multiple = selectable && state.selectionManager.selectionMode === 'multiple';\n const {\n menuItemProps: ariaItemProps,\n keyboardShortcutProps,\n isSelected,\n isPressed,\n } = useMenuItem(\n {\n ...triggerProps,\n key: node.key,\n 'aria-label': props['aria-label'],\n // Disabled items stay focusable (the menu's `disabledBehavior`) but do nothing.\n isDisabled,\n isSelected: plainInSelectable ? false : undefined,\n // M3: multi-select menus stay open until they're dismissed, Enter included; a plain\n // action closes the menu, as in any menu.\n closeOnSelect: multiple ? false : plainInSelectable ? true : undefined,\n },\n state,\n ref,\n );\n const menuItemProps =\n plainInSelectable && !props.submenu\n ? { ...ariaItemProps, role: 'menuitem', 'aria-checked': undefined }\n : ariaItemProps;\n const { interactionProps, dataAttributes } = useM3Interaction(\n { isPressed, isDisabled, isSelected },\n ref,\n );\n const styles = menuStyles({\n variant,\n itemTop: top,\n itemBottom: bottom,\n hasDescription: Boolean(props.description),\n });\n const selectedIcon = props.selectedIcon ?? (selectable ? <CheckIcon /> : null);\n const trailingIcon = props.trailingIcon ?? (props.submenu ? <SubmenuIcon /> : null);\n const badgeId = useId();\n // Named by its content: the label and supporting text in the order they show, each read\n // once. React Aria names it by the label alone and describes it by the supporting text,\n // which leaves visible text out of its name (WCAG 2.5.3), as List options did. The shortcut\n // and the badge are extra information, not the item's label: they stay its description,\n // hidden from the content so they're read once.\n const describedBy =\n [props.shortcut ? keyboardShortcutProps.id : undefined, props.badge ? badgeId : undefined]\n .filter(Boolean)\n .join(' ') || undefined;\n\n return (\n <div\n {...mergeProps(menuItemProps, interactionProps)}\n {...dataAttributes}\n aria-labelledby={undefined}\n aria-describedby={describedBy}\n data-menu-open={submenuOpen || undefined}\n // Each item resolves the menu's tokens, so its selection picks its colours.\n data-tokens={mergeDataTokens('md-comp-menus', menuItemProps)}\n data-variant={variant}\n data-slot=\"item\"\n ref={ref}\n className={styles.item({ class: cn(classNames?.item, props.className) })}\n >\n {props.leadingIcon && selectedIcon ? (\n <span\n aria-hidden=\"true\"\n data-slot=\"leading-icon\"\n className={styles.icon({ class: classNames?.icon })}\n >\n {isSelected ? selectedIcon : props.leadingIcon}\n </span>\n ) : props.leadingIcon ? (\n <span\n aria-hidden=\"true\"\n data-slot=\"leading-icon\"\n className={styles.icon({ class: classNames?.icon })}\n >\n {props.leadingIcon}\n </span>\n ) : selectedIcon ? (\n // No leading icon: the selected icon expands in when selected (Compose).\n <span aria-hidden=\"true\" className={styles.selectedIcon()}>\n <span className={styles.selectedIconInner()}>\n <span className={styles.icon({ class: classNames?.icon })}>{selectedIcon}</span>\n </span>\n </span>\n ) : null}\n <span className={styles.text()}>\n <span>{node.rendered}</span>\n {props.description && (\n <span className={styles.description({ class: classNames?.description })}>\n {props.description}\n </span>\n )}\n </span>\n {props.badge && (\n <span id={badgeId} aria-hidden=\"true\" className={styles.badge()}>\n {props.badge}\n </span>\n )}\n {props.shortcut && (\n <kbd\n {...keyboardShortcutProps}\n aria-hidden=\"true\"\n className={styles.trailing({ class: cn('font-plain', classNames?.shortcut) })}\n >\n {props.shortcut}\n </kbd>\n )}\n {trailingIcon && (\n <span\n aria-hidden=\"true\"\n data-slot=\"trailing-icon\"\n className={styles.trailing({ class: classNames?.trailingIcon })}\n >\n {trailingIcon}\n </span>\n )}\n </div>\n );\n}\n\nexport type { Key as MenuKey };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Menu.js","names":["Overlay"],"sources":["../../../src/components/menu/Menu.tsx"],"sourcesContent":["'use client';\n\nimport {\n cloneElement,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n useSyncExternalStore,\n type CSSProperties,\n type ElementType,\n type FocusEvent,\n type JSX,\n type Key,\n type ReactElement,\n type ReactNode,\n type Ref,\n type RefObject,\n} from 'react';\nimport {\n DismissButton,\n I18nProvider,\n mergeProps,\n useLocale,\n useMenu,\n useMenuItem,\n useMenuSection,\n useObjectRef,\n usePopover,\n useButton,\n useSubmenuTrigger,\n type AriaMenuItemProps,\n type AriaMenuOptions,\n type AriaMenuProps,\n type AriaPopoverProps,\n type Placement,\n} from 'react-aria';\nimport {\n Item,\n Section,\n useSubmenuTriggerState,\n useTreeState,\n type Node,\n type RootMenuTriggerState,\n type SubmenuTriggerState,\n type TreeState,\n} from 'react-stately';\nimport { localeWithDirection } from '../../primitives/locale-with-direction';\nimport { Overlay } from '../../primitives/Overlay';\nimport { TriggerContext } from '../../primitives/TriggerContext';\nimport { useM3Interaction } from '../../primitives/use-m3-interaction';\nimport { usePresence } from '../../primitives/use-presence';\nimport { densityAttribute, type Density } from '../../tokens/density';\nimport { toAriaPlacement } from '../../utils/placement';\nimport { assertCollectionChildren } from '../../utils/assert-collection-children';\nimport { cn } from '../../utils/cn';\nimport { useTypeaheadDelegate } from '../../utils/typeahead';\nimport { COMPACT_WINDOW, useMediaQuery } from '../date-picker/use-media-query';\nimport { Divider } from '../divider/Divider';\nimport { LazyBottomSheet, usePreloadBottomSheet } from '../sheet/lazy-bottom-sheet';\nimport { BackIcon } from '../sheet/sheet-icons';\nimport { useRevealEntering } from './use-reveal-entering';\nimport { menuOwnWidth, menuStyles, type MenuLayout, type MenuVariant } from './menu-styles';\nimport { MenuTriggerContext, type MenuTriggerContextValue } from './menu-trigger';\n\nexport {\n ContextMenuTrigger,\n MenuTrigger,\n type ContextMenuTriggerProps,\n type MenuTriggerProps,\n} from './menu-trigger';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\nimport { splitRootProps, type RootDOMProps } from '../../utils/root-props';\n\nconst CheckIcon = () => (\n <svg viewBox=\"0 -960 960 960\" fill=\"currentColor\">\n <path d=\"M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z\" />\n </svg>\n);\n\n/** Clears the link attributes `useMenuItem` copies from an item's props onto one that isn't a link. */\nconst NOT_A_LINK = {\n href: undefined,\n target: undefined,\n rel: undefined,\n download: undefined,\n ping: undefined,\n referrerPolicy: undefined,\n};\n\n/** The default submenu arrow (Material Symbols `arrow_right`), mirrored in RTL. */\nconst SubmenuIcon = () => (\n <svg viewBox=\"0 -960 960 960\" fill=\"currentColor\" className=\"rtl:-scale-x-100\">\n <path d=\"M400-280v-400l200 200-200 200Z\" />\n </svg>\n);\n\n/** Props of a {@link MenuItem}; the React `key` identifies it for `onAction` and selection. */\nexport interface MenuItemProps {\n /** The label. */\n children: ReactNode;\n /** Plain-text label for typeahead when `children` is not a string. */\n textValue?: string;\n /** Icon before the label. */\n leadingIcon?: ReactNode;\n /** Icon shown while selected (defaults to a check in selectable menus). */\n selectedIcon?: ReactNode;\n /** Second line of text (supporting text), read as part of the item's name after the label. */\n description?: ReactNode;\n /**\n * A badge before the trailing text or icon (M3 anatomy), such as a `Badge` with a count.\n * It's read after the name, as the item's description.\n */\n badge?: ReactNode;\n /** Keyboard shortcut shown at the end, e.g. \"⌘C\", read as the item's description. */\n shortcut?: string;\n /** Icon at the end (an item with a submenu shows an arrow by default). */\n trailingIcon?: ReactNode;\n /**\n * Makes the item a link: a real `<a>` with the item's look, role and keys, so the browser's\n * link menu (open in a new tab, copy or save the link) and modified clicks work. A press,\n * Enter or Space follows it, through a `RouterProvider` (such as `NextRouterProvider`) on a\n * plain click, and closes the menu. A disabled item, or one that opens a submenu, isn't a link.\n */\n href?: string;\n /** A link item's target; `_blank` adds `rel=\"noopener noreferrer\"` unless `rel` is given. */\n target?: string;\n /** A link item's `rel`, e.g. `\"nofollow\"`. */\n rel?: string;\n /** Downloads the link item's file instead of opening it (optionally with a file name). */\n download?: boolean | string;\n /** Accessible name when the label isn't plain text. */\n 'aria-label'?: string;\n /** Classes for the item, merged after `classNames.item`; consumer classes win. */\n className?: string;\n}\n\n/** An item in a `Menu` (a React Stately collection item: identify it with `key`). */\nexport const MenuItem = Item as unknown as (props: MenuItemProps) => JSX.Element;\n\nexport interface MenuGroupProps {\n /** Group label shown above its items. */\n title?: ReactNode;\n /** Accessible name for the group when it has no `title`. */\n 'aria-label'?: string;\n /** `MenuItem`s and `SubmenuTrigger`s. */\n children: ReactElement | ReactElement[];\n}\n\n/**\n * A group of items (a React Stately collection section): set off by dividers in the\n * `divider` layout, its own surface in the `gap` layout.\n */\nexport const MenuGroup = Section as unknown as (props: MenuGroupProps) => JSX.Element;\n\nexport interface SubmenuTriggerProps {\n /** The `MenuItem` that opens the submenu, then the submenu's `Menu`. */\n children: [ReactElement<MenuItemProps>, ReactElement<MenuProps>];\n /** How long (ms) the pointer rests on the item before the submenu opens. @default 200 */\n delay?: number;\n}\n\n/** What a `SubmenuTrigger` adds to its item's collection node. */\ninterface SubmenuItemProps extends MenuItemProps {\n submenu?: ReactElement<MenuProps>;\n submenuDelay?: number;\n /** See {@link CheckableMenuItemProps}. */\n checkable?: boolean;\n}\n\n/**\n * Props of a {@link CheckableMenuItem}, for the library's own menus (not exported from the\n * package): in a selectable menu, `checkable={false}` makes an item a plain action, with the\n * `menuitem` role, no checked state and no check, that closes the menu like any action.\n */\nexport interface CheckableMenuItemProps extends MenuItemProps {\n /** Whether the item can be checked in a selectable menu. @default true */\n checkable?: boolean;\n}\n\n/** A {@link MenuItem} that can opt out of the menu's selection (library-internal). */\nexport const CheckableMenuItem = Item as unknown as (props: CheckableMenuItemProps) => JSX.Element;\n\n/** Only a menu collection reads a `SubmenuTrigger`; rendered on its own it's nothing. */\nfunction SubmenuTriggerElement(): null {\n return null;\n}\n\n/** React Stately builds the item's node from this, with the submenu on its props. */\nSubmenuTriggerElement.getCollectionNode = function* getCollectionNode({\n children: [item, submenu],\n delay,\n}: SubmenuTriggerProps) {\n // `cloneElement` keeps the item's own `key`.\n yield { element: cloneElement(item, { submenu, submenuDelay: delay } as SubmenuItemProps) };\n};\n\n/**\n * Gives a menu item a submenu (M3: \"On web, menus can open submenus\"). It opens beside the\n * item, without covering it, on hover, press, Enter, Space or the arrow key pointing away\n * from the menu (Left in right-to-left), and closes with the opposite arrow or Escape.\n * While it's open, the item shows its active (pressed) layer, and the menu holding focus\n * takes rounder corners than the other one.\n *\n * @example\n * <SubmenuTrigger>\n * <MenuItem key=\"share\">Share</MenuItem>\n * <Menu onAction={(key) => share(key)}>\n * <MenuItem key=\"email\">Email</MenuItem>\n * <MenuItem key=\"link\">Copy link</MenuItem>\n * </Menu>\n * </SubmenuTrigger>\n */\nexport const SubmenuTrigger = SubmenuTriggerElement as unknown as (\n props: SubmenuTriggerProps,\n) => JSX.Element;\n\nexport interface MenuClassNames {\n /**\n * The menu's surface (the `menu` element): its colour, corners, size and the list that\n * scrolls. In a sheet it spans the sheet, without a surface of its own.\n */\n panel?: string;\n /**\n * The bottom sheet the menu opens in (`presentation=\"sheet\"`, or `\"auto\"` on compact\n * windows): the sheet's surface, as its `classNames.panel`.\n */\n sheet?: string;\n /** Each group of items (its own surface in the `gap` layout). */\n group?: string;\n /** Each `MenuGroup`'s heading. */\n heading?: string;\n /** Each item. */\n item?: string;\n /** Each item's leading icon, or the icon marking a selected item. */\n icon?: string;\n /** Each item's `description` line. */\n description?: string;\n /** Each item's keyboard `shortcut`. */\n shortcut?: string;\n /** Each item's trailing icon, including a submenu's arrow. */\n trailingIcon?: string;\n /** The dividers between groups in the `divider` layout. */\n divider?: string;\n}\n\nexport interface MenuProps\n extends\n Omit<\n AriaMenuProps<object>,\n | 'children'\n | 'items'\n | 'onClose'\n | 'autoFocus'\n | 'disallowEmptySelection'\n // The arrow keys always wrap and Escape always closes the menu, as M3's menus do.\n | 'shouldFocusWrap'\n | 'escapeKeyBehavior'\n >,\n Omit<RootDOMProps, keyof AriaMenuProps<object>> {\n /**\n * With `selectionMode`: the last selected item can be deselected, leaving none. `false` keeps\n * at least one item selected. @default true\n */\n deselectable?: boolean;\n /**\n * The keys of items that can't be chosen. They stay focusable, as M3 asks (\"Disabled menu\n * items can receive focus but aren't selectable\").\n */\n disabledKeys?: AriaMenuProps<object>['disabledKeys'];\n /** `MenuItem`s, `MenuGroup`s and `SubmenuTrigger`s. */\n children: ReactNode;\n /** `standard` (surface colours) or `vibrant` (tertiary container). @default \"standard\" */\n variant?: MenuVariant;\n /**\n * How groups are set apart. `divider`: one surface with dividers between groups, which M3\n * recommends on the web and for menus that scroll. `gap`: each group is its own surface,\n * 2px apart; a `gap` menu that has to scroll switches to dividers, since M3 doesn't use\n * gaps in scrolling menus. A submenu takes its parent's layout and variant unless it\n * sets its own.\n * @default \"divider\"\n */\n layout?: MenuLayout;\n /**\n * Web density (M3: \"On web only … Increasing density decreases the top and bottom\n * padding\"): each step down takes 4px off the items' height (44px at 0, 32px at -3) and 2px\n * off the space around dividers. Left out, the menu takes the density of the nearest\n * `ThemeProvider` or `ThemeScope`; a submenu takes its parent menu's. Keep 0 for touch.\n * @default 0\n */\n density?: Density;\n /**\n * Placement relative to the trigger: a side, then optionally the edge to align with\n * (`start` / `end` follow the text direction). A submenu always opens beside its item.\n * @default \"bottom-start\"\n */\n placement?: MenuPlacement;\n /** Gap between the trigger and the menu, in px (a split button's menu sits 4px away). @default 0 */\n offset?: number;\n /**\n * The least space kept between the menu and the window's edges, in px. A split button\n * passes 0, so its menu lines up with the trailing button even at the edge of the screen.\n * @default 12\n */\n containerPadding?: number;\n /**\n * Where the menu opens. `popover`: anchored to its trigger. `sheet`: in a modal bottom\n * sheet. `auto`: a sheet on compact windows (below 600px wide) and a popover on larger\n * ones (M3: \"Consider adapting menus into bottom sheets on small screens\"). Set it on the\n * root menu.\n * @default \"popover\"\n */\n presentation?: MenuPresentation;\n /** Words the menu shows in a sheet. Set them on the root menu. */\n labels?: Partial<MenuLabels>;\n /**\n * Open and close with no motion (M3: \"In dense products, such as on desktop, menus can\n * open instantly to reduce motion\"). Submenus follow their root menu. Popovers only: a menu\n * shown as a bottom sheet (`presentation=\"sheet\"`, or `auto` on a compact window) keeps the\n * sheet's slide, as M3's and Compose's modal bottom sheets always animate; under reduced\n * motion it fades instead.\n * @default false\n */\n instant?: boolean;\n /**\n * Goes with `style` on the menu's outermost element: the box React Aria places against the\n * trigger, or the bottom sheet's outermost element in a sheet. The surface inside keeps\n * its size and its place against the trigger whatever layout class lands here (the box\n * keeps the menu's own width); style it with `classNames.panel`.\n */\n className?: string;\n /** Classes for each part, by slot. */\n classNames?: MenuClassNames;\n /** Goes on the menu's outermost element, with `className`. */\n style?: CSSProperties;\n /** Ref to the menu's list, the `role=\"menu\"` element, in a popover or a sheet. */\n ref?: Ref<HTMLDivElement>;\n}\n\n/** Where a {@link Menu} sits against its trigger: a side, then optionally the edge it aligns with. */\nexport type MenuPlacement =\n | 'top'\n | 'bottom'\n | 'start'\n | 'end'\n | 'top-start'\n | 'top-end'\n | 'bottom-start'\n | 'bottom-end'\n | 'start-top'\n | 'start-bottom'\n | 'end-top'\n | 'end-bottom';\n\n/** Where a {@link Menu} opens. */\nexport type MenuPresentation = 'popover' | 'sheet' | 'auto';\n\n/** Words a {@link Menu} shows. */\nexport interface MenuLabels {\n /**\n * In a sheet, the button above a submenu that goes back to the menu that opened it.\n * @default \"Back\"\n */\n back: string;\n}\n\nconst DEFAULT_LABELS: MenuLabels = { back: 'Back' };\n\n/**\n * M3 Expressive menu: items on one surface with dividers between groups (or on grouped\n * surfaces), standard or vibrant colours, optional single or multiple selection with a\n * selected shape and check, submenus, keyboard navigation and typeahead (React Aria\n * `useMenu`). Must be inside a `MenuTrigger` or `ContextMenuTrigger`; it opens anchored to\n * the trigger in a portal that keeps the surrounding theme and direction.\n *\n * @example\n * <MenuTrigger>\n * <IconButton icon={<MoreIcon />} aria-label=\"More\" />\n * <Menu onAction={(key) => run(key)}>\n * <MenuGroup>\n * <MenuItem key=\"copy\" shortcut=\"⌘C\">Copy</MenuItem>\n * <MenuItem key=\"paste\" shortcut=\"⌘V\">Paste</MenuItem>\n * </MenuGroup>\n * <MenuGroup>\n * <MenuItem key=\"delete\">Delete</MenuItem>\n * </MenuGroup>\n * </Menu>\n * </MenuTrigger>\n */\nexport function Menu({ presentation = 'popover', instant = false, ...props }: MenuProps) {\n const trigger = useContext(MenuTriggerContext);\n if (!trigger) throw new Error('[@windmorph/react] <Menu> must be used inside <MenuTrigger>.');\n const compact = useMediaQuery(COMPACT_WINDOW);\n const presence = usePresence(trigger.state.isOpen);\n const asSheet = presentation === 'sheet' || (presentation === 'auto' && compact);\n usePreloadBottomSheet(trigger.triggerRef, asSheet);\n if (asSheet) return <MenuSheet {...props} trigger={trigger} />;\n // An instant menu leaves at once, without waiting for an exit transition.\n const isPresent = instant ? trigger.state.isOpen : presence.isPresent;\n const isExiting = !instant && presence.isExiting;\n if (!isPresent) return null;\n return (\n <Overlay exiting={isExiting}>\n <MenuPopover\n {...props}\n trigger={trigger}\n instant={instant}\n isExiting={isExiting}\n exitProps={presence.exitProps}\n />\n </Overlay>\n );\n}\n\n/**\n * The menu in a modal bottom sheet (M3: menus can adapt into bottom sheets on small\n * screens). The sheet is a dialog named like the menu; the menu inside keeps its role,\n * keyboard, typeahead and initial focus, and spans the sheet without a surface of its own.\n * The sheet's code loads on first open (or when the trigger is hovered or focused), so a\n * menu that only opens as a popover doesn't carry it.\n */\nfunction MenuSheet({\n trigger,\n layout: _layout,\n placement: _placement,\n offset: _offset,\n variant = 'standard',\n labels,\n className,\n style,\n ...props\n}: Omit<MenuProps, 'presentation' | 'instant'> & { trigger: MenuTriggerContextValue }) {\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n const [activeLevel, setActiveLevel] = useState(0);\n const menuProps = mergeProps(trigger.menuProps, props);\n const label = menuProps['aria-label'];\n const naming =\n label != null\n ? { 'aria-label': label }\n : { 'aria-labelledby': menuProps['aria-labelledby'] ?? '' };\n return (\n <LazyBottomSheet\n {...naming}\n // A menu sheet opens at its own height (up to the window, the list scrolling inside):\n // at half the window, a short phone left its last items off-screen, where the arrow\n // keys could still move focus.\n skipPartiallyExpanded\n open={trigger.state.isOpen}\n onOpenChange={(open) => (open ? trigger.state.open() : trigger.state.close())}\n className={className}\n style={style}\n classNames={props.classNames?.sheet ? { panel: props.classNames.sheet } : undefined}\n >\n <div ref={setContainer} className={menuStyles().sheetContent()}>\n <TriggerContext value={null}>\n <MenuTreeContext\n value={{\n rootState: trigger.state,\n container,\n activeLevel,\n setActiveLevel,\n layout: 'divider',\n variant,\n density: props.density,\n instant: false,\n sheet: true,\n labels: { ...DEFAULT_LABELS, ...labels },\n }}\n >\n <MenuList\n {...menuProps}\n layout=\"divider\"\n variant={variant}\n level={0}\n embedded\n onClose={trigger.state.close}\n autoFocus={trigger.state.focusStrategy ?? 'first'}\n />\n </MenuTreeContext>\n </TriggerContext>\n </div>\n </LazyBottomSheet>\n );\n}\n\n/** Shared by a menu and its submenus. */\ninterface MenuTreeContextValue {\n rootState: RootMenuTriggerState;\n /** The root popover, which submenus portal into (inside its focus scope, never inert). */\n container: HTMLElement | null;\n /** The level of the menu holding focus (0 is the root menu). */\n activeLevel: number;\n setActiveLevel: (level: number) => void;\n /** What submenus inherit. */\n layout: MenuLayout;\n variant: MenuVariant;\n density: MenuProps['density'];\n /** Submenus open and close with no motion too. */\n instant: boolean;\n /**\n * In a sheet, a submenu takes the sheet's place of the menu that opened it, below a back\n * button, instead of opening beside its item.\n */\n sheet: boolean;\n labels: MenuLabels;\n}\n\nconst MenuTreeContext = createContext<MenuTreeContextValue | null>(null);\n\n/** The menu element an item is rendered in (a submenu's parent). */\nconst MenuListContext = createContext<RefObject<HTMLDivElement | null> | null>(null);\n\nconst subscribeToNothing = () => () => {};\n\n/**\n * React Aria resolves `start` / `end` from its locale context, not the DOM `dir`, so an\n * RTL region without an `I18nProvider` would align the menu to the wrong edge. Resolve\n * them to physical sides from the trigger's computed direction instead.\n */\nfunction toPhysicalPlacement(placement: MenuPlacement, direction: string): Placement {\n return toAriaPlacement(placement, direction === 'rtl');\n}\n\ntype ExitProps = ReturnType<typeof usePresence>['exitProps'];\n\nfunction MenuPopover({\n trigger,\n instant,\n isExiting,\n exitProps,\n placement = 'bottom-start',\n offset = 0,\n containerPadding = 12,\n layout = 'divider',\n variant = 'standard',\n labels,\n className,\n style,\n ...props\n}: Omit<MenuProps, 'presentation' | 'instant'> & {\n trigger: MenuTriggerContextValue;\n instant: boolean;\n isExiting: boolean;\n exitProps: ExitProps;\n}) {\n const popoverRef = useRef<HTMLDivElement>(null);\n const [container, setContainer] = useState<HTMLDivElement | null>(null);\n const setPopover = useCallback((element: HTMLDivElement | null) => {\n popoverRef.current = element;\n setContainer(element);\n }, []);\n const [activeLevel, setActiveLevel] = useState(0);\n // The DOM is the source of truth for direction; read it like an external store.\n const direction = useSyncExternalStore(\n subscribeToNothing,\n () =>\n trigger.triggerRef.current ? getComputedStyle(trigger.triggerRef.current).direction : 'ltr',\n () => 'ltr',\n );\n // Submenus' arrow keys and placement follow the locale's direction: give it the DOM's.\n const { locale, direction: localeDirection } = useLocale();\n const menuLocale =\n direction === localeDirection\n ? locale\n : localeWithDirection(locale, direction === 'rtl' ? 'rtl' : 'ltr');\n const {\n popoverProps,\n underlayProps,\n placement: actualPlacement,\n } = usePopover(\n {\n triggerRef: trigger.triggerRef,\n popoverRef,\n placement: toPhysicalPlacement(placement, direction),\n offset,\n containerPadding,\n },\n trigger.state,\n );\n const revealRef = useRef<HTMLDivElement>(null);\n const entering = useRevealEntering(actualPlacement, revealRef);\n const close = trigger.state.close;\n // Tab moves on from an open menu and closes it and its submenus (the APG menu button\n // pattern). React Aria's focus scope moves focus past the trigger, or back to it when\n // nothing follows; without the close, the menu stayed open with focus outside it. That\n // scope handles Tab on the document's capture phase and stops it, so this listens on the\n // window's, which comes first.\n useEffect(() => {\n const view = container?.ownerDocument.defaultView;\n if (!container || !view) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Tab' || event.altKey || event.ctrlKey || event.metaKey) return;\n if (container.contains(container.ownerDocument.activeElement)) close();\n };\n view.addEventListener('keydown', onKeyDown, true);\n return () => view.removeEventListener('keydown', onKeyDown, true);\n }, [container, close]);\n const styles = menuStyles();\n return (\n <>\n <div {...underlayProps} className=\"fixed inset-0\" />\n <div\n {...popoverProps}\n ref={setPopover}\n // React Aria's placement wins over a consumer style's and, through the attribute\n // (styles/utilities.css), over consumer inset classes.\n style={{ ...style, ...popoverProps.style }}\n data-overlay-placement=\"\"\n className={styles.popover({ class: cn(menuOwnWidth, className) })}\n >\n <DismissButton onDismiss={trigger.state.close} />\n <div\n {...exitProps}\n ref={revealRef}\n data-entering={entering || undefined}\n data-exiting={isExiting || undefined}\n data-placement={actualPlacement ?? undefined}\n className={menuStyles({ instant }).reveal()}\n >\n <TriggerContext value={null}>\n <I18nProvider locale={menuLocale}>\n <MenuTreeContext\n value={{\n rootState: trigger.state,\n container,\n activeLevel,\n setActiveLevel,\n layout,\n variant,\n density: props.density,\n instant,\n sheet: false,\n labels: { ...DEFAULT_LABELS, ...labels },\n }}\n >\n <MenuList\n {...mergeProps(trigger.menuProps, props)}\n layout={layout}\n variant={variant}\n level={0}\n onClose={trigger.state.close}\n autoFocus={trigger.state.focusStrategy ?? 'first'}\n />\n </MenuTreeContext>\n </I18nProvider>\n </TriggerContext>\n </div>\n <DismissButton onDismiss={trigger.state.close} />\n </div>\n </>\n );\n}\n\ntype Position = 'only' | 'first' | 'middle' | 'last';\ntype Edges = { top: boolean; bottom: boolean };\n\nconst positionOf = (index: number, count: number): Position =>\n count === 1 ? 'only' : index === 0 ? 'first' : index === count - 1 ? 'last' : 'middle';\n\n/** Which of a group's edges have the outer corners (the others have 8px). */\nconst roundEdges = (position: Position): Edges => ({\n top: position === 'only' || position === 'first',\n bottom: position === 'only' || position === 'last',\n});\n\n/** Sections become groups; runs of loose items between them form implicit groups. */\nfunction toGroups(nodes: Node<object>[]): (Node<object> | Node<object>[])[] {\n const groups: (Node<object> | Node<object>[])[] = [];\n let run: Node<object>[] = [];\n for (const node of nodes) {\n if (node.type === 'section') {\n if (run.length) groups.push(run);\n run = [];\n groups.push(node);\n } else {\n run.push(node);\n }\n }\n if (run.length) groups.push(run);\n return groups;\n}\n\ntype MenuListProps = Omit<\n MenuProps,\n 'placement' | 'layout' | 'variant' | 'presentation' | 'instant' | 'className' | 'style'\n> &\n Pick<AriaMenuOptions<object>, 'onClose' | 'autoFocus' | 'onKeyDown' | 'onKeyUp'> & {\n layout: MenuLayout;\n variant: MenuVariant;\n /** 0 for the root menu, then one more per submenu. */\n level: number;\n listRef?: RefObject<HTMLDivElement | null>;\n /** In a sheet: the list spans it, with no surface of its own. */\n embedded?: boolean;\n };\n\n/**\n * Whether the list has had to scroll, from a `ResizeObserver` (so it's measured only when\n * its size changes). Once true it stays true while the menu is open, so a `gap` menu that\n * switches to dividers can't flip back and forth.\n */\nfunction useHasScrolled(ref: RefObject<HTMLElement | null>, enabled: boolean) {\n const [scrolls, setScrolls] = useState(false);\n useLayoutEffect(() => {\n const element = ref.current;\n if (!enabled || scrolls || !element || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(() => {\n if (element.scrollHeight > element.clientHeight + 1) setScrolls(true);\n });\n observer.observe(element);\n return () => observer.disconnect();\n }, [ref, enabled, scrolls]);\n return scrolls;\n}\n\nfunction MenuList({\n deselectable = true,\n variant,\n layout: requestedLayout,\n density,\n level,\n classNames,\n listRef,\n embedded = false,\n ref: forwardedRef,\n ...allProps\n}: MenuListProps) {\n // The menu list is the menu's root (its `ref` and `id` land there): its DOM attributes too.\n const { root, rest: props } = splitRootProps(allProps);\n // MenuItem / MenuGroup are React Stately Item / Section, so children are a collection.\n assertCollectionChildren(\n props.children,\n 'Menu',\n 'MenuItem, MenuGroup and SubmenuTrigger elements',\n );\n const tree = useContext(MenuTreeContext);\n // M3: \"Disabled menu items can receive focus but aren't selectable.\"\n const state = useTreeState({\n ...(props as AriaMenuProps<object>),\n disallowEmptySelection: !deselectable,\n disabledBehavior: 'selection',\n });\n const ownRef = useObjectRef(forwardedRef);\n const ref = listRef ?? ownRef;\n // M3 keyboard table: \"Letters — Focus moves to the next menu item starting with letter\".\n const keyboardDelegate = useTypeaheadDelegate(state, ref);\n const { menuProps } = useMenu(\n // The arrow keys wrap from the last item to the first and back, as M3's menus and the rest\n // of the library's collections do (APG makes it optional).\n { ...(props as AriaMenuOptions<object>), keyboardDelegate, shouldFocusWrap: true },\n state,\n ref,\n );\n const groups = toGroups([...state.collection]);\n const scrolled = useHasScrolled(ref, requestedLayout === 'gap');\n const layout: MenuLayout = scrolled ? 'divider' : requestedLayout;\n const styles = menuStyles({ variant, layout, embedded });\n // While a submenu is open, the menu holding focus is rounder than the others (M3).\n const submenuOpen = Boolean(tree && tree.rootState.expandedKeysStack.length > 0);\n const shape = !submenuOpen ? undefined : tree?.activeLevel === level ? 'active' : 'inactive';\n const setActive = tree?.setActiveLevel;\n\n return (\n <div\n {...mergeProps(root, menuProps, {\n // Focus events bubble through portals: count only focus inside this menu's element.\n onFocus: (event: FocusEvent<HTMLDivElement>) => {\n if (event.currentTarget.contains(event.target)) setActive?.(level);\n },\n })}\n ref={ref}\n {...densityAttribute(density)}\n data-tokens={mergeDataTokens('md-comp-menus', root)}\n data-variant={variant}\n data-layout={layout}\n data-shape={shape}\n className={styles.menu({ class: classNames?.panel })}\n >\n <MenuListContext value={ref}>\n {groups.map((group, index) => {\n const position = positionOf(index, groups.length);\n // In the divider layout the menu is the surface: its first and last items nest in\n // its corners, and dividers go between groups.\n const edges: Edges =\n layout === 'divider'\n ? { top: index === 0, bottom: index === groups.length - 1 }\n : roundEdges(position);\n const groupStyles = menuStyles({ variant, layout, groupPosition: position, morph: true });\n return (\n <GroupWithDivider\n key={Array.isArray(group) ? group[0]!.key : group.key}\n divider={\n layout === 'divider' && index > 0 ? (\n // A menu's divider is a `separator` between its groups (WAI-ARIA menu).\n <Divider\n decorative={false}\n className={styles.divider({ class: classNames?.divider })}\n />\n ) : null\n }\n >\n {Array.isArray(group) ? (\n <div\n role=\"presentation\"\n className={groupStyles.group({ class: classNames?.group })}\n >\n <MenuItems\n nodes={group}\n state={state}\n variant={variant}\n edges={edges}\n classNames={classNames}\n />\n </div>\n ) : (\n <MenuSection\n section={group}\n state={state}\n variant={variant}\n className={groupStyles.group({ class: classNames?.group })}\n headingClassName={groupStyles.heading({ class: classNames?.heading })}\n edges={edges}\n classNames={classNames}\n />\n )}\n </GroupWithDivider>\n );\n })}\n </MenuListContext>\n </div>\n );\n}\n\nfunction GroupWithDivider({ divider, children }: { divider: ReactNode; children: ReactNode }) {\n return (\n <>\n {divider}\n {children}\n </>\n );\n}\n\nfunction MenuSection({\n section,\n state,\n variant,\n className,\n headingClassName,\n edges,\n classNames,\n}: {\n section: Node<object>;\n state: TreeState<object>;\n variant: MenuVariant;\n className: string;\n headingClassName: string;\n edges: Edges;\n classNames?: MenuClassNames;\n}) {\n const { itemProps, headingProps, groupProps } = useMenuSection({\n heading: section.rendered,\n 'aria-label': section['aria-label'],\n });\n return (\n <div {...itemProps} className={className}>\n {section.rendered && (\n <span {...headingProps} className={headingClassName}>\n {section.rendered}\n </span>\n )}\n <div {...groupProps} className=\"flex flex-col\">\n <MenuItems\n nodes={[...section.childNodes]}\n state={state}\n variant={variant}\n // A heading takes the group's top edge, so the first item doesn't touch it.\n edges={{ ...edges, top: !section.rendered && edges.top }}\n classNames={classNames}\n />\n </div>\n </div>\n );\n}\n\n/**\n * Items nest their corners inside the container's: an item on an outer edge gets the\n * outer corner less the 4px padding, and every other corner is 4px.\n */\nfunction MenuItems({\n nodes,\n state,\n variant,\n edges,\n classNames,\n}: {\n nodes: Node<object>[];\n state: TreeState<object>;\n variant: MenuVariant;\n edges: Edges;\n classNames?: MenuClassNames;\n}) {\n return nodes.map((node, index) => {\n const itemProps: MenuItemViewProps = {\n node,\n state,\n variant,\n top: index === 0 && edges.top ? 'outer' : 'plain',\n bottom: index === nodes.length - 1 && edges.bottom ? 'outer' : 'plain',\n classNames,\n };\n return (node.props as SubmenuItemProps).submenu ? (\n <SubmenuItemView key={node.key} {...itemProps} />\n ) : (\n <MenuItemView key={node.key} {...itemProps} />\n );\n });\n}\n\ninterface MenuItemViewProps {\n node: Node<object>;\n state: TreeState<object>;\n variant: MenuVariant;\n top: 'outer' | 'plain';\n bottom: 'outer' | 'plain';\n classNames?: MenuClassNames;\n /** A submenu trigger's props (from `useSubmenuTrigger`) and ref. */\n triggerProps?: Partial<AriaMenuItemProps>;\n itemRef?: RefObject<HTMLDivElement | null>;\n /** Whether the item's submenu is open (its active state). */\n submenuOpen?: boolean;\n}\n\n/** An item that opens a submenu, and the submenu's popover. */\nfunction SubmenuItemView(props: MenuItemViewProps) {\n const tree = useContext(MenuTreeContext)!;\n const parentMenuRef = useContext(MenuListContext)!;\n const { submenu, submenuDelay } = props.node.props as SubmenuItemProps;\n const submenuState = useSubmenuTriggerState({ triggerKey: props.node.key }, tree.rootState);\n const itemRef = useRef<HTMLDivElement>(null);\n const submenuRef = useRef<HTMLDivElement>(null);\n const { submenuTriggerProps, submenuProps, popoverProps } = useSubmenuTrigger(\n {\n parentMenuRef,\n submenuRef,\n delay: submenuDelay,\n isDisabled: props.state.disabledKeys.has(props.node.key),\n },\n submenuState,\n itemRef,\n );\n // In a sheet a submenu replaces its menu, so hovering an item doesn't open it.\n const { isOpen, onHoverChange, ...triggerProps } = submenuTriggerProps;\n // A sheet's submenu hides the item that opened it, so React Aria's focus on the item when\n // the submenu closes (← or Escape) finds it hidden. Once the menu shows again, the item\n // takes focus back from the closed submenu (or the back button), unless every menu closed.\n const wasOpen = useRef(isOpen);\n useLayoutEffect(() => {\n const closed = wasOpen.current && !isOpen;\n wasOpen.current = isOpen;\n const item = itemRef.current;\n if (!closed || !tree.sheet || !tree.rootState.isOpen || !item) return;\n const active = item.ownerDocument.activeElement;\n if (active == null || active === item.ownerDocument.body) item.focus();\n });\n return (\n <>\n <MenuItemView\n {...props}\n triggerProps={tree.sheet ? triggerProps : { ...triggerProps, onHoverChange }}\n itemRef={itemRef}\n submenuOpen={isOpen}\n />\n {tree.sheet ? (\n <SubmenuSheetPanel\n state={submenuState}\n submenuRef={submenuRef}\n menuProps={submenuProps}\n submenu={submenu!}\n title={props.node.rendered}\n />\n ) : (\n <SubmenuPopover\n state={submenuState}\n triggerRef={itemRef}\n submenuRef={submenuRef}\n menuProps={submenuProps}\n popoverProps={popoverProps}\n submenu={submenu!}\n />\n )}\n </>\n );\n}\n\n/**\n * A submenu in a sheet (M3 adapts menus into bottom sheets on small screens, where there's\n * no room beside an item): it takes the place of the menu that opened it, which the sheet\n * hides while it's open, below a back button and its item's label. ← (→ in right-to-left),\n * Escape and the back button go back. It's portalled into the sheet after its menu, so the\n * sheet's focus scope covers it.\n */\nfunction SubmenuSheetPanel({\n state,\n submenuRef,\n menuProps,\n submenu,\n title,\n}: Pick<SubmenuPopoverProps, 'state' | 'submenuRef' | 'menuProps' | 'submenu'> & {\n title: ReactNode;\n}) {\n const tree = useContext(MenuTreeContext)!;\n if (!state.isOpen || !tree.container) return null;\n const { submenuLevel, ...ariaMenuProps } = menuProps;\n const own = submenu.props;\n const variant = own.variant ?? tree.variant;\n const styles = menuStyles({ variant });\n return (\n <Overlay portalContainer={tree.container} disableFocusManagement>\n <div data-submenu-panel=\"\" className={styles.sheetPanel()}>\n <div className={styles.sheetHeader()}>\n <SheetBackButton\n label={tree.labels.back}\n onPress={() => state.close()}\n className={styles.sheetBack()}\n />\n <span className={styles.sheetTitle()}>{title}</span>\n </div>\n <MenuList\n {...mergeProps(ariaMenuProps, own)}\n listRef={submenuRef}\n layout=\"divider\"\n variant={variant}\n density={own.density ?? tree.density}\n level={submenuLevel + 1}\n embedded\n // However it opened (a touch leaves focus on the item, now hidden), focus moves in.\n autoFocus={state.focusStrategy ?? 'first'}\n />\n </div>\n </Overlay>\n );\n}\n\n/** The back button above a sheet's submenu: 48px, with an outline focus ring. */\nfunction SheetBackButton({\n label,\n onPress,\n className,\n}: {\n label: string;\n onPress: () => void;\n className: string;\n}) {\n const ref = useRef<HTMLButtonElement>(null);\n const { buttonProps, isPressed } = useButton({ onPress, 'aria-label': label }, ref);\n const { interactionProps, dataAttributes } = useM3Interaction({ isPressed }, ref);\n return (\n <button\n {...mergeProps(buttonProps, interactionProps)}\n {...dataAttributes}\n ref={ref}\n className={className}\n >\n <BackIcon />\n </button>\n );\n}\n\ninterface SubmenuPopoverProps {\n state: SubmenuTriggerState;\n triggerRef: RefObject<HTMLDivElement | null>;\n submenuRef: RefObject<HTMLDivElement | null>;\n menuProps: ReturnType<typeof useSubmenuTrigger>['submenuProps'];\n popoverProps: ReturnType<typeof useSubmenuTrigger>['popoverProps'];\n submenu: ReactElement<MenuProps>;\n}\n\nfunction SubmenuPopover(props: SubmenuPopoverProps) {\n const tree = useContext(MenuTreeContext)!;\n const presence = usePresence(props.state.isOpen);\n const isPresent = tree.instant ? props.state.isOpen : presence.isPresent;\n const isExiting = !tree.instant && presence.isExiting;\n const { exitProps } = presence;\n if (!isPresent || !tree.container) return null;\n // Inside the root popover: its focus scope and outside-press handling cover the submenu,\n // and the modal root menu doesn't make it inert.\n return (\n <Overlay portalContainer={tree.container} disableFocusManagement exiting={isExiting}>\n <SubmenuPopoverContent {...props} isExiting={isExiting} exitProps={exitProps} />\n </Overlay>\n );\n}\n\nfunction SubmenuPopoverContent({\n state,\n triggerRef,\n submenuRef,\n menuProps,\n popoverProps,\n submenu,\n isExiting,\n exitProps,\n}: SubmenuPopoverProps & { isExiting: boolean; exitProps: ExitProps }) {\n const tree = useContext(MenuTreeContext)!;\n const popoverRef = useRef<HTMLDivElement>(null);\n const { submenuLevel, ...ariaMenuProps } = menuProps;\n const { popoverProps: positionProps, placement } = usePopover(\n {\n ...popoverProps,\n // React Aria handles this as a submenu's popover (dismissable, non-modal).\n ...({ trigger: 'SubmenuTrigger' } as Partial<AriaPopoverProps>),\n triggerRef,\n popoverRef,\n placement: 'end top',\n // Beside the parent menu (the 4px padding around the item and a 2px gap), with its\n // first item level with the item that opened it.\n offset: 6,\n crossOffset: -4,\n },\n state,\n );\n const revealRef = useRef<HTMLDivElement>(null);\n const entering = useRevealEntering(placement, revealRef);\n const { className, style, ...own } = submenu.props;\n const styles = menuStyles();\n return (\n // `w-max` (`menuOwnWidth`): positioned inside the narrower root popover, it would otherwise shrink to fit\n // what's left of that popover's width and wrap every label. Like the root menu's, the\n // submenu's `className` and `style` go on this outermost element.\n <div\n {...positionProps}\n ref={popoverRef}\n style={{ ...style, ...positionProps.style }}\n data-overlay-placement=\"\"\n className={styles.popover({ class: cn(menuOwnWidth, className) })}\n >\n <div\n {...exitProps}\n ref={revealRef}\n data-entering={entering || undefined}\n data-exiting={isExiting || undefined}\n data-placement={placement ?? undefined}\n className={menuStyles({ instant: tree.instant }).reveal()}\n >\n <MenuList\n {...mergeProps(ariaMenuProps, own)}\n listRef={submenuRef}\n layout={own.layout ?? tree.layout}\n variant={own.variant ?? tree.variant}\n density={own.density ?? tree.density}\n level={submenuLevel + 1}\n />\n </div>\n </div>\n );\n}\n\nfunction MenuItemView({\n node,\n state,\n variant,\n top,\n bottom,\n classNames,\n triggerProps,\n itemRef,\n submenuOpen = false,\n}: MenuItemViewProps) {\n const ownRef = useRef<HTMLElement>(null);\n const ref = itemRef ?? ownRef;\n const props = node.props as SubmenuItemProps;\n const isDisabled = state.disabledKeys.has(node.key);\n // A plain action in a selectable menu (`checkable={false}`) is never checked.\n const selectable = state.selectionManager.selectionMode !== 'none' && props.checkable !== false;\n const plainInSelectable = state.selectionManager.selectionMode !== 'none' && !selectable;\n const multiple = selectable && state.selectionManager.selectionMode === 'multiple';\n const {\n menuItemProps: ariaItemProps,\n keyboardShortcutProps,\n isSelected,\n isPressed,\n } = useMenuItem(\n {\n ...triggerProps,\n key: node.key,\n 'aria-label': props['aria-label'],\n // Disabled items stay focusable (the menu's `disabledBehavior`) but do nothing.\n isDisabled,\n isSelected: plainInSelectable ? false : undefined,\n // M3: multi-select menus stay open until they're dismissed, Enter included; a plain\n // action closes the menu, as in any menu.\n closeOnSelect: multiple ? false : plainInSelectable ? true : undefined,\n },\n state,\n ref,\n );\n const menuItemProps =\n plainInSelectable && !props.submenu\n ? { ...ariaItemProps, role: 'menuitem', 'aria-checked': undefined }\n : ariaItemProps;\n // A link item is a real `<a>`: `useMenuItem` gives it the link's attributes, routes a plain\n // click and closes the menu. A disabled item, or one that opens a submenu, stays a plain\n // element without them, so it can't be followed.\n const link = props.href != null && !isDisabled && !props.submenu;\n // Widened, so the item's ref (an element of either kind) fits both.\n const Root = (link ? 'a' : 'div') as ElementType;\n const linkAttributes = link\n ? { rel: props.rel ?? (props.target === '_blank' ? 'noopener noreferrer' : undefined) }\n : NOT_A_LINK;\n const { interactionProps, dataAttributes } = useM3Interaction(\n { isPressed, isDisabled, isSelected },\n ref,\n );\n const styles = menuStyles({\n variant,\n itemTop: top,\n itemBottom: bottom,\n hasDescription: Boolean(props.description),\n });\n const selectedIcon = props.selectedIcon ?? (selectable ? <CheckIcon /> : null);\n const trailingIcon = props.trailingIcon ?? (props.submenu ? <SubmenuIcon /> : null);\n const badgeId = useId();\n // Named by its content: the label and supporting text in the order they show, each read\n // once. React Aria names it by the label alone and describes it by the supporting text,\n // which leaves visible text out of its name (WCAG 2.5.3), as List options did. The shortcut\n // and the badge are extra information, not the item's label: they stay its description,\n // hidden from the content so they're read once.\n const describedBy =\n [props.shortcut ? keyboardShortcutProps.id : undefined, props.badge ? badgeId : undefined]\n .filter(Boolean)\n .join(' ') || undefined;\n\n return (\n <Root\n {...mergeProps(menuItemProps, interactionProps)}\n {...linkAttributes}\n {...dataAttributes}\n aria-labelledby={undefined}\n aria-describedby={describedBy}\n data-menu-open={submenuOpen || undefined}\n // Each item resolves the menu's tokens, so its selection picks its colours.\n data-tokens={mergeDataTokens('md-comp-menus', menuItemProps)}\n data-variant={variant}\n data-slot=\"item\"\n ref={ref}\n className={styles.item({ class: cn(classNames?.item, props.className) })}\n >\n {props.leadingIcon && selectedIcon ? (\n <span\n aria-hidden=\"true\"\n data-slot=\"leading-icon\"\n className={styles.icon({ class: classNames?.icon })}\n >\n {isSelected ? selectedIcon : props.leadingIcon}\n </span>\n ) : props.leadingIcon ? (\n <span\n aria-hidden=\"true\"\n data-slot=\"leading-icon\"\n className={styles.icon({ class: classNames?.icon })}\n >\n {props.leadingIcon}\n </span>\n ) : selectedIcon ? (\n // No leading icon: the selected icon expands in when selected (Compose).\n <span aria-hidden=\"true\" className={styles.selectedIcon()}>\n <span className={styles.selectedIconInner()}>\n <span className={styles.icon({ class: classNames?.icon })}>{selectedIcon}</span>\n </span>\n </span>\n ) : null}\n <span className={styles.text()}>\n <span>{node.rendered}</span>\n {props.description && (\n <span className={styles.description({ class: classNames?.description })}>\n {props.description}\n </span>\n )}\n </span>\n {props.badge && (\n <span id={badgeId} aria-hidden=\"true\" className={styles.badge()}>\n {props.badge}\n </span>\n )}\n {props.shortcut && (\n <kbd\n {...keyboardShortcutProps}\n aria-hidden=\"true\"\n className={styles.trailing({ class: cn('font-plain', classNames?.shortcut) })}\n >\n {props.shortcut}\n </kbd>\n )}\n {trailingIcon && (\n <span\n aria-hidden=\"true\"\n data-slot=\"trailing-icon\"\n className={styles.trailing({ class: classNames?.trailingIcon })}\n >\n {trailingIcon}\n </span>\n )}\n </Root>\n );\n}\n\nexport type { Key as MenuKey };\n"],"mappings":"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"}
|
|
@@ -2,6 +2,6 @@ import { ComponentTokenSet } from "../../tokens/component.js";
|
|
|
2
2
|
import "../../index.js";
|
|
3
3
|
//#region src/components/menu/menu-tokens.d.ts
|
|
4
4
|
/** The tokens of {@link Menu}: `--md-comp-menus-<part>` and `--md-comp-menus-standard-<part>`. */
|
|
5
|
-
export declare const menuTokenSet: ComponentTokenSet<"container-
|
|
5
|
+
export declare const menuTokenSet: ComponentTokenSet<"container-elevation" | "container-shape">;
|
|
6
6
|
//#endregion
|
|
7
7
|
//# sourceMappingURL=menu-tokens.d.ts.map
|
|
@@ -23,7 +23,7 @@ const navigationRailStyles = tv({
|
|
|
23
23
|
]
|
|
24
24
|
],
|
|
25
25
|
body: "flex min-h-0 flex-1 flex-col overflow-x-clip overflow-y-auto pt-[44px]",
|
|
26
|
-
header: "grid grid-cols-[56px] justify-items-center-safe ps-(--m3-rail-item-inset) pb-[40px] [&>*]:
|
|
26
|
+
header: "grid grid-cols-[56px] justify-items-center-safe ps-(--m3-rail-item-inset) pb-[40px] [&>*]:whitespace-nowrap",
|
|
27
27
|
items: ["flex flex-col", "[transition-property:gap] [transition-duration:var(--md-sys-motion-spring-spatial-default-duration)] [transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]"],
|
|
28
28
|
scrim: [
|
|
29
29
|
"fixed inset-0 z-(--md-sys-z-scrim) bg-scrim/32 opacity-100",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-styles.js","names":[],"sources":["../../../src/components/navigation/navigation-styles.ts"],"sourcesContent":["import { reducedMotionFade } from '../../motion/reduced-motion';\nimport { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Values follow Compose Material 3 (WideNavigationRail.kt, ShortNavigationBar.kt and the\n * NavigationRail* / NavigationBar* token files).\n *\n * Rail: `surface`, 96px collapsed; expanded it is as wide as its widest item plus the 20px\n * insets, 220–360px, and the width springs on the default spatial spring. Content starts\n * 44px down; a header sits 40px above the items, which are 4px apart collapsed and flush\n * expanded. The modal expanded rail is `surface-container` at level 2 with 16px end\n * corners over a 32% scrim; it grows from the collapsed width on the fast spatial spring, or\n * slides in on the default one when the collapsed rail is hidden. It stays mounted until its\n * own width or slide (or, under reduced motion, its fade) has ended.\n *\n * Icons stay on one line in both states, through the middle of the collapsed rail, as in\n * M3's rail motion: only the labels and the rail's edge move. While the width springs open\n * (and while the modal sheet shrinks away) labels stay on one line, clipped by the rail, so\n * none wraps letter by letter in a rail that is still narrow and no item grows taller. So nothing is centred in\n * the animated width. The collapsed width is `--m3-rail-collapsed-width` (96px), and the\n * item inset `--m3-rail-item-inset` is half of what's left beside a 56px pill (20px). The\n * header is a 56px column at that inset: anything up to 56px (the menu button, a FAB) is\n * centred on the icon line, and anything wider (an extended FAB) keeps its width and starts\n * at the column's edge.\n *\n * Items align as a group to the top (default) or, with `itemAlignment=\"center\"`, to the\n * middle of the space below the header (M3: centre alignment on tablets); the header stays\n * at the top. An optional divider (`outline-variant`) marks the edge next to the content.\n *\n * Bar: `surface-container`, at least 64px (it grows when labels wrap), no elevation.\n * Stacked items share the width equally, or, when centred, sit within side padding of\n * (100% − 10% × (items + 3)) / 2 (up to 6 items). Items beside their icons\n * (`iconPlacement=\"start\"`) are centred by default and sized to their content, so the extra\n * space goes to the bar's ends (M3: \"Horizontal navigation items have a fixed width\").\n * With `scrollBehavior=\"hide-on-scroll\"` the bar is sticky at the bottom and its offset\n * (`--m3-nav-bar-offset`, 0 down to minus its height) follows the page scroll.\n *\n * The rail's width is the `--m3-rail-width` variable, so a consumer `w-*` class still wins;\n * a narrower or wider collapsed rail sets `--m3-rail-collapsed-width` instead.\n *\n * Container colours and the modal rail's corners come from the component tokens\n * (`navigation-tokens.ts`, `--md-comp-nav-bar-*` and `--md-comp-nav-rail-*`): the bar, the\n * rail and the modal rail's sheet (`data-modal`) carry `data-tokens`. A consumer class still\n * wins in `cn()`.\n * Every class is written out in full so Tailwind can find it.\n */\n\n/** Half of what's left beside a 56px pill in the collapsed rail. */\nconst itemInset = '[--m3-rail-item-inset:calc((var(--m3-rail-collapsed-width)_-_56px)/2)]';\n\n/**\n * While the expanded rail is still widening (`data-widening`), labels stay on one line and\n * the rail clips them, so the items keep their height and the icons their places.\n */\nconst oneLineWhileResizing = '[&[data-widening]_[data-nav-label]]:text-nowrap';\n\nconst widthMotion = [\n '[transition-property:width]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n];\n\n/** Variant definitions for {@link NavigationRail}; extend them to add variants. */\nexport const navigationRailStyles = tv({\n slots: {\n root: [\n // Positioned, so a secondary column placed against it escapes the body's scrolling.\n 'relative flex w-(--m3-rail-width) shrink-0 flex-col text-on-surface',\n 'bg-[var(--_md-comp-nav-rail-collapsed-container-color)] data-expanded:bg-[var(--_md-comp-nav-rail-expanded-container-color)]',\n itemInset,\n oneLineWhileResizing,\n ...widthMotion,\n ],\n // Library-owned wrapper: clips labels while the width animates.\n body: 'flex min-h-0 flex-1 flex-col overflow-x-clip overflow-y-auto pt-[44px]',\n // Children keep their own width (a centred grid item would shrink to the 56px column).\n header:\n 'grid grid-cols-[56px] justify-items-center-safe ps-(--m3-rail-item-inset) pb-[40px] [&>*]:min-w-max',\n // The gap between items springs 4 → 0px as the rail expands (default spatial, as\n // Compose's itemVerticalSpacedBy; 2026-10-17), instant under reduced motion.\n items: [\n 'flex flex-col',\n '[transition-property:gap] [transition-duration:var(--md-sys-motion-spring-spatial-default-duration)] [transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n ],\n 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 // In and out on the default effects spring, as Compose's modal rail scrim (2026-10-15).\n 'starting:opacity-0 data-exiting:opacity-0',\n ],\n sheet: [\n 'fixed inset-y-0 start-0 z-(--md-sys-z-sheet) flex w-(--m3-rail-width) flex-col overflow-x-clip overflow-y-auto pt-[44px]',\n 'rounded-e-[var(--_md-comp-nav-rail-expanded-modal-container-shape)] bg-[var(--_md-comp-nav-rail-expanded-modal-container-color)]',\n 'text-on-surface shadow-elevation-2 outline-none',\n itemInset,\n oneLineWhileResizing,\n '[&[data-exiting]_[data-nav-label]]:text-nowrap',\n '[transition-property:width,translate]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing)]',\n // Under reduced motion it fades in and out at its full width.\n ...reducedMotionFade,\n ],\n },\n variants: {\n expanded: {\n true: { items: 'gap-0' },\n false: { items: 'gap-[4px]' },\n },\n /** Auto margins centre the items without hiding any when the rail overflows. */\n itemAlignment: {\n top: {},\n center: { items: 'my-auto' },\n },\n /** `hideOnCollapse=\"compact\"`: the collapsed rail shows from 600px wide. */\n hiddenOnCompact: {\n true: { root: 'max-medium:hidden' },\n false: {},\n },\n /** A divider on the edge next to the content. */\n divider: {\n true: { root: 'border-e border-outline-variant' },\n false: {},\n },\n /**\n * `indicatorMotion=\"slide\"` in the expanded rail: the list is isolated, so the items'\n * raised content stays above a sliding pill and no higher.\n */\n slidingIndicator: {\n true: { items: 'isolate' },\n false: {},\n },\n /**\n * Modal sheet entry: from the collapsed width, or sliding in from the start edge. Under\n * reduced motion it fades instead.\n */\n sheetEntry: {\n grow: {\n sheet:\n 'motion-safe:starting:w-(--m3-rail-collapsed-width) motion-safe:data-exiting:w-(--m3-rail-collapsed-width)',\n },\n // Sliding in, the sheet takes the default spatial spring, as Compose's modal rail\n // (`WideNavigationRail.kt`): the fast one overshot 21px off the window edge (2026-10-15).\n slide: {\n sheet: [\n 'motion-safe:starting:-translate-x-full motion-safe:data-exiting:-translate-x-full motion-safe:rtl:starting:translate-x-full motion-safe:rtl:data-exiting:translate-x-full',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n ],\n },\n },\n },\n defaultVariants: {\n expanded: false,\n sheetEntry: 'grow',\n itemAlignment: 'top',\n divider: false,\n slidingIndicator: false,\n },\n});\n\n/** Variant definitions for {@link NavigationBar}; extend them to add variants. */\nexport const navigationBarStyles = tv({\n slots: {\n root: 'flex min-h-[64px] w-full bg-[var(--_md-comp-nav-bar-container-color)] text-on-surface',\n item: 'min-w-0',\n },\n variants: {\n arrangement: {\n equal: { item: 'flex-1 basis-0' },\n centered: { root: 'justify-center', item: 'flex-1 basis-0' },\n },\n iconPlacement: { top: {}, start: {} },\n hideOnScroll: {\n // Settling after a scroll ends (`data-settling`), the offset eases to shown or hidden on\n // the default effects spring; while scrolling it follows the scroll directly.\n true: {\n root: [\n 'sticky bottom-(--m3-nav-bar-offset) z-(--md-sys-z-sticky)',\n 'data-settling:[transition-property:bottom]',\n 'data-settling:[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n 'data-settling:[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n ],\n },\n false: {},\n },\n },\n compoundVariants: [\n // Items beside their icons keep their own width; the extra space goes to the ends.\n { arrangement: 'centered', iconPlacement: 'start', class: { item: 'flex-initial basis-auto' } },\n ],\n defaultVariants: { arrangement: 'equal', iconPlacement: 'top', hideOnScroll: false },\n});\n\n/**\n * The variants of {@link navigationBarStyles}, 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 NavigationBarStyleProps = VariantProps<typeof navigationBarStyles>;\nexport type NavigationBarArrangement = NonNullable<NavigationBarStyleProps['arrangement']>;\n"],"mappings":";;;;AAgDA,MAAM,YAAY;;;;;AAMlB,MAAM,uBAAuB;;AAS7B,MAAa,uBAAuB,GAAG;CACrC,OAAO;EACL,MAAM;GAEJ;GACA;GACA;GACA;GACA,GAAG;IAdP;IACA;IACA;GAYO;EACL;EAEA,MAAM;EAEN,QACE;EAGF,OAAO,CACL,iBACA,sLACF;EACA,OAAO;GACL;GACA;GACA;GACA;GAEA;EACF;EACA,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA,GAAG;EACL;CACF;CACA,UAAU;EACR,UAAU;GACR,MAAM,EAAE,OAAO,QAAQ;GACvB,OAAO,EAAE,OAAO,YAAY;EAC9B;;EAEA,eAAe;GACb,KAAK,CAAC;GACN,QAAQ,EAAE,OAAO,UAAU;EAC7B;;EAEA,iBAAiB;GACf,MAAM,EAAE,MAAM,oBAAoB;GAClC,OAAO,CAAC;EACV;;EAEA,SAAS;GACP,MAAM,EAAE,MAAM,kCAAkC;GAChD,OAAO,CAAC;EACV;;;;;EAKA,kBAAkB;GAChB,MAAM,EAAE,OAAO,UAAU;GACzB,OAAO,CAAC;EACV;;;;;EAKA,YAAY;GACV,MAAM,EACJ,OACE,4GACJ;GAGA,OAAO,EACL,OAAO;IACL;IACA;IACA;GACF,EACF;EACF;CACF;CACA,iBAAiB;EACf,UAAU;EACV,YAAY;EACZ,eAAe;EACf,SAAS;EACT,kBAAkB;CACpB;AACF,CAAC;;AAGD,MAAa,sBAAsB,GAAG;CACpC,OAAO;EACL,MAAM;EACN,MAAM;CACR;CACA,UAAU;EACR,aAAa;GACX,OAAO,EAAE,MAAM,iBAAiB;GAChC,UAAU;IAAE,MAAM;IAAkB,MAAM;GAAiB;EAC7D;EACA,eAAe;GAAE,KAAK,CAAC;GAAG,OAAO,CAAC;EAAE;EACpC,cAAc;GAGZ,MAAM,EACJ,MAAM;IACJ;IACA;IACA;IACA;GACF,EACF;GACA,OAAO,CAAC;EACV;CACF;CACA,kBAAkB,CAEhB;EAAE,aAAa;EAAY,eAAe;EAAS,OAAO,EAAE,MAAM,0BAA0B;CAAE,CAChG;CACA,iBAAiB;EAAE,aAAa;EAAS,eAAe;EAAO,cAAc;CAAM;AACrF,CAAC"}
|
|
1
|
+
{"version":3,"file":"navigation-styles.js","names":[],"sources":["../../../src/components/navigation/navigation-styles.ts"],"sourcesContent":["import { reducedMotionFade } from '../../motion/reduced-motion';\nimport { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Values follow Compose Material 3 (WideNavigationRail.kt, ShortNavigationBar.kt and the\n * NavigationRail* / NavigationBar* token files).\n *\n * Rail: `surface`, 96px collapsed; expanded it is as wide as its widest item plus the 20px\n * insets, 220–360px, and the width springs on the default spatial spring. Content starts\n * 44px down; a header sits 40px above the items, which are 4px apart collapsed and flush\n * expanded. The modal expanded rail is `surface-container` at level 2 with 16px end\n * corners over a 32% scrim; it grows from the collapsed width on the fast spatial spring, or\n * slides in on the default one when the collapsed rail is hidden. It stays mounted until its\n * own width or slide (or, under reduced motion, its fade) has ended.\n *\n * Icons stay on one line in both states, through the middle of the collapsed rail, as in\n * M3's rail motion: only the labels and the rail's edge move. While the width springs open\n * (and while the modal sheet shrinks away) labels stay on one line, clipped by the rail, so\n * none wraps letter by letter in a rail that is still narrow and no item grows taller. So nothing is centred in\n * the animated width. The collapsed width is `--m3-rail-collapsed-width` (96px), and the\n * item inset `--m3-rail-item-inset` is half of what's left beside a 56px pill (20px). The\n * header is a 56px column at that inset: anything up to 56px (the menu button, a FAB) is\n * centred on the icon line, and anything wider (an extended FAB) keeps its width and starts\n * at the column's edge.\n *\n * Items align as a group to the top (default) or, with `itemAlignment=\"center\"`, to the\n * middle of the space below the header (M3: centre alignment on tablets); the header stays\n * at the top. An optional divider (`outline-variant`) marks the edge next to the content.\n *\n * Bar: `surface-container`, at least 64px (it grows when labels wrap), no elevation.\n * Stacked items share the width equally, or, when centred, sit within side padding of\n * (100% − 10% × (items + 3)) / 2 (up to 6 items). Items beside their icons\n * (`iconPlacement=\"start\"`) are centred by default and sized to their content, so the extra\n * space goes to the bar's ends (M3: \"Horizontal navigation items have a fixed width\").\n * With `scrollBehavior=\"hide-on-scroll\"` the bar is sticky at the bottom and its offset\n * (`--m3-nav-bar-offset`, 0 down to minus its height) follows the page scroll.\n *\n * The rail's width is the `--m3-rail-width` variable, so a consumer `w-*` class still wins;\n * a narrower or wider collapsed rail sets `--m3-rail-collapsed-width` instead.\n *\n * Container colours and the modal rail's corners come from the component tokens\n * (`navigation-tokens.ts`, `--md-comp-nav-bar-*` and `--md-comp-nav-rail-*`): the bar, the\n * rail and the modal rail's sheet (`data-modal`) carry `data-tokens`. A consumer class still\n * wins in `cn()`.\n * Every class is written out in full so Tailwind can find it.\n */\n\n/** Half of what's left beside a 56px pill in the collapsed rail. */\nconst itemInset = '[--m3-rail-item-inset:calc((var(--m3-rail-collapsed-width)_-_56px)/2)]';\n\n/**\n * While the expanded rail is still widening (`data-widening`), labels stay on one line and\n * the rail clips them, so the items keep their height and the icons their places.\n */\nconst oneLineWhileResizing = '[&[data-widening]_[data-nav-label]]:text-nowrap';\n\nconst widthMotion = [\n '[transition-property:width]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n];\n\n/** Variant definitions for {@link NavigationRail}; extend them to add variants. */\nexport const navigationRailStyles = tv({\n slots: {\n root: [\n // Positioned, so a secondary column placed against it escapes the body's scrolling.\n 'relative flex w-(--m3-rail-width) shrink-0 flex-col text-on-surface',\n 'bg-[var(--_md-comp-nav-rail-collapsed-container-color)] data-expanded:bg-[var(--_md-comp-nav-rail-expanded-container-color)]',\n itemInset,\n oneLineWhileResizing,\n ...widthMotion,\n ],\n // Library-owned wrapper: clips labels while the width animates.\n body: 'flex min-h-0 flex-1 flex-col overflow-x-clip overflow-y-auto pt-[44px]',\n // A 56px column at the item inset: children up to 56px centre on the icon line, a wider\n // one (an extended FAB, an app's name) keeps its width on one line and starts at the\n // column's edge. Text doesn't wrap (2026-11-11) instead of every child taking\n // `min-width: max-content`, which grew a box holding an image at its full size (an image\n // avatar) to the image's natural width (2026-11-10).\n header:\n 'grid grid-cols-[56px] justify-items-center-safe ps-(--m3-rail-item-inset) pb-[40px] [&>*]:whitespace-nowrap',\n // The gap between items springs 4 → 0px as the rail expands (default spatial, as\n // Compose's itemVerticalSpacedBy; 2026-10-17), instant under reduced motion.\n items: [\n 'flex flex-col',\n '[transition-property:gap] [transition-duration:var(--md-sys-motion-spring-spatial-default-duration)] [transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n ],\n 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 // In and out on the default effects spring, as Compose's modal rail scrim (2026-10-15).\n 'starting:opacity-0 data-exiting:opacity-0',\n ],\n sheet: [\n 'fixed inset-y-0 start-0 z-(--md-sys-z-sheet) flex w-(--m3-rail-width) flex-col overflow-x-clip overflow-y-auto pt-[44px]',\n 'rounded-e-[var(--_md-comp-nav-rail-expanded-modal-container-shape)] bg-[var(--_md-comp-nav-rail-expanded-modal-container-color)]',\n 'text-on-surface shadow-elevation-2 outline-none',\n itemInset,\n oneLineWhileResizing,\n '[&[data-exiting]_[data-nav-label]]:text-nowrap',\n '[transition-property:width,translate]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing)]',\n // Under reduced motion it fades in and out at its full width.\n ...reducedMotionFade,\n ],\n },\n variants: {\n expanded: {\n true: { items: 'gap-0' },\n false: { items: 'gap-[4px]' },\n },\n /** Auto margins centre the items without hiding any when the rail overflows. */\n itemAlignment: {\n top: {},\n center: { items: 'my-auto' },\n },\n /** `hideOnCollapse=\"compact\"`: the collapsed rail shows from 600px wide. */\n hiddenOnCompact: {\n true: { root: 'max-medium:hidden' },\n false: {},\n },\n /** A divider on the edge next to the content. */\n divider: {\n true: { root: 'border-e border-outline-variant' },\n false: {},\n },\n /**\n * `indicatorMotion=\"slide\"` in the expanded rail: the list is isolated, so the items'\n * raised content stays above a sliding pill and no higher.\n */\n slidingIndicator: {\n true: { items: 'isolate' },\n false: {},\n },\n /**\n * Modal sheet entry: from the collapsed width, or sliding in from the start edge. Under\n * reduced motion it fades instead.\n */\n sheetEntry: {\n grow: {\n sheet:\n 'motion-safe:starting:w-(--m3-rail-collapsed-width) motion-safe:data-exiting:w-(--m3-rail-collapsed-width)',\n },\n // Sliding in, the sheet takes the default spatial spring, as Compose's modal rail\n // (`WideNavigationRail.kt`): the fast one overshot 21px off the window edge (2026-10-15).\n slide: {\n sheet: [\n 'motion-safe:starting:-translate-x-full motion-safe:data-exiting:-translate-x-full motion-safe:rtl:starting:translate-x-full motion-safe:rtl:data-exiting:translate-x-full',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n ],\n },\n },\n },\n defaultVariants: {\n expanded: false,\n sheetEntry: 'grow',\n itemAlignment: 'top',\n divider: false,\n slidingIndicator: false,\n },\n});\n\n/** Variant definitions for {@link NavigationBar}; extend them to add variants. */\nexport const navigationBarStyles = tv({\n slots: {\n root: 'flex min-h-[64px] w-full bg-[var(--_md-comp-nav-bar-container-color)] text-on-surface',\n item: 'min-w-0',\n },\n variants: {\n arrangement: {\n equal: { item: 'flex-1 basis-0' },\n centered: { root: 'justify-center', item: 'flex-1 basis-0' },\n },\n iconPlacement: { top: {}, start: {} },\n hideOnScroll: {\n // Settling after a scroll ends (`data-settling`), the offset eases to shown or hidden on\n // the default effects spring; while scrolling it follows the scroll directly.\n true: {\n root: [\n 'sticky bottom-(--m3-nav-bar-offset) z-(--md-sys-z-sticky)',\n 'data-settling:[transition-property:bottom]',\n 'data-settling:[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n 'data-settling:[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n ],\n },\n false: {},\n },\n },\n compoundVariants: [\n // Items beside their icons keep their own width; the extra space goes to the ends.\n { arrangement: 'centered', iconPlacement: 'start', class: { item: 'flex-initial basis-auto' } },\n ],\n defaultVariants: { arrangement: 'equal', iconPlacement: 'top', hideOnScroll: false },\n});\n\n/**\n * The variants of {@link navigationBarStyles}, 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 NavigationBarStyleProps = VariantProps<typeof navigationBarStyles>;\nexport type NavigationBarArrangement = NonNullable<NavigationBarStyleProps['arrangement']>;\n"],"mappings":";;;;AAgDA,MAAM,YAAY;;;;;AAMlB,MAAM,uBAAuB;;AAS7B,MAAa,uBAAuB,GAAG;CACrC,OAAO;EACL,MAAM;GAEJ;GACA;GACA;GACA;GACA,GAAG;IAdP;IACA;IACA;GAYO;EACL;EAEA,MAAM;EAMN,QACE;EAGF,OAAO,CACL,iBACA,sLACF;EACA,OAAO;GACL;GACA;GACA;GACA;GAEA;EACF;EACA,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA,GAAG;EACL;CACF;CACA,UAAU;EACR,UAAU;GACR,MAAM,EAAE,OAAO,QAAQ;GACvB,OAAO,EAAE,OAAO,YAAY;EAC9B;;EAEA,eAAe;GACb,KAAK,CAAC;GACN,QAAQ,EAAE,OAAO,UAAU;EAC7B;;EAEA,iBAAiB;GACf,MAAM,EAAE,MAAM,oBAAoB;GAClC,OAAO,CAAC;EACV;;EAEA,SAAS;GACP,MAAM,EAAE,MAAM,kCAAkC;GAChD,OAAO,CAAC;EACV;;;;;EAKA,kBAAkB;GAChB,MAAM,EAAE,OAAO,UAAU;GACzB,OAAO,CAAC;EACV;;;;;EAKA,YAAY;GACV,MAAM,EACJ,OACE,4GACJ;GAGA,OAAO,EACL,OAAO;IACL;IACA;IACA;GACF,EACF;EACF;CACF;CACA,iBAAiB;EACf,UAAU;EACV,YAAY;EACZ,eAAe;EACf,SAAS;EACT,kBAAkB;CACpB;AACF,CAAC;;AAGD,MAAa,sBAAsB,GAAG;CACpC,OAAO;EACL,MAAM;EACN,MAAM;CACR;CACA,UAAU;EACR,aAAa;GACX,OAAO,EAAE,MAAM,iBAAiB;GAChC,UAAU;IAAE,MAAM;IAAkB,MAAM;GAAiB;EAC7D;EACA,eAAe;GAAE,KAAK,CAAC;GAAG,OAAO,CAAC;EAAE;EACpC,cAAc;GAGZ,MAAM,EACJ,MAAM;IACJ;IACA;IACA;IACA;GACF,EACF;GACA,OAAO,CAAC;EACV;CACF;CACA,kBAAkB,CAEhB;EAAE,aAAa;EAAY,eAAe;EAAS,OAAO,EAAE,MAAM,0BAA0B;CAAE,CAChG;CACA,iBAAiB;EAAE,aAAa;EAAS,eAAe;EAAO,cAAc;CAAM;AACrF,CAAC"}
|
|
@@ -9,8 +9,8 @@ const searchBarStyles = tv({
|
|
|
9
9
|
leading: "ms-[4px] flex size-[48px] shrink-0 items-center justify-center text-[color:var(--_md-comp-search-bar-leading-icon-color)] [--md-comp-icon-button-icon-color:currentColor] [&>svg]:size-[24px]",
|
|
10
10
|
trailing: ["me-[4px] flex h-[48px] min-w-[48px] shrink-0 items-center justify-center gap-[8px] px-[4px] text-[color:var(--_md-comp-search-bar-trailing-icon-color)] [--md-comp-icon-button-icon-color:currentColor]", "[&>svg]:size-[24px] [&>img]:size-[30px] [&>img]:rounded-full"],
|
|
11
11
|
input: [
|
|
12
|
-
"h-full min-w-0 flex-1 bg-transparent text-body-large text-[color:var(--_md-comp-search-bar-input-text-color)] caret-primary outline-none",
|
|
13
|
-
"placeholder:text-on-surface-variant",
|
|
12
|
+
"h-full min-w-0 flex-1 bg-transparent text-body-large text-[color:var(--_md-comp-search-bar-input-text-color)] caret-primary outline-none text-ellipsis",
|
|
13
|
+
"placeholder:text-on-surface-variant placeholder:text-ellipsis",
|
|
14
14
|
"[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none"
|
|
15
15
|
],
|
|
16
16
|
scrim: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-styles.js","names":[],"sources":["../../../src/components/search/search-styles.ts"],"sourcesContent":["import { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Values follow Compose Material 3 (SearchBar.kt: SearchBar, AppBarWithSearch,\n * ExpandedDockedSearchBarWithGap, ExpandedFullScreenSearchBar, SearchBarDefaults.InputField;\n * SearchBar / SearchView tokens).\n *\n * Field: 56px, full corners, `surface-container-high`, 360px wide by default and at most\n * 720px. Input and placeholder `body-large`, `on-surface` / `on-surface-variant`. Icons sit\n * in 48px boxes shifted 4px inwards (leading `on-surface`, trailing `on-surface-variant`);\n * the text keeps 4px from the leading and trailing tap targets (the spec's \"Leading icon and\n * label padding (from tap target): 4dp\" and \"Label and trailing icon padding\"; Compose's\n * code leaves none), so it starts 56px in after a leading icon, 16px without one.\n *\n * Docked (with gap): the expanded field, 56px tall whatever the collapsed bar's height\n * (M3's focused search bar container), sits centred over the collapsed one and widens by 12px on\n * each side (M3 Expressive: the margins change from 24dp to 12dp when focused). It morphs from\n * the bar's bounds (position and size) on the default spatial spring and back on the same\n * spring floored at the bar, so it lands on the bar exactly; a dropdown 2px below it (12px corners,\n * `surface-container-high`; with the bar, at least 240px and at most 2/3 of the window\n * tall, per the M3 spec) slides down from half its height on the default spatial spring\n * (fast spatial back) while its content fades in over 100ms after 50ms, and its height follows\n * the results on the default spatial spring; a 32% scrim covers the page.\n *\n * Full screen, in M3 Expressive's contained style: a `surface-container-low` surface grows\n * from the bar's bounds (28px corners) to the whole window on the slow spatial spring\n * (default spatial back), clamped so nothing overshoots; the field keeps its\n * `surface-container-high` pill and moves and grows from the bar to the top, 8px down and\n * 16px from each side, and the content starts 72px down.\n *\n * In a search app bar the field is `surface-container` (`surface-container-highest` once\n * content scrolls under the bar) and fills the free space up to 312px, then half of it,\n * at most 720px (M3 app bar guidelines).\n *\n * The field's colours and corners come from the search bar's component tokens and the\n * views' from the search view's (`search-tokens.ts`, `--md-comp-search-bar-*` and\n * `--md-comp-search-view-*`); the pill, the dropdown and the full-screen view carry\n * `data-tokens`. The search app bar reads the app bar's tokens (`app-bar-tokens.ts`). A\n * consumer class still wins in `cn()`.\n *\n * Every class is written out in full so Tailwind can find it.\n */\n\nconst contentFade = [\n 'opacity-100 starting:opacity-0 data-exiting:opacity-0',\n // Compose: fade in over 100ms after 50ms (standard accelerate), out over 100ms.\n '[transition-delay:0s,50ms] data-exiting:[transition-delay:0s,0s]',\n];\n\n/** Variant definitions for {@link SearchBar}; extend them to add variants. */\nexport const searchBarStyles = tv({\n slots: {\n // The bar's height is set here alone (56px, Compose's); a consumer `h-*` resizes it.\n root: 'flex h-[56px] w-[360px] max-w-[min(720px,100%)] shrink-0',\n // The pill holding the icons and the input fills the bar.\n // Keyboard focus in the input draws Compose's inset focus ring around the pill.\n // Forced colours drop the pill's colour: a system-colour border keeps its edge.\n field: [\n 'focus-ring-inset flex h-full w-full min-w-0 items-center rounded-[var(--_md-comp-search-bar-container-shape)] text-on-surface',\n 'forced-colors:border forced-colors:border-solid forced-colors:border-[CanvasText]',\n ],\n // Icon buttons in the slots take the slot's colour (and so its icon colour token), as\n // in the app bar's slots: Compose colours the search bar's and view's leading icon\n // `on-surface` and trailing icons `on-surface-variant`, buttons included.\n leading:\n 'ms-[4px] flex size-[48px] shrink-0 items-center justify-center text-[color:var(--_md-comp-search-bar-leading-icon-color)] [--md-comp-icon-button-icon-color:currentColor] [&>svg]:size-[24px]',\n // One or two trailing icon buttons, or an avatar (30px) and an icon button (M3). Two\n // 40px buttons sit 8px apart inside 4px of padding, so their 48px targets meet edge to\n // edge within the slot (a single one is centred in 48px, as before).\n trailing: [\n 'me-[4px] flex h-[48px] min-w-[48px] shrink-0 items-center justify-center gap-[8px] px-[4px] text-[color:var(--_md-comp-search-bar-trailing-icon-color)] [--md-comp-icon-button-icon-color:currentColor]',\n '[&>svg]:size-[24px] [&>img]:size-[30px] [&>img]:rounded-full',\n ],\n input: [\n 'h-full min-w-0 flex-1 bg-transparent text-body-large text-[color:var(--_md-comp-search-bar-input-text-color)] caret-primary outline-none',\n 'placeholder:text-on-surface-variant',\n '[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none',\n ],\n scrim: [\n 'fixed inset-0 z-(--md-sys-z-scrim) bg-scrim/32 opacity-100 data-exiting:pointer-events-none',\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 * Docked view: the panel sits over the collapsed bar and morphs from the bar's bounds\n * (`--m3-search-left/top/width/height`) to the focused field's (the `-dx/-dy/-dw/-dh`\n * deltas) as `--m3-search-progress` runs 0 → 1, as Compose lerps the layout with its\n * search bar state's progress. `--m3-search-p` is that progress floored at 0 (Compose's\n * `coerceAtLeast(0f)`): the spring may overshoot past the focused bounds on the way in,\n * but never past the bar's on the way back, so the field lands on the bar exactly.\n * Reduced motion: the progress stays at 1, so the field is at its focused size at once.\n */\n dockedPanel: [\n // Leaving, the view takes no presses: the spring's tail runs on after the field has\n // landed on the bar, and a press there ran a suggestion of the invisible dropdown.\n 'fixed z-(--md-sys-z-sheet) flex flex-col outline-none data-exiting:pointer-events-none',\n '[--m3-search-progress:1] [--m3-search-p:max(0,var(--m3-search-progress))]',\n 'motion-safe:starting:[--m3-search-progress:0] motion-safe:data-exiting:[--m3-search-progress:0]',\n 'top-[calc(var(--m3-search-top)_+_var(--m3-search-p)_*_var(--m3-search-dy))]',\n 'left-[calc(var(--m3-search-left)_+_var(--m3-search-p)_*_var(--m3-search-dx))]',\n 'w-[calc(var(--m3-search-width)_+_var(--m3-search-p)_*_var(--m3-search-dw))]',\n // Both ways on the default spatial spring (Compose's collapse spec).\n '[transition-property:--m3-search-progress]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n ],\n // The focused field is 56px (M3), even when the collapsed bar is resized; it grows from\n // the bar's height with the panel.\n // The full-window layer the docked view sits in (React Aria's underlay).\n underlay: 'fixed inset-0 z-(--md-sys-z-sheet) data-exiting:pointer-events-none',\n dockedHeader:\n 'flex h-[calc(var(--m3-search-height)_+_var(--m3-search-p)_*_var(--m3-search-dh))] shrink-0',\n dropdown: [\n 'mt-[2px] overflow-y-auto overscroll-contain data-resizing:overflow-y-hidden data-exiting:pointer-events-none',\n // 58px is the field and the gap above the dropdown.\n 'min-h-[min(182px,calc(66.67dvh-58px))] max-h-[calc(66.67dvh-58px)]',\n // The height follows the content (`--m3-search-content-height`, measured) within those\n // bounds, so it animates as the results change. Without the measurement (before\n // hydration, or under reduced motion) it's `auto`.\n 'h-[clamp(min(182px,calc(66.67dvh_-_58px)),var(--m3-search-content-height),calc(66.67dvh_-_58px))]',\n 'rounded-[var(--_md-comp-search-view-docked-container-shape)] bg-[var(--_md-comp-search-view-container-color)] text-on-surface',\n 'forced-colors:border forced-colors:border-solid forced-colors:border-[CanvasText]',\n // Reduced motion: it only fades.\n 'translate-y-0 motion-safe:starting:-translate-y-1/2 motion-safe:data-exiting:-translate-y-1/2',\n // The delays below cycle, so the height (third) starts at once like the slide.\n '[transition-property:translate,opacity,height]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration),100ms,var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing),cubic-bezier(0.3,0,1,1),var(--md-sys-motion-spring-spatial-default-easing)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration),100ms,var(--md-sys-motion-spring-spatial-fast-duration)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing),cubic-bezier(0,0,0,1),var(--md-sys-motion-spring-spatial-fast-easing)]',\n ...contentFade,\n ],\n /*\n * Full-screen view: the surface grows from the bar's bounds to the window as\n * `--m3-search-progress` runs 0 → 1, through a clip (nothing is transformed). Compose\n * clamps the full-screen progress to 0..1 (`coerceIn`), so `--m3-search-p` is clamped too:\n * the clip and the field never overshoot the window's or the bar's bounds.\n */\n fullScreenPanel: [\n 'fixed inset-0 z-(--md-sys-z-sheet) bg-[var(--_md-comp-search-view-container-color)] text-on-surface outline-none data-exiting:pointer-events-none',\n '[--m3-search-progress:1] [--m3-search-p:clamp(0,var(--m3-search-progress),1)]',\n 'motion-safe:starting:[--m3-search-progress:0] motion-safe:data-exiting:[--m3-search-progress:0]',\n '[clip-path:inset(calc((1_-_var(--m3-search-p))_*_var(--m3-search-top))_calc((1_-_var(--m3-search-p))_*_var(--m3-search-right))_calc((1_-_var(--m3-search-p))_*_var(--m3-search-bottom))_calc((1_-_var(--m3-search-p))_*_var(--m3-search-left))_round_calc((1_-_var(--m3-search-p))_*_28px))]',\n 'motion-safe:[transition-property:--m3-search-progress]',\n 'motion-safe:[transition-duration:var(--md-sys-motion-spring-spatial-slow-duration)]',\n 'motion-safe:[transition-timing-function:var(--md-sys-motion-spring-spatial-slow-easing)]',\n 'motion-safe:data-exiting:[transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n 'motion-safe:data-exiting:[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n // Reduced motion: the view fades in and out over the window instead of growing.\n 'motion-reduce:starting:opacity-0 motion-reduce:data-exiting:opacity-0',\n 'motion-reduce:[transition-property:opacity]',\n 'motion-reduce:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'motion-reduce:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n // Compose's full-screen field is 56px whatever the bar's size, 8px down and 16px from each\n // side; it moves and grows there from the bar's bounds with the surface.\n fullScreenHeader: [\n 'absolute',\n 'start-[calc(var(--m3-search-start)_+_var(--m3-search-p)_*_(16px_-_var(--m3-search-start)))]',\n 'top-[calc(var(--m3-search-top)_+_var(--m3-search-p)_*_(8px_-_var(--m3-search-top)))]',\n 'w-[calc(var(--m3-search-width)_+_var(--m3-search-p)_*_(100%_-_32px_-_var(--m3-search-width)))]',\n 'h-[calc(var(--m3-search-height)_+_var(--m3-search-p)_*_(56px_-_var(--m3-search-height)))]',\n ],\n fullScreenContent: [\n 'absolute inset-x-0 top-[72px] bottom-0 overflow-y-auto overscroll-contain',\n '[transition-property:opacity] [transition-duration:100ms]',\n '[transition-timing-function:cubic-bezier(0.3,0,1,1)]',\n 'data-exiting:[transition-timing-function:cubic-bezier(0,0,0,1)]',\n ...contentFade,\n ],\n },\n variants: {\n /**\n * Inside a search app bar the bar fills the free space up to 312px, then grows to half\n * of it (M3), up to 720px.\n */\n inAppBar: { true: { root: 'w-[max(min(100%,312px),50%)]' }, false: {} },\n // 4px from a tap target (M3), else 16px from the pill's edge.\n hasLeading: { true: { input: 'ps-[4px]' }, false: { input: 'ps-[16px]' } },\n hasTrailing: { true: { input: 'pe-[4px]' }, false: { input: 'pe-[16px]' } },\n /**\n * Container colour: the bar's own; in a search app bar `surface-container`, and one\n * step higher than the bar once content scrolls under it.\n */\n tone: {\n default: { field: 'bg-[var(--_md-comp-search-bar-container-color)]' },\n appBar: { field: 'bg-surface-container' },\n scrolled: { field: 'bg-surface-container-highest' },\n },\n /** Centre the placeholder and the query while the bar is collapsed. */\n textAlign: { start: {}, center: { input: 'text-center' } },\n /**\n * A disabled `SearchField`: M3's disabled colours, the content `on-surface` at 38% and the\n * container at 12%; forced colours draw it in `GrayText`.\n */\n disabled: {\n true: {\n field: 'bg-on-surface/12 forced-colors:border-[GrayText]',\n leading: 'text-on-surface/38 forced-colors:text-[GrayText]',\n trailing: 'text-on-surface/38 forced-colors:text-[GrayText]',\n input:\n 'cursor-default text-on-surface/38 placeholder:text-on-surface/38 forced-colors:text-[GrayText]',\n },\n false: {},\n },\n },\n defaultVariants: {\n inAppBar: false,\n hasLeading: false,\n hasTrailing: false,\n tone: 'default',\n textAlign: 'start',\n disabled: false,\n },\n});\n\n/**\n * Variant definitions for {@link SearchField}'s root. Its pill and the parts in it are the search\n * bar's ({@link searchBarStyles}), so the two can't drift.\n */\nexport const searchFieldStyles = tv({\n slots: {\n // 56px, the search bar's height, less 4px a density step (44px at -3), filling its\n // container: it filters a page, a list or a pane in place. A consumer `w-*` or `h-*` wins.\n root: 'flex h-[calc(56px+var(--md-sys-density,0)*4px)] w-full min-w-0',\n // The 48px icon slots keep to a dense pill's height; their buttons keep 48px targets.\n slot: 'max-h-full',\n },\n variants: {},\n});\n\n/** Variant definitions for {@link SearchAppBar}. */\nexport const searchAppBarStyles = tv({\n slots: {\n root: [\n 'flex min-h-[64px] w-full items-center text-on-surface',\n 'bg-[var(--_md-comp-app-bar-container-color)] data-scrolled:bg-[var(--_md-comp-app-bar-on-scroll-container-color)]',\n '[transition-property:background-color]',\n '[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n ],\n // Icon buttons take each slot's content colour.\n navigation:\n 'flex shrink-0 items-center ps-[8px] pe-[4px] text-[color:var(--_md-comp-app-bar-leading-icon-color)] [--md-comp-icon-button-icon-color:currentColor]',\n // Centres the search bar in the free space, 8px from its neighbours and 4px vertically.\n search: 'flex min-w-0 flex-1 justify-center ps-[8px] pe-[8px] pt-[4px] pb-[4px]',\n actions:\n 'flex shrink-0 items-center gap-[8px] ps-[4px] pe-[8px] text-[color:var(--_md-comp-app-bar-trailing-icon-color)] [--md-comp-icon-button-icon-color:currentColor]',\n },\n variants: {\n scrolling: {\n true: { root: 'sticky top-(--m3-app-bar-offset) z-(--md-sys-z-sticky)' },\n false: {},\n },\n },\n defaultVariants: { scrolling: false },\n});\n\n/**\n * The variants of {@link searchBarStyles}, 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 SearchBarStyleProps = VariantProps<typeof searchBarStyles>;\n/**\n * The variants of {@link searchAppBarStyles}, 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 SearchAppBarStyleProps = VariantProps<typeof searchAppBarStyles>;\n/**\n * The variants of {@link searchFieldStyles}, for building on its styles. They are the recipe's keys, not\n * component props: their names follow the classes and may change.\n */\nexport type SearchFieldStyleProps = VariantProps<typeof searchFieldStyles>;\n"],"mappings":";;AA2CA,MAAM,cAAc,CAClB,yDAEA,kEACF;;AAGA,MAAa,kBAAkB,GAAG;CAChC,OAAO;EAEL,MAAM;EAIN,OAAO,CACL,iIACA,mFACF;EAIA,SACE;EAIF,UAAU,CACR,2MACA,8DACF;EACA,OAAO;GACL;GACA;GACA;EACF;EACA,OAAO;GACL;GACA;GACA;GACA;GACA;EACF;EAUA,aAAa;GAGX;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;EACF;EAIA,UAAU;EACV,cACE;EACF,UAAU;GACR;GAEA;GAIA;GACA;GACA;GAEA;GAEA;GACA;GACA;GACA;GACA;GACA,GAAG;EACL;EAOA,iBAAiB;GACf;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;EACF;EAGA,kBAAkB;GAChB;GACA;GACA;GACA;GACA;EACF;EACA,mBAAmB;GACjB;GACA;GACA;GACA;GACA,GAAG;EACL;CACF;CACA,UAAU;;;;;EAKR,UAAU;GAAE,MAAM,EAAE,MAAM,+BAA+B;GAAG,OAAO,CAAC;EAAE;EAEtE,YAAY;GAAE,MAAM,EAAE,OAAO,WAAW;GAAG,OAAO,EAAE,OAAO,YAAY;EAAE;EACzE,aAAa;GAAE,MAAM,EAAE,OAAO,WAAW;GAAG,OAAO,EAAE,OAAO,YAAY;EAAE;;;;;EAK1E,MAAM;GACJ,SAAS,EAAE,OAAO,kDAAkD;GACpE,QAAQ,EAAE,OAAO,uBAAuB;GACxC,UAAU,EAAE,OAAO,+BAA+B;EACpD;;EAEA,WAAW;GAAE,OAAO,CAAC;GAAG,QAAQ,EAAE,OAAO,cAAc;EAAE;;;;;EAKzD,UAAU;GACR,MAAM;IACJ,OAAO;IACP,SAAS;IACT,UAAU;IACV,OACE;GACJ;GACA,OAAO,CAAC;EACV;CACF;CACA,iBAAiB;EACf,UAAU;EACV,YAAY;EACZ,aAAa;EACb,MAAM;EACN,WAAW;EACX,UAAU;CACZ;AACF,CAAC;;;;;AAMD,MAAa,oBAAoB,GAAG;CAClC,OAAO;EAGL,MAAM;EAEN,MAAM;CACR;CACA,UAAU,CAAC;AACb,CAAC;;AAGD,MAAa,qBAAqB,GAAG;CACnC,OAAO;EACL,MAAM;GACJ;GACA;GACA;GACA;GACA;EACF;EAEA,YACE;EAEF,QAAQ;EACR,SACE;CACJ;CACA,UAAU,EACR,WAAW;EACT,MAAM,EAAE,MAAM,yDAAyD;EACvE,OAAO,CAAC;CACV,EACF;CACA,iBAAiB,EAAE,WAAW,MAAM;AACtC,CAAC"}
|
|
1
|
+
{"version":3,"file":"search-styles.js","names":[],"sources":["../../../src/components/search/search-styles.ts"],"sourcesContent":["import { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Values follow Compose Material 3 (SearchBar.kt: SearchBar, AppBarWithSearch,\n * ExpandedDockedSearchBarWithGap, ExpandedFullScreenSearchBar, SearchBarDefaults.InputField;\n * SearchBar / SearchView tokens).\n *\n * Field: 56px, full corners, `surface-container-high`, 360px wide by default and at most\n * 720px. Input and placeholder `body-large`, `on-surface` / `on-surface-variant`. Icons sit\n * in 48px boxes shifted 4px inwards (leading `on-surface`, trailing `on-surface-variant`);\n * the text keeps 4px from the leading and trailing tap targets (the spec's \"Leading icon and\n * label padding (from tap target): 4dp\" and \"Label and trailing icon padding\"; Compose's\n * code leaves none), so it starts 56px in after a leading icon, 16px without one.\n *\n * Docked (with gap): the expanded field, 56px tall whatever the collapsed bar's height\n * (M3's focused search bar container), sits centred over the collapsed one and widens by 12px on\n * each side (M3 Expressive: the margins change from 24dp to 12dp when focused). It morphs from\n * the bar's bounds (position and size) on the default spatial spring and back on the same\n * spring floored at the bar, so it lands on the bar exactly; a dropdown 2px below it (12px corners,\n * `surface-container-high`; with the bar, at least 240px and at most 2/3 of the window\n * tall, per the M3 spec) slides down from half its height on the default spatial spring\n * (fast spatial back) while its content fades in over 100ms after 50ms, and its height follows\n * the results on the default spatial spring; a 32% scrim covers the page.\n *\n * Full screen, in M3 Expressive's contained style: a `surface-container-low` surface grows\n * from the bar's bounds (28px corners) to the whole window on the slow spatial spring\n * (default spatial back), clamped so nothing overshoots; the field keeps its\n * `surface-container-high` pill and moves and grows from the bar to the top, 8px down and\n * 16px from each side, and the content starts 72px down.\n *\n * In a search app bar the field is `surface-container` (`surface-container-highest` once\n * content scrolls under the bar) and fills the free space up to 312px, then half of it,\n * at most 720px (M3 app bar guidelines).\n *\n * The field's colours and corners come from the search bar's component tokens and the\n * views' from the search view's (`search-tokens.ts`, `--md-comp-search-bar-*` and\n * `--md-comp-search-view-*`); the pill, the dropdown and the full-screen view carry\n * `data-tokens`. The search app bar reads the app bar's tokens (`app-bar-tokens.ts`). A\n * consumer class still wins in `cn()`.\n *\n * Every class is written out in full so Tailwind can find it.\n */\n\nconst contentFade = [\n 'opacity-100 starting:opacity-0 data-exiting:opacity-0',\n // Compose: fade in over 100ms after 50ms (standard accelerate), out over 100ms.\n '[transition-delay:0s,50ms] data-exiting:[transition-delay:0s,0s]',\n];\n\n/** Variant definitions for {@link SearchBar}; extend them to add variants. */\nexport const searchBarStyles = tv({\n slots: {\n // The bar's height is set here alone (56px, Compose's); a consumer `h-*` resizes it.\n root: 'flex h-[56px] w-[360px] max-w-[min(720px,100%)] shrink-0',\n // The pill holding the icons and the input fills the bar.\n // Keyboard focus in the input draws Compose's inset focus ring around the pill.\n // Forced colours drop the pill's colour: a system-colour border keeps its edge.\n field: [\n 'focus-ring-inset flex h-full w-full min-w-0 items-center rounded-[var(--_md-comp-search-bar-container-shape)] text-on-surface',\n 'forced-colors:border forced-colors:border-solid forced-colors:border-[CanvasText]',\n ],\n // Icon buttons in the slots take the slot's colour (and so its icon colour token), as\n // in the app bar's slots: Compose colours the search bar's and view's leading icon\n // `on-surface` and trailing icons `on-surface-variant`, buttons included.\n leading:\n 'ms-[4px] flex size-[48px] shrink-0 items-center justify-center text-[color:var(--_md-comp-search-bar-leading-icon-color)] [--md-comp-icon-button-icon-color:currentColor] [&>svg]:size-[24px]',\n // One or two trailing icon buttons, or an avatar (30px) and an icon button (M3). Two\n // 40px buttons sit 8px apart inside 4px of padding, so their 48px targets meet edge to\n // edge within the slot (a single one is centred in 48px, as before).\n trailing: [\n 'me-[4px] flex h-[48px] min-w-[48px] shrink-0 items-center justify-center gap-[8px] px-[4px] text-[color:var(--_md-comp-search-bar-trailing-icon-color)] [--md-comp-icon-button-icon-color:currentColor]',\n '[&>svg]:size-[24px] [&>img]:size-[30px] [&>img]:rounded-full',\n ],\n // A hint or query longer than the field ends in an ellipsis, not mid-letter (as a text\n // field's label); the query only while the field isn't focused, since focused it scrolls.\n input: [\n 'h-full min-w-0 flex-1 bg-transparent text-body-large text-[color:var(--_md-comp-search-bar-input-text-color)] caret-primary outline-none text-ellipsis',\n 'placeholder:text-on-surface-variant placeholder:text-ellipsis',\n '[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none',\n ],\n scrim: [\n 'fixed inset-0 z-(--md-sys-z-scrim) bg-scrim/32 opacity-100 data-exiting:pointer-events-none',\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 * Docked view: the panel sits over the collapsed bar and morphs from the bar's bounds\n * (`--m3-search-left/top/width/height`) to the focused field's (the `-dx/-dy/-dw/-dh`\n * deltas) as `--m3-search-progress` runs 0 → 1, as Compose lerps the layout with its\n * search bar state's progress. `--m3-search-p` is that progress floored at 0 (Compose's\n * `coerceAtLeast(0f)`): the spring may overshoot past the focused bounds on the way in,\n * but never past the bar's on the way back, so the field lands on the bar exactly.\n * Reduced motion: the progress stays at 1, so the field is at its focused size at once.\n */\n dockedPanel: [\n // Leaving, the view takes no presses: the spring's tail runs on after the field has\n // landed on the bar, and a press there ran a suggestion of the invisible dropdown.\n 'fixed z-(--md-sys-z-sheet) flex flex-col outline-none data-exiting:pointer-events-none',\n '[--m3-search-progress:1] [--m3-search-p:max(0,var(--m3-search-progress))]',\n 'motion-safe:starting:[--m3-search-progress:0] motion-safe:data-exiting:[--m3-search-progress:0]',\n 'top-[calc(var(--m3-search-top)_+_var(--m3-search-p)_*_var(--m3-search-dy))]',\n 'left-[calc(var(--m3-search-left)_+_var(--m3-search-p)_*_var(--m3-search-dx))]',\n 'w-[calc(var(--m3-search-width)_+_var(--m3-search-p)_*_var(--m3-search-dw))]',\n // Both ways on the default spatial spring (Compose's collapse spec).\n '[transition-property:--m3-search-progress]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n ],\n // The focused field is 56px (M3), even when the collapsed bar is resized; it grows from\n // the bar's height with the panel.\n // The full-window layer the docked view sits in (React Aria's underlay).\n underlay: 'fixed inset-0 z-(--md-sys-z-sheet) data-exiting:pointer-events-none',\n dockedHeader:\n 'flex h-[calc(var(--m3-search-height)_+_var(--m3-search-p)_*_var(--m3-search-dh))] shrink-0',\n dropdown: [\n 'mt-[2px] overflow-y-auto overscroll-contain data-resizing:overflow-y-hidden data-exiting:pointer-events-none',\n // 58px is the field and the gap above the dropdown.\n 'min-h-[min(182px,calc(66.67dvh-58px))] max-h-[calc(66.67dvh-58px)]',\n // The height follows the content (`--m3-search-content-height`, measured) within those\n // bounds, so it animates as the results change. Without the measurement (before\n // hydration, or under reduced motion) it's `auto`.\n 'h-[clamp(min(182px,calc(66.67dvh_-_58px)),var(--m3-search-content-height),calc(66.67dvh_-_58px))]',\n 'rounded-[var(--_md-comp-search-view-docked-container-shape)] bg-[var(--_md-comp-search-view-container-color)] text-on-surface',\n 'forced-colors:border forced-colors:border-solid forced-colors:border-[CanvasText]',\n // Reduced motion: it only fades.\n 'translate-y-0 motion-safe:starting:-translate-y-1/2 motion-safe:data-exiting:-translate-y-1/2',\n // The delays below cycle, so the height (third) starts at once like the slide.\n '[transition-property:translate,opacity,height]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-default-duration),100ms,var(--md-sys-motion-spring-spatial-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing),cubic-bezier(0.3,0,1,1),var(--md-sys-motion-spring-spatial-default-easing)]',\n 'data-exiting:[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration),100ms,var(--md-sys-motion-spring-spatial-fast-duration)]',\n 'data-exiting:[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing),cubic-bezier(0,0,0,1),var(--md-sys-motion-spring-spatial-fast-easing)]',\n ...contentFade,\n ],\n /*\n * Full-screen view: the surface grows from the bar's bounds to the window as\n * `--m3-search-progress` runs 0 → 1, through a clip (nothing is transformed). Compose\n * clamps the full-screen progress to 0..1 (`coerceIn`), so `--m3-search-p` is clamped too:\n * the clip and the field never overshoot the window's or the bar's bounds.\n */\n fullScreenPanel: [\n 'fixed inset-0 z-(--md-sys-z-sheet) bg-[var(--_md-comp-search-view-container-color)] text-on-surface outline-none data-exiting:pointer-events-none',\n '[--m3-search-progress:1] [--m3-search-p:clamp(0,var(--m3-search-progress),1)]',\n 'motion-safe:starting:[--m3-search-progress:0] motion-safe:data-exiting:[--m3-search-progress:0]',\n '[clip-path:inset(calc((1_-_var(--m3-search-p))_*_var(--m3-search-top))_calc((1_-_var(--m3-search-p))_*_var(--m3-search-right))_calc((1_-_var(--m3-search-p))_*_var(--m3-search-bottom))_calc((1_-_var(--m3-search-p))_*_var(--m3-search-left))_round_calc((1_-_var(--m3-search-p))_*_28px))]',\n 'motion-safe:[transition-property:--m3-search-progress]',\n 'motion-safe:[transition-duration:var(--md-sys-motion-spring-spatial-slow-duration)]',\n 'motion-safe:[transition-timing-function:var(--md-sys-motion-spring-spatial-slow-easing)]',\n 'motion-safe:data-exiting:[transition-duration:var(--md-sys-motion-spring-spatial-default-duration)]',\n 'motion-safe:data-exiting:[transition-timing-function:var(--md-sys-motion-spring-spatial-default-easing)]',\n // Reduced motion: the view fades in and out over the window instead of growing.\n 'motion-reduce:starting:opacity-0 motion-reduce:data-exiting:opacity-0',\n 'motion-reduce:[transition-property:opacity]',\n 'motion-reduce:[transition-duration:var(--md-sys-motion-spring-effects-fast-duration)]',\n 'motion-reduce:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n // Compose's full-screen field is 56px whatever the bar's size, 8px down and 16px from each\n // side; it moves and grows there from the bar's bounds with the surface.\n fullScreenHeader: [\n 'absolute',\n 'start-[calc(var(--m3-search-start)_+_var(--m3-search-p)_*_(16px_-_var(--m3-search-start)))]',\n 'top-[calc(var(--m3-search-top)_+_var(--m3-search-p)_*_(8px_-_var(--m3-search-top)))]',\n 'w-[calc(var(--m3-search-width)_+_var(--m3-search-p)_*_(100%_-_32px_-_var(--m3-search-width)))]',\n 'h-[calc(var(--m3-search-height)_+_var(--m3-search-p)_*_(56px_-_var(--m3-search-height)))]',\n ],\n fullScreenContent: [\n 'absolute inset-x-0 top-[72px] bottom-0 overflow-y-auto overscroll-contain',\n '[transition-property:opacity] [transition-duration:100ms]',\n '[transition-timing-function:cubic-bezier(0.3,0,1,1)]',\n 'data-exiting:[transition-timing-function:cubic-bezier(0,0,0,1)]',\n ...contentFade,\n ],\n },\n variants: {\n /**\n * Inside a search app bar the bar fills the free space up to 312px, then grows to half\n * of it (M3), up to 720px.\n */\n inAppBar: { true: { root: 'w-[max(min(100%,312px),50%)]' }, false: {} },\n // 4px from a tap target (M3), else 16px from the pill's edge.\n hasLeading: { true: { input: 'ps-[4px]' }, false: { input: 'ps-[16px]' } },\n hasTrailing: { true: { input: 'pe-[4px]' }, false: { input: 'pe-[16px]' } },\n /**\n * Container colour: the bar's own; in a search app bar `surface-container`, and one\n * step higher than the bar once content scrolls under it.\n */\n tone: {\n default: { field: 'bg-[var(--_md-comp-search-bar-container-color)]' },\n appBar: { field: 'bg-surface-container' },\n scrolled: { field: 'bg-surface-container-highest' },\n },\n /** Centre the placeholder and the query while the bar is collapsed. */\n textAlign: { start: {}, center: { input: 'text-center' } },\n /**\n * A disabled `SearchField`: M3's disabled colours, the content `on-surface` at 38% and the\n * container at 12%; forced colours draw it in `GrayText`.\n */\n disabled: {\n true: {\n field: 'bg-on-surface/12 forced-colors:border-[GrayText]',\n leading: 'text-on-surface/38 forced-colors:text-[GrayText]',\n trailing: 'text-on-surface/38 forced-colors:text-[GrayText]',\n input:\n 'cursor-default text-on-surface/38 placeholder:text-on-surface/38 forced-colors:text-[GrayText]',\n },\n false: {},\n },\n },\n defaultVariants: {\n inAppBar: false,\n hasLeading: false,\n hasTrailing: false,\n tone: 'default',\n textAlign: 'start',\n disabled: false,\n },\n});\n\n/**\n * Variant definitions for {@link SearchField}'s root. Its pill and the parts in it are the search\n * bar's ({@link searchBarStyles}), so the two can't drift.\n */\nexport const searchFieldStyles = tv({\n slots: {\n // 56px, the search bar's height, less 4px a density step (44px at -3), filling its\n // container: it filters a page, a list or a pane in place. A consumer `w-*` or `h-*` wins.\n root: 'flex h-[calc(56px+var(--md-sys-density,0)*4px)] w-full min-w-0',\n // The 48px icon slots keep to a dense pill's height; their buttons keep 48px targets.\n slot: 'max-h-full',\n },\n variants: {},\n});\n\n/** Variant definitions for {@link SearchAppBar}. */\nexport const searchAppBarStyles = tv({\n slots: {\n root: [\n 'flex min-h-[64px] w-full items-center text-on-surface',\n 'bg-[var(--_md-comp-app-bar-container-color)] data-scrolled:bg-[var(--_md-comp-app-bar-on-scroll-container-color)]',\n '[transition-property:background-color]',\n '[transition-duration:var(--md-sys-motion-spring-effects-default-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-effects-default-easing)]',\n ],\n // Icon buttons take each slot's content colour.\n navigation:\n 'flex shrink-0 items-center ps-[8px] pe-[4px] text-[color:var(--_md-comp-app-bar-leading-icon-color)] [--md-comp-icon-button-icon-color:currentColor]',\n // Centres the search bar in the free space, 8px from its neighbours and 4px vertically.\n search: 'flex min-w-0 flex-1 justify-center ps-[8px] pe-[8px] pt-[4px] pb-[4px]',\n actions:\n 'flex shrink-0 items-center gap-[8px] ps-[4px] pe-[8px] text-[color:var(--_md-comp-app-bar-trailing-icon-color)] [--md-comp-icon-button-icon-color:currentColor]',\n },\n variants: {\n scrolling: {\n true: { root: 'sticky top-(--m3-app-bar-offset) z-(--md-sys-z-sticky)' },\n false: {},\n },\n },\n defaultVariants: { scrolling: false },\n});\n\n/**\n * The variants of {@link searchBarStyles}, 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 SearchBarStyleProps = VariantProps<typeof searchBarStyles>;\n/**\n * The variants of {@link searchAppBarStyles}, 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 SearchAppBarStyleProps = VariantProps<typeof searchAppBarStyles>;\n/**\n * The variants of {@link searchFieldStyles}, for building on its styles. They are the recipe's keys, not\n * component props: their names follow the classes and may change.\n */\nexport type SearchFieldStyleProps = VariantProps<typeof searchFieldStyles>;\n"],"mappings":";;AA2CA,MAAM,cAAc,CAClB,yDAEA,kEACF;;AAGA,MAAa,kBAAkB,GAAG;CAChC,OAAO;EAEL,MAAM;EAIN,OAAO,CACL,iIACA,mFACF;EAIA,SACE;EAIF,UAAU,CACR,2MACA,8DACF;EAGA,OAAO;GACL;GACA;GACA;EACF;EACA,OAAO;GACL;GACA;GACA;GACA;GACA;EACF;EAUA,aAAa;GAGX;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;EACF;EAIA,UAAU;EACV,cACE;EACF,UAAU;GACR;GAEA;GAIA;GACA;GACA;GAEA;GAEA;GACA;GACA;GACA;GACA;GACA,GAAG;EACL;EAOA,iBAAiB;GACf;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GAEA;GACA;GACA;GACA;EACF;EAGA,kBAAkB;GAChB;GACA;GACA;GACA;GACA;EACF;EACA,mBAAmB;GACjB;GACA;GACA;GACA;GACA,GAAG;EACL;CACF;CACA,UAAU;;;;;EAKR,UAAU;GAAE,MAAM,EAAE,MAAM,+BAA+B;GAAG,OAAO,CAAC;EAAE;EAEtE,YAAY;GAAE,MAAM,EAAE,OAAO,WAAW;GAAG,OAAO,EAAE,OAAO,YAAY;EAAE;EACzE,aAAa;GAAE,MAAM,EAAE,OAAO,WAAW;GAAG,OAAO,EAAE,OAAO,YAAY;EAAE;;;;;EAK1E,MAAM;GACJ,SAAS,EAAE,OAAO,kDAAkD;GACpE,QAAQ,EAAE,OAAO,uBAAuB;GACxC,UAAU,EAAE,OAAO,+BAA+B;EACpD;;EAEA,WAAW;GAAE,OAAO,CAAC;GAAG,QAAQ,EAAE,OAAO,cAAc;EAAE;;;;;EAKzD,UAAU;GACR,MAAM;IACJ,OAAO;IACP,SAAS;IACT,UAAU;IACV,OACE;GACJ;GACA,OAAO,CAAC;EACV;CACF;CACA,iBAAiB;EACf,UAAU;EACV,YAAY;EACZ,aAAa;EACb,MAAM;EACN,WAAW;EACX,UAAU;CACZ;AACF,CAAC;;;;;AAMD,MAAa,oBAAoB,GAAG;CAClC,OAAO;EAGL,MAAM;EAEN,MAAM;CACR;CACA,UAAU,CAAC;AACb,CAAC;;AAGD,MAAa,qBAAqB,GAAG;CACnC,OAAO;EACL,MAAM;GACJ;GACA;GACA;GACA;GACA;EACF;EAEA,YACE;EAEF,QAAQ;EACR,SACE;CACJ;CACA,UAAU,EACR,WAAW;EACT,MAAM,EAAE,MAAM,yDAAyD;EACvE,OAAO,CAAC;CACV,EACF;CACA,iBAAiB,EAAE,WAAW,MAAM;AACtC,CAAC"}
|
|
@@ -156,6 +156,8 @@ function SliderRoot(props) {
|
|
|
156
156
|
state,
|
|
157
157
|
trackRef,
|
|
158
158
|
name,
|
|
159
|
+
groupId: groupProps.id,
|
|
160
|
+
labelledBy,
|
|
159
161
|
label: range ? index === 0 ? labels.start : labels.end : void 0,
|
|
160
162
|
errorMessage,
|
|
161
163
|
valueLabel,
|
|
@@ -223,7 +225,7 @@ const INCREASE_KEYS = {
|
|
|
223
225
|
ArrowDown: false
|
|
224
226
|
}
|
|
225
227
|
};
|
|
226
|
-
function Thumb({ index, at, state, trackRef, name, label, errorMessage, valueLabel, styles, classNames }) {
|
|
228
|
+
function Thumb({ index, at, state, trackRef, name, groupId, labelledBy, label, errorMessage, valueLabel, styles, classNames }) {
|
|
227
229
|
const inputRef = useRef(null);
|
|
228
230
|
const { thumbProps, inputProps, isDragging } = useSliderThumb({
|
|
229
231
|
index,
|
|
@@ -233,8 +235,10 @@ function Thumb({ index, at, state, trackRef, name, label, errorMessage, valueLab
|
|
|
233
235
|
"aria-label": label,
|
|
234
236
|
"aria-errormessage": errorMessage
|
|
235
237
|
}, state);
|
|
238
|
+
const thumbLabelledBy = labelledBy?.trim() ? inputProps["aria-labelledby"]?.split(" ").flatMap((id) => id === groupId ? labelledBy.trim().split(/\s+/) : [id]).join(" ") : inputProps["aria-labelledby"];
|
|
236
239
|
const thumbInputProps = {
|
|
237
240
|
...inputProps,
|
|
241
|
+
"aria-labelledby": thumbLabelledBy,
|
|
238
242
|
"aria-describedby": inputProps["aria-describedby"] || void 0,
|
|
239
243
|
"aria-details": inputProps["aria-details"] || void 0
|
|
240
244
|
};
|