@puzzle-ui/ui 0.4.0 → 0.5.1

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 (163) hide show
  1. package/README.md +61 -77
  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/rating/index.cjs +84 -0
  12. package/dist/components/data-display/rating/index.cjs.map +1 -0
  13. package/dist/components/data-display/rating/index.d.cts +3 -0
  14. package/dist/components/data-display/rating/index.d.ts +3 -0
  15. package/dist/components/data-display/rating/index.js +84 -0
  16. package/dist/components/data-display/rating/index.js.map +1 -0
  17. package/dist/components/data-display/rating/rating.css +72 -0
  18. package/dist/components/data-display/rating/rating.types.d.cts +15 -0
  19. package/dist/components/data-display/rating/rating.types.d.ts +15 -0
  20. package/dist/components/data-display/rating/rating.utils.cjs +27 -0
  21. package/dist/components/data-display/rating/rating.utils.cjs.map +1 -0
  22. package/dist/components/data-display/rating/rating.utils.js +25 -0
  23. package/dist/components/data-display/rating/rating.utils.js.map +1 -0
  24. package/dist/components/forms/checkbox/checkbox.css +2 -10
  25. package/dist/components/forms/input-group/input-group.d.cts +4 -2
  26. package/dist/components/forms/input-group/input-group.d.ts +4 -2
  27. package/dist/components/forms/radio-group/index.d.cts +2 -1
  28. package/dist/components/forms/radio-group/index.d.ts +2 -1
  29. package/dist/components/forms/stepper/index.d.cts +4 -2
  30. package/dist/components/forms/stepper/index.d.ts +4 -2
  31. package/dist/components/forms/time-picker/index.cjs +108 -0
  32. package/dist/components/forms/time-picker/index.cjs.map +1 -0
  33. package/dist/components/forms/time-picker/index.d.cts +3 -0
  34. package/dist/components/forms/time-picker/index.d.ts +3 -0
  35. package/dist/components/forms/time-picker/index.js +108 -0
  36. package/dist/components/forms/time-picker/index.js.map +1 -0
  37. package/dist/components/forms/time-picker/time-picker.css +166 -0
  38. package/dist/components/forms/time-picker/time-picker.types.d.cts +34 -0
  39. package/dist/components/forms/time-picker/time-picker.types.d.ts +34 -0
  40. package/dist/components/forms/time-picker/time-picker.utils.cjs +25 -0
  41. package/dist/components/forms/time-picker/time-picker.utils.cjs.map +1 -0
  42. package/dist/components/forms/time-picker/time-picker.utils.js +24 -0
  43. package/dist/components/forms/time-picker/time-picker.utils.js.map +1 -0
  44. package/dist/components/forms/token-input/index.cjs +189 -0
  45. package/dist/components/forms/token-input/index.cjs.map +1 -0
  46. package/dist/components/forms/token-input/index.d.cts +3 -0
  47. package/dist/components/forms/token-input/index.d.ts +3 -0
  48. package/dist/components/forms/token-input/index.js +189 -0
  49. package/dist/components/forms/token-input/index.js.map +1 -0
  50. package/dist/components/forms/token-input/token-input.css +145 -0
  51. package/dist/components/forms/token-input/token-input.types.d.cts +48 -0
  52. package/dist/components/forms/token-input/token-input.types.d.ts +48 -0
  53. package/dist/components/forms/token-input/token-input.utils.cjs +30 -0
  54. package/dist/components/forms/token-input/token-input.utils.cjs.map +1 -0
  55. package/dist/components/forms/token-input/token-input.utils.js +24 -0
  56. package/dist/components/forms/token-input/token-input.utils.js.map +1 -0
  57. package/dist/components/layout/aspect-ratio/aspect-ratio.css +14 -0
  58. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.cjs +11 -0
  59. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.cjs.map +1 -0
  60. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.js +11 -0
  61. package/dist/components/layout/aspect-ratio/aspect-ratio.utils.js.map +1 -0
  62. package/dist/components/layout/aspect-ratio/index.cjs +16 -0
  63. package/dist/components/layout/aspect-ratio/index.cjs.map +1 -0
  64. package/dist/components/layout/aspect-ratio/index.d.cts +11 -0
  65. package/dist/components/layout/aspect-ratio/index.d.ts +11 -0
  66. package/dist/components/layout/aspect-ratio/index.js +16 -0
  67. package/dist/components/layout/aspect-ratio/index.js.map +1 -0
  68. package/dist/components/layout/container/container.css +1 -0
  69. package/dist/components/layout/page-layout/page-layout.css +1 -0
  70. package/dist/components/layout/scroll-area/index.cjs +27 -0
  71. package/dist/components/layout/scroll-area/index.cjs.map +1 -0
  72. package/dist/components/layout/scroll-area/index.d.cts +15 -0
  73. package/dist/components/layout/scroll-area/index.d.ts +15 -0
  74. package/dist/components/layout/scroll-area/index.js +27 -0
  75. package/dist/components/layout/scroll-area/index.js.map +1 -0
  76. package/dist/components/layout/scroll-area/scroll-area.css +25 -0
  77. package/dist/components/layout/scroll-area/scroll-area.utils.cjs +8 -0
  78. package/dist/components/layout/scroll-area/scroll-area.utils.cjs.map +1 -0
  79. package/dist/components/layout/scroll-area/scroll-area.utils.d.cts +2 -0
  80. package/dist/components/layout/scroll-area/scroll-area.utils.d.ts +2 -0
  81. package/dist/components/layout/scroll-area/scroll-area.utils.js +8 -0
  82. package/dist/components/layout/scroll-area/scroll-area.utils.js.map +1 -0
  83. package/dist/components/layout/section/index.cjs +10 -4
  84. package/dist/components/layout/section/index.cjs.map +1 -1
  85. package/dist/components/layout/section/index.d.cts +1 -1
  86. package/dist/components/layout/section/index.d.ts +1 -1
  87. package/dist/components/layout/section/index.js +11 -5
  88. package/dist/components/layout/section/index.js.map +1 -1
  89. package/dist/components/layout/section/section.css +9 -3
  90. package/dist/components/layout/section/types.d.cts +5 -5
  91. package/dist/components/layout/section/types.d.ts +5 -5
  92. package/dist/components/layout/separator/index.cjs +18 -0
  93. package/dist/components/layout/separator/index.cjs.map +1 -0
  94. package/dist/components/layout/separator/index.d.cts +17 -0
  95. package/dist/components/layout/separator/index.d.ts +17 -0
  96. package/dist/components/layout/separator/index.js +18 -0
  97. package/dist/components/layout/separator/index.js.map +1 -0
  98. package/dist/components/layout/separator/separator.css +15 -0
  99. package/dist/components/layout/separator/separator.utils.cjs +11 -0
  100. package/dist/components/layout/separator/separator.utils.cjs.map +1 -0
  101. package/dist/components/layout/separator/separator.utils.d.cts +2 -0
  102. package/dist/components/layout/separator/separator.utils.d.ts +2 -0
  103. package/dist/components/layout/separator/separator.utils.js +11 -0
  104. package/dist/components/layout/separator/separator.utils.js.map +1 -0
  105. package/dist/components/layout/stack/index.cjs +18 -2
  106. package/dist/components/layout/stack/index.cjs.map +1 -1
  107. package/dist/components/layout/stack/index.d.cts +8 -6
  108. package/dist/components/layout/stack/index.d.ts +8 -6
  109. package/dist/components/layout/stack/index.js +18 -2
  110. package/dist/components/layout/stack/index.js.map +1 -1
  111. package/dist/components/layout/stack/stack.css +118 -138
  112. package/dist/components/navigation/carousel/index.cjs +4 -1
  113. package/dist/components/navigation/carousel/index.cjs.map +1 -1
  114. package/dist/components/navigation/carousel/index.js +4 -1
  115. package/dist/components/navigation/carousel/index.js.map +1 -1
  116. package/dist/components/navigation/tabs/index.d.cts +3 -2
  117. package/dist/components/navigation/tabs/index.d.ts +3 -2
  118. package/dist/components/navigation/tree-view/index.cjs +1 -1
  119. package/dist/components/navigation/tree-view/index.cjs.map +1 -1
  120. package/dist/components/navigation/tree-view/index.d.cts +5 -1
  121. package/dist/components/navigation/tree-view/index.d.ts +5 -1
  122. package/dist/components/navigation/tree-view/index.js +1 -1
  123. package/dist/components/navigation/tree-view/index.js.map +1 -1
  124. package/dist/components/navigation/tree-view/tree-view.css +5 -3
  125. package/dist/components/overlays/context-menu/context-menu.css +151 -0
  126. package/dist/components/overlays/context-menu/context-menu.types.d.cts +69 -0
  127. package/dist/components/overlays/context-menu/context-menu.types.d.ts +69 -0
  128. package/dist/components/overlays/context-menu/context-menu.utils.cjs +46 -0
  129. package/dist/components/overlays/context-menu/context-menu.utils.cjs.map +1 -0
  130. package/dist/components/overlays/context-menu/context-menu.utils.js +40 -0
  131. package/dist/components/overlays/context-menu/context-menu.utils.js.map +1 -0
  132. package/dist/components/overlays/context-menu/index.cjs +830 -0
  133. package/dist/components/overlays/context-menu/index.cjs.map +1 -0
  134. package/dist/components/overlays/context-menu/index.d.cts +42 -0
  135. package/dist/components/overlays/context-menu/index.d.ts +42 -0
  136. package/dist/components/overlays/context-menu/index.js +818 -0
  137. package/dist/components/overlays/context-menu/index.js.map +1 -0
  138. package/dist/components/typography/heading.cjs +6 -2
  139. package/dist/components/typography/heading.cjs.map +1 -1
  140. package/dist/components/typography/heading.d.cts +3 -2
  141. package/dist/components/typography/heading.d.ts +3 -2
  142. package/dist/components/typography/heading.js +6 -2
  143. package/dist/components/typography/heading.js.map +1 -1
  144. package/dist/components/typography/text.cjs +6 -2
  145. package/dist/components/typography/text.cjs.map +1 -1
  146. package/dist/components/typography/text.d.cts +3 -2
  147. package/dist/components/typography/text.d.ts +3 -2
  148. package/dist/components/typography/text.js +6 -2
  149. package/dist/components/typography/text.js.map +1 -1
  150. package/dist/components/utilities/preview/index.cjs +88 -20
  151. package/dist/components/utilities/preview/index.cjs.map +1 -1
  152. package/dist/components/utilities/preview/index.d.cts +6 -2
  153. package/dist/components/utilities/preview/index.d.ts +6 -2
  154. package/dist/components/utilities/preview/index.js +88 -20
  155. package/dist/components/utilities/preview/index.js.map +1 -1
  156. package/dist/components/utilities/preview/preview.css +26 -11
  157. package/dist/components/utilities/transition/index.d.cts +1 -1
  158. package/dist/components/utilities/transition/index.d.ts +1 -1
  159. package/dist/index.cjs +123 -97
  160. package/dist/index.d.cts +15 -2
  161. package/dist/index.d.ts +15 -2
  162. package/dist/index.js +8 -1
  163. package/package.json +11 -6
