@beydesign/storybook 0.4.8 → 0.4.10

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.
@@ -29,6 +29,7 @@ const spin = keyframes `
29
29
  const Spinner = () => (_jsx(Box, { display: 'flex', sx: { animation: `${spin} 1.5s linear infinite` }, children: _jsx(ArrowsClockwise, { size: 24, color: 'var(--color-text-text-white)' }) }));
30
30
  export const EntityCard = ({ view = 'avatar', name, image, description = '', showDescription = false, date, aspectRatio, processing = false, processingText = 'Please wait', error = false, errorText = 'Something went wrong', onPreview, previewButtonText, previewIcon = 'Play', showStatusBadge = false, statusBadgeText = '', statusBadgeColor = BadgeColor.Green, menuItems = [], showMenu = false, width, testId, }) => {
31
31
  const [hovered, setHovered] = useState(false);
32
+ const [menuOpen, setMenuOpen] = useState(false);
32
33
  const [imageError, setImageError] = useState(false);
33
34
  const isVideo = view === 'video';
34
35
  const cardWidth = width ?? WIDTH_BY_VIEW[view];
@@ -38,6 +39,7 @@ export const EntityCard = ({ view = 'avatar', name, image, description = '', sho
38
39
  const isProcessing = processing && !error;
39
40
  const canPreview = !!onPreview && !isProcessing && !error;
40
41
  const showActions = canPreview || hasMenu;
42
+ const active = hovered || menuOpen;
41
43
  const showBadge = showStatusBadge && !!statusBadgeText && !error;
42
44
  const showImage = !!image && !imageError && !error;
43
45
  const resolvedTestId = testId ?? `${entityKey}-${isVideo ? 'video' : 'entity'}-card`;
@@ -45,7 +47,7 @@ export const EntityCard = ({ view = 'avatar', name, image, description = '', sho
45
47
  event.stopPropagation();
46
48
  onPreview?.();
47
49
  };
48
- 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
50
+ 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: active
49
51
  ? 'var(--color-background-bg-card-highlight-hover)'
50
52
  : '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), role: canPreview ? 'button' : undefined, tabIndex: canPreview ? 0 : undefined, "aria-label": canPreview ? (previewButtonText ?? name) : undefined, onClick: canPreview ? onPreview : undefined, onKeyDown: canPreview
51
53
  ? (event) => {
@@ -70,18 +72,22 @@ export const EntityCard = ({ view = 'avatar', name, image, description = '', sho
70
72
  : { width: '206px', height: '224px' }),
71
73
  }, children: _jsx(AvatarSilhouette, {}) }) })), _jsx(Box, { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', sx: {
72
74
  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%)',
73
- opacity: hovered && !isProcessing && !error ? 1 : 0,
75
+ opacity: active && !isProcessing && !error ? 1 : 0,
74
76
  transition: 'opacity 0.15s ease-in-out',
75
77
  pointerEvents: 'none',
76
78
  } }), isProcessing && (_jsxs(Box, { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', bgcolor: 'var(--color-background-bg-overlay-2)', sx: { zIndex: 1 }, children: [_jsx(Spinner, {}), _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.SemiBold, color: 'var(--color-text-text-white)', children: processingText })] })), showBadge && (_jsx(Box, { position: 'absolute', top: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', left: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', sx: { zIndex: 2 }, children: _jsx(Badge, { text: statusBadgeText, color: statusBadgeColor, size: BadgeSize.md, showIcon: true, icon: 'Circle', iconWeight: 'fill', iconSize: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)' }) })), showActions && (_jsxs(Box, { className: ACTIONS_CLASS, position: 'absolute', top: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', right: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', sx: {
77
79
  zIndex: 4,
78
80
  transition: 'opacity 0.15s ease-in-out',
79
- '@media (hover: hover)': {
80
- opacity: 0,
81
- pointerEvents: 'none',
82
- },
81
+ ...(menuOpen
82
+ ? { opacity: 1, pointerEvents: 'auto' }
83
+ : {
84
+ '@media (hover: hover)': {
85
+ opacity: 0,
86
+ pointerEvents: 'none',
87
+ },
88
+ }),
83
89
  }, children: [canPreview &&
84
- (previewButtonText ? (_jsx(MainButton, { text: previewButtonText, showLeadingIcon: true, leadingIcon: previewIcon, iconWeight: 'fill', hierarchy: ButtonHierarchy.SecondaryTransparent, size: ButtonSize.sm, onClick: handlePreviewButtonClick, testId: `${entityKey}-preview-button` })) : (_jsx(MainButton, { displayIcon: previewIcon, displayIconMode: ButtonDisplayMode.Only, hierarchy: ButtonHierarchy.SecondaryTransparent, size: ButtonSize.sm, onClick: handlePreviewButtonClick, testId: `${entityKey}-preview-button` }))), hasMenu && (_jsx(Box, { onClick: (event) => event.stopPropagation(), children: _jsx(Menu, { items: menuItems, testKey: entityKey, size: ButtonSize.sm }) }))] })), isVideo ? (_jsxs(Box, { position: 'absolute', left: 0, right: 0, bottom: 0, display: 'flex', flexDirection: 'row', alignItems: 'flex-end', justifyContent: 'space-between', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-3xl) var(--spacing-tokens-size-in-px-spacing-spacing-lg) var(--spacing-tokens-size-in-px-spacing-spacing-lg)', sx: {
90
+ (previewButtonText ? (_jsx(MainButton, { text: previewButtonText, showLeadingIcon: true, leadingIcon: previewIcon, iconWeight: 'fill', hierarchy: ButtonHierarchy.SecondaryTransparent, size: ButtonSize.sm, onClick: handlePreviewButtonClick, testId: `${entityKey}-preview-button` })) : (_jsx(MainButton, { displayIcon: previewIcon, displayIconMode: ButtonDisplayMode.Only, hierarchy: ButtonHierarchy.SecondaryTransparent, size: ButtonSize.sm, onClick: handlePreviewButtonClick, testId: `${entityKey}-preview-button` }))), hasMenu && (_jsx(Box, { onClick: (event) => event.stopPropagation(), children: _jsx(Menu, { items: menuItems, testKey: entityKey, size: ButtonSize.sm, onOpenChange: setMenuOpen }) }))] })), isVideo ? (_jsxs(Box, { position: 'absolute', left: 0, right: 0, bottom: 0, display: 'flex', flexDirection: 'row', alignItems: 'flex-end', justifyContent: 'space-between', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-3xl) var(--spacing-tokens-size-in-px-spacing-spacing-lg) var(--spacing-tokens-size-in-px-spacing-spacing-lg)', sx: {
85
91
  zIndex: 2,
86
92
  pointerEvents: 'none',
87
93
  backgroundImage: 'linear-gradient(rgba(45, 38, 55, 0) 0%, rgba(45, 38, 55, 0.16) 34%, rgba(45, 38, 55, 0.64) 100%)',
@@ -15,6 +15,19 @@ const LogoHeader = styled.div `
15
15
  min-height: 38px;
