@wallarm-org/design-system 0.50.1 → 0.50.2-rc-feature-shell.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.
Files changed (52) hide show
  1. package/dist/components/AppShell/story-content/_storyConfigRenderer.d.ts +1 -1
  2. package/dist/components/AppShell/story-content/_storyConfigRenderer.js +15 -29
  3. package/dist/components/AppShell/story-content/_storyLib.js +1 -1
  4. package/dist/components/AppShell/story-content/_storyNavConfigs.d.ts +1 -1
  5. package/dist/components/AppShell/story-content/_storyNavConfigs.js +5 -0
  6. package/dist/components/NavPanel/NavPanelHeader.js +1 -1
  7. package/dist/components/Page/PageContent.js +1 -1
  8. package/dist/components/Page/PageHeader.js +1 -1
  9. package/dist/components/{ProductNav → RemoteShell}/HeaderActions.d.ts +1 -1
  10. package/dist/components/{ProductNav → RemoteShell}/NavItemsList.d.ts +1 -1
  11. package/dist/components/RemoteShell/NavPanelContent.d.ts +6 -0
  12. package/dist/components/RemoteShell/NavPanelContent.js +65 -0
  13. package/dist/components/RemoteShell/RemoteShell.d.ts +7 -0
  14. package/dist/components/RemoteShell/RemoteShell.js +109 -9
  15. package/dist/components/RemoteShell/RemoteShellBreadcrumb.js +39 -4
  16. package/dist/components/RemoteShell/RemoteShellContent.d.ts +1 -0
  17. package/dist/components/RemoteShell/RemoteShellContent.js +7 -3
  18. package/dist/components/RemoteShell/RemoteShellPanel.d.ts +4 -2
  19. package/dist/components/RemoteShell/RemoteShellPanel.js +76 -3
  20. package/dist/components/RemoteShell/index.d.ts +2 -0
  21. package/dist/components/RemoteShell/index.js +2 -1
  22. package/dist/components/{ProductNav/ProductNavContext.d.ts → RemoteShell/model/RemoteShellContext.d.ts} +3 -3
  23. package/dist/components/RemoteShell/model/RemoteShellContext.js +9 -0
  24. package/dist/components/RemoteShell/model/index.d.ts +5 -0
  25. package/dist/components/RemoteShell/model/index.js +5 -0
  26. package/dist/components/{ProductNav → RemoteShell/model}/matchNav.js +1 -1
  27. package/dist/components/{ProductNav → RemoteShell/model}/types.d.ts +2 -3
  28. package/dist/index.d.ts +1 -2
  29. package/dist/index.js +2 -3
  30. package/dist/metadata/components.json +35 -303
  31. package/package.json +1 -1
  32. package/dist/components/ProductNav/ProductNav.d.ts +0 -13
  33. package/dist/components/ProductNav/ProductNav.js +0 -109
  34. package/dist/components/ProductNav/ProductNavBreadcrumbs.d.ts +0 -2
  35. package/dist/components/ProductNav/ProductNavBreadcrumbs.js +0 -38
  36. package/dist/components/ProductNav/ProductNavContext.js +0 -9
  37. package/dist/components/ProductNav/ProductNavPanel.d.ts +0 -6
  38. package/dist/components/ProductNav/ProductNavPanel.js +0 -107
  39. package/dist/components/ProductNav/index.d.ts +0 -11
  40. package/dist/components/ProductNav/index.js +0 -10
  41. package/dist/components/ProductNav/useProductNav.d.ts +0 -16
  42. package/dist/components/ProductNav/useProductNav.js +0 -19
  43. /package/dist/components/{ProductNav → RemoteShell}/HeaderActions.js +0 -0
  44. /package/dist/components/{ProductNav → RemoteShell}/NavItemsList.js +0 -0
  45. /package/dist/components/{ProductNav → RemoteShell/model}/matchNav.d.ts +0 -0
  46. /package/dist/components/{ProductNav → RemoteShell/model}/navUtils.d.ts +0 -0
  47. /package/dist/components/{ProductNav → RemoteShell/model}/navUtils.js +0 -0
  48. /package/dist/components/{ProductNav → RemoteShell/model}/types.js +0 -0
  49. /package/dist/components/{ProductNav → RemoteShell/model}/useDrillTransition.d.ts +0 -0
  50. /package/dist/components/{ProductNav → RemoteShell/model}/useDrillTransition.js +0 -0
  51. /package/dist/components/{ProductNav → RemoteShell/model}/useLocationPathname.d.ts +0 -0
  52. /package/dist/components/{ProductNav → RemoteShell/model}/useLocationPathname.js +0 -0
