@onehat/ui 0.4.77 → 0.4.79

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 (59) hide show
  1. package/package.json +1 -1
  2. package/src/Components/Editor/AttachmentDirectoriesEditor.js +51 -0
  3. package/src/Components/Editor/AttachmentsEditor.js +81 -0
  4. package/src/Components/Form/Field/Combo/AttachmentDirectoriesCombo.js +20 -0
  5. package/src/Components/Form/Field/Combo/AttachmentDirectoriesComboEditor.js +22 -0
  6. package/src/Components/Form/Field/Combo/AttachmentsCombo.js +20 -0
  7. package/src/Components/Form/Field/Combo/AttachmentsComboEditor.js +22 -0
  8. package/src/Components/Form/Field/Tag/AttachmentDirectoriesTag.js +22 -0
  9. package/src/Components/Form/Field/Tag/AttachmentDirectoriesTagEditor.js +22 -0
  10. package/src/Components/Form/Field/Tag/AttachmentsTag.js +22 -0
  11. package/src/Components/Form/Field/Tag/AttachmentsTagEditor.js +22 -0
  12. package/src/Components/Form/Form.js +17 -17
  13. package/src/Components/Grid/AttachmentDirectoriesFilteredGrid.js +17 -0
  14. package/src/Components/Grid/AttachmentDirectoriesFilteredGridEditor.js +17 -0
  15. package/src/Components/Grid/AttachmentDirectoriesFilteredInlineGridEditor.js +17 -0
  16. package/src/Components/Grid/AttachmentDirectoriesFilteredSideGridEditor.js +17 -0
  17. package/src/Components/Grid/AttachmentDirectoriesGrid.js +20 -0
  18. package/src/Components/Grid/AttachmentDirectoriesGridEditor.js +27 -0
  19. package/src/Components/Grid/AttachmentDirectoriesInlineGridEditor.js +25 -0
  20. package/src/Components/Grid/AttachmentDirectoriesSideGridEditor.js +24 -0
  21. package/src/Components/Grid/AttachmentsFilteredGrid.js +17 -0
  22. package/src/Components/Grid/AttachmentsFilteredGridEditor.js +17 -0
  23. package/src/Components/Grid/AttachmentsFilteredInlineGridEditor.js +17 -0
  24. package/src/Components/Grid/AttachmentsFilteredSideGridEditor.js +17 -0
  25. package/src/Components/Grid/AttachmentsGrid.js +20 -0
  26. package/src/Components/Grid/AttachmentsGridEditor.js +27 -0
  27. package/src/Components/Grid/AttachmentsInlineGridEditor.js +25 -0
  28. package/src/Components/Grid/AttachmentsSideGridEditor.js +24 -0
  29. package/src/Components/Grid/Columns/AttachmentDirectoriesGridColumns.js +32 -0
  30. package/src/Components/Grid/Columns/AttachmentsGridColumns.js +133 -0
  31. package/src/Components/Grid/Grid.js +194 -21
  32. package/src/Components/Grid/GridHeaderRow.js +10 -17
  33. package/src/Components/Grid/GridRow.js +49 -22
  34. package/src/Components/Grid/RowHandle.js +8 -6
  35. package/src/Components/Hoc/withEditor.js +18 -1
  36. package/src/Components/Hoc/withModal.js +4 -0
  37. package/src/Components/Hoc/withPdfButtons.js +1 -1
  38. package/src/Components/Hoc/withSelection.js +26 -4
  39. package/src/Components/Layout/AsyncOperation.js +299 -195
  40. package/src/Components/Messages/GlobalModals.js +1 -2
  41. package/src/Components/Panel/Panel.js +14 -2
  42. package/src/Components/Panel/TabPanel.js +1 -1
  43. package/src/Components/Panel/TreePanel.js +1 -1
  44. package/src/Components/Report/Report.js +106 -17
  45. package/src/Components/Toolbar/PaginationToolbar.js +4 -3
  46. package/src/Components/Toolbar/Toolbar.js +6 -3
  47. package/src/Components/Tree/Tree.js +219 -148
  48. package/src/Components/Tree/TreeNode.js +20 -13
  49. package/src/Components/Window/AttachmentDirectoriesEditorWindow.js +34 -0
  50. package/src/Components/Window/AttachmentsEditorWindow.js +34 -0
  51. package/src/Components/index.js +92 -1
  52. package/src/Constants/Attachments.js +2 -0
  53. package/src/Constants/Dates.js +2 -2
  54. package/src/Constants/Progress.js +5 -1
  55. package/src/Models/Schemas/AttachmentDirectories.js +66 -0
  56. package/src/Models/Schemas/Attachments.js +88 -0
  57. package/src/Models/Slices/SystemSlice.js +220 -0
  58. package/src/PlatformImports/Web/Attachments.js +855 -161
  59. package/src/Styles/Global.css +7 -2