16
16
  margin-bottom: 40px;
17
17
  `;
18
+ const LogoButton = styled.button `
19
+ display: flex;
20
+ align-items: center;
21
+ padding: 0;
22
+ border: none;
23
+ background: none;
24
+ cursor: pointer;
25
+ &:focus-visible {
26
+ outline: 2px solid var(--color-border-border-component-active);
27
+ outline-offset: 2px;
28
+ border-radius: var(--spacing-tokens-size-in-px-radius-radius-xs, 4px);
29
+ }
30
+ `;
18
31
  const Nav = styled.nav `
19
32
  display: flex;
20
33
  flex-direction: column;
@@ -69,5 +82,5 @@ export const DrawerBody = ({ navItems, activeKey, size = DrawerSize.Expanded, cr
69
82
  item.onClick?.();
70
83
  }
71
84
  };
72
- return (_jsxs(_Fragment, { children: [_jsx(LogoHeader, { "$collapsed": isCollapsed, children: _jsx(Logo, { variant: isCollapsed ? LogoVariant.Icon : LogoVariant.Horizontal, color: logo?.color ?? 'var(--color-foreground-fg-brand-logo)', gradient: logo?.gradient, svgUrl: logo?.svgUrl }) }), _jsxs(Nav, { children: [_jsx(NavTop, { children: navItems.map((item) => (_jsx(DrawerNavItem, { text: item.text, icon: item.icon, state: resolveState(item, activeKey), size: size, hierarchy: item.hierarchy ?? DrawerNavItemHierarchy.First, showBadge: item.showBadge, badgeText: item.badgeText, showSubNavCaret: Boolean(item.subNav && onSubNavOpen), onClick: () => handleItemClick(item) }, item.key ?? item.text))) }), (credits || creditsLoading || profile || profileLoading) && (_jsxs(Footer, { children: [(credits || creditsLoading) && (_jsx(FooterSlot, { children: creditsLoading ? (_jsx(DrawerCreditsCardSkeleton, { size: size })) : (_jsx(DrawerCreditsCard, { ...credits, size: size })) })), (profile || profileLoading) && (_jsx(FooterSlot, { children: profileLoading ? (_jsx(DrawerProfileSkeleton, { size: size })) : (profile && _jsx(DrawerProfile, { ...profile, size: size })) }))] }))] })] }));
85
+ return (_jsxs(_Fragment, { children: [_jsx(LogoHeader, { "$collapsed": isCollapsed, children: logo?.onClick ? (_jsx(LogoButton, { type: "button", onClick: logo.onClick, "aria-label": "Go to home", children: _jsx(Logo, { variant: isCollapsed ? LogoVariant.Icon : LogoVariant.Horizontal, color: logo?.color ?? 'var(--color-foreground-fg-brand-logo)', gradient: logo?.gradient, svgUrl: logo?.svgUrl }) })) : (_jsx(Logo, { variant: isCollapsed ? LogoVariant.Icon : LogoVariant.Horizontal, color: logo?.color ?? 'var(--color-foreground-fg-brand-logo)', gradient: logo?.gradient, svgUrl: logo?.svgUrl })) }), _jsxs(Nav, { children: [_jsx(NavTop, { children: navItems.map((item) => (_jsx(DrawerNavItem, { text: item.text, icon: item.icon, state: resolveState(item, activeKey), size: size, hierarchy: item.hierarchy ?? DrawerNavItemHierarchy.First, showBadge: item.showBadge, badgeText: item.badgeText, showSubNavCaret: Boolean(item.subNav && onSubNavOpen), onClick: () => handleItemClick(item) }, item.key ?? item.text))) }), (credits || creditsLoading || profile || profileLoading) && (_jsxs(Footer, { children: [(credits || creditsLoading) && (_jsx(FooterSlot, { children: creditsLoading ? (_jsx(DrawerCreditsCardSkeleton, { size: size })) : (_jsx(DrawerCreditsCard, { ...credits, size: size })) })), (profile || profileLoading) && (_jsx(FooterSlot, { children: profileLoading ? (_jsx(DrawerProfileSkeleton, { size: size })) : (profile && _jsx(DrawerProfile, { ...profile, size: size })) }))] }))] })] }));
73
86
  };