@@ -1,4 +1,4 @@
1
- import { type NavConfig } from '../../ProductNav';
1
+ import { type NavConfig } from '../../RemoteShell';
2
2
  import { type Product } from './_storyLib';
3
3
  export interface ConfigRemoteProps {
4
4
  config: NavConfig;
@@ -1,13 +1,11 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
- import { NavPanel, NavPanelHeader } from "../../NavPanel/index.js";
4
3
  import { Page, PageContent, PageHeader, PageTitle } from "../../Page/index.js";
5
- import { NavPanelSkeleton, ProductNav, ProductNavBreadcrumbs, ProductNavPanel, useProductNavContext } from "../../ProductNav/index.js";
6
- import { RemoteShell, RemoteShellBreadcrumb, RemoteShellContent, RemoteShellPanel } from "../../RemoteShell/index.js";
4
+ import { RemoteShell, RemoteShellBreadcrumb, RemoteShellContent, RemoteShellPanel, useRemoteShellContext } from "../../RemoteShell/index.js";
7
5
  import { HomeContent } from "./_storyHomeContent.js";
8
6
  import { PRODUCT_CONFIGS } from "./_storyLib.js";
9
7
  const RemotePageContent = ()=>{
10
- const { breadcrumbSegments } = useProductNavContext();
8
+ const { breadcrumbSegments } = useRemoteShellContext();
11
9
  const lastSegment = breadcrumbSegments[breadcrumbSegments.length - 1];
12
10
  const pageTitle = lastSegment?.label ?? '';
13
11
  const fullPath = breadcrumbSegments.map((s)=>s.label).join(' / ');
@@ -44,36 +42,24 @@ const ConfigRemote = ({ config, basePath })=>{
44
42
  }, [
45
43
  config.productLabel
46
44
  ]);
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, {
45
+ return /*#__PURE__*/ jsx(RemoteShell, {
65
46
  config: config,
66
47
  basePath: basePath,
67
- children: /*#__PURE__*/ jsxs(RemoteShell, {
48
+ children: loading ? /*#__PURE__*/ jsxs(Fragment, {
68
49
  children: [
69
50
  /*#__PURE__*/ jsx(RemoteShellPanel, {
70
- children: /*#__PURE__*/ jsx(ProductNavPanel, {
71
- resizable: true
72
- })
51
+ isLoading: true
73
52
  }),
74
- /*#__PURE__*/ jsx(RemoteShellBreadcrumb, {
75
- children: /*#__PURE__*/ jsx(ProductNavBreadcrumbs, {})
53
+ /*#__PURE__*/ jsx(RemoteShellContent, {
54
+ isLoading: true
55
+ })
56
+ ]
57
+ }) : /*#__PURE__*/ jsxs(Fragment, {
58
+ children: [
59
+ /*#__PURE__*/ jsx(RemoteShellPanel, {
60
+ resizable: true
76
61
  }),
62
+ /*#__PURE__*/ jsx(RemoteShellBreadcrumb, {}),
77
63
  /*#__PURE__*/ jsx(RemoteShellContent, {
78
64
  children: /*#__PURE__*/ jsx(RemotePageContent, {})
79
65
  })
@@ -1,4 +1,4 @@
1
- import { findFirstLinkPath, pushPathname } from "../../ProductNav/index.js";
1
+ import { findFirstLinkPath, pushPathname } from "../../RemoteShell/index.js";
2
2
  import { aiHypervisorNavConfig, edgeNavConfig, infraDiscoveryNavConfig, securityTestingNavConfig, settingsNavConfig } from "./_storyNavConfigs.js";
3
3
  const KNOWN_PRODUCTS = [
4
4
  'home',
@@ -1,4 +1,4 @@
1
- import type { NavConfig } from '../../ProductNav';
1
+ import type { NavConfig } from '../../RemoteShell';
2
2
  export declare const edgeNavConfig: NavConfig;
3
3
  export declare const aiHypervisorNavConfig: NavConfig;
4
4
  export declare const infraDiscoveryNavConfig: NavConfig;
@@ -1,6 +1,7 @@
1
1
  import { CircleDashed, Filter, Plus } from "../../../icons/index.js";
2
2
  const edgeNavConfig = {
3
3
  productLabel: 'Edge',
4
+ productPath: '/edge',
4
5
  headerActions: [
5
6
  {
6
7
  icon: Filter,
@@ -340,6 +341,7 @@ const edgeNavConfig = {
340
341
  };
341
342
  const aiHypervisorNavConfig = {
342
343
  productLabel: 'AI Hypervisor',
344
+ productPath: '/ai-hypervisor',
343
345
  items: [
344
346
  {
345
347
  type: 'link',
@@ -454,6 +456,7 @@ const aiHypervisorNavConfig = {
454
456
  };
455
457
  const infraDiscoveryNavConfig = {
456
458
  productLabel: 'Infra Discovery',
459
+ productPath: '/infra-discovery',
457
460
  headerActions: [
458
461
  {
459
462
  icon: Filter,
@@ -554,6 +557,7 @@ const infraDiscoveryNavConfig = {
554
557
  };
555
558
  const securityTestingNavConfig = {
556
559
  productLabel: 'Security Testing',
560
+ productPath: '/security-testing',
557
561
  items: [
558
562
  {
559
563
  type: 'link',
@@ -627,6 +631,7 @@ const securityTestingNavConfig = {
627
631
  };
628
632
  const settingsNavConfig = {
629
633
  productLabel: 'Settings',
634
+ productPath: '/settings',
630
635
  items: [
631
636
  {
632
637
  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', className),
12
+ className: cn('sticky top-0 z-10 flex shrink-0 items-center p-4 bg-bg-surface-2', 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-24 py-16', className),
11
+ className: cn('flex-1 min-h-0 overflow-auto px-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-24 py-16 gap-12', sticky && 'sticky top-0 z-10 bg-bg-primary', className),
11
+ className: cn('flex items-center px-16 pb-16 gap-12', sticky && 'sticky top-0 z-10 bg-bg-surface-2', className),
12
12
  children: children
13
13
  });
14
14
  };
@@ -1,5 +1,5 @@
1
1
  import type { FC } from 'react';
2
- import type { NavConfigHeaderAction } from './types';
2
+ import type { NavConfigHeaderAction } from './model';
3
3
  export type HeaderActionsProps = {
4
4
  actions: NavConfigHeaderAction[];
5
5
  };
@@ -1,5 +1,5 @@
1
1
  import type { FC } from 'react';
2
- import type { NavConfigDrill, NavConfigNode } from './types';
2
+ import type { NavConfigDrill, NavConfigNode } from './model/types';
3
3
  export type NavItemsListProps = {
4
4
  items: NavConfigNode[];
5
5
  activeItemId: string | null;
@@ -0,0 +1,6 @@
1
+ import type { FC } from 'react';
2
+ interface NavPanelContentProps {
3
+ level: number;
4
+ }
5
+ export declare const NavPanelContent: FC<NavPanelContentProps>;
6
+ export {};
@@ -0,0 +1,65 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { NavPanelBack, NavPanelDivider, NavPanelHeader } from "../NavPanel/index.js";
3
+ import { Text } from "../Text/index.js";
4
+ import { HeaderActions } from "./HeaderActions.js";
5
+ import { findDrillNode, useRemoteShellContext } from "./model/index.js";
6
+ import { NavItemsList } from "./NavItemsList.js";
7
+ const NavPanelContent = ({ level: rawLevel })=>{
8
+ const { config, navStack, effectiveActiveItemId, navigate, drillInto, goBack } = useRemoteShellContext();
9
+ const level = Math.min(rawLevel, navStack.length - 1);
10
+ const entry = navStack[level];
11
+ const hasHeaderActions = !!config.headerActions?.length;
12
+ if (0 === level) return /*#__PURE__*/ jsxs(Fragment, {
13
+ children: [
14
+ hasHeaderActions ? /*#__PURE__*/ jsxs("div", {
15
+ className: "sticky top-0 z-10 flex shrink-0 items-center justify-between p-4 bg-bg-surface-2",
16
+ children: [
17
+ /*#__PURE__*/ jsx(Text, {
18
+ size: "sm",
19
+ weight: "medium",
20
+ children: config.productLabel
21
+ }),
22
+ /*#__PURE__*/ jsx(HeaderActions, {
23
+ actions: config.headerActions
24
+ })
25
+ ]
26
+ }) : /*#__PURE__*/ jsx(NavPanelHeader, {
27
+ children: config.productLabel
28
+ }),
29
+ /*#__PURE__*/ jsx(NavItemsList, {
30
+ items: config.items,
31
+ activeItemId: effectiveActiveItemId,
32
+ onNavigate: navigate,
33
+ onDrillClick: drillInto
34
+ })
35
+ ]
36
+ });
37
+ const parentEntry = navStack[level - 1];
38
+ const drillNode = findDrillNode(parentEntry.items, parentEntry.activeItemId);
39
+ const backLabel = drillNode?.label ?? 'Back';
40
+ return /*#__PURE__*/ jsxs(Fragment, {
41
+ children: [
42
+ /*#__PURE__*/ jsxs("div", {
43
+ className: "sticky top-0 z-10 flex flex-col gap-2 bg-bg-surface-1",
44
+ children: [
45
+ /*#__PURE__*/ jsx(NavPanelHeader, {
46
+ children: entry.title
47
+ }),
48
+ /*#__PURE__*/ jsx(NavPanelBack, {
49
+ onClick: goBack,
50
+ children: backLabel
51
+ }),
52
+ /*#__PURE__*/ jsx(NavPanelDivider, {})
53
+ ]
54
+ }),
55
+ /*#__PURE__*/ jsx(NavItemsList, {
56
+ items: entry.items,
57
+ activeItemId: effectiveActiveItemId,
58
+ onNavigate: navigate,
59
+ onDrillClick: drillInto
60
+ })
61
+ ]
62
+ });
63
+ };
64
+ NavPanelContent.displayName = 'NavPanelContent';
65
+ export { NavPanelContent };
@@ -1,7 +1,14 @@
1
1
  import type { FC, HTMLAttributes, ReactNode, Ref } from 'react';
2
2
  import { type TestableProps } from '../../utils/testId';
3
+ import type { NavConfig } from './model';
3
4
  export interface RemoteShellProps extends HTMLAttributes<HTMLDivElement>, TestableProps {
4
5
  ref?: Ref<HTMLDivElement>;
5
6
  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;
6
13
  }
7
14
  export declare const RemoteShell: FC<RemoteShellProps>;
@@ -1,16 +1,116 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
3
  import { cn } from "../../utils/cn.js";
3
4
  import { TestIdProvider } from "../../utils/testId.js";
4
- const RemoteShell = ({ ref, className, children, 'data-testid': testId, ...props })=>/*#__PURE__*/ jsx(TestIdProvider, {
5
- value: testId,
6
- children: /*#__PURE__*/ jsx("div", {
7
- ...props,
8
- ref: ref,
9
- "data-slot": "remote-shell",
10
- "data-testid": testId,
11
- className: cn('grid h-full overflow-hidden overscroll-none [grid-template-areas:"panel_breadcrumb""panel_content"] [grid-template-columns:auto_1fr] [grid-template-rows:auto_1fr]', className),
12
- children: children
5
+ import { RemoteShellContextProvider, findFirstLinkPath, matchNav, pushPathname, useLocationPathname } from "./model/index.js";
6
+ const RemoteShell = ({ ref, className, children, config, basePath, onNavigate, 'data-testid': testId, ...props })=>{
7
+ const fullPathname = useLocationPathname();
8
+ const pathname = basePath && fullPathname.startsWith(basePath) ? fullPathname.slice(basePath.length) || '/' : fullPathname;
9
+ const setPathname = useCallback((next)=>{
10
+ const fullPath = basePath ? `${basePath}${next}` : next;
11
+ if (onNavigate) onNavigate(fullPath);
12
+ else pushPathname(fullPath);
13
+ }, [
14
+ basePath,
15
+ onNavigate
16
+ ]);
17
+ const { navStack, breadcrumbSegments, activeItemId } = useMemo(()=>matchNav(pathname, config), [
18
+ pathname,
19
+ config
20
+ ]);
21
+ const urlDrillLevel = navStack.length - 1;
22
+ const [visualDrillLevel, setVisualDrillLevel] = useState(null);
23
+ useEffect(()=>{
24
+ setVisualDrillLevel(null);
25
+ }, [
26
+ pathname
27
+ ]);
28
+ const effectiveDrillLevel = visualDrillLevel ?? urlDrillLevel;
29
+ const effectiveActiveItemId = effectiveDrillLevel < urlDrillLevel ? navStack[effectiveDrillLevel]?.activeItemId ?? activeItemId : activeItemId;
30
+ const navigate = useCallback((path)=>{
31
+ setVisualDrillLevel(null);
32
+ const segments = pathname.replace(/^\/+|\/+$/g, '').split('/').filter(Boolean);
33
+ const prefixSegments = segments.slice(0, 2 * effectiveDrillLevel);
34
+ setPathname(`/${[
35
+ ...prefixSegments,
36
+ path
37
+ ].join('/')}`);
38
+ }, [
39
+ effectiveDrillLevel,
40
+ pathname,
41
+ setPathname
42
+ ]);
43
+ const drillInto = useCallback((drill)=>{
44
+ setVisualDrillLevel(null);
45
+ const segments = pathname.replace(/^\/+|\/+$/g, '').split('/').filter(Boolean);
46
+ const prefixSegments = segments.slice(0, 2 * effectiveDrillLevel);
47
+ const defaultEntity = drill.entities?.[0]?.id ?? 'default';
48
+ const firstChildPath = findFirstLinkPath(drill.children) ?? '';
49
+ setPathname(`/${[
50
+ ...prefixSegments,
51
+ drill.path,
52
+ defaultEntity,
53
+ firstChildPath
54
+ ].join('/')}`);
55
+ }, [
56
+ effectiveDrillLevel,
57
+ pathname,
58
+ setPathname
59
+ ]);
60
+ const goBack = useCallback(()=>{
61
+ setVisualDrillLevel((prev)=>{
62
+ const current = prev ?? urlDrillLevel;
63
+ return Math.max(current - 1, 0);
64
+ });
65
+ }, [
66
+ urlDrillLevel
67
+ ]);
68
+ const navigateTo = useCallback((href)=>{
69
+ setVisualDrillLevel(null);
70
+ href === config.productPath ? setPathname('/') : setPathname(href);
71
+ }, [
72
+ config.productPath,
73
+ setPathname
74
+ ]);
75
+ const navCtxValue = useMemo(()=>({
76
+ config,
77
+ pathname,
78
+ navStack,
79
+ breadcrumbSegments,
80
+ activeItemId,
81
+ drillLevel: effectiveDrillLevel,
82
+ effectiveActiveItemId,
83
+ navigate,
84
+ drillInto,
85
+ goBack,
86
+ navigateTo
87
+ }), [
88
+ config,
89
+ pathname,
90
+ navStack,
91
+ breadcrumbSegments,
92
+ activeItemId,
93
+ effectiveDrillLevel,
94
+ effectiveActiveItemId,
95
+ navigate,
96
+ drillInto,
97
+ goBack,
98
+ navigateTo
99
+ ]);
100
+ return /*#__PURE__*/ jsx(RemoteShellContextProvider, {
101
+ value: navCtxValue,
102
+ children: /*#__PURE__*/ jsx(TestIdProvider, {
103
+ value: testId,
104
+ children: /*#__PURE__*/ jsx("div", {
105
+ ...props,
106
+ ref: ref,
107
+ "data-slot": "remote-shell",
108
+ "data-testid": testId,
109
+ className: cn('grid h-full overflow-hidden overscroll-none [grid-template-areas:"panel_breadcrumb""panel_content"] [grid-template-columns:auto_1fr] [grid-template-rows:auto_1fr]', className),
110
+ children: children
111
+ })
13
112
  })
14
113
  });
114
+ };
15
115
  RemoteShell.displayName = 'RemoteShell';
16
116
  export { RemoteShell };
@@ -1,15 +1,50 @@
1
- import { jsx } from "react/jsx-runtime";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { cn } from "../../utils/cn.js";
3
3
  import { useTestId } from "../../utils/testId.js";
4
+ import { Breadcrumbs, BreadcrumbsItem, BreadcrumbsScopeSwitcher } from "../Breadcrumbs/index.js";
5
+ import { useRemoteShellContext } from "./model/index.js";
4
6
  const RemoteShellBreadcrumb = ({ ref, className, children, ...props })=>{
5
7
  const testId = useTestId('breadcrumb');
6
- return /*#__PURE__*/ jsx("div", {
8
+ const { breadcrumbSegments, navigateTo } = useRemoteShellContext();
9
+ return /*#__PURE__*/ jsxs("div", {
7
10
  ...props,
8
11
  ref: ref,
9
12
  "data-slot": "remote-shell-breadcrumb",
10
13
  "data-testid": testId,
11
- className: cn('[grid-area:breadcrumb] flex items-center px-24 py-8', className),
12
- children: children
14
+ className: cn('[grid-area:breadcrumb] flex items-center gap-8 px-16 py-8', className),
15
+ children: [
16
+ /*#__PURE__*/ jsx(Breadcrumbs, {
17
+ "data-slot": "nav-breadcrumbs",
18
+ children: breadcrumbSegments.map((segment, i)=>{
19
+ const isLast = i === breadcrumbSegments.length - 1;
20
+ if ('scope-switcher' === segment.type) {
21
+ if (segment.scopeItems?.length && segment.paramValue) return /*#__PURE__*/ jsx(BreadcrumbsScopeSwitcher, {
22
+ value: segment.paramValue,
23
+ items: segment.scopeItems,
24
+ onSelect: (item)=>navigateTo?.(item.href),
25
+ children: segment.label
26
+ }, i);
27
+ return /*#__PURE__*/ jsx(BreadcrumbsItem, {
28
+ children: segment.label
29
+ }, i);
30
+ }
31
+ if ('link' === segment.type && !isLast) return /*#__PURE__*/ jsx(BreadcrumbsItem, {
32
+ href: segment.href,
33
+ onClick: (e)=>{
34
+ if (navigateTo && segment.href) {
35
+ e.preventDefault();
36
+ navigateTo(segment.href);
37
+ }
38
+ },
39
+ children: segment.label
40
+ }, i);
41
+ return /*#__PURE__*/ jsx(BreadcrumbsItem, {
42
+ children: segment.label
43
+ }, i);
44
+ })
45
+ }),
46
+ children
47
+ ]
13
48
  });
14
49
  };
15
50
  RemoteShellBreadcrumb.displayName = 'RemoteShellBreadcrumb';
@@ -2,5 +2,6 @@ import type { FC, HTMLAttributes, ReactNode, Ref } from 'react';
2
2
  export interface RemoteShellContentProps extends HTMLAttributes<HTMLDivElement> {
3
3
  ref?: Ref<HTMLDivElement>;
4
4
  children?: ReactNode;
5
+ isLoading?: boolean;
5
6
  }
6
7
  export declare const RemoteShellContent: FC<RemoteShellContentProps>;
@@ -1,15 +1,19 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { cn } from "../../utils/cn.js";
3
3
  import { useTestId } from "../../utils/testId.js";
4
- const RemoteShellContent = ({ ref, className, children, ...props })=>{
4
+ import { Loader } from "../Loader/index.js";
5
+ const RemoteShellContent = ({ ref, className, children, isLoading, ...props })=>{
5
6
  const testId = useTestId('content');
6
7
  return /*#__PURE__*/ jsx("div", {
7
8
  ...props,
8
9
  ref: ref,
9
10
  "data-slot": "remote-shell-content",
10
11
  "data-testid": testId,
11
- className: cn('[grid-area:content] min-h-0 overflow-auto overscroll-none px-24 py-16 [scrollbar-width:thin]', className),
12
- children: children
12
+ className: cn('[grid-area:content] min-h-0 overflow-auto overscroll-none [scrollbar-width:thin]', isLoading && 'flex items-center justify-center', className),
13
+ children: isLoading ? /*#__PURE__*/ jsx(Loader, {
14
+ size: "3xl",
15
+ color: "brand"
16
+ }) : children
13
17
  });
14
18
  };
15
19
  RemoteShellContent.displayName = 'RemoteShellContent';
@@ -1,6 +1,8 @@
1
- import type { FC, HTMLAttributes, ReactNode, Ref } from 'react';
1
+ import type { FC, HTMLAttributes, Ref } from 'react';
2
2
  export interface RemoteShellPanelProps extends HTMLAttributes<HTMLDivElement> {
3
3
  ref?: Ref<HTMLDivElement>;
4
- children?: ReactNode;
4
+ resizable?: boolean;
5
+ isLoading?: boolean;
6
+ loaderCount?: number;
5
7
  }
6
8
  export declare const RemoteShellPanel: FC<RemoteShellPanelProps>;
@@ -1,15 +1,88 @@
1
- import { jsx } from "react/jsx-runtime";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { cn } from "../../utils/cn.js";
3
3
  import { useTestId } from "../../utils/testId.js";
4
- const RemoteShellPanel = ({ ref, className, children, ...props })=>{
4
+ import { NavPanel, NavPanelHeader, NavPanelSkeleton } from "../NavPanel/index.js";
5
+ import { useRemoteShellContext } from "./model/index.js";
6
+ import { DRILL_ANIMATION_DURATION, DRILL_ANIMATION_EASING, useDrillTransition } from "./model/useDrillTransition.js";
7
+ import { NavPanelContent } from "./NavPanelContent.js";
8
+ const RemoteShellPanel = ({ ref, className, resizable, isLoading, loaderCount = 6, ...props })=>{
5
9
  const testId = useTestId('panel');
10
+ const { config, drillLevel } = useRemoteShellContext();
11
+ const { transition, clearTransition } = useDrillTransition(drillLevel);
12
+ if (isLoading) return /*#__PURE__*/ jsx("div", {
13
+ ...props,
14
+ ref: ref,
15
+ "data-slot": "remote-shell-panel",
16
+ "data-testid": testId,
17
+ className: cn('[grid-area:panel] min-h-0', className),
18
+ children: /*#__PURE__*/ jsxs(NavPanel, {
19
+ children: [
20
+ /*#__PURE__*/ jsx(NavPanelHeader, {
21
+ children: config.productLabel
22
+ }),
23
+ /*#__PURE__*/ jsx(NavPanelSkeleton, {
24
+ count: loaderCount
25
+ })
26
+ ]
27
+ })
28
+ });
29
+ if (transition) {
30
+ const isForward = 'forward' === transition.direction;
31
+ const slideAnim = isForward ? 'ds-nav-drill-forward' : 'ds-nav-drill-backward';
32
+ const timing = `${DRILL_ANIMATION_DURATION} ${DRILL_ANIMATION_EASING} both`;
33
+ return /*#__PURE__*/ jsx("div", {
34
+ ...props,
35
+ ref: ref,
36
+ "data-slot": "remote-shell-panel",
37
+ "data-testid": testId,
38
+ className: cn('[grid-area:panel] min-h-0', className),
39
+ children: /*#__PURE__*/ jsx(NavPanel, {
40
+ resizable: resizable,
41
+ children: /*#__PURE__*/ jsx("div", {
42
+ className: "min-h-0 flex-1 overflow-hidden",
43
+ children: /*#__PURE__*/ jsxs("div", {
44
+ className: "flex motion-reduce:animate-none",
45
+ style: {
46
+ animation: `${slideAnim} ${timing}`
47
+ },
48
+ onAnimationEnd: clearTransition,
49
+ children: [
50
+ /*#__PURE__*/ jsx("div", {
51
+ className: "flex w-full shrink-0 flex-col gap-2 motion-reduce:animate-none",
52
+ style: {
53
+ animation: `${isForward ? 'ds-nav-blur-out' : 'ds-nav-blur-in'} ${timing}`
54
+ },
55
+ children: /*#__PURE__*/ jsx(NavPanelContent, {
56
+ level: isForward ? transition.fromLevel : drillLevel
57
+ })
58
+ }),
59
+ /*#__PURE__*/ jsx("div", {
60
+ className: "flex w-full shrink-0 flex-col gap-2 motion-reduce:animate-none",
61
+ style: {
62
+ animation: `${isForward ? 'ds-nav-blur-in' : 'ds-nav-blur-out'} ${timing}`
63
+ },
64
+ children: /*#__PURE__*/ jsx(NavPanelContent, {
65
+ level: isForward ? drillLevel : transition.fromLevel
66
+ })
67
+ })
68
+ ]
69
+ })
70
+ })
71
+ })
72
+ });
73
+ }
6
74
  return /*#__PURE__*/ jsx("div", {
7
75
  ...props,
8
76
  ref: ref,
9
77
  "data-slot": "remote-shell-panel",
10
78
  "data-testid": testId,
11
79
  className: cn('[grid-area:panel] min-h-0', className),
12
- children: children
80
+ children: /*#__PURE__*/ jsx(NavPanel, {
81
+ resizable: resizable,
82
+ children: /*#__PURE__*/ jsx(NavPanelContent, {
83
+ level: drillLevel
84
+ })
85
+ })
13
86
  });
14
87
  };
15
88
  RemoteShellPanel.displayName = 'RemoteShellPanel';
@@ -1,3 +1,5 @@
1
+ export type { BreadcrumbSegment, NavConfig, NavConfigDrill, NavConfigGroup, NavConfigHeaderAction, NavConfigLink, NavConfigNode, NavConfigSectionHeader, NavStackEntry, RemoteShellContextValue, } from './model';
2
+ export { findFirstLinkPath, pushPathname, useLocationPathname, useRemoteShellContext, } from './model';
1
3
  export { RemoteShell, type RemoteShellProps } from './RemoteShell';
2
4
  export { RemoteShellBreadcrumb, type RemoteShellBreadcrumbProps } from './RemoteShellBreadcrumb';
3
5
  export { RemoteShellContent, type RemoteShellContentProps } from './RemoteShellContent';
@@ -1,5 +1,6 @@
1
+ import { findFirstLinkPath, pushPathname, useLocationPathname, useRemoteShellContext } from "./model/index.js";
1
2
  import { RemoteShell } from "./RemoteShell.js";
2
3
  import { RemoteShellBreadcrumb } from "./RemoteShellBreadcrumb.js";
3
4
  import { RemoteShellContent } from "./RemoteShellContent.js";
4
5
  import { RemoteShellPanel } from "./RemoteShellPanel.js";
5
- export { RemoteShell, RemoteShellBreadcrumb, RemoteShellContent, RemoteShellPanel };
6
+ export { RemoteShell, RemoteShellBreadcrumb, RemoteShellContent, RemoteShellPanel, findFirstLinkPath, pushPathname, useLocationPathname, useRemoteShellContext };
@@ -1,5 +1,5 @@
1
1
  import type { BreadcrumbSegment, NavConfig, NavConfigDrill, NavStackEntry } from './types';
2
- export interface ProductNavContextValue {
2
+ export interface RemoteShellContextValue {
3
3
  config: NavConfig;
4
4
  pathname: string;
5
5
  navStack: NavStackEntry[];
@@ -18,5 +18,5 @@ export interface ProductNavContextValue {
18
18
  /** Navigate to an absolute href (used by breadcrumbs) */
19
19
  navigateTo: (href: string) => void;
20
20
  }
21
- export declare const ProductNavContextProvider: import("react").Provider<ProductNavContextValue | null>;
22
- export declare function useProductNavContext(): ProductNavContextValue;
21
+ export declare const RemoteShellContextProvider: import("react").Provider<RemoteShellContextValue | null>;
22
+ export declare function useRemoteShellContext(): RemoteShellContextValue;
@@ -0,0 +1,9 @@
1
+ import { createContext, useContext } from "react";
2
+ const RemoteShellCtx = createContext(null);
3
+ const RemoteShellContextProvider = RemoteShellCtx.Provider;
4
+ function useRemoteShellContext() {
5
+ const ctx = useContext(RemoteShellCtx);
6
+ if (!ctx) throw new Error('useRemoteShellContext must be used within a RemoteShell with a config prop');
7
+ return ctx;
8
+ }
9
+ export { RemoteShellContextProvider, useRemoteShellContext };
@@ -0,0 +1,5 @@
1
+ export { type MatchNavResult, matchNav } from './matchNav';
2
+ export { findDrillNode, findFirstLinkPath } from './navUtils';
3
+ export { RemoteShellContextProvider, type RemoteShellContextValue, useRemoteShellContext, } from './RemoteShellContext';
4
+ export type { BreadcrumbSegment, NavConfig, NavConfigDrill, NavConfigGroup, NavConfigHeaderAction, NavConfigLink, NavConfigNode, NavConfigSectionHeader, NavStackEntry, } from './types';
5
+ export { pushPathname, useLocationPathname } from './useLocationPathname';
@@ -0,0 +1,5 @@
1
+ import { matchNav } from "./matchNav.js";
2
+ import { findDrillNode, findFirstLinkPath } from "./navUtils.js";
3
+ import { RemoteShellContextProvider, useRemoteShellContext } from "./RemoteShellContext.js";
4
+ import { pushPathname, useLocationPathname } from "./useLocationPathname.js";
5
+ export { RemoteShellContextProvider, findDrillNode, findFirstLinkPath, matchNav, pushPathname, useLocationPathname, useRemoteShellContext };
@@ -28,7 +28,7 @@ const matchNav = (pathname, config)=>{
28
28
  breadcrumbSegments.push({
29
29
  type: 'link',
30
30
  label: config.productLabel,
31
- href: '/'
31
+ href: config.productPath
32
32
  });
33
33
  let segmentIndex = 0;
34
34
  let currentItems = config.items;