react-material-expressive 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -563,7 +563,8 @@ function useOutsideClose(ref, onClose, enabled = true) {
563
563
  const target = event.target;
564
564
  if (!element || !(target instanceof Node)) return;
565
565
  if (element.contains(target)) return;
566
- if (target instanceof Element && target.closest('[role="menu"]')) return;
566
+ if (target instanceof Element && target.closest('[role="menu"],[role="listbox"]'))
567
+ return;
567
568
  onClose();
568
569
  };
569
570
  document.addEventListener("pointerdown", handler);
@@ -798,6 +799,83 @@ function IconButton({
798
799
  }
799
800
  );
800
801
  }
802
+ var useIsomorphicLayoutEffect = typeof window === "undefined" ? react.useEffect : react.useLayoutEffect;
803
+
804
+ // src/components/_usePopoverPosition.ts
805
+ var MARGIN = 8;
806
+ var FALLBACK_WIDTH = 224;
807
+ function usePopoverPosition(anchor, floating, open, options = {}) {
808
+ const { gap = 0, matchWidth = false, placement = "bottom-start" } = options;
809
+ const [pos, setPos] = react.useState({
810
+ flippedVertically: false,
811
+ left: 0,
812
+ top: 0
813
+ });
814
+ useIsomorphicLayoutEffect(() => {
815
+ if (!open) return;
816
+ const compute = () => {
817
+ const a = anchor.current?.getBoundingClientRect();
818
+ if (!a) return;
819
+ const f = floating.current?.getBoundingClientRect();
820
+ const width = matchWidth ? a.width : f?.width || FALLBACK_WIDTH;
821
+ const height = f?.height || 0;
822
+ const vw = window.innerWidth;
823
+ const vh = window.innerHeight;
824
+ const dash = placement.indexOf("-");
825
+ const side = placement.slice(0, dash);
826
+ const align = placement.slice(dash + 1);
827
+ let left;
828
+ let top;
829
+ let flippedVertically = false;
830
+ if (side === "right") {
831
+ left = a.right + gap;
832
+ if (left + width > vw - MARGIN) {
833
+ const flipped = a.left - width - gap;
834
+ left = flipped >= MARGIN ? flipped : Math.max(MARGIN, vw - MARGIN - width);
835
+ }
836
+ top = a.top;
837
+ if (top + height > vh - MARGIN) {
838
+ top = Math.max(MARGIN, vh - MARGIN - height);
839
+ }
840
+ } else {
841
+ left = align === "end" ? a.right - width : a.left;
842
+ if (left + width > vw - MARGIN) left = vw - MARGIN - width;
843
+ if (left < MARGIN) left = MARGIN;
844
+ const below = a.bottom + gap;
845
+ const above = a.top - height - gap;
846
+ const roomBelow = vh - MARGIN - a.bottom;
847
+ const roomAbove = a.top - MARGIN;
848
+ if (side === "bottom") {
849
+ if (height > roomBelow && roomAbove > roomBelow) {
850
+ top = Math.max(MARGIN, above);
851
+ flippedVertically = true;
852
+ } else {
853
+ top = below;
854
+ }
855
+ } else if (height > roomAbove && roomBelow > roomAbove) {
856
+ top = below;
857
+ flippedVertically = true;
858
+ } else {
859
+ top = Math.max(MARGIN, above);
860
+ }
861
+ }
862
+ setPos({
863
+ flippedVertically,
864
+ left,
865
+ top,
866
+ width: matchWidth ? a.width : void 0
867
+ });
868
+ };
869
+ compute();
870
+ window.addEventListener("scroll", compute, true);
871
+ window.addEventListener("resize", compute);
872
+ return () => {
873
+ window.removeEventListener("scroll", compute, true);
874
+ window.removeEventListener("resize", compute);
875
+ };
876
+ }, [anchor, floating, open, gap, matchWidth, placement]);
877
+ return pos;
878
+ }
801
879
  var MENU_NOOP = () => {
802
880
  };
803
881
  var MenuContext = react.createContext({
@@ -920,41 +998,6 @@ function MenuLabel({ children, className, leftElement }) {
920
998
  }
921
999
  );
922
1000
  }
923
- var useIsomorphicLayoutEffect = typeof window === "undefined" ? react.useEffect : react.useLayoutEffect;
924
-
925
- // src/components/menu/_useSubmenu.ts
926
- var MARGIN = 8;
927
- function useSubmenuPosition(anchor, floating, open) {
928
- const [pos, setPos] = react.useState({ left: 0, top: 0 });
929
- useIsomorphicLayoutEffect(() => {
930
- if (!open) return;
931
- const compute = () => {
932
- const a = anchor.current?.getBoundingClientRect();
933
- if (!a) return;
934
- const f = floating.current?.getBoundingClientRect();
935
- const width = f?.width || 224;
936
- const height = f?.height || 0;
937
- let left = a.right;
938
- if (left + width > window.innerWidth - MARGIN) {
939
- const flipped = a.left - width;
940
- left = flipped >= MARGIN ? flipped : Math.max(MARGIN, window.innerWidth - MARGIN - width);
941
- }
942
- let top = a.top;
943
- if (top + height > window.innerHeight - MARGIN) {
944
- top = Math.max(MARGIN, window.innerHeight - MARGIN - height);
945
- }
946
- setPos({ left, top });
947
- };
948
- compute();
949
- window.addEventListener("scroll", compute, true);
950
- window.addEventListener("resize", compute);
951
- return () => {
952
- window.removeEventListener("scroll", compute, true);
953
- window.removeEventListener("resize", compute);
954
- };
955
- }, [anchor, floating, open]);
956
- return pos;
957
- }
958
1001
  var HOVER_OPEN_MS = 120;
959
1002
  var HOVER_CLOSE_MS = 220;
