@onehat/ui 0.3.227 → 0.3.231
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 +2 -2
- package/src/Components/Grid/Grid.js +236 -208
- package/src/Components/Grid/GridRow.js +15 -7
- package/src/Components/Hoc/withDnd.js +138 -27
- package/src/Components/Hoc/withSelection.js +33 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onehat/ui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.231",
|
|
4
4
|
"description": "Base UI for OneHat apps",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"license": "UNLICENSED",
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@gluestack-style/react": "^0.2.38",
|
|
29
|
-
"@gluestack-ui/themed": "^
|
|
29
|
+
"@gluestack-ui/themed": "^1.1.22",
|
|
30
30
|
"@hookform/resolvers": "^3.3.1",
|
|
31
31
|
"@k-renwick/colour-mixer": "^1.2.1",
|
|
32
32
|
"@onehat/data": "^1.20.0",
|
|
@@ -50,12 +50,12 @@ import getIconButtonFromConfig from '../../Functions/getIconButtonFromConfig.js'
|
|
|
50
50
|
import testProps from '../../Functions/testProps.js';
|
|
51
51
|
import nbToRgb from '../../Functions/nbToRgb.js';
|
|
52
52
|
import Loading from '../Messages/Loading.js';
|
|
53
|
-
import GridHeaderRow from '
|
|
54
|
-
import GridRow from './GridRow.js';
|
|
53
|
+
import GridHeaderRow from '../Grid/GridHeaderRow.js';
|
|
54
|
+
import GridRow, { DragSourceDropTargetGridRow, DragSourceGridRow, DropTargetGridRow } from './GridRow.js';
|
|
55
55
|
import IconButton from '../Buttons/IconButton.js';
|
|
56
56
|
import ExpandButton from '../Buttons/ExpandButton.js';
|
|
57
57
|
import PaginationToolbar from '../Toolbar/PaginationToolbar.js';
|
|
58
|
-
import NoRecordsFound from '
|
|
58
|
+
import NoRecordsFound from '../Grid/NoRecordsFound.js';
|
|
59
59
|
import Toolbar from '../Toolbar/Toolbar.js';
|
|
60
60
|
import NoReorderRows from '../Icons/NoReorderRows.js';
|
|
61
61
|
import ReorderRows from '../Icons/ReorderRows.js';
|
|
@@ -204,12 +204,13 @@ function GridComponent(props) {
|
|
|
204
204
|
gridContainerRef = useRef(),
|
|
205
205
|
isAddingRef = useRef(),
|
|
206
206
|
expandedRowsRef = useRef({}),
|
|
207
|
+
cachedDragElements = useRef(),
|
|
208
|
+
idsRef = useRef([]),
|
|
207
209
|
[isInited, setIsInited] = useState(false),
|
|
208
210
|
[isReady, setIsReady] = useState(false),
|
|
209
211
|
[isLoading, setIsLoading] = useState(false),
|
|
210
212
|
[localColumnsConfig, setLocalColumnsConfigRaw] = useState([]),
|
|
211
213
|
[isDragMode, setIsDragMode] = useState(false),
|
|
212
|
-
[cachedDragElements, setCachedDragElements] = useState(null),
|
|
213
214
|
[dragRow, setDragRow] = useState(),
|
|
214
215
|
getIsExpanded = (index) => {
|
|
215
216
|
return !!expandedRowsRef.current[index];
|
|
@@ -303,8 +304,7 @@ function GridComponent(props) {
|
|
|
303
304
|
},
|
|
304
305
|
getFooterToolbarItems = () => {
|
|
305
306
|
const items = _.map(additionalToolbarButtons, (config, ix) => getIconButtonFromConfig(config, ix, self));
|
|
306
|
-
|
|
307
|
-
if (canRowsReorder) {
|
|
307
|
+
if (canRowsReorder && CURRENT_MODE === UI_MODE_WEB) { // DND is currently web-only TODO: implement for RN
|
|
308
308
|
items.unshift(<IconButton
|
|
309
309
|
key="reorderBtn"
|
|
310
310
|
parent={self}
|
|
@@ -332,159 +332,184 @@ function GridComponent(props) {
|
|
|
332
332
|
rowProps = getRowProps && !isHeaderRow ? getRowProps(item) : {},
|
|
333
333
|
isSelected = !isHeaderRow && !disableWithSelection && isInSelection(item);
|
|
334
334
|
|
|
335
|
-
let rowComponent =
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
335
|
+
let rowComponent =
|
|
336
|
+
<Pressable
|
|
337
|
+
// {...testProps(Repository ? Repository.schema.name + '-' + item.id : item.id)}
|
|
338
|
+
onPress={(e) => {
|
|
339
|
+
if (e.preventDefault && e.cancelable) {
|
|
340
|
+
e.preventDefault();
|
|
341
|
+
}
|
|
342
|
+
if (isHeaderRow || isDragMode) {
|
|
343
|
+
return
|
|
344
|
+
}
|
|
345
|
+
if (CURRENT_MODE === UI_MODE_WEB) {
|
|
346
|
+
switch (e.detail) {
|
|
347
|
+
case 1: // single click
|
|
348
|
+
onRowClick(item, e); // sets selection
|
|
349
|
+
if (onEditorRowClick) {
|
|
350
|
+
onEditorRowClick(item, index, e);
|
|
351
|
+
}
|
|
352
|
+
break;
|
|
353
|
+
case 2: // double click
|
|
354
|
+
if (!isSelected) { // If a row was already selected when double-clicked, the first click will deselect it,
|
|
355
|
+
onRowClick(item, e); // so reselect it
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (UiGlobals.doubleClickingGridRowOpensEditorInViewMode) { // global setting
|
|
359
|
+
if (onView) {
|
|
360
|
+
onView(true);
|
|
355
361
|
}
|
|
356
|
-
|
|
357
|
-
if (
|
|
358
|
-
if (
|
|
359
|
-
|
|
360
|
-
}
|
|
361
|
-
} else {
|
|
362
|
-
if (onEdit) {
|
|
363
|
-
if (verifyCanEdit && !verifyCanEdit(selection)) {
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
onEdit();
|
|
362
|
+
} else {
|
|
363
|
+
if (onEdit) {
|
|
364
|
+
if (verifyCanEdit && !verifyCanEdit(selection)) {
|
|
365
|
+
return;
|
|
367
366
|
}
|
|
367
|
+
onEdit();
|
|
368
368
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
} else if (CURRENT_MODE === UI_MODE_REACT_NATIVE) {
|
|
375
|
-
onRowClick(item, e); // sets selection
|
|
376
|
-
if (onEditorRowClick) {
|
|
377
|
-
onEditorRowClick(item, index, e);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}}
|
|
381
|
-
onLongPress={(e) => {
|
|
382
|
-
if (e.preventDefault && e.cancelable) {
|
|
383
|
-
e.preventDefault();
|
|
384
|
-
}
|
|
385
|
-
if (isHeaderRow || isDragMode) {
|
|
386
|
-
return
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// context menu
|
|
390
|
-
const selection = [item];
|
|
391
|
-
if (!disableWithSelection) {
|
|
392
|
-
setSelection(selection);
|
|
369
|
+
}
|
|
370
|
+
break;
|
|
371
|
+
case 3: // triple click
|
|
372
|
+
break;
|
|
373
|
+
default:
|
|
393
374
|
}
|
|
394
|
-
|
|
375
|
+
} else if (CURRENT_MODE === UI_MODE_REACT_NATIVE) {
|
|
376
|
+
onRowClick(item, e); // sets selection
|
|
377
|
+
if (onEditorRowClick) {
|
|
395
378
|
onEditorRowClick(item, index, e);
|
|
396
379
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
380
|
+
}
|
|
381
|
+
}}
|
|
382
|
+
onLongPress={(e) => {
|
|
383
|
+
if (e.preventDefault && e.cancelable) {
|
|
384
|
+
e.preventDefault();
|
|
385
|
+
}
|
|
386
|
+
if (isHeaderRow || isDragMode) {
|
|
387
|
+
return
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// context menu
|
|
391
|
+
const selection = [item];
|
|
392
|
+
if (!disableWithSelection) {
|
|
393
|
+
setSelection(selection);
|
|
394
|
+
}
|
|
395
|
+
if (onEditorRowClick) { // e.g. inline editor
|
|
396
|
+
onEditorRowClick(item, index, e);
|
|
397
|
+
}
|
|
398
|
+
if (onContextMenu) {
|
|
399
|
+
onContextMenu(item, e, selection, setSelection);
|
|
400
|
+
}
|
|
401
|
+
}}
|
|
402
|
+
flexDirection="row"
|
|
403
|
+
flexGrow={1}
|
|
404
|
+
>
|
|
405
|
+
{({
|
|
406
|
+
isHovered,
|
|
407
|
+
isFocused,
|
|
408
|
+
isPressed,
|
|
409
|
+
}) => {
|
|
410
|
+
if (isHeaderRow) {
|
|
411
|
+
return <GridHeaderRow
|
|
412
|
+
Repository={Repository}
|
|
413
|
+
columnsConfig={localColumnsConfig}
|
|
414
|
+
setColumnsConfig={setLocalColumnsConfig}
|
|
415
|
+
hideNavColumn={hideNavColumn}
|
|
416
|
+
canColumnsSort={canColumnsSort}
|
|
417
|
+
canColumnsReorder={canColumnsReorder}
|
|
418
|
+
canColumnsResize={canColumnsResize}
|
|
419
|
+
setSelection={setSelection}
|
|
420
|
+
gridRef={gridRef}
|
|
421
|
+
isHovered={isHovered}
|
|
422
|
+
isInlineEditorShown={isInlineEditorShown}
|
|
423
|
+
/>;
|
|
424
|
+
}
|
|
424
425
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
}
|
|
433
|
-
} else if (showHovers && isHovered) {
|
|
434
|
-
mixWith = styles.GRID_ROW_HOVER_BG;
|
|
435
|
-
} else if (alternateRowBackgrounds && index % alternatingInterval === 0) { // i.e. every second line, or every third line
|
|
436
|
-
mixWith = styles.GRID_ROW_ALTERNATE_BG;
|
|
437
|
-
}
|
|
438
|
-
if (mixWith) {
|
|
439
|
-
const
|
|
440
|
-
mixWithObj = nbToRgb(mixWith),
|
|
441
|
-
ratio = mixWithObj.alpha ? 1 - mixWithObj.alpha : 0.5;
|
|
442
|
-
bg = colourMixer.blend(bg, ratio, mixWithObj.color);
|
|
426
|
+
let bg = rowProps.bg || styles.GRID_ROW_BG,
|
|
427
|
+
mixWith;
|
|
428
|
+
if (isSelected) {
|
|
429
|
+
if (showHovers && isHovered) {
|
|
430
|
+
mixWith = styles.GRID_ROW_SELECTED_HOVER_BG;
|
|
431
|
+
} else {
|
|
432
|
+
mixWith = styles.GRID_ROW_SELECTED_BG;
|
|
443
433
|
}
|
|
434
|
+
} else if (showHovers && isHovered) {
|
|
435
|
+
mixWith = styles.GRID_ROW_HOVER_BG;
|
|
436
|
+
} else if (alternateRowBackgrounds && index % alternatingInterval === 0) { // i.e. every second line, or every third line
|
|
437
|
+
mixWith = styles.GRID_ROW_ALTERNATE_BG;
|
|
438
|
+
}
|
|
439
|
+
if (mixWith) {
|
|
444
440
|
const
|
|
445
|
-
|
|
446
|
-
|
|
441
|
+
mixWithObj = nbToRgb(mixWith),
|
|
442
|
+
ratio = mixWithObj.alpha ? 1 - mixWithObj.alpha : 0.5;
|
|
443
|
+
bg = colourMixer.blend(bg, ratio, mixWithObj.color);
|
|
444
|
+
}
|
|
445
|
+
const
|
|
446
|
+
rowReorderProps = {},
|
|
447
|
+
rowDragProps = {};
|
|
448
|
+
let WhichRow = GridRow;
|
|
449
|
+
if (CURRENT_MODE === UI_MODE_WEB) { // DND is currrently web-only TODO: implement for RN
|
|
450
|
+
// Create a method that gets an always-current copy of the selection ids
|
|
451
|
+
const ids = _.map(selection, (item) => item.id);
|
|
452
|
+
idsRef.current = ids;
|
|
453
|
+
const getIds = () => idsRef.current;
|
|
454
|
+
|
|
455
|
+
// assign event handlers
|
|
447
456
|
if (canRowsReorder && isDragMode) {
|
|
448
|
-
|
|
449
|
-
rowReorderProps.
|
|
450
|
-
rowReorderProps.
|
|
451
|
-
|
|
452
|
-
rowReorderProps.
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
+
WhichRow = DragSourceGridRow;
|
|
458
|
+
rowReorderProps.isDragSource = true;
|
|
459
|
+
rowReorderProps.dragSourceType = 'row';
|
|
460
|
+
const dragIx = showHeaders ? index - 1 : index;
|
|
461
|
+
rowReorderProps.dragSourceItem = {
|
|
462
|
+
id: item.id,
|
|
463
|
+
getIds,
|
|
464
|
+
onDrag: (dragState) => {
|
|
465
|
+
onRowReorderDrag(dragState, dragIx);
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
rowReorderProps.onDragEnd = onRowReorderEnd;
|
|
457
469
|
} else {
|
|
458
470
|
// Don't allow drag/drop from withDnd while reordering
|
|
459
471
|
if (areRowsDragSource) {
|
|
472
|
+
WhichRow = DragSourceGridRow;
|
|
460
473
|
rowDragProps.isDragSource = true;
|
|
461
474
|
rowDragProps.dragSourceType = rowDragSourceType;
|
|
462
|
-
|
|
475
|
+
if (getRowDragSourceItem) {
|
|
476
|
+
rowDragProps.dragSourceItem = getRowDragSourceItem(item, getIds);
|
|
477
|
+
} else {
|
|
478
|
+
rowDragProps.dragSourceItem = {
|
|
479
|
+
id: item.id,
|
|
480
|
+
getIds,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
463
483
|
}
|
|
464
484
|
if (areRowsDropTarget) {
|
|
485
|
+
WhichRow = DropTargetGridRow;
|
|
465
486
|
rowDragProps.isDropTarget = true;
|
|
466
487
|
rowDragProps.dropTargetAccept = dropTargetAccept;
|
|
467
488
|
rowDragProps.onDrop = (droppedItem) => {
|
|
468
|
-
|
|
469
|
-
// might have something to do with memoization
|
|
470
|
-
onRowDrop(item, droppedItem);
|
|
489
|
+
onRowDrop(item, droppedItem); // item is what it was dropped on; droppedItem is the dragSourceItem defined above
|
|
471
490
|
};
|
|
472
491
|
}
|
|
492
|
+
if (areRowsDragSource && areRowsDropTarget) {
|
|
493
|
+
WhichRow = DragSourceDropTargetGridRow;
|
|
494
|
+
}
|
|
473
495
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
496
|
+
}
|
|
497
|
+
return <WhichRow
|
|
498
|
+
columnsConfig={localColumnsConfig}
|
|
499
|
+
columnProps={columnProps}
|
|
500
|
+
fields={fields}
|
|
501
|
+
rowProps={rowProps}
|
|
502
|
+
hideNavColumn={hideNavColumn}
|
|
503
|
+
bg={bg}
|
|
504
|
+
item={item}
|
|
505
|
+
isInlineEditorShown={isInlineEditorShown}
|
|
506
|
+
{...rowReorderProps}
|
|
507
|
+
{...rowDragProps}
|
|
508
|
+
|
|
509
|
+
key1={item.id}
|
|
510
|
+
/>;
|
|
511
|
+
}}
|
|
512
|
+
</Pressable>;
|
|
488
513
|
|
|
489
514
|
if (showRowExpander && !isHeaderRow) {
|
|
490
515
|
const isExpanded = getIsExpanded(index);
|
|
@@ -505,31 +530,10 @@ function GridComponent(props) {
|
|
|
505
530
|
}
|
|
506
531
|
return rowComponent;
|
|
507
532
|
},
|
|
508
|
-
getReorderProxy = (node) => {
|
|
509
|
-
const
|
|
510
|
-
row = node.parentElement.parentElement,
|
|
511
|
-
rowRect = row.getBoundingClientRect(),
|
|
512
|
-
parent = row.parentElement,
|
|
513
|
-
parentRect = parent.getBoundingClientRect(),
|
|
514
|
-
proxy = row.cloneNode(true),
|
|
515
|
-
top = rowRect.top - parentRect.top;
|
|
516
|
-
|
|
517
|
-
setDragRow(row);
|
|
518
|
-
|
|
519
|
-
proxy.style.top = top + 'px';
|
|
520
|
-
proxy.style.left = '20px';
|
|
521
|
-
proxy.style.height = rowRect.height + 'px';
|
|
522
|
-
proxy.style.width = rowRect.width + 'px';
|
|
523
|
-
proxy.style.display = 'flex';
|
|
524
|
-
// proxy.style.backgroundColor = '#ccc';
|
|
525
|
-
proxy.style.position = 'absolute';
|
|
526
|
-
proxy.style.border = '1px solid #000';
|
|
527
|
-
return proxy;
|
|
528
|
-
},
|
|
529
533
|
getOverState = (rows, currentY, mouseX) => {
|
|
530
534
|
// determines which row the mouse is over
|
|
531
535
|
// and whether the marker should be moved to the top or bottom of the row
|
|
532
|
-
let newIx =
|
|
536
|
+
let newIx = -1,
|
|
533
537
|
useBottom = false;
|
|
534
538
|
_.each(rows, (row, ix) => {
|
|
535
539
|
const
|
|
@@ -562,71 +566,89 @@ function GridComponent(props) {
|
|
|
562
566
|
useBottom,
|
|
563
567
|
};
|
|
564
568
|
},
|
|
565
|
-
|
|
566
|
-
// console.log('onRowReorderDragStart', info, e, proxy, node);
|
|
567
|
-
|
|
569
|
+
buildCachedDragElements = (dragState) => {
|
|
568
570
|
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
571
|
+
{
|
|
572
|
+
canDrag,
|
|
573
|
+
isDragging,
|
|
574
|
+
clientOffset,
|
|
575
|
+
sourceClientOffset,
|
|
576
|
+
} = dragState,
|
|
577
|
+
|
|
578
|
+
scrollRef = gridRef.current._listRef._scrollRef,
|
|
579
|
+
isUsingScroll = scrollRef.childNodes[0].style.overflow === 'auto',
|
|
580
|
+
flatlist = isUsingScroll ? scrollRef.childNodes[0] : scrollRef,
|
|
572
581
|
flatlistRect = flatlist.getBoundingClientRect(),
|
|
573
582
|
rows = _.filter(flatlist.childNodes, (childNode, ix) => {
|
|
574
583
|
const
|
|
575
584
|
isZeroHeight = childNode.getBoundingClientRect().height === 0,
|
|
576
|
-
isProxy = childNode === proxy,
|
|
577
585
|
isHeader = showHeaders && ix === 0;
|
|
578
|
-
return !isZeroHeight && !
|
|
586
|
+
return !isZeroHeight && !isHeader;
|
|
579
587
|
}),
|
|
580
|
-
|
|
581
|
-
{ ix, useBottom } = getOverState(rows, currentY, e.clientX);
|
|
588
|
+
{ ix, useBottom } = getOverState(rows, clientOffset.y, clientOffset.x);
|
|
582
589
|
|
|
583
590
|
|
|
584
591
|
// Render marker showing destination location
|
|
585
|
-
const
|
|
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
|
|
590
|
-
marker = document.createElement('div');
|
|
592
|
+
const marker = document.createElement('div');
|
|
591
593
|
marker.style.position = 'absolute';
|
|
592
|
-
marker.style.top =
|
|
593
|
-
marker.style.height = '
|
|
594
|
+
marker.style.top = '0px';
|
|
595
|
+
marker.style.height = '8px';
|
|
594
596
|
marker.style.width = flatlistRect.width + 'px';
|
|
595
|
-
marker.style.backgroundColor = '#
|
|
597
|
+
marker.style.backgroundColor = '#ccc';
|
|
596
598
|
flatlist.appendChild(marker);
|
|
599
|
+
|
|
600
|
+
if (ix !== -1) {
|
|
601
|
+
marker.style.visibility = 'visible';
|
|
602
|
+
const
|
|
603
|
+
rowContainerRect = rows[ix].getBoundingClientRect(),
|
|
604
|
+
top = (useBottom ? rowContainerRect.bottom : rowContainerRect.top)
|
|
605
|
+
- flatlistRect.top
|
|
606
|
+
- (flatlist.style.borderWidth ? parseInt(flatlist.style.borderWidth) : 0); // get relative Y position
|
|
607
|
+
marker.style.top = top + 'px';
|
|
608
|
+
} else {
|
|
609
|
+
marker.style.visibility = 'hidden';
|
|
610
|
+
}
|
|
597
611
|
|
|
598
|
-
|
|
612
|
+
return { ix, useBottom, marker, rows };
|
|
599
613
|
},
|
|
600
|
-
onRowReorderDrag = (
|
|
601
|
-
//
|
|
614
|
+
onRowReorderDrag = (dragState, dragIx) => {
|
|
615
|
+
// initial setup
|
|
616
|
+
if (!cachedDragElements.current) {
|
|
617
|
+
cachedDragElements.current = buildCachedDragElements(dragState);
|
|
618
|
+
}
|
|
619
|
+
|
|
602
620
|
const
|
|
603
|
-
{
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
621
|
+
{
|
|
622
|
+
canDrag,
|
|
623
|
+
isDragging,
|
|
624
|
+
clientOffset,
|
|
625
|
+
sourceClientOffset,
|
|
626
|
+
} = dragState,
|
|
627
|
+
{ marker, rows, } = cachedDragElements.current,
|
|
628
|
+
flatlist = gridRef.current._listRef._scrollRef.childNodes[0],
|
|
607
629
|
flatlistRect = flatlist.getBoundingClientRect(),
|
|
608
|
-
|
|
609
|
-
{ ix, useBottom } = getOverState(rows, currentY, e.clientX);
|
|
610
|
-
|
|
630
|
+
{ ix, useBottom } = getOverState(rows, clientOffset.y, clientOffset.x);
|
|
611
631
|
|
|
612
632
|
// move marker to new location
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
633
|
+
if (ix !== -1) {
|
|
634
|
+
marker.style.visibility = 'visible';
|
|
635
|
+
const
|
|
636
|
+
rowContainerRect = rows[ix].getBoundingClientRect(),
|
|
637
|
+
top = (useBottom ? rowContainerRect.bottom : rowContainerRect.top)
|
|
638
|
+
- flatlistRect.top
|
|
639
|
+
- (flatlist.style.borderWidth ? parseInt(flatlist.style.borderWidth) : 0); // get relative Y position
|
|
640
|
+
marker.style.top = top + 'px';
|
|
641
|
+
} else {
|
|
642
|
+
marker.style.visibility = 'hidden';
|
|
643
|
+
}
|
|
619
644
|
|
|
620
|
-
|
|
645
|
+
cachedDragElements.current = { ix, useBottom, marker, rows, dragIx };
|
|
621
646
|
},
|
|
622
|
-
|
|
623
|
-
// console.log('onRowReorderDragStop', delta, e, config);
|
|
647
|
+
onRowReorderEnd = (item, monitor) => {
|
|
624
648
|
const
|
|
625
|
-
{ ix: dropIx, useBottom, marker, rows, } = cachedDragElements,
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
const shouldMove = dropIx !== dragIx;
|
|
629
|
-
if (shouldMove) {
|
|
649
|
+
{ ix: dropIx, useBottom, marker, rows, dragIx } = cachedDragElements.current,
|
|
650
|
+
shouldMove = dropIx !== dragIx;
|
|
651
|
+
if (shouldMove && dropIx !== -1) {
|
|
630
652
|
// Update the row with the new ix
|
|
631
653
|
let dragRecord,
|
|
632
654
|
dropRecord;
|
|
@@ -647,7 +669,7 @@ function GridComponent(props) {
|
|
|
647
669
|
}
|
|
648
670
|
|
|
649
671
|
marker.remove();
|
|
650
|
-
|
|
672
|
+
cachedDragElements.current = null;
|
|
651
673
|
},
|
|
652
674
|
calculatePageSize = (containerHeight) => {
|
|
653
675
|
const
|
|
@@ -934,7 +956,7 @@ function GridComponent(props) {
|
|
|
934
956
|
nestedScrollEnabled={true}
|
|
935
957
|
contentContainerStyle={{
|
|
936
958
|
overflow: 'auto',
|
|
937
|
-
|
|
959
|
+
height: '100%',
|
|
938
960
|
}}
|
|
939
961
|
refreshing={isLoading}
|
|
940
962
|
onRefresh={pullToRefresh ? onRefresh : null}
|
|
@@ -970,15 +992,21 @@ function GridComponent(props) {
|
|
|
970
992
|
}
|
|
971
993
|
|
|
972
994
|
const gridContainerBorderProps = {};
|
|
973
|
-
if (
|
|
974
|
-
gridContainerBorderProps.borderTopWidth = 2;
|
|
975
|
-
gridContainerBorderProps.borderTopColor = '#f00';
|
|
976
|
-
} else if (isDragMode) {
|
|
995
|
+
if (isDragMode) {
|
|
977
996
|
gridContainerBorderProps.borderWidth = styles.REORDER_BORDER_WIDTH;
|
|
978
997
|
gridContainerBorderProps.borderColor = styles.REORDER_BORDER_COLOR;
|
|
979
998
|
gridContainerBorderProps.borderStyle = styles.REORDER_BORDER_STYLE;
|
|
999
|
+
gridContainerBorderProps.borderTopWidth = null;
|
|
1000
|
+
if (isLoading) {
|
|
1001
|
+
gridContainerBorderProps.borderTopColor = '#f00';
|
|
1002
|
+
} else {
|
|
1003
|
+
gridContainerBorderProps.borderTopColor = null;
|
|
1004
|
+
}
|
|
1005
|
+
} else if (isLoading) {
|
|
1006
|
+
gridContainerBorderProps.borderTopWidth = 4;
|
|
1007
|
+
gridContainerBorderProps.borderTopColor = '#f00';
|
|
980
1008
|
} else {
|
|
981
|
-
gridContainerBorderProps.borderTopWidth = 1
|
|
1009
|
+
gridContainerBorderProps.borderTopWidth = 1;
|
|
982
1010
|
gridContainerBorderProps.borderTopColor = 'trueGray.300';
|
|
983
1011
|
}
|
|
984
1012
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useMemo, } from 'react';
|
|
1
|
+
import { useEffect, useMemo, useRef, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
4
|
Row,
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
import getComponentFromType from '../../Functions/getComponentFromType.js';
|
|
11
11
|
import UiGlobals from '../../UiGlobals.js';
|
|
12
12
|
import { withDragSource, withDropTarget } from '../Hoc/withDnd.js';
|
|
13
|
-
import withDraggable from '../Hoc/withDraggable.js';
|
|
14
13
|
import AngleRight from '../Icons/AngleRight.js';
|
|
15
14
|
import RowDragHandle from './RowDragHandle.js';
|
|
16
15
|
import _ from 'lodash';
|
|
@@ -30,6 +29,8 @@ function GridRow(props) {
|
|
|
30
29
|
isDraggable = false, // withDraggable
|
|
31
30
|
isDragSource = false, // withDnd
|
|
32
31
|
isOver = false,
|
|
32
|
+
dragSourceRef,
|
|
33
|
+
dropTargetRef,
|
|
33
34
|
} = props,
|
|
34
35
|
styles = UiGlobals.styles;
|
|
35
36
|
|
|
@@ -199,11 +200,11 @@ function GridRow(props) {
|
|
|
199
200
|
/>}
|
|
200
201
|
</>;
|
|
201
202
|
|
|
202
|
-
if (
|
|
203
|
-
rowContents = <Row flexGrow={1} flex={1} w="100%" bg={bg} ref={
|
|
203
|
+
if (dragSourceRef) {
|
|
204
|
+
rowContents = <Row flexGrow={1} flex={1} w="100%" bg={bg} ref={dragSourceRef}>{rowContents}</Row>;
|
|
204
205
|
}
|
|
205
|
-
if (
|
|
206
|
-
rowContents = <Row flexGrow={1} flex={1} w="100%" bg={bg} ref={
|
|
206
|
+
if (dropTargetRef) {
|
|
207
|
+
rowContents = <Row flexGrow={1} flex={1} w="100%" bg={bg} ref={dropTargetRef}>{rowContents}</Row>;
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
return <Row
|
|
@@ -225,7 +226,14 @@ function GridRow(props) {
|
|
|
225
226
|
hash, // this is an easy way to determine if the data has changed and the item needs to be rerendered
|
|
226
227
|
isInlineEditorShown,
|
|
227
228
|
isOver,
|
|
229
|
+
dragSourceRef,
|
|
230
|
+
dropTargetRef,
|
|
228
231
|
]);
|
|
229
232
|
}
|
|
230
233
|
|
|
231
|
-
export default withDraggable(withDragSource(withDropTarget(GridRow)));
|
|
234
|
+
// export default withDraggable(withDragSource(withDropTarget(GridRow)));
|
|
235
|
+
export default GridRow;
|
|
236
|
+
|
|
237
|
+
export const DragSourceGridRow = withDragSource(GridRow);
|
|
238
|
+
export const DropTargetGridRow = withDropTarget(GridRow);
|
|
239
|
+
export const DragSourceDropTargetGridRow = withDragSource(withDropTarget(GridRow));
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useRef, } from 'react';
|
|
2
|
+
import { useDrag, useDrop, useDragLayer } from 'react-dnd'; // https://react-dnd.github.io/react-dnd/about don't forget the wrapping <DndProvider /> as shown here: https://react-dnd.github.io/react-dnd/docs/api/dnd-provider
|
|
3
|
+
|
|
2
4
|
|
|
3
5
|
// This HOC allows components to be dragged and dropped onto another component.
|
|
4
|
-
// It
|
|
6
|
+
// It can't contrain the movement of the preview item, because react-dnd uses
|
|
7
|
+
// a native drag layer for its proxy, and there are no contraints on that provided by OS.
|
|
8
|
+
// If you need constraints, you can potentially use a CustomDragLayer (see code at bottom)
|
|
9
|
+
// but it will lag behind, compared to what the native drag layer can do
|
|
5
10
|
|
|
6
11
|
export function withDragSource(WrappedComponent) {
|
|
7
12
|
return (props) => {
|
|
@@ -16,47 +21,71 @@ export function withDragSource(WrappedComponent) {
|
|
|
16
21
|
if (!props.dragSourceItem) {
|
|
17
22
|
throw Error('dragSourceItem not defined');
|
|
18
23
|
}
|
|
19
|
-
|
|
24
|
+
|
|
20
25
|
const {
|
|
21
26
|
dragSourceType,
|
|
22
27
|
dragSourceItem,
|
|
28
|
+
dragPreviewOptions = null,
|
|
29
|
+
dragOptions = null,
|
|
30
|
+
dropEffect = null,
|
|
31
|
+
// onDrag,
|
|
32
|
+
onDragEnd = null,
|
|
33
|
+
canDrag = null,
|
|
34
|
+
isDragging = null,
|
|
35
|
+
dragCollect = (monitor, props2) => { // Optional. The collecting function. It should return a plain object of the props to return for injection into your component. It receives two parameters, monitor and props. Read the overview for an introduction to the monitors and the collecting function. See the collecting function described in detail in the next section.
|
|
36
|
+
// monitor fn determines which props from dnd state get passed
|
|
37
|
+
return {
|
|
38
|
+
// canDrag: !!monitor.canDrag(), // Returns trueif no drag operation is in progress, and the owner's canDrag() returns true or is not defined.
|
|
39
|
+
isDragging: !!monitor.isDragging(), // Returns trueif a drag operation is in progress, and either the owner initiated the drag, or its isDragging() is defined and returns true.
|
|
40
|
+
type: monitor.getItemType(), // Returns a string or a symbol identifying the type of the current dragged item. Returns null if no item is being dragged.
|
|
41
|
+
item: monitor.getItem(), // Returns a plain object representing the currently dragged item. Every drag source must specify it by returning an object from its beginDrag() method. Returns nullif no item is being dragged.
|
|
42
|
+
dropResult: monitor.getDropResult(), // Returns a plain object representing the last recorded drop result. The drop targets may optionally specify it by returning an object from their drop()methods. When a chain of drop()is dispatched for the nested targets, bottom up, any parent that explicitly returns its own result from drop()overrides the child drop result previously set by the child. Returns nullif called outside endDrag().
|
|
43
|
+
didDrop: !!monitor.didDrop(), // Returns trueif some drop target has handled the drop event, falseotherwise. Even if a target did not return a drop result, didDrop() returns true. Use it inside endDrag()to test whether any drop target has handled the drop. Returns falseif called outside endDrag().
|
|
44
|
+
initialClientOffset: monitor.getInitialClientOffset(), // Returns the { x, y }client offset of the pointer at the time when the current drag operation has started. Returns nullif no item is being dragged.
|
|
45
|
+
initialSourceClientOffset: monitor.getInitialSourceClientOffset(), // Returns the { x, y }client offset of the drag source component's root DOM node at the time when the current drag operation has started. Returns nullif no item is being dragged.
|
|
46
|
+
clientOffset: monitor.getClientOffset(), // Returns the last recorded { x, y }client offset of the pointer while a drag operation is in progress. Returns nullif no item is being dragged.
|
|
47
|
+
differenceFromInitialOffset: monitor.getDifferenceFromInitialOffset(), // Returns the { x, y }difference between the last recorded client offset of the pointer and the client offset when the current drag operation has started. Returns nullif no item is being dragged.
|
|
48
|
+
sourceClientOffset: monitor.getSourceClientOffset(), // Returns the projected { x, y }client offset of the drag source component's root DOM node, based on its position at the time when the current drag operation has started, and the movement difference. Returns nullif no item is being dragged.
|
|
49
|
+
};
|
|
50
|
+
},
|
|
23
51
|
} = props,
|
|
24
52
|
[dragState, dragSourceRef, dragPreviewRef] = useDrag(() => { // A specification object or a function that creates a specification object.
|
|
25
53
|
// The useDrag hook provides a way to wire your component into the DnD system as a drag source. By passing in a specification into useDrag, you declaratively describe the typeof draggable being generated, the itemobject representing the drag source, what props to collect, and more. The useDraghooks returns a few key items: a set of collected props, and refs that may be attached to drag source and drag preview elements
|
|
54
|
+
|
|
26
55
|
return {
|
|
27
56
|
type: dragSourceType, // Required. This must be either a string or a symbol. Only the drop targets registered for the same type will react to this item.
|
|
28
57
|
item: dragSourceItem, // Required (object or function).
|
|
29
58
|
// When an object, it is a plain JavaScript object describing the data being dragged. This is the only information available to the drop targets about the drag source so it's important to pick the minimal data they need to know. You may be tempted to put a complex reference here, but you should try very hard to avoid doing this because it couples the drag sources and drop targets. It's a good idea to use something like { id }.
|
|
30
59
|
// When a function, it is fired at the beginning of the drag operation and returns an object representing the drag operation (see first bullet). If null is returned, the drag operation is cancelled.
|
|
31
|
-
previewOptions:
|
|
32
|
-
options:
|
|
33
|
-
|
|
34
|
-
end:
|
|
35
|
-
canDrag
|
|
36
|
-
isDragging
|
|
37
|
-
collect: (monitor, props) => { // Optional. The collecting function. It should return a plain object of the props to return for injection into your component. It receives two parameters, monitor and props. Read the overview for an introduction to the monitors and the collecting function. See the collecting function described in detail in the next section.
|
|
60
|
+
previewOptions: dragPreviewOptions, // Optional. A plain JavaScript object describing drag preview options.
|
|
61
|
+
options: dragOptions, // Optional. A plain object optionally containing any of the following properties:
|
|
62
|
+
dropEffect, // Optional: The type of drop effect to use on this drag. ("move" or "copy" are valid values.)
|
|
63
|
+
end: onDragEnd, // (item, monitor) Optional. When the dragging stops, endis called. For every begin call, a corresponding end call is guaranteed. You may call monitor.didDrop() to check whether or not the drop was handled by a compatible drop target. If it was handled, and the drop target specified a drop result by returning a plain object from its drop()method, it will be available as monitor.getDropResult(). This method is a good place to fire a Flux action. Note: If the component is unmounted while dragging, componentparameter is set to be null.
|
|
64
|
+
canDrag, // (monitor): Optional. Use it to specify whether the dragging is currently allowed. If you want to always allow it, just omit this method. Specifying it is handy if you'd like to disable dragging based on some predicate over props. Note: You may not call monitor.canDrag()inside this method.
|
|
65
|
+
isDragging, // (monitor): Optional. By default, only the drag source that initiated the drag operation is considered to be dragging. You can override this behavior by defining a custom isDraggingmethod. It might return something like props.id === monitor.getItem().id. Do this if the original component may be unmounted during the dragging and later “resurrected” with a different parent. For example, when moving a card across the lists in a Kanban board, you want it to retain the dragged appearance—even though technically, the component gets unmounted and a different one gets mounted every time you move it to another list. Note: You may not call monitor.isDragging()inside this method.
|
|
66
|
+
collect: dragCollect, /* (monitor, props) => { // Optional. The collecting function. It should return a plain object of the props to return for injection into your component. It receives two parameters, monitor and props. Read the overview for an introduction to the monitors and the collecting function. See the collecting function described in detail in the next section.
|
|
38
67
|
// monitor fn determines which props from dnd state get passed
|
|
39
68
|
return {
|
|
40
69
|
canDrag: !!monitor.canDrag(), // Returns trueif no drag operation is in progress, and the owner's canDrag() returns true or is not defined.
|
|
41
70
|
isDragging: !!monitor.isDragging(), // Returns trueif a drag operation is in progress, and either the owner initiated the drag, or its isDragging() is defined and returns true.
|
|
42
71
|
// type: monitor.getItemType(), // Returns a string or a symbol identifying the type of the current dragged item. Returns null if no item is being dragged.
|
|
43
|
-
// item: monitor.getItem(), // Returns a plain object representing the currently dragged item. Every drag source must specify it by returning an object from its beginDrag()method. Returns nullif no item is being dragged.
|
|
72
|
+
// item: monitor.getItem(), // Returns a plain object representing the currently dragged item. Every drag source must specify it by returning an object from its beginDrag() method. Returns nullif no item is being dragged.
|
|
44
73
|
// dropResult: monitor.getDropResult(), // Returns a plain object representing the last recorded drop result. The drop targets may optionally specify it by returning an object from their drop()methods. When a chain of drop()is dispatched for the nested targets, bottom up, any parent that explicitly returns its own result from drop()overrides the child drop result previously set by the child. Returns nullif called outside endDrag().
|
|
45
|
-
// didDrop: !!monitor.didDrop(), // Returns trueif some drop target has handled the drop event, falseotherwise. Even if a target did not return a drop result, didDrop()returns true. Use it inside endDrag()to test whether any drop target has handled the drop. Returns falseif called outside endDrag().
|
|
74
|
+
// didDrop: !!monitor.didDrop(), // Returns trueif some drop target has handled the drop event, falseotherwise. Even if a target did not return a drop result, didDrop() returns true. Use it inside endDrag()to test whether any drop target has handled the drop. Returns falseif called outside endDrag().
|
|
46
75
|
// initialClientOffset: monitor.getInitialClientOffset(), // Returns the { x, y }client offset of the pointer at the time when the current drag operation has started. Returns nullif no item is being dragged.
|
|
47
76
|
// initialSourceClientOffset: monitor.getInitialSourceClientOffset(), // Returns the { x, y }client offset of the drag source component's root DOM node at the time when the current drag operation has started. Returns nullif no item is being dragged.
|
|
48
77
|
// clientOffset: monitor.getClientOffset(), // Returns the last recorded { x, y }client offset of the pointer while a drag operation is in progress. Returns nullif no item is being dragged.
|
|
49
78
|
// differenceFromInitialOffset: monitor.getDifferenceFromInitialOffset(), // Returns the { x, y }difference between the last recorded client offset of the pointer and the client offset when the current drag operation has started. Returns nullif no item is being dragged.
|
|
50
79
|
// sourceClientOffset: monitor.getSourceClientOffset(), // Returns the projected { x, y }client offset of the drag source component's root DOM node, based on its position at the time when the current drag operation has started, and the movement difference. Returns nullif no item is being dragged.
|
|
51
80
|
};
|
|
52
|
-
}
|
|
81
|
+
},*/
|
|
53
82
|
};
|
|
54
83
|
}),
|
|
55
84
|
{
|
|
56
|
-
canDrag,
|
|
57
|
-
isDragging,
|
|
85
|
+
canDrag: stateCanDrag,
|
|
86
|
+
isDragging: stateIsDragging,
|
|
58
87
|
// type,
|
|
59
|
-
// item,
|
|
88
|
+
// item: stateItem,
|
|
60
89
|
// dropResult,
|
|
61
90
|
// didDrop,
|
|
62
91
|
// initialClientOffset,
|
|
@@ -65,12 +94,24 @@ export function withDragSource(WrappedComponent) {
|
|
|
65
94
|
// differenceFromInitialOffset,
|
|
66
95
|
// sourceClientOffset,
|
|
67
96
|
} = dragState;
|
|
97
|
+
|
|
98
|
+
if (dragSourceItem.onDrag) {
|
|
99
|
+
// This will enable the onDrag callback, as useDragLayer will cause withDragSource to re-render any time state changes. useDrag will not
|
|
100
|
+
const layer = useDragLayer(dragCollect);
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (layer.isDragging && dragSourceItem?.id === layer.item.id) {
|
|
103
|
+
dragSourceItem.onDrag(layer);
|
|
104
|
+
}
|
|
105
|
+
}, [layer]);
|
|
106
|
+
}
|
|
68
107
|
|
|
69
108
|
return <WrappedComponent
|
|
70
|
-
canDrag={canDrag}
|
|
71
|
-
isDragging={isDragging}
|
|
72
|
-
dragSourceRef={dragSourceRef}
|
|
73
109
|
{...props}
|
|
110
|
+
canDrag={stateCanDrag}
|
|
111
|
+
isDragging={stateIsDragging}
|
|
112
|
+
dragSourceRef={dragSourceRef}
|
|
113
|
+
dragPreviewRef={dragPreviewRef}
|
|
114
|
+
dragState={dragState}
|
|
74
115
|
/>;
|
|
75
116
|
};
|
|
76
117
|
}
|
|
@@ -78,6 +119,7 @@ export function withDragSource(WrappedComponent) {
|
|
|
78
119
|
|
|
79
120
|
export function withDropTarget(WrappedComponent) {
|
|
80
121
|
return (props) => {
|
|
122
|
+
|
|
81
123
|
if (!props.isDropTarget) {
|
|
82
124
|
return <WrappedComponent {...props} />;
|
|
83
125
|
}
|
|
@@ -88,17 +130,27 @@ export function withDropTarget(WrappedComponent) {
|
|
|
88
130
|
|
|
89
131
|
const {
|
|
90
132
|
dropTargetAccept,
|
|
133
|
+
dropOptions = null,
|
|
91
134
|
onDrop = null,
|
|
135
|
+
onDropHover = null,
|
|
136
|
+
canDrop = null,
|
|
137
|
+
dropCollect = (monitor, props) => {
|
|
138
|
+
return {
|
|
139
|
+
canDrop: !!monitor.canDrop(),
|
|
140
|
+
isOver: !!monitor.isOver(),
|
|
141
|
+
};
|
|
142
|
+
},
|
|
92
143
|
} = props,
|
|
144
|
+
localTargetRef = useRef(null),
|
|
93
145
|
[dropState, dropTargetRef] = useDrop(() => { // A specification object or a function that creates a specification object.
|
|
94
146
|
// The useDrophook provides a way for you to wire in your component into the DnD system as a drop target. By passing in a specification into the useDrophook, you can specify including what types of data items the drop-target will accept, what props to collect, and more. This function returns an array containing a ref to attach to the Drop Target node and the collected props.
|
|
95
147
|
return {
|
|
96
148
|
accept: dropTargetAccept, // Required. A string, a symbol, or an array of either. This drop target will only react to the items produced by the drag sources of the specified type or types. Read the overview to learn more about the items and types.
|
|
97
|
-
|
|
149
|
+
options: dropOptions, // Optional. A plain object. If some of the props to your component are not scalar (that is, are not primitive values or functions), specifying a custom arePropsEqual(props, otherProps) function inside the options object can improve the performance. Unless you have performance problems, don't worry about it.
|
|
98
150
|
drop: onDrop, // (item, monitor): Optional. Called when a compatible item is dropped on the target. You may either return undefined, or a plain object. If you return an object, it is going to become the drop result and will be available to the drag source in its endDragmethod as monitor.getDropResult(). This is useful in case you want to perform different actions depending on which target received the drop. If you have nested drop targets, you can test whether a nested target has already handled dropby checking monitor.didDrop()and monitor.getDropResult(). Both this method and the source's endDragmethod are good places to fire Flux actions. This method will not be called if canDrop()is defined and returns false.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
collect: (monitor, props) => { // Optional. The collecting function. It should return a plain object of the props to return for injection into your component. It receives two parameters, monitorand props. Read the overview for an introduction to the monitors and the collecting function. See the collecting function described in detail in the next section.
|
|
151
|
+
hover: onDropHover, // (item, monitor): Optional. Called when an item is hovered over the component. You can check monitor.isOver({ shallow: true })to test whether the hover happens over only the current target, or over a nested one. Unlike drop(), this method will be called even if canDrop()is defined and returns false. You can check monitor.canDrop()to test whether this is the case.
|
|
152
|
+
canDrop, // (item, monitor): Optional. Use it to specify whether the drop target is able to accept the item. If you want to always allow it, omit this method. Specifying it is handy if you'd like to disable dropping based on some predicate over props or monitor.getItem(). Note: You may not call monitor.canDrop() inside this method.
|
|
153
|
+
collect: dropCollect, /*: (monitor, props) => { // Optional. The collecting function. It should return a plain object of the props to return for injection into your component. It receives two parameters, monitorand props. Read the overview for an introduction to the monitors and the collecting function. See the collecting function described in detail in the next section.
|
|
102
154
|
return {
|
|
103
155
|
canDrop: !!monitor.canDrop(),
|
|
104
156
|
isOver: !!monitor.isOver(),
|
|
@@ -112,11 +164,11 @@ export function withDropTarget(WrappedComponent) {
|
|
|
112
164
|
// receiveHandlerId
|
|
113
165
|
// subscribeToStateChange
|
|
114
166
|
};
|
|
115
|
-
}
|
|
167
|
+
},*/
|
|
116
168
|
};
|
|
117
169
|
}),
|
|
118
170
|
{
|
|
119
|
-
canDrop,
|
|
171
|
+
canDrop: stateCanDrop,
|
|
120
172
|
isOver,
|
|
121
173
|
// didDrop,
|
|
122
174
|
// clientOffset,
|
|
@@ -127,11 +179,70 @@ export function withDropTarget(WrappedComponent) {
|
|
|
127
179
|
// initialSourceClientOffset,
|
|
128
180
|
} = dropState;
|
|
129
181
|
|
|
182
|
+
dropTargetRef(localTargetRef); // register DOM node with react-dnd
|
|
183
|
+
|
|
130
184
|
return <WrappedComponent
|
|
131
|
-
canDrop={
|
|
185
|
+
canDrop={stateCanDrop}
|
|
132
186
|
isOver={isOver}
|
|
133
|
-
dropTargetRef={
|
|
187
|
+
dropTargetRef={localTargetRef}
|
|
134
188
|
{...props}
|
|
135
189
|
/>;
|
|
136
190
|
};
|
|
137
191
|
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
// export function CustomDragLayer(props) {
|
|
195
|
+
|
|
196
|
+
// // if (CURRENT_MODE !== UI_MODE_WEB) {
|
|
197
|
+
// // throw Error('CustomDragLayer only works in web mode');
|
|
198
|
+
// // }
|
|
199
|
+
|
|
200
|
+
// const
|
|
201
|
+
// {
|
|
202
|
+
// onDrag,
|
|
203
|
+
// axis = null,
|
|
204
|
+
// } = props,
|
|
205
|
+
// layer = useDragLayer((monitor) => ({
|
|
206
|
+
// isDragging: monitor.isDragging(),
|
|
207
|
+
// item: monitor.getItem(),
|
|
208
|
+
// currentOffset: monitor.getSourceClientOffset(),
|
|
209
|
+
// })),
|
|
210
|
+
// { isDragging, item, currentOffset } = layer;
|
|
211
|
+
|
|
212
|
+
// useEffect(() => {
|
|
213
|
+
// if (layer.isDragging) {
|
|
214
|
+
// onDrag(layer);
|
|
215
|
+
// }
|
|
216
|
+
// }, [layer]);
|
|
217
|
+
|
|
218
|
+
// return null;
|
|
219
|
+
|
|
220
|
+
// if (!isDragging || !currentOffset) {
|
|
221
|
+
// return null;
|
|
222
|
+
// }
|
|
223
|
+
|
|
224
|
+
// let transform;
|
|
225
|
+
// if (axis === 'x') {
|
|
226
|
+
// transform = `translate(${currentOffset.x}px, 0)`;
|
|
227
|
+
// } else if (axis === 'y') {
|
|
228
|
+
// transform = `translate(0, ${currentOffset.y}px)`;
|
|
229
|
+
// } else {
|
|
230
|
+
// transform = `translate(${currentOffset.x}px, ${currentOffset.y}px)`;
|
|
231
|
+
// }
|
|
232
|
+
|
|
233
|
+
// return (
|
|
234
|
+
// <div id="dragLayer" style={{
|
|
235
|
+
// background: '#f00',
|
|
236
|
+
// position: 'fixed',
|
|
237
|
+
// pointerEvents: 'none',
|
|
238
|
+
// zIndex: 10000,
|
|
239
|
+
// width: '200px',
|
|
240
|
+
// height: '10px',
|
|
241
|
+
// left: 0,
|
|
242
|
+
// top: 0,
|
|
243
|
+
// transform,
|
|
244
|
+
// }}>
|
|
245
|
+
// {children}
|
|
246
|
+
// </div>
|
|
247
|
+
// );
|
|
248
|
+
// }
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { useState, useEffect, } from 'react';
|
|
1
|
+
import { useState, useEffect, useRef, } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
SELECTION_MODE_SINGLE,
|
|
4
4
|
SELECTION_MODE_MULTI,
|
|
5
5
|
SELECT_UP,
|
|
6
6
|
SELECT_DOWN,
|
|
7
7
|
} from '../../Constants/Selection.js';
|
|
8
|
+
import useForceUpdate from '../../Hooks/useForceUpdate.js';
|
|
8
9
|
import inArray from '../../Functions/inArray.js';
|
|
9
10
|
import _ from 'lodash';
|
|
10
11
|
|
|
@@ -46,20 +47,22 @@ export default function withSelection(WrappedComponent) {
|
|
|
46
47
|
} = props,
|
|
47
48
|
usesWithValue = !!setValue,
|
|
48
49
|
initialSelection = selection || defaultSelection || [],
|
|
49
|
-
|
|
50
|
+
forceUpdate = useForceUpdate(),
|
|
51
|
+
localSelection = useRef(initialSelection),
|
|
50
52
|
[isReady, setIsReady] = useState(selection || false), // if selection is already defined, or value is not null and we don't need to load repository, it's ready
|
|
51
53
|
setSelection = (selection) => {
|
|
52
|
-
if (_.isEqual(selection, localSelection)) {
|
|
54
|
+
if (_.isEqual(selection, localSelection.current)) {
|
|
53
55
|
return;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
localSelection.current = selection;
|
|
57
59
|
if (onChangeSelection) {
|
|
58
60
|
onChangeSelection(selection);
|
|
59
61
|
}
|
|
60
62
|
if (fireEvent) {
|
|
61
63
|
fireEvent('changeSelection', selection);
|
|
62
64
|
}
|
|
65
|
+
forceUpdate();
|
|
63
66
|
},
|
|
64
67
|
selectNext = () => {
|
|
65
68
|
selectDirection(SELECT_DOWN);
|
|
@@ -91,23 +94,21 @@ export default function withSelection(WrappedComponent) {
|
|
|
91
94
|
},
|
|
92
95
|
addToSelection = (item) => {
|
|
93
96
|
let newSelection = [];
|
|
94
|
-
newSelection = _.clone(localSelection); // so we get a new object, so descendants rerender
|
|
97
|
+
newSelection = _.clone(localSelection.current); // so we get a new object, so descendants rerender
|
|
95
98
|
newSelection.push(item);
|
|
96
99
|
setSelection(newSelection);
|
|
97
100
|
},
|
|
98
101
|
removeFromSelection = (item) => {
|
|
99
102
|
let newSelection = [];
|
|
100
103
|
if (Repository) {
|
|
101
|
-
newSelection = _.remove(localSelection, (sel) => sel !== item);
|
|
104
|
+
newSelection = _.remove(localSelection.current, (sel) => sel !== item);
|
|
102
105
|
} else {
|
|
103
|
-
newSelection = _.remove(localSelection, (sel) => sel[idIx] !== item[idIx]);
|
|
106
|
+
newSelection = _.remove(localSelection.current, (sel) => sel[idIx] !== item[idIx]);
|
|
104
107
|
}
|
|
105
108
|
setSelection(newSelection);
|
|
106
109
|
},
|
|
107
110
|
deselectAll = () => {
|
|
108
|
-
|
|
109
|
-
setSelection([]);
|
|
110
|
-
}
|
|
111
|
+
setSelection([]);
|
|
111
112
|
},
|
|
112
113
|
getMaxMinSelectionIndices = () => {
|
|
113
114
|
let items,
|
|
@@ -134,9 +135,9 @@ export default function withSelection(WrappedComponent) {
|
|
|
134
135
|
selectRangeTo = (item) => {
|
|
135
136
|
// Select above max or below min to this one
|
|
136
137
|
const
|
|
137
|
-
currentSelectionLength = localSelection.length,
|
|
138
|
+
currentSelectionLength = localSelection.current.length,
|
|
138
139
|
index = getIndexOfSelectedItem(item);
|
|
139
|
-
let newSelection = _.clone(localSelection); // so we get a new object, so descendants rerender
|
|
140
|
+
let newSelection = _.clone(localSelection.current); // so we get a new object, so descendants rerender
|
|
140
141
|
|
|
141
142
|
if (currentSelectionLength) {
|
|
142
143
|
const { items, max, min, } = getMaxMinSelectionIndices();
|
|
@@ -163,10 +164,10 @@ export default function withSelection(WrappedComponent) {
|
|
|
163
164
|
},
|
|
164
165
|
isInSelection = (item) => {
|
|
165
166
|
if (Repository) {
|
|
166
|
-
return inArray(item, localSelection);
|
|
167
|
+
return inArray(item, localSelection.current);
|
|
167
168
|
}
|
|
168
169
|
|
|
169
|
-
const found = _.find(localSelection, (selectedItem) => {
|
|
170
|
+
const found = _.find(localSelection.current, (selectedItem) => {
|
|
170
171
|
return selectedItem[idIx] === item[idIx];
|
|
171
172
|
});
|
|
172
173
|
return !!found;
|
|
@@ -188,10 +189,10 @@ export default function withSelection(WrappedComponent) {
|
|
|
188
189
|
return found;
|
|
189
190
|
},
|
|
190
191
|
getIdsFromLocalSelection = () => {
|
|
191
|
-
if (!localSelection[0]) {
|
|
192
|
+
if (!localSelection.current[0]) {
|
|
192
193
|
return null;
|
|
193
194
|
}
|
|
194
|
-
const values = _.map(localSelection, (item) => {
|
|
195
|
+
const values = _.map(localSelection.current, (item) => {
|
|
195
196
|
if (Repository) {
|
|
196
197
|
return item.id;
|
|
197
198
|
}
|
|
@@ -202,7 +203,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
202
203
|
}
|
|
203
204
|
return values;
|
|
204
205
|
},
|
|
205
|
-
|
|
206
|
+
getDisplayValuesFromSelection = (selection) => {
|
|
206
207
|
if (!selection[0]) {
|
|
207
208
|
return '';
|
|
208
209
|
}
|
|
@@ -275,11 +276,21 @@ export default function withSelection(WrappedComponent) {
|
|
|
275
276
|
}
|
|
276
277
|
}
|
|
277
278
|
|
|
278
|
-
if (!_.isEqual(newSelection, localSelection)) {
|
|
279
|
+
if (!_.isEqual(newSelection, localSelection.current)) {
|
|
279
280
|
setSelection(newSelection);
|
|
280
281
|
}
|
|
281
282
|
};
|
|
282
283
|
|
|
284
|
+
if (Repository) {
|
|
285
|
+
useEffect(() => {
|
|
286
|
+
// clear the selection when Repository loads
|
|
287
|
+
Repository.on('load', deselectAll);
|
|
288
|
+
return () => {
|
|
289
|
+
Repository.off('load', deselectAll);
|
|
290
|
+
};
|
|
291
|
+
}, []);
|
|
292
|
+
}
|
|
293
|
+
|
|
283
294
|
useEffect(() => {
|
|
284
295
|
|
|
285
296
|
(async () => {
|
|
@@ -317,7 +328,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
317
328
|
}, [value]);
|
|
318
329
|
|
|
319
330
|
if (self) {
|
|
320
|
-
self.selection = localSelection;
|
|
331
|
+
self.selection = localSelection.current;
|
|
321
332
|
self.setSelection = setSelection;
|
|
322
333
|
self.selectNext = selectNext;
|
|
323
334
|
self.selectPrev = selectPrev;
|
|
@@ -327,7 +338,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
327
338
|
self.selectRangeTo = selectRangeTo;
|
|
328
339
|
self.isInSelection = isInSelection;
|
|
329
340
|
self.getIdsFromLocalSelection = getIdsFromLocalSelection;
|
|
330
|
-
self.getDisplayValuesFromSelection =
|
|
341
|
+
self.getDisplayValuesFromSelection = getDisplayValuesFromSelection;
|
|
331
342
|
}
|
|
332
343
|
|
|
333
344
|
if (usesWithValue) {
|
|
@@ -357,7 +368,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
357
368
|
return <WrappedComponent
|
|
358
369
|
{...props}
|
|
359
370
|
disableWithSelection={false}
|
|
360
|
-
selection={localSelection}
|
|
371
|
+
selection={localSelection.current}
|
|
361
372
|
setSelection={setSelection}
|
|
362
373
|
selectionMode={selectionMode}
|
|
363
374
|
selectNext={selectNext}
|
|
@@ -368,7 +379,7 @@ export default function withSelection(WrappedComponent) {
|
|
|
368
379
|
selectRangeTo={selectRangeTo}
|
|
369
380
|
isInSelection={isInSelection}
|
|
370
381
|
getIdsFromSelection={getIdsFromLocalSelection}
|
|
371
|
-
getDisplayValuesFromSelection={
|
|
382
|
+
getDisplayValuesFromSelection={getDisplayValuesFromSelection}
|
|
372
383
|
/>;
|
|
373
384
|
};
|
|
374
385
|
}
|