@linzjs/step-ag-grid 30.4.4 → 31.2.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.
Files changed (81) hide show
  1. package/dist/GridTheme.scss +12 -7
  2. package/dist/index.css +6 -3
  3. package/dist/src/components/gridDragUtil.d.ts +10 -0
  4. package/dist/src/components/gridDragUtil.test.d.ts +1 -0
  5. package/dist/src/components/index.d.ts +1 -0
  6. package/dist/src/components/types.d.ts +4 -0
  7. package/dist/src/utils/__tests__/testQuick.ts +7 -7
  8. package/dist/src/utils/__tests__/testUtil.ts +3 -1
  9. package/dist/src/utils/__tests__/vitestUtil.ts +3 -1
  10. package/dist/src/utils/storybook.d.ts +4 -0
  11. package/dist/src/utils/useInterval.d.ts +17 -0
  12. package/dist/src/utils/useIsomorphicLayoutEffect.d.ts +17 -0
  13. package/dist/step-ag-grid.cjs +402 -218
  14. package/dist/step-ag-grid.cjs.map +1 -1
  15. package/dist/step-ag-grid.esm.js +402 -219
  16. package/dist/step-ag-grid.esm.js.map +1 -1
  17. package/package.json +26 -52
  18. package/src/components/Grid.tsx +75 -8
  19. package/src/components/PostSortRowsHook.ts +3 -1
  20. package/src/components/gridDragUtil.test.ts +115 -0
  21. package/src/components/gridDragUtil.ts +32 -0
  22. package/src/components/gridFilter/GridFilterColumnsMultiSelect.scss +18 -20
  23. package/src/components/gridFilter/GridFilterColumnsToggle.tsx +3 -4
  24. package/src/components/gridFilter/useGridFilter.ts +2 -2
  25. package/src/components/gridForm/GridFormDropDown.tsx +1 -1
  26. package/src/components/gridForm/GridFormInlineTextInput.tsx +1 -2
  27. package/src/components/gridForm/GridFormMultiSelect.tsx +10 -4
  28. package/src/components/gridForm/GridFormSubComponentTextArea.tsx +1 -2
  29. package/src/components/gridForm/GridFormSubComponentTextInput.tsx +1 -2
  30. package/src/components/gridForm/GridFormTextArea.tsx +1 -2
  31. package/src/components/gridForm/GridFormTextInput.tsx +1 -2
  32. package/src/components/gridHeader/GridHeaderSelect.tsx +3 -1
  33. package/src/components/gridHook/useGridCopy.ts +13 -3
  34. package/src/components/gridHook/useGridRangeSelection.ts +29 -16
  35. package/src/components/index.ts +1 -0
  36. package/src/components/types.ts +4 -0
  37. package/src/contexts/GridContextProvider.tsx +18 -7
  38. package/src/lui/ActionButton.scss +1 -1
  39. package/src/lui/ActionButton.tsx +5 -1
  40. package/src/lui/FormError.scss +1 -1
  41. package/src/lui/TextAreaInput.tsx +1 -1
  42. package/src/lui/TextInputFormatted.scss +13 -13
  43. package/src/lui/TextInputFormatted.tsx +1 -1
  44. package/src/react-menu3/README.md +66 -67
  45. package/src/react-menu3/components/ControlledMenu.tsx +2 -2
  46. package/src/react-menu3/components/Menu.tsx +1 -1
  47. package/src/react-menu3/components/MenuItem.tsx +1 -1
  48. package/src/react-menu3/components/MenuList.tsx +8 -2
  49. package/src/react-menu3/components/SubMenu.tsx +6 -6
  50. package/src/react-menu3/hooks/useBEM.ts +2 -1
  51. package/src/react-menu3/hooks/useItemState.ts +8 -4
  52. package/src/react-menu3/styles/_var.scss +1 -1
  53. package/src/react-menu3/styles/core.scss +6 -6
  54. package/src/react-menu3/styles/index.scss +11 -8
  55. package/src/react-menu3/styles/theme-dark.scss +1 -1
  56. package/src/react-menu3/styles/transitions/slide.scss +8 -8
  57. package/src/react-menu3/utils/utils.ts +2 -1
  58. package/src/stories/grid/FormTest.scss +13 -13
  59. package/src/stories/grid/GridDragRow.stories.tsx +61 -8
  60. package/src/stories/grid/GridInlineText.stories.tsx +2 -2
  61. package/src/stories/grid/GridSorting.stories.tsx +6 -2
  62. package/src/styles/ComponentLoadingWrapper.scss +1 -1
  63. package/src/styles/ContextMenu.scss +1 -1
  64. package/src/styles/Grid.scss +11 -9
  65. package/src/styles/GridCell.scss +1 -1
  66. package/src/styles/GridFilterColumnsToggle.scss +1 -1
  67. package/src/styles/GridFormInlineTextInput.scss +7 -7
  68. package/src/styles/GridFormMultiSelectGrid.scss +1 -1
  69. package/src/styles/GridFormSubComponentTextInput.scss +4 -4
  70. package/src/styles/GridIcon.scss +1 -1
  71. package/src/styles/GridPopoverMenu.scss +1 -1
  72. package/src/styles/GridTheme.scss +12 -7
  73. package/src/styles/index.scss +16 -16
  74. package/src/styles/react-menu-customisations.scss +119 -120
  75. package/src/utils/__tests__/testQuick.ts +7 -7
  76. package/src/utils/__tests__/testUtil.ts +3 -1
  77. package/src/utils/__tests__/vitestUtil.ts +3 -1
  78. package/src/utils/deferredPromise.ts +1 -1
  79. package/src/utils/storybook.ts +5 -0
  80. package/src/utils/useInterval.ts +44 -0
  81. package/src/utils/useIsomorphicLayoutEffect.ts +18 -0
