@linzjs/step-ag-grid 1.4.6 → 1.4.8

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.
Files changed (71) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +133 -99
  3. package/dist/index.js.map +1 -1
  4. package/dist/src/components/GridCell.d.ts +5 -4
  5. package/dist/src/components/gridForm/GridFormPopoutMenu.d.ts +1 -0
  6. package/dist/src/components/gridRender/GridRenderGenericCell.d.ts +8 -5
  7. package/dist/src/react-menu3/components/ControlledMenu.d.ts +8 -2
  8. package/dist/src/react-menu3/components/FocusableItem.d.ts +2 -2
  9. package/dist/src/react-menu3/components/Menu.d.ts +1 -1
  10. package/dist/src/react-menu3/components/MenuButton.d.ts +1 -1
  11. package/dist/src/react-menu3/components/MenuDivider.d.ts +3 -3
  12. package/dist/src/react-menu3/components/MenuGroup.d.ts +1 -1
  13. package/dist/src/react-menu3/components/MenuHeader.d.ts +1 -1
  14. package/dist/src/react-menu3/components/MenuItem.d.ts +2 -4
  15. package/dist/src/react-menu3/components/MenuList.d.ts +2 -56
  16. package/dist/src/react-menu3/components/MenuRadioGroup.d.ts +2 -2
  17. package/dist/src/react-menu3/components/SubMenu.d.ts +6 -6
  18. package/dist/src/react-menu3/hooks/useBEM.d.ts +1 -1
  19. package/dist/src/react-menu3/hooks/useItems.d.ts +1 -1
  20. package/dist/src/react-menu3/hooks/useMenuChange.d.ts +1 -1
  21. package/dist/src/react-menu3/hooks/useMenuState.d.ts +1 -1
  22. package/dist/src/react-menu3/hooks/useMenuStateAndFocus.d.ts +1 -1
  23. package/dist/src/react-menu3/index.d.ts +1 -391
  24. package/dist/src/react-menu3/positionUtils/getPositionHelpers.d.ts +1 -1
  25. package/dist/src/react-menu3/positionUtils/placeLeftorRight.d.ts +1 -1
  26. package/dist/src/react-menu3/positionUtils/placeToporBottom.d.ts +1 -1
  27. package/dist/src/react-menu3/positionUtils/positionContextMenu.d.ts +1 -1
  28. package/dist/src/react-menu3/positionUtils/positionMenu.d.ts +1 -1
  29. package/dist/src/react-menu3/types.d.ts +389 -0
  30. package/dist/src/react-menu3/utils/constants.d.ts +12 -5
  31. package/dist/src/react-menu3/utils/index.d.ts +0 -1
  32. package/dist/src/react-menu3/utils/{propTypes.d.ts → propTypes2.d.ts} +0 -0
  33. package/dist/src/react-menu3/utils/utils.d.ts +2 -2
  34. package/dist/step-ag-grid.esm.js +124 -101
  35. package/dist/step-ag-grid.esm.js.map +1 -1
  36. package/package.json +1 -1
  37. package/src/components/GridCell.tsx +32 -16
  38. package/src/components/GridPopoverHook.tsx +1 -1
  39. package/src/components/gridForm/GridFormMultiSelect.tsx +32 -19
  40. package/src/components/gridForm/GridFormPopoutMenu.tsx +11 -8
  41. package/src/components/gridRender/GridRenderGenericCell.tsx +14 -9
  42. package/src/components/gridRender/GridRenderPopoutMenuCell.tsx +14 -2
  43. package/src/contexts/GridContextProvider.tsx +3 -2
  44. package/src/react-menu3/components/ControlledMenu.tsx +40 -46
  45. package/src/react-menu3/components/FocusableItem.tsx +2 -2
  46. package/src/react-menu3/components/Menu.tsx +1 -1
  47. package/src/react-menu3/components/MenuButton.tsx +1 -1
  48. package/src/react-menu3/components/MenuDivider.tsx +2 -2
  49. package/src/react-menu3/components/MenuGroup.tsx +1 -1
  50. package/src/react-menu3/components/MenuHeader.tsx +1 -1
  51. package/src/react-menu3/components/MenuItem.tsx +2 -3
  52. package/src/react-menu3/components/MenuList.tsx +19 -62
  53. package/src/react-menu3/components/MenuRadioGroup.tsx +2 -2
  54. package/src/react-menu3/components/SubMenu.tsx +17 -8
  55. package/src/react-menu3/hooks/useBEM.ts +1 -1
  56. package/src/react-menu3/hooks/useItems.ts +1 -1
  57. package/src/react-menu3/hooks/useMenuChange.ts +1 -1
  58. package/src/react-menu3/hooks/useMenuState.ts +1 -1
  59. package/src/react-menu3/hooks/useMenuStateAndFocus.ts +1 -1
  60. package/src/react-menu3/index.ts +1 -431
  61. package/src/react-menu3/positionUtils/getPositionHelpers.ts +3 -3
  62. package/src/react-menu3/positionUtils/placeLeftorRight.ts +1 -1
  63. package/src/react-menu3/positionUtils/placeToporBottom.ts +1 -1
  64. package/src/react-menu3/positionUtils/positionContextMenu.ts +1 -1
  65. package/src/react-menu3/positionUtils/positionMenu.ts +1 -1
  66. package/src/react-menu3/{index.d.ts → types.ts} +106 -359
  67. package/src/react-menu3/utils/constants.ts +24 -5
  68. package/src/react-menu3/utils/index.ts +1 -1
  69. package/src/react-menu3/utils/{propTypes.ts → propTypes2.ts} +0 -0
  70. package/src/react-menu3/utils/utils.ts +2 -2
  71. package/src/stories/components/GridReadOnly.stories.tsx +9 -5
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.6",
5
+ "version": "1.4.8",
6
6
  "main": "dist/index.js",
