@teselagen/ui 0.5.23-beta.26 → 0.5.23-beta.28

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.
@@ -0,0 +1,7 @@
1
+ export function EditableCell({ cancelEdit, dataTest, finishEdit, isNumeric, initialValue }: {
2
+ cancelEdit: any;
3
+ dataTest: any;
4
+ finishEdit: any;
5
+ isNumeric: any;
6
+ initialValue: any;
7
+ }): import("react/jsx-runtime").JSX.Element;
package/index.cjs.js CHANGED
@@ -31027,11 +31027,11 @@ const _TgSelect = class _TgSelect extends React$1.Component {
31027
31027
  return true;
31028
31028
  });
31029
31029
  }
31030
- return onChange([...valArray, item]);
31030
+ return onChange([...valArray, item], e2);
31031
31031
  } else {
31032
31032
  this.setOpenState(false);
31033
31033
  this.input && this.input.blur();
31034
- return onChange(item);
31034
+ return onChange(item, e2);
31035
31035
  }
31036
31036
  }, "handleItemSelect"));
31037
31037
  __publicField(this, "handleTagRemove", /* @__PURE__ */ __name((e2, tagProps) => {
package/index.es.js CHANGED
@@ -31009,11 +31009,11 @@ const _TgSelect = class _TgSelect extends React__default.Component {
31009
31009
  return true;
31010
31010
  });
31011
31011
  }
31012
- return onChange([...valArray, item]);
31012
+ return onChange([...valArray, item], e2);
31013
31013
  } else {
31014
31014
  this.setOpenState(false);
31015
31015
  this.input && this.input.blur();
31016
- return onChange(item);
31016
+ return onChange(item, e2);
31017
31017
  }
31018
31018
  }, "handleItemSelect"));
