@linzjs/step-ag-grid 1.4.4 → 1.4.5
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/index.js +1602 -3043
- package/dist/index.js.map +1 -1
- package/dist/src/components/GridLoadableCell.d.ts +2 -0
- package/dist/src/components/gridForm/GridFormDropDown.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormMultiSelect.d.ts +1 -1
- package/dist/src/components/gridPopoverEdit/GridPopoverMenu.d.ts +1 -1
- package/dist/src/react-menu3/components/ControlledMenu.d.ts +2 -0
- package/dist/src/react-menu3/components/FocusableItem.d.ts +1 -0
- package/dist/src/react-menu3/components/Menu.d.ts +2 -0
- package/dist/src/react-menu3/components/MenuButton.d.ts +1 -0
- package/dist/src/react-menu3/components/MenuDivider.d.ts +2 -0
- package/dist/src/react-menu3/components/MenuGroup.d.ts +2 -0
- package/dist/src/react-menu3/components/MenuHeader.d.ts +2 -0
- package/dist/src/react-menu3/components/MenuItem.d.ts +1 -0
- package/dist/src/react-menu3/components/MenuList.d.ts +30 -0
- package/dist/src/react-menu3/components/MenuRadioGroup.d.ts +2 -0
- package/dist/src/react-menu3/components/SubMenu.d.ts +1 -0
- package/dist/src/react-menu3/hooks/index.d.ts +9 -0
- package/dist/src/react-menu3/hooks/useBEM.d.ts +9 -0
- package/dist/src/react-menu3/hooks/useCombinedRef.d.ts +2 -0
- package/dist/src/react-menu3/hooks/useIsomorphicLayoutEffect.d.ts +3 -0
- package/dist/src/react-menu3/hooks/useItemEffect.d.ts +2 -0
- package/dist/src/react-menu3/hooks/useItemState.d.ts +6 -0
- package/dist/src/react-menu3/hooks/useItems.d.ts +5 -0
- package/dist/src/react-menu3/hooks/useMenuChange.d.ts +1 -0
- package/dist/src/react-menu3/hooks/useMenuState.d.ts +11 -0
- package/dist/src/react-menu3/hooks/useMenuStateAndFocus.d.ts +10 -0
- package/dist/src/react-menu3/index.d.ts +11 -0
- package/dist/src/react-menu3/positionUtils/getPositionHelpers.d.ts +10 -0
- package/dist/src/react-menu3/positionUtils/index.d.ts +3 -0
- package/dist/src/react-menu3/positionUtils/placeArrowHorizontal.d.ts +7 -0
- package/dist/src/react-menu3/positionUtils/placeArrowVertical.d.ts +7 -0
- package/dist/src/react-menu3/positionUtils/placeLeftorRight.d.ts +21 -0
- package/dist/src/react-menu3/positionUtils/placeToporBottom.d.ts +21 -0
- package/dist/src/react-menu3/positionUtils/positionContextMenu.d.ts +8 -0
- package/dist/src/react-menu3/positionUtils/positionMenu.d.ts +21 -0
- package/dist/src/react-menu3/style-utils/index.d.ts +11 -0
- package/dist/src/react-menu3/utils/constants.d.ts +50 -0
- package/dist/src/react-menu3/utils/index.d.ts +4 -0
- package/dist/src/react-menu3/utils/propTypes.d.ts +55 -0
- package/dist/src/react-menu3/utils/utils.d.ts +27 -0
- package/dist/src/react-menu3/utils/withHovering.d.ts +1 -0
- package/dist/step-ag-grid.esm.js +1513 -2953
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +18 -9
- package/src/components/GridLoadableCell.scss +4 -0
- package/src/components/GridLoadableCell.tsx +5 -1
- package/src/components/GridPopoverHook.tsx +4 -50
- package/src/components/gridForm/GridFormDropDown.tsx +2 -2
- package/src/components/gridForm/GridFormMultiSelect.tsx +4 -4
- package/src/components/gridForm/GridFormPopoutMenu.tsx +1 -1
- package/src/components/gridPopoverEdit/GridPopoverMenu.scss +6 -2
- package/src/components/gridPopoverEdit/GridPopoverMenu.tsx +1 -1
- package/src/components/gridRender/GridRenderPopoutMenuCell.tsx +7 -9
- package/src/react-menu3/components/ControlledMenu.tsx +260 -0
- package/src/react-menu3/components/FocusableItem.tsx +70 -0
- package/src/react-menu3/components/Menu.tsx +96 -0
- package/src/react-menu3/components/MenuButton.tsx +33 -0
- package/src/react-menu3/components/MenuDivider.tsx +21 -0
- package/src/react-menu3/components/MenuGroup.tsx +38 -0
- package/src/react-menu3/components/MenuHeader.tsx +21 -0
- package/src/react-menu3/components/MenuItem.tsx +130 -0
- package/src/react-menu3/components/MenuList.tsx +434 -0
- package/src/react-menu3/components/MenuRadioGroup.tsx +33 -0
- package/src/react-menu3/components/SubMenu.tsx +243 -0
- package/src/react-menu3/hooks/index.ts +9 -0
- package/src/react-menu3/hooks/useBEM.ts +32 -0
- package/src/react-menu3/hooks/useCombinedRef.ts +30 -0
- package/src/react-menu3/hooks/useIsomorphicLayoutEffect.ts +15 -0
- package/src/react-menu3/hooks/useItemEffect.ts +22 -0
- package/src/react-menu3/hooks/useItemState.ts +63 -0
- package/src/react-menu3/hooks/useItems.ts +110 -0
- package/src/react-menu3/hooks/useMenuChange.ts +12 -0
- package/src/react-menu3/hooks/useMenuState.ts +24 -0
- package/src/react-menu3/hooks/useMenuStateAndFocus.ts +15 -0
- package/src/react-menu3/index.d.ts +682 -0
- package/src/react-menu3/index.ts +11 -0
- package/src/react-menu3/positionUtils/getPositionHelpers.ts +76 -0
- package/src/react-menu3/positionUtils/index.ts +3 -0
- package/src/react-menu3/positionUtils/placeArrowHorizontal.ts +8 -0
- package/src/react-menu3/positionUtils/placeArrowVertical.ts +8 -0
- package/src/react-menu3/positionUtils/placeLeftorRight.ts +76 -0
- package/src/react-menu3/positionUtils/placeToporBottom.ts +77 -0
- package/src/react-menu3/positionUtils/positionContextMenu.ts +49 -0
- package/src/react-menu3/positionUtils/positionMenu.ts +73 -0
- package/src/react-menu3/style-utils/index.ts +80 -0
- package/src/react-menu3/style-utils.d.ts +110 -0
- package/src/react-menu3/styles/_mixins.scss +11 -0
- package/src/react-menu3/styles/_var.scss +20 -0
- package/src/react-menu3/styles/core.scss +80 -0
- package/src/react-menu3/styles/index.scss +70 -0
- package/src/react-menu3/styles/theme-dark.scss +30 -0
- package/src/react-menu3/styles/transitions/slide.scss +47 -0
- package/src/react-menu3/utils/constants.ts +63 -0
- package/src/react-menu3/utils/index.ts +4 -0
- package/src/react-menu3/utils/propTypes.ts +61 -0
- package/src/react-menu3/utils/utils.ts +71 -0
- package/src/react-menu3/utils/withHovering.tsx +22 -0
- package/src/stories/components/GridPopoutEditDropDown.stories.tsx +1 -3
- package/src/utils/util.ts +2 -1
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@linzjs/step-ag-grid",
|
|
3
3
|
"repository": "github:linz/step-ag-grid.git",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "1.4.
|
|
5
|
+
"version": "1.4.5",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"typings": "dist/index.d.ts",
|
|
8
8
|
"module": "dist/step-ag-grid.esm.js",
|
|
@@ -14,7 +14,14 @@
|
|
|
14
14
|
"access": "public"
|
|
15
15
|
},
|
|
16
16
|
"engines": {
|
|
17
|
-
"node": ">=
|
|
17
|
+
"node": ">=14"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"react": ">=17",
|
|
21
|
+
"react-dom": ">=17",
|
|
22
|
+
"ag-grid-community": ">=27",
|
|
23
|
+
"ag-grid-react": ">=27",
|
|
24
|
+
"@linzjs/lui": ">=17"
|
|
18
25
|
},
|
|
19
26
|
"dependencies": {
|
|
20
27
|
"@linzjs/lui": "^17.18.2",
|
|
@@ -24,7 +31,9 @@
|
|
|
24
31
|
"lodash-es": "^4.17.21",
|
|
25
32
|
"react": "^17.0.2",
|
|
26
33
|
"react-dom": "^17.0.2",
|
|
27
|
-
"uuid": "^9.0.0"
|
|
34
|
+
"uuid": "^9.0.0",
|
|
35
|
+
"prop-types": "^15.8.1",
|
|
36
|
+
"react-transition-state": "^1.1.5"
|
|
28
37
|
},
|
|
29
38
|
"scripts": {
|
|
30
39
|
"build": "rollup -c && npm run",
|
|
@@ -56,16 +65,15 @@
|
|
|
56
65
|
]
|
|
57
66
|
},
|
|
58
67
|
"devDependencies": {
|
|
59
|
-
"@rollup/plugin-commonjs": "^
|
|
60
|
-
"@rollup/plugin-json": "^
|
|
61
|
-
"@rollup/plugin-node-resolve": "^
|
|
68
|
+
"@rollup/plugin-commonjs": "^23.0.2",
|
|
69
|
+
"@rollup/plugin-json": "^5.0.1",
|
|
70
|
+
"@rollup/plugin-node-resolve": "^15.0.1",
|
|
62
71
|
"@semantic-release/changelog": "^6.0.1",
|
|
63
72
|
"@semantic-release/git": "^10.0.1",
|
|
64
73
|
"@storybook/addon-actions": "^6.5.13",
|
|
65
74
|
"@storybook/addon-essentials": "^6.5.13",
|
|
66
75
|
"@storybook/addon-interactions": "^6.5.13",
|
|
67
76
|
"@storybook/addon-links": "^6.5.13",
|
|
68
|
-
"storybook-addon-mock": "^2.4.1",
|
|
69
77
|
"@storybook/jest": "^0.0.10",
|
|
70
78
|
"@storybook/react": "6.5.13",
|
|
71
79
|
"@storybook/test-runner": "^0.9.0",
|
|
@@ -77,8 +85,8 @@
|
|
|
77
85
|
"@types/jest": "^29.2.0",
|
|
78
86
|
"@types/lodash-es": "^4.17.6",
|
|
79
87
|
"@types/node": "^18.11.7",
|
|
80
|
-
"@types/react": "^
|
|
81
|
-
"@types/react-dom": "^
|
|
88
|
+
"@types/react": "^17.0.52",
|
|
89
|
+
"@types/react-dom": "^17.0.18",
|
|
82
90
|
"@types/uuid": "^8.3.4",
|
|
83
91
|
"@typescript-eslint/parser": "^5.41.0",
|
|
84
92
|
"babel-jest": "^26.6.3",
|
|
@@ -108,6 +116,7 @@
|
|
|
108
116
|
"sass": "^1.55.0",
|
|
109
117
|
"sass-loader": "10.1.1",
|
|
110
118
|
"semantic-release": "^19.0.5",
|
|
119
|
+
"storybook-addon-mock": "^2.4.1",
|
|
111
120
|
"style-loader": "^2.0.0",
|
|
112
121
|
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
|
113
122
|
"typescript": "^4.8.4"
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import "./GridLoadableCell.scss";
|
|
2
|
+
|
|
1
3
|
import { LuiMiniSpinner } from "@linzjs/lui";
|
|
4
|
+
import clsx from "clsx";
|
|
2
5
|
|
|
3
6
|
export const GridLoadableCell = (props: {
|
|
4
7
|
isLoading: boolean;
|
|
5
8
|
dataTestId?: string;
|
|
6
9
|
children: JSX.Element | string;
|
|
10
|
+
className?: string;
|
|
7
11
|
}): JSX.Element => {
|
|
8
12
|
// console.log(`Rendering LoadableCell - loading: ${props.isLoading}`);
|
|
9
13
|
if (props.isLoading) {
|
|
@@ -15,7 +19,7 @@ export const GridLoadableCell = (props: {
|
|
|
15
19
|
}
|
|
16
20
|
// only add test id into ONE of the columns in a grid. this way each row will have one unique id :)
|
|
17
21
|
return (
|
|
18
|
-
<div data-testid={props.dataTestId}
|
|
22
|
+
<div data-testid={props.dataTestId} className={clsx("GridLoadableCell-container", props.className)}>
|
|
19
23
|
{props.children}
|
|
20
24
|
</div>
|
|
21
25
|
);
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { ICellEditorParams } from "ag-grid-community";
|
|
2
2
|
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { GridContext } from "@contexts/GridContext";
|
|
4
|
-
import { ControlledMenu } from "@szhsin/react-menu";
|
|
5
4
|
import { GridFormProps } from "./GridCell";
|
|
6
|
-
import { hasParentClass } from "@utils/util";
|
|
7
5
|
import { GridBaseRow } from "./Grid";
|
|
8
|
-
import {
|
|
6
|
+
import { ControlledMenu } from "../react-menu3";
|
|
9
7
|
|
|
10
8
|
export const useGridPopoverHook = <RowType extends GridBaseRow>(
|
|
11
9
|
props: GridFormProps<RowType>,
|
|
@@ -33,51 +31,6 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(
|
|
|
33
31
|
[save, stopEditing, updateValue],
|
|
34
32
|
);
|
|
35
33
|
|
|
36
|
-
const clickIsWithinMenu = useCallback((ev: MouseEvent) => {
|
|
37
|
-
return hasParentClass("szh-menu--state-open", ev.target as Node);
|
|
38
|
-
}, []);
|
|
39
|
-
|
|
40
|
-
const handleScreenMouseDown = useCallback(
|
|
41
|
-
(ev: MouseEvent) => {
|
|
42
|
-
if (!clickIsWithinMenu(ev)) {
|
|
43
|
-
ev.preventDefault();
|
|
44
|
-
ev.stopPropagation();
|
|
45
|
-
// There's an issue in React17
|
|
46
|
-
// the cell doesn't refresh during update if save is invoked from a native event
|
|
47
|
-
// This doesn't happen in React18
|
|
48
|
-
// To work around it, I invoke the save by clicking on an invisible button in the dropdown
|
|
49
|
-
saveButtonRef.current?.click();
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
[clickIsWithinMenu],
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
const handleScreenMouseEvent = useCallback(
|
|
56
|
-
(ev: MouseEvent) => {
|
|
57
|
-
if (!clickIsWithinMenu(ev)) {
|
|
58
|
-
ev.preventDefault();
|
|
59
|
-
ev.stopPropagation();
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
[clickIsWithinMenu],
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
useEffect(() => {
|
|
66
|
-
if (isOpen) {
|
|
67
|
-
document.addEventListener("mousedown", handleScreenMouseDown, true);
|
|
68
|
-
document.addEventListener("mouseup", handleScreenMouseEvent, true);
|
|
69
|
-
document.addEventListener("click", handleScreenMouseEvent, true);
|
|
70
|
-
document.addEventListener("dblclick", handleScreenMouseEvent, true);
|
|
71
|
-
return () => {
|
|
72
|
-
document.removeEventListener("mousedown", handleScreenMouseDown, true);
|
|
73
|
-
document.removeEventListener("mouseup", handleScreenMouseEvent, true);
|
|
74
|
-
document.removeEventListener("click", handleScreenMouseEvent, true);
|
|
75
|
-
document.removeEventListener("dblclick", handleScreenMouseEvent, true);
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
return () => {};
|
|
79
|
-
}, [handleScreenMouseDown, handleScreenMouseEvent, isOpen]);
|
|
80
|
-
|
|
81
34
|
const popoverWrapper = useCallback(
|
|
82
35
|
(children: JSX.Element) => {
|
|
83
36
|
return (
|
|
@@ -85,11 +38,12 @@ export const useGridPopoverHook = <RowType extends GridBaseRow>(
|
|
|
85
38
|
{anchorRef.current && (
|
|
86
39
|
<ControlledMenu
|
|
87
40
|
state={isOpen ? "open" : "closed"}
|
|
88
|
-
portal={
|
|
41
|
+
portal={true}
|
|
89
42
|
unmountOnClose={true}
|
|
90
43
|
anchorRef={anchorRef}
|
|
44
|
+
saveButtonRef={saveButtonRef}
|
|
91
45
|
menuClassName={"lui-menu"}
|
|
92
|
-
onClose={(event) => triggerSave(event.reason).then()}
|
|
46
|
+
onClose={(event: { reason: string }) => triggerSave(event.reason).then()}
|
|
93
47
|
>
|
|
94
48
|
{saving && (
|
|
95
49
|
<div
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "
|
|
1
|
+
import "../../react-menu3/styles/index.scss";
|
|
2
2
|
|
|
3
|
-
import { MenuItem, MenuDivider, FocusableItem } from "@
|
|
3
|
+
import { MenuItem, MenuDivider, FocusableItem } from "@react-menu3";
|
|
4
4
|
import { useCallback, useContext, useEffect, useRef, useState, KeyboardEvent } from "react";
|
|
5
5
|
import { GridBaseRow } from "../Grid";
|
|
6
6
|
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "
|
|
1
|
+
import "../../react-menu3/styles/index.scss";
|
|
2
2
|
|
|
3
|
-
import { MenuItem, MenuDivider, FocusableItem } from "@
|
|
3
|
+
import { MenuItem, MenuDivider, FocusableItem } from "@react-menu3";
|
|
4
4
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
5
5
|
import { GridBaseRow } from "../Grid";
|
|
6
6
|
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
|
|
@@ -137,7 +137,7 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(prop
|
|
|
137
137
|
<>
|
|
138
138
|
<MenuItem
|
|
139
139
|
key={`${item.value}`}
|
|
140
|
-
onClick={(e) => {
|
|
140
|
+
onClick={(e: { keepOpen: boolean }) => {
|
|
141
141
|
e.keepOpen = true;
|
|
142
142
|
// onSelectMenuOption(itemIndex, e.value);
|
|
143
143
|
return false;
|
|
@@ -158,7 +158,7 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(prop
|
|
|
158
158
|
</MenuItem>
|
|
159
159
|
{selectedValues.includes(item.value) && item.subComponent && (
|
|
160
160
|
<FocusableItem className={"LuiDeprecatedForms"} key={`${item.value}_subcomponent`}>
|
|
161
|
-
{(ref) =>
|
|
161
|
+
{(ref: any) =>
|
|
162
162
|
item.subComponent &&
|
|
163
163
|
item.subComponent(
|
|
164
164
|
{
|
|
@@ -2,7 +2,7 @@ import { GridBaseRow } from "../Grid";
|
|
|
2
2
|
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { GridContext } from "@contexts/GridContext";
|
|
4
4
|
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
|
|
5
|
-
import { MenuDivider, MenuItem } from "@
|
|
5
|
+
import { MenuDivider, MenuItem } from "@react-menu3";
|
|
6
6
|
import { GenericCellEditorParams, GridFormProps } from "../GridCell";
|
|
7
7
|
import { useGridPopoverHook } from "../GridPopoverHook";
|
|
8
8
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ICellRendererParams } from "ag-grid-community";
|
|
2
1
|
import { useContext } from "react";
|
|
3
|
-
import {
|
|
2
|
+
import { ICellRendererParams } from "ag-grid-community";
|
|
3
|
+
import { UpdatingContext } from "@contexts/UpdatingContext";
|
|
4
4
|
import { GridLoadableCell } from "../GridLoadableCell";
|
|
5
5
|
import { LuiIcon } from "@linzjs/lui";
|
|
6
6
|
|
|
@@ -10,13 +10,11 @@ export const GridRenderPopoutMenuCell = (props: ICellRendererParams) => {
|
|
|
10
10
|
const disabled = !props.colDef?.editable;
|
|
11
11
|
|
|
12
12
|
return (
|
|
13
|
-
<GridLoadableCell
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
className={disabled ? `GridPopoutMenu-burgerDisabled` : `GridPopoutMenu-burger`}
|
|
19
|
-
/>
|
|
13
|
+
<GridLoadableCell
|
|
14
|
+
isLoading={isLoading}
|
|
15
|
+
className={disabled ? `GridPopoutMenu-burgerDisabled` : `GridPopoutMenu-burger`}
|
|
16
|
+
>
|
|
17
|
+
<LuiIcon name={"ic_more_vert"} alt={"More actions"} size={"md"} />
|
|
20
18
|
</GridLoadableCell>
|
|
21
19
|
);
|
|
22
20
|
};
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { forwardRef, useRef, useMemo, useCallback, useEffect } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
import { string, number, bool, func, object, oneOf, oneOfType, exact } from "prop-types";
|
|
5
|
+
import { MenuList } from "./MenuList";
|
|
6
|
+
import { useBEM } from "../hooks";
|
|
7
|
+
import {
|
|
8
|
+
rootMenuPropTypes,
|
|
9
|
+
menuContainerClass,
|
|
10
|
+
mergeProps,
|
|
11
|
+
safeCall,
|
|
12
|
+
isMenuOpen,
|
|
13
|
+
getTransition,
|
|
14
|
+
values,
|
|
15
|
+
CloseReason,
|
|
16
|
+
Keys,
|
|
17
|
+
MenuStateMap,
|
|
18
|
+
EventHandlersContext,
|
|
19
|
+
SettingsContext,
|
|
20
|
+
ItemSettingsContext,
|
|
21
|
+
} from "../utils";
|
|
22
|
+
import { hasParentClass } from "@utils/util";
|
|
23
|
+
|
|
24
|
+
export const ControlledMenu = forwardRef(function ControlledMenu(
|
|
25
|
+
{
|
|
26
|
+
"aria-label": ariaLabel,
|
|
27
|
+
className,
|
|
28
|
+
containerProps,
|
|
29
|
+
initialMounted,
|
|
30
|
+
unmountOnClose,
|
|
31
|
+
transition,
|
|
32
|
+
transitionTimeout,
|
|
33
|
+
boundingBoxRef,
|
|
34
|
+
boundingBoxPadding,
|
|
35
|
+
reposition = "auto",
|
|
36
|
+
submenuOpenDelay = 300,
|
|
37
|
+
submenuCloseDelay = 150,
|
|
38
|
+
skipOpen,
|
|
39
|
+
viewScroll = "initial",
|
|
40
|
+
portal,
|
|
41
|
+
theming,
|
|
42
|
+
onItemClick,
|
|
43
|
+
onClose,
|
|
44
|
+
saveButtonRef,
|
|
45
|
+
...restProps
|
|
46
|
+
}: any,
|
|
47
|
+
externalRef,
|
|
48
|
+
) {
|
|
49
|
+
const containerRef = useRef(null);
|
|
50
|
+
const scrollNodesRef = useRef({});
|
|
51
|
+
const { anchorRef, state } = restProps;
|
|
52
|
+
|
|
53
|
+
const settings = useMemo(
|
|
54
|
+
() => ({
|
|
55
|
+
initialMounted,
|
|
56
|
+
unmountOnClose,
|
|
57
|
+
transition,
|
|
58
|
+
transitionTimeout,
|
|
59
|
+
boundingBoxRef,
|
|
60
|
+
boundingBoxPadding,
|
|
61
|
+
rootMenuRef: containerRef,
|
|
62
|
+
rootAnchorRef: anchorRef,
|
|
63
|
+
scrollNodesRef,
|
|
64
|
+
reposition,
|
|
65
|
+
viewScroll,
|
|
66
|
+
}),
|
|
67
|
+
[
|
|
68
|
+
initialMounted,
|
|
69
|
+
unmountOnClose,
|
|
70
|
+
transition,
|
|
71
|
+
transitionTimeout,
|
|
72
|
+
anchorRef,
|
|
73
|
+
boundingBoxRef,
|
|
74
|
+
boundingBoxPadding,
|
|
75
|
+
reposition,
|
|
76
|
+
viewScroll,
|
|
77
|
+
],
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const clickIsWithinMenu = useCallback((ev: MouseEvent) => {
|
|
81
|
+
return hasParentClass("szh-menu--state-open", ev.target as Node);
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
const handleScreenEventForSave = useCallback(
|
|
85
|
+
(ev: MouseEvent) => {
|
|
86
|
+
if (!clickIsWithinMenu(ev)) {
|
|
87
|
+
//!ev.currentTarget.contains(ev.relatedTarget || document.activeElement)) {
|
|
88
|
+
ev.preventDefault();
|
|
89
|
+
ev.stopPropagation();
|
|
90
|
+
// FIXME There's an issue in React17
|
|
91
|
+
// the cell doesn't refresh during update if save is invoked from a native event
|
|
92
|
+
// This doesn't happen in React18
|
|
93
|
+
// To work around it, I invoke the save by clicking on a passed in invisible button ref
|
|
94
|
+
if (saveButtonRef.current) saveButtonRef.current.click();
|
|
95
|
+
else safeCall(onClose, { reason: CloseReason.BLUR });
|
|
96
|
+
|
|
97
|
+
// If a user clicks on the menu button when a menu is open, we need to close the menu.
|
|
98
|
+
// However, a blur event will be fired prior to the click event on menu button,
|
|
99
|
+
// which makes the menu first close and then open again.
|
|
100
|
+
// If this happens, e.relatedTarget is incorrectly set to null instead of the button in Safari and Firefox,
|
|
101
|
+
// and makes it difficult to determine whether onBlur is fired because of clicking on menu button.
|
|
102
|
+
// This is a workaround approach which sets a flag to skip a following click event.
|
|
103
|
+
if (skipOpen) {
|
|
104
|
+
skipOpen.current = true;
|
|
105
|
+
setTimeout(() => (skipOpen.current = false), 300);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
[clickIsWithinMenu, onClose],
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const handleScreenEventForCancel = useCallback(
|
|
113
|
+
(ev: MouseEvent) => {
|
|
114
|
+
if (!clickIsWithinMenu(ev)) {
|
|
115
|
+
ev.preventDefault();
|
|
116
|
+
ev.stopPropagation();
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
[clickIsWithinMenu],
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (isMenuOpen(state)) {
|
|
124
|
+
const thisDocument = anchorRef.current ? anchorRef.current.ownerDocument : document;
|
|
125
|
+
thisDocument.addEventListener("mousedown", handleScreenEventForCancel, true);
|
|
126
|
+
thisDocument.addEventListener("mouseup", handleScreenEventForSave, true);
|
|
127
|
+
thisDocument.addEventListener("click", handleScreenEventForCancel, true);
|
|
128
|
+
thisDocument.addEventListener("dblclick", handleScreenEventForCancel, true);
|
|
129
|
+
return () => {
|
|
130
|
+
thisDocument.removeEventListener("mousedown", handleScreenEventForCancel, true);
|
|
131
|
+
thisDocument.removeEventListener("mouseup", handleScreenEventForSave, true);
|
|
132
|
+
thisDocument.removeEventListener("click", handleScreenEventForCancel, true);
|
|
133
|
+
thisDocument.removeEventListener("dblclick", handleScreenEventForCancel, true);
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return () => {};
|
|
137
|
+
}, [handleScreenEventForSave, handleScreenEventForCancel, state]);
|
|
138
|
+
|
|
139
|
+
const itemSettings = useMemo(
|
|
140
|
+
() => ({
|
|
141
|
+
submenuOpenDelay,
|
|
142
|
+
submenuCloseDelay,
|
|
143
|
+
}),
|
|
144
|
+
[submenuOpenDelay, submenuCloseDelay],
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const eventHandlers = useMemo(
|
|
148
|
+
() => ({
|
|
149
|
+
handleClick(event, isCheckorRadio) {
|
|
150
|
+
if (!event.stopPropagation) safeCall(onItemClick, event);
|
|
151
|
+
|
|
152
|
+
let keepOpen = event.keepOpen;
|
|
153
|
+
if (keepOpen === undefined) {
|
|
154
|
+
// if event.keepOpen is undefined, the following default behaviour is used
|
|
155
|
+
// According to WAI-ARIA Authoring Practices 1.1
|
|
156
|
+
// Keep menu open when check or radio is invoked by SPACE key
|
|
157
|
+
keepOpen = isCheckorRadio && event.key === Keys.SPACE;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!keepOpen) {
|
|
161
|
+
safeCall(onClose, {
|
|
162
|
+
value: event.value,
|
|
163
|
+
key: event.key,
|
|
164
|
+
reason: CloseReason.CLICK,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
handleClose(key) {
|
|
170
|
+
safeCall(onClose, { key, reason: CloseReason.CLICK });
|
|
171
|
+
},
|
|
172
|
+
}),
|
|
173
|
+
[onItemClick, onClose],
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const onKeyDown = ({ key }) => {
|
|
177
|
+
switch (key) {
|
|
178
|
+
case Keys.ESC:
|
|
179
|
+
safeCall(onClose, { key, reason: CloseReason.CANCEL });
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const onBlur = (e) => {
|
|
185
|
+
if (isMenuOpen(state) && !e.currentTarget.contains(e.relatedTarget || document.activeElement)) {
|
|
186
|
+
safeCall(onClose, { reason: CloseReason.BLUR });
|
|
187
|
+
|
|
188
|
+
// If a user clicks on the menu button when a menu is open, we need to close the menu.
|
|
189
|
+
// However, a blur event will be fired prior to the click event on menu button,
|
|
190
|
+
// which makes the menu first close and then open again.
|
|
191
|
+
// If this happen, e.relatedTarget is incorrectly set to null instead of the button in Safari and Firefox,
|
|
192
|
+
// and makes it difficult to determine whether onBlur is fired because of clicking on menu button.
|
|
193
|
+
// This is a workaround approach which sets a flag to skip a following click event.
|
|
194
|
+
if (skipOpen) {
|
|
195
|
+
skipOpen.current = true;
|
|
196
|
+
setTimeout(() => (skipOpen.current = false), 300);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const itemTransition = getTransition(transition, "item");
|
|
202
|
+
const modifiers = useMemo(() => ({ theme: theming, itemTransition }), [theming, itemTransition]);
|
|
203
|
+
|
|
204
|
+
const menuList = (
|
|
205
|
+
<div
|
|
206
|
+
{...mergeProps({ onKeyDown, onBlur }, containerProps)}
|
|
207
|
+
className={useBEM({
|
|
208
|
+
block: menuContainerClass,
|
|
209
|
+
modifiers,
|
|
210
|
+
className,
|
|
211
|
+
})}
|
|
212
|
+
style={{ ...containerProps?.style, position: "relative" }}
|
|
213
|
+
ref={containerRef}
|
|
214
|
+
>
|
|
215
|
+
{state && (
|
|
216
|
+
<SettingsContext.Provider value={settings}>
|
|
217
|
+
<ItemSettingsContext.Provider value={itemSettings}>
|
|
218
|
+
<EventHandlersContext.Provider value={eventHandlers}>
|
|
219
|
+
<MenuList
|
|
220
|
+
{...restProps}
|
|
221
|
+
ariaLabel={ariaLabel || "Menu"}
|
|
222
|
+
externalRef={externalRef}
|
|
223
|
+
containerRef={containerRef}
|
|
224
|
+
onClose={onClose}
|
|
225
|
+
/>
|
|
226
|
+
</EventHandlersContext.Provider>
|
|
227
|
+
</ItemSettingsContext.Provider>
|
|
228
|
+
</SettingsContext.Provider>
|
|
229
|
+
)}
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
if (portal === true && anchorRef.current !== undefined) {
|
|
234
|
+
portal = { target: anchorRef.current.ownerDocument.body };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (portal === true && typeof document !== "undefined") {
|
|
238
|
+
return createPortal(menuList, document.body);
|
|
239
|
+
} else if (portal) {
|
|
240
|
+
return portal.target ? createPortal(menuList, portal.target) : portal.stablePosition ? null : menuList;
|
|
241
|
+
}
|
|
242
|
+
return menuList;
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
ControlledMenu.propTypes /* remove-proptypes */ = {
|
|
246
|
+
...rootMenuPropTypes,
|
|
247
|
+
state: oneOf(values(MenuStateMap)),
|
|
248
|
+
anchorPoint: exact({
|
|
249
|
+
x: number,
|
|
250
|
+
y: number,
|
|
251
|
+
}),
|
|
252
|
+
anchorRef: object,
|
|
253
|
+
skipOpen: object,
|
|
254
|
+
captureFocus: bool,
|
|
255
|
+
menuItemFocus: exact({
|
|
256
|
+
position: oneOfType([string, number]),
|
|
257
|
+
alwaysUpdate: bool,
|
|
258
|
+
}),
|
|
259
|
+
onClose: func,
|
|
260
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { useContext, useMemo, useRef } from "react";
|
|
3
|
+
import { bool, func } from "prop-types";
|
|
4
|
+
import { useBEM, useCombinedRef, useItemState } from "../hooks";
|
|
5
|
+
import {
|
|
6
|
+
mergeProps,
|
|
7
|
+
commonProps,
|
|
8
|
+
safeCall,
|
|
9
|
+
menuClass,
|
|
10
|
+
menuItemClass,
|
|
11
|
+
stylePropTypes,
|
|
12
|
+
withHovering,
|
|
13
|
+
EventHandlersContext,
|
|
14
|
+
} from "../utils";
|
|
15
|
+
|
|
16
|
+
export const FocusableItem = withHovering(
|
|
17
|
+
"FocusableItem",
|
|
18
|
+
function FocusableItem({ className, disabled, children, isHovering, itemRef, externalRef, ...restProps }) {
|
|
19
|
+
const isDisabled = !!disabled;
|
|
20
|
+
const ref = useRef(null);
|
|
21
|
+
const { setHover, onPointerLeave, ...restStateProps } = useItemState(itemRef, ref, isHovering, isDisabled);
|
|
22
|
+
const { handleClose } = useContext(EventHandlersContext);
|
|
23
|
+
|
|
24
|
+
const modifiers = useMemo(
|
|
25
|
+
() => ({
|
|
26
|
+
disabled: isDisabled,
|
|
27
|
+
hover: isHovering,
|
|
28
|
+
focusable: true,
|
|
29
|
+
}),
|
|
30
|
+
[isDisabled, isHovering],
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const renderChildren = useMemo(
|
|
34
|
+
() =>
|
|
35
|
+
safeCall(children, {
|
|
36
|
+
...modifiers,
|
|
37
|
+
ref,
|
|
38
|
+
closeMenu: handleClose,
|
|
39
|
+
}),
|
|
40
|
+
[children, modifiers, handleClose],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const mergedProps = mergeProps(
|
|
44
|
+
{
|
|
45
|
+
...restStateProps,
|
|
46
|
+
onPointerLeave: (e) => onPointerLeave(e, true),
|
|
47
|
+
onFocus: setHover,
|
|
48
|
+
},
|
|
49
|
+
restProps,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<li
|
|
54
|
+
role="menuitem"
|
|
55
|
+
{...mergedProps}
|
|
56
|
+
{...commonProps(isDisabled)}
|
|
57
|
+
ref={useCombinedRef(externalRef, itemRef)}
|
|
58
|
+
className={useBEM({ block: menuClass, element: menuItemClass, modifiers, className })}
|
|
59
|
+
>
|
|
60
|
+
{renderChildren}
|
|
61
|
+
</li>
|
|
62
|
+
);
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
FocusableItem.propTypes = {
|
|
67
|
+
...stylePropTypes(),
|
|
68
|
+
disabled: bool,
|
|
69
|
+
children: func,
|
|
70
|
+
};
|