@flanksource/clicky-ui 0.2.12 → 0.2.14

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 (173) 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/SecretKeySelector.cjs +151 -0
  14. package/dist/components/SecretKeySelector.cjs.map +1 -0
  15. package/dist/components/SecretKeySelector.d.ts +51 -0
  16. package/dist/components/SecretKeySelector.d.ts.map +1 -0
  17. package/dist/components/SecretKeySelector.js +151 -0
  18. package/dist/components/SecretKeySelector.js.map +1 -0
  19. package/dist/components/WorkloadPicker.cjs +144 -0
  20. package/dist/components/WorkloadPicker.cjs.map +1 -0
  21. package/dist/components/WorkloadPicker.d.ts +52 -0
  22. package/dist/components/WorkloadPicker.d.ts.map +1 -0
  23. package/dist/components/WorkloadPicker.js +144 -0
  24. package/dist/components/WorkloadPicker.js.map +1 -0
  25. package/dist/components/icon-menu-picker.cjs +83 -112
  26. package/dist/components/icon-menu-picker.cjs.map +1 -1
  27. package/dist/components/icon-menu-picker.d.ts.map +1 -1
  28. package/dist/components/icon-menu-picker.js +85 -114
  29. package/dist/components/icon-menu-picker.js.map +1 -1
  30. package/dist/components.cjs +10 -0
  31. package/dist/components.cjs.map +1 -1
  32. package/dist/components.d.ts +3 -0
  33. package/dist/components.d.ts.map +1 -1
  34. package/dist/components.js +11 -1
  35. package/dist/components.js.map +1 -1
  36. package/dist/data/Clicky.cjs +1 -1
  37. package/dist/data/Clicky.cjs.map +1 -1
  38. package/dist/data/Clicky.js +1 -1
  39. package/dist/data/Clicky.js.map +1 -1
  40. package/dist/data/TimeseriesCoreBars.cjs +8 -1
  41. package/dist/data/TimeseriesCoreBars.cjs.map +1 -1
  42. package/dist/data/TimeseriesCoreBars.d.ts.map +1 -1
  43. package/dist/data/TimeseriesCoreBars.js +8 -1
  44. package/dist/data/TimeseriesCoreBars.js.map +1 -1
  45. package/dist/data/Version.cjs +42 -0
  46. package/dist/data/Version.cjs.map +1 -0
  47. package/dist/data/Version.d.ts +36 -0
  48. package/dist/data/Version.d.ts.map +1 -0
  49. package/dist/data/Version.js +42 -0
  50. package/dist/data/Version.js.map +1 -0
  51. package/dist/data/test-runner/TestDetailPanel.cjs +165 -0
  52. package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -0
  53. package/dist/data/test-runner/TestDetailPanel.d.ts +7 -0
  54. package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -0
  55. package/dist/data/test-runner/TestDetailPanel.js +165 -0
  56. package/dist/data/test-runner/TestDetailPanel.js.map +1 -0
  57. package/dist/data/test-runner/TestFailureDetail.cjs +78 -0
  58. package/dist/data/test-runner/TestFailureDetail.cjs.map +1 -0
  59. package/dist/data/test-runner/TestFailureDetail.d.ts +14 -0
  60. package/dist/data/test-runner/TestFailureDetail.d.ts.map +1 -0
  61. package/dist/data/test-runner/TestFailureDetail.js +78 -0
  62. package/dist/data/test-runner/TestFailureDetail.js.map +1 -0
  63. package/dist/data/test-runner/TestFilterBar.cjs +108 -0
  64. package/dist/data/test-runner/TestFilterBar.cjs.map +1 -0
  65. package/dist/data/test-runner/TestFilterBar.d.ts +15 -0
  66. package/dist/data/test-runner/TestFilterBar.d.ts.map +1 -0
  67. package/dist/data/test-runner/TestFilterBar.js +108 -0
  68. package/dist/data/test-runner/TestFilterBar.js.map +1 -0
  69. package/dist/data/test-runner/TestRunSummary.cjs +81 -0
  70. package/dist/data/test-runner/TestRunSummary.cjs.map +1 -0
  71. package/dist/data/test-runner/TestRunSummary.d.ts +19 -0
  72. package/dist/data/test-runner/TestRunSummary.d.ts.map +1 -0
  73. package/dist/data/test-runner/TestRunSummary.js +81 -0
  74. package/dist/data/test-runner/TestRunSummary.js.map +1 -0
  75. package/dist/data/test-runner/TestRunner.cjs +143 -0
  76. package/dist/data/test-runner/TestRunner.cjs.map +1 -0
  77. package/dist/data/test-runner/TestRunner.d.ts +42 -0
  78. package/dist/data/test-runner/TestRunner.d.ts.map +1 -0
  79. package/dist/data/test-runner/TestRunner.fixtures.d.ts +26 -0
  80. package/dist/data/test-runner/TestRunner.fixtures.d.ts.map +1 -0
  81. package/dist/data/test-runner/TestRunner.js +143 -0
  82. package/dist/data/test-runner/TestRunner.js.map +1 -0
  83. package/dist/data/test-runner/TestTree.cjs +42 -0
  84. package/dist/data/test-runner/TestTree.cjs.map +1 -0
  85. package/dist/data/test-runner/TestTree.d.ts +9 -0
  86. package/dist/data/test-runner/TestTree.d.ts.map +1 -0
  87. package/dist/data/test-runner/TestTree.js +42 -0
  88. package/dist/data/test-runner/TestTree.js.map +1 -0
  89. package/dist/data/test-runner/TestTreeNode.cjs +99 -0
  90. package/dist/data/test-runner/TestTreeNode.cjs.map +1 -0
  91. package/dist/data/test-runner/TestTreeNode.d.ts +12 -0
  92. package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -0
  93. package/dist/data/test-runner/TestTreeNode.js +99 -0
  94. package/dist/data/test-runner/TestTreeNode.js.map +1 -0
  95. package/dist/data/test-runner/adapter.cjs +57 -0
  96. package/dist/data/test-runner/adapter.cjs.map +1 -0
  97. package/dist/data/test-runner/adapter.d.ts +60 -0
  98. package/dist/data/test-runner/adapter.d.ts.map +1 -0
  99. package/dist/data/test-runner/adapter.js +57 -0
  100. package/dist/data/test-runner/adapter.js.map +1 -0
  101. package/dist/data/test-runner/context.cjs +15 -0
  102. package/dist/data/test-runner/context.cjs.map +1 -0
  103. package/dist/data/test-runner/context.d.ts +40 -0
  104. package/dist/data/test-runner/context.d.ts.map +1 -0
  105. package/dist/data/test-runner/context.js +15 -0
  106. package/dist/data/test-runner/context.js.map +1 -0
  107. package/dist/data/test-runner/filterState.cjs +47 -0
  108. package/dist/data/test-runner/filterState.cjs.map +1 -0
  109. package/dist/data/test-runner/filterState.d.ts +14 -0
  110. package/dist/data/test-runner/filterState.d.ts.map +1 -0
  111. package/dist/data/test-runner/filterState.js +47 -0
  112. package/dist/data/test-runner/filterState.js.map +1 -0
  113. package/dist/data/test-runner/frameworkIcon.cjs +27 -0
  114. package/dist/data/test-runner/frameworkIcon.cjs.map +1 -0
  115. package/dist/data/test-runner/frameworkIcon.d.ts +3 -0
  116. package/dist/data/test-runner/frameworkIcon.d.ts.map +1 -0
  117. package/dist/data/test-runner/frameworkIcon.js +27 -0
  118. package/dist/data/test-runner/frameworkIcon.js.map +1 -0
  119. package/dist/data/test-runner/index.d.ts +14 -0
  120. package/dist/data/test-runner/index.d.ts.map +1 -0
  121. package/dist/data/test-runner/status.cjs +234 -0
  122. package/dist/data/test-runner/status.cjs.map +1 -0
  123. package/dist/data/test-runner/status.d.ts +52 -0
  124. package/dist/data/test-runner/status.d.ts.map +1 -0
  125. package/dist/data/test-runner/status.js +234 -0
  126. package/dist/data/test-runner/status.js.map +1 -0
  127. package/dist/data/test-runner/types.d.ts +148 -0
  128. package/dist/data/test-runner/types.d.ts.map +1 -0
  129. package/dist/data/version-info.cjs +26 -0
  130. package/dist/data/version-info.cjs.map +1 -0
  131. package/dist/data/version-info.d.ts +11 -0
  132. package/dist/data/version-info.d.ts.map +1 -0
  133. package/dist/data/version-info.js +26 -0
  134. package/dist/data/version-info.js.map +1 -0
  135. package/dist/data.cjs +49 -0
  136. package/dist/data.cjs.map +1 -1
  137. package/dist/data.d.ts +3 -0
  138. package/dist/data.d.ts.map +1 -1
  139. package/dist/data.js +50 -1
  140. package/dist/data.js.map +1 -1
  141. package/dist/index.cjs +59 -0
  142. package/dist/index.cjs.map +1 -1
  143. package/dist/index.js +60 -1
  144. package/dist/index.js.map +1 -1
  145. package/dist/overlay/DropdownMenu.cjs +57 -30
  146. package/dist/overlay/DropdownMenu.cjs.map +1 -1
  147. package/dist/overlay/DropdownMenu.d.ts.map +1 -1
  148. package/dist/overlay/DropdownMenu.js +57 -30
  149. package/dist/overlay/DropdownMenu.js.map +1 -1
  150. package/dist/overlay/Modal.cjs +62 -6
  151. package/dist/overlay/Modal.cjs.map +1 -1
  152. package/dist/overlay/Modal.d.ts +19 -1
  153. package/dist/overlay/Modal.d.ts.map +1 -1
  154. package/dist/overlay/Modal.js +62 -6
  155. package/dist/overlay/Modal.js.map +1 -1
  156. package/dist/rpc/CommandForm.cjs +2 -2
  157. package/dist/rpc/CommandForm.cjs.map +1 -1
  158. package/dist/rpc/CommandForm.d.ts.map +1 -1
  159. package/dist/rpc/CommandForm.js +3 -3
  160. package/dist/rpc/CommandForm.js.map +1 -1
  161. package/dist/rpc/formMetadata.cjs +5 -4
  162. package/dist/rpc/formMetadata.cjs.map +1 -1
  163. package/dist/rpc/formMetadata.d.ts.map +1 -1
  164. package/dist/rpc/formMetadata.js +6 -5
  165. package/dist/rpc/formMetadata.js.map +1 -1
  166. package/dist/rpc/types.cjs +4 -0
  167. package/dist/rpc/types.cjs.map +1 -1
  168. package/dist/rpc/types.d.ts +10 -0
  169. package/dist/rpc/types.d.ts.map +1 -1
  170. package/dist/rpc/types.js +5 -1
  171. package/dist/rpc/types.js.map +1 -1
  172. package/dist/styles.css +1 -1
  173. package/package.json +3 -1
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import { useSort } from "./hooks/use-sort.js";
8
8
  import { useHistoryRoute } from "./hooks/use-history-route.js";
