@beydesign/storybook 0.4.1 → 0.4.3

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.
@@ -4,53 +4,107 @@ import styled from '@emotion/styled';
4
4
  import { ButtonSize, ButtonHierarchy, ButtonState, ButtonShape, ButtonDisplayMode, ButtonType, } from './types';
5
5
  import { Typography, TypographySize, TypographyWeight } from '../Typography';
6
6
  import { getIconComponent } from '../../utils';
7
+ const isDisabled = (props) => props.$state === ButtonState.Disabled || props.$disabled;
8
+ const getTextColor = (props) => {
9
+ if (props.$destructive) {
10
+ if (isDisabled(props))
11
+ return 'var(--color-text-text-disabled)';
12
+ return 'var(--color-text-text-white)';
13
+ }
14
+ if (isDisabled(props))
15
+ return 'var(--color-text-text-inactive)';
16
+ switch (props.$hierarchy) {
17
+ case ButtonHierarchy.Primary:
18
+ return 'var(--color-text-text-on-accent-color)';
19
+ case ButtonHierarchy.PrimaryLemon:
20
+ return 'var(--color-text-text-title)';
21
+ case ButtonHierarchy.Secondary:
22
+ return 'var(--color-text-text-title)';
23
+ case ButtonHierarchy.SecondaryColor:
24
+ return 'var(--color-text-text-clicable)';
25
+ case ButtonHierarchy.SecondaryTransparent:
26
+ return 'var(--color-text-text-white)';
27
+ case ButtonHierarchy.Tertiary:
28
+ return 'var(--color-text-text-title)';
29
+ case ButtonHierarchy.TertiaryColor:
30
+ return 'var(--color-text-text-clicable)';
31
+ case ButtonHierarchy.WhiteText:
32
+ return 'var(--color-text-text-white)';
33
+ default:
34
+ return 'var(--color-text-text-white)';
35
+ }
36
+ };
37
+ const ICON_ONLY_SIZE = {
38
+ [ButtonShape.Square]: {
39
+ [ButtonSize.xxs]: 32,
40
+ [ButtonSize.xs]: 36,
41
+ [ButtonSize.sm]: 40,
42
+ [ButtonSize.md]: 44,
43
+ [ButtonSize.lg]: 48,
44
+ [ButtonSize.xl]: 56,
45
+ },
46
+ [ButtonShape.Circle]: {
47
+ [ButtonSize.xxs]: 28,
48
+ [ButtonSize.xs]: 32,
49
+ [ButtonSize.sm]: 36,
50
+ [ButtonSize.md]: 40,
51
+ [ButtonSize.lg]: 44,
52
+ [ButtonSize.xl]: 52,
53
+ },
54
+ };
7
55
  const StyledButton = styled.button `
8
56
  text-transform: none;
57
+ box-sizing: border-box;
9
58
  padding: ${(props) => props.$displayIconMode === ButtonDisplayMode.Only
10
- ? 'var(--spacing-tokens-size-in-px-spacing-spacing-md)'
59
+ ? '0'
11
60
  : props.$sizeStyles.padding};
61
+ ${(props) => {
62
+ if (props.$displayIconMode !== ButtonDisplayMode.Only)
63
+ return '';
64
+ const box = `${ICON_ONLY_SIZE[props.$shape][props.$size]}px`;
65
+ return `width: ${box};
66
+ height: ${box};`;
67
+ }}
12
68
  font-size: ${(props) => props.$sizeStyles.fontSize};
13
69
  border-radius: ${(props) => props.$shape === ButtonShape.Square
14
70
  ? 'var(--spacing-tokens-size-in-px-radius-radius-xs)'
15
71
  : 'var(--spacing-tokens-size-in-px-spacing-spacing-11xl)'};
16
72
  background: ${(props) => {
17
73
  if (props.$destructive) {
18
- if (props.$state === ButtonState.Disabled || props.$disabled)
74
+ // Destructive is the one hierarchy Figma gives its own disabled fill,
75
+ // rather than the shared bg-button-disabled every other one uses.
76
+ if (isDisabled(props))
19
77
  return 'var(--color-background-bg-element)';
20
78
  return 'var(--color-background-bg-error)';
21
79
  }
80
+ if (isDisabled(props)) {
81
+ switch (props.$hierarchy) {
82
+ case ButtonHierarchy.Tertiary:
83
+ case ButtonHierarchy.TertiaryColor:
84
+ return 'transparent';
85
+ case ButtonHierarchy.WhiteText:
86
+ // Stays a scrim when disabled so it reads over media.
87
+ return 'var(--primitives-white-primary-black-10)';
88
+ default:
89
+ return 'var(--color-background-bg-button-disabled)';
90
+ }
91
+ }
22
92
  switch (props.$hierarchy) {
23
93
  case ButtonHierarchy.Primary:
24
- if (props.$state === ButtonState.Disabled || props.$disabled)
25
- return 'var(--color-background-bg-button-disabled)';
26
94
  return 'var(--color-background-bg-brand-primary)';
27
95
  case ButtonHierarchy.PrimaryLemon:
28
- if (props.$state === ButtonState.Disabled || props.$disabled)
29
- return 'var(--color-background-bg-element)';
30
96
  return 'var(--color-background-bg-brand-secondary)';
31
97
  case ButtonHierarchy.Secondary:
32
- if (props.$state === ButtonState.Disabled || props.$disabled)
33
- return 'var(--color-background-bg-button-disabled)';
34
98
  return 'transparent';
35
99
  case ButtonHierarchy.SecondaryColor:
36
- if (props.$state === ButtonState.Disabled || props.$disabled)
37
- return 'var(--color-background-bg-element)';
38
100
  return 'var(--color-background-bg-secondary-action)';
39
101
  case ButtonHierarchy.SecondaryTransparent:
40
- if (props.$state === ButtonState.Disabled || props.$disabled)
41
- return 'var(--color-background-bg-overlay-1)';
42
102
  return 'var(--color-background-bg-button-transparent)';
43
103
  case ButtonHierarchy.Tertiary:
44
- if (props.$state === ButtonState.Disabled || props.$disabled)
45
- return 'transparent';
46
104
  return 'transparent';
47
105
  case ButtonHierarchy.TertiaryColor:
48
- if (props.$state === ButtonState.Disabled || props.$disabled)
49
- return 'transparent';
50
106
  return 'transparent';
51
107
  case ButtonHierarchy.WhiteText:
52
- if (props.$state === ButtonState.Disabled || props.$disabled)
53
- return 'var(--color-background-bg-button-disabled)';
54
108
  // Figma "White-text" fill is the Primary/Black/40 primitive
55
109
  // (rgba(0,0,0,0.4)) — a dark scrim over media, not an overlay token.
56
110
  return 'var(--primitives-white-primary-black-40)';
@@ -58,89 +112,43 @@ const StyledButton = styled.button `
58
112
  return 'var(--color-background-bg-brand-primary)';
