@geomak/ui 1.7.4 → 1.7.5

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.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { C as COLORS, S as SemanticColorKey, a as SemanticSharedKey, V as VarColorKey, b as VarDensityKey, c as VarMotionKey, d as VarRadiusKey, e as VarShadowKey, f as VarTypoKey, g as VarZIndexKey, P as palette, s as semanticTokens, v as vars } from './index-CvyV3YPI.cjs';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
- import React$1 from 'react';
3
+ import React$1, { ReactNode } from 'react';
4
4
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
5
5
 
6
6
  declare const Icon: {
@@ -652,7 +652,6 @@ declare function FadingBase({ className, isMounted, children, }: FadingBaseProps
652
652
  interface ListItem {
653
653
  key: string | number;
654
654
  label: React$1.ReactNode;
655
- [key: string]: any;
656
655
  }
657
656
  interface ListProps {
658
657
  items: ListItem[];
@@ -1399,7 +1398,7 @@ interface ButtonProps {
1399
1398
  declare function Button({ content, variant, size, buttonType, loading, disabled, style, icon, onClick, }: ButtonProps): react_jsx_runtime.JSX.Element;
1400
1399
 
1401
1400
  interface TextInputProps {
1402
- value?: any;
1401
+ value?: string;
1403
1402
  onChange?: React$1.ChangeEventHandler<HTMLInputElement>;
1404
1403
  disabled?: boolean;
1405
1404
  label?: React$1.ReactNode;
@@ -1408,13 +1407,12 @@ interface TextInputProps {
1408
1407
  name?: string;
1409
1408
  inputStyle?: React$1.CSSProperties;
1410
1409
  style?: React$1.CSSProperties;
1411
- /** 'horizontal' | 'vertical' */
1412
- layout?: string;
1410
+ /** Label/input orientation. Defaults to `'horizontal'`. */
1411
+ layout?: 'horizontal' | 'vertical';
1413
1412
  onBlur?: React$1.FocusEventHandler<HTMLInputElement>;
1414
1413
  errorMessage?: React$1.ReactNode;
1415
1414
  labelColor?: string;
1416
1415
  id?: string;
1417
- [key: string]: any;
1418
1416
  }
1419
1417
  /**
1420
1418
  * Standard text input with label and validation message.
@@ -1453,7 +1451,7 @@ interface NumberInputProps {
1453
1451
  declare function NumberInput({ step, value, onChange, label, htmlFor, name, disabled, layout, errorMessage, inputStyle, labelStyle, placeholder, style, min, max, readOnly, }: NumberInputProps): react_jsx_runtime.JSX.Element;
1454
1452
 
1455
1453
  interface PasswordProps {
1456
- value?: any;
1454
+ value?: string;
1457
1455
  onChange?: React$1.ChangeEventHandler<HTMLInputElement>;
1458
1456
  disabled?: boolean;
1459
1457
  label?: React$1.ReactNode;
@@ -1462,12 +1460,12 @@ interface PasswordProps {
1462
1460
  name?: string;
1463
1461
  inputStyle?: React$1.CSSProperties;
1464
1462
  style?: React$1.CSSProperties;
1465
- layout?: string;
1463
+ /** Label/input orientation. Defaults to `'horizontal'`. */
1464
+ layout?: 'horizontal' | 'vertical';
1466
1465
  onBlur?: React$1.FocusEventHandler<HTMLInputElement>;
1467
1466
  errorMessage?: React$1.ReactNode;
1468
1467
  labelColor?: string;
1469
1468
  iconColor?: string;
1470
- [key: string]: any;
1471
1469
  }
1472
1470
  /**
1473
1471
  * Password input with show/hide toggle.
@@ -1475,7 +1473,7 @@ interface PasswordProps {
1475
1473
  declare function Password({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout, onBlur, errorMessage, labelColor, iconColor, }: PasswordProps): react_jsx_runtime.JSX.Element;
1476
1474
 
1477
1475
  interface SearchInputProps {
1478
- value?: any;
1476
+ value?: string;
1479
1477
  onChange?: React$1.ChangeEventHandler<HTMLInputElement>;
1480
1478
  disabled?: boolean;
1481
1479
  label?: React$1.ReactNode;
@@ -1484,16 +1482,16 @@ interface SearchInputProps {
1484
1482
  name?: string;
1485
1483
  inputStyle?: React$1.CSSProperties;
1486
1484
  style?: React$1.CSSProperties;
1487
- layout?: string;
1488
- [key: string]: any;
1485
+ /** Label/input orientation. Defaults to `'vertical'`. */
1486
+ layout?: 'horizontal' | 'vertical';
1489
1487
  }
1490
1488
  /**
1491
1489
  * Search text field with a magnifier icon on the right.
1492
1490
  */
1493
- declare const SearchInput: React$1.ForwardRefExoticComponent<Omit<SearchInputProps, "ref"> & React$1.RefAttributes<HTMLInputElement>>;
1491
+ declare const SearchInput: React$1.ForwardRefExoticComponent<SearchInputProps & React$1.RefAttributes<HTMLInputElement>>;
1494
1492
 
1495
1493
  interface DropdownPillProps {
1496
- value?: React.ReactNode;
1494
+ value?: ReactNode;
1497
1495
  hasSiblings?: boolean;
1498
1496
  }
1499
1497
  /**
@@ -1546,7 +1544,6 @@ interface SwitchInputProps {
1546
1544
  }) => void;
1547
1545
  checkedIcon?: React$1.ReactNode;
1548
1546
  uncheckedIcon?: React$1.ReactNode;
1549
- [key: string]: any;
1550
1547
  }
1551
1548
  /**
1552
1549
  * Form switch (on/off toggle) powered by Radix Switch.
@@ -1569,30 +1566,39 @@ interface DropdownItem {
1569
1566
  label: React$1.ReactNode;
1570
1567
  icon?: React$1.ReactNode;
1571
1568
  }
1569
+ /**
1570
+ * Item key type — DOM-friendly subset of `React.Key` (no bigint, since UI
1571
+ * keys are always strings or numbers in practice).
1572
+ */
1573
+ type DropdownKey = string | number;
1574
+ /**
1575
+ * Selected value(s). In single-select mode this is a single key matching
1576
+ * one of the items. In multi-select mode it is an array of keys.
1577
+ */
1578
+ type DropdownValue = DropdownKey | DropdownKey[];
1572
1579
  interface DropdownProps {
1573
1580
  isMultiselect?: boolean;
1574
1581
  hasSearch?: boolean;
1575
1582
  label?: React$1.ReactNode;
1576
1583
  name?: string;
1577
- value?: any;
1584
+ value?: DropdownValue;
1578
1585
  onChange?: (e: {
1579
1586
  target: {
1580
- value: any;
1587
+ value: DropdownValue;
1581
1588
  id?: string;
1582
1589
  name?: string;
1583
1590
  };
1584
1591
  }) => void;
1585
1592
  onBlur?: React$1.FocusEventHandler;
1586
1593
  disabled?: boolean;
1587
- /** 'horizontal' | 'vertical' */
1588
- layout?: string;
1594
+ /** Label/input orientation. Defaults to `'vertical'`. */
1595
+ layout?: 'horizontal' | 'vertical';
1589
1596
  errorMessage?: React$1.ReactNode;
1590
1597
  style?: React$1.CSSProperties;
1591
1598
  htmlFor?: string;
1592
1599
  items?: DropdownItem[];
1593
1600
  labelStyle?: React$1.CSSProperties;
1594
1601
  placeholder?: string;
1595
- [key: string]: any;
1596
1602
  }
1597
1603
  /**
1598
1604
  * Select / multi-select dropdown powered by Radix Popover.
@@ -1624,13 +1630,12 @@ interface AutoCompleteProps {
1624
1630
  name?: string;
1625
1631
  inputStyle?: React$1.CSSProperties;
1626
1632
  style?: React$1.CSSProperties;
1627
- /** 'horizontal' | 'vertical' */
1628
- layout?: string;
1633
+ /** Label/input orientation. Defaults to `'vertical'`. */
1634
+ layout?: 'horizontal' | 'vertical';
1629
1635
  items?: AutoCompleteItem[];
1630
1636
  onItemClick?: (value: string) => void;
1631
1637
  /** Custom "empty" message */
1632
1638
  emptyText?: string;
1633
- [key: string]: any;
1634
1639
  }
1635
1640
  /**
1636
1641
  * Search-as-you-type autocomplete powered by Radix Popover.
@@ -1695,7 +1700,6 @@ interface FileInputProps {
1695
1700
  name?: string;
1696
1701
  /** Accepted MIME types / extensions */
1697
1702
  accept?: string;
1698
- [key: string]: any;
1699
1703
  }
1700
1704
  /**
1701
1705
  * Drag-and-drop / click file input.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { C as COLORS, S as SemanticColorKey, a as SemanticSharedKey, V as VarColorKey, b as VarDensityKey, c as VarMotionKey, d as VarRadiusKey, e as VarShadowKey, f as VarTypoKey, g as VarZIndexKey, P as palette, s as semanticTokens, v as vars } from './index-CvyV3YPI.js';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
- import React$1 from 'react';
3
+ import React$1, { ReactNode } from 'react';
4
4
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
5
5
 
6
6
  declare const Icon: {
@@ -652,7 +652,6 @@ declare function FadingBase({ className, isMounted, children, }: FadingBaseProps
652
652
  interface ListItem {
653
653
  key: string | number;
654
654
  label: React$1.ReactNode;
655
- [key: string]: any;
656
655
  }
657
656
  interface ListProps {
658
657
  items: ListItem[];
@@ -1399,7 +1398,7 @@ interface ButtonProps {
1399
1398
  declare function Button({ content, variant, size, buttonType, loading, disabled, style, icon, onClick, }: ButtonProps): react_jsx_runtime.JSX.Element;
1400
1399
 
1401
1400
  interface TextInputProps {
1402
- value?: any;
1401
+ value?: string;
1403
1402
  onChange?: React$1.ChangeEventHandler<HTMLInputElement>;
1404
1403
  disabled?: boolean;
1405
1404
  label?: React$1.ReactNode;
@@ -1408,13 +1407,12 @@ interface TextInputProps {
1408
1407
  name?: string;
1409
1408
  inputStyle?: React$1.CSSProperties;
1410
1409
  style?: React$1.CSSProperties;
1411
- /** 'horizontal' | 'vertical' */
1412
- layout?: string;
1410
+ /** Label/input orientation. Defaults to `'horizontal'`. */
1411
+ layout?: 'horizontal' | 'vertical';
1413
1412
  onBlur?: React$1.FocusEventHandler<HTMLInputElement>;
1414
1413
  errorMessage?: React$1.ReactNode;
1415
1414
  labelColor?: string;
1416
1415
  id?: string;
1417
- [key: string]: any;
1418
1416
  }
