@onehat/ui 0.3.225 → 0.3.227

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.3.225",
3
+ "version": "0.3.227",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -209,8 +209,8 @@ function GridComponent(props) {
209
209
  [isLoading, setIsLoading] = useState(false),
210
210
  [localColumnsConfig, setLocalColumnsConfigRaw] = useState([]),
211
211
  [isDragMode, setIsDragMode] = useState(false),
212
- [dragRowSlot, setDragRowSlot] = useState(null),
213
- [dragRowIx, setDragRowIx] = useState(),
212
+ [cachedDragElements, setCachedDragElements] = useState(null),
213
+ [dragRow, setDragRow] = useState(),
214
214
  getIsExpanded = (index) => {
215
215
  return !!expandedRowsRef.current[index];
216
216
  },
@@ -454,20 +454,22 @@ function GridComponent(props) {
454
454
  rowReorderProps.proxyPositionRelativeToParent = true;
455
455
  rowReorderProps.getParentNode = (node) => node.parentElement.parentElement.parentElement;
456
456
  rowReorderProps.getProxy = getReorderProxy;
457
- }
458
- if (areRowsDragSource) {
459
- rowDragProps.isDragSource = true;
460
- rowDragProps.dragSourceType = rowDragSourceType;
461
- rowDragProps.dragSourceItem = getRowDragSourceItem ? getRowDragSourceItem(item) : { id: item.id };
462
- }
463
- if (areRowsDropTarget) {
464
- rowDragProps.isDropTarget = true;
465
- rowDragProps.dropTargetAccept = dropTargetAccept;
466
- rowDragProps.onDrop = (droppedItem) => {
467
- // TODO: the item is somehow getting stale
468
- // might have something to do with memoization
469
- onRowDrop(item, droppedItem);
470
- };
457
+ } else {
458
+ // Don't allow drag/drop from withDnd while reordering
459
+ if (areRowsDragSource) {
460
+ rowDragProps.isDragSource = true;
461
+ rowDragProps.dragSourceType = rowDragSourceType;
462
+ rowDragProps.dragSourceItem = getRowDragSourceItem ? getRowDragSourceItem(item) : { id: item.id };
463
+ }
464
+ if (areRowsDropTarget) {
465
+ rowDragProps.isDropTarget = true;
466
+ rowDragProps.dropTargetAccept = dropTargetAccept;
467
+ rowDragProps.onDrop = (droppedItem) => {
468
+ // TODO: the item is somehow getting stale
469
+ // might have something to do with memoization
470
+ onRowDrop(item, droppedItem);
471
+ };
472
+ }
471
473
  }
472
474
  return <GridRow
473
475
  columnsConfig={localColumnsConfig}
@@ -510,10 +512,9 @@ function GridComponent(props) {
510
512
  parent = row.parentElement,
511
513
  parentRect = parent.getBoundingClientRect(),
512
514
  proxy = row.cloneNode(true),
513
- top = rowRect.top - parentRect.top,
514
- dragRowIx = Array.from(parent.children).indexOf(row)
515
+ top = rowRect.top - parentRect.top;
515
516
 
516
- setDragRowIx(dragRowIx); // the ix of which record is being dragged
517
+ setDragRow(row);
517
518
 
518
519
  proxy.style.top = top + 'px';
519
520
  proxy.style.left = '20px';
@@ -525,212 +526,116 @@ function GridComponent(props) {
525
526
  proxy.style.border = '1px solid #000';
526
527
  return proxy;
527
528
  },
528
- onRowReorderDragStart = (info, e, proxy, node) => {
529
- // console.log('onRowReorderDragStart', info, e, proxy, node);
530
- const
531
- proxyRect = proxy.getBoundingClientRect(),
532
- row = node.parentElement.parentElement,
533
- parent = row.parentElement,
534
- parentRect = parent.getBoundingClientRect(),
535
- rows = _.filter(parent.children, (childNode) => {
536
- return childNode.getBoundingClientRect().height !== 0; // Skip zero-height children
537
- }),
538
- currentY = proxyRect.top - parentRect.top, // top position of pointer, relative to page
539
- headerRowIx = showHeaders ? 0 : null,
540
- firstActualRowIx = showHeaders ? 1 : 0;
541
-
542
- // Figure out which index the user wants
543
- let newIx = 0;
544
- _.each(rows, (child, ix, all) => {
529
+ getOverState = (rows, currentY, mouseX) => {
530
+ // determines which row the mouse is over
531
+ // and whether the marker should be moved to the top or bottom of the row
532
+ let newIx = 0,
533
+ useBottom = false;
534
+ _.each(rows, (row, ix) => {
545
535
  const
546
- rect = child.getBoundingClientRect(), // rect of the row of this iteration
536
+ rect = row.getBoundingClientRect(),
547
537
  {
548
538
  top,
549
539
  bottom,
550
540
  height,
551
541
  } = rect,
552
- compensatedTop = top - parentRect.top,
553
- compensatedBottom = bottom - parentRect.top,
554
- halfHeight = height / 2;
555
-
556
- if (ix === headerRowIx || child === proxy) {
557
- return;
558
- }
559
- if (ix === firstActualRowIx) {
560
- // first row
561
- if (currentY < compensatedTop + halfHeight) {
562
- newIx = firstActualRowIx;
563
- return false;
564
- } else if (currentY < compensatedBottom) {
565
- newIx = firstActualRowIx + 1;
566
- return false;
567
- }
568
- return;
569
- } else if (ix === all.length -1) {
570
- // last row
571
- if (currentY < compensatedTop + halfHeight) {
572
- newIx = ix;
573
- return false;
574
- }
575
- newIx = ix +1;
576
- return false;
577
- }
578
-
579
- // all other rows
580
- if (compensatedTop <= currentY && currentY < compensatedTop + halfHeight) {
542
+ isOver = (
543
+ mouseX >= rect.left &&
544
+ mouseX <= rect.right &&
545
+ currentY >= rect.top &&
546
+ currentY <= rect.bottom
547
+ );
548
+ if (isOver) {
581
549
  newIx = ix;
582
- return false;
583
- } else if (currentY < compensatedBottom) {
584
- newIx = ix +1;
550
+
551
+ const
552
+ halfHeight = height / 2,
553
+ isOverTopHalf = currentY < top + halfHeight;
554
+
555
+ useBottom = !isOverTopHalf;
556
+
585
557
  return false;
586
558
  }
587
559
  });
560
+ return {
561
+ ix: newIx,
562
+ useBottom,
563
+ };
564
+ },
565
+ onRowReorderDragStart = (info, e, proxy, node) => {
566
+ // console.log('onRowReorderDragStart', info, e, proxy, node);
567
+
568
+ const
569
+ proxyRect = proxy.getBoundingClientRect(),
570
+ row = node.parentElement.parentElement,
571
+ flatlist = row.parentElement,
572
+ flatlistRect = flatlist.getBoundingClientRect(),
573
+ rows = _.filter(flatlist.childNodes, (childNode, ix) => {
574
+ const
575
+ isZeroHeight = childNode.getBoundingClientRect().height === 0,
576
+ isProxy = childNode === proxy,
577
+ isHeader = showHeaders && ix === 0;
578
+ return !isZeroHeight && !isProxy && !isHeader;
579
+ }),
580
+ currentY = proxyRect.top - flatlistRect.top, // top position of pointer, relative to page
581
+ { ix, useBottom } = getOverState(rows, currentY, e.clientX);
588
582
 
589
- let useBottom = false;
590
- if (!rows[newIx] || rows[newIx] === proxy) {
591
- newIx--;
592
- useBottom = true;
593
- }
594
583
 
595
584
  // Render marker showing destination location
596
585
  const
597
- rowContainerRect = rows[newIx].getBoundingClientRect(),
598
- top = (useBottom ? rowContainerRect.bottom : rowContainerRect.top) - parentRect.top - parseInt(parent.style.borderWidth), // get relative Y position
599
- gridRowsContainer = gridRef.current._listRef._scrollRef.childNodes[0],
600
- gridRowsContainerRect = gridRowsContainer.getBoundingClientRect(),
586
+ rowContainerRect = rows[ix].getBoundingClientRect(),
587
+ top = (useBottom ? rowContainerRect.bottom : rowContainerRect.top)
588
+ - flatlistRect.top
589
+ - (flatlist.style.borderWidth ? parseInt(flatlist.style.borderWidth) : 0), // get relative Y position
601
590
  marker = document.createElement('div');
602
-
603
591
  marker.style.position = 'absolute';
604
- marker.style.top = top -4 + 'px'; // -4 so it's always visible
592
+ marker.style.top = top + 'px';
605
593
  marker.style.height = '4px';
606
- marker.style.width = gridRowsContainerRect.width + 'px';
594
+ marker.style.width = flatlistRect.width + 'px';
607
595
  marker.style.backgroundColor = '#f00';
596
+ flatlist.appendChild(marker);
608
597
 
609
- gridRowsContainer.appendChild(marker);
610
-
611
- setDragRowSlot({ ix: newIx, marker, useBottom, });
598
+ setCachedDragElements({ ix, useBottom, marker, rows, });
612
599
  },
613
600
  onRowReorderDrag = (info, e, proxy, node) => {
614
601
  // console.log('onRowReorderDrag', info, e, proxy, node);
615
602
  const
603
+ { marker, rows, } = cachedDragElements,
616
604
  proxyRect = proxy.getBoundingClientRect(),
617
605
  row = node.parentElement.parentElement,
618
- parent = row.parentElement,
619
- parentRect = parent.getBoundingClientRect(),
620
- rows = _.filter(parent.children, (childNode) => {
621
- return childNode.getBoundingClientRect().height !== 0; // Skip zero-height children
622
- }),
623
- currentY = proxyRect.top - parentRect.top, // top position of pointer, relative to page
624
- headerRowIx = showHeaders ? 0 : null,
625
- firstActualRowIx = showHeaders ? 1 : 0;
626
-
627
- // Figure out which index the user wants
628
- let newIx = 0;
629
- _.each(rows, (child, ix, all) => {
630
- const
631
- rect = child.getBoundingClientRect(), // rect of the row of this iteration
632
- {
633
- top,
634
- bottom,
635
- height,
636
- } = rect,
637
- compensatedTop = top - parentRect.top,
638
- compensatedBottom = bottom - parentRect.top,
639
- halfHeight = height / 2;
640
-
641
- if (ix === headerRowIx || child === proxy) {
642
- return;
643
- }
644
- if (ix === firstActualRowIx) {
645
- // first row
646
- if (currentY < compensatedTop + halfHeight) {
647
- newIx = firstActualRowIx;
648
- return false;
649
- } else if (currentY < compensatedBottom) {
650
- newIx = firstActualRowIx + 1;
651
- return false;
652
- }
653
- return;
654
- } else if (ix === all.length -1) {
655
- // last row
656
- if (currentY < compensatedTop + halfHeight) {
657
- newIx = ix;
658
- return false;
659
- }
660
- newIx = ix +1;
661
- return false;
662
- }
663
-
664
- // all other rows
665
- if (compensatedTop <= currentY && currentY < compensatedTop + halfHeight) {
666
- newIx = ix;
667
- return false;
668
- } else if (currentY < compensatedBottom) {
669
- newIx = ix +1;
670
- return false;
671
- }
672
- });
673
-
674
- let useBottom = false;
675
- if (!rows[newIx] || rows[newIx] === proxy) {
676
- newIx--;
677
- useBottom = true;
678
- }
606
+ flatlist = row.parentElement,
607
+ flatlistRect = flatlist.getBoundingClientRect(),
608
+ currentY = proxyRect.top - flatlistRect.top, // top position of pointer, relative to page
609
+ { ix, useBottom } = getOverState(rows, currentY, e.clientX);
610
+
679
611
 
680
- // Render marker showing destination location (can't use regular render cycle because this div is absolutely positioned on page)
612
+ // move marker to new location
681
613
  const
682
- rowContainerRect = rows[newIx].getBoundingClientRect(),
683
- top = (useBottom ? rowContainerRect.bottom : rowContainerRect.top) - parentRect.top - parseInt(parent.style.borderWidth); // get relative Y position
684
- let marker = dragRowSlot?.marker;
685
- if (marker) {
686
- marker.style.top = top -4 + 'px'; // -4 so it's always visible
687
- }
688
-
689
- setDragRowSlot({ ix: newIx, marker, useBottom, });
690
- // console.log('onRowReorderDrag slot', newIx);
614
+ rowContainerRect = rows[ix].getBoundingClientRect(),
615
+ top = (useBottom ? rowContainerRect.bottom : rowContainerRect.top)
616
+ - flatlistRect.top
617
+ - (flatlist.style.borderWidth ? parseInt(flatlist.style.borderWidth) : 0); // get relative Y position
618
+ marker.style.top = top + 'px';
691
619
 
620
+ setCachedDragElements({ ix, useBottom, marker, rows, });
692
621
  },
693
622
  onRowReorderDragStop = (delta, e, config) => {
694
623
  // console.log('onRowReorderDragStop', delta, e, config);
695
624
  const
696
- dropIx = dragRowSlot.ix,
697
- compensatedDragIx = showHeaders ? dragRowIx -1 : dragRowIx, // ix, without taking header row into account
698
- compensatedDropIx = showHeaders ? dropIx -1 : dropIx, // // ix, without taking header row into account
699
- dropPosition = dragRowSlot.useBottom ? DROP_POSITION_AFTER : DROP_POSITION_BEFORE;
700
-
701
- let shouldMove = true,
702
- finalDropIx = compensatedDropIx;
703
-
704
- if (dropPosition === DROP_POSITION_BEFORE) {
705
- if (dragRowIx === dropIx || dragRowIx === dropIx -1) { // basically before or after the drag row's origin
706
- // Same as origin; don't do anything
707
- shouldMove = false;
708
- } else {
709
- // Actually move it
710
- if (!Repository) { // If we're just going to be switching rows, rather than telling server to reorder rows, so maybe adjust finalDropIx...
711
- if (finalDropIx > compensatedDragIx) { // if we're dropping *before* the origin ix
712
- finalDropIx = finalDropIx -1; // Because we're using BEFORE, we want to switch with the row *prior to* the ix we're dropping before
713
- }
714
- }
715
- }
716
- } else if (dropPosition === DROP_POSITION_AFTER) {
717
- // Only happens on the very last row. Everything else is BEFORE...
718
- if (dragRowIx === dropIx) {
719
- // Same as origin; don't do anything
720
- shouldMove = false;
721
- }
722
- }
625
+ { ix: dropIx, useBottom, marker, rows, } = cachedDragElements,
626
+ dragIx = rows.indexOf(dragRow);
723
627
 
628
+ const shouldMove = dropIx !== dragIx;
724
629
  if (shouldMove) {
725
630
  // Update the row with the new ix
726
631
  let dragRecord,
727
632
  dropRecord;
728
633
  if (Repository) {
729
- dragRecord = Repository.getByIx(compensatedDragIx);
730
- dropRecord = Repository.getByIx(finalDropIx);
731
-
732
- Repository.reorder(dragRecord, dropRecord, dropPosition);
733
-
634
+ dragRecord = Repository.getByIx(dragIx);
635
+ dropRecord = Repository.getByIx(dropIx);
636
+ if (dropRecord) {
637
+ Repository.reorder(dragRecord, dropRecord, useBottom ? DROP_POSITION_AFTER : DROP_POSITION_BEFORE);
638
+ }
734
639
  } else {
735
640
  function arrayMove(arr, fromIndex, toIndex) {
736
641
  var element = arr[fromIndex];
@@ -741,10 +646,8 @@ function GridComponent(props) {
741
646
  }
742
647
  }
743
648
 
744
- if (dragRowSlot) {
745
- dragRowSlot.marker.remove();
746
- }
747
- setDragRowSlot(null);
649
+ marker.remove();
650
+ setCachedDragElements(null);
748
651
  },
749
652
  calculatePageSize = (containerHeight) => {
750
653
  const
@@ -1025,15 +928,13 @@ function GridComponent(props) {
1025
928
  }
1026
929
 
1027
930
  let grid = <FlatList
931
+ testID="flatlist"
1028
932
  ref={gridRef}
1029
933
  scrollEnabled={CURRENT_MODE === UI_MODE_WEB}
1030
934
  nestedScrollEnabled={true}
1031
935
  contentContainerStyle={{
1032
936
  overflow: 'auto',
1033
- borderWidth: isDragMode ? styles.REORDER_BORDER_WIDTH : 0,
1034
- borderColor: isDragMode ? styles.REORDER_BORDER_COLOR : null,
1035
- borderStyle: styles.REORDER_BORDER_STYLE,
1036
- flex: 1,
937
+ // flex: 1,
1037
938
  }}
1038
939
  refreshing={isLoading}
1039
940
  onRefresh={pullToRefresh ? onRefresh : null}
@@ -1054,6 +955,7 @@ function GridComponent(props) {
1054
955
  bg="trueGray.100"
1055
956
  {...flatListProps}
1056
957
  />
958
+
1057
959
  if (CURRENT_MODE === UI_MODE_REACT_NATIVE) {
1058
960
  grid = <ScrollView flex={1} w="100%">{grid}</ScrollView>
1059
961
  }
@@ -1067,8 +969,22 @@ function GridComponent(props) {
1067
969
  }
1068
970
  }
1069
971
 
972
+ const gridContainerBorderProps = {};
973
+ if (isLoading) {
974
+ gridContainerBorderProps.borderTopWidth = 2;
975
+ gridContainerBorderProps.borderTopColor = '#f00';
976
+ } else if (isDragMode) {
977
+ gridContainerBorderProps.borderWidth = styles.REORDER_BORDER_WIDTH;
978
+ gridContainerBorderProps.borderColor = styles.REORDER_BORDER_COLOR;
979
+ gridContainerBorderProps.borderStyle = styles.REORDER_BORDER_STYLE;
980
+ } else {
981
+ gridContainerBorderProps.borderTopWidth = 1
982
+ gridContainerBorderProps.borderTopColor = 'trueGray.300';
983
+ }
984
+
1070
985
  grid = <Column
1071
986
  {...testProps('Grid')}
987
+ testID="outerContainer"
1072
988
  ref={containerRef}
1073
989
  w="100%"
1074
990
  bg={bg}
@@ -1079,11 +995,19 @@ function GridComponent(props) {
1079
995
  >
1080
996
  {topToolbar}
1081
997
 
1082
- <Column ref={gridContainerRef} w="100%" flex={1} minHeight={40} borderTopWidth={isLoading ? 2 : 1} borderTopColor={isLoading ? '#f00' : 'trueGray.300'} onClick={() => {
1083
- if (!isDragMode && !isInlineEditorShown) {
1084
- deselectAll();
1085
- }
1086
- }}>
998
+ <Column
999
+ testID="gridContainer"
1000
+ ref={gridContainerRef}
1001
+ w="100%"
1002
+ flex={1}
1003
+ minHeight={40}
1004
+ {...gridContainerBorderProps}
1005
+ onClick={() => {
1006
+ if (!isDragMode && !isInlineEditorShown) {
1007
+ deselectAll();
1008
+ }
1009
+ }}
1010
+ >
1087
1011
  {grid}
1088
1012
  </Column>
1089
1013
 
@@ -27,7 +27,8 @@ function GridRow(props) {
27
27
  bg,
28
28
  item,
29
29
  isInlineEditorShown,
30
- isDragSource = false,
30
+ isDraggable = false, // withDraggable
31
+ isDragSource = false, // withDnd
31
32
  isOver = false,
32
33
  } = props,
33
34
  styles = UiGlobals.styles;
@@ -40,12 +41,6 @@ function GridRow(props) {
40
41
  isPhantom = item.isPhantom,
41
42
  hash = item?.hash || item;
42
43
 
43
- if (props.dragSourceRef) {
44
- rowProps.ref = props.dragSourceRef;
45
- }
46
- if (props.dropTargetRef) {
47
- rowProps.ref = props.dropTargetRef;
48
- }
49
44
  return useMemo(() => {
50
45
  const renderColumns = (item) => {
51
46
  if (_.isArray(columnsConfig)) {
@@ -188,26 +183,36 @@ function GridRow(props) {
188
183
  rowProps.borderWidth = 0;
189
184
  rowProps.borderColor = null;
190
185
  }
186
+
187
+ let rowContents = <>
188
+ {(isDragSource || isDraggable) && <RowDragHandle />}
189
+ {isPhantom && <Box position="absolute" bg="#f00" h={2} w={2} t={0} l={0} />}
190
+
191
+ {renderColumns(item)}
192
+
193
+ {!hideNavColumn && <AngleRight
194
+ color={styles.GRID_NAV_COLUMN_COLOR}
195
+ variant="ghost"
196
+ w={30}
197
+ alignSelf="center"
198
+ mx={3}
199
+ />}
200
+ </>;
201
+
202
+ if (props.dragSourceRef) {
203
+ rowContents = <Row flexGrow={1} flex={1} w="100%" bg={bg} ref={props.dragSourceRef}>{rowContents}</Row>;
204
+ }
205
+ if (props.dropTargetRef) {
206
+ rowContents = <Row flexGrow={1} flex={1} w="100%" bg={bg} ref={props.dropTargetRef}>{rowContents}</Row>;
207
+ }
208
+
191
209
  return <Row
192
210
  alignItems="center"
193
211
  flexGrow={1}
194
212
  {...rowProps}
195
213
  bg={bg}
196
214
  key={hash}
197
- >
198
- {isDragSource && <RowDragHandle />}
199
- {isPhantom && <Box position="absolute" bg="#f00" h={2} w={2} t={0} l={0} />}
200
-
201
- {renderColumns(item)}
202
-
203
- {!hideNavColumn && <AngleRight
204
- color={styles.GRID_NAV_COLUMN_COLOR}
205
- variant="ghost"
206
- w={30}
207
- alignSelf="center"
208
- mx={3}
209
- />}
210
- </Row>;
215
+ >{rowContents}</Row>;
211
216
  }, [
212
217
  columnsConfig,
213
218
  columnProps,
@@ -11,7 +11,7 @@ function RowDragHandle(props) {
11
11
  return <Column
12
12
  testID="HeaderReorderHandle"
13
13
  bg="trueGray.100"
14
- h="100%"
14
+ // h="100%"
15
15
  w={3}
16
16
  alignItems="center"
17
17
  justifyContent="center"