@windmorph/react 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/components/carousel/carousel-styles.d.ts +6 -15
  2. package/dist/components/carousel/carousel-styles.js +4 -7
  3. package/dist/components/carousel/carousel-styles.js.map +1 -1
  4. package/dist/components/chip/chip-tokens.d.ts +4 -4
  5. package/dist/components/date-picker/DatePicker.js +11 -4
  6. package/dist/components/date-picker/DatePicker.js.map +1 -1
  7. package/dist/components/date-picker/date-picker-styles.d.ts +3 -3
  8. package/dist/components/date-picker/date-picker-tokens.d.ts +1 -1
  9. package/dist/components/dialog/dialog-tokens.d.ts +1 -1
  10. package/dist/components/list/List.d.ts +9 -0
  11. package/dist/components/list/List.js +7 -4
  12. package/dist/components/list/List.js.map +1 -1
  13. package/dist/components/list/list-styles.js +6 -6
  14. package/dist/components/list/list-styles.js.map +1 -1
  15. package/dist/components/list/list-tokens.d.ts +1 -1
  16. package/dist/components/list/list-tokens.js +50 -0
  17. package/dist/components/list/list-tokens.js.map +1 -1
  18. package/dist/components/menu/menu-tokens.d.ts +1 -1
  19. package/dist/components/navigation/NavigationRail.js +52 -15
  20. package/dist/components/navigation/NavigationRail.js.map +1 -1
  21. package/dist/components/search/SearchBar.js +19 -34
  22. package/dist/components/search/SearchBar.js.map +1 -1
  23. package/dist/components/search/SearchField.d.ts +111 -0
  24. package/dist/components/search/SearchField.js +128 -0
  25. package/dist/components/search/SearchField.js.map +1 -0
  26. package/dist/components/search/search-field-frame.js +36 -0
  27. package/dist/components/search/search-field-frame.js.map +1 -0
  28. package/dist/components/search/search-styles.d.ts +58 -0
  29. package/dist/components/search/search-styles.js +27 -2
  30. package/dist/components/search/search-styles.js.map +1 -1
  31. package/dist/components/select/search-list.js +8 -13
  32. package/dist/components/select/search-list.js.map +1 -1
  33. package/dist/components/snackbar/snackbar-tokens.d.ts +1 -1
  34. package/dist/components/text-field/TextField.js +19 -5
  35. package/dist/components/text-field/TextField.js.map +1 -1
  36. package/dist/components/toolbar/toolbar-styles.js +2 -2
  37. package/dist/components/toolbar/toolbar-styles.js.map +1 -1
  38. package/dist/components/tooltip/tooltip-tokens.d.ts +1 -1
  39. package/dist/index.d.ts +3 -2
  40. package/dist/index.js +3 -2
  41. package/dist/styles/generated/components.css +5 -0
  42. package/dist/styles.compiled.css +1 -1
  43. package/package.json +1 -1
@@ -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 defaultVariants: {\n inAppBar: false,\n hasLeading: false,\n hasTrailing: false,\n tone: 'default',\n textAlign: 'start',\n },\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"],"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;CAC3D;CACA,iBAAiB;EACf,UAAU;EACV,YAAY;EACZ,aAAa;EACb,MAAM;EACN,WAAW;CACb;AACF,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 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"}
@@ -4,9 +4,10 @@ import { FOCUSABLE_DISABLED_OPTIONS } from "../../utils/focusable-disabled.js";
4
4
  import { menuStyles } from "../menu/menu-styles.js";
5
5
  import { OptionList } from "./option-list.js";
6
6
  import { searchBarStyles } from "../search/search-styles.js";
7
+ import { SearchFieldFrame } from "../search/search-field-frame.js";
7
8
  import { useCallback, useEffect, useRef, useState } from "react";
8
9
  import { jsx, jsxs } from "react/jsx-runtime";
9
- import { useAutocomplete, useFocusRing, useSearchField } from "react-aria";
10
+ import { useAutocomplete, useSearchField } from "react-aria";
10
11
  import { useListState } from "react-stately";
11
12
  //#region src/components/select/search-list.tsx
12
13
  /** Material Symbols `search`. */
@@ -71,10 +72,6 @@ function SearchList({ listProps, search, onSearchChange, filter, autoFocusSearch
71
72
  value: search,
72
73
  setValue: onSearchChange
73
74
  }, inputRef);
74
- const { focusProps, isFocusVisible } = useFocusRing({
75
- within: true,
76
- isTextInput: true
77
- });
78
75
  useEffect(() => {
79
76
  if (autoFocusSearch) inputRef.current?.focus();
80
77
  }, [autoFocusSearch]);