@@ -172,11 +172,12 @@ export interface DrawerProps {
172
172
  profile?: Omit<DrawerProfileProps, 'size' | 'className' | 'style'>;
173
173
  /** Render the profile footer as a loading skeleton instead of its content. */
174
174
  profileLoading?: boolean;
175
- /** Logo appearance overrides. */
175
+ /** Logo appearance overrides. `onClick` makes the logo an activatable button. */
176
176
  logo?: {
177
177
  color?: string;
178
178
  gradient?: string;
179
179
  svgUrl?: string;
180
+ onClick?: () => void;
180
181
  };
181
182
  className?: string;
182
183
  style?: CSSProperties;
@@ -25,9 +25,27 @@ const NavIconButton = ({ icon, ariaLabel, onClick, }) => (_jsx(Box, { component:
25
25
  }) }));
26
26
  export const MobileNavBar = ({ onSearchClick, drawerProps, showSearch = true, logoProps, style, className, }) => {
27
27
  const [open, setOpen] = useState(false);
28
+ const { onClick: onLogoClick, ...logoStyleProps } = logoProps ?? {};
28
29
  const handleMenuClick = () => {
29
30
  if (drawerProps)
30
31
  setOpen(true);
31
32
  };
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 }))] }));
33
+ 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, { ...(onLogoClick
34
+ ? {
35
+ component: 'button',
36
+ type: 'button',
37
+ onClick: onLogoClick,
38
+ 'aria-label': 'Go to home',
39
+ }
40
+ : {}), display: 'flex', alignItems: 'center', sx: {
41
+ padding: 0,
42
+ border: 'none',
43
+ background: 'none',
44
+ cursor: onLogoClick ? 'pointer' : 'default',
45
+ '& svg': { display: 'block', height: '20px', width: 'auto' },
46
+ '&:focus-visible': {
47
+ outline: '2px solid var(--color-border-border-component-active)',
48
+ outlineOffset: '2px',
49
+ },
50
+ }, children: _jsx(Logo, { color: 'var(--color-foreground-fg-brand-logo)', ...logoStyleProps }) }), _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 }))] }));
33
51
  };
