@controleonline/ui-default 1.0.270 → 1.0.273

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 (100) hide show
  1. package/.github/workflows/integration-source-gate.yml +68 -0
  2. package/.github/workflows/manager-worker.yml +19 -0
  3. package/.github/workflows/publish-npm.yml +91 -0
  4. package/AGENTS.md +46 -50
  5. package/docs/technical/Contrato-de-colunas.md +67 -0
  6. package/docs/technical/Default-Components.md +76 -0
  7. package/docs/technical/DefaultForm.md +80 -0
  8. package/docs/technical/DefaultInput-e-DefaultSelect.md +73 -0
  9. package/docs/technical/DefaultTable.md +272 -0
  10. package/docs/wiki.md +1 -0
  11. package/package.json +4 -3
  12. package/scripts/validate-wiki-pointer.cjs +13 -0
  13. package/src/react/components/address/DefaultAddress.js +446 -0
  14. package/src/react/components/address/DefaultAddress.styles.js +133 -0
  15. package/src/react/components/address/DefaultAddressCoordFields.js +59 -0
  16. package/src/react/components/address/DefaultAddressMapPane.js +55 -0
  17. package/src/react/components/address/LatLonReadonlyFields.js +51 -0
  18. package/src/react/components/address/defaultAddressHelpers.js +163 -0
  19. package/src/react/components/address/defaultAddressStyles.js +148 -0
  20. package/src/react/components/address/defaultAddressUtils.js +129 -0
  21. package/src/react/components/extra-data/DefaultExtraData.js +152 -0
  22. package/src/react/components/extra-data/DefaultExtraData.styles.js +61 -0
  23. package/src/react/components/files/DefaultFile.js +231 -46
  24. package/src/react/components/files/DefaultFileColumn.js +123 -0
  25. package/src/react/components/files/DefaultFileColumn.styles.js +52 -0
  26. package/src/react/components/files/DefaultFileColumn.utils.js +104 -0
  27. package/src/react/components/filters/CompactFilterSelector.js +51 -35
  28. package/src/react/components/filters/DefaultColumnFilter.js +60 -48
  29. package/src/react/components/filters/DefaultExternalFilters.js +324 -265
  30. package/src/react/components/filters/filterValue.js +65 -0
  31. package/src/react/components/form/DefaultForm.js +99 -79
  32. package/src/react/components/help/DefaultTooltip.js +48 -12
  33. package/src/react/components/help/DefaultTooltip.styles.js +20 -0
  34. package/src/react/components/inputs/DefaultColorInput.js +153 -0
  35. package/src/react/components/inputs/DefaultFeatherIconPicker.js +146 -0
  36. package/src/react/components/inputs/DefaultFeatherIconPicker.styles.js +113 -0
  37. package/src/react/components/inputs/DefaultInput.js +296 -141
  38. package/src/react/components/inputs/DefaultInput.styles.js +26 -0
  39. package/src/react/components/inputs/DefaultSelect.js +134 -123
  40. package/src/react/components/inputs/defaultInputUtils.js +262 -213
  41. package/src/react/components/map/DefaultMap.native.js +73 -73
  42. package/src/react/components/map/DefaultMap.shared.js +911 -762
  43. package/src/react/components/map/DefaultMap.web.js +124 -68
  44. package/src/react/components/table/DefaultColumnMenu.js +22 -3
  45. package/src/react/components/table/DefaultDebug.js +44 -37
  46. package/src/react/components/table/DefaultSearchModal.js +9 -1
  47. package/src/react/components/table/DefaultTable.grouping.js +98 -0
  48. package/src/react/components/table/DefaultTable.js +217 -111
  49. package/src/react/components/table/DefaultTable.styles.js +369 -310
  50. package/src/react/components/table/DefaultTable.utils.js +38 -4
  51. package/src/react/components/table/DefaultTableBody.js +13 -8
  52. package/src/react/components/table/DefaultTableCards.js +20 -1
  53. package/src/react/components/table/DefaultTableControls.js +180 -205
  54. package/src/react/components/table/DefaultTableImportModal.js +108 -0
  55. package/src/react/components/table/DefaultTableInput.js +1 -0
  56. package/src/react/components/table/DefaultTableRowActions.js +1 -1
  57. package/src/react/components/table/DefaultTableRows.js +357 -325
  58. package/src/react/components/table/DefaultTableSearch.js +28 -5
  59. package/src/react/components/table/DefaultTableToolbar.js +92 -3
  60. package/src/react/components/table/useDefaultTablePagination.js +56 -7
  61. package/src/react/components/table/useDefaultTableStoreSync.js +164 -0
  62. package/src/react/components/upload/DefaultUpload.js +406 -0
  63. package/src/react/components/upload/DefaultUpload.styles.js +153 -0
  64. package/src/react/components/upload/DefaultUploadAttachmentsList.js +146 -0
  65. package/src/react/components/upload/DefaultUploadManagerModal.js +247 -0
  66. package/src/react/components/upload/defaultUploadHelpers.js +108 -0
  67. package/src/react/components/upload/defaultUploadLibrary.js +248 -0
  68. package/src/react/components/upload/fileUpload.js +93 -0
  69. package/src/react/hooks/usePostalCodeLookup.js +159 -0
  70. package/src/react/index.js +6 -0
  71. package/src/react/services/addressFormUtils.js +262 -0
  72. package/src/react/services/addressGeo.js +230 -0
  73. package/src/react/utils/tableVisibleColumnsPreferences.js +113 -15
  74. package/src/tests/react/components/address/defaultAddressHelpers.test.js +104 -0
  75. package/src/tests/react/components/files/DefaultFile.test.js +22 -6
  76. package/src/tests/react/components/files/DefaultFileColumn.company.test.js +24 -0
  77. package/src/tests/react/components/form/DefaultForm.test.js +123 -0
  78. package/src/tests/react/components/help/DefaultTooltip.test.js +93 -0
  79. package/src/tests/react/components/inputs/DefaultSelect.test.js +108 -79
  80. package/src/tests/react/components/table/DefaultTable.grouping.test.js +49 -0
  81. package/src/tests/react/components/table/DefaultTable.test.js +53 -2
  82. package/src/tests/react/components/table/DefaultTable.utils.test.js +10 -0
  83. package/src/tests/react/components/table/DefaultTableControls.filters.test.mjs +21 -0
  84. package/src/tests/react/components/table/DefaultTableRowActions.test.js +23 -0
  85. package/src/tests/react/components/table/DefaultTableRows.rowActions.test.js +152 -0
  86. package/src/tests/react/components/table/resolveRowBackgroundColor.test.mjs +45 -0
  87. package/src/tests/react/components/table/useDefaultTableStoreSync.test.js +161 -0
  88. package/src/tests/react/components/upload/DefaultUpload.test.js +353 -0
  89. package/src/tests/react/components/upload/defaultUploadLibrary.test.js +104 -0
  90. package/src/tests/react/components/upload/peopleMediaLibraryPreview.test.mjs +57 -0
  91. package/src/tests/react/filters/filterValue.test.js +46 -0
  92. package/src/tests/react/hooks/usePostalCodeLookup.test.js +107 -0
  93. package/src/tests/react/inputs/defaultInputUtils.test.js +9 -1
  94. package/src/tests/react/services/SMOKE-637-cep-hydrate.md +45 -0
  95. package/src/tests/react/services/addressFormUtils.test.js +179 -0
  96. package/src/tests/react/services/addressGeoLookup.async.test.js +104 -0
  97. package/src/tests/react/services/addressGeoLookup.test.js +105 -0
  98. package/src/tests/react/services/smoke-637/form-cep-12941040.png +0 -0
  99. package/src/tests/react/services/smoke-637/index.html +132 -0
  100. package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +96 -49
