@wallarm-org/design-system 0.50.2 → 0.51.0-rc-feature-AS-1026.2
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/AppShell/story-content/_storyConfigRenderer.d.ts +1 -1
- package/dist/components/AppShell/story-content/_storyConfigRenderer.js +29 -15
- package/dist/components/AppShell/story-content/_storyLib.js +1 -1
- package/dist/components/AppShell/story-content/_storyNavConfigs.d.ts +1 -1
- package/dist/components/AppShell/story-content/_storyNavConfigs.js +0 -5
- package/dist/components/NavPanel/NavPanelHeader.js +1 -1
- package/dist/components/Page/PageContent.js +1 -1
- package/dist/components/Page/PageHeader.js +1 -1
- package/dist/components/{RemoteShell → ProductNav}/HeaderActions.d.ts +1 -1
- package/dist/components/{RemoteShell → ProductNav}/NavItemsList.d.ts +1 -1
- package/dist/components/ProductNav/ProductNav.d.ts +13 -0
- package/dist/components/ProductNav/ProductNav.js +109 -0
- package/dist/components/ProductNav/ProductNavBreadcrumbs.d.ts +2 -0
- package/dist/components/ProductNav/ProductNavBreadcrumbs.js +38 -0
- package/dist/components/{RemoteShell/model/RemoteShellContext.d.ts → ProductNav/ProductNavContext.d.ts} +3 -3
- package/dist/components/ProductNav/ProductNavContext.js +9 -0
- package/dist/components/ProductNav/ProductNavPanel.d.ts +6 -0
- package/dist/components/ProductNav/ProductNavPanel.js +107 -0
- package/dist/components/ProductNav/index.d.ts +11 -0
- package/dist/components/ProductNav/index.js +10 -0
- package/dist/components/{RemoteShell/model → ProductNav}/matchNav.js +1 -1
- package/dist/components/{RemoteShell/model → ProductNav}/types.d.ts +3 -2
- package/dist/components/ProductNav/useProductNav.d.ts +16 -0
- package/dist/components/ProductNav/useProductNav.js +19 -0
- package/dist/components/RemoteShell/RemoteShell.d.ts +0 -7
- package/dist/components/RemoteShell/RemoteShell.js +9 -109
- package/dist/components/RemoteShell/RemoteShellBreadcrumb.js +4 -39
- package/dist/components/RemoteShell/RemoteShellContent.d.ts +0 -1
- package/dist/components/RemoteShell/RemoteShellContent.js +3 -7
- package/dist/components/RemoteShell/RemoteShellPanel.d.ts +2 -4
- package/dist/components/RemoteShell/RemoteShellPanel.js +3 -76
- package/dist/components/RemoteShell/index.d.ts +0 -2
- package/dist/components/RemoteShell/index.js +1 -2
- package/dist/components/Table/TableBody/TableBodyVirtualizedContainer.js +5 -2
- package/dist/components/Table/TableBody/TableBodyVirtualizedWindow.js +5 -2
- package/dist/components/Table/TableBody/useResetVirtualizerOnDataChange.d.ts +3 -3
- package/dist/components/Table/TableBody/useResetVirtualizerOnDataChange.js +8 -6
- package/dist/components/Table/TableContext/TableProvider.js +7 -1
- package/dist/components/Table/TableContext/types.d.ts +3 -0
- package/dist/components/Table/TableInner/TableInnerContainer.js +9 -4
- package/dist/components/Table/TableInner/TableInnerWindow.js +9 -4
- package/dist/components/Table/hooks/index.d.ts +1 -1
- package/dist/components/Table/hooks/index.js +2 -2
- package/dist/components/Table/hooks/infiniteScroll/index.d.ts +1 -0
- package/dist/components/Table/hooks/infiniteScroll/index.js +2 -0
- package/dist/components/Table/hooks/infiniteScroll/useInfiniteScroll.d.ts +18 -0
- package/dist/components/Table/hooks/infiniteScroll/useInfiniteScroll.js +35 -0
- package/dist/components/Table/hooks/infiniteScroll/useInitialAnchor.d.ts +16 -0
- package/dist/components/Table/hooks/infiniteScroll/useInitialAnchor.js +28 -0
- package/dist/components/Table/hooks/infiniteScroll/usePrependScrollAnchor.d.ts +22 -0
- package/dist/components/Table/hooks/infiniteScroll/usePrependScrollAnchor.js +39 -0
- package/dist/components/Table/hooks/infiniteScroll/useScrollEdge.d.ts +20 -0
- package/dist/components/Table/hooks/{useEndReached.js → infiniteScroll/useScrollEdge.js} +15 -11
- package/dist/components/Table/lib/constants.d.ts +3 -0
- package/dist/components/Table/lib/constants.js +3 -1
- package/dist/components/Table/lib/detectDataChange.d.ts +4 -0
- package/dist/components/Table/lib/detectDataChange.js +7 -0
- package/dist/components/Table/lib/getRowKey.d.ts +4 -0
- package/dist/components/Table/lib/getRowKey.js +2 -0
- package/dist/components/Table/lib/index.d.ts +3 -1
- package/dist/components/Table/lib/index.js +4 -2
- package/dist/components/Table/mocks.d.ts +13 -0
- package/dist/components/Table/mocks.js +59 -1
- package/dist/components/Table/types.d.ts +11 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -2
- package/dist/metadata/components.json +312 -34
- package/package.json +1 -1
- package/dist/components/RemoteShell/NavPanelContent.d.ts +0 -6
- package/dist/components/RemoteShell/NavPanelContent.js +0 -65
- package/dist/components/RemoteShell/model/RemoteShellContext.js +0 -9
- package/dist/components/RemoteShell/model/index.d.ts +0 -5
- package/dist/components/RemoteShell/model/index.js +0 -5
- package/dist/components/Table/hooks/useEndReached.d.ts +0 -19
- /package/dist/components/{RemoteShell → ProductNav}/HeaderActions.js +0 -0
- /package/dist/components/{RemoteShell → ProductNav}/NavItemsList.js +0 -0
- /package/dist/components/{RemoteShell/model → ProductNav}/matchNav.d.ts +0 -0
- /package/dist/components/{RemoteShell/model → ProductNav}/navUtils.d.ts +0 -0
- /package/dist/components/{RemoteShell/model → ProductNav}/navUtils.js +0 -0
- /package/dist/components/{RemoteShell/model → ProductNav}/types.js +0 -0
- /package/dist/components/{RemoteShell/model → ProductNav}/useDrillTransition.d.ts +0 -0
- /package/dist/components/{RemoteShell/model → ProductNav}/useDrillTransition.js +0 -0
- /package/dist/components/{RemoteShell/model → ProductNav}/useLocationPathname.d.ts +0 -0
- /package/dist/components/{RemoteShell/model → ProductNav}/useLocationPathname.js +0 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
+
import { NavPanel, NavPanelHeader } from "../../NavPanel/index.js";
|
|
3
4
|
import { Page, PageContent, PageHeader, PageTitle } from "../../Page/index.js";
|
|
4
|
-
import {
|
|
5
|
+
import { NavPanelSkeleton, ProductNav, ProductNavBreadcrumbs, ProductNavPanel, useProductNavContext } from "../../ProductNav/index.js";
|
|
6
|
+
import { RemoteShell, RemoteShellBreadcrumb, RemoteShellContent, RemoteShellPanel } from "../../RemoteShell/index.js";
|
|
5
7
|
import { HomeContent } from "./_storyHomeContent.js";
|
|
6
8
|
import { PRODUCT_CONFIGS } from "./_storyLib.js";
|
|
7
9
|
const RemotePageContent = ()=>{
|
|
8
|
-
const { breadcrumbSegments } =
|
|
10
|
+
const { breadcrumbSegments } = useProductNavContext();
|
|
9
11
|
const lastSegment = breadcrumbSegments[breadcrumbSegments.length - 1];
|
|
10
12
|
const pageTitle = lastSegment?.label ?? '';
|
|
11
13
|
const fullPath = breadcrumbSegments.map((s)=>s.label).join(' / ');
|
|
@@ -42,24 +44,36 @@ const ConfigRemote = ({ config, basePath })=>{
|
|
|
42
44
|
}, [
|
|
43
45
|
config.productLabel
|
|
44
46
|
]);
|
|
45
|
-
return /*#__PURE__*/
|
|
47
|
+
if (loading) return /*#__PURE__*/ jsxs(RemoteShell, {
|
|
48
|
+
children: [
|
|
49
|
+
/*#__PURE__*/ jsx(RemoteShellPanel, {
|
|
50
|
+
children: /*#__PURE__*/ jsxs(NavPanel, {
|
|
51
|
+
children: [
|
|
52
|
+
/*#__PURE__*/ jsx(NavPanelHeader, {
|
|
53
|
+
children: config.productLabel
|
|
54
|
+
}),
|
|
55
|
+
/*#__PURE__*/ jsx(NavPanelSkeleton, {
|
|
56
|
+
count: 6
|
|
57
|
+
})
|
|
58
|
+
]
|
|
59
|
+
})
|
|
60
|
+
}),
|
|
61
|
+
/*#__PURE__*/ jsx(RemoteShellContent, {})
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
return /*#__PURE__*/ jsx(ProductNav, {
|
|
46
65
|
config: config,
|
|
47
66
|
basePath: basePath,
|
|
48
|
-
children:
|
|
67
|
+
children: /*#__PURE__*/ jsxs(RemoteShell, {
|
|
49
68
|
children: [
|
|
50
69
|
/*#__PURE__*/ jsx(RemoteShellPanel, {
|
|
51
|
-
|
|
70
|
+
children: /*#__PURE__*/ jsx(ProductNavPanel, {
|
|
71
|
+
resizable: true
|
|
72
|
+
})
|
|
52
73
|
}),
|
|
53
|
-
/*#__PURE__*/ jsx(
|
|
54
|
-
|
|
55
|
-
})
|
|
56
|
-
]
|
|
57
|
-
}) : /*#__PURE__*/ jsxs(Fragment, {
|
|
58
|
-
children: [
|
|
59
|
-
/*#__PURE__*/ jsx(RemoteShellPanel, {
|
|
60
|
-
resizable: true
|
|
74
|
+
/*#__PURE__*/ jsx(RemoteShellBreadcrumb, {
|
|
75
|
+
children: /*#__PURE__*/ jsx(ProductNavBreadcrumbs, {})
|
|
61
76
|
}),
|
|
62
|
-
/*#__PURE__*/ jsx(RemoteShellBreadcrumb, {}),
|
|
63
77
|
/*#__PURE__*/ jsx(RemoteShellContent, {
|
|
64
78
|
children: /*#__PURE__*/ jsx(RemotePageContent, {})
|
|
65
79
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findFirstLinkPath, pushPathname } from "../../
|
|
1
|
+
import { findFirstLinkPath, pushPathname } from "../../ProductNav/index.js";
|
|
2
2
|
import { aiHypervisorNavConfig, edgeNavConfig, infraDiscoveryNavConfig, securityTestingNavConfig, settingsNavConfig } from "./_storyNavConfigs.js";
|
|
3
3
|
const KNOWN_PRODUCTS = [
|
|
4
4
|
'home',
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { CircleDashed, Filter, Plus } from "../../../icons/index.js";
|
|
2
2
|
const edgeNavConfig = {
|
|
3
3
|
productLabel: 'Edge',
|
|
4
|
-
productPath: '/edge',
|
|
5
4
|
headerActions: [
|
|
6
5
|
{
|
|
7
6
|
icon: Filter,
|
|
@@ -341,7 +340,6 @@ const edgeNavConfig = {
|
|
|
341
340
|
};
|
|
342
341
|
const aiHypervisorNavConfig = {
|
|
343
342
|
productLabel: 'AI Hypervisor',
|
|
344
|
-
productPath: '/ai-hypervisor',
|
|
345
343
|
items: [
|
|
346
344
|
{
|
|
347
345
|
type: 'link',
|
|
@@ -456,7 +454,6 @@ const aiHypervisorNavConfig = {
|
|
|
456
454
|
};
|
|
457
455
|
const infraDiscoveryNavConfig = {
|
|
458
456
|
productLabel: 'Infra Discovery',
|
|
459
|
-
productPath: '/infra-discovery',
|
|
460
457
|
headerActions: [
|
|
461
458
|
{
|
|
462
459
|
icon: Filter,
|
|
@@ -557,7 +554,6 @@ const infraDiscoveryNavConfig = {
|
|
|
557
554
|
};
|
|
558
555
|
const securityTestingNavConfig = {
|
|
559
556
|
productLabel: 'Security Testing',
|
|
560
|
-
productPath: '/security-testing',
|
|
561
557
|
items: [
|
|
562
558
|
{
|
|
563
559
|
type: 'link',
|
|
@@ -631,7 +627,6 @@ const securityTestingNavConfig = {
|
|
|
631
627
|
};
|
|
632
628
|
const settingsNavConfig = {
|
|
633
629
|
productLabel: 'Settings',
|
|
634
|
-
productPath: '/settings',
|
|
635
630
|
items: [
|
|
636
631
|
{
|
|
637
632
|
type: 'link',
|
|
@@ -9,7 +9,7 @@ const NavPanelHeader = ({ ref, className, children, ...props })=>{
|
|
|
9
9
|
ref: ref,
|
|
10
10
|
"data-slot": "nav-panel-header",
|
|
11
11
|
"data-testid": testId,
|
|
12
|
-
className: cn('sticky top-0 z-10 flex shrink-0 items-center p-4
|
|
12
|
+
className: cn('sticky top-0 z-10 flex shrink-0 items-center p-4', className),
|
|
13
13
|
children: /*#__PURE__*/ jsx(Text, {
|
|
14
14
|
size: "sm",
|
|
15
15
|
weight: "medium",
|
|
@@ -8,7 +8,7 @@ const PageContent = ({ ref, children, className, ...props })=>{
|
|
|
8
8
|
ref: ref,
|
|
9
9
|
"data-testid": testId,
|
|
10
10
|
"data-slot": "page-content",
|
|
11
|
-
className: cn('flex-1 min-h-0 overflow-auto px-16', className),
|
|
11
|
+
className: cn('flex-1 min-h-0 overflow-auto px-24 py-16', className),
|
|
12
12
|
children: children
|
|
13
13
|
});
|
|
14
14
|
};
|
|
@@ -8,7 +8,7 @@ const PageHeader = ({ ref, sticky, children, className, ...props })=>{
|
|
|
8
8
|
ref: ref,
|
|
9
9
|
"data-testid": testId,
|
|
10
10
|
"data-slot": "page-header",
|
|
11
|
-
className: cn('flex items-center px-
|
|
11
|
+
className: cn('flex items-center px-24 py-16 gap-12', sticky && 'sticky top-0 z-10 bg-bg-primary', className),
|
|
12
12
|
children: children
|
|
13
13
|
});
|
|
14
14
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type FC, type ReactNode } from 'react';
|
|
2
|
+
import type { NavConfig } from './types';
|
|
3
|
+
export interface ProductNavProps {
|
|
4
|
+
config: NavConfig;
|
|
5
|
+
/** URL prefix stripped before matching and prepended when navigating.
|
|
6
|
+
* Example: `"/edge"` turns URL `/edge/overview` into effective pathname `/overview`. */
|
|
7
|
+
basePath?: string;
|
|
8
|
+
/** Custom navigation handler for router integration (React Router, Next.js, etc.).
|
|
9
|
+
* Default: uses history.pushState to update the URL directly. */
|
|
10
|
+
onNavigate?: (pathname: string) => void;
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
export declare const ProductNav: FC<ProductNavProps>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { matchNav } from "./matchNav.js";
|
|
4
|
+
import { findFirstLinkPath } from "./navUtils.js";
|
|
5
|
+
import { ProductNavContextProvider } from "./ProductNavContext.js";
|
|
6
|
+
import { pushPathname, useLocationPathname } from "./useLocationPathname.js";
|
|
7
|
+
const ProductNav = ({ config, basePath, onNavigate, children })=>{
|
|
8
|
+
const fullPathname = useLocationPathname();
|
|
9
|
+
const pathname = basePath && fullPathname.startsWith(basePath) ? fullPathname.slice(basePath.length) || '/' : fullPathname;
|
|
10
|
+
const setPathname = useCallback((next)=>{
|
|
11
|
+
const fullPath = basePath ? `${basePath}${next}` : next;
|
|
12
|
+
if (onNavigate) onNavigate(fullPath);
|
|
13
|
+
else pushPathname(fullPath);
|
|
14
|
+
}, [
|
|
15
|
+
basePath,
|
|
16
|
+
onNavigate
|
|
17
|
+
]);
|
|
18
|
+
const { navStack, breadcrumbSegments, activeItemId } = useMemo(()=>matchNav(pathname, config), [
|
|
19
|
+
pathname,
|
|
20
|
+
config
|
|
21
|
+
]);
|
|
22
|
+
const urlDrillLevel = navStack.length - 1;
|
|
23
|
+
const [visualDrillLevel, setVisualDrillLevel] = useState(null);
|
|
24
|
+
useEffect(()=>{
|
|
25
|
+
setVisualDrillLevel(null);
|
|
26
|
+
}, [
|
|
27
|
+
pathname
|
|
28
|
+
]);
|
|
29
|
+
const effectiveDrillLevel = visualDrillLevel ?? urlDrillLevel;
|
|
30
|
+
const effectiveActiveItemId = effectiveDrillLevel < urlDrillLevel ? navStack[effectiveDrillLevel]?.activeItemId ?? activeItemId : activeItemId;
|
|
31
|
+
const navigate = useCallback((path)=>{
|
|
32
|
+
setVisualDrillLevel(null);
|
|
33
|
+
const segments = pathname.replace(/^\/+|\/+$/g, '').split('/').filter(Boolean);
|
|
34
|
+
const prefixSegments = segments.slice(0, 2 * effectiveDrillLevel);
|
|
35
|
+
setPathname(`/${[
|
|
36
|
+
...prefixSegments,
|
|
37
|
+
path
|
|
38
|
+
].join('/')}`);
|
|
39
|
+
}, [
|
|
40
|
+
effectiveDrillLevel,
|
|
41
|
+
pathname,
|
|
42
|
+
setPathname
|
|
43
|
+
]);
|
|
44
|
+
const drillInto = useCallback((drill)=>{
|
|
45
|
+
setVisualDrillLevel(null);
|
|
46
|
+
const segments = pathname.replace(/^\/+|\/+$/g, '').split('/').filter(Boolean);
|
|
47
|
+
const prefixSegments = segments.slice(0, 2 * effectiveDrillLevel);
|
|
48
|
+
const defaultEntity = drill.entities?.[0]?.id ?? 'default';
|
|
49
|
+
const firstChildPath = findFirstLinkPath(drill.children) ?? '';
|
|
50
|
+
setPathname(`/${[
|
|
51
|
+
...prefixSegments,
|
|
52
|
+
drill.path,
|
|
53
|
+
defaultEntity,
|
|
54
|
+
firstChildPath
|
|
55
|
+
].join('/')}`);
|
|
56
|
+
}, [
|
|
57
|
+
effectiveDrillLevel,
|
|
58
|
+
pathname,
|
|
59
|
+
setPathname
|
|
60
|
+
]);
|
|
61
|
+
const goBack = useCallback(()=>{
|
|
62
|
+
setVisualDrillLevel((prev)=>{
|
|
63
|
+
const current = prev ?? urlDrillLevel;
|
|
64
|
+
return Math.max(current - 1, 0);
|
|
65
|
+
});
|
|
66
|
+
}, [
|
|
67
|
+
urlDrillLevel
|
|
68
|
+
]);
|
|
69
|
+
const navigateTo = useCallback((href)=>{
|
|
70
|
+
setVisualDrillLevel(null);
|
|
71
|
+
if ('/' === href) {
|
|
72
|
+
const firstPath = findFirstLinkPath(config.items) ?? '';
|
|
73
|
+
setPathname(`/${firstPath}`);
|
|
74
|
+
} else setPathname(href);
|
|
75
|
+
}, [
|
|
76
|
+
config.items,
|
|
77
|
+
setPathname
|
|
78
|
+
]);
|
|
79
|
+
const navCtxValue = useMemo(()=>({
|
|
80
|
+
config,
|
|
81
|
+
pathname,
|
|
82
|
+
navStack,
|
|
83
|
+
breadcrumbSegments,
|
|
84
|
+
activeItemId,
|
|
85
|
+
drillLevel: effectiveDrillLevel,
|
|
86
|
+
effectiveActiveItemId,
|
|
87
|
+
navigate,
|
|
88
|
+
drillInto,
|
|
89
|
+
goBack,
|
|
90
|
+
navigateTo
|
|
91
|
+
}), [
|
|
92
|
+
config,
|
|
93
|
+
pathname,
|
|
94
|
+
navStack,
|
|
95
|
+
breadcrumbSegments,
|
|
96
|
+
activeItemId,
|
|
97
|
+
effectiveDrillLevel,
|
|
98
|
+
effectiveActiveItemId,
|
|
99
|
+
navigate,
|
|
100
|
+
drillInto,
|
|
101
|
+
goBack,
|
|
102
|
+
navigateTo
|
|
103
|
+
]);
|
|
104
|
+
return /*#__PURE__*/ jsx(ProductNavContextProvider, {
|
|
105
|
+
value: navCtxValue,
|
|
106
|
+
children: children
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
export { ProductNav };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Breadcrumbs, BreadcrumbsItem, BreadcrumbsScopeSwitcher } from "../Breadcrumbs/index.js";
|
|
3
|
+
import { useProductNavContext } from "./ProductNavContext.js";
|
|
4
|
+
const ProductNavBreadcrumbs = ()=>{
|
|
5
|
+
const { breadcrumbSegments, navigateTo } = useProductNavContext();
|
|
6
|
+
return /*#__PURE__*/ jsx(Breadcrumbs, {
|
|
7
|
+
"data-slot": "nav-breadcrumbs",
|
|
8
|
+
children: breadcrumbSegments.map((segment, i)=>{
|
|
9
|
+
const isLast = i === breadcrumbSegments.length - 1;
|
|
10
|
+
if ('scope-switcher' === segment.type) {
|
|
11
|
+
if (segment.scopeItems?.length && segment.paramValue) return /*#__PURE__*/ jsx(BreadcrumbsScopeSwitcher, {
|
|
12
|
+
value: segment.paramValue,
|
|
13
|
+
items: segment.scopeItems,
|
|
14
|
+
onSelect: (item)=>navigateTo?.(item.href),
|
|
15
|
+
children: segment.label
|
|
16
|
+
}, i);
|
|
17
|
+
return /*#__PURE__*/ jsx(BreadcrumbsItem, {
|
|
18
|
+
children: segment.label
|
|
19
|
+
}, i);
|
|
20
|
+
}
|
|
21
|
+
if ('link' === segment.type && !isLast) return /*#__PURE__*/ jsx(BreadcrumbsItem, {
|
|
22
|
+
href: segment.href,
|
|
23
|
+
onClick: (e)=>{
|
|
24
|
+
if (navigateTo && segment.href) {
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
navigateTo(segment.href);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
children: segment.label
|
|
30
|
+
}, i);
|
|
31
|
+
return /*#__PURE__*/ jsx(BreadcrumbsItem, {
|
|
32
|
+
children: segment.label
|
|
33
|
+
}, i);
|
|
34
|
+
})
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
ProductNavBreadcrumbs.displayName = 'ProductNavBreadcrumbs';
|
|
38
|
+
export { ProductNavBreadcrumbs };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BreadcrumbSegment, NavConfig, NavConfigDrill, NavStackEntry } from './types';
|
|
2
|
-
export interface
|
|
2
|
+
export interface ProductNavContextValue {
|
|
3
3
|
config: NavConfig;
|
|
4
4
|
pathname: string;
|
|
5
5
|
navStack: NavStackEntry[];
|
|
@@ -18,5 +18,5 @@ export interface RemoteShellContextValue {
|
|
|
18
18
|
/** Navigate to an absolute href (used by breadcrumbs) */
|
|
19
19
|
navigateTo: (href: string) => void;
|
|
20
20
|
}
|
|
21
|
-
export declare const
|
|
22
|
-
export declare function
|
|
21
|
+
export declare const ProductNavContextProvider: import("react").Provider<ProductNavContextValue | null>;
|
|
22
|
+
export declare function useProductNavContext(): ProductNavContextValue;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
const ProductNavCtx = createContext(null);
|
|
3
|
+
const ProductNavContextProvider = ProductNavCtx.Provider;
|
|
4
|
+
function useProductNavContext() {
|
|
5
|
+
const ctx = useContext(ProductNavCtx);
|
|
6
|
+
if (!ctx) throw new Error('useProductNavContext must be used within a ProductNav provider');
|
|
7
|
+
return ctx;
|
|
8
|
+
}
|
|
9
|
+
export { ProductNavContextProvider, useProductNavContext };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { NavPanel, NavPanelBack, NavPanelDivider, NavPanelHeader } from "../NavPanel/index.js";
|
|
3
|
+
import { Text } from "../Text/index.js";
|
|
4
|
+
import { HeaderActions } from "./HeaderActions.js";
|
|
5
|
+
import { NavItemsList } from "./NavItemsList.js";
|
|
6
|
+
import { findDrillNode } from "./navUtils.js";
|
|
7
|
+
import { useProductNavContext } from "./ProductNavContext.js";
|
|
8
|
+
import { DRILL_ANIMATION_DURATION, DRILL_ANIMATION_EASING, useDrillTransition } from "./useDrillTransition.js";
|
|
9
|
+
const ProductNavPanel = ({ resizable })=>{
|
|
10
|
+
const { config, navStack, effectiveActiveItemId, navigate, drillInto, goBack, drillLevel } = useProductNavContext();
|
|
11
|
+
const { transition, clearTransition } = useDrillTransition(drillLevel);
|
|
12
|
+
const hasHeaderActions = !!config.headerActions?.length;
|
|
13
|
+
const renderContent = (level)=>{
|
|
14
|
+
const entry = navStack[Math.min(level, navStack.length - 1)];
|
|
15
|
+
if (0 === level) return /*#__PURE__*/ jsxs(Fragment, {
|
|
16
|
+
children: [
|
|
17
|
+
hasHeaderActions ? /*#__PURE__*/ jsxs("div", {
|
|
18
|
+
className: "sticky top-0 z-10 flex shrink-0 items-center justify-between p-4",
|
|
19
|
+
children: [
|
|
20
|
+
/*#__PURE__*/ jsx(Text, {
|
|
21
|
+
size: "sm",
|
|
22
|
+
weight: "medium",
|
|
23
|
+
children: config.productLabel
|
|
24
|
+
}),
|
|
25
|
+
/*#__PURE__*/ jsx(HeaderActions, {
|
|
26
|
+
actions: config.headerActions
|
|
27
|
+
})
|
|
28
|
+
]
|
|
29
|
+
}) : /*#__PURE__*/ jsx(NavPanelHeader, {
|
|
30
|
+
children: config.productLabel
|
|
31
|
+
}),
|
|
32
|
+
/*#__PURE__*/ jsx(NavItemsList, {
|
|
33
|
+
items: config.items,
|
|
34
|
+
activeItemId: effectiveActiveItemId,
|
|
35
|
+
onNavigate: navigate,
|
|
36
|
+
onDrillClick: drillInto
|
|
37
|
+
})
|
|
38
|
+
]
|
|
39
|
+
});
|
|
40
|
+
const parentEntry = navStack[level - 1];
|
|
41
|
+
const drillNode = findDrillNode(parentEntry.items, parentEntry.activeItemId);
|
|
42
|
+
const backLabel = drillNode?.label ?? 'Back';
|
|
43
|
+
return /*#__PURE__*/ jsxs(Fragment, {
|
|
44
|
+
children: [
|
|
45
|
+
/*#__PURE__*/ jsxs("div", {
|
|
46
|
+
className: "sticky top-0 z-10 flex flex-col gap-2 bg-bg-surface-1",
|
|
47
|
+
children: [
|
|
48
|
+
/*#__PURE__*/ jsx(NavPanelHeader, {
|
|
49
|
+
children: entry.title
|
|
50
|
+
}),
|
|
51
|
+
/*#__PURE__*/ jsx(NavPanelBack, {
|
|
52
|
+
onClick: goBack,
|
|
53
|
+
children: backLabel
|
|
54
|
+
}),
|
|
55
|
+
/*#__PURE__*/ jsx(NavPanelDivider, {})
|
|
56
|
+
]
|
|
57
|
+
}),
|
|
58
|
+
/*#__PURE__*/ jsx(NavItemsList, {
|
|
59
|
+
items: entry.items,
|
|
60
|
+
activeItemId: effectiveActiveItemId,
|
|
61
|
+
onNavigate: navigate,
|
|
62
|
+
onDrillClick: drillInto
|
|
63
|
+
})
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
if (transition) {
|
|
68
|
+
const isForward = 'forward' === transition.direction;
|
|
69
|
+
const slideAnim = isForward ? 'ds-nav-drill-forward' : 'ds-nav-drill-backward';
|
|
70
|
+
const timing = `${DRILL_ANIMATION_DURATION} ${DRILL_ANIMATION_EASING} both`;
|
|
71
|
+
return /*#__PURE__*/ jsx(NavPanel, {
|
|
72
|
+
resizable: resizable,
|
|
73
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
74
|
+
className: "min-h-0 flex-1 overflow-hidden",
|
|
75
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
76
|
+
className: "flex motion-reduce:animate-none",
|
|
77
|
+
style: {
|
|
78
|
+
animation: `${slideAnim} ${timing}`
|
|
79
|
+
},
|
|
80
|
+
onAnimationEnd: clearTransition,
|
|
81
|
+
children: [
|
|
82
|
+
/*#__PURE__*/ jsx("div", {
|
|
83
|
+
className: "flex w-full shrink-0 flex-col gap-2 motion-reduce:animate-none",
|
|
84
|
+
style: {
|
|
85
|
+
animation: `${isForward ? 'ds-nav-blur-out' : 'ds-nav-blur-in'} ${timing}`
|
|
86
|
+
},
|
|
87
|
+
children: renderContent(isForward ? transition.fromLevel : drillLevel)
|
|
88
|
+
}),
|
|
89
|
+
/*#__PURE__*/ jsx("div", {
|
|
90
|
+
className: "flex w-full shrink-0 flex-col gap-2 motion-reduce:animate-none",
|
|
91
|
+
style: {
|
|
92
|
+
animation: `${isForward ? 'ds-nav-blur-in' : 'ds-nav-blur-out'} ${timing}`
|
|
93
|
+
},
|
|
94
|
+
children: renderContent(isForward ? drillLevel : transition.fromLevel)
|
|
95
|
+
})
|
|
96
|
+
]
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return /*#__PURE__*/ jsx(NavPanel, {
|
|
102
|
+
resizable: resizable,
|
|
103
|
+
children: renderContent(drillLevel)
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
ProductNavPanel.displayName = 'ProductNavPanel';
|
|
107
|
+
export { ProductNavPanel };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { NavPanelSkeleton, type NavPanelSkeletonProps } from '../NavPanel';
|
|
2
|
+
export { type MatchNavResult, matchNav } from './matchNav';
|
|
3
|
+
export { findDrillNode, findFirstLinkPath } from './navUtils';
|
|
4
|
+
export { ProductNav, type ProductNavProps } from './ProductNav';
|
|
5
|
+
export { ProductNavBreadcrumbs } from './ProductNavBreadcrumbs';
|
|
6
|
+
export type { ProductNavContextValue } from './ProductNavContext';
|
|
7
|
+
export { useProductNavContext } from './ProductNavContext';
|
|
8
|
+
export { ProductNavPanel } from './ProductNavPanel';
|
|
9
|
+
export type { BreadcrumbSegment, NavConfig, NavConfigDrill, NavConfigGroup, NavConfigHeaderAction, NavConfigLink, NavConfigNode, NavConfigSectionHeader, NavStackEntry, } from './types';
|
|
10
|
+
export { pushPathname, useLocationPathname } from './useLocationPathname';
|
|
11
|
+
export { type UseProductNavResult, useProductNav } from './useProductNav';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { NavPanelSkeleton } from "../NavPanel/index.js";
|
|
2
|
+
import { matchNav } from "./matchNav.js";
|
|
3
|
+
import { findDrillNode, findFirstLinkPath } from "./navUtils.js";
|
|
4
|
+
import { ProductNav } from "./ProductNav.js";
|
|
5
|
+
import { ProductNavBreadcrumbs } from "./ProductNavBreadcrumbs.js";
|
|
6
|
+
import { useProductNavContext } from "./ProductNavContext.js";
|
|
7
|
+
import { ProductNavPanel } from "./ProductNavPanel.js";
|
|
8
|
+
import { pushPathname, useLocationPathname } from "./useLocationPathname.js";
|
|
9
|
+
import { useProductNav } from "./useProductNav.js";
|
|
10
|
+
export { NavPanelSkeleton, ProductNav, ProductNavBreadcrumbs, ProductNavPanel, findDrillNode, findFirstLinkPath, matchNav, pushPathname, useLocationPathname, useProductNav, useProductNavContext };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentType } from 'react';
|
|
2
|
-
import type { SvgIconProps } from '
|
|
2
|
+
import type { SvgIconProps } from '../../icons/SvgIcon';
|
|
3
3
|
export interface NavConfigHeaderAction {
|
|
4
4
|
icon: ComponentType<SvgIconProps>;
|
|
5
5
|
label: string;
|
|
@@ -8,7 +8,6 @@ export interface NavConfigHeaderAction {
|
|
|
8
8
|
}
|
|
9
9
|
export interface NavConfig {
|
|
10
10
|
productLabel: string;
|
|
11
|
-
productPath: string;
|
|
12
11
|
items: NavConfigNode[];
|
|
13
12
|
headerActions?: NavConfigHeaderAction[];
|
|
14
13
|
}
|
|
@@ -36,6 +35,7 @@ export interface NavConfigDrill {
|
|
|
36
35
|
label: string;
|
|
37
36
|
description?: string;
|
|
38
37
|
}[];
|
|
38
|
+
/** Render a visual divider after this item */
|
|
39
39
|
dividerAfter?: boolean;
|
|
40
40
|
}
|
|
41
41
|
export interface NavConfigGroup {
|
|
@@ -45,6 +45,7 @@ export interface NavConfigGroup {
|
|
|
45
45
|
children: NavConfigNode[];
|
|
46
46
|
icon?: ComponentType<SvgIconProps>;
|
|
47
47
|
defaultExpanded?: boolean;
|
|
48
|
+
/** Render a visual divider after this item */
|
|
48
49
|
dividerAfter?: boolean;
|
|
49
50
|
}
|
|
50
51
|
export interface NavConfigSectionHeader {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Dispatch, type SetStateAction } from 'react';
|
|
2
|
+
import type { BreadcrumbSegment, NavStackEntry } from './types';
|
|
3
|
+
export interface UseProductNavResult {
|
|
4
|
+
navStack: NavStackEntry[];
|
|
5
|
+
peekDepth: number;
|
|
6
|
+
setPeekDepth: Dispatch<SetStateAction<number>>;
|
|
7
|
+
breadcrumbSegments: BreadcrumbSegment[];
|
|
8
|
+
activeItemId: string | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Hook providing computed nav state + peekDepth management.
|
|
12
|
+
*
|
|
13
|
+
* Reads computed state from ProductNav context.
|
|
14
|
+
* `peekDepth` is the only local mutable state and resets on pathname changes.
|
|
15
|
+
*/
|
|
16
|
+
export declare function useProductNav(): UseProductNavResult;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { useProductNavContext } from "./ProductNavContext.js";
|
|
3
|
+
function useProductNav() {
|
|
4
|
+
const { pathname, navStack, breadcrumbSegments, activeItemId } = useProductNavContext();
|
|
5
|
+
const [peekDepth, setPeekDepth] = useState(0);
|
|
6
|
+
useEffect(()=>{
|
|
7
|
+
setPeekDepth(0);
|
|
8
|
+
}, [
|
|
9
|
+
pathname
|
|
10
|
+
]);
|
|
11
|
+
return {
|
|
12
|
+
navStack,
|
|
13
|
+
peekDepth,
|
|
14
|
+
setPeekDepth,
|
|
15
|
+
breadcrumbSegments,
|
|
16
|
+
activeItemId
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export { useProductNav };
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import type { FC, HTMLAttributes, ReactNode, Ref } from 'react';
|
|
2
2
|
import { type TestableProps } from '../../utils/testId';
|
|
3
|
-
import type { NavConfig } from './model';
|
|
4
3
|
export interface RemoteShellProps extends HTMLAttributes<HTMLDivElement>, TestableProps {
|
|
5
4
|
ref?: Ref<HTMLDivElement>;
|
|
6
5
|
children?: ReactNode;
|
|
7
|
-
/** Navigation config used to build nav state for sub-components. */
|
|
8
|
-
config: NavConfig;
|
|
9
|
-
/** URL prefix stripped before matching and prepended when navigating (e.g. `"/edge"`). */
|
|
10
|
-
basePath?: string;
|
|
11
|
-
/** Custom navigation handler for router integration (React Router, Next.js, etc.). */
|
|
12
|
-
onNavigate?: (pathname: string) => void;
|
|
13
6
|
}
|
|
14
7
|
export declare const RemoteShell: FC<RemoteShellProps>;
|