@salt-ds/lab 1.0.0-alpha.95 → 1.0.0-alpha.97

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 (220) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/css/salt-lab.css +70 -249
  3. package/dist-cjs/common-hooks/useSelection.js +6 -3
  4. package/dist-cjs/common-hooks/useSelection.js.map +1 -1
  5. package/dist-cjs/index.js +12 -14
  6. package/dist-cjs/index.js.map +1 -1
  7. package/dist-cjs/list/useList.js +7 -1
  8. package/dist-cjs/list/useList.js.map +1 -1
  9. package/dist-cjs/mega-menu/MegaMenu.js +8 -14
  10. package/dist-cjs/mega-menu/MegaMenu.js.map +1 -1
  11. package/dist-cjs/mega-menu/MegaMenuActions.css.js +6 -0
  12. package/dist-cjs/mega-menu/MegaMenuActions.css.js.map +1 -0
  13. package/dist-cjs/mega-menu/MegaMenuActions.js +33 -0
  14. package/dist-cjs/mega-menu/MegaMenuActions.js.map +1 -0
  15. package/dist-cjs/mega-menu/MegaMenuAside.css.js +6 -0
  16. package/dist-cjs/mega-menu/MegaMenuAside.css.js.map +1 -0
  17. package/dist-cjs/mega-menu/MegaMenuAside.js +33 -0
  18. package/dist-cjs/mega-menu/MegaMenuAside.js.map +1 -0
  19. package/dist-cjs/mega-menu/MegaMenuContent.css.js +6 -0
  20. package/dist-cjs/mega-menu/MegaMenuContent.css.js.map +1 -0
  21. package/dist-cjs/mega-menu/MegaMenuContent.js +25 -0
  22. package/dist-cjs/mega-menu/MegaMenuContent.js.map +1 -0
  23. package/dist-cjs/mega-menu/MegaMenuContext.js.map +1 -1
  24. package/dist-cjs/mega-menu/MegaMenuGroup.css.js +1 -1
  25. package/dist-cjs/mega-menu/MegaMenuGroup.js +10 -25
  26. package/dist-cjs/mega-menu/MegaMenuGroup.js.map +1 -1
  27. package/dist-cjs/mega-menu/MegaMenuGroupContext.js +17 -0
  28. package/dist-cjs/mega-menu/MegaMenuGroupContext.js.map +1 -0
  29. package/dist-cjs/mega-menu/MegaMenuGroupHeading.css.js +6 -0
  30. package/dist-cjs/mega-menu/MegaMenuGroupHeading.css.js.map +1 -0
  31. package/dist-cjs/mega-menu/MegaMenuGroupHeading.js +39 -0
  32. package/dist-cjs/mega-menu/MegaMenuGroupHeading.js.map +1 -0
  33. package/dist-cjs/mega-menu/MegaMenuGroups.css.js +1 -1
  34. package/dist-cjs/mega-menu/MegaMenuGroups.js.map +1 -1
  35. package/dist-cjs/mega-menu/MegaMenuList.css.js +6 -0
  36. package/dist-cjs/mega-menu/MegaMenuList.css.js.map +1 -0
  37. package/dist-cjs/mega-menu/MegaMenuList.js +43 -0
  38. package/dist-cjs/mega-menu/MegaMenuList.js.map +1 -0
  39. package/dist-cjs/mega-menu/MegaMenuListItem.css.js +6 -0
  40. package/dist-cjs/mega-menu/MegaMenuListItem.css.js.map +1 -0
  41. package/dist-cjs/mega-menu/MegaMenuListItem.js +45 -0
  42. package/dist-cjs/mega-menu/MegaMenuListItem.js.map +1 -0
  43. package/dist-cjs/mega-menu/MegaMenuPanel.css.js +1 -1
  44. package/dist-cjs/mega-menu/MegaMenuPanel.js +27 -47
  45. package/dist-cjs/mega-menu/MegaMenuPanel.js.map +1 -1
  46. package/dist-cjs/mega-menu/MegaMenuTrigger.js +16 -18
  47. package/dist-cjs/mega-menu/MegaMenuTrigger.js.map +1 -1
  48. package/dist-cjs/mega-menu/useMegaMenuNavigation.js +282 -0
  49. package/dist-cjs/mega-menu/useMegaMenuNavigation.js.map +1 -0
  50. package/dist-cjs/mega-menu/usePageMargin.js +22 -0
  51. package/dist-cjs/mega-menu/usePageMargin.js.map +1 -0
  52. package/dist-es/common-hooks/useSelection.js +6 -3
  53. package/dist-es/common-hooks/useSelection.js.map +1 -1
  54. package/dist-es/index.js +6 -7
  55. package/dist-es/index.js.map +1 -1
  56. package/dist-es/list/useList.js +7 -1
  57. package/dist-es/list/useList.js.map +1 -1
  58. package/dist-es/mega-menu/MegaMenu.js +9 -15
  59. package/dist-es/mega-menu/MegaMenu.js.map +1 -1
  60. package/dist-es/mega-menu/MegaMenuActions.css.js +4 -0
  61. package/dist-es/mega-menu/MegaMenuActions.css.js.map +1 -0
  62. package/dist-es/mega-menu/MegaMenuActions.js +31 -0
  63. package/dist-es/mega-menu/MegaMenuActions.js.map +1 -0
  64. package/dist-es/mega-menu/MegaMenuAside.css.js +4 -0
  65. package/dist-es/mega-menu/MegaMenuAside.css.js.map +1 -0
  66. package/dist-es/mega-menu/MegaMenuAside.js +31 -0
  67. package/dist-es/mega-menu/MegaMenuAside.js.map +1 -0
  68. package/dist-es/mega-menu/MegaMenuContent.css.js +4 -0
  69. package/dist-es/mega-menu/MegaMenuContent.css.js.map +1 -0
  70. package/dist-es/mega-menu/{MegaMenuHeader.js → MegaMenuContent.js} +8 -8
  71. package/dist-es/mega-menu/MegaMenuContent.js.map +1 -0
  72. package/dist-es/mega-menu/MegaMenuContext.js.map +1 -1
  73. package/dist-es/mega-menu/MegaMenuGroup.css.js +1 -1
  74. package/dist-es/mega-menu/MegaMenuGroup.js +13 -28
  75. package/dist-es/mega-menu/MegaMenuGroup.js.map +1 -1
  76. package/dist-es/mega-menu/MegaMenuGroupContext.js +14 -0
  77. package/dist-es/mega-menu/MegaMenuGroupContext.js.map +1 -0
  78. package/dist-es/mega-menu/MegaMenuGroupHeading.css.js +4 -0
  79. package/dist-es/mega-menu/MegaMenuGroupHeading.css.js.map +1 -0
  80. package/dist-es/mega-menu/MegaMenuGroupHeading.js +37 -0
  81. package/dist-es/mega-menu/MegaMenuGroupHeading.js.map +1 -0
  82. package/dist-es/mega-menu/MegaMenuGroups.css.js +1 -1
  83. package/dist-es/mega-menu/MegaMenuGroups.js.map +1 -1
  84. package/dist-es/mega-menu/MegaMenuList.css.js +4 -0
  85. package/dist-es/mega-menu/MegaMenuList.css.js.map +1 -0
  86. package/dist-es/mega-menu/MegaMenuList.js +41 -0
  87. package/dist-es/mega-menu/MegaMenuList.js.map +1 -0
  88. package/dist-es/mega-menu/MegaMenuListItem.css.js +4 -0
  89. package/dist-es/mega-menu/MegaMenuListItem.css.js.map +1 -0
  90. package/dist-es/mega-menu/MegaMenuListItem.js +43 -0
  91. package/dist-es/mega-menu/MegaMenuListItem.js.map +1 -0
  92. package/dist-es/mega-menu/MegaMenuPanel.css.js +1 -1
  93. package/dist-es/mega-menu/MegaMenuPanel.js +30 -50
  94. package/dist-es/mega-menu/MegaMenuPanel.js.map +1 -1
  95. package/dist-es/mega-menu/MegaMenuTrigger.js +14 -16
  96. package/dist-es/mega-menu/MegaMenuTrigger.js.map +1 -1
  97. package/dist-es/mega-menu/useMegaMenuNavigation.js +278 -0
  98. package/dist-es/mega-menu/useMegaMenuNavigation.js.map +1 -0
  99. package/dist-es/mega-menu/usePageMargin.js +20 -0
  100. package/dist-es/mega-menu/usePageMargin.js.map +1 -0
  101. package/dist-types/index.d.ts +0 -1
  102. package/dist-types/mega-menu/MegaMenu.d.ts +11 -2
  103. package/dist-types/mega-menu/MegaMenuActions.d.ts +10 -0
  104. package/dist-types/mega-menu/MegaMenuAside.d.ts +9 -0
  105. package/dist-types/mega-menu/MegaMenuContent.d.ts +9 -0
  106. package/dist-types/mega-menu/MegaMenuContext.d.ts +6 -7
  107. package/dist-types/mega-menu/MegaMenuGroup.d.ts +4 -3
  108. package/dist-types/mega-menu/MegaMenuGroupContext.d.ts +12 -0
  109. package/dist-types/mega-menu/MegaMenuGroupHeading.d.ts +13 -0
  110. package/dist-types/mega-menu/MegaMenuGroups.d.ts +3 -3
  111. package/dist-types/mega-menu/MegaMenuList.d.ts +16 -0
  112. package/dist-types/mega-menu/MegaMenuListItem.d.ts +25 -0
  113. package/dist-types/mega-menu/MegaMenuPanel.d.ts +2 -2
  114. package/dist-types/mega-menu/MegaMenuTrigger.d.ts +1 -0
  115. package/dist-types/mega-menu/index.d.ts +6 -4
  116. package/dist-types/mega-menu/useMegaMenuNavigation.d.ts +16 -0
  117. package/dist-types/mega-menu/usePageMargin.d.ts +6 -0
  118. package/package.json +2 -2
  119. package/dist-cjs/mega-menu/MegaMenuHeader.css.js +0 -6
  120. package/dist-cjs/mega-menu/MegaMenuHeader.css.js.map +0 -1
  121. package/dist-cjs/mega-menu/MegaMenuHeader.js +0 -25
  122. package/dist-cjs/mega-menu/MegaMenuHeader.js.map +0 -1
  123. package/dist-cjs/mega-menu/MegaMenuItem.css.js +0 -6
  124. package/dist-cjs/mega-menu/MegaMenuItem.css.js.map +0 -1
  125. package/dist-cjs/mega-menu/MegaMenuItem.js +0 -53
  126. package/dist-cjs/mega-menu/MegaMenuItem.js.map +0 -1
  127. package/dist-cjs/mega-menu/MegaMenuSupportingActions.css.js +0 -6
  128. package/dist-cjs/mega-menu/MegaMenuSupportingActions.css.js.map +0 -1
  129. package/dist-cjs/mega-menu/MegaMenuSupportingActions.js +0 -31
  130. package/dist-cjs/mega-menu/MegaMenuSupportingActions.js.map +0 -1
  131. package/dist-cjs/mega-menu/MegaMenuSupportingContent.css.js +0 -6
  132. package/dist-cjs/mega-menu/MegaMenuSupportingContent.css.js.map +0 -1
  133. package/dist-cjs/mega-menu/MegaMenuSupportingContent.js +0 -31
  134. package/dist-cjs/mega-menu/MegaMenuSupportingContent.js.map +0 -1
  135. package/dist-cjs/mega-menu/useMegaMenuKeyboard.js +0 -209
  136. package/dist-cjs/mega-menu/useMegaMenuKeyboard.js.map +0 -1
  137. package/dist-cjs/toolbar-next/ToolbarContentNext.css.js +0 -6
  138. package/dist-cjs/toolbar-next/ToolbarContentNext.css.js.map +0 -1
  139. package/dist-cjs/toolbar-next/ToolbarContentNext.js +0 -32
  140. package/dist-cjs/toolbar-next/ToolbarContentNext.js.map +0 -1
  141. package/dist-cjs/toolbar-next/ToolbarNext.css.js +0 -6
  142. package/dist-cjs/toolbar-next/ToolbarNext.css.js.map +0 -1
  143. package/dist-cjs/toolbar-next/ToolbarNext.js +0 -394
  144. package/dist-cjs/toolbar-next/ToolbarNext.js.map +0 -1
  145. package/dist-cjs/toolbar-next/ToolbarNextOverflow.css.js +0 -6
  146. package/dist-cjs/toolbar-next/ToolbarNextOverflow.css.js.map +0 -1
  147. package/dist-cjs/toolbar-next/ToolbarNextOverflow.js +0 -706
  148. package/dist-cjs/toolbar-next/ToolbarNextOverflow.js.map +0 -1
  149. package/dist-cjs/toolbar-next/ToolbarNextOverflowFloatingBoundary.js +0 -165
  150. package/dist-cjs/toolbar-next/ToolbarNextOverflowFloatingBoundary.js.map +0 -1
  151. package/dist-cjs/toolbar-next/TooltrayNext.css.js +0 -6
  152. package/dist-cjs/toolbar-next/TooltrayNext.css.js.map +0 -1
  153. package/dist-cjs/toolbar-next/TooltrayNext.js +0 -55
  154. package/dist-cjs/toolbar-next/TooltrayNext.js.map +0 -1
  155. package/dist-cjs/toolbar-next/toolbarNextKeyboardUtils.js +0 -409
  156. package/dist-cjs/toolbar-next/toolbarNextKeyboardUtils.js.map +0 -1
  157. package/dist-cjs/toolbar-next/toolbarNextUtils.js +0 -215
  158. package/dist-cjs/toolbar-next/toolbarNextUtils.js.map +0 -1
  159. package/dist-cjs/toolbar-next/useToolbarNextKeyboardNavigation.js +0 -312
  160. package/dist-cjs/toolbar-next/useToolbarNextKeyboardNavigation.js.map +0 -1
  161. package/dist-cjs/toolbar-next/useToolbarNextOverflow.js +0 -743
  162. package/dist-cjs/toolbar-next/useToolbarNextOverflow.js.map +0 -1
  163. package/dist-es/mega-menu/MegaMenuHeader.css.js +0 -4
  164. package/dist-es/mega-menu/MegaMenuHeader.css.js.map +0 -1
  165. package/dist-es/mega-menu/MegaMenuHeader.js.map +0 -1
  166. package/dist-es/mega-menu/MegaMenuItem.css.js +0 -4
  167. package/dist-es/mega-menu/MegaMenuItem.css.js.map +0 -1
  168. package/dist-es/mega-menu/MegaMenuItem.js +0 -51
  169. package/dist-es/mega-menu/MegaMenuItem.js.map +0 -1
  170. package/dist-es/mega-menu/MegaMenuSupportingActions.css.js +0 -4
  171. package/dist-es/mega-menu/MegaMenuSupportingActions.css.js.map +0 -1
  172. package/dist-es/mega-menu/MegaMenuSupportingActions.js +0 -29
  173. package/dist-es/mega-menu/MegaMenuSupportingActions.js.map +0 -1
  174. package/dist-es/mega-menu/MegaMenuSupportingContent.css.js +0 -4
  175. package/dist-es/mega-menu/MegaMenuSupportingContent.css.js.map +0 -1
  176. package/dist-es/mega-menu/MegaMenuSupportingContent.js +0 -29
  177. package/dist-es/mega-menu/MegaMenuSupportingContent.js.map +0 -1
  178. package/dist-es/mega-menu/useMegaMenuKeyboard.js +0 -205
  179. package/dist-es/mega-menu/useMegaMenuKeyboard.js.map +0 -1
  180. package/dist-es/toolbar-next/ToolbarContentNext.css.js +0 -4
  181. package/dist-es/toolbar-next/ToolbarContentNext.css.js.map +0 -1
  182. package/dist-es/toolbar-next/ToolbarContentNext.js +0 -30
  183. package/dist-es/toolbar-next/ToolbarContentNext.js.map +0 -1
  184. package/dist-es/toolbar-next/ToolbarNext.css.js +0 -4
  185. package/dist-es/toolbar-next/ToolbarNext.css.js.map +0 -1
  186. package/dist-es/toolbar-next/ToolbarNext.js +0 -392
  187. package/dist-es/toolbar-next/ToolbarNext.js.map +0 -1
  188. package/dist-es/toolbar-next/ToolbarNextOverflow.css.js +0 -4
  189. package/dist-es/toolbar-next/ToolbarNextOverflow.css.js.map +0 -1
  190. package/dist-es/toolbar-next/ToolbarNextOverflow.js +0 -701
  191. package/dist-es/toolbar-next/ToolbarNextOverflow.js.map +0 -1
  192. package/dist-es/toolbar-next/ToolbarNextOverflowFloatingBoundary.js +0 -159
  193. package/dist-es/toolbar-next/ToolbarNextOverflowFloatingBoundary.js.map +0 -1
  194. package/dist-es/toolbar-next/TooltrayNext.css.js +0 -4
  195. package/dist-es/toolbar-next/TooltrayNext.css.js.map +0 -1
  196. package/dist-es/toolbar-next/TooltrayNext.js +0 -53
  197. package/dist-es/toolbar-next/TooltrayNext.js.map +0 -1
  198. package/dist-es/toolbar-next/toolbarNextKeyboardUtils.js +0 -390
  199. package/dist-es/toolbar-next/toolbarNextKeyboardUtils.js.map +0 -1
  200. package/dist-es/toolbar-next/toolbarNextUtils.js +0 -211
  201. package/dist-es/toolbar-next/toolbarNextUtils.js.map +0 -1
  202. package/dist-es/toolbar-next/useToolbarNextKeyboardNavigation.js +0 -310
  203. package/dist-es/toolbar-next/useToolbarNextKeyboardNavigation.js.map +0 -1
  204. package/dist-es/toolbar-next/useToolbarNextOverflow.js +0 -741
  205. package/dist-es/toolbar-next/useToolbarNextOverflow.js.map +0 -1
  206. package/dist-types/mega-menu/MegaMenuHeader.d.ts +0 -8
  207. package/dist-types/mega-menu/MegaMenuItem.d.ts +0 -13
  208. package/dist-types/mega-menu/MegaMenuSupportingActions.d.ts +0 -8
  209. package/dist-types/mega-menu/MegaMenuSupportingContent.d.ts +0 -8
  210. package/dist-types/mega-menu/useMegaMenuKeyboard.d.ts +0 -30
  211. package/dist-types/toolbar-next/ToolbarContentNext.d.ts +0 -11
  212. package/dist-types/toolbar-next/ToolbarNext.d.ts +0 -12
  213. package/dist-types/toolbar-next/ToolbarNextOverflow.d.ts +0 -34
  214. package/dist-types/toolbar-next/ToolbarNextOverflowFloatingBoundary.d.ts +0 -16
  215. package/dist-types/toolbar-next/TooltrayNext.d.ts +0 -37
  216. package/dist-types/toolbar-next/index.d.ts +0 -3
  217. package/dist-types/toolbar-next/toolbarNextKeyboardUtils.d.ts +0 -39
  218. package/dist-types/toolbar-next/toolbarNextUtils.d.ts +0 -42
  219. package/dist-types/toolbar-next/useToolbarNextKeyboardNavigation.d.ts +0 -42
  220. package/dist-types/toolbar-next/useToolbarNextOverflow.d.ts +0 -37