9
9
  import { useTaskRun, useTaskRuns } from "./hooks/use-task-run.js";
10
10
  import { Button, buttonVariants } from "./components/button.js";
11
+ import { IconButton } from "./components/IconButton.js";
11
12
  import { SplitButton } from "./components/SplitButton.js";
12
13
  import { CLICKY_FORMAT_OPTIONS, FormatOptionsDropdown } from "./components/FormatOptionsDropdown.js";
13
14
  import { DateField } from "./components/DateField.js";
@@ -19,6 +20,8 @@ import { FilterBar } from "./components/FilterBar.js";
19
20
  import { MultiSelect } from "./components/MultiSelect.js";
20
21
  import { Select } from "./components/select.js";
21
22
  import { Combobox } from "./components/Combobox.js";
23
+ import { WorkloadPicker, kindForValue, loadedWorkloads, parseWorkloadKey, workloadKey } from "./components/WorkloadPicker.js";
24
+ import { SecretKeySelector } from "./components/SecretKeySelector.js";
22
25
  import { ThemeSwitcher } from "./components/theme-switcher.js";
23
26
  import { DensitySwitcher } from "./components/density-switcher.js";
24
27
  import { IconMenuPicker } from "./components/icon-menu-picker.js";
@@ -60,6 +63,8 @@ import { TimeseriesPanel, resolveBreakdown, resolveSeries } from "./data/Timeser
60
63
  import { TimeseriesGauge } from "./data/TimeseriesGauge.js";
61
64
  import { TimeseriesCoreBars, deriveCoreBars } from "./data/TimeseriesCoreBars.js";
62
65
  import { StackedStatusBar, StatusRows, segment } from "./data/StatusBreakdown.js";
66
+ import { Version } from "./data/Version.js";
67
+ import { getVersionInfo } from "./data/version-info.js";
63
68
  import { Tree } from "./data/Tree.js";
64
69
  import { TreeNode } from "./data/TreeNode.js";
65
70
  import { TreeGroupHeader } from "./data/TreeGroupHeader.js";
@@ -95,6 +100,15 @@ import { useOpenAPI, useOperationById, useOperations } from "./rpc/useOperations
95
100
  import { OperationsApiClientError, createOperationsApiClient } from "./rpc/apiClient.js";
96
101
  import { filterOperationsByDomain, findDetailEndpoint, findListEndpoint, normalizeRows, parseJsonBody } from "./rpc/classify.js";
97
102
  import { isPositionalParam } from "./rpc/types.js";
103
+ import { TestAttempts, TestFailureDetail, TestOutput } from "./data/test-runner/TestFailureDetail.js";
104
+ import { TestDetailPanel } from "./data/test-runner/TestDetailPanel.js";
105
+ import { TestFilterBar } from "./data/test-runner/TestFilterBar.js";
106
+ import { TestNodeAdapterRegistry, createTestRunnerRegistry } from "./data/test-runner/adapter.js";
107
+ import { TestRunSummary } from "./data/test-runner/TestRunSummary.js";
108
+ import { TestRunner } from "./data/test-runner/TestRunner.js";
109
+ import { TestRunnerProvider, useTestRunner } from "./data/test-runner/context.js";
110
+ import { TestTree } from "./data/test-runner/TestTree.js";
111
+ import { TestTreeNode } from "./data/test-runner/TestTreeNode.js";
98
112
  import { UiActivity } from "./icons/components/UiActivity.js";
99
113
  import { UiAdd, UiAddFilled } from "./icons/components/UiAdd.js";
100
114
  import { UiArchive, UiArchiveFilled } from "./icons/components/UiArchive.js";
@@ -355,6 +369,9 @@ import { UiZap, UiZapFilled } from "./icons/components/UiZap.js";
355
369
  import { UiZoomIn, UiZoomInFilled } from "./icons/components/UiZoomIn.js";
356
370
  import { UiZoomOut, UiZoomOutFilled } from "./icons/components/UiZoomOut.js";
357
371
  import { changeIconAliases, getChangeIcon } from "./icons/changeIconAliases.js";
