@servicetitan/navigation 11.0.0-canary.237.4d902dc.0 → 11.0.0-canary.237.6ce8e81.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.
Files changed (53) hide show
  1. package/dist/components/header-navigation/header-navigation-stacked.stories.js +1 -1
  2. package/dist/components/header-navigation/header-navigation-stacked.stories.js.map +1 -1
  3. package/dist/components/header-navigation/header-navigation.stories.js +1 -1
  4. package/dist/components/header-navigation/header-navigation.stories.js.map +1 -1
  5. package/dist/components/left-navigation/header-navigation-tiny.stories.js +2 -2
  6. package/dist/components/left-navigation/header-navigation-tiny.stories.js.map +1 -1
  7. package/dist/components/profile-dropdown/profile-dropdown.d.ts +9 -7
  8. package/dist/components/profile-dropdown/profile-dropdown.d.ts.map +1 -1
  9. package/dist/components/profile-dropdown/profile-dropdown.js.map +1 -1
  10. package/dist/components/titan-layout/layout-header.d.ts.map +1 -1
  11. package/dist/components/titan-layout/layout-header.js +1 -1
  12. package/dist/components/titan-layout/layout-header.js.map +1 -1
  13. package/dist/components/titan-layout/layout-header.module.less +37 -16
  14. package/dist/components/titan-layout/layout-profile.d.ts.map +1 -1
  15. package/dist/components/titan-layout/layout-profile.js +13 -4
  16. package/dist/components/titan-layout/layout-profile.js.map +1 -1
  17. package/dist/components/titan-layout/layout-profile.stories.d.ts.map +1 -1
  18. package/dist/components/titan-layout/layout-profile.stories.js +1 -1
  19. package/dist/components/titan-layout/layout-profile.stories.js.map +1 -1
  20. package/dist/components/titan-layout/layout-sidebar-links.d.ts.map +1 -1
  21. package/dist/components/titan-layout/layout-sidebar-links.js +2 -2
  22. package/dist/components/titan-layout/layout-sidebar-links.js.map +1 -1
  23. package/dist/components/titan-layout/layout-sidebar.d.ts +0 -1
  24. package/dist/components/titan-layout/layout-sidebar.d.ts.map +1 -1
  25. package/dist/components/titan-layout/layout-sidebar.js +2 -2
  26. package/dist/components/titan-layout/layout-sidebar.js.map +1 -1
  27. package/dist/components/titan-layout/layout-sidebar.module.less +4 -16
  28. package/dist/components/titan-layout/titan-layout.d.ts +4 -3
  29. package/dist/components/titan-layout/titan-layout.d.ts.map +1 -1
  30. package/dist/components/titan-layout/titan-layout.js +39 -11
  31. package/dist/components/titan-layout/titan-layout.js.map +1 -1
  32. package/dist/components/titan-layout/titan-layout.module.less +47 -16
  33. package/dist/components/titan-layout/titan-layout.stories.d.ts +13 -11
  34. package/dist/components/titan-layout/titan-layout.stories.d.ts.map +1 -1
  35. package/dist/components/titan-layout/titan-layout.stories.js +30 -12
  36. package/dist/components/titan-layout/titan-layout.stories.js.map +1 -1
  37. package/package.json +2 -2
  38. package/src/components/header-navigation/header-navigation-stacked.stories.tsx +1 -1
  39. package/src/components/header-navigation/header-navigation.stories.tsx +1 -1
  40. package/src/components/left-navigation/header-navigation-tiny.stories.tsx +2 -2
  41. package/src/components/profile-dropdown/profile-dropdown.tsx +9 -6
  42. package/src/components/titan-layout/layout-header.module.less +37 -16
  43. package/src/components/titan-layout/layout-header.tsx +5 -1
  44. package/src/components/titan-layout/layout-profile.stories.tsx +9 -0
  45. package/src/components/titan-layout/layout-profile.tsx +18 -6
  46. package/src/components/titan-layout/layout-sidebar-links.tsx +5 -2
  47. package/src/components/titan-layout/layout-sidebar.module.less +4 -16
  48. package/src/components/titan-layout/layout-sidebar.module.less.d.ts +0 -2
  49. package/src/components/titan-layout/layout-sidebar.tsx +1 -4
  50. package/src/components/titan-layout/titan-layout.module.less +47 -16
  51. package/src/components/titan-layout/titan-layout.module.less.d.ts +4 -0
  52. package/src/components/titan-layout/titan-layout.stories.tsx +156 -18
  53. package/src/components/titan-layout/titan-layout.tsx +89 -17
