@linzjs/step-ag-grid 23.0.1 → 24.0.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/dist/src/components/ComponentLoadingWrapper.d.ts +1 -1
- package/dist/src/components/Grid.d.ts +17 -17
- package/dist/src/components/GridCell.d.ts +9 -9
- package/dist/src/components/GridCellFiller.d.ts +2 -2
- package/dist/src/components/GridCellMultiEditor.d.ts +6 -6
- package/dist/src/components/GridCellMultiSelectClassRules.d.ts +1 -1
- package/dist/src/components/GridIcon.d.ts +2 -2
- package/dist/src/components/GridNoRowsOverlay.d.ts +1 -1
- package/dist/src/components/GridPopoverHook.d.ts +2 -2
- package/dist/src/components/GridWrapper.d.ts +1 -1
- package/dist/src/components/PostSortRowsHook.d.ts +1 -1
- package/dist/src/components/clickInputWhenContainingCellClicked.d.ts +1 -1
- package/dist/src/components/gridFilter/GridFilterButtons.d.ts +5 -5
- package/dist/src/components/gridFilter/GridFilterColumnsToggle.d.ts +1 -1
- package/dist/src/components/gridFilter/GridFilterDownloadCsvButton.d.ts +1 -1
- package/dist/src/components/gridFilter/GridFilterHeaderIconButton.d.ts +2 -2
- package/dist/src/components/gridFilter/GridFilters.d.ts +1 -1
- package/dist/src/components/gridFilter/index.d.ts +7 -7
- package/dist/src/components/gridFilter/useGridFilter.d.ts +2 -2
- package/dist/src/components/gridForm/GridFormDropDown.d.ts +8 -8
- package/dist/src/components/gridForm/GridFormEditBearing.d.ts +2 -2
- package/dist/src/components/gridForm/GridFormMessage.d.ts +3 -3
- package/dist/src/components/gridForm/GridFormMultiSelect.d.ts +4 -4
- package/dist/src/components/gridForm/GridFormMultiSelectGrid.d.ts +4 -4
- package/dist/src/components/gridForm/GridFormPopoverMenu.d.ts +5 -5
- package/dist/src/components/gridForm/GridFormSubComponentTextArea.d.ts +4 -4
- package/dist/src/components/gridForm/GridFormSubComponentTextInput.d.ts +4 -4
- package/dist/src/components/gridForm/GridFormTextArea.d.ts +4 -4
- package/dist/src/components/gridForm/GridFormTextInput.d.ts +3 -3
- package/dist/src/components/gridForm/index.d.ts +10 -10
- package/dist/src/components/gridHeader/GridHeaderSelect.d.ts +1 -1
- package/dist/src/components/gridHeader/index.d.ts +1 -1
- package/dist/src/components/gridHook/index.d.ts +1 -1
- package/dist/src/components/gridHook/useGridContextMenu.d.ts +4 -4
- package/dist/src/components/gridPopoverEdit/GridButton.d.ts +5 -5
- package/dist/src/components/gridPopoverEdit/GridEditBoolean.d.ts +4 -4
- package/dist/src/components/gridPopoverEdit/GridPopoutEditMultiSelect.d.ts +4 -4
- package/dist/src/components/gridPopoverEdit/GridPopoutEditMultiSelectGrid.d.ts +4 -4
- package/dist/src/components/gridPopoverEdit/GridPopoverEditBearing.d.ts +4 -4
- package/dist/src/components/gridPopoverEdit/GridPopoverEditDropDown.d.ts +4 -4
- package/dist/src/components/gridPopoverEdit/GridPopoverMenu.d.ts +4 -4
- package/dist/src/components/gridPopoverEdit/GridPopoverMessage.d.ts +4 -4
- package/dist/src/components/gridPopoverEdit/GridPopoverTextArea.d.ts +4 -4
- package/dist/src/components/gridPopoverEdit/GridPopoverTextInput.d.ts +4 -4
- package/dist/src/components/gridPopoverEdit/index.d.ts +11 -11
- package/dist/src/components/gridRender/GridRenderGenericCell.d.ts +5 -5
- package/dist/src/components/gridRender/GridRenderPopoutMenuCell.d.ts +1 -1
- package/dist/src/components/gridRender/index.d.ts +2 -2
- package/dist/src/components/gridUtil.d.ts +1 -1
- package/dist/src/components/index.d.ts +19 -19
- package/dist/src/contexts/GridContext.d.ts +4 -4
- package/dist/src/contexts/GridContextProvider.d.ts +3 -3
- package/dist/src/contexts/GridPopoverContext.d.ts +3 -3
- package/dist/src/contexts/GridPopoverContextProvider.d.ts +4 -4
- package/dist/src/contexts/GridUpdatingContextProvider.d.ts +1 -1
- package/dist/src/index.d.ts +17 -17
- package/dist/src/lui/ActionButton.d.ts +9 -9
- package/dist/src/lui/FormError.d.ts +2 -2
- package/dist/src/lui/TextAreaInput.d.ts +1 -1
- package/dist/src/lui/TextInputFormatted.d.ts +2 -2
- package/dist/src/lui/reactUtils.d.ts +1 -1
- package/dist/src/lui/stateDeferredHook.d.ts +1 -1
- package/dist/src/react-menu3/components/ControlledMenu.d.ts +2 -2
- package/dist/src/react-menu3/components/FocusableItem.d.ts +3 -3
- package/dist/src/react-menu3/components/Menu.d.ts +3 -3
- package/dist/src/react-menu3/components/MenuButton.d.ts +2 -2
- package/dist/src/react-menu3/components/MenuDivider.d.ts +1 -1
- package/dist/src/react-menu3/components/MenuGroup.d.ts +2 -2
- package/dist/src/react-menu3/components/MenuHeader.d.ts +2 -2
- package/dist/src/react-menu3/components/MenuItem.d.ts +3 -3
- package/dist/src/react-menu3/components/MenuList.d.ts +1 -1
- package/dist/src/react-menu3/components/MenuRadioGroup.d.ts +2 -2
- package/dist/src/react-menu3/components/SubMenu.d.ts +5 -5
- package/dist/src/react-menu3/contexts/EventHandlersContext.d.ts +1 -1
- package/dist/src/react-menu3/contexts/MenuListContext.d.ts +2 -2
- package/dist/src/react-menu3/contexts/MenuListItemContext.d.ts +1 -1
- package/dist/src/react-menu3/contexts/RadioGroupContext.d.ts +1 -1
- package/dist/src/react-menu3/contexts/SettingsContext.d.ts +2 -2
- package/dist/src/react-menu3/hooks/index.d.ts +9 -9
- package/dist/src/react-menu3/hooks/useBEM.d.ts +1 -1
- package/dist/src/react-menu3/hooks/useCombinedRef.d.ts +1 -1
- package/dist/src/react-menu3/hooks/useIsomorphicLayoutEffect.d.ts +1 -1
- package/dist/src/react-menu3/hooks/useItemEffect.d.ts +1 -1
- package/dist/src/react-menu3/hooks/useItemState.d.ts +1 -1
- package/dist/src/react-menu3/hooks/useItems.d.ts +2 -2
- package/dist/src/react-menu3/hooks/useMenuChange.d.ts +1 -1
- package/dist/src/react-menu3/hooks/useMenuState.d.ts +1 -1
- package/dist/src/react-menu3/hooks/useMenuStateAndFocus.d.ts +1 -1
- package/dist/src/react-menu3/index.d.ts +11 -11
- package/dist/src/react-menu3/positionUtils/getPositionHelpers.d.ts +1 -1
- package/dist/src/react-menu3/positionUtils/index.d.ts +3 -3
- package/dist/src/react-menu3/positionUtils/placeArrowHorizontal.d.ts +1 -1
- package/dist/src/react-menu3/positionUtils/placeArrowVertical.d.ts +1 -1
- package/dist/src/react-menu3/positionUtils/placeLeftorRight.d.ts +4 -4
- package/dist/src/react-menu3/positionUtils/placeToporBottom.d.ts +5 -5
- package/dist/src/react-menu3/positionUtils/positionContextMenu.d.ts +2 -2
- package/dist/src/react-menu3/positionUtils/positionMenu.d.ts +5 -5
- package/dist/src/react-menu3/style-utils/index.d.ts +1 -1
- package/dist/src/react-menu3/types.d.ts +15 -15
- package/dist/src/react-menu3/utils/constants.d.ts +1 -1
- package/dist/src/react-menu3/utils/index.d.ts +3 -3
- package/dist/src/react-menu3/utils/utils.d.ts +5 -5
- package/dist/src/react-menu3/utils/withHovering.d.ts +1 -1
- package/dist/src/utils/testQuick.d.ts +2 -2
- package/dist/src/utils/testUtil.d.ts +2 -2
- package/dist/src/utils/textValidator.d.ts +2 -2
- package/dist/step-ag-grid.cjs.js +966 -952
- package/dist/step-ag-grid.cjs.js.map +1 -1
- package/dist/step-ag-grid.esm.js +968 -954
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +26 -41
- package/src/components/ComponentLoadingWrapper.tsx +5 -6
- package/src/components/Grid.tsx +85 -83
- package/src/components/GridCell.test.tsx +15 -15
- package/src/components/GridCell.tsx +46 -44
- package/src/components/GridCellFiller.tsx +5 -4
- package/src/components/GridCellMultiEditor.tsx +7 -7
- package/src/components/GridCellMultiSelectClassRules.tsx +3 -3
- package/src/components/GridIcon.tsx +6 -7
- package/src/components/GridLoadableCell.tsx +2 -2
- package/src/components/GridNoRowsOverlay.tsx +8 -8
- package/src/components/GridPopoverHook.tsx +16 -16
- package/src/components/GridWrapper.tsx +2 -2
- package/src/components/PostSortRowsHook.ts +11 -11
- package/src/components/clickInputWhenContainingCellClicked.tsx +5 -5
- package/src/components/gridFilter/GridFilterButtons.tsx +8 -9
- package/src/components/gridFilter/GridFilterColumnsToggle.tsx +25 -26
- package/src/components/gridFilter/GridFilterDownloadCsvButton.tsx +12 -13
- package/src/components/gridFilter/GridFilterHeaderIconButton.tsx +9 -10
- package/src/components/gridFilter/GridFilterQuick.tsx +6 -6
- package/src/components/gridFilter/GridFilters.tsx +1 -1
- package/src/components/gridFilter/index.ts +7 -7
- package/src/components/gridFilter/useGridFilter.ts +3 -3
- package/src/components/gridForm/GridFormDropDown.tsx +56 -52
- package/src/components/gridForm/GridFormEditBearing.tsx +12 -12
- package/src/components/gridForm/GridFormMessage.tsx +10 -10
- package/src/components/gridForm/GridFormMultiSelect.tsx +53 -51
- package/src/components/gridForm/GridFormMultiSelectGrid.tsx +25 -26
- package/src/components/gridForm/GridFormPopoverMenu.tsx +32 -29
- package/src/components/gridForm/GridFormSubComponentTextArea.tsx +10 -10
- package/src/components/gridForm/GridFormSubComponentTextInput.tsx +8 -8
- package/src/components/gridForm/GridFormTextArea.tsx +11 -11
- package/src/components/gridForm/GridFormTextInput.tsx +11 -11
- package/src/components/gridForm/index.ts +10 -10
- package/src/components/gridHeader/GridHeaderSelect.tsx +9 -9
- package/src/components/gridHeader/index.ts +1 -1
- package/src/components/gridHook/index.ts +1 -1
- package/src/components/gridHook/useGridContextMenu.tsx +7 -7
- package/src/components/gridPopoverEdit/GridButton.tsx +13 -12
- package/src/components/gridPopoverEdit/GridEditBoolean.tsx +19 -18
- package/src/components/gridPopoverEdit/GridPopoutEditMultiSelect.ts +7 -6
- package/src/components/gridPopoverEdit/GridPopoutEditMultiSelectGrid.ts +7 -6
- package/src/components/gridPopoverEdit/GridPopoverEditBearing.ts +9 -9
- package/src/components/gridPopoverEdit/GridPopoverEditDropDown.ts +8 -7
- package/src/components/gridPopoverEdit/GridPopoverMenu.tsx +6 -6
- package/src/components/gridPopoverEdit/GridPopoverMessage.ts +4 -4
- package/src/components/gridPopoverEdit/GridPopoverTextArea.ts +4 -4
- package/src/components/gridPopoverEdit/GridPopoverTextInput.ts +4 -4
- package/src/components/gridPopoverEdit/index.ts +11 -11
- package/src/components/gridRender/GridRenderGenericCell.tsx +5 -5
- package/src/components/gridRender/GridRenderPopoutMenuCell.tsx +8 -9
- package/src/components/gridRender/index.ts +2 -2
- package/src/components/gridUtil.ts +3 -3
- package/src/components/index.ts +19 -19
- package/src/contexts/GridContext.tsx +52 -46
- package/src/contexts/GridContextProvider.test.tsx +14 -14
- package/src/contexts/GridContextProvider.tsx +42 -41
- package/src/contexts/GridPopoverContext.tsx +10 -7
- package/src/contexts/GridPopoverContextProvider.tsx +12 -10
- package/src/contexts/GridSubComponentContext.ts +6 -5
- package/src/contexts/GridUpdatingContext.tsx +5 -4
- package/src/contexts/GridUpdatingContextProvider.tsx +3 -3
- package/src/index.ts +17 -21
- package/src/lui/ActionButton.tsx +42 -41
- package/src/lui/FormError.tsx +4 -4
- package/src/lui/TextAreaInput.tsx +9 -9
- package/src/lui/TextInputFormatted.tsx +11 -11
- package/src/lui/reactUtils.test.tsx +8 -8
- package/src/lui/reactUtils.tsx +2 -2
- package/src/lui/stateDeferredHook.tsx +3 -5
- package/src/lui/timeoutHook.test.tsx +5 -5
- package/src/lui/timeoutHook.tsx +1 -1
- package/src/react-app-env.d.ts +1 -1
- package/src/react-menu3/components/ControlledMenu.tsx +48 -48
- package/src/react-menu3/components/FocusableItem.tsx +7 -7
- package/src/react-menu3/components/Menu.tsx +11 -11
- package/src/react-menu3/components/MenuButton.tsx +4 -4
- package/src/react-menu3/components/MenuDivider.tsx +4 -4
- package/src/react-menu3/components/MenuGroup.tsx +5 -5
- package/src/react-menu3/components/MenuHeader.tsx +4 -4
- package/src/react-menu3/components/MenuItem.tsx +14 -14
- package/src/react-menu3/components/MenuList.tsx +55 -55
- package/src/react-menu3/components/MenuRadioGroup.tsx +7 -7
- package/src/react-menu3/components/SubMenu.tsx +24 -24
- package/src/react-menu3/contexts/EventHandlersContext.ts +2 -2
- package/src/react-menu3/contexts/HoverItemContext.ts +1 -1
- package/src/react-menu3/contexts/ItemSettingsContext.ts +1 -1
- package/src/react-menu3/contexts/MenuListContext.ts +2 -2
- package/src/react-menu3/contexts/MenuListItemContext.ts +2 -2
- package/src/react-menu3/contexts/RadioGroupContext.ts +2 -2
- package/src/react-menu3/contexts/SettingsContext.ts +2 -2
- package/src/react-menu3/hooks/index.ts +9 -9
- package/src/react-menu3/hooks/useBEM.ts +4 -4
- package/src/react-menu3/hooks/useCombinedRef.ts +2 -2
- package/src/react-menu3/hooks/useIsomorphicLayoutEffect.ts +4 -4
- package/src/react-menu3/hooks/useItemEffect.ts +3 -3
- package/src/react-menu3/hooks/useItemState.ts +5 -5
- package/src/react-menu3/hooks/useItems.ts +8 -8
- package/src/react-menu3/hooks/useMenuChange.ts +3 -3
- package/src/react-menu3/hooks/useMenuState.ts +5 -5
- package/src/react-menu3/hooks/useMenuStateAndFocus.ts +4 -4
- package/src/react-menu3/index.ts +11 -11
- package/src/react-menu3/positionUtils/getPositionHelpers.ts +2 -2
- package/src/react-menu3/positionUtils/index.ts +3 -3
- package/src/react-menu3/positionUtils/placeArrowHorizontal.ts +1 -1
- package/src/react-menu3/positionUtils/placeArrowVertical.ts +1 -1
- package/src/react-menu3/positionUtils/placeLeftorRight.ts +13 -13
- package/src/react-menu3/positionUtils/placeToporBottom.ts +15 -15
- package/src/react-menu3/positionUtils/positionContextMenu.ts +4 -4
- package/src/react-menu3/positionUtils/positionMenu.ts +14 -14
- package/src/react-menu3/style-utils/index.ts +24 -24
- package/src/react-menu3/types.ts +15 -15
- package/src/react-menu3/utils/constants.ts +33 -33
- package/src/react-menu3/utils/index.ts +3 -4
- package/src/react-menu3/utils/propTypes2.ts +10 -10
- package/src/react-menu3/utils/utils.ts +14 -14
- package/src/react-menu3/utils/withHovering.tsx +3 -3
- package/src/stories/components/ActionButton.stories.tsx +23 -24
- package/src/stories/grid/FormTest.tsx +15 -16
- package/src/stories/grid/GridDragRow.stories.tsx +33 -34
- package/src/stories/grid/GridFilterButtons.stories.tsx +32 -33
- package/src/stories/grid/GridNoRowsOverlay.stories.tsx +9 -10
- package/src/stories/grid/GridNonEditableRow.stories.tsx +40 -41
- package/src/stories/grid/GridPinnedRow.stories.tsx +25 -26
- package/src/stories/grid/GridPopoutContextMenu.stories.tsx +33 -32
- package/src/stories/grid/GridPopoutEditBoolean.stories.tsx +21 -22
- package/src/stories/grid/GridPopoutEditGeneric.stories.tsx +18 -19
- package/src/stories/grid/GridPopoutEditGenericTextArea.stories.tsx +50 -49
- package/src/stories/grid/GridPopoverEditBearing.stories.tsx +25 -26
- package/src/stories/grid/GridPopoverEditDropDown.stories.tsx +93 -94
- package/src/stories/grid/GridPopoverEditMultiSelect.stories.tsx +62 -63
- package/src/stories/grid/GridPopoverEditMultiSelectGrid.stories.tsx +28 -28
- package/src/stories/grid/GridReadOnly.stories.tsx +66 -67
- package/src/stories/grid/GridViewList.stories.tsx +37 -38
- package/src/stories/grid/gridFormInteraction/GridFormDropDownInteraction.stories.tsx +37 -38
- package/src/stories/grid/gridFormInteraction/GridFormEditBearingCorrectionInteraction.stories.tsx +25 -26
- package/src/stories/grid/gridFormInteraction/GridFormEditBearingInteraction.stories.tsx +25 -26
- package/src/stories/grid/gridFormInteraction/GridFormMultiSelectGridInteraction.stories.tsx +31 -31
- package/src/stories/grid/gridFormInteraction/GridFormMultiSelectInteraction.stories.tsx +47 -48
- package/src/stories/grid/gridFormInteraction/GridFormPopoverMenuInteraction.stories.tsx +44 -45
- package/src/stories/grid/gridFormInteraction/GridFormTextAreaInteraction.stories.tsx +19 -20
- package/src/stories/grid/gridFormInteraction/GridFormTextInputInteraction.stories.tsx +21 -22
- package/src/stories/grid/gridFormStatic/GridFormDropDown.stories.tsx +40 -41
- package/src/stories/grid/gridFormStatic/GridFormEditBearing.stories.tsx +14 -15
- package/src/stories/grid/gridFormStatic/GridFormEditBearingCorrection.stories.tsx +14 -15
- package/src/stories/grid/gridFormStatic/GridFormMessage.stories.tsx +10 -11
- package/src/stories/grid/gridFormStatic/GridFormMultiSelect.stories.tsx +20 -21
- package/src/stories/grid/gridFormStatic/GridFormPopoverMenu.stories.tsx +16 -17
- package/src/stories/grid/gridFormStatic/GridFormTextArea.stories.tsx +13 -14
- package/src/stories/grid/gridFormStatic/GridFormTextInput.stories.tsx +13 -14
- package/src/stories/grid/interactions/GridKeyboardInteractions.stories.tsx +68 -71
- package/src/stories/react-menu/ReactMenu.stories.tsx +26 -26
- package/src/utils/bearing.test.ts +44 -44
- package/src/utils/bearing.ts +20 -20
- package/src/utils/deferredPromise.ts +1 -1
- package/src/utils/storybookTestUtil.ts +2 -2
- package/src/utils/testQuick.ts +15 -14
- package/src/utils/testUtil.ts +31 -36
- package/src/utils/textMatcher.test.ts +9 -9
- package/src/utils/textMatcher.ts +8 -8
- package/src/utils/textValidator.test.ts +56 -56
- package/src/utils/textValidator.ts +11 -11
- package/src/utils/util.test.ts +20 -20
- package/src/utils/util.ts +7 -7
package/dist/step-ag-grid.esm.js
CHANGED
|
@@ -1,16 +1,48 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { LuiMiniSpinner, LuiStatusSpinner, LuiIcon, LuiButton, LuiCheckboxInput
|
|
2
|
+
import { LuiMiniSpinner, LuiStatusSpinner, LuiIcon, LuiButtonGroup, LuiButton, LuiCheckboxInput } from '@linzjs/lui';
|
|
3
3
|
import { RowHighlightPosition } from 'ag-grid-community';
|
|
4
4
|
import { AgGridReact } from 'ag-grid-react';
|
|
5
5
|
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';
|
|
6
|
-
import React, { useRef, useEffect, useLayoutEffect, createContext, useContext,
|
|
6
|
+
import React, { useRef, useEffect, useLayoutEffect, createContext, useContext, useState, useMemo, memo, forwardRef, useCallback, useReducer, cloneElement, useImperativeHandle, Fragment as Fragment$1, useId } from 'react';
|
|
7
7
|
import { unstable_batchedUpdates, flushSync, createPortal } from 'react-dom';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* AgGrid checkbox select does not pass clicks within cell but not on the checkbox to checkbox.
|
|
11
|
+
* This passes the event to the checkbox when you click anywhere in the cell.
|
|
12
|
+
*/
|
|
13
|
+
const clickInputWhenContainingCellClicked = (params) => {
|
|
14
|
+
const { data, event, colDef } = params;
|
|
15
|
+
if (!data || !event)
|
|
16
|
+
return;
|
|
17
|
+
const element = event.target;
|
|
18
|
+
// Already handled
|
|
19
|
+
if (element.closest('.GridCell-readonly') ||
|
|
20
|
+
(['BUTTON', 'INPUT'].includes(element?.tagName) && element.closest('.ag-cell-inline-editing'))) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const row = element.closest('[row-id]');
|
|
24
|
+
if (!row)
|
|
25
|
+
return;
|
|
26
|
+
const colId = colDef.colId;
|
|
27
|
+
if (!colId)
|
|
28
|
+
return;
|
|
29
|
+
const clickInput = () => {
|
|
30
|
+
const cell = row.querySelector(`[col-id='${colId}']`);
|
|
31
|
+
if (!cell)
|
|
32
|
+
return;
|
|
33
|
+
const input = cell.querySelector('input, button');
|
|
34
|
+
if (!input)
|
|
35
|
+
return;
|
|
36
|
+
input?.dispatchEvent(event);
|
|
37
|
+
};
|
|
38
|
+
setTimeout(clickInput, 20);
|
|
39
|
+
};
|
|
40
|
+
|
|
9
41
|
/**
|
|
10
42
|
* If loading is true this returns a loading spinner, otherwise it returns its children.
|
|
11
43
|
*/
|
|
12
44
|
const ComponentLoadingWrapper = (props) => {
|
|
13
|
-
return props.loading ? (jsx(LuiMiniSpinner, { size: 22, divProps: { role:
|
|
45
|
+
return props.loading ? (jsx(LuiMiniSpinner, { size: 22, divProps: { role: 'status', ['aria-label']: 'Loading', style: { padding: 16 } } })) : (jsxs("div", { style: { pointerEvents: props.saving ? 'none' : 'inherit' }, className: props.className, children: [props.saving && jsx("div", { className: 'ComponentLoadingWrapper-saveOverlay' }), props.children] }));
|
|
14
46
|
};
|
|
15
47
|
|
|
16
48
|
function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
|
|
@@ -59,153 +91,160 @@ const GridContext = createContext({
|
|
|
59
91
|
gridReady: false,
|
|
60
92
|
gridRenderState: () => null,
|
|
61
93
|
getColDef: () => {
|
|
62
|
-
console.error(
|
|
94
|
+
console.error('no context provider for getColDef');
|
|
63
95
|
return undefined;
|
|
64
96
|
},
|
|
65
97
|
getColumns: () => {
|
|
66
|
-
console.error(
|
|
98
|
+
console.error('no context provider for getColumns');
|
|
67
99
|
return [];
|
|
68
100
|
},
|
|
69
101
|
getColumnIds: () => {
|
|
70
|
-
console.error(
|
|
102
|
+
console.error('no context provider for getColumnIds');
|
|
71
103
|
return [];
|
|
72
104
|
},
|
|
73
105
|
invisibleColumnIds: undefined,
|
|
74
106
|
setInvisibleColumnIds: () => {
|
|
75
|
-
console.error(
|
|
107
|
+
console.error('no context provider for setInvisibleColumnIds');
|
|
76
108
|
},
|
|
77
109
|
prePopupOps: () => {
|
|
78
|
-
console.error(
|
|
110
|
+
console.error('no context provider for prePopupOps');
|
|
79
111
|
},
|
|
80
112
|
postPopupOps: () => {
|
|
81
|
-
console.error(
|
|
113
|
+
console.error('no context provider for prePopupOps');
|
|
82
114
|
},
|
|
83
115
|
externallySelectedItemsAreInSync: false,
|
|
84
116
|
setApis: () => {
|
|
85
|
-
console.error(
|
|
117
|
+
console.error('no context provider for setApis');
|
|
86
118
|
},
|
|
87
119
|
setQuickFilter: () => {
|
|
88
|
-
console.error(
|
|
120
|
+
console.error('no context provider for setQuickFilter');
|
|
89
121
|
},
|
|
90
122
|
selectRowsById: () => {
|
|
91
|
-
console.error(
|
|
123
|
+
console.error('no context provider for selectRows');
|
|
92
124
|
},
|
|
93
125
|
getSelectedRows: () => {
|
|
94
|
-
console.error(
|
|
126
|
+
console.error('no context provider for getSelectedRows');
|
|
95
127
|
return [];
|
|
96
128
|
},
|
|
97
129
|
getFilteredSelectedRows: () => {
|
|
98
|
-
console.error(
|
|
130
|
+
console.error('no context provider for getFilteredSelectedRows');
|
|
99
131
|
return [];
|
|
100
132
|
},
|
|
101
133
|
getSelectedRowIds: () => {
|
|
102
|
-
console.error(
|
|
134
|
+
console.error('no context provider for getSelectedRowIds');
|
|
103
135
|
return [];
|
|
104
136
|
},
|
|
105
137
|
getFilteredSelectedRowIds: () => {
|
|
106
|
-
console.error(
|
|
138
|
+
console.error('no context provider for getFilteredSelectedRowIds');
|
|
107
139
|
return [];
|
|
108
140
|
},
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
109
142
|
selectRowsDiff: async () => {
|
|
110
|
-
console.error(
|
|
143
|
+
console.error('no context provider for selectRowsDiff');
|
|
111
144
|
},
|
|
112
145
|
selectRowsByIdWithFlash: () => {
|
|
113
|
-
console.error(
|
|
146
|
+
console.error('no context provider for selectRowsWithFlash');
|
|
114
147
|
},
|
|
148
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
115
149
|
selectRowsWithFlashDiff: async () => {
|
|
116
|
-
console.error(
|
|
150
|
+
console.error('no context provider for selectRowsWithFlashDiff');
|
|
117
151
|
},
|
|
118
152
|
flashRows: () => {
|
|
119
|
-
console.error(
|
|
153
|
+
console.error('no context provider for flashRows');
|
|
120
154
|
},
|
|
155
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
121
156
|
flashRowsDiff: async () => {
|
|
122
|
-
console.error(
|
|
157
|
+
console.error('no context provider for flashRows');
|
|
123
158
|
},
|
|
159
|
+
// eslint-disable-next-line @typescript-eslint/require-await,@typescript-eslint/no-misused-promises
|
|
124
160
|
focusByRowById: async () => {
|
|
125
|
-
console.error(
|
|
161
|
+
console.error('no context provider for focusByRowById');
|
|
126
162
|
},
|
|
127
163
|
ensureRowVisible: () => {
|
|
128
|
-
console.error(
|
|
164
|
+
console.error('no context provider for ensureRowVisible');
|
|
129
165
|
return true;
|
|
130
166
|
},
|
|
131
167
|
ensureSelectedRowIsVisible: () => {
|
|
132
|
-
console.error(
|
|
168
|
+
console.error('no context provider for ensureSelectedRowIsVisible');
|
|
133
169
|
},
|
|
134
170
|
getFirstRowId: () => {
|
|
135
|
-
console.error(
|
|
171
|
+
console.error('no context provider for getFirstRowId');
|
|
136
172
|
return -1;
|
|
137
173
|
},
|
|
138
174
|
autoSizeColumns: () => {
|
|
139
|
-
console.error(
|
|
175
|
+
console.error('no context provider for autoSizeColumns');
|
|
140
176
|
return null;
|
|
141
177
|
},
|
|
142
178
|
sizeColumnsToFit: () => {
|
|
143
|
-
console.error(
|
|
179
|
+
console.error('no context provider for autoSizeAllColumns');
|
|
144
180
|
return null;
|
|
145
181
|
},
|
|
146
182
|
editingCells: () => {
|
|
147
|
-
console.error(
|
|
183
|
+
console.error('no context provider for editingCells');
|
|
148
184
|
return false;
|
|
149
185
|
},
|
|
150
186
|
cancelEdit: () => {
|
|
151
|
-
console.error(
|
|
187
|
+
console.error('no context provider for cancelEdit');
|
|
152
188
|
},
|
|
153
189
|
startCellEditing: () => {
|
|
154
|
-
console.error(
|
|
190
|
+
console.error('no context provider for startCellEditing');
|
|
155
191
|
},
|
|
156
192
|
stopEditing: () => {
|
|
157
|
-
console.error(
|
|
193
|
+
console.error('no context provider for stopEditing');
|
|
158
194
|
},
|
|
195
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
159
196
|
updatingCells: async () => {
|
|
160
|
-
console.error(
|
|
197
|
+
console.error('no context provider for modifyUpdating');
|
|
161
198
|
return false;
|
|
162
199
|
},
|
|
163
200
|
redrawRows: () => {
|
|
164
|
-
console.error(
|
|
201
|
+
console.error('no context provider for redrawRows');
|
|
165
202
|
},
|
|
166
203
|
setExternallySelectedItemsAreInSync: () => {
|
|
167
|
-
console.error(
|
|
204
|
+
console.error('no context provider for setExternallySelectedItemsAreInSync');
|
|
168
205
|
},
|
|
206
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
169
207
|
waitForExternallySelectedItemsToBeInSync: async () => {
|
|
170
|
-
console.error(
|
|
208
|
+
console.error('no context provider for waitForExternallySelectedItemsToBeInSync');
|
|
171
209
|
},
|
|
172
210
|
addExternalFilter: () => {
|
|
173
|
-
console.error(
|
|
211
|
+
console.error('no context provider for addExternalFilter');
|
|
174
212
|
},
|
|
175
213
|
removeExternalFilter: () => {
|
|
176
|
-
console.error(
|
|
214
|
+
console.error('no context provider for removeExternalFilter');
|
|
177
215
|
},
|
|
178
216
|
isExternalFilterPresent: () => {
|
|
179
|
-
console.error(
|
|
217
|
+
console.error('no context provider for isExternalFilterPresent');
|
|
180
218
|
return false;
|
|
181
219
|
},
|
|
182
220
|
doesExternalFilterPass: () => {
|
|
183
|
-
console.error(
|
|
221
|
+
console.error('no context provider for doesExternalFilterPass');
|
|
184
222
|
return true;
|
|
185
223
|
},
|
|
186
224
|
downloadCsv: () => {
|
|
187
|
-
console.error(
|
|
225
|
+
console.error('no context provider for downloadCsv');
|
|
188
226
|
},
|
|
189
227
|
setOnCellEditingComplete: () => {
|
|
190
|
-
console.error(
|
|
228
|
+
console.error('no context provider for setOnCellEditingComplete');
|
|
191
229
|
},
|
|
192
230
|
showNoRowsOverlay: () => {
|
|
193
|
-
console.error(
|
|
231
|
+
console.error('no context provider for showLoadingOverlay');
|
|
194
232
|
},
|
|
195
233
|
});
|
|
196
234
|
const useGridContext = () => useContext(GridContext);
|
|
197
235
|
|
|
198
236
|
const GridUpdatingContext = createContext({
|
|
199
237
|
checkUpdating: () => {
|
|
200
|
-
console.error(
|
|
238
|
+
console.error('Missing GridUpdatingContext');
|
|
201
239
|
return false;
|
|
202
240
|
},
|
|
203
241
|
updatingCols: () => {
|
|
204
|
-
console.error(
|
|
242
|
+
console.error('Missing GridUpdatingContext');
|
|
205
243
|
return [];
|
|
206
244
|
},
|
|
245
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
207
246
|
modifyUpdating: async () => {
|
|
208
|
-
console.error(
|
|
247
|
+
console.error('Missing GridUpdatingContext');
|
|
209
248
|
},
|
|
210
249
|
updatedDep: 0,
|
|
211
250
|
});
|
|
@@ -247,196 +286,19 @@ const hasParentClass = function (className, child) {
|
|
|
247
286
|
return false;
|
|
248
287
|
};
|
|
249
288
|
const stringByteLengthIsInvalid = (str, maxBytes) => new TextEncoder().encode(str).length > maxBytes;
|
|
250
|
-
const fnOrVar = (fn, param) => (typeof fn ===
|
|
289
|
+
const fnOrVar = (fn, param) => (typeof fn === 'function' ? fn(param) : fn);
|
|
251
290
|
const sanitiseFileName = (filename) => {
|
|
252
291
|
const valid = filename
|
|
253
292
|
.trim()
|
|
254
|
-
.replaceAll(/(\/|\\)+/g,
|
|
255
|
-
.replaceAll(/\s+/g,
|
|
256
|
-
.replaceAll(/[^\w\-āēīōūĀĒĪŌŪ.]/g,
|
|
257
|
-
const parts = valid.split(
|
|
293
|
+
.replaceAll(/(\/|\\)+/g, '-')
|
|
294
|
+
.replaceAll(/\s+/g, '_')
|
|
295
|
+
.replaceAll(/[^\w\-āēīōūĀĒĪŌŪ.]/g, '');
|
|
296
|
+
const parts = valid.split('.');
|
|
258
297
|
const fileExt = parts.length > 1 ? parts.pop() : undefined;
|
|
259
298
|
// Arbitrary max filename length of 64 chars + extension
|
|
260
299
|
if (!fileExt)
|
|
261
300
|
return valid.slice().slice(0, 64);
|
|
262
|
-
return valid.slice(0, -fileExt.length - 1).slice(0, 64) +
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
const GridLoadingOverlayComponentFr = (props, externalRef) => (jsx("div", { ref: externalRef, style: {
|
|
266
|
-
left: 0,
|
|
267
|
-
top: 0,
|
|
268
|
-
bottom: 0,
|
|
269
|
-
right: 0,
|
|
270
|
-
position: "absolute",
|
|
271
|
-
backgroundColor: "rgba(255,255,255,0.5)",
|
|
272
|
-
}, children: jsx("div", { style: { height: "100%", position: "relative" }, children: jsx("div", { style: { position: "absolute", left: 0, top: props.headerRowHeight, right: 0, bottom: 0 }, children: jsx(LuiStatusSpinner, {}) }) }) }));
|
|
273
|
-
const GridLoadingOverlayComponent = forwardRef(GridLoadingOverlayComponentFr);
|
|
274
|
-
const GridNoRowsOverlayFr = (props, externalRef) => {
|
|
275
|
-
if (props.loading) {
|
|
276
|
-
return jsx(GridLoadingOverlayComponent, { ref: externalRef, headerRowHeight: props.headerRowHeight });
|
|
277
|
-
}
|
|
278
|
-
if (props.rowCount === 0) {
|
|
279
|
-
return jsx("div", { ref: externalRef, children: props.noRowsOverlayText ?? "There are currently no rows" });
|
|
280
|
-
}
|
|
281
|
-
if (props.filteredRowCount === 0) {
|
|
282
|
-
return jsx("div", { ref: externalRef, children: props.noRowsMatchingOverlayText ?? "All rows have been filtered" });
|
|
283
|
-
}
|
|
284
|
-
return jsx("div", { ref: externalRef });
|
|
285
|
-
};
|
|
286
|
-
const GridNoRowsOverlay = forwardRef(GridNoRowsOverlayFr);
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Retains last sort order from via <AgGrid postRowSort>.
|
|
290
|
-
* Then applies this sort until next sort column change.
|
|
291
|
-
* Handles stale sort, when you edit a row but don't want to re-sort.
|
|
292
|
-
*/
|
|
293
|
-
const usePostSortRowsHook = ({ setStaleGrid }) => {
|
|
294
|
-
const { redrawRows } = useContext(GridContext);
|
|
295
|
-
// On first run we need to init the first backed up sort order
|
|
296
|
-
const initialised = useRef(false);
|
|
297
|
-
// Used to detect sort order has changed since last run
|
|
298
|
-
const lastSortOrderHash = useRef("");
|
|
299
|
-
// Used to detect if sort order was the same, has the direction changed
|
|
300
|
-
const previousRowSortIndexRef = useRef({});
|
|
301
|
-
// stale sort is when there's a sort and user edits a row
|
|
302
|
-
// this applies a class to the div wrapping the grid which in turn adds a * beside the sort arrow
|
|
303
|
-
const sortWasStale = useRef(false);
|
|
304
|
-
// Previous col sort is stored and reapplied if the sort direction changes on the same column and the sort was stale
|
|
305
|
-
// As a sort on a stale row should just resort, not change sort direction
|
|
306
|
-
const previousColSort = useRef([]);
|
|
307
|
-
const previousQuickFilter = useRef("");
|
|
308
|
-
return useCallback(({ api, nodes }) => {
|
|
309
|
-
// Grid is destroyed
|
|
310
|
-
if (!api)
|
|
311
|
-
return;
|
|
312
|
-
const previousRowSortIndex = previousRowSortIndexRef.current;
|
|
313
|
-
const hashNode = (node) => {
|
|
314
|
-
return node ? JSON.stringify(node.data) : "";
|
|
315
|
-
};
|
|
316
|
-
const copyCurrentSortSettings = () => api.getColumnState().map((row) => ({ colId: row.colId, sort: row.sort, sortIndex: row.sortIndex }));
|
|
317
|
-
const backupSortOrder = () => {
|
|
318
|
-
for (const x in previousRowSortIndex)
|
|
319
|
-
delete previousRowSortIndex[x];
|
|
320
|
-
nodes.forEach((node, index) => (previousRowSortIndex[`${node.data.id}`] = { index, hash: hashNode(node) }));
|
|
321
|
-
};
|
|
322
|
-
// Check if column is the first sorted column. Note: column is preconfigured to sort its sortIndex is null not 1
|
|
323
|
-
const isFirstSortColumn = (row) => row.sort && [0, null].includes(row.sortIndex ?? null);
|
|
324
|
-
const isSameColumnAndDifferentSort = (col1, col2) => col1 && col2 && col1.colId === col2.colId && col1.sort != col2.sort;
|
|
325
|
-
const restorePreviousSortColumnState = () => api.applyColumnState({ state: previousColSort.current });
|
|
326
|
-
const sortNodesByPreviousSort = () => {
|
|
327
|
-
nodes.sort((a, b) => (previousRowSortIndex[`${a.data.id}`]?.index ?? Number.MAX_SAFE_INTEGER) -
|
|
328
|
-
(previousRowSortIndex[`${b.data.id}`]?.index ?? Number.MAX_SAFE_INTEGER));
|
|
329
|
-
};
|
|
330
|
-
// On first load copy the current sort
|
|
331
|
-
if (!initialised.current) {
|
|
332
|
-
initialised.current = true;
|
|
333
|
-
previousColSort.current = copyCurrentSortSettings();
|
|
334
|
-
}
|
|
335
|
-
const newSortOrder = JSON.stringify(copyCurrentSortSettings());
|
|
336
|
-
let sortOrderChanged = newSortOrder != lastSortOrderHash.current;
|
|
337
|
-
const quickFilter = api.getQuickFilter();
|
|
338
|
-
if (previousQuickFilter.current != quickFilter) {
|
|
339
|
-
previousQuickFilter.current = quickFilter ?? "";
|
|
340
|
-
sortOrderChanged = true;
|
|
341
|
-
}
|
|
342
|
-
if (isEmpty(previousRowSortIndex)) {
|
|
343
|
-
backupSortOrder();
|
|
344
|
-
}
|
|
345
|
-
if (sortOrderChanged) {
|
|
346
|
-
const thisFirstCol = copyCurrentSortSettings().find(isFirstSortColumn);
|
|
347
|
-
const previousFirstCol = previousColSort.current.find(isFirstSortColumn);
|
|
348
|
-
// Change to sort can be: (in the context of stale sort do...)
|
|
349
|
-
// - no sort to sorted (do nothing)
|
|
350
|
-
// - asc to desc (resort as asc, and remove stale sort)
|
|
351
|
-
// - desc to no sort (resort as desc, and remove stale sort)
|
|
352
|
-
const wasSortChangedOnSameColumn = previousFirstCol && (!thisFirstCol || isSameColumnAndDifferentSort(previousFirstCol, thisFirstCol));
|
|
353
|
-
// if sort was stale, and we're here, someone has clicked a stale sort order (^*) in the column header
|
|
354
|
-
if (sortWasStale.current && wasSortChangedOnSameColumn) {
|
|
355
|
-
// Sort was an existing priority 1 column
|
|
356
|
-
// We want to re-sort with the old sort direction and clear stale sort
|
|
357
|
-
sortWasStale.current = false;
|
|
358
|
-
setStaleGrid(false);
|
|
359
|
-
// trigger a recapture of sort order
|
|
360
|
-
lastSortOrderHash.current = "";
|
|
361
|
-
restorePreviousSortColumnState();
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
364
|
-
// Sort was on a different column so clear sort and sort like normal
|
|
365
|
-
if (sortWasStale.current) {
|
|
366
|
-
sortWasStale.current = false;
|
|
367
|
-
setStaleGrid(false);
|
|
368
|
-
}
|
|
369
|
-
// Columns were sorted so retain the sort
|
|
370
|
-
backupSortOrder();
|
|
371
|
-
lastSortOrderHash.current = newSortOrder;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
else {
|
|
375
|
-
let firstChangedNodeIndex = -1;
|
|
376
|
-
let lastNewNode = undefined;
|
|
377
|
-
let changedRowCount = 0;
|
|
378
|
-
let newRowCount = 0;
|
|
379
|
-
let index = 0;
|
|
380
|
-
for (const node of nodes) {
|
|
381
|
-
const psr = previousRowSortIndex[`${node.data.id}`];
|
|
382
|
-
if (psr) {
|
|
383
|
-
if (psr.hash != hashNode(node)) {
|
|
384
|
-
if (firstChangedNodeIndex === -1)
|
|
385
|
-
firstChangedNodeIndex = index;
|
|
386
|
-
changedRowCount++;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
else {
|
|
390
|
-
lastNewNode = node;
|
|
391
|
-
newRowCount++;
|
|
392
|
-
}
|
|
393
|
-
index++;
|
|
394
|
-
}
|
|
395
|
-
let wasStale = false;
|
|
396
|
-
if (changedRowCount === 0 && newRowCount === 1) {
|
|
397
|
-
// insert new row at end
|
|
398
|
-
const newIndex = index - 1;
|
|
399
|
-
previousRowSortIndex[`${lastNewNode?.data.id}`] = { index: newIndex, hash: hashNode(lastNewNode) };
|
|
400
|
-
wasStale = true;
|
|
401
|
-
}
|
|
402
|
-
else if (changedRowCount === 2 && newRowCount === 0) {
|
|
403
|
-
// This must be a swap rows (sometimes it's not, needs further attention)
|
|
404
|
-
backupSortOrder();
|
|
405
|
-
wasStale = false;
|
|
406
|
-
}
|
|
407
|
-
else if (changedRowCount > 1 && newRowCount === 1) {
|
|
408
|
-
// This must be a insert so, insert new row near the row that changed
|
|
409
|
-
previousRowSortIndex[`${lastNewNode?.data.id}`] = {
|
|
410
|
-
index: firstChangedNodeIndex + 0.5,
|
|
411
|
-
hash: hashNode(lastNewNode),
|
|
412
|
-
};
|
|
413
|
-
wasStale = true;
|
|
414
|
-
// For some reason AgGrid mis-positions the inserted row.
|
|
415
|
-
lastNewNode && redrawRows();
|
|
416
|
-
}
|
|
417
|
-
else if (changedRowCount == 1 && newRowCount === 0) {
|
|
418
|
-
// User edited one row so, do nothing, retain sort
|
|
419
|
-
wasStale = true;
|
|
420
|
-
}
|
|
421
|
-
else if (changedRowCount !== 0 || newRowCount != 0) {
|
|
422
|
-
// too many rows changed, resort
|
|
423
|
-
backupSortOrder();
|
|
424
|
-
}
|
|
425
|
-
if (wasStale) {
|
|
426
|
-
// Check if the sort order the aggrid passed matches our stale sort order
|
|
427
|
-
const stillStale = Object.keys(previousRowSortIndex).length != nodes.length ||
|
|
428
|
-
nodes.some((node, index) => previousRowSortIndex[`${node.data.id}`]?.index !== index);
|
|
429
|
-
// If we haven't already processed a stale sort then...
|
|
430
|
-
if (stillStale && !sortWasStale.current) {
|
|
431
|
-
// backup sort state, so we can restore it when sort is clicked on a stale column
|
|
432
|
-
previousColSort.current = copyCurrentSortSettings();
|
|
433
|
-
sortWasStale.current = true;
|
|
434
|
-
setStaleGrid(true);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
sortNodesByPreviousSort();
|
|
438
|
-
}
|
|
439
|
-
}, [redrawRows, setStaleGrid]);
|
|
301
|
+
return valid.slice(0, -fileExt.length - 1).slice(0, 64) + '.' + fileExt;
|
|
440
302
|
};
|
|
441
303
|
|
|
442
304
|
/**
|
|
@@ -452,9 +314,9 @@ const GridHeaderSelect = ({ api }) => {
|
|
|
452
314
|
const clickHandler = () => {
|
|
453
315
|
setUpdateCounter(updateCounter + 1);
|
|
454
316
|
};
|
|
455
|
-
api.addEventListener(
|
|
317
|
+
api.addEventListener('selectionChanged', clickHandler);
|
|
456
318
|
return () => {
|
|
457
|
-
!api.isDestroyed() && api.removeEventListener(
|
|
319
|
+
!api.isDestroyed() && api.removeEventListener('selectionChanged', clickHandler);
|
|
458
320
|
};
|
|
459
321
|
}, [api, updateCounter]);
|
|
460
322
|
const handleMultiSelect = () => {
|
|
@@ -468,11 +330,23 @@ const GridHeaderSelect = ({ api }) => {
|
|
|
468
330
|
const totalNodeCount = api.getDisplayedRowCount();
|
|
469
331
|
const partialSelect = selectedNodeCount != 0 && selectedNodeCount != totalNodeCount;
|
|
470
332
|
const allSelected = selectedNodeCount != 0 && selectedNodeCount == totalNodeCount;
|
|
471
|
-
return (jsx("div", { className: clsx(
|
|
333
|
+
return (jsx("div", { className: clsx('ag-wrapper ag-input-wrapper ag-checkbox-input-wrapper', partialSelect && 'ag-indeterminate', allSelected && 'ag-checked'), onClick: handleMultiSelect, children: jsx("input", { type: "checkbox", className: 'ag-checkbox-input-wrapper', onChange: () => {
|
|
472
334
|
/* do nothing */
|
|
473
335
|
} }) }));
|
|
474
336
|
};
|
|
475
337
|
|
|
338
|
+
const EventHandlersContext = createContext({
|
|
339
|
+
handleClick: () => { },
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const ItemSettingsContext = createContext({
|
|
343
|
+
submenuOpenDelay: 0,
|
|
344
|
+
submenuCloseDelay: 0,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// FIXME hacking a default context in here is probably bad, but the context is mess
|
|
348
|
+
const SettingsContext = createContext({});
|
|
349
|
+
|
|
476
350
|
// Generate className following BEM methodology: http://getbem.com/naming/
|
|
477
351
|
// Modifier value can be one of the following types: boolean, string, undefined
|
|
478
352
|
const useBEM = ({ block, element, modifiers, className }) => useMemo(() => {
|
|
@@ -484,8 +358,8 @@ const useBEM = ({ block, element, modifiers, className }) => useMemo(() => {
|
|
|
484
358
|
if (value)
|
|
485
359
|
classString += ` ${blockElement}--${value === true ? name : `${name}-${value}`}`;
|
|
486
360
|
});
|
|
487
|
-
let expandedClassName = typeof className ===
|
|
488
|
-
if (typeof expandedClassName ===
|
|
361
|
+
let expandedClassName = typeof className === 'function' ? className(modifiers) : className;
|
|
362
|
+
if (typeof expandedClassName === 'string') {
|
|
489
363
|
expandedClassName = expandedClassName.trim();
|
|
490
364
|
if (expandedClassName)
|
|
491
365
|
classString += ` ${expandedClassName}`;
|
|
@@ -496,7 +370,7 @@ const useBEM = ({ block, element, modifiers, className }) => useMemo(() => {
|
|
|
496
370
|
// Adapted from material-ui
|
|
497
371
|
// https://github.com/mui-org/material-ui/blob/f996027d00e7e4bff3fc040786c1706f9c6c3f82/packages/material-ui-utils/src/useForkRef.ts
|
|
498
372
|
const setRef = (ref, instance) => {
|
|
499
|
-
if (typeof ref ===
|
|
373
|
+
if (typeof ref === 'function') {
|
|
500
374
|
ref(instance);
|
|
501
375
|
}
|
|
502
376
|
else if (ref) {
|
|
@@ -516,33 +390,50 @@ const useCombinedRef = (refA, refB) => {
|
|
|
516
390
|
// https://github.com/reduxjs/react-redux/blob/b48d087d76f666e1c6c5a9713bbec112a1631841/src/utils/useIsomorphicLayoutEffect.js#L12
|
|
517
391
|
// https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85
|
|
518
392
|
// https://github.com/facebook/react/issues/14927#issuecomment-549457471
|
|
519
|
-
const useIsomorphicLayoutEffect = typeof window !==
|
|
520
|
-
typeof window.document !==
|
|
521
|
-
typeof window.document.createElement !==
|
|
393
|
+
const useIsomorphicLayoutEffect = typeof window !== 'undefined' &&
|
|
394
|
+
typeof window.document !== 'undefined' &&
|
|
395
|
+
typeof window.document.createElement !== 'undefined'
|
|
522
396
|
? useLayoutEffect
|
|
523
397
|
: useEffect;
|
|
524
398
|
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
const
|
|
399
|
+
const useItemEffect = (isDisabled, menuItemRef, updateItems) => {
|
|
400
|
+
useIsomorphicLayoutEffect(() => {
|
|
401
|
+
if (!menuItemRef)
|
|
402
|
+
return;
|
|
403
|
+
if (process.env.NODE_ENV !== 'production' && !updateItems) {
|
|
404
|
+
throw new Error(`[React-Menu] This menu item or submenu should be rendered under a menu: ${menuItemRef.current.outerHTML}`);
|
|
405
|
+
}
|
|
406
|
+
if (isDisabled)
|
|
407
|
+
return;
|
|
408
|
+
const item = menuItemRef.current;
|
|
409
|
+
updateItems(item, true);
|
|
410
|
+
return () => {
|
|
411
|
+
updateItems(item);
|
|
412
|
+
};
|
|
413
|
+
}, [isDisabled, menuItemRef, updateItems]);
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
const menuContainerClass = 'szh-menu-container';
|
|
417
|
+
const menuClass = 'szh-menu';
|
|
418
|
+
const menuButtonClass = 'szh-menu-button';
|
|
419
|
+
const menuArrowClass = 'arrow';
|
|
420
|
+
const menuItemClass = 'item';
|
|
421
|
+
const menuDividerClass = 'divider';
|
|
422
|
+
const menuHeaderClass = 'header';
|
|
423
|
+
const menuGroupClass = 'group';
|
|
424
|
+
const subMenuClass = 'submenu';
|
|
425
|
+
const radioGroupClass = 'radio-group';
|
|
535
426
|
const Keys = Object.freeze({
|
|
536
|
-
ENTER:
|
|
537
|
-
TAB:
|
|
538
|
-
ESC:
|
|
539
|
-
SPACE:
|
|
540
|
-
HOME:
|
|
541
|
-
END:
|
|
542
|
-
LEFT:
|
|
543
|
-
RIGHT:
|
|
544
|
-
UP:
|
|
545
|
-
DOWN:
|
|
427
|
+
ENTER: 'Enter',
|
|
428
|
+
TAB: 'Tab',
|
|
429
|
+
ESC: 'Escape',
|
|
430
|
+
SPACE: ' ',
|
|
431
|
+
HOME: 'Home',
|
|
432
|
+
END: 'End',
|
|
433
|
+
LEFT: 'ArrowLeft',
|
|
434
|
+
RIGHT: 'ArrowRight',
|
|
435
|
+
UP: 'ArrowUp',
|
|
436
|
+
DOWN: 'ArrowDown',
|
|
546
437
|
});
|
|
547
438
|
const HoverActionTypes = Object.freeze({
|
|
548
439
|
RESET: 0,
|
|
@@ -555,39 +446,39 @@ const HoverActionTypes = Object.freeze({
|
|
|
555
446
|
SET_INDEX: 7,
|
|
556
447
|
});
|
|
557
448
|
const CloseReason = Object.freeze({
|
|
558
|
-
CLICK:
|
|
559
|
-
CANCEL:
|
|
560
|
-
BLUR:
|
|
561
|
-
SCROLL:
|
|
562
|
-
TAB_FORWARD:
|
|
563
|
-
TAB_BACKWARD:
|
|
449
|
+
CLICK: 'click',
|
|
450
|
+
CANCEL: 'cancel',
|
|
451
|
+
BLUR: 'blur',
|
|
452
|
+
SCROLL: 'scroll',
|
|
453
|
+
TAB_FORWARD: 'tab_forward',
|
|
454
|
+
TAB_BACKWARD: 'tab_backward',
|
|
564
455
|
});
|
|
565
456
|
const FocusPositions = Object.freeze({
|
|
566
|
-
FIRST:
|
|
567
|
-
LAST:
|
|
457
|
+
FIRST: 'first',
|
|
458
|
+
LAST: 'last',
|
|
568
459
|
});
|
|
569
460
|
const MenuStateMap = Object.freeze({
|
|
570
|
-
entering:
|
|
571
|
-
entered:
|
|
572
|
-
exiting:
|
|
573
|
-
exited:
|
|
461
|
+
entering: 'opening',
|
|
462
|
+
entered: 'open',
|
|
463
|
+
exiting: 'closing',
|
|
464
|
+
exited: 'closed',
|
|
574
465
|
});
|
|
575
466
|
|
|
576
|
-
const isMenuOpen = (state) => !!state && state[0] ===
|
|
467
|
+
const isMenuOpen = (state) => !!state && state[0] === 'o';
|
|
577
468
|
const batchedUpdates = unstable_batchedUpdates || ((callback) => callback());
|
|
578
469
|
const floatEqual = (a, b, diff = 0.0001) => Math.abs(a - b) < diff;
|
|
579
470
|
const getTransition = (transition, name) => transition === true || !!(transition && transition[name]);
|
|
580
471
|
function safeCall(fn, arg) {
|
|
581
|
-
return typeof fn ===
|
|
472
|
+
return typeof fn === 'function' ? fn(arg) : fn;
|
|
582
473
|
}
|
|
583
|
-
const internalKey =
|
|
474
|
+
const internalKey = '_szhsinMenu';
|
|
584
475
|
const getName = (component) => component[internalKey];
|
|
585
476
|
const mergeProps = (target, source) => {
|
|
586
477
|
source &&
|
|
587
478
|
Object.keys(source).forEach((key) => {
|
|
588
479
|
const targetProp = target[key];
|
|
589
480
|
const sourceProp = source[key];
|
|
590
|
-
if (typeof sourceProp ===
|
|
481
|
+
if (typeof sourceProp === 'function' && targetProp) {
|
|
591
482
|
target[key] = (...arg) => {
|
|
592
483
|
sourceProp(...arg);
|
|
593
484
|
targetProp(...arg);
|
|
@@ -629,24 +520,24 @@ const getScrollAncestor = (node) => {
|
|
|
629
520
|
};
|
|
630
521
|
function commonProps(isDisabled, isHovering) {
|
|
631
522
|
return {
|
|
632
|
-
|
|
523
|
+
'aria-disabled': isDisabled || undefined,
|
|
633
524
|
tabIndex: isHovering ? 0 : -1,
|
|
634
525
|
};
|
|
635
526
|
}
|
|
636
527
|
const indexOfNode = (nodeList, node) => findIndex(nodeList, node);
|
|
637
528
|
const focusFirstInput = (container) => {
|
|
638
529
|
// We can't use instanceof Element in portals, so I use querySelectorAll as a proxy here
|
|
639
|
-
if (!container || !(
|
|
530
|
+
if (!container || !('querySelectorAll' in container))
|
|
640
531
|
return false;
|
|
641
532
|
const inputs = container.querySelectorAll("input[type='text'],input:not([type]),textarea");
|
|
642
533
|
const input = inputs[0];
|
|
643
534
|
// Using focus as proxy for HTMLElement
|
|
644
|
-
if (!input || !(
|
|
535
|
+
if (!input || !('focus' in input))
|
|
645
536
|
return false;
|
|
646
537
|
input.focus();
|
|
647
538
|
// Text areas should start at end
|
|
648
539
|
// this is a proxy for instanceof HTMLTextAreaElement
|
|
649
|
-
if ([
|
|
540
|
+
if (['textarea', 'text'].includes(input.type)) {
|
|
650
541
|
input.setSelectionRange(0, input.value.length);
|
|
651
542
|
}
|
|
652
543
|
return true;
|
|
@@ -698,7 +589,7 @@ const useItems = (menuRef, focusRef) => {
|
|
|
698
589
|
const sortItems = () => {
|
|
699
590
|
if (mutableState.sorted)
|
|
700
591
|
return;
|
|
701
|
-
const orderedNodes = menuRef.current.querySelectorAll(
|
|
592
|
+
const orderedNodes = menuRef.current.querySelectorAll('.szh-menu__item');
|
|
702
593
|
items.sort((a, b) => indexOfNode(orderedNodes, a) - indexOfNode(orderedNodes, b));
|
|
703
594
|
mutableState.sorted = true;
|
|
704
595
|
};
|
|
@@ -725,12 +616,12 @@ const useItems = (menuRef, focusRef) => {
|
|
|
725
616
|
newItem = items[index];
|
|
726
617
|
break;
|
|
727
618
|
case HoverActionTypes.SET_INDEX:
|
|
728
|
-
if (typeof nextIndex !==
|
|
619
|
+
if (typeof nextIndex !== 'number')
|
|
729
620
|
break;
|
|
730
621
|
sortItems();
|
|
731
622
|
index = nextIndex;
|
|
732
623
|
newItem = items[index];
|
|
733
|
-
defer$1(() => newItem.scrollIntoView({ behavior:
|
|
624
|
+
defer$1(() => newItem.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }));
|
|
734
625
|
break;
|
|
735
626
|
case HoverActionTypes.INCREASE:
|
|
736
627
|
sortItems();
|
|
@@ -756,7 +647,7 @@ const useItems = (menuRef, focusRef) => {
|
|
|
756
647
|
break;
|
|
757
648
|
}
|
|
758
649
|
default:
|
|
759
|
-
if (process.env.NODE_ENV !==
|
|
650
|
+
if (process.env.NODE_ENV !== 'production')
|
|
760
651
|
throw new Error(`[React-Menu] Unknown hover action type: ${actionType}`);
|
|
761
652
|
}
|
|
762
653
|
if (!newItem && !newItemFn)
|
|
@@ -767,28 +658,6 @@ const useItems = (menuRef, focusRef) => {
|
|
|
767
658
|
return { hoverItem, dispatch, updateItems };
|
|
768
659
|
};
|
|
769
660
|
|
|
770
|
-
const useItemEffect = (isDisabled, menuItemRef, updateItems) => {
|
|
771
|
-
useIsomorphicLayoutEffect(() => {
|
|
772
|
-
if (!menuItemRef)
|
|
773
|
-
return;
|
|
774
|
-
if (process.env.NODE_ENV !== "production" && !updateItems) {
|
|
775
|
-
throw new Error(`[React-Menu] This menu item or submenu should be rendered under a menu: ${menuItemRef.current.outerHTML}`);
|
|
776
|
-
}
|
|
777
|
-
if (isDisabled)
|
|
778
|
-
return;
|
|
779
|
-
const item = menuItemRef.current;
|
|
780
|
-
updateItems(item, true);
|
|
781
|
-
return () => {
|
|
782
|
-
updateItems(item);
|
|
783
|
-
};
|
|
784
|
-
}, [isDisabled, menuItemRef, updateItems]);
|
|
785
|
-
};
|
|
786
|
-
|
|
787
|
-
const ItemSettingsContext = createContext({
|
|
788
|
-
submenuOpenDelay: 0,
|
|
789
|
-
submenuCloseDelay: 0,
|
|
790
|
-
});
|
|
791
|
-
|
|
792
661
|
const MenuListItemContext = createContext({
|
|
793
662
|
dispatch: () => { },
|
|
794
663
|
updateItems: () => { },
|
|
@@ -956,8 +825,8 @@ const useMenuState = (props) => {
|
|
|
956
825
|
mountOnEnter: !initialMounted,
|
|
957
826
|
unmountOnExit: unmountOnClose,
|
|
958
827
|
timeout: transitionTimeout ?? 500,
|
|
959
|
-
enter: getTransition(transition,
|
|
960
|
-
exit: getTransition(transition,
|
|
828
|
+
enter: getTransition(transition, 'open'),
|
|
829
|
+
exit: getTransition(transition, 'close'),
|
|
961
830
|
});
|
|
962
831
|
return [{ state: MenuStateMap[state.status], endTransition }, toggleMenu];
|
|
963
832
|
};
|
|
@@ -972,18 +841,6 @@ const useMenuStateAndFocus = (options) => {
|
|
|
972
841
|
return [{ menuItemFocus, ...menuProps }, toggleMenu, openMenu];
|
|
973
842
|
};
|
|
974
843
|
|
|
975
|
-
const MenuButton = forwardRef(function MenuButton({ className, isOpen, disabled, children, ...restProps }, ref) {
|
|
976
|
-
const modifiers = useMemo(() => ({ open: isOpen }), [isOpen]);
|
|
977
|
-
return (jsx("button", { "aria-haspopup": true, "aria-expanded": isOpen, "aria-disabled": disabled || undefined, type: "button", disabled: disabled, ...restProps, ref: ref, className: useBEM({ block: menuButtonClass, modifiers, className }), children: children }));
|
|
978
|
-
});
|
|
979
|
-
|
|
980
|
-
const EventHandlersContext = createContext({
|
|
981
|
-
handleClick: () => { },
|
|
982
|
-
});
|
|
983
|
-
|
|
984
|
-
// FIXME hacking a default context in here is probably bad, but the context is mess
|
|
985
|
-
const SettingsContext = createContext({});
|
|
986
|
-
|
|
987
844
|
const MenuListContext = createContext({});
|
|
988
845
|
|
|
989
846
|
const getPositionHelpers = (containerRef, menuRef, menuScroll, boundingBoxPadding) => {
|
|
@@ -1078,14 +935,14 @@ const positionContextMenu = ({ positionHelpers, anchorPoint, }) => {
|
|
|
1078
935
|
x = confineHorizontally(x);
|
|
1079
936
|
}
|
|
1080
937
|
// Similar logic to the left and right side above.
|
|
1081
|
-
let computedDirection =
|
|
938
|
+
let computedDirection = 'bottom';
|
|
1082
939
|
const bottomOverflow = getBottomOverflow(y);
|
|
1083
940
|
if (bottomOverflow > 0) {
|
|
1084
941
|
const adjustedY = y - menuRect.height;
|
|
1085
942
|
const topOverflow = getTopOverflow(adjustedY);
|
|
1086
943
|
if (topOverflow >= 0 || -topOverflow < bottomOverflow) {
|
|
1087
944
|
y = adjustedY;
|
|
1088
|
-
computedDirection =
|
|
945
|
+
computedDirection = 'top';
|
|
1089
946
|
}
|
|
1090
947
|
y = confineVertically(y);
|
|
1091
948
|
}
|
|
@@ -1104,18 +961,18 @@ const placeLeftorRight = (props) => {
|
|
|
1104
961
|
const { anchorRect, containerRect, menuRect, placeLeftorRightY, placeLeftX, placeRightX, getLeftOverflow, getRightOverflow, confineHorizontally, confineVertically, arrowRef, arrow, direction, position, } = props;
|
|
1105
962
|
let computedDirection = direction;
|
|
1106
963
|
let y = placeLeftorRightY;
|
|
1107
|
-
if (position !==
|
|
964
|
+
if (position !== 'initial') {
|
|
1108
965
|
y = confineVertically(y);
|
|
1109
|
-
if (position ===
|
|
966
|
+
if (position === 'anchor') {
|
|
1110
967
|
// restrict menu to the edge of anchor element
|
|
1111
968
|
y = Math.min(y, anchorRect.bottom - containerRect.top);
|
|
1112
969
|
y = Math.max(y, anchorRect.top - containerRect.top - menuRect.height);
|
|
1113
970
|
}
|
|
1114
971
|
}
|
|
1115
972
|
let x, leftOverflow, rightOverflow;
|
|
1116
|
-
if (computedDirection ===
|
|
973
|
+
if (computedDirection === 'left') {
|
|
1117
974
|
x = placeLeftX;
|
|
1118
|
-
if (position !==
|
|
975
|
+
if (position !== 'initial') {
|
|
1119
976
|
// if menu overflows to the left,
|
|
1120
977
|
// try to reposition it to the right of the anchor.
|
|
1121
978
|
leftOverflow = getLeftOverflow(x);
|
|
@@ -1125,7 +982,7 @@ const placeLeftorRight = (props) => {
|
|
|
1125
982
|
rightOverflow = getRightOverflow(placeRightX);
|
|
1126
983
|
if (rightOverflow <= 0 || -leftOverflow > rightOverflow) {
|
|
1127
984
|
x = placeRightX;
|
|
1128
|
-
computedDirection =
|
|
985
|
+
computedDirection = 'right';
|
|
1129
986
|
}
|
|
1130
987
|
}
|
|
1131
988
|
}
|
|
@@ -1133,18 +990,18 @@ const placeLeftorRight = (props) => {
|
|
|
1133
990
|
else {
|
|
1134
991
|
// Opposite logic to the 'left' direction above
|
|
1135
992
|
x = placeRightX;
|
|
1136
|
-
if (position !==
|
|
993
|
+
if (position !== 'initial') {
|
|
1137
994
|
rightOverflow = getRightOverflow(x);
|
|
1138
995
|
if (rightOverflow > 0) {
|
|
1139
996
|
leftOverflow = getLeftOverflow(placeLeftX);
|
|
1140
997
|
if (leftOverflow >= 0 || -leftOverflow < rightOverflow) {
|
|
1141
998
|
x = placeLeftX;
|
|
1142
|
-
computedDirection =
|
|
999
|
+
computedDirection = 'left';
|
|
1143
1000
|
}
|
|
1144
1001
|
}
|
|
1145
1002
|
}
|
|
1146
1003
|
}
|
|
1147
|
-
if (position ===
|
|
1004
|
+
if (position === 'auto')
|
|
1148
1005
|
x = confineHorizontally(x);
|
|
1149
1006
|
const arrowY = arrow
|
|
1150
1007
|
? placeArrowVertical({
|
|
@@ -1169,20 +1026,20 @@ const placeArrowHorizontal = (p) => {
|
|
|
1169
1026
|
const placeToporBottom = (props) => {
|
|
1170
1027
|
const { anchorRect, containerRect, menuRect, placeToporBottomX, placeTopY, placeBottomY, getTopOverflow, getBottomOverflow, confineHorizontally, confineVertically, arrowRef, arrow, direction, position, } = props;
|
|
1171
1028
|
// make sure invalid direction is treated as 'bottom'
|
|
1172
|
-
let computedDirection = direction ===
|
|
1029
|
+
let computedDirection = direction === 'top' ? 'top' : 'bottom';
|
|
1173
1030
|
let x = placeToporBottomX;
|
|
1174
|
-
if (position !==
|
|
1031
|
+
if (position !== 'initial') {
|
|
1175
1032
|
x = confineHorizontally(x);
|
|
1176
|
-
if (position ===
|
|
1033
|
+
if (position === 'anchor') {
|
|
1177
1034
|
// restrict menu to the edge of anchor element
|
|
1178
1035
|
x = Math.min(x, anchorRect.right - containerRect.left);
|
|
1179
1036
|
x = Math.max(x, anchorRect.left - containerRect.left - menuRect.width);
|
|
1180
1037
|
}
|
|
1181
1038
|
}
|
|
1182
1039
|
let y, topOverflow, bottomOverflow;
|
|
1183
|
-
if (computedDirection ===
|
|
1040
|
+
if (computedDirection === 'top') {
|
|
1184
1041
|
y = placeTopY;
|
|
1185
|
-
if (position !==
|
|
1042
|
+
if (position !== 'initial') {
|
|
1186
1043
|
// if menu overflows to the top,
|
|
1187
1044
|
// try to reposition it to the bottom of the anchor.
|
|
1188
1045
|
topOverflow = getTopOverflow(y);
|
|
@@ -1192,7 +1049,7 @@ const placeToporBottom = (props) => {
|
|
|
1192
1049
|
bottomOverflow = getBottomOverflow(placeBottomY);
|
|
1193
1050
|
if (bottomOverflow <= 0 || -topOverflow > bottomOverflow) {
|
|
1194
1051
|
y = placeBottomY;
|
|
1195
|
-
computedDirection =
|
|
1052
|
+
computedDirection = 'bottom';
|
|
1196
1053
|
}
|
|
1197
1054
|
}
|
|
1198
1055
|
}
|
|
@@ -1200,18 +1057,18 @@ const placeToporBottom = (props) => {
|
|
|
1200
1057
|
else {
|
|
1201
1058
|
// Opposite logic to the 'top' direction above
|
|
1202
1059
|
y = placeBottomY;
|
|
1203
|
-
if (position !==
|
|
1060
|
+
if (position !== 'initial') {
|
|
1204
1061
|
bottomOverflow = getBottomOverflow(y);
|
|
1205
1062
|
if (bottomOverflow > 0) {
|
|
1206
1063
|
topOverflow = getTopOverflow(placeTopY);
|
|
1207
1064
|
if (topOverflow >= 0 || -topOverflow < bottomOverflow) {
|
|
1208
1065
|
y = placeTopY;
|
|
1209
|
-
computedDirection =
|
|
1066
|
+
computedDirection = 'top';
|
|
1210
1067
|
}
|
|
1211
1068
|
}
|
|
1212
1069
|
}
|
|
1213
1070
|
}
|
|
1214
|
-
if (position ===
|
|
1071
|
+
if (position === 'auto')
|
|
1215
1072
|
y = confineVertically(y);
|
|
1216
1073
|
const arrowX = arrow
|
|
1217
1074
|
? placeArrowHorizontal({
|
|
@@ -1231,7 +1088,7 @@ const positionMenu = (props) => {
|
|
|
1231
1088
|
let horizontalOffset = offsetX;
|
|
1232
1089
|
let verticalOffset = offsetY;
|
|
1233
1090
|
if (arrow && arrowRef.current) {
|
|
1234
|
-
if (direction ===
|
|
1091
|
+
if (direction === 'left' || direction === 'right') {
|
|
1235
1092
|
horizontalOffset += arrowRef.current.offsetWidth;
|
|
1236
1093
|
}
|
|
1237
1094
|
else {
|
|
@@ -1246,11 +1103,11 @@ const positionMenu = (props) => {
|
|
|
1246
1103
|
const placeTopY = anchorRect.top - containerRect.top - menuRect.height - verticalOffset;
|
|
1247
1104
|
const placeBottomY = anchorRect.bottom - containerRect.top + verticalOffset;
|
|
1248
1105
|
let placeToporBottomX, placeLeftorRightY;
|
|
1249
|
-
if (align ===
|
|
1106
|
+
if (align === 'end') {
|
|
1250
1107
|
placeToporBottomX = anchorRect.right - containerRect.left - menuRect.width;
|
|
1251
1108
|
placeLeftorRightY = anchorRect.bottom - containerRect.top - menuRect.height;
|
|
1252
1109
|
}
|
|
1253
|
-
else if (align ===
|
|
1110
|
+
else if (align === 'center') {
|
|
1254
1111
|
placeToporBottomX = anchorRect.left - containerRect.left - (menuRect.width - anchorRect.width) / 2;
|
|
1255
1112
|
placeLeftorRightY = anchorRect.top - containerRect.top - (menuRect.height - anchorRect.height) / 2;
|
|
1256
1113
|
}
|
|
@@ -1275,17 +1132,17 @@ const positionMenu = (props) => {
|
|
|
1275
1132
|
position,
|
|
1276
1133
|
};
|
|
1277
1134
|
switch (direction) {
|
|
1278
|
-
case
|
|
1279
|
-
case
|
|
1135
|
+
case 'left':
|
|
1136
|
+
case 'right':
|
|
1280
1137
|
return placeLeftorRight(options);
|
|
1281
|
-
case
|
|
1282
|
-
case
|
|
1138
|
+
case 'top':
|
|
1139
|
+
case 'bottom':
|
|
1283
1140
|
default:
|
|
1284
1141
|
return placeToporBottom(options);
|
|
1285
1142
|
}
|
|
1286
1143
|
};
|
|
1287
1144
|
|
|
1288
|
-
const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowStyle, anchorPoint, anchorRef, containerRef, externalRef, parentScrollingRef, arrow, align =
|
|
1145
|
+
const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowStyle, anchorPoint, anchorRef, containerRef, externalRef, parentScrollingRef, arrow, align = 'start', direction = 'bottom', position = 'auto', overflow = 'visible', setDownOverflow, repositionFlag, captureFocus = true, state, endTransition, isDisabled, menuItemFocus, dontShrinkIfDirectionIsTop, offsetX = 0, offsetY = 0, children, onClose, ...restProps }) => {
|
|
1289
1146
|
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
|
1290
1147
|
const [arrowPosition, setArrowPosition] = useState({ x: 0, y: 0 });
|
|
1291
1148
|
const [overflowData, setOverflowData] = useState();
|
|
@@ -1303,8 +1160,8 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1303
1160
|
const latestHandlePosition = useRef(() => { });
|
|
1304
1161
|
const { hoverItem, dispatch, updateItems } = useItems(menuRef, focusRef);
|
|
1305
1162
|
const isOpen = isMenuOpen(state);
|
|
1306
|
-
const openTransition = getTransition(transition,
|
|
1307
|
-
const closeTransition = getTransition(transition,
|
|
1163
|
+
const openTransition = getTransition(transition, 'open');
|
|
1164
|
+
const closeTransition = getTransition(transition, 'close');
|
|
1308
1165
|
const scrollNodes = scrollNodesRef.current;
|
|
1309
1166
|
/**
|
|
1310
1167
|
* Given the menu-item we are on, find the closest or furthest next menu-item horizontally and focus it.
|
|
@@ -1314,8 +1171,8 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1314
1171
|
if (!menuItemEl)
|
|
1315
1172
|
return false;
|
|
1316
1173
|
let [best, worst] = [];
|
|
1317
|
-
findParentWithClass(
|
|
1318
|
-
?.querySelectorAll(
|
|
1174
|
+
findParentWithClass('szh-menu', menuItemEl)
|
|
1175
|
+
?.querySelectorAll('.szh-menu__item')
|
|
1319
1176
|
?.forEach((item, i) => {
|
|
1320
1177
|
// Must be at same height as currently focused menu-item
|
|
1321
1178
|
if (item.offsetTop === menuItemEl.offsetTop) {
|
|
@@ -1330,14 +1187,14 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1330
1187
|
const r = best ?? worst;
|
|
1331
1188
|
if (!r)
|
|
1332
1189
|
return false;
|
|
1333
|
-
dispatch(HoverActionTypes.SET_INDEX, undefined, r[
|
|
1190
|
+
dispatch(HoverActionTypes.SET_INDEX, undefined, r['i']);
|
|
1334
1191
|
return true;
|
|
1335
1192
|
};
|
|
1336
1193
|
const onKeyDown = (e) => {
|
|
1337
1194
|
const elementTarget = e.target instanceof HTMLElement ? e.target : null;
|
|
1338
1195
|
const isTextInputTarget = elementTarget &&
|
|
1339
|
-
(elementTarget.nodeName ===
|
|
1340
|
-
(elementTarget.nodeName ===
|
|
1196
|
+
(elementTarget.nodeName === 'TEXTAREA' ||
|
|
1197
|
+
(elementTarget.nodeName === 'INPUT' && elementTarget.getAttribute('type') === 'text'));
|
|
1341
1198
|
switch (e.key) {
|
|
1342
1199
|
case Keys.HOME:
|
|
1343
1200
|
// Don't eat home/end events on inputs
|
|
@@ -1381,14 +1238,14 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1381
1238
|
e.stopPropagation();
|
|
1382
1239
|
};
|
|
1383
1240
|
const onAnimationEnd = () => {
|
|
1384
|
-
if (state ===
|
|
1241
|
+
if (state === 'closing') {
|
|
1385
1242
|
setOverflowData(undefined); // reset overflowData after closing
|
|
1386
1243
|
}
|
|
1387
1244
|
endTransition && safeCall(endTransition);
|
|
1388
1245
|
};
|
|
1389
1246
|
const handlePosition = useCallback((noOverflowCheck) => {
|
|
1390
1247
|
if (!containerRef?.current) {
|
|
1391
|
-
if (process.env.NODE_ENV !==
|
|
1248
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1392
1249
|
console.error('[React-Menu] Menu cannot be positioned properly as container ref is null. If you need to initialise `state` prop to "open" for ControlledMenu, please see this solution: https://codesandbox.io/s/initial-open-sp10wn');
|
|
1393
1250
|
}
|
|
1394
1251
|
return;
|
|
@@ -1402,7 +1259,7 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1402
1259
|
const positionHelpers = getPositionHelpers(containerRef, menuRef, scrollNodes.menu, boundingBoxPadding);
|
|
1403
1260
|
const { menuRect } = positionHelpers;
|
|
1404
1261
|
let results = {
|
|
1405
|
-
computedDirection:
|
|
1262
|
+
computedDirection: 'bottom',
|
|
1406
1263
|
x: 0,
|
|
1407
1264
|
y: 0,
|
|
1408
1265
|
};
|
|
@@ -1425,7 +1282,7 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1425
1282
|
let { y } = results;
|
|
1426
1283
|
const { x, arrowX, arrowY, computedDirection } = results;
|
|
1427
1284
|
let menuHeight = menuRect.height;
|
|
1428
|
-
if (!noOverflowCheck && overflow !==
|
|
1285
|
+
if (!noOverflowCheck && overflow !== 'visible') {
|
|
1429
1286
|
const { getTopOverflow, getBottomOverflow } = positionHelpers;
|
|
1430
1287
|
let height, overflowAmt;
|
|
1431
1288
|
const prevHeight = latestMenuSize.current.height;
|
|
@@ -1506,21 +1363,21 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1506
1363
|
}
|
|
1507
1364
|
}
|
|
1508
1365
|
let scroll = viewScroll;
|
|
1509
|
-
if (scrollNodes.anchors.length && scroll ===
|
|
1510
|
-
scroll =
|
|
1511
|
-
if (scroll ===
|
|
1366
|
+
if (scrollNodes.anchors.length && scroll === 'initial')
|
|
1367
|
+
scroll = 'auto';
|
|
1368
|
+
if (scroll === 'initial')
|
|
1512
1369
|
return;
|
|
1513
1370
|
const handleScroll = () => {
|
|
1514
|
-
if (scroll ===
|
|
1371
|
+
if (scroll === 'auto') {
|
|
1515
1372
|
batchedUpdates(() => handlePosition(true));
|
|
1516
1373
|
}
|
|
1517
1374
|
else {
|
|
1518
1375
|
safeCall(onClose, { reason: CloseReason.SCROLL });
|
|
1519
1376
|
}
|
|
1520
1377
|
};
|
|
1521
|
-
const scrollObservers = scrollNodes.anchors.concat(viewScroll !==
|
|
1522
|
-
scrollObservers.forEach((o) => o.addEventListener(
|
|
1523
|
-
return () => scrollObservers.forEach((o) => o.removeEventListener(
|
|
1378
|
+
const scrollObservers = scrollNodes.anchors.concat(viewScroll !== 'initial' ? menuScroll : []);
|
|
1379
|
+
scrollObservers.forEach((o) => o.addEventListener('scroll', handleScroll));
|
|
1380
|
+
return () => scrollObservers.forEach((o) => o.removeEventListener('scroll', handleScroll));
|
|
1524
1381
|
}, [rootAnchorRef, scrollNodes, isOpen, onClose, viewScroll, handlePosition]);
|
|
1525
1382
|
const hasOverflow = !!overflowData && overflowData.overflowAmt != null && overflowData.overflowAmt > 0;
|
|
1526
1383
|
useEffect(() => {
|
|
@@ -1528,11 +1385,11 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1528
1385
|
return;
|
|
1529
1386
|
const handleScroll = () => batchedUpdates(handlePosition);
|
|
1530
1387
|
const parentScroll = parentScrollingRef.current;
|
|
1531
|
-
parentScroll.addEventListener(
|
|
1532
|
-
return () => parentScroll.removeEventListener(
|
|
1388
|
+
parentScroll.addEventListener('scroll', handleScroll);
|
|
1389
|
+
return () => parentScroll.removeEventListener('scroll', handleScroll);
|
|
1533
1390
|
}, [isOpen, hasOverflow, parentScrollingRef, handlePosition]);
|
|
1534
1391
|
useEffect(() => {
|
|
1535
|
-
if (typeof ResizeObserver !==
|
|
1392
|
+
if (typeof ResizeObserver !== 'function' || reposition === 'initial')
|
|
1536
1393
|
return;
|
|
1537
1394
|
const resizeObserver = new ResizeObserver(([entry]) => {
|
|
1538
1395
|
const { borderBoxSize, target } = entry;
|
|
@@ -1557,12 +1414,12 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1557
1414
|
});
|
|
1558
1415
|
});
|
|
1559
1416
|
const observeTarget = menuRef.current;
|
|
1560
|
-
resizeObserver.observe(observeTarget, { box:
|
|
1417
|
+
resizeObserver.observe(observeTarget, { box: 'border-box' });
|
|
1561
1418
|
return () => resizeObserver.unobserve(observeTarget);
|
|
1562
1419
|
}, [reposition]);
|
|
1563
1420
|
// Matt added window resize observer
|
|
1564
1421
|
useEffect(() => {
|
|
1565
|
-
if (typeof ResizeObserver !==
|
|
1422
|
+
if (typeof ResizeObserver !== 'function' || reposition === 'initial')
|
|
1566
1423
|
return;
|
|
1567
1424
|
const callback = debounce$1(() => {
|
|
1568
1425
|
const { width, height } = menuRef.current.ownerDocument.body.getBoundingClientRect();
|
|
@@ -1580,7 +1437,7 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1580
1437
|
}, 250);
|
|
1581
1438
|
const resizeObserver = new ResizeObserver(callback);
|
|
1582
1439
|
const observeTarget = menuRef.current.ownerDocument.body;
|
|
1583
|
-
resizeObserver.observe(observeTarget, { box:
|
|
1440
|
+
resizeObserver.observe(observeTarget, { box: 'border-box' });
|
|
1584
1441
|
return () => {
|
|
1585
1442
|
callback.cancel();
|
|
1586
1443
|
resizeObserver.unobserve(observeTarget);
|
|
@@ -1601,7 +1458,7 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1601
1458
|
else if (position === FocusPositions.LAST) {
|
|
1602
1459
|
dispatch(HoverActionTypes.LAST, undefined, 0);
|
|
1603
1460
|
}
|
|
1604
|
-
else if (typeof position ===
|
|
1461
|
+
else if (typeof position === 'number' && position >= -1) {
|
|
1605
1462
|
dispatch(HoverActionTypes.SET_INDEX, undefined, position);
|
|
1606
1463
|
}
|
|
1607
1464
|
};
|
|
@@ -1663,10 +1520,10 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1663
1520
|
if (minHeight.current < h)
|
|
1664
1521
|
minHeight.current = h;
|
|
1665
1522
|
}
|
|
1666
|
-
const dontShrinkOps = useMemo(() => expandedDirection ===
|
|
1523
|
+
const dontShrinkOps = useMemo(() => expandedDirection === 'top' && dontShrinkIfDirectionIsTop
|
|
1667
1524
|
? {
|
|
1668
|
-
overflowY: isSubmenuOpen ?
|
|
1669
|
-
minHeight: (isSubmenuOpen ? 0 : minHeight.current) +
|
|
1525
|
+
overflowY: isSubmenuOpen ? '' : 'auto',
|
|
1526
|
+
minHeight: (isSubmenuOpen ? 0 : minHeight.current) + 'px',
|
|
1670
1527
|
}
|
|
1671
1528
|
: undefined, [dontShrinkIfDirectionIsTop, expandedDirection, isSubmenuOpen]);
|
|
1672
1529
|
return (jsxs("ul", { role: "menu", "aria-label": ariaLabel, ...mergeProps({ onKeyDown, onAnimationEnd }, restProps), ...commonProps(isDisabled), ref: useCombinedRef(externalRef, menuRef), className: useBEM({ block: menuClass, modifiers, className: menuClassName }), style: {
|
|
@@ -1674,18 +1531,18 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1674
1531
|
...overflowStyle,
|
|
1675
1532
|
...dontShrinkOps,
|
|
1676
1533
|
margin: 0,
|
|
1677
|
-
display: state ===
|
|
1678
|
-
position:
|
|
1534
|
+
display: state === 'closed' ? 'none' : undefined,
|
|
1535
|
+
position: 'absolute',
|
|
1679
1536
|
left: menuPosition.x,
|
|
1680
1537
|
top: menuPosition.y,
|
|
1681
|
-
}, children: [jsx("div", { ref: focusRef, tabIndex: -1, style: { position:
|
|
1682
|
-
if (event.key ==
|
|
1538
|
+
}, children: [jsx("div", { ref: focusRef, tabIndex: -1, style: { position: 'absolute', left: 0, top: 0 }, onKeyDown: (event) => {
|
|
1539
|
+
if (event.key == 'Tab') {
|
|
1683
1540
|
event.preventDefault();
|
|
1684
1541
|
event.stopPropagation();
|
|
1685
1542
|
safeCall(onClose, {
|
|
1686
1543
|
key: event.key,
|
|
1687
1544
|
shiftKey: event.shiftKey,
|
|
1688
|
-
reason: event.key ===
|
|
1545
|
+
reason: event.key === 'Tab'
|
|
1689
1546
|
? event.shiftKey
|
|
1690
1547
|
? CloseReason.TAB_BACKWARD
|
|
1691
1548
|
: CloseReason.TAB_FORWARD
|
|
@@ -1694,13 +1551,13 @@ const MenuList = ({ ariaLabel, menuClassName, menuStyle, arrowClassName, arrowSt
|
|
|
1694
1551
|
}
|
|
1695
1552
|
} }), arrow && (jsx("div", { className: _arrowClass, style: {
|
|
1696
1553
|
...arrowStyle,
|
|
1697
|
-
position:
|
|
1554
|
+
position: 'absolute',
|
|
1698
1555
|
left: arrowPosition.x,
|
|
1699
1556
|
top: arrowPosition.y,
|
|
1700
1557
|
}, ref: arrowRef })), jsx(MenuListContext.Provider, { value: listContext, children: jsx(MenuListItemContext.Provider, { value: itemContext, children: jsx(HoverItemContext.Provider, { value: hoverItem, children: children }) }) })] }));
|
|
1701
1558
|
};
|
|
1702
1559
|
|
|
1703
|
-
const ControlledMenuFr = ({
|
|
1560
|
+
const ControlledMenuFr = ({ 'aria-label': ariaLabel, className, containerProps, initialMounted, unmountOnClose, transition, transitionTimeout, boundingBoxRef, boundingBoxPadding, reposition = 'auto', submenuOpenDelay = 300, submenuCloseDelay = 150, skipOpen, viewScroll = 'initial', portal, theming, onItemClick, onClose, saveButtonRef, ...restProps }, externalRef) => {
|
|
1704
1561
|
const containerRef = useRef();
|
|
1705
1562
|
const scrollNodesRef = useRef({});
|
|
1706
1563
|
const { anchorRef, state } = restProps;
|
|
@@ -1727,10 +1584,10 @@ const ControlledMenuFr = ({ "aria-label": ariaLabel, className, containerProps,
|
|
|
1727
1584
|
reposition,
|
|
1728
1585
|
viewScroll,
|
|
1729
1586
|
]);
|
|
1730
|
-
const isWithinMenu = useCallback((target) => hasParentClass(
|
|
1587
|
+
const isWithinMenu = useCallback((target) => hasParentClass('szh-menu--state-open', target) ||
|
|
1731
1588
|
// This is temporary, it will be removed when the overlay click is fixed
|
|
1732
|
-
hasParentClass(
|
|
1733
|
-
hasParentClass(
|
|
1589
|
+
hasParentClass('LuiModalPrefab', target) ||
|
|
1590
|
+
hasParentClass('prefab-modal', target), []);
|
|
1734
1591
|
const handleScreenEventForSave = useCallback((ev) => {
|
|
1735
1592
|
if (!isWithinMenu(ev.target)) {
|
|
1736
1593
|
ev.preventDefault();
|
|
@@ -1740,7 +1597,7 @@ const ControlledMenuFr = ({ "aria-label": ariaLabel, className, containerProps,
|
|
|
1740
1597
|
// This doesn't happen in React18
|
|
1741
1598
|
// To work around it, I invoke the save by clicking on a passed in invisible button ref
|
|
1742
1599
|
if (saveButtonRef?.current) {
|
|
1743
|
-
saveButtonRef.current.setAttribute(
|
|
1600
|
+
saveButtonRef.current.setAttribute('data-reason', CloseReason.BLUR);
|
|
1744
1601
|
saveButtonRef.current.click();
|
|
1745
1602
|
}
|
|
1746
1603
|
else
|
|
@@ -1770,7 +1627,7 @@ const ControlledMenuFr = ({ "aria-label": ariaLabel, className, containerProps,
|
|
|
1770
1627
|
const activeElement = thisDocument.activeElement;
|
|
1771
1628
|
if (!anchorRef?.current || !activeElement)
|
|
1772
1629
|
return;
|
|
1773
|
-
if (ev.key !==
|
|
1630
|
+
if (ev.key !== 'Tab' && ev.key !== 'Enter')
|
|
1774
1631
|
return;
|
|
1775
1632
|
if (ev.repeat) {
|
|
1776
1633
|
ev.preventDefault();
|
|
@@ -1780,12 +1637,12 @@ const ControlledMenuFr = ({ "aria-label": ariaLabel, className, containerProps,
|
|
|
1780
1637
|
const invokeSave = (reason) => {
|
|
1781
1638
|
if (!saveButtonRef?.current)
|
|
1782
1639
|
return;
|
|
1783
|
-
saveButtonRef.current?.setAttribute(
|
|
1640
|
+
saveButtonRef.current?.setAttribute('data-reason', reason);
|
|
1784
1641
|
saveButtonRef?.current?.click();
|
|
1785
1642
|
};
|
|
1786
1643
|
// data-allowtabtosave is used such that list filter inputs can use tab to save
|
|
1787
|
-
const allowTabToSave = activeElement.getAttribute(
|
|
1788
|
-
const inputElsIterator = thisDocument.querySelectorAll(
|
|
1644
|
+
const allowTabToSave = activeElement.getAttribute('data-allowtabtosave') == 'true';
|
|
1645
|
+
const inputElsIterator = thisDocument.querySelectorAll('.szh-menu--state-open input,textarea');
|
|
1789
1646
|
let inputEls = [];
|
|
1790
1647
|
inputElsIterator.forEach((el) => inputEls.push(el));
|
|
1791
1648
|
inputEls = inputEls.filter((el) => !el.disabled);
|
|
@@ -1793,9 +1650,9 @@ const ControlledMenuFr = ({ "aria-label": ariaLabel, className, containerProps,
|
|
|
1793
1650
|
return;
|
|
1794
1651
|
const firstInputEl = inputEls[0];
|
|
1795
1652
|
const lastInputEl = inputEls[inputEls.length - 1];
|
|
1796
|
-
const isTextArea = activeElement.nodeName ===
|
|
1797
|
-
const suppressEnterAutoSave = activeElement.getAttribute(
|
|
1798
|
-
if (ev.key ===
|
|
1653
|
+
const isTextArea = activeElement.nodeName === 'TEXTAREA';
|
|
1654
|
+
const suppressEnterAutoSave = activeElement.getAttribute('data-disableenterautosave') == 'true' || isTextArea;
|
|
1655
|
+
if (ev.key === 'Tab') {
|
|
1799
1656
|
const tabDirection = ev.shiftKey ? CloseReason.TAB_BACKWARD : CloseReason.TAB_FORWARD;
|
|
1800
1657
|
if ((activeElement === lastInputEl && !ev.shiftKey) ||
|
|
1801
1658
|
(activeElement === firstInputEl && ev.shiftKey) ||
|
|
@@ -1810,13 +1667,13 @@ const ControlledMenuFr = ({ "aria-label": ariaLabel, className, containerProps,
|
|
|
1810
1667
|
}
|
|
1811
1668
|
}
|
|
1812
1669
|
}
|
|
1813
|
-
const type = activeElement.getAttribute(
|
|
1814
|
-
const isTextInput = type ===
|
|
1670
|
+
const type = activeElement.getAttribute('type');
|
|
1671
|
+
const isTextInput = type === 'text' || type == null || type === 'textarea';
|
|
1815
1672
|
switch (activeElement.nodeName) {
|
|
1816
|
-
case
|
|
1673
|
+
case 'INPUT':
|
|
1817
1674
|
{
|
|
1818
1675
|
// If there's only one input element, we support tab and enter
|
|
1819
|
-
if (isTextInput && ev.key ===
|
|
1676
|
+
if (isTextInput && ev.key === 'Enter' && !suppressEnterAutoSave) {
|
|
1820
1677
|
ev.preventDefault();
|
|
1821
1678
|
ev.stopPropagation();
|
|
1822
1679
|
if (isDown) {
|
|
@@ -1835,19 +1692,19 @@ const ControlledMenuFr = ({ "aria-label": ariaLabel, className, containerProps,
|
|
|
1835
1692
|
useEffect(() => {
|
|
1836
1693
|
if (isMenuOpen(state)) {
|
|
1837
1694
|
const thisDocument = anchorRef?.current ? anchorRef?.current.ownerDocument : document;
|
|
1838
|
-
thisDocument.addEventListener(
|
|
1839
|
-
thisDocument.addEventListener(
|
|
1840
|
-
thisDocument.addEventListener(
|
|
1841
|
-
thisDocument.addEventListener(
|
|
1842
|
-
thisDocument.addEventListener(
|
|
1843
|
-
thisDocument.addEventListener(
|
|
1695
|
+
thisDocument.addEventListener('keydown', handleKeydownTabAndEnter, true);
|
|
1696
|
+
thisDocument.addEventListener('keyup', handleKeyupTabAndEnter, true);
|
|
1697
|
+
thisDocument.addEventListener('mousedown', handleScreenEventForSave, true);
|
|
1698
|
+
thisDocument.addEventListener('mouseup', handleScreenEventForCancel, true);
|
|
1699
|
+
thisDocument.addEventListener('click', handleScreenEventForCancel, true);
|
|
1700
|
+
thisDocument.addEventListener('dblclick', handleScreenEventForCancel, true);
|
|
1844
1701
|
return () => {
|
|
1845
|
-
thisDocument.removeEventListener(
|
|
1846
|
-
thisDocument.removeEventListener(
|
|
1847
|
-
thisDocument.removeEventListener(
|
|
1848
|
-
thisDocument.removeEventListener(
|
|
1849
|
-
thisDocument.removeEventListener(
|
|
1850
|
-
thisDocument.removeEventListener(
|
|
1702
|
+
thisDocument.removeEventListener('keydown', handleKeydownTabAndEnter, true);
|
|
1703
|
+
thisDocument.removeEventListener('keyup', handleKeyupTabAndEnter, true);
|
|
1704
|
+
thisDocument.removeEventListener('mousedown', handleScreenEventForSave, true);
|
|
1705
|
+
thisDocument.removeEventListener('mouseup', handleScreenEventForCancel, true);
|
|
1706
|
+
thisDocument.removeEventListener('click', handleScreenEventForCancel, true);
|
|
1707
|
+
thisDocument.removeEventListener('dblclick', handleScreenEventForCancel, true);
|
|
1851
1708
|
};
|
|
1852
1709
|
}
|
|
1853
1710
|
return () => { };
|
|
@@ -1879,7 +1736,7 @@ const ControlledMenuFr = ({ "aria-label": ariaLabel, className, containerProps,
|
|
|
1879
1736
|
value: event.value,
|
|
1880
1737
|
key: event.key,
|
|
1881
1738
|
shiftKey: event.shiftKey,
|
|
1882
|
-
reason: event.key ===
|
|
1739
|
+
reason: event.key === 'Tab'
|
|
1883
1740
|
? event.shiftKey
|
|
1884
1741
|
? CloseReason.TAB_BACKWARD
|
|
1885
1742
|
: CloseReason.TAB_FORWARD
|
|
@@ -1917,15 +1774,15 @@ const ControlledMenuFr = ({ "aria-label": ariaLabel, className, containerProps,
|
|
|
1917
1774
|
}
|
|
1918
1775
|
}
|
|
1919
1776
|
};
|
|
1920
|
-
const itemTransition = getTransition(transition,
|
|
1777
|
+
const itemTransition = getTransition(transition, 'item');
|
|
1921
1778
|
const modifiers = useMemo(() => ({ theme: theming, itemTransition }), [theming, itemTransition]);
|
|
1922
1779
|
const menuList = (jsx("div", { ...mergeProps({ onKeyUp, onBlur }, containerProps), className: useBEM({
|
|
1923
1780
|
block: menuContainerClass,
|
|
1924
1781
|
modifiers,
|
|
1925
1782
|
className,
|
|
1926
|
-
}), style: { ...containerProps?.style, position:
|
|
1783
|
+
}), style: { ...containerProps?.style, position: 'relative' }, ref: containerRef, children: state && (jsx(SettingsContext.Provider, { value: settings, children: jsx(ItemSettingsContext.Provider, { value: itemSettings, children: jsx(EventHandlersContext.Provider, { value: eventHandlers, children: jsx(MenuList, { ...restProps, ariaLabel: ariaLabel || 'Menu', externalRef: externalRef, containerRef: containerRef, onClose: onClose }) }) }) })) }));
|
|
1927
1784
|
if (portal === true && anchorRef?.current != null) {
|
|
1928
|
-
if (hasParentClass(
|
|
1785
|
+
if (hasParentClass('react-menu-inline-test', anchorRef.current)) {
|
|
1929
1786
|
portal = false;
|
|
1930
1787
|
}
|
|
1931
1788
|
else {
|
|
@@ -1933,8 +1790,8 @@ const ControlledMenuFr = ({ "aria-label": ariaLabel, className, containerProps,
|
|
|
1933
1790
|
}
|
|
1934
1791
|
}
|
|
1935
1792
|
if (portal) {
|
|
1936
|
-
if (typeof portal ===
|
|
1937
|
-
if (portal && typeof document !==
|
|
1793
|
+
if (typeof portal === 'boolean') {
|
|
1794
|
+
if (portal && typeof document !== 'undefined') {
|
|
1938
1795
|
return createPortal(menuList, document.body);
|
|
1939
1796
|
}
|
|
1940
1797
|
}
|
|
@@ -1946,7 +1803,31 @@ const ControlledMenuFr = ({ "aria-label": ariaLabel, className, containerProps,
|
|
|
1946
1803
|
};
|
|
1947
1804
|
const ControlledMenu = forwardRef(ControlledMenuFr);
|
|
1948
1805
|
|
|
1949
|
-
|
|
1806
|
+
const FocusableItemFr = ({ className, disabled, children, isHovering, menuItemRef, externalRef, ...restProps }) => {
|
|
1807
|
+
const isDisabled = !!disabled;
|
|
1808
|
+
const ref = useRef(null);
|
|
1809
|
+
const { setHover, onPointerLeave, ...restStateProps } = useItemState(menuItemRef, ref, isHovering, isDisabled);
|
|
1810
|
+
const { handleClose } = useContext(EventHandlersContext);
|
|
1811
|
+
const modifiers = useMemo(() => ({
|
|
1812
|
+
disabled: isDisabled,
|
|
1813
|
+
hover: isHovering,
|
|
1814
|
+
focusable: true,
|
|
1815
|
+
}), [isDisabled, isHovering]);
|
|
1816
|
+
const renderChildren = useMemo(() => safeCall(children, {
|
|
1817
|
+
...modifiers,
|
|
1818
|
+
ref,
|
|
1819
|
+
closeMenu: handleClose,
|
|
1820
|
+
}), [children, modifiers, handleClose]);
|
|
1821
|
+
const mergedProps = mergeProps({
|
|
1822
|
+
...restStateProps,
|
|
1823
|
+
onPointerLeave: (e) => onPointerLeave(e, true),
|
|
1824
|
+
onFocus: setHover,
|
|
1825
|
+
}, restProps);
|
|
1826
|
+
return (jsx("li", { role: "menuitem", ...mergedProps, ...commonProps(isDisabled), ref: useCombinedRef(externalRef, menuItemRef), className: useBEM({ block: menuClass, element: menuItemClass, modifiers, className }), children: renderChildren }));
|
|
1827
|
+
};
|
|
1828
|
+
const FocusableItem = withHovering('FocusableItem', FocusableItemFr);
|
|
1829
|
+
|
|
1830
|
+
function MenuFr({ 'aria-label': ariaLabel, menuButton, instanceRef, onMenuChange, ...restProps }, externalRef) {
|
|
1950
1831
|
const [stateProps, toggleMenu, openMenu] = useMenuStateAndFocus(restProps);
|
|
1951
1832
|
const isOpen = isMenuOpen(stateProps.state);
|
|
1952
1833
|
const skipOpen = useRef(false);
|
|
@@ -1978,13 +1859,13 @@ function MenuFr({ "aria-label": ariaLabel, menuButton, instanceRef, onMenuChange
|
|
|
1978
1859
|
// Too many mixed types here to figure out what to pick for button. Bad code.
|
|
1979
1860
|
const button = safeCall(menuButton, { open: isOpen });
|
|
1980
1861
|
if (!button || !button.type)
|
|
1981
|
-
throw new Error(
|
|
1862
|
+
throw new Error('Menu requires a menuButton prop.');
|
|
1982
1863
|
const buttonProps = {
|
|
1983
1864
|
ref: useCombinedRef(button.ref, buttonRef),
|
|
1984
1865
|
...mergeProps({ onClick, onKeyDown }, button.props),
|
|
1985
1866
|
isOpen: false,
|
|
1986
1867
|
};
|
|
1987
|
-
if (getName(button.type) ===
|
|
1868
|
+
if (getName(button.type) === 'MenuButton') {
|
|
1988
1869
|
buttonProps.isOpen = isOpen;
|
|
1989
1870
|
}
|
|
1990
1871
|
const renderButton = cloneElement(button, buttonProps);
|
|
@@ -1993,17 +1874,165 @@ function MenuFr({ "aria-label": ariaLabel, menuButton, instanceRef, onMenuChange
|
|
|
1993
1874
|
openMenu,
|
|
1994
1875
|
closeMenu: () => toggleMenu(false),
|
|
1995
1876
|
}));
|
|
1996
|
-
return (jsxs(Fragment$1, { children: [renderButton, jsx(ControlledMenu, { ...restProps, ...stateProps, "aria-label": ariaLabel || (typeof button.props.children ===
|
|
1877
|
+
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 })] }));
|
|
1997
1878
|
}
|
|
1998
1879
|
const Menu = forwardRef(MenuFr);
|
|
1999
1880
|
|
|
2000
|
-
const
|
|
1881
|
+
const MenuButton = forwardRef(function MenuButton({ className, isOpen, disabled, children, ...restProps }, ref) {
|
|
1882
|
+
const modifiers = useMemo(() => ({ open: isOpen }), [isOpen]);
|
|
1883
|
+
return (jsx("button", { "aria-haspopup": true, "aria-expanded": isOpen, "aria-disabled": disabled || undefined, type: "button", disabled: disabled, ...restProps, ref: ref, className: useBEM({ block: menuButtonClass, modifiers, className }), children: children }));
|
|
1884
|
+
});
|
|
1885
|
+
|
|
1886
|
+
const MenuDividerFr = ({ className, ...restProps }, externalRef) => {
|
|
1887
|
+
return (jsx("li", { role: "separator", ...restProps, ref: externalRef, className: useBEM({ block: menuClass, element: menuDividerClass, className }) }));
|
|
1888
|
+
};
|
|
1889
|
+
const MenuDivider = memo(forwardRef(MenuDividerFr));
|
|
1890
|
+
|
|
1891
|
+
const MenuGroupFr = ({ className, style, takeOverflow, ...restProps }, externalRef) => {
|
|
1892
|
+
const ref = useRef(null);
|
|
1893
|
+
const [overflowStyle, setOverflowStyle] = useState();
|
|
1894
|
+
const { overflow, overflowAmt } = useContext(MenuListContext);
|
|
1895
|
+
useIsomorphicLayoutEffect(() => {
|
|
1896
|
+
let maxHeight;
|
|
1897
|
+
if (takeOverflow && overflowAmt != null && overflowAmt >= 0 && ref.current) {
|
|
1898
|
+
maxHeight = ref.current.getBoundingClientRect().height - overflowAmt;
|
|
1899
|
+
if (maxHeight < 0)
|
|
1900
|
+
maxHeight = 0;
|
|
1901
|
+
}
|
|
1902
|
+
setOverflowStyle(maxHeight != null && maxHeight >= 0 ? { maxHeight, overflow } : undefined);
|
|
1903
|
+
}, [takeOverflow, overflow, overflowAmt]);
|
|
1904
|
+
useIsomorphicLayoutEffect(() => {
|
|
1905
|
+
if (overflowStyle && ref.current)
|
|
1906
|
+
ref.current.scrollTop = 0;
|
|
1907
|
+
}, [overflowStyle]);
|
|
1908
|
+
return (jsx("div", { ...restProps, ref: useCombinedRef(externalRef, ref), className: useBEM({ block: menuClass, element: menuGroupClass, className }), style: { ...style, ...overflowStyle } }));
|
|
1909
|
+
};
|
|
1910
|
+
const MenuGroup = forwardRef(MenuGroupFr);
|
|
1911
|
+
|
|
1912
|
+
const MenuHeaderFr = ({ className, ...restProps }, externalRef) => {
|
|
1913
|
+
return (jsx("li", { role: "presentation", ...restProps, ref: externalRef, className: useBEM({ block: menuClass, element: menuHeaderClass, className }) }));
|
|
1914
|
+
};
|
|
1915
|
+
const MenuHeader = memo(forwardRef(MenuHeaderFr));
|
|
1916
|
+
|
|
1917
|
+
const RadioGroupContext = createContext({});
|
|
1918
|
+
|
|
1919
|
+
const MenuItemFr = ({ className, value, href, type, checked, disabled, children, onClick, isHovering, menuItemRef, externalRef, ...restProps }) => {
|
|
1920
|
+
const isDisabled = !!disabled;
|
|
1921
|
+
const { setHover, ...restStateProps } = useItemState(menuItemRef, menuItemRef, isHovering, isDisabled);
|
|
1922
|
+
const eventHandlers = useContext(EventHandlersContext);
|
|
1923
|
+
const radioGroup = useContext(RadioGroupContext);
|
|
1924
|
+
const isRadio = type === 'radio';
|
|
1925
|
+
const isCheckBox = type === 'checkbox';
|
|
1926
|
+
const isAnchor = !!href && !isDisabled && !isRadio && !isCheckBox;
|
|
1927
|
+
const isChecked = isRadio ? radioGroup.value === value : isCheckBox ? !!checked : false;
|
|
1928
|
+
// handle click seems to be a combination of multiple event types, bad code.
|
|
1929
|
+
const handleClick = (e) => {
|
|
1930
|
+
if (isDisabled) {
|
|
1931
|
+
if (e.syntheticEvent) {
|
|
1932
|
+
e.syntheticEvent.stopPropagation();
|
|
1933
|
+
e.syntheticEvent.preventDefault();
|
|
1934
|
+
}
|
|
1935
|
+
else {
|
|
1936
|
+
e.stopPropagation();
|
|
1937
|
+
e.preventDefault();
|
|
1938
|
+
}
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
const event = {
|
|
1942
|
+
value,
|
|
1943
|
+
syntheticEvent: e,
|
|
1944
|
+
};
|
|
1945
|
+
if (e.key !== undefined) {
|
|
1946
|
+
const ke = e;
|
|
1947
|
+
event.key = ke.key;
|
|
1948
|
+
event.shiftKey = ke.shiftKey;
|
|
1949
|
+
}
|
|
1950
|
+
if (isCheckBox)
|
|
1951
|
+
event.checked = !isChecked;
|
|
1952
|
+
if (isRadio)
|
|
1953
|
+
event.name = radioGroup.name;
|
|
1954
|
+
safeCall(onClick, event);
|
|
1955
|
+
if (isRadio)
|
|
1956
|
+
safeCall(radioGroup.onRadioChange, event);
|
|
1957
|
+
eventHandlers.handleClick(event, isCheckBox || isRadio);
|
|
1958
|
+
};
|
|
1959
|
+
const handleKeyDown = (e) => {
|
|
1960
|
+
// if tab is allowed the handleKeyUp event can't process the tab
|
|
1961
|
+
if (e.key === 'Tab') {
|
|
1962
|
+
e.preventDefault();
|
|
1963
|
+
e.stopPropagation();
|
|
1964
|
+
}
|
|
1965
|
+
};
|
|
1966
|
+
/**
|
|
1967
|
+
* Keyboard events are triggered on up, otherwise subcomponents get spaces and enters typed in them
|
|
1968
|
+
*/
|
|
1969
|
+
const handleKeyUp = (e) => {
|
|
1970
|
+
if (!isHovering)
|
|
1971
|
+
return;
|
|
1972
|
+
switch (e.key) {
|
|
1973
|
+
case Keys.ENTER:
|
|
1974
|
+
case Keys.TAB:
|
|
1975
|
+
case Keys.SPACE:
|
|
1976
|
+
e.preventDefault();
|
|
1977
|
+
e.stopPropagation();
|
|
1978
|
+
if (isAnchor) {
|
|
1979
|
+
menuItemRef?.current && menuItemRef.current.click();
|
|
1980
|
+
}
|
|
1981
|
+
else {
|
|
1982
|
+
handleClick(e);
|
|
1983
|
+
}
|
|
1984
|
+
break;
|
|
1985
|
+
}
|
|
1986
|
+
};
|
|
1987
|
+
const modifiers = useMemo(() => ({
|
|
1988
|
+
type,
|
|
1989
|
+
disabled: isDisabled,
|
|
1990
|
+
hover: isHovering,
|
|
1991
|
+
checked: isChecked,
|
|
1992
|
+
anchor: isAnchor,
|
|
1993
|
+
}), [type, isDisabled, isHovering, isChecked, isAnchor]);
|
|
1994
|
+
const mergedProps = mergeProps({
|
|
1995
|
+
...restStateProps,
|
|
1996
|
+
onPointerDown: setHover,
|
|
1997
|
+
onKeyDown: handleKeyDown,
|
|
1998
|
+
onKeyUp: handleKeyUp,
|
|
1999
|
+
onClick: handleClick,
|
|
2000
|
+
}, restProps);
|
|
2001
|
+
// Order of props overriding (same in all components):
|
|
2002
|
+
// 1. Preset props adhering to WAI-ARIA Authoring Practices.
|
|
2003
|
+
// 2. Merged outer and local props
|
|
2004
|
+
// 3. ref, className
|
|
2005
|
+
const menuItemProps = {
|
|
2006
|
+
role: isRadio ? 'menuitemradio' : isCheckBox ? 'menuitemcheckbox' : 'menuitem',
|
|
2007
|
+
'aria-checked': isRadio || isCheckBox ? isChecked : undefined,
|
|
2008
|
+
...mergedProps,
|
|
2009
|
+
...commonProps(isDisabled, isHovering),
|
|
2010
|
+
ref: useCombinedRef(externalRef, menuItemRef),
|
|
2011
|
+
className: useBEM({ block: menuClass, element: menuItemClass, modifiers, className }),
|
|
2012
|
+
children: useMemo(() => safeCall(children, modifiers), [children, modifiers]),
|
|
2013
|
+
};
|
|
2014
|
+
if (isAnchor) {
|
|
2015
|
+
return (jsx("li", { role: "presentation", children: jsx("a", { href: href, ...menuItemProps }) }));
|
|
2016
|
+
}
|
|
2017
|
+
else {
|
|
2018
|
+
return jsx("li", { ...menuItemProps });
|
|
2019
|
+
}
|
|
2020
|
+
};
|
|
2021
|
+
const MenuItem = withHovering('MenuItem', MenuItemFr);
|
|
2022
|
+
|
|
2023
|
+
const MenuRadioGroupFr = ({ 'aria-label': ariaLabel, className, name, value, onRadioChange, ...restProps }, externalRef) => {
|
|
2024
|
+
const contextValue = useMemo(() => ({ name, value, onRadioChange }), [name, value, onRadioChange]);
|
|
2025
|
+
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 }) }) }) }));
|
|
2026
|
+
};
|
|
2027
|
+
const MenuRadioGroup = forwardRef(MenuRadioGroupFr);
|
|
2028
|
+
|
|
2029
|
+
const SubMenuFr = ({ 'aria-label': ariaLabel, className, disabled, direction, label, openTrigger, onMenuChange, isHovering, instanceRef, menuItemRef, itemProps = {}, ...restProps }) => {
|
|
2001
2030
|
const settings = useContext(SettingsContext);
|
|
2002
2031
|
const { rootMenuRef } = settings;
|
|
2003
2032
|
const { submenuOpenDelay, submenuCloseDelay } = useContext(ItemSettingsContext);
|
|
2004
2033
|
const { parentMenuRef, parentDir, overflow: parentOverflow } = useContext(MenuListContext);
|
|
2005
2034
|
const { isParentOpen, isSubmenuOpen, setOpenSubmenuCount, dispatch, updateItems } = useContext(MenuListItemContext);
|
|
2006
|
-
const isPortal = parentOverflow !==
|
|
2035
|
+
const isPortal = parentOverflow !== 'visible';
|
|
2007
2036
|
const [stateProps, toggleMenu, _openMenu] = useMenuStateAndFocus(settings);
|
|
2008
2037
|
const { state } = stateProps;
|
|
2009
2038
|
const isDisabled = !!disabled;
|
|
@@ -2071,7 +2100,7 @@ const SubMenuFr = ({ "aria-label": ariaLabel, className, disabled, direction, la
|
|
|
2071
2100
|
case Keys.ENTER:
|
|
2072
2101
|
case Keys.SPACE:
|
|
2073
2102
|
case Keys.RIGHT:
|
|
2074
|
-
openTrigger !==
|
|
2103
|
+
openTrigger !== 'none' && openMenu(FocusPositions.FIRST);
|
|
2075
2104
|
break;
|
|
2076
2105
|
}
|
|
2077
2106
|
};
|
|
@@ -2113,188 +2142,21 @@ const SubMenuFr = ({ "aria-label": ariaLabel, className, disabled, direction, la
|
|
|
2113
2142
|
onPointerMove: handlePointerMove,
|
|
2114
2143
|
onPointerLeave: handlePointerLeave,
|
|
2115
2144
|
onKeyDown: handleItemKeyDown,
|
|
2116
|
-
onClick: () => openTrigger !==
|
|
2145
|
+
onClick: () => openTrigger !== 'none' && openMenu(),
|
|
2117
2146
|
}, restItemProps);
|
|
2118
2147
|
const getMenuList = () => {
|
|
2119
|
-
const menuList = (jsx(MenuList, { ...restProps, ...stateProps, ariaLabel: ariaLabel || (typeof label ===
|
|
2148
|
+
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 }));
|
|
2120
2149
|
const container = rootMenuRef?.current;
|
|
2121
2150
|
return isPortal && container ? createPortal(menuList, container) : menuList;
|
|
2122
2151
|
};
|
|
2123
|
-
return (jsxs("li", { className: useBEM({ block: menuClass, element: subMenuClass, className }), style: { position:
|
|
2152
|
+
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({
|
|
2124
2153
|
block: menuClass,
|
|
2125
2154
|
element: menuItemClass,
|
|
2126
2155
|
modifiers,
|
|
2127
2156
|
className: itemClassName,
|
|
2128
2157
|
}), children: useMemo(() => safeCall(label, modifiers), [label, modifiers]) }), state && getMenuList()] }));
|
|
2129
2158
|
};
|
|
2130
|
-
const SubMenu = withHovering(
|
|
2131
|
-
|
|
2132
|
-
const RadioGroupContext = createContext({});
|
|
2133
|
-
|
|
2134
|
-
const MenuItemFr = ({ className, value, href, type, checked, disabled, children, onClick, isHovering, menuItemRef, externalRef, ...restProps }) => {
|
|
2135
|
-
const isDisabled = !!disabled;
|
|
2136
|
-
const { setHover, ...restStateProps } = useItemState(menuItemRef, menuItemRef, isHovering, isDisabled);
|
|
2137
|
-
const eventHandlers = useContext(EventHandlersContext);
|
|
2138
|
-
const radioGroup = useContext(RadioGroupContext);
|
|
2139
|
-
const isRadio = type === "radio";
|
|
2140
|
-
const isCheckBox = type === "checkbox";
|
|
2141
|
-
const isAnchor = !!href && !isDisabled && !isRadio && !isCheckBox;
|
|
2142
|
-
const isChecked = isRadio ? radioGroup.value === value : isCheckBox ? !!checked : false;
|
|
2143
|
-
// handle click seems to be a combination of multiple event types, bad code.
|
|
2144
|
-
const handleClick = (e) => {
|
|
2145
|
-
if (isDisabled) {
|
|
2146
|
-
if (e.syntheticEvent) {
|
|
2147
|
-
e.syntheticEvent.stopPropagation();
|
|
2148
|
-
e.syntheticEvent.preventDefault();
|
|
2149
|
-
}
|
|
2150
|
-
else {
|
|
2151
|
-
e.stopPropagation();
|
|
2152
|
-
e.preventDefault();
|
|
2153
|
-
}
|
|
2154
|
-
return;
|
|
2155
|
-
}
|
|
2156
|
-
const event = {
|
|
2157
|
-
value,
|
|
2158
|
-
syntheticEvent: e,
|
|
2159
|
-
};
|
|
2160
|
-
if (e.key !== undefined) {
|
|
2161
|
-
const ke = e;
|
|
2162
|
-
event.key = ke.key;
|
|
2163
|
-
event.shiftKey = ke.shiftKey;
|
|
2164
|
-
}
|
|
2165
|
-
if (isCheckBox)
|
|
2166
|
-
event.checked = !isChecked;
|
|
2167
|
-
if (isRadio)
|
|
2168
|
-
event.name = radioGroup.name;
|
|
2169
|
-
safeCall(onClick, event);
|
|
2170
|
-
if (isRadio)
|
|
2171
|
-
safeCall(radioGroup.onRadioChange, event);
|
|
2172
|
-
eventHandlers.handleClick(event, isCheckBox || isRadio);
|
|
2173
|
-
};
|
|
2174
|
-
const handleKeyDown = (e) => {
|
|
2175
|
-
// if tab is allowed the handleKeyUp event can't process the tab
|
|
2176
|
-
if (e.key === "Tab") {
|
|
2177
|
-
e.preventDefault();
|
|
2178
|
-
e.stopPropagation();
|
|
2179
|
-
}
|
|
2180
|
-
};
|
|
2181
|
-
/**
|
|
2182
|
-
* Keyboard events are triggered on up, otherwise subcomponents get spaces and enters typed in them
|
|
2183
|
-
*/
|
|
2184
|
-
const handleKeyUp = (e) => {
|
|
2185
|
-
if (!isHovering)
|
|
2186
|
-
return;
|
|
2187
|
-
switch (e.key) {
|
|
2188
|
-
case Keys.ENTER:
|
|
2189
|
-
case Keys.TAB:
|
|
2190
|
-
case Keys.SPACE:
|
|
2191
|
-
e.preventDefault();
|
|
2192
|
-
e.stopPropagation();
|
|
2193
|
-
if (isAnchor) {
|
|
2194
|
-
menuItemRef?.current && menuItemRef.current.click();
|
|
2195
|
-
}
|
|
2196
|
-
else {
|
|
2197
|
-
handleClick(e);
|
|
2198
|
-
}
|
|
2199
|
-
break;
|
|
2200
|
-
}
|
|
2201
|
-
};
|
|
2202
|
-
const modifiers = useMemo(() => ({
|
|
2203
|
-
type,
|
|
2204
|
-
disabled: isDisabled,
|
|
2205
|
-
hover: isHovering,
|
|
2206
|
-
checked: isChecked,
|
|
2207
|
-
anchor: isAnchor,
|
|
2208
|
-
}), [type, isDisabled, isHovering, isChecked, isAnchor]);
|
|
2209
|
-
const mergedProps = mergeProps({
|
|
2210
|
-
...restStateProps,
|
|
2211
|
-
onPointerDown: setHover,
|
|
2212
|
-
onKeyDown: handleKeyDown,
|
|
2213
|
-
onKeyUp: handleKeyUp,
|
|
2214
|
-
onClick: handleClick,
|
|
2215
|
-
}, restProps);
|
|
2216
|
-
// Order of props overriding (same in all components):
|
|
2217
|
-
// 1. Preset props adhering to WAI-ARIA Authoring Practices.
|
|
2218
|
-
// 2. Merged outer and local props
|
|
2219
|
-
// 3. ref, className
|
|
2220
|
-
const menuItemProps = {
|
|
2221
|
-
role: isRadio ? "menuitemradio" : isCheckBox ? "menuitemcheckbox" : "menuitem",
|
|
2222
|
-
"aria-checked": isRadio || isCheckBox ? isChecked : undefined,
|
|
2223
|
-
...mergedProps,
|
|
2224
|
-
...commonProps(isDisabled, isHovering),
|
|
2225
|
-
ref: useCombinedRef(externalRef, menuItemRef),
|
|
2226
|
-
className: useBEM({ block: menuClass, element: menuItemClass, modifiers, className }),
|
|
2227
|
-
children: useMemo(() => safeCall(children, modifiers), [children, modifiers]),
|
|
2228
|
-
};
|
|
2229
|
-
if (isAnchor) {
|
|
2230
|
-
return (jsx("li", { role: "presentation", children: jsx("a", { href: href, ...menuItemProps }) }));
|
|
2231
|
-
}
|
|
2232
|
-
else {
|
|
2233
|
-
return jsx("li", { ...menuItemProps });
|
|
2234
|
-
}
|
|
2235
|
-
};
|
|
2236
|
-
const MenuItem = withHovering("MenuItem", MenuItemFr);
|
|
2237
|
-
|
|
2238
|
-
const FocusableItemFr = ({ className, disabled, children, isHovering, menuItemRef, externalRef, ...restProps }) => {
|
|
2239
|
-
const isDisabled = !!disabled;
|
|
2240
|
-
const ref = useRef(null);
|
|
2241
|
-
const { setHover, onPointerLeave, ...restStateProps } = useItemState(menuItemRef, ref, isHovering, isDisabled);
|
|
2242
|
-
const { handleClose } = useContext(EventHandlersContext);
|
|
2243
|
-
const modifiers = useMemo(() => ({
|
|
2244
|
-
disabled: isDisabled,
|
|
2245
|
-
hover: isHovering,
|
|
2246
|
-
focusable: true,
|
|
2247
|
-
}), [isDisabled, isHovering]);
|
|
2248
|
-
const renderChildren = useMemo(() => safeCall(children, {
|
|
2249
|
-
...modifiers,
|
|
2250
|
-
ref,
|
|
2251
|
-
closeMenu: handleClose,
|
|
2252
|
-
}), [children, modifiers, handleClose]);
|
|
2253
|
-
const mergedProps = mergeProps({
|
|
2254
|
-
...restStateProps,
|
|
2255
|
-
onPointerLeave: (e) => onPointerLeave(e, true),
|
|
2256
|
-
onFocus: setHover,
|
|
2257
|
-
}, restProps);
|
|
2258
|
-
return (jsx("li", { role: "menuitem", ...mergedProps, ...commonProps(isDisabled), ref: useCombinedRef(externalRef, menuItemRef), className: useBEM({ block: menuClass, element: menuItemClass, modifiers, className }), children: renderChildren }));
|
|
2259
|
-
};
|
|
2260
|
-
const FocusableItem = withHovering("FocusableItem", FocusableItemFr);
|
|
2261
|
-
|
|
2262
|
-
const MenuDividerFr = ({ className, ...restProps }, externalRef) => {
|
|
2263
|
-
return (jsx("li", { role: "separator", ...restProps, ref: externalRef, className: useBEM({ block: menuClass, element: menuDividerClass, className }) }));
|
|
2264
|
-
};
|
|
2265
|
-
const MenuDivider = memo(forwardRef(MenuDividerFr));
|
|
2266
|
-
|
|
2267
|
-
const MenuHeaderFr = ({ className, ...restProps }, externalRef) => {
|
|
2268
|
-
return (jsx("li", { role: "presentation", ...restProps, ref: externalRef, className: useBEM({ block: menuClass, element: menuHeaderClass, className }) }));
|
|
2269
|
-
};
|
|
2270
|
-
const MenuHeader = memo(forwardRef(MenuHeaderFr));
|
|
2271
|
-
|
|
2272
|
-
const MenuGroupFr = ({ className, style, takeOverflow, ...restProps }, externalRef) => {
|
|
2273
|
-
const ref = useRef(null);
|
|
2274
|
-
const [overflowStyle, setOverflowStyle] = useState();
|
|
2275
|
-
const { overflow, overflowAmt } = useContext(MenuListContext);
|
|
2276
|
-
useIsomorphicLayoutEffect(() => {
|
|
2277
|
-
let maxHeight;
|
|
2278
|
-
if (takeOverflow && overflowAmt != null && overflowAmt >= 0 && ref.current) {
|
|
2279
|
-
maxHeight = ref.current.getBoundingClientRect().height - overflowAmt;
|
|
2280
|
-
if (maxHeight < 0)
|
|
2281
|
-
maxHeight = 0;
|
|
2282
|
-
}
|
|
2283
|
-
setOverflowStyle(maxHeight != null && maxHeight >= 0 ? { maxHeight, overflow } : undefined);
|
|
2284
|
-
}, [takeOverflow, overflow, overflowAmt]);
|
|
2285
|
-
useIsomorphicLayoutEffect(() => {
|
|
2286
|
-
if (overflowStyle && ref.current)
|
|
2287
|
-
ref.current.scrollTop = 0;
|
|
2288
|
-
}, [overflowStyle]);
|
|
2289
|
-
return (jsx("div", { ...restProps, ref: useCombinedRef(externalRef, ref), className: useBEM({ block: menuClass, element: menuGroupClass, className }), style: { ...style, ...overflowStyle } }));
|
|
2290
|
-
};
|
|
2291
|
-
const MenuGroup = forwardRef(MenuGroupFr);
|
|
2292
|
-
|
|
2293
|
-
const MenuRadioGroupFr = ({ "aria-label": ariaLabel, className, name, value, onRadioChange, ...restProps }, externalRef) => {
|
|
2294
|
-
const contextValue = useMemo(() => ({ name, value, onRadioChange }), [name, value, onRadioChange]);
|
|
2295
|
-
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 }) }) }) }));
|
|
2296
|
-
};
|
|
2297
|
-
const MenuRadioGroup = forwardRef(MenuRadioGroupFr);
|
|
2159
|
+
const SubMenu = withHovering('SubMenu', SubMenuFr);
|
|
2298
2160
|
|
|
2299
2161
|
const useGridContextMenu = ({ contextMenuSelectRow, contextMenu: ContextMenu, }) => {
|
|
2300
2162
|
const { redrawRows, prePopupOps, postPopupOps, getSelectedRows } = useContext(GridContext);
|
|
@@ -2329,46 +2191,191 @@ const useGridContextMenu = ({ contextMenuSelectRow, contextMenu: ContextMenu, })
|
|
|
2329
2191
|
return {
|
|
2330
2192
|
openMenu,
|
|
2331
2193
|
cellContextMenu,
|
|
2332
|
-
component: ContextMenu ? (jsx(Fragment, { children: jsx(ControlledMenu, { anchorPoint: anchorPoint, state: isOpen ?
|
|
2194
|
+
component: ContextMenu ? (jsx(Fragment, { 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,
|
|
2333
2195
|
};
|
|
2334
2196
|
};
|
|
2335
2197
|
|
|
2198
|
+
const GridLoadingOverlayComponentFr = (props, externalRef) => (jsx("div", { ref: externalRef, style: {
|
|
2199
|
+
left: 0,
|
|
2200
|
+
top: 0,
|
|
2201
|
+
bottom: 0,
|
|
2202
|
+
right: 0,
|
|
2203
|
+
position: 'absolute',
|
|
2204
|
+
backgroundColor: 'rgba(255,255,255,0.5)',
|
|
2205
|
+
}, children: jsx("div", { style: { height: '100%', position: 'relative' }, children: jsx("div", { style: { position: 'absolute', left: 0, top: props.headerRowHeight, right: 0, bottom: 0 }, children: jsx(LuiStatusSpinner, {}) }) }) }));
|
|
2206
|
+
const GridLoadingOverlayComponent = forwardRef(GridLoadingOverlayComponentFr);
|
|
2207
|
+
const GridNoRowsOverlayFr = (props, externalRef) => {
|
|
2208
|
+
if (props.loading) {
|
|
2209
|
+
return jsx(GridLoadingOverlayComponent, { ref: externalRef, headerRowHeight: props.headerRowHeight });
|
|
2210
|
+
}
|
|
2211
|
+
if (props.rowCount === 0) {
|
|
2212
|
+
return jsx("div", { ref: externalRef, children: props.noRowsOverlayText ?? 'There are currently no rows' });
|
|
2213
|
+
}
|
|
2214
|
+
if (props.filteredRowCount === 0) {
|
|
2215
|
+
return jsx("div", { ref: externalRef, children: props.noRowsMatchingOverlayText ?? 'All rows have been filtered' });
|
|
2216
|
+
}
|
|
2217
|
+
return jsx("div", { ref: externalRef });
|
|
2218
|
+
};
|
|
2219
|
+
const GridNoRowsOverlay = forwardRef(GridNoRowsOverlayFr);
|
|
2220
|
+
|
|
2336
2221
|
/**
|
|
2337
|
-
*
|
|
2338
|
-
*
|
|
2222
|
+
* Retains last sort order from via <AgGrid postRowSort>.
|
|
2223
|
+
* Then applies this sort until next sort column change.
|
|
2224
|
+
* Handles stale sort, when you edit a row but don't want to re-sort.
|
|
2339
2225
|
*/
|
|
2340
|
-
const
|
|
2341
|
-
const {
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
if
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
const
|
|
2351
|
-
if
|
|
2352
|
-
|
|
2353
|
-
const
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
if (!cell)
|
|
2359
|
-
return;
|
|
2360
|
-
const input = cell.querySelector("input, button");
|
|
2361
|
-
if (!input)
|
|
2226
|
+
const usePostSortRowsHook = ({ setStaleGrid }) => {
|
|
2227
|
+
const { redrawRows } = useContext(GridContext);
|
|
2228
|
+
// On first run we need to init the first backed up sort order
|
|
2229
|
+
const initialised = useRef(false);
|
|
2230
|
+
// Used to detect sort order has changed since last run
|
|
2231
|
+
const lastSortOrderHash = useRef('');
|
|
2232
|
+
// Used to detect if sort order was the same, has the direction changed
|
|
2233
|
+
const previousRowSortIndexRef = useRef({});
|
|
2234
|
+
// stale sort is when there's a sort and user edits a row
|
|
2235
|
+
// this applies a class to the div wrapping the grid which in turn adds a * beside the sort arrow
|
|
2236
|
+
const sortWasStale = useRef(false);
|
|
2237
|
+
// Previous col sort is stored and reapplied if the sort direction changes on the same column and the sort was stale
|
|
2238
|
+
// As a sort on a stale row should just resort, not change sort direction
|
|
2239
|
+
const previousColSort = useRef([]);
|
|
2240
|
+
const previousQuickFilter = useRef('');
|
|
2241
|
+
return useCallback(({ api, nodes }) => {
|
|
2242
|
+
// Grid is destroyed
|
|
2243
|
+
if (!api)
|
|
2362
2244
|
return;
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2245
|
+
const previousRowSortIndex = previousRowSortIndexRef.current;
|
|
2246
|
+
const hashNode = (node) => {
|
|
2247
|
+
return node ? JSON.stringify(node.data) : '';
|
|
2248
|
+
};
|
|
2249
|
+
const copyCurrentSortSettings = () => api.getColumnState().map((row) => ({ colId: row.colId, sort: row.sort, sortIndex: row.sortIndex }));
|
|
2250
|
+
const backupSortOrder = () => {
|
|
2251
|
+
for (const x in previousRowSortIndex)
|
|
2252
|
+
delete previousRowSortIndex[x];
|
|
2253
|
+
nodes.forEach((node, index) => (previousRowSortIndex[`${node.data.id}`] = { index, hash: hashNode(node) }));
|
|
2254
|
+
};
|
|
2255
|
+
// Check if column is the first sorted column. Note: column is preconfigured to sort its sortIndex is null not 1
|
|
2256
|
+
const isFirstSortColumn = (row) => row.sort && [0, null].includes(row.sortIndex ?? null);
|
|
2257
|
+
const isSameColumnAndDifferentSort = (col1, col2) => col1 && col2 && col1.colId === col2.colId && col1.sort != col2.sort;
|
|
2258
|
+
const restorePreviousSortColumnState = () => api.applyColumnState({ state: previousColSort.current });
|
|
2259
|
+
const sortNodesByPreviousSort = () => {
|
|
2260
|
+
nodes.sort((a, b) => (previousRowSortIndex[`${a.data.id}`]?.index ?? Number.MAX_SAFE_INTEGER) -
|
|
2261
|
+
(previousRowSortIndex[`${b.data.id}`]?.index ?? Number.MAX_SAFE_INTEGER));
|
|
2262
|
+
};
|
|
2263
|
+
// On first load copy the current sort
|
|
2264
|
+
if (!initialised.current) {
|
|
2265
|
+
initialised.current = true;
|
|
2266
|
+
previousColSort.current = copyCurrentSortSettings();
|
|
2267
|
+
}
|
|
2268
|
+
const newSortOrder = JSON.stringify(copyCurrentSortSettings());
|
|
2269
|
+
let sortOrderChanged = newSortOrder != lastSortOrderHash.current;
|
|
2270
|
+
const quickFilter = api.getQuickFilter();
|
|
2271
|
+
if (previousQuickFilter.current != quickFilter) {
|
|
2272
|
+
previousQuickFilter.current = quickFilter ?? '';
|
|
2273
|
+
sortOrderChanged = true;
|
|
2274
|
+
}
|
|
2275
|
+
if (isEmpty(previousRowSortIndex)) {
|
|
2276
|
+
backupSortOrder();
|
|
2277
|
+
}
|
|
2278
|
+
if (sortOrderChanged) {
|
|
2279
|
+
const thisFirstCol = copyCurrentSortSettings().find(isFirstSortColumn);
|
|
2280
|
+
const previousFirstCol = previousColSort.current.find(isFirstSortColumn);
|
|
2281
|
+
// Change to sort can be: (in the context of stale sort do...)
|
|
2282
|
+
// - no sort to sorted (do nothing)
|
|
2283
|
+
// - asc to desc (resort as asc, and remove stale sort)
|
|
2284
|
+
// - desc to no sort (resort as desc, and remove stale sort)
|
|
2285
|
+
const wasSortChangedOnSameColumn = previousFirstCol && (!thisFirstCol || isSameColumnAndDifferentSort(previousFirstCol, thisFirstCol));
|
|
2286
|
+
// if sort was stale, and we're here, someone has clicked a stale sort order (^*) in the column header
|
|
2287
|
+
if (sortWasStale.current && wasSortChangedOnSameColumn) {
|
|
2288
|
+
// Sort was an existing priority 1 column
|
|
2289
|
+
// We want to re-sort with the old sort direction and clear stale sort
|
|
2290
|
+
sortWasStale.current = false;
|
|
2291
|
+
setStaleGrid(false);
|
|
2292
|
+
// trigger a recapture of sort order
|
|
2293
|
+
lastSortOrderHash.current = '';
|
|
2294
|
+
restorePreviousSortColumnState();
|
|
2295
|
+
}
|
|
2296
|
+
else {
|
|
2297
|
+
// Sort was on a different column so clear sort and sort like normal
|
|
2298
|
+
if (sortWasStale.current) {
|
|
2299
|
+
sortWasStale.current = false;
|
|
2300
|
+
setStaleGrid(false);
|
|
2301
|
+
}
|
|
2302
|
+
// Columns were sorted so retain the sort
|
|
2303
|
+
backupSortOrder();
|
|
2304
|
+
lastSortOrderHash.current = newSortOrder;
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
else {
|
|
2308
|
+
let firstChangedNodeIndex = -1;
|
|
2309
|
+
let lastNewNode = undefined;
|
|
2310
|
+
let changedRowCount = 0;
|
|
2311
|
+
let newRowCount = 0;
|
|
2312
|
+
let index = 0;
|
|
2313
|
+
for (const node of nodes) {
|
|
2314
|
+
const psr = previousRowSortIndex[`${node.data.id}`];
|
|
2315
|
+
if (psr) {
|
|
2316
|
+
if (psr.hash != hashNode(node)) {
|
|
2317
|
+
if (firstChangedNodeIndex === -1)
|
|
2318
|
+
firstChangedNodeIndex = index;
|
|
2319
|
+
changedRowCount++;
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
else {
|
|
2323
|
+
lastNewNode = node;
|
|
2324
|
+
newRowCount++;
|
|
2325
|
+
}
|
|
2326
|
+
index++;
|
|
2327
|
+
}
|
|
2328
|
+
let wasStale = false;
|
|
2329
|
+
if (changedRowCount === 0 && newRowCount === 1) {
|
|
2330
|
+
// insert new row at end
|
|
2331
|
+
const newIndex = index - 1;
|
|
2332
|
+
previousRowSortIndex[`${lastNewNode?.data.id}`] = { index: newIndex, hash: hashNode(lastNewNode) };
|
|
2333
|
+
wasStale = true;
|
|
2334
|
+
}
|
|
2335
|
+
else if (changedRowCount === 2 && newRowCount === 0) {
|
|
2336
|
+
// This must be a swap rows (sometimes it's not, needs further attention)
|
|
2337
|
+
backupSortOrder();
|
|
2338
|
+
wasStale = false;
|
|
2339
|
+
}
|
|
2340
|
+
else if (changedRowCount > 1 && newRowCount === 1) {
|
|
2341
|
+
// This must be a insert so, insert new row near the row that changed
|
|
2342
|
+
previousRowSortIndex[`${lastNewNode?.data.id}`] = {
|
|
2343
|
+
index: firstChangedNodeIndex + 0.5,
|
|
2344
|
+
hash: hashNode(lastNewNode),
|
|
2345
|
+
};
|
|
2346
|
+
wasStale = true;
|
|
2347
|
+
// For some reason AgGrid mis-positions the inserted row.
|
|
2348
|
+
lastNewNode && redrawRows();
|
|
2349
|
+
}
|
|
2350
|
+
else if (changedRowCount == 1 && newRowCount === 0) {
|
|
2351
|
+
// User edited one row so, do nothing, retain sort
|
|
2352
|
+
wasStale = true;
|
|
2353
|
+
}
|
|
2354
|
+
else if (changedRowCount !== 0 || newRowCount != 0) {
|
|
2355
|
+
// too many rows changed, resort
|
|
2356
|
+
backupSortOrder();
|
|
2357
|
+
}
|
|
2358
|
+
if (wasStale) {
|
|
2359
|
+
// Check if the sort order the aggrid passed matches our stale sort order
|
|
2360
|
+
const stillStale = Object.keys(previousRowSortIndex).length != nodes.length ||
|
|
2361
|
+
nodes.some((node, index) => previousRowSortIndex[`${node.data.id}`]?.index !== index);
|
|
2362
|
+
// If we haven't already processed a stale sort then...
|
|
2363
|
+
if (stillStale && !sortWasStale.current) {
|
|
2364
|
+
// backup sort state, so we can restore it when sort is clicked on a stale column
|
|
2365
|
+
previousColSort.current = copyCurrentSortSettings();
|
|
2366
|
+
sortWasStale.current = true;
|
|
2367
|
+
setStaleGrid(true);
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
sortNodesByPreviousSort();
|
|
2371
|
+
}
|
|
2372
|
+
}, [redrawRows, setStaleGrid]);
|
|
2366
2373
|
};
|
|
2367
2374
|
|
|
2368
2375
|
/**
|
|
2369
2376
|
* Wrapper for AgGrid to add commonly used functionality.
|
|
2370
2377
|
*/
|
|
2371
|
-
const Grid = ({
|
|
2378
|
+
const Grid = ({ 'data-testid': dataTestId, defaultPostSort = true, rowSelection = 'multiple', suppressColumnVirtualization = true, theme = 'ag-theme-step-default', sizeColumns = 'auto', selectColumnPinned = null, contextMenuSelectRow = false, singleClickEdit = false, rowData, rowHeight = theme === 'ag-theme-step-default' ? 40 : theme === 'ag-theme-step-compact' ? 36 : 40, ...params }) => {
|
|
2372
2379
|
const { gridReady, gridRenderState, setApis, ensureRowVisible, getFirstRowId, selectRowsById, focusByRowById, ensureSelectedRowIsVisible, autoSizeColumns, sizeColumnsToFit, externallySelectedItemsAreInSync, setExternallySelectedItemsAreInSync, isExternalFilterPresent, doesExternalFilterPass, setOnCellEditingComplete, getColDef, showNoRowsOverlay, prePopupOps, stopEditing, } = useContext(GridContext);
|
|
2373
2380
|
const { checkUpdating, updatedDep, updatingCols } = useContext(GridUpdatingContext);
|
|
2374
2381
|
const gridDivRef = useRef(null);
|
|
@@ -2397,20 +2404,20 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2397
2404
|
needsAutoSize.current = true;
|
|
2398
2405
|
return;
|
|
2399
2406
|
}
|
|
2400
|
-
const skipHeader = sizeColumns ===
|
|
2401
|
-
if (sizeColumns ===
|
|
2407
|
+
const skipHeader = sizeColumns === 'auto-skip-headers' && gridRendered === 'rows-visible';
|
|
2408
|
+
if (sizeColumns === 'auto' || skipHeader) {
|
|
2402
2409
|
const result = autoSizeColumns({ skipHeader, userSizedColIds: userSizedColIds.current, includeFlex: true });
|
|
2403
2410
|
if (!result) {
|
|
2404
2411
|
needsAutoSize.current = true;
|
|
2405
2412
|
return;
|
|
2406
2413
|
}
|
|
2407
|
-
if (gridRendered ===
|
|
2414
|
+
if (gridRendered === 'empty') {
|
|
2408
2415
|
if (!hasSetContentSizeEmpty.current && !hasSetContentSize.current) {
|
|
2409
2416
|
hasSetContentSizeEmpty.current = true;
|
|
2410
2417
|
params.onContentSize?.(result);
|
|
2411
2418
|
}
|
|
2412
2419
|
}
|
|
2413
|
-
else if (gridRendered ===
|
|
2420
|
+
else if (gridRendered === 'rows-visible') {
|
|
2414
2421
|
if (!hasSetContentSize.current) {
|
|
2415
2422
|
if (lastFullResize.current === result.width) {
|
|
2416
2423
|
hasSetContentSize.current = true;
|
|
@@ -2424,10 +2431,10 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2424
2431
|
}
|
|
2425
2432
|
else {
|
|
2426
2433
|
// It should be impossible to get here
|
|
2427
|
-
console.error(
|
|
2434
|
+
console.error('Unknown value returned from hasGridRendered');
|
|
2428
2435
|
}
|
|
2429
2436
|
}
|
|
2430
|
-
if (sizeColumns !==
|
|
2437
|
+
if (sizeColumns !== 'none') {
|
|
2431
2438
|
sizeColumnsToFit();
|
|
2432
2439
|
}
|
|
2433
2440
|
setAutoSized(true);
|
|
@@ -2446,7 +2453,7 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2446
2453
|
needsAutoSize.current = true;
|
|
2447
2454
|
}
|
|
2448
2455
|
}
|
|
2449
|
-
if (needsAutoSize.current || (!hasSetContentSize.current && sizeColumns ===
|
|
2456
|
+
if (needsAutoSize.current || (!hasSetContentSize.current && sizeColumns === 'auto')) {
|
|
2450
2457
|
needsAutoSize.current = false;
|
|
2451
2458
|
setInitialContentSize();
|
|
2452
2459
|
}
|
|
@@ -2488,7 +2495,7 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2488
2495
|
}
|
|
2489
2496
|
const selectedRows = api.getSelectedRows();
|
|
2490
2497
|
// We don't want to update selected Items if it hasn't changed to prevent excess renders
|
|
2491
|
-
if (params.externalSelectedItems.length
|
|
2498
|
+
if (params.externalSelectedItems.length !== selectedRows.length ||
|
|
2492
2499
|
isNotEmpty(xorBy(selectedRows, params.externalSelectedItems, (row) => row.id))) {
|
|
2493
2500
|
setExternallySelectedItemsAreInSync(false);
|
|
2494
2501
|
params.setExternalSelectedItems([...selectedRows]);
|
|
@@ -2544,14 +2551,14 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2544
2551
|
editable,
|
|
2545
2552
|
cellClassRules: {
|
|
2546
2553
|
...colDef.cellClassRules,
|
|
2547
|
-
|
|
2554
|
+
'GridCell-readonly': (ccp) => !editable(ccp),
|
|
2548
2555
|
},
|
|
2549
2556
|
};
|
|
2550
2557
|
});
|
|
2551
2558
|
return params.selectable || params.onRowDragEnd
|
|
2552
2559
|
? [
|
|
2553
2560
|
{
|
|
2554
|
-
colId:
|
|
2561
|
+
colId: 'selection',
|
|
2555
2562
|
editable: false,
|
|
2556
2563
|
rowDrag: !!params.onRowDragEnd,
|
|
2557
2564
|
minWidth: params.selectable && params.onRowDragEnd ? 76 : 48,
|
|
@@ -2561,12 +2568,12 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2561
2568
|
exportable: false,
|
|
2562
2569
|
},
|
|
2563
2570
|
checkboxSelection: params.selectable,
|
|
2564
|
-
headerClass: params.onRowDragEnd ?
|
|
2565
|
-
headerComponent: rowSelection ===
|
|
2571
|
+
headerClass: params.onRowDragEnd ? 'ag-header-select-draggable' : undefined,
|
|
2572
|
+
headerComponent: rowSelection === 'multiple' ? GridHeaderSelect : null,
|
|
2566
2573
|
suppressHeaderKeyboardEvent: (e) => {
|
|
2567
2574
|
if (!params.selectable)
|
|
2568
2575
|
return false;
|
|
2569
|
-
if ((e.event.key ===
|
|
2576
|
+
if ((e.event.key === 'Enter' || e.event.key === ' ') && !e.event.repeat) {
|
|
2570
2577
|
if (isEmpty(e.api.getSelectedRows())) {
|
|
2571
2578
|
e.api.selectAllFiltered();
|
|
2572
2579
|
}
|
|
@@ -2617,7 +2624,7 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2617
2624
|
// Don't update while there are spinners
|
|
2618
2625
|
if (!isEmpty(updatingCols()))
|
|
2619
2626
|
return;
|
|
2620
|
-
const skipHeader = sizeColumns ===
|
|
2627
|
+
const skipHeader = sizeColumns === 'auto-skip-headers';
|
|
2621
2628
|
if (hasSetContentSize.current) {
|
|
2622
2629
|
autoSizeColumns({ skipHeader, userSizedColIds: userSizedColIds.current, colIds: colIdsEdited.current });
|
|
2623
2630
|
}
|
|
@@ -2647,7 +2654,7 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2647
2654
|
event.node.setSelected(true, true);
|
|
2648
2655
|
}
|
|
2649
2656
|
// Cell already being edited, so don't re-edit until finished
|
|
2650
|
-
if (checkUpdating([event.colDef.field ??
|
|
2657
|
+
if (checkUpdating([event.colDef.field ?? ''], event.data.id)) {
|
|
2651
2658
|
return;
|
|
2652
2659
|
}
|
|
2653
2660
|
if (event.rowIndex !== null) {
|
|
@@ -2693,11 +2700,11 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2693
2700
|
*/
|
|
2694
2701
|
const onCellKeyPress = useCallback((e) => {
|
|
2695
2702
|
const kbe = e.event;
|
|
2696
|
-
if (kbe.key ===
|
|
2703
|
+
if (kbe.key === 'Enter') {
|
|
2697
2704
|
if (!invokeEditAction(e))
|
|
2698
2705
|
startCellEditing(e);
|
|
2699
2706
|
}
|
|
2700
|
-
if (kbe.key ===
|
|
2707
|
+
if (kbe.key === 'Tab') {
|
|
2701
2708
|
// eslint-disable-next-line
|
|
2702
2709
|
prePopupOps();
|
|
2703
2710
|
}
|
|
@@ -2707,14 +2714,14 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2707
2714
|
* but we don't want the non-flex auto-sized columns to "fit" size, so suppressSizeToFit is set to true.
|
|
2708
2715
|
*/
|
|
2709
2716
|
const columnDefsAdjusted = useMemo(() => {
|
|
2710
|
-
const adjustColDefOrGroup = (colDef) =>
|
|
2717
|
+
const adjustColDefOrGroup = (colDef) => 'children' in colDef ? adjustGroupColDef(colDef) : adjustColDef(colDef);
|
|
2711
2718
|
const adjustGroupColDef = (colDef) => ({
|
|
2712
2719
|
...colDef,
|
|
2713
2720
|
children: colDef.children.map((colDef) => adjustColDefOrGroup(colDef)),
|
|
2714
2721
|
});
|
|
2715
2722
|
const adjustColDef = (colDef) => ({
|
|
2716
2723
|
...colDef,
|
|
2717
|
-
suppressSizeToFit: (sizeColumns ===
|
|
2724
|
+
suppressSizeToFit: (sizeColumns === 'auto' || sizeColumns === 'auto-skip-headers') && !colDef.flex,
|
|
2718
2725
|
sortable: colDef.sortable && params.defaultColDef?.sortable !== false,
|
|
2719
2726
|
});
|
|
2720
2727
|
return columnDefs.map((colDef) => adjustColDefOrGroup(colDef));
|
|
@@ -2736,8 +2743,8 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2736
2743
|
if (isEmpty(colIds)) {
|
|
2737
2744
|
// Columns to resize?
|
|
2738
2745
|
if (!isEmpty(colIdsEdited.current)) {
|
|
2739
|
-
const skipHeader = sizeColumns ===
|
|
2740
|
-
if (sizeColumns ===
|
|
2746
|
+
const skipHeader = sizeColumns === 'auto-skip-headers';
|
|
2747
|
+
if (sizeColumns === 'auto' || skipHeader) {
|
|
2741
2748
|
defer$1(() => {
|
|
2742
2749
|
if (hasSetContentSize.current) {
|
|
2743
2750
|
autoSizeColumns({
|
|
@@ -2773,7 +2780,7 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2773
2780
|
* Resize columns to fit if required on window/container resize
|
|
2774
2781
|
*/
|
|
2775
2782
|
const onGridSizeChanged = useCallback(() => {
|
|
2776
|
-
if (sizeColumns !==
|
|
2783
|
+
if (sizeColumns !== 'none') {
|
|
2777
2784
|
sizeColumnsToFit();
|
|
2778
2785
|
}
|
|
2779
2786
|
}, [sizeColumns, sizeColumnsToFit]);
|
|
@@ -2789,10 +2796,10 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2789
2796
|
if (colId == null)
|
|
2790
2797
|
return;
|
|
2791
2798
|
switch (e.source) {
|
|
2792
|
-
case
|
|
2799
|
+
case 'uiColumnDragged':
|
|
2793
2800
|
userSizedColIds.current.add(colId);
|
|
2794
2801
|
break;
|
|
2795
|
-
case
|
|
2802
|
+
case 'autosizeColumns':
|
|
2796
2803
|
userSizedColIds.current.delete(colId);
|
|
2797
2804
|
break;
|
|
2798
2805
|
}
|
|
@@ -2814,33 +2821,35 @@ const Grid = ({ "data-testid": dataTestId, defaultPostSort = true, rowSelection
|
|
|
2814
2821
|
}
|
|
2815
2822
|
}
|
|
2816
2823
|
}, []);
|
|
2817
|
-
const onRowDragEnd = useCallback(
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2824
|
+
const onRowDragEnd = useCallback((event) => {
|
|
2825
|
+
void (async () => {
|
|
2826
|
+
const clientSideRowModel = event.api.getModel();
|
|
2827
|
+
if (event.node.rowIndex != null) {
|
|
2828
|
+
const lastHighlightedRowNode = clientSideRowModel.getLastHighlightedRowNode();
|
|
2829
|
+
const isBelow = lastHighlightedRowNode && lastHighlightedRowNode.highlighted === RowHighlightPosition.Below;
|
|
2830
|
+
let targetIndex = event.overIndex;
|
|
2831
|
+
if (event.node.rowIndex > event.overIndex) {
|
|
2832
|
+
targetIndex += isBelow ? 1 : 0;
|
|
2833
|
+
}
|
|
2834
|
+
else {
|
|
2835
|
+
targetIndex += isBelow ? 0 : -1;
|
|
2836
|
+
}
|
|
2837
|
+
const moved = event.node.data;
|
|
2838
|
+
const target = event.overNode?.data;
|
|
2839
|
+
moved.id !== target?.id && //moved over a different row
|
|
2840
|
+
event.node.rowIndex != targetIndex && //moved to a different index
|
|
2841
|
+
params.onRowDragEnd &&
|
|
2842
|
+
(await params.onRowDragEnd(moved, target, targetIndex));
|
|
2828
2843
|
}
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
moved.id !== target?.id && //moved over a different row
|
|
2832
|
-
event.node.rowIndex != targetIndex && //moved to a different index
|
|
2833
|
-
params.onRowDragEnd &&
|
|
2834
|
-
(await params.onRowDragEnd(moved, target, targetIndex));
|
|
2835
|
-
}
|
|
2836
|
-
clientSideRowModel.highlightRowAtPixel(null);
|
|
2844
|
+
clientSideRowModel.highlightRowAtPixel(null);
|
|
2845
|
+
})();
|
|
2837
2846
|
}, [params]);
|
|
2838
2847
|
// This is setting a ref in the GridContext so won't be triggering an update loop
|
|
2839
2848
|
setOnCellEditingComplete(params.onCellEditingComplete);
|
|
2840
2849
|
const headerRowCount = columnDefs.some((c) => c.children) ? 2 : 1;
|
|
2841
|
-
return (jsxs("div", { "data-testid": dataTestId, className: clsx(
|
|
2850
|
+
return (jsxs("div", { "data-testid": dataTestId, className: clsx('Grid-container', theme, 'theme-specific', staleGrid && 'Grid-sortIsStale', gridReady && rowData && autoSized && 'Grid-ready'), children: [gridContextMenu.component, jsx("div", { style: { flex: 1 }, ref: gridDivRef, children: jsx(AgGridReact, { rowHeight: rowHeight, animateRows: params.animateRows ?? false, rowClassRules: params.rowClassRules, getRowId: (params) => `${params.data.id}`, suppressRowClickSelection: true, rowSelection: rowSelection, suppressBrowserResizeObserver: true, onGridSizeChanged: onGridSizeChanged, suppressColumnVirtualisation: suppressColumnVirtualization, suppressClickEdit: true, onColumnVisible: () => {
|
|
2842
2851
|
setInitialContentSize();
|
|
2843
|
-
}, onRowDataUpdated: onRowDataChanged, onCellKeyDown: onCellKeyPress, onCellClicked: onCellClicked, onCellDoubleClicked: onCellDoubleClick, onCellEditingStarted: refreshSelectedRows, domLayout: params.domLayout, onColumnResized: onColumnResized, defaultColDef: { minWidth: 48, ...omit(params.defaultColDef, [
|
|
2852
|
+
}, onRowDataUpdated: onRowDataChanged, onCellKeyDown: onCellKeyPress, onCellClicked: onCellClicked, onCellDoubleClicked: onCellDoubleClick, onCellEditingStarted: refreshSelectedRows, domLayout: params.domLayout, onColumnResized: onColumnResized, defaultColDef: { minWidth: 48, ...omit(params.defaultColDef, ['editable']) }, columnDefs: columnDefsAdjusted, rowData: rowData, noRowsOverlayComponent: (event) => {
|
|
2844
2853
|
let rowCount = 0;
|
|
2845
2854
|
event.api.forEachNode(() => rowCount++);
|
|
2846
2855
|
return (jsx(GridNoRowsOverlay, { loading: !rowData || params.loading === true, rowCount: rowCount, headerRowHeight: headerRowCount * rowHeight, filteredRowCount: event.api.getDisplayedRowCount(), noRowsOverlayText: params.noRowsOverlayText, noRowsMatchingOverlayText: params.noRowsMatchingOverlayText }));
|
|
@@ -2851,18 +2860,20 @@ const GridPopoverContext = createContext({
|
|
|
2851
2860
|
anchorRef: { current: null },
|
|
2852
2861
|
saving: false,
|
|
2853
2862
|
setSaving: () => { },
|
|
2854
|
-
colId:
|
|
2855
|
-
field:
|
|
2863
|
+
colId: '',
|
|
2864
|
+
field: '',
|
|
2856
2865
|
value: null,
|
|
2857
2866
|
data: {},
|
|
2858
2867
|
selectedRows: [],
|
|
2859
|
-
updateValue:
|
|
2860
|
-
formatValue: () =>
|
|
2868
|
+
updateValue: () => false,
|
|
2869
|
+
formatValue: () => '! No gridPopoverContextProvider !',
|
|
2861
2870
|
});
|
|
2862
2871
|
const useGridPopoverContext = () => useContext(GridPopoverContext);
|
|
2863
2872
|
|
|
2864
|
-
const GridPopoverContextProvider = (
|
|
2873
|
+
const GridPopoverContextProvider = (props2) => {
|
|
2865
2874
|
const { getFilteredSelectedRows, updatingCells } = useContext(GridContext);
|
|
2875
|
+
const children = props2.children;
|
|
2876
|
+
const props = props2.props;
|
|
2866
2877
|
const anchorRef = useRef(props.eGridCell);
|
|
2867
2878
|
const hasSaved = useRef(false);
|
|
2868
2879
|
const [saving, setSaving] = useState(false);
|
|
@@ -2871,8 +2882,8 @@ const GridPopoverContextProvider = ({ props, children }) => {
|
|
|
2871
2882
|
const multiEdit = cellEditorParams?.multiEdit ?? false;
|
|
2872
2883
|
// Then item that is clicked on will always be first in the list
|
|
2873
2884
|
const selectedRows = useMemo(() => multiEdit ? sortBy(getFilteredSelectedRows(), (row) => row.id !== props.data.id) : [props.data], [getFilteredSelectedRows, multiEdit, props.data]);
|
|
2874
|
-
const field = props.colDef?.field ??
|
|
2875
|
-
const colId = props.colDef?.colId ?? field ??
|
|
2885
|
+
const field = props.colDef?.field ?? '';
|
|
2886
|
+
const colId = props.colDef?.colId ?? field ?? '';
|
|
2876
2887
|
const updateValue = useCallback(async (saveFn, tabDirection) => {
|
|
2877
2888
|
if (hasSaved.current)
|
|
2878
2889
|
return true;
|
|
@@ -2894,7 +2905,7 @@ const GridPopoverContextProvider = ({ props, children }) => {
|
|
|
2894
2905
|
};
|
|
2895
2906
|
|
|
2896
2907
|
const GridCellMultiSelectClassRules = {
|
|
2897
|
-
|
|
2908
|
+
'ag-selected-for-edit': (params) => {
|
|
2898
2909
|
const { api, node, colDef } = params;
|
|
2899
2910
|
const cep = colDef.cellEditorSelector
|
|
2900
2911
|
? colDef.cellEditorSelector(params)?.params
|
|
@@ -2908,9 +2919,9 @@ const GridCellMultiSelectClassRules = {
|
|
|
2908
2919
|
},
|
|
2909
2920
|
};
|
|
2910
2921
|
|
|
2911
|
-
const GridIcon = (props) => (jsx(LuiIcon, { name: props.icon, title: props.title, alt: props.title, size: props.size ??
|
|
2922
|
+
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') }));
|
|
2912
2923
|
|
|
2913
|
-
const GridLoadableCell = () => (jsx(LuiMiniSpinner, { size: 22, divProps: { className:
|
|
2924
|
+
const GridLoadableCell = () => (jsx(LuiMiniSpinner, { size: 22, divProps: { className: 'GridLoadableCell-container', role: 'status', 'aria-label': 'Loading' } }));
|
|
2914
2925
|
|
|
2915
2926
|
const GridCellRenderer = (props) => {
|
|
2916
2927
|
const { checkUpdating } = useContext(GridUpdatingContext);
|
|
@@ -2921,38 +2932,38 @@ const GridCellRenderer = (props) => {
|
|
|
2921
2932
|
const infoFn = rendererParams?.info;
|
|
2922
2933
|
let infoText = props.data !== undefined && infoFn ? infoFn(props) : undefined;
|
|
2923
2934
|
if (Array.isArray(warningText))
|
|
2924
|
-
warningText = warningText.join(
|
|
2935
|
+
warningText = warningText.join('\n');
|
|
2925
2936
|
if (Array.isArray(infoText))
|
|
2926
|
-
infoText = infoText.join(
|
|
2927
|
-
return checkUpdating(colDef.field ?? colDef.colId ??
|
|
2937
|
+
infoText = infoText.join('\n');
|
|
2938
|
+
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 }))] }));
|
|
2928
2939
|
};
|
|
2929
2940
|
const suppressCellKeyboardEvents = (e) => {
|
|
2930
2941
|
const shortcutKeys = e.colDef.cellRendererParams?.shortcutKeys ?? {};
|
|
2931
2942
|
const exec = shortcutKeys[e.event.key];
|
|
2932
|
-
if (exec && !e.editing && !e.event.repeat && e.event.type ===
|
|
2943
|
+
if (exec && !e.editing && !e.event.repeat && e.event.type === 'keydown') {
|
|
2933
2944
|
const editable = fnOrVar(e.colDef?.editable, e);
|
|
2934
2945
|
return editable ? (exec(e) ?? true) : true;
|
|
2935
2946
|
}
|
|
2936
2947
|
// It's important that aggrid doesn't trigger edit on enter
|
|
2937
2948
|
// as the incorrect selected rows will be returned
|
|
2938
|
-
return ![
|
|
2949
|
+
return !['ArrowLeft', 'ArrowRight', 'ArrowDown', 'ArrowUp', 'Tab', ' ', 'Home', 'End', 'PageUp', 'PageDown'].includes(e.event.key);
|
|
2939
2950
|
};
|
|
2940
2951
|
const generateFilterGetter = (field, filterValueGetter, valueFormatter) => {
|
|
2941
2952
|
if (filterValueGetter)
|
|
2942
2953
|
return filterValueGetter;
|
|
2943
2954
|
// aggrid will default to valueGetter
|
|
2944
|
-
if (typeof valueFormatter !==
|
|
2955
|
+
if (typeof valueFormatter !== 'function' || !field)
|
|
2945
2956
|
return undefined;
|
|
2946
2957
|
return (params) => {
|
|
2947
2958
|
const value = params.getValue(field);
|
|
2948
2959
|
let formattedValue = valueFormatter({ ...params, value });
|
|
2949
2960
|
// Search for null values using standard dash
|
|
2950
|
-
if (formattedValue ===
|
|
2951
|
-
formattedValue +=
|
|
2961
|
+
if (formattedValue === '–')
|
|
2962
|
+
formattedValue += ' -';
|
|
2952
2963
|
// Search by raw value as well as formatted
|
|
2953
|
-
const gotValue = [
|
|
2954
|
-
return (formattedValue + (gotValue != null && formattedValue != gotValue ?
|
|
2955
|
-
.replace(/\s+/g,
|
|
2964
|
+
const gotValue = ['string', 'number'].includes(typeof value) ? value : undefined;
|
|
2965
|
+
return (formattedValue + (gotValue != null && formattedValue != gotValue ? ' ' + gotValue : '')) //
|
|
2966
|
+
.replace(/\s+/g, ' ')
|
|
2956
2967
|
.trim();
|
|
2957
2968
|
};
|
|
2958
2969
|
};
|
|
@@ -2995,15 +3006,15 @@ const GridCell = (props, custom) => {
|
|
|
2995
3006
|
// Default value formatter, otherwise react freaks out on objects
|
|
2996
3007
|
valueFormatter: (params) => {
|
|
2997
3008
|
if (params.value == null)
|
|
2998
|
-
return
|
|
2999
|
-
const types = [
|
|
3009
|
+
return '–';
|
|
3010
|
+
const types = ['number', 'boolean', 'string'];
|
|
3000
3011
|
if (types.includes(typeof params.value))
|
|
3001
3012
|
return `${params.value}`;
|
|
3002
3013
|
else
|
|
3003
3014
|
return JSON.stringify(params.value);
|
|
3004
3015
|
},
|
|
3005
3016
|
...props,
|
|
3006
|
-
cellRenderer: typeof props.cellRenderer ===
|
|
3017
|
+
cellRenderer: typeof props.cellRenderer === 'string' ? props.cellRenderer : GridCellRenderer,
|
|
3007
3018
|
cellRendererParams: {
|
|
3008
3019
|
originalCellRenderer: props.cellRenderer,
|
|
3009
3020
|
...props.cellRendererParams,
|
|
@@ -3019,16 +3030,16 @@ const GenericCellEditorComponentWrapper = (editor) => {
|
|
|
3019
3030
|
return forwardRef(function GenericCellEditorComponentFr(cellEditorParams, _) {
|
|
3020
3031
|
const valueFormatted = cellEditorParams.formatValue
|
|
3021
3032
|
? cellEditorParams.formatValue(cellEditorParams.value)
|
|
3022
|
-
:
|
|
3023
|
-
return (
|
|
3033
|
+
: 'Missing formatter';
|
|
3034
|
+
return (jsx(GridPopoverContextProvider, { props: cellEditorParams, children: jsxs(Fragment, { children: [jsx(cellEditorParams.colDef.cellRenderer, { ...cellEditorParams, valueFormatted: valueFormatted, ...cellEditorParams.colDef.cellRendererParams }), obj.editor && jsx(obj.editor, { ...cellEditorParams })] }) }));
|
|
3024
3035
|
});
|
|
3025
3036
|
};
|
|
3026
3037
|
|
|
3027
|
-
const GridCellFillerColId =
|
|
3038
|
+
const GridCellFillerColId = 'gridCellFiller';
|
|
3028
3039
|
const isGridCellFiller = (col) => col.colId === GridCellFillerColId;
|
|
3029
3040
|
const GridCellFiller = () => ({
|
|
3030
3041
|
colId: GridCellFillerColId,
|
|
3031
|
-
headerName:
|
|
3042
|
+
headerName: '',
|
|
3032
3043
|
flex: 1,
|
|
3033
3044
|
});
|
|
3034
3045
|
|
|
@@ -3046,8 +3057,27 @@ const GridCellMultiEditor = (props, cellEditorSelector) => GridCell({
|
|
|
3046
3057
|
...props,
|
|
3047
3058
|
});
|
|
3048
3059
|
|
|
3049
|
-
const
|
|
3050
|
-
|
|
3060
|
+
const useGridFilter = (filter) => {
|
|
3061
|
+
const { addExternalFilter, removeExternalFilter } = useContext(GridContext);
|
|
3062
|
+
useEffect(() => {
|
|
3063
|
+
const thisFilter = filter;
|
|
3064
|
+
thisFilter && addExternalFilter(thisFilter);
|
|
3065
|
+
return () => {
|
|
3066
|
+
thisFilter && removeExternalFilter(thisFilter);
|
|
3067
|
+
};
|
|
3068
|
+
}, [addExternalFilter, filter, removeExternalFilter]);
|
|
3069
|
+
};
|
|
3070
|
+
|
|
3071
|
+
const GridFilterButtons = ({ className, luiButtonProps, options, }) => {
|
|
3072
|
+
// Select defaultSelected option, otherwise first option. If no options select none.
|
|
3073
|
+
const [selectedOption, setSelectedOption] = useState(options.find((option) => option.defaultSelected) ?? options[0]);
|
|
3074
|
+
const filter = useMemo(() => selectedOption?.filter, [selectedOption]);
|
|
3075
|
+
useGridFilter(filter);
|
|
3076
|
+
return (jsx("div", { className: clsx(className, 'flex-col-center'), children: jsx(LuiButtonGroup, { children: options.map((option, index) => (jsx(LuiButton, { ...luiButtonProps, className: clsx(`lui-button lui-button-secondary`, selectedOption?.label === option.label && `lui-button-active`, luiButtonProps?.className), onClick: () => setSelectedOption(option), children: option.label }, `${index}`))) }) }));
|
|
3077
|
+
};
|
|
3078
|
+
|
|
3079
|
+
const GridFilterHeaderIconButton = forwardRef(function columnsButton({ icon, title, onClick, buttonProps, disabled = false, size = 'md' }, ref) {
|
|
3080
|
+
return (jsx(LuiButton, { ...buttonProps, type: 'button', level: 'tertiary', className: 'lui-button-icon-only', ref: ref, buttonProps: { 'aria-label': title }, title: title, onClick: onClick, disabled: disabled, children: jsx(LuiIcon, { name: icon, alt: 'Menu', size: size }) }));
|
|
3051
3081
|
});
|
|
3052
3082
|
|
|
3053
3083
|
const GridFilterColumnsToggle = ({ saveState = true }) => {
|
|
@@ -3055,9 +3085,9 @@ const GridFilterColumnsToggle = ({ saveState = true }) => {
|
|
|
3055
3085
|
const { getColumns, getColumnIds, invisibleColumnIds, setInvisibleColumnIds } = useContext(GridContext);
|
|
3056
3086
|
const columnStorageKey = useMemo(() => isEmpty(getColumnIds())
|
|
3057
3087
|
? null // Grid hasn't been initialised yet
|
|
3058
|
-
:
|
|
3088
|
+
: 'stepAgGrid_invisibleColumnIds_' + getColumnIds().join('_'), [getColumnIds]);
|
|
3059
3089
|
// infer the invisible ids from colDefs
|
|
3060
|
-
const resetColumns = useCallback(() => setInvisibleColumnIds(getColumnIds(
|
|
3090
|
+
const resetColumns = useCallback(() => setInvisibleColumnIds(getColumnIds('initialHide')), [getColumnIds, setInvisibleColumnIds]);
|
|
3061
3091
|
// Load state on start
|
|
3062
3092
|
useEffect(() => {
|
|
3063
3093
|
if (!columnStorageKey || loaded)
|
|
@@ -3069,11 +3099,11 @@ const GridFilterColumnsToggle = ({ saveState = true }) => {
|
|
|
3069
3099
|
resetColumns();
|
|
3070
3100
|
}
|
|
3071
3101
|
else {
|
|
3072
|
-
const invisibleIds = JSON.parse(stored ??
|
|
3102
|
+
const invisibleIds = JSON.parse(stored ?? '[]');
|
|
3073
3103
|
if (!Array.isArray(invisibleIds)) {
|
|
3074
3104
|
console.error(`stored invisible ids not an array: ${stored}`);
|
|
3075
3105
|
}
|
|
3076
|
-
else if (!invisibleIds.every((id) => typeof id ===
|
|
3106
|
+
else if (!invisibleIds.every((id) => typeof id === 'string')) {
|
|
3077
3107
|
console.error(`stored invisible ids not strings: ${stored}`);
|
|
3078
3108
|
}
|
|
3079
3109
|
else {
|
|
@@ -3103,15 +3133,15 @@ const GridFilterColumnsToggle = ({ saveState = true }) => {
|
|
|
3103
3133
|
}, [invisibleColumnIds, setInvisibleColumnIds]);
|
|
3104
3134
|
const numericRegExp = /^\d+$/;
|
|
3105
3135
|
const isNonManageableColumn = (col) => col.lockVisible || col.colId == null || numericRegExp.test(col.colId);
|
|
3106
|
-
return (jsxs(Menu, { menuButton: jsx(GridFilterHeaderIconButton, { icon:
|
|
3136
|
+
return (jsxs(Menu, { menuButton: jsx(GridFilterHeaderIconButton, { icon: 'ic_columns', title: 'Column visibility' }), menuClassName: 'step-ag-grid-react-menu', portal: true, unmountOnClose: true, children: [jsx("div", { className: 'GridFilterColumnsToggle-container', children: getColumns('headerName').map((col) => (jsx(MenuItem, { disabled: isNonManageableColumn(col), onClick: (e) => {
|
|
3107
3137
|
// Global react-menu MenuItem handler handles tabs
|
|
3108
|
-
if (e.key !==
|
|
3138
|
+
if (e.key !== 'Tab') {
|
|
3109
3139
|
e.keepOpen = true;
|
|
3110
|
-
if (e.key !==
|
|
3140
|
+
if (e.key !== 'Enter') {
|
|
3111
3141
|
toggleColumn(col.colId);
|
|
3112
3142
|
}
|
|
3113
3143
|
}
|
|
3114
|
-
}, children: jsx(LuiCheckboxInput, { isChecked: !!invisibleColumnIds && !invisibleColumnIds.includes(col.colId ??
|
|
3144
|
+
}, children: jsx(LuiCheckboxInput, { isChecked: !!invisibleColumnIds && !invisibleColumnIds.includes(col.colId ?? ''), value: `${col.colId}`, label: col.headerName ?? '', isDisabled: isNonManageableColumn(col), inputProps: {
|
|
3115
3145
|
onClick: (e) => {
|
|
3116
3146
|
// Click is handled by MenuItem onClick so keyboard events work
|
|
3117
3147
|
e.preventDefault();
|
|
@@ -3119,47 +3149,15 @@ const GridFilterColumnsToggle = ({ saveState = true }) => {
|
|
|
3119
3149
|
},
|
|
3120
3150
|
}, onChange: () => {
|
|
3121
3151
|
/*Do nothing, change handled by menuItem*/
|
|
3122
|
-
} }) }, col.colId ??
|
|
3152
|
+
} }) }, col.colId ?? 'no_col_id'))) }), jsx(MenuDivider, {}, `$$divider_reset_columns`), jsxs(MenuItem, { onClick: (e) => {
|
|
3123
3153
|
// Global react-menu MenuItem handler handles tabs
|
|
3124
|
-
if (e.key !==
|
|
3125
|
-
e.keepOpen = e.key !==
|
|
3154
|
+
if (e.key !== 'Tab') {
|
|
3155
|
+
e.keepOpen = e.key !== 'Enter';
|
|
3126
3156
|
resetColumns();
|
|
3127
3157
|
}
|
|
3128
|
-
}, children: [jsx(LuiIcon, { name:
|
|
3129
|
-
};
|
|
3130
|
-
|
|
3131
|
-
const useGridFilter = (filter) => {
|
|
3132
|
-
const { addExternalFilter, removeExternalFilter } = useContext(GridContext);
|
|
3133
|
-
useEffect(() => {
|
|
3134
|
-
const thisFilter = filter;
|
|
3135
|
-
thisFilter && addExternalFilter(thisFilter);
|
|
3136
|
-
return () => {
|
|
3137
|
-
thisFilter && removeExternalFilter(thisFilter);
|
|
3138
|
-
};
|
|
3139
|
-
}, [addExternalFilter, filter, removeExternalFilter]);
|
|
3140
|
-
};
|
|
3141
|
-
|
|
3142
|
-
const GridFilterButtons = ({ className, luiButtonProps, options, }) => {
|
|
3143
|
-
// Select defaultSelected option, otherwise first option. If no options select none.
|
|
3144
|
-
const [selectedOption, setSelectedOption] = useState(options.find((option) => option.defaultSelected) ?? options[0]);
|
|
3145
|
-
const filter = useMemo(() => selectedOption?.filter, [selectedOption]);
|
|
3146
|
-
useGridFilter(filter);
|
|
3147
|
-
return (jsx("div", { className: clsx(className, "flex-col-center"), children: jsx(LuiButtonGroup, { children: options.map((option, index) => (jsx(LuiButton, { ...luiButtonProps, className: clsx(`lui-button lui-button-secondary`, selectedOption?.label === option.label && `lui-button-active`, luiButtonProps?.className), onClick: () => setSelectedOption(option), children: option.label }, `${index}`))) }) }));
|
|
3148
|
-
};
|
|
3149
|
-
|
|
3150
|
-
const GridFilterQuick = ({ quickFilterPlaceholder, defaultValue }) => {
|
|
3151
|
-
const { setQuickFilter } = useContext(GridContext);
|
|
3152
|
-
const [quickFilterValue, setQuickFilterValue] = useState(defaultValue ?? "");
|
|
3153
|
-
useEffect(() => {
|
|
3154
|
-
setQuickFilter(quickFilterValue);
|
|
3155
|
-
}, [quickFilterValue, setQuickFilter]);
|
|
3156
|
-
return (jsxs("div", { className: "GridFilterQuick-container", children: [jsx("div", { className: "GridFilterQuick-icon", children: jsx(LuiIcon, { name: "ic_search", alt: "Search", size: "md" }) }), jsx("input", { "aria-label": "Search", className: "GridFilterQuick-input", type: "text", placeholder: quickFilterPlaceholder ?? "Search", value: quickFilterValue, onChange: (event) => {
|
|
3157
|
-
setQuickFilterValue(event.target.value);
|
|
3158
|
-
} })] }));
|
|
3158
|
+
}, children: [jsx(LuiIcon, { name: 'ic_regenerate', alt: 'Reset columns', size: 'md', className: 'MenuItem-icon' }), "Reset columns"] }, '$$reset_columns')] }));
|
|
3159
3159
|
};
|
|
3160
3160
|
|
|
3161
|
-
const GridFilters = ({ children }) => (jsx("div", { className: "Grid-container-filters", children: children }));
|
|
3162
|
-
|
|
3163
3161
|
/**
|
|
3164
3162
|
* Cancels timeouts on scope being destroyed.
|
|
3165
3163
|
*
|
|
@@ -3226,12 +3224,25 @@ const GridFilterDownloadCsvButton = (csvExportParams) => {
|
|
|
3226
3224
|
}, 100);
|
|
3227
3225
|
};
|
|
3228
3226
|
return downloading ? (jsx(LuiMiniSpinner, { size: 22, divProps: {
|
|
3229
|
-
role:
|
|
3230
|
-
[
|
|
3231
|
-
style: { width: 42, display:
|
|
3232
|
-
} })) : (jsx(GridFilterHeaderIconButton, { icon:
|
|
3227
|
+
role: 'status',
|
|
3228
|
+
['aria-label']: 'Downloading...',
|
|
3229
|
+
style: { width: 42, display: 'flex', justifyContent: 'center' },
|
|
3230
|
+
} })) : (jsx(GridFilterHeaderIconButton, { icon: 'ic_csv_file', title: 'Download CSV', onClick: handleDownloadClick, disabled: downloading }));
|
|
3231
|
+
};
|
|
3232
|
+
|
|
3233
|
+
const GridFilterQuick = ({ quickFilterPlaceholder, defaultValue }) => {
|
|
3234
|
+
const { setQuickFilter } = useContext(GridContext);
|
|
3235
|
+
const [quickFilterValue, setQuickFilterValue] = useState(defaultValue ?? '');
|
|
3236
|
+
useEffect(() => {
|
|
3237
|
+
setQuickFilter(quickFilterValue);
|
|
3238
|
+
}, [quickFilterValue, setQuickFilter]);
|
|
3239
|
+
return (jsxs("div", { className: "GridFilterQuick-container", children: [jsx("div", { className: "GridFilterQuick-icon", children: jsx(LuiIcon, { name: 'ic_search', alt: 'Search', size: 'md' }) }), jsx("input", { "aria-label": "Search", className: "GridFilterQuick-input", type: "text", placeholder: quickFilterPlaceholder ?? 'Search', value: quickFilterValue, onChange: (event) => {
|
|
3240
|
+
setQuickFilterValue(event.target.value);
|
|
3241
|
+
} })] }));
|
|
3233
3242
|
};
|
|
3234
3243
|
|
|
3244
|
+
const GridFilters = ({ children }) => (jsx("div", { className: "Grid-container-filters", children: children }));
|
|
3245
|
+
|
|
3235
3246
|
/* global setTimeout, clearTimeout */
|
|
3236
3247
|
|
|
3237
3248
|
var dist = function debounce(fn) {
|
|
@@ -3307,16 +3318,17 @@ function defer() {
|
|
|
3307
3318
|
var debounce = /*@__PURE__*/getDefaultExportFromCjs(dist);
|
|
3308
3319
|
|
|
3309
3320
|
const GridSubComponentContext = createContext({
|
|
3310
|
-
value:
|
|
3321
|
+
value: 'GridSubComponentContext value no context',
|
|
3311
3322
|
data: {},
|
|
3312
3323
|
setValue: () => {
|
|
3313
|
-
console.error(
|
|
3324
|
+
console.error('GridSubComponentContext setValue no context');
|
|
3314
3325
|
},
|
|
3315
3326
|
setValid: () => {
|
|
3316
|
-
console.error(
|
|
3327
|
+
console.error('GridSubComponentContext setValid no context');
|
|
3317
3328
|
},
|
|
3329
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
3318
3330
|
triggerSave: async () => {
|
|
3319
|
-
console.error(
|
|
3331
|
+
console.error('GridSubComponentContext triggerSave no context');
|
|
3320
3332
|
},
|
|
3321
3333
|
context: null,
|
|
3322
3334
|
});
|
|
@@ -3352,7 +3364,7 @@ var css_248z$2 = ".FormError{display:flex}.FormError-helpText{color:#6b6966;font
|
|
|
3352
3364
|
styleInject(css_248z$2);
|
|
3353
3365
|
|
|
3354
3366
|
const FormError = (props) => {
|
|
3355
|
-
return (jsxs(Fragment, { children: [props.error && (jsxs("div", { className: "FormError", children: [jsx(LuiIcon, { alt: "error", name: "ic_error", className: "FormError-text-icon", size: "sm", status: "error" }), jsx("span", { className: "FormError-error", children: props.error })] })), props.helpText && !props.error && jsx("span", { className:
|
|
3367
|
+
return (jsxs(Fragment, { children: [props.error && (jsxs("div", { className: "FormError", children: [jsx(LuiIcon, { alt: "error", name: "ic_error", className: "FormError-text-icon", size: "sm", status: "error" }), jsx("span", { className: "FormError-error", children: props.error })] })), props.helpText && !props.error && jsx("span", { className: 'FormError-helpText', children: props.helpText })] }));
|
|
3356
3368
|
};
|
|
3357
3369
|
|
|
3358
3370
|
function escapeStringRegexp(string) {
|
|
@@ -3492,18 +3504,18 @@ const textMatch = (text, filter) => {
|
|
|
3492
3504
|
if (text == null)
|
|
3493
3505
|
return true;
|
|
3494
3506
|
const superFilters = filter
|
|
3495
|
-
.split(
|
|
3507
|
+
.split(',')
|
|
3496
3508
|
.map((sf) => sf.trim())
|
|
3497
3509
|
.filter((sf) => sf)
|
|
3498
3510
|
.map((r) => r
|
|
3499
3511
|
.split(/\s+/)
|
|
3500
|
-
.map((f) => (f.startsWith(
|
|
3501
|
-
.join(
|
|
3502
|
-
const [negativeFilters, positiveFilters] = partition(superFilters, (superFilters) => superFilters.startsWith(
|
|
3503
|
-
const values = text.replaceAll(
|
|
3512
|
+
.map((f) => (f.startsWith('=') ? f.slice(1) : f + '*'))
|
|
3513
|
+
.join(' '));
|
|
3514
|
+
const [negativeFilters, positiveFilters] = partition(superFilters, (superFilters) => superFilters.startsWith('!'));
|
|
3515
|
+
const values = text.replaceAll(',', ' ').trim().split(/\s+/);
|
|
3504
3516
|
return ((isEmpty(positiveFilters) || // Not filtered
|
|
3505
3517
|
positiveFilters.some((superFilter) => {
|
|
3506
|
-
const subFilters = superFilter.split(/\s+/).map((s) => s.replaceAll(/([!?])/g,
|
|
3518
|
+
const subFilters = superFilter.split(/\s+/).map((s) => s.replaceAll(/([!?])/g, '\\$1'));
|
|
3507
3519
|
return subFilters.every((subFilter) => values.some((value) => isMatch(value, subFilter)));
|
|
3508
3520
|
})) &&
|
|
3509
3521
|
(isEmpty(negativeFilters) ||
|
|
@@ -3540,19 +3552,19 @@ const useGridPopoverHook = (props) => {
|
|
|
3540
3552
|
}
|
|
3541
3553
|
}, [cancelEdit, props, stopEditing, updateValue]);
|
|
3542
3554
|
const popoverWrapper = useCallback((children) => {
|
|
3543
|
-
return (jsx(Fragment, { children: anchorRef.current && (jsxs(ControlledMenu, { state: isOpen ?
|
|
3555
|
+
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) => {
|
|
3544
3556
|
// Prevent menu from closing when modals are invoked
|
|
3545
3557
|
if (event.reason === CloseReason.BLUR)
|
|
3546
3558
|
return;
|
|
3547
|
-
triggerSave(event.reason)
|
|
3548
|
-
}, viewScroll:
|
|
3549
|
-
jsx("div", { className:
|
|
3550
|
-
let reason = e.currentTarget.getAttribute(
|
|
3559
|
+
void triggerSave(event.reason);
|
|
3560
|
+
}, viewScroll: 'auto', dontShrinkIfDirectionIsTop: true, className: props.className, children: [saving && ( // This is the overlay that prevents editing when the editor is saving
|
|
3561
|
+
jsx("div", { className: 'ComponentLoadingWrapper-saveOverlay' })), children, jsx("button", { ref: saveButtonRef, "data-reason": '', onClick: (e) => {
|
|
3562
|
+
let reason = e.currentTarget.getAttribute('data-reason') ?? undefined;
|
|
3551
3563
|
if (props.dontSaveOnExternalClick && reason === CloseReason.BLUR) {
|
|
3552
3564
|
reason = CloseReason.CANCEL;
|
|
3553
3565
|
}
|
|
3554
|
-
triggerSave(reason)
|
|
3555
|
-
}, style: { display:
|
|
3566
|
+
void triggerSave(reason);
|
|
3567
|
+
}, style: { display: 'none' } })] })) }));
|
|
3556
3568
|
}, [anchorRef, isOpen, props.className, props.dontSaveOnExternalClick, saving, triggerSave]);
|
|
3557
3569
|
return {
|
|
3558
3570
|
popoverWrapper,
|
|
@@ -3560,19 +3572,19 @@ const useGridPopoverHook = (props) => {
|
|
|
3560
3572
|
};
|
|
3561
3573
|
};
|
|
3562
3574
|
|
|
3563
|
-
const MenuSeparatorString =
|
|
3575
|
+
const MenuSeparatorString = '_____MENU_SEPARATOR_____';
|
|
3564
3576
|
const MenuSeparator = Object.freeze({ value: MenuSeparatorString });
|
|
3565
|
-
const MenuHeaderString =
|
|
3577
|
+
const MenuHeaderString = '_____MENU_HEADER_____';
|
|
3566
3578
|
const MenuHeaderItem = (title) => {
|
|
3567
3579
|
return { label: title, value: MenuHeaderString };
|
|
3568
3580
|
};
|
|
3569
3581
|
const fieldToString = (field) => {
|
|
3570
|
-
return typeof field
|
|
3582
|
+
return typeof field === 'symbol' ? field.toString() : `${field}`;
|
|
3571
3583
|
};
|
|
3572
3584
|
const GridFormDropDown = (props) => {
|
|
3573
3585
|
const { selectedRows, field, data } = useGridPopoverContext();
|
|
3574
3586
|
// Save triggers during async action processing which triggers another selectItem(), this ref blocks that
|
|
3575
|
-
const [filter, setFilter] = useState(props.filterDefaultValue ??
|
|
3587
|
+
const [filter, setFilter] = useState(props.filterDefaultValue ?? '');
|
|
3576
3588
|
const [filteredValues, setFilteredValues] = useState();
|
|
3577
3589
|
const [options, setOptions] = useState(null);
|
|
3578
3590
|
const subComponentIsValid = useRef(false);
|
|
@@ -3603,12 +3615,12 @@ const GridFormDropDown = (props) => {
|
|
|
3603
3615
|
if (options)
|
|
3604
3616
|
return;
|
|
3605
3617
|
let optionsConf = props.options;
|
|
3606
|
-
(async () => {
|
|
3607
|
-
if (typeof optionsConf
|
|
3618
|
+
void (async () => {
|
|
3619
|
+
if (typeof optionsConf === 'function') {
|
|
3608
3620
|
optionsConf = await optionsConf(selectedRows, filter);
|
|
3609
3621
|
}
|
|
3610
3622
|
if (optionsConf !== undefined) {
|
|
3611
|
-
const optionsList = optionsConf?.map((item) => item == null || typeof item
|
|
3623
|
+
const optionsList = optionsConf?.map((item) => item == null || typeof item === 'string'
|
|
3612
3624
|
? {
|
|
3613
3625
|
value: item,
|
|
3614
3626
|
label: item,
|
|
@@ -3621,16 +3633,16 @@ const GridFormDropDown = (props) => {
|
|
|
3621
3633
|
}, [filter, options, props, selectedRows]);
|
|
3622
3634
|
// Local filtering.
|
|
3623
3635
|
useEffect(() => {
|
|
3624
|
-
if (props.filtered ==
|
|
3636
|
+
if (props.filtered == 'local') {
|
|
3625
3637
|
if (options == null)
|
|
3626
3638
|
return;
|
|
3627
3639
|
setFilteredValues(options
|
|
3628
3640
|
.map((option) => {
|
|
3629
|
-
if (option.label != null && typeof option.label !==
|
|
3630
|
-
console.error(
|
|
3641
|
+
if (option.label != null && typeof option.label !== 'string') {
|
|
3642
|
+
console.error('Cannot filter non-string labels', option);
|
|
3631
3643
|
return undefined;
|
|
3632
3644
|
}
|
|
3633
|
-
return textMatch(option.label ||
|
|
3645
|
+
return textMatch(option.label || '', filter) ? option : undefined;
|
|
3634
3646
|
})
|
|
3635
3647
|
.filter((r) => r !== undefined));
|
|
3636
3648
|
}
|
|
@@ -3641,9 +3653,9 @@ const GridFormDropDown = (props) => {
|
|
|
3641
3653
|
const previousFilter = useRef(filter);
|
|
3642
3654
|
// Reload filtering.
|
|
3643
3655
|
useEffect(() => {
|
|
3644
|
-
if (previousFilter.current != filter && props.filtered ==
|
|
3656
|
+
if (previousFilter.current != filter && props.filtered == 'reload') {
|
|
3645
3657
|
previousFilter.current = filter;
|
|
3646
|
-
reSearchOnFilterChange()
|
|
3658
|
+
void reSearchOnFilterChange();
|
|
3647
3659
|
}
|
|
3648
3660
|
}, [filter, props, reSearchOnFilterChange]);
|
|
3649
3661
|
/**
|
|
@@ -3681,12 +3693,12 @@ const GridFormDropDown = (props) => {
|
|
|
3681
3693
|
});
|
|
3682
3694
|
let lastHeader = null;
|
|
3683
3695
|
let showHeader = null;
|
|
3684
|
-
return popoverWrapper(jsxs(Fragment, { children: [props.filtered && (jsxs("div", { className:
|
|
3696
|
+
return popoverWrapper(jsxs(Fragment, { children: [props.filtered && (jsxs("div", { className: 'GridFormDropDown-filter', children: [jsx(FocusableItem, { className: 'filter-item', onFocus: () => {
|
|
3685
3697
|
setSelectedItem(null);
|
|
3686
3698
|
setSubSelectedValue(null);
|
|
3687
3699
|
subComponentIsValid.current = true;
|
|
3688
|
-
}, children: ({ ref }) => (jsxs("div", { style: { display:
|
|
3689
|
-
!(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:
|
|
3700
|
+
}, 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 &&
|
|
3701
|
+
!(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) => {
|
|
3690
3702
|
showHeader = null;
|
|
3691
3703
|
if (item.value === MenuSeparatorString) {
|
|
3692
3704
|
return jsx(MenuDivider, {}, `$$divider_${index}`);
|
|
@@ -3701,7 +3713,7 @@ const GridFormDropDown = (props) => {
|
|
|
3701
3713
|
lastHeader = null;
|
|
3702
3714
|
}
|
|
3703
3715
|
}
|
|
3704
|
-
return ((!filteredValues || filteredValues.includes(item)) && (jsxs(Fragment$1, { children: [showHeader, jsxs("div", { children: [jsxs(MenuItem, { disabled: !!item.disabled, title: item.disabled && typeof item.disabled !==
|
|
3716
|
+
return ((!filteredValues || filteredValues.includes(item)) && (jsxs(Fragment$1, { children: [showHeader, jsxs("div", { children: [jsxs(MenuItem, { disabled: !!item.disabled, title: item.disabled && typeof item.disabled !== 'boolean' ? item.disabled : '', value: item.value, onFocus: () => {
|
|
3705
3717
|
if (selectedItem !== item) {
|
|
3706
3718
|
setSelectedItem(item);
|
|
3707
3719
|
setSubSelectedValue(null);
|
|
@@ -3712,7 +3724,7 @@ const GridFormDropDown = (props) => {
|
|
|
3712
3724
|
}
|
|
3713
3725
|
}, onClick: (e) => {
|
|
3714
3726
|
e.keepOpen = !!item.subComponent;
|
|
3715
|
-
}, children: [item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`), item.subComponent ?
|
|
3727
|
+
}, children: [item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`), item.subComponent ? '...' : ''] }, `${fieldToString(field)}-${index}`), item.subComponent && selectedItem === item && (jsx(FocusableItem, { className: 'LuiDeprecatedForms', children: () => (jsx(GridSubComponentContext.Provider, { value: {
|
|
3716
3728
|
context: { options },
|
|
3717
3729
|
data,
|
|
3718
3730
|
value: subSelectedValue,
|
|
@@ -3729,7 +3741,7 @@ const GridFormDropDown = (props) => {
|
|
|
3729
3741
|
triggerSave: async () => {
|
|
3730
3742
|
// empty
|
|
3731
3743
|
},
|
|
3732
|
-
}, children: item.subComponent && (jsx("div", { className:
|
|
3744
|
+
}, children: item.subComponent && (jsx("div", { className: 'subComponent', children: jsx(item.subComponent, {}) })) })) }, `${String(item.label)}_subcomponent`))] }, `menu-wrapper-${index}`)] }, `${index}`)));
|
|
3733
3745
|
})] }) })] }));
|
|
3734
3746
|
};
|
|
3735
3747
|
|
|
@@ -3737,45 +3749,45 @@ var css_248z$1 = ".LuiTextInput.GridLuiTextInput{margin-bottom:0}.LuiTextInput-f
|
|
|
3737
3749
|
styleInject(css_248z$1);
|
|
3738
3750
|
|
|
3739
3751
|
const TextInputFormatted = (props) => {
|
|
3740
|
-
return (jsxs("div", { className: clsx(
|
|
3752
|
+
return (jsxs("div", { className: clsx('LuiTextInput GridLuiTextInput Grid-popoverContainer', props.error && 'hasError', props.className), children: [jsxs("span", { className: "LuiTextInput-inputWrapper", children: [jsx("input", { type: 'text', spellCheck: true, defaultValue: props.value, ...omit(props, ['error', 'value', 'helpText', 'formatted', 'className', 'allowTabToSave']), className: 'LuiTextInput-input', onMouseEnter: (e) => {
|
|
3741
3753
|
e.currentTarget.focus();
|
|
3742
3754
|
props.onMouseEnter && props.onMouseEnter(e);
|
|
3743
|
-
}, "data-allowtabtosave": props.allowTabToSave }), jsx("span", { className:
|
|
3755
|
+
}, "data-allowtabtosave": props.allowTabToSave }), jsx("span", { className: 'LuiTextInput-formatted', children: props.formatted })] }), jsx(FormError, { error: props.error, helpText: props.helpText })] }));
|
|
3744
3756
|
};
|
|
3745
3757
|
|
|
3746
3758
|
const bearingValueFormatter = (value) => {
|
|
3747
|
-
const safeValue = typeof value
|
|
3759
|
+
const safeValue = typeof value === 'string' ? parseFloat(value) : value;
|
|
3748
3760
|
if (safeValue == null) {
|
|
3749
|
-
return
|
|
3761
|
+
return '–';
|
|
3750
3762
|
}
|
|
3751
3763
|
return convertDDToDMS(safeValue, false);
|
|
3752
3764
|
};
|
|
3753
3765
|
const bearingCorrectionValueFormatter = (value) => {
|
|
3754
3766
|
const safeValue = value;
|
|
3755
3767
|
if (safeValue == null) {
|
|
3756
|
-
return
|
|
3768
|
+
return '–';
|
|
3757
3769
|
}
|
|
3758
|
-
if (typeof safeValue ===
|
|
3770
|
+
if (typeof safeValue === 'string') {
|
|
3759
3771
|
return convertDDToDMS(bearingNumberParser(safeValue), true, 4);
|
|
3760
3772
|
}
|
|
3761
3773
|
return convertDDToDMS(safeValue, true, 4);
|
|
3762
3774
|
};
|
|
3763
3775
|
const bearingNumberParser = (value) => {
|
|
3764
|
-
if (value ===
|
|
3776
|
+
if (value === '')
|
|
3765
3777
|
return null;
|
|
3766
3778
|
return parseFloat(value);
|
|
3767
3779
|
};
|
|
3768
3780
|
const validMaskForDmsBearing = /^((-)?\d+)?(\.([0-5](\d([0-5](\d(\d+)?)?)?)?)?)?$/;
|
|
3769
3781
|
const bearingStringValidator = (value, customInvalid) => {
|
|
3770
3782
|
value = value.trim();
|
|
3771
|
-
if (value ===
|
|
3783
|
+
if (value === '')
|
|
3772
3784
|
return null;
|
|
3773
3785
|
const match = value.match(validMaskForDmsBearing);
|
|
3774
3786
|
if (!match)
|
|
3775
|
-
return
|
|
3787
|
+
return 'Bearing must be a number in D.MMSSS format';
|
|
3776
3788
|
const decimalPart = match[4];
|
|
3777
3789
|
if (decimalPart != null && decimalPart.length > 5) {
|
|
3778
|
-
return
|
|
3790
|
+
return 'Bearing has a maximum of 5 decimal places';
|
|
3779
3791
|
}
|
|
3780
3792
|
const bearing = parseFloat(value);
|
|
3781
3793
|
return customInvalid ? customInvalid(bearing) : null;
|
|
@@ -3794,9 +3806,9 @@ const bearingStringValidator = (value, customInvalid) => {
|
|
|
3794
3806
|
*/
|
|
3795
3807
|
const convertDDToDMS = (dd, showPositiveSymbol = true, trailingZeroDp = 2) => {
|
|
3796
3808
|
if (dd == null)
|
|
3797
|
-
return
|
|
3809
|
+
return '–';
|
|
3798
3810
|
// toFixed rounds parts up greater than 60, which has to be corrected below
|
|
3799
|
-
const [bearingWholeString, bearingDecimalString] = dd.toFixed(5).split(
|
|
3811
|
+
const [bearingWholeString, bearingDecimalString] = dd.toFixed(5).split('.');
|
|
3800
3812
|
let bearingWhole = Math.abs(parseInt(bearingWholeString));
|
|
3801
3813
|
let minNumeric = parseInt(bearingDecimalString?.substring(0, 2));
|
|
3802
3814
|
let secNumeric = parseInt(bearingDecimalString?.substring(2, 4));
|
|
@@ -3812,11 +3824,11 @@ const convertDDToDMS = (dd, showPositiveSymbol = true, trailingZeroDp = 2) => {
|
|
|
3812
3824
|
if (bearingWhole >= 360) {
|
|
3813
3825
|
bearingWhole -= 360;
|
|
3814
3826
|
}
|
|
3815
|
-
const minString = minNumeric.toString().padStart(2,
|
|
3816
|
-
const secString = secNumeric.toString().padStart(2,
|
|
3827
|
+
const minString = minNumeric.toString().padStart(2, '0');
|
|
3828
|
+
const secString = secNumeric.toString().padStart(2, '0');
|
|
3817
3829
|
const deciSecString = bearingDecimalString?.substring(4, 5);
|
|
3818
|
-
let dmsString = `${showPositiveSymbol && dd > 0 ?
|
|
3819
|
-
if (trailingZeroDp === 5 || deciSecString !=
|
|
3830
|
+
let dmsString = `${showPositiveSymbol && dd > 0 ? '+' : ''}${dd < 0 ? '-' : ''}${bearingWhole}°`;
|
|
3831
|
+
if (trailingZeroDp === 5 || deciSecString != '0') {
|
|
3820
3832
|
dmsString += `\xa0${minString}'\xa0${secString}.${deciSecString}"`; // "\xa0" is here for non-breaking space
|
|
3821
3833
|
}
|
|
3822
3834
|
else if (trailingZeroDp === 4 || secNumeric != 0) {
|
|
@@ -3829,27 +3841,27 @@ const convertDDToDMS = (dd, showPositiveSymbol = true, trailingZeroDp = 2) => {
|
|
|
3829
3841
|
};
|
|
3830
3842
|
const bearingRangeValidator = (value) => {
|
|
3831
3843
|
if (value === null)
|
|
3832
|
-
return
|
|
3844
|
+
return 'Bearing is required';
|
|
3833
3845
|
if (value >= 360)
|
|
3834
|
-
return
|
|
3846
|
+
return 'Bearing must be less than 360 degrees';
|
|
3835
3847
|
if (value < 0)
|
|
3836
|
-
return
|
|
3848
|
+
return 'Bearing must not be negative';
|
|
3837
3849
|
return null;
|
|
3838
3850
|
};
|
|
3839
3851
|
const bearingCorrectionRangeValidator = (value) => {
|
|
3840
3852
|
if (value === null)
|
|
3841
|
-
return
|
|
3853
|
+
return 'Bearing correction is required';
|
|
3842
3854
|
if (value >= 360)
|
|
3843
|
-
return
|
|
3855
|
+
return 'Bearing correction must be less than 360 degrees';
|
|
3844
3856
|
if (value <= -180)
|
|
3845
|
-
return
|
|
3857
|
+
return 'Bearing correction must be greater then -180 degrees';
|
|
3846
3858
|
return null;
|
|
3847
3859
|
};
|
|
3848
3860
|
|
|
3849
3861
|
const GridFormEditBearing = (props) => {
|
|
3850
3862
|
const { field, value: initialValue } = useGridPopoverContext();
|
|
3851
3863
|
// This clears out any scientific precision
|
|
3852
|
-
const defaultValue = useMemo(() => (initialValue == null ?
|
|
3864
|
+
const defaultValue = useMemo(() => (initialValue == null ? '' : parseFloat(parseFloat(initialValue).toFixed(10)).toString()), [initialValue]);
|
|
3853
3865
|
const [value, setValue] = useState(defaultValue);
|
|
3854
3866
|
const invalid = useCallback(() => bearingStringValidator(value, props.range), [props.range, value]);
|
|
3855
3867
|
const save = useCallback(async (selectedRows) => {
|
|
@@ -3863,7 +3875,7 @@ const GridFormEditBearing = (props) => {
|
|
|
3863
3875
|
}
|
|
3864
3876
|
else {
|
|
3865
3877
|
if (field == null) {
|
|
3866
|
-
console.error(
|
|
3878
|
+
console.error('field is not defined in ColDef');
|
|
3867
3879
|
}
|
|
3868
3880
|
else {
|
|
3869
3881
|
selectedRows.forEach((row) => {
|
|
@@ -3878,11 +3890,11 @@ const GridFormEditBearing = (props) => {
|
|
|
3878
3890
|
invalid,
|
|
3879
3891
|
save,
|
|
3880
3892
|
});
|
|
3881
|
-
return popoverWrapper(jsx("div", { className:
|
|
3893
|
+
return popoverWrapper(jsx("div", { className: 'GridFormEditBearing-input', children: jsx(TextInputFormatted, { value: defaultValue, onChange: (e) => {
|
|
3882
3894
|
setValue(e.target.value.trim());
|
|
3883
3895
|
}, autoFocus: true, placeholder: props.placeHolder, formatted: bearingStringValidator(value, props.range) || !props.formatValue
|
|
3884
|
-
?
|
|
3885
|
-
: props.formatValue(bearingNumberParser(value)), error: bearingStringValidator(value, props.range), helpText:
|
|
3896
|
+
? '?'
|
|
3897
|
+
: props.formatValue(bearingNumberParser(value)), error: bearingStringValidator(value, props.range), helpText: 'Press enter or tab to save' }) }));
|
|
3886
3898
|
};
|
|
3887
3899
|
|
|
3888
3900
|
const GridFormMessage = (props) => {
|
|
@@ -3890,11 +3902,11 @@ const GridFormMessage = (props) => {
|
|
|
3890
3902
|
const [message, setMessage] = useState(null);
|
|
3891
3903
|
const { popoverWrapper } = useGridPopoverHook({ className: props.className });
|
|
3892
3904
|
useEffect(() => {
|
|
3893
|
-
(async () => {
|
|
3905
|
+
void (async () => {
|
|
3894
3906
|
setMessage(await props.message(selectedRows));
|
|
3895
|
-
})()
|
|
3907
|
+
})();
|
|
3896
3908
|
}, [props, selectedRows]);
|
|
3897
|
-
return popoverWrapper(jsx(ComponentLoadingWrapper, { loading: message === null, className: clsx(
|
|
3909
|
+
return popoverWrapper(jsx(ComponentLoadingWrapper, { loading: message === null, className: clsx('GridFormMessage-container', props.className), children: jsx(Fragment, { children: message }) }));
|
|
3898
3910
|
};
|
|
3899
3911
|
|
|
3900
3912
|
const GridFormMultiSelect = (props) => {
|
|
@@ -3902,8 +3914,8 @@ const GridFormMultiSelect = (props) => {
|
|
|
3902
3914
|
const subComponentIsValidRef = useRef({});
|
|
3903
3915
|
const optionsInitialising = useRef(false);
|
|
3904
3916
|
const firstInputSubComponent = useRef(null);
|
|
3905
|
-
const [filter, setFilter] = useState(
|
|
3906
|
-
const [initialValues, setInitialValues] = useState(
|
|
3917
|
+
const [filter, setFilter] = useState('');
|
|
3918
|
+
const [initialValues, setInitialValues] = useState('');
|
|
3907
3919
|
const [options, setOptions] = useState();
|
|
3908
3920
|
const invalid = useCallback(() => {
|
|
3909
3921
|
if (!options)
|
|
@@ -3931,8 +3943,8 @@ const GridFormMultiSelect = (props) => {
|
|
|
3931
3943
|
if (options || optionsInitialising.current)
|
|
3932
3944
|
return;
|
|
3933
3945
|
optionsInitialising.current = true;
|
|
3934
|
-
(async () => {
|
|
3935
|
-
const optionsList = typeof props.options ===
|
|
3946
|
+
void (async () => {
|
|
3947
|
+
const optionsList = typeof props.options === 'function' ? await props.options(selectedRows) : props.options;
|
|
3936
3948
|
setInitialValues(JSON.stringify(optionsList));
|
|
3937
3949
|
setOptions(optionsList);
|
|
3938
3950
|
optionsInitialising.current = false;
|
|
@@ -3944,7 +3956,7 @@ const GridFormMultiSelect = (props) => {
|
|
|
3944
3956
|
const headerGroups = useMemo(() => {
|
|
3945
3957
|
if (!options)
|
|
3946
3958
|
return undefined;
|
|
3947
|
-
const result = groupBy(options.filter((o) => textMatch(o.label, filter) && o.value != null),
|
|
3959
|
+
const result = groupBy(options.filter((o) => textMatch(o.label, filter) && o.value != null), 'filter');
|
|
3948
3960
|
// remove leading/trailing/duplicate dividers
|
|
3949
3961
|
return fromPairs(toPairs(result).map(([key, arr]) => {
|
|
3950
3962
|
let lastWasDivider = true;
|
|
@@ -3968,14 +3980,14 @@ const GridFormMultiSelect = (props) => {
|
|
|
3968
3980
|
];
|
|
3969
3981
|
}));
|
|
3970
3982
|
}, [filter, options]);
|
|
3971
|
-
const headers = useMemo(() => props.headers ?? [{ header:
|
|
3983
|
+
const headers = useMemo(() => props.headers ?? [{ header: '' }], [props.headers]);
|
|
3972
3984
|
const { popoverWrapper, triggerSave } = useGridPopoverHook({
|
|
3973
3985
|
className: props.className,
|
|
3974
3986
|
invalid,
|
|
3975
3987
|
save,
|
|
3976
3988
|
});
|
|
3977
|
-
return popoverWrapper(jsx(ComponentLoadingWrapper, { loading: !options, className:
|
|
3978
|
-
(isEmpty(headerGroups) || !toPairs(headerGroups).some(([_, options]) => !isEmpty(options))) && (jsx(MenuItem, { className:
|
|
3989
|
+
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 &&
|
|
3990
|
+
(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) => {
|
|
3979
3991
|
const subOptions = headerGroups[`${header.filter}`];
|
|
3980
3992
|
return (!isEmpty(subOptions) && (jsxs(Fragment$1, { children: [header.header && jsx(MenuHeader, { children: header.header }), subOptions.map((item, index) => item.value === MenuSeparatorString ? (jsx(MenuDivider, {}, `div_${index}`)) : (jsxs(Fragment$1, { children: [jsx(MenuRadioItem, { item: item, options: options, setOptions: setOptions, onChecked: () => {
|
|
3981
3993
|
// Default to focus on first input in subComponent
|
|
@@ -4017,7 +4029,7 @@ const FilterInput = (props) => {
|
|
|
4017
4029
|
return;
|
|
4018
4030
|
const filterTrimmed = filter.trim();
|
|
4019
4031
|
if (isEmpty(filterTrimmed)) {
|
|
4020
|
-
triggerSave()
|
|
4032
|
+
void triggerSave();
|
|
4021
4033
|
return;
|
|
4022
4034
|
}
|
|
4023
4035
|
const preFilterOptions = JSON.stringify(options);
|
|
@@ -4026,15 +4038,15 @@ const FilterInput = (props) => {
|
|
|
4026
4038
|
if (preFilterOptions === JSON.stringify(options))
|
|
4027
4039
|
return;
|
|
4028
4040
|
setOptions([...options]);
|
|
4029
|
-
setFilter(
|
|
4041
|
+
setFilter('');
|
|
4030
4042
|
}, [filter, onSelectFilter, options, setFilter, setOptions, triggerSave]);
|
|
4031
4043
|
const handleKeyDown = useCallback((e) => {
|
|
4032
|
-
if (e.key ===
|
|
4044
|
+
if (e.key === 'Enter') {
|
|
4033
4045
|
enterHasBeenPressed.current = true;
|
|
4034
4046
|
}
|
|
4035
4047
|
}, []);
|
|
4036
4048
|
const handleKeyUp = useCallback((e) => {
|
|
4037
|
-
if (e.key ===
|
|
4049
|
+
if (e.key === 'Enter') {
|
|
4038
4050
|
e.stopPropagation();
|
|
4039
4051
|
e.preventDefault();
|
|
4040
4052
|
if (e.ctrlKey)
|
|
@@ -4042,22 +4054,22 @@ const FilterInput = (props) => {
|
|
|
4042
4054
|
else if (enterHasBeenPressed.current) {
|
|
4043
4055
|
const filterTrimmed = filter.trim();
|
|
4044
4056
|
if (isEmpty(filterTrimmed)) {
|
|
4045
|
-
triggerSave()
|
|
4057
|
+
void triggerSave();
|
|
4046
4058
|
return;
|
|
4047
4059
|
}
|
|
4048
4060
|
onSelectFilter && addCustomFilterValue();
|
|
4049
4061
|
}
|
|
4050
4062
|
lastKeyWasEnter.current = true;
|
|
4051
4063
|
}
|
|
4052
|
-
else if (e.key ===
|
|
4053
|
-
lastKeyWasEnter.current && setFilter(
|
|
4064
|
+
else if (e.key === 'Control') {
|
|
4065
|
+
lastKeyWasEnter.current && setFilter('');
|
|
4054
4066
|
lastKeyWasEnter.current = false;
|
|
4055
4067
|
}
|
|
4056
4068
|
else {
|
|
4057
4069
|
lastKeyWasEnter.current = false;
|
|
4058
4070
|
}
|
|
4059
4071
|
}, [addCustomFilterValue, filter, onSelectFilter, setFilter, toggleSelectAllVisible, triggerSave]);
|
|
4060
|
-
return (jsxs(Fragment, { children: [jsx(FocusableItem, { className:
|
|
4072
|
+
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`)] }));
|
|
4061
4073
|
};
|
|
4062
4074
|
const MenuRadioItem = (props) => {
|
|
4063
4075
|
const { item, options, setOptions } = props;
|
|
@@ -4067,12 +4079,12 @@ const MenuRadioItem = (props) => {
|
|
|
4067
4079
|
}, [options, setOptions]);
|
|
4068
4080
|
return (jsx(MenuItem, { onClick: (e) => {
|
|
4069
4081
|
// Global react-menu MenuItem handler handles tabs
|
|
4070
|
-
if (e.key !==
|
|
4082
|
+
if (e.key !== 'Tab' && e.key !== 'Enter') {
|
|
4071
4083
|
e.keepOpen = true;
|
|
4072
4084
|
toggleValue(item);
|
|
4073
4085
|
}
|
|
4074
4086
|
item.checked && props.onChecked && props.onChecked();
|
|
4075
|
-
}, children: jsx(LuiCheckboxInput, { isChecked: item.checked ?? false, value: `${item.value}`, label: jsxs(Fragment, { children: [item.warning && jsx(GridIcon, { icon:
|
|
4087
|
+
}, 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: {
|
|
4076
4088
|
onClick: (e) => {
|
|
4077
4089
|
// Click is handled by MenuItem onClick
|
|
4078
4090
|
e.preventDefault();
|
|
@@ -4088,8 +4100,8 @@ function MenuSubComponentFr(props, ref) {
|
|
|
4088
4100
|
const focusableRef = React.useRef(null);
|
|
4089
4101
|
useEffect(() => {
|
|
4090
4102
|
if (focusableRef.current) {
|
|
4091
|
-
const firstInputElement = focusableRef.current.querySelectorAll(
|
|
4092
|
-
if (typeof ref ===
|
|
4103
|
+
const firstInputElement = focusableRef.current.querySelectorAll('input')[0] ?? null;
|
|
4104
|
+
if (typeof ref === 'function') {
|
|
4093
4105
|
ref(firstInputElement);
|
|
4094
4106
|
}
|
|
4095
4107
|
else if (ref) {
|
|
@@ -4097,7 +4109,7 @@ function MenuSubComponentFr(props, ref) {
|
|
|
4097
4109
|
}
|
|
4098
4110
|
}
|
|
4099
4111
|
}, [ref]);
|
|
4100
|
-
return (jsx(FocusableItem, { className:
|
|
4112
|
+
return (jsx(FocusableItem, { className: 'LuiDeprecatedForms', ref: focusableRef, children: () => (jsx(GridSubComponentContext.Provider, { value: {
|
|
4101
4113
|
context: { options },
|
|
4102
4114
|
data,
|
|
4103
4115
|
value: item.subValue,
|
|
@@ -4109,7 +4121,7 @@ function MenuSubComponentFr(props, ref) {
|
|
|
4109
4121
|
subComponentIsValid[`${item.value}`] = valid;
|
|
4110
4122
|
},
|
|
4111
4123
|
triggerSave,
|
|
4112
|
-
}, children: jsx("div", { className:
|
|
4124
|
+
}, children: jsx("div", { className: 'subComponent', children: item.subComponent && jsx(item.subComponent, {}) }) })) }, `${item.value}_subcomponent`));
|
|
4113
4125
|
}
|
|
4114
4126
|
|
|
4115
4127
|
const GridFormMultiSelectGrid = (props) => {
|
|
@@ -4154,33 +4166,33 @@ const GridFormMultiSelectGrid = (props) => {
|
|
|
4154
4166
|
if (options || optionsInitialising.current)
|
|
4155
4167
|
return;
|
|
4156
4168
|
optionsInitialising.current = true;
|
|
4157
|
-
(async () => {
|
|
4158
|
-
const optionsList = typeof props.options ===
|
|
4159
|
-
setInitialValues(optionsList.map((o) => ({ ...o, label:
|
|
4169
|
+
void (async () => {
|
|
4170
|
+
const optionsList = typeof props.options === 'function' ? await props.options(selectedRows) : props.options;
|
|
4171
|
+
setInitialValues(optionsList.map((o) => ({ ...o, label: '' })));
|
|
4160
4172
|
setOptions(optionsList);
|
|
4161
4173
|
optionsInitialising.current = false;
|
|
4162
4174
|
})();
|
|
4163
4175
|
}, [options, props, selectedRows]);
|
|
4164
4176
|
const toggleValue = useCallback((item) => {
|
|
4165
4177
|
if (options) {
|
|
4166
|
-
item.checked = item.checked === true && item.canSelectPartial ?
|
|
4178
|
+
item.checked = item.checked === true && item.canSelectPartial ? 'partial' : !item.checked;
|
|
4167
4179
|
setOptions([...options]);
|
|
4168
4180
|
}
|
|
4169
4181
|
}, [options, setOptions]);
|
|
4170
|
-
return popoverWrapper(jsx(Fragment, { children: jsx("div", { className:
|
|
4171
|
-
display:
|
|
4172
|
-
gridAutoFlow:
|
|
4182
|
+
return popoverWrapper(jsx(Fragment, { children: jsx("div", { className: 'Grid-popoverContainer', children: jsx("div", { style: {
|
|
4183
|
+
display: 'grid',
|
|
4184
|
+
gridAutoFlow: 'column',
|
|
4173
4185
|
gridTemplateRows: `repeat(${props.maxRowCount ?? 10}, auto)`,
|
|
4174
|
-
maxWidth:
|
|
4175
|
-
overflowX:
|
|
4186
|
+
maxWidth: 'calc(min(100vw,500px))',
|
|
4187
|
+
overflowX: 'auto',
|
|
4176
4188
|
}, children: options &&
|
|
4177
4189
|
options.map((o) => (jsx(Fragment, { children: jsx(MenuItem, { onClick: (e) => {
|
|
4178
4190
|
// Global react-menu MenuItem handler handles tabs
|
|
4179
|
-
if (e.key !==
|
|
4191
|
+
if (e.key !== 'Tab' && e.key !== 'Enter') {
|
|
4180
4192
|
e.keepOpen = true;
|
|
4181
4193
|
toggleValue(o);
|
|
4182
4194
|
}
|
|
4183
|
-
}, children: jsx(LuiCheckboxInput, { isChecked: !!o.checked, isIndeterminate: o.checked ===
|
|
4195
|
+
}, children: jsx(LuiCheckboxInput, { isChecked: !!o.checked, isIndeterminate: o.checked === 'partial', value: `${o.value}`, label: jsxs(Fragment, { children: [o.warning && jsx(GridIcon, { icon: 'ic_warning_outline', title: o.warning }, '$$icon$$'), jsx("span", { className: 'GridMultiSelectGrid-Label', children: o.label ?? (o.value == null ? `<${o.value}>` : `${o.value}`) }, '$$label$$')] }), inputProps: {
|
|
4184
4196
|
onClick: (e) => {
|
|
4185
4197
|
// Click is handled by MenuItem onClick
|
|
4186
4198
|
e.preventDefault();
|
|
@@ -4192,7 +4204,7 @@ const GridFormMultiSelectGrid = (props) => {
|
|
|
4192
4204
|
};
|
|
4193
4205
|
|
|
4194
4206
|
/** Menu configuration types **/
|
|
4195
|
-
const __isMenuSeparator__ =
|
|
4207
|
+
const __isMenuSeparator__ = '__isMenuSeparator__';
|
|
4196
4208
|
const PopoutMenuSeparator = Object.freeze({ label: __isMenuSeparator__ });
|
|
4197
4209
|
/**
|
|
4198
4210
|
* NOTE: If the popout menu doesn't appear on single click when also selecting row it's because
|
|
@@ -4209,7 +4221,7 @@ const GridFormPopoverMenu = (props) => {
|
|
|
4209
4221
|
if (props.defaultAction)
|
|
4210
4222
|
await props.defaultAction(params);
|
|
4211
4223
|
else
|
|
4212
|
-
console.error(`No action specified for ${params.menuOption.label} menu options`);
|
|
4224
|
+
console.error(`No action specified for ${String(params.menuOption.label)} menu options`);
|
|
4213
4225
|
}, [props]);
|
|
4214
4226
|
// Load up options list if it's async function
|
|
4215
4227
|
useEffect(() => {
|
|
@@ -4217,8 +4229,8 @@ const GridFormPopoverMenu = (props) => {
|
|
|
4217
4229
|
return;
|
|
4218
4230
|
optionsInitialising.current = true;
|
|
4219
4231
|
const optionsConf = props.options ?? [];
|
|
4220
|
-
(async () => {
|
|
4221
|
-
const newOptions = typeof optionsConf
|
|
4232
|
+
void (async () => {
|
|
4233
|
+
const newOptions = typeof optionsConf === 'function' ? await optionsConf(selectedRows) : optionsConf;
|
|
4222
4234
|
setOptions(newOptions);
|
|
4223
4235
|
if (!props.defaultAction) {
|
|
4224
4236
|
const anyOptionsAreMissingAction = newOptions.some((option) => !option.action);
|
|
@@ -4248,7 +4260,7 @@ const GridFormPopoverMenu = (props) => {
|
|
|
4248
4260
|
else {
|
|
4249
4261
|
subComponentIsValid.current = true;
|
|
4250
4262
|
setSubSelectedValue(null);
|
|
4251
|
-
await updateValue(async () => actionClick(item), e.key ===
|
|
4263
|
+
await updateValue(async () => actionClick(item), e.key === 'Tab' ? (e.shiftKey ? -1 : 1) : 0);
|
|
4252
4264
|
}
|
|
4253
4265
|
}, [actionClick, subComponentSelected, updateValue]);
|
|
4254
4266
|
const save = useCallback(async () => {
|
|
@@ -4266,7 +4278,7 @@ const GridFormPopoverMenu = (props) => {
|
|
|
4266
4278
|
invalid: () => subComponentSelected && !subComponentIsValid.current,
|
|
4267
4279
|
save,
|
|
4268
4280
|
});
|
|
4269
|
-
return popoverWrapper(jsx(ComponentLoadingWrapper, { loading: !options, className:
|
|
4281
|
+
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$1, { children: [item.subMenu ? (jsx(SubMenu, { disabled: !!item.disabled, label: item.label, title: item.disabled && typeof item.disabled !== 'boolean' ? item.disabled : '', children: jsx(item.subMenu, {}) }, `${String(item.label)}`)) : (jsx(MenuItem, { onClick: (e) => void onMenuItemClick(e, item), disabled: !!item.disabled, title: item.disabled && typeof item.disabled !== 'boolean' ? item.disabled : '', children: item.label }, `${String(item.label)}`)), item.subComponent && subComponentSelected === item && (jsx(FocusableItem, { className: 'LuiDeprecatedForms', children: () => (jsx(GridSubComponentContext.Provider, { value: {
|
|
4270
4282
|
context: {},
|
|
4271
4283
|
data,
|
|
4272
4284
|
value: subSelectedValue,
|
|
@@ -4277,13 +4289,13 @@ const GridFormPopoverMenu = (props) => {
|
|
|
4277
4289
|
subComponentIsValid.current = valid;
|
|
4278
4290
|
},
|
|
4279
4291
|
triggerSave,
|
|
4280
|
-
}, children: jsx("div", { className:
|
|
4292
|
+
}, children: jsx("div", { className: 'subComponent', children: item.subComponent && jsx(item.subComponent, {}) }) })) }, `${String(item.label)}_subcomponent`))] }, `${String(item.label)}`))))) }) }));
|
|
4281
4293
|
};
|
|
4282
4294
|
|
|
4283
4295
|
const TextAreaInput = (props) => {
|
|
4284
4296
|
const reactId = useId();
|
|
4285
4297
|
const id = props?.id ?? reactId;
|
|
4286
|
-
return (jsxs("div", { className: clsx(
|
|
4298
|
+
return (jsxs("div", { className: clsx('LuiTextAreaInput Grid-popoverContainer', props.disabled ? 'isDisabled' : '', props.error ? 'hasError' : '', props.className), children: [jsxs("label", { htmlFor: id, children: [props.mandatory != null && jsx("span", { className: "LuiTextAreaInput-mandatory", children: "*" }), props.label != null && jsx("span", { className: "LuiTextAreaInput-label", children: props.label }), jsx("div", { className: "LuiTextAreaInput-wrapper", children: jsx("textarea", { rows: 5, ...omit(props, ['error', 'value', 'helpText', 'formatted', 'className', 'allowTabToSave']), id: id, value: props.value ?? '', spellCheck: true, onMouseEnter: (e) => {
|
|
4287
4299
|
if (document.activeElement != e.currentTarget) {
|
|
4288
4300
|
e.currentTarget.focus();
|
|
4289
4301
|
e.currentTarget.selectionStart = e.currentTarget.value.length;
|
|
@@ -4296,11 +4308,11 @@ const TextInputValidator = (props, value, data, context) => {
|
|
|
4296
4308
|
if (value == null)
|
|
4297
4309
|
return null;
|
|
4298
4310
|
// This can happen because subcomponent is invoked without type safety
|
|
4299
|
-
if (typeof value !==
|
|
4300
|
-
return
|
|
4311
|
+
if (typeof value !== 'string') {
|
|
4312
|
+
return 'Value is not a string';
|
|
4301
4313
|
}
|
|
4302
|
-
if (props.required && value.trim() ==
|
|
4303
|
-
return
|
|
4314
|
+
if (props.required && value.trim() == '') {
|
|
4315
|
+
return 'Must not be empty';
|
|
4304
4316
|
}
|
|
4305
4317
|
if (props.maxLength && value.length > props.maxLength) {
|
|
4306
4318
|
return `Must be no longer than ${props.maxLength} characters`;
|
|
@@ -4310,10 +4322,10 @@ const TextInputValidator = (props, value, data, context) => {
|
|
|
4310
4322
|
}
|
|
4311
4323
|
const nf = props.numberFormat;
|
|
4312
4324
|
if (nf) {
|
|
4313
|
-
if (value !=
|
|
4325
|
+
if (value != '' && !isFloat(value)) {
|
|
4314
4326
|
return `Must be a valid number`;
|
|
4315
4327
|
}
|
|
4316
|
-
if (value !=
|
|
4328
|
+
if (value != '') {
|
|
4317
4329
|
const number = parseFloat(value);
|
|
4318
4330
|
if (nf.notZero && number === 0) {
|
|
4319
4331
|
return `Must not be 0`;
|
|
@@ -4330,12 +4342,12 @@ const TextInputValidator = (props, value, data, context) => {
|
|
|
4330
4342
|
if (nf.leMax != null && number > nf.leMax) {
|
|
4331
4343
|
return `Must not be greater than ${nf.leMax.toLocaleString()}`;
|
|
4332
4344
|
}
|
|
4333
|
-
if (nf.precision != null && value !=
|
|
4345
|
+
if (nf.precision != null && value != '') {
|
|
4334
4346
|
if (parseFloat(number.toPrecision(nf.precision)) != number) {
|
|
4335
4347
|
return `Must have no more than ${nf.precision} digits precision`;
|
|
4336
4348
|
}
|
|
4337
4349
|
}
|
|
4338
|
-
if (nf.scale != null && value !=
|
|
4350
|
+
if (nf.scale != null && value != '') {
|
|
4339
4351
|
if (parseFloat(number.toFixed(nf.scale)) != number) {
|
|
4340
4352
|
return nf.scale == 0 ? `Must be a whole number` : `Must have no more than ${nf.scale} decimal places`;
|
|
4341
4353
|
}
|
|
@@ -4350,7 +4362,7 @@ const TextInputValidator = (props, value, data, context) => {
|
|
|
4350
4362
|
|
|
4351
4363
|
const GridFormSubComponentTextArea = (props) => {
|
|
4352
4364
|
const { value, data, setValue, setValid, context } = useContext(GridSubComponentContext);
|
|
4353
|
-
const helpText = props.helpText ??
|
|
4365
|
+
const helpText = props.helpText ?? 'Press tab to save';
|
|
4354
4366
|
// If is not initialised yet as it's just been created then set the default value
|
|
4355
4367
|
useEffect(() => {
|
|
4356
4368
|
if (value == null)
|
|
@@ -4360,12 +4372,12 @@ const GridFormSubComponentTextArea = (props) => {
|
|
|
4360
4372
|
useEffect(() => {
|
|
4361
4373
|
setValid(value != null && invalid() == null);
|
|
4362
4374
|
}, [setValid, invalid, value]);
|
|
4363
|
-
return (jsx("div", { className: clsx(
|
|
4375
|
+
return (jsx("div", { className: clsx('FreeTextInput', props.className), children: jsx(TextAreaInput, { className: 'free-text-input', value: value, onChange: (e) => setValue(e.target.value), error: invalid(), helpText: helpText, placeholder: props.placeholder, allowTabToSave: true }) }));
|
|
4364
4376
|
};
|
|
4365
4377
|
|
|
4366
4378
|
const GridFormSubComponentTextInput = (props) => {
|
|
4367
4379
|
const { value, setValue, setValid, data, context } = useContext(GridSubComponentContext);
|
|
4368
|
-
const helpText = props.helpText ??
|
|
4380
|
+
const helpText = props.helpText ?? 'Press enter or tab to save';
|
|
4369
4381
|
// If is not initialised yet as it's just been created then set the default value
|
|
4370
4382
|
useEffect(() => {
|
|
4371
4383
|
if (value == null)
|
|
@@ -4375,14 +4387,14 @@ const GridFormSubComponentTextInput = (props) => {
|
|
|
4375
4387
|
useEffect(() => {
|
|
4376
4388
|
setValid(value != null && invalid() == null);
|
|
4377
4389
|
}, [setValid, invalid, value]);
|
|
4378
|
-
return (jsx(TextInputFormatted, { value: value, error: invalid(), onChange: (e) => setValue(e.target.value), helpText: helpText, placeholder: props.placeholder, style: { width:
|
|
4390
|
+
return (jsx(TextInputFormatted, { value: value, error: invalid(), onChange: (e) => setValue(e.target.value), helpText: helpText, placeholder: props.placeholder, style: { width: '100%' }, allowTabToSave: true }));
|
|
4379
4391
|
};
|
|
4380
4392
|
|
|
4381
4393
|
const GridFormTextArea = (props) => {
|
|
4382
4394
|
const { field, value: initialVale, data } = useGridPopoverContext();
|
|
4383
|
-
const initValue = useMemo(() => (initialVale == null ?
|
|
4395
|
+
const initValue = useMemo(() => (initialVale == null ? '' : `${initialVale}`), [initialVale]);
|
|
4384
4396
|
const [value, setValue] = useState(initValue);
|
|
4385
|
-
const helpText = props.helpText ??
|
|
4397
|
+
const helpText = props.helpText ?? 'Press tab to save';
|
|
4386
4398
|
const invalid = useCallback(() => TextInputValidator(props, value, data, {}), [props, value, data]);
|
|
4387
4399
|
const save = useCallback(async (selectedRows) => {
|
|
4388
4400
|
if (invalid())
|
|
@@ -4402,13 +4414,13 @@ const GridFormTextArea = (props) => {
|
|
|
4402
4414
|
invalid,
|
|
4403
4415
|
save,
|
|
4404
4416
|
});
|
|
4405
|
-
return popoverWrapper(jsx("div", { className:
|
|
4417
|
+
return popoverWrapper(jsx("div", { className: 'subComponent', style: { display: 'flex', flexDirection: 'row', width: props.width ?? 240 }, children: jsx(TextAreaInput, { value: value, onChange: (e) => setValue(e.target.value), error: invalid(), placeholder: props.placeholder ?? 'Type here', helpText: helpText }) }));
|
|
4406
4418
|
};
|
|
4407
4419
|
|
|
4408
4420
|
const GridFormTextInput = (props) => {
|
|
4409
4421
|
const { field, value: initialVale, data } = useGridPopoverContext();
|
|
4410
|
-
const helpText = props.helpText ??
|
|
4411
|
-
const initValue = useMemo(() => (initialVale == null ?
|
|
4422
|
+
const helpText = props.helpText ?? 'Press enter or tab to save';
|
|
4423
|
+
const initValue = useMemo(() => (initialVale == null ? '' : `${initialVale}`), [initialVale]);
|
|
4412
4424
|
const [value, setValue] = useState(initValue);
|
|
4413
4425
|
const invalid = useCallback(() => TextInputValidator(props, value, data, {}), [data, props, value]);
|
|
4414
4426
|
const save = useCallback(async (selectedRows) => {
|
|
@@ -4429,7 +4441,7 @@ const GridFormTextInput = (props) => {
|
|
|
4429
4441
|
invalid,
|
|
4430
4442
|
save,
|
|
4431
4443
|
});
|
|
4432
|
-
return popoverWrapper(jsx("div", { style: { display:
|
|
4444
|
+
return popoverWrapper(jsx("div", { style: { display: 'flex', flexDirection: 'row' }, className: 'FormTest subComponent', children: jsx(TextInputFormatted, { value: value, onChange: (e) => setValue(e.target.value), error: invalid(), formatted: props.units, style: { width: props.width ?? 240 }, placeholder: props.placeholder ?? 'Type here', helpText: helpText }) }));
|
|
4433
4445
|
};
|
|
4434
4446
|
|
|
4435
4447
|
const ButtonCellRenderer = (props) => {
|
|
@@ -4441,24 +4453,24 @@ const ButtonCellRenderer = (props) => {
|
|
|
4441
4453
|
inputRef.current?.focus();
|
|
4442
4454
|
}
|
|
4443
4455
|
};
|
|
4444
|
-
api.addEventListener(
|
|
4456
|
+
api.addEventListener('cellFocused', checkFocus);
|
|
4445
4457
|
return () => {
|
|
4446
|
-
api.removeEventListener(
|
|
4458
|
+
api.removeEventListener('cellFocused', checkFocus);
|
|
4447
4459
|
};
|
|
4448
4460
|
}, [api, column, node.rowIndex]);
|
|
4449
4461
|
return (jsx(LuiButton, { ref: inputRef, className: "lui-button-icon-only", size: "sm", level: "text", onClick: () => {
|
|
4450
4462
|
const selectedRows = [data];
|
|
4451
4463
|
const selectedRowIds = selectedRows.map((r) => r.id);
|
|
4452
4464
|
colDef?.cellEditorParams.onClick?.({ selectedRows, selectedRowIds });
|
|
4453
|
-
}, style: { display: colDef?.cellEditorParams?.visible?.(props) !== false ?
|
|
4465
|
+
}, style: { display: colDef?.cellEditorParams?.visible?.(props) !== false ? '' : 'none' }, children: jsx(LuiIcon, { name: "ic_redo", alt: "revert", size: "md" }) }));
|
|
4454
4466
|
};
|
|
4455
4467
|
const GridButton = (colDef, editor) => {
|
|
4456
4468
|
return GridCell({
|
|
4457
4469
|
minWidth: 72,
|
|
4458
4470
|
maxWidth: 72,
|
|
4459
4471
|
resizable: false,
|
|
4460
|
-
headerClass:
|
|
4461
|
-
cellClass:
|
|
4472
|
+
headerClass: 'GridHeaderAlignCenter',
|
|
4473
|
+
cellClass: 'GridCellAlignCenter',
|
|
4462
4474
|
cellRenderer: ButtonCellRenderer,
|
|
4463
4475
|
cellEditorParams: {
|
|
4464
4476
|
...editor,
|
|
@@ -4476,15 +4488,15 @@ const BooleanCellRenderer = (props) => {
|
|
|
4476
4488
|
inputRef.current?.focus();
|
|
4477
4489
|
}
|
|
4478
4490
|
};
|
|
4479
|
-
api.addEventListener(
|
|
4491
|
+
api.addEventListener('cellFocused', checkFocus);
|
|
4480
4492
|
return () => {
|
|
4481
|
-
api.removeEventListener(
|
|
4493
|
+
api.removeEventListener('cellFocused', checkFocus);
|
|
4482
4494
|
};
|
|
4483
4495
|
}, [api, column, node.rowIndex]);
|
|
4484
4496
|
const isDisabled = !fnOrVar(colDef?.editable, props);
|
|
4485
|
-
return (jsx("div", { className: clsx(
|
|
4486
|
-
|
|
4487
|
-
|
|
4497
|
+
return (jsx("div", { className: clsx('ag-wrapper ag-input-wrapper ag-checkbox-input-wrapper', {
|
|
4498
|
+
'ag-checked': props.value,
|
|
4499
|
+
'ag-disabled': isDisabled,
|
|
4488
4500
|
}), children: jsx("input", { type: "checkbox", className: "ag-input-field-input ag-checkbox-input", disabled: isDisabled, ref: inputRef, checked: value, onChange: () => { }, onClick: (e) => {
|
|
4489
4501
|
e.stopPropagation();
|
|
4490
4502
|
// cell has to be in edit mode
|
|
@@ -4504,7 +4516,7 @@ const BooleanCellRenderer = (props) => {
|
|
|
4504
4516
|
});
|
|
4505
4517
|
const checked = !value;
|
|
4506
4518
|
onValueChange(checked);
|
|
4507
|
-
params.onClick({ selectedRows, selectedRowIds: selectedRows.map((r) => r.id), checked })
|
|
4519
|
+
void params.onClick({ selectedRows, selectedRowIds: selectedRows.map((r) => r.id), checked });
|
|
4508
4520
|
} }) }));
|
|
4509
4521
|
};
|
|
4510
4522
|
const GridEditBoolean = (colDef, editorProps) => {
|
|
@@ -4518,8 +4530,8 @@ const GridEditBoolean = (colDef, editorProps) => {
|
|
|
4518
4530
|
singleClickEdit: true,
|
|
4519
4531
|
resizable: false,
|
|
4520
4532
|
editable: true,
|
|
4521
|
-
cellClass:
|
|
4522
|
-
headerClass:
|
|
4533
|
+
cellClass: 'GridCellAlignCenter',
|
|
4534
|
+
headerClass: 'GridHeaderAlignCenter',
|
|
4523
4535
|
...colDef,
|
|
4524
4536
|
});
|
|
4525
4537
|
};
|
|
@@ -4529,7 +4541,7 @@ const GridPopoutEditMultiSelect = (colDef, props) => GridCell(colDef, {
|
|
|
4529
4541
|
...props,
|
|
4530
4542
|
editorParams: {
|
|
4531
4543
|
...props.editorParams,
|
|
4532
|
-
className: clsx(
|
|
4544
|
+
className: clsx('GridMultiSelect-containerMedium', props.editorParams?.className),
|
|
4533
4545
|
},
|
|
4534
4546
|
});
|
|
4535
4547
|
|
|
@@ -4538,47 +4550,19 @@ const GridPopoutEditMultiSelectGrid = (colDef, props) => GridCell(colDef, {
|
|
|
4538
4550
|
...props,
|
|
4539
4551
|
editorParams: {
|
|
4540
4552
|
...props.editorParams,
|
|
4541
|
-
className: clsx(
|
|
4542
|
-
},
|
|
4543
|
-
});
|
|
4544
|
-
|
|
4545
|
-
const GridRenderPopoutMenuCell = (props) => {
|
|
4546
|
-
const disabled = !fnOrVar(props.colDef?.editable, props);
|
|
4547
|
-
return (jsx(LuiIcon, { name: "ic_more_vert", alt: "More actions", size: "md", className: clsx("GridPopoutMenu-burger", disabled && "GridPopoutMenu-burgerDisabled") }));
|
|
4548
|
-
};
|
|
4549
|
-
|
|
4550
|
-
/**
|
|
4551
|
-
* Popout burger menu
|
|
4552
|
-
*/
|
|
4553
|
-
const GridPopoverMenu = (colDef, custom) => GridCell({
|
|
4554
|
-
minWidth: 48,
|
|
4555
|
-
maxWidth: 48,
|
|
4556
|
-
width: 40,
|
|
4557
|
-
sortable: false,
|
|
4558
|
-
editable: colDef.editable != null ? colDef.editable : true,
|
|
4559
|
-
exportable: false,
|
|
4560
|
-
cellStyle: { flex: 1, justifyContent: "center" },
|
|
4561
|
-
cellRenderer: GridRenderPopoutMenuCell,
|
|
4562
|
-
...colDef,
|
|
4563
|
-
cellRendererParams: {
|
|
4564
|
-
// Menus open on single click, this parameter is picked up in Grid.tsx
|
|
4565
|
-
singleClickEdit: true,
|
|
4553
|
+
className: clsx('GridMultiSelect-containerMedium', props.editorParams?.className),
|
|
4566
4554
|
},
|
|
4567
|
-
}, {
|
|
4568
|
-
editor: GridFormPopoverMenu,
|
|
4569
|
-
preventAutoEdit: true,
|
|
4570
|
-
...custom,
|
|
4571
4555
|
});
|
|
4572
4556
|
|
|
4573
4557
|
const GridPopoverEditBearingLike = (colDef, props) => GridCell({
|
|
4574
|
-
valueFormatter: (params) => props.editorParams?.formatValue(params.value) ??
|
|
4558
|
+
valueFormatter: (params) => props.editorParams?.formatValue(params.value) ?? '',
|
|
4575
4559
|
...colDef,
|
|
4576
4560
|
}, {
|
|
4577
4561
|
editor: GridFormEditBearing,
|
|
4578
4562
|
...props,
|
|
4579
4563
|
});
|
|
4580
4564
|
const GridPopoverEditBearingEditorParams = {
|
|
4581
|
-
placeHolder:
|
|
4565
|
+
placeHolder: 'Enter bearing',
|
|
4582
4566
|
formatValue: bearingValueFormatter,
|
|
4583
4567
|
range: bearingRangeValidator,
|
|
4584
4568
|
};
|
|
@@ -4590,7 +4574,7 @@ const GridPopoverEditBearing = (colDef, props) => GridPopoverEditBearingLike(col
|
|
|
4590
4574
|
},
|
|
4591
4575
|
});
|
|
4592
4576
|
const GridPopoverEditBearingCorrectionEditorParams = {
|
|
4593
|
-
placeHolder:
|
|
4577
|
+
placeHolder: 'Enter bearing correction',
|
|
4594
4578
|
formatValue: bearingCorrectionValueFormatter,
|
|
4595
4579
|
range: bearingCorrectionRangeValidator,
|
|
4596
4580
|
};
|
|
@@ -4608,11 +4592,39 @@ const GridPopoverEditDropDown = (colDef, props) => GridCell(colDef, {
|
|
|
4608
4592
|
editorParams: {
|
|
4609
4593
|
...props.editorParams,
|
|
4610
4594
|
className: clsx({
|
|
4611
|
-
|
|
4595
|
+
'GridPopoverEditDropDown-containerLarge': !props.editorParams?.className?.includes('GridPopoverEditDropDown-container'),
|
|
4612
4596
|
}, props.editorParams?.className),
|
|
4613
4597
|
},
|
|
4614
4598
|
});
|
|
4615
4599
|
|
|
4600
|
+
const GridRenderPopoutMenuCell = (props) => {
|
|
4601
|
+
const disabled = !fnOrVar(props.colDef?.editable, props);
|
|
4602
|
+
return (jsx(LuiIcon, { name: 'ic_more_vert', alt: 'More actions', size: 'md', className: clsx('GridPopoutMenu-burger', disabled && 'GridPopoutMenu-burgerDisabled') }));
|
|
4603
|
+
};
|
|
4604
|
+
|
|
4605
|
+
/**
|
|
4606
|
+
* Popout burger menu
|
|
4607
|
+
*/
|
|
4608
|
+
const GridPopoverMenu = (colDef, custom) => GridCell({
|
|
4609
|
+
minWidth: 48,
|
|
4610
|
+
maxWidth: 48,
|
|
4611
|
+
width: 40,
|
|
4612
|
+
sortable: false,
|
|
4613
|
+
editable: colDef.editable != null ? colDef.editable : true,
|
|
4614
|
+
exportable: false,
|
|
4615
|
+
cellStyle: { flex: 1, justifyContent: 'center' },
|
|
4616
|
+
cellRenderer: GridRenderPopoutMenuCell,
|
|
4617
|
+
...colDef,
|
|
4618
|
+
cellRendererParams: {
|
|
4619
|
+
// Menus open on single click, this parameter is picked up in Grid.tsx
|
|
4620
|
+
singleClickEdit: true,
|
|
4621
|
+
},
|
|
4622
|
+
}, {
|
|
4623
|
+
editor: GridFormPopoverMenu,
|
|
4624
|
+
preventAutoEdit: true,
|
|
4625
|
+
...custom,
|
|
4626
|
+
});
|
|
4627
|
+
|
|
4616
4628
|
const GridPopoverMessage = (colDef, props) => GridCell({
|
|
4617
4629
|
resizable: true,
|
|
4618
4630
|
...colDef,
|
|
@@ -4632,9 +4644,9 @@ const GridPopoverTextInput = (colDef, params) => GridCell(colDef, {
|
|
|
4632
4644
|
...params,
|
|
4633
4645
|
});
|
|
4634
4646
|
|
|
4635
|
-
const GridWrapper = ({ children, maxHeight }) => (jsx("div", { className:
|
|
4647
|
+
const GridWrapper = ({ children, maxHeight }) => (jsx("div", { className: 'Grid-wrapper', style: { maxHeight }, children: children }));
|
|
4636
4648
|
|
|
4637
|
-
const getColId = (colDef) => colDef.colId ??
|
|
4649
|
+
const getColId = (colDef) => colDef.colId ?? '';
|
|
4638
4650
|
const isFlexColumn = (colDef) => !!colDef.flex && !isGridCellFiller(colDef);
|
|
4639
4651
|
|
|
4640
4652
|
/**
|
|
@@ -4646,7 +4658,7 @@ const GridContextProvider = (props) => {
|
|
|
4646
4658
|
const { modifyUpdating, checkUpdating } = useContext(GridUpdatingContext);
|
|
4647
4659
|
const [gridApi, setGridApi] = useState();
|
|
4648
4660
|
const [gridReady, setGridReady] = useState(false);
|
|
4649
|
-
const [quickFilter, _setQuickFilter] = useState(
|
|
4661
|
+
const [quickFilter, _setQuickFilter] = useState('');
|
|
4650
4662
|
const [invisibleColumnIds, _setInvisibleColumnIds] = useState();
|
|
4651
4663
|
const testId = useRef();
|
|
4652
4664
|
const idsBeforeUpdate = useRef([]);
|
|
@@ -4666,7 +4678,7 @@ const GridContextProvider = (props) => {
|
|
|
4666
4678
|
* Set quick filter directly on grid, based on previously save quickFilter state.
|
|
4667
4679
|
*/
|
|
4668
4680
|
useEffect(() => {
|
|
4669
|
-
gridApi?.setGridOption(
|
|
4681
|
+
gridApi?.setGridOption('quickFilterText', quickFilter);
|
|
4670
4682
|
}, [gridApi, quickFilter]);
|
|
4671
4683
|
/**
|
|
4672
4684
|
* Wraps things that require gridApi in common handling, for when gridApi not present.
|
|
@@ -4699,9 +4711,9 @@ const GridContextProvider = (props) => {
|
|
|
4699
4711
|
let id = 0;
|
|
4700
4712
|
try {
|
|
4701
4713
|
gridApi?.forEachNodeAfterFilterAndSort((rowNode) => {
|
|
4702
|
-
id = parseInt(rowNode.id ??
|
|
4714
|
+
id = parseInt(rowNode.id ?? '0');
|
|
4703
4715
|
// this is the only way to get out of the loop
|
|
4704
|
-
throw
|
|
4716
|
+
throw 'expected exception - exit_loop';
|
|
4705
4717
|
});
|
|
4706
4718
|
}
|
|
4707
4719
|
catch (ex) {
|
|
@@ -4715,7 +4727,7 @@ const GridContextProvider = (props) => {
|
|
|
4715
4727
|
const setApis = useCallback((gridApi, dataTestId) => {
|
|
4716
4728
|
testId.current = dataTestId;
|
|
4717
4729
|
setGridApi(gridApi);
|
|
4718
|
-
gridApi?.setGridOption(
|
|
4730
|
+
gridApi?.setGridOption('quickFilterText', quickFilter);
|
|
4719
4731
|
setGridReady(!!gridApi);
|
|
4720
4732
|
}, [quickFilter]);
|
|
4721
4733
|
/**
|
|
@@ -4725,9 +4737,9 @@ const GridContextProvider = (props) => {
|
|
|
4725
4737
|
if (!gridApi)
|
|
4726
4738
|
return null;
|
|
4727
4739
|
if (!gridApi.getDisplayedRowCount())
|
|
4728
|
-
return
|
|
4740
|
+
return 'empty';
|
|
4729
4741
|
if (!isEmpty(gridApi.getRenderedNodes()))
|
|
4730
|
-
return
|
|
4742
|
+
return 'rows-visible';
|
|
4731
4743
|
// If there are rows to render, but there are no rendered nodes then we should wait
|
|
4732
4744
|
return null;
|
|
4733
4745
|
}, [gridApi]);
|
|
@@ -4783,7 +4795,7 @@ const GridContextProvider = (props) => {
|
|
|
4783
4795
|
* Uses beforeUpdate ids to find new nodes.
|
|
4784
4796
|
*/
|
|
4785
4797
|
const _getNewNodes = useCallback(() => {
|
|
4786
|
-
return gridApiOp((gridApi) => compact(difference(_getAllRowIds(), idsBeforeUpdate.current).map((rowId) => gridApi.getRowNode(
|
|
4798
|
+
return gridApiOp((gridApi) => compact(difference(_getAllRowIds(), idsBeforeUpdate.current).map((rowId) => gridApi.getRowNode('' + rowId))), () => []);
|
|
4787
4799
|
}, [_getAllRowIds, gridApiOp]);
|
|
4788
4800
|
/**
|
|
4789
4801
|
* Get grid nodes via rowIds. If rowIds has no matching node the result may be smaller than expected.
|
|
@@ -4792,7 +4804,7 @@ const GridContextProvider = (props) => {
|
|
|
4792
4804
|
* @param rowIds Row ids to get from grid.
|
|
4793
4805
|
*/
|
|
4794
4806
|
const _rowIdsToNodes = useCallback((rowIds) => {
|
|
4795
|
-
return gridApiOp((gridApi) => compact(rowIds.map((rowId) => gridApi.getRowNode(
|
|
4807
|
+
return gridApiOp((gridApi) => compact(rowIds.map((rowId) => gridApi.getRowNode('' + rowId))), () => []);
|
|
4796
4808
|
}, [gridApiOp]);
|
|
4797
4809
|
/**
|
|
4798
4810
|
* Get ColDefs, with flattened ColGroupDefs
|
|
@@ -4933,7 +4945,7 @@ const GridContextProvider = (props) => {
|
|
|
4933
4945
|
gridApi.autoSizeColumns(colsToResize.map((colState) => colState.colId), skipHeader);
|
|
4934
4946
|
}
|
|
4935
4947
|
return {
|
|
4936
|
-
width: sumBy(gridApi.getColumnState().filter((col) => !col.hide),
|
|
4948
|
+
width: sumBy(gridApi.getColumnState().filter((col) => !col.hide), 'width'),
|
|
4937
4949
|
};
|
|
4938
4950
|
}, [gridApi]);
|
|
4939
4951
|
/**
|
|
@@ -4950,7 +4962,9 @@ const GridContextProvider = (props) => {
|
|
|
4950
4962
|
}
|
|
4951
4963
|
gridApi.stopEditing();
|
|
4952
4964
|
}, [gridApi]);
|
|
4953
|
-
const startCellEditing = useCallback(
|
|
4965
|
+
const startCellEditing = useCallback(
|
|
4966
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
4967
|
+
({ rowId, colId }) => {
|
|
4954
4968
|
if (!gridApi)
|
|
4955
4969
|
return;
|
|
4956
4970
|
const colDef = gridApi.getColumnDef(colId);
|
|
@@ -4965,7 +4979,7 @@ const GridContextProvider = (props) => {
|
|
|
4965
4979
|
rowNode.setSelected(true, true);
|
|
4966
4980
|
}
|
|
4967
4981
|
// Cell already being edited, so don't re-edit until finished
|
|
4968
|
-
if (checkUpdating([colDef.field ??
|
|
4982
|
+
if (checkUpdating([colDef.field ?? ''], rowId)) {
|
|
4969
4983
|
return;
|
|
4970
4984
|
}
|
|
4971
4985
|
const rowIndex = rowNode.rowIndex;
|
|
@@ -5036,11 +5050,11 @@ const GridContextProvider = (props) => {
|
|
|
5036
5050
|
return await gridApiOp(async (gridApi) => {
|
|
5037
5051
|
const selectedRows = props.selectedRows;
|
|
5038
5052
|
let ok = false;
|
|
5039
|
-
await modifyUpdating(props.field ??
|
|
5053
|
+
await modifyUpdating(props.field ?? '', selectedRows.map((data) => data.id), async () => {
|
|
5040
5054
|
// Need to refresh to get spinners to work on all rows
|
|
5041
5055
|
gridApi.refreshCells({ rowNodes: props.selectedRows, force: true });
|
|
5042
5056
|
ok = await fnUpdate(selectedRows).catch((ex) => {
|
|
5043
|
-
console.error(
|
|
5057
|
+
console.error('Exception during modifyUpdating', ex);
|
|
5044
5058
|
return false;
|
|
5045
5059
|
});
|
|
5046
5060
|
});
|
|
@@ -5077,7 +5091,7 @@ const GridContextProvider = (props) => {
|
|
|
5077
5091
|
}
|
|
5078
5092
|
catch (ex) {
|
|
5079
5093
|
// Hide errors in jest, but log them in browser
|
|
5080
|
-
if (typeof jest ===
|
|
5094
|
+
if (typeof jest === 'undefined')
|
|
5081
5095
|
console.error(ex);
|
|
5082
5096
|
}
|
|
5083
5097
|
}, 50), [gridApi]);
|
|
@@ -5099,7 +5113,7 @@ const GridContextProvider = (props) => {
|
|
|
5099
5113
|
gridApi?.forEachNodeAfterFilter((rowNode) => {
|
|
5100
5114
|
arr.push(rowNode.id);
|
|
5101
5115
|
});
|
|
5102
|
-
return arr.join(
|
|
5116
|
+
return arr.join('|');
|
|
5103
5117
|
};
|
|
5104
5118
|
if (gridApi) {
|
|
5105
5119
|
const hasFocusedCell = gridApi.getFocusedCell();
|
|
@@ -5113,11 +5127,11 @@ const GridContextProvider = (props) => {
|
|
|
5113
5127
|
}, 200), [gridApi]);
|
|
5114
5128
|
const addExternalFilter = (filter) => {
|
|
5115
5129
|
externalFilters.current.push(filter);
|
|
5116
|
-
onFilterChanged()
|
|
5130
|
+
void onFilterChanged();
|
|
5117
5131
|
};
|
|
5118
5132
|
const removeExternalFilter = (filter) => {
|
|
5119
5133
|
remove(externalFilters.current, (v) => v === filter);
|
|
5120
|
-
onFilterChanged()
|
|
5134
|
+
void onFilterChanged();
|
|
5121
5135
|
};
|
|
5122
5136
|
const isExternalFilterPresent = () => !isEmpty(externalFilters.current);
|
|
5123
5137
|
const doesExternalFilterPass = (node) => externalFilters.current.every((filter) => filter(node.data, node));
|
|
@@ -5221,10 +5235,10 @@ const GridContextProvider = (props) => {
|
|
|
5221
5235
|
const downloadCsvUseValueFormattersProcessCellCallback = (params) => {
|
|
5222
5236
|
const encodeToString = (value) => {
|
|
5223
5237
|
// Convert nullish values to blank
|
|
5224
|
-
if (value ===
|
|
5225
|
-
return
|
|
5238
|
+
if (value === '-' || value === '–' || value == null) {
|
|
5239
|
+
return '';
|
|
5226
5240
|
}
|
|
5227
|
-
if (typeof value ===
|
|
5241
|
+
if (typeof value === 'string') {
|
|
5228
5242
|
return value;
|
|
5229
5243
|
}
|
|
5230
5244
|
return JSON.stringify(value);
|
|
@@ -5238,19 +5252,19 @@ const downloadCsvUseValueFormattersProcessCellCallback = (params) => {
|
|
|
5238
5252
|
const valueFormatter = colDef.valueFormatter;
|
|
5239
5253
|
// If no valueFormatter then value _must_ be a string
|
|
5240
5254
|
if (valueFormatter == null) {
|
|
5241
|
-
if (params.value != null && typeof params.value !==
|
|
5255
|
+
if (params.value != null && typeof params.value !== 'string') {
|
|
5242
5256
|
console.error(`downloadCsv: valueFormatter missing and getValue is not a string, colId: ${colDef.colId}`);
|
|
5243
5257
|
}
|
|
5244
5258
|
return encodeToString(params.value);
|
|
5245
5259
|
}
|
|
5246
5260
|
// We don't have access to registered functions, so we can't call them
|
|
5247
|
-
if (typeof valueFormatter !==
|
|
5261
|
+
if (typeof valueFormatter !== 'function') {
|
|
5248
5262
|
console.error(`downloadCsv: String type (registered) value formatters are unsupported in downloadCsv, colId: ${colDef.colId}`);
|
|
5249
5263
|
return encodeToString(params.value);
|
|
5250
5264
|
}
|
|
5251
5265
|
const result = valueFormatter({ ...params, data: params.node?.data, colDef, node: null });
|
|
5252
5266
|
// type may not be string due to casting, leave the type check in
|
|
5253
|
-
if (params.value != null && typeof result !==
|
|
5267
|
+
if (params.value != null && typeof result !== 'string') {
|
|
5254
5268
|
console.error(`downloadCsv: valueFormatter is returning non string values, colDef:", colId: ${colDef.colId}`);
|
|
5255
5269
|
}
|
|
5256
5270
|
// We add an extra encodeToString here just in case valueFormatter is returning non string values
|
|
@@ -5309,7 +5323,7 @@ const usePrevious = (value) => {
|
|
|
5309
5323
|
|
|
5310
5324
|
// Kept this less than one second, so I don't have issues with waitFor as it defaults to 1s
|
|
5311
5325
|
const minimumInProgressTimeMs = 950;
|
|
5312
|
-
const ActionButton = ({ icon, name, inProgressName, disabled, dataTestId, style, className, title, onClick, size =
|
|
5326
|
+
const ActionButton = ({ icon, name, inProgressName, disabled, dataTestId, style, className, title, onClick, size = 'sm', iconPosition = 'left', level = 'tertiary', 'aria-label': ariaLabel, }) => {
|
|
5313
5327
|
const [inProgress, setInProgress] = useState(false);
|
|
5314
5328
|
const lastInProgress = usePrevious(inProgress ?? false);
|
|
5315
5329
|
const [localInProgress, setLocalInProgress, setLocalInProgressDeferred] = useStateDeferred(inProgress);
|
|
@@ -5318,28 +5332,28 @@ const ActionButton = ({ icon, name, inProgressName, disabled, dataTestId, style,
|
|
|
5318
5332
|
return;
|
|
5319
5333
|
inProgress ? setLocalInProgress(true) : setLocalInProgressDeferred(false, minimumInProgressTimeMs);
|
|
5320
5334
|
}, [inProgress, lastInProgress, setLocalInProgress, setLocalInProgressDeferred]);
|
|
5321
|
-
const buttonText = (jsxs("span", { className:
|
|
5322
|
-
return (jsxs(LuiButton, { "data-testid": dataTestId, type:
|
|
5335
|
+
const buttonText = (jsxs("span", { className: 'ActionButton-minimalArea', children: [jsx("span", { className: 'ActionButton-minimalAreaDisplay', children: (localInProgress ? inProgressName : name) ?? name }), jsx("span", { className: 'ActionButton-minimalAreaExpand', children: name })] }));
|
|
5336
|
+
return (jsxs(LuiButton, { "data-testid": dataTestId, type: 'button', level: level, title: title ?? ariaLabel ?? name, buttonProps: { 'aria-label': ariaLabel ?? name }, className: clsx('lui-button-icon-right', 'ActionButton', className, localInProgress && 'ActionButton-inProgress', name != null && !className?.includes('ActionButton-fill') && 'ActionButton-minimal', name == null && 'ActionButton-iconOnly'), size: 'lg', style: style, onClick: () => {
|
|
5323
5337
|
const promise = onClick();
|
|
5324
|
-
const isPromise = typeof promise !==
|
|
5338
|
+
const isPromise = typeof promise !== 'undefined';
|
|
5325
5339
|
if (isPromise) {
|
|
5326
5340
|
setInProgress(true);
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5341
|
+
void promise.finally(() => {
|
|
5342
|
+
setInProgress(false);
|
|
5343
|
+
});
|
|
5330
5344
|
}
|
|
5331
|
-
}, disabled: localInProgress || disabled, children: [iconPosition ===
|
|
5332
|
-
|
|
5345
|
+
}, disabled: localInProgress || disabled, children: [iconPosition === 'right' && buttonText, localInProgress && (jsx("div", { style: { position: 'relative' }, children: jsx(LuiMiniSpinner, { size: 14, divProps: {
|
|
5346
|
+
'data-testid': 'loading-spinner',
|
|
5333
5347
|
style: {
|
|
5334
|
-
position:
|
|
5348
|
+
position: 'absolute',
|
|
5335
5349
|
left: 4,
|
|
5336
5350
|
top: -10,
|
|
5337
5351
|
bottom: 10,
|
|
5338
5352
|
right: -4,
|
|
5339
5353
|
},
|
|
5340
|
-
role:
|
|
5341
|
-
|
|
5342
|
-
} }) })), jsx(LuiIcon, { name: icon, alt: ariaLabel ?? name ??
|
|
5354
|
+
role: 'status',
|
|
5355
|
+
'aria-label': 'Loading',
|
|
5356
|
+
} }) })), jsx(LuiIcon, { name: icon, alt: ariaLabel ?? name ?? '', size: size }), iconPosition === 'left' && buttonText] }));
|
|
5343
5357
|
};
|
|
5344
5358
|
|
|
5345
5359
|
const useDeferredPromise = () => {
|