@@ -0,0 +1,278 @@
1
+ import { useMemo } from 'react';
2
+
3
+ const COLUMN_SELECTOR = "[data-mega-menu-column]";
4
+ const ACTIONS_SELECTOR = "[data-mega-menu-actions]";
5
+ const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])';
6
+ const SELF_CONSUMING_SELECTOR = 'input, textarea, select, [contenteditable], [role="combobox"], [role="listbox"], [role="slider"], [role="spinbutton"], [role="textbox"]';
7
+ const HIDDEN_ANCESTOR_SELECTOR = '[aria-hidden="true"], [inert]';
8
+ function queryFocusables(root) {
9
+ return Array.from(
10
+ root.querySelectorAll(FOCUSABLE_SELECTOR)
11
+ ).filter((el) => !el.closest(HIDDEN_ANCESTOR_SELECTOR));
12
+ }
13
+ function firstFocusable(root) {
14
+ return root ? queryFocusables(root)[0] ?? null : null;
15
+ }
16
+ function buildModel(panel) {
17
+ const columns = Array.from(
18
+ panel.querySelectorAll(COLUMN_SELECTOR)
19
+ ).filter((el) => queryFocusables(el).length > 0);
20
+ const actions = Array.from(
21
+ panel.querySelectorAll(ACTIONS_SELECTOR)
22
+ ).filter((el) => queryFocusables(el).length > 0);
23
+ return { columns, actions };
24
+ }
25
+ function focusTrigger(context) {
26
+ var _a;
27
+ const reference = context.elements.reference;
28
+ (_a = firstFocusable(reference) ?? reference) == null ? void 0 : _a.focus();
29
+ }
30
+ function focusNextTriggerAndClose(context) {
31
+ const reference = context.elements.reference;
32
+ const trigger = firstFocusable(reference) ?? reference;
33
+ const li = trigger == null ? void 0 : trigger.closest("li");
34
+ const next = (li == null ? void 0 : li.nextElementSibling) instanceof HTMLElement ? firstFocusable(li.nextElementSibling) : null;
35
+ if (next) {
36
+ context.onOpenChange(false);
37
+ next.focus();
38
+ return true;
39
+ }
40
+ return false;
41
+ }
42
+ function columnsStacked(columns) {
43
+ if (columns.length < 2) return false;
44
+ const first = columns[0].getBoundingClientRect();
45
+ const second = columns[1].getBoundingClientRect();
46
+ return second.top >= first.bottom - 1;
47
+ }
48
+ function handleLinear(key, cell, panel, context) {
49
+ var _a, _b;
50
+ const cells = queryFocusables(panel);
51
+ const index = cells.indexOf(cell);
52
+ if (index === -1) return false;
53
+ switch (key) {
54
+ case "ArrowDown":
55
+ case "ArrowRight": {
56
+ if (index < cells.length - 1) {
57
+ cells[index + 1].focus();
58
+ }
59
+ return true;
60
+ }
61
+ case "ArrowUp":
62
+ case "ArrowLeft": {
63
+ if (index > 0) {
64
+ cells[index - 1].focus();
65
+ } else {
66
+ focusTrigger(context);
67
+ }
68
+ return true;
69
+ }
70
+ case "Home": {
71
+ (_a = cells[0]) == null ? void 0 : _a.focus();
72
+ return true;
73
+ }
74
+ case "End": {
75
+ (_b = cells[cells.length - 1]) == null ? void 0 : _b.focus();
76
+ return true;
77
+ }
78
+ default:
79
+ return false;
80
+ }
81
+ }
82
+ function handleArrow(key, cell, panel, context) {
83
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
84
+ const { columns, actions } = buildModel(panel);
85
+ if (columnsStacked(columns)) {
86
+ return handleLinear(key, cell, panel, context);
87
+ }
88
+ const column = cell.closest(COLUMN_SELECTOR);
89
+ const actionsEl = cell.closest(ACTIONS_SELECTOR);
90
+ if (column) {
91
+ const colIndex = columns.indexOf(column);
92
+ const cells = queryFocusables(column);
93
+ const rowIndex = cells.indexOf(cell);
94
+ switch (key) {
95
+ case "ArrowDown": {
96
+ if (rowIndex < cells.length - 1) {
97
+ cells[rowIndex + 1].focus();
98
+ } else if (colIndex < columns.length - 1) {
99
+ (_a = firstFocusable(columns[colIndex + 1])) == null ? void 0 : _a.focus();
100
+ } else if (actions.length > 0) {
101
+ (_b = firstFocusable(actions[0])) == null ? void 0 : _b.focus();
102
+ } else {
103
+ focusNextTriggerAndClose(context);
104
+ }
105
+ return true;
106
+ }
107
+ case "ArrowUp": {
108
+ if (rowIndex > 0) {
109
+ cells[rowIndex - 1].focus();
110
+ } else {
111
+ focusTrigger(context);
112
+ }
113
+ return true;
114
+ }
115
+ case "ArrowRight": {
116
+ if (colIndex < columns.length - 1) {
117
+ (_c = firstFocusable(columns[colIndex + 1])) == null ? void 0 : _c.focus();
118
+ return true;
119
+ }
120
+ if (rowIndex === cells.length - 1 && focusNextTriggerAndClose(context)) {
121
+ return true;
122
+ }
123
+ focusTrigger(context);
124
+ return true;
125
+ }
126
+ case "ArrowLeft": {
127
+ if (colIndex > 0) {
128
+ (_d = firstFocusable(columns[colIndex - 1])) == null ? void 0 : _d.focus();
129
+ } else {
130
+ focusTrigger(context);
131
+ }
132
+ return true;
133
+ }
134
+ case "Home": {
135
+ (_e = cells[0]) == null ? void 0 : _e.focus();
136
+ return true;
137
+ }
138
+ case "End": {
139
+ (_f = cells[cells.length - 1]) == null ? void 0 : _f.focus();
140
+ return true;
141
+ }
142
+ default:
143
+ return false;
144
+ }
145
+ }
146
+ if (actionsEl) {
147
+ const cells = queryFocusables(actionsEl);
148
+ const index = cells.indexOf(cell);
149
+ switch (key) {
150
+ case "ArrowRight": {
151
+ if (index < cells.length - 1) {
152
+ cells[index + 1].focus();
153
+ } else if (!focusNextTriggerAndClose(context)) {
154
+ focusTrigger(context);
155
+ }
156
+ return true;
157
+ }
158
+ case "ArrowLeft": {
159
+ if (index > 0) {
160
+ cells[index - 1].focus();
161
+ } else if (cell === firstFocusable(panel)) {
162
+ focusTrigger(context);
163
+ }
164
+ return true;
165
+ }
166
+ case "ArrowUp": {
167
+ if (columns.length > 0) {
168
+ const lastColumnCells = queryFocusables(columns[columns.length - 1]);
169
+ (_g = lastColumnCells[lastColumnCells.length - 1]) == null ? void 0 : _g.focus();
170
+ } else {
171
+ focusTrigger(context);
172
+ }
173
+ return true;
174
+ }
175
+ case "ArrowDown": {
176
+ if (index === cells.length - 1) {
177
+ focusNextTriggerAndClose(context);
178
+ }
179
+ return true;
180
+ }
181
+ case "Home": {
182
+ (_h = cells[0]) == null ? void 0 : _h.focus();
183
+ return true;
184
+ }
185
+ case "End": {
186
+ (_i = cells[cells.length - 1]) == null ? void 0 : _i.focus();
187
+ return true;
188
+ }
189
+ default:
190
+ return false;
191
+ }
192
+ }
193
+ return false;
194
+ }
195
+ function focusNextAfterPanel(context, panel) {
196
+ var _a;
197
+ const reference = context.elements.reference;
198
+ const refFocusable = firstFocusable(reference) ?? reference;
199
+ const nextLi = (_a = refFocusable == null ? void 0 : refFocusable.closest("li")) == null ? void 0 : _a.nextElementSibling;
200
+ const nextSibling = nextLi instanceof HTMLElement ? firstFocusable(nextLi) : null;
201
+ const nextOutside = nextSibling || (() => {
202
+ const allFocusable = queryFocusables(
203
+ panel.ownerDocument.documentElement
204
+ ).filter((el) => !panel.contains(el));
205
+ const idx = refFocusable ? allFocusable.indexOf(refFocusable) : -1;
206
+ return idx >= 0 ? allFocusable[idx + 1] : void 0;
207
+ })();
208
+ if (nextOutside) {
209
+ const view = panel.ownerDocument.defaultView;
210
+ view == null ? void 0 : view.requestAnimationFrame(() => {
211
+ view == null ? void 0 : view.requestAnimationFrame(() => {
212
+ nextOutside.focus();
213
+ });
214
+ });
215
+ }
216
+ }
217
+ function useMegaMenuNavigation(context, props = {}) {
218
+ const { enabled = true } = props;
219
+ const { open, onOpenChange } = context;
220
+ return useMemo(() => {
221
+ if (!enabled) {
222
+ return {};
223
+ }
224
+ return {
225
+ floating: {
226
+ onKeyDown(event) {
227
+ var _a, _b;
228
+ if (!open) return;
229
+ const panel = event.currentTarget;
230
+ const target = event.target;
231
+ const { key } = event;
232
+ const isArrowOrEdge = key === "ArrowUp" || key === "ArrowDown" || key === "ArrowLeft" || key === "ArrowRight" || key === "Home" || key === "End";
233
+ if (isArrowOrEdge && target.closest(SELF_CONSUMING_SELECTOR)) {
234
+ return;
235
+ }
236
+ const cell = target.closest(FOCUSABLE_SELECTOR);
237
+ if (key === "Tab") {
238
+ const allCells = queryFocusables(panel);
239
+ const linearIndex = cell ? allCells.indexOf(cell) : -1;
240
+ if (linearIndex === -1) return;
241
+ event.preventDefault();
242
+ if (event.shiftKey) {
243
+ if (linearIndex === 0) {
244
+ focusTrigger(context);
245
+ } else {
246
+ (_a = allCells[linearIndex - 1]) == null ? void 0 : _a.focus();
247
+ }
248
+ } else if (linearIndex === allCells.length - 1) {
249
+ onOpenChange(false);
250
+ focusNextAfterPanel(context, panel);
251
+ } else {
252
+ (_b = allCells[linearIndex + 1]) == null ? void 0 : _b.focus();
253
+ }
254
+ return;
255
+ }
256
+ if (!cell || !isArrowOrEdge) return;
257
+ if (handleArrow(key, cell, panel, context)) {
258
+ event.preventDefault();
259
+ }
260
+ }
261
+ }
262
+ };
263
+ }, [enabled, open, context, onOpenChange]);
264
+ }
265
+ function focusFirstItem(panel, attempt = 0) {
266
+ const firstItem = firstFocusable(panel);
267
+ if (firstItem) {
268
+ firstItem.focus();
269
+ return;
270
+ }
271
+ const view = panel.ownerDocument.defaultView;
272
+ if (attempt < 3 && view) {
273
+ view.requestAnimationFrame(() => focusFirstItem(panel, attempt + 1));
274
+ }
275
+ }
276
+
277
+ export { FOCUSABLE_SELECTOR, focusFirstItem, useMegaMenuNavigation };
278
+ //# sourceMappingURL=useMegaMenuNavigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMegaMenuNavigation.js","sources":["../src/mega-menu/useMegaMenuNavigation.ts"],"sourcesContent":["import type { ElementProps, FloatingRootContext } from \"@floating-ui/react\";\nimport { useMemo } from \"react\";\n\nconst COLUMN_SELECTOR = \"[data-mega-menu-column]\";\nconst ACTIONS_SELECTOR = \"[data-mega-menu-actions]\";\n\nexport const FOCUSABLE_SELECTOR =\n 'a[href], button:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n// Controls that own the arrow keys (caret, option lists, steppers); the engine\n// leaves their keys alone when focus is inside one.\nconst SELF_CONSUMING_SELECTOR =\n 'input, textarea, select, [contenteditable], [role=\"combobox\"], [role=\"listbox\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"textbox\"]';\n\n// aria-hidden / inert subtrees are out of the focus order; filtered by ancestor\n// lookup since `:not()` cannot match on an ancestor.\nconst HIDDEN_ANCESTOR_SELECTOR = '[aria-hidden=\"true\"], [inert]';\n\nfunction queryFocusables(root: HTMLElement): HTMLElement[] {\n return Array.from(\n root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR),\n ).filter((el) => !el.closest(HIDDEN_ANCESTOR_SELECTOR));\n}\n\nfunction firstFocusable(root: HTMLElement | null): HTMLElement | null {\n return root ? (queryFocusables(root)[0] ?? null) : null;\n}\n\ninterface NavModel {\n /** Columns (`data-mega-menu-column`) in DOM order, each with ≥1 cell. */\n columns: HTMLElement[];\n /** Full-width actions row; the bottom of the center area. */\n actions: HTMLElement[];\n}\n\n/** Build the navigation model from the panel DOM at keypress time. */\nfunction buildModel(panel: HTMLElement): NavModel {\n const columns = Array.from(\n panel.querySelectorAll<HTMLElement>(COLUMN_SELECTOR),\n ).filter((el) => queryFocusables(el).length > 0);\n\n const actions = Array.from(\n panel.querySelectorAll<HTMLElement>(ACTIONS_SELECTOR),\n ).filter((el) => queryFocusables(el).length > 0);\n\n return { columns, actions };\n}\n\nfunction focusTrigger(context: FloatingRootContext) {\n const reference = context.elements.reference as HTMLElement | null;\n (firstFocusable(reference) ?? reference)?.focus();\n}\n\n/** Exit to the next nav-bar trigger and close. Returns false when there is none. */\nfunction focusNextTriggerAndClose(context: FloatingRootContext): boolean {\n const reference = context.elements.reference as HTMLElement | null;\n const trigger = firstFocusable(reference) ?? reference;\n const li = trigger?.closest(\"li\");\n const next =\n li?.nextElementSibling instanceof HTMLElement\n ? firstFocusable(li.nextElementSibling)\n : null;\n if (next) {\n context.onOpenChange(false);\n next.focus();\n return true;\n }\n return false;\n}\n\n/** True when the grid has collapsed to one stacked column (small viewport). */\nfunction columnsStacked(columns: HTMLElement[]): boolean {\n if (columns.length < 2) return false;\n const first = columns[0].getBoundingClientRect();\n const second = columns[1].getBoundingClientRect();\n return second.top >= first.bottom - 1;\n}\n\n/** Stacked layout: all arrows degrade to prev/next over every cell. */\nfunction handleLinear(\n key: string,\n cell: HTMLElement,\n panel: HTMLElement,\n context: FloatingRootContext,\n): boolean {\n const cells = queryFocusables(panel);\n const index = cells.indexOf(cell);\n if (index === -1) return false;\n\n switch (key) {\n case \"ArrowDown\":\n case \"ArrowRight\": {\n if (index < cells.length - 1) {\n cells[index + 1].focus();\n }\n return true;\n }\n case \"ArrowUp\":\n case \"ArrowLeft\": {\n if (index > 0) {\n cells[index - 1].focus();\n } else {\n focusTrigger(context);\n }\n return true;\n }\n case \"Home\": {\n cells[0]?.focus();\n return true;\n }\n case \"End\": {\n cells[cells.length - 1]?.focus();\n return true;\n }\n default:\n return false;\n }\n}\n\n/** Arrow/Home/End movement from `cell`. Returns `true` when the key was consumed. */\nfunction handleArrow(\n key: string,\n cell: HTMLElement,\n panel: HTMLElement,\n context: FloatingRootContext,\n): boolean {\n const { columns, actions } = buildModel(panel);\n\n if (columnsStacked(columns)) {\n return handleLinear(key, cell, panel, context);\n }\n\n const column = cell.closest<HTMLElement>(COLUMN_SELECTOR);\n const actionsEl = cell.closest<HTMLElement>(ACTIONS_SELECTOR);\n\n if (column) {\n const colIndex = columns.indexOf(column);\n const cells = queryFocusables(column);\n const rowIndex = cells.indexOf(cell);\n\n switch (key) {\n case \"ArrowDown\": {\n if (rowIndex < cells.length - 1) {\n cells[rowIndex + 1].focus();\n } else if (colIndex < columns.length - 1) {\n firstFocusable(columns[colIndex + 1])?.focus();\n } else if (actions.length > 0) {\n firstFocusable(actions[0])?.focus();\n } else {\n focusNextTriggerAndClose(context);\n }\n return true;\n }\n case \"ArrowUp\": {\n if (rowIndex > 0) {\n cells[rowIndex - 1].focus();\n } else {\n focusTrigger(context);\n }\n return true;\n }\n case \"ArrowRight\": {\n if (colIndex < columns.length - 1) {\n firstFocusable(columns[colIndex + 1])?.focus();\n return true;\n }\n // Right border column: exit to the next trigger and close if available\n if (\n rowIndex === cells.length - 1 &&\n focusNextTriggerAndClose(context)\n ) {\n return true;\n }\n // Wrap to the current trigger and keep the menu open,\n // mirroring Left in the first column.\n focusTrigger(context);\n return true;\n }\n case \"ArrowLeft\": {\n if (colIndex > 0) {\n firstFocusable(columns[colIndex - 1])?.focus();\n } else {\n focusTrigger(context);\n }\n return true;\n }\n case \"Home\": {\n cells[0]?.focus();\n return true;\n }\n case \"End\": {\n cells[cells.length - 1]?.focus();\n return true;\n }\n default:\n return false;\n }\n }\n\n if (actionsEl) {\n const cells = queryFocusables(actionsEl);\n const index = cells.indexOf(cell);\n\n switch (key) {\n case \"ArrowRight\": {\n if (index < cells.length - 1) {\n cells[index + 1].focus();\n } else if (!focusNextTriggerAndClose(context)) {\n // Last action with no next trigger: wrap to the current trigger.\n focusTrigger(context);\n }\n return true;\n }\n case \"ArrowLeft\": {\n if (index > 0) {\n cells[index - 1].focus();\n } else if (cell === firstFocusable(panel)) {\n focusTrigger(context);\n }\n return true;\n }\n case \"ArrowUp\": {\n if (columns.length > 0) {\n const lastColumnCells = queryFocusables(columns[columns.length - 1]);\n lastColumnCells[lastColumnCells.length - 1]?.focus();\n } else {\n focusTrigger(context);\n }\n return true;\n }\n case \"ArrowDown\": {\n if (index === cells.length - 1) {\n focusNextTriggerAndClose(context);\n }\n return true;\n }\n case \"Home\": {\n cells[0]?.focus();\n return true;\n }\n case \"End\": {\n cells[cells.length - 1]?.focus();\n return true;\n }\n default:\n return false;\n }\n }\n\n return false;\n}\n\n/** Focus the first document-focusable after the panel, once it has unmounted. */\nfunction focusNextAfterPanel(context: FloatingRootContext, panel: HTMLElement) {\n const reference = context.elements.reference as HTMLElement | null;\n const refFocusable = firstFocusable(reference) ?? reference;\n\n const nextLi = refFocusable?.closest(\"li\")?.nextElementSibling;\n const nextSibling =\n nextLi instanceof HTMLElement ? firstFocusable(nextLi) : null;\n\n const nextOutside =\n nextSibling ||\n (() => {\n const allFocusable = queryFocusables(\n panel.ownerDocument.documentElement,\n ).filter((el) => !panel.contains(el));\n const idx = refFocusable ? allFocusable.indexOf(refFocusable) : -1;\n return idx >= 0 ? allFocusable[idx + 1] : undefined;\n })();\n\n if (nextOutside) {\n const view = panel.ownerDocument.defaultView;\n view?.requestAnimationFrame(() => {\n view?.requestAnimationFrame(() => {\n nextOutside.focus();\n });\n });\n }\n}\n\nexport interface UseMegaMenuNavigationProps {\n /**\n * Whether the interaction is enabled.\n * @default true\n */\n enabled?: boolean;\n}\n\n/**\n * Floating-ui interaction hook for mega menu keyboard navigation. Handles keys\n * on the panel only; the trigger keys are owned by `MegaMenuTrigger`.\n */\nexport function useMegaMenuNavigation(\n context: FloatingRootContext,\n props: UseMegaMenuNavigationProps = {},\n): ElementProps {\n const { enabled = true } = props;\n const { open, onOpenChange } = context;\n\n return useMemo(() => {\n if (!enabled) {\n return {};\n }\n\n return {\n floating: {\n onKeyDown(event: React.KeyboardEvent) {\n if (!open) return;\n\n const panel = event.currentTarget as HTMLElement;\n const target = event.target as HTMLElement;\n const { key } = event;\n\n const isArrowOrEdge =\n key === \"ArrowUp\" ||\n key === \"ArrowDown\" ||\n key === \"ArrowLeft\" ||\n key === \"ArrowRight\" ||\n key === \"Home\" ||\n key === \"End\";\n\n // A self-consuming control keeps its own navigation keys.\n if (isArrowOrEdge && target.closest(SELF_CONSUMING_SELECTOR)) {\n return;\n }\n\n const cell = target.closest<HTMLElement>(FOCUSABLE_SELECTOR);\n\n // Tab walks every cell in layout order; the panel is portaled, so the\n // boundary transitions are handled manually.\n if (key === \"Tab\") {\n const allCells = queryFocusables(panel);\n const linearIndex = cell ? allCells.indexOf(cell) : -1;\n if (linearIndex === -1) return;\n event.preventDefault();\n if (event.shiftKey) {\n if (linearIndex === 0) {\n focusTrigger(context);\n } else {\n allCells[linearIndex - 1]?.focus();\n }\n } else if (linearIndex === allCells.length - 1) {\n onOpenChange(false);\n focusNextAfterPanel(context, panel);\n } else {\n allCells[linearIndex + 1]?.focus();\n }\n return;\n }\n\n if (!cell || !isArrowOrEdge) return;\n\n if (handleArrow(key, cell, panel, context)) {\n event.preventDefault();\n }\n },\n },\n };\n }, [enabled, open, context, onOpenChange]);\n}\n\n/** Focus the first navigable item inside a mega menu panel, retrying until rendered. */\nexport function focusFirstItem(panel: HTMLElement, attempt = 0): void {\n const firstItem = firstFocusable(panel);\n\n if (firstItem) {\n firstItem.focus();\n return;\n }\n\n const view = panel.ownerDocument.defaultView;\n if (attempt < 3 && view) {\n view.requestAnimationFrame(() => focusFirstItem(panel, attempt + 1));\n }\n}\n"],"names":[],"mappings":";;AAGA,MAAM,eAAA,GAAkB,yBAAA;AACxB,MAAM,gBAAA,GAAmB,0BAAA;AAElB,MAAM,kBAAA,GACX;AAIF,MAAM,uBAAA,GACJ,yIAAA;AAIF,MAAM,wBAAA,GAA2B,+BAAA;AAEjC,SAAS,gBAAgB,IAAA,EAAkC;AACzD,EAAA,OAAO,KAAA,CAAM,IAAA;AAAA,IACX,IAAA,CAAK,iBAA8B,kBAAkB;AAAA,GACvD,CAAE,OAAO,CAAC,EAAA,KAAO,CAAC,EAAA,CAAG,OAAA,CAAQ,wBAAwB,CAAC,CAAA;AACxD;AAEA,SAAS,eAAe,IAAA,EAA8C;AACpE,EAAA,OAAO,OAAQ,eAAA,CAAgB,IAAI,CAAA,CAAE,CAAC,KAAK,IAAA,GAAQ,IAAA;AACrD;AAUA,SAAS,WAAW,KAAA,EAA8B;AAChD,EAAA,MAAM,UAAU,KAAA,CAAM,IAAA;AAAA,IACpB,KAAA,CAAM,iBAA8B,eAAe;AAAA,GACrD,CAAE,OAAO,CAAC,EAAA,KAAO,gBAAgB,EAAE,CAAA,CAAE,SAAS,CAAC,CAAA;AAE/C,EAAA,MAAM,UAAU,KAAA,CAAM,IAAA;AAAA,IACpB,KAAA,CAAM,iBAA8B,gBAAgB;AAAA,GACtD,CAAE,OAAO,CAAC,EAAA,KAAO,gBAAgB,EAAE,CAAA,CAAE,SAAS,CAAC,CAAA;AAE/C,EAAA,OAAO,EAAE,SAAS,OAAA,EAAQ;AAC5B;AAEA,SAAS,aAAa,OAAA,EAA8B;AAhDpD,EAAA,IAAA,EAAA;AAiDE,EAAA,MAAM,SAAA,GAAY,QAAQ,QAAA,CAAS,SAAA;AACnC,EAAA,CAAC,EAAA,GAAA,cAAA,CAAe,SAAS,CAAA,IAAK,SAAA,KAA7B,IAAA,GAAA,MAAA,GAAA,EAAA,CAAyC,KAAA,EAAA;AAC5C;AAGA,SAAS,yBAAyB,OAAA,EAAuC;AACvE,EAAA,MAAM,SAAA,GAAY,QAAQ,QAAA,CAAS,SAAA;AACnC,EAAA,MAAM,OAAA,GAAU,cAAA,CAAe,SAAS,CAAA,IAAK,SAAA;AAC7C,EAAA,MAAM,EAAA,GAAK,mCAAS,OAAA,CAAQ,IAAA,CAAA;AAC5B,EAAA,MAAM,QACJ,EAAA,IAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAI,kBAAA,aAA8B,cAC9B,cAAA,CAAe,EAAA,CAAG,kBAAkB,CAAA,GACpC,IAAA;AACN,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,OAAA,CAAQ,aAAa,KAAK,CAAA;AAC1B,IAAA,IAAA,CAAK,KAAA,EAAM;AACX,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA;AACT;AAGA,SAAS,eAAe,OAAA,EAAiC;AACvD,EAAA,IAAI,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG,OAAO,KAAA;AAC/B,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,CAAC,CAAA,CAAE,qBAAA,EAAsB;AAC/C,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,CAAC,CAAA,CAAE,qBAAA,EAAsB;AAChD,EAAA,OAAO,MAAA,CAAO,GAAA,IAAO,KAAA,CAAM,MAAA,GAAS,CAAA;AACtC;AAGA,SAAS,YAAA,CACP,GAAA,EACA,IAAA,EACA,KAAA,EACA,OAAA,EACS;AApFX,EAAA,IAAA,EAAA,EAAA,EAAA;AAqFE,EAAA,MAAM,KAAA,GAAQ,gBAAgB,KAAK,CAAA;AACnC,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA;AAChC,EAAA,IAAI,KAAA,KAAU,IAAI,OAAO,KAAA;AAEzB,EAAA,QAAQ,GAAA;AAAK,IACX,KAAK,WAAA;AAAA,IACL,KAAK,YAAA,EAAc;AACjB,MAAA,IAAI,KAAA,GAAQ,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG;AAC5B,QAAA,KAAA,CAAM,KAAA,GAAQ,CAAC,CAAA,CAAE,KAAA,EAAM;AAAA,MACzB;AACA,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,IACA,KAAK,SAAA;AAAA,IACL,KAAK,WAAA,EAAa;AAChB,MAAA,IAAI,QAAQ,CAAA,EAAG;AACb,QAAA,KAAA,CAAM,KAAA,GAAQ,CAAC,CAAA,CAAE,KAAA,EAAM;AAAA,MACzB,CAAA,MAAO;AACL,QAAA,YAAA,CAAa,OAAO,CAAA;AAAA,MACtB;AACA,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,IACA,KAAK,MAAA,EAAQ;AACX,MAAA,CAAA,EAAA,GAAA,KAAA,CAAM,CAAC,MAAP,IAAA,GAAA,MAAA,GAAA,EAAA,CAAU,KAAA,EAAA;AACV,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,IACA,KAAK,KAAA,EAAO;AACV,MAAA,CAAA,EAAA,GAAA,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA,KAAtB,IAAA,GAAA,MAAA,GAAA,EAAA,CAAyB,KAAA,EAAA;AACzB,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,IACA;AACE,MAAA,OAAO,KAAA;AAAA;AAEb;AAGA,SAAS,WAAA,CACP,GAAA,EACA,IAAA,EACA,KAAA,EACA,OAAA,EACS;AA7HX,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AA8HE,EAAA,MAAM,EAAE,OAAA,EAAS,OAAA,EAAQ,GAAI,WAAW,KAAK,CAAA;AAE7C,EAAA,IAAI,cAAA,CAAe,OAAO,CAAA,EAAG;AAC3B,IAAA,OAAO,YAAA,CAAa,GAAA,EAAK,IAAA,EAAM,KAAA,EAAO,OAAO,CAAA;AAAA,EAC/C;AAEA,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAqB,eAAe,CAAA;AACxD,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,OAAA,CAAqB,gBAAgB,CAAA;AAE5D,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA;AACvC,IAAA,MAAM,KAAA,GAAQ,gBAAgB,MAAM,CAAA;AACpC,IAAA,MAAM,QAAA,GAAW,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA;AAEnC,IAAA,QAAQ,GAAA;AAAK,MACX,KAAK,WAAA,EAAa;AAChB,QAAA,IAAI,QAAA,GAAW,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG;AAC/B,UAAA,KAAA,CAAM,QAAA,GAAW,CAAC,CAAA,CAAE,KAAA,EAAM;AAAA,QAC5B,CAAA,MAAA,IAAW,QAAA,GAAW,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AACxC,UAAA,CAAA,EAAA,GAAA,cAAA,CAAe,OAAA,CAAQ,QAAA,GAAW,CAAC,CAAC,MAApC,IAAA,GAAA,MAAA,GAAA,EAAA,CAAuC,KAAA,EAAA;AAAA,QACzC,CAAA,MAAA,IAAW,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AAC7B,UAAA,CAAA,EAAA,GAAA,cAAA,CAAe,OAAA,CAAQ,CAAC,CAAC,CAAA,KAAzB,IAAA,GAAA,MAAA,GAAA,EAAA,CAA4B,KAAA,EAAA;AAAA,QAC9B,CAAA,MAAO;AACL,UAAA,wBAAA,CAAyB,OAAO,CAAA;AAAA,QAClC;AACA,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,MACA,KAAK,SAAA,EAAW;AACd,QAAA,IAAI,WAAW,CAAA,EAAG;AAChB,UAAA,KAAA,CAAM,QAAA,GAAW,CAAC,CAAA,CAAE,KAAA,EAAM;AAAA,QAC5B,CAAA,MAAO;AACL,UAAA,YAAA,CAAa,OAAO,CAAA;AAAA,QACtB;AACA,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,MACA,KAAK,YAAA,EAAc;AACjB,QAAA,IAAI,QAAA,GAAW,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AACjC,UAAA,CAAA,EAAA,GAAA,cAAA,CAAe,OAAA,CAAQ,QAAA,GAAW,CAAC,CAAC,MAApC,IAAA,GAAA,MAAA,GAAA,EAAA,CAAuC,KAAA,EAAA;AACvC,UAAA,OAAO,IAAA;AAAA,QACT;AAEA,QAAA,IACE,aAAa,KAAA,CAAM,MAAA,GAAS,CAAA,IAC5B,wBAAA,CAAyB,OAAO,CAAA,EAChC;AACA,UAAA,OAAO,IAAA;AAAA,QACT;AAGA,QAAA,YAAA,CAAa,OAAO,CAAA;AACpB,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,MACA,KAAK,WAAA,EAAa;AAChB,QAAA,IAAI,WAAW,CAAA,EAAG;AAChB,UAAA,CAAA,EAAA,GAAA,cAAA,CAAe,OAAA,CAAQ,QAAA,GAAW,CAAC,CAAC,MAApC,IAAA,GAAA,MAAA,GAAA,EAAA,CAAuC,KAAA,EAAA;AAAA,QACzC,CAAA,MAAO;AACL,UAAA,YAAA,CAAa,OAAO,CAAA;AAAA,QACtB;AACA,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,MACA,KAAK,MAAA,EAAQ;AACX,QAAA,CAAA,EAAA,GAAA,KAAA,CAAM,CAAC,MAAP,IAAA,GAAA,MAAA,GAAA,EAAA,CAAU,KAAA,EAAA;AACV,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,MACA,KAAK,KAAA,EAAO;AACV,QAAA,CAAA,EAAA,GAAA,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA,KAAtB,IAAA,GAAA,MAAA,GAAA,EAAA,CAAyB,KAAA,EAAA;AACzB,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,MACA;AACE,QAAA,OAAO,KAAA;AAAA;AACX,EACF;AAEA,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,MAAM,KAAA,GAAQ,gBAAgB,SAAS,CAAA;AACvC,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA;AAEhC,IAAA,QAAQ,GAAA;AAAK,MACX,KAAK,YAAA,EAAc;AACjB,QAAA,IAAI,KAAA,GAAQ,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG;AAC5B,UAAA,KAAA,CAAM,KAAA,GAAQ,CAAC,CAAA,CAAE,KAAA,EAAM;AAAA,QACzB,CAAA,MAAA,IAAW,CAAC,wBAAA,CAAyB,OAAO,CAAA,EAAG;AAE7C,UAAA,YAAA,CAAa,OAAO,CAAA;AAAA,QACtB;AACA,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,MACA,KAAK,WAAA,EAAa;AAChB,QAAA,IAAI,QAAQ,CAAA,EAAG;AACb,UAAA,KAAA,CAAM,KAAA,GAAQ,CAAC,CAAA,CAAE,KAAA,EAAM;AAAA,QACzB,CAAA,MAAA,IAAW,IAAA,KAAS,cAAA,CAAe,KAAK,CAAA,EAAG;AACzC,UAAA,YAAA,CAAa,OAAO,CAAA;AAAA,QACtB;AACA,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,MACA,KAAK,SAAA,EAAW;AACd,QAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtB,UAAA,MAAM,kBAAkB,eAAA,CAAgB,OAAA,CAAQ,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAC,CAAA;AACnE,UAAA,CAAA,EAAA,GAAA,eAAA,CAAgB,eAAA,CAAgB,MAAA,GAAS,CAAC,CAAA,KAA1C,IAAA,GAAA,MAAA,GAAA,EAAA,CAA6C,KAAA,EAAA;AAAA,QAC/C,CAAA,MAAO;AACL,UAAA,YAAA,CAAa,OAAO,CAAA;AAAA,QACtB;AACA,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,MACA,KAAK,WAAA,EAAa;AAChB,QAAA,IAAI,KAAA,KAAU,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG;AAC9B,UAAA,wBAAA,CAAyB,OAAO,CAAA;AAAA,QAClC;AACA,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,MACA,KAAK,MAAA,EAAQ;AACX,QAAA,CAAA,EAAA,GAAA,KAAA,CAAM,CAAC,MAAP,IAAA,GAAA,MAAA,GAAA,EAAA,CAAU,KAAA,EAAA;AACV,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,MACA,KAAK,KAAA,EAAO;AACV,QAAA,CAAA,EAAA,GAAA,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA,KAAtB,IAAA,GAAA,MAAA,GAAA,EAAA,CAAyB,KAAA,EAAA;AACzB,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,MACA;AACE,QAAA,OAAO,KAAA;AAAA;AACX,EACF;AAEA,EAAA,OAAO,KAAA;AACT;AAGA,SAAS,mBAAA,CAAoB,SAA8B,KAAA,EAAoB;AA7P/E,EAAA,IAAA,EAAA;AA8PE,EAAA,MAAM,SAAA,GAAY,QAAQ,QAAA,CAAS,SAAA;AACnC,EAAA,MAAM,YAAA,GAAe,cAAA,CAAe,SAAS,CAAA,IAAK,SAAA;AAElD,EAAA,MAAM,MAAA,GAAA,CAAS,EAAA,GAAA,YAAA,IAAA,IAAA,GAAA,MAAA,GAAA,YAAA,CAAc,OAAA,CAAQ,IAAA,CAAA,KAAtB,IAAA,GAAA,MAAA,GAAA,EAAA,CAA6B,kBAAA;AAC5C,EAAA,MAAM,WAAA,GACJ,MAAA,YAAkB,WAAA,GAAc,cAAA,CAAe,MAAM,CAAA,GAAI,IAAA;AAE3D,EAAA,MAAM,WAAA,GACJ,gBACC,MAAM;AACL,IAAA,MAAM,YAAA,GAAe,eAAA;AAAA,MACnB,MAAM,aAAA,CAAc;AAAA,KACtB,CAAE,OAAO,CAAC,EAAA,KAAO,CAAC,KAAA,CAAM,QAAA,CAAS,EAAE,CAAC,CAAA;AACpC,IAAA,MAAM,GAAA,GAAM,YAAA,GAAe,YAAA,CAAa,OAAA,CAAQ,YAAY,CAAA,GAAI,EAAA;AAChE,IAAA,OAAO,GAAA,IAAO,CAAA,GAAI,YAAA,CAAa,GAAA,GAAM,CAAC,CAAA,GAAI,MAAA;AAAA,EAC5C,CAAA,GAAG;AAEL,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,MAAM,IAAA,GAAO,MAAM,aAAA,CAAc,WAAA;AACjC,IAAA,IAAA,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAM,sBAAsB,MAAM;AAChC,MAAA,IAAA,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAM,sBAAsB,MAAM;AAChC,QAAA,WAAA,CAAY,KAAA,EAAM;AAAA,MACpB,CAAA,CAAA;AAAA,IACF,CAAA,CAAA;AAAA,EACF;AACF;AAcO,SAAS,qBAAA,CACd,OAAA,EACA,KAAA,GAAoC,EAAC,EACvB;AACd,EAAA,MAAM,EAAE,OAAA,GAAU,IAAA,EAAK,GAAI,KAAA;AAC3B,EAAA,MAAM,EAAE,IAAA,EAAM,YAAA,EAAa,GAAI,OAAA;AAE/B,EAAA,OAAO,QAAQ,MAAM;AACnB,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,OAAO,EAAC;AAAA,IACV;AAEA,IAAA,OAAO;AAAA,MACL,QAAA,EAAU;AAAA,QACR,UAAU,KAAA,EAA4B;AAnT9C,UAAA,IAAA,EAAA,EAAA,EAAA;AAoTU,UAAA,IAAI,CAAC,IAAA,EAAM;AAEX,UAAA,MAAM,QAAQ,KAAA,CAAM,aAAA;AACpB,UAAA,MAAM,SAAS,KAAA,CAAM,MAAA;AACrB,UAAA,MAAM,EAAE,KAAI,GAAI,KAAA;AAEhB,UAAA,MAAM,aAAA,GACJ,GAAA,KAAQ,SAAA,IACR,GAAA,KAAQ,WAAA,IACR,GAAA,KAAQ,WAAA,IACR,GAAA,KAAQ,YAAA,IACR,GAAA,KAAQ,MAAA,IACR,GAAA,KAAQ,KAAA;AAGV,UAAA,IAAI,aAAA,IAAiB,MAAA,CAAO,OAAA,CAAQ,uBAAuB,CAAA,EAAG;AAC5D,YAAA;AAAA,UACF;AAEA,UAAA,MAAM,IAAA,GAAO,MAAA,CAAO,OAAA,CAAqB,kBAAkB,CAAA;AAI3D,UAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,YAAA,MAAM,QAAA,GAAW,gBAAgB,KAAK,CAAA;AACtC,YAAA,MAAM,WAAA,GAAc,IAAA,GAAO,QAAA,CAAS,OAAA,CAAQ,IAAI,CAAA,GAAI,EAAA;AACpD,YAAA,IAAI,gBAAgB,EAAA,EAAI;AACxB,YAAA,KAAA,CAAM,cAAA,EAAe;AACrB,YAAA,IAAI,MAAM,QAAA,EAAU;AAClB,cAAA,IAAI,gBAAgB,CAAA,EAAG;AACrB,gBAAA,YAAA,CAAa,OAAO,CAAA;AAAA,cACtB,CAAA,MAAO;AACL,gBAAA,CAAA,EAAA,GAAA,QAAA,CAAS,WAAA,GAAc,CAAC,CAAA,KAAxB,IAAA,GAAA,MAAA,GAAA,EAAA,CAA2B,KAAA,EAAA;AAAA,cAC7B;AAAA,YACF,CAAA,MAAA,IAAW,WAAA,KAAgB,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG;AAC9C,cAAA,YAAA,CAAa,KAAK,CAAA;AAClB,cAAA,mBAAA,CAAoB,SAAS,KAAK,CAAA;AAAA,YACpC,CAAA,MAAO;AACL,cAAA,CAAA,EAAA,GAAA,QAAA,CAAS,WAAA,GAAc,CAAC,CAAA,KAAxB,IAAA,GAAA,MAAA,GAAA,EAAA,CAA2B,KAAA,EAAA;AAAA,YAC7B;AACA,YAAA;AAAA,UACF;AAEA,UAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,aAAA,EAAe;AAE7B,UAAA,IAAI,WAAA,CAAY,GAAA,EAAK,IAAA,EAAM,KAAA,EAAO,OAAO,CAAA,EAAG;AAC1C,YAAA,KAAA,CAAM,cAAA,EAAe;AAAA,UACvB;AAAA,QACF;AAAA;AACF,KACF;AAAA,EACF,GAAG,CAAC,OAAA,EAAS,IAAA,EAAM,OAAA,EAAS,YAAY,CAAC,CAAA;AAC3C;AAGO,SAAS,cAAA,CAAe,KAAA,EAAoB,OAAA,GAAU,CAAA,EAAS;AACpE,EAAA,MAAM,SAAA,GAAY,eAAe,KAAK,CAAA;AAEtC,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,SAAA,CAAU,KAAA,EAAM;AAChB,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,GAAO,MAAM,aAAA,CAAc,WAAA;AACjC,EAAA,IAAI,OAAA,GAAU,KAAK,IAAA,EAAM;AACvB,IAAA,IAAA,CAAK,sBAAsB,MAAM,cAAA,CAAe,KAAA,EAAO,OAAA,GAAU,CAAC,CAAC,CAAA;AAAA,EACrE;AACF;;;;"}
@@ -0,0 +1,20 @@
1
+ import { useDensity, useIsomorphicLayoutEffect } from '@salt-ds/core';
2
+ import { useState } from 'react';
3
+
4
+ function usePageMargin(referenceEl) {
5
+ const density = useDensity();
6
+ const [pageMargin, setPageMargin] = useState(0);
7
+ useIsomorphicLayoutEffect(() => {
8
+ if (!(referenceEl == null ? void 0 : referenceEl.parentNode)) return;
9
+ const probe = referenceEl.ownerDocument.createElement("div");
10
+ probe.style.cssText = "position:absolute;visibility:hidden;pointer-events:none;width:var(--saltMegaMenuPanel-pageMargin, var(--salt-layout-page-margin));";
11
+ referenceEl.insertAdjacentElement("afterend", probe);
12
+ const width = probe.getBoundingClientRect().width;
13
+ probe.remove();
14
+ setPageMargin((prev) => prev === width ? prev : width);
15
+ }, [referenceEl, density]);
16
+ return pageMargin;
17
+ }
18
+
19
+ export { usePageMargin };
20
+ //# sourceMappingURL=usePageMargin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePageMargin.js","sources":["../src/mega-menu/usePageMargin.ts"],"sourcesContent":["import { useDensity, useIsomorphicLayoutEffect } from \"@salt-ds/core\";\nimport { useState } from \"react\";\n\n/**\n * Resolves the panel's page-margin token to pixels for floating-ui padding, to\n * handle token (`--saltMegaMenuPanel-pageMargin`) override by consumers\n * (edge to edge example).\n */\nexport function usePageMargin(\n referenceEl: HTMLElement | null | undefined,\n): number {\n const density = useDensity();\n const [pageMargin, setPageMargin] = useState(0);\n // biome-ignore lint/correctness/useExhaustiveDependencies: density isn't read here — signal that the resolved token changed.\n useIsomorphicLayoutEffect(() => {\n if (!referenceEl?.parentNode) return;\n const probe = referenceEl.ownerDocument.createElement(\"div\");\n probe.style.cssText =\n \"position:absolute;visibility:hidden;pointer-events:none;width:var(--saltMegaMenuPanel-pageMargin, var(--salt-layout-page-margin));\";\n referenceEl.insertAdjacentElement(\"afterend\", probe);\n const width = probe.getBoundingClientRect().width;\n probe.remove();\n setPageMargin((prev) => (prev === width ? prev : width));\n }, [referenceEl, density]);\n return pageMargin;\n}\n"],"names":[],"mappings":";;;AAQO,SAAS,cACd,WAAA,EACQ;AACR,EAAA,MAAM,UAAU,UAAA,EAAW;AAC3B,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAS,CAAC,CAAA;AAE9C,EAAA,yBAAA,CAA0B,MAAM;AAC9B,IAAA,IAAI,EAAC,2CAAa,UAAA,CAAA,EAAY;AAC9B,IAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,aAAA,CAAc,aAAA,CAAc,KAAK,CAAA;AAC3D,IAAA,KAAA,CAAM,MAAM,OAAA,GACV,oIAAA;AACF,IAAA,WAAA,CAAY,qBAAA,CAAsB,YAAY,KAAK,CAAA;AACnD,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,qBAAA,EAAsB,CAAE,KAAA;AAC5C,IAAA,KAAA,CAAM,MAAA,EAAO;AACb,IAAA,aAAA,CAAc,CAAC,IAAA,KAAU,IAAA,KAAS,KAAA,GAAQ,OAAO,KAAM,CAAA;AAAA,EACzD,CAAA,EAAG,CAAC,WAAA,EAAa,OAAO,CAAC,CAAA;AACzB,EAAA,OAAO,UAAA;AACT;;;;"}
@@ -37,6 +37,5 @@ export * from "./toast-group";
37
37
  export * from "./tokenized-input";