31019
31019
  __publicField(this, "handleTagRemove", /* @__PURE__ */ __name((e2, tagProps) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teselagen/ui",
3
- "version": "0.5.23-beta.26",
3
+ "version": "0.5.23-beta.28",
4
4
  "main": "./src/index.js",
5
5
  "exports": {
6
6
  ".": {
@@ -1,4 +1,4 @@
1
- import React, { isValidElement } from "react";
1
+ import React, { isValidElement, useCallback } from "react";
2
2
  import classNames from "classnames";
3
3
  import { Button, Classes, Checkbox, Icon } from "@blueprintjs/core";
4
4
  import {
@@ -24,7 +24,6 @@ import {
24
24
  getIdOrCodeOrIndex,
25
25
  getNumberStrAtEnd,
26
26
  getSelectedRowsFromEntities,
27
- isBottomRightCornerOfRectangle,
28
27
  PRIMARY_SELECTED_VAL,
29
28
  stripNumberAtEnd
30
29
  } from "./utils";
@@ -33,12 +32,11 @@ import { ColumnFilterMenu } from "./ColumnFilterMenu";
33
32
  import getTextFromEl from "../utils/getTextFromEl";
34
33
  import rowClick, { finalizeSelection } from "./utils/rowClick";
35
34
  import { editCellHelper } from "./editCellHelper";
36
- import { DropdownCell } from "./DropdownCell";
37
- import { EditableCell } from "./EditabelCell";
38
- import { getVals } from "./getVals";
39
- import { CellDragHandle } from "./CellDragHandle";
40
35
  import { getCellVal } from "./getCellVal";
41
36
  import { getCCDisplayName } from "./utils/queryParams";
37
+ import { useDispatch } from "react-redux";
38
+ import { change as _change } from "redux-form";
39
+ import { RenderCell } from "./RenderCell";
42
40
 
43
41
  dayjs.extend(localizedFormat);
44
42
 
@@ -311,479 +309,425 @@ const renderCheckboxHeader = ({
311
309
  ) : null;
312
310
  };
313
311
 
314
- const RenderCell = ({
315
- oldFunc,
316
- getCopyTextForCell,
317
- column,
318
- isCellEditable,
319
- isEntityDisabled,
320
- finishCellEdit,
321
- noEllipsis,
312
+ export const useColumns = ({
313
+ addFilters,
314
+ cellRenderer,
315
+ columns,
316
+ currentParams,
317
+ compact,
322
318
  editingCell,
323
- cancelCellEdit,
324
- editableCellValue,
325
- setEditableCellValue,
319
+ editingCellSelectAll,
320
+ entities,
321
+ expandedEntityIdMap,
322
+ extraCompact,
323
+ filters,
324
+ formName,
326
325
  getCellHoverText,
327
- selectedCells,
326
+ isCellEditable,
327
+ isEntityDisabled,
328
+ isLocalCall,
329
+ isSimple,
330
+ isSingleSelect,
328
331
  isSelectionARectangle,
332
+ noDeselectAll,
333
+ noSelect,
334
+ noUserSelect,
335
+ onDeselect,
336
+ onMultiRowSelect,
337
+ onRowClick,
338
+ onRowSelect,
339
+ onSingleRowSelect,
340
+ order,
341
+ primarySelectedCellId,
342
+ reduxFormCellValidation,
343
+ reduxFormSelectedEntityIdMap,
344
+ refocusTable,
345
+ removeSingleFilter = noop,
346
+ schema,
347
+ selectedCells,
348
+ setExpandedEntityIdMap,
349
+ setNewParams,
350
+ setOrder = noop,
351
+ setSelectedCells,
352
+ shouldShowSubComponent,
329
353
  startCellEdit,
354
+ SubComponent,
330
355
  tableRef,
331
- onDragEnd,
332
- args
356
+ updateEntitiesHelper,
357
+ updateValidation,
358
+ withCheckboxes,
359
+ withExpandAndCollapseAllButton,
360
+ withFilter: _withFilter,
361
+ withSort = true
333
362
  }) => {
334
- const [row] = args;
335
- const rowId = getIdOrCodeOrIndex(row.original, row.index);
336
- const cellId = `${rowId}:${row.column.path}`;
337
- let val = oldFunc(...args);
338
- const oldVal = val;
339
- const text = getCopyTextForCell(val, row, column);
340
- const isBool = column.type === "boolean";
341
- const dataTest = {
342
- "data-test": "tgCell_" + column.path
343
- };
344
- const fullValue = row.original?.[row.column.path];
345
- if (isCellEditable && isBool) {
346
- val = (
347
- <Checkbox
348
- disabled={isEntityDisabled(row.original)}
349
- className="tg-cell-edit-boolean-checkbox"
350
- checked={oldVal === "True"}
351
- onChange={e => {
352
- const checked = e.target.checked;
353
- finishCellEdit(cellId, checked);
354
- }}
355
- />
356
- );
357
- noEllipsis = true;
358
- } else if (editingCell === cellId) {
359
- if (column.type === "genericSelect") {
360
- const GenericSelectComp = column.GenericSelectComp;
361
-
362
- return (
363
- <GenericSelectComp
364
- rowId={rowId}
365
- fullValue={fullValue}
366
- initialValue={text}
367
- {...dataTest}
368
- finishEdit={(newVal, doNotStopEditing) => {
369
- finishCellEdit(cellId, newVal, doNotStopEditing);
370
- }}
371
- dataTest={dataTest}
372
- cancelEdit={cancelCellEdit}
373
- />
374
- );
375
- }
376
- if (column.type === "dropdown" || column.type === "dropdownMulti") {
377
- return (
378
- <DropdownCell
379
- isMulti={dataTest.isMulti || column.type === "dropdownMulti"}
380
- initialValue={dataTest.initialValue || text}
381
- options={getVals(column.values)}
382
- finishEdit={(newVal, doNotStopEditing) => {
383
- finishCellEdit(cellId, newVal, doNotStopEditing);
384
- }}
385
- dataTest={dataTest}
386
- cancelEdit={cancelCellEdit}
387
- />
388
- );
389
- } else {
390
- return (
391
- <EditableCell
392
- value={editableCellValue}
393
- setValue={setEditableCellValue}
394
- dataTest={dataTest}
395
- cancelEdit={cancelCellEdit}
396
- isNumeric={column.type === "number"}
397
- initialValue={text}
398
- finishEdit={newVal => {
399
- finishCellEdit(cellId, newVal);
400
- }}
401
- />
402
- );
403
- }
404
- }
405
-
406
- //wrap the original tableColumn.Cell function in another div in order to add a title attribute
407
- let title = text;
408
- if (getCellHoverText) title = getCellHoverText(...args);
409
- else if (column.getTitleAttr) title = column.getTitleAttr(...args);
410
- const isSelectedCell = selectedCells?.[cellId];
411
- const {
412
- isRect,
413
- selectionGrid,
414
- lastRowIndex,
415
- lastCellIndex,
416
- entityMap,
417
- pathToIndex
418
- } = isSelectionARectangle();
419
-
420
- return (
421
- <>
422
- <div
423
- style={{
424
- ...(!noEllipsis && {
425
- textOverflow: "ellipsis",
426
- overflow: "hidden"
427
- })
428
- }}
429
- {...dataTest}
430
- className="tg-cell-wrapper"
431
- data-copy-text={text}
432
- data-copy-json={JSON.stringify(
433
- //tnw: eventually we'll parse these back out and use either the fullValue (for the generic selects) or the regular text vals for everything else
434
- column.type === "genericSelect"
435
- ? {
436
- __strVal: fullValue,
437
- __genSelCol: column.path
438
- }
439
- : { __strVal: text }
440
- )}
441
- title={title || undefined}
442
- >
443
- {val}
444
- </div>
445
- {isCellEditable &&
446
- (column.type === "dropdown" ||
447
- column.type === "dropdownMulti" ||
448
- column.type === "genericSelect") && (
449
- <Icon
450
- icon="caret-down"
451
- style={{
452
- position: "absolute",
453
- right: 5,
454
- opacity: 0.3
455
- }}
456
- className="cell-edit-dropdown"
457
- onClick={() => {
458
- startCellEdit(cellId);
459
- }}
460
- />
461
- )}
462
-
463
- {isSelectedCell &&
464
- (isRect
465
- ? isBottomRightCornerOfRectangle({
466
- cellId,
467
- selectionGrid,
468
- lastRowIndex,
469
- lastCellIndex,
470
- entityMap,
471
- pathToIndex
472
- })
473
- : isSelectedCell === PRIMARY_SELECTED_VAL) && (
474
- <CellDragHandle
475
- key={cellId}
476
- thisTable={tableRef.current.tableRef}
477
- cellId={cellId}
478
- isSelectionARectangle={isSelectionARectangle}
479
- onDragEnd={onDragEnd}
480
- />
481
- )}
482
- </>
363
+ const dispatch = useDispatch();
364
+ const change = useCallback(
365
+ (...args) => dispatch(_change(formName, ...args)),
366
+ [dispatch, formName]
483
367
  );
484
- };
485
-
486
- export const RenderColumns = props => {
487
- const {
488
- addFilters,
489
- cellRenderer,
490
- change,
491
- columns,
492
- currentParams,
493
- compact,
494
- editableCellValue,
495
- editingCell,
496
- editingCellSelectAll,
497
- entities,
498
- expandedEntityIdMap,
499
- extraCompact,
500
- filters,
501
- getCellHoverText,
502
- isCellEditable,
503
- isEntityDisabled,
504
- isLocalCall,
505
- isSimple,
506
- isSingleSelect,
507
- isSelectionARectangle,
508
- noDeselectAll,
509
- noSelect,
510
- noUserSelect,
511
- onDeselect,
512
- onMultiRowSelect,
513
- onRowClick,
514
- onRowSelect,
515
- onSingleRowSelect,
516
- order,
517
- primarySelectedCellId,
518
- reduxFormCellValidation,
519
- reduxFormSelectedEntityIdMap,
520
- refocusTable,
521
- removeSingleFilter = noop,
522
- schema,
523
- selectedCells,
524
- setEditableCellValue,
525
- setEditingCell,
526
- setExpandedEntityIdMap,
527
- setNewParams,
528
- setOrder = noop,
529
- setSelectedCells,
530
- shouldShowSubComponent,
531
- startCellEdit,
532
- SubComponent,
533
- tableRef,
534
- updateEntitiesHelper,
535
- updateValidation,
536
- withCheckboxes,
537
- withExpandAndCollapseAllButton,
538
- withFilter: _withFilter,
539
- withSort = true
540
- } = props;
541
-
542
368
  const withFilter = _withFilter === undefined ? !isSimple : _withFilter;
543
369
 
544
- const onDragEnd = cellsToSelect => {
545
- const [primaryRowId, primaryCellPath] = primarySelectedCellId.split(":");
546
- const pathToField = getFieldPathToField(schema);
547
- const { selectedPaths, selectionGrid } = isSelectionARectangle();
548
- let allSelectedPaths = selectedPaths;
549
- if (!allSelectedPaths) {
550
- allSelectedPaths = [primaryCellPath];
551
- }
552
-
553
- updateEntitiesHelper(entities, entities => {
554
- let newSelectedCells;
555
- if (selectedPaths) {
556
- newSelectedCells = {
557
- ...selectedCells
558
- };
559
- } else {
560
- newSelectedCells = {
561
- [primarySelectedCellId]: PRIMARY_SELECTED_VAL
562
- };
370
+ const onDragEnd = useCallback(
371
+ cellsToSelect => {
372
+ const [primaryRowId, primaryCellPath] = primarySelectedCellId.split(":");
373
+ const pathToField = getFieldPathToField(schema);
374
+ const { selectedPaths, selectionGrid } = isSelectionARectangle();
375
+ let allSelectedPaths = selectedPaths;
376
+ if (!allSelectedPaths) {
377
+ allSelectedPaths = [primaryCellPath];
563
378
  }
564
379
 
565
- const newCellValidate = {
566
- ...reduxFormCellValidation
567
- };
568
- const entityMap = getEntityIdToEntity(entities);
569
- const { e: selectedEnt } = entityMap[primaryRowId];
570
- const firstCellToSelectRowIndex =
571
- entityMap[cellsToSelect[0]?.split(":")[0]]?.i;
572
- const pathToIndex = getFieldPathToIndex(schema);
573
-
574
- allSelectedPaths.forEach(selectedPath => {
575
- const column = pathToField[selectedPath];
380
+ updateEntitiesHelper(entities, entities => {
381
+ let newSelectedCells;
382
+ if (selectedPaths) {
383
+ newSelectedCells = {
384
+ ...selectedCells
385
+ };
386
+ } else {
387
+ newSelectedCells = {
388
+ [primarySelectedCellId]: PRIMARY_SELECTED_VAL
389
+ };
390
+ }
576
391
 
577
- const selectedCellVal = getCellVal(selectedEnt, selectedPath, column);
578
- const cellIndexOfSelectedPath = pathToIndex[selectedPath];
579
- let incrementStart;
580
- let incrementPrefix;
581
- let incrementPad = 0;
582
- if (column.type === "string" || column.type === "number") {
583
- const cellNumStr = getNumberStrAtEnd(selectedCellVal);
584
- const cellNum = Number(cellNumStr);
585
- const entityAbovePrimaryCell =
586
- entities[entityMap[primaryRowId].i - 1];
587
- if (cellNumStr !== null && !isNaN(cellNum)) {
588
- if (
589
- entityAbovePrimaryCell &&
590
- (!selectionGrid || selectionGrid.length <= 1)
591
- ) {
592
- const cellAboveVal = get(
593
- entityAbovePrimaryCell,
594
- selectedPath,
595
- ""
596
- );
597
- const cellAboveNumStr = getNumberStrAtEnd(cellAboveVal);
598
- const cellAboveNum = Number(cellAboveNumStr);
599
- if (!isNaN(cellAboveNum)) {
600
- const isIncremental = cellNum - cellAboveNum === 1;
601
- if (isIncremental) {
602
- const cellTextNoNum = stripNumberAtEnd(selectedCellVal);
603
- const sameText =
604
- stripNumberAtEnd(cellAboveVal) === cellTextNoNum;
605
- if (sameText) {
606
- incrementStart = cellNum + 1;
607
- incrementPrefix = cellTextNoNum || "";
608
- if (cellNumStr && cellNumStr.startsWith("0")) {
609
- incrementPad = cellNumStr.length;
392
+ const newCellValidate = {
393
+ ...reduxFormCellValidation
394
+ };
395
+ const entityMap = getEntityIdToEntity(entities);
396
+ const { e: selectedEnt } = entityMap[primaryRowId];
397
+ const firstCellToSelectRowIndex =
398
+ entityMap[cellsToSelect[0]?.split(":")[0]]?.i;
399
+ const pathToIndex = getFieldPathToIndex(schema);
400
+
401
+ allSelectedPaths.forEach(selectedPath => {
402
+ const column = pathToField[selectedPath];
403
+
404
+ const selectedCellVal = getCellVal(selectedEnt, selectedPath, column);
405
+ const cellIndexOfSelectedPath = pathToIndex[selectedPath];
406
+ let incrementStart;
407
+ let incrementPrefix;
408
+ let incrementPad = 0;
409
+ if (column.type === "string" || column.type === "number") {
410
+ const cellNumStr = getNumberStrAtEnd(selectedCellVal);
411
+ const cellNum = Number(cellNumStr);
412
+ const entityAbovePrimaryCell =
413
+ entities[entityMap[primaryRowId].i - 1];
414
+ if (cellNumStr !== null && !isNaN(cellNum)) {
415
+ if (
416
+ entityAbovePrimaryCell &&
417
+ (!selectionGrid || selectionGrid.length <= 1)
418
+ ) {
419
+ const cellAboveVal = get(
420
+ entityAbovePrimaryCell,
421
+ selectedPath,
422
+ ""
423
+ );
424
+ const cellAboveNumStr = getNumberStrAtEnd(cellAboveVal);
425
+ const cellAboveNum = Number(cellAboveNumStr);
426
+ if (!isNaN(cellAboveNum)) {
427
+ const isIncremental = cellNum - cellAboveNum === 1;
428
+ if (isIncremental) {
429
+ const cellTextNoNum = stripNumberAtEnd(selectedCellVal);
430
+ const sameText =
431
+ stripNumberAtEnd(cellAboveVal) === cellTextNoNum;
432
+ if (sameText) {
433
+ incrementStart = cellNum + 1;
434
+ incrementPrefix = cellTextNoNum || "";
435
+ if (cellNumStr && cellNumStr.startsWith("0")) {
436
+ incrementPad = cellNumStr.length;
437
+ }
610
438
  }
611
439
  }
612
440
  }
613
441
  }
614
- }
615
- if (incrementStart === undefined) {
616
- const draggingDown =
617
- firstCellToSelectRowIndex > selectionGrid?.[0][0].rowIndex;
618
- if (selectedPaths && draggingDown) {
619
- let checkIncrement;
620
- let prefix;
621
- let maybePad;
622
- // determine if all the cells in this column of the selectionGrid are incrementing
623
- const allAreIncrementing = selectionGrid.every(row => {
624
- // see if cell is selected
625
- const cellInfo = row[cellIndexOfSelectedPath];
626
- if (!cellInfo) return false;
627
- const { cellId } = cellInfo;
628
- const [rowId] = cellId.split(":");
629
- const cellVal = getCellVal(
630
- entityMap[rowId].e,
631
- selectedPath,
632
- pathToField[selectedPath]
633
- );
634
- const cellNumStr = getNumberStrAtEnd(cellVal);
635
- const cellNum = Number(cellNumStr);
636
- const cellTextNoNum = stripNumberAtEnd(cellVal);
637
- if (cellNumStr?.startsWith("0")) {
638
- maybePad = cellNumStr.length;
639
- }
640
- if (cellTextNoNum && !prefix) {
641
- prefix = cellTextNoNum;
642
- }
643
- if (cellTextNoNum && prefix !== cellTextNoNum) {
644
- return false;
645
- }
646
- if (!isNaN(cellNum)) {
647
- if (!checkIncrement) {
648
- checkIncrement = cellNum;
649
- return true;
442
+ if (incrementStart === undefined) {
443
+ const draggingDown =
444
+ firstCellToSelectRowIndex > selectionGrid?.[0][0].rowIndex;
445
+ if (selectedPaths && draggingDown) {
446
+ let checkIncrement;
447
+ let prefix;
448
+ let maybePad;
449
+ // determine if all the cells in this column of the selectionGrid are incrementing
450
+ const allAreIncrementing = selectionGrid.every(row => {
451
+ // see if cell is selected
452
+ const cellInfo = row[cellIndexOfSelectedPath];
453
+ if (!cellInfo) return false;
454
+ const { cellId } = cellInfo;
455
+ const [rowId] = cellId.split(":");
456
+ const cellVal = getCellVal(
457
+ entityMap[rowId].e,
458
+ selectedPath,
459
+ pathToField[selectedPath]
460
+ );
461
+ const cellNumStr = getNumberStrAtEnd(cellVal);
462
+ const cellNum = Number(cellNumStr);
463
+ const cellTextNoNum = stripNumberAtEnd(cellVal);
464
+ if (cellNumStr?.startsWith("0")) {
465
+ maybePad = cellNumStr.length;
466
+ }
467
+ if (cellTextNoNum && !prefix) {
468
+ prefix = cellTextNoNum;
469
+ }
470
+ if (cellTextNoNum && prefix !== cellTextNoNum) {
471
+ return false;
472
+ }
473
+ if (!isNaN(cellNum)) {
474
+ if (!checkIncrement) {
475
+ checkIncrement = cellNum;
476
+ return true;
477
+ } else {
478
+ return ++checkIncrement === cellNum;
479
+ }
650
480
  } else {
651
- return ++checkIncrement === cellNum;
481
+ return false;
652
482
  }
653
- } else {
654
- return false;
655
- }
656
- });
483
+ });
657
484
 
658
- if (allAreIncrementing) {
659
- incrementStart = checkIncrement + 1;
660
- incrementPrefix = prefix || "";
661
- incrementPad = maybePad;
485
+ if (allAreIncrementing) {
486
+ incrementStart = checkIncrement + 1;
487
+ incrementPrefix = prefix || "";
488
+ incrementPad = maybePad;
489
+ }
662
490
  }
663
491
  }
664
492
  }
665
493
  }
666
- }
667
494
 
668
- let firstSelectedCellRowIndex;
669
- if (selectionGrid) {
670
- selectionGrid[0].some(cell => {
671
- if (cell) {
672
- firstSelectedCellRowIndex = cell.rowIndex;
673
- return true;
674
- }
675
- return false;
676
- });
677
- }
495
+ let firstSelectedCellRowIndex;
496
+ if (selectionGrid) {
497
+ selectionGrid[0].some(cell => {
498
+ if (cell) {
499
+ firstSelectedCellRowIndex = cell.rowIndex;
500
+ return true;
501
+ }
502
+ return false;
503
+ });
504
+ }
505
+
506
+ cellsToSelect.forEach(cellId => {
507
+ const [rowId, cellPath] = cellId.split(":");
508
+ if (cellPath !== selectedPath) return;
509
+ newSelectedCells[cellId] = true;
510
+ const { e: entityToUpdate, i: rowIndex } = entityMap[rowId] || {};
511
+ if (entityToUpdate) {
512
+ delete entityToUpdate._isClean;
513
+ let newVal;
514
+ if (incrementStart !== undefined) {
515
+ const num = incrementStart++;
516
+ newVal = incrementPrefix + padStart(num, incrementPad, "0");
517
+ } else {
518
+ if (selectionGrid && selectionGrid.length > 1) {
519
+ // if there are multiple cells selected then we want to copy them repeating
520
+ // ex: if we have 1,2,3 selected and we drag for 5 more rows we want it to
521
+ // be 1,2,3,1,2 for the new row cells in this column
522
+ const draggingDown = rowIndex > firstSelectedCellRowIndex;
523
+ const cellIndex = pathToIndex[cellPath];
524
+ let cellIdToCopy;
525
+ if (draggingDown) {
526
+ const { cellId } = selectionGrid[
527
+ (rowIndex - firstSelectedCellRowIndex) %
528
+ selectionGrid.length
529
+ ].find(g => g && g.cellIndex === cellIndex);
530
+ cellIdToCopy = cellId;
531
+ } else {
532
+ const lastIndexInGrid =
533
+ selectionGrid[selectionGrid.length - 1][0].rowIndex;
534
+ const { cellId } = selectionGrid[
535
+ (rowIndex + lastIndexInGrid + 1) % selectionGrid.length
536
+ ].find(g => g.cellIndex === cellIndex);
537
+ cellIdToCopy = cellId;
538
+ }
678
539
 
679
- cellsToSelect.forEach(cellId => {
680
- const [rowId, cellPath] = cellId.split(":");
681
- if (cellPath !== selectedPath) return;
682
- newSelectedCells[cellId] = true;
683
- const { e: entityToUpdate, i: rowIndex } = entityMap[rowId] || {};
684
- if (entityToUpdate) {
685
- delete entityToUpdate._isClean;
686
- let newVal;
687
- if (incrementStart !== undefined) {
688
- const num = incrementStart++;
689
- newVal = incrementPrefix + padStart(num, incrementPad, "0");
690
- } else {
691
- if (selectionGrid && selectionGrid.length > 1) {
692
- // if there are multiple cells selected then we want to copy them repeating
693
- // ex: if we have 1,2,3 selected and we drag for 5 more rows we want it to
694
- // be 1,2,3,1,2 for the new row cells in this column
695
- const draggingDown = rowIndex > firstSelectedCellRowIndex;
696
- const cellIndex = pathToIndex[cellPath];
697
- let cellIdToCopy;
698
- if (draggingDown) {
699
- const { cellId } = selectionGrid[
700
- (rowIndex - firstSelectedCellRowIndex) %
701
- selectionGrid.length
702
- ].find(g => g && g.cellIndex === cellIndex);
703
- cellIdToCopy = cellId;
540
+ const [rowIdToCopy, cellPathToCopy] = cellIdToCopy.split(":");
541
+ newVal = getCellVal(
542
+ entityMap[rowIdToCopy].e,
543
+ cellPathToCopy,
544
+ pathToField[cellPathToCopy]
545
+ );
704
546
  } else {
705
- const lastIndexInGrid =
706
- selectionGrid[selectionGrid.length - 1][0].rowIndex;
707
- const { cellId } = selectionGrid[
708
- (rowIndex + lastIndexInGrid + 1) % selectionGrid.length
709
- ].find(g => g.cellIndex === cellIndex);
710
- cellIdToCopy = cellId;
547
+ newVal = selectedCellVal;
711
548
  }
712
-
713
- const [rowIdToCopy, cellPathToCopy] = cellIdToCopy.split(":");
714
- newVal = getCellVal(
715
- entityMap[rowIdToCopy].e,
716
- cellPathToCopy,
717
- pathToField[cellPathToCopy]
718
- );
719
- } else {
720
- newVal = selectedCellVal;
721
549
  }
550
+ const { error } = editCellHelper({
551
+ entity: entityToUpdate,
552
+ path: cellPath,
553
+ schema,
554
+ newVal
555
+ });
556
+ newCellValidate[cellId] = error;
722
557
  }
723
- const { error } = editCellHelper({
724
- entity: entityToUpdate,
725
- path: cellPath,
726
- schema,
727
- newVal
728
- });
729
- newCellValidate[cellId] = error;
730
- }
558
+ });
731
559
  });
732
- });
733
-
734
- // select the new cells
735
- updateValidation(entities, newCellValidate);
736
- setSelectedCells(newSelectedCells);
737
- });
738
- };
739
560
 
740
- const getCopyTextForCell = (val, row = {}, column = {}) => {
741
- // TODOCOPY we need a way to potentially omit certain columns from being added as a \t element (talk to taoh about this)
742
- let text = typeof val !== "string" ? row.value : val;
561
+ // select the new cells
562
+ updateValidation(entities, newCellValidate);
563
+ setSelectedCells(newSelectedCells);
564
+ });
565
+ },
566
+ [
567
+ entities,
568
+ isSelectionARectangle,
569
+ primarySelectedCellId,
570
+ reduxFormCellValidation,
571
+ schema,
572
+ selectedCells,
573
+ setSelectedCells,
574
+ updateEntitiesHelper,
575
+ updateValidation
576
+ ]
577
+ );
743
578
 
744
- // We should try to take out the props from here, it produces
745
- // unnecessary rerenders
746
- const record = row.original;
747
- if (column.getClipboardData) {
748
- text = column.getClipboardData(row.value, record, row, props);
749
- } else if (column.getValueToFilterOn) {
750
- text = column.getValueToFilterOn(record, props);
751
- } else if (column.render) {
752
- text = column.render(row.value, record, row, props);
753
- } else if (cellRenderer && cellRenderer[column.path]) {
754
- text = cellRenderer[column.path](row.value, row.original, row, props);
755
- } else if (text) {
756
- text = isValidElement(text) ? text : String(text);
757
- }
758
- const getTextFromElementOrLink = text => {
759
- if (isValidElement(text)) {
760
- if (text.props?.to) {
761
- // this will convert Link elements to url strings
762
- return joinUrl(
763
- window.location.origin,
764
- window.frontEndConfig?.clientBasePath || "",
765
- text.props.to
766
- );
579
+ const getCopyTextForCell = useCallback(
580
+ (val, row = {}, column = {}) => {
581
+ // TODOCOPY we need a way to potentially omit certain columns from being added as a \t element (talk to taoh about this)
582
+ let text = typeof val !== "string" ? row.value : val;
583
+
584
+ // We should try to take out the props from here, it produces
585
+ // unnecessary rerenders
586
+ const record = row.original;
587
+ if (column.getClipboardData) {
588
+ text = column.getClipboardData(row.value, record, row);
589
+ } else if (column.getValueToFilterOn) {
590
+ text = column.getValueToFilterOn(record);
591
+ } else if (column.render) {
592
+ text = column.render(row.value, record, row, {
593
+ currentParams,
594
+ setNewParams
595
+ });
596
+ } else if (cellRenderer && cellRenderer[column.path]) {
597
+ text = cellRenderer[column.path](row.value, row.original, row, {
598
+ currentParams,
599
+ setNewParams
600
+ });
601
+ } else if (text) {
602
+ text = isValidElement(text) ? text : String(text);
603
+ }
604
+ const getTextFromElementOrLink = text => {
605
+ if (isValidElement(text)) {
606
+ if (text.props?.to) {
607
+ // this will convert Link elements to url strings
608
+ return joinUrl(
609
+ window.location.origin,
610
+ window.frontEndConfig?.clientBasePath || "",
611
+ text.props.to
612
+ );
613
+ } else {
614
+ return getTextFromEl(text);
615
+ }
767
616
  } else {
768
- return getTextFromEl(text);
617
+ return text;
769
618
  }
770
- } else {
771
- return text;
619
+ };
620
+ text = getTextFromElementOrLink(text);
621
+
622
+ if (Array.isArray(text)) {
623
+ let arrText = text.map(getTextFromElementOrLink).join(", ");
624
+ // because we sometimes insert commas after links when mapping over an array of elements we will have double ,'s
625
+ arrText = arrText.replace(/, ,/g, ",");
626
+ text = arrText;
772
627
  }
773
- };
774
- text = getTextFromElementOrLink(text);
775
628
 
776
- if (Array.isArray(text)) {
777
- let arrText = text.map(getTextFromElementOrLink).join(", ");
778
- // because we sometimes insert commas after links when mapping over an array of elements we will have double ,'s
779
- arrText = arrText.replace(/, ,/g, ",");
780
- text = arrText;
781
- }
629
+ const stringText = toString(text);
630
+ if (stringText === "[object Object]") return "";
631
+ return stringText;
632
+ },
633
+ [cellRenderer, currentParams, setNewParams]
634
+ );
782
635
 
783
- const stringText = toString(text);
784
- if (stringText === "[object Object]") return "";
785
- return stringText;
786
- };
636
+ const renderCheckboxCell = useCallback(
637
+ row => {
638
+ const rowIndex = row.index;
639
+ const checkedRows = getSelectedRowsFromEntities(
640
+ entities,
641
+ reduxFormSelectedEntityIdMap
642
+ );
643
+
644
+ const isSelected = checkedRows.some(rowNum => {
645
+ return rowNum === rowIndex;
646
+ });
647
+ if (rowIndex >= entities.length) {
648
+ return <div />;
649
+ }
650
+ const entity = entities[rowIndex];
651
+ return (
652
+ <Checkbox
653
+ name={`${getIdOrCodeOrIndex(entity, rowIndex)}-checkbox`}
654
+ disabled={noSelect || noUserSelect || isEntityDisabled(entity)}
655
+ onClick={e => {
656
+ rowClick(e, row, entities, {
657
+ reduxFormSelectedEntityIdMap,
658
+ isSingleSelect,
659
+ noSelect,
660
+ onRowClick,
661
+ isEntityDisabled,
662
+ withCheckboxes,
663
+ onDeselect,
664
+ onSingleRowSelect,
665
+ onMultiRowSelect,
666
+ noDeselectAll,
667
+ onRowSelect,
668
+ change
669
+ });
670
+ }}
671
+ checked={isSelected}
672
+ />
673
+ );
674
+ },
675
+ [
676
+ change,
677
+ entities,
678
+ isEntityDisabled,
679
+ isSingleSelect,
680
+ noDeselectAll,
681
+ noSelect,
682
+ noUserSelect,
683
+ onDeselect,
684
+ onMultiRowSelect,
685
+ onRowClick,
686
+ onRowSelect,
687
+ onSingleRowSelect,
688
+ reduxFormSelectedEntityIdMap,
689
+ withCheckboxes
690
+ ]
691
+ );
692
+
693
+ const finishCellEdit = useCallback(
694
+ (cellId, newVal, doNotStopEditing) => {
695
+ const [rowId, path] = cellId.split(":");
696
+ !doNotStopEditing && change("reduxFormEditingCell", null);
697
+ updateEntitiesHelper(entities, entities => {
698
+ const entity = entities.find((e, i) => {
699
+ return getIdOrCodeOrIndex(e, i) === rowId;
700
+ });
701
+ delete entity._isClean;
702
+ const { error } = editCellHelper({
703
+ entity,
704
+ path,
705
+ schema,
706
+ newVal
707
+ });
708
+
709
+ updateValidation(entities, {
710
+ ...reduxFormCellValidation,
711
+ [cellId]: error
712
+ });
713
+ });
714
+ !doNotStopEditing && refocusTable();
715
+ },
716
+ [
717
+ change,
718
+ entities,
719
+ reduxFormCellValidation,
720
+ refocusTable,
721
+ schema,
722
+ updateEntitiesHelper,
723
+ updateValidation
724
+ ]
725
+ );
726
+
727
+ const cancelCellEdit = useCallback(() => {
728
+ change("reduxFormEditingCell", null);
729
+ refocusTable();
730
+ }, [change, refocusTable]);
787
731
 
788
732
  if (!columns.length) {
789
733
  return columns;
@@ -848,72 +792,6 @@ export const RenderColumns = props => {
848
792
  });
849
793
  }
850
794
 
851
- const renderCheckboxCell = row => {
852
- const rowIndex = row.index;
853
- const checkedRows = getSelectedRowsFromEntities(
854
- entities,
855
- reduxFormSelectedEntityIdMap
856
- );
857
-
858
- const isSelected = checkedRows.some(rowNum => {
859
- return rowNum === rowIndex;
860
- });
861
- if (rowIndex >= entities.length) {
862
- return <div />;
863
- }
864
- const entity = entities[rowIndex];
865
- return (
866
- <Checkbox
867
- name={`${getIdOrCodeOrIndex(entity, rowIndex)}-checkbox`}
868
- disabled={noSelect || noUserSelect || isEntityDisabled(entity)}
869
- onClick={e => {
870
- rowClick(e, row, entities, {
871
- reduxFormSelectedEntityIdMap,
872
- isSingleSelect,
873
- noSelect,
874
- onRowClick,
875
- isEntityDisabled,
876
- withCheckboxes,
877
- onDeselect,
878
- onSingleRowSelect,
879
- onMultiRowSelect,
880
- noDeselectAll,
881
- onRowSelect,
882
- change
883
- });
884
- }}
885
- checked={isSelected}
886
- />
887
- );
888
- };
889
-
890
- const finishCellEdit = (cellId, newVal, doNotStopEditing) => {
891
- const [rowId, path] = cellId.split(":");
892
- !doNotStopEditing && setEditingCell(null);
893
- updateEntitiesHelper(entities, entities => {
894
- const entity = entities.find((e, i) => {
895
- return getIdOrCodeOrIndex(e, i) === rowId;
896
- });
897
- delete entity._isClean;
898
- const { error } = editCellHelper({
899
- entity,
900
- path,
901
- schema,
902
- newVal
903
- });
904
- updateValidation(entities, {
905
- ...reduxFormCellValidation,
906
- [cellId]: error
907
- });
908
- });
909
- !doNotStopEditing && refocusTable();
910
- };
911
-
912
- const cancelCellEdit = () => {
913
- setEditingCell(null);
914
- refocusTable();
915
- };
916
-
917
795
  if (withCheckboxes) {
918
796
  columnsToRender.push({
919
797
  Header: renderCheckboxHeader({
@@ -947,7 +825,7 @@ export const RenderColumns = props => {
947
825
  });
948
826
  }
949
827
 
950
- columns.forEach(column => {
828
+ const tableColumns = columns.map(column => {
951
829
  const tableColumn = {
952
830
  ...column,
953
831
  Header: RenderColumnHeader({
@@ -982,17 +860,18 @@ export const RenderColumns = props => {
982
860
  }
983
861
  if (cellRenderer && cellRenderer[column.path]) {
984
862
  tableColumn.Cell = row => {
985
- const val = cellRenderer[column.path](
986
- row.value,
987
- row.original,
988
- row,
989
- props
990
- );
863
+ const val = cellRenderer[column.path](row.value, row.original, row, {
864
+ currentParams,
865
+ setNewParams
866
+ });
991
867
  return val;
992
868
  };
993
869
  } else if (column.render) {
994
870
  tableColumn.Cell = row => {
995
- const val = column.render(row.value, row.original, row, props);
871
+ const val = column.render(row.value, row.original, row, {
872
+ currentParams,
873
+ setNewParams
874
+ });
996
875
  return val;
997
876
  };
998
877
  } else if (column.type === "timestamp") {
@@ -1040,6 +919,7 @@ export const RenderColumns = props => {
1040
919
  tableColumn.Cell = (...args) => (
1041
920
  <RenderCell
1042
921
  oldFunc={oldFunc}
922
+ formName={formName}
1043
923
  getCopyTextForCell={getCopyTextForCell}
1044
924
  column={column}
1045
925
  isCellEditable={isCellEditable}
@@ -1048,8 +928,6 @@ export const RenderColumns = props => {
1048
928
  noEllipsis={noEllipsis}
1049
929
  editingCell={editingCell}
1050
930
  cancelCellEdit={cancelCellEdit}
1051
- editableCellValue={editableCellValue}
1052
- setEditableCellValue={setEditableCellValue}
1053
931
  editingCellSelectAll={editingCellSelectAll}
1054
932
  getCellHoverText={getCellHoverText}
1055
933
  selectedCells={selectedCells}
@@ -1060,7 +938,8 @@ export const RenderColumns = props => {
1060
938
  args={args}
1061
939
  />
1062
940
  );
1063
- columnsToRender.push(tableColumn);
941
+ return tableColumn;
1064
942
  });
1065
- return columnsToRender;
943
+
944
+ return columnsToRender.concat(tableColumns);
1066
945
  };
@@ -0,0 +1,44 @@
1
+ import React, { useEffect, useRef, useState } from "react";
2
+
3
+ export const EditableCell = ({
4
+ cancelEdit,
5
+ dataTest,
6
+ finishEdit,
7
+ isNumeric,
8
+ initialValue
9
+ }) => {
10
+ const [value, setValue] = useState(initialValue);
11
+ const inputRef = useRef(null);
12
+
13
+ useEffect(() => {
14
+ if (inputRef.current) {
15
+ inputRef.current.focus();
16
+ }
17
+ }, [isNumeric]);
18
+
19
+ return (
20
+ <input
21
+ style={{
22
+ border: 0,
23
+ width: "95%",
24
+ fontSize: 12,
25
+ background: "none"
26
+ }}
27
+ ref={inputRef}
28
+ {...dataTest}
29
+ autoFocus
30
+ onKeyDown={e => {
31
+ e.stopPropagation();
32
+ if (e.key === "Enter") {
33
+ e.target.blur();
34
+ } else if (e.key === "Escape") {
35
+ cancelEdit();
36
+ }
37
+ }}
38
+ onBlur={() => finishEdit(value)}
39
+ onChange={e => setValue(e.target.value)}
40
+ type={isNumeric ? "number" : undefined}
41
+ value={value}
42
+ />
43
+ );
44
+ };
@@ -0,0 +1,191 @@
1
+ import React from "react";
2
+ import { useSelector } from "react-redux";
3
+ import { Checkbox, Icon } from "@blueprintjs/core";
4
+ import {
5
+ getIdOrCodeOrIndex,
6
+ isBottomRightCornerOfRectangle,
7
+ PRIMARY_SELECTED_VAL
8
+ } from "./utils";
9
+ import { DropdownCell } from "./DropdownCell";
10
+ import { EditableCell } from "./EditableCell";
11
+ import { getVals } from "./getVals";
12
+ import { CellDragHandle } from "./CellDragHandle";
13
+
14
+ export const RenderCell = ({
15
+ oldFunc,
16
+ getCopyTextForCell,
17
+ column,
18
+ isCellEditable,
19
+ isEntityDisabled,
20
+ finishCellEdit,
21
+ formName,
22
+ noEllipsis,
23
+ cancelCellEdit,
24
+ getCellHoverText,
25
+ selectedCells,
26
+ isSelectionARectangle,
27
+ startCellEdit,
28
+ tableRef,
29
+ onDragEnd,
30
+ args
31
+ }) => {
32
+ const editingCell = useSelector(
33
+ state => state.form?.[formName]?.values?.reduxFormEditingCell
34
+ );
35
+ const initialValue = useSelector(
36
+ state => state.form?.[formName]?.values?.reduxFormInitialValue
37
+ );
38
+
39
+ const [row] = args;
40
+ const rowId = getIdOrCodeOrIndex(row.original, row.index);
41
+ const cellId = `${rowId}:${row.column.path}`;
42
+ const isEditingCell = editingCell === cellId;
43
+ let val = oldFunc(...args);
44
+ const oldVal = val;
45
+ const text = getCopyTextForCell(val, row, column);
46
+ const dataTest = {
47
+ "data-test": "tgCell_" + column.path
48
+ };
49
+ const fullValue = row.original?.[row.column.path];
50
+
51
+ if (isEditingCell) {
52
+ if (column.type === "genericSelect") {
53
+ const GenericSelectComp = column.GenericSelectComp;
54
+
55
+ return (
56
+ <GenericSelectComp
57
+ rowId={rowId}
58
+ fullValue={fullValue}
59
+ initialValue={text}
60
+ {...dataTest}
61
+ finishEdit={(newVal, doNotStopEditing) => {
62
+ finishCellEdit(cellId, newVal, doNotStopEditing);
63
+ }}
64
+ dataTest={dataTest}
65
+ cancelEdit={cancelCellEdit}
66
+ />
67
+ );
68
+ }
69
+ if (column.type === "dropdown" || column.type === "dropdownMulti") {
70
+ return (
71
+ <DropdownCell
72
+ isMulti={dataTest.isMulti || column.type === "dropdownMulti"}
73
+ initialValue={dataTest.initialValue || text}
74
+ options={getVals(column.values)}
75
+ finishEdit={(newVal, doNotStopEditing) => {
76
+ finishCellEdit(cellId, newVal, doNotStopEditing);
77
+ }}
78
+ dataTest={dataTest}
79
+ cancelEdit={cancelCellEdit}
80
+ />
81
+ );
82
+ } else {
83
+ return (
84
+ <EditableCell
85
+ dataTest={dataTest}
86
+ cancelEdit={cancelCellEdit}
87
+ isNumeric={column.type === "number"}
88
+ initialValue={initialValue || text}
89
+ finishEdit={newVal => {
90
+ finishCellEdit(cellId, newVal);
91
+ }}
92
+ />
93
+ );
94
+ }
95
+ }
96
+
97
+ const isBool = column.type === "boolean";
98
+ if (isCellEditable && isBool) {
99
+ val = (
100
+ <Checkbox
101
+ disabled={isEntityDisabled(row.original)}
102
+ className="tg-cell-edit-boolean-checkbox"
103
+ checked={oldVal === "True"}
104
+ onChange={e => {
105
+ const checked = e.target.checked;
106
+ finishCellEdit(cellId, checked);
107
+ }}
108
+ />
109
+ );
110
+ noEllipsis = true;
111
+ }
112
+
113
+ //wrap the original tableColumn.Cell function in another div in order to add a title attribute
114
+ let title = text;
115
+ if (getCellHoverText) title = getCellHoverText(...args);
116
+ else if (column.getTitleAttr) title = column.getTitleAttr(...args);
117
+ const isSelectedCell = selectedCells?.[cellId];
118
+ const {
119
+ isRect,
120
+ selectionGrid,
121
+ lastRowIndex,
122
+ lastCellIndex,
123
+ entityMap,
124
+ pathToIndex
125
+ } = isSelectionARectangle();
126
+
127
+ return (
128
+ <>
129
+ <div
130
+ style={{
131
+ ...(!noEllipsis && {
132
+ textOverflow: "ellipsis",
133
+ overflow: "hidden"
134
+ })
135
+ }}
136
+ {...dataTest}
137
+ className="tg-cell-wrapper"
138
+ data-copy-text={text}
139
+ data-copy-json={JSON.stringify(
140
+ //tnw: eventually we'll parse these back out and use either the fullValue (for the generic selects) or the regular text vals for everything else
141
+ column.type === "genericSelect"
142
+ ? {
143
+ __strVal: fullValue,
144
+ __genSelCol: column.path
145
+ }
146
+ : { __strVal: text }
147
+ )}
148
+ title={title || undefined}
149
+ >
150
+ {val}
151
+ </div>
152
+ {isCellEditable &&
153
+ (column.type === "dropdown" ||
154
+ column.type === "dropdownMulti" ||
155
+ column.type === "genericSelect") && (
156
+ <Icon
157
+ icon="caret-down"
158
+ style={{
159
+ position: "absolute",
160
+ right: 5,
161
+ opacity: 0.3
162
+ }}
163
+ className="cell-edit-dropdown"
164
+ onClick={() => {
165
+ startCellEdit(cellId);
166
+ }}
167
+ />
168
+ )}
169
+
170
+ {isSelectedCell &&
171
+ (isRect
172
+ ? isBottomRightCornerOfRectangle({
173
+ cellId,
174
+ selectionGrid,
175
+ lastRowIndex,
176
+ lastCellIndex,
177
+ entityMap,
178
+ pathToIndex
179
+ })
180
+ : isSelectedCell === PRIMARY_SELECTED_VAL) && (
181
+ <CellDragHandle
182
+ key={cellId}
183
+ thisTable={tableRef.current.tableRef}
184
+ cellId={cellId}
185
+ isSelectionARectangle={isSelectionARectangle}
186
+ onDragEnd={onDragEnd}
187
+ />
188
+ )}
189
+ </>
190
+ );
191
+ };
@@ -93,11 +93,11 @@ class TgSelect extends React.Component {
93
93
  return true;
94
94
  });
95
95
  }
96
- return onChange([...valArray, item]);
96
+ return onChange([...valArray, item], e);
97
97
  } else {
98
98
  this.setOpenState(false);
99
99
  this.input && this.input.blur();
100
- return onChange(item);
100
+ return onChange(item, e);
101
101
  }
102
102
  };
103
103