@@ -219,7 +219,17 @@ export const useGridCopy = <TData extends GridBaseRow>({
219
219
  void navigator.clipboard.writeText(result).catch((err) => console.error('Failed to copy: ', err));
220
220
  }
221
221
  },
222
- [getCellValue, ranges, copyType],
222
+ [
223
+ getCellValue,
224
+ ranges,
225
+ copyType,
226
+ rangeStartRef,
227
+ hasSelectedMoreThanOneCellRef,
228
+ rangeEndRef,
229
+ showToast,
230
+ getColDef,
231
+ getSelectedRowIds,
232
+ ],
223
233
  );
224
234
 
225
235
  const onCopyEvent = useCallback(
@@ -240,7 +250,7 @@ export const useGridCopy = <TData extends GridBaseRow>({
240
250
 
241
251
  onCopy();
242
252
  },
243
- [onCopy],
253
+ [onCopy, rangeStartRef, rangeEndRef, hasSelectedMoreThanOneCellRef],
244
254
  );
245
255
 
246
256
  const { cellContextMenu: rangeSelectContextMenu, contextMenuComponent: rangeSelectContextMenuComponent } =
@@ -255,7 +265,7 @@ export const useGridCopy = <TData extends GridBaseRow>({
255
265
  ? rangeSelectContextMenu(event)
256
266
  : cellContextMenu(event);
257
267
  },
258
- [cellContextMenu, rangeSelectContextMenu],
268
+ [cellContextMenu, rangeSelectContextMenu, rangeStartRef, hasSelectedMoreThanOneCellRef],
259
269
  );
260
270
 
261
271
  return {
@@ -1,7 +1,7 @@
1
1
  import { CellMouseDownEvent, CellMouseOverEvent, IRowNode } from 'ag-grid-community';
2
2
  import { MutableRefObject, RefObject, useCallback, useEffect, useRef, useState } from 'react';
3
- import { useInterval } from 'usehooks-ts';
4
3
 
4
+ import { useInterval } from '../../utils/useInterval';
5
5
  import { GridBaseRow } from '../types';
6
6
 
7
7
  export interface CellLocation {
@@ -79,7 +79,7 @@ export const useGridRangeSelection = <TData extends GridBaseRow>({
79
79
  );
80
80
 
81
81
  return { selectedColIds, selectedNodes };
82
- }, []);
82
+ }, [gridDivRef, rangeSortedNodesRef, rangeStartRef, rangeEndRef]);
83
83
 
84
84
  const redrawSelectedRanges = useCallback(() => {
85
85
  const gridElement = gridDivRef.current!;
@@ -106,7 +106,7 @@ export const useGridRangeSelection = <TData extends GridBaseRow>({
106
106
  }
107
107
  });
108
108
  });
