@puzzle-ui/ui 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/README.md +70 -70
  2. package/dist/components/actions/button/button.css +5 -2
  3. package/dist/components/actions/button-group/button-group.css +16 -17
  4. package/dist/components/data-display/badge/badge.css +15 -2
  5. package/dist/components/data-display/badge/index.cjs +13 -2
  6. package/dist/components/data-display/badge/index.cjs.map +1 -1
  7. package/dist/components/data-display/badge/index.d.cts +7 -2
  8. package/dist/components/data-display/badge/index.d.ts +7 -2
  9. package/dist/components/data-display/badge/index.js +13 -2
  10. package/dist/components/data-display/badge/index.js.map +1 -1
  11. package/dist/components/data-display/empty-state/empty-state.css +128 -0
  12. package/dist/components/data-display/empty-state/empty-state.utils.cjs +6 -0
  13. package/dist/components/data-display/empty-state/empty-state.utils.cjs.map +1 -0
  14. package/dist/components/data-display/empty-state/empty-state.utils.js +6 -0
  15. package/dist/components/data-display/empty-state/empty-state.utils.js.map +1 -0
  16. package/dist/components/data-display/empty-state/index.cjs +49 -0
  17. package/dist/components/data-display/empty-state/index.cjs.map +1 -0
  18. package/dist/components/data-display/empty-state/index.d.cts +19 -0
  19. package/dist/components/data-display/empty-state/index.d.ts +19 -0
  20. package/dist/components/data-display/empty-state/index.js +44 -0
  21. package/dist/components/data-display/empty-state/index.js.map +1 -0
  22. package/dist/components/data-display/rating/index.cjs +84 -0
  23. package/dist/components/data-display/rating/index.cjs.map +1 -0
  24. package/dist/components/data-display/rating/index.d.cts +3 -0
  25. package/dist/components/data-display/rating/index.d.ts +3 -0
  26. package/dist/components/data-display/rating/index.js +84 -0
  27. package/dist/components/data-display/rating/index.js.map +1 -0
  28. package/dist/components/data-display/rating/rating.css +72 -0
  29. package/dist/components/data-display/rating/rating.types.d.cts +15 -0
  30. package/dist/components/data-display/rating/rating.types.d.ts +15 -0
  31. package/dist/components/data-display/rating/rating.utils.cjs +27 -0
  32. package/dist/components/data-display/rating/rating.utils.cjs.map +1 -0
  33. package/dist/components/data-display/rating/rating.utils.js +25 -0
  34. package/dist/components/data-display/rating/rating.utils.js.map +1 -0
  35. package/dist/components/editors/text-editor/editor.cjs.map +1 -1
  36. package/dist/components/editors/text-editor/editor.js.map +1 -1
  37. package/dist/components/editors/text-editor/text-document.cjs.map +1 -1
  38. package/dist/components/editors/text-editor/text-document.js.map +1 -1
  39. package/dist/components/editors/text-editor/text-parser.cjs.map +1 -1
  40. package/dist/components/editors/text-editor/text-parser.js.map +1 -1
  41. package/dist/components/editors/text-editor/text-renderer.cjs.map +1 -1
  42. package/dist/components/editors/text-editor/text-renderer.js.map +1 -1
  43. package/dist/components/forms/autocomplete/index.cjs.map +1 -1
  44. package/dist/components/forms/autocomplete/index.js.map +1 -1
  45. package/dist/components/forms/checkbox/checkbox.css +2 -10
  46. package/dist/components/forms/color-picker/color-picker.css +351 -0
  47. package/dist/components/forms/color-picker/color-picker.types.d.cts +25 -0
  48. package/dist/components/forms/color-picker/color-picker.types.d.ts +25 -0
  49. package/dist/components/forms/color-picker/color-picker.utils.cjs +116 -0
  50. package/dist/components/forms/color-picker/color-picker.utils.cjs.map +1 -0
  51. package/dist/components/forms/color-picker/color-picker.utils.js +109 -0
  52. package/dist/components/forms/color-picker/color-picker.utils.js.map +1 -0
  53. package/dist/components/forms/color-picker/index.cjs +340 -0
  54. package/dist/components/forms/color-picker/index.cjs.map +1 -0
  55. package/dist/components/forms/color-picker/index.d.cts +3 -0
  56. package/dist/components/forms/color-picker/index.d.ts +3 -0
  57. package/dist/components/forms/color-picker/index.js +340 -0
  58. package/dist/components/forms/color-picker/index.js.map +1 -0
  59. package/dist/components/forms/file-upload/file-upload.utils.cjs.map +1 -1
  60. package/dist/components/forms/file-upload/file-upload.utils.js.map +1 -1
  61. package/dist/components/forms/input/number-input.js +5 -5
  62. package/dist/components/forms/input/number-input.js.map +1 -1
  63. package/dist/components/forms/input-group/index.d.cts +3 -0
  64. package/dist/components/forms/input-group/index.d.ts +3 -0
  65. package/dist/components/forms/input-group/input-group.cjs +18 -0
  66. package/dist/components/forms/input-group/input-group.cjs.map +1 -0
  67. package/dist/components/forms/input-group/input-group.css +62 -0
  68. package/dist/components/forms/input-group/input-group.d.cts +7 -0
  69. package/dist/components/forms/input-group/input-group.d.ts +7 -0
  70. package/dist/components/forms/input-group/input-group.js +18 -0
  71. package/dist/components/forms/input-group/input-group.js.map +1 -0
  72. package/dist/components/forms/input-group/input-group.types.d.cts +7 -0
  73. package/dist/components/forms/input-group/input-group.types.d.ts +7 -0
  74. package/dist/components/forms/input-group/input-group.utils.cjs +5 -0
  75. package/dist/components/forms/input-group/input-group.utils.cjs.map +1 -0
  76. package/dist/components/forms/input-group/input-group.utils.js +5 -0
  77. package/dist/components/forms/input-group/input-group.utils.js.map +1 -0
  78. package/dist/components/forms/radio-group/index.d.cts +2 -1
  79. package/dist/components/forms/radio-group/index.d.ts +2 -1
  80. package/dist/components/forms/select/index.cjs.map +1 -1
  81. package/dist/components/forms/select/index.js.map +1 -1
  82. package/dist/components/forms/slider/index.cjs +1 -1
  83. package/dist/components/forms/slider/index.cjs.map +1 -1
  84. package/dist/components/forms/slider/index.js +1 -1
  85. package/dist/components/forms/slider/index.js.map +1 -1
  86. package/dist/components/forms/stepper/index.d.cts +4 -2
  87. package/dist/components/forms/stepper/index.d.ts +4 -2
  88. package/dist/components/forms/time-picker/index.cjs +108 -0
  89. package/dist/components/forms/time-picker/index.cjs.map +1 -0
  90. package/dist/components/forms/time-picker/index.d.cts +3 -0
  91. package/dist/components/forms/time-picker/index.d.ts +3 -0
  92. package/dist/components/forms/time-picker/index.js +108 -0
  93. package/dist/components/forms/time-picker/index.js.map +1 -0
  94. package/dist/components/forms/time-picker/time-picker.css +166 -0
  95. package/dist/components/forms/time-picker/time-picker.types.d.cts +34 -0
  96. package/dist/components/forms/time-picker/time-picker.types.d.ts +34 -0
  97. package/dist/components/forms/time-picker/time-picker.utils.cjs +25 -0
  98. package/dist/components/forms/time-picker/time-picker.utils.cjs.map +1 -0
  99. package/dist/components/forms/time-picker/time-picker.utils.js +24 -0
  100. package/dist/components/forms/time-picker/time-picker.utils.js.map +1 -0
  101. package/dist/components/forms/token-input/index.cjs +189 -0
  102. package/dist/components/forms/token-input/index.cjs.map +1 -0
  103. package/dist/components/forms/token-input/index.d.cts +3 -0
  104. package/dist/components/forms/token-input/index.d.ts +3 -0
  105. package/dist/components/forms/token-input/index.js +189 -0
  106. package/dist/components/forms/token-input/index.js.map +1 -0
  107. package/dist/components/forms/token-input/token-input.css +145 -0
  108. package/dist/components/forms/token-input/token-input.types.d.cts +48 -0
  109. package/dist/components/forms/token-input/token-input.types.d.ts +48 -0
  110. package/dist/components/forms/token-input/token-input.utils.cjs +30 -0
  111. package/dist/components/forms/token-input/token-input.utils.cjs.map +1 -0
  112. package/dist/components/forms/token-input/token-input.utils.js +24 -0
  113. package/dist/components/forms/token-input/token-input.utils.js.map +1 -0
  114. package/dist/components/layout/aspect-ratio/aspect-ratio.css +14 -0
  115. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.cjs +11 -0
  116. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.cjs.map +1 -0
  117. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.js +11 -0
  118. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.js.map +1 -0
  119. package/dist/components/layout/aspect-ratio/index.cjs +16 -0
  120. package/dist/components/layout/aspect-ratio/index.cjs.map +1 -0
  121. package/dist/components/layout/aspect-ratio/index.d.cts +11 -0
  122. package/dist/components/layout/aspect-ratio/index.d.ts +11 -0
  123. package/dist/components/layout/aspect-ratio/index.js +16 -0
  124. package/dist/components/layout/aspect-ratio/index.js.map +1 -0
  125. package/dist/components/layout/container/container.css +1 -0
  126. package/dist/components/layout/page-layout/page-layout.css +1 -0
  127. package/dist/components/layout/resizable-panels/index.cjs +1 -1
  128. package/dist/components/layout/resizable-panels/index.cjs.map +1 -1
  129. package/dist/components/layout/resizable-panels/index.js +1 -1
  130. package/dist/components/layout/resizable-panels/index.js.map +1 -1
  131. package/dist/components/layout/resizable-panels/resizable-panels.utils.cjs.map +1 -1
  132. package/dist/components/layout/resizable-panels/resizable-panels.utils.js.map +1 -1
  133. package/dist/components/layout/scroll-area/index.cjs +27 -0
  134. package/dist/components/layout/scroll-area/index.cjs.map +1 -0
  135. package/dist/components/layout/scroll-area/index.d.cts +15 -0
  136. package/dist/components/layout/scroll-area/index.d.ts +15 -0
  137. package/dist/components/layout/scroll-area/index.js +27 -0
  138. package/dist/components/layout/scroll-area/index.js.map +1 -0
  139. package/dist/components/layout/scroll-area/scroll-area.css +25 -0
  140. package/dist/components/layout/scroll-area/scroll-area.utils.cjs +8 -0
  141. package/dist/components/layout/scroll-area/scroll-area.utils.cjs.map +1 -0
  142. package/dist/components/layout/scroll-area/scroll-area.utils.d.cts +2 -0
  143. package/dist/components/layout/scroll-area/scroll-area.utils.d.ts +2 -0
  144. package/dist/components/layout/scroll-area/scroll-area.utils.js +8 -0
  145. package/dist/components/layout/scroll-area/scroll-area.utils.js.map +1 -0
  146. package/dist/components/layout/section/index.cjs +10 -4
  147. package/dist/components/layout/section/index.cjs.map +1 -1
  148. package/dist/components/layout/section/index.d.cts +1 -1
  149. package/dist/components/layout/section/index.d.ts +1 -1
  150. package/dist/components/layout/section/index.js +11 -5
  151. package/dist/components/layout/section/index.js.map +1 -1
  152. package/dist/components/layout/section/section.css +9 -3
  153. package/dist/components/layout/section/types.d.cts +5 -5
  154. package/dist/components/layout/section/types.d.ts +5 -5
  155. package/dist/components/layout/separator/index.cjs +18 -0
  156. package/dist/components/layout/separator/index.cjs.map +1 -0
  157. package/dist/components/layout/separator/index.d.cts +17 -0
  158. package/dist/components/layout/separator/index.d.ts +17 -0
  159. package/dist/components/layout/separator/index.js +18 -0
  160. package/dist/components/layout/separator/index.js.map +1 -0
  161. package/dist/components/layout/separator/separator.css +15 -0
  162. package/dist/components/layout/separator/separator.utils.cjs +11 -0
  163. package/dist/components/layout/separator/separator.utils.cjs.map +1 -0
  164. package/dist/components/layout/separator/separator.utils.d.cts +2 -0
  165. package/dist/components/layout/separator/separator.utils.d.ts +2 -0
  166. package/dist/components/layout/separator/separator.utils.js +11 -0
  167. package/dist/components/layout/separator/separator.utils.js.map +1 -0
  168. package/dist/components/layout/stack/index.cjs +18 -2
  169. package/dist/components/layout/stack/index.cjs.map +1 -1
  170. package/dist/components/layout/stack/index.d.cts +8 -6
  171. package/dist/components/layout/stack/index.d.ts +8 -6
  172. package/dist/components/layout/stack/index.js +18 -2
  173. package/dist/components/layout/stack/index.js.map +1 -1
  174. package/dist/components/layout/stack/stack.css +118 -138
  175. package/dist/components/navigation/accordion/index.cjs.map +1 -1
  176. package/dist/components/navigation/accordion/index.js.map +1 -1
  177. package/dist/components/navigation/carousel/index.cjs +5 -2
  178. package/dist/components/navigation/carousel/index.cjs.map +1 -1
  179. package/dist/components/navigation/carousel/index.js +5 -2
  180. package/dist/components/navigation/carousel/index.js.map +1 -1
  181. package/dist/components/navigation/pagination/index.cjs +146 -0
  182. package/dist/components/navigation/pagination/index.cjs.map +1 -0
  183. package/dist/components/navigation/pagination/index.d.cts +3 -0
  184. package/dist/components/navigation/pagination/index.d.ts +3 -0
  185. package/dist/components/navigation/pagination/index.js +146 -0
  186. package/dist/components/navigation/pagination/index.js.map +1 -0
  187. package/dist/components/navigation/pagination/pagination.css +134 -0
  188. package/dist/components/navigation/pagination/pagination.types.d.cts +38 -0
  189. package/dist/components/navigation/pagination/pagination.types.d.ts +38 -0
  190. package/dist/components/navigation/pagination/pagination.utils.cjs +54 -0
  191. package/dist/components/navigation/pagination/pagination.utils.cjs.map +1 -0
  192. package/dist/components/navigation/pagination/pagination.utils.js +53 -0
  193. package/dist/components/navigation/pagination/pagination.utils.js.map +1 -0
  194. package/dist/components/navigation/tabs/index.d.cts +3 -2
  195. package/dist/components/navigation/tabs/index.d.ts +3 -2
  196. package/dist/components/navigation/tree-view/index.cjs.map +1 -1
  197. package/dist/components/navigation/tree-view/index.js.map +1 -1
  198. package/dist/components/overlays/context-menu/context-menu.css +151 -0
  199. package/dist/components/overlays/context-menu/context-menu.types.d.cts +69 -0
  200. package/dist/components/overlays/context-menu/context-menu.types.d.ts +69 -0
  201. package/dist/components/overlays/context-menu/context-menu.utils.cjs +46 -0
  202. package/dist/components/overlays/context-menu/context-menu.utils.cjs.map +1 -0
  203. package/dist/components/overlays/context-menu/context-menu.utils.js +40 -0
  204. package/dist/components/overlays/context-menu/context-menu.utils.js.map +1 -0
  205. package/dist/components/overlays/context-menu/index.cjs +830 -0
  206. package/dist/components/overlays/context-menu/index.cjs.map +1 -0
  207. package/dist/components/overlays/context-menu/index.d.cts +42 -0
  208. package/dist/components/overlays/context-menu/index.d.ts +42 -0
  209. package/dist/components/overlays/context-menu/index.js +818 -0
  210. package/dist/components/overlays/context-menu/index.js.map +1 -0
  211. package/dist/components/overlays/dropdown-menu/dropdown-menu-content.cjs.map +1 -1
  212. package/dist/components/overlays/dropdown-menu/dropdown-menu-content.js.map +1 -1
  213. package/dist/components/overlays/dropdown-menu/dropdown-menu.utils.cjs.map +1 -1
  214. package/dist/components/overlays/dropdown-menu/dropdown-menu.utils.js.map +1 -1
  215. package/dist/components/typography/heading.cjs +6 -2
  216. package/dist/components/typography/heading.cjs.map +1 -1
  217. package/dist/components/typography/heading.d.cts +3 -2
  218. package/dist/components/typography/heading.d.ts +3 -2
  219. package/dist/components/typography/heading.js +6 -2
  220. package/dist/components/typography/heading.js.map +1 -1
  221. package/dist/components/typography/text.cjs +6 -2
  222. package/dist/components/typography/text.cjs.map +1 -1
  223. package/dist/components/typography/text.d.cts +3 -2
  224. package/dist/components/typography/text.d.ts +3 -2
  225. package/dist/components/typography/text.js +6 -2
  226. package/dist/components/typography/text.js.map +1 -1
  227. package/dist/components/utilities/preview/index.cjs +89 -21
  228. package/dist/components/utilities/preview/index.cjs.map +1 -1
  229. package/dist/components/utilities/preview/index.d.cts +6 -2
  230. package/dist/components/utilities/preview/index.d.ts +6 -2
  231. package/dist/components/utilities/preview/index.js +89 -21
  232. package/dist/components/utilities/preview/index.js.map +1 -1
  233. package/dist/components/utilities/preview/preview.css +26 -11
  234. package/dist/components/utilities/transition/index.d.cts +1 -1
  235. package/dist/components/utilities/transition/index.d.ts +1 -1
  236. package/dist/index.cjs +136 -97
  237. package/dist/index.d.cts +23 -2
  238. package/dist/index.d.ts +23 -2
  239. package/dist/index.js +12 -1
  240. package/package.json +7 -6