7
7
  "typings": "dist/index.d.ts",
8
8
  "module": "dist/step-ag-grid.esm.js",
@@ -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";
@@ -6,6 +6,8 @@ import { GenericMultiEditCellClass } from "./GenericCellClass";
6
6
  import { GenericCellRendererParams, GridRendererGenericCell } from "./gridRender/GridRenderGenericCell";
7
7
  import { ColDef, ICellEditorParams, ICellRendererParams } from "ag-grid-community";
8
8
  import { GridLoadableCell } from "./GridLoadableCell";
9
+ import { GridIcon } from "@components/GridIcon";
10
+ import { ValueFormatterParams } from "ag-grid-community/dist/lib/entities/colDef";
9
11
 
10
12
  export interface GridFormProps<RowType extends GridBaseRow> {
11
13
  cellEditorParams: ICellEditorParams;
@@ -27,24 +29,29 @@ export interface GenericCellEditorColDef<
27
29
  FormProps extends GenericCellEditorParams<RowType>,
28
30
  > extends ColDef {
29
31
  cellEditorParams?: FormProps;
30
- cellRendererParams?: GenericCellRendererParams;
32
+ cellRendererParams?: GenericCellRendererParams<RowType>;
31
33
  }
32
34
 
33
- export const GridCellRenderer = (cellRendererParams: ICellRendererParams) => {
35
+ export const GridCellRenderer = (props: ICellRendererParams) => {
34
36
  const { checkUpdating } = useContext(UpdatingContext);
35
- const colDef = cellRendererParams.colDef;
37
+ const colDef = props.colDef as ColDef;
36
38
 
37
- return colDef?.cellRendererParams?.oldCellRenderer ? (
38
- <GridLoadableCell
39
- isLoading={checkUpdating(
40
- cellRendererParams.colDef?.field ?? cellRendererParams.colDef?.colId ?? "",
41
- cellRendererParams.data.id,
42
- )}
43
- >
44
- <colDef.cellRendererParams.oldCellRenderer {...cellRendererParams} />
39
+ const rendererParams = colDef.cellRendererParams as GenericCellRendererParams<any> | undefined;
40
+ const warningFn = rendererParams?.warning;
41
+ const warningText = warningFn ? warningFn(props) : undefined;
42
+ const infoFn = rendererParams?.info;
43
+ const infoText = infoFn ? infoFn(props) : undefined;
44
+
45
+ return colDef?.cellRendererParams?.originalCellRenderer ? (
46
+ <GridLoadableCell isLoading={checkUpdating(colDef.field ?? colDef.colId ?? "", props.data.id)}>
47
+ <>
48
+ {typeof warningText === "string" && <GridIcon icon={"ic_warning"} title={warningText} />}
49
+ {typeof infoText === "string" && <GridIcon icon={"ic_info"} title={infoText} />}
50
+ <colDef.cellRendererParams.originalCellRenderer {...props} />
51
+ </>
45
52
  </GridLoadableCell>
46
53
  ) : (
47
- <GridRendererGenericCell {...cellRendererParams} />
54
+ <GridRendererGenericCell {...props} />
48
55
  );
49
56
  };
50
57
 
@@ -55,7 +62,6 @@ export const GridCell = <RowType extends GridBaseRow, FormProps extends GenericC
55
62
  props: GenericCellEditorColDef<RowType, FormProps>,
56
63
  ): ColDef => {
57
64
  return {
58
- cellRenderer: GridCellRenderer,
59
65
  sortable: !!(props?.field || props?.valueGetter),
60
66
  resizable: true,
61
67
  ...(props.cellEditorParams && {
@@ -63,9 +69,16 @@ export const GridCell = <RowType extends GridBaseRow, FormProps extends GenericC
63
69
  editable: true,
64
70
  cellEditor: GenericCellEditorComponent,
65
71
  }),
72
+ // Default value formatter, otherwise react freaks out on objects
73
+ valueFormatter: (params: ValueFormatterParams) => {
74
+ const types = ["number", "undefined", "boolean", "string"];
75
+ if (types.includes(typeof params.value)) return params.value;
76
+ else return JSON.stringify(params.value);
77
+ },
66
78
  ...props,
79
+ cellRenderer: GridCellRenderer,
67
80
  cellRendererParams: {
68
- originalCellRender: props.cellRenderer,
81
+ originalCellRenderer: props.cellRenderer,
69
82
  ...props.cellRendererParams,
70
83
  },
71
84
  };
@@ -77,8 +90,9 @@ interface GenericCellEditorICellEditorParams<RowType extends GridBaseRow, FormPr
77
90
  colDef: GenericCellEditorColDef<RowType, FormProps>;
78
91
  }
79
92
 
80
- export const GenericCellEditorComponent = <RowType extends GridBaseRow, FormProps extends Record<string, any>>(
93
+ export const GenericCellEditorComponentFr = <RowType extends GridBaseRow, FormProps extends Record<string, any>>(
81
94
  props: GenericCellEditorICellEditorParams<RowType, FormProps>,
95
+ _: ForwardedRef<any>, // We don't forward the ref, as that's for generic aggrid cell editing
82
96
  ) => {
83
97
  const { updatingCells, getSelectedRows } = useContext(GridContext);
84
98
 
@@ -123,3 +137,5 @@ export const GenericCellEditorComponent = <RowType extends GridBaseRow, FormProp
123
137
  </>
124
138
  );
125
139
  };
140
+
141
+ 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 "../react-menu3";
6
+ import { ControlledMenu } from "@react-menu3";
7
7
 
8
8
  export const useGridPopoverHook = <RowType extends GridBaseRow>(
9
9
  props: GridFormProps<RowType>,
@@ -1,7 +1,7 @@
1
1
  import "../../react-menu3/styles/index.scss";
2
2
 
3
- import { MenuItem, MenuDivider, FocusableItem, RadioChangeEvent } 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";
@@ -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,34 +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={`${item.value}`}
140
- onClick={(e: RadioChangeEvent) => {
141
- // FIXME Matt Event type guessed here
139
+ key={`${index}`}
140
+ onClick={(e: ClickEvent) => {
142
141
  e.keepOpen = true;
143
- if (selectedValues.includes(e.value)) {
142
+ if (selectedValues.includes(item.value)) {
144
143
  setSelectedValues(selectedValues.filter((value) => value != item.value));
145
144
  } else {
146
- setSelectedValues([...selectedValues, item.value as string]);
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();
147
158
  }
148
- return false;
149
159
  }}
150
- onKeyDown={async (e) => e.key === "Enter" && triggerSave().then()}
151
160
  >
152
161
  <LuiCheckboxInput
153
162
  isChecked={selectedValues.includes(item.value)}
154
163
  value={`${item.value}`}
155
164
  label={item.label ?? (item.value == null ? `<${item.value}>` : `${item.value}`)}
156
- onChange={(e) => {
157
- if (e.target.checked) {
158
- setSelectedValues([...selectedValues, item.value as string]);
159
- } else {
160
- setSelectedValues(selectedValues.filter((value) => value != item.value));
161
- }
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*/
162
174
  }}
163
175
  />
164
176
  </MenuItem>
177
+
165
178
  {selectedValues.includes(item.value) && item.subComponent && (
166
179
  <FocusableItem className={"LuiDeprecatedForms"} key={`${item.value}_subcomponent`}>
167
180
  {(ref: any) =>
@@ -177,7 +190,7 @@ export const GridFormMultiSelect = <RowType extends GridBaseRow, ValueType>(prop
177
190
  }
178
191
  </FocusableItem>
179
192
  )}
180
- </>
193
+ </Fragment>
181
194
  ),
182
195
  )}
183
196
  </div>
@@ -22,6 +22,7 @@ export interface MenuOption<RowType> {
22
22
  action?: (selectedRows: RowType[]) => Promise<boolean>;
23
23
  disabled?: string | boolean;
24
24
  supportsMultiEdit: boolean;
25
+ hidden?: boolean;
25
26
  }
26
27
 
27
28
  /**
@@ -76,14 +77,16 @@ export const GridFormPopoutMenu = <RowType extends GridBaseRow>(props: GridFormP
76
77
  item.label === MenuSeparator ? (
77
78
  <MenuDivider key={`$$divider_${index}`} />
78
79
  ) : (
79
- <MenuItem
80
- key={`${item.label}`}
81
- onClick={() => actionClick(item)}
82
- disabled={!!item.disabled || !filteredOptions?.includes(item)}
83
- title={item.disabled && typeof item.disabled !== "boolean" ? item.disabled : ""}
84
- >
85
- {item.label as JSX.Element | string}
86
- </MenuItem>
80
+ !item.hidden && (
81
+ <MenuItem
82
+ key={`${item.label}`}
83
+ onClick={() => actionClick(item)}
84
+ disabled={!!item.disabled || !filteredOptions?.includes(item)}
85
+ title={item.disabled && typeof item.disabled !== "boolean" ? item.disabled : ""}
86
+ >
87
+ {item.label as JSX.Element | string}
88
+ </MenuItem>
89
+ )
87
90
  ),
88
91
  )}
89
92
  </div>
@@ -1,7 +1,7 @@
1
1
  import "./GridRenderGenericCell.scss";
2
2
 
3
3
  import { useContext } from "react";
4
- import { UpdatingContext } from "../../contexts/UpdatingContext";
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,28 +9,33 @@ 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: ICellRendererParams) => string | boolean | undefined;
20
- info?: (props: ICellRendererParams) => string | boolean | undefined;
23
+ warning?: (props: RowICellRendererParams<RowType>) => string | boolean | null | undefined;
24
+ info?: (props: RowICellRendererParams<RowType>) => string | boolean | null | 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 colDef = props.colDef as ColDef;
31
+ const cellRendererParams = colDef.cellRendererParams as GenericCellRendererParams<RowType> | undefined;
27
32
  const warningFn = cellRendererParams?.warning;
28
33
  const warningText = warningFn ? warningFn(props) : undefined;
29
34
  const infoFn = cellRendererParams?.info;
30
35
  const infoText = infoFn ? infoFn(props) : undefined;
31
36
 
32
37
  const defaultFormatter = (props: ValueFormatterParams): string => props.value;
33
- const formatter = props.colDef?.valueFormatter ?? defaultFormatter;
38
+ const formatter = colDef.valueFormatter ?? defaultFormatter;
34
39
  if (typeof formatter === "string") {
35
40
  console.error("valueFormatter must be a function");
36
41
  return <span>valueFormatter must be a function</span>;
@@ -38,7 +43,7 @@ export const GridRendererGenericCell = (props: ICellRendererParams): JSX.Element
38
43
  const formatted = formatter(props as ValueFormatterParams);
39
44
 
40
45
  return (
41
- <GridLoadableCell isLoading={checkUpdating(props.colDef?.field ?? props.colDef?.colId ?? "", props.data.id)}>
46
+ <GridLoadableCell isLoading={checkUpdating(colDef.field ?? colDef.colId ?? "", props.data.id)}>
42
47
  <>
43
48
  {typeof warningText === "string" && <GridIcon icon={"ic_warning"} title={warningText} />}
44
49
  {typeof infoText === "string" && <GridIcon icon={"ic_info"} title={infoText} />}
@@ -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 disabled = !props.colDef?.editable;
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
- stopEditing();
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,28 +1,33 @@
1
- // @ts-nocheck
2
- /* eslint-disable */
3
- import { forwardRef, useRef, useMemo, useCallback, useEffect } from "react";
1
+ import {
2
+ forwardRef,
3
+ useRef,
4
+ useMemo,
5
+ useCallback,
6
+ useEffect,
7
+ KeyboardEvent,
8
+ FocusEvent,
9
+ MutableRefObject,
10
+ ForwardedRef,
11
+ } from "react";
4
12
  import { createPortal } from "react-dom";
5
- import { string, number, bool, func, object, oneOf, oneOfType, exact } from "prop-types";
6
13
  import { MenuList } from "./MenuList";
7
14
  import { useBEM } from "../hooks";
8
15
  import {
9
- rootMenuPropTypes,
10
16
  menuContainerClass,
11
17
  mergeProps,
12
18
  safeCall,
13
19
  isMenuOpen,
14
20
  getTransition,
15
- values,
16
21
  CloseReason,
17
22
  Keys,
18
- MenuStateMap,
19
23
  EventHandlersContext,
20
24
  SettingsContext,
21
25
  ItemSettingsContext,
22
26
  } from "../utils";
23
27
  import { hasParentClass } from "@utils/util";
28
+ import { ControlledMenuProps, PortalFieldType, RadioChangeEvent } from "../types";
24
29
 
25
- export const ControlledMenu = forwardRef(function ControlledMenu(
30
+ export const ControlledMenuFr = (
26
31
  {
27
32
  "aria-label": ariaLabel,
28
33
  className,
@@ -44,11 +49,11 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
44
49
  onClose,
45
50
  saveButtonRef,
46
51
  ...restProps
47
- }: any,
48
- externalRef,
49
- ) {
50
- const containerRef = useRef(null);
51
- const scrollNodesRef = useRef<{anchors?:HTMLDivElement[]}>({});
52
+ }: ControlledMenuProps & { saveButtonRef?: MutableRefObject<HTMLButtonElement | null> },
53
+ externalRef: ForwardedRef<HTMLUListElement>,
54
+ ) => {
55
+ const containerRef = useRef<HTMLElement>();
56
+ const scrollNodesRef = useRef<{ anchors?: HTMLDivElement[] }>({});
52
57
  const { anchorRef, state } = restProps;
53
58
 
54
59
  const settings = useMemo(
@@ -92,7 +97,7 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
92
97
  // the cell doesn't refresh during update if save is invoked from a native event
93
98
  // This doesn't happen in React18
94
99
  // To work around it, I invoke the save by clicking on a passed in invisible button ref
95
- if (saveButtonRef.current) saveButtonRef.current.click();
100
+ if (saveButtonRef?.current) saveButtonRef.current.click();
96
101
  else safeCall(onClose, { reason: CloseReason.BLUR });
97
102
 
98
103
  // If a user clicks on the menu button when a menu is open, we need to close the menu.
@@ -107,7 +112,7 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
107
112
  }
108
113
  }
109
114
  },
110
- [clickIsWithinMenu, onClose],
115
+ [clickIsWithinMenu, onClose, saveButtonRef, skipOpen],
111
116
  );
112
117
 
113
118
  const handleScreenEventForCancel = useCallback(
@@ -122,7 +127,7 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
122
127
 
123
128
  useEffect(() => {
124
129
  if (isMenuOpen(state)) {
125
- const thisDocument = anchorRef.current ? anchorRef.current.ownerDocument : document;
130
+ const thisDocument = anchorRef?.current ? anchorRef?.current.ownerDocument : document;
126
131
  thisDocument.addEventListener("mousedown", handleScreenEventForCancel, true);
127
132
  thisDocument.addEventListener("mouseup", handleScreenEventForSave, true);
128
133
  thisDocument.addEventListener("click", handleScreenEventForCancel, true);
@@ -135,7 +140,7 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
135
140
  };
136
141
  }
137
142
  return () => {};
138
- }, [handleScreenEventForSave, handleScreenEventForCancel, state]);
143
+ }, [handleScreenEventForSave, handleScreenEventForCancel, state, anchorRef]);
139
144
 
140
145
  const itemSettings = useMemo(
141
146
  () => ({
@@ -147,7 +152,7 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
147
152
 
148
153
  const eventHandlers = useMemo(
149
154
  () => ({
150
- handleClick(event, isCheckorRadio) {
155
+ handleClick(event: RadioChangeEvent, isCheckOrRadio: boolean) {
151
156
  if (!event.stopPropagation) safeCall(onItemClick, event);
152
157
 
153
158
  let keepOpen = event.keepOpen;
@@ -155,7 +160,7 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
155
160
  // if event.keepOpen is undefined, the following default behaviour is used
156
161
  // According to WAI-ARIA Authoring Practices 1.1
157
162
  // Keep menu open when check or radio is invoked by SPACE key
158
- keepOpen = isCheckorRadio && event.key === Keys.SPACE;
163
+ keepOpen = isCheckOrRadio && event.key === Keys.SPACE;
159
164
  }
160
165
 
161
166
  if (!keepOpen) {
@@ -167,14 +172,14 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
167
172
  }
168
173
  },
169
174
 
170
- handleClose(key) {
175
+ handleClose(key?: string) {
171
176
  safeCall(onClose, { key, reason: CloseReason.CLICK });
172
177
  },
173
178
  }),
174
179
  [onItemClick, onClose],
175
180
  );
176
181
 
177
- const onKeyDown = ({ key }) => {
182
+ const onKeyDown = ({ key }: KeyboardEvent) => {
178
183
  switch (key) {
179
184
  case Keys.ESC:
180
185
  safeCall(onClose, { key, reason: CloseReason.CANCEL });
@@ -182,14 +187,14 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
182
187
  }
183
188
  };
184
189
 
185
- const onBlur = (e) => {
190
+ const onBlur = (e: FocusEvent) => {
186
191
  if (isMenuOpen(state) && !e.currentTarget.contains(e.relatedTarget || document.activeElement)) {
187
192
  safeCall(onClose, { reason: CloseReason.BLUR });
188
193
 
189
194
  // If a user clicks on the menu button when a menu is open, we need to close the menu.
190
195
  // However, a blur event will be fired prior to the click event on menu button,
191
196
  // which makes the menu first close and then open again.
192
- // If this happen, e.relatedTarget is incorrectly set to null instead of the button in Safari and Firefox,
197
+ // If this happens, e.relatedTarget is incorrectly set to null instead of the button in Safari and Firefox,
193
198
  // and makes it difficult to determine whether onBlur is fired because of clicking on menu button.
194
199
  // This is a workaround approach which sets a flag to skip a following click event.
195
200
  if (skipOpen) {
@@ -231,31 +236,20 @@ export const ControlledMenu = forwardRef(function ControlledMenu(
231
236
  </div>
232
237
  );
233
238
 
234
- if (portal === true && anchorRef.current !== undefined) {
235
- portal = { target: anchorRef.current.ownerDocument.body };
239
+ if (portal === true && anchorRef?.current !== undefined) {
240
+ portal = { target: anchorRef.current.ownerDocument.body } as PortalFieldType;
236
241
  }
237
242
 
238
- if (portal === true && typeof document !== "undefined") {
239
- return createPortal(menuList, document.body);
240
- } else if (portal) {
241
- return portal.target ? createPortal(menuList, portal.target) : portal.stablePosition ? null : menuList;
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
+ }
242
251
  }
243
252
  return menuList;
244
- });
245
-
246
- ControlledMenu.propTypes /* remove-proptypes */ = {
247
- ...rootMenuPropTypes,
248
- state: oneOf(values(MenuStateMap)),
249
- anchorPoint: exact({
250
- x: number,
251
- y: number,
252
- }),
253
- anchorRef: object,
254
- skipOpen: object,
255
- captureFocus: bool,
256
- menuItemFocus: exact({
257
- position: oneOfType([string, number]),
258
- alwaysUpdate: bool,
259
- }),
260
- onClose: func,
261
253
  };
254
+
255
+ export const ControlledMenu = forwardRef(ControlledMenuFr);
@@ -9,7 +9,7 @@ import {
9
9
  withHovering,
10
10
  EventHandlersContext,
11
11
  } from "../utils";
12
- import { BaseProps } from "../index";
12
+ import { BaseProps } from "../types";
13
13
  import { withHoveringResultProps } from "../utils/withHovering";
14
14
 
15
15
  export interface FocusableItemProps extends BaseProps, withHoveringResultProps {
@@ -72,4 +72,4 @@ export const FocusableItemFr = ({
72
72
  );
73
73
  };
74
74
 
75
- export const FocusableItem = withHovering("FocusableItem", FocusableItemFr) as any as typeof FocusableItemFr;
75
+ export const FocusableItem = withHovering("FocusableItem", FocusableItemFr);
@@ -11,7 +11,7 @@ import {
11
11
  import { ControlledMenu } from "./ControlledMenu";
12
12
  import { useMenuChange, useMenuStateAndFocus, useCombinedRef } from "../hooks";
13
13
  import { getName, mergeProps, safeCall, isMenuOpen, Keys, FocusPositions } from "../utils";
14
- import { MenuButtonModifiers, RenderProp, RootMenuProps, UncontrolledMenuProps } from "../index";
14
+ import { MenuButtonModifiers, RenderProp, RootMenuProps, UncontrolledMenuProps } from "../types";
15
15
 
16
16
  //
17
17
  // Menu
@@ -1,7 +1,7 @@
1
1
  import { forwardRef, LegacyRef, ReactNode, useMemo } from "react";
2
2
  import { useBEM } from "../hooks";
3
3
  import { menuButtonClass } from "../utils";
4
- import { BaseProps, MenuButtonModifiers } from "../index";
4
+ import { BaseProps, MenuButtonModifiers } from "../types";
5
5
 
6
6
  export interface MenuButtonProps extends BaseProps<MenuButtonModifiers> {
7
7
  disabled?: boolean;
@@ -1,7 +1,7 @@
1
1
  import { memo, forwardRef, LegacyRef } from "react";
2
2
  import { useBEM } from "../hooks";
3
3
  import { menuClass, menuDividerClass } from "../utils";
4
- import { BaseProps } from "../index";
4
+ import { BaseProps } from "../types";
5
5
 
6
6
  const MenuDividerFr = ({ className, ...restProps }: BaseProps, externalRef: LegacyRef<HTMLLIElement>) => {
7
7
  return (
@@ -14,4 +14,4 @@ const MenuDividerFr = ({ className, ...restProps }: BaseProps, externalRef: Lega
14
14
  );
15
15
  };
16
16
 
17
- export const MenuDivider = memo(forwardRef(MenuDividerFr)) as any as typeof MenuDividerFr;
17
+ export const MenuDivider = memo(forwardRef(MenuDividerFr));
@@ -1,7 +1,7 @@
1
1
  import { ForwardedRef, forwardRef, ReactNode, useContext, useRef, useState } from "react";
2
2
  import { useBEM, useLayoutEffect, useCombinedRef } from "../hooks";
3
3
  import { menuClass, menuGroupClass, MenuListContext } from "../utils";
4
- import { BaseProps, MenuOverflow } from "../index";
4
+ import { BaseProps, MenuOverflow } from "../types";
5
5
 
6
6
  export interface MenuGroupProps extends BaseProps {
7
7
  children?: ReactNode;
@@ -1,7 +1,7 @@
1
1
  import { memo, forwardRef, ForwardedRef } from "react";
2
2
  import { useBEM } from "../hooks";
3
3
  import { menuClass, menuHeaderClass } from "../utils";
4
- import { BaseProps } from "../index";
4
+ import { BaseProps } from "../types";
5
5
 
6
6
  export const MenuHeaderFr = ({ className, ...restProps }: BaseProps, externalRef: ForwardedRef<HTMLLIElement>) => {
7
7
  return (
@@ -12,7 +12,7 @@ import {
12
12
  Keys,
13
13
  RMEvent,
14
14
  } from "../utils";
15
- import { BaseProps, ClickEvent, EventHandler, Hoverable, MenuItemTypeProp, RenderProp } from "../index";
15
+ import { BaseProps, ClickEvent, EventHandler, Hoverable, MenuItemTypeProp, RenderProp } from "../types";
16
16
  import { withHoveringResultProps } from "../utils/withHovering";
17
17
 
18
18
  //
@@ -179,5 +179,4 @@ const MenuItemFr = ({
179
179
  }
180
180
  };
181
181
 
182
- // FIXME matt as any
183
- export const MenuItem = withHovering("MenuItem", MenuItemFr) as any as typeof MenuItemFr;
182
+ export const MenuItem = withHovering("MenuItem", MenuItemFr);