@@ -0,0 +1,25 @@
1
+ /**
2
+ * COPYRIGHT NOTICE
3
+ * This file is categorized as "Custom Source Code"
4
+ * and is subject to the terms and conditions defined in the
5
+ * "LICENSE.txt" file, which is part of this source code package.
6
+ */
7
+
8
+ import {
9
+ SELECTION_MODE_SINGLE,
10
+ } from '../../Constants/Selection.js';
11
+ import { InlineGridEditor } from './Grid.js';
12
+ import AttachmentsGridColumns from './Columns/AttachmentsGridColumns.js';
13
+
14
+ export default function AttachmentsInlineGridEditor(props) {
15
+ return <InlineGridEditor
16
+ reference="AttachmentsInlineGridEditor"
17
+ model="Attachments"
18
+ usePermissions={true}
19
+ selectionMode={SELECTION_MODE_SINGLE}
20
+ columnsConfig={AttachmentsGridColumns}
21
+
22
+
23
+ {...props}
24
+ />;
25
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * COPYRIGHT NOTICE
3
+ * This file is categorized as "Custom Source Code"
4
+ * and is subject to the terms and conditions defined in the
5
+ * "LICENSE.txt" file, which is part of this source code package.
6
+ */
7
+
8
+ import { SideGridEditor } from './Grid.js';
9
+ import AttachmentsEditor from '../Editor/AttachmentsEditor.js';
10
+ import AttachmentsGridColumns from './Columns/AttachmentsGridColumns.js';
11
+
12
+ export default function AttachmentsSideGridEditor(props) {
13
+ return <SideGridEditor
14
+ reference="AttachmentsSideGridEditor"
15
+ model="Attachments"
16
+ usePermissions={true}
17
+ isCollapsible={false}
18
+ Editor={AttachmentsEditor}
19
+ columnsConfig={AttachmentsGridColumns}
20
+
21
+
22
+ {...props}
23
+ />;
24
+ }
@@ -0,0 +1,32 @@
1
+ export default [
2
+ {
3
+ "id": "attachment_directories__name",
4
+ "header": "Name",
5
+ "fieldName": "attachment_directories__name",
6
+ "isSortable": true,
7
+ "isEditable": true,
8
+ "isReorderable": true,
9
+ "isResizable": true,
10
+ "w": 150
11
+ },
12
+ {
13
+ "id": "attachment_directories__model",
14
+ "header": "Model",
15
+ "fieldName": "attachment_directories__model",
16
+ "isSortable": true,
17
+ "isEditable": true,
18
+ "isReorderable": true,
19
+ "isResizable": true,
20
+ "w": 150
21
+ },
22
+ {
23
+ "id": "attachment_directories__modelid",
24
+ "header": "Modelid",
25
+ "fieldName": "attachment_directories__modelid",
26
+ "isSortable": true,
27
+ "isEditable": true,
28
+ "isReorderable": true,
29
+ "isResizable": true,
30
+ "w": 100
31
+ }
32
+ ];
@@ -0,0 +1,133 @@
1
+ export default [
2
+ {
3
+ "id": "attachments__model_display",
4
+ "header": "Model Display",
5
+ "fieldName": "attachments__model_display",
6
+ "isSortable": false,
7
+ "isEditable": false,
8
+ "isReorderable": true,
9
+ "isResizable": true,
10
+ "w": 200
11
+ },
12
+ {
13
+ "id": "attachments__size_formatted",
14
+ "header": "Size Formatted",
15
+ "fieldName": "attachments__size_formatted",
16
+ "isSortable": false,
17
+ "isEditable": false,
18
+ "isReorderable": true,
19
+ "isResizable": true,
20
+ "w": 200
21
+ },
22
+ {
23
+ "id": "attachments__info",
24
+ "header": "Info",
25
+ "fieldName": "attachments__info",
26
+ "isSortable": false,
27
+ "isEditable": false,
28
+ "isReorderable": true,
29
+ "isResizable": true,
30
+ "w": 200
31
+ },
32
+ {
33
+ "id": "attachments__uri",
34
+ "header": "Uri",
35
+ "fieldName": "attachments__uri",
36
+ "isSortable": false,
37
+ "isEditable": false,
38
+ "isReorderable": true,
39
+ "isResizable": true,
40
+ "w": 200
41
+ },
42
+ {
43
+ "id": "attachments__attachment_directory_id",
44
+ "header": "Attachment Directory",
45
+ "fieldName": "attachment_directories__name",
46
+ "isSortable": true,
47
+ "isEditable": true,
48
+ "isReorderable": true,
49
+ "isResizable": true,
50
+ "w": 100,
51
+ "editField": "attachments__attachment_directory_id"
52
+ },
53
+ {
54
+ "id": "attachments__abs_path",
55
+ "header": "Abs Path",
56
+ "fieldName": "attachments__abs_path",
57
+ "isSortable": false,
58
+ "isEditable": false,
59
+ "isReorderable": true,
60
+ "isResizable": true,
61
+ "w": 200
62
+ },
63
+ {
64
+ "id": "attachments__model",
65
+ "header": "Model",
66
+ "fieldName": "attachments__model",
67
+ "isSortable": true,
68
+ "isEditable": true,
69
+ "isReorderable": true,
70
+ "isResizable": true,
71
+ "w": 200
72
+ },
73
+ {
74
+ "id": "attachments__modelid",
75
+ "header": "Modelid",
76
+ "fieldName": "attachments__modelid",
77
+ "isSortable": true,
78
+ "isEditable": true,
79
+ "isReorderable": true,
80
+ "isResizable": true,
81
+ "w": 100
82
+ },
83
+ {
84
+ "id": "attachments__uuid",
85
+ "header": "Uuid",
86
+ "fieldName": "attachments__uuid",
87
+ "isSortable": true,
88
+ "isEditable": true,
89
+ "isReorderable": true,
90
+ "isResizable": true,
91
+ "w": 150
92
+ },
93
+ {
94
+ "id": "attachments__path",
95
+ "header": "Path",
96
+ "fieldName": "attachments__path",
97
+ "isSortable": true,
98
+ "isEditable": true,
99
+ "isReorderable": true,
100
+ "isResizable": true,
101
+ "w": 200
102
+ },
103
+ {
104
+ "id": "attachments__filename",
105
+ "header": "Filename",
106
+ "fieldName": "attachments__filename",
107
+ "isSortable": true,
108
+ "isEditable": true,
109
+ "isReorderable": true,
110
+ "isResizable": true,
111
+ "w": 150
112
+ },
113
+ {
114
+ "id": "attachments__mimetype",
115
+ "header": "Mimetype",
116
+ "fieldName": "attachments__mimetype",
117
+ "isSortable": true,
118
+ "isEditable": true,
119
+ "isReorderable": true,
120
+ "isResizable": true,
121
+ "w": 150
122
+ },
123
+ {
124
+ "id": "attachments__size",
125
+ "header": "Size",
126
+ "fieldName": "attachments__size",
127
+ "isSortable": true,
128
+ "isEditable": true,
129
+ "isReorderable": true,
130
+ "isResizable": true,
131
+ "w": 100
132
+ }
133
+ ];
@@ -103,7 +103,10 @@ import _ from 'lodash';
103
103
  const
104
104
  SINGLE_CLICK = 1,
105
105
  DOUBLE_CLICK = 2,
106
- TRIPLE_CLICK = 3;
106
+ TRIPLE_CLICK = 3,
107
+ PHASES_INITIAL = 'initial',
108
+ PHASES_MEASURING = 'measuring',
109
+ PHASES_OPTIMIZED = 'optimized';
107
110
 
108
111
  function GridComponent(props) {
109
112
  const {
@@ -184,6 +187,8 @@ function GridComponent(props) {
184
187
  areRowsDropTarget = false,
185
188
  dropTargetAccept,
186
189
  onRowDrop,
190
+ onDragStart,
191
+ onDragEnd,
187
192
 
188
193
  // withComponent
189
194
  self,
@@ -263,6 +268,7 @@ function GridComponent(props) {
263
268
  [isLoading, setIsLoading] = useState(false),
264
269
  [localColumnsConfig, setLocalColumnsConfigRaw] = useState([]),
265
270
  [isReorderMode, setIsReorderMode] = useState(false),
271
+ showRowHandle = showSelectHandle || areRowsDragSource || (canRowsReorder && isReorderMode),
266
272
  getIsExpanded = (index) => {
267
273
  return !!expandedRowsRef.current[index];
268
274
  },
@@ -491,6 +497,7 @@ function GridComponent(props) {
491
497
  'Row',
492
498
  'flex-row',
493
499
  'grow',
500
+ 'max-h-[80px]',
494
501
  )}
495
502
  >
496
503
  {({
@@ -500,6 +507,7 @@ function GridComponent(props) {
500
507
  }) => {
501
508
  if (isHeaderRow) {
502
509
  let headerRow = <GridHeaderRow
510
+ ref={headerRowRef}
503
511
  Repository={Repository}
504
512
  columnsConfig={localColumnsConfig}
505
513
  setColumnsConfig={setLocalColumnsConfig}
@@ -513,7 +521,7 @@ function GridComponent(props) {
513
521
  isInlineEditorShown={isInlineEditorShown}
514
522
  areRowsDragSource={areRowsDragSource}
515
523
  showColumnsSelector={showColumnsSelector}
516
- showSelectHandle={showSelectHandle}
524
+ showRowHandle={showRowHandle}
517
525
  />;
518
526
  if (showRowExpander) {
519
527
  // align the header row to content rows by adding a spacer that matches the width of the Grid-rowExpander-expandBtn
@@ -527,7 +535,9 @@ function GridComponent(props) {
527
535
  const
528
536
  rowReorderProps = {},
529
537
  rowDragProps = {};
530
- let WhichRow = GridRow;
538
+ let WhichRow = GridRow,
539
+ rowCanSelect = true,
540
+ rowCanDrag = false;
531
541
  if (CURRENT_MODE === UI_MODE_WEB) { // DND is currently web-only TODO: implement for RN
532
542
  // Create a method that gets an always-current copy of the selection ids
533
543
  dragSelectionRef.current = selection;
@@ -545,11 +555,13 @@ function GridComponent(props) {
545
555
  id: item.id,
546
556
  getSelection,
547
557
  isInSelection,
558
+ sourceComponentRef: gridRef, // Reference to the originating component
548
559
  onDrag: (dragState) => {
549
560
  onRowReorderDrag(dragState, dragIx);
550
561
  },
551
562
  };
552
563
  rowReorderProps.onDragEnd = onRowReorderEnd;
564
+ rowCanDrag = true;
553
565
  } else {
554
566
  // Don't allow drag/drop from withDnd while reordering
555
567
  if (areRowsDragSource && (!canRowDrag || canRowDrag(item))) {
@@ -558,6 +570,10 @@ function GridComponent(props) {
558
570
  rowDragProps.dragSourceType = rowDragSourceType;
559
571
  if (getRowDragSourceItem) {
560
572
  rowDragProps.dragSourceItem = getRowDragSourceItem(item, getSelection, isInSelection, rowDragSourceType);
573
+ // Ensure all drag items have a component reference
574
+ if (!rowDragProps.dragSourceItem.sourceComponentRef) {
575
+ rowDragProps.dragSourceItem.sourceComponentRef = gridRef;
576
+ }
561
577
  } else {
562
578
  rowDragProps.dragSourceItem = {
563
579
  id: item.id,
@@ -565,6 +581,7 @@ function GridComponent(props) {
565
581
  getSelection,
566
582
  isInSelection,
567
583
  type: rowDragSourceType,
584
+ sourceComponentRef: gridRef, // Reference to the originating component
568
585
  };
569
586
  }
570
587
  rowDragProps.dragSourceItem.onDragStart = () => {
@@ -572,6 +589,9 @@ function GridComponent(props) {
572
589
  // reset the selection to just this one node if it's not already selected
573
590
  setSelection([item]);
574
591
  }
592
+ if (onDragStart) {
593
+ onDragStart(item, rowDragProps.dragSourceItem);
594
+ }
575
595
  };
576
596
  if (canRowDrag) {
577
597
  rowDragProps.canDrag = () => canRowDrag(item, rowDragProps.dragSourceItem);
@@ -582,10 +602,17 @@ function GridComponent(props) {
582
602
  rowDragProps.dragPreviewOptions = dragPreviewOptions;
583
603
  }
584
604
 
605
+ // Add onDragEnd callback
606
+ if (onDragEnd) {
607
+ rowDragProps.onDragEnd = onDragEnd;
608
+ }
609
+
585
610
  // Add drag preview rendering
586
611
  rowDragProps.getDragProxy = getCustomDragProxy ?
587
612
  (dragItem) => getCustomDragProxy(item, getSelection()) :
588
613
  null; // Let GlobalDragProxy handle the default case
614
+
615
+ rowCanDrag = true;
589
616
  }
590
617
  if (areRowsDropTarget) {
591
618
  WhichRow = DropTargetGridRow;
@@ -611,7 +638,20 @@ function GridComponent(props) {
611
638
  }
612
639
 
613
640
  }
641
+
642
+ // Use pre-created ref for row height measurement (sample first few rows)
643
+ let rowRef = null;
644
+ if (autoAdjustPageSizeToHeight && measurementPhase === PHASES_MEASURING &&
645
+ !isHeaderRow && index >= 1 && index <= 5) { // Sample first 5 data rows (index 1-5)
646
+ 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);
650
+ }
651
+ }
652
+
614
653
  return <WhichRow
654
+ ref={rowRef}
615
655
  columnsConfig={localColumnsConfig}
616
656
  columnProps={columnProps}
617
657
  fields={fields}
@@ -622,7 +662,9 @@ function GridComponent(props) {
622
662
  isSelected={isSelected}
623
663
  isHovered={hovered}
624
664
  showHovers={showHovers}
625
- showSelectHandle={showSelectHandle}
665
+ showRowHandle={showRowHandle}
666
+ rowCanSelect={rowCanSelect}
667
+ rowCanDrag={rowCanDrag}
626
668
  index={index}
627
669
  alternatingInterval={alternatingInterval}
628
670
  alternateRowBackgrounds={alternateRowBackgrounds}
@@ -816,19 +858,107 @@ function GridComponent(props) {
816
858
  marker.remove();
817
859
  cachedDragElements.current = null;
818
860
  },
819
- calculatePageSize = (containerHeight) => {
820
- const
821
- headerHeight = showHeaders ? 50 : 0,
822
- footerHeight = !disablePagination ? 50 : 0,
823
- availableHeight = containerHeight - headerHeight - footerHeight,
824
- maxClassNormal = styles.GRID_ROW_MAX_HEIGHT_NORMAL, // e.g. max-h-[40px]
825
- rowNormalHeight = parseInt(maxClassNormal.match(/\d+/)[0]);
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
+ calculatePageSize = (containerHeight, useActualMeasurements = false) => {
874
+ // Phase 1: Initial calculation using estimated heights
875
+ if (!useActualMeasurements || measurementPhase === PHASES_INITIAL) {
876
+ const
877
+ headerHeight = showHeaders ? 50 : 0,
878
+ footerHeight = !disablePagination ? 50 : 0,
879
+ availableHeight = containerHeight - headerHeight - footerHeight,
880
+ maxClassNormal = styles.GRID_ROW_MAX_HEIGHT_NORMAL, // e.g. max-h-[40px]
881
+ rowNormalHeight = parseInt(maxClassNormal.match(/\d+/)[0]);
882
+
883
+ let pageSize = Math.floor(availableHeight / rowNormalHeight);
884
+ // Add 20% buffer for better measurement accuracy
885
+ pageSize = Math.floor(pageSize * 1.2);
886
+ if (pageSize < 1) {
887
+ pageSize = 1;
888
+ }
889
+ return pageSize;
890
+ }
826
891
 
827
- let pageSize = Math.floor(availableHeight / rowNormalHeight);
828
- if (pageSize < 1) {
829
- pageSize = 1;
892
+ // 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;
897
+
898
+ // Measure actual header height
899
+ if (showHeaders && headerRowRef.current) {
900
+ actualHeaderHeight = headerRowRef.current.offsetHeight || headerRowRef.current.clientHeight || 50;
901
+ }
902
+
903
+ // Measure actual pagination toolbar height
904
+ if (!disablePagination && paginationToolbarRef.current) {
905
+ actualFooterHeight = paginationToolbarRef.current.offsetHeight || paginationToolbarRef.current.clientHeight || 50;
906
+ }
907
+
908
+ const availableHeight = containerHeight - actualHeaderHeight - actualFooterHeight;
909
+ let pageSize = Math.floor(availableHeight / actualRowHeight);
910
+
911
+ if (pageSize < 1) {
912
+ pageSize = 1;
913
+ }
914
+ return pageSize;
915
+ }
916
+
917
+ // Fallback to Phase 1 logic
918
+ return calculatePageSize(containerHeight, false);
919
+ },
920
+ measureActualRowHeights = () => {
921
+ if (CURRENT_MODE !== UI_MODE_WEB || !gridContainerRef.current) {
922
+ return;
923
+ }
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;
929
+ }
930
+
931
+ let totalHeight = 0;
932
+ 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++;
940
+ }
941
+ }
942
+ });
943
+
944
+ if (measuredCount > 0) {
945
+ 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);
949
+
950
+ // Clear sample refs for next measurement cycle
951
+ sampleRowsRef.current = [];
952
+
953
+ // Recalculate pageSize with actual measurements
954
+ if (lastMeasuredContainerHeight > 0) {
955
+ const newPageSize = calculatePageSize(lastMeasuredContainerHeight, true);
956
+ console.log(`[Grid] Optimized pageSize: ${newPageSize} (was ${Repository.pageSize})`);
957
+ if (newPageSize !== Repository.pageSize) {
958
+ Repository.setPageSize(newPageSize);
959
+ }
960
+ }
830
961
  }
831
- return pageSize;
832
962
  },
833
963
  adjustPageSizeToHeight = (e) => {
834
964
  if (CURRENT_MODE !== UI_MODE_WEB) { // TODO: Remove this conditional, and don't even do the double render for RN
@@ -846,9 +976,19 @@ function GridComponent(props) {
846
976
  if (doAdjustment) {
847
977
  const containerHeight = e.nativeEvent.layout.height;
848
978
  if (containerHeight > 0) {
849
- const pageSize = calculatePageSize(containerHeight);
979
+ setLastMeasuredContainerHeight(containerHeight);
980
+
981
+ // 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
+
850
985
  if (pageSize !== Repository.pageSize) {
851
986
  Repository.setPageSize(pageSize);
987
+
988
+ // Trigger Phase 2: Enable measurement mode after render
989
+ if (measurementPhase === PHASES_INITIAL) {
990
+ setMeasurementPhase(PHASES_MEASURING);
991
+ }
852
992
  }
853
993
  }
854
994
  }
@@ -1006,8 +1146,6 @@ function GridComponent(props) {
1006
1146
  (async () => {
1007
1147
 
1008
1148
  // second call -- do other necessary setup
1009
-
1010
-
1011
1149
  let columnsConfigVariable = columnsConfig,
1012
1150
  localColumnsConfig = [],
1013
1151
  savedLocalColumnsConfig,
@@ -1100,6 +1238,20 @@ function GridComponent(props) {
1100
1238
  if (!Repository.isAutoLoad) {
1101
1239
  Repository.reload();
1102
1240
  }
1241
+
1242
+ // Reset measurement phase and recalculate pageSize if auto-adjust is enabled
1243
+ if (autoAdjustPageSizeToHeight && lastMeasuredContainerHeight > 0) {
1244
+ console.log(`[Grid] Filters changed - resetting pageSize measurement`);
1245
+ setMeasurementPhase(PHASES_INITIAL);
1246
+ setMeasuredRowHeight(null);
1247
+ sampleRowsRef.current = [];
1248
+
1249
+ // Recalculate pageSize with fresh measurements
1250
+ const pageSize = calculatePageSize(lastMeasuredContainerHeight, false);
1251
+ if (pageSize !== Repository.pageSize) {
1252
+ Repository.setPageSize(pageSize);
1253
+ }
1254
+ }
1103
1255
  },
1104
1256
  onChangeSorters = () => {
1105
1257
  if (!Repository.isAutoLoad) {
@@ -1125,7 +1277,7 @@ function GridComponent(props) {
1125
1277
  applySelectorSelected();
1126
1278
  Repository.resumeEvents();
1127
1279
 
1128
- if (((Repository.isRemote && !Repository.isLoaded) || forceLoadOnRender) && !disableLoadOnRender) { // default remote repositories to load on render, optionally force or disable load on render
1280
+ if (((Repository.isRemote && !Repository.isLoaded && !Repository.isLoading) || forceLoadOnRender) && !disableLoadOnRender) { // default remote repositories to load on render, optionally force or disable load on render
1129
1281
  Repository.load();
1130
1282
  }
1131
1283
 
@@ -1151,6 +1303,26 @@ function GridComponent(props) {
1151
1303
 
1152
1304
  }, [selectorId, selectorSelected]);
1153
1305
 
1306
+ // Effect to trigger row height measurement after render
1307
+ useEffect(() => {
1308
+ if (measurementPhase === PHASES_MEASURING && data && data.length > 0) {
1309
+ // Small delay to ensure DOM is fully rendered
1310
+ const timer = setTimeout(() => {
1311
+ measureActualRowHeights();
1312
+ }, 100);
1313
+ return () => clearTimeout(timer);
1314
+ }
1315
+ }, [measurementPhase, data]);
1316
+
1317
+ // Effect to reset measurement state when autoAdjustPageSizeToHeight changes
1318
+ useEffect(() => {
1319
+ if (autoAdjustPageSizeToHeight) {
1320
+ setMeasurementPhase(PHASES_INITIAL);
1321
+ setMeasuredRowHeight(null);
1322
+ sampleRowsRef.current = [];
1323
+ }
1324
+ }, [autoAdjustPageSizeToHeight]);
1325
+
1154
1326
  if (canUser && !canUser('view')) {
1155
1327
  return <Unauthorized />;
1156
1328
  }
@@ -1203,6 +1375,7 @@ function GridComponent(props) {
1203
1375
  showMoreOnly = true;
1204
1376
  }
1205
1377
  listFooterComponent = <PaginationToolbar
1378
+ ref={paginationToolbarRef}
1206
1379
  Repository={Repository}
1207
1380
  self={self}
1208
1381
  toolbarItems={footerToolbarItemComponents}
@@ -1297,7 +1470,7 @@ function GridComponent(props) {
1297
1470
  'border-grey-300',
1298
1471
  );
1299
1472
  if (props.className) {
1300
- className += props.className;
1473
+ className += ' ' + props.className;
1301
1474
  }
1302
1475
 
1303
1476
  grid = <VStackNative
@@ -1321,7 +1494,7 @@ function GridComponent(props) {
1321
1494
  className={clsx(
1322
1495
  'gridContainer',
1323
1496
  'w-full',
1324
- 'h-full',
1497
+ // 'h-full',
1325
1498
  'flex-1',
1326
1499
  'min-h-[40px]',
1327
1500
  gridContainerBorderClassName,
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useMemo, } from 'react';
1
+ import { useState, useEffect, useMemo, forwardRef, } from 'react';
2
2
  import {
3
3
  Box,
4
4
  HStack,
@@ -30,9 +30,7 @@ import SortDown from '../Icons/SortDown.js';
30
30
  import SortUp from '../Icons/SortUp.js';
31
31
  import _ from 'lodash';
32
32
 
33
- // This was broken out from Grid simply so we can memoize it
34
-
35
- export default function GridHeaderRow(props) {
33
+ export default forwardRef(function GridHeaderRow(props, ref) {
36
34
  let {
37
35
  canColumnsReorder,
38
36
  canColumnsResize,
@@ -49,7 +47,7 @@ export default function GridHeaderRow(props) {
49
47
  isInlineEditorShown,
50
48
  areRowsDragSource,
51
49
  showColumnsSelector,
52
- showSelectHandle,
50
+ showRowHandle,
53
51
  } = props,
54
52
  styles = UiGlobals.styles,
55
53
  sortFn = Repository && Repository.getSortFn(),
@@ -467,18 +465,10 @@ export default function GridHeaderRow(props) {
467
465
  />}
468
466
  </Pressable>;
469
467
  });
470
- if (showSelectHandle) {
471
- headerColumns.unshift(<Box
472
- key="RowSelectHandle"
473
- className="Spacer-RowSelectHandle px-2 items-center justify-center flex-none w-[40px]"
474
- >
475
- <Icon as={Arcs} className={`Arcs w-[20px] h-[20px] text-[#aaa]`} />
476
- </Box>);
477
- }
478
- if (areRowsDragSource) {
468
+ if (showRowHandle) {
479
469
  headerColumns.unshift(<Box
480
- key="spacer"
481
- className="Spacer w-[7px]"
470
+ key="RowHandleSpacer"
471
+ className="Spacer-RowHandle w-[40px] flex-none"
482
472
  />);
483
473
  }
484
474
  if (!hideNavColumn) {
@@ -488,6 +478,7 @@ export default function GridHeaderRow(props) {
488
478
  };
489
479
 
490
480
  return <HStack
481
+ ref={ref}
491
482
  style={{
492
483
  scrollbarWidth: 'none',
493
484
  }}
@@ -515,6 +506,8 @@ export default function GridHeaderRow(props) {
515
506
  sortFn,
516
507
  sortField,
517
508
  isInlineEditorShown,
509
+ areRowsDragSource,
510
+ showRowHandle,
518
511
  ]);
519
- }
512
+ });
520
513