@fabio.caffarello/react-design-system 3.12.0 → 4.0.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.
Files changed (159) hide show
  1. package/dist/granular/index.js +393 -389
  2. package/dist/granular/index.js.map +1 -1
  3. package/dist/granular/ui/components/Autocomplete/Autocomplete.js +103 -86
  4. package/dist/granular/ui/components/Autocomplete/Autocomplete.js.map +1 -1
  5. package/dist/granular/ui/components/Autocomplete/AutocompleteList.js +57 -47
  6. package/dist/granular/ui/components/Autocomplete/AutocompleteList.js.map +1 -1
  7. package/dist/granular/ui/components/Autocomplete/AutocompleteOption.js +21 -20
  8. package/dist/granular/ui/components/Autocomplete/AutocompleteOption.js.map +1 -1
  9. package/dist/granular/ui/components/Breadcrumb/Breadcrumb.js.map +1 -1
  10. package/dist/granular/ui/components/ButtonGroup/ButtonGroup.js +68 -0
  11. package/dist/granular/ui/components/ButtonGroup/ButtonGroup.js.map +1 -0
  12. package/dist/granular/ui/components/ColorPicker/ColorPicker.js.map +1 -1
  13. package/dist/granular/ui/components/CommandPalette/CommandPalette.js +187 -149
  14. package/dist/granular/ui/components/CommandPalette/CommandPalette.js.map +1 -1
  15. package/dist/granular/ui/components/DataGrid/DataGrid.js +92 -92
  16. package/dist/granular/ui/components/DataGrid/DataGrid.js.map +1 -1
  17. package/dist/granular/ui/components/DatePicker/DatePickerCalendar.js +154 -139
  18. package/dist/granular/ui/components/DatePicker/DatePickerCalendar.js.map +1 -1
  19. package/dist/granular/ui/components/Dialog/AlertDialog.js +73 -40
  20. package/dist/granular/ui/components/Dialog/AlertDialog.js.map +1 -1
  21. package/dist/granular/ui/components/Dialog/DialogContent.js +54 -48
  22. package/dist/granular/ui/components/Dialog/DialogContent.js.map +1 -1
  23. package/dist/granular/ui/components/Dialog/DialogDescription.js +31 -31
  24. package/dist/granular/ui/components/Dialog/DialogDescription.js.map +1 -1
  25. package/dist/granular/ui/components/Dialog/DialogTitle.js +30 -30
  26. package/dist/granular/ui/components/Dialog/DialogTitle.js.map +1 -1
  27. package/dist/granular/ui/components/Drawer/Drawer.js.map +1 -1
  28. package/dist/granular/ui/components/Dropdown/Dropdown.js.map +1 -1
  29. package/dist/granular/ui/components/EmptyState/EmptyState.js.map +1 -1
  30. package/dist/granular/ui/components/FileUpload/FileUpload.js.map +1 -1
  31. package/dist/granular/ui/components/Form/Form.js +38 -37
  32. package/dist/granular/ui/components/Form/Form.js.map +1 -1
  33. package/dist/granular/ui/components/Form/FormField.js +28 -26
  34. package/dist/granular/ui/components/Form/FormField.js.map +1 -1
  35. package/dist/granular/ui/components/Header/Header.js.map +1 -1
  36. package/dist/granular/ui/components/Header/components/HeaderActions.js.map +1 -1
  37. package/dist/granular/ui/components/Header/components/HeaderHamburger.js.map +1 -1
  38. package/dist/granular/ui/components/Header/components/HeaderLogo.js.map +1 -1
  39. package/dist/granular/ui/components/Header/components/HeaderMobileMenu.js.map +1 -1
  40. package/dist/granular/ui/components/Header/components/HeaderNavigation.js.map +1 -1
  41. package/dist/granular/ui/components/Header/contexts/HeaderContext.js.map +1 -1
  42. package/dist/granular/ui/components/Menu/Menu.js.map +1 -1
  43. package/dist/granular/ui/components/Modal/Modal.js +98 -86
  44. package/dist/granular/ui/components/Modal/Modal.js.map +1 -1
  45. package/dist/granular/ui/components/MultiSelect/MultiSelect.js +122 -106
  46. package/dist/granular/ui/components/MultiSelect/MultiSelect.js.map +1 -1
  47. package/dist/granular/ui/components/Navigation/Navigation.js.map +1 -1
  48. package/dist/granular/ui/components/PageHeader/PageHeader.js.map +1 -1
  49. package/dist/granular/ui/components/Pagination/Pagination.js.map +1 -1
  50. package/dist/granular/ui/components/Popover/Popover.js.map +1 -1
  51. package/dist/granular/ui/components/Rating/Rating.js.map +1 -1
  52. package/dist/granular/ui/components/SearchInput/SearchInput.js.map +1 -1
  53. package/dist/granular/ui/components/SideNavbar/components/Navbar/NavbarGroup.js +82 -64
  54. package/dist/granular/ui/components/SideNavbar/components/Navbar/NavbarGroup.js.map +1 -1
  55. package/dist/granular/ui/components/SideNavbar/components/Navbar/NavbarItem.js +30 -29
  56. package/dist/granular/ui/components/SideNavbar/components/Navbar/NavbarItem.js.map +1 -1
  57. package/dist/granular/ui/components/SideNavbar/components/SideNavbarResizeHandle.js +37 -35
  58. package/dist/granular/ui/components/SideNavbar/components/SideNavbarResizeHandle.js.map +1 -1
  59. package/dist/granular/ui/components/SideNavbar/providers/SideNavbarStateProvider.js +57 -57
  60. package/dist/granular/ui/components/SideNavbar/providers/SideNavbarStateProvider.js.map +1 -1
  61. package/dist/granular/ui/components/Stepper/Stepper.js +102 -94
  62. package/dist/granular/ui/components/Stepper/Stepper.js.map +1 -1
  63. package/dist/granular/ui/components/Table/Table.js +41 -35
  64. package/dist/granular/ui/components/Table/Table.js.map +1 -1
  65. package/dist/granular/ui/components/Table/TableActions/TableActions.js.map +1 -1
  66. package/dist/granular/ui/components/Table/TableFilters/TableFilters.js +49 -46
  67. package/dist/granular/ui/components/Table/TableFilters/TableFilters.js.map +1 -1
  68. package/dist/granular/ui/components/Table/TablePagination/TablePagination.js.map +1 -1
  69. package/dist/granular/ui/components/Table/TableProvider.js +82 -80
  70. package/dist/granular/ui/components/Table/TableProvider.js.map +1 -1
  71. package/dist/granular/ui/components/Table/TableRow.js +57 -53
  72. package/dist/granular/ui/components/Table/TableRow.js.map +1 -1
  73. package/dist/granular/ui/components/Table/useColumnResizing.js +53 -53
  74. package/dist/granular/ui/components/Table/useColumnResizing.js.map +1 -1
  75. package/dist/granular/ui/components/TimePicker/TimePicker.js +149 -103
  76. package/dist/granular/ui/components/TimePicker/TimePicker.js.map +1 -1
  77. package/dist/granular/ui/components/Timeline/Timeline.js.map +1 -1
  78. package/dist/granular/ui/hooks/useFocusRestore.js +14 -15
  79. package/dist/granular/ui/hooks/useFocusRestore.js.map +1 -1
  80. package/dist/granular/ui/primitives/Badge/Badge.js.map +1 -1
  81. package/dist/granular/ui/primitives/Checkbox/Checkbox.js.map +1 -1
  82. package/dist/granular/ui/primitives/Chip/Chip.js +91 -71
  83. package/dist/granular/ui/primitives/Chip/Chip.js.map +1 -1
  84. package/dist/granular/ui/primitives/Dot/Dot.js +99 -0
  85. package/dist/granular/ui/primitives/Dot/Dot.js.map +1 -0
  86. package/dist/granular/ui/primitives/ErrorMessage/ErrorMessage.js.map +1 -1
  87. package/dist/granular/ui/primitives/Input/Input.js.map +1 -1
  88. package/dist/granular/ui/primitives/Label/Label.js.map +1 -1
  89. package/dist/granular/ui/primitives/NavLink/NavLink.js.map +1 -1
  90. package/dist/granular/ui/primitives/Radio/Radio.js.map +1 -1
  91. package/dist/granular/ui/primitives/Select/Select.js.map +1 -1
  92. package/dist/granular/ui/primitives/Separator/Separator.js.map +1 -1
  93. package/dist/granular/ui/primitives/Skeleton/Skeleton.js.map +1 -1
  94. package/dist/granular/ui/primitives/Slider/Slider.js.map +1 -1
  95. package/dist/granular/ui/primitives/Spinner/Spinner.js.map +1 -1
  96. package/dist/granular/ui/primitives/Switch/Switch.js.map +1 -1
  97. package/dist/granular/ui/primitives/Tooltip/Tooltip.js.map +1 -1
  98. package/dist/granular/ui/providers/DialogContext.js.map +1 -1
  99. package/dist/granular/ui/providers/DialogProvider.js +24 -20
  100. package/dist/granular/ui/providers/DialogProvider.js.map +1 -1
  101. package/dist/index.cjs +134 -134
  102. package/dist/index.cjs.map +1 -1
  103. package/dist/index.js +5945 -5542
  104. package/dist/index.js.map +1 -1
  105. package/dist/react-design-system.css +1 -1
  106. package/dist/server/index.cjs +7 -7
  107. package/dist/server/index.cjs.map +1 -1
  108. package/dist/server/index.js +404 -384
  109. package/dist/server/index.js.map +1 -1
  110. package/dist/ui/components/Autocomplete/AutocompleteList.d.ts +4 -0
  111. package/dist/ui/components/Autocomplete/AutocompleteOption.d.ts +8 -0
  112. package/dist/ui/components/Breadcrumb/Breadcrumb.d.ts +0 -1
  113. package/dist/ui/components/ButtonGroup/ButtonGroup.d.ts +2 -2
  114. package/dist/ui/components/ColorPicker/ColorPicker.d.ts +0 -1
  115. package/dist/ui/components/CommandPalette/CommandPalette.d.ts +0 -1
  116. package/dist/ui/components/DataGrid/DataGrid.d.ts +0 -1
  117. package/dist/ui/components/Dialog/DialogContent.d.ts +20 -1
  118. package/dist/ui/components/Drawer/Drawer.d.ts +0 -1
  119. package/dist/ui/components/Dropdown/Dropdown.d.ts +0 -1
  120. package/dist/ui/components/EmptyState/EmptyState.d.ts +0 -1
  121. package/dist/ui/components/FileUpload/FileUpload.d.ts +0 -1
  122. package/dist/ui/components/Form/FormField.d.ts +7 -0
  123. package/dist/ui/components/Header/Header.d.ts +1 -1
  124. package/dist/ui/components/Header/components/HeaderActions.d.ts +1 -1
  125. package/dist/ui/components/Header/components/HeaderHamburger.d.ts +1 -1
  126. package/dist/ui/components/Header/components/HeaderLogo.d.ts +1 -1
  127. package/dist/ui/components/Header/components/HeaderMobileMenu.d.ts +1 -1
  128. package/dist/ui/components/Header/components/HeaderNavigation.d.ts +1 -1
  129. package/dist/ui/components/Header/contexts/HeaderContext.d.ts +1 -1
  130. package/dist/ui/components/Menu/Menu.d.ts +0 -1
  131. package/dist/ui/components/Modal/Modal.d.ts +1 -2
  132. package/dist/ui/components/Navigation/Navigation.d.ts +1 -1
  133. package/dist/ui/components/PageHeader/PageHeader.d.ts +1 -1
  134. package/dist/ui/components/Pagination/Pagination.d.ts +0 -1
  135. package/dist/ui/components/Popover/Popover.d.ts +0 -1
  136. package/dist/ui/components/Rating/Rating.d.ts +0 -1
  137. package/dist/ui/components/SearchInput/SearchInput.d.ts +0 -1
  138. package/dist/ui/components/Stepper/Stepper.d.ts +0 -1
  139. package/dist/ui/components/Table/TableActions/TableActions.d.ts +0 -1
  140. package/dist/ui/components/Table/TableFilters/TableFilters.d.ts +0 -1
  141. package/dist/ui/components/Table/TablePagination/TablePagination.d.ts +0 -1
  142. package/dist/ui/components/TimePicker/TimePicker.d.ts +0 -1
  143. package/dist/ui/components/Timeline/Timeline.d.ts +0 -1
  144. package/dist/ui/components/index.d.ts +2 -0
  145. package/dist/ui/primitives/Checkbox/Checkbox.d.ts +0 -1
  146. package/dist/ui/primitives/Chip/Chip.d.ts +21 -0
  147. package/dist/ui/primitives/ErrorMessage/ErrorMessage.d.ts +0 -1
  148. package/dist/ui/primitives/Input/Input.d.ts +0 -1
  149. package/dist/ui/primitives/Label/Label.d.ts +0 -1
  150. package/dist/ui/primitives/NavLink/NavLink.d.ts +1 -1
  151. package/dist/ui/primitives/Radio/Radio.d.ts +0 -1
  152. package/dist/ui/primitives/Select/Select.d.ts +0 -1
  153. package/dist/ui/primitives/Skeleton/Skeleton.d.ts +0 -1
  154. package/dist/ui/primitives/Slider/Slider.d.ts +0 -1
  155. package/dist/ui/primitives/Switch/Switch.d.ts +0 -1
  156. package/dist/ui/primitives/Tooltip/Tooltip.d.ts +0 -1
  157. package/dist/ui/primitives/index.d.ts +2 -0
  158. package/dist/ui/providers/DialogContext.d.ts +8 -0
  159. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,133 +1,150 @@
