@infinite-table/infinite-react 0.4.13 → 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 (282) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/API.md +0 -0
  3. package/build/buildPackageJson.js +43 -0
  4. package/build/scripts/test.js +37 -0
  5. package/bundler.config.js +25 -0
  6. package/globals.d.ts +7 -0
  7. package/package.json +95 -3
  8. package/rollup.config.js +15 -0
  9. package/run-build-deepmap.js +47 -0
  10. package/run-build.js +45 -0
  11. package/run-dts-concat.js +21 -0
  12. package/src/components/CSSVariableWatch.tsx +82 -0
  13. package/src/components/DataSource/BooleanCollectionState.ts +214 -0
  14. package/src/components/DataSource/BooleanDeepCollectionState.ts +208 -0
  15. package/src/components/DataSource/DataSourceContext.ts +20 -0
  16. package/src/components/DataSource/GroupRowsState.ts +74 -0
  17. package/src/components/DataSource/Indexer.ts +27 -0
  18. package/src/components/DataSource/RowSelectionState.ts +763 -0
  19. package/src/components/DataSource/defaultFilterTypes.ts +64 -0
  20. package/src/components/DataSource/index.tsx +108 -0
  21. package/src/components/DataSource/privateHooks/getChangeDetect.ts +7 -0
  22. package/src/components/DataSource/privateHooks/useLoadData.ts +783 -0
  23. package/src/components/DataSource/publicHooks/useDataSource.ts +19 -0
  24. package/src/components/DataSource/publicHooks/useDataSourceActions.ts +14 -0
  25. package/src/components/DataSource/state/getInitialState.ts +494 -0
  26. package/src/components/DataSource/state/normalizeSortInfo.ts +35 -0
  27. package/src/components/DataSource/state/reducer.ts +466 -0
  28. package/src/components/DataSource/state/rowInfoStatus.ts +27 -0
  29. package/src/components/DataSource/types.ts +548 -0
  30. package/src/components/HeadlessTable/HeadlessTableWithPinnedContainers.tsx +142 -0
  31. package/src/components/HeadlessTable/MappedCells.ts +251 -0
  32. package/src/components/HeadlessTable/RawTable.tsx +98 -0
  33. package/src/components/HeadlessTable/ReactHeadlessTableRenderer.tsx +1516 -0
  34. package/src/components/HeadlessTable/index.tsx +234 -0
  35. package/src/components/InfiniteTable/InfiniteCls.css.ts +164 -0
  36. package/src/components/InfiniteTable/InfiniteTableContext.ts +16 -0
  37. package/src/components/InfiniteTable/api/getImperativeApi.ts +391 -0
  38. package/src/components/InfiniteTable/api/getSelectionApi.ts +364 -0
  39. package/src/components/InfiniteTable/api/type.ts +11 -0
  40. package/src/components/InfiniteTable/components/ActiveCellIndicator.css.ts +51 -0
  41. package/src/components/InfiniteTable/components/ActiveCellIndicator.tsx +116 -0
  42. package/src/components/InfiniteTable/components/ActiveRowIndicator.css.ts +56 -0
  43. package/src/components/InfiniteTable/components/ActiveRowIndicator.tsx +94 -0
  44. package/src/components/InfiniteTable/components/CheckBox.css.ts +13 -0
  45. package/src/components/InfiniteTable/components/CheckBox.tsx +96 -0
  46. package/src/components/InfiniteTable/components/FilterEditors.tsx +52 -0
  47. package/src/components/InfiniteTable/components/InfiniteTableBody/InfiniteTableBody.tsx +27 -0
  48. package/src/components/InfiniteTable/components/InfiniteTableBody/index.tsx +1 -0
  49. package/src/components/InfiniteTable/components/InfiniteTableFooter/InfiniteTableFooter.tsx +31 -0
  50. package/src/components/InfiniteTable/components/InfiniteTableFooter/index.tsx +1 -0
  51. package/src/components/InfiniteTable/components/InfiniteTableHeader/InfiniteTableColumnHeaderFilter.tsx +102 -0
  52. package/src/components/InfiniteTable/components/InfiniteTableHeader/InfiniteTableHeader.tsx +163 -0
  53. package/src/components/InfiniteTable/components/InfiniteTableHeader/InfiniteTableHeaderCell.tsx +574 -0
  54. package/src/components/InfiniteTable/components/InfiniteTableHeader/InfiniteTableHeaderGroup.tsx +79 -0
  55. package/src/components/InfiniteTable/components/InfiniteTableHeader/InfiniteTableHeaderTypes.tsx +25 -0
  56. package/src/components/InfiniteTable/components/InfiniteTableHeader/InfiniteTableHeaderWrapper.tsx +185 -0
  57. package/src/components/InfiniteTable/components/InfiniteTableHeader/ResizeHandle/GroupResizeHandle.tsx +159 -0
  58. package/src/components/InfiniteTable/components/InfiniteTableHeader/ResizeHandle/ResizeHandle.css.ts +124 -0
  59. package/src/components/InfiniteTable/components/InfiniteTableHeader/ResizeHandle/columnResizer.ts +166 -0
  60. package/src/components/InfiniteTable/components/InfiniteTableHeader/ResizeHandle/index.tsx +146 -0
  61. package/src/components/InfiniteTable/components/InfiniteTableHeader/buildColumnAndGroupTree.ts +240 -0
  62. package/src/components/InfiniteTable/components/InfiniteTableHeader/header.css.ts +358 -0
  63. package/src/components/InfiniteTable/components/InfiniteTableHeader/index.tsx +1 -0
  64. package/src/components/InfiniteTable/components/InfiniteTableHeader/useColumnGroupResizeHandle.tsx +114 -0
  65. package/src/components/InfiniteTable/components/InfiniteTableHeader/useColumnResizeHandle.tsx +146 -0
  66. package/src/components/InfiniteTable/components/InfiniteTableLicenseFooter/footer.css.ts +7 -0
  67. package/src/components/InfiniteTable/components/InfiniteTableLicenseFooter/index.tsx +110 -0
  68. package/src/components/InfiniteTable/components/InfiniteTableRow/InfiniteTableCell.tsx +120 -0
  69. package/src/components/InfiniteTable/components/InfiniteTableRow/InfiniteTableCellTypes.ts +74 -0
  70. package/src/components/InfiniteTable/components/InfiniteTableRow/InfiniteTableColumnCell.tsx +457 -0
  71. package/src/components/InfiniteTable/components/InfiniteTableRow/InfiniteTableRow.tsx +107 -0
  72. package/src/components/InfiniteTable/components/InfiniteTableRow/InfiniteTableRowClassName.ts +6 -0
  73. package/src/components/InfiniteTable/components/InfiniteTableRow/InfiniteTableRowTypes.ts +32 -0
  74. package/src/components/InfiniteTable/components/InfiniteTableRow/InfiniteTableRowUnvirtualized.tsx +75 -0
  75. package/src/components/InfiniteTable/components/InfiniteTableRow/columnRendering.tsx +263 -0
  76. package/src/components/InfiniteTable/components/InfiniteTableRow/index.tsx +1 -0
  77. package/src/components/InfiniteTable/components/InfiniteTableRow/row.css.ts +74 -0
  78. package/src/components/InfiniteTable/components/InfiniteTableRow/useRowDOMProps.ts +199 -0
  79. package/src/components/InfiniteTable/components/LoadMask.css.ts +34 -0
  80. package/src/components/InfiniteTable/components/LoadMask.tsx +27 -0
  81. package/src/components/InfiniteTable/components/ScrollbarPlaceholder.tsx +56 -0
  82. package/src/components/InfiniteTable/components/cell.css.ts +192 -0
  83. package/src/components/InfiniteTable/components/icons/ArrowDown.tsx +14 -0
  84. package/src/components/InfiniteTable/components/icons/ArrowUp.tsx +14 -0
  85. package/src/components/InfiniteTable/components/icons/ExpanderIcon.css.ts +31 -0
  86. package/src/components/InfiniteTable/components/icons/ExpanderIcon.tsx +63 -0
  87. package/src/components/InfiniteTable/components/icons/Icon.tsx +18 -0
  88. package/src/components/InfiniteTable/components/icons/InfiniteTableIconClassName.ts +1 -0
  89. package/src/components/InfiniteTable/components/icons/LoadingIcon.css.ts +8 -0
  90. package/src/components/InfiniteTable/components/icons/LoadingIcon.tsx +45 -0
  91. package/src/components/InfiniteTable/components/icons/MenuIcon.tsx +50 -0
  92. package/src/components/InfiniteTable/components/icons/SortIcon.tsx +113 -0
  93. package/src/components/InfiniteTable/eventHandlers/eventHandlerTypes.ts +57 -0
  94. package/src/components/InfiniteTable/eventHandlers/index.ts +95 -0
  95. package/src/components/InfiniteTable/eventHandlers/keyboardNavigation.ts +232 -0
  96. package/src/components/InfiniteTable/eventHandlers/keyboardSelection.ts +110 -0
  97. package/src/components/InfiniteTable/eventHandlers/onCellClick.ts +94 -0
  98. package/src/components/InfiniteTable/eventHandlers/onKeyDown.ts +25 -0
  99. package/src/components/InfiniteTable/hooks/adjustColumnOrderForAllColumns.ts +70 -0
  100. package/src/components/InfiniteTable/hooks/reorderColumnsOnDrag.ts +476 -0
  101. package/src/components/InfiniteTable/hooks/useAutoSizeColumns.ts +206 -0
  102. package/src/components/InfiniteTable/hooks/useCellClassName.ts +80 -0
  103. package/src/components/InfiniteTable/hooks/useCellRendering.tsx +217 -0
  104. package/src/components/InfiniteTable/hooks/useColumnGroups.ts +71 -0
  105. package/src/components/InfiniteTable/hooks/useColumnMenu.ts +72 -0
  106. package/src/components/InfiniteTable/hooks/useColumnPointerEvents.ts +258 -0
  107. package/src/components/InfiniteTable/hooks/useColumnRowspan.ts +43 -0
  108. package/src/components/InfiniteTable/hooks/useColumnSizeFn.ts +24 -0
  109. package/src/components/InfiniteTable/hooks/useColumnsWhen.ts +582 -0
  110. package/src/components/InfiniteTable/hooks/useComputed.ts +186 -0
  111. package/src/components/InfiniteTable/hooks/useComputedVisibleColumns.ts +219 -0
  112. package/src/components/InfiniteTable/hooks/useDOMProps.ts +306 -0
  113. package/src/components/InfiniteTable/hooks/useEffectWithRaf.ts +47 -0
  114. package/src/components/InfiniteTable/hooks/useInfiniteTable.ts +10 -0
  115. package/src/components/InfiniteTable/hooks/useInfiniteTableState.ts +11 -0
  116. package/src/components/InfiniteTable/hooks/useInternalProps.ts +8 -0
  117. package/src/components/InfiniteTable/hooks/useLicense/LicenseDetails.ts +13 -0
  118. package/src/components/InfiniteTable/hooks/useLicense/crc32.ts +56 -0
  119. package/src/components/InfiniteTable/hooks/useLicense/decode.ts +147 -0
  120. package/src/components/InfiniteTable/hooks/useLicense/useLicense.ts +39 -0
  121. package/src/components/InfiniteTable/hooks/useRerenderOnKeyChange.ts +21 -0
  122. package/src/components/InfiniteTable/hooks/useRowHover.ts +18 -0
  123. package/src/components/InfiniteTable/hooks/useScrollToActiveCell.ts +48 -0
  124. package/src/components/InfiniteTable/hooks/useScrollToActiveRow.ts +44 -0
  125. package/src/components/InfiniteTable/hooks/useScrollbars.ts +51 -0
  126. package/src/components/InfiniteTable/hooks/useToggleGroupRow.ts +56 -0
  127. package/src/components/InfiniteTable/hooks/useUnpinnedRendering.tsx +165 -0
  128. package/src/components/InfiniteTable/hooks/useYourBrain.ts +55 -0
  129. package/src/components/InfiniteTable/index.tsx +352 -0
  130. package/src/components/InfiniteTable/internalProps.ts +4 -0
  131. package/src/components/InfiniteTable/state/computeColumnGroupsDepths.ts +29 -0
  132. package/src/components/InfiniteTable/state/getColumnVisibilityForHideEmptyGroupColumns.ts +127 -0
  133. package/src/components/InfiniteTable/state/getInitialState.ts +375 -0
  134. package/src/components/InfiniteTable/theme.css.ts +556 -0
  135. package/src/components/InfiniteTable/types/InfiniteTableAction.ts +6 -0
  136. package/src/components/InfiniteTable/types/InfiniteTableActionType.ts +12 -0
  137. package/src/components/InfiniteTable/types/InfiniteTableColumn.ts +478 -0
  138. package/src/components/InfiniteTable/types/InfiniteTableComputedValues.ts +43 -0
  139. package/src/components/InfiniteTable/types/InfiniteTableContextValue.ts +12 -0
  140. package/src/components/InfiniteTable/types/InfiniteTableInternalProps.ts +3 -0
  141. package/src/components/InfiniteTable/types/InfiniteTableProps.ts +511 -0
  142. package/src/components/InfiniteTable/types/InfiniteTableState.ts +195 -0
  143. package/src/components/InfiniteTable/types/Utility.ts +35 -0
  144. package/src/components/InfiniteTable/types/index.ts +96 -0
  145. package/src/components/InfiniteTable/utilities.css.ts +179 -0
  146. package/src/components/InfiniteTable/utils/MultiRowSelector.ts +111 -0
  147. package/src/components/InfiniteTable/utils/RenderHookComponentForInfinite.tsx +52 -0
  148. package/src/components/InfiniteTable/utils/adjustColumnOrderForPinning.ts +33 -0
  149. package/src/components/InfiniteTable/utils/assignFiltered.ts +29 -0
  150. package/src/components/InfiniteTable/utils/defaultGetColumContextMenuItems.tsx +108 -0
  151. package/src/components/InfiniteTable/utils/getColumnComputedType.ts +33 -0
  152. package/src/components/InfiniteTable/utils/getColumnForGroupBy.tsx +313 -0
  153. package/src/components/InfiniteTable/utils/getComputedVisibleColumns.ts +773 -0
  154. package/src/components/InfiniteTable/utils/getContextMenuForColumn.tsx +72 -0
  155. package/src/components/InfiniteTable/utils/infiniteDOMUtils.ts +189 -0
  156. package/src/components/InfiniteTable/utils/mapToObject.ts +20 -0
  157. package/src/components/InfiniteTable/utils/moveXatY.ts +25 -0
  158. package/src/components/InfiniteTable/utils/objectValuesExcept.ts +9 -0
  159. package/src/components/InfiniteTable/utils/progressiveSpeedScroller.ts +158 -0
  160. package/src/components/InfiniteTable/utils/rafFn.ts +13 -0
  161. package/src/components/InfiniteTable/utils/toCSSVarName.ts +1 -0
  162. package/src/components/InfiniteTable/utils/toMap.ts +24 -0
  163. package/src/components/Menu/Menu.tsx +750 -0
  164. package/src/components/Menu/MenuCls.css.ts +139 -0
  165. package/src/components/Menu/MenuContext.ts +10 -0
  166. package/src/components/Menu/MenuContextValue.ts +15 -0
  167. package/src/components/Menu/MenuProps.ts +98 -0
  168. package/src/components/Menu/MenuState.ts +56 -0
  169. package/src/components/Menu/MenuTriangleContext.ts +352 -0
  170. package/src/components/Menu/childrenToRuntimeItems.tsx +103 -0
  171. package/src/components/Menu/getMenuState.tsx +119 -0
  172. package/src/components/Menu/index.tsx +88 -0
  173. package/src/components/Menu/propToIdentifyMenu.ts +1 -0
  174. package/src/components/RawList/AvoidReactDiff.tsx +48 -0
  175. package/src/components/RawList/MappedItems.ts +143 -0
  176. package/src/components/RawList/ReactVirtualRenderer.tsx +307 -0
  177. package/src/components/RawList/index.tsx +42 -0
  178. package/src/components/RawList/types.ts +23 -0
  179. package/src/components/RawList/useVirtualRenderer.ts +36 -0
  180. package/src/components/RenderHookComponent.tsx +10 -0
  181. package/src/components/ResizeObserver/index.tsx +170 -0
  182. package/src/components/VirtualBrain/MatrixBrain.ts +1621 -0
  183. package/src/components/VirtualBrain/ScrollListener.ts +39 -0
  184. package/src/components/VirtualBrain/index.ts +664 -0
  185. package/src/components/VirtualList/ColumnListWithExternalScrolling.tsx +78 -0
  186. package/src/components/VirtualList/InfiniteListRootClassName.ts +1 -0
  187. package/src/components/VirtualList/RowListWithExternalScrolling.tsx +120 -0
  188. package/src/components/VirtualList/SpacePlaceholder.tsx +32 -0
  189. package/src/components/VirtualList/VirtualList.css.ts +29 -0
  190. package/src/components/VirtualList/VirtualList.tsx +141 -0
  191. package/src/components/VirtualList/VirtualRowList.tsx +34 -0
  192. package/src/components/VirtualList/types.ts +56 -0
  193. package/src/components/VirtualList/useCorrectHeightForRowElements.ts +14 -0
  194. package/src/components/VirtualScrollContainer/VirtualScrollContainer.css.ts +51 -0
  195. package/src/components/VirtualScrollContainer/getScrollableClassName.ts +30 -0
  196. package/src/components/VirtualScrollContainer/index.tsx +63 -0
  197. package/src/components/flexbox/index.ts +562 -0
  198. package/src/components/hooks/useComponentState/index.tsx +562 -0
  199. package/src/components/hooks/useComponentState/types.ts +32 -0
  200. package/src/components/hooks/useEffectOnceWithProperUnmount.ts +33 -0
  201. package/src/components/hooks/useEffectWithChanges.ts +56 -0
  202. package/src/components/hooks/useImmutableRef.ts +7 -0
  203. package/src/components/hooks/useInterceptedMap.ts +71 -0
  204. package/src/components/hooks/useLatest.tsx +8 -0
  205. package/src/components/hooks/useLazyLatest.ts +23 -0
  206. package/src/components/hooks/useMemoShallowObjectMerge.ts +16 -0
  207. package/src/components/hooks/useMounted.ts +18 -0
  208. package/src/components/hooks/useOnMount.ts +21 -0
  209. package/src/components/hooks/useOnScroll.ts +34 -0
  210. package/src/components/hooks/useOnce.ts +10 -0
  211. package/src/components/hooks/useOverlay/index.tsx +418 -0
  212. package/src/components/hooks/usePrevious.ts +8 -0
  213. package/src/components/hooks/usePropertyOld.ts +168 -0
  214. package/src/components/hooks/useRerender.ts +12 -0
  215. package/src/components/hooks/xuseProperty.ts +165 -0
  216. package/src/components/types/CellPosition.ts +4 -0
  217. package/src/components/types/NonUndefined.ts +1 -0
  218. package/src/components/types/RemoveObject.ts +22 -0
  219. package/src/components/types/Renderable.ts +3 -0
  220. package/src/components/types/ScrollPosition.ts +7 -0
  221. package/src/components/types/Setter.ts +2 -0
  222. package/src/components/types/Size.ts +6 -0
  223. package/src/components/types/SubscriptionCallback.ts +10 -0
  224. package/src/components/types/VoidFn.ts +1 -0
  225. package/src/components/utils/buildSubscriptionCallback.tsx +60 -0
  226. package/src/components/utils/clamp.ts +3 -0
  227. package/src/components/utils/debounce.ts +16 -0
  228. package/src/components/utils/decamelize.ts +11 -0
  229. package/src/components/utils/discardCallsWithEqualArg.ts +42 -0
  230. package/src/components/utils/getRenderIndexes.ts +33 -0
  231. package/src/components/utils/getScrollbarWidth.ts +31 -0
  232. package/src/components/utils/getVirtualRenderCount.ts +21 -0
  233. package/src/components/utils/isControlled.ts +12 -0
  234. package/src/components/utils/isControlledValue.ts +5 -0
  235. package/src/index.tsx +34 -0
  236. package/src/test.tsx +18 -0
  237. package/src/utils/DeepMap/index.ts +588 -0
  238. package/src/utils/DeepMap/once.ts +16 -0
  239. package/src/utils/DeepMap/sortAscending.ts +1 -0
  240. package/src/utils/DeepMap/tsconfig.deepmap.json +17 -0
  241. package/src/utils/DeepMap/xpackage.json +26 -0
  242. package/src/utils/debug.ts +40 -0
  243. package/src/utils/deepClone.ts +38 -0
  244. package/src/utils/getGlobal.ts +3 -0
  245. package/src/utils/groupAndPivot/defaultToKey.ts +3 -0
  246. package/src/utils/groupAndPivot/getGroupKeysForDataItem.ts +19 -0
  247. package/src/utils/groupAndPivot/getPivotColumnsAndColumnGroups.ts +361 -0
  248. package/src/utils/groupAndPivot/index.ts +1229 -0
  249. package/src/utils/groupAndPivot/types.ts +10 -0
  250. package/src/utils/join.ts +4 -0
  251. package/src/utils/mathIntersection.ts +21 -0
  252. package/src/utils/minified.d.ts +2 -0
  253. package/src/utils/minified.js +6 -0
  254. package/src/utils/multisort/index.ts +151 -0
  255. package/src/utils/multisort/sortTypes.ts +25 -0
  256. package/src/utils/notes-status.md +92 -0
  257. package/src/utils/pageGeometry/ConvexPoly.ts +34 -0
  258. package/src/utils/pageGeometry/Point.ts +51 -0
  259. package/src/utils/pageGeometry/PolyWithPoints.ts +52 -0
  260. package/src/utils/pageGeometry/Rectangle.ts +98 -0
  261. package/src/utils/pageGeometry/Triangle.ts +16 -0
  262. package/src/utils/pageGeometry/alignment/index.ts +221 -0
  263. package/src/utils/pageGeometry/polyContainsPoint.ts +182 -0
  264. package/src/utils/pageGeometry/types.ts +3 -0
  265. package/src/utils/proxyFnCall.ts +46 -0
  266. package/src/utils/raf.ts +13 -0
  267. package/src/utils/selectParent.ts +18 -0
  268. package/src/utils/shallowEqualObjects.ts +49 -0
  269. package/src/utils/stripVar.ts +3 -0
  270. package/src/utils/toUpperFirst.ts +5 -0
  271. package/todo.md +2 -0
  272. package/tsconfig.build.json +8 -0
  273. package/tsconfig.dev.json +7 -0
  274. package/tsconfig.json +30 -0
  275. package/tsconfig.typebundler.json +13 -0
  276. package/typedoc.json +4 -0
  277. package/README.md +0 -104
  278. package/index.css +0 -857
  279. package/index.d.ts +0 -2111
  280. package/index.esm.css +0 -857
  281. package/index.esm.js +0 -13695
  282. package/index.js +0 -13565