@@ -0,0 +1,830 @@
1
+ "use client";
2
+ const require_index = require("../popout/index.cjs");
3
+ const require_context_menu_utils = require("./context-menu.utils.cjs");
4
+ ;/* empty css */
5
+ let _puzzle_ui_core = require("@puzzle-ui/core");
6
+ let react = require("react");
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ // src/components/overlays/context-menu/index.tsx
9
+ const ContextMenuContext = (0, react.createContext)(null);
10
+ const RadioGroupContext = (0, react.createContext)(null);
11
+ const SubContext = (0, react.createContext)(null);
12
+ const useContextMenuContext = () => {
13
+ const context = (0, react.useContext)(ContextMenuContext);
14
+ if (!context) throw new Error("ContextMenu components must be rendered inside ContextMenu.");
15
+ return context;
16
+ };
17
+ const useRadioGroupContext = () => {
18
+ const context = (0, react.useContext)(RadioGroupContext);
19
+ if (!context) throw new Error("ContextMenu.RadioItem must be rendered inside ContextMenu.RadioGroup.");
20
+ return context;
21
+ };
22
+ const useSubContext = () => {
23
+ const context = (0, react.useContext)(SubContext);
24
+ if (!context) throw new Error("ContextMenu.SubTrigger and SubContent must be rendered inside ContextMenu.Sub.");
25
+ return context;
26
+ };
27
+ const getMenuElements = (children) => react.Children.toArray(children).flatMap((child) => {
28
+ if ((0, react.isValidElement)(child) && child.type === react.Fragment) return getMenuElements(child.props.children);
29
+ return (0, react.isValidElement)(child) ? [child] : [];
30
+ });
31
+ const ContextMenuRoot = ({ children, open, defaultOpen = false, onOpenChange, disabled = false, triggerClassName, contentClassName }) => {
32
+ const baseId = (0, react.useId)();
33
+ const triggerId = `${baseId}-trigger`;
34
+ const contentId = `${baseId}-content`;
35
+ const childElements = getMenuElements(children);
36
+ const triggers = childElements.filter((child) => child.type === ContextMenuTrigger);
37
+ const contents = childElements.filter((child) => child.type === ContextMenuContent);
38
+ const triggerElement = triggers[0];
39
+ const contentElement = contents[0];
40
+ if (triggers.length !== 1 || contents.length !== 1 || !triggerElement || !contentElement) throw new Error("ContextMenu requires exactly one ContextMenu.Trigger and one ContextMenu.Content.");
41
+ const controlled = open !== void 0;
42
+ const [internalOpen, setInternalOpen] = (0, react.useState)(defaultOpen);
43
+ const triggerDisabled = disabled || triggerElement.props.disabled === true;
44
+ const isOpen = !triggerDisabled && (controlled ? open ?? false : internalOpen);
45
+ const [point, setPoint] = (0, react.useState)({
46
+ x: 0,
47
+ y: 0
48
+ });
49
+ const triggerRef = (0, react.useRef)(null);
50
+ const focusPositionRef = (0, react.useRef)("first");
51
+ const changeOpen = (0, react.useCallback)((nextOpen) => {
52
+ if (triggerDisabled && nextOpen) return;
53
+ if (nextOpen === isOpen) return;
54
+ if (!controlled) setInternalOpen(nextOpen);
55
+ onOpenChange?.(nextOpen);
56
+ }, [
57
+ controlled,
58
+ isOpen,
59
+ onOpenChange,
60
+ triggerDisabled
61
+ ]);
62
+ const openAt = (0, react.useCallback)((nextPoint, position = "first") => {
63
+ if (triggerDisabled) return;
64
+ setPoint({
65
+ x: Math.max(0, nextPoint.x),
66
+ y: Math.max(0, nextPoint.y)
67
+ });
68
+ focusPositionRef.current = position;
69
+ changeOpen(true);
70
+ }, [changeOpen, triggerDisabled]);
71
+ const close = (0, react.useCallback)((restoreFocus = false) => {
72
+ changeOpen(false);
73
+ if (restoreFocus) require_context_menu_utils.focusElement(triggerRef.current ?? void 0);
74
+ }, [changeOpen]);
75
+ const context = (0, react.useMemo)(() => ({
76
+ open: isOpen,
77
+ disabled: triggerDisabled,
78
+ triggerId,
79
+ contentId,
80
+ triggerRef,
81
+ focusPositionRef,
82
+ openAt,
83
+ changeOpen,
84
+ close
85
+ }), [
86
+ isOpen,
87
+ triggerDisabled,
88
+ triggerId,
89
+ contentId,
90
+ openAt,
91
+ changeOpen,
92
+ close
93
+ ]);
94
+ const trigger = (0, react.cloneElement)(triggerElement, {
95
+ id: triggerId,
96
+ disabled: triggerDisabled,
97
+ className: (0, _puzzle_ui_core.cn)(triggerElement.props.className, triggerClassName)
98
+ });
99
+ const content = (0, react.cloneElement)(contentElement, {
100
+ id: contentId,
101
+ className: (0, _puzzle_ui_core.cn)(contentElement.props.className, contentClassName)
102
+ });
103
+ const anchor = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
104
+ "aria-hidden": "true",
105
+ className: "puzzle-context-menu__anchor",
106
+ style: {
107
+ left: point.x,
108
+ top: point.y
109
+ },
110
+ tabIndex: -1
111
+ });
112
+ (0, react.useEffect)(() => {
113
+ if (!isOpen || !triggerRef.current) return;
114
+ const ownerDocument = triggerRef.current.ownerDocument;
115
+ const handleOutsidePointerDown = (event) => {
116
+ const target = event.target;
117
+ if (!(target instanceof ownerDocument.defaultView.Node)) return;
118
+ if (triggerRef.current?.contains(target) || target instanceof Element && target.closest(".puzzle-context-menu__popover")) return;
119
+ changeOpen(false);
120
+ };
121
+ ownerDocument.addEventListener("pointerdown", handleOutsidePointerDown, true);
122
+ return () => {
123
+ ownerDocument.removeEventListener("pointerdown", handleOutsidePointerDown, true);
124
+ };
125
+ }, [isOpen, changeOpen]);
126
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ContextMenuContext.Provider, {
127
+ value: context,
128
+ children: [trigger, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.Popout, {
129
+ trigger: anchor,
130
+ open: isOpen,
131
+ onOpenChange: (nextOpen) => {
132
+ if (nextOpen) changeOpen(true);
133
+ else close(true);
134
+ },
135
+ position: "bottom",
136
+ align: "start",
137
+ offset: 0,
138
+ collisionPadding: 8,
139
+ closeOnOutsideClick: false,
140
+ closeOnEscape: true,
141
+ contentClassName: "puzzle-context-menu__popover",
142
+ children: content
143
+ })]
144
+ });
145
+ };
146
+ const ContextMenuTrigger = ({ children, asChild = false, disabled = false, id, className, ref, onContextMenu, onClick, onKeyDown, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, ...triggerProps }) => {
147
+ const context = useContextMenuContext();
148
+ const longPressTimerRef = (0, react.useRef)(null);
149
+ const pointerStartRef = (0, react.useRef)(null);
150
+ const isDisabled = disabled || context.disabled;
151
+ const clearLongPress = (0, react.useCallback)(() => {
152
+ if (longPressTimerRef.current !== null) {
153
+ clearTimeout(longPressTimerRef.current);
154
+ longPressTimerRef.current = null;
155
+ }
156
+ }, []);
157
+ (0, react.useEffect)(() => clearLongPress, [clearLongPress]);
158
+ const handleRef = (0, react.useCallback)((node) => {
159
+ context.triggerRef.current = node;
160
+ require_context_menu_utils.setRef(ref, node);
161
+ }, [context.triggerRef, ref]);
162
+ const handleContextMenu = (event) => {
163
+ onContextMenu?.(event);
164
+ if (event.defaultPrevented || isDisabled) return;
165
+ event.preventDefault();
166
+ context.triggerRef.current = event.currentTarget;
167
+ context.openAt({
168
+ x: event.clientX,
169
+ y: event.clientY
170
+ });
171
+ };
172
+ const handleClick = (event) => {
173
+ onClick?.(event);
174
+ if (event.defaultPrevented || isDisabled || event.button !== 0) return;
175
+ if (event.ctrlKey) {
176
+ event.preventDefault();
177
+ context.triggerRef.current = event.currentTarget;
178
+ context.openAt({
179
+ x: event.clientX,
180
+ y: event.clientY
181
+ });
182
+ return;
183
+ }
184
+ if (context.open) context.close(false);
185
+ };
186
+ const handleKeyDown = (event) => {
187
+ onKeyDown?.(event);
188
+ if (event.defaultPrevented || isDisabled) return;
189
+ const target = event.currentTarget;
190
+ const rect = target.getBoundingClientRect();
191
+ if (event.key === "ContextMenu" || event.shiftKey && event.key === "F10") {
192
+ event.preventDefault();
193
+ context.triggerRef.current = target;
194
+ context.openAt({
195
+ x: rect.left,
196
+ y: rect.bottom
197
+ });
198
+ return;
199
+ }
200
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
201
+ event.preventDefault();
202
+ context.triggerRef.current = target;
203
+ context.openAt({
204
+ x: rect.left,
205
+ y: rect.bottom
206
+ }, event.key === "ArrowUp" ? "last" : "first");
207
+ }
208
+ };
209
+ const handlePointerDown = (event) => {
210
+ onPointerDown?.(event);
211
+ if (event.defaultPrevented || isDisabled || event.pointerType !== "touch") return;
212
+ pointerStartRef.current = {
213
+ x: event.clientX,
214
+ y: event.clientY
215
+ };
216
+ const target = event.currentTarget;
217
+ longPressTimerRef.current = setTimeout(() => {
218
+ const rect = target.getBoundingClientRect();
219
+ context.triggerRef.current = target;
220
+ context.openAt({
221
+ x: rect.left + rect.width / 2,
222
+ y: rect.top + rect.height / 2
223
+ });
224
+ }, 550);
225
+ };
226
+ const handlePointerMove = (event) => {
227
+ onPointerMove?.(event);
228
+ const start = pointerStartRef.current;
229
+ if (start && (Math.abs(event.clientX - start.x) > 10 || Math.abs(event.clientY - start.y) > 10)) {
230
+ clearLongPress();
231
+ pointerStartRef.current = null;
232
+ }
233
+ };
234
+ const handlePointerEnd = (event, callback) => {
235
+ callback?.(event);
236
+ clearLongPress();
237
+ pointerStartRef.current = null;
238
+ };
239
+ const commonProps = {
240
+ ...triggerProps,
241
+ id,
242
+ ref: handleRef,
243
+ className: (0, _puzzle_ui_core.cn)("puzzle-context-menu__trigger", className),
244
+ role: "button",
245
+ tabIndex: isDisabled ? -1 : triggerProps.tabIndex ?? 0,
246
+ "aria-haspopup": "menu",
247
+ "aria-expanded": context.open,
248
+ "aria-controls": context.open ? context.contentId : void 0,
249
+ "aria-disabled": isDisabled || void 0,
250
+ "data-state": context.open ? "open" : "closed",
251
+ onContextMenu: handleContextMenu,
252
+ onClick: handleClick,
253
+ onKeyDown: handleKeyDown,
254
+ onPointerDown: handlePointerDown,
255
+ onPointerMove: handlePointerMove,
256
+ onPointerUp: (event) => handlePointerEnd(event, onPointerUp),
257
+ onPointerCancel: (event) => handlePointerEnd(event, onPointerCancel)
258
+ };
259
+ if (!asChild) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
260
+ ...commonProps,
261
+ children
262
+ });
263
+ const child = react.Children.only(children);
264
+ if (!(0, react.isValidElement)(child) || child.type === react.Fragment) throw new Error("ContextMenu.Trigger with asChild expects one non-Fragment element.");
265
+ const childProps = child.props;
266
+ const childDisabled = isDisabled || childProps.disabled === true || childProps["aria-disabled"] === true || childProps["aria-disabled"] === "true";
267
+ return (0, react.cloneElement)(child, {
268
+ ...commonProps,
269
+ ref: (node) => {
270
+ handleRef(node);
271
+ require_context_menu_utils.setRef(childProps.ref, node);
272
+ },
273
+ className: (0, _puzzle_ui_core.cn)("puzzle-context-menu__trigger", childProps.className, className),
274
+ role: child.type === "button" ? void 0 : "button",
275
+ tabIndex: childDisabled ? -1 : childProps.tabIndex ?? 0,
276
+ "aria-disabled": childDisabled || void 0,
277
+ disabled: child.type === "button" ? childDisabled : void 0,
278
+ onContextMenu: (event) => {
279
+ if (childDisabled) {
280
+ event.preventDefault();
281
+ return;
282
+ }
283
+ childProps.onContextMenu?.(event);
284
+ if (!event.defaultPrevented) handleContextMenu(event);
285
+ },
286
+ onClick: (event) => {
287
+ if (childDisabled) return;
288
+ childProps.onClick?.(event);
289
+ if (!event.defaultPrevented) handleClick(event);
290
+ },
291
+ onKeyDown: (event) => {
292
+ childProps.onKeyDown?.(event);
293
+ if (!event.defaultPrevented) handleKeyDown(event);
294
+ },
295
+ onPointerDown: (event) => {
296
+ childProps.onPointerDown?.(event);
297
+ if (!event.defaultPrevented) handlePointerDown(event);
298
+ },
299
+ onPointerMove: (event) => {
300
+ childProps.onPointerMove?.(event);
301
+ if (!event.defaultPrevented) handlePointerMove(event);
302
+ },
303
+ onPointerUp: (event) => {
304
+ childProps.onPointerUp?.(event);
305
+ handlePointerEnd(event);
306
+ },
307
+ onPointerCancel: (event) => {
308
+ childProps.onPointerCancel?.(event);
309
+ handlePointerEnd(event);
310
+ }
311
+ });
312
+ };
313
+ const ContextMenuContent = ({ children, id, className, style, maxHeight = "min(24rem, calc(100dvh - 1rem))", onKeyDown, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...contentProps }) => {
314
+ const context = useContextMenuContext();
315
+ const positioned = require_index.usePopoutPositioned();
316
+ const menuRef = (0, react.useRef)(null);
317
+ const typeaheadRef = (0, react.useRef)("");
318
+ const typeaheadTimerRef = (0, react.useRef)(null);
319
+ const initialFocusDoneRef = (0, react.useRef)(false);
320
+ (0, react.useEffect)(() => {
321
+ if (!context.open) {
322
+ initialFocusDoneRef.current = false;
323
+ typeaheadRef.current = "";
324
+ if (typeaheadTimerRef.current !== null) {
325
+ clearTimeout(typeaheadTimerRef.current);
326
+ typeaheadTimerRef.current = null;
327
+ }
328
+ }
329
+ }, [context.open]);
330
+ (0, react.useEffect)(() => {
331
+ if (!context.open || !positioned || initialFocusDoneRef.current) return;
332
+ const menu = menuRef.current;
333
+ if (!menu) return;
334
+ initialFocusDoneRef.current = true;
335
+ const items = require_context_menu_utils.getEnabledMenuItems(menu);
336
+ const target = context.focusPositionRef.current === "last" ? items[items.length - 1] : items[0];
337
+ require_context_menu_utils.focusElement(target ?? menu);
338
+ }, [
339
+ context.open,
340
+ context.focusPositionRef,
341
+ positioned
342
+ ]);
343
+ (0, react.useEffect)(() => () => {
344
+ if (typeaheadTimerRef.current !== null) clearTimeout(typeaheadTimerRef.current);
345
+ }, []);
346
+ const focusIndex = (items, index) => {
347
+ if (index >= 0 && index < items.length) require_context_menu_utils.focusElement(items[index]);
348
+ };
349
+ const handleKeyDown = (event) => {
350
+ onKeyDown?.(event);
351
+ if (event.defaultPrevented) return;
352
+ const menu = menuRef.current;
353
+ if (!menu) return;
354
+ if (event.key === "Escape") {
355
+ event.preventDefault();
356
+ context.close(true);
357
+ return;
358
+ }
359
+ if (event.key === "Tab") {
360
+ const ownerDocument = event.currentTarget.ownerDocument;
361
+ const trigger = context.triggerRef.current;
362
+ const selector = [
363
+ "a[href]",
364
+ "area[href]",
365
+ "button:not([disabled])",
366
+ "input:not([disabled]):not([type='hidden'])",
367
+ "select:not([disabled])",
368
+ "textarea:not([disabled])",
369
+ "iframe",
370
+ "summary",
371
+ "[contenteditable='true']",
372
+ "[tabindex]:not([tabindex='-1'])"
373
+ ].join(",");
374
+ const tabbableElements = Array.from(ownerDocument.querySelectorAll(selector)).filter((element) => {
375
+ if (menu.contains(element) || element.hidden || element.tabIndex < 0 || element.getAttribute("aria-disabled") === "true" || element.closest("[hidden], [inert], [aria-hidden='true']")) return false;
376
+ const computedStyle = ownerDocument.defaultView?.getComputedStyle(element);
377
+ return computedStyle?.display !== "none" && computedStyle?.visibility !== "hidden";
378
+ });
379
+ const triggerIndex = trigger ? tabbableElements.indexOf(trigger) : -1;
380
+ const targetIndex = triggerIndex + (event.shiftKey ? -1 : 1);
381
+ const target = triggerIndex >= 0 && tabbableElements.length > 0 ? tabbableElements[(targetIndex + tabbableElements.length) % tabbableElements.length] : void 0;
382
+ context.close(false);
383
+ if (target) {
384
+ event.preventDefault();
385
+ require_context_menu_utils.focusElement(target);
386
+ }
387
+ return;
388
+ }
389
+ const items = require_context_menu_utils.getEnabledMenuItems(menu);
390
+ const activeIndex = items.indexOf(menu.ownerDocument.activeElement);
391
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
392
+ event.preventDefault();
393
+ const direction = event.key === "ArrowDown" ? 1 : -1;
394
+ const startIndex = activeIndex < 0 ? direction === 1 ? -1 : 0 : activeIndex;
395
+ const nextIndex = require_context_menu_utils.findNextMenuItemIndex(items.map(() => ({ disabled: false })), startIndex, direction);
396
+ focusIndex(items, nextIndex);
397
+ return;
398
+ }
399
+ if (event.key === "Home" || event.key === "End") {
400
+ event.preventDefault();
401
+ focusIndex(items, event.key === "Home" ? 0 : items.length - 1);
402
+ return;
403
+ }
404
+ if (event.key === "ArrowRight") {
405
+ const activeItem = items[activeIndex];
406
+ if (activeItem?.hasAttribute("data-puzzle-context-menu-sub-trigger")) {
407
+ event.preventDefault();
408
+ activeItem.click();
409
+ }
410
+ return;
411
+ }
412
+ if (event.key.length !== 1 || event.key === " " || event.ctrlKey || event.metaKey || event.altKey) return;
413
+ event.preventDefault();
414
+ const nextBuffer = `${typeaheadRef.current}${event.key}`;
415
+ const query = [...nextBuffer].every((character) => character.toLocaleLowerCase() === nextBuffer[0].toLocaleLowerCase()) ? event.key : nextBuffer;
416
+ typeaheadRef.current = query;
417
+ if (typeaheadTimerRef.current !== null) clearTimeout(typeaheadTimerRef.current);
418
+ typeaheadTimerRef.current = setTimeout(() => {
419
+ typeaheadRef.current = "";
420
+ typeaheadTimerRef.current = null;
421
+ }, 500);
422
+ const nextIndex = require_context_menu_utils.findMenuItemIndexByText(items.map((item) => ({ text: require_context_menu_utils.getMenuItemText(item) })), query, activeIndex);
423
+ focusIndex(items, nextIndex);
424
+ };
425
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
426
+ ...contentProps,
427
+ ref: menuRef,
428
+ id: id ?? context.contentId,
429
+ className: (0, _puzzle_ui_core.cn)("puzzle-context-menu__content", className),
430
+ role: "menu",
431
+ "aria-orientation": "vertical",
432
+ "aria-label": ariaLabel,
433
+ "aria-labelledby": ariaLabel ? void 0 : ariaLabelledBy ?? context.triggerId,
434
+ tabIndex: -1,
435
+ "data-state": context.open ? "open" : "closed",
436
+ style: {
437
+ ...style,
438
+ maxHeight
439
+ },
440
+ onKeyDown: handleKeyDown,
441
+ children
442
+ });
443
+ };
444
+ const ContextMenuItem = ({ children, className, disabled = false, "aria-disabled": ariaDisabled, textValue, closeOnSelect = true, variant = "default", onClick, onSelect, ...itemProps }) => {
445
+ const context = useContextMenuContext();
446
+ const itemDisabled = disabled || ariaDisabled === true || ariaDisabled === "true";
447
+ const handleClick = (event) => {
448
+ if (itemDisabled) {
449
+ event.preventDefault();
450
+ event.stopPropagation();
451
+ return;
452
+ }
453
+ onClick?.(event);
454
+ if (event.defaultPrevented) return;
455
+ onSelect?.(event);
456
+ if (!event.defaultPrevented && closeOnSelect) context.close(true);
457
+ };
458
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
459
+ ...itemProps,
460
+ type: "button",
461
+ className: (0, _puzzle_ui_core.cn)("puzzle-context-menu__item", variant === "danger" && "puzzle-context-menu__item--danger", className),
462
+ role: "menuitem",
463
+ tabIndex: -1,
464
+ disabled: itemDisabled,
465
+ "aria-disabled": itemDisabled || void 0,
466
+ "data-puzzle-context-menu-item": "true",
467
+ "data-text-value": textValue,
468
+ onClick: handleClick,
469
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
470
+ className: "puzzle-context-menu__item-label",
471
+ children
472
+ })
473
+ });
474
+ };
475
+ const ContextMenuGroup = ({ children, className, "aria-label": ariaLabel, ...groupProps }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
476
+ ...groupProps,
477
+ className: (0, _puzzle_ui_core.cn)("puzzle-context-menu__group", className),
478
+ role: "group",
479
+ "aria-label": ariaLabel,
480
+ children
481
+ });
482
+ const ContextMenuLabel = ({ children, className, ...labelProps }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
483
+ ...labelProps,
484
+ className: (0, _puzzle_ui_core.cn)("puzzle-context-menu__label", className),
485
+ children
486
+ });
487
+ const ContextMenuSeparator = ({ className, ...separatorProps }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
488
+ ...separatorProps,
489
+ className: (0, _puzzle_ui_core.cn)("puzzle-context-menu__separator", className),
490
+ role: "separator",
491
+ "aria-orientation": "horizontal"
492
+ });
493
+ const ContextMenuCheckboxItem = ({ children, checked, defaultChecked = false, onCheckedChange, closeOnSelect = false, textValue, className, disabled = false, "aria-disabled": ariaDisabled, onClick, onSelect, ...itemProps }) => {
494
+ const context = useContextMenuContext();
495
+ const itemDisabled = disabled || ariaDisabled === true || ariaDisabled === "true";
496
+ const controlled = checked !== void 0;
497
+ const [internalChecked, setInternalChecked] = (0, react.useState)(defaultChecked);
498
+ const resolvedChecked = checked ?? internalChecked;
499
+ const handleClick = (event) => {
500
+ if (itemDisabled) {
501
+ event.preventDefault();
502
+ return;
503
+ }
504
+ onClick?.(event);
505
+ if (event.defaultPrevented) return;
506
+ onSelect?.(event);
507
+ if (event.defaultPrevented) return;
508
+ const nextChecked = resolvedChecked !== true;
509
+ if (!controlled) setInternalChecked(nextChecked);
510
+ onCheckedChange?.(nextChecked);
511
+ if (closeOnSelect) context.close(true);
512
+ };
513
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
514
+ ...itemProps,
515
+ type: "button",
516
+ className: (0, _puzzle_ui_core.cn)("puzzle-context-menu__item", className),
517
+ role: "menuitemcheckbox",
518
+ tabIndex: -1,
519
+ disabled: itemDisabled,
520
+ "aria-disabled": itemDisabled || void 0,
521
+ "aria-checked": resolvedChecked === "indeterminate" ? "mixed" : resolvedChecked,
522
+ "data-state": resolvedChecked === "indeterminate" ? "indeterminate" : resolvedChecked ? "checked" : "unchecked",
523
+ "data-puzzle-context-menu-item": "true",
524
+ "data-text-value": textValue,
525
+ onClick: handleClick,
526
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
527
+ className: "puzzle-context-menu__indicator",
528
+ "aria-hidden": "true",
529
+ children: resolvedChecked === "indeterminate" ? "−" : resolvedChecked ? "✓" : ""
530
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
531
+ className: "puzzle-context-menu__item-label",
532
+ children
533
+ })]
534
+ });
535
+ };
536
+ const ContextMenuRadioGroup = ({ children, value, defaultValue, onValueChange, className, ...groupProps }) => {
537
+ const controlled = value !== void 0;
538
+ const [internalValue, setInternalValue] = (0, react.useState)(defaultValue);
539
+ const resolvedValue = controlled ? value : internalValue;
540
+ const setValue = (0, react.useCallback)((nextValue) => {
541
+ if (nextValue === resolvedValue) return;
542
+ if (!controlled) setInternalValue(nextValue);
543
+ onValueChange?.(nextValue);
544
+ }, [
545
+ controlled,
546
+ onValueChange,
547
+ resolvedValue
548
+ ]);
549
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RadioGroupContext.Provider, {
550
+ value: {
551
+ value: resolvedValue,
552
+ setValue
553
+ },
554
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
555
+ ...groupProps,
556
+ className: (0, _puzzle_ui_core.cn)("puzzle-context-menu__group", className),
557
+ role: "group",
558
+ children
559
+ })
560
+ });
561
+ };
562
+ const ContextMenuRadioItem = ({ children, value, closeOnSelect = true, textValue, className, disabled = false, "aria-disabled": ariaDisabled, onClick, onSelect, ...itemProps }) => {
563
+ const context = useContextMenuContext();
564
+ const radioGroup = useRadioGroupContext();
565
+ const checked = radioGroup.value === value;
566
+ const itemDisabled = disabled || ariaDisabled === true || ariaDisabled === "true";
567
+ const handleClick = (event) => {
568
+ if (itemDisabled) {
569
+ event.preventDefault();
570
+ return;
571
+ }
572
+ onClick?.(event);
573
+ if (event.defaultPrevented) return;
574
+ onSelect?.(event);
575
+ if (event.defaultPrevented) return;
576
+ radioGroup.setValue(value);
577
+ if (closeOnSelect) context.close(true);
578
+ };
579
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
580
+ ...itemProps,
581
+ type: "button",
582
+ className: (0, _puzzle_ui_core.cn)("puzzle-context-menu__item", className),
583
+ role: "menuitemradio",
584
+ tabIndex: -1,
585
+ disabled: itemDisabled,
586
+ "aria-disabled": itemDisabled || void 0,
587
+ "aria-checked": checked,
588
+ "data-state": checked ? "checked" : "unchecked",
589
+ "data-puzzle-context-menu-item": "true",
590
+ "data-text-value": textValue,
591
+ onClick: handleClick,
592
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
593
+ className: "puzzle-context-menu__indicator",
594
+ "aria-hidden": "true",
595
+ children: checked ? "●" : ""
596
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
597
+ className: "puzzle-context-menu__item-label",
598
+ children
599
+ })]
600
+ });
601
+ };
602
+ const ContextMenuSub = ({ children, defaultOpen = false }) => {
603
+ const rootContext = useContextMenuContext();
604
+ const baseId = (0, react.useId)();
605
+ const triggerId = `${baseId}-trigger`;
606
+ const contentId = `${baseId}-content`;
607
+ const [open, setOpen] = (0, react.useState)(defaultOpen);
608
+ const triggerRef = (0, react.useRef)(null);
609
+ const elements = getMenuElements(children);
610
+ const triggers = elements.filter((child) => child.type === ContextMenuSubTrigger);
611
+ const contents = elements.filter((child) => child.type === ContextMenuSubContent);
612
+ const triggerElement = triggers[0];
613
+ const contentElement = contents[0];
614
+ if (triggers.length !== 1 || contents.length !== 1 || !triggerElement || !contentElement) throw new Error("ContextMenu.Sub requires exactly one SubTrigger and one SubContent.");
615
+ (0, react.useEffect)(() => {
616
+ if (!rootContext.open) setOpen(false);
617
+ }, [rootContext.open]);
618
+ const close = (0, react.useCallback)((restoreFocus = false) => {
619
+ setOpen(false);
620
+ if (restoreFocus) require_context_menu_utils.focusElement(triggerRef.current ?? void 0);
621
+ }, []);
622
+ const subContext = (0, react.useMemo)(() => ({
623
+ open,
624
+ setOpen,
625
+ triggerId,
626
+ contentId,
627
+ triggerRef,
628
+ close
629
+ }), [
630
+ open,
631
+ triggerId,
632
+ contentId,
633
+ close
634
+ ]);
635
+ const trigger = (0, react.cloneElement)(triggerElement, { id: triggerId });
636
+ const content = (0, react.cloneElement)(contentElement, { id: contentId });
637
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubContext.Provider, {
638
+ value: subContext,
639
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.Popout, {
640
+ trigger,
641
+ open,
642
+ onOpenChange: setOpen,
643
+ position: "right",
644
+ align: "start",
645
+ offset: 4,
646
+ collisionPadding: 8,
647
+ action: "hover",
648
+ closeOnOutsideClick: true,
649
+ closeOnEscape: true,
650
+ contentClassName: "puzzle-context-menu__popover puzzle-context-menu__submenu-popover",
651
+ children: content
652
+ })
653
+ });
654
+ };
655
+ const ContextMenuSubTrigger = (0, react.forwardRef)(({ children, className, disabled = false, onClick, onKeyDown, ...props }, ref) => {
656
+ const context = useSubContext();
657
+ const handleClick = (event) => {
658
+ onClick?.(event);
659
+ if (!event.defaultPrevented && !disabled) context.setOpen(true);
660
+ };
661
+ const handleKeyDown = (event) => {
662
+ onKeyDown?.(event);
663
+ if (!event.defaultPrevented && !disabled && event.key === "ArrowRight") {
664
+ event.preventDefault();
665
+ context.setOpen(true);
666
+ }
667
+ if (!event.defaultPrevented && !disabled && event.key === "ArrowLeft") {
668
+ event.preventDefault();
669
+ context.close(true);
670
+ }
671
+ };
672
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
673
+ ...props,
674
+ ref: (node) => {
675
+ context.triggerRef.current = node;
676
+ require_context_menu_utils.setRef(ref, node);
677
+ },
678
+ id: context.triggerId,
679
+ type: "button",
680
+ role: "menuitem",
681
+ tabIndex: -1,
682
+ className: (0, _puzzle_ui_core.cn)("puzzle-context-menu__item", className),
683
+ "aria-haspopup": "menu",
684
+ "aria-expanded": context.open,
685
+ "aria-controls": context.open ? context.contentId : void 0,
686
+ "aria-disabled": disabled || void 0,
687
+ disabled,
688
+ "data-puzzle-context-menu-item": "true",
689
+ "data-puzzle-context-menu-sub-trigger": "true",
690
+ onClick: handleClick,
691
+ onKeyDown: handleKeyDown,
692
+ onPointerEnter: () => {
693
+ if (!disabled) context.setOpen(true);
694
+ },
695
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
696
+ className: "puzzle-context-menu__item-label",
697
+ children
698
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
699
+ className: "puzzle-context-menu__submenu-indicator",
700
+ "aria-hidden": "true",
701
+ children: "›"
702
+ })]
703
+ });
704
+ });
705
+ ContextMenuSubTrigger.displayName = "ContextMenuSubTrigger";
706
+ const ContextMenuSubContent = ({ children, id, className, style, maxHeight = "min(24rem, calc(100dvh - 1rem))", onKeyDown, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...contentProps }) => {
707
+ const context = useSubContext();
708
+ const positioned = require_index.usePopoutPositioned();
709
+ const menuRef = (0, react.useRef)(null);
710
+ const typeaheadRef = (0, react.useRef)("");
711
+ const typeaheadTimerRef = (0, react.useRef)(null);
712
+ const initialFocusDoneRef = (0, react.useRef)(false);
713
+ (0, react.useEffect)(() => {
714
+ if (!context.open) {
715
+ initialFocusDoneRef.current = false;
716
+ typeaheadRef.current = "";
717
+ if (typeaheadTimerRef.current !== null) {
718
+ clearTimeout(typeaheadTimerRef.current);
719
+ typeaheadTimerRef.current = null;
720
+ }
721
+ }
722
+ }, [context.open]);
723
+ (0, react.useEffect)(() => () => {
724
+ if (typeaheadTimerRef.current !== null) clearTimeout(typeaheadTimerRef.current);
725
+ }, []);
726
+ (0, react.useEffect)(() => {
727
+ if (!context.open || !positioned || initialFocusDoneRef.current) return;
728
+ const menu = menuRef.current;
729
+ if (!menu) return;
730
+ initialFocusDoneRef.current = true;
731
+ const items = require_context_menu_utils.getEnabledMenuItems(menu);
732
+ require_context_menu_utils.focusElement(items[0] ?? menu);
733
+ }, [context.open, positioned]);
734
+ const handleKeyDown = (event) => {
735
+ onKeyDown?.(event);
736
+ if (event.defaultPrevented) return;
737
+ event.stopPropagation();
738
+ if (event.key === "ArrowLeft") {
739
+ event.preventDefault();
740
+ context.close(true);
741
+ return;
742
+ }
743
+ if (event.key === "Escape") {
744
+ event.preventDefault();
745
+ context.close(true);
746
+ return;
747
+ }
748
+ const menu = menuRef.current;
749
+ if (!menu) return;
750
+ const items = require_context_menu_utils.getEnabledMenuItems(menu);
751
+ const activeIndex = items.indexOf(menu.ownerDocument.activeElement);
752
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
753
+ event.preventDefault();
754
+ const direction = event.key === "ArrowDown" ? 1 : -1;
755
+ const startIndex = activeIndex < 0 ? direction === 1 ? -1 : 0 : activeIndex;
756
+ const nextIndex = require_context_menu_utils.findNextMenuItemIndex(items.map(() => ({ disabled: false })), startIndex, direction);
757
+ if (nextIndex >= 0) require_context_menu_utils.focusElement(items[nextIndex]);
758
+ return;
759
+ }
760
+ if (event.key === "Home" || event.key === "End") {
761
+ event.preventDefault();
762
+ const target = event.key === "Home" ? items[0] : items[items.length - 1];
763
+ if (target) require_context_menu_utils.focusElement(target);
764
+ return;
765
+ }
766
+ if (event.key.length !== 1 || event.key === " " || event.ctrlKey || event.metaKey || event.altKey) return;
767
+ event.preventDefault();
768
+ const nextBuffer = `${typeaheadRef.current}${event.key}`;
769
+ const query = [...nextBuffer].every((character) => character.toLocaleLowerCase() === nextBuffer[0].toLocaleLowerCase()) ? event.key : nextBuffer;
770
+ typeaheadRef.current = query;
771
+ if (typeaheadTimerRef.current !== null) clearTimeout(typeaheadTimerRef.current);
772
+ typeaheadTimerRef.current = setTimeout(() => {
773
+ typeaheadRef.current = "";
774
+ typeaheadTimerRef.current = null;
775
+ }, 500);
776
+ const nextIndex = require_context_menu_utils.findMenuItemIndexByText(items.map((item) => ({ text: require_context_menu_utils.getMenuItemText(item) })), query, activeIndex);
777
+ if (nextIndex >= 0) require_context_menu_utils.focusElement(items[nextIndex]);
778
+ };
779
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
780
+ ...contentProps,
781
+ ref: menuRef,
782
+ id: id ?? context.contentId,
783
+ className: (0, _puzzle_ui_core.cn)("puzzle-context-menu__content", className),
784
+ role: "menu",
785
+ "aria-orientation": "vertical",
786
+ "aria-label": ariaLabel,
787
+ "aria-labelledby": ariaLabel ? void 0 : ariaLabelledBy ?? context.triggerId,
788
+ tabIndex: -1,
789
+ "data-state": context.open ? "open" : "closed",
790
+ style: {
791
+ ...style,
792
+ maxHeight
793
+ },
794
+ onKeyDown: handleKeyDown,
795
+ children
796
+ });
797
+ };
798
+ const ContextMenuSubCompound = Object.assign(ContextMenuSub, {
799
+ Trigger: ContextMenuSubTrigger,
800
+ Content: ContextMenuSubContent
801
+ });
802
+ const ContextMenu = Object.assign(ContextMenuRoot, {
803
+ Trigger: ContextMenuTrigger,
804
+ Content: ContextMenuContent,
805
+ Item: ContextMenuItem,
806
+ Group: ContextMenuGroup,
807
+ Label: ContextMenuLabel,
808
+ Separator: ContextMenuSeparator,
809
+ CheckboxItem: ContextMenuCheckboxItem,
810
+ RadioGroup: ContextMenuRadioGroup,
811
+ RadioItem: ContextMenuRadioItem,
812
+ Sub: ContextMenuSubCompound,
813
+ SubTrigger: ContextMenuSubTrigger,
814
+ SubContent: ContextMenuSubContent
815
+ });
816
+ exports.ContextMenu = ContextMenu;
817
+ exports.ContextMenuCheckboxItem = ContextMenuCheckboxItem;
818
+ exports.ContextMenuContent = ContextMenuContent;
819
+ exports.ContextMenuGroup = ContextMenuGroup;
820
+ exports.ContextMenuItem = ContextMenuItem;
821
+ exports.ContextMenuLabel = ContextMenuLabel;
822
+ exports.ContextMenuRadioGroup = ContextMenuRadioGroup;
823
+ exports.ContextMenuRadioItem = ContextMenuRadioItem;
824
+ exports.ContextMenuSeparator = ContextMenuSeparator;
825
+ exports.ContextMenuSub = ContextMenuSubCompound;
826
+ exports.ContextMenuSubContent = ContextMenuSubContent;
827
+ exports.ContextMenuSubTrigger = ContextMenuSubTrigger;
828
+ exports.ContextMenuTrigger = ContextMenuTrigger;
829
+
830
+ //# sourceMappingURL=index.cjs.map