@flanksource/clicky-ui 0.2.11 → 0.2.13

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 (188) hide show
  1. package/dist/components/Combobox.cjs +47 -33
  2. package/dist/components/Combobox.cjs.map +1 -1
  3. package/dist/components/Combobox.d.ts +12 -0
  4. package/dist/components/Combobox.d.ts.map +1 -1
  5. package/dist/components/Combobox.js +48 -34
  6. package/dist/components/Combobox.js.map +1 -1
  7. package/dist/components/IconButton.cjs +26 -0
  8. package/dist/components/IconButton.cjs.map +1 -0
  9. package/dist/components/IconButton.d.ts +25 -0
  10. package/dist/components/IconButton.d.ts.map +1 -0
  11. package/dist/components/IconButton.js +26 -0
  12. package/dist/components/IconButton.js.map +1 -0
  13. package/dist/components/JsonSchemaForm.cjs +1 -1
  14. package/dist/components/JsonSchemaForm.cjs.map +1 -1
  15. package/dist/components/JsonSchemaForm.js +1 -1
  16. package/dist/components/JsonSchemaForm.js.map +1 -1
  17. package/dist/components/SecretKeySelector.cjs +151 -0
  18. package/dist/components/SecretKeySelector.cjs.map +1 -0
  19. package/dist/components/SecretKeySelector.d.ts +47 -0
  20. package/dist/components/SecretKeySelector.d.ts.map +1 -0
  21. package/dist/components/SecretKeySelector.js +151 -0
  22. package/dist/components/SecretKeySelector.js.map +1 -0
  23. package/dist/components/WorkloadPicker.cjs +144 -0
  24. package/dist/components/WorkloadPicker.cjs.map +1 -0
  25. package/dist/components/WorkloadPicker.d.ts +52 -0
  26. package/dist/components/WorkloadPicker.d.ts.map +1 -0
  27. package/dist/components/WorkloadPicker.js +144 -0
  28. package/dist/components/WorkloadPicker.js.map +1 -0
  29. package/dist/components/icon-menu-picker.cjs +83 -112
  30. package/dist/components/icon-menu-picker.cjs.map +1 -1
  31. package/dist/components/icon-menu-picker.d.ts.map +1 -1
  32. package/dist/components/icon-menu-picker.js +85 -114
  33. package/dist/components/icon-menu-picker.js.map +1 -1
  34. package/dist/components/json-schema-form-fields.cjs +9 -7
  35. package/dist/components/json-schema-form-fields.cjs.map +1 -1
  36. package/dist/components/json-schema-form-fields.d.ts.map +1 -1
  37. package/dist/components/json-schema-form-fields.js +9 -7
  38. package/dist/components/json-schema-form-fields.js.map +1 -1
  39. package/dist/components/json-schema-form-types.d.ts +8 -4
  40. package/dist/components/json-schema-form-types.d.ts.map +1 -1
  41. package/dist/components.cjs +10 -0
  42. package/dist/components.cjs.map +1 -1
  43. package/dist/components.d.ts +3 -0
  44. package/dist/components.d.ts.map +1 -1
  45. package/dist/components.js +11 -1
  46. package/dist/components.js.map +1 -1
  47. package/dist/data/Clicky.cjs +1 -1
  48. package/dist/data/Clicky.cjs.map +1 -1
  49. package/dist/data/Clicky.js +1 -1
  50. package/dist/data/Clicky.js.map +1 -1
  51. package/dist/data/LogDetails.cjs +4 -2
  52. package/dist/data/LogDetails.cjs.map +1 -1
  53. package/dist/data/LogDetails.js +4 -2
  54. package/dist/data/LogDetails.js.map +1 -1
  55. package/dist/data/Version.cjs +42 -0
  56. package/dist/data/Version.cjs.map +1 -0
  57. package/dist/data/Version.d.ts +36 -0
  58. package/dist/data/Version.d.ts.map +1 -0
  59. package/dist/data/Version.js +42 -0
  60. package/dist/data/Version.js.map +1 -0
  61. package/dist/data/test-runner/TestDetailPanel.cjs +165 -0
  62. package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -0
  63. package/dist/data/test-runner/TestDetailPanel.d.ts +7 -0
  64. package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -0
  65. package/dist/data/test-runner/TestDetailPanel.js +165 -0
  66. package/dist/data/test-runner/TestDetailPanel.js.map +1 -0
  67. package/dist/data/test-runner/TestFailureDetail.cjs +78 -0
  68. package/dist/data/test-runner/TestFailureDetail.cjs.map +1 -0
  69. package/dist/data/test-runner/TestFailureDetail.d.ts +14 -0
  70. package/dist/data/test-runner/TestFailureDetail.d.ts.map +1 -0
  71. package/dist/data/test-runner/TestFailureDetail.js +78 -0
  72. package/dist/data/test-runner/TestFailureDetail.js.map +1 -0
  73. package/dist/data/test-runner/TestFilterBar.cjs +108 -0
  74. package/dist/data/test-runner/TestFilterBar.cjs.map +1 -0
  75. package/dist/data/test-runner/TestFilterBar.d.ts +15 -0
  76. package/dist/data/test-runner/TestFilterBar.d.ts.map +1 -0
  77. package/dist/data/test-runner/TestFilterBar.js +108 -0
  78. package/dist/data/test-runner/TestFilterBar.js.map +1 -0
  79. package/dist/data/test-runner/TestRunSummary.cjs +81 -0
  80. package/dist/data/test-runner/TestRunSummary.cjs.map +1 -0
  81. package/dist/data/test-runner/TestRunSummary.d.ts +19 -0
  82. package/dist/data/test-runner/TestRunSummary.d.ts.map +1 -0
  83. package/dist/data/test-runner/TestRunSummary.js +81 -0
  84. package/dist/data/test-runner/TestRunSummary.js.map +1 -0
  85. package/dist/data/test-runner/TestRunner.cjs +143 -0
  86. package/dist/data/test-runner/TestRunner.cjs.map +1 -0
  87. package/dist/data/test-runner/TestRunner.d.ts +42 -0
  88. package/dist/data/test-runner/TestRunner.d.ts.map +1 -0
  89. package/dist/data/test-runner/TestRunner.fixtures.d.ts +26 -0
  90. package/dist/data/test-runner/TestRunner.fixtures.d.ts.map +1 -0
  91. package/dist/data/test-runner/TestRunner.js +143 -0
  92. package/dist/data/test-runner/TestRunner.js.map +1 -0
  93. package/dist/data/test-runner/TestTree.cjs +42 -0
  94. package/dist/data/test-runner/TestTree.cjs.map +1 -0
  95. package/dist/data/test-runner/TestTree.d.ts +9 -0
  96. package/dist/data/test-runner/TestTree.d.ts.map +1 -0
  97. package/dist/data/test-runner/TestTree.js +42 -0
  98. package/dist/data/test-runner/TestTree.js.map +1 -0
  99. package/dist/data/test-runner/TestTreeNode.cjs +99 -0
  100. package/dist/data/test-runner/TestTreeNode.cjs.map +1 -0
  101. package/dist/data/test-runner/TestTreeNode.d.ts +12 -0
  102. package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -0
  103. package/dist/data/test-runner/TestTreeNode.js +99 -0
  104. package/dist/data/test-runner/TestTreeNode.js.map +1 -0
  105. package/dist/data/test-runner/adapter.cjs +57 -0
  106. package/dist/data/test-runner/adapter.cjs.map +1 -0
  107. package/dist/data/test-runner/adapter.d.ts +60 -0
  108. package/dist/data/test-runner/adapter.d.ts.map +1 -0
  109. package/dist/data/test-runner/adapter.js +57 -0
  110. package/dist/data/test-runner/adapter.js.map +1 -0
  111. package/dist/data/test-runner/context.cjs +15 -0
  112. package/dist/data/test-runner/context.cjs.map +1 -0
  113. package/dist/data/test-runner/context.d.ts +40 -0
  114. package/dist/data/test-runner/context.d.ts.map +1 -0
  115. package/dist/data/test-runner/context.js +15 -0
  116. package/dist/data/test-runner/context.js.map +1 -0
  117. package/dist/data/test-runner/filterState.cjs +47 -0
  118. package/dist/data/test-runner/filterState.cjs.map +1 -0
  119. package/dist/data/test-runner/filterState.d.ts +14 -0
  120. package/dist/data/test-runner/filterState.d.ts.map +1 -0
  121. package/dist/data/test-runner/filterState.js +47 -0
  122. package/dist/data/test-runner/filterState.js.map +1 -0
  123. package/dist/data/test-runner/frameworkIcon.cjs +27 -0
  124. package/dist/data/test-runner/frameworkIcon.cjs.map +1 -0
  125. package/dist/data/test-runner/frameworkIcon.d.ts +3 -0
  126. package/dist/data/test-runner/frameworkIcon.d.ts.map +1 -0
  127. package/dist/data/test-runner/frameworkIcon.js +27 -0
  128. package/dist/data/test-runner/frameworkIcon.js.map +1 -0
  129. package/dist/data/test-runner/index.d.ts +14 -0
  130. package/dist/data/test-runner/index.d.ts.map +1 -0
  131. package/dist/data/test-runner/status.cjs +234 -0
  132. package/dist/data/test-runner/status.cjs.map +1 -0
  133. package/dist/data/test-runner/status.d.ts +52 -0
  134. package/dist/data/test-runner/status.d.ts.map +1 -0
  135. package/dist/data/test-runner/status.js +234 -0
  136. package/dist/data/test-runner/status.js.map +1 -0
  137. package/dist/data/test-runner/types.d.ts +148 -0
  138. package/dist/data/test-runner/types.d.ts.map +1 -0
  139. package/dist/data/version-info.cjs +26 -0
  140. package/dist/data/version-info.cjs.map +1 -0
  141. package/dist/data/version-info.d.ts +11 -0
  142. package/dist/data/version-info.d.ts.map +1 -0
  143. package/dist/data/version-info.js +26 -0
  144. package/dist/data/version-info.js.map +1 -0
  145. package/dist/data.cjs +49 -0
  146. package/dist/data.cjs.map +1 -1
  147. package/dist/data.d.ts +3 -0
  148. package/dist/data.d.ts.map +1 -1
  149. package/dist/data.js +50 -1
  150. package/dist/data.js.map +1 -1
  151. package/dist/index.cjs +59 -0
  152. package/dist/index.cjs.map +1 -1
  153. package/dist/index.js +60 -1
  154. package/dist/index.js.map +1 -1
  155. package/dist/layout/SplitPane.cjs +34 -27
  156. package/dist/layout/SplitPane.cjs.map +1 -1
  157. package/dist/layout/SplitPane.d.ts.map +1 -1
  158. package/dist/layout/SplitPane.js +34 -27
  159. package/dist/layout/SplitPane.js.map +1 -1
  160. package/dist/overlay/DropdownMenu.cjs +57 -30
  161. package/dist/overlay/DropdownMenu.cjs.map +1 -1
  162. package/dist/overlay/DropdownMenu.d.ts.map +1 -1
  163. package/dist/overlay/DropdownMenu.js +57 -30
  164. package/dist/overlay/DropdownMenu.js.map +1 -1
  165. package/dist/overlay/Modal.cjs +62 -6
  166. package/dist/overlay/Modal.cjs.map +1 -1
  167. package/dist/overlay/Modal.d.ts +19 -1
  168. package/dist/overlay/Modal.d.ts.map +1 -1
  169. package/dist/overlay/Modal.js +62 -6
  170. package/dist/overlay/Modal.js.map +1 -1
  171. package/dist/rpc/CommandForm.cjs +2 -2
  172. package/dist/rpc/CommandForm.cjs.map +1 -1
  173. package/dist/rpc/CommandForm.d.ts.map +1 -1
  174. package/dist/rpc/CommandForm.js +3 -3
  175. package/dist/rpc/CommandForm.js.map +1 -1
  176. package/dist/rpc/formMetadata.cjs +5 -4
  177. package/dist/rpc/formMetadata.cjs.map +1 -1
  178. package/dist/rpc/formMetadata.d.ts.map +1 -1
  179. package/dist/rpc/formMetadata.js +6 -5
  180. package/dist/rpc/formMetadata.js.map +1 -1
  181. package/dist/rpc/types.cjs +4 -0
  182. package/dist/rpc/types.cjs.map +1 -1
  183. package/dist/rpc/types.d.ts +10 -0
  184. package/dist/rpc/types.d.ts.map +1 -1
  185. package/dist/rpc/types.js +5 -1
  186. package/dist/rpc/types.js.map +1 -1
  187. package/dist/styles.css +1 -1
  188. package/package.json +3 -1
