@onehat/ui 0.4.95 → 0.4.97

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/ui",
3
- "version": "0.4.95",
3
+ "version": "0.4.97",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -118,77 +118,81 @@ function Container(props) {
118
118
  westWidthRef = useRef(westInitialWidth),
119
119
  [isReady, setIsReady] = useState(false),
120
120
  [isComponentsDisabled, setIsComponentsDisabled] = useState(false),
121
- [localNorthIsCollapsed, setLocalNorthIsCollapsed] = useState(northInitialIsCollapsed),
122
- [localSouthIsCollapsed, setLocalSouthIsCollapsed] = useState(southInitialIsCollapsed),
123
- [localEastIsCollapsed, setLocalEastIsCollapsed] = useState(eastInitialIsCollapsed),
124
- [localWestIsCollapsed, setLocalWestIsCollapsed] = useState(westInitialIsCollapsed),
121
+ localNorthIsCollapsedRef = useRef(northInitialIsCollapsed),
122
+ localSouthIsCollapsedRef = useRef(southInitialIsCollapsed),
123
+ localEastIsCollapsedRef = useRef(eastInitialIsCollapsed),
124
+ localWestIsCollapsedRef = useRef(westInitialIsCollapsed),
125
125
  setNorthIsCollapsed = (bool) => {
126
126
  if (setExternalNorthIsCollapsed) {
127
127
  setExternalNorthIsCollapsed(bool);
128
128
  } else {
129
- setLocalNorthIsCollapsed(bool);
129
+ localNorthIsCollapsedRef.current = bool;
130
130
  }
131
131
 
132
132
  if (id) {
133
133
  setSaved(id + '-northIsCollapsed', bool);
134
134
  }
135
+ forceUpdate();
135
136
  },
136
137
  getNorthIsCollapsed = () => {
137
138
  if (setExternalNorthIsCollapsed) {
138
139
  return northIsCollapsed;
139
140
  }
140
- return localNorthIsCollapsed;
141
+ return localNorthIsCollapsedRef.current;
141
142
  },
142
143
  setSouthIsCollapsed = (bool) => {
143
144
  if (setExternalSouthIsCollapsed) {
144
145
  setExternalSouthIsCollapsed(bool);
145
146
  } else {
146
- setLocalSouthIsCollapsed(bool);
147
+ localSouthIsCollapsedRef.current = bool;
147
148
  }
148
149
 
149
150
  if (id) {
150
151
  setSaved(id + '-southIsCollapsed', bool);
151
152
  }
153
+ forceUpdate();
152
154
  },
153
155
  getSouthIsCollapsed = () => {
154
156
  if (setExternalSouthIsCollapsed) {
155
157
  return southIsCollapsed;
156
158
  }
157
- return localSouthIsCollapsed;
159
+ return localSouthIsCollapsedRef.current;
158
160
  },
159
161
  setEastIsCollapsed = (bool) => {
160
162
  if (setExternalEastIsCollapsed) {
161
163
  setExternalEastIsCollapsed(bool);
162
164
  } else {
163
- setLocalEastIsCollapsed(bool);
165
+ localEastIsCollapsedRef.current = bool;
164
166
  }
165
167
 
166
168
  if (id) {
167
169
  setSaved(id + '-eastIsCollapsed', bool);
168
170
  }
171
+ forceUpdate();
169
172
  },
170
173
  getEastIsCollapsed = () => {
171
174
  if (setExternalEastIsCollapsed) {
172
175
  return eastIsCollapsed;
173
176
  }
174
- return localEastIsCollapsed;
177
+ return localEastIsCollapsedRef.current;
175
178
  },
176
179
  setWestIsCollapsed = (bool) => {
177
180
  if (setExternalWestIsCollapsed) {
178
181
  setExternalWestIsCollapsed(bool);
179
182
  } else {
180
- setLocalWestIsCollapsed(bool);
183
+ localWestIsCollapsedRef.current = bool;
181
184
  }
182
185
 
183
186
  if (id) {
184
187
  setSaved(id + '-westIsCollapsed', bool);
185
188
  }
189
+ forceUpdate();
186
190
  },
187
191
  getWestIsCollapsed = () => {
188
192
  if (setExternalWestIsCollapsed) {
189
193
  return westIsCollapsed;
190
194
  }
191
- return localWestIsCollapsed;
195
+ return localWestIsCollapsedRef.current;
192
196
  },
193
197
  setNorthHeight = (height) => {
194
198
  if (!getNorthIsCollapsed()) {
@@ -348,7 +352,7 @@ function Container(props) {
348
352
  return null;
349
353
  }
350
354
 
351
- let componentProps = {},
355
+ let componentProps = { _panel: { ...center?.props?._panel }, },
352
356
  wrapperProps = null,
353
357
  centerComponent = null,
354
358
  northComponent = null,
@@ -360,15 +364,15 @@ function Container(props) {
360
364
  westComponent = null,
361
365
  westSplitter = null;
362
366
 
363
- componentProps.isCollapsible = false;
364
- componentProps.isDisabled = isDisabled || isComponentsDisabled;
367
+ componentProps._panel.isCollapsible = false;
368
+ componentProps._panel.isDisabled = isDisabled || isComponentsDisabled;
365
369
  centerComponent = cloneElement(center, componentProps);
366
370
  if (north) {
367
- componentProps = {};
371
+ componentProps = { _panel: { ...north.props?._panel }, };
368
372
  wrapperProps = {};
369
373
 
370
- componentProps.isDisabled = isDisabled || isComponentsDisabled;
371
- componentProps.className = (north.props.className || '') + ' h-full w-full';
374
+ componentProps._panel.isDisabled = isDisabled || isComponentsDisabled;
375
+ componentProps._panel.className = 'h-full w-full ' + (north.props.className || '');
372
376
  wrapperProps.onLayout = (e) => {
373
377
  const height = parseFloat(e.nativeEvent.layout.height);
374
378
  if (height && height !== northHeight) {
@@ -387,9 +391,9 @@ function Container(props) {
387
391
  wrapperProps.style = { height: northHeight, };
388
392
  }
389
393
  }
390
- componentProps.collapseDirection = VERTICAL;
391
- componentProps.isCollapsed = getNorthIsCollapsed();
392
- componentProps.setIsCollapsed = setNorthIsCollapsed;
394
+ componentProps._panel.collapseDirection = VERTICAL;
395
+ componentProps._panel.isCollapsed = getNorthIsCollapsed();
396
+ componentProps._panel.setIsCollapsed = setNorthIsCollapsed;
393
397
  if (isWeb && northIsResizable) {
394
398
  northSplitter = <Splitter
395
399
  mode={VERTICAL}
@@ -402,11 +406,11 @@ function Container(props) {
402
406
  </BoxNative>;
403
407
  }
404
408
  if (south) {
405
- componentProps = {};
409
+ componentProps = { _panel: { ...south.props?._panel }, };
406
410
  wrapperProps = {};
407
411
 
408
- componentProps.isDisabled = isDisabled || isComponentsDisabled;
409
- componentProps.className = (south.props.className || '') + ' h-full w-full';
412
+ componentProps._panel.isDisabled = isDisabled || isComponentsDisabled;
413
+ componentProps._panel.className = 'h-full w-full ' + (south.props.className || '');
410
414
  wrapperProps.onLayout = (e) => {
411
415
  const height = parseFloat(e.nativeEvent.layout.height);
412
416
  if (height && height !== getSouthHeight()) {
@@ -425,9 +429,9 @@ function Container(props) {
425
429
  wrapperProps.style = { height: southHeight, };
426
430
  }
427
431
  }
428
- componentProps.collapseDirection = VERTICAL;
429
- componentProps.isCollapsed = getSouthIsCollapsed();
430
- componentProps.setIsCollapsed = setSouthIsCollapsed;
432
+ componentProps._panel.collapseDirection = VERTICAL;
433
+ componentProps._panel.isCollapsed = getSouthIsCollapsed();
434
+ componentProps._panel.setIsCollapsed = setSouthIsCollapsed;
431
435
  if (isWeb && southIsResizable) {
432
436
  southSplitter = <Splitter
433
437
  mode={VERTICAL}
@@ -440,11 +444,11 @@ function Container(props) {
440
444
  </BoxNative>;
441
445
  }
442
446
  if (east) {
443
- componentProps = {};
447
+ componentProps = { _panel: { ...east.props?._panel }, };
444
448
  wrapperProps = {};
445
449
 
446
- componentProps.isDisabled = isDisabled || isComponentsDisabled;
447
- componentProps.className = (east.props.className || '') + ' h-full w-full';
450
+ componentProps._panel.isDisabled = isDisabled || isComponentsDisabled;
451
+ componentProps._panel.className = 'h-full w-full ' + (east.props.className || '');
448
452
  wrapperProps.onLayout = (e) => {
449
453
  const width = parseFloat(e.nativeEvent.layout.width);
450
454
  if (width && width !== getEastWidth()) {
@@ -463,9 +467,9 @@ function Container(props) {
463
467
  wrapperProps.style = { width: eastWidth, };
464
468
  }
465
469
  }
466
- componentProps.collapseDirection = HORIZONTAL;
467
- componentProps.isCollapsed = getEastIsCollapsed();
468
- componentProps.setIsCollapsed = setEastIsCollapsed;
470
+ componentProps._panel.collapseDirection = HORIZONTAL;
471
+ componentProps._panel.isCollapsed = getEastIsCollapsed();
472
+ componentProps._panel.setIsCollapsed = setEastIsCollapsed;
469
473
  if (isWeb && eastIsResizable) {
470
474
  eastSplitter = <Splitter
471
475
  mode={HORIZONTAL}
@@ -478,11 +482,11 @@ function Container(props) {
478
482
  </BoxNative>;
479
483
  }
480
484
  if (west) {
481
- componentProps = {};
485
+ componentProps = { _panel: { ...west.props?._panel }, };
482
486
  wrapperProps = {};
483
487
 
484
- componentProps.isDisabled = isDisabled || isComponentsDisabled;
485
- componentProps.className = (west.props.className || '') + ' h-full w-full';
488
+ componentProps._panel.isDisabled = isDisabled || isComponentsDisabled;
489
+ componentProps._panel.className = 'h-full w-full ' + (west.props.className || '');
486
490
  wrapperProps.onLayout = (e) => {
487
491
  const width = parseFloat(e.nativeEvent.layout.width);
488
492
  if (width && width !== getWestWidth()) {
@@ -501,9 +505,9 @@ function Container(props) {
501
505
  wrapperProps.style = { width: westWidth, };
502
506
  }
503
507
  }
504
- componentProps.collapseDirection = HORIZONTAL;
505
- componentProps.isCollapsed = getWestIsCollapsed();
506
- componentProps.setIsCollapsed = setWestIsCollapsed;
508
+ componentProps._panel.collapseDirection = HORIZONTAL;
509
+ componentProps._panel.isCollapsed = getWestIsCollapsed();
510
+ componentProps._panel.setIsCollapsed = setWestIsCollapsed;
507
511
  if (isWeb && westIsResizable) {
508
512
  westSplitter = <Splitter
509
513
  mode={HORIZONTAL}
@@ -517,17 +521,17 @@ function Container(props) {
517
521
  }
518
522
  return <VStack className="Container-all w-full flex-1">
519
523
  {northComponent}
520
- {!northIsCollapsed && !localNorthIsCollapsed && northSplitter}
524
+ {!getNorthIsCollapsed() && northSplitter}
521
525
  <HStack className="Container-mid w-full flex-[100]">
522
526
  {westComponent}
523
- {!westIsCollapsed && !localWestIsCollapsed && westSplitter}
527
+ {!getWestIsCollapsed() && westSplitter}
524
528
  <VStack className="Container-center h-full overflow-auto flex-[100]">
525
529
  {centerComponent}
526
530
  </VStack>
527
- {!eastIsCollapsed && !localEastIsCollapsed && eastSplitter}
531
+ {!getEastIsCollapsed() && eastSplitter}
528
532
  {eastComponent}
529
533
  </HStack>
530
- {!southIsCollapsed && !localSouthIsCollapsed && southSplitter}
534
+ {!getSouthIsCollapsed() && southSplitter}
531
535
  {southComponent}
532
536
  </VStack>;
533
537
  }
@@ -522,6 +522,8 @@ function Form(props) {
522
522
  }
523
523
  let {
524
524
  type,
525
+ editorType: itemEditorType,
526
+ viewerType,
525
527
  title,
526
528
  name,
527
529
  isEditable = true,
@@ -560,12 +562,18 @@ function Form(props) {
560
562
  }
561
563
  if (!type) {
562
564
  if (isEditable) {
563
- const {
564
- type: t,
565
- ...p
566
- } = propertyDef?.editorType;
567
- type = t;
568
- editorTypeProps = p;
565
+ if (itemEditorType) {
566
+ type = itemEditorType;
567
+ } else {
568
+ const {
569
+ type: t,
570
+ ...p
571
+ } = propertyDef?.editorType;
572
+ type = t;
573
+ editorTypeProps = p;
574
+ }
575
+ } else if (viewerType) {
576
+ type = viewerType;
569
577
  } else if (propertyDef?.viewerType) {
570
578
  const {
571
579
  type: t,
@@ -1247,7 +1255,7 @@ function Form(props) {
1247
1255
 
1248
1256
  if (inArray(editorType, [EDITOR_TYPE__SIDE, EDITOR_TYPE__SMART, EDITOR_TYPE__WINDOWED]) &&
1249
1257
  isSingle && getEditorMode() === EDITOR_MODE__EDIT &&
1250
- (onBack || (onViewMode && !disableView))) {
1258
+ (onBack || onViewMode)) {
1251
1259
  modeHeader = <Toolbar>
1252
1260
  <HStack className="flex-1 items-center">
1253
1261
  {onBack &&
@@ -5,6 +5,7 @@ import {
5
5
  HStack,
6
6
  Pressable,
7
7
  // ScrollView,
8
+ Text,
8
9
  VStack,
9
10
  VStackNative,
10
11
  } from '@project-components/Gluestack';
@@ -187,7 +188,17 @@ function GridComponent(props) {
187
188
  canRowsReorder = false,
188
189
  canRowDrag, // optional fn to customize whether each row can be dragged
189
190
  canRowAcceptDrop, // optional fn to customize whether each node can accept a dropped item: (targetItem, draggedItem) => boolean
190
- getCustomDragProxy, // optional fn to render custom drag preview: (item, selection) => ReactElement
191
+ dragProxyField,
192
+ getCustomDragProxy = (item, selection) => { // optional fn to render custom drag preview: (item, selection) => ReactElement
193
+ let selectionCount = selection?.length || 1,
194
+ displayText = dragProxyField ? item[dragProxyField] : (item.displayValue || 'Selected Row');
195
+ return <VStack className="bg-white border border-gray-300 rounded-lg p-3 shadow-lg max-w-[200px]">
196
+ <Text className="font-semibold text-gray-800">{displayText}</Text>
197
+ {selectionCount > 1 &&
198
+ <Text className="text-sm text-gray-600">(+{selectionCount -1} more item{selectionCount > 2 ? 's' : ''})</Text>
199
+ }
200
+ </VStack>;
201
+ },
191
202
  dragPreviewOptions, // optional object for drag preview positioning options
192
203
  areRowsDragSource = false,
193
204
  rowDragSourceType,
@@ -260,6 +271,7 @@ function GridComponent(props) {
260
271
  } = props,
261
272
  styles = UiGlobals.styles,
262
273
  id = props.id || props.self?.path,
274
+ entities = Repository ? (Repository.isRemote ? Repository.entities : Repository.getEntitiesOnPage()) : data,
263
275
  localColumnsConfigKey = id && id + '-localColumnsConfig',
264
276
  [hasUnserializableColumns] = useState(() => {
265
277
  return !isSerializable(columnsConfig); // (runs only once, when the component is first created)
@@ -280,6 +292,8 @@ function GridComponent(props) {
280
292
  measuredRowsRef = useRef([]),
281
293
  footerToolbarRef = useRef(null),
282
294
  rowRefs = useRef([]),
295
+ previousEntitiesLength = useRef(0),
296
+ hasRemeasuredAfterRowsAppeared = useRef(false),
283
297
  [isInited, setIsInited] = useState(false),
284
298
  [isReady, setIsReady] = useState(false),
285
299
  [isLoading, setIsLoading] = useState(false),
@@ -409,7 +423,16 @@ function GridComponent(props) {
409
423
  }
410
424
  },
411
425
  getFooterToolbarItems = () => {
412
- const items = _.map(additionalToolbarButtons, (config, ix) => getIconButtonFromConfig(config, ix, self));
426
+ // Process additionalToolbarButtons to evaluate getIsButtonDisabled functions
427
+ const processedButtons = _.map(additionalToolbarButtons, (config) => {
428
+ const processedConfig = { ...config };
429
+ // If the button has an getIsButtonDisabled function, evaluate it with current selection
430
+ if (_.isFunction(config.getIsButtonDisabled)) {
431
+ processedConfig.isDisabled = config.getIsButtonDisabled(selection);
432
+ }
433
+ return processedConfig;
434
+ });
435
+ const items = _.map(processedButtons, (config, ix) => getIconButtonFromConfig(config, ix, self));
413
436
  if (canRowsReorder && CURRENT_MODE === UI_MODE_WEB) { // DND is currently web-only TODO: implement for RN
414
437
  items.unshift(<IconButton
415
438
  {...testProps('reorderBtn')}
@@ -462,33 +485,41 @@ function GridComponent(props) {
462
485
  }
463
486
  break;
464
487
  case DOUBLE_CLICK:
465
- if (!isSelected) { // If a row was already selected when double-clicked, the first click will deselect it,
466
- onRowClick(item, e); // so reselect it
467
- }
468
-
469
- if (UiGlobals.doubleClickingGridRowOpensEditorInViewMode) { // global setting
470
- if (onView) {
471
- if (canUser && !canUser(VIEW)) { // permissions
472
- return;
473
- }
474
- onView(!props.isEditorViewOnly);
488
+ if (editorType === EDITOR_TYPE__SIDE) {
489
+ // For side-editors, a double-click just acts like a single click
490
+ if (!getIsEditorShown()) {
491
+ onRowClick(item, e); // sets selection
475
492
  }
476
493
  } else {
477
- let canDoEdit = false,
478
- canDoView = false;
479
- if (onEdit && canUser && canUser(EDIT) && (!canRecordBeEdited || canRecordBeEdited(selection)) && !props.disableEdit && !isEditorViewOnly) {
480
- canDoEdit = true;
481
- } else
482
- if (onView && canUser && canUser(VIEW) && !props.disableView) {
483
- canDoView = true;
494
+ if (!isSelected) { // If a row was already selected when double-clicked, the first click will deselect it,
495
+ onRowClick(item, e); // so reselect it
484
496
  }
497
+ if (UiGlobals.doubleClickingGridRowOpensEditorInViewMode) { // global setting
498
+ if (onView) {
499
+ if (canUser && !canUser(VIEW)) { // permissions
500
+ return;
501
+ }
502
+ onView(!props.isEditorViewOnly);
503
+ }
504
+ } else {
505
+ let canDoEdit = false,
506
+ canDoView = false;
507
+ if (onEdit && canUser && canUser(EDIT) && (!canRecordBeEdited || canRecordBeEdited(selection)) && !props.disableEdit && !isEditorViewOnly) {
508
+ canDoEdit = true;
509
+ } else
510
+ if (onView && canUser && canUser(VIEW) && !props.disableView) {
511
+ canDoView = true;
512
+ }
485
513
 
486
- if (canDoEdit) {
487
- onEdit();
488
- } else if (canDoView) {
489
- onView();
514
+ if (canDoEdit) {
515
+ onEdit();
516
+ } else if (canDoView) {
517
+ onView();
518
+ }
490
519
  }
520
+
491
521
  }
522
+
492
523
  break;
493
524
  case TRIPLE_CLICK:
494
525
  break;
@@ -1118,12 +1149,12 @@ function GridComponent(props) {
1118
1149
  // Keep the current estimated pageSize, just hide the loading overlay
1119
1150
  }
1120
1151
  },
1121
- adjustPageSizeToHeight = (containerHeight) => {
1152
+ adjustPageSizeToHeight = (containerHeight, forceRemeasure = false) => {
1122
1153
  if (!Repository || Repository.isDestroyed) { // This method gets delayed, so it's possible for Repository to have been destroyed. Check for this
1123
1154
  return;
1124
1155
  }
1125
1156
  if (DEBUG) {
1126
- console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight A`);
1157
+ console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight A forceRemeasure=${forceRemeasure}`);
1127
1158
  }
1128
1159
 
1129
1160
  let doAdjustment = autoAdjustPageSizeToHeight;
@@ -1135,11 +1166,11 @@ function GridComponent(props) {
1135
1166
  console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight A2 doAdjustment=${doAdjustment}, autoAdjustPageSizeToHeight=${autoAdjustPageSizeToHeight}, UiGlobals.autoAdjustPageSizeToHeight=${UiGlobals.autoAdjustPageSizeToHeight}, containerHeight=${containerHeight}`);
1136
1167
  }
1137
1168
 
1138
- // Only proceed if height changed significantly
1169
+ // Only proceed if height changed significantly or forced
1139
1170
  const
1140
1171
  heightChanged = Math.abs(containerHeight - lastMeasuredContainerHeight) > 5, // 5px tolerance
1141
1172
  isFirstMeasurement = lastMeasuredContainerHeight === 0;
1142
- if (containerHeight > 0 && (isFirstMeasurement || heightChanged)) {
1173
+ if (containerHeight > 0 && (isFirstMeasurement || heightChanged || forceRemeasure)) {
1143
1174
  if (editorType === EDITOR_TYPE__SIDE && getIsEditorShown()) {
1144
1175
  // When side editor is shown, skip adjustment to avoid layout thrashing
1145
1176
  console.log(`${getMeasurementPhase()}, adjustPageSizeToHeight A4 height changed significantly, but side editor is shown, skipping remeasurement`);
@@ -1543,6 +1574,43 @@ function GridComponent(props) {
1543
1574
  }
1544
1575
  }, [autoAdjustPageSizeToHeight]);
1545
1576
 
1577
+ // Reset measurement when rows were first empty then became populated
1578
+ useEffect(() => {
1579
+ const
1580
+ currentLength = entities?.length || 0,
1581
+ wasEmpty = previousEntitiesLength.current === 0,
1582
+ isNowPopulated = currentLength > 0,
1583
+ hasPhantomRecord = entities?.some(entity => entity?.isPhantom);
1584
+
1585
+ // NOTE: The Repository was reloading when a phantom record was added,
1586
+ // and this broke the Editor because selection was being reset to zero.
1587
+ // This is because adjustPageSizeToHeight calls setPageSize,
1588
+ // which calls _onChangePagination, which calls reload.
1589
+ // The reloaded repository doesn’t get the new phantom record,
1590
+ // so it’s not found, and selection goes to zero.
1591
+ // So we skip this adjustment when there is a phantom record.
1592
+
1593
+ // Only remeasure the FIRST time rows appear after being empty
1594
+ if (!hasPhantomRecord && autoAdjustPageSizeToHeight && wasEmpty && isNowPopulated && !hasRemeasuredAfterRowsAppeared.current) {
1595
+ // Rows just appeared for the first time - restart measurement cycle to use actual heights
1596
+ if (DEBUG) {
1597
+ console.log(`${getMeasurementPhase()}, useEffect 5 - rows appeared for first time, restarting measurement cycle`);
1598
+ }
1599
+ hasRemeasuredAfterRowsAppeared.current = true;
1600
+
1601
+ setMeasurementPhase(PHASES__INITIAL);
1602
+ setMeasuredRowHeight(null);
1603
+ measuredRowsRef.current = [];
1604
+
1605
+ // Trigger remeasurement with force flag since actual rows are now available
1606
+ if (lastMeasuredContainerHeight > 0) {
1607
+ adjustPageSizeToHeight(lastMeasuredContainerHeight, true);
1608
+ }
1609
+ }
1610
+
1611
+ previousEntitiesLength.current = currentLength;
1612
+ }, [entities?.length, autoAdjustPageSizeToHeight]);
1613
+
1546
1614
  if (canUser && !canUser('view')) {
1547
1615
  return <Unauthorized />;
1548
1616
  }
@@ -1560,13 +1628,24 @@ function GridComponent(props) {
1560
1628
  // first time through, render a placeholder so we can get container dimensions
1561
1629
  return <VStackNative
1562
1630
  onLayout={(e) => {
1563
- if (DEBUG) {
1564
- console.log(`${getMeasurementPhase()}, placeholder onLayout, call adjustPageSizeToHeight()`);
1565
- }
1566
- const containerHeight = e.nativeEvent.layout.height;
1567
- adjustPageSizeToHeight(containerHeight);
1568
- if (DEBUG) {
1569
- console.log(`${getMeasurementPhase()}, placeholder onLayout, call setIsInited(true)`);
1631
+ const hasPhantomRecord = entities?.some(entity => entity?.isPhantom);
1632
+ if (!hasPhantomRecord) {
1633
+ // NOTE: The Repository was reloading when a phantom record was added,
1634
+ // and this broke the Editor because selection was being reset to zero.
1635
+ // This is because adjustPageSizeToHeight calls setPageSize,
1636
+ // which calls _onChangePagination, which calls reload.
1637
+ // The reloaded repository doesn’t get the new phantom record,
1638
+ // so it’s not found, and selection goes to zero.
1639
+ // So we skip this adjustment when there is a phantom record.
1640
+
1641
+ if (DEBUG) {
1642
+ console.log(`${getMeasurementPhase()}, placeholder onLayout, call adjustPageSizeToHeight()`);
1643
+ }
1644
+ const containerHeight = e.nativeEvent.layout.height;
1645
+ adjustPageSizeToHeight(containerHeight);
1646
+ if (DEBUG) {
1647
+ console.log(`${getMeasurementPhase()}, placeholder onLayout, call setIsInited(true)`);
1648
+ }
1570
1649
  }
1571
1650
  setIsInited(true);
1572
1651
  }}
@@ -1579,7 +1658,6 @@ function GridComponent(props) {
1579
1658
  }
1580
1659
 
1581
1660
  // Actual data to show in the grid
1582
- const entities = Repository ? (Repository.isRemote ? Repository.entities : Repository.getEntitiesOnPage()) : data;
1583
1661
  let rowData = [...entities]; // don't use the original array, make a new one so alterations to it are temporary
1584
1662
  if (showHeaders) {
1585
1663
  rowData.unshift({ id: 'headerRow' });
@@ -1744,14 +1822,11 @@ function GridComponent(props) {
1744
1822
  )}
1745
1823
  >
1746
1824
  {grid}
1747
- {/* Loading overlay during measurement phases to prevent visual flashing */}
1748
- {autoAdjustPageSizeToHeight &&
1749
- (getMeasurementPhase() === PHASES__INITIAL || getMeasurementPhase() === PHASES__MEASURING) &&
1750
- entities?.length > 0 && (
1825
+ {/* Load overlay during initial phase to prevent visual flashing * /
1826
+ autoAdjustPageSizeToHeight && getMeasurementPhase() === PHASES__INITIAL &&
1751
1827
  <VStack className="absolute inset-0 z-10 bg-white">
1752
1828
  <Loading />
1753
- </VStack>
1754
- )}
1829
+ </VStack>*/}
1755
1830
  </VStack>
1756
1831
 
1757
1832
  {listFooterComponent}
@@ -61,6 +61,7 @@ function withAlert(WrappedComponent) {
61
61
  'flex-1',
62
62
  'items-start',
63
63
  'justify-center',
64
+ 'p-4',
64
65
  )}>
65
66
  <Text className={clsx(
66
67
  'withAlert-Text',
@@ -164,7 +165,7 @@ function withAlert(WrappedComponent) {
164
165
  }),
165
166
  onOk: () => hideModal(),
166
167
  canClose: true,
167
- h: 200,
168
+ h: 250,
168
169
  w: 400,
169
170
  whichModal: 'info',
170
171
  });