@@ -0,0 +1,69 @@
1
+ import { ButtonHTMLAttributes, CSSProperties, HTMLAttributes, MouseEvent, ReactNode, Ref } from "react";
2
+ export type ContextMenuItemVariant = "default" | "danger";
3
+ export type ContextMenuCheckedState = boolean | "indeterminate";
4
+ export type ContextMenuProps = {
5
+ children: ReactNode;
6
+ open?: boolean;
7
+ defaultOpen?: boolean;
8
+ onOpenChange?: (open: boolean) => void;
9
+ disabled?: boolean;
10
+ triggerClassName?: string;
11
+ contentClassName?: string;
12
+ };
13
+ export type ContextMenuTriggerProps = Omit<HTMLAttributes<HTMLElement>, "children"> & {
14
+ children: ReactNode;
15
+ asChild?: boolean;
16
+ disabled?: boolean;
17
+ ref?: Ref<HTMLElement>;
18
+ };
19
+ export type ContextMenuContentProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
20
+ children: ReactNode;
21
+ maxHeight?: CSSProperties["maxHeight"];
22
+ };
23
+ export type ContextMenuItemProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children" | "onSelect"> & {
24
+ children: ReactNode;
25
+ textValue?: string;
26
+ closeOnSelect?: boolean;
27
+ variant?: ContextMenuItemVariant;
28
+ onSelect?: (event: MouseEvent<HTMLElement>) => void;
29
+ };
30
+ export type ContextMenuGroupProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
31
+ children: ReactNode;
32
+ };
33
+ export type ContextMenuLabelProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
34
+ children: ReactNode;
35
+ };
36
+ export type ContextMenuSeparatorProps = HTMLAttributes<HTMLDivElement>;
37
+ export type ContextMenuCheckboxItemProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children" | "onSelect"> & {
38
+ children: ReactNode;
39
+ checked?: ContextMenuCheckedState;
40
+ defaultChecked?: boolean;
41
+ onCheckedChange?: (checked: boolean) => void;
42
+ closeOnSelect?: boolean;
43
+ textValue?: string;
44
+ onSelect?: (event: MouseEvent<HTMLElement>) => void;
45
+ };
46
+ export type ContextMenuRadioGroupProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
47
+ children: ReactNode;
48
+ value?: string;
49
+ defaultValue?: string;
50
+ onValueChange?: (value: string) => void;
51
+ };
52
+ export type ContextMenuRadioItemProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children" | "onSelect"> & {
53
+ children: ReactNode;
54
+ value: string;
55
+ closeOnSelect?: boolean;
56
+ textValue?: string;
57
+ onSelect?: (event: MouseEvent<HTMLElement>) => void;
58
+ };
59
+ export type ContextMenuSubProps = {
60
+ children: ReactNode;
61
+ defaultOpen?: boolean;
62
+ };
63
+ export type ContextMenuSubTriggerProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> & {
64
+ children: ReactNode;
65
+ };
66
+ export type ContextMenuSubContentProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
67
+ children: ReactNode;
68
+ maxHeight?: CSSProperties["maxHeight"];
69
+ };
@@ -0,0 +1,69 @@
1
+ import { ButtonHTMLAttributes, CSSProperties, HTMLAttributes, MouseEvent, ReactNode, Ref } from "react";
2
+ export type ContextMenuItemVariant = "default" | "danger";
3
+ export type ContextMenuCheckedState = boolean | "indeterminate";
4
+ export type ContextMenuProps = {
5
+ children: ReactNode;
6
+ open?: boolean;
7
+ defaultOpen?: boolean;
8
+ onOpenChange?: (open: boolean) => void;
9
+ disabled?: boolean;
10
+ triggerClassName?: string;
11
+ contentClassName?: string;
12
+ };
13
+ export type ContextMenuTriggerProps = Omit<HTMLAttributes<HTMLElement>, "children"> & {
14
+ children: ReactNode;
15
+ asChild?: boolean;
16
+ disabled?: boolean;
17
+ ref?: Ref<HTMLElement>;
18
+ };
19
+ export type ContextMenuContentProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
20
+ children: ReactNode;
21
+ maxHeight?: CSSProperties["maxHeight"];
22
+ };
23
+ export type ContextMenuItemProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children" | "onSelect"> & {
24
+ children: ReactNode;
25
+ textValue?: string;
26
+ closeOnSelect?: boolean;
27
+ variant?: ContextMenuItemVariant;
28
+ onSelect?: (event: MouseEvent<HTMLElement>) => void;
29
+ };
30
+ export type ContextMenuGroupProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
31
+ children: ReactNode;
32
+ };
33
+ export type ContextMenuLabelProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
34
+ children: ReactNode;
35
+ };
36
+ export type ContextMenuSeparatorProps = HTMLAttributes<HTMLDivElement>;
37
+ export type ContextMenuCheckboxItemProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children" | "onSelect"> & {
38
+ children: ReactNode;
39
+ checked?: ContextMenuCheckedState;
40
+ defaultChecked?: boolean;
41
+ onCheckedChange?: (checked: boolean) => void;
42
+ closeOnSelect?: boolean;
43
+ textValue?: string;
44
+ onSelect?: (event: MouseEvent<HTMLElement>) => void;
45
+ };
46
+ export type ContextMenuRadioGroupProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
47
+ children: ReactNode;
48
+ value?: string;
49
+ defaultValue?: string;
50
+ onValueChange?: (value: string) => void;
51
+ };
52
+ export type ContextMenuRadioItemProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children" | "onSelect"> & {
53
+ children: ReactNode;
54
+ value: string;
55
+ closeOnSelect?: boolean;
56
+ textValue?: string;
57
+ onSelect?: (event: MouseEvent<HTMLElement>) => void;
58
+ };
59
+ export type ContextMenuSubProps = {
60
+ children: ReactNode;
61
+ defaultOpen?: boolean;
62
+ };
63
+ export type ContextMenuSubTriggerProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> & {
64
+ children: ReactNode;
65
+ };
66
+ export type ContextMenuSubContentProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
67
+ children: ReactNode;
68
+ maxHeight?: CSSProperties["maxHeight"];
69
+ };
@@ -0,0 +1,46 @@
1
+ // src/components/overlays/context-menu/context-menu.utils.ts
2
+ const normalizeMenuText = (value) => value.normalize("NFKD").replace(/\p{Diacritic}/gu, "").trim().toLocaleLowerCase();
3
+ const findNextMenuItemIndex = (items, currentIndex, direction, loop = true) => {
4
+ if (items.length === 0) return -1;
5
+ for (let step = 1; step <= items.length; step += 1) {
6
+ let index = currentIndex + step * direction;
7
+ if (loop) index = (index + items.length) % items.length;
8
+ else if (index < 0 || index >= items.length) return -1;
9
+ if (!items[index].disabled) return index;
10
+ }
11
+ return -1;
12
+ };
13
+ const findMenuItemIndexByText = (items, query, currentIndex = -1) => {
14
+ const normalizedQuery = normalizeMenuText(query);
15
+ if (!normalizedQuery || items.length === 0) return -1;
16
+ for (let step = 1; step <= items.length; step += 1) {
17
+ const index = (currentIndex + step + items.length) % items.length;
18
+ const item = items[index];
19
+ if (!item.disabled && normalizeMenuText(item.text ?? "").startsWith(normalizedQuery)) return index;
20
+ }
21
+ return -1;
22
+ };
23
+ const setRef = (ref, value) => {
24
+ if (typeof ref === "function") ref(value);
25
+ else if (ref) ref.current = value;
26
+ };
27
+ const getEnabledMenuItems = (menu) => Array.from(menu.querySelectorAll("[data-puzzle-context-menu-item=\"true\"]")).filter((item) => item.getAttribute("aria-disabled") !== "true" && !("disabled" in item && Boolean(item.disabled)) && !item.hasAttribute("hidden") && !item.closest("[hidden], [inert], [aria-hidden='true']"));
28
+ const focusElement = (element) => {
29
+ if (!element) return;
30
+ try {
31
+ element.focus({ preventScroll: true });
32
+ } catch {
33
+ element.focus();
34
+ }
35
+ element.scrollIntoView?.({ block: "nearest" });
36
+ };
37
+ const getMenuItemText = (item) => item.dataset.textValue ?? item.querySelector(".puzzle-context-menu__item-label")?.textContent ?? item.textContent ?? "";
38
+ exports.findMenuItemIndexByText = findMenuItemIndexByText;
39
+ exports.findNextMenuItemIndex = findNextMenuItemIndex;
40
+ exports.focusElement = focusElement;
41
+ exports.getEnabledMenuItems = getEnabledMenuItems;
42
+ exports.getMenuItemText = getMenuItemText;
43
+ exports.normalizeMenuText = normalizeMenuText;
44
+ exports.setRef = setRef;
45
+
46
+ //# sourceMappingURL=context-menu.utils.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-menu.utils.cjs","names":[],"sources":["../../../../src/components/overlays/context-menu/context-menu.utils.ts"],"sourcesContent":["import type { MutableRefObject, Ref } from \"react\";\n\nexport type MenuItemLike = {\n disabled?: boolean;\n text?: string;\n};\n\nconst normalizeMenuText = (value: string): string =>\n value\n .normalize(\"NFKD\")\n .replace(/\\p{Diacritic}/gu, \"\")\n .trim()\n .toLocaleLowerCase();\n\nconst findNextMenuItemIndex = <T extends MenuItemLike>(\n items: readonly T[],\n currentIndex: number,\n direction: -1 | 1,\n loop = true,\n): number => {\n if (items.length === 0) {\n return -1;\n }\n\n for (let step = 1; step <= items.length; step += 1) {\n let index = currentIndex + step * direction;\n\n if (loop) {\n index = (index + items.length) % items.length;\n } else if (index < 0 || index >= items.length) {\n return -1;\n }\n\n if (!items[index]!.disabled) {\n return index;\n }\n }\n\n return -1;\n};\n\nconst findMenuItemIndexByText = <T extends MenuItemLike>(\n items: readonly T[],\n query: string,\n currentIndex = -1,\n): number => {\n const normalizedQuery = normalizeMenuText(query);\n\n if (!normalizedQuery || items.length === 0) {\n return -1;\n }\n\n for (let step = 1; step <= items.length; step += 1) {\n const index = (currentIndex + step + items.length) % items.length;\n const item = items[index]!;\n\n if (\n !item.disabled &&\n normalizeMenuText(item.text ?? \"\").startsWith(normalizedQuery)\n ) {\n return index;\n }\n }\n\n return -1;\n};\n\nconst setRef = <T>(ref: Ref<T> | undefined, value: T | null): void => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref) {\n (ref as MutableRefObject<T | null>).current = value;\n }\n};\n\nconst getEnabledMenuItems = (menu: HTMLElement): HTMLElement[] =>\n Array.from(\n menu.querySelectorAll<HTMLElement>(\n '[data-puzzle-context-menu-item=\"true\"]',\n ),\n ).filter(\n (item) =>\n item.getAttribute(\"aria-disabled\") !== \"true\" &&\n !(\"disabled\" in item && Boolean((item as HTMLButtonElement).disabled)) &&\n !item.hasAttribute(\"hidden\") &&\n !item.closest(\"[hidden], [inert], [aria-hidden='true']\"),\n );\n\nconst focusElement = (element: HTMLElement | undefined): void => {\n if (!element) {\n return;\n }\n\n try {\n element.focus({ preventScroll: true });\n } catch {\n element.focus();\n }\n\n element.scrollIntoView?.({ block: \"nearest\" });\n};\n\nconst getMenuItemText = (item: HTMLElement): string =>\n item.dataset.textValue ??\n item.querySelector(\".puzzle-context-menu__item-label\")?.textContent ??\n item.textContent ??\n \"\";\n\nexport {\n findMenuItemIndexByText,\n findNextMenuItemIndex,\n focusElement,\n getEnabledMenuItems,\n getMenuItemText,\n normalizeMenuText,\n setRef,\n};\n"],"mappings":";AAOA,MAAM,qBAAqB,UACzB,MACG,UAAU,MAAM,CAAC,CACjB,QAAQ,mBAAmB,EAAE,CAAC,CAC9B,KAAK,CAAC,CACN,kBAAkB;AAEvB,MAAM,yBACJ,OACA,cACA,WACA,OAAO,SACI;CACX,IAAI,MAAM,WAAW,GACnB,OAAO;CAGT,KAAK,IAAI,OAAO,GAAG,QAAQ,MAAM,QAAQ,QAAQ,GAAG;EAClD,IAAI,QAAQ,eAAe,OAAO;EAElC,IAAI,MACF,SAAS,QAAQ,MAAM,UAAU,MAAM;OAClC,IAAI,QAAQ,KAAK,SAAS,MAAM,QACrC,OAAO;EAGT,IAAI,CAAC,MAAM,MAAM,CAAE,UACjB,OAAO;CAEX;CAEA,OAAO;AACT;AAEA,MAAM,2BACJ,OACA,OACA,eAAe,OACJ;CACX,MAAM,kBAAkB,kBAAkB,KAAK;CAE/C,IAAI,CAAC,mBAAmB,MAAM,WAAW,GACvC,OAAO;CAGT,KAAK,IAAI,OAAO,GAAG,QAAQ,MAAM,QAAQ,QAAQ,GAAG;EAClD,MAAM,SAAS,eAAe,OAAO,MAAM,UAAU,MAAM;EAC3D,MAAM,OAAO,MAAM;EAEnB,IACE,CAAC,KAAK,YACN,kBAAkB,KAAK,QAAQ,EAAE,CAAC,CAAC,WAAW,eAAe,GAE7D,OAAO;CAEX;CAEA,OAAO;AACT;AAEA,MAAM,UAAa,KAAyB,UAA0B;CACpE,IAAI,OAAO,QAAQ,YACjB,IAAI,KAAK;MACJ,IAAI,KACT,IAAoC,UAAU;AAElD;AAEA,MAAM,uBAAuB,SAC3B,MAAM,KACJ,KAAK,iBACH,0CACF,CACF,CAAC,CAAC,QACC,SACC,KAAK,aAAa,eAAe,MAAM,UACvC,EAAE,cAAc,QAAQ,QAAS,KAA2B,QAAQ,MACpE,CAAC,KAAK,aAAa,QAAQ,KAC3B,CAAC,KAAK,QAAQ,yCAAyC,CAC3D;AAEF,MAAM,gBAAgB,YAA2C;CAC/D,IAAI,CAAC,SACH;CAGF,IAAI;EACF,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;CACvC,QAAQ;EACN,QAAQ,MAAM;CAChB;CAEA,QAAQ,iBAAiB,EAAE,OAAO,UAAU,CAAC;AAC/C;AAEA,MAAM,mBAAmB,SACvB,KAAK,QAAQ,aACb,KAAK,cAAc,kCAAkC,CAAC,EAAE,eACxD,KAAK,eACL"}
@@ -0,0 +1,40 @@
1
+ // src/components/overlays/context-menu/context-menu.utils.ts
2
+ const normalizeMenuText = (value) => value.normalize("NFKD").replace(/\p{Diacritic}/gu, "").trim().toLocaleLowerCase();
3
+ const findNextMenuItemIndex = (items, currentIndex, direction, loop = true) => {
4
+ if (items.length === 0) return -1;
5
+ for (let step = 1; step <= items.length; step += 1) {
6
+ let index = currentIndex + step * direction;
7
+ if (loop) index = (index + items.length) % items.length;
8
+ else if (index < 0 || index >= items.length) return -1;
9
+ if (!items[index].disabled) return index;
10
+ }
11
+ return -1;
12
+ };
13
+ const findMenuItemIndexByText = (items, query, currentIndex = -1) => {
14
+ const normalizedQuery = normalizeMenuText(query);
15
+ if (!normalizedQuery || items.length === 0) return -1;
16
+ for (let step = 1; step <= items.length; step += 1) {
17
+ const index = (currentIndex + step + items.length) % items.length;
18
+ const item = items[index];
19
+ if (!item.disabled && normalizeMenuText(item.text ?? "").startsWith(normalizedQuery)) return index;
20
+ }
21
+ return -1;
22
+ };
23
+ const setRef = (ref, value) => {
24
+ if (typeof ref === "function") ref(value);
25
+ else if (ref) ref.current = value;
26
+ };
27
+ const getEnabledMenuItems = (menu) => Array.from(menu.querySelectorAll("[data-puzzle-context-menu-item=\"true\"]")).filter((item) => item.getAttribute("aria-disabled") !== "true" && !("disabled" in item && Boolean(item.disabled)) && !item.hasAttribute("hidden") && !item.closest("[hidden], [inert], [aria-hidden='true']"));
28
+ const focusElement = (element) => {
29
+ if (!element) return;
30
+ try {
31
+ element.focus({ preventScroll: true });
32
+ } catch {
33
+ element.focus();
34
+ }
35
+ element.scrollIntoView?.({ block: "nearest" });
36
+ };
37
+ const getMenuItemText = (item) => item.dataset.textValue ?? item.querySelector(".puzzle-context-menu__item-label")?.textContent ?? item.textContent ?? "";
38
+ export { findMenuItemIndexByText, findNextMenuItemIndex, focusElement, getEnabledMenuItems, getMenuItemText, normalizeMenuText, setRef };
39
+
40
+ //# sourceMappingURL=context-menu.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-menu.utils.js","names":[],"sources":["../../../../src/components/overlays/context-menu/context-menu.utils.ts"],"sourcesContent":["import type { MutableRefObject, Ref } from \"react\";\n\nexport type MenuItemLike = {\n disabled?: boolean;\n text?: string;\n};\n\nconst normalizeMenuText = (value: string): string =>\n value\n .normalize(\"NFKD\")\n .replace(/\\p{Diacritic}/gu, \"\")\n .trim()\n .toLocaleLowerCase();\n\nconst findNextMenuItemIndex = <T extends MenuItemLike>(\n items: readonly T[],\n currentIndex: number,\n direction: -1 | 1,\n loop = true,\n): number => {\n if (items.length === 0) {\n return -1;\n }\n\n for (let step = 1; step <= items.length; step += 1) {\n let index = currentIndex + step * direction;\n\n if (loop) {\n index = (index + items.length) % items.length;\n } else if (index < 0 || index >= items.length) {\n return -1;\n }\n\n if (!items[index]!.disabled) {\n return index;\n }\n }\n\n return -1;\n};\n\nconst findMenuItemIndexByText = <T extends MenuItemLike>(\n items: readonly T[],\n query: string,\n currentIndex = -1,\n): number => {\n const normalizedQuery = normalizeMenuText(query);\n\n if (!normalizedQuery || items.length === 0) {\n return -1;\n }\n\n for (let step = 1; step <= items.length; step += 1) {\n const index = (currentIndex + step + items.length) % items.length;\n const item = items[index]!;\n\n if (\n !item.disabled &&\n normalizeMenuText(item.text ?? \"\").startsWith(normalizedQuery)\n ) {\n return index;\n }\n }\n\n return -1;\n};\n\nconst setRef = <T>(ref: Ref<T> | undefined, value: T | null): void => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref) {\n (ref as MutableRefObject<T | null>).current = value;\n }\n};\n\nconst getEnabledMenuItems = (menu: HTMLElement): HTMLElement[] =>\n Array.from(\n menu.querySelectorAll<HTMLElement>(\n '[data-puzzle-context-menu-item=\"true\"]',\n ),\n ).filter(\n (item) =>\n item.getAttribute(\"aria-disabled\") !== \"true\" &&\n !(\"disabled\" in item && Boolean((item as HTMLButtonElement).disabled)) &&\n !item.hasAttribute(\"hidden\") &&\n !item.closest(\"[hidden], [inert], [aria-hidden='true']\"),\n );\n\nconst focusElement = (element: HTMLElement | undefined): void => {\n if (!element) {\n return;\n }\n\n try {\n element.focus({ preventScroll: true });\n } catch {\n element.focus();\n }\n\n element.scrollIntoView?.({ block: \"nearest\" });\n};\n\nconst getMenuItemText = (item: HTMLElement): string =>\n item.dataset.textValue ??\n item.querySelector(\".puzzle-context-menu__item-label\")?.textContent ??\n item.textContent ??\n \"\";\n\nexport {\n findMenuItemIndexByText,\n findNextMenuItemIndex,\n focusElement,\n getEnabledMenuItems,\n getMenuItemText,\n normalizeMenuText,\n setRef,\n};\n"],"mappings":";AAOA,MAAM,qBAAqB,UACzB,MACG,UAAU,MAAM,CAAC,CACjB,QAAQ,mBAAmB,EAAE,CAAC,CAC9B,KAAK,CAAC,CACN,kBAAkB;AAEvB,MAAM,yBACJ,OACA,cACA,WACA,OAAO,SACI;CACX,IAAI,MAAM,WAAW,GACnB,OAAO;CAGT,KAAK,IAAI,OAAO,GAAG,QAAQ,MAAM,QAAQ,QAAQ,GAAG;EAClD,IAAI,QAAQ,eAAe,OAAO;EAElC,IAAI,MACF,SAAS,QAAQ,MAAM,UAAU,MAAM;OAClC,IAAI,QAAQ,KAAK,SAAS,MAAM,QACrC,OAAO;EAGT,IAAI,CAAC,MAAM,MAAM,CAAE,UACjB,OAAO;CAEX;CAEA,OAAO;AACT;AAEA,MAAM,2BACJ,OACA,OACA,eAAe,OACJ;CACX,MAAM,kBAAkB,kBAAkB,KAAK;CAE/C,IAAI,CAAC,mBAAmB,MAAM,WAAW,GACvC,OAAO;CAGT,KAAK,IAAI,OAAO,GAAG,QAAQ,MAAM,QAAQ,QAAQ,GAAG;EAClD,MAAM,SAAS,eAAe,OAAO,MAAM,UAAU,MAAM;EAC3D,MAAM,OAAO,MAAM;EAEnB,IACE,CAAC,KAAK,YACN,kBAAkB,KAAK,QAAQ,EAAE,CAAC,CAAC,WAAW,eAAe,GAE7D,OAAO;CAEX;CAEA,OAAO;AACT;AAEA,MAAM,UAAa,KAAyB,UAA0B;CACpE,IAAI,OAAO,QAAQ,YACjB,IAAI,KAAK;MACJ,IAAI,KACT,IAAoC,UAAU;AAElD;AAEA,MAAM,uBAAuB,SAC3B,MAAM,KACJ,KAAK,iBACH,0CACF,CACF,CAAC,CAAC,QACC,SACC,KAAK,aAAa,eAAe,MAAM,UACvC,EAAE,cAAc,QAAQ,QAAS,KAA2B,QAAQ,MACpE,CAAC,KAAK,aAAa,QAAQ,KAC3B,CAAC,KAAK,QAAQ,yCAAyC,CAC3D;AAEF,MAAM,gBAAgB,YAA2C;CAC/D,IAAI,CAAC,SACH;CAGF,IAAI;EACF,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;CACvC,QAAQ;EACN,QAAQ,MAAM;CAChB;CAEA,QAAQ,iBAAiB,EAAE,OAAO,UAAU,CAAC;AAC/C;AAEA,MAAM,mBAAmB,SACvB,KAAK,QAAQ,aACb,KAAK,cAAc,kCAAkC,CAAC,EAAE,eACxD,KAAK,eACL"}