960
1003
  function ChevronRight() {
@@ -984,7 +1027,9 @@ function MenuSub({
984
1027
  const trigger = react.useRef(null);
985
1028
  const floating = react.useRef(null);
986
1029
  const timer = react.useRef(void 0);
987
- const pos = useSubmenuPosition(trigger, floating, open);
1030
+ const pos = usePopoverPosition(trigger, floating, open, {
1031
+ placement: "right-start"
1032
+ });
988
1033
  const clear = () => timer.current && clearTimeout(timer.current);
989
1034
  const hoverOpen = () => {
990
1035
  clear();
@@ -1065,7 +1110,7 @@ function MenuSub({
1065
1110
  /* @__PURE__ */ jsxRuntime.jsx(
1066
1111
  "div",
1067
1112
  {
1068
- className: "fixed z-50",
1113
+ className: "fixed z-[var(--md-sys-z-menu)]",
1069
1114
  onMouseEnter: clear,
1070
1115
  onMouseLeave: hoverClose,
1071
1116
  ref: floating,
@@ -1180,7 +1225,7 @@ function Menu({
1180
1225
  // outline-none: the surface is focusable (tabIndex -1) so a
1181
1226
  // root menu can focus it on open without highlighting any
1182
1227
  // item — it must never show a focus ring of its own.
1183
- "z-50 flex w-max min-w-[112px] max-w-[280px] flex-col outline-none [--menu-clip-bleed:-24px]",
1228
+ "z-[var(--md-sys-z-menu)] flex w-max min-w-[112px] max-w-[280px] flex-col outline-none [--menu-clip-bleed:-24px]",
1184
1229
  grouped ? (
1185
1230
  // Transparent stack: each Menu.Group is its own
1186
1231
  // surface, separated by a 2dp gap. overflow-visible
@@ -1288,8 +1333,13 @@ function SplitButton({
1288
1333
  const [isOpen, setIsOpen] = react.useState(false);
1289
1334
  const wrapper = react.useRef(null);
1290
1335
  const trailing = react.useRef(null);
1336
+ const floating = react.useRef(null);
1291
1337
  const wasOpen = react.useRef(false);
1292
1338
  const { exiting, mounted } = useDismissable(isOpen, 150);
1339
+ const pos = usePopoverPosition(wrapper, floating, mounted, {
1340
+ gap: 8,
1341
+ placement: "bottom-end"
1342
+ });
1293
1343
  const config = SIZES4[size];
1294
1344
  const corners = (classes) => disabled ? classes.replace(/ (?:hover|focus-visible|active):\S+/g, "") : classes;
1295
1345
  useOutsideClose(wrapper, () => setIsOpen(false), isOpen);
@@ -1363,15 +1413,27 @@ function SplitButton({
1363
1413
  )
1364
1414
  }
1365
1415
  ),
1366
- mounted ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-full right-0 z-20 flex flex-col", children: /* @__PURE__ */ jsxRuntime.jsx(
1367
- Menu,
1368
- {
1369
- className: cn("mt-2", menuClassName),
1370
- exiting,
1371
- onClose: () => setIsOpen(false),
1372
- children: menu
1373
- }
1374
- ) }) : null
1416
+ mounted && typeof document !== "undefined" ? reactDom.createPortal(
1417
+ /* @__PURE__ */ jsxRuntime.jsx(
1418
+ "div",
1419
+ {
1420
+ className: "fixed z-[var(--md-sys-z-menu)]",
1421
+ ref: floating,
1422
+ style: { left: pos.left, top: pos.top },
1423
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1424
+ Menu,
1425
+ {
1426
+ className: menuClassName,
1427
+ exiting,
1428
+ onClose: () => setIsOpen(false),
1429
+ up: pos.flippedVertically,
1430
+ children: menu
1431
+ }
1432
+ )
1433
+ }
1434
+ ),
1435
+ document.body
1436
+ ) : null
1375
1437
  ]
1376
1438
  }
1377
1439
  );
@@ -1550,37 +1612,981 @@ function Chips({
1550
1612
  children: /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { selected })
1551
1613
  }
1552
1614
  ),
1553
- /* @__PURE__ */ jsxRuntime.jsx(
1554
- "span",
1615
+ /* @__PURE__ */ jsxRuntime.jsx(
1616
+ "span",
1617
+ {
1618
+ className: cn(
1619
+ "absolute inset-0 flex items-center justify-center",
1620
+ selected ? "ease-md-linear opacity-0 transition-opacity duration-[50ms]" : "ease-legacy opacity-100 transition-opacity delay-[50ms] duration-150",
1621
+ accentIcon && !disabled && "text-primary"
1622
+ ),
1623
+ children: leftElement
1624
+ }
1625
+ )
1626
+ ] })
1627
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1628
+ Icon,
1629
+ {
1630
+ className: cn(
1631
+ accentIcon && !disabled && !hasAvatar && "text-primary"
1632
+ ),
1633
+ iconLeft: leftElement,
1634
+ size: hasAvatar ? 24 : 18
1635
+ }
1636
+ ) : null,
1637
+ children ?? text,
1638
+ rightElement ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { iconRight: rightElement, size: 18 }) : null
1639
+ ]
1640
+ }
1641
+ );
1642
+ return removeButton ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative inline-flex w-fit align-middle", children: [
1643
+ chip,
1644
+ removeButton
1645
+ ] }) : chip;
1646
+ }
1647
+ function FloatingLabel({
1648
+ children,
1649
+ className,
1650
+ floating,
1651
+ floatingClassName,
1652
+ htmlFor,
1653
+ restingClassName
1654
+ }) {
1655
+ const floatingRef = react.useRef(null);
1656
+ const restingRef = react.useRef(null);
1657
+ const animationRef = react.useRef(null);
1658
+ const prevFloating = react.useRef(floating);
1659
+ const [animating, setAnimating] = react.useState(false);
1660
+ useIsomorphicLayoutEffect(() => {
1661
+ if (prevFloating.current === floating) return;
1662
+ prevFloating.current = floating;
1663
+ const floatingEl = floatingRef.current;
1664
+ const restingEl = restingRef.current;
1665
+ if (!floatingEl || !restingEl) return;
1666
+ const f = floatingEl.getBoundingClientRect();
1667
+ const r = restingEl.getBoundingClientRect();
1668
+ if (!f.width || !r.width) return;
1669
+ const scale = r.width / f.width;
1670
+ const xDelta = r.x - f.x;
1671
+ const yDelta = r.y - f.y + Math.round((r.height - f.height * scale) / 2);
1672
+ const rest = `translateX(${xDelta}px) translateY(${yDelta}px) scale(${scale})`;
1673
+ const float = `translateX(0) translateY(0) scale(1)`;
1674
+ animationRef.current?.cancel();
1675
+ setAnimating(true);
1676
+ const animation = floatingEl.animate(
1677
+ { transform: floating ? [rest, float] : [float, rest] },
1678
+ {
1679
+ duration: 150,
1680
+ easing: "cubic-bezier(0.2, 0, 0, 1)",
1681
+ fill: "forwards"
1682
+ }
1683
+ );
1684
+ animationRef.current = animation;
1685
+ const done = () => {
1686
+ if (animationRef.current === animation) setAnimating(false);
1687
+ };
1688
+ animation.addEventListener("finish", done);
1689
+ animation.addEventListener("cancel", done);
1690
+ }, [floating]);
1691
+ useIsomorphicLayoutEffect(() => {
1692
+ if (animating || !animationRef.current) return;
1693
+ animationRef.current.cancel();
1694
+ animationRef.current = null;
1695
+ }, [animating]);
1696
+ const showFloating = floating || animating;
1697
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1698
+ /* @__PURE__ */ jsxRuntime.jsx(
1699
+ "label",
1700
+ {
1701
+ "aria-hidden": true,
1702
+ className: cn(
1703
+ "pointer-events-none absolute",
1704
+ floatingClassName,
1705
+ className,
1706
+ !showFloating && "opacity-0"
1707
+ ),
1708
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block origin-top-left", ref: floatingRef, children })
1709
+ }
1710
+ ),
1711
+ /* @__PURE__ */ jsxRuntime.jsx(
1712
+ "label",
1713
+ {
1714
+ className: cn(
1715
+ "pointer-events-none absolute",
1716
+ restingClassName,
1717
+ className,
1718
+ showFloating && "opacity-0"
1719
+ ),
1720
+ htmlFor,
1721
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block", ref: restingRef, children })
1722
+ }
1723
+ )
1724
+ ] });
1725
+ }
1726
+ function useFieldState(value, defaultValue, disabled) {
1727
+ const [focused, setFocusedState] = react.useState(false);
1728
+ const [internalHasValue, setInternalHasValue] = react.useState(
1729
+ defaultValue !== void 0 && defaultValue !== null && String(defaultValue).length > 0
1730
+ );
1731
+ const hasValue = value !== void 0 && value !== null ? String(value).length > 0 : internalHasValue;
1732
+ return {
1733
+ focused: focused && !disabled,
1734
+ hasValue,
1735
+ setFocused: setFocusedState,
1736
+ setInternalHasValue
1737
+ };
1738
+ }
1739
+ function labelColor(opts) {
1740
+ if (opts.disabled) return "text-on-surface/38";
1741
+ if (opts.error) return "text-error";
1742
+ if (opts.focused) return "text-primary";
1743
+ return "text-on-surface-variant";
1744
+ }
1745
+ function SupportingText({
1746
+ error,
1747
+ errorText,
1748
+ id,
1749
+ supportingText
1750
+ }) {
1751
+ const isError = !!(error && errorText);
1752
+ const text = isError ? errorText : supportingText;
1753
+ if (!text) return null;
1754
+ return /* @__PURE__ */ jsxRuntime.jsx(
1755
+ "p",
1756
+ {
1757
+ role: isError ? "alert" : void 0,
1758
+ id,
1759
+ className: cn(
1760
+ "px-4 pt-1 text-body-small",
1761
+ error ? "text-error" : "text-on-surface-variant"
1762
+ ),
1763
+ children: text
1764
+ }
1765
+ );
1766
+ }
1767
+ function FieldIcon({
1768
+ children,
1769
+ className
1770
+ }) {
1771
+ return /* @__PURE__ */ jsxRuntime.jsx(
1772
+ "span",
1773
+ {
1774
+ className: cn(
1775
+ "pointer-events-auto absolute flex h-6 w-6 items-center justify-center leading-none text-on-surface-variant",
1776
+ className
1777
+ ),
1778
+ children
1779
+ }
1780
+ );
1781
+ }
1782
+ function useControlled(controlled, defaultValue) {
1783
+ const isControlled = react.useRef(controlled !== void 0).current;
1784
+ const [internal, setInternal] = react.useState(defaultValue);
1785
+ const value = isControlled && controlled !== void 0 ? controlled : internal;
1786
+ const setValue = react.useCallback(
1787
+ (next) => {
1788
+ if (!isControlled) setInternal(next);
1789
+ },
1790
+ [isControlled]
1791
+ );
1792
+ return [value, setValue];
1793
+ }
1794
+ var TYPEAHEAD_BUFFER_MS = 200;
1795
+ function optionText(option) {
1796
+ return typeof option.label === "string" ? option.label : option.value;
1797
+ }
1798
+ function useSelect({
1799
+ defaultValue,
1800
+ disabled,
1801
+ onChange,
1802
+ options,
1803
+ value: valueProp
1804
+ }) {
1805
+ const [value, setValue] = useControlled(valueProp, defaultValue ?? "");
1806
+ const [isOpen, setIsOpen] = react.useState(false);
1807
+ const [focused, setFocused] = react.useState(false);
1808
+ const [highlighted, setHighlighted] = react.useState(-1);
1809
+ const wrapper = react.useRef(null);
1810
+ const typeahead = react.useRef({ buffer: "", time: 0 });
1811
+ const { exiting, mounted } = useDismissable(isOpen, 150);
1812
+ useOutsideClose(wrapper, () => setIsOpen(false), isOpen);
1813
+ const selectedIndex = options.findIndex((option) => option.value === value);
1814
+ const move = (start, delta) => {
1815
+ for (let step = 1; step <= options.length; step += 1) {
1816
+ const index = (start + delta * step + options.length * step) % options.length;
1817
+ if (!options[index]?.disabled) return index;
1818
+ }
1819
+ return -1;
1820
+ };
1821
+ const openMenu = () => {
1822
+ if (disabled) return;
1823
+ setHighlighted(
1824
+ selectedIndex >= 0 && !options[selectedIndex]?.disabled ? selectedIndex : move(-1, 1)
1825
+ );
1826
+ setIsOpen(true);
1827
+ };
1828
+ const commit = (next) => {
1829
+ setValue(next);
1830
+ onChange?.(next);
1831
+ setIsOpen(false);
1832
+ };
1833
+ const typeaheadFind = (char) => {
1834
+ const now = Date.now();
1835
+ const stale = now - typeahead.current.time > TYPEAHEAD_BUFFER_MS;
1836
+ typeahead.current = {
1837
+ buffer: (stale ? "" : typeahead.current.buffer) + char.toLowerCase(),
1838
+ time: now
1839
+ };
1840
+ return options.findIndex(
1841
+ (option) => !option.disabled && optionText(option).toLowerCase().startsWith(typeahead.current.buffer)
1842
+ );
1843
+ };
1844
+ const handleKeyDown = (event) => {
1845
+ if (disabled) return;
1846
+ const { key } = event;
1847
+ const printable = key.length === 1 && key !== " " && !event.altKey && !event.ctrlKey && !event.metaKey;
1848
+ if (!isOpen) {
1849
+ if (["ArrowDown", "ArrowUp", "Enter", " "].includes(key)) {
1850
+ event.preventDefault();
1851
+ openMenu();
1852
+ } else if (printable) {
1853
+ const index = typeaheadFind(key);
1854
+ if (index >= 0) {
1855
+ setValue(options[index].value);
1856
+ onChange?.(options[index].value);
1857
+ }
1858
+ }
1859
+ return;
1860
+ }
1861
+ switch (key) {
1862
+ case "ArrowDown":
1863
+ event.preventDefault();
1864
+ setHighlighted((current) => move(current, 1));
1865
+ break;
1866
+ case "ArrowUp":
1867
+ event.preventDefault();
1868
+ setHighlighted((current) => move(current, -1));
1869
+ break;
1870
+ case "Home":
1871
+ event.preventDefault();
1872
+ setHighlighted(move(-1, 1));
1873
+ break;
1874
+ case "End":
1875
+ event.preventDefault();
1876
+ setHighlighted(move(options.length, -1));
1877
+ break;
1878
+ case "Enter":
1879
+ case " ": {
1880
+ event.preventDefault();
1881
+ const option = options[highlighted];
1882
+ if (option && !option.disabled) commit(option.value);
1883
+ break;
1884
+ }
1885
+ case "Escape":
1886
+ event.preventDefault();
1887
+ setIsOpen(false);
1888
+ break;
1889
+ case "Tab":
1890
+ setIsOpen(false);
1891
+ break;
1892
+ default:
1893
+ if (printable) {
1894
+ const index = typeaheadFind(key);
1895
+ if (index >= 0) setHighlighted(index);
1896
+ }
1897
+ }
1898
+ };
1899
+ return {
1900
+ commit,
1901
+ exiting,
1902
+ focused: focused && !disabled,
1903
+ handleKeyDown,
1904
+ highlighted,
1905
+ isOpen,
1906
+ mounted,
1907
+ openMenu,
1908
+ selectedIndex,
1909
+ setFocused,
1910
+ setHighlighted,
1911
+ setIsOpen,
1912
+ value,
1913
+ wrapper
1914
+ };
1915
+ }
1916
+ function SelectMenu({
1917
+ baseId,
1918
+ exiting,
1919
+ highlighted,
1920
+ onHighlight,
1921
+ onSelect,
1922
+ options,
1923
+ value
1924
+ }) {
1925
+ react.useEffect(() => {
1926
+ if (highlighted < 0) return;
1927
+ document.getElementById(`${baseId}-opt-${highlighted}`)?.scrollIntoView({ block: "nearest" });
1928
+ }, [baseId, highlighted]);
1929
+ return /* @__PURE__ */ jsxRuntime.jsx(
1930
+ "ul",
1931
+ {
1932
+ className: cn(
1933
+ "absolute top-full left-0 z-[var(--md-sys-z-dropdown)] mt-1 flex max-h-[280px] w-full min-w-max flex-col overflow-hidden overflow-y-auto rounded-large bg-surface-container-low py-0.5 shadow-mm-3 [--menu-clip-bleed:-24px]",
1934
+ exiting ? "animate-menu-out" : "animate-menu-in"
1935
+ ),
1936
+ id: `${baseId}-listbox`,
1937
+ role: "listbox",
1938
+ children: options.map((option, index) => {
1939
+ const isSelected = option.value === value;
1940
+ return /* @__PURE__ */ jsxRuntime.jsx(
1941
+ "li",
1942
+ {
1943
+ "aria-disabled": option.disabled || void 0,
1944
+ "aria-selected": isSelected,
1945
+ className: cn(
1946
+ "mx-1 my-0.5 flex h-11 shrink-0 cursor-pointer items-center rounded-extra-small px-3 text-label-large first:rounded-t-medium last:rounded-b-medium",
1947
+ isSelected ? "rounded-medium bg-tertiary-container text-on-tertiary-container" : "text-on-surface",
1948
+ highlighted === index && !isSelected && "bg-on-surface/8",
1949
+ option.disabled && "cursor-not-allowed text-on-surface/38"
1950
+ ),
1951
+ id: `${baseId}-opt-${index}`,
1952
+ onClick: () => {
1953
+ if (!option.disabled) onSelect(option.value);
1954
+ },
1955
+ onMouseDown: (event) => event.preventDefault(),
1956
+ onMouseEnter: () => {
1957
+ if (!option.disabled) onHighlight(index);
1958
+ },
1959
+ role: "option",
1960
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: option.label ?? option.value })
1961
+ },
1962
+ option.value
1963
+ );
1964
+ })
1965
+ }
1966
+ );
1967
+ }
1968
+ function SelectCaret({ open = false }) {
1969
+ const fade = "ease-md-linear transition-opacity delay-75 duration-75";
1970
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1971
+ "svg",
1972
+ {
1973
+ "aria-hidden": true,
1974
+ fill: "currentColor",
1975
+ height: 24,
1976
+ viewBox: "0 0 24 24",
1977
+ width: 24,
1978
+ children: [
1979
+ /* @__PURE__ */ jsxRuntime.jsx(
1980
+ "path",
1981
+ {
1982
+ className: cn(fade, open ? "opacity-0" : "opacity-100"),
1983
+ d: "M7 10l5 5 5-5z"
1984
+ }
1985
+ ),
1986
+ /* @__PURE__ */ jsxRuntime.jsx(
1987
+ "path",
1988
+ {
1989
+ className: cn(fade, open ? "opacity-100" : "opacity-0"),
1990
+ d: "M7 15l5-5 5 5z"
1991
+ }
1992
+ )
1993
+ ]
1994
+ }
1995
+ );
1996
+ }
1997
+ var COMBOBOX_LABELS = {
1998
+ clear: "Clear",
1999
+ empty: "No results",
2000
+ loading: "Loading\u2026"
2001
+ };
2002
+ function useCombobox({
2003
+ defaultValue,
2004
+ disabled,
2005
+ onChange,
2006
+ onInputChange,
2007
+ options,
2008
+ value: valueProp
2009
+ }) {
2010
+ const [value, setValue] = useControlled(valueProp, defaultValue ?? "");
2011
+ const labelFor = (next) => options.find((option) => option.value === next)?.label ?? next;
2012
+ const [query, setQuery] = react.useState(() => value ? labelFor(value) : "");
2013
+ const [isOpen, setIsOpen] = react.useState(false);
2014
+ const [focused, setFocused] = react.useState(false);
2015
+ const [highlighted, setHighlighted] = react.useState(-1);
2016
+ const wrapper = react.useRef(null);
2017
+ const input = react.useRef(null);
2018
+ const { exiting, mounted } = useDismissable(isOpen, 150);
2019
+ react.useEffect(() => {
2020
+ if (!isOpen) setQuery(value ? labelFor(value) : "");
2021
+ }, [isOpen, value]);
2022
+ const move = (start, delta) => {
2023
+ if (options.length === 0) return -1;
2024
+ for (let step = 1; step <= options.length; step += 1) {
2025
+ const index = (start + delta * step + options.length * step) % options.length;
2026
+ if (!options[index]?.disabled) return index;
2027
+ }
2028
+ return -1;
2029
+ };
2030
+ const open = () => {
2031
+ if (disabled) return;
2032
+ const selectedIndex = options.findIndex((option) => option.value === value);
2033
+ setHighlighted(
2034
+ selectedIndex >= 0 && !options[selectedIndex]?.disabled ? selectedIndex : move(-1, 1)
2035
+ );
2036
+ setIsOpen(true);
2037
+ };
2038
+ const closeAndRevert = () => {
2039
+ setIsOpen(false);
2040
+ setHighlighted(-1);
2041
+ setQuery(value ? labelFor(value) : "");
2042
+ };
2043
+ useOutsideClose(wrapper, closeAndRevert, isOpen);
2044
+ const onType = (next) => {
2045
+ setQuery(next);
2046
+ setIsOpen(true);
2047
+ setHighlighted(-1);
2048
+ onInputChange?.(next);
2049
+ };
2050
+ const commit = (next) => {
2051
+ setValue(next);
2052
+ onChange?.(next);
2053
+ setQuery(labelFor(next));
2054
+ setIsOpen(false);
2055
+ setHighlighted(-1);
2056
+ };
2057
+ const clear = () => {
2058
+ setValue("");
2059
+ onChange?.("");
2060
+ setQuery("");
2061
+ setHighlighted(-1);
2062
+ onInputChange?.("");
2063
+ input.current?.focus();
2064
+ };
2065
+ const handleKeyDown = (event) => {
2066
+ if (disabled) return;
2067
+ switch (event.key) {
2068
+ case "ArrowDown":
2069
+ event.preventDefault();
2070
+ if (isOpen) setHighlighted((current) => move(current, 1));
2071
+ else open();
2072
+ break;
2073
+ case "ArrowUp":
2074
+ event.preventDefault();
2075
+ if (isOpen) setHighlighted((current) => move(current, -1));
2076
+ else open();
2077
+ break;
2078
+ case "Home":
2079
+ if (isOpen) {
2080
+ event.preventDefault();
2081
+ setHighlighted(move(-1, 1));
2082
+ }
2083
+ break;
2084
+ case "End":
2085
+ if (isOpen) {
2086
+ event.preventDefault();
2087
+ setHighlighted(move(options.length, -1));
2088
+ }
2089
+ break;
2090
+ case "Enter":
2091
+ if (isOpen && highlighted >= 0) {
2092
+ event.preventDefault();
2093
+ const option = options[highlighted];
2094
+ if (option && !option.disabled) commit(option.value);
2095
+ }
2096
+ break;
2097
+ case "Escape":
2098
+ if (isOpen) {
2099
+ event.preventDefault();
2100
+ closeAndRevert();
2101
+ }
2102
+ break;
2103
+ case "Tab":
2104
+ if (isOpen) closeAndRevert();
2105
+ break;
2106
+ }
2107
+ };
2108
+ return {
2109
+ clear,
2110
+ commit,
2111
+ exiting,
2112
+ focused: focused && !disabled,
2113
+ handleKeyDown,
2114
+ highlighted,
2115
+ input,
2116
+ isOpen,
2117
+ mounted,
2118
+ onType,
2119
+ query,
2120
+ setFocused,
2121
+ setHighlighted,
2122
+ setIsOpen,
2123
+ value,
2124
+ wrapper
2125
+ };
2126
+ }
2127
+ function CloseGlyph() {
2128
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { "aria-hidden": true, fill: "currentColor", height: 18, viewBox: "0 0 24 24", width: 18, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" }) });
2129
+ }
2130
+ function ComboboxTrailing({
2131
+ clearLabel,
2132
+ disabled,
2133
+ error,
2134
+ isOpen,
2135
+ onClear,
2136
+ showClear
2137
+ }) {
2138
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pointer-events-none absolute top-1/2 right-2 flex -translate-y-1/2 items-center gap-1", children: [
2139
+ showClear ? /* @__PURE__ */ jsxRuntime.jsx(
2140
+ "button",
2141
+ {
2142
+ "aria-label": clearLabel,
2143
+ className: "state-layer pointer-events-auto flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-on-surface-variant",
2144
+ onMouseDown: (event) => event.preventDefault(),
2145
+ onClick: onClear,
2146
+ type: "button",
2147
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseGlyph, {})
2148
+ }
2149
+ ) : null,
2150
+ /* @__PURE__ */ jsxRuntime.jsx(
2151
+ "span",
2152
+ {
2153
+ className: cn(
2154
+ "flex h-6 w-6 items-center justify-center text-on-surface-variant",
2155
+ error && "text-error",
2156
+ disabled && "text-on-surface/38"
2157
+ ),
2158
+ children: /* @__PURE__ */ jsxRuntime.jsx(SelectCaret, { open: isOpen })
2159
+ }
2160
+ )
2161
+ ] });
2162
+ }
2163
+ function ComboboxListbox({
2164
+ anchorRef,
2165
+ baseId,
2166
+ emptyText,
2167
+ exiting,
2168
+ highlighted,
2169
+ listboxClassName,
2170
+ loading,
2171
+ loadingText,
2172
+ onHighlight,
2173
+ onSelect,
2174
+ options,
2175
+ value
2176
+ }) {
2177
+ const floating = react.useRef(null);
2178
+ const pos = usePopoverPosition(anchorRef, floating, true, {
2179
+ gap: 4,
2180
+ matchWidth: true,
2181
+ placement: "bottom-start"
2182
+ });
2183
+ react.useEffect(() => {
2184
+ if (highlighted < 0) return;
2185
+ document.getElementById(`${baseId}-opt-${highlighted}`)?.scrollIntoView?.({ block: "nearest" });
2186
+ }, [baseId, highlighted]);
2187
+ if (typeof document === "undefined") return null;
2188
+ const anim = exiting ? "animate-menu-out" : "animate-menu-in";
2189
+ const surface = "max-h-[280px] w-full overflow-hidden overflow-y-auto rounded-large bg-surface-container-low py-0.5 shadow-mm-3 [--menu-clip-bleed:-24px]";
2190
+ return reactDom.createPortal(
2191
+ /* @__PURE__ */ jsxRuntime.jsx(
2192
+ "div",
2193
+ {
2194
+ className: "fixed z-[var(--md-sys-z-menu)]",
2195
+ ref: floating,
2196
+ style: { left: pos.left, top: pos.top, width: pos.width },
2197
+ children: loading || options.length === 0 ? (
2198
+ // An empty listbox is an a11y violation — show a status row instead.
2199
+ /* @__PURE__ */ jsxRuntime.jsx(
2200
+ "div",
2201
+ {
2202
+ "aria-live": "polite",
2203
+ className: cn(
2204
+ surface,
2205
+ anim,
2206
+ "px-4 py-3 text-body-medium text-on-surface-variant",
2207
+ listboxClassName
2208
+ ),
2209
+ role: "status",
2210
+ children: loading ? loadingText : emptyText
2211
+ }
2212
+ )
2213
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
2214
+ "ul",
2215
+ {
2216
+ className: cn("flex flex-col", surface, anim, listboxClassName),
2217
+ id: `${baseId}-listbox`,
2218
+ role: "listbox",
2219
+ children: options.map((option, index) => {
2220
+ const isSelected = option.value === value;
2221
+ return /* @__PURE__ */ jsxRuntime.jsx(
2222
+ "li",
2223
+ {
2224
+ "aria-disabled": option.disabled || void 0,
2225
+ "aria-selected": isSelected,
2226
+ className: cn(
2227
+ "mx-1 my-0.5 flex h-11 shrink-0 cursor-pointer items-center rounded-extra-small px-3 text-label-large first:rounded-t-medium last:rounded-b-medium",
2228
+ isSelected ? "rounded-medium bg-tertiary-container text-on-tertiary-container" : "text-on-surface",
2229
+ highlighted === index && !isSelected && "bg-on-surface/8",
2230
+ option.disabled && "cursor-not-allowed text-on-surface/38"
2231
+ ),
2232
+ id: `${baseId}-opt-${index}`,
2233
+ onClick: () => {
2234
+ if (!option.disabled) onSelect(option.value);
2235
+ },
2236
+ onMouseDown: (event) => event.preventDefault(),
2237
+ onMouseEnter: () => {
2238
+ if (!option.disabled) onHighlight(index);
2239
+ },
2240
+ role: "option",
2241
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: option.label ?? option.value })
2242
+ },
2243
+ option.value
2244
+ );
2245
+ })
2246
+ }
2247
+ )
2248
+ }
2249
+ ),
2250
+ document.body
2251
+ );
2252
+ }
2253
+ function ComboboxFilled({
2254
+ className,
2255
+ clearable = true,
2256
+ defaultValue,
2257
+ disabled,
2258
+ error,
2259
+ errorText,
2260
+ id,
2261
+ inputClassName,
2262
+ label,
2263
+ labels,
2264
+ leftElement,
2265
+ listboxClassName,
2266
+ loading,
2267
+ name,
2268
+ onChange,
2269
+ onInputChange,
2270
+ options,
2271
+ supportingText,
2272
+ value: valueProp,
2273
+ ...inputProps
2274
+ }) {
2275
+ const l = { ...COMBOBOX_LABELS, ...labels };
2276
+ const autoId = react.useId();
2277
+ const inputId = id ?? `${autoId}-input`;
2278
+ const {
2279
+ clear,
2280
+ commit,
2281
+ exiting,
2282
+ focused,
2283
+ handleKeyDown,
2284
+ highlighted,
2285
+ input,
2286
+ isOpen,
2287
+ mounted,
2288
+ onType,
2289
+ query,
2290
+ setFocused,
2291
+ setHighlighted,
2292
+ setIsOpen,
2293
+ value,
2294
+ wrapper
2295
+ } = useCombobox({
2296
+ defaultValue,
2297
+ disabled,
2298
+ onChange,
2299
+ onInputChange,
2300
+ options,
2301
+ value: valueProp
2302
+ });
2303
+ const active = focused || isOpen;
2304
+ const floating = active || query.length > 0;
2305
+ const showClear = clearable && query.length > 0 && !disabled;
2306
+ const hasOptions = !loading && options.length > 0;
2307
+ const describedById = error && errorText || supportingText ? `${autoId}-support` : void 0;
2308
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("w-full", className), children: [
2309
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: wrapper, children: [
2310
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group relative flex h-14 w-full overflow-hidden rounded-t-small", children: [
2311
+ leftElement ? /* @__PURE__ */ jsxRuntime.jsx(FieldIcon, { className: "top-1/2 left-3 z-10 -translate-y-1/2", children: leftElement }) : null,
2312
+ /* @__PURE__ */ jsxRuntime.jsx(
2313
+ "input",
2314
+ {
2315
+ ...inputProps,
2316
+ "aria-activedescendant": isOpen && hasOptions && highlighted >= 0 ? `${autoId}-opt-${highlighted}` : void 0,
2317
+ "aria-autocomplete": onInputChange ? "list" : "none",
2318
+ "aria-controls": mounted && hasOptions ? `${autoId}-listbox` : void 0,
2319
+ "aria-describedby": describedById,
2320
+ "aria-expanded": isOpen,
2321
+ "aria-invalid": error || void 0,
2322
+ autoComplete: "off",
2323
+ className: cn(
2324
+ "h-14 w-full bg-surface-container-highest text-body-large text-on-surface outline-none disabled:cursor-not-allowed disabled:bg-on-surface/4 disabled:text-on-surface/38",
2325
+ label ? "pt-5 pb-1" : "",
2326
+ leftElement ? "pl-12" : "pl-4",
2327
+ showClear ? "pr-20" : "pr-12",
2328
+ inputClassName
2329
+ ),
2330
+ disabled,
2331
+ id: inputId,
2332
+ onBlur: () => setFocused(false),
2333
+ onChange: (event) => onType(event.target.value),
2334
+ onClick: () => {
2335
+ if (!disabled) setIsOpen(true);
2336
+ },
2337
+ onFocus: () => setFocused(true),
2338
+ onKeyDown: handleKeyDown,
2339
+ ref: input,
2340
+ role: "combobox",
2341
+ type: "text",
2342
+ value: query
2343
+ }
2344
+ ),
2345
+ !disabled ? /* @__PURE__ */ jsxRuntime.jsx(
2346
+ "span",
2347
+ {
2348
+ "aria-hidden": true,
2349
+ className: "ease-md-linear pointer-events-none absolute inset-0 bg-on-surface opacity-0 transition-opacity duration-[15ms] group-hover:opacity-8"
2350
+ }
2351
+ ) : null,
2352
+ /* @__PURE__ */ jsxRuntime.jsx(
2353
+ "span",
2354
+ {
2355
+ "aria-hidden": true,
2356
+ className: cn(
2357
+ "pointer-events-none absolute inset-x-0 bottom-0 transition-all",
2358
+ error ? "bg-error" : active ? "bg-primary" : "bg-on-surface-variant",
2359
+ active || error ? "h-0.5" : "h-px",
2360
+ disabled && "bg-on-surface/38"
2361
+ )
2362
+ }
2363
+ ),
2364
+ label ? /* @__PURE__ */ jsxRuntime.jsx(
2365
+ FloatingLabel,
2366
+ {
2367
+ className: labelColor({ disabled, error, focused: active }),
2368
+ floating,
2369
+ floatingClassName: cn(
2370
+ "top-2 text-body-small",
2371
+ leftElement ? "left-12" : "left-4"
2372
+ ),
2373
+ htmlFor: inputId,
2374
+ restingClassName: cn(
2375
+ "top-1/2 -translate-y-1/2 text-body-large",
2376
+ leftElement ? "left-12" : "left-4"
2377
+ ),
2378
+ children: label
2379
+ }
2380
+ ) : null,
2381
+ /* @__PURE__ */ jsxRuntime.jsx(
2382
+ ComboboxTrailing,
2383
+ {
2384
+ clearLabel: l.clear,
2385
+ disabled,
2386
+ error,
2387
+ isOpen,
2388
+ onClear: clear,
2389
+ showClear
2390
+ }
2391
+ )
2392
+ ] }),
2393
+ mounted ? /* @__PURE__ */ jsxRuntime.jsx(
2394
+ ComboboxListbox,
2395
+ {
2396
+ anchorRef: wrapper,
2397
+ baseId: autoId,
2398
+ emptyText: l.empty,
2399
+ exiting,
2400
+ highlighted,
2401
+ listboxClassName,
2402
+ loading,
2403
+ loadingText: l.loading,
2404
+ onHighlight: setHighlighted,
2405
+ onSelect: commit,
2406
+ options,
2407
+ value
2408
+ }
2409
+ ) : null
2410
+ ] }),
2411
+ name ? /* @__PURE__ */ jsxRuntime.jsx("input", { name, type: "hidden", value }) : null,
2412
+ /* @__PURE__ */ jsxRuntime.jsx(
2413
+ SupportingText,
2414
+ {
2415
+ error,
2416
+ errorText,
2417
+ id: describedById,
2418
+ supportingText
2419
+ }
2420
+ )
2421
+ ] });
2422
+ }
2423
+ function ComboboxOutlined({
2424
+ className,
2425
+ clearable = true,
2426
+ defaultValue,
2427
+ disabled,
2428
+ error,
2429
+ errorText,
2430
+ id,
2431
+ inputClassName,
2432
+ label,
2433
+ labels,
2434
+ leftElement,
2435
+ listboxClassName,
2436
+ loading,
2437
+ name,
2438
+ onChange,
2439
+ onInputChange,
2440
+ options,
2441
+ supportingText,
2442
+ value: valueProp,
2443
+ ...inputProps
2444
+ }) {
2445
+ const l = { ...COMBOBOX_LABELS, ...labels };
2446
+ const autoId = react.useId();
2447
+ const inputId = id ?? `${autoId}-input`;
2448
+ const {
2449
+ clear,
2450
+ commit,
2451
+ exiting,
2452
+ focused,
2453
+ handleKeyDown,
2454
+ highlighted,
2455
+ input,
2456
+ isOpen,
2457
+ mounted,
2458
+ onType,
2459
+ query,
2460
+ setFocused,
2461
+ setHighlighted,
2462
+ setIsOpen,
2463
+ value,
2464
+ wrapper
2465
+ } = useCombobox({
2466
+ defaultValue,
2467
+ disabled,
2468
+ onChange,
2469
+ onInputChange,
2470
+ options,
2471
+ value: valueProp
2472
+ });
2473
+ const active = focused || isOpen;
2474
+ const floating = active || query.length > 0;
2475
+ const showClear = clearable && query.length > 0 && !disabled;
2476
+ const hasOptions = !loading && options.length > 0;
2477
+ const describedById = error && errorText || supportingText ? `${autoId}-support` : void 0;
2478
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("w-full", className), children: [
2479
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: wrapper, children: [
2480
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group relative flex h-14 w-full", children: [
2481
+ leftElement ? /* @__PURE__ */ jsxRuntime.jsx(FieldIcon, { className: "top-1/2 left-3 -translate-y-1/2", children: leftElement }) : null,
2482
+ /* @__PURE__ */ jsxRuntime.jsx(
2483
+ "input",
2484
+ {
2485
+ ...inputProps,
2486
+ "aria-activedescendant": isOpen && hasOptions && highlighted >= 0 ? `${autoId}-opt-${highlighted}` : void 0,
2487
+ "aria-autocomplete": onInputChange ? "list" : "none",
2488
+ "aria-controls": mounted && hasOptions ? `${autoId}-listbox` : void 0,
2489
+ "aria-describedby": describedById,
2490
+ "aria-expanded": isOpen,
2491
+ "aria-invalid": error || void 0,
2492
+ autoComplete: "off",
2493
+ className: cn(
2494
+ "h-14 w-full bg-transparent text-body-large text-on-surface outline-none disabled:cursor-not-allowed disabled:text-on-surface/38",
2495
+ leftElement ? "pl-12" : "pl-4",
2496
+ showClear ? "pr-20" : "pr-12",
2497
+ inputClassName
2498
+ ),
2499
+ disabled,
2500
+ id: inputId,
2501
+ onBlur: () => setFocused(false),
2502
+ onChange: (event) => onType(event.target.value),
2503
+ onClick: () => {
2504
+ if (!disabled) setIsOpen(true);
2505
+ },
2506
+ onFocus: () => setFocused(true),
2507
+ onKeyDown: handleKeyDown,
2508
+ ref: input,
2509
+ role: "combobox",
2510
+ type: "text",
2511
+ value: query
2512
+ }
2513
+ ),
2514
+ /* @__PURE__ */ jsxRuntime.jsx(
2515
+ "fieldset",
2516
+ {
2517
+ "aria-hidden": true,
2518
+ className: cn(
2519
+ "pointer-events-none absolute inset-0 m-0 min-w-0 rounded-small px-2 transition-colors",
2520
+ error ? active ? "border-2 border-error" : "border border-error" : active ? "border-2 border-primary" : "border border-outline group-hover:border-on-surface",
2521
+ disabled && "border-on-surface/12 group-hover:border-on-surface/12"
2522
+ ),
2523
+ children: label ? /* @__PURE__ */ jsxRuntime.jsx(
2524
+ "legend",
1555
2525
  {
1556
2526
  className: cn(
1557
- "absolute inset-0 flex items-center justify-center",
1558
- selected ? "ease-md-linear opacity-0 transition-opacity duration-[50ms]" : "ease-legacy opacity-100 transition-opacity delay-[50ms] duration-150",
1559
- accentIcon && !disabled && "text-primary"
2527
+ "invisible h-0 px-0 text-body-small whitespace-nowrap transition-all",
2528
+ floating ? "max-w-full px-1" : "max-w-0"
1560
2529
  ),
1561
- children: leftElement
2530
+ children: label
1562
2531
  }
1563
- )
1564
- ] })
1565
- ) : /* @__PURE__ */ jsxRuntime.jsx(
1566
- Icon,
2532
+ ) : null
2533
+ }
2534
+ ),
2535
+ label ? /* @__PURE__ */ jsxRuntime.jsx(
2536
+ FloatingLabel,
1567
2537
  {
1568
- className: cn(
1569
- accentIcon && !disabled && !hasAvatar && "text-primary"
2538
+ className: labelColor({ disabled, error, focused: active }),
2539
+ floating,
2540
+ floatingClassName: "top-0 left-3 -translate-y-1/2 px-1 text-body-small",
2541
+ htmlFor: inputId,
2542
+ restingClassName: cn(
2543
+ "top-1/2 -translate-y-1/2 text-body-large",
2544
+ leftElement ? "left-12" : "left-4"
1570
2545
  ),
1571
- iconLeft: leftElement,
1572
- size: hasAvatar ? 24 : 18
2546
+ children: label
1573
2547
  }
1574
2548
  ) : null,
1575
- children ?? text,
1576
- rightElement ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { iconRight: rightElement, size: 18 }) : null
1577
- ]
1578
- }
1579
- );
1580
- return removeButton ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative inline-flex w-fit align-middle", children: [
1581
- chip,
1582
- removeButton
1583
- ] }) : chip;
2549
+ /* @__PURE__ */ jsxRuntime.jsx(
2550
+ ComboboxTrailing,
2551
+ {
2552
+ clearLabel: l.clear,
2553
+ disabled,
2554
+ error,
2555
+ isOpen,
2556
+ onClear: clear,
2557
+ showClear
2558
+ }
2559
+ )
2560
+ ] }),
2561
+ mounted ? /* @__PURE__ */ jsxRuntime.jsx(
2562
+ ComboboxListbox,
2563
+ {
2564
+ anchorRef: wrapper,
2565
+ baseId: autoId,
2566
+ emptyText: l.empty,
2567
+ exiting,
2568
+ highlighted,
2569
+ listboxClassName,
2570
+ loading,
2571
+ loadingText: l.loading,
2572
+ onHighlight: setHighlighted,
2573
+ onSelect: commit,
2574
+ options,
2575
+ value
2576
+ }
2577
+ ) : null
2578
+ ] }),
2579
+ name ? /* @__PURE__ */ jsxRuntime.jsx("input", { name, type: "hidden", value }) : null,
2580
+ /* @__PURE__ */ jsxRuntime.jsx(
2581
+ SupportingText,
2582
+ {
2583
+ error,
2584
+ errorText,
2585
+ id: describedById,
2586
+ supportingText
2587
+ }
2588
+ )
2589
+ ] });
1584
2590
  }
