@ndlib/component-library 0.0.66 → 0.0.68

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.
@@ -11,6 +11,5 @@ type DropdownLinksProps = StyledElementProps<HTMLDivElement, {
11
11
  renderOption?: RenderOption<string, LinkOption>;
12
12
  openNewTab?: boolean;
13
13
  }, DropdownChildrenFn>;
14
- export declare const Overlay: React.FC<StyledElementProps<HTMLDivElement>>;
15
14
  export declare const DropdownLinks: React.FC<DropdownLinksProps>;
16
15
  export {};
@@ -1,87 +1,28 @@
1
- var __rest = (this && this.__rest) || function (s, e) {
2
- var t = {};
3
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
- t[p] = s[p];
5
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
- t[p[i]] = s[p[i]];
9
- }
10
- return t;
11
- };
12
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
- import { useCallback, useMemo, useState } from 'react';
14
- import { autoPlacement, offset, size, useFloating } from '@floating-ui/react';
1
+ import { jsx as _jsx } from "react/jsx-runtime";
15
2
  import { ListBox } from '../../elements/ListBox';
16
3
  import { Link } from '../../elements/Link';
17
- import { Box } from '../../elements/layout/Box';
18
4
  import { useComponentConfig } from '../../providers/componentConfig';
19
- import { useUniqueId } from '../../providers/uniqueIds';
20
- import { Z_INDEX } from '../../../theme/custom';
21
- export const Overlay = (props) => (_jsx(Box, Object.assign({}, props, { sx: {
22
- position: 'fixed',
23
- top: 0,
24
- left: 0,
25
- width: '100vw',
26
- height: '100vh',
27
- zIndex: Z_INDEX.ELEVATED,
28
- } })));
29
- export const DropdownLinks = (_a) => {
30
- var { children, options: optionsParam, renderOption: renderOptionParam, openNewTab, role = 'navigation' } = _a, rest = __rest(_a, ["children", "options", "renderOption", "openNewTab", "role"]);
31
- const [isOpen, setIsOpen] = useState(false);
32
- const [dropdownMinWidth, setDropdownMinWidth] = useState('0px');
33
- const listboxId = useUniqueId('dropdown-links');
34
- const anchorId = useUniqueId('dropdown-links-anchor');
5
+ import { Dropdown } from '../../elements/Dropdown';
6
+ export const DropdownLinks = ({ children, options: optionsParam, renderOption: renderOptionParam, openNewTab, role = 'navigation', }) => {
35
7
  const { navigate } = useComponentConfig().link;
36
- const options = optionsParam.map((option) => (Object.assign(Object.assign({}, option), { onClick: () => {
37
- if (option.onClick) {
38
- option.onClick();
39
- }
40
- else if (openNewTab) {
41
- window.open(option.value, '_blank');
42
- }
43
- else if (option.value) {
44
- navigate(option.value);
45
- }
46
- setIsOpen(false);
47
- } })));
48
8
  const renderOption = renderOptionParam
49
9
  ? renderOptionParam
50
10
  : (params) => (_jsx(Link, Object.assign({ to: params.option.value, target: openNewTab ? '_blank' : undefined, onClick: (e) => {
51
11
  e.preventDefault();
52
12
  } }, { children: params.option.label })));
53
- const { refs, floatingStyles } = useFloating({
54
- placement: 'bottom-start',
55
- middleware: [
56
- offset(2),
57
- size({
58
- apply({ rects }) {
59
- setDropdownMinWidth(`${rects.reference.width}px`);
60
- },
61
- }),
62
- autoPlacement({
63
- allowedPlacements: [
64
- 'top-start',
65
- 'bottom-start',
66
- 'top-end',
67
- 'bottom-end',
68
- ],
69
- }),
70
- ],
71
- });
72
- const onClick = useCallback(() => {
73
- setIsOpen(!isOpen);
74
- }, [isOpen]);
75
- const dropdownAnchorProps = useMemo(() => ({
76
- id: anchorId,
77
- 'aria-haspopup': true,
78
- 'aria-expanded': isOpen,
79
- 'aria-owns': listboxId,
80
- ref: refs.setReference,
81
- onClick,
82
- }), [refs, onClick, anchorId, isOpen, listboxId]);
83
- return (_jsxs(Box, Object.assign({}, rest, { children: [children &&
84
- children({
85
- anchorProps: dropdownAnchorProps,
86
- }), isOpen && (_jsxs(_Fragment, { children: [_jsx(ListBox, { role: role, id: listboxId, options: options, ref: refs.setFloating, renderOption: renderOption, style: Object.assign(Object.assign({ minWidth: dropdownMinWidth }, floatingStyles), { zIndex: Z_INDEX.DIALOG }) }), _jsx(Overlay, { onClick: () => setIsOpen(false) })] }))] })));
13
+ return (_jsx(Dropdown, { shouldRenderDropdownContainer: false, renderAnchor: children, renderDropdown: ({ ref, styles, id, setIsOpen }) => {
14
+ const options = optionsParam.map((option) => (Object.assign(Object.assign({}, option), { onClick: () => {
15
+ if (option.onClick) {
16
+ option.onClick();
17
+ }
18
+ else if (openNewTab) {
19
+ window.open(option.value, '_blank');
20
+ }
21
+ else if (option.value) {
22
+ navigate(option.value);
23
+ }
24
+ setIsOpen(false);
25
+ } })));
26
+ return (_jsx(ListBox, { role: role, id: id, options: options, ref: ref, renderOption: renderOption, style: styles }));
27
+ } }));
87
28
  };
@@ -83,6 +83,90 @@ const demoMenu = {
83
83
  },
84
84
  },
