@servicetitan/navigation 9.0.1 → 9.1.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 +1 -4
- package/dist/components/counter-tag.d.ts.map +1 -1
- package/dist/components/counter-tag.js.map +1 -1
- package/dist/components/left-navigation/index.d.ts +2 -0
- package/dist/components/left-navigation/index.d.ts.map +1 -1
- package/dist/components/left-navigation/index.js +2 -0
- package/dist/components/left-navigation/index.js.map +1 -1
- package/dist/components/left-navigation/interface-internal.d.ts +10 -0
- package/dist/components/left-navigation/interface-internal.d.ts.map +1 -0
- package/dist/components/left-navigation/interface-internal.js +2 -0
- package/dist/components/left-navigation/interface-internal.js.map +1 -0
- package/dist/components/left-navigation/interface.d.ts +11 -0
- package/dist/components/left-navigation/interface.d.ts.map +1 -0
- package/dist/components/left-navigation/interface.js +2 -0
- package/dist/components/left-navigation/interface.js.map +1 -0
- package/dist/components/left-navigation/side-navigation-links-internal.d.ts +23 -0
- package/dist/components/left-navigation/side-navigation-links-internal.d.ts.map +1 -0
- package/dist/components/left-navigation/side-navigation-links-internal.js +29 -0
- package/dist/components/left-navigation/side-navigation-links-internal.js.map +1 -0
- package/dist/components/left-navigation/side-navigation-links.d.ts +7 -0
- package/dist/components/left-navigation/side-navigation-links.d.ts.map +1 -0
- package/dist/components/left-navigation/side-navigation-links.js +20 -0
- package/dist/components/left-navigation/side-navigation-links.js.map +1 -0
- package/dist/components/left-navigation/side-navigation.d.ts +3 -11
- package/dist/components/left-navigation/side-navigation.d.ts.map +1 -1
- package/dist/components/left-navigation/side-navigation.js +12 -26
- package/dist/components/left-navigation/side-navigation.js.map +1 -1
- package/dist/components/left-navigation/side-navigation.module.less +11 -4
- package/dist/components/left-navigation/side-navigation.stories.d.ts +1 -0
- package/dist/components/left-navigation/side-navigation.stories.d.ts.map +1 -1
- package/dist/components/left-navigation/side-navigation.stories.js +22 -3
- package/dist/components/left-navigation/side-navigation.stories.js.map +1 -1
- package/dist/utils/counter-tag.d.ts +8 -0
- package/dist/utils/counter-tag.d.ts.map +1 -0
- package/dist/utils/counter-tag.js +2 -0
- package/dist/utils/counter-tag.js.map +1 -0
- package/dist/utils/side-nav.d.ts +3 -0
- package/dist/utils/side-nav.d.ts.map +1 -0
- package/dist/utils/side-nav.js +27 -0
- package/dist/utils/side-nav.js.map +1 -0
- package/package.json +2 -2
- package/src/components/counter-tag.tsx +1 -5
- package/src/components/left-navigation/index.ts +2 -0
- package/src/components/left-navigation/interface-internal.ts +11 -0
- package/src/components/left-navigation/interface.ts +13 -0
- package/src/components/left-navigation/side-navigation-links-internal.tsx +128 -0
- package/src/components/left-navigation/side-navigation-links.tsx +39 -0
- package/src/components/left-navigation/side-navigation.module.less +11 -4
- package/src/components/left-navigation/side-navigation.module.less.d.ts +2 -1
- package/src/components/left-navigation/side-navigation.stories.tsx +56 -3
- package/src/components/left-navigation/side-navigation.tsx +29 -124
- package/src/utils/counter-tag.ts +11 -0
- package/src/utils/side-nav.ts +34 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { NavLinkComponentProps } from '../../utils/navigation';
|
|
3
|
+
import { SideNavigationExpandedState } from './interface';
|
|
4
|
+
|
|
5
|
+
export interface NavigationComponentProps {
|
|
6
|
+
navigationComponent: FC<NavLinkComponentProps>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface SideNavigationExpandedProps {
|
|
10
|
+
expanded?: SideNavigationExpandedState;
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { HeaderNavigationItemData } from '../../utils/navigation';
|
|
2
|
+
|
|
3
|
+
export interface SideNavigationExpandedState {
|
|
4
|
+
bar: boolean;
|
|
5
|
+
submenus?: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface SideNavigationLinkProps
|
|
9
|
+
extends Omit<HeaderNavigationItemData, 'iconName' | 'submenu'> {}
|
|
10
|
+
export interface SideNavigationTriggerProps
|
|
11
|
+
extends Omit<SideNavigationLinkProps, 'to' | 'isActive'> {
|
|
12
|
+
isActive?: boolean;
|
|
13
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Icon } from '@servicetitan/anvil2';
|
|
2
|
+
import SvgGroupCollapse from '@servicetitan/anvil2/assets/icons/material/round/expand_less.svg';
|
|
3
|
+
import SvgGroupExpand from '@servicetitan/anvil2/assets/icons/material/round/expand_more.svg';
|
|
4
|
+
|
|
5
|
+
import classNames from 'classnames';
|
|
6
|
+
import { FC, Fragment } from 'react';
|
|
7
|
+
import { HeaderNavigationItemData } from '../../utils/navigation';
|
|
8
|
+
import { CounterTag } from '../counter-tag';
|
|
9
|
+
import { SideNavigationTriggerProps } from './interface';
|
|
10
|
+
import { NavigationComponentProps, SideNavigationExpandedProps } from './interface-internal';
|
|
11
|
+
import * as Styles from './side-navigation.module.less';
|
|
12
|
+
|
|
13
|
+
export interface InternalSideNavigationItemContentProps
|
|
14
|
+
extends Omit<HeaderNavigationItemData, 'iconName' | 'to' | ''> {
|
|
15
|
+
submenuExpanded: boolean | undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const InternalSideNavigationItemContent: FC<InternalSideNavigationItemContentProps> = ({
|
|
19
|
+
icon,
|
|
20
|
+
iconActive,
|
|
21
|
+
iconClassName,
|
|
22
|
+
iconComponent: IconComponent,
|
|
23
|
+
tag,
|
|
24
|
+
title,
|
|
25
|
+
submenuExpanded,
|
|
26
|
+
}) => (
|
|
27
|
+
<Fragment>
|
|
28
|
+
<div className={Styles.navigationItemIconWrapper}>
|
|
29
|
+
{IconComponent ? (
|
|
30
|
+
<i className={classNames(Styles.navigationIcon, iconClassName)}>
|
|
31
|
+
<IconComponent />
|
|
32
|
+
</i>
|
|
33
|
+
) : (
|
|
34
|
+
<Fragment>
|
|
35
|
+
{icon && (
|
|
36
|
+
<Icon
|
|
37
|
+
svg={icon}
|
|
38
|
+
className={classNames(
|
|
39
|
+
Styles.navigationIcon,
|
|
40
|
+
Styles.navigationIconInactive,
|
|
41
|
+
iconClassName
|
|
42
|
+
)}
|
|
43
|
+
/>
|
|
44
|
+
)}
|
|
45
|
+
{iconActive && (
|
|
46
|
+
<Icon
|
|
47
|
+
svg={iconActive}
|
|
48
|
+
className={classNames(
|
|
49
|
+
Styles.navigationIcon,
|
|
50
|
+
Styles.navigationIconActive,
|
|
51
|
+
iconClassName
|
|
52
|
+
)}
|
|
53
|
+
/>
|
|
54
|
+
)}
|
|
55
|
+
</Fragment>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
<div className={Styles.navigationItemTextExpanded}>{title}</div>
|
|
59
|
+
{!!tag && <CounterTag data={tag} className={Styles.navigationItemCounter} />}
|
|
60
|
+
{typeof submenuExpanded === 'boolean' && (
|
|
61
|
+
<Icon
|
|
62
|
+
svg={submenuExpanded ? SvgGroupCollapse : SvgGroupExpand}
|
|
63
|
+
className={Styles.navigationItemGroupToggle}
|
|
64
|
+
/>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div
|
|
69
|
+
className={classNames(Styles.navigationItemTextCollapsed, {
|
|
70
|
+
[Styles.navigationItemTextSmall]: title.length >= 10,
|
|
71
|
+
})}
|
|
72
|
+
>
|
|
73
|
+
{title}
|
|
74
|
+
</div>
|
|
75
|
+
</Fragment>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
export interface InternalSideNavigationLinkProps
|
|
79
|
+
extends Omit<HeaderNavigationItemData, 'iconName'>,
|
|
80
|
+
NavigationComponentProps,
|
|
81
|
+
SideNavigationExpandedProps {
|
|
82
|
+
submenuExpanded: boolean | undefined;
|
|
83
|
+
dataPrefix?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const internalNavigationContentContainerProps = ({
|
|
87
|
+
className,
|
|
88
|
+
icon,
|
|
89
|
+
iconActive,
|
|
90
|
+
iconComponent,
|
|
91
|
+
id,
|
|
92
|
+
isActive,
|
|
93
|
+
prefix,
|
|
94
|
+
}: Omit<SideNavigationTriggerProps, 'isActive'> & { prefix: string; isActive?: any }) => ({
|
|
95
|
+
'data-cy': `${prefix}-${id}`,
|
|
96
|
+
'data-pendo': `${prefix}-${id}`,
|
|
97
|
+
'className': classNames(Styles.navigationItem, className, {
|
|
98
|
+
[Styles.navigationItemActive]: isActive === true,
|
|
99
|
+
[Styles.navigationItemIconSwitch]: !!icon && !!iconActive && !iconComponent,
|
|
100
|
+
}),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
/** Side Navigation menu item (for internal usage) */
|
|
104
|
+
export const InternalSideNavigationLink: FC<InternalSideNavigationLinkProps> = ({
|
|
105
|
+
to,
|
|
106
|
+
className,
|
|
107
|
+
dataPrefix,
|
|
108
|
+
isActive,
|
|
109
|
+
navigationComponent: NavigationComponent,
|
|
110
|
+
submenuExpanded,
|
|
111
|
+
...props
|
|
112
|
+
}) => {
|
|
113
|
+
return (
|
|
114
|
+
<NavigationComponent
|
|
115
|
+
{...internalNavigationContentContainerProps({
|
|
116
|
+
...props,
|
|
117
|
+
prefix: dataPrefix ?? 'navigation-item',
|
|
118
|
+
className,
|
|
119
|
+
isActive,
|
|
120
|
+
})}
|
|
121
|
+
to={to}
|
|
122
|
+
isActive={typeof isActive === 'function' ? isActive : undefined}
|
|
123
|
+
activeClassName={Styles.navigationItemActive}
|
|
124
|
+
>
|
|
125
|
+
<InternalSideNavigationItemContent submenuExpanded={submenuExpanded} {...props} />
|
|
126
|
+
</NavigationComponent>
|
|
127
|
+
);
|
|
128
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { FC, useContext } from 'react';
|
|
3
|
+
import { NavigationComponentContext } from '../../utils/navigation-context';
|
|
4
|
+
import { SideNavigationLinkProps, SideNavigationTriggerProps } from './interface';
|
|
5
|
+
import {
|
|
6
|
+
InternalSideNavigationItemContent,
|
|
7
|
+
InternalSideNavigationLink,
|
|
8
|
+
internalNavigationContentContainerProps,
|
|
9
|
+
} from './side-navigation-links-internal';
|
|
10
|
+
|
|
11
|
+
/** Side Navigation menu link */
|
|
12
|
+
export const SideNavigationLink: FC<SideNavigationLinkProps> = props => {
|
|
13
|
+
const NavigationComponent = useContext(NavigationComponentContext);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<InternalSideNavigationLink
|
|
17
|
+
{...props}
|
|
18
|
+
navigationComponent={NavigationComponent}
|
|
19
|
+
submenuExpanded={undefined}
|
|
20
|
+
dataPrefix="navigation-link"
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Side Navigation menu link */
|
|
26
|
+
export const SideNavigationTrigger: FC<SideNavigationTriggerProps> = props => {
|
|
27
|
+
return (
|
|
28
|
+
<div
|
|
29
|
+
{...internalNavigationContentContainerProps({
|
|
30
|
+
...props,
|
|
31
|
+
prefix: 'navigation-trigger',
|
|
32
|
+
className: classNames(props.className, 'cursor-pointer'),
|
|
33
|
+
isActive: props.isActive,
|
|
34
|
+
})}
|
|
35
|
+
>
|
|
36
|
+
<InternalSideNavigationItemContent submenuExpanded={undefined} {...props} />
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
@@ -44,7 +44,10 @@
|
|
|
44
44
|
padding: @spacing-half;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
.navigation-item-text {
|
|
47
|
+
.navigation-item-text-expanded {
|
|
48
|
+
display: none;
|
|
49
|
+
}
|
|
50
|
+
.navigation-item-text-collapsed {
|
|
48
51
|
font-family: @base-font-family;
|
|
49
52
|
font-size: 11px;
|
|
50
53
|
line-height: 11px;
|
|
@@ -52,7 +55,7 @@
|
|
|
52
55
|
text-align: center;
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
.navigation-item-text.navigation-item-text-small {
|
|
58
|
+
.navigation-item-text-collapsed.navigation-item-text-small {
|
|
56
59
|
font-size: 10.5px;
|
|
57
60
|
}
|
|
58
61
|
}
|
|
@@ -81,13 +84,17 @@
|
|
|
81
84
|
padding: @spacing-1 @spacing-half;
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
.navigation-item-text {
|
|
87
|
+
.navigation-item-text-expanded {
|
|
85
88
|
font-family: @base-font-family;
|
|
86
89
|
font-size: @typescale-3;
|
|
87
90
|
padding-left: @spacing-1;
|
|
88
91
|
flex: 1;
|
|
89
92
|
}
|
|
90
93
|
|
|
94
|
+
.navigation-item-text-collapsed {
|
|
95
|
+
display: none;
|
|
96
|
+
}
|
|
97
|
+
|
|
91
98
|
.navigation-item-counter {
|
|
92
99
|
margin-right: @spacing-1;
|
|
93
100
|
}
|
|
@@ -190,7 +197,7 @@
|
|
|
190
197
|
}
|
|
191
198
|
|
|
192
199
|
.navigation-item-counter {
|
|
193
|
-
color: @color-
|
|
200
|
+
color: @color-black;
|
|
194
201
|
font-weight: @font-weight-semibold;
|
|
195
202
|
min-width: 12px !important;
|
|
196
203
|
min-height: 12px !important;
|
|
@@ -9,7 +9,8 @@ export const navigationItemCounter: string;
|
|
|
9
9
|
export const navigationItemGroupToggle: string;
|
|
10
10
|
export const navigationItemIconSwitch: string;
|
|
11
11
|
export const navigationItemIconWrapper: string;
|
|
12
|
-
export const
|
|
12
|
+
export const navigationItemTextCollapsed: string;
|
|
13
|
+
export const navigationItemTextExpanded: string;
|
|
13
14
|
export const navigationItemTextSmall: string;
|
|
14
15
|
export const optionsIcon: string;
|
|
15
16
|
export const optionsIconWrapper: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { Popover } from '@servicetitan/anvil2';
|
|
1
2
|
import { Page } from '@servicetitan/design-system';
|
|
2
|
-
import { ComponentType, useState } from 'react';
|
|
3
|
+
import { ComponentType, Fragment, useState } from 'react';
|
|
3
4
|
import {
|
|
4
5
|
LocationInfo,
|
|
5
6
|
items,
|
|
@@ -7,7 +8,12 @@ import {
|
|
|
7
8
|
withDefaultRedirects,
|
|
8
9
|
withMemoryRouter,
|
|
9
10
|
} from '../../test/data';
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
SideNavigation,
|
|
13
|
+
SideNavigationExpandedState,
|
|
14
|
+
SideNavigationLink,
|
|
15
|
+
SideNavigationTrigger,
|
|
16
|
+
} from './';
|
|
11
17
|
|
|
12
18
|
const layout = (Story: ComponentType) => {
|
|
13
19
|
return (
|
|
@@ -50,7 +56,7 @@ export const DefaultSideNavigation = () => {
|
|
|
50
56
|
items.pointOfSale,
|
|
51
57
|
items.reports,
|
|
52
58
|
]}
|
|
53
|
-
itemsTop={
|
|
59
|
+
itemsTop={<SideNavigationLink {...items.tasks} />}
|
|
54
60
|
/>
|
|
55
61
|
);
|
|
56
62
|
};
|
|
@@ -106,3 +112,50 @@ export const SideNavigationWithSubmenu = () => {
|
|
|
106
112
|
/>
|
|
107
113
|
);
|
|
108
114
|
};
|
|
115
|
+
|
|
116
|
+
const SideLinkWithPopover = () => {
|
|
117
|
+
return (
|
|
118
|
+
<Popover placement="right" openOnHover>
|
|
119
|
+
<Popover.Trigger>
|
|
120
|
+
{props => (
|
|
121
|
+
<div {...props}>
|
|
122
|
+
<SideNavigationTrigger {...items.marketing} isActive={false} />
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
125
|
+
</Popover.Trigger>
|
|
126
|
+
<Popover.Content>popover content</Popover.Content>
|
|
127
|
+
</Popover>
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const SideNavigationWithCustomTopElements = () => {
|
|
132
|
+
const [expanded, setExpanded] = useState<SideNavigationExpandedState | undefined>(undefined);
|
|
133
|
+
return (
|
|
134
|
+
<SideNavigation
|
|
135
|
+
expanded={expanded}
|
|
136
|
+
onExpandedChange={setExpanded}
|
|
137
|
+
items={[
|
|
138
|
+
items.dashboard,
|
|
139
|
+
items.schedule,
|
|
140
|
+
items.dispatch,
|
|
141
|
+
|
|
142
|
+
items.accountingWithSubmenu,
|
|
143
|
+
items.purchasingWithSubmenu,
|
|
144
|
+
|
|
145
|
+
items.followUpsWithSubmenu,
|
|
146
|
+
items.reports,
|
|
147
|
+
items.marketing,
|
|
148
|
+
items.priceBook,
|
|
149
|
+
|
|
150
|
+
items.projects,
|
|
151
|
+
]}
|
|
152
|
+
itemsTop={
|
|
153
|
+
<Fragment>
|
|
154
|
+
<SideNavigationLink {...items.calls} />
|
|
155
|
+
<SideNavigationLink {...items.tasks} />
|
|
156
|
+
<SideLinkWithPopover />
|
|
157
|
+
</Fragment>
|
|
158
|
+
}
|
|
159
|
+
/>
|
|
160
|
+
);
|
|
161
|
+
};
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { Icon, Popover, PopoverTriggerProps, Text } from '@servicetitan/anvil2';
|
|
2
|
-
import SvgGroupCollapse from '@servicetitan/anvil2/assets/icons/material/round/expand_less.svg';
|
|
3
|
-
import SvgGroupExpand from '@servicetitan/anvil2/assets/icons/material/round/expand_more.svg';
|
|
4
2
|
import SvgCollapse from '@servicetitan/anvil2/assets/icons/st/gnav_menu_collapse.svg';
|
|
5
3
|
import SvgExpand from '@servicetitan/anvil2/assets/icons/st/gnav_menu_expand.svg';
|
|
6
4
|
import { Collapsible, Headline } from '@servicetitan/design-system';
|
|
@@ -12,6 +10,7 @@ import {
|
|
|
12
10
|
Fragment,
|
|
13
11
|
MouseEvent,
|
|
14
12
|
ReactElement,
|
|
13
|
+
ReactNode,
|
|
15
14
|
useCallback,
|
|
16
15
|
useContext,
|
|
17
16
|
} from 'react';
|
|
@@ -19,18 +18,16 @@ import {
|
|
|
19
18
|
HeaderNavigationItemData,
|
|
20
19
|
HeaderNavigationItemSubmenu,
|
|
21
20
|
HeaderNavigationItemSubmenuLink,
|
|
22
|
-
NavLinkComponentProps,
|
|
23
21
|
} from '../../utils/navigation';
|
|
24
22
|
import { NavigationComponentContext } from '../../utils/navigation-context';
|
|
23
|
+
import { getSubmenuGroupTag } from '../../utils/side-nav';
|
|
25
24
|
import { CounterTag } from '../counter-tag';
|
|
25
|
+
import { SideNavigationExpandedState } from './interface';
|
|
26
|
+
import { NavigationComponentProps, SideNavigationExpandedProps } from './interface-internal';
|
|
27
|
+
import { InternalSideNavigationLink } from './side-navigation-links-internal';
|
|
26
28
|
import * as Styles from './side-navigation.module.less';
|
|
27
29
|
import { withTooltip } from './with-tooltip';
|
|
28
30
|
|
|
29
|
-
export interface SideNavigationExpandedState {
|
|
30
|
-
bar: boolean;
|
|
31
|
-
submenus?: string[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
31
|
export interface SideNavigationProps {
|
|
35
32
|
/** container class name */
|
|
36
33
|
className?: string;
|
|
@@ -39,7 +36,7 @@ export interface SideNavigationProps {
|
|
|
39
36
|
/** main navigation items */
|
|
40
37
|
items?: HeaderNavigationItemData[];
|
|
41
38
|
/** top navigation items */
|
|
42
|
-
itemsTop?:
|
|
39
|
+
itemsTop?: ReactNode;
|
|
43
40
|
/** is menu expanded */
|
|
44
41
|
expanded?: SideNavigationExpandedState;
|
|
45
42
|
/** expand change handler */
|
|
@@ -65,17 +62,10 @@ export const SideNavigation: FC<SideNavigationProps> = ({
|
|
|
65
62
|
id={id}
|
|
66
63
|
data-cy="side-navigation"
|
|
67
64
|
>
|
|
68
|
-
{!!itemsTop
|
|
65
|
+
{!!itemsTop && (
|
|
69
66
|
<Fragment>
|
|
70
67
|
<div className={Styles.sideNavTop} data-cy="navigation-items-top">
|
|
71
|
-
{itemsTop
|
|
72
|
-
<SideNavigationItem
|
|
73
|
-
key={item.id}
|
|
74
|
-
expanded={expanded}
|
|
75
|
-
navigationComponent={NavigationComponent}
|
|
76
|
-
{...item}
|
|
77
|
-
/>
|
|
78
|
-
))}
|
|
68
|
+
{itemsTop}
|
|
79
69
|
</div>
|
|
80
70
|
<div className={Styles.divider} />
|
|
81
71
|
</Fragment>
|
|
@@ -91,9 +81,9 @@ export const SideNavigation: FC<SideNavigationProps> = ({
|
|
|
91
81
|
{...item}
|
|
92
82
|
/>
|
|
93
83
|
) : (
|
|
94
|
-
<
|
|
84
|
+
<InternalSideNavigationLink
|
|
95
85
|
key={item.id}
|
|
96
|
-
|
|
86
|
+
submenuExpanded={undefined}
|
|
97
87
|
navigationComponent={NavigationComponent}
|
|
98
88
|
{...item}
|
|
99
89
|
/>
|
|
@@ -111,113 +101,22 @@ export const SideNavigation: FC<SideNavigationProps> = ({
|
|
|
111
101
|
);
|
|
112
102
|
};
|
|
113
103
|
|
|
114
|
-
interface NavigationComponentProps {
|
|
115
|
-
navigationComponent: FC<NavLinkComponentProps>;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
interface SideNavigationItemProps extends HeaderNavigationItemData, NavigationComponentProps {
|
|
119
|
-
expanded?: SideNavigationExpandedState;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** Side Navigation menu item */
|
|
123
|
-
const SideNavigationItem: FC<SideNavigationItemProps> = ({
|
|
124
|
-
id,
|
|
125
|
-
to,
|
|
126
|
-
title,
|
|
127
|
-
className,
|
|
128
|
-
iconClassName,
|
|
129
|
-
iconComponent: IconComponent,
|
|
130
|
-
icon,
|
|
131
|
-
iconActive,
|
|
132
|
-
isActive,
|
|
133
|
-
navigationComponent: NavigationComponent,
|
|
134
|
-
tag,
|
|
135
|
-
expanded,
|
|
136
|
-
submenu,
|
|
137
|
-
}) => {
|
|
138
|
-
const iconSwitch = !!icon && !!iconActive && !IconComponent;
|
|
139
|
-
const hasSubmenu = !!submenu;
|
|
140
|
-
|
|
141
|
-
return (
|
|
142
|
-
<NavigationComponent
|
|
143
|
-
data-cy={`navigation-item-${id}`}
|
|
144
|
-
data-pendo={`navigation-item-${id}`}
|
|
145
|
-
key={id}
|
|
146
|
-
to={to}
|
|
147
|
-
className={classNames(Styles.navigationItem, className, {
|
|
148
|
-
[Styles.navigationItemActive]: isActive === true,
|
|
149
|
-
[Styles.navigationItemIconSwitch]: iconSwitch,
|
|
150
|
-
})}
|
|
151
|
-
isActive={typeof isActive === 'function' ? isActive : undefined}
|
|
152
|
-
activeClassName={Styles.navigationItemActive}
|
|
153
|
-
>
|
|
154
|
-
<div className={Styles.navigationItemIconWrapper}>
|
|
155
|
-
{IconComponent ? (
|
|
156
|
-
<i className={classNames(Styles.navigationIcon, iconClassName)}>
|
|
157
|
-
<IconComponent />
|
|
158
|
-
</i>
|
|
159
|
-
) : (
|
|
160
|
-
<Fragment>
|
|
161
|
-
{icon && (
|
|
162
|
-
<Icon
|
|
163
|
-
svg={icon}
|
|
164
|
-
className={classNames(
|
|
165
|
-
Styles.navigationIcon,
|
|
166
|
-
Styles.navigationIconInactive,
|
|
167
|
-
iconClassName
|
|
168
|
-
)}
|
|
169
|
-
/>
|
|
170
|
-
)}
|
|
171
|
-
{iconActive && (
|
|
172
|
-
<Icon
|
|
173
|
-
svg={iconActive}
|
|
174
|
-
className={classNames(
|
|
175
|
-
Styles.navigationIcon,
|
|
176
|
-
Styles.navigationIconActive,
|
|
177
|
-
iconClassName
|
|
178
|
-
)}
|
|
179
|
-
/>
|
|
180
|
-
)}
|
|
181
|
-
</Fragment>
|
|
182
|
-
)}
|
|
183
|
-
|
|
184
|
-
{!!expanded?.bar && <div className={Styles.navigationItemText}>{title}</div>}
|
|
185
|
-
{!!tag && <CounterTag data={tag} className={Styles.navigationItemCounter} />}
|
|
186
|
-
{hasSubmenu && !!expanded?.bar && (
|
|
187
|
-
<Icon
|
|
188
|
-
svg={expanded?.submenus?.includes(id) ? SvgGroupCollapse : SvgGroupExpand}
|
|
189
|
-
className={Styles.navigationItemGroupToggle}
|
|
190
|
-
/>
|
|
191
|
-
)}
|
|
192
|
-
</div>
|
|
193
|
-
|
|
194
|
-
{!expanded?.bar && (
|
|
195
|
-
<div
|
|
196
|
-
className={classNames(Styles.navigationItemText, {
|
|
197
|
-
[Styles.navigationItemTextSmall]: title.length >= 10,
|
|
198
|
-
})}
|
|
199
|
-
>
|
|
200
|
-
{title}
|
|
201
|
-
</div>
|
|
202
|
-
)}
|
|
203
|
-
</NavigationComponent>
|
|
204
|
-
);
|
|
205
|
-
};
|
|
206
|
-
|
|
207
104
|
const submenuPopoverStyles = { '--background-color-strong': '#24323C' } as CSSProperties;
|
|
208
105
|
|
|
209
106
|
/** Side Navigation menu item */
|
|
210
107
|
const SideNavigationGroupItem: FC<
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
108
|
+
HeaderNavigationItemData &
|
|
109
|
+
SideNavigationExpandedProps &
|
|
110
|
+
NavigationComponentProps & {
|
|
111
|
+
onExpandedChange: undefined | ((expanded: SideNavigationExpandedState) => void);
|
|
112
|
+
}
|
|
214
113
|
> = ({ onExpandedChange, ...props }) => {
|
|
114
|
+
const isSubmenuExpanded = props.expanded?.submenus?.includes(props.id) ?? false;
|
|
215
115
|
const triggerClick = useCallback(
|
|
216
116
|
(e: MouseEvent<HTMLDivElement>) => {
|
|
217
117
|
e.stopPropagation();
|
|
218
118
|
e.preventDefault();
|
|
219
119
|
|
|
220
|
-
const isSubmenuExpanded = props.expanded?.submenus?.includes(props.id);
|
|
221
120
|
onExpandedChange?.({
|
|
222
121
|
bar: !!props.expanded?.bar,
|
|
223
122
|
submenus: [
|
|
@@ -226,17 +125,19 @@ const SideNavigationGroupItem: FC<
|
|
|
226
125
|
],
|
|
227
126
|
});
|
|
228
127
|
},
|
|
229
|
-
[props.id, props.expanded, onExpandedChange]
|
|
128
|
+
[props.id, props.expanded, isSubmenuExpanded, onExpandedChange]
|
|
230
129
|
);
|
|
231
130
|
|
|
232
|
-
const tag = props.submenu
|
|
233
|
-
? true
|
|
234
|
-
: props.tag;
|
|
131
|
+
const tag = getSubmenuGroupTag(props.submenu, props.tag);
|
|
235
132
|
|
|
236
133
|
return props.expanded?.bar ? (
|
|
237
134
|
<Fragment>
|
|
238
135
|
<div onClickCapture={triggerClick}>
|
|
239
|
-
<
|
|
136
|
+
<InternalSideNavigationLink
|
|
137
|
+
{...props}
|
|
138
|
+
submenuExpanded={isSubmenuExpanded}
|
|
139
|
+
tag={tag}
|
|
140
|
+
/>
|
|
240
141
|
</div>
|
|
241
142
|
<Collapsible open={props.expanded?.submenus?.includes(props.id)} animate>
|
|
242
143
|
<div className={Styles.submenu}>
|
|
@@ -252,7 +153,11 @@ const SideNavigationGroupItem: FC<
|
|
|
252
153
|
<Popover.Trigger>
|
|
253
154
|
{(triggerProps: PopoverTriggerProps) => (
|
|
254
155
|
<div {...triggerProps}>
|
|
255
|
-
<
|
|
156
|
+
<InternalSideNavigationLink
|
|
157
|
+
{...props}
|
|
158
|
+
submenuExpanded={undefined}
|
|
159
|
+
tag={tag}
|
|
160
|
+
/>
|
|
256
161
|
</div>
|
|
257
162
|
)}
|
|
258
163
|
</Popover.Trigger>
|
|
@@ -329,7 +234,7 @@ const SideNavigationGroupLink: FC<HeaderNavigationItemSubmenuLink & NavigationCo
|
|
|
329
234
|
};
|
|
330
235
|
|
|
331
236
|
/** Side Navigation options toggle */
|
|
332
|
-
|
|
237
|
+
const SideNavigationOptionsToggle: FC<{
|
|
333
238
|
expanded?: SideNavigationExpandedState;
|
|
334
239
|
onExpandedChange?(expanded: SideNavigationExpandedState): void;
|
|
335
240
|
}> = ({ expanded, onExpandedChange }) =>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CounterTagProps } from '../components/counter-tag';
|
|
2
|
+
|
|
3
|
+
export interface CounterTagPropsStrict {
|
|
4
|
+
value: number | boolean;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type CounterTagType = boolean | number | CounterTagProps;
|
|
9
|
+
|
|
10
|
+
export const isCounterPropsObject = (tag?: CounterTagType): tag is CounterTagProps =>
|
|
11
|
+
!!tag && (tag as any).value !== undefined;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { isCounterPropsObject } from './counter-tag';
|
|
2
|
+
import { HeaderNavigationItemData, HeaderNavigationItemSubmenu } from './navigation';
|
|
3
|
+
|
|
4
|
+
export function getSubmenuGroupTag(
|
|
5
|
+
submenu: HeaderNavigationItemSubmenu | undefined,
|
|
6
|
+
defaultTag: HeaderNavigationItemData['tag']
|
|
7
|
+
): HeaderNavigationItemData['tag'] {
|
|
8
|
+
if (!submenu) {
|
|
9
|
+
return defaultTag;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let tagValue: number | boolean | undefined = undefined;
|
|
13
|
+
|
|
14
|
+
for (const group of submenu.groups) {
|
|
15
|
+
for (const link of group.links) {
|
|
16
|
+
const ltv: number | boolean | undefined = isCounterPropsObject(link.tag)
|
|
17
|
+
? link.tag.value
|
|
18
|
+
: link.tag;
|
|
19
|
+
|
|
20
|
+
if (ltv) {
|
|
21
|
+
if (typeof ltv === 'number') {
|
|
22
|
+
if (typeof tagValue !== 'number') {
|
|
23
|
+
tagValue = 0;
|
|
24
|
+
}
|
|
25
|
+
tagValue += ltv;
|
|
26
|
+
} else if (typeof tagValue !== 'number') {
|
|
27
|
+
tagValue = true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return tagValue ?? defaultTag;
|
|
34
|
+
}
|