@infinite-table/infinite-react 0.4.12 → 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 -13689
  282. package/index.js +0 -13560
@@ -0,0 +1,562 @@
1
+ import * as React from 'react';
2
+ import {
3
+ useReducer,
4
+ createContext,
5
+ useMemo,
6
+ useEffect,
7
+ useState,
8
+ useRef,
9
+ useLayoutEffect,
10
+ } from 'react';
11
+
12
+ import { dbg } from '../../../utils/debug';
13
+
14
+ import { proxyFn } from '../../../utils/proxyFnCall';
15
+ import { toUpperFirst } from '../../../utils/toUpperFirst';
16
+
17
+ import { isControlled } from '../../utils/isControlled';
18
+ import { useEffectOnce } from '../useEffectOnceWithProperUnmount';
19
+ import { useLatest } from '../useLatest';
20
+ import { usePrevious } from '../usePrevious';
21
+ import {
22
+ ComponentInterceptedActions,
23
+ ComponentMappedCallbacks,
24
+ ComponentStateActions,
25
+ ComponentStateContext,
26
+ ComponentStateGeneratedActions,
27
+ } from './types';
28
+
29
+ export const notifyChange = (
30
+ props: any,
31
+ callbackPropName: string,
32
+ values: any[],
33
+ ) => {
34
+ const callbackProp = props[callbackPropName] as Function;
35
+
36
+ if (typeof callbackProp === 'function') {
37
+ callbackProp(...values);
38
+ }
39
+ };
40
+
41
+ const debug = dbg('rerender');
42
+
43
+ let ComponentContext: any;
44
+
45
+ export function getComponentStateContext<T>(): React.Context<T> {
46
+ if (ComponentContext as React.Context<T>) {
47
+ return ComponentContext;
48
+ }
49
+
50
+ return (ComponentContext = createContext<T>(null as any as T));
51
+ }
52
+
53
+ function getReducerGeneratedActions<T_STATE, T_PROPS>(
54
+ dispatch: React.Dispatch<any>,
55
+ getState: () => T_STATE,
56
+ getProps: () => T_PROPS,
57
+ propsToForward: ForwardPropsToStateFnResult<T_PROPS, T_STATE>,
58
+ allowedControlledPropOverrides?: Record<keyof T_PROPS, boolean>,
59
+ interceptedActions?: ComponentInterceptedActions<T_STATE>,
60
+ mappedCallbacks?: ComponentMappedCallbacks<T_STATE>,
61
+ ): ComponentStateGeneratedActions<T_STATE> {
62
+ const state = getState();
63
+ //@ts-ignore
64
+ return Object.keys(state).reduce((actions, stateKey) => {
65
+ const key = stateKey as any as keyof T_STATE;
66
+
67
+ const setter = (value: T_STATE[typeof key]) => {
68
+ const props = getProps();
69
+ const state = getState();
70
+ const currentValue = state[key];
71
+ if (currentValue === value) {
72
+ // #samevaluecheckfailswhennotflushed
73
+ // early exit, as no change detected - this works if state updates are flushed, but could fail us when state updates are batched
74
+ // as we could discard a valid update, since the last/previous value could have not been flushed yet
75
+ // eg: in DataSource.useLoadData we set actions.loading = true, but this is not written to the state right away but is batched
76
+ // so if on the same tick we do actions.loading = false, this will be discarded, as the state is still loading: false as the above/previous actions was batched and hasn't been applied
77
+ //
78
+ // so in order to avoid the above scenario, simply allow same value updates to be applied
79
+ // return;
80
+ // we skip this return, as starting with React 18 we have batched updates
81
+ // so if we return we could be discarding a valid update, since the last/previous value could not have been flushed yet
82
+ // so this current value could be the same as the old value, but different from the value that was not yet flushed
83
+ // and therefore the current value to be set could be a valid new value
84
+ }
85
+
86
+ let notifyTheChange = true;
87
+
88
+ if (interceptedActions && typeof interceptedActions[key] === 'function') {
89
+ if (interceptedActions[key]!(value, { actions, state }) === false) {
90
+ notifyTheChange = false;
91
+ }
92
+ }
93
+
94
+ // it's important that we notify with the value that we receive
95
+ // directly from the setter (see continuation below)
96
+ if (notifyTheChange) {
97
+ let callbackParams = [value];
98
+ let callbackName = `on${toUpperFirst(stateKey)}Change` as string;
99
+
100
+ if (mappedCallbacks && mappedCallbacks[key]) {
101
+ const res = mappedCallbacks[key](value, state);
102
+ callbackName = res.callbackName || callbackName;
103
+ callbackParams = res.callbackParams;
104
+ }
105
+
106
+ notifyChange(props, callbackName, callbackParams);
107
+ }
108
+
109
+ //@ts-ignore
110
+ const forwardFn = propsToForward[key];
111
+
112
+ if (typeof forwardFn === 'function') {
113
+ // and not with the modified value from the forwardFn
114
+ value = forwardFn(value);
115
+ }
116
+
117
+ const allowControlled =
118
+ !!allowedControlledPropOverrides?.[key as any as keyof T_PROPS];
119
+
120
+ if (isControlled(stateKey as keyof T_PROPS, props) && !allowControlled) {
121
+ return;
122
+ }
123
+
124
+ dispatch({
125
+ payload: {
126
+ updatedProps: null,
127
+ mappedState: {
128
+ [stateKey]: value,
129
+ },
130
+ },
131
+ });
132
+ };
133
+
134
+ Object.defineProperty(actions, stateKey, {
135
+ set: setter,
136
+ });
137
+
138
+ return actions;
139
+ }, {} as ComponentStateGeneratedActions<T_STATE>);
140
+ }
141
+
142
+ export type ForwardPropsToStateFnResult<TYPE_PROPS, TYPE_RESULT> = {
143
+ [propName in keyof TYPE_PROPS & keyof TYPE_RESULT]:
144
+ | 1
145
+ | ((value: TYPE_PROPS[propName]) => TYPE_RESULT[propName]);
146
+ };
147
+
148
+ function forwardProps<T_PROPS, T_RESULT>(
149
+ propsToForward: Partial<ForwardPropsToStateFnResult<T_PROPS, T_RESULT>>,
150
+ props: T_PROPS,
151
+ ): T_RESULT {
152
+ const mappedState = {} as T_RESULT;
153
+ for (let k in propsToForward)
154
+ if (propsToForward.hasOwnProperty(k)) {
155
+ const forwardFn = propsToForward[k as keyof typeof propsToForward];
156
+ let propValue = isControlled(k as keyof T_PROPS, props)
157
+ ? props[k as keyof T_PROPS]
158
+ : props[`default${toUpperFirst(k)}` as keyof T_PROPS];
159
+
160
+ if (typeof forwardFn === 'function') {
161
+ //@ts-ignore
162
+ propValue = forwardFn(propValue);
163
+ }
164
+ //@ts-ignore
165
+ mappedState[k as any as keyof T_RESULT] = propValue;
166
+ }
167
+
168
+ return mappedState;
169
+ }
170
+
171
+ type ComponentStateRootConfig<
172
+ T_PROPS,
173
+ COMPONENT_MAPPED_STATE,
174
+ COMPONENT_SETUP_STATE = {},
175
+ COMPONENT_DERIVED_STATE = {},
176
+ T_ACTIONS = {},
177
+ T_PARENT_STATE = {},
178
+ > = {
179
+ debugName?: string;
180
+ initSetupState?: () => COMPONENT_SETUP_STATE;
181
+
182
+ layoutEffect?: boolean;
183
+
184
+ forwardProps?: (
185
+ setupState: COMPONENT_SETUP_STATE,
186
+ ) => ForwardPropsToStateFnResult<T_PROPS, COMPONENT_MAPPED_STATE>;
187
+ allowedControlledPropOverrides?: Record<keyof T_PROPS, true>;
188
+ interceptActions?: ComponentInterceptedActions<
189
+ COMPONENT_MAPPED_STATE & COMPONENT_DERIVED_STATE & COMPONENT_SETUP_STATE
190
+ >;
191
+ mappedCallbacks?: ComponentMappedCallbacks<
192
+ COMPONENT_MAPPED_STATE & COMPONENT_DERIVED_STATE & COMPONENT_SETUP_STATE
193
+ >;
194
+ onPropChange?: (
195
+ params: {
196
+ name: keyof T_PROPS;
197
+ oldValue: any;
198
+ newValue: any;
199
+ },
200
+ props: T_PROPS,
201
+ actions: ComponentStateActions<
202
+ COMPONENT_MAPPED_STATE & COMPONENT_DERIVED_STATE & COMPONENT_SETUP_STATE
203
+ >,
204
+ ) => void;
205
+ mapPropsToState?: (params: {
206
+ props: T_PROPS;
207
+ state: COMPONENT_MAPPED_STATE &
208
+ COMPONENT_SETUP_STATE &
209
+ Partial<COMPONENT_DERIVED_STATE>;
210
+ oldState:
211
+ | null
212
+ | (COMPONENT_MAPPED_STATE &
213
+ COMPONENT_SETUP_STATE &
214
+ Partial<COMPONENT_DERIVED_STATE>);
215
+ parentState: T_PARENT_STATE | null;
216
+ }) => COMPONENT_DERIVED_STATE;
217
+ concludeReducer?: (params: {
218
+ previousState: COMPONENT_MAPPED_STATE &
219
+ COMPONENT_SETUP_STATE &
220
+ COMPONENT_DERIVED_STATE;
221
+ state: COMPONENT_MAPPED_STATE &
222
+ COMPONENT_SETUP_STATE &
223
+ COMPONENT_DERIVED_STATE;
224
+ updatedProps: Partial<T_PROPS> | null;
225
+ parentState: T_PARENT_STATE | null;
226
+ }) => COMPONENT_MAPPED_STATE &
227
+ COMPONENT_SETUP_STATE &
228
+ COMPONENT_DERIVED_STATE;
229
+ getReducerActions?: (dispatch: React.Dispatch<any>) => T_ACTIONS;
230
+
231
+ getParentState?: () => T_PARENT_STATE;
232
+
233
+ cleanup?: (
234
+ state: COMPONENT_MAPPED_STATE &
235
+ COMPONENT_SETUP_STATE &
236
+ COMPONENT_DERIVED_STATE,
237
+ ) => void;
238
+
239
+ onControlledPropertyChange?: (
240
+ name: string,
241
+ newValue: any,
242
+ oldValue: any,
243
+ ) => void | ((value: any, oldValue: any) => any);
244
+ };
245
+
246
+ export function getComponentStateRoot<
247
+ T_PROPS,
248
+ COMPONENT_MAPPED_STATE extends object,
249
+ COMPONENT_SETUP_STATE extends object = {},
250
+ COMPONENT_DERIVED_STATE extends object = {},
251
+ T_ACTIONS = {},
252
+ T_PARENT_STATE = {},
253
+ >(
254
+ config: ComponentStateRootConfig<
255
+ T_PROPS,
256
+ COMPONENT_MAPPED_STATE,
257
+ COMPONENT_SETUP_STATE,
258
+ COMPONENT_DERIVED_STATE,
259
+ T_ACTIONS,
260
+ T_PARENT_STATE
261
+ >,
262
+ ) {
263
+ /**
264
+ * since config is passed outside the cmp, we can skip it inside useMemo deps list
265
+ */
266
+ return React.memo(function ComponentStateRoot(
267
+ props: T_PROPS & { children: React.ReactNode },
268
+ ) {
269
+ const [initialSetupState] = useState<COMPONENT_SETUP_STATE>(() => {
270
+ return config.initSetupState
271
+ ? config.initSetupState()
272
+ : ({} as COMPONENT_SETUP_STATE);
273
+ });
274
+ const propsToStateSetRef = useRef<Set<string>>(new Set());
275
+ const propsToForward = useMemo<
276
+ Partial<ForwardPropsToStateFnResult<T_PROPS, COMPONENT_MAPPED_STATE>>
277
+ >(
278
+ () => (config.forwardProps ? config.forwardProps(initialSetupState) : {}),
279
+ [initialSetupState],
280
+ );
281
+
282
+ type COMPONENT_STATE = COMPONENT_MAPPED_STATE &
283
+ COMPONENT_DERIVED_STATE &
284
+ COMPONENT_SETUP_STATE;
285
+
286
+ const parentState = config.getParentState?.() ?? null;
287
+ const getParentState = useLatest(parentState);
288
+
289
+ function initStateOnce() {
290
+ // STEP 1: call setupState
291
+
292
+ let mappedState = {} as COMPONENT_MAPPED_STATE;
293
+
294
+ if (propsToForward) {
295
+ mappedState = forwardProps<T_PROPS, COMPONENT_MAPPED_STATE>(
296
+ propsToForward,
297
+ props,
298
+ );
299
+ }
300
+
301
+ const state = { ...initialSetupState, ...mappedState };
302
+
303
+ if (config.mapPropsToState) {
304
+ const { fn: mapPropsToState, propertyReads } = proxyFn(
305
+ config.mapPropsToState,
306
+ {
307
+ getProxyTargetFromArgs: (initialArg) => initialArg.props,
308
+ putProxyToArgs: (props: T_PROPS, initialArg) => {
309
+ return [{ ...initialArg, props }];
310
+ },
311
+ },
312
+ );
313
+ const stateFromProps = mapPropsToState({
314
+ props,
315
+ state,
316
+ oldState: null,
317
+ parentState,
318
+ // getState: getComponentState,
319
+ });
320
+
321
+ propsToStateSetRef.current = new Set([
322
+ ...propsToStateSetRef.current,
323
+ ...propertyReads,
324
+ ]);
325
+ return {
326
+ ...state,
327
+ ...stateFromProps,
328
+ };
329
+ }
330
+
331
+ return state as COMPONENT_MAPPED_STATE &
332
+ COMPONENT_DERIVED_STATE &
333
+ COMPONENT_SETUP_STATE;
334
+ }
335
+ const [wholeState] = useState<COMPONENT_STATE>(initStateOnce);
336
+
337
+ const getProps = useLatest(props);
338
+
339
+ const theReducer: React.Reducer<COMPONENT_STATE, any> = (
340
+ previousState: COMPONENT_STATE,
341
+ action: any,
342
+ ) => {
343
+ if (action.type === 'REPLACE_STATE') {
344
+ return action.payload;
345
+ }
346
+
347
+ const parentState = getParentState?.() ?? null;
348
+ const mappedState: Partial<COMPONENT_MAPPED_STATE> | null =
349
+ action.payload.mappedState;
350
+ const updatedProps: Partial<T_PROPS> | null =
351
+ action.payload.updatedPropsToState;
352
+
353
+ const newState: COMPONENT_STATE = Object.assign({}, previousState);
354
+
355
+ if (mappedState) {
356
+ Object.assign(newState, mappedState);
357
+ }
358
+
359
+ if (config.mapPropsToState) {
360
+ const { fn: mapPropsToState, propertyReads } = proxyFn(
361
+ config.mapPropsToState,
362
+ {
363
+ getProxyTargetFromArgs: (initialArg) => initialArg.props,
364
+ putProxyToArgs: (props: T_PROPS, initialArg) => {
365
+ return [{ ...initialArg, props }];
366
+ },
367
+ },
368
+ );
369
+
370
+ const stateFromProps = mapPropsToState({
371
+ props: getProps(),
372
+ state: newState,
373
+ oldState: previousState,
374
+ parentState,
375
+ // getState: getComponentState
376
+ });
377
+
378
+ propsToStateSetRef.current = new Set([
379
+ ...propsToStateSetRef.current,
380
+ ...propertyReads,
381
+ ]);
382
+
383
+ Object.assign(newState, stateFromProps);
384
+ }
385
+
386
+ const result = config.concludeReducer
387
+ ? config.concludeReducer({
388
+ previousState,
389
+ state: newState,
390
+ updatedProps,
391
+ parentState,
392
+ })
393
+ : newState;
394
+
395
+ return result;
396
+ };
397
+
398
+ const [state, dispatch] = useReducer<React.Reducer<COMPONENT_STATE, any>>(
399
+ theReducer,
400
+ wholeState,
401
+ );
402
+
403
+ const getComponentState = useLatest(state);
404
+
405
+ type ACTIONS_TYPE = ComponentStateActions<COMPONENT_STATE>;
406
+
407
+ const { allowedControlledPropOverrides } = config;
408
+
409
+ const actions = useMemo(() => {
410
+ const generatedActions = getReducerGeneratedActions<
411
+ COMPONENT_STATE,
412
+ T_PROPS
413
+ >(
414
+ dispatch,
415
+ getComponentState,
416
+ getProps,
417
+ propsToForward as ForwardPropsToStateFnResult<T_PROPS, COMPONENT_STATE>,
418
+ allowedControlledPropOverrides,
419
+ config.interceptActions,
420
+ config.mappedCallbacks,
421
+ );
422
+
423
+ return generatedActions;
424
+ }, [
425
+ dispatch,
426
+ propsToForward,
427
+ allowedControlledPropOverrides,
428
+ ]) as ACTIONS_TYPE;
429
+
430
+ const Context =
431
+ getComponentStateContext<
432
+ ComponentStateContext<COMPONENT_STATE, ACTIONS_TYPE>
433
+ >();
434
+
435
+ const contextValue = useMemo(
436
+ () => ({
437
+ componentState: state,
438
+ componentActions: actions,
439
+ getComponentState,
440
+ }),
441
+ [state, actions, getComponentState],
442
+ );
443
+
444
+ const prevProps = usePrevious(props);
445
+
446
+ const effectFn = config.layoutEffect ? useLayoutEffect : useEffect;
447
+ effectFn(() => {
448
+ const currentProps = props;
449
+ const newMappedState: Partial<COMPONENT_MAPPED_STATE> = {};
450
+ let newMappedStateCount = 0;
451
+
452
+ const updatedPropsToState: Partial<T_PROPS> = {};
453
+ let updatedPropsToStateCount = 0;
454
+
455
+ type UPDATED_PROPS = {
456
+ [key in keyof T_PROPS]?: {
457
+ newValue: T_PROPS[key];
458
+ oldValue: T_PROPS[key];
459
+ };
460
+ };
461
+ const rawUpdatedProps: UPDATED_PROPS = {};
462
+
463
+ for (var k in props) {
464
+ const key = k as string as keyof T_PROPS;
465
+ const oldValue = prevProps[key];
466
+ const newValue = currentProps[key];
467
+
468
+ if (key === 'children') {
469
+ continue;
470
+ }
471
+ if (oldValue === newValue) {
472
+ continue;
473
+ }
474
+ rawUpdatedProps[key] = { newValue, oldValue };
475
+
476
+ if (isControlled(key, props) || isControlled(key, prevProps)) {
477
+ if (propsToForward.hasOwnProperty(k)) {
478
+ let valueToSet = newValue;
479
+ const forwardFn = propsToForward[k as keyof typeof propsToForward];
480
+ if (typeof forwardFn === 'function') {
481
+ //@ts-ignore
482
+ valueToSet = forwardFn(newValue);
483
+ }
484
+ //@ts-ignore
485
+ if (state[key] !== valueToSet) {
486
+ //@ts-ignore
487
+ newMappedState[key] = valueToSet;
488
+ newMappedStateCount++;
489
+ }
490
+
491
+ // or even if there is not, but props from propsToStateSet have changed
492
+ } else if (propsToStateSetRef.current.has(k)) {
493
+ updatedPropsToState[key] = currentProps[key];
494
+ updatedPropsToStateCount++;
495
+ }
496
+ }
497
+ }
498
+
499
+ if (updatedPropsToStateCount > 0 || newMappedStateCount > 0) {
500
+ debug(
501
+ 'Triggered by new values for the following props',
502
+ ...[
503
+ ...Object.keys(newMappedState ?? {}),
504
+ ...Object.keys(updatedPropsToState ?? {}),
505
+ ],
506
+ );
507
+ const action = {
508
+ payload: {
509
+ mappedState: newMappedStateCount ? newMappedState : null,
510
+ updatedPropsToState: updatedPropsToStateCount
511
+ ? updatedPropsToState
512
+ : null,
513
+ },
514
+ };
515
+
516
+ // const newState = theReducer(state, action);
517
+
518
+ dispatch(action);
519
+
520
+ if (config.onPropChange) {
521
+ for (var prop in rawUpdatedProps)
522
+ if (rawUpdatedProps.hasOwnProperty(prop)) {
523
+ const { newValue, oldValue } = rawUpdatedProps[prop]!;
524
+ config.onPropChange(
525
+ { name: prop, newValue, oldValue },
526
+ props,
527
+ actions,
528
+ );
529
+ }
530
+ }
531
+ // config.onPropChange?.(
532
+ // { name: key, oldValue, newValue },
533
+
534
+ // actions as ACTIONS_TYPE,
535
+ // );
536
+ // dispatch({
537
+ // type: 'REPLACE_STATE',
538
+ // payload: newState,
539
+ // });
540
+ }
541
+ });
542
+
543
+ useEffectOnce(() => {
544
+ return () => {
545
+ config.cleanup?.(getComponentState());
546
+ };
547
+ });
548
+
549
+ return (
550
+ <Context.Provider value={contextValue}>{props.children}</Context.Provider>
551
+ );
552
+ });
553
+ }
554
+
555
+ export function useComponentState<COMPONENT_STATE>() {
556
+ type ACTIONS_TYPE = ComponentStateActions<COMPONENT_STATE>;
557
+ const Context =
558
+ getComponentStateContext<
559
+ ComponentStateContext<COMPONENT_STATE, ACTIONS_TYPE>
560
+ >();
561
+ return React.useContext(Context);
562
+ }
@@ -0,0 +1,32 @@
1
+ export type ComponentStateContext<T_STATE, T_ACTIONS> = {
2
+ getComponentState: () => T_STATE;
3
+ componentState: T_STATE;
4
+ componentActions: T_ACTIONS;
5
+ };
6
+
7
+ export type ComponentStateGeneratedActions<T_STATE> = {
8
+ [k in keyof T_STATE]: T_STATE[k] | React.SetStateAction<T_STATE[k]>;
9
+ };
10
+
11
+ export type ComponentInterceptedActions<T_STATE> = {
12
+ [k in keyof T_STATE]?: (
13
+ value: T_STATE[k],
14
+ {
15
+ actions,
16
+ state,
17
+ }: { actions: ComponentStateGeneratedActions<T_STATE>; state: T_STATE },
18
+ ) => void | boolean;
19
+ };
20
+
21
+ export type ComponentMappedCallbacks<T_STATE> = {
22
+ [k in keyof T_STATE]: (
23
+ value: T_STATE[k],
24
+ state: T_STATE,
25
+ ) => {
26
+ callbackName: string;
27
+ callbackParams: any[];
28
+ };
29
+ };
30
+
31
+ export type ComponentStateActions<T_STATE> =
32
+ ComponentStateGeneratedActions<T_STATE>;
@@ -0,0 +1,33 @@
1
+ import { EffectCallback, useCallback, useEffect, useRef } from 'react';
2
+ import { useRerender } from './useRerender';
3
+
4
+ export const useEffectOnce = (effectCallback: EffectCallback) => {
5
+ const effectFunction = useCallback<EffectCallback>(effectCallback, []);
6
+ const effectCalled = useRef(false);
7
+ const componentRendered = useRef(false);
8
+ const destroyFn = useRef<void | VoidFunction>();
9
+ const [_, rerender] = useRerender();
10
+
11
+ if (effectCalled.current) {
12
+ componentRendered.current = true;
13
+ }
14
+
15
+ useEffect(() => {
16
+ if (!effectCalled.current) {
17
+ destroyFn.current = effectFunction;
18
+ effectCalled.current = true;
19
+ }
20
+
21
+ rerender();
22
+
23
+ return () => {
24
+ if (!componentRendered.current) {
25
+ return;
26
+ }
27
+
28
+ if (destroyFn.current) {
29
+ destroyFn.current();
30
+ }
31
+ };
32
+ }, []);
33
+ };
@@ -0,0 +1,56 @@
1
+ import { EffectCallback, useEffect, useRef } from 'react';
2
+
3
+ export function useEffectWithChanges<T>(
4
+ fn: (
5
+ changes: Record<keyof T, any>,
6
+ prevValues: Record<string, any>,
7
+ ) => void | (() => void),
8
+ deps: Record<keyof T, any>,
9
+ ) {
10
+ const prevRef = useRef({});
11
+
12
+ const oldValuesRef = useRef<Record<string, any>>({});
13
+ const oldValues: Record<string, any> = oldValuesRef.current;
14
+
15
+ const changesRef = useRef<Record<keyof T, any>>({} as Record<keyof T, any>);
16
+ const changes: Record<string, any> = changesRef.current;
17
+
18
+ const useEffectDeps: any[] = [];
19
+
20
+ for (const k in deps) {
21
+ if (deps.hasOwnProperty(k)) {
22
+ if (deps[k] !== (prevRef.current as any)[k]) {
23
+ changes[k] = deps[k];
24
+ oldValues[k] = (prevRef.current as any)[k];
25
+ }
26
+ useEffectDeps.push(deps[k]);
27
+ }
28
+ }
29
+
30
+ prevRef.current = deps;
31
+
32
+ useEffect(() => {
33
+ const changes = changesRef.current;
34
+
35
+ const result = fn(changes, oldValues);
36
+
37
+ changesRef.current = {} as Record<keyof T, any>;
38
+ oldValuesRef.current = {};
39
+ return result;
40
+ }, useEffectDeps);
41
+ }
42
+
43
+ export function useEffectWithObject(
44
+ fn: EffectCallback,
45
+ deps: Record<string, any>,
46
+ ) {
47
+ const useEffectDeps: any[] = [];
48
+
49
+ for (const k in deps) {
50
+ if (deps.hasOwnProperty(k)) {
51
+ useEffectDeps.push(deps[k]);
52
+ }
53
+ }
54
+
55
+ useEffect(fn, useEffectDeps);
56
+ }
@@ -0,0 +1,7 @@
1
+ import { useRef } from 'react';
2
+
3
+ const useImmutableRef = <T>(value: T) => {
4
+ return useRef<T>(value).current;
5
+ };
6
+
7
+ export default useImmutableRef;