@beydesign/storybook 0.2.15 → 0.2.17
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 +1 -1
- package/dist/stories/UI/ButtonCard.d.ts +8 -0
- package/dist/stories/UI/ButtonCard.js +35 -0
- package/dist/stories/UI/ButtonCard.stories.d.ts +14 -0
- package/dist/stories/UI/ButtonCard.stories.js +108 -0
- package/dist/stories/UI/ConversationalAvatarsBanner.d.ts +3 -0
- package/dist/stories/UI/ConversationalAvatarsBanner.js +52 -0
- package/dist/stories/UI/ConversationalAvatarsBanner.stories.d.ts +7 -0
- package/dist/stories/UI/ConversationalAvatarsBanner.stories.js +76 -0
- package/dist/stories/UI/ConversationalVoiceAgentsBanner.d.ts +3 -0
- package/dist/stories/UI/ConversationalVoiceAgentsBanner.js +42 -0
- package/dist/stories/UI/ConversationalVoiceAgentsBanner.stories.d.ts +7 -0
- package/dist/stories/UI/ConversationalVoiceAgentsBanner.stories.js +104 -0
- package/dist/stories/UI/PromoBanner.js +65 -9
- package/dist/stories/UI/PromoBanner.stories.d.ts +1 -0
- package/dist/stories/UI/PromoBanner.stories.js +51 -11
- package/dist/stories/UI/bannerAvatars.d.ts +12 -0
- package/dist/stories/UI/bannerAvatars.js +20 -0
- package/dist/stories/UI/index.d.ts +3 -1
- package/dist/stories/UI/index.js +3 -1
- package/dist/stories/UI/types.d.ts +73 -27
- package/dist/stories/UI/types.js +0 -9
- package/package.json +1 -1
- package/dist/stories/UI/FeatureBanner.d.ts +0 -3
- package/dist/stories/UI/FeatureBanner.js +0 -34
- package/dist/stories/UI/FeatureBanner.stories.d.ts +0 -9
- package/dist/stories/UI/FeatureBanner.stories.js +0 -131
|
@@ -90,7 +90,7 @@ const StyledButton = styled.button `
|
|
|
90
90
|
case ButtonHierarchy.TertiaryColor:
|
|
91
91
|
if (props.$state === ButtonState.Disabled || props.$disabled)
|
|
92
92
|
return 'var(--color-text-text-disabled)';
|
|
93
|
-
return 'var(--color-text-text-
|
|
93
|
+
return 'var(--color-text-text-clicable)';
|
|
94
94
|
case ButtonHierarchy.WhiteText:
|
|
95
95
|
if (props.$state === ButtonState.Disabled || props.$disabled)
|
|
96
96
|
return 'var(--color-text-text-inactive)';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ButtonCardProps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* ButtonCard — a compact, clickable card with a leading accent icon, a title,
|
|
5
|
+
* a short description and an optional corner action button. Matches the Figma
|
|
6
|
+
* "Button card" component with its default and hover states.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ButtonCard: React.FC<ButtonCardProps>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from '@mui/material';
|
|
3
|
+
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
4
|
+
import { MainButton, ButtonHierarchy, ButtonSize } from '../Buttons';
|
|
5
|
+
import { getIconComponent } from '../../utils';
|
|
6
|
+
/**
|
|
7
|
+
* ButtonCard — a compact, clickable card with a leading accent icon, a title,
|
|
8
|
+
* a short description and an optional corner action button. Matches the Figma
|
|
9
|
+
* "Button card" component with its default and hover states.
|
|
10
|
+
*/
|
|
11
|
+
export const ButtonCard = ({ title, description, icon = 'UserFocus', iconColor = 'var(--color-foreground-fg-accent-yellow)', iconBackgroundColor = 'var(--color-background-bg-badge-accent-yellow)', onClick, showActionButton = false, actionButtonText = 'Contact Us', onActionClick, disabled = false, width = '294px', style, testId, }) => {
|
|
12
|
+
const cardKey = title?.toLowerCase()?.replace(/\s+/g, '-');
|
|
13
|
+
const isInteractive = !disabled && !!onClick;
|
|
14
|
+
return (_jsxs(Box, { width: width, display: 'flex', alignItems: 'flex-start', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl)', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-4xl)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', bgcolor: 'var(--color-background-bg-component)', boxSizing: 'border-box', position: 'relative', onClick: isInteractive ? onClick : undefined, style: style, "data-testid": testId ?? `${cardKey}-button-card`, sx: {
|
|
15
|
+
opacity: disabled ? 0.5 : 1,
|
|
16
|
+
cursor: isInteractive ? 'pointer' : 'default',
|
|
17
|
+
pointerEvents: disabled ? 'none' : 'auto',
|
|
18
|
+
boxShadow: '0px 2px 4px 0px var(--global-shadows-shadow-xs-color)',
|
|
19
|
+
transition: 'background-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out',
|
|
20
|
+
...(isInteractive && {
|
|
21
|
+
'&:hover': {
|
|
22
|
+
backgroundColor: 'var(--color-background-bg-component-hover)',
|
|
23
|
+
boxShadow: `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)`,
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
26
|
+
}, children: [_jsxs(Box, { display: 'flex', flex: '1 0 0', minWidth: 0, alignItems: 'flex-start', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)', children: [_jsx(Box, { display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, width: '34px', height: '34px', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', bgcolor: iconBackgroundColor,
|
|
27
|
+
// Without border-box the 34px tile grows to 50px (34 + 8px padding
|
|
28
|
+
// each side); Figma sizes the tile at a fixed 34×34. The 24px icon
|
|
29
|
+
// must not shrink to fit the 18px content box — like Figma it stays
|
|
30
|
+
// 24px and overflows the nominal padding, centered in the tile.
|
|
31
|
+
boxSizing: 'border-box', sx: { '& svg': { flexShrink: 0 } }, children: getIconComponent(icon, { size: 24, color: iconColor }) }), _jsxs(Box, { display: 'flex', flex: '1 0 0', minWidth: 0, flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', alignItems: 'flex-start', sx: { wordBreak: 'break-word' }, children: [_jsx(Typography, { size: TypographySize.HeadingS, weight: TypographyWeight.Bold, color: 'var(--color-text-text-title)', textStyle: { textAlign: 'left' }, children: title }), description && (_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Regular, color: 'var(--color-text-text-paragraph)', textStyle: { textAlign: 'left' }, children: description }))] })] }), showActionButton && (_jsx(Box, { position: 'absolute', top: '6px', right: '8px', children: _jsx(MainButton, { text: actionButtonText, hierarchy: ButtonHierarchy.TertiaryColor, size: ButtonSize.xxs, onClick: (e) => {
|
|
32
|
+
e.stopPropagation();
|
|
33
|
+
onActionClick?.();
|
|
34
|
+
}, testId: `${cardKey}-action-button` }) }))] }));
|
|
35
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { ButtonCard } from './ButtonCard';
|
|
3
|
+
declare const meta: Meta<typeof ButtonCard>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof ButtonCard>;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
/**
|
|
8
|
+
* The four cards from the Home "Preview" section, matching the Figma design —
|
|
9
|
+
* each with its own accent color, and "Contact Us" shown on the middle two.
|
|
10
|
+
*/
|
|
11
|
+
export declare const PreviewSection: Story;
|
|
12
|
+
export declare const WithActionButton: Story;
|
|
13
|
+
export declare const SeparatelyClickable: Story;
|
|
14
|
+
export declare const Disabled: Story;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ButtonCard } from './ButtonCard';
|
|
3
|
+
const meta = {
|
|
4
|
+
title: 'Design System/Components/UI/ButtonCard',
|
|
5
|
+
component: ButtonCard,
|
|
6
|
+
parameters: {
|
|
7
|
+
layout: 'centered',
|
|
8
|
+
design: {
|
|
9
|
+
type: 'figspec',
|
|
10
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=5640-2170',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
tags: ['autodocs'],
|
|
14
|
+
argTypes: {
|
|
15
|
+
title: { control: 'text', description: 'Card title' },
|
|
16
|
+
description: { control: 'text', description: 'Supporting description' },
|
|
17
|
+
icon: { control: 'text', description: 'Leading Phosphor icon name' },
|
|
18
|
+
iconColor: { control: 'text', description: 'Icon color' },
|
|
19
|
+
iconBackgroundColor: {
|
|
20
|
+
control: 'text',
|
|
21
|
+
description: 'Background color of the icon tile',
|
|
22
|
+
},
|
|
23
|
+
showActionButton: {
|
|
24
|
+
control: 'boolean',
|
|
25
|
+
description: 'Show the corner action button',
|
|
26
|
+
},
|
|
27
|
+
actionButtonText: {
|
|
28
|
+
control: 'text',
|
|
29
|
+
table: { defaultValue: { summary: 'Contact Us' } },
|
|
30
|
+
},
|
|
31
|
+
disabled: { control: 'boolean' },
|
|
32
|
+
width: { control: 'text', table: { defaultValue: { summary: '294px' } } },
|
|
33
|
+
onClick: { action: 'card clicked' },
|
|
34
|
+
onActionClick: { action: 'action clicked' },
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
export default meta;
|
|
38
|
+
export const Default = {
|
|
39
|
+
args: {
|
|
40
|
+
title: 'Create Personal Avatar',
|
|
41
|
+
description: 'Create your Digital Twin.',
|
|
42
|
+
icon: 'UserFocus',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The four cards from the Home "Preview" section, matching the Figma design —
|
|
47
|
+
* each with its own accent color, and "Contact Us" shown on the middle two.
|
|
48
|
+
*/
|
|
49
|
+
export const PreviewSection = {
|
|
50
|
+
parameters: {
|
|
51
|
+
layout: 'padded',
|
|
52
|
+
design: {
|
|
53
|
+
type: 'figspec',
|
|
54
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=7018-9469',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
render: () => (_jsxs("div", { style: {
|
|
58
|
+
display: 'flex',
|
|
59
|
+
flexWrap: 'wrap',
|
|
60
|
+
gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
|
|
61
|
+
alignItems: 'stretch',
|
|
62
|
+
}, children: [_jsx(ButtonCard, { title: "Create Agent", description: "Build and customize your digital agent.", icon: "Headset", iconColor: "var(--color-foreground-fg-accent-lemon)", iconBackgroundColor: "var(--color-background-bg-badge-accent-brand-lemon)", onClick: () => { } }), _jsx(ButtonCard, { title: "Create Personal Avatar", description: "Request your Digital Twin.", icon: "UserFocus", iconColor: "var(--color-foreground-fg-accent-pink)", iconBackgroundColor: "var(--color-background-bg-badge-accent-pink)", showActionButton: true, actionButtonText: "Contact Us", onClick: () => { }, onActionClick: () => { } }), _jsx(ButtonCard, { title: "Create Videos", description: "Generate videos with your avatars.", icon: "Video", iconColor: "var(--color-foreground-fg-accent-cyan)", iconBackgroundColor: "var(--color-background-bg-badge-accent-cyan)", showActionButton: true, actionButtonText: "Contact Us", onClick: () => { }, onActionClick: () => { } }), _jsx(ButtonCard, { title: "Create API Key", description: "Unlock access to our avatar technology with an API key.", icon: "Key", iconColor: "var(--color-foreground-fg-accent-success)", iconBackgroundColor: "var(--color-background-bg-badge-accent-success)", onClick: () => { } })] })),
|
|
63
|
+
};
|
|
64
|
+
export const WithActionButton = {
|
|
65
|
+
args: {
|
|
66
|
+
...Default.args,
|
|
67
|
+
showActionButton: true,
|
|
68
|
+
actionButtonText: 'Contact Us',
|
|
69
|
+
},
|
|
70
|
+
parameters: {
|
|
71
|
+
docs: {
|
|
72
|
+
description: {
|
|
73
|
+
story: 'A card with a corner action link — e.g. an upsell that links to sales.',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
export const SeparatelyClickable = {
|
|
79
|
+
args: {
|
|
80
|
+
...Default.args,
|
|
81
|
+
showActionButton: true,
|
|
82
|
+
actionButtonText: 'Contact Us',
|
|
83
|
+
},
|
|
84
|
+
parameters: {
|
|
85
|
+
docs: {
|
|
86
|
+
description: {
|
|
87
|
+
story: 'The card body and the "Contact Us" link are independent click targets. ' +
|
|
88
|
+
'Clicking the link fires `onActionClick` only — it stops propagation so the ' +
|
|
89
|
+
'card-level `onClick` does not fire. Open the Actions panel and click each ' +
|
|
90
|
+
'target to see which handler runs.',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
export const Disabled = {
|
|
96
|
+
args: {
|
|
97
|
+
...Default.args,
|
|
98
|
+
showActionButton: true,
|
|
99
|
+
disabled: true,
|
|
100
|
+
},
|
|
101
|
+
parameters: {
|
|
102
|
+
docs: {
|
|
103
|
+
description: {
|
|
104
|
+
story: 'A disabled card is dimmed and blocks all pointer events, including the action link.',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from '@mui/material';
|
|
3
|
+
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
4
|
+
import { MainButton, ButtonHierarchy, ButtonSize } from '../Buttons';
|
|
5
|
+
import { BANNER_AVATARS, AVATAR_TILE_BACKGROUND, AVATAR_TILE_WIDTH, } from './bannerAvatars';
|
|
6
|
+
const DEFAULT_BACKGROUND = 'linear-gradient(180deg, rgba(194, 171, 252, 0.08) 0%, rgba(128, 96, 198, 0.2) 100%)';
|
|
7
|
+
// The avatar column stays on the right at every breakpoint (unlike FeatureBanner
|
|
8
|
+
// which stacks its media), so the banner only reduces its padding/gap on mobile.
|
|
9
|
+
const MOBILE = '@media (max-width: 640px)';
|
|
10
|
+
// Height the card holds so the avatar collage shows the hero tile plus a sliver
|
|
11
|
+
// of the tiles above/below.
|
|
12
|
+
const BANNER_MIN_HEIGHT = 198;
|
|
13
|
+
// Figma tile size — 130×150; the 198px card centers the middle (hero) tile and
|
|
14
|
+
// clips the tiles above/below to slivers.
|
|
15
|
+
const TILE_HEIGHT = 150;
|
|
16
|
+
// On mobile the tighter card can't fit a full tile, so the collage bleeds this
|
|
17
|
+
// many extra pixels past the right edge — the tile's right side (and rounded
|
|
18
|
+
// corner) is clipped, matching the Figma mobile layout.
|
|
19
|
+
const MOBILE_TILE_CUTOFF = 15;
|
|
20
|
+
// Avatars shown in the collage column (reused from the PromoBanner set). The
|
|
21
|
+
// middle tile is the fully-visible hero; the rest are clipped by the height.
|
|
22
|
+
const IMAGE_COLUMN = [
|
|
23
|
+
BANNER_AVATARS.fjolla,
|
|
24
|
+
BANNER_AVATARS.micheal,
|
|
25
|
+
BANNER_AVATARS.yuruo,
|
|
26
|
+
];
|
|
27
|
+
export const ConversationalAvatarsBanner = ({ title, description, actions = [], background = DEFAULT_BACKGROUND, style: customStyle, testId, }) => {
|
|
28
|
+
const renderAction = (action, index) => (_jsx(MainButton, { text: action.text, size: ButtonSize.md, hierarchy: action.primary
|
|
29
|
+
? ButtonHierarchy.Primary
|
|
30
|
+
: ButtonHierarchy.SecondaryColor, onClick: action.onClick, testId: testId ? `${testId}-action-${index}` : undefined }, `${action.text}-${index}`));
|
|
31
|
+
return (_jsxs(Box, { display: "flex", alignItems: "center", gap: "40px", width: "100%", minHeight: `${BANNER_MIN_HEIGHT}px`, overflow: "hidden", borderRadius: "var(--spacing-tokens-size-in-px-radius-radius-lg)", padding: "var(--spacing-tokens-size-in-px-spacing-spacing-3xl) var(--spacing-tokens-size-in-px-spacing-spacing-2xl)", sx: {
|
|
32
|
+
background,
|
|
33
|
+
boxSizing: 'border-box',
|
|
34
|
+
[MOBILE]: {
|
|
35
|
+
gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
|
|
36
|
+
padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl) var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
|
|
37
|
+
},
|
|
38
|
+
...customStyle,
|
|
39
|
+
}, "data-testid": testId, children: [_jsxs(Box, { display: "flex", flexDirection: "column", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-xl)", flex: 1, minWidth: 0, children: [_jsxs(Box, { display: "flex", flexDirection: "column", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", children: [title && (_jsx(Typography, { size: TypographySize.HeadingM, weight: TypographyWeight.Bold, color: "var(--color-text-text-title)", children: title })), description && (_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Regular, color: "var(--color-text-text-label)", children: description }))] }), actions.length > 0 && (_jsx(Box, { display: "flex", alignItems: "center", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", children: actions.map(renderAction) }))] }), _jsx(Box, { flexShrink: 0, alignSelf: "stretch", position: "relative", overflow: "hidden", width: `${AVATAR_TILE_WIDTH}px`, sx: {
|
|
40
|
+
marginY: 'calc(-1 * var(--spacing-tokens-size-in-px-spacing-spacing-3xl))',
|
|
41
|
+
[MOBILE]: {
|
|
42
|
+
marginY: 'calc(-1 * var(--spacing-tokens-size-in-px-spacing-spacing-xl))',
|
|
43
|
+
// Push the strip past the right edge so the tile is clipped there.
|
|
44
|
+
marginRight: `calc(-1 * var(--spacing-tokens-size-in-px-spacing-spacing-lg) - ${MOBILE_TILE_CUTOFF}px)`,
|
|
45
|
+
},
|
|
46
|
+
}, children: _jsx(Box, { display: "flex", flexDirection: "column", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", position: "absolute", left: 0, right: 0, top: "50%", sx: { transform: 'translateY(-50%)' }, children: IMAGE_COLUMN.map((image, index) => (_jsx(Box, { component: "img", src: image, alt: "", width: "100%", height: `${TILE_HEIGHT}px`, flexShrink: 0, sx: {
|
|
47
|
+
objectFit: 'cover',
|
|
48
|
+
borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
|
|
49
|
+
background: AVATAR_TILE_BACKGROUND,
|
|
50
|
+
display: 'block',
|
|
51
|
+
} }, index))) }) })] }));
|
|
52
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { ConversationalAvatarsBanner } from './ConversationalAvatarsBanner';
|
|
3
|
+
declare const meta: Meta<typeof ConversationalAvatarsBanner>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof ConversationalAvatarsBanner>;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const Mobile: Story;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ConversationalAvatarsBanner } from './ConversationalAvatarsBanner';
|
|
2
|
+
const meta = {
|
|
3
|
+
title: 'Design System/Components/UI/ConversationalAvatarsBanner',
|
|
4
|
+
component: ConversationalAvatarsBanner,
|
|
5
|
+
parameters: {
|
|
6
|
+
layout: 'padded',
|
|
7
|
+
design: {
|
|
8
|
+
type: 'figspec',
|
|
9
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=7081-24780',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
argTypes: {
|
|
14
|
+
title: {
|
|
15
|
+
control: 'text',
|
|
16
|
+
description: 'Title of the banner',
|
|
17
|
+
table: { type: { summary: 'string' } },
|
|
18
|
+
},
|
|
19
|
+
description: {
|
|
20
|
+
control: 'text',
|
|
21
|
+
description: 'Description shown below the title',
|
|
22
|
+
table: { type: { summary: 'string' } },
|
|
23
|
+
},
|
|
24
|
+
actions: {
|
|
25
|
+
control: 'object',
|
|
26
|
+
description: 'Action buttons — set primary: true for the brand (violet) button',
|
|
27
|
+
table: { type: { summary: 'BannerAction[]' } },
|
|
28
|
+
},
|
|
29
|
+
background: {
|
|
30
|
+
control: 'text',
|
|
31
|
+
description: 'Background of the banner',
|
|
32
|
+
table: { type: { summary: 'string' } },
|
|
33
|
+
},
|
|
34
|
+
style: {
|
|
35
|
+
control: 'object',
|
|
36
|
+
description: 'Custom style for the banner',
|
|
37
|
+
table: { type: { summary: 'object' } },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
export default meta;
|
|
42
|
+
export const Default = {
|
|
43
|
+
args: {
|
|
44
|
+
title: 'Introducing Conversational Avatars',
|
|
45
|
+
description: 'Bring your ideas to life with intelligent conversational avatars.',
|
|
46
|
+
actions: [{ text: 'Create New Agent' }],
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
const FIGMA_MOBILE_VIEWPORT = {
|
|
50
|
+
viewport: {
|
|
51
|
+
viewports: {
|
|
52
|
+
figmaMobile: {
|
|
53
|
+
name: 'Figma Mobile (375px)',
|
|
54
|
+
styles: { width: '375px', height: '812px' },
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
defaultViewport: 'figmaMobile',
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
export const Mobile = {
|
|
61
|
+
args: {
|
|
62
|
+
...Default.args,
|
|
63
|
+
},
|
|
64
|
+
parameters: {
|
|
65
|
+
layout: 'padded',
|
|
66
|
+
...FIGMA_MOBILE_VIEWPORT,
|
|
67
|
+
docs: {
|
|
68
|
+
description: {
|
|
69
|
+
story: 'The avatar banner on the mobile Home screen — unlike the feature ' +
|
|
70
|
+
'banner it keeps the avatar collage as a narrow strip on the right ' +
|
|
71
|
+
'instead of stacking. Best viewed in the Canvas with the preset ' +
|
|
72
|
+
'mobile viewport.',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from '@mui/material';
|
|
3
|
+
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
4
|
+
import { MainButton, ButtonHierarchy, ButtonSize, ButtonDisplayMode, } from '../Buttons';
|
|
5
|
+
import { BANNER_VIDEO_PREVIEW } from './bannerAvatars';
|
|
6
|
+
const PINK_GRADIENT = 'linear-gradient(90deg, rgba(235, 205, 240, 0.05) 0%, rgba(202, 59, 224, 0.15) 100%)';
|
|
7
|
+
// Below this width the banner stacks vertically to match the Figma mobile
|
|
8
|
+
// layout (text + actions on top, video preview full-width below).
|
|
9
|
+
const MOBILE = '@media (max-width: 640px)';
|
|
10
|
+
// The video preview keeps the Figma 296×150 tile on desktop.
|
|
11
|
+
const PREVIEW_WIDTH = 296;
|
|
12
|
+
const PREVIEW_HEIGHT = 150;
|
|
13
|
+
export const ConversationalVoiceAgentsBanner = ({ title, description, actions = [], previewImage = BANNER_VIDEO_PREVIEW, onPlayClick, previewCtaText, onPreviewCtaClick, background = PINK_GRADIENT, style: customStyle, testId, }) => {
|
|
14
|
+
const renderAction = (action, index) => (_jsx(MainButton, { text: action.text, size: ButtonSize.md, hierarchy: action.primary
|
|
15
|
+
? ButtonHierarchy.Primary
|
|
16
|
+
: ButtonHierarchy.SecondaryColor, onClick: action.onClick, testId: testId ? `${testId}-action-${index}` : undefined }, `${action.text}-${index}`));
|
|
17
|
+
return (_jsxs(Box, { display: "flex", alignItems: "center", gap: "40px", width: "100%", overflow: "hidden", borderRadius: "var(--spacing-tokens-size-in-px-radius-radius-lg)", padding: "var(--spacing-tokens-size-in-px-spacing-spacing-3xl) var(--spacing-tokens-size-in-px-spacing-spacing-2xl)", sx: {
|
|
18
|
+
background,
|
|
19
|
+
boxSizing: 'border-box',
|
|
20
|
+
[MOBILE]: {
|
|
21
|
+
flexDirection: 'column',
|
|
22
|
+
alignItems: 'stretch',
|
|
23
|
+
gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-3xl)',
|
|
24
|
+
padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl) var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
|
|
25
|
+
},
|
|
26
|
+
...customStyle,
|
|
27
|
+
}, "data-testid": testId, children: [_jsxs(Box, { display: "flex", flexDirection: "column", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-xl)", flex: 1, minWidth: 0, children: [_jsxs(Box, { display: "flex", flexDirection: "column", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", children: [title && (_jsx(Typography, { size: TypographySize.HeadingM, weight: TypographyWeight.Bold, color: "var(--color-text-text-title)", children: title })), description && (_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Regular, color: "var(--color-text-text-label)", children: description }))] }), actions.length > 0 && (_jsx(Box, { display: "flex", alignItems: "center", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", sx: {
|
|
28
|
+
// On mobile the two actions split the row into equal-width
|
|
29
|
+
// buttons, matching the Figma mobile layout.
|
|
30
|
+
[MOBILE]: {
|
|
31
|
+
width: '100%',
|
|
32
|
+
'& > *': { flex: 1, minWidth: 0 },
|
|
33
|
+
'& button': { width: '100%' },
|
|
34
|
+
},
|
|
35
|
+
}, children: actions.map(renderAction) }))] }), _jsxs(Box, { position: "relative", flexShrink: 0, overflow: "hidden", width: `${PREVIEW_WIDTH}px`, height: `${PREVIEW_HEIGHT}px`, borderRadius: "var(--spacing-tokens-size-in-px-radius-radius-sm)", sx: {
|
|
36
|
+
[MOBILE]: {
|
|
37
|
+
width: '100%',
|
|
38
|
+
height: 'auto',
|
|
39
|
+
aspectRatio: `${PREVIEW_WIDTH} / ${PREVIEW_HEIGHT}`,
|
|
40
|
+
},
|
|
41
|
+
}, children: [_jsx(Box, { component: "img", src: previewImage, alt: "", width: "100%", height: "100%", sx: { objectFit: 'cover', display: 'block' } }), _jsx(Box, { position: "absolute", left: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", bottom: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", children: _jsx(MainButton, { size: ButtonSize.md, hierarchy: ButtonHierarchy.SecondaryTransparent, displayIconMode: ButtonDisplayMode.Only, displayIcon: "Play", onClick: onPlayClick, style: { border: 'none' }, testId: testId ? `${testId}-play` : undefined }) }), previewCtaText && (_jsx(Box, { position: "absolute", right: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", bottom: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", children: _jsx(MainButton, { text: previewCtaText, size: ButtonSize.md, hierarchy: ButtonHierarchy.SecondaryTransparent, onClick: onPreviewCtaClick, style: { border: 'none' }, testId: testId ? `${testId}-preview-cta` : undefined }) }))] })] }));
|
|
42
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { ConversationalVoiceAgentsBanner } from './ConversationalVoiceAgentsBanner';
|
|
3
|
+
declare const meta: Meta<typeof ConversationalVoiceAgentsBanner>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof ConversationalVoiceAgentsBanner>;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const Mobile: Story;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { ConversationalVoiceAgentsBanner } from './ConversationalVoiceAgentsBanner';
|
|
2
|
+
const meta = {
|
|
3
|
+
title: 'Design System/Components/UI/ConversationalVoiceAgentsBanner',
|
|
4
|
+
component: ConversationalVoiceAgentsBanner,
|
|
5
|
+
parameters: {
|
|
6
|
+
layout: 'padded',
|
|
7
|
+
design: {
|
|
8
|
+
type: 'figspec',
|
|
9
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=6938-20061',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
argTypes: {
|
|
14
|
+
title: {
|
|
15
|
+
control: 'text',
|
|
16
|
+
description: 'Title of the banner',
|
|
17
|
+
table: { type: { summary: 'string' } },
|
|
18
|
+
},
|
|
19
|
+
description: {
|
|
20
|
+
control: 'text',
|
|
21
|
+
description: 'Description shown below the title',
|
|
22
|
+
table: { type: { summary: 'string' } },
|
|
23
|
+
},
|
|
24
|
+
actions: {
|
|
25
|
+
control: 'object',
|
|
26
|
+
description: 'Action buttons — set primary: true for the brand (violet) button',
|
|
27
|
+
table: { type: { summary: 'BannerAction[]' } },
|
|
28
|
+
},
|
|
29
|
+
previewImage: {
|
|
30
|
+
control: 'text',
|
|
31
|
+
description: 'Thumbnail/poster image URL for the video preview',
|
|
32
|
+
table: { type: { summary: 'string' } },
|
|
33
|
+
},
|
|
34
|
+
onPlayClick: {
|
|
35
|
+
action: 'play clicked',
|
|
36
|
+
description: 'Called when the play button on the video preview is clicked',
|
|
37
|
+
table: { type: { summary: 'function' } },
|
|
38
|
+
},
|
|
39
|
+
previewCtaText: {
|
|
40
|
+
control: 'text',
|
|
41
|
+
description: 'CTA text overlaid on the video preview',
|
|
42
|
+
table: { type: { summary: 'string' } },
|
|
43
|
+
},
|
|
44
|
+
onPreviewCtaClick: {
|
|
45
|
+
action: 'preview cta clicked',
|
|
46
|
+
description: 'Called when the preview CTA is clicked',
|
|
47
|
+
table: { type: { summary: 'function' } },
|
|
48
|
+
},
|
|
49
|
+
background: {
|
|
50
|
+
control: 'text',
|
|
51
|
+
description: 'Background of the banner',
|
|
52
|
+
table: { type: { summary: 'string' } },
|
|
53
|
+
},
|
|
54
|
+
style: {
|
|
55
|
+
control: 'object',
|
|
56
|
+
description: 'Custom style for the banner',
|
|
57
|
+
table: { type: { summary: 'object' } },
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
export default meta;
|
|
62
|
+
export const Default = {
|
|
63
|
+
args: {
|
|
64
|
+
title: 'Add a Human Face to your Conversational Voice Agents',
|
|
65
|
+
description: 'Bring your ideas to life with intelligent conversational avatars.',
|
|
66
|
+
actions: [
|
|
67
|
+
{ text: 'Create API Key' },
|
|
68
|
+
{ text: 'Go to Docs', primary: true },
|
|
69
|
+
],
|
|
70
|
+
previewCtaText: 'How to Get Started',
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
const FIGMA_MOBILE_VIEWPORT = {
|
|
74
|
+
viewport: {
|
|
75
|
+
viewports: {
|
|
76
|
+
figmaMobile: {
|
|
77
|
+
name: 'Figma Mobile (375px)',
|
|
78
|
+
styles: { width: '375px', height: '812px' },
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
defaultViewport: 'figmaMobile',
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
export const Mobile = {
|
|
85
|
+
args: {
|
|
86
|
+
...Default.args,
|
|
87
|
+
},
|
|
88
|
+
parameters: {
|
|
89
|
+
layout: 'padded',
|
|
90
|
+
...FIGMA_MOBILE_VIEWPORT,
|
|
91
|
+
design: {
|
|
92
|
+
type: 'figspec',
|
|
93
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=7081-24780',
|
|
94
|
+
},
|
|
95
|
+
docs: {
|
|
96
|
+
description: {
|
|
97
|
+
story: 'The conversational agents banner on the mobile Home screen — text ' +
|
|
98
|
+
'and actions stack on top, the video preview spans full-width below. ' +
|
|
99
|
+
'Below 640px the component reflows automatically; best viewed in the ' +
|
|
100
|
+
'Canvas with the preset mobile viewport.',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -1,34 +1,90 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box } from '@mui/material';
|
|
2
|
+
import { Box, useMediaQuery } from '@mui/material';
|
|
3
3
|
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
4
4
|
import { MainButton, ButtonHierarchy, ButtonSize } from '../Buttons';
|
|
5
|
+
import { BANNER_AVATARS as AVATARS, AVATAR_TILE_BACKGROUND, } from './bannerAvatars';
|
|
5
6
|
const DEFAULT_BACKGROUND = 'linear-gradient(180deg, rgba(194, 171, 252, 0.08) 0%, rgba(128, 96, 198, 0.2) 100%)';
|
|
7
|
+
// Below this width the banner switches to the Figma mobile variant: text
|
|
8
|
+
// centered, features/footer hidden, promo code + CTA stacked, and the mobile*
|
|
9
|
+
// content overrides applied.
|
|
10
|
+
const MOBILE_QUERY = '(max-width: 640px)';
|
|
11
|
+
const MOBILE = `@media ${MOBILE_QUERY}`;
|
|
12
|
+
// The side collages only fit alongside the 458px content column at the Figma
|
|
13
|
+
// frame width (~1122px). Below that they would crush the content and overflow
|
|
14
|
+
// the buttons, so hide them and let the content center on its own.
|
|
15
|
+
const HIDE_COLLAGE = '@media (max-width: 1121px)';
|
|
16
|
+
// Width of the Figma text/content column. Capping the middle content keeps the
|
|
17
|
+
// side collages adjacent to the text on wide (e.g. Full HD) viewports instead
|
|
18
|
+
// of stretching apart; the row stays centered so extra space becomes gutters.
|
|
19
|
+
const CONTENT_MAX_WIDTH = '458px';
|
|
20
|
+
// The middle tile of each column is the fully-visible "hero"; the first and
|
|
21
|
+
// last tiles are clipped to slivers above/below by the banner height. Columns
|
|
22
|
+
// are centered in the banner, then `offset` shifts them vertically (negative =
|
|
23
|
+
// up) so the two columns of each side interlock, matching the Figma layout.
|
|
24
|
+
const LEFT_COLUMNS = [
|
|
25
|
+
{ offset: 0, images: [AVATARS.jerome, AVATARS.micheal, AVATARS.jerome] },
|
|
26
|
+
{ offset: -40, images: [AVATARS.laura, AVATARS.nelly, AVATARS.fjolla] },
|
|
27
|
+
];
|
|
28
|
+
const RIGHT_COLUMNS = [
|
|
29
|
+
{ offset: 0, images: [AVATARS.jerome, AVATARS.fjolla, AVATARS.yuruo] },
|
|
30
|
+
{ offset: -20, images: [AVATARS.micheal, AVATARS.laura, AVATARS.nelly] },
|
|
31
|
+
];
|
|
6
32
|
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 = [
|
|
7
33
|
'Real-Time',
|
|
8
34
|
'Multilingual',
|
|
9
35
|
'API-first',
|
|
10
36
|
'Enterprise-ready',
|
|
11
37
|
'GDPR Compliant',
|
|
12
|
-
], promoCode, promoCodeLabel = 'YOUR PROMO CODE', onCopyPromoCode, ctaText, onCtaClick, footerText,
|
|
38
|
+
], promoCode, promoCodeLabel = 'YOUR PROMO CODE', onCopyPromoCode, ctaText, onCtaClick, footerText, mobileTitle, mobileDescription, mobileCtaText, showImages = true, background = DEFAULT_BACKGROUND, style: customStyle, testId, }) => {
|
|
39
|
+
// Below 640px the banner renders the Figma mobile variant, swapping to any
|
|
40
|
+
// provided mobile* copy and falling back to the desktop content otherwise.
|
|
41
|
+
const isMobile = useMediaQuery(MOBILE_QUERY);
|
|
42
|
+
const resolvedTitle = isMobile && mobileTitle ? mobileTitle : title;
|
|
43
|
+
const resolvedDescription = isMobile && mobileDescription ? mobileDescription : description;
|
|
44
|
+
const resolvedCtaText = isMobile && mobileCtaText ? mobileCtaText : ctaText;
|
|
13
45
|
const handleCopyPromoCode = () => {
|
|
14
46
|
if (!promoCode)
|
|
15
47
|
return;
|
|
16
48
|
navigator.clipboard?.writeText(promoCode);
|
|
17
49
|
onCopyPromoCode?.(promoCode);
|
|
18
50
|
};
|
|
19
|
-
|
|
51
|
+
// Features are part of the desktop-only layout (hidden on the mobile variant).
|
|
52
|
+
const featuresText = !isMobile && features?.length
|
|
20
53
|
? features.map((feature) => `✓ ${feature}`).join(', ') + '.'
|
|
21
54
|
: '';
|
|
22
|
-
const
|
|
23
|
-
if (!showImages
|
|
55
|
+
const renderAvatarMatrix = (columns) => {
|
|
56
|
+
if (!showImages)
|
|
24
57
|
return null;
|
|
25
|
-
return (_jsx(Box, {
|
|
58
|
+
return (_jsx(Box, { display: "flex", alignItems: "center", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-lg)", height: "100%", flexShrink: 0, overflow: "hidden", sx: { [HIDE_COLLAGE]: { display: 'none' } }, children: columns.map((column, columnIndex) => (_jsx(Box, { display: "flex", flexDirection: "column", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", flexShrink: 0, sx: { transform: `translateY(${column.offset}px)` }, children: column.images.map((image, imageIndex) => (_jsx(Box, { component: "img", src: image, alt: "", width: "130px", height: "150px", flexShrink: 0, sx: {
|
|
59
|
+
objectFit: 'cover',
|
|
60
|
+
borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
|
|
61
|
+
background: AVATAR_TILE_BACKGROUND,
|
|
62
|
+
} }, imageIndex))) }, columnIndex))) }));
|
|
26
63
|
};
|
|
27
|
-
return (_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "center", gap: "40px", height: "220px", width: "100%", overflow: "hidden", borderRadius: "var(--spacing-tokens-size-in-px-radius-radius-lg)", paddingX: "var(--spacing-tokens-size-in-px-spacing-spacing-2xl)", sx: {
|
|
28
|
-
|
|
64
|
+
return (_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "center", gap: "40px", height: "220px", width: "100%", overflow: "hidden", borderRadius: "var(--spacing-tokens-size-in-px-radius-radius-lg)", paddingX: "var(--spacing-tokens-size-in-px-spacing-spacing-2xl)", sx: {
|
|
65
|
+
background,
|
|
66
|
+
boxSizing: 'border-box',
|
|
67
|
+
[MOBILE]: {
|
|
68
|
+
height: 'auto',
|
|
69
|
+
gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-3xl)',
|
|
70
|
+
paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
|
|
71
|
+
},
|
|
72
|
+
...customStyle,
|
|
73
|
+
}, "data-testid": testId, children: [renderAvatarMatrix(LEFT_COLUMNS), _jsxs(Box, { display: "flex", flexDirection: "column", alignItems: "center", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-2xl)", flex: 1, minWidth: 0, maxWidth: CONTENT_MAX_WIDTH, paddingY: "var(--spacing-tokens-size-in-px-spacing-spacing-3xl)", children: [_jsxs(Box, { display: "flex", flexDirection: "column", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", width: "100%",
|
|
74
|
+
// Text is centered on the mobile variant (inherited by the children).
|
|
75
|
+
sx: { [MOBILE]: { textAlign: 'center' } }, children: [resolvedTitle && (_jsx(Typography, { size: TypographySize.HeadingM, weight: TypographyWeight.Bold, color: "var(--color-text-text-title)", textStyle: { textAlign: 'center' }, children: resolvedTitle })), (resolvedDescription || featuresText) && (_jsxs(Box, { display: "flex", flexDirection: "column", children: [resolvedDescription && (_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Regular, color: "var(--color-text-text-title)", children: resolvedDescription })), featuresText && (_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Bold, color: "var(--color-text-text-title)", children: featuresText }))] }))] }), (promoCode || resolvedCtaText) && (_jsxs(Box, { display: "flex", justifyContent: "center", alignItems: "stretch", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", sx: {
|
|
76
|
+
// On mobile the promo code + CTA stack and share the wider CTA's
|
|
77
|
+
// width (centered), matching Figma — not stretched full-width.
|
|
78
|
+
[MOBILE]: {
|
|
79
|
+
flexDirection: 'column',
|
|
80
|
+
width: 'fit-content',
|
|
81
|
+
'& button': { width: '100%' },
|
|
82
|
+
},
|
|
83
|
+
}, children: [promoCode && (_jsxs(Box, { position: "relative", display: "flex", children: [_jsx(MainButton, { text: promoCode, size: ButtonSize.md, hierarchy: ButtonHierarchy.SecondaryColor, showTrailingIcon: true, trailingIcon: "Copy", onClick: handleCopyPromoCode, style: {
|
|
84
|
+
border: '1px dashed var(--color-border-border-secondary-action)',
|
|
29
85
|
height: '100%',
|
|
30
86
|
}, testId: testId ? `${testId}-promo-code` : undefined }), promoCodeLabel && (_jsx(Box, { position: "absolute", top: "-13px", left: "-8px", bgcolor: "var(--color-background-bg-icon-badge)", borderRadius: "var(--spacing-tokens-size-in-px-radius-radius-xs)", padding: "var(--spacing-tokens-size-in-px-spacing-spacing-xxs) var(--spacing-tokens-size-in-px-spacing-spacing-sm)", whiteSpace: "nowrap", zIndex: 1, children: _jsx(Typography, { size: TypographySize.Text2XS, weight: TypographyWeight.SemiBold,
|
|
31
87
|
// Invariant violet label (bg-icon-badge) -> pin to the
|
|
32
88
|
// invariant dark text so it stays legible in both themes.
|
|
33
|
-
color: "var(--color-text-text-dark)", children: promoCodeLabel }) }))] })),
|
|
89
|
+
color: "var(--color-text-text-dark)", children: promoCodeLabel }) }))] })), resolvedCtaText && (_jsx(MainButton, { text: resolvedCtaText, size: ButtonSize.md, hierarchy: ButtonHierarchy.Primary, onClick: onCtaClick, testId: testId ? `${testId}-cta` : undefined }))] })), !isMobile && footerText && (_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Regular, color: "var(--color-text-text-title)", textStyle: { width: '100%' }, children: footerText }))] }), renderAvatarMatrix(RIGHT_COLUMNS)] }));
|
|
34
90
|
};
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { PromoBanner } from './PromoBanner';
|
|
2
|
-
import bannerAvatarsLeft from '../assets/banner/banner-avatars-left.png';
|
|
3
|
-
import bannerAvatarsRight from '../assets/banner/banner-avatars-right.png';
|
|
4
2
|
const meta = {
|
|
5
3
|
title: 'Design System/Components/UI/PromoBanner',
|
|
6
4
|
component: PromoBanner,
|
|
@@ -76,23 +74,30 @@ const meta = {
|
|
|
76
74
|
type: { summary: 'string' },
|
|
77
75
|
},
|
|
78
76
|
},
|
|
79
|
-
|
|
77
|
+
mobileTitle: {
|
|
80
78
|
control: 'text',
|
|
81
|
-
description: '
|
|
79
|
+
description: 'Title override used on the mobile variant (< 640px)',
|
|
82
80
|
table: {
|
|
83
81
|
type: { summary: 'string' },
|
|
84
82
|
},
|
|
85
83
|
},
|
|
86
|
-
|
|
84
|
+
mobileDescription: {
|
|
87
85
|
control: 'text',
|
|
88
|
-
description: '
|
|
86
|
+
description: 'Description override used on the mobile variant (< 640px)',
|
|
87
|
+
table: {
|
|
88
|
+
type: { summary: 'string' },
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
mobileCtaText: {
|
|
92
|
+
control: 'text',
|
|
93
|
+
description: 'CTA text override used on the mobile variant (< 640px)',
|
|
89
94
|
table: {
|
|
90
95
|
type: { summary: 'string' },
|
|
91
96
|
},
|
|
92
97
|
},
|
|
93
98
|
showImages: {
|
|
94
99
|
control: 'boolean',
|
|
95
|
-
description: 'Whether to show the avatar
|
|
100
|
+
description: 'Whether to show the avatar collage on both sides',
|
|
96
101
|
table: {
|
|
97
102
|
type: { summary: 'boolean' },
|
|
98
103
|
},
|
|
@@ -129,8 +134,10 @@ export const Default = {
|
|
|
129
134
|
promoCodeLabel: 'YOUR PROMO CODE',
|
|
130
135
|
ctaText: 'Enjoy 1 month free on Starter',
|
|
131
136
|
footerText: 'Use Promo code at checkout and get 90 End-to-End API minutes or 180 Audio-to-Video API minutes free.',
|
|
132
|
-
|
|
133
|
-
|
|
137
|
+
// Below 640px the banner renders the Figma mobile variant with this copy.
|
|
138
|
+
mobileTitle: 'Welcome! Get your free Starter plan for 1 month',
|
|
139
|
+
mobileDescription: "For a limited time, enjoy 1 month free on our starter plan (worth $50). That's 9k credits for 90 End-to-End API minutes or 180 Audio-to-Video API minutes. Just apply the code below at checkout.",
|
|
140
|
+
mobileCtaText: 'Activate My Free Starter Plan',
|
|
134
141
|
},
|
|
135
142
|
};
|
|
136
143
|
export const WithoutImages = {
|
|
@@ -153,7 +160,40 @@ export const TitleAndCtaOnly = {
|
|
|
153
160
|
description: undefined,
|
|
154
161
|
features: [],
|
|
155
162
|
ctaText: 'Get Started',
|
|
156
|
-
|
|
157
|
-
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
const FIGMA_MOBILE_VIEWPORT = {
|
|
166
|
+
viewport: {
|
|
167
|
+
viewports: {
|
|
168
|
+
figmaMobile: {
|
|
169
|
+
name: 'Figma Mobile (375px)',
|
|
170
|
+
styles: { width: '375px', height: '812px' },
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
defaultViewport: 'figmaMobile',
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
export const Mobile = {
|
|
177
|
+
// Same props as Default — below 640px the component swaps to the mobile*
|
|
178
|
+
// content overrides and the Figma mobile layout automatically.
|
|
179
|
+
args: {
|
|
180
|
+
...Default.args,
|
|
181
|
+
},
|
|
182
|
+
parameters: {
|
|
183
|
+
layout: 'padded',
|
|
184
|
+
...FIGMA_MOBILE_VIEWPORT,
|
|
185
|
+
design: {
|
|
186
|
+
type: 'figspec',
|
|
187
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=7080-14298',
|
|
188
|
+
},
|
|
189
|
+
docs: {
|
|
190
|
+
description: {
|
|
191
|
+
story: 'The same PromoBanner as Default, viewed below 640px. The component ' +
|
|
192
|
+
'renders the Figma mobile variant: side collages and features/footer ' +
|
|
193
|
+
'hidden, text centered, promo code and CTA stacked full-width, and the ' +
|
|
194
|
+
'mobileTitle / mobileDescription / mobileCtaText copy applied. Best ' +
|
|
195
|
+
'viewed in the Canvas with the preset mobile viewport.',
|
|
196
|
+
},
|
|
197
|
+
},
|
|
158
198
|
},
|
|
159
199
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const BANNER_AVATARS: {
|
|
2
|
+
nelly: string;
|
|
3
|
+
micheal: string;
|
|
4
|
+
fjolla: string;
|
|
5
|
+
laura: string;
|
|
6
|
+
yuruo: string;
|
|
7
|
+
jerome: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const AVATAR_TILE_BACKGROUND = "linear-gradient(180deg, #e9e9e9 0%, #beb1df 100%)";
|
|
10
|
+
export declare const BANNER_VIDEO_PREVIEW = "https://storage.googleapis.com/bey-dev-public/yuruo-preview.png";
|
|
11
|
+
export declare const AVATAR_TILE_WIDTH = 130;
|
|
12
|
+
export declare const AVATAR_TILE_HEIGHT = 150;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Shared avatar imagery used by the banner collages (PromoBanner side columns
|
|
2
|
+
// and ConversationalAvatarsBanner collage column). Keeping them here lets both components reuse
|
|
3
|
+
// the same set of avatars.
|
|
4
|
+
const AVATAR_BASE = 'https://storage.googleapis.com/bey-dev-public';
|
|
5
|
+
export const BANNER_AVATARS = {
|
|
6
|
+
nelly: `${AVATAR_BASE}/nelly.jpg`,
|
|
7
|
+
micheal: `${AVATAR_BASE}/micheal.jpg`,
|
|
8
|
+
fjolla: `${AVATAR_BASE}/fjolla.png`,
|
|
9
|
+
laura: `${AVATAR_BASE}/laura.png`,
|
|
10
|
+
yuruo: `${AVATAR_BASE}/yuruo.png`,
|
|
11
|
+
jerome: `${AVATAR_BASE}/jerome.png`,
|
|
12
|
+
};
|
|
13
|
+
// Placeholder gradient shown behind avatars with transparent areas so every
|
|
14
|
+
// tile has a consistent backdrop (matches the Figma image tile fill).
|
|
15
|
+
export const AVATAR_TILE_BACKGROUND = 'linear-gradient(180deg, #e9e9e9 0%, #beb1df 100%)';
|
|
16
|
+
// Poster/thumbnail shown in the ConversationalVoiceAgentsBanner video preview.
|
|
17
|
+
export const BANNER_VIDEO_PREVIEW = `${AVATAR_BASE}/yuruo-preview.png`;
|
|
18
|
+
// Size of a single avatar tile in the collages.
|
|
19
|
+
export const AVATAR_TILE_WIDTH = 130;
|
|
20
|
+
export const AVATAR_TILE_HEIGHT = 150;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export * from './Badge';
|
|
2
|
+
export * from './ButtonCard';
|
|
2
3
|
export * from './Avatar';
|
|
4
|
+
export * from './ConversationalAvatarsBanner';
|
|
3
5
|
export * from './CheckIcon';
|
|
4
|
-
export * from './
|
|
6
|
+
export * from './ConversationalVoiceAgentsBanner';
|
|
5
7
|
export * from './HintBubble';
|
|
6
8
|
export * from './Logo';
|
|
7
9
|
export * from './Menu';
|
package/dist/stories/UI/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export * from './Badge';
|
|
2
|
+
export * from './ButtonCard';
|
|
2
3
|
export * from './Avatar';
|
|
4
|
+
export * from './ConversationalAvatarsBanner';
|
|
3
5
|
export * from './CheckIcon';
|
|
4
|
-
export * from './
|
|
6
|
+
export * from './ConversationalVoiceAgentsBanner';
|
|
5
7
|
export * from './HintBubble';
|
|
6
8
|
export * from './Logo';
|
|
7
9
|
export * from './Menu';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Theme, SxProps, TooltipProps } from '@mui/material';
|
|
2
|
-
import { ReactElement, ReactNode } from 'react';
|
|
2
|
+
import { CSSProperties, ReactElement, ReactNode } from 'react';
|
|
3
3
|
import type { IconWeight } from '@phosphor-icons/react';
|
|
4
4
|
import { IconName } from '../../utils';
|
|
5
5
|
import { ButtonSize, ButtonHierarchy } from '../Buttons';
|
|
@@ -354,13 +354,15 @@ export interface PromoBannerProps {
|
|
|
354
354
|
ctaText?: string;
|
|
355
355
|
/** Called when the call-to-action button is clicked */
|
|
356
356
|
onCtaClick?: () => void;
|
|
357
|
-
/** Footer note shown below the actions */
|
|
357
|
+
/** Footer note shown below the actions (desktop only) */
|
|
358
358
|
footerText?: string;
|
|
359
|
-
/**
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
|
|
363
|
-
/**
|
|
359
|
+
/** Title override used on the mobile variant (falls back to `title`) */
|
|
360
|
+
mobileTitle?: string;
|
|
361
|
+
/** Description override used on the mobile variant (falls back to `description`) */
|
|
362
|
+
mobileDescription?: string;
|
|
363
|
+
/** CTA text override used on the mobile variant (falls back to `ctaText`) */
|
|
364
|
+
mobileCtaText?: string;
|
|
365
|
+
/** Whether to show the avatar collage on both sides of the banner */
|
|
364
366
|
showImages?: boolean;
|
|
365
367
|
/** Background of the banner */
|
|
366
368
|
background?: string;
|
|
@@ -370,17 +372,10 @@ export interface PromoBannerProps {
|
|
|
370
372
|
testId?: string;
|
|
371
373
|
}
|
|
372
374
|
/**
|
|
373
|
-
*
|
|
375
|
+
* Shared banner action button, used by ConversationalAvatarsBanner and
|
|
376
|
+
* ConversationalVoiceAgentsBanner.
|
|
374
377
|
*/
|
|
375
|
-
export
|
|
376
|
-
Video = "Video",
|
|
377
|
-
Image = "Image",
|
|
378
|
-
None = "None"
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* FeatureBanner action button
|
|
382
|
-
*/
|
|
383
|
-
export interface FeatureBannerAction {
|
|
378
|
+
export interface BannerAction {
|
|
384
379
|
/** Button text */
|
|
385
380
|
text: string;
|
|
386
381
|
/** Called when the button is clicked */
|
|
@@ -389,25 +384,44 @@ export interface FeatureBannerAction {
|
|
|
389
384
|
primary?: boolean;
|
|
390
385
|
}
|
|
391
386
|
/**
|
|
392
|
-
*
|
|
387
|
+
* ConversationalAvatarsBanner component props — a feature card whose media is a collage column
|
|
388
|
+
* of avatars (reused from the PromoBanner set) that stays on the right at all
|
|
389
|
+
* breakpoints.
|
|
393
390
|
*/
|
|
394
|
-
export interface
|
|
391
|
+
export interface ConversationalAvatarsBannerProps {
|
|
395
392
|
/** Title of the banner */
|
|
396
393
|
title?: string;
|
|
397
394
|
/** Description shown below the title */
|
|
398
395
|
description?: string;
|
|
399
396
|
/** Action buttons */
|
|
400
|
-
actions?:
|
|
401
|
-
/**
|
|
402
|
-
|
|
403
|
-
/**
|
|
404
|
-
|
|
397
|
+
actions?: BannerAction[];
|
|
398
|
+
/** Background of the banner */
|
|
399
|
+
background?: string;
|
|
400
|
+
/** Custom style of the banner */
|
|
401
|
+
style?: SxProps<Theme>;
|
|
402
|
+
/** Test ID of the banner */
|
|
403
|
+
testId?: string;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* ConversationalVoiceAgentsBanner component props — a feature card whose media is a
|
|
407
|
+
* video preview (thumbnail with a play button and an optional CTA pill). Text
|
|
408
|
+
* and actions sit on the left on desktop and stack above the preview on mobile.
|
|
409
|
+
*/
|
|
410
|
+
export interface ConversationalVoiceAgentsBannerProps {
|
|
411
|
+
/** Title of the banner */
|
|
412
|
+
title?: string;
|
|
413
|
+
/** Description shown below the title */
|
|
414
|
+
description?: string;
|
|
415
|
+
/** Action buttons */
|
|
416
|
+
actions?: BannerAction[];
|
|
417
|
+
/** Thumbnail/poster image URL for the video preview */
|
|
418
|
+
previewImage?: string;
|
|
405
419
|
/** Called when the play button on the video preview is clicked */
|
|
406
420
|
onPlayClick?: () => void;
|
|
407
421
|
/** CTA text overlaid on the video preview */
|
|
408
|
-
|
|
409
|
-
/** Called when the
|
|
410
|
-
|
|
422
|
+
previewCtaText?: string;
|
|
423
|
+
/** Called when the preview CTA is clicked */
|
|
424
|
+
onPreviewCtaClick?: () => void;
|
|
411
425
|
/** Background of the banner */
|
|
412
426
|
background?: string;
|
|
413
427
|
/** Custom style of the banner */
|
|
@@ -486,3 +500,35 @@ export interface TabMenuProps {
|
|
|
486
500
|
/** Test ID of the tab menu. */
|
|
487
501
|
testId?: string;
|
|
488
502
|
}
|
|
503
|
+
/**
|
|
504
|
+
* Button card component props — a compact, clickable card with a leading icon,
|
|
505
|
+
* title, description and an optional corner action button.
|
|
506
|
+
*/
|
|
507
|
+
export interface ButtonCardProps {
|
|
508
|
+
/** Card title (e.g. "Create Personal Avatar") */
|
|
509
|
+
title: string;
|
|
510
|
+
/** Short supporting description shown under the title */
|
|
511
|
+
description?: string;
|
|
512
|
+
/** Leading icon rendered inside the accent tile (defaults to "UserFocus") */
|
|
513
|
+
icon?: IconName;
|
|
514
|
+
/** Icon color (defaults to the yellow accent foreground) */
|
|
515
|
+
iconColor?: string;
|
|
516
|
+
/** Background color of the icon tile (defaults to the yellow accent background) */
|
|
517
|
+
iconBackgroundColor?: string;
|
|
518
|
+
/** Card click handler */
|
|
519
|
+
onClick?: () => void;
|
|
520
|
+
/** Show the corner action button (e.g. "Contact Us") */
|
|
521
|
+
showActionButton?: boolean;
|
|
522
|
+
/** Corner action button label (defaults to "Contact Us") */
|
|
523
|
+
actionButtonText?: string;
|
|
524
|
+
/** Corner action button click handler */
|
|
525
|
+
onActionClick?: () => void;
|
|
526
|
+
/** Disable the card (dims it and blocks the click handler) */
|
|
527
|
+
disabled?: boolean;
|
|
528
|
+
/** Width of the card (defaults to "294px") */
|
|
529
|
+
width?: string | number;
|
|
530
|
+
/** Custom style */
|
|
531
|
+
style?: CSSProperties;
|
|
532
|
+
/** Test id */
|
|
533
|
+
testId?: string;
|
|
534
|
+
}
|
package/dist/stories/UI/types.js
CHANGED
|
@@ -110,15 +110,6 @@ export var ProgressIndicatorMode;
|
|
|
110
110
|
/** Renders only the bar, with no accompanying value text. */
|
|
111
111
|
ProgressIndicatorMode["Bar"] = "Bar";
|
|
112
112
|
})(ProgressIndicatorMode || (ProgressIndicatorMode = {}));
|
|
113
|
-
/**
|
|
114
|
-
* FeatureBanner media variants
|
|
115
|
-
*/
|
|
116
|
-
export var FeatureBannerMedia;
|
|
117
|
-
(function (FeatureBannerMedia) {
|
|
118
|
-
FeatureBannerMedia["Video"] = "Video";
|
|
119
|
-
FeatureBannerMedia["Image"] = "Image";
|
|
120
|
-
FeatureBannerMedia["None"] = "None";
|
|
121
|
-
})(FeatureBannerMedia || (FeatureBannerMedia = {}));
|
|
122
113
|
/**
|
|
123
114
|
* TabMenu size variants
|
|
124
115
|
*/
|
package/package.json
CHANGED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box } from '@mui/material';
|
|
3
|
-
import { FeatureBannerMedia, } from './types';
|
|
4
|
-
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
5
|
-
import { MainButton, ButtonHierarchy, ButtonSize, ButtonDisplayMode, } from '../Buttons';
|
|
6
|
-
const VIOLET_GRADIENT = 'linear-gradient(180deg, rgba(194, 171, 252, 0.08) 0%, rgba(128, 96, 198, 0.2) 100%)';
|
|
7
|
-
const PINK_GRADIENT = 'linear-gradient(90deg, rgba(235, 205, 240, 0.05) 0%, rgba(202, 59, 224, 0.15) 100%)';
|
|
8
|
-
export const FeatureBanner = ({ title, description, actions = [], media = FeatureBannerMedia.None, mediaImage, onPlayClick, mediaCtaText, onMediaCtaClick, background, style: customStyle, testId, }) => {
|
|
9
|
-
const bannerBackground = background ||
|
|
10
|
-
(media === FeatureBannerMedia.Video ? PINK_GRADIENT : VIOLET_GRADIENT);
|
|
11
|
-
const renderAction = (action, index) => (_jsx(MainButton, { text: action.text, size: ButtonSize.md, hierarchy: action.primary
|
|
12
|
-
? ButtonHierarchy.Primary
|
|
13
|
-
: ButtonHierarchy.SecondaryColor, onClick: action.onClick, style: action.primary
|
|
14
|
-
? undefined
|
|
15
|
-
: {
|
|
16
|
-
border: '1px solid var(--color-border-border-component)',
|
|
17
|
-
}, testId: testId ? `${testId}-action-${index}` : undefined }, `${action.text}-${index}`));
|
|
18
|
-
const renderMedia = () => {
|
|
19
|
-
if (media === FeatureBannerMedia.None || !mediaImage)
|
|
20
|
-
return null;
|
|
21
|
-
if (media === FeatureBannerMedia.Video) {
|
|
22
|
-
return (_jsxs(Box, { position: "relative", width: "296px", height: "150px", flexShrink: 0, overflow: "hidden", borderRadius: "var(--spacing-tokens-size-in-px-radius-radius-sm)", children: [_jsx(Box, { component: "img", src: mediaImage, alt: "", width: "100%", height: "100%", sx: { objectFit: 'cover', display: 'block' } }), _jsx(Box, { position: "absolute", left: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", bottom: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", children: _jsx(MainButton, { size: ButtonSize.md, hierarchy: ButtonHierarchy.SecondaryTransparent, displayIconMode: ButtonDisplayMode.Only, displayIcon: "Play", onClick: onPlayClick, style: { border: 'none' }, testId: testId ? `${testId}-play` : undefined }) }), mediaCtaText && (_jsx(Box, { position: "absolute", right: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", bottom: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", children: _jsx(MainButton, { text: mediaCtaText, size: ButtonSize.md, hierarchy: ButtonHierarchy.SecondaryTransparent, onClick: onMediaCtaClick, style: { border: 'none' }, testId: testId ? `${testId}-media-cta` : undefined }) }))] }));
|
|
23
|
-
}
|
|
24
|
-
// Image media spans the full banner height by escaping the vertical padding
|
|
25
|
-
return (_jsx(Box, { component: "img", src: mediaImage, alt: "", flexShrink: 0, sx: {
|
|
26
|
-
marginY: 'calc(-1 * var(--spacing-tokens-size-in-px-spacing-spacing-3xl))',
|
|
27
|
-
} }));
|
|
28
|
-
};
|
|
29
|
-
return (_jsxs(Box, { display: "flex", alignItems: "center", gap: "40px", width: "100%", overflow: "hidden", borderRadius: "var(--spacing-tokens-size-in-px-radius-radius-lg)", padding: "var(--spacing-tokens-size-in-px-spacing-spacing-3xl) var(--spacing-tokens-size-in-px-spacing-spacing-2xl)", sx: {
|
|
30
|
-
background: bannerBackground,
|
|
31
|
-
boxSizing: 'border-box',
|
|
32
|
-
...customStyle,
|
|
33
|
-
}, "data-testid": testId, children: [_jsxs(Box, { display: "flex", flexDirection: "column", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-xl)", flex: 1, minWidth: 0, children: [_jsxs(Box, { display: "flex", flexDirection: "column", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", children: [title && (_jsx(Typography, { size: TypographySize.HeadingM, weight: TypographyWeight.Bold, color: "var(--color-text-text-title)", children: title })), description && (_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Regular, color: "var(--color-text-text-label)", children: description }))] }), actions.length > 0 && (_jsx(Box, { display: "flex", alignItems: "center", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", children: actions.map(renderAction) }))] }), renderMedia()] }));
|
|
34
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import { FeatureBanner } from './FeatureBanner';
|
|
3
|
-
declare const meta: Meta<typeof FeatureBanner>;
|
|
4
|
-
export default meta;
|
|
5
|
-
type Story = StoryObj<typeof FeatureBanner>;
|
|
6
|
-
export declare const Video: Story;
|
|
7
|
-
export declare const Image: Story;
|
|
8
|
-
export declare const TextOnly: Story;
|
|
9
|
-
export declare const HomeLayout: Story;
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { FeatureBanner } from './FeatureBanner';
|
|
3
|
-
import { FeatureBannerMedia } from './types';
|
|
4
|
-
import videoPreview from '../assets/banner/video-preview.png';
|
|
5
|
-
import avatarsColumn from '../assets/banner/banner-avatars-column.png';
|
|
6
|
-
const meta = {
|
|
7
|
-
title: 'Design System/Components/UI/FeatureBanner',
|
|
8
|
-
component: FeatureBanner,
|
|
9
|
-
parameters: {
|
|
10
|
-
layout: 'padded',
|
|
11
|
-
design: {
|
|
12
|
-
type: 'figspec',
|
|
13
|
-
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=7011-27024',
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
tags: ['autodocs'],
|
|
17
|
-
argTypes: {
|
|
18
|
-
title: {
|
|
19
|
-
control: 'text',
|
|
20
|
-
description: 'Title of the banner',
|
|
21
|
-
table: {
|
|
22
|
-
type: { summary: 'string' },
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
description: {
|
|
26
|
-
control: 'text',
|
|
27
|
-
description: 'Description shown below the title',
|
|
28
|
-
table: {
|
|
29
|
-
type: { summary: 'string' },
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
actions: {
|
|
33
|
-
control: 'object',
|
|
34
|
-
description: 'Action buttons — set primary: true for the brand (violet) button',
|
|
35
|
-
table: {
|
|
36
|
-
type: { summary: 'FeatureBannerAction[]' },
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
media: {
|
|
40
|
-
control: 'select',
|
|
41
|
-
description: 'Media shown on the right side of the banner',
|
|
42
|
-
options: Object.values(FeatureBannerMedia),
|
|
43
|
-
table: {
|
|
44
|
-
type: { summary: 'string' },
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
mediaImage: {
|
|
48
|
-
control: 'text',
|
|
49
|
-
description: 'Image URL for the media (video thumbnail or image column)',
|
|
50
|
-
table: {
|
|
51
|
-
type: { summary: 'string' },
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
onPlayClick: {
|
|
55
|
-
action: 'play clicked',
|
|
56
|
-
description: 'Called when the play button on the video preview is clicked',
|
|
57
|
-
table: {
|
|
58
|
-
type: { summary: 'function' },
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
mediaCtaText: {
|
|
62
|
-
control: 'text',
|
|
63
|
-
description: 'CTA text overlaid on the video preview',
|
|
64
|
-
table: {
|
|
65
|
-
type: { summary: 'string' },
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
onMediaCtaClick: {
|
|
69
|
-
action: 'media cta clicked',
|
|
70
|
-
description: 'Called when the media CTA is clicked',
|
|
71
|
-
table: {
|
|
72
|
-
type: { summary: 'function' },
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
background: {
|
|
76
|
-
control: 'text',
|
|
77
|
-
description: 'Background of the banner',
|
|
78
|
-
table: {
|
|
79
|
-
type: { summary: 'string' },
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
style: {
|
|
83
|
-
control: 'object',
|
|
84
|
-
description: 'Custom style for the banner',
|
|
85
|
-
table: {
|
|
86
|
-
type: { summary: 'object' },
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
export default meta;
|
|
92
|
-
export const Video = {
|
|
93
|
-
args: {
|
|
94
|
-
title: 'Add a Human Face to your Conversational Voice Agents',
|
|
95
|
-
description: 'Bring your ideas to life with intelligent conversational avatars.',
|
|
96
|
-
actions: [
|
|
97
|
-
{ text: 'Create API Key' },
|
|
98
|
-
{ text: 'Go to Docs', primary: true },
|
|
99
|
-
],
|
|
100
|
-
media: FeatureBannerMedia.Video,
|
|
101
|
-
mediaImage: videoPreview,
|
|
102
|
-
mediaCtaText: 'How to Get Started',
|
|
103
|
-
},
|
|
104
|
-
};
|
|
105
|
-
export const Image = {
|
|
106
|
-
args: {
|
|
107
|
-
title: 'Introducing Conversational Avatars',
|
|
108
|
-
description: 'Bring your ideas to life with intelligent conversational avatars.',
|
|
109
|
-
actions: [{ text: 'Create New Agent' }],
|
|
110
|
-
media: FeatureBannerMedia.Image,
|
|
111
|
-
mediaImage: avatarsColumn,
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
export const TextOnly = {
|
|
115
|
-
args: {
|
|
116
|
-
title: 'Introducing Conversational Avatars',
|
|
117
|
-
description: 'Bring your ideas to life with intelligent conversational avatars.',
|
|
118
|
-
actions: [{ text: 'Create New Agent' }],
|
|
119
|
-
media: FeatureBannerMedia.None,
|
|
120
|
-
},
|
|
121
|
-
};
|
|
122
|
-
export const HomeLayout = {
|
|
123
|
-
parameters: {
|
|
124
|
-
docs: {
|
|
125
|
-
description: {
|
|
126
|
-
story: 'Both banners side by side as they appear on the Home screen of the app.',
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
render: () => (_jsxs("div", { style: { display: 'flex', gap: '12px', alignItems: 'stretch' }, children: [_jsx("div", { style: { flex: 3, minWidth: 0, display: 'flex' }, children: _jsx(FeatureBanner, { ...Video.args }) }), _jsx("div", { style: { flex: 2, minWidth: 0, display: 'flex' }, children: _jsx(FeatureBanner, { ...Image.args }) })] })),
|
|
131
|
-
};
|