59
113
  }
60
114
  }};
61
- color: ${(props) => {
62
- if (props.$destructive) {
63
- if (props.$state === ButtonState.Disabled || props.$disabled)
64
- return 'var(--color-text-text-disabled)';
65
- return 'var(--color-text-text-white)';
66
- }
67
- switch (props.$hierarchy) {
68
- case ButtonHierarchy.Primary:
69
- if (props.$state === ButtonState.Disabled || props.$disabled)
70
- return 'var(--color-text-text-inactive)';
71
- return 'var(--color-text-text-on-accent-color)';
72
- case ButtonHierarchy.PrimaryLemon:
73
- if (props.$state === ButtonState.Disabled || props.$disabled)
74
- return 'var(--color-text-text-subtle)';
75
- return 'var(--color-text-text-title)';
76
- case ButtonHierarchy.Secondary:
77
- if (props.$state === ButtonState.Disabled || props.$disabled)
78
- return 'var(--color-text-text-inactive)';
79
- return 'var(--color-text-text-title)';
80
- case ButtonHierarchy.SecondaryColor:
81
- if (props.$state === ButtonState.Disabled || props.$disabled)
82
- return 'var(--color-text-text-disabled)';
83
- return 'var(--color-text-text-clicable)';
84
- case ButtonHierarchy.SecondaryTransparent:
85
- if (props.$state === ButtonState.Disabled || props.$disabled)
86
- return 'var(--color-text-text-white)';
87
- return 'var(--color-text-text-white)';
88
- case ButtonHierarchy.Tertiary:
89
- if (props.$state === ButtonState.Disabled || props.$disabled)
90
- return 'var(--color-text-text-disabled)';
91
- return 'var(--color-text-text-title)';
92
- case ButtonHierarchy.TertiaryColor:
93
- if (props.$state === ButtonState.Disabled || props.$disabled)
94
- return 'var(--color-text-text-disabled)';
95
- return 'var(--color-text-text-clicable)';
96
- case ButtonHierarchy.WhiteText:
97
- if (props.$state === ButtonState.Disabled || props.$disabled)
98
- return 'var(--color-text-text-inactive)';
99
- return 'var(--color-text-text-white)';
100
- default:
101
- return 'var(--color-text-text-white)';
102
- }
103
- }};
115
+ color: ${getTextColor};
104
116
  border: 1px solid
105
117
  ${(props) => {
106
118
  if (props.$destructive) {
107
- if (props.$state === ButtonState.Disabled || props.$disabled)
119
+ if (isDisabled(props))
108
120
  return 'var(--color-background-bg-element)';
109
121
  return 'var(--color-background-bg-error)';
110
122
  }
123
+ if (isDisabled(props)) {
124
+ switch (props.$hierarchy) {
125
+ case ButtonHierarchy.Tertiary:
126
+ case ButtonHierarchy.TertiaryColor:
127
+ case ButtonHierarchy.WhiteText:
128
+ return 'transparent';
129
+ default:
130
+ return 'var(--color-border-border-disabled)';
131
+ }
132
+ }
111
133
  switch (props.$hierarchy) {
112
134
  case ButtonHierarchy.Primary:
113
- if (props.$state === ButtonState.Disabled || props.$disabled)
114
- return 'var(--color-border-border-disabled)';
115
- return 'var(--color-background-bg-brand-primary)';
135
+ return 'var(--color-border-border-brand)';
116
136
  case ButtonHierarchy.PrimaryLemon:
117
- if (props.$state === ButtonState.Disabled || props.$disabled)
118
- return 'var(--color-border-border-component)';
119
137
  return 'var(--color-background-bg-brand-secondary)';
120
138
  case ButtonHierarchy.Secondary:
121
- if (props.$state === ButtonState.Disabled || props.$disabled)
122
- return 'var(--color-border-border-disabled)';
123
139
  return 'var(--color-border-border-component)';
124
140
  case ButtonHierarchy.SecondaryColor:
125
- if (props.$state === ButtonState.Disabled || props.$disabled)
126
- return 'var(--color-border-border-component)';
127
141
  return 'var(--color-border-border-secondary-action)';
128
142
  case ButtonHierarchy.SecondaryTransparent:
129
- if (props.$state === ButtonState.Disabled || props.$disabled)
130
- return 'var(--color-border-border-disabled)';
131
143
  return 'transparent';
132
144
  case ButtonHierarchy.Tertiary:
133
- if (props.$state === ButtonState.Disabled || props.$disabled)
134
- return 'transparent';
135
145
  return 'transparent';
136
146
  case ButtonHierarchy.TertiaryColor:
137
- if (props.$state === ButtonState.Disabled || props.$disabled)
138
- return 'transparent';
139
147
  return 'transparent';
140
148
  case ButtonHierarchy.WhiteText:
141
149
  return 'transparent';
142
150
  default:
143
- return 'var(--color-background-bg-brand-primary)';
151
+ return 'var(--color-border-border-brand)';
144
152
  }
145
153
  }};
146
154
  display: flex;
@@ -181,9 +189,9 @@ const StyledButton = styled.button `
181
189
  case ButtonHierarchy.SecondaryTransparent:
182
190
  return 'var(--color-background-bg-button-transparent-hover)';
183
191
  case ButtonHierarchy.Tertiary:
184
- return 'var(--color-background-bg-component-hover)';
192
+ return 'var(--color-background-bg-button-overlay)';
185
193
  case ButtonHierarchy.TertiaryColor:
186
- return 'var(--color-background-bg-card-highlight-hover)';
194
+ return 'var(--primitives-white-primary-white-10)';
187
195
  case ButtonHierarchy.WhiteText:
188
196
  // Darken the black-40 scrim one step on hover (no black-50 exists).
189
197
  return 'var(--primitives-white-primary-black-60)';
@@ -191,6 +199,9 @@ const StyledButton = styled.button `
191
199
  return 'var(--color-background-bg-brand-primary-hover)';
192
200
  }
193
201
  }};