1419
1417
  /**
1420
1418
  * Standard text input with label and validation message.
@@ -1453,7 +1451,7 @@ interface NumberInputProps {
1453
1451
  declare function NumberInput({ step, value, onChange, label, htmlFor, name, disabled, layout, errorMessage, inputStyle, labelStyle, placeholder, style, min, max, readOnly, }: NumberInputProps): react_jsx_runtime.JSX.Element;
1454
1452
 
1455
1453
  interface PasswordProps {
1456
- value?: any;
1454
+ value?: string;
1457
1455
  onChange?: React$1.ChangeEventHandler<HTMLInputElement>;
1458
1456
  disabled?: boolean;
1459
1457
  label?: React$1.ReactNode;
@@ -1462,12 +1460,12 @@ interface PasswordProps {
1462
1460
  name?: string;
1463
1461
  inputStyle?: React$1.CSSProperties;
1464
1462
  style?: React$1.CSSProperties;
1465
- layout?: string;
1463
+ /** Label/input orientation. Defaults to `'horizontal'`. */
1464
+ layout?: 'horizontal' | 'vertical';
1466
1465
  onBlur?: React$1.FocusEventHandler<HTMLInputElement>;
1467
1466
  errorMessage?: React$1.ReactNode;
1468
1467
  labelColor?: string;