@@ -12,6 +12,11 @@ import styles from './DefaultTable.styles';
12
12
  import useDefaultTableTheme from './useDefaultTableTheme';
13
13
 
14
14
  const shouldRenderSearch = store => {
15
+ const configs = store?.getters?.configs || {};
16
+
17
+ if (configs.showSearch === true) return true;
18
+ if (configs.showSearch === false) return false;
19
+
15
20
  const columns = Array.isArray(store?.getters?.columns) ? store.getters.columns : [];
16
21
 
17
22
  return columns.some(column =>
@@ -45,22 +50,38 @@ const useSearchState = storeName => {
45
50
  };
46
51
 
47
52
  export const DefaultTableCompactSearch = ({ storeName }) => {
53
+ const store = useStore(storeName);
54
+ const configs = store?.getters?.configs || {};
48
55
  const { renders, rendersCompactTotal } = useSearchState(storeName);
49
56
 
50
57
  return renders && rendersCompactTotal ? (
51
- <DefaultSearch compact storeName={storeName} />
58
+ <DefaultSearch
59
+ compact
60
+ placeholder={configs.searchPlaceholder}
61
+ searchKey={configs.searchKey || 'search'}
62
+ storeName={storeName}
63
+ />
52
64
  ) : null;
53
65
  };
54
66
 
55
67
  export const DefaultTableInlineSearch = ({ storeName }) => {
68
+ const store = useStore(storeName);
69
+ const configs = store?.getters?.configs || {};
56
70
  const { collapses, renders, rendersCompactTotal } = useSearchState(storeName);
57
71
 
58
72
  return renders && !rendersCompactTotal && !collapses ? (
59
- <DefaultSearch compact storeName={storeName} />
73
+ <DefaultSearch
74
+ compact
75
+ placeholder={configs.searchPlaceholder}
76
+ searchKey={configs.searchKey || 'search'}
77
+ storeName={storeName}
78
+ />
60
79
  ) : null;
61
80
  };
62
81
 
63
82
  export const DefaultTableCollapsedSearch = ({ storeName }) => {
83
+ const store = useStore(storeName);
84
+ const configs = store?.getters?.configs || {};
64
85
  const { collapses, renders, rendersCompactTotal } = useSearchState(storeName);
65
86
  const { tableButtonColors } = useDefaultTableTheme();
66
87
 
@@ -89,9 +110,11 @@ export const DefaultTableCollapsedSearch = ({ storeName }) => {
89
110
  )}
90
111
  >
91
112
  {({ close, isOpen }) => (
92
- <DefaultSearchModal
93
- storeName={storeName}
94
- visible={isOpen}
113
+ <DefaultSearchModal
114
+ searchKey={configs.searchKey || 'search'}
115
+ searchPlaceholder={configs.searchPlaceholder}
116
+ storeName={storeName}
117
+ visible={isOpen}
95
118
  onClose={close}
96
119
  />
97
120
  )}
@@ -1,7 +1,8 @@
1
- import React from 'react';
1
+ import React, {useMemo, useState} from 'react';
2
2
  import { Text, View, useWindowDimensions } from 'react-native';
3
3
  import { useStore } from '@store';
4
4
  import DefaultTableControls from './DefaultTableControls';
5
+ import DefaultTableImportModal from './DefaultTableImportModal';
5
6
  import {
6
7
  DefaultTableCollapsedSearch,
7
8
  DefaultTableCompactSearch,
@@ -11,8 +12,31 @@ import DefaultToolbarAction from './DefaultToolbarAction';
11
12
  import styles from './DefaultTable.styles';
12
13
  import useDefaultTableTheme from './useDefaultTableTheme';
13
14
 
15
+ const translateConfigKey = key => {
16
+ if (!Array.isArray(key) || key.length < 3) {
17
+ return '';
18
+ }
19
+
20
+ return String(global.t?.t?.(key[0], key[1], key[2]) ?? '').trim();
21
+ };
22
+
23
+ const resolveConfiguredLabel = config => {
24
+ const literalLabel = String(config?.label ?? '').trim();
25
+ if (literalLabel) {
26
+ return literalLabel;
27
+ }
28
+
29
+ const configuredLabel = translateConfigKey(config?.labelKey);
30
+ if (configuredLabel) {
31
+ return configuredLabel;
32
+ }
33
+
34
+ return global.t?.t?.('defaultTable', 'button', 'import');
35
+ };
36
+
14
37
  const DefaultTableToolbar = ({ storeName }) => {
15
38
  const store = useStore(storeName);
39
+ const [isImportModalVisible, setIsImportModalVisible] = useState(false);
16
40
  const { width } = useWindowDimensions();
17
41
  const configs = store?.getters?.configs || {};
18
42
  const compactBreakpoint = Number(configs.compactBreakpoint || 768);
@@ -20,6 +44,66 @@ const DefaultTableToolbar = ({ storeName }) => {
20
44
  const showTotalItemsInCompactToolbar = configs.showTotalItemsInCompactToolbar === true;
21
45
  const showTotalItemsInFooter = configs.showTotalItemsInFooter !== false;
22
46
  const toolbarActions = Array.isArray(configs.toolbarActions) ? configs.toolbarActions : [];
47
+ const buildTransferAction = (action, fallback) => {
48
+ if (!action) return null;
49
+
50
+ if (typeof action === 'function') {
51
+ return {
52
+ ...fallback,
53
+ onPress: action,
54
+ };
55
+ }
56
+
57
+ if (typeof action === 'object') {
58
+ return {
59
+ ...fallback,
60
+ ...action,
61
+ };
62
+ }
63
+
64
+ return null;
65
+ };
66
+ const importConfig = configs.import || null;
67
+ const importActionOverrides =
68
+ configs.importAction && typeof configs.importAction === 'object'
69
+ ? configs.importAction
70
+ : null;
71
+ const genericImportAction = useMemo(() => {
72
+ if (!importConfig || importConfig.enabled === false || !importConfig.importType) {
73
+ return null;
74
+ }
75
+
76
+ return {
77
+ ...(importActionOverrides || {}),
78
+ key: 'default-table-import',
79
+ icon: importConfig.icon || 'upload',
80
+ label: resolveConfiguredLabel(importConfig),
81
+ onPress: () => setIsImportModalVisible(true),
82
+ };
83
+ }, [importActionOverrides, importConfig]);
84
+ const importTableAction =
85
+ genericImportAction ||
86
+ buildTransferAction(
87
+ configs.importAction || configs.onImport,
88
+ {
89
+ key: 'default-table-import',
90
+ icon: 'upload',
91
+ label: global.t?.t?.('defaultTable', 'button', 'import'),
92
+ },
93
+ );
94
+ const exportTableAction = buildTransferAction(
95
+ configs.exportAction || configs.onExport,
96
+ {
97
+ key: 'default-table-export',
98
+ icon: 'download',
99
+ label: global.t?.t?.('defaultTable', 'button', 'export'),
100
+ },
101
+ );
102
+ const resolvedToolbarActions = [
103
+ importTableAction,
104
+ exportTableAction,
105
+ ...toolbarActions,
106
+ ].filter(Boolean);
23
107
  const { tableBorderColors, toolbarColors } = useDefaultTableTheme();
24
108
  const {
25
109
  backgroundColor: toolbarBackgroundColor,
@@ -49,9 +133,9 @@ const DefaultTableToolbar = ({ storeName }) => {
49
133
  ? `${totalItemsNumber} ${global.t?.t(storeName, 'label', 'items')}`
50
134
  : '';
51
135
  const renderToolbarActions = () =>
52
- Array.isArray(toolbarActions) && toolbarActions.length > 0 ? (
136
+ resolvedToolbarActions.length > 0 ? (
53
137
  <View style={styles.toolbarActionGroup}>
54
- {toolbarActions.map(action => (
138
+ {resolvedToolbarActions.map(action => (
55
139
  <DefaultToolbarAction
56
140
  key={action?.key || action?.icon || action?.label}
57
141
  action={action}
@@ -97,6 +181,11 @@ const DefaultTableToolbar = ({ storeName }) => {
97
181
  <DefaultTableControls storeName={storeName} />
98
182
  </View>
99
183
  </View>
184
+ <DefaultTableImportModal
185
+ onClose={() => setIsImportModalVisible(false)}
186
+ storeName={storeName}
187
+ visible={isImportModalVisible}
188
+ />
100
189
  </View>
101
190
  );
102
191
  };
@@ -79,22 +79,31 @@ const useAutoPageLoader = ({
79
79
  const [autoHasLoaded, setAutoHasLoaded] = useState(false);
80
80
  const [autoLoading, setAutoLoading] = useState(false);
81
81
  const [autoLoadingMore, setAutoLoadingMore] = useState(false);
82
+ const [autoRefreshing, setAutoRefreshing] = useState(false);
82
83
  const [autoLastPageCount, setAutoLastPageCount] = useState(0);
83
84
  const autoRequestIdRef = useRef(0);
84
85
  const autoLoadedQueryKeyRef = useRef('');
85
86
  const autoErroredQueryKeyRef = useRef('');
86
87
  const autoPageRef = useRef(0);
88
+ const isFetchingRef = useRef(false);
87
89
 
88
90
  const loadAutoPage = useCallback(
89
- (page, { append = false } = {}) => {
91
+ (page, { append = false, refresh = false } = {}) => {
90
92
  if (!autoMode || typeof resolvedActions.getItems !== 'function') {
91
93
  return Promise.resolve([]);
92
94
  }
95
+ if (isFetchingRef.current) {
96
+ return Promise.resolve([]);
97
+ }
98
+ isFetchingRef.current = true;
93
99
 
94
100
  const requestId = autoRequestIdRef.current + 1;
95
101
  autoRequestIdRef.current = requestId;
96
102
 
97
- if (append) {
103
+ if (refresh) {
104
+ setAutoRefreshing(true);
105
+ autoPageRef.current = 0;
106
+ } else if (append) {
98
107
  setAutoLoadingMore(true);
99
108
  } else {
100
109
  setAutoLoading(true);
@@ -130,9 +139,11 @@ const useAutoPageLoader = ({
130
139
  return [];
131
140
  })
132
141
  .finally(() => {
142
+ isFetchingRef.current = false;
133
143
  if (autoRequestIdRef.current === requestId) {
134
144
  setAutoLoading(false);
135
145
  setAutoLoadingMore(false);
146
+ setAutoRefreshing(false);
136
147
  endReachedLockRef.current = false;
137
148
  }
138
149
  });
@@ -155,6 +166,7 @@ const useAutoPageLoader = ({
155
166
  autoLoadedQueryKeyRef,
156
167
  autoLoading,
157
168
  autoLoadingMore,
169
+ autoRefreshing,
158
170
  autoPageRef,
159
171
  loadAutoPage,
160
172
  pageSizeNumber,
@@ -209,6 +221,7 @@ export const useDefaultTablePagination = ({
209
221
  hasMore,
210
222
  isFocused,
211
223
  isLoading,
224
+ onRefresh,
212
225
  onEndReached,
213
226
  pageSize,
214
227
  requestParams,
@@ -227,6 +240,8 @@ export const useDefaultTablePagination = ({
227
240
  sortField: resolvedSort?.field,
228
241
  });
229
242
  const pageSizeNumber = Number(requestParams.itemsPerPage || pageSize || 50) || 50;
243
+ const resourceEndpoint = store?.getters?.resourceEndpoint || '';
244
+ const storeReload = store?.getters?.reload;
230
245
  const buildRequestQuery = useCallback(
231
246
  (page, append = false) =>
232
247
  buildQueryFromState({
@@ -243,12 +258,20 @@ export const useDefaultTablePagination = ({
243
258
  const autoQuerySignature = useMemo(
244
259
  () =>
245
260
  stableSerialize({
246
- filters,
247
- pageSize: pageSizeNumber,
248
- requestParams,
249
- sort: resolvedSort,
261
+ endpoint: resourceEndpoint,
262
+ query: buildQueryFromState({
263
+ append: false,
264
+ columnsForTable,
265
+ filters,
266
+ page: 1,
267
+ pageSizeNumber,
268
+ requestParams,
269
+ sort: resolvedSort,
270
+ }),
271
+ reload: Boolean(storeReload),
272
+ storeName,
250
273
  }),
251
- [filters, pageSizeNumber, requestParams, resolvedSort],
274
+ [columnsForTable, filters, pageSizeNumber, requestParams, resolvedSort, resourceEndpoint, storeName, storeReload],
252
275
  );
253
276
  const {
254
277
  autoErroredQueryKeyRef,
@@ -257,6 +280,7 @@ export const useDefaultTablePagination = ({
257
280
  autoLoadedQueryKeyRef,
258
281
  autoLoading,
259
282
  autoLoadingMore,
283
+ autoRefreshing,
260
284
  autoPageRef,
261
285
  loadAutoPage,
262
286
  } = useAutoPageLoader({
@@ -283,9 +307,18 @@ export const useDefaultTablePagination = ({
283
307
  resolvedActions,
284
308
  });
285
309
 
310
+ useEffect(() => {
311
+ if (storeReload && typeof store?.actions?.setReload === 'function') {
312
+ store.actions.setReload(false);
313
+ }
314
+ }, [storeReload, store]);
315
+
286
316
  const resolvedIsLoading = autoMode
287
317
  ? (autoLoading || autoLoadingMore)
288
318
  : Boolean(isLoading);
319
+ const resolvedIsRefreshing = autoMode
320
+ ? autoRefreshing
321
+ : false;
289
322
  const resolvedData = autoMode
290
323
  ? (autoHasLoaded && Array.isArray(store?.getters?.items) ? store.getters.items : [])
291
324
  : (Array.isArray(data) ? data : []);
@@ -358,13 +391,29 @@ export const useDefaultTablePagination = ({
358
391
  resolvedIsLoading,
359
392
  ]);
360
393
 
394
+ const handleRefresh = useCallback(() => {
395
+ endReachedLockRef.current = false;
396
+
397
+ if (autoMode) {
398
+ return loadAutoPage(1, { append: false, refresh: true });
399
+ }
400
+
401
+ if (typeof onRefresh === 'function') {
402
+ return Promise.resolve(onRefresh());
403
+ }
404
+
405
+ return Promise.resolve([]);
406
+ }, [autoMode, loadAutoPage, onRefresh]);
407
+
361
408
  return {
362
409
  buildRequestQuery,
363
410
  currentPage: autoPageRef.current,
364
411
  handleEndReached,
412
+ handleRefresh,
365
413
  pageSizeNumber,
366
414
  resolvedData,
367
415
  resolvedHasMore,
416
+ resolvedIsRefreshing,
368
417
  resolvedIsLoading,
369
418
  };
370
419
  };
@@ -0,0 +1,164 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import {
3
+ resolveStoredTableFiltersPreference,
4
+ resolveStoredVisibleColumnsPreference,
5
+ sanitizeTableFiltersPreference,
6
+ sanitizeVisibleColumnsPreference,
7
+ } from '../../utils/tableVisibleColumnsPreferences';
8
+
9
+ export const publishStoreValue = (store, actionName, value, getterName) => {
10
+ if (typeof store?.actions?.[actionName] === 'function') {
11
+ store.actions[actionName](value);
12
+ return;
13
+ }
14
+
15
+ if (store?.getters) {
16
+ store.getters[getterName] = value;
17
+ }
18
+ };
19
+
20
+ export const assignGetterValue = (store, getterName, value) => {
21
+ if (store?.getters) {
22
+ store.getters[getterName] = value;
23
+ }
24
+ };
25
+
26
+ /** Stable JSON compare for filter objects (anti React #185 on period toggle). */
27
+ export const areTableFiltersEqual = (a, b) => {
28
+ try {
29
+ return JSON.stringify(a ?? {}) === JSON.stringify(b ?? {});
30
+ } catch {
31
+ return a === b;
32
+ }
33
+ };
34
+
35
+ /**
36
+ * Keeps controlled DefaultTable props (data/columns/configs/preferences) in sync
37
+ * with the zustand store without depending on store object identity.
38
+ * Zustand replaces the store slice on every commit; using that identity in effect
39
+ * deps caused React error #185 (Maximum update depth exceeded) on Devices.
40
+ *
41
+ * Stored filters are hydrated once per preference scope / columns signature.
42
+ * Re-applying on every storeFilters change caused Maximum update depth when
43
+ * toggling date period (today → all → today) on OrderHistoryPage (app-community#448).
44
+ */
45
+ export function useDefaultTableStoreSync({
46
+ columns,
47
+ columnsForTable,
48
+ data,
49
+ defaultTableConfigs,
50
+ defaultTableConfigsSignature,
51
+ store,
52
+ storeColumnsLength,
53
+ storeFilters,
54
+ storeName,
55
+ tablePreferenceScope,
56
+ }) {
57
+ const storeRef = useRef(store);
58
+ storeRef.current = store;
59
+ const lastPublishedItemsRef = useRef(undefined);
60
+ const configsSignatureRef = useRef('');
61
+ const lastHydratedFiltersKeyRef = useRef('');
62
+ const storeFiltersRef = useRef(storeFilters);
63
+ storeFiltersRef.current = storeFilters;
64
+
65
+ useEffect(() => {
66
+ if (storeColumnsLength > 0 || !Array.isArray(columns) || columns.length === 0) {
67
+ return;
68
+ }
69
+
70
+ publishStoreValue(storeRef.current, 'setColumns', columns, 'columns');
71
+ }, [columns, storeColumnsLength, storeName]);
72
+
73
+ useEffect(() => {
74
+ if (data === undefined || !Array.isArray(data)) {
75
+ lastPublishedItemsRef.current = undefined;
76
+ return;
77
+ }
78
+
79
+ if (lastPublishedItemsRef.current === data) {
80
+ return;
81
+ }
82
+
83
+ lastPublishedItemsRef.current = data;
84
+ publishStoreValue(storeRef.current, 'setItems', data, 'items');
85
+ }, [data, storeName]);
86
+
87
+ useEffect(() => {
88
+ if (!columnsForTable.length) {
89
+ return;
90
+ }
91
+
92
+ const storedVisibleColumns = resolveStoredVisibleColumnsPreference(tablePreferenceScope);
93
+ if (!storedVisibleColumns) {
94
+ return;
95
+ }
96
+
97
+ const nextVisibleColumns = sanitizeVisibleColumnsPreference({
98
+ columns: columnsForTable,
99
+ visibleColumns: storedVisibleColumns,
100
+ });
101
+
102
+ if (!nextVisibleColumns.length) {
103
+ return;
104
+ }
105
+
106
+ publishStoreValue(
107
+ storeRef.current,
108
+ 'setVisibleColumns',
109
+ nextVisibleColumns,
110
+ 'visibleColumns',
111
+ );
112
+ }, [columnsForTable, storeName, tablePreferenceScope]);
113
+
114
+ useEffect(() => {
115
+ if (!columnsForTable.length) {
116
+ return;
117
+ }
118
+
119
+ const scopeKey = [
120
+ tablePreferenceScope?.companyKey || '',
121
+ tablePreferenceScope?.storeKey || '',
122
+ tablePreferenceScope?.routeKey || '',
123
+ columnsForTable.map(c => c?.key || c?.name || '').join('|'),
124
+ ].join('::');
125
+
126
+ const storedFilters = resolveStoredTableFiltersPreference(tablePreferenceScope);
127
+ if (!storedFilters) {
128
+ lastHydratedFiltersKeyRef.current = scopeKey;
129
+ return;
130
+ }
131
+
132
+ const nextFilters = sanitizeTableFiltersPreference({
133
+ columns: columnsForTable,
134
+ filters: storedFilters,
135
+ });
136
+
137
+ if (Object.keys(nextFilters).length === 0) {
138
+ lastHydratedFiltersKeyRef.current = scopeKey;
139
+ return;
140
+ }
141
+
142
+ // Hydrate once per scope/columns; never chase storeFilters (loop on period toggle).
143
+ if (lastHydratedFiltersKeyRef.current === scopeKey) {
144
+ return;
145
+ }
146
+
147
+ if (areTableFiltersEqual(storeFiltersRef.current, nextFilters)) {
148
+ lastHydratedFiltersKeyRef.current = scopeKey;
149
+ return;
150
+ }
151
+
152
+ lastHydratedFiltersKeyRef.current = scopeKey;
153
+ publishStoreValue(storeRef.current, 'setFilters', nextFilters, 'filters');
154
+ }, [columnsForTable, storeName, tablePreferenceScope]);
155
+
156
+ useEffect(() => {
157
+ if (configsSignatureRef.current === defaultTableConfigsSignature) {
158
+ return;
159
+ }
160
+
161
+ configsSignatureRef.current = defaultTableConfigsSignature;
162
+ publishStoreValue(storeRef.current, 'setConfigs', defaultTableConfigs, 'configs');
163
+ }, [defaultTableConfigs, defaultTableConfigsSignature, storeName]);
164
+ }