1585
2591
  function TableBody({ children, className, ...props }) {
1586
2592
  return /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: cn("text-body-medium", className), ...props, children });
@@ -1756,7 +2762,7 @@ function Dialog({
1756
2762
  "div",
1757
2763
  {
1758
2764
  className: cn(
1759
- "fixed inset-0 z-50 flex items-center justify-center bg-scrim/32 p-4",
2765
+ "fixed inset-0 z-[var(--md-sys-z-modal)] flex items-center justify-center bg-scrim/32 p-4",
1760
2766
  exiting ? "animate-scrim-out" : "animate-scrim-in"
1761
2767
  ),
1762
2768
  onClick: handleScrim,
@@ -1785,141 +2791,6 @@ function Dialog({
1785
2791
  Dialog.Header = DialogHeader;
1786
2792
  Dialog.Body = DialogBody;
1787
2793
  Dialog.Footer = DialogFooter;
1788
- function FloatingLabel({
1789
- children,
1790
- className,
1791
- floating,
1792
- floatingClassName,
1793
- htmlFor,
1794
- restingClassName
1795
- }) {
1796
- const floatingRef = react.useRef(null);
1797
- const restingRef = react.useRef(null);
1798
- const animationRef = react.useRef(null);
1799
- const prevFloating = react.useRef(floating);
1800
- const [animating, setAnimating] = react.useState(false);
1801
- useIsomorphicLayoutEffect(() => {
1802
- if (prevFloating.current === floating) return;
1803
- prevFloating.current = floating;
1804
- const floatingEl = floatingRef.current;
1805
- const restingEl = restingRef.current;
1806
- if (!floatingEl || !restingEl) return;
1807
- const f = floatingEl.getBoundingClientRect();
1808
- const r = restingEl.getBoundingClientRect();
1809
- if (!f.width || !r.width) return;
1810
- const scale = r.width / f.width;
1811
- const xDelta = r.x - f.x;
1812
- const yDelta = r.y - f.y + Math.round((r.height - f.height * scale) / 2);
1813
- const rest = `translateX(${xDelta}px) translateY(${yDelta}px) scale(${scale})`;
1814
- const float = `translateX(0) translateY(0) scale(1)`;
1815
- animationRef.current?.cancel();
1816
- setAnimating(true);
1817
- const animation = floatingEl.animate(
1818
- { transform: floating ? [rest, float] : [float, rest] },
1819
- {
1820
- duration: 150,
1821
- easing: "cubic-bezier(0.2, 0, 0, 1)",
1822
- fill: "forwards"
1823
- }
1824
- );
1825
- animationRef.current = animation;
1826
- const done = () => {
1827
- if (animationRef.current === animation) setAnimating(false);
1828
- };
1829
- animation.addEventListener("finish", done);
1830
- animation.addEventListener("cancel", done);
1831
- }, [floating]);
1832
- useIsomorphicLayoutEffect(() => {
1833
- if (animating || !animationRef.current) return;
1834
- animationRef.current.cancel();
1835
- animationRef.current = null;
1836
- }, [animating]);
1837
- const showFloating = floating || animating;
1838
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1839
- /* @__PURE__ */ jsxRuntime.jsx(
1840
- "label",
1841
- {
1842
- "aria-hidden": true,
1843
- className: cn(
1844
- "pointer-events-none absolute",
1845
- floatingClassName,
1846
- className,
1847
- !showFloating && "opacity-0"
1848
- ),
1849
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block origin-top-left", ref: floatingRef, children })
1850
- }
1851
- ),
1852
- /* @__PURE__ */ jsxRuntime.jsx(
1853
- "label",
1854
- {
1855
- className: cn(
1856
- "pointer-events-none absolute",
1857
- restingClassName,
1858
- className,
1859
- showFloating && "opacity-0"
1860
- ),
1861
- htmlFor,
1862
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block", ref: restingRef, children })
1863
- }
1864
- )
1865
- ] });
1866
- }
1867
- function useFieldState(value, defaultValue, disabled) {
1868
- const [focused, setFocusedState] = react.useState(false);
1869
- const [internalHasValue, setInternalHasValue] = react.useState(
1870
- defaultValue !== void 0 && defaultValue !== null && String(defaultValue).length > 0
1871
- );
1872
- const hasValue = value !== void 0 && value !== null ? String(value).length > 0 : internalHasValue;
1873
- return {
1874
- focused: focused && !disabled,
1875
- hasValue,
1876
- setFocused: setFocusedState,
1877
- setInternalHasValue
1878
- };
1879
- }
1880
- function labelColor(opts) {
1881
- if (opts.disabled) return "text-on-surface/38";
1882
- if (opts.error) return "text-error";
1883
- if (opts.focused) return "text-primary";
1884
- return "text-on-surface-variant";
1885
- }
1886
- function SupportingText({
1887
- error,
1888
- errorText,
1889
- id,
1890
- supportingText
1891
- }) {
1892
- const isError = !!(error && errorText);
1893
- const text = isError ? errorText : supportingText;
1894
- if (!text) return null;
1895
- return /* @__PURE__ */ jsxRuntime.jsx(
1896
- "p",
1897
- {
1898
- role: isError ? "alert" : void 0,
1899
- id,
1900
- className: cn(
1901
- "px-4 pt-1 text-body-small",
1902
- error ? "text-error" : "text-on-surface-variant"
1903
- ),
1904
- children: text
1905
- }
1906
- );
1907
- }
1908
- function FieldIcon({
1909
- children,
1910
- className
1911
- }) {
1912
- return /* @__PURE__ */ jsxRuntime.jsx(
1913
- "span",
1914
- {
1915
- className: cn(
1916
- "pointer-events-auto absolute flex h-6 w-6 items-center justify-center leading-none text-on-surface-variant",
1917
- className
1918
- ),
1919
- children
1920
- }
1921
- );
1922
- }
1923
2794
  function InputOutlined({
1924
2795
  "aria-describedby": ariaDescribedBy,
1925
2796
  "aria-invalid": ariaInvalid,
@@ -2384,11 +3255,14 @@ var DOCKED_LABELS = {
2384
3255
  };
2385
3256
  function DatePickerDocked({
2386
3257
  className,
3258
+ error,
3259
+ errorText,
2387
3260
  labels,
2388
3261
  locale,
2389
3262
  max,
2390
3263
  min,
2391
3264
  onChange,
3265
+ supportingText,
2392
3266
  value = null,
2393
3267
  weekStartsOn
2394
3268
  }) {
@@ -2401,6 +3275,8 @@ function DatePickerDocked({
2401
3275
  /* @__PURE__ */ jsxRuntime.jsx(
2402
3276
  InputOutlined,
2403
3277
  {
3278
+ error,
3279
+ errorText,
2404
3280
  inputClassName: "cursor-pointer",
2405
3281
  label: l.field,
2406
3282
  onFocus: () => setOpen(true),
@@ -2418,6 +3294,7 @@ function DatePickerDocked({
2418
3294
  children: calendarIcon
2419
3295
  }
2420
3296
  ),
3297
+ supportingText,
2421
3298
  value: formatShort(value, locale)
2422
3299
  }
2423
3300
  ),
@@ -2425,7 +3302,7 @@ function DatePickerDocked({
2425
3302
  "div",
2426
3303
  {
2427
3304
  "aria-label": l.field,
2428
- className: "animate-menu-in absolute top-full left-0 z-40 mt-1 w-90 max-w-[calc(100vw-2rem)] overflow-hidden rounded-large bg-surface-container-high py-2 shadow-mm-3 [--menu-clip-bleed:-24px]",
3305
+ className: "animate-menu-in absolute top-full left-0 z-[var(--md-sys-z-dropdown)] mt-1 w-90 max-w-[calc(100vw-2rem)] overflow-hidden rounded-large bg-surface-container-high py-2 shadow-mm-3 [--menu-clip-bleed:-24px]",
2429
3306
  id: popupId,
2430
3307
  role: "dialog",
2431
3308
  children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2574,9 +3451,14 @@ function Dropdown({
2574
3451
  const [open, setOpen] = react.useState(false);
2575
3452
  const wrapper = react.useRef(null);
2576
3453
  const trigger = react.useRef(null);
3454
+ const floating = react.useRef(null);
2577
3455
  const wasOpen = react.useRef(false);
2578
3456
  const menuId = react.useId();
2579
3457
  const { exiting, mounted } = useDismissable(open, 150);
3458
+ const pos = usePopoverPosition(wrapper, floating, mounted, {
3459
+ gap: apart ? 4 : 0,
3460
+ placement: "bottom-start"
3461
+ });
2580
3462
  useOutsideClose(wrapper, () => setOpen(false), open);
2581
3463
  const triggerNode = react.isValidElement(children) ? react.cloneElement(
2582
3464
  children,
@@ -2607,17 +3489,29 @@ function Dropdown({
2607
3489
  children: triggerNode
2608
3490
  }
2609
3491
  ),
2610
- mounted ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-full left-0 z-30 flex w-full flex-col", children: /* @__PURE__ */ jsxRuntime.jsx(
2611
- Menu,
2612
- {
2613
- className: cn(apart && "mt-1", menuClassName),
2614
- exiting,
2615
- id: menuId,
2616
- onClose: () => setOpen(false),
2617
- vibrant,
2618
- children: menu
2619
- }
2620
- ) }) : null
3492
+ mounted && typeof document !== "undefined" ? reactDom.createPortal(
3493
+ /* @__PURE__ */ jsxRuntime.jsx(
3494
+ "div",
3495
+ {
3496
+ className: "fixed z-[var(--md-sys-z-menu)]",
3497
+ ref: floating,
3498
+ style: { left: pos.left, top: pos.top },
3499
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3500
+ Menu,
3501
+ {
3502
+ className: menuClassName,
3503
+ exiting,
3504
+ id: menuId,
3505
+ onClose: () => setOpen(false),
3506
+ up: pos.flippedVertically,
3507
+ vibrant,
3508
+ children: menu
3509
+ }
3510
+ )
3511
+ }
3512
+ ),
3513
+ document.body
3514
+ ) : null
2621
3515
  ]
2622
3516
  }
2623
3517
  );
@@ -3000,7 +3894,7 @@ function NavigationRail({
3000
3894
  "div",
3001
3895
  {
3002
3896
  className: cn(
3003
- "fixed inset-0 z-40 bg-scrim/32",
3897
+ "fixed inset-0 z-[var(--md-sys-z-modal)] bg-scrim/32",
3004
3898
  scrim.exiting ? "animate-scrim-drawer-out" : "animate-scrim-drawer-in"
3005
3899
  ),
3006
3900
  onClick: onClose
@@ -3025,11 +3919,16 @@ function OverflowMenu({
3025
3919
  const [open, setOpen] = react.useState(false);
3026
3920
  const wrapper = react.useRef(null);
3027
3921
  const trigger = react.useRef(null);
3922
+ const floating = react.useRef(null);
3028
3923
  const wasOpen = react.useRef(false);
3029
3924
  const menuId = react.useId();
3030
- const noPosition = !topLeft && !topRight && !bottomLeft && !bottomRight;
3031
- const opensUp = Boolean(topLeft || topRight);
3925
+ const placement = topRight ? "top-end" : topLeft ? "top-start" : bottomLeft ? "bottom-start" : bottomRight ? "bottom-end" : "bottom-end";
3032
3926
  const { exiting, mounted } = useDismissable(open, 150);
3927
+ const pos = usePopoverPosition(wrapper, floating, mounted, {
3928
+ gap: 8,
3929
+ placement
3930
+ });
3931
+ const up = placement.startsWith("top") !== pos.flippedVertically;
3033
3932
  useOutsideClose(wrapper, () => setOpen(false), open);
3034
3933
  const triggerNode = react.isValidElement(children) ? react.cloneElement(
3035
3934
  children,
@@ -3060,29 +3959,28 @@ function OverflowMenu({
3060
3959
  children: triggerNode
3061
3960
  }
3062
3961
  ),
3063
- mounted ? /* @__PURE__ */ jsxRuntime.jsx(
3064
- "div",
3065
- {
3066
- className: cn(
3067
- "absolute z-20 flex flex-col",
3068
- topRight && "right-0 bottom-full",
3069
- topLeft && "bottom-full left-0",
3070
- (bottomRight || noPosition) && "top-full right-0",
3071
- bottomLeft && "top-full left-0"
3072
- ),
3073
- children: /* @__PURE__ */ jsxRuntime.jsx(
3074
- Menu,
3075
- {
3076
- className: cn(opensUp ? "mb-2" : "mt-2", menuClassName),
3077
- exiting,
3078
- id: menuId,
3079
- onClose: () => setOpen(false),
3080
- up: opensUp,
3081
- vibrant,
3082
- children: menu
3083
- }
3084
- )
3085
- }
3962
+ mounted && typeof document !== "undefined" ? reactDom.createPortal(
3963
+ /* @__PURE__ */ jsxRuntime.jsx(
3964
+ "div",
3965
+ {
3966
+ className: "fixed z-[var(--md-sys-z-menu)]",
3967
+ ref: floating,
3968
+ style: { left: pos.left, top: pos.top },
3969
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3970
+ Menu,
3971
+ {
3972
+ className: menuClassName,
3973
+ exiting,
3974
+ id: menuId,
3975
+ onClose: () => setOpen(false),
3976
+ up,
3977
+ vibrant,
3978
+ children: menu
3979
+ }
3980
+ )
3981
+ }
3982
+ ),
3983
+ document.body
3086
3984
  ) : null
3087
3985
  ]
3088
3986
  }
@@ -3611,17 +4509,54 @@ function SearchItem({
3611
4509
  );
3612
4510
  }
3613
4511
  var SEARCH_INPUT_LABELS = {
4512
+ clear: "Clear",
3614
4513
  placeholder: "Search"
3615
4514
  };
4515
+ function CloseIcon2() {
4516
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { "aria-hidden": true, className: "h-6 w-6 fill-current", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" }) });
4517
+ }
3616
4518
  function SearchInput({
3617
4519
  className,
4520
+ clearable,
4521
+ defaultValue,
3618
4522
  inputClassName,
3619
4523
  labels,
3620
4524
  leftElement,
4525
+ onChange,
4526
+ onClear,
3621
4527
  rightElement,
4528
+ value: valueProp,
3622
4529
  ...inputProps
3623
4530
  }) {
3624
4531
  const l = { ...SEARCH_INPUT_LABELS, ...labels };
4532
+ const input = react.useRef(null);
4533
+ const [value, setValue] = useControlled(
4534
+ valueProp,
4535
+ typeof defaultValue === "string" ? defaultValue : ""
4536
+ );
4537
+ const hasValue = value.length > 0;
4538
+ const showClear = Boolean(
4539
+ clearable && hasValue && !inputProps.disabled && !rightElement
4540
+ );
4541
+ const handleChange = (event) => {
4542
+ setValue(event.target.value);
4543
+ onChange?.(event);
4544
+ };
4545
+ const clear = () => {
4546
+ const el = input.current;
4547
+ if (el) {
4548
+ const setter = Object.getOwnPropertyDescriptor(
4549
+ HTMLInputElement.prototype,
4550
+ "value"
4551
+ )?.set;
4552
+ setter?.call(el, "");
4553
+ el.dispatchEvent(new Event("input", { bubbles: true }));
4554
+ el.focus();
4555
+ } else {
4556
+ setValue("");
4557
+ }
4558
+ onClear?.();
4559
+ };
3625
4560
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative flex h-14 w-full", className), children: [
3626
4561
  leftElement ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 flex h-14 w-14 items-center justify-center text-on-surface", children: leftElement }) : null,
3627
4562
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -3631,15 +4566,29 @@ function SearchInput({
3631
4566
  className: cn(
3632
4567
  "h-14 w-full bg-transparent text-body-large text-on-surface outline-none placeholder:text-on-surface-variant",
3633
4568
  leftElement ? "pl-14" : "pl-4",
3634
- rightElement ? "pr-14" : "pr-4",
4569
+ rightElement || showClear ? "pr-14" : "pr-4",
4570
+ // Hide the inconsistent native clear when we render our own.
4571
+ showClear && "[&::-webkit-search-cancel-button]:hidden",
3635
4572
  inputClassName
3636
4573
  ),
3637
4574
  type: "search",
3638
4575
  ...inputProps,
3639
- placeholder: l.placeholder
4576
+ onChange: handleChange,
4577
+ placeholder: l.placeholder,
4578
+ ref: input,
4579
+ value
3640
4580
  }
3641
4581
  ),
3642
- rightElement ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 flex h-14 w-14 items-center justify-center text-on-surface-variant", children: rightElement }) : null
4582
+ rightElement ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 flex h-14 w-14 items-center justify-center text-on-surface-variant", children: rightElement }) : showClear ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 flex h-14 w-14 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
4583
+ "button",
4584
+ {
4585
+ "aria-label": l.clear,
4586
+ className: "state-layer flex h-12 w-12 cursor-pointer items-center justify-center rounded-full text-on-surface-variant",
4587
+ onClick: clear,
4588
+ type: "button",
4589
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon2, {})
4590
+ }
4591
+ ) }) : null
3643
4592
  ] });
3644
4593
  }
3645
4594
  function Search({
@@ -3658,7 +4607,7 @@ function Search({
3658
4607
  "div",
3659
4608
  {
3660
4609
  className: cn(
3661
- "relative z-30 flex h-fit w-full min-w-[360px] max-w-[720px] cursor-pointer bg-surface-container-high text-on-surface",
4610
+ "relative z-[var(--md-sys-z-dropdown)] flex h-fit w-full min-w-[360px] max-w-[720px] cursor-pointer bg-surface-container-high text-on-surface",
3662
4611
  // Contained (M3 Expressive): the bar stays a persistent 28px
3663
4612
  // pill (real px — corner-full would snap instead of morphing).
3664
4613
  // Divided (baseline): its bottom corners square off in step
@@ -3709,221 +4658,6 @@ function Search({
3709
4658
  }
3710
4659
  Search.Item = SearchItem;
3711
4660
  Search.Input = SearchInput;
3712
- function useControlled(controlled, defaultValue) {
3713
- const isControlled = react.useRef(controlled !== void 0).current;
3714
- const [internal, setInternal] = react.useState(defaultValue);
3715
- const value = isControlled && controlled !== void 0 ? controlled : internal;
3716
- const setValue = react.useCallback(
3717
- (next) => {
3718
- if (!isControlled) setInternal(next);
3719
- },
3720
- [isControlled]
3721
- );
3722
- return [value, setValue];
3723
- }
3724
- var TYPEAHEAD_BUFFER_MS = 200;
3725
- function optionText(option) {
3726
- return typeof option.label === "string" ? option.label : option.value;
3727
- }
3728
- function useSelect({
3729
- defaultValue,
3730
- disabled,
3731
- onChange,
3732
- options,
3733
- value: valueProp
3734
- }) {
3735
- const [value, setValue] = useControlled(valueProp, defaultValue ?? "");
3736
- const [isOpen, setIsOpen] = react.useState(false);
3737
- const [focused, setFocused] = react.useState(false);
3738
- const [highlighted, setHighlighted] = react.useState(-1);
3739
- const wrapper = react.useRef(null);
3740
- const typeahead = react.useRef({ buffer: "", time: 0 });
3741
- const { exiting, mounted } = useDismissable(isOpen, 150);
3742
- useOutsideClose(wrapper, () => setIsOpen(false), isOpen);
3743
- const selectedIndex = options.findIndex((option) => option.value === value);
3744
- const move = (start, delta) => {
3745
- for (let step = 1; step <= options.length; step += 1) {
3746
- const index = (start + delta * step + options.length * step) % options.length;
3747
- if (!options[index]?.disabled) return index;
3748
- }
3749
- return -1;
3750
- };
3751
- const openMenu = () => {
3752
- if (disabled) return;
3753
- setHighlighted(
3754
- selectedIndex >= 0 && !options[selectedIndex]?.disabled ? selectedIndex : move(-1, 1)
3755
- );
3756
- setIsOpen(true);
3757
- };
3758
- const commit = (next) => {
3759
- setValue(next);
3760
- onChange?.(next);
3761
- setIsOpen(false);
3762
- };
3763
- const typeaheadFind = (char) => {
3764
- const now = Date.now();
3765
- const stale = now - typeahead.current.time > TYPEAHEAD_BUFFER_MS;
3766
- typeahead.current = {
3767
- buffer: (stale ? "" : typeahead.current.buffer) + char.toLowerCase(),
3768
- time: now
3769
- };
3770
- return options.findIndex(
3771
- (option) => !option.disabled && optionText(option).toLowerCase().startsWith(typeahead.current.buffer)
3772
- );
3773
- };
3774
- const handleKeyDown = (event) => {
3775
- if (disabled) return;
3776
- const { key } = event;
3777
- const printable = key.length === 1 && key !== " " && !event.altKey && !event.ctrlKey && !event.metaKey;
3778
- if (!isOpen) {
3779
- if (["ArrowDown", "ArrowUp", "Enter", " "].includes(key)) {
3780
- event.preventDefault();
3781
- openMenu();
3782
- } else if (printable) {
3783
- const index = typeaheadFind(key);
3784
- if (index >= 0) {
3785
- setValue(options[index].value);
3786
- onChange?.(options[index].value);
3787
- }
3788
- }
3789
- return;
3790
- }
3791
- switch (key) {
3792
- case "ArrowDown":
3793
- event.preventDefault();
3794
- setHighlighted((current) => move(current, 1));
3795
- break;
3796
- case "ArrowUp":
3797
- event.preventDefault();
3798
- setHighlighted((current) => move(current, -1));
3799
- break;
3800
- case "Home":
3801
- event.preventDefault();
3802
- setHighlighted(move(-1, 1));
3803
- break;
3804
- case "End":
3805
- event.preventDefault();
3806
- setHighlighted(move(options.length, -1));
3807
- break;
3808
- case "Enter":
3809
- case " ": {
3810
- event.preventDefault();
3811
- const option = options[highlighted];
3812
- if (option && !option.disabled) commit(option.value);
3813
- break;
3814
- }
3815
- case "Escape":
3816
- event.preventDefault();
3817
- setIsOpen(false);
3818
- break;
3819
- case "Tab":
3820
- setIsOpen(false);
3821
- break;
3822
- default:
3823
- if (printable) {
3824
- const index = typeaheadFind(key);
3825
- if (index >= 0) setHighlighted(index);
3826
- }
3827
- }
3828
- };
3829
- return {
3830
- commit,
3831
- exiting,
3832
- focused: focused && !disabled,
3833
- handleKeyDown,
3834
- highlighted,
3835
- isOpen,
3836
- mounted,
3837
- openMenu,
3838
- selectedIndex,
3839
- setFocused,
3840
- setHighlighted,
3841
- setIsOpen,
3842
- value,
3843
- wrapper
3844
- };
3845
- }
3846
- function SelectMenu({
3847
- baseId,
3848
- exiting,
3849
- highlighted,
3850
- onHighlight,
3851
- onSelect,
3852
- options,
3853
- value
3854
- }) {
3855
- react.useEffect(() => {
3856
- if (highlighted < 0) return;
3857
- document.getElementById(`${baseId}-opt-${highlighted}`)?.scrollIntoView({ block: "nearest" });
3858
- }, [baseId, highlighted]);
3859
- return /* @__PURE__ */ jsxRuntime.jsx(
3860
- "ul",
3861
- {
3862
- className: cn(
3863
- "absolute top-full left-0 z-30 mt-1 flex max-h-[280px] w-full min-w-max flex-col overflow-hidden overflow-y-auto rounded-large bg-surface-container-low py-0.5 shadow-mm-3 [--menu-clip-bleed:-24px]",
3864
- exiting ? "animate-menu-out" : "animate-menu-in"
3865
- ),
3866
- id: `${baseId}-listbox`,
3867
- role: "listbox",
3868
- children: options.map((option, index) => {
3869
- const isSelected = option.value === value;
3870
- return /* @__PURE__ */ jsxRuntime.jsx(
3871
- "li",
3872
- {
3873
- "aria-disabled": option.disabled || void 0,
3874
- "aria-selected": isSelected,
3875
- className: cn(
3876
- "mx-1 my-0.5 flex h-11 shrink-0 cursor-pointer items-center rounded-extra-small px-3 text-label-large first:rounded-t-medium last:rounded-b-medium",
3877
- isSelected ? "rounded-medium bg-tertiary-container text-on-tertiary-container" : "text-on-surface",
3878
- highlighted === index && !isSelected && "bg-on-surface/8",
3879
- option.disabled && "cursor-not-allowed text-on-surface/38"
3880
- ),
3881
- id: `${baseId}-opt-${index}`,
3882
- onClick: () => {
3883
- if (!option.disabled) onSelect(option.value);
3884
- },
3885
- onMouseDown: (event) => event.preventDefault(),
3886
- onMouseEnter: () => {
3887
- if (!option.disabled) onHighlight(index);
3888
- },
3889
- role: "option",
3890
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: option.label ?? option.value })
3891
- },
3892
- option.value
3893
- );
3894
- })
3895
- }
3896
- );
3897
- }
3898
- function SelectCaret({ open = false }) {
3899
- const fade = "ease-md-linear transition-opacity delay-75 duration-75";
3900
- return /* @__PURE__ */ jsxRuntime.jsxs(
3901
- "svg",
3902
- {
3903
- "aria-hidden": true,
3904
- fill: "currentColor",
3905
- height: 24,
3906
- viewBox: "0 0 24 24",
3907
- width: 24,
3908
- children: [
3909
- /* @__PURE__ */ jsxRuntime.jsx(
3910
- "path",
3911
- {
3912
- className: cn(fade, open ? "opacity-0" : "opacity-100"),
3913
- d: "M7 10l5 5 5-5z"
3914
- }
3915
- ),
3916
- /* @__PURE__ */ jsxRuntime.jsx(
3917
- "path",
3918
- {
3919
- className: cn(fade, open ? "opacity-100" : "opacity-0"),
3920
- d: "M7 15l5-5 5 5z"
3921
- }
3922
- )
3923
- ]
3924
- }
3925
- );
3926
- }
3927
4661
  function SelectFilled({
3928
4662
  className,
3929
4663
  defaultValue,
@@ -4252,7 +4986,7 @@ function BottomSheet({
4252
4986
  "div",
4253
4987
  {
4254
4988
  className: cn(
4255
- "fixed inset-0 z-40 flex items-end justify-center bg-scrim/32",
4989
+ "fixed inset-0 z-[var(--md-sys-z-modal)] flex items-end justify-center bg-scrim/32",
4256
4990
  exiting ? "animate-scrim-sheet-out" : "animate-scrim-sheet-in"
4257
4991
  ),
4258
4992
  onClick: onClose,
@@ -4294,7 +5028,7 @@ var SIDE_SHEET_LABELS = {
4294
5028
  close: "Close",
4295
5029
  label: "Side sheet"
4296
5030
  };
4297
- function CloseIcon2() {
5031
+ function CloseIcon3() {
4298
5032
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { "aria-hidden": true, fill: "none", height: 24, viewBox: "0 0 24 24", width: 24, children: /* @__PURE__ */ jsxRuntime.jsx(
4299
5033
  "path",
4300
5034
  {
@@ -4340,7 +5074,7 @@ function SideSheet({
4340
5074
  "div",
4341
5075
  {
4342
5076
  className: cn(
4343
- "fixed inset-0 z-40 flex justify-end bg-scrim/32",
5077
+ "fixed inset-0 z-[var(--md-sys-z-modal)] flex justify-end bg-scrim/32",
4344
5078
  exiting ? "animate-scrim-sheet-out" : "animate-scrim-sheet-in"
4345
5079
  ),
4346
5080
  onClick: onClose,
@@ -4374,7 +5108,7 @@ function SideSheet({
4374
5108
  IconButton,
4375
5109
  {
4376
5110
  "aria-label": l.close,
4377
- icon: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon2, {}),
5111
+ icon: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon3, {}),
4378
5112
  onClick: onClose,
4379
5113
  variant: "standard"
4380
5114
  }
@@ -4771,7 +5505,7 @@ function SliderDual({
4771
5505
  );
4772
5506
  }
4773
5507
  var SNACKBAR_LABELS = { dismiss: "Dismiss" };
4774
- function CloseIcon3() {
5508
+ function CloseIcon4() {
4775
5509
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { "aria-hidden": true, fill: "none", height: 24, viewBox: "0 0 24 24", width: 24, children: /* @__PURE__ */ jsxRuntime.jsx(
4776
5510
  "path",
4777
5511
  {
@@ -4850,7 +5584,7 @@ function Snackbar({
4850
5584
  className: "iconBtn standard text-inverse-on-surface",
4851
5585
  onClick: onClose,
4852
5586
  type: "button",
4853
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-6 w-6 items-center justify-center leading-none", children: closeIcon ?? /* @__PURE__ */ jsxRuntime.jsx(CloseIcon3, {}) })
5587
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-6 w-6 items-center justify-center leading-none", children: closeIcon ?? /* @__PURE__ */ jsxRuntime.jsx(CloseIcon4, {}) })
4854
5588
  }
4855
5589
  ) : null
4856
5590
  ] })
@@ -4864,7 +5598,7 @@ function SnackbarWrapper({ children, className }) {
4864
5598
  react.useEffect(() => setMounted(true), []);
4865
5599
  if (!mounted) return null;
4866
5600
  return reactDom.createPortal(
4867
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none fixed inset-x-4 top-auto bottom-0 z-60 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
5601
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none fixed inset-x-4 top-auto bottom-0 z-[var(--md-sys-z-snackbar)] flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
4868
5602
  "div",
4869
5603
  {
4870
5604
  className: cn(
@@ -6177,7 +6911,7 @@ function Tooltip({
6177
6911
  {
6178
6912
  id: tooltipId,
6179
6913
  className: cn(
6180
- "tooltip absolute z-40 my-1",
6914
+ "tooltip absolute z-[var(--md-sys-z-tooltip)] my-1",
6181
6915
  rich ? "pointer-events-none w-max max-w-[320px] rounded-medium bg-surface-container px-4 pt-3 pb-2 shadow-mm-2 group-hover:pointer-events-auto group-focus-within:pointer-events-auto" : "pointer-events-none max-w-[200px] rounded-extra-small bg-inverse-surface px-2 py-1",
6182
6916
  topRight && "right-0 bottom-full",
6183
6917
  topLeft && "bottom-full left-0",
@@ -6869,40 +7603,54 @@ function Switch({
6869
7603
  );
6870
7604
  }
6871
7605
  function TextElement({
7606
+ as: Wrapper = "div",
6872
7607
  body,
7608
+ bodyAs: Body = "p",
6873
7609
  bodyStyle,
6874
7610
  className,
6875
7611
  label,
7612
+ labelAs: Label = "p",
6876
7613
  labelStyle,
6877
7614
  title,
7615
+ titleAs: Title = "h2",
6878
7616
  titleStyle
6879
7617
  }) {
7618
+ const bare = [label, title, body].filter(Boolean).length === 1;
7619
+ const labelNode = label ? /* @__PURE__ */ jsxRuntime.jsx(
7620
+ Label,
7621
+ {
7622
+ className: cn(
7623
+ "text-label-medium text-on-surface-variant",
7624
+ labelStyle,
7625
+ bare && className
7626
+ ),
7627
+ children: label
7628
+ }
7629
+ ) : null;
7630
+ const titleNode = title ? /* @__PURE__ */ jsxRuntime.jsx(Title, { className: cn("text-title-medium text-on-surface", titleStyle, bare && className), children: title }) : null;
7631
+ const bodyNode = body ? /* @__PURE__ */ jsxRuntime.jsx(
7632
+ Body,
7633
+ {
7634
+ className: cn(
7635
+ "text-body-medium text-on-surface-variant",
7636
+ bodyStyle,
7637
+ bare && className
7638
+ ),
7639
+ children: body
7640
+ }
7641
+ ) : null;
7642
+ if (bare) return labelNode ?? titleNode ?? bodyNode;
6880
7643
  return /* @__PURE__ */ jsxRuntime.jsxs(
6881
- "div",
7644
+ Wrapper,
6882
7645
  {
6883
7646
  className: cn(
6884
7647
  "flex flex-col items-start justify-center text-on-surface",
6885
7648
  className
6886
7649
  ),
6887
7650
  children: [
6888
- label ? /* @__PURE__ */ jsxRuntime.jsx(
6889
- "p",
6890
- {
6891
- className: cn(
6892
- "text-label-medium text-on-surface-variant",
6893
- labelStyle
6894
- ),
6895
- children: label
6896
- }
6897
- ) : null,
6898
- title ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: cn("text-title-medium", titleStyle), children: title }) : null,
6899
- body ? /* @__PURE__ */ jsxRuntime.jsx(
6900
- "p",
6901
- {
6902
- className: cn("text-body-medium text-on-surface-variant", bodyStyle),
6903
- children: body
6904
- }
6905
- ) : null
7651
+ labelNode,
7652
+ titleNode,
7653
+ bodyNode
6906
7654
  ]
6907
7655
  }
6908
7656
  );
@@ -6951,6 +7699,8 @@ exports.Card = Card;
6951
7699
  exports.Checkbox = Checkbox;
6952
7700
  exports.Chips = Chips;
6953
7701
  exports.Circle = Circle;
7702
+ exports.ComboboxFilled = ComboboxFilled;
7703
+ exports.ComboboxOutlined = ComboboxOutlined;
6954
7704
  exports.Container = Container;
6955
7705
  exports.DatePicker = DatePicker;
6956
7706
  exports.DateRangePicker = DateRangePicker;