1469
1468
  iconColor?: string;
1470
- [key: string]: any;
1471
1469
  }
1472
1470
  /**
1473
1471
  * Password input with show/hide toggle.
@@ -1475,7 +1473,7 @@ interface PasswordProps {
1475
1473
  declare function Password({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout, onBlur, errorMessage, labelColor, iconColor, }: PasswordProps): react_jsx_runtime.JSX.Element;
1476
1474
 
1477
1475
  interface SearchInputProps {
1478
- value?: any;
1476
+ value?: string;
1479
1477
  onChange?: React$1.ChangeEventHandler<HTMLInputElement>;
1480
1478
  disabled?: boolean;
1481
1479
  label?: React$1.ReactNode;
@@ -1484,16 +1482,16 @@ interface SearchInputProps {
1484
1482
  name?: string;
1485
1483
  inputStyle?: React$1.CSSProperties;
1486
1484
  style?: React$1.CSSProperties;
1487
- layout?: string;
1488
- [key: string]: any;
1485
+ /** Label/input orientation. Defaults to `'vertical'`. */
1486
+ layout?: 'horizontal' | 'vertical';
1489
1487
  }
1490
1488
  /**
1491
1489
  * Search text field with a magnifier icon on the right.
1492
1490
  */
1493
- declare const SearchInput: React$1.ForwardRefExoticComponent<Omit<SearchInputProps, "ref"> & React$1.RefAttributes<HTMLInputElement>>;
1491
+ declare const SearchInput: React$1.ForwardRefExoticComponent<SearchInputProps & React$1.RefAttributes<HTMLInputElement>>;
1494
1492
 
