@linzjs/step-ag-grid 1.4.5 → 1.4.7
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.d.ts +1 -0
- package/dist/index.js +284 -232
- package/dist/index.js.map +1 -1
- package/dist/src/components/GridCell.d.ts +4 -3
- package/dist/src/components/gridRender/GridRenderGenericCell.d.ts +8 -5
- package/dist/src/react-menu3/components/ControlledMenu.d.ts +8 -2
- package/dist/src/react-menu3/components/FocusableItem.d.ts +8 -0
- package/dist/src/react-menu3/components/Menu.d.ts +16 -2
- package/dist/src/react-menu3/components/MenuButton.d.ts +8 -1
- package/dist/src/react-menu3/components/MenuDivider.d.ts +2 -1
- package/dist/src/react-menu3/components/MenuGroup.d.ts +12 -2
- package/dist/src/react-menu3/components/MenuHeader.d.ts +4 -2
- package/dist/src/react-menu3/components/MenuItem.d.ts +56 -0
- package/dist/src/react-menu3/components/MenuList.d.ts +2 -29
- package/dist/src/react-menu3/components/MenuRadioGroup.d.ts +44 -2
- package/dist/src/react-menu3/components/SubMenu.d.ts +109 -0
- package/dist/src/react-menu3/hooks/useBEM.d.ts +4 -3
- package/dist/src/react-menu3/hooks/useCombinedRef.d.ts +2 -2
- package/dist/src/react-menu3/hooks/useItemEffect.d.ts +1 -1
- package/dist/src/react-menu3/hooks/useItemState.d.ts +4 -3
- package/dist/src/react-menu3/hooks/useItems.d.ts +6 -4
- package/dist/src/react-menu3/hooks/useMenuChange.d.ts +2 -1
- package/dist/src/react-menu3/hooks/useMenuState.d.ts +13 -10
- package/dist/src/react-menu3/hooks/useMenuStateAndFocus.d.ts +2 -10
- package/dist/src/react-menu3/index.d.ts +1 -0
- package/dist/src/react-menu3/positionUtils/getPositionHelpers.d.ts +10 -9
- package/dist/src/react-menu3/positionUtils/placeArrowHorizontal.d.ts +7 -6
- package/dist/src/react-menu3/positionUtils/placeArrowVertical.d.ts +7 -6
- package/dist/src/react-menu3/positionUtils/placeLeftorRight.d.ts +17 -19
- package/dist/src/react-menu3/positionUtils/placeToporBottom.d.ts +18 -20
- package/dist/src/react-menu3/positionUtils/positionContextMenu.d.ts +11 -6
- package/dist/src/react-menu3/positionUtils/positionMenu.d.ts +23 -18
- package/dist/src/react-menu3/types.d.ts +389 -0
- package/dist/src/react-menu3/utils/constants.d.ts +58 -9
- package/dist/src/react-menu3/utils/index.d.ts +0 -1
- package/dist/src/react-menu3/utils/{propTypes.d.ts → propTypes2.d.ts} +1 -0
- package/dist/src/react-menu3/utils/utils.d.ts +20 -11
- package/dist/src/react-menu3/utils/withHovering.d.ts +7 -1
- package/dist/src/stories/components/ReactMenu.stories.d.ts +8 -0
- package/dist/step-ag-grid.esm.js +276 -235
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +11 -11
- package/src/components/GridCell.tsx +6 -3
- package/src/components/GridPopoverHook.tsx +1 -1
- package/src/components/gridForm/GridFormDropDown.tsx +1 -1
- package/src/components/gridForm/GridFormMultiSelect.tsx +36 -17
- package/src/components/gridRender/GridRenderGenericCell.tsx +11 -7
- package/src/components/gridRender/GridRenderPopoutMenuCell.tsx +14 -2
- package/src/contexts/GridContextProvider.tsx +3 -2
- package/src/react-menu3/components/ControlledMenu.tsx +40 -45
- package/src/react-menu3/components/FocusableItem.tsx +58 -53
- package/src/react-menu3/components/Menu.tsx +40 -28
- package/src/react-menu3/components/MenuButton.tsx +30 -29
- package/src/react-menu3/components/MenuDivider.tsx +14 -18
- package/src/react-menu3/components/MenuGroup.tsx +25 -16
- package/src/react-menu3/components/MenuHeader.tsx +14 -18
- package/src/react-menu3/components/MenuItem.tsx +158 -106
- package/src/react-menu3/components/MenuList.tsx +61 -36
- package/src/react-menu3/components/MenuRadioGroup.tsx +54 -15
- package/src/react-menu3/components/SubMenu.tsx +317 -202
- package/src/react-menu3/hooks/useBEM.ts +4 -3
- package/src/react-menu3/hooks/useCombinedRef.ts +6 -12
- package/src/react-menu3/hooks/useItemEffect.ts +6 -5
- package/src/react-menu3/hooks/useItemState.ts +16 -12
- package/src/react-menu3/hooks/useItems.ts +11 -10
- package/src/react-menu3/hooks/useMenuChange.ts +3 -3
- package/src/react-menu3/hooks/useMenuState.ts +25 -7
- package/src/react-menu3/hooks/useMenuStateAndFocus.ts +6 -4
- package/src/react-menu3/index.ts +1 -0
- package/src/react-menu3/positionUtils/getPositionHelpers.ts +19 -12
- package/src/react-menu3/positionUtils/placeArrowHorizontal.ts +12 -5
- package/src/react-menu3/positionUtils/placeArrowVertical.ts +12 -5
- package/src/react-menu3/positionUtils/placeLeftorRight.ts +33 -17
- package/src/react-menu3/positionUtils/placeToporBottom.ts +34 -18
- package/src/react-menu3/positionUtils/positionContextMenu.ts +15 -3
- package/src/react-menu3/positionUtils/positionMenu.ts +21 -14
- package/src/react-menu3/style-utils/index.ts +10 -7
- package/src/react-menu3/{index.d.ts → types.ts} +106 -358
- package/src/react-menu3/utils/constants.ts +82 -10
- package/src/react-menu3/utils/index.ts +1 -1
- package/src/react-menu3/utils/{propTypes.ts → propTypes2.ts} +3 -1
- package/src/react-menu3/utils/utils.ts +41 -24
- package/src/react-menu3/utils/withHovering.tsx +11 -6
- package/src/stories/components/GridReadOnly.stories.tsx +3 -4
- package/src/stories/components/ReactMenu.stories.tsx +37 -0
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.7",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"typings": "dist/index.d.ts",
|
|
8
8
|
"module": "dist/step-ag-grid.esm.js",
|
|
@@ -17,23 +17,23 @@
|
|
|
17
17
|
"node": ">=14"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"
|
|
21
|
-
"react-dom": ">=17",
|
|
20
|
+
"@linzjs/lui": ">=17",
|
|
22
21
|
"ag-grid-community": ">=27",
|
|
23
22
|
"ag-grid-react": ">=27",
|
|
24
|
-
"
|
|
23
|
+
"react": ">=17",
|
|
24
|
+
"react-dom": ">=17"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@linzjs/lui": "
|
|
28
|
-
"ag-grid-community": "
|
|
29
|
-
"ag-grid-react": "
|
|
27
|
+
"@linzjs/lui": ">=17",
|
|
28
|
+
"ag-grid-community": ">=27",
|
|
29
|
+
"ag-grid-react": ">=27",
|
|
30
30
|
"debounce-promise": "^3.1.2",
|
|
31
31
|
"lodash-es": "^4.17.21",
|
|
32
|
-
"react": "^17.0.2",
|
|
33
|
-
"react-dom": "^17.0.2",
|
|
34
|
-
"uuid": "^9.0.0",
|
|
35
32
|
"prop-types": "^15.8.1",
|
|
36
|
-
"react
|
|
33
|
+
"react": ">=17",
|
|
34
|
+
"react-dom": ">=17",
|
|
35
|
+
"react-transition-state": "^1.1.5",
|
|
36
|
+
"uuid": "^9.0.0"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "rollup -c && npm run",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useContext, useMemo, useState } from "react";
|
|
1
|
+
import { ForwardedRef, forwardRef, useCallback, useContext, useMemo, useState } from "react";
|
|
2
2
|
import { GridBaseRow } from "./Grid";
|
|
3
3
|
import { UpdatingContext } from "@contexts/UpdatingContext";
|
|
4
4
|
import { GridContext } from "@contexts/GridContext";
|
|
@@ -27,7 +27,7 @@ export interface GenericCellEditorColDef<
|
|
|
27
27
|
FormProps extends GenericCellEditorParams<RowType>,
|
|
28
28
|
> extends ColDef {
|
|
29
29
|
cellEditorParams?: FormProps;
|
|
30
|
-
cellRendererParams?: GenericCellRendererParams
|
|
30
|
+
cellRendererParams?: GenericCellRendererParams<RowType>;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export const GridCellRenderer = (cellRendererParams: ICellRendererParams) => {
|
|
@@ -77,8 +77,9 @@ interface GenericCellEditorICellEditorParams<RowType extends GridBaseRow, FormPr
|
|
|
77
77
|
colDef: GenericCellEditorColDef<RowType, FormProps>;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
export const
|
|
80
|
+
export const GenericCellEditorComponentFr = <RowType extends GridBaseRow, FormProps extends Record<string, any>>(
|
|
81
81
|
props: GenericCellEditorICellEditorParams<RowType, FormProps>,
|
|
82
|
+
_: ForwardedRef<any>, // We don't forward the ref, as that's for generic aggrid cell editing
|
|
82
83
|
) => {
|
|
83
84
|
const { updatingCells, getSelectedRows } = useContext(GridContext);
|
|
84
85
|
|
|
@@ -123,3 +124,5 @@ export const GenericCellEditorComponent = <RowType extends GridBaseRow, FormProp
|
|
|
123
124
|
</>
|
|
124
125
|
);
|
|
125
126
|
};
|
|
127
|
+
|
|
128
|
+
export const GenericCellEditorComponent = forwardRef(GenericCellEditorComponentFr);
|
|
@@ -3,7 +3,7 @@ import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
|
3
3
|
import { GridContext } from "@contexts/GridContext";
|
|
4
4
|
import { GridFormProps } from "./GridCell";
|
|
5
5
|
import { GridBaseRow } from "./Grid";
|
|
6
|
-
import { ControlledMenu } from "
|
|
6
|
+
import { ControlledMenu } from "@react-menu3";
|
|
7
7
|
|
|
8
8
|
export const useGridPopoverHook = <RowType extends GridBaseRow>(
|
|
9
9
|
props: GridFormProps<RowType>,
|
|
@@ -153,7 +153,7 @@ export const GridFormDropDown = <RowType extends GridBaseRow, ValueType>(props:
|
|
|
153
153
|
<>
|
|
154
154
|
{formProps.filtered && (
|
|
155
155
|
<>
|
|
156
|
-
<FocusableItem className={"filter-item"}
|
|
156
|
+
<FocusableItem className={"filter-item"}>
|
|
157
157
|
{({ ref }: any) => (
|
|
158
158
|
<div style={{ display: "flex", width: "100%" }}>
|
|
159
159
|
<input
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "../../react-menu3/styles/index.scss";
|
|
2
2
|
|
|
3
|
-
import { MenuItem, MenuDivider, FocusableItem } from "@react-menu3";
|
|
4
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { MenuItem, MenuDivider, FocusableItem, ClickEvent } from "@react-menu3";
|
|
4
|
+
import { Fragment, useCallback, useEffect, useRef, useState, KeyboardEvent } from "react";
|
|
5
5
|
import { GridBaseRow } from "../Grid";
|
|
6
6
|
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
|
|
7
7
|
import { delay } from "lodash-es";
|
|
@@ -58,7 +58,7 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(prop
|
|
|
58
58
|
},
|
|
59
59
|
[formProps, selectedValues],
|
|
60
60
|
);
|
|
61
|
-
const { popoverWrapper } = useGridPopoverHook(props, save);
|
|
61
|
+
const { popoverWrapper, triggerSave } = useGridPopoverHook(props, save);
|
|
62
62
|
|
|
63
63
|
// Load up options list if it's async function
|
|
64
64
|
useEffect(() => {
|
|
@@ -99,7 +99,7 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(prop
|
|
|
99
99
|
return undefined;
|
|
100
100
|
}
|
|
101
101
|
const str = (option.label as string) || "";
|
|
102
|
-
return str.toLowerCase().indexOf(filter) === -1 ? option.value : undefined;
|
|
102
|
+
return str.toLowerCase().indexOf(filter.trim()) === -1 ? option.value : undefined;
|
|
103
103
|
})
|
|
104
104
|
.filter((r) => r !== undefined),
|
|
105
105
|
);
|
|
@@ -110,7 +110,7 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(prop
|
|
|
110
110
|
<div className={"Grid-popoverContainerList"}>
|
|
111
111
|
{options && formProps.filtered && (
|
|
112
112
|
<>
|
|
113
|
-
<FocusableItem className={"filter-item"}>
|
|
113
|
+
<FocusableItem className={"filter-item"} key={"filter"}>
|
|
114
114
|
{({ ref }: any) => (
|
|
115
115
|
<div style={{ display: "flex", width: "100%" }}>
|
|
116
116
|
<input
|
|
@@ -134,28 +134,47 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(prop
|
|
|
134
134
|
item.value === MenuSeparatorString ? (
|
|
135
135
|
<MenuDivider key={`$$divider_${index}`} />
|
|
136
136
|
) : filteredValues.includes(item.value) ? null : (
|
|
137
|
-
|
|
137
|
+
<Fragment key={`${index}`}>
|
|
138
138
|
<MenuItem
|
|
139
|
-
key={`${
|
|
140
|
-
onClick={(e:
|
|
139
|
+
key={`${index}`}
|
|
140
|
+
onClick={(e: ClickEvent) => {
|
|
141
141
|
e.keepOpen = true;
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
if (selectedValues.includes(item.value)) {
|
|
143
|
+
setSelectedValues(selectedValues.filter((value) => value != item.value));
|
|
144
|
+
} else {
|
|
145
|
+
setSelectedValues([...selectedValues, item.value]);
|
|
146
|
+
}
|
|
147
|
+
}}
|
|
148
|
+
onKeyDown={async (e: KeyboardEvent) => {
|
|
149
|
+
if (e.key === "Enter") triggerSave().then();
|
|
150
|
+
else if (e.key === " ") {
|
|
151
|
+
if (selectedValues.includes(item.value)) {
|
|
152
|
+
setSelectedValues(selectedValues.filter((value) => value != item.value));
|
|
153
|
+
} else {
|
|
154
|
+
setSelectedValues([...selectedValues, item.value]);
|
|
155
|
+
}
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
e.stopPropagation();
|
|
158
|
+
}
|
|
144
159
|
}}
|
|
145
160
|
>
|
|
146
161
|
<LuiCheckboxInput
|
|
147
162
|
isChecked={selectedValues.includes(item.value)}
|
|
148
163
|
value={`${item.value}`}
|
|
149
164
|
label={item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`)}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
165
|
+
inputProps={{
|
|
166
|
+
onClick: (e) => {
|
|
167
|
+
e.preventDefault();
|
|
168
|
+
e.stopPropagation();
|
|
169
|
+
return false;
|
|
170
|
+
},
|
|
171
|
+
}}
|
|
172
|
+
onChange={() => {
|
|
173
|
+
/*Do nothing, change handled by menuItem*/
|
|
156
174
|
}}
|
|
157
175
|
/>
|
|
158
176
|
</MenuItem>
|
|
177
|
+
|
|
159
178
|
{selectedValues.includes(item.value) && item.subComponent && (
|
|
160
179
|
<FocusableItem className={"LuiDeprecatedForms"} key={`${item.value}_subcomponent`}>
|
|
161
180
|
{(ref: any) =>
|
|
@@ -171,7 +190,7 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(prop
|
|
|
171
190
|
}
|
|
172
191
|
</FocusableItem>
|
|
173
192
|
)}
|
|
174
|
-
|
|
193
|
+
</Fragment>
|
|
175
194
|
),
|
|
176
195
|
)}
|
|
177
196
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./GridRenderGenericCell.scss";
|
|
2
2
|
|
|
3
3
|
import { useContext } from "react";
|
|
4
|
-
import { UpdatingContext } from "
|
|
4
|
+
import { UpdatingContext } from "@contexts/UpdatingContext";
|
|
5
5
|
import { GridLoadableCell } from "../GridLoadableCell";
|
|
6
6
|
import { GridIcon } from "../GridIcon";
|
|
7
7
|
import { ColDef, ICellRendererParams } from "ag-grid-community";
|
|
@@ -9,21 +9,25 @@ import { ValueFormatterParams } from "ag-grid-community/dist/lib/entities/colDef
|
|
|
9
9
|
import { GenericCellEditorParams } from "../GridCell";
|
|
10
10
|
import { GridBaseRow } from "../Grid";
|
|
11
11
|
|
|
12
|
+
export interface RowICellRendererParams<RowType extends GridBaseRow> extends ICellRendererParams {
|
|
13
|
+
data: RowType;
|
|
14
|
+
}
|
|
15
|
+
|
|
12
16
|
export interface GenericCellColDef<RowType extends GridBaseRow, FormProps extends Record<string, any>> extends ColDef {
|
|
13
|
-
cellRendererParams?: GenericCellRendererParams
|
|
17
|
+
cellRendererParams?: GenericCellRendererParams<RowType>;
|
|
14
18
|
cellEditorParams?: GenericCellEditorParams<RowType> & FormProps;
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
export interface GenericCellRendererParams {
|
|
21
|
+
export interface GenericCellRendererParams<RowType extends GridBaseRow> {
|
|
18
22
|
singleClickEdit?: boolean;
|
|
19
|
-
warning?: (props:
|
|
20
|
-
info?: (props:
|
|
23
|
+
warning?: (props: RowICellRendererParams<RowType>) => string | boolean | undefined;
|
|
24
|
+
info?: (props: RowICellRendererParams<RowType>) => string | boolean | undefined;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
|
-
export const GridRendererGenericCell = (props: ICellRendererParams): JSX.Element => {
|
|
27
|
+
export const GridRendererGenericCell = <RowType extends GridBaseRow>(props: ICellRendererParams): JSX.Element => {
|
|
24
28
|
const { checkUpdating } = useContext(UpdatingContext);
|
|
25
29
|
|
|
26
|
-
const cellRendererParams = props.colDef?.cellRendererParams as GenericCellRendererParams | undefined;
|
|
30
|
+
const cellRendererParams = props.colDef?.cellRendererParams as GenericCellRendererParams<RowType> | undefined;
|
|
27
31
|
const warningFn = cellRendererParams?.warning;
|
|
28
32
|
const warningText = warningFn ? warningFn(props) : undefined;
|
|
29
33
|
const infoFn = cellRendererParams?.info;
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import { useContext } from "react";
|
|
2
|
-
import { ICellRendererParams } from "ag-grid-community";
|
|
2
|
+
import { ColDef, ICellRendererParams } from "ag-grid-community";
|
|
3
3
|
import { UpdatingContext } from "@contexts/UpdatingContext";
|
|
4
4
|
import { GridLoadableCell } from "../GridLoadableCell";
|
|
5
5
|
import { LuiIcon } from "@linzjs/lui";
|
|
6
|
+
import { Column } from "ag-grid-community/dist/lib/entities/column";
|
|
6
7
|
|
|
7
8
|
export const GridRenderPopoutMenuCell = (props: ICellRendererParams) => {
|
|
8
9
|
const { checkUpdating } = useContext(UpdatingContext);
|
|
9
10
|
const isLoading = checkUpdating(props.colDef?.field ?? "", props.data.id);
|
|
10
|
-
const
|
|
11
|
+
const editable = props.colDef?.editable;
|
|
12
|
+
const disabled = !(typeof editable === "function"
|
|
13
|
+
? editable({
|
|
14
|
+
node: props.node,
|
|
15
|
+
data: props.data,
|
|
16
|
+
column: props.column as Column,
|
|
17
|
+
colDef: props.colDef as ColDef,
|
|
18
|
+
api: props.api,
|
|
19
|
+
columnApi: props.columnApi,
|
|
20
|
+
context: props.context,
|
|
21
|
+
})
|
|
22
|
+
: editable);
|
|
11
23
|
|
|
12
24
|
return (
|
|
13
25
|
<GridLoadableCell
|
|
@@ -258,9 +258,10 @@ export const GridContextProvider = (props: GridContextProps): ReactElement => {
|
|
|
258
258
|
}
|
|
259
259
|
// This is needed to trigger postSortRowsHook
|
|
260
260
|
gridApi.refreshClientSideRowModel();
|
|
261
|
-
|
|
261
|
+
} else {
|
|
262
|
+
// Don't set saving if ok as the form has already closed
|
|
263
|
+
setSaving && setSaving(false);
|
|
262
264
|
}
|
|
263
|
-
setSaving && setSaving(false);
|
|
264
265
|
return ok;
|
|
265
266
|
});
|
|
266
267
|
};
|
|
@@ -1,27 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useRef,
|
|
4
|
+
useMemo,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
KeyboardEvent,
|
|
8
|
+
FocusEvent,
|
|
9
|
+
MutableRefObject,
|
|
10
|
+
ForwardedRef,
|
|
11
|
+
} from "react";
|
|
3
12
|
import { createPortal } from "react-dom";
|
|
4
|
-
import { string, number, bool, func, object, oneOf, oneOfType, exact } from "prop-types";
|
|
5
13
|
import { MenuList } from "./MenuList";
|
|
6
14
|
import { useBEM } from "../hooks";
|
|
7
15
|
import {
|
|
8
|
-
rootMenuPropTypes,
|
|
9
16
|
menuContainerClass,
|
|
10
17
|
mergeProps,
|
|
11
18
|
safeCall,
|
|
12
19
|
isMenuOpen,
|
|
13
20
|
getTransition,
|
|
14
|
-
values,
|
|
15
21
|
CloseReason,
|
|
16
22
|
Keys,
|
|
17
|
-
MenuStateMap,
|
|
18
23
|
EventHandlersContext,
|
|
19
24
|
SettingsContext,
|
|
20
25
|
ItemSettingsContext,
|
|
21
26
|
} from "../utils";
|
|
22
27
|
import { hasParentClass } from "@utils/util";
|
|
28
|
+
import { ControlledMenuProps, PortalFieldType, RadioChangeEvent } from "../types";
|
|
23
29
|
|
|
24
|
-
export const
|
|
30
|
+
export const ControlledMenuFr = (
|
|
25
31
|
{
|
|
26
32
|
"aria-label": ariaLabel,
|
|
27
33
|
className,
|
|
@@ -43,11 +49,11 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
|
|
|
43
49
|
onClose,
|
|
44
50
|
saveButtonRef,
|
|
45
51
|
...restProps
|
|
46
|
-
}:
|
|
47
|
-
externalRef
|
|
48
|
-
) {
|
|
49
|
-
const containerRef = useRef(
|
|
50
|
-
const scrollNodesRef = useRef({});
|
|
52
|
+
}: ControlledMenuProps & { saveButtonRef?: MutableRefObject<HTMLButtonElement | null> },
|
|
53
|
+
externalRef: ForwardedRef<HTMLUListElement>,
|
|
54
|
+
) => {
|
|
55
|
+
const containerRef = useRef<HTMLElement>();
|
|
56
|
+
const scrollNodesRef = useRef<{ anchors?: HTMLDivElement[] }>({});
|
|
51
57
|
const { anchorRef, state } = restProps;
|
|
52
58
|
|
|
53
59
|
const settings = useMemo(
|
|
@@ -91,7 +97,7 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
|
|
|
91
97
|
// the cell doesn't refresh during update if save is invoked from a native event
|
|
92
98
|
// This doesn't happen in React18
|
|
93
99
|
// To work around it, I invoke the save by clicking on a passed in invisible button ref
|
|
94
|
-
if (saveButtonRef
|
|
100
|
+
if (saveButtonRef?.current) saveButtonRef.current.click();
|
|
95
101
|
else safeCall(onClose, { reason: CloseReason.BLUR });
|
|
96
102
|
|
|
97
103
|
// If a user clicks on the menu button when a menu is open, we need to close the menu.
|
|
@@ -106,7 +112,7 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
|
|
|
106
112
|
}
|
|
107
113
|
}
|
|
108
114
|
},
|
|
109
|
-
[clickIsWithinMenu, onClose],
|
|
115
|
+
[clickIsWithinMenu, onClose, saveButtonRef, skipOpen],
|
|
110
116
|
);
|
|
111
117
|
|
|
112
118
|
const handleScreenEventForCancel = useCallback(
|
|
@@ -121,7 +127,7 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
|
|
|
121
127
|
|
|
122
128
|
useEffect(() => {
|
|
123
129
|
if (isMenuOpen(state)) {
|
|
124
|
-
const thisDocument = anchorRef
|
|
130
|
+
const thisDocument = anchorRef?.current ? anchorRef?.current.ownerDocument : document;
|
|
125
131
|
thisDocument.addEventListener("mousedown", handleScreenEventForCancel, true);
|
|
126
132
|
thisDocument.addEventListener("mouseup", handleScreenEventForSave, true);
|
|
127
133
|
thisDocument.addEventListener("click", handleScreenEventForCancel, true);
|
|
@@ -134,7 +140,7 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
|
|
|
134
140
|
};
|
|
135
141
|
}
|
|
136
142
|
return () => {};
|
|
137
|
-
}, [handleScreenEventForSave, handleScreenEventForCancel, state]);
|
|
143
|
+
}, [handleScreenEventForSave, handleScreenEventForCancel, state, anchorRef]);
|
|
138
144
|
|
|
139
145
|
const itemSettings = useMemo(
|
|
140
146
|
() => ({
|
|
@@ -146,7 +152,7 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
|
|
|
146
152
|
|
|
147
153
|
const eventHandlers = useMemo(
|
|
148
154
|
() => ({
|
|
149
|
-
handleClick(event,
|
|
155
|
+
handleClick(event: RadioChangeEvent, isCheckOrRadio: boolean) {
|
|
150
156
|
if (!event.stopPropagation) safeCall(onItemClick, event);
|
|
151
157
|
|
|
152
158
|
let keepOpen = event.keepOpen;
|
|
@@ -154,7 +160,7 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
|
|
|
154
160
|
// if event.keepOpen is undefined, the following default behaviour is used
|
|
155
161
|
// According to WAI-ARIA Authoring Practices 1.1
|
|
156
162
|
// Keep menu open when check or radio is invoked by SPACE key
|
|
157
|
-
keepOpen =
|
|
163
|
+
keepOpen = isCheckOrRadio && event.key === Keys.SPACE;
|
|
158
164
|
}
|
|
159
165
|
|
|
160
166
|
if (!keepOpen) {
|
|
@@ -166,14 +172,14 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
|
|
|
166
172
|
}
|
|
167
173
|
},
|
|
168
174
|
|
|
169
|
-
handleClose(key) {
|
|
175
|
+
handleClose(key?: string) {
|
|
170
176
|
safeCall(onClose, { key, reason: CloseReason.CLICK });
|
|
171
177
|
},
|
|
172
178
|
}),
|
|
173
179
|
[onItemClick, onClose],
|
|
174
180
|
);
|
|
175
181
|
|
|
176
|
-
const onKeyDown = ({ key }) => {
|
|
182
|
+
const onKeyDown = ({ key }: KeyboardEvent) => {
|
|
177
183
|
switch (key) {
|
|
178
184
|
case Keys.ESC:
|
|
179
185
|
safeCall(onClose, { key, reason: CloseReason.CANCEL });
|
|
@@ -181,14 +187,14 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
|
|
|
181
187
|
}
|
|
182
188
|
};
|
|
183
189
|
|
|
184
|
-
const onBlur = (e) => {
|
|
190
|
+
const onBlur = (e: FocusEvent) => {
|
|
185
191
|
if (isMenuOpen(state) && !e.currentTarget.contains(e.relatedTarget || document.activeElement)) {
|
|
186
192
|
safeCall(onClose, { reason: CloseReason.BLUR });
|
|
187
193
|
|
|
188
194
|
// If a user clicks on the menu button when a menu is open, we need to close the menu.
|
|
189
195
|
// However, a blur event will be fired prior to the click event on menu button,
|
|
190
196
|
// which makes the menu first close and then open again.
|
|
191
|
-
// If this
|
|
197
|
+
// If this happens, e.relatedTarget is incorrectly set to null instead of the button in Safari and Firefox,
|
|
192
198
|
// and makes it difficult to determine whether onBlur is fired because of clicking on menu button.
|
|
193
199
|
// This is a workaround approach which sets a flag to skip a following click event.
|
|
194
200
|
if (skipOpen) {
|
|
@@ -230,31 +236,20 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
|
|
|
230
236
|
</div>
|
|
231
237
|
);
|
|
232
238
|
|
|
233
|
-
if (portal === true && anchorRef
|
|
234
|
-
portal = { target: anchorRef.current.ownerDocument.body };
|
|
239
|
+
if (portal === true && anchorRef?.current !== undefined) {
|
|
240
|
+
portal = { target: anchorRef.current.ownerDocument.body } as PortalFieldType;
|
|
235
241
|
}
|
|
236
242
|
|
|
237
|
-
if (portal
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
243
|
+
if (portal) {
|
|
244
|
+
if (typeof portal === "boolean") {
|
|
245
|
+
if (portal && typeof document !== "undefined") {
|
|
246
|
+
return createPortal(menuList, document.body);
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
return portal.target ? createPortal(menuList, portal.target) : portal.stablePosition ? null : menuList;
|
|
250
|
+
}
|
|
241
251
|
}
|
|
242
252
|
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
253
|
};
|
|
254
|
+
|
|
255
|
+
export const ControlledMenu = forwardRef(ControlledMenuFr);
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { useContext, useMemo, useRef } from "react";
|
|
3
|
-
import { bool, func } from "prop-types";
|
|
1
|
+
import { LegacyRef, useContext, useMemo, useRef } from "react";
|
|
4
2
|
import { useBEM, useCombinedRef, useItemState } from "../hooks";
|
|
5
3
|
import {
|
|
6
4
|
mergeProps,
|
|
@@ -8,63 +6,70 @@ import {
|
|
|
8
6
|
safeCall,
|
|
9
7
|
menuClass,
|
|
10
8
|
menuItemClass,
|
|
11
|
-
stylePropTypes,
|
|
12
9
|
withHovering,
|
|
13
10
|
EventHandlersContext,
|
|
14
11
|
} from "../utils";
|
|
12
|
+
import { BaseProps } from "../types";
|
|
13
|
+
import { withHoveringResultProps } from "../utils/withHovering";
|
|
15
14
|
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const ref = useRef(null);
|
|
21
|
-
const { setHover, onPointerLeave, ...restStateProps } = useItemState(itemRef, ref, isHovering, isDisabled);
|
|
22
|
-
const { handleClose } = useContext(EventHandlersContext);
|
|
15
|
+
export interface FocusableItemProps extends BaseProps, withHoveringResultProps {
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
children: (ref: LegacyRef<any>) => JSX.Element;
|
|
18
|
+
}
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
export const FocusableItemFr = ({
|
|
21
|
+
className,
|
|
22
|
+
disabled,
|
|
23
|
+
children,
|
|
24
|
+
isHovering,
|
|
25
|
+
menuItemRef,
|
|
26
|
+
externalRef,
|
|
27
|
+
...restProps
|
|
28
|
+
}: FocusableItemProps) => {
|
|
29
|
+
const isDisabled = !!disabled;
|
|
30
|
+
const ref = useRef(null);
|
|
31
|
+
const { setHover, onPointerLeave, ...restStateProps } = useItemState(menuItemRef, ref, isHovering, isDisabled);
|
|
32
|
+
const { handleClose } = useContext(EventHandlersContext);
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
);
|
|
34
|
+
const modifiers = useMemo(
|
|
35
|
+
() => ({
|
|
36
|
+
disabled: isDisabled,
|
|
37
|
+
hover: isHovering,
|
|
38
|
+
focusable: true,
|
|
39
|
+
}),
|
|
40
|
+
[isDisabled, isHovering],
|
|
41
|
+
);
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
const renderChildren = useMemo(
|
|
44
|
+
() =>
|
|
45
|
+
safeCall(children, {
|
|
46
|
+
...modifiers,
|
|
47
|
+
ref,
|
|
48
|
+
closeMenu: handleClose,
|
|
49
|
+
}),
|
|
50
|
+
[children, modifiers, handleClose],
|
|
51
|
+
);
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
{renderChildren}
|
|
61
|
-
</li>
|
|
62
|
-
);
|
|
63
|
-
},
|
|
64
|
-
);
|
|
53
|
+
const mergedProps = mergeProps(
|
|
54
|
+
{
|
|
55
|
+
...restStateProps,
|
|
56
|
+
onPointerLeave: (e: PointerEvent) => onPointerLeave(e, true),
|
|
57
|
+
onFocus: setHover,
|
|
58
|
+
},
|
|
59
|
+
restProps,
|
|
60
|
+
);
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
return (
|
|
63
|
+
<li
|
|
64
|
+
role="menuitem"
|
|
65
|
+
{...mergedProps}
|
|
66
|
+
{...commonProps(isDisabled)}
|
|
67
|
+
ref={useCombinedRef(externalRef, menuItemRef)}
|
|
68
|
+
className={useBEM({ block: menuClass, element: menuItemClass, modifiers, className })}
|
|
69
|
+
>
|
|
70
|
+
{renderChildren}
|
|
71
|
+
</li>
|
|
72
|
+
);
|
|
70
73
|
};
|
|
74
|
+
|
|
75
|
+
export const FocusableItem = withHovering("FocusableItem", FocusableItemFr);
|