109
- }, []);
109
+ }, [ranges, gridDivRef]);
110
110
 
111
111
  const updateRangeSelectionCellClasses = useCallback(
112
112
  (justRefresh?: boolean) => {
@@ -158,7 +158,7 @@ export const useGridRangeSelection = <TData extends GridBaseRow>({
158
158
 
159
159
  redrawSelectedRanges();
160
160
  },
161
- [ranges, redrawSelectedRanges],
161
+ [redrawSelectedRanges, rangeStartRef, rangeEndRef, gridDivRef, hasSelectedMoreThanOneCellRef, rangeSortedNodesRef],
162
162
  );
163
163
 
164
164
  // Handle updates after scroll / grid refresh
@@ -178,18 +178,24 @@ export const useGridRangeSelection = <TData extends GridBaseRow>({
178
178
  rangeEndRef.current = null;
179
179
 
180
180
  updateRangeSelectionCellClasses();
181
- }, [updateRangeSelectionCellClasses]);
181
+ }, [updateRangeSelectionCellClasses, hasSelectedMoreThanOneCellRef, rangeStartRef, rangeEndRef]);
182
182
 
183
183
  const mouseDownRef = useRef<[number, number]>([0, 0]);
184
- const trackMouseDown = useCallback((e: MouseEvent) => {
185
- mouseDownRef.current = [e.screenX, e.screenY];
186
- }, []);
184
+ const trackMouseDown = useCallback(
185
+ (e: MouseEvent) => {
186
+ mouseDownRef.current = [e.screenX, e.screenY];
187
+ },
188
+ [mouseDownRef],
189
+ );
187
190
 
188
- const trackMouseUp = useCallback((e: MouseEvent) => {
189
- if (rangeEndRef.current) {
190
- rangeEndRef.current.clickLocation = [e.screenX, e.screenY];
191
- }
192
- }, []);
191
+ const trackMouseUp = useCallback(
192
+ (e: MouseEvent) => {
193
+ if (rangeEndRef.current) {
194
+ rangeEndRef.current.clickLocation = [e.screenX, e.screenY];
195
+ }
196
+ },
197
+ [rangeEndRef],
198
+ );
193
199
 
194
200
  useEffect(() => {
195
201
  document.addEventListener('mousedown', trackMouseDown, { capture: true });
@@ -198,7 +204,7 @@ export const useGridRangeSelection = <TData extends GridBaseRow>({
198
204
  document.removeEventListener('mousemove', trackMouseDown, { capture: true });
199
205
  document.removeEventListener('mouseup', trackMouseUp, { capture: true });
200
206
  };
201
- }, []);
207
+ }, [trackMouseDown, trackMouseUp]);
202
208
 
203
209
  const onCellMouseOver = useCallback(
204
210
  (e: CellMouseOverEvent, mouseDown?: boolean) => {
@@ -238,7 +244,14 @@ export const useGridRangeSelection = <TData extends GridBaseRow>({
238
244
 
239
245
  updateRangeSelectionCellClasses();
240
246
  },
241
- [enableRangeSelection, updateRangeSelectionCellClasses],
247
+ [
248
+ enableRangeSelection,
249
+ updateRangeSelectionCellClasses,
250
+ hasSelectedMoreThanOneCellRef,
251
+ rangeEndRef,
252
+ rangeSortedNodesRef,
253
+ rangeStartRef,
254
+ ],
242
255
  );
243
256
 
244
257
  const onCellMouseDown = useCallback(
@@ -249,7 +262,7 @@ export const useGridRangeSelection = <TData extends GridBaseRow>({
249
262
  }
250
263
  onCellMouseOver(e as unknown as CellMouseOverEvent, true);
251
264
  },
252
- [onCellMouseOver],
265
+ [onCellMouseOver, clearRangeSelection],
253
266
  );
254
267
 
255
268
  return { clearRangeSelection, ranges, onCellMouseDown, onCellMouseOver };
@@ -4,6 +4,7 @@ export * from './GridCell';
4
4
  export * from './GridCellFiller';
5
5
  export * from './GridCellMultiEditor';
6
6
  export * from './GridCellMultiSelectClassRules';
7
+ export * from './gridDragUtil';
7
8
  export * from './gridFilter';
8
9
  export * from './gridForm';
9
10
  export * from './gridHook';
@@ -8,8 +8,12 @@ export interface GridBaseRow {
8
8
  }
9
9
 
10
10
  export interface GridOnRowDragEndProps<TData extends GridBaseRow> {
11
+ /** The row the user physically grabbed by its drag handle. */
11
12
  movedRow: TData;
13
+ /** All rows being moved, ordered by display index. Single-element array when dragging an unselected row. */
14
+ movedRows: TData[];
12
15
  targetRow: TData;
16
+ /** -1 = dropped above target, 1 = dropped below target. */
13
17
  direction: -1 | 1;
14
18
  }
15
19
 
@@ -122,7 +122,7 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
122
122
  // this is the only way to get out of the loop
123
123
  throw 'expected exception - exit_loop';
124
124
  });