202
+ color: ${(props) => !props.$destructive && props.$hierarchy === ButtonHierarchy.TertiaryColor
203
+ ? 'var(--color-text-text-clicable-hover)'
204
+ : getTextColor(props)};
194
205
  border-color: ${(props) => {
195
206
  if (props.$destructive) {
196
207
  return 'var(--primitives-desktop-primary-red-700)';
@@ -224,58 +235,38 @@ const StyledButton = styled.button `
224
235
  }
225
236
  `;
226
237
  export const MainButton = ({ onClick, text, disabled = false, loading = false, type = ButtonType.Button, style, size = ButtonSize.xl, hierarchy = ButtonHierarchy.Primary, displayIcon, iconWeight = 'regular', displayIconMode = ButtonDisplayMode.Default, state = ButtonState.Default, shape = ButtonShape.Square, destructive = false, showLeadingIcon = false, showTrailingIcon = false, leadingIcon = 'Circle', trailingIcon = 'Circle', testId, }) => {
227
- const getIconSize = () => {
228
- const sizes = {
229
- [ButtonSize.xl]: 24,
230
- [ButtonSize.lg]: 22,
231
- [ButtonSize.md]: 20,
232
- [ButtonSize.sm]: 18,
233
- [ButtonSize.xs]: 16,
234
- [ButtonSize.xxs]: 14,
235
- };
236
- return sizes[size] || sizes[ButtonSize.xl];
237
- };
238
+ // Figma draws a 20px icon at every button size.
239
+ const iconSize = 20;
240
+ // Icon-only buttons take their box from ICON_ONLY_SIZE and ignore padding.
238
241
  const getButtonSize = () => {
239
242
  const sizes = {
240
243
  [ButtonSize.xl]: {
241
- padding: displayIconMode === ButtonDisplayMode.Only
242
- ? 'var(--spacing-tokens-size-in-px-spacing-spacing-md)'
243
- : 'var(--spacing-tokens-size-in-px-spacing-spacing-lg) var(--spacing-tokens-size-in-px-spacing-spacing-3xl)',
244
+ padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg) var(--spacing-tokens-size-in-px-spacing-spacing-3xl)',
244
245
  fontSize: 'var(--global-font-size-3)',
245
246
  gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)',
246
247
  },
247
248
  [ButtonSize.lg]: {
248
- padding: displayIconMode === ButtonDisplayMode.Only
249
- ? 'var(--spacing-tokens-size-in-px-spacing-spacing-md)'
250
- : 'var(--spacing-tokens-size-in-px-spacing-spacing-lg) var(--spacing-tokens-size-in-px-spacing-spacing-2xl)',
249
+ padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg) var(--spacing-tokens-size-in-px-spacing-spacing-2xl)',
251
250
  fontSize: 'var(--global-font-size-3)',
252
251
  gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)',
253
252
  },
254
253
  [ButtonSize.md]: {
255
- padding: displayIconMode === ButtonDisplayMode.Only
256
- ? 'var(--spacing-tokens-size-in-px-spacing-spacing-md)'
257
- : 'var(--spacing-tokens-size-in-px-spacing-spacing-md) var(--spacing-tokens-size-in-px-spacing-spacing-xl)',
254
+ padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-md) var(--spacing-tokens-size-in-px-spacing-spacing-xl)',
258
255
  fontSize: 'var(--global-font-size-2)',
259
256
  gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)',
260
257
  },
261
258
  [ButtonSize.sm]: {
262
- padding: displayIconMode === ButtonDisplayMode.Only
263
- ? 'var(--spacing-tokens-size-in-px-spacing-spacing-md)'
264
- : 'var(--spacing-tokens-size-in-px-spacing-spacing-md) var(--spacing-tokens-size-in-px-spacing-spacing-xl)',
259
+ padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-md) var(--spacing-tokens-size-in-px-spacing-spacing-xl)',
265
260
  fontSize: 'var(--global-font-size-1)',
266
261
  gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
267
262
  },
268
263
  [ButtonSize.xs]: {
269
- padding: displayIconMode === ButtonDisplayMode.Only
270
- ? 'var(--spacing-tokens-size-in-px-spacing-spacing-md)'
271
- : 'var(--spacing-tokens-size-in-px-spacing-spacing-md) var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
264
+ padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-md) var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
272
265
  fontSize: 'var(--global-font-size-1)',
273
266
  gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
274
267
  },
275
268
  [ButtonSize.xxs]: {
276
- padding: displayIconMode === ButtonDisplayMode.Only
277
- ? 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)'
278
- : 'var(--spacing-tokens-size-in-px-spacing-spacing-sm) var(--spacing-tokens-size-in-px-spacing-spacing-md)',
269
+ padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm) var(--spacing-tokens-size-in-px-spacing-spacing-md)',
279
270
  fontSize: 'var(--global-font-size-0)',
280
271
  gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
281
272
  },