@@ -17,7 +17,10 @@ const WrappedLink: FC<{
17
17
 
18
18
  /** Side Navigation menu link */
19
19
  export function TitanLayoutSidebarLink({ wrapper, ...props }: TitanLayoutSidebarLinkProps) {
20
- const { NavigationComponent } = useTitanLayoutContext();
20
+ const {
21
+ NavigationComponent,
22
+ breakpoint: { isMobile },
23
+ } = useTitanLayoutContext();
21
24
 
22
25
  const element = (
23
26
  <InternalSideNavigationLink
@@ -29,7 +32,7 @@ export function TitanLayoutSidebarLink({ wrapper, ...props }: TitanLayoutSidebar
29
32
  />
30
33
  );
31
34
 
32
- return wrapper ? <WrappedLink wrapper={wrapper}>{element}</WrappedLink> : element;
35
+ return wrapper && !isMobile ? <WrappedLink wrapper={wrapper}>{element}</WrappedLink> : element;
33
36
  }
34
37
 
35
38
  /** Side Navigation menu trigger */
@@ -15,21 +15,6 @@
15
15
  align-items: stretch;
16
16
  }
17
17
 
18
- // desktop version positioned with flex
19
- .nav-flex {
20
- flex-basis: var(--nav-offset-left);
21
- flex-grow: 0;
22
- flex-shrink: 0;
23
- }
24
-
25
- // desktop version positioned with fixed
26
- .nav-fixed {
27
- position: fixed;
28
- top: var(--nav-offset-top);
29
- bottom: 0;
30
- left: 0;
31
- }
32
-
33
18
  // mobile version
34
19
  .nav-drawer {
35
20
  display: none;
@@ -156,7 +141,6 @@
156
141
  // desktop version expanded
157
142
  .nav-wide {
158
143
  width: var(--nav-offset-left);
159
- padding-top: @spacing-2;
160
144
 
161
145
  .toggle {
162
146
  .toggle-content {
@@ -177,6 +161,10 @@
177
161
  margin-top: @spacing-1;
178
162
  margin-bottom: @spacing-1;
179
163
  }
164
+
165
+ .nav-main {
166
+ padding-top: @spacing-1;
167
+ }
180
168
  }
181
169
 
182
170
  .nav-drawer,
@@ -6,8 +6,6 @@ export const navClose: string;
6
6
  export const navCloseWrapper: string;
7
7
  export const navDrawer: string;
8
8
  export const navDrawerOpened: string;
9
- export const navFixed: string;
10
- export const navFlex: string;
11
9
  export const navFooter: string;
12
10
  export const navGroupItem: string;
13
11
  export const navGroupWrapper: string;
@@ -31,7 +31,6 @@ export interface LayoutSidebarProps {
31
31
  top?: ReactElement[];
32
32
  bottom?: ReactElement;
33
33
  mainItems?: NavigationItemData[];
34
- flex: boolean;
35
34
  barExpanded: boolean;
36
35
  submenuExpanded: string | undefined;
37
36
  mobile: boolean;
@@ -42,7 +41,6 @@ export interface LayoutSidebarProps {
42
41
 
43
42
  export const LayoutSidebar: FC<LayoutSidebarProps> = ({
44
43
  className,
45
- flex,
46
44
  mobile,
47
45
  barExpanded,
48
46
  submenuExpanded,
@@ -65,7 +63,6 @@ export const LayoutSidebar: FC<LayoutSidebarProps> = ({
65
63
  mobile && Styles.navDrawer,
66
64
  mobile && barExpanded && Styles.navDrawerOpened,
67
65
  !mobile && (barExpanded ? Styles.navWide : Styles.navSlim),
68
- !mobile && (flex ? Styles.navFlex : Styles.navFixed),
69
66
  className
70
67
  )}
71
68
  data-cy="side-navigation"
@@ -82,7 +79,7 @@ export const LayoutSidebar: FC<LayoutSidebarProps> = ({
82
79
  </div>
83
80
  </div>
84
81
  )}
85
- {top?.length && <SidebarTop>{top}</SidebarTop>}
82
+ {!!top?.length && <SidebarTop>{top}</SidebarTop>}
86
83
 
87
84
  <div data-cy="navigation-items">
88
85
  {mainItems?.map(item =>
@@ -8,30 +8,53 @@
8
8
  @bg-color-hover: rgba(255, 255, 255, 0.08);
9
9
  @bg-color-active: rgba(120, 187, 250, 0.2);
10
10
 
11
- .layout-legacy {
12
- padding-left: var(--nav-offset-left);
13
- }
14
-
15
11
  .layout-anvil1 {
16
- display: block;
17
- }
12
+ height: calc(100vh - var(--nav-offset-top));
13
+ margin-top: var(--nav-offset-top);
18
14
 
19
- .layout-anvil2 {
20
- display: block;
15
+ display: flex;
16
+ flex-direction: row;
17
+
18
+ .side {
19
+ flex-basis: var(--nav-offset-left);
20
+ flex-grow: 0;
21
+ flex-shrink: 0;
22
+ }
23
+
24
+ .content {
25
+ flex-grow: 1;
26
+
27
+ display: flex;
28
+ flex-direction: column;
29
+ overflow: hidden;
30
+ }
21
31
  }
22
32
 
23
- .layout-legacy,
24
33
  .layout-anvil2 {
25
- .top-placeholder {
26
- height: var(--nav-offset-top);
34
+ .header {
35
+ position: sticky;
36
+ top: var(--nav-offset-top);
37
+ z-index: 989;
27
38
  }
39
+ }
28
40
 
29
- .top {
41
+ .layout-desktop {
42
+ padding-left: var(--nav-offset-left);
43
+
44
+ .side {
30
45
  position: fixed;
31
- top: 0;
46
+ top: var(--nav-offset-top);
47
+ bottom: 0;
32
48
  left: 0;
33
49
  right: 0;
34
- z-index: 991;
50
+ z-index: @z-index-global-nav;
51
+ }
52
+ }
53
+
54
+ .layout-legacy,
55
+ .layout-anvil2 {
56
+ .top-placeholder {
57
+ height: var(--nav-offset-top);
35
58
  }
36
59
  }
37
60
 
@@ -43,11 +66,19 @@
43
66
  --nav-offset-left: 0;
44
67
  }
45
68
 
46
- &.layout-nav-slim {
69
+ &.layout-desktop.layout-nav-slim {
47
70
  --nav-offset-left: 64px;
48
71
  }
49
72
 
50
- &.layout-nav-wide {
73
+ &.layout-desktop.layout-nav-wide {
51
74
  --nav-offset-left: 212px;
52
75
  }
76
+
77
+ .top {
78
+ position: fixed;
79
+ top: 0;
80
+ left: 0;
81
+ right: 0;
82
+ z-index: 991;
83
+ }
53
84
  }
@@ -1,11 +1,15 @@
1
1
  export const __esModule: true;
2
+ export const content: string;
3
+ export const header: string;
2
4
  export const layout: string;
3
5
  export const layoutAnvil1: string;
4
6
  export const layoutAnvil2: string;
7
+ export const layoutDesktop: string;
5
8
  export const layoutLegacy: string;
6
9
  export const layoutMobile: string;
7
10
  export const layoutNavSlim: string;
8
11
  export const layoutNavWide: string;
12
+ export const side: string;
9
13
  export const top: string;
10
14
  export const topPlaceholder: string;
11
15
 
@@ -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,28 @@ 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
+ }
31
+
23
32
  export default {
24
33
  title: 'Navigation/TitanLayout',
25
- component: TitanLayout,
26
34
  decorators: [withDefaultRedirects, withMemoryRouter, withAnvil],
27
35
  parameters: {},
36
+ argTypes: {},
37
+ args: {
38
+ header: true,
39
+ sideTop: true,
40
+ extraText: true,
41
+ search: true,
42
+ longContent: true,
43
+ wideContent: false,
44
+ } as LayoutContentArgs,
28
45
  };
29
46
 
30
47
  const mainNavItems = [
@@ -131,9 +148,9 @@ const SideLinkPopoverWrapper: FC<SideNavigationLinkWrapperProps> = ({ children,
131
148
  };
132
149
 
133
150
  const sidebarTop = () => [
134
- <TitanLayout.SidebarLink key="tasks" {...items.tasks} />,
135
- <TitanLayout.SidebarLink key="calls" {...items.calls} />,
136
- <TitanLayout.SidebarTrigger
151
+ <TitanLayout.Link key="tasks" {...items.tasks} />,
152
+ <TitanLayout.Link key="calls" {...items.calls} />,
153
+ <TitanLayout.Trigger
137
154
  key="marketing"
138
155
  {...items.marketing}
139
156
  isActive={false}
@@ -142,51 +159,172 @@ const sidebarTop = () => [
142
159
  counter={50}
143
160
  />,
144
161
  ];
145
- const useLayoutProps = (): TitanLayoutProps => {
162
+ const ContentHeader = () => {
163
+ const [longInfo, setLongInfo] = useState(false);
164
+
165
+ return (
166
+ <Fragment>
167
+ <Announcement title="Some info" status="info" />
168
+ <Announcement title="Some warning" status="warning" />
169
+ <div
170
+ className="d-f justify-content-center align-items-center bg-purple-100-i"
171
+ style={{ height: longInfo ? '120px' : '48px' }}
172
+ >
173
+ <div className="d-f align-items-center gap-1">
174
+ custom content{' '}
175
+ <Button onClick={() => setLongInfo(!longInfo)} size="small" aria-label="test">
176
+ {longInfo ? '↑' : '↓'}
177
+ </Button>
178
+ </div>
179
+ </div>
180
+ </Fragment>
181
+ );
182
+ };
183
+ const SearchBar = () => <TextField size="small" placeholder="Search" className="w-100-i" />;
184
+
185
+ const useLayoutProps = (args: LayoutContentArgs): TitanLayoutProps => {
146
186
  const [state, setState] = useState<TitanLayoutState | undefined>(undefined);
147
187
 
148
188
  return {
149
189
  state,
150
190
  onStateChange: setState,
151
191
 
192
+ navigationComponent: NavLinkMock,
152
193
  navigationMainItems: mainNavItems,
194
+
153
195
  profile,
196
+ top: args.search ? <SearchBar /> : undefined,
197
+ header: args.header ? <ContentHeader /> : undefined,
198
+
154
199
  extraLinks,
155
200
  extraLinksTop,
201
+ extraText: args.extraText ? 'EST (-8 hrs)' : undefined,
156
202
 
157
- sidebarTop: sidebarTop(),
158
- navigationComponent: NavLinkMock,
159
- extraText: 'EST (-8 hrs)',
203
+ sideTop: args.sideTop ? sidebarTop() : undefined,
160
204
  };
161
205
  };
162
206
 
163
- export const TitanLayoutLegacy = () => (
164
- <TitanLayout {...useLayoutProps()} appearance="legacy">
207
+ const Content = (args: LayoutContentArgs) => {
208
+ return (
209
+ <Fragment>
210
+ <LocationInfo />
211
+ {args.wideContent && (
212
+ <div style={{ width: '1200px' }}>
213
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
214
+ incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
215
+ nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
216
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
217
+ fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
218
+ culpa qui officia deserunt mollit anim id est laborum.
219
+ </div>
220
+ )}
221
+
222
+ {args.longContent && (
223
+ <div>
224
+ <p>Lorem</p>
225
+ <p>ipsum</p>
226
+ <p>dolor</p>
227
+ <p>sit</p>
228
+ <p>amet,</p>
229
+ <p>consectetur</p>
230
+ <p>adipiscing</p>
231
+ <p>elit,</p>
232
+ <p>sed</p>
233
+ <p>do</p>
234
+ <p>eiusmod</p>
235
+ <p>tempor</p>
236
+ <p>incididunt</p>
237
+ <p>ut</p>
238
+ <p>labore</p>
239
+ <p>et</p>
240
+ <p>dolore</p>
241
+ <p>magna</p>
242
+ <p>aliqua.</p>
243
+ <p>Ut</p>
244
+ <p>enim</p>
245
+ <p>ad</p>
246
+ <p>minim</p>
247
+ <p>veniam,</p>
248
+ <p>quis</p>
249
+ <p>nostrud</p>
250
+ <p>exercitation</p>
251
+ <p>ullamco</p>
252
+ <p>laboris</p>
253
+ <p>nisi</p>
254
+ <p>ut</p>
255
+ <p>aliquip</p>
256
+ <p>ex</p>
257
+ <p>ea</p>
258
+ <p>commodo</p>
259
+ <p>consequat.</p>
260
+ <p>Duis</p>
261
+ <p>aute</p>
262
+ <p>irure</p>
263
+ <p>dolor</p>
264
+ <p>in</p>
265
+ <p>reprehenderit</p>
266
+ <p>in</p>
267
+ <p>voluptate</p>
268
+ <p>velit</p>
269
+ <p>esse</p>
270
+ <p>cillum</p>
271
+ <p>dolore</p>
272
+ <p>eu</p>
273
+ <p>fugiat</p>
274
+ <p>nulla</p>
275
+ <p>pariatur.</p>
276
+ <p>Excepteur</p>
277
+ <p>sint</p>
278
+ <p>occaecat</p>
279
+ <p>cupidatat</p>
280
+ <p>non</p>
281
+ <p>proident,</p>
282
+ <p>sunt</p>
283
+ <p>in</p>
284
+ <p>culpa</p>
285
+ <p>qui</p>
286
+ <p>officia</p>
287
+ <p>deserunt</p>
288
+ <p>mollit</p>
289
+ <p>anim</p>
290
+ <p>id</p>
291
+ <p>est</p>
292
+ <p>laborum.</p>
293
+ </div>
294
+ )}
295
+ </Fragment>
296
+ );
297
+ };
298
+
299
+ export const TitanLayoutLegacy = (args: LayoutContentArgs) => (
300
+ <TitanLayout {...useLayoutProps(args)} appearance="legacy">
165
301
  <TitanLayout.Logo title />
166
302
  <TitanLayout.Content>
167
- <LocationInfo />
303
+ <div className="p-3">
304
+ <Content {...args} />
305
+ </div>
168
306
  </TitanLayout.Content>
169
307
  </TitanLayout>
170
308
  );
171
309
 
172
- export const TitanLayoutAnvil1 = () => (
173
- <TitanLayout {...useLayoutProps()} appearance="anvil1">
310
+ export const TitanLayoutAnvil1 = (args: LayoutContentArgs) => (
311
+ <TitanLayout {...useLayoutProps(args)} appearance="anvil1">
174
312
  <TitanLayout.Logo title />
175
313
  <TitanLayout.Content>
176
314
  <Anvil1Page>
177
- <LocationInfo />
315
+ <Content {...args} />
178
316
  </Anvil1Page>
179
317
  </TitanLayout.Content>
180
318
  </TitanLayout>
181
319
  );
182
320
 
183
- export const TitanLayoutAnvil2 = () => (
184
- <TitanLayout {...useLayoutProps()} appearance="anvil2">
321
+ export const TitanLayoutAnvil2 = (args: LayoutContentArgs) => (
322
+ <TitanLayout {...useLayoutProps(args)} appearance="anvil2">
185
323
  <TitanLayout.Logo title />
186
324
  <TitanLayout.Content>
187
325
  <Anvil2Page>
188
326
  <Anvil2Page.Content>
189
- <LocationInfo />
327
+ <Content {...args} />
190
328
  </Anvil2Page.Content>
191
329
  </Anvil2Page>
192
330
  </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';
@@ -50,11 +51,12 @@ export type TitanLayoutProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'
50
51
  onStateChange?: (state: TitanLayoutState) => void;
51
52
 
52
53
  header?: ReactElement;
54
+ top?: ReactElement;
55
+ sideTop?: ReactElement[];
53
56
  profile?: ReactElement;
54
57
  extraLinks?: ReactElement;
55
58
  extraLinksTop?: ReactElement;
56
59
  extraText?: string;
57
- sidebarTop?: ReactElement[];
58
60
  };
59
61
 
60
62
  const defaultSidebarContext: TitanLayoutSidebarContextType = {
@@ -76,7 +78,7 @@ const useVariant = (appearance: TitanLayoutProps['appearance']) =>
76
78
  isLegacy,
77
79
  isAnvil1,
78
80
  isAnvil2,
79
- isSequent: isLegacy || isAnvil1,
81
+ isSequent: isLegacy || isAnvil2,
80
82
  };
81
83
  }, [appearance]);
82
84
 
@@ -115,6 +117,7 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
115
117
  children,
116
118
  navigationComponent,
117
119
  header,
120
+ top,
118
121
  profile,
119
122
  state,
120
123
  onStateChange,
@@ -122,7 +125,7 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
122
125
  extraLinks,
123
126
  extraLinksTop,
124
127
  extraText,
125
- sidebarTop,
128
+ sideTop,
126
129
  }) => {
127
130
  const breakpoint = useTitanBreakpoint();
128
131
  const isMobile = breakpoint.isMobile;
@@ -179,8 +182,6 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
179
182
  [state, onStateChange]
180
183
  );
181
184
 
182
- const [layoutStyles] = useState<object>({});
183
-
184
185
  const layoutClass = variant.isLegacy
185
186
  ? Styles.layoutLegacy
186
187
  : variant.isAnvil1
@@ -194,21 +195,19 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
194
195
  id={id}
195
196
  className={classNames(
196
197
  Styles.layout,
197
- isMobile
198
- ? Styles.layoutMobile
199
- : state?.navCollapsed
200
- ? Styles.layoutNavSlim
201
- : Styles.layoutNavWide,
198
+ isMobile ? Styles.layoutMobile : Styles.layoutDesktop,
199
+ !isMobile && state?.navCollapsed
200
+ ? Styles.layoutNavSlim
201
+ : Styles.layoutNavWide,
202
202
  layoutClass
203
203
  )}
204
- style={layoutStyles}
205
204
  >
206
205
  {variant.isSequent && <div className={Styles.topPlaceholder} />}
207
206
  <LayoutHeader
208
207
  className={Styles.top}
209
208
  logo={logo}
210
209
  profile={isMobile ? undefined : profile}
211
- center={header}
210
+ center={top}
212
211
  rightText={isMobile ? undefined : extraText}
213
212
  right={
214
213
  <Fragment>
@@ -220,13 +219,13 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
220
219
  />
221
220
 
222
221
  <LayoutSidebar
223
- flex={!variant.isSequent}
222
+ className={Styles.side}
224
223
  mobile={breakpoint.isMobile}
225
224
  barExpanded={isMobile ? mobileDrawerOpened : !state?.navCollapsed}
226
225
  submenuExpanded={state?.submenuExpanded}
227
226
  onBarExpandChange={onBarExpandChange}
228
227
  onSubmenuExpandChange={onSubmenuExpandChange}
229
- top={sidebarTop}
228
+ top={sideTop}
230
229
  mainItems={navigationMainItems}
231
230
  navigationComponent={context.NavigationComponent}
232
231
  bottom={
@@ -250,21 +249,94 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
250
249
  }
251
250
  />
252
251
 
253
- {content}
252
+ <LayoutContent
253
+ header={header}
254
+ anvil2={variant.isAnvil2}
255
+ anvil1={variant.isAnvil1}
256
+ >
257
+ {content}
258
+ </LayoutContent>
254
259
  </div>
255
260
  </LayoutPlacementContext.Provider>
256
261
  </LayoutContext.Provider>
257
262
  );
258
263
  };
259
264
 
265
+ const TitanLayoutHeaderObserved: FC<{
266
+ children: ReactNode;
267
+ heightChange?(value: number): void;
268
+ }> = ({ children, heightChange }) => {
269
+ const ref = useRef<HTMLDivElement>(null);
270
+
271
+ useEffect(() => {
272
+ if (ref.current) {
273
+ const updatePosition = () => {
274
+ if (ref.current && heightChange) {
275
+ const pos = ref.current.getBoundingClientRect();
276
+ heightChange(pos.height);
277
+ }
278
+ };
279
+
280
+ const observer = new ResizeObserver(updatePosition);
281
+ observer.observe(ref.current);
282
+
283
+ updatePosition();
284
+ return () => observer.disconnect();
285
+ }
286
+ }, [heightChange]);
287
+
288
+ useEffect(() => {
289
+ return () => {
290
+ heightChange?.(0);
291
+ };
292
+ }, [heightChange]);
293
+ return (
294
+ <div ref={ref} className={Styles.header}>
295
+ {children}
296
+ </div>
297
+ );
298
+ };
299
+
260
300
  export interface TitanLayoutContentProps {
261
301
  children: ReactNode;
262
302
  }
263
303
  const TitanLayoutContent: FC<TitanLayoutContentProps> = ({ children }) => children;
264
304
 
305
+ const LayoutContent: FC<{
306
+ children: ReactNode;
307
+ header?: ReactNode;
308
+ anvil1: boolean;
309
+ anvil2: boolean;
310
+ }> = ({ anvil1, anvil2, children, header }) => {
311
+ const [anvil2Styles, setAnvil2Styles] = useState<object>({});
312
+ const updateIndicatorsHeight = useCallback((offset: number) => {
313
+ setAnvil2Styles({ '--offset': `calc(var(--nav-offset-top) + ${offset}px)` });
314
+ }, []);
315
+
316
+ return (
317
+ <div className={Styles.content} style={anvil2Styles}>
318
+ {!!header &&
319
+ (anvil2 ? (
320
+ <TitanLayoutHeaderObserved heightChange={updateIndicatorsHeight}>
321
+ {header}
322
+ </TitanLayoutHeaderObserved>
323
+ ) : (
324
+ header
325
+ ))}
326
+ {anvil1 ? (
327
+ <div className="position-relative d-f flex-grow-1 flex-basis-0 of-hidden">
328
+ {children}
329
+ </div>
330
+ ) : (
331
+ children
332
+ )}
333
+ </div>
334
+ );
335
+ };
336
+
265
337
  export const TitanLayout = Object.assign(TitanLayoutComponent, {
266
338
  Content: TitanLayoutContent,
267
339
  Logo: TitanLayoutLogo,
268
- SidebarLink: TitanLayoutSidebarLink,
269
- SidebarTrigger: TitanLayoutSidebarTrigger,
340
+ Link: TitanLayoutSidebarLink,
341
+ Trigger: TitanLayoutSidebarTrigger,
270
342
  });