125
- } catch (ex) {
125
+ } catch {
126
126
  // ignore
127
127
  }
128
128
  return id;
@@ -312,7 +312,7 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
312
312
  const colId = col.colId;
313
313
  // We need to make sure we aren't currently editing a cell otherwise tests will fail
314
314
  // as they will start to edit the cell before this stuff has a chance to run
315
- colId &&
315
+ if (colId) {
316
316
  delay(() => {
317
317
  if (
318
318
  !gridApi.isDestroyed() &&
@@ -326,6 +326,7 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
326
326
  gridApi.setFocusedCell(rowIndex, agGridSelectRowColId);
327
327
  }
328
328
  }, 100);
329
+ }
329
330
  }
330
331
  }
331
332
  }
@@ -342,7 +343,9 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
342
343
  if (flash) {
343
344
  delay(() => {
344
345
  try {
345
- !gridApi.isDestroyed() && gridApi.flashCells({ rowNodes });
346
+ if (!gridApi.isDestroyed()) {
347
+ gridApi.flashCells({ rowNodes });
348
+ }
346
349
  } catch {
347
350
  // ignore, flash cells sometimes throws errors as nodes have gone out of scope
348
351
  }
@@ -749,7 +752,9 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
749
752
  // }
750
753
 
751
754
  // This is needed to trigger postSortRowsHook
752
- !gridApi.isDestroyed && gridApi.refreshClientSideRowModel();
755
+ if (!gridApi.isDestroyed) {
756
+ gridApi.refreshClientSideRowModel();
757
+ }
753
758
  }
754
759
 
755
760
  void (async () => {
@@ -782,7 +787,9 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
782
787
  () =>
783
788
  debounce((rowNodes?: IRowNode[]) => {
784
789
  try {
785
- gridApi && gridApi.redrawRows(rowNodes ? { rowNodes } : undefined);
790
+ if (gridApi) {
791
+ gridApi.redrawRows(rowNodes ? { rowNodes } : undefined);
792
+ }
786
793
  } catch (ex) {
787
794
  console.error(ex);
788
795
  }
@@ -812,7 +819,9 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
812
819
  const postHash = hasFocusedCell && getDisplayedRowsHash();
813
820
  // Ag-grid has a bug where if a focused cell comes into view after a filter the filter loses focus
814
821
  // So the focus is cleared to prevent this
815
- preHash !== postHash && gridApi.clearFocusedCell();
822
+ if (preHash !== postHash) {
823
+ gridApi.clearFocusedCell();
824
+ }
816
825
  }
817
826
  }, 200),
818
827
  [gridApi],
@@ -864,7 +873,9 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
864
873
  const visibleColumnsContainsAFlex = newVisibleColumns.some(isFlexColumn);
865
874
  if (!visibleColumnsContainsAFlex) {
866
875
  const fillerColumn = getColumns(isGridCellFiller)[0];
867
- fillerColumn && newVisibleColumns.push(fillerColumn);
876
+ if (fillerColumn) {
877
+ newVisibleColumns.push(fillerColumn);
878
+ }
868
879
  }
869
880
  gridApi.setColumnsVisible(compact(newVisibleColumns.map(getColId)), true);
870
881
 
@@ -1,4 +1,4 @@
1
- @use "../../node_modules/@linzjs/lui/dist/scss/Core" as lui;
1
+ @use '../../node_modules/@linzjs/lui/dist/scss/Core' as lui;
2
2
 
3
3
  .ActionButton {
4
4
  display: flex;
@@ -50,7 +50,11 @@ export const ActionButton = ({
50
50
 
51
51
  useEffect(() => {
52
52
  if (inProgress == lastInProgress) return;
53
- inProgress ? setLocalInProgress(true) : setLocalInProgressDeferred(false, minimumInProgressTimeMs);
53
+ if (inProgress) {
54
+ setLocalInProgress(true);
55
+ } else {
56
+ setLocalInProgressDeferred(false, minimumInProgressTimeMs);
57
+ }
54
58
  }, [inProgress, lastInProgress, setLocalInProgress, setLocalInProgressDeferred]);
55
59
 
56
60
  const buttonText = (
@@ -1,4 +1,4 @@
1
- @use "../../node_modules/@linzjs/lui/dist/scss/Core" as lui;
1
+ @use '../../node_modules/@linzjs/lui/dist/scss/Core' as lui;
2
2
 
3
3
  .FormError {
4
4
  display: flex;
@@ -45,7 +45,7 @@ export const TextAreaInput = (props: LuiTextAreaInputProps) => {
45
45
  e.currentTarget.focus();
46
46
  e.currentTarget.selectionStart = e.currentTarget.value.length;
47
47
  }
48
- props.onMouseEnter && props.onMouseEnter(e);
48
+ props.onMouseEnter?.(e);
49
49
  }}
50
50
  data-allowtabtosave={props.allowTabToSave}
51
51
  >
@@ -1,13 +1,13 @@
1
- @use "../../node_modules/@linzjs/lui/dist/scss/Core" as lui;
2
-
3
- .LuiTextInput.GridLuiTextInput {
4
- margin-bottom: 0;
5
- }
6
-
7
- .LuiTextInput-formatted {
8
- position: absolute;
9
- right: 14px;
10
- top: 0;
11
- line-height: 48px;
12
- color: lui.$fuscous;
13
- }
1
+ @use '../../node_modules/@linzjs/lui/dist/scss/Core' as lui;
2
+
3
+ .LuiTextInput.GridLuiTextInput {
4
+ margin-bottom: 0;
5
+ }
6
+
7
+ .LuiTextInput-formatted {
8
+ position: absolute;
9
+ right: 14px;
10
+ top: 0;
11
+ line-height: 48px;
12
+ color: lui.$fuscous;
13
+ }
@@ -36,7 +36,7 @@ export const TextInputFormatted = (props: LuiTextInputProps): ReactElement => {
36
36
  className={'LuiTextInput-input'}
37
37
  onMouseEnter={(e) => {
38
38
  e.currentTarget.focus();
39
- props.onMouseEnter && props.onMouseEnter(e);
39
+ props.onMouseEnter?.(e);
40
40
  }}
41
41
  data-allowtabtosave={props.allowTabToSave}
42
42
  />
@@ -1,67 +1,66 @@
1
- # React-Menu
2
-
3
- > This is a fork of @szhsin/react-menu. v3.2.0
4
-
5
- > An accessible and keyboard-friendly React menu library.
6
-
7
- **[Live examples and docs](https://szhsin.github.io/react-menu/)**
8
-
9
- ## Features
10
-
11
- - React menu components for easy and fast web development.
12
- - Unlimited levels of submenu.
13
- - Supports dropdown or context menu.
14
- - Supports radio and checkbox menu items.
15
- - Flexible menu positioning.
16
- - Comprehensive keyboard interactions.
17
- - Unstyled components and easy [customisation](https://szhsin.github.io/react-menu/#styling).
18
- - [Optimal level support](https://github.com/reactwg/react-18/discussions/70) (level 3) of **React 18** concurrent rendering.
19
- - Works in major browsers without polyfills.
20
- - [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/patterns/menu/) compliant.
21
-
22
- ## Install
23
-
24
- with npm
25
-
26
- ```bash
27
- npm install @szhsin/react-menu
28
- ```
29
-
30
- or with Yarn
31
-
32
- ```bash
33
- yarn add @szhsin/react-menu
34
- ```
35
-
36
- ## Usage
37
-
38
- ```jsx
39
- import { Menu, MenuItem, MenuButton, SubMenu } from "@szhsin/react-menu";
40
-
41
- export default function App() {
42
- return (
43
- <Menu menuButton={<MenuButton>Open menu</MenuButton>}>
44
- <MenuItem>New File</MenuItem>
45
- <MenuItem>Save</MenuItem>
46
- <SubMenu label="Edit">
47
- <MenuItem>Cut</MenuItem>
48
- <MenuItem>Copy</MenuItem>
49
- <MenuItem>Paste</MenuItem>
50
- </SubMenu>
51
- <MenuItem>Print...</MenuItem>
52
- </Menu>
53
- );
54
- }
55
- ```
56
-
57
- **[Edit on CodeSandbox](https://codesandbox.io/s/react-menu-starter-3ez3c)**<br>
58
- **[Visit more examples and docs](https://szhsin.github.io/react-menu/)**<br><br>
59
- [Migration from v2 to v3](https://github.com/szhsin/react-menu/wiki/Migration-from-v2-to-v3)<br>
60
- [Migration from v1 to v2](https://github.com/szhsin/react-menu/wiki/Migration-from-v1-to-v2)
61
-
62
- _Please note that React-Menu v3 requires React 16.14.0 or higher. If you are not able to upgrade React, you could install v2/v1 which requires React 16.8.0._
63
-
64
- ## License
65
-
66
- [MIT](https://github.com/szhsin/react-menu/blob/master/LICENSE) Licensed.
67
-
1
+ # React-Menu
2
+
3
+ > This is a fork of @szhsin/react-menu. v3.2.0
4
+
5
+ > An accessible and keyboard-friendly React menu library.
6
+
7
+ **[Live examples and docs](https://szhsin.github.io/react-menu/)**
8
+
9
+ ## Features
10
+
11
+ - React menu components for easy and fast web development.
12
+ - Unlimited levels of submenu.
13
+ - Supports dropdown or context menu.
14
+ - Supports radio and checkbox menu items.
15
+ - Flexible menu positioning.
16
+ - Comprehensive keyboard interactions.
17
+ - Unstyled components and easy [customisation](https://szhsin.github.io/react-menu/#styling).
18
+ - [Optimal level support](https://github.com/reactwg/react-18/discussions/70) (level 3) of **React 18** concurrent rendering.
19
+ - Works in major browsers without polyfills.
20
+ - [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/patterns/menu/) compliant.
21
+
22
+ ## Install
23
+
24
+ with npm
25
+
26
+ ```bash
27
+ npm install @szhsin/react-menu
28
+ ```
29
+
30
+ or with Yarn
31
+
32
+ ```bash
33
+ yarn add @szhsin/react-menu
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ```jsx
39
+ import { Menu, MenuItem, MenuButton, SubMenu } from '@szhsin/react-menu';
40
+
41
+ export default function App() {
42
+ return (
43
+ <Menu menuButton={<MenuButton>Open menu</MenuButton>}>
44
+ <MenuItem>New File</MenuItem>
45
+ <MenuItem>Save</MenuItem>
46
+ <SubMenu label="Edit">
47
+ <MenuItem>Cut</MenuItem>
48
+ <MenuItem>Copy</MenuItem>
49
+ <MenuItem>Paste</MenuItem>
50
+ </SubMenu>
51
+ <MenuItem>Print...</MenuItem>
52
+ </Menu>
53
+ );
54
+ }
55
+ ```
56
+
57
+ **[Edit on CodeSandbox](https://codesandbox.io/s/react-menu-starter-3ez3c)**<br>
58
+ **[Visit more examples and docs](https://szhsin.github.io/react-menu/)**<br><br>
59
+ [Migration from v2 to v3](https://github.com/szhsin/react-menu/wiki/Migration-from-v2-to-v3)<br>
60
+ [Migration from v1 to v2](https://github.com/szhsin/react-menu/wiki/Migration-from-v1-to-v2)
61
+
62
+ _Please note that React-Menu v3 requires React 16.14.0 or higher. If you are not able to upgrade React, you could install v2/v1 which requires React 16.8.0._
63
+
64
+ ## License
65
+
66
+ [MIT](https://github.com/szhsin/react-menu/blob/master/LICENSE) Licensed.
@@ -173,7 +173,7 @@ export const ControlledMenuFr = (
173
173
  if (isDown) {
174
174
  lastTabDownEl.current = activeElement;
175
175
  } else {
176
- lastTabDownEl.current == activeElement && invokeSave(tabDirection);
176
+ if (lastTabDownEl.current == activeElement) invokeSave(tabDirection);
177
177
  }
178
178
  }
179
179
  }
@@ -191,7 +191,7 @@ export const ControlledMenuFr = (
191
191
  if (isDown) {
192
192
  lastEnterDownEl.current = activeElement;
193
193
  } else {
194
- lastEnterDownEl.current == activeElement && invokeSave(CloseReason.CLICK);
194
+ if (lastEnterDownEl.current == activeElement) invokeSave(CloseReason.CLICK);
195
195
  }
196
196
  }
197
197
  }
@@ -42,7 +42,7 @@ export function MenuFr(
42
42
  const handleClose = useCallback(
43
43
  (e: MenuCloseEvent) => {
44
44
  toggleMenu(false);
45
- if (e.key) buttonRef.current && buttonRef.current.focus();
45
+ if (e.key) buttonRef?.current?.focus();
46
46
  },
47
47
  [toggleMenu],
48
48
  );
@@ -141,7 +141,7 @@ const MenuItemFr = ({
141
141
  e.preventDefault();
142
142
  e.stopPropagation();
143
143
  if (isAnchor) {
144
- menuItemRef?.current && menuItemRef.current.click();
144
+ menuItemRef?.current.click();
145
145
  } else {
146
146
  handleClick(e);
147
147
  }
@@ -175,7 +175,9 @@ export const MenuList = ({
175
175
  setOverflowData(undefined); // reset overflowData after closing
176
176
  }
177
177
 
178
- endTransition && safeCall(endTransition);
178
+ if (endTransition) {
179
+ safeCall(endTransition);
180
+ }
179
181
  };
180
182
 
181
183
  const handlePosition = useCallback(
@@ -455,7 +457,11 @@ export const MenuList = ({
455
457
  let maxHeight: number | undefined;
456
458
  let overflowAmt: number | undefined;
457
459
  if (overflowData) {
458
- setDownOverflow ? (overflowAmt = overflowData.overflowAmt) : (maxHeight = overflowData.height);
460
+ if (setDownOverflow) {
461
+ overflowAmt = overflowData.overflowAmt;
462
+ } else {
463
+ maxHeight = overflowData.height;
464
+ }
459
465
  }
460
466
 
461
467
  const listContext = useMemo(
@@ -194,7 +194,7 @@ export const SubMenuFr = ({
194
194
  const openMenu = (...args: any[]) => {
195
195
  stopTimer();
196
196
  setHover();
197
- !isDisabled && _openMenu(...args);
197
+ if (!isDisabled) _openMenu(...args);
198
198
  };
199
199
 
200
200
  const setHover = () => !isHovering && !isDisabled && dispatch(HoverActionTypes.SET, menuItemRef?.current, 0);
@@ -226,7 +226,7 @@ export const SubMenuFr = ({
226
226
  // LEFT key is bubbled up from submenu items
227
227
  case Keys.LEFT:
228
228
  if (isOpen) {
229
- menuItemRef?.current && menuItemRef.current.focus();
229
+ menuItemRef?.current.focus();
230
230
  toggleMenu(false);
231
231
  handled = true;
232
232
  }
@@ -251,7 +251,7 @@ export const SubMenuFr = ({
251
251
  case Keys.ENTER:
252
252
  case Keys.SPACE:
253
253
  case Keys.RIGHT:
254
- openTrigger !== 'none' && openMenu(FocusPositions.FIRST);
254
+ if (openTrigger !== 'none') openMenu(FocusPositions.FIRST);
255
255
  break;
256
256
  }
257
257
  };
@@ -264,7 +264,7 @@ export const SubMenuFr = ({
264
264
  // Don't set focus when parent menu is closed, otherwise focus will be lost
265
265
  // and onBlur event will be fired with relatedTarget setting as null.
266
266
  if (isHovering && isParentOpen) {
267
- menuItemRef?.current && menuItemRef.current.focus();
267
+ menuItemRef?.current?.focus();
268
268
  } else {
269
269
  toggleMenu(false);
270
270
  }
@@ -276,11 +276,11 @@ export const SubMenuFr = ({
276
276
 
277
277
  useImperativeHandle(instanceRef, () => ({
278
278
  openMenu: (...args) => {
279
- isParentOpen && openMenu(...args);
279
+ if (isParentOpen) openMenu(...args);
280
280
  },
281
281
  closeMenu: () => {
282
282
  if (isOpen) {
283
- menuItemRef?.current && menuItemRef.current.focus();
283
+ menuItemRef?.current?.focus();
284
284
  toggleMenu(false);
285
285
  }
286
286
  },
@@ -18,11 +18,12 @@ export const useBEM = ({ block, element, modifiers, className }: useBemProps) =>
18
18
  const blockElement = element ? `${block}__${element}` : block;
19
19
 
20
20
  let classString = blockElement;
21
- modifiers &&
21
+ if (modifiers) {
22
22
  Object.keys(modifiers).forEach((name) => {
23
23
  const value = modifiers[name];
24
24
  if (value) classString += ` ${blockElement}--${value === true ? name : `${name}-${value}`}`;
25
25
  });
26
+ }
26
27
 
27
28
  let expandedClassName = typeof className === 'function' ? className(modifiers) : className;
28
29
  if (typeof expandedClassName === 'string') {
@@ -17,11 +17,11 @@ export const useItemState = (
17
17
  const timeoutId = useRef<ReturnType<typeof setTimeout>>(undefined);
18
18
 
19
19
  const setHover = () => {
20
- !isHovering && !isDisabled && dispatch(HoverActionTypes.SET, menuItemRef?.current, 0);
20
+ if (!isHovering && !isDisabled) dispatch(HoverActionTypes.SET, menuItemRef?.current, 0);
21
21
  };
22
22
 
23
23
  const unsetHover = () => {
24
- !isDisabled && dispatch(HoverActionTypes.UNSET, menuItemRef?.current, 0);
24
+ if (!isDisabled) dispatch(HoverActionTypes.UNSET, menuItemRef?.current, 0);
25
25
  };
26
26
 
27
27
  const onBlur = (e: FocusEvent) => {
@@ -48,7 +48,9 @@ export const useItemState = (
48
48
  timeoutId.current = undefined;
49
49
  }
50
50
 
51
- !keepHover && unsetHover();
51
+ if (!keepHover) {
52
+ unsetHover();
53
+ }
52
54
  };
53
55
 
54
56
  useItemEffect(isDisabled, menuItemRef, updateItems);
@@ -57,7 +59,9 @@ export const useItemState = (
57
59
  // Don't set focus when parent menu is closed, otherwise focus will be lost
58
60
  // and onBlur event will be fired with relatedTarget setting as null.
59
61
  if (isHovering && isParentOpen) {
60
- focusRef?.current && focusRef.current.focus();
62
+ if (focusRef?.current) {
63
+ focusRef.current.focus();
64
+ }
61
65
  }
62
66
  }, [focusRef, isHovering, isParentOpen]);
63
67
 
@@ -1,4 +1,4 @@
1
- @use "sass:math";
1
+ @use 'sass:math';
2
2
 
3
3
  $color: #212529;
4
4
  $color-dark: #cad1d8;
@@ -1,9 +1,9 @@
1
- @use "var";
2
- @use "mixins";
1
+ @use 'var';
2
+ @use 'mixins';
3
3
 
4
4
  .szh-menu {
5
- @include mixins.reset-list ();
6
- @include mixins.remove-focus ();
5
+ @include mixins.reset-list();
6
+ @include mixins.remove-focus();
7
7
  box-sizing: border-box;
8
8
  width: max-content;
9
9
  z-index: 100;
@@ -42,7 +42,7 @@
42
42
  }
43
43
 
44
44
  &__item {
45
- @include mixins.remove-focus ();
45
+ @include mixins.remove-focus();
46
46
  cursor: pointer;
47
47
 
48
48
  &--hover {
@@ -65,7 +65,7 @@
65
65
  }
66
66
 
67
67
  &__radio-group {
68
- @include mixins.reset-list ();
68
+ @include mixins.reset-list();
69
69
  }
70
70
 
71
71
  &__divider {