@servicetitan/navigation 13.0.0 → 13.1.0-canary.259.8b03eb7.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 (36) hide show
  1. package/dist/components/profile-dropdown/profile-dropdown.d.ts.map +1 -1
  2. package/dist/components/profile-dropdown/profile-dropdown.js +1 -0
  3. package/dist/components/profile-dropdown/profile-dropdown.js.map +1 -1
  4. package/dist/components/titan-layout/layout-header-links-internal.d.ts.map +1 -1
  5. package/dist/components/titan-layout/layout-header-links-internal.js +1 -0
  6. package/dist/components/titan-layout/layout-header-links-internal.js.map +1 -1
  7. package/dist/components/titan-layout/layout-header.js +1 -0
  8. package/dist/components/titan-layout/layout-header.js.map +1 -1
  9. package/dist/components/titan-layout/layout-header.module.less +18 -0
  10. package/dist/components/titan-layout/layout-profile.d.ts.map +1 -1
  11. package/dist/components/titan-layout/layout-profile.js +2 -3
  12. package/dist/components/titan-layout/layout-profile.js.map +1 -1
  13. package/dist/components/titan-layout/layout-sidebar-links-internal.d.ts +1 -1
  14. package/dist/components/titan-layout/layout-sidebar-links-internal.d.ts.map +1 -1
  15. package/dist/components/titan-layout/layout-sidebar-links-internal.js +38 -27
  16. package/dist/components/titan-layout/layout-sidebar-links-internal.js.map +1 -1
  17. package/dist/components/titan-layout/layout-sidebar.d.ts +1 -1
  18. package/dist/components/titan-layout/layout-sidebar.d.ts.map +1 -1
  19. package/dist/components/titan-layout/layout-sidebar.js +3 -14
  20. package/dist/components/titan-layout/layout-sidebar.js.map +1 -1
  21. package/dist/components/titan-layout/layout-sidebar.module.less +24 -29
  22. package/dist/components/titan-layout/titan-layout-default.stories.d.ts +1 -0
  23. package/dist/components/titan-layout/titan-layout-default.stories.d.ts.map +1 -1
  24. package/dist/components/titan-layout/titan-layout.js +2 -2
  25. package/dist/components/titan-layout/titan-layout.js.map +1 -1
  26. package/package.json +2 -2
  27. package/src/components/profile-dropdown/profile-dropdown.tsx +1 -0
  28. package/src/components/titan-layout/layout-header-links-internal.tsx +1 -0
  29. package/src/components/titan-layout/layout-header.module.less +18 -0
  30. package/src/components/titan-layout/layout-header.tsx +1 -1
  31. package/src/components/titan-layout/layout-profile.tsx +3 -4
  32. package/src/components/titan-layout/layout-sidebar-links-internal.tsx +54 -40
  33. package/src/components/titan-layout/layout-sidebar.module.less +24 -29
  34. package/src/components/titan-layout/layout-sidebar.tsx +5 -24
  35. package/src/components/titan-layout/titan-layout-default.stories.tsx +17 -1
  36. package/src/components/titan-layout/titan-layout.tsx +2 -2
@@ -94,6 +94,7 @@ export const InternalSideNavigationLink: FC<
94
94
  to={to}
95
95
  isActive={typeof isActive === 'function' ? isActive : undefined}
96
96
  activeClassName={Styles.navItemActive}
97
+ tabIndex={0}
97
98
  >
98
99
  <InternalSideNavigationItemContent
99
100
  icon={icon}
@@ -133,6 +134,7 @@ export const InternalSideNavigationTrigger: FC<
133
134
  <div
134
135
  data-cy={`navigation-item-${id}`}
135
136
  data-pendo={`navigation-item-${id}`}
137
+ tabIndex={rest.onClick ? 0 : -1}
136
138
  {...rest}
