@servicetitan/navigation 11.0.0-canary.237.4d902dc.0 → 11.0.0-canary.237.5f3f02d.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 +61 -21
- 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 +12 -5
- package/dist/components/titan-layout/layout-sidebar-links.js.map +1 -1
- package/dist/components/titan-layout/layout-sidebar.d.ts +2 -1
- 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 +29 -21
- 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 +10 -6
- package/dist/components/titan-layout/titan-layout.d.ts.map +1 -1
- package/dist/components/titan-layout/titan-layout.js +104 -21
- package/dist/components/titan-layout/titan-layout.js.map +1 -1
- package/dist/components/titan-layout/titan-layout.module.less +74 -19
- package/dist/components/titan-layout/titan-layout.stories.d.ts +15 -11
- package/dist/components/titan-layout/titan-layout.stories.d.ts.map +1 -1
- package/dist/components/titan-layout/titan-layout.stories.js +35 -14
- 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 +61 -21
- package/src/components/titan-layout/layout-header.module.less.d.ts +2 -0
- package/src/components/titan-layout/layout-header.tsx +17 -6
- 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 +21 -5
- package/src/components/titan-layout/layout-sidebar.module.less +29 -21
- package/src/components/titan-layout/layout-sidebar.module.less.d.ts +1 -2
- package/src/components/titan-layout/layout-sidebar.tsx +15 -9
- package/src/components/titan-layout/notifications-context.tsx +44 -0
- package/src/components/titan-layout/titan-layout.module.less +74 -19
- package/src/components/titan-layout/titan-layout.module.less.d.ts +5 -0
- package/src/components/titan-layout/titan-layout.stories.tsx +171 -19
- package/src/components/titan-layout/titan-layout.tsx +244 -74
- package/src/test/data.tsx +2 -3
- package/src/utils/use-breakpoint.ts +3 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Page as Anvil2Page, Popover } from '@servicetitan/anvil2';
|
|
1
|
+
import { Announcement, Page as Anvil2Page, Button, Popover, TextField } from '@servicetitan/anvil2';
|
|
2
2
|
import SvgSearch from '@servicetitan/anvil2/assets/icons/material/round/search.svg';
|
|
3
3
|
import SvgAtlas from '@servicetitan/anvil2/assets/icons/st/atlas_logo.svg';
|
|
4
4
|
import SvgSettingsActive from '@servicetitan/anvil2/assets/icons/st/gnav_settings_active.svg';
|
|
@@ -20,11 +20,32 @@ import { SideNavigationLinkWrapperProps } from '../left-navigation';
|
|
|
20
20
|
import { HeaderNavigationLink, HeaderNavigationTrigger } from '../links';
|
|
21
21
|
import { ProfileDropdown, TitanLayout, TitanLayoutProps, TitanLayoutState } from './';
|
|
22
22
|
|
|
23
|
+
interface LayoutContentArgs {
|
|
24
|
+
header: boolean;
|
|
25
|
+
sideTop: boolean;
|
|
26
|
+
extraText: boolean;
|
|
27
|
+
search: boolean;
|
|
28
|
+
longContent: boolean;
|
|
29
|
+
wideContent: boolean;
|
|
30
|
+
minWidth: boolean;
|
|
31
|
+
emptyNav: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
23
34
|
export default {
|
|
24
35
|
title: 'Navigation/TitanLayout',
|
|
25
|
-
component: TitanLayout,
|
|
26
36
|
decorators: [withDefaultRedirects, withMemoryRouter, withAnvil],
|
|
27
37
|
parameters: {},
|
|
38
|
+
argTypes: {},
|
|
39
|
+
args: {
|
|
40
|
+
header: true,
|
|
41
|
+
sideTop: true,
|
|
42
|
+
extraText: true,
|
|
43
|
+
search: true,
|
|
44
|
+
longContent: true,
|
|
45
|
+
wideContent: false,
|
|
46
|
+
minWidth: false,
|
|
47
|
+
emptyNav: false,
|
|
48
|
+
} as LayoutContentArgs,
|
|
28
49
|
};
|
|
29
50
|
|
|
30
51
|
const mainNavItems = [
|
|
@@ -51,6 +72,7 @@ const profile = (
|
|
|
51
72
|
to="https://google.com"
|
|
52
73
|
tooltip="Google it"
|
|
53
74
|
target="_blank"
|
|
75
|
+
tag={{ value: true }}
|
|
54
76
|
>
|
|
55
77
|
first link
|
|
56
78
|
</ProfileDropdown.Link>
|
|
@@ -58,6 +80,7 @@ const profile = (
|
|
|
58
80
|
id="second"
|
|
59
81
|
onClick={() => alert('second click')}
|
|
60
82
|
tooltip="Second hint"
|
|
83
|
+
counter
|
|
61
84
|
>
|
|
62
85
|
second link
|
|
63
86
|
</ProfileDropdown.Section>
|
|
@@ -131,9 +154,9 @@ const SideLinkPopoverWrapper: FC<SideNavigationLinkWrapperProps> = ({ children,
|
|
|
131
154
|
};
|
|
132
155
|
|
|
133
156
|
const sidebarTop = () => [
|
|
134
|
-
<TitanLayout.
|
|
135
|
-
<TitanLayout.
|
|
136
|
-
<TitanLayout.
|
|
157
|
+
<TitanLayout.Link key="tasks" {...items.tasks} />,
|
|
158
|
+
<TitanLayout.Link key="calls" {...items.calls} />,
|
|
159
|
+
<TitanLayout.Trigger
|
|
137
160
|
key="marketing"
|
|
138
161
|
{...items.marketing}
|
|
139
162
|
isActive={false}
|
|
@@ -142,51 +165,180 @@ const sidebarTop = () => [
|
|
|
142
165
|
counter={50}
|
|
143
166
|
/>,
|
|
144
167
|
];
|
|
145
|
-
const
|
|
168
|
+
const ContentHeader = () => {
|
|
169
|
+
const [longInfo, setLongInfo] = useState(false);
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<Fragment>
|
|
173
|
+
<Announcement title="Some info" status="info" />
|
|
174
|
+
<Announcement title="Some warning" status="warning" />
|
|
175
|
+
<div
|
|
176
|
+
className="d-f justify-content-center align-items-center bg-purple-100-i"
|
|
177
|
+
style={{ height: longInfo ? '120px' : '48px' }}
|
|
178
|
+
>
|
|
179
|
+
<div className="d-f align-items-center gap-1">
|
|
180
|
+
custom content{' '}
|
|
181
|
+
<Button onClick={() => setLongInfo(!longInfo)} size="small" aria-label="test">
|
|
182
|
+
{longInfo ? '↑' : '↓'}
|
|
183
|
+
</Button>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
</Fragment>
|
|
187
|
+
);
|
|
188
|
+
};
|
|
189
|
+
const SearchBar = () => (
|
|
190
|
+
<TextField size="small" placeholder="Search" className="w-100-i m-x-half-i" />
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const useLayoutProps = (args: LayoutContentArgs): TitanLayoutProps => {
|
|
146
194
|
const [state, setState] = useState<TitanLayoutState | undefined>(undefined);
|
|
147
195
|
|
|
148
196
|
return {
|
|
149
197
|
state,
|
|
150
198
|
onStateChange: setState,
|
|
151
199
|
|
|
152
|
-
|
|
200
|
+
navigationComponent: NavLinkMock,
|
|
201
|
+
navigationMainItems: args.emptyNav ? [] : mainNavItems,
|
|
202
|
+
|
|
153
203
|
profile,
|
|
204
|
+
top: args.search ? <SearchBar /> : undefined,
|
|
205
|
+
header: args.header ? <ContentHeader /> : undefined,
|
|
206
|
+
|
|
154
207
|
extraLinks,
|
|
155
208
|
extraLinksTop,
|
|
209
|
+
extraText: args.extraText ? 'EST (-8 hrs)' : undefined,
|
|
156
210
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
211
|
+
sideTop: args.sideTop && !args.emptyNav ? sidebarTop() : undefined,
|
|
212
|
+
|
|
213
|
+
minContentWidth: args.minWidth ? 900 : undefined,
|
|
160
214
|
};
|
|
161
215
|
};
|
|
162
216
|
|
|
163
|
-
|
|
164
|
-
|
|
217
|
+
const Content = (args: LayoutContentArgs) => {
|
|
218
|
+
return (
|
|
219
|
+
<Fragment>
|
|
220
|
+
<LocationInfo className="m-b-3" />
|
|
221
|
+
<div className="m-b-3">rendered - {new Date().toLocaleTimeString()}</div>
|
|
222
|
+
{args.wideContent && (
|
|
223
|
+
<div style={{ width: '1200px' }}>
|
|
224
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
|
|
225
|
+
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
|
|
226
|
+
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
|
227
|
+
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
|
228
|
+
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
|
|
229
|
+
culpa qui officia deserunt mollit anim id est laborum.
|
|
230
|
+
</div>
|
|
231
|
+
)}
|
|
232
|
+
|
|
233
|
+
{args.longContent && (
|
|
234
|
+
<div>
|
|
235
|
+
<p>Lorem</p>
|
|
236
|
+
<p>ipsum</p>
|
|
237
|
+
<p>dolor</p>
|
|
238
|
+
<p>sit</p>
|
|
239
|
+
<p>amet,</p>
|
|
240
|
+
<p>consectetur</p>
|
|
241
|
+
<p>adipiscing</p>
|
|
242
|
+
<p>elit,</p>
|
|
243
|
+
<p>sed</p>
|
|
244
|
+
<p>do</p>
|
|
245
|
+
<p>eiusmod</p>
|
|
246
|
+
<p>tempor</p>
|
|
247
|
+
<p>incididunt</p>
|
|
248
|
+
<p>ut</p>
|
|
249
|
+
<p>labore</p>
|
|
250
|
+
<p>et</p>
|
|
251
|
+
<p>dolore</p>
|
|
252
|
+
<p>magna</p>
|
|
253
|
+
<p>aliqua.</p>
|
|
254
|
+
<p>Ut</p>
|
|
255
|
+
<p>enim</p>
|
|
256
|
+
<p>ad</p>
|
|
257
|
+
<p>minim</p>
|
|
258
|
+
<p>veniam,</p>
|
|
259
|
+
<p>quis</p>
|
|
260
|
+
<p>nostrud</p>
|
|
261
|
+
<p>exercitation</p>
|
|
262
|
+
<p>ullamco</p>
|
|
263
|
+
<p>laboris</p>
|
|
264
|
+
<p>nisi</p>
|
|
265
|
+
<p>ut</p>
|
|
266
|
+
<p>aliquip</p>
|
|
267
|
+
<p>ex</p>
|
|
268
|
+
<p>ea</p>
|
|
269
|
+
<p>commodo</p>
|
|
270
|
+
<p>consequat.</p>
|
|
271
|
+
<p>Duis</p>
|
|
272
|
+
<p>aute</p>
|
|
273
|
+
<p>irure</p>
|
|
274
|
+
<p>dolor</p>
|
|
275
|
+
<p>in</p>
|
|
276
|
+
<p>reprehenderit</p>
|
|
277
|
+
<p>in</p>
|
|
278
|
+
<p>voluptate</p>
|
|
279
|
+
<p>velit</p>
|
|
280
|
+
<p>esse</p>
|
|
281
|
+
<p>cillum</p>
|
|
282
|
+
<p>dolore</p>
|
|
283
|
+
<p>eu</p>
|
|
284
|
+
<p>fugiat</p>
|
|
285
|
+
<p>nulla</p>
|
|
286
|
+
<p>pariatur.</p>
|
|
287
|
+
<p>Excepteur</p>
|
|
288
|
+
<p>sint</p>
|
|
289
|
+
<p>occaecat</p>
|
|
290
|
+
<p>cupidatat</p>
|
|
291
|
+
<p>non</p>
|
|
292
|
+
<p>proident,</p>
|
|
293
|
+
<p>sunt</p>
|
|
294
|
+
<p>in</p>
|
|
295
|
+
<p>culpa</p>
|
|
296
|
+
<p>qui</p>
|
|
297
|
+
<p>officia</p>
|
|
298
|
+
<p>deserunt</p>
|
|
299
|
+
<p>mollit</p>
|
|
300
|
+
<p>anim</p>
|
|
301
|
+
<p>id</p>
|
|
302
|
+
<p>est</p>
|
|
303
|
+
<p>laborum.</p>
|
|
304
|
+
</div>
|
|
305
|
+
)}
|
|
306
|
+
</Fragment>
|
|
307
|
+
);
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
export const TitanLayoutLegacy = (args: LayoutContentArgs) => (
|
|
311
|
+
<TitanLayout {...useLayoutProps(args)} appearance="legacy">
|
|
165
312
|
<TitanLayout.Logo title />
|
|
166
313
|
<TitanLayout.Content>
|
|
167
|
-
<
|
|
314
|
+
<div
|
|
315
|
+
className="p-3"
|
|
316
|
+
style={{ position: 'absolute', width: 'calc(100% - var(--nav-offset-left));' }}
|
|
317
|
+
>
|
|
318
|
+
<Content {...args} />
|
|
319
|
+
</div>
|
|
168
320
|
</TitanLayout.Content>
|
|
169
321
|
</TitanLayout>
|
|
170
322
|
);
|
|
171
323
|
|
|
172
|
-
export const TitanLayoutAnvil1 = () => (
|
|
173
|
-
<TitanLayout {...useLayoutProps()} appearance="anvil1">
|
|
324
|
+
export const TitanLayoutAnvil1 = (args: LayoutContentArgs) => (
|
|
325
|
+
<TitanLayout {...useLayoutProps(args)} appearance="anvil1">
|
|
174
326
|
<TitanLayout.Logo title />
|
|
175
327
|
<TitanLayout.Content>
|
|
176
328
|
<Anvil1Page>
|
|
177
|
-
<
|
|
329
|
+
<Content {...args} />
|
|
178
330
|
</Anvil1Page>
|
|
179
331
|
</TitanLayout.Content>
|
|
180
332
|
</TitanLayout>
|
|
181
333
|
);
|
|
182
334
|
|
|
183
|
-
export const TitanLayoutAnvil2 = () => (
|
|
184
|
-
<TitanLayout {...useLayoutProps()} appearance="anvil2">
|
|
335
|
+
export const TitanLayoutAnvil2 = (args: LayoutContentArgs) => (
|
|
336
|
+
<TitanLayout {...useLayoutProps(args)} appearance="anvil2">
|
|
185
337
|
<TitanLayout.Logo title />
|
|
186
338
|
<TitanLayout.Content>
|
|
187
339
|
<Anvil2Page>
|
|
188
340
|
<Anvil2Page.Content>
|
|
189
|
-
<
|
|
341
|
+
<Content {...args} />
|
|
190
342
|
</Anvil2Page.Content>
|
|
191
343
|
</Anvil2Page>
|
|
192
344
|
</TitanLayout.Content>
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
useCallback,
|
|
12
12
|
useEffect,
|
|
13
13
|
useMemo,
|
|
14
|
+
useRef,
|
|
14
15
|
useState,
|
|
15
16
|
} from 'react';
|
|
16
17
|
import { NavigationItemData } from '../../utils/navigation';
|
|
@@ -28,33 +29,38 @@ import { TitanLayoutLogo, TitanLayoutLogoProps } from './layout-logo';
|
|
|
28
29
|
import { LayoutSidebar } from './layout-sidebar';
|
|
29
30
|
import { TitanLayoutSidebarLink, TitanLayoutSidebarTrigger } from './layout-sidebar-links';
|
|
30
31
|
import { InternalSideNavigationTrigger } from './layout-sidebar-links-internal';
|
|
32
|
+
import { useNotificationsState } from './notifications-context';
|
|
31
33
|
import * as Styles from './titan-layout.module.less';
|
|
32
34
|
|
|
33
35
|
type TitanLayoutChild = ReactElement<TitanLayoutContentProps> | ReactElement<TitanLayoutLogoProps>;
|
|
34
36
|
|
|
35
37
|
export type TitanLayoutProps = Omit<ComponentPropsWithoutRef<'div'>, 'children' | 'style'> & {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
/** layout appearance */
|
|
38
39
|
appearance?: 'legacy' | 'anvil1' | 'anvil2';
|
|
39
40
|
|
|
41
|
+
/** layout's content */
|
|
42
|
+
children?: TitanLayoutChild | TitanLayoutChild[];
|
|
43
|
+
|
|
44
|
+
/** show only content without side and top bars */
|
|
45
|
+
contentOnly?: boolean;
|
|
46
|
+
|
|
40
47
|
/** component used for navigation */
|
|
41
48
|
navigationComponent?: FC<NavLinkComponentProps>;
|
|
42
49
|
|
|
43
50
|
/** data for main navigation links */
|
|
44
51
|
navigationMainItems?: NavigationItemData[];
|
|
45
52
|
|
|
46
|
-
/** layout's content */
|
|
47
|
-
children?: TitanLayoutChild | TitanLayoutChild[];
|
|
48
|
-
|
|
49
53
|
state?: TitanLayoutState;
|
|
50
54
|
onStateChange?: (state: TitanLayoutState) => void;
|
|
51
55
|
|
|
52
56
|
header?: ReactElement;
|
|
57
|
+
top?: ReactElement;
|
|
58
|
+
sideTop?: ReactElement[];
|
|
53
59
|
profile?: ReactElement;
|
|
54
60
|
extraLinks?: ReactElement;
|
|
55
61
|
extraLinksTop?: ReactElement;
|
|
56
62
|
extraText?: string;
|
|
57
|
-
|
|
63
|
+
minContentWidth?: number;
|
|
58
64
|
};
|
|
59
65
|
|
|
60
66
|
const defaultSidebarContext: TitanLayoutSidebarContextType = {
|
|
@@ -76,7 +82,7 @@ const useVariant = (appearance: TitanLayoutProps['appearance']) =>
|
|
|
76
82
|
isLegacy,
|
|
77
83
|
isAnvil1,
|
|
78
84
|
isAnvil2,
|
|
79
|
-
isSequent: isLegacy ||
|
|
85
|
+
isSequent: isLegacy || isAnvil2,
|
|
80
86
|
};
|
|
81
87
|
}, [appearance]);
|
|
82
88
|
|
|
@@ -113,8 +119,10 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
|
|
|
113
119
|
appearance = 'anvil2',
|
|
114
120
|
id,
|
|
115
121
|
children,
|
|
122
|
+
contentOnly,
|
|
116
123
|
navigationComponent,
|
|
117
124
|
header,
|
|
125
|
+
top,
|
|
118
126
|
profile,
|
|
119
127
|
state,
|
|
120
128
|
onStateChange,
|
|
@@ -122,10 +130,10 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
|
|
|
122
130
|
extraLinks,
|
|
123
131
|
extraLinksTop,
|
|
124
132
|
extraText,
|
|
125
|
-
|
|
133
|
+
minContentWidth,
|
|
134
|
+
sideTop,
|
|
126
135
|
}) => {
|
|
127
136
|
const breakpoint = useTitanBreakpoint();
|
|
128
|
-
const isMobile = breakpoint.isMobile;
|
|
129
137
|
const context: TitanLayoutContextType = useMemo(
|
|
130
138
|
() => ({
|
|
131
139
|
NavigationComponent: navigationComponent ?? DefaultNavLinkComponent,
|
|
@@ -138,21 +146,25 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
|
|
|
138
146
|
const variant = useVariant(appearance);
|
|
139
147
|
const [mobileDrawerOpened, setMobileDrawerOpened] = useState(false);
|
|
140
148
|
const { content, logo } = useLayoutChildren(children);
|
|
149
|
+
const { hasNotifications, NotificationsContextProvider } = useNotificationsState();
|
|
150
|
+
const [offsetTopStyles, setOffsetTopStyles] = useState<object>({});
|
|
151
|
+
const updateIndicatorsHeight = useCallback((offset: number) => {
|
|
152
|
+
setOffsetTopStyles({
|
|
153
|
+
'--content-offset-top': `calc(var(--nav-offset-top) + ${offset}px)`,
|
|
154
|
+
});
|
|
155
|
+
}, []);
|
|
156
|
+
|
|
157
|
+
const isMobile = breakpoint.isMobile;
|
|
158
|
+
const hasSideBar = !contentOnly && (!!navigationMainItems?.length || !!sideTop?.length);
|
|
159
|
+
const hasTopBar = !contentOnly;
|
|
141
160
|
|
|
142
161
|
useEffect(() => {
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
|
|
162
|
+
if (variant.isAnvil1) {
|
|
163
|
+
const bodyClassName = 'of-hidden-i';
|
|
164
|
+
document.body.classList.add(bodyClassName);
|
|
165
|
+
return () => document.body.classList.remove(bodyClassName);
|
|
146
166
|
}
|
|
147
|
-
|
|
148
|
-
const listener = () => {
|
|
149
|
-
setMobileDrawerOpened(false);
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
document.addEventListener('click', listener);
|
|
153
|
-
|
|
154
|
-
return () => document.removeEventListener('click', listener);
|
|
155
|
-
}, [isMobile]);
|
|
167
|
+
}, [variant.isAnvil1]);
|
|
156
168
|
|
|
157
169
|
const onBurgerClick = useCallback((e: MouseEvent) => {
|
|
158
170
|
setMobileDrawerOpened(true);
|
|
@@ -178,8 +190,49 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
|
|
|
178
190
|
},
|
|
179
191
|
[state, onStateChange]
|
|
180
192
|
);
|
|
193
|
+
const hasMenuNotifications = useMemo(() => {
|
|
194
|
+
try {
|
|
195
|
+
return (
|
|
196
|
+
navigationMainItems?.some(item => {
|
|
197
|
+
if (item.counter || item.tag?.value) {
|
|
198
|
+
return true;
|
|
199
|
+
} else if (item.submenu) {
|
|
200
|
+
return item.submenu.groups.some(group =>
|
|
201
|
+
group.links.some(link => !!link.counter || !!link.tag?.value)
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
}) ?? false
|
|
206
|
+
);
|
|
207
|
+
} catch {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}, [navigationMainItems]);
|
|
181
211
|
|
|
182
|
-
const
|
|
212
|
+
const limitContentWidth = useMemo(() => {
|
|
213
|
+
if (variant.isAnvil2 || !minContentWidth) {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (breakpoint.width < minContentWidth) {
|
|
218
|
+
return minContentWidth;
|
|
219
|
+
}
|
|
220
|
+
}, [variant, minContentWidth, breakpoint.width]);
|
|
221
|
+
|
|
222
|
+
const contentStyles = useMemo(() => {
|
|
223
|
+
if (variant.isAnvil2) {
|
|
224
|
+
return offsetTopStyles;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (variant.isLegacy) {
|
|
228
|
+
return {
|
|
229
|
+
...(limitContentWidth
|
|
230
|
+
? { display: 'flex', flexDirection: 'column', minHeight: '100vh' }
|
|
231
|
+
: {}),
|
|
232
|
+
...offsetTopStyles,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}, [variant, offsetTopStyles, limitContentWidth]);
|
|
183
236
|
|
|
184
237
|
const layoutClass = variant.isLegacy
|
|
185
238
|
? Styles.layoutLegacy
|
|
@@ -194,77 +247,194 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
|
|
|
194
247
|
id={id}
|
|
195
248
|
className={classNames(
|
|
196
249
|
Styles.layout,
|
|
197
|
-
isMobile
|
|
198
|
-
|
|
199
|
-
:
|
|
200
|
-
|
|
201
|
-
|
|
250
|
+
isMobile ? Styles.layoutMobile : Styles.layoutDesktop,
|
|
251
|
+
{
|
|
252
|
+
[Styles.layoutTop]: hasTopBar,
|
|
253
|
+
[Styles.layoutNavSlim]: !isMobile && hasSideBar && state?.navCollapsed,
|
|
254
|
+
[Styles.layoutNavWide]: !isMobile && hasSideBar && !state?.navCollapsed,
|
|
255
|
+
},
|
|
202
256
|
layoutClass
|
|
203
257
|
)}
|
|
204
|
-
style={
|
|
258
|
+
style={contentStyles}
|
|
205
259
|
>
|
|
206
260
|
{variant.isSequent && <div className={Styles.topPlaceholder} />}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
{extraLinksTop}
|
|
216
|
-
{!isMobile && extraLinks}
|
|
217
|
-
</Fragment>
|
|
218
|
-
}
|
|
219
|
-
onBurgerClick={onBurgerClick}
|
|
220
|
-
/>
|
|
221
|
-
|
|
222
|
-
<LayoutSidebar
|
|
223
|
-
flex={!variant.isSequent}
|
|
224
|
-
mobile={breakpoint.isMobile}
|
|
225
|
-
barExpanded={isMobile ? mobileDrawerOpened : !state?.navCollapsed}
|
|
226
|
-
submenuExpanded={state?.submenuExpanded}
|
|
227
|
-
onBarExpandChange={onBarExpandChange}
|
|
228
|
-
onSubmenuExpandChange={onSubmenuExpandChange}
|
|
229
|
-
top={sidebarTop}
|
|
230
|
-
mainItems={navigationMainItems}
|
|
231
|
-
navigationComponent={context.NavigationComponent}
|
|
232
|
-
bottom={
|
|
233
|
-
isMobile ? (
|
|
261
|
+
{hasTopBar && (
|
|
262
|
+
<LayoutHeader
|
|
263
|
+
className={Styles.top}
|
|
264
|
+
logo={logo}
|
|
265
|
+
profile={isMobile ? undefined : profile}
|
|
266
|
+
center={top}
|
|
267
|
+
rightText={isMobile ? undefined : extraText}
|
|
268
|
+
right={
|
|
234
269
|
<Fragment>
|
|
235
|
-
{
|
|
236
|
-
{extraLinks}
|
|
237
|
-
{!!extraText && (
|
|
238
|
-
<InternalSideNavigationTrigger
|
|
239
|
-
id="__extra_text"
|
|
240
|
-
title={extraText}
|
|
241
|
-
submenuExpanded={undefined}
|
|
242
|
-
dataPrefix="navigation-extra-text"
|
|
243
|
-
tag={undefined}
|
|
244
|
-
icon={undefined}
|
|
245
|
-
iconActive={undefined}
|
|
246
|
-
/>
|
|
247
|
-
)}
|
|
270
|
+
{extraLinksTop}
|
|
271
|
+
{!isMobile && extraLinks}
|
|
248
272
|
</Fragment>
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
273
|
+
}
|
|
274
|
+
isMobile={isMobile}
|
|
275
|
+
hasNotifications={hasNotifications || hasMenuNotifications}
|
|
276
|
+
onBurgerClick={onBurgerClick}
|
|
277
|
+
/>
|
|
278
|
+
)}
|
|
279
|
+
|
|
280
|
+
{hasSideBar && (
|
|
281
|
+
<NotificationsContextProvider>
|
|
282
|
+
<LayoutSidebar
|
|
283
|
+
className={Styles.side}
|
|
284
|
+
mobile={breakpoint.isMobile}
|
|
285
|
+
barExpanded={!state?.navCollapsed}
|
|
286
|
+
onBarExpandChange={onBarExpandChange}
|
|
287
|
+
submenuExpanded={state?.submenuExpanded}
|
|
288
|
+
onSubmenuExpandChange={onSubmenuExpandChange}
|
|
289
|
+
drawerOpened={mobileDrawerOpened}
|
|
290
|
+
onDrawerOpenChange={setMobileDrawerOpened}
|
|
291
|
+
top={sideTop}
|
|
292
|
+
mainItems={navigationMainItems}
|
|
293
|
+
navigationComponent={context.NavigationComponent}
|
|
294
|
+
bottom={
|
|
295
|
+
isMobile ? (
|
|
296
|
+
<Fragment>
|
|
297
|
+
{profile}
|
|
298
|
+
{extraLinks}
|
|
299
|
+
{!!extraText && (
|
|
300
|
+
<InternalSideNavigationTrigger
|
|
301
|
+
id="__extra_text"
|
|
302
|
+
title={extraText}
|
|
303
|
+
submenuExpanded={undefined}
|
|
304
|
+
dataPrefix="navigation-extra-text"
|
|
305
|
+
tag={undefined}
|
|
306
|
+
icon={undefined}
|
|
307
|
+
iconActive={undefined}
|
|
308
|
+
/>
|
|
309
|
+
)}
|
|
310
|
+
</Fragment>
|
|
311
|
+
) : undefined
|
|
312
|
+
}
|
|
313
|
+
/>
|
|
314
|
+
</NotificationsContextProvider>
|
|
315
|
+
)}
|
|
252
316
|
|
|
253
|
-
{
|
|
317
|
+
{variant.isSequent && (
|
|
318
|
+
<TitanLayoutHeaderObserved heightChange={updateIndicatorsHeight}>
|
|
319
|
+
{header}
|
|
320
|
+
</TitanLayoutHeaderObserved>
|
|
321
|
+
)}
|
|
322
|
+
{variant.isAnvil1 ? (
|
|
323
|
+
<LayoutContentAnvil1 header={header} minWidth={limitContentWidth}>
|
|
324
|
+
{content}
|
|
325
|
+
</LayoutContentAnvil1>
|
|
326
|
+
) : variant.isLegacy ? (
|
|
327
|
+
<LayoutContentLegacy minWidth={limitContentWidth}>
|
|
328
|
+
{content}
|
|
329
|
+
</LayoutContentLegacy>
|
|
330
|
+
) : (
|
|
331
|
+
children
|
|
332
|
+
)}
|
|
254
333
|
</div>
|
|
255
334
|
</LayoutPlacementContext.Provider>
|
|
256
335
|
</LayoutContext.Provider>
|
|
257
336
|
);
|
|
258
337
|
};
|
|
259
338
|
|
|
339
|
+
const TitanLayoutHeaderObserved: FC<{
|
|
340
|
+
children: ReactNode;
|
|
341
|
+
heightChange?(value: number): void;
|
|
342
|
+
}> = ({ children, heightChange }) => {
|
|
343
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
344
|
+
|
|
345
|
+
useEffect(() => {
|
|
346
|
+
if (ref.current) {
|
|
347
|
+
const updatePosition = () => {
|
|
348
|
+
if (ref.current && heightChange) {
|
|
349
|
+
const pos = ref.current.getBoundingClientRect();
|
|
350
|
+
heightChange(pos.height);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const observer = new ResizeObserver(updatePosition);
|
|
355
|
+
observer.observe(ref.current);
|
|
356
|
+
|
|
357
|
+
updatePosition();
|
|
358
|
+
return () => observer.disconnect();
|
|
359
|
+
}
|
|
360
|
+
}, [heightChange]);
|
|
361
|
+
|
|
362
|
+
useEffect(() => {
|
|
363
|
+
return () => {
|
|
364
|
+
heightChange?.(0);
|
|
365
|
+
};
|
|
366
|
+
}, [heightChange]);
|
|
367
|
+
return (
|
|
368
|
+
<div ref={ref} className={Styles.contentHeader} data-cy="layout-content-header">
|
|
369
|
+
{children}
|
|
370
|
+
</div>
|
|
371
|
+
);
|
|
372
|
+
};
|
|
373
|
+
const TitanLayoutHeader: FC<{ children: ReactNode }> = ({ children }) => {
|
|
374
|
+
return (
|
|
375
|
+
<div className={Styles.contentHeader} data-cy="layout-content-header">
|
|
376
|
+
{children}
|
|
377
|
+
</div>
|
|
378
|
+
);
|
|
379
|
+
};
|
|
380
|
+
|
|
260
381
|
export interface TitanLayoutContentProps {
|
|
261
382
|
children: ReactNode;
|
|
262
383
|
}
|
|
263
384
|
const TitanLayoutContent: FC<TitanLayoutContentProps> = ({ children }) => children;
|
|
264
385
|
|
|
386
|
+
const LayoutContentAnvil1: FC<{
|
|
387
|
+
children: ReactNode;
|
|
388
|
+
header?: ReactNode;
|
|
389
|
+
minWidth?: number;
|
|
390
|
+
}> = ({ children, header, minWidth }) => {
|
|
391
|
+
const innerContentStyles: CSSProperties = useMemo(
|
|
392
|
+
() => ({
|
|
393
|
+
...(minWidth ? { minWidth: `${minWidth}px` } : {}),
|
|
394
|
+
}),
|
|
395
|
+
[minWidth]
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
return (
|
|
399
|
+
<Fragment>
|
|
400
|
+
<TitanLayoutHeader>{header}</TitanLayoutHeader>
|
|
401
|
+
<div
|
|
402
|
+
className={classNames(Styles.content, { 'of-x-auto': !!minWidth })}
|
|
403
|
+
data-cy="layout-content"
|
|
404
|
+
>
|
|
405
|
+
<div
|
|
406
|
+
className="position-relative d-f flex-grow-1 flex-basis-0 of-hidden"
|
|
407
|
+
style={innerContentStyles}
|
|
408
|
+
>
|
|
409
|
+
{children}
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
</Fragment>
|
|
413
|
+
);
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
const LayoutContentLegacy: FC<{
|
|
417
|
+
children: ReactNode;
|
|
418
|
+
minWidth: number | undefined;
|
|
419
|
+
}> = ({ children, minWidth }) => {
|
|
420
|
+
const innerContentStyles: CSSProperties = useMemo(
|
|
421
|
+
() => ({
|
|
422
|
+
position: 'relative',
|
|
423
|
+
minWidth: `${minWidth}px`,
|
|
424
|
+
}),
|
|
425
|
+
[minWidth]
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
return (
|
|
429
|
+
<div className={minWidth ? 'of-x-auto flex-basis-0 flex-grow-1' : undefined}>
|
|
430
|
+
<div style={minWidth ? innerContentStyles : undefined}>{children}</div>
|
|
431
|
+
</div>
|
|
432
|
+
);
|
|
433
|
+
};
|
|
434
|
+
|
|
265
435
|
export const TitanLayout = Object.assign(TitanLayoutComponent, {
|
|
266
436
|
Content: TitanLayoutContent,
|
|
267
437
|
Logo: TitanLayoutLogo,
|
|
268
|
-
|
|
269
|
-
|
|
438
|
+
Link: TitanLayoutSidebarLink,
|
|
439
|
+
Trigger: TitanLayoutSidebarTrigger,
|
|
270
440
|
});
|