@servicetitan/navigation 11.0.0-canary.237.fef17f5.0 → 11.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/badge-tag.d.ts +1 -1
- package/dist/components/badge-tag.d.ts.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.stories.js +1 -1
- package/dist/components/header-navigation/header-navigation.stories.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/logo/logo-titan-text.d.ts +1 -1
- package/dist/components/logo/logo-titan-text.d.ts.map +1 -1
- package/dist/components/profile-dropdown/profile-dropdown.d.ts +12 -7
- package/dist/components/profile-dropdown/profile-dropdown.d.ts.map +1 -1
- package/dist/components/profile-dropdown/profile-dropdown.js +10 -7
- package/dist/components/profile-dropdown/profile-dropdown.js.map +1 -1
- package/dist/components/profile-dropdown/profile-dropdown.module.less +20 -6
- package/dist/components/titan-layout/layout-context.js +1 -1
- package/dist/components/titan-layout/layout-context.js.map +1 -1
- package/dist/components/titan-layout/layout-header-links.d.ts.map +1 -1
- package/dist/components/titan-layout/layout-header-links.js +1 -1
- package/dist/components/titan-layout/layout-header-links.js.map +1 -1
- package/dist/components/titan-layout/layout-header.d.ts +2 -0
- package/dist/components/titan-layout/layout-header.d.ts.map +1 -1
- package/dist/components/titan-layout/layout-header.js +3 -4
- package/dist/components/titan-layout/layout-header.js.map +1 -1
- package/dist/components/titan-layout/layout-header.module.less +56 -33
- package/dist/components/titan-layout/layout-logo.d.ts.map +1 -1
- package/dist/components/titan-layout/layout-logo.js +2 -1
- package/dist/components/titan-layout/layout-logo.js.map +1 -1
- package/dist/components/titan-layout/layout-profile.d.ts.map +1 -1
- package/dist/components/titan-layout/layout-profile.js +39 -11
- package/dist/components/titan-layout/layout-profile.js.map +1 -1
- package/dist/components/titan-layout/layout-profile.stories.d.ts.map +1 -1
- package/dist/components/titan-layout/layout-profile.stories.js +1 -1
- package/dist/components/titan-layout/layout-profile.stories.js.map +1 -1
- package/dist/components/titan-layout/layout-sidebar-links-internal.d.ts +2 -2
- package/dist/components/titan-layout/layout-sidebar-links-internal.d.ts.map +1 -1
- package/dist/components/titan-layout/layout-sidebar-links-internal.js +4 -4
- package/dist/components/titan-layout/layout-sidebar-links-internal.js.map +1 -1
- package/dist/components/titan-layout/layout-sidebar-links.d.ts.map +1 -1
- package/dist/components/titan-layout/layout-sidebar-links.js +10 -3
- package/dist/components/titan-layout/layout-sidebar-links.js.map +1 -1
- package/dist/components/titan-layout/layout-sidebar.d.ts +2 -0
- package/dist/components/titan-layout/layout-sidebar.d.ts.map +1 -1
- package/dist/components/titan-layout/layout-sidebar.js +6 -4
- package/dist/components/titan-layout/layout-sidebar.js.map +1 -1
- package/dist/components/titan-layout/layout-sidebar.module.less +25 -5
- package/dist/components/titan-layout/notifications-context.d.ts +13 -0
- package/dist/components/titan-layout/notifications-context.d.ts.map +1 -0
- package/dist/components/titan-layout/notifications-context.js +23 -0
- package/dist/components/titan-layout/notifications-context.js.map +1 -0
- package/dist/components/titan-layout/titan-layout.d.ts +6 -3
- package/dist/components/titan-layout/titan-layout.d.ts.map +1 -1
- package/dist/components/titan-layout/titan-layout.js +77 -22
- package/dist/components/titan-layout/titan-layout.js.map +1 -1
- package/dist/components/titan-layout/titan-layout.module.less +44 -20
- package/dist/components/titan-layout/titan-layout.stories.d.ts +4 -0
- package/dist/components/titan-layout/titan-layout.stories.d.ts.map +1 -1
- package/dist/components/titan-layout/titan-layout.stories.js +15 -7
- package/dist/components/titan-layout/titan-layout.stories.js.map +1 -1
- package/dist/test/data.d.ts +4 -1
- package/dist/test/data.d.ts.map +1 -1
- package/dist/test/data.js +2 -3
- package/dist/test/data.js.map +1 -1
- package/dist/utils/use-breakpoint.d.ts +1 -0
- package/dist/utils/use-breakpoint.d.ts.map +1 -1
- package/dist/utils/use-breakpoint.js +3 -2
- package/dist/utils/use-breakpoint.js.map +1 -1
- package/package.json +2 -2
- package/src/components/badge-tag.tsx +1 -1
- package/src/components/header-navigation/header-navigation-stacked.stories.tsx +1 -1
- package/src/components/header-navigation/header-navigation.stories.tsx +1 -1
- package/src/components/left-navigation/header-navigation-tiny.stories.tsx +2 -2
- package/src/components/logo/logo-titan-text.tsx +1 -1
- package/src/components/profile-dropdown/profile-dropdown.module.less +20 -6
- package/src/components/profile-dropdown/profile-dropdown.module.less.d.ts +2 -0
- package/src/components/profile-dropdown/profile-dropdown.tsx +50 -15
- package/src/components/titan-layout/layout-context.tsx +1 -1
- package/src/components/titan-layout/layout-header-links.tsx +2 -1
- package/src/components/titan-layout/layout-header.module.less +56 -33
- package/src/components/titan-layout/layout-header.module.less.d.ts +2 -0
- package/src/components/titan-layout/layout-header.tsx +12 -5
- package/src/components/titan-layout/layout-logo.tsx +13 -6
- package/src/components/titan-layout/layout-profile.stories.tsx +10 -1
- package/src/components/titan-layout/layout-profile.tsx +92 -28
- package/src/components/titan-layout/layout-sidebar-links-internal.tsx +18 -5
- package/src/components/titan-layout/layout-sidebar-links.tsx +16 -3
- package/src/components/titan-layout/layout-sidebar.module.less +25 -5
- package/src/components/titan-layout/layout-sidebar.module.less.d.ts +1 -0
- package/src/components/titan-layout/layout-sidebar.tsx +14 -5
- package/src/components/titan-layout/notifications-context.tsx +44 -0
- package/src/components/titan-layout/titan-layout.module.less +44 -20
- package/src/components/titan-layout/titan-layout.module.less.d.ts +2 -1
- package/src/components/titan-layout/titan-layout.stories.tsx +118 -6
- package/src/components/titan-layout/titan-layout.tsx +196 -87
- package/src/test/data.tsx +2 -3
- package/src/utils/use-breakpoint.ts +3 -1
|
@@ -123,6 +123,11 @@
|
|
|
123
123
|
|
|
124
124
|
.dropdown-section {
|
|
125
125
|
padding: @spacing-1 @spacing-2;
|
|
126
|
+
position: relative;
|
|
127
|
+
|
|
128
|
+
&.dropdown-section-with-counter {
|
|
129
|
+
padding-right: @spacing-3;
|
|
130
|
+
}
|
|
126
131
|
}
|
|
127
132
|
|
|
128
133
|
.dropdown-link {
|
|
@@ -136,13 +141,22 @@
|
|
|
136
141
|
}
|
|
137
142
|
}
|
|
138
143
|
|
|
139
|
-
.counter {
|
|
140
|
-
color: @color-white;
|
|
141
|
-
font-size: @typescale-0;
|
|
142
|
-
font-weight: @font-weight-semibold;
|
|
144
|
+
.counter-wrapper {
|
|
143
145
|
position: absolute;
|
|
144
|
-
top:
|
|
145
|
-
|
|
146
|
+
top: 0;
|
|
147
|
+
bottom: 0;
|
|
148
|
+
right: @spacing-1;
|
|
149
|
+
|
|
150
|
+
display: flex;
|
|
151
|
+
align-items: center;
|
|
152
|
+
|
|
153
|
+
.counter {
|
|
154
|
+
color: @color-white;
|
|
155
|
+
font-size: @typescale-0;
|
|
156
|
+
font-weight: @font-weight-semibold;
|
|
157
|
+
min-width: 12px !important;
|
|
158
|
+
min-height: 12px !important;
|
|
159
|
+
}
|
|
146
160
|
}
|
|
147
161
|
}
|
|
148
162
|
|
|
@@ -4,12 +4,14 @@ export const badge: string;
|
|
|
4
4
|
export const badgeNoContent: string;
|
|
5
5
|
export const badgeWithContent: string;
|
|
6
6
|
export const counter: string;
|
|
7
|
+
export const counterWrapper: string;
|
|
7
8
|
export const dropdown: string;
|
|
8
9
|
export const dropdownContent: string;
|
|
9
10
|
export const dropdownContentBottomLeft: string;
|
|
10
11
|
export const dropdownContentWrapper: string;
|
|
11
12
|
export const dropdownLink: string;
|
|
12
13
|
export const dropdownSection: string;
|
|
14
|
+
export const dropdownSectionWithCounter: string;
|
|
13
15
|
export const expandIcon: string;
|
|
14
16
|
export const hint: string;
|
|
15
17
|
export const hintContent: string;
|
|
@@ -6,6 +6,7 @@ import SvgAccountInactive from '@servicetitan/anvil2/assets/icons/st/gnav_accoun
|
|
|
6
6
|
import { BodyText, Popover, PopoverPropsStrict } from '@servicetitan/design-system';
|
|
7
7
|
import classNames from 'classnames';
|
|
8
8
|
import {
|
|
9
|
+
ComponentPropsWithoutRef,
|
|
9
10
|
FC,
|
|
10
11
|
HTMLAttributeAnchorTarget,
|
|
11
12
|
MouseEvent,
|
|
@@ -147,22 +148,42 @@ const ProfileDropdownTrigger: FC<ProfileDropdownTriggerProps> = ({
|
|
|
147
148
|
);
|
|
148
149
|
};
|
|
149
150
|
|
|
151
|
+
const useTag = (counter?: CounterTagValue, tag?: CounterTagData) =>
|
|
152
|
+
useMemo(() => {
|
|
153
|
+
const data = getCounterTag(counter, tag);
|
|
154
|
+
|
|
155
|
+
return data ? (
|
|
156
|
+
<div className={Styles.counterWrapper}>
|
|
157
|
+
<CounterTag className={Styles.counter} data={data} />
|
|
158
|
+
</div>
|
|
159
|
+
) : undefined;
|
|
160
|
+
}, [counter, tag]);
|
|
161
|
+
|
|
162
|
+
export type ProfileItemContent =
|
|
163
|
+
| { children: string; text?: string }
|
|
164
|
+
| { children: ReactNode; text: string };
|
|
165
|
+
|
|
150
166
|
export interface ProfileDropdownSectionPropsStrict {
|
|
151
167
|
children: ReactNode;
|
|
152
168
|
id: string;
|
|
153
169
|
tooltip?: string;
|
|
154
170
|
className?: string;
|
|
171
|
+
tag?: CounterTagData;
|
|
172
|
+
counter?: CounterTagValue;
|
|
155
173
|
onClick?(e: MouseEvent): void;
|
|
156
174
|
}
|
|
157
175
|
|
|
158
|
-
export
|
|
159
|
-
|
|
160
|
-
|
|
176
|
+
export type ProfileDropdownSectionProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'> &
|
|
177
|
+
ProfileDropdownSectionPropsStrict &
|
|
178
|
+
ProfileItemContent;
|
|
161
179
|
|
|
162
180
|
export const ProfileDropdownSection: FC<ProfileDropdownSectionProps> = ({
|
|
163
181
|
children,
|
|
164
182
|
className,
|
|
183
|
+
counter,
|
|
165
184
|
id,
|
|
185
|
+
tag,
|
|
186
|
+
text,
|
|
166
187
|
tooltip,
|
|
167
188
|
onClick,
|
|
168
189
|
...rest
|
|
@@ -175,17 +196,23 @@ export const ProfileDropdownSection: FC<ProfileDropdownSectionProps> = ({
|
|
|
175
196
|
}
|
|
176
197
|
};
|
|
177
198
|
|
|
199
|
+
const tagElement = useTag(counter, tag);
|
|
200
|
+
|
|
178
201
|
return withTooltip(
|
|
179
202
|
<div
|
|
180
|
-
className={classNames(
|
|
181
|
-
|
|
182
|
-
|
|
203
|
+
className={classNames(
|
|
204
|
+
Styles.dropdownSection,
|
|
205
|
+
tagElement && Styles.dropdownSectionWithCounter,
|
|
206
|
+
!!onClick && Styles.dropdownLink,
|
|
207
|
+
className
|
|
208
|
+
)}
|
|
183
209
|
onClick={clickHandler}
|
|
184
210
|
data-cy={`profile-dropdown-section-${id}`}
|
|
185
211
|
data-pendo={`profile-dropdown-section-${id}`}
|
|
186
212
|
{...rest}
|
|
187
213
|
>
|
|
188
214
|
{children}
|
|
215
|
+
{tagElement}
|
|
189
216
|
</div>,
|
|
190
217
|
tooltip,
|
|
191
218
|
'left'
|
|
@@ -206,9 +233,9 @@ export interface ProfileDropdownLinkPropsStrict {
|
|
|
206
233
|
counter?: CounterTagValue;
|
|
207
234
|
}
|
|
208
235
|
|
|
209
|
-
export
|
|
210
|
-
|
|
211
|
-
|
|
236
|
+
export type ProfileDropdownLinkProps = Omit<ComponentPropsWithoutRef<'a'>, 'children'> &
|
|
237
|
+
ProfileDropdownLinkPropsStrict &
|
|
238
|
+
ProfileItemContent;
|
|
212
239
|
|
|
213
240
|
export const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = ({
|
|
214
241
|
children,
|
|
@@ -218,6 +245,7 @@ export const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = ({
|
|
|
218
245
|
counter,
|
|
219
246
|
tag,
|
|
220
247
|
target,
|
|
248
|
+
text,
|
|
221
249
|
to,
|
|
222
250
|
tooltip,
|
|
223
251
|
onClick,
|
|
@@ -227,15 +255,17 @@ export const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = ({
|
|
|
227
255
|
|
|
228
256
|
const isExternalLink = external ?? to?.startsWith('http');
|
|
229
257
|
|
|
230
|
-
const tagElement =
|
|
231
|
-
() => <CounterTag data={getCounterTag(counter, tag)} className={Styles.counter} />,
|
|
232
|
-
[counter, tag]
|
|
233
|
-
);
|
|
258
|
+
const tagElement = useTag(counter, tag);
|
|
234
259
|
|
|
235
260
|
return withTooltip(
|
|
236
261
|
isExternalLink ? (
|
|
237
262
|
<a
|
|
238
|
-
className={classNames(
|
|
263
|
+
className={classNames(
|
|
264
|
+
Styles.dropdownSection,
|
|
265
|
+
tagElement && Styles.dropdownSectionWithCounter,
|
|
266
|
+
Styles.dropdownLink,
|
|
267
|
+
className
|
|
268
|
+
)}
|
|
239
269
|
href={to}
|
|
240
270
|
target={target}
|
|
241
271
|
data-cy={`profile-dropdown-link-${id}`}
|
|
@@ -247,7 +277,12 @@ export const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = ({
|
|
|
247
277
|
</a>
|
|
248
278
|
) : (
|
|
249
279
|
<NavigationComponent
|
|
250
|
-
className={classNames(
|
|
280
|
+
className={classNames(
|
|
281
|
+
Styles.dropdownSection,
|
|
282
|
+
Styles.dropdownLink,
|
|
283
|
+
{ [Styles.dropdownSectionWithCounter]: !!tagElement },
|
|
284
|
+
className
|
|
285
|
+
)}
|
|
251
286
|
target={target}
|
|
252
287
|
to={to}
|
|
253
288
|
data-cy={`profile-dropdown-link-${id}`}
|
|
@@ -19,7 +19,7 @@ export interface TitanLayoutContextType {
|
|
|
19
19
|
|
|
20
20
|
export const LayoutContext = createContext<TitanLayoutContextType>({
|
|
21
21
|
NavigationComponent: DefaultNavLinkComponent,
|
|
22
|
-
breakpoint: { name: 'lg', isMobile: false },
|
|
22
|
+
breakpoint: { name: 'lg', isMobile: false, width: 0 },
|
|
23
23
|
isTitanLayout: false,
|
|
24
24
|
sidebar: { styles: { popoverContent: {} } },
|
|
25
25
|
});
|
|
@@ -134,7 +134,8 @@ export const LayoutHeaderNavigationTrigger: FC<HeaderNavigationTriggerProps> = (
|
|
|
134
134
|
>
|
|
135
135
|
<HeaderNavigationItemContent
|
|
136
136
|
tag={getCounterTag(counter, tag)}
|
|
137
|
-
icon={
|
|
137
|
+
icon={icon}
|
|
138
|
+
iconActive={iconActive}
|
|
138
139
|
label={label}
|
|
139
140
|
labelClassName={labelClassName}
|
|
140
141
|
/>
|
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
@size-links-tiny: 24px;
|
|
5
5
|
|
|
6
6
|
.header {
|
|
7
|
+
--nav-top-content-height: 32px;
|
|
7
8
|
display: flex;
|
|
8
9
|
justify-content: space-between;
|
|
9
10
|
|
|
10
11
|
background-color: @color-white;
|
|
11
12
|
color: @color-black;
|
|
12
|
-
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
outline: 1px solid @color-neutral-60;
|
|
13
15
|
|
|
14
16
|
& > * {
|
|
15
17
|
overflow-y: hidden;
|
|
@@ -20,6 +22,10 @@
|
|
|
20
22
|
align-items: center;
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
.he-top-center {
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
.he-top-right {
|
|
24
30
|
& > * {
|
|
25
31
|
color: @color-black;
|
|
@@ -57,7 +63,7 @@
|
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
&.navigation-item-icon-state.navigation-item-active {
|
|
60
|
-
.navigation-icon:not(.navigation-icon-active) {
|
|
66
|
+
.navigation-icon[data-anv][data-anv]:not(.navigation-icon-active) {
|
|
61
67
|
display: none;
|
|
62
68
|
}
|
|
63
69
|
|
|
@@ -84,28 +90,59 @@
|
|
|
84
90
|
}
|
|
85
91
|
}
|
|
86
92
|
|
|
93
|
+
.header-mobile {
|
|
94
|
+
padding: @spacing-2 @spacing-0;
|
|
95
|
+
height: var(--nav-offset-top);
|
|
96
|
+
|
|
97
|
+
--nav-top-content-height: 40px;
|
|
98
|
+
|
|
99
|
+
.navigation-link {
|
|
100
|
+
padding: 10px;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.he-top-center {
|
|
104
|
+
max-width: unset;
|
|
105
|
+
flex: 1;
|
|
106
|
+
margin-left: @spacing-3;
|
|
107
|
+
margin-right: @spacing-3;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.he-top-left {
|
|
111
|
+
margin-left: @spacing-half;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.he-top-right {
|
|
115
|
+
margin-right: @spacing-half;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
87
119
|
// desktop
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
120
|
+
.header-desktop {
|
|
121
|
+
height: var(--nav-offset-top);
|
|
122
|
+
.navigation-link {
|
|
123
|
+
margin: 6px 2px;
|
|
124
|
+
padding: 6px 6px;
|
|
94
125
|
|
|
95
|
-
.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.he-top-center {
|
|
99
|
-
flex: 1;
|
|
100
|
-
margin-left: @spacing-2;
|
|
101
|
-
margin-right: @spacing-2;
|
|
102
|
-
max-width: 400px;
|
|
126
|
+
.navigation-item-counter {
|
|
127
|
+
min-width: 12px !important;
|
|
128
|
+
height: 12px !important;
|
|
103
129
|
}
|
|
104
130
|
}
|
|
131
|
+
|
|
132
|
+
.he-top-left {
|
|
133
|
+
padding-left: @spacing-1;
|
|
134
|
+
}
|
|
135
|
+
.he-top-center {
|
|
136
|
+
flex: 1;
|
|
137
|
+
margin-left: @spacing-2;
|
|
138
|
+
margin-right: @spacing-2;
|
|
139
|
+
max-width: 400px;
|
|
140
|
+
}
|
|
105
141
|
}
|
|
142
|
+
|
|
106
143
|
// desktop wide
|
|
107
144
|
@media only screen and (min-width: 1200px) {
|
|
108
|
-
.header {
|
|
145
|
+
.header-desktop {
|
|
109
146
|
display: grid;
|
|
110
147
|
grid-template-columns: repeat(3, 1fr);
|
|
111
148
|
grid-template-rows: 48px;
|
|
@@ -122,21 +159,6 @@
|
|
|
122
159
|
}
|
|
123
160
|
}
|
|
124
161
|
|
|
125
|
-
// mobile
|
|
126
|
-
@media only screen and (max-width: 768px) {
|
|
127
|
-
.header {
|
|
128
|
-
display: grid;
|
|
129
|
-
grid-template-columns: repeat(3, 1fr);
|
|
130
|
-
grid-template-rows: 44px;
|
|
131
|
-
|
|
132
|
-
padding: @spacing-1 @spacing-half;
|
|
133
|
-
|
|
134
|
-
.navigation-link {
|
|
135
|
-
padding: 10px;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
162
|
.navigation-link {
|
|
141
163
|
// styles specific to extra nav links
|
|
142
164
|
color: @color-black;
|
|
@@ -150,7 +172,8 @@
|
|
|
150
172
|
color: @color-white;
|
|
151
173
|
font-weight: @font-weight-semibold;
|
|
152
174
|
font-size: 8px !important;
|
|
153
|
-
min-width:
|
|
175
|
+
min-width: 16px !important;
|
|
176
|
+
height: 16px !important;
|
|
154
177
|
position: absolute;
|
|
155
178
|
top: 4px;
|
|
156
179
|
right: -2px;
|
|
@@ -4,6 +4,8 @@ export const heTopLeft: string;
|
|
|
4
4
|
export const heTopRight: string;
|
|
5
5
|
export const heTopRightText: string;
|
|
6
6
|
export const header: string;
|
|
7
|
+
export const headerDesktop: string;
|
|
8
|
+
export const headerMobile: string;
|
|
7
9
|
export const navigationIcon: string;
|
|
8
10
|
export const navigationIconActive: string;
|
|
9
11
|
export const navigationItemActive: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import SvgBurgerMenu from '@servicetitan/anvil2/assets/icons/material/round/menu.svg';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { ComponentPropsWithoutRef, FC, ReactElement, ReactNode } from 'react';
|
|
4
|
-
import { LayoutPlacementContext
|
|
4
|
+
import { LayoutPlacementContext } from './layout-context';
|
|
5
5
|
import { LayoutHeaderNavigationTrigger } from './layout-header-links';
|
|
6
6
|
import * as Styles from './layout-header.module.less';
|
|
7
7
|
import { TitanLayoutLogoProps } from './layout-logo';
|
|
@@ -25,6 +25,8 @@ export type LayoutHeaderProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'
|
|
|
25
25
|
logo: ReactElement<TitanLayoutLogoProps>;
|
|
26
26
|
profile?: ReactElement;
|
|
27
27
|
|
|
28
|
+
isMobile: boolean;
|
|
29
|
+
hasNotifications: boolean;
|
|
28
30
|
onBurgerClick?: (e: MouseEvent) => void;
|
|
29
31
|
};
|
|
30
32
|
|
|
@@ -35,22 +37,26 @@ export const LayoutHeader: FC<LayoutHeaderProps> = ({
|
|
|
35
37
|
rightClassName,
|
|
36
38
|
center,
|
|
37
39
|
centerClassName,
|
|
40
|
+
isMobile,
|
|
41
|
+
hasNotifications,
|
|
38
42
|
logo,
|
|
39
43
|
profile,
|
|
40
44
|
onBurgerClick,
|
|
41
45
|
...rest
|
|
42
46
|
}) => {
|
|
43
|
-
const { breakpoint } = useTitanLayoutContext();
|
|
44
|
-
|
|
45
47
|
return (
|
|
46
48
|
<LayoutPlacementContext.Provider value="top">
|
|
47
49
|
<div
|
|
48
|
-
className={classNames(
|
|
50
|
+
className={classNames(
|
|
51
|
+
Styles.header,
|
|
52
|
+
isMobile ? Styles.headerMobile : Styles.headerDesktop,
|
|
53
|
+
className
|
|
54
|
+
)}
|
|
49
55
|
{...rest}
|
|
50
56
|
data-cy="header-navigation"
|
|
51
57
|
>
|
|
52
58
|
<div className={classNames(Styles.heTopLeft)} data-cy="navigation-left">
|
|
53
|
-
{
|
|
59
|
+
{isMobile && (
|
|
54
60
|
<LayoutHeaderNavigationTrigger
|
|
55
61
|
id="burger"
|
|
56
62
|
title=""
|
|
@@ -58,6 +64,7 @@ export const LayoutHeader: FC<LayoutHeaderProps> = ({
|
|
|
58
64
|
iconActive={SvgBurgerMenu}
|
|
59
65
|
className="m-r-1"
|
|
60
66
|
onClick={onBurgerClick}
|
|
67
|
+
tag={{ value: hasNotifications }}
|
|
61
68
|
/>
|
|
62
69
|
)}
|
|
63
70
|
{logo}
|
|
@@ -33,20 +33,27 @@ export const TitanLayoutLogo: FC<TitanLayoutLogoProps> = ({
|
|
|
33
33
|
const Wrapper = logoWrapper;
|
|
34
34
|
const logoSize = isMobile ? 44 : 56;
|
|
35
35
|
const logoCompanySize = 48;
|
|
36
|
+
const showCompanyTitle = title === true && !isMobile;
|
|
36
37
|
|
|
37
38
|
return (
|
|
38
|
-
<div
|
|
39
|
-
{
|
|
39
|
+
<div
|
|
40
|
+
className={classNames(
|
|
41
|
+
'd-f align-items-center',
|
|
42
|
+
{ 'p-t-half': showCompanyTitle },
|
|
43
|
+
className
|
|
44
|
+
)}
|
|
45
|
+
>
|
|
46
|
+
{showCompanyTitle ? (
|
|
47
|
+
<Wrapper>
|
|
48
|
+
<LogoCompanyTitle height={logoCompanySize} />
|
|
49
|
+
</Wrapper>
|
|
50
|
+
) : typeof title === 'string' ? (
|
|
40
51
|
<Fragment>
|
|
41
52
|
<LogoTitan size={logoSize} mantleFill={mantleFill} logoWrapper={Wrapper} />
|
|
42
53
|
{!isMobile && (
|
|
43
54
|
<LogoTitanTitle className="c-inherit m-l-1">{title}</LogoTitanTitle>
|
|
44
55
|
)}
|
|
45
56
|
</Fragment>
|
|
46
|
-
) : title === true && !isMobile ? (
|
|
47
|
-
<Wrapper className="">
|
|
48
|
-
<LogoCompanyTitle height={logoCompanySize} />
|
|
49
|
-
</Wrapper>
|
|
50
57
|
) : (
|
|
51
58
|
<LogoTitan size={logoSize} mantleFill={mantleFill} logoWrapper={Wrapper} />
|
|
52
59
|
)}
|
|
@@ -18,7 +18,7 @@ export default {
|
|
|
18
18
|
|
|
19
19
|
export const ProfileDefault = withTitanLayout(
|
|
20
20
|
<ProfileDropdown>
|
|
21
|
-
<ProfileDropdown.Link id="first" to="https://google.com">
|
|
21
|
+
<ProfileDropdown.Link id="first" to="https://google.com" external>
|
|
22
22
|
first link
|
|
23
23
|
</ProfileDropdown.Link>
|
|
24
24
|
<ProfileDropdown.Section id="second" onClick={() => alert('second click')}>
|
|
@@ -33,5 +33,14 @@ export const ProfileDefault = withTitanLayout(
|
|
|
33
33
|
third link
|
|
34
34
|
</ProfileDropdown.Link>
|
|
35
35
|
<ProfileDropdown.Divider />
|
|
36
|
+
<ProfileDropdown.Section
|
|
37
|
+
id="forth"
|
|
38
|
+
onClick={() => alert('forth click')}
|
|
39
|
+
text="Sign Out user"
|
|
40
|
+
>
|
|
41
|
+
Sign Out
|
|
42
|
+
<span className="c-neutral-60 m-l-1">user</span>
|
|
43
|
+
</ProfileDropdown.Section>
|
|
44
|
+
<ProfileDropdown.Divider />
|
|
36
45
|
</ProfileDropdown>
|
|
37
46
|
);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import SvgAccountActive from '@servicetitan/anvil2/assets/icons/st/gnav_account_active.svg';
|
|
2
2
|
import SvgAccountInactive from '@servicetitan/anvil2/assets/icons/st/gnav_account_inactive.svg';
|
|
3
3
|
|
|
4
|
-
import { FC, useState } from 'react';
|
|
5
|
-
import { NavigationComponentContext } from '../../utils/navigation-context';
|
|
4
|
+
import { FC, MouseEvent, useEffect, useState } from 'react';
|
|
5
|
+
import { NavLinkComponentProps, NavigationComponentContext } from '../../utils/navigation-context';
|
|
6
6
|
import {
|
|
7
7
|
ProfileDropdown as DesktopProfileDropdown,
|
|
8
8
|
ProfileDropdownLinkProps,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
InternalSideNavigationGroupLink,
|
|
18
18
|
InternalSideNavigationGroupTrigger,
|
|
19
19
|
} from './layout-sidebar-links-internal';
|
|
20
|
+
import { useNotificationsContext, useNotificationsState } from './notifications-context';
|
|
20
21
|
|
|
21
22
|
export type {
|
|
22
23
|
ProfileDropdownProps,
|
|
@@ -24,14 +25,28 @@ export type {
|
|
|
24
25
|
ProfileDropdownLinkProps,
|
|
25
26
|
} from '../profile-dropdown/profile-dropdown';
|
|
26
27
|
|
|
28
|
+
const ExternalNavComponent: FC<NavLinkComponentProps> = ({
|
|
29
|
+
children,
|
|
30
|
+
isActive,
|
|
31
|
+
to,
|
|
32
|
+
activeClassName,
|
|
33
|
+
...props
|
|
34
|
+
}) => (
|
|
35
|
+
<a {...props} href={to}>
|
|
36
|
+
{children}
|
|
37
|
+
</a>
|
|
38
|
+
);
|
|
39
|
+
|
|
27
40
|
const ProfileDropdownContent: FC<ProfileDropdownProps> = props => {
|
|
28
|
-
const { breakpoint, NavigationComponent } = useTitanLayoutContext();
|
|
41
|
+
const { isTitanLayout, breakpoint, NavigationComponent } = useTitanLayoutContext();
|
|
29
42
|
return breakpoint.isMobile ? (
|
|
30
43
|
<MobileProfileDropdown {...props} navigationComponent={NavigationComponent} />
|
|
31
|
-
) : (
|
|
44
|
+
) : isTitanLayout ? (
|
|
32
45
|
<NavigationComponentContext.Provider value={NavigationComponent}>
|
|
33
46
|
<DesktopProfileDropdown {...props} />
|
|
34
47
|
</NavigationComponentContext.Provider>
|
|
48
|
+
) : (
|
|
49
|
+
<DesktopProfileDropdown {...props} />
|
|
35
50
|
);
|
|
36
51
|
};
|
|
37
52
|
ProfileDropdownContent.displayName = 'ProfileDropdown';
|
|
@@ -40,24 +55,37 @@ const MobileProfileDropdown: FC<ProfileDropdownProps & NavigationComponentProps>
|
|
|
40
55
|
children,
|
|
41
56
|
...props
|
|
42
57
|
}) => {
|
|
58
|
+
const id = '__profile';
|
|
43
59
|
const [expanded, setExpanded] = useState(false);
|
|
44
|
-
const
|
|
60
|
+
const { hasNotifications, NotificationsContextProvider } = useNotificationsState();
|
|
61
|
+
const { onNotificationsUpdate } = useNotificationsContext();
|
|
62
|
+
const onExpandToggle = (e: MouseEvent<never>) => {
|
|
63
|
+
e.stopPropagation();
|
|
64
|
+
setExpanded(!expanded);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
onNotificationsUpdate(id, hasNotifications);
|
|
69
|
+
}, [hasNotifications, onNotificationsUpdate]);
|
|
70
|
+
|
|
45
71
|
return (
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
72
|
+
<NotificationsContextProvider>
|
|
73
|
+
<InternalSideNavigationGroup
|
|
74
|
+
id={id}
|
|
75
|
+
to={undefined}
|
|
76
|
+
title="Profile"
|
|
77
|
+
icon={SvgAccountInactive}
|
|
78
|
+
iconActive={SvgAccountActive}
|
|
79
|
+
isActive={expanded}
|
|
80
|
+
{...props}
|
|
81
|
+
submenuExpanded={expanded}
|
|
82
|
+
onExpandToggle={onExpandToggle}
|
|
83
|
+
onClick={onExpandToggle}
|
|
84
|
+
tag={{ value: hasNotifications }}
|
|
85
|
+
>
|
|
86
|
+
{children}
|
|
87
|
+
</InternalSideNavigationGroup>
|
|
88
|
+
</NotificationsContextProvider>
|
|
61
89
|
);
|
|
62
90
|
};
|
|
63
91
|
|
|
@@ -70,6 +98,25 @@ const ProfileDropdownDivider: FC = () => {
|
|
|
70
98
|
);
|
|
71
99
|
};
|
|
72
100
|
|
|
101
|
+
const getText = (children: any, text: any): string | undefined => {
|
|
102
|
+
if (typeof children === 'string') {
|
|
103
|
+
return children;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (typeof text === 'string') {
|
|
107
|
+
return text;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return undefined;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const getTag = (
|
|
114
|
+
tag: ProfileDropdownLinkProps['tag'],
|
|
115
|
+
counter: ProfileDropdownLinkProps['counter']
|
|
116
|
+
): boolean => {
|
|
117
|
+
return !!tag?.value || !!counter;
|
|
118
|
+
};
|
|
119
|
+
|
|
73
120
|
const ProfileDropdownSection: FC<ProfileDropdownSectionProps> = props => {
|
|
74
121
|
const { breakpoint } = useTitanLayoutContext();
|
|
75
122
|
return breakpoint.isMobile ? (
|
|
@@ -78,10 +125,19 @@ const ProfileDropdownSection: FC<ProfileDropdownSectionProps> = props => {
|
|
|
78
125
|
<DesktopProfileDropdown.Section {...props} />
|
|
79
126
|
);
|
|
80
127
|
};
|
|
81
|
-
const MobileProfileDropdownSection: FC<ProfileDropdownSectionProps> =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
128
|
+
const MobileProfileDropdownSection: FC<ProfileDropdownSectionProps> = ({
|
|
129
|
+
children,
|
|
130
|
+
text,
|
|
131
|
+
tooltip,
|
|
132
|
+
title,
|
|
133
|
+
...props
|
|
134
|
+
}) => {
|
|
135
|
+
const sectionText = getText(children, text);
|
|
136
|
+
const { onNotificationsUpdate } = useNotificationsContext();
|
|
137
|
+
onNotificationsUpdate(props.id, getTag(props.tag, props.counter));
|
|
138
|
+
|
|
139
|
+
return sectionText ? (
|
|
140
|
+
<InternalSideNavigationGroupTrigger {...props} title={sectionText} />
|
|
85
141
|
) : null;
|
|
86
142
|
};
|
|
87
143
|
|
|
@@ -94,17 +150,25 @@ const ProfileDropdownLink: FC<ProfileDropdownLinkProps> = props => {
|
|
|
94
150
|
);
|
|
95
151
|
};
|
|
96
152
|
const MobileProfileDropdownLink: FC<ProfileDropdownLinkProps & NavigationComponentProps> = ({
|
|
153
|
+
external,
|
|
97
154
|
to,
|
|
155
|
+
tooltip,
|
|
156
|
+
text,
|
|
157
|
+
children,
|
|
98
158
|
navigationComponent,
|
|
99
159
|
...props
|
|
100
160
|
}) => {
|
|
101
|
-
const
|
|
102
|
-
|
|
161
|
+
const { onNotificationsUpdate } = useNotificationsContext();
|
|
162
|
+
const linkText = getText(children, text);
|
|
163
|
+
const isExternalLink = external ?? to?.startsWith('http');
|
|
164
|
+
onNotificationsUpdate(props.id, getTag(props.tag, props.counter));
|
|
165
|
+
|
|
166
|
+
return linkText ? (
|
|
103
167
|
<InternalSideNavigationGroupLink
|
|
104
168
|
{...props}
|
|
105
169
|
to={to}
|
|
106
|
-
title={
|
|
107
|
-
navigationComponent={navigationComponent}
|
|
170
|
+
title={linkText}
|
|
171
|
+
navigationComponent={isExternalLink ? ExternalNavComponent : navigationComponent}
|
|
108
172
|
/>
|
|
109
173
|
) : null;
|
|
110
174
|
};
|