@servicetitan/navigation 14.2.0 → 14.3.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/titan-layout/__tests__/layout-sidebar-links-internal.test.d.ts +2 -0
- package/dist/components/titan-layout/__tests__/layout-sidebar-links-internal.test.d.ts.map +1 -0
- package/dist/components/titan-layout/__tests__/navigation-catalog-reset.test.d.ts +2 -0
- package/dist/components/titan-layout/__tests__/navigation-catalog-reset.test.d.ts.map +1 -0
- package/dist/components/titan-layout/interface.d.ts +66 -0
- package/dist/components/titan-layout/interface.d.ts.map +1 -1
- package/dist/components/titan-layout/interface.js.map +1 -1
- package/dist/components/titan-layout/layout-sidebar-links-internal.d.ts +3 -0
- package/dist/components/titan-layout/layout-sidebar-links-internal.d.ts.map +1 -1
- package/dist/components/titan-layout/layout-sidebar-links-internal.js +58 -13
- package/dist/components/titan-layout/layout-sidebar-links-internal.js.map +1 -1
- package/dist/components/titan-layout/layout-sidebar.d.ts +14 -0
- package/dist/components/titan-layout/layout-sidebar.d.ts.map +1 -1
- package/dist/components/titan-layout/layout-sidebar.js +169 -97
- package/dist/components/titan-layout/layout-sidebar.js.map +1 -1
- package/dist/components/titan-layout/layout-sidebar.module.less +230 -144
- package/dist/components/titan-layout/layout-sidebar.module.less.d.ts +16 -5
- package/dist/components/titan-layout/navigation-catalog/index.d.ts +3 -0
- package/dist/components/titan-layout/navigation-catalog/index.d.ts.map +1 -0
- package/dist/components/titan-layout/navigation-catalog/index.js +4 -0
- package/dist/components/titan-layout/navigation-catalog/index.js.map +1 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog-footer.d.ts +8 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog-footer.d.ts.map +1 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog-footer.js +88 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog-footer.js.map +1 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.d.ts +21 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.d.ts.map +1 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.js +138 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.js.map +1 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.module.less +89 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.module.less.d.ts +9 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.d.ts +14 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.d.ts.map +1 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.js +382 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.js.map +1 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.module.less +436 -0
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.module.less.d.ts +24 -0
- package/dist/components/titan-layout/navigation-scroll-indicators.d.ts +12 -0
- package/dist/components/titan-layout/navigation-scroll-indicators.d.ts.map +1 -0
- package/dist/components/titan-layout/navigation-scroll-indicators.js +48 -0
- package/dist/components/titan-layout/navigation-scroll-indicators.js.map +1 -0
- package/dist/components/titan-layout/titan-layout.d.ts +29 -1
- package/dist/components/titan-layout/titan-layout.d.ts.map +1 -1
- package/dist/components/titan-layout/titan-layout.js +46 -6
- package/dist/components/titan-layout/titan-layout.js.map +1 -1
- package/dist/components/titan-layout/titan-layout.module.less +2 -1
- package/dist/components/titan-layout/use-navigation-scroll.d.ts +20 -0
- package/dist/components/titan-layout/use-navigation-scroll.d.ts.map +1 -0
- package/dist/components/titan-layout/use-navigation-scroll.js +140 -0
- package/dist/components/titan-layout/use-navigation-scroll.js.map +1 -0
- package/dist/utils/__tests__/navigation-notifications.test.d.ts +2 -0
- package/dist/utils/__tests__/navigation-notifications.test.d.ts.map +1 -0
- package/dist/utils/__tests__/submenu-state.test.d.ts +2 -0
- package/dist/utils/__tests__/submenu-state.test.d.ts.map +1 -0
- package/dist/utils/navigation-notifications.d.ts +21 -0
- package/dist/utils/navigation-notifications.d.ts.map +1 -0
- package/dist/utils/navigation-notifications.js +39 -0
- package/dist/utils/navigation-notifications.js.map +1 -0
- package/dist/utils/side-nav.js +3 -1
- package/dist/utils/side-nav.js.map +1 -1
- package/dist/utils/submenu-state.d.ts +3 -0
- package/dist/utils/submenu-state.d.ts.map +1 -0
- package/dist/utils/submenu-state.js +15 -0
- package/dist/utils/submenu-state.js.map +1 -0
- package/package.json +2 -2
- package/src/components/titan-layout/__tests__/layout-sidebar-links-internal.test.tsx +225 -0
- package/src/components/titan-layout/__tests__/navigation-catalog-reset.test.tsx +186 -0
- package/src/components/titan-layout/interface.ts +73 -0
- package/src/components/titan-layout/layout-sidebar-links-internal.tsx +86 -14
- package/src/components/titan-layout/layout-sidebar.module.less +230 -144
- package/src/components/titan-layout/layout-sidebar.module.less.d.ts +16 -5
- package/src/components/titan-layout/layout-sidebar.tsx +193 -117
- package/src/components/titan-layout/navigation-catalog/index.ts +2 -0
- package/src/components/titan-layout/navigation-catalog/navigation-catalog-footer.tsx +88 -0
- package/src/components/titan-layout/navigation-catalog/navigation-catalog-trigger.module.less +89 -0
- package/src/components/titan-layout/navigation-catalog/navigation-catalog-trigger.module.less.d.ts +9 -0
- package/src/components/titan-layout/navigation-catalog/navigation-catalog-trigger.tsx +170 -0
- package/src/components/titan-layout/navigation-catalog/navigation-catalog.module.less +436 -0
- package/src/components/titan-layout/navigation-catalog/navigation-catalog.module.less.d.ts +24 -0
- package/src/components/titan-layout/navigation-catalog/navigation-catalog.tsx +458 -0
- package/src/components/titan-layout/navigation-scroll-indicators.tsx +80 -0
- package/src/components/titan-layout/titan-layout.module.less +2 -1
- package/src/components/titan-layout/titan-layout.tsx +81 -5
- package/src/components/titan-layout/use-navigation-scroll.ts +145 -0
- package/src/utils/__tests__/navigation-notifications.test.ts +70 -0
- package/src/utils/__tests__/submenu-state.test.ts +39 -0
- package/src/utils/navigation-notifications.ts +59 -0
- package/src/utils/side-nav.ts +1 -1
- package/src/utils/submenu-state.ts +16 -0
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { DefaultNavLinkComponent } from '../../utils/navigation-context';
|
|
5
|
+
import { updateOpenSubmenus } from '../../utils/submenu-state';
|
|
5
6
|
import { useTitanBreakpoint } from '../../utils/use-breakpoint';
|
|
6
7
|
import { LayoutContext, LayoutPlacementContext } from './layout-context';
|
|
7
8
|
import { LayoutHeader } from './layout-header';
|
|
@@ -42,7 +43,7 @@ export const useViewportConfigs = (noResponsive)=>{
|
|
|
42
43
|
noResponsive
|
|
43
44
|
]);
|
|
44
45
|
};
|
|
45
|
-
function TitanLayoutComponent({ appearance = 'anvil2', navVariant = 'left', id, children, contentOnly, navigationComponent, header, top, profile, expanded, openSubmenus, logo, onExpandedChange, onOpenSubmenuChange, navigationMainItems, navigationOverflowItems, onNavigationLinkClick, extraLinks, extraLinksTop, extraText, minContentWidth, noResponsive, sideTop }) {
|
|
46
|
+
function TitanLayoutComponent({ appearance = 'anvil2', navVariant = 'left', id, children, contentOnly, navigationComponent, header, top, profile, expanded, openSubmenus, logo, onExpandedChange, onOpenSubmenuChange, onOpenSubmenusChange, navigationMainItems, pinnedNavigation, navigationOverflowItems, onNavigationLinkClick, extraLinks, extraLinksTop, extraText, minContentWidth, noResponsive, sideTop, sideTopItems, navigationScrollIndicators = true }) {
|
|
46
47
|
const breakpoint = useTitanBreakpoint(noResponsive);
|
|
47
48
|
const context = useMemo(()=>({
|
|
48
49
|
NavigationComponent: navigationComponent !== null && navigationComponent !== void 0 ? navigationComponent : DefaultNavLinkComponent,
|
|
@@ -58,7 +59,7 @@ function TitanLayoutComponent({ appearance = 'anvil2', navVariant = 'left', id,
|
|
|
58
59
|
const { hasNotifications, NotificationsContextProvider } = useNotificationsState();
|
|
59
60
|
const [headerHeight, setHeaderHeight] = useState(0);
|
|
60
61
|
const isMobile = breakpoint.isMobile;
|
|
61
|
-
const hasSideBar = !contentOnly && (navVariant === 'left' || navVariant === 'top' && isMobile) && (!!(navigationMainItems === null || navigationMainItems === void 0 ? void 0 : navigationMainItems.length) || !!(sideTop === null || sideTop === void 0 ? void 0 : sideTop.length));
|
|
62
|
+
const hasSideBar = !contentOnly && (navVariant === 'left' || navVariant === 'top' && isMobile) && (!!(navigationMainItems === null || navigationMainItems === void 0 ? void 0 : navigationMainItems.length) || !!(sideTop === null || sideTop === void 0 ? void 0 : sideTop.length) || !!(sideTopItems === null || sideTopItems === void 0 ? void 0 : sideTopItems.length) || !!pinnedNavigation);
|
|
62
63
|
const hasTopBar = !contentOnly;
|
|
63
64
|
useEffect(()=>{
|
|
64
65
|
if (view.isAnvil1) {
|
|
@@ -92,9 +93,29 @@ function TitanLayoutComponent({ appearance = 'anvil2', navVariant = 'left', id,
|
|
|
92
93
|
isMobile
|
|
93
94
|
]);
|
|
94
95
|
const onSubmenuOpenChange = useCallback((id, opened)=>{
|
|
95
|
-
|
|
96
|
+
if (onOpenSubmenusChange) {
|
|
97
|
+
onOpenSubmenusChange(updateOpenSubmenus(openSubmenus !== null && openSubmenus !== void 0 ? openSubmenus : [], [
|
|
98
|
+
id
|
|
99
|
+
], opened));
|
|
100
|
+
} else {
|
|
101
|
+
onOpenSubmenuChange === null || onOpenSubmenuChange === void 0 ? void 0 : onOpenSubmenuChange(id, opened);
|
|
102
|
+
}
|
|
103
|
+
}, [
|
|
104
|
+
onOpenSubmenuChange,
|
|
105
|
+
onOpenSubmenusChange,
|
|
106
|
+
openSubmenus
|
|
107
|
+
]);
|
|
108
|
+
/*
|
|
109
|
+
* Bulk actions require `onOpenSubmenusChange`, which takes the whole next set at once.
|
|
110
|
+
* They are deliberately not emulated with `onOpenSubmenuChange`: that contract is one
|
|
111
|
+
* call per user action, so a host deriving its next list from the current one would see
|
|
112
|
+
* the same stale list on every call of a loop and keep only the last module. `Expand All`
|
|
113
|
+
* is hidden entirely when a host supplies only the per-module callback.
|
|
114
|
+
*/ const onSubmenusOpenChange = useCallback((ids, opened)=>{
|
|
115
|
+
onOpenSubmenusChange === null || onOpenSubmenusChange === void 0 ? void 0 : onOpenSubmenusChange(updateOpenSubmenus(openSubmenus !== null && openSubmenus !== void 0 ? openSubmenus : [], ids, opened));
|
|
96
116
|
}, [
|
|
97
|
-
|
|
117
|
+
onOpenSubmenusChange,
|
|
118
|
+
openSubmenus
|
|
98
119
|
]);
|
|
99
120
|
const sidebarNavItems = useMemo(()=>{
|
|
100
121
|
if (!hasSideBar) {
|
|
@@ -113,10 +134,25 @@ function TitanLayoutComponent({ appearance = 'anvil2', navVariant = 'left', id,
|
|
|
113
134
|
navigationOverflowItems,
|
|
114
135
|
navVariant
|
|
115
136
|
]);
|
|
137
|
+
/*
|
|
138
|
+
* The burger badge speaks for the whole drawer, so it has to count what sits above the
|
|
139
|
+
* divider as well. A permanent destination asking for attention would otherwise go unseen
|
|
140
|
+
* while the drawer is closed.
|
|
141
|
+
*/ const badgeNavItems = useMemo(/*
|
|
142
|
+
* Gated on `hasSideBar`, not `sidebarNavItems`: on the left variant a rail that exists
|
|
143
|
+
* only for `sideTopItems` still has `sidebarNavItems` (i.e. `navigationMainItems`) undefined.
|
|
144
|
+
*/ ()=>hasSideBar ? [
|
|
145
|
+
...sideTopItems !== null && sideTopItems !== void 0 ? sideTopItems : [],
|
|
146
|
+
...sidebarNavItems !== null && sidebarNavItems !== void 0 ? sidebarNavItems : []
|
|
147
|
+
] : undefined, [
|
|
148
|
+
hasSideBar,
|
|
149
|
+
sideTopItems,
|
|
150
|
+
sidebarNavItems
|
|
151
|
+
]);
|
|
116
152
|
const hasMenuNotifications = useMemo(()=>{
|
|
117
153
|
try {
|
|
118
154
|
var _ref;
|
|
119
|
-
return (_ref =
|
|
155
|
+
return (_ref = badgeNavItems === null || badgeNavItems === void 0 ? void 0 : badgeNavItems.some((item)=>{
|
|
120
156
|
var _item_tag;
|
|
121
157
|
if (item.counter || ((_item_tag = item.tag) === null || _item_tag === void 0 ? void 0 : _item_tag.value)) {
|
|
122
158
|
return true;
|
|
@@ -132,7 +168,7 @@ function TitanLayoutComponent({ appearance = 'anvil2', navVariant = 'left', id,
|
|
|
132
168
|
return false;
|
|
133
169
|
}
|
|
134
170
|
}, [
|
|
135
|
-
|
|
171
|
+
badgeNavItems
|
|
136
172
|
]);
|
|
137
173
|
const limitContentWidth = useMemo(()=>{
|
|
138
174
|
if (view.isAnvil2 || !minContentWidth) {
|
|
@@ -245,9 +281,13 @@ function TitanLayoutComponent({ appearance = 'anvil2', navVariant = 'left', id,
|
|
|
245
281
|
onBarExpandChange: onBarExpandChange,
|
|
246
282
|
submenusOpened: openSubmenus,
|
|
247
283
|
onSubmenuOpenChange: onSubmenuOpenChange,
|
|
284
|
+
onSubmenusOpenChange: onOpenSubmenusChange ? onSubmenusOpenChange : undefined,
|
|
248
285
|
drawerOpened: mobileDrawerOpened,
|
|
249
286
|
onDrawerOpenChange: setMobileDrawerOpened,
|
|
250
287
|
top: sideTop,
|
|
288
|
+
topItems: sideTopItems,
|
|
289
|
+
scrollIndicators: navigationScrollIndicators,
|
|
290
|
+
pinnedNavigation: pinnedNavigation,
|
|
251
291
|
mainItems: sidebarNavItems,
|
|
252
292
|
navigationComponent: context.NavigationComponent,
|
|
253
293
|
onNavigationLinkClick: onNavigationLinkClick,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/titan-layout/titan-layout.tsx"],"sourcesContent":["import classNames from 'classnames';\nimport {\n CSSProperties,\n ComponentPropsWithoutRef,\n FC,\n Fragment,\n MouseEvent,\n ReactElement,\n ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { NavigationItemData } from '../../utils/navigation';\nimport { DefaultNavLinkComponent, NavLinkComponentProps } from '../../utils/navigation-context';\nimport { useTitanBreakpoint } from '../../utils/use-breakpoint';\nimport {\n LayoutContext,\n LayoutPlacementContext,\n TitanLayoutContextType,\n TitanLayoutSidebarContextType,\n} from './layout-context';\nimport { LayoutHeader, LayoutHeaderProps } from './layout-header';\nimport { LayoutHeaderDark } from './layout-header-dark';\nimport { TitanLayoutLogoProps } from './layout-logo';\nimport { LayoutSidebar } from './layout-sidebar';\nimport { InternalSideNavigationTrigger } from './layout-sidebar-links-internal';\nimport { useNotificationsState } from './notifications-context';\nimport { TitanLayoutLink, TitanLayoutTrigger } from './titan-layout-links';\nimport * as Styles from './titan-layout.module.less';\n\nexport type TitanLayoutProps = Omit<ComponentPropsWithoutRef<'div'>, 'children' | 'style'> & {\n /** layout appearance */\n appearance?: 'legacy' | 'anvil1' | 'anvil2';\n\n /** layout navigation variant (left by default) */\n navVariant?: 'left' | 'top';\n\n /** layout's content */\n children?: ReactNode;\n\n /** show only content without side and top bars */\n contentOnly?: boolean;\n\n /** component used for navigation */\n navigationComponent?: FC<NavLinkComponentProps>;\n\n /** data for main navigation links */\n navigationMainItems?: NavigationItemData[];\n\n /** data for overflow navigation links (used only with top variant) */\n navigationOverflowItems?: NavigationItemData[];\n\n /** logo props */\n logo?: TitanLayoutLogoProps;\n\n /** expanded state */\n expanded?: boolean;\n /** expanded state change handler */\n onExpandedChange?: (expanded: boolean) => void;\n\n /** submenus accordion opened state */\n openSubmenus?: string[];\n /** submenu accordion open state change handler */\n onOpenSubmenuChange?: (id: string, opened: boolean) => void;\n\n /** navigation link click handler */\n onNavigationLinkClick?: (itemId: string, submenuItemId?: string) => void;\n\n /** content header content */\n header?: ReactElement;\n\n /** layout header content (center) */\n top?: ReactElement;\n\n /** top links for side navigation */\n sideTop?: ReactElement[];\n\n /**\n * profile element for layout\n * @see ProfileDropdown\n */\n profile?: ReactElement;\n\n /**\n * extra links for layout header\n * shown in side nav footer on mobile\n */\n extraLinks?: ReactElement;\n\n /**\n * fixed extra links for layout header\n * shown in header on mobile as well\n */\n extraLinksTop?: ReactElement;\n\n /**\n * text shown in layout's header\n * shown in side nav footer on mobile\n */\n extraText?: string;\n\n /**\n * minimal width set for content area\n * used for pages that aren't adopted to mobile\n */\n minContentWidth?: number;\n\n /**\n * disable responsive behavior\n * can be used for pages that aren't adopted to mobile\n */\n noResponsive?: boolean;\n};\n\nconst defaultSidebarContext: TitanLayoutSidebarContextType = {\n styles: {\n popoverContent: {\n '--background-color-strong': '#0f1d26',\n 'color': 'var(--color-white)',\n } as CSSProperties,\n },\n};\n\nconst useAppearance = (appearance: TitanLayoutProps['appearance']) =>\n useMemo(() => {\n const isLegacy = appearance === 'legacy';\n const isAnvil1 = appearance === 'anvil1';\n const isAnvil2 = appearance === 'anvil2';\n\n return {\n isLegacy,\n isAnvil1,\n isAnvil2,\n isSequent: isLegacy || isAnvil2,\n };\n }, [appearance]);\n\nexport const useViewportConfigs = (noResponsive?: boolean) => {\n useEffect(() => {\n const meta: HTMLMetaElement | null = document.head.querySelector('meta[name=\"viewport\"]');\n\n if (meta) {\n meta.content = noResponsive\n ? ''\n : 'width=device-width, initial-scale=1, maximum-scale=1';\n }\n }, [noResponsive]);\n};\n\nfunction TitanLayoutComponent({\n appearance = 'anvil2',\n navVariant = 'left',\n id,\n children,\n contentOnly,\n navigationComponent,\n header,\n top,\n profile,\n expanded,\n openSubmenus,\n logo,\n onExpandedChange,\n onOpenSubmenuChange,\n navigationMainItems,\n navigationOverflowItems,\n onNavigationLinkClick,\n extraLinks,\n extraLinksTop,\n extraText,\n minContentWidth,\n noResponsive,\n sideTop,\n}: TitanLayoutProps) {\n const breakpoint = useTitanBreakpoint(noResponsive);\n const context: TitanLayoutContextType = useMemo(\n () => ({\n NavigationComponent: navigationComponent ?? DefaultNavLinkComponent,\n breakpoint,\n isTitanLayout: true,\n sidebar: defaultSidebarContext,\n }),\n [navigationComponent, breakpoint]\n );\n const view = useAppearance(appearance);\n const [mobileDrawerOpened, setMobileDrawerOpened] = useState(false);\n const { hasNotifications, NotificationsContextProvider } = useNotificationsState();\n const [headerHeight, setHeaderHeight] = useState(0);\n\n const isMobile = breakpoint.isMobile;\n const hasSideBar =\n !contentOnly &&\n (navVariant === 'left' || (navVariant === 'top' && isMobile)) &&\n (!!navigationMainItems?.length || !!sideTop?.length);\n const hasTopBar = !contentOnly;\n\n useEffect(() => {\n if (view.isAnvil1) {\n const bodyClassName = 'of-hidden-i';\n document.body.classList.add(bodyClassName);\n return () => document.body.classList.remove(bodyClassName);\n }\n }, [view.isAnvil1]);\n\n const onBurgerClick = useCallback(\n (e: MouseEvent<never>) => {\n if (isMobile) {\n setMobileDrawerOpened(true);\n } else {\n onExpandedChange?.(!expanded);\n }\n\n e.stopPropagation();\n },\n [isMobile, expanded, onExpandedChange]\n );\n\n const onBarExpandChange = useCallback(\n (expanded: boolean) => {\n if (isMobile) {\n setMobileDrawerOpened(false);\n } else {\n onExpandedChange?.(expanded);\n }\n },\n [onExpandedChange, isMobile]\n );\n const onSubmenuOpenChange = useCallback(\n (id: string, opened: boolean) => {\n onOpenSubmenuChange?.(id, opened);\n },\n [onOpenSubmenuChange]\n );\n const sidebarNavItems = useMemo(() => {\n if (!hasSideBar) {\n return undefined;\n }\n\n if (navVariant === 'left') {\n return navigationMainItems;\n }\n\n return [...(navigationMainItems ?? []), ...(navigationOverflowItems ?? [])];\n }, [hasSideBar, navigationMainItems, navigationOverflowItems, navVariant]);\n\n const hasMenuNotifications = useMemo(() => {\n try {\n return (\n sidebarNavItems?.some(item => {\n if (item.counter || item.tag?.value) {\n return true;\n } else if (item.submenu) {\n return item.submenu.groups.some(group =>\n group.links.some(link => !!link.counter || !!link.tag?.value)\n );\n }\n return false;\n }) ?? false\n );\n } catch {\n return false;\n }\n }, [sidebarNavItems]);\n\n const limitContentWidth = useMemo(() => {\n if (view.isAnvil2 || !minContentWidth) {\n return undefined;\n }\n\n if (breakpoint.width < minContentWidth) {\n return minContentWidth;\n }\n }, [view, minContentWidth, breakpoint.width]);\n\n const contentStyles = useMemo(() => {\n if (view.isAnvil2) {\n return {\n '--content-offset-top': `calc(var(--nav-offset-top) + ${headerHeight}px)`,\n } as CSSProperties;\n }\n }, [view, headerHeight]);\n\n const layoutClass = view.isLegacy\n ? Styles.layoutLegacy\n : view.isAnvil1\n ? Styles.layoutAnvil1\n : Styles.layoutAnvil2;\n\n const burgerProps: LayoutHeaderProps['burger'] = useMemo(() => {\n if (!hasTopBar) {\n return undefined;\n }\n\n if (isMobile) {\n return {\n onClick: onBurgerClick,\n tag: { value: hasNotifications || hasMenuNotifications },\n };\n }\n\n // !hasTopBar always false - we hide burger on desktop for now\n if (navVariant === 'left' && !hasTopBar) {\n return hasSideBar\n ? {\n 'onClick': onBurgerClick,\n 'tooltip': expanded ? 'Collapse' : 'Expand',\n 'data-pendo': 'navigation-left-options',\n 'data-cy': 'navigation-left-options',\n }\n : undefined;\n }\n\n return undefined;\n }, [\n isMobile,\n hasTopBar,\n navVariant,\n expanded,\n onBurgerClick,\n hasNotifications,\n hasMenuNotifications,\n hasSideBar,\n ]);\n\n return (\n <LayoutContext.Provider value={context}>\n <LayoutPlacementContext.Provider value=\"unset\">\n <div\n id={id}\n className={classNames(\n Styles.layout,\n isMobile ? Styles.layoutMobile : Styles.layoutDesktop,\n noResponsive && Styles.layoutNoResponsive,\n hasTopBar &&\n (navVariant === 'left' || !top\n ? Styles.layoutTopLight\n : Styles.layoutTopNav),\n {\n [Styles.layoutNavSlim]: !isMobile && hasSideBar && !expanded,\n [Styles.layoutNavWide]: !isMobile && hasSideBar && expanded,\n },\n layoutClass\n )}\n style={contentStyles}\n >\n <div className={Styles.topPlaceholder} />\n {hasTopBar &&\n (navVariant === 'left' ? (\n <LayoutHeader\n className={Styles.top}\n variant=\"light\"\n logo={logo}\n profile={isMobile ? undefined : profile}\n center={top}\n rightText={isMobile ? undefined : extraText}\n right={\n <Fragment>\n {extraLinksTop}\n {!isMobile && extraLinks}\n </Fragment>\n }\n isMobile={isMobile}\n burger={burgerProps}\n />\n ) : (\n <LayoutHeaderDark\n className={Styles.top}\n logo={logo}\n profile={isMobile ? undefined : profile}\n center={top}\n rightText={isMobile ? undefined : extraText}\n right={\n <Fragment>\n {extraLinksTop}\n {!isMobile && extraLinks}\n </Fragment>\n }\n isMobile={isMobile}\n burger={burgerProps}\n navigationMainItems={navigationMainItems}\n navigationOverflowItems={navigationOverflowItems}\n />\n ))}\n\n {hasSideBar && (\n <NotificationsContextProvider>\n <LayoutSidebar\n className={Styles.side}\n mobile={breakpoint.isMobile}\n touchDevice={breakpoint.isTouchDevice}\n barExpanded={!!expanded}\n onBarExpandChange={onBarExpandChange}\n submenusOpened={openSubmenus}\n onSubmenuOpenChange={onSubmenuOpenChange}\n drawerOpened={mobileDrawerOpened}\n onDrawerOpenChange={setMobileDrawerOpened}\n top={sideTop}\n mainItems={sidebarNavItems}\n navigationComponent={context.NavigationComponent}\n onNavigationLinkClick={onNavigationLinkClick}\n bottom={\n isMobile ? (\n <Fragment>\n {profile}\n {extraLinks}\n {!!extraText && (\n <InternalSideNavigationTrigger\n id=\"--extra-text\"\n title={extraText}\n isActive={undefined}\n icon={undefined}\n iconActive={undefined}\n tag={undefined}\n className={undefined}\n />\n )}\n </Fragment>\n ) : undefined\n }\n />\n </NotificationsContextProvider>\n )}\n\n {view.isAnvil2 ? (\n <TitanLayoutHeader>\n <ObservedHeight heightChange={setHeaderHeight}>{header}</ObservedHeight>\n </TitanLayoutHeader>\n ) : view.isLegacy ? (\n <TitanLayoutHeader>{header}</TitanLayoutHeader>\n ) : null}\n {view.isAnvil1 ? (\n <LayoutContentAnvil1 header={header} minWidth={limitContentWidth}>\n {children}\n </LayoutContentAnvil1>\n ) : view.isLegacy ? (\n <LayoutContentLegacy minWidth={limitContentWidth}>\n {children}\n </LayoutContentLegacy>\n ) : (\n children\n )}\n </div>\n </LayoutPlacementContext.Provider>\n </LayoutContext.Provider>\n );\n}\n\nconst ObservedHeight: FC<{\n children: ReactNode;\n heightChange?(value: number): void;\n}> = ({ children, heightChange }) => {\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (ref.current) {\n const updatePosition = () => {\n if (ref.current && heightChange) {\n const pos = ref.current.getBoundingClientRect();\n heightChange(pos.height);\n }\n };\n\n const observer = new ResizeObserver(updatePosition);\n observer.observe(ref.current);\n\n updatePosition();\n return () => observer.disconnect();\n }\n }, [heightChange]);\n\n useEffect(() => {\n return () => {\n heightChange?.(0);\n };\n }, [heightChange]);\n return <div ref={ref}>{children}</div>;\n};\nconst TitanLayoutHeader: FC<{ children: ReactNode }> = ({ children }) => {\n return (\n <div className={Styles.contentHeader} data-cy=\"layout-content-header\">\n {children}\n </div>\n );\n};\n\nconst LayoutContentAnvil1: FC<{\n children: ReactNode;\n header?: ReactNode;\n minWidth?: number;\n}> = ({ children, header, minWidth }) => {\n const innerContentStyles: CSSProperties = useMemo(\n () => ({\n ...(minWidth ? { minWidth: `${minWidth}px`, overflowX: 'auto' } : {}),\n }),\n [minWidth]\n );\n\n return (\n <Fragment>\n <TitanLayoutHeader>{header}</TitanLayoutHeader>\n <div\n className={classNames(Styles.content, { 'of-x-auto-i': !!minWidth })}\n data-cy=\"layout-content\"\n >\n <div\n className=\"position-relative d-f flex-grow-1 flex-basis-0 of-hidden\"\n style={innerContentStyles}\n >\n {children}\n </div>\n </div>\n </Fragment>\n );\n};\n\nconst LayoutContentLegacy: FC<{\n children: ReactNode;\n minWidth: number | undefined;\n}> = ({ children, minWidth }) => {\n const innerContentStyles: CSSProperties = useMemo(\n () => ({\n position: 'relative',\n minWidth: `${minWidth}px`,\n }),\n [minWidth]\n );\n\n return <div style={minWidth ? innerContentStyles : undefined}>{children}</div>;\n};\n\nexport const TitanLayout = Object.assign(TitanLayoutComponent, {\n Link: TitanLayoutLink,\n Trigger: TitanLayoutTrigger,\n});\n"],"names":["classNames","Fragment","useCallback","useEffect","useMemo","useRef","useState","DefaultNavLinkComponent","useTitanBreakpoint","LayoutContext","LayoutPlacementContext","LayoutHeader","LayoutHeaderDark","LayoutSidebar","InternalSideNavigationTrigger","useNotificationsState","TitanLayoutLink","TitanLayoutTrigger","Styles","defaultSidebarContext","styles","popoverContent","useAppearance","appearance","isLegacy","isAnvil1","isAnvil2","isSequent","useViewportConfigs","noResponsive","meta","document","head","querySelector","content","TitanLayoutComponent","navVariant","id","children","contentOnly","navigationComponent","header","top","profile","expanded","openSubmenus","logo","onExpandedChange","onOpenSubmenuChange","navigationMainItems","navigationOverflowItems","onNavigationLinkClick","extraLinks","extraLinksTop","extraText","minContentWidth","sideTop","breakpoint","context","NavigationComponent","isTitanLayout","sidebar","view","mobileDrawerOpened","setMobileDrawerOpened","hasNotifications","NotificationsContextProvider","headerHeight","setHeaderHeight","isMobile","hasSideBar","length","hasTopBar","bodyClassName","body","classList","add","remove","onBurgerClick","e","stopPropagation","onBarExpandChange","onSubmenuOpenChange","opened","sidebarNavItems","undefined","hasMenuNotifications","some","item","counter","tag","value","submenu","groups","group","links","link","limitContentWidth","width","contentStyles","layoutClass","layoutLegacy","layoutAnvil1","layoutAnvil2","burgerProps","onClick","Provider","div","className","layout","layoutMobile","layoutDesktop","layoutNoResponsive","layoutTopLight","layoutTopNav","layoutNavSlim","layoutNavWide","style","topPlaceholder","variant","center","rightText","right","burger","side","mobile","touchDevice","isTouchDevice","barExpanded","submenusOpened","drawerOpened","onDrawerOpenChange","mainItems","bottom","title","isActive","icon","iconActive","TitanLayoutHeader","ObservedHeight","heightChange","LayoutContentAnvil1","minWidth","LayoutContentLegacy","ref","current","updatePosition","pos","getBoundingClientRect","height","observer","ResizeObserver","observe","disconnect","contentHeader","data-cy","innerContentStyles","overflowX","position","TitanLayout","Object","assign","Link","Trigger"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/titan-layout/titan-layout.tsx"],"sourcesContent":["import classNames from 'classnames';\nimport {\n CSSProperties,\n ComponentPropsWithoutRef,\n FC,\n Fragment,\n MouseEvent,\n ReactElement,\n ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { NavigationItemData } from '../../utils/navigation';\nimport { DefaultNavLinkComponent, NavLinkComponentProps } from '../../utils/navigation-context';\nimport { updateOpenSubmenus } from '../../utils/submenu-state';\nimport { useTitanBreakpoint } from '../../utils/use-breakpoint';\nimport { PinnedNavigationConfig } from './interface';\nimport {\n LayoutContext,\n LayoutPlacementContext,\n TitanLayoutContextType,\n TitanLayoutSidebarContextType,\n} from './layout-context';\nimport { LayoutHeader, LayoutHeaderProps } from './layout-header';\nimport { LayoutHeaderDark } from './layout-header-dark';\nimport { TitanLayoutLogoProps } from './layout-logo';\nimport { LayoutSidebar } from './layout-sidebar';\nimport { InternalSideNavigationTrigger } from './layout-sidebar-links-internal';\nimport { useNotificationsState } from './notifications-context';\nimport { TitanLayoutLink, TitanLayoutTrigger } from './titan-layout-links';\nimport * as Styles from './titan-layout.module.less';\n\nexport type TitanLayoutProps = Omit<ComponentPropsWithoutRef<'div'>, 'children' | 'style'> & {\n /** layout appearance */\n appearance?: 'legacy' | 'anvil1' | 'anvil2';\n\n /** layout navigation variant (left by default) */\n navVariant?: 'left' | 'top';\n\n /** layout's content */\n children?: ReactNode;\n\n /** show only content without side and top bars */\n contentOnly?: boolean;\n\n /** component used for navigation */\n navigationComponent?: FC<NavLinkComponentProps>;\n\n /** data for main navigation links */\n navigationMainItems?: NavigationItemData[];\n\n /**\n * Turns the navigation catalog on: renders a trigger in the sidebar and the dialog behind it.\n * Omit it and the navigation is unchanged.\n */\n pinnedNavigation?: PinnedNavigationConfig;\n\n /** data for overflow navigation links (used only with top variant) */\n navigationOverflowItems?: NavigationItemData[];\n\n /** logo props */\n logo?: TitanLayoutLogoProps;\n\n /** expanded state */\n expanded?: boolean;\n /** expanded state change handler */\n onExpandedChange?: (expanded: boolean) => void;\n\n /** submenus accordion opened state */\n openSubmenus?: string[];\n /** submenu accordion open state change handler */\n onOpenSubmenuChange?: (id: string, opened: boolean) => void;\n /**\n * Receives the complete next set of open modules for individual and bulk actions.\n * Preferred over onOpenSubmenuChange when provided, so bulk updates are atomic.\n * Required for the rail's `Expand All` / `Collapse All` control, which is hidden\n * for hosts that supply only the per-module `onOpenSubmenuChange`.\n */\n onOpenSubmenusChange?: (ids: string[]) => void;\n\n /** navigation link click handler */\n onNavigationLinkClick?: (itemId: string, submenuItemId?: string) => void;\n\n /** content header content */\n header?: ReactElement;\n\n /** layout header content (center) */\n top?: ReactElement;\n\n /** top links for side navigation */\n sideTop?: ReactElement[];\n\n /**\n * Destinations pinned above the sidebar's divider, ahead of `sideTop`.\n *\n * Rendered with the same machinery as `navigationMainItems`, so one with pages beneath it is\n * an expandable group rather than a bare link. `sideTop` is unchanged and still takes elements\n * the host builds itself; the two can be used together.\n */\n sideTopItems?: NavigationItemData[];\n\n /**\n * Whether the sidebar shows the scroll and overflow-notification indicators.\n *\n * Defaults on, so a host that says nothing keeps them. Navigation versions that predate the\n * indicators pass `false`.\n */\n navigationScrollIndicators?: boolean;\n\n /**\n * profile element for layout\n * @see ProfileDropdown\n */\n profile?: ReactElement;\n\n /**\n * extra links for layout header\n * shown in side nav footer on mobile\n */\n extraLinks?: ReactElement;\n\n /**\n * fixed extra links for layout header\n * shown in header on mobile as well\n */\n extraLinksTop?: ReactElement;\n\n /**\n * text shown in layout's header\n * shown in side nav footer on mobile\n */\n extraText?: string;\n\n /**\n * minimal width set for content area\n * used for pages that aren't adopted to mobile\n */\n minContentWidth?: number;\n\n /**\n * disable responsive behavior\n * can be used for pages that aren't adopted to mobile\n */\n noResponsive?: boolean;\n};\n\nconst defaultSidebarContext: TitanLayoutSidebarContextType = {\n styles: {\n popoverContent: {\n '--background-color-strong': '#0f1d26',\n 'color': 'var(--color-white)',\n } as CSSProperties,\n },\n};\n\nconst useAppearance = (appearance: TitanLayoutProps['appearance']) =>\n useMemo(() => {\n const isLegacy = appearance === 'legacy';\n const isAnvil1 = appearance === 'anvil1';\n const isAnvil2 = appearance === 'anvil2';\n\n return {\n isLegacy,\n isAnvil1,\n isAnvil2,\n isSequent: isLegacy || isAnvil2,\n };\n }, [appearance]);\n\nexport const useViewportConfigs = (noResponsive?: boolean) => {\n useEffect(() => {\n const meta: HTMLMetaElement | null = document.head.querySelector('meta[name=\"viewport\"]');\n\n if (meta) {\n meta.content = noResponsive\n ? ''\n : 'width=device-width, initial-scale=1, maximum-scale=1';\n }\n }, [noResponsive]);\n};\n\nfunction TitanLayoutComponent({\n appearance = 'anvil2',\n navVariant = 'left',\n id,\n children,\n contentOnly,\n navigationComponent,\n header,\n top,\n profile,\n expanded,\n openSubmenus,\n logo,\n onExpandedChange,\n onOpenSubmenuChange,\n onOpenSubmenusChange,\n navigationMainItems,\n pinnedNavigation,\n navigationOverflowItems,\n onNavigationLinkClick,\n extraLinks,\n extraLinksTop,\n extraText,\n minContentWidth,\n noResponsive,\n sideTop,\n sideTopItems,\n navigationScrollIndicators = true,\n}: TitanLayoutProps) {\n const breakpoint = useTitanBreakpoint(noResponsive);\n const context: TitanLayoutContextType = useMemo(\n () => ({\n NavigationComponent: navigationComponent ?? DefaultNavLinkComponent,\n breakpoint,\n isTitanLayout: true,\n sidebar: defaultSidebarContext,\n }),\n [navigationComponent, breakpoint]\n );\n const view = useAppearance(appearance);\n const [mobileDrawerOpened, setMobileDrawerOpened] = useState(false);\n const { hasNotifications, NotificationsContextProvider } = useNotificationsState();\n const [headerHeight, setHeaderHeight] = useState(0);\n\n const isMobile = breakpoint.isMobile;\n const hasSideBar =\n !contentOnly &&\n (navVariant === 'left' || (navVariant === 'top' && isMobile)) &&\n (!!navigationMainItems?.length ||\n !!sideTop?.length ||\n !!sideTopItems?.length ||\n !!pinnedNavigation);\n const hasTopBar = !contentOnly;\n\n useEffect(() => {\n if (view.isAnvil1) {\n const bodyClassName = 'of-hidden-i';\n document.body.classList.add(bodyClassName);\n return () => document.body.classList.remove(bodyClassName);\n }\n }, [view.isAnvil1]);\n\n const onBurgerClick = useCallback(\n (e: MouseEvent<never>) => {\n if (isMobile) {\n setMobileDrawerOpened(true);\n } else {\n onExpandedChange?.(!expanded);\n }\n\n e.stopPropagation();\n },\n [isMobile, expanded, onExpandedChange]\n );\n\n const onBarExpandChange = useCallback(\n (expanded: boolean) => {\n if (isMobile) {\n setMobileDrawerOpened(false);\n } else {\n onExpandedChange?.(expanded);\n }\n },\n [onExpandedChange, isMobile]\n );\n const onSubmenuOpenChange = useCallback(\n (id: string, opened: boolean) => {\n if (onOpenSubmenusChange) {\n onOpenSubmenusChange(updateOpenSubmenus(openSubmenus ?? [], [id], opened));\n } else {\n onOpenSubmenuChange?.(id, opened);\n }\n },\n [onOpenSubmenuChange, onOpenSubmenusChange, openSubmenus]\n );\n /*\n * Bulk actions require `onOpenSubmenusChange`, which takes the whole next set at once.\n * They are deliberately not emulated with `onOpenSubmenuChange`: that contract is one\n * call per user action, so a host deriving its next list from the current one would see\n * the same stale list on every call of a loop and keep only the last module. `Expand All`\n * is hidden entirely when a host supplies only the per-module callback.\n */\n const onSubmenusOpenChange = useCallback(\n (ids: string[], opened: boolean) => {\n onOpenSubmenusChange?.(updateOpenSubmenus(openSubmenus ?? [], ids, opened));\n },\n [onOpenSubmenusChange, openSubmenus]\n );\n const sidebarNavItems = useMemo(() => {\n if (!hasSideBar) {\n return undefined;\n }\n\n if (navVariant === 'left') {\n return navigationMainItems;\n }\n\n return [...(navigationMainItems ?? []), ...(navigationOverflowItems ?? [])];\n }, [hasSideBar, navigationMainItems, navigationOverflowItems, navVariant]);\n\n /*\n * The burger badge speaks for the whole drawer, so it has to count what sits above the\n * divider as well. A permanent destination asking for attention would otherwise go unseen\n * while the drawer is closed.\n */\n const badgeNavItems = useMemo(\n /*\n * Gated on `hasSideBar`, not `sidebarNavItems`: on the left variant a rail that exists\n * only for `sideTopItems` still has `sidebarNavItems` (i.e. `navigationMainItems`) undefined.\n */\n () => (hasSideBar ? [...(sideTopItems ?? []), ...(sidebarNavItems ?? [])] : undefined),\n [hasSideBar, sideTopItems, sidebarNavItems]\n );\n\n const hasMenuNotifications = useMemo(() => {\n try {\n return (\n badgeNavItems?.some(item => {\n if (item.counter || item.tag?.value) {\n return true;\n } else if (item.submenu) {\n return item.submenu.groups.some(group =>\n group.links.some(link => !!link.counter || !!link.tag?.value)\n );\n }\n return false;\n }) ?? false\n );\n } catch {\n return false;\n }\n }, [badgeNavItems]);\n\n const limitContentWidth = useMemo(() => {\n if (view.isAnvil2 || !minContentWidth) {\n return undefined;\n }\n\n if (breakpoint.width < minContentWidth) {\n return minContentWidth;\n }\n }, [view, minContentWidth, breakpoint.width]);\n\n const contentStyles = useMemo(() => {\n if (view.isAnvil2) {\n return {\n '--content-offset-top': `calc(var(--nav-offset-top) + ${headerHeight}px)`,\n } as CSSProperties;\n }\n }, [view, headerHeight]);\n\n const layoutClass = view.isLegacy\n ? Styles.layoutLegacy\n : view.isAnvil1\n ? Styles.layoutAnvil1\n : Styles.layoutAnvil2;\n\n const burgerProps: LayoutHeaderProps['burger'] = useMemo(() => {\n if (!hasTopBar) {\n return undefined;\n }\n\n if (isMobile) {\n return {\n onClick: onBurgerClick,\n tag: { value: hasNotifications || hasMenuNotifications },\n };\n }\n\n // !hasTopBar always false - we hide burger on desktop for now\n if (navVariant === 'left' && !hasTopBar) {\n return hasSideBar\n ? {\n 'onClick': onBurgerClick,\n 'tooltip': expanded ? 'Collapse' : 'Expand',\n 'data-pendo': 'navigation-left-options',\n 'data-cy': 'navigation-left-options',\n }\n : undefined;\n }\n\n return undefined;\n }, [\n isMobile,\n hasTopBar,\n navVariant,\n expanded,\n onBurgerClick,\n hasNotifications,\n hasMenuNotifications,\n hasSideBar,\n ]);\n\n return (\n <LayoutContext.Provider value={context}>\n <LayoutPlacementContext.Provider value=\"unset\">\n <div\n id={id}\n className={classNames(\n Styles.layout,\n isMobile ? Styles.layoutMobile : Styles.layoutDesktop,\n noResponsive && Styles.layoutNoResponsive,\n hasTopBar &&\n (navVariant === 'left' || !top\n ? Styles.layoutTopLight\n : Styles.layoutTopNav),\n {\n [Styles.layoutNavSlim]: !isMobile && hasSideBar && !expanded,\n [Styles.layoutNavWide]: !isMobile && hasSideBar && expanded,\n },\n layoutClass\n )}\n style={contentStyles}\n >\n <div className={Styles.topPlaceholder} />\n {hasTopBar &&\n (navVariant === 'left' ? (\n <LayoutHeader\n className={Styles.top}\n variant=\"light\"\n logo={logo}\n profile={isMobile ? undefined : profile}\n center={top}\n rightText={isMobile ? undefined : extraText}\n right={\n <Fragment>\n {extraLinksTop}\n {!isMobile && extraLinks}\n </Fragment>\n }\n isMobile={isMobile}\n burger={burgerProps}\n />\n ) : (\n <LayoutHeaderDark\n className={Styles.top}\n logo={logo}\n profile={isMobile ? undefined : profile}\n center={top}\n rightText={isMobile ? undefined : extraText}\n right={\n <Fragment>\n {extraLinksTop}\n {!isMobile && extraLinks}\n </Fragment>\n }\n isMobile={isMobile}\n burger={burgerProps}\n navigationMainItems={navigationMainItems}\n navigationOverflowItems={navigationOverflowItems}\n />\n ))}\n\n {hasSideBar && (\n <NotificationsContextProvider>\n <LayoutSidebar\n className={Styles.side}\n mobile={breakpoint.isMobile}\n touchDevice={breakpoint.isTouchDevice}\n barExpanded={!!expanded}\n onBarExpandChange={onBarExpandChange}\n submenusOpened={openSubmenus}\n onSubmenuOpenChange={onSubmenuOpenChange}\n onSubmenusOpenChange={\n onOpenSubmenusChange ? onSubmenusOpenChange : undefined\n }\n drawerOpened={mobileDrawerOpened}\n onDrawerOpenChange={setMobileDrawerOpened}\n top={sideTop}\n topItems={sideTopItems}\n scrollIndicators={navigationScrollIndicators}\n pinnedNavigation={pinnedNavigation}\n mainItems={sidebarNavItems}\n navigationComponent={context.NavigationComponent}\n onNavigationLinkClick={onNavigationLinkClick}\n bottom={\n isMobile ? (\n <Fragment>\n {profile}\n {extraLinks}\n {!!extraText && (\n <InternalSideNavigationTrigger\n id=\"--extra-text\"\n title={extraText}\n isActive={undefined}\n icon={undefined}\n iconActive={undefined}\n tag={undefined}\n className={undefined}\n />\n )}\n </Fragment>\n ) : undefined\n }\n />\n </NotificationsContextProvider>\n )}\n\n {view.isAnvil2 ? (\n <TitanLayoutHeader>\n <ObservedHeight heightChange={setHeaderHeight}>{header}</ObservedHeight>\n </TitanLayoutHeader>\n ) : view.isLegacy ? (\n <TitanLayoutHeader>{header}</TitanLayoutHeader>\n ) : null}\n {view.isAnvil1 ? (\n <LayoutContentAnvil1 header={header} minWidth={limitContentWidth}>\n {children}\n </LayoutContentAnvil1>\n ) : view.isLegacy ? (\n <LayoutContentLegacy minWidth={limitContentWidth}>\n {children}\n </LayoutContentLegacy>\n ) : (\n children\n )}\n </div>\n </LayoutPlacementContext.Provider>\n </LayoutContext.Provider>\n );\n}\n\nconst ObservedHeight: FC<{\n children: ReactNode;\n heightChange?(value: number): void;\n}> = ({ children, heightChange }) => {\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (ref.current) {\n const updatePosition = () => {\n if (ref.current && heightChange) {\n const pos = ref.current.getBoundingClientRect();\n heightChange(pos.height);\n }\n };\n\n const observer = new ResizeObserver(updatePosition);\n observer.observe(ref.current);\n\n updatePosition();\n return () => observer.disconnect();\n }\n }, [heightChange]);\n\n useEffect(() => {\n return () => {\n heightChange?.(0);\n };\n }, [heightChange]);\n return <div ref={ref}>{children}</div>;\n};\nconst TitanLayoutHeader: FC<{ children: ReactNode }> = ({ children }) => {\n return (\n <div className={Styles.contentHeader} data-cy=\"layout-content-header\">\n {children}\n </div>\n );\n};\n\nconst LayoutContentAnvil1: FC<{\n children: ReactNode;\n header?: ReactNode;\n minWidth?: number;\n}> = ({ children, header, minWidth }) => {\n const innerContentStyles: CSSProperties = useMemo(\n () => ({\n ...(minWidth ? { minWidth: `${minWidth}px`, overflowX: 'auto' } : {}),\n }),\n [minWidth]\n );\n\n return (\n <Fragment>\n <TitanLayoutHeader>{header}</TitanLayoutHeader>\n <div\n className={classNames(Styles.content, { 'of-x-auto-i': !!minWidth })}\n data-cy=\"layout-content\"\n >\n <div\n className=\"position-relative d-f flex-grow-1 flex-basis-0 of-hidden\"\n style={innerContentStyles}\n >\n {children}\n </div>\n </div>\n </Fragment>\n );\n};\n\nconst LayoutContentLegacy: FC<{\n children: ReactNode;\n minWidth: number | undefined;\n}> = ({ children, minWidth }) => {\n const innerContentStyles: CSSProperties = useMemo(\n () => ({\n position: 'relative',\n minWidth: `${minWidth}px`,\n }),\n [minWidth]\n );\n\n return <div style={minWidth ? innerContentStyles : undefined}>{children}</div>;\n};\n\nexport const TitanLayout = Object.assign(TitanLayoutComponent, {\n Link: TitanLayoutLink,\n Trigger: TitanLayoutTrigger,\n});\n"],"names":["classNames","Fragment","useCallback","useEffect","useMemo","useRef","useState","DefaultNavLinkComponent","updateOpenSubmenus","useTitanBreakpoint","LayoutContext","LayoutPlacementContext","LayoutHeader","LayoutHeaderDark","LayoutSidebar","InternalSideNavigationTrigger","useNotificationsState","TitanLayoutLink","TitanLayoutTrigger","Styles","defaultSidebarContext","styles","popoverContent","useAppearance","appearance","isLegacy","isAnvil1","isAnvil2","isSequent","useViewportConfigs","noResponsive","meta","document","head","querySelector","content","TitanLayoutComponent","navVariant","id","children","contentOnly","navigationComponent","header","top","profile","expanded","openSubmenus","logo","onExpandedChange","onOpenSubmenuChange","onOpenSubmenusChange","navigationMainItems","pinnedNavigation","navigationOverflowItems","onNavigationLinkClick","extraLinks","extraLinksTop","extraText","minContentWidth","sideTop","sideTopItems","navigationScrollIndicators","breakpoint","context","NavigationComponent","isTitanLayout","sidebar","view","mobileDrawerOpened","setMobileDrawerOpened","hasNotifications","NotificationsContextProvider","headerHeight","setHeaderHeight","isMobile","hasSideBar","length","hasTopBar","bodyClassName","body","classList","add","remove","onBurgerClick","e","stopPropagation","onBarExpandChange","onSubmenuOpenChange","opened","onSubmenusOpenChange","ids","sidebarNavItems","undefined","badgeNavItems","hasMenuNotifications","some","item","counter","tag","value","submenu","groups","group","links","link","limitContentWidth","width","contentStyles","layoutClass","layoutLegacy","layoutAnvil1","layoutAnvil2","burgerProps","onClick","Provider","div","className","layout","layoutMobile","layoutDesktop","layoutNoResponsive","layoutTopLight","layoutTopNav","layoutNavSlim","layoutNavWide","style","topPlaceholder","variant","center","rightText","right","burger","side","mobile","touchDevice","isTouchDevice","barExpanded","submenusOpened","drawerOpened","onDrawerOpenChange","topItems","scrollIndicators","mainItems","bottom","title","isActive","icon","iconActive","TitanLayoutHeader","ObservedHeight","heightChange","LayoutContentAnvil1","minWidth","LayoutContentLegacy","ref","current","updatePosition","pos","getBoundingClientRect","height","observer","ResizeObserver","observe","disconnect","contentHeader","data-cy","innerContentStyles","overflowX","position","TitanLayout","Object","assign","Link","Trigger"],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Measures what the rail's scroller is hiding, for the overflow notification pills.
|
|
3
|
+
*
|
|
4
|
+
* `enabled` is false on navigation versions that do not show the indicators. The refs are still
|
|
5
|
+
* handed back so the caller's markup is unchanged, but nothing is observed and no state is kept —
|
|
6
|
+
* this effect measures on every scroll and resize, which is not work to do for a feature that is
|
|
7
|
+
* switched off.
|
|
8
|
+
*/
|
|
9
|
+
export declare function useNavigationScroll(enabled?: boolean): {
|
|
10
|
+
revealNotification: (direction: "above" | "below") => void;
|
|
11
|
+
above: number;
|
|
12
|
+
below: number;
|
|
13
|
+
hasContentAbove: boolean;
|
|
14
|
+
hasContentBelow: boolean;
|
|
15
|
+
regionRef: import("react").RefObject<HTMLDivElement>;
|
|
16
|
+
scrollRef: import("react").RefObject<HTMLDivElement>;
|
|
17
|
+
aboveIndicatorRef: import("react").RefObject<HTMLDivElement>;
|
|
18
|
+
belowIndicatorRef: import("react").RefObject<HTMLDivElement>;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=use-navigation-scroll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-navigation-scroll.d.ts","sourceRoot":"","sources":["../../../src/components/titan-layout/use-navigation-scroll.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,UAAO;oCAoG9B,OAAO,GAAG,OAAO;;;;;;;;;EAiCpC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { getOverflowNotifications } from '../../utils/navigation-notifications';
|
|
3
|
+
/**
|
|
4
|
+
* Measures what the rail's scroller is hiding, for the overflow notification pills.
|
|
5
|
+
*
|
|
6
|
+
* `enabled` is false on navigation versions that do not show the indicators. The refs are still
|
|
7
|
+
* handed back so the caller's markup is unchanged, but nothing is observed and no state is kept —
|
|
8
|
+
* this effect measures on every scroll and resize, which is not work to do for a feature that is
|
|
9
|
+
* switched off.
|
|
10
|
+
*/ export function useNavigationScroll(enabled = true) {
|
|
11
|
+
const regionRef = useRef(null);
|
|
12
|
+
const scrollRef = useRef(null);
|
|
13
|
+
const aboveIndicatorRef = useRef(null);
|
|
14
|
+
const belowIndicatorRef = useRef(null);
|
|
15
|
+
const targets = useRef({});
|
|
16
|
+
const [overflow, setOverflow] = useState({
|
|
17
|
+
above: 0,
|
|
18
|
+
below: 0,
|
|
19
|
+
hasContentAbove: false,
|
|
20
|
+
hasContentBelow: false
|
|
21
|
+
});
|
|
22
|
+
/*
|
|
23
|
+
* The counted area, inset from the scrolling box by whichever notification pills are on
|
|
24
|
+
* screen. The pills float over the rail's own edges, so a row one covers is hidden even
|
|
25
|
+
* though it still sits inside the scroller — without the inset the rail can report a count
|
|
26
|
+
* of zero while a pill is physically covering the row it should be counting. Only the pills
|
|
27
|
+
* inset: the scroll shadow is a separate element precisely so its 12px gradient, which
|
|
28
|
+
* hides nothing, does not shrink the area.
|
|
29
|
+
*/ const getCountedViewport = useCallback((scroller)=>{
|
|
30
|
+
var _ref, _ref1;
|
|
31
|
+
var _aboveIndicatorRef_current, _belowIndicatorRef_current;
|
|
32
|
+
const { top, bottom } = scroller.getBoundingClientRect();
|
|
33
|
+
return {
|
|
34
|
+
top: top + ((_ref = (_aboveIndicatorRef_current = aboveIndicatorRef.current) === null || _aboveIndicatorRef_current === void 0 ? void 0 : _aboveIndicatorRef_current.offsetHeight) !== null && _ref !== void 0 ? _ref : 0),
|
|
35
|
+
bottom: bottom - ((_ref1 = (_belowIndicatorRef_current = belowIndicatorRef.current) === null || _belowIndicatorRef_current === void 0 ? void 0 : _belowIndicatorRef_current.offsetHeight) !== null && _ref1 !== void 0 ? _ref1 : 0)
|
|
36
|
+
};
|
|
37
|
+
}, []);
|
|
38
|
+
useEffect(()=>{
|
|
39
|
+
const scroller = scrollRef.current;
|
|
40
|
+
const region = regionRef.current;
|
|
41
|
+
if (!enabled || !scroller || !region) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
let frame = 0;
|
|
45
|
+
const measure = ()=>{
|
|
46
|
+
frame = 0;
|
|
47
|
+
const viewport = getCountedViewport(scroller);
|
|
48
|
+
const rows = Array.from(scroller.querySelectorAll('[data-navigation-notification-count]'));
|
|
49
|
+
const { above, below, aboveIndex, belowIndex } = getOverflowNotifications(rows.map((row)=>{
|
|
50
|
+
const { top, bottom } = row.getBoundingClientRect();
|
|
51
|
+
return {
|
|
52
|
+
top,
|
|
53
|
+
bottom,
|
|
54
|
+
count: Number(row.dataset.navigationNotificationCount) || 0
|
|
55
|
+
};
|
|
56
|
+
}), viewport);
|
|
57
|
+
targets.current = {
|
|
58
|
+
above: rows[aboveIndex],
|
|
59
|
+
below: rows[belowIndex]
|
|
60
|
+
};
|
|
61
|
+
const hasContentAbove = scroller.scrollTop > 1;
|
|
62
|
+
const hasContentBelow = scroller.scrollHeight - scroller.clientHeight - scroller.scrollTop > 1;
|
|
63
|
+
setOverflow((previous)=>previous.above === above && previous.below === below && previous.hasContentAbove === hasContentAbove && previous.hasContentBelow === hasContentBelow ? previous : {
|
|
64
|
+
above,
|
|
65
|
+
below,
|
|
66
|
+
hasContentAbove,
|
|
67
|
+
hasContentBelow
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
const scheduleMeasure = ()=>{
|
|
71
|
+
if (!frame) {
|
|
72
|
+
frame = requestAnimationFrame(measure);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const resizeObserver = new ResizeObserver(scheduleMeasure);
|
|
76
|
+
resizeObserver.observe(scroller);
|
|
77
|
+
if (scroller.firstElementChild) {
|
|
78
|
+
resizeObserver.observe(scroller.firstElementChild);
|
|
79
|
+
}
|
|
80
|
+
const mutationObserver = new MutationObserver(scheduleMeasure);
|
|
81
|
+
/*
|
|
82
|
+
* The whole region rather than just the scroller: the pills mount and unmount as siblings
|
|
83
|
+
* of the scrolling element, and their height feeds back into the counted area above. The
|
|
84
|
+
* pass this schedules settles immediately, since a pill's height does not change with the
|
|
85
|
+
* number it shows (the label cannot wrap).
|
|
86
|
+
*/ mutationObserver.observe(region, {
|
|
87
|
+
childList: true,
|
|
88
|
+
subtree: true,
|
|
89
|
+
attributes: true,
|
|
90
|
+
attributeFilter: [
|
|
91
|
+
'data-navigation-notification-count',
|
|
92
|
+
'class',
|
|
93
|
+
'style'
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
scroller.addEventListener('scroll', scheduleMeasure, {
|
|
97
|
+
passive: true
|
|
98
|
+
});
|
|
99
|
+
scheduleMeasure();
|
|
100
|
+
return ()=>{
|
|
101
|
+
cancelAnimationFrame(frame);
|
|
102
|
+
resizeObserver.disconnect();
|
|
103
|
+
mutationObserver.disconnect();
|
|
104
|
+
scroller.removeEventListener('scroll', scheduleMeasure);
|
|
105
|
+
};
|
|
106
|
+
}, [
|
|
107
|
+
enabled,
|
|
108
|
+
getCountedViewport
|
|
109
|
+
]);
|
|
110
|
+
const revealNotification = useCallback((direction)=>{
|
|
111
|
+
const scroller = scrollRef.current;
|
|
112
|
+
const target = targets.current[direction];
|
|
113
|
+
if (!scroller || !target) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Centred within the counted area, so the row lands clear of both pills.
|
|
117
|
+
const viewport = getCountedViewport(scroller);
|
|
118
|
+
const row = target.getBoundingClientRect();
|
|
119
|
+
// Reveal the item without activating its link or changing the selected destination.
|
|
120
|
+
target.focus({
|
|
121
|
+
preventScroll: true
|
|
122
|
+
});
|
|
123
|
+
scroller.scrollTo({
|
|
124
|
+
top: scroller.scrollTop + row.top - viewport.top - (viewport.bottom - viewport.top - row.height) / 2,
|
|
125
|
+
behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth'
|
|
126
|
+
});
|
|
127
|
+
}, [
|
|
128
|
+
getCountedViewport
|
|
129
|
+
]);
|
|
130
|
+
return {
|
|
131
|
+
regionRef,
|
|
132
|
+
scrollRef,
|
|
133
|
+
aboveIndicatorRef,
|
|
134
|
+
belowIndicatorRef,
|
|
135
|
+
...overflow,
|
|
136
|
+
revealNotification
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
//# sourceMappingURL=use-navigation-scroll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/titan-layout/use-navigation-scroll.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { getOverflowNotifications } from '../../utils/navigation-notifications';\n\n/**\n * Measures what the rail's scroller is hiding, for the overflow notification pills.\n *\n * `enabled` is false on navigation versions that do not show the indicators. The refs are still\n * handed back so the caller's markup is unchanged, but nothing is observed and no state is kept —\n * this effect measures on every scroll and resize, which is not work to do for a feature that is\n * switched off.\n */\nexport function useNavigationScroll(enabled = true) {\n const regionRef = useRef<HTMLDivElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n const aboveIndicatorRef = useRef<HTMLDivElement>(null);\n const belowIndicatorRef = useRef<HTMLDivElement>(null);\n const targets = useRef<{ above?: HTMLElement; below?: HTMLElement }>({});\n const [overflow, setOverflow] = useState({\n above: 0,\n below: 0,\n hasContentAbove: false,\n hasContentBelow: false,\n });\n\n /*\n * The counted area, inset from the scrolling box by whichever notification pills are on\n * screen. The pills float over the rail's own edges, so a row one covers is hidden even\n * though it still sits inside the scroller — without the inset the rail can report a count\n * of zero while a pill is physically covering the row it should be counting. Only the pills\n * inset: the scroll shadow is a separate element precisely so its 12px gradient, which\n * hides nothing, does not shrink the area.\n */\n const getCountedViewport = useCallback((scroller: HTMLElement) => {\n const { top, bottom } = scroller.getBoundingClientRect();\n return {\n top: top + (aboveIndicatorRef.current?.offsetHeight ?? 0),\n bottom: bottom - (belowIndicatorRef.current?.offsetHeight ?? 0),\n };\n }, []);\n\n useEffect(() => {\n const scroller = scrollRef.current;\n const region = regionRef.current;\n if (!enabled || !scroller || !region) {\n return;\n }\n let frame = 0;\n const measure = () => {\n frame = 0;\n const viewport = getCountedViewport(scroller);\n const rows = Array.from(\n scroller.querySelectorAll<HTMLElement>('[data-navigation-notification-count]')\n );\n const { above, below, aboveIndex, belowIndex } = getOverflowNotifications(\n rows.map(row => {\n const { top, bottom } = row.getBoundingClientRect();\n return {\n top,\n bottom,\n count: Number(row.dataset.navigationNotificationCount) || 0,\n };\n }),\n viewport\n );\n targets.current = { above: rows[aboveIndex], below: rows[belowIndex] };\n const hasContentAbove = scroller.scrollTop > 1;\n const hasContentBelow =\n scroller.scrollHeight - scroller.clientHeight - scroller.scrollTop > 1;\n setOverflow(previous =>\n previous.above === above &&\n previous.below === below &&\n previous.hasContentAbove === hasContentAbove &&\n previous.hasContentBelow === hasContentBelow\n ? previous\n : { above, below, hasContentAbove, hasContentBelow }\n );\n };\n const scheduleMeasure = () => {\n if (!frame) {\n frame = requestAnimationFrame(measure);\n }\n };\n const resizeObserver = new ResizeObserver(scheduleMeasure);\n resizeObserver.observe(scroller);\n if (scroller.firstElementChild) {\n resizeObserver.observe(scroller.firstElementChild);\n }\n const mutationObserver = new MutationObserver(scheduleMeasure);\n /*\n * The whole region rather than just the scroller: the pills mount and unmount as siblings\n * of the scrolling element, and their height feeds back into the counted area above. The\n * pass this schedules settles immediately, since a pill's height does not change with the\n * number it shows (the label cannot wrap).\n */\n mutationObserver.observe(region, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['data-navigation-notification-count', 'class', 'style'],\n });\n scroller.addEventListener('scroll', scheduleMeasure, { passive: true });\n scheduleMeasure();\n return () => {\n cancelAnimationFrame(frame);\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n scroller.removeEventListener('scroll', scheduleMeasure);\n };\n }, [enabled, getCountedViewport]);\n\n const revealNotification = useCallback(\n (direction: 'above' | 'below') => {\n const scroller = scrollRef.current;\n const target = targets.current[direction];\n if (!scroller || !target) {\n return;\n }\n // Centred within the counted area, so the row lands clear of both pills.\n const viewport = getCountedViewport(scroller);\n const row = target.getBoundingClientRect();\n // Reveal the item without activating its link or changing the selected destination.\n target.focus({ preventScroll: true });\n scroller.scrollTo({\n top:\n scroller.scrollTop +\n row.top -\n viewport.top -\n (viewport.bottom - viewport.top - row.height) / 2,\n behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches\n ? 'auto'\n : 'smooth',\n });\n },\n [getCountedViewport]\n );\n\n return {\n regionRef,\n scrollRef,\n aboveIndicatorRef,\n belowIndicatorRef,\n ...overflow,\n revealNotification,\n };\n}\n"],"names":["useCallback","useEffect","useRef","useState","getOverflowNotifications","useNavigationScroll","enabled","regionRef","scrollRef","aboveIndicatorRef","belowIndicatorRef","targets","overflow","setOverflow","above","below","hasContentAbove","hasContentBelow","getCountedViewport","scroller","top","bottom","getBoundingClientRect","current","offsetHeight","region","frame","measure","viewport","rows","Array","from","querySelectorAll","aboveIndex","belowIndex","map","row","count","Number","dataset","navigationNotificationCount","scrollTop","scrollHeight","clientHeight","previous","scheduleMeasure","requestAnimationFrame","resizeObserver","ResizeObserver","observe","firstElementChild","mutationObserver","MutationObserver","childList","subtree","attributes","attributeFilter","addEventListener","passive","cancelAnimationFrame","disconnect","removeEventListener","revealNotification","direction","target","focus","preventScroll","scrollTo","height","behavior","window","matchMedia","matches"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation-notifications.test.d.ts","sourceRoot":"","sources":["../../../src/utils/__tests__/navigation-notifications.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submenu-state.test.d.ts","sourceRoot":"","sources":["../../../src/utils/__tests__/submenu-state.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CounterTagValue } from './counter-tag';
|
|
2
|
+
import { NavigationItemData } from './navigation';
|
|
3
|
+
export declare const getNotificationCount: (value: CounterTagValue | undefined) => number;
|
|
4
|
+
/** A closed module summarizes its pages; an open module lets each page count itself. */
|
|
5
|
+
export declare function getModuleNotificationCount(item: NavigationItemData, submenuOpened: boolean): number;
|
|
6
|
+
export interface NotificationRowBounds {
|
|
7
|
+
top: number;
|
|
8
|
+
bottom: number;
|
|
9
|
+
count: number;
|
|
10
|
+
}
|
|
11
|
+
/** Only fully offscreen rows contribute; partially visible notifications are still discoverable. */
|
|
12
|
+
export declare function getOverflowNotifications(rows: NotificationRowBounds[], viewport: {
|
|
13
|
+
top: number;
|
|
14
|
+
bottom: number;
|
|
15
|
+
}): {
|
|
16
|
+
above: number;
|
|
17
|
+
below: number;
|
|
18
|
+
aboveIndex: number;
|
|
19
|
+
belowIndex: number;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=navigation-notifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation-notifications.d.ts","sourceRoot":"","sources":["../../src/utils/navigation-notifications.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,eAAO,MAAM,oBAAoB,GAAI,OAAO,eAAe,GAAG,SAAS,KAAG,MAK7D,CAAC;AAEd,wFAAwF;AACxF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,kBAAkB,EAAE,aAAa,EAAE,OAAO,UAe1F;AAED,MAAM,WAAW,qBAAqB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,oGAAoG;AACpG,wBAAgB,wBAAwB,CACpC,IAAI,EAAE,qBAAqB,EAAE,EAC7B,QAAQ,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;;;;;EAqB5C"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const getNotificationCount = (value)=>value === true ? 1 : typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0;
|
|
2
|
+
/** A closed module summarizes its pages; an open module lets each page count itself. */ export function getModuleNotificationCount(item, submenuOpened) {
|
|
3
|
+
var _ref, _item_counter;
|
|
4
|
+
var _item_submenu, _item_tag;
|
|
5
|
+
const childCount = ((_ref = (_item_submenu = item.submenu) === null || _item_submenu === void 0 ? void 0 : _item_submenu.groups) !== null && _ref !== void 0 ? _ref : []).reduce((total, group)=>total + group.links.reduce((sum, link)=>{
|
|
6
|
+
var _link_counter;
|
|
7
|
+
var _link_tag;
|
|
8
|
+
return sum + getNotificationCount((_link_counter = link.counter) !== null && _link_counter !== void 0 ? _link_counter : (_link_tag = link.tag) === null || _link_tag === void 0 ? void 0 : _link_tag.value);
|
|
9
|
+
}, 0), 0);
|
|
10
|
+
return childCount > 0 ? submenuOpened ? 0 : childCount : getNotificationCount((_item_counter = item.counter) !== null && _item_counter !== void 0 ? _item_counter : (_item_tag = item.tag) === null || _item_tag === void 0 ? void 0 : _item_tag.value);
|
|
11
|
+
}
|
|
12
|
+
/** Only fully offscreen rows contribute; partially visible notifications are still discoverable. */ export function getOverflowNotifications(rows, viewport) {
|
|
13
|
+
let above = 0;
|
|
14
|
+
let below = 0;
|
|
15
|
+
let aboveIndex = -1;
|
|
16
|
+
let belowIndex = -1;
|
|
17
|
+
rows.forEach((row, index)=>{
|
|
18
|
+
if (row.count <= 0 || row.bottom <= row.top) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (row.bottom <= viewport.top) {
|
|
22
|
+
above += row.count;
|
|
23
|
+
aboveIndex = index;
|
|
24
|
+
} else if (row.top >= viewport.bottom) {
|
|
25
|
+
below += row.count;
|
|
26
|
+
if (belowIndex === -1) {
|
|
27
|
+
belowIndex = index;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
above,
|
|
33
|
+
below,
|
|
34
|
+
aboveIndex,
|
|
35
|
+
belowIndex
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=navigation-notifications.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/navigation-notifications.ts"],"sourcesContent":["import { CounterTagValue } from './counter-tag';\nimport { NavigationItemData } from './navigation';\n\nexport const getNotificationCount = (value: CounterTagValue | undefined): number =>\n value === true\n ? 1\n : typeof value === 'number' && Number.isFinite(value) && value > 0\n ? value\n : 0;\n\n/** A closed module summarizes its pages; an open module lets each page count itself. */\nexport function getModuleNotificationCount(item: NavigationItemData, submenuOpened: boolean) {\n const childCount = (item.submenu?.groups ?? []).reduce(\n (total, group) =>\n total +\n group.links.reduce(\n (sum, link) => sum + getNotificationCount(link.counter ?? link.tag?.value),\n 0\n ),\n 0\n );\n return childCount > 0\n ? submenuOpened\n ? 0\n : childCount\n : getNotificationCount(item.counter ?? item.tag?.value);\n}\n\nexport interface NotificationRowBounds {\n top: number;\n bottom: number;\n count: number;\n}\n\n/** Only fully offscreen rows contribute; partially visible notifications are still discoverable. */\nexport function getOverflowNotifications(\n rows: NotificationRowBounds[],\n viewport: { top: number; bottom: number }\n) {\n let above = 0;\n let below = 0;\n let aboveIndex = -1;\n let belowIndex = -1;\n rows.forEach((row, index) => {\n if (row.count <= 0 || row.bottom <= row.top) {\n return;\n }\n if (row.bottom <= viewport.top) {\n above += row.count;\n aboveIndex = index;\n } else if (row.top >= viewport.bottom) {\n below += row.count;\n if (belowIndex === -1) {\n belowIndex = index;\n }\n }\n });\n return { above, below, aboveIndex, belowIndex };\n}\n"],"names":["getNotificationCount","value","Number","isFinite","getModuleNotificationCount","item","submenuOpened","childCount","submenu","groups","reduce","total","group","links","sum","link","counter","tag","getOverflowNotifications","rows","viewport","above","below","aboveIndex","belowIndex","forEach","row","index","count","bottom","top"],"mappings":"AAGA,OAAO,MAAMA,uBAAuB,CAACC,QACjCA,UAAU,OACJ,IACA,OAAOA,UAAU,YAAYC,OAAOC,QAAQ,CAACF,UAAUA,QAAQ,IAC7DA,QACA,EAAE;AAEd,sFAAsF,GACtF,OAAO,SAASG,2BAA2BC,IAAwB,EAAEC,aAAsB;cAc5DD;QAbPA,eAauBA;IAb3C,MAAME,aAAa,UAACF,gBAAAA,KAAKG,OAAO,cAAZH,oCAAAA,cAAcI,MAAM,uCAAI,EAAE,EAAEC,MAAM,CAClD,CAACC,OAAOC,QACJD,QACAC,MAAMC,KAAK,CAACH,MAAM,CACd,CAACI,KAAKC;gBAAoCA;gBAAgBA;mBAA3CD,MAAMd,sBAAqBe,gBAAAA,KAAKC,OAAO,cAAZD,2BAAAA,iBAAgBA,YAAAA,KAAKE,GAAG,cAARF,gCAAAA,UAAUd,KAAK;WACzE,IAER;IAEJ,OAAOM,aAAa,IACdD,gBACI,IACAC,aACJP,sBAAqBK,gBAAAA,KAAKW,OAAO,cAAZX,2BAAAA,iBAAgBA,YAAAA,KAAKY,GAAG,cAARZ,gCAAAA,UAAUJ,KAAK;AAC9D;AAQA,kGAAkG,GAClG,OAAO,SAASiB,yBACZC,IAA6B,EAC7BC,QAAyC;IAEzC,IAAIC,QAAQ;IACZ,IAAIC,QAAQ;IACZ,IAAIC,aAAa,CAAC;IAClB,IAAIC,aAAa,CAAC;IAClBL,KAAKM,OAAO,CAAC,CAACC,KAAKC;QACf,IAAID,IAAIE,KAAK,IAAI,KAAKF,IAAIG,MAAM,IAAIH,IAAII,GAAG,EAAE;YACzC;QACJ;QACA,IAAIJ,IAAIG,MAAM,IAAIT,SAASU,GAAG,EAAE;YAC5BT,SAASK,IAAIE,KAAK;YAClBL,aAAaI;QACjB,OAAO,IAAID,IAAII,GAAG,IAAIV,SAASS,MAAM,EAAE;YACnCP,SAASI,IAAIE,KAAK;YAClB,IAAIJ,eAAe,CAAC,GAAG;gBACnBA,aAAaG;YACjB;QACJ;IACJ;IACA,OAAO;QAAEN;QAAOC;QAAOC;QAAYC;IAAW;AAClD"}
|
package/dist/utils/side-nav.js
CHANGED
|
@@ -5,7 +5,9 @@ export function getSubmenuGroupTag(submenu, defaultTag) {
|
|
|
5
5
|
let tagValue = undefined;
|
|
6
6
|
for (const group of submenu.groups){
|
|
7
7
|
for (const link of group.links){
|
|
8
|
-
|
|
8
|
+
var _link_counter;
|
|
9
|
+
var _link_tag;
|
|
10
|
+
const ltv = (_link_counter = link.counter) !== null && _link_counter !== void 0 ? _link_counter : (_link_tag = link.tag) === null || _link_tag === void 0 ? void 0 : _link_tag.value;
|
|
9
11
|
if (ltv) {
|
|
10
12
|
if (typeof ltv === 'number') {
|
|
11
13
|
if (typeof tagValue !== 'number') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/side-nav.ts"],"sourcesContent":["import { CounterTagProps } from '../components/counter-tag';\nimport { CounterTagData, CounterTagValue } from './counter-tag';\nimport { NavigationSubmenuData } from './navigation';\n\nexport function getSubmenuGroupTag(\n submenu: NavigationSubmenuData | undefined,\n defaultTag: CounterTagProps | undefined\n): CounterTagProps | undefined {\n if (!submenu) {\n return defaultTag;\n }\n\n let tagValue: number | boolean | undefined = undefined;\n\n for (const group of submenu.groups) {\n for (const link of group.links) {\n const ltv: number | boolean | undefined = link.counter;\n\n if (ltv) {\n if (typeof ltv === 'number') {\n if (typeof tagValue !== 'number') {\n tagValue = 0;\n }\n tagValue += ltv;\n } else if (typeof tagValue !== 'number') {\n tagValue = true;\n }\n }\n }\n }\n\n return tagValue ? getCounterTag(tagValue) : defaultTag;\n}\n\nexport const getCounterTag = (\n counter: CounterTagValue | undefined,\n tag?: CounterTagData\n): CounterTagProps | undefined => {\n const value = counter ?? tag?.value;\n\n return value ? { value, className: '' } : undefined;\n};\n"],"names":["getSubmenuGroupTag","submenu","defaultTag","tagValue","undefined","group","groups","link","links","ltv","counter","
|
|
1
|
+
{"version":3,"sources":["../../src/utils/side-nav.ts"],"sourcesContent":["import { CounterTagProps } from '../components/counter-tag';\nimport { CounterTagData, CounterTagValue } from './counter-tag';\nimport { NavigationSubmenuData } from './navigation';\n\nexport function getSubmenuGroupTag(\n submenu: NavigationSubmenuData | undefined,\n defaultTag: CounterTagProps | undefined\n): CounterTagProps | undefined {\n if (!submenu) {\n return defaultTag;\n }\n\n let tagValue: number | boolean | undefined = undefined;\n\n for (const group of submenu.groups) {\n for (const link of group.links) {\n const ltv: number | boolean | undefined = link.counter ?? link.tag?.value;\n\n if (ltv) {\n if (typeof ltv === 'number') {\n if (typeof tagValue !== 'number') {\n tagValue = 0;\n }\n tagValue += ltv;\n } else if (typeof tagValue !== 'number') {\n tagValue = true;\n }\n }\n }\n }\n\n return tagValue ? getCounterTag(tagValue) : defaultTag;\n}\n\nexport const getCounterTag = (\n counter: CounterTagValue | undefined,\n tag?: CounterTagData\n): CounterTagProps | undefined => {\n const value = counter ?? tag?.value;\n\n return value ? { value, className: '' } : undefined;\n};\n"],"names":["getSubmenuGroupTag","submenu","defaultTag","tagValue","undefined","group","groups","link","links","ltv","counter","tag","value","getCounterTag","className"],"mappings":"AAIA,OAAO,SAASA,mBACZC,OAA0C,EAC1CC,UAAuC;IAEvC,IAAI,CAACD,SAAS;QACV,OAAOC;IACX;IAEA,IAAIC,WAAyCC;IAE7C,KAAK,MAAMC,SAASJ,QAAQK,MAAM,CAAE;QAChC,KAAK,MAAMC,QAAQF,MAAMG,KAAK,CAAE;gBACcD;gBAAgBA;YAA1D,MAAME,OAAoCF,gBAAAA,KAAKG,OAAO,cAAZH,2BAAAA,iBAAgBA,YAAAA,KAAKI,GAAG,cAARJ,gCAAAA,UAAUK,KAAK;YAEzE,IAAIH,KAAK;gBACL,IAAI,OAAOA,QAAQ,UAAU;oBACzB,IAAI,OAAON,aAAa,UAAU;wBAC9BA,WAAW;oBACf;oBACAA,YAAYM;gBAChB,OAAO,IAAI,OAAON,aAAa,UAAU;oBACrCA,WAAW;gBACf;YACJ;QACJ;IACJ;IAEA,OAAOA,WAAWU,cAAcV,YAAYD;AAChD;AAEA,OAAO,MAAMW,gBAAgB,CACzBH,SACAC;IAEA,MAAMC,QAAQF,oBAAAA,qBAAAA,UAAWC,gBAAAA,0BAAAA,IAAKC,KAAK;IAEnC,OAAOA,QAAQ;QAAEA;QAAOE,WAAW;IAAG,IAAIV;AAC9C,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submenu-state.d.ts","sourceRoot":"","sources":["../../src/utils/submenu-state.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,eAAO,MAAM,kBAAkB,GAC3B,SAAS,SAAS,MAAM,EAAE,EAC1B,WAAW,SAAS,MAAM,EAAE,EAC5B,QAAQ,OAAO,KAChB,MAAM,EAUR,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Update only the requested modules, preserving the state of modules outside the action. */ export const updateOpenSubmenus = (current, moduleIds, opened)=>{
|
|
2
|
+
const next = new Set(current);
|
|
3
|
+
moduleIds.forEach((id)=>{
|
|
4
|
+
if (opened) {
|
|
5
|
+
next.add(id);
|
|
6
|
+
} else {
|
|
7
|
+
next.delete(id);
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
return [
|
|
11
|
+
...next
|
|
12
|
+
];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=submenu-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/submenu-state.ts"],"sourcesContent":["/** Update only the requested modules, preserving the state of modules outside the action. */\nexport const updateOpenSubmenus = (\n current: readonly string[],\n moduleIds: readonly string[],\n opened: boolean\n): string[] => {\n const next = new Set(current);\n moduleIds.forEach(id => {\n if (opened) {\n next.add(id);\n } else {\n next.delete(id);\n }\n });\n return [...next];\n};\n"],"names":["updateOpenSubmenus","current","moduleIds","opened","next","Set","forEach","id","add","delete"],"mappings":"AAAA,2FAA2F,GAC3F,OAAO,MAAMA,qBAAqB,CAC9BC,SACAC,WACAC;IAEA,MAAMC,OAAO,IAAIC,IAAIJ;IACrBC,UAAUI,OAAO,CAACC,CAAAA;QACd,IAAIJ,QAAQ;YACRC,KAAKI,GAAG,CAACD;QACb,OAAO;YACHH,KAAKK,MAAM,CAACF;QAChB;IACJ;IACA,OAAO;WAAIH;KAAK;AACpB,EAAE"}
|