137
139
  className={classNames(Styles.navItem, className, {
138
140
  [Styles.navLink]: !!rest.onClick,
@@ -244,7 +246,7 @@ export const InternalSideNavigationGroup: FC<
244
246
  children: ReactNode;
245
247
  submenuExpanded: boolean;
246
248
  onClick?: (e: MouseEvent<never>) => void;
247
- onExpandToggle(e: MouseEvent<never>): void;
249
+ onSubmenuExpand(id: string, expanded: boolean, force: boolean): void;
248
250
  }
249
251
  > = ({
250
252
  id,
@@ -258,46 +260,56 @@ export const InternalSideNavigationGroup: FC<
258
260
 
259
261
  children,
260
262
  submenuExpanded,
261
- onExpandToggle,
263
+ onSubmenuExpand,
262
264
  onClick,
263
265
  navigationComponent,
264
266
  ...rest
265
267
  }) => {
266
- const onRootClickCapture = (e: MouseEvent<never>) => {
267
- if (!submenuExpanded) {
268
- onExpandToggle?.(e);
268
+ const onRootClick = (e: MouseEvent<never>) => {
269
+ if (submenuExpanded) {
270
+ onSubmenuExpand(id, true, true);
271
+ } else {
272
+ onSubmenuExpand(id, true, false);
269
273
  e.preventDefault();
270
274
  e.stopPropagation();
271
275
  }
272
276
  };
277
+ const onToggleClick = (e: MouseEvent<never>) => {
278
+ onSubmenuExpand(id, !submenuExpanded, false);
279
+ e.preventDefault();
280
+ e.stopPropagation();
281
+ };
282
+
273
283
  return (
274
284
  <div className={classNames(Styles.navGroupWrapper)}>
275
- <div className={Styles.navGroupItem} onClickCapture={onRootClickCapture}>
276
- {to ? (
277
- <InternalSideNavigationLink
278
- id={id}
279
- to={to}
280
- title={title}
281
- isActive={isActive}
282
- icon={icon}
283
- iconActive={iconActive}
284
- tag={tag}
285
- className={className}
286
- {...rest}
287
- navigationComponent={navigationComponent}
288
- />
289
- ) : (
290
- <InternalSideNavigationTrigger
291
- id={id}
292
- title={title}
293
- isActive={isActive === true || undefined}
294
- icon={icon}
295
- iconActive={iconActive}
296
- tag={tag}
297
- className={className}
298
- {...rest}
299
- />
300
- )}
285
+ <div className={Styles.navGroupItem}>
286
+ <div onClick={onRootClick}>
287
+ {to ? (
288
+ <InternalSideNavigationLink
289
+ id={id}
290
+ to={to}
291
+ title={title}
292
+ isActive={isActive}
293
+ icon={icon}
294
+ iconActive={iconActive}
295
+ tag={tag}
296
+ className={className}
297
+ {...rest}
298
+ navigationComponent={navigationComponent}
299
+ />
300
+ ) : (
301
+ <InternalSideNavigationTrigger
302
+ id={id}
303
+ title={title}
304
+ isActive={isActive === true || undefined}
305
+ icon={icon}
306
+ iconActive={iconActive}
307
+ tag={tag}
308
+ className={className}
309
+ {...rest}
310
+ />
311
+ )}
312
+ </div>
301
313
 
302
314
  <div className={Styles.navItemGroupToggleWrapper}>
303
315
  {withTooltip(
@@ -306,7 +318,7 @@ export const InternalSideNavigationGroup: FC<
306
318
  size="small"
307
319
  icon={submenuExpanded ? SvgGroupCollapse : SvgGroupExpand}
308
320
  className={Styles.navItemGroupToggle}
309
- onClick={onExpandToggle}
321
+ onClick={onToggleClick}
310
322
  aria-label="Toggle submenu"
311
323
  data-cy="nav-item-group-expand"
312
324
  />,
@@ -315,14 +327,16 @@ export const InternalSideNavigationGroup: FC<
315
327
  )}
316
328
  </div>
317
329
  </div>
318
- <div
319
- className={classNames(Styles.submenuWrapper, {
320
- [Styles.submenuWrapperCollapsed]: !submenuExpanded,
321
- })}
322
- data-cy={`navigation-submenu-${id}`}
323
- >
324
- <div className={Styles.submenu}>{children}</div>
325
- </div>
330
+ {submenuExpanded && (
331
+ <div
332
+ className={classNames(Styles.submenuWrapper, {
333
+ [Styles.submenuWrapperCollapsed]: !submenuExpanded,
334
+ })}
335
+ data-cy={`navigation-submenu-${id}`}
336
+ >
337
+ <div className={Styles.submenu}>{children}</div>
338
+ </div>
339
+ )}
326
340
  </div>
327
341
  );
328
342
  };
@@ -47,11 +47,11 @@
47
47
  }
48
48
  }
49
49
 
50
- .nav-item {
51
- margin-bottom: @spacing-half;
52
- }
53
50
  .nav-top {
54
- margin-bottom: @spacing-half;
51
+ .divider {
52
+ margin-top: 10px;
53
+ margin-bottom: @spacing-half;
54
+ }
55
55
  }
56
56
  .nav-bottom {
57
57
  .divider {
@@ -134,10 +134,7 @@
134
134
  width: 64px;
135
135
  overflow-x: hidden;
136
136
  padding-top: @spacing-1;
137
-
138
- &:not(:first-child) {
139
- margin-top: @spacing-half;
140
- }
137
+ margin-top: @spacing-half;
141
138
 
142
139
  .nav-item-counter[data-anv][data-anv] {
143
140
  position: absolute;
@@ -171,8 +168,13 @@
171
168
  }
172
169
  }
173
170
 
174
- .nav-top .divider {
175
- margin-top: @spacing-1;
171
+ .nav-main {
172
+ padding-top: @spacing-half;
173
+ }
174
+ .nav-top {
175
+ .divider {
176
+ margin-top: 12px;
177
+ }
176
178
  }
177
179
  }
178
180
 
@@ -185,28 +187,15 @@
185
187
  margin-right: @spacing-1;
186
188
  }
187
189
 
188
- .toggle {
189
- .toggle-content {
190
- margin: @spacing-2;
191
-
192
- .toggle-icon-wrapper {
193
- padding-left: @spacing-half;
194
- padding-right: @spacing-half;
195
- }
196
-
197
- .toggle-text {
198
- padding-left: @spacing-0 !important;
199
- }
190
+ .nav-top {
191
+ .divider {
192
+ margin-top: 10px;
193
+ margin-bottom: 6px;
200
194
  }
201
195
  }
202
196
 
203
- .nav-top .divider {
204
- margin-top: @spacing-half;
205
- margin-bottom: @spacing-1;
206
- }
207
-
208
197
  .nav-main {
209
- padding-top: @spacing-1;
198
+ padding-top: 12px;
210
199
  }
211
200
 
212
201
  .nav-group-item {
@@ -232,7 +221,7 @@
232
221
  .nav-wide {
233
222
  .nav-item {
234
223
  flex-direction: row;
235
- margin-bottom: @spacing-half;
224
+ margin-top: @spacing-half;
236
225
 
237
226
  .nav-item-icon-wrapper {
238
227
  flex: 1;
@@ -401,6 +390,12 @@
401
390
  width: 24px;
402
391
  }
403
392
  }
393
+
394
+ &:focus-visible {
395
+ .nav-item-icon-wrapper {
396
+ outline: -webkit-focus-ring-color auto 1px;
397
+ }
398
+ }
404
399
  }
405
400
 
406
401
  .nav-link {
@@ -1,15 +1,7 @@
1
1
  import { Icon, Popover, Text, ThemeProvider } from '@servicetitan/anvil2';
2
2
  import SvgClose from '@servicetitan/anvil2/assets/icons/material/round/close.svg';
3
3
  import classNames from 'classnames';
4
- import {
5
- Children,
6
- FC,
7
- Fragment,
8
- MouseEvent,
9
- ReactElement,
10
- isValidElement,
11
- useCallback,
12
- } from 'react';
4
+ import { Children, FC, Fragment, ReactElement, isValidElement } from 'react';
13
5
  import { NavigationItemData, NavigationSubmenuData } from '../../utils/navigation';
14
6
  import { NavLinkComponentProps } from '../../utils/navigation-context';
15
7
  import { getCounterTag, getSubmenuGroupTag } from '../../utils/side-nav';
@@ -34,7 +26,7 @@ export interface LayoutSidebarProps {
34
26
  navigationComponent: FC<NavLinkComponentProps>;
35
27
  onBarExpandChange(expanded: boolean): void;
36
28
  onDrawerOpenChange(expanded: boolean): void;
37
- onSubmenuExpandChange(id: string, expanded: boolean): void;
29
+ onSubmenuExpandChange(id: string, expanded: boolean, force: boolean): void;
38
30
  }
39
31
 
40
32
  export const LayoutSidebar: FC<LayoutSidebarProps> = ({
@@ -133,19 +125,8 @@ const SideNavigationGroupItem: FC<{
133
125
  navigationComponent: FC<NavLinkComponentProps>;
134
126
  barExpanded: boolean;
135
127
  submenuExpanded: boolean;
136
- onSubmenuExpand: undefined | ((id: string, expanded: boolean) => void);
128
+ onSubmenuExpand: (id: string, expanded: boolean, force: boolean) => void;
137
129
  }> = ({ item, onSubmenuExpand, barExpanded, submenuExpanded, navigationComponent }) => {
138
- const onExpandToggle = useCallback(
139
- (e: MouseEvent<never>) => {
140
- e.preventDefault();
141
- e.stopPropagation();
142
-
143
- if (item.id) {
144
- onSubmenuExpand?.(item.id, !submenuExpanded);
145
- }
146
- },
147
- [item.id, submenuExpanded, onSubmenuExpand]
148
- );
149
130
  const {
150
131
  sidebar: {
151
132
  styles: { popoverContent },
@@ -165,7 +146,7 @@ const SideNavigationGroupItem: FC<{
165
146
  className={item.className}
166
147
  tag={tag}
167
148
  submenuExpanded={submenuExpanded}
168
- onExpandToggle={onExpandToggle}
149
+ onSubmenuExpand={onSubmenuExpand}
169
150
  navigationComponent={navigationComponent}
170
151
  >
171
152
  <SideNavigationGroupContent
@@ -175,7 +156,7 @@ const SideNavigationGroupItem: FC<{
175
156
  />
176
157
  </InternalSideNavigationGroup>
177
158
  ) : (
178
- <Popover placement="right-start" openOnHover delay={500}>
159
+ <Popover placement="right-start" openOnHover delay={300}>
179
160
  <Popover.Trigger>
180
161
  {(triggerProps: any) => (
181
162
  <div {...triggerProps}>
@@ -162,7 +162,6 @@ const SideLinkPopoverWrapper: TitanLayoutLinkWrapper = ({ children, context }) =
162
162
 
163
163
  const sidebarTop = () => [
164
164
  <TitanLayout.Link key="tasks" {...navItems.tasks} />,
165
- <TitanLayout.Link key="calls" {...navItems.calls} />,
166
165
  <TitanLayout.Trigger
167
166
  key="marketing"
168
167
  {...navItems.marketing}
@@ -170,6 +169,7 @@ const sidebarTop = () => [
170
169
  wrapper={SideLinkPopoverWrapper}
171
170
  counter={50}
172
171
  />,
172
+ <TitanLayout.Link key="calls" {...navItems.calls} />,
173
173
  ];
174
174
  const ContentHeader = () => {
175
175
  const [longInfo, setLongInfo] = useState(false);
@@ -350,6 +350,22 @@ export const ContentAnvil2 = (args: LayoutContentArgs) => (
350
350
  </TitanLayout>
351
351
  );
352
352
 
353
+ export const CustomNavigation = (args: LayoutContentArgs) => (
354
+ <TitanLayout
355
+ {...useLayoutProps(args)}
356
+ appearance="anvil1"
357
+ {...useDefaultLayoutProps()}
358
+ logo={{
359
+ title: 'Commercial',
360
+ mantleFill: '#2270EE',
361
+ }}
362
+ >
363
+ <Anvil1Page>
364
+ <Content {...args} />
365
+ </Anvil1Page>
366
+ </TitanLayout>
367
+ );
368
+
353
369
  export const ExtraWithTitle = (args: LayoutContentArgs) => (
354
370
  <TitanLayout
355
371
  {...useLayoutProps(args)}
@@ -205,11 +205,11 @@ function TitanLayoutComponent({
205
205
  [onStateChange, isMobile]
206
206
  );
207
207
  const onSubmenuExpandChange = useCallback(
208
- (id: string, expanded: boolean) => {
208
+ (id: string, expanded: boolean, force: boolean) => {
209
209
  onStateChange?.({
210
210
  navCollapsed: state?.navCollapsed ?? false,
211
211
  submenusExpanded: [
212
- ...(state?.submenusExpanded ?? []).filter(i => i !== id),
212
+ ...(force ? [] : (state?.submenusExpanded ?? []).filter(i => i !== id)),
213
213
  ...(expanded ? [id] : []),
214
214
  ],
215
215
  });