1495
1493
  interface DropdownPillProps {
1496
- value?: React.ReactNode;
1494
+ value?: ReactNode;
1497
1495
  hasSiblings?: boolean;
1498
1496
  }
1499
1497
  /**
@@ -1546,7 +1544,6 @@ interface SwitchInputProps {
1546
1544
  }) => void;
1547
1545
  checkedIcon?: React$1.ReactNode;
1548
1546
  uncheckedIcon?: React$1.ReactNode;
1549
- [key: string]: any;
1550
1547
  }
1551
1548
  /**
1552
1549
  * Form switch (on/off toggle) powered by Radix Switch.
@@ -1569,30 +1566,39 @@ interface DropdownItem {
1569
1566
  label: React$1.ReactNode;
1570
1567
  icon?: React$1.ReactNode;
1571
1568
  }
1569
+ /**
1570
+ * Item key type — DOM-friendly subset of `React.Key` (no bigint, since UI
1571
+ * keys are always strings or numbers in practice).
1572
+ */
1573
+ type DropdownKey = string | number;
1574
+ /**
1575
+ * Selected value(s). In single-select mode this is a single key matching
1576
+ * one of the items. In multi-select mode it is an array of keys.
1577
+ */
1578
+ type DropdownValue = DropdownKey | DropdownKey[];
1572
1579
  interface DropdownProps {
1573
1580
  isMultiselect?: boolean;
1574
1581
  hasSearch?: boolean;
1575
1582
  label?: React$1.ReactNode;
1576
1583
  name?: string;
1577
- value?: any;
1584
+ value?: DropdownValue;
1578
1585
  onChange?: (e: {
1579
1586
  target: {
1580
- value: any;
1587
+ value: DropdownValue;
1581
1588
  id?: string;
1582
1589
  name?: string;
1583
1590
  };
1584
1591
  }) => void;
1585
1592
  onBlur?: React$1.FocusEventHandler;
1586
1593
  disabled?: boolean;
1587
- /** 'horizontal' | 'vertical' */
1588
- layout?: string;
1594
+ /** Label/input orientation. Defaults to `'vertical'`. */
1595
+ layout?: 'horizontal' | 'vertical';
1589
1596
  errorMessage?: React$1.ReactNode;
1590
1597
  style?: React$1.CSSProperties;
1591
1598
  htmlFor?: string;
1592
1599
  items?: DropdownItem[];
1593
1600
  labelStyle?: React$1.CSSProperties;
1594
1601
  placeholder?: string;
1595
- [key: string]: any;
1596
1602
  }
1597
1603
  /**
1598
1604
  * Select / multi-select dropdown powered by Radix Popover.
@@ -1624,13 +1630,12 @@ interface AutoCompleteProps {
1624
1630
  name?: string;
1625
1631
  inputStyle?: React$1.CSSProperties;
1626
1632
  style?: React$1.CSSProperties;
1627
- /** 'horizontal' | 'vertical' */
1628
- layout?: string;
1633
+ /** Label/input orientation. Defaults to `'vertical'`. */
1634
+ layout?: 'horizontal' | 'vertical';
1629
1635
  items?: AutoCompleteItem[];
