@ndlib/component-library 0.0.67 → 0.0.69
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/components/composites/DropdownLinks/index.d.ts +0 -1
- package/dist/components/composites/DropdownLinks/index.js +18 -77
- package/dist/components/composites/NavMenu/demo.js +84 -0
- package/dist/components/composites/NavMenu/index.js +4 -3
- package/dist/components/elements/Dropdown/Dropdown.stories.d.ts +7 -0
- package/dist/components/elements/Dropdown/Dropdown.stories.js +20 -0
- package/dist/components/elements/Dropdown/index.d.ts +24 -0
- package/dist/components/elements/Dropdown/index.js +87 -0
- package/dist/components/elements/List/index.js +5 -3
- package/dist/components/elements/Table/Table.stories.js +0 -1
- package/dist/components/providers/menu.js +28 -17
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/utils/hooks/useCheckMidClick.js +2 -2
- package/dist/utils/hooks/useCheckMidKeyPress.d.ts +9 -0
- package/dist/utils/hooks/useCheckMidKeyPress.js +23 -0
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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 {
|
|
20
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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 {
|
|
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: {
|
|
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
|
-
|
|
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
|
-
}
|
|
102
|
+
} }, { children: children }))] })));
|
|
101
103
|
};
|
|
@@ -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
|
|
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
|
-
}, [
|
|
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
|
-
|
|
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
|
-
!
|
|
262
|
+
!isMidKeyPress.current &&
|
|
254
263
|
isOpen) {
|
|
255
264
|
close();
|
|
256
265
|
}
|
|
257
|
-
else if (isSubmenu &&
|
|
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:
|
|
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
|
@@ -27,6 +27,7 @@ export { RadioGroup } from './components/elements/Fields/RadioGroup';
|
|
|
27
27
|
export { List, ListItem, LIST_SIZE } from './components/elements/List';
|
|
28
28
|
export { ReadMore } from './components/elements/text/ReadMore';
|
|
29
29
|
export { Icon } from './components/elements/Icon';
|
|
30
|
+
export { Dropdown } from './components/elements/Dropdown';
|
|
30
31
|
export { Spinner, SPINNER_SIZE } from './components/elements/Spinner';
|
|
31
32
|
export { Pill, PILL_SIZE, PILL_TYPE } from './components/elements/Pill';
|
|
32
33
|
export { Table, TableColumn } from './components/elements/Table';
|
|
@@ -34,7 +35,6 @@ export { TabList, Tab } from './components/elements/TabList';
|
|
|
34
35
|
export { Card, CARD_SIZE, CARD_LAYOUT } from './components/composites/Card';
|
|
35
36
|
export { SnackBar } from './components/composites/SnackBar';
|
|
36
37
|
export { NavMenu } from './components/composites/NavMenu';
|
|
37
|
-
export { MENU_ACTION_TYPE, MENU_ORIENTATION } from './components/providers/menu';
|
|
38
38
|
export { EmptyState, EMPTY_STATE_SIZE, } from './components/composites/EmptyState';
|
|
39
39
|
export { DropdownLinks } from './components/composites/DropdownLinks';
|
|
40
40
|
export { Modal } from './components/composites/Modal';
|
|
@@ -42,7 +42,7 @@ export { DragDropList, DragHandle } from './components/composites/DragDropList';
|
|
|
42
42
|
export { Seo } from './components/composites/Seo';
|
|
43
43
|
export { StructuredData } from './components/composites/StructuredData';
|
|
44
44
|
export { UiProvider } from './components/providers/ui';
|
|
45
|
-
export { MenuProvider, useMenu } from './components/providers/menu';
|
|
45
|
+
export { MenuProvider, useMenu, MENU_ACTION_TYPE, MENU_ORIENTATION, } from './components/providers/menu';
|
|
46
46
|
export { SnackBarProvider, useSnackBar } from './components/providers/snackBar';
|
|
47
47
|
export { useAlerts, AlertsProvider, ALERT_TYPE, ALERT_DOMAIN, } from './components/providers/alerts';
|
|
48
48
|
export { MediaSizeProvider, useMediaQuery } from './components/providers/media';
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,7 @@ export { RadioGroup } from './components/elements/Fields/RadioGroup';
|
|
|
26
26
|
export { List, ListItem, LIST_SIZE } from './components/elements/List';
|
|
27
27
|
export { ReadMore } from './components/elements/text/ReadMore';
|
|
28
28
|
export { Icon } from './components/elements/Icon';
|
|
29
|
+
export { Dropdown } from './components/elements/Dropdown';
|
|
29
30
|
export { Spinner, SPINNER_SIZE } from './components/elements/Spinner';
|
|
30
31
|
export { Pill, PILL_SIZE, PILL_TYPE } from './components/elements/Pill';
|
|
31
32
|
export { Table, TableColumn } from './components/elements/Table';
|
|
@@ -33,7 +34,6 @@ export { TabList, Tab } from './components/elements/TabList';
|
|
|
33
34
|
export { Card, CARD_SIZE, CARD_LAYOUT } from './components/composites/Card';
|
|
34
35
|
export { SnackBar } from './components/composites/SnackBar';
|
|
35
36
|
export { NavMenu } from './components/composites/NavMenu';
|
|
36
|
-
export { MENU_ACTION_TYPE, MENU_ORIENTATION } from './components/providers/menu';
|
|
37
37
|
export { EmptyState, EMPTY_STATE_SIZE, } from './components/composites/EmptyState';
|
|
38
38
|
export { DropdownLinks } from './components/composites/DropdownLinks';
|
|
39
39
|
export { Modal } from './components/composites/Modal';
|
|
@@ -41,7 +41,7 @@ export { DragDropList, DragHandle } from './components/composites/DragDropList';
|
|
|
41
41
|
export { Seo } from './components/composites/Seo';
|
|
42
42
|
export { StructuredData } from './components/composites/StructuredData';
|
|
43
43
|
export { UiProvider } from './components/providers/ui';
|
|
44
|
-
export { MenuProvider, useMenu } from './components/providers/menu';
|
|
44
|
+
export { MenuProvider, useMenu, MENU_ACTION_TYPE, MENU_ORIENTATION, } from './components/providers/menu';
|
|
45
45
|
export { SnackBarProvider, useSnackBar } from './components/providers/snackBar';
|
|
46
46
|
export { useAlerts, AlertsProvider, ALERT_TYPE, ALERT_DOMAIN, } from './components/providers/alerts';
|
|
47
47
|
export { MediaSizeProvider, useMediaQuery } from './components/providers/media';
|
|
@@ -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
|
+
};
|