1
1
  "use client";
2
- import { jsxs as Y, jsx as h } from "react/jsx-runtime";
3
- import { forwardRef as Z, useId as _, useState as w, useRef as p, useEffect as v } from "react";
4
- import M from "../../primitives/Input/Input.js";
5
- import { Loader2 as C, ChevronDown as B } from "lucide-react";
6
- import P from "./AutocompleteList.js";
7
- const ee = Z(
2
+ import { jsxs as ee, jsx as x } from "react/jsx-runtime";
3
+ import { forwardRef as te, useId as re, useState as g, useRef as k, useEffect as d } from "react";
4
+ import ne from "../../primitives/Input/Input.js";
5
+ import { Loader2 as oe, ChevronDown as se } from "lucide-react";
6
+ import ie from "./AutocompleteList.js";
7
+ import { mergeRefs as le } from "../../utils/mergeRefs.js";
8
+ const ae = te(
8
9
  function({
9
- options: u,
10
- value: O,
11
- defaultValue: R,
10
+ options: a,
11
+ value: $,
12
+ defaultValue: F,
12
13
  onChange: s,
13
- onSelect: b,
14
- placeholder: K = "Type to search...",
15
- loading: I = !1,
16
- disabled: L = !1,
17
- emptyMessage: V = "No options found",
18
- debounceMs: S = 300,
19
- filterOptions: g,
20
- className: $ = "",
21
- inputClassName: q = "",
22
- size: H = "md",
23
- label: k,
24
- "aria-label": y,
25
- "aria-labelledby": i,
26
- id: D
27
- }, U) {
28
- const z = _(), x = D != null ? D : z, [G, T] = w(
29
- typeof R == "string" ? R : ""
30
- ), [c, l] = w(!1), [r, o] = w(-1), [f, a] = w(""), d = p(null), F = p(null), A = p(null), E = p(null), N = O !== void 0, m = N ? O : G, n = f.trim() ? g ? g(u, f) : u.filter(
31
- (e) => e.label.toLowerCase().includes(f.toLowerCase())
32
- ) : u, J = n.length > 0, Q = (e) => {
14
+ onSelect: y,
15
+ placeholder: z = "Type to search...",
16
+ loading: D = !1,
17
+ disabled: G = !1,
18
+ emptyMessage: j = "No options found",
19
+ debounceMs: J = 300,
20
+ filterOptions: H,
21
+ className: Q = "",
22
+ inputClassName: W = "",
23
+ size: X = "md",
24
+ label: A,
25
+ "aria-label": O,
26
+ "aria-labelledby": m,
27
+ id: R
28
+ }, Y) {
29
+ const Z = re(), E = R != null ? R : Z, N = `${E}-listbox`, [_, K] = g(
30
+ typeof F == "string" ? F : ""
31
+ ), [c, i] = g(!1), [r, l] = g(-1), [p, u] = g(""), h = k(null), L = k(null), v = k(null), f = k(null), V = $ !== void 0, w = V ? $ : _, n = p.trim() ? H ? H(a, p) : a.filter(
32
+ (e) => e.label.toLowerCase().includes(p.toLowerCase())
33
+ ) : a, M = n.length > 0, B = (e) => {
33
34
  const t = e.target.value;
34
- a(t), N || T(t), s == null || s(t), E.current && clearTimeout(E.current), E.current = setTimeout(() => {
35
- l(!0), o(-1);
36
- }, S);
37
- }, j = (e) => {
35
+ u(t), V || K(t), s == null || s(t), f.current && clearTimeout(f.current), f.current = setTimeout(() => {
36
+ i(!0), l(-1);
37
+ }, J);
38
+ }, S = (e) => {
38
39
  var t;
39
- e.disabled || (N || T(e.value), a(e.label), l(!1), o(-1), s == null || s(e.value), b == null || b(e), (t = F.current) == null || t.focus());
40
- }, W = (e) => {
40
+ e.disabled || (V || K(e.value), u(e.label), i(!1), l(-1), s == null || s(e.value), y == null || y(e), (t = L.current) == null || t.focus());
41
+ }, q = (e, t) => {
42
+ const o = n.length;
43
+ if (o === 0) return -1;
44
+ const I = e < 0 ? t === 1 ? -1 : 0 : e;
45
+ for (let T = 1; T <= o; T++) {
46
+ const U = ((I + t * T) % o + o) % o;
47
+ if (!n[U].disabled) return U;
48
+ }
49
+ return e;
50
+ }, C = (e) => {
41
51
  if (!c || n.length === 0) {
42
- (e.key === "ArrowDown" || e.key === "Enter") && l(!0);
52
+ (e.key === "ArrowDown" || e.key === "Enter") && i(!0);
43
53
  return;
44
54
  }
45
55
  switch (e.key) {
46
56
  case "ArrowDown":
47
- e.preventDefault(), o(
48
- (t) => t < n.length - 1 ? t + 1 : 0
49
- );
57
+ e.preventDefault(), l((t) => q(t, 1));
50
58
  break;
51
59
  case "ArrowUp":
52
- e.preventDefault(), o(
53
- (t) => t > 0 ? t - 1 : n.length - 1
54
- );
60
+ e.preventDefault(), l((t) => q(t, -1));
55
61
  break;
56
62
  case "Enter":
57
- e.preventDefault(), r >= 0 && r < n.length && j(n[r]);
63
+ e.preventDefault(), r >= 0 && r < n.length && S(n[r]);
58
64
  break;
59
65
  case "Escape":
60
- e.preventDefault(), l(!1), o(-1);
66
+ e.preventDefault(), i(!1), l(-1);
61
67
  break;
62
68
  }
63
69
  };
64
- v(() => {
70
+ d(() => {
65
71
  if (!c) return;
66
72
  const e = (t) => {
67
- d.current && !d.current.contains(t.target) && (l(!1), o(-1));
73
+ var I;
74
+ const o = t.target;
75
+ h.current && !h.current.contains(o) && !((I = v.current) != null && I.contains(o)) && (i(!1), l(-1));
68
76
  };
69
77
  return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
70
- }, [c]), v(() => {
71
- if (r >= 0 && A.current) {
72
- const e = A.current.querySelectorAll('[role="option"]');
78
+ }, [c]), d(() => () => {
79
+ f.current && clearTimeout(f.current);
80
+ }, []), d(() => {
81
+ if (r >= 0 && v.current) {
82
+ const e = v.current.querySelectorAll('[role="option"]');
73
83
  e[r] && typeof e[r].scrollIntoView == "function" && e[r].scrollIntoView({
74
84
  block: "nearest",
75
85
  behavior: "smooth"
76
86
  });
77
87
  }
78
- }, [r]), v(() => {
79
- if (m) {
80
- const e = u.find((t) => t.value === m);
81
- a(e ? e.label : m);
88
+ }, [r]), d(() => {
89
+ if (w) {
90
+ const e = a.find((t) => t.value === w);
91
+ u(e ? e.label : w);
82
92
  } else
83
- a("");
84
- }, [m, u]), v(() => {
85
- }, [k, y, i, x]);
86
- const X = c && (J || I || V);
87
- return /* @__PURE__ */ Y("div", { ref: d, className: `relative ${$}`, children: [
88
- /* @__PURE__ */ h(
89
- M,
93
+ u("");
94
+ }, [w, a]), d(() => {
95
+ }, [A, O, m, E]);
96
+ const b = c && (M || D || !!j), P = b && r >= 0 && r < n.length ? `${N}-option-${r}` : void 0;
97
+ return /* @__PURE__ */ ee("div", { ref: h, className: `relative ${Q}`, children: [
98
+ /* @__PURE__ */ x(
99
+ ne,
90
100
  {
91
- ref: F || U,
92
- id: x,
93
- label: k,
94
- "aria-label": y,
95
- "aria-labelledby": i,
96
- value: f,
97
- onChange: Q,
98
- onKeyDown: W,
99
- onFocus: () => l(!0),
100
- placeholder: K,
101
- disabled: L,
102
- size: H,
103
- rightIcon: I ? /* @__PURE__ */ h(C, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ h(
104
- B,
101
+ ref: le(L, Y),
102
+ id: E,
103
+ label: A,
104
+ "aria-label": O,
105
+ "aria-labelledby": m,
106
+ role: "combobox",
107
+ "aria-expanded": b,
108
+ "aria-controls": b ? N : void 0,
109
+ "aria-haspopup": "listbox",
110
+ "aria-autocomplete": "list",
111
+ "aria-activedescendant": P,
112
+ value: p,
113
+ onChange: B,
114
+ onKeyDown: C,
115
+ onFocus: () => i(!0),
116
+ placeholder: z,
117
+ disabled: G,
118
+ size: X,
119
+ rightIcon: D ? /* @__PURE__ */ x(oe, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ x(
120
+ se,
105
121
  {
106
122
  className: `h-4 w-4 transition-transform ${c ? "rotate-180" : ""}`
107
123
  }
108
124
  ),
109
- className: q
125
+ className: W
110
126
  }
111
127
  ),
112
- X && /* @__PURE__ */ h(
113
- P,
128
+ b && /* @__PURE__ */ x(
129
+ ie,
114
130
  {
115
- ref: A,
131
+ ref: v,
132
+ id: N,
116
133
  options: n,
117
134
  highlightedIndex: r,
118
- onSelect: j,
119
- loading: I,
120
- emptyMessage: V,
121
- containerRef: d,
122
- "aria-labelledby": i,
123
- "aria-label": i ? void 0 : y || k
135
+ onSelect: S,
136
+ loading: D,
137
+ emptyMessage: j,
138
+ containerRef: h,
139
+ "aria-labelledby": m,
140
+ "aria-label": m ? void 0 : O || A
124
141
  }
125
142
  )
126
143
  ] });
127
144
  }
128
145
  );
129
- ee.displayName = "Autocomplete";
146
+ ae.displayName = "Autocomplete";
130
147
  export {
131
- ee as default
148
+ ae as default
132
149
  };
133
150
  //# sourceMappingURL=Autocomplete.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Autocomplete.js","sources":["../../../../../src/ui/components/Autocomplete/Autocomplete.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useRef, useEffect, useId, forwardRef } from \"react\";\nimport Input from \"../../primitives/Input/Input\";\nimport { ChevronDown, Loader2 } from \"lucide-react\";\nimport AutocompleteList from \"./AutocompleteList\";\nimport type { AutocompleteOptionType } from \"./AutocompleteOption\";\n\nexport interface AutocompleteProps {\n options: AutocompleteOptionType[];\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n onSelect?: (option: AutocompleteOptionType) => void;\n placeholder?: string;\n loading?: boolean;\n disabled?: boolean;\n emptyMessage?: string;\n debounceMs?: number;\n filterOptions?: (\n options: AutocompleteOptionType[],\n searchValue: string,\n ) => AutocompleteOptionType[];\n className?: string;\n inputClassName?: string;\n size?: \"sm\" | \"md\" | \"lg\";\n /**\n * Visible label rendered above the input via the `Input` primitive's\n * `label` API, which wires `<label htmlFor>` to the inner `<input>`.\n * Provides the accessible name axe `aria-input-field-name` requires.\n */\n label?: string;\n /**\n * Invisible accessible name. Use when the input has no visible label\n * (e.g. a tightly-packed toolbar combobox). One of `label`,\n * `aria-label`, or `aria-labelledby` MUST be present — without any,\n * the input has no programmatic name and axe `aria-input-field-name`\n * (serious) flags it. A dev-only warning fires when all are missing.\n */\n \"aria-label\"?: string;\n /**\n * Override path: point at an id the consumer manages externally.\n */\n \"aria-labelledby\"?: string;\n id?: string;\n}\n\n/**\n * Autocomplete Component\n *\n * An input component with autocomplete suggestions.\n * Supports keyboard navigation, loading states, and custom filtering.\n *\n * @example\n * ```tsx\n * <Autocomplete\n * options={[\n * { value: '1', label: 'Option 1' },\n * { value: '2', label: 'Option 2' },\n * ]}\n * onSelect={(option) => console.log(option)}\n * />\n * ```\n */\nconst Autocomplete = forwardRef<HTMLInputElement, AutocompleteProps>(\n function Autocomplete(\n {\n options,\n value: controlledValue,\n defaultValue,\n onChange,\n onSelect,\n placeholder = \"Type to search...\",\n loading = false,\n disabled = false,\n emptyMessage = \"No options found\",\n debounceMs = 300,\n filterOptions,\n className = \"\",\n inputClassName = \"\",\n size = \"md\",\n label,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n id: idProp,\n },\n ref,\n ) {\n const autoId = useId();\n const inputId = idProp ?? autoId;\n const [internalValue, setInternalValue] = useState<string>(\n typeof defaultValue === \"string\" ? defaultValue : \"\",\n );\n const [isOpen, setIsOpen] = useState(false);\n const [highlightedIndex, setHighlightedIndex] = useState(-1);\n const [searchValue, setSearchValue] = useState(\"\");\n const containerRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const isControlled = controlledValue !== undefined;\n const currentValue = isControlled ? controlledValue : internalValue;\n\n // Filter options\n const getFilteredOptions = (): AutocompleteOptionType[] => {\n if (!searchValue.trim()) {\n return options;\n }\n\n if (filterOptions) {\n return filterOptions(options, searchValue);\n }\n\n // Default filter: case-insensitive search in label\n return options.filter((option) =>\n option.label.toLowerCase().includes(searchValue.toLowerCase()),\n );\n };\n\n const filteredOptions = getFilteredOptions();\n const hasOptions = filteredOptions.length > 0;\n\n // Handle input change\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n setSearchValue(newValue);\n\n if (!isControlled) {\n setInternalValue(newValue);\n }\n\n onChange?.(newValue);\n\n // Debounce search\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n }\n\n debounceTimerRef.current = setTimeout(() => {\n setIsOpen(true);\n setHighlightedIndex(-1);\n }, debounceMs);\n };\n\n // Handle option select\n const handleSelect = (option: AutocompleteOptionType) => {\n if (option.disabled) return;\n\n if (!isControlled) {\n setInternalValue(option.value);\n }\n\n setSearchValue(option.label);\n setIsOpen(false);\n setHighlightedIndex(-1);\n onChange?.(option.value);\n onSelect?.(option);\n inputRef.current?.focus();\n };\n\n // Handle keyboard navigation\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (!isOpen || filteredOptions.length === 0) {\n if (e.key === \"ArrowDown\" || e.key === \"Enter\") {\n setIsOpen(true);\n }\n return;\n }\n\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n setHighlightedIndex((prev) =>\n prev < filteredOptions.length - 1 ? prev + 1 : 0,\n );\n break;\n case \"ArrowUp\":\n e.preventDefault();\n setHighlightedIndex((prev) =>\n prev > 0 ? prev - 1 : filteredOptions.length - 1,\n );\n break;\n case \"Enter\":\n e.preventDefault();\n if (\n highlightedIndex >= 0 &&\n highlightedIndex < filteredOptions.length\n ) {\n handleSelect(filteredOptions[highlightedIndex]);\n }\n break;\n case \"Escape\":\n e.preventDefault();\n setIsOpen(false);\n setHighlightedIndex(-1);\n break;\n }\n };\n\n // Close on click outside\n useEffect(() => {\n if (!isOpen) return;\n\n const handleClickOutside = (e: MouseEvent) => {\n if (\n containerRef.current &&\n !containerRef.current.contains(e.target as Node)\n ) {\n setIsOpen(false);\n setHighlightedIndex(-1);\n }\n };\n\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () =>\n document.removeEventListener(\"mousedown\", handleClickOutside);\n }, [isOpen]);\n\n // Scroll highlighted item into view\n useEffect(() => {\n if (highlightedIndex >= 0 && listRef.current) {\n const items = listRef.current.querySelectorAll('[role=\"option\"]');\n if (\n items[highlightedIndex] &&\n typeof items[highlightedIndex].scrollIntoView === \"function\"\n ) {\n items[highlightedIndex].scrollIntoView({\n block: \"nearest\",\n behavior: \"smooth\",\n });\n }\n }\n }, [highlightedIndex]);\n\n // Sync search value with current value\n useEffect(() => {\n if (currentValue) {\n const option = options.find((opt) => opt.value === currentValue);\n if (option) {\n setSearchValue(option.label);\n } else {\n setSearchValue(currentValue);\n }\n } else {\n setSearchValue(\"\");\n }\n }, [currentValue, options]);\n\n // Dev-only accessible-name warning. Same four-path shape as the\n // Textarea 6c guard: label / aria-label / aria-labelledby / external\n // `<label htmlFor={id}>`. Without any source, axe\n // `aria-input-field-name` (serious) flags the input. Silent in\n // production.\n useEffect(() => {\n if (!import.meta.env.DEV) return;\n if (label || ariaLabel || ariaLabelledBy) return;\n const externalLabel =\n typeof document !== \"undefined\"\n ? document.querySelector(`label[for=\"${CSS.escape(inputId)}\"]`)\n : null;\n if (externalLabel) return;\n console.warn(\n \"[Autocomplete] Missing accessible name. Provide a `label` prop, `aria-label`, `aria-labelledby`, or pair an external `<label htmlFor={id}>` with the same `id`.\",\n );\n }, [label, ariaLabel, ariaLabelledBy, inputId]);\n\n const shouldShowList = isOpen && (hasOptions || loading || emptyMessage);\n\n return (\n <div ref={containerRef} className={`relative ${className}`}>\n <Input\n ref={inputRef || ref}\n id={inputId}\n label={label}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n value={searchValue}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n onFocus={() => setIsOpen(true)}\n placeholder={placeholder}\n disabled={disabled}\n size={size}\n rightIcon={\n loading ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <ChevronDown\n className={`h-4 w-4 transition-transform ${\n isOpen ? \"rotate-180\" : \"\"\n }`}\n />\n )\n }\n className={inputClassName}\n />\n\n {shouldShowList && (\n <AutocompleteList\n ref={listRef}\n options={filteredOptions}\n highlightedIndex={highlightedIndex}\n onSelect={handleSelect}\n loading={loading}\n emptyMessage={emptyMessage}\n containerRef={containerRef}\n // Cascade the same accessible-name source the consumer\n // provided for the input to the listbox portal — keeps\n // axe `aria-input-field-name` satisfied on the listbox\n // without making the consumer re-state the name.\n aria-labelledby={ariaLabelledBy}\n aria-label={ariaLabelledBy ? undefined : ariaLabel || label}\n />\n )}\n </div>\n );\n },\n);\n\nAutocomplete.displayName = \"Autocomplete\";\n\nexport default Autocomplete;\n"],"names":["Autocomplete","forwardRef","options","controlledValue","defaultValue","onChange","onSelect","placeholder","loading","disabled","emptyMessage","debounceMs","filterOptions","className","inputClassName","size","label","ariaLabel","ariaLabelledBy","idProp","ref","autoId","useId","inputId","internalValue","setInternalValue","useState","isOpen","setIsOpen","highlightedIndex","setHighlightedIndex","searchValue","setSearchValue","containerRef","useRef","inputRef","listRef","debounceTimerRef","isControlled","currentValue","filteredOptions","option","hasOptions","handleInputChange","newValue","handleSelect","_a","handleKeyDown","prev","useEffect","handleClickOutside","e","items","opt","shouldShowList","jsx","Input","Loader2","ChevronDown","AutocompleteList"],"mappings":"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"}
1
+ {"version":3,"file":"Autocomplete.js","sources":["../../../../../src/ui/components/Autocomplete/Autocomplete.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useRef, useEffect, useId, forwardRef } from \"react\";\nimport Input from \"../../primitives/Input/Input\";\nimport { mergeRefs } from \"../../utils\";\nimport { ChevronDown, Loader2 } from \"lucide-react\";\nimport AutocompleteList from \"./AutocompleteList\";\nimport type { AutocompleteOptionType } from \"./AutocompleteOption\";\n\nexport interface AutocompleteProps {\n options: AutocompleteOptionType[];\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n onSelect?: (option: AutocompleteOptionType) => void;\n placeholder?: string;\n loading?: boolean;\n disabled?: boolean;\n emptyMessage?: string;\n debounceMs?: number;\n filterOptions?: (\n options: AutocompleteOptionType[],\n searchValue: string,\n ) => AutocompleteOptionType[];\n className?: string;\n inputClassName?: string;\n size?: \"sm\" | \"md\" | \"lg\";\n /**\n * Visible label rendered above the input via the `Input` primitive's\n * `label` API, which wires `<label htmlFor>` to the inner `<input>`.\n * Provides the accessible name axe `aria-input-field-name` requires.\n */\n label?: string;\n /**\n * Invisible accessible name. Use when the input has no visible label\n * (e.g. a tightly-packed toolbar combobox). One of `label`,\n * `aria-label`, or `aria-labelledby` MUST be present — without any,\n * the input has no programmatic name and axe `aria-input-field-name`\n * (serious) flags it. A dev-only warning fires when all are missing.\n */\n \"aria-label\"?: string;\n /**\n * Override path: point at an id the consumer manages externally.\n */\n \"aria-labelledby\"?: string;\n id?: string;\n}\n\n/**\n * Autocomplete Component\n *\n * An input component with autocomplete suggestions.\n * Supports keyboard navigation, loading states, and custom filtering.\n *\n * @example\n * ```tsx\n * <Autocomplete\n * options={[\n * { value: '1', label: 'Option 1' },\n * { value: '2', label: 'Option 2' },\n * ]}\n * onSelect={(option) => console.log(option)}\n * />\n * ```\n */\nconst Autocomplete = forwardRef<HTMLInputElement, AutocompleteProps>(\n function Autocomplete(\n {\n options,\n value: controlledValue,\n defaultValue,\n onChange,\n onSelect,\n placeholder = \"Type to search...\",\n loading = false,\n disabled = false,\n emptyMessage = \"No options found\",\n debounceMs = 300,\n filterOptions,\n className = \"\",\n inputClassName = \"\",\n size = \"md\",\n label,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n id: idProp,\n },\n ref,\n ) {\n const autoId = useId();\n const inputId = idProp ?? autoId;\n const listboxId = `${inputId}-listbox`;\n const [internalValue, setInternalValue] = useState<string>(\n typeof defaultValue === \"string\" ? defaultValue : \"\",\n );\n const [isOpen, setIsOpen] = useState(false);\n const [highlightedIndex, setHighlightedIndex] = useState(-1);\n const [searchValue, setSearchValue] = useState(\"\");\n const containerRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const isControlled = controlledValue !== undefined;\n const currentValue = isControlled ? controlledValue : internalValue;\n\n // Filter options\n const getFilteredOptions = (): AutocompleteOptionType[] => {\n if (!searchValue.trim()) {\n return options;\n }\n\n if (filterOptions) {\n return filterOptions(options, searchValue);\n }\n\n // Default filter: case-insensitive search in label\n return options.filter((option) =>\n option.label.toLowerCase().includes(searchValue.toLowerCase()),\n );\n };\n\n const filteredOptions = getFilteredOptions();\n const hasOptions = filteredOptions.length > 0;\n\n // Handle input change\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n setSearchValue(newValue);\n\n if (!isControlled) {\n setInternalValue(newValue);\n }\n\n onChange?.(newValue);\n\n // Debounce search\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n }\n\n debounceTimerRef.current = setTimeout(() => {\n setIsOpen(true);\n setHighlightedIndex(-1);\n }, debounceMs);\n };\n\n // Handle option select\n const handleSelect = (option: AutocompleteOptionType) => {\n if (option.disabled) return;\n\n if (!isControlled) {\n setInternalValue(option.value);\n }\n\n setSearchValue(option.label);\n setIsOpen(false);\n setHighlightedIndex(-1);\n onChange?.(option.value);\n onSelect?.(option);\n inputRef.current?.focus();\n };\n\n // Next/previous ENABLED option index, wrapping, skipping disabled\n // options (arrow nav previously landed on disabled options where\n // Enter then did nothing — a dead keypress).\n const moveHighlight = (prev: number, dir: 1 | -1): number => {\n const n = filteredOptions.length;\n if (n === 0) return -1;\n const start = prev < 0 ? (dir === 1 ? -1 : 0) : prev;\n for (let i = 1; i <= n; i++) {\n const idx = (((start + dir * i) % n) + n) % n;\n if (!filteredOptions[idx].disabled) return idx;\n }\n return prev;\n };\n\n // Handle keyboard navigation\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (!isOpen || filteredOptions.length === 0) {\n if (e.key === \"ArrowDown\" || e.key === \"Enter\") {\n setIsOpen(true);\n }\n return;\n }\n\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n setHighlightedIndex((prev) => moveHighlight(prev, 1));\n break;\n case \"ArrowUp\":\n e.preventDefault();\n setHighlightedIndex((prev) => moveHighlight(prev, -1));\n break;\n case \"Enter\":\n e.preventDefault();\n if (\n highlightedIndex >= 0 &&\n highlightedIndex < filteredOptions.length\n ) {\n handleSelect(filteredOptions[highlightedIndex]);\n }\n break;\n case \"Escape\":\n e.preventDefault();\n setIsOpen(false);\n setHighlightedIndex(-1);\n break;\n }\n };\n\n // Close on click outside\n useEffect(() => {\n if (!isOpen) return;\n\n const handleClickOutside = (e: MouseEvent) => {\n const target = e.target as Node;\n // The list is portalled to document.body, so it is NOT inside\n // containerRef — without the listRef check, a mousedown on an\n // option closed the list before its click fired, losing the\n // selection.\n if (\n containerRef.current &&\n !containerRef.current.contains(target) &&\n !listRef.current?.contains(target)\n ) {\n setIsOpen(false);\n setHighlightedIndex(-1);\n }\n };\n\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () =>\n document.removeEventListener(\"mousedown\", handleClickOutside);\n }, [isOpen]);\n\n // Clear any pending debounce timer on unmount (avoids a state update\n // on an unmounted component / a dangling timer).\n useEffect(() => {\n return () => {\n if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);\n };\n }, []);\n\n // Scroll highlighted item into view\n useEffect(() => {\n if (highlightedIndex >= 0 && listRef.current) {\n const items = listRef.current.querySelectorAll('[role=\"option\"]');\n if (\n items[highlightedIndex] &&\n typeof items[highlightedIndex].scrollIntoView === \"function\"\n ) {\n items[highlightedIndex].scrollIntoView({\n block: \"nearest\",\n behavior: \"smooth\",\n });\n }\n }\n }, [highlightedIndex]);\n\n // Sync search value with current value\n useEffect(() => {\n if (currentValue) {\n const option = options.find((opt) => opt.value === currentValue);\n if (option) {\n setSearchValue(option.label);\n } else {\n setSearchValue(currentValue);\n }\n } else {\n setSearchValue(\"\");\n }\n }, [currentValue, options]);\n\n // Dev-only accessible-name warning. Same four-path shape as the\n // Textarea 6c guard: label / aria-label / aria-labelledby / external\n // `<label htmlFor={id}>`. Without any source, axe\n // `aria-input-field-name` (serious) flags the input. Silent in\n // production.\n useEffect(() => {\n if (!import.meta.env.DEV) return;\n if (label || ariaLabel || ariaLabelledBy) return;\n const externalLabel =\n typeof document !== \"undefined\"\n ? document.querySelector(`label[for=\"${CSS.escape(inputId)}\"]`)\n : null;\n if (externalLabel) return;\n console.warn(\n \"[Autocomplete] Missing accessible name. Provide a `label` prop, `aria-label`, `aria-labelledby`, or pair an external `<label htmlFor={id}>` with the same `id`.\",\n );\n }, [label, ariaLabel, ariaLabelledBy, inputId]);\n\n const shouldShowList =\n isOpen && (hasOptions || loading || Boolean(emptyMessage));\n const activeOptionId =\n shouldShowList &&\n highlightedIndex >= 0 &&\n highlightedIndex < filteredOptions.length\n ? `${listboxId}-option-${highlightedIndex}`\n : undefined;\n\n return (\n <div ref={containerRef} className={`relative ${className}`}>\n <Input\n ref={mergeRefs(inputRef, ref)}\n id={inputId}\n label={label}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n role=\"combobox\"\n aria-expanded={shouldShowList}\n aria-controls={shouldShowList ? listboxId : undefined}\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n aria-activedescendant={activeOptionId}\n value={searchValue}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n onFocus={() => setIsOpen(true)}\n placeholder={placeholder}\n disabled={disabled}\n size={size}\n rightIcon={\n loading ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <ChevronDown\n className={`h-4 w-4 transition-transform ${\n isOpen ? \"rotate-180\" : \"\"\n }`}\n />\n )\n }\n className={inputClassName}\n />\n\n {shouldShowList && (\n <AutocompleteList\n ref={listRef}\n id={listboxId}\n options={filteredOptions}\n highlightedIndex={highlightedIndex}\n onSelect={handleSelect}\n loading={loading}\n emptyMessage={emptyMessage}\n containerRef={containerRef}\n // Cascade the same accessible-name source the consumer\n // provided for the input to the listbox portal — keeps\n // axe `aria-input-field-name` satisfied on the listbox\n // without making the consumer re-state the name.\n aria-labelledby={ariaLabelledBy}\n aria-label={ariaLabelledBy ? undefined : ariaLabel || label}\n />\n )}\n </div>\n );\n },\n);\n\nAutocomplete.displayName = \"Autocomplete\";\n\nexport default Autocomplete;\n"],"names":["Autocomplete","forwardRef","options","controlledValue","defaultValue","onChange","onSelect","placeholder","loading","disabled","emptyMessage","debounceMs","filterOptions","className","inputClassName","size","label","ariaLabel","ariaLabelledBy","idProp","ref","autoId","useId","inputId","listboxId","internalValue","setInternalValue","useState","isOpen","setIsOpen","highlightedIndex","setHighlightedIndex","searchValue","setSearchValue","containerRef","useRef","inputRef","listRef","debounceTimerRef","isControlled","currentValue","filteredOptions","option","hasOptions","handleInputChange","newValue","handleSelect","_a","moveHighlight","prev","dir","n","start","i","idx","handleKeyDown","useEffect","handleClickOutside","e","target","items","opt","shouldShowList","activeOptionId","jsx","Input","mergeRefs","Loader2","ChevronDown","AutocompleteList"],"mappings":"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"}
@@ -1,51 +1,55 @@
1
1
  "use client";
2
- import { jsx as e, jsxs as v, Fragment as y } from "react/jsx-runtime";
3
- import { forwardRef as $, useState as C, useEffect as A } from "react";
4
- import { createPortal as N } from "react-dom";
5
- import { getRadiusClass as L } from "../../tokens/radius.js";
6
- import { getShadowClass as S } from "../../tokens/shadows.js";
7
- import { getZIndexClass as j } from "../../tokens/z-index.js";
2
+ import { jsx as e, jsxs as y, Fragment as C } from "react/jsx-runtime";
3
+ import { forwardRef as A, useState as N, useEffect as L } from "react";
4
+ import { createPortal as S } from "react-dom";
5
+ import { getRadiusClass as j } from "../../tokens/radius.js";
6
+ import { getShadowClass as P } from "../../tokens/shadows.js";
7
+ import { getZIndexClass as k } from "../../tokens/z-index.js";
8
8
  import { getSpacingClass as o } from "../../tokens/spacing.js";
9
- import P from "./AutocompleteOption.js";
10
- const k = $(
9
+ import D from "./AutocompleteOption.js";
10
+ const E = A(
11
11
  function({
12
- options: i,
13
- highlightedIndex: d,
14
- onSelect: m,
15
- loading: n = !1,
16
- emptyMessage: c = "No options found",
12
+ options: a,
13
+ highlightedIndex: c,
14
+ onSelect: p,
15
+ loading: f = !1,
16
+ emptyMessage: u = "No options found",
17
17
  containerRef: l,
18
- showSelectAll: p = !1,
19
- allSelected: s = !1,
20
- onSelectAll: f,
21
- onDeselectAll: u,
22
- "aria-label": x,
23
- "aria-labelledby": a
24
- }, g) {
25
- const [r, h] = C({ top: 0, left: 0, width: 0 });
26
- A(() => {
18
+ showSelectAll: b = !1,
19
+ allSelected: d = !1,
20
+ onSelectAll: g,
21
+ onDeselectAll: h,
22
+ id: r,
23
+ selectedValues: i,
24
+ "aria-label": v,
25
+ "aria-labelledby": m
26
+ }, x) {
27
+ const [s, w] = N({ top: 0, left: 0, width: 0 });
28
+ L(() => {
27
29
  if (l.current) {
28
30
  const t = l.current.getBoundingClientRect();
29
- h({
31
+ w({
30
32
  top: t.bottom + window.scrollY + 4,
31
33
  left: t.left + window.scrollX,
32
34
  width: t.width
33
35
  });
34
36
  }
35
37
  }, [l]);
36
- const b = /* @__PURE__ */ e(
38
+ const $ = /* @__PURE__ */ e(
37
39
  "div",
38
40
  {
39
- ref: g,
41
+ ref: x,
42
+ id: r,
40
43
  role: "listbox",
41
- "aria-label": a ? void 0 : x,
42
- "aria-labelledby": a,
44
+ "aria-multiselectable": i ? !0 : void 0,
45
+ "aria-label": m ? void 0 : v,
46
+ "aria-labelledby": m,
43
47
  className: `
44
48
  absolute
45
- ${j("popover")}
49
+ ${k("popover")}
46
50
  bg-surface-overlay
47
- ${L("md")}
48
- ${S("lg")}
51
+ ${j("md")}
52
+ ${P("lg")}
49
53
  border
50
54
  border-line-default
51
55
  max-h-60
@@ -53,13 +57,15 @@ const k = $(
53
57
  ${o("xs", "py")}
54
58
  `,
55
59
  style: {
56
- top: `${r.top}px`,
57
- left: `${r.left}px`,
58
- width: `${r.width}px`
60
+ top: `${s.top}px`,
61
+ left: `${s.left}px`,
62
+ width: `${s.width}px`
59
63
  },
60
- children: n ? /* @__PURE__ */ e(
64
+ children: f ? /* @__PURE__ */ e(
61
65
  "div",
62
66
  {
67
+ role: "status",
68
+ "aria-live": "polite",
63
69
  className: `
64
70
  ${o("md", "p")}
65
71
  text-sm
@@ -68,19 +74,21 @@ const k = $(
68
74
  `,
69
75
  children: "Loading..."
70
76
  }
71
- ) : i.length === 0 ? /* @__PURE__ */ e(
77
+ ) : a.length === 0 ? /* @__PURE__ */ e(
72
78
  "div",
73
79
  {
80
+ role: "status",
81
+ "aria-live": "polite",
74
82
  className: `
75
83
  ${o("md", "p")}
76
84
  text-sm
77
85
  text-fg-tertiary
78
86
  text-center
79
87
  `,
80
- children: c
88
+ children: u
81
89
  }
82
- ) : /* @__PURE__ */ v(y, { children: [
83
- p && /* @__PURE__ */ e(
90
+ ) : /* @__PURE__ */ y(C, { children: [
91
+ b && /* @__PURE__ */ e(
84
92
  "div",
85
93
  {
86
94
  className: `
@@ -93,27 +101,29 @@ const k = $(
93
101
  border-b
94
102
  border-line-default
95
103
  `,
96
- onClick: s ? u : f,
97
- children: s ? "Deselect All" : "Select All"
104
+ onClick: d ? h : g,
105
+ children: d ? "Deselect All" : "Select All"
98
106
  }
99
107
  ),
100
- i.map((t, w) => /* @__PURE__ */ e(
101
- P,
108
+ a.map((t, n) => /* @__PURE__ */ e(
109
+ D,
102
110
  {
111
+ id: r ? `${r}-option-${n}` : void 0,
103
112
  option: t,
104
- isHighlighted: w === d,
105
- onSelect: m
113
+ isHighlighted: n === c,
114
+ selected: i ? i.includes(t.value) : void 0,
115
+ onSelect: p
106
116
  },
107
117
  t.value
108
118
  ))
109
119
  ] })
110
120
  }
111
121
  );
112
- return typeof window != "undefined" ? N(b, document.body) : null;
122
+ return typeof window != "undefined" ? S($, document.body) : null;
113
123
  }
114
124
  );
115
- k.displayName = "AutocompleteList";
125
+ E.displayName = "AutocompleteList";
116
126
  export {
117
- k as default
127
+ E as default
118
128
  };
119
129
  //# sourceMappingURL=AutocompleteList.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AutocompleteList.js","sources":["../../../../../src/ui/components/Autocomplete/AutocompleteList.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useEffect, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { getRadiusClass } from \"../../tokens/radius\";\nimport { getShadowClass } from \"../../tokens/shadows\";\nimport { getZIndexClass } from \"../../tokens/z-index\";\nimport { getSpacingClass } from \"../../tokens/spacing\";\nimport AutocompleteOption from \"./AutocompleteOption\";\nimport type { AutocompleteOptionType } from \"./AutocompleteOption\";\n\nexport interface AutocompleteListProps {\n options: AutocompleteOptionType[];\n highlightedIndex: number;\n onSelect: (option: AutocompleteOptionType) => void;\n loading?: boolean;\n emptyMessage?: string;\n containerRef: React.RefObject<HTMLDivElement | null>;\n showSelectAll?: boolean;\n allSelected?: boolean;\n onSelectAll?: () => void;\n onDeselectAll?: () => void;\n /**\n * Accessible name for the listbox. axe `aria-input-field-name`\n * (serious) flags a `role=\"listbox\"` portal without `aria-label` /\n * `aria-labelledby` / `title`. The Autocomplete / MultiSelect parent\n * cascades whatever accessible-name source the consumer provided\n * (label string, aria-label, or external id) so the listbox inherits\n * the same name as the input it dropdowns from.\n */\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n}\n\n/**\n * AutocompleteList Component\n *\n * The list container for autocomplete options.\n * Renders in a portal and positions itself below the input.\n */\nconst AutocompleteList = forwardRef<HTMLDivElement, AutocompleteListProps>(\n function AutocompleteList(\n {\n options,\n highlightedIndex,\n onSelect,\n loading = false,\n emptyMessage = \"No options found\",\n containerRef,\n showSelectAll = false,\n allSelected = false,\n onSelectAll,\n onDeselectAll,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n },\n ref,\n ) {\n const [position, setPosition] = useState({ top: 0, left: 0, width: 0 });\n\n // Calculate position\n useEffect(() => {\n if (containerRef.current) {\n const rect = containerRef.current.getBoundingClientRect();\n setPosition({\n top: rect.bottom + window.scrollY + 4,\n left: rect.left + window.scrollX,\n width: rect.width,\n });\n }\n }, [containerRef]);\n\n const listContent = (\n <div\n ref={ref}\n role=\"listbox\"\n aria-label={ariaLabelledBy ? undefined : ariaLabel}\n aria-labelledby={ariaLabelledBy}\n className={`\n absolute\n ${getZIndexClass(\"popover\")}\n bg-surface-overlay\n ${getRadiusClass(\"md\")}\n ${getShadowClass(\"lg\")}\n border\n border-line-default\n max-h-60\n overflow-y-auto\n ${getSpacingClass(\"xs\", \"py\")}\n `}\n style={{\n top: `${position.top}px`,\n left: `${position.left}px`,\n width: `${position.width}px`,\n }}\n >\n {loading ? (\n <div\n className={`\n ${getSpacingClass(\"md\", \"p\")}\n text-sm\n text-fg-tertiary\n text-center\n `}\n >\n Loading...\n </div>\n ) : options.length === 0 ? (\n <div\n className={`\n ${getSpacingClass(\"md\", \"p\")}\n text-sm\n text-fg-tertiary\n text-center\n `}\n >\n {emptyMessage}\n </div>\n ) : (\n <>\n {showSelectAll && (\n <div\n className={`\n ${getSpacingClass(\"sm\", \"px\")}\n ${getSpacingClass(\"sm\", \"py\")}\n text-sm\n font-medium\n cursor-pointer\n hover:bg-surface-hover\n border-b\n border-line-default\n `}\n onClick={allSelected ? onDeselectAll : onSelectAll}\n >\n {allSelected ? \"Deselect All\" : \"Select All\"}\n </div>\n )}\n {options.map((option, index) => (\n <AutocompleteOption\n key={option.value}\n option={option}\n isHighlighted={index === highlightedIndex}\n onSelect={onSelect}\n />\n ))}\n </>\n )}\n </div>\n );\n\n return typeof window !== \"undefined\"\n ? createPortal(listContent, document.body)\n : null;\n },\n);\n\nAutocompleteList.displayName = \"AutocompleteList\";\n\nexport default AutocompleteList;\n"],"names":["AutocompleteList","forwardRef","options","highlightedIndex","onSelect","loading","emptyMessage","containerRef","showSelectAll","allSelected","onSelectAll","onDeselectAll","ariaLabel","ariaLabelledBy","ref","position","setPosition","useState","useEffect","rect","listContent","jsx","getZIndexClass","getRadiusClass","getShadowClass","getSpacingClass","jsxs","Fragment","option","index","AutocompleteOption","createPortal"],"mappings":";;;;;;;;;AAwCA,MAAMA,IAAmBC;AAAA,EACvB,SACE;AAAA,IACE,SAAAC;AAAA,IACA,kBAAAC;AAAA,IACA,UAAAC;AAAA,IACA,SAAAC,IAAU;AAAA,IACV,cAAAC,IAAe;AAAA,IACf,cAAAC;AAAA,IACA,eAAAC,IAAgB;AAAA,IAChB,aAAAC,IAAc;AAAA,IACd,aAAAC;AAAA,IACA,eAAAC;AAAA,IACA,cAAcC;AAAA,IACd,mBAAmBC;AAAA,EAAA,GAErBC,GACA;AACA,UAAM,CAACC,GAAUC,CAAW,IAAIC,EAAS,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,EAAA,CAAG;AAGtE,IAAAC,EAAU,MAAM;AACd,UAAIX,EAAa,SAAS;AACxB,cAAMY,IAAOZ,EAAa,QAAQ,sBAAA;AAClC,QAAAS,EAAY;AAAA,UACV,KAAKG,EAAK,SAAS,OAAO,UAAU;AAAA,UACpC,MAAMA,EAAK,OAAO,OAAO;AAAA,UACzB,OAAOA,EAAK;AAAA,QAAA,CACb;AAAA,MACH;AAAA,IACF,GAAG,CAACZ,CAAY,CAAC;AAEjB,UAAMa,IACJ,gBAAAC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAAP;AAAA,QACA,MAAK;AAAA,QACL,cAAYD,IAAiB,SAAYD;AAAA,QACzC,mBAAiBC;AAAA,QACjB,WAAW;AAAA;AAAA,YAEPS,EAAe,SAAS,CAAC;AAAA;AAAA,YAEzBC,EAAe,IAAI,CAAC;AAAA,YACpBC,EAAe,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,YAKpBC,EAAgB,MAAM,IAAI,CAAC;AAAA;AAAA,QAE/B,OAAO;AAAA,UACL,KAAK,GAAGV,EAAS,GAAG;AAAA,UACpB,MAAM,GAAGA,EAAS,IAAI;AAAA,UACtB,OAAO,GAAGA,EAAS,KAAK;AAAA,QAAA;AAAA,QAGzB,UAAAV,IACC,gBAAAgB;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAW;AAAA,gBACPI,EAAgB,MAAM,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,YAK/B,UAAA;AAAA,UAAA;AAAA,QAAA,IAGCvB,EAAQ,WAAW,IACrB,gBAAAmB;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAW;AAAA,gBACPI,EAAgB,MAAM,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,YAM7B,UAAAnB;AAAA,UAAA;AAAA,QAAA,IAGH,gBAAAoB,EAAAC,GAAA,EACG,UAAA;AAAA,UAAAnB,KACC,gBAAAa;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAW;AAAA,oBACPI,EAAgB,MAAM,IAAI,CAAC;AAAA,oBAC3BA,EAAgB,MAAM,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAQ/B,SAAShB,IAAcE,IAAgBD;AAAA,cAEtC,cAAc,iBAAiB;AAAA,YAAA;AAAA,UAAA;AAAA,UAGnCR,EAAQ,IAAI,CAAC0B,GAAQC,MACpB,gBAAAR;AAAA,YAACS;AAAA,YAAA;AAAA,cAEC,QAAAF;AAAA,cACA,eAAeC,MAAU1B;AAAA,cACzB,UAAAC;AAAA,YAAA;AAAA,YAHKwB,EAAO;AAAA,UAAA,CAKf;AAAA,QAAA,EAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAKN,WAAO,OAAO,UAAW,cACrBG,EAAaX,GAAa,SAAS,IAAI,IACvC;AAAA,EACN;AACF;AAEApB,EAAiB,cAAc;"}
1
+ {"version":3,"file":"AutocompleteList.js","sources":["../../../../../src/ui/components/Autocomplete/AutocompleteList.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useEffect, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { getRadiusClass } from \"../../tokens/radius\";\nimport { getShadowClass } from \"../../tokens/shadows\";\nimport { getZIndexClass } from \"../../tokens/z-index\";\nimport { getSpacingClass } from \"../../tokens/spacing\";\nimport AutocompleteOption from \"./AutocompleteOption\";\nimport type { AutocompleteOptionType } from \"./AutocompleteOption\";\n\nexport interface AutocompleteListProps {\n options: AutocompleteOptionType[];\n highlightedIndex: number;\n onSelect: (option: AutocompleteOptionType) => void;\n loading?: boolean;\n emptyMessage?: string;\n containerRef: React.RefObject<HTMLDivElement | null>;\n showSelectAll?: boolean;\n allSelected?: boolean;\n onSelectAll?: () => void;\n onDeselectAll?: () => void;\n /** listbox id; option ids are derived as `${id}-option-${index}`. */\n id?: string;\n /** Multi-select: values currently selected, to drive aria-selected. */\n selectedValues?: string[];\n /**\n * Accessible name for the listbox. axe `aria-input-field-name`\n * (serious) flags a `role=\"listbox\"` portal without `aria-label` /\n * `aria-labelledby` / `title`. The Autocomplete / MultiSelect parent\n * cascades whatever accessible-name source the consumer provided\n * (label string, aria-label, or external id) so the listbox inherits\n * the same name as the input it dropdowns from.\n */\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n}\n\n/**\n * AutocompleteList Component\n *\n * The list container for autocomplete options.\n * Renders in a portal and positions itself below the input.\n */\nconst AutocompleteList = forwardRef<HTMLDivElement, AutocompleteListProps>(\n function AutocompleteList(\n {\n options,\n highlightedIndex,\n onSelect,\n loading = false,\n emptyMessage = \"No options found\",\n containerRef,\n showSelectAll = false,\n allSelected = false,\n onSelectAll,\n onDeselectAll,\n id,\n selectedValues,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n },\n ref,\n ) {\n const [position, setPosition] = useState({ top: 0, left: 0, width: 0 });\n\n // Calculate position\n useEffect(() => {\n if (containerRef.current) {\n const rect = containerRef.current.getBoundingClientRect();\n setPosition({\n top: rect.bottom + window.scrollY + 4,\n left: rect.left + window.scrollX,\n width: rect.width,\n });\n }\n }, [containerRef]);\n\n const listContent = (\n <div\n ref={ref}\n id={id}\n role=\"listbox\"\n aria-multiselectable={selectedValues ? true : undefined}\n aria-label={ariaLabelledBy ? undefined : ariaLabel}\n aria-labelledby={ariaLabelledBy}\n className={`\n absolute\n ${getZIndexClass(\"popover\")}\n bg-surface-overlay\n ${getRadiusClass(\"md\")}\n ${getShadowClass(\"lg\")}\n border\n border-line-default\n max-h-60\n overflow-y-auto\n ${getSpacingClass(\"xs\", \"py\")}\n `}\n style={{\n top: `${position.top}px`,\n left: `${position.left}px`,\n width: `${position.width}px`,\n }}\n >\n {loading ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className={`\n ${getSpacingClass(\"md\", \"p\")}\n text-sm\n text-fg-tertiary\n text-center\n `}\n >\n Loading...\n </div>\n ) : options.length === 0 ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className={`\n ${getSpacingClass(\"md\", \"p\")}\n text-sm\n text-fg-tertiary\n text-center\n `}\n >\n {emptyMessage}\n </div>\n ) : (\n <>\n {showSelectAll && (\n <div\n className={`\n ${getSpacingClass(\"sm\", \"px\")}\n ${getSpacingClass(\"sm\", \"py\")}\n text-sm\n font-medium\n cursor-pointer\n hover:bg-surface-hover\n border-b\n border-line-default\n `}\n onClick={allSelected ? onDeselectAll : onSelectAll}\n >\n {allSelected ? \"Deselect All\" : \"Select All\"}\n </div>\n )}\n {options.map((option, index) => (\n <AutocompleteOption\n key={option.value}\n id={id ? `${id}-option-${index}` : undefined}\n option={option}\n isHighlighted={index === highlightedIndex}\n selected={\n selectedValues\n ? selectedValues.includes(option.value)\n : undefined\n }\n onSelect={onSelect}\n />\n ))}\n </>\n )}\n </div>\n );\n\n return typeof window !== \"undefined\"\n ? createPortal(listContent, document.body)\n : null;\n },\n);\n\nAutocompleteList.displayName = \"AutocompleteList\";\n\nexport default AutocompleteList;\n"],"names":["AutocompleteList","forwardRef","options","highlightedIndex","onSelect","loading","emptyMessage","containerRef","showSelectAll","allSelected","onSelectAll","onDeselectAll","id","selectedValues","ariaLabel","ariaLabelledBy","ref","position","setPosition","useState","useEffect","rect","listContent","jsx","getZIndexClass","getRadiusClass","getShadowClass","getSpacingClass","jsxs","Fragment","option","index","AutocompleteOption","createPortal"],"mappings":"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"}
@@ -1,42 +1,43 @@
1
1
  "use client";
2
- import { jsxs as t, jsx as r } from "react/jsx-runtime";
3
- import { forwardRef as i } from "react";
4
- import { getSpacingClass as s } from "../../tokens/spacing.js";
5
- const n = i(
6
- function({ option: e, isHighlighted: a, onSelect: l }, c) {
7
- const o = () => {
8
- e.disabled || l(e);
2
+ import { jsxs as n, jsx as l } from "react/jsx-runtime";
3
+ import { forwardRef as m } from "react";
4
+ import { getSpacingClass as r } from "../../tokens/spacing.js";
5
+ const p = m(
6
+ function({ option: a, isHighlighted: s, onSelect: o, id: i, selected: e }, c) {
7
+ const t = () => {
8
+ a.disabled || o(a);
9
9
  };
10
- return /* @__PURE__ */ t(
10
+ return /* @__PURE__ */ n(
11
11
  "div",
12
12
  {
13
13
  ref: c,
14
+ id: i,
14
15
  role: "option",
15
- "aria-selected": a,
16
- "aria-disabled": e.disabled,
17
- onClick: o,
16
+ "aria-selected": e != null ? e : s,
17
+ "aria-disabled": a.disabled,
18
+ onClick: t,
18
19
  className: `
19
20
  flex
20
21
  items-center
21
- ${s("sm", "gap")}
22
- ${s("sm", "px")}
23
- ${s("sm", "py")}
22
+ ${r("sm", "gap")}
23
+ ${r("sm", "px")}
24
+ ${r("sm", "py")}
24
25
  text-sm
25
26
  cursor-pointer
26
27
  transition-colors
27
- ${a ? "bg-surface-active" : ""}
28
- ${e.disabled ? "opacity-50 cursor-not-allowed" : "hover:bg-surface-hover"}
28
+ ${s ? "bg-surface-active" : ""}
29
+ ${a.disabled ? "opacity-50 cursor-not-allowed" : "hover:bg-surface-hover"}
29
30
  `,
30
31
  children: [
31
- e.icon && /* @__PURE__ */ r("span", { className: "flex-shrink-0", children: e.icon }),
32
- /* @__PURE__ */ r("span", { className: "flex-1", children: e.label })
32
+ a.icon && /* @__PURE__ */ l("span", { className: "flex-shrink-0", children: a.icon }),
33
+ /* @__PURE__ */ l("span", { className: "flex-1", children: a.label })
33
34
  ]
34
35
  }
35
36
  );
36
37
  }
37
38
  );
38
- n.displayName = "AutocompleteOption";
39
+ p.displayName = "AutocompleteOption";
39
40
  export {
40
- n as default
41
+ p as default
41
42
  };
42
43
  //# sourceMappingURL=AutocompleteOption.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AutocompleteOption.js","sources":["../../../../../src/ui/components/Autocomplete/AutocompleteOption.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, type ReactNode } from \"react\";\nimport { getSpacingClass } from \"../../tokens/spacing\";\n\nexport interface AutocompleteOptionType {\n value: string;\n label: string;\n disabled?: boolean;\n icon?: ReactNode;\n group?: string;\n}\n\nexport interface AutocompleteOptionProps {\n option: AutocompleteOptionType;\n isHighlighted: boolean;\n onSelect: (option: AutocompleteOptionType) => void;\n}\n\n/**\n * AutocompleteOption Component\n *\n * A single option in the autocomplete list.\n */\nconst AutocompleteOption = forwardRef<HTMLDivElement, AutocompleteOptionProps>(\n function AutocompleteOption({ option, isHighlighted, onSelect }, ref) {\n const handleClick = () => {\n if (!option.disabled) {\n onSelect(option);\n }\n };\n\n return (\n <div\n ref={ref}\n role=\"option\"\n aria-selected={isHighlighted}\n aria-disabled={option.disabled}\n onClick={handleClick}\n className={`\n flex\n items-center\n ${getSpacingClass(\"sm\", \"gap\")}\n ${getSpacingClass(\"sm\", \"px\")}\n ${getSpacingClass(\"sm\", \"py\")}\n text-sm\n cursor-pointer\n transition-colors\n ${isHighlighted ? \"bg-surface-active\" : \"\"}\n ${option.disabled ? \"opacity-50 cursor-not-allowed\" : \"hover:bg-surface-hover\"}\n `}\n >\n {option.icon && <span className=\"flex-shrink-0\">{option.icon}</span>}\n <span className=\"flex-1\">{option.label}</span>\n </div>\n );\n },\n);\n\nAutocompleteOption.displayName = \"AutocompleteOption\";\n\nexport default AutocompleteOption;\n"],"names":["AutocompleteOption","forwardRef","option","isHighlighted","onSelect","ref","handleClick","jsxs","getSpacingClass","jsx"],"mappings":";;;;AAwBA,MAAMA,IAAqBC;AAAA,EACzB,SAA4B,EAAE,QAAAC,GAAQ,eAAAC,GAAe,UAAAC,EAAA,GAAYC,GAAK;AACpE,UAAMC,IAAc,MAAM;AACxB,MAAKJ,EAAO,YACVE,EAASF,CAAM;AAAA,IAEnB;AAEA,WACE,gBAAAK;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAAF;AAAA,QACA,MAAK;AAAA,QACL,iBAAeF;AAAA,QACf,iBAAeD,EAAO;AAAA,QACtB,SAASI;AAAA,QACT,WAAW;AAAA;AAAA;AAAA,YAGPE,EAAgB,MAAM,KAAK,CAAC;AAAA,YAC5BA,EAAgB,MAAM,IAAI,CAAC;AAAA,YAC3BA,EAAgB,MAAM,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,YAI3BL,IAAgB,sBAAsB,EAAE;AAAA,YACxCD,EAAO,WAAW,kCAAkC,wBAAwB;AAAA;AAAA,QAG/E,UAAA;AAAA,UAAAA,EAAO,QAAQ,gBAAAO,EAAC,QAAA,EAAK,WAAU,iBAAiB,YAAO,MAAK;AAAA,UAC7D,gBAAAA,EAAC,QAAA,EAAK,WAAU,UAAU,YAAO,MAAA,CAAM;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAG7C;AACF;AAEAT,EAAmB,cAAc;"}
1
+ {"version":3,"file":"AutocompleteOption.js","sources":["../../../../../src/ui/components/Autocomplete/AutocompleteOption.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, type ReactNode } from \"react\";\nimport { getSpacingClass } from \"../../tokens/spacing\";\n\nexport interface AutocompleteOptionType {\n value: string;\n label: string;\n disabled?: boolean;\n icon?: ReactNode;\n group?: string;\n}\n\nexport interface AutocompleteOptionProps {\n option: AutocompleteOptionType;\n isHighlighted: boolean;\n onSelect: (option: AutocompleteOptionType) => void;\n /** Stable id so the combobox input can point aria-activedescendant here. */\n id?: string;\n /**\n * Selection state for multi-select usage. When provided it drives\n * aria-selected; otherwise aria-selected reflects the highlight (the\n * single-select activedescendant model).\n */\n selected?: boolean;\n}\n\n/**\n * AutocompleteOption Component\n *\n * A single option in the autocomplete list.\n */\nconst AutocompleteOption = forwardRef<HTMLDivElement, AutocompleteOptionProps>(\n function AutocompleteOption(\n { option, isHighlighted, onSelect, id, selected },\n ref,\n ) {\n const handleClick = () => {\n if (!option.disabled) {\n onSelect(option);\n }\n };\n\n return (\n <div\n ref={ref}\n id={id}\n role=\"option\"\n aria-selected={selected ?? isHighlighted}\n aria-disabled={option.disabled}\n onClick={handleClick}\n className={`\n flex\n items-center\n ${getSpacingClass(\"sm\", \"gap\")}\n ${getSpacingClass(\"sm\", \"px\")}\n ${getSpacingClass(\"sm\", \"py\")}\n text-sm\n cursor-pointer\n transition-colors\n ${isHighlighted ? \"bg-surface-active\" : \"\"}\n ${option.disabled ? \"opacity-50 cursor-not-allowed\" : \"hover:bg-surface-hover\"}\n `}\n >\n {option.icon && <span className=\"flex-shrink-0\">{option.icon}</span>}\n <span className=\"flex-1\">{option.label}</span>\n </div>\n );\n },\n);\n\nAutocompleteOption.displayName = \"AutocompleteOption\";\n\nexport default AutocompleteOption;\n"],"names":["AutocompleteOption","forwardRef","option","isHighlighted","onSelect","id","selected","ref","handleClick","jsxs","getSpacingClass","jsx"],"mappings":";;;;AAgCA,MAAMA,IAAqBC;AAAA,EACzB,SACE,EAAE,QAAAC,GAAQ,eAAAC,GAAe,UAAAC,GAAU,IAAAC,GAAI,UAAAC,EAAA,GACvCC,GACA;AACA,UAAMC,IAAc,MAAM;AACxB,MAAKN,EAAO,YACVE,EAASF,CAAM;AAAA,IAEnB;AAEA,WACE,gBAAAO;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAAF;AAAA,QACA,IAAAF;AAAA,QACA,MAAK;AAAA,QACL,iBAAeC,KAAA,OAAAA,IAAYH;AAAA,QAC3B,iBAAeD,EAAO;AAAA,QACtB,SAASM;AAAA,QACT,WAAW;AAAA;AAAA;AAAA,YAGPE,EAAgB,MAAM,KAAK,CAAC;AAAA,YAC5BA,EAAgB,MAAM,IAAI,CAAC;AAAA,YAC3BA,EAAgB,MAAM,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,YAI3BP,IAAgB,sBAAsB,EAAE;AAAA,YACxCD,EAAO,WAAW,kCAAkC,wBAAwB;AAAA;AAAA,QAG/E,UAAA;AAAA,UAAAA,EAAO,QAAQ,gBAAAS,EAAC,QAAA,EAAK,WAAU,iBAAiB,YAAO,MAAK;AAAA,UAC7D,gBAAAA,EAAC,QAAA,EAAK,WAAU,UAAU,YAAO,MAAA,CAAM;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAG7C;AACF;AAEAX,EAAmB,cAAc;"}