@utahdts/utah-design-system 1.15.5 → 1.16.1
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/css/1-settings/_spacing.scss +16 -0
- package/css/6-components/_components-index.scss +1 -0
- package/css/6-components/base-components/forms/_combo-box-input.scss +5 -0
- package/css/6-components/base-components/forms/_time-input.scss +28 -0
- package/css/6-components/base-components/navigation/_menu-item.scss +35 -0
- package/css/6-components/base-components/navigation/_side-panel-navigation.scss +6 -1
- package/css/6-components/base-components/navigation/_vertical-menu.scss +45 -1
- package/dist/style.css +297 -9
- package/dist/utah-design-system.es.js +3846 -3441
- package/dist/utah-design-system.umd.js +3845 -3440
- package/index.js +2 -0
- package/package.json +12 -12
- package/react/components/forms/ComboBox/ComboBox.jsx +9 -5
- package/react/components/forms/ComboBox/ComboBoxOption.jsx +10 -0
- package/react/components/forms/ComboBox/context/ComboBoxContextProvider.jsx +10 -0
- package/react/components/forms/ComboBox/internal/CombBoxListBox.jsx +6 -4
- package/react/components/forms/ComboBox/internal/ComboBoxTextInput.jsx +11 -1
- package/react/components/forms/FormContext/useFormContextInputValue.js +4 -1
- package/react/components/forms/MultiSelect/MultiSelect.jsx +6 -0
- package/react/components/forms/MultiSelect/MultiSelectComboBox.jsx +6 -0
- package/react/components/forms/PlainText.jsx +7 -2
- package/react/components/forms/Select.jsx +3 -3
- package/react/components/forms/TextArea.jsx +3 -3
- package/react/components/forms/TextInput.jsx +3 -3
- package/react/components/forms/TimeInput.jsx +166 -0
- package/react/components/navigation/HorizontalMenu.jsx +2 -2
- package/react/components/navigation/VerticalMenu.jsx +45 -8
- package/react/components/navigation/items/MenuItemFlyout.jsx +119 -0
- package/react/components/navigation/{MenuItem.jsx → items/MenuItemInline.jsx} +24 -8
- package/react/components/navigation/items/MenuItemPlain.jsx +63 -0
- package/react/enums/menuTypes.js +7 -0
- package/react/hooks/useDebounceFunc.js +1 -1
- package/react/hooks/useGlobalKeyEvent.js +1 -1
|
@@ -1,34 +1,71 @@
|
|
|
1
|
+
import { childrenMenuTypes } from '@utahdts/utah-design-system-header';
|
|
1
2
|
import { joinClassNames } from '../../util/joinClassNames';
|
|
2
|
-
import {
|
|
3
|
+
import { MenuItemInline } from './items/MenuItemInline';
|
|
4
|
+
import { MenuItemPlain } from './items/MenuItemPlain';
|
|
5
|
+
import { MenuItemFlyout } from './items/MenuItemFlyout';
|
|
6
|
+
import { menuTypes } from '../../enums/menuTypes';
|
|
3
7
|
|
|
4
8
|
/** @typedef {import('@utahdts/utah-design-system').WebsiteMainMenu} WebsiteMainMenu */
|
|
5
9
|
/** @typedef {import('@utahdts/utah-design-system').WebsiteMainMenuItem} WebsiteMainMenuItem */
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* @param {object} props
|
|
13
|
+
* @param {string} [props.className]
|
|
9
14
|
* @param {WebsiteMainMenu | WebsiteMainMenuItem} [props.currentMenuItem]
|
|
10
15
|
* @param {WebsiteMainMenu[]} props.menus
|
|
11
16
|
* @returns {import('react').JSX.Element}
|
|
12
17
|
*/
|
|
13
|
-
export function VerticalMenu({ currentMenuItem, menus }) {
|
|
18
|
+
export function VerticalMenu({ className, currentMenuItem, menus }) {
|
|
14
19
|
return (
|
|
15
20
|
<>
|
|
16
21
|
{
|
|
17
22
|
menus.map((menu) => {
|
|
18
23
|
const TitleTagName = menu.titleTagName || 'h2';
|
|
19
24
|
return (
|
|
20
|
-
<nav className=
|
|
25
|
+
<nav className={className} key={`vertical-menu__${menu.id}`} aria-labelledby={menu.id}>
|
|
21
26
|
{/* @ts-ignore */}
|
|
22
27
|
<TitleTagName
|
|
23
28
|
id={menu.id}
|
|
24
|
-
className={joinClassNames(menu.titleTagClassName, '
|
|
29
|
+
className={joinClassNames(menu.titleTagClassName, 'vertical-menu__header')}
|
|
25
30
|
>
|
|
26
31
|
{menu.header}
|
|
27
32
|
</TitleTagName>
|
|
28
|
-
<ul key={`
|
|
29
|
-
{menu.menuItems.map((menuItem) =>
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
<ul key={`vertical-menu__list__${menu.id}`} role="menu" className="vertical-menu">
|
|
34
|
+
{menu.menuItems.map((menuItem) => {
|
|
35
|
+
let result;
|
|
36
|
+
switch (menuItem.childrenMenuType) {
|
|
37
|
+
case childrenMenuTypes.INLINE:
|
|
38
|
+
result = (
|
|
39
|
+
<MenuItemInline
|
|
40
|
+
menuType={menuTypes.VERTICAL}
|
|
41
|
+
currentMenuItem={currentMenuItem}
|
|
42
|
+
menuItem={menuItem}
|
|
43
|
+
key={`vertical-menu__menu-item__${menuItem.link}-${menuItem.title}}`}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
break;
|
|
47
|
+
case childrenMenuTypes.FLYOUT:
|
|
48
|
+
result = (
|
|
49
|
+
<MenuItemFlyout
|
|
50
|
+
menuType={menuTypes.VERTICAL}
|
|
51
|
+
currentMenuItem={currentMenuItem}
|
|
52
|
+
menuItem={menuItem}
|
|
53
|
+
key={`vertical-menu__menu-item__${menuItem.link}-${menuItem.title}}`}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
break;
|
|
57
|
+
default:
|
|
58
|
+
result = (
|
|
59
|
+
<MenuItemPlain
|
|
60
|
+
menuType={menuTypes.VERTICAL}
|
|
61
|
+
menuItem={menuItem}
|
|
62
|
+
key={`vertical-menu__menu-item__${menuItem.link}-${menuItem.title}}`}
|
|
63
|
+
currentMenuItem={currentMenuItem}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
})}
|
|
32
69
|
</ul>
|
|
33
70
|
</nav>
|
|
34
71
|
);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { NavLink } from 'react-router-dom';
|
|
2
|
+
import { useImmer } from 'use-immer';
|
|
3
|
+
import { useRef } from 'react';
|
|
4
|
+
import { usePopper } from 'react-popper';
|
|
5
|
+
import { joinClassNames } from '../../../util/joinClassNames';
|
|
6
|
+
import { IconButton } from '../../buttons/IconButton';
|
|
7
|
+
import { ICON_BUTTON_APPEARANCE } from '../../../enums/buttonEnums';
|
|
8
|
+
import { Icons } from '../../icons/Icons';
|
|
9
|
+
import { menuTypes } from '../../../enums/menuTypes';
|
|
10
|
+
import { useClickOutside } from '../../../hooks/useClickOutside';
|
|
11
|
+
|
|
12
|
+
/** @typedef {import('@utahdts/utah-design-system').WebsiteMainMenu} WebsiteMainMenu */
|
|
13
|
+
/** @typedef {import('@utahdts/utah-design-system').WebsiteMainMenuItem} WebsiteMainMenuItem */
|
|
14
|
+
/** @typedef {import('@utahdts/utah-design-system').MenuTypes} MenuTypes */
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {object} props
|
|
18
|
+
* @param {WebsiteMainMenu | WebsiteMainMenuItem} [props.currentMenuItem]
|
|
19
|
+
* @param {WebsiteMainMenuItem} props.menuItem
|
|
20
|
+
* @param {MenuTypes} [props.menuType]
|
|
21
|
+
* @returns {import('react').JSX.Element}
|
|
22
|
+
*/
|
|
23
|
+
export function MenuItemFlyout({
|
|
24
|
+
currentMenuItem,
|
|
25
|
+
menuItem,
|
|
26
|
+
menuType,
|
|
27
|
+
}) {
|
|
28
|
+
const [isChildrenOpen, setIsChildrenOpen] = useImmer(false);
|
|
29
|
+
const referenceElement = useRef(/** @type {HTMLLIElement | null} */(null));
|
|
30
|
+
const popperRef = useRef(/** @type {HTMLDivElement | null} */(null));
|
|
31
|
+
const { styles, attributes } = usePopper(referenceElement.current, popperRef.current, { placement: 'right-start' });
|
|
32
|
+
|
|
33
|
+
const navLinkRef = useRef(/** @type {HTMLAnchorElement | null} */(null));
|
|
34
|
+
useClickOutside(popperRef, () => setIsChildrenOpen(false), !isChildrenOpen);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<li className={menuType === menuTypes.VERTICAL ? 'vertical-menu__item' : 'menu-item'} ref={referenceElement}>
|
|
38
|
+
<span className={menuType === menuTypes.VERTICAL ? 'vertical-menu__title' : 'menu-item__title'}>
|
|
39
|
+
{
|
|
40
|
+
(!menuItem?.link || menuItem?.link?.includes('::'))
|
|
41
|
+
? (
|
|
42
|
+
<button
|
|
43
|
+
aria-expanded={isChildrenOpen ? 'true' : 'false'}
|
|
44
|
+
aria-controls={`menu-item-${menuItem.id}-${menuItem.link}-popup`}
|
|
45
|
+
aria-haspopup="dialog"
|
|
46
|
+
className="menu-item__button-title"
|
|
47
|
+
id={`menu-item-${menuItem.id}-${menuItem.link}`}
|
|
48
|
+
onClick={() => setIsChildrenOpen((previouslyOpen) => !previouslyOpen)}
|
|
49
|
+
type="button"
|
|
50
|
+
title={menuItem.children ? 'Expand sub-menu' : ''}
|
|
51
|
+
>
|
|
52
|
+
<span className={menuType === menuTypes.VERTICAL ? 'vertical-menu__link-text' : 'menu__link-text'}>{menuItem.title}</span>
|
|
53
|
+
{menuItem.children ? <span className="utds-icon-before-chevron-right vertical-menu__chevron is-closed" aria-hidden="true" /> : null}
|
|
54
|
+
</button>
|
|
55
|
+
)
|
|
56
|
+
: (
|
|
57
|
+
<NavLink
|
|
58
|
+
className={(navData) => joinClassNames(
|
|
59
|
+
menuType === menuTypes.VERTICAL ? 'vertical-menu__link-title' : 'menu-item__link-title',
|
|
60
|
+
(currentMenuItem?.parentLinks?.includes(menuItem.link ?? '') || navData.isActive)
|
|
61
|
+
&& (currentMenuItem?.children?.length ? 'menu-item--selected_parent' : 'menu-item--selected')
|
|
62
|
+
)}
|
|
63
|
+
end
|
|
64
|
+
to={menuItem.link}
|
|
65
|
+
ref={navLinkRef}
|
|
66
|
+
>
|
|
67
|
+
{menuItem.title}
|
|
68
|
+
</NavLink>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
{
|
|
72
|
+
(menuItem.children && menuItem?.link)
|
|
73
|
+
? (
|
|
74
|
+
<IconButton
|
|
75
|
+
appearance={ICON_BUTTON_APPEARANCE.BORDERLESS}
|
|
76
|
+
aria-labelledby={`menu-item-${menuItem.id}-${menuItem.link}`}
|
|
77
|
+
aria-expanded={isChildrenOpen ? 'true' : 'false'}
|
|
78
|
+
className="menu-item__chevron"
|
|
79
|
+
onClick={() => setIsChildrenOpen((previouslyOpen) => !previouslyOpen)}
|
|
80
|
+
icon={<Icons.IconChevron />}
|
|
81
|
+
title="Expand sub-menu"
|
|
82
|
+
/>
|
|
83
|
+
)
|
|
84
|
+
: null
|
|
85
|
+
}
|
|
86
|
+
<span className="menu-chiclet" />
|
|
87
|
+
</span>
|
|
88
|
+
{
|
|
89
|
+
menuItem.children
|
|
90
|
+
? (
|
|
91
|
+
<div
|
|
92
|
+
aria-labelledby={`menu-item-${menuItem.id}-${menuItem.link}`}
|
|
93
|
+
className={joinClassNames(
|
|
94
|
+
'popup__wrapper',
|
|
95
|
+
isChildrenOpen ? 'popup__wrapper--visible' : 'popup__wrapper--hidden'
|
|
96
|
+
)}
|
|
97
|
+
id={`menu-item-${menuItem.id}-${menuItem.link}-popup`}
|
|
98
|
+
ref={popperRef}
|
|
99
|
+
style={styles.popper}
|
|
100
|
+
{...attributes.popper}
|
|
101
|
+
>
|
|
102
|
+
<div className="popup__content flyout-menu">
|
|
103
|
+
<ul role="menu" className={menuType === menuTypes.VERTICAL ? 'vertical-menu' : ''}>
|
|
104
|
+
{menuItem.children?.map((menuItemChild) => (
|
|
105
|
+
<MenuItemFlyout
|
|
106
|
+
key={`menu-item__child__${menuItemChild.link}-${menuItemChild.title}}`}
|
|
107
|
+
menuItem={menuItemChild}
|
|
108
|
+
menuType={menuType}
|
|
109
|
+
/>
|
|
110
|
+
))}
|
|
111
|
+
</ul>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
)
|
|
115
|
+
: null
|
|
116
|
+
}
|
|
117
|
+
</li>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { useEffect, useLayoutEffect, useRef } from 'react';
|
|
2
2
|
import { NavLink, useLocation } from 'react-router-dom';
|
|
3
3
|
import { useImmer } from 'use-immer';
|
|
4
|
-
import { ICON_BUTTON_APPEARANCE } from '
|
|
5
|
-
import { joinClassNames } from '
|
|
6
|
-
import { IconButton } from '
|
|
7
|
-
import { Icons } from '
|
|
4
|
+
import { ICON_BUTTON_APPEARANCE } from '../../../enums/buttonEnums';
|
|
5
|
+
import { joinClassNames } from '../../../util/joinClassNames';
|
|
6
|
+
import { IconButton } from '../../buttons/IconButton';
|
|
7
|
+
import { Icons } from '../../icons/Icons';
|
|
8
|
+
import { menuTypes } from '../../../enums/menuTypes';
|
|
8
9
|
|
|
9
10
|
/** @typedef {import('@utahdts/utah-design-system').WebsiteMainMenu} WebsiteMainMenu */
|
|
10
11
|
/** @typedef {import('@utahdts/utah-design-system').WebsiteMainMenuItem} WebsiteMainMenuItem */
|
|
12
|
+
/** @typedef {import('@utahdts/utah-design-system').MenuTypes} MenuTypes */
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* @param {object} props
|
|
14
16
|
* @param {WebsiteMainMenu | WebsiteMainMenuItem} [props.currentMenuItem]
|
|
15
17
|
* @param {WebsiteMainMenuItem} props.menuItem
|
|
18
|
+
* @param {MenuTypes} [props.menuType]
|
|
16
19
|
* @returns {import('react').JSX.Element}
|
|
17
20
|
*/
|
|
18
|
-
export function
|
|
21
|
+
export function MenuItemInline({
|
|
22
|
+
currentMenuItem,
|
|
23
|
+
menuItem,
|
|
24
|
+
menuType = menuTypes.VERTICAL,
|
|
25
|
+
}) {
|
|
19
26
|
const { pathname } = useLocation();
|
|
20
27
|
// check if any of this menuItem's children are the currently open page/menuItem and if so, then keep this menuItem's children list open
|
|
21
28
|
const [isChildrenOpen, setIsChildrenOpen] = useImmer(() => (
|
|
@@ -49,7 +56,7 @@ export function MenuItem({ currentMenuItem, menuItem }) {
|
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
return (
|
|
52
|
-
<li className=
|
|
59
|
+
<li className={menuType === menuTypes.VERTICAL ? 'vertical-menu__item' : 'menu-item'}>
|
|
53
60
|
<span className="menu-item__title">
|
|
54
61
|
{/* === menu item title === */}
|
|
55
62
|
{
|
|
@@ -68,6 +75,7 @@ export function MenuItem({ currentMenuItem, menuItem }) {
|
|
|
68
75
|
: (
|
|
69
76
|
<NavLink
|
|
70
77
|
className={(navData) => joinClassNames(
|
|
78
|
+
'menu-item__link-title',
|
|
71
79
|
(currentMenuItem?.parentLinks?.includes(menuItem.link ?? '') || navData.isActive)
|
|
72
80
|
&& (currentMenuItem?.children?.length ? 'menu-item--selected_parent' : 'menu-item--selected')
|
|
73
81
|
)}
|
|
@@ -106,11 +114,19 @@ export function MenuItem({ currentMenuItem, menuItem }) {
|
|
|
106
114
|
{
|
|
107
115
|
menuItem.children
|
|
108
116
|
? (
|
|
109
|
-
<ul
|
|
117
|
+
<ul
|
|
118
|
+
role="menu"
|
|
119
|
+
className={joinClassNames(
|
|
120
|
+
'menu-item__sub-menu',
|
|
121
|
+
menuType === menuTypes.VERTICAL ? 'vertical-menu' : '',
|
|
122
|
+
isChildrenOpen ? 'menu-item__sub-menu--open' : ''
|
|
123
|
+
)}
|
|
124
|
+
>
|
|
110
125
|
{menuItem.children?.map((menuItemChild) => (
|
|
111
|
-
<
|
|
126
|
+
<MenuItemInline
|
|
112
127
|
key={`menu-item__child__${menuItemChild.link}-${menuItemChild.title}}`}
|
|
113
128
|
menuItem={menuItemChild}
|
|
129
|
+
menuType={menuType}
|
|
114
130
|
/>
|
|
115
131
|
))}
|
|
116
132
|
</ul>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { NavLink } from 'react-router-dom';
|
|
2
|
+
import { useRef } from 'react';
|
|
3
|
+
import { joinClassNames } from '../../../util/joinClassNames';
|
|
4
|
+
import { menuTypes } from '../../../enums/menuTypes';
|
|
5
|
+
|
|
6
|
+
/** @typedef {import('@utahdts/utah-design-system').WebsiteMainMenu} WebsiteMainMenu */
|
|
7
|
+
/** @typedef {import('@utahdts/utah-design-system').WebsiteMainMenuItem} WebsiteMainMenuItem */
|
|
8
|
+
/** @typedef {import('@utahdts/utah-design-system').MenuTypes} MenuTypes */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {object} props
|
|
12
|
+
* @param {WebsiteMainMenu | WebsiteMainMenuItem} [props.currentMenuItem]
|
|
13
|
+
* @param {WebsiteMainMenuItem} props.menuItem
|
|
14
|
+
* @param {MenuTypes} [props.menuType]
|
|
15
|
+
* @returns {import('react').JSX.Element}
|
|
16
|
+
*/
|
|
17
|
+
export function MenuItemPlain({
|
|
18
|
+
currentMenuItem,
|
|
19
|
+
menuItem,
|
|
20
|
+
menuType,
|
|
21
|
+
}) {
|
|
22
|
+
const navLinkRef = useRef(/** @type {HTMLAnchorElement | null} */(null));
|
|
23
|
+
return (
|
|
24
|
+
<li className={menuType === menuTypes.VERTICAL ? 'vertical-menu__item' : 'menu-item'}>
|
|
25
|
+
<span className={menuType === menuTypes.VERTICAL ? 'vertical-menu__title' : 'menu-item__title'}>
|
|
26
|
+
{
|
|
27
|
+
(!menuItem?.link || menuItem?.link?.includes('::'))
|
|
28
|
+
? <span className={menuType === menuTypes.VERTICAL ? 'vertical-menu__title__plain' : 'menu-item__title__plain'}>{menuItem.title}</span>
|
|
29
|
+
: (
|
|
30
|
+
<NavLink
|
|
31
|
+
className={(navData) => joinClassNames(
|
|
32
|
+
menuType === menuTypes.VERTICAL ? 'vertical-menu__link-title' : 'menu-item__link-title',
|
|
33
|
+
(currentMenuItem?.parentLinks?.includes(menuItem.link ?? '') || navData.isActive)
|
|
34
|
+
&& (currentMenuItem?.children?.length ? 'menu-item--selected_parent' : 'menu-item--selected')
|
|
35
|
+
)}
|
|
36
|
+
end
|
|
37
|
+
to={menuItem.link}
|
|
38
|
+
ref={navLinkRef}
|
|
39
|
+
>
|
|
40
|
+
{menuItem.title}
|
|
41
|
+
</NavLink>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
<span className="menu-chiclet" />
|
|
45
|
+
</span>
|
|
46
|
+
{
|
|
47
|
+
menuItem.children?.length
|
|
48
|
+
? (
|
|
49
|
+
<ul role="menu" className={menuType === menuTypes.VERTICAL ? 'vertical-menu' : ''}>
|
|
50
|
+
{menuItem.children.map((menuItemChild) => (
|
|
51
|
+
<MenuItemPlain
|
|
52
|
+
key={`menu-item__child__${menuItemChild.link}-${menuItemChild.title}}`}
|
|
53
|
+
menuItem={menuItemChild}
|
|
54
|
+
menuType={menuType}
|
|
55
|
+
/>
|
|
56
|
+
))}
|
|
57
|
+
</ul>
|
|
58
|
+
)
|
|
59
|
+
: null
|
|
60
|
+
}
|
|
61
|
+
</li>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -30,7 +30,7 @@ export function useDebounceFunc(func, delay = 1000) {
|
|
|
30
30
|
clearTimeout(timeoutRef.current);
|
|
31
31
|
timeoutRef.current = window.setTimeout(
|
|
32
32
|
() => {
|
|
33
|
-
func(lastVarArgsRef.current);
|
|
33
|
+
func(...(lastVarArgsRef.current || []));
|
|
34
34
|
lastVarArgsRef.current = null;
|
|
35
35
|
lastInvocationRef.current = NaN;
|
|
36
36
|
resolve(varArgs);
|
|
@@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from 'react';
|
|
|
3
3
|
/**
|
|
4
4
|
* @template KeyboardEventHandlerT
|
|
5
5
|
* @param {object} params
|
|
6
|
-
* @param {string} params.whichKeyCode
|
|
6
|
+
* @param {string} params.whichKeyCode https://www.w3.org/TR/uievents-key/#named-key-attribute-values
|
|
7
7
|
* @param {import('react').KeyboardEventHandler<KeyboardEventHandlerT>} [params.onKeyDown]
|
|
8
8
|
* @param {import('react').KeyboardEventHandler<KeyboardEventHandlerT>} [params.onKeyUp]
|
|
9
9
|
* @returns {boolean}
|