@wallarm-org/design-system 0.47.0 → 0.48.0-rc-feature-AS-1033.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/AppShell/AppShellRemote.js +1 -1
- package/dist/components/AppShell/story-content/_storyConfigRenderer.d.ts +5 -3
- package/dist/components/AppShell/story-content/_storyConfigRenderer.js +44 -3
- package/dist/components/AppShell/story-content/_storyLib.d.ts +9 -0
- package/dist/components/AppShell/story-content/_storyLib.js +39 -0
- package/dist/components/AppShell/story-content/_storyRecentDropdown.d.ts +1 -0
- package/dist/components/AppShell/story-content/_storyRecentDropdown.js +89 -0
- package/dist/components/AppShell/story-content/index.d.ts +3 -1
- package/dist/components/AppShell/story-content/index.js +4 -2
- package/dist/components/Kbd/Kbd.d.ts +1 -1
- package/dist/components/Kbd/Kbd.js +1 -0
- package/dist/components/NavPanel/NavPanelSkeleton.d.ts +6 -0
- package/dist/components/NavPanel/NavPanelSkeleton.js +23 -0
- package/dist/components/NavPanel/index.d.ts +1 -0
- package/dist/components/NavPanel/index.js +2 -1
- package/dist/components/NavRail/NavRailSkeleton.d.ts +6 -0
- package/dist/components/NavRail/NavRailSkeleton.js +23 -0
- package/dist/components/NavRail/index.d.ts +1 -0
- package/dist/components/NavRail/index.js +2 -1
- package/dist/components/OverflowList/OverflowList.js +24 -12
- package/dist/components/ProductNav/index.d.ts +1 -0
- package/dist/components/ProductNav/index.js +2 -1
- package/dist/components/Table/mocks.js +40 -10
- package/dist/hooks/useOverflowItems.helpers.d.ts +15 -0
- package/dist/hooks/useOverflowItems.helpers.js +19 -0
- package/dist/hooks/useOverflowItems.js +80 -64
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/metadata/components.json +885 -5
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ const AppShellRemote = ({ ref, className, children, ...props })=>{
|
|
|
8
8
|
ref: ref,
|
|
9
9
|
"data-slot": "app-shell-remote",
|
|
10
10
|
"data-testid": testId,
|
|
11
|
-
className: cn('[grid-area:remote] overflow-auto overscroll-none rounded-tl-12 border border-border-primary-light bg-bg-page-bg', className),
|
|
11
|
+
className: cn('[grid-area:remote] relative overflow-auto overscroll-none rounded-tl-12 border border-border-primary-light bg-bg-page-bg', className),
|
|
12
12
|
children: children
|
|
13
13
|
});
|
|
14
14
|
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type
|
|
1
|
+
import { type NavConfig } from '../../ProductNav';
|
|
2
|
+
import { type Product } from './_storyLib';
|
|
3
3
|
export interface ConfigRemoteProps {
|
|
4
4
|
config: NavConfig;
|
|
5
5
|
basePath?: string;
|
|
6
6
|
}
|
|
7
|
-
export declare const
|
|
7
|
+
export declare const RemoteForProduct: ({ product }: {
|
|
8
|
+
product: Product;
|
|
9
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { NavPanel, NavPanelHeader } from "../../NavPanel/index.js";
|
|
4
|
+
import { NavPanelSkeleton, ProductNav, ProductNavBreadcrumbs, ProductNavPanel, useProductNavContext } from "../../ProductNav/index.js";
|
|
3
5
|
import { RemoteShell, RemoteShellBreadcrumb, RemoteShellContent, RemoteShellPanel } from "../../RemoteShell/index.js";
|
|
6
|
+
import { HomeContent } from "./_storyHomeContent.js";
|
|
7
|
+
import { PRODUCT_CONFIGS } from "./_storyLib.js";
|
|
4
8
|
const PageContent = ()=>{
|
|
5
9
|
const { config, breadcrumbSegments } = useProductNavContext();
|
|
6
10
|
const lastSegment = breadcrumbSegments[breadcrumbSegments.length - 1];
|
|
@@ -24,7 +28,35 @@ const PageContent = ()=>{
|
|
|
24
28
|
]
|
|
25
29
|
});
|
|
26
30
|
};
|
|
27
|
-
const ConfigRemote = ({ config, basePath })
|
|
31
|
+
const ConfigRemote = ({ config, basePath })=>{
|
|
32
|
+
const [loading, setLoading] = useState(true);
|
|
33
|
+
useEffect(()=>{
|
|
34
|
+
setLoading(true);
|
|
35
|
+
const timer = setTimeout(()=>{
|
|
36
|
+
setLoading(false);
|
|
37
|
+
}, 2000);
|
|
38
|
+
return ()=>clearTimeout(timer);
|
|
39
|
+
}, [
|
|
40
|
+
config.productLabel
|
|
41
|
+
]);
|
|
42
|
+
if (loading) return /*#__PURE__*/ jsxs(RemoteShell, {
|
|
43
|
+
children: [
|
|
44
|
+
/*#__PURE__*/ jsx(RemoteShellPanel, {
|
|
45
|
+
children: /*#__PURE__*/ jsxs(NavPanel, {
|
|
46
|
+
children: [
|
|
47
|
+
/*#__PURE__*/ jsx(NavPanelHeader, {
|
|
48
|
+
children: config.productLabel
|
|
49
|
+
}),
|
|
50
|
+
/*#__PURE__*/ jsx(NavPanelSkeleton, {
|
|
51
|
+
count: 6
|
|
52
|
+
})
|
|
53
|
+
]
|
|
54
|
+
})
|
|
55
|
+
}),
|
|
56
|
+
/*#__PURE__*/ jsx(RemoteShellContent, {})
|
|
57
|
+
]
|
|
58
|
+
});
|
|
59
|
+
return /*#__PURE__*/ jsx(ProductNav, {
|
|
28
60
|
config: config,
|
|
29
61
|
basePath: basePath,
|
|
30
62
|
children: /*#__PURE__*/ jsxs(RemoteShell, {
|
|
@@ -43,4 +75,13 @@ const ConfigRemote = ({ config, basePath })=>/*#__PURE__*/ jsx(ProductNav, {
|
|
|
43
75
|
]
|
|
44
76
|
})
|
|
45
77
|
});
|
|
46
|
-
|
|
78
|
+
};
|
|
79
|
+
const RemoteForProduct = ({ product })=>{
|
|
80
|
+
if ('home' === product) return /*#__PURE__*/ jsx(HomeContent, {});
|
|
81
|
+
const { config } = PRODUCT_CONFIGS[product];
|
|
82
|
+
return /*#__PURE__*/ jsx(ConfigRemote, {
|
|
83
|
+
config: config,
|
|
84
|
+
basePath: `/${product}`
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
export { RemoteForProduct };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { edgeNavConfig } from './_storyNavConfigs';
|
|
2
|
+
declare const KNOWN_PRODUCTS: readonly ["home", "edge", "ai-hypervisor", "infra-discovery", "security-testing", "settings"];
|
|
3
|
+
export type Product = (typeof KNOWN_PRODUCTS)[number];
|
|
4
|
+
export declare const PRODUCT_CONFIGS: Record<Exclude<Product, 'home'>, {
|
|
5
|
+
config: typeof edgeNavConfig;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function deriveProduct(pathname: string): Product;
|
|
8
|
+
export declare function navigateToProduct(product: Product): void;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { findFirstLinkPath, pushPathname } from "../../ProductNav/index.js";
|
|
2
|
+
import { aiHypervisorNavConfig, edgeNavConfig, infraDiscoveryNavConfig, securityTestingNavConfig, settingsNavConfig } from "./_storyNavConfigs.js";
|
|
3
|
+
const KNOWN_PRODUCTS = [
|
|
4
|
+
'home',
|
|
5
|
+
'edge',
|
|
6
|
+
'ai-hypervisor',
|
|
7
|
+
'infra-discovery',
|
|
8
|
+
'security-testing',
|
|
9
|
+
'settings'
|
|
10
|
+
];
|
|
11
|
+
const PRODUCT_CONFIGS = {
|
|
12
|
+
edge: {
|
|
13
|
+
config: edgeNavConfig
|
|
14
|
+
},
|
|
15
|
+
'ai-hypervisor': {
|
|
16
|
+
config: aiHypervisorNavConfig
|
|
17
|
+
},
|
|
18
|
+
'infra-discovery': {
|
|
19
|
+
config: infraDiscoveryNavConfig
|
|
20
|
+
},
|
|
21
|
+
'security-testing': {
|
|
22
|
+
config: securityTestingNavConfig
|
|
23
|
+
},
|
|
24
|
+
settings: {
|
|
25
|
+
config: settingsNavConfig
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
function deriveProduct(pathname) {
|
|
29
|
+
const segment = pathname.split('/').filter(Boolean)[0];
|
|
30
|
+
if (segment && KNOWN_PRODUCTS.includes(segment)) return segment;
|
|
31
|
+
return 'home';
|
|
32
|
+
}
|
|
33
|
+
function navigateToProduct(product) {
|
|
34
|
+
if ('home' === product) return void pushPathname('/home');
|
|
35
|
+
const { config } = PRODUCT_CONFIGS[product];
|
|
36
|
+
const firstPath = findFirstLinkPath(config.items) ?? '';
|
|
37
|
+
pushPathname(`/${product}/${firstPath}`);
|
|
38
|
+
}
|
|
39
|
+
export { PRODUCT_CONFIGS, deriveProduct, navigateToProduct };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const RecentDropdown: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { History } from "../../../icons/index.js";
|
|
3
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuItemContent, DropdownMenuItemText, DropdownMenuLabel, DropdownMenuTrigger } from "../../DropdownMenu/index.js";
|
|
4
|
+
import { NavRailItem } from "../../NavRail/index.js";
|
|
5
|
+
import { Text } from "../../Text/index.js";
|
|
6
|
+
const RECENT_ITEMS = [
|
|
7
|
+
{
|
|
8
|
+
pageName: 'WAF Rules',
|
|
9
|
+
productName: 'Edge'
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
pageName: 'API Sessions',
|
|
13
|
+
dataPlane: 'US Cloud',
|
|
14
|
+
productName: 'Edge'
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
pageName: 'Prompt Inspector',
|
|
18
|
+
productName: 'AI Hypervisor'
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
pageName: 'Scanner Results',
|
|
22
|
+
dataPlane: 'EU Cloud',
|
|
23
|
+
productName: 'Security Testing'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
pageName: 'Asset Inventory',
|
|
27
|
+
productName: 'Infra Discovery'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
pageName: 'Pupu',
|
|
31
|
+
productName: 'Edge'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
pageName: 'Pupupu',
|
|
35
|
+
dataPlane: 'US Cloud',
|
|
36
|
+
productName: 'Edge'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
pageName: 'PuPuPuuu',
|
|
40
|
+
productName: 'AI Hypervisor'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
pageName: 'MeowMeow',
|
|
44
|
+
dataPlane: 'EU Cloud',
|
|
45
|
+
productName: 'Security Testing'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
pageName: '...',
|
|
49
|
+
productName: '....'
|
|
50
|
+
}
|
|
51
|
+
];
|
|
52
|
+
const RecentDropdown = ()=>/*#__PURE__*/ jsxs(DropdownMenu, {
|
|
53
|
+
positioning: {
|
|
54
|
+
placement: 'right-start',
|
|
55
|
+
gutter: 8
|
|
56
|
+
},
|
|
57
|
+
children: [
|
|
58
|
+
/*#__PURE__*/ jsx(DropdownMenuTrigger, {
|
|
59
|
+
children: /*#__PURE__*/ jsx(NavRailItem, {
|
|
60
|
+
icon: History,
|
|
61
|
+
label: "Recent"
|
|
62
|
+
})
|
|
63
|
+
}),
|
|
64
|
+
/*#__PURE__*/ jsxs(DropdownMenuContent, {
|
|
65
|
+
className: "w-304 h-290 overscroll-none",
|
|
66
|
+
children: [
|
|
67
|
+
/*#__PURE__*/ jsx(DropdownMenuLabel, {
|
|
68
|
+
className: "sticky top-0 z-10 bg-bg-surface-2",
|
|
69
|
+
children: "Recent"
|
|
70
|
+
}),
|
|
71
|
+
RECENT_ITEMS.map((item)=>/*#__PURE__*/ jsx(DropdownMenuItem, {
|
|
72
|
+
children: /*#__PURE__*/ jsxs(DropdownMenuItemContent, {
|
|
73
|
+
children: [
|
|
74
|
+
/*#__PURE__*/ jsx(DropdownMenuItemText, {
|
|
75
|
+
children: item.pageName
|
|
76
|
+
}),
|
|
77
|
+
/*#__PURE__*/ jsx(Text, {
|
|
78
|
+
size: "xs",
|
|
79
|
+
color: "secondary",
|
|
80
|
+
children: item.dataPlane ? `${item.dataPlane} \u00b7 ${item.productName}` : item.productName
|
|
81
|
+
})
|
|
82
|
+
]
|
|
83
|
+
})
|
|
84
|
+
}, `${item.pageName}-${item.productName}`))
|
|
85
|
+
]
|
|
86
|
+
})
|
|
87
|
+
]
|
|
88
|
+
});
|
|
89
|
+
export { RecentDropdown };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { AccountDropdown, type SidebarMode } from './_storyAccountDropdown';
|
|
2
|
-
export {
|
|
2
|
+
export { RemoteForProduct } from './_storyConfigRenderer';
|
|
3
3
|
export { HomeContent } from './_storyHomeContent';
|
|
4
|
+
export { deriveProduct, navigateToProduct } from './_storyLib';
|
|
4
5
|
export { aiHypervisorNavConfig, edgeNavConfig, infraDiscoveryNavConfig, securityTestingNavConfig, settingsNavConfig, } from './_storyNavConfigs';
|
|
5
6
|
export { QuickHelpDropdown } from './_storyQuickHelpDropdown';
|
|
7
|
+
export { RecentDropdown } from './_storyRecentDropdown';
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { AccountDropdown } from "./_storyAccountDropdown.js";
|
|
2
|
-
import {
|
|
2
|
+
import { RemoteForProduct } from "./_storyConfigRenderer.js";
|
|
3
3
|
import { HomeContent } from "./_storyHomeContent.js";
|
|
4
|
+
import { deriveProduct, navigateToProduct } from "./_storyLib.js";
|
|
4
5
|
import { aiHypervisorNavConfig, edgeNavConfig, infraDiscoveryNavConfig, securityTestingNavConfig, settingsNavConfig } from "./_storyNavConfigs.js";
|
|
5
6
|
import { QuickHelpDropdown } from "./_storyQuickHelpDropdown.js";
|
|
6
|
-
|
|
7
|
+
import { RecentDropdown } from "./_storyRecentDropdown.js";
|
|
8
|
+
export { AccountDropdown, HomeContent, QuickHelpDropdown, RecentDropdown, RemoteForProduct, aiHypervisorNavConfig, deriveProduct, edgeNavConfig, infraDiscoveryNavConfig, navigateToProduct, securityTestingNavConfig, settingsNavConfig };
|
|
@@ -2,7 +2,7 @@ import type { ComponentProps, FC } from 'react';
|
|
|
2
2
|
import { type VariantProps } from 'class-variance-authority';
|
|
3
3
|
import type { TestableProps } from '../../utils/testId';
|
|
4
4
|
declare const kbdVariants: (props?: ({
|
|
5
|
-
size?: "small" | "medium" | null | undefined;
|
|
5
|
+
size?: "small" | "medium" | "xsmall" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
type KbdVariantsProps = VariantProps<typeof kbdVariants>;
|
|
8
8
|
type KbdProps = ComponentProps<'kbd'> & KbdVariantsProps & TestableProps;
|
|
@@ -4,6 +4,7 @@ import { cn } from "../../utils/cn.js";
|
|
|
4
4
|
const kbdVariants = cva(cn('inline-flex items-center justify-center gap-1 w-fit', 'border border-component-border-hotkey rounded-4 bg-bg-surface-2', 'font-sans font-medium', 'text-text-primary text-xs', 'pointer-events-none select-none'), {
|
|
5
5
|
variants: {
|
|
6
6
|
size: {
|
|
7
|
+
xsmall: 'h-16 min-w-20 p-2',
|
|
7
8
|
small: 'h-20 min-w-20 p-4',
|
|
8
9
|
medium: 'h-24 min-w-24 p-6'
|
|
9
10
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../utils/cn.js";
|
|
3
|
+
import { useTestId } from "../../utils/testId.js";
|
|
4
|
+
import { Skeleton } from "../Skeleton/index.js";
|
|
5
|
+
const NavPanelSkeleton = ({ ref, className, count = 6, ...props })=>{
|
|
6
|
+
const testId = useTestId('skeleton');
|
|
7
|
+
return /*#__PURE__*/ jsx("div", {
|
|
8
|
+
...props,
|
|
9
|
+
ref: ref,
|
|
10
|
+
"data-slot": "nav-panel-skeleton",
|
|
11
|
+
"data-testid": testId,
|
|
12
|
+
className: cn('flex flex-col gap-6', className),
|
|
13
|
+
children: Array.from({
|
|
14
|
+
length: count
|
|
15
|
+
}, (_, i)=>/*#__PURE__*/ jsx(Skeleton, {
|
|
16
|
+
width: "100%",
|
|
17
|
+
height: "28px",
|
|
18
|
+
rounded: 6
|
|
19
|
+
}, i))
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
NavPanelSkeleton.displayName = 'NavPanelSkeleton';
|
|
23
|
+
export { NavPanelSkeleton };
|
|
@@ -8,3 +8,4 @@ export { NavPanelGroupLabel, type NavPanelGroupLabelProps } from './NavPanelGrou
|
|
|
8
8
|
export { NavPanelHeader, type NavPanelHeaderProps } from './NavPanelHeader';
|
|
9
9
|
export { NavPanelItem, type NavPanelItemProps } from './NavPanelItem';
|
|
10
10
|
export { NavPanelSectionHeader, type NavPanelSectionHeaderProps } from './NavPanelSectionHeader';
|
|
11
|
+
export { NavPanelSkeleton, type NavPanelSkeletonProps } from './NavPanelSkeleton';
|
|
@@ -8,4 +8,5 @@ import { NavPanelGroupLabel } from "./NavPanelGroupLabel.js";
|
|
|
8
8
|
import { NavPanelHeader } from "./NavPanelHeader.js";
|
|
9
9
|
import { NavPanelItem } from "./NavPanelItem.js";
|
|
10
10
|
import { NavPanelSectionHeader } from "./NavPanelSectionHeader.js";
|
|
11
|
-
|
|
11
|
+
import { NavPanelSkeleton } from "./NavPanelSkeleton.js";
|
|
12
|
+
export { NavPanel, NavPanelBack, NavPanelDivider, NavPanelGroup, NavPanelGroupContent, NavPanelGroupItem, NavPanelGroupLabel, NavPanelHeader, NavPanelItem, NavPanelSectionHeader, NavPanelSkeleton };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../utils/cn.js";
|
|
3
|
+
import { useTestId } from "../../utils/testId.js";
|
|
4
|
+
import { Skeleton } from "../Skeleton/index.js";
|
|
5
|
+
const NavRailSkeleton = ({ ref, className, count = 4, ...props })=>{
|
|
6
|
+
const testId = useTestId('skeleton');
|
|
7
|
+
return /*#__PURE__*/ jsx("div", {
|
|
8
|
+
...props,
|
|
9
|
+
ref: ref,
|
|
10
|
+
"data-slot": "nav-rail-skeleton",
|
|
11
|
+
"data-testid": testId,
|
|
12
|
+
className: cn('flex flex-col gap-6', className),
|
|
13
|
+
children: Array.from({
|
|
14
|
+
length: count
|
|
15
|
+
}, (_, i)=>/*#__PURE__*/ jsx(Skeleton, {
|
|
16
|
+
width: "100%",
|
|
17
|
+
height: "28px",
|
|
18
|
+
rounded: 6
|
|
19
|
+
}, i))
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
NavRailSkeleton.displayName = 'NavRailSkeleton';
|
|
23
|
+
export { NavRailSkeleton };
|
|
@@ -4,3 +4,4 @@ export { useNavRailContext } from './NavRailContext';
|
|
|
4
4
|
export { NavRailFooter, type NavRailFooterProps } from './NavRailFooter';
|
|
5
5
|
export { NavRailItem, type NavRailItemProps } from './NavRailItem';
|
|
6
6
|
export { NavRailSeparator, type NavRailSeparatorProps } from './NavRailSeparator';
|
|
7
|
+
export { NavRailSkeleton, type NavRailSkeletonProps } from './NavRailSkeleton';
|
|
@@ -4,4 +4,5 @@ import { useNavRailContext } from "./NavRailContext.js";
|
|
|
4
4
|
import { NavRailFooter } from "./NavRailFooter.js";
|
|
5
5
|
import { NavRailItem } from "./NavRailItem.js";
|
|
6
6
|
import { NavRailSeparator } from "./NavRailSeparator.js";
|
|
7
|
-
|
|
7
|
+
import { NavRailSkeleton } from "./NavRailSkeleton.js";
|
|
8
|
+
export { NavRail, NavRailBody, NavRailFooter, NavRailItem, NavRailSeparator, NavRailSkeleton, useNavRailContext };
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { memo, useCallback, useMemo } from "react";
|
|
2
|
+
import { memo, useCallback, useEffect, useMemo, useRef } from "react";
|
|
3
3
|
import { useOverflowItems } from "../../hooks/index.js";
|
|
4
4
|
import { cn } from "../../utils/cn.js";
|
|
5
5
|
const OverflowListComponent = ({ items, itemRenderer, overflowRenderer, className, collapseFrom = 'end', minVisibleItems = 0, alwaysRenderOverflow = false, onOverflow, ...props })=>{
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
6
|
+
const indexMap = useMemo(()=>{
|
|
7
|
+
const map = new Map();
|
|
8
|
+
items.forEach((item, index)=>{
|
|
9
|
+
if (!map.has(item)) map.set(item, index);
|
|
10
|
+
});
|
|
11
|
+
return map;
|
|
9
12
|
}, [
|
|
10
|
-
items
|
|
13
|
+
items
|
|
14
|
+
]);
|
|
15
|
+
const memoizedItemRenderer = useCallback((item)=>itemRenderer(item, indexMap.get(item) ?? 0), [
|
|
16
|
+
indexMap,
|
|
11
17
|
itemRenderer
|
|
12
18
|
]);
|
|
13
|
-
const memoizedMeasurementRenderer = useCallback((item)=>
|
|
14
|
-
|
|
15
|
-
return itemRenderer(item, index);
|
|
16
|
-
}, [
|
|
17
|
-
items,
|
|
19
|
+
const memoizedMeasurementRenderer = useCallback((item)=>itemRenderer(item, indexMap.get(item) ?? 0), [
|
|
20
|
+
indexMap,
|
|
18
21
|
itemRenderer
|
|
19
22
|
]);
|
|
20
23
|
const { containerRef, visibleItems, hiddenItems, MeasurementContainer } = useOverflowItems({
|
|
@@ -41,8 +44,16 @@ const OverflowListComponent = ({ items, itemRenderer, overflowRenderer, classNam
|
|
|
41
44
|
hiddenItems
|
|
42
45
|
]);
|
|
43
46
|
const finalHiddenCount = finalHiddenItems.length;
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
const prevHiddenRef = useRef(null);
|
|
48
|
+
useEffect(()=>{
|
|
49
|
+
if (!onOverflow || 0 === finalHiddenItems.length) {
|
|
50
|
+
prevHiddenRef.current = finalHiddenItems;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const prev = prevHiddenRef.current;
|
|
54
|
+
const changed = !prev || prev.length !== finalHiddenItems.length || finalHiddenItems.some((item, index)=>item !== prev[index]);
|
|
55
|
+
if (changed) onOverflow(finalHiddenItems);
|
|
56
|
+
prevHiddenRef.current = finalHiddenItems;
|
|
46
57
|
}, [
|
|
47
58
|
finalHiddenItems,
|
|
48
59
|
onOverflow
|
|
@@ -65,6 +76,7 @@ const OverflowListComponent = ({ items, itemRenderer, overflowRenderer, classNam
|
|
|
65
76
|
/*#__PURE__*/ jsx(MeasurementContainer, {}),
|
|
66
77
|
/*#__PURE__*/ jsxs("div", {
|
|
67
78
|
ref: containerRef,
|
|
79
|
+
"data-slot": "overflow-list",
|
|
68
80
|
className: cn('flex w-full min-w-0', className),
|
|
69
81
|
...props,
|
|
70
82
|
children: [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NavPanelSkeleton } from "../NavPanel/index.js";
|
|
1
2
|
import { matchNav } from "./matchNav.js";
|
|
2
3
|
import { findDrillNode, findFirstLinkPath } from "./navUtils.js";
|
|
3
4
|
import { ProductNav } from "./ProductNav.js";
|
|
@@ -6,4 +7,4 @@ import { useProductNavContext } from "./ProductNavContext.js";
|
|
|
6
7
|
import { ProductNavPanel } from "./ProductNavPanel.js";
|
|
7
8
|
import { pushPathname, useLocationPathname } from "./useLocationPathname.js";
|
|
8
9
|
import { useProductNav } from "./useProductNav.js";
|
|
9
|
-
export { ProductNav, ProductNavBreadcrumbs, ProductNavPanel, findDrillNode, findFirstLinkPath, matchNav, pushPathname, useLocationPathname, useProductNav, useProductNavContext };
|
|
10
|
+
export { NavPanelSkeleton, ProductNav, ProductNavBreadcrumbs, ProductNavPanel, findDrillNode, findFirstLinkPath, matchNav, pushPathname, useLocationPathname, useProductNav, useProductNavContext };
|
|
@@ -20,7 +20,10 @@ const securityEvents = [
|
|
|
20
20
|
objectName: 'Rate limiting abuse on the payment endpoint',
|
|
21
21
|
tags: [
|
|
22
22
|
'api-abuse',
|
|
23
|
-
'account-takeover'
|
|
23
|
+
'account-takeover',
|
|
24
|
+
'credential-stuffing',
|
|
25
|
+
'scanner',
|
|
26
|
+
'brute-force'
|
|
24
27
|
],
|
|
25
28
|
isActive: false,
|
|
26
29
|
requests: 22000,
|
|
@@ -40,7 +43,10 @@ const securityEvents = [
|
|
|
40
43
|
objectName: 'Mass assignment vulnerability in user profile',
|
|
41
44
|
tags: [
|
|
42
45
|
'api-abuse',
|
|
43
|
-
'account-takeover'
|
|
46
|
+
'account-takeover',
|
|
47
|
+
'credential-stuffing',
|
|
48
|
+
'scanner',
|
|
49
|
+
'brute-force'
|
|
44
50
|
],
|
|
45
51
|
isActive: false,
|
|
46
52
|
requests: 25000,
|
|
@@ -60,7 +66,10 @@ const securityEvents = [
|
|
|
60
66
|
objectName: 'Insecure direct object reference in user data',
|
|
61
67
|
tags: [
|
|
62
68
|
'api-abuse',
|
|
63
|
-
'account-takeover'
|
|
69
|
+
'account-takeover',
|
|
70
|
+
'credential-stuffing',
|
|
71
|
+
'scanner',
|
|
72
|
+
'brute-force'
|
|
64
73
|
],
|
|
65
74
|
isActive: true,
|
|
66
75
|
requests: 30000,
|
|
@@ -80,7 +89,10 @@ const securityEvents = [
|
|
|
80
89
|
objectName: 'Improper error handling leading to info leak',
|
|
81
90
|
tags: [
|
|
82
91
|
'api-abuse',
|
|
83
|
-
'account-takeover'
|
|
92
|
+
'account-takeover',
|
|
93
|
+
'credential-stuffing',
|
|
94
|
+
'scanner',
|
|
95
|
+
'brute-force'
|
|
84
96
|
],
|
|
85
97
|
isActive: true,
|
|
86
98
|
requests: 35000,
|
|
@@ -100,7 +112,10 @@ const securityEvents = [
|
|
|
100
112
|
objectName: 'Broken authentication in the login API',
|
|
101
113
|
tags: [
|
|
102
114
|
'api-abuse',
|
|
103
|
-
'account-takeover'
|
|
115
|
+
'account-takeover',
|
|
116
|
+
'credential-stuffing',
|
|
117
|
+
'scanner',
|
|
118
|
+
'brute-force'
|
|
104
119
|
],
|
|
105
120
|
isActive: true,
|
|
106
121
|
requests: 20000,
|
|
@@ -120,7 +135,10 @@ const securityEvents = [
|
|
|
120
135
|
objectName: 'Lack of resource validation in file upload',
|
|
121
136
|
tags: [
|
|
122
137
|
'api-abuse',
|
|
123
|
-
'account-takeover'
|
|
138
|
+
'account-takeover',
|
|
139
|
+
'credential-stuffing',
|
|
140
|
+
'scanner',
|
|
141
|
+
'brute-force'
|
|
124
142
|
],
|
|
125
143
|
isActive: false,
|
|
126
144
|
requests: 40000,
|
|
@@ -140,7 +158,10 @@ const securityEvents = [
|
|
|
140
158
|
objectName: 'Server-side request forgery in image proxy',
|
|
141
159
|
tags: [
|
|
142
160
|
'api-abuse',
|
|
143
|
-
'account-takeover'
|
|
161
|
+
'account-takeover',
|
|
162
|
+
'credential-stuffing',
|
|
163
|
+
'scanner',
|
|
164
|
+
'brute-force'
|
|
144
165
|
],
|
|
145
166
|
isActive: false,
|
|
146
167
|
requests: 50000,
|
|
@@ -160,7 +181,10 @@ const securityEvents = [
|
|
|
160
181
|
objectName: 'Unvalidated redirects and forwards in auth flow',
|
|
161
182
|
tags: [
|
|
162
183
|
'api-abuse',
|
|
163
|
-
'account-takeover'
|
|
184
|
+
'account-takeover',
|
|
185
|
+
'credential-stuffing',
|
|
186
|
+
'scanner',
|
|
187
|
+
'brute-force'
|
|
164
188
|
],
|
|
165
189
|
isActive: true,
|
|
166
190
|
requests: 75000,
|
|
@@ -180,7 +204,10 @@ const securityEvents = [
|
|
|
180
204
|
objectName: 'SQL injection in the user ID',
|
|
181
205
|
tags: [
|
|
182
206
|
'api-abuse',
|
|
183
|
-
'account-takeover'
|
|
207
|
+
'account-takeover',
|
|
208
|
+
'credential-stuffing',
|
|
209
|
+
'scanner',
|
|
210
|
+
'brute-force'
|
|
184
211
|
],
|
|
185
212
|
isActive: false,
|
|
186
213
|
requests: 15000,
|
|
@@ -200,7 +227,10 @@ const securityEvents = [
|
|
|
200
227
|
objectName: "Cross-site scripting in the API endpoint",
|
|
201
228
|
tags: [
|
|
202
229
|
'api-abuse',
|
|
203
|
-
'account-takeover'
|
|
230
|
+
'account-takeover',
|
|
231
|
+
'credential-stuffing',
|
|
232
|
+
'scanner',
|
|
233
|
+
'brute-force'
|
|
204
234
|
],
|
|
205
235
|
isActive: false,
|
|
206
236
|
requests: 18000,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface CalculateVisibleCountParams {
|
|
2
|
+
/** Width of each item in source order (px). */
|
|
3
|
+
itemWidths: number[];
|
|
4
|
+
/** Gap between flex children of the container (px). */
|
|
5
|
+
gap: number;
|
|
6
|
+
/** Available width of the container (px). */
|
|
7
|
+
availableWidth: number;
|
|
8
|
+
/** Measured width of the '+N' indicator (px); fallback — reserveSpace. */
|
|
9
|
+
indicatorWidth: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Pure arithmetic: how many leading items fit before an overflow indicator
|
|
13
|
+
* is required. Does not touch the DOM — safe to call on every resize frame.
|
|
14
|
+
*/
|
|
15
|
+
export declare function calculateVisibleCount({ itemWidths, gap, availableWidth, indicatorWidth, }: CalculateVisibleCountParams): number;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
function calculateVisibleCount({ itemWidths, gap, availableWidth, indicatorWidth }) {
|
|
2
|
+
if (0 === itemWidths.length) return 0;
|
|
3
|
+
if (availableWidth <= 0) return itemWidths.length;
|
|
4
|
+
let total = 0;
|
|
5
|
+
for(let i = 0; i < itemWidths.length; i++)total += (itemWidths[i] ?? 0) + (i > 0 ? gap : 0);
|
|
6
|
+
if (total <= availableWidth) return itemWidths.length;
|
|
7
|
+
const maxWidth = availableWidth - indicatorWidth - gap;
|
|
8
|
+
let accumulated = 0;
|
|
9
|
+
let count = 0;
|
|
10
|
+
for(let i = 0; i < itemWidths.length; i++){
|
|
11
|
+
const widthWithGap = (itemWidths[i] ?? 0) + (i > 0 ? gap : 0);
|
|
12
|
+
if (accumulated + widthWithGap <= maxWidth || 0 === i) {
|
|
13
|
+
accumulated += widthWithGap;
|
|
14
|
+
count++;
|
|
15
|
+
} else break;
|
|
16
|
+
}
|
|
17
|
+
return Math.max(count, 1);
|
|
18
|
+
}
|
|
19
|
+
export { calculateVisibleCount };
|