@beydesign/storybook 0.4.2 → 0.4.4

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,121 @@ 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
+ };
55
+ // Figma sizes a text button by its height, with the label centred inside, so
56
+ // take the height from the design rather than deriving it from padding: a
57
+ // CSS border sits outside the box where a Figma stroke sits inside, which
58
+ // would otherwise leave every button 2px taller than the design.
59
+ const TEXT_BUTTON_HEIGHT = {
60
+ [ButtonSize.xxs]: 32,
61
+ [ButtonSize.xs]: 36,
62
+ [ButtonSize.sm]: 40,
63
+ [ButtonSize.md]: 44,
64
+ [ButtonSize.lg]: 48,
65
+ [ButtonSize.xl]: 52,
66
+ };
7
67
  const StyledButton = styled.button `
8
68
  text-transform: none;
9
- padding: ${(props) => props.$displayIconMode === ButtonDisplayMode.Only
10
- ? 'var(--spacing-tokens-size-in-px-spacing-spacing-md)'
11
- : props.$sizeStyles.padding};
69
+ box-sizing: border-box;
70
+ ${(props) => {
71
+ if (props.$displayIconMode === ButtonDisplayMode.Only) {
72
+ const box = `${ICON_ONLY_SIZE[props.$shape][props.$size]}px`;
73
+ return `padding: 0;
74
+ width: ${box};
75
+ height: ${box};`;
76
+ }
77
+ // min-height, not height, so a label that ever wraps grows the button
78
+ // instead of spilling out of it.
79
+ return `padding: ${props.$sizeStyles.padding};
80
+ min-height: ${TEXT_BUTTON_HEIGHT[props.$size]}px;`;
81
+ }}
12
82
  font-size: ${(props) => props.$sizeStyles.fontSize};
13
83
  border-radius: ${(props) => props.$shape === ButtonShape.Square
14
84
  ? 'var(--spacing-tokens-size-in-px-radius-radius-xs)'
15
85
  : 'var(--spacing-tokens-size-in-px-spacing-spacing-11xl)'};
16
86
  background: ${(props) => {
17
87
  if (props.$destructive) {
18
- if (props.$state === ButtonState.Disabled || props.$disabled)
88
+ // Destructive is the one hierarchy Figma gives its own disabled fill,
89
+ // rather than the shared bg-button-disabled every other one uses.
90
+ if (isDisabled(props))
19
91
  return 'var(--color-background-bg-element)';
20
92
  return 'var(--color-background-bg-error)';
21
93
  }
94
+ if (isDisabled(props)) {
95
+ switch (props.$hierarchy) {
96
+ case ButtonHierarchy.Tertiary:
97
+ case ButtonHierarchy.TertiaryColor:
98
+ return 'transparent';
99
+ case ButtonHierarchy.WhiteText:
100
+ // Stays a scrim when disabled so it reads over media.
101
+ return 'var(--primitives-white-primary-black-10)';
102
+ default:
103
+ return 'var(--color-background-bg-button-disabled)';
104
+ }
105
+ }
22
106
  switch (props.$hierarchy) {
23
107
  case ButtonHierarchy.Primary:
24
- if (props.$state === ButtonState.Disabled || props.$disabled)
25
- return 'var(--color-background-bg-button-disabled)';
26
108
  return 'var(--color-background-bg-brand-primary)';
27
109
  case ButtonHierarchy.PrimaryLemon:
28
- if (props.$state === ButtonState.Disabled || props.$disabled)
29
- return 'var(--color-background-bg-element)';
30
110
  return 'var(--color-background-bg-brand-secondary)';
31
111
  case ButtonHierarchy.Secondary:
32
- if (props.$state === ButtonState.Disabled || props.$disabled)
33
- return 'var(--color-background-bg-button-disabled)';
34
112
  return 'transparent';
35
113
  case ButtonHierarchy.SecondaryColor:
36
- if (props.$state === ButtonState.Disabled || props.$disabled)
37
- return 'var(--color-background-bg-element)';
38
114
  return 'var(--color-background-bg-secondary-action)';
39
115
  case ButtonHierarchy.SecondaryTransparent:
40
- if (props.$state === ButtonState.Disabled || props.$disabled)
41
- return 'var(--color-background-bg-overlay-1)';
42
116
  return 'var(--color-background-bg-button-transparent)';
43
117
  case ButtonHierarchy.Tertiary:
44
- if (props.$state === ButtonState.Disabled || props.$disabled)
45
- return 'transparent';
46
118
  return 'transparent';
47
119
  case ButtonHierarchy.TertiaryColor:
48
- if (props.$state === ButtonState.Disabled || props.$disabled)
49
- return 'transparent';
50
120
  return 'transparent';
51
121
  case ButtonHierarchy.WhiteText:
52
- if (props.$state === ButtonState.Disabled || props.$disabled)
53
- return 'var(--color-background-bg-button-disabled)';
54
122
  // Figma "White-text" fill is the Primary/Black/40 primitive
55
123
  // (rgba(0,0,0,0.4)) — a dark scrim over media, not an overlay token.
56
124
  return 'var(--primitives-white-primary-black-40)';
@@ -58,89 +126,43 @@ const StyledButton = styled.button `
58
126
  return 'var(--color-background-bg-brand-primary)';