@@ -2,9 +2,11 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const react = require("react");
5
+ const react$1 = require("@floating-ui/react");
5
6
  const utils = require("../lib/utils.cjs");
6
7
  const Icon = require("../data/Icon.cjs");
7
8
  const button = require("./button.cjs");
9
+ const IconButton = require("./IconButton.cjs");
8
10
  const UiChevronDown = require("../icons/components/UiChevronDown.cjs");
9
11
  const UiCheck = require("../icons/components/UiCheck.cjs");
10
12
  function IconMenuPickerInner({
@@ -18,137 +20,104 @@ function IconMenuPickerInner({
18
20
  className,
19
21
  triggerClassName,
20
22
  menuClassName
21
- }, ref) {
22
- const rootRef = react.useRef(null);
23
- const triggerRef = react.useRef(null);
24
- const itemRefs = react.useRef([]);
23
+ }, forwardedRef) {
25
24
  const [open, setOpen] = react.useState(false);
26
- react.useImperativeHandle(ref, () => rootRef.current);
27
- const activeIndex = react.useMemo(
28
- () => Math.max(
29
- 0,
30
- options.findIndex((option) => option.value === value)
31
- ),
25
+ const [activeIndex, setActiveIndex] = react.useState(null);
26
+ const listRef = react.useRef([]);
27
+ const labelsRef = react.useRef(options.map((o) => o.label));
28
+ labelsRef.current = options.map((o) => o.label);
29
+ const selectedIndex = react.useMemo(
30
+ () => options.findIndex((option) => option.value === value),
32
31
  [options, value]
33
32
  );
34
- const close = react.useCallback((returnFocus) => {
35
- var _a;
33
+ const { refs, floatingStyles, context } = react$1.useFloating({
34
+ open,
35
+ onOpenChange: setOpen,
36
+ placement: "bottom-start",
37
+ whileElementsMounted: react$1.autoUpdate,
38
+ // offset(6) reproduces the previous 0.375rem gap; flip opens the menu
39
+ // upward when the trigger is near the bottom edge; shift slides it back
40
+ // on-screen at the left/right edges. The 8px padding keeps a gutter.
41
+ middleware: [react$1.offset(6), react$1.flip({ padding: 8 }), react$1.shift({ padding: 8 })]
42
+ });
43
+ const click = react$1.useClick(context);
44
+ const dismiss = react$1.useDismiss(context);
45
+ const role = react$1.useRole(context, { role: "menu" });
46
+ const listNav = react$1.useListNavigation(context, {
47
+ listRef,
48
+ activeIndex,
49
+ selectedIndex,
50
+ onNavigate: setActiveIndex,
51
+ loop: true
52
+ });
53
+ const typeahead = react$1.useTypeahead(context, {
54
+ listRef: labelsRef,
55
+ activeIndex,
56
+ onMatch: setActiveIndex
57
+ });
58
+ const { getReferenceProps, getFloatingProps, getItemProps } = react$1.useInteractions([
59
+ click,
60
+ dismiss,
61
+ role,
62
+ listNav,
63
+ typeahead
64
+ ]);
65
+ const referenceRef = react$1.useMergeRefs([refs.setReference, forwardedRef]);
66
+ const select = (next) => {
67
+ onChange(next);
36
68
  setOpen(false);
37
- if (returnFocus) (_a = triggerRef.current) == null ? void 0 : _a.focus();
38
- }, []);
39
- const select = react.useCallback(
40
- (next) => {
41
- onChange(next);
42
- close(true);
43
- },
44
- [close, onChange]
45
- );
46
- react.useEffect(() => {
47
- if (!open) return;
48
- const onPointerDown = (event) => {
49
- var _a;
50
- if (event.button !== 0) return;
51
- if (!((_a = rootRef.current) == null ? void 0 : _a.contains(event.target))) {
52
- close(false);
53
- }
54
- };
55
- const onKeyDown = (event) => {
56
- if (event.key === "Escape") {
57
- event.preventDefault();
58
- close(true);
59
- }
60
- };
61
- document.addEventListener("pointerdown", onPointerDown);
62
- document.addEventListener("keydown", onKeyDown);
63
- return () => {
64
- document.removeEventListener("pointerdown", onPointerDown);
65
- document.removeEventListener("keydown", onKeyDown);
66
- };
67
- }, [close, open]);
68
- react.useEffect(() => {
69
- if (!open) return;
70
- const node = itemRefs.current[activeIndex];
71
- node == null ? void 0 : node.focus();
72
- }, [activeIndex, open]);
73
- const focusItem = (index) => {
74
- var _a;
75
- const wrapped = (index + options.length) % options.length;
76
- (_a = itemRefs.current[wrapped]) == null ? void 0 : _a.focus();
77
69
  };
78
- const onMenuKeyDown = (event) => {
79
- const focusedIndex = itemRefs.current.findIndex((node) => node === document.activeElement);
80
- if (event.key === "ArrowDown") {
81
- event.preventDefault();
82
- focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) + 1);
83
- return;
84
- }
85
- if (event.key === "ArrowUp") {
86
- event.preventDefault();
87
- focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) - 1);
88
- return;
89
- }
90
- if (event.key === "Home") {
91
- event.preventDefault();
92
- focusItem(0);
93
- return;
94
- }
95
- if (event.key === "End") {
96
- event.preventDefault();
97
- focusItem(options.length - 1);
98
- return;
99
- }
100
- if (event.key === "Tab") {
101
- close(false);
102
- }
103
- };
104
- const onTriggerKeyDown = (event) => {
105
- if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
106
- event.preventDefault();
107
- setOpen(true);
108
- }
109
- };
110
- const selected = options[activeIndex];
70
+ const selected = options[selectedIndex];
111
71
  if (!selected) {
112
72
  throw new Error(`IconMenuPicker: value "${value}" not found in options for ${ariaLabel}`);
113
73
  }