@@ -283,7 +274,7 @@ export const MainButton = ({ onClick, text, disabled = false, loading = false, t
283
274
  return sizes[size] || sizes[ButtonSize.xl];
284
275
  };
285
276
  const iconProps = {
286
- size: getIconSize(),
277
+ size: iconSize,
287
278
  color: disabled ? 'var(--color-text-text-disabled)' : 'currentColor',
288
279
  weight: iconWeight,
289
280
  };
@@ -18,6 +18,8 @@ const ASPECT_RATIO_BY_VIEW = {
18
18
  agent: '1 / 1',
19
19
  video: '16 / 9',
20
20
  };
21
+ /** Hook the root's hover/focus rules use to reveal the action buttons. */
22
+ const ACTIONS_CLASS = 'EntityCard-actions';
21
23
  const spin = keyframes `
22
24
  from { transform: rotate(0deg); }
23
25
  to { transform: rotate(360deg); }
@@ -34,19 +36,44 @@ export const EntityCard = ({ view = 'avatar', name, image, description = '', sho
34
36
  const hasMenu = showMenu && menuItems.length > 0;
35
37
  const isProcessing = processing && !error;
36
38
  const canPreview = !!onPreview && !isProcessing && !error;
37
- const showActions = hovered && (canPreview || hasMenu);
39
+ const showActions = canPreview || hasMenu;
38
40
  const showBadge = showStatusBadge && !!statusBadgeText;
39
41
  const showImage = !!image && !imageError;
40
42
  const resolvedTestId = testId ?? `${entityKey}-${isVideo ? 'video' : 'entity'}-card`;
43
+ const handlePreviewButtonClick = (event) => {
44
+ event.stopPropagation();
45
+ onPreview?.();
46
+ };
41
47
  return (_jsxs(Box, { width: cardWidth, display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', bgcolor: hovered
42
48
  ? 'var(--color-background-bg-card-highlight-hover)'
43
- : 'var(--color-background-bg-card-highlight)', border: '1px solid var(--color-border-border-subtle)', boxSizing: 'border-box', overflow: 'hidden', onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), sx: { transition: 'background-color 0.15s ease-in-out' }, "data-testid": resolvedTestId, children: [_jsxs(Box, { position: 'relative', width: '100%', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', overflow: 'hidden', sx: { aspectRatio: imageAspectRatio }, children: [showImage ? (_jsx("img", { src: image, alt: name, width: '100%', height: '100%', style: { objectFit: 'cover', height: '100%', width: '100%' }, onError: () => setImageError(true) })) : (_jsx(Box, { width: '100%', height: '100%', bgcolor: 'var(--color-background-bg-element)', display: 'flex', justifyContent: 'center', alignItems: 'center', children: isVideo ? (_jsx(FilmSlate, { size: 64, weight: 'thin', color: 'var(--color-text-text-subtle)' })) : (_jsx(UserCircle, { size: 74, weight: 'thin', color: 'var(--color-text-text-subtle)' })) })), _jsx(Box, { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', sx: {
49
+ : 'var(--color-background-bg-card-highlight)', border: '1px solid var(--color-border-border-subtle)', boxSizing: 'border-box', overflow: 'hidden', onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), role: canPreview ? 'button' : undefined, tabIndex: canPreview ? 0 : undefined, "aria-label": canPreview ? (previewButtonText ?? name) : undefined, onClick: canPreview ? onPreview : undefined, onKeyDown: canPreview
50
+ ? (event) => {
51
+ if (event.key === 'Enter' || event.key === ' ') {
52
+ event.preventDefault();
53
+ onPreview?.();
54
+ }
55
+ }
56
+ : undefined, sx: {
57
+ transition: 'background-color 0.15s ease-in-out',
58
+ cursor: canPreview ? 'pointer' : 'default',
59
+ [`&:hover .${ACTIONS_CLASS}, &:focus-within .${ACTIONS_CLASS}`]: {
60
+ opacity: 1,
61
+ pointerEvents: 'auto',
62
+ },
63
+ }, "data-testid": resolvedTestId, children: [_jsxs(Box, { position: 'relative', width: '100%', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', overflow: 'hidden', sx: { aspectRatio: imageAspectRatio }, children: [showImage ? (_jsx("img", { src: image, alt: name, width: '100%', height: '100%', style: { objectFit: 'cover', height: '100%', width: '100%' }, onError: () => setImageError(true) })) : (_jsx(Box, { width: '100%', height: '100%', bgcolor: 'var(--color-background-bg-element)', display: 'flex', justifyContent: 'center', alignItems: 'center', children: isVideo ? (_jsx(FilmSlate, { size: 64, weight: 'thin', color: 'var(--color-text-text-subtle)' })) : (_jsx(UserCircle, { size: 74, weight: 'thin', color: 'var(--color-text-text-subtle)' })) })), _jsx(Box, { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', sx: {
44
64
  backgroundImage: 'linear-gradient(180deg, rgba(21, 0, 73, 0) 0%, rgba(72, 29, 180, 0.152) 16.76%, rgba(32, 13, 82, 0.36) 100%)',
45
65
  opacity: hovered && !isProcessing && !error ? 1 : 0,
46
66
  transition: 'opacity 0.15s ease-in-out',
47
67
  pointerEvents: 'none',
48
- } }), isProcessing && (_jsxs(Box, { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', bgcolor: 'var(--color-background-bg-overlay-2)', sx: { zIndex: 1 }, children: [_jsx(Spinner, {}), _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.SemiBold, color: 'var(--color-text-text-white)', children: processingText })] })), error && (_jsxs(Box, { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', bgcolor: 'var(--color-background-bg-error-solid)', sx: { zIndex: 3, opacity: 0.7 }, children: [_jsx(Warning, { size: 74, color: 'var(--color-text-text-white)' }), _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.SemiBold, color: 'var(--color-text-text-white)', children: errorText })] })), showBadge && (_jsx(Box, { position: 'absolute', top: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', left: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', sx: { zIndex: 2 }, children: _jsx(Badge, { text: statusBadgeText, color: statusBadgeColor, size: BadgeSize.md, showIcon: true, icon: 'Circle', iconWeight: 'fill', iconSize: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', iconColor: 'var(--color-foreground-fg-accent-success)' }) })), showActions && (_jsxs(Box, { position: 'absolute', top: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', right: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', sx: { zIndex: 4 }, children: [canPreview &&
49
- (previewButtonText ? (_jsx(MainButton, { text: previewButtonText, showLeadingIcon: true, leadingIcon: previewIcon, iconWeight: 'fill', hierarchy: ButtonHierarchy.SecondaryTransparent, size: ButtonSize.sm, onClick: onPreview, testId: `${entityKey}-preview-button` })) : (_jsx(MainButton, { displayIcon: previewIcon, displayIconMode: ButtonDisplayMode.Only, hierarchy: ButtonHierarchy.SecondaryTransparent, size: ButtonSize.sm, onClick: onPreview, testId: `${entityKey}-preview-button` }))), hasMenu && (_jsx(Menu, { items: menuItems, testKey: entityKey, size: ButtonSize.sm }))] })), isVideo ? (_jsxs(Box, { position: 'absolute', left: 0, right: 0, bottom: 0, display: 'flex', flexDirection: 'row', alignItems: 'flex-end', justifyContent: 'space-between', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-3xl) var(--spacing-tokens-size-in-px-spacing-spacing-lg) var(--spacing-tokens-size-in-px-spacing-spacing-lg)', sx: {
68
+ } }), isProcessing && (_jsxs(Box, { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', bgcolor: 'var(--color-background-bg-overlay-2)', sx: { zIndex: 1 }, children: [_jsx(Spinner, {}), _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.SemiBold, color: 'var(--color-text-text-white)', children: processingText })] })), error && (_jsxs(Box, { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', bgcolor: 'var(--color-background-bg-error-solid)', sx: { zIndex: 3, opacity: 0.7 }, children: [_jsx(Warning, { size: 74, color: 'var(--color-text-text-white)' }), _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.SemiBold, color: 'var(--color-text-text-white)', children: errorText })] })), showBadge && (_jsx(Box, { position: 'absolute', top: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', left: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', sx: { zIndex: 2 }, children: _jsx(Badge, { text: statusBadgeText, color: statusBadgeColor, size: BadgeSize.md, showIcon: true, icon: 'Circle', iconWeight: 'fill', iconSize: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)' }) })), showActions && (_jsxs(Box, { className: ACTIONS_CLASS, position: 'absolute', top: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', right: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', sx: {
69
+ zIndex: 4,
70
+ transition: 'opacity 0.15s ease-in-out',
71
+ '@media (hover: hover)': {
72
+ opacity: 0,
73
+ pointerEvents: 'none',
74
+ },
75
+ }, children: [canPreview &&
76
+ (previewButtonText ? (_jsx(MainButton, { text: previewButtonText, showLeadingIcon: true, leadingIcon: previewIcon, iconWeight: 'fill', hierarchy: ButtonHierarchy.SecondaryTransparent, size: ButtonSize.sm, onClick: handlePreviewButtonClick, testId: `${entityKey}-preview-button` })) : (_jsx(MainButton, { displayIcon: previewIcon, displayIconMode: ButtonDisplayMode.Only, hierarchy: ButtonHierarchy.SecondaryTransparent, size: ButtonSize.sm, onClick: handlePreviewButtonClick, testId: `${entityKey}-preview-button` }))), hasMenu && (_jsx(Box, { onClick: (event) => event.stopPropagation(), children: _jsx(Menu, { items: menuItems, testKey: entityKey, size: ButtonSize.sm }) }))] })), isVideo ? (_jsxs(Box, { position: 'absolute', left: 0, right: 0, bottom: 0, display: 'flex', flexDirection: 'row', alignItems: 'flex-end', justifyContent: 'space-between', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-3xl) var(--spacing-tokens-size-in-px-spacing-spacing-lg) var(--spacing-tokens-size-in-px-spacing-spacing-lg)', sx: {
50
77
  zIndex: 2,
51
78
  pointerEvents: 'none',
52
79
  backgroundImage: 'linear-gradient(rgba(45, 38, 55, 0) 0%, rgba(45, 38, 55, 0.16) 34%, rgba(45, 38, 55, 0.64) 100%)',
@@ -118,7 +118,7 @@ export const AutoComplete = ({ options, value, onChange, leadingIcon = 'Magnifyi
118
118
  }, [options, searchText]);
119
119
  const sizeToken = getTypographySizeToken(TypographySize.TextS);
120
120
  const weightToken = getTypographyWeightToken(TypographyWeight.Regular);
121
- const { fontSize, fontWeight, fontFamily, lineHeight, letterSpacing, paragraphIndent, paragraphSpacing, textDecoration, textTransform, } = getTextStyles(sizeToken, weightToken);
121
+ const { fontSize, fontWeight, fontFamily, lineHeight, paragraphIndent, textDecoration, textTransform, } = getTextStyles(sizeToken, weightToken);
122
122
  return (_jsxs(Box, { display: 'flex', flexDirection: 'column', position: 'relative', width: "100%", children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', alignItems: 'center', justifyContent: 'center', border: `1px solid ${focused ? 'var(--color-border-border-component-active)' : 'var(--color-border-border-component)'}`, borderRadius: '4px', bgcolor: disabled
123
123
  ? 'var(--color-background-bg-component-disabled)'
124
124
  : 'var(--color-background-bg-component)', children: [getIconComponent(leadingIcon, {
@@ -133,10 +133,8 @@ export const AutoComplete = ({ options, value, onChange, leadingIcon = 'Magnifyi
133
133
  fontSize: fontSize,
134
134
  fontWeight: fontWeight,
135
135
  fontFamily: fontFamily + ', sans-serif',
136
- lineHeight: lineHeight + 'px',
137
- letterSpacing: letterSpacing,
136
+ lineHeight: lineHeight,
138
137
  textIndent: paragraphIndent,
139
- marginBottom: paragraphSpacing,
140
138
  textTransform: textTransform,
141
139
  textDecoration: textDecoration,
142
140
  color: disabled
@@ -2,6 +2,56 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Checkbox as MuiCheckbox } from '@mui/material';
3
3
  import { CheckboxType } from './types';
4
4
  import { Typography, TypographySize, TypographyWeight } from '../Typography';
5
+ import { getIconComponent } from '../../utils';
6
+ /** Control is 20px; the check insets 12.5% and the radio dot 31.25% per Figma. */
7
+ const CONTROL_SIZE = 20;
8
+ const CHECK_SIZE = 15;
9
+ const RADIO_DOT_SIZE = 7.5;
10
+ const RADIUS_FULL = 'var(--spacing-tokens-size-in-px-radius-radius-full)';
11
+ const FG_TERTIARY = 'var(--color-foreground-fg-tertiary)';
12
+ const FG_DISABLED = 'var(--color-foreground-fg-disabled)';
13
+ const FG_ON_ACCENT = 'var(--color-foreground-fg-on-primary-accent-color)';
14
+ /** Class hook so the MUI root can drive the control's hover border. */
15
+ const CONTROL_CLASS = 'BeyCheckbox-control';
16
+ const accentFor = (type) => type === CheckboxType.Radio
17
+ ? 'var(--color-foreground-fg-accent-violet)'
18
+ : 'var(--color-foreground-fg-violet)';
19
+ const controlStyles = (type, checked, disabled) => ({
20
+ boxSizing: 'border-box',
21
+ width: `${CONTROL_SIZE}px`,
22
+ height: `${CONTROL_SIZE}px`,
23
+ display: 'flex',
24
+ alignItems: 'center',
25
+ justifyContent: 'center',
26
+ flexShrink: 0,
27
+ borderRadius: type === CheckboxType.Radio
28
+ ? RADIUS_FULL
29
+ : 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
30
+ // Only checked + enabled is a solid fill; every other state is an outline,
31
+ // disabled included — so a disabled checked control stays hollow.
32
+ border: checked && !disabled
33
+ ? 'none'
34
+ : `1px solid ${disabled ? FG_DISABLED : FG_TERTIARY}`,
35
+ backgroundColor: checked && !disabled ? accentFor(type) : 'transparent',
36
+ transition: 'all 0.2s ease-in-out',
37
+ });
38
+ /** The check glyph or radio dot shown once the control is checked. */
39
+ const ControlMark = ({ type, disabled, }) => {
40
+ const color = disabled ? FG_DISABLED : FG_ON_ACCENT;
41
+ if (type === CheckboxType.Radio) {
42
+ return (_jsx(Box, { sx: {
43
+ width: `${RADIO_DOT_SIZE}px`,
44
+ height: `${RADIO_DOT_SIZE}px`,
45
+ borderRadius: RADIUS_FULL,
46
+ backgroundColor: color,
47
+ } }));
48
+ }
49
+ return getIconComponent('Check', {
50
+ size: `${CHECK_SIZE}px`,
51
+ weight: 'bold',
52
+ color,
53
+ });
54
+ };
5
55
  export const Checkbox = ({ type = CheckboxType.Checkbox, label, checked, disabled, required, onChange, style, }) => {
6
56
  const props = {
7
57
  checked,
@@ -18,48 +68,22 @@ export const Checkbox = ({ type = CheckboxType.Checkbox, label, checked, disable
18
68
  onChange(event);
19
69
  }
20
70
  };
21
- return (_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', sx: style, children: [type === CheckboxType.Checkbox ? (_jsx(MuiCheckbox, { ...props, sx: {
71
+ // A checked control has no border, so the hover colour is a no-op there and
72
+ // this stays correct even when MUI owns the checked state. The checkbox is
73
+ // hovered via its MUI root (the input overlays the control), the radio
74
+ // directly.
75
+ const hoverBorder = { borderColor: accentFor(type) };
76
+ const checkboxHoverSx = disabled
77
+ ? undefined
78
+ : { [`&:hover .${CONTROL_CLASS}`]: hoverBorder };
79
+ const radioHoverSx = disabled ? undefined : { '&:hover': hoverBorder };
80
+ return (_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)', sx: style, children: [type === CheckboxType.Checkbox ? (_jsx(MuiCheckbox, { ...props, icon: _jsx(Box, { className: CONTROL_CLASS, sx: controlStyles(type, false, !!disabled) }), checkedIcon: _jsx(Box, { className: CONTROL_CLASS, sx: controlStyles(type, true, !!disabled), children: _jsx(ControlMark, { type: type, disabled: !!disabled }) }), sx: {
22
81
  padding: 0,
23
- '&.Mui-checked': {
24
- color: disabled
25
- ? 'var(--color-background-bg-button-disabled)'
26
- : 'var(--color-background-bg-brand-primary)',
27
- borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
28
- '&:hover': {
29
- color: 'var(--color-background-bg-brand-primary-hover)',
30
- },
31
- },
32
- } })) : type === CheckboxType.Radio ? (_jsx(Box, { onClick: handleRadioClick, sx: {
33
- width: '20px',
34
- height: '20px',
35
- borderRadius: 'var(--spacing-tokens-size-in-px-radius-radius-full)',
36
- border: checked ? '6px solid' : '1px solid',
37
- borderColor: disabled
38
- ? 'var(--color-background-bg-button-disabled)'
39
- : checked
40
- ? 'var(--color-background-bg-brand-primary)'
41
- : 'var(--color-background-bg-button-disabled)',
42
- display: 'flex',
43
- alignItems: 'center',
44
- justifyContent: 'center',
82
+ '&:hover': { backgroundColor: 'transparent' },
83
+ ...checkboxHoverSx,
84
+ } })) : type === CheckboxType.Radio ? (_jsx(Box, { onClick: handleRadioClick, className: CONTROL_CLASS, sx: {
85
+ ...controlStyles(type, !!checked, !!disabled),
45
86
  cursor: disabled ? 'not-allowed' : 'pointer',
46
- position: 'relative',
47
- backgroundColor: 'transparent',
48
- boxSizing: 'border-box',
49
- transition: 'all 0.2s ease-in-out',
50
- '&:hover': !disabled
51
- ? {
52
- borderColor: 'var(--color-background-bg-brand-primary-hover)',
53
- }
54
- : undefined,
55
- '&::after': {
56
- content: '""',
57
- position: 'absolute',
58
- width: '10px',
59
- height: '10px',
60
- borderRadius: 'var(--spacing-tokens-size-in-px-radius-radius-full)',
61
- backgroundColor: 'transparent',
62
- transition: 'all 0.2s ease-in-out',
63
- },
64
- } })) : null, label && (_jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Medium, children: label }))] }));
87
+ ...radioHoverSx,
88
+ }, children: checked && _jsx(ControlMark, { type: type, disabled: !!disabled }) })) : null, label && (_jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Medium, children: label }))] }));
65
89
  };
@@ -24,6 +24,7 @@ const COLOR = {
24
24
  component: 'var(--color-background-bg-component)',
25
25
  borderComponent: 'var(--color-border-border-component)',
26
26
  borderBrand: 'var(--color-border-border-brand)',
27
+ borderDarkOnly: 'var(--color-border-border-only-for-dark-mode)',
27
28
  fgSecondary: 'var(--color-foreground-fg-secondary)',
28
29
  };
29
30
  const RADIUS_FULL = 'var(--spacing-tokens-size-in-px-radius-radius-full)';
@@ -106,7 +107,7 @@ const CalendarCell = ({ label, onClick, disabled = false, muted = false, today =
106
107
  boxSizing: 'border-box',
107
108
  cursor: interactive ? 'pointer' : 'default',
108
109
  ...hoverSx,
109
- }, children: _jsx(Box, { className: "datepicker-cell-inner", display: "flex", alignItems: "center", justifyContent: "center", width: "28px", height: "28px", borderRadius: "14px", bgcolor: innerBg, children: _jsx(Typography, { className: "datepicker-cell-label", size: TypographySize.TextS, weight: TypographyWeight.Regular, color: textColor, textStyle: { lineHeight: '20px' }, children: label }) }) }));
110
+ }, children: _jsx(Box, { className: "datepicker-cell-inner", display: "flex", alignItems: "center", justifyContent: "center", width: "28px", height: "28px", borderRadius: "14px", bgcolor: innerBg, children: _jsx(Typography, { className: "datepicker-cell-label", size: TypographySize.TextS, weight: TypographyWeight.Regular, color: textColor, children: label }) }) }));
110
111
  };
111
112
  const CalendarHeader = ({ title, onPrev, onNext, onTitleClick, }) => {
112
113
  const caretButton = (icon, onClick) => (_jsx(Box, { onClick: onClick, display: "flex", alignItems: "center", justifyContent: "center", padding: "10px", borderRadius: RADIUS_XS, flexShrink: 0, sx: {
@@ -279,7 +280,7 @@ export const DatePicker = ({ label = 'Select date', placeholder = 'Select a date
279
280
  'aria-label': 'Clear date',
280
281
  role: 'button',
281
282
  tabIndex: 0,
282
- }) }))] }), _jsx(Popper, { open: open, anchorEl: anchorRef.current, placement: "bottom-start", style: { zIndex: 1300 }, children: _jsxs(Box, { bgcolor: COLOR.card, borderRadius: SPACING_MD, mt: "4px", paddingX: "24px", paddingY: "20px", width: "328px", role: "dialog", sx: {
283
+ }) }))] }), _jsx(Popper, { open: open, anchorEl: anchorRef.current, placement: "bottom-start", style: { zIndex: 1300 }, children: _jsxs(Box, { bgcolor: COLOR.card, border: `1px solid ${COLOR.borderDarkOnly}`, borderRadius: SPACING_MD, mt: "4px", paddingX: "24px", paddingY: "20px", width: "330px", role: "dialog", sx: {
283
284
  boxSizing: 'border-box',
284
285
  boxShadow: '0px 20px 24px -4px rgba(34, 7, 56, 0.08)',
285
286
  }, children: [view === 'date' && renderDateView(), view === 'month' && renderMonthView(), view === 'year' && renderYearView()] }) })] }) }));
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { DrawerCreditsCardProps } from './types';
3
3
  /**
4
- * Compact "Upgrade plan" credits card for the {@link Drawer} footer. Shows a
4
+ * Compact "Upgrade Plan" credits card for the {@link Drawer} footer. Shows a
5
5
  * progress bar and a credits caption, with an info tooltip listing the credit
6
6
  * conversion rates. When collapsed it shrinks to the gem glyph and reveals the
7
7
  * full details (and an upgrade action) in a hover popover. Fully theme-aware.
@@ -128,13 +128,13 @@ const PopoverGroup = styled.div `
128
128
  `;
129
129
  const InfoLines = ({ lines }) => (_jsx("div", { style: { display: 'flex', flexDirection: 'column' }, children: lines.map((line, i) => (_jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: "var(--color-text-text-white)", children: line }, i))) }));
130
130
  /**
131
- * Compact "Upgrade plan" credits card for the {@link Drawer} footer. Shows a
131
+ * Compact "Upgrade Plan" credits card for the {@link Drawer} footer. Shows a
132
132
  * progress bar and a credits caption, with an info tooltip listing the credit
133
133
  * conversion rates. When collapsed it shrinks to the gem glyph and reveals the
134
134
  * full details (and an upgrade action) in a hover popover. Fully theme-aware.
135
135
  */
136
- export const DrawerCreditsCard = ({ size = DrawerSize.Expanded, usedCredits = 0, maxCredits = 0, upgradeLabel = 'Upgrade plan', creditsCaption, icon, infoLines = DEFAULT_INFO_LINES, onUpgradeClick, className, style, testId, }) => {
137
- const caption = creditsCaption ?? `${usedCredits} credits used from ${maxCredits}`;
136
+ export const DrawerCreditsCard = ({ size = DrawerSize.Expanded, usedCredits = 0, maxCredits = 0, upgradeLabel = 'Upgrade Plan', creditsCaption, icon, infoLines = DEFAULT_INFO_LINES, onUpgradeClick, className, style, testId, }) => {
137
+ const caption = creditsCaption ?? `${usedCredits} credits used of ${maxCredits}`;
138
138
  const glyph = icon ? (getIconComponent(icon, { size: 20 })) : (_jsx(GemIcon, { size: 20 }));
139
139
  if (size === DrawerSize.Collapsed) {
140
140
  return (_jsx(Popover, { placement: "right", content: _jsxs(PopoverBody, { children: [_jsx(PopoverButton, { type: "button", onClick: () => onUpgradeClick?.(), children: _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.SemiBold, color: "var(--color-text-text-on-accent-color)", children: upgradeLabel }) }), _jsxs(PopoverGroup, { children: [_jsx(CreditsBar, { used: usedCredits, max: maxCredits }), _jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Regular, color: "var(--color-text-text-white)", children: caption })] }), _jsx(InfoLines, { lines: infoLines })] }), children: _jsx(CollapsedBox, { className: className, style: style, "data-testid": testId, onClick: () => onUpgradeClick?.(), children: glyph }) }));
@@ -48,10 +48,10 @@ export interface DrawerCreditsCardProps {
48
48
  usedCredits?: number;
49
49
  /** Total credits available in the plan. */
50
50
  maxCredits?: number;
51
- /** Title/link label. Defaults to "Upgrade plan". */
51
+ /** Title/link label. Defaults to "Upgrade Plan". */
52
52
  upgradeLabel?: string;
53
53
  /**
54
- * Overrides the auto-generated "{used} credits used from {max}" caption.
54
+ * Overrides the auto-generated "{used} credits used of {max}" caption.
55
55
  */
56
56
  creditsCaption?: string;
57
57
  /** Leading icon. Defaults to the gem glyph. */
@@ -11,7 +11,7 @@ export const PlanCard = ({ title, description, price, priceSuffix = '/month', ba
11
11
  ? 'var(--color-border-border-brand)'
12
12
  : 'var(--color-border-border-subtle)'}`, boxSizing: 'border-box', style: style, "data-testid": testId ?? `${planKey}-plan-card`, children: [_jsxs(Box, { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', minHeight: '24px', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', children: [_jsx(Typography, { size: TypographySize.HeadingM, weight: TypographyWeight.Bold, color: 'var(--color-text-text-title)', children: title }), badgeText && (_jsx(Badge, { text: badgeText, color: badgeColor, size: BadgeSize.xs, fill: true }))] }), description && (_jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: 'var(--color-text-text-paragraph)', textStyle: { textAlign: 'left' }, children: description })), price && (_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'baseline', gap: '2px', children: [_jsx(Typography, { size: TypographySize.HeadingL, weight: TypographyWeight.Bold, color: 'var(--color-text-text-title)', children: price }), priceSuffix && (_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Regular, color: 'var(--color-text-text-paragraph)', children: priceSuffix }))] }))] }), buttonText && (_jsx(MainButton, { text: buttonText, hierarchy: buttonHierarchy, size: ButtonSize.md, disabled: buttonDisabled, loading: buttonLoading, onClick: onButtonClick, style: { width: '100%' }, testId: `${planKey}-plan-button` })), features.length > 0 && (_jsxs(Box, { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', alignItems: 'flex-start', children: [_jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Medium, color: 'var(--color-text-text-paragraph)', children: featuresTitle }), features.map((feature, index) => {
13
13
  const isHeading = feature?.startsWith('-');
14
- return (_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'flex-start', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', width: '100%', children: [!isHeading && (_jsx(CheckIcon, { color: CheckIconColor.Violet, size: CheckIconSize.xs, type: CheckIconType.Duotone })), _jsx(Typography, { size: TypographySize.TextM, weight: isHeading
14
+ return (_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'flex-start', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', width: '100%', children: [!isHeading && (_jsx(Box, { display: 'flex', alignItems: 'center', flexShrink: 0, height: '1lh', fontFamily: 'var(--global-text-m-regular-font-family), sans-serif', fontSize: 'var(--global-text-m-regular-font-size)', lineHeight: 'calc(var(--global-text-m-regular-line-height) * 1px)', children: _jsx(CheckIcon, { color: CheckIconColor.Violet, size: CheckIconSize.xs, type: CheckIconType.Duotone }) })), _jsx(Typography, { size: TypographySize.TextM, weight: isHeading
15
15
  ? TypographyWeight.Medium
16
16
  : TypographyWeight.Regular, color: 'var(--color-text-text-title)', textStyle: { textAlign: 'left' }, children: isHeading ? feature?.slice(1)?.trim() : feature })] }, `${index}-${feature}`));
