@servicetitan/navigation 11.0.0-canary.237.0c461af.0 → 11.0.0-canary.237.2d7cfa1.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/badge-tag.d.ts +1 -1
  2. package/dist/components/badge-tag.d.ts.map +1 -1
  3. package/dist/components/titan-layout/layout-header.d.ts +2 -0
  4. package/dist/components/titan-layout/layout-header.d.ts.map +1 -1
  5. package/dist/components/titan-layout/layout-header.js +3 -4
  6. package/dist/components/titan-layout/layout-header.js.map +1 -1
  7. package/dist/components/titan-layout/layout-header.module.less +24 -2
  8. package/dist/components/titan-layout/layout-profile.d.ts.map +1 -1
  9. package/dist/components/titan-layout/layout-profile.js +19 -5
  10. package/dist/components/titan-layout/layout-profile.js.map +1 -1
  11. package/dist/components/titan-layout/layout-sidebar-links-internal.d.ts +2 -2
  12. package/dist/components/titan-layout/layout-sidebar-links-internal.d.ts.map +1 -1
  13. package/dist/components/titan-layout/layout-sidebar-links-internal.js +2 -2
  14. package/dist/components/titan-layout/layout-sidebar-links-internal.js.map +1 -1
  15. package/dist/components/titan-layout/layout-sidebar-links.d.ts.map +1 -1
  16. package/dist/components/titan-layout/layout-sidebar-links.js +9 -2
  17. package/dist/components/titan-layout/layout-sidebar-links.js.map +1 -1
  18. package/dist/components/titan-layout/layout-sidebar.d.ts +2 -0
  19. package/dist/components/titan-layout/layout-sidebar.d.ts.map +1 -1
  20. package/dist/components/titan-layout/layout-sidebar.js +6 -4
  21. package/dist/components/titan-layout/layout-sidebar.js.map +1 -1
  22. package/dist/components/titan-layout/layout-sidebar.module.less +16 -3
  23. package/dist/components/titan-layout/notifications-context.d.ts +13 -0
  24. package/dist/components/titan-layout/notifications-context.d.ts.map +1 -0
  25. package/dist/components/titan-layout/notifications-context.js +23 -0
  26. package/dist/components/titan-layout/notifications-context.js.map +1 -0
  27. package/dist/components/titan-layout/titan-layout.d.ts.map +1 -1
  28. package/dist/components/titan-layout/titan-layout.js +24 -14
  29. package/dist/components/titan-layout/titan-layout.js.map +1 -1
  30. package/dist/components/titan-layout/titan-layout.module.less +1 -1
  31. package/dist/components/titan-layout/titan-layout.stories.d.ts +1 -0
  32. package/dist/components/titan-layout/titan-layout.stories.d.ts.map +1 -1
  33. package/dist/components/titan-layout/titan-layout.stories.js +4 -2
  34. package/dist/components/titan-layout/titan-layout.stories.js.map +1 -1
  35. package/dist/test/data.d.ts +4 -1
  36. package/dist/test/data.d.ts.map +1 -1
  37. package/dist/test/data.js +2 -3
  38. package/dist/test/data.js.map +1 -1
  39. package/package.json +2 -2
  40. package/src/components/badge-tag.tsx +1 -1
  41. package/src/components/titan-layout/layout-header.module.less +24 -2
  42. package/src/components/titan-layout/layout-header.tsx +7 -4
  43. package/src/components/titan-layout/layout-profile.tsx +37 -18
  44. package/src/components/titan-layout/layout-sidebar-links-internal.tsx +6 -3
  45. package/src/components/titan-layout/layout-sidebar-links.tsx +11 -2
  46. package/src/components/titan-layout/layout-sidebar.module.less +16 -3
  47. package/src/components/titan-layout/layout-sidebar.module.less.d.ts +1 -0
  48. package/src/components/titan-layout/layout-sidebar.tsx +14 -5
  49. package/src/components/titan-layout/notifications-context.tsx +44 -0
  50. package/src/components/titan-layout/titan-layout.module.less +1 -1
  51. package/src/components/titan-layout/titan-layout.stories.tsx +6 -1
  52. package/src/components/titan-layout/titan-layout.tsx +59 -48
  53. package/src/test/data.tsx +2 -3
