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