@beydesign/storybook 0.2.15 → 0.2.16
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 +30 -0
- package/dist/stories/UI/ButtonCard.stories.d.ts +14 -0
- package/dist/stories/UI/ButtonCard.stories.js +108 -0
- package/dist/stories/UI/FeatureBanner.js +17 -1
- package/dist/stories/UI/FeatureBanner.stories.d.ts +1 -0
- package/dist/stories/UI/FeatureBanner.stories.js +32 -0
- package/dist/stories/UI/PromoBanner.js +20 -2
- package/dist/stories/UI/PromoBanner.stories.d.ts +1 -0
- package/dist/stories/UI/PromoBanner.stories.js +39 -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 +33 -1
- package/package.json +1 -1
|
@@ -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,30 @@
|
|
|
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, 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) => {
|
|
27
|
+
e.stopPropagation();
|
|
28
|
+
onActionClick?.();
|
|
29
|
+
}, testId: `${cardKey}-action-button` }) }))] }));
|
|
30
|
+
};
|
|
@@ -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
|
+
};
|
|
@@ -5,6 +5,9 @@ import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
|
5
5
|
import { MainButton, ButtonHierarchy, ButtonSize, ButtonDisplayMode, } from '../Buttons';
|
|
6
6
|
const VIOLET_GRADIENT = 'linear-gradient(180deg, rgba(194, 171, 252, 0.08) 0%, rgba(128, 96, 198, 0.2) 100%)';
|
|
7
7
|
const PINK_GRADIENT = 'linear-gradient(90deg, rgba(235, 205, 240, 0.05) 0%, rgba(202, 59, 224, 0.15) 100%)';
|
|
8
|
+
// Below this width the banner stacks vertically to match the Figma mobile
|
|
9
|
+
// layout (text + actions on top, media full-width below).
|
|
10
|
+
const MOBILE = '@media (max-width: 640px)';
|
|
8
11
|
export const FeatureBanner = ({ title, description, actions = [], media = FeatureBannerMedia.None, mediaImage, onPlayClick, mediaCtaText, onMediaCtaClick, background, style: customStyle, testId, }) => {
|
|
9
12
|
const bannerBackground = background ||
|
|
10
13
|
(media === FeatureBannerMedia.Video ? PINK_GRADIENT : VIOLET_GRADIENT);
|
|
@@ -19,16 +22,29 @@ export const FeatureBanner = ({ title, description, actions = [], media = Featur
|
|
|
19
22
|
if (media === FeatureBannerMedia.None || !mediaImage)
|
|
20
23
|
return null;
|
|
21
24
|
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)",
|
|
25
|
+
return (_jsxs(Box, { position: "relative", width: "296px", height: "150px", flexShrink: 0, overflow: "hidden", borderRadius: "var(--spacing-tokens-size-in-px-radius-radius-sm)", sx: {
|
|
26
|
+
[MOBILE]: {
|
|
27
|
+
width: '100%',
|
|
28
|
+
height: 'auto',
|
|
29
|
+
aspectRatio: '296 / 150',
|
|
30
|
+
},
|
|
31
|
+
}, 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
32
|
}
|
|
24
33
|
// Image media spans the full banner height by escaping the vertical padding
|
|
25
34
|
return (_jsx(Box, { component: "img", src: mediaImage, alt: "", flexShrink: 0, sx: {
|
|
26
35
|
marginY: 'calc(-1 * var(--spacing-tokens-size-in-px-spacing-spacing-3xl))',
|
|
36
|
+
[MOBILE]: { marginY: 0, maxWidth: '100%' },
|
|
27
37
|
} }));
|
|
28
38
|
};
|
|
29
39
|
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
40
|
background: bannerBackground,
|
|
31
41
|
boxSizing: 'border-box',
|
|
42
|
+
[MOBILE]: {
|
|
43
|
+
flexDirection: 'column',
|
|
44
|
+
alignItems: 'stretch',
|
|
45
|
+
gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-3xl)',
|
|
46
|
+
padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl) var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
|
|
47
|
+
},
|
|
32
48
|
...customStyle,
|
|
33
49
|
}, "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
50
|
};
|
|
@@ -119,6 +119,38 @@ export const TextOnly = {
|
|
|
119
119
|
media: FeatureBannerMedia.None,
|
|
120
120
|
},
|
|
121
121
|
};
|
|
122
|
+
const FIGMA_MOBILE_VIEWPORT = {
|
|
123
|
+
viewport: {
|
|
124
|
+
viewports: {
|
|
125
|
+
figmaMobile: {
|
|
126
|
+
name: 'Figma Mobile (375px)',
|
|
127
|
+
styles: { width: '375px', height: '812px' },
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
defaultViewport: 'figmaMobile',
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
export const Mobile = {
|
|
134
|
+
args: {
|
|
135
|
+
...Video.args,
|
|
136
|
+
},
|
|
137
|
+
parameters: {
|
|
138
|
+
layout: 'padded',
|
|
139
|
+
...FIGMA_MOBILE_VIEWPORT,
|
|
140
|
+
design: {
|
|
141
|
+
type: 'figspec',
|
|
142
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=7081-24780',
|
|
143
|
+
},
|
|
144
|
+
docs: {
|
|
145
|
+
description: {
|
|
146
|
+
story: 'The feature banner on the mobile Home screen — text and actions ' +
|
|
147
|
+
'stack on top, media spans full-width below. Below 640px the component ' +
|
|
148
|
+
'reflows automatically; best viewed in the Canvas with the preset ' +
|
|
149
|
+
'mobile viewport.',
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
122
154
|
export const HomeLayout = {
|
|
123
155
|
parameters: {
|
|
124
156
|
docs: {
|
|
@@ -3,6 +3,9 @@ import { Box } from '@mui/material';
|
|
|
3
3
|
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
4
4
|
import { MainButton, ButtonHierarchy, ButtonSize } from '../Buttons';
|
|
5
5
|
const DEFAULT_BACKGROUND = 'linear-gradient(180deg, rgba(194, 171, 252, 0.08) 0%, rgba(128, 96, 198, 0.2) 100%)';
|
|
6
|
+
// Below this width the banner stacks vertically to match the Figma mobile
|
|
7
|
+
// layout (side images hidden, promo code + CTA stacked full-width).
|
|
8
|
+
const MOBILE = '@media (max-width: 640px)';
|
|
6
9
|
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
10
|
'Real-Time',
|
|
8
11
|
'Multilingual',
|
|
@@ -22,9 +25,24 @@ export const PromoBanner = ({ title = 'Real-time AI avatars for every conversati
|
|
|
22
25
|
const renderSideImage = (image) => {
|
|
23
26
|
if (!showImages || !image)
|
|
24
27
|
return null;
|
|
25
|
-
return (_jsx(Box, { component: "img", src: image, alt: "", height: "100%", flexShrink: 0, sx: { objectFit: 'contain' } }));
|
|
28
|
+
return (_jsx(Box, { component: "img", src: image, alt: "", height: "100%", flexShrink: 0, sx: { objectFit: 'contain', [MOBILE]: { display: 'none' } } }));
|
|
26
29
|
};
|
|
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: {
|
|
30
|
+
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: {
|
|
31
|
+
background,
|
|
32
|
+
boxSizing: 'border-box',
|
|
33
|
+
[MOBILE]: {
|
|
34
|
+
height: 'auto',
|
|
35
|
+
gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-3xl)',
|
|
36
|
+
paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
|
|
37
|
+
},
|
|
38
|
+
...customStyle,
|
|
39
|
+
}, "data-testid": testId, children: [renderSideImage(leftImage), _jsxs(Box, { display: "flex", flexDirection: "column", alignItems: "center", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-2xl)", flex: 1, minWidth: 0, 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%", children: [title && (_jsx(Typography, { size: TypographySize.HeadingM, weight: TypographyWeight.Bold, color: "var(--color-text-text-title)", textStyle: { textAlign: 'center' }, children: title })), (description || featuresText) && (_jsxs(Box, { display: "flex", flexDirection: "column", children: [description && (_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Regular, color: "var(--color-text-text-title)", children: description })), featuresText && (_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Bold, color: "var(--color-text-text-title)", children: featuresText }))] }))] }), (promoCode || ctaText) && (_jsxs(Box, { display: "flex", justifyContent: "center", alignItems: "stretch", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-md)", sx: {
|
|
40
|
+
[MOBILE]: {
|
|
41
|
+
flexDirection: 'column',
|
|
42
|
+
width: '100%',
|
|
43
|
+
'& button': { width: '100%' },
|
|
44
|
+
},
|
|
45
|
+
}, 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: {
|
|
28
46
|
border: '1px dashed var(--color-border-border-component)',
|
|
29
47
|
height: '100%',
|
|
30
48
|
}, 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,
|
|
@@ -157,3 +157,42 @@ export const TitleAndCtaOnly = {
|
|
|
157
157
|
rightImage: bannerAvatarsRight,
|
|
158
158
|
},
|
|
159
159
|
};
|
|
160
|
+
const FIGMA_MOBILE_VIEWPORT = {
|
|
161
|
+
viewport: {
|
|
162
|
+
viewports: {
|
|
163
|
+
figmaMobile: {
|
|
164
|
+
name: 'Figma Mobile (375px)',
|
|
165
|
+
styles: { width: '375px', height: '812px' },
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
defaultViewport: 'figmaMobile',
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
export const Mobile = {
|
|
172
|
+
args: {
|
|
173
|
+
title: 'Welcome! Get your free Starter plan for 1 month',
|
|
174
|
+
description: "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.",
|
|
175
|
+
features: [],
|
|
176
|
+
promoCode: 'PKAJ2HGO',
|
|
177
|
+
promoCodeLabel: 'YOUR PROMO CODE',
|
|
178
|
+
ctaText: 'Activate My Free Starter Plan',
|
|
179
|
+
footerText: undefined,
|
|
180
|
+
showImages: false,
|
|
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 promo banner on the mobile Home screen — side images hidden, ' +
|
|
192
|
+
'auto height, promo code and CTA stacked full-width. Below 640px the ' +
|
|
193
|
+
'component reflows automatically; best viewed in the Canvas with the ' +
|
|
194
|
+
'preset mobile viewport.',
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
};
|
package/dist/stories/UI/index.js
CHANGED
|
@@ -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';
|
|
@@ -486,3 +486,35 @@ export interface TabMenuProps {
|
|
|
486
486
|
/** Test ID of the tab menu. */
|
|
487
487
|
testId?: string;
|
|
488
488
|
}
|
|
489
|
+
/**
|
|
490
|
+
* Button card component props — a compact, clickable card with a leading icon,
|
|
491
|
+
* title, description and an optional corner action button.
|
|
492
|
+
*/
|
|
493
|
+
export interface ButtonCardProps {
|
|
494
|
+
/** Card title (e.g. "Create Personal Avatar") */
|
|
495
|
+
title: string;
|
|
496
|
+
/** Short supporting description shown under the title */
|
|
497
|
+
description?: string;
|
|
498
|
+
/** Leading icon rendered inside the accent tile (defaults to "UserFocus") */
|
|
499
|
+
icon?: IconName;
|
|
500
|
+
/** Icon color (defaults to the yellow accent foreground) */
|
|
501
|
+
iconColor?: string;
|
|
502
|
+
/** Background color of the icon tile (defaults to the yellow accent background) */
|
|
503
|
+
iconBackgroundColor?: string;
|
|
504
|
+
/** Card click handler */
|
|
505
|
+
onClick?: () => void;
|
|
506
|
+
/** Show the corner action button (e.g. "Contact Us") */
|
|
507
|
+
showActionButton?: boolean;
|
|
508
|
+
/** Corner action button label (defaults to "Contact Us") */
|
|
509
|
+
actionButtonText?: string;
|
|
510
|
+
/** Corner action button click handler */
|
|
511
|
+
onActionClick?: () => void;
|
|
512
|
+
/** Disable the card (dims it and blocks the click handler) */
|
|
513
|
+
disabled?: boolean;
|
|
514
|
+
/** Width of the card (defaults to "294px") */
|
|
515
|
+
width?: string | number;
|
|
516
|
+
/** Custom style */
|
|
517
|
+
style?: CSSProperties;
|
|
518
|
+
/** Test id */
|
|
519
|
+
testId?: string;
|
|
520
|
+
}
|