59
127
  }
60
128
  }};
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
- }};
129
+ color: ${getTextColor};
104
130
  border: 1px solid
105
131
  ${(props) => {
106
132
  if (props.$destructive) {
107
- if (props.$state === ButtonState.Disabled || props.$disabled)
133
+ if (isDisabled(props))
108
134
  return 'var(--color-background-bg-element)';
109
135
  return 'var(--color-background-bg-error)';
110
136
  }
137
+ if (isDisabled(props)) {
138
+ switch (props.$hierarchy) {
139
+ case ButtonHierarchy.Tertiary:
140
+ case ButtonHierarchy.TertiaryColor:
141
+ case ButtonHierarchy.WhiteText:
142
+ return 'transparent';
143
+ default:
144
+ return 'var(--color-border-border-disabled)';
145
+ }
146
+ }
111
147
  switch (props.$hierarchy) {
112
148
  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)';
149
+ return 'var(--color-border-border-brand)';
116
150
  case ButtonHierarchy.PrimaryLemon:
117
- if (props.$state === ButtonState.Disabled || props.$disabled)
118
- return 'var(--color-border-border-component)';
119
151
  return 'var(--color-background-bg-brand-secondary)';
120
152
  case ButtonHierarchy.Secondary:
121
- if (props.$state === ButtonState.Disabled || props.$disabled)
122
- return 'var(--color-border-border-disabled)';
123
153
  return 'var(--color-border-border-component)';
124
154
  case ButtonHierarchy.SecondaryColor:
125
- if (props.$state === ButtonState.Disabled || props.$disabled)
126
- return 'var(--color-border-border-component)';
127
155
  return 'var(--color-border-border-secondary-action)';
128
156
  case ButtonHierarchy.SecondaryTransparent:
129
- if (props.$state === ButtonState.Disabled || props.$disabled)
130
- return 'var(--color-border-border-disabled)';
131
157
  return 'transparent';
132
158
  case ButtonHierarchy.Tertiary:
133
- if (props.$state === ButtonState.Disabled || props.$disabled)
134
- return 'transparent';
135
159
  return 'transparent';
136
160
  case ButtonHierarchy.TertiaryColor:
137
- if (props.$state === ButtonState.Disabled || props.$disabled)
138
- return 'transparent';
139
161
  return 'transparent';
140
162
  case ButtonHierarchy.WhiteText:
141
163
  return 'transparent';
142
164
  default:
143
- return 'var(--color-background-bg-brand-primary)';
165
+ return 'var(--color-border-border-brand)';
144
166
  }
145
167
  }};