1630
1636
  onItemClick?: (value: string) => void;
1631
1637
  /** Custom "empty" message */
1632
1638
  emptyText?: string;
1633
- [key: string]: any;
1634
1639
  }
1635
1640
  /**
1636
1641
  * Search-as-you-type autocomplete powered by Radix Popover.
@@ -1695,7 +1700,6 @@ interface FileInputProps {
1695
1700
  name?: string;
1696
1701
  /** Accepted MIME types / extensions */
1697
1702
  accept?: string;
1698
- [key: string]: any;
1699
1703
  }
1700
1704
  /**
1701
1705
  * Drag-and-drop / click file input.
package/dist/index.js CHANGED
@@ -1027,16 +1027,28 @@ function FadingBase({
1027
1027
  );
1028
1028
  }
1029
1029
  function List2({ items, onItemClick, activeKey }) {
1030
- return /* @__PURE__ */ jsx("div", { role: "listbox", children: items.map((item) => /* @__PURE__ */ jsx(
1031
- "div",
1032
- {
1033
- role: "option",
1034
- "aria-selected": activeKey === item.key,
1035
- className: `hover:bg-ice-dark dark:hover:bg-independence cursor-pointer p-3 border-b border-b-ice-dark dark:border-b-independence transition-all duration-300 ${activeKey === item.key ? "bg-ice-dark dark:bg-independence" : ""}`,
1036
- onClick: () => onItemClick(item),
1037
- children: item.label
1038
- },
1039
- item.key
1030
+ return /* @__PURE__ */ jsx("div", { role: "listbox", children: items.map((item) => (
1031
+ // tabIndex + Enter/Space onKeyDown makes each option
1032
+ // keyboard-activatable. Previously the items were only mouse-
1033
+ // clickable — keyboard-only users couldn't select anything.
1034
+ /* @__PURE__ */ jsx(
1035
+ "div",
1036
+ {
1037
+ role: "option",
1038
+ "aria-selected": activeKey === item.key,
1039
+ tabIndex: 0,
1040
+ className: `hover:bg-ice-dark dark:hover:bg-independence cursor-pointer p-3 border-b border-b-ice-dark dark:border-b-independence transition-all duration-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${activeKey === item.key ? "bg-ice-dark dark:bg-independence" : ""}`,
1041
+ onClick: () => onItemClick(item),
1042
+ onKeyDown: (e) => {
1043
+ if (e.key === "Enter" || e.key === " ") {
1044
+ e.preventDefault();
1045
+ onItemClick(item);
1046
+ }
1047
+ },
1048
+ children: item.label
1049
+ },
1050
+ item.key
1051
+ )
1040
1052
  )) });
1041
1053
  }
1042
1054
  function ScalableContainer({
@@ -1542,6 +1554,8 @@ function Dropdown({
1542
1554
  const [hoveredItem, setHoveredItem] = useState(null);
1543
1555
  const [searchTerm, setSearchTerm] = useState("");
1544
1556
  const [innerItems, setInnerItems] = useState([]);
1557
+ const errorId = useId();
1558
+ const hasError = errorMessage != null;
1545
1559
  useEffect(() => {
1546
1560
  setInnerItems(items);
1547
1561
  }, [items]);
@@ -1594,10 +1608,18 @@ function Dropdown({
1594
1608
  role: "combobox",
1595
1609
  "aria-expanded": open,
1596
1610
  "aria-haspopup": "listbox",
1611
+ "aria-invalid": hasError || void 0,
1612
+ "aria-describedby": hasError ? errorId : void 0,
1597
1613
  style,
1598
1614
  className: `flex items-center justify-between relative h-9 rounded-lg cursor-pointer select-none ${disabled ? "cursor-not-allowed bg-disabled" : "bg-white"}`,
1599
1615
  tabIndex: disabled ? -1 : 0,
1600
- onKeyDown: (e) => e.key === "Enter" && !disabled && setOpen(true),
1616
+ onKeyDown: (e) => {
1617
+ if (disabled) return;
1618
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown" || e.key === "ArrowUp") {
1619
+ e.preventDefault();
1620
+ setOpen(true);
1621
+ }
1622
+ },
1601
1623
  children: [
1602
1624
  /* @__PURE__ */ jsx(
1603
1625
  "div",
@@ -1639,34 +1661,49 @@ function Dropdown({
1639
1661
  placeholder: "Search..."
1640
1662
  }
1641
1663
  ) }),
1642
- /* @__PURE__ */ jsx("div", { role: "listbox", "aria-multiselectable": isMultiselect, className: "max-h-40 overflow-y-auto", children: innerItems.map((item, idx) => /* @__PURE__ */ jsxs(
1643
- "div",
1644
- {
1645
- role: "option",
1646
- "aria-selected": isSelected(item.key),
1647
- "aria-rowindex": idx,
1648
- className: `flex items-center justify-between p-2 hover:bg-prussian-blue hover:text-white transition-all duration-150 text-sm text-prussian-blue rounded-lg cursor-pointer ${selectedItems.includes(item.key) ? "bg-ice-dark" : ""}`,
1649
- onClick: () => selectItem(item.key),
1650
- onMouseEnter: () => setHoveredItem(item.key),
1651
- onMouseLeave: () => setHoveredItem(null),
1652
- children: [
1653
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
1654
- item.icon && /* @__PURE__ */ jsx("div", { children: item.icon }),
1655
- item.label
1656
- ] }),
1657
- isSelected(item.key) && /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsx(
1658
- "path",
1659
- {
1660
- d: "M4 10l4.5 4.5L16 6",
1661
- stroke: hoveredItem === item.key ? "#fff" : colors_default.PALETTE["prussian-blue"],
1662
- strokeWidth: "2",
1663
- strokeLinecap: "round",
1664
- strokeLinejoin: "round"
1664
+ /* @__PURE__ */ jsx("div", { role: "listbox", "aria-multiselectable": isMultiselect, className: "max-h-40 overflow-y-auto", children: innerItems.map((item) => (
1665
+ // aria-rowindex was previously set here but
1666
+ // it's invalid ARIA on role="option" (it
1667
+ // belongs on rows of a grid/treegrid). Dropped.
1668
+ // tabIndex={0} + Enter/Space handler makes the
1669
+ // option keyboard-activatable; the full
1670
+ // combobox roving-tabindex pattern is deferred
1671
+ // until the planned Phase-5 rewrite.
1672
+ /* @__PURE__ */ jsxs(
1673
+ "div",
1674
+ {
1675
+ role: "option",
1676
+ "aria-selected": isSelected(item.key),
1677
+ tabIndex: 0,
1678
+ className: `flex items-center justify-between p-2 hover:bg-prussian-blue hover:text-white transition-all duration-150 text-sm text-prussian-blue rounded-lg cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${selectedItems.includes(item.key) ? "bg-ice-dark" : ""}`,
1679
+ onClick: () => selectItem(item.key),
1680
+ onKeyDown: (e) => {
1681
+ if (e.key === "Enter" || e.key === " ") {
1682
+ e.preventDefault();
1683
+ selectItem(item.key);
1665
1684
  }
1666
- ) })
1667
- ]
1668
- },
1669
- item.key
1685
+ },
1686
+ onMouseEnter: () => setHoveredItem(item.key),
1687
+ onMouseLeave: () => setHoveredItem(null),
1688
+ children: [
1689
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
1690
+ item.icon && /* @__PURE__ */ jsx("div", { children: item.icon }),
1691
+ item.label
1692
+ ] }),
1693
+ isSelected(item.key) && /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ jsx(
1694
+ "path",
1695
+ {
1696
+ d: "M4 10l4.5 4.5L16 6",
1697
+ stroke: hoveredItem === item.key ? "#fff" : colors_default.PALETTE["prussian-blue"],
1698
+ strokeWidth: "2",
1699
+ strokeLinecap: "round",
1700
+ strokeLinejoin: "round"
1701
+ }
1702
+ ) })
1703
+ ]
1704
+ },
1705
+ item.key
1706
+ )
1670
1707
  )) })
1671
1708
  ]
1672
1709
  }
@@ -1675,7 +1712,7 @@ function Dropdown({
1675
1712
  ]
1676
1713
  }
1677
1714
  ),