17
17
  })] }))] }));
@@ -4,19 +4,17 @@ import { getTextStyles, getTypographySizeToken, getTypographyWeightToken, } from
4
4
  export const Typography = ({ text, color, size = TypographySize.HeadingL, weight = TypographyWeight.Bold, className, required = false, textStyle, children, title, }) => {
5
5
  const sizeToken = getTypographySizeToken(size);
6
6
  const weightToken = getTypographyWeightToken(weight);
7
- const { fontSize, fontWeight, fontFamily, lineHeight, letterSpacing, paragraphIndent, paragraphSpacing, textDecoration, textTransform, } = getTextStyles(sizeToken, weightToken);
7
+ const { fontSize, fontWeight, fontFamily, lineHeight, paragraphIndent, textDecoration, textTransform, } = getTextStyles(sizeToken, weightToken);
8
8
  const requiredColor = 'var(--color-text-text-error)';
9
9
  return (_jsxs("span", { className: className, style: {
10
10
  fontSize: fontSize,
11
11
  fontWeight: fontWeight,
12
12
  fontFamily: fontFamily + ', sans-serif',
13
- lineHeight: lineHeight + 'px',
14
- letterSpacing: letterSpacing,
13
+ lineHeight: lineHeight,
15
14
  textIndent: paragraphIndent,
16
- marginBottom: paragraphSpacing,
17
15
  textTransform: textTransform,
18
16
  textDecoration: textDecoration,
19
- color: color || 'var(--global-text-text-title)',
17
+ color: color,
20
18
  ...textStyle,
21
19
  }, title: title, children: [children || text, required && _jsx("span", { style: { color: requiredColor }, children: ' *' })] }));
22
20
  };
