@beydesign/storybook 0.2.27 → 0.2.29
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.
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { DropdownProps } from './types';
|
|
3
|
-
export declare const Dropdown: React.
|
|
3
|
+
export declare const Dropdown: <T extends string = string>({ disabled, placeholder, label, showLeadingIcon, leadingIcon, showClear, options, required, showCheckboxes, showTicks, onEndReached, isLoadingMore, ...props }: DropdownProps<T>) => React.JSX.Element;
|
|
@@ -5,7 +5,7 @@ import { Box, CircularProgress, ClickAwayListener, Popper, } from '@mui/material
|
|
|
5
5
|
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
6
6
|
import { getIconComponent } from '../../utils';
|
|
7
7
|
import { Checkbox } from './Checkbox';
|
|
8
|
-
import { Badge, BadgeColor, BadgeSize } from '../UI';
|
|
8
|
+
import { Avatar, AvatarSize, Badge, BadgeColor, BadgeSize } from '../UI';
|
|
9
9
|
export const Dropdown = ({ disabled = false, placeholder = 'Select an option', label = 'Select', showLeadingIcon = false, leadingIcon = 'List', showClear = true, options, required = false, showCheckboxes = false, showTicks = true, onEndReached, isLoadingMore = false, ...props }) => {
|
|
10
10
|
const [open, setOpen] = useState(false);
|
|
11
11
|
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
|
@@ -36,6 +36,7 @@ export const Dropdown = ({ disabled = false, placeholder = 'Select an option', l
|
|
|
36
36
|
// trigger renders the rich "Avatar leading" layout; otherwise it falls back to
|
|
37
37
|
// the plain joined-label text (preserving the original behavior).
|
|
38
38
|
const hasRichSelection = selectedOptions.some((option) => option.avatar || option.badge);
|
|
39
|
+
const hasAvatarOption = normalizedOptions.some((option) => option.avatar);
|
|
39
40
|
const hasSelection = selectedOptions.length > 0;
|
|
40
41
|
// Plain-text representation of the selection, used for the non-rich trigger.
|
|
41
42
|
const displayValue = useMemo(() => selectedOptions.map((option) => option.label).join(', '), [selectedOptions]);
|
|
@@ -151,10 +152,7 @@ export const Dropdown = ({ disabled = false, placeholder = 'Select an option', l
|
|
|
151
152
|
? 'var(--color-border-border-brand)'
|
|
152
153
|
: 'var(--color-border-border-component)'}`, borderRadius: '4px', bgcolor: disabled
|
|
153
154
|
? 'var(--color-background-bg-component-disabled)'
|
|
154
|
-
: 'var(--color-background-bg-component)', position: 'relative', onClick: () => !disabled && setOpen((prev) => !prev), onKeyDown: handleKeyDown, tabIndex: disabled ? -1 : 0, "aria-haspopup": "listbox", "aria-expanded": open, "aria-disabled": disabled, role: "combobox", sx: { boxSizing: 'border-box' }, children: [_jsx(Box, { display: "flex", flexDirection: "row", alignItems: "center", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-sm)", flexGrow: 1, minWidth: 0, overflow: "hidden", children: hasSelection && hasRichSelection ? (_jsxs(_Fragment, { children: [
|
|
155
|
-
objectFit: 'cover',
|
|
156
|
-
border: '0.5px solid var(--color-border-border-divider)',
|
|
157
|
-
} })), _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: "var(--color-text-text-title)", textStyle: {
|
|
155
|
+
: 'var(--color-background-bg-component)', position: 'relative', onClick: () => !disabled && setOpen((prev) => !prev), onKeyDown: handleKeyDown, tabIndex: disabled ? -1 : 0, "aria-haspopup": "listbox", "aria-expanded": open, "aria-disabled": disabled, role: "combobox", sx: { boxSizing: 'border-box' }, children: [_jsx(Box, { display: "flex", flexDirection: "row", alignItems: "center", gap: "var(--spacing-tokens-size-in-px-spacing-spacing-sm)", flexGrow: 1, minWidth: 0, overflow: "hidden", children: hasSelection && (hasRichSelection || hasAvatarOption) ? (_jsxs(_Fragment, { children: [hasAvatarOption && (_jsx(Box, { flexShrink: 0, display: "flex", children: _jsx(Avatar, { image: selectedOptions[0].avatar, text: selectedOptions[0].label, size: AvatarSize.xs, border: true }) })), _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: "var(--color-text-text-title)", textStyle: {
|
|
158
156
|
minWidth: 0,
|
|
159
157
|
textAlign: 'left',
|
|
160
158
|
overflow: 'hidden',
|
|
@@ -210,10 +208,7 @@ export const Dropdown = ({ disabled = false, placeholder = 'Select an option', l
|
|
|
210
208
|
},
|
|
211
209
|
width: '100%',
|
|
212
210
|
boxSizing: 'border-box',
|
|
213
|
-
}, role: "option", "aria-selected": isOptionSelected(option.value), children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', flexGrow: 1, minWidth: 0, overflow: "hidden", children: [showCheckboxes && (_jsx(Box, { flexShrink: 0, children: _jsx(Checkbox, { checked: isOptionSelected(option.value), onChange: () => handleOptionSelect(option.value), style: { margin: 0, padding: 0 } }) })),
|
|
214
|
-
objectFit: 'cover',
|
|
215
|
-
border: '0.5px solid var(--color-border-border-divider)',
|
|
216
|
-
} })), _jsx(Box, { flexGrow: 1, minWidth: 0, overflow: "hidden", children: _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: "var(--color-text-text-title)", textStyle: {
|
|
211
|
+
}, role: "option", "aria-selected": isOptionSelected(option.value), children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', flexGrow: 1, minWidth: 0, overflow: "hidden", children: [showCheckboxes && (_jsx(Box, { flexShrink: 0, children: _jsx(Checkbox, { checked: isOptionSelected(option.value), onChange: () => handleOptionSelect(option.value), style: { margin: 0, padding: 0 } }) })), hasAvatarOption && (_jsx(Box, { flexShrink: 0, display: "flex", children: _jsx(Avatar, { image: option.avatar, text: option.label, size: AvatarSize.xs, border: true }) })), _jsx(Box, { flexGrow: 1, minWidth: 0, overflow: "hidden", children: _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: "var(--color-text-text-title)", textStyle: {
|
|
217
212
|
overflow: 'hidden',
|
|
218
213
|
textOverflow: 'ellipsis',
|
|
219
214
|
whiteSpace: 'nowrap',
|
|
@@ -248,9 +248,9 @@ export declare enum DropdownType {
|
|
|
248
248
|
* when present, render a leading avatar and a trailing badge pill (matching the
|
|
249
249
|
* "Avatar leading" variant in Figma).
|
|
250
250
|
*/
|
|
251
|
-
export type DropdownOption = {
|
|
251
|
+
export type DropdownOption<T extends string = string> = {
|
|
252
252
|
/** Stable identifier used for selection and returned by onChange */
|
|
253
|
-
value:
|
|
253
|
+
value: T;
|
|
254
254
|
/** Human-readable text shown for the option */
|
|
255
255
|
label: string;
|
|
256
256
|
/** Optional leading avatar image URL (rendered as a 24px rounded avatar) */
|
|
@@ -258,24 +258,30 @@ export type DropdownOption = {
|
|
|
258
258
|
/** Optional trailing badge pill text (e.g. "Screening Interview") */
|
|
259
259
|
badge?: string;
|
|
260
260
|
};
|
|
261
|
-
type SingleSelectDropdownProps = {
|
|
261
|
+
type SingleSelectDropdownProps<T extends string = string> = {
|
|
262
262
|
/** The value of the selected option */
|
|
263
|
-
value:
|
|
263
|
+
value: T;
|
|
264
264
|
/** The onChange handler for the selected option */
|
|
265
|
-
onChange: (value:
|
|
265
|
+
onChange: (value: T) => void;
|
|
266
266
|
/** The type of the dropdown */
|
|
267
267
|
type: DropdownType.SingleSelect;
|
|
268
268
|
};
|
|
269
|
-
type MultiSelectDropdownProps = {
|
|
269
|
+
type MultiSelectDropdownProps<T extends string = string> = {
|
|
270
270
|
/** The value of the selected options */
|
|
271
|
-
value:
|
|
271
|
+
value: T[];
|
|
272
272
|
/** The onChange handler for the selected options */
|
|
273
|
-
onChange: (value:
|
|
273
|
+
onChange: (value: T[]) => void;
|
|
274
274
|
/** The type of the dropdown */
|
|
275
275
|
type: DropdownType.MultiSelect;
|
|
276
276
|
};
|
|
277
|
-
type DropdownBaseProps = SingleSelectDropdownProps | MultiSelectDropdownProps
|
|
278
|
-
|
|
277
|
+
type DropdownBaseProps<T extends string = string> = SingleSelectDropdownProps<T> | MultiSelectDropdownProps<T>;
|
|
278
|
+
/**
|
|
279
|
+
* Props for the {@link Dropdown}. Generic over the option value type `T`
|
|
280
|
+
* (constrained to `string`). Leaving `T` unspecified defaults it to `string`,
|
|
281
|
+
* so existing consumers are unaffected; passing a string-literal union (e.g.
|
|
282
|
+
* `Dropdown<'a' | 'b'>`) gives type-safe `value`/`onChange` with no casts.
|
|
283
|
+
*/
|
|
284
|
+
export type DropdownProps<T extends string = string> = DropdownBaseProps<T> & {
|
|
279
285
|
/** The label of the dropdown */
|
|
280
286
|
label?: string | null;
|
|
281
287
|
/**
|
|
@@ -284,7 +290,7 @@ export type DropdownProps = DropdownBaseProps & {
|
|
|
284
290
|
* {@link DropdownOption} objects for id-based selection and avatar/badge
|
|
285
291
|
* support.
|
|
286
292
|
*/
|
|
287
|
-
options:
|
|
293
|
+
options: T[] | DropdownOption<T>[];
|
|
288
294
|
/** The placeholder of the dropdown */
|
|
289
295
|
placeholder?: string;
|
|
290
296
|
/** Whether the dropdown is disabled */
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { AvatarSize, AvatarStyle } from './types';
|
|
3
3
|
import { Avatar as MuiAvatar, } from '@mui/material';
|
|
4
4
|
import { User } from '@phosphor-icons/react';
|
|
5
5
|
import { Typography, TypographySize, TypographyWeight } from '../Typography';
|
|
6
6
|
import { getInitials } from '../../utils';
|
|
7
|
-
export const Avatar = ({ avatarStyle
|
|
7
|
+
export const Avatar = ({ avatarStyle, border = false, image, size = AvatarSize.xxl, style: customStyle, text, }) => {
|
|
8
|
+
// When the caller doesn't pin a style, derive it from the data so
|
|
9
|
+
// `<Avatar image={url} text={name} />` "just works": an image wins, then any
|
|
10
|
+
// non-empty text renders initials, otherwise the placeholder icon.
|
|
11
|
+
const resolvedStyle = avatarStyle ??
|
|
12
|
+
(image?.trim()
|
|
13
|
+
? AvatarStyle.Picture
|
|
14
|
+
: text?.trim()
|
|
15
|
+
? AvatarStyle.Letters
|
|
16
|
+
: AvatarStyle.Placeholder);
|
|
8
17
|
const getDimensions = (avatarSize) => {
|
|
9
18
|
switch (avatarSize) {
|
|
10
19
|
case AvatarSize.xs:
|
|
@@ -58,16 +67,16 @@ export const Avatar = ({ avatarStyle = AvatarStyle.Placeholder, border = false,
|
|
|
58
67
|
? '1px solid var(--color-border-border-component)'
|
|
59
68
|
: 'none',
|
|
60
69
|
backgroundColor: 'var(--color-background-bg-card-highlight)',
|
|
61
|
-
color: getColor(
|
|
70
|
+
color: getColor(resolvedStyle),
|
|
62
71
|
fontSize: getTextSize(),
|
|
63
72
|
borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-11xl)',
|
|
64
73
|
...customStyle,
|
|
65
74
|
},
|
|
66
75
|
};
|
|
67
|
-
if (
|
|
68
|
-
return (_jsx(MuiAvatar, { ...props, children: _jsx(Typography, { size: getTextSize(), weight: TypographyWeight.Medium, children: getInitials(text) }) }));
|
|
76
|
+
if (resolvedStyle === AvatarStyle.Letters) {
|
|
77
|
+
return (_jsx(MuiAvatar, { ...props, children: _jsx(Typography, { size: getTextSize(), weight: TypographyWeight.Medium, children: getInitials(text ?? '') }) }));
|
|
69
78
|
}
|
|
70
|
-
else if (
|
|
79
|
+
else if (resolvedStyle === AvatarStyle.Picture) {
|
|
71
80
|
return _jsx(MuiAvatar, { ...props, src: image });
|
|
72
81
|
}
|
|
73
82
|
else {
|
|
@@ -118,8 +118,12 @@ export declare const AvatarImage = "https://encrypted-tbn0.gstatic.com/images?q=
|
|
|
118
118
|
* Avatar component props
|
|
119
119
|
*/
|
|
120
120
|
export type AvatarProps = {
|
|
121
|
-
/**
|
|
122
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Style variant of the avatar. When omitted, it is derived from the data:
|
|
123
|
+
* `image` present → `Picture`, else non-empty `text` → `Letters`, else
|
|
124
|
+
* `Placeholder`. Pass explicitly to override the derivation.
|
|
125
|
+
*/
|
|
126
|
+
avatarStyle?: AvatarStyle;
|
|
123
127
|
/** Text content of the avatar if avatarStyle is Letters */
|
|
124
128
|
text?: string;
|
|
125
129
|
/** Image source of the avatar if avatarStyle is Picture */
|