@servicetitan/navigation 1.6.0 → 2.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/header-navigation/header-navigation-content.d.ts +30 -0
- package/dist/components/header-navigation/header-navigation-content.d.ts.map +1 -0
- package/dist/components/header-navigation/header-navigation-content.js +21 -0
- package/dist/components/header-navigation/header-navigation-content.js.map +1 -0
- package/dist/components/header-navigation/header-navigation-extra.stories.d.ts +2 -2
- package/dist/components/header-navigation/header-navigation-extra.stories.d.ts.map +1 -1
- package/dist/components/header-navigation/header-navigation-extra.stories.js +7 -7
- package/dist/components/header-navigation/header-navigation-extra.stories.js.map +1 -1
- package/dist/components/header-navigation/header-navigation-links.d.ts +38 -0
- package/dist/components/header-navigation/header-navigation-links.d.ts.map +1 -0
- package/dist/components/header-navigation/header-navigation-links.js +38 -0
- package/dist/components/header-navigation/header-navigation-links.js.map +1 -0
- package/dist/components/header-navigation/header-navigation-stacked.stories.d.ts +11 -0
- package/dist/components/header-navigation/header-navigation-stacked.stories.d.ts.map +1 -0
- package/dist/components/header-navigation/header-navigation-stacked.stories.js +66 -0
- package/dist/components/header-navigation/header-navigation-stacked.stories.js.map +1 -0
- package/dist/components/header-navigation/header-navigation.d.ts +43 -36
- package/dist/components/header-navigation/header-navigation.d.ts.map +1 -1
- package/dist/components/header-navigation/header-navigation.js +41 -81
- package/dist/components/header-navigation/header-navigation.js.map +1 -1
- package/dist/components/header-navigation/header-navigation.module.less +172 -95
- package/dist/components/header-navigation/header-navigation.stories.d.ts.map +1 -1
- package/dist/components/header-navigation/header-navigation.stories.js +8 -8
- package/dist/components/header-navigation/header-navigation.stories.js.map +1 -1
- package/dist/components/header-navigation/index.d.ts +3 -0
- package/dist/components/header-navigation/index.d.ts.map +1 -0
- package/dist/components/header-navigation/index.js +3 -0
- package/dist/components/header-navigation/index.js.map +1 -0
- package/dist/components/logo/logo-titan-text.d.ts +21 -5
- package/dist/components/logo/logo-titan-text.d.ts.map +1 -1
- package/dist/components/logo/logo-titan-text.js +9 -3
- package/dist/components/logo/logo-titan-text.js.map +1 -1
- package/dist/components/logo/logo-titan-text.module.less +12 -9
- package/dist/components/logo/logo-titan.d.ts +2 -2
- package/dist/components/logo/logo-titan.d.ts.map +1 -1
- package/dist/components/logo/logo-titan.js +1 -1
- package/dist/components/logo/logo-titan.js.map +1 -1
- package/dist/components/logo/logo.stories.d.ts +2 -1
- package/dist/components/logo/logo.stories.d.ts.map +1 -1
- package/dist/components/logo/logo.stories.js +7 -5
- package/dist/components/logo/logo.stories.js.map +1 -1
- package/dist/components/profile-dropdown/profile-dropdown-stacked.stories.d.ts +15 -0
- package/dist/components/profile-dropdown/profile-dropdown-stacked.stories.d.ts.map +1 -0
- package/dist/components/profile-dropdown/profile-dropdown-stacked.stories.js +51 -0
- package/dist/components/profile-dropdown/profile-dropdown-stacked.stories.js.map +1 -0
- package/dist/components/profile-dropdown/profile-dropdown.d.ts +1 -0
- 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/components/profile-dropdown/profile-dropdown.module.less +0 -2
- package/dist/components/profile-dropdown/profile-dropdown.stories.js +1 -1
- package/dist/components/profile-dropdown/profile-dropdown.stories.js.map +1 -1
- package/dist/components/profile-dropdown/profile-icon.d.ts +1 -1
- package/dist/components/profile-dropdown/profile-icon.d.ts.map +1 -1
- package/dist/components/profile-dropdown/profile-icon.js +1 -1
- package/dist/components/profile-dropdown/profile-icon.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/with-tooltip.d.ts +3 -0
- package/dist/utils/with-tooltip.d.ts.map +1 -0
- package/dist/utils/with-tooltip.js +4 -0
- package/dist/utils/with-tooltip.js.map +1 -0
- package/package.json +2 -2
- package/src/components/header-navigation/header-navigation-content.tsx +118 -0
- package/src/components/header-navigation/header-navigation-extra.stories.tsx +23 -17
- package/src/components/header-navigation/header-navigation-links.tsx +143 -0
- package/src/components/header-navigation/header-navigation-stacked.stories.tsx +172 -0
- package/src/components/header-navigation/header-navigation.module.less +172 -95
- package/src/components/header-navigation/header-navigation.module.less.d.ts +14 -9
- package/src/components/header-navigation/header-navigation.stories.tsx +11 -19
- package/src/components/header-navigation/header-navigation.tsx +163 -283
- package/src/components/header-navigation/index.ts +2 -0
- package/src/components/logo/logo-titan-text.module.less +12 -9
- package/src/components/logo/logo-titan-text.tsx +62 -20
- package/src/components/logo/logo-titan.tsx +2 -2
- package/src/components/logo/logo.stories.tsx +13 -4
- package/src/components/profile-dropdown/profile-dropdown-stacked.stories.tsx +178 -0
- package/src/components/profile-dropdown/profile-dropdown.module.less +0 -2
- package/src/components/profile-dropdown/profile-dropdown.stories.tsx +1 -1
- package/src/components/profile-dropdown/profile-dropdown.tsx +5 -3
- package/src/components/profile-dropdown/profile-icon.tsx +2 -1
- package/src/index.ts +2 -1
- package/src/utils/with-tooltip.tsx +11 -0
|
@@ -1,31 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Icon,
|
|
3
|
-
IconPropsStrict,
|
|
4
|
-
Popover,
|
|
5
|
-
PopoverPropsStrict,
|
|
6
|
-
Tooltip,
|
|
7
|
-
} from '@servicetitan/design-system';
|
|
1
|
+
import { Icon, Popover, PopoverPropsStrict } from '@servicetitan/design-system';
|
|
8
2
|
import classNames from 'classnames';
|
|
9
|
-
import {
|
|
10
|
-
FC,
|
|
11
|
-
Fragment,
|
|
12
|
-
HTMLAttributeAnchorTarget,
|
|
13
|
-
ReactElement,
|
|
14
|
-
ReactNode,
|
|
15
|
-
useCallback,
|
|
16
|
-
useEffect,
|
|
17
|
-
useLayoutEffect,
|
|
18
|
-
useMemo,
|
|
19
|
-
useRef,
|
|
20
|
-
useState,
|
|
21
|
-
} from 'react';
|
|
3
|
+
import { FC, ReactElement, ReactNode, useCallback, useEffect, useRef, useState } from 'react';
|
|
22
4
|
import { HeaderNavigationItemData, NavLinkComponentProps } from '../../utils/navigation';
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
NavLinkContext,
|
|
26
|
-
useNavLink,
|
|
27
|
-
} from '../../utils/navigation-context';
|
|
28
|
-
import { CounterTag, CounterTagPropsType } from '../counter-tag';
|
|
5
|
+
import { DefaultNavLinkComponent, NavLinkContext } from '../../utils/navigation-context';
|
|
6
|
+
import { HeaderNavigationItem } from './header-navigation-content';
|
|
29
7
|
import * as Styles from './header-navigation.module.less';
|
|
30
8
|
|
|
31
9
|
function useForceUpdate() {
|
|
@@ -35,205 +13,6 @@ function useForceUpdate() {
|
|
|
35
13
|
}, []);
|
|
36
14
|
}
|
|
37
15
|
|
|
38
|
-
interface HeaderNavigationItemContentPropsStrict {
|
|
39
|
-
title?: string;
|
|
40
|
-
counter?: CounterTagPropsType;
|
|
41
|
-
iconClassName?: string;
|
|
42
|
-
iconComponent?: FC;
|
|
43
|
-
iconName?: IconPropsStrict['name'];
|
|
44
|
-
}
|
|
45
|
-
export interface HeaderNavigationTriggerPropsStrict extends HeaderNavigationItemContentPropsStrict {
|
|
46
|
-
id: string;
|
|
47
|
-
hint?: string;
|
|
48
|
-
className?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
interface HeaderNavigationTriggerProps extends HeaderNavigationTriggerPropsStrict {
|
|
52
|
-
[key: string]: any;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface HeaderNavigationLinkPropsStrict extends HeaderNavigationTriggerPropsStrict {
|
|
56
|
-
to: string;
|
|
57
|
-
isActive?: boolean | ((pathname: string) => boolean);
|
|
58
|
-
target?: HTMLAttributeAnchorTarget;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface HeaderNavigationLinkProps extends HeaderNavigationLinkPropsStrict {
|
|
62
|
-
[key: string]: any;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export const HeaderNavigationItemContent: FC<HeaderNavigationItemContentPropsStrict> = ({
|
|
66
|
-
title,
|
|
67
|
-
counter,
|
|
68
|
-
iconClassName,
|
|
69
|
-
iconComponent: IconComponent,
|
|
70
|
-
iconName,
|
|
71
|
-
}) => (
|
|
72
|
-
<Fragment>
|
|
73
|
-
{IconComponent ? (
|
|
74
|
-
<i className={classNames(Styles.icon, iconClassName)}>
|
|
75
|
-
<IconComponent />
|
|
76
|
-
</i>
|
|
77
|
-
) : iconName ? (
|
|
78
|
-
<Icon size="20px" name={iconName} className={classNames(Styles.icon, iconClassName)} />
|
|
79
|
-
) : (
|
|
80
|
-
<i className={classNames(Styles.icon, iconClassName)} />
|
|
81
|
-
)}
|
|
82
|
-
|
|
83
|
-
{!!title && <ins>{title}</ins>}
|
|
84
|
-
|
|
85
|
-
{!!counter && (
|
|
86
|
-
<CounterTag
|
|
87
|
-
data={counter}
|
|
88
|
-
className={Styles.counter}
|
|
89
|
-
longClassName={Styles.counterLong}
|
|
90
|
-
/>
|
|
91
|
-
)}
|
|
92
|
-
</Fragment>
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
export const HeaderNavigationLink: FC<HeaderNavigationLinkProps> = ({
|
|
96
|
-
id,
|
|
97
|
-
to,
|
|
98
|
-
title,
|
|
99
|
-
hint,
|
|
100
|
-
counter,
|
|
101
|
-
className,
|
|
102
|
-
iconClassName,
|
|
103
|
-
iconComponent,
|
|
104
|
-
iconName,
|
|
105
|
-
isActive,
|
|
106
|
-
target,
|
|
107
|
-
...rest
|
|
108
|
-
}) => {
|
|
109
|
-
const NavigationComponent = useNavLink();
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<NavigationComponent
|
|
113
|
-
data-cy={`navigation-link-${id}`}
|
|
114
|
-
data-pendo={`navigation-link-${id}`}
|
|
115
|
-
{...rest}
|
|
116
|
-
key={id}
|
|
117
|
-
to={to}
|
|
118
|
-
title={hint}
|
|
119
|
-
className={classNames(Styles.link, className, {
|
|
120
|
-
[Styles.active]: isActive === true,
|
|
121
|
-
})}
|
|
122
|
-
isActive={typeof isActive === 'function' ? isActive : undefined}
|
|
123
|
-
activeClassName={Styles.active}
|
|
124
|
-
target={target}
|
|
125
|
-
>
|
|
126
|
-
<HeaderNavigationItemContent
|
|
127
|
-
title={title}
|
|
128
|
-
counter={counter}
|
|
129
|
-
iconComponent={iconComponent}
|
|
130
|
-
iconClassName={iconClassName}
|
|
131
|
-
iconName={iconName}
|
|
132
|
-
/>
|
|
133
|
-
</NavigationComponent>
|
|
134
|
-
);
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
interface HeaderNavigationItemPropsStrict extends HeaderNavigationItemData {
|
|
138
|
-
minimized: boolean;
|
|
139
|
-
main: boolean;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const HeaderNavigationItem: FC<HeaderNavigationItemPropsStrict> = ({
|
|
143
|
-
id,
|
|
144
|
-
to,
|
|
145
|
-
title,
|
|
146
|
-
hint,
|
|
147
|
-
counter,
|
|
148
|
-
className,
|
|
149
|
-
iconClassName,
|
|
150
|
-
iconComponent,
|
|
151
|
-
iconName,
|
|
152
|
-
isActive,
|
|
153
|
-
main,
|
|
154
|
-
minimized,
|
|
155
|
-
}) => {
|
|
156
|
-
const NavigationComponent = useNavLink();
|
|
157
|
-
|
|
158
|
-
const trigger = (
|
|
159
|
-
<NavigationComponent
|
|
160
|
-
data-cy={`navigation-item-${id}`}
|
|
161
|
-
data-pendo={`navigation-item-${id}`}
|
|
162
|
-
key={id}
|
|
163
|
-
to={to}
|
|
164
|
-
title={hint}
|
|
165
|
-
className={classNames(
|
|
166
|
-
Styles.link,
|
|
167
|
-
className,
|
|
168
|
-
main ? Styles.linkMain : Styles.linkOverflow,
|
|
169
|
-
{
|
|
170
|
-
[Styles.active]: isActive === true,
|
|
171
|
-
}
|
|
172
|
-
)}
|
|
173
|
-
isActive={typeof isActive === 'function' ? isActive : undefined}
|
|
174
|
-
activeClassName={Styles.active}
|
|
175
|
-
>
|
|
176
|
-
<HeaderNavigationItemContent
|
|
177
|
-
title={minimized ? undefined : title}
|
|
178
|
-
counter={counter}
|
|
179
|
-
iconComponent={iconComponent}
|
|
180
|
-
iconClassName={iconClassName}
|
|
181
|
-
iconName={iconName}
|
|
182
|
-
/>
|
|
183
|
-
</NavigationComponent>
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
return minimized && title ? (
|
|
187
|
-
<Tooltip el="div" direction="b" text={title}>
|
|
188
|
-
{trigger}
|
|
189
|
-
</Tooltip>
|
|
190
|
-
) : (
|
|
191
|
-
trigger
|
|
192
|
-
);
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
export const HeaderNavigationTrigger: FC<HeaderNavigationTriggerProps> = ({
|
|
196
|
-
id,
|
|
197
|
-
hint,
|
|
198
|
-
className,
|
|
199
|
-
counter,
|
|
200
|
-
iconClassName,
|
|
201
|
-
iconComponent,
|
|
202
|
-
iconName,
|
|
203
|
-
title,
|
|
204
|
-
...rest
|
|
205
|
-
}) => (
|
|
206
|
-
<div
|
|
207
|
-
data-cy={`navigation-trigger-${id}`}
|
|
208
|
-
data-pendo={`navigation-trigger-${id}`}
|
|
209
|
-
{...rest}
|
|
210
|
-
title={hint}
|
|
211
|
-
className={classNames(Styles.link, 'cursor-pointer', className)}
|
|
212
|
-
>
|
|
213
|
-
<HeaderNavigationItemContent
|
|
214
|
-
counter={counter}
|
|
215
|
-
iconComponent={iconComponent}
|
|
216
|
-
iconClassName={iconClassName}
|
|
217
|
-
iconName={iconName}
|
|
218
|
-
title={title}
|
|
219
|
-
/>
|
|
220
|
-
</div>
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
export const HeaderNavigationTriggerCustom: FC<
|
|
224
|
-
Omit<HeaderNavigationTriggerProps, 'title'> & { children: ReactNode }
|
|
225
|
-
> = ({ children, id, hint, className, iconClassName, iconComponent, iconName, ...rest }) => (
|
|
226
|
-
<div
|
|
227
|
-
data-cy={`navigation-custom-${id}`}
|
|
228
|
-
data-pendo={`navigation-custom-${id}`}
|
|
229
|
-
{...rest}
|
|
230
|
-
title={hint}
|
|
231
|
-
className={classNames(Styles.link, 'cursor-pointer', className)}
|
|
232
|
-
>
|
|
233
|
-
{children}
|
|
234
|
-
</div>
|
|
235
|
-
);
|
|
236
|
-
|
|
237
16
|
export interface HeaderNavigationOverflowProps {
|
|
238
17
|
direction: PopoverPropsStrict['direction'];
|
|
239
18
|
width: PopoverPropsStrict['width'];
|
|
@@ -281,23 +60,30 @@ const HeaderNavigationOverflow: FC<{
|
|
|
281
60
|
};
|
|
282
61
|
|
|
283
62
|
export interface HeaderNavigationProps {
|
|
63
|
+
/** extra navigation */
|
|
284
64
|
children?: ReactNode;
|
|
65
|
+
/** container class name */
|
|
285
66
|
className?: string;
|
|
286
|
-
|
|
67
|
+
/** extra navigation container class name */
|
|
68
|
+
rightClassName?: string;
|
|
69
|
+
/** container id */
|
|
287
70
|
id?: string;
|
|
71
|
+
/** left content (usually used for logo) */
|
|
288
72
|
left?: ReactElement;
|
|
73
|
+
/** left container class name */
|
|
289
74
|
leftClassName?: string;
|
|
75
|
+
/** minimal width for navigation bar */
|
|
290
76
|
minWidth?: number;
|
|
77
|
+
/** main navigation items */
|
|
291
78
|
items?: HeaderNavigationItemData[];
|
|
79
|
+
/** main navigation overflow items */
|
|
292
80
|
itemsOverflow?: HeaderNavigationItemData[];
|
|
81
|
+
/** navigation component used for routing */
|
|
293
82
|
navigationComponent?: FC<NavLinkComponentProps>;
|
|
294
|
-
|
|
295
|
-
alignMinimized?: 'left' | 'right' | 'center';
|
|
83
|
+
/** props for main items overflow component */
|
|
296
84
|
overflow?: HeaderNavigationOverflowProps;
|
|
297
85
|
}
|
|
298
86
|
|
|
299
|
-
const shortItemWidth = 44;
|
|
300
|
-
|
|
301
87
|
enum MinimizedState {
|
|
302
88
|
Calculating,
|
|
303
89
|
Minimized,
|
|
@@ -305,16 +91,14 @@ enum MinimizedState {
|
|
|
305
91
|
}
|
|
306
92
|
|
|
307
93
|
export const HeaderNavigation: FC<HeaderNavigationProps> = ({
|
|
308
|
-
align,
|
|
309
|
-
alignMinimized,
|
|
310
94
|
children,
|
|
311
95
|
className,
|
|
312
|
-
contentClassName,
|
|
313
96
|
id,
|
|
314
97
|
items,
|
|
315
98
|
itemsOverflow,
|
|
316
99
|
left,
|
|
317
100
|
leftClassName,
|
|
101
|
+
rightClassName,
|
|
318
102
|
minWidth = 800,
|
|
319
103
|
navigationComponent = DefaultNavLinkComponent,
|
|
320
104
|
overflow,
|
|
@@ -326,45 +110,6 @@ export const HeaderNavigation: FC<HeaderNavigationProps> = ({
|
|
|
326
110
|
const forceUpdate = useForceUpdate();
|
|
327
111
|
const [minimized, setMinimized] = useState(MinimizedState.Calculating);
|
|
328
112
|
|
|
329
|
-
const navigationAlign = useMemo(() => {
|
|
330
|
-
if (alignMinimized && minimized === MinimizedState.Minimized) {
|
|
331
|
-
return alignMinimized;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return align ?? 'left';
|
|
335
|
-
}, [align, alignMinimized, minimized]);
|
|
336
|
-
|
|
337
|
-
const navigationAlignClass = useMemo(() => {
|
|
338
|
-
switch (navigationAlign) {
|
|
339
|
-
case 'center':
|
|
340
|
-
return 'justify-content-center';
|
|
341
|
-
case 'right':
|
|
342
|
-
return 'justify-content-end';
|
|
343
|
-
default:
|
|
344
|
-
return 'justify-content-start';
|
|
345
|
-
}
|
|
346
|
-
}, [navigationAlign]);
|
|
347
|
-
|
|
348
|
-
const calculatePaddings = useCallback(() => {
|
|
349
|
-
if (leftRef.current && rightRef.current && centerRef.current) {
|
|
350
|
-
let diff = leftRef.current.clientWidth - rightRef.current.clientWidth;
|
|
351
|
-
const margin = 8;
|
|
352
|
-
|
|
353
|
-
// if no enough space to show navigation links in the middle of page
|
|
354
|
-
if (centerRef.current.clientWidth < (items?.length ?? 0) * shortItemWidth) {
|
|
355
|
-
// show them in the middle of available space
|
|
356
|
-
diff = 0;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
if (navigationAlign !== 'center') {
|
|
360
|
-
diff = 0;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
centerRef.current.style.marginLeft = `${margin + (diff < 0 ? -diff : 0)}px`;
|
|
364
|
-
centerRef.current.style.marginRight = `${margin + (diff > 0 ? diff : 0)}px`;
|
|
365
|
-
}
|
|
366
|
-
}, [items, navigationAlign]);
|
|
367
|
-
|
|
368
113
|
useEffect(() => {
|
|
369
114
|
const handleResize = () => {
|
|
370
115
|
setMinimized(MinimizedState.Calculating);
|
|
@@ -380,15 +125,10 @@ export const HeaderNavigation: FC<HeaderNavigationProps> = ({
|
|
|
380
125
|
forceUpdate();
|
|
381
126
|
}, [items, itemsOverflow, forceUpdate]);
|
|
382
127
|
|
|
383
|
-
useLayoutEffect(() => {
|
|
384
|
-
calculatePaddings();
|
|
385
|
-
}, [calculatePaddings]);
|
|
386
|
-
|
|
387
128
|
const updateIsMinimized = () => {
|
|
388
129
|
if (centerRef.current && navigationRef.current) {
|
|
389
130
|
if (navigationRef.current.clientWidth > centerRef.current.clientWidth) {
|
|
390
131
|
setMinimized(MinimizedState.Minimized);
|
|
391
|
-
calculatePaddings();
|
|
392
132
|
} else if (minimized === MinimizedState.Calculating) {
|
|
393
133
|
setMinimized(MinimizedState.Full);
|
|
394
134
|
}
|
|
@@ -403,7 +143,6 @@ export const HeaderNavigation: FC<HeaderNavigationProps> = ({
|
|
|
403
143
|
<NavLinkContext.Provider value={navigationComponent}>
|
|
404
144
|
<div
|
|
405
145
|
className={classNames(Styles.header, className, {
|
|
406
|
-
[Styles.minimized]: minimized === MinimizedState.Minimized,
|
|
407
146
|
[Styles.calculating]: minimized === MinimizedState.Calculating,
|
|
408
147
|
})}
|
|
409
148
|
style={{ minWidth: `${minWidth}px` }}
|
|
@@ -416,13 +155,12 @@ export const HeaderNavigation: FC<HeaderNavigationProps> = ({
|
|
|
416
155
|
<div
|
|
417
156
|
ref={centerRef}
|
|
418
157
|
className={classNames(
|
|
419
|
-
'd-if flex-grow-1 flex-basis-0',
|
|
420
|
-
navigationAlignClass,
|
|
158
|
+
'd-if flex-grow-1 flex-basis-0 justify-content-center',
|
|
421
159
|
Styles.center
|
|
422
160
|
)}
|
|
423
161
|
data-cy="navigation-items"
|
|
424
162
|
>
|
|
425
|
-
<div ref={navigationRef} className={classNames(
|
|
163
|
+
<div ref={navigationRef} className={classNames('d-if')}>
|
|
426
164
|
{items?.map(item => (
|
|
427
165
|
<HeaderNavigationItem
|
|
428
166
|
{...item}
|
|
@@ -440,10 +178,10 @@ export const HeaderNavigation: FC<HeaderNavigationProps> = ({
|
|
|
440
178
|
className={classNames(
|
|
441
179
|
'd-f flex-row justify-content-end align-items-center',
|
|
442
180
|
Styles.right,
|
|
443
|
-
|
|
181
|
+
rightClassName
|
|
444
182
|
)}
|
|
445
183
|
ref={rightRef}
|
|
446
|
-
data-cy="navigation-
|
|
184
|
+
data-cy="navigation-right"
|
|
447
185
|
>
|
|
448
186
|
{children}
|
|
449
187
|
</div>
|
|
@@ -451,3 +189,145 @@ export const HeaderNavigation: FC<HeaderNavigationProps> = ({
|
|
|
451
189
|
</NavLinkContext.Provider>
|
|
452
190
|
);
|
|
453
191
|
};
|
|
192
|
+
|
|
193
|
+
export interface HeaderNavigationStackedProps {
|
|
194
|
+
/** container class name */
|
|
195
|
+
className?: string;
|
|
196
|
+
/** extra navigation */
|
|
197
|
+
right?: ReactNode;
|
|
198
|
+
/** extra navigation container class name */
|
|
199
|
+
rightClassName?: string;
|
|
200
|
+
/** container id */
|
|
201
|
+
id?: string;
|
|
202
|
+
/** left content (usually used for logo) */
|
|
203
|
+
left?: ReactElement;
|
|
204
|
+
/** left container class name */
|
|
205
|
+
leftClassName?: string;
|
|
206
|
+
/** center content */
|
|
207
|
+
center?: ReactElement;
|
|
208
|
+
/** center container class name */
|
|
209
|
+
centerClassName?: string;
|
|
210
|
+
/** minimal width for navigation bar */
|
|
211
|
+
minWidth?: number;
|
|
212
|
+
/** main navigation items */
|
|
213
|
+
items?: HeaderNavigationItemData[];
|
|
214
|
+
/** main navigation overflow items */
|
|
215
|
+
itemsOverflow?: HeaderNavigationItemData[];
|
|
216
|
+
/** navigation component used for routing */
|
|
217
|
+
navigationComponent?: FC<NavLinkComponentProps>;
|
|
218
|
+
/** props for main items overflow component */
|
|
219
|
+
overflow?: HeaderNavigationOverflowProps;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export const HeaderNavigationStacked: FC<HeaderNavigationStackedProps> = ({
|
|
223
|
+
className,
|
|
224
|
+
id,
|
|
225
|
+
items,
|
|
226
|
+
itemsOverflow,
|
|
227
|
+
left,
|
|
228
|
+
leftClassName,
|
|
229
|
+
right,
|
|
230
|
+
rightClassName,
|
|
231
|
+
center,
|
|
232
|
+
centerClassName,
|
|
233
|
+
minWidth = 800,
|
|
234
|
+
navigationComponent = DefaultNavLinkComponent,
|
|
235
|
+
overflow,
|
|
236
|
+
}) => {
|
|
237
|
+
const bottomRef = useRef<HTMLDivElement>(null);
|
|
238
|
+
const navigationRef = useRef<HTMLDivElement>(null);
|
|
239
|
+
const forceUpdate = useForceUpdate();
|
|
240
|
+
const [minimized, setMinimized] = useState(MinimizedState.Calculating);
|
|
241
|
+
|
|
242
|
+
useEffect(() => {
|
|
243
|
+
const handleResize = () => {
|
|
244
|
+
setMinimized(MinimizedState.Calculating);
|
|
245
|
+
forceUpdate();
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
window.addEventListener('resize', handleResize);
|
|
249
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
250
|
+
}, [forceUpdate]);
|
|
251
|
+
|
|
252
|
+
useEffect(() => {
|
|
253
|
+
setMinimized(MinimizedState.Calculating);
|
|
254
|
+
forceUpdate();
|
|
255
|
+
}, [items, itemsOverflow, forceUpdate]);
|
|
256
|
+
|
|
257
|
+
const updateIsMinimized = () => {
|
|
258
|
+
if (bottomRef.current && navigationRef.current) {
|
|
259
|
+
if (navigationRef.current.clientWidth + 16 > bottomRef.current.clientWidth) {
|
|
260
|
+
setMinimized(MinimizedState.Minimized);
|
|
261
|
+
} else if (minimized === MinimizedState.Calculating) {
|
|
262
|
+
setMinimized(MinimizedState.Full);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
useEffect(() => {
|
|
268
|
+
updateIsMinimized();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
return (
|
|
272
|
+
<NavLinkContext.Provider value={navigationComponent}>
|
|
273
|
+
<div
|
|
274
|
+
className={classNames(
|
|
275
|
+
Styles.headerStacked,
|
|
276
|
+
{
|
|
277
|
+
[Styles.calculating]: minimized === MinimizedState.Calculating,
|
|
278
|
+
},
|
|
279
|
+
className
|
|
280
|
+
)}
|
|
281
|
+
style={{ minWidth: `${minWidth}px` }}
|
|
282
|
+
id={id}
|
|
283
|
+
data-cy="header-navigation"
|
|
284
|
+
>
|
|
285
|
+
<div
|
|
286
|
+
className={classNames(Styles.heTopLeft, leftClassName)}
|
|
287
|
+
data-cy="navigation-left"
|
|
288
|
+
>
|
|
289
|
+
{left}
|
|
290
|
+
</div>
|
|
291
|
+
<div
|
|
292
|
+
className={classNames(Styles.heTopCenter, centerClassName)}
|
|
293
|
+
data-cy="navigation-center"
|
|
294
|
+
>
|
|
295
|
+
{center}
|
|
296
|
+
</div>
|
|
297
|
+
<div
|
|
298
|
+
className={classNames(
|
|
299
|
+
'd-f flex-row justify-content-end align-items-center',
|
|
300
|
+
Styles.heTopRight,
|
|
301
|
+
rightClassName
|
|
302
|
+
)}
|
|
303
|
+
data-cy="navigation-right"
|
|
304
|
+
>
|
|
305
|
+
{right}
|
|
306
|
+
</div>
|
|
307
|
+
<div
|
|
308
|
+
ref={bottomRef}
|
|
309
|
+
className={classNames(
|
|
310
|
+
Styles.heBottom,
|
|
311
|
+
'd-if flex-grow-1 flex-basis-0 justify-content-center',
|
|
312
|
+
Styles.center
|
|
313
|
+
)}
|
|
314
|
+
data-cy="navigation-items"
|
|
315
|
+
>
|
|
316
|
+
<div ref={navigationRef} className={classNames('d-if')}>
|
|
317
|
+
{items?.map(item => (
|
|
318
|
+
<HeaderNavigationItem
|
|
319
|
+
{...item}
|
|
320
|
+
minimized={minimized === MinimizedState.Minimized}
|
|
321
|
+
main
|
|
322
|
+
key={item.id}
|
|
323
|
+
/>
|
|
324
|
+
))}
|
|
325
|
+
{!!itemsOverflow?.length && (
|
|
326
|
+
<HeaderNavigationOverflow items={itemsOverflow} overflow={overflow} />
|
|
327
|
+
)}
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
</NavLinkContext.Provider>
|
|
332
|
+
);
|
|
333
|
+
};
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
@import (reference) '@servicetitan/tokens/core/tokens.less';
|
|
2
2
|
|
|
3
|
+
.logo-text-title {
|
|
4
|
+
font-weight: @font-weight-semibold;
|
|
5
|
+
font-size: 21px;
|
|
6
|
+
line-height: 24px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.logo-text-description {
|
|
10
|
+
font-size: @typescale-1;
|
|
11
|
+
line-height: 16px;
|
|
12
|
+
}
|
|
13
|
+
|
|
3
14
|
.container {
|
|
4
15
|
height: 56px;
|
|
5
16
|
max-width: 200px;
|
|
@@ -13,16 +24,8 @@
|
|
|
13
24
|
}
|
|
14
25
|
}
|
|
15
26
|
|
|
16
|
-
.logo-text-title
|
|
17
|
-
font-weight: @font-weight-semibold;
|
|
18
|
-
font-size: 21px;
|
|
19
|
-
max-width: 140px;
|
|
20
|
-
line-height: 24px;
|
|
21
|
-
}
|
|
22
|
-
|
|
27
|
+
.logo-text-title,
|
|
23
28
|
.logo-text-description {
|
|
24
|
-
font-size: @typescale-1;
|
|
25
29
|
max-width: 140px;
|
|
26
|
-
line-height: 16px;
|
|
27
30
|
}
|
|
28
31
|
}
|
|
@@ -1,43 +1,85 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import { FC, ReactNode } from 'react';
|
|
3
|
-
import {
|
|
2
|
+
import { CSSProperties, FC, ReactNode } from 'react';
|
|
3
|
+
import { LogoTitanSvg } from './logo-titan';
|
|
4
4
|
import * as Styles from './logo-titan-text.module.less';
|
|
5
5
|
|
|
6
|
+
export type WrapperProps = FC<{ className: string; children: ReactNode; style?: CSSProperties }>;
|
|
7
|
+
|
|
8
|
+
export interface LogoTitanProps {
|
|
9
|
+
mantleFill?: string;
|
|
10
|
+
className?: string;
|
|
11
|
+
logoWrapper?: WrapperProps;
|
|
12
|
+
size?: number;
|
|
13
|
+
innerSize?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
6
16
|
export interface LogoTitanTextProps {
|
|
7
17
|
mantleFill?: string;
|
|
8
18
|
className?: string;
|
|
9
19
|
title: string;
|
|
10
20
|
description: string;
|
|
11
|
-
logoWrapper?:
|
|
21
|
+
logoWrapper?: WrapperProps;
|
|
12
22
|
}
|
|
13
23
|
|
|
14
|
-
|
|
15
|
-
className
|
|
16
|
-
children
|
|
17
|
-
}
|
|
18
|
-
|
|
24
|
+
export interface LogoTitanTitleProps {
|
|
25
|
+
className?: string;
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const DefaultLogoWrapper: WrapperProps = ({ className, children, style }) => (
|
|
30
|
+
<div className={className} style={style}>
|
|
31
|
+
{children}
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export const LogoTitan: FC<LogoTitanProps> = ({
|
|
19
36
|
className,
|
|
20
|
-
description,
|
|
21
37
|
logoWrapper: LogoWrapper = DefaultLogoWrapper,
|
|
22
38
|
mantleFill,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
39
|
+
size = 56,
|
|
40
|
+
innerSize,
|
|
41
|
+
}) => {
|
|
42
|
+
const is = innerSize ? Math.min(innerSize, size) : Math.round(size * 0.7);
|
|
43
|
+
|
|
44
|
+
return (
|
|
26
45
|
<LogoWrapper
|
|
27
46
|
className={classNames(
|
|
28
47
|
'bg-white d-if justify-content-center align-items-center',
|
|
29
|
-
|
|
48
|
+
className
|
|
30
49
|
)}
|
|
50
|
+
style={{ height: `${size}px`, width: `${size}px` }}
|
|
31
51
|
>
|
|
32
|
-
<
|
|
52
|
+
<LogoTitanSvg width={is} height={is} mantleFill={mantleFill} />
|
|
33
53
|
</LogoWrapper>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const LogoTitanTitle: FC<LogoTitanTitleProps> = ({ className, children }) => (
|
|
58
|
+
<div className={classNames(Styles.logoTextTitle, 'ff-display', className)}>{children}</div>
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
export const LogoTitanDescription: FC<LogoTitanTitleProps> = ({ className, children }) => (
|
|
62
|
+
<div className={classNames(Styles.logoTextDescription, 'ff-default', className)}>
|
|
63
|
+
{children}
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
export const LogoTitanText: FC<LogoTitanTextProps> = ({
|
|
68
|
+
className,
|
|
69
|
+
description,
|
|
70
|
+
logoWrapper,
|
|
71
|
+
mantleFill,
|
|
72
|
+
title,
|
|
73
|
+
}) => (
|
|
74
|
+
<div className={classNames('d-f', Styles.container, className)}>
|
|
75
|
+
<LogoTitan
|
|
76
|
+
className={Styles.logoWrapper}
|
|
77
|
+
logoWrapper={logoWrapper}
|
|
78
|
+
mantleFill={mantleFill}
|
|
79
|
+
/>
|
|
34
80
|
<div className="d-if p-l-1 c-white flex-column justify-content-center align-items-start">
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
</span>
|
|
38
|
-
<span className={classNames(Styles.logoTextDescription, 'ff-default t-truncate')}>
|
|
39
|
-
{description}
|
|
40
|
-
</span>
|
|
81
|
+
<LogoTitanTitle className="t-truncate p-b-half">{title}</LogoTitanTitle>
|
|
82
|
+
<LogoTitanDescription className="t-truncate">{description}</LogoTitanDescription>
|
|
41
83
|
</div>
|
|
42
84
|
</div>
|
|
43
85
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FC } from 'react';
|
|
2
2
|
|
|
3
|
-
export interface
|
|
3
|
+
export interface LogoTitanSvgProps {
|
|
4
4
|
height?: number;
|
|
5
5
|
width?: number;
|
|
6
6
|
fill?: string;
|
|
@@ -8,7 +8,7 @@ export interface LogoTitanProps {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/* tslint:disable: max-line-length */
|
|
11
|
-
export const
|
|
11
|
+
export const LogoTitanSvg: FC<LogoTitanSvgProps> = props => {
|
|
12
12
|
const dimensions = {
|
|
13
13
|
width: props.width ?? 116,
|
|
14
14
|
height: props.height ?? 106,
|