@@ -90,26 +87,24 @@ function SearchList({ listProps, search, onSearchChange, filter, autoFocusSearch
90
87
  className: cn("flex min-h-0 flex-col", embedded ? "flex-1" : [menu.group(), "max-h-[inherit] overflow-hidden"]),
91
88
  children: [/* @__PURE__ */ jsx("div", {
92
89
  className: cn("flex h-[calc(52px+var(--md-sys-density,0)*4px)] shrink-0 pb-[4px]", embedded && "sticky top-0 z-[1] bg-[var(--_md-comp-sheet-bottom-docked-container-color)]"),
93
- children: /* @__PURE__ */ jsxs("div", {
94
- ...focusProps,
95
- "data-tokens": "md-comp-search-bar",
96
- "data-focus-visible": isFocusVisible || void 0,
90
+ children: /* @__PURE__ */ jsx(SearchFieldFrame, {
97
91
  onPointerDown: (event) => {
98
92
  if (event.target !== event.currentTarget) return;
99
93
  event.preventDefault();
100
94
  inputRef.current?.focus();
101
95
  },
102
- className: field.field({ class: cn(denseSearchTarget, classNames?.search) }),
103
- children: [/* @__PURE__ */ jsx("span", {
96
+ className: cn(denseSearchTarget, classNames?.search),
97
+ leading: /* @__PURE__ */ jsx("span", {
104
98
  "data-slot": "leading",
105
99
  className: field.leading({ class: "h-full" }),
106
100
  children: /* @__PURE__ */ jsx(SearchIcon, {})
107
- }), /* @__PURE__ */ jsx("input", {
101
+ }),
102
+ children: /* @__PURE__ */ jsx("input", {
108
103
  ...inputProps,
109
104
  ref: inputRef,
110
105
  "data-slot": "input",
111
106
  className: field.input()
112
- })]
107
+ })
113
108
  })
114
109
  }), /* @__PURE__ */ jsx(OptionList, {
115
110
  embedded: true,
@@ -1 +1 @@
1
- {"version":3,"file":"search-list.js","names":[],"sources":["../../../src/components/select/search-list.tsx"],"sourcesContent":["'use client';\n\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type HTMLAttributes,\n type RefObject,\n} from 'react';\nimport { useAutocomplete, useFocusRing, useSearchField } from 'react-aria';\nimport { useListState, type Node } from 'react-stately';\nimport type { ListProps } from 'react-stately/useListState';\nimport { cn } from '../../utils/cn';\nimport { menuStyles } from '../menu/menu-styles';\nimport { searchBarStyles } from '../search/search-styles';\nimport { FOCUSABLE_DISABLED_OPTIONS, OptionList, type OptionListClassNames } from './option-list';\n\n/** Material Symbols `search`. */\nconst SearchIcon = () => (\n <svg viewBox=\"0 -960 960 960\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z\" />\n </svg>\n);\n\n/**\n * Keeps the items that match, and the sections that still hold one (as React Stately's\n * combobox filters), so a section with no matches disappears with its heading.\n */\nfunction filterNodes<T>(nodes: Iterable<Node<T>>, matches: (node: Node<T>) => boolean): Node<T>[] {\n const kept: Node<T>[] = [];\n for (const node of nodes) {\n if (node.type === 'section') {\n const children = filterNodes(node.childNodes, matches);\n if (children.some((child) => child.type === 'item')) {\n kept.push({ ...node, childNodes: children });\n }\n } else if (node.type !== 'item' || matches(node)) {\n kept.push(node);\n }\n }\n return kept;\n}\n\nexport interface SearchListProps<T> {\n /** The options, their selection and disabled keys, as for `useListState`. */\n listProps: ListProps<T>;\n /** The typed text. */\n search: string;\n onSearchChange: (search: string) => void;\n /** Whether an option matches the search; `null` when the app filters the options itself. */\n filter: ((textValue: string, search: string, key: Node<T>['key']) => boolean) | null;\n /** Focus the search when the list opens (in a menu; a sheet shows the list first). */\n autoFocusSearch: boolean;\n /** Names the list (the field's label): `aria-labelledby`, or `aria-label` without one. */\n labelling: { 'aria-label'?: string | undefined; 'aria-labelledby'?: string | undefined };\n /** In a menu, the panel is the dialog the field opens (`role`, `id`, its name). */\n dialogProps?: HTMLAttributes<HTMLDivElement> | undefined;\n labels: { search: string; noResults: string; loading: string };\n loading: boolean;\n isBlocked: ((key: Node<T>['key']) => boolean) | undefined;\n /** In a sheet: no menu panel of its own. */\n embedded: boolean;\n classNames?: (OptionListClassNames & { search?: string }) | undefined;\n}\n\n/*\n * A dense search field (44, 40 or 36px tall at -1 to -3) extends its hit area by up to 4px\n * below it (the gap above the list) and up to 4px above it (the panel's padding, which clips\n * anything further): 48px at -1 and -2, 44px at -3. Like dense menu rows, it can't reach 48px\n * at -3 without covering the first option; it stays well above WCAG 2.5.8's 24px.\n */\nconst denseSearchTarget = [\n 'relative',\n \"before:absolute before:inset-x-0 before:bottom-full before:h-[clamp(0px,calc(var(--md-sys-density,0)*-4px-clamp(0px,calc(var(--md-sys-density,0)*-2px),4px)),4px)] before:content-['']\",\n \"after:absolute after:inset-x-0 after:top-full after:h-[clamp(0px,calc(var(--md-sys-density,0)*-2px),4px)] after:content-['']\",\n];\n\n/**\n * A search field over an option list, wired as one editable combobox with list autocomplete\n * (React Aria `useAutocomplete`): typing filters the options, the arrow keys move through\n * them while focus stays in the search (`aria-activedescendant`), Enter chooses, and Escape\n * clears the search, then reaches the menu or sheet to close it. The search is the M3 search\n * bar's field at 48px, M3's minimum touch target, so it doesn't dominate the 44px rows.\n * Used by `Select`'s `searchable` and `PhoneField`'s country field.\n */\nexport function SearchList<T>({\n listProps,\n search,\n onSearchChange,\n filter,\n autoFocusSearch,\n labelling,\n dialogProps,\n labels,\n loading,\n isBlocked,\n embedded,\n classNames,\n}: SearchListProps<T>) {\n const matches = useCallback(\n (nodes: Iterable<Node<T>>) =>\n filter ? filterNodes(nodes, (node) => filter(node.textValue, search, node.key)) : [...nodes],\n [filter, search],\n );\n const state = useListState<T>({ ...listProps, ...FOCUSABLE_DISABLED_OPTIONS, filter: matches });\n\n const [focusedNodeId, setFocusedNodeId] = useState<string | null>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const listBoxRef = useRef<HTMLUListElement>(null);\n const {\n inputProps: autocompleteInputProps,\n collectionProps,\n collectionRef,\n } = useAutocomplete(\n { inputRef, collectionRef: listBoxRef },\n { inputValue: search, setInputValue: onSearchChange, focusedNodeId, setFocusedNodeId },\n );\n const { inputProps } = useSearchField(\n { ...autocompleteInputProps, 'aria-label': labels.search, placeholder: labels.search },\n { value: search, setValue: onSearchChange },\n inputRef,\n );\n const { focusProps, isFocusVisible } = useFocusRing({ within: true, isTextInput: true });\n\n useEffect(() => {\n if (autoFocusSearch) inputRef.current?.focus();\n }, [autoFocusSearch]);\n\n const field = searchBarStyles({ hasLeading: true });\n const menu = menuStyles({ variant: 'standard', groupPosition: 'only' });\n\n return (\n <div\n {...dialogProps}\n // The menu's component tokens (menu-tokens.ts) for its panel and options.\n data-tokens=\"md-comp-menus\"\n data-variant=\"standard\"\n className={cn(\n 'flex min-h-0 flex-col',\n embedded ? 'flex-1' : [menu.group(), 'max-h-[inherit] overflow-hidden'],\n )}\n >\n {/* 48px (4px less per density step), 4px above the list, like the panel's 4px padding\n around it. In a sheet the whole content scrolls (its swipe-to-dismiss reads that\n scroller's position), so the search sticks to the top, over the options passing under. */}\n <div\n className={cn(\n 'flex h-[calc(52px+var(--md-sys-density,0)*4px)] shrink-0 pb-[4px]',\n embedded && 'sticky top-0 z-[1] bg-[var(--_md-comp-sheet-bottom-docked-container-color)]',\n )}\n >\n <div\n {...focusProps}\n data-tokens=\"md-comp-search-bar\"\n data-focus-visible={isFocusVisible || undefined}\n // A press on the hit area around a dense field (below) puts the caret in the input.\n onPointerDown={(event) => {\n if (event.target !== event.currentTarget) return;\n event.preventDefault();\n inputRef.current?.focus();\n }}\n className={field.field({ class: cn(denseSearchTarget, classNames?.search) })}\n >\n <span data-slot=\"leading\" className={field.leading({ class: 'h-full' })}>\n <SearchIcon />\n </span>\n <input {...inputProps} ref={inputRef} data-slot=\"input\" className={field.input()} />\n </div>\n </div>\n <OptionList\n embedded\n animateFiltering\n listBoxProps={{\n ...collectionProps,\n ...labelling,\n // Escape belongs to the search and then the menu; the list must not take it to\n // clear the selection (which also kept a chosen option's menu from closing).\n escapeKeyBehavior: 'none',\n }}\n state={state}\n listBoxRef={collectionRef as RefObject<HTMLUListElement | null>}\n emptyLabel={labels.noResults}\n loading={loading}\n loadingLabel={labels.loading}\n isBlocked={isBlocked}\n classNames={classNames}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"search-list.js","names":[],"sources":["../../../src/components/select/search-list.tsx"],"sourcesContent":["'use client';\n\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type HTMLAttributes,\n type RefObject,\n} from 'react';\nimport { useAutocomplete, useSearchField } from 'react-aria';\nimport { useListState, type Node } from 'react-stately';\nimport type { ListProps } from 'react-stately/useListState';\nimport { cn } from '../../utils/cn';\nimport { menuStyles } from '../menu/menu-styles';\nimport { SearchFieldFrame } from '../search/search-field-frame';\nimport { searchBarStyles } from '../search/search-styles';\nimport { FOCUSABLE_DISABLED_OPTIONS, OptionList, type OptionListClassNames } from './option-list';\n\n/** Material Symbols `search`. */\nconst SearchIcon = () => (\n <svg viewBox=\"0 -960 960 960\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z\" />\n </svg>\n);\n\n/**\n * Keeps the items that match, and the sections that still hold one (as React Stately's\n * combobox filters), so a section with no matches disappears with its heading.\n */\nfunction filterNodes<T>(nodes: Iterable<Node<T>>, matches: (node: Node<T>) => boolean): Node<T>[] {\n const kept: Node<T>[] = [];\n for (const node of nodes) {\n if (node.type === 'section') {\n const children = filterNodes(node.childNodes, matches);\n if (children.some((child) => child.type === 'item')) {\n kept.push({ ...node, childNodes: children });\n }\n } else if (node.type !== 'item' || matches(node)) {\n kept.push(node);\n }\n }\n return kept;\n}\n\nexport interface SearchListProps<T> {\n /** The options, their selection and disabled keys, as for `useListState`. */\n listProps: ListProps<T>;\n /** The typed text. */\n search: string;\n onSearchChange: (search: string) => void;\n /** Whether an option matches the search; `null` when the app filters the options itself. */\n filter: ((textValue: string, search: string, key: Node<T>['key']) => boolean) | null;\n /** Focus the search when the list opens (in a menu; a sheet shows the list first). */\n autoFocusSearch: boolean;\n /** Names the list (the field's label): `aria-labelledby`, or `aria-label` without one. */\n labelling: { 'aria-label'?: string | undefined; 'aria-labelledby'?: string | undefined };\n /** In a menu, the panel is the dialog the field opens (`role`, `id`, its name). */\n dialogProps?: HTMLAttributes<HTMLDivElement> | undefined;\n labels: { search: string; noResults: string; loading: string };\n loading: boolean;\n isBlocked: ((key: Node<T>['key']) => boolean) | undefined;\n /** In a sheet: no menu panel of its own. */\n embedded: boolean;\n classNames?: (OptionListClassNames & { search?: string }) | undefined;\n}\n\n/*\n * A dense search field (44, 40 or 36px tall at -1 to -3) extends its hit area by up to 4px\n * below it (the gap above the list) and up to 4px above it (the panel's padding, which clips\n * anything further): 48px at -1 and -2, 44px at -3. Like dense menu rows, it can't reach 48px\n * at -3 without covering the first option; it stays well above WCAG 2.5.8's 24px.\n */\nconst denseSearchTarget = [\n 'relative',\n \"before:absolute before:inset-x-0 before:bottom-full before:h-[clamp(0px,calc(var(--md-sys-density,0)*-4px-clamp(0px,calc(var(--md-sys-density,0)*-2px),4px)),4px)] before:content-['']\",\n \"after:absolute after:inset-x-0 after:top-full after:h-[clamp(0px,calc(var(--md-sys-density,0)*-2px),4px)] after:content-['']\",\n];\n\n/**\n * A search field over an option list, wired as one editable combobox with list autocomplete\n * (React Aria `useAutocomplete`): typing filters the options, the arrow keys move through\n * them while focus stays in the search (`aria-activedescendant`), Enter chooses, and Escape\n * clears the search, then reaches the menu or sheet to close it. The search is the M3 search\n * bar's field at 48px, M3's minimum touch target, so it doesn't dominate the 44px rows.\n * Used by `Select`'s `searchable` and `PhoneField`'s country field.\n */\nexport function SearchList<T>({\n listProps,\n search,\n onSearchChange,\n filter,\n autoFocusSearch,\n labelling,\n dialogProps,\n labels,\n loading,\n isBlocked,\n embedded,\n classNames,\n}: SearchListProps<T>) {\n const matches = useCallback(\n (nodes: Iterable<Node<T>>) =>\n filter ? filterNodes(nodes, (node) => filter(node.textValue, search, node.key)) : [...nodes],\n [filter, search],\n );\n const state = useListState<T>({ ...listProps, ...FOCUSABLE_DISABLED_OPTIONS, filter: matches });\n\n const [focusedNodeId, setFocusedNodeId] = useState<string | null>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const listBoxRef = useRef<HTMLUListElement>(null);\n const {\n inputProps: autocompleteInputProps,\n collectionProps,\n collectionRef,\n } = useAutocomplete(\n { inputRef, collectionRef: listBoxRef },\n { inputValue: search, setInputValue: onSearchChange, focusedNodeId, setFocusedNodeId },\n );\n const { inputProps } = useSearchField(\n { ...autocompleteInputProps, 'aria-label': labels.search, placeholder: labels.search },\n { value: search, setValue: onSearchChange },\n inputRef,\n );\n\n useEffect(() => {\n if (autoFocusSearch) inputRef.current?.focus();\n }, [autoFocusSearch]);\n\n const field = searchBarStyles({ hasLeading: true });\n const menu = menuStyles({ variant: 'standard', groupPosition: 'only' });\n\n return (\n <div\n {...dialogProps}\n // The menu's component tokens (menu-tokens.ts) for its panel and options.\n data-tokens=\"md-comp-menus\"\n data-variant=\"standard\"\n className={cn(\n 'flex min-h-0 flex-col',\n embedded ? 'flex-1' : [menu.group(), 'max-h-[inherit] overflow-hidden'],\n )}\n >\n {/* 48px (4px less per density step), 4px above the list, like the panel's 4px padding\n around it. In a sheet the whole content scrolls (its swipe-to-dismiss reads that\n scroller's position), so the search sticks to the top, over the options passing under. */}\n <div\n className={cn(\n 'flex h-[calc(52px+var(--md-sys-density,0)*4px)] shrink-0 pb-[4px]',\n embedded && 'sticky top-0 z-[1] bg-[var(--_md-comp-sheet-bottom-docked-container-color)]',\n )}\n >\n {/* The search bar's pill, as `SearchField` draws it, at 48px. */}\n <SearchFieldFrame\n // A press on the hit area around a dense field (below) puts the caret in the input.\n onPointerDown={(event) => {\n if (event.target !== event.currentTarget) return;\n event.preventDefault();\n inputRef.current?.focus();\n }}\n className={cn(denseSearchTarget, classNames?.search)}\n leading={\n <span data-slot=\"leading\" className={field.leading({ class: 'h-full' })}>\n <SearchIcon />\n </span>\n }\n >\n <input {...inputProps} ref={inputRef} data-slot=\"input\" className={field.input()} />\n </SearchFieldFrame>\n </div>\n <OptionList\n embedded\n animateFiltering\n listBoxProps={{\n ...collectionProps,\n ...labelling,\n // Escape belongs to the search and then the menu; the list must not take it to\n // clear the selection (which also kept a chosen option's menu from closing).\n escapeKeyBehavior: 'none',\n }}\n state={state}\n listBoxRef={collectionRef as RefObject<HTMLUListElement | null>}\n emptyLabel={labels.noResults}\n loading={loading}\n loadingLabel={labels.loading}\n isBlocked={isBlocked}\n classNames={classNames}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAoBA,MAAM,mBACJ,oBAAC,OAAD;CAAK,SAAQ;CAAiB,MAAK;CAAe,eAAY;CAC5D,UAAA,oBAAC,QAAD,EAAM,GAAE,8PAA+P,CAAA;AACpQ,CAAA;;;;;AAOP,SAAS,YAAe,OAA0B,SAAgD;CAChG,MAAM,OAAkB,CAAC;CACzB,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,SAAS,WAAW;EAC3B,MAAM,WAAW,YAAY,KAAK,YAAY,OAAO;EACrD,IAAI,SAAS,MAAM,UAAU,MAAM,SAAS,MAAM,GAChD,KAAK,KAAK;GAAE,GAAG;GAAM,YAAY;EAAS,CAAC;CAE/C,OAAO,IAAI,KAAK,SAAS,UAAU,QAAQ,IAAI,GAC7C,KAAK,KAAK,IAAI;CAGlB,OAAO;AACT;AA8BA,MAAM,oBAAoB;CACxB;CACA;CACA;AACF;;;;;;;;;AAUA,SAAgB,WAAc,EAC5B,WACA,QACA,gBACA,QACA,iBACA,WACA,aACA,QACA,SACA,WACA,UACA,cACqB;CACrB,MAAM,UAAU,aACb,UACC,SAAS,YAAY,QAAQ,SAAS,OAAO,KAAK,WAAW,QAAQ,KAAK,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,GAC7F,CAAC,QAAQ,MAAM,CACjB;CACA,MAAM,QAAQ,aAAgB;EAAE,GAAG;EAAW,GAAG;EAA4B,QAAQ;CAAQ,CAAC;CAE9F,MAAM,CAAC,eAAe,oBAAoB,SAAwB,IAAI;CACtE,MAAM,WAAW,OAAyB,IAAI;CAC9C,MAAM,aAAa,OAAyB,IAAI;CAChD,MAAM,EACJ,YAAY,wBACZ,iBACA,kBACE,gBACF;EAAE;EAAU,eAAe;CAAW,GACtC;EAAE,YAAY;EAAQ,eAAe;EAAgB;EAAe;CAAiB,CACvF;CACA,MAAM,EAAE,eAAe,eACrB;EAAE,GAAG;EAAwB,cAAc,OAAO;EAAQ,aAAa,OAAO;CAAO,GACrF;EAAE,OAAO;EAAQ,UAAU;CAAe,GAC1C,QACF;CAEA,gBAAgB;EACd,IAAI,iBAAiB,SAAS,SAAS,MAAM;CAC/C,GAAG,CAAC,eAAe,CAAC;CAEpB,MAAM,QAAQ,gBAAgB,EAAE,YAAY,KAAK,CAAC;CAClD,MAAM,OAAO,WAAW;EAAE,SAAS;EAAY,eAAe;CAAO,CAAC;CAEtE,OACE,qBAAC,OAAD;EACE,GAAI;EAEJ,eAAY;EACZ,gBAAa;EACb,WAAW,GACT,yBACA,WAAW,WAAW,CAAC,KAAK,MAAM,GAAG,iCAAiC,CACxE;EARF,UAAA,CAaE,oBAAC,OAAD;GACE,WAAW,GACT,qEACA,YAAY,6EACd;GAGA,UAAA,oBAAC,kBAAD;IAEE,gBAAgB,UAAU;KACxB,IAAI,MAAM,WAAW,MAAM,eAAe;KAC1C,MAAM,eAAe;KACrB,SAAS,SAAS,MAAM;IAC1B;IACA,WAAW,GAAG,mBAAmB,YAAY,MAAM;IACnD,SACE,oBAAC,QAAD;KAAM,aAAU;KAAU,WAAW,MAAM,QAAQ,EAAE,OAAO,SAAS,CAAC;KACpE,UAAA,oBAAC,YAAD,CAAa,CAAA;IACT,CAAA;IAGR,UAAA,oBAAC,SAAD;KAAO,GAAI;KAAY,KAAK;KAAU,aAAU;KAAQ,WAAW,MAAM,MAAM;IAAI,CAAA;GACnE,CAAA;EACf,CAAA,GACL,oBAAC,YAAD;GACE,UAAA;GACA,kBAAA;GACA,cAAc;IACZ,GAAG;IACH,GAAG;IAGH,mBAAmB;GACrB;GACO;GACP,YAAY;GACZ,YAAY,OAAO;GACV;GACT,cAAc,OAAO;GACV;GACC;EACb,CAAA,CACE;;AAET"}
@@ -2,6 +2,6 @@ import { ComponentTokenSet } from "../../tokens/component.js";
2
2
  import "../../index.js";
3
3
  //#region src/components/snackbar/snackbar-tokens.d.ts
4
4
  /** The tokens of {@link Snackbar}: `--md-comp-snackbar-<part>`. */
5
- export declare const snackbarTokenSet: ComponentTokenSet<"container-color" | "container-elevation" | "icon-color" | "container-shape" | "supporting-text-color" | "action-label-text-color">;
5
+ export declare const snackbarTokenSet: ComponentTokenSet<"container-color" | "container-shape" | "supporting-text-color" | "container-elevation" | "icon-color" | "action-label-text-color">;
6
6
  //#endregion
7
7
  //# sourceMappingURL=snackbar-tokens.d.ts.map
@@ -67,11 +67,25 @@ function TextField(props) {
67
67
  useLayoutEffect(() => {
68
68
  const element = domInputRef.current;
69
69
  if (!growing || !element) return;
70
- element.style.height = "auto";
71
- const lineHeight = Number.parseFloat(getComputedStyle(element).lineHeight) || 24;
72
- const max = maxRows ? lineHeight * maxRows : Number.POSITIVE_INFINITY;
73
- element.style.height = `${Math.min(element.scrollHeight, max)}px`;
74
- element.style.overflowY = element.scrollHeight > max ? "auto" : "hidden";
70
+ const fit = () => {
71
+ element.style.height = "auto";
72
+ const lineHeight = Number.parseFloat(getComputedStyle(element).lineHeight) || 24;
73
+ const max = maxRows ? lineHeight * maxRows : Number.POSITIVE_INFINITY;
74
+ element.style.height = `${Math.min(element.scrollHeight, max)}px`;
75
+ element.style.overflowY = element.scrollHeight > max ? "auto" : "hidden";
76
+ };
77
+ if (element.getClientRects().length > 0 || typeof ResizeObserver === "undefined") {
78
+ fit();
79
+ return;
80
+ }
81
+ element.style.height = "";
82
+ const observer = new ResizeObserver(() => {
83
+ if (element.getClientRects().length === 0) return;
84
+ observer.disconnect();
85
+ fit();
86
+ });
87
+ observer.observe(element);
88
+ return () => observer.disconnect();
75
89
  }, [
76
90
  value,
77
91
  growing,
@@ -1 +1 @@
1
- {"version":3,"file":"TextField.js","names":[],"sources":["../../../src/components/text-field/TextField.tsx"],"sourcesContent":["'use client';\n\nimport {\n useId,\n useLayoutEffect,\n type AriaAttributes,\n type CSSProperties,\n type MouseEvent,\n type PointerEvent,\n type ReactNode,\n type Ref,\n} from 'react';\nimport {\n mergeProps,\n useFocusRing,\n useHover,\n useObjectRef,\n useTextField,\n type AriaTextFieldOptions,\n type AriaTextFieldProps,\n} from 'react-aria';\nimport { useControlledState } from 'react-stately/useControlledState';\nimport {\n CharacterCounter,\n ErrorText,\n FieldErrorIcon,\n FieldLabel,\n SupportingText,\n} from '../../primitives/Field';\nimport { densityAttribute, type Density } from '../../tokens/density';\nimport { cn } from '../../utils/cn';\nimport { renderedDescription } from '../../utils/rendered-description';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\nimport { pickWidgetAria, splitRootProps, type WrapperRootDOMProps } from '../../utils/root-props';\nimport { textFieldStyles, type TextFieldVariant } from './text-field-styles';\n\nexport interface TextFieldClassNames {\n /** The root element, merged with `className`. */\n root?: string;\n /** The filled or outlined container around the label, input and icons. */\n container?: string;\n /** The floating label. */\n label?: string;\n /** The `<input>` or `<textarea>`. */\n input?: string;\n /** The leading icon's 48px box. */\n leadingIcon?: string;\n /** The trailing icon's 48px box. */\n trailingIcon?: string;\n /** The prefix text before the input. */\n prefix?: string;\n /** The suffix text after the input. */\n suffix?: string;\n /** The helper text below the field. */\n supportingText?: string;\n /** The error message below the field. */\n errorText?: string;\n /** The character counter below the field. */\n counter?: string;\n /** The built-in error icon. */\n errorIcon?: string;\n /** The outlined field's border (a `<fieldset>`, notched for the floated label). */\n outline?: string;\n /** The filled field's active indicator, the line along the container's bottom. */\n activeIndicator?: string;\n}\n\n/** Words the text field announces; override them for other languages. */\nexport interface TextFieldLabels {\n /** The error icon's name (M3: \"Error\"). @default \"Error\" */\n error: string;\n /**\n * What screen readers hear for the character counter (M3 calls it the \"character count\").\n * @default (count, max) => `Character count: ${count} of ${max}`\n */\n characterCount: (count: number, max: number) => string;\n}\n\nconst DEFAULT_LABELS: TextFieldLabels = {\n error: 'Error',\n characterCount: (count, max) => `Character count: ${count} of ${max}`,\n};\n\n/** Input types the browser draws its own format in when empty, which a resting label would cover. */\nconst FORMAT_SHOWING_TYPES = new Set(['date', 'time', 'datetime-local', 'month', 'week']);\n\ntype FieldState = 'disabled' | 'error-focus' | 'error-hover' | 'error' | 'focus' | 'hover' | 'rest';\n\n/** Props React Aria names with an `is` prefix; we use the HTML-style names instead. */\ntype RenamedAriaProps =\n | 'isDisabled'\n | 'isReadOnly'\n | 'isRequired'\n | 'isInvalid'\n | 'label'\n | 'description'\n | 'inputElementType'\n | 'value'\n | 'defaultValue'\n | 'onChange'\n // Left out: the deprecated `validationState` (`invalid` replaces it) and React Aria's\n // `excludeFromTabOrder` (`tabIndex={-1}` covers the rare case).\n | 'validationState'\n | 'excludeFromTabOrder';\n\ninterface TextFieldOwnProps\n extends\n Omit<AriaTextFieldProps<HTMLInputElement>, RenamedAriaProps>,\n Omit<WrapperRootDOMProps, keyof AriaTextFieldProps<HTMLInputElement>>,\n // The attributes that describe the input go to it, with React Aria's `aria-controls`,\n // `aria-haspopup`, `aria-autocomplete` and `aria-activedescendant`: a field that opens its\n // own suggestions can say so.\n Pick<\n AriaAttributes,\n | 'aria-expanded'\n | 'aria-required'\n | 'aria-invalid'\n | 'aria-readonly'\n | 'aria-multiline'\n | 'aria-placeholder'\n > {\n /** The `<input>`'s (or `<textarea>`'s) id, for a `<label for>` or an error summary's link. */\n id?: string;\n /** The container style: a filled surface or an outline. @default \"filled\" */\n variant?: TextFieldVariant;\n /**\n * Web density, `0` to `-3`: each step takes 4px off the container (56px at 0, 44px at -3).\n * Left out, the field takes the density of the nearest `ThemeProvider` or `ThemeScope`. M3\n * asks for density to be the reader's choice rather than a default, so prefer setting it\n * there, from a setting.\n * @default 0\n */\n density?: Density;\n /** The text, when controlled. */\n value?: string;\n /** The initial text, when uncontrolled. @default \"\" */\n defaultValue?: string;\n /** Called with the new text on every change. */\n onChange?: (value: string) => void;\n /**\n * Helper text below the field. Replaced visually by the error message when invalid, and\n * shown in the error colour while the field is invalid.\n */\n supportingText?: ReactNode;\n /**\n * Shows the supporting text only while the field has focus (M3 allows it \"persistently\n * visible or visible only on focus\"). Its space is kept, and screen readers always get it.\n * @default false\n */\n supportingTextOnFocus?: boolean;\n /** Marks the field invalid (in addition to `validate` and native constraints). */\n invalid?: boolean;\n /** A value is required: adds the `*` to the label and native `required` validation. */\n required?: boolean;\n /** Disables the field: it can't be focused or edited. @default false */\n disabled?: boolean;\n /**\n * The value can be read and copied but not changed. M3 styles it as a regular field and\n * asks for it to be \"clearly labeled as read-only\", e.g. in the supporting text. The root\n * gets `data-readonly`.\n * @default false\n */\n readOnly?: boolean;\n /** Decorative icon at the start, in a 48px box. */\n leadingIcon?: ReactNode;\n /**\n * Icon or icon button at the end (e.g. clear, visibility toggle), in a 48px box. It takes\n * the place of the error icon.\n */\n trailingIcon?: ReactNode;\n /**\n * The icon shown at the end while the field is invalid and has no `trailingIcon` (M3\n * strongly recommends one). Defaults to Material Symbols `error`, named by `labels.error`;\n * `false` turns it off.\n */\n errorIcon?: ReactNode | false;\n /** Text before the input, e.g. \"$\". Shown once the label floats. */\n prefix?: ReactNode;\n /**\n * What screen readers hear for the prefix, e.g. \"US dollars\" for \"$\". The prefix is in the\n * input's description either way; without this, its visible text is read.\n */\n prefixLabel?: string;\n /** Text after the input, e.g. \"kg\". Shown once the label floats. */\n suffix?: ReactNode;\n /** What screen readers hear for the suffix, e.g. \"kilograms\" for \"kg\". */\n suffixLabel?: string;\n /**\n * Renders a `<textarea>`. By default it's an M3 text area: a fixed height of `rows` lines\n * that scrolls vertically, which M3 recommends over growing fields on the web.\n * @default false\n */\n multiline?: boolean;\n /**\n * With `multiline`: grow with the content from `rows` up to `maxRows` (M3's multi-line\n * field) instead of keeping a fixed height.\n * @default false\n */\n autoGrow?: boolean;\n /** Visible lines of a multiline field. @default 4, or 1 with autoGrow */\n rows?: number;\n /** With `autoGrow`: lines after which the field scrolls instead of growing. */\n maxRows?: number;\n /** Shows `count/maxLength` below the field. Defaults to `true` when `maxLength` is set. */\n counter?: boolean;\n /** Words the field announces, for other languages. */\n labels?: Partial<TextFieldLabels>;\n /** Classes for the root element, merged with `classNames.root`; consumer classes win. */\n className?: string;\n /** Classes for each part, by slot. */\n classNames?: TextFieldClassNames;\n /** Inline styles for the root element. */\n style?: CSSProperties;\n /** The root element. Use `inputRef` for the input itself. */\n ref?: Ref<HTMLDivElement>;\n /** The `<input>` (or `<textarea>` when multiline), e.g. for form libraries. */\n inputRef?: Ref<HTMLInputElement | HTMLTextAreaElement>;\n}\n\n/** A visible label, or an accessible name for fields without one. */\ntype TextFieldLabel =\n | {\n /** The visible label, which floats above the input once it has focus or a value. */\n label: ReactNode;\n }\n | {\n label?: undefined;\n /** Accessible name when there's no visible label. */\n 'aria-label': string;\n }\n | {\n label?: undefined;\n /** The id of the element that names the field, when there's no visible label. */\n 'aria-labelledby': string;\n };\n\nexport type TextFieldProps = TextFieldOwnProps & TextFieldLabel;\n\nconst INTERACTIVE = 'input, textarea, button, a, [role=\"button\"]';\n\n/**\n * M3 text field: filled or outlined, with a floating label, leading / trailing icons,\n * prefix and suffix, supporting text, error message and icon, character counter, and a\n * multiline form (a fixed-height text area, or a growing field with `autoGrow`). Built on React Aria `useTextField`, so labelling,\n * descriptions, `validate` and native constraints work as in React Aria.\n *\n * `className` and `style` go on the root; `inputRef` reaches the input.\n *\n * @example\n * <TextField label=\"Email\" type=\"email\" required supportingText=\"We never share it.\" />\n * <TextField variant=\"outlined\" label=\"Price\" prefix=\"$\" inputMode=\"decimal\" />\n */\nexport function TextField(props: TextFieldProps) {\n const {\n variant = 'filled',\n density,\n label,\n value: valueProp,\n defaultValue,\n onChange,\n supportingText,\n supportingTextOnFocus = false,\n errorMessage,\n invalid,\n required,\n disabled = false,\n readOnly,\n leadingIcon,\n trailingIcon,\n errorIcon,\n prefix,\n prefixLabel,\n suffix,\n suffixLabel,\n multiline = false,\n autoGrow = false,\n rows = autoGrow ? 1 : 4,\n maxRows,\n counter,\n labels: labelsProp,\n className,\n classNames,\n style,\n ref,\n inputRef,\n ...otherProps\n } = props;\n\n // `data-*` attributes and the root's DOM attributes describe the component, so they go on the\n // root with `className`; everything else (the input's ARIA, attributes and events) goes to\n // the input via React Aria.\n const { root: rootData, rest: ariaProps } = splitRootProps(otherProps, { wrapper: true });\n const labels = { ...DEFAULT_LABELS, ...labelsProp };\n const prefixId = useId();\n const suffixId = useId();\n const counterId = useId();\n\n const [value, setValue] = useControlledState(valueProp, defaultValue ?? '', onChange);\n const domInputRef = useObjectRef(inputRef as Ref<HTMLInputElement>);\n const {\n labelProps,\n inputProps,\n descriptionProps,\n errorMessageProps,\n isInvalid,\n validationErrors,\n validationDetails,\n } = useTextField(\n {\n ...ariaProps,\n label,\n description: supportingText,\n errorMessage,\n value,\n onChange: setValue,\n isInvalid: invalid,\n isRequired: required,\n isDisabled: disabled,\n isReadOnly: readOnly,\n inputElementType: multiline ? 'textarea' : 'input',\n } as AriaTextFieldOptions<'input' | 'textarea'>,\n domInputRef,\n );\n const { focusProps, isFocused } = useFocusRing({ isTextInput: true });\n const { hoverProps, isHovered } = useHover({ isDisabled: disabled });\n\n const growing = multiline && autoGrow;\n // Grow a multiline field with its content, up to maxRows.\n useLayoutEffect(() => {\n const element = domInputRef.current as HTMLTextAreaElement | null;\n if (!growing || !element) return;\n element.style.height = 'auto';\n const lineHeight = Number.parseFloat(getComputedStyle(element).lineHeight) || 24;\n const max = maxRows ? lineHeight * maxRows : Number.POSITIVE_INFINITY;\n element.style.height = `${Math.min(element.scrollHeight, max)}px`;\n element.style.overflowY = element.scrollHeight > max ? 'auto' : 'hidden';\n }, [value, growing, maxRows, domInputRef]);\n\n const hasLabel = label !== undefined && label !== null;\n const floated =\n hasLabel && (isFocused || value.length > 0 || FORMAT_SHOWING_TYPES.has(ariaProps.type ?? ''));\n const state: FieldState = disabled\n ? 'disabled'\n : isInvalid\n ? isFocused\n ? 'error-focus'\n : isHovered\n ? 'error-hover'\n : 'error'\n : isFocused\n ? 'focus'\n : isHovered\n ? 'hover'\n : 'rest';\n\n const resolvedError =\n typeof errorMessage === 'function'\n ? errorMessage({ isInvalid, validationErrors, validationDetails })\n : (errorMessage ?? validationErrors.join(' '));\n const showError = isInvalid && Boolean(resolvedError);\n const maxLength = ariaProps.maxLength;\n const showCounter = maxLength !== undefined && counter !== false;\n const showErrorIcon = isInvalid && !trailingIcon && errorIcon !== false;\n\n const styles = textFieldStyles({\n variant,\n hasLabel,\n hasLeadingIcon: Boolean(leadingIcon),\n hasTrailingIcon: Boolean(trailingIcon) || showErrorIcon,\n multiline,\n fixedHeight: multiline && !autoGrow,\n supportingTextOnFocus,\n });\n // The affixes and the counter join React Aria's description (supporting text, then error).\n const describedBy =\n [\n prefix ? prefixId : null,\n suffix ? suffixId : null,\n renderedDescription(inputProps['aria-describedby'], [\n !supportingText && descriptionProps.id,\n !showError && errorMessageProps.id,\n ]),\n showCounter ? counterId : null,\n ]\n .filter(Boolean)\n .join(' ') || undefined;\n\n // Pressing the container (not an icon button) focuses the input, like Compose.\n const focusInput = (event: PointerEvent<HTMLDivElement>) => {\n if ((event.target as Element).closest(INTERACTIVE) || disabled) return;\n event.preventDefault();\n // The field is where the press landed: the page never scrolls to it (2026-10-15).\n domInputRef.current?.focus({ preventScroll: true });\n };\n // Chrome's touch adjustment can send a tap's pointer events to the input while the mouse\n // events it emulates, and the click, land on the padding beside it, so the input never\n // takes focus (2026-10-17). A click on the field that leaves the input unfocused focuses it.\n // The mouse never needs it: its press on the padding already focused the input.\n const focusInputOnClick = (event: MouseEvent<HTMLDivElement>) => {\n const target = event.target as Element;\n if (\n (event.nativeEvent as globalThis.PointerEvent).pointerType === 'mouse' ||\n !event.currentTarget.contains(target) ||\n target.closest(INTERACTIVE) ||\n disabled ||\n document.activeElement === domInputRef.current\n ) {\n return;\n }\n domInputRef.current?.focus({ preventScroll: true });\n };\n\n // Part of the accessible name, as M3 asks (\"The accessibility label must include the\n // asterisk\"); the copy in the outline's notch is hidden with the fieldset.\n const requiredMark = required ? ' *' : null;\n const Input = multiline ? 'textarea' : 'input';\n\n return (\n <div\n {...rootData}\n ref={ref}\n style={style}\n className={styles.root({ class: cn(classNames?.root, className) })}\n data-tokens={mergeDataTokens('md-comp-text-field', rootData)}\n data-variant={variant}\n {...densityAttribute(density)}\n data-field-state={state}\n data-focused={isFocused || undefined}\n data-hovered={(isHovered && !disabled) || undefined}\n data-invalid={isInvalid || undefined}\n data-disabled={disabled || undefined}\n data-readonly={readOnly || undefined}\n data-floated={floated || undefined}\n >\n <div\n {...hoverProps}\n onPointerDown={focusInput}\n onClick={focusInputOnClick}\n data-slot=\"container\"\n className={styles.container({ class: classNames?.container })}\n >\n {variant === 'outlined' && (\n <fieldset\n aria-hidden=\"true\"\n data-slot=\"outline\"\n className={styles.outline({ class: classNames?.outline })}\n >\n {hasLabel && (\n <legend className={styles.notch()}>\n <span className={styles.notchClip()}>\n <span className={styles.notchText()}>\n {label}\n {requiredMark}\n </span>\n </span>\n </legend>\n )}\n </fieldset>\n )}\n {leadingIcon && (\n <span\n aria-hidden=\"true\"\n data-slot=\"leading-icon\"\n className={styles.icon({ class: cn(styles.leadingIcon(), classNames?.leadingIcon) })}\n >\n {leadingIcon}\n </span>\n )}\n <div className={styles.field()}>\n {prefix && (\n <span\n id={prefixId}\n className={styles.affix({ class: cn(styles.prefix(), classNames?.prefix) })}\n >\n <Affix text={prefix} label={prefixLabel} />\n </span>\n )}\n <Input\n {...(mergeProps(inputProps, pickWidgetAria(ariaProps), focusProps) as object)}\n aria-describedby={describedBy}\n ref={domInputRef as Ref<never>}\n data-slot=\"input\"\n rows={multiline ? rows : undefined}\n className={styles.input({ class: classNames?.input })}\n />\n {suffix && (\n <span\n id={suffixId}\n className={styles.affix({ class: cn(styles.suffix(), classNames?.suffix) })}\n >\n <Affix text={suffix} label={suffixLabel} />\n </span>\n )}\n </div>\n {trailingIcon ? (\n <span\n data-slot=\"trailing-icon\"\n className={styles.icon({\n class: cn(styles.trailingIcon(), classNames?.trailingIcon),\n })}\n >\n {trailingIcon}\n </span>\n ) : showErrorIcon ? (\n <span className={styles.icon({ class: styles.trailingIcon() })}>\n <FieldErrorIcon\n label={labels.error}\n icon={errorIcon}\n className={styles.errorIcon({ class: classNames?.errorIcon })}\n />\n </span>\n ) : null}\n {hasLabel && (\n <FieldLabel\n {...labelProps}\n data-slot=\"label\"\n className={styles.label({ class: classNames?.label })}\n >\n {label}\n {requiredMark}\n </FieldLabel>\n )}\n <span\n aria-hidden=\"true\"\n data-slot=\"active-indicator\"\n className={styles.indicator({ class: classNames?.activeIndicator })}\n />\n </div>\n {(supportingText || showError || showCounter) && (\n <div className={styles.supporting()}>\n {supportingText && (\n <SupportingText\n {...descriptionProps}\n data-slot=\"supporting-text\"\n className={cn(\n styles.supportingText({\n class: cn(styles.supportingTextColor(), classNames?.supportingText),\n }),\n showError && 'sr-only',\n )}\n >\n {supportingText}\n </SupportingText>\n )}\n {showError && (\n <ErrorText\n {...errorMessageProps}\n className={styles.supportingText({\n supportingTextOnFocus: false,\n class: classNames?.errorText,\n })}\n >\n {resolvedError}\n </ErrorText>\n )}\n {showCounter && (\n <CharacterCounter\n id={counterId}\n count={value.length}\n max={maxLength}\n label={labels.characterCount(value.length, maxLength)}\n className={styles.counter({ class: classNames?.counter })}\n />\n )}\n </div>\n )}\n </div>\n );\n}\n\n/** An affix's visible text, read as its `label` when there is one. */\nfunction Affix({ text, label }: { text: ReactNode; label: string | undefined }) {\n if (label === undefined) return text;\n return (\n <>\n <span aria-hidden=\"true\">{text}</span>\n <span className=\"sr-only\">{label}</span>\n </>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TextField.js","names":[],"sources":["../../../src/components/text-field/TextField.tsx"],"sourcesContent":["'use client';\n\nimport {\n useId,\n useLayoutEffect,\n type AriaAttributes,\n type CSSProperties,\n type MouseEvent,\n type PointerEvent,\n type ReactNode,\n type Ref,\n} from 'react';\nimport {\n mergeProps,\n useFocusRing,\n useHover,\n useObjectRef,\n useTextField,\n type AriaTextFieldOptions,\n type AriaTextFieldProps,\n} from 'react-aria';\nimport { useControlledState } from 'react-stately/useControlledState';\nimport {\n CharacterCounter,\n ErrorText,\n FieldErrorIcon,\n FieldLabel,\n SupportingText,\n} from '../../primitives/Field';\nimport { densityAttribute, type Density } from '../../tokens/density';\nimport { cn } from '../../utils/cn';\nimport { renderedDescription } from '../../utils/rendered-description';\nimport { mergeDataTokens } from '../../utils/merge-data-tokens';\nimport { pickWidgetAria, splitRootProps, type WrapperRootDOMProps } from '../../utils/root-props';\nimport { textFieldStyles, type TextFieldVariant } from './text-field-styles';\n\nexport interface TextFieldClassNames {\n /** The root element, merged with `className`. */\n root?: string;\n /** The filled or outlined container around the label, input and icons. */\n container?: string;\n /** The floating label. */\n label?: string;\n /** The `<input>` or `<textarea>`. */\n input?: string;\n /** The leading icon's 48px box. */\n leadingIcon?: string;\n /** The trailing icon's 48px box. */\n trailingIcon?: string;\n /** The prefix text before the input. */\n prefix?: string;\n /** The suffix text after the input. */\n suffix?: string;\n /** The helper text below the field. */\n supportingText?: string;\n /** The error message below the field. */\n errorText?: string;\n /** The character counter below the field. */\n counter?: string;\n /** The built-in error icon. */\n errorIcon?: string;\n /** The outlined field's border (a `<fieldset>`, notched for the floated label). */\n outline?: string;\n /** The filled field's active indicator, the line along the container's bottom. */\n activeIndicator?: string;\n}\n\n/** Words the text field announces; override them for other languages. */\nexport interface TextFieldLabels {\n /** The error icon's name (M3: \"Error\"). @default \"Error\" */\n error: string;\n /**\n * What screen readers hear for the character counter (M3 calls it the \"character count\").\n * @default (count, max) => `Character count: ${count} of ${max}`\n */\n characterCount: (count: number, max: number) => string;\n}\n\nconst DEFAULT_LABELS: TextFieldLabels = {\n error: 'Error',\n characterCount: (count, max) => `Character count: ${count} of ${max}`,\n};\n\n/** Input types the browser draws its own format in when empty, which a resting label would cover. */\nconst FORMAT_SHOWING_TYPES = new Set(['date', 'time', 'datetime-local', 'month', 'week']);\n\ntype FieldState = 'disabled' | 'error-focus' | 'error-hover' | 'error' | 'focus' | 'hover' | 'rest';\n\n/** Props React Aria names with an `is` prefix; we use the HTML-style names instead. */\ntype RenamedAriaProps =\n | 'isDisabled'\n | 'isReadOnly'\n | 'isRequired'\n | 'isInvalid'\n | 'label'\n | 'description'\n | 'inputElementType'\n | 'value'\n | 'defaultValue'\n | 'onChange'\n // Left out: the deprecated `validationState` (`invalid` replaces it) and React Aria's\n // `excludeFromTabOrder` (`tabIndex={-1}` covers the rare case).\n | 'validationState'\n | 'excludeFromTabOrder';\n\ninterface TextFieldOwnProps\n extends\n Omit<AriaTextFieldProps<HTMLInputElement>, RenamedAriaProps>,\n Omit<WrapperRootDOMProps, keyof AriaTextFieldProps<HTMLInputElement>>,\n // The attributes that describe the input go to it, with React Aria's `aria-controls`,\n // `aria-haspopup`, `aria-autocomplete` and `aria-activedescendant`: a field that opens its\n // own suggestions can say so.\n Pick<\n AriaAttributes,\n | 'aria-expanded'\n | 'aria-required'\n | 'aria-invalid'\n | 'aria-readonly'\n | 'aria-multiline'\n | 'aria-placeholder'\n > {\n /** The `<input>`'s (or `<textarea>`'s) id, for a `<label for>` or an error summary's link. */\n id?: string;\n /** The container style: a filled surface or an outline. @default \"filled\" */\n variant?: TextFieldVariant;\n /**\n * Web density, `0` to `-3`: each step takes 4px off the container (56px at 0, 44px at -3).\n * Left out, the field takes the density of the nearest `ThemeProvider` or `ThemeScope`. M3\n * asks for density to be the reader's choice rather than a default, so prefer setting it\n * there, from a setting.\n * @default 0\n */\n density?: Density;\n /** The text, when controlled. */\n value?: string;\n /** The initial text, when uncontrolled. @default \"\" */\n defaultValue?: string;\n /** Called with the new text on every change. */\n onChange?: (value: string) => void;\n /**\n * Helper text below the field. Replaced visually by the error message when invalid, and\n * shown in the error colour while the field is invalid.\n */\n supportingText?: ReactNode;\n /**\n * Shows the supporting text only while the field has focus (M3 allows it \"persistently\n * visible or visible only on focus\"). Its space is kept, and screen readers always get it.\n * @default false\n */\n supportingTextOnFocus?: boolean;\n /** Marks the field invalid (in addition to `validate` and native constraints). */\n invalid?: boolean;\n /** A value is required: adds the `*` to the label and native `required` validation. */\n required?: boolean;\n /** Disables the field: it can't be focused or edited. @default false */\n disabled?: boolean;\n /**\n * The value can be read and copied but not changed. M3 styles it as a regular field and\n * asks for it to be \"clearly labeled as read-only\", e.g. in the supporting text. The root\n * gets `data-readonly`.\n * @default false\n */\n readOnly?: boolean;\n /** Decorative icon at the start, in a 48px box. */\n leadingIcon?: ReactNode;\n /**\n * Icon or icon button at the end (e.g. clear, visibility toggle), in a 48px box. It takes\n * the place of the error icon.\n */\n trailingIcon?: ReactNode;\n /**\n * The icon shown at the end while the field is invalid and has no `trailingIcon` (M3\n * strongly recommends one). Defaults to Material Symbols `error`, named by `labels.error`;\n * `false` turns it off.\n */\n errorIcon?: ReactNode | false;\n /** Text before the input, e.g. \"$\". Shown once the label floats. */\n prefix?: ReactNode;\n /**\n * What screen readers hear for the prefix, e.g. \"US dollars\" for \"$\". The prefix is in the\n * input's description either way; without this, its visible text is read.\n */\n prefixLabel?: string;\n /** Text after the input, e.g. \"kg\". Shown once the label floats. */\n suffix?: ReactNode;\n /** What screen readers hear for the suffix, e.g. \"kilograms\" for \"kg\". */\n suffixLabel?: string;\n /**\n * Renders a `<textarea>`. By default it's an M3 text area: a fixed height of `rows` lines\n * that scrolls vertically, which M3 recommends over growing fields on the web.\n * @default false\n */\n multiline?: boolean;\n /**\n * With `multiline`: grow with the content from `rows` up to `maxRows` (M3's multi-line\n * field) instead of keeping a fixed height.\n * @default false\n */\n autoGrow?: boolean;\n /** Visible lines of a multiline field. @default 4, or 1 with autoGrow */\n rows?: number;\n /** With `autoGrow`: lines after which the field scrolls instead of growing. */\n maxRows?: number;\n /** Shows `count/maxLength` below the field. Defaults to `true` when `maxLength` is set. */\n counter?: boolean;\n /** Words the field announces, for other languages. */\n labels?: Partial<TextFieldLabels>;\n /** Classes for the root element, merged with `classNames.root`; consumer classes win. */\n className?: string;\n /** Classes for each part, by slot. */\n classNames?: TextFieldClassNames;\n /** Inline styles for the root element. */\n style?: CSSProperties;\n /** The root element. Use `inputRef` for the input itself. */\n ref?: Ref<HTMLDivElement>;\n /** The `<input>` (or `<textarea>` when multiline), e.g. for form libraries. */\n inputRef?: Ref<HTMLInputElement | HTMLTextAreaElement>;\n}\n\n/** A visible label, or an accessible name for fields without one. */\ntype TextFieldLabel =\n | {\n /** The visible label, which floats above the input once it has focus or a value. */\n label: ReactNode;\n }\n | {\n label?: undefined;\n /** Accessible name when there's no visible label. */\n 'aria-label': string;\n }\n | {\n label?: undefined;\n /** The id of the element that names the field, when there's no visible label. */\n 'aria-labelledby': string;\n };\n\nexport type TextFieldProps = TextFieldOwnProps & TextFieldLabel;\n\nconst INTERACTIVE = 'input, textarea, button, a, [role=\"button\"]';\n\n/**\n * M3 text field: filled or outlined, with a floating label, leading / trailing icons,\n * prefix and suffix, supporting text, error message and icon, character counter, and a\n * multiline form (a fixed-height text area, or a growing field with `autoGrow`). Built on React Aria `useTextField`, so labelling,\n * descriptions, `validate` and native constraints work as in React Aria.\n *\n * `className` and `style` go on the root; `inputRef` reaches the input.\n *\n * @example\n * <TextField label=\"Email\" type=\"email\" required supportingText=\"We never share it.\" />\n * <TextField variant=\"outlined\" label=\"Price\" prefix=\"$\" inputMode=\"decimal\" />\n */\nexport function TextField(props: TextFieldProps) {\n const {\n variant = 'filled',\n density,\n label,\n value: valueProp,\n defaultValue,\n onChange,\n supportingText,\n supportingTextOnFocus = false,\n errorMessage,\n invalid,\n required,\n disabled = false,\n readOnly,\n leadingIcon,\n trailingIcon,\n errorIcon,\n prefix,\n prefixLabel,\n suffix,\n suffixLabel,\n multiline = false,\n autoGrow = false,\n rows = autoGrow ? 1 : 4,\n maxRows,\n counter,\n labels: labelsProp,\n className,\n classNames,\n style,\n ref,\n inputRef,\n ...otherProps\n } = props;\n\n // `data-*` attributes and the root's DOM attributes describe the component, so they go on the\n // root with `className`; everything else (the input's ARIA, attributes and events) goes to\n // the input via React Aria.\n const { root: rootData, rest: ariaProps } = splitRootProps(otherProps, { wrapper: true });\n const labels = { ...DEFAULT_LABELS, ...labelsProp };\n const prefixId = useId();\n const suffixId = useId();\n const counterId = useId();\n\n const [value, setValue] = useControlledState(valueProp, defaultValue ?? '', onChange);\n const domInputRef = useObjectRef(inputRef as Ref<HTMLInputElement>);\n const {\n labelProps,\n inputProps,\n descriptionProps,\n errorMessageProps,\n isInvalid,\n validationErrors,\n validationDetails,\n } = useTextField(\n {\n ...ariaProps,\n label,\n description: supportingText,\n errorMessage,\n value,\n onChange: setValue,\n isInvalid: invalid,\n isRequired: required,\n isDisabled: disabled,\n isReadOnly: readOnly,\n inputElementType: multiline ? 'textarea' : 'input',\n } as AriaTextFieldOptions<'input' | 'textarea'>,\n domInputRef,\n );\n const { focusProps, isFocused } = useFocusRing({ isTextInput: true });\n const { hoverProps, isHovered } = useHover({ isDisabled: disabled });\n\n const growing = multiline && autoGrow;\n // Grow a multiline field with its content, up to maxRows.\n useLayoutEffect(() => {\n const element = domInputRef.current as HTMLTextAreaElement | null;\n if (!growing || !element) return;\n const fit = () => {\n element.style.height = 'auto';\n const lineHeight = Number.parseFloat(getComputedStyle(element).lineHeight) || 24;\n const max = maxRows ? lineHeight * maxRows : Number.POSITIVE_INFINITY;\n element.style.height = `${Math.min(element.scrollHeight, max)}px`;\n element.style.overflowY = element.scrollHeight > max ? 'auto' : 'hidden';\n };\n if (element.getClientRects().length > 0 || typeof ResizeObserver === 'undefined') {\n fit();\n return;\n }\n // Out of sight (`display: none` above it: a closed tab or panel), it has no height to read,\n // and set to its 0px it stayed invisible once shown. It keeps its rows until it shows.\n element.style.height = '';\n const observer = new ResizeObserver(() => {\n if (element.getClientRects().length === 0) return;\n observer.disconnect();\n fit();\n });\n observer.observe(element);\n return () => observer.disconnect();\n }, [value, growing, maxRows, domInputRef]);\n\n const hasLabel = label !== undefined && label !== null;\n const floated =\n hasLabel && (isFocused || value.length > 0 || FORMAT_SHOWING_TYPES.has(ariaProps.type ?? ''));\n const state: FieldState = disabled\n ? 'disabled'\n : isInvalid\n ? isFocused\n ? 'error-focus'\n : isHovered\n ? 'error-hover'\n : 'error'\n : isFocused\n ? 'focus'\n : isHovered\n ? 'hover'\n : 'rest';\n\n const resolvedError =\n typeof errorMessage === 'function'\n ? errorMessage({ isInvalid, validationErrors, validationDetails })\n : (errorMessage ?? validationErrors.join(' '));\n const showError = isInvalid && Boolean(resolvedError);\n const maxLength = ariaProps.maxLength;\n const showCounter = maxLength !== undefined && counter !== false;\n const showErrorIcon = isInvalid && !trailingIcon && errorIcon !== false;\n\n const styles = textFieldStyles({\n variant,\n hasLabel,\n hasLeadingIcon: Boolean(leadingIcon),\n hasTrailingIcon: Boolean(trailingIcon) || showErrorIcon,\n multiline,\n fixedHeight: multiline && !autoGrow,\n supportingTextOnFocus,\n });\n // The affixes and the counter join React Aria's description (supporting text, then error).\n const describedBy =\n [\n prefix ? prefixId : null,\n suffix ? suffixId : null,\n renderedDescription(inputProps['aria-describedby'], [\n !supportingText && descriptionProps.id,\n !showError && errorMessageProps.id,\n ]),\n showCounter ? counterId : null,\n ]\n .filter(Boolean)\n .join(' ') || undefined;\n\n // Pressing the container (not an icon button) focuses the input, like Compose.\n const focusInput = (event: PointerEvent<HTMLDivElement>) => {\n if ((event.target as Element).closest(INTERACTIVE) || disabled) return;\n event.preventDefault();\n // The field is where the press landed: the page never scrolls to it (2026-10-15).\n domInputRef.current?.focus({ preventScroll: true });\n };\n // Chrome's touch adjustment can send a tap's pointer events to the input while the mouse\n // events it emulates, and the click, land on the padding beside it, so the input never\n // takes focus (2026-10-17). A click on the field that leaves the input unfocused focuses it.\n // The mouse never needs it: its press on the padding already focused the input.\n const focusInputOnClick = (event: MouseEvent<HTMLDivElement>) => {\n const target = event.target as Element;\n if (\n (event.nativeEvent as globalThis.PointerEvent).pointerType === 'mouse' ||\n !event.currentTarget.contains(target) ||\n target.closest(INTERACTIVE) ||\n disabled ||\n document.activeElement === domInputRef.current\n ) {\n return;\n }\n domInputRef.current?.focus({ preventScroll: true });\n };\n\n // Part of the accessible name, as M3 asks (\"The accessibility label must include the\n // asterisk\"); the copy in the outline's notch is hidden with the fieldset.\n const requiredMark = required ? ' *' : null;\n const Input = multiline ? 'textarea' : 'input';\n\n return (\n <div\n {...rootData}\n ref={ref}\n style={style}\n className={styles.root({ class: cn(classNames?.root, className) })}\n data-tokens={mergeDataTokens('md-comp-text-field', rootData)}\n data-variant={variant}\n {...densityAttribute(density)}\n data-field-state={state}\n data-focused={isFocused || undefined}\n data-hovered={(isHovered && !disabled) || undefined}\n data-invalid={isInvalid || undefined}\n data-disabled={disabled || undefined}\n data-readonly={readOnly || undefined}\n data-floated={floated || undefined}\n >\n <div\n {...hoverProps}\n onPointerDown={focusInput}\n onClick={focusInputOnClick}\n data-slot=\"container\"\n className={styles.container({ class: classNames?.container })}\n >\n {variant === 'outlined' && (\n <fieldset\n aria-hidden=\"true\"\n data-slot=\"outline\"\n className={styles.outline({ class: classNames?.outline })}\n >\n {hasLabel && (\n <legend className={styles.notch()}>\n <span className={styles.notchClip()}>\n <span className={styles.notchText()}>\n {label}\n {requiredMark}\n </span>\n </span>\n </legend>\n )}\n </fieldset>\n )}\n {leadingIcon && (\n <span\n aria-hidden=\"true\"\n data-slot=\"leading-icon\"\n className={styles.icon({ class: cn(styles.leadingIcon(), classNames?.leadingIcon) })}\n >\n {leadingIcon}\n </span>\n )}\n <div className={styles.field()}>\n {prefix && (\n <span\n id={prefixId}\n className={styles.affix({ class: cn(styles.prefix(), classNames?.prefix) })}\n >\n <Affix text={prefix} label={prefixLabel} />\n </span>\n )}\n <Input\n {...(mergeProps(inputProps, pickWidgetAria(ariaProps), focusProps) as object)}\n aria-describedby={describedBy}\n ref={domInputRef as Ref<never>}\n data-slot=\"input\"\n rows={multiline ? rows : undefined}\n className={styles.input({ class: classNames?.input })}\n />\n {suffix && (\n <span\n id={suffixId}\n className={styles.affix({ class: cn(styles.suffix(), classNames?.suffix) })}\n >\n <Affix text={suffix} label={suffixLabel} />\n </span>\n )}\n </div>\n {trailingIcon ? (\n <span\n data-slot=\"trailing-icon\"\n className={styles.icon({\n class: cn(styles.trailingIcon(), classNames?.trailingIcon),\n })}\n >\n {trailingIcon}\n </span>\n ) : showErrorIcon ? (\n <span className={styles.icon({ class: styles.trailingIcon() })}>\n <FieldErrorIcon\n label={labels.error}\n icon={errorIcon}\n className={styles.errorIcon({ class: classNames?.errorIcon })}\n />\n </span>\n ) : null}\n {hasLabel && (\n <FieldLabel\n {...labelProps}\n data-slot=\"label\"\n className={styles.label({ class: classNames?.label })}\n >\n {label}\n {requiredMark}\n </FieldLabel>\n )}\n <span\n aria-hidden=\"true\"\n data-slot=\"active-indicator\"\n className={styles.indicator({ class: classNames?.activeIndicator })}\n />\n </div>\n {(supportingText || showError || showCounter) && (\n <div className={styles.supporting()}>\n {supportingText && (\n <SupportingText\n {...descriptionProps}\n data-slot=\"supporting-text\"\n className={cn(\n styles.supportingText({\n class: cn(styles.supportingTextColor(), classNames?.supportingText),\n }),\n showError && 'sr-only',\n )}\n >\n {supportingText}\n </SupportingText>\n )}\n {showError && (\n <ErrorText\n {...errorMessageProps}\n className={styles.supportingText({\n supportingTextOnFocus: false,\n class: classNames?.errorText,\n })}\n >\n {resolvedError}\n </ErrorText>\n )}\n {showCounter && (\n <CharacterCounter\n id={counterId}\n count={value.length}\n max={maxLength}\n label={labels.characterCount(value.length, maxLength)}\n className={styles.counter({ class: classNames?.counter })}\n />\n )}\n </div>\n )}\n </div>\n );\n}\n\n/** An affix's visible text, read as its `label` when there is one. */\nfunction Affix({ text, label }: { text: ReactNode; label: string | undefined }) {\n if (label === undefined) return text;\n return (\n <>\n <span aria-hidden=\"true\">{text}</span>\n <span className=\"sr-only\">{label}</span>\n </>\n );\n}\n"],"mappings":"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"}
@@ -83,7 +83,7 @@ const floatingToolbarStyles = tv({
83
83
  root: "min-h-[64px] flex-row",
84
84
  items: "flex-row gap-[8px] ps-[4px] pe-[4px]",
85
85
  collapsibleItems: "w-max shrink-0",
86
- collapse: "data-collapsed:w-0",
86
+ collapse: "data-collapsed:w-0 data-collapsed:overflow-x-clip",
87
87
  clip: "w-full flex-row [clip-path:inset(-12px_-2px)]",
88
88
  toolbarSlot: "h-[64px] w-(--m3-toolbar-size) flex-row",
89
89
  surface: "h-[64px] w-(--m3-toolbar-size) shrink-0 data-collapsed:w-0",
@@ -94,7 +94,7 @@ const floatingToolbarStyles = tv({
94
94
  root: "min-w-[64px] flex-col",
95
95
  items: "flex-col gap-[8px] pt-[4px] pb-[4px]",
96
96
  collapsibleItems: "h-max shrink-0",
97
- collapse: "flex-col data-collapsed:h-0",
97
+ collapse: "flex-col data-collapsed:h-0 data-collapsed:overflow-y-clip",
98
98
  clip: "h-full flex-col [clip-path:inset(-2px_-12px)]",
99
99
  toolbarSlot: "h-(--m3-toolbar-size) w-[64px] flex-col",
100
100
  surface: "h-(--m3-toolbar-size) w-[64px] shrink-0 data-collapsed:h-0",
@@ -1 +1 @@
1
- {"version":3,"file":"toolbar-styles.js","names":[],"sources":["../../../src/components/toolbar/toolbar-styles.ts"],"sourcesContent":["import { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Values follow the M3 Expressive spec and its tokens (the FloatingToolbar / DockedToolbar\n * token files), with Compose Material 3's FloatingToolbar.kt and AppBar.kt's\n * FlexibleBottomAppBar as the recipe.\n *\n * Docked: 64px, `surface-container`, square corners, no elevation, 16px at each end; items\n * sit 32px apart in the centre (the default) or spread out (space-between, for compact\n * windows).\n *\n * Floating: at least 64px across, full corners, 8px padding, `surface-container` (standard)\n * or `primary-container` (vibrant), level 1 by default (M3: floating toolbars have\n * elevation by default). Content is `on-surface-variant` (standard, the icon-button role)\n * or `on-primary-container` (vibrant). Compose's icon buttons take up 48px of layout while\n * ours take 40px (the 48px touch target is overlaid), so every content group adds 4px at\n * its ends and 8px between items, which reproduces Compose's spacing for icon buttons.\n * Leading / trailing content expands on the fast spatial spring and collapses on the fast\n * effects curve, its box's width (or height) in px between 0 and the content's measured\n * size, so the root's size follows it without transforms. Collapsed content stays in the tab order and\n * the accessibility tree, clipped to nothing, and focusing it expands the toolbar.\n *\n * Exiting on scroll (`visible={false}`): the toolbar slides off its edge on the fast\n * spatial spring (a `transform` set only while hidden, so it composes with Tailwind's\n * `translate-*` utilities); reduced motion fades it instead.\n *\n * With a FAB: the toolbar is level 1 expanded and level 0 collapsed, 8px from the FAB.\n * Collapsing shrinks the toolbar's width to 0 towards the FAB while the FAB grows from 56px\n * to 80px; the component keeps its expanded size, as Compose's layout does.\n *\n * Container colours and the floating toolbar's corners come from the component tokens\n * (`toolbar-tokens.ts`, `--md-comp-toolbar-docked-*` and `--md-comp-toolbar-floating-*`):\n * the roots carry `data-tokens`, the floating one also `data-color`. A consumer class still\n * wins in `cn()`.\n *\n * Every class is written out in full so Tailwind can find it.\n */\n\nconst spatialFast = [\n '[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing)]',\n];\n\n/** The root's exit-on-scroll motion; each edge variant sets the hidden transform. */\nconst exitMotion = [\n 'data-hidden:invisible motion-reduce:data-hidden:opacity-0 motion-reduce:data-hidden:[transform:none]',\n '[transition-property:transform,opacity,visibility]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration),var(--md-sys-motion-spring-effects-fast-duration),max(var(--md-sys-motion-spring-spatial-fast-duration),var(--md-sys-motion-spring-effects-fast-duration))]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing),var(--md-sys-motion-spring-effects-fast-easing),linear]',\n];\n\n/** Where a hidden toolbar goes: past its edge plus the 24px margin of a vertical toolbar. */\nconst exitEdges = {\n bottom: { root: 'data-hidden:[transform:translateY(calc(100%+24px))]' },\n top: { root: 'data-hidden:[transform:translateY(calc(-100%-24px))]' },\n start: {\n root: 'data-hidden:[transform:translateX(calc(-100%-24px))] rtl:data-hidden:[transform:translateX(calc(100%+24px))]',\n },\n end: {\n root: 'data-hidden:[transform:translateX(calc(100%+24px))] rtl:data-hidden:[transform:translateX(calc(-100%-24px))]',\n },\n} as const;\n\n/** Variant definitions for {@link DockedToolbar}; extend them to add variants. */\nexport const dockedToolbarStyles = tv({\n slots: {\n root: [\n 'flex h-[64px] w-full items-center bg-[var(--_md-comp-toolbar-docked-container-color)] ps-[16px] pe-[16px]',\n // Forced colours paint the container as Canvas: a CanvasText line marks its top edge\n // against the page (2026-10-15).\n 'forced-colors:border-t forced-colors:border-solid forced-colors:border-[CanvasText]',\n ...exitMotion,\n 'data-hidden:[transform:translateY(100%)]',\n ],\n },\n variants: {\n arrangement: {\n 'space-between': { root: 'justify-between' },\n centered: { root: 'justify-center gap-[32px]' },\n },\n // As the floating toolbar's (2026-10-15): vibrant is primary-container (its own token,\n // scoped by `data-color`, 2026-10-17), and standard icon buttons take its content colour.\n color: {\n standard: { root: 'text-on-surface-variant' },\n vibrant: {\n root: 'text-on-primary-container [--md-comp-icon-button-icon-color:currentColor]',\n },\n },\n },\n defaultVariants: { arrangement: 'centered', color: 'standard' },\n});\n\n/** Variant definitions for {@link FloatingToolbar}; extend them to add colour styles. */\nexport const floatingToolbarStyles = tv({\n slots: {\n // Collapsing and expanding resize its parts in place, so none of them is ever the\n // browser's scroll anchor (which would scroll the page to follow it). The root itself\n // may be: its start edge stays put while it resizes, whereas excluding it hands the\n // anchor to the content after it, which moves by the whole change (2026-10-15).\n root: [\n 'inline-flex shrink-0 items-center justify-center [overflow-anchor:auto] **:[overflow-anchor:none]',\n ...exitMotion,\n ],\n // A group of items (main, leading or trailing content).\n items: 'flex shrink-0 items-center',\n collapsibleItems: '',\n // Collapsible leading / trailing content: a box that animates its width (or height) in\n // px between 0 and the content's measured size (2026-10-17, as Compose's\n // `expandHorizontally`), so expanding shows the spatial spring's overshoot, which a\n // 0fr ↔ 1fr track couldn't. Collapsing uses the effects spring, which doesn't\n // overshoot, because a size can't overshoot below 0 and would bounce back open by a\n // pixel or two. Before it is measured (on the server) the box takes the content's\n // natural size, or 0 collapsed. Collapsed content stays focusable and in the\n // accessibility tree.\n collapse: [\n 'flex shrink-0',\n '[transition-property:width,height]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration),var(--md-sys-motion-spring-spatial-fast-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing),var(--md-sys-motion-spring-spatial-fast-easing)]',\n 'data-collapsed:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-effects-fast-easing)]',\n // The content itself changed size (a web font arriving): follow it at once.\n 'data-resized:[transition-duration:0s]',\n ],\n // Clips the content horizontally (or vertically) while it collapses, leaving room for\n // focus rings and touch targets on the other axis. Once the track has closed (a delay\n // as long as the collapse) it clips everything, so no sliver stays visible.\n clip: [\n 'flex min-h-0 min-w-0 data-collapsed:[clip-path:inset(50%)]',\n '[transition-property:clip-path] [transition-duration:0s]',\n 'data-collapsed:[transition-delay:var(--md-sys-motion-spring-effects-fast-duration)]',\n // Under reduced motion the track closes at once, and so does the clip.\n 'motion-reduce:data-collapsed:[transition-delay:0s]',\n ],\n // FAB layout: the toolbar's slot keeps the expanded width while the surface shrinks,\n // collapsing on the effects spring for the same reason as `collapse`.\n toolbarSlot: 'flex shrink-0',\n surface: [\n 'overflow-hidden rounded-[var(--_md-comp-toolbar-floating-container-shape)] opacity-100',\n // Forced colours drop the container and its shadow: a 1px CanvasText boundary instead,\n // as menus and FABs (2026-10-15).\n 'forced-colors:border forced-colors:border-solid forced-colors:border-[CanvasText]',\n 'data-collapsed:opacity-0 data-collapsed:shadow-elevation-0',\n 'data-resized:[transition-duration:0s]',\n // Width, height and shadow move on the spatial spring; the surface also fades on the\n // fast effects spring, so the last pixels of a collapse never show as a thin line.\n // Collapsed, it stays focusable and in the accessibility tree, clipped to nothing.\n '[transition-property:width,height,box-shadow,opacity]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration),var(--md-sys-motion-spring-spatial-fast-duration),var(--md-sys-motion-spring-spatial-fast-duration),var(--md-sys-motion-spring-effects-fast-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing),var(--md-sys-motion-spring-spatial-fast-easing),var(--md-sys-motion-spring-spatial-fast-easing),var(--md-sys-motion-spring-effects-fast-easing)]',\n 'data-collapsed:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n content: 'flex items-center',\n fabSlot: 'flex shrink-0 items-center',\n fabBox: [\n 'flex size-[56px] shrink-0 data-collapsed:size-[80px]',\n '[transition-property:width,height]',\n ...spatialFast,\n ],\n },\n variants: {\n orientation: {\n horizontal: {\n root: 'min-h-[64px] flex-row',\n items: 'flex-row gap-[8px] ps-[4px] pe-[4px]',\n // Collapsible content keeps its natural width while its box closes (it's measured).\n collapsibleItems: 'w-max shrink-0',\n collapse: 'data-collapsed:w-0',\n clip: 'w-full flex-row [clip-path:inset(-12px_-2px)]',\n toolbarSlot: 'h-[64px] w-(--m3-toolbar-size) flex-row',\n surface: 'h-[64px] w-(--m3-toolbar-size) shrink-0 data-collapsed:w-0',\n content: 'h-full w-max flex-row gap-[8px] ps-[12px] pe-[12px]',\n fabSlot: 'w-[56px] flex-row',\n },\n vertical: {\n root: 'min-w-[64px] flex-col',\n items: 'flex-col gap-[8px] pt-[4px] pb-[4px]',\n collapsibleItems: 'h-max shrink-0',\n collapse: 'flex-col data-collapsed:h-0',\n clip: 'h-full flex-col [clip-path:inset(-2px_-12px)]',\n toolbarSlot: 'h-(--m3-toolbar-size) w-[64px] flex-col',\n surface: 'h-(--m3-toolbar-size) w-[64px] shrink-0 data-collapsed:h-0',\n content: 'h-max w-full flex-col gap-[8px] pt-[12px] pb-[12px]',\n fabSlot: 'h-[56px] flex-col',\n },\n },\n color: {\n standard: { root: 'text-on-surface-variant' },\n // Standard and outlined icon buttons take the vibrant content colour.\n vibrant: {\n root: 'text-on-primary-container [--md-comp-icon-button-icon-color:currentColor]',\n },\n },\n /** Level 1 by default; turn it off over visually distinct content. */\n elevated: { true: {}, false: {} },\n exitEdge: exitEdges,\n /** Paints the container on the root (no FAB) or on the toolbar surface (with a FAB). */\n hasFab: {\n false: {\n root: [\n 'rounded-[var(--_md-comp-toolbar-floating-container-shape)] bg-[var(--_md-comp-toolbar-floating-container-color)] p-[8px]',\n 'forced-colors:border forced-colors:border-solid forced-colors:border-[CanvasText]',\n ],\n },\n true: {\n root: 'gap-[8px]',\n surface: 'bg-[var(--_md-comp-toolbar-floating-container-color)]',\n },\n },\n /** The side of the toolbar the FAB sits on (logical: start / top or end / bottom). */\n fabAt: {\n start: { toolbarSlot: 'justify-start', fabSlot: 'justify-start' },\n end: { toolbarSlot: 'justify-end', fabSlot: 'justify-end' },\n },\n },\n compoundVariants: [\n { hasFab: false, elevated: true, class: { root: 'shadow-elevation-1' } },\n // With a FAB, the toolbar's own surface is level 1 expanded and level 0 collapsed.\n { hasFab: true, elevated: true, class: { surface: 'shadow-elevation-1' } },\n // Fixed at the FAB's largest size, as Compose's layout: the FAB's spring overshoot\n // (80 → 82px) spills over the edges instead of resizing the root and moving the page.\n { hasFab: true, orientation: 'horizontal', class: { root: 'h-[80px]' } },\n { hasFab: true, orientation: 'vertical', class: { root: 'w-[80px]' } },\n // The FAB comes first visually but after the toolbar in reading order, as in Compose.\n {\n hasFab: true,\n fabAt: 'start',\n orientation: 'horizontal',\n class: { root: 'flex-row-reverse' },\n },\n { hasFab: true, fabAt: 'start', orientation: 'vertical', class: { root: 'flex-col-reverse' } },\n ],\n defaultVariants: {\n orientation: 'horizontal',\n color: 'standard',\n elevated: true,\n exitEdge: 'bottom',\n hasFab: false,\n fabAt: 'end',\n },\n});\n\n/**\n * Collapsing content is anchored like Compose's AnimatedVisibility: horizontally, leading\n * content expands from the start and shrinks towards the end (trailing: the reverse);\n * vertically, leading content stays at the bottom and trailing content at the top.\n */\nexport const toolbarCollapseAnchor = tv({\n base: '',\n variants: {\n slot: { leading: '', trailing: '' },\n orientation: { horizontal: '', vertical: '' },\n },\n compoundVariants: [\n {\n slot: 'leading',\n orientation: 'horizontal',\n class: 'justify-start data-collapsed:justify-end',\n },\n {\n slot: 'trailing',\n orientation: 'horizontal',\n class: 'justify-end data-collapsed:justify-start',\n },\n { slot: 'leading', orientation: 'vertical', class: 'justify-end' },\n { slot: 'trailing', orientation: 'vertical', class: 'justify-start' },\n ],\n});\n\n/** Variant definitions for {@link ToolbarFab}. */\nexport const toolbarFabStyles = tv({\n slots: {\n root: [\n 'inline-flex size-full cursor-pointer items-center justify-center select-none',\n 'rounded-[var(--_md-comp-toolbar-floating-fab-container-shape)]',\n 'bg-[var(--_md-comp-toolbar-floating-fab-container-color)] text-[color:var(--_md-comp-toolbar-floating-fab-icon-color)]',\n 'state-layer focus-ring container-motion',\n 'forced-colors:border forced-colors:border-solid forced-colors:border-[CanvasText]',\n ],\n icon: 'inline-flex size-[24px] shrink-0 items-center justify-center [&>svg]:size-full',\n },\n variants: {\n // The colours come from the FAB's tokens (`toolbarFabTokenSet`), picked by `data-color`.\n color: { standard: {}, vibrant: {} },\n /**\n * Like `Fab`: level 2 (Compose's toolbar FAB: hover 3, pressed 2), level 1 (hover 2,\n * pressed 1), or level 0 for a flat FAB. The hover level never applies while pressed.\n */\n elevation: {\n default: { root: 'shadow-elevation-2 data-hovered:not-data-pressed:shadow-elevation-3' },\n lowered: { root: 'shadow-elevation-1 data-hovered:not-data-pressed:shadow-elevation-2' },\n flat: { root: 'shadow-elevation-0' },\n },\n },\n defaultVariants: { color: 'standard', elevation: 'default' },\n});\n\n/**\n * The variants of {@link dockedToolbarStyles}, 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 DockedToolbarStyleProps = VariantProps<typeof dockedToolbarStyles>;\nexport type DockedToolbarArrangement = NonNullable<DockedToolbarStyleProps['arrangement']>;\nexport type DockedToolbarColor = NonNullable<DockedToolbarStyleProps['color']>;\n/**\n * The variants of {@link floatingToolbarStyles}, 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 FloatingToolbarStyleProps = VariantProps<typeof floatingToolbarStyles>;\nexport type FloatingToolbarColor = NonNullable<FloatingToolbarStyleProps['color']>;\nexport type ToolbarOrientation = NonNullable<FloatingToolbarStyleProps['orientation']>;\nexport type ToolbarExitEdge = NonNullable<FloatingToolbarStyleProps['exitEdge']>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"toolbar-styles.js","names":[],"sources":["../../../src/components/toolbar/toolbar-styles.ts"],"sourcesContent":["import { tv, type VariantProps } from '../../utils/tv';\n\n/*\n * Values follow the M3 Expressive spec and its tokens (the FloatingToolbar / DockedToolbar\n * token files), with Compose Material 3's FloatingToolbar.kt and AppBar.kt's\n * FlexibleBottomAppBar as the recipe.\n *\n * Docked: 64px, `surface-container`, square corners, no elevation, 16px at each end; items\n * sit 32px apart in the centre (the default) or spread out (space-between, for compact\n * windows).\n *\n * Floating: at least 64px across, full corners, 8px padding, `surface-container` (standard)\n * or `primary-container` (vibrant), level 1 by default (M3: floating toolbars have\n * elevation by default). Content is `on-surface-variant` (standard, the icon-button role)\n * or `on-primary-container` (vibrant). Compose's icon buttons take up 48px of layout while\n * ours take 40px (the 48px touch target is overlaid), so every content group adds 4px at\n * its ends and 8px between items, which reproduces Compose's spacing for icon buttons.\n * Leading / trailing content expands on the fast spatial spring and collapses on the fast\n * effects curve, its box's width (or height) in px between 0 and the content's measured\n * size, so the root's size follows it without transforms. Collapsed content stays in the tab order and\n * the accessibility tree, clipped to nothing, and focusing it expands the toolbar.\n *\n * Exiting on scroll (`visible={false}`): the toolbar slides off its edge on the fast\n * spatial spring (a `transform` set only while hidden, so it composes with Tailwind's\n * `translate-*` utilities); reduced motion fades it instead.\n *\n * With a FAB: the toolbar is level 1 expanded and level 0 collapsed, 8px from the FAB.\n * Collapsing shrinks the toolbar's width to 0 towards the FAB while the FAB grows from 56px\n * to 80px; the component keeps its expanded size, as Compose's layout does.\n *\n * Container colours and the floating toolbar's corners come from the component tokens\n * (`toolbar-tokens.ts`, `--md-comp-toolbar-docked-*` and `--md-comp-toolbar-floating-*`):\n * the roots carry `data-tokens`, the floating one also `data-color`. A consumer class still\n * wins in `cn()`.\n *\n * Every class is written out in full so Tailwind can find it.\n */\n\nconst spatialFast = [\n '[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing)]',\n];\n\n/** The root's exit-on-scroll motion; each edge variant sets the hidden transform. */\nconst exitMotion = [\n 'data-hidden:invisible motion-reduce:data-hidden:opacity-0 motion-reduce:data-hidden:[transform:none]',\n '[transition-property:transform,opacity,visibility]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration),var(--md-sys-motion-spring-effects-fast-duration),max(var(--md-sys-motion-spring-spatial-fast-duration),var(--md-sys-motion-spring-effects-fast-duration))]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing),var(--md-sys-motion-spring-effects-fast-easing),linear]',\n];\n\n/** Where a hidden toolbar goes: past its edge plus the 24px margin of a vertical toolbar. */\nconst exitEdges = {\n bottom: { root: 'data-hidden:[transform:translateY(calc(100%+24px))]' },\n top: { root: 'data-hidden:[transform:translateY(calc(-100%-24px))]' },\n start: {\n root: 'data-hidden:[transform:translateX(calc(-100%-24px))] rtl:data-hidden:[transform:translateX(calc(100%+24px))]',\n },\n end: {\n root: 'data-hidden:[transform:translateX(calc(100%+24px))] rtl:data-hidden:[transform:translateX(calc(-100%-24px))]',\n },\n} as const;\n\n/** Variant definitions for {@link DockedToolbar}; extend them to add variants. */\nexport const dockedToolbarStyles = tv({\n slots: {\n root: [\n 'flex h-[64px] w-full items-center bg-[var(--_md-comp-toolbar-docked-container-color)] ps-[16px] pe-[16px]',\n // Forced colours paint the container as Canvas: a CanvasText line marks its top edge\n // against the page (2026-10-15).\n 'forced-colors:border-t forced-colors:border-solid forced-colors:border-[CanvasText]',\n ...exitMotion,\n 'data-hidden:[transform:translateY(100%)]',\n ],\n },\n variants: {\n arrangement: {\n 'space-between': { root: 'justify-between' },\n centered: { root: 'justify-center gap-[32px]' },\n },\n // As the floating toolbar's (2026-10-15): vibrant is primary-container (its own token,\n // scoped by `data-color`, 2026-10-17), and standard icon buttons take its content colour.\n color: {\n standard: { root: 'text-on-surface-variant' },\n vibrant: {\n root: 'text-on-primary-container [--md-comp-icon-button-icon-color:currentColor]',\n },\n },\n },\n defaultVariants: { arrangement: 'centered', color: 'standard' },\n});\n\n/** Variant definitions for {@link FloatingToolbar}; extend them to add colour styles. */\nexport const floatingToolbarStyles = tv({\n slots: {\n // Collapsing and expanding resize its parts in place, so none of them is ever the\n // browser's scroll anchor (which would scroll the page to follow it). The root itself\n // may be: its start edge stays put while it resizes, whereas excluding it hands the\n // anchor to the content after it, which moves by the whole change (2026-10-15).\n root: [\n 'inline-flex shrink-0 items-center justify-center [overflow-anchor:auto] **:[overflow-anchor:none]',\n ...exitMotion,\n ],\n // A group of items (main, leading or trailing content).\n items: 'flex shrink-0 items-center',\n collapsibleItems: '',\n // Collapsible leading / trailing content: a box that animates its width (or height) in\n // px between 0 and the content's measured size (2026-10-17, as Compose's\n // `expandHorizontally`), so expanding shows the spatial spring's overshoot, which a\n // 0fr ↔ 1fr track couldn't. Collapsing uses the effects spring, which doesn't\n // overshoot, because a size can't overshoot below 0 and would bounce back open by a\n // pixel or two. Before it is measured (on the server) the box takes the content's\n // natural size, or 0 collapsed. Collapsed content stays focusable and in the\n // accessibility tree.\n collapse: [\n 'flex shrink-0',\n '[transition-property:width,height]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration),var(--md-sys-motion-spring-spatial-fast-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing),var(--md-sys-motion-spring-spatial-fast-easing)]',\n 'data-collapsed:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-effects-fast-easing)]',\n // The content itself changed size (a web font arriving): follow it at once.\n 'data-resized:[transition-duration:0s]',\n ],\n // Clips the content horizontally (or vertically) while it collapses, leaving room for\n // focus rings and touch targets on the other axis. Once the track has closed (a delay\n // as long as the collapse) it clips everything, so no sliver stays visible.\n clip: [\n 'flex min-h-0 min-w-0 data-collapsed:[clip-path:inset(50%)]',\n '[transition-property:clip-path] [transition-duration:0s]',\n 'data-collapsed:[transition-delay:var(--md-sys-motion-spring-effects-fast-duration)]',\n // Under reduced motion the track closes at once, and so does the clip.\n 'motion-reduce:data-collapsed:[transition-delay:0s]',\n ],\n // FAB layout: the toolbar's slot keeps the expanded width while the surface shrinks,\n // collapsing on the effects spring for the same reason as `collapse`.\n toolbarSlot: 'flex shrink-0',\n surface: [\n 'overflow-hidden rounded-[var(--_md-comp-toolbar-floating-container-shape)] opacity-100',\n // Forced colours drop the container and its shadow: a 1px CanvasText boundary instead,\n // as menus and FABs (2026-10-15).\n 'forced-colors:border forced-colors:border-solid forced-colors:border-[CanvasText]',\n 'data-collapsed:opacity-0 data-collapsed:shadow-elevation-0',\n 'data-resized:[transition-duration:0s]',\n // Width, height and shadow move on the spatial spring; the surface also fades on the\n // fast effects spring, so the last pixels of a collapse never show as a thin line.\n // Collapsed, it stays focusable and in the accessibility tree, clipped to nothing.\n '[transition-property:width,height,box-shadow,opacity]',\n '[transition-duration:var(--md-sys-motion-spring-spatial-fast-duration),var(--md-sys-motion-spring-spatial-fast-duration),var(--md-sys-motion-spring-spatial-fast-duration),var(--md-sys-motion-spring-effects-fast-duration)]',\n '[transition-timing-function:var(--md-sys-motion-spring-spatial-fast-easing),var(--md-sys-motion-spring-spatial-fast-easing),var(--md-sys-motion-spring-spatial-fast-easing),var(--md-sys-motion-spring-effects-fast-easing)]',\n 'data-collapsed:[transition-timing-function:var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-effects-fast-easing),var(--md-sys-motion-spring-effects-fast-easing)]',\n ],\n content: 'flex items-center',\n fabSlot: 'flex shrink-0 items-center',\n fabBox: [\n 'flex size-[56px] shrink-0 data-collapsed:size-[80px]',\n '[transition-property:width,height]',\n ...spatialFast,\n ],\n },\n variants: {\n orientation: {\n horizontal: {\n root: 'min-h-[64px] flex-row',\n items: 'flex-row gap-[8px] ps-[4px] pe-[4px]',\n // Collapsible content keeps its natural width while its box closes (it's measured).\n collapsibleItems: 'w-max shrink-0',\n // Clipped on its axis while collapsed: the clip slot's `clip-path` hides the content\n // but leaves it in the scrollable area, so a collapsed toolbar by the window's edge\n // scrolled the page sideways (right to left, its leading content past the start edge).\n collapse: 'data-collapsed:w-0 data-collapsed:overflow-x-clip',\n clip: 'w-full flex-row [clip-path:inset(-12px_-2px)]',\n toolbarSlot: 'h-[64px] w-(--m3-toolbar-size) flex-row',\n surface: 'h-[64px] w-(--m3-toolbar-size) shrink-0 data-collapsed:w-0',\n content: 'h-full w-max flex-row gap-[8px] ps-[12px] pe-[12px]',\n fabSlot: 'w-[56px] flex-row',\n },\n vertical: {\n root: 'min-w-[64px] flex-col',\n items: 'flex-col gap-[8px] pt-[4px] pb-[4px]',\n collapsibleItems: 'h-max shrink-0',\n collapse: 'flex-col data-collapsed:h-0 data-collapsed:overflow-y-clip',\n clip: 'h-full flex-col [clip-path:inset(-2px_-12px)]',\n toolbarSlot: 'h-(--m3-toolbar-size) w-[64px] flex-col',\n surface: 'h-(--m3-toolbar-size) w-[64px] shrink-0 data-collapsed:h-0',\n content: 'h-max w-full flex-col gap-[8px] pt-[12px] pb-[12px]',\n fabSlot: 'h-[56px] flex-col',\n },\n },\n color: {\n standard: { root: 'text-on-surface-variant' },\n // Standard and outlined icon buttons take the vibrant content colour.\n vibrant: {\n root: 'text-on-primary-container [--md-comp-icon-button-icon-color:currentColor]',\n },\n },\n /** Level 1 by default; turn it off over visually distinct content. */\n elevated: { true: {}, false: {} },\n exitEdge: exitEdges,\n /** Paints the container on the root (no FAB) or on the toolbar surface (with a FAB). */\n hasFab: {\n false: {\n root: [\n 'rounded-[var(--_md-comp-toolbar-floating-container-shape)] bg-[var(--_md-comp-toolbar-floating-container-color)] p-[8px]',\n 'forced-colors:border forced-colors:border-solid forced-colors:border-[CanvasText]',\n ],\n },\n true: {\n root: 'gap-[8px]',\n surface: 'bg-[var(--_md-comp-toolbar-floating-container-color)]',\n },\n },\n /** The side of the toolbar the FAB sits on (logical: start / top or end / bottom). */\n fabAt: {\n start: { toolbarSlot: 'justify-start', fabSlot: 'justify-start' },\n end: { toolbarSlot: 'justify-end', fabSlot: 'justify-end' },\n },\n },\n compoundVariants: [\n { hasFab: false, elevated: true, class: { root: 'shadow-elevation-1' } },\n // With a FAB, the toolbar's own surface is level 1 expanded and level 0 collapsed.\n { hasFab: true, elevated: true, class: { surface: 'shadow-elevation-1' } },\n // Fixed at the FAB's largest size, as Compose's layout: the FAB's spring overshoot\n // (80 → 82px) spills over the edges instead of resizing the root and moving the page.\n { hasFab: true, orientation: 'horizontal', class: { root: 'h-[80px]' } },\n { hasFab: true, orientation: 'vertical', class: { root: 'w-[80px]' } },\n // The FAB comes first visually but after the toolbar in reading order, as in Compose.\n {\n hasFab: true,\n fabAt: 'start',\n orientation: 'horizontal',\n class: { root: 'flex-row-reverse' },\n },\n { hasFab: true, fabAt: 'start', orientation: 'vertical', class: { root: 'flex-col-reverse' } },\n ],\n defaultVariants: {\n orientation: 'horizontal',\n color: 'standard',\n elevated: true,\n exitEdge: 'bottom',\n hasFab: false,\n fabAt: 'end',\n },\n});\n\n/**\n * Collapsing content is anchored like Compose's AnimatedVisibility: horizontally, leading\n * content expands from the start and shrinks towards the end (trailing: the reverse);\n * vertically, leading content stays at the bottom and trailing content at the top.\n */\nexport const toolbarCollapseAnchor = tv({\n base: '',\n variants: {\n slot: { leading: '', trailing: '' },\n orientation: { horizontal: '', vertical: '' },\n },\n compoundVariants: [\n {\n slot: 'leading',\n orientation: 'horizontal',\n class: 'justify-start data-collapsed:justify-end',\n },\n {\n slot: 'trailing',\n orientation: 'horizontal',\n class: 'justify-end data-collapsed:justify-start',\n },\n { slot: 'leading', orientation: 'vertical', class: 'justify-end' },\n { slot: 'trailing', orientation: 'vertical', class: 'justify-start' },\n ],\n});\n\n/** Variant definitions for {@link ToolbarFab}. */\nexport const toolbarFabStyles = tv({\n slots: {\n root: [\n 'inline-flex size-full cursor-pointer items-center justify-center select-none',\n 'rounded-[var(--_md-comp-toolbar-floating-fab-container-shape)]',\n 'bg-[var(--_md-comp-toolbar-floating-fab-container-color)] text-[color:var(--_md-comp-toolbar-floating-fab-icon-color)]',\n 'state-layer focus-ring container-motion',\n 'forced-colors:border forced-colors:border-solid forced-colors:border-[CanvasText]',\n ],\n icon: 'inline-flex size-[24px] shrink-0 items-center justify-center [&>svg]:size-full',\n },\n variants: {\n // The colours come from the FAB's tokens (`toolbarFabTokenSet`), picked by `data-color`.\n color: { standard: {}, vibrant: {} },\n /**\n * Like `Fab`: level 2 (Compose's toolbar FAB: hover 3, pressed 2), level 1 (hover 2,\n * pressed 1), or level 0 for a flat FAB. The hover level never applies while pressed.\n */\n elevation: {\n default: { root: 'shadow-elevation-2 data-hovered:not-data-pressed:shadow-elevation-3' },\n lowered: { root: 'shadow-elevation-1 data-hovered:not-data-pressed:shadow-elevation-2' },\n flat: { root: 'shadow-elevation-0' },\n },\n },\n defaultVariants: { color: 'standard', elevation: 'default' },\n});\n\n/**\n * The variants of {@link dockedToolbarStyles}, 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 DockedToolbarStyleProps = VariantProps<typeof dockedToolbarStyles>;\nexport type DockedToolbarArrangement = NonNullable<DockedToolbarStyleProps['arrangement']>;\nexport type DockedToolbarColor = NonNullable<DockedToolbarStyleProps['color']>;\n/**\n * The variants of {@link floatingToolbarStyles}, 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 FloatingToolbarStyleProps = VariantProps<typeof floatingToolbarStyles>;\nexport type FloatingToolbarColor = NonNullable<FloatingToolbarStyleProps['color']>;\nexport type ToolbarOrientation = NonNullable<FloatingToolbarStyleProps['orientation']>;\nexport type ToolbarExitEdge = NonNullable<FloatingToolbarStyleProps['exitEdge']>;\n"],"mappings":"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"}
@@ -4,6 +4,6 @@ import "../../index.js";
4
4
  /** The tokens of {@link Tooltip}: `--md-comp-plain-tooltip-<part>`. */
5
5
  export declare const plainTooltipTokenSet: ComponentTokenSet<"container-color" | "container-shape" | "supporting-text-color">;
6
6
  /** The tokens of {@link RichTooltip}: `--md-comp-rich-tooltip-<part>`. */
7
- export declare const richTooltipTokenSet: ComponentTokenSet<"container-color" | "container-elevation" | "container-shape" | "supporting-text-color" | "subhead-color">;
7
+ export declare const richTooltipTokenSet: ComponentTokenSet<"container-color" | "container-shape" | "supporting-text-color" | "container-elevation" | "subhead-color">;
8
8
  //#endregion
9
9
  //# sourceMappingURL=tooltip-tokens.d.ts.map
package/dist/index.d.ts CHANGED
@@ -107,7 +107,8 @@ import { TopAppBar, TopAppBarClassNames, TopAppBarProps, TopAppBarScrolledColor
107
107
  import { appBarTokenSet } from "./components/app-bar/app-bar-tokens.js";
108
108
  import { SearchBar, SearchBarClassNames, SearchBarIconState, SearchBarInputProps, SearchBarProps, SearchBarView } from "./components/search/SearchBar.js";
109
109
  import { SearchAppBar, SearchAppBarClassNames, SearchAppBarProps } from "./components/search/SearchAppBar.js";
110
- import { SearchAppBarStyleProps, SearchBarStyleProps, searchAppBarStyles, searchBarStyles } from "./components/search/search-styles.js";
110
+ import { SearchField, SearchFieldClassNames, SearchFieldInputProps, SearchFieldLabels, SearchFieldProps } from "./components/search/SearchField.js";
111
+ import { SearchAppBarStyleProps, SearchBarStyleProps, SearchFieldStyleProps, searchAppBarStyles, searchBarStyles, searchFieldStyles } from "./components/search/search-styles.js";
111
112
  import { searchBarTokenSet, searchViewTokenSet } from "./components/search/search-tokens.js";
112
113
  import { DividerInset, DividerOrientation, DividerStyleProps, dividerStyles, labeledDividerStyles } from "./components/divider/divider-styles.js";
113
114
  import { Divider, DividerProps, LabeledDivider, LabeledDividerClassNames, LabeledDividerProps } from "./components/divider/Divider.js";
@@ -141,4 +142,4 @@ import { TimeField, TimeFieldClassNames, TimeFieldLabels, TimeFieldProps } from
141
142
  import { TimePickerStyleProps, timePickerStyles } from "./components/time-picker/time-picker-styles.js";
142
143
  import { timePickerTokenSet } from "./components/time-picker/time-picker-tokens.js";
143
144
  import { Time, parseTime } from "@internationalized/date";
144
- export { AssistChip, type AssistChipProps, Autocomplete, type AutocompleteClassNames, AutocompleteItem, type AutocompleteKey, type AutocompleteLabels, type AutocompleteProps, AutocompleteSection, BREAKPOINTS, BREAKPOINT_NAMES, BUILT_IN_THEMES, BUILT_IN_THEME_NAMES, Badge, type BadgeClassNames, type BadgeLabels, type BadgeProps, type BadgeStyleProps, BadgedBox, type BadgedBoxClassNames, type BadgedBoxProps, BottomSheet, type BottomSheetClassNames, type BottomSheetDetent, type BottomSheetLabels, type BottomSheetProps, type BottomSheetStyleProps, BreakpointName, BuiltInThemeName, Button, type ButtonActionProps, type ButtonClassNames, ButtonContext, type ButtonContextValue, ButtonGroup, type ButtonGroupClassNames, type ButtonGroupLabels, type ButtonGroupProps, type ButtonGroupSelectionMode, type ButtonGroupVariant, type ButtonLabels, type ButtonLinkProps, type ButtonProps, type ButtonShape, type ButtonSize, type ButtonStyleProps, type ButtonToggleProps, type ButtonVariant, COLOR_MODES, COLOR_ROLES, CONTRAST_LEVELS, CONTRAST_LEVEL_NAMES, CORNERS, CORNER_NAMES, CUSTOM_COLORS, CUSTOM_COLOR_ROLES, Card, type CardActionProps, type CardLinkProps, type CardProps, type CardStaticProps, type CardStyleProps, type CardVariant, Carousel, type CarouselClassNames, type CarouselProps, type CarouselStyleProps, Checkbox, type CheckboxClassNames, type CheckboxProps, type CheckboxStyleProps, type ChipClassNames, type ChipLabels, type ChipOutline, ChipSet, type ChipSetClassNames, type ChipSetLayout, type ChipSetProps, type ChipStyleProps, CircularProgressIndicator, type CircularProgressIndicatorClassNames, type CircularProgressIndicatorProps, type CircularProgressStyleProps, type ClosedBy, ColorMode, ColorRole, type ComponentToken, type ComponentTokenScope, type ComponentTokenSet, ContextMenuTrigger, type ContextMenuTriggerProps, ContrastLevel, CornerName, type CreateThemeOptions, CustomColorName, DEFAULT_CUSTOM_COLORS, DEFAULT_DENSITY, DEFAULT_MOTION_SCHEME, DEFAULT_STORAGE_KEY, DEFAULT_THEME, DEFAULT_THEME_STATE, DENSITIES, DateField, type DateFieldClassNames, type DateFieldLabels, type DateFieldProps, type DateInputLabels, DatePicker, type DatePickerClassNames, type DatePickerLabels, type DatePickerProps, type DatePickerStyleProps, type DateRange, DateRangePicker, type DateRangePickerClassNames, type DateRangePickerProps, Density, Dialog, DialogActions, type DialogActionsProps, type DialogClassNames, DialogContent, type DialogContentProps, type DialogFullScreen, DialogHeader, type DialogHeaderClassNames, type DialogHeaderLabels, type DialogHeaderProps, type DialogOrigin, type DialogPlacement, type DialogProps, type DialogRenderProps, type DialogStyleProps, DialogTitle, type DialogTitleProps, type DialogTransition, DialogTrigger, type DialogTriggerProps, Divider, type DividerInset, type DividerOrientation, type DividerProps, type DividerStyleProps, DockedToolbar, type DockedToolbarArrangement, type DockedToolbarClassNames, type DockedToolbarColor, type DockedToolbarProps, type DockedToolbarStyleProps, ELEVATION_LEVELS, ELEVATION_LEVEL_NAMES, ElevationLevel, ExtendedFab, type ExtendedFabClassNames, type ExtendedFabProps, type ExtendedFabSize, type ExtendedFabStyleProps, FOCUS_INDICATOR, Fab, type FabClassNames, type FabColor, type FabElevation, FabMenu, type FabMenuClassNames, type FabMenuColor, FabMenuItem, type FabMenuItemClassNames, type FabMenuItemProps, type FabMenuPresentation, type FabMenuProps, type FabMenuSize, type FabMenuStyleProps, type FabProps, type FabSize, type FabStyleProps, FilterChip, type FilterChipClassNames, type FilterChipProps, FloatingToolbar, type FloatingToolbarClassNames, type FloatingToolbarColor, type FloatingToolbarProps, type FloatingToolbarStyleProps, IconButton, type IconButtonActionProps, type IconButtonClassNames, type IconButtonLinkProps, type IconButtonProps, type IconButtonStyleProps, type IconButtonToggleProps, type IconButtonVariant, type IconButtonWidth, type IconTransition, InputChip, type InputChipClassNames, type InputChipProps, LEGACY_DURATIONS, LEGACY_EASINGS, LabeledDivider, type LabeledDividerClassNames, type LabeledDividerProps, type LineLimit, LinearProgressIndicator, type LinearProgressIndicatorClassNames, type LinearProgressIndicatorProps, type LinearProgressStyleProps, List, type ListClassNames, type ListDividers, ListItem, ListItemAvatar, type ListItemAvatarProps, ListItemImage, type ListItemImageProps, type ListItemProps, ListItemVideo, type ListItemVideoProps, type ListProps, ListSection, type ListSectionProps, type ListStyleProps, type ListVariant, LoadingIndicator, type LoadingIndicatorClassNames, type LoadingIndicatorProps, type LoadingIndicatorStyleProps, type LoadingIndicatorVariant, MOTION_SCHEMES, Menu, type MenuClassNames, MenuGroup, type MenuGroupProps, MenuItem, type MenuItemProps, type Key as MenuKey, type MenuLabels, type MenuLayout, type MenuPlacement, type MenuPresentation, type MenuProps, type MenuStyleProps, MenuTrigger, type MenuTriggerProps, type MenuVariant, MotionScheme, MotionSpring, type NavItemClassNames, type NavItemLayout, NavigationBar, type NavigationBarArrangement, type NavigationBarClassNames, NavigationBarItem, type NavigationBarItemProps, type NavigationBarProps, type NavigationBarStyleProps, NavigationRail, type NavigationRailClassNames, NavigationRailItem, type NavigationRailItemProps, type NavigationRailProps, type NavigationRailState, type OptionItemProps, OptionPopover, type OptionSectionProps, PALETTE_NAMES, PaletteName, PaletteSource, PickerDialog, type PickerDialogClassNames, type PickerDialogLabels, type PickerDialogProps, type PublicComponentToken, PullToRefresh, type PullToRefreshClassNames, type PullToRefreshLabels, type PullToRefreshProps, Radio, type RadioClassNames, RadioGroup, type RadioGroupClassNames, type RadioGroupProps, type RadioGroupStyleProps, type RadioProps, type RadioStyleProps, RangeSlider, type RangeSliderLabels, type RangeSliderProps, ResolvedColorMode, RichTooltip, type RichTooltipClassNames, type RichTooltipProps, type RichTooltipStyleProps, RichTooltipTrigger, type RichTooltipTriggerProps, SCHEME_VARIANTS, SHADOW_AMBIENT_OPACITY, SHADOW_KEY_OPACITY, SPRINGS, SPRING_FAMILIES, SPRING_SPEEDS, SPRING_TOKEN_NAMES, STATE_OPACITIES, SchemeVariant, SearchAppBar, type SearchAppBarClassNames, type SearchAppBarProps, type SearchAppBarStyleProps, SearchBar, type SearchBarClassNames, type SearchBarIconState, type SearchBarInputProps, type SearchBarProps, type SearchBarStyleProps, type SearchBarView, Select, type SelectClassNames, SelectItem, type SelectKey, type SelectLabels, type SelectPresentation, type SelectProps, SelectSection, type SelectedOption, SheetTrigger, type SheetTriggerProps, type ShowSnackbarOptions, SideSheet, type SideSheetClassNames, type SideSheetLabels, type SideSheetProps, type SideSheetStyleProps, Slider, type SliderClassNames, type SliderOrientation, type SliderProps, type SliderSize, type SliderStyleProps, Snackbar, type SnackbarClassNames, type SnackbarData, type SnackbarDuration, SnackbarHost, type SnackbarHostProps, SnackbarHostState, type SnackbarLabels, type SnackbarProps, type SnackbarResult, type SnackbarStyleProps, type SnackbarVisuals, SplitButton, type SplitButtonClassNames, type SplitButtonLabels, type SplitButtonProps, type SplitButtonSize, type SplitButtonStyleProps, type SplitButtonVariant, SpringFamily, SpringSpeed, SpringToken, StateName, SubmenuTrigger, type SubmenuTriggerProps, SuggestionChip, type SuggestionChipProps, Switch, type SwitchClassNames, type SwitchProps, type SwitchStyleProps, TYPEFACES, TYPE_ROLES, TYPE_SCALE, Tab, type TabProps, Tabs, type TabsClassNames, type TabsProps, type TabsStyleProps, type TabsVariant, TextField, type TextFieldClassNames, type TextFieldLabels, type TextFieldProps, type TextFieldStyleProps, type TextFieldVariant, type ThemeContextValue, type ThemeDefinition, ThemePalettes, ThemeProvider, type ThemeProviderProps, ThemeScope, type ThemeScopeProps, ThemeScript, type ThemeScriptProps, ThemeSeed, type ThemeState, type ThemeStorage, Time, TimeField, type TimeFieldClassNames, type TimeFieldLabels, type TimeFieldProps, TimePicker, type TimePickerClassNames, type TimePickerLabels, type TimePickerMode, TimePickerModeToggle, type TimePickerModeToggleProps, type TimePickerProps, type TimePickerStyleProps, ToggleGroupStateContext, type TokenPaints, type TokenValue, type ToolbarExitEdge, ToolbarFab, type ToolbarFabClassNames, type ToolbarFabProps, type ToolbarOrientation, type ToolbarScrollExitOptions, type ToolbarScrollExpansionOptions, Tooltip, type TooltipClassNames, type TooltipPlacement, TooltipPlacementContext, type TooltipProps, TooltipTrigger, type TooltipTriggerProps, TopAppBar, type TopAppBarClassNames, type TopAppBarProps, type TopAppBarScrollBehavior, type TopAppBarScrolledColor, type TopAppBarStyleProps, type TopAppBarTitleAlign, type TopAppBarVariant, TypeRole, TypeStyle, Typeface, type VariantProps, ZIndexName, Z_INDEX, appBarTokenSet, assistChipTokenSet, badgeStyles, badgeTokenSet, badgedBoxStyles, bottomSheetStyles, bottomSheetTokenSet, buttonGroupStyles, buttonStyles, buttonTokenSet, buttonVariantClasses, cardStyles, cardTokenSet, carouselStyles, carouselTokenSet, checkboxStyles, checkboxTokenSet, chipSetStyles, chipStyles, circularProgressStyles, cn, color, colorAt, componentTokenList, componentTokensCss, corner, createTheme, datePickerStyles, datePickerTokenSet, defineComponentTokens, densityAttribute, dialogStyles, dialogTokenSet, dividerStyles, dividerTokenSet, dockedDatePickerStyles, dockedToolbarStyles, dockedToolbarTokenSet, elevation, extendedFabStyles, extendedFabTokenSet, fabMenuStyles, fabMenuTokenSet, fabStyles, fabTokenSet, filterChipTokenSet, floatingToolbarStyles, floatingToolbarTokenSet, getM3Spring, getThemeScriptSource, iconButtonStyles, iconButtonTokenSet, inputChipTokenSet, isComponentTokenSet, isDensity, labeledDividerStyles, linearProgressStyles, listMediaStyles, listStyles, listTokenSet, loadingIndicatorStyles, loadingIndicatorTokenSet, matchesSearch, menuStyles, menuTokenSet, navBarTokenSet, navItemStyles, navRailTokenSet, navigationBarStyles, navigationRailStyles, normalizeSearch, parseThemeState, parseTime, pickerDialogStyles, plainTooltipTokenSet, progressIndicatorTokenSet, pullToRefreshStyles, px, radioGroupStyles, radioStyles, radioTokenSet, richTooltipStyles, richTooltipTokenSet, searchAppBarStyles, searchBarStyles, searchBarTokenSet, searchViewTokenSet, serializeThemeState, sideSheetStyles, sideSheetTokenSet, sliderStyles, sliderTokenSet, snackbarHostStyles, snackbarStyles, snackbarTokenSet, splitButtonStyles, splitButtonTokenSet, spring, springPosition, springSettleDuration, springToLinearEasing, suggestionChipTokenSet, switchStyles, switchTokenSet, tabsStyles, tabsTokenSet, textFieldStyles, textFieldTokenSet, themeAttributes, timePickerStyles, timePickerTokenSet, toMotionSpring, toolbarFabStyles, toolbarFabTokenSet, tooltipStyles, topAppBarStyles, tv, twMergeConfig, typeTokens, typescale, useM3Spring, useNavigationRail, useSnackbarHostState, useTheme, useThemeScope, useTimePickerRoom, useToolbarScrollExit, useToolbarScrollExpansion };
145
+ export { AssistChip, type AssistChipProps, Autocomplete, type AutocompleteClassNames, AutocompleteItem, type AutocompleteKey, type AutocompleteLabels, type AutocompleteProps, AutocompleteSection, BREAKPOINTS, BREAKPOINT_NAMES, BUILT_IN_THEMES, BUILT_IN_THEME_NAMES, Badge, type BadgeClassNames, type BadgeLabels, type BadgeProps, type BadgeStyleProps, BadgedBox, type BadgedBoxClassNames, type BadgedBoxProps, BottomSheet, type BottomSheetClassNames, type BottomSheetDetent, type BottomSheetLabels, type BottomSheetProps, type BottomSheetStyleProps, BreakpointName, BuiltInThemeName, Button, type ButtonActionProps, type ButtonClassNames, ButtonContext, type ButtonContextValue, ButtonGroup, type ButtonGroupClassNames, type ButtonGroupLabels, type ButtonGroupProps, type ButtonGroupSelectionMode, type ButtonGroupVariant, type ButtonLabels, type ButtonLinkProps, type ButtonProps, type ButtonShape, type ButtonSize, type ButtonStyleProps, type ButtonToggleProps, type ButtonVariant, COLOR_MODES, COLOR_ROLES, CONTRAST_LEVELS, CONTRAST_LEVEL_NAMES, CORNERS, CORNER_NAMES, CUSTOM_COLORS, CUSTOM_COLOR_ROLES, Card, type CardActionProps, type CardLinkProps, type CardProps, type CardStaticProps, type CardStyleProps, type CardVariant, Carousel, type CarouselClassNames, type CarouselProps, type CarouselStyleProps, Checkbox, type CheckboxClassNames, type CheckboxProps, type CheckboxStyleProps, type ChipClassNames, type ChipLabels, type ChipOutline, ChipSet, type ChipSetClassNames, type ChipSetLayout, type ChipSetProps, type ChipStyleProps, CircularProgressIndicator, type CircularProgressIndicatorClassNames, type CircularProgressIndicatorProps, type CircularProgressStyleProps, type ClosedBy, ColorMode, ColorRole, type ComponentToken, type ComponentTokenScope, type ComponentTokenSet, ContextMenuTrigger, type ContextMenuTriggerProps, ContrastLevel, CornerName, type CreateThemeOptions, CustomColorName, DEFAULT_CUSTOM_COLORS, DEFAULT_DENSITY, DEFAULT_MOTION_SCHEME, DEFAULT_STORAGE_KEY, DEFAULT_THEME, DEFAULT_THEME_STATE, DENSITIES, DateField, type DateFieldClassNames, type DateFieldLabels, type DateFieldProps, type DateInputLabels, DatePicker, type DatePickerClassNames, type DatePickerLabels, type DatePickerProps, type DatePickerStyleProps, type DateRange, DateRangePicker, type DateRangePickerClassNames, type DateRangePickerProps, Density, Dialog, DialogActions, type DialogActionsProps, type DialogClassNames, DialogContent, type DialogContentProps, type DialogFullScreen, DialogHeader, type DialogHeaderClassNames, type DialogHeaderLabels, type DialogHeaderProps, type DialogOrigin, type DialogPlacement, type DialogProps, type DialogRenderProps, type DialogStyleProps, DialogTitle, type DialogTitleProps, type DialogTransition, DialogTrigger, type DialogTriggerProps, Divider, type DividerInset, type DividerOrientation, type DividerProps, type DividerStyleProps, DockedToolbar, type DockedToolbarArrangement, type DockedToolbarClassNames, type DockedToolbarColor, type DockedToolbarProps, type DockedToolbarStyleProps, ELEVATION_LEVELS, ELEVATION_LEVEL_NAMES, ElevationLevel, ExtendedFab, type ExtendedFabClassNames, type ExtendedFabProps, type ExtendedFabSize, type ExtendedFabStyleProps, FOCUS_INDICATOR, Fab, type FabClassNames, type FabColor, type FabElevation, FabMenu, type FabMenuClassNames, type FabMenuColor, FabMenuItem, type FabMenuItemClassNames, type FabMenuItemProps, type FabMenuPresentation, type FabMenuProps, type FabMenuSize, type FabMenuStyleProps, type FabProps, type FabSize, type FabStyleProps, FilterChip, type FilterChipClassNames, type FilterChipProps, FloatingToolbar, type FloatingToolbarClassNames, type FloatingToolbarColor, type FloatingToolbarProps, type FloatingToolbarStyleProps, IconButton, type IconButtonActionProps, type IconButtonClassNames, type IconButtonLinkProps, type IconButtonProps, type IconButtonStyleProps, type IconButtonToggleProps, type IconButtonVariant, type IconButtonWidth, type IconTransition, InputChip, type InputChipClassNames, type InputChipProps, LEGACY_DURATIONS, LEGACY_EASINGS, LabeledDivider, type LabeledDividerClassNames, type LabeledDividerProps, type LineLimit, LinearProgressIndicator, type LinearProgressIndicatorClassNames, type LinearProgressIndicatorProps, type LinearProgressStyleProps, List, type ListClassNames, type ListDividers, ListItem, ListItemAvatar, type ListItemAvatarProps, ListItemImage, type ListItemImageProps, type ListItemProps, ListItemVideo, type ListItemVideoProps, type ListProps, ListSection, type ListSectionProps, type ListStyleProps, type ListVariant, LoadingIndicator, type LoadingIndicatorClassNames, type LoadingIndicatorProps, type LoadingIndicatorStyleProps, type LoadingIndicatorVariant, MOTION_SCHEMES, Menu, type MenuClassNames, MenuGroup, type MenuGroupProps, MenuItem, type MenuItemProps, type Key as MenuKey, type MenuLabels, type MenuLayout, type MenuPlacement, type MenuPresentation, type MenuProps, type MenuStyleProps, MenuTrigger, type MenuTriggerProps, type MenuVariant, MotionScheme, MotionSpring, type NavItemClassNames, type NavItemLayout, NavigationBar, type NavigationBarArrangement, type NavigationBarClassNames, NavigationBarItem, type NavigationBarItemProps, type NavigationBarProps, type NavigationBarStyleProps, NavigationRail, type NavigationRailClassNames, NavigationRailItem, type NavigationRailItemProps, type NavigationRailProps, type NavigationRailState, type OptionItemProps, OptionPopover, type OptionSectionProps, PALETTE_NAMES, PaletteName, PaletteSource, PickerDialog, type PickerDialogClassNames, type PickerDialogLabels, type PickerDialogProps, type PublicComponentToken, PullToRefresh, type PullToRefreshClassNames, type PullToRefreshLabels, type PullToRefreshProps, Radio, type RadioClassNames, RadioGroup, type RadioGroupClassNames, type RadioGroupProps, type RadioGroupStyleProps, type RadioProps, type RadioStyleProps, RangeSlider, type RangeSliderLabels, type RangeSliderProps, ResolvedColorMode, RichTooltip, type RichTooltipClassNames, type RichTooltipProps, type RichTooltipStyleProps, RichTooltipTrigger, type RichTooltipTriggerProps, SCHEME_VARIANTS, SHADOW_AMBIENT_OPACITY, SHADOW_KEY_OPACITY, SPRINGS, SPRING_FAMILIES, SPRING_SPEEDS, SPRING_TOKEN_NAMES, STATE_OPACITIES, SchemeVariant, SearchAppBar, type SearchAppBarClassNames, type SearchAppBarProps, type SearchAppBarStyleProps, SearchBar, type SearchBarClassNames, type SearchBarIconState, type SearchBarInputProps, type SearchBarProps, type SearchBarStyleProps, type SearchBarView, SearchField, type SearchFieldClassNames, type SearchFieldInputProps, type SearchFieldLabels, type SearchFieldProps, type SearchFieldStyleProps, Select, type SelectClassNames, SelectItem, type SelectKey, type SelectLabels, type SelectPresentation, type SelectProps, SelectSection, type SelectedOption, SheetTrigger, type SheetTriggerProps, type ShowSnackbarOptions, SideSheet, type SideSheetClassNames, type SideSheetLabels, type SideSheetProps, type SideSheetStyleProps, Slider, type SliderClassNames, type SliderOrientation, type SliderProps, type SliderSize, type SliderStyleProps, Snackbar, type SnackbarClassNames, type SnackbarData, type SnackbarDuration, SnackbarHost, type SnackbarHostProps, SnackbarHostState, type SnackbarLabels, type SnackbarProps, type SnackbarResult, type SnackbarStyleProps, type SnackbarVisuals, SplitButton, type SplitButtonClassNames, type SplitButtonLabels, type SplitButtonProps, type SplitButtonSize, type SplitButtonStyleProps, type SplitButtonVariant, SpringFamily, SpringSpeed, SpringToken, StateName, SubmenuTrigger, type SubmenuTriggerProps, SuggestionChip, type SuggestionChipProps, Switch, type SwitchClassNames, type SwitchProps, type SwitchStyleProps, TYPEFACES, TYPE_ROLES, TYPE_SCALE, Tab, type TabProps, Tabs, type TabsClassNames, type TabsProps, type TabsStyleProps, type TabsVariant, TextField, type TextFieldClassNames, type TextFieldLabels, type TextFieldProps, type TextFieldStyleProps, type TextFieldVariant, type ThemeContextValue, type ThemeDefinition, ThemePalettes, ThemeProvider, type ThemeProviderProps, ThemeScope, type ThemeScopeProps, ThemeScript, type ThemeScriptProps, ThemeSeed, type ThemeState, type ThemeStorage, Time, TimeField, type TimeFieldClassNames, type TimeFieldLabels, type TimeFieldProps, TimePicker, type TimePickerClassNames, type TimePickerLabels, type TimePickerMode, TimePickerModeToggle, type TimePickerModeToggleProps, type TimePickerProps, type TimePickerStyleProps, ToggleGroupStateContext, type TokenPaints, type TokenValue, type ToolbarExitEdge, ToolbarFab, type ToolbarFabClassNames, type ToolbarFabProps, type ToolbarOrientation, type ToolbarScrollExitOptions, type ToolbarScrollExpansionOptions, Tooltip, type TooltipClassNames, type TooltipPlacement, TooltipPlacementContext, type TooltipProps, TooltipTrigger, type TooltipTriggerProps, TopAppBar, type TopAppBarClassNames, type TopAppBarProps, type TopAppBarScrollBehavior, type TopAppBarScrolledColor, type TopAppBarStyleProps, type TopAppBarTitleAlign, type TopAppBarVariant, TypeRole, TypeStyle, Typeface, type VariantProps, ZIndexName, Z_INDEX, appBarTokenSet, assistChipTokenSet, badgeStyles, badgeTokenSet, badgedBoxStyles, bottomSheetStyles, bottomSheetTokenSet, buttonGroupStyles, buttonStyles, buttonTokenSet, buttonVariantClasses, cardStyles, cardTokenSet, carouselStyles, carouselTokenSet, checkboxStyles, checkboxTokenSet, chipSetStyles, chipStyles, circularProgressStyles, cn, color, colorAt, componentTokenList, componentTokensCss, corner, createTheme, datePickerStyles, datePickerTokenSet, defineComponentTokens, densityAttribute, dialogStyles, dialogTokenSet, dividerStyles, dividerTokenSet, dockedDatePickerStyles, dockedToolbarStyles, dockedToolbarTokenSet, elevation, extendedFabStyles, extendedFabTokenSet, fabMenuStyles, fabMenuTokenSet, fabStyles, fabTokenSet, filterChipTokenSet, floatingToolbarStyles, floatingToolbarTokenSet, getM3Spring, getThemeScriptSource, iconButtonStyles, iconButtonTokenSet, inputChipTokenSet, isComponentTokenSet, isDensity, labeledDividerStyles, linearProgressStyles, listMediaStyles, listStyles, listTokenSet, loadingIndicatorStyles, loadingIndicatorTokenSet, matchesSearch, menuStyles, menuTokenSet, navBarTokenSet, navItemStyles, navRailTokenSet, navigationBarStyles, navigationRailStyles, normalizeSearch, parseThemeState, parseTime, pickerDialogStyles, plainTooltipTokenSet, progressIndicatorTokenSet, pullToRefreshStyles, px, radioGroupStyles, radioStyles, radioTokenSet, richTooltipStyles, richTooltipTokenSet, searchAppBarStyles, searchBarStyles, searchBarTokenSet, searchFieldStyles, searchViewTokenSet, serializeThemeState, sideSheetStyles, sideSheetTokenSet, sliderStyles, sliderTokenSet, snackbarHostStyles, snackbarStyles, snackbarTokenSet, splitButtonStyles, splitButtonTokenSet, spring, springPosition, springSettleDuration, springToLinearEasing, suggestionChipTokenSet, switchStyles, switchTokenSet, tabsStyles, tabsTokenSet, textFieldStyles, textFieldTokenSet, themeAttributes, timePickerStyles, timePickerTokenSet, toMotionSpring, toolbarFabStyles, toolbarFabTokenSet, tooltipStyles, topAppBarStyles, tv, twMergeConfig, typeTokens, typescale, useM3Spring, useNavigationRail, useSnackbarHostState, useTheme, useThemeScope, useTimePickerRoom, useToolbarScrollExit, useToolbarScrollExpansion };