@@ -221,11 +221,12 @@ export interface MobileNavBarProps {
221
221
  drawerProps?: DrawerProps;
222
222
  /** Whether to show the search button */
223
223
  showSearch?: boolean;
224
- /** Logo customization props */
224
+ /** Logo customization props. `onClick` makes the logo an activatable button. */
225
225
  logoProps?: {
226
226
  gradient?: string;
227
227
  color?: string;
228
228
  svgUrl?: string;
229
+ onClick?: () => void;
229
230
  };
230
231
  /** The style object to apply to the component */
231
232
  style?: SxProps<Theme>;
@@ -70,19 +70,19 @@ const LEFT_COLUMNS = [
70
70
  {
71
71
  offset: 0,
72
72
  images: [AVATARS.laura, AVATARS.jerome, AVATARS.yuruo],
73
- marqueeImages: [AVATARS.laura, AVATARS.jerome, AVATARS.yuruo],
73
+ marqueeImages: [AVATARS.jerome, AVATARS.laura, AVATARS.yuruo],
74
74
  },
75
75
  ];
76
76
  const RIGHT_COLUMNS = [
77
77
  {
78
78
  offset: -20,
79
- images: [AVATARS.laura, AVATARS.yuruo, AVATARS.jerome],
80
- marqueeImages: [AVATARS.yuruo, AVATARS.laura, AVATARS.jerome],
79
+ images: [AVATARS.laura, AVATARS.micheal, AVATARS.jerome],
80
+ marqueeImages: [AVATARS.micheal, AVATARS.laura, AVATARS.jerome],
81
81
  },
82
82
  {
83
83
  offset: 0,
84
84
  images: [AVATARS.fjolla, AVATARS.yuruo, AVATARS.nelly],
85
- marqueeImages: [AVATARS.fjolla, AVATARS.yuruo, AVATARS.nelly],
85
+ marqueeImages: [AVATARS.yuruo, AVATARS.fjolla, AVATARS.nelly],
86
86
  },
87
87
  ];
88
88
  export const PromoBanner = ({ title = 'Real-time AI avatars for every conversation.', description = 'Trusted by developers, startups, educators, and enterprises to build engaging AI experiences at scale:', features = [
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@beydesign/storybook",
3
3
  "private": false,
4
- "version": "0.4.8",
4
+ "version": "0.4.10",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",