38
38
  export * from "./tokenized-input-next";
39
39
  export * from "./toolbar";
40
- export * from "./toolbar-next";
41
40
  export * from "./utils";
42
41
  export * from "./window";
@@ -1,5 +1,8 @@
1
- import { type Placement } from "@floating-ui/react";
2
1
  import { type ReactNode } from "react";
2
+ /**
3
+ * Supported placements for the mega menu panel.
4
+ */
5
+ export type MegaMenuPlacement = "bottom" | "bottom-start" | "bottom-end";
3
6
  export interface MegaMenuProps {
4
7
  /**
5
8
  * The content of the mega menu, typically `MegaMenuTrigger` and `MegaMenuPanel`.
@@ -22,6 +25,12 @@ export interface MegaMenuProps {
22
25
  * The placement of the mega menu panel relative to the trigger.
23
26
  * @default "bottom"
24
27
  */
25
- placement?: Placement;
28
+ placement?: MegaMenuPlacement;
26
29
  }
30
+ /**
31
+ * Root of a mega menu, coordinating its trigger and panel.
32
+ *
33
+ * Not intended to be nested inside another floating overlay (e.g. a dialog or
34
+ * popover): the mega menu does not participate in floating-ui's `FloatingTree`
35
+ */
27
36
  export declare function MegaMenu({ children, open, defaultOpen, onOpenChange, placement, }: MegaMenuProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { type ComponentPropsWithoutRef, type ReactNode } from "react";
2
+ export interface MegaMenuActionsProps extends ComponentPropsWithoutRef<"div"> {
3
+ /**
4
+ * The content of the actions. Place `MegaMenuActions` inside
5
+ * `MegaMenuContent`, after `MegaMenuGroups`; it renders as a band beneath the
6
+ * groups, spanning their width.
7
+ */
8
+ children?: ReactNode;
9
+ }
10
+ export declare const MegaMenuActions: import("react").ForwardRefExoticComponent<MegaMenuActionsProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,9 @@
1
+ import { type ComponentPropsWithoutRef, type ReactNode } from "react";
2
+ export interface MegaMenuAsideProps extends ComponentPropsWithoutRef<"aside"> {
3
+ /**
4
+ * The content. Renders to the left of `MegaMenuContent` when placed before
5
+ * it, and to the right when placed after.
6
+ */
7
+ children?: ReactNode;
8
+ }
9
+ export declare const MegaMenuAside: import("react").ForwardRefExoticComponent<MegaMenuAsideProps & import("react").RefAttributes<HTMLElement>>;
@@ -0,0 +1,9 @@
1
+ import { type ComponentPropsWithoutRef, type ReactNode } from "react";
2
+ export interface MegaMenuContentProps extends ComponentPropsWithoutRef<"div"> {
3
+ /**
4
+ * The content of the mega menu navigation area, typically a `MegaMenuGroups`
5
+ * and an optional `MegaMenuActions`.
6
+ */
7
+ children?: ReactNode;
8
+ }
9
+ export declare const MegaMenuContent: import("react").ForwardRefExoticComponent<MegaMenuContentProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -1,5 +1,6 @@
1
- import type { FloatingRootContext, Placement } from "@floating-ui/react";
2
- import type { Dispatch, HTMLProps, SetStateAction } from "react";
1
+ import type { FloatingRootContext } from "@floating-ui/react";
2
+ import type { Dispatch, HTMLProps, MutableRefObject, SetStateAction } from "react";
3
+ import type { MegaMenuPlacement } from "./MegaMenu";
3
4
  export interface MegaMenuContextValue {
4
5
  /** Whether the mega menu is currently open. */
5
6
  openState: boolean;
@@ -8,7 +9,7 @@ export interface MegaMenuContextValue {
8
9
  /** The floating-ui root context for coordinating interactions. */
9
10
  floatingRootContext: FloatingRootContext;
10
11
  /** The placement of the mega menu panel relative to the trigger. */
11
- placement: Placement;
12
+ placement: MegaMenuPlacement;
12
13
  /** Props getter for the trigger (reference) element. Merges floating-ui interaction props with user props. */
13
14
  getReferenceProps: (userProps?: HTMLProps<Element> | undefined) => Record<string, unknown>;
14
15
  /** Props getter for the floating panel element. Merges floating-ui interaction props with user props. */
@@ -18,12 +19,10 @@ export interface MegaMenuContextValue {
18
19
  /** Ref setter for the trigger (reference) element. */
19
20
  setReference: Dispatch<SetStateAction<HTMLElement | null>>;
20
21
  /** Whether the first item should receive focus when the panel opens. */
21
- focusFirstItemOnOpen: boolean;
22
- /** Toggle the focus-first-item-on-open flag. */
23
- setFocusFirstItemOnOpen: Dispatch<SetStateAction<boolean>>;
22
+ focusFirstItemOnOpenRef: MutableRefObject<boolean>;
24
23
  /** The id of the mega menu panel, used for aria-controls on the trigger. */
25
24
  panelId: string | undefined;
26
- /** Set the panel id when the panel mounts. */
25
+ /** Registers the panel's id so the trigger's aria-controls stays in sync. */
27
26
  setPanelId: Dispatch<SetStateAction<string | undefined>>;
28
27
  }
29
28
  export declare const MegaMenuContext: import("react").Context<MegaMenuContextValue | undefined>;
@@ -1,7 +1,8 @@
1
- import { type HTMLAttributes, type ReactNode } from "react";
2
- export interface MegaMenuGroupProps extends HTMLAttributes<HTMLDivElement> {
1
+ import { type ComponentPropsWithoutRef, type ReactNode } from "react";
2
+ export interface MegaMenuGroupProps extends ComponentPropsWithoutRef<"div"> {
3
3
  /**
4
- * The content of the mega menu group, typically MegaMenuHeader and MegaMenuItem components.
4
+ * The content of the mega menu group: a `MegaMenuGroupHeading` and a
5
+ * `MegaMenuList` of `MegaMenuListItem`s.
5
6
  */
6
7
  children?: ReactNode;
7
8
  }
@@ -0,0 +1,12 @@
1
+ export interface MegaMenuGroupContextValue {
2
+ /**
3
+ * The rendered heading's id, used to label the item list via `aria-labelledby`.
4
+ * `undefined` when the group has no `MegaMenuGroupHeading`.
5
+ */
6
+ headingId: string | undefined;
7
+ /** Register (or clear, with `undefined`) the heading's id. */
8
+ setHeadingId: (id: string | undefined) => void;
9
+ }
10
+ export declare const MegaMenuGroupContext: import("react").Context<MegaMenuGroupContextValue>;
11
+ /** Access the nearest `MegaMenuGroup` context. */
12
+ export declare function useMegaMenuGroup(): MegaMenuGroupContextValue;
@@ -0,0 +1,13 @@
1
+ import { type ComponentPropsWithoutRef, type ReactNode } from "react";
2
+ export interface MegaMenuGroupHeadingProps extends ComponentPropsWithoutRef<"h2"> {
3
+ /**
4
+ * The content of the mega menu group heading.
5
+ */
6
+ children?: ReactNode;
7
+ /**
8
+ * The heading element to render.
9
+ * @default "h3"
10
+ */
11
+ as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
12
+ }
13
+ export declare const MegaMenuGroupHeading: import("react").ForwardRefExoticComponent<MegaMenuGroupHeadingProps & import("react").RefAttributes<HTMLHeadingElement>>;
@@ -1,7 +1,7 @@
1
- import { type HTMLAttributes, type ReactNode } from "react";
2
- export interface MegaMenuGroupsProps extends HTMLAttributes<HTMLDivElement> {
1
+ import { type ComponentPropsWithoutRef, type ReactNode } from "react";
2
+ export interface MegaMenuGroupsProps extends ComponentPropsWithoutRef<"div"> {
3
3
  /**
4
- * The content of the mega menu section.
4
+ * The `MegaMenuGroup` columns of the mega menu.
5
5
  */
6
6
  children?: ReactNode;
7
7
  }
@@ -0,0 +1,16 @@
1
+ import { type PolymorphicComponentPropWithRef } from "@salt-ds/core";
2
+ import { type ElementType, type FunctionComponent, type ReactNode } from "react";
3
+ export type MegaMenuListProps<T extends ElementType = "ul"> = PolymorphicComponentPropWithRef<T, {
4
+ /**
5
+ * The items of the group, typically `MegaMenuListItem` components. Each
6
+ * `MegaMenuListItem` renders its own `<li>`.
7
+ */
8
+ children?: ReactNode;
9
+ }>;
10
+ type MegaMenuListComponent = <T extends ElementType = "ul">(props: MegaMenuListProps<T>) => ReturnType<FunctionComponent>;
11
+ /**
12
+ * The list of a group's `MegaMenuListItem`s. Renders a `<ul>` by default; pass
13
+ * `as="ol"` for an ordered list.
14
+ */
15
+ export declare const MegaMenuList: MegaMenuListComponent;
16
+ export {};
@@ -0,0 +1,25 @@
1
+ import { type RenderPropsType } from "@salt-ds/core";
2
+ import { type ComponentPropsWithoutRef, type MouseEventHandler, type ReactNode } from "react";
3
+ export interface MegaMenuListItemProps extends Omit<ComponentPropsWithoutRef<"li">, "onClick"> {
4
+ /**
5
+ * The content of the mega menu item.
6
+ */
7
+ children?: ReactNode;
8
+ /**
9
+ * Whether the item is the current page. Sets `aria-current="page"`
10
+ */
11
+ current?: boolean;
12
+ /**
13
+ * Href passed to the link action element.
14
+ */
15
+ href?: string;
16
+ /**
17
+ * Called when the item's action is activated, before the menu closes.
18
+ */
19
+ onClick?: MouseEventHandler<HTMLAnchorElement>;
20
+ /**
21
+ * Render prop to enable customization of the underlying action element (e.g. a router `Link`).
22
+ */
23
+ render?: RenderPropsType["render"];
24
+ }
25
+ export declare const MegaMenuListItem: import("react").ForwardRefExoticComponent<MegaMenuListItemProps & import("react").RefAttributes<HTMLLIElement>>;
@@ -1,5 +1,5 @@
1
- import { type HTMLAttributes, type ReactNode } from "react";
2
- export interface MegaMenuPanelProps extends HTMLAttributes<HTMLDivElement> {
1
+ import { type ComponentPropsWithoutRef, type ReactNode } from "react";
2
+ export interface MegaMenuPanelProps extends ComponentPropsWithoutRef<"div"> {
3
3
  /**
4
4
  * The content of the mega menu panel.
5
5
  */
@@ -2,6 +2,7 @@ import { type ReactNode } from "react";
2
2
  export interface MegaMenuTriggerProps {
3
3
  /**
4
4
  * The trigger element for the mega menu, typically a `NavigationItem` or `Button`.
5
+ * Wrap each mega menu in its own `<li>` — arrow-key navigation between triggers depends on it.
5
6
  */
6
7
  children?: ReactNode;
7
8
  }
@@ -1,9 +1,11 @@
1
1
  export * from "./MegaMenu";
2
+ export * from "./MegaMenuActions";
3
+ export * from "./MegaMenuAside";
4
+ export * from "./MegaMenuContent";
2
5
  export * from "./MegaMenuGroup";
6
+ export * from "./MegaMenuGroupHeading";
3
7
  export * from "./MegaMenuGroups";
4
- export * from "./MegaMenuHeader";
5
- export * from "./MegaMenuItem";
8
+ export * from "./MegaMenuList";
9
+ export * from "./MegaMenuListItem";
6
10
  export * from "./MegaMenuPanel";
7
- export * from "./MegaMenuSupportingActions";
8
- export * from "./MegaMenuSupportingContent";
9
11
  export * from "./MegaMenuTrigger";
@@ -0,0 +1,16 @@
1
+ import type { ElementProps, FloatingRootContext } from "@floating-ui/react";
2
+ export declare const FOCUSABLE_SELECTOR = "a[href], button:not([disabled]), [tabindex]:not([tabindex=\"-1\"])";
3
+ export interface UseMegaMenuNavigationProps {
4
+ /**
5
+ * Whether the interaction is enabled.
6
+ * @default true
7
+ */
8
+ enabled?: boolean;
9
+ }
10
+ /**
11
+ * Floating-ui interaction hook for mega menu keyboard navigation. Handles keys
12
+ * on the panel only; the trigger keys are owned by `MegaMenuTrigger`.
13
+ */
14
+ export declare function useMegaMenuNavigation(context: FloatingRootContext, props?: UseMegaMenuNavigationProps): ElementProps;
15
+ /** Focus the first navigable item inside a mega menu panel, retrying until rendered. */
16
+ export declare function focusFirstItem(panel: HTMLElement, attempt?: number): void;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Resolves the panel's page-margin token to pixels for floating-ui padding, to
3
+ * handle token (`--saltMegaMenuPanel-pageMargin`) override by consumers
4
+ * (edge to edge example).
5
+ */
6
+ export declare function usePageMargin(referenceEl: HTMLElement | null | undefined): number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salt-ds/lab",
3
- "version": "1.0.0-alpha.95",
3
+ "version": "1.0.0-alpha.97",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "dependencies": {
23
23
  "@floating-ui/react": "^0.26.28",
24
- "@salt-ds/core": "^1.64.0",
24
+ "@salt-ds/core": "^1.65.0",
25
25
  "@salt-ds/icons": "^1.18.0",
26
26
  "@salt-ds/styles": "0.3.0",
27
27
  "@salt-ds/window": "0.1.1",
@@ -1,6 +0,0 @@
1
- 'use strict';
2
-
3
- var css_248z = ".saltMegaMenuHeader-content {\n display: flex;\n align-items: center;\n padding: var(--salt-spacing-50) var(--salt-spacing-100);\n font-family: var(--salt-text-label-fontFamily);\n font-size: var(--salt-text-label-fontSize);\n font-weight: var(--salt-text-fontWeight-strong);\n line-height: var(--salt-text-label-lineHeight);\n min-height: var(--salt-size-base);\n color: var(--salt-content-secondary-foreground);\n}\n.saltMegaMenuHeader {\n margin-bottom: var(--salt-spacing-50);\n border-bottom: var(--salt-size-fixed-100) var(--salt-borderStyle-solid) var(--salt-separable-tertiary-borderColor);\n}\n";
4
-
5
- module.exports = css_248z;
6
- //# sourceMappingURL=MegaMenuHeader.css.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MegaMenuHeader.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}