114
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: utils.cn("relative inline-flex", showLabel && "w-full", className), children: [
115
- /* @__PURE__ */ jsxRuntime.jsxs(
74
+ const triggerTitleText = triggerTitle ?? `${ariaLabel}: ${selected.label}`;
75
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: referenceRef, className: utils.cn("relative inline-flex", showLabel && "w-full", className), children: [
76
+ showLabel ? /* @__PURE__ */ jsxRuntime.jsxs(
116
77
  button.Button,
117
78
  {
118
- ref: triggerRef,
119
79
  type: "button",
120
80
  variant: "ghost",
121
- size: showLabel ? "default" : "icon",
81
+ size: "default",
122
82
  "aria-label": ariaLabel,
123
83
  "aria-haspopup": "menu",
124
84
  "aria-expanded": open,
125
- title: triggerTitle ?? `${ariaLabel}: ${selected.label}`,
126
- onClick: () => setOpen((current) => !current),
127
- onKeyDown: onTriggerKeyDown,
85
+ title: triggerTitleText,
128
86
  className: utils.cn(
129
- "text-muted-foreground hover:text-foreground",
130
- showLabel && "w-full justify-start",
87
+ "w-full justify-start text-muted-foreground hover:text-foreground",
131
88
  triggerClassName
132
89
  ),
90
+ ...getReferenceProps(),
133
91
  children: [
134
- /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: selected.icon, ...showLabel ? { className: "shrink-0 text-foreground" } : {} }),
135
- showLabel && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
136
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-left capitalize", children: `${ariaLabel}: ${selected.label}` }),
137
- /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiChevronDown.UiChevronDown, className: "shrink-0 text-muted-foreground" })
138
- ] })
92
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: selected.icon, className: "shrink-0 text-foreground" }),
93
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-left capitalize", children: `${ariaLabel}: ${selected.label}` }),
94
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiChevronDown.UiChevronDown, className: "shrink-0 text-muted-foreground" })
139
95
  ]
140
96
  }
97
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
98
+ IconButton.IconButton,
99
+ {
100
+ icon: selected.icon,
101
+ label: ariaLabel,
102
+ title: triggerTitleText,
103
+ "aria-haspopup": "menu",
104
+ "aria-expanded": open,
105
+ className: triggerClassName,
106
+ ...getReferenceProps()
107
+ }
141
108
  ),