85
85
  },
86
+ {
87
+ id: 'test-menu-3',
88
+ label: 'test menu 3',
89
+ action: {
90
+ type: MENU_ACTION_TYPE.SUBMENU,
91
+ submenu: {
92
+ orientation: MENU_ORIENTATION.HORIZONTAL,
93
+ items: [
94
+ {
95
+ label: 'test submenu 5',
96
+ id: 'test-submenu-5',
97
+ action: {
98
+ type: MENU_ACTION_TYPE.SUBMENU,
99
+ submenu: {
100
+ orientation: MENU_ORIENTATION.VERTICAL,
101
+ items: [
102
+ {
103
+ label: 'test leaf 1',
104
+ id: 'test-leaf-1',
105
+ action: {
106
+ type: MENU_ACTION_TYPE.LINK,
107
+ to: '/',
108
+ },
109
+ },
110
+ {
111
+ label: 'test leaf 2',
112
+ id: 'test-leaf-2',
113
+ action: {
114
+ type: MENU_ACTION_TYPE.LINK,
115
+ to: '/',
116
+ },
117
+ },
118
+ ],
119
+ },
120
+ },
121
+ },
122
+ {
123
+ action: {
124
+ type: MENU_ACTION_TYPE.SUBMENU,
125
+ submenu: {
126
+ orientation: MENU_ORIENTATION.VERTICAL,
127
+ items: [
128
+ {
129
+ label: 'test leaf 3',
130
+ id: 'test-leaf-3',
131
+ action: {
132
+ type: MENU_ACTION_TYPE.LINK,
133
+ to: '/',
134
+ },
135
+ },
136
+ {
137
+ label: 'test leaf 4',
138
+ id: 'test-leaf-4',
139
+ action: {
140
+ type: MENU_ACTION_TYPE.LINK,
141
+ to: '/',
142
+ },
143
+ },
144
+ {
145
+ label: 'test leaf 5',
146
+ id: 'test-leaf-5',
147
+ action: {
148
+ type: MENU_ACTION_TYPE.LINK,
149
+ to: '/',
150
+ },
151
+ },
152
+ {
153
+ label: 'test leaf 6',
154
+ id: 'test-leaf-6',
155
+ action: {
156
+ type: MENU_ACTION_TYPE.LINK,
157
+ to: '/',
158
+ },
159
+ },
160
+ ],
161
+ },
162
+ },
163
+ label: 'test submenu 6',
164
+ id: 'test-submenu-6',
165
+ },
166
+ ],
167
+ },
168
+ },
169
+ },
86
170
  ],
87
171
  };
88
172
  export const DemoMenuImplementation = () => (_jsx(MenuProvider, Object.assign({ menu: demoMenu }, { children: ({ menu, getMenuProps, getMenuItemProps }) => (_jsxs(_Fragment, { children: [_jsx(Row, Object.assign({}, getMenuProps(), { children: menu.items.map((menuItem) => (_createElement(Button, Object.assign({}, getMenuItemProps([menuItem.id]), { sx: { m: 1 }, key: menuItem.id }), menuItem.label))) })), _jsx(NavMenu, {})] })) })));
