@onehat/ui 0.4.81 → 0.4.82

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