@@ -36,7 +36,10 @@ export const ConversationalAvatarsBanner = ({ title, description, actions = [],
36
36
  padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl) var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
37
37
  },
38
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: {
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)", sx: {
40
+ flexWrap: 'wrap',
41
+ '& button': { whiteSpace: 'nowrap' },
42
+ }, children: actions.map(renderAction) }))] }), _jsx(Box, { flexShrink: 0, alignSelf: "stretch", position: "relative", overflow: "hidden", width: `${AVATAR_TILE_WIDTH}px`, sx: {
40
43
  marginY: 'calc(-1 * var(--spacing-tokens-size-in-px-spacing-spacing-3xl))',
41
44
  [MOBILE]: {
42
45
  marginY: 'calc(-1 * var(--spacing-tokens-size-in-px-spacing-spacing-xl))',
@@ -25,12 +25,11 @@ export const ConversationalVoiceAgentsBanner = ({ title, description, actions =
25
25
  },
26
26
  ...customStyle,
27
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.
28
+ flexWrap: 'wrap',
29
+ '& button': { whiteSpace: 'nowrap' },
30
30
  [MOBILE]: {
31
31
  width: '100%',
32
- '& > *': { flex: 1, minWidth: 0 },
33
- '& button': { width: '100%' },
32
+ '& > *': { flex: '1 1 auto' },
34
33
  },
35
34
  }, 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
35
  [MOBILE]: {
@@ -8,5 +8,5 @@ export declare const BANNER_AVATARS: {
8
8
  };
9
9
  export declare const AVATAR_TILE_BACKGROUND = "linear-gradient(180deg, #e9e9e9 0%, #beb1df 100%)";
10
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;
11
+ export declare const AVATAR_TILE_WIDTH = 120;
12
12
  export declare const AVATAR_TILE_HEIGHT = 150;
@@ -16,5 +16,5 @@ export const AVATAR_TILE_BACKGROUND = 'linear-gradient(180deg, #e9e9e9 0%, #beb1
16
16
  // Poster/thumbnail shown in the ConversationalVoiceAgentsBanner video preview.
17
17
  export const BANNER_VIDEO_PREVIEW = `${AVATAR_BASE}/yuruo-preview.png`;
18
18
  // Size of a single avatar tile in the collages.
19
- export const AVATAR_TILE_WIDTH = 130;
19
+ export const AVATAR_TILE_WIDTH = 120;
20
20
  export const AVATAR_TILE_HEIGHT = 150;
@@ -6,9 +6,7 @@ export declare const getTextStyles: (sizeToken: string, weightToken: string) =>
6
6
  fontWeight: string;
7
7
  fontFamily: string;
8
8
  lineHeight: string;
9
- letterSpacing: string;
10
9
  paragraphIndent: string;
11
- paragraphSpacing: string;
12
10
  textDecoration: string;
13
11
  textTransform: "uppercase" | "none";
14
12
  };
@@ -26,10 +26,11 @@ export const getTextStyles = (sizeToken, weightToken) => {
26
26
  const fontSize = `var(--global-${sizeToken}-${weightToken}-font-size)`;
27
27
  const fontWeight = `var(--global-${sizeToken}-${weightToken}-font-weight)`;
28
28
  const fontFamily = `var(--global-${sizeToken}-${weightToken}-font-family)`;
29
- const lineHeight = `var(--global-${sizeToken}-${weightToken}-line-height)`;
30
- const letterSpacing = `var(--global-${sizeToken}-${weightToken}-letter-spacing)`;
29
+ // The line-height tokens are unitless numbers, and var() substitutes tokens
30
+ // rather than text: `var(--x)px` yields `24 px`, not `24px`. calc() is what
31
+ // actually attaches the unit.
32
+ const lineHeight = `calc(var(--global-${sizeToken}-${weightToken}-line-height) * 1px)`;
31
33
  const paragraphIndent = `var(--global-${sizeToken}-${weightToken}-paragraph-indent)`;
32
- const paragraphSpacing = `var(--global-${sizeToken}-${weightToken}-paragraph-spacing)`;
33
34
  const textDecoration = `var(--global-${sizeToken}-${weightToken}-text-decoration)`;
34
35
  const textTransform = getTextTransform(sizeToken, weightToken);
35
36
  return {
@@ -37,9 +38,7 @@ export const getTextStyles = (sizeToken, weightToken) => {
37
38
  fontWeight,
38
39
  fontFamily,
39
40
  lineHeight,
40
- letterSpacing,
41
41
  paragraphIndent,
42
- paragraphSpacing,
43
42
  textDecoration,
44
43
  textTransform,
45
44
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@beydesign/storybook",
3
3
  "private": false,
4
- "version": "0.4.1",
4
+ "version": "0.4.3",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -25,7 +25,7 @@
25
25
  "access": "public"
26
26
  },
27
27
  "dependencies": {
28
- "@beydesign/tokens": "^0.1.1",
28
+ "@beydesign/tokens": "^0.1.2",
29
29
  "@emotion/react": "^11.14.0",
30
30
  "@emotion/styled": "^11.14.1",
31
31
  "@fortawesome/fontawesome-svg-core": "^6.7.2",