@@ -6,7 +6,7 @@ import { Row } from '../../elements/layout/Row';
6
6
  import { Column } from '../../elements/layout/Column';
7
7
  import { HEADING_SIZE, Heading } from '../../elements/text/Heading';
8
8
  import { Link } from '../../elements/Link';
9
- import { LINE_HEIGHT } from '../../../theme/typography';
9
+ import { FONT_WEIGHT } from '../../../theme/typography';
10
10
  import { COLOR } from '../../../theme/colors';
11
11
  import { importedDefaultComponentShim } from '../../../utils/misc';
12
12
  const ChevronRightIcon = importedDefaultComponentShim(_ChevronRightIcon);
@@ -23,7 +23,6 @@ export const NavMenu = (props) => {
23
23
  }
24
24
  const submenu = activeTopLevelMenu.action.submenu;
25
25
  return (_jsxs(Row, Object.assign({}, props, { sx: {
26
- alignItems: 'center',
27
26
  justifyContent: 'center',
28
27
  p: 3,
29
28
  border: 'solid 1px',
@@ -37,7 +36,9 @@ export const NavMenu = (props) => {
37
36
  const submenuPath = [topLevelMenuId, submenuItem.id];
38
37
  return (_jsxs(Column, Object.assign({ sx: { flexBasis: '275px', p: 2, flexShrink: 0, flexGrow: 0 } }, { children: [_jsx(Heading, Object.assign({ standalone: true, size: HEADING_SIZE.SM, underline: true }, getMenuItemProps(submenuPath), { children: submenuItem.label })), _jsx(List, Object.assign({}, getMenuProps(submenuPath), { icon: ChevronRightIcon }, { children: leafMenuItems.map((leafMenuItem, i) => {
39
38
  const leafMenuPath = submenuPath.concat(leafMenuItem.id);
40
- return (_jsx(ListItem, Object.assign({ index: i, sx: { mt: 1 } }, getMenuItemProps(leafMenuPath), { children: _jsx(Link, Object.assign({ to: "/", sx: { lineHeight: LINE_HEIGHT.CONDENSED } }, { children: leafMenuItem.label })) }), leafMenuItem.id));
39
+ return (_jsx(ListItem, Object.assign({ index: i, sx: { mt: 1 } }, getMenuItemProps(leafMenuPath), { children: _jsx(Link, Object.assign({ to: "/", sx: {
40
+ fontWeight: FONT_WEIGHT.BOLD,
41
+ }, tabIndex: -1 }, { children: leafMenuItem.label })) }), leafMenuItem.id));
41
42
  }) }))] }), submenuItem.id));
42
43
  }), _jsx(Column, { sx: { flexGrow: 1 } })] })));
43
44
  };