@@ -33,9 +33,11 @@ export interface LayoutSidebarProps {
33
33
  mainItems?: NavigationItemData[];
34
34
  barExpanded: boolean;
35
35
  submenuExpanded: string | undefined;
36
+ drawerOpened: boolean;
36
37
  mobile: boolean;
37
38
  navigationComponent: FC<NavLinkComponentProps>;
38
39
  onBarExpandChange(expanded: boolean): void;
40
+ onDrawerOpenChange(expanded: boolean): void;
39
41
  onSubmenuExpandChange(id: string, expanded: boolean): void;
40
42
  }
41
43
 
@@ -44,29 +46,36 @@ export const LayoutSidebar: FC<LayoutSidebarProps> = ({
44
46
  mobile,
45
47
  barExpanded,
46
48
  submenuExpanded,
49
+ drawerOpened,
47
50
  onBarExpandChange,
48
51
  onSubmenuExpandChange,
52
+ onDrawerOpenChange,
49
53
  mainItems,
50
54
  top,
51
55
  bottom,
52
56
  navigationComponent,
53
57
  }) => {
54
- const handleClick = (e: MouseEvent<never>) => {
55
- e.stopPropagation();
58
+ const closeDrawer = () => {
59
+ if (mobile) {
60
+ onDrawerOpenChange?.(false);
61
+ }
56
62
  };
57
63
 
58
64
  return (
59
65
  <LayoutPlacementContext.Provider value="side">
66
+ {mobile && drawerOpened && (
67
+ <div className={Styles.navDrawerBackdrop} onClick={closeDrawer} />
68
+ )}
60
69
  <div
61
70
  className={classNames(
62
71
  Styles.nav,
63
72
  mobile && Styles.navDrawer,
64
- mobile && barExpanded && Styles.navDrawerOpened,
73
+ mobile && drawerOpened && Styles.navDrawerOpened,
65
74
  !mobile && (barExpanded ? Styles.navWide : Styles.navSlim),
66
75
  className
67
76
  )}
68
77
  data-cy="side-navigation"
69
- onClick={handleClick}
78
+ onClick={closeDrawer}
70
79
  >
71
80
  <ThemeProvider mode="dark" className={Styles.navMain}>
72
81
  {mobile && (
@@ -86,7 +95,7 @@ export const LayoutSidebar: FC<LayoutSidebarProps> = ({
86
95
  item.submenu ? (
87
96
  <SideNavigationGroupItem
88
97
  key={item.id}
89
- barExpanded={barExpanded}
98
+ barExpanded={mobile ? barExpanded : drawerOpened}
90
99
  submenuExpanded={!!item.id && submenuExpanded === item.id}
91
100
  onSubmenuExpand={onSubmenuExpandChange}
92
101
  navigationComponent={navigationComponent}
@@ -0,0 +1,44 @@
1
+ import {
2
+ FC,
3
+ PropsWithChildren,
4
+ createContext,
5
+ useCallback,
6
+ useContext,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
10
+
11
+ interface NotificationsContextType {
12
+ onNotificationsUpdate: (id: string, hasNotifications: boolean) => void;
13
+ }
14
+
15
+ const NotificationsContext = createContext<NotificationsContextType>({
16
+ onNotificationsUpdate: () => {},
17
+ });
18
+
19
+ export const useNotificationsContext = () => useContext(NotificationsContext);
20
+
21
+ export const useNotificationsState = () => {
22
+ const [hasNotifications, setHasNotifications] = useState(false);
23
+ const items = useRef(new Set<string>());
24
+ const onNotificationsUpdate = useCallback((id: string, hasNotifications: boolean) => {
25
+ if (hasNotifications && !items.current.has(id)) {
26
+ items.current.add(id);
27
+ setHasNotifications(!!items.current.size);
28
+ } else if (!hasNotifications && items.current.has(id)) {
29
+ items.current.delete(id);
30
+ setHasNotifications(!!items.current.size);
31
+ }
32
+ }, []);
33
+
34
+ const NotificationsContextProvider: FC<PropsWithChildren> = useCallback(
35
+ ({ children }) => (
36
+ <NotificationsContext.Provider value={{ onNotificationsUpdate }}>
37
+ {children}
38
+ </NotificationsContext.Provider>
39
+ ),
40
+ [onNotificationsUpdate]
41
+ );
42
+
43
+ return { NotificationsContextProvider, hasNotifications };
44
+ };
@@ -77,9 +77,9 @@
77
77
 
78
78
  .top {
79
79
  position: fixed;
80
+ width: 100vw;
80
81
  top: 0;
81
82
  left: 0;
82
- right: 0;
83
83
  z-index: 991;
84
84
  }
85
85
 
@@ -27,6 +27,7 @@ interface LayoutContentArgs {
27
27
  search: boolean;
28
28
  longContent: boolean;
29
29
  wideContent: boolean;
30
+ minWidth: boolean;
30
31
  }
31
32
 
32
33
  export default {
@@ -41,6 +42,7 @@ export default {
41
42
  search: true,
42
43
  longContent: true,
43
44
  wideContent: false,
45
+ minWidth: false,
44
46
  } as LayoutContentArgs,
45
47
  };
46
48
 
@@ -68,6 +70,7 @@ const profile = (
68
70
  to="https://google.com"
69
71
  tooltip="Google it"
70
72
  target="_blank"
73
+ tag={{ value: true }}
71
74
  >
72
75
  first link
73
76
  </ProfileDropdown.Link>
@@ -201,13 +204,15 @@ const useLayoutProps = (args: LayoutContentArgs): TitanLayoutProps => {
201
204
  extraText: args.extraText ? 'EST (-8 hrs)' : undefined,
202
205
 
203
206
  sideTop: args.sideTop ? sidebarTop() : undefined,
207
+
208
+ minContentWidth: args.minWidth ? 900 : undefined,
204
209
  };
205
210
  };
206
211
 
207
212
  const Content = (args: LayoutContentArgs) => {
208
213
  return (
209
214
  <Fragment>
210
- <LocationInfo />
215
+ <LocationInfo className="m-b-3" />
211
216
  {args.wideContent && (
212
217
  <div style={{ width: '1200px' }}>
213
218
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
@@ -29,6 +29,7 @@ import { TitanLayoutLogo, TitanLayoutLogoProps } from './layout-logo';
29
29
  import { LayoutSidebar } from './layout-sidebar';
30
30
  import { TitanLayoutSidebarLink, TitanLayoutSidebarTrigger } from './layout-sidebar-links';
31
31
  import { InternalSideNavigationTrigger } from './layout-sidebar-links-internal';
32
+ import { useNotificationsState } from './notifications-context';
32
33
  import * as Styles from './titan-layout.module.less';
33
34
 
34
35
  type TitanLayoutChild = ReactElement<TitanLayoutContentProps> | ReactElement<TitanLayoutLogoProps>;
@@ -143,26 +144,13 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
143
144
  const variant = useVariant(appearance);
144
145
  const [mobileDrawerOpened, setMobileDrawerOpened] = useState(false);
145
146
  const { content, logo } = useLayoutChildren(children);
146
-
147
- useEffect(() => {
148
- if (!isMobile) {
149
- setMobileDrawerOpened(false);
150
- return;
151
- }
152
-
153
- const listener = () => {
154
- setMobileDrawerOpened(false);
155
- };
156
-
157
- document.addEventListener('click', listener);
158
-
159
- return () => document.removeEventListener('click', listener);
160
- }, [isMobile]);
147
+ const { hasNotifications, NotificationsContextProvider } = useNotificationsState();
161
148
 
162
149
  useEffect(() => {
163
150
  if (variant.isAnvil1) {
164
- document.body.classList.add('of-hidden-i');
165
- return () => document.body.classList.remove('of-hidden');
151
+ const bodyClassName = 'of-hidden-i';
152
+ document.body.classList.add(bodyClassName);
153
+ return () => document.body.classList.remove(bodyClassName);
166
154
  }
167
155
  }, [variant.isAnvil1]);
168
156
 
@@ -190,6 +178,24 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
190
178
  },
191
179
  [state, onStateChange]
192
180
  );
181
+ const hasMenuNotifications = useMemo(() => {
182
+ try {
183
+ return (
184
+ navigationMainItems?.some(item => {
185
+ if (item.counter || item.tag?.value) {
186
+ return true;
187
+ } else if (item.submenu) {
188
+ return item.submenu.groups.some(group =>
189
+ group.links.some(link => !!link.counter || !!link.tag?.value)
190
+ );
191
+ }
192
+ return false;
193
+ }) ?? false
194
+ );
195
+ } catch {
196
+ return false;
197
+ }
198
+ }, [navigationMainItems]);
193
199
 
194
200
  const layoutClass = variant.isLegacy
195
201
  ? Styles.layoutLegacy
@@ -224,40 +230,45 @@ const TitanLayoutComponent: FC<TitanLayoutProps> = ({
224
230
  {!isMobile && extraLinks}
225
231
  </Fragment>
226
232
  }
233
+ isMobile={isMobile}
234
+ hasNotifications={hasNotifications || hasMenuNotifications}
227
235
  onBurgerClick={onBurgerClick}
228
236
  />
229
237
 
230
- <LayoutSidebar
231
- className={Styles.side}
232
- mobile={breakpoint.isMobile}
233
- barExpanded={isMobile ? mobileDrawerOpened : !state?.navCollapsed}
234
- submenuExpanded={state?.submenuExpanded}
235
- onBarExpandChange={onBarExpandChange}
236
- onSubmenuExpandChange={onSubmenuExpandChange}
237
- top={sideTop}
238
- mainItems={navigationMainItems}
239
- navigationComponent={context.NavigationComponent}
240
- bottom={
241
- isMobile ? (
242
- <Fragment>
243
- {profile}
244
- {extraLinks}
245
- {!!extraText && (
246
- <InternalSideNavigationTrigger
247
- id="__extra_text"
248
- title={extraText}
249
- submenuExpanded={undefined}
250
- dataPrefix="navigation-extra-text"
251
- tag={undefined}
252
- icon={undefined}
253
- iconActive={undefined}
254
- />
255
- )}
256
- </Fragment>
257
- ) : undefined
258
- }
259
- />
260
-
238
+ <NotificationsContextProvider>
239
+ <LayoutSidebar
240
+ className={Styles.side}
241
+ mobile={breakpoint.isMobile}
242
+ barExpanded={!state?.navCollapsed}
243
+ onBarExpandChange={onBarExpandChange}
244
+ submenuExpanded={state?.submenuExpanded}
245
+ onSubmenuExpandChange={onSubmenuExpandChange}
246
+ drawerOpened={mobileDrawerOpened}
247
+ onDrawerOpenChange={setMobileDrawerOpened}
248
+ top={sideTop}
249
+ mainItems={navigationMainItems}
250
+ navigationComponent={context.NavigationComponent}
251
+ bottom={
252
+ isMobile ? (
253
+ <Fragment>
254
+ {profile}
255
+ {extraLinks}
256
+ {!!extraText && (
257
+ <InternalSideNavigationTrigger
258
+ id="__extra_text"
259
+ title={extraText}
260
+ submenuExpanded={undefined}
261
+ dataPrefix="navigation-extra-text"
262
+ tag={undefined}
263
+ icon={undefined}
264
+ iconActive={undefined}
265
+ />
266
+ )}
267
+ </Fragment>
268
+ ) : undefined
269
+ }
270
+ />
271
+ </NotificationsContextProvider>
261
272
  <LayoutContent
262
273
  header={header}
263
274
  anvil2={variant.isAnvil2}
package/src/test/data.tsx CHANGED
@@ -66,7 +66,6 @@ export const NavLinkMock = forwardRef<any, NavLinkComponentProps>(
66
66
  {...rest}
67
67
  onClick={e => {
68
68
  e.preventDefault();
69
- e.stopPropagation();
70
69
 
71
70
  if (!to.startsWith('http')) {
72
71
  history.replace(to);
@@ -82,10 +81,10 @@ export const NavLinkMock = forwardRef<any, NavLinkComponentProps>(
82
81
  }
83
82
  );
84
83
 
85
- export const LocationInfo = () => {
84
+ export const LocationInfo: FC<{ className?: string }> = ({ className }) => {
86
85
  const location = useLocation();
87
86
 
88
- return <BodyText>current location - {location.pathname}</BodyText>;
87
+ return <BodyText className={className}>current location - {location.pathname}</BodyText>;
89
88
  };
90
89
 
91
90
  const LocationProvider: FC<{ children: any }> = ({ children }) => {