@beydesign/storybook 0.2.12 → 0.2.14
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/stories/Buttons/MainButton.js +2 -2
- package/dist/stories/Buttons/types.d.ts +3 -0
- package/dist/stories/EntityCard/EntityCard.js +2 -1
- package/dist/stories/UI/Menu.js +9 -2
- package/dist/stories/UI/Menu.stories.js +35 -8
- package/dist/stories/UI/TabMenu.d.ts +3 -0
- package/dist/stories/UI/TabMenu.js +85 -0
- package/dist/stories/UI/TabMenu.stories.d.ts +9 -0
- package/dist/stories/UI/TabMenu.stories.js +110 -0
- package/dist/stories/UI/index.d.ts +1 -0
- package/dist/stories/UI/index.js +1 -0
- package/dist/stories/UI/types.d.ts +42 -0
- package/dist/stories/UI/types.js +8 -0
- package/package.json +7 -3
|
@@ -220,7 +220,7 @@ const StyledButton = styled.button `
|
|
|
220
220
|
var(--global-shadows-shadow-xs-color);
|
|
221
221
|
}
|
|
222
222
|
`;
|
|
223
|
-
export const MainButton = ({ onClick, text, disabled = false, loading = false, type = ButtonType.Button, style, size = ButtonSize.xl, hierarchy = ButtonHierarchy.Primary, displayIcon, displayIconMode = ButtonDisplayMode.Default, state = ButtonState.Default, shape = ButtonShape.Square, destructive = false, showLeadingIcon = false, showTrailingIcon = false, leadingIcon = 'Circle', trailingIcon = 'Circle', testId, }) => {
|
|
223
|
+
export const MainButton = ({ onClick, text, disabled = false, loading = false, type = ButtonType.Button, style, size = ButtonSize.xl, hierarchy = ButtonHierarchy.Primary, displayIcon, iconWeight = 'regular', displayIconMode = ButtonDisplayMode.Default, state = ButtonState.Default, shape = ButtonShape.Square, destructive = false, showLeadingIcon = false, showTrailingIcon = false, leadingIcon = 'Circle', trailingIcon = 'Circle', testId, }) => {
|
|
224
224
|
const getIconSize = () => {
|
|
225
225
|
const sizes = {
|
|
226
226
|
[ButtonSize.xl]: 24,
|
|
@@ -282,7 +282,7 @@ export const MainButton = ({ onClick, text, disabled = false, loading = false, t
|
|
|
282
282
|
const iconProps = {
|
|
283
283
|
size: getIconSize(),
|
|
284
284
|
color: disabled ? 'var(--color-text-text-disabled)' : 'currentColor',
|
|
285
|
-
weight:
|
|
285
|
+
weight: iconWeight,
|
|
286
286
|
};
|
|
287
287
|
const sizeStyles = getButtonSize();
|
|
288
288
|
const renderContent = () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CSSProperties } from 'react';
|
|
2
|
+
import type { IconWeight } from '@phosphor-icons/react';
|
|
2
3
|
import { IconName } from '../../utils';
|
|
3
4
|
import { SxProps } from '@mui/material';
|
|
4
5
|
import { Theme } from '@mui/material';
|
|
@@ -86,6 +87,8 @@ export interface MainButtonProps {
|
|
|
86
87
|
hierarchy?: ButtonHierarchy;
|
|
87
88
|
/** Icon to display when in icon mode */
|
|
88
89
|
displayIcon?: IconName;
|
|
90
|
+
/** Weight of the rendered icons (defaults to `regular`) */
|
|
91
|
+
iconWeight?: IconWeight;
|
|
89
92
|
/** How to display the icon */
|
|
90
93
|
displayIconMode?: ButtonDisplayMode;
|
|
91
94
|
/** Current state of the button */
|
|
@@ -17,7 +17,8 @@ export const EntityCard = ({ view = 'avatar', name, image, description = '', sho
|
|
|
17
17
|
const showBadge = showStatusBadge && !!statusBadgeText;
|
|
18
18
|
return (_jsxs(Box, { width: cardWidth, display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', bgcolor: hovered
|
|
19
19
|
? 'var(--color-background-bg-card-highlight-hover)'
|
|
20
|
-
: 'var(--color-background-bg-card-highlight)', border: '1px solid var(--color-border-border-subtle)', boxSizing: 'border-box', overflow: 'hidden', onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), sx: { transition: 'background-color 0.15s ease-in-out' }, "data-testid": `${entityKey}-entity-card`, children: [_jsxs(Box, { position: 'relative', width: '100%', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', overflow: 'hidden', sx: { aspectRatio: imageAspectRatio }, children: [image && !imageError ? (_jsx("img", { src: image, alt: name, width: '100%', height: '100%', style: { objectFit: 'cover', height: '100%', width: '100%' }, onError: () => setImageError(true) })) : (_jsx(Box, { width: '100%', height: '100%', bgcolor: 'var(--color-background-bg-element)', display: 'flex', justifyContent: 'center', alignItems: 'center', children: _jsx(UserCircle, { size: 74, weight: 'thin', color: 'var(--color-text-text-subtle)' }) })), _jsx(Box, { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
|
|
20
|
+
: 'var(--color-background-bg-card-highlight)', border: '1px solid var(--color-border-border-subtle)', boxSizing: 'border-box', overflow: 'hidden', onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), sx: { transition: 'background-color 0.15s ease-in-out' }, "data-testid": `${entityKey}-entity-card`, children: [_jsxs(Box, { position: 'relative', width: '100%', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', overflow: 'hidden', sx: { aspectRatio: imageAspectRatio }, children: [image && !imageError ? (_jsx("img", { src: image, alt: name, width: '100%', height: '100%', style: { objectFit: 'cover', height: '100%', width: '100%' }, onError: () => setImageError(true) })) : (_jsx(Box, { width: '100%', height: '100%', bgcolor: 'var(--color-background-bg-element)', display: 'flex', justifyContent: 'center', alignItems: 'center', children: _jsx(UserCircle, { size: 74, weight: 'thin', color: 'var(--color-text-text-subtle)' }) })), _jsx(Box, { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', sx: {
|
|
21
|
+
backgroundImage: 'linear-gradient(180deg, rgba(21, 0, 73, 0) 0%, rgba(72, 29, 180, 0.152) 16.76%, rgba(32, 13, 82, 0.36) 100%)',
|
|
21
22
|
opacity: hovered ? 1 : 0,
|
|
22
23
|
transition: 'opacity 0.15s ease-in-out',
|
|
23
24
|
pointerEvents: 'none',
|
package/dist/stories/UI/Menu.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Fragment, useState } from 'react';
|
|
|
4
4
|
import { MainButton, ButtonDisplayMode, ButtonHierarchy, ButtonType, ButtonSize, } from '../Buttons';
|
|
5
5
|
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
6
6
|
import { getIconComponent } from '../../utils';
|
|
7
|
-
export const Menu = ({ icon = '
|
|
7
|
+
export const Menu = ({ icon = 'DotsThreeOutline', iconWeight = 'bold', items, style, testKey, size = ButtonSize.xl, hierarchy = ButtonHierarchy.Tertiary, }) => {
|
|
8
8
|
const [anchorEl, setAnchorEl] = useState(null);
|
|
9
9
|
const open = Boolean(anchorEl);
|
|
10
10
|
const handleClick = (event) => {
|
|
@@ -24,7 +24,14 @@ export const Menu = ({ icon = 'DotsThree', items, style, testKey, size = ButtonS
|
|
|
24
24
|
return 'var(--color-text-text-error)';
|
|
25
25
|
return 'var(--color-text-text-title)';
|
|
26
26
|
};
|
|
27
|
-
return (_jsxs(Fragment, { children: [_jsx(MainButton, { displayIcon: icon, onClick: handleClick, type: ButtonType.Button, displayIconMode: ButtonDisplayMode.Only, hierarchy: hierarchy, size: size, style:
|
|
27
|
+
return (_jsxs(Fragment, { children: [_jsx(MainButton, { displayIcon: icon, iconWeight: iconWeight, onClick: handleClick, type: ButtonType.Button, displayIconMode: ButtonDisplayMode.Only, hierarchy: hierarchy, size: size, style: {
|
|
28
|
+
// The menu trigger sits on top of media (cards, images), so it uses a
|
|
29
|
+
// theme-aware translucent overlay (white 30% in light, black 30% in
|
|
30
|
+
// dark) with no shadow. Consumer `style` can still override.
|
|
31
|
+
background: 'var(--color-background-bg-button-overlay)',
|
|
32
|
+
boxShadow: 'none',
|
|
33
|
+
...style,
|
|
34
|
+
}, testId: `${testKey}-menu-button` }), _jsx(MuiMenu, { id: "basic-menu", "data-testid": `${testKey}-menu`, anchorEl: anchorEl, open: open, onClose: handleClose, MenuListProps: {
|
|
28
35
|
'aria-labelledby': 'basic-button',
|
|
29
36
|
style: {
|
|
30
37
|
padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
2
|
import { Menu } from './Menu';
|
|
2
3
|
import { ButtonSize, ButtonHierarchy } from '../Buttons';
|
|
4
|
+
// The Menu trigger uses a theme-aware translucent overlay background
|
|
5
|
+
// (`bg-button-overlay`), so it's designed to sit on top of media. This backdrop
|
|
6
|
+
// mirrors how it appears on cards (EntityCard / AgentCard) — without it, the
|
|
7
|
+
// overlay reads as a faint box against the plain page.
|
|
8
|
+
const mediaBackdrop = 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?fm=jpg&q=60&w=600&ixlib=rb-4.0.3';
|
|
3
9
|
const meta = {
|
|
4
10
|
title: 'Design System/Components/UI/Menu',
|
|
5
11
|
component: Menu,
|
|
@@ -7,16 +13,38 @@ const meta = {
|
|
|
7
13
|
layout: 'centered',
|
|
8
14
|
},
|
|
9
15
|
tags: ['autodocs'],
|
|
16
|
+
decorators: [
|
|
17
|
+
(Story) => (_jsx("div", { style: {
|
|
18
|
+
width: 240,
|
|
19
|
+
height: 160,
|
|
20
|
+
borderRadius: 8,
|
|
21
|
+
overflow: 'hidden',
|
|
22
|
+
backgroundImage: `url(${mediaBackdrop})`,
|
|
23
|
+
backgroundSize: 'cover',
|
|
24
|
+
backgroundPosition: 'center',
|
|
25
|
+
display: 'flex',
|
|
26
|
+
alignItems: 'flex-start',
|
|
27
|
+
justifyContent: 'flex-end',
|
|
28
|
+
padding: 8,
|
|
29
|
+
}, children: _jsx(Story, {}) })),
|
|
30
|
+
],
|
|
10
31
|
argTypes: {
|
|
11
32
|
icon: {
|
|
12
33
|
control: 'select',
|
|
13
|
-
options: ['DotsThree', 'Pencil', 'Trash'],
|
|
14
|
-
|
|
15
|
-
description: 'The icon to display in the menu',
|
|
34
|
+
options: ['DotsThreeOutline', 'DotsThree', 'Pencil', 'Trash'],
|
|
35
|
+
description: 'The icon to display in the menu trigger',
|
|
16
36
|
table: {
|
|
17
|
-
type: {
|
|
18
|
-
|
|
19
|
-
|
|
37
|
+
type: { summary: 'IconName' },
|
|
38
|
+
defaultValue: { summary: 'DotsThreeOutline' },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
iconWeight: {
|
|
42
|
+
control: 'select',
|
|
43
|
+
options: ['thin', 'light', 'regular', 'bold', 'fill', 'duotone'],
|
|
44
|
+
description: 'Weight of the trigger icon',
|
|
45
|
+
table: {
|
|
46
|
+
type: { summary: 'IconWeight' },
|
|
47
|
+
defaultValue: { summary: 'bold' },
|
|
20
48
|
},
|
|
21
49
|
},
|
|
22
50
|
items: {
|
|
@@ -50,7 +78,7 @@ const meta = {
|
|
|
50
78
|
description: 'Hierarchy of the trigger button',
|
|
51
79
|
table: {
|
|
52
80
|
type: { summary: 'ButtonHierarchy' },
|
|
53
|
-
defaultValue: { summary: ButtonHierarchy.
|
|
81
|
+
defaultValue: { summary: ButtonHierarchy.Tertiary },
|
|
54
82
|
},
|
|
55
83
|
},
|
|
56
84
|
},
|
|
@@ -58,7 +86,6 @@ const meta = {
|
|
|
58
86
|
export default meta;
|
|
59
87
|
export const Default = {
|
|
60
88
|
args: {
|
|
61
|
-
icon: 'DotsThree',
|
|
62
89
|
items: [
|
|
63
90
|
{ label: 'Edit', onClick: () => { }, destructive: false, icon: 'Pencil' },
|
|
64
91
|
{ label: 'Delete', onClick: () => { }, destructive: true, icon: 'Trash' },
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useLayoutEffect, useRef, useState } from 'react';
|
|
3
|
+
import { Box } from '@mui/material';
|
|
4
|
+
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
5
|
+
import { TabMenuSize } from './types';
|
|
6
|
+
const SIZE_TOKENS = {
|
|
7
|
+
[TabMenuSize.md]: {
|
|
8
|
+
containerGap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
|
|
9
|
+
containerPadding: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
|
|
10
|
+
tabPaddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)',
|
|
11
|
+
tabRadius: 'var(--spacing-tokens-size-in-px-radius-radius-xs)',
|
|
12
|
+
tabHeight: '40px',
|
|
13
|
+
dividerHeight: '24px',
|
|
14
|
+
textSize: TypographySize.TextM,
|
|
15
|
+
textWeight: TypographyWeight.SemiBold,
|
|
16
|
+
},
|
|
17
|
+
[TabMenuSize.sm]: {
|
|
18
|
+
containerGap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xxs)',
|
|
19
|
+
containerPadding: 'var(--spacing-tokens-size-in-px-spacing-spacing-xxs)',
|
|
20
|
+
tabPaddingY: '5px',
|
|
21
|
+
tabRadius: 'var(--spacing-tokens-size-in-px-radius-radius-sm)',
|
|
22
|
+
tabHeight: 'auto',
|
|
23
|
+
dividerHeight: '20px',
|
|
24
|
+
textSize: TypographySize.TextS,
|
|
25
|
+
textWeight: TypographyWeight.Medium,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
const normalizeTab = (tab) => typeof tab === 'string' ? { label: tab } : tab;
|
|
29
|
+
export const TabMenu = ({ tabs, activeTab = 0, onTabChange, size = TabMenuSize.md, showDividers = true, animated = true, style, tabStyle, testId, }) => {
|
|
30
|
+
const tokens = SIZE_TOKENS[size];
|
|
31
|
+
const containerRef = useRef(null);
|
|
32
|
+
const tabRefs = useRef([]);
|
|
33
|
+
const [indicator, setIndicator] = useState(null);
|
|
34
|
+
useLayoutEffect(() => {
|
|
35
|
+
const measure = () => {
|
|
36
|
+
const container = containerRef.current;
|
|
37
|
+
const tab = tabRefs.current[activeTab];
|
|
38
|
+
if (!container || !tab)
|
|
39
|
+
return;
|
|
40
|
+
const containerRect = container.getBoundingClientRect();
|
|
41
|
+
const tabRect = tab.getBoundingClientRect();
|
|
42
|
+
setIndicator({
|
|
43
|
+
left: tabRect.left - containerRect.left,
|
|
44
|
+
top: tabRect.top - containerRect.top,
|
|
45
|
+
width: tabRect.width,
|
|
46
|
+
height: tabRect.height,
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
measure();
|
|
50
|
+
window.addEventListener('resize', measure);
|
|
51
|
+
return () => window.removeEventListener('resize', measure);
|
|
52
|
+
}, [activeTab, size, showDividers, tabs]);
|
|
53
|
+
return (_jsxs(Box, { ref: containerRef, "data-testid": testId, position: 'relative', display: 'inline-flex', flexDirection: 'row', alignItems: 'center', gap: tokens.containerGap, padding: tokens.containerPadding, bgcolor: 'var(--color-background-bg-component)', border: '1px solid var(--color-border-border-component)', borderRadius: 'var(--spacing-tokens-size-in-px-radius-radius-md)', sx: style, children: [indicator && (_jsx(Box, { "aria-hidden": true, position: 'absolute', bgcolor: 'var(--color-background-bg-brand-transparent)', borderRadius: tokens.tabRadius, sx: {
|
|
54
|
+
left: 0,
|
|
55
|
+
top: 0,
|
|
56
|
+
width: indicator.width,
|
|
57
|
+
height: indicator.height,
|
|
58
|
+
transform: `translate(${indicator.left}px, ${indicator.top}px)`,
|
|
59
|
+
transition: animated
|
|
60
|
+
? 'transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), width 0.25s cubic-bezier(0.4, 0, 0.2, 1)'
|
|
61
|
+
: 'none',
|
|
62
|
+
pointerEvents: 'none',
|
|
63
|
+
zIndex: 0,
|
|
64
|
+
} })), tabs.map((tab, index) => {
|
|
65
|
+
const { label } = normalizeTab(tab);
|
|
66
|
+
const isActive = index === activeTab;
|
|
67
|
+
return (_jsxs(React.Fragment, { children: [showDividers && index > 0 && (_jsx(Box, { width: '1px', height: tokens.dividerHeight, bgcolor: 'var(--color-border-border-divider)', flexShrink: 0, sx: { zIndex: 1 } })), _jsx(Box, { ref: (el) => {
|
|
68
|
+
tabRefs.current[index] = el;
|
|
69
|
+
}, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', position: 'relative', flexShrink: 0, height: tokens.tabHeight, paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-4xl)', paddingY: tokens.tabPaddingY, borderRadius: tokens.tabRadius, onClick: onTabChange ? () => onTabChange(index) : undefined, sx: {
|
|
70
|
+
zIndex: 1,
|
|
71
|
+
cursor: onTabChange ? 'pointer' : 'default',
|
|
72
|
+
'&:hover': onTabChange && !isActive
|
|
73
|
+
? {
|
|
74
|
+
bgcolor: 'var(--color-background-bg-brand-transparent-hover)',
|
|
75
|
+
transition: 'background-color 0.15s ease',
|
|
76
|
+
}
|
|
77
|
+
: undefined,
|
|
78
|
+
...tabStyle,
|
|
79
|
+
}, children: _jsx(Typography, { size: tokens.textSize, weight: tokens.textWeight, color: isActive
|
|
80
|
+
? 'var(--color-text-text-clicable)'
|
|
81
|
+
: 'var(--color-text-text-subtle)', textStyle: {
|
|
82
|
+
transition: animated ? 'color 0.25s ease' : 'none',
|
|
83
|
+
}, children: label }) })] }, index));
|
|
84
|
+
})] }));
|
|
85
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { TabMenu } from './TabMenu';
|
|
3
|
+
declare const meta: Meta<typeof TabMenu>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof TabMenu>;
|
|
6
|
+
export declare const Medium: Story;
|
|
7
|
+
export declare const Small: Story;
|
|
8
|
+
export declare const MediumStatic: Story;
|
|
9
|
+
export declare const SmallStatic: Story;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { TabMenu } from './TabMenu';
|
|
4
|
+
import { TabMenuSize } from './types';
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'Design System/Components/UI/TabMenu',
|
|
7
|
+
component: TabMenu,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
design: {
|
|
11
|
+
type: 'figspec',
|
|
12
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=6516-41431',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
tags: ['autodocs'],
|
|
16
|
+
argTypes: {
|
|
17
|
+
tabs: {
|
|
18
|
+
control: 'object',
|
|
19
|
+
description: 'Tabs to render (labels or { label, value } objects)',
|
|
20
|
+
table: {
|
|
21
|
+
type: { summary: 'string[] | TabMenuTab[]' },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
activeTab: {
|
|
25
|
+
control: 'number',
|
|
26
|
+
description: 'Index of the active tab',
|
|
27
|
+
table: {
|
|
28
|
+
type: { summary: 'number' },
|
|
29
|
+
defaultValue: { summary: '0' },
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
onTabChange: {
|
|
33
|
+
description: 'Called with the index of the clicked tab',
|
|
34
|
+
table: {
|
|
35
|
+
type: { summary: 'function' },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
size: {
|
|
39
|
+
control: 'select',
|
|
40
|
+
options: Object.values(TabMenuSize),
|
|
41
|
+
description: 'Size variant of the tab menu',
|
|
42
|
+
table: {
|
|
43
|
+
type: { summary: 'string' },
|
|
44
|
+
defaultValue: { summary: 'md' },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
showDividers: {
|
|
48
|
+
control: 'boolean',
|
|
49
|
+
description: 'Show the vertical dividers between tabs',
|
|
50
|
+
table: {
|
|
51
|
+
type: { summary: 'boolean' },
|
|
52
|
+
defaultValue: { summary: 'true' },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
animated: {
|
|
56
|
+
control: 'boolean',
|
|
57
|
+
description: 'Animate the active highlight when switching tabs',
|
|
58
|
+
table: {
|
|
59
|
+
type: { summary: 'boolean' },
|
|
60
|
+
defaultValue: { summary: 'true' },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
testId: {
|
|
64
|
+
control: 'text',
|
|
65
|
+
description: 'Test ID of the tab menu',
|
|
66
|
+
table: {
|
|
67
|
+
type: { summary: 'string' },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
export default meta;
|
|
73
|
+
const TABS = ['Tab name', 'Tab name', 'Tab name', 'Tab name', 'Tab name'];
|
|
74
|
+
const InteractiveTabMenu = (args) => {
|
|
75
|
+
const [activeTab, setActiveTab] = useState(args.activeTab ?? 0);
|
|
76
|
+
return _jsx(TabMenu, { ...args, activeTab: activeTab, onTabChange: setActiveTab });
|
|
77
|
+
};
|
|
78
|
+
export const Medium = {
|
|
79
|
+
render: (args) => _jsx(InteractiveTabMenu, { ...args }),
|
|
80
|
+
args: {
|
|
81
|
+
tabs: TABS,
|
|
82
|
+
activeTab: 0,
|
|
83
|
+
size: TabMenuSize.md,
|
|
84
|
+
showDividers: true,
|
|
85
|
+
animated: true,
|
|
86
|
+
testId: 'tab-menu',
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
export const Small = {
|
|
90
|
+
render: (args) => _jsx(InteractiveTabMenu, { ...args }),
|
|
91
|
+
args: {
|
|
92
|
+
...Medium.args,
|
|
93
|
+
size: TabMenuSize.sm,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
export const MediumStatic = {
|
|
97
|
+
render: (args) => _jsx(InteractiveTabMenu, { ...args }),
|
|
98
|
+
args: {
|
|
99
|
+
...Medium.args,
|
|
100
|
+
animated: false,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
export const SmallStatic = {
|
|
104
|
+
render: (args) => _jsx(InteractiveTabMenu, { ...args }),
|
|
105
|
+
args: {
|
|
106
|
+
...Medium.args,
|
|
107
|
+
size: TabMenuSize.sm,
|
|
108
|
+
animated: false,
|
|
109
|
+
},
|
|
110
|
+
};
|
package/dist/stories/UI/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Theme, SxProps, TooltipProps } from '@mui/material';
|
|
2
2
|
import { ReactElement, ReactNode } from 'react';
|
|
3
|
+
import type { IconWeight } from '@phosphor-icons/react';
|
|
3
4
|
import { IconName } from '../../utils';
|
|
4
5
|
import { ButtonSize, ButtonHierarchy } from '../Buttons';
|
|
5
6
|
/**
|
|
@@ -200,6 +201,8 @@ export interface MenuItemProps {
|
|
|
200
201
|
export type MenuProps = {
|
|
201
202
|
/** Icon */
|
|
202
203
|
icon?: IconName;
|
|
204
|
+
/** Weight of the trigger icon (defaults to `bold`) */
|
|
205
|
+
iconWeight?: IconWeight;
|
|
203
206
|
/** Items */
|
|
204
207
|
items?: MenuItemProps[];
|
|
205
208
|
/** Style */
|
|
@@ -444,3 +447,42 @@ export interface StepperProps {
|
|
|
444
447
|
/** Connector width of the stepper */
|
|
445
448
|
connectorWidth?: string | number;
|
|
446
449
|
}
|
|
450
|
+
/**
|
|
451
|
+
* TabMenu size variants
|
|
452
|
+
*/
|
|
453
|
+
export declare enum TabMenuSize {
|
|
454
|
+
md = "md",
|
|
455
|
+
sm = "sm"
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* A single tab within a {@link TabMenu}.
|
|
459
|
+
*/
|
|
460
|
+
export interface TabMenuTab {
|
|
461
|
+
/** Label shown on the tab */
|
|
462
|
+
label: string;
|
|
463
|
+
/** Optional value identifying the tab. Defaults to the label. */
|
|
464
|
+
value?: string;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* TabMenu component props — a bordered segmented control of selectable tabs.
|
|
468
|
+
*/
|
|
469
|
+
export interface TabMenuProps {
|
|
470
|
+
/** Tabs to render. Accepts labels or `{ label, value }` objects. */
|
|
471
|
+
tabs: (string | TabMenuTab)[];
|
|
472
|
+
/** Index of the active tab. Defaults to `0`. */
|
|
473
|
+
activeTab?: number;
|
|
474
|
+
/** Called with the index of the tab that was clicked. */
|
|
475
|
+
onTabChange?: (index: number) => void;
|
|
476
|
+
/** Size variant. Defaults to `Md`. */
|
|
477
|
+
size?: TabMenuSize;
|
|
478
|
+
/** Show the vertical dividers between tabs. Defaults to `true`. */
|
|
479
|
+
showDividers?: boolean;
|
|
480
|
+
/** Animate the active highlight when switching tabs. Defaults to `true`. */
|
|
481
|
+
animated?: boolean;
|
|
482
|
+
/** Custom style of the container. */
|
|
483
|
+
style?: SxProps<Theme>;
|
|
484
|
+
/** Custom style applied to each tab. */
|
|
485
|
+
tabStyle?: SxProps<Theme>;
|
|
486
|
+
/** Test ID of the tab menu. */
|
|
487
|
+
testId?: string;
|
|
488
|
+
}
|
package/dist/stories/UI/types.js
CHANGED
|
@@ -119,3 +119,11 @@ export var FeatureBannerMedia;
|
|
|
119
119
|
FeatureBannerMedia["Image"] = "Image";
|
|
120
120
|
FeatureBannerMedia["None"] = "None";
|
|
121
121
|
})(FeatureBannerMedia || (FeatureBannerMedia = {}));
|
|
122
|
+
/**
|
|
123
|
+
* TabMenu size variants
|
|
124
|
+
*/
|
|
125
|
+
export var TabMenuSize;
|
|
126
|
+
(function (TabMenuSize) {
|
|
127
|
+
TabMenuSize["md"] = "md";
|
|
128
|
+
TabMenuSize["sm"] = "sm";
|
|
129
|
+
})(TabMenuSize || (TabMenuSize = {}));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beydesign/storybook",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.14",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|
|
@@ -35,10 +35,12 @@
|
|
|
35
35
|
"@mui/icons-material": "^5.18.0",
|
|
36
36
|
"@mui/material": "^5.18.0",
|
|
37
37
|
"@phosphor-icons/react": "^2.1.10",
|
|
38
|
-
"react": "^18.3.1",
|
|
39
|
-
"react-dom": "^18.3.1",
|
|
40
38
|
"typescript": "~5.9.3"
|
|
41
39
|
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"react": "^18.3.1 || ^19.0.0",
|
|
42
|
+
"react-dom": "^18.3.1 || ^19.0.0"
|
|
43
|
+
},
|
|
42
44
|
"devDependencies": {
|
|
43
45
|
"@chromatic-com/storybook": "^3.2.7",
|
|
44
46
|
"@storybook/addon-designs": "^8.2.1",
|
|
@@ -66,6 +68,8 @@
|
|
|
66
68
|
"eslint-plugin-storybook": "^0.11.6",
|
|
67
69
|
"postcss": "^8.5.8",
|
|
68
70
|
"prettier": "^3.8.1",
|
|
71
|
+
"react": "^18.3.1",
|
|
72
|
+
"react-dom": "^18.3.1",
|
|
69
73
|
"storybook": "^8.6.17",
|
|
70
74
|
"typescript-eslint": "^7.18.0",
|
|
71
75
|
"vite": "^5.4.21"
|