@@ -0,0 +1,7 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Dropdown } from '.';
3
+ declare const meta: Meta<typeof Dropdown>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof Dropdown>;
6
+ export declare const Default: Story;
7
+ export declare const MatchWidth: Story;
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
3
+ import { Button } from '../../elements/Button';
4
+ import { Box } from '../../elements/layout/Box';
5
+ import { Dropdown } from '.';
6
+ import { Paragraph } from '../text/Paragraph';
7
+ const meta = {
8
+ title: 'Elements/Dropdown',
9
+ component: Dropdown,
10
+ tags: ['autodocs'],
11
+ };
12
+ export default meta;
13
+ const lorem = `Lorem ipsum dolor sit amet, consectetur adipiscing elit.
14
+ Nullam id nunc vitae magna aliquam aliquet. In hac habitasse platea dictumst.`;
15
+ export const Default = {
16
+ render: () => (_jsx(Box, Object.assign({ sx: { width: '200px', height: '200px' } }, { children: _jsx(Dropdown, { renderAnchor: ({ anchorProps }) => (_jsx(Button, Object.assign({}, anchorProps, { rightIcon: ArrowDropDownIcon }, { children: "Open Dropdown" }))), renderDropdown: () => _jsx(Paragraph, { children: lorem }) }) }))),
17
+ };
18
+ export const MatchWidth = {
19
+ render: () => (_jsx(Box, Object.assign({ sx: { width: '200px', height: '200px' } }, { children: _jsx(Dropdown, { matchWidth: true, renderAnchor: ({ anchorProps }) => (_jsx(Button, Object.assign({}, anchorProps, { rightIcon: ArrowDropDownIcon }, { children: "Open Dropdown" }))), renderDropdown: () => _jsx(Paragraph, { children: lorem }) }) }))),
20
+ };
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { StyledElementProps } from '../../../theme';
3
+ type AnchorProps = Omit<React.HTMLAttributes<HTMLButtonElement>, 'color'>;
4
+ type AnchorRenderFn = (params: {
5
+ anchorProps: AnchorProps;
6
+ isOpen: boolean;
7
+ setIsOpen: (isOpen: boolean) => void;
8
+ }) => React.ReactNode;
9
+ type DropdownRenderFn = (params: {
10
+ styles: React.CSSProperties;
11
+ ref: React.Ref<any>;
12
+ isOpen: boolean;
13
+ setIsOpen: (isOpen: boolean) => void;
14
+ id: string;
15
+ }) => React.ReactNode;
16
+ type DropdownProps = StyledElementProps<HTMLDivElement, {
17
+ renderAnchor: AnchorRenderFn;
18
+ renderDropdown: DropdownRenderFn;
19
+ shouldRenderDropdownContainer?: boolean;
20
+ matchWidth?: boolean;
21
+ }>;
22
+ export declare const Overlay: React.FC<StyledElementProps<HTMLDivElement>>;
23
+ export declare const Dropdown: React.FC<DropdownProps>;
24
+ export {};
@@ -0,0 +1,87 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
+ import { useCallback, useMemo, useState } from 'react';
14
+ import { useTheme } from '../../../theme';
15
+ import { autoPlacement, offset, size, useFloating } from '@floating-ui/react';
16
+ import { Box } from '../../elements/layout/Box';
17
+ import { useUniqueId } from '../../providers/uniqueIds';
18
+ import { Z_INDEX } from '../../../theme/custom';
19
+ import { COLOR } from '../../../theme/colors';
20
+ export const Overlay = (props) => (_jsx(Box, Object.assign({}, props, { sx: {
21
+ position: 'fixed',
22
+ top: 0,
23
+ left: 0,
24
+ width: '100vw',
25
+ height: '100vh',
26
+ zIndex: Z_INDEX.ELEVATED,
27
+ } })));
28
+ export const Dropdown = (_a) => {
29
+ var { renderAnchor, renderDropdown, matchWidth, shouldRenderDropdownContainer = true } = _a, rest = __rest(_a, ["renderAnchor", "renderDropdown", "matchWidth", "shouldRenderDropdownContainer"]);
30
+ const [isOpen, setIsOpen] = useState(false);
31
+ const [dropdownMinWidth, setDropdownMinWidth] = useState('0px');
32
+ const [dropdownMaxWidth, setDropdownMaxWidth] = useState();
33
+ const dropdownId = useUniqueId('dropdown');
34
+ const anchorId = useUniqueId('dropdown-anchor');
35
+ const { boxShadow } = useTheme();
36
+ const { refs, floatingStyles } = useFloating({
37
+ placement: 'bottom-start',
38
+ middleware: [
39
+ offset(2),
40
+ size({
41
+ apply({ rects }) {
42
+ setDropdownMinWidth(`${rects.reference.width}px`);
43
+ if (matchWidth) {
44
+ setDropdownMaxWidth(`${rects.reference.width}px`);
45
+ }
46
+ },
47
+ }),
48
+ autoPlacement({
49
+ allowedPlacements: [
50
+ 'top-start',
51
+ 'bottom-start',
52
+ 'top-end',
53
+ 'bottom-end',
54
+ ],
55
+ }),
56
+ ],
57
+ });
58
+ const dropdownBoxStyles = Object.assign(Object.assign({}, floatingStyles), { minWidth: dropdownMinWidth, maxWidth: dropdownMaxWidth, zIndex: Z_INDEX.DIALOG, backgroundColor: COLOR.WHITE, boxShadow: boxShadow.NORMAL });
59
+ const onClick = useCallback(() => {
60
+ setIsOpen(!isOpen);
61
+ }, [isOpen]);
62
+ const dropdownAnchorProps = useMemo(() => ({
63
+ id: anchorId,
64
+ 'aria-haspopup': true,
65
+ 'aria-expanded': isOpen,
66
+ 'aria-owns': dropdownId,
67
+ ref: refs.setReference,
68
+ onClick,
69
+ }), [refs, onClick, anchorId, isOpen, dropdownId]);
70
+ return (_jsxs(Box, Object.assign({}, rest, { children: [renderAnchor({
71
+ anchorProps: dropdownAnchorProps,
72
+ isOpen,
73
+ setIsOpen,
74
+ }), isOpen && (_jsxs(_Fragment, { children: [shouldRenderDropdownContainer ? (_jsx(Box, Object.assign({ id: dropdownId, ref: refs.setFloating, style: dropdownBoxStyles }, { children: renderDropdown({
75
+ styles: dropdownBoxStyles,
76
+ ref: refs.setFloating,
77
+ setIsOpen,
78
+ isOpen,
79
+ id: dropdownId,
80
+ }) }))) : (renderDropdown({
81
+ styles: dropdownBoxStyles,
82
+ ref: refs.setFloating,
83
+ isOpen,
84
+ setIsOpen,
85
+ id: dropdownId,
86
+ })), _jsx(Overlay, { onClick: () => setIsOpen(false) })] }))] })));
87
+ };
@@ -80,7 +80,7 @@ export const List = ({ sx, size: sizeParam, ordered, icon, iconSize: iconSizePar
80
80
  } }, { children: ordered ? (_jsx("ul", Object.assign({ sx: Object.assign(Object.assign({}, defaultStyles), sx) }, { children: children }))) : (_jsx("ol", Object.assign({ sx: Object.assign(Object.assign({}, defaultStyles), sx) }, { children: children }))) })));
