@beydesign/storybook 0.1.17 → 0.1.20
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/App.js +104 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/stories/EntityCard/EntityCard.d.ts +3 -0
- package/dist/stories/EntityCard/EntityCard.js +22 -0
- package/dist/stories/EntityCard/EntityCard.stories.d.ts +10 -0
- package/dist/stories/EntityCard/EntityCard.stories.js +187 -0
- package/dist/stories/EntityCard/EntityCardSkeleton.d.ts +3 -0
- package/dist/stories/EntityCard/EntityCardSkeleton.js +9 -0
- package/dist/stories/EntityCard/EntityCardSkeleton.stories.d.ts +10 -0
- package/dist/stories/EntityCard/EntityCardSkeleton.stories.js +73 -0
- package/dist/stories/EntityCard/index.d.ts +3 -0
- package/dist/stories/EntityCard/index.js +3 -0
- package/dist/stories/EntityCard/types.d.ts +54 -0
- package/dist/stories/EntityCard/types.js +1 -0
- package/dist/stories/Form/TextInput.js +46 -12
- package/dist/stories/Form/TextInput.stories.d.ts +13 -0
- package/dist/stories/Form/TextInput.stories.js +20 -1
- package/dist/stories/Form/types.d.ts +5 -0
- package/dist/stories/UI/FeatureBanner.d.ts +3 -0
- package/dist/stories/UI/FeatureBanner.js +34 -0
- package/dist/stories/UI/FeatureBanner.stories.d.ts +9 -0
- package/dist/stories/UI/FeatureBanner.stories.js +131 -0
- package/dist/stories/UI/Menu.js +3 -3
- package/dist/stories/UI/Menu.stories.d.ts +1 -0
- package/dist/stories/UI/Menu.stories.js +32 -0
- package/dist/stories/UI/PromoBanner.d.ts +3 -0
- package/dist/stories/UI/PromoBanner.js +31 -0
- package/dist/stories/UI/PromoBanner.stories.d.ts +9 -0
- package/dist/stories/UI/PromoBanner.stories.js +159 -0
- package/dist/stories/UI/index.d.ts +2 -0
- package/dist/stories/UI/index.js +2 -0
- package/dist/stories/UI/types.d.ts +86 -0
- package/dist/stories/UI/types.js +9 -0
- package/package.json +2 -2
package/dist/App.js
CHANGED
|
@@ -1,10 +1,109 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import
|
|
4
|
-
import viteLogo from '/vite.svg';
|
|
3
|
+
import { EntityCard, EntityCardSkeleton } from './stories/EntityCard';
|
|
5
4
|
import './App.css';
|
|
5
|
+
const avatars = [
|
|
6
|
+
{
|
|
7
|
+
id: 'nelly',
|
|
8
|
+
name: 'Nelly',
|
|
9
|
+
image: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?fm=jpg&q=60&w=600&ixlib=rb-4.0.3',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
id: 'ava',
|
|
13
|
+
name: 'Ava',
|
|
14
|
+
image: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?fm=jpg&q=60&w=600&ixlib=rb-4.0.3',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'leo',
|
|
18
|
+
name: 'Leo',
|
|
19
|
+
image: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?fm=jpg&q=60&w=600&ixlib=rb-4.0.3',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'maya',
|
|
23
|
+
name: 'Maya Customer Support',
|
|
24
|
+
image: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?fm=jpg&q=60&w=600&ixlib=rb-4.0.3',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'no-image',
|
|
28
|
+
name: 'No Image',
|
|
29
|
+
image: undefined,
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
const agentMenuItems = [
|
|
33
|
+
{
|
|
34
|
+
label: 'Conversation Link',
|
|
35
|
+
icon: 'ShareNetwork',
|
|
36
|
+
onClick: () => { },
|
|
37
|
+
},
|
|
38
|
+
{ label: 'Copy Agent ID', icon: 'CopySimple', onClick: () => { } },
|
|
39
|
+
{ label: 'Embed', icon: 'Code', onClick: () => { } },
|
|
40
|
+
{
|
|
41
|
+
label: 'Agent Details',
|
|
42
|
+
icon: 'IdentificationCard',
|
|
43
|
+
onClick: () => { },
|
|
44
|
+
},
|
|
45
|
+
{ label: 'Duplicate', icon: 'Copy', onClick: () => { } },
|
|
46
|
+
{
|
|
47
|
+
label: 'Delete',
|
|
48
|
+
icon: 'Trash',
|
|
49
|
+
onClick: () => { },
|
|
50
|
+
destructive: true,
|
|
51
|
+
},
|
|
52
|
+
];
|
|
6
53
|
function App() {
|
|
7
|
-
const [
|
|
8
|
-
return (_jsxs(
|
|
54
|
+
const [loading, setLoading] = useState(false);
|
|
55
|
+
return (_jsxs("div", { style: { padding: '32px' }, children: [_jsx("button", { type: "button", onClick: () => setLoading((prev) => !prev), style: {
|
|
56
|
+
fontFamily: 'Figtree, sans-serif',
|
|
57
|
+
marginBottom: '24px',
|
|
58
|
+
padding: '8px 16px',
|
|
59
|
+
borderRadius: '6px',
|
|
60
|
+
border: '1px solid var(--colors-light-border-border-component)',
|
|
61
|
+
background: 'var(--colors-light-background-bg-white)',
|
|
62
|
+
color: 'var(--colors-light-text-text-title)',
|
|
63
|
+
cursor: 'pointer',
|
|
64
|
+
}, children: loading ? 'Show loaded cards' : 'Show loading skeletons' }), _jsx("h2", { style: {
|
|
65
|
+
fontFamily: 'Figtree, sans-serif',
|
|
66
|
+
color: 'var(--colors-light-text-text-title)',
|
|
67
|
+
marginBottom: '4px',
|
|
68
|
+
}, children: "Explore our Avatar Library" }), _jsx("p", { style: {
|
|
69
|
+
fontFamily: 'Figtree, sans-serif',
|
|
70
|
+
color: 'var(--colors-light-text-text-subtle)',
|
|
71
|
+
marginBottom: '24px',
|
|
72
|
+
}, children: "Select the perfect studio-quality digital human to make your interactions feel personal." }), _jsx("div", { style: {
|
|
73
|
+
display: 'flex',
|
|
74
|
+
flexWrap: 'wrap',
|
|
75
|
+
gap: '16px',
|
|
76
|
+
}, children: avatars.map((avatar) => loading ? (_jsx(EntityCardSkeleton, { view: "avatar", showDescription: true }, avatar.id)) : (_jsx(EntityCard, { name: avatar.name, image: avatar.image, onPreview: () => console.log('Preview', avatar.name), showMenu: true, menuItems: [
|
|
77
|
+
{
|
|
78
|
+
label: 'Copy Avatar ID',
|
|
79
|
+
icon: 'Copy',
|
|
80
|
+
onClick: () => console.log('Copy', avatar.id),
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
label: 'Create Agent',
|
|
84
|
+
icon: 'UserCirclePlus',
|
|
85
|
+
onClick: () => console.log('Create Agent', avatar.name),
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
label: 'Delete',
|
|
89
|
+
icon: 'Trash',
|
|
90
|
+
onClick: () => console.log('Delete', avatar.name),
|
|
91
|
+
destructive: true,
|
|
92
|
+
},
|
|
93
|
+
], description: "daijdaij pa oapopao ao opaop op", showDescription: true }, avatar.id))) }), _jsx("h2", { style: {
|
|
94
|
+
fontFamily: 'Figtree, sans-serif',
|
|
95
|
+
color: 'var(--colors-light-text-text-title)',
|
|
96
|
+
margin: '40px 0 4px',
|
|
97
|
+
}, children: "Explore our Agent library" }), _jsx("p", { style: {
|
|
98
|
+
fontFamily: 'Figtree, sans-serif',
|
|
99
|
+
color: 'var(--colors-light-text-text-subtle)',
|
|
100
|
+
marginBottom: '24px',
|
|
101
|
+
}, children: "Your active agents \u2014 hover a card to join a live conversation." }), _jsx("div", { style: {
|
|
102
|
+
display: 'flex',
|
|
103
|
+
flexWrap: 'wrap',
|
|
104
|
+
gap: '16px',
|
|
105
|
+
}, children: avatars
|
|
106
|
+
.slice(0, 4)
|
|
107
|
+
.map((avatar) => loading ? (_jsx(EntityCardSkeleton, { view: "agent" }, `agent-${avatar.id}`)) : (_jsx(EntityCard, { view: "agent", name: `${avatar.name} Customer Support`, image: avatar.image, showStatusBadge: true, statusBadgeText: "Active", onPreview: () => console.log('Join', avatar.name), previewButtonText: "Join", previewIcon: "VideoCamera", showMenu: true, menuItems: agentMenuItems }, `agent-${avatar.id}`))) })] }));
|
|
9
108
|
}
|
|
10
109
|
export default App;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Box } from '@mui/material';
|
|
4
|
+
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
5
|
+
import { Menu, Badge, BadgeColor, BadgeSize } from '../UI';
|
|
6
|
+
import { MainButton, ButtonHierarchy, ButtonSize, ButtonDisplayMode, } from '../Buttons';
|
|
7
|
+
import { UserCircle } from '@phosphor-icons/react';
|
|
8
|
+
export const EntityCard = ({ view = 'avatar', name, image, description = '', showDescription = false, onPreview, previewButtonText, previewIcon = 'Play', showStatusBadge = false, statusBadgeText = '', statusBadgeColor = BadgeColor.Green, menuItems = [], showMenu = false, width, }) => {
|
|
9
|
+
const [hovered, setHovered] = useState(false);
|
|
10
|
+
const [imageError, setImageError] = useState(false);
|
|
11
|
+
const isAgent = view === 'agent';
|
|
12
|
+
const cardWidth = width ?? (isAgent ? '280px' : '218px');
|
|
13
|
+
const imageAspectRatio = isAgent ? '1 / 1' : '203 / 264';
|
|
14
|
+
const entityKey = name?.toLowerCase()?.replace(/\s+/g, '-');
|
|
15
|
+
const hasMenu = showMenu && menuItems.length > 0;
|
|
16
|
+
const showActions = hovered && (!!onPreview || hasMenu);
|
|
17
|
+
const showBadge = showStatusBadge && !!statusBadgeText;
|
|
18
|
+
return (_jsxs(Box, { width: cardWidth, display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', bgcolor: 'var(--colors-light-background-bg-card-highlight)', border: '1px solid var(--colors-light-border-border-subtle)', boxSizing: 'border-box', overflow: 'hidden', "data-testid": `${entityKey}-entity-card`, children: [_jsxs(Box, { position: 'relative', width: '100%', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', overflow: 'hidden', sx: { aspectRatio: imageAspectRatio }, onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), children: [image && !imageError ? (_jsx("img", { src: image, alt: name, width: '100%', height: '100%', style: { objectFit: 'cover', height: '100%', width: '100%' }, onError: () => setImageError(true) })) : (_jsx(Box, { width: '100%', height: '100%', bgcolor: 'var(--colors-light-background-bg-element)', display: 'flex', justifyContent: 'center', alignItems: 'center', children: _jsx(UserCircle, { size: 74, weight: 'thin', color: 'var(--colors-light-text-text-subtle)' }) })), _jsx(Box, { position: 'absolute', left: 0, right: 0, bottom: 0, display: 'flex', alignItems: 'center', padding: '24px 12px 12px', sx: {
|
|
19
|
+
backgroundImage: 'linear-gradient(rgba(45, 38, 55, 0) 0%, rgba(45, 38, 55, 0.16) 34%, rgba(45, 38, 55, 0.64) 100%)',
|
|
20
|
+
}, children: _jsx(Typography, { size: TypographySize.HeadingXS, weight: TypographyWeight.SemiBold, color: 'var(--colors-light-text-text-white)', textStyle: { wordBreak: 'break-word' }, children: name }) }), showBadge && (_jsx(Box, { position: 'absolute', top: '8px', left: '8px', sx: { zIndex: 2 }, children: _jsx(Badge, { text: statusBadgeText, color: statusBadgeColor, size: BadgeSize.md, showIcon: true, icon: 'Circle', iconWeight: 'fill', iconSize: '8px', iconColor: 'var(--colors-light-background-bg-success)' }) })), showActions && (_jsxs(Box, { position: 'absolute', top: '8px', right: '8px', display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', sx: { zIndex: 2 }, children: [onPreview &&
|
|
21
|
+
(previewButtonText ? (_jsx(MainButton, { text: previewButtonText, showLeadingIcon: true, leadingIcon: previewIcon, hierarchy: ButtonHierarchy.Primary, size: ButtonSize.sm, onClick: onPreview, testId: `${entityKey}-preview-button` })) : (_jsx(MainButton, { displayIcon: previewIcon, displayIconMode: ButtonDisplayMode.Only, hierarchy: ButtonHierarchy.Primary, size: ButtonSize.sm, onClick: onPreview, testId: `${entityKey}-preview-button` }))), hasMenu && (_jsx(Menu, { items: menuItems, testKey: entityKey, size: ButtonSize.sm }))] }))] }), showDescription && description && (_jsx(Box, { display: 'flex', alignItems: 'center', padding: '0 12px', width: '100%', boxSizing: 'border-box', children: _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: 'var(--colors-light-text-text-label)', textStyle: { wordBreak: 'break-word' }, children: description }) }))] }));
|
|
22
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { EntityCard } from './EntityCard';
|
|
3
|
+
declare const meta: Meta<typeof EntityCard>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof EntityCard>;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const AgentView: Story;
|
|
8
|
+
export declare const WithDescription: Story;
|
|
9
|
+
export declare const NoActions: Story;
|
|
10
|
+
export declare const NoImage: Story;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { EntityCard } from './EntityCard';
|
|
2
|
+
import { BadgeColor } from '../UI';
|
|
3
|
+
const meta = {
|
|
4
|
+
title: 'Design System/Components/EntityCard/EntityCard',
|
|
5
|
+
component: EntityCard,
|
|
6
|
+
parameters: {
|
|
7
|
+
layout: 'centered',
|
|
8
|
+
design: {
|
|
9
|
+
type: 'figspec',
|
|
10
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=6916-20176',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
tags: ['autodocs'],
|
|
14
|
+
argTypes: {
|
|
15
|
+
view: {
|
|
16
|
+
control: 'select',
|
|
17
|
+
options: ['avatar', 'agent'],
|
|
18
|
+
description: 'View variant — drives default width and image aspect ratio',
|
|
19
|
+
table: {
|
|
20
|
+
type: { summary: "'avatar' | 'agent'" },
|
|
21
|
+
defaultValue: { summary: 'avatar' },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
name: {
|
|
25
|
+
control: 'text',
|
|
26
|
+
description: 'Display name shown over the image',
|
|
27
|
+
table: { type: { summary: 'string' } },
|
|
28
|
+
},
|
|
29
|
+
image: {
|
|
30
|
+
control: 'text',
|
|
31
|
+
description: 'Image url',
|
|
32
|
+
table: { type: { summary: 'string' } },
|
|
33
|
+
},
|
|
34
|
+
description: {
|
|
35
|
+
control: 'text',
|
|
36
|
+
description: 'Optional description shown below the image',
|
|
37
|
+
table: { type: { summary: 'string' } },
|
|
38
|
+
},
|
|
39
|
+
showDescription: {
|
|
40
|
+
control: 'boolean',
|
|
41
|
+
description: 'Whether to show the description',
|
|
42
|
+
table: { type: { summary: 'boolean' } },
|
|
43
|
+
},
|
|
44
|
+
showMenu: {
|
|
45
|
+
control: 'boolean',
|
|
46
|
+
description: 'Whether to show the menu on hover',
|
|
47
|
+
table: { type: { summary: 'boolean' } },
|
|
48
|
+
},
|
|
49
|
+
onPreview: {
|
|
50
|
+
description: 'Preview handler — renders the action button on hover',
|
|
51
|
+
table: { type: { summary: 'function' } },
|
|
52
|
+
},
|
|
53
|
+
previewButtonText: {
|
|
54
|
+
control: 'text',
|
|
55
|
+
description: 'Label for the action button; when set it renders text + icon, otherwise icon-only',
|
|
56
|
+
table: { type: { summary: 'string' } },
|
|
57
|
+
},
|
|
58
|
+
previewIcon: {
|
|
59
|
+
control: 'text',
|
|
60
|
+
description: 'Icon for the action button',
|
|
61
|
+
table: {
|
|
62
|
+
type: { summary: 'IconName' },
|
|
63
|
+
defaultValue: { summary: 'Play' },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
showStatusBadge: {
|
|
67
|
+
control: 'boolean',
|
|
68
|
+
description: 'Whether to show the status badge (top-left)',
|
|
69
|
+
table: { type: { summary: 'boolean' } },
|
|
70
|
+
},
|
|
71
|
+
statusBadgeText: {
|
|
72
|
+
control: 'text',
|
|
73
|
+
description: 'Status badge text (e.g. "Active")',
|
|
74
|
+
table: { type: { summary: 'string' } },
|
|
75
|
+
},
|
|
76
|
+
statusBadgeColor: {
|
|
77
|
+
control: 'select',
|
|
78
|
+
options: Object.values(BadgeColor),
|
|
79
|
+
description: 'Status badge color',
|
|
80
|
+
table: {
|
|
81
|
+
type: { summary: 'BadgeColor' },
|
|
82
|
+
defaultValue: { summary: BadgeColor.Green },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
menuItems: {
|
|
86
|
+
description: 'Menu items for the dropdown',
|
|
87
|
+
table: { type: { summary: 'array' } },
|
|
88
|
+
},
|
|
89
|
+
width: {
|
|
90
|
+
control: 'text',
|
|
91
|
+
description: 'Width of the card',
|
|
92
|
+
table: {
|
|
93
|
+
type: { summary: 'string | number' },
|
|
94
|
+
defaultValue: { summary: '218px' },
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
export default meta;
|
|
100
|
+
const avatarImage = 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?fm=jpg&q=60&w=600&ixlib=rb-4.0.3';
|
|
101
|
+
export const Default = {
|
|
102
|
+
args: {
|
|
103
|
+
name: 'Nelly',
|
|
104
|
+
image: avatarImage,
|
|
105
|
+
onPreview: () => { },
|
|
106
|
+
showMenu: true,
|
|
107
|
+
menuItems: [
|
|
108
|
+
{ label: 'Copy Avatar ID', icon: 'Copy', onClick: () => { } },
|
|
109
|
+
{ label: 'Create Agent', icon: 'UserCirclePlus', onClick: () => { } },
|
|
110
|
+
{ label: 'Delete', icon: 'Trash', onClick: () => { }, destructive: true },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
export const AgentView = {
|
|
115
|
+
args: {
|
|
116
|
+
view: 'agent',
|
|
117
|
+
name: 'Nelly Customer Support',
|
|
118
|
+
image: avatarImage,
|
|
119
|
+
onPreview: () => { },
|
|
120
|
+
previewButtonText: 'Join',
|
|
121
|
+
previewIcon: 'VideoCamera',
|
|
122
|
+
showStatusBadge: true,
|
|
123
|
+
statusBadgeText: 'Active',
|
|
124
|
+
showMenu: true,
|
|
125
|
+
menuItems: [
|
|
126
|
+
{ label: 'Conversation Link', icon: 'ShareNetwork', onClick: () => { } },
|
|
127
|
+
{ label: 'Copy Agent ID', icon: 'CopySimple', onClick: () => { } },
|
|
128
|
+
{ label: 'Embed', icon: 'Code', onClick: () => { } },
|
|
129
|
+
{ label: 'Agent Details', icon: 'IdentificationCard', onClick: () => { } },
|
|
130
|
+
{ label: 'Duplicate', icon: 'Copy', onClick: () => { } },
|
|
131
|
+
{ label: 'Delete', icon: 'Trash', onClick: () => { }, destructive: true },
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
parameters: {
|
|
135
|
+
design: {
|
|
136
|
+
type: 'figspec',
|
|
137
|
+
url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=6919-20599',
|
|
138
|
+
},
|
|
139
|
+
docs: {
|
|
140
|
+
description: {
|
|
141
|
+
story: 'The agent view — wider, square image, a persistent status badge, and a labelled "Join" action. Hover to reveal the action row.',
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
export const WithDescription = {
|
|
147
|
+
args: {
|
|
148
|
+
...Default.args,
|
|
149
|
+
name: 'Nelly Customer Support',
|
|
150
|
+
showDescription: true,
|
|
151
|
+
description: 'Support description',
|
|
152
|
+
},
|
|
153
|
+
parameters: {
|
|
154
|
+
docs: {
|
|
155
|
+
description: {
|
|
156
|
+
story: 'Avatar card with an optional description below the image.',
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
export const NoActions = {
|
|
162
|
+
args: {
|
|
163
|
+
name: 'Nelly',
|
|
164
|
+
image: avatarImage,
|
|
165
|
+
},
|
|
166
|
+
parameters: {
|
|
167
|
+
docs: {
|
|
168
|
+
description: {
|
|
169
|
+
story: 'A read-only avatar card without preview or menu actions on hover.',
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
export const NoImage = {
|
|
175
|
+
args: {
|
|
176
|
+
...Default.args,
|
|
177
|
+
name: 'No Image',
|
|
178
|
+
image: undefined,
|
|
179
|
+
},
|
|
180
|
+
parameters: {
|
|
181
|
+
docs: {
|
|
182
|
+
description: {
|
|
183
|
+
story: 'Fallback placeholder shown when no image is provided or it fails to load.',
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from '@mui/material';
|
|
3
|
+
import { SkeletonLoader } from '../UI';
|
|
4
|
+
export const EntityCardSkeleton = ({ view = 'avatar', width, showDescription = false, style, }) => {
|
|
5
|
+
const isAgent = view === 'agent';
|
|
6
|
+
const cardWidth = width ?? (isAgent ? '280px' : '218px');
|
|
7
|
+
const imageAspectRatio = isAgent ? '1 / 1' : '203 / 264';
|
|
8
|
+
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: 'var(--colors-light-background-bg-card-highlight)', border: '1px solid var(--colors-light-border-border-subtle)', boxSizing: 'border-box', overflow: 'hidden', style: style, children: [_jsx(Box, { width: '100%', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', overflow: 'hidden', sx: { aspectRatio: imageAspectRatio }, children: _jsx(SkeletonLoader, {}) }), showDescription && (_jsxs(Box, { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', padding: '0 12px', width: '100%', boxSizing: 'border-box', children: [_jsx(SkeletonLoader, { width: '100%', height: '16px', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)' }), _jsx(SkeletonLoader, { width: '70%', height: '16px', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)' })] }))] }));
|
|
9
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { EntityCardSkeleton } from './EntityCardSkeleton';
|
|
3
|
+
declare const meta: Meta<typeof EntityCardSkeleton>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof EntityCardSkeleton>;
|
|
6
|
+
export declare const Avatar: Story;
|
|
7
|
+
export declare const AvatarWithDescription: Story;
|
|
8
|
+
export declare const Agent: Story;
|
|
9
|
+
export declare const Grid: Story;
|
|
10
|
+
export declare const LoadingState: Story;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from '@mui/material';
|
|
3
|
+
import { EntityCardSkeleton } from './EntityCardSkeleton';
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Design System/Components/EntityCard/EntityCardSkeleton',
|
|
6
|
+
component: EntityCardSkeleton,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'centered',
|
|
9
|
+
},
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
argTypes: {
|
|
12
|
+
view: {
|
|
13
|
+
control: 'select',
|
|
14
|
+
options: ['avatar', 'agent'],
|
|
15
|
+
description: 'View variant — drives default width and image aspect ratio',
|
|
16
|
+
table: {
|
|
17
|
+
type: { summary: "'avatar' | 'agent'" },
|
|
18
|
+
defaultValue: { summary: 'avatar' },
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
width: {
|
|
22
|
+
control: 'text',
|
|
23
|
+
description: 'Width of the card (overrides the view default)',
|
|
24
|
+
table: {
|
|
25
|
+
type: { summary: 'string | number' },
|
|
26
|
+
defaultValue: { summary: '218px (avatar) / 280px (agent)' },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
showDescription: {
|
|
30
|
+
control: 'boolean',
|
|
31
|
+
description: 'Show the description placeholder',
|
|
32
|
+
table: {
|
|
33
|
+
type: { summary: 'boolean' },
|
|
34
|
+
defaultValue: { summary: 'false' },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
style: {
|
|
38
|
+
control: 'object',
|
|
39
|
+
description: 'Custom style for the skeleton',
|
|
40
|
+
table: {
|
|
41
|
+
type: { summary: 'React.CSSProperties' },
|
|
42
|
+
defaultValue: { summary: 'undefined' },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
export default meta;
|
|
48
|
+
export const Avatar = {
|
|
49
|
+
args: {
|
|
50
|
+
view: 'avatar',
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
export const AvatarWithDescription = {
|
|
54
|
+
args: {
|
|
55
|
+
view: 'avatar',
|
|
56
|
+
showDescription: true,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
export const Agent = {
|
|
60
|
+
args: {
|
|
61
|
+
view: 'agent',
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
export const Grid = {
|
|
65
|
+
render: () => (_jsxs(Box, { display: "flex", flexWrap: "wrap", gap: 2, children: [_jsx(EntityCardSkeleton, { view: "avatar" }), _jsx(EntityCardSkeleton, { view: "avatar", showDescription: true }), _jsx(EntityCardSkeleton, { view: "agent" })] })),
|
|
66
|
+
};
|
|
67
|
+
export const LoadingState = {
|
|
68
|
+
render: () => (_jsxs(Box, { display: "flex", flexDirection: "column", gap: 3, children: [_jsxs(Box, { children: [_jsx("h3", { children: "Loading state example" }), _jsx("p", { children: "How a row of entity cards appears while data is loading." })] }), _jsx(Box, { display: "flex", flexWrap: "wrap", gap: 2, children: Array(5)
|
|
69
|
+
.fill(0)
|
|
70
|
+
.map((_, index) => (_jsx(EntityCardSkeleton, { view: "avatar" }, index))) }), _jsx(Box, { display: "flex", flexWrap: "wrap", gap: 2, children: Array(4)
|
|
71
|
+
.fill(0)
|
|
72
|
+
.map((_, index) => (_jsx(EntityCardSkeleton, { view: "agent" }, index))) })] })),
|
|
73
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { MenuItemProps, BadgeColor } from '../UI';
|
|
2
|
+
import { IconName } from '../../utils';
|
|
3
|
+
/**
|
|
4
|
+
* Entity card view variants.
|
|
5
|
+
* - `avatar`: portrait avatar tile (icon-only preview action)
|
|
6
|
+
* - `agent`: wider, square agent tile (labelled action + status badge)
|
|
7
|
+
*/
|
|
8
|
+
export type EntityCardView = 'avatar' | 'agent';
|
|
9
|
+
/**
|
|
10
|
+
* Entity card component props
|
|
11
|
+
*/
|
|
12
|
+
export interface EntityCardProps {
|
|
13
|
+
/** View variant — drives default width and image aspect ratio */
|
|
14
|
+
view?: EntityCardView;
|
|
15
|
+
/** Display name shown over the image */
|
|
16
|
+
name: string;
|
|
17
|
+
/** Image url */
|
|
18
|
+
image?: string;
|
|
19
|
+
/** Optional description shown below the image */
|
|
20
|
+
description?: string;
|
|
21
|
+
/** Whether to show the description */
|
|
22
|
+
showDescription?: boolean;
|
|
23
|
+
/** Preview handler — renders the action button on hover */
|
|
24
|
+
onPreview?: () => void;
|
|
25
|
+
/** Label for the action button; when set it renders text + icon, otherwise icon-only */
|
|
26
|
+
previewButtonText?: string;
|
|
27
|
+
/** Icon for the action button (defaults to `Play`) */
|
|
28
|
+
previewIcon?: IconName;
|
|
29
|
+
/** Whether to show the status badge (top-left) */
|
|
30
|
+
showStatusBadge?: boolean;
|
|
31
|
+
/** Status badge text (e.g. "Active") */
|
|
32
|
+
statusBadgeText?: string;
|
|
33
|
+
/** Status badge color (defaults to Green) */
|
|
34
|
+
statusBadgeColor?: BadgeColor;
|
|
35
|
+
/** Menu items for the dropdown — renders the menu button on hover */
|
|
36
|
+
menuItems?: MenuItemProps[];
|
|
37
|
+
/** Whether to show the menu */
|
|
38
|
+
showMenu?: boolean;
|
|
39
|
+
/** Width of the card (overrides the view default) */
|
|
40
|
+
width?: string | number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Entity card skeleton props
|
|
44
|
+
*/
|
|
45
|
+
export interface EntityCardSkeletonProps {
|
|
46
|
+
/** View variant — drives default width and image aspect ratio */
|
|
47
|
+
view?: EntityCardView;
|
|
48
|
+
/** Width of the card (overrides the view default) */
|
|
49
|
+
width?: string | number;
|
|
50
|
+
/** Show the description placeholder */
|
|
51
|
+
showDescription?: boolean;
|
|
52
|
+
/** Custom style for the skeleton */
|
|
53
|
+
style?: React.CSSProperties;
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,23 +1,57 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
3
|
import { Box } from '@mui/material';
|
|
4
4
|
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
5
5
|
import { HintBubble, HintBubbleSize } from '../UI';
|
|
6
|
-
|
|
6
|
+
import { getIconComponent } from '../../utils';
|
|
7
|
+
export const TextInput = ({ label, value, onChange, placeholder, type = 'text', hintText, errorText, disabled, trailingText, style, hintBubbleText, hintBubbleProps, required, labelTrailingElement, labelContainerStyle, showPasswordToggle = true, }) => {
|
|
7
8
|
const [focused, setFocused] = useState(false);
|
|
9
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
10
|
+
const isPassword = type === 'password';
|
|
11
|
+
const enablePasswordToggle = isPassword && showPasswordToggle;
|
|
12
|
+
const inputType = enablePasswordToggle && showPassword ? 'text' : type;
|
|
13
|
+
// Reset visibility whenever the toggle is removed, so the field never
|
|
14
|
+
// reappears revealed after `type` switches away from password or
|
|
15
|
+
// `showPasswordToggle` is turned off.
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!enablePasswordToggle)
|
|
18
|
+
setShowPassword(false);
|
|
19
|
+
}, [enablePasswordToggle]);
|
|
20
|
+
const togglePasswordVisibility = () => {
|
|
21
|
+
if (!disabled)
|
|
22
|
+
setShowPassword((prev) => !prev);
|
|
23
|
+
};
|
|
24
|
+
const iconColor = disabled
|
|
25
|
+
? 'var(--colors-light-text-text-disabled)'
|
|
26
|
+
: 'var(--colors-light-foreground-fg-secondary)';
|
|
8
27
|
return (_jsx(Box, { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', sx: style, children: _jsxs(Box, { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)', minWidth: '260px', children: [label && (_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', sx: labelContainerStyle, children: [_jsx(Typography, { size: TypographySize.HeadingXS, weight: TypographyWeight.SemiBold, required: required, children: label }), labelTrailingElement] })), hintBubbleText && (_jsx(HintBubble, { text: hintBubbleText, size: HintBubbleSize.sm, ...hintBubbleProps })), _jsxs(Box, { display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', bgcolor: 'var(--colors-light-background-bg-component)', border: errorText
|
|
9
28
|
? 'none'
|
|
10
|
-
: '1px solid var(--colors-light-border-border-component)', overflow: 'hidden', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', children: [
|
|
29
|
+
: '1px solid var(--colors-light-border-border-component)', overflow: 'hidden', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)', width: '100%', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs) 0 0 var(--spacing-tokens-size-in-px-spacing-spacing-xs)', border: `${errorText
|
|
11
30
|
? '1px solid var(--colors-light-border-border-error)'
|
|
12
31
|
: focused
|
|
13
32
|
? '1px solid var(--colors-light-border-border-component-active)'
|
|
14
|
-
: 'transparent'}`, children: _jsx("input", { type:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
: 'transparent'}`, children: [_jsx("input", { type: inputType, value: value, onChange: (e) => onChange(e.target.value), placeholder: placeholder, disabled: disabled, style: {
|
|
34
|
+
border: 'none',
|
|
35
|
+
outline: 'none',
|
|
36
|
+
width: '100%',
|
|
37
|
+
color: disabled
|
|
38
|
+
? 'var(--colors-light-text-text-disabled)'
|
|
39
|
+
: 'var(--colors-light-text-text-title)',
|
|
40
|
+
backgroundColor: 'transparent',
|
|
41
|
+
}, onFocus: () => setFocused(true), onBlur: () => setFocused(false), required: required }), enablePasswordToggle && (_jsx(Box, { component: 'button', type: 'button', onClick: togglePasswordVisibility, disabled: disabled, "aria-label": showPassword ? 'Hide password' : 'Show password', "aria-pressed": showPassword, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, sx: {
|
|
42
|
+
padding: 0,
|
|
43
|
+
border: 'none',
|
|
44
|
+
borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
|
|
45
|
+
backgroundColor: 'transparent',
|
|
46
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
47
|
+
'&:focus-visible': {
|
|
48
|
+
outline: '2px solid var(--colors-light-border-border-component-active)',
|
|
49
|
+
outlineOffset: '2px',
|
|
50
|
+
},
|
|
51
|
+
}, children: getIconComponent(showPassword ? 'EyeSlash' : 'Eye', {
|
|
52
|
+
width: '20px',
|
|
53
|
+
height: '20px',
|
|
54
|
+
color: iconColor,
|
|
55
|
+
'aria-hidden': 'true',
|
|
56
|
+
}) }))] }), trailingText && (_jsx(Box, { display: 'flex', alignItems: 'center', height: '100%', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)', borderLeft: '1px solid var(--colors-light-border-border-component)', children: _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: 'var(--colors-light-text-text-paragraph)', children: trailingText }) }))] }), errorText && (_jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: 'var(--colors-light-text-text-error)', children: errorText })), hintText && !errorText && (_jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: 'var(--colors-light-text-text-subtle)', children: hintText }))] }) }));
|
|
23
57
|
};
|
|
@@ -133,10 +133,23 @@ declare const meta: {
|
|
|
133
133
|
};
|
|
134
134
|
};
|
|
135
135
|
};
|
|
136
|
+
showPasswordToggle: {
|
|
137
|
+
control: "boolean";
|
|
138
|
+
description: string;
|
|
139
|
+
table: {
|
|
140
|
+
type: {
|
|
141
|
+
summary: string;
|
|
142
|
+
};
|
|
143
|
+
defaultValue: {
|
|
144
|
+
summary: string;
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
};
|
|
136
148
|
};
|
|
137
149
|
};
|
|
138
150
|
export default meta;
|
|
139
151
|
type Story = StoryObj<typeof meta>;
|
|
140
152
|
export declare const Default: Story;
|
|
141
153
|
export declare const Password: Story;
|
|
154
|
+
export declare const PasswordWithoutToggle: Story;
|
|
142
155
|
export declare const Email: Story;
|