@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.
- package/dist/stories/Buttons/MainButton.js +123 -113
- package/dist/stories/EntityCard/EntityCard.js +31 -4
- package/dist/stories/Form/AutoComplete.js +2 -4
- package/dist/stories/Form/Checkbox.js +66 -42
- package/dist/stories/Form/DatePicker.js +3 -2
- package/dist/stories/Navigation/Drawer/DrawerCreditsCard.d.ts +1 -1
- package/dist/stories/Navigation/Drawer/DrawerCreditsCard.js +3 -3
- package/dist/stories/Navigation/Drawer/types.d.ts +2 -2
- package/dist/stories/PlanCard/PlanCard.js +1 -1
- package/dist/stories/Typography/Typography.js +3 -5
- package/dist/stories/UI/ConversationalAvatarsBanner.js +4 -1
- package/dist/stories/UI/ConversationalVoiceAgentsBanner.js +3 -4
- package/dist/stories/UI/bannerAvatars.d.ts +1 -1
- package/dist/stories/UI/bannerAvatars.js +1 -1
- package/dist/utils/typography.d.ts +0 -2
- package/dist/utils/typography.js +4 -5
- package/package.json +2 -2
|
@@ -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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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: ${
|
|
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
|
|
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
|
-
|
|
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-
|
|
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-
|
|
206
|
+
return 'var(--color-background-bg-button-overlay)';
|
|
185
207
|
case ButtonHierarchy.TertiaryColor:
|
|
186
|
-
return 'var(--
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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:
|
|
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: '
|
|
265
|
+
gap: '10px',
|
|
246
266
|
},
|
|
247
267
|
[ButtonSize.lg]: {
|
|
248
|
-
padding:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
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),
|
|
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: {
|
|
49
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
'
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
47
|
-
|
|
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,
|
|
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: "
|
|
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
|
|
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
|
|
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
|
|
137
|
-
const caption = creditsCaption ?? `${usedCredits} credits used
|
|
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
|
|
51
|
+
/** Title/link label. Defaults to "Upgrade Plan". */
|
|
52
52
|
upgradeLabel?: string;
|
|
53
53
|
/**
|
|
54
|
-
* Overrides the auto-generated "{used} credits used
|
|
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,
|
|
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
|
|
14
|
-
letterSpacing: letterSpacing,
|
|
13
|
+
lineHeight: lineHeight,
|
|
15
14
|
textIndent: paragraphIndent,
|
|
16
|
-
marginBottom: paragraphSpacing,
|
|
17
15
|
textTransform: textTransform,
|
|
18
16
|
textDecoration: textDecoration,
|
|
19
|
-
color: color
|
|
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)",
|
|
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
|
-
|
|
29
|
-
|
|
28
|
+
flexWrap: 'wrap',
|
|
29
|
+
'& button': { whiteSpace: 'nowrap' },
|
|
30
30
|
[MOBILE]: {
|
|
31
31
|
width: '100%',
|
|
32
|
-
'& > *': { flex: 1
|
|
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 =
|
|
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 =
|
|
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
|
};
|
package/dist/utils/typography.js
CHANGED
|
@@ -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
|
-
|
|
30
|
-
|
|
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.
|
|
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.
|
|
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",
|