@@ -0,0 +1,750 @@
1
+ import * as React from 'react';
2
+ import {
3
+ HTMLProps,
4
+ useCallback,
5
+ useLayoutEffect,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
9
+
10
+ import { join } from '../../utils/join';
11
+
12
+ import { Rectangle } from '../../utils/pageGeometry/Rectangle';
13
+
14
+ import { RenderHookComponent } from '../RenderHookComponent';
15
+ import { MenuCls, MenuItemCls, MenuRowCls } from './MenuCls.css';
16
+ import type {
17
+ MenuColumn,
18
+ MenuItemObject,
19
+ MenuProps,
20
+ MenuRenderable,
21
+ MenuRuntimeItem,
22
+ MenuRuntimeItemSelectable,
23
+ } from './MenuProps';
24
+ import { useOverlay } from '../hooks/useOverlay';
25
+ import { selectParent } from '../../utils/selectParent';
26
+
27
+ import { MenuTriangleContext } from './MenuTriangleContext';
28
+
29
+ import { useMenuContext } from './MenuContext';
30
+ import { Menu } from '.';
31
+ import { MenuApi, MenuState } from './MenuState';
32
+ import { raf } from '../../utils/raf';
33
+ import { display } from '../InfiniteTable/utilities.css';
34
+ import { useMounted } from '../hooks/useMounted';
35
+
36
+ function renderSubmenuForItem(
37
+ item: MenuRuntimeItemSelectable,
38
+ config: {
39
+ api: MenuApi;
40
+ parentMenuId: string;
41
+ constrainTo: MenuState['constrainTo'];
42
+ setSubmenuApi: (api: MenuApi) => void;
43
+ setActiveItemKey: (itemKey: string | null) => void;
44
+ setKeyboardActiveItemKey: (itemKey: string | null) => void;
45
+ },
46
+ ) {
47
+ const {
48
+ parentMenuId,
49
+ setSubmenuApi,
50
+ setActiveItemKey,
51
+ setKeyboardActiveItemKey,
52
+ constrainTo,
53
+ api,
54
+ } = config;
55
+ const overlayId = `${parentMenuId}-submenu`;
56
+
57
+ let itemMenu = item.menu;
58
+ if (!itemMenu) {
59
+ return null;
60
+ }
61
+
62
+ if (typeof itemMenu === 'function') {
63
+ itemMenu = itemMenu();
64
+ }
65
+
66
+ return (
67
+ <Menu
68
+ key={overlayId}
69
+ parentMenuItemKey={item.key}
70
+ parentMenuId={parentMenuId}
71
+ constrainTo={constrainTo}
72
+ {...itemMenu}
73
+ autoFocus={false}
74
+ onShow={(state, api) => {
75
+ (itemMenu as MenuProps)?.onShow?.(state, api);
76
+ setSubmenuApi(api);
77
+ }}
78
+ onHide={(state) => {
79
+ if (state.focused) {
80
+ // the submenu was focused but now is unmounted
81
+ setActiveItemKey(null);
82
+ setKeyboardActiveItemKey(item.key);
83
+ // so make sure we focus current menu
84
+ api.focus();
85
+ }
86
+ }}
87
+ />
88
+ );
89
+ }
90
+ /**
91
+ *
92
+ * @param menuId The menu id
93
+ * @param itemKey the item key
94
+ * @returns Rectangle
95
+ */
96
+ function getMenuItemRect(menuId: string, itemKey: string) {
97
+ const cells = getMenuItemNodes(menuId, itemKey);
98
+ if (!cells.length) {
99
+ return null;
100
+ }
101
+ const first = Rectangle.from(cells[0]?.getBoundingClientRect());
102
+ const last = Rectangle.from(cells[cells.length - 1].getBoundingClientRect());
103
+
104
+ const rect = first;
105
+ rect.width = last.left + last.width - first.left;
106
+
107
+ return rect;
108
+ }
109
+
110
+ function getMenuItemNodes(menuId: string, itemKey: string): HTMLDivElement[] {
111
+ const menuNode = selectParent(
112
+ document.querySelector(
113
+ `[data-menu-id="${menuId}"] [data-menu-item-key="${itemKey}"]`,
114
+ ) as HTMLElement,
115
+ `[data-menu-id="${menuId}"]`,
116
+ );
117
+
118
+ if (!menuNode) {
119
+ return [];
120
+ }
121
+
122
+ const cells = menuNode.querySelectorAll(`[data-menu-item-key="${itemKey}"]`);
123
+
124
+ return Array.from(cells) as HTMLDivElement[];
125
+ }
126
+
127
+ function getFirstCheckBoxInsideMenuItem(menuId: string, itemKey: string) {
128
+ const cells = getMenuItemNodes(menuId, itemKey);
129
+ if (!cells.length) {
130
+ return null;
131
+ }
132
+
133
+ for (const cell of cells) {
134
+ const input = cell.querySelector(
135
+ 'input[type="checkbox"]',
136
+ ) as HTMLInputElement | null;
137
+ if (input) {
138
+ return input;
139
+ }
140
+ }
141
+ return null;
142
+ }
143
+
144
+ function useSubmenus({
145
+ onItemActivate,
146
+ }: {
147
+ onItemActivate: (item: MenuRuntimeItemSelectable | null) => void;
148
+ }) {
149
+ const { getState } = useMenuContext();
150
+ const [{ onItemEnter, onItemLeave }] = useState(() => {
151
+ return new MenuTriangleContext({
152
+ parentMenuId: getState().parentMenuId,
153
+ onItemActivate: (itemKey: string | null) => {
154
+ if (!itemKey) {
155
+ onItemActivate(null);
156
+ return;
157
+ }
158
+
159
+ const item = getState().runtimeItems.filter(
160
+ (item) => item.type === 'item' && item.key === itemKey,
161
+ )[0];
162
+
163
+ onItemActivate(item as MenuRuntimeItemSelectable);
164
+ },
165
+ itemHasSubMenu: (itemKey: string) => {
166
+ const item = getState().runtimeItems.filter(
167
+ (item) => item.type === 'item' && item.key === itemKey && item.menu,
168
+ )[0];
169
+
170
+ return !!item;
171
+ },
172
+ getMenuRectFor: (itemKey) => {
173
+ const selector = `[data-submenu-for="${itemKey}"]`;
174
+ const menuNode = document.querySelector(selector);
175
+ if (!menuNode) {
176
+ return undefined;
177
+ }
178
+ const rect = Rectangle.from(menuNode.getBoundingClientRect());
179
+
180
+ return rect;
181
+ },
182
+ }).getHandlers();
183
+ });
184
+
185
+ return {
186
+ onItemEnter: useCallback(onItemEnter, []),
187
+ onItemLeave: useCallback(onItemLeave, []),
188
+ };
189
+ }
190
+
191
+ // function useRealignRef() {
192
+ // const domRef = useRef<HTMLDivElement | null>(null);
193
+
194
+ // const onRealign = useCallback((event: Event) => {
195
+ // // console.log('realigned', event);
196
+ // }, []);
197
+
198
+ // const refCallback = useCallback((node: HTMLDivElement) => {
199
+ // if (node) {
200
+ // node.addEventListener('realign', onRealign);
201
+ // } else {
202
+ // if (domRef.current) {
203
+ // domRef.current.removeEventListener('realign', onRealign);
204
+ // }
205
+ // }
206
+ // domRef.current = node;
207
+ // }, []);
208
+
209
+ // return refCallback;
210
+ // }
211
+ export function MenuComponent(props: { domProps: HTMLProps<HTMLDivElement> }) {
212
+ const { domProps } = props;
213
+
214
+ const {
215
+ componentState: {
216
+ columns,
217
+ runtimeItems,
218
+ menuId,
219
+ activeItemKey,
220
+ keyboardActiveItemKey,
221
+ constrainTo,
222
+ destroyed,
223
+ onAction,
224
+ wrapLabels,
225
+ onShow,
226
+ onHide,
227
+ autoFocus,
228
+ parentMenuItemKey,
229
+ domRef,
230
+ },
231
+ componentActions,
232
+ getState,
233
+ } = useMenuContext();
234
+
235
+ const setKeyboardActiveItemKey = useCallback((key: string | null) => {
236
+ componentActions.keyboardActiveItemKey = key;
237
+ }, []);
238
+ const setActiveItemKey = useCallback((key: string | null) => {
239
+ componentActions.activeItemKey = key;
240
+ }, []);
241
+
242
+ const isMounted = useMounted();
243
+
244
+ const [submenuApi, setSubmenuApi] = useState<MenuApi | null>(null);
245
+
246
+ let renderedChildren: MenuRenderable = null;
247
+
248
+ // it is VERY important this is useLayoutEffect and not useEffect - otherwise the menu triangulation logic would not work
249
+ useLayoutEffect(() => {
250
+ const { runtimeItems } = getState();
251
+
252
+ const activeItem = runtimeItems.filter(
253
+ (item) => item.type === 'item' && activeItemKey === item.key,
254
+ )[0] as MenuRuntimeItemSelectable | null;
255
+
256
+ if (!activeItem || !activeItem.menu) {
257
+ setSubmenuApi(null);
258
+ clearAll();
259
+ return;
260
+ }
261
+
262
+ if (activeItem) {
263
+ const alignTo = getMenuItemRect(menuId, activeItem.key)!;
264
+
265
+ const menu = () =>
266
+ renderSubmenuForItem(activeItem, {
267
+ constrainTo,
268
+ setSubmenuApi,
269
+ setActiveItemKey,
270
+ setKeyboardActiveItemKey,
271
+ parentMenuId: menuId,
272
+ api,
273
+ });
274
+
275
+ if (alignTo) {
276
+ showOverlay(menu, {
277
+ id: `${menuId}-submenu`,
278
+ alignTo,
279
+ alignPosition: [
280
+ ['TopLeft', 'TopRight'],
281
+ ['TopRight', 'TopLeft'],
282
+ ],
283
+ constrainTo,
284
+ });
285
+ }
286
+ return;
287
+ }
288
+ }, [activeItemKey]);
289
+
290
+ const onItemClick = (
291
+ event: React.MouseEvent,
292
+ item: MenuRuntimeItemSelectable,
293
+ ) => {
294
+ setKeyboardActiveItemKey(item.key);
295
+ setActiveItemKey(item.key);
296
+
297
+ if (item.originalMenuItem.onClick) {
298
+ item.originalMenuItem.onClick(event);
299
+ }
300
+ if (item.originalMenuItem.onAction) {
301
+ item.originalMenuItem.onAction(item.key, item.originalMenuItem);
302
+ }
303
+ if (onAction) {
304
+ onAction(item.key, item.originalMenuItem);
305
+ }
306
+ };
307
+
308
+ const {
309
+ showOverlay,
310
+ portal: portalWithSubmenus,
311
+ clearAll,
312
+ } = useOverlay({
313
+ // all submenus will be displayed inside this menu
314
+ // so when the parent menu is moved, the submenus are also moved
315
+ portalContainer: false,
316
+ });
317
+
318
+ const { onItemEnter, onItemLeave } = useSubmenus({
319
+ onItemActivate: (item) => {
320
+ if (!isMounted()) {
321
+ return;
322
+ }
323
+ const key = item ? item.key : null;
324
+ setActiveItemKey(key);
325
+ },
326
+ });
327
+
328
+ renderedChildren = runtimeItems.map((runtimeItem, index) => (
329
+ <RuntimeItemRenderer
330
+ key={runtimeItem.type === 'item' ? runtimeItem.key : index}
331
+ onItemEnter={(item, event) => {
332
+ onItemEnter(item.key, event);
333
+ }}
334
+ onItemLeave={(item, event) => {
335
+ onItemLeave(item.key, event);
336
+ }}
337
+ columns={columns}
338
+ active={runtimeItem.type === 'item' && runtimeItem.key === activeItemKey}
339
+ keyboardActive={
340
+ runtimeItem.type === 'item' && runtimeItem.key === keyboardActiveItemKey
341
+ }
342
+ item={runtimeItem}
343
+ index={index}
344
+ onClick={onItemClick}
345
+ />
346
+ ));
347
+
348
+ const gridTemplateColumns = columns
349
+ .map((col) => {
350
+ return (
351
+ col.width ||
352
+ (typeof col.flex === 'number' ? `${col.flex}fr` : col.flex) ||
353
+ 'auto'
354
+ );
355
+ })
356
+ .join(' ');
357
+
358
+ const style: React.CSSProperties = {
359
+ ...domProps.style,
360
+ gridTemplateColumns,
361
+ gridTemplateRows: runtimeItems.map(() => `auto`).join(' '),
362
+ };
363
+ if (!wrapLabels) {
364
+ style.whiteSpace = 'nowrap';
365
+ }
366
+
367
+ function handleKeydown(keyboardEvent: KeyboardEvent) {
368
+ const { runtimeItems, runtimeSelectableItems } = getState();
369
+ let keyboardActiveIndex = runtimeItems.findIndex(
370
+ (runtimeItem) =>
371
+ runtimeItem.type === 'item' &&
372
+ runtimeItem.key === keyboardActiveItemKey,
373
+ );
374
+
375
+ let newKeyboardActiveItemKey = keyboardActiveItemKey;
376
+
377
+ const keyboardActiveItem = runtimeItems[
378
+ keyboardActiveIndex
379
+ ] as MenuRuntimeItemSelectable;
380
+
381
+ const validKeys = {
382
+ ArrowUp: () => {
383
+ const newActiveItem = runtimeItems
384
+ .slice(0, keyboardActiveIndex)
385
+ .filter((item) => item.type === 'item' && !item.disabled)
386
+ .pop();
387
+ if (
388
+ newActiveItem &&
389
+ newActiveItem.type === 'item' &&
390
+ newActiveItem.key
391
+ ) {
392
+ newKeyboardActiveItemKey = newActiveItem.key;
393
+ }
394
+ },
395
+ ArrowDown: () => {
396
+ const newActiveItem = runtimeItems
397
+ .slice(keyboardActiveIndex + 1)
398
+ .filter((item) => item.type === 'item' && !item.disabled)[0];
399
+
400
+ if (
401
+ newActiveItem &&
402
+ newActiveItem.type === 'item' &&
403
+ newActiveItem.key
404
+ ) {
405
+ newKeyboardActiveItemKey = newActiveItem.key;
406
+ }
407
+ },
408
+ Home: () => {
409
+ validKeys.PageUp();
410
+ },
411
+ End: () => {
412
+ validKeys.PageDown();
413
+ },
414
+ PageUp: () => {
415
+ newKeyboardActiveItemKey = runtimeSelectableItems[0].key;
416
+ },
417
+ PageDown: () => {
418
+ newKeyboardActiveItemKey =
419
+ runtimeSelectableItems[runtimeSelectableItems.length - 1].key;
420
+ },
421
+ ArrowLeft: () => {
422
+ setActiveItemKey(null);
423
+
424
+ // if we're in a submenu, we want to hide it (destroy it)
425
+ if (getState().parentMenuId) {
426
+ componentActions.destroyed = true;
427
+ }
428
+ },
429
+
430
+ ArrowRight: () => {
431
+ if (!keyboardActiveItem) {
432
+ return;
433
+ }
434
+ setActiveItemKey(keyboardActiveItem.key);
435
+
436
+ raf(() => {
437
+ if (
438
+ isMounted() &&
439
+ submenuApi &&
440
+ submenuApi.getParentMenuId() === getState().menuId
441
+ ) {
442
+ submenuApi.focus();
443
+ }
444
+ });
445
+ },
446
+ Enter: () => {
447
+ if (!keyboardActiveItem) {
448
+ return;
449
+ }
450
+ if (keyboardActiveItem.originalMenuItem?.menu) {
451
+ // we close it if it was opened
452
+ if (
453
+ keyboardActiveItem &&
454
+ keyboardActiveItem.originalMenuItem.menu &&
455
+ submenuApi
456
+ ) {
457
+ validKeys.ArrowLeft();
458
+ return;
459
+ }
460
+ // or open if was closed
461
+ if (keyboardActiveItem) {
462
+ setActiveItemKey(keyboardActiveItem.key);
463
+ }
464
+ } else {
465
+ onItemClick(
466
+ keyboardEvent as any as React.MouseEvent,
467
+ keyboardActiveItem,
468
+ );
469
+ }
470
+ },
471
+ ' ': () => {
472
+ if (!keyboardActiveItem) {
473
+ return;
474
+ }
475
+ if (!keyboardActiveItem.originalMenuItem.menu) {
476
+ onItemClick(
477
+ keyboardEvent as any as React.MouseEvent,
478
+ keyboardActiveItem,
479
+ );
480
+ }
481
+
482
+ const checkbox = getFirstCheckBoxInsideMenuItem(
483
+ menuId,
484
+ keyboardActiveItem.key,
485
+ );
486
+ if (!checkbox) {
487
+ return;
488
+ }
489
+
490
+ checkbox.click();
491
+ },
492
+ Escape: () => {
493
+ validKeys.ArrowLeft();
494
+
495
+ const state = getState();
496
+
497
+ if (!state.parentMenuId) {
498
+ // this is a root menu, so make known to the parent that
499
+ // it should possibly be hidden/destroyed on Escape
500
+ state.onHideIntent?.(state);
501
+ }
502
+ },
503
+ };
504
+
505
+ const fn = validKeys[keyboardEvent.key as keyof typeof validKeys];
506
+
507
+ if (fn) {
508
+ fn();
509
+ keyboardEvent.preventDefault();
510
+ }
511
+ if (newKeyboardActiveItemKey != keyboardActiveItemKey) {
512
+ setKeyboardActiveItemKey(newKeyboardActiveItemKey);
513
+ }
514
+ }
515
+ function onMenuBlur() {
516
+ componentActions.focused = false;
517
+ }
518
+
519
+ const onMenuFocus = useCallback(function () {
520
+ componentActions.focused = true;
521
+
522
+ // we're using a special lazy focus (we consider shouldSelectFirstItemOnFocus), as the user might be actually clicking a menu item
523
+ // directly and in this case, we would end up with a flicker
524
+ // as we would select the first item and then the corresponding clicked
525
+ // item would be selected - so we're using shouldSelectFirstItemOnFocus
526
+ // which is adjusted on mousedown/up to prevent this flicker
527
+ setTimeout(() => {
528
+ // if it's still focused
529
+ if (
530
+ shouldSelectFirstItemOnFocus.current &&
531
+ domRef.current &&
532
+ domRef.current === document.activeElement &&
533
+ getState().keyboardActiveItemKey == null
534
+ ) {
535
+ setKeyboardActiveItemKey(
536
+ getState().runtimeSelectableItems[0]?.key ?? null,
537
+ );
538
+ }
539
+ });
540
+ }, []);
541
+
542
+ const shouldSelectFirstItemOnFocus = useRef(true);
543
+
544
+ const [api] = useState(() => {
545
+ const result: MenuApi = {
546
+ focus: () => {
547
+ if (domRef.current) {
548
+ domRef.current.focus();
549
+ // for whatever reason
550
+ // when doing arrow left navigation from a third level menu
551
+ // to a second level and then to a root menu
552
+ // the onMenuFocus fn does not get called for the root menu
553
+ // so we're calling it explicitly here
554
+ onMenuFocus();
555
+ }
556
+ },
557
+ getMenuId: () => getState().menuId,
558
+ getParentMenuId: () => getState().parentMenuId,
559
+ };
560
+
561
+ return result;
562
+ });
563
+
564
+ const refCallback = useCallback((node: HTMLDivElement) => {
565
+ if (node) {
566
+ domRef.current = node;
567
+ onShow?.(getState(), api);
568
+ if (autoFocus) {
569
+ api.focus();
570
+ }
571
+ } else {
572
+ onHide?.(getState());
573
+ domRef.current = node;
574
+ }
575
+ }, []);
576
+ const result = (
577
+ <div className={display.contents}>
578
+ <div
579
+ {...domProps}
580
+ data-menu-id={menuId}
581
+ data-submenu-for={parentMenuItemKey}
582
+ tabIndex={0}
583
+ ref={refCallback}
584
+ onKeyDown={(event) => {
585
+ domProps.onKeyDown?.(event);
586
+ handleKeydown(event as any as KeyboardEvent);
587
+ }}
588
+ onMouseDown={(event) => {
589
+ shouldSelectFirstItemOnFocus.current = false;
590
+ domProps.onMouseDown?.(event);
591
+ }}
592
+ onMouseUp={(event) => {
593
+ shouldSelectFirstItemOnFocus.current = true;
594
+ domProps.onMouseUp?.(event);
595
+ }}
596
+ onFocus={onMenuFocus}
597
+ onBlur={onMenuBlur}
598
+ className={join(MenuCls, domProps.className, 'InfiniteMenu')}
599
+ style={style}
600
+ >
601
+ {renderedChildren}
602
+ </div>
603
+ {portalWithSubmenus}
604
+ </div>
605
+ );
606
+
607
+ return destroyed ? null : result;
608
+ }
609
+
610
+ function RuntimeItemRenderer(props: {
611
+ columns: MenuColumn[];
612
+ item: MenuRuntimeItem;
613
+ index: number;
614
+ active: boolean;
615
+ keyboardActive: boolean;
616
+ onItemEnter?: (item: MenuRuntimeItemSelectable, event: PointerEvent) => void;
617
+ onItemLeave?: (item: MenuRuntimeItemSelectable, event: PointerEvent) => void;
618
+ onClick: (event: React.MouseEvent, item: MenuRuntimeItemSelectable) => void;
619
+ }) {
620
+ const [pressed, setPressed] = React.useState(false);
621
+ const { columns, item, index, active, keyboardActive } = props;
622
+ const key = item.type === 'item' ? item.value.key : index;
623
+
624
+ let content = null;
625
+
626
+ if (item.type === 'decoration') {
627
+ content = <div style={item.style}>{item.value}</div>;
628
+ } else {
629
+ let spanIndex = 0;
630
+ content = columns
631
+ .map((col, i) => {
632
+ const target = item.value;
633
+ const field = col.field || col.name;
634
+ const start = i + 1;
635
+ const end = start + item.span;
636
+
637
+ if (start < spanIndex) {
638
+ return null;
639
+ }
640
+
641
+ spanIndex = end;
642
+ const style = {
643
+ ...col.style,
644
+ ...item.style,
645
+ gridColumnStart: start,
646
+ gridColumnEnd: end,
647
+ };
648
+
649
+ const domProps: React.HTMLProps<HTMLDivElement> = {
650
+ style,
651
+ //@ts-ignore
652
+ 'data-menu-column-id': `${item.parentMenuId}-col-${col.name}`,
653
+ 'data-menu-row-id': `${item.parentMenuId}-row-${index}`,
654
+ 'data-menu-item-key': `${item.key}`,
655
+ className: join(
656
+ MenuItemCls({
657
+ active,
658
+ disabled: !!item.disabled,
659
+ keyboardActive,
660
+ pressed: pressed || props.active,
661
+ }),
662
+ item.className,
663
+ ),
664
+ onPointerEnter: (event) => {
665
+ if (!item.disabled) {
666
+ props.onItemEnter?.(item, event as any as PointerEvent);
667
+ }
668
+ },
669
+ onPointerLeave: (event) => {
670
+ if (!item.disabled) {
671
+ props.onItemLeave?.(item, event as any as PointerEvent);
672
+ }
673
+ },
674
+ onMouseDown: () => {
675
+ if (!item.disabled) {
676
+ setPressed(true);
677
+ }
678
+ },
679
+ onMouseUp: () => {
680
+ if (!item.disabled) {
681
+ setPressed(false);
682
+ }
683
+ },
684
+ onClick: (event: React.MouseEvent) => {
685
+ if (!item.disabled) {
686
+ props.onClick(event, item);
687
+ }
688
+ },
689
+ };
690
+
691
+ return col.render ? (
692
+ <RenderHookComponent
693
+ key={`${key}-${field}`}
694
+ render={col.render}
695
+ renderParam={{
696
+ item: target,
697
+ column: col,
698
+ value: target[field],
699
+ domProps,
700
+ }}
701
+ />
702
+ ) : (
703
+ <div key={`${key}-${field}`} {...domProps}>
704
+ {target[field]}
705
+ </div>
706
+ );
707
+ })
708
+ .filter(Boolean);
709
+ }
710
+ return <div className={MenuRowCls}>{content}</div>;
711
+ }
712
+
713
+ function MenuItem(_props: {
714
+ key: MenuItemObject['key'];
715
+ span?: MenuItemObject['span'];
716
+ label?: MenuItemObject['label'];
717
+ children?: MenuItemObject['description'];
718
+ description?: MenuItemObject['description'];
719
+ __is_menu_item: boolean;
720
+ }) {
721
+ // this component is just for declaratively rendering menu items when used in static situations like:
722
+
723
+ /**
724
+ *
725
+ <Menu>
726
+ <MenuItem key="copy">Copy</MenuItem>
727
+ <MenuItem key="paste">Paste</MenuItem>
728
+ </Menu>
729
+ */
730
+
731
+ // should not be used in dynamic situations like:
732
+
733
+ /**
734
+ *
735
+ <Menu items={[...]} />
736
+ */
737
+
738
+ return null;
739
+ }
740
+
741
+ MenuItem.defaultProps = {
742
+ __is_menu_item: true,
743
+ };
744
+
745
+ export { MenuComponent as Menu, MenuItem };
746
+ export type {
747
+ MenuProps,
748
+ MenuItemObject,
749
+ MenuItemDefinition,
750
+ } from './MenuProps';