@servicetitan/navigation 8.2.0 → 9.0.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.
- package/dist/components/counter-tag.d.ts +2 -4
- package/dist/components/counter-tag.d.ts.map +1 -1
- package/dist/components/counter-tag.js +3 -4
- package/dist/components/counter-tag.js.map +1 -1
- package/dist/components/header-navigation/header-navigation-content.d.ts +2 -2
- package/dist/components/header-navigation/header-navigation-content.d.ts.map +1 -1
- package/dist/components/header-navigation/header-navigation-content.js +4 -4
- package/dist/components/header-navigation/header-navigation-content.js.map +1 -1
- package/dist/components/header-navigation/header-navigation-extra.stories.js +3 -3
- package/dist/components/header-navigation/header-navigation-extra.stories.js.map +1 -1
- package/dist/components/header-navigation/header-navigation-links.d.ts.map +1 -1
- package/dist/components/header-navigation/header-navigation-links.js +4 -4
- package/dist/components/header-navigation/header-navigation-links.js.map +1 -1
- package/dist/components/header-navigation/header-navigation-stacked.stories.js +1 -1
- package/dist/components/header-navigation/header-navigation-stacked.stories.js.map +1 -1
- package/dist/components/header-navigation/header-navigation.module.less +2 -1
- package/dist/components/header-navigation/header-navigation.stories.js +1 -1
- package/dist/components/header-navigation/header-navigation.stories.js.map +1 -1
- package/dist/components/layout.stories.d.ts.map +1 -1
- package/dist/components/layout.stories.js +2 -1
- package/dist/components/layout.stories.js.map +1 -1
- package/dist/components/left-navigation/header-navigation-tiny-links.d.ts +1 -1
- package/dist/components/left-navigation/header-navigation-tiny-links.d.ts.map +1 -1
- package/dist/components/left-navigation/header-navigation-tiny-links.js +6 -6
- package/dist/components/left-navigation/header-navigation-tiny-links.js.map +1 -1
- package/dist/components/left-navigation/header-navigation-tiny.stories.js +2 -2
- package/dist/components/left-navigation/header-navigation-tiny.stories.js.map +1 -1
- package/dist/components/left-navigation/side-navigation.d.ts +8 -4
- package/dist/components/left-navigation/side-navigation.d.ts.map +1 -1
- package/dist/components/left-navigation/side-navigation.js +33 -20
- package/dist/components/left-navigation/side-navigation.js.map +1 -1
- package/dist/components/left-navigation/side-navigation.module.less +25 -13
- package/dist/components/left-navigation/side-navigation.stories.d.ts.map +1 -1
- package/dist/components/left-navigation/side-navigation.stories.js +6 -5
- package/dist/components/left-navigation/side-navigation.stories.js.map +1 -1
- package/dist/components/profile-dropdown/profile-dropdown.d.ts +1 -1
- package/dist/components/profile-dropdown/profile-dropdown.d.ts.map +1 -1
- package/dist/components/profile-dropdown/profile-dropdown.js +3 -3
- package/dist/components/profile-dropdown/profile-dropdown.js.map +1 -1
- package/dist/test/data.d.ts +1 -0
- package/dist/test/data.d.ts.map +1 -1
- package/dist/test/data.js +23 -7
- package/dist/test/data.js.map +1 -1
- package/dist/utils/navigation.d.ts +9 -5
- package/dist/utils/navigation.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/components/counter-tag.tsx +12 -9
- package/src/components/header-navigation/header-navigation-content.tsx +7 -7
- package/src/components/header-navigation/header-navigation-extra.stories.tsx +3 -3
- package/src/components/header-navigation/header-navigation-links.tsx +6 -4
- package/src/components/header-navigation/header-navigation-stacked.stories.tsx +2 -2
- package/src/components/header-navigation/header-navigation.module.less +2 -1
- package/src/components/header-navigation/header-navigation.stories.tsx +2 -2
- package/src/components/layout.stories.tsx +5 -2
- package/src/components/left-navigation/header-navigation-tiny-links.tsx +8 -8
- package/src/components/left-navigation/header-navigation-tiny.stories.tsx +3 -3
- package/src/components/left-navigation/side-navigation.module.less +25 -13
- package/src/components/left-navigation/side-navigation.module.less.d.ts +2 -0
- package/src/components/left-navigation/side-navigation.stories.tsx +10 -7
- package/src/components/left-navigation/side-navigation.tsx +71 -42
- package/src/components/profile-dropdown/profile-dropdown.tsx +6 -9
- package/src/test/data.tsx +27 -11
- package/src/utils/navigation.ts +10 -5
|
@@ -12,14 +12,16 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
|
|
|
12
12
|
to,
|
|
13
13
|
hint,
|
|
14
14
|
tooltip,
|
|
15
|
-
counter,
|
|
16
15
|
className,
|
|
16
|
+
icon,
|
|
17
|
+
iconActive,
|
|
17
18
|
iconClassName,
|
|
18
19
|
iconComponent,
|
|
19
20
|
iconName,
|
|
20
21
|
isActive,
|
|
21
22
|
label,
|
|
22
23
|
labelClassName,
|
|
24
|
+
tag,
|
|
23
25
|
target,
|
|
24
26
|
...rest
|
|
25
27
|
}) => {
|
|
@@ -41,7 +43,7 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
|
|
|
41
43
|
target={target}
|
|
42
44
|
>
|
|
43
45
|
<HeaderNavigationItemContent
|
|
44
|
-
|
|
46
|
+
tag={tag}
|
|
45
47
|
iconComponent={iconComponent}
|
|
46
48
|
iconClassName={iconClassName}
|
|
47
49
|
iconName={iconName}
|
|
@@ -57,7 +59,6 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
|
|
|
57
59
|
export const HeaderNavigationTrigger: FC<HeaderNavigationTriggerProps> = ({
|
|
58
60
|
id,
|
|
59
61
|
className,
|
|
60
|
-
counter,
|
|
61
62
|
iconClassName,
|
|
62
63
|
iconComponent,
|
|
63
64
|
iconName,
|
|
@@ -65,6 +66,7 @@ export const HeaderNavigationTrigger: FC<HeaderNavigationTriggerProps> = ({
|
|
|
65
66
|
label,
|
|
66
67
|
labelClassName,
|
|
67
68
|
hint,
|
|
69
|
+
tag,
|
|
68
70
|
tooltip,
|
|
69
71
|
...rest
|
|
70
72
|
}) => {
|
|
@@ -85,7 +87,7 @@ export const HeaderNavigationTrigger: FC<HeaderNavigationTriggerProps> = ({
|
|
|
85
87
|
)}
|
|
86
88
|
>
|
|
87
89
|
<HeaderNavigationItemContent
|
|
88
|
-
|
|
90
|
+
tag={tag}
|
|
89
91
|
iconComponent={iconComponent}
|
|
90
92
|
iconClassName={iconClassName}
|
|
91
93
|
iconName={iconName}
|
|
@@ -96,7 +96,7 @@ export const WithAllMonolithData = () => (
|
|
|
96
96
|
<HeaderNavigationTrigger
|
|
97
97
|
id="dialpad"
|
|
98
98
|
iconName="phone"
|
|
99
|
-
|
|
99
|
+
tag={2}
|
|
100
100
|
icon={SvgIcon}
|
|
101
101
|
iconActive={SvgIcon}
|
|
102
102
|
/>
|
|
@@ -122,7 +122,7 @@ export const WithAllMonolithData = () => (
|
|
|
122
122
|
/>
|
|
123
123
|
|
|
124
124
|
<ProfileDropdown>
|
|
125
|
-
<ProfileDropdown.Link id="tasks" to="https://googgle.com"
|
|
125
|
+
<ProfileDropdown.Link id="tasks" to="https://googgle.com" tag={10}>
|
|
126
126
|
Task Management
|
|
127
127
|
</ProfileDropdown.Link>
|
|
128
128
|
<ProfileDropdown.Divider />
|
|
@@ -219,7 +219,8 @@
|
|
|
219
219
|
.navigation-item-counter {
|
|
220
220
|
color: @color-white;
|
|
221
221
|
font-weight: @font-weight-semibold;
|
|
222
|
-
min-width: 12px;
|
|
222
|
+
min-width: 12px !important;
|
|
223
|
+
min-height: 12px !important;
|
|
223
224
|
}
|
|
224
225
|
|
|
225
226
|
&:not(.navigation-item-overflow) .navigation-item-counter {
|
|
@@ -108,7 +108,7 @@ export const WithAllMonolithData = () => (
|
|
|
108
108
|
<HeaderNavigationTrigger
|
|
109
109
|
id="dialpad"
|
|
110
110
|
iconName="phone"
|
|
111
|
-
|
|
111
|
+
tag={2}
|
|
112
112
|
icon={SvgIcon}
|
|
113
113
|
iconActive={SvgIcon}
|
|
114
114
|
/>
|
|
@@ -145,7 +145,7 @@ export const WithAllMonolithData = () => (
|
|
|
145
145
|
/>
|
|
146
146
|
|
|
147
147
|
<ProfileDropdown>
|
|
148
|
-
<ProfileDropdown.Link id="tasks" to="https://googgle.com"
|
|
148
|
+
<ProfileDropdown.Link id="tasks" to="https://googgle.com" tag={10}>
|
|
149
149
|
Task Management
|
|
150
150
|
</ProfileDropdown.Link>
|
|
151
151
|
<ProfileDropdown.Divider />
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Page, Sidebar } from '@servicetitan/design-system';
|
|
1
2
|
import { LocationInfo, withAnvil, withMemoryRouter } from '../test/data';
|
|
2
3
|
import {
|
|
3
4
|
WithAllMonolithData,
|
|
@@ -57,8 +58,10 @@ export const LeftNavLayoutSubmenu = () => {
|
|
|
57
58
|
<WithAllMonolithDataCommercial />
|
|
58
59
|
<div className="flex-grow-1 flex-basis-0 d-f">
|
|
59
60
|
<SideNavigationWithSubmenu />
|
|
60
|
-
<div className="flex-grow-1 flex-basis-0
|
|
61
|
-
<
|
|
61
|
+
<div className="flex-grow-1 flex-basis-0">
|
|
62
|
+
<Page sidebar={<Sidebar localStorageKey="undefined">sidebar</Sidebar>}>
|
|
63
|
+
<LocationInfo />
|
|
64
|
+
</Page>
|
|
62
65
|
</div>
|
|
63
66
|
</div>
|
|
64
67
|
</div>
|
|
@@ -9,13 +9,13 @@ import { withTooltip } from './with-tooltip';
|
|
|
9
9
|
|
|
10
10
|
/** Content for navigation items */
|
|
11
11
|
export const HeaderNavigationItemContent: FC<{
|
|
12
|
-
|
|
12
|
+
tag?: CounterTagPropsType;
|
|
13
13
|
counterClassName?: string;
|
|
14
14
|
icon: IconProps['svg'] | undefined;
|
|
15
15
|
iconActive: IconProps['svg'] | undefined;
|
|
16
16
|
label?: string;
|
|
17
17
|
labelClassName?: string;
|
|
18
|
-
}> = ({
|
|
18
|
+
}> = ({ counterClassName, icon, iconActive, label, labelClassName, tag }) => {
|
|
19
19
|
return (
|
|
20
20
|
<Fragment>
|
|
21
21
|
{!!icon && <Icon svg={icon} className={Styles.navigationIcon} />}
|
|
@@ -32,9 +32,9 @@ export const HeaderNavigationItemContent: FC<{
|
|
|
32
32
|
</span>
|
|
33
33
|
)}
|
|
34
34
|
|
|
35
|
-
{!!
|
|
35
|
+
{!!tag && (
|
|
36
36
|
<CounterTag
|
|
37
|
-
data={
|
|
37
|
+
data={tag}
|
|
38
38
|
className={classNames(Styles.navigationItemCounter, counterClassName)}
|
|
39
39
|
longClassName={Styles.navigationItemCounterLong}
|
|
40
40
|
/>
|
|
@@ -49,13 +49,13 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
|
|
|
49
49
|
to,
|
|
50
50
|
hint,
|
|
51
51
|
tooltip,
|
|
52
|
-
counter,
|
|
53
52
|
className,
|
|
54
53
|
icon,
|
|
55
54
|
iconActive,
|
|
56
55
|
isActive,
|
|
57
56
|
label,
|
|
58
57
|
labelClassName,
|
|
58
|
+
tag,
|
|
59
59
|
target,
|
|
60
60
|
...rest
|
|
61
61
|
}) => {
|
|
@@ -78,7 +78,7 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
|
|
|
78
78
|
target={target}
|
|
79
79
|
>
|
|
80
80
|
<HeaderNavigationItemContent
|
|
81
|
-
|
|
81
|
+
tag={tag}
|
|
82
82
|
icon={icon}
|
|
83
83
|
iconActive={iconActive}
|
|
84
84
|
label={label}
|
|
@@ -93,13 +93,13 @@ export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
|
|
|
93
93
|
export const HeaderNavigationTrigger: FC<HeaderNavigationTriggerProps> = ({
|
|
94
94
|
id,
|
|
95
95
|
className,
|
|
96
|
-
counter,
|
|
97
96
|
icon,
|
|
98
97
|
iconActive,
|
|
99
98
|
isActive,
|
|
100
99
|
hint,
|
|
101
100
|
label,
|
|
102
101
|
labelClassName,
|
|
102
|
+
tag,
|
|
103
103
|
tooltip,
|
|
104
104
|
title,
|
|
105
105
|
titleClassName,
|
|
@@ -122,7 +122,7 @@ export const HeaderNavigationTrigger: FC<HeaderNavigationTriggerProps> = ({
|
|
|
122
122
|
)}
|
|
123
123
|
>
|
|
124
124
|
<HeaderNavigationItemContent
|
|
125
|
-
|
|
125
|
+
tag={tag}
|
|
126
126
|
icon={icon}
|
|
127
127
|
iconActive={iconActive}
|
|
128
128
|
label={label}
|
|
@@ -86,7 +86,7 @@ export const WithAllMonolithData = () => (
|
|
|
86
86
|
/>
|
|
87
87
|
|
|
88
88
|
<ProfileDropdown>
|
|
89
|
-
<ProfileDropdown.Link id="tasks" to="https://googgle.com"
|
|
89
|
+
<ProfileDropdown.Link id="tasks" to="https://googgle.com" tag={10}>
|
|
90
90
|
Task Management
|
|
91
91
|
</ProfileDropdown.Link>
|
|
92
92
|
<ProfileDropdown.Divider />
|
|
@@ -107,7 +107,7 @@ export const WithAllMonolithData = () => (
|
|
|
107
107
|
|
|
108
108
|
export const WithAllMonolithDataCommercial = () => (
|
|
109
109
|
<HeaderNavigationTiny
|
|
110
|
-
className="border-bottom"
|
|
110
|
+
className="border-bottom z-global-nav"
|
|
111
111
|
left={
|
|
112
112
|
<Flex alignItems="center">
|
|
113
113
|
<LogoTitan mantleFill="#2270EE" className="p-x-half" size={48} />
|
|
@@ -152,7 +152,7 @@ export const WithAllMonolithDataCommercial = () => (
|
|
|
152
152
|
/>
|
|
153
153
|
|
|
154
154
|
<ProfileDropdown>
|
|
155
|
-
<ProfileDropdown.Link id="tasks" to="https://googgle.com"
|
|
155
|
+
<ProfileDropdown.Link id="tasks" to="https://googgle.com" tag={10}>
|
|
156
156
|
Task Management
|
|
157
157
|
</ProfileDropdown.Link>
|
|
158
158
|
<ProfileDropdown.Divider />
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
@text-color: var(--colorsTextInverted, @color-white);
|
|
5
5
|
@text-color-active: var(--colorsTextPrimarySubdued, @color-blue-300);
|
|
6
6
|
@border-color: var(--colorsTextOnGrey, @color-neutral-200);
|
|
7
|
-
@bg-color:
|
|
7
|
+
@bg-color: #0f1d26;
|
|
8
8
|
@bg-color-hover: rgba(255, 255, 255, 0.08);
|
|
9
9
|
@bg-color-active: rgba(120, 187, 250, 0.2);
|
|
10
10
|
|
|
@@ -51,6 +51,10 @@
|
|
|
51
51
|
margin: 2px @spacing-half @spacing-0;
|
|
52
52
|
text-align: center;
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
.navigation-item-text.navigation-item-text-small {
|
|
56
|
+
font-size: 10.5px;
|
|
57
|
+
}
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
.options-item {
|
|
@@ -69,7 +73,6 @@
|
|
|
69
73
|
|
|
70
74
|
.navigation-item {
|
|
71
75
|
flex-direction: row;
|
|
72
|
-
margin-bottom: @spacing-half;
|
|
73
76
|
margin-left: @spacing-1;
|
|
74
77
|
margin-right: @spacing-1;
|
|
75
78
|
|
|
@@ -81,9 +84,13 @@
|
|
|
81
84
|
.navigation-item-text {
|
|
82
85
|
font-family: @base-font-family;
|
|
83
86
|
font-size: @typescale-3;
|
|
84
|
-
padding-left: @spacing-
|
|
87
|
+
padding-left: @spacing-1;
|
|
85
88
|
flex: 1;
|
|
86
89
|
}
|
|
90
|
+
|
|
91
|
+
.navigation-item-counter {
|
|
92
|
+
margin-right: @spacing-1;
|
|
93
|
+
}
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
.options-item {
|
|
@@ -185,6 +192,8 @@
|
|
|
185
192
|
.navigation-item-counter {
|
|
186
193
|
color: @color-white;
|
|
187
194
|
font-weight: @font-weight-semibold;
|
|
195
|
+
min-width: 12px !important;
|
|
196
|
+
min-height: 12px !important;
|
|
188
197
|
}
|
|
189
198
|
|
|
190
199
|
.navigation-item-group-toggle[data-anv][data-anv] {
|
|
@@ -211,16 +220,6 @@
|
|
|
211
220
|
margin-bottom: @spacing-1;
|
|
212
221
|
position: relative;
|
|
213
222
|
|
|
214
|
-
&:before {
|
|
215
|
-
content: '';
|
|
216
|
-
position: absolute;
|
|
217
|
-
border-left: 1px solid @color-neutral-100;
|
|
218
|
-
width: 1px;
|
|
219
|
-
top: @spacing-2;
|
|
220
|
-
bottom: @spacing-1;
|
|
221
|
-
left: 0;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
223
|
.submenu-group-header[data-anv][data-anv] {
|
|
225
224
|
padding-top: @spacing-2;
|
|
226
225
|
padding-bottom: @spacing-half;
|
|
@@ -301,12 +300,25 @@
|
|
|
301
300
|
color: @text-color;
|
|
302
301
|
font-size: @typescale-2;
|
|
303
302
|
border-radius: @border-radius-2;
|
|
303
|
+
display: flex;
|
|
304
|
+
flex-direction: row;
|
|
305
|
+
justify-content: space-between;
|
|
306
|
+
align-items: center;
|
|
304
307
|
}
|
|
305
308
|
|
|
306
309
|
.submenu-link-active {
|
|
307
310
|
color: @text-color-active;
|
|
308
311
|
}
|
|
309
312
|
|
|
313
|
+
.submenu-link-counter {
|
|
314
|
+
min-height: 19px !important;
|
|
315
|
+
min-width: 19px !important;
|
|
316
|
+
|
|
317
|
+
> span:first-child {
|
|
318
|
+
color: @color-black;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
310
322
|
.submenu-link:hover:not(.submenu-link-active) {
|
|
311
323
|
background-color: @bg-color-hover;
|
|
312
324
|
}
|
|
@@ -10,6 +10,7 @@ export const navigationItemGroupToggle: string;
|
|
|
10
10
|
export const navigationItemIconSwitch: string;
|
|
11
11
|
export const navigationItemIconWrapper: string;
|
|
12
12
|
export const navigationItemText: string;
|
|
13
|
+
export const navigationItemTextSmall: string;
|
|
13
14
|
export const optionsIcon: string;
|
|
14
15
|
export const optionsIconWrapper: string;
|
|
15
16
|
export const optionsItem: string;
|
|
@@ -24,5 +25,6 @@ export const submenu: string;
|
|
|
24
25
|
export const submenuGroupHeader: string;
|
|
25
26
|
export const submenuLink: string;
|
|
26
27
|
export const submenuLinkActive: string;
|
|
28
|
+
export const submenuLinkCounter: string;
|
|
27
29
|
export const submenuPopover: string;
|
|
28
30
|
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import { Page, Sidebar } from '@servicetitan/design-system';
|
|
1
2
|
import { ComponentType, useState } from 'react';
|
|
2
3
|
import { LocationInfo, items, withAnvil, withMemoryRouter } from '../../test/data';
|
|
3
|
-
import { SideNavigation } from './';
|
|
4
|
+
import { SideNavigation, SideNavigationExpandedState } from './';
|
|
4
5
|
|
|
5
6
|
const layout = (Story: ComponentType) => {
|
|
6
7
|
return (
|
|
7
8
|
<div className="d-f border" style={{ height: '800px' }}>
|
|
8
9
|
<Story />
|
|
9
|
-
<div className="flex-grow-1 flex-basis-0
|
|
10
|
-
<
|
|
10
|
+
<div className="flex-grow-1 flex-basis-0">
|
|
11
|
+
<Page sidebar={<Sidebar localStorageKey="undefined">qq</Sidebar>}>
|
|
12
|
+
<LocationInfo />
|
|
13
|
+
</Page>
|
|
11
14
|
</div>
|
|
12
15
|
</div>
|
|
13
16
|
);
|
|
@@ -20,7 +23,7 @@ export default {
|
|
|
20
23
|
};
|
|
21
24
|
|
|
22
25
|
export const DefaultSideNavigation = () => {
|
|
23
|
-
const [expanded, setExpanded] = useState(
|
|
26
|
+
const [expanded, setExpanded] = useState<SideNavigationExpandedState | undefined>(undefined);
|
|
24
27
|
return (
|
|
25
28
|
<SideNavigation
|
|
26
29
|
expanded={expanded}
|
|
@@ -47,7 +50,7 @@ export const DefaultSideNavigation = () => {
|
|
|
47
50
|
};
|
|
48
51
|
|
|
49
52
|
export const SideNavigationLinksOnly = () => {
|
|
50
|
-
const [expanded, setExpanded] = useState(
|
|
53
|
+
const [expanded, setExpanded] = useState<SideNavigationExpandedState | undefined>(undefined);
|
|
51
54
|
return (
|
|
52
55
|
<SideNavigation
|
|
53
56
|
expanded={expanded}
|
|
@@ -73,7 +76,7 @@ export const SideNavigationLinksOnly = () => {
|
|
|
73
76
|
};
|
|
74
77
|
|
|
75
78
|
export const SideNavigationWithSubmenu = () => {
|
|
76
|
-
const [expanded, setExpanded] = useState(
|
|
79
|
+
const [expanded, setExpanded] = useState<SideNavigationExpandedState | undefined>(undefined);
|
|
77
80
|
return (
|
|
78
81
|
<SideNavigation
|
|
79
82
|
expanded={expanded}
|
|
@@ -87,7 +90,7 @@ export const SideNavigationWithSubmenu = () => {
|
|
|
87
90
|
items.accountingWithSubmenu,
|
|
88
91
|
items.purchasingWithSubmenu,
|
|
89
92
|
|
|
90
|
-
items.
|
|
93
|
+
items.followUpsWithSubmenu,
|
|
91
94
|
items.reports,
|
|
92
95
|
items.marketing,
|
|
93
96
|
items.priceBook,
|
|
@@ -14,12 +14,11 @@ import {
|
|
|
14
14
|
ReactElement,
|
|
15
15
|
useCallback,
|
|
16
16
|
useContext,
|
|
17
|
-
useState,
|
|
18
17
|
} from 'react';
|
|
19
18
|
import {
|
|
20
19
|
HeaderNavigationItemData,
|
|
21
|
-
HeaderNavigationItemLinkProps,
|
|
22
20
|
HeaderNavigationItemSubmenu,
|
|
21
|
+
HeaderNavigationItemSubmenuLink,
|
|
23
22
|
NavLinkComponentProps,
|
|
24
23
|
} from '../../utils/navigation';
|
|
25
24
|
import { NavigationComponentContext } from '../../utils/navigation-context';
|
|
@@ -27,6 +26,11 @@ import { CounterTag } from '../counter-tag';
|
|
|
27
26
|
import * as Styles from './side-navigation.module.less';
|
|
28
27
|
import { withTooltip } from './with-tooltip';
|
|
29
28
|
|
|
29
|
+
export interface SideNavigationExpandedState {
|
|
30
|
+
bar: boolean;
|
|
31
|
+
submenus?: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
export interface SideNavigationProps {
|
|
31
35
|
/** container class name */
|
|
32
36
|
className?: string;
|
|
@@ -37,9 +41,9 @@ export interface SideNavigationProps {
|
|
|
37
41
|
/** top navigation items */
|
|
38
42
|
itemsTop?: HeaderNavigationItemData[];
|
|
39
43
|
/** is menu expanded */
|
|
40
|
-
expanded?:
|
|
44
|
+
expanded?: SideNavigationExpandedState;
|
|
41
45
|
/** expand change handler */
|
|
42
|
-
onExpandedChange(expanded:
|
|
46
|
+
onExpandedChange?(expanded: SideNavigationExpandedState): void;
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
export const SideNavigation: FC<SideNavigationProps> = ({
|
|
@@ -55,7 +59,7 @@ export const SideNavigation: FC<SideNavigationProps> = ({
|
|
|
55
59
|
<div
|
|
56
60
|
className={classNames(
|
|
57
61
|
Styles.sideNav,
|
|
58
|
-
expanded ? Styles.sideNavExpanded : Styles.sideNavSlim,
|
|
62
|
+
expanded?.bar ? Styles.sideNavExpanded : Styles.sideNavSlim,
|
|
59
63
|
className
|
|
60
64
|
)}
|
|
61
65
|
id={id}
|
|
@@ -82,6 +86,7 @@ export const SideNavigation: FC<SideNavigationProps> = ({
|
|
|
82
86
|
<SideNavigationGroupItem
|
|
83
87
|
key={item.id}
|
|
84
88
|
expanded={expanded}
|
|
89
|
+
onExpandedChange={onExpandedChange}
|
|
85
90
|
navigationComponent={NavigationComponent}
|
|
86
91
|
{...item}
|
|
87
92
|
/>
|
|
@@ -111,18 +116,14 @@ interface NavigationComponentProps {
|
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
interface SideNavigationItemProps extends HeaderNavigationItemData, NavigationComponentProps {
|
|
114
|
-
expanded?:
|
|
115
|
-
submenuExpanded?: boolean;
|
|
119
|
+
expanded?: SideNavigationExpandedState;
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
/** Side Navigation menu item */
|
|
119
123
|
const SideNavigationItem: FC<SideNavigationItemProps> = ({
|
|
120
124
|
id,
|
|
121
|
-
submenuExpanded,
|
|
122
125
|
to,
|
|
123
126
|
title,
|
|
124
|
-
hint,
|
|
125
|
-
counter,
|
|
126
127
|
className,
|
|
127
128
|
iconClassName,
|
|
128
129
|
iconComponent: IconComponent,
|
|
@@ -130,10 +131,12 @@ const SideNavigationItem: FC<SideNavigationItemProps> = ({
|
|
|
130
131
|
iconActive,
|
|
131
132
|
isActive,
|
|
132
133
|
navigationComponent: NavigationComponent,
|
|
134
|
+
tag,
|
|
133
135
|
expanded,
|
|
136
|
+
submenu,
|
|
134
137
|
}) => {
|
|
135
138
|
const iconSwitch = !!icon && !!iconActive && !IconComponent;
|
|
136
|
-
const hasSubmenu =
|
|
139
|
+
const hasSubmenu = !!submenu;
|
|
137
140
|
|
|
138
141
|
return (
|
|
139
142
|
<NavigationComponent
|
|
@@ -141,7 +144,6 @@ const SideNavigationItem: FC<SideNavigationItemProps> = ({
|
|
|
141
144
|
data-pendo={`navigation-item-${id}`}
|
|
142
145
|
key={id}
|
|
143
146
|
to={to}
|
|
144
|
-
title={hint}
|
|
145
147
|
className={classNames(Styles.navigationItem, className, {
|
|
146
148
|
[Styles.navigationItemActive]: isActive === true,
|
|
147
149
|
[Styles.navigationItemIconSwitch]: iconSwitch,
|
|
@@ -179,46 +181,68 @@ const SideNavigationItem: FC<SideNavigationItemProps> = ({
|
|
|
179
181
|
</Fragment>
|
|
180
182
|
)}
|
|
181
183
|
|
|
182
|
-
{!!expanded && <div className={Styles.navigationItemText}>{title}</div>}
|
|
183
|
-
{!!
|
|
184
|
-
|
|
185
|
-
)}
|
|
186
|
-
{hasSubmenu && !!expanded && (
|
|
184
|
+
{!!expanded?.bar && <div className={Styles.navigationItemText}>{title}</div>}
|
|
185
|
+
{!!tag && <CounterTag data={tag} className={Styles.navigationItemCounter} />}
|
|
186
|
+
{hasSubmenu && !!expanded?.bar && (
|
|
187
187
|
<Icon
|
|
188
|
-
svg={
|
|
188
|
+
svg={expanded?.submenus?.includes(id) ? SvgGroupCollapse : SvgGroupExpand}
|
|
189
189
|
className={Styles.navigationItemGroupToggle}
|
|
190
190
|
/>
|
|
191
191
|
)}
|
|
192
192
|
</div>
|
|
193
193
|
|
|
194
|
-
{!expanded &&
|
|
194
|
+
{!expanded?.bar && (
|
|
195
|
+
<div
|
|
196
|
+
className={classNames(Styles.navigationItemText, {
|
|
197
|
+
[Styles.navigationItemTextSmall]: title.length >= 10,
|
|
198
|
+
})}
|
|
199
|
+
>
|
|
200
|
+
{title}
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
195
203
|
</NavigationComponent>
|
|
196
204
|
);
|
|
197
205
|
};
|
|
198
206
|
|
|
199
207
|
const submenuPopoverStyles = { '--background-color-strong': '#24323C' } as CSSProperties;
|
|
208
|
+
const getFirstSubmenuLinkTo = (
|
|
209
|
+
submenu: HeaderNavigationItemSubmenu | undefined,
|
|
210
|
+
defaultTo: string
|
|
211
|
+
) => submenu?.groups[0]?.links?.[0].to ?? defaultTo;
|
|
200
212
|
|
|
201
213
|
/** Side Navigation menu item */
|
|
202
|
-
const SideNavigationGroupItem: FC<
|
|
203
|
-
|
|
214
|
+
const SideNavigationGroupItem: FC<
|
|
215
|
+
SideNavigationItemProps & {
|
|
216
|
+
onExpandedChange: undefined | ((expanded: SideNavigationExpandedState) => void);
|
|
217
|
+
}
|
|
218
|
+
> = ({ onExpandedChange, ...props }) => {
|
|
204
219
|
const triggerClick = useCallback(
|
|
205
220
|
(e: MouseEvent<HTMLDivElement>) => {
|
|
206
221
|
e.stopPropagation();
|
|
207
222
|
e.preventDefault();
|
|
208
223
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
224
|
+
const isSubmenuExpanded = props.expanded?.submenus?.includes(props.id);
|
|
225
|
+
onExpandedChange?.({
|
|
226
|
+
bar: !!props.expanded?.bar,
|
|
227
|
+
submenus: [
|
|
228
|
+
...(props.expanded?.submenus?.filter(id => id !== props.id) ?? []),
|
|
229
|
+
...(isSubmenuExpanded ? [] : [props.id]),
|
|
230
|
+
],
|
|
231
|
+
});
|
|
212
232
|
},
|
|
213
|
-
[props.expanded]
|
|
233
|
+
[props.id, props.expanded, onExpandedChange]
|
|
214
234
|
);
|
|
215
235
|
|
|
216
|
-
|
|
236
|
+
const tag = props.submenu?.groups.some(group => group.links.some(link => !!link.tag))
|
|
237
|
+
? true
|
|
238
|
+
: props.tag;
|
|
239
|
+
|
|
240
|
+
return props.expanded?.bar ? (
|
|
217
241
|
<Fragment>
|
|
218
242
|
<div onClickCapture={triggerClick}>
|
|
219
|
-
<SideNavigationItem {...props}
|
|
243
|
+
<SideNavigationItem {...props} tag={tag} />
|
|
220
244
|
</div>
|
|
221
|
-
<Collapsible open={
|
|
245
|
+
<Collapsible open={props.expanded?.submenus?.includes(props.id)} animate>
|
|
222
246
|
<div className={Styles.submenu}>
|
|
223
247
|
<SideNavigationGroupContent
|
|
224
248
|
groups={props.submenu?.groups ?? []}
|
|
@@ -231,12 +255,16 @@ const SideNavigationGroupItem: FC<SideNavigationItemProps> = ({ ...props }) => {
|
|
|
231
255
|
<Popover placement="right-start" openOnHover>
|
|
232
256
|
<Popover.Trigger>
|
|
233
257
|
{(triggerProps: PopoverTriggerProps) => (
|
|
234
|
-
<div {...triggerProps}
|
|
235
|
-
<SideNavigationItem
|
|
258
|
+
<div {...triggerProps}>
|
|
259
|
+
<SideNavigationItem
|
|
260
|
+
{...props}
|
|
261
|
+
to={getFirstSubmenuLinkTo(props.submenu, props.to)}
|
|
262
|
+
tag={tag}
|
|
263
|
+
/>
|
|
236
264
|
</div>
|
|
237
265
|
)}
|
|
238
266
|
</Popover.Trigger>
|
|
239
|
-
<Popover.Content style={submenuPopoverStyles}>
|
|
267
|
+
<Popover.Content style={submenuPopoverStyles} className="z-global-nav-i">
|
|
240
268
|
<div className={Styles.submenuPopover}>
|
|
241
269
|
<Headline size="small" className="c-white m-b-half-i m-t-1">
|
|
242
270
|
{props.title}
|
|
@@ -256,17 +284,14 @@ const SideNavigationGroupContent: FC<HeaderNavigationItemSubmenu & NavigationCom
|
|
|
256
284
|
}) => {
|
|
257
285
|
return (
|
|
258
286
|
<Fragment>
|
|
259
|
-
{groups.reduce((out, group) => {
|
|
287
|
+
{groups.reduce((out, group, index) => {
|
|
260
288
|
if (!group.links.length) {
|
|
261
289
|
return out;
|
|
262
290
|
}
|
|
263
291
|
|
|
292
|
+
const key = `:group:${index}:title`;
|
|
264
293
|
out.push(
|
|
265
|
-
<Text
|
|
266
|
-
key=":group:title"
|
|
267
|
-
variant="eyebrow"
|
|
268
|
-
className={Styles.submenuGroupHeader}
|
|
269
|
-
>
|
|
294
|
+
<Text key={key} variant="eyebrow" className={Styles.submenuGroupHeader}>
|
|
270
295
|
{group.title}
|
|
271
296
|
</Text>
|
|
272
297
|
);
|
|
@@ -285,8 +310,9 @@ const SideNavigationGroupContent: FC<HeaderNavigationItemSubmenu & NavigationCom
|
|
|
285
310
|
</Fragment>
|
|
286
311
|
);
|
|
287
312
|
};
|
|
288
|
-
const SideNavigationGroupLink: FC<
|
|
313
|
+
const SideNavigationGroupLink: FC<HeaderNavigationItemSubmenuLink & NavigationComponentProps> = ({
|
|
289
314
|
id,
|
|
315
|
+
tag,
|
|
290
316
|
title,
|
|
291
317
|
to,
|
|
292
318
|
isActive,
|
|
@@ -304,22 +330,25 @@ const SideNavigationGroupLink: FC<HeaderNavigationItemLinkProps & NavigationComp
|
|
|
304
330
|
isActive={typeof isActive === 'function' ? isActive : undefined}
|
|
305
331
|
activeClassName={Styles.submenuLinkActive}
|
|
306
332
|
>
|
|
307
|
-
{title}
|
|
333
|
+
<span>{title}</span>
|
|
334
|
+
{!!tag && <CounterTag data={tag} className={Styles.submenuLinkCounter} />}
|
|
308
335
|
</NavigationComponent>
|
|
309
336
|
);
|
|
310
337
|
};
|
|
311
338
|
|
|
312
339
|
/** Side Navigation options toggle */
|
|
313
340
|
export const SideNavigationOptionsToggle: FC<{
|
|
314
|
-
expanded?:
|
|
315
|
-
onExpandedChange(expanded:
|
|
341
|
+
expanded?: SideNavigationExpandedState;
|
|
342
|
+
onExpandedChange?(expanded: SideNavigationExpandedState): void;
|
|
316
343
|
}> = ({ expanded, onExpandedChange }) =>
|
|
317
344
|
withTooltip(
|
|
318
345
|
<div
|
|
319
346
|
data-cy="navigation-left-options"
|
|
320
347
|
data-pendo="navigation-left-options"
|
|
321
348
|
className={classNames(Styles.optionsItem)}
|
|
322
|
-
onClick={() =>
|
|
349
|
+
onClick={() =>
|
|
350
|
+
onExpandedChange?.({ bar: !expanded?.bar, submenus: expanded?.submenus })
|
|
351
|
+
}
|
|
323
352
|
>
|
|
324
353
|
<div className={Styles.optionsIconWrapper}>
|
|
325
354
|
<Icon className={Styles.optionsIcon} svg={expanded ? SvgCollapse : SvgExpand} />
|