@onehat/ui 0.4.81 → 0.4.83

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 (31) hide show
  1. package/package.json +7 -6
  2. package/src/Components/Container/Container.js +4 -4
  3. package/src/Components/Form/Field/Combo/Combo.js +88 -16
  4. package/src/Components/Form/Field/Combo/MeterTypesCombo.js +1 -0
  5. package/src/Components/Form/Field/Date.js +1 -1
  6. package/src/Components/Form/Field/Json.js +2 -1
  7. package/src/Components/Form/Field/Select/PageSizeSelect.js +6 -1
  8. package/src/Components/Form/Field/Select/Select.js +14 -38
  9. package/src/Components/Form/Field/Tag/Tag.js +234 -14
  10. package/src/Components/Form/Field/Tag/ValueBox.js +20 -1
  11. package/src/Components/Form/Form.js +26 -13
  12. package/src/Components/Grid/Grid.js +316 -106
  13. package/src/Components/Grid/GridHeaderRow.js +42 -22
  14. package/src/Components/Grid/GridRow.js +16 -6
  15. package/src/Components/Grid/RowHandle.js +16 -4
  16. package/src/Components/Hoc/Secondary/withSecondaryEditor.js +137 -43
  17. package/src/Components/Hoc/Secondary/withSecondarySideEditor.js +1 -1
  18. package/src/Components/Hoc/withData.js +7 -0
  19. package/src/Components/Hoc/withEditor.js +19 -4
  20. package/src/Components/Hoc/withPresetButtons.js +1 -1
  21. package/src/Components/Hoc/withSideEditor.js +1 -1
  22. package/src/Components/Icons/Join.js +10 -0
  23. package/src/Components/Layout/AsyncOperation.js +61 -14
  24. package/src/Components/Layout/CenterBox.js +1 -1
  25. package/src/Components/Screens/Manager.js +1 -1
  26. package/src/Components/Toolbar/Pagination.js +108 -106
  27. package/src/Components/Toolbar/PaginationToolbar.js +3 -1
  28. package/src/Components/Toolbar/Toolbar.js +10 -6
  29. package/src/Components/Tree/TreeNode.js +39 -9
  30. package/src/Components/Viewer/Viewer.js +7 -2
  31. package/src/Constants/Progress.js +2 -1
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useRef, useMemo, useCallback, } from 'react';
1
+ import { useState, useEffect, useRef, useMemo, useCallback, createRef, } from 'react';
2
2
  import {
3
3
  Box,
4
4
  FlatList,
@@ -16,6 +16,9 @@ import {
16
16
  SELECTION_MODE_SINGLE,
17
17
  SELECTION_MODE_MULTI,
18
18
  } from '../../Constants/Selection.js';
19
+ import {
20
+ EDITOR_TYPE__SIDE,
21
+ } from '../../Constants/Editor.js';
19
22
  import {
20
23
  EDIT,
21
24
  VIEW,
@@ -104,9 +107,10 @@ const
104
107
  SINGLE_CLICK = 1,
105
108
  DOUBLE_CLICK = 2,
106
109
  TRIPLE_CLICK = 3,
107
- PHASES_INITIAL = 'initial',
108
- PHASES_MEASURING = 'measuring',
109
- PHASES_OPTIMIZED = 'optimized';
110
+ PHASES__INITIAL = 'initial',
111
+ PHASES__MEASURING = 'measuring',
112
+ PHASES__OPTIMIZED = 'optimized',
113
+ DEBUG = false;
110
114
 
111
115
  function GridComponent(props) {
112
116
  const {
@@ -129,6 +133,7 @@ function GridComponent(props) {
129
133
  flatListProps = {},
130
134
  onRowPress,
131
135
  onRender,
136
+ onLayout,
132
137
  disableLoadOnRender = false,
133
138
  forceLoadOnRender = false,
134
139
  pullToRefresh = true,
@@ -145,6 +150,7 @@ function GridComponent(props) {
145
150
  canColumnsSort = true,
146
151
  canColumnsReorder = true,
147
152
  canColumnsResize = true,
153
+ areCellsScrollable = true,
148
154
  allowToggleSelection = false, // i.e. single click with no shift key toggles the selection of the item clicked on
149
155
  disableBottomToolbar = false,
150
156
  disablePagination = false,
@@ -205,6 +211,8 @@ function GridComponent(props) {
205
211
  onContextMenu,
206
212
  isAdding,
207
213
  isEditorViewOnly,
214
+ getIsEditorShown,
215
+ editorType,
208
216
 
209
217
  // withData
210
218
  Repository,
@@ -255,20 +263,42 @@ function GridComponent(props) {
255
263
  return !isSerializable(columnsConfig); // (runs only once, when the component is first created)
256
264
  }),
257
265
  forceUpdate = useForceUpdate(),
266
+ isAddingRaw = useRef(),
267
+ measurementPhaseRaw = useRef(PHASES__INITIAL),
268
+ measuredRowHeightRaw = useRef(null),
258
269
  containerRef = useRef(),
259
270
  gridRef = useRef(),
260
271
  gridContainerRef = useRef(),
261
- isAddingRef = useRef(),
262
272
  expandedRowsRef = useRef({}),
263
273
  cachedDragElements = useRef(),
264
274
  dragSelectionRef = useRef([]),
265
275
  previousSelectorId = useRef(),
276
+ headerRowRef = useRef(null),
277
+ topToolbarRef = useRef(null),
278
+ measuredRowsRef = useRef([]),
279
+ footerToolbarRef = useRef(null),
280
+ rowRefs = useRef([]),
266
281
  [isInited, setIsInited] = useState(false),
267
282
  [isReady, setIsReady] = useState(false),
268
283
  [isLoading, setIsLoading] = useState(false),
269
284
  [localColumnsConfig, setLocalColumnsConfigRaw] = useState([]),
270
285
  [isReorderMode, setIsReorderMode] = useState(false),
271
286
  showRowHandle = showSelectHandle || areRowsDragSource || (canRowsReorder && isReorderMode),
287
+ [lastMeasuredContainerHeight, setLastMeasuredContainerHeight] = useState(0),
288
+ getMeasurementPhase = () => {
289
+ return measurementPhaseRaw.current;
290
+ },
291
+ setMeasurementPhase = (phase) => {
292
+ measurementPhaseRaw.current = phase;
293
+ forceUpdate();
294
+ },
295
+ getMeasuredRowHeight = () => {
296
+ return measuredRowHeightRaw.current;
297
+ },
298
+ setMeasuredRowHeight = (height) => {
299
+ measuredRowHeightRaw.current = height;
300
+ forceUpdate();
301
+ },
272
302
  getIsExpanded = (index) => {
273
303
  return !!expandedRowsRef.current[index];
274
304
  },
@@ -639,14 +669,20 @@ function GridComponent(props) {
639
669
 
640
670
  }
641
671
 
642
- // Use pre-created ref for row height measurement (sample first few rows)
672
+ // assign ref for row height measurement during measurement phase
643
673
  let rowRef = null;
644
- if (autoAdjustPageSizeToHeight && measurementPhase === PHASES_MEASURING &&
645
- !isHeaderRow && index >= 1 && index <= 5) { // Sample first 5 data rows (index 1-5)
674
+ if (autoAdjustPageSizeToHeight && getMeasurementPhase() === PHASES__MEASURING &&
675
+ !isHeaderRow && index >= 1) { // Sample all data rows (index 1+)
646
676
  const refIndex = index - 1; // Convert to 0-based index
647
- rowRef = rowRefs[refIndex];
648
- if (rowRef && !sampleRowsRef.current.includes(rowRef)) {
649
- sampleRowsRef.current.push(rowRef);
677
+
678
+ // Create ref if it doesn't exist
679
+ if (!rowRefs.current[refIndex]) {
680
+ rowRefs.current[refIndex] = createRef();
681
+ }
682
+ rowRef = rowRefs.current[refIndex];
683
+
684
+ if (rowRef && !measuredRowsRef.current.includes(rowRef)) {
685
+ measuredRowsRef.current.push(rowRef);
650
686
  }
651
687
  }
652
688
 
@@ -661,6 +697,7 @@ function GridComponent(props) {
661
697
  isRowHoverable={isRowHoverable}
662
698
  isSelected={isSelected}
663
699
  isHovered={hovered}
700
+ areCellsScrollable={areCellsScrollable}
664
701
  showHovers={showHovers}
665
702
  showRowHandle={showRowHandle}
666
703
  rowCanSelect={rowCanSelect}
@@ -858,25 +895,17 @@ function GridComponent(props) {
858
895
  marker.remove();
859
896
  cachedDragElements.current = null;
860
897
  },
861
- // Refs for measuring actual row heights
862
- headerRowRef = useRef(null),
863
- paginationToolbarRef = useRef(null),
864
- sampleRowsRef = useRef([]),
865
-
866
- // Pre-create refs for first 5 rows for measurement
867
- rowRefs = [useRef(null), useRef(null), useRef(null), useRef(null), useRef(null)],
868
-
869
- // State for tracking measurement phases
870
- [measurementPhase, setMeasurementPhase] = useState(PHASES_INITIAL), //
871
- [lastMeasuredContainerHeight, setLastMeasuredContainerHeight] = useState(0),
872
- [measuredRowHeight, setMeasuredRowHeight] = useState(null),
873
898
  calculatePageSize = (containerHeight, useActualMeasurements = false) => {
899
+ if (DEBUG) {
900
+ console.log(`${getMeasurementPhase()}, calculatePageSize A containerHeight=${containerHeight}, useActualMeasurements=${useActualMeasurements}, measuredRowHeight=${getMeasuredRowHeight()}`);
901
+ }
874
902
  // Phase 1: Initial calculation using estimated heights
875
- if (!useActualMeasurements || measurementPhase === PHASES_INITIAL) {
903
+ if (!useActualMeasurements || getMeasurementPhase() === PHASES__INITIAL) {
876
904
  const
877
- headerHeight = showHeaders ? 50 : 0,
905
+ headerRowHeight = showHeaders ? 50 : 0,
906
+ topToolbarHeight = topToolbar ? 50 : 0, // Estimate top toolbar height
878
907
  footerHeight = !disablePagination ? 50 : 0,
879
- availableHeight = containerHeight - headerHeight - footerHeight,
908
+ availableHeight = containerHeight - topToolbarHeight - headerRowHeight - footerHeight,
880
909
  maxClassNormal = styles.GRID_ROW_MAX_HEIGHT_NORMAL, // e.g. max-h-[40px]
881
910
  rowNormalHeight = parseInt(maxClassNormal.match(/\d+/)[0]);
882
911
 
@@ -886,111 +915,251 @@ function GridComponent(props) {
886
915
  if (pageSize < 1) {
887
916
  pageSize = 1;
888
917
  }
918
+ if (DEBUG) {
919
+ console.log(`${getMeasurementPhase()}, calculatePageSize B using ESTIMATED heights, pageSize=${pageSize}`);
920
+ }
889
921
  return pageSize;
890
922
  }
891
923
 
892
924
  // Phase 3: Optimized calculation using actual measurements
893
- if (useActualMeasurements && measurementPhase === PHASES_OPTIMIZED && measuredRowHeight) {
894
- let actualHeaderHeight = 0;
895
- let actualFooterHeight = 0;
896
- let actualRowHeight = measuredRowHeight;
925
+ if (useActualMeasurements && getMeasurementPhase() === PHASES__OPTIMIZED && getMeasuredRowHeight()) {
926
+ let actualTopToolbarHeight = 0,
927
+ actualHeaderHeight = 0,
928
+ actualFooterHeight = 0,
929
+ actualRowHeight = getMeasuredRowHeight();
897
930
 
898
- // Measure actual header height
931
+ if (topToolbar && topToolbarRef.current) {
932
+ actualTopToolbarHeight = topToolbarRef.current.offsetHeight || topToolbarRef.current.clientHeight || 50;
933
+ }
899
934
  if (showHeaders && headerRowRef.current) {
900
935
  actualHeaderHeight = headerRowRef.current.offsetHeight || headerRowRef.current.clientHeight || 50;
901
936
  }
902
-
903
- // Measure actual pagination toolbar height
904
- if (!disablePagination && paginationToolbarRef.current) {
905
- actualFooterHeight = paginationToolbarRef.current.offsetHeight || paginationToolbarRef.current.clientHeight || 50;
937
+ if (!disablePagination && footerToolbarRef.current) {
938
+ actualFooterHeight = footerToolbarRef.current.offsetHeight || footerToolbarRef.current.clientHeight || 50;
906
939
  }
907
940
 
908
- const availableHeight = containerHeight - actualHeaderHeight - actualFooterHeight;
941
+ const availableHeight = containerHeight - actualTopToolbarHeight - actualHeaderHeight - actualFooterHeight;
909
942
  let pageSize = Math.floor(availableHeight / actualRowHeight);
910
943
 
911
944
  if (pageSize < 1) {
912
945
  pageSize = 1;
913
946
  }
947
+ if (DEBUG) {
948
+ console.log(`${getMeasurementPhase()}, calculatePageSize C using ACTUAL heights, pageSize=${pageSize}`);
949
+ }
914
950
  return pageSize;
915
951
  }
916
952
 
917
953
  // Fallback to Phase 1 logic
954
+ if (DEBUG) {
955
+ console.log(`${getMeasurementPhase()}, calculatePageSize D fallback to ESTIMATED heights by calling calculatePageSize(${containerHeight}, false)`);
956
+ }
918
957
  return calculatePageSize(containerHeight, false);
919
958
  },
920
959
  measureActualRowHeights = () => {
921
- if (CURRENT_MODE !== UI_MODE_WEB || !gridContainerRef.current) {
922
- return;
960
+ if (!gridContainerRef.current) {
961
+ return null;
923
962
  }
924
-
925
- // Measure a sample of rendered rows to get average height
926
- const sampleRows = sampleRowsRef.current.filter(ref => ref && ref.current);
927
- if (sampleRows.length === 0) {
928
- return;
963
+ if (DEBUG) {
964
+ console.log(`${getMeasurementPhase()}, measureActualRowHeights A`);
965
+ }
966
+
967
+ const measuredRows = measuredRowsRef.current.filter(ref => ref && ref.current);
968
+ if (measuredRows.length === 0) {
969
+ if (DEBUG) {
970
+ console.log(`${getMeasurementPhase()}, measureActualRowHeights B no rows to measure`);
971
+ }
972
+ return null;
929
973
  }
930
974
 
931
975
  let totalHeight = 0;
932
976
  let measuredCount = 0;
933
-
934
- sampleRows.forEach(rowRef => {
935
- if (rowRef.current) {
936
- const height = rowRef.current.offsetHeight || rowRef.current.clientHeight;
937
- if (height > 0) {
938
- totalHeight += height;
939
- measuredCount++;
977
+
978
+ if (CURRENT_MODE === UI_MODE_WEB) {
979
+ // Web: Use DOM measurement APIs
980
+ _.each(measuredRows, (rowRef) => {
981
+ if (rowRef.current) {
982
+ const height = rowRef.current.offsetHeight || rowRef.current.clientHeight;
983
+ if (height > 0) {
984
+ totalHeight += height;
985
+ measuredCount++;
986
+ }
940
987
  }
941
- }
942
- });
988
+ });
989
+ } else if (CURRENT_MODE === UI_MODE_NATIVE) {
990
+ // React Native: Use measure API with promises
991
+ return new Promise((resolve) => {
992
+ let completed = 0;
993
+ const measurements = [];
994
+
995
+ _.each(measuredRows, (rowRef) => {
996
+ if (rowRef.current && rowRef.current.measure) {
997
+ rowRef.current.measure((x, y, width, height) => {
998
+ if (height > 0) {
999
+ measurements.push(height);
1000
+ }
1001
+ completed++;
1002
+
1003
+ if (completed === measuredRows.length) {
1004
+ if (measurements.length > 0) {
1005
+ const averageHeight = measurements.reduce((sum, h) => sum + h, 0) / measurements.length;
1006
+ if (DEBUG) {
1007
+ console.log(`[Grid] Measured actual row height: ${averageHeight}px from ${measurements.length} measured rows`);
1008
+ }
1009
+
1010
+ // Clear measured refs for next measurement cycle
1011
+ measuredRowsRef.current = [];
1012
+
1013
+ resolve(averageHeight);
1014
+ } else {
1015
+ resolve(null);
1016
+ }
1017
+ }
1018
+ });
1019
+ } else {
1020
+ completed++;
1021
+ if (completed === measuredRows.length && measurements.length === 0) {
1022
+ resolve(null);
1023
+ }
1024
+ }
1025
+ });
1026
+
1027
+ // Timeout fallback
1028
+ setTimeout(() => {
1029
+ if (measurements.length > 0) {
1030
+ const averageHeight = measurements.reduce((sum, h) => sum + h, 0) / measurements.length;
1031
+ if (DEBUG) {
1032
+ log(`[Grid] Measured actual row height (timeout): ${averageHeight}px from ${measurements.length} measured rows`);
1033
+ }
1034
+ measuredRowsRef.current = [];
1035
+ resolve(averageHeight);
1036
+ } else {
1037
+ resolve(null);
1038
+ }
1039
+ }, 200);
1040
+ });
1041
+ }
943
1042
 
944
1043
  if (measuredCount > 0) {
945
1044
  const averageHeight = totalHeight / measuredCount;
946
- console.log(`[Grid] Measured actual row height: ${averageHeight}px from ${measuredCount} sample rows`);
947
- setMeasuredRowHeight(averageHeight);
948
- setMeasurementPhase(PHASES_OPTIMIZED);
1045
+
1046
+ if (DEBUG) {
1047
+ console.log(`${getMeasurementPhase()}, measureActualRowHeights C averageHeight=${averageHeight}, measuredCount=${measuredCount}`);
1048
+ }
1049
+
1050
+ // Clear measured refs for next measurement cycle
1051
+ measuredRowsRef.current = [];
949
1052
 
950
- // Clear sample refs for next measurement cycle
951
- sampleRowsRef.current = [];
1053
+ return averageHeight;
1054
+ }
1055
+
1056
+ if (DEBUG) {
1057
+ console.log(`${getMeasurementPhase()}, measureActualRowHeights D measuredCount=0`);
1058
+ }
1059
+ return null;
1060
+ },
1061
+ applyMeasuredRowHeight = (averageHeight) => {
1062
+ if (DEBUG) {
1063
+ console.log(`${getMeasurementPhase()}, applyMeasuredRowHeight A averageHeight=${averageHeight}, lastMeasuredContainerHeight=${lastMeasuredContainerHeight}, setMeasurementPhase(${PHASES__OPTIMIZED})`);
1064
+ }
1065
+
1066
+ // Always transition to optimized phase, even if measurement failed
1067
+ setMeasurementPhase(PHASES__OPTIMIZED);
1068
+
1069
+ if (averageHeight) {
1070
+ setMeasuredRowHeight(averageHeight);
952
1071
 
953
1072
  // Recalculate pageSize with actual measurements
954
1073
  if (lastMeasuredContainerHeight > 0) {
1074
+ if (DEBUG) {
1075
+ console.log(`${getMeasurementPhase()}, applyMeasuredRowHeight B call calculatePageSize(${lastMeasuredContainerHeight}, true)`);
1076
+ }
955
1077
  const newPageSize = calculatePageSize(lastMeasuredContainerHeight, true);
956
- console.log(`[Grid] Optimized pageSize: ${newPageSize} (was ${Repository.pageSize})`);
957
1078
  if (newPageSize !== Repository.pageSize) {
1079
+ if (DEBUG) {
1080
+ console.log(`${getMeasurementPhase()}, applyMeasuredRowHeight B Repository.setPageSize(${newPageSize})`);
1081
+ }
958
1082
  Repository.setPageSize(newPageSize);
959
1083
  }
960
1084
  }
1085
+ } else {
1086
+ if (DEBUG) {
1087
+ console.log(`[Grid] Row height measurement failed or unavailable - using estimated pageSize`);
1088
+ }
1089
+ // Keep the current estimated pageSize, just hide the loading overlay
961
1090
  }
962
1091
  },
963
1092
  adjustPageSizeToHeight = (e) => {
964
- if (CURRENT_MODE !== UI_MODE_WEB) { // TODO: Remove this conditional, and don't even do the double render for RN
965
- return;
966
- }
967
1093
  if (!Repository || Repository.isDestroyed) { // This method gets delayed, so it's possible for Repository to have been destroyed. Check for this
968
1094
  return;
969
1095
  }
1096
+ if (onLayout) {
1097
+ onLayout(e);
1098
+ }
1099
+ if (DEBUG) {
1100
+ console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight A`);
1101
+ }
970
1102
 
971
1103
  let doAdjustment = autoAdjustPageSizeToHeight;
972
1104
  if (!_.isNil(UiGlobals.autoAdjustPageSizeToHeight) && !UiGlobals.autoAdjustPageSizeToHeight) {
973
1105
  // allow global override to prevent this auto adjustment
974
1106
  doAdjustment = false;
975
1107
  }
976
- if (doAdjustment) {
977
- const containerHeight = e.nativeEvent.layout.height;
978
- if (containerHeight > 0) {
1108
+ if (DEBUG) {
1109
+ console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight A2 doAdjustment=${doAdjustment}, autoAdjustPageSizeToHeight=${autoAdjustPageSizeToHeight}, UiGlobals.autoAdjustPageSizeToHeight=${UiGlobals.autoAdjustPageSizeToHeight}`);
1110
+ }
1111
+ const containerHeight = e.nativeEvent.layout.height;
1112
+ if (DEBUG) {
1113
+ console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight A3 containerHeight=${containerHeight}`);
1114
+ }
1115
+
1116
+ // Only proceed if height changed significantly
1117
+ const
1118
+ heightChanged = Math.abs(containerHeight - lastMeasuredContainerHeight) > 5, // 5px tolerance
1119
+ isFirstMeasurement = lastMeasuredContainerHeight === 0;
1120
+ if (containerHeight > 0 && (isFirstMeasurement || heightChanged)) {
1121
+ if (editorType === EDITOR_TYPE__SIDE && getIsEditorShown()) {
1122
+ // When side editor is shown, skip adjustment to avoid layout thrashing
1123
+ console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight A4 height changed significantly, but side editor is shown, skipping remeasurement`);
1124
+ return;
1125
+ }
1126
+ if (DEBUG) {
1127
+ console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight A4 height changed significantly, proceeding with remeasurement`);
1128
+ }
1129
+
1130
+ if (doAdjustment) {
979
1131
  setLastMeasuredContainerHeight(containerHeight);
980
1132
 
981
1133
  // Phase 1: Initial calculation with buffer
982
- const pageSize = calculatePageSize(containerHeight, false);
983
- console.log(`[Grid] Adjusting pageSize: containerHeight=${containerHeight}, phase=${measurementPhase}, calculatedPageSize=${pageSize}, currentPageSize=${Repository.pageSize}`);
984
-
1134
+ if (DEBUG) {
1135
+ console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight B call calculatePageSize(${containerHeight}, false)`);
1136
+ }
1137
+ const
1138
+ useActualMeasurements = (getMeasurementPhase() === PHASES__OPTIMIZED && getMeasuredRowHeight()),
1139
+ pageSize = calculatePageSize(containerHeight, useActualMeasurements);
1140
+ if (DEBUG) {
1141
+ console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight C containerHeight=${containerHeight}, pageSize=${pageSize}, currentPageSize=${Repository.pageSize}`);
1142
+ }
1143
+
985
1144
  if (pageSize !== Repository.pageSize) {
986
- Repository.setPageSize(pageSize);
987
-
988
- // Trigger Phase 2: Enable measurement mode after render
989
- if (measurementPhase === PHASES_INITIAL) {
990
- setMeasurementPhase(PHASES_MEASURING);
1145
+ if (DEBUG) {
1146
+ console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight D Repository.setPageSize(${pageSize})`);
991
1147
  }
1148
+ Repository.setPageSize(pageSize);
992
1149
  }
993
1150
  }
1151
+
1152
+ // Trigger Phase 2: Enable measurement mode after render
1153
+ if (getMeasurementPhase() === PHASES__INITIAL) {
1154
+ if (DEBUG) {
1155
+ console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight E setMeasurementPhase(${PHASES__MEASURING})`);
1156
+ }
1157
+ setMeasurementPhase(PHASES__MEASURING);
1158
+ }
1159
+ } else {
1160
+ if (DEBUG) {
1161
+ console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight A5 height unchanged, skipping remeasurement`);
1162
+ }
994
1163
  }
995
1164
  },
996
1165
  debouncedAdjustPageSizeToHeight = useCallback(_.debounce(adjustPageSizeToHeight, 200), []),
@@ -1130,22 +1299,24 @@ function GridComponent(props) {
1130
1299
 
1131
1300
  useEffect(() => {
1132
1301
  if (!isInited) {
1133
- // first call -- meant to render placeholder so we get container dimensions
1302
+ // first call, Repository.pauseEvents, while we render placeholder so we get container dimensions
1134
1303
  if (Repository) {
1135
1304
  if (Repository.isRemote) {
1136
1305
  Repository.isAutoLoad = false;
1137
1306
  }
1307
+ if (DEBUG) {
1308
+ console.log(`${getMeasurementPhase()}, useEffect 1 - first call, Repository.pauseEvents, while we render placeholder to get container dimensions`);
1309
+ }
1138
1310
  Repository.pauseEvents();
1139
1311
  }
1140
- if (onRender) {
1141
- onRender(self)
1142
- }
1143
1312
  return () => {};
1144
1313
  }
1145
1314
 
1146
1315
  (async () => {
1147
-
1148
- // second call -- do other necessary setup
1316
+ if (DEBUG) {
1317
+ console.log(`${getMeasurementPhase()}, useEffect 1 - second call, do other necessary column setup`);
1318
+ }
1319
+ // second call, do other necessary column setup
1149
1320
  let columnsConfigVariable = columnsConfig,
1150
1321
  localColumnsConfig = [],
1151
1322
  savedLocalColumnsConfig,
@@ -1231,6 +1402,9 @@ function GridComponent(props) {
1231
1402
 
1232
1403
  setLocalColumnsConfig(localColumnsConfig);
1233
1404
 
1405
+ if (onRender) {
1406
+ onRender(self)
1407
+ }
1234
1408
  setIsReady(true);
1235
1409
  })();
1236
1410
 
@@ -1243,23 +1417,12 @@ function GridComponent(props) {
1243
1417
  setTrue = () => setIsLoading(true),
1244
1418
  setFalse = () => setIsLoading(false),
1245
1419
  onChangeFilters = () => {
1420
+ if (DEBUG) {
1421
+ console.log('onChangeFilters, reload and re-measure');
1422
+ }
1246
1423
  if (!Repository.isAutoLoad) {
1247
1424
  Repository.reload();
1248
1425
  }
1249
-
1250
- // Reset measurement phase and recalculate pageSize if auto-adjust is enabled
1251
- if (autoAdjustPageSizeToHeight && lastMeasuredContainerHeight > 0) {
1252
- console.log(`[Grid] Filters changed - resetting pageSize measurement`);
1253
- setMeasurementPhase(PHASES_INITIAL);
1254
- setMeasuredRowHeight(null);
1255
- sampleRowsRef.current = [];
1256
-
1257
- // Recalculate pageSize with fresh measurements
1258
- const pageSize = calculatePageSize(lastMeasuredContainerHeight, false);
1259
- if (pageSize !== Repository.pageSize) {
1260
- Repository.setPageSize(pageSize);
1261
- }
1262
- }
1263
1426
  },
1264
1427
  onChangeSorters = () => {
1265
1428
  if (!Repository.isAutoLoad) {
@@ -1283,9 +1446,16 @@ function GridComponent(props) {
1283
1446
  Repository.on('changePage', onChangePage);
1284
1447
 
1285
1448
  applySelectorSelected();
1449
+
1450
+ if (DEBUG) {
1451
+ console.log(`${getMeasurementPhase()}, useEffect 1 - Repository.resumeEvents()`);
1452
+ }
1286
1453
  Repository.resumeEvents();
1287
1454
 
1288
1455
  if (((Repository.isRemote && !Repository.isLoaded && !Repository.isLoading) || forceLoadOnRender) && !disableLoadOnRender) { // default remote repositories to load on render, optionally force or disable load on render
1456
+ if (DEBUG) {
1457
+ console.log(`${getMeasurementPhase()}, useEffect 1 - Repository.load()`);
1458
+ }
1289
1459
  Repository.load();
1290
1460
  }
1291
1461
 
@@ -1307,27 +1477,47 @@ function GridComponent(props) {
1307
1477
  return () => {};
1308
1478
  }
1309
1479
 
1480
+ if (DEBUG) {
1481
+ console.log(`useEffect 2 - applySelectorSelected()`);
1482
+ }
1310
1483
  applySelectorSelected();
1311
1484
 
1312
1485
  }, [selectorId, selectorSelected]);
1313
1486
 
1314
1487
  // Effect to trigger row height measurement after render
1315
1488
  useEffect(() => {
1316
- if (measurementPhase === PHASES_MEASURING && data && data.length > 0) {
1317
- // Small delay to ensure DOM is fully rendered
1318
- const timer = setTimeout(() => {
1319
- measureActualRowHeights();
1320
- }, 100);
1489
+ if (getMeasurementPhase() === PHASES__MEASURING) {
1490
+ // Small delay to ensure elements are fully rendered
1491
+ const timer = setTimeout(async () => {
1492
+ try {
1493
+ if (DEBUG) {
1494
+ console.log(`${getMeasurementPhase()}, useEffect 3 call measureActualRowHeights()`);
1495
+ }
1496
+ const averageHeight = await measureActualRowHeights();
1497
+ if (DEBUG) {
1498
+ console.log(`${getMeasurementPhase()}, useEffect 3 averageHeight=${averageHeight}, call applyMeasuredRowHeight()`);
1499
+ }
1500
+ applyMeasuredRowHeight(averageHeight);
1501
+ } catch (error) {
1502
+ if (DEBUG) {
1503
+ console.warn('useEffect 3 - error', error);
1504
+ }
1505
+ // Fallback: use default height estimation
1506
+ applyMeasuredRowHeight(null);
1507
+ }
1508
+ }, 1000);
1321
1509
  return () => clearTimeout(timer);
1322
1510
  }
1323
- }, [measurementPhase, data]);
1511
+ }, [getMeasurementPhase(), data]);
1324
1512
 
1325
- // Effect to reset measurement state when autoAdjustPageSizeToHeight changes
1326
1513
  useEffect(() => {
1327
- if (autoAdjustPageSizeToHeight) {
1328
- setMeasurementPhase(PHASES_INITIAL);
1514
+ if (autoAdjustPageSizeToHeight && getMeasurementPhase() !== PHASES__INITIAL) {
1515
+ if (DEBUG) {
1516
+ console.log(`${getMeasurementPhase()}, useEffect 4 setMeasurementPhase(${PHASES__INITIAL})`);
1517
+ }
1518
+ setMeasurementPhase(PHASES__INITIAL);
1329
1519
  setMeasuredRowHeight(null);
1330
- sampleRowsRef.current = [];
1520
+ measuredRowsRef.current = [];
1331
1521
  }
1332
1522
  }, [autoAdjustPageSizeToHeight]);
1333
1523
 
@@ -1340,7 +1530,7 @@ function GridComponent(props) {
1340
1530
  self.gridRef = gridRef;
1341
1531
  }
1342
1532
 
1343
- isAddingRef.current = isAdding;
1533
+ isAddingRaw.current = isAdding;
1344
1534
 
1345
1535
  const footerToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [Repository?.hash, additionalToolbarButtons, isReorderMode]);
1346
1536
 
@@ -1348,7 +1538,13 @@ function GridComponent(props) {
1348
1538
  // first time through, render a placeholder so we can get container dimensions
1349
1539
  return <VStackNative
1350
1540
  onLayout={(e) => {
1541
+ if (DEBUG) {
1542
+ console.log(`${getMeasurementPhase()}, placeholder onLayout, call adjustPageSizeToHeight()`);
1543
+ }
1351
1544
  adjustPageSizeToHeight(e);
1545
+ if (DEBUG) {
1546
+ console.log(`${getMeasurementPhase()}, placeholder onLayout, call setIsInited(true)`);
1547
+ }
1352
1548
  setIsInited(true);
1353
1549
  }}
1354
1550
  className="w-full flex-1"
@@ -1383,7 +1579,7 @@ function GridComponent(props) {
1383
1579
  showMoreOnly = true;
1384
1580
  }
1385
1581
  listFooterComponent = <PaginationToolbar
1386
- ref={paginationToolbarRef}
1582
+ ref={footerToolbarRef}
1387
1583
  Repository={Repository}
1388
1584
  self={self}
1389
1585
  toolbarItems={footerToolbarItemComponents}
@@ -1392,7 +1588,9 @@ function GridComponent(props) {
1392
1588
  {..._paginationToolbarProps}
1393
1589
  />;
1394
1590
  } else if (footerToolbarItemComponents.length) {
1395
- listFooterComponent = <Toolbar>
1591
+ listFooterComponent = <Toolbar
1592
+ ref={footerToolbarRef}
1593
+ >
1396
1594
  <ReloadButton Repository={Repository} self={self} />
1397
1595
  {footerToolbarItemComponents}
1398
1596
  </Toolbar>;
@@ -1490,7 +1688,10 @@ function GridComponent(props) {
1490
1688
  className={className}
1491
1689
  style={style}
1492
1690
  >
1493
- {topToolbar}
1691
+ {topToolbar &&
1692
+ <VStack ref={topToolbarRef}>
1693
+ {topToolbar}
1694
+ </VStack>}
1494
1695
 
1495
1696
  <VStack
1496
1697
  ref={gridContainerRef}
@@ -1505,10 +1706,19 @@ function GridComponent(props) {
1505
1706
  // 'h-full',
1506
1707
  'flex-1',
1507
1708
  'min-h-[40px]',
1709
+ 'relative', // Enable positioning for overlay
1508
1710
  gridContainerBorderClassName,
1509
1711
  )}
1510
1712
  >
1511
1713
  {grid}
1714
+ {/* Loading overlay during measurement phases to prevent visual flashing */}
1715
+ {autoAdjustPageSizeToHeight &&
1716
+ (getMeasurementPhase() === PHASES__INITIAL || getMeasurementPhase() === PHASES__MEASURING) &&
1717
+ entities?.length > 0 && (
1718
+ <VStack className="absolute inset-0 z-10 bg-white">
1719
+ <Loading isScreen={true} />
1720
+ </VStack>
1721
+ )}
1512
1722
  </VStack>
1513
1723
 
1514
1724
  {listFooterComponent}