81
81
  };
82
82
  export const ListItem = (_a) => {
83
- var { index, sx, icon: iconParam, iconSize: iconSizeParam, iconColor: iconColorParam, onIconClick, onClick, children, iconLabel } = _a, rest = __rest(_a, ["index", "sx", "icon", "iconSize", "iconColor", "onIconClick", "onClick", "children", "iconLabel"]);
83
+ var { index, sx, icon: iconParam, iconSize: iconSizeParam, iconColor: iconColorParam, onIconClick, onClick, children, iconLabel, tabIndex: tabIndexParam } = _a, rest = __rest(_a, ["index", "sx", "icon", "iconSize", "iconColor", "onIconClick", "onClick", "children", "iconLabel", "tabIndex"]);
84
84
  const { ordered, icon: listIcon, iconColor: listIconColor, iconSize: listIconSize, size, depth, } = useListConfig();
85
85
  const { flagInDevelopment } = useEnvironment();
86
86
  if (onIconClick && !iconLabel) {
@@ -92,10 +92,12 @@ export const ListItem = (_a) => {
92
92
  const typography = sizeTypographyMap[size];
93
93
  const typographyStyles = getTypographyStyles(sizeTypographyMap[size]);
94
94
  const lineHeight = useLinesHeight({ typography, lines: 1 });
95
- return (_jsxs("li", Object.assign({ sx: Object.assign(Object.assign({ depth, display: 'flex', mt: index === 0 && depth === 0 ? 0 : marginSizeMap[size], ml: 1 }, typographyStyles), sx) }, rest, { children: [ordered ? (_jsxs(Label, Object.assign({ standalone: true, sx: { mr: 2 } }, { children: [index + 1, "."] }))) : (_jsx(Column, Object.assign({ sx: { height: lineHeight }, justify: "center" }, { children: _jsx(Icon, { icon: icon, color: iconColor, size: iconSize, onClick: onIconClick, "aria-label": iconLabel, sx: {
95
+ const tabIndex = tabIndexParam || (onClick ? 0 : undefined);
96
+ console.log('rendering tab index', tabIndex);
97
+ return (_jsxs("li", Object.assign({ sx: Object.assign(Object.assign({ depth, display: 'flex', mt: index === 0 && depth === 0 ? 0 : marginSizeMap[size], ml: 1 }, typographyStyles), sx), tabIndex: tabIndex }, rest, { children: [ordered ? (_jsxs(Label, Object.assign({ standalone: true, sx: { mr: 2 } }, { children: [index + 1, "."] }))) : (_jsx(Column, Object.assign({ sx: { height: lineHeight }, justify: "center" }, { children: _jsx(Icon, { icon: icon, color: iconColor, size: iconSize, onClick: onIconClick, "aria-label": iconLabel, sx: {
96
98
  mr: 3,
97
99
  alignItems: 'flex-start',
98
100
  } }) }))), _jsx("div", Object.assign({ sx: {
99
101
  cursor: onClick ? 'pointer' : 'default',
100
- }, tabIndex: onClick ? 0 : undefined }, { children: children }))] })));
102
+ } }, { children: children }))] })));
101
103
  };
@@ -78,7 +78,6 @@ const SortableStory = () => {
78
78
  key: 'name',
79
79
  direction: SORT_DIRECTION.ASC,
80
80
  });
81
- console.log(sortConfig);
82
81
  const sorted = sortByKey({
83
82
  items: data,
84
83
  config: sortConfig,
@@ -4,6 +4,7 @@ import { useEnvironment } from './env';
4
4
  import { KEY_CODES, equals } from '../../utils/misc';
5
5
  import { useCheckMidClick } from '../../utils/hooks/useCheckMidClick';
6
6
  import { useUniqueId } from './uniqueIds';
7
+ import { useCheckMidKeyPress } from '../../utils/hooks/useCheckMidKeyPress';
7
8
  export var MENU_ACTION_TYPE;
8
9
  (function (MENU_ACTION_TYPE) {
9
10
  MENU_ACTION_TYPE["SUBMENU"] = "SUBMENU";
@@ -43,7 +44,7 @@ export const MenuProvider = ({ menu, children, }) => {
43
44
  const { flagInDevelopment } = useEnvironment();
44
45
  const menuId = useUniqueId('hcl-menu');
45
46
  const { isMidClick, elementProps: checkMidclickProps } = useCheckMidClick();
46
- const [usingKeyboard, setUsingKeyboard] = useState(false);
47
+ const { isMidKeyPress, elementProps: checkMidKeyPressProps } = useCheckMidKeyPress();
47
48
  const closeOnBlurTimeout = React.useRef();
48
49
  const initialPath = React.useMemo(() => [menu.items[0].id], [menu]);
49
50
  const [activePath, setActivePath] = useState(initialPath);
@@ -61,7 +62,6 @@ export const MenuProvider = ({ menu, children, }) => {
61
62
  }, []);
62
63
  const close = React.useCallback(() => {
63
64
  setIsOpen(false);
64
- setUsingKeyboard(false);
65
65
  }, []);
66
66
  const getMenuItem = React.useCallback((path) => {
67
67
  let menuItem = undefined;
@@ -100,17 +100,18 @@ export const MenuProvider = ({ menu, children, }) => {
100
100
  const currentMenuLength = activeParentMenu.items.length;
101
101
  const onFocus = React.useCallback(() => {
102
102
  if (!isMidClick.current) {
103
- setUsingKeyboard(true);
104
103
  open();
105
104
  }
106
105
  }, [isMidClick, open]);
107
106
  const onBlur = React.useCallback(() => {
107
+ checkMidKeyPressProps.onBlur();
108
108
  closeOnBlurTimeout.current = setTimeout(() => {
109
109
  close();
110
110
  setActivePath(initialPath);
111
111
  }, 300);
112
- }, [close, initialPath]);
112
+ }, [close, initialPath, checkMidKeyPressProps]);
113
113
  const onKeyDown = React.useCallback((e) => {
114
+ checkMidKeyPressProps.onKeyDown(e);
114
115
  const navigateNext = () => {
115
116
  const nextIndex = (activeIndex + 1) % currentMenuLength;
116
117
  const nextId = activeParentMenu.items[nextIndex].id;
@@ -160,9 +161,6 @@ export const MenuProvider = ({ menu, children, }) => {
160
161
  navigateIn();
161
162
  }
162
163
  }
163
- else {
164
- setUsingKeyboard(true);
165
- }
166
164
  if (e.key === KEY_CODES.ESCAPE) {
167
165
  if (activePath.length > 1) {
168
166
  navigateOut();
@@ -211,6 +209,7 @@ export const MenuProvider = ({ menu, children, }) => {
211
209
  activeParentMenu,
212
210
  currentMenuLength,
213
211
  getItemId,
212
+ checkMidKeyPressProps,
214
213
  ]);
215
214
  const getMenuProps = React.useCallback((path) => {
216
215
  const isTopLevel = !path;
@@ -223,15 +222,25 @@ export const MenuProvider = ({ menu, children, }) => {
223
222
  }
224
223
  }
225
224
  const isVertical = _menu.orientation === MENU_ORIENTATION.VERTICAL;
226
- const topLevelProps = Object.assign(Object.assign({}, checkMidclickProps), { onKeyDown,
225
+ const topLevelProps = Object.assign(Object.assign({}, checkMidclickProps), { onKeyUp: checkMidKeyPressProps.onKeyUp, onKeyDown,
227
226
  onFocus,
228
227
  onBlur });
229
228
  return Object.assign({ role: 'menu', 'aria-orientation': isVertical ? 'vertical' : 'horizontal', tabIndex: isTopLevel ? 0 : -1 }, (isTopLevel ? topLevelProps : {}));
230
- }, [menu, onFocus, onBlur, checkMidclickProps, getMenuItem, onKeyDown]);
229
+ }, [
230
+ menu,
231
+ onFocus,
232
+ onBlur,
233
+ checkMidclickProps,
234
+ checkMidKeyPressProps,
235
+ getMenuItem,
236
+ onKeyDown,
237
+ ]);
231
238
  const getMenuItemProps = React.useCallback((path) => {
232
239
  const isTopLevel = path.length === 1;
233
240
  const isActivePath = equals(path, activePath);
234
- return Object.assign(Object.assign({}, checkMidclickProps), { role: 'menuitem', 'aria-roledescription': getAriaRoleDescription(activeParentMenu.orientation), id: getItemId(path), onKeyDown: equals(path, activePath) ? onKeyDown : undefined, tabIndex: -1, onFocus: () => {
241
+ const useActiveStyles = isOpen && equals(path, activePath);
242
+ const styles = useActiveStyles ? activeStyles : inactiveStyles;
243
+ return Object.assign(Object.assign({}, checkMidclickProps), { role: 'menuitem', 'aria-roledescription': getAriaRoleDescription(activeParentMenu.orientation), id: getItemId(path), onKeyDown: equals(path, activePath) ? onKeyDown : undefined, onKeyUp: checkMidKeyPressProps.onKeyUp, tabIndex: -1, onFocus: () => {
235
244
  if (!isMidClick) {
236
245
  setActivePath(path);
237
246
  }
@@ -250,11 +259,14 @@ export const MenuProvider = ({ menu, children, }) => {
250
259
  if (isSubmenu &&
251
260
  isActivePath &&
252
261
  isTopLevel &&
253
- !usingKeyboard &&
262
+ !isMidKeyPress.current &&
254
263
  isOpen) {
255
264
  close();
256
265
  }
257
- else if (isSubmenu && isTopLevel && !usingKeyboard && !isOpen) {
266
+ else if (isSubmenu &&
267
+ isTopLevel &&
268
+ !isMidKeyPress.current &&
269
+ (!isOpen || !isActivePath)) {
258
270
  setActivePath(path);
259
271
  open();
260
272
  }
@@ -263,19 +275,18 @@ export const MenuProvider = ({ menu, children, }) => {
263
275
  key: KEY_CODES.ENTER,
264
276
  });
265
277
  }
266
- }, style: isOpen && usingKeyboard && equals(path, activePath)
267
- ? activeStyles
268
- : inactiveStyles });
278
+ }, style: styles });
269
279
  }, [
270
280
  getItemId,
271
281
  getMenuItem,
272
282
  activePath,
273
- isOpen,
274
283
  checkMidclickProps,
284
+ isMidKeyPress,
285
+ checkMidKeyPressProps,
286
+ isOpen,
275
287
  close,
276
288
  open,
277
289
  onKeyDown,
278
- usingKeyboard,
279
290
  activeParentMenu,
280
291
  isMidClick,
281
292
  ]);
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { theme, ThemeProvider, useTheme } from './theme';
2
- export type { StylesProp } from './theme';
2
+ export type { StylesProp, StyledElementProps } from './theme';
3
3
  export { COLOR } from './theme/colors';
4
4
  export { TYPOGRAPHY_TYPE, FONT, FONT_SIZE, FONT_WEIGHT, LINE_HEIGHT, } from './theme/typography';
5
5
  export { GlobalStyles } from './theme/GlobalStyles';
@@ -34,7 +34,6 @@ export { TabList, Tab } from './components/elements/TabList';
34
34
  export { Card, CARD_SIZE, CARD_LAYOUT } from './components/composites/Card';
35
35
  export { SnackBar } from './components/composites/SnackBar';
36
36
  export { NavMenu } from './components/composites/NavMenu';
37
- export { MENU_ACTION_TYPE, MENU_ORIENTATION } from './components/providers/menu';
38
37
  export { EmptyState, EMPTY_STATE_SIZE, } from './components/composites/EmptyState';
39
38
  export { DropdownLinks } from './components/composites/DropdownLinks';
40
39
  export { Modal } from './components/composites/Modal';
@@ -42,7 +41,7 @@ export { DragDropList, DragHandle } from './components/composites/DragDropList';
42
41
  export { Seo } from './components/composites/Seo';
43
42
  export { StructuredData } from './components/composites/StructuredData';
44
43
  export { UiProvider } from './components/providers/ui';
45
- export { MenuProvider, useMenu } from './components/providers/menu';
44
+ export { MenuProvider, useMenu, MENU_ACTION_TYPE, MENU_ORIENTATION, } from './components/providers/menu';
46
45
  export { SnackBarProvider, useSnackBar } from './components/providers/snackBar';
47
46
  export { useAlerts, AlertsProvider, ALERT_TYPE, ALERT_DOMAIN, } from './components/providers/alerts';
48
47
  export { MediaSizeProvider, useMediaQuery } from './components/providers/media';
package/dist/index.js CHANGED
@@ -33,7 +33,6 @@ export { TabList, Tab } from './components/elements/TabList';
33
33
  export { Card, CARD_SIZE, CARD_LAYOUT } from './components/composites/Card';
34
34
  export { SnackBar } from './components/composites/SnackBar';
35
35
  export { NavMenu } from './components/composites/NavMenu';
36
- export { MENU_ACTION_TYPE, MENU_ORIENTATION } from './components/providers/menu';
37
36
  export { EmptyState, EMPTY_STATE_SIZE, } from './components/composites/EmptyState';
38
37
  export { DropdownLinks } from './components/composites/DropdownLinks';
39
38
  export { Modal } from './components/composites/Modal';
@@ -41,7 +40,7 @@ export { DragDropList, DragHandle } from './components/composites/DragDropList';
41
40
  export { Seo } from './components/composites/Seo';
42
41
  export { StructuredData } from './components/composites/StructuredData';
43
42
  export { UiProvider } from './components/providers/ui';
44
- export { MenuProvider, useMenu } from './components/providers/menu';
43
+ export { MenuProvider, useMenu, MENU_ACTION_TYPE, MENU_ORIENTATION, } from './components/providers/menu';
45
44
  export { SnackBarProvider, useSnackBar } from './components/providers/snackBar';
46
45
  export { useAlerts, AlertsProvider, ALERT_TYPE, ALERT_DOMAIN, } from './components/providers/alerts';
47
46
  export { MediaSizeProvider, useMediaQuery } from './components/providers/media';
@@ -7,11 +7,11 @@ export const useCheckMidClick = () => {
7
7
  const onMouseUp = () => {
8
8
  isMidClick.current = false;
9
9
  };
10
- return {
10
+ return React.useMemo(() => ({
11
11
  elementProps: {
12
12
  onMouseDown,
13
13
  onMouseUp,
14
14
  },
15
15
  isMidClick,
16
- };
16
+ }), []);
17
17
  };
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ export declare const useCheckMidKeyPress: () => {
3
+ elementProps: {
4
+ onKeyDown: (e: React.KeyboardEvent) => void;
5
+ onKeyUp: () => void;
6
+ onBlur: () => void;
7
+ };
8
+ isMidKeyPress: React.MutableRefObject<boolean>;
9
+ };
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ export const useCheckMidKeyPress = () => {
3
+ const isMidKeyPress = React.useRef(false);
4
+ const onKeyDown = (e) => {
5
+ if (e.key === 'Tab' || e.key === 'Escape')
6
+ return;
7
+ isMidKeyPress.current = true;
8
+ };
9
+ const onKeyUp = () => {
10
+ isMidKeyPress.current = false;
11
+ };
12
+ const onBlur = () => {
13
+ isMidKeyPress.current = false;
14
+ };
15
+ return React.useMemo(() => ({
16
+ elementProps: {
17
+ onKeyDown,
18
+ onKeyUp,
19
+ onBlur,
20
+ },
21
+ isMidKeyPress,
22
+ }), []);
23
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ndlib/component-library",
3
- "version": "0.0.66",
3
+ "version": "0.0.68",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "files": [