142
- open && /* @__PURE__ */ jsxRuntime.jsxs(
109
+ open && /* @__PURE__ */ jsxRuntime.jsx(react$1.FloatingPortal, { children: /* @__PURE__ */ jsxRuntime.jsx(react$1.FloatingFocusManager, { context, modal: false, initialFocus: selectedIndex, children: /* @__PURE__ */ jsxRuntime.jsxs(
143
110
  "div",
144
111
  {
112
+ ref: refs.setFloating,
145
113
  role: "menu",
146
114
  "aria-label": ariaLabel,
147
- onKeyDown: onMenuKeyDown,
115
+ style: floatingStyles,
148
116
  className: utils.cn(
149
- "absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5",
117
+ "z-[9999] min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5",
150
118
  menuClassName
151
119
  ),
120
+ ...getFloatingProps(),
152
121
  children: [
153
122
  options.map((option, index) => {
154
123
  const active = option.value === value;
@@ -156,24 +125,26 @@ function IconMenuPickerInner({
156
125
  "button",
157
126
  {
158
127
  ref: (node) => {
159
- itemRefs.current[index] = node;
128
+ listRef.current[index] = node;
160
129
  },
161
130
  type: "button",
162
131
  role: "menuitemradio",
163
132
  "aria-checked": active,
164
- tabIndex: active ? 0 : -1,
165
- onClick: () => select(option.value),
166
- onKeyDown: (event) => {
167
- if (event.key === "Enter" || event.key === " ") {
168
- event.preventDefault();
169
- select(option.value);
170
- }
171
- },
133
+ tabIndex: index === activeIndex ? 0 : -1,
172
134
  className: utils.cn(
173
135
  "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors",
174
136
  "hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none",
175
137
  active && "text-foreground"
176
138
  ),
139
+ ...getItemProps({
140
+ onClick: () => select(option.value),
141
+ onKeyDown: (event) => {
142
+ if (event.key === "Enter" || event.key === " ") {
143
+ event.preventDefault();
144
+ select(option.value);
145
+ }
146
+ }
147
+ }),
177
148
  children: [
178
149
  /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: option.icon, className: "shrink-0 text-muted-foreground" }),
179
150
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate capitalize", children: option.label }),
@@ -186,7 +157,7 @@ function IconMenuPickerInner({
186
157
  footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground", children: footer }) : null
187
158
  ]
188
159
  }
189
- )
160
+ ) }) })
190
161
  ] });
191
162
  }
192
163
  const IconMenuPicker = react.forwardRef(IconMenuPickerInner);
@@ -1 +1 @@
1
- {"version":3,"file":"icon-menu-picker.cjs","sources":["../../src/components/icon-menu-picker.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiCheck, UiChevronDown } from \"../icons\";\nimport { Button } from \"./button\";\n\nexport type IconMenuOption<T extends string> = {\n value: T;\n icon: StaticIconComponent;\n label: string;\n description?: string;\n};\n\nexport type IconMenuPickerProps<T extends string> = {\n value: T;\n onChange: (next: T) => void;\n options: IconMenuOption<T>[];\n ariaLabel: string;\n triggerTitle?: string | undefined;\n footer?: ReactNode | undefined;\n showLabel?: boolean | undefined;\n className?: string | undefined;\n triggerClassName?: string | undefined;\n menuClassName?: string | undefined;\n};\n\nfunction IconMenuPickerInner<T extends string>(\n {\n value,\n onChange,\n options,\n ariaLabel,\n triggerTitle,\n footer,\n showLabel,\n className,\n triggerClassName,\n menuClassName,\n }: IconMenuPickerProps<T>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n const [open, setOpen] = useState(false);\n\n useImperativeHandle(ref, () => rootRef.current as HTMLDivElement);\n\n const activeIndex = useMemo(\n () =>\n Math.max(\n 0,\n options.findIndex((option) => option.value === value),\n ),\n [options, value],\n );\n\n const close = useCallback((returnFocus: boolean) => {\n setOpen(false);\n if (returnFocus) triggerRef.current?.focus();\n }, []);\n\n const select = useCallback(\n (next: T) => {\n onChange(next);\n close(true);\n },\n [close, onChange],\n );\n\n useEffect(() => {\n if (!open) return;\n\n const onPointerDown = (event: PointerEvent) => {\n if (event.button !== 0) return;\n if (!rootRef.current?.contains(event.target as Node)) {\n close(false);\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n close(true);\n }\n };\n\n document.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [close, open]);\n\n useEffect(() => {\n if (!open) return;\n const node = itemRefs.current[activeIndex];\n node?.focus();\n }, [activeIndex, open]);\n\n const focusItem = (index: number) => {\n const wrapped = (index + options.length) % options.length;\n itemRefs.current[wrapped]?.focus();\n };\n\n const onMenuKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>) => {\n const focusedIndex = itemRefs.current.findIndex((node) => node === document.activeElement);\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) + 1);\n return;\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) - 1);\n return;\n }\n if (event.key === \"Home\") {\n event.preventDefault();\n focusItem(0);\n return;\n }\n if (event.key === \"End\") {\n event.preventDefault();\n focusItem(options.length - 1);\n return;\n }\n if (event.key === \"Tab\") {\n close(false);\n }\n };\n\n const onTriggerKeyDown = (event: ReactKeyboardEvent<HTMLButtonElement>) => {\n if (event.key === \"ArrowDown\" || event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n setOpen(true);\n }\n };\n\n const selected = options[activeIndex];\n if (!selected) {\n throw new Error(`IconMenuPicker: value \"${value}\" not found in options for ${ariaLabel}`);\n }\n\n return (\n <div ref={rootRef} className={cn(\"relative inline-flex\", showLabel && \"w-full\", className)}>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant=\"ghost\"\n size={showLabel ? \"default\" : \"icon\"}\n aria-label={ariaLabel}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n title={triggerTitle ?? `${ariaLabel}: ${selected.label}`}\n onClick={() => setOpen((current) => !current)}\n onKeyDown={onTriggerKeyDown}\n className={cn(\n \"text-muted-foreground hover:text-foreground\",\n showLabel && \"w-full justify-start\",\n triggerClassName,\n )}\n >\n <Icon icon={selected.icon} {...(showLabel ? { className: \"shrink-0 text-foreground\" } : {})} />\n {showLabel && (\n <>\n <span className=\"min-w-0 flex-1 truncate text-left capitalize\">\n {`${ariaLabel}: ${selected.label}`}\n </span>\n <Icon icon={UiChevronDown} className=\"shrink-0 text-muted-foreground\" />\n </>\n )}\n </Button>\n {open && (\n <div\n role=\"menu\"\n aria-label={ariaLabel}\n onKeyDown={onMenuKeyDown}\n className={cn(\n \"absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n >\n {options.map((option, index) => {\n const active = option.value === value;\n return (\n <button\n key={option.value}\n ref={(node) => {\n itemRefs.current[index] = node;\n }}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={active}\n tabIndex={active ? 0 : -1}\n onClick={() => select(option.value)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n select(option.value);\n }\n }}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors\",\n \"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none\",\n active && \"text-foreground\",\n )}\n >\n <Icon icon={option.icon} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate capitalize\">{option.label}</span>\n {active ? (\n <Icon icon={UiCheck} className=\"shrink-0 text-foreground\" />\n ) : (\n <span className=\"inline-block size-4 shrink-0\" aria-hidden />\n )}\n </button>\n );\n })}\n {footer ? (\n <div className=\"mt-1 border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground\">\n {footer}\n </div>\n ) : null}\n </div>\n )}\n </div>\n );\n}\n\nexport const IconMenuPicker = forwardRef(IconMenuPickerInner) as <T extends string>(\n props: IconMenuPickerProps<T> & { ref?: React.Ref<HTMLDivElement> },\n) => JSX.Element;\n"],"names":["useRef","useState","useImperativeHandle","useMemo","useCallback","useEffect","jsxs","cn","Button","jsx","Icon","Fragment","UiChevronDown","UiCheck","forwardRef"],"mappings":"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"}
1
+ {"version":3,"file":"icon-menu-picker.cjs","sources":["../../src/components/icon-menu-picker.tsx"],"sourcesContent":["import {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport {\n autoUpdate,\n flip,\n FloatingFocusManager,\n FloatingPortal,\n offset,\n shift,\n useClick,\n useDismiss,\n useFloating,\n useInteractions,\n useListNavigation,\n useMergeRefs,\n useRole,\n useTypeahead,\n} from \"@floating-ui/react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiCheck, UiChevronDown } from \"../icons\";\nimport { Button } from \"./button\";\nimport { IconButton } from \"./IconButton\";\n\nexport type IconMenuOption<T extends string> = {\n value: T;\n icon: StaticIconComponent;\n label: string;\n description?: string;\n};\n\nexport type IconMenuPickerProps<T extends string> = {\n value: T;\n onChange: (next: T) => void;\n options: IconMenuOption<T>[];\n ariaLabel: string;\n triggerTitle?: string | undefined;\n footer?: ReactNode | undefined;\n showLabel?: boolean | undefined;\n className?: string | undefined;\n triggerClassName?: string | undefined;\n menuClassName?: string | undefined;\n};\n\nfunction IconMenuPickerInner<T extends string>(\n {\n value,\n onChange,\n options,\n ariaLabel,\n triggerTitle,\n footer,\n showLabel,\n className,\n triggerClassName,\n menuClassName,\n }: IconMenuPickerProps<T>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [open, setOpen] = useState(false);\n // Index of the item highlighted by keyboard navigation. Null until the user\n // arrows/typeaheads, so a pointer-opened menu starts with the selected row\n // focused (selectedIndex below) rather than the first.\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n const listRef = useRef<(HTMLElement | null)[]>([]);\n const labelsRef = useRef<string[]>(options.map((o) => o.label));\n labelsRef.current = options.map((o) => o.label);\n\n const selectedIndex = useMemo(\n () => options.findIndex((option) => option.value === value),\n [options, value],\n );\n\n const { refs, floatingStyles, context } = useFloating<HTMLDivElement>({\n open,\n onOpenChange: setOpen,\n placement: \"bottom-start\",\n whileElementsMounted: autoUpdate,\n // offset(6) reproduces the previous 0.375rem gap; flip opens the menu\n // upward when the trigger is near the bottom edge; shift slides it back\n // on-screen at the left/right edges. The 8px padding keeps a gutter.\n middleware: [offset(6), flip({ padding: 8 }), shift({ padding: 8 })],\n });\n\n const click = useClick(context);\n const dismiss = useDismiss(context);\n const role = useRole(context, { role: \"menu\" });\n const listNav = useListNavigation(context, {\n listRef,\n activeIndex,\n selectedIndex,\n onNavigate: setActiveIndex,\n loop: true,\n });\n const typeahead = useTypeahead(context, {\n listRef: labelsRef,\n activeIndex,\n onMatch: setActiveIndex,\n });\n\n const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([\n click,\n dismiss,\n role,\n listNav,\n typeahead,\n ]);\n\n // Keep the trigger wrapper available both to floating-ui (as the positioning\n // reference) and to the consumer's forwarded ref.\n const referenceRef = useMergeRefs([refs.setReference, forwardedRef]);\n\n const select = (next: T) => {\n onChange(next);\n setOpen(false);\n };\n\n const selected = options[selectedIndex];\n if (!selected) {\n throw new Error(`IconMenuPicker: value \"${value}\" not found in options for ${ariaLabel}`);\n }\n\n const triggerTitleText = triggerTitle ?? `${ariaLabel}: ${selected.label}`;\n\n return (\n <div ref={referenceRef} className={cn(\"relative inline-flex\", showLabel && \"w-full\", className)}>\n {showLabel ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"default\"\n aria-label={ariaLabel}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n title={triggerTitleText}\n className={cn(\n \"w-full justify-start text-muted-foreground hover:text-foreground\",\n triggerClassName,\n )}\n {...getReferenceProps()}\n >\n <Icon icon={selected.icon} className=\"shrink-0 text-foreground\" />\n <span className=\"min-w-0 flex-1 truncate text-left capitalize\">\n {`${ariaLabel}: ${selected.label}`}\n </span>\n <Icon icon={UiChevronDown} className=\"shrink-0 text-muted-foreground\" />\n </Button>\n ) : (\n <IconButton\n icon={selected.icon}\n label={ariaLabel}\n title={triggerTitleText}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={triggerClassName}\n {...getReferenceProps()}\n />\n )}\n {open && (\n <FloatingPortal>\n <FloatingFocusManager context={context} modal={false} initialFocus={selectedIndex}>\n <div\n ref={refs.setFloating}\n role=\"menu\"\n aria-label={ariaLabel}\n style={floatingStyles}\n className={cn(\n \"z-[9999] min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n {...getFloatingProps()}\n >\n {options.map((option, index) => {\n const active = option.value === value;\n return (\n <button\n key={option.value}\n ref={(node) => {\n listRef.current[index] = node;\n }}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={active}\n tabIndex={index === activeIndex ? 0 : -1}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors\",\n \"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none\",\n active && \"text-foreground\",\n )}\n {...getItemProps({\n onClick: () => select(option.value),\n onKeyDown: (event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n select(option.value);\n }\n },\n })}\n >\n <Icon icon={option.icon} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate capitalize\">{option.label}</span>\n {active ? (\n <Icon icon={UiCheck} className=\"shrink-0 text-foreground\" />\n ) : (\n <span className=\"inline-block size-4 shrink-0\" aria-hidden />\n )}\n </button>\n );\n })}\n {footer ? (\n <div className=\"mt-1 border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground\">\n {footer}\n </div>\n ) : null}\n </div>\n </FloatingFocusManager>\n </FloatingPortal>\n )}\n </div>\n );\n}\n\nexport const IconMenuPicker = forwardRef(IconMenuPickerInner) as <T extends string>(\n props: IconMenuPickerProps<T> & { ref?: React.Ref<HTMLDivElement> },\n) => JSX.Element;\n"],"names":["useState","useRef","useMemo","useFloating","autoUpdate","offset","flip","shift","useClick","useDismiss","useRole","useListNavigation","useTypeahead","useInteractions","useMergeRefs","jsxs","cn","Button","jsx","Icon","UiChevronDown","IconButton","FloatingPortal","FloatingFocusManager","UiCheck","forwardRef"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"icon-menu-picker.d.ts","sourceRoot":"","sources":["../../src/components/icon-menu-picker.tsx"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAI9D,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,MAAM,IAAI;IAC7C,KAAK,EAAE,CAAC,CAAC;IACT,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,MAAM,IAAI;IAClD,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,CAAC;AA8MF,eAAO,MAAM,cAAc,EAAsC,CAAC,CAAC,SAAS,MAAM,EAChF,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KAChE,GAAG,CAAC,OAAO,CAAC"}
1
+ {"version":3,"file":"icon-menu-picker.d.ts","sourceRoot":"","sources":["../../src/components/icon-menu-picker.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAkBf,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAK9D,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,MAAM,IAAI;IAC7C,KAAK,EAAE,CAAC,CAAC;IACT,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,MAAM,IAAI;IAClD,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,CAAC;AAoLF,eAAO,MAAM,cAAc,EAAsC,CAAC,CAAC,SAAS,MAAM,EAChF,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KAChE,GAAG,CAAC,OAAO,CAAC"}
@@ -1,8 +1,10 @@
1
- import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { forwardRef, useRef, useState, useImperativeHandle, useMemo, useCallback, useEffect } from "react";
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useState, useRef, useMemo } from "react";
3
+ import { useFloating, autoUpdate, offset, flip, shift, useClick, useDismiss, useRole, useListNavigation, useTypeahead, useInteractions, useMergeRefs, FloatingPortal, FloatingFocusManager } from "@floating-ui/react";
3
4
  import { cn } from "../lib/utils.js";
4
5
  import { Icon } from "../data/Icon.js";
5
6
  import { Button } from "./button.js";
7
+ import { IconButton } from "./IconButton.js";
6
8
  import { UiChevronDown } from "../icons/components/UiChevronDown.js";
7
9
  import { UiCheck } from "../icons/components/UiCheck.js";
8
10
  function IconMenuPickerInner({
@@ -16,137 +18,104 @@ function IconMenuPickerInner({
16
18
  className,
17
19
  triggerClassName,
18
20
  menuClassName
19
- }, ref) {
20
- const rootRef = useRef(null);
21
- const triggerRef = useRef(null);
22
- const itemRefs = useRef([]);
21
+ }, forwardedRef) {
23
22
  const [open, setOpen] = useState(false);
24
- useImperativeHandle(ref, () => rootRef.current);
25
- const activeIndex = useMemo(
26
- () => Math.max(
27
- 0,
28
- options.findIndex((option) => option.value === value)
29
- ),
23
+ const [activeIndex, setActiveIndex] = useState(null);
24
+ const listRef = useRef([]);
25
+ const labelsRef = useRef(options.map((o) => o.label));
26
+ labelsRef.current = options.map((o) => o.label);
27
+ const selectedIndex = useMemo(
28
+ () => options.findIndex((option) => option.value === value),
30
29
  [options, value]
31
30
  );
32
- const close = useCallback((returnFocus) => {
33
- var _a;
31
+ const { refs, floatingStyles, context } = useFloating({
32
+ open,
33
+ onOpenChange: setOpen,
34
+ placement: "bottom-start",
35
+ whileElementsMounted: autoUpdate,
36
+ // offset(6) reproduces the previous 0.375rem gap; flip opens the menu
37
+ // upward when the trigger is near the bottom edge; shift slides it back
38
+ // on-screen at the left/right edges. The 8px padding keeps a gutter.
39
+ middleware: [offset(6), flip({ padding: 8 }), shift({ padding: 8 })]
40
+ });
41
+ const click = useClick(context);
42
+ const dismiss = useDismiss(context);
43
+ const role = useRole(context, { role: "menu" });
44
+ const listNav = useListNavigation(context, {
45
+ listRef,
46
+ activeIndex,
47
+ selectedIndex,
48
+ onNavigate: setActiveIndex,
49
+ loop: true
50
+ });
51
+ const typeahead = useTypeahead(context, {
52
+ listRef: labelsRef,
53
+ activeIndex,
54
+ onMatch: setActiveIndex
55
+ });
56
+ const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([
57
+ click,
58
+ dismiss,
59
+ role,
60
+ listNav,
61
+ typeahead
62
+ ]);
63
+ const referenceRef = useMergeRefs([refs.setReference, forwardedRef]);
64
+ const select = (next) => {
65
+ onChange(next);
34
66
  setOpen(false);
35
- if (returnFocus) (_a = triggerRef.current) == null ? void 0 : _a.focus();
36
- }, []);
37
- const select = useCallback(
38
- (next) => {
39
- onChange(next);
40
- close(true);
41
- },
42
- [close, onChange]
43
- );
44
- useEffect(() => {
45
- if (!open) return;
46
- const onPointerDown = (event) => {
47
- var _a;
48
- if (event.button !== 0) return;
49
- if (!((_a = rootRef.current) == null ? void 0 : _a.contains(event.target))) {
50
- close(false);
51
- }
52
- };
53
- const onKeyDown = (event) => {
54
- if (event.key === "Escape") {
55
- event.preventDefault();
56
- close(true);
57
- }
58
- };
59
- document.addEventListener("pointerdown", onPointerDown);
60
- document.addEventListener("keydown", onKeyDown);
61
- return () => {
62
- document.removeEventListener("pointerdown", onPointerDown);
63
- document.removeEventListener("keydown", onKeyDown);
64
- };
65
- }, [close, open]);
66
- useEffect(() => {
67
- if (!open) return;
68
- const node = itemRefs.current[activeIndex];
69
- node == null ? void 0 : node.focus();
70
- }, [activeIndex, open]);
71
- const focusItem = (index) => {
72
- var _a;
73
- const wrapped = (index + options.length) % options.length;
74
- (_a = itemRefs.current[wrapped]) == null ? void 0 : _a.focus();
75
67
  };
76
- const onMenuKeyDown = (event) => {
77
- const focusedIndex = itemRefs.current.findIndex((node) => node === document.activeElement);
78
- if (event.key === "ArrowDown") {
79
- event.preventDefault();
80
- focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) + 1);
81
- return;
82
- }
83
- if (event.key === "ArrowUp") {
84
- event.preventDefault();
85
- focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) - 1);
86
- return;
87
- }
88
- if (event.key === "Home") {
89
- event.preventDefault();
90
- focusItem(0);
91
- return;
92
- }
93
- if (event.key === "End") {
94
- event.preventDefault();
95
- focusItem(options.length - 1);
96
- return;
97
- }
98
- if (event.key === "Tab") {
99
- close(false);
100
- }
101
- };
102
- const onTriggerKeyDown = (event) => {
103
- if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
104
- event.preventDefault();
105
- setOpen(true);
106
- }
107
- };
108
- const selected = options[activeIndex];
68
+ const selected = options[selectedIndex];
109
69
  if (!selected) {
110
70
  throw new Error(`IconMenuPicker: value "${value}" not found in options for ${ariaLabel}`);
111
71
  }
112
- return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cn("relative inline-flex", showLabel && "w-full", className), children: [
113
- /* @__PURE__ */ jsxs(
72
+ const triggerTitleText = triggerTitle ?? `${ariaLabel}: ${selected.label}`;
73
+ return /* @__PURE__ */ jsxs("div", { ref: referenceRef, className: cn("relative inline-flex", showLabel && "w-full", className), children: [
74
+ showLabel ? /* @__PURE__ */ jsxs(
114
75
  Button,
115
76
  {
116
- ref: triggerRef,
117
77
  type: "button",
118
78
  variant: "ghost",
119
- size: showLabel ? "default" : "icon",
79
+ size: "default",
120
80
  "aria-label": ariaLabel,
121
81
  "aria-haspopup": "menu",
122
82
  "aria-expanded": open,
123
- title: triggerTitle ?? `${ariaLabel}: ${selected.label}`,
124
- onClick: () => setOpen((current) => !current),
125
- onKeyDown: onTriggerKeyDown,
83
+ title: triggerTitleText,
126
84
  className: cn(
127
- "text-muted-foreground hover:text-foreground",
128
- showLabel && "w-full justify-start",
85
+ "w-full justify-start text-muted-foreground hover:text-foreground",
129
86
  triggerClassName
130
87
  ),
88
+ ...getReferenceProps(),
131
89
  children: [
132
- /* @__PURE__ */ jsx(Icon, { icon: selected.icon, ...showLabel ? { className: "shrink-0 text-foreground" } : {} }),
133
- showLabel && /* @__PURE__ */ jsxs(Fragment, { children: [
134
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left capitalize", children: `${ariaLabel}: ${selected.label}` }),
135
- /* @__PURE__ */ jsx(Icon, { icon: UiChevronDown, className: "shrink-0 text-muted-foreground" })
136
- ] })
90
+ /* @__PURE__ */ jsx(Icon, { icon: selected.icon, className: "shrink-0 text-foreground" }),
91
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left capitalize", children: `${ariaLabel}: ${selected.label}` }),
92
+ /* @__PURE__ */ jsx(Icon, { icon: UiChevronDown, className: "shrink-0 text-muted-foreground" })
137
93
  ]
138
94
  }
95
+ ) : /* @__PURE__ */ jsx(
96
+ IconButton,
97
+ {
98
+ icon: selected.icon,
99
+ label: ariaLabel,
100
+ title: triggerTitleText,
101
+ "aria-haspopup": "menu",
102
+ "aria-expanded": open,
103
+ className: triggerClassName,
104
+ ...getReferenceProps()
105
+ }
139
106
  ),
140
- open && /* @__PURE__ */ jsxs(
107
+ open && /* @__PURE__ */ jsx(FloatingPortal, { children: /* @__PURE__ */ jsx(FloatingFocusManager, { context, modal: false, initialFocus: selectedIndex, children: /* @__PURE__ */ jsxs(
141
108
  "div",
142
109
  {
110
+ ref: refs.setFloating,
143
111
  role: "menu",
144
112
  "aria-label": ariaLabel,
145
- onKeyDown: onMenuKeyDown,
113
+ style: floatingStyles,
146
114
  className: cn(
147
- "absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5",
115
+ "z-[9999] min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5",
148
116
  menuClassName
149
117
  ),
118
+ ...getFloatingProps(),
150
119
  children: [
151
120
  options.map((option, index) => {
152
121
  const active = option.value === value;
@@ -154,24 +123,26 @@ function IconMenuPickerInner({
154
123
  "button",
155
124
  {
156
125
  ref: (node) => {
157
- itemRefs.current[index] = node;
126
+ listRef.current[index] = node;
158
127
  },
159
128
  type: "button",
160
129
  role: "menuitemradio",
161
130
  "aria-checked": active,
162
- tabIndex: active ? 0 : -1,
163
- onClick: () => select(option.value),
164
- onKeyDown: (event) => {
165
- if (event.key === "Enter" || event.key === " ") {
166
- event.preventDefault();
167
- select(option.value);
168
- }
169
- },
131
+ tabIndex: index === activeIndex ? 0 : -1,
170
132
  className: cn(
171
133
  "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors",
172
134
  "hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none",
173
135
  active && "text-foreground"
174
136
  ),
137
+ ...getItemProps({
138
+ onClick: () => select(option.value),
139
+ onKeyDown: (event) => {
140
+ if (event.key === "Enter" || event.key === " ") {
141
+ event.preventDefault();
142
+ select(option.value);
143
+ }
144
+ }
145
+ }),
175
146
  children: [
176
147
  /* @__PURE__ */ jsx(Icon, { icon: option.icon, className: "shrink-0 text-muted-foreground" }),
177
148
  /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate capitalize", children: option.label }),
@@ -184,7 +155,7 @@ function IconMenuPickerInner({
184
155
  footer ? /* @__PURE__ */ jsx("div", { className: "mt-1 border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground", children: footer }) : null
185
156
  ]
186
157
  }
187
- )
158
+ ) }) })
188
159
  ] });
189
160
  }
190
161
  const IconMenuPicker = forwardRef(IconMenuPickerInner);
@@ -1 +1 @@
1
- {"version":3,"file":"icon-menu-picker.js","sources":["../../src/components/icon-menu-picker.tsx"],"sourcesContent":["import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiCheck, UiChevronDown } from \"../icons\";\nimport { Button } from \"./button\";\n\nexport type IconMenuOption<T extends string> = {\n value: T;\n icon: StaticIconComponent;\n label: string;\n description?: string;\n};\n\nexport type IconMenuPickerProps<T extends string> = {\n value: T;\n onChange: (next: T) => void;\n options: IconMenuOption<T>[];\n ariaLabel: string;\n triggerTitle?: string | undefined;\n footer?: ReactNode | undefined;\n showLabel?: boolean | undefined;\n className?: string | undefined;\n triggerClassName?: string | undefined;\n menuClassName?: string | undefined;\n};\n\nfunction IconMenuPickerInner<T extends string>(\n {\n value,\n onChange,\n options,\n ariaLabel,\n triggerTitle,\n footer,\n showLabel,\n className,\n triggerClassName,\n menuClassName,\n }: IconMenuPickerProps<T>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const itemRefs = useRef<(HTMLButtonElement | null)[]>([]);\n const [open, setOpen] = useState(false);\n\n useImperativeHandle(ref, () => rootRef.current as HTMLDivElement);\n\n const activeIndex = useMemo(\n () =>\n Math.max(\n 0,\n options.findIndex((option) => option.value === value),\n ),\n [options, value],\n );\n\n const close = useCallback((returnFocus: boolean) => {\n setOpen(false);\n if (returnFocus) triggerRef.current?.focus();\n }, []);\n\n const select = useCallback(\n (next: T) => {\n onChange(next);\n close(true);\n },\n [close, onChange],\n );\n\n useEffect(() => {\n if (!open) return;\n\n const onPointerDown = (event: PointerEvent) => {\n if (event.button !== 0) return;\n if (!rootRef.current?.contains(event.target as Node)) {\n close(false);\n }\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n close(true);\n }\n };\n\n document.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [close, open]);\n\n useEffect(() => {\n if (!open) return;\n const node = itemRefs.current[activeIndex];\n node?.focus();\n }, [activeIndex, open]);\n\n const focusItem = (index: number) => {\n const wrapped = (index + options.length) % options.length;\n itemRefs.current[wrapped]?.focus();\n };\n\n const onMenuKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>) => {\n const focusedIndex = itemRefs.current.findIndex((node) => node === document.activeElement);\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) + 1);\n return;\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n focusItem((focusedIndex < 0 ? activeIndex : focusedIndex) - 1);\n return;\n }\n if (event.key === \"Home\") {\n event.preventDefault();\n focusItem(0);\n return;\n }\n if (event.key === \"End\") {\n event.preventDefault();\n focusItem(options.length - 1);\n return;\n }\n if (event.key === \"Tab\") {\n close(false);\n }\n };\n\n const onTriggerKeyDown = (event: ReactKeyboardEvent<HTMLButtonElement>) => {\n if (event.key === \"ArrowDown\" || event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n setOpen(true);\n }\n };\n\n const selected = options[activeIndex];\n if (!selected) {\n throw new Error(`IconMenuPicker: value \"${value}\" not found in options for ${ariaLabel}`);\n }\n\n return (\n <div ref={rootRef} className={cn(\"relative inline-flex\", showLabel && \"w-full\", className)}>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant=\"ghost\"\n size={showLabel ? \"default\" : \"icon\"}\n aria-label={ariaLabel}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n title={triggerTitle ?? `${ariaLabel}: ${selected.label}`}\n onClick={() => setOpen((current) => !current)}\n onKeyDown={onTriggerKeyDown}\n className={cn(\n \"text-muted-foreground hover:text-foreground\",\n showLabel && \"w-full justify-start\",\n triggerClassName,\n )}\n >\n <Icon icon={selected.icon} {...(showLabel ? { className: \"shrink-0 text-foreground\" } : {})} />\n {showLabel && (\n <>\n <span className=\"min-w-0 flex-1 truncate text-left capitalize\">\n {`${ariaLabel}: ${selected.label}`}\n </span>\n <Icon icon={UiChevronDown} className=\"shrink-0 text-muted-foreground\" />\n </>\n )}\n </Button>\n {open && (\n <div\n role=\"menu\"\n aria-label={ariaLabel}\n onKeyDown={onMenuKeyDown}\n className={cn(\n \"absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n >\n {options.map((option, index) => {\n const active = option.value === value;\n return (\n <button\n key={option.value}\n ref={(node) => {\n itemRefs.current[index] = node;\n }}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={active}\n tabIndex={active ? 0 : -1}\n onClick={() => select(option.value)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n select(option.value);\n }\n }}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors\",\n \"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none\",\n active && \"text-foreground\",\n )}\n >\n <Icon icon={option.icon} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate capitalize\">{option.label}</span>\n {active ? (\n <Icon icon={UiCheck} className=\"shrink-0 text-foreground\" />\n ) : (\n <span className=\"inline-block size-4 shrink-0\" aria-hidden />\n )}\n </button>\n );\n })}\n {footer ? (\n <div className=\"mt-1 border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground\">\n {footer}\n </div>\n ) : null}\n </div>\n )}\n </div>\n );\n}\n\nexport const IconMenuPicker = forwardRef(IconMenuPickerInner) as <T extends string>(\n props: IconMenuPickerProps<T> & { ref?: React.Ref<HTMLDivElement> },\n) => JSX.Element;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"icon-menu-picker.js","sources":["../../src/components/icon-menu-picker.tsx"],"sourcesContent":["import {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport {\n autoUpdate,\n flip,\n FloatingFocusManager,\n FloatingPortal,\n offset,\n shift,\n useClick,\n useDismiss,\n useFloating,\n useInteractions,\n useListNavigation,\n useMergeRefs,\n useRole,\n useTypeahead,\n} from \"@floating-ui/react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiCheck, UiChevronDown } from \"../icons\";\nimport { Button } from \"./button\";\nimport { IconButton } from \"./IconButton\";\n\nexport type IconMenuOption<T extends string> = {\n value: T;\n icon: StaticIconComponent;\n label: string;\n description?: string;\n};\n\nexport type IconMenuPickerProps<T extends string> = {\n value: T;\n onChange: (next: T) => void;\n options: IconMenuOption<T>[];\n ariaLabel: string;\n triggerTitle?: string | undefined;\n footer?: ReactNode | undefined;\n showLabel?: boolean | undefined;\n className?: string | undefined;\n triggerClassName?: string | undefined;\n menuClassName?: string | undefined;\n};\n\nfunction IconMenuPickerInner<T extends string>(\n {\n value,\n onChange,\n options,\n ariaLabel,\n triggerTitle,\n footer,\n showLabel,\n className,\n triggerClassName,\n menuClassName,\n }: IconMenuPickerProps<T>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [open, setOpen] = useState(false);\n // Index of the item highlighted by keyboard navigation. Null until the user\n // arrows/typeaheads, so a pointer-opened menu starts with the selected row\n // focused (selectedIndex below) rather than the first.\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n const listRef = useRef<(HTMLElement | null)[]>([]);\n const labelsRef = useRef<string[]>(options.map((o) => o.label));\n labelsRef.current = options.map((o) => o.label);\n\n const selectedIndex = useMemo(\n () => options.findIndex((option) => option.value === value),\n [options, value],\n );\n\n const { refs, floatingStyles, context } = useFloating<HTMLDivElement>({\n open,\n onOpenChange: setOpen,\n placement: \"bottom-start\",\n whileElementsMounted: autoUpdate,\n // offset(6) reproduces the previous 0.375rem gap; flip opens the menu\n // upward when the trigger is near the bottom edge; shift slides it back\n // on-screen at the left/right edges. The 8px padding keeps a gutter.\n middleware: [offset(6), flip({ padding: 8 }), shift({ padding: 8 })],\n });\n\n const click = useClick(context);\n const dismiss = useDismiss(context);\n const role = useRole(context, { role: \"menu\" });\n const listNav = useListNavigation(context, {\n listRef,\n activeIndex,\n selectedIndex,\n onNavigate: setActiveIndex,\n loop: true,\n });\n const typeahead = useTypeahead(context, {\n listRef: labelsRef,\n activeIndex,\n onMatch: setActiveIndex,\n });\n\n const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([\n click,\n dismiss,\n role,\n listNav,\n typeahead,\n ]);\n\n // Keep the trigger wrapper available both to floating-ui (as the positioning\n // reference) and to the consumer's forwarded ref.\n const referenceRef = useMergeRefs([refs.setReference, forwardedRef]);\n\n const select = (next: T) => {\n onChange(next);\n setOpen(false);\n };\n\n const selected = options[selectedIndex];\n if (!selected) {\n throw new Error(`IconMenuPicker: value \"${value}\" not found in options for ${ariaLabel}`);\n }\n\n const triggerTitleText = triggerTitle ?? `${ariaLabel}: ${selected.label}`;\n\n return (\n <div ref={referenceRef} className={cn(\"relative inline-flex\", showLabel && \"w-full\", className)}>\n {showLabel ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"default\"\n aria-label={ariaLabel}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n title={triggerTitleText}\n className={cn(\n \"w-full justify-start text-muted-foreground hover:text-foreground\",\n triggerClassName,\n )}\n {...getReferenceProps()}\n >\n <Icon icon={selected.icon} className=\"shrink-0 text-foreground\" />\n <span className=\"min-w-0 flex-1 truncate text-left capitalize\">\n {`${ariaLabel}: ${selected.label}`}\n </span>\n <Icon icon={UiChevronDown} className=\"shrink-0 text-muted-foreground\" />\n </Button>\n ) : (\n <IconButton\n icon={selected.icon}\n label={ariaLabel}\n title={triggerTitleText}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={triggerClassName}\n {...getReferenceProps()}\n />\n )}\n {open && (\n <FloatingPortal>\n <FloatingFocusManager context={context} modal={false} initialFocus={selectedIndex}>\n <div\n ref={refs.setFloating}\n role=\"menu\"\n aria-label={ariaLabel}\n style={floatingStyles}\n className={cn(\n \"z-[9999] min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg shadow-black/5\",\n menuClassName,\n )}\n {...getFloatingProps()}\n >\n {options.map((option, index) => {\n const active = option.value === value;\n return (\n <button\n key={option.value}\n ref={(node) => {\n listRef.current[index] = node;\n }}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={active}\n tabIndex={index === activeIndex ? 0 : -1}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm transition-colors\",\n \"hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none\",\n active && \"text-foreground\",\n )}\n {...getItemProps({\n onClick: () => select(option.value),\n onKeyDown: (event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n select(option.value);\n }\n },\n })}\n >\n <Icon icon={option.icon} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1 truncate capitalize\">{option.label}</span>\n {active ? (\n <Icon icon={UiCheck} className=\"shrink-0 text-foreground\" />\n ) : (\n <span className=\"inline-block size-4 shrink-0\" aria-hidden />\n )}\n </button>\n );\n })}\n {footer ? (\n <div className=\"mt-1 border-t border-border/60 px-2 py-1.5 text-[11px] text-muted-foreground\">\n {footer}\n </div>\n ) : null}\n </div>\n </FloatingFocusManager>\n </FloatingPortal>\n )}\n </div>\n );\n}\n\nexport const IconMenuPicker = forwardRef(IconMenuPickerInner) as <T extends string>(\n props: IconMenuPickerProps<T> & { ref?: React.Ref<HTMLDivElement> },\n) => JSX.Element;\n"],"names":[],"mappings":"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"}