1678
- /* @__PURE__ */ jsx("div", { className: "text-center text-error dark:text-prussian-blue min-h-0", children: errorMessage })
1715
+ hasError && /* @__PURE__ */ jsx("div", { id: errorId, className: "text-center text-error dark:text-prussian-blue min-h-0", children: errorMessage })
1679
1716
  ] });
1680
1717
  }
1681
1718
  var DEFAULT_PICKER = [
@@ -1845,10 +1882,11 @@ function Pagination({
1845
1882
  isMultiselect: false,
1846
1883
  value: displayPerPageKey,
1847
1884
  onChange: ({ target: { value } }) => {
1848
- const key = typeof value === "string" && /^\d+$/.test(value) ? Number(value) : value;
1849
- if (!serverSide) setPerPageKey(key);
1850
- const opt = picker.find((o) => o.key === key);
1851
- onPerPageChange(opt?.label ?? opt?.value ?? key);
1885
+ if (Array.isArray(value)) return;
1886
+ const numKey = typeof value === "number" ? value : Number(value);
1887
+ if (!serverSide) setPerPageKey(numKey);
1888
+ const opt = picker.find((o) => o.key === numKey);
1889
+ onPerPageChange(opt?.label ?? opt?.value ?? numKey);
1852
1890
  }
1853
1891
  }
1854
1892
  )
@@ -2501,6 +2539,8 @@ function TextInput({
2501
2539
  errorMessage,
2502
2540
  labelColor
2503
2541
  }) {
2542
+ const errorId = useId();
2543
+ const hasError = errorMessage != null;
2504
2544
  return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center justify-center", children: [
2505
2545
  /* @__PURE__ */ jsxs(
2506
2546
  "div",
@@ -2531,7 +2571,9 @@ function TextInput({
2531
2571
  type: "text",
2532
2572
  name,
2533
2573
  id: htmlFor,
2534
- className: `${errorMessage !== void 0 ? "border border-error" : ""} focus:outline-oxford-blue-700-opaque p-2 h-9 w-60 outline-offset-2 text-prussian-blue mt-1 rounded-lg disabled:bg-disabled disabled:cursor-not-allowed transition-all`,
2574
+ "aria-invalid": hasError || void 0,
2575
+ "aria-describedby": hasError ? errorId : void 0,
2576
+ className: `${hasError ? "border border-error" : ""} focus:outline-oxford-blue-700-opaque p-2 h-9 w-60 outline-offset-2 text-prussian-blue mt-1 rounded-lg disabled:bg-disabled disabled:cursor-not-allowed transition-all`,
2535
2577
  style: inputStyle ?? {},
2536
2578
  placeholder: placeholder ?? ""
2537
2579
  }
@@ -2539,7 +2581,7 @@ function TextInput({
2539
2581
  ]
2540
2582
  }
2541
2583
  ),
2542
- /* @__PURE__ */ jsx("div", { className: "text-center text-error dark:text-prussian-blue min-h-0", children: errorMessage })
2584
+ hasError && /* @__PURE__ */ jsx("div", { id: errorId, className: "text-center text-error dark:text-prussian-blue min-h-0", children: errorMessage })
2543
2585
  ] });