146
168
  display: flex;
@@ -181,9 +203,9 @@ const StyledButton = styled.button `
181
203
  case ButtonHierarchy.SecondaryTransparent:
182
204
  return 'var(--color-background-bg-button-transparent-hover)';
183
205
  case ButtonHierarchy.Tertiary:
184
- return 'var(--color-background-bg-component-hover)';
206
+ return 'var(--color-background-bg-button-overlay)';
185
207
  case ButtonHierarchy.TertiaryColor:
186
- return 'var(--color-background-bg-card-highlight-hover)';
208
+ return 'var(--primitives-white-primary-white-10)';
187
209
  case ButtonHierarchy.WhiteText:
188
210
  // Darken the black-40 scrim one step on hover (no black-50 exists).
189
211
  return 'var(--primitives-white-primary-black-60)';
@@ -191,6 +213,9 @@ const StyledButton = styled.button `
191
213
  return 'var(--color-background-bg-brand-primary-hover)';
192
214
  }
193
215
  }};
216
+ color: ${(props) => !props.$destructive && props.$hierarchy === ButtonHierarchy.TertiaryColor
217
+ ? 'var(--color-text-text-clicable-hover)'
218
+ : getTextColor(props)};
194
219
  border-color: ${(props) => {
195
220
  if (props.$destructive) {
196
221
  return 'var(--primitives-desktop-primary-red-700)';
@@ -224,58 +249,43 @@ const StyledButton = styled.button `
224
249
  }
225
250
  `;
226
251
  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
- };
252
+ // Figma draws a 20px icon at every button size.
253
+ const iconSize = 20;
254
+ // Vertical padding comes from TEXT_BUTTON_HEIGHT; only the horizontal value
255
+ // is set here. Icon-only buttons take their box from ICON_ONLY_SIZE and
256
+ // ignore padding entirely.
257
+ //
258
+ // Figma leaves the sm, lg and xl horizontal padding, and the xl gap, off the
259
+ // spacing scale, so those are raw values with no token to point at.
238
260
  const getButtonSize = () => {
239
261
  const sizes = {
240
262
  [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)',
263
+ padding: '0 22px',
244
264
  fontSize: 'var(--global-font-size-3)',
245
- gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)',
265
+ gap: '10px',
246
266
  },
247
267
  [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)',
268
+ padding: '0 18px',
251
269
  fontSize: 'var(--global-font-size-3)',
252
270
  gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)',
253
271
  },
254
272
  [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)',
273
+ padding: '0 var(--spacing-tokens-size-in-px-spacing-spacing-xl)',
258
274
  fontSize: 'var(--global-font-size-2)',
259
275
  gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)',
260
276
  },
261
277
  [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)',
278
+ padding: '0 14px',
265
279
  fontSize: 'var(--global-font-size-1)',
266
280
  gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
267
281
  },
268
282
  [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)',
283
+ padding: '0 var(--spacing-tokens-size-in-px-spacing-spacing-lg)',
272
284
  fontSize: 'var(--global-font-size-1)',
273
285
  gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
274
286
  },
275
287
  [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)',
288
+ padding: '0 var(--spacing-tokens-size-in-px-spacing-spacing-md)',
279
289
  fontSize: 'var(--global-font-size-0)',
280
290
  gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
281
291
  },
@@ -283,7 +293,7 @@ export const MainButton = ({ onClick, text, disabled = false, loading = false, t
283
293
  return sizes[size] || sizes[ButtonSize.xl];
284
294
  };
285
295
  const iconProps = {
286
- size: getIconSize(),
296
+ size: iconSize,
287
297
  color: disabled ? 'var(--color-text-text-disabled)' : 'currentColor',
288
298
  weight: iconWeight,
289
299
  };
@@ -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)' }) })), 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.2",
4
+ "version": "0.4.4",
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",