@beydesign/storybook 0.2.30 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/stories/{Drawer → Navigation/Drawer}/Drawer.d.ts +4 -0
- package/dist/stories/Navigation/Drawer/Drawer.js +81 -0
- package/dist/stories/{Drawer → Navigation/Drawer}/Drawer.stories.js +1 -1
- package/dist/stories/Navigation/Drawer/DrawerBody.d.ts +9 -0
- package/dist/stories/Navigation/Drawer/DrawerBody.js +73 -0
- package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCard.js +3 -3
- package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCard.stories.js +1 -1
- package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCardSkeleton.js +1 -1
- package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCardSkeleton.stories.js +1 -1
- package/dist/stories/{Drawer → Navigation/Drawer}/DrawerNavItem.d.ts +1 -1
- package/dist/stories/Navigation/Drawer/DrawerNavItem.js +22 -0
- package/dist/stories/{Drawer → Navigation/Drawer}/DrawerNavItem.stories.js +1 -1
- package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfile.js +3 -3
- package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfile.stories.js +1 -1
- package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfileSkeleton.js +1 -1
- package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfileSkeleton.stories.js +1 -1
- package/dist/stories/{Drawer → Navigation/Drawer}/index.d.ts +1 -0
- package/dist/stories/{Drawer → Navigation/Drawer}/index.js +1 -0
- package/dist/stories/{Drawer → Navigation/Drawer}/types.d.ts +44 -34
- package/dist/stories/Navigation/Drawer/types.js +7 -0
- package/dist/stories/Navigation/MobileNav.d.ts +15 -0
- package/dist/stories/Navigation/MobileNav.js +97 -0
- package/dist/stories/Navigation/MobileNav.stories.d.ts +25 -0
- package/dist/stories/Navigation/MobileNav.stories.js +65 -0
- package/dist/stories/Navigation/MobileNavBar.js +3 -13
- package/dist/stories/Navigation/MobileNavBar.stories.js +5 -18
- package/dist/stories/Navigation/SettingsPage.stories.d.ts +19 -0
- package/dist/stories/Navigation/SettingsPage.stories.js +75 -0
- package/dist/stories/Navigation/SubNav/SubNav.d.ts +14 -0
- package/dist/stories/Navigation/SubNav/SubNav.js +16 -0
- package/dist/stories/{Settings/SettingsNav.stories.d.ts → Navigation/SubNav/SubNav.stories.d.ts} +3 -3
- package/dist/stories/{Settings/SettingsNav.stories.js → Navigation/SubNav/SubNav.stories.js} +3 -3
- package/dist/stories/Navigation/SubNav/SubNavBody.d.ts +10 -0
- package/dist/stories/Navigation/SubNav/SubNavBody.js +91 -0
- package/dist/stories/Navigation/SubNav/SubNavItem.d.ts +10 -0
- package/dist/stories/Navigation/SubNav/SubNavItem.js +12 -0
- package/dist/stories/Navigation/SubNav/index.d.ts +4 -0
- package/dist/stories/Navigation/SubNav/index.js +4 -0
- package/dist/stories/Navigation/SubNav/types.d.ts +54 -0
- package/dist/stories/Navigation/index.d.ts +3 -0
- package/dist/stories/Navigation/index.js +3 -0
- package/dist/stories/Navigation/internal/NavPanel.d.ts +21 -0
- package/dist/stories/Navigation/internal/NavPanel.js +45 -0
- package/dist/stories/Navigation/internal/NavRow.d.ts +45 -0
- package/dist/stories/Navigation/internal/NavRow.js +79 -0
- package/dist/stories/Navigation/internal/index.d.ts +11 -0
- package/dist/stories/Navigation/internal/index.js +11 -0
- package/dist/stories/Navigation/internal/types.d.ts +71 -0
- package/dist/stories/Navigation/internal/types.js +30 -0
- package/dist/stories/Navigation/navigation.fixtures.d.ts +22 -0
- package/dist/stories/Navigation/navigation.fixtures.js +64 -0
- package/dist/stories/Navigation/types.d.ts +24 -1
- package/package.json +1 -1
- package/dist/stories/Drawer/Drawer.js +0 -163
- package/dist/stories/Drawer/DrawerNavItem.js +0 -87
- package/dist/stories/Drawer/types.js +0 -29
- package/dist/stories/Settings/SettingsNav.d.ts +0 -10
- package/dist/stories/Settings/SettingsNav.js +0 -97
- package/dist/stories/Settings/SettingsNavItem.d.ts +0 -9
- package/dist/stories/Settings/SettingsNavItem.js +0 -75
- package/dist/stories/Settings/SettingsPage.stories.d.ts +0 -11
- package/dist/stories/Settings/SettingsPage.stories.js +0 -93
- package/dist/stories/Settings/index.d.ts +0 -3
- package/dist/stories/Settings/index.js +0 -3
- package/dist/stories/Settings/types.d.ts +0 -68
- /package/dist/stories/{Drawer → Navigation/Drawer}/Drawer.stories.d.ts +0 -0
- /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCard.d.ts +0 -0
- /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCard.stories.d.ts +0 -0
- /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCardSkeleton.d.ts +0 -0
- /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerCreditsCardSkeleton.stories.d.ts +0 -0
- /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerIcons.d.ts +0 -0
- /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerIcons.js +0 -0
- /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerNavItem.stories.d.ts +0 -0
- /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfile.d.ts +0 -0
- /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfile.stories.d.ts +0 -0
- /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfileSkeleton.d.ts +0 -0
- /package/dist/stories/{Drawer → Navigation/Drawer}/DrawerProfileSkeleton.stories.d.ts +0 -0
- /package/dist/stories/{Settings → Navigation/SubNav}/types.js +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { MobileNav } from './MobileNav';
|
|
4
|
+
import { credits, drawerNavItems, profile, settingsSections, } from './navigation.fixtures';
|
|
5
|
+
import { MainButton, ButtonHierarchy, ButtonSize, ButtonDisplayMode, } from '../Buttons';
|
|
6
|
+
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
7
|
+
/**
|
|
8
|
+
* Drives the overlay the way an app does: a trigger button, and `open` state
|
|
9
|
+
* the component reports back through `onOpenChange`.
|
|
10
|
+
*/
|
|
11
|
+
const MobileNavExample = ({ navItems = drawerNavItems, activeKey = 'settings', ...rest }) => {
|
|
12
|
+
const [open, setOpen] = useState(true);
|
|
13
|
+
return (_jsxs("div", { style: {
|
|
14
|
+
height: '100vh',
|
|
15
|
+
padding: 16,
|
|
16
|
+
backgroundColor: 'var(--color-background-bg-page)',
|
|
17
|
+
}, children: [_jsx(MainButton, { text: "Open menu", displayIcon: "List", displayIconMode: ButtonDisplayMode.Default, hierarchy: ButtonHierarchy.Secondary, size: ButtonSize.sm, onClick: () => setOpen(true) }), _jsx("div", { style: { marginTop: 16 }, children: _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: "var(--color-text-text-paragraph)", children: "Tap \"Settings\" in the panel to push the settings sub-nav, then use the back arrow to return." }) }), _jsx(MobileNav, { open: open, onOpenChange: setOpen, navItems: navItems, activeKey: activeKey, credits: credits, profile: profile, ...rest })] }));
|
|
18
|
+
};
|
|
19
|
+
const meta = {
|
|
20
|
+
title: 'Design System/Components/Navigation/MobileNav',
|
|
21
|
+
component: MobileNavExample,
|
|
22
|
+
parameters: {
|
|
23
|
+
layout: 'fullscreen',
|
|
24
|
+
viewport: { defaultViewport: 'mobile1' },
|
|
25
|
+
design: {
|
|
26
|
+
type: 'figspec',
|
|
27
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=7317-23750',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
tags: ['autodocs'],
|
|
31
|
+
};
|
|
32
|
+
export default meta;
|
|
33
|
+
/**
|
|
34
|
+
* The root pane. "Settings" carries a `subNav`, so it renders a trailing caret
|
|
35
|
+
* and pushes instead of navigating.
|
|
36
|
+
*/
|
|
37
|
+
export const Default = {};
|
|
38
|
+
/**
|
|
39
|
+
* Without a `subNav` anywhere in `navItems` there is nothing to drill into, so
|
|
40
|
+
* no item shows a caret and the overlay is a plain single-pane nav.
|
|
41
|
+
*/
|
|
42
|
+
export const WithoutSubNav = {
|
|
43
|
+
args: {
|
|
44
|
+
navItems: drawerNavItems.filter((item) => item.key !== 'settings'),
|
|
45
|
+
activeKey: 'kb',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* A sub-nav is not a settings-only idea — any item can declare one. Here the
|
|
50
|
+
* developer entry drills into a subset of the same sections.
|
|
51
|
+
*/
|
|
52
|
+
export const CustomSubNav = {
|
|
53
|
+
args: {
|
|
54
|
+
activeKey: 'developers',
|
|
55
|
+
navItems: drawerNavItems.map((item) => item.key === 'developers'
|
|
56
|
+
? {
|
|
57
|
+
...item,
|
|
58
|
+
subNav: {
|
|
59
|
+
title: 'Developers',
|
|
60
|
+
sections: settingsSections.filter((section) => section.key === 'developers'),
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
: item),
|
|
64
|
+
},
|
|
65
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import { Box
|
|
3
|
+
import { Box } from '@mui/material';
|
|
4
4
|
import { Logo } from '../UI';
|
|
5
|
-
import {
|
|
5
|
+
import { MobileNav } from './MobileNav';
|
|
6
6
|
import { getIconComponent } from '../../utils';
|
|
7
7
|
const shadowXs = 'var(--global-shadows-shadow-xs-offset-x) ' +
|
|
8
8
|
'var(--global-shadows-shadow-xs-offset-y) ' +
|
|
@@ -29,15 +29,5 @@ export const MobileNavBar = ({ onSearchClick, drawerProps, showSearch = true, lo
|
|
|
29
29
|
if (drawerProps)
|
|
30
30
|
setOpen(true);
|
|
31
31
|
};
|
|
32
|
-
|
|
33
|
-
const navItems = drawerProps?.navItems.map((item) => ({
|
|
34
|
-
...item,
|
|
35
|
-
onClick: () => {
|
|
36
|
-
item.onClick?.();
|
|
37
|
-
closeDrawer();
|
|
38
|
-
},
|
|
39
|
-
}));
|
|
40
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Box, { className: className, sx: style, boxSizing: 'border-box', display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', width: '100%', height: '60px', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl)', borderBottom: '1px solid var(--color-border-border-component)', bgcolor: 'var(--color-background-bg-page)', children: [_jsx(Box, { display: 'flex', alignItems: 'center', sx: { '& svg': { display: 'block', height: '20px', width: 'auto' } }, children: _jsx(Logo, { color: 'var(--color-foreground-fg-brand-logo)', ...logoProps }) }), _jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', children: [showSearch && (_jsx(NavIconButton, { icon: 'MagnifyingGlass', ariaLabel: 'Search', onClick: onSearchClick })), _jsx(NavIconButton, { icon: 'List', ariaLabel: 'Open menu', onClick: handleMenuClick })] })] }), drawerProps && navItems && (_jsx(MuiDrawer, { anchor: 'left', open: open, onClose: closeDrawer, PaperProps: {
|
|
41
|
-
sx: { backgroundColor: 'transparent', boxShadow: 'none' },
|
|
42
|
-
}, children: _jsx(Drawer, { ...drawerProps, navItems: navItems, showToggle: false }) }))] }));
|
|
32
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Box, { className: className, sx: style, boxSizing: 'border-box', display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', width: '100%', height: '60px', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl)', borderBottom: '1px solid var(--color-border-border-component)', bgcolor: 'var(--color-background-bg-page)', children: [_jsx(Box, { display: 'flex', alignItems: 'center', sx: { '& svg': { display: 'block', height: '20px', width: 'auto' } }, children: _jsx(Logo, { color: 'var(--color-foreground-fg-brand-logo)', ...logoProps }) }), _jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', children: [showSearch && (_jsx(NavIconButton, { icon: 'MagnifyingGlass', ariaLabel: 'Search', onClick: onSearchClick })), _jsx(NavIconButton, { icon: 'List', ariaLabel: 'Open menu', onClick: handleMenuClick })] })] }), drawerProps && (_jsx(MobileNav, { open: open, onOpenChange: setOpen, navItems: drawerProps.navItems, activeKey: drawerProps.activeKey, credits: drawerProps.credits, creditsLoading: drawerProps.creditsLoading, profile: drawerProps.profile, profileLoading: drawerProps.profileLoading, logo: drawerProps.logo }))] }));
|
|
43
33
|
};
|
|
@@ -1,23 +1,10 @@
|
|
|
1
1
|
import { MobileNavBar } from './MobileNavBar';
|
|
2
|
-
|
|
3
|
-
{ key: 'home', text: 'Home', icon: 'House' },
|
|
4
|
-
{ key: 'avatars', text: 'Avatars', icon: 'UserFocus' },
|
|
5
|
-
{ key: 'agents', text: 'My Agents', icon: 'Headset' },
|
|
6
|
-
{ key: 'ttv', text: 'Text-to-Video', icon: 'VideoCamera' },
|
|
7
|
-
{ key: 'kb', text: 'Knowledge Base', icon: 'Book' },
|
|
8
|
-
{ key: 'conversations', text: 'Conversations', icon: 'ChatTeardropDots' },
|
|
9
|
-
{ key: 'analytics', text: 'Analytics', icon: 'ChartBar' },
|
|
10
|
-
{ key: 'api', text: 'API keys', icon: 'Key' },
|
|
11
|
-
{ key: 'developers', text: 'Developers', icon: 'BracketsCurly' },
|
|
12
|
-
];
|
|
2
|
+
import { credits, drawerNavItems, profile } from './navigation.fixtures';
|
|
13
3
|
const drawerProps = {
|
|
14
|
-
navItems,
|
|
4
|
+
navItems: drawerNavItems,
|
|
15
5
|
activeKey: 'kb',
|
|
16
|
-
credits
|
|
17
|
-
profile
|
|
18
|
-
name: 'Emma Novak',
|
|
19
|
-
email: 'emma@example.com',
|
|
20
|
-
},
|
|
6
|
+
credits,
|
|
7
|
+
profile,
|
|
21
8
|
};
|
|
22
9
|
const meta = {
|
|
23
10
|
title: 'Design System/Components/Navigation/MobileNavBar',
|
|
@@ -37,7 +24,7 @@ const meta = {
|
|
|
37
24
|
},
|
|
38
25
|
drawerProps: {
|
|
39
26
|
control: false,
|
|
40
|
-
description: 'Drawer config. When provided, the menu button opens the navigation
|
|
27
|
+
description: 'Drawer config. When provided, the menu button opens the navigation as a `MobileNav` overlay. Any nav item carrying a `subNav` (here: Settings) becomes a pushable pane inside it.',
|
|
41
28
|
table: { type: { summary: 'DrawerProps' } },
|
|
42
29
|
},
|
|
43
30
|
showSearch: {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
/**
|
|
3
|
+
* The settings page at both sizes, driven by a single navigation config.
|
|
4
|
+
*
|
|
5
|
+
* Above 640px the primary {@link Drawer} and the {@link SubNav} sit side
|
|
6
|
+
* by side. Below it there is no room for two panels, so `MobileNavBar` slides in
|
|
7
|
+
* one `MobileNav` overlay where the Settings item — carrying that same
|
|
8
|
+
* `sections` array as its `subNav` — pushes the settings pane instead.
|
|
9
|
+
*
|
|
10
|
+
* Resize the preview across 640px to see them swap.
|
|
11
|
+
*/
|
|
12
|
+
declare const SettingsPageExample: () => import("react").JSX.Element;
|
|
13
|
+
declare const meta: Meta<typeof SettingsPageExample>;
|
|
14
|
+
export default meta;
|
|
15
|
+
type Story = StoryObj<typeof SettingsPageExample>;
|
|
16
|
+
/** Two panels side by side. Widen the preview past 640px. */
|
|
17
|
+
export declare const DrawerWithSubNav: Story;
|
|
18
|
+
/** The same config, merged into one overlay. Open the menu, then tap Settings. */
|
|
19
|
+
export declare const Mobile: Story;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useMediaQuery } from '@mui/material';
|
|
4
|
+
import { SubNav } from './SubNav';
|
|
5
|
+
import { Drawer } from './Drawer';
|
|
6
|
+
import { MobileNavBar } from './MobileNavBar';
|
|
7
|
+
import { credits, drawerNavItems, profile, settingsLabels, settingsSections, } from './navigation.fixtures';
|
|
8
|
+
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
9
|
+
/** The repo's de-facto mobile breakpoint (see `UI/PromoBanner.tsx`). */
|
|
10
|
+
const MOBILE_QUERY = '(max-width: 640px)';
|
|
11
|
+
/**
|
|
12
|
+
* The settings page at both sizes, driven by a single navigation config.
|
|
13
|
+
*
|
|
14
|
+
* Above 640px the primary {@link Drawer} and the {@link SubNav} sit side
|
|
15
|
+
* by side. Below it there is no room for two panels, so `MobileNavBar` slides in
|
|
16
|
+
* one `MobileNav` overlay where the Settings item — carrying that same
|
|
17
|
+
* `sections` array as its `subNav` — pushes the settings pane instead.
|
|
18
|
+
*
|
|
19
|
+
* Resize the preview across 640px to see them swap.
|
|
20
|
+
*/
|
|
21
|
+
const SettingsPageExample = () => {
|
|
22
|
+
const isMobile = useMediaQuery(MOBILE_QUERY);
|
|
23
|
+
const [activeSetting, setActiveSetting] = useState('api-keys');
|
|
24
|
+
const sections = settingsSections.map((section) => ({
|
|
25
|
+
...section,
|
|
26
|
+
items: section.items.map((item) => ({
|
|
27
|
+
...item,
|
|
28
|
+
onClick: () => setActiveSetting(item.key ?? item.text),
|
|
29
|
+
})),
|
|
30
|
+
}));
|
|
31
|
+
const navItems = drawerNavItems.map((item) => item.subNav ? { ...item, subNav: { ...item.subNav, sections } } : item);
|
|
32
|
+
const content = (_jsxs("main", { style: { flex: 1, padding: '32px 40px', overflowY: 'auto' }, children: [_jsx(Typography, { size: TypographySize.HeadingM, weight: TypographyWeight.SemiBold, color: "var(--color-text-text-title)", children: settingsLabels[activeSetting] ?? activeSetting }), _jsx("div", { style: { marginTop: 12 }, children: _jsxs(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: "var(--color-text-text-paragraph)", children: ["Content for the \"", settingsLabels[activeSetting] ?? activeSetting, "\" settings page renders here."] }) })] }));
|
|
33
|
+
if (isMobile) {
|
|
34
|
+
return (_jsxs("div", { style: {
|
|
35
|
+
display: 'flex',
|
|
36
|
+
flexDirection: 'column',
|
|
37
|
+
height: '100vh',
|
|
38
|
+
backgroundColor: 'var(--color-background-bg-page)',
|
|
39
|
+
}, children: [_jsx(MobileNavBar, { showSearch: false, drawerProps: {
|
|
40
|
+
navItems,
|
|
41
|
+
activeKey: 'settings',
|
|
42
|
+
credits,
|
|
43
|
+
profile,
|
|
44
|
+
} }), content] }));
|
|
45
|
+
}
|
|
46
|
+
return (_jsxs("div", { style: {
|
|
47
|
+
display: 'flex',
|
|
48
|
+
height: '100vh',
|
|
49
|
+
backgroundColor: 'var(--color-background-bg-page)',
|
|
50
|
+
}, children: [_jsx(Drawer, { navItems: navItems, activeKey: "settings", credits: credits, profile: profile }), _jsx(SubNav, { sections: sections, activeKey: activeSetting }), content] }));
|
|
51
|
+
};
|
|
52
|
+
const meta = {
|
|
53
|
+
title: 'Design System/Examples/Settings Page',
|
|
54
|
+
component: SettingsPageExample,
|
|
55
|
+
parameters: {
|
|
56
|
+
layout: 'fullscreen',
|
|
57
|
+
design: {
|
|
58
|
+
type: 'figspec',
|
|
59
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=7002-33215',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
export default meta;
|
|
64
|
+
/** Two panels side by side. Widen the preview past 640px. */
|
|
65
|
+
export const DrawerWithSubNav = {};
|
|
66
|
+
/** The same config, merged into one overlay. Open the menu, then tap Settings. */
|
|
67
|
+
export const Mobile = {
|
|
68
|
+
parameters: {
|
|
69
|
+
viewport: { defaultViewport: 'mobile1' },
|
|
70
|
+
design: {
|
|
71
|
+
type: 'figspec',
|
|
72
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=7317-23750',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { SubNavProps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Secondary navigation for the Settings area: a "Settings" heading followed by
|
|
5
|
+
* titled sections of {@link SubNavItem} rows. Sits beside the primary
|
|
6
|
+
* {@link Drawer} and only appears on settings pages. Every colour, space and
|
|
7
|
+
* radius resolves to `@beydesign/tokens` CSS variables, so it renders correctly
|
|
8
|
+
* in light and dark.
|
|
9
|
+
*
|
|
10
|
+
* Below the mobile breakpoint there is no room for a second panel — pass the
|
|
11
|
+
* same `sections` as a nav item's `subNav` to `MobileNav` instead, which pushes
|
|
12
|
+
* the identical {@link SubNavBody} as a pane of the one overlay.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SubNav: React.FC<SubNavProps>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { SubNavBody } from './SubNavBody';
|
|
3
|
+
import { NavPanel } from '../internal';
|
|
4
|
+
const WIDTH = '238px';
|
|
5
|
+
/**
|
|
6
|
+
* Secondary navigation for the Settings area: a "Settings" heading followed by
|
|
7
|
+
* titled sections of {@link SubNavItem} rows. Sits beside the primary
|
|
8
|
+
* {@link Drawer} and only appears on settings pages. Every colour, space and
|
|
9
|
+
* radius resolves to `@beydesign/tokens` CSS variables, so it renders correctly
|
|
10
|
+
* in light and dark.
|
|
11
|
+
*
|
|
12
|
+
* Below the mobile breakpoint there is no room for a second panel — pass the
|
|
13
|
+
* same `sections` as a nav item's `subNav` to `MobileNav` instead, which pushes
|
|
14
|
+
* the identical {@link SubNavBody} as a pane of the one overlay.
|
|
15
|
+
*/
|
|
16
|
+
export const SubNav = ({ title, sections, activeKey, onBack, className, style, testId, }) => (_jsx(NavPanel, { as: "aside", "$width": WIDTH, "$height": "100vh", "$borderColor": "var(--color-border-border-component)", className: className, style: style, "data-testid": testId, children: _jsx(SubNavBody, { title: title, sections: sections, activeKey: activeKey, onBack: onBack }) }));
|
package/dist/stories/{Settings/SettingsNav.stories.d.ts → Navigation/SubNav/SubNav.stories.d.ts}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import {
|
|
3
|
-
declare const meta: Meta<typeof
|
|
2
|
+
import { SubNav } from './SubNav';
|
|
3
|
+
declare const meta: Meta<typeof SubNav>;
|
|
4
4
|
export default meta;
|
|
5
|
-
type Story = StoryObj<typeof
|
|
5
|
+
type Story = StoryObj<typeof SubNav>;
|
|
6
6
|
export declare const Default: Story;
|
|
7
7
|
export declare const BrandingActive: Story;
|
package/dist/stories/{Settings/SettingsNav.stories.js → Navigation/SubNav/SubNav.stories.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { SubNav } from './SubNav';
|
|
3
3
|
const sections = [
|
|
4
4
|
{
|
|
5
5
|
key: 'business',
|
|
@@ -33,8 +33,8 @@ const sections = [
|
|
|
33
33
|
},
|
|
34
34
|
];
|
|
35
35
|
const meta = {
|
|
36
|
-
title: 'Design System/Components/
|
|
37
|
-
component:
|
|
36
|
+
title: 'Design System/Components/Navigation/SubNav',
|
|
37
|
+
component: SubNav,
|
|
38
38
|
parameters: {
|
|
39
39
|
layout: 'fullscreen',
|
|
40
40
|
design: {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { SubNavBodyProps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* The settings navigation's content — heading and titled sections of
|
|
5
|
+
* {@link SubNavItem} rows — without any panel chrome. `SubNav` wraps
|
|
6
|
+
* it in a `NavPanel` to sit beside the desktop `Drawer`; `MobileNav` pushes the
|
|
7
|
+
* same body as the second pane of its overlay, where `onBack` renders the
|
|
8
|
+
* back arrow that pops it.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SubNavBody: React.FC<SubNavBodyProps>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import styled from '@emotion/styled';
|
|
3
|
+
import { SubNavItem } from './SubNavItem';
|
|
4
|
+
import { NavItemState } from '../internal';
|
|
5
|
+
import { Typography, TypographySize, TypographyWeight } from '../../Typography';
|
|
6
|
+
import { getIconComponent } from '../../../utils';
|
|
7
|
+
/** Composed from the semantic `shadow-xs` design token. */
|
|
8
|
+
const SHADOW_XS = 'var(--global-shadows-shadow-xs-offset-x) var(--global-shadows-shadow-xs-offset-y) var(--global-shadows-shadow-xs-blur) var(--global-shadows-shadow-xs-spread) var(--global-shadows-shadow-xs-color)';
|
|
9
|
+
const Content = styled.div `
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
gap: var(--spacing-tokens-size-in-px-spacing-spacing-2xl, 20px);
|
|
13
|
+
height: 100%;
|
|
14
|
+
`;
|
|
15
|
+
const Header = styled.div `
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: var(--spacing-tokens-size-in-px-spacing-spacing-md, 8px);
|
|
19
|
+
padding: ${({ $hasBack }) => $hasBack
|
|
20
|
+
? '0'
|
|
21
|
+
: '0 var(--spacing-tokens-size-in-px-spacing-spacing-md, 8px)'};
|
|
22
|
+
`;
|
|
23
|
+
const BackButton = styled.button `
|
|
24
|
+
appearance: none;
|
|
25
|
+
display: inline-flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: center;
|
|
28
|
+
flex-shrink: 0;
|
|
29
|
+
padding: var(--spacing-tokens-size-in-px-spacing-spacing-md, 8px);
|
|
30
|
+
border: none;
|
|
31
|
+
border-radius: var(--spacing-tokens-size-in-px-radius-radius-xs, 4px);
|
|
32
|
+
background-color: transparent;
|
|
33
|
+
color: var(--color-foreground-fg-secondary);
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
filter: drop-shadow(${SHADOW_XS});
|
|
36
|
+
transition: color 0.2s ease-in-out;
|
|
37
|
+
|
|
38
|
+
&:hover {
|
|
39
|
+
color: var(--color-text-text-clicable);
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
const Sections = styled.nav `
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
gap: var(--spacing-tokens-size-in-px-spacing-spacing-2xl, 20px);
|
|
46
|
+
`;
|
|
47
|
+
const Section = styled.div `
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
gap: var(--spacing-tokens-size-in-px-spacing-spacing-md, 8px);
|
|
51
|
+
`;
|
|
52
|
+
const SectionTitle = styled.div `
|
|
53
|
+
padding: 0 var(--spacing-tokens-size-in-px-spacing-spacing-md, 8px);
|
|
54
|
+
text-transform: uppercase;
|
|
55
|
+
`;
|
|
56
|
+
const Items = styled.div `
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
gap: var(--spacing-tokens-size-in-px-spacing-spacing-xs, 4px);
|
|
60
|
+
`;
|
|
61
|
+
const resolveState = (item, activeKey) => {
|
|
62
|
+
if (item.state)
|
|
63
|
+
return item.state;
|
|
64
|
+
if (item.disabled)
|
|
65
|
+
return NavItemState.Disabled;
|
|
66
|
+
const key = item.key ?? item.text;
|
|
67
|
+
if (activeKey !== undefined && key === activeKey) {
|
|
68
|
+
return NavItemState.Active;
|
|
69
|
+
}
|
|
70
|
+
return NavItemState.Inactive;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* The settings navigation's content — heading and titled sections of
|
|
74
|
+
* {@link SubNavItem} rows — without any panel chrome. `SubNav` wraps
|
|
75
|
+
* it in a `NavPanel` to sit beside the desktop `Drawer`; `MobileNav` pushes the
|
|
76
|
+
* same body as the second pane of its overlay, where `onBack` renders the
|
|
77
|
+
* back arrow that pops it.
|
|
78
|
+
*/
|
|
79
|
+
export const SubNavBody = ({ title = 'Settings', sections, activeKey, onBack, }) => {
|
|
80
|
+
const handleItemClick = (item) => {
|
|
81
|
+
if (item.disabled)
|
|
82
|
+
return;
|
|
83
|
+
if (item.link) {
|
|
84
|
+
window.location.href = item.link;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
item.onClick?.();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
return (_jsxs(Content, { children: [_jsxs(Header, { "$hasBack": Boolean(onBack), children: [onBack && (_jsx(BackButton, { type: "button", onClick: onBack, "aria-label": "Back", children: getIconComponent('ArrowLeft', { size: 20 }) })), _jsx(Typography, { size: TypographySize.HeadingS, weight: TypographyWeight.Bold, color: "var(--color-text-text-title)", children: title })] }), _jsx(Sections, { children: sections.map((section, sectionIndex) => (_jsxs(Section, { children: [section.title && (_jsx(SectionTitle, { children: _jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.SemiBold, color: "var(--color-text-text-subtle)", children: section.title }) })), _jsx(Items, { children: section.items.map((item) => (_jsx(SubNavItem, { text: item.text, icon: item.icon, state: resolveState(item, activeKey), showBadge: item.showBadge, badgeText: item.badgeText, onClick: () => handleItemClick(item) }, item.key ?? item.text))) })] }, section.key ?? section.title ?? sectionIndex))) })] }));
|
|
91
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { SubNavItemProps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* A single navigation row for the {@link SubNav}. Deliberately more compact
|
|
5
|
+
* than {@link DrawerNavItem} — 28px tall (4px vertical padding), a 16px icon and
|
|
6
|
+
* a Medium-weight label — to read as secondary navigation. A configuration of
|
|
7
|
+
* the shared {@link NavRow}, so it shares its states, colours and hover
|
|
8
|
+
* behaviour with every other nav row in the system.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SubNavItem: React.FC<SubNavItemProps>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { NavItemState, NavRow } from '../internal';
|
|
3
|
+
import { TypographySize, TypographyWeight } from '../../Typography';
|
|
4
|
+
const PADDING = 'var(--spacing-tokens-size-in-px-spacing-spacing-xs, 4px) var(--spacing-tokens-size-in-px-spacing-spacing-md, 8px)';
|
|
5
|
+
/**
|
|
6
|
+
* A single navigation row for the {@link SubNav}. Deliberately more compact
|
|
7
|
+
* than {@link DrawerNavItem} — 28px tall (4px vertical padding), a 16px icon and
|
|
8
|
+
* a Medium-weight label — to read as secondary navigation. A configuration of
|
|
9
|
+
* the shared {@link NavRow}, so it shares its states, colours and hover
|
|
10
|
+
* behaviour with every other nav row in the system.
|
|
11
|
+
*/
|
|
12
|
+
export const SubNavItem = ({ text, icon, state = NavItemState.Inactive, showBadge = false, badgeText = 'Coming Soon', onClick, className, style, testId, }) => (_jsx(NavRow, { text: text, icon: icon, state: state, iconSize: 16, typographySize: TypographySize.TextS, typographyWeight: TypographyWeight.Medium, padding: PADDING, showBadge: showBadge, badgeText: badgeText, onClick: onClick, className: className, style: style, testId: testId }));
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import type { IconName } from '../../../utils';
|
|
3
|
+
import type { NavItemState, SubNavConfig } from '../internal/types';
|
|
4
|
+
/**
|
|
5
|
+
* The entry, section and config shapes are declared in `../internal/types` so
|
|
6
|
+
* that a `Drawer` nav item can carry a `subNav` without the two folders
|
|
7
|
+
* depending on each other. This is their public home — and it is what lets the
|
|
8
|
+
* very same `sections` array drive both the desktop {@link SubNav} panel and
|
|
9
|
+
* the pane `MobileNav` pushes.
|
|
10
|
+
*/
|
|
11
|
+
export type { SubNavConfig, SubNavItemConfig, SubNavSection, } from '../internal/types';
|
|
12
|
+
/**
|
|
13
|
+
* Props for the standalone {@link SubNavItem} row. Compared to
|
|
14
|
+
* {@link DrawerNavItem} this is a more compact secondary-nav row: 28px tall,
|
|
15
|
+
* 16px icon and Medium-weight label.
|
|
16
|
+
*/
|
|
17
|
+
export interface SubNavItemProps {
|
|
18
|
+
/** Label text. */
|
|
19
|
+
text: string;
|
|
20
|
+
/** Phosphor icon name rendered to the left of the label. */
|
|
21
|
+
icon: IconName;
|
|
22
|
+
/** Visual state of the item. */
|
|
23
|
+
state?: NavItemState;
|
|
24
|
+
/** Show a trailing badge (e.g. "Coming Soon"). */
|
|
25
|
+
showBadge?: boolean;
|
|
26
|
+
/** Text for the trailing badge. */
|
|
27
|
+
badgeText?: string;
|
|
28
|
+
/** Click handler. Not fired when the item is disabled. */
|
|
29
|
+
onClick?: () => void;
|
|
30
|
+
className?: string;
|
|
31
|
+
style?: CSSProperties;
|
|
32
|
+
/** Renders as `data-testid` for testing. */
|
|
33
|
+
testId?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Props for the {@link SubNavBody} — the settings navigation's content
|
|
37
|
+
* without any panel chrome, so it can be pushed as a pane inside the
|
|
38
|
+
* `MobileNav` overlay.
|
|
39
|
+
*/
|
|
40
|
+
export interface SubNavBodyProps extends SubNavConfig {
|
|
41
|
+
/**
|
|
42
|
+
* When provided, a back button is rendered before the title. Used by
|
|
43
|
+
* `MobileNav` to pop back to the root nav; the desktop panel omits it.
|
|
44
|
+
*/
|
|
45
|
+
onBack?: () => void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Props for the composed {@link SubNav} secondary navigation.
|
|
49
|
+
*/
|
|
50
|
+
export interface SubNavProps extends SubNavBodyProps {
|
|
51
|
+
className?: string;
|
|
52
|
+
style?: CSSProperties;
|
|
53
|
+
testId?: string;
|
|
54
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The panel chrome shared by every navigation surface: the `Drawer`, the
|
|
3
|
+
* `SubNav` beside it, and the single overlay panel `MobileNav` slides in.
|
|
4
|
+
* Owns the padding, page-contrast background, right border, scroll behaviour
|
|
5
|
+
* and the theme-aware thin scrollbar, so none of that is repeated per nav.
|
|
6
|
+
*
|
|
7
|
+
* Sizing is left to the caller — `Drawer` fills a shell that animates its own
|
|
8
|
+
* width, `SubNav` is a fixed-width full-height column, and `MobileNav`
|
|
9
|
+
* fills the overlay.
|
|
10
|
+
*/
|
|
11
|
+
export declare const NavPanel: import("@emotion/styled").StyledComponent<{
|
|
12
|
+
theme?: import("@emotion/react").Theme;
|
|
13
|
+
as?: React.ElementType;
|
|
14
|
+
} & {
|
|
15
|
+
/** Panel width. Defaults to filling its parent. */
|
|
16
|
+
$width?: string;
|
|
17
|
+
/** Panel height. Defaults to filling its parent. */
|
|
18
|
+
$height?: string;
|
|
19
|
+
/** Right border colour. Defaults to the subtle border token. */
|
|
20
|
+
$borderColor?: string;
|
|
21
|
+
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import styled from '@emotion/styled';
|
|
2
|
+
/**
|
|
3
|
+
* The panel chrome shared by every navigation surface: the `Drawer`, the
|
|
4
|
+
* `SubNav` beside it, and the single overlay panel `MobileNav` slides in.
|
|
5
|
+
* Owns the padding, page-contrast background, right border, scroll behaviour
|
|
6
|
+
* and the theme-aware thin scrollbar, so none of that is repeated per nav.
|
|
7
|
+
*
|
|
8
|
+
* Sizing is left to the caller — `Drawer` fills a shell that animates its own
|
|
9
|
+
* width, `SubNav` is a fixed-width full-height column, and `MobileNav`
|
|
10
|
+
* fills the overlay.
|
|
11
|
+
*/
|
|
12
|
+
export const NavPanel = styled.div `
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
flex-shrink: 0;
|
|
17
|
+
width: ${({ $width }) => $width ?? '100%'};
|
|
18
|
+
height: ${({ $height }) => $height ?? '100%'};
|
|
19
|
+
padding: var(--spacing-tokens-size-in-px-spacing-spacing-xl, 16px)
|
|
20
|
+
var(--spacing-tokens-size-in-px-spacing-spacing-xl, 16px)
|
|
21
|
+
var(--spacing-tokens-size-in-px-spacing-spacing-2xl, 24px);
|
|
22
|
+
background-color: var(--color-background-bg-page-contrast);
|
|
23
|
+
border-right: 1px solid
|
|
24
|
+
${({ $borderColor }) => $borderColor ?? 'var(--color-border-border-subtle)'};
|
|
25
|
+
overflow-x: hidden;
|
|
26
|
+
overflow-y: auto;
|
|
27
|
+
|
|
28
|
+
/* Theme-aware thin scrollbar */
|
|
29
|
+
scrollbar-width: thin;
|
|
30
|
+
scrollbar-color: var(--color-border-border-component) transparent;
|
|
31
|
+
|
|
32
|
+
&::-webkit-scrollbar {
|
|
33
|
+
width: 6px;
|
|
34
|
+
}
|
|
35
|
+
&::-webkit-scrollbar-track {
|
|
36
|
+
background: transparent;
|
|
37
|
+
}
|
|
38
|
+
&::-webkit-scrollbar-thumb {
|
|
39
|
+
background-color: var(--color-border-border-component);
|
|
40
|
+
border-radius: var(--spacing-tokens-size-in-px-radius-radius-full);
|
|
41
|
+
}
|
|
42
|
+
&::-webkit-scrollbar-thumb:hover {
|
|
43
|
+
background-color: var(--color-text-text-subtle);
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import { NavItemState } from './types';
|
|
3
|
+
import { TypographySize, TypographyWeight } from '../../Typography';
|
|
4
|
+
import { IconName } from '../../../utils';
|
|
5
|
+
export interface NavRowProps {
|
|
6
|
+
/** Label text. Hidden (and used as the hover tooltip) when collapsed. */
|
|
7
|
+
text: string;
|
|
8
|
+
/** Phosphor icon name rendered to the left of the label. */
|
|
9
|
+
icon: IconName;
|
|
10
|
+
/** Visual state of the row. */
|
|
11
|
+
state?: NavItemState;
|
|
12
|
+
/** Icon-only 36px square row. */
|
|
13
|
+
collapsed?: boolean;
|
|
14
|
+
/** Size of the leading icon in px. */
|
|
15
|
+
iconSize?: number;
|
|
16
|
+
/** Type scale of the label. */
|
|
17
|
+
typographySize?: TypographySize;
|
|
18
|
+
/** Weight of the label. */
|
|
19
|
+
typographyWeight?: TypographyWeight;
|
|
20
|
+
/** Row padding when expanded. */
|
|
21
|
+
padding?: string;
|
|
22
|
+
/** Show a trailing badge (e.g. "Coming Soon"). Hidden when collapsed. */
|
|
23
|
+
showBadge?: boolean;
|
|
24
|
+
/** Text for the trailing badge. */
|
|
25
|
+
badgeText?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Rendered at the very end of the row, after the label and badge — e.g. the
|
|
28
|
+
* caret that marks an item as opening a sub-navigation. Hidden when collapsed.
|
|
29
|
+
*/
|
|
30
|
+
trailing?: ReactNode;
|
|
31
|
+
/** Click handler. Not fired when the row is disabled. */
|
|
32
|
+
onClick?: () => void;
|
|
33
|
+
className?: string;
|
|
34
|
+
style?: CSSProperties;
|
|
35
|
+
/** Renders as `data-testid` for testing. */
|
|
36
|
+
testId?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The single navigation row used by every nav in the system. `DrawerNavItem`
|
|
40
|
+
* and `SubNavItem` are thin configurations of this component — they differ
|
|
41
|
+
* only in padding, icon size and type scale — so the states, colours, hover
|
|
42
|
+
* behaviour and badge handling live here exactly once. All colours resolve to
|
|
43
|
+
* theme tokens, so it adapts to light and dark.
|
|
44
|
+
*/
|
|
45
|
+
export declare const NavRow: React.FC<NavRowProps>;
|