372
+ import { collapseSingleChildChains, collectFrameworks, filterTests, formatCount, formatTestDuration, hasFailed, hasPending, humanizeName, statusIconFor, statusToneFor, sum, sumNonTaskTests, testStatus, totalDuration } from "./data/test-runner/status.js";
373
+ import { cycleFilterState, decodeFilterState, emptyTestFilters, encodeFilterState, matchesFilterState } from "./data/test-runner/filterState.js";
374
+ import { frameworkIcon } from "./data/test-runner/frameworkIcon.js";
358
375
  export {
359
376
  ACCEPT_OPTIONS,
360
377
  AVATAR_PALETTE,
@@ -398,6 +415,7 @@ export {
398
415
  HarPanel,
399
416
  HoverCard,
400
417
  Icon,
418
+ IconButton,
401
419
  IconMenuPicker,
402
420
  InlineError,
403
421
  JsonSchemaForm,
@@ -424,6 +442,7 @@ export {
424
442
  RangeSlider,
425
443
  SIZE_TOKENS,
426
444
  STATUS_TOKEN_GROUPS,
445
+ SecretKeySelector,
427
446
  Section,
428
447
  Select,
429
448
  SignedDeltaBar,
@@ -439,6 +458,17 @@ export {
439
458
  TagList,
440
459
  TaskManager,
441
460
  TaskProgress,
461
+ TestAttempts,
462
+ TestDetailPanel,
463
+ TestFailureDetail,
464
+ TestFilterBar,
465
+ TestNodeAdapterRegistry,
466
+ TestOutput,
467
+ TestRunSummary,
468
+ TestRunner,
469
+ TestRunnerProvider,
470
+ TestTree,
471
+ TestTreeNode,
442
472
  ThemeProvider,
443
473
  ThemeSwitcher,
444
474
  TimeRange,
@@ -939,6 +969,8 @@ export {
939
969
  UiZoomOut,
940
970
  UiZoomOutFilled,
941
971
  VIEW_OPTIONS,
972
+ Version,
973
+ WorkloadPicker,
942
974
  allGroupsTerminal,
943
975
  badgeVariants,
944
976
  bucketTasks,
@@ -946,37 +978,55 @@ export {
946
978
  changeIconAliases,
947
979
  chooseTimestampFormat,
948
980
  cn,
981
+ collapseSingleChildChains,
982
+ collectFrameworks,
949
983
  compactStackPath,
950
984
  countGoroutinesByState,
951
985
  countProcesses,
952
986
  countStackByState,
953
987
  countThreadsByState,
954
988
  createOperationsApiClient,
989
+ createTestRunnerRegistry,
990
+ cycleFilterState,
991
+ decodeFilterState,
955
992
  deriveCoreBars,
956
993
  detectDumpFormat,
957
994
  effectiveProperties,
995
+ emptyTestFilters,
996
+ encodeFilterState,
958
997
  filterOperationsByDomain,
998
+ filterTests,
959
999
  findDetailEndpoint,
960
1000
  findListEndpoint,
961
1001
  findProcessByPID,
962
1002
  fnv1a32,
963
1003
  formatBytes,
964
1004
  formatBytesPerSecond,
1005
+ formatCount,
965
1006
  formatDateTimeRelative,
966
1007
  formatDuration,
967
1008
  formatPropertyLabel,
968
1009
  formatRelativeTime,
969
1010
  formatShort,
1011
+ formatTestDuration,
970
1012
  formatTimestamp,
971
1013
  formatUnit,
972
1014
  frameHasSource,
1015
+ frameworkIcon,
973
1016
  getChangeIcon,
1017
+ getVersionInfo,
1018
+ hasFailed,
1019
+ hasPending,
1020
+ humanizeName,
974
1021
  inferColumns,
975
1022
  isApplicationStackFrame,
976
1023
  isOpenStringMap,
977
1024
  isPositionalParam,
978
1025
  isTerminalStatus,
1026
+ kindForValue,
1027
+ loadedWorkloads,
979
1028
  logLevelColor,
1029
+ matchesFilterState,
980
1030
  matchesIf,
981
1031
  modeToFormat,
982
1032
  normalizeErrorDiagnostics,
@@ -993,6 +1043,7 @@ export {
993
1043
  parseJvmThreadDump,
994
1044
  parseStackDump,
995
1045
  parseTimestamp,
1046
+ parseWorkloadKey,
996
1047
  processLabel,
997
1048
  processStateColor,
998
1049
  processStateIcon,
@@ -1003,12 +1054,18 @@ export {
1003
1054
  segment,
1004
1055
  setFallbackIconProvider,
1005
1056
  splitTagToken,
1057
+ statusIconFor,
1058
+ statusToneFor,
1059
+ sum,
1060
+ sumNonTaskTests,
1006
1061
  tagActionsFromRecord,
1007
1062
  tagFilterTokens,
1008
1063
  taskSegments,
1009
1064
  taskStatusBg,
1010
1065
  taskStatusColor,
1011
1066
  taskStatusIcon,
1067
+ testStatus,
1068
+ totalDuration,
1012
1069
  useDensity,
1013
1070
  useDensityValue,
1014
1071
  useHistoryRoute,
@@ -1022,6 +1079,8 @@ export {
1022
1079
  useTagActionsValue,
1023
1080
  useTaskRun,
1024
1081
  useTaskRuns,
1025
- useTheme
1082
+ useTestRunner,
1083
+ useTheme,
1084
+ workloadKey
1026
1085
  };
1027
1086
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -2,6 +2,7 @@
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 button = require("../components/button.cjs");
7
8
  const Icon = require("../data/Icon.cjs");
@@ -22,50 +23,61 @@ function DropdownMenu({
22
23
  menuClassName
23
24
  }) {
24
25
  const [open, setOpen] = react.useState(false);
25
- const wrapperRef = react.useRef(null);
26
+ const [activeIndex, setActiveIndex] = react.useState(null);
27
+ const listRef = react.useRef([]);
26
28
  const onOpenChangeRef = react.useRef(onOpenChange);
27
29
  onOpenChangeRef.current = onOpenChange;
28
30
  react.useEffect(() => {
29
31
  var _a;
30
32
  (_a = onOpenChangeRef.current) == null ? void 0 : _a.call(onOpenChangeRef, open);
31
33
  }, [open]);
32
- react.useEffect(() => {
33
- if (!open) return;
34
- const onMouseDown = (e) => {
35
- var _a;
36
- if (!((_a = wrapperRef.current) == null ? void 0 : _a.contains(e.target))) setOpen(false);
37
- };
38
- const onKeyDown = (e) => {
39
- if (e.key === "Escape") setOpen(false);
40
- };
41
- document.addEventListener("mousedown", onMouseDown);
42
- document.addEventListener("keydown", onKeyDown);
43
- return () => {
44
- document.removeEventListener("mousedown", onMouseDown);
45
- document.removeEventListener("keydown", onKeyDown);
46
- };
47
- }, [open]);
34
+ const { refs, floatingStyles, context } = react$1.useFloating({
35
+ open,
36
+ onOpenChange: setOpen,
37
+ // bottom-end / bottom-start reproduce the previous right/left alignment.
38
+ placement: align === "right" ? "bottom-end" : "bottom-start",
39
+ whileElementsMounted: react$1.autoUpdate,
40
+ // offset(4) the previous mt-1; flip opens upward at the bottom edge and
41
+ // shift slides the menu back on-screen at the horizontal edges.
42
+ middleware: [react$1.offset(4), react$1.flip({ padding: 8 }), react$1.shift({ padding: 8 })]
43
+ });
44
+ const click = react$1.useClick(context);
45
+ const dismiss = react$1.useDismiss(context);
46
+ const role = react$1.useRole(context, { role: "menu" });
47
+ const listNav = react$1.useListNavigation(context, {
48
+ listRef,
49
+ activeIndex,
50
+ onNavigate: setActiveIndex,
51
+ loop: true
52
+ });
53
+ const { getReferenceProps, getFloatingProps, getItemProps } = react$1.useInteractions([
54
+ click,
55
+ dismiss,
56
+ role,
57
+ listNav
58
+ ]);
48
59
  const closeMenu = () => setOpen(false);
49
- const toggle = () => setOpen((v) => !v);
50
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("relative inline-flex", className), ref: wrapperRef, children: [
60
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("relative inline-flex", className), children: [
51
61
  trigger ? /* @__PURE__ */ jsxRuntime.jsx(
52
62
  "span",
53
63
  {
54
- className: "contents",
55
- onClick: toggle,
64
+ ref: refs.setReference,
65
+ className: "inline-flex",
56
66
  "aria-haspopup": "menu",
57
67
  "aria-expanded": open,
68
+ ...getReferenceProps(),
58
69
  children: trigger
59
70
  }
60
71
  ) : /* @__PURE__ */ jsxRuntime.jsxs(
61
72
  button.Button,
62
73
  {
74
+ ref: refs.setReference,
63
75
  variant,
64
76
  size,
65
- onClick: toggle,
66
77
  title,
67
78
  "aria-haspopup": "menu",
68
79
  "aria-expanded": open,
80
+ ...getReferenceProps(),
69
81
  children: [
70
82
  icon && /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { ...typeof icon === "string" ? { name: icon } : { icon } }),
71
83
  label,
@@ -73,27 +85,42 @@ function DropdownMenu({
73
85
  ]
74
86
  }
75
87
  ),
76
- open && /* @__PURE__ */ jsxRuntime.jsx(
88
+ open && /* @__PURE__ */ jsxRuntime.jsx(react$1.FloatingPortal, { children: /* @__PURE__ */ jsxRuntime.jsx(react$1.FloatingFocusManager, { context, modal: false, children: /* @__PURE__ */ jsxRuntime.jsx(
77
89
  "div",
78
90
  {
91
+ ref: refs.setFloating,
79
92
  role: "menu",
93
+ style: floatingStyles,
80
94
  className: utils.cn(
81
- "absolute z-20 mt-1 min-w-[8rem] rounded-md border border-border bg-popover py-1 shadow-md",
82
- align === "right" ? "right-0" : "left-0",
95
+ "z-[9999] min-w-[8rem] rounded-md border border-border bg-popover py-1 shadow-md",
83
96
  menuClassName
84
97
  ),
98
+ ...getFloatingProps(),
85
99
  children: children ? children(closeMenu) : items == null ? void 0 : items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsxs(
86
100
  "button",
87
101
  {
102
+ ref: (node) => {
103
+ listRef.current[i] = node;
104
+ },
88
105
  type: "button",
89
106
  role: "menuitem",
107
+ tabIndex: i === activeIndex ? 0 : -1,
90
108
  disabled: item.disabled,
91
109
  title: item.title,
92
- onClick: () => {
93
- item.onSelect();
94
- closeMenu();
95
- },
96
110
  className: "flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50",
111
+ ...getItemProps({
112
+ onClick: () => {
113
+ item.onSelect();
114
+ closeMenu();
115
+ },
116
+ onKeyDown: (event) => {
117
+ if (event.key === "Enter" || event.key === " ") {
118
+ event.preventDefault();
119
+ item.onSelect();
120
+ closeMenu();
121
+ }
122
+ }
123
+ }),
97
124
  children: [
98
125
  item.icon && /* @__PURE__ */ jsxRuntime.jsx(
99
126
  Icon.Icon,
@@ -107,7 +134,7 @@ function DropdownMenu({
107
134
  i
108
135
  ))
109
136
  }
110
- )
137
+ ) }) })
111
138
  ] });
112
139
  }
113
140
  exports.DropdownMenu = DropdownMenu;
@@ -1 +1 @@
1
- {"version":3,"file":"DropdownMenu.cjs","sources":["../../src/overlay/DropdownMenu.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button, type ButtonProps } from \"../components/button\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiChevronDown } from \"../icons\";\n\nexport type DropdownMenuItem = {\n /** Visible label. */\n label: ReactNode;\n /** Called when the item is chosen. */\n onSelect: () => void;\n /** Iconify name or imported icon component rendered before the label. */\n icon?: string | StaticIconComponent;\n /** Browser tooltip for the item. */\n title?: string;\n /** Disable selection. */\n disabled?: boolean;\n};\n\nexport type DropdownMenuProps = {\n /** Trigger label. Ignored when `trigger` is provided. */\n label?: ReactNode;\n /** Icon shown before the trigger label. Ignored when `trigger` is provided. */\n icon?: string | StaticIconComponent;\n /** Hide the trailing chevron on the default trigger. */\n hideChevron?: boolean;\n /** Variant forwarded to the default Button trigger. */\n variant?: ButtonProps[\"variant\"];\n /** Size forwarded to the default Button trigger. */\n size?: ButtonProps[\"size\"];\n /** Fully custom trigger. Receives no props; the menu wraps it with open state. */\n trigger?: ReactNode;\n /** Declarative menu items. Provide this or `children`, not both. */\n items?: DropdownMenuItem[];\n /** Custom menu content. Use `closeMenu` to dismiss after a selection. */\n children?: (closeMenu: () => void) => ReactNode;\n /** Horizontal alignment of the menu relative to the trigger. */\n align?: \"left\" | \"right\";\n /** Notified whenever the menu opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /** Browser tooltip / accessible label for the default trigger. */\n title?: string;\n /** Classes applied to the wrapper. */\n className?: string;\n /** Classes applied to the floating menu. */\n menuClassName?: string;\n};\n\nexport function DropdownMenu({\n label,\n icon,\n hideChevron = false,\n variant = \"outline\",\n size = \"sm\",\n trigger,\n items,\n children,\n align = \"right\",\n onOpenChange,\n title,\n className,\n menuClassName,\n}: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n const onOpenChangeRef = useRef(onOpenChange);\n onOpenChangeRef.current = onOpenChange;\n useEffect(() => {\n onOpenChangeRef.current?.(open);\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n const onMouseDown = (e: MouseEvent) => {\n if (!wrapperRef.current?.contains(e.target as Node)) setOpen(false);\n };\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"mousedown\", onMouseDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onMouseDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [open]);\n\n const closeMenu = () => setOpen(false);\n const toggle = () => setOpen((v) => !v);\n\n return (\n <div className={cn(\"relative inline-flex\", className)} ref={wrapperRef}>\n {trigger ? (\n <span\n className=\"contents\"\n onClick={toggle}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n >\n {trigger}\n </span>\n ) : (\n <Button\n variant={variant}\n size={size}\n onClick={toggle}\n title={title}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n >\n {icon && <Icon {...(typeof icon === \"string\" ? { name: icon } : { icon })} />}\n {label}\n {!hideChevron && <Icon icon={UiChevronDown} />}\n </Button>\n )}\n {open && (\n <div\n role=\"menu\"\n className={cn(\n \"absolute z-20 mt-1 min-w-[8rem] rounded-md border border-border bg-popover py-1 shadow-md\",\n align === \"right\" ? \"right-0\" : \"left-0\",\n menuClassName,\n )}\n >\n {children\n ? children(closeMenu)\n : items?.map((item, i) => (\n <button\n key={i}\n type=\"button\"\n role=\"menuitem\"\n disabled={item.disabled}\n title={item.title}\n onClick={() => {\n item.onSelect();\n closeMenu();\n }}\n className=\"flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n {item.icon && (\n <Icon\n {...(typeof item.icon === \"string\"\n ? { name: item.icon }\n : { icon: item.icon })}\n />\n )}\n {item.label}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"names":["useState","useRef","useEffect","jsxs","cn","jsx","Button","Icon","UiChevronDown"],"mappings":"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"}
1
+ {"version":3,"file":"DropdownMenu.cjs","sources":["../../src/overlay/DropdownMenu.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactNode } 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 useRole,\n} from \"@floating-ui/react\";\nimport { cn } from \"../lib/utils\";\nimport { Button, type ButtonProps } from \"../components/button\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiChevronDown } from \"../icons\";\n\nexport type DropdownMenuItem = {\n /** Visible label. */\n label: ReactNode;\n /** Called when the item is chosen. */\n onSelect: () => void;\n /** Iconify name or imported icon component rendered before the label. */\n icon?: string | StaticIconComponent;\n /** Browser tooltip for the item. */\n title?: string;\n /** Disable selection. */\n disabled?: boolean;\n};\n\nexport type DropdownMenuProps = {\n /** Trigger label. Ignored when `trigger` is provided. */\n label?: ReactNode;\n /** Icon shown before the trigger label. Ignored when `trigger` is provided. */\n icon?: string | StaticIconComponent;\n /** Hide the trailing chevron on the default trigger. */\n hideChevron?: boolean;\n /** Variant forwarded to the default Button trigger. */\n variant?: ButtonProps[\"variant\"];\n /** Size forwarded to the default Button trigger. */\n size?: ButtonProps[\"size\"];\n /** Fully custom trigger. Receives no props; the menu wraps it with open state. */\n trigger?: ReactNode;\n /** Declarative menu items. Provide this or `children`, not both. */\n items?: DropdownMenuItem[];\n /** Custom menu content. Use `closeMenu` to dismiss after a selection. */\n children?: (closeMenu: () => void) => ReactNode;\n /** Horizontal alignment of the menu relative to the trigger. */\n align?: \"left\" | \"right\";\n /** Notified whenever the menu opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /** Browser tooltip / accessible label for the default trigger. */\n title?: string;\n /** Classes applied to the wrapper. */\n className?: string;\n /** Classes applied to the floating menu. */\n menuClassName?: string;\n};\n\nexport function DropdownMenu({\n label,\n icon,\n hideChevron = false,\n variant = \"outline\",\n size = \"sm\",\n trigger,\n items,\n children,\n align = \"right\",\n onOpenChange,\n title,\n className,\n menuClassName,\n}: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n const listRef = useRef<(HTMLElement | null)[]>([]);\n\n const onOpenChangeRef = useRef(onOpenChange);\n onOpenChangeRef.current = onOpenChange;\n useEffect(() => {\n onOpenChangeRef.current?.(open);\n }, [open]);\n\n const { refs, floatingStyles, context } = useFloating<HTMLDivElement>({\n open,\n onOpenChange: setOpen,\n // bottom-end / bottom-start reproduce the previous right/left alignment.\n placement: align === \"right\" ? \"bottom-end\" : \"bottom-start\",\n whileElementsMounted: autoUpdate,\n // offset(4) the previous mt-1; flip opens upward at the bottom edge and\n // shift slides the menu back on-screen at the horizontal edges.\n middleware: [offset(4), 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 onNavigate: setActiveIndex,\n loop: true,\n });\n\n const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([\n click,\n dismiss,\n role,\n listNav,\n ]);\n\n const closeMenu = () => setOpen(false);\n\n return (\n <div className={cn(\"relative inline-flex\", className)}>\n {trigger ? (\n <span\n ref={refs.setReference}\n className=\"inline-flex\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n {...getReferenceProps()}\n >\n {trigger}\n </span>\n ) : (\n <Button\n ref={refs.setReference as React.Ref<HTMLButtonElement>}\n variant={variant}\n size={size}\n title={title}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n {...getReferenceProps()}\n >\n {icon && <Icon {...(typeof icon === \"string\" ? { name: icon } : { icon })} />}\n {label}\n {!hideChevron && <Icon icon={UiChevronDown} />}\n </Button>\n )}\n {open && (\n <FloatingPortal>\n <FloatingFocusManager context={context} modal={false}>\n <div\n ref={refs.setFloating}\n role=\"menu\"\n style={floatingStyles}\n className={cn(\n \"z-[9999] min-w-[8rem] rounded-md border border-border bg-popover py-1 shadow-md\",\n menuClassName,\n )}\n {...getFloatingProps()}\n >\n {children\n ? children(closeMenu)\n : items?.map((item, i) => (\n <button\n key={i}\n ref={(node) => {\n listRef.current[i] = node;\n }}\n type=\"button\"\n role=\"menuitem\"\n tabIndex={i === activeIndex ? 0 : -1}\n disabled={item.disabled}\n title={item.title}\n className=\"flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n {...getItemProps({\n onClick: () => {\n item.onSelect();\n closeMenu();\n },\n onKeyDown: (event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n item.onSelect();\n closeMenu();\n }\n },\n })}\n >\n {item.icon && (\n <Icon\n {...(typeof item.icon === \"string\"\n ? { name: item.icon }\n : { icon: item.icon })}\n />\n )}\n {item.label}\n </button>\n ))}\n </div>\n </FloatingFocusManager>\n </FloatingPortal>\n )}\n </div>\n );\n}\n"],"names":["useState","useRef","useEffect","useFloating","autoUpdate","offset","flip","shift","useClick","useDismiss","useRole","useListNavigation","useInteractions","cn","jsx","jsxs","Button","Icon","UiChevronDown","FloatingPortal","FloatingFocusManager"],"mappings":";;;;;;;;;AA8DO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,UAAU;AAAA,EACV,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAsB;AACpB,QAAM,CAAC,MAAM,OAAO,IAAIA,MAAAA,SAAS,KAAK;AACtC,QAAM,CAAC,aAAa,cAAc,IAAIA,MAAAA,SAAwB,IAAI;AAClE,QAAM,UAAUC,MAAAA,OAA+B,EAAE;AAEjD,QAAM,kBAAkBA,MAAAA,OAAO,YAAY;AAC3C,kBAAgB,UAAU;AAC1BC,QAAAA,UAAU,MAAM;;AACd,0BAAgB,YAAhB,yCAA0B;AAAA,EAC5B,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,EAAE,MAAM,gBAAgB,QAAA,IAAYC,QAAAA,YAA4B;AAAA,IACpE;AAAA,IACA,cAAc;AAAA;AAAA,IAEd,WAAW,UAAU,UAAU,eAAe;AAAA,IAC9C,sBAAsBC,QAAAA;AAAAA;AAAAA;AAAAA,IAGtB,YAAY,CAACC,QAAAA,OAAO,CAAC,GAAGC,QAAAA,KAAK,EAAE,SAAS,EAAA,CAAG,GAAGC,QAAAA,MAAM,EAAE,SAAS,EAAA,CAAG,CAAC;AAAA,EAAA,CACpE;AAED,QAAM,QAAQC,QAAAA,SAAS,OAAO;AAC9B,QAAM,UAAUC,QAAAA,WAAW,OAAO;AAClC,QAAM,OAAOC,QAAAA,QAAQ,SAAS,EAAE,MAAM,QAAQ;AAC9C,QAAM,UAAUC,QAAAA,kBAAkB,SAAS;AAAA,IACzC;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,MAAM;AAAA,EAAA,CACP;AAED,QAAM,EAAE,mBAAmB,kBAAkB,aAAA,IAAiBC,QAAAA,gBAAgB;AAAA,IAC5E;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AAED,QAAM,YAAY,MAAM,QAAQ,KAAK;AAErC,yCACG,OAAA,EAAI,WAAWC,MAAAA,GAAG,wBAAwB,SAAS,GACjD,UAAA;AAAA,IAAA,UACCC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAK,KAAK;AAAA,QACV,WAAU;AAAA,QACV,iBAAc;AAAA,QACd,iBAAe;AAAA,QACd,GAAG,kBAAA;AAAA,QAEH,UAAA;AAAA,MAAA;AAAA,IAAA,IAGHC,2BAAAA;AAAAA,MAACC,OAAAA;AAAAA,MAAA;AAAA,QACC,KAAK,KAAK;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA,iBAAc;AAAA,QACd,iBAAe;AAAA,QACd,GAAG,kBAAA;AAAA,QAEH,UAAA;AAAA,UAAA,QAAQF,2BAAAA,IAACG,WAAA,EAAM,GAAI,OAAO,SAAS,WAAW,EAAE,MAAM,KAAA,IAAS,EAAE,KAAA,EAAK,CAAI;AAAA,UAC1E;AAAA,UACA,CAAC,eAAeH,2BAAAA,IAACG,KAAAA,MAAA,EAAK,MAAMC,cAAAA,cAAA,CAAe;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAG/C,QACCJ,2BAAAA,IAACK,wBAAA,EACC,yCAACC,8BAAA,EAAqB,SAAkB,OAAO,OAC7C,UAAAN,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAK,KAAK;AAAA,QACV,MAAK;AAAA,QACL,OAAO;AAAA,QACP,WAAWD,MAAAA;AAAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAED,GAAG,iBAAA;AAAA,QAEH,UAAA,WACG,SAAS,SAAS,IAClB,+BAAO,IAAI,CAAC,MAAM,MAChBE,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YAEC,KAAK,CAAC,SAAS;AACb,sBAAQ,QAAQ,CAAC,IAAI;AAAA,YACvB;AAAA,YACA,MAAK;AAAA,YACL,MAAK;AAAA,YACL,UAAU,MAAM,cAAc,IAAI;AAAA,YAClC,UAAU,KAAK;AAAA,YACf,OAAO,KAAK;AAAA,YACZ,WAAU;AAAA,YACT,GAAG,aAAa;AAAA,cACf,SAAS,MAAM;AACb,qBAAK,SAAA;AACL,0BAAA;AAAA,cACF;AAAA,cACA,WAAW,CAAC,UAAU;AACpB,oBAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC9C,wBAAM,eAAA;AACN,uBAAK,SAAA;AACL,4BAAA;AAAA,gBACF;AAAA,cACF;AAAA,YAAA,CACD;AAAA,YAEA,UAAA;AAAA,cAAA,KAAK,QACJD,2BAAAA;AAAAA,gBAACG,KAAAA;AAAAA,gBAAA;AAAA,kBACE,GAAI,OAAO,KAAK,SAAS,WACtB,EAAE,MAAM,KAAK,KAAA,IACb,EAAE,MAAM,KAAK,KAAA;AAAA,gBAAK;AAAA,cAAA;AAAA,cAGzB,KAAK;AAAA,YAAA;AAAA,UAAA;AAAA,UA/BD;AAAA,QAAA;AAAA,MAiCR;AAAA,IAAA,GAET,EAAA,CACF;AAAA,EAAA,GAEJ;AAEJ;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"DropdownMenu.d.ts","sourceRoot":"","sources":["../../src/overlay/DropdownMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEpE,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAG9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qBAAqB;IACrB,KAAK,EAAE,SAAS,CAAC;IACjB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,yDAAyD;IACzD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,wDAAwD;IACxD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uDAAuD;IACvD,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACjC,oDAAoD;IACpD,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,kFAAkF;IAClF,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,oEAAoE;IACpE,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC;IAChD,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,kDAAkD;IAClD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,IAAI,EACJ,WAAmB,EACnB,OAAmB,EACnB,IAAW,EACX,OAAO,EACP,KAAK,EACL,QAAQ,EACR,KAAe,EACf,YAAY,EACZ,KAAK,EACL,SAAS,EACT,aAAa,GACd,EAAE,iBAAiB,2CA4FnB"}
1
+ {"version":3,"file":"DropdownMenu.d.ts","sourceRoot":"","sources":["../../src/overlay/DropdownMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAgBpE,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAG9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qBAAqB;IACrB,KAAK,EAAE,SAAS,CAAC;IACjB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,yDAAyD;IACzD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,wDAAwD;IACxD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uDAAuD;IACvD,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACjC,oDAAoD;IACpD,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,kFAAkF;IAClF,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,oEAAoE;IACpE,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC;IAChD,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,kDAAkD;IAClD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,IAAI,EACJ,WAAmB,EACnB,OAAmB,EACnB,IAAW,EACX,OAAO,EACP,KAAK,EACL,QAAQ,EACR,KAAe,EACf,YAAY,EACZ,KAAK,EACL,SAAS,EACT,aAAa,GACd,EAAE,iBAAiB,2CA6HnB"}
@@ -1,5 +1,6 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { useState, useRef, useEffect } from "react";
3
+ import { useFloating, autoUpdate, offset, flip, shift, useClick, useDismiss, useRole, useListNavigation, useInteractions, FloatingPortal, FloatingFocusManager } from "@floating-ui/react";
3
4
  import { cn } from "../lib/utils.js";
4
5
  import { Button } from "../components/button.js";
5
6
  import { Icon } from "../data/Icon.js";
@@ -20,50 +21,61 @@ function DropdownMenu({
20
21
  menuClassName
21
22
  }) {
22
23
  const [open, setOpen] = useState(false);
23
- const wrapperRef = useRef(null);
24
+ const [activeIndex, setActiveIndex] = useState(null);
25
+ const listRef = useRef([]);
24
26
  const onOpenChangeRef = useRef(onOpenChange);
25
27
  onOpenChangeRef.current = onOpenChange;
26
28
  useEffect(() => {
27
29
  var _a;
28
30
  (_a = onOpenChangeRef.current) == null ? void 0 : _a.call(onOpenChangeRef, open);
29
31
  }, [open]);
30
- useEffect(() => {
31
- if (!open) return;
32
- const onMouseDown = (e) => {
33
- var _a;
34
- if (!((_a = wrapperRef.current) == null ? void 0 : _a.contains(e.target))) setOpen(false);
35
- };
36
- const onKeyDown = (e) => {
37
- if (e.key === "Escape") setOpen(false);
38
- };
39
- document.addEventListener("mousedown", onMouseDown);
40
- document.addEventListener("keydown", onKeyDown);
41
- return () => {
42
- document.removeEventListener("mousedown", onMouseDown);
43
- document.removeEventListener("keydown", onKeyDown);
44
- };
45
- }, [open]);
32
+ const { refs, floatingStyles, context } = useFloating({
33
+ open,
34
+ onOpenChange: setOpen,
35
+ // bottom-end / bottom-start reproduce the previous right/left alignment.
36
+ placement: align === "right" ? "bottom-end" : "bottom-start",
37
+ whileElementsMounted: autoUpdate,
38
+ // offset(4) the previous mt-1; flip opens upward at the bottom edge and
39
+ // shift slides the menu back on-screen at the horizontal edges.
40
+ middleware: [offset(4), flip({ padding: 8 }), shift({ padding: 8 })]
41
+ });
42
+ const click = useClick(context);
43
+ const dismiss = useDismiss(context);
44
+ const role = useRole(context, { role: "menu" });
45
+ const listNav = useListNavigation(context, {
46
+ listRef,
47
+ activeIndex,
48
+ onNavigate: setActiveIndex,
49
+ loop: true
50
+ });
51
+ const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([
52
+ click,
53
+ dismiss,
54
+ role,
55
+ listNav
56
+ ]);
46
57
  const closeMenu = () => setOpen(false);
47
- const toggle = () => setOpen((v) => !v);
48
- return /* @__PURE__ */ jsxs("div", { className: cn("relative inline-flex", className), ref: wrapperRef, children: [
58
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative inline-flex", className), children: [
49
59
  trigger ? /* @__PURE__ */ jsx(
50
60
  "span",
51
61
  {
52
- className: "contents",
53
- onClick: toggle,
62
+ ref: refs.setReference,
63
+ className: "inline-flex",
54
64
  "aria-haspopup": "menu",
55
65
  "aria-expanded": open,
66
+ ...getReferenceProps(),
56
67
  children: trigger
57
68
  }
58
69
  ) : /* @__PURE__ */ jsxs(
59
70
  Button,
60
71
  {
72
+ ref: refs.setReference,
61
73
  variant,
62
74
  size,
63
- onClick: toggle,
64
75
  title,
65
76
  "aria-haspopup": "menu",
66
77
  "aria-expanded": open,
78
+ ...getReferenceProps(),
67
79
  children: [
68
80
  icon && /* @__PURE__ */ jsx(Icon, { ...typeof icon === "string" ? { name: icon } : { icon } }),
69
81
  label,
@@ -71,27 +83,42 @@ function DropdownMenu({
71
83
  ]
72
84
  }
73
85
  ),
74
- open && /* @__PURE__ */ jsx(
86
+ open && /* @__PURE__ */ jsx(FloatingPortal, { children: /* @__PURE__ */ jsx(FloatingFocusManager, { context, modal: false, children: /* @__PURE__ */ jsx(
75
87
  "div",
76
88
  {
89
+ ref: refs.setFloating,
77
90
  role: "menu",
91
+ style: floatingStyles,
78
92
  className: cn(
79
- "absolute z-20 mt-1 min-w-[8rem] rounded-md border border-border bg-popover py-1 shadow-md",
80
- align === "right" ? "right-0" : "left-0",
93
+ "z-[9999] min-w-[8rem] rounded-md border border-border bg-popover py-1 shadow-md",
81
94
  menuClassName
82
95
  ),
96
+ ...getFloatingProps(),
83
97
  children: children ? children(closeMenu) : items == null ? void 0 : items.map((item, i) => /* @__PURE__ */ jsxs(
84
98
  "button",
85
99
  {
100
+ ref: (node) => {
101
+ listRef.current[i] = node;
102
+ },
86
103
  type: "button",
87
104
  role: "menuitem",
105
+ tabIndex: i === activeIndex ? 0 : -1,
88
106
  disabled: item.disabled,
89
107
  title: item.title,
90
- onClick: () => {
91
- item.onSelect();
92
- closeMenu();
93
- },
94
108
  className: "flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50",
109
+ ...getItemProps({
110
+ onClick: () => {
111
+ item.onSelect();
112
+ closeMenu();
113
+ },
114
+ onKeyDown: (event) => {
115
+ if (event.key === "Enter" || event.key === " ") {
116
+ event.preventDefault();
117
+ item.onSelect();
118
+ closeMenu();
119
+ }
120
+ }
121
+ }),
95
122
  children: [
96
123
  item.icon && /* @__PURE__ */ jsx(
97
124
  Icon,
@@ -105,7 +132,7 @@ function DropdownMenu({
105
132
  i
106
133
  ))
107
134
  }
108
- )
135
+ ) }) })
109
136
  ] });
110
137
  }
111
138
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"DropdownMenu.js","sources":["../../src/overlay/DropdownMenu.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button, type ButtonProps } from \"../components/button\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiChevronDown } from \"../icons\";\n\nexport type DropdownMenuItem = {\n /** Visible label. */\n label: ReactNode;\n /** Called when the item is chosen. */\n onSelect: () => void;\n /** Iconify name or imported icon component rendered before the label. */\n icon?: string | StaticIconComponent;\n /** Browser tooltip for the item. */\n title?: string;\n /** Disable selection. */\n disabled?: boolean;\n};\n\nexport type DropdownMenuProps = {\n /** Trigger label. Ignored when `trigger` is provided. */\n label?: ReactNode;\n /** Icon shown before the trigger label. Ignored when `trigger` is provided. */\n icon?: string | StaticIconComponent;\n /** Hide the trailing chevron on the default trigger. */\n hideChevron?: boolean;\n /** Variant forwarded to the default Button trigger. */\n variant?: ButtonProps[\"variant\"];\n /** Size forwarded to the default Button trigger. */\n size?: ButtonProps[\"size\"];\n /** Fully custom trigger. Receives no props; the menu wraps it with open state. */\n trigger?: ReactNode;\n /** Declarative menu items. Provide this or `children`, not both. */\n items?: DropdownMenuItem[];\n /** Custom menu content. Use `closeMenu` to dismiss after a selection. */\n children?: (closeMenu: () => void) => ReactNode;\n /** Horizontal alignment of the menu relative to the trigger. */\n align?: \"left\" | \"right\";\n /** Notified whenever the menu opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /** Browser tooltip / accessible label for the default trigger. */\n title?: string;\n /** Classes applied to the wrapper. */\n className?: string;\n /** Classes applied to the floating menu. */\n menuClassName?: string;\n};\n\nexport function DropdownMenu({\n label,\n icon,\n hideChevron = false,\n variant = \"outline\",\n size = \"sm\",\n trigger,\n items,\n children,\n align = \"right\",\n onOpenChange,\n title,\n className,\n menuClassName,\n}: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n const onOpenChangeRef = useRef(onOpenChange);\n onOpenChangeRef.current = onOpenChange;\n useEffect(() => {\n onOpenChangeRef.current?.(open);\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n const onMouseDown = (e: MouseEvent) => {\n if (!wrapperRef.current?.contains(e.target as Node)) setOpen(false);\n };\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"mousedown\", onMouseDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onMouseDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [open]);\n\n const closeMenu = () => setOpen(false);\n const toggle = () => setOpen((v) => !v);\n\n return (\n <div className={cn(\"relative inline-flex\", className)} ref={wrapperRef}>\n {trigger ? (\n <span\n className=\"contents\"\n onClick={toggle}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n >\n {trigger}\n </span>\n ) : (\n <Button\n variant={variant}\n size={size}\n onClick={toggle}\n title={title}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n >\n {icon && <Icon {...(typeof icon === \"string\" ? { name: icon } : { icon })} />}\n {label}\n {!hideChevron && <Icon icon={UiChevronDown} />}\n </Button>\n )}\n {open && (\n <div\n role=\"menu\"\n className={cn(\n \"absolute z-20 mt-1 min-w-[8rem] rounded-md border border-border bg-popover py-1 shadow-md\",\n align === \"right\" ? \"right-0\" : \"left-0\",\n menuClassName,\n )}\n >\n {children\n ? children(closeMenu)\n : items?.map((item, i) => (\n <button\n key={i}\n type=\"button\"\n role=\"menuitem\"\n disabled={item.disabled}\n title={item.title}\n onClick={() => {\n item.onSelect();\n closeMenu();\n }}\n className=\"flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n {item.icon && (\n <Icon\n {...(typeof item.icon === \"string\"\n ? { name: item.icon }\n : { icon: item.icon })}\n />\n )}\n {item.label}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;AAgDO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,UAAU;AAAA,EACV,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAsB;AACpB,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,aAAa,OAAuB,IAAI;AAE9C,QAAM,kBAAkB,OAAO,YAAY;AAC3C,kBAAgB,UAAU;AAC1B,YAAU,MAAM;;AACd,0BAAgB,YAAhB,yCAA0B;AAAA,EAC5B,GAAG,CAAC,IAAI,CAAC;AAET,YAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,UAAM,cAAc,CAAC,MAAkB;;AACrC,UAAI,GAAC,gBAAW,YAAX,mBAAoB,SAAS,EAAE,kBAAyB,KAAK;AAAA,IACpE;AACA,UAAM,YAAY,CAAC,MAAqB;AACtC,UAAI,EAAE,QAAQ,SAAU,SAAQ,KAAK;AAAA,IACvC;AACA,aAAS,iBAAiB,aAAa,WAAW;AAClD,aAAS,iBAAiB,WAAW,SAAS;AAC9C,WAAO,MAAM;AACX,eAAS,oBAAoB,aAAa,WAAW;AACrD,eAAS,oBAAoB,WAAW,SAAS;AAAA,IACnD;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,YAAY,MAAM,QAAQ,KAAK;AACrC,QAAM,SAAS,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;AAEtC,SACE,qBAAC,SAAI,WAAW,GAAG,wBAAwB,SAAS,GAAG,KAAK,YACzD,UAAA;AAAA,IAAA,UACC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,SAAS;AAAA,QACT,iBAAc;AAAA,QACd,iBAAe;AAAA,QAEd,UAAA;AAAA,MAAA;AAAA,IAAA,IAGH;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT;AAAA,QACA,iBAAc;AAAA,QACd,iBAAe;AAAA,QAEd,UAAA;AAAA,UAAA,QAAQ,oBAAC,MAAA,EAAM,GAAI,OAAO,SAAS,WAAW,EAAE,MAAM,KAAA,IAAS,EAAE,KAAA,EAAK,CAAI;AAAA,UAC1E;AAAA,UACA,CAAC,eAAe,oBAAC,MAAA,EAAK,MAAM,cAAA,CAAe;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAG/C,QACC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAW;AAAA,UACT;AAAA,UACA,UAAU,UAAU,YAAY;AAAA,UAChC;AAAA,QAAA;AAAA,QAGD,UAAA,WACG,SAAS,SAAS,IAClB,+BAAO,IAAI,CAAC,MAAM,MAChB;AAAA,UAAC;AAAA,UAAA;AAAA,YAEC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,UAAU,KAAK;AAAA,YACf,OAAO,KAAK;AAAA,YACZ,SAAS,MAAM;AACb,mBAAK,SAAA;AACL,wBAAA;AAAA,YACF;AAAA,YACA,WAAU;AAAA,YAET,UAAA;AAAA,cAAA,KAAK,QACJ;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACE,GAAI,OAAO,KAAK,SAAS,WACtB,EAAE,MAAM,KAAK,KAAA,IACb,EAAE,MAAM,KAAK,KAAA;AAAA,gBAAK;AAAA,cAAA;AAAA,cAGzB,KAAK;AAAA,YAAA;AAAA,UAAA;AAAA,UAlBD;AAAA,QAAA;AAAA,MAoBR;AAAA,IAAA;AAAA,EACP,GAEJ;AAEJ;"}
1
+ {"version":3,"file":"DropdownMenu.js","sources":["../../src/overlay/DropdownMenu.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactNode } 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 useRole,\n} from \"@floating-ui/react\";\nimport { cn } from \"../lib/utils\";\nimport { Button, type ButtonProps } from \"../components/button\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiChevronDown } from \"../icons\";\n\nexport type DropdownMenuItem = {\n /** Visible label. */\n label: ReactNode;\n /** Called when the item is chosen. */\n onSelect: () => void;\n /** Iconify name or imported icon component rendered before the label. */\n icon?: string | StaticIconComponent;\n /** Browser tooltip for the item. */\n title?: string;\n /** Disable selection. */\n disabled?: boolean;\n};\n\nexport type DropdownMenuProps = {\n /** Trigger label. Ignored when `trigger` is provided. */\n label?: ReactNode;\n /** Icon shown before the trigger label. Ignored when `trigger` is provided. */\n icon?: string | StaticIconComponent;\n /** Hide the trailing chevron on the default trigger. */\n hideChevron?: boolean;\n /** Variant forwarded to the default Button trigger. */\n variant?: ButtonProps[\"variant\"];\n /** Size forwarded to the default Button trigger. */\n size?: ButtonProps[\"size\"];\n /** Fully custom trigger. Receives no props; the menu wraps it with open state. */\n trigger?: ReactNode;\n /** Declarative menu items. Provide this or `children`, not both. */\n items?: DropdownMenuItem[];\n /** Custom menu content. Use `closeMenu` to dismiss after a selection. */\n children?: (closeMenu: () => void) => ReactNode;\n /** Horizontal alignment of the menu relative to the trigger. */\n align?: \"left\" | \"right\";\n /** Notified whenever the menu opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /** Browser tooltip / accessible label for the default trigger. */\n title?: string;\n /** Classes applied to the wrapper. */\n className?: string;\n /** Classes applied to the floating menu. */\n menuClassName?: string;\n};\n\nexport function DropdownMenu({\n label,\n icon,\n hideChevron = false,\n variant = \"outline\",\n size = \"sm\",\n trigger,\n items,\n children,\n align = \"right\",\n onOpenChange,\n title,\n className,\n menuClassName,\n}: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n const listRef = useRef<(HTMLElement | null)[]>([]);\n\n const onOpenChangeRef = useRef(onOpenChange);\n onOpenChangeRef.current = onOpenChange;\n useEffect(() => {\n onOpenChangeRef.current?.(open);\n }, [open]);\n\n const { refs, floatingStyles, context } = useFloating<HTMLDivElement>({\n open,\n onOpenChange: setOpen,\n // bottom-end / bottom-start reproduce the previous right/left alignment.\n placement: align === \"right\" ? \"bottom-end\" : \"bottom-start\",\n whileElementsMounted: autoUpdate,\n // offset(4) the previous mt-1; flip opens upward at the bottom edge and\n // shift slides the menu back on-screen at the horizontal edges.\n middleware: [offset(4), 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 onNavigate: setActiveIndex,\n loop: true,\n });\n\n const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([\n click,\n dismiss,\n role,\n listNav,\n ]);\n\n const closeMenu = () => setOpen(false);\n\n return (\n <div className={cn(\"relative inline-flex\", className)}>\n {trigger ? (\n <span\n ref={refs.setReference}\n className=\"inline-flex\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n {...getReferenceProps()}\n >\n {trigger}\n </span>\n ) : (\n <Button\n ref={refs.setReference as React.Ref<HTMLButtonElement>}\n variant={variant}\n size={size}\n title={title}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n {...getReferenceProps()}\n >\n {icon && <Icon {...(typeof icon === \"string\" ? { name: icon } : { icon })} />}\n {label}\n {!hideChevron && <Icon icon={UiChevronDown} />}\n </Button>\n )}\n {open && (\n <FloatingPortal>\n <FloatingFocusManager context={context} modal={false}>\n <div\n ref={refs.setFloating}\n role=\"menu\"\n style={floatingStyles}\n className={cn(\n \"z-[9999] min-w-[8rem] rounded-md border border-border bg-popover py-1 shadow-md\",\n menuClassName,\n )}\n {...getFloatingProps()}\n >\n {children\n ? children(closeMenu)\n : items?.map((item, i) => (\n <button\n key={i}\n ref={(node) => {\n listRef.current[i] = node;\n }}\n type=\"button\"\n role=\"menuitem\"\n tabIndex={i === activeIndex ? 0 : -1}\n disabled={item.disabled}\n title={item.title}\n className=\"flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n {...getItemProps({\n onClick: () => {\n item.onSelect();\n closeMenu();\n },\n onKeyDown: (event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n item.onSelect();\n closeMenu();\n }\n },\n })}\n >\n {item.icon && (\n <Icon\n {...(typeof item.icon === \"string\"\n ? { name: item.icon }\n : { icon: item.icon })}\n />\n )}\n {item.label}\n </button>\n ))}\n </div>\n </FloatingFocusManager>\n </FloatingPortal>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}