@servicetitan/navigation 14.4.0 → 14.5.1
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/layout-sidebar.d.ts.map +1 -1
- package/dist/components/titan-layout/layout-sidebar.js +30 -11
- package/dist/components/titan-layout/layout-sidebar.js.map +1 -1
- package/dist/components/titan-layout/layout-sidebar.module.less +1 -1
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.js +1 -1
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog-trigger.js.map +1 -1
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.d.ts.map +1 -1
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.js +54 -63
- package/dist/components/titan-layout/navigation-catalog/navigation-catalog.js.map +1 -1
- package/dist/utils/__tests__/side-nav.test.d.ts +2 -0
- package/dist/utils/__tests__/side-nav.test.d.ts.map +1 -0
- package/dist/utils/side-nav.d.ts +17 -0
- package/dist/utils/side-nav.d.ts.map +1 -1
- package/dist/utils/side-nav.js +32 -0
- package/dist/utils/side-nav.js.map +1 -1
- package/package.json +2 -2
- package/src/components/titan-layout/__tests__/navigation-catalog-reset.test.tsx +59 -7
- package/src/components/titan-layout/layout-sidebar.module.less +1 -1
- package/src/components/titan-layout/layout-sidebar.tsx +34 -10
- package/src/components/titan-layout/navigation-catalog/navigation-catalog-trigger.tsx +1 -1
- package/src/components/titan-layout/navigation-catalog/navigation-catalog.tsx +58 -74
- package/src/utils/__tests__/side-nav.test.ts +77 -0
- package/src/utils/side-nav.ts +42 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout-sidebar.d.ts","sourceRoot":"","sources":["../../../src/components/titan-layout/layout-sidebar.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAY,EAAE,EAAY,YAAY,
|
|
1
|
+
{"version":3,"file":"layout-sidebar.d.ts","sourceRoot":"","sources":["../../../src/components/titan-layout/layout-sidebar.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAY,EAAE,EAAY,YAAY,EAAuC,MAAM,OAAO,CAAC;AAClG,OAAO,EAAE,kBAAkB,EAAyB,MAAM,wBAAwB,CAAC;AACnF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAQvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAcrD,MAAM,WAAW,kBAAkB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,YAAY,EAAE,CAAC;IAErB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAEhC,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACjC,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAC1C,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACrC,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,mBAAmB,EAAE,EAAE,CAAC,qBAAqB,CAAC,CAAC;IAC/C,iBAAiB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3C,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5C,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IACvD,oBAAoB,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5D,qBAAqB,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxE;AAED,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CA8KhD,CAAC"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Button, Divider, Icon, Popover, Text, ThemeProvider } from '@servicetitan/anvil2';
|
|
2
|
+
import { Button, Divider, Flex, Icon, Popover, Text, ThemeProvider } from '@servicetitan/anvil2';
|
|
3
3
|
import SvgClose from '@servicetitan/anvil2/assets/icons/material/round/close.svg';
|
|
4
4
|
import SvgCollapseAll from '@servicetitan/anvil2/assets/icons/material/round/unfold_less.svg';
|
|
5
5
|
import SvgExpandAll from '@servicetitan/anvil2/assets/icons/material/round/unfold_more.svg';
|
|
6
6
|
import SvgCollapse from '@servicetitan/anvil2/assets/icons/st/gnav_menu_collapse.svg';
|
|
7
7
|
import SvgExpand from '@servicetitan/anvil2/assets/icons/st/gnav_menu_expand.svg';
|
|
8
8
|
import classNames from 'classnames';
|
|
9
|
-
import { Children, Fragment, isValidElement } from 'react';
|
|
9
|
+
import { Children, Fragment, isValidElement, useCallback, useRef } from 'react';
|
|
10
10
|
import { getModuleNotificationCount } from '../../utils/navigation-notifications';
|
|
11
|
-
import { getCounterTag, getSubmenuGroupTag } from '../../utils/side-nav';
|
|
11
|
+
import { getCounterTag, getModuleActiveMatcher, getModuleDestination, getSubmenuGroupTag } from '../../utils/side-nav';
|
|
12
12
|
import { LayoutPlacementContext, useTitanLayoutContext } from './layout-context';
|
|
13
13
|
import { InternalSideNavigationGroup, InternalSideNavigationGroupLink, InternalSideNavigationLink } from './layout-sidebar-links-internal';
|
|
14
14
|
import * as Styles from './layout-sidebar.module.less';
|
|
@@ -164,7 +164,8 @@ export const LayoutSidebar = ({ className, mobile, touchDevice, barExpanded, sub
|
|
|
164
164
|
/*#__PURE__*/ _jsx("div", {
|
|
165
165
|
className: Styles.divider
|
|
166
166
|
}),
|
|
167
|
-
/*#__PURE__*/ _jsx(
|
|
167
|
+
/*#__PURE__*/ _jsx(Flex, {
|
|
168
|
+
direction: "column",
|
|
168
169
|
className: Styles.toggleWrapper,
|
|
169
170
|
children: /*#__PURE__*/ _jsx(SideNavigationOptionsToggle, {
|
|
170
171
|
expanded: barExpanded,
|
|
@@ -182,6 +183,8 @@ LayoutSidebar.displayName = 'LayoutSidebar';
|
|
|
182
183
|
/** Side Navigation options toggle */ const SideNavigationOptionsToggle = ({ expanded, onExpandedChange })=>withTooltip(/*#__PURE__*/ _jsx(Button, {
|
|
183
184
|
appearance: "ghost",
|
|
184
185
|
size: "small",
|
|
186
|
+
flexGrow: 1,
|
|
187
|
+
justifyContent: "flex-start",
|
|
185
188
|
icon: expanded ? SvgCollapse : SvgExpand,
|
|
186
189
|
"data-cy": "navigation-left-options",
|
|
187
190
|
"data-pendo": "navigation-left-options",
|
|
@@ -190,18 +193,32 @@ LayoutSidebar.displayName = 'LayoutSidebar';
|
|
|
190
193
|
className: Styles.toggle,
|
|
191
194
|
onClick: ()=>onExpandedChange === null || onExpandedChange === void 0 ? void 0 : onExpandedChange(!expanded)
|
|
192
195
|
}), expanded ? 'Collapse Menu' : 'Expand Menu', {
|
|
193
|
-
placement: 'right'
|
|
196
|
+
placement: 'right',
|
|
197
|
+
pointerEventsNone: true
|
|
194
198
|
});
|
|
195
199
|
/** Side Navigation menu item */ const SideNavigationGroupItem = ({ item, onSubmenuOpen, onNavigationLinkClick, barExpanded, submenuOpened, navigationComponent, touchDevice })=>{
|
|
196
|
-
var _ref, _ref1;
|
|
200
|
+
var _item_isActive, _ref, _ref1;
|
|
197
201
|
var _item_submenu, _item_submenu1;
|
|
198
202
|
const { sidebar: { styles: { popoverContent } } } = useTitanLayoutContext();
|
|
199
203
|
const tag = getSubmenuGroupTag(item.submenu, getCounterTag(item.counter, item.tag));
|
|
204
|
+
const isActive = (_item_isActive = item.isActive) !== null && _item_isActive !== void 0 ? _item_isActive : getModuleActiveMatcher(item.to, item.submenu);
|
|
205
|
+
/*
|
|
206
|
+
* Some destinations (legacy monolith pages) never fire the route-change signal the popover
|
|
207
|
+
* would otherwise rely on to dismiss itself, leaving it stranded open over the loaded page.
|
|
208
|
+
* Closing explicitly on click, rather than waiting on navigation to settle, works everywhere.
|
|
209
|
+
*/ const popoverRef = useRef(null);
|
|
210
|
+
const handleSubmenuLinkClick = useCallback((itemId, submenuItemId)=>{
|
|
211
|
+
var _popoverRef_current;
|
|
212
|
+
(_popoverRef_current = popoverRef.current) === null || _popoverRef_current === void 0 ? void 0 : _popoverRef_current.closePopover();
|
|
213
|
+
onNavigationLinkClick === null || onNavigationLinkClick === void 0 ? void 0 : onNavigationLinkClick(itemId, submenuItemId);
|
|
214
|
+
}, [
|
|
215
|
+
onNavigationLinkClick
|
|
216
|
+
]);
|
|
200
217
|
return barExpanded ? /*#__PURE__*/ _jsx(InternalSideNavigationGroup, {
|
|
201
218
|
id: item.id,
|
|
202
|
-
to: item.to,
|
|
219
|
+
to: getModuleDestination(item.to, item.submenu),
|
|
203
220
|
title: item.title,
|
|
204
|
-
isActive:
|
|
221
|
+
isActive: isActive,
|
|
205
222
|
icon: item.icon,
|
|
206
223
|
iconActive: item.iconActive,
|
|
207
224
|
className: item.className,
|
|
@@ -217,6 +234,7 @@ LayoutSidebar.displayName = 'LayoutSidebar';
|
|
|
217
234
|
onNavigationLinkClick: onNavigationLinkClick
|
|
218
235
|
})
|
|
219
236
|
}) : /*#__PURE__*/ _jsxs(Popover, {
|
|
237
|
+
ref: popoverRef,
|
|
220
238
|
placement: "right-start",
|
|
221
239
|
openOnHover: !touchDevice,
|
|
222
240
|
delay: 200,
|
|
@@ -230,9 +248,9 @@ LayoutSidebar.displayName = 'LayoutSidebar';
|
|
|
230
248
|
...triggerProps,
|
|
231
249
|
children: /*#__PURE__*/ _jsx(InternalSideNavigationLink, {
|
|
232
250
|
id: item.id,
|
|
233
|
-
to: item.to,
|
|
251
|
+
to: getModuleDestination(item.to, item.submenu),
|
|
234
252
|
title: (_item_collapsedTitle = item.collapsedTitle) !== null && _item_collapsedTitle !== void 0 ? _item_collapsedTitle : item.title,
|
|
235
|
-
isActive:
|
|
253
|
+
isActive: isActive,
|
|
236
254
|
icon: item.icon,
|
|
237
255
|
iconActive: item.iconActive,
|
|
238
256
|
className: classNames(item.className, touchDevice && Styles.pointerEventsNone),
|
|
@@ -265,7 +283,8 @@ LayoutSidebar.displayName = 'LayoutSidebar';
|
|
|
265
283
|
children: /*#__PURE__*/ _jsx(SideNavigationGroupContent, {
|
|
266
284
|
parentId: item.id,
|
|
267
285
|
groups: (_ref1 = (_item_submenu1 = item.submenu) === null || _item_submenu1 === void 0 ? void 0 : _item_submenu1.groups) !== null && _ref1 !== void 0 ? _ref1 : [],
|
|
268
|
-
navigationComponent: navigationComponent
|
|
286
|
+
navigationComponent: navigationComponent,
|
|
287
|
+
onNavigationLinkClick: handleSubmenuLinkClick
|
|
269
288
|
})
|
|
270
289
|
})
|
|
271
290
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/titan-layout/layout-sidebar.tsx"],"sourcesContent":["import { Button, Divider, Icon, Popover, Text, ThemeProvider } from '@servicetitan/anvil2';\nimport SvgClose from '@servicetitan/anvil2/assets/icons/material/round/close.svg';\nimport SvgCollapseAll from '@servicetitan/anvil2/assets/icons/material/round/unfold_less.svg';\nimport SvgExpandAll from '@servicetitan/anvil2/assets/icons/material/round/unfold_more.svg';\nimport SvgCollapse from '@servicetitan/anvil2/assets/icons/st/gnav_menu_collapse.svg';\nimport SvgExpand from '@servicetitan/anvil2/assets/icons/st/gnav_menu_expand.svg';\nimport classNames from 'classnames';\nimport { Children, FC, Fragment, ReactElement, isValidElement } from 'react';\nimport { NavigationItemData, NavigationSubmenuData } from '../../utils/navigation';\nimport { NavLinkComponentProps } from '../../utils/navigation-context';\nimport { getModuleNotificationCount } from '../../utils/navigation-notifications';\nimport { getCounterTag, getSubmenuGroupTag } from '../../utils/side-nav';\nimport { PinnedNavigationConfig } from './interface';\nimport { NavigationComponentProps } from './interface-internal';\nimport { LayoutPlacementContext, useTitanLayoutContext } from './layout-context';\nimport {\n InternalSideNavigationGroup,\n InternalSideNavigationGroupLink,\n InternalSideNavigationLink,\n} from './layout-sidebar-links-internal';\nimport * as Styles from './layout-sidebar.module.less';\nimport { NavigationCatalogTrigger } from './navigation-catalog';\nimport { NavigationScrollIndicators } from './navigation-scroll-indicators';\nimport { useNavigationScroll } from './use-navigation-scroll';\nimport { withTooltip } from './with-tooltip';\n\nexport interface LayoutSidebarProps {\n className?: string;\n top?: ReactElement[];\n\n /**\n * Destinations rendered above the divider, ahead of `top`.\n *\n * Separate from `top` rather than widening it: that one takes elements a host builds itself\n * and renders them as-is, which is what a bespoke control needs. This takes navigation items\n * and puts them through the same renderer as `mainItems`, so one with pages beneath it is an\n * expandable group instead of being flattened to a bare link.\n */\n topItems?: NavigationItemData[];\n\n /** Whether the rail shows the scroll and overflow-notification indicators. */\n scrollIndicators?: boolean;\n bottom?: ReactElement;\n mainItems?: NavigationItemData[];\n pinnedNavigation?: PinnedNavigationConfig;\n barExpanded: boolean;\n submenusOpened: string[] | undefined;\n drawerOpened: boolean;\n mobile: boolean;\n touchDevice: boolean;\n navigationComponent: FC<NavLinkComponentProps>;\n onBarExpandChange(expanded: boolean): void;\n onDrawerOpenChange(expanded: boolean): void;\n onSubmenuOpenChange(id: string, opened: boolean): void;\n onSubmenusOpenChange?(ids: string[], opened: boolean): void;\n onNavigationLinkClick?(itemId: string, submenuItemId?: string): void;\n}\n\nexport const LayoutSidebar: FC<LayoutSidebarProps> = ({\n className,\n mobile,\n touchDevice,\n barExpanded,\n submenusOpened,\n drawerOpened,\n onBarExpandChange,\n onSubmenuOpenChange,\n onSubmenusOpenChange,\n onDrawerOpenChange,\n onNavigationLinkClick,\n mainItems,\n pinnedNavigation,\n top,\n topItems,\n scrollIndicators,\n bottom,\n navigationComponent,\n}) => {\n const { regionRef, scrollRef, revealNotification, ...scrollOverflow } =\n useNavigationScroll(scrollIndicators);\n const closeDrawer = () => {\n if (mobile) {\n onDrawerOpenChange?.(false);\n }\n };\n\n const effectiveExpanded = mobile ? drawerOpened : barExpanded;\n /*\n * Both regions, because a destination above the divider expands exactly like one below it.\n * Counting only `mainItems` would let Expand All skip those groups — and hide the control\n * entirely on a rail whose only expandable modules sit up there.\n */\n const expandableIds = [...(topItems ?? []), ...(mainItems ?? [])]\n .filter(item => item.submenu?.groups.some(group => group.links.length > 0))\n .map(item => item.id);\n const hasOpenSubmenus = expandableIds.some(id => submenusOpened?.includes(id));\n const toggleAllLabel = hasOpenSubmenus ? 'Collapse All' : 'Expand All';\n const toggleAll =\n effectiveExpanded && expandableIds.length > 0 && onSubmenusOpenChange\n ? withTooltip(\n <Button\n appearance=\"secondary\"\n size=\"small\"\n icon={hasOpenSubmenus ? SvgCollapseAll : SvgExpandAll}\n aria-label={toggleAllLabel}\n aria-expanded={hasOpenSubmenus}\n data-cy=\"navigation-toggle-all\"\n data-pendo=\"navigation-toggle-all\"\n onClick={event => {\n event.stopPropagation();\n onSubmenusOpenChange(expandableIds, !hasOpenSubmenus);\n }}\n />,\n toggleAllLabel,\n { placement: 'top' }\n )\n : undefined;\n\n const scrollHeader = !mobile && !barExpanded;\n const navigationHeader = (\n <div className={Styles.navHeader}>\n {mobile && (\n <div className={Styles.navCloseWrapper}>\n <div className={Styles.navClose} onClick={() => onBarExpandChange(false)}>\n <Icon svg={SvgClose} size=\"large\" />\n </div>\n </div>\n )}\n {pinnedNavigation && (\n <NavigationCatalogTrigger\n expanded={effectiveExpanded}\n {...pinnedNavigation}\n trailingAction={toggleAll}\n />\n )}\n {!pinnedNavigation && toggleAll && <div className={Styles.navTools}>{toggleAll}</div>}\n </div>\n );\n\n /*\n * Shared by the region above the divider and the list below it, so a destination renders the\n * same way wherever the host puts it. Two copies of this branch would drift.\n */\n const renderItems = (items: NavigationItemData[] | undefined) =>\n items?.map(item =>\n item.submenu ? (\n <SideNavigationGroupItem\n key={item.id}\n item={item}\n barExpanded={effectiveExpanded}\n submenuOpened={!!item.id && !!submenusOpened?.includes(item.id)}\n onSubmenuOpen={onSubmenuOpenChange}\n navigationComponent={navigationComponent}\n onNavigationLinkClick={onNavigationLinkClick}\n touchDevice={touchDevice}\n />\n ) : (\n <InternalSideNavigationLink\n key={item.id}\n id={item.id}\n to={item.to}\n title={effectiveExpanded ? item.title : (item.collapsedTitle ?? item.title)}\n isActive={item.isActive}\n icon={item.icon}\n iconActive={item.iconActive}\n className={item.className}\n tag={getSubmenuGroupTag(item.submenu, getCounterTag(item.counter, item.tag))}\n navigationComponent={navigationComponent}\n />\n )\n );\n\n return (\n <LayoutPlacementContext.Provider value=\"side\">\n {mobile && drawerOpened && (\n <div className={Styles.navDrawerBackdrop} onClick={closeDrawer} />\n )}\n <div\n className={classNames(\n Styles.nav,\n mobile && Styles.navDrawer,\n mobile && drawerOpened && Styles.navDrawerOpened,\n !mobile && (barExpanded ? Styles.navWide : Styles.navSlim),\n className\n )}\n data-cy=\"side-navigation\"\n onClick={closeDrawer}\n >\n <ThemeProvider mode=\"dark\" className={Styles.navContent}>\n {!scrollHeader && navigationHeader}\n <div ref={regionRef} className={Styles.navScrollRegion}>\n <div ref={scrollRef} className={Styles.navMain} data-cy=\"navigation-scroll\">\n <div>\n {scrollHeader && navigationHeader}\n {(!!top?.length || !!topItems?.length) && (\n <SidebarTop>\n {renderItems(topItems)}\n {top}\n </SidebarTop>\n )}\n\n <div className={Styles.navItems} data-cy=\"navigation-items\">\n {renderItems(mainItems)}\n </div>\n\n {!!bottom && <SidebarBottom>{bottom}</SidebarBottom>}\n </div>\n </div>\n {scrollIndicators && (\n <NavigationScrollIndicators\n {...scrollOverflow}\n expanded={effectiveExpanded}\n onReveal={revealNotification}\n />\n )}\n </div>\n </ThemeProvider>\n\n {!mobile && (\n <ThemeProvider mode=\"dark\" className={Styles.navFooter}>\n <div className={Styles.divider} />\n <div className={Styles.toggleWrapper}>\n <SideNavigationOptionsToggle\n expanded={barExpanded}\n onExpandedChange={() => onBarExpandChange(!barExpanded)}\n />\n </div>\n </ThemeProvider>\n )}\n </div>\n </LayoutPlacementContext.Provider>\n );\n};\nLayoutSidebar.displayName = 'LayoutSidebar';\n\n/** Side Navigation options toggle */\nconst SideNavigationOptionsToggle: FC<{\n expanded: boolean;\n onExpandedChange?(expanded: boolean): void;\n}> = ({ expanded, onExpandedChange }) =>\n withTooltip(\n <Button\n appearance=\"ghost\"\n size=\"small\"\n icon={expanded ? SvgCollapse : SvgExpand}\n data-cy=\"navigation-left-options\"\n data-pendo=\"navigation-left-options\"\n aria-label={expanded ? 'Collapse Menu' : 'Expand Menu'}\n aria-expanded={expanded}\n className={Styles.toggle}\n onClick={() => onExpandedChange?.(!expanded)}\n />,\n expanded ? 'Collapse Menu' : 'Expand Menu',\n { placement: 'right' }\n );\n\n/** Side Navigation menu item */\nconst SideNavigationGroupItem: FC<{\n item: NavigationItemData;\n navigationComponent: FC<NavLinkComponentProps>;\n touchDevice: boolean;\n barExpanded: boolean;\n submenuOpened: boolean;\n onSubmenuOpen: (id: string, opened: boolean) => void;\n onNavigationLinkClick?: (itemId: string, submenuItemId?: string) => void;\n}> = ({\n item,\n onSubmenuOpen,\n onNavigationLinkClick,\n barExpanded,\n submenuOpened,\n navigationComponent,\n touchDevice,\n}) => {\n const {\n sidebar: {\n styles: { popoverContent },\n },\n } = useTitanLayoutContext();\n\n const tag = getSubmenuGroupTag(item.submenu, getCounterTag(item.counter, item.tag));\n\n return barExpanded ? (\n <InternalSideNavigationGroup\n id={item.id}\n to={item.to}\n title={item.title}\n isActive={item.isActive}\n icon={item.icon}\n iconActive={item.iconActive}\n className={item.className}\n tag={tag}\n notificationCount={getModuleNotificationCount(item, submenuOpened)}\n submenuOpened={submenuOpened}\n onSubmenuOpen={onSubmenuOpen}\n navigationComponent={navigationComponent}\n >\n <SideNavigationGroupContent\n parentId={item.id}\n groups={item.submenu?.groups ?? []}\n navigationComponent={navigationComponent}\n onNavigationLinkClick={onNavigationLinkClick}\n />\n </InternalSideNavigationGroup>\n ) : (\n <Popover\n placement=\"right-start\"\n openOnHover={!touchDevice}\n delay={200}\n noPadding\n disableCaret\n >\n <Popover.Trigger>\n {(triggerProps: any) => (\n <div {...triggerProps}>\n <InternalSideNavigationLink\n id={item.id}\n to={item.to}\n title={item.collapsedTitle ?? item.title}\n isActive={item.isActive}\n icon={item.icon}\n iconActive={item.iconActive}\n className={classNames(\n item.className,\n touchDevice && Styles.pointerEventsNone\n )}\n tag={tag}\n notificationCount={getModuleNotificationCount(item, false)}\n navigationComponent={navigationComponent}\n />\n </div>\n )}\n </Popover.Trigger>\n <Popover.Content\n style={popoverContent}\n className={classNames('z-global-nav-i', Styles.submenuPopoverContent)}\n data-cy=\"navigation-item-popover-content\"\n >\n <div className={Styles.submenuPopover}>\n <Text\n variant=\"headline\"\n el=\"h4\"\n size=\"small\"\n className={Styles.submenuPopoverTitle}\n >\n {item.title}\n </Text>\n <Divider className={Styles.submenuPopoverDivider} />\n <div className={Styles.submenuPopoverBody}>\n <SideNavigationGroupContent\n parentId={item.id}\n groups={item.submenu?.groups ?? []}\n navigationComponent={navigationComponent}\n />\n </div>\n </div>\n </Popover.Content>\n </Popover>\n );\n};\nconst SideNavigationGroupContent: FC<\n NavigationSubmenuData &\n NavigationComponentProps & {\n parentId: string;\n onNavigationLinkClick?: (itemId: string, submenuItemId?: string) => void;\n }\n> = ({ groups, parentId, navigationComponent, onNavigationLinkClick }) => {\n return (\n <Fragment>\n {groups.map((group, index) => {\n if (!group.links.length) {\n return null;\n }\n\n const title = group.title?.trim() ?? '';\n /* eslint-disable react/no-array-index-key */\n return (\n <div key={`:group:${parentId}:${index}`} className={Styles.submenuGroup}>\n {title && (\n <Text\n variant=\"eyebrow\"\n size=\"small\"\n className={Styles.submenuGroupHeader}\n >\n {title}\n </Text>\n )}\n {group.links.map((link, index) => (\n <InternalSideNavigationGroupLink\n key={`:${parentId}:${link.id}:${index}`}\n id={link.id}\n to={link.to}\n title={link.title}\n isActive={link.isActive}\n className={undefined}\n tag={getCounterTag(link.counter, link.tag)}\n parentId={parentId}\n navigationComponent={navigationComponent}\n onLinkClick={onNavigationLinkClick}\n />\n ))}\n </div>\n );\n /* eslint-enable react/no-array-index-key */\n })}\n </Fragment>\n );\n};\n\nfunction SidebarTop({ children }: any) {\n const list = Children.map(children, child => {\n return child && isValidElement(child) ? child : null;\n });\n return list?.length ? (\n <ThemeProvider mode=\"dark\" className={Styles.navTop} data-cy=\"navigation-items-top\">\n <div className={Styles.navTopItems}>{list}</div>\n {/* Anvil 2 spacing 3 resolves to the design's 12px above and below. */}\n <Divider spacing={3} className={Styles.topDivider} />\n </ThemeProvider>\n ) : null;\n}\n\nfunction SidebarBottom({ children }: any) {\n return (\n <ThemeProvider mode=\"dark\" className={Styles.navBottom} data-cy=\"navigation-items-bottom\">\n <div className={Styles.divider} />\n {children}\n </ThemeProvider>\n );\n}\n"],"names":["Button","Divider","Icon","Popover","Text","ThemeProvider","SvgClose","SvgCollapseAll","SvgExpandAll","SvgCollapse","SvgExpand","classNames","Children","Fragment","isValidElement","getModuleNotificationCount","getCounterTag","getSubmenuGroupTag","LayoutPlacementContext","useTitanLayoutContext","InternalSideNavigationGroup","InternalSideNavigationGroupLink","InternalSideNavigationLink","Styles","NavigationCatalogTrigger","NavigationScrollIndicators","useNavigationScroll","withTooltip","LayoutSidebar","className","mobile","touchDevice","barExpanded","submenusOpened","drawerOpened","onBarExpandChange","onSubmenuOpenChange","onSubmenusOpenChange","onDrawerOpenChange","onNavigationLinkClick","mainItems","pinnedNavigation","top","topItems","scrollIndicators","bottom","navigationComponent","regionRef","scrollRef","revealNotification","scrollOverflow","closeDrawer","effectiveExpanded","expandableIds","filter","item","submenu","groups","some","group","links","length","map","id","hasOpenSubmenus","includes","toggleAllLabel","toggleAll","appearance","size","icon","aria-label","aria-expanded","data-cy","data-pendo","onClick","event","stopPropagation","placement","undefined","scrollHeader","navigationHeader","div","navHeader","navCloseWrapper","navClose","svg","expanded","trailingAction","navTools","renderItems","items","SideNavigationGroupItem","submenuOpened","onSubmenuOpen","to","title","collapsedTitle","isActive","iconActive","tag","counter","Provider","value","navDrawerBackdrop","nav","navDrawer","navDrawerOpened","navWide","navSlim","mode","navContent","ref","navScrollRegion","navMain","SidebarTop","navItems","SidebarBottom","onReveal","navFooter","divider","toggleWrapper","SideNavigationOptionsToggle","onExpandedChange","displayName","toggle","sidebar","styles","popoverContent","notificationCount","SideNavigationGroupContent","parentId","openOnHover","delay","noPadding","disableCaret","Trigger","triggerProps","pointerEventsNone","Content","style","submenuPopoverContent","submenuPopover","variant","el","submenuPopoverTitle","submenuPopoverDivider","submenuPopoverBody","index","trim","submenuGroup","submenuGroupHeader","link","onLinkClick","children","list","child","navTop","navTopItems","spacing","topDivider","navBottom"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/titan-layout/layout-sidebar.tsx"],"sourcesContent":["import { Button, Divider, Flex, Icon, Popover, Text, ThemeProvider } from '@servicetitan/anvil2';\nimport SvgClose from '@servicetitan/anvil2/assets/icons/material/round/close.svg';\nimport SvgCollapseAll from '@servicetitan/anvil2/assets/icons/material/round/unfold_less.svg';\nimport SvgExpandAll from '@servicetitan/anvil2/assets/icons/material/round/unfold_more.svg';\nimport SvgCollapse from '@servicetitan/anvil2/assets/icons/st/gnav_menu_collapse.svg';\nimport SvgExpand from '@servicetitan/anvil2/assets/icons/st/gnav_menu_expand.svg';\nimport classNames from 'classnames';\nimport { Children, FC, Fragment, ReactElement, isValidElement, useCallback, useRef } from 'react';\nimport { NavigationItemData, NavigationSubmenuData } from '../../utils/navigation';\nimport { NavLinkComponentProps } from '../../utils/navigation-context';\nimport { getModuleNotificationCount } from '../../utils/navigation-notifications';\nimport {\n getCounterTag,\n getModuleActiveMatcher,\n getModuleDestination,\n getSubmenuGroupTag,\n} from '../../utils/side-nav';\nimport { PinnedNavigationConfig } from './interface';\nimport { NavigationComponentProps } from './interface-internal';\nimport { LayoutPlacementContext, useTitanLayoutContext } from './layout-context';\nimport {\n InternalSideNavigationGroup,\n InternalSideNavigationGroupLink,\n InternalSideNavigationLink,\n} from './layout-sidebar-links-internal';\nimport * as Styles from './layout-sidebar.module.less';\nimport { NavigationCatalogTrigger } from './navigation-catalog';\nimport { NavigationScrollIndicators } from './navigation-scroll-indicators';\nimport { useNavigationScroll } from './use-navigation-scroll';\nimport { withTooltip } from './with-tooltip';\n\nexport interface LayoutSidebarProps {\n className?: string;\n top?: ReactElement[];\n\n /**\n * Destinations rendered above the divider, ahead of `top`.\n *\n * Separate from `top` rather than widening it: that one takes elements a host builds itself\n * and renders them as-is, which is what a bespoke control needs. This takes navigation items\n * and puts them through the same renderer as `mainItems`, so one with pages beneath it is an\n * expandable group instead of being flattened to a bare link.\n */\n topItems?: NavigationItemData[];\n\n /** Whether the rail shows the scroll and overflow-notification indicators. */\n scrollIndicators?: boolean;\n bottom?: ReactElement;\n mainItems?: NavigationItemData[];\n pinnedNavigation?: PinnedNavigationConfig;\n barExpanded: boolean;\n submenusOpened: string[] | undefined;\n drawerOpened: boolean;\n mobile: boolean;\n touchDevice: boolean;\n navigationComponent: FC<NavLinkComponentProps>;\n onBarExpandChange(expanded: boolean): void;\n onDrawerOpenChange(expanded: boolean): void;\n onSubmenuOpenChange(id: string, opened: boolean): void;\n onSubmenusOpenChange?(ids: string[], opened: boolean): void;\n onNavigationLinkClick?(itemId: string, submenuItemId?: string): void;\n}\n\nexport const LayoutSidebar: FC<LayoutSidebarProps> = ({\n className,\n mobile,\n touchDevice,\n barExpanded,\n submenusOpened,\n drawerOpened,\n onBarExpandChange,\n onSubmenuOpenChange,\n onSubmenusOpenChange,\n onDrawerOpenChange,\n onNavigationLinkClick,\n mainItems,\n pinnedNavigation,\n top,\n topItems,\n scrollIndicators,\n bottom,\n navigationComponent,\n}) => {\n const { regionRef, scrollRef, revealNotification, ...scrollOverflow } =\n useNavigationScroll(scrollIndicators);\n const closeDrawer = () => {\n if (mobile) {\n onDrawerOpenChange?.(false);\n }\n };\n\n const effectiveExpanded = mobile ? drawerOpened : barExpanded;\n /*\n * Both regions, because a destination above the divider expands exactly like one below it.\n * Counting only `mainItems` would let Expand All skip those groups — and hide the control\n * entirely on a rail whose only expandable modules sit up there.\n */\n const expandableIds = [...(topItems ?? []), ...(mainItems ?? [])]\n .filter(item => item.submenu?.groups.some(group => group.links.length > 0))\n .map(item => item.id);\n const hasOpenSubmenus = expandableIds.some(id => submenusOpened?.includes(id));\n const toggleAllLabel = hasOpenSubmenus ? 'Collapse All' : 'Expand All';\n const toggleAll =\n effectiveExpanded && expandableIds.length > 0 && onSubmenusOpenChange\n ? withTooltip(\n <Button\n appearance=\"secondary\"\n size=\"small\"\n icon={hasOpenSubmenus ? SvgCollapseAll : SvgExpandAll}\n aria-label={toggleAllLabel}\n aria-expanded={hasOpenSubmenus}\n data-cy=\"navigation-toggle-all\"\n data-pendo=\"navigation-toggle-all\"\n onClick={event => {\n event.stopPropagation();\n onSubmenusOpenChange(expandableIds, !hasOpenSubmenus);\n }}\n />,\n toggleAllLabel,\n { placement: 'top' }\n )\n : undefined;\n\n const scrollHeader = !mobile && !barExpanded;\n const navigationHeader = (\n <div className={Styles.navHeader}>\n {mobile && (\n <div className={Styles.navCloseWrapper}>\n <div className={Styles.navClose} onClick={() => onBarExpandChange(false)}>\n <Icon svg={SvgClose} size=\"large\" />\n </div>\n </div>\n )}\n {pinnedNavigation && (\n <NavigationCatalogTrigger\n expanded={effectiveExpanded}\n {...pinnedNavigation}\n trailingAction={toggleAll}\n />\n )}\n {!pinnedNavigation && toggleAll && <div className={Styles.navTools}>{toggleAll}</div>}\n </div>\n );\n\n /*\n * Shared by the region above the divider and the list below it, so a destination renders the\n * same way wherever the host puts it. Two copies of this branch would drift.\n */\n const renderItems = (items: NavigationItemData[] | undefined) =>\n items?.map(item =>\n item.submenu ? (\n <SideNavigationGroupItem\n key={item.id}\n item={item}\n barExpanded={effectiveExpanded}\n submenuOpened={!!item.id && !!submenusOpened?.includes(item.id)}\n onSubmenuOpen={onSubmenuOpenChange}\n navigationComponent={navigationComponent}\n onNavigationLinkClick={onNavigationLinkClick}\n touchDevice={touchDevice}\n />\n ) : (\n <InternalSideNavigationLink\n key={item.id}\n id={item.id}\n to={item.to}\n title={effectiveExpanded ? item.title : (item.collapsedTitle ?? item.title)}\n isActive={item.isActive}\n icon={item.icon}\n iconActive={item.iconActive}\n className={item.className}\n tag={getSubmenuGroupTag(item.submenu, getCounterTag(item.counter, item.tag))}\n navigationComponent={navigationComponent}\n />\n )\n );\n\n return (\n <LayoutPlacementContext.Provider value=\"side\">\n {mobile && drawerOpened && (\n <div className={Styles.navDrawerBackdrop} onClick={closeDrawer} />\n )}\n <div\n className={classNames(\n Styles.nav,\n mobile && Styles.navDrawer,\n mobile && drawerOpened && Styles.navDrawerOpened,\n !mobile && (barExpanded ? Styles.navWide : Styles.navSlim),\n className\n )}\n data-cy=\"side-navigation\"\n onClick={closeDrawer}\n >\n <ThemeProvider mode=\"dark\" className={Styles.navContent}>\n {!scrollHeader && navigationHeader}\n <div ref={regionRef} className={Styles.navScrollRegion}>\n <div ref={scrollRef} className={Styles.navMain} data-cy=\"navigation-scroll\">\n <div>\n {scrollHeader && navigationHeader}\n {(!!top?.length || !!topItems?.length) && (\n <SidebarTop>\n {renderItems(topItems)}\n {top}\n </SidebarTop>\n )}\n\n <div className={Styles.navItems} data-cy=\"navigation-items\">\n {renderItems(mainItems)}\n </div>\n\n {!!bottom && <SidebarBottom>{bottom}</SidebarBottom>}\n </div>\n </div>\n {scrollIndicators && (\n <NavigationScrollIndicators\n {...scrollOverflow}\n expanded={effectiveExpanded}\n onReveal={revealNotification}\n />\n )}\n </div>\n </ThemeProvider>\n\n {!mobile && (\n <ThemeProvider mode=\"dark\" className={Styles.navFooter}>\n <div className={Styles.divider} />\n <Flex direction=\"column\" className={Styles.toggleWrapper}>\n <SideNavigationOptionsToggle\n expanded={barExpanded}\n onExpandedChange={() => onBarExpandChange(!barExpanded)}\n />\n </Flex>\n </ThemeProvider>\n )}\n </div>\n </LayoutPlacementContext.Provider>\n );\n};\nLayoutSidebar.displayName = 'LayoutSidebar';\n\n/** Side Navigation options toggle */\nconst SideNavigationOptionsToggle: FC<{\n expanded: boolean;\n onExpandedChange?(expanded: boolean): void;\n}> = ({ expanded, onExpandedChange }) =>\n withTooltip(\n <Button\n appearance=\"ghost\"\n size=\"small\"\n flexGrow={1}\n justifyContent=\"flex-start\"\n icon={expanded ? SvgCollapse : SvgExpand}\n data-cy=\"navigation-left-options\"\n data-pendo=\"navigation-left-options\"\n aria-label={expanded ? 'Collapse Menu' : 'Expand Menu'}\n aria-expanded={expanded}\n className={Styles.toggle}\n onClick={() => onExpandedChange?.(!expanded)}\n />,\n expanded ? 'Collapse Menu' : 'Expand Menu',\n { placement: 'right', pointerEventsNone: true }\n );\n\n/** Side Navigation menu item */\nconst SideNavigationGroupItem: FC<{\n item: NavigationItemData;\n navigationComponent: FC<NavLinkComponentProps>;\n touchDevice: boolean;\n barExpanded: boolean;\n submenuOpened: boolean;\n onSubmenuOpen: (id: string, opened: boolean) => void;\n onNavigationLinkClick?: (itemId: string, submenuItemId?: string) => void;\n}> = ({\n item,\n onSubmenuOpen,\n onNavigationLinkClick,\n barExpanded,\n submenuOpened,\n navigationComponent,\n touchDevice,\n}) => {\n const {\n sidebar: {\n styles: { popoverContent },\n },\n } = useTitanLayoutContext();\n\n const tag = getSubmenuGroupTag(item.submenu, getCounterTag(item.counter, item.tag));\n const isActive = item.isActive ?? getModuleActiveMatcher(item.to, item.submenu);\n\n /*\n * Some destinations (legacy monolith pages) never fire the route-change signal the popover\n * would otherwise rely on to dismiss itself, leaving it stranded open over the loaded page.\n * Closing explicitly on click, rather than waiting on navigation to settle, works everywhere.\n */\n const popoverRef = useRef<{ closePopover: () => void }>(null);\n const handleSubmenuLinkClick = useCallback(\n (itemId: string, submenuItemId?: string) => {\n popoverRef.current?.closePopover();\n onNavigationLinkClick?.(itemId, submenuItemId);\n },\n [onNavigationLinkClick]\n );\n\n return barExpanded ? (\n <InternalSideNavigationGroup\n id={item.id}\n to={getModuleDestination(item.to, item.submenu)}\n title={item.title}\n isActive={isActive}\n icon={item.icon}\n iconActive={item.iconActive}\n className={item.className}\n tag={tag}\n notificationCount={getModuleNotificationCount(item, submenuOpened)}\n submenuOpened={submenuOpened}\n onSubmenuOpen={onSubmenuOpen}\n navigationComponent={navigationComponent}\n >\n <SideNavigationGroupContent\n parentId={item.id}\n groups={item.submenu?.groups ?? []}\n navigationComponent={navigationComponent}\n onNavigationLinkClick={onNavigationLinkClick}\n />\n </InternalSideNavigationGroup>\n ) : (\n <Popover\n ref={popoverRef}\n placement=\"right-start\"\n openOnHover={!touchDevice}\n delay={200}\n noPadding\n disableCaret\n >\n <Popover.Trigger>\n {(triggerProps: any) => (\n <div {...triggerProps}>\n <InternalSideNavigationLink\n id={item.id}\n to={getModuleDestination(item.to, item.submenu)}\n title={item.collapsedTitle ?? item.title}\n isActive={isActive}\n icon={item.icon}\n iconActive={item.iconActive}\n className={classNames(\n item.className,\n touchDevice && Styles.pointerEventsNone\n )}\n tag={tag}\n notificationCount={getModuleNotificationCount(item, false)}\n navigationComponent={navigationComponent}\n />\n </div>\n )}\n </Popover.Trigger>\n <Popover.Content\n style={popoverContent}\n className={classNames('z-global-nav-i', Styles.submenuPopoverContent)}\n data-cy=\"navigation-item-popover-content\"\n >\n <div className={Styles.submenuPopover}>\n <Text\n variant=\"headline\"\n el=\"h4\"\n size=\"small\"\n className={Styles.submenuPopoverTitle}\n >\n {item.title}\n </Text>\n <Divider className={Styles.submenuPopoverDivider} />\n <div className={Styles.submenuPopoverBody}>\n <SideNavigationGroupContent\n parentId={item.id}\n groups={item.submenu?.groups ?? []}\n navigationComponent={navigationComponent}\n onNavigationLinkClick={handleSubmenuLinkClick}\n />\n </div>\n </div>\n </Popover.Content>\n </Popover>\n );\n};\nconst SideNavigationGroupContent: FC<\n NavigationSubmenuData &\n NavigationComponentProps & {\n parentId: string;\n onNavigationLinkClick?: (itemId: string, submenuItemId?: string) => void;\n }\n> = ({ groups, parentId, navigationComponent, onNavigationLinkClick }) => {\n return (\n <Fragment>\n {groups.map((group, index) => {\n if (!group.links.length) {\n return null;\n }\n\n const title = group.title?.trim() ?? '';\n /* eslint-disable react/no-array-index-key */\n return (\n <div key={`:group:${parentId}:${index}`} className={Styles.submenuGroup}>\n {title && (\n <Text\n variant=\"eyebrow\"\n size=\"small\"\n className={Styles.submenuGroupHeader}\n >\n {title}\n </Text>\n )}\n {group.links.map((link, index) => (\n <InternalSideNavigationGroupLink\n key={`:${parentId}:${link.id}:${index}`}\n id={link.id}\n to={link.to}\n title={link.title}\n isActive={link.isActive}\n className={undefined}\n tag={getCounterTag(link.counter, link.tag)}\n parentId={parentId}\n navigationComponent={navigationComponent}\n onLinkClick={onNavigationLinkClick}\n />\n ))}\n </div>\n );\n /* eslint-enable react/no-array-index-key */\n })}\n </Fragment>\n );\n};\n\nfunction SidebarTop({ children }: any) {\n const list = Children.map(children, child => {\n return child && isValidElement(child) ? child : null;\n });\n return list?.length ? (\n <ThemeProvider mode=\"dark\" className={Styles.navTop} data-cy=\"navigation-items-top\">\n <div className={Styles.navTopItems}>{list}</div>\n {/* Anvil 2 spacing 3 resolves to the design's 12px above and below. */}\n <Divider spacing={3} className={Styles.topDivider} />\n </ThemeProvider>\n ) : null;\n}\n\nfunction SidebarBottom({ children }: any) {\n return (\n <ThemeProvider mode=\"dark\" className={Styles.navBottom} data-cy=\"navigation-items-bottom\">\n <div className={Styles.divider} />\n {children}\n </ThemeProvider>\n );\n}\n"],"names":["Button","Divider","Flex","Icon","Popover","Text","ThemeProvider","SvgClose","SvgCollapseAll","SvgExpandAll","SvgCollapse","SvgExpand","classNames","Children","Fragment","isValidElement","useCallback","useRef","getModuleNotificationCount","getCounterTag","getModuleActiveMatcher","getModuleDestination","getSubmenuGroupTag","LayoutPlacementContext","useTitanLayoutContext","InternalSideNavigationGroup","InternalSideNavigationGroupLink","InternalSideNavigationLink","Styles","NavigationCatalogTrigger","NavigationScrollIndicators","useNavigationScroll","withTooltip","LayoutSidebar","className","mobile","touchDevice","barExpanded","submenusOpened","drawerOpened","onBarExpandChange","onSubmenuOpenChange","onSubmenusOpenChange","onDrawerOpenChange","onNavigationLinkClick","mainItems","pinnedNavigation","top","topItems","scrollIndicators","bottom","navigationComponent","regionRef","scrollRef","revealNotification","scrollOverflow","closeDrawer","effectiveExpanded","expandableIds","filter","item","submenu","groups","some","group","links","length","map","id","hasOpenSubmenus","includes","toggleAllLabel","toggleAll","appearance","size","icon","aria-label","aria-expanded","data-cy","data-pendo","onClick","event","stopPropagation","placement","undefined","scrollHeader","navigationHeader","div","navHeader","navCloseWrapper","navClose","svg","expanded","trailingAction","navTools","renderItems","items","SideNavigationGroupItem","submenuOpened","onSubmenuOpen","to","title","collapsedTitle","isActive","iconActive","tag","counter","Provider","value","navDrawerBackdrop","nav","navDrawer","navDrawerOpened","navWide","navSlim","mode","navContent","ref","navScrollRegion","navMain","SidebarTop","navItems","SidebarBottom","onReveal","navFooter","divider","direction","toggleWrapper","SideNavigationOptionsToggle","onExpandedChange","displayName","flexGrow","justifyContent","toggle","pointerEventsNone","sidebar","styles","popoverContent","popoverRef","handleSubmenuLinkClick","itemId","submenuItemId","current","closePopover","notificationCount","SideNavigationGroupContent","parentId","openOnHover","delay","noPadding","disableCaret","Trigger","triggerProps","Content","style","submenuPopoverContent","submenuPopover","variant","el","submenuPopoverTitle","submenuPopoverDivider","submenuPopoverBody","index","trim","submenuGroup","submenuGroupHeader","link","onLinkClick","children","list","child","navTop","navTopItems","spacing","topDivider","navBottom"],"mappings":"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"}
|
|
@@ -401,7 +401,6 @@
|
|
|
401
401
|
background-color: @bg-color;
|
|
402
402
|
|
|
403
403
|
.toggle-wrapper {
|
|
404
|
-
display: flex;
|
|
405
404
|
padding: @spacing-1 @spacing-1 @spacing-1 14px;
|
|
406
405
|
}
|
|
407
406
|
|
|
@@ -620,6 +619,7 @@
|
|
|
620
619
|
|
|
621
620
|
.submenu-popover-title[data-anv][data-anv] {
|
|
622
621
|
color: @color-white;
|
|
622
|
+
font-size: @typescale-2;
|
|
623
623
|
margin-bottom: @spacing-0;
|
|
624
624
|
margin-top: @spacing-1;
|
|
625
625
|
padding: @spacing-1;
|
|
@@ -69,7 +69,7 @@ import * as styles from './navigation-catalog-trigger.module.less';
|
|
|
69
69
|
* that opens a dialog rather than a destination, so sharing the namespace invites a
|
|
70
70
|
* selector written for a link to match it.
|
|
71
71
|
*/ 'data-cy': 'navigation-catalog-trigger',
|
|
72
|
-
'data-pendo': '
|
|
72
|
+
'data-pendo': 'global-nav-go-to',
|
|
73
73
|
'appearance': 'secondary',
|
|
74
74
|
'size': 'small',
|
|
75
75
|
'aria-label': 'Go to',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/titan-layout/navigation-catalog/navigation-catalog-trigger.tsx"],"sourcesContent":["import { Button, Chip, Flex, Text, ThemeProvider } from '@servicetitan/anvil2';\nimport IconSearch from '@servicetitan/anvil2/assets/icons/material/round/search.svg';\nimport classNames from 'classnames';\nimport { FC, Fragment, ReactNode, useCallback, useEffect, useRef, useState } from 'react';\nimport { PinnedNavigationConfig } from '../interface';\nimport { NavigationCatalog } from './navigation-catalog';\n\nimport * as styles from './navigation-catalog-trigger.module.less';\n\n/**\n * True when the keystroke should open the catalog rather than reach the focused control.\n *\n * The shortcut is deliberately a modifier chord: a bare key would be swallowed by, or would steal\n * from, every text field on the page.\n */\nconst isCatalogShortcut = (event: KeyboardEvent): boolean =>\n (event.metaKey || event.ctrlKey) && !event.altKey && event.key.toLowerCase() === 'k';\n\n/**\n * The chord as the user's own keyboard prints it.\n *\n * The listener accepts either modifier on every platform, so this only decides which one to\n * advertise. `platform` is deprecated but is still the only reliable signal here; the user agent is\n * the fallback and a wrong guess costs nothing but a mislabelled badge.\n */\nconst shortcutLabel = (): string => {\n const platform =\n typeof window.navigator === 'undefined'\n ? ''\n : window.navigator.platform || window.navigator.userAgent;\n\n return /Mac|iPod|iPhone|iPad/.test(platform) ? '⌘ K' : 'Ctrl K';\n};\n\nexport interface NavigationCatalogTriggerProps extends PinnedNavigationConfig {\n /** Whether the rail is expanded; the compact trigger keeps its label below the icon. */\n expanded: boolean;\n /** Optional sidebar action beside the expanded search trigger. */\n trailingAction?: ReactNode;\n}\n\n/**\n * Sidebar entry point for the navigation catalog.\n *\n * Presented as a labelled control rather than a bare icon: the catalog is the only route to an\n * unpinned destination, so the affordance has to read as \"search everything\" at a glance. It is a\n * button and not an input — the real field lives in the dialog and takes focus on open, and two\n * inputs would mean typing in one and getting results in the other.\n *\n * The catalog is never opened automatically; an unpinned navigation is a deliberate state, and this\n * trigger plus its shortcut are what keep the user from being stranded there.\n */\nexport const NavigationCatalogTrigger: FC<NavigationCatalogTriggerProps> = ({\n expanded,\n trailingAction,\n ...catalog\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const wasOpen = useRef(false);\n\n const close = useCallback(() => setIsOpen(false), []);\n const toggle = useCallback(() => setIsOpen(open => !open), []);\n\n useEffect(() => {\n // The dialog unmounts on close, so restore focus after its modal content is removed.\n if (wasOpen.current && !isOpen) {\n triggerRef.current?.focus();\n }\n wasOpen.current = isOpen;\n }, [isOpen]);\n\n useEffect(() => {\n const handler = (event: KeyboardEvent) => {\n if (!isCatalogShortcut(event)) {\n return;\n }\n\n event.preventDefault();\n toggle();\n };\n\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [toggle]);\n\n /*\n * Labelled explicitly so the name stays \"Go to\" whether or not the text is rendered, and so the\n * shortcut badge never reads out as part of it.\n */\n const buttonProps = {\n /*\n * Not `navigation-item-*`: that prefix belongs to the rail's links, and this is a control\n * that opens a dialog rather than a destination, so sharing the namespace invites a\n * selector written for a link to match it.\n */\n 'data-cy': 'navigation-catalog-trigger',\n 'data-pendo': 'navigation-catalog-trigger',\n 'appearance': 'secondary',\n 'size': 'small',\n 'aria-label': 'Go to',\n 'aria-haspopup': 'dialog',\n 'aria-expanded': isOpen,\n 'aria-keyshortcuts': 'Meta+K Control+K',\n 'onClick': toggle,\n } as const;\n\n return (\n <Fragment>\n <div className={classNames(styles.container, !expanded && styles.containerCollapsed)}>\n {expanded ? (\n <Button\n {...buttonProps}\n ref={triggerRef}\n icon={{ before: IconSearch }}\n className={styles.trigger}\n >\n <Flex alignItems=\"center\" justifyContent=\"space-between\" gap={1} grow={1}>\n {/* The ellipsis is part of the label, matching the field in the dialog. */}\n <Text inline size=\"small\" className={styles.triggerLabel}>\n Go to...\n </Text>\n {/* Decorative: the chord is already on the button as aria-keyshortcuts. */}\n <Chip\n label={shortcutLabel()}\n size=\"small\"\n className={styles.shortcut}\n />\n </Flex>\n </Button>\n ) : (\n <Button\n {...buttonProps}\n ref={triggerRef}\n appearance=\"ghost\"\n icon={IconSearch}\n className={styles.triggerCollapsed}\n >\n <span className={styles.collapsedLabel}>Go to...</span>\n </Button>\n )}\n {expanded && trailingAction}\n </div>\n {/*\n * Mounted only while open, so a host that resolves its catalog lazily is not asked for\n * it on every page load, and a second navigation tree is not held inside the sidebar's\n * subtree for a dialog nobody opened.\n *\n * Dark, because the catalog belongs to the rail rather than to the page. The rail paints\n * itself dark whatever the application's mode is, and this is the rail's own menu — the\n * same `ThemeProvider` the rail uses on its own sections.\n *\n * A `ThemeProvider` rather than a second `AnvilProvider`: the latter is the\n * application-level provider, and mounting a nested one alongside the host's meant its\n * unmount tore down state the outer one was still relying on, so closing the catalog\n * left the rail unstyled.\n *\n * This only sets the mode, which is what makes the *text* tokens light. The surface is\n * matched to the rail separately, in `navigation-catalog.module.less` — anvil2's dark is\n * a neutral grey and the rail's is a navy, so the mode alone would leave two visibly\n * different darks.\n */}\n {isOpen && (\n <ThemeProvider mode=\"dark\">\n <NavigationCatalog open onClose={close} {...catalog} />\n </ThemeProvider>\n )}\n </Fragment>\n );\n};\n"],"names":["Button","Chip","Flex","Text","ThemeProvider","IconSearch","classNames","Fragment","useCallback","useEffect","useRef","useState","NavigationCatalog","styles","isCatalogShortcut","event","metaKey","ctrlKey","altKey","key","toLowerCase","shortcutLabel","platform","window","navigator","userAgent","test","NavigationCatalogTrigger","expanded","trailingAction","catalog","isOpen","setIsOpen","triggerRef","wasOpen","close","toggle","open","current","focus","handler","preventDefault","addEventListener","removeEventListener","buttonProps","div","className","container","containerCollapsed","ref","icon","before","trigger","alignItems","justifyContent","gap","grow","inline","size","triggerLabel","label","shortcut","appearance","triggerCollapsed","span","collapsedLabel","mode","onClose"],"mappings":";AAAA,SAASA,MAAM,EAAEC,IAAI,EAAEC,IAAI,EAAEC,IAAI,EAAEC,aAAa,QAAQ,uBAAuB;AAC/E,OAAOC,gBAAgB,8DAA8D;AACrF,OAAOC,gBAAgB,aAAa;AACpC,SAAaC,QAAQ,EAAaC,WAAW,EAAEC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAQ;AAE1F,SAASC,iBAAiB,QAAQ,uBAAuB;AAEzD,YAAYC,YAAY,2CAA2C;AAEnE;;;;;CAKC,GACD,MAAMC,oBAAoB,CAACC,QACvB,AAACA,CAAAA,MAAMC,OAAO,IAAID,MAAME,OAAO,AAAD,KAAM,CAACF,MAAMG,MAAM,IAAIH,MAAMI,GAAG,CAACC,WAAW,OAAO;AAErF;;;;;;CAMC,GACD,MAAMC,gBAAgB;IAClB,MAAMC,WACF,OAAOC,OAAOC,SAAS,KAAK,cACtB,KACAD,OAAOC,SAAS,CAACF,QAAQ,IAAIC,OAAOC,SAAS,CAACC,SAAS;IAEjE,OAAO,uBAAuBC,IAAI,CAACJ,YAAY,QAAQ;AAC3D;AASA;;;;;;;;;;CAUC,GACD,OAAO,MAAMK,2BAA8D,CAAC,EACxEC,QAAQ,EACRC,cAAc,EACd,GAAGC,SACN;IACG,MAAM,CAACC,QAAQC,UAAU,GAAGrB,SAAS;IACrC,MAAMsB,aAAavB,OAA0B;IAC7C,MAAMwB,UAAUxB,OAAO;IAEvB,MAAMyB,QAAQ3B,YAAY,IAAMwB,UAAU,QAAQ,EAAE;IACpD,MAAMI,SAAS5B,YAAY,IAAMwB,UAAUK,CAAAA,OAAQ,CAACA,OAAO,EAAE;IAE7D5B,UAAU;QACN,qFAAqF;QACrF,IAAIyB,QAAQI,OAAO,IAAI,CAACP,QAAQ;gBAC5BE;aAAAA,sBAAAA,WAAWK,OAAO,cAAlBL,0CAAAA,oBAAoBM,KAAK;QAC7B;QACAL,QAAQI,OAAO,GAAGP;IACtB,GAAG;QAACA;KAAO;IAEXtB,UAAU;QACN,MAAM+B,UAAU,CAACzB;YACb,IAAI,CAACD,kBAAkBC,QAAQ;gBAC3B;YACJ;YAEAA,MAAM0B,cAAc;YACpBL;QACJ;QAEAb,OAAOmB,gBAAgB,CAAC,WAAWF;QACnC,OAAO,IAAMjB,OAAOoB,mBAAmB,CAAC,WAAWH;IACvD,GAAG;QAACJ;KAAO;IAEX;;;KAGC,GACD,MAAMQ,cAAc;QAChB;;;;SAIC,GACD,WAAW;QACX,cAAc;QACd,cAAc;QACd,QAAQ;QACR,cAAc;QACd,iBAAiB;QACjB,iBAAiBb;QACjB,qBAAqB;QACrB,WAAWK;IACf;IAEA,qBACI,MAAC7B;;0BACG,MAACsC;gBAAIC,WAAWxC,WAAWO,OAAOkC,SAAS,EAAE,CAACnB,YAAYf,OAAOmC,kBAAkB;;oBAC9EpB,yBACG,KAAC5B;wBACI,GAAG4C,WAAW;wBACfK,KAAKhB;wBACLiB,MAAM;4BAAEC,QAAQ9C;wBAAW;wBAC3ByC,WAAWjC,OAAOuC,OAAO;kCAEzB,cAAA,MAAClD;4BAAKmD,YAAW;4BAASC,gBAAe;4BAAgBC,KAAK;4BAAGC,MAAM;;8CAEnE,KAACrD;oCAAKsD,MAAM;oCAACC,MAAK;oCAAQZ,WAAWjC,OAAO8C,YAAY;8CAAE;;8CAI1D,KAAC1D;oCACG2D,OAAOvC;oCACPqC,MAAK;oCACLZ,WAAWjC,OAAOgD,QAAQ;;;;uCAKtC,KAAC7D;wBACI,GAAG4C,WAAW;wBACfK,KAAKhB;wBACL6B,YAAW;wBACXZ,MAAM7C;wBACNyC,WAAWjC,OAAOkD,gBAAgB;kCAElC,cAAA,KAACC;4BAAKlB,WAAWjC,OAAOoD,cAAc;sCAAE;;;oBAG/CrC,YAAYC;;;YAqBhBE,wBACG,KAAC3B;gBAAc8D,MAAK;0BAChB,cAAA,KAACtD;oBAAkByB,IAAI;oBAAC8B,SAAShC;oBAAQ,GAAGL,OAAO;;;;;AAKvE,EAAE"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/titan-layout/navigation-catalog/navigation-catalog-trigger.tsx"],"sourcesContent":["import { Button, Chip, Flex, Text, ThemeProvider } from '@servicetitan/anvil2';\nimport IconSearch from '@servicetitan/anvil2/assets/icons/material/round/search.svg';\nimport classNames from 'classnames';\nimport { FC, Fragment, ReactNode, useCallback, useEffect, useRef, useState } from 'react';\nimport { PinnedNavigationConfig } from '../interface';\nimport { NavigationCatalog } from './navigation-catalog';\n\nimport * as styles from './navigation-catalog-trigger.module.less';\n\n/**\n * True when the keystroke should open the catalog rather than reach the focused control.\n *\n * The shortcut is deliberately a modifier chord: a bare key would be swallowed by, or would steal\n * from, every text field on the page.\n */\nconst isCatalogShortcut = (event: KeyboardEvent): boolean =>\n (event.metaKey || event.ctrlKey) && !event.altKey && event.key.toLowerCase() === 'k';\n\n/**\n * The chord as the user's own keyboard prints it.\n *\n * The listener accepts either modifier on every platform, so this only decides which one to\n * advertise. `platform` is deprecated but is still the only reliable signal here; the user agent is\n * the fallback and a wrong guess costs nothing but a mislabelled badge.\n */\nconst shortcutLabel = (): string => {\n const platform =\n typeof window.navigator === 'undefined'\n ? ''\n : window.navigator.platform || window.navigator.userAgent;\n\n return /Mac|iPod|iPhone|iPad/.test(platform) ? '⌘ K' : 'Ctrl K';\n};\n\nexport interface NavigationCatalogTriggerProps extends PinnedNavigationConfig {\n /** Whether the rail is expanded; the compact trigger keeps its label below the icon. */\n expanded: boolean;\n /** Optional sidebar action beside the expanded search trigger. */\n trailingAction?: ReactNode;\n}\n\n/**\n * Sidebar entry point for the navigation catalog.\n *\n * Presented as a labelled control rather than a bare icon: the catalog is the only route to an\n * unpinned destination, so the affordance has to read as \"search everything\" at a glance. It is a\n * button and not an input — the real field lives in the dialog and takes focus on open, and two\n * inputs would mean typing in one and getting results in the other.\n *\n * The catalog is never opened automatically; an unpinned navigation is a deliberate state, and this\n * trigger plus its shortcut are what keep the user from being stranded there.\n */\nexport const NavigationCatalogTrigger: FC<NavigationCatalogTriggerProps> = ({\n expanded,\n trailingAction,\n ...catalog\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const wasOpen = useRef(false);\n\n const close = useCallback(() => setIsOpen(false), []);\n const toggle = useCallback(() => setIsOpen(open => !open), []);\n\n useEffect(() => {\n // The dialog unmounts on close, so restore focus after its modal content is removed.\n if (wasOpen.current && !isOpen) {\n triggerRef.current?.focus();\n }\n wasOpen.current = isOpen;\n }, [isOpen]);\n\n useEffect(() => {\n const handler = (event: KeyboardEvent) => {\n if (!isCatalogShortcut(event)) {\n return;\n }\n\n event.preventDefault();\n toggle();\n };\n\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [toggle]);\n\n /*\n * Labelled explicitly so the name stays \"Go to\" whether or not the text is rendered, and so the\n * shortcut badge never reads out as part of it.\n */\n const buttonProps = {\n /*\n * Not `navigation-item-*`: that prefix belongs to the rail's links, and this is a control\n * that opens a dialog rather than a destination, so sharing the namespace invites a\n * selector written for a link to match it.\n */\n 'data-cy': 'navigation-catalog-trigger',\n 'data-pendo': 'global-nav-go-to',\n 'appearance': 'secondary',\n 'size': 'small',\n 'aria-label': 'Go to',\n 'aria-haspopup': 'dialog',\n 'aria-expanded': isOpen,\n 'aria-keyshortcuts': 'Meta+K Control+K',\n 'onClick': toggle,\n } as const;\n\n return (\n <Fragment>\n <div className={classNames(styles.container, !expanded && styles.containerCollapsed)}>\n {expanded ? (\n <Button\n {...buttonProps}\n ref={triggerRef}\n icon={{ before: IconSearch }}\n className={styles.trigger}\n >\n <Flex alignItems=\"center\" justifyContent=\"space-between\" gap={1} grow={1}>\n {/* The ellipsis is part of the label, matching the field in the dialog. */}\n <Text inline size=\"small\" className={styles.triggerLabel}>\n Go to...\n </Text>\n {/* Decorative: the chord is already on the button as aria-keyshortcuts. */}\n <Chip\n label={shortcutLabel()}\n size=\"small\"\n className={styles.shortcut}\n />\n </Flex>\n </Button>\n ) : (\n <Button\n {...buttonProps}\n ref={triggerRef}\n appearance=\"ghost\"\n icon={IconSearch}\n className={styles.triggerCollapsed}\n >\n <span className={styles.collapsedLabel}>Go to...</span>\n </Button>\n )}\n {expanded && trailingAction}\n </div>\n {/*\n * Mounted only while open, so a host that resolves its catalog lazily is not asked for\n * it on every page load, and a second navigation tree is not held inside the sidebar's\n * subtree for a dialog nobody opened.\n *\n * Dark, because the catalog belongs to the rail rather than to the page. The rail paints\n * itself dark whatever the application's mode is, and this is the rail's own menu — the\n * same `ThemeProvider` the rail uses on its own sections.\n *\n * A `ThemeProvider` rather than a second `AnvilProvider`: the latter is the\n * application-level provider, and mounting a nested one alongside the host's meant its\n * unmount tore down state the outer one was still relying on, so closing the catalog\n * left the rail unstyled.\n *\n * This only sets the mode, which is what makes the *text* tokens light. The surface is\n * matched to the rail separately, in `navigation-catalog.module.less` — anvil2's dark is\n * a neutral grey and the rail's is a navy, so the mode alone would leave two visibly\n * different darks.\n */}\n {isOpen && (\n <ThemeProvider mode=\"dark\">\n <NavigationCatalog open onClose={close} {...catalog} />\n </ThemeProvider>\n )}\n </Fragment>\n );\n};\n"],"names":["Button","Chip","Flex","Text","ThemeProvider","IconSearch","classNames","Fragment","useCallback","useEffect","useRef","useState","NavigationCatalog","styles","isCatalogShortcut","event","metaKey","ctrlKey","altKey","key","toLowerCase","shortcutLabel","platform","window","navigator","userAgent","test","NavigationCatalogTrigger","expanded","trailingAction","catalog","isOpen","setIsOpen","triggerRef","wasOpen","close","toggle","open","current","focus","handler","preventDefault","addEventListener","removeEventListener","buttonProps","div","className","container","containerCollapsed","ref","icon","before","trigger","alignItems","justifyContent","gap","grow","inline","size","triggerLabel","label","shortcut","appearance","triggerCollapsed","span","collapsedLabel","mode","onClose"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation-catalog.d.ts","sourceRoot":"","sources":["../../../../src/components/titan-layout/navigation-catalog/navigation-catalog.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,EAAE,EAAmD,MAAM,OAAO,CAAC;AAS5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"navigation-catalog.d.ts","sourceRoot":"","sources":["../../../../src/components/titan-layout/navigation-catalog/navigation-catalog.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,EAAE,EAAmD,MAAM,OAAO,CAAC;AAS5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AA2LtD,MAAM,WAAW,sBAAuB,SAAQ,sBAAsB;IAClE,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,sBAAsB,CAyMxD,CAAC"}
|
|
@@ -45,6 +45,7 @@ const PinToggle = ({ pinned, label, onToggle })=>{
|
|
|
45
45
|
className: classNames(styles.star, pinned && styles.starActive),
|
|
46
46
|
"aria-label": label,
|
|
47
47
|
"aria-pressed": pinned,
|
|
48
|
+
"data-pendo": "global-nav-favorite-star",
|
|
48
49
|
onClick: toggle
|
|
49
50
|
})
|
|
50
51
|
}),
|
|
@@ -52,20 +53,21 @@ const PinToggle = ({ pinned, label, onToggle })=>{
|
|
|
52
53
|
style: {
|
|
53
54
|
pointerEvents: 'none'
|
|
54
55
|
},
|
|
55
|
-
children: pinned
|
|
56
|
+
children: pinned ? 'Remove from Favorites' : 'Add to Favorites'
|
|
56
57
|
})
|
|
57
58
|
]
|
|
58
59
|
});
|
|
59
60
|
};
|
|
60
|
-
const CatalogRows = ({ item, scopeIds, isPinned, isLocked,
|
|
61
|
+
const CatalogRows = ({ item, scopeIds, isPinned, isLocked, onToggleModule, onTogglePage, onNavigate })=>{
|
|
61
62
|
const { NavigationComponent } = useTitanLayoutContext();
|
|
62
63
|
const submenuGroups = getCatalogSubmenuGroups(item);
|
|
63
64
|
/*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*/ const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
65
|
+
* A pinned module id means the whole module, every page included. Pinned page ids without
|
|
66
|
+
* their module mean the module is on the rail carrying only those pages.
|
|
67
|
+
*/ const isWholeModule = isPinned(item.id);
|
|
68
|
+
const isOnRail = scopeIds.some(isPinned);
|
|
69
|
+
const pageIds = scopeIds.filter((id)=>id !== item.id && !isLocked(id));
|
|
70
|
+
const isFullyPinned = isWholeModule || pageIds.length > 0 && pageIds.every(isPinned);
|
|
69
71
|
const tag = getCounterTag(item.counter, item.tag);
|
|
70
72
|
return /*#__PURE__*/ _jsxs("div", {
|
|
71
73
|
className: styles.moduleGroup,
|
|
@@ -97,9 +99,9 @@ const CatalogRows = ({ item, scopeIds, isPinned, isLocked, onTogglePin, onToggle
|
|
|
97
99
|
]
|
|
98
100
|
}),
|
|
99
101
|
isLocked(item.id) ? /*#__PURE__*/ _jsx(LockedPin, {}) : /*#__PURE__*/ _jsx(PinToggle, {
|
|
100
|
-
pinned:
|
|
101
|
-
label:
|
|
102
|
-
onToggle: ()=>
|
|
102
|
+
pinned: isFullyPinned ? true : isOnRail ? 'mixed' : false,
|
|
103
|
+
label: isOnRail ? `Unpin ${item.title} from the navigation` : `Pin ${item.title} to the navigation`,
|
|
104
|
+
onToggle: ()=>onToggleModule(item.id)
|
|
103
105
|
})
|
|
104
106
|
]
|
|
105
107
|
}),
|
|
@@ -119,7 +121,7 @@ const CatalogRows = ({ item, scopeIds, isPinned, isLocked, onTogglePin, onToggle
|
|
|
119
121
|
* screen reader running through the links or a voice command naming one, would
|
|
120
122
|
* otherwise be given the same name several times.
|
|
121
123
|
*/ const qualifiedTitle = `${item.title}: ${child.title}`;
|
|
122
|
-
const childPinned = isPinned(child.id);
|
|
124
|
+
const childPinned = isWholeModule || isPinned(child.id);
|
|
123
125
|
const childTag = getCounterTag(child.counter, child.tag);
|
|
124
126
|
return /*#__PURE__*/ _jsxs("div", {
|
|
125
127
|
className: styles.row,
|
|
@@ -144,7 +146,7 @@ const CatalogRows = ({ item, scopeIds, isPinned, isLocked, onTogglePin, onToggle
|
|
|
144
146
|
isLocked(child.id) ? /*#__PURE__*/ _jsx(LockedPin, {}) : /*#__PURE__*/ _jsx(PinToggle, {
|
|
145
147
|
pinned: childPinned,
|
|
146
148
|
label: childPinned ? `Unpin ${qualifiedTitle} from the navigation` : `Pin ${qualifiedTitle} to the navigation`,
|
|
147
|
-
onToggle: ()=>
|
|
149
|
+
onToggle: ()=>onTogglePage(child.id, item.id)
|
|
148
150
|
})
|
|
149
151
|
]
|
|
150
152
|
}, child.id);
|
|
@@ -194,66 +196,55 @@ const CatalogRows = ({ item, scopeIds, isPinned, isLocked, onTogglePin, onToggle
|
|
|
194
196
|
]);
|
|
195
197
|
const isPinned = (id)=>pinnedSet.has(id);
|
|
196
198
|
const isLocked = (id)=>lockedSet.has(id);
|
|
197
|
-
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
* the module along when it is not already there, and unfavouriting the last one takes the
|
|
202
|
-
* module with it. Leaving the module behind on its own would not read as an emptier
|
|
203
|
-
* navigation — it would put the module back carrying every page it has.
|
|
199
|
+
/*
|
|
200
|
+
* The saved list follows the host's model: a module id pins the whole module, and page ids
|
|
201
|
+
* without their module pin the module carrying only those pages. A module id is therefore
|
|
202
|
+
* never saved next to its own page ids — the host would read that as the whole module.
|
|
204
203
|
*
|
|
205
204
|
* Locked ids never reach a proposal. The controls that would move them are not rendered, so
|
|
206
205
|
* that part is belt and braces — but the host's list is the thing that gets persisted, and a
|
|
207
206
|
* locked destination has no business appearing in or disappearing from it.
|
|
208
|
-
*/ const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
/** The module, when it is a separate row that this page can legitimately move. */ const owner = ownerId !== id && ownerId && !lockedSet.has(ownerId) ? ownerId : undefined;
|
|
213
|
-
if (pinnedSet.has(id)) {
|
|
214
|
-
const removing = new Set([
|
|
215
|
-
id
|
|
216
|
-
]);
|
|
217
|
-
if (owner) {
|
|
218
|
-
var _scopeById_get;
|
|
219
|
-
const siblings = ((_scopeById_get = scopeById.get(owner)) !== null && _scopeById_get !== void 0 ? _scopeById_get : []).filter((scoped)=>scoped !== owner && scoped !== id);
|
|
220
|
-
if (!siblings.some((sibling)=>pinnedSet.has(sibling))) {
|
|
221
|
-
removing.add(owner);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
onPinnedIdsChange(pinnedIds.filter((pinned)=>!removing.has(pinned)));
|
|
207
|
+
*/ /** Takes a module off the rail with all of its pages, or puts the whole module on it. */ const toggleModule = (moduleId)=>{
|
|
208
|
+
var _scopeById_get;
|
|
209
|
+
if (lockedSet.has(moduleId)) {
|
|
225
210
|
return;
|
|
226
211
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
...pinnedIds,
|
|
236
|
-
...additions
|
|
212
|
+
const scope = ((_scopeById_get = scopeById.get(moduleId)) !== null && _scopeById_get !== void 0 ? _scopeById_get : [
|
|
213
|
+
moduleId
|
|
214
|
+
]).filter((id)=>!lockedSet.has(id));
|
|
215
|
+
const scopeSet = new Set(scope);
|
|
216
|
+
const withoutModule = pinnedIds.filter((pinned)=>!scopeSet.has(pinned));
|
|
217
|
+
onPinnedIdsChange(scope.some((id)=>pinnedSet.has(id)) ? withoutModule : [
|
|
218
|
+
...withoutModule,
|
|
219
|
+
moduleId
|
|
237
220
|
]);
|
|
238
221
|
};
|
|
239
|
-
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
onPinnedIdsChange(pinnedIds.filter((pinned)=>!removing.has(pinned)));
|
|
222
|
+
/**
|
|
223
|
+
* Favourites or unfavourites a single page.
|
|
224
|
+
*
|
|
225
|
+
* Unstarring a page of a wholly favourited module narrows the module to the rest of its pages,
|
|
226
|
+
* in the module's place; unstarring its last page takes the module off the rail.
|
|
227
|
+
*/ const togglePage = (id, moduleId)=>{
|
|
228
|
+
var _scopeById_get;
|
|
229
|
+
if (lockedSet.has(id)) {
|
|
248
230
|
return;
|
|
249
231
|
}
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
232
|
+
const scope = (_scopeById_get = scopeById.get(moduleId)) !== null && _scopeById_get !== void 0 ? _scopeById_get : [
|
|
233
|
+
moduleId
|
|
234
|
+
];
|
|
235
|
+
const pages = scope.filter((scoped)=>scoped !== moduleId && !lockedSet.has(scoped));
|
|
236
|
+
if (pinnedSet.has(moduleId) && !lockedSet.has(moduleId)) {
|
|
237
|
+
const scopeSet = new Set(scope);
|
|
238
|
+
const rest = pages.filter((page)=>page !== id);
|
|
239
|
+
onPinnedIdsChange(pinnedIds.flatMap((pinned)=>pinned === moduleId ? rest : scopeSet.has(pinned) ? [] : [
|
|
240
|
+
pinned
|
|
241
|
+
]));
|
|
242
|
+
return;
|
|
256
243
|
}
|
|
244
|
+
onPinnedIdsChange(pinnedSet.has(id) ? pinnedIds.filter((pinned)=>pinned !== id) : [
|
|
245
|
+
...pinnedIds,
|
|
246
|
+
id
|
|
247
|
+
]);
|
|
257
248
|
};
|
|
258
249
|
const close = ()=>{
|
|
259
250
|
setQuery('');
|
|
@@ -316,8 +307,8 @@ const CatalogRows = ({ item, scopeIds, isPinned, isLocked, onTogglePin, onToggle
|
|
|
316
307
|
],
|
|
317
308
|
isPinned: isPinned,
|
|
318
309
|
isLocked: isLocked,
|
|
319
|
-
|
|
320
|
-
|
|
310
|
+
onToggleModule: toggleModule,
|
|
311
|
+
onTogglePage: togglePage,
|
|
321
312
|
onNavigate: close
|
|
322
313
|
}, item.id);
|
|
323
314
|
})
|