@linzjs/step-ag-grid 15.0.2 → 15.1.0
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/README.md +27 -0
- package/dist/src/components/Grid.d.ts +10 -1
- package/dist/src/components/gridHook/index.d.ts +1 -0
- package/dist/src/components/gridHook/useGridContextMenu.d.ts +19 -0
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/contexts/GridContext.d.ts +1 -0
- package/dist/step-ag-grid.cjs.js +1329 -1275
- package/dist/step-ag-grid.cjs.js.map +1 -1
- package/dist/step-ag-grid.esm.js +1367 -1314
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Grid.tsx +18 -0
- package/src/components/gridHook/index.ts +1 -0
- package/src/components/gridHook/useGridContextMenu.tsx +92 -0
- package/src/components/index.ts +1 -0
- package/src/contexts/GridContext.tsx +4 -0
- package/src/contexts/GridContextProvider.tsx +11 -0
- package/src/stories/grid/GridPopoutContextMenu.stories.tsx +136 -0
package/dist/step-ag-grid.esm.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
1
|
+
import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
2
2
|
import { LuiMiniSpinner, LuiIcon, LuiButton, LuiCheckboxInput, LuiButtonGroup } from '@linzjs/lui';
|
|
3
3
|
import { AgGridReact } from 'ag-grid-react';
|
|
4
|
-
import { negate, isEmpty,
|
|
4
|
+
import { negate, isEmpty, findIndex, defer as defer$1, debounce as debounce$1, xorBy, last, difference, omit, sortBy, delay, partition, pick, groupBy, fromPairs, toPairs, isEqual, compact, filter, sumBy, pull, remove, castArray, flatten } from 'lodash-es';
|
|
5
5
|
import * as React from 'react';
|
|
6
|
-
import { createContext, useContext, useRef, useEffect, useCallback, useState, useMemo,
|
|
6
|
+
import { createContext, useContext, useRef, useEffect, useCallback, useState, useMemo, useLayoutEffect, memo, forwardRef, useReducer, cloneElement, useImperativeHandle, Fragment } from 'react';
|
|
7
7
|
import ReactDOM, { unstable_batchedUpdates, flushSync, createPortal } from 'react-dom';
|
|
8
8
|
import require$$0 from 'util';
|
|
9
9
|
import * as testUtils from 'react-dom/test-utils';
|
|
@@ -38,6 +38,9 @@ const GridContext = createContext({
|
|
|
38
38
|
prePopupOps: () => {
|
|
39
39
|
console.error("no context provider for prePopupOps");
|
|
40
40
|
},
|
|
41
|
+
postPopupOps: () => {
|
|
42
|
+
console.error("no context provider for prePopupOps");
|
|
43
|
+
},
|
|
41
44
|
externallySelectedItemsAreInSync: false,
|
|
42
45
|
setApis: () => {
|
|
43
46
|
console.error("no context provider for setApis");
|
|
@@ -454,1007 +457,378 @@ const GridHeaderSelect = ({ api }) => {
|
|
|
454
457
|
} }) }));
|
|
455
458
|
};
|
|
456
459
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
460
|
+
// Generate className following BEM methodology: http://getbem.com/naming/
|
|
461
|
+
// Modifier value can be one of the following types: boolean, string, undefined
|
|
462
|
+
const useBEM = ({ block, element, modifiers, className }) => useMemo(() => {
|
|
463
|
+
const blockElement = element ? `${block}__${element}` : block;
|
|
464
|
+
let classString = blockElement;
|
|
465
|
+
modifiers &&
|
|
466
|
+
Object.keys(modifiers).forEach((name) => {
|
|
467
|
+
const value = modifiers[name];
|
|
468
|
+
if (value)
|
|
469
|
+
classString += ` ${blockElement}--${value === true ? name : `${name}-${value}`}`;
|
|
470
|
+
});
|
|
471
|
+
let expandedClassName = typeof className === "function" ? className(modifiers) : className;
|
|
472
|
+
if (typeof expandedClassName === "string") {
|
|
473
|
+
expandedClassName = expandedClassName.trim();
|
|
474
|
+
if (expandedClassName)
|
|
475
|
+
classString += ` ${expandedClassName}`;
|
|
476
|
+
}
|
|
477
|
+
return classString;
|
|
478
|
+
}, [block, element, modifiers, className]);
|
|
479
|
+
|
|
480
|
+
// Adapted from material-ui
|
|
481
|
+
// https://github.com/mui-org/material-ui/blob/f996027d00e7e4bff3fc040786c1706f9c6c3f82/packages/material-ui-utils/src/useForkRef.ts
|
|
482
|
+
const setRef = (ref, instance) => {
|
|
483
|
+
if (typeof ref === "function") {
|
|
484
|
+
ref(instance);
|
|
485
|
+
}
|
|
486
|
+
else if (ref) {
|
|
487
|
+
ref.current = instance;
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
const useCombinedRef = (refA, refB) => {
|
|
491
|
+
return useMemo(() => {
|
|
492
|
+
return (instance) => {
|
|
493
|
+
setRef(refA, instance);
|
|
494
|
+
setRef(refB, instance);
|
|
495
|
+
};
|
|
496
|
+
}, [refA, refB]);
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// Get around a warning when using useLayoutEffect on the server.
|
|
500
|
+
// https://github.com/reduxjs/react-redux/blob/b48d087d76f666e1c6c5a9713bbec112a1631841/src/utils/useIsomorphicLayoutEffect.js#L12
|
|
501
|
+
// https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85
|
|
502
|
+
// https://github.com/facebook/react/issues/14927#issuecomment-549457471
|
|
503
|
+
const useIsomorphicLayoutEffect = typeof window !== "undefined" &&
|
|
504
|
+
typeof window.document !== "undefined" &&
|
|
505
|
+
typeof window.document.createElement !== "undefined"
|
|
506
|
+
? useLayoutEffect
|
|
507
|
+
: useEffect;
|
|
508
|
+
|
|
509
|
+
const menuContainerClass = "szh-menu-container";
|
|
510
|
+
const menuClass = "szh-menu";
|
|
511
|
+
const menuButtonClass = "szh-menu-button";
|
|
512
|
+
const menuArrowClass = "arrow";
|
|
513
|
+
const menuItemClass = "item";
|
|
514
|
+
const menuDividerClass = "divider";
|
|
515
|
+
const menuHeaderClass = "header";
|
|
516
|
+
const menuGroupClass = "group";
|
|
517
|
+
const subMenuClass = "submenu";
|
|
518
|
+
const radioGroupClass = "radio-group";
|
|
519
|
+
const Keys = Object.freeze({
|
|
520
|
+
ENTER: "Enter",
|
|
521
|
+
TAB: "Tab",
|
|
522
|
+
ESC: "Escape",
|
|
523
|
+
SPACE: " ",
|
|
524
|
+
HOME: "Home",
|
|
525
|
+
END: "End",
|
|
526
|
+
LEFT: "ArrowLeft",
|
|
527
|
+
RIGHT: "ArrowRight",
|
|
528
|
+
UP: "ArrowUp",
|
|
529
|
+
DOWN: "ArrowDown",
|
|
530
|
+
});
|
|
531
|
+
const HoverActionTypes = Object.freeze({
|
|
532
|
+
RESET: 0,
|
|
533
|
+
SET: 1,
|
|
534
|
+
UNSET: 2,
|
|
535
|
+
INCREASE: 3,
|
|
536
|
+
DECREASE: 4,
|
|
537
|
+
FIRST: 5,
|
|
538
|
+
LAST: 6,
|
|
539
|
+
SET_INDEX: 7,
|
|
540
|
+
});
|
|
541
|
+
const CloseReason = Object.freeze({
|
|
542
|
+
CLICK: "click",
|
|
543
|
+
CANCEL: "cancel",
|
|
544
|
+
BLUR: "blur",
|
|
545
|
+
SCROLL: "scroll",
|
|
546
|
+
TAB_FORWARD: "tab_forward",
|
|
547
|
+
TAB_BACKWARD: "tab_backward",
|
|
548
|
+
});
|
|
549
|
+
const FocusPositions = Object.freeze({
|
|
550
|
+
FIRST: "first",
|
|
551
|
+
LAST: "last",
|
|
552
|
+
});
|
|
553
|
+
const MenuStateMap = Object.freeze({
|
|
554
|
+
entering: "opening",
|
|
555
|
+
entered: "open",
|
|
556
|
+
exiting: "closing",
|
|
557
|
+
exited: "closed",
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
const isMenuOpen = (state) => !!state && state[0] === "o";
|
|
561
|
+
const batchedUpdates = unstable_batchedUpdates || ((callback) => callback());
|
|
562
|
+
const floatEqual = (a, b, diff = 0.0001) => Math.abs(a - b) < diff;
|
|
563
|
+
const getTransition = (transition, name) => transition === true || !!(transition && transition[name]);
|
|
564
|
+
function safeCall(fn, arg) {
|
|
565
|
+
return typeof fn === "function" ? fn(arg) : fn;
|
|
566
|
+
}
|
|
567
|
+
const internalKey = "_szhsinMenu";
|
|
568
|
+
const getName = (component) => component[internalKey];
|
|
569
|
+
const mergeProps = (target, source) => {
|
|
570
|
+
source &&
|
|
571
|
+
Object.keys(source).forEach((key) => {
|
|
572
|
+
const targetProp = target[key];
|
|
573
|
+
const sourceProp = source[key];
|
|
574
|
+
if (typeof sourceProp === "function" && targetProp) {
|
|
575
|
+
target[key] = (...arg) => {
|
|
576
|
+
sourceProp(...arg);
|
|
577
|
+
targetProp(...arg);
|
|
578
|
+
};
|
|
497
579
|
}
|
|
498
580
|
else {
|
|
499
|
-
|
|
500
|
-
hasSetContentSize.current = true;
|
|
501
|
-
params.onContentSize && params.onContentSize(result);
|
|
502
|
-
}
|
|
581
|
+
target[key] = sourceProp;
|
|
503
582
|
}
|
|
583
|
+
});
|
|
584
|
+
return target;
|
|
585
|
+
};
|
|
586
|
+
const parsePadding = (paddingStr) => {
|
|
587
|
+
if (paddingStr == null)
|
|
588
|
+
return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
589
|
+
const padding = paddingStr.trim().split(/\s+/, 4).map(parseFloat);
|
|
590
|
+
const top = !isNaN(padding[0]) ? padding[0] : 0;
|
|
591
|
+
const right = !isNaN(padding[1]) ? padding[1] : top;
|
|
592
|
+
return {
|
|
593
|
+
top,
|
|
594
|
+
right,
|
|
595
|
+
bottom: !isNaN(padding[2]) ? padding[2] : top,
|
|
596
|
+
left: !isNaN(padding[3]) ? padding[3] : right,
|
|
597
|
+
};
|
|
598
|
+
};
|
|
599
|
+
// Adapted from https://github.com/popperjs/popper-core/tree/v2.9.1/src/dom-utils
|
|
600
|
+
const getScrollAncestor = (node) => {
|
|
601
|
+
const thisWindow = (node?.ownerDocument ?? document).defaultView ?? window;
|
|
602
|
+
while (node) {
|
|
603
|
+
node = node.parentNode;
|
|
604
|
+
if (!node || node === thisWindow?.document?.body)
|
|
605
|
+
return null;
|
|
606
|
+
if (node instanceof Element) {
|
|
607
|
+
const { overflow, overflowX, overflowY } = thisWindow.getComputedStyle(node);
|
|
608
|
+
if (/auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX))
|
|
609
|
+
return node;
|
|
504
610
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
611
|
+
}
|
|
612
|
+
return null;
|
|
613
|
+
};
|
|
614
|
+
function commonProps(isDisabled, isHovering) {
|
|
615
|
+
return {
|
|
616
|
+
"aria-disabled": isDisabled || undefined,
|
|
617
|
+
tabIndex: isHovering ? 0 : -1,
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
const indexOfNode = (nodeList, node) => findIndex(nodeList, node);
|
|
621
|
+
const focusFirstInput = (container) => {
|
|
622
|
+
// We can't use instanceof Element in portals, so I use querySelectorAll as a proxy here
|
|
623
|
+
if (!container || !("querySelectorAll" in container))
|
|
624
|
+
return false;
|
|
625
|
+
const inputs = container.querySelectorAll("input[type='text'],input:not([type]),textarea");
|
|
626
|
+
const input = inputs[0];
|
|
627
|
+
// Using focus as proxy for HTMLElement
|
|
628
|
+
if (!input || !("focus" in input))
|
|
629
|
+
return false;
|
|
630
|
+
input.focus();
|
|
631
|
+
// Text areas should start at end
|
|
632
|
+
// this is a proxy for instanceof HTMLTextAreaElement
|
|
633
|
+
if (["textarea", "text"].includes(input.type)) {
|
|
634
|
+
input.setSelectionRange(0, input.value.length);
|
|
635
|
+
}
|
|
636
|
+
return true;
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
const HoverItemContext = createContext(undefined);
|
|
640
|
+
|
|
641
|
+
const withHovering = (name, WrappedComponent) => {
|
|
642
|
+
const Component = memo(WrappedComponent);
|
|
643
|
+
const WithHovering = forwardRef((props, ref) => {
|
|
644
|
+
const menuItemRef = useRef(null);
|
|
645
|
+
return (jsx(Component, { ...props, menuItemRef: menuItemRef, externalRef: ref, isHovering: useContext(HoverItemContext) === menuItemRef.current }));
|
|
530
646
|
});
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
647
|
+
WithHovering.displayName = `WithHovering(${name})`;
|
|
648
|
+
return WithHovering;
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
const useItems = (menuRef, focusRef) => {
|
|
652
|
+
const [hoverItem, setHoverItem] = useState();
|
|
653
|
+
const stateRef = useRef({
|
|
654
|
+
items: [],
|
|
655
|
+
hoverIndex: -1,
|
|
656
|
+
sorted: false,
|
|
657
|
+
});
|
|
658
|
+
const mutableState = stateRef.current;
|
|
659
|
+
const updateItems = useCallback((item, isMounted) => {
|
|
660
|
+
const { items } = mutableState;
|
|
661
|
+
if (!item) {
|
|
662
|
+
mutableState.items = [];
|
|
536
663
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
if (params.autoSelectFirstRow) {
|
|
549
|
-
selectRowsById([firstRowId]);
|
|
664
|
+
else if (isMounted) {
|
|
665
|
+
items.push(item);
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
const index = items.indexOf(item);
|
|
669
|
+
if (index > -1) {
|
|
670
|
+
items.splice(index, 1);
|
|
671
|
+
if (item.contains(document.activeElement)) {
|
|
672
|
+
focusRef?.current?.focus();
|
|
673
|
+
setHoverItem(undefined);
|
|
674
|
+
}
|
|
550
675
|
}
|
|
551
|
-
|
|
552
|
-
|
|
676
|
+
}
|
|
677
|
+
mutableState.hoverIndex = -1;
|
|
678
|
+
mutableState.sorted = false;
|
|
679
|
+
}, [mutableState, focusRef]);
|
|
680
|
+
const dispatch = useCallback((actionType, item, nextIndex) => {
|
|
681
|
+
const { items, hoverIndex } = mutableState;
|
|
682
|
+
const sortItems = () => {
|
|
683
|
+
if (mutableState.sorted)
|
|
684
|
+
return;
|
|
685
|
+
const orderedNodes = menuRef.current.querySelectorAll(".szh-menu__item");
|
|
686
|
+
items.sort((a, b) => indexOfNode(orderedNodes, a) - indexOfNode(orderedNodes, b));
|
|
687
|
+
mutableState.sorted = true;
|
|
688
|
+
};
|
|
689
|
+
let index = -1;
|
|
690
|
+
let newItem = undefined;
|
|
691
|
+
let newItemFn = undefined;
|
|
692
|
+
switch (actionType) {
|
|
693
|
+
case HoverActionTypes.RESET:
|
|
694
|
+
break;
|
|
695
|
+
case HoverActionTypes.SET:
|
|
696
|
+
newItem = item;
|
|
697
|
+
break;
|
|
698
|
+
case HoverActionTypes.UNSET:
|
|
699
|
+
newItemFn = (prevItem) => (prevItem === item ? undefined : prevItem);
|
|
700
|
+
break;
|
|
701
|
+
case HoverActionTypes.FIRST:
|
|
702
|
+
sortItems();
|
|
703
|
+
index = 0;
|
|
704
|
+
newItem = items[index];
|
|
705
|
+
break;
|
|
706
|
+
case HoverActionTypes.LAST:
|
|
707
|
+
sortItems();
|
|
708
|
+
index = items.length - 1;
|
|
709
|
+
newItem = items[index];
|
|
710
|
+
break;
|
|
711
|
+
case HoverActionTypes.SET_INDEX:
|
|
712
|
+
if (typeof nextIndex !== "number")
|
|
713
|
+
break;
|
|
714
|
+
sortItems();
|
|
715
|
+
index = nextIndex;
|
|
716
|
+
newItem = items[index];
|
|
717
|
+
defer$1(() => newItem.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" }));
|
|
718
|
+
break;
|
|
719
|
+
case HoverActionTypes.INCREASE:
|
|
720
|
+
sortItems();
|
|
721
|
+
index = hoverIndex;
|
|
722
|
+
if (index < 0)
|
|
723
|
+
index = items.indexOf(item);
|
|
724
|
+
index++;
|
|
725
|
+
if (index >= items.length)
|
|
726
|
+
index = 0;
|
|
727
|
+
newItem = items[index];
|
|
728
|
+
focusFirstInput(newItem);
|
|
729
|
+
break;
|
|
730
|
+
case HoverActionTypes.DECREASE: {
|
|
731
|
+
sortItems();
|
|
732
|
+
index = hoverIndex;
|
|
733
|
+
if (index < 0)
|
|
734
|
+
index = items.indexOf(item);
|
|
735
|
+
index--;
|
|
736
|
+
if (index < 0)
|
|
737
|
+
index = items.length - 1;
|
|
738
|
+
newItem = items[index];
|
|
739
|
+
focusFirstInput(newItem);
|
|
740
|
+
break;
|
|
553
741
|
}
|
|
742
|
+
default:
|
|
743
|
+
if (process.env.NODE_ENV !== "production")
|
|
744
|
+
throw new Error(`[React-Menu] Unknown hover action type: ${actionType}`);
|
|
554
745
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
* AgGrid checkbox select does not pass clicks within cell but not on the checkbox to checkbox.
|
|
567
|
-
* This passes the event to the checkbox when you click anywhere in the cell.
|
|
568
|
-
*/
|
|
569
|
-
const clickSelectorCheckboxWhenContainingCellClicked = useCallback(({ event }) => {
|
|
570
|
-
if (!event)
|
|
571
|
-
return;
|
|
572
|
-
const input = event.target.querySelector("input");
|
|
573
|
-
input?.dispatchEvent(event);
|
|
574
|
-
}, []);
|
|
575
|
-
/**
|
|
576
|
-
* Ensure external selected items list is in sync with panel.
|
|
577
|
-
*/
|
|
578
|
-
const synchroniseExternalStateToGridSelection = useCallback(({ api }) => {
|
|
579
|
-
if (!params.externalSelectedItems || !params.setExternalSelectedItems) {
|
|
580
|
-
setExternallySelectedItemsAreInSync(true);
|
|
746
|
+
if (!newItem && !newItemFn)
|
|
747
|
+
index = -1;
|
|
748
|
+
setHoverItem(newItem ?? newItemFn);
|
|
749
|
+
mutableState.hoverIndex = index;
|
|
750
|
+
}, [menuRef, mutableState]);
|
|
751
|
+
return { hoverItem, dispatch, updateItems };
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
const useItemEffect = (isDisabled, menuItemRef, updateItems) => {
|
|
755
|
+
useIsomorphicLayoutEffect(() => {
|
|
756
|
+
if (!menuItemRef)
|
|
581
757
|
return;
|
|
758
|
+
if (process.env.NODE_ENV !== "production" && !updateItems) {
|
|
759
|
+
throw new Error(`[React-Menu] This menu item or submenu should be rendered under a menu: ${menuItemRef.current.outerHTML}`);
|
|
582
760
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
761
|
+
if (isDisabled)
|
|
762
|
+
return;
|
|
763
|
+
const item = menuItemRef.current;
|
|
764
|
+
updateItems(item, true);
|
|
765
|
+
return () => {
|
|
766
|
+
updateItems(item);
|
|
767
|
+
};
|
|
768
|
+
}, [isDisabled, menuItemRef, updateItems]);
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
const ItemSettingsContext = createContext({
|
|
772
|
+
submenuOpenDelay: 0,
|
|
773
|
+
submenuCloseDelay: 0,
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
const MenuListItemContext = createContext({
|
|
777
|
+
dispatch: () => { },
|
|
778
|
+
updateItems: () => { },
|
|
779
|
+
setOpenSubmenuCount: () => 0,
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
// This hook includes some common stateful logic in MenuItem and FocusableItem
|
|
783
|
+
const useItemState = (menuItemRef, focusRef, isHovering, isDisabled) => {
|
|
784
|
+
const { submenuCloseDelay } = useContext(ItemSettingsContext);
|
|
785
|
+
const { isParentOpen, isSubmenuOpen, dispatch, updateItems } = useContext(MenuListItemContext);
|
|
786
|
+
const timeoutId = useRef();
|
|
787
|
+
const setHover = () => {
|
|
788
|
+
!isHovering && !isDisabled && dispatch(HoverActionTypes.SET, menuItemRef?.current, 0);
|
|
789
|
+
};
|
|
790
|
+
const unsetHover = () => {
|
|
791
|
+
!isDisabled && dispatch(HoverActionTypes.UNSET, menuItemRef?.current, 0);
|
|
792
|
+
};
|
|
793
|
+
const onBlur = (e) => {
|
|
794
|
+
// Focus has moved out of the entire item
|
|
795
|
+
// It handles situation such as clicking on a sibling disabled menu item
|
|
796
|
+
if (isHovering && !e.currentTarget.contains(e.relatedTarget))
|
|
797
|
+
unsetHover();
|
|
798
|
+
};
|
|
799
|
+
const onPointerMove = () => {
|
|
800
|
+
if (isSubmenuOpen) {
|
|
801
|
+
if (!timeoutId.current)
|
|
802
|
+
timeoutId.current = setTimeout(() => {
|
|
803
|
+
timeoutId.current = undefined;
|
|
804
|
+
setHover();
|
|
805
|
+
}, submenuCloseDelay);
|
|
589
806
|
}
|
|
590
807
|
else {
|
|
591
|
-
|
|
808
|
+
setHover();
|
|
592
809
|
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
const synchroniseExternallySelectedItemsToGrid = useCallback(() => {
|
|
599
|
-
if (!gridReady)
|
|
600
|
-
return;
|
|
601
|
-
if (!params.externalSelectedItems) {
|
|
602
|
-
setExternallySelectedItemsAreInSync(true);
|
|
603
|
-
return;
|
|
810
|
+
};
|
|
811
|
+
const onPointerLeave = (_, keepHover) => {
|
|
812
|
+
if (timeoutId.current) {
|
|
813
|
+
clearTimeout(timeoutId.current);
|
|
814
|
+
timeoutId.current = undefined;
|
|
604
815
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
816
|
+
!keepHover && unsetHover();
|
|
817
|
+
};
|
|
818
|
+
useItemEffect(isDisabled, menuItemRef, updateItems);
|
|
819
|
+
useEffect(() => () => clearTimeout(timeoutId.current), []);
|
|
820
|
+
useEffect(() => {
|
|
821
|
+
// Don't set focus when parent menu is closed, otherwise focus will be lost
|
|
822
|
+
// and onBlur event will be fired with relatedTarget setting as null.
|
|
823
|
+
if (isHovering && isParentOpen) {
|
|
824
|
+
focusRef?.current && focusRef.current.focus();
|
|
609
825
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
*/
|
|
617
|
-
const combineEditables = (...editables) => (params) => {
|
|
618
|
-
const results = editables.map((editable) => fnOrVar(editable, params));
|
|
619
|
-
// If editable is not set anywhere then it's non-editable
|
|
620
|
-
if (results.every((v) => v == null))
|
|
621
|
-
return false;
|
|
622
|
-
// If any editable value is or returns false then it's non-editable
|
|
623
|
-
return !results.some((v) => v == false);
|
|
624
|
-
};
|
|
625
|
-
/**
|
|
626
|
-
* Synchronise externally selected items to grid on externalSelectedItems change
|
|
627
|
-
*/
|
|
628
|
-
useEffect(synchroniseExternallySelectedItemsToGrid, [synchroniseExternallySelectedItemsToGrid]);
|
|
629
|
-
/**
|
|
630
|
-
* Add selectable column to colDefs. Adjust column defs to block fit for auto sized columns.
|
|
631
|
-
*/
|
|
632
|
-
const columnDefs = useMemo(() => {
|
|
633
|
-
const adjustColDefs = params.columnDefs.map((colDef) => {
|
|
634
|
-
const colDefEditable = colDef.editable;
|
|
635
|
-
const editable = combineEditables(params.readOnly !== true, params.defaultColDef?.editable, colDefEditable);
|
|
636
|
-
return {
|
|
637
|
-
...colDef,
|
|
638
|
-
editable,
|
|
639
|
-
cellClassRules: {
|
|
640
|
-
...colDef.cellClassRules,
|
|
641
|
-
"GridCell-readonly": (ccp) => !editable(ccp),
|
|
642
|
-
},
|
|
643
|
-
};
|
|
644
|
-
});
|
|
645
|
-
return params.selectable
|
|
646
|
-
? [
|
|
647
|
-
{
|
|
648
|
-
colId: "selection",
|
|
649
|
-
editable: false,
|
|
650
|
-
minWidth: 42,
|
|
651
|
-
maxWidth: 42,
|
|
652
|
-
pinned: selectColumnPinned,
|
|
653
|
-
headerComponentParams: {
|
|
654
|
-
exportable: false,
|
|
655
|
-
},
|
|
656
|
-
checkboxSelection: true,
|
|
657
|
-
headerComponent: rowSelection === "multiple" ? GridHeaderSelect : null,
|
|
658
|
-
suppressHeaderKeyboardEvent: (e) => {
|
|
659
|
-
if ((e.event.key === "Enter" || e.event.key === " ") && !e.event.repeat) {
|
|
660
|
-
if (isEmpty(e.api.getSelectedRows())) {
|
|
661
|
-
e.api.selectAllFiltered();
|
|
662
|
-
}
|
|
663
|
-
else {
|
|
664
|
-
e.api.deselectAll();
|
|
665
|
-
}
|
|
666
|
-
return true;
|
|
667
|
-
}
|
|
668
|
-
return false;
|
|
669
|
-
},
|
|
670
|
-
onCellClicked: clickSelectorCheckboxWhenContainingCellClicked,
|
|
671
|
-
},
|
|
672
|
-
...adjustColDefs,
|
|
673
|
-
]
|
|
674
|
-
: adjustColDefs;
|
|
675
|
-
}, [
|
|
676
|
-
params.columnDefs,
|
|
677
|
-
params.selectable,
|
|
678
|
-
params.readOnly,
|
|
679
|
-
params.defaultColDef?.editable,
|
|
680
|
-
selectColumnPinned,
|
|
681
|
-
rowSelection,
|
|
682
|
-
clickSelectorCheckboxWhenContainingCellClicked,
|
|
683
|
-
]);
|
|
684
|
-
/**
|
|
685
|
-
* When grid is ready set the apis to the grid context and sync selected items to grid.
|
|
686
|
-
*/
|
|
687
|
-
const onGridReady = useCallback((event) => {
|
|
688
|
-
setApis(event.api, event.columnApi, dataTestId);
|
|
689
|
-
synchroniseExternallySelectedItemsToGrid();
|
|
690
|
-
}, [dataTestId, setApis, synchroniseExternallySelectedItemsToGrid]);
|
|
691
|
-
/**
|
|
692
|
-
* When the grid is being initialized the data may be empty.
|
|
693
|
-
* This will resize columns when we have at least one row.
|
|
694
|
-
*/
|
|
695
|
-
const previousRowDataLength = useRef(0);
|
|
696
|
-
const onRowDataChanged = useCallback(() => {
|
|
697
|
-
const length = params.rowData?.length ?? 0;
|
|
698
|
-
if (previousRowDataLength.current !== length) {
|
|
699
|
-
setInitialContentSize();
|
|
700
|
-
previousRowDataLength.current = length;
|
|
701
|
-
}
|
|
702
|
-
if (lastUpdatedDep.current === updatedDep || isEmpty(colIdsEdited.current))
|
|
703
|
-
return;
|
|
704
|
-
lastUpdatedDep.current = updatedDep;
|
|
705
|
-
// Don't update while there are spinners
|
|
706
|
-
if (!isEmpty(updatingCols()))
|
|
707
|
-
return;
|
|
708
|
-
const skipHeader = sizeColumns === "auto-skip-headers";
|
|
709
|
-
autoSizeColumns({ skipHeader, userSizedColIds: userSizedColIds.current, colIds: colIdsEdited.current });
|
|
710
|
-
colIdsEdited.current.clear();
|
|
711
|
-
}, [autoSizeColumns, params.rowData?.length, setInitialContentSize, sizeColumns, updatedDep, updatingCols]);
|
|
712
|
-
/**
|
|
713
|
-
* Show/hide no rows overlay when model changes.
|
|
714
|
-
*/
|
|
715
|
-
const isShowingNoRowsOverlay = useRef(false);
|
|
716
|
-
const onModelUpdated = useCallback((event) => {
|
|
717
|
-
if (event.api.getDisplayedRowCount() === 0) {
|
|
718
|
-
if (!isShowingNoRowsOverlay.current) {
|
|
719
|
-
event.api.showNoRowsOverlay();
|
|
720
|
-
isShowingNoRowsOverlay.current = true;
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
else {
|
|
724
|
-
if (isShowingNoRowsOverlay.current) {
|
|
725
|
-
event.api.hideOverlay();
|
|
726
|
-
isShowingNoRowsOverlay.current = false;
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
}, []);
|
|
730
|
-
/**
|
|
731
|
-
* Force-refresh all selected rows to re-run class function, to update selection highlighting
|
|
732
|
-
*/
|
|
733
|
-
const refreshSelectedRows = useCallback((event) => {
|
|
734
|
-
event.api.refreshCells({
|
|
735
|
-
force: true,
|
|
736
|
-
rowNodes: event.api.getSelectedNodes(),
|
|
737
|
-
});
|
|
738
|
-
}, []);
|
|
739
|
-
/**
|
|
740
|
-
* Make sure node is selected for editing and start edit
|
|
741
|
-
*/
|
|
742
|
-
const startCellEditing = useCallback((event) => {
|
|
743
|
-
prePopupOps();
|
|
744
|
-
if (!event.node.isSelected()) {
|
|
745
|
-
event.node.setSelected(true, true);
|
|
746
|
-
}
|
|
747
|
-
// Cell already being edited, so don't re-edit until finished
|
|
748
|
-
if (checkUpdating([event.colDef.field ?? ""], event.data.id)) {
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
if (event.rowIndex !== null) {
|
|
752
|
-
event.api.startEditingCell({
|
|
753
|
-
rowIndex: event.rowIndex,
|
|
754
|
-
colKey: event.column.getColId(),
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
}, [checkUpdating, prePopupOps]);
|
|
758
|
-
/**
|
|
759
|
-
* Handle double click edit
|
|
760
|
-
*/
|
|
761
|
-
const onCellDoubleClick = useCallback((event) => {
|
|
762
|
-
if (!invokeEditAction(event))
|
|
763
|
-
startCellEditing(event);
|
|
764
|
-
}, [startCellEditing]);
|
|
765
|
-
/**
|
|
766
|
-
* Handle single click edits
|
|
767
|
-
*/
|
|
768
|
-
const onCellClicked = useCallback((event) => {
|
|
769
|
-
if (event.colDef?.cellRendererParams?.singleClickEdit) {
|
|
770
|
-
startCellEditing(event);
|
|
771
|
-
}
|
|
772
|
-
}, [startCellEditing]);
|
|
773
|
-
/**
|
|
774
|
-
* If cell has an edit action invoke it (if editable)
|
|
775
|
-
*/
|
|
776
|
-
const invokeEditAction = (e) => {
|
|
777
|
-
const editAction = e.colDef?.cellRendererParams?.editAction;
|
|
778
|
-
if (!editAction)
|
|
779
|
-
return false;
|
|
780
|
-
const editable = fnOrVar(e.colDef?.editable, e);
|
|
781
|
-
if (editable) {
|
|
782
|
-
if (!e.node.isSelected()) {
|
|
783
|
-
e.node.setSelected(true, true);
|
|
784
|
-
}
|
|
785
|
-
editAction([e.data, ...e.api.getSelectedRows().filter((row) => row.id !== e.data.id)]);
|
|
786
|
-
}
|
|
787
|
-
return true;
|
|
788
|
-
};
|
|
789
|
-
/**
|
|
790
|
-
* Start editing on pressing Enter
|
|
791
|
-
*/
|
|
792
|
-
const onCellKeyPress = useCallback((e) => {
|
|
793
|
-
if (e.event.key === "Enter") {
|
|
794
|
-
if (!invokeEditAction(e))
|
|
795
|
-
startCellEditing(e);
|
|
796
|
-
}
|
|
797
|
-
}, [startCellEditing]);
|
|
798
|
-
/**
|
|
799
|
-
* Once the grid has auto-sized we want to run fit to fit the grid in its container,
|
|
800
|
-
* but we don't want the non-flex auto-sized columns to "fit" size, so suppressSizeToFit is set to true.
|
|
801
|
-
*/
|
|
802
|
-
const columnDefsAdjusted = useMemo(() => {
|
|
803
|
-
const adjustColDefOrGroup = (colDef) => "children" in colDef ? adjustGroupColDef(colDef) : adjustColDef(colDef);
|
|
804
|
-
const adjustGroupColDef = (colDef) => ({
|
|
805
|
-
...colDef,
|
|
806
|
-
children: colDef.children.map((colDef) => adjustColDefOrGroup(colDef)),
|
|
807
|
-
});
|
|
808
|
-
const adjustColDef = (colDef) => ({
|
|
809
|
-
...colDef,
|
|
810
|
-
suppressSizeToFit: (sizeColumns === "auto" || sizeColumns === "auto-skip-headers") && !colDef.flex,
|
|
811
|
-
sortable: colDef.sortable && params.defaultColDef?.sortable !== false,
|
|
812
|
-
});
|
|
813
|
-
return columnDefs.map((colDef) => adjustColDefOrGroup(colDef));
|
|
814
|
-
}, [columnDefs, params.defaultColDef?.sortable, sizeColumns]);
|
|
815
|
-
/**
|
|
816
|
-
* Set of colIds that need auto-sizing.
|
|
817
|
-
*/
|
|
818
|
-
const colIdsEdited = useRef(new Set());
|
|
819
|
-
const lastUpdatedDep = useRef(updatedDep);
|
|
820
|
-
/**
|
|
821
|
-
* When cell editing has completed the colId as needing auto-sizing
|
|
822
|
-
*/
|
|
823
|
-
useEffect(() => {
|
|
824
|
-
if (lastUpdatedDep.current === updatedDep)
|
|
825
|
-
return;
|
|
826
|
-
lastUpdatedDep.current = updatedDep;
|
|
827
|
-
const colIds = updatingCols();
|
|
828
|
-
// Updating possibly completed
|
|
829
|
-
if (isEmpty(colIds)) {
|
|
830
|
-
// Columns to resize?
|
|
831
|
-
if (!isEmpty(colIdsEdited.current)) {
|
|
832
|
-
const skipHeader = sizeColumns === "auto-skip-headers";
|
|
833
|
-
if (sizeColumns === "auto" || skipHeader) {
|
|
834
|
-
defer$1(() => autoSizeColumns({
|
|
835
|
-
skipHeader,
|
|
836
|
-
userSizedColIds: userSizedColIds.current,
|
|
837
|
-
colIds: colIdsEdited.current,
|
|
838
|
-
}));
|
|
839
|
-
}
|
|
840
|
-
colIdsEdited.current.clear();
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
else {
|
|
844
|
-
// Updates not complete, add them to a list of columns needing resize
|
|
845
|
-
colIds.forEach((colId) => {
|
|
846
|
-
if (colId && !getColDef(colId)?.flex) {
|
|
847
|
-
colIdsEdited.current.add(colId);
|
|
848
|
-
}
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
}, [autoSizeColumns, getColDef, sizeColumns, updatedDep, updatingCols]);
|
|
852
|
-
/**
|
|
853
|
-
* Resize columns to fit if required on window/container resize
|
|
854
|
-
*/
|
|
855
|
-
const onGridSizeChanged = useCallback(() => {
|
|
856
|
-
if (sizeColumns !== "none") {
|
|
857
|
-
sizeColumnsToFit();
|
|
858
|
-
}
|
|
859
|
-
}, [sizeColumns, sizeColumnsToFit]);
|
|
860
|
-
/**
|
|
861
|
-
* Set of column Id's that are prevented from auto-sizing as they are user set
|
|
862
|
-
*/
|
|
863
|
-
const userSizedColIds = useRef(new Set());
|
|
864
|
-
/**
|
|
865
|
-
* Lock/unlock column width on user edit/reset.
|
|
866
|
-
*/
|
|
867
|
-
const onColumnResized = useCallback((e) => {
|
|
868
|
-
const colId = e.column?.getColId();
|
|
869
|
-
if (colId == null)
|
|
870
|
-
return;
|
|
871
|
-
switch (e.source) {
|
|
872
|
-
case "uiColumnDragged":
|
|
873
|
-
userSizedColIds.current.add(colId);
|
|
874
|
-
break;
|
|
875
|
-
case "autosizeColumns":
|
|
876
|
-
userSizedColIds.current.delete(colId);
|
|
877
|
-
break;
|
|
878
|
-
}
|
|
879
|
-
}, []);
|
|
880
|
-
setOnCellEditingComplete(params.onCellEditingComplete);
|
|
881
|
-
return (jsx("div", { "data-testid": dataTestId, className: clsx("Grid-container", theme, staleGrid && "Grid-sortIsStale", gridReady && params.rowData && "Grid-ready"), children: jsx("div", { style: { flex: 1 }, ref: gridDivRef, children: jsx(AgGridReact, { rowHeight: params.rowHeight, animateRows: params.animateRows, rowClassRules: params.rowClassRules, getRowId: (params) => `${params.data.id}`, suppressRowClickSelection: true, rowSelection: rowSelection, suppressBrowserResizeObserver: true, onGridSizeChanged: onGridSizeChanged, suppressColumnVirtualisation: suppressColumnVirtualization, suppressClickEdit: true, onColumnVisible: () => {
|
|
882
|
-
setInitialContentSize();
|
|
883
|
-
}, onRowDataChanged: onRowDataChanged, onCellKeyPress: onCellKeyPress, onCellClicked: onCellClicked, onCellDoubleClicked: onCellDoubleClick, onCellEditingStarted: refreshSelectedRows, domLayout: params.domLayout, onColumnResized: onColumnResized, defaultColDef: { minWidth: 48, ...omit(params.defaultColDef, ["editable"]) }, columnDefs: columnDefsAdjusted, rowData: params.rowData, noRowsOverlayComponent: GridNoRowsOverlay, noRowsOverlayComponentParams: {
|
|
884
|
-
rowData: params.rowData,
|
|
885
|
-
noRowsOverlayText: params.noRowsOverlayText,
|
|
886
|
-
}, onModelUpdated: onModelUpdated, onGridReady: onGridReady, onSortChanged: ensureSelectedRowIsVisible, postSortRows: params.postSortRows ?? postSortRows, onSelectionChanged: synchroniseExternalStateToGridSelection, onColumnMoved: params.onColumnMoved, alwaysShowVerticalScroll: params.alwaysShowVerticalScroll, isExternalFilterPresent: isExternalFilterPresent, doesExternalFilterPass: doesExternalFilterPass, maintainColumnOrder: true }) }) }));
|
|
887
|
-
};
|
|
888
|
-
|
|
889
|
-
const GridPopoverContext = createContext({
|
|
890
|
-
anchorRef: { current: null },
|
|
891
|
-
saving: false,
|
|
892
|
-
setSaving: () => { },
|
|
893
|
-
field: "",
|
|
894
|
-
value: null,
|
|
895
|
-
data: {},
|
|
896
|
-
selectedRows: [],
|
|
897
|
-
updateValue: async () => false,
|
|
898
|
-
formatValue: () => "! No gridPopoverContextProvider !",
|
|
899
|
-
});
|
|
900
|
-
const useGridPopoverContext = () => useContext(GridPopoverContext);
|
|
901
|
-
|
|
902
|
-
const GridPopoverContextProvider = ({ props, children }) => {
|
|
903
|
-
const { getFilteredSelectedRows, updatingCells } = useContext(GridContext);
|
|
904
|
-
const anchorRef = useRef(props.eGridCell);
|
|
905
|
-
const hasSaved = useRef(false);
|
|
906
|
-
const [saving, setSaving] = useState(false);
|
|
907
|
-
const { colDef } = props;
|
|
908
|
-
const { cellEditorParams } = colDef;
|
|
909
|
-
const multiEdit = cellEditorParams?.multiEdit ?? false;
|
|
910
|
-
// Then item that is clicked on will always be first in the list
|
|
911
|
-
const selectedRows = useMemo(() => multiEdit ? sortBy(getFilteredSelectedRows(), (row) => row.id !== props.data.id) : [props.data], [getFilteredSelectedRows, multiEdit, props.data]);
|
|
912
|
-
const field = props.colDef?.field ?? "";
|
|
913
|
-
const updateValue = useCallback(async (saveFn, tabDirection) => {
|
|
914
|
-
if (hasSaved.current)
|
|
915
|
-
return true;
|
|
916
|
-
hasSaved.current = true;
|
|
917
|
-
return saving ? false : await updatingCells({ selectedRows, field }, saveFn, setSaving, tabDirection);
|
|
918
|
-
}, [field, saving, selectedRows, updatingCells]);
|
|
919
|
-
return (jsx(GridPopoverContext.Provider, { value: {
|
|
920
|
-
anchorRef: anchorRef,
|
|
921
|
-
saving,
|
|
922
|
-
setSaving,
|
|
923
|
-
selectedRows,
|
|
924
|
-
field,
|
|
925
|
-
data: props.data,
|
|
926
|
-
value: props.value,
|
|
927
|
-
updateValue,
|
|
928
|
-
formatValue: props.formatValue,
|
|
929
|
-
}, children: children }));
|
|
930
|
-
};
|
|
931
|
-
|
|
932
|
-
const GridCellMultiSelectClassRules = {
|
|
933
|
-
"ag-selected-for-edit": (params) => {
|
|
934
|
-
const { api, node, colDef } = params;
|
|
935
|
-
const cep = colDef.cellEditorSelector
|
|
936
|
-
? colDef.cellEditorSelector(params)?.params
|
|
937
|
-
: colDef.cellEditorParams;
|
|
938
|
-
return (cep?.multiEdit &&
|
|
939
|
-
api
|
|
940
|
-
.getSelectedNodes()
|
|
941
|
-
.map((row) => row.id)
|
|
942
|
-
.includes(node.id) &&
|
|
943
|
-
api.getEditingCells().some((cell) => cell.column.getColDef() === colDef));
|
|
944
|
-
},
|
|
945
|
-
};
|
|
946
|
-
|
|
947
|
-
const GridIcon = (props) => (jsx(LuiIcon, { name: props.icon, title: props.title, alt: props.title, size: props.size ?? "md", className: clsx(`AgGridGenericCellRenderer-${props.icon}Icon`, props.className, props.disabled && "GridIcon-disabled") }));
|
|
948
|
-
|
|
949
|
-
const GridLoadableCell = () => (jsx(LuiMiniSpinner, { size: 22, divProps: { className: "GridLoadableCell-container", role: "status", "aria-label": "Loading" } }));
|
|
950
|
-
|
|
951
|
-
const GridCellRenderer = (props) => {
|
|
952
|
-
const { checkUpdating } = useContext(GridUpdatingContext);
|
|
953
|
-
const colDef = props.colDef;
|
|
954
|
-
const rendererParams = colDef.cellRendererParams;
|
|
955
|
-
const warningFn = rendererParams?.warning;
|
|
956
|
-
let warningText = warningFn ? warningFn(props) : undefined;
|
|
957
|
-
const infoFn = rendererParams?.info;
|
|
958
|
-
let infoText = infoFn ? infoFn(props) : undefined;
|
|
959
|
-
if (Array.isArray(warningText))
|
|
960
|
-
warningText = warningText.join("\n");
|
|
961
|
-
if (Array.isArray(infoText))
|
|
962
|
-
infoText = infoText.join("\n");
|
|
963
|
-
return checkUpdating(colDef.field ?? colDef.colId ?? "", props.data.id) ? (jsx(GridLoadableCell, {})) : (jsxs(Fragment, { children: [!!warningText && (jsx(GridIcon, { icon: "ic_warning_outline", title: typeof warningText === "string" ? warningText : "Warning" })), !!infoText && jsx(GridIcon, { icon: "ic_info_outline", title: typeof infoText === "string" ? infoText : "Info" }), jsx("div", { className: "GridCell-container", children: colDef.cellRendererParams?.originalCellRenderer ? (jsx(colDef.cellRendererParams.originalCellRenderer, { ...props })) : (jsx("span", { title: props.valueFormatted ?? undefined, children: props.valueFormatted })) }), fnOrVar(colDef.editable, props) && rendererParams?.rightHoverElement && (jsx("div", { className: "GridCell-hoverRight", children: rendererParams?.rightHoverElement }))] }));
|
|
964
|
-
};
|
|
965
|
-
const suppressCellKeyboardEvents = (e) => {
|
|
966
|
-
const shortcutKeys = e.colDef.cellRendererParams?.shortcutKeys ?? {};
|
|
967
|
-
const exec = shortcutKeys[e.event.key];
|
|
968
|
-
if (!e.editing && !e.event.repeat && e.event.type === "keypress" && exec) {
|
|
969
|
-
const editable = fnOrVar(e.colDef?.editable, e);
|
|
970
|
-
return editable ? exec(e) ?? true : true;
|
|
971
|
-
}
|
|
972
|
-
// It's important that aggrid doesn't trigger edit on enter
|
|
973
|
-
// as the incorrect selected rows will be returned
|
|
974
|
-
return !["ArrowLeft", "ArrowRight", "ArrowDown", "ArrowUp", "Tab", " ", "Home", "End", "PageUp", "PageDown"].includes(e.event.key);
|
|
975
|
-
};
|
|
976
|
-
const generateFilterGetter = (field, filterValueGetter, valueFormatter) => {
|
|
977
|
-
if (filterValueGetter)
|
|
978
|
-
return filterValueGetter;
|
|
979
|
-
// aggrid will default to valueGetter
|
|
980
|
-
if (typeof valueFormatter !== "function" || !field)
|
|
981
|
-
return undefined;
|
|
982
|
-
return (params) => {
|
|
983
|
-
const value = params.getValue(field);
|
|
984
|
-
let formattedValue = valueFormatter({ ...params, value });
|
|
985
|
-
// Search for null values using standard dash
|
|
986
|
-
if (formattedValue === "–")
|
|
987
|
-
formattedValue += " -";
|
|
988
|
-
// Search by raw value as well as formatted
|
|
989
|
-
const gotValue = ["string", "number"].includes(typeof value) ? value : undefined;
|
|
990
|
-
return (formattedValue + (gotValue != null && formattedValue != gotValue ? " " + gotValue : "")) //
|
|
991
|
-
.replaceAll(/\s+/g, " ")
|
|
992
|
-
.trim();
|
|
993
|
-
};
|
|
994
|
-
};
|
|
995
|
-
/*
|
|
996
|
-
* All cells should use this.
|
|
997
|
-
*/
|
|
998
|
-
const GridCell = (props, custom) => {
|
|
999
|
-
// Generate a default filter value getter which uses the formatted value plus
|
|
1000
|
-
// the editable value if it's a string and different from the formatted value.
|
|
1001
|
-
// This is so that e.g. bearings can be searched for by DMS or raw number.
|
|
1002
|
-
const valueFormatter = props.valueFormatter;
|
|
1003
|
-
const filterValueGetter = generateFilterGetter(props.field, props.filterValueGetter, valueFormatter);
|
|
1004
|
-
const exportable = props.exportable;
|
|
1005
|
-
// Can't leave this here ag-grid will complain
|
|
1006
|
-
delete props.exportable;
|
|
1007
|
-
return {
|
|
1008
|
-
colId: props.field ?? props.field,
|
|
1009
|
-
headerTooltip: props.headerName,
|
|
1010
|
-
sortable: !!(props?.field || props?.valueGetter),
|
|
1011
|
-
resizable: true,
|
|
1012
|
-
editable: props.editable ?? false,
|
|
1013
|
-
...(custom?.editor && {
|
|
1014
|
-
cellClassRules: GridCellMultiSelectClassRules,
|
|
1015
|
-
editable: props.editable ?? true,
|
|
1016
|
-
cellEditor: GenericCellEditorComponentWrapper(custom?.editor),
|
|
1017
|
-
}),
|
|
1018
|
-
suppressKeyboardEvent: suppressCellKeyboardEvents,
|
|
1019
|
-
...(custom?.editorParams && {
|
|
1020
|
-
cellEditorParams: {
|
|
1021
|
-
...custom.editorParams,
|
|
1022
|
-
multiEdit: custom.multiEdit,
|
|
1023
|
-
preventAutoEdit: custom.preventAutoEdit ?? false,
|
|
1024
|
-
},
|
|
1025
|
-
}),
|
|
1026
|
-
// If there's a valueFormatter and no filterValueGetter then create a filterValueGetter
|
|
1027
|
-
filterValueGetter,
|
|
1028
|
-
// Default value formatter, otherwise react freaks out on objects
|
|
1029
|
-
valueFormatter: (params) => {
|
|
1030
|
-
if (params.value == null)
|
|
1031
|
-
return "–";
|
|
1032
|
-
const types = ["number", "boolean", "string"];
|
|
1033
|
-
if (types.includes(typeof params.value))
|
|
1034
|
-
return `${params.value}`;
|
|
1035
|
-
else
|
|
1036
|
-
return JSON.stringify(params.value);
|
|
1037
|
-
},
|
|
1038
|
-
...props,
|
|
1039
|
-
cellRenderer: GridCellRenderer,
|
|
1040
|
-
cellRendererParams: {
|
|
1041
|
-
originalCellRenderer: props.cellRenderer,
|
|
1042
|
-
...props.cellRendererParams,
|
|
1043
|
-
},
|
|
1044
|
-
headerComponentParams: {
|
|
1045
|
-
exportable,
|
|
1046
|
-
...props.headerComponentParams,
|
|
1047
|
-
},
|
|
1048
|
-
};
|
|
1049
|
-
};
|
|
1050
|
-
const GenericCellEditorComponentWrapper = (editor) => {
|
|
1051
|
-
const obj = { editor };
|
|
1052
|
-
return forwardRef(function GenericCellEditorComponentFr(cellEditorParams, _) {
|
|
1053
|
-
const valueFormatted = cellEditorParams.formatValue
|
|
1054
|
-
? cellEditorParams.formatValue(cellEditorParams.value)
|
|
1055
|
-
: "Missing formatter";
|
|
1056
|
-
return (jsxs(GridPopoverContextProvider, { props: cellEditorParams, children: [jsx(cellEditorParams.colDef.cellRenderer, { ...cellEditorParams, valueFormatted: valueFormatted, ...cellEditorParams.colDef.cellRendererParams }), obj.editor && jsx(obj.editor, { ...cellEditorParams })] }));
|
|
1057
|
-
});
|
|
1058
|
-
};
|
|
1059
|
-
|
|
1060
|
-
const GridCellFillerColId = "gridCellFiller";
|
|
1061
|
-
const isGridCellFiller = (col) => col.colId === GridCellFillerColId;
|
|
1062
|
-
const GridCellFiller = () => ({
|
|
1063
|
-
colId: GridCellFillerColId,
|
|
1064
|
-
headerName: "",
|
|
1065
|
-
flex: 1,
|
|
1066
|
-
});
|
|
1067
|
-
|
|
1068
|
-
const Editor = (props) => ({
|
|
1069
|
-
component: GenericCellEditorComponentWrapper(props.editor),
|
|
1070
|
-
params: { ...props.editorParams, multiEdit: props.multiEdit },
|
|
1071
|
-
});
|
|
1072
|
-
/**
|
|
1073
|
-
* Used to choose between cell editors based in data.
|
|
1074
|
-
*/
|
|
1075
|
-
const GridCellMultiEditor = (props, cellEditorSelector) => GridCell({
|
|
1076
|
-
cellClassRules: GridCellMultiSelectClassRules,
|
|
1077
|
-
cellEditorSelector,
|
|
1078
|
-
editable: props.editable ?? true,
|
|
1079
|
-
...props,
|
|
1080
|
-
});
|
|
1081
|
-
|
|
1082
|
-
const GridFilterHeaderIconButton = forwardRef(function columnsButton({ icon, title, onClick, buttonProps, disabled = false, size = "md" }, ref) {
|
|
1083
|
-
return (jsx(LuiButton, { ...buttonProps, type: "button", level: "tertiary", className: "lui-button-icon-only", ref: ref, "aria-label": title, title: title, onClick: onClick, disabled: disabled, children: jsx(LuiIcon, { name: icon, alt: "Menu", size: size }) }));
|
|
1084
|
-
});
|
|
1085
|
-
|
|
1086
|
-
// Generate className following BEM methodology: http://getbem.com/naming/
|
|
1087
|
-
// Modifier value can be one of the following types: boolean, string, undefined
|
|
1088
|
-
const useBEM = ({ block, element, modifiers, className }) => useMemo(() => {
|
|
1089
|
-
const blockElement = element ? `${block}__${element}` : block;
|
|
1090
|
-
let classString = blockElement;
|
|
1091
|
-
modifiers &&
|
|
1092
|
-
Object.keys(modifiers).forEach((name) => {
|
|
1093
|
-
const value = modifiers[name];
|
|
1094
|
-
if (value)
|
|
1095
|
-
classString += ` ${blockElement}--${value === true ? name : `${name}-${value}`}`;
|
|
1096
|
-
});
|
|
1097
|
-
let expandedClassName = typeof className === "function" ? className(modifiers) : className;
|
|
1098
|
-
if (typeof expandedClassName === "string") {
|
|
1099
|
-
expandedClassName = expandedClassName.trim();
|
|
1100
|
-
if (expandedClassName)
|
|
1101
|
-
classString += ` ${expandedClassName}`;
|
|
1102
|
-
}
|
|
1103
|
-
return classString;
|
|
1104
|
-
}, [block, element, modifiers, className]);
|
|
1105
|
-
|
|
1106
|
-
// Adapted from material-ui
|
|
1107
|
-
// https://github.com/mui-org/material-ui/blob/f996027d00e7e4bff3fc040786c1706f9c6c3f82/packages/material-ui-utils/src/useForkRef.ts
|
|
1108
|
-
const setRef = (ref, instance) => {
|
|
1109
|
-
if (typeof ref === "function") {
|
|
1110
|
-
ref(instance);
|
|
1111
|
-
}
|
|
1112
|
-
else if (ref) {
|
|
1113
|
-
ref.current = instance;
|
|
1114
|
-
}
|
|
1115
|
-
};
|
|
1116
|
-
const useCombinedRef = (refA, refB) => {
|
|
1117
|
-
return useMemo(() => {
|
|
1118
|
-
return (instance) => {
|
|
1119
|
-
setRef(refA, instance);
|
|
1120
|
-
setRef(refB, instance);
|
|
1121
|
-
};
|
|
1122
|
-
}, [refA, refB]);
|
|
1123
|
-
};
|
|
1124
|
-
|
|
1125
|
-
// Get around a warning when using useLayoutEffect on the server.
|
|
1126
|
-
// https://github.com/reduxjs/react-redux/blob/b48d087d76f666e1c6c5a9713bbec112a1631841/src/utils/useIsomorphicLayoutEffect.js#L12
|
|
1127
|
-
// https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85
|
|
1128
|
-
// https://github.com/facebook/react/issues/14927#issuecomment-549457471
|
|
1129
|
-
const useIsomorphicLayoutEffect = typeof window !== "undefined" &&
|
|
1130
|
-
typeof window.document !== "undefined" &&
|
|
1131
|
-
typeof window.document.createElement !== "undefined"
|
|
1132
|
-
? useLayoutEffect
|
|
1133
|
-
: useEffect;
|
|
1134
|
-
|
|
1135
|
-
const menuContainerClass = "szh-menu-container";
|
|
1136
|
-
const menuClass = "szh-menu";
|
|
1137
|
-
const menuButtonClass = "szh-menu-button";
|
|
1138
|
-
const menuArrowClass = "arrow";
|
|
1139
|
-
const menuItemClass = "item";
|
|
1140
|
-
const menuDividerClass = "divider";
|
|
1141
|
-
const menuHeaderClass = "header";
|
|
1142
|
-
const menuGroupClass = "group";
|
|
1143
|
-
const subMenuClass = "submenu";
|
|
1144
|
-
const radioGroupClass = "radio-group";
|
|
1145
|
-
const Keys = Object.freeze({
|
|
1146
|
-
ENTER: "Enter",
|
|
1147
|
-
TAB: "Tab",
|
|
1148
|
-
ESC: "Escape",
|
|
1149
|
-
SPACE: " ",
|
|
1150
|
-
HOME: "Home",
|
|
1151
|
-
END: "End",
|
|
1152
|
-
LEFT: "ArrowLeft",
|
|
1153
|
-
RIGHT: "ArrowRight",
|
|
1154
|
-
UP: "ArrowUp",
|
|
1155
|
-
DOWN: "ArrowDown",
|
|
1156
|
-
});
|
|
1157
|
-
const HoverActionTypes = Object.freeze({
|
|
1158
|
-
RESET: 0,
|
|
1159
|
-
SET: 1,
|
|
1160
|
-
UNSET: 2,
|
|
1161
|
-
INCREASE: 3,
|
|
1162
|
-
DECREASE: 4,
|
|
1163
|
-
FIRST: 5,
|
|
1164
|
-
LAST: 6,
|
|
1165
|
-
SET_INDEX: 7,
|
|
1166
|
-
});
|
|
1167
|
-
const CloseReason = Object.freeze({
|
|
1168
|
-
CLICK: "click",
|
|
1169
|
-
CANCEL: "cancel",
|
|
1170
|
-
BLUR: "blur",
|
|
1171
|
-
SCROLL: "scroll",
|
|
1172
|
-
TAB_FORWARD: "tab_forward",
|
|
1173
|
-
TAB_BACKWARD: "tab_backward",
|
|
1174
|
-
});
|
|
1175
|
-
const FocusPositions = Object.freeze({
|
|
1176
|
-
FIRST: "first",
|
|
1177
|
-
LAST: "last",
|
|
1178
|
-
});
|
|
1179
|
-
const MenuStateMap = Object.freeze({
|
|
1180
|
-
entering: "opening",
|
|
1181
|
-
entered: "open",
|
|
1182
|
-
exiting: "closing",
|
|
1183
|
-
exited: "closed",
|
|
1184
|
-
});
|
|
1185
|
-
|
|
1186
|
-
const isMenuOpen = (state) => !!state && state[0] === "o";
|
|
1187
|
-
const batchedUpdates = unstable_batchedUpdates || ((callback) => callback());
|
|
1188
|
-
const floatEqual = (a, b, diff = 0.0001) => Math.abs(a - b) < diff;
|
|
1189
|
-
const getTransition = (transition, name) => transition === true || !!(transition && transition[name]);
|
|
1190
|
-
function safeCall(fn, arg) {
|
|
1191
|
-
return typeof fn === "function" ? fn(arg) : fn;
|
|
1192
|
-
}
|
|
1193
|
-
const internalKey = "_szhsinMenu";
|
|
1194
|
-
const getName = (component) => component[internalKey];
|
|
1195
|
-
const mergeProps = (target, source) => {
|
|
1196
|
-
source &&
|
|
1197
|
-
Object.keys(source).forEach((key) => {
|
|
1198
|
-
const targetProp = target[key];
|
|
1199
|
-
const sourceProp = source[key];
|
|
1200
|
-
if (typeof sourceProp === "function" && targetProp) {
|
|
1201
|
-
target[key] = (...arg) => {
|
|
1202
|
-
sourceProp(...arg);
|
|
1203
|
-
targetProp(...arg);
|
|
1204
|
-
};
|
|
1205
|
-
}
|
|
1206
|
-
else {
|
|
1207
|
-
target[key] = sourceProp;
|
|
1208
|
-
}
|
|
1209
|
-
});
|
|
1210
|
-
return target;
|
|
1211
|
-
};
|
|
1212
|
-
const parsePadding = (paddingStr) => {
|
|
1213
|
-
if (paddingStr == null)
|
|
1214
|
-
return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
1215
|
-
const padding = paddingStr.trim().split(/\s+/, 4).map(parseFloat);
|
|
1216
|
-
const top = !isNaN(padding[0]) ? padding[0] : 0;
|
|
1217
|
-
const right = !isNaN(padding[1]) ? padding[1] : top;
|
|
1218
|
-
return {
|
|
1219
|
-
top,
|
|
1220
|
-
right,
|
|
1221
|
-
bottom: !isNaN(padding[2]) ? padding[2] : top,
|
|
1222
|
-
left: !isNaN(padding[3]) ? padding[3] : right,
|
|
1223
|
-
};
|
|
1224
|
-
};
|
|
1225
|
-
// Adapted from https://github.com/popperjs/popper-core/tree/v2.9.1/src/dom-utils
|
|
1226
|
-
const getScrollAncestor = (node) => {
|
|
1227
|
-
const thisWindow = (node?.ownerDocument ?? document).defaultView ?? window;
|
|
1228
|
-
while (node) {
|
|
1229
|
-
node = node.parentNode;
|
|
1230
|
-
if (!node || node === thisWindow?.document?.body)
|
|
1231
|
-
return null;
|
|
1232
|
-
if (node instanceof Element) {
|
|
1233
|
-
const { overflow, overflowX, overflowY } = thisWindow.getComputedStyle(node);
|
|
1234
|
-
if (/auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX))
|
|
1235
|
-
return node;
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
return null;
|
|
1239
|
-
};
|
|
1240
|
-
function commonProps(isDisabled, isHovering) {
|
|
1241
|
-
return {
|
|
1242
|
-
"aria-disabled": isDisabled || undefined,
|
|
1243
|
-
tabIndex: isHovering ? 0 : -1,
|
|
1244
|
-
};
|
|
1245
|
-
}
|
|
1246
|
-
const indexOfNode = (nodeList, node) => findIndex(nodeList, node);
|
|
1247
|
-
const focusFirstInput = (container) => {
|
|
1248
|
-
// We can't use instanceof Element in portals, so I use querySelectorAll as a proxy here
|
|
1249
|
-
if (!container || !("querySelectorAll" in container))
|
|
1250
|
-
return false;
|
|
1251
|
-
const inputs = container.querySelectorAll("input[type='text'],input:not([type]),textarea");
|
|
1252
|
-
const input = inputs[0];
|
|
1253
|
-
// Using focus as proxy for HTMLElement
|
|
1254
|
-
if (!input || !("focus" in input))
|
|
1255
|
-
return false;
|
|
1256
|
-
input.focus();
|
|
1257
|
-
// Text areas should start at end
|
|
1258
|
-
// this is a proxy for instanceof HTMLTextAreaElement
|
|
1259
|
-
if (["textarea", "text"].includes(input.type)) {
|
|
1260
|
-
input.setSelectionRange(0, input.value.length);
|
|
1261
|
-
}
|
|
1262
|
-
return true;
|
|
1263
|
-
};
|
|
1264
|
-
|
|
1265
|
-
const HoverItemContext = createContext(undefined);
|
|
1266
|
-
|
|
1267
|
-
const withHovering = (name, WrappedComponent) => {
|
|
1268
|
-
const Component = memo(WrappedComponent);
|
|
1269
|
-
const WithHovering = forwardRef((props, ref) => {
|
|
1270
|
-
const menuItemRef = useRef(null);
|
|
1271
|
-
return (jsx(Component, { ...props, menuItemRef: menuItemRef, externalRef: ref, isHovering: useContext(HoverItemContext) === menuItemRef.current }));
|
|
1272
|
-
});
|
|
1273
|
-
WithHovering.displayName = `WithHovering(${name})`;
|
|
1274
|
-
return WithHovering;
|
|
1275
|
-
};
|
|
1276
|
-
|
|
1277
|
-
const useItems = (menuRef, focusRef) => {
|
|
1278
|
-
const [hoverItem, setHoverItem] = useState();
|
|
1279
|
-
const stateRef = useRef({
|
|
1280
|
-
items: [],
|
|
1281
|
-
hoverIndex: -1,
|
|
1282
|
-
sorted: false,
|
|
1283
|
-
});
|
|
1284
|
-
const mutableState = stateRef.current;
|
|
1285
|
-
const updateItems = useCallback((item, isMounted) => {
|
|
1286
|
-
const { items } = mutableState;
|
|
1287
|
-
if (!item) {
|
|
1288
|
-
mutableState.items = [];
|
|
1289
|
-
}
|
|
1290
|
-
else if (isMounted) {
|
|
1291
|
-
items.push(item);
|
|
1292
|
-
}
|
|
1293
|
-
else {
|
|
1294
|
-
const index = items.indexOf(item);
|
|
1295
|
-
if (index > -1) {
|
|
1296
|
-
items.splice(index, 1);
|
|
1297
|
-
if (item.contains(document.activeElement)) {
|
|
1298
|
-
focusRef?.current?.focus();
|
|
1299
|
-
setHoverItem(undefined);
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
mutableState.hoverIndex = -1;
|
|
1304
|
-
mutableState.sorted = false;
|
|
1305
|
-
}, [mutableState, focusRef]);
|
|
1306
|
-
const dispatch = useCallback((actionType, item, nextIndex) => {
|
|
1307
|
-
const { items, hoverIndex } = mutableState;
|
|
1308
|
-
const sortItems = () => {
|
|
1309
|
-
if (mutableState.sorted)
|
|
1310
|
-
return;
|
|
1311
|
-
const orderedNodes = menuRef.current.querySelectorAll(".szh-menu__item");
|
|
1312
|
-
items.sort((a, b) => indexOfNode(orderedNodes, a) - indexOfNode(orderedNodes, b));
|
|
1313
|
-
mutableState.sorted = true;
|
|
1314
|
-
};
|
|
1315
|
-
let index = -1;
|
|
1316
|
-
let newItem = undefined;
|
|
1317
|
-
let newItemFn = undefined;
|
|
1318
|
-
switch (actionType) {
|
|
1319
|
-
case HoverActionTypes.RESET:
|
|
1320
|
-
break;
|
|
1321
|
-
case HoverActionTypes.SET:
|
|
1322
|
-
newItem = item;
|
|
1323
|
-
break;
|
|
1324
|
-
case HoverActionTypes.UNSET:
|
|
1325
|
-
newItemFn = (prevItem) => (prevItem === item ? undefined : prevItem);
|
|
1326
|
-
break;
|
|
1327
|
-
case HoverActionTypes.FIRST:
|
|
1328
|
-
sortItems();
|
|
1329
|
-
index = 0;
|
|
1330
|
-
newItem = items[index];
|
|
1331
|
-
break;
|
|
1332
|
-
case HoverActionTypes.LAST:
|
|
1333
|
-
sortItems();
|
|
1334
|
-
index = items.length - 1;
|
|
1335
|
-
newItem = items[index];
|
|
1336
|
-
break;
|
|
1337
|
-
case HoverActionTypes.SET_INDEX:
|
|
1338
|
-
if (typeof nextIndex !== "number")
|
|
1339
|
-
break;
|
|
1340
|
-
sortItems();
|
|
1341
|
-
index = nextIndex;
|
|
1342
|
-
newItem = items[index];
|
|
1343
|
-
defer$1(() => newItem.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" }));
|
|
1344
|
-
break;
|
|
1345
|
-
case HoverActionTypes.INCREASE:
|
|
1346
|
-
sortItems();
|
|
1347
|
-
index = hoverIndex;
|
|
1348
|
-
if (index < 0)
|
|
1349
|
-
index = items.indexOf(item);
|
|
1350
|
-
index++;
|
|
1351
|
-
if (index >= items.length)
|
|
1352
|
-
index = 0;
|
|
1353
|
-
newItem = items[index];
|
|
1354
|
-
focusFirstInput(newItem);
|
|
1355
|
-
break;
|
|
1356
|
-
case HoverActionTypes.DECREASE: {
|
|
1357
|
-
sortItems();
|
|
1358
|
-
index = hoverIndex;
|
|
1359
|
-
if (index < 0)
|
|
1360
|
-
index = items.indexOf(item);
|
|
1361
|
-
index--;
|
|
1362
|
-
if (index < 0)
|
|
1363
|
-
index = items.length - 1;
|
|
1364
|
-
newItem = items[index];
|
|
1365
|
-
focusFirstInput(newItem);
|
|
1366
|
-
break;
|
|
1367
|
-
}
|
|
1368
|
-
default:
|
|
1369
|
-
if (process.env.NODE_ENV !== "production")
|
|
1370
|
-
throw new Error(`[React-Menu] Unknown hover action type: ${actionType}`);
|
|
1371
|
-
}
|
|
1372
|
-
if (!newItem && !newItemFn)
|
|
1373
|
-
index = -1;
|
|
1374
|
-
setHoverItem(newItem ?? newItemFn);
|
|
1375
|
-
mutableState.hoverIndex = index;
|
|
1376
|
-
}, [menuRef, mutableState]);
|
|
1377
|
-
return { hoverItem, dispatch, updateItems };
|
|
1378
|
-
};
|
|
1379
|
-
|
|
1380
|
-
const useItemEffect = (isDisabled, menuItemRef, updateItems) => {
|
|
1381
|
-
useIsomorphicLayoutEffect(() => {
|
|
1382
|
-
if (!menuItemRef)
|
|
1383
|
-
return;
|
|
1384
|
-
if (process.env.NODE_ENV !== "production" && !updateItems) {
|
|
1385
|
-
throw new Error(`[React-Menu] This menu item or submenu should be rendered under a menu: ${menuItemRef.current.outerHTML}`);
|
|
1386
|
-
}
|
|
1387
|
-
if (isDisabled)
|
|
1388
|
-
return;
|
|
1389
|
-
const item = menuItemRef.current;
|
|
1390
|
-
updateItems(item, true);
|
|
1391
|
-
return () => {
|
|
1392
|
-
updateItems(item);
|
|
1393
|
-
};
|
|
1394
|
-
}, [isDisabled, menuItemRef, updateItems]);
|
|
1395
|
-
};
|
|
1396
|
-
|
|
1397
|
-
const ItemSettingsContext = createContext({
|
|
1398
|
-
submenuOpenDelay: 0,
|
|
1399
|
-
submenuCloseDelay: 0,
|
|
1400
|
-
});
|
|
1401
|
-
|
|
1402
|
-
const MenuListItemContext = createContext({
|
|
1403
|
-
dispatch: () => { },
|
|
1404
|
-
updateItems: () => { },
|
|
1405
|
-
setOpenSubmenuCount: () => 0,
|
|
1406
|
-
});
|
|
1407
|
-
|
|
1408
|
-
// This hook includes some common stateful logic in MenuItem and FocusableItem
|
|
1409
|
-
const useItemState = (menuItemRef, focusRef, isHovering, isDisabled) => {
|
|
1410
|
-
const { submenuCloseDelay } = useContext(ItemSettingsContext);
|
|
1411
|
-
const { isParentOpen, isSubmenuOpen, dispatch, updateItems } = useContext(MenuListItemContext);
|
|
1412
|
-
const timeoutId = useRef();
|
|
1413
|
-
const setHover = () => {
|
|
1414
|
-
!isHovering && !isDisabled && dispatch(HoverActionTypes.SET, menuItemRef?.current, 0);
|
|
1415
|
-
};
|
|
1416
|
-
const unsetHover = () => {
|
|
1417
|
-
!isDisabled && dispatch(HoverActionTypes.UNSET, menuItemRef?.current, 0);
|
|
1418
|
-
};
|
|
1419
|
-
const onBlur = (e) => {
|
|
1420
|
-
// Focus has moved out of the entire item
|
|
1421
|
-
// It handles situation such as clicking on a sibling disabled menu item
|
|
1422
|
-
if (isHovering && !e.currentTarget.contains(e.relatedTarget))
|
|
1423
|
-
unsetHover();
|
|
1424
|
-
};
|
|
1425
|
-
const onPointerMove = () => {
|
|
1426
|
-
if (isSubmenuOpen) {
|
|
1427
|
-
if (!timeoutId.current)
|
|
1428
|
-
timeoutId.current = setTimeout(() => {
|
|
1429
|
-
timeoutId.current = undefined;
|
|
1430
|
-
setHover();
|
|
1431
|
-
}, submenuCloseDelay);
|
|
1432
|
-
}
|
|
1433
|
-
else {
|
|
1434
|
-
setHover();
|
|
1435
|
-
}
|
|
1436
|
-
};
|
|
1437
|
-
const onPointerLeave = (_, keepHover) => {
|
|
1438
|
-
if (timeoutId.current) {
|
|
1439
|
-
clearTimeout(timeoutId.current);
|
|
1440
|
-
timeoutId.current = undefined;
|
|
1441
|
-
}
|
|
1442
|
-
!keepHover && unsetHover();
|
|
1443
|
-
};
|
|
1444
|
-
useItemEffect(isDisabled, menuItemRef, updateItems);
|
|
1445
|
-
useEffect(() => () => clearTimeout(timeoutId.current), []);
|
|
1446
|
-
useEffect(() => {
|
|
1447
|
-
// Don't set focus when parent menu is closed, otherwise focus will be lost
|
|
1448
|
-
// and onBlur event will be fired with relatedTarget setting as null.
|
|
1449
|
-
if (isHovering && isParentOpen) {
|
|
1450
|
-
focusRef?.current && focusRef.current.focus();
|
|
1451
|
-
}
|
|
1452
|
-
}, [focusRef, isHovering, isParentOpen]);
|
|
1453
|
-
return {
|
|
1454
|
-
setHover,
|
|
1455
|
-
onBlur,
|
|
1456
|
-
onPointerMove,
|
|
1457
|
-
onPointerLeave,
|
|
826
|
+
}, [focusRef, isHovering, isParentOpen]);
|
|
827
|
+
return {
|
|
828
|
+
setHover,
|
|
829
|
+
onBlur,
|
|
830
|
+
onPointerMove,
|
|
831
|
+
onPointerLeave,
|
|
1458
832
|
};
|
|
1459
833
|
};
|
|
1460
834
|
|
|
@@ -2543,360 +1917,1028 @@ const ControlledMenuFr = ({ "aria-label": ariaLabel, className, containerProps,
|
|
|
2543
1917
|
else {
|
|
2544
1918
|
return portal.target ? createPortal(menuList, portal.target) : portal.stablePosition ? null : menuList;
|
|
2545
1919
|
}
|
|
2546
|
-
}
|
|
2547
|
-
return menuList;
|
|
2548
|
-
};
|
|
2549
|
-
const ControlledMenu = forwardRef(ControlledMenuFr);
|
|
2550
|
-
|
|
2551
|
-
function MenuFr({ "aria-label": ariaLabel, menuButton, instanceRef, onMenuChange, ...restProps }, externalRef) {
|
|
2552
|
-
const [stateProps, toggleMenu, openMenu] = useMenuStateAndFocus(restProps);
|
|
2553
|
-
const isOpen = isMenuOpen(stateProps.state);
|
|
2554
|
-
const skipOpen = useRef(false);
|
|
2555
|
-
const buttonRef = useRef(null);
|
|
2556
|
-
const handleClose = useCallback((e) => {
|
|
2557
|
-
toggleMenu(false);
|
|
2558
|
-
if (e.key)
|
|
2559
|
-
buttonRef.current && buttonRef.current.focus();
|
|
2560
|
-
}, [toggleMenu]);
|
|
2561
|
-
const onClick = (e) => {
|
|
2562
|
-
if (skipOpen.current)
|
|
2563
|
-
return;
|
|
2564
|
-
// Focus (hover) the first menu item when onClick event is trigger by keyboard
|
|
2565
|
-
openMenu(e.detail === 0 ? FocusPositions.FIRST : undefined);
|
|
1920
|
+
}
|
|
1921
|
+
return menuList;
|
|
1922
|
+
};
|
|
1923
|
+
const ControlledMenu = forwardRef(ControlledMenuFr);
|
|
1924
|
+
|
|
1925
|
+
function MenuFr({ "aria-label": ariaLabel, menuButton, instanceRef, onMenuChange, ...restProps }, externalRef) {
|
|
1926
|
+
const [stateProps, toggleMenu, openMenu] = useMenuStateAndFocus(restProps);
|
|
1927
|
+
const isOpen = isMenuOpen(stateProps.state);
|
|
1928
|
+
const skipOpen = useRef(false);
|
|
1929
|
+
const buttonRef = useRef(null);
|
|
1930
|
+
const handleClose = useCallback((e) => {
|
|
1931
|
+
toggleMenu(false);
|
|
1932
|
+
if (e.key)
|
|
1933
|
+
buttonRef.current && buttonRef.current.focus();
|
|
1934
|
+
}, [toggleMenu]);
|
|
1935
|
+
const onClick = (e) => {
|
|
1936
|
+
if (skipOpen.current)
|
|
1937
|
+
return;
|
|
1938
|
+
// Focus (hover) the first menu item when onClick event is trigger by keyboard
|
|
1939
|
+
openMenu(e.detail === 0 ? FocusPositions.FIRST : undefined);
|
|
1940
|
+
};
|
|
1941
|
+
const onKeyDown = (e) => {
|
|
1942
|
+
switch (e.key) {
|
|
1943
|
+
case Keys.UP:
|
|
1944
|
+
openMenu(FocusPositions.LAST);
|
|
1945
|
+
break;
|
|
1946
|
+
case Keys.DOWN:
|
|
1947
|
+
openMenu(FocusPositions.FIRST);
|
|
1948
|
+
break;
|
|
1949
|
+
default:
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
e.preventDefault();
|
|
1953
|
+
};
|
|
1954
|
+
// Too many mixed types here to figure out what to pick for button. Bad code.
|
|
1955
|
+
const button = safeCall(menuButton, { open: isOpen });
|
|
1956
|
+
if (!button || !button.type)
|
|
1957
|
+
throw new Error("Menu requires a menuButton prop.");
|
|
1958
|
+
const buttonProps = {
|
|
1959
|
+
ref: useCombinedRef(button.ref, buttonRef),
|
|
1960
|
+
...mergeProps({ onClick, onKeyDown }, button.props),
|
|
1961
|
+
isOpen: false,
|
|
1962
|
+
};
|
|
1963
|
+
if (getName(button.type) === "MenuButton") {
|
|
1964
|
+
buttonProps.isOpen = isOpen;
|
|
1965
|
+
}
|
|
1966
|
+
const renderButton = cloneElement(button, buttonProps);
|
|
1967
|
+
useMenuChange(onMenuChange, isOpen);
|
|
1968
|
+
useImperativeHandle(instanceRef, () => ({
|
|
1969
|
+
openMenu,
|
|
1970
|
+
closeMenu: () => toggleMenu(false),
|
|
1971
|
+
}));
|
|
1972
|
+
return (jsxs(Fragment, { children: [renderButton, jsx(ControlledMenu, { ...restProps, ...stateProps, "aria-label": ariaLabel || (typeof button.props.children === "string" ? button.props.children : "Menu"), anchorRef: buttonRef, ref: externalRef, onClose: handleClose, skipOpen: skipOpen })] }));
|
|
1973
|
+
}
|
|
1974
|
+
const Menu = forwardRef(MenuFr);
|
|
1975
|
+
|
|
1976
|
+
const SubMenuFr = ({ "aria-label": ariaLabel, className, disabled, direction, label, openTrigger, onMenuChange, isHovering, instanceRef, menuItemRef, itemProps = {}, ...restProps }) => {
|
|
1977
|
+
const settings = useContext(SettingsContext);
|
|
1978
|
+
const { rootMenuRef } = settings;
|
|
1979
|
+
const { submenuOpenDelay, submenuCloseDelay } = useContext(ItemSettingsContext);
|
|
1980
|
+
const { parentMenuRef, parentDir, overflow: parentOverflow } = useContext(MenuListContext);
|
|
1981
|
+
const { isParentOpen, isSubmenuOpen, setOpenSubmenuCount, dispatch, updateItems } = useContext(MenuListItemContext);
|
|
1982
|
+
const isPortal = parentOverflow !== "visible";
|
|
1983
|
+
const [stateProps, toggleMenu, _openMenu] = useMenuStateAndFocus(settings);
|
|
1984
|
+
const { state } = stateProps;
|
|
1985
|
+
const isDisabled = !!disabled;
|
|
1986
|
+
const isOpen = isMenuOpen(state);
|
|
1987
|
+
const containerRef = useRef(null);
|
|
1988
|
+
const timeoutId = useRef();
|
|
1989
|
+
const stopTimer = () => {
|
|
1990
|
+
if (timeoutId.current) {
|
|
1991
|
+
clearTimeout(timeoutId.current);
|
|
1992
|
+
timeoutId.current = undefined;
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
const openMenu = (...args) => {
|
|
1996
|
+
stopTimer();
|
|
1997
|
+
setHover();
|
|
1998
|
+
!isDisabled && _openMenu(...args);
|
|
1999
|
+
};
|
|
2000
|
+
const setHover = () => !isHovering && !isDisabled && dispatch(HoverActionTypes.SET, menuItemRef?.current, 0);
|
|
2001
|
+
const delayOpen = (delay) => {
|
|
2002
|
+
setHover();
|
|
2003
|
+
if (!openTrigger)
|
|
2004
|
+
timeoutId.current = setTimeout(() => batchedUpdates(openMenu), Math.max(delay, 0));
|
|
2005
|
+
};
|
|
2006
|
+
const handlePointerMove = () => {
|
|
2007
|
+
if (timeoutId.current || isOpen || isDisabled)
|
|
2008
|
+
return;
|
|
2009
|
+
if (isSubmenuOpen) {
|
|
2010
|
+
timeoutId.current = setTimeout(() => delayOpen(submenuOpenDelay - submenuCloseDelay), submenuCloseDelay);
|
|
2011
|
+
}
|
|
2012
|
+
else {
|
|
2013
|
+
delayOpen(submenuOpenDelay);
|
|
2014
|
+
}
|
|
2015
|
+
};
|
|
2016
|
+
const handlePointerLeave = () => {
|
|
2017
|
+
stopTimer();
|
|
2018
|
+
if (!isOpen)
|
|
2019
|
+
dispatch(HoverActionTypes.UNSET, menuItemRef?.current, 0);
|
|
2020
|
+
};
|
|
2021
|
+
const handleKeyDown = (e) => {
|
|
2022
|
+
let handled = false;
|
|
2023
|
+
switch (e.key) {
|
|
2024
|
+
// LEFT key is bubbled up from submenu items
|
|
2025
|
+
case Keys.LEFT:
|
|
2026
|
+
if (isOpen) {
|
|
2027
|
+
menuItemRef?.current && menuItemRef.current.focus();
|
|
2028
|
+
toggleMenu(false);
|
|
2029
|
+
handled = true;
|
|
2030
|
+
}
|
|
2031
|
+
break;
|
|
2032
|
+
// prevent browser from scrolling page to the right
|
|
2033
|
+
case Keys.RIGHT:
|
|
2034
|
+
if (!isOpen)
|
|
2035
|
+
handled = true;
|
|
2036
|
+
break;
|
|
2037
|
+
}
|
|
2038
|
+
if (handled) {
|
|
2039
|
+
e.preventDefault();
|
|
2040
|
+
e.stopPropagation();
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
const handleItemKeyDown = (e) => {
|
|
2044
|
+
if (!isHovering)
|
|
2045
|
+
return;
|
|
2046
|
+
switch (e.key) {
|
|
2047
|
+
case Keys.ENTER:
|
|
2048
|
+
case Keys.SPACE:
|
|
2049
|
+
case Keys.RIGHT:
|
|
2050
|
+
openTrigger !== "none" && openMenu(FocusPositions.FIRST);
|
|
2051
|
+
break;
|
|
2052
|
+
}
|
|
2053
|
+
};
|
|
2054
|
+
useItemEffect(isDisabled, menuItemRef, updateItems);
|
|
2055
|
+
useMenuChange(onMenuChange, isOpen);
|
|
2056
|
+
useEffect(() => () => clearTimeout(timeoutId.current), []);
|
|
2057
|
+
useEffect(() => {
|
|
2058
|
+
// Don't set focus when parent menu is closed, otherwise focus will be lost
|
|
2059
|
+
// and onBlur event will be fired with relatedTarget setting as null.
|
|
2060
|
+
if (isHovering && isParentOpen) {
|
|
2061
|
+
menuItemRef?.current && menuItemRef.current.focus();
|
|
2062
|
+
}
|
|
2063
|
+
else {
|
|
2064
|
+
toggleMenu(false);
|
|
2065
|
+
}
|
|
2066
|
+
}, [isHovering, isParentOpen, toggleMenu, menuItemRef]);
|
|
2067
|
+
useEffect(() => {
|
|
2068
|
+
setOpenSubmenuCount((count) => (isOpen ? count + 1 : Math.max(count - 1, 0)));
|
|
2069
|
+
}, [setOpenSubmenuCount, isOpen]);
|
|
2070
|
+
useImperativeHandle(instanceRef, () => ({
|
|
2071
|
+
openMenu: (...args) => {
|
|
2072
|
+
isParentOpen && openMenu(...args);
|
|
2073
|
+
},
|
|
2074
|
+
closeMenu: () => {
|
|
2075
|
+
if (isOpen) {
|
|
2076
|
+
menuItemRef?.current && menuItemRef.current.focus();
|
|
2077
|
+
toggleMenu(false);
|
|
2078
|
+
}
|
|
2079
|
+
},
|
|
2080
|
+
}));
|
|
2081
|
+
const modifiers = useMemo(() => ({
|
|
2082
|
+
open: isOpen,
|
|
2083
|
+
hover: isHovering,
|
|
2084
|
+
disabled: isDisabled,
|
|
2085
|
+
submenu: true,
|
|
2086
|
+
}), [isOpen, isHovering, isDisabled]);
|
|
2087
|
+
const { ref: externalItemRef, className: itemClassName, ...restItemProps } = itemProps;
|
|
2088
|
+
const mergedItemProps = mergeProps({
|
|
2089
|
+
onPointerMove: handlePointerMove,
|
|
2090
|
+
onPointerLeave: handlePointerLeave,
|
|
2091
|
+
onKeyDown: handleItemKeyDown,
|
|
2092
|
+
onClick: () => openTrigger !== "none" && openMenu(),
|
|
2093
|
+
}, restItemProps);
|
|
2094
|
+
const getMenuList = () => {
|
|
2095
|
+
const menuList = (jsx(MenuList, { ...restProps, ...stateProps, ariaLabel: ariaLabel || (typeof label === "string" ? label : "Submenu"), anchorRef: menuItemRef, containerRef: isPortal ? rootMenuRef : containerRef, direction: direction || (parentDir === "right" || parentDir === "left" ? parentDir : "right"), parentScrollingRef: isPortal && parentMenuRef, isDisabled: isDisabled }));
|
|
2096
|
+
const container = rootMenuRef?.current;
|
|
2097
|
+
return isPortal && container ? createPortal(menuList, container) : menuList;
|
|
2098
|
+
};
|
|
2099
|
+
return (jsxs("li", { className: useBEM({ block: menuClass, element: subMenuClass, className }), style: { position: "relative" }, role: "presentation", ref: containerRef, onKeyDown: handleKeyDown, children: [jsx("div", { role: "menuitem", "aria-haspopup": true, "aria-expanded": isOpen, ...mergedItemProps, ...commonProps(isDisabled, isHovering), ref: useCombinedRef(externalItemRef, menuItemRef), className: useBEM({
|
|
2100
|
+
block: menuClass,
|
|
2101
|
+
element: menuItemClass,
|
|
2102
|
+
modifiers,
|
|
2103
|
+
className: itemClassName,
|
|
2104
|
+
}), children: useMemo(() => safeCall(label, modifiers), [label, modifiers]) }), state && getMenuList()] }));
|
|
2105
|
+
};
|
|
2106
|
+
const SubMenu = withHovering("SubMenu", SubMenuFr);
|
|
2107
|
+
|
|
2108
|
+
const RadioGroupContext = createContext({});
|
|
2109
|
+
|
|
2110
|
+
const MenuItemFr = ({ className, value, href, type, checked, disabled, children, onClick, isHovering, menuItemRef, externalRef, ...restProps }) => {
|
|
2111
|
+
const isDisabled = !!disabled;
|
|
2112
|
+
const { setHover, ...restStateProps } = useItemState(menuItemRef, menuItemRef, isHovering, isDisabled);
|
|
2113
|
+
const eventHandlers = useContext(EventHandlersContext);
|
|
2114
|
+
const radioGroup = useContext(RadioGroupContext);
|
|
2115
|
+
const isRadio = type === "radio";
|
|
2116
|
+
const isCheckBox = type === "checkbox";
|
|
2117
|
+
const isAnchor = !!href && !isDisabled && !isRadio && !isCheckBox;
|
|
2118
|
+
const isChecked = isRadio ? radioGroup.value === value : isCheckBox ? !!checked : false;
|
|
2119
|
+
// handle click seems to be a combination of multiple event types, bad code.
|
|
2120
|
+
const handleClick = (e) => {
|
|
2121
|
+
if (isDisabled) {
|
|
2122
|
+
if (e.syntheticEvent) {
|
|
2123
|
+
e.syntheticEvent.stopPropagation();
|
|
2124
|
+
e.syntheticEvent.preventDefault();
|
|
2125
|
+
}
|
|
2126
|
+
else {
|
|
2127
|
+
e.stopPropagation();
|
|
2128
|
+
e.preventDefault();
|
|
2129
|
+
}
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
const event = {
|
|
2133
|
+
value,
|
|
2134
|
+
syntheticEvent: e,
|
|
2135
|
+
};
|
|
2136
|
+
if (e.key !== undefined) {
|
|
2137
|
+
const ke = e;
|
|
2138
|
+
event.key = ke.key;
|
|
2139
|
+
event.shiftKey = ke.shiftKey;
|
|
2140
|
+
}
|
|
2141
|
+
if (isCheckBox)
|
|
2142
|
+
event.checked = !isChecked;
|
|
2143
|
+
if (isRadio)
|
|
2144
|
+
event.name = radioGroup.name;
|
|
2145
|
+
safeCall(onClick, event);
|
|
2146
|
+
if (isRadio)
|
|
2147
|
+
safeCall(radioGroup.onRadioChange, event);
|
|
2148
|
+
eventHandlers.handleClick(event, isCheckBox || isRadio);
|
|
2149
|
+
};
|
|
2150
|
+
const handleKeyDown = (e) => {
|
|
2151
|
+
// if tab is allowed the handleKeyUp event can't process the tab
|
|
2152
|
+
if (e.key === "Tab") {
|
|
2153
|
+
e.preventDefault();
|
|
2154
|
+
e.stopPropagation();
|
|
2155
|
+
}
|
|
2566
2156
|
};
|
|
2567
|
-
|
|
2157
|
+
/**
|
|
2158
|
+
* Keyboard events are triggered on up, otherwise subcomponents get spaces and enters typed in them
|
|
2159
|
+
*/
|
|
2160
|
+
const handleKeyUp = (e) => {
|
|
2161
|
+
if (!isHovering)
|
|
2162
|
+
return;
|
|
2568
2163
|
switch (e.key) {
|
|
2569
|
-
case Keys.
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2164
|
+
case Keys.ENTER:
|
|
2165
|
+
case Keys.TAB:
|
|
2166
|
+
case Keys.SPACE:
|
|
2167
|
+
e.preventDefault();
|
|
2168
|
+
e.stopPropagation();
|
|
2169
|
+
if (isAnchor) {
|
|
2170
|
+
menuItemRef?.current && menuItemRef.current.click();
|
|
2171
|
+
}
|
|
2172
|
+
else {
|
|
2173
|
+
handleClick(e);
|
|
2174
|
+
}
|
|
2574
2175
|
break;
|
|
2575
|
-
default:
|
|
2576
|
-
return;
|
|
2577
2176
|
}
|
|
2578
|
-
e.preventDefault();
|
|
2579
2177
|
};
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2178
|
+
const modifiers = useMemo(() => ({
|
|
2179
|
+
type,
|
|
2180
|
+
disabled: isDisabled,
|
|
2181
|
+
hover: isHovering,
|
|
2182
|
+
checked: isChecked,
|
|
2183
|
+
anchor: isAnchor,
|
|
2184
|
+
}), [type, isDisabled, isHovering, isChecked, isAnchor]);
|
|
2185
|
+
const mergedProps = mergeProps({
|
|
2186
|
+
...restStateProps,
|
|
2187
|
+
onPointerDown: setHover,
|
|
2188
|
+
onKeyDown: handleKeyDown,
|
|
2189
|
+
onKeyUp: handleKeyUp,
|
|
2190
|
+
onClick: handleClick,
|
|
2191
|
+
}, restProps);
|
|
2192
|
+
// Order of props overriding (same in all components):
|
|
2193
|
+
// 1. Preset props adhering to WAI-ARIA Authoring Practices.
|
|
2194
|
+
// 2. Merged outer and local props
|
|
2195
|
+
// 3. ref, className
|
|
2196
|
+
const menuItemProps = {
|
|
2197
|
+
role: isRadio ? "menuitemradio" : isCheckBox ? "menuitemcheckbox" : "menuitem",
|
|
2198
|
+
"aria-checked": isRadio || isCheckBox ? isChecked : undefined,
|
|
2199
|
+
...mergedProps,
|
|
2200
|
+
...commonProps(isDisabled, isHovering),
|
|
2201
|
+
ref: useCombinedRef(externalRef, menuItemRef),
|
|
2202
|
+
className: useBEM({ block: menuClass, element: menuItemClass, modifiers, className }),
|
|
2203
|
+
children: useMemo(() => safeCall(children, modifiers), [children, modifiers]),
|
|
2588
2204
|
};
|
|
2589
|
-
if (
|
|
2590
|
-
|
|
2205
|
+
if (isAnchor) {
|
|
2206
|
+
return (jsx("li", { role: "presentation", children: jsx("a", { href: href, ...menuItemProps }) }));
|
|
2591
2207
|
}
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
}));
|
|
2598
|
-
return (jsxs(Fragment$1, { children: [renderButton, jsx(ControlledMenu, { ...restProps, ...stateProps, "aria-label": ariaLabel || (typeof button.props.children === "string" ? button.props.children : "Menu"), anchorRef: buttonRef, ref: externalRef, onClose: handleClose, skipOpen: skipOpen })] }));
|
|
2599
|
-
}
|
|
2600
|
-
const Menu = forwardRef(MenuFr);
|
|
2208
|
+
else {
|
|
2209
|
+
return jsx("li", { ...menuItemProps });
|
|
2210
|
+
}
|
|
2211
|
+
};
|
|
2212
|
+
const MenuItem = withHovering("MenuItem", MenuItemFr);
|
|
2601
2213
|
|
|
2602
|
-
const
|
|
2603
|
-
const settings = useContext(SettingsContext);
|
|
2604
|
-
const { rootMenuRef } = settings;
|
|
2605
|
-
const { submenuOpenDelay, submenuCloseDelay } = useContext(ItemSettingsContext);
|
|
2606
|
-
const { parentMenuRef, parentDir, overflow: parentOverflow } = useContext(MenuListContext);
|
|
2607
|
-
const { isParentOpen, isSubmenuOpen, setOpenSubmenuCount, dispatch, updateItems } = useContext(MenuListItemContext);
|
|
2608
|
-
const isPortal = parentOverflow !== "visible";
|
|
2609
|
-
const [stateProps, toggleMenu, _openMenu] = useMenuStateAndFocus(settings);
|
|
2610
|
-
const { state } = stateProps;
|
|
2214
|
+
const FocusableItemFr = ({ className, disabled, children, isHovering, menuItemRef, externalRef, ...restProps }) => {
|
|
2611
2215
|
const isDisabled = !!disabled;
|
|
2612
|
-
const
|
|
2613
|
-
const
|
|
2614
|
-
const
|
|
2615
|
-
const
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2216
|
+
const ref = useRef(null);
|
|
2217
|
+
const { setHover, onPointerLeave, ...restStateProps } = useItemState(menuItemRef, ref, isHovering, isDisabled);
|
|
2218
|
+
const { handleClose } = useContext(EventHandlersContext);
|
|
2219
|
+
const modifiers = useMemo(() => ({
|
|
2220
|
+
disabled: isDisabled,
|
|
2221
|
+
hover: isHovering,
|
|
2222
|
+
focusable: true,
|
|
2223
|
+
}), [isDisabled, isHovering]);
|
|
2224
|
+
const renderChildren = useMemo(() => safeCall(children, {
|
|
2225
|
+
...modifiers,
|
|
2226
|
+
ref,
|
|
2227
|
+
closeMenu: handleClose,
|
|
2228
|
+
}), [children, modifiers, handleClose]);
|
|
2229
|
+
const mergedProps = mergeProps({
|
|
2230
|
+
...restStateProps,
|
|
2231
|
+
onPointerLeave: (e) => onPointerLeave(e, true),
|
|
2232
|
+
onFocus: setHover,
|
|
2233
|
+
}, restProps);
|
|
2234
|
+
return (jsx("li", { role: "menuitem", ...mergedProps, ...commonProps(isDisabled), ref: useCombinedRef(externalRef, menuItemRef), className: useBEM({ block: menuClass, element: menuItemClass, modifiers, className }), children: renderChildren }));
|
|
2235
|
+
};
|
|
2236
|
+
const FocusableItem = withHovering("FocusableItem", FocusableItemFr);
|
|
2237
|
+
|
|
2238
|
+
const MenuDividerFr = ({ className, ...restProps }, externalRef) => {
|
|
2239
|
+
return (jsx("li", { role: "separator", ...restProps, ref: externalRef, className: useBEM({ block: menuClass, element: menuDividerClass, className }) }));
|
|
2240
|
+
};
|
|
2241
|
+
const MenuDivider = memo(forwardRef(MenuDividerFr));
|
|
2242
|
+
|
|
2243
|
+
const MenuHeaderFr = ({ className, ...restProps }, externalRef) => {
|
|
2244
|
+
return (jsx("li", { role: "presentation", ...restProps, ref: externalRef, className: useBEM({ block: menuClass, element: menuHeaderClass, className }) }));
|
|
2245
|
+
};
|
|
2246
|
+
const MenuHeader = memo(forwardRef(MenuHeaderFr));
|
|
2247
|
+
|
|
2248
|
+
const MenuGroupFr = ({ className, style, takeOverflow, ...restProps }, externalRef) => {
|
|
2249
|
+
const ref = useRef(null);
|
|
2250
|
+
const [overflowStyle, setOverflowStyle] = useState();
|
|
2251
|
+
const { overflow, overflowAmt } = useContext(MenuListContext);
|
|
2252
|
+
useIsomorphicLayoutEffect(() => {
|
|
2253
|
+
let maxHeight;
|
|
2254
|
+
if (takeOverflow && overflowAmt != null && overflowAmt >= 0 && ref.current) {
|
|
2255
|
+
maxHeight = ref.current.getBoundingClientRect().height - overflowAmt;
|
|
2256
|
+
if (maxHeight < 0)
|
|
2257
|
+
maxHeight = 0;
|
|
2619
2258
|
}
|
|
2259
|
+
setOverflowStyle(maxHeight != null && maxHeight >= 0 ? { maxHeight, overflow } : undefined);
|
|
2260
|
+
}, [takeOverflow, overflow, overflowAmt]);
|
|
2261
|
+
useIsomorphicLayoutEffect(() => {
|
|
2262
|
+
if (overflowStyle && ref.current)
|
|
2263
|
+
ref.current.scrollTop = 0;
|
|
2264
|
+
}, [overflowStyle]);
|
|
2265
|
+
return (jsx("div", { ...restProps, ref: useCombinedRef(externalRef, ref), className: useBEM({ block: menuClass, element: menuGroupClass, className }), style: { ...style, ...overflowStyle } }));
|
|
2266
|
+
};
|
|
2267
|
+
const MenuGroup = forwardRef(MenuGroupFr);
|
|
2268
|
+
|
|
2269
|
+
const MenuRadioGroupFr = ({ "aria-label": ariaLabel, className, name, value, onRadioChange, ...restProps }, externalRef) => {
|
|
2270
|
+
const contextValue = useMemo(() => ({ name, value, onRadioChange }), [name, value, onRadioChange]);
|
|
2271
|
+
return (jsx(RadioGroupContext.Provider, { value: contextValue, children: jsx("li", { role: "presentation", children: jsx("ul", { role: "group", "aria-label": ariaLabel || name || "Radio group", ...restProps, ref: externalRef, className: useBEM({ block: menuClass, element: radioGroupClass, className }) }) }) }));
|
|
2272
|
+
};
|
|
2273
|
+
const MenuRadioGroup = forwardRef(MenuRadioGroupFr);
|
|
2274
|
+
|
|
2275
|
+
const useGridContextMenu = ({ contextMenuSelectRow, contextMenu: ContextMenu, }) => {
|
|
2276
|
+
const { redrawRows, prePopupOps, postPopupOps, getSelectedRows } = useContext(GridContext);
|
|
2277
|
+
const [isOpen, setOpen] = useState(false);
|
|
2278
|
+
const [anchorPoint, setAnchorPoint] = useState({ x: 0, y: 0 });
|
|
2279
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2280
|
+
const clickedColDefRef = useRef(null);
|
|
2281
|
+
const selectedRowsRef = useRef([]);
|
|
2282
|
+
const clickedRowRef = useRef(null);
|
|
2283
|
+
const openMenu = useCallback((e) => {
|
|
2284
|
+
if (!e)
|
|
2285
|
+
return;
|
|
2286
|
+
prePopupOps();
|
|
2287
|
+
setAnchorPoint({ x: e.clientX, y: e.clientY });
|
|
2288
|
+
setOpen(true);
|
|
2289
|
+
}, [prePopupOps]);
|
|
2290
|
+
const closeMenu = useCallback(() => {
|
|
2291
|
+
setOpen(false);
|
|
2292
|
+
redrawRows();
|
|
2293
|
+
postPopupOps();
|
|
2294
|
+
}, [postPopupOps, redrawRows]);
|
|
2295
|
+
const cellContextMenu = useCallback((event) => {
|
|
2296
|
+
if (contextMenuSelectRow && !event.node.isSelected()) {
|
|
2297
|
+
event.node.setSelected(true, true);
|
|
2298
|
+
}
|
|
2299
|
+
clickedColDefRef.current = event.colDef;
|
|
2300
|
+
selectedRowsRef.current = getSelectedRows();
|
|
2301
|
+
clickedRowRef.current = event.data;
|
|
2302
|
+
// This is actually a pointer event
|
|
2303
|
+
openMenu(event.event);
|
|
2304
|
+
}, [contextMenuSelectRow, getSelectedRows, openMenu]);
|
|
2305
|
+
return {
|
|
2306
|
+
openMenu,
|
|
2307
|
+
cellContextMenu,
|
|
2308
|
+
component: ContextMenu ? (jsx(Fragment$1, { children: jsx(ControlledMenu, { anchorPoint: anchorPoint, state: isOpen ? "open" : "closed", direction: "right", onClose: closeMenu, children: isOpen && (jsx(ContextMenu, { selectedRows: selectedRowsRef.current, clickedRow: clickedRowRef.current, colDef: clickedColDefRef.current, close: closeMenu })) }) })) : null,
|
|
2620
2309
|
};
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
const
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
const
|
|
2633
|
-
|
|
2310
|
+
};
|
|
2311
|
+
|
|
2312
|
+
/**
|
|
2313
|
+
* Wrapper for AgGrid to add commonly used functionality.
|
|
2314
|
+
*/
|
|
2315
|
+
const Grid = ({ "data-testid": dataTestId, rowSelection = "multiple", suppressColumnVirtualization = true, theme = "ag-theme-alpine", sizeColumns = "auto", selectColumnPinned = null, contextMenuSelectRow = false, ...params }) => {
|
|
2316
|
+
const { gridReady, setApis, ensureRowVisible, getFirstRowId, selectRowsById, focusByRowById, ensureSelectedRowIsVisible, autoSizeColumns, sizeColumnsToFit, externallySelectedItemsAreInSync, setExternallySelectedItemsAreInSync, isExternalFilterPresent, doesExternalFilterPass, setOnCellEditingComplete, getColDef, } = useContext(GridContext);
|
|
2317
|
+
const { checkUpdating, updatedDep, updatingCols } = useContext(GridUpdatingContext);
|
|
2318
|
+
const { prePopupOps } = useContext(GridContext);
|
|
2319
|
+
const gridDivRef = useRef(null);
|
|
2320
|
+
const lastSelectedIds = useRef([]);
|
|
2321
|
+
const [staleGrid, setStaleGrid] = useState(false);
|
|
2322
|
+
const postSortRows = usePostSortRowsHook({ setStaleGrid });
|
|
2323
|
+
/**
|
|
2324
|
+
* onContentSize should only be called at maximum twice.
|
|
2325
|
+
* Once when an empty grid is loaded.
|
|
2326
|
+
* And again when the grid has content.
|
|
2327
|
+
*/
|
|
2328
|
+
const hasSetContentSize = useRef(false);
|
|
2329
|
+
const hasSetContentSizeEmpty = useRef(false);
|
|
2330
|
+
const needsAutoSize = useRef(false);
|
|
2331
|
+
const setInitialContentSize = useCallback(() => {
|
|
2332
|
+
if (!gridDivRef.current?.clientWidth) {
|
|
2333
|
+
// Don't resize grids if they are offscreen as it doesn't work.
|
|
2334
|
+
needsAutoSize.current = true;
|
|
2634
2335
|
return;
|
|
2635
|
-
if (isSubmenuOpen) {
|
|
2636
|
-
timeoutId.current = setTimeout(() => delayOpen(submenuOpenDelay - submenuCloseDelay), submenuCloseDelay);
|
|
2637
2336
|
}
|
|
2638
|
-
|
|
2639
|
-
|
|
2337
|
+
const headerCellCount = gridDivRef.current?.getElementsByClassName("ag-header-cell-label")?.length;
|
|
2338
|
+
if (headerCellCount < 2) {
|
|
2339
|
+
// Don't resize grids until all the columns are visible
|
|
2340
|
+
// as `autoSizeColumns` will fail silently in this case
|
|
2341
|
+
needsAutoSize.current = true;
|
|
2342
|
+
return;
|
|
2640
2343
|
}
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
toggleMenu(false);
|
|
2655
|
-
handled = true;
|
|
2344
|
+
const skipHeader = sizeColumns === "auto-skip-headers" && !isEmpty(params.rowData);
|
|
2345
|
+
if (sizeColumns === "auto" || skipHeader) {
|
|
2346
|
+
const result = autoSizeColumns({ skipHeader, userSizedColIds: userSizedColIds.current });
|
|
2347
|
+
if (isEmpty(params.rowData)) {
|
|
2348
|
+
if (!hasSetContentSizeEmpty.current && result && !hasSetContentSize.current) {
|
|
2349
|
+
hasSetContentSizeEmpty.current = true;
|
|
2350
|
+
params.onContentSize && params.onContentSize(result);
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
else {
|
|
2354
|
+
if (result && !hasSetContentSize.current) {
|
|
2355
|
+
hasSetContentSize.current = true;
|
|
2356
|
+
params.onContentSize && params.onContentSize(result);
|
|
2656
2357
|
}
|
|
2657
|
-
|
|
2658
|
-
// prevent browser from scrolling page to the right
|
|
2659
|
-
case Keys.RIGHT:
|
|
2660
|
-
if (!isOpen)
|
|
2661
|
-
handled = true;
|
|
2662
|
-
break;
|
|
2358
|
+
}
|
|
2663
2359
|
}
|
|
2664
|
-
if (
|
|
2665
|
-
|
|
2666
|
-
e.stopPropagation();
|
|
2360
|
+
if (sizeColumns !== "none") {
|
|
2361
|
+
sizeColumnsToFit();
|
|
2667
2362
|
}
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2363
|
+
needsAutoSize.current = false;
|
|
2364
|
+
}, [autoSizeColumns, params, sizeColumns, sizeColumnsToFit]);
|
|
2365
|
+
const lastOwnerDocumentRef = useRef();
|
|
2366
|
+
/**
|
|
2367
|
+
* Auto-size windows that had deferred auto-size
|
|
2368
|
+
*/
|
|
2369
|
+
useIntervalHook({
|
|
2370
|
+
callback: () => {
|
|
2371
|
+
// Check if window has been popped out and needs resize
|
|
2372
|
+
const currentDocument = gridDivRef.current?.ownerDocument;
|
|
2373
|
+
if (currentDocument !== lastOwnerDocumentRef.current) {
|
|
2374
|
+
lastOwnerDocumentRef.current = currentDocument;
|
|
2375
|
+
if (currentDocument) {
|
|
2376
|
+
needsAutoSize.current = true;
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
if (needsAutoSize.current) {
|
|
2380
|
+
needsAutoSize.current = false;
|
|
2381
|
+
setInitialContentSize();
|
|
2382
|
+
}
|
|
2383
|
+
},
|
|
2384
|
+
timeoutMs: 1000,
|
|
2385
|
+
});
|
|
2386
|
+
const previousGridReady = useRef(gridReady);
|
|
2387
|
+
useEffect(() => {
|
|
2388
|
+
if (!previousGridReady.current && gridReady) {
|
|
2389
|
+
previousGridReady.current = true;
|
|
2390
|
+
setInitialContentSize();
|
|
2678
2391
|
}
|
|
2679
|
-
};
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2392
|
+
}, [gridReady, setInitialContentSize]);
|
|
2393
|
+
/**
|
|
2394
|
+
* On data load select the first row of the grid if required.
|
|
2395
|
+
*/
|
|
2396
|
+
const hasSelectedFirstItem = useRef(false);
|
|
2683
2397
|
useEffect(() => {
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2398
|
+
if (!gridReady || hasSelectedFirstItem.current || !params.rowData || !externallySelectedItemsAreInSync)
|
|
2399
|
+
return;
|
|
2400
|
+
hasSelectedFirstItem.current = true;
|
|
2401
|
+
if (isNotEmpty(params.rowData) && isEmpty(params.externalSelectedItems)) {
|
|
2402
|
+
const firstRowId = getFirstRowId();
|
|
2403
|
+
if (params.autoSelectFirstRow) {
|
|
2404
|
+
selectRowsById([firstRowId]);
|
|
2405
|
+
}
|
|
2406
|
+
else {
|
|
2407
|
+
focusByRowById(firstRowId);
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
}, [
|
|
2411
|
+
externallySelectedItemsAreInSync,
|
|
2412
|
+
focusByRowById,
|
|
2413
|
+
gridReady,
|
|
2414
|
+
params.externalSelectedItems,
|
|
2415
|
+
params.autoSelectFirstRow,
|
|
2416
|
+
params.rowData,
|
|
2417
|
+
selectRowsById,
|
|
2418
|
+
getFirstRowId,
|
|
2419
|
+
]);
|
|
2420
|
+
/**
|
|
2421
|
+
* AgGrid checkbox select does not pass clicks within cell but not on the checkbox to checkbox.
|
|
2422
|
+
* This passes the event to the checkbox when you click anywhere in the cell.
|
|
2423
|
+
*/
|
|
2424
|
+
const clickSelectorCheckboxWhenContainingCellClicked = useCallback(({ event }) => {
|
|
2425
|
+
if (!event)
|
|
2426
|
+
return;
|
|
2427
|
+
const input = event.target.querySelector("input");
|
|
2428
|
+
input?.dispatchEvent(event);
|
|
2429
|
+
}, []);
|
|
2430
|
+
/**
|
|
2431
|
+
* Ensure external selected items list is in sync with panel.
|
|
2432
|
+
*/
|
|
2433
|
+
const synchroniseExternalStateToGridSelection = useCallback(({ api }) => {
|
|
2434
|
+
if (!params.externalSelectedItems || !params.setExternalSelectedItems) {
|
|
2435
|
+
setExternallySelectedItemsAreInSync(true);
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2438
|
+
const selectedRows = api.getSelectedRows();
|
|
2439
|
+
// We don't want to update selected Items if it hasn't changed to prevent excess renders
|
|
2440
|
+
if (params.externalSelectedItems.length != selectedRows.length ||
|
|
2441
|
+
isNotEmpty(xorBy(selectedRows, params.externalSelectedItems, (row) => row.id))) {
|
|
2442
|
+
setExternallySelectedItemsAreInSync(false);
|
|
2443
|
+
params.setExternalSelectedItems([...selectedRows]);
|
|
2688
2444
|
}
|
|
2689
2445
|
else {
|
|
2690
|
-
|
|
2446
|
+
setExternallySelectedItemsAreInSync(true);
|
|
2691
2447
|
}
|
|
2692
|
-
}, [
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
}
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
return isPortal && container ? createPortal(menuList, container) : menuList;
|
|
2448
|
+
}, [params, setExternallySelectedItemsAreInSync]);
|
|
2449
|
+
/**
|
|
2450
|
+
* Synchronise externally selected items to grid.
|
|
2451
|
+
* If new ids are selected scroll them into view.
|
|
2452
|
+
*/
|
|
2453
|
+
const synchroniseExternallySelectedItemsToGrid = useCallback(() => {
|
|
2454
|
+
if (!gridReady)
|
|
2455
|
+
return;
|
|
2456
|
+
if (!params.externalSelectedItems) {
|
|
2457
|
+
setExternallySelectedItemsAreInSync(true);
|
|
2458
|
+
return;
|
|
2459
|
+
}
|
|
2460
|
+
const selectedIds = params.externalSelectedItems.map((row) => row.id);
|
|
2461
|
+
const lastNewId = last(difference(selectedIds, lastSelectedIds.current));
|
|
2462
|
+
if (lastNewId != null) {
|
|
2463
|
+
ensureRowVisible(lastNewId);
|
|
2464
|
+
}
|
|
2465
|
+
lastSelectedIds.current = selectedIds;
|
|
2466
|
+
selectRowsById(selectedIds);
|
|
2467
|
+
setExternallySelectedItemsAreInSync(true);
|
|
2468
|
+
}, [gridReady, params.externalSelectedItems, ensureRowVisible, selectRowsById, setExternallySelectedItemsAreInSync]);
|
|
2469
|
+
/**
|
|
2470
|
+
* Combine grid and cell editable into one function
|
|
2471
|
+
*/
|
|
2472
|
+
const combineEditables = (...editables) => (params) => {
|
|
2473
|
+
const results = editables.map((editable) => fnOrVar(editable, params));
|
|
2474
|
+
// If editable is not set anywhere then it's non-editable
|
|
2475
|
+
if (results.every((v) => v == null))
|
|
2476
|
+
return false;
|
|
2477
|
+
// If any editable value is or returns false then it's non-editable
|
|
2478
|
+
return !results.some((v) => v == false);
|
|
2724
2479
|
};
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
const
|
|
2733
|
-
|
|
2734
|
-
const
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2480
|
+
/**
|
|
2481
|
+
* Synchronise externally selected items to grid on externalSelectedItems change
|
|
2482
|
+
*/
|
|
2483
|
+
useEffect(synchroniseExternallySelectedItemsToGrid, [synchroniseExternallySelectedItemsToGrid]);
|
|
2484
|
+
/**
|
|
2485
|
+
* Add selectable column to colDefs. Adjust column defs to block fit for auto sized columns.
|
|
2486
|
+
*/
|
|
2487
|
+
const columnDefs = useMemo(() => {
|
|
2488
|
+
const adjustColDefs = params.columnDefs.map((colDef) => {
|
|
2489
|
+
const colDefEditable = colDef.editable;
|
|
2490
|
+
const editable = combineEditables(params.readOnly !== true, params.defaultColDef?.editable, colDefEditable);
|
|
2491
|
+
return {
|
|
2492
|
+
...colDef,
|
|
2493
|
+
editable,
|
|
2494
|
+
cellClassRules: {
|
|
2495
|
+
...colDef.cellClassRules,
|
|
2496
|
+
"GridCell-readonly": (ccp) => !editable(ccp),
|
|
2497
|
+
},
|
|
2498
|
+
};
|
|
2499
|
+
});
|
|
2500
|
+
return params.selectable
|
|
2501
|
+
? [
|
|
2502
|
+
{
|
|
2503
|
+
colId: "selection",
|
|
2504
|
+
editable: false,
|
|
2505
|
+
minWidth: 42,
|
|
2506
|
+
maxWidth: 42,
|
|
2507
|
+
pinned: selectColumnPinned,
|
|
2508
|
+
headerComponentParams: {
|
|
2509
|
+
exportable: false,
|
|
2510
|
+
},
|
|
2511
|
+
checkboxSelection: true,
|
|
2512
|
+
headerComponent: rowSelection === "multiple" ? GridHeaderSelect : null,
|
|
2513
|
+
suppressHeaderKeyboardEvent: (e) => {
|
|
2514
|
+
if ((e.event.key === "Enter" || e.event.key === " ") && !e.event.repeat) {
|
|
2515
|
+
if (isEmpty(e.api.getSelectedRows())) {
|
|
2516
|
+
e.api.selectAllFiltered();
|
|
2517
|
+
}
|
|
2518
|
+
else {
|
|
2519
|
+
e.api.deselectAll();
|
|
2520
|
+
}
|
|
2521
|
+
return true;
|
|
2522
|
+
}
|
|
2523
|
+
return false;
|
|
2524
|
+
},
|
|
2525
|
+
onCellClicked: clickSelectorCheckboxWhenContainingCellClicked,
|
|
2526
|
+
},
|
|
2527
|
+
...adjustColDefs,
|
|
2528
|
+
]
|
|
2529
|
+
: adjustColDefs;
|
|
2530
|
+
}, [
|
|
2531
|
+
params.columnDefs,
|
|
2532
|
+
params.selectable,
|
|
2533
|
+
params.readOnly,
|
|
2534
|
+
params.defaultColDef?.editable,
|
|
2535
|
+
selectColumnPinned,
|
|
2536
|
+
rowSelection,
|
|
2537
|
+
clickSelectorCheckboxWhenContainingCellClicked,
|
|
2538
|
+
]);
|
|
2539
|
+
/**
|
|
2540
|
+
* When grid is ready set the apis to the grid context and sync selected items to grid.
|
|
2541
|
+
*/
|
|
2542
|
+
const onGridReady = useCallback((event) => {
|
|
2543
|
+
setApis(event.api, event.columnApi, dataTestId);
|
|
2544
|
+
synchroniseExternallySelectedItemsToGrid();
|
|
2545
|
+
}, [dataTestId, setApis, synchroniseExternallySelectedItemsToGrid]);
|
|
2546
|
+
/**
|
|
2547
|
+
* When the grid is being initialized the data may be empty.
|
|
2548
|
+
* This will resize columns when we have at least one row.
|
|
2549
|
+
*/
|
|
2550
|
+
const previousRowDataLength = useRef(0);
|
|
2551
|
+
const onRowDataChanged = useCallback(() => {
|
|
2552
|
+
const length = params.rowData?.length ?? 0;
|
|
2553
|
+
if (previousRowDataLength.current !== length) {
|
|
2554
|
+
setInitialContentSize();
|
|
2555
|
+
previousRowDataLength.current = length;
|
|
2556
|
+
}
|
|
2557
|
+
if (lastUpdatedDep.current === updatedDep || isEmpty(colIdsEdited.current))
|
|
2558
|
+
return;
|
|
2559
|
+
lastUpdatedDep.current = updatedDep;
|
|
2560
|
+
// Don't update while there are spinners
|
|
2561
|
+
if (!isEmpty(updatingCols()))
|
|
2562
|
+
return;
|
|
2563
|
+
const skipHeader = sizeColumns === "auto-skip-headers";
|
|
2564
|
+
autoSizeColumns({ skipHeader, userSizedColIds: userSizedColIds.current, colIds: colIdsEdited.current });
|
|
2565
|
+
colIdsEdited.current.clear();
|
|
2566
|
+
}, [autoSizeColumns, params.rowData?.length, setInitialContentSize, sizeColumns, updatedDep, updatingCols]);
|
|
2567
|
+
/**
|
|
2568
|
+
* Show/hide no rows overlay when model changes.
|
|
2569
|
+
*/
|
|
2570
|
+
const isShowingNoRowsOverlay = useRef(false);
|
|
2571
|
+
const onModelUpdated = useCallback((event) => {
|
|
2572
|
+
if (event.api.getDisplayedRowCount() === 0) {
|
|
2573
|
+
if (!isShowingNoRowsOverlay.current) {
|
|
2574
|
+
event.api.showNoRowsOverlay();
|
|
2575
|
+
isShowingNoRowsOverlay.current = true;
|
|
2751
2576
|
}
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2577
|
+
}
|
|
2578
|
+
else {
|
|
2579
|
+
if (isShowingNoRowsOverlay.current) {
|
|
2580
|
+
event.api.hideOverlay();
|
|
2581
|
+
isShowingNoRowsOverlay.current = false;
|
|
2755
2582
|
}
|
|
2583
|
+
}
|
|
2584
|
+
}, []);
|
|
2585
|
+
/**
|
|
2586
|
+
* Force-refresh all selected rows to re-run class function, to update selection highlighting
|
|
2587
|
+
*/
|
|
2588
|
+
const refreshSelectedRows = useCallback((event) => {
|
|
2589
|
+
event.api.refreshCells({
|
|
2590
|
+
force: true,
|
|
2591
|
+
rowNodes: event.api.getSelectedNodes(),
|
|
2592
|
+
});
|
|
2593
|
+
}, []);
|
|
2594
|
+
/**
|
|
2595
|
+
* Make sure node is selected for editing and start edit
|
|
2596
|
+
*/
|
|
2597
|
+
const startCellEditing = useCallback((event) => {
|
|
2598
|
+
prePopupOps();
|
|
2599
|
+
if (!event.node.isSelected()) {
|
|
2600
|
+
event.node.setSelected(true, true);
|
|
2601
|
+
}
|
|
2602
|
+
// Cell already being edited, so don't re-edit until finished
|
|
2603
|
+
if (checkUpdating([event.colDef.field ?? ""], event.data.id)) {
|
|
2756
2604
|
return;
|
|
2757
2605
|
}
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
const ke = e;
|
|
2764
|
-
event.key = ke.key;
|
|
2765
|
-
event.shiftKey = ke.shiftKey;
|
|
2606
|
+
if (event.rowIndex !== null) {
|
|
2607
|
+
event.api.startEditingCell({
|
|
2608
|
+
rowIndex: event.rowIndex,
|
|
2609
|
+
colKey: event.column.getColId(),
|
|
2610
|
+
});
|
|
2766
2611
|
}
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
if (
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2612
|
+
}, [checkUpdating, prePopupOps]);
|
|
2613
|
+
/**
|
|
2614
|
+
* Handle double click edit
|
|
2615
|
+
*/
|
|
2616
|
+
const onCellDoubleClick = useCallback((event) => {
|
|
2617
|
+
if (!invokeEditAction(event))
|
|
2618
|
+
startCellEditing(event);
|
|
2619
|
+
}, [startCellEditing]);
|
|
2620
|
+
/**
|
|
2621
|
+
* Handle single click edits
|
|
2622
|
+
*/
|
|
2623
|
+
const onCellClicked = useCallback((event) => {
|
|
2624
|
+
if (event.colDef?.cellRendererParams?.singleClickEdit) {
|
|
2625
|
+
startCellEditing(event);
|
|
2626
|
+
}
|
|
2627
|
+
}, [startCellEditing]);
|
|
2628
|
+
/**
|
|
2629
|
+
* If cell has an edit action invoke it (if editable)
|
|
2630
|
+
*/
|
|
2631
|
+
const invokeEditAction = (e) => {
|
|
2632
|
+
const editAction = e.colDef?.cellRendererParams?.editAction;
|
|
2633
|
+
if (!editAction)
|
|
2634
|
+
return false;
|
|
2635
|
+
const editable = fnOrVar(e.colDef?.editable, e);
|
|
2636
|
+
if (editable) {
|
|
2637
|
+
if (!e.node.isSelected()) {
|
|
2638
|
+
e.node.setSelected(true, true);
|
|
2639
|
+
}
|
|
2640
|
+
editAction([e.data, ...e.api.getSelectedRows().filter((row) => row.id !== e.data.id)]);
|
|
2781
2641
|
}
|
|
2642
|
+
return true;
|
|
2782
2643
|
};
|
|
2783
2644
|
/**
|
|
2784
|
-
*
|
|
2645
|
+
* Start editing on pressing Enter
|
|
2785
2646
|
*/
|
|
2786
|
-
const
|
|
2787
|
-
if (
|
|
2647
|
+
const onCellKeyPress = useCallback((e) => {
|
|
2648
|
+
if (e.event.key === "Enter") {
|
|
2649
|
+
if (!invokeEditAction(e))
|
|
2650
|
+
startCellEditing(e);
|
|
2651
|
+
}
|
|
2652
|
+
}, [startCellEditing]);
|
|
2653
|
+
/**
|
|
2654
|
+
* Once the grid has auto-sized we want to run fit to fit the grid in its container,
|
|
2655
|
+
* but we don't want the non-flex auto-sized columns to "fit" size, so suppressSizeToFit is set to true.
|
|
2656
|
+
*/
|
|
2657
|
+
const columnDefsAdjusted = useMemo(() => {
|
|
2658
|
+
const adjustColDefOrGroup = (colDef) => "children" in colDef ? adjustGroupColDef(colDef) : adjustColDef(colDef);
|
|
2659
|
+
const adjustGroupColDef = (colDef) => ({
|
|
2660
|
+
...colDef,
|
|
2661
|
+
children: colDef.children.map((colDef) => adjustColDefOrGroup(colDef)),
|
|
2662
|
+
});
|
|
2663
|
+
const adjustColDef = (colDef) => ({
|
|
2664
|
+
...colDef,
|
|
2665
|
+
suppressSizeToFit: (sizeColumns === "auto" || sizeColumns === "auto-skip-headers") && !colDef.flex,
|
|
2666
|
+
sortable: colDef.sortable && params.defaultColDef?.sortable !== false,
|
|
2667
|
+
});
|
|
2668
|
+
return columnDefs.map((colDef) => adjustColDefOrGroup(colDef));
|
|
2669
|
+
}, [columnDefs, params.defaultColDef?.sortable, sizeColumns]);
|
|
2670
|
+
/**
|
|
2671
|
+
* Set of colIds that need auto-sizing.
|
|
2672
|
+
*/
|
|
2673
|
+
const colIdsEdited = useRef(new Set());
|
|
2674
|
+
const lastUpdatedDep = useRef(updatedDep);
|
|
2675
|
+
/**
|
|
2676
|
+
* When cell editing has completed the colId as needing auto-sizing
|
|
2677
|
+
*/
|
|
2678
|
+
useEffect(() => {
|
|
2679
|
+
if (lastUpdatedDep.current === updatedDep)
|
|
2788
2680
|
return;
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2681
|
+
lastUpdatedDep.current = updatedDep;
|
|
2682
|
+
const colIds = updatingCols();
|
|
2683
|
+
// Updating possibly completed
|
|
2684
|
+
if (isEmpty(colIds)) {
|
|
2685
|
+
// Columns to resize?
|
|
2686
|
+
if (!isEmpty(colIdsEdited.current)) {
|
|
2687
|
+
const skipHeader = sizeColumns === "auto-skip-headers";
|
|
2688
|
+
if (sizeColumns === "auto" || skipHeader) {
|
|
2689
|
+
defer$1(() => autoSizeColumns({
|
|
2690
|
+
skipHeader,
|
|
2691
|
+
userSizedColIds: userSizedColIds.current,
|
|
2692
|
+
colIds: colIdsEdited.current,
|
|
2693
|
+
}));
|
|
2797
2694
|
}
|
|
2798
|
-
|
|
2799
|
-
|
|
2695
|
+
colIdsEdited.current.clear();
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
else {
|
|
2699
|
+
// Updates not complete, add them to a list of columns needing resize
|
|
2700
|
+
colIds.forEach((colId) => {
|
|
2701
|
+
if (colId && !getColDef(colId)?.flex) {
|
|
2702
|
+
colIdsEdited.current.add(colId);
|
|
2800
2703
|
}
|
|
2704
|
+
});
|
|
2705
|
+
}
|
|
2706
|
+
}, [autoSizeColumns, getColDef, sizeColumns, updatedDep, updatingCols]);
|
|
2707
|
+
/**
|
|
2708
|
+
* Resize columns to fit if required on window/container resize
|
|
2709
|
+
*/
|
|
2710
|
+
const onGridSizeChanged = useCallback(() => {
|
|
2711
|
+
if (sizeColumns !== "none") {
|
|
2712
|
+
sizeColumnsToFit();
|
|
2713
|
+
}
|
|
2714
|
+
}, [sizeColumns, sizeColumnsToFit]);
|
|
2715
|
+
/**
|
|
2716
|
+
* Set of column Id's that are prevented from auto-sizing as they are user set
|
|
2717
|
+
*/
|
|
2718
|
+
const userSizedColIds = useRef(new Set());
|
|
2719
|
+
/**
|
|
2720
|
+
* Lock/unlock column width on user edit/reset.
|
|
2721
|
+
*/
|
|
2722
|
+
const onColumnResized = useCallback((e) => {
|
|
2723
|
+
const colId = e.column?.getColId();
|
|
2724
|
+
if (colId == null)
|
|
2725
|
+
return;
|
|
2726
|
+
switch (e.source) {
|
|
2727
|
+
case "uiColumnDragged":
|
|
2728
|
+
userSizedColIds.current.add(colId);
|
|
2729
|
+
break;
|
|
2730
|
+
case "autosizeColumns":
|
|
2731
|
+
userSizedColIds.current.delete(colId);
|
|
2801
2732
|
break;
|
|
2802
2733
|
}
|
|
2803
|
-
};
|
|
2804
|
-
const
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
}
|
|
2834
|
-
|
|
2835
|
-
|
|
2734
|
+
}, []);
|
|
2735
|
+
const gridContextMenu = useGridContextMenu({ contextMenu: params.contextMenu, contextMenuSelectRow });
|
|
2736
|
+
// This is setting a ref in the GridContext so won't be triggering an update loop
|
|
2737
|
+
setOnCellEditingComplete(params.onCellEditingComplete);
|
|
2738
|
+
return (jsxs("div", { "data-testid": dataTestId, className: clsx("Grid-container", theme, staleGrid && "Grid-sortIsStale", gridReady && params.rowData && "Grid-ready"), children: [gridContextMenu.component, jsx("div", { style: { flex: 1 }, ref: gridDivRef, children: jsx(AgGridReact, { rowHeight: params.rowHeight, animateRows: params.animateRows, rowClassRules: params.rowClassRules, getRowId: (params) => `${params.data.id}`, suppressRowClickSelection: true, rowSelection: rowSelection, suppressBrowserResizeObserver: true, onGridSizeChanged: onGridSizeChanged, suppressColumnVirtualisation: suppressColumnVirtualization, suppressClickEdit: true, onColumnVisible: () => {
|
|
2739
|
+
setInitialContentSize();
|
|
2740
|
+
}, onRowDataChanged: onRowDataChanged, onCellKeyPress: onCellKeyPress, onCellClicked: onCellClicked, onCellDoubleClicked: onCellDoubleClick, onCellEditingStarted: refreshSelectedRows, domLayout: params.domLayout, onColumnResized: onColumnResized, defaultColDef: { minWidth: 48, ...omit(params.defaultColDef, ["editable"]) }, columnDefs: columnDefsAdjusted, rowData: params.rowData, noRowsOverlayComponent: GridNoRowsOverlay, noRowsOverlayComponentParams: {
|
|
2741
|
+
rowData: params.rowData,
|
|
2742
|
+
noRowsOverlayText: params.noRowsOverlayText,
|
|
2743
|
+
}, onModelUpdated: onModelUpdated, onGridReady: onGridReady, onSortChanged: ensureSelectedRowIsVisible, postSortRows: params.postSortRows ?? postSortRows, onSelectionChanged: synchroniseExternalStateToGridSelection, onColumnMoved: params.onColumnMoved, alwaysShowVerticalScroll: params.alwaysShowVerticalScroll, isExternalFilterPresent: isExternalFilterPresent, doesExternalFilterPass: doesExternalFilterPass, maintainColumnOrder: true, preventDefaultOnContextMenu: true, onCellContextMenu: gridContextMenu.cellContextMenu }) })] }));
|
|
2744
|
+
};
|
|
2745
|
+
|
|
2746
|
+
const GridPopoverContext = createContext({
|
|
2747
|
+
anchorRef: { current: null },
|
|
2748
|
+
saving: false,
|
|
2749
|
+
setSaving: () => { },
|
|
2750
|
+
field: "",
|
|
2751
|
+
value: null,
|
|
2752
|
+
data: {},
|
|
2753
|
+
selectedRows: [],
|
|
2754
|
+
updateValue: async () => false,
|
|
2755
|
+
formatValue: () => "! No gridPopoverContextProvider !",
|
|
2756
|
+
});
|
|
2757
|
+
const useGridPopoverContext = () => useContext(GridPopoverContext);
|
|
2758
|
+
|
|
2759
|
+
const GridPopoverContextProvider = ({ props, children }) => {
|
|
2760
|
+
const { getFilteredSelectedRows, updatingCells } = useContext(GridContext);
|
|
2761
|
+
const anchorRef = useRef(props.eGridCell);
|
|
2762
|
+
const hasSaved = useRef(false);
|
|
2763
|
+
const [saving, setSaving] = useState(false);
|
|
2764
|
+
const { colDef } = props;
|
|
2765
|
+
const { cellEditorParams } = colDef;
|
|
2766
|
+
const multiEdit = cellEditorParams?.multiEdit ?? false;
|
|
2767
|
+
// Then item that is clicked on will always be first in the list
|
|
2768
|
+
const selectedRows = useMemo(() => multiEdit ? sortBy(getFilteredSelectedRows(), (row) => row.id !== props.data.id) : [props.data], [getFilteredSelectedRows, multiEdit, props.data]);
|
|
2769
|
+
const field = props.colDef?.field ?? "";
|
|
2770
|
+
const updateValue = useCallback(async (saveFn, tabDirection) => {
|
|
2771
|
+
if (hasSaved.current)
|
|
2772
|
+
return true;
|
|
2773
|
+
hasSaved.current = true;
|
|
2774
|
+
return saving ? false : await updatingCells({ selectedRows, field }, saveFn, setSaving, tabDirection);
|
|
2775
|
+
}, [field, saving, selectedRows, updatingCells]);
|
|
2776
|
+
return (jsx(GridPopoverContext.Provider, { value: {
|
|
2777
|
+
anchorRef: anchorRef,
|
|
2778
|
+
saving,
|
|
2779
|
+
setSaving,
|
|
2780
|
+
selectedRows,
|
|
2781
|
+
field,
|
|
2782
|
+
data: props.data,
|
|
2783
|
+
value: props.value,
|
|
2784
|
+
updateValue,
|
|
2785
|
+
formatValue: props.formatValue,
|
|
2786
|
+
}, children: children }));
|
|
2787
|
+
};
|
|
2788
|
+
|
|
2789
|
+
const GridCellMultiSelectClassRules = {
|
|
2790
|
+
"ag-selected-for-edit": (params) => {
|
|
2791
|
+
const { api, node, colDef } = params;
|
|
2792
|
+
const cep = colDef.cellEditorSelector
|
|
2793
|
+
? colDef.cellEditorSelector(params)?.params
|
|
2794
|
+
: colDef.cellEditorParams;
|
|
2795
|
+
return (cep?.multiEdit &&
|
|
2796
|
+
api
|
|
2797
|
+
.getSelectedNodes()
|
|
2798
|
+
.map((row) => row.id)
|
|
2799
|
+
.includes(node.id) &&
|
|
2800
|
+
api.getEditingCells().some((cell) => cell.column.getColDef() === colDef));
|
|
2801
|
+
},
|
|
2802
|
+
};
|
|
2803
|
+
|
|
2804
|
+
const GridIcon = (props) => (jsx(LuiIcon, { name: props.icon, title: props.title, alt: props.title, size: props.size ?? "md", className: clsx(`AgGridGenericCellRenderer-${props.icon}Icon`, props.className, props.disabled && "GridIcon-disabled") }));
|
|
2805
|
+
|
|
2806
|
+
const GridLoadableCell = () => (jsx(LuiMiniSpinner, { size: 22, divProps: { className: "GridLoadableCell-container", role: "status", "aria-label": "Loading" } }));
|
|
2807
|
+
|
|
2808
|
+
const GridCellRenderer = (props) => {
|
|
2809
|
+
const { checkUpdating } = useContext(GridUpdatingContext);
|
|
2810
|
+
const colDef = props.colDef;
|
|
2811
|
+
const rendererParams = colDef.cellRendererParams;
|
|
2812
|
+
const warningFn = rendererParams?.warning;
|
|
2813
|
+
let warningText = warningFn ? warningFn(props) : undefined;
|
|
2814
|
+
const infoFn = rendererParams?.info;
|
|
2815
|
+
let infoText = infoFn ? infoFn(props) : undefined;
|
|
2816
|
+
if (Array.isArray(warningText))
|
|
2817
|
+
warningText = warningText.join("\n");
|
|
2818
|
+
if (Array.isArray(infoText))
|
|
2819
|
+
infoText = infoText.join("\n");
|
|
2820
|
+
return checkUpdating(colDef.field ?? colDef.colId ?? "", props.data.id) ? (jsx(GridLoadableCell, {})) : (jsxs(Fragment$1, { children: [!!warningText && (jsx(GridIcon, { icon: "ic_warning_outline", title: typeof warningText === "string" ? warningText : "Warning" })), !!infoText && jsx(GridIcon, { icon: "ic_info_outline", title: typeof infoText === "string" ? infoText : "Info" }), jsx("div", { className: "GridCell-container", children: colDef.cellRendererParams?.originalCellRenderer ? (jsx(colDef.cellRendererParams.originalCellRenderer, { ...props })) : (jsx("span", { title: props.valueFormatted ?? undefined, children: props.valueFormatted })) }), fnOrVar(colDef.editable, props) && rendererParams?.rightHoverElement && (jsx("div", { className: "GridCell-hoverRight", children: rendererParams?.rightHoverElement }))] }));
|
|
2821
|
+
};
|
|
2822
|
+
const suppressCellKeyboardEvents = (e) => {
|
|
2823
|
+
const shortcutKeys = e.colDef.cellRendererParams?.shortcutKeys ?? {};
|
|
2824
|
+
const exec = shortcutKeys[e.event.key];
|
|
2825
|
+
if (!e.editing && !e.event.repeat && e.event.type === "keypress" && exec) {
|
|
2826
|
+
const editable = fnOrVar(e.colDef?.editable, e);
|
|
2827
|
+
return editable ? exec(e) ?? true : true;
|
|
2836
2828
|
}
|
|
2829
|
+
// It's important that aggrid doesn't trigger edit on enter
|
|
2830
|
+
// as the incorrect selected rows will be returned
|
|
2831
|
+
return !["ArrowLeft", "ArrowRight", "ArrowDown", "ArrowUp", "Tab", " ", "Home", "End", "PageUp", "PageDown"].includes(e.event.key);
|
|
2837
2832
|
};
|
|
2838
|
-
const
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
...restStateProps,
|
|
2857
|
-
onPointerLeave: (e) => onPointerLeave(e, true),
|
|
2858
|
-
onFocus: setHover,
|
|
2859
|
-
}, restProps);
|
|
2860
|
-
return (jsx("li", { role: "menuitem", ...mergedProps, ...commonProps(isDisabled), ref: useCombinedRef(externalRef, menuItemRef), className: useBEM({ block: menuClass, element: menuItemClass, modifiers, className }), children: renderChildren }));
|
|
2833
|
+
const generateFilterGetter = (field, filterValueGetter, valueFormatter) => {
|
|
2834
|
+
if (filterValueGetter)
|
|
2835
|
+
return filterValueGetter;
|
|
2836
|
+
// aggrid will default to valueGetter
|
|
2837
|
+
if (typeof valueFormatter !== "function" || !field)
|
|
2838
|
+
return undefined;
|
|
2839
|
+
return (params) => {
|
|
2840
|
+
const value = params.getValue(field);
|
|
2841
|
+
let formattedValue = valueFormatter({ ...params, value });
|
|
2842
|
+
// Search for null values using standard dash
|
|
2843
|
+
if (formattedValue === "–")
|
|
2844
|
+
formattedValue += " -";
|
|
2845
|
+
// Search by raw value as well as formatted
|
|
2846
|
+
const gotValue = ["string", "number"].includes(typeof value) ? value : undefined;
|
|
2847
|
+
return (formattedValue + (gotValue != null && formattedValue != gotValue ? " " + gotValue : "")) //
|
|
2848
|
+
.replaceAll(/\s+/g, " ")
|
|
2849
|
+
.trim();
|
|
2850
|
+
};
|
|
2861
2851
|
};
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2852
|
+
/*
|
|
2853
|
+
* All cells should use this.
|
|
2854
|
+
*/
|
|
2855
|
+
const GridCell = (props, custom) => {
|
|
2856
|
+
// Generate a default filter value getter which uses the formatted value plus
|
|
2857
|
+
// the editable value if it's a string and different from the formatted value.
|
|
2858
|
+
// This is so that e.g. bearings can be searched for by DMS or raw number.
|
|
2859
|
+
const valueFormatter = props.valueFormatter;
|
|
2860
|
+
const filterValueGetter = generateFilterGetter(props.field, props.filterValueGetter, valueFormatter);
|
|
2861
|
+
const exportable = props.exportable;
|
|
2862
|
+
// Can't leave this here ag-grid will complain
|
|
2863
|
+
delete props.exportable;
|
|
2864
|
+
return {
|
|
2865
|
+
colId: props.field ?? props.field,
|
|
2866
|
+
headerTooltip: props.headerName,
|
|
2867
|
+
sortable: !!(props?.field || props?.valueGetter),
|
|
2868
|
+
resizable: true,
|
|
2869
|
+
editable: props.editable ?? false,
|
|
2870
|
+
...(custom?.editor && {
|
|
2871
|
+
cellClassRules: GridCellMultiSelectClassRules,
|
|
2872
|
+
editable: props.editable ?? true,
|
|
2873
|
+
cellEditor: GenericCellEditorComponentWrapper(custom?.editor),
|
|
2874
|
+
}),
|
|
2875
|
+
suppressKeyboardEvent: suppressCellKeyboardEvents,
|
|
2876
|
+
...(custom?.editorParams && {
|
|
2877
|
+
cellEditorParams: {
|
|
2878
|
+
...custom.editorParams,
|
|
2879
|
+
multiEdit: custom.multiEdit,
|
|
2880
|
+
preventAutoEdit: custom.preventAutoEdit ?? false,
|
|
2881
|
+
},
|
|
2882
|
+
}),
|
|
2883
|
+
// If there's a valueFormatter and no filterValueGetter then create a filterValueGetter
|
|
2884
|
+
filterValueGetter,
|
|
2885
|
+
// Default value formatter, otherwise react freaks out on objects
|
|
2886
|
+
valueFormatter: (params) => {
|
|
2887
|
+
if (params.value == null)
|
|
2888
|
+
return "–";
|
|
2889
|
+
const types = ["number", "boolean", "string"];
|
|
2890
|
+
if (types.includes(typeof params.value))
|
|
2891
|
+
return `${params.value}`;
|
|
2892
|
+
else
|
|
2893
|
+
return JSON.stringify(params.value);
|
|
2894
|
+
},
|
|
2895
|
+
...props,
|
|
2896
|
+
cellRenderer: GridCellRenderer,
|
|
2897
|
+
cellRendererParams: {
|
|
2898
|
+
originalCellRenderer: props.cellRenderer,
|
|
2899
|
+
...props.cellRendererParams,
|
|
2900
|
+
},
|
|
2901
|
+
headerComponentParams: {
|
|
2902
|
+
exportable,
|
|
2903
|
+
...props.headerComponentParams,
|
|
2904
|
+
},
|
|
2905
|
+
};
|
|
2866
2906
|
};
|
|
2867
|
-
const
|
|
2907
|
+
const GenericCellEditorComponentWrapper = (editor) => {
|
|
2908
|
+
const obj = { editor };
|
|
2909
|
+
return forwardRef(function GenericCellEditorComponentFr(cellEditorParams, _) {
|
|
2910
|
+
const valueFormatted = cellEditorParams.formatValue
|
|
2911
|
+
? cellEditorParams.formatValue(cellEditorParams.value)
|
|
2912
|
+
: "Missing formatter";
|
|
2913
|
+
return (jsxs(GridPopoverContextProvider, { props: cellEditorParams, children: [jsx(cellEditorParams.colDef.cellRenderer, { ...cellEditorParams, valueFormatted: valueFormatted, ...cellEditorParams.colDef.cellRendererParams }), obj.editor && jsx(obj.editor, { ...cellEditorParams })] }));
|
|
2914
|
+
});
|
|
2915
|
+
};
|
|
2868
2916
|
|
|
2869
|
-
const
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2917
|
+
const GridCellFillerColId = "gridCellFiller";
|
|
2918
|
+
const isGridCellFiller = (col) => col.colId === GridCellFillerColId;
|
|
2919
|
+
const GridCellFiller = () => ({
|
|
2920
|
+
colId: GridCellFillerColId,
|
|
2921
|
+
headerName: "",
|
|
2922
|
+
flex: 1,
|
|
2923
|
+
});
|
|
2873
2924
|
|
|
2874
|
-
const
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
useIsomorphicLayoutEffect(() => {
|
|
2888
|
-
if (overflowStyle && ref.current)
|
|
2889
|
-
ref.current.scrollTop = 0;
|
|
2890
|
-
}, [overflowStyle]);
|
|
2891
|
-
return (jsx("div", { ...restProps, ref: useCombinedRef(externalRef, ref), className: useBEM({ block: menuClass, element: menuGroupClass, className }), style: { ...style, ...overflowStyle } }));
|
|
2892
|
-
};
|
|
2893
|
-
const MenuGroup = forwardRef(MenuGroupFr);
|
|
2925
|
+
const Editor = (props) => ({
|
|
2926
|
+
component: GenericCellEditorComponentWrapper(props.editor),
|
|
2927
|
+
params: { ...props.editorParams, multiEdit: props.multiEdit },
|
|
2928
|
+
});
|
|
2929
|
+
/**
|
|
2930
|
+
* Used to choose between cell editors based in data.
|
|
2931
|
+
*/
|
|
2932
|
+
const GridCellMultiEditor = (props, cellEditorSelector) => GridCell({
|
|
2933
|
+
cellClassRules: GridCellMultiSelectClassRules,
|
|
2934
|
+
cellEditorSelector,
|
|
2935
|
+
editable: props.editable ?? true,
|
|
2936
|
+
...props,
|
|
2937
|
+
});
|
|
2894
2938
|
|
|
2895
|
-
const
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
};
|
|
2899
|
-
const MenuRadioGroup = forwardRef(MenuRadioGroupFr);
|
|
2939
|
+
const GridFilterHeaderIconButton = forwardRef(function columnsButton({ icon, title, onClick, buttonProps, disabled = false, size = "md" }, ref) {
|
|
2940
|
+
return (jsx(LuiButton, { ...buttonProps, type: "button", level: "tertiary", className: "lui-button-icon-only", ref: ref, "aria-label": title, title: title, onClick: onClick, disabled: disabled, children: jsx(LuiIcon, { name: icon, alt: "Menu", size: size }) }));
|
|
2941
|
+
});
|
|
2900
2942
|
|
|
2901
2943
|
const GridFilterColumnsToggle = ({ saveState = true }) => {
|
|
2902
2944
|
const [loaded, setLoaded] = useState(false);
|
|
@@ -3173,7 +3215,7 @@ var css_248z$2 = ".helpText{color:#6b6966;font-size:.75rem;font-weight:400}";
|
|
|
3173
3215
|
styleInject(css_248z$2);
|
|
3174
3216
|
|
|
3175
3217
|
const FormError = (props) => {
|
|
3176
|
-
return (jsxs(Fragment, { children: [props.error && (jsx("span", { className: "LuiTextInput-error", style: { paddingLeft: 0 }, children: props.error })), props.helpText && !props.error && jsx("span", { className: "helpText", children: props.helpText })] }));
|
|
3218
|
+
return (jsxs(Fragment$1, { children: [props.error && (jsx("span", { className: "LuiTextInput-error", style: { paddingLeft: 0 }, children: props.error })), props.helpText && !props.error && jsx("span", { className: "helpText", children: props.helpText })] }));
|
|
3177
3219
|
};
|
|
3178
3220
|
|
|
3179
3221
|
function escapeStringRegexp(string) {
|
|
@@ -3361,7 +3403,7 @@ const useGridPopoverHook = (props) => {
|
|
|
3361
3403
|
}
|
|
3362
3404
|
}, [cancelEdit, props, stopEditing, updateValue]);
|
|
3363
3405
|
const popoverWrapper = useCallback((children) => {
|
|
3364
|
-
return (jsx(Fragment, { children: anchorRef.current && (jsxs(ControlledMenu, { state: isOpen ? "open" : "closed", portal: true, unmountOnClose: true, anchorRef: anchorRef, saveButtonRef: saveButtonRef, menuClassName: "step-ag-grid-react-menu", onClose: (event) => {
|
|
3406
|
+
return (jsx(Fragment$1, { children: anchorRef.current && (jsxs(ControlledMenu, { state: isOpen ? "open" : "closed", portal: true, unmountOnClose: true, anchorRef: anchorRef, saveButtonRef: saveButtonRef, menuClassName: "step-ag-grid-react-menu", onClose: (event) => {
|
|
3365
3407
|
// Prevent menu from closing when modals are invoked
|
|
3366
3408
|
if (event.reason === CloseReason.BLUR)
|
|
3367
3409
|
return;
|
|
@@ -3497,19 +3539,19 @@ const GridFormDropDown = (props) => {
|
|
|
3497
3539
|
});
|
|
3498
3540
|
let lastHeader = null;
|
|
3499
3541
|
let showHeader = null;
|
|
3500
|
-
return popoverWrapper(jsxs(Fragment, { children: [props.filtered && (jsxs("div", { className: "GridFormDropDown-filter", children: [jsx(FocusableItem, { className: "filter-item", onFocus: () => {
|
|
3542
|
+
return popoverWrapper(jsxs(Fragment$1, { children: [props.filtered && (jsxs("div", { className: "GridFormDropDown-filter", children: [jsx(FocusableItem, { className: "filter-item", onFocus: () => {
|
|
3501
3543
|
setSelectedItem(null);
|
|
3502
3544
|
setSubSelectedValue(null);
|
|
3503
3545
|
subComponentIsValid.current = true;
|
|
3504
3546
|
}, children: ({ ref }) => (jsxs("div", { style: { display: "flex", flexDirection: "column", width: "100%" }, children: [jsx("input", { className: "LuiTextInput-input", ref: ref, type: "text", placeholder: props.filterPlaceholder ?? "Filter...", "data-testid": "filteredMenu-free-text-input", defaultValue: filter, "data-allowtabtosave": true, "data-disableenterautosave": !props.onSelectFilter &&
|
|
3505
|
-
!(filteredValues && filteredValues.length === 1 && !filteredValues[0].subComponent), onChange: (e) => setFilter(e.target.value) }), props.filterHelpText && isNotEmpty(filter) && (jsx(FormError, { error: null, helpText: props.filterHelpText }))] })) }), jsx(MenuDivider, {}, `$$divider_filter`)] })), jsx(ComponentLoadingWrapper, { loading: !options, className: "GridFormDropDown-options", children: jsxs(Fragment, { children: [options && (isEmpty(options) || (filteredValues && isEmpty(filteredValues))) && (jsx(MenuItem, { className: "GridPopoverEditDropDown-noOptions", disabled: true, children: props.noOptionsMessage ?? "No Options" }, `${fieldToString(field)}-empty`)), options?.map((item, index) => {
|
|
3547
|
+
!(filteredValues && filteredValues.length === 1 && !filteredValues[0].subComponent), onChange: (e) => setFilter(e.target.value) }), props.filterHelpText && isNotEmpty(filter) && (jsx(FormError, { error: null, helpText: props.filterHelpText }))] })) }), jsx(MenuDivider, {}, `$$divider_filter`)] })), jsx(ComponentLoadingWrapper, { loading: !options, className: "GridFormDropDown-options", children: jsxs(Fragment$1, { children: [options && (isEmpty(options) || (filteredValues && isEmpty(filteredValues))) && (jsx(MenuItem, { className: "GridPopoverEditDropDown-noOptions", disabled: true, children: props.noOptionsMessage ?? "No Options" }, `${fieldToString(field)}-empty`)), options?.map((item, index) => {
|
|
3506
3548
|
showHeader = null;
|
|
3507
3549
|
if (item.value === MenuSeparatorString) {
|
|
3508
3550
|
return jsx(MenuDivider, {}, `$$divider_${index}`);
|
|
3509
3551
|
}
|
|
3510
3552
|
else if (item.value === MenuHeaderString) {
|
|
3511
3553
|
lastHeader = jsx(MenuHeader, { children: item.label }, `$$header_${index}`);
|
|
3512
|
-
return jsx(Fragment, {});
|
|
3554
|
+
return jsx(Fragment$1, {});
|
|
3513
3555
|
}
|
|
3514
3556
|
else {
|
|
3515
3557
|
if (lastHeader) {
|
|
@@ -3517,7 +3559,7 @@ const GridFormDropDown = (props) => {
|
|
|
3517
3559
|
lastHeader = null;
|
|
3518
3560
|
}
|
|
3519
3561
|
}
|
|
3520
|
-
return ((!filteredValues || filteredValues.includes(item)) && (jsxs(Fragment
|
|
3562
|
+
return ((!filteredValues || filteredValues.includes(item)) && (jsxs(Fragment, { children: [showHeader, jsxs("div", { children: [jsxs(MenuItem, { disabled: !!item.disabled, title: item.disabled && typeof item.disabled !== "boolean" ? item.disabled : "", value: item.value, onFocus: () => {
|
|
3521
3563
|
if (selectedItem !== item) {
|
|
3522
3564
|
setSelectedItem(item);
|
|
3523
3565
|
setSubSelectedValue(null);
|
|
@@ -3710,7 +3752,7 @@ const GridFormMessage = (props) => {
|
|
|
3710
3752
|
setMessage(await props.message(selectedRows));
|
|
3711
3753
|
})().then();
|
|
3712
3754
|
}, [props, selectedRows]);
|
|
3713
|
-
return popoverWrapper(jsx(ComponentLoadingWrapper, { loading: message === null, className: clsx("GridFormMessage-container", props.className), children: jsx(Fragment, { children: message }) }));
|
|
3755
|
+
return popoverWrapper(jsx(ComponentLoadingWrapper, { loading: message === null, className: clsx("GridFormMessage-container", props.className), children: jsx(Fragment$1, { children: message }) }));
|
|
3714
3756
|
};
|
|
3715
3757
|
|
|
3716
3758
|
const GridFormMultiSelect = (props) => {
|
|
@@ -3789,10 +3831,10 @@ const GridFormMultiSelect = (props) => {
|
|
|
3789
3831
|
invalid,
|
|
3790
3832
|
save,
|
|
3791
3833
|
});
|
|
3792
|
-
return popoverWrapper(jsx(ComponentLoadingWrapper, { loading: !options, className: "GridFormMultiSelect-container", children: options && (jsxs(Fragment, { children: [props.filtered && (jsx(FilterInput, { ...{ headerGroups, options, setOptions, filter, setFilter, triggerSave }, filterHelpText: props.filterHelpText, onSelectFilter: props.onSelectFilter, filterPlaceholder: props.filterPlaceholder })), headerGroups &&
|
|
3834
|
+
return popoverWrapper(jsx(ComponentLoadingWrapper, { loading: !options, className: "GridFormMultiSelect-container", children: options && (jsxs(Fragment$1, { children: [props.filtered && (jsx(FilterInput, { ...{ headerGroups, options, setOptions, filter, setFilter, triggerSave }, filterHelpText: props.filterHelpText, onSelectFilter: props.onSelectFilter, filterPlaceholder: props.filterPlaceholder })), headerGroups &&
|
|
3793
3835
|
(isEmpty(headerGroups) || !toPairs(headerGroups).some(([_, options]) => !isEmpty(options))) && (jsx(MenuItem, { className: "GridMultiSelect-noOptions", disabled: true, children: props.noOptionsMessage ?? "No Options" }, "noOptions")), headerGroups && !isEmpty(headerGroups) && (jsx("div", { className: "GridFormMultiSelect-options", children: headers.map((header, index) => {
|
|
3794
3836
|
const subOptions = headerGroups[`${header.filter}`];
|
|
3795
|
-
return (!isEmpty(subOptions) && (jsxs(Fragment
|
|
3837
|
+
return (!isEmpty(subOptions) && (jsxs(Fragment, { children: [header.header && jsx(MenuHeader, { children: header.header }), subOptions.map((item, index) => item.value === MenuSeparatorString ? (jsx(MenuDivider, {}, `div_${index}`)) : (jsxs(Fragment, { children: [jsx(MenuRadioItem, { item: item, options: options, setOptions: setOptions }), item.checked && item.subComponent && (jsx(MenuSubComponent, { ...{ item, options, setOptions, data, triggerSave }, subComponentIsValid: subComponentIsValidRef.current }))] }, `val_${item.value}`)))] }, `group_${index}`)));
|
|
3796
3838
|
}) }))] })) }));
|
|
3797
3839
|
};
|
|
3798
3840
|
const FilterInput = (props) => {
|
|
@@ -3867,7 +3909,7 @@ const FilterInput = (props) => {
|
|
|
3867
3909
|
lastKeyWasEnter.current = false;
|
|
3868
3910
|
}
|
|
3869
3911
|
}, [addCustomFilterValue, filter, onSelectFilter, setFilter, toggleSelectAllVisible, triggerSave]);
|
|
3870
|
-
return (jsxs(Fragment, { children: [jsx(FocusableItem, { className: "filter-item", children: (_) => (jsxs("div", { style: { width: "100%" }, className: "GridFormMultiSelect-filter", children: [jsx("input", { className: "LuiTextInput-input", type: "text", placeholder: filterPlaceholder ?? "Filter...", "data-testid": "filteredMenu-free-text-input", value: filter, "data-disableenterautosave": true, "data-allowtabtosave": true, onChange: (e) => setFilter(e.target.value), onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }), filterHelpText && (jsx(FormError, { error: null, helpText: typeof filterHelpText === "function" ? filterHelpText(filter.trim(), options) : filterHelpText }))] })) }, "filter"), jsx(MenuDivider, {}, `$$divider_filter`)] }));
|
|
3912
|
+
return (jsxs(Fragment$1, { children: [jsx(FocusableItem, { className: "filter-item", children: (_) => (jsxs("div", { style: { width: "100%" }, className: "GridFormMultiSelect-filter", children: [jsx("input", { className: "LuiTextInput-input", type: "text", placeholder: filterPlaceholder ?? "Filter...", "data-testid": "filteredMenu-free-text-input", value: filter, "data-disableenterautosave": true, "data-allowtabtosave": true, onChange: (e) => setFilter(e.target.value), onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }), filterHelpText && (jsx(FormError, { error: null, helpText: typeof filterHelpText === "function" ? filterHelpText(filter.trim(), options) : filterHelpText }))] })) }, "filter"), jsx(MenuDivider, {}, `$$divider_filter`)] }));
|
|
3871
3913
|
};
|
|
3872
3914
|
const MenuRadioItem = (props) => {
|
|
3873
3915
|
const { item, options, setOptions } = props;
|
|
@@ -3881,7 +3923,7 @@ const MenuRadioItem = (props) => {
|
|
|
3881
3923
|
e.keepOpen = true;
|
|
3882
3924
|
toggleValue(item);
|
|
3883
3925
|
}
|
|
3884
|
-
}, children: jsx(LuiCheckboxInput, { isChecked: item.checked ?? false, value: `${item.value}`, label: jsxs(Fragment, { children: [item.warning && jsx(GridIcon, { icon: "ic_warning_outline", title: item.warning }), item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`)] }), inputProps: {
|
|
3926
|
+
}, children: jsx(LuiCheckboxInput, { isChecked: item.checked ?? false, value: `${item.value}`, label: jsxs(Fragment$1, { children: [item.warning && jsx(GridIcon, { icon: "ic_warning_outline", title: item.warning }), item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`)] }), inputProps: {
|
|
3885
3927
|
onClick: (e) => {
|
|
3886
3928
|
// Click is handled by MenuItem onClick
|
|
3887
3929
|
e.preventDefault();
|
|
@@ -3963,20 +4005,20 @@ const GridFormMultiSelectGrid = (props) => {
|
|
|
3963
4005
|
setOptions([...options]);
|
|
3964
4006
|
}
|
|
3965
4007
|
}, [options, setOptions]);
|
|
3966
|
-
return popoverWrapper(jsx(Fragment, { children: jsx("div", { className: "Grid-popoverContainer", children: jsx("div", { style: {
|
|
4008
|
+
return popoverWrapper(jsx(Fragment$1, { children: jsx("div", { className: "Grid-popoverContainer", children: jsx("div", { style: {
|
|
3967
4009
|
display: "grid",
|
|
3968
4010
|
gridAutoFlow: "column",
|
|
3969
4011
|
gridTemplateRows: `repeat(${props.maxRowCount ?? 10}, auto)`,
|
|
3970
4012
|
maxWidth: "calc(min(100vw,500px))",
|
|
3971
4013
|
overflowX: "auto",
|
|
3972
4014
|
}, children: options &&
|
|
3973
|
-
options.map((o) => (jsx(Fragment, { children: jsx(MenuItem, { onClick: (e) => {
|
|
4015
|
+
options.map((o) => (jsx(Fragment$1, { children: jsx(MenuItem, { onClick: (e) => {
|
|
3974
4016
|
// Global react-menu MenuItem handler handles tabs
|
|
3975
4017
|
if (e.key !== "Tab" && e.key !== "Enter") {
|
|
3976
4018
|
e.keepOpen = true;
|
|
3977
4019
|
toggleValue(o);
|
|
3978
4020
|
}
|
|
3979
|
-
}, children: jsx(LuiCheckboxInput, { isChecked: !!o.checked ?? false, isIndeterminate: o.checked === "partial", value: `${o.value}`, label: jsxs(Fragment, { children: [o.warning && jsx(GridIcon, { icon: "ic_warning_outline", title: o.warning }), jsx("span", { className: "GridMultiSelectGrid-Label", children: o.label ?? (o.value == null ? `<${o.value}>` : `${o.value}`) })] }), inputProps: {
|
|
4021
|
+
}, children: jsx(LuiCheckboxInput, { isChecked: !!o.checked ?? false, isIndeterminate: o.checked === "partial", value: `${o.value}`, label: jsxs(Fragment$1, { children: [o.warning && jsx(GridIcon, { icon: "ic_warning_outline", title: o.warning }), jsx("span", { className: "GridMultiSelectGrid-Label", children: o.label ?? (o.value == null ? `<${o.value}>` : `${o.value}`) })] }), inputProps: {
|
|
3980
4022
|
onClick: (e) => {
|
|
3981
4023
|
// Click is handled by MenuItem onClick
|
|
3982
4024
|
e.preventDefault();
|
|
@@ -4062,7 +4104,7 @@ const GridFormPopoverMenu = (props) => {
|
|
|
4062
4104
|
invalid: () => subComponentSelected && !subComponentIsValid.current,
|
|
4063
4105
|
save,
|
|
4064
4106
|
});
|
|
4065
|
-
return popoverWrapper(jsx(ComponentLoadingWrapper, { loading: !options, className: "GridFormPopupMenu", children: jsx(Fragment, { children: isEmpty(options) ? (jsx(MenuItem, { className: "GridPopoverMenu-noOptions", disabled: true, children: "No actions" }, `GridPopoverMenu-empty`)) : (options?.map((item, index) => item.label === "__isMenuSeparator__" ? (jsx(MenuDivider, {}, `$$divider_${index}`)) : (!item.hidden && (jsxs(Fragment
|
|
4107
|
+
return popoverWrapper(jsx(ComponentLoadingWrapper, { loading: !options, className: "GridFormPopupMenu", children: jsx(Fragment$1, { children: isEmpty(options) ? (jsx(MenuItem, { className: "GridPopoverMenu-noOptions", disabled: true, children: "No actions" }, `GridPopoverMenu-empty`)) : (options?.map((item, index) => item.label === "__isMenuSeparator__" ? (jsx(MenuDivider, {}, `$$divider_${index}`)) : (!item.hidden && (jsxs(Fragment, { children: [jsx(MenuItem, { onClick: (e) => onMenuItemClick(e, item), disabled: !!item.disabled, title: item.disabled && typeof item.disabled !== "boolean" ? item.disabled : "", children: item.label }, `${item.label}`), item.subComponent && subComponentSelected === item && (jsx(FocusableItem, { className: "LuiDeprecatedForms", children: () => (jsx(GridSubComponentContext.Provider, { value: {
|
|
4066
4108
|
context: {},
|
|
4067
4109
|
data,
|
|
4068
4110
|
value: subSelectedValue,
|
|
@@ -4506,6 +4548,16 @@ const GridContextProvider = (props) => {
|
|
|
4506
4548
|
const prePopupOps = useCallback(() => {
|
|
4507
4549
|
prePopupFocusedCell.current = gridApi?.getFocusedCell() ?? undefined;
|
|
4508
4550
|
}, [gridApi]);
|
|
4551
|
+
/**
|
|
4552
|
+
* After a popup refocus the cell.
|
|
4553
|
+
*/
|
|
4554
|
+
const postPopupOps = useCallback(() => {
|
|
4555
|
+
if (!gridApi)
|
|
4556
|
+
return;
|
|
4557
|
+
if (prePopupFocusedCell.current) {
|
|
4558
|
+
gridApi?.setFocusedCell(prePopupFocusedCell.current.rowIndex, prePopupFocusedCell.current.column);
|
|
4559
|
+
}
|
|
4560
|
+
}, [gridApi]);
|
|
4509
4561
|
/**
|
|
4510
4562
|
* Get all row id's in grid.
|
|
4511
4563
|
*/
|
|
@@ -4878,6 +4930,7 @@ const GridContextProvider = (props) => {
|
|
|
4878
4930
|
setInvisibleColumnIds,
|
|
4879
4931
|
gridReady,
|
|
4880
4932
|
prePopupOps,
|
|
4933
|
+
postPopupOps,
|
|
4881
4934
|
setApis,
|
|
4882
4935
|
setQuickFilter,
|
|
4883
4936
|
selectRowsById,
|
|
@@ -28489,5 +28542,5 @@ const clickActionButton = async (text, container) => {
|
|
|
28489
28542
|
});
|
|
28490
28543
|
};
|
|
28491
28544
|
|
|
28492
|
-
export { ActionButton, ComponentLoadingWrapper, ControlledMenu, Editor, FocusableItem, FormError, GenericCellEditorComponentWrapper, Grid, GridCell, GridCellFiller, GridCellFillerColId, GridCellMultiEditor, GridCellMultiSelectClassRules, GridCellRenderer, GridContext, GridContextProvider, GridFilterButtons, GridFilterColumnsToggle, GridFilterDownloadCsvButton, GridFilterHeaderIconButton, GridFilterQuick, GridFilters, GridFormDropDown, GridFormEditBearing, GridFormMessage, GridFormMultiSelect, GridFormMultiSelectGrid, GridFormPopoverMenu, GridFormSubComponentTextArea, GridFormSubComponentTextInput, GridFormTextArea, GridFormTextInput, GridHeaderSelect, GridIcon, GridLoadableCell, GridNoRowsOverlay, GridPopoutEditMultiSelect, GridPopoutEditMultiSelectGrid, GridPopoverContext, GridPopoverContextProvider, GridPopoverEditBearing, GridPopoverEditBearingCorrection, GridPopoverEditBearingCorrectionEditorParams, GridPopoverEditBearingEditorParams, GridPopoverEditDropDown, GridPopoverMenu, GridPopoverMessage, GridPopoverTextArea, GridPopoverTextInput, GridRenderPopoutMenuCell, GridSubComponentContext, GridUpdatingContext, GridUpdatingContextProvider, GridWrapper, Menu, MenuButton, MenuDivider, MenuGroup, MenuHeader, MenuHeaderItem, MenuHeaderString, MenuItem, MenuRadioGroup, MenuSeparator, MenuSeparatorString, PopoutMenuSeparator, SubMenu, TextAreaInput, TextInputFormatted, bearingCorrectionRangeValidator, bearingCorrectionValueFormatter, bearingNumberParser, bearingRangeValidator, bearingStringValidator, bearingValueFormatter, clickActionButton, clickMenuOption, clickMultiSelectOption, closeMenu, closePopover, convertDDToDMS, countRows, deselectRow, downloadCsvUseValueFormattersProcessCellCallback, editCell, findActionButton, findCell, findCellContains, findMenuOption, findMultiSelectOption, findOpenPopover, findParentWithClass, findRow, fnOrVar, generateFilterGetter, getMultiSelectOptions, hasParentClass, isCellReadOnly, isFloat, isGridCellFiller, isNotEmpty, openAndClickMenuOption, openAndFindMenuOption, queryMenuOption, queryRow, sanitiseFileName, selectCell, selectRow, stringByteLengthIsInvalid, suppressCellKeyboardEvents, typeInputByLabel, typeInputByPlaceholder, typeOnlyInput, typeOtherInput, typeOtherTextArea, useDeferredPromise, useGenerateOrDefaultId, useGridContext, useGridFilter, useGridPopoverContext, useGridPopoverHook, useMenuState, usePostSortRowsHook, validateMenuOptions, wait$1 as wait };
|
|
28545
|
+
export { ActionButton, ComponentLoadingWrapper, ControlledMenu, Editor, FocusableItem, FormError, GenericCellEditorComponentWrapper, Grid, GridCell, GridCellFiller, GridCellFillerColId, GridCellMultiEditor, GridCellMultiSelectClassRules, GridCellRenderer, GridContext, GridContextProvider, GridFilterButtons, GridFilterColumnsToggle, GridFilterDownloadCsvButton, GridFilterHeaderIconButton, GridFilterQuick, GridFilters, GridFormDropDown, GridFormEditBearing, GridFormMessage, GridFormMultiSelect, GridFormMultiSelectGrid, GridFormPopoverMenu, GridFormSubComponentTextArea, GridFormSubComponentTextInput, GridFormTextArea, GridFormTextInput, GridHeaderSelect, GridIcon, GridLoadableCell, GridNoRowsOverlay, GridPopoutEditMultiSelect, GridPopoutEditMultiSelectGrid, GridPopoverContext, GridPopoverContextProvider, GridPopoverEditBearing, GridPopoverEditBearingCorrection, GridPopoverEditBearingCorrectionEditorParams, GridPopoverEditBearingEditorParams, GridPopoverEditDropDown, GridPopoverMenu, GridPopoverMessage, GridPopoverTextArea, GridPopoverTextInput, GridRenderPopoutMenuCell, GridSubComponentContext, GridUpdatingContext, GridUpdatingContextProvider, GridWrapper, Menu, MenuButton, MenuDivider, MenuGroup, MenuHeader, MenuHeaderItem, MenuHeaderString, MenuItem, MenuRadioGroup, MenuSeparator, MenuSeparatorString, PopoutMenuSeparator, SubMenu, TextAreaInput, TextInputFormatted, bearingCorrectionRangeValidator, bearingCorrectionValueFormatter, bearingNumberParser, bearingRangeValidator, bearingStringValidator, bearingValueFormatter, clickActionButton, clickMenuOption, clickMultiSelectOption, closeMenu, closePopover, convertDDToDMS, countRows, deselectRow, downloadCsvUseValueFormattersProcessCellCallback, editCell, findActionButton, findCell, findCellContains, findMenuOption, findMultiSelectOption, findOpenPopover, findParentWithClass, findRow, fnOrVar, generateFilterGetter, getMultiSelectOptions, hasParentClass, isCellReadOnly, isFloat, isGridCellFiller, isNotEmpty, openAndClickMenuOption, openAndFindMenuOption, queryMenuOption, queryRow, sanitiseFileName, selectCell, selectRow, stringByteLengthIsInvalid, suppressCellKeyboardEvents, typeInputByLabel, typeInputByPlaceholder, typeOnlyInput, typeOtherInput, typeOtherTextArea, useDeferredPromise, useGenerateOrDefaultId, useGridContext, useGridContextMenu, useGridFilter, useGridPopoverContext, useGridPopoverHook, useMenuState, usePostSortRowsHook, validateMenuOptions, wait$1 as wait };
|
|
28493
28546
|
//# sourceMappingURL=step-ag-grid.esm.js.map
|