2544
2586
  }
2545
2587
  function NumberInput({
@@ -2655,6 +2697,8 @@ function Password({
2655
2697
  }) {
2656
2698
  const [passwordVisible, setPasswordVisible] = useState(false);
2657
2699
  const color = iconColor ?? colors_default.PALETTE["prussian-blue"];
2700
+ const errorId = useId();
2701
+ const hasError = errorMessage != null;
2658
2702
  return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center justify-center", style: style ?? {}, children: [
2659
2703
  /* @__PURE__ */ jsxs("div", { className: `flex ${layout === "vertical" ? "flex-col" : "flex-row items-center gap-2"}`, children: [
2660
2704
  label && // Render <label> only when a label is provided. An empty
@@ -2681,7 +2725,9 @@ function Password({
2681
2725
  type: passwordVisible ? "text" : "password",
2682
2726
  name,
2683
2727
  id: htmlFor,
2684
- className: `${errorMessage !== void 0 ? "border border-error" : ""} focus:outline-oxford-blue-700-opaque p-2 h-9 w-52 outline-offset-2 text-prussian-blue mt-1 rounded-lg disabled:bg-disabled disabled:cursor-not-allowed transition-all`,
2728
+ "aria-invalid": hasError || void 0,
2729
+ "aria-describedby": hasError ? errorId : void 0,
2730
+ className: `${hasError ? "border border-error" : ""} focus:outline-oxford-blue-700-opaque p-2 h-9 w-52 outline-offset-2 text-prussian-blue mt-1 rounded-lg disabled:bg-disabled disabled:cursor-not-allowed transition-all`,
2685
2731
  style: inputStyle ?? {},
2686
2732
  placeholder: placeholder ?? ""
2687
2733
  }
@@ -2711,7 +2757,7 @@ function Password({
2711
2757
  )
2712
2758
  ] })
2713
2759
  ] }),
2714
- /* @__PURE__ */ jsx("div", { className: "text-center text-error dark:text-prussian-blue min-h-0", children: errorMessage })
2760
+ hasError && /* @__PURE__ */ jsx("div", { id: errorId, className: "text-center text-error dark:text-prussian-blue min-h-0", children: errorMessage })
2715
2761
  ] });
2716
2762
  }
2717
2763
  function Checkbox({
@@ -2878,23 +2924,36 @@ function AutoComplete({
2878
2924
  sideOffset: 4,
2879
2925
  onOpenAutoFocus: (e) => e.preventDefault(),
2880
2926
  className: "w-64 bg-ice dark:bg-midnight-green-eagle-900 rounded-lg mt-1 shadow-md z-50 overflow-y-auto max-h-36 animate-in fade-in-0 zoom-in-95",
2881
- children: foundItems.length === 0 ? /* @__PURE__ */ jsx("div", { className: "h-full w-full flex flex-col items-center justify-center py-4 text-sm text-prussian-blue dark:text-white", children: emptyText }) : /* @__PURE__ */ jsx("div", { role: "listbox", children: foundItems.map((item) => /* @__PURE__ */ jsxs(
2882
- "div",
2883
- {
2884
- role: "option",
2885
- className: "text-sm flex items-center gap-2 p-2 transition-all duration-150 hover:bg-ice-dark dark:hover:bg-prussian-blue cursor-pointer text-prussian-blue dark:text-white",
2886
- onClick: () => handleSelect(item),
2887
- children: [
2888
- item.icon,
2889
- /* @__PURE__ */ jsxs("span", { children: [
2890
- item.label,
2891
- " (",
2892
- item.value,
2893
- ")"
2894
- ] })
2895
- ]
2896
- },
2897
- item.key
2927
+ children: foundItems.length === 0 ? /* @__PURE__ */ jsx("div", { className: "h-full w-full flex flex-col items-center justify-center py-4 text-sm text-prussian-blue dark:text-white", children: emptyText }) : /* @__PURE__ */ jsx("div", { role: "listbox", children: foundItems.map((item) => (
2928
+ // tabIndex + Enter/Space onKeyDown
2929
+ // makes each option keyboard-activatable.
2930
+ // Full roving-tabindex / arrow-key nav
2931
+ // is deferred to the Phase-5 rewrite.
2932
+ /* @__PURE__ */ jsxs(
2933
+ "div",
2934
+ {
2935
+ role: "option",
2936
+ tabIndex: 0,
2937
+ className: "text-sm flex items-center gap-2 p-2 transition-all duration-150 hover:bg-ice-dark dark:hover:bg-prussian-blue cursor-pointer text-prussian-blue dark:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
2938
+ onClick: () => handleSelect(item),
2939
+ onKeyDown: (e) => {
2940
+ if (e.key === "Enter" || e.key === " ") {
2941
+ e.preventDefault();
2942
+ handleSelect(item);
2943
+ }
2944
+ },
2945
+ children: [
2946
+ item.icon,
2947
+ /* @__PURE__ */ jsxs("span", { children: [
2948
+ item.label,
2949
+ " (",
2950
+ item.value,
2951
+ ")"
2952
+ ] })
2953
+ ]
2954
+ },
2955
+ item.key
2956
+ )
2898
2957
  )) })
2899
2958
  }
2900
2959
  ) })