react-restyle-components 0.4.39 → 0.4.41

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 (243) hide show
  1. package/lib/index.js +3 -1
  2. package/lib/src/core-components/index.js +1 -1
  3. package/lib/src/core-components/src/components/Accordion/AccordionSection/Accordion.js +15 -1
  4. package/lib/src/core-components/src/components/Accordion/AccordionSection/AccordionContext.js +6 -1
  5. package/lib/src/core-components/src/components/Accordion/AccordionSection/AccordionSection.js +39 -1
  6. package/lib/src/core-components/src/components/Accordion/AccordionSection/Header.js +39 -1
  7. package/lib/src/core-components/src/components/Accordion/AccordionSection/elements.js +94 -57
  8. package/lib/src/core-components/src/components/Accordion/AccordionSection/hooks/useCurrentAccordionIndex.js +13 -1
  9. package/lib/src/core-components/src/components/Accordion/AccordionSection/index.js +3 -1
  10. package/lib/src/core-components/src/components/Accordion/AccordionSection/types.js +9 -1
  11. package/lib/src/core-components/src/components/Accordion/Collapsible/Collapsible2.component.js +249 -1
  12. package/lib/src/core-components/src/components/Action/types.js +8 -1
  13. package/lib/src/core-components/src/components/AlertBanner/AlertBanner.js +45 -1
  14. package/lib/src/core-components/src/components/AlertBanner/elements.js +120 -45
  15. package/lib/src/core-components/src/components/AlertBanner/index.js +2 -1
  16. package/lib/src/core-components/src/components/AlertBanner/types.js +10 -1
  17. package/lib/src/core-components/src/components/AutoComplete/auto-complete-filter-group-by-multiple-select-multiple-fields-display/auto-complete-filter-group-by-multiple-select-multiple-fields-display.component.js +334 -1
  18. package/lib/src/core-components/src/components/AutoComplete/auto-complete-filter-multi-select-multi-fields-display-drag-drop/auto-complete-filter-multi-select-multi-fields-display-drag-drop.component.js +229 -1
  19. package/lib/src/core-components/src/components/AutoComplete/auto-complete-filter-multi-select-selected-top-display/auto-complete-filter-multi-select-selected-top-display.component.js +174 -1
  20. package/lib/src/core-components/src/components/AutoComplete/auto-complete-filter-multiple-select-multiple-fields-display/auto-complete-filter-multiple-select-multiple-fields-display.component.js +129 -2
  21. package/lib/src/core-components/src/components/AutoComplete/auto-complete-filter-single-select/auto-complete-filter-single-select.component.js +55 -1
  22. package/lib/src/core-components/src/components/AutoComplete/auto-complete-filter-single-select-multiple-fields-display/auto-complete-filter-single-select-multiple-fields-display.component.js +308 -2
  23. package/lib/src/core-components/src/components/AutoComplete/auto-complete-group-by/auto-complete-group-by.component.js +115 -1
  24. package/lib/src/core-components/src/components/AutoComplete/autocomplete/autocomplete.js +442 -2
  25. package/lib/src/core-components/src/components/AutoComplete/index.js +8 -1
  26. package/lib/src/core-components/src/components/Avatar/Avatar.js +148 -14
  27. package/lib/src/core-components/src/components/Badge/Badge.js +25 -2
  28. package/lib/src/core-components/src/components/Badge/InnerBadge/Inline.js +25 -1
  29. package/lib/src/core-components/src/components/Badge/InnerBadge/InnerBadge.js +25 -1
  30. package/lib/src/core-components/src/components/Badge/InnerBadge/elements.js +78 -32
  31. package/lib/src/core-components/src/components/Badge/InnerBadge/index.js +1 -1
  32. package/lib/src/core-components/src/components/Badge/index.js +1 -1
  33. package/lib/src/core-components/src/components/Badge/types.js +5 -1
  34. package/lib/src/core-components/src/components/Breadcrumb/Breadcrumb.js +83 -1
  35. package/lib/src/core-components/src/components/Breadcrumb/elements.js +166 -94
  36. package/lib/src/core-components/src/components/Breadcrumb/index.js +3 -1
  37. package/lib/src/core-components/src/components/Breadcrumb/types.js +3 -1
  38. package/lib/src/core-components/src/components/Button/button.component.js +18 -1
  39. package/lib/src/core-components/src/components/Button/buttonGroup/buttonGroup.component.js +4 -1
  40. package/lib/src/core-components/src/components/Button/index.js +2 -1
  41. package/lib/src/core-components/src/components/Button/types.js +1 -1
  42. package/lib/src/core-components/src/components/Chip/Chip.js +64 -1
  43. package/lib/src/core-components/src/components/Chip/elements.js +148 -48
  44. package/lib/src/core-components/src/components/Chip/index.js +2 -1
  45. package/lib/src/core-components/src/components/Chip/types.js +4 -1
  46. package/lib/src/core-components/src/components/Divider/Divider.js +25 -1
  47. package/lib/src/core-components/src/components/Divider/elements.js +68 -31
  48. package/lib/src/core-components/src/components/Divider/index.js +2 -1
  49. package/lib/src/core-components/src/components/Divider/types.js +4 -1
  50. package/lib/src/core-components/src/components/DynamicGrid/DynamicGrid.js +147 -1
  51. package/lib/src/core-components/src/components/DynamicGrid/GridContainer.js +24 -17
  52. package/lib/src/core-components/src/components/DynamicGrid/elements.js +162 -100
  53. package/lib/src/core-components/src/components/DynamicGrid/index.js +3 -1
  54. package/lib/src/core-components/src/components/DynamicGrid/types.js +28 -1
  55. package/lib/src/core-components/src/components/DynamicGrid/utils.js +193 -1
  56. package/lib/src/core-components/src/components/FormField/FormField.js +103 -1
  57. package/lib/src/core-components/src/components/FormField/components/CheckboxGroupInput.js +37 -1
  58. package/lib/src/core-components/src/components/FormField/components/CheckboxInput.js +8 -1
  59. package/lib/src/core-components/src/components/FormField/components/CssMultilineInput.js +192 -1
  60. package/lib/src/core-components/src/components/FormField/components/DatePickerInput.js +99 -1
  61. package/lib/src/core-components/src/components/FormField/components/DropdownInput.js +70 -1
  62. package/lib/src/core-components/src/components/FormField/components/OtpInput.js +81 -1
  63. package/lib/src/core-components/src/components/FormField/components/PasswordInput.js +40 -1
  64. package/lib/src/core-components/src/components/FormField/components/PinInput.js +75 -1
  65. package/lib/src/core-components/src/components/FormField/components/RadioInput.js +54 -1
  66. package/lib/src/core-components/src/components/FormField/components/TextDropdownInput.js +94 -1
  67. package/lib/src/core-components/src/components/FormField/components/ToggleInput.js +36 -1
  68. package/lib/src/core-components/src/components/FormField/components/index.js +11 -1
  69. package/lib/src/core-components/src/components/FormField/css-properties.js +161 -1
  70. package/lib/src/core-components/src/components/FormField/index.js +2 -1
  71. package/lib/src/core-components/src/components/Icon/Icon.js +79 -1
  72. package/lib/src/core-components/src/components/Icon/index.js +2 -1
  73. package/lib/src/core-components/src/components/Icon/types.js +9 -1
  74. package/lib/src/core-components/src/components/Loader/loader.component.js +215 -2
  75. package/lib/src/core-components/src/components/Masonry/Masonry.js +73 -1
  76. package/lib/src/core-components/src/components/Masonry/elements.js +46 -29
  77. package/lib/src/core-components/src/components/Masonry/hooks.js +100 -1
  78. package/lib/src/core-components/src/components/Masonry/index.js +3 -1
  79. package/lib/src/core-components/src/components/Masonry/types.js +1 -1
  80. package/lib/src/core-components/src/components/Modal/BasicModal/modal.component.js +116 -1
  81. package/lib/src/core-components/src/components/Modal/index.js +2 -1
  82. package/lib/src/core-components/src/components/Modal/modal-confirm/modal-confirm.component.js +147 -1
  83. package/lib/src/core-components/src/components/Picker/color-picker/color-picker.component.js +12 -1
  84. package/lib/src/core-components/src/components/Picker/color-picker-modal/color-picker-modal.component.js +40 -1
  85. package/lib/src/core-components/src/components/Picker/index.js +2 -1
  86. package/lib/src/core-components/src/components/Selection/index.js +4 -1
  87. package/lib/src/core-components/src/components/Selection/multi-select/multi-select.component.js +49 -1
  88. package/lib/src/core-components/src/components/Selection/multi-select-with-field/multi-select-with-field.component.js +58 -1
  89. package/lib/src/core-components/src/components/Selection/multi-selection-dropdown/multi-selection-dropdown.component.js +43 -1
  90. package/lib/src/core-components/src/components/Selection/single-select/single-select.component.js +87 -1
  91. package/lib/src/core-components/src/components/Skeleton/Skeleton.js +51 -1
  92. package/lib/src/core-components/src/components/Skeleton/elements.js +143 -54
  93. package/lib/src/core-components/src/components/Skeleton/index.js +2 -1
  94. package/lib/src/core-components/src/components/Skeleton/types.js +4 -1
  95. package/lib/src/core-components/src/components/SpeedDial/SpeedDial.js +128 -1
  96. package/lib/src/core-components/src/components/SpeedDial/elements.js +132 -62
  97. package/lib/src/core-components/src/components/SpeedDial/index.js +2 -1
  98. package/lib/src/core-components/src/components/SpeedDial/types.js +3 -1
  99. package/lib/src/core-components/src/components/Stepper2/stepper.component.js +139 -1
  100. package/lib/src/core-components/src/components/Switch/Switch.js +26 -1
  101. package/lib/src/core-components/src/components/Switch/elements.js +103 -34
  102. package/lib/src/core-components/src/components/Switch/index.js +2 -1
  103. package/lib/src/core-components/src/components/Switch/types.js +3 -1
  104. package/lib/src/core-components/src/components/Table/Table.js +1357 -1
  105. package/lib/src/core-components/src/components/Table/columnReorder.d.ts +128 -0
  106. package/lib/src/core-components/src/components/Table/columnReorder.js +530 -0
  107. package/lib/src/core-components/src/components/Table/columnResize.d.ts +99 -0
  108. package/lib/src/core-components/src/components/Table/columnResize.js +399 -0
  109. package/lib/src/core-components/src/components/Table/elements.d.ts +9 -1
  110. package/lib/src/core-components/src/components/Table/elements.js +324 -155
  111. package/lib/src/core-components/src/components/Table/filters.js +555 -30
  112. package/lib/src/core-components/src/components/Table/hooks.js +536 -2
  113. package/lib/src/core-components/src/components/Table/index.d.ts +4 -0
  114. package/lib/src/core-components/src/components/Table/index.js +6 -1
  115. package/lib/src/core-components/src/components/Table/types.d.ts +58 -0
  116. package/lib/src/core-components/src/components/Table/types.js +1 -1
  117. package/lib/src/core-components/src/components/Tabs/tabs.component.js +22 -1
  118. package/lib/src/core-components/src/components/Tags1/Tags.component.js +118 -1
  119. package/lib/src/core-components/src/components/Tags1/types.js +20 -1
  120. package/lib/src/core-components/src/components/Timer1/timer.component.js +76 -1
  121. package/lib/src/core-components/src/components/Toast/Toast.js +50 -1
  122. package/lib/src/core-components/src/components/Toast/elements.js +122 -41
  123. package/lib/src/core-components/src/components/Toast/index.js +2 -1
  124. package/lib/src/core-components/src/components/Toast/types.js +9 -1
  125. package/lib/src/core-components/src/components/Tooltip/Tooltip.js +200 -1
  126. package/lib/src/core-components/src/components/Tooltip/elements.js +117 -55
  127. package/lib/src/core-components/src/components/Tooltip/index.js +2 -1
  128. package/lib/src/core-components/src/components/Tooltip/types.js +17 -1
  129. package/lib/src/core-components/src/components/Tooltip/utils.js +140 -1
  130. package/lib/src/core-components/src/components/TreeSelect/TreeSelect.js +303 -1
  131. package/lib/src/core-components/src/components/TreeSelect/elements.js +216 -117
  132. package/lib/src/core-components/src/components/TreeSelect/hooks.js +252 -1
  133. package/lib/src/core-components/src/components/TreeSelect/index.js +3 -1
  134. package/lib/src/core-components/src/components/TreeSelect/types.js +1 -1
  135. package/lib/src/core-components/src/components/ag-grid/AgGrid.d.ts +11 -0
  136. package/lib/src/core-components/src/components/ag-grid/AgGrid.js +733 -0
  137. package/lib/src/core-components/src/components/ag-grid/elements.d.ts +246 -0
  138. package/lib/src/core-components/src/components/ag-grid/elements.js +1156 -0
  139. package/lib/src/core-components/src/components/ag-grid/hooks.d.ts +196 -0
  140. package/lib/src/core-components/src/components/ag-grid/hooks.js +943 -0
  141. package/lib/src/core-components/src/components/ag-grid/index.d.ts +9 -0
  142. package/lib/src/core-components/src/components/ag-grid/index.js +13 -0
  143. package/lib/src/core-components/src/components/ag-grid/types.d.ts +1367 -0
  144. package/lib/src/core-components/src/components/ag-grid/types.js +6 -0
  145. package/lib/src/core-components/src/components/index.d.ts +1 -0
  146. package/lib/src/core-components/src/components/index.js +31 -1
  147. package/lib/src/core-components/src/components/pdf/pdf-images.components.js +7 -1
  148. package/lib/src/core-components/src/components/pdf/pdf-table.components.js +48 -5
  149. package/lib/src/core-components/src/components/pdf/pdf-typography.components.js +70 -1
  150. package/lib/src/core-components/src/components/pdf/pdf-wrapped-view.components.js +50 -1
  151. package/lib/src/core-components/src/core-components/Avatar.js +33 -4
  152. package/lib/src/core-components/src/core-components/CoreButton/CoreButton.js +10 -1
  153. package/lib/src/core-components/src/core-components/CoreButton/elements.js +176 -67
  154. package/lib/src/core-components/src/core-components/CoreButton/index.js +3 -1
  155. package/lib/src/core-components/src/core-components/CoreButton/types.js +6 -1
  156. package/lib/src/core-components/src/core-components/CoreButton/utils.js +12 -1
  157. package/lib/src/core-components/src/core-components/Divider/Divider.js +19 -4
  158. package/lib/src/core-components/src/core-components/Divider/index.js +1 -1
  159. package/lib/src/core-components/src/core-components/SelectionCardStrip/index.js +1 -1
  160. package/lib/src/core-components/src/core-components/SelectionCardStrip/selectionCardStrip.js +33 -10
  161. package/lib/src/core-components/src/core-components/StateLayer.js +5 -3
  162. package/lib/src/core-components/src/core-components/ToggleCore/elements.js +50 -25
  163. package/lib/src/core-components/src/core-components/ToggleCore/index.js +2 -1
  164. package/lib/src/core-components/src/core-components/ToggleCore/toggleCore.js +14 -1
  165. package/lib/src/core-components/src/core-components/atoms/Input/Input.js +22 -1
  166. package/lib/src/core-components/src/core-components/atoms/Label/Label.js +21 -1
  167. package/lib/src/core-components/src/core-components/atoms/Textarea/Textarea.js +19 -1
  168. package/lib/src/core-components/src/core-components/index.js +3 -1
  169. package/lib/src/core-components/src/helpers/constants.js +11 -1
  170. package/lib/src/core-components/src/hooks/index.js +1 -1
  171. package/lib/src/core-components/src/hooks/outside.hook.js +28 -1
  172. package/lib/src/core-components/src/index.js +12 -1
  173. package/lib/src/core-components/src/tc.global.css +16 -3
  174. package/lib/src/core-components/src/tc.module.css +2 -2
  175. package/lib/src/core-components/src/utils/abstracts/breakpoints/index.js +28 -1
  176. package/lib/src/core-components/src/utils/abstracts/colors/index.js +49 -1
  177. package/lib/src/core-components/src/utils/abstracts/index.js +5 -1
  178. package/lib/src/core-components/src/utils/abstracts/space/index.js +26 -1
  179. package/lib/src/core-components/src/utils/abstracts/theme/ThemeBoundary.js +8 -1
  180. package/lib/src/core-components/src/utils/abstracts/theme/default-themes.js +30 -1
  181. package/lib/src/core-components/src/utils/abstracts/theme/index.js +3 -1
  182. package/lib/src/core-components/src/utils/abstracts/theme/theme.js +30 -1
  183. package/lib/src/core-components/src/utils/abstracts/theme/types.js +1 -1
  184. package/lib/src/core-components/src/utils/abstracts/theme/useTheme.js +17 -1
  185. package/lib/src/core-components/src/utils/abstracts/typography/index.js +25 -1
  186. package/lib/src/core-components/src/utils/context/DefaultsProvider.js +8 -1
  187. package/lib/src/core-components/src/utils/context/InternalProvider.js +24 -1
  188. package/lib/src/core-components/src/utils/context/index.js +2 -1
  189. package/lib/src/core-components/src/utils/designTokens.js +125 -1
  190. package/lib/src/core-components/src/utils/helpers/attachSubComponents.js +23 -1
  191. package/lib/src/core-components/src/utils/helpers/flattenChildren.js +11 -1
  192. package/lib/src/core-components/src/utils/helpers/getChildByType.js +3 -1
  193. package/lib/src/core-components/src/utils/helpers/index.js +5 -1
  194. package/lib/src/core-components/src/utils/helpers/isComponentType.js +16 -1
  195. package/lib/src/core-components/src/utils/helpers/separateChildrenByType.js +12 -1
  196. package/lib/src/core-components/src/utils/hooks/index.js +18 -1
  197. package/lib/src/core-components/src/utils/hooks/useClickOutside.js +18 -1
  198. package/lib/src/core-components/src/utils/hooks/useCombinedRefs.js +17 -1
  199. package/lib/src/core-components/src/utils/hooks/useDebouncedCallback.js +12 -1
  200. package/lib/src/core-components/src/utils/hooks/useDebouncedValue.js +20 -1
  201. package/lib/src/core-components/src/utils/hooks/useDeprecation.js +40 -1
  202. package/lib/src/core-components/src/utils/hooks/useDeviceDetect.js +10 -1
  203. package/lib/src/core-components/src/utils/hooks/useDeviceForm.js +24 -1
  204. package/lib/src/core-components/src/utils/hooks/useDisableBodyScroll.js +16 -1
  205. package/lib/src/core-components/src/utils/hooks/useHoverState.js +36 -1
  206. package/lib/src/core-components/src/utils/hooks/useId.js +7 -1
  207. package/lib/src/core-components/src/utils/hooks/useIsBrowser.js +11 -1
  208. package/lib/src/core-components/src/utils/hooks/useMediaQuery.js +16 -1
  209. package/lib/src/core-components/src/utils/hooks/useOverflow.js +22 -1
  210. package/lib/src/core-components/src/utils/hooks/useSafeLayoutEffect.js +5 -1
  211. package/lib/src/core-components/src/utils/hooks/useScrollingUp.js +18 -1
  212. package/lib/src/core-components/src/utils/hooks/useTrapFocus.js +30 -1
  213. package/lib/src/core-components/src/utils/hooks/useWindowDimensions.js +23 -1
  214. package/lib/src/core-components/src/utils/index.js +9 -1
  215. package/lib/src/core-components/src/utils/stories/Wrappers.js +23 -8
  216. package/lib/src/core-components/src/utils/stories/cleanProps.js +5 -1
  217. package/lib/src/core-components/src/utils/stories/index.js +4 -1
  218. package/lib/src/core-components/src/utils/stories/sleep.js +4 -1
  219. package/lib/src/core-components/src/utils/stories/view-ports.js +50 -1
  220. package/lib/src/core-components/src/utils/styling/calcWidthOfColumns.js +5 -1
  221. package/lib/src/core-components/src/utils/styling/createGridContainer.js +12 -6
  222. package/lib/src/core-components/src/utils/styling/createTransition.js +7 -2
  223. package/lib/src/core-components/src/utils/styling/forwardProps.js +10 -1
  224. package/lib/src/core-components/src/utils/styling/index.js +5 -1
  225. package/lib/src/core-components/src/utils/styling/pxToRem.js +6 -1
  226. package/lib/src/core-components/src/utils/testing/getComputedStyle.js +3 -1
  227. package/lib/src/core-components/src/utils/testing/index.js +1 -1
  228. package/lib/src/core-components/src/utils/utility.util.js +14 -1
  229. package/lib/src/core-components/tailwind.config.js +233 -1
  230. package/lib/src/core-hooks/index.js +3 -1
  231. package/lib/src/core-hooks/src/useClickOutside/useClickOutside.hook.js +46 -1
  232. package/lib/src/core-hooks/src/useDebounce/useDebounce.hook.js +30 -1
  233. package/lib/src/core-hooks/src/usePreventEKey/usePreventEKey.hook.js +8 -1
  234. package/lib/src/core-utils/index.js +7 -1
  235. package/lib/src/core-utils/src/calculation/calculation.util.js +89 -1
  236. package/lib/src/core-utils/src/colors/color.util.js +15 -1
  237. package/lib/src/core-utils/src/convert/numberToWords/numToWords.util.js +145 -1
  238. package/lib/src/core-utils/src/convert/typography/camelCaseToTitleCase.util.js +5 -1
  239. package/lib/src/core-utils/src/form-helper/form-helper.util.js +82 -1
  240. package/lib/src/core-utils/src/index.js +7 -1
  241. package/lib/src/core-utils/src/utility/utility.util.js +12 -1
  242. package/lib/src/core-utils/src/uuid/uuid.util.js +8 -1
  243. package/package.json +1 -1
@@ -0,0 +1,943 @@
1
+ /**
2
+ * AG Grid Hooks
3
+ * Custom React hooks for AG Grid-like functionality
4
+ */
5
+ import { useState, useCallback, useMemo, useEffect, useRef } from 'react';
6
+ export function useSorting(initialSort = [], multiSort = false) {
7
+ const [sortModel, setSortModel] = useState(initialSort);
8
+ const handleSort = useCallback((colId, multiSortKey = false) => {
9
+ setSortModel((prev) => {
10
+ const existing = prev.find((s) => s.colId === colId);
11
+ let nextSort;
12
+ if (!existing) {
13
+ nextSort = 'asc';
14
+ }
15
+ else if (existing.sort === 'asc') {
16
+ nextSort = 'desc';
17
+ }
18
+ else {
19
+ nextSort = null;
20
+ }
21
+ if (multiSort && multiSortKey) {
22
+ // Multi-sort: add/update/remove from list
23
+ if (nextSort === null) {
24
+ return prev.filter((s) => s.colId !== colId);
25
+ }
26
+ if (existing) {
27
+ return prev.map((s) => s.colId === colId ? { ...s, sort: nextSort } : s);
28
+ }
29
+ return [...prev, { colId, sort: nextSort }];
30
+ }
31
+ // Single sort
32
+ if (nextSort === null) {
33
+ return [];
34
+ }
35
+ return [{ colId, sort: nextSort }];
36
+ });
37
+ }, [multiSort]);
38
+ const getSortForColumn = useCallback((colId) => {
39
+ const index = sortModel.findIndex((s) => s.colId === colId);
40
+ if (index === -1)
41
+ return { sort: null };
42
+ return {
43
+ sort: sortModel[index].sort,
44
+ sortIndex: multiSort ? index : undefined,
45
+ };
46
+ }, [sortModel, multiSort]);
47
+ const clearSort = useCallback(() => {
48
+ setSortModel([]);
49
+ }, []);
50
+ // Apply sorting to data
51
+ const sortData = useCallback((data, columns) => {
52
+ if (sortModel.length === 0)
53
+ return data;
54
+ return [...data].sort((a, b) => {
55
+ for (const { colId, sort } of sortModel) {
56
+ if (!sort)
57
+ continue;
58
+ const col = columns.find((c) => c.field === colId || c.colId === colId);
59
+ if (!col || !col.field)
60
+ continue;
61
+ const field = col.field;
62
+ const aVal = getNestedValue(a, field);
63
+ const bVal = getNestedValue(b, field);
64
+ // Custom comparator
65
+ if (col.comparator) {
66
+ const nodeA = createRowNode(a, 0);
67
+ const nodeB = createRowNode(b, 0);
68
+ const result = col.comparator(aVal, bVal, nodeA, nodeB, sort === 'desc');
69
+ if (result !== 0)
70
+ return sort === 'asc' ? result : -result;
71
+ continue;
72
+ }
73
+ // Default comparison
74
+ let comparison = 0;
75
+ if (aVal === bVal)
76
+ comparison = 0;
77
+ else if (aVal == null)
78
+ comparison = 1;
79
+ else if (bVal == null)
80
+ comparison = -1;
81
+ else if (typeof aVal === 'string' && typeof bVal === 'string') {
82
+ comparison = aVal.localeCompare(bVal);
83
+ }
84
+ else {
85
+ comparison = aVal < bVal ? -1 : 1;
86
+ }
87
+ if (comparison !== 0) {
88
+ return sort === 'asc' ? comparison : -comparison;
89
+ }
90
+ }
91
+ return 0;
92
+ });
93
+ }, [sortModel]);
94
+ return {
95
+ sortModel,
96
+ setSortModel,
97
+ handleSort,
98
+ getSortForColumn,
99
+ clearSort,
100
+ sortData,
101
+ };
102
+ }
103
+ export function useFiltering(initialFilters = {}) {
104
+ const [filterModel, setFilterModel] = useState(initialFilters);
105
+ const [quickFilterText, setQuickFilterText] = useState('');
106
+ const setColumnFilter = useCallback((field, filter) => {
107
+ setFilterModel((prev) => {
108
+ if (filter === null) {
109
+ const { [field]: _, ...rest } = prev;
110
+ return rest;
111
+ }
112
+ return { ...prev, [field]: filter };
113
+ });
114
+ }, []);
115
+ const clearFilters = useCallback(() => {
116
+ setFilterModel({});
117
+ setQuickFilterText('');
118
+ }, []);
119
+ const isFilterActive = useCallback((field) => {
120
+ return field in filterModel;
121
+ }, [filterModel]);
122
+ const hasActiveFilters = useMemo(() => {
123
+ return Object.keys(filterModel).length > 0 || quickFilterText.length > 0;
124
+ }, [filterModel, quickFilterText]);
125
+ // Apply filtering to data
126
+ const filterData = useCallback((data, columns) => {
127
+ let filtered = data;
128
+ // Apply column filters
129
+ for (const [field, filter] of Object.entries(filterModel)) {
130
+ filtered = filtered.filter((row) => {
131
+ const value = getNestedValue(row, field);
132
+ return matchesFilter(value, filter);
133
+ });
134
+ }
135
+ // Apply quick filter
136
+ if (quickFilterText) {
137
+ const search = quickFilterText.toLowerCase();
138
+ filtered = filtered.filter((row) => {
139
+ return columns.some((col) => {
140
+ if (!col.field)
141
+ return false;
142
+ const value = getNestedValue(row, col.field);
143
+ if (value == null)
144
+ return false;
145
+ return String(value).toLowerCase().includes(search);
146
+ });
147
+ });
148
+ }
149
+ return filtered;
150
+ }, [filterModel, quickFilterText]);
151
+ return {
152
+ filterModel,
153
+ setFilterModel,
154
+ quickFilterText,
155
+ setQuickFilterText,
156
+ setColumnFilter,
157
+ clearFilters,
158
+ isFilterActive,
159
+ hasActiveFilters,
160
+ filterData,
161
+ };
162
+ }
163
+ function matchesFilter(value, filter) {
164
+ if (value == null)
165
+ return false;
166
+ const strValue = String(value).toLowerCase();
167
+ switch (filter.filterType) {
168
+ case 'text':
169
+ if (filter.filter == null)
170
+ return true;
171
+ const filterStr = String(filter.filter).toLowerCase();
172
+ switch (filter.type) {
173
+ case 'equals':
174
+ return strValue === filterStr;
175
+ case 'notEqual':
176
+ return strValue !== filterStr;
177
+ case 'startsWith':
178
+ return strValue.startsWith(filterStr);
179
+ case 'endsWith':
180
+ return strValue.endsWith(filterStr);
181
+ case 'contains':
182
+ default:
183
+ return strValue.includes(filterStr);
184
+ case 'notContains':
185
+ return !strValue.includes(filterStr);
186
+ }
187
+ case 'number':
188
+ const numValue = Number(value);
189
+ const filterNum = Number(filter.filter);
190
+ if (isNaN(numValue) || isNaN(filterNum))
191
+ return false;
192
+ switch (filter.type) {
193
+ case 'equals':
194
+ return numValue === filterNum;
195
+ case 'notEqual':
196
+ return numValue !== filterNum;
197
+ case 'greaterThan':
198
+ return numValue > filterNum;
199
+ case 'greaterThanOrEqual':
200
+ return numValue >= filterNum;
201
+ case 'lessThan':
202
+ return numValue < filterNum;
203
+ case 'lessThanOrEqual':
204
+ return numValue <= filterNum;
205
+ case 'inRange':
206
+ return numValue >= filterNum && numValue <= Number(filter.filterTo);
207
+ default:
208
+ return true;
209
+ }
210
+ case 'date':
211
+ const dateValue = new Date(value);
212
+ if (isNaN(dateValue.getTime()))
213
+ return false;
214
+ if (filter.dateFrom) {
215
+ const from = new Date(filter.dateFrom);
216
+ if (filter.dateTo) {
217
+ const to = new Date(filter.dateTo);
218
+ return dateValue >= from && dateValue <= to;
219
+ }
220
+ switch (filter.type) {
221
+ case 'equals':
222
+ return dateValue.toDateString() === from.toDateString();
223
+ case 'greaterThan':
224
+ return dateValue > from;
225
+ case 'lessThan':
226
+ return dateValue < from;
227
+ default:
228
+ return dateValue >= from;
229
+ }
230
+ }
231
+ return true;
232
+ case 'set':
233
+ if (!filter.values || filter.values.length === 0)
234
+ return true;
235
+ return filter.values.includes(String(value));
236
+ default:
237
+ return true;
238
+ }
239
+ }
240
+ // ============================================================================
241
+ // Pagination Hook
242
+ // ============================================================================
243
+ export function usePagination(totalRows, initialPageSize = 10, initialPage = 0) {
244
+ const [currentPage, setCurrentPage] = useState(initialPage);
245
+ const [pageSize, setPageSize] = useState(initialPageSize);
246
+ const totalPages = useMemo(() => Math.max(1, Math.ceil(totalRows / pageSize)), [totalRows, pageSize]);
247
+ // Clamp current page when total pages changes
248
+ useEffect(() => {
249
+ if (currentPage >= totalPages) {
250
+ setCurrentPage(Math.max(0, totalPages - 1));
251
+ }
252
+ }, [totalPages, currentPage]);
253
+ const goToPage = useCallback((page) => {
254
+ const validPage = Math.max(0, Math.min(page, totalPages - 1));
255
+ setCurrentPage(validPage);
256
+ }, [totalPages]);
257
+ const goToNextPage = useCallback(() => goToPage(currentPage + 1), [currentPage, goToPage]);
258
+ const goToPreviousPage = useCallback(() => goToPage(currentPage - 1), [currentPage, goToPage]);
259
+ const goToFirstPage = useCallback(() => goToPage(0), [goToPage]);
260
+ const goToLastPage = useCallback(() => goToPage(totalPages - 1), [totalPages, goToPage]);
261
+ const changePageSize = useCallback((size) => {
262
+ setPageSize(size);
263
+ setCurrentPage(0);
264
+ }, []);
265
+ const startRow = currentPage * pageSize;
266
+ const endRow = Math.min(startRow + pageSize, totalRows);
267
+ const paginateData = useCallback((data) => {
268
+ return data.slice(startRow, startRow + pageSize);
269
+ }, [startRow, pageSize]);
270
+ return {
271
+ currentPage,
272
+ pageSize,
273
+ totalPages,
274
+ totalRows,
275
+ startRow,
276
+ endRow,
277
+ goToPage,
278
+ goToNextPage,
279
+ goToPreviousPage,
280
+ goToFirstPage,
281
+ goToLastPage,
282
+ changePageSize,
283
+ paginateData,
284
+ setCurrentPage,
285
+ setPageSize,
286
+ };
287
+ }
288
+ // ============================================================================
289
+ // Row Selection Hook
290
+ // ============================================================================
291
+ export function useRowSelection(data, getRowId, mode = 'multiple', isRowSelectable) {
292
+ const [selectedIds, setSelectedIds] = useState(new Set());
293
+ const isSelected = useCallback((id) => selectedIds.has(id), [selectedIds]);
294
+ const toggleRow = useCallback((row, index) => {
295
+ const id = getRowId(row);
296
+ const node = createRowNode(row, index);
297
+ if (isRowSelectable && !isRowSelectable(node))
298
+ return;
299
+ setSelectedIds((prev) => {
300
+ if (mode === 'single') {
301
+ return prev.has(id) ? new Set() : new Set([id]);
302
+ }
303
+ const next = new Set(prev);
304
+ if (next.has(id)) {
305
+ next.delete(id);
306
+ }
307
+ else {
308
+ next.add(id);
309
+ }
310
+ return next;
311
+ });
312
+ }, [getRowId, mode, isRowSelectable]);
313
+ const selectAll = useCallback(() => {
314
+ const ids = data
315
+ .filter((row, i) => {
316
+ if (!isRowSelectable)
317
+ return true;
318
+ return isRowSelectable(createRowNode(row, i));
319
+ })
320
+ .map(getRowId);
321
+ setSelectedIds(new Set(ids));
322
+ }, [data, getRowId, isRowSelectable]);
323
+ const deselectAll = useCallback(() => {
324
+ setSelectedIds(new Set());
325
+ }, []);
326
+ const selectRows = useCallback((rows) => {
327
+ const ids = rows.map(getRowId);
328
+ setSelectedIds(new Set(ids));
329
+ }, [getRowId]);
330
+ const isAllSelected = useMemo(() => {
331
+ if (data.length === 0)
332
+ return false;
333
+ const selectableRows = data.filter((row, i) => {
334
+ if (!isRowSelectable)
335
+ return true;
336
+ return isRowSelectable(createRowNode(row, i));
337
+ });
338
+ return selectableRows.every((row) => selectedIds.has(getRowId(row)));
339
+ }, [data, selectedIds, getRowId, isRowSelectable]);
340
+ const isIndeterminate = useMemo(() => {
341
+ if (data.length === 0)
342
+ return false;
343
+ const selectableRows = data.filter((row, i) => {
344
+ if (!isRowSelectable)
345
+ return true;
346
+ return isRowSelectable(createRowNode(row, i));
347
+ });
348
+ const selectedCount = selectableRows.filter((row) => selectedIds.has(getRowId(row))).length;
349
+ return selectedCount > 0 && selectedCount < selectableRows.length;
350
+ }, [data, selectedIds, getRowId, isRowSelectable]);
351
+ const selectedRows = useMemo(() => {
352
+ return data.filter((row) => selectedIds.has(getRowId(row)));
353
+ }, [data, selectedIds, getRowId]);
354
+ const selectedNodes = useMemo(() => {
355
+ return data
356
+ .map((row, i) => createRowNode(row, i))
357
+ .filter((node) => selectedIds.has(node.id));
358
+ }, [data, selectedIds]);
359
+ return {
360
+ selectedIds,
361
+ setSelectedIds,
362
+ isSelected,
363
+ toggleRow,
364
+ selectAll,
365
+ deselectAll,
366
+ selectRows,
367
+ isAllSelected,
368
+ isIndeterminate,
369
+ selectedRows,
370
+ selectedNodes,
371
+ };
372
+ }
373
+ // ============================================================================
374
+ // Row Expansion Hook (for groups and master/detail)
375
+ // ============================================================================
376
+ export function useRowExpansion(getRowId, defaultExpanded = 0 // -1 for all expanded
377
+ ) {
378
+ const [expandedIds, setExpandedIds] = useState(new Set());
379
+ const isExpanded = useCallback((id) => expandedIds.has(id), [expandedIds]);
380
+ const toggleExpand = useCallback((row) => {
381
+ const id = getRowId(row);
382
+ setExpandedIds((prev) => {
383
+ const next = new Set(prev);
384
+ if (next.has(id)) {
385
+ next.delete(id);
386
+ }
387
+ else {
388
+ next.add(id);
389
+ }
390
+ return next;
391
+ });
392
+ }, [getRowId]);
393
+ const expandAll = useCallback((rows) => {
394
+ const ids = rows.map(getRowId);
395
+ setExpandedIds(new Set(ids));
396
+ }, [getRowId]);
397
+ const collapseAll = useCallback(() => {
398
+ setExpandedIds(new Set());
399
+ }, []);
400
+ const setExpanded = useCallback((id, expanded) => {
401
+ setExpandedIds((prev) => {
402
+ const next = new Set(prev);
403
+ if (expanded) {
404
+ next.add(id);
405
+ }
406
+ else {
407
+ next.delete(id);
408
+ }
409
+ return next;
410
+ });
411
+ }, []);
412
+ return {
413
+ expandedIds,
414
+ setExpandedIds,
415
+ isExpanded,
416
+ toggleExpand,
417
+ expandAll,
418
+ collapseAll,
419
+ setExpanded,
420
+ };
421
+ }
422
+ // ============================================================================
423
+ // Column State Hook
424
+ // ============================================================================
425
+ export function useColumnState(columns, persistKey) {
426
+ const [columnState, setColumnState] = useState(() => {
427
+ // Load from localStorage if available
428
+ if (persistKey && typeof window !== 'undefined') {
429
+ const saved = localStorage.getItem(`ag-grid-columns-${persistKey}`);
430
+ if (saved) {
431
+ try {
432
+ return JSON.parse(saved);
433
+ }
434
+ catch (e) {
435
+ // Ignore
436
+ }
437
+ }
438
+ }
439
+ // Initialize from column definitions
440
+ return columns.map((col) => ({
441
+ colId: col.colId || col.field || '',
442
+ hide: col.hide ?? col.initialHide ?? false,
443
+ width: col.width ?? col.initialWidth,
444
+ pinned: col.pinned ?? col.initialPinned ?? null,
445
+ sort: col.sort ?? null,
446
+ }));
447
+ });
448
+ // Persist to localStorage
449
+ useEffect(() => {
450
+ if (persistKey && typeof window !== 'undefined') {
451
+ localStorage.setItem(`ag-grid-columns-${persistKey}`, JSON.stringify(columnState));
452
+ }
453
+ }, [columnState, persistKey]);
454
+ const getColumnState = useCallback((colId) => {
455
+ return columnState.find((s) => s.colId === colId);
456
+ }, [columnState]);
457
+ // Helper to extract colId from string or Column object
458
+ const getColId = (colIdOrColumn) => {
459
+ if (typeof colIdOrColumn === 'string')
460
+ return colIdOrColumn;
461
+ return colIdOrColumn.colId;
462
+ };
463
+ const setColumnVisible = useCallback((colIdOrColumn, visible) => {
464
+ const colId = getColId(colIdOrColumn);
465
+ setColumnState((prev) => prev.map((s) => (s.colId === colId ? { ...s, hide: !visible } : s)));
466
+ }, []);
467
+ const setColumnWidth = useCallback((colIdOrColumn, width) => {
468
+ const colId = getColId(colIdOrColumn);
469
+ setColumnState((prev) => prev.map((s) => (s.colId === colId ? { ...s, width } : s)));
470
+ }, []);
471
+ const setColumnPinned = useCallback((colIdOrColumn, pinned) => {
472
+ const colId = getColId(colIdOrColumn);
473
+ setColumnState((prev) => prev.map((s) => (s.colId === colId ? { ...s, pinned } : s)));
474
+ }, []);
475
+ const moveColumn = useCallback((colIdOrColumn, toIndex) => {
476
+ const colId = getColId(colIdOrColumn);
477
+ setColumnState((prev) => {
478
+ const fromIndex = prev.findIndex((s) => s.colId === colId);
479
+ if (fromIndex === -1 || fromIndex === toIndex)
480
+ return prev;
481
+ const next = [...prev];
482
+ const [removed] = next.splice(fromIndex, 1);
483
+ next.splice(toIndex, 0, removed);
484
+ return next;
485
+ });
486
+ }, []);
487
+ const resetColumnState = useCallback(() => {
488
+ setColumnState(columns.map((col) => ({
489
+ colId: col.colId || col.field || '',
490
+ hide: col.hide ?? col.initialHide ?? false,
491
+ width: col.width ?? col.initialWidth,
492
+ pinned: col.pinned ?? col.initialPinned ?? null,
493
+ sort: col.sort ?? null,
494
+ })));
495
+ }, [columns]);
496
+ // Get processed columns with state applied
497
+ const processedColumns = useMemo(() => {
498
+ return columnState
499
+ .map((state) => {
500
+ const col = columns.find((c) => (c.colId || c.field) === state.colId);
501
+ if (!col)
502
+ return null;
503
+ return {
504
+ ...col,
505
+ hide: state.hide,
506
+ width: state.width,
507
+ pinned: state.pinned,
508
+ sort: state.sort,
509
+ };
510
+ })
511
+ .filter(Boolean);
512
+ }, [columns, columnState]);
513
+ const visibleColumns = useMemo(() => {
514
+ return processedColumns.filter((col) => !col.hide);
515
+ }, [processedColumns]);
516
+ return {
517
+ columnState,
518
+ setColumnState,
519
+ getColumnState,
520
+ setColumnVisible,
521
+ setColumnWidth,
522
+ setColumnPinned,
523
+ moveColumn,
524
+ resetColumnState,
525
+ processedColumns,
526
+ visibleColumns,
527
+ };
528
+ }
529
+ export function useCellEditing(onCellValueChanged) {
530
+ const [editingCell, setEditingCell] = useState(null);
531
+ const editHistory = useRef([]);
532
+ const historyIndex = useRef(-1);
533
+ const startEditing = useCallback((rowId, colId, rowIndex, value) => {
534
+ setEditingCell({ rowId, colId, rowIndex, value });
535
+ }, []);
536
+ const stopEditing = useCallback((cancel = false, newValue) => {
537
+ if (!editingCell)
538
+ return;
539
+ if (!cancel && newValue !== undefined && newValue !== editingCell.value) {
540
+ // Record in history for undo/redo
541
+ editHistory.current = editHistory.current.slice(0, historyIndex.current + 1);
542
+ editHistory.current.push({
543
+ data: {},
544
+ field: editingCell.colId,
545
+ oldValue: editingCell.value,
546
+ newValue,
547
+ });
548
+ historyIndex.current = editHistory.current.length - 1;
549
+ }
550
+ setEditingCell(null);
551
+ }, [editingCell]);
552
+ const isEditing = useCallback((rowId, colId) => {
553
+ return editingCell?.rowId === rowId && editingCell?.colId === colId;
554
+ }, [editingCell]);
555
+ const undo = useCallback(() => {
556
+ if (historyIndex.current < 0)
557
+ return null;
558
+ const entry = editHistory.current[historyIndex.current];
559
+ historyIndex.current--;
560
+ return entry;
561
+ }, []);
562
+ const redo = useCallback(() => {
563
+ if (historyIndex.current >= editHistory.current.length - 1)
564
+ return null;
565
+ historyIndex.current++;
566
+ return editHistory.current[historyIndex.current];
567
+ }, []);
568
+ const canUndo = historyIndex.current >= 0;
569
+ const canRedo = historyIndex.current < editHistory.current.length - 1;
570
+ return {
571
+ editingCell,
572
+ startEditing,
573
+ stopEditing,
574
+ isEditing,
575
+ undo,
576
+ redo,
577
+ canUndo,
578
+ canRedo,
579
+ };
580
+ }
581
+ export function useRowGrouping(data, groupByFields, aggregations) {
582
+ const [expandedGroups, setExpandedGroups] = useState(new Set());
583
+ const groupData = useCallback((items, fields, level = 0) => {
584
+ if (fields.length === 0 || items.length === 0)
585
+ return items;
586
+ const [field, ...remainingFields] = fields;
587
+ const groups = new Map();
588
+ items.forEach((item) => {
589
+ const value = getNestedValue(item, field);
590
+ const existing = groups.get(value) || [];
591
+ existing.push(item);
592
+ groups.set(value, existing);
593
+ });
594
+ return Array.from(groups.entries()).map(([value, groupItems]) => {
595
+ const groupKey = `${field}:${value}:${level}`;
596
+ const childrenData = groupData(groupItems, remainingFields, level + 1);
597
+ // Calculate aggregations
598
+ const aggs = {};
599
+ if (aggregations) {
600
+ for (const [aggField, aggFn] of Object.entries(aggregations)) {
601
+ const values = groupItems.map((item) => getNestedValue(item, aggField));
602
+ aggs[aggField] = aggFn(values);
603
+ }
604
+ }
605
+ return {
606
+ isGroup: true,
607
+ groupValue: value,
608
+ groupField: field,
609
+ children: childrenData,
610
+ level,
611
+ expanded: expandedGroups.has(groupKey),
612
+ aggregations: aggs,
613
+ };
614
+ });
615
+ }, [expandedGroups, aggregations]);
616
+ const groupedData = useMemo(() => {
617
+ return groupData(data, groupByFields);
618
+ }, [data, groupByFields, groupData]);
619
+ const toggleGroup = useCallback((field, value, level) => {
620
+ const key = `${field}:${value}:${level}`;
621
+ setExpandedGroups((prev) => {
622
+ const next = new Set(prev);
623
+ if (next.has(key)) {
624
+ next.delete(key);
625
+ }
626
+ else {
627
+ next.add(key);
628
+ }
629
+ return next;
630
+ });
631
+ }, []);
632
+ const expandAllGroups = useCallback(() => {
633
+ const collectKeys = (items) => {
634
+ const keys = [];
635
+ items.forEach((item) => {
636
+ if ('isGroup' in item && item.isGroup) {
637
+ keys.push(`${item.groupField}:${item.groupValue}:${item.level}`);
638
+ keys.push(...collectKeys(item.children));
639
+ }
640
+ });
641
+ return keys;
642
+ };
643
+ setExpandedGroups(new Set(collectKeys(groupedData)));
644
+ }, [groupedData]);
645
+ const collapseAllGroups = useCallback(() => {
646
+ setExpandedGroups(new Set());
647
+ }, []);
648
+ // Flatten grouped data for rendering
649
+ const flattenedData = useMemo(() => {
650
+ const flatten = (items) => {
651
+ const result = [];
652
+ items.forEach((item) => {
653
+ result.push(item);
654
+ if ('isGroup' in item && item.isGroup && item.expanded) {
655
+ result.push(...flatten(item.children));
656
+ }
657
+ });
658
+ return result;
659
+ };
660
+ return flatten(groupedData);
661
+ }, [groupedData]);
662
+ return {
663
+ groupedData,
664
+ flattenedData,
665
+ expandedGroups,
666
+ toggleGroup,
667
+ expandAllGroups,
668
+ collapseAllGroups,
669
+ };
670
+ }
671
+ // ============================================================================
672
+ // Virtual Scrolling Hook
673
+ // ============================================================================
674
+ export function useVirtualScrolling(data, rowHeight, containerHeight, overscan = 5) {
675
+ const [scrollTop, setScrollTop] = useState(0);
676
+ const totalHeight = data.length * rowHeight;
677
+ const visibleRange = useMemo(() => {
678
+ const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
679
+ const endIndex = Math.min(data.length, Math.ceil((scrollTop + containerHeight) / rowHeight) + overscan);
680
+ return { startIndex, endIndex };
681
+ }, [scrollTop, rowHeight, containerHeight, data.length, overscan]);
682
+ const visibleData = useMemo(() => {
683
+ return data.slice(visibleRange.startIndex, visibleRange.endIndex);
684
+ }, [data, visibleRange]);
685
+ const offsetY = visibleRange.startIndex * rowHeight;
686
+ const handleScroll = useCallback((e) => {
687
+ setScrollTop(e.currentTarget.scrollTop);
688
+ }, []);
689
+ return {
690
+ totalHeight,
691
+ visibleData,
692
+ visibleRange,
693
+ offsetY,
694
+ handleScroll,
695
+ scrollTop,
696
+ };
697
+ }
698
+ // ============================================================================
699
+ // Clipboard Hook
700
+ // ============================================================================
701
+ export function useClipboard() {
702
+ const copyToClipboard = useCallback(async (data, columns, includeHeaders = true) => {
703
+ const rows = [];
704
+ if (includeHeaders) {
705
+ const header = columns
706
+ .filter((col) => !col.hide)
707
+ .map((col) => col.headerName || col.field || '')
708
+ .join('\t');
709
+ rows.push(header);
710
+ }
711
+ data.forEach((row) => {
712
+ const values = columns
713
+ .filter((col) => !col.hide)
714
+ .map((col) => {
715
+ if (!col.field)
716
+ return '';
717
+ const value = getNestedValue(row, col.field);
718
+ return value == null ? '' : String(value);
719
+ })
720
+ .join('\t');
721
+ rows.push(values);
722
+ });
723
+ const text = rows.join('\n');
724
+ await navigator.clipboard.writeText(text);
725
+ }, []);
726
+ const pasteFromClipboard = useCallback(async () => {
727
+ const text = await navigator.clipboard.readText();
728
+ return text.split('\n').map((row) => row.split('\t'));
729
+ }, []);
730
+ return {
731
+ copyToClipboard,
732
+ pasteFromClipboard,
733
+ };
734
+ }
735
+ // ============================================================================
736
+ // Responsive Hook
737
+ // ============================================================================
738
+ export function useResponsive(breakpoint = 768) {
739
+ const [isMobile, setIsMobile] = useState(false);
740
+ useEffect(() => {
741
+ const checkMobile = () => {
742
+ setIsMobile(window.innerWidth < breakpoint);
743
+ };
744
+ checkMobile();
745
+ window.addEventListener('resize', checkMobile);
746
+ return () => window.removeEventListener('resize', checkMobile);
747
+ }, [breakpoint]);
748
+ return { isMobile };
749
+ }
750
+ export function useContextMenu() {
751
+ const [contextMenu, setContextMenu] = useState({
752
+ visible: false,
753
+ x: 0,
754
+ y: 0,
755
+ });
756
+ const showContextMenu = useCallback((x, y, data) => {
757
+ setContextMenu({ visible: true, x, y, data });
758
+ }, []);
759
+ const hideContextMenu = useCallback(() => {
760
+ setContextMenu((prev) => ({ ...prev, visible: false }));
761
+ }, []);
762
+ // Close on click outside
763
+ useEffect(() => {
764
+ if (!contextMenu.visible)
765
+ return;
766
+ const handleClick = () => hideContextMenu();
767
+ const handleEscape = (e) => {
768
+ if (e.key === 'Escape')
769
+ hideContextMenu();
770
+ };
771
+ document.addEventListener('click', handleClick);
772
+ document.addEventListener('keydown', handleEscape);
773
+ return () => {
774
+ document.removeEventListener('click', handleClick);
775
+ document.removeEventListener('keydown', handleEscape);
776
+ };
777
+ }, [contextMenu.visible, hideContextMenu]);
778
+ return {
779
+ contextMenu,
780
+ showContextMenu,
781
+ hideContextMenu,
782
+ };
783
+ }
784
+ // ============================================================================
785
+ // Keyboard Navigation Hook
786
+ // ============================================================================
787
+ export function useKeyboardNavigation(data, columns, onCellSelect, onEnter) {
788
+ const [focusedCell, setFocusedCell] = useState(null);
789
+ const handleKeyDown = useCallback((e) => {
790
+ if (!focusedCell)
791
+ return;
792
+ const { rowIndex, colIndex } = focusedCell;
793
+ const visibleCols = columns.filter((col) => !col.hide);
794
+ switch (e.key) {
795
+ case 'ArrowUp':
796
+ e.preventDefault();
797
+ if (rowIndex > 0) {
798
+ setFocusedCell({ rowIndex: rowIndex - 1, colIndex });
799
+ }
800
+ break;
801
+ case 'ArrowDown':
802
+ e.preventDefault();
803
+ if (rowIndex < data.length - 1) {
804
+ setFocusedCell({ rowIndex: rowIndex + 1, colIndex });
805
+ }
806
+ break;
807
+ case 'ArrowLeft':
808
+ e.preventDefault();
809
+ if (colIndex > 0) {
810
+ setFocusedCell({ rowIndex, colIndex: colIndex - 1 });
811
+ }
812
+ break;
813
+ case 'ArrowRight':
814
+ e.preventDefault();
815
+ if (colIndex < visibleCols.length - 1) {
816
+ setFocusedCell({ rowIndex, colIndex: colIndex + 1 });
817
+ }
818
+ break;
819
+ case 'Enter':
820
+ e.preventDefault();
821
+ const col = visibleCols[colIndex];
822
+ if (col?.field) {
823
+ onEnter?.(rowIndex, col.field);
824
+ }
825
+ break;
826
+ case 'Tab':
827
+ if (e.shiftKey) {
828
+ if (colIndex > 0) {
829
+ e.preventDefault();
830
+ setFocusedCell({ rowIndex, colIndex: colIndex - 1 });
831
+ }
832
+ else if (rowIndex > 0) {
833
+ e.preventDefault();
834
+ setFocusedCell({
835
+ rowIndex: rowIndex - 1,
836
+ colIndex: visibleCols.length - 1,
837
+ });
838
+ }
839
+ }
840
+ else {
841
+ if (colIndex < visibleCols.length - 1) {
842
+ e.preventDefault();
843
+ setFocusedCell({ rowIndex, colIndex: colIndex + 1 });
844
+ }
845
+ else if (rowIndex < data.length - 1) {
846
+ e.preventDefault();
847
+ setFocusedCell({ rowIndex: rowIndex + 1, colIndex: 0 });
848
+ }
849
+ }
850
+ break;
851
+ }
852
+ }, [focusedCell, data.length, columns, onEnter]);
853
+ useEffect(() => {
854
+ if (focusedCell) {
855
+ const visibleCols = columns.filter((col) => !col.hide);
856
+ const col = visibleCols[focusedCell.colIndex];
857
+ if (col?.field) {
858
+ onCellSelect?.(focusedCell.rowIndex, col.field);
859
+ }
860
+ }
861
+ }, [focusedCell, columns, onCellSelect]);
862
+ return {
863
+ focusedCell,
864
+ setFocusedCell,
865
+ handleKeyDown,
866
+ };
867
+ }
868
+ // ============================================================================
869
+ // Utility Functions
870
+ // ============================================================================
871
+ export function getNestedValue(obj, path) {
872
+ return path.split('.').reduce((acc, part) => acc?.[part], obj);
873
+ }
874
+ export function setNestedValue(obj, path, value) {
875
+ const parts = path.split('.');
876
+ const result = { ...obj };
877
+ let current = result;
878
+ for (let i = 0; i < parts.length - 1; i++) {
879
+ current[parts[i]] = { ...current[parts[i]] };
880
+ current = current[parts[i]];
881
+ }
882
+ current[parts[parts.length - 1]] = value;
883
+ return result;
884
+ }
885
+ export function createRowNode(data, index, id) {
886
+ const rowId = id || String(index);
887
+ return {
888
+ id: rowId,
889
+ data,
890
+ rowIndex: index,
891
+ isSelected: false,
892
+ expanded: false,
893
+ level: 0,
894
+ setData: () => { },
895
+ setSelected: () => { },
896
+ isSelectable: () => true,
897
+ setExpanded: () => { },
898
+ getRowId: () => rowId,
899
+ };
900
+ }
901
+ // Aggregation helper functions
902
+ export const aggregations = {
903
+ sum: (values) => values.reduce((a, b) => (a || 0) + (b || 0), 0),
904
+ avg: (values) => {
905
+ const validValues = values.filter((v) => v != null && !isNaN(v));
906
+ return validValues.length
907
+ ? validValues.reduce((a, b) => a + b, 0) / validValues.length
908
+ : 0;
909
+ },
910
+ min: (values) => Math.min(...values.filter((v) => v != null && !isNaN(v))),
911
+ max: (values) => Math.max(...values.filter((v) => v != null && !isNaN(v))),
912
+ count: (values) => values.length,
913
+ first: (values) => values[0],
914
+ last: (values) => values[values.length - 1],
915
+ };
916
+ // Export to CSV
917
+ export function exportToCsv(data, columns, filename = 'export.csv') {
918
+ const exportCols = columns.filter((col) => !col.suppressCsvExport && !col.hide);
919
+ const header = exportCols
920
+ .map((col) => `"${col.headerName || col.field || ''}"`)
921
+ .join(',');
922
+ const rows = data.map((row) => {
923
+ return exportCols
924
+ .map((col) => {
925
+ if (!col.field)
926
+ return '""';
927
+ const value = getNestedValue(row, col.field);
928
+ if (value == null)
929
+ return '""';
930
+ const str = String(value).replace(/"/g, '""');
931
+ return `"${str}"`;
932
+ })
933
+ .join(',');
934
+ });
935
+ const csv = [header, ...rows].join('\n');
936
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
937
+ const url = URL.createObjectURL(blob);
938
+ const link = document.createElement('a');
939
+ link.href = url;
940
+ link.download = filename;
941
+ link.click();
942
+ URL.revokeObjectURL(url);
943
+ }