@bspk/ui 1.1.18 → 1.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Avatar.js +1 -1
- package/dist/Dropdown.d.ts +3 -3
- package/dist/Dropdown.js +3 -3
- package/dist/Dropdown.js.map +1 -1
- package/dist/Menu.d.ts +15 -8
- package/dist/Menu.js +63 -41
- package/dist/Menu.js.map +1 -1
- package/dist/SearchBar.d.ts +41 -5
- package/dist/SearchBar.js +17 -15
- package/dist/SearchBar.js.map +1 -1
- package/dist/avatar.css +1 -1
- package/dist/demo/examples.js +22 -0
- package/dist/demo/examples.js.map +1 -1
- package/dist/dropdown.css +1 -1
- package/dist/hooks/useKeyboardNavigation.js +5 -2
- package/dist/hooks/useKeyboardNavigation.js.map +1 -1
- package/dist/menu.css +1 -1
- package/package.json +1 -1
- package/src/Dropdown.tsx +4 -2
- package/src/Menu.tsx +131 -92
- package/src/SearchBar.tsx +100 -45
- package/src/avatar.scss +21 -0
- package/src/demo/examples.tsx +27 -0
- package/src/dropdown.scss +4 -0
- package/src/hooks/useKeyboardNavigation.ts +3 -2
- package/src/menu.scss +4 -0
package/package.json
CHANGED
package/src/Dropdown.tsx
CHANGED
|
@@ -21,7 +21,7 @@ export type DropdownProps<T extends DropdownOption = DropdownOption> = CommonPro
|
|
|
21
21
|
'aria-label' | 'disabled' | 'id' | 'name' | 'readOnly' | 'size'
|
|
22
22
|
> &
|
|
23
23
|
InvalidPropsLibrary &
|
|
24
|
-
Pick<MenuProps<T>, 'isMulti' | 'itemCount' | 'renderListItem'> & {
|
|
24
|
+
Pick<MenuProps<T>, 'isMulti' | 'itemCount' | 'renderListItem' | 'selectAll'> & {
|
|
25
25
|
/**
|
|
26
26
|
* Array of options to display in the dropdown
|
|
27
27
|
*
|
|
@@ -52,7 +52,7 @@ export type DropdownProps<T extends DropdownOption = DropdownOption> = CommonPro
|
|
|
52
52
|
/**
|
|
53
53
|
* Placeholder for the dropdown
|
|
54
54
|
*
|
|
55
|
-
* @default Select...
|
|
55
|
+
* @default Select one...
|
|
56
56
|
*/
|
|
57
57
|
placeholder?: string;
|
|
58
58
|
/**
|
|
@@ -94,6 +94,7 @@ function Dropdown({
|
|
|
94
94
|
isMulti,
|
|
95
95
|
renderListItem,
|
|
96
96
|
style: styleProp,
|
|
97
|
+
selectAll,
|
|
97
98
|
}: DropdownProps) {
|
|
98
99
|
const id = useId(propId);
|
|
99
100
|
|
|
@@ -142,6 +143,7 @@ function Dropdown({
|
|
|
142
143
|
onChange?.(next);
|
|
143
144
|
}}
|
|
144
145
|
renderListItem={renderListItem}
|
|
146
|
+
selectAll={selectAll}
|
|
145
147
|
selectedValues={selected}
|
|
146
148
|
{...menuProps}
|
|
147
149
|
/>
|
package/src/Menu.tsx
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import './menu.scss';
|
|
2
|
-
import { ComponentProps, CSSProperties, useMemo } from 'react';
|
|
2
|
+
import { ComponentProps, CSSProperties, ReactNode, useMemo } from 'react';
|
|
3
3
|
|
|
4
4
|
import { Checkbox } from './Checkbox';
|
|
5
5
|
import { ListItem } from './ListItem';
|
|
6
|
-
import { Txt } from './Txt';
|
|
7
6
|
import { useId } from './hooks/useId';
|
|
8
7
|
|
|
9
8
|
import { CommonProps, ElementProps, SetRef } from './';
|
|
10
9
|
|
|
10
|
+
const DEFAULT = {
|
|
11
|
+
selectAll: 'Select All',
|
|
12
|
+
};
|
|
13
|
+
|
|
11
14
|
export const MIN_ITEM_COUNT = 3;
|
|
12
15
|
export const MAX_ITEM_COUNT = 10;
|
|
13
16
|
|
|
@@ -68,12 +71,8 @@ export type MenuProps<T extends MenuItem = MenuItem> = CommonProps<'disabled' |
|
|
|
68
71
|
items?: T[];
|
|
69
72
|
/** A ref to the inner div element. */
|
|
70
73
|
innerRef?: SetRef<HTMLDivElement>;
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
*
|
|
74
|
-
* @type multiline
|
|
75
|
-
*/
|
|
76
|
-
noResultsMessage?: string;
|
|
74
|
+
/** Message to display when no results are found */
|
|
75
|
+
noResultsMessage?: ReactNode;
|
|
77
76
|
/** The index of the currently highlighted item. */
|
|
78
77
|
activeIndex?: number;
|
|
79
78
|
/** The values of the selected items */
|
|
@@ -92,6 +91,17 @@ export type MenuProps<T extends MenuItem = MenuItem> = CommonProps<'disabled' |
|
|
|
92
91
|
* @default false
|
|
93
92
|
*/
|
|
94
93
|
isMulti?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* The label for the "Select All" option.
|
|
96
|
+
*
|
|
97
|
+
* Ignored if `isMulti` is false.
|
|
98
|
+
*
|
|
99
|
+
* If `isMulti` is `true`, defaults to "Select All". If a string, it will be used as the label. If false the select
|
|
100
|
+
* all option will not be rendered.
|
|
101
|
+
*
|
|
102
|
+
* @default false
|
|
103
|
+
*/
|
|
104
|
+
selectAll?: boolean | string;
|
|
95
105
|
/**
|
|
96
106
|
* The function to call when the selected values change.
|
|
97
107
|
*
|
|
@@ -118,24 +128,39 @@ function Menu({
|
|
|
118
128
|
id: idProp,
|
|
119
129
|
renderListItem,
|
|
120
130
|
isMulti,
|
|
131
|
+
selectAll: selectAllProp,
|
|
121
132
|
...props
|
|
122
133
|
}: ElementProps<MenuProps, 'div'>) {
|
|
123
134
|
const menuId = useId(idProp);
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
()
|
|
135
|
+
|
|
136
|
+
const selectAll = useMemo(() => {
|
|
137
|
+
if (!isMulti) return false;
|
|
138
|
+
if (selectAllProp && typeof selectAllProp === 'string') return selectAllProp;
|
|
139
|
+
return selectAllProp === true ? DEFAULT.selectAll : false;
|
|
140
|
+
}, [isMulti, selectAllProp]);
|
|
141
|
+
|
|
142
|
+
const { items, itemCount } = useMemo(() => {
|
|
143
|
+
const itemsNext = Array.isArray(itemsProp) ? itemsProp : [];
|
|
144
|
+
return {
|
|
145
|
+
items: itemsNext,
|
|
127
146
|
// Ensure itemCount is within the range of items.length
|
|
128
|
-
Math.min(
|
|
129
|
-
|
|
147
|
+
itemCount: Math.min(
|
|
148
|
+
itemsNext.length,
|
|
130
149
|
// pin itemCountProp to a range of 3 to 10
|
|
131
150
|
Math.max(MIN_ITEM_COUNT, Math.min(itemCountProp, MAX_ITEM_COUNT)),
|
|
132
151
|
),
|
|
133
|
-
|
|
152
|
+
};
|
|
153
|
+
}, [itemCountProp, itemsProp]);
|
|
154
|
+
|
|
155
|
+
const allSelected = useMemo(
|
|
156
|
+
() => !!(items.length && items.every((item) => selectedValues.includes(item.value))),
|
|
157
|
+
[items, selectedValues],
|
|
134
158
|
);
|
|
135
159
|
|
|
136
160
|
return (
|
|
137
161
|
<div
|
|
138
162
|
{...props}
|
|
163
|
+
aria-multiselectable={isMulti || undefined}
|
|
139
164
|
data-bspk="menu"
|
|
140
165
|
data-disabled={disabled || undefined}
|
|
141
166
|
data-item-count={itemCount || undefined}
|
|
@@ -145,87 +170,101 @@ function Menu({
|
|
|
145
170
|
role="listbox"
|
|
146
171
|
style={{ ...props.style, '--item-count': itemCount } as CSSProperties}
|
|
147
172
|
>
|
|
148
|
-
{
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
<ListItem
|
|
167
|
-
{...renderProps}
|
|
168
|
-
active={activeIndex === index || undefined}
|
|
169
|
-
aria-disabled={item.disabled || undefined}
|
|
170
|
-
aria-posinset={index + 1}
|
|
171
|
-
aria-selected={selected || undefined}
|
|
172
|
-
as="button"
|
|
173
|
-
data-menu-item
|
|
174
|
-
data-selected={selected || undefined}
|
|
175
|
-
disabled={item.disabled || undefined}
|
|
176
|
-
id={itemId}
|
|
177
|
-
key={itemId}
|
|
178
|
-
label={renderProps?.label?.toString() || item.label?.toString()}
|
|
179
|
-
onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
|
180
|
-
if (renderProps) renderProps?.onClick?.(event);
|
|
181
|
-
|
|
182
|
-
if (onChange) {
|
|
183
|
-
if (!isMulti) {
|
|
184
|
-
onChange?.([item.value], event);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
onChange(
|
|
188
|
-
selected
|
|
189
|
-
? selectedValues.filter((value) => value !== item.value)
|
|
190
|
-
: [...selectedValues, item.value],
|
|
191
|
-
event,
|
|
192
|
-
);
|
|
193
|
-
}
|
|
173
|
+
{isMulti && selectAll && (
|
|
174
|
+
<ListItem
|
|
175
|
+
as="button"
|
|
176
|
+
data-selected={allSelected || undefined}
|
|
177
|
+
key="select-all"
|
|
178
|
+
label={selectAll}
|
|
179
|
+
onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
|
180
|
+
onChange?.(allSelected ? [] : items.map((item) => item.value), event);
|
|
181
|
+
}}
|
|
182
|
+
role="option"
|
|
183
|
+
tabIndex={-1}
|
|
184
|
+
trailing={
|
|
185
|
+
<Checkbox
|
|
186
|
+
aria-label={selectAll}
|
|
187
|
+
checked={!!allSelected}
|
|
188
|
+
name=""
|
|
189
|
+
onChange={(checked) => {
|
|
190
|
+
onChange?.(checked ? items.map((item) => item.value) : []);
|
|
194
191
|
}}
|
|
195
|
-
|
|
196
|
-
tabIndex={-1}
|
|
197
|
-
trailing={
|
|
198
|
-
isMulti ? (
|
|
199
|
-
<Checkbox
|
|
200
|
-
aria-label={item.label}
|
|
201
|
-
checked={selected}
|
|
202
|
-
name={item.value}
|
|
203
|
-
onChange={(checked) => {
|
|
204
|
-
onChange?.(
|
|
205
|
-
checked
|
|
206
|
-
? selectedValues.filter((value) => value !== item.value)
|
|
207
|
-
: [...selectedValues, item.value],
|
|
208
|
-
);
|
|
209
|
-
}}
|
|
210
|
-
value={item.value}
|
|
211
|
-
/>
|
|
212
|
-
) : (
|
|
213
|
-
renderProps?.trailing
|
|
214
|
-
)
|
|
215
|
-
}
|
|
192
|
+
value=""
|
|
216
193
|
/>
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
) : (
|
|
220
|
-
<>
|
|
221
|
-
<Txt as="div" variant="heading-h5">
|
|
222
|
-
No results found
|
|
223
|
-
</Txt>
|
|
224
|
-
<Txt as="div" variant="body-base">
|
|
225
|
-
{noResultsMessage}
|
|
226
|
-
</Txt>
|
|
227
|
-
</>
|
|
194
|
+
}
|
|
195
|
+
/>
|
|
228
196
|
)}
|
|
197
|
+
{items.length
|
|
198
|
+
? items.map((item, index) => {
|
|
199
|
+
const itemId = item.id || menuItemId(menuId, index);
|
|
200
|
+
|
|
201
|
+
const selected = Boolean(Array.isArray(selectedValues) && selectedValues.includes(item.value));
|
|
202
|
+
|
|
203
|
+
const renderProps = renderListItem?.({
|
|
204
|
+
activeIndex,
|
|
205
|
+
index,
|
|
206
|
+
item,
|
|
207
|
+
selectedValues,
|
|
208
|
+
isMulti,
|
|
209
|
+
menuId: menuId || '',
|
|
210
|
+
selected,
|
|
211
|
+
itemId,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<ListItem
|
|
216
|
+
{...renderProps}
|
|
217
|
+
active={activeIndex === index || undefined}
|
|
218
|
+
aria-disabled={item.disabled || undefined}
|
|
219
|
+
aria-posinset={index + 1}
|
|
220
|
+
aria-selected={selected || undefined}
|
|
221
|
+
as="button"
|
|
222
|
+
//data-selected={selected || undefined}
|
|
223
|
+
disabled={item.disabled || undefined}
|
|
224
|
+
id={itemId}
|
|
225
|
+
key={itemId}
|
|
226
|
+
label={renderProps?.label?.toString() || item.label?.toString()}
|
|
227
|
+
onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
|
228
|
+
if (renderProps) renderProps?.onClick?.(event);
|
|
229
|
+
|
|
230
|
+
if (onChange) {
|
|
231
|
+
if (!isMulti) {
|
|
232
|
+
onChange?.([item.value], event);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
onChange(
|
|
236
|
+
selected
|
|
237
|
+
? selectedValues.filter((value) => value !== item.value)
|
|
238
|
+
: [...selectedValues, item.value],
|
|
239
|
+
event,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
}}
|
|
243
|
+
role="option"
|
|
244
|
+
tabIndex={-1}
|
|
245
|
+
trailing={
|
|
246
|
+
isMulti ? (
|
|
247
|
+
<Checkbox
|
|
248
|
+
aria-label={item.label}
|
|
249
|
+
checked={selected}
|
|
250
|
+
name={item.value}
|
|
251
|
+
onChange={(checked) => {
|
|
252
|
+
onChange?.(
|
|
253
|
+
checked
|
|
254
|
+
? selectedValues.filter((value) => value !== item.value)
|
|
255
|
+
: [...selectedValues, item.value],
|
|
256
|
+
);
|
|
257
|
+
}}
|
|
258
|
+
value={item.value}
|
|
259
|
+
/>
|
|
260
|
+
) : (
|
|
261
|
+
renderProps?.trailing
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
/>
|
|
265
|
+
);
|
|
266
|
+
})
|
|
267
|
+
: noResultsMessage}
|
|
229
268
|
</div>
|
|
230
269
|
);
|
|
231
270
|
}
|
package/src/SearchBar.tsx
CHANGED
|
@@ -5,21 +5,26 @@ import { useRef } from 'react';
|
|
|
5
5
|
import { MenuItem, MenuProps, Menu } from './Menu';
|
|
6
6
|
import { Portal } from './Portal';
|
|
7
7
|
import { TextInputProps, TextInput } from './TextInput';
|
|
8
|
+
import { Txt } from './Txt';
|
|
8
9
|
import { useFloatingMenu } from './hooks/useFloatingMenu';
|
|
9
10
|
import { useId } from './hooks/useId';
|
|
10
11
|
//import { useFloatingMenu } from './hooks/useFloatingMenu';
|
|
11
12
|
|
|
12
|
-
export type SearchBarProps = Pick<MenuProps
|
|
13
|
+
export type SearchBarProps<T extends MenuItem = MenuItem> = Pick<MenuProps<T>, 'itemCount' | 'noResultsMessage'> &
|
|
13
14
|
Pick<TextInputProps, 'aria-label' | 'id' | 'inputRef' | 'name' | 'placeholder' | 'size'> & {
|
|
14
|
-
/**
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The current value of the search bar.
|
|
17
|
+
*
|
|
18
|
+
* @default Search
|
|
19
|
+
*/
|
|
20
|
+
value?: string;
|
|
16
21
|
/**
|
|
17
22
|
* Handler for state updates.
|
|
18
23
|
*
|
|
19
24
|
* @type (value: String) => void
|
|
20
25
|
* @required
|
|
21
26
|
*/
|
|
22
|
-
|
|
27
|
+
onChange: (value: string) => void;
|
|
23
28
|
/*
|
|
24
29
|
* Handler for item selection.
|
|
25
30
|
*
|
|
@@ -27,6 +32,38 @@ export type SearchBarProps = Pick<MenuProps, 'itemCount' | 'items' | 'noResultsM
|
|
|
27
32
|
* @required
|
|
28
33
|
*/
|
|
29
34
|
onSelect: (item?: MenuItem) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Content to display in the menu.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* [
|
|
40
|
+
* { value: '1', label: 'Apple Pie' },
|
|
41
|
+
* { value: '2', label: 'Banana Split' },
|
|
42
|
+
* { value: '3', label: 'Cherry Tart' },
|
|
43
|
+
* { value: '4', label: 'Dragonfruit Sorbet' },
|
|
44
|
+
* { value: '5', label: 'Elderberry Jam' },
|
|
45
|
+
* { value: '6', label: 'Fig Newton' },
|
|
46
|
+
* { value: '7', label: 'Grape Soda' },
|
|
47
|
+
* { value: '8', label: 'Honeydew Smoothie' },
|
|
48
|
+
* { value: '9', label: 'Ice Cream Sandwich' },
|
|
49
|
+
* { value: '10', label: 'Jackfruit Pudding' },
|
|
50
|
+
* ];
|
|
51
|
+
*
|
|
52
|
+
* @type Array<MenuItem>
|
|
53
|
+
*/
|
|
54
|
+
items?: T[];
|
|
55
|
+
/**
|
|
56
|
+
* Message to display when no results are found
|
|
57
|
+
*
|
|
58
|
+
* @type multiline
|
|
59
|
+
*/
|
|
60
|
+
noResultsMessage?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Whether to show or hide menu.
|
|
63
|
+
*
|
|
64
|
+
* @default true
|
|
65
|
+
*/
|
|
66
|
+
showMenu?: boolean;
|
|
30
67
|
};
|
|
31
68
|
|
|
32
69
|
/**
|
|
@@ -45,8 +82,9 @@ function SearchBar({
|
|
|
45
82
|
name,
|
|
46
83
|
size = 'medium',
|
|
47
84
|
onSelect,
|
|
48
|
-
|
|
49
|
-
|
|
85
|
+
value,
|
|
86
|
+
onChange,
|
|
87
|
+
showMenu = true,
|
|
50
88
|
}: SearchBarProps) {
|
|
51
89
|
const id = useId(idProp);
|
|
52
90
|
const {
|
|
@@ -61,49 +99,66 @@ function SearchBar({
|
|
|
61
99
|
|
|
62
100
|
return (
|
|
63
101
|
<>
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
102
|
+
<div data-bspk="search-bar">
|
|
103
|
+
<TextInput
|
|
104
|
+
aria-label={ariaLabel}
|
|
105
|
+
autoComplete="off"
|
|
106
|
+
containerRef={triggerRef}
|
|
107
|
+
id={id}
|
|
108
|
+
inputRef={(node) => {
|
|
109
|
+
inputRef?.(node || null);
|
|
110
|
+
inputRefLocal.current = node;
|
|
111
|
+
}}
|
|
112
|
+
leading={<SvgSearch />}
|
|
113
|
+
name={name}
|
|
114
|
+
onChange={(str) => onChange(str)}
|
|
115
|
+
placeholder={placeholder}
|
|
116
|
+
size={size}
|
|
117
|
+
value={value}
|
|
118
|
+
{...triggerProps}
|
|
119
|
+
onClick={(event) => {
|
|
120
|
+
if (items?.length) onClick(event);
|
|
121
|
+
}}
|
|
122
|
+
onKeyDownCapture={(event) => {
|
|
123
|
+
const handled = onKeyDownCapture(event);
|
|
86
124
|
|
|
87
|
-
|
|
125
|
+
if (handled) return;
|
|
88
126
|
|
|
89
|
-
|
|
90
|
-
}}
|
|
91
|
-
/>
|
|
92
|
-
<Portal>
|
|
93
|
-
<Menu
|
|
94
|
-
itemCount={itemCount}
|
|
95
|
-
items={items}
|
|
96
|
-
noResultsMessage={noResultsMessage}
|
|
97
|
-
onChange={(selectedValues, event) => {
|
|
98
|
-
event?.preventDefault();
|
|
99
|
-
const item = items?.find((i) => i.value === selectedValues[0]);
|
|
100
|
-
onSelect?.(item);
|
|
101
|
-
setSearchValue(item?.label || '');
|
|
102
|
-
closeMenu();
|
|
127
|
+
inputRefLocal.current?.focus();
|
|
103
128
|
}}
|
|
104
|
-
{...menuProps}
|
|
105
129
|
/>
|
|
106
|
-
</
|
|
130
|
+
</div>
|
|
131
|
+
{showMenu && (
|
|
132
|
+
<Portal>
|
|
133
|
+
<Menu
|
|
134
|
+
itemCount={itemCount}
|
|
135
|
+
items={items}
|
|
136
|
+
noResultsMessage={
|
|
137
|
+
!!value?.length &&
|
|
138
|
+
!items?.length && (
|
|
139
|
+
<>
|
|
140
|
+
<Txt as="div" variant="heading-h5">
|
|
141
|
+
No results found
|
|
142
|
+
</Txt>
|
|
143
|
+
{noResultsMessage && (
|
|
144
|
+
<Txt as="div" variant="body-base">
|
|
145
|
+
{noResultsMessage}
|
|
146
|
+
</Txt>
|
|
147
|
+
)}
|
|
148
|
+
</>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
onChange={(selectedValues, event) => {
|
|
152
|
+
event?.preventDefault();
|
|
153
|
+
const item = items?.find((i) => i.value === selectedValues[0]);
|
|
154
|
+
onSelect?.(item);
|
|
155
|
+
onChange(item?.label || '');
|
|
156
|
+
closeMenu();
|
|
157
|
+
}}
|
|
158
|
+
{...menuProps}
|
|
159
|
+
/>
|
|
160
|
+
</Portal>
|
|
161
|
+
)}
|
|
107
162
|
</>
|
|
108
163
|
);
|
|
109
164
|
}
|
package/src/avatar.scss
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
[data-bspk='avatar'] {
|
|
2
2
|
--height: var(--spacing-sizing-10);
|
|
3
3
|
--font: var(--labels-base);
|
|
4
|
+
--svg-size: var(--spacing-sizing-10);
|
|
4
5
|
|
|
5
6
|
&:not([data-color]) {
|
|
6
7
|
--foreground: var(--foreground-neutral-on-surface);
|
|
@@ -28,49 +29,69 @@
|
|
|
28
29
|
max-width: 100%;
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
svg {
|
|
33
|
+
width: var(--svg-size);
|
|
34
|
+
height: var(--svg-size);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[data-icon] {
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
justify-content: center;
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
&[data-size='x-small'] {
|
|
32
44
|
--height: var(--spacing-sizing-06);
|
|
33
45
|
--font: var(--labels-x-small);
|
|
46
|
+
--svg-size: var(--spacing-sizing-04);
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
&[data-size='small'] {
|
|
37
50
|
--height: var(--spacing-sizing-08);
|
|
38
51
|
--font: var(--labels-small);
|
|
52
|
+
--svg-size: var(--spacing-sizing-05);
|
|
39
53
|
}
|
|
40
54
|
|
|
41
55
|
&[data-size='medium'] {
|
|
42
56
|
--height: var(--spacing-sizing-10);
|
|
43
57
|
--font: var(--labels-base);
|
|
58
|
+
--svg-size: var(--spacing-sizing-05);
|
|
44
59
|
}
|
|
45
60
|
|
|
46
61
|
&[data-size='large'] {
|
|
47
62
|
--height: var(--spacing-sizing-12);
|
|
48
63
|
--font: var(--labels-large);
|
|
64
|
+
--svg-size: var(--spacing-sizing-06);
|
|
49
65
|
}
|
|
50
66
|
|
|
51
67
|
&[data-size='x-large'] {
|
|
52
68
|
--height: var(--spacing-sizing-14);
|
|
53
69
|
--font: var(--subheader-x-large);
|
|
70
|
+
--svg-size: var(--spacing-sizing-08);
|
|
54
71
|
}
|
|
55
72
|
|
|
56
73
|
&[data-size='xx-large'] {
|
|
57
74
|
--height: var(--spacing-sizing-17);
|
|
58
75
|
--font: var(--subheader-xx-large);
|
|
76
|
+
--svg-size: var(--spacing-sizing-09);
|
|
59
77
|
}
|
|
60
78
|
|
|
61
79
|
&[data-size='xxx-large'] {
|
|
62
80
|
--height: var(--spacing-sizing-19);
|
|
63
81
|
--font: var(--display-regular-small);
|
|
82
|
+
--svg-size: var(--spacing-sizing-12);
|
|
64
83
|
}
|
|
65
84
|
|
|
66
85
|
&[data-size='xxxx-large'] {
|
|
67
86
|
--height: var(--spacing-sizing-21);
|
|
68
87
|
--font: var(--display-regular-medium);
|
|
88
|
+
--svg-size: var(--spacing-sizing-15);
|
|
69
89
|
}
|
|
70
90
|
|
|
71
91
|
&[data-size='xxxxx-large'] {
|
|
72
92
|
--height: var(--spacing-sizing-23);
|
|
73
93
|
--font: var(--display-regular-large);
|
|
94
|
+
--svg-size: var(--spacing-sizing-17);
|
|
74
95
|
}
|
|
75
96
|
}
|
|
76
97
|
|
package/src/demo/examples.tsx
CHANGED
|
@@ -19,6 +19,7 @@ import { ModalProps } from '../Modal';
|
|
|
19
19
|
import { Popover, PopoverProps } from '../Popover';
|
|
20
20
|
import { ProgressionStepperProps } from '../ProgressionStepper';
|
|
21
21
|
import { Radio } from '../Radio';
|
|
22
|
+
import { SearchBarProps } from '../SearchBar';
|
|
22
23
|
import { SegmentedControlProps } from '../SegmentedControl';
|
|
23
24
|
import { Switch } from '../Switch';
|
|
24
25
|
import { TabGroupProps } from '../TabGroup';
|
|
@@ -550,6 +551,32 @@ export const examples: (setState: DemoSetState, action: DemoAction) => Record<st
|
|
|
550
551
|
},
|
|
551
552
|
]),
|
|
552
553
|
},
|
|
554
|
+
SearchBar: {
|
|
555
|
+
containerStyle: { width: '400px' },
|
|
556
|
+
render: ({ props: state, Component, preset }) => {
|
|
557
|
+
const props = { ...state } as SearchBarProps;
|
|
558
|
+
|
|
559
|
+
if (preset?.label === 'Show Filtered Items') {
|
|
560
|
+
const searchValue = (props.value as string | undefined)?.trim()?.toLowerCase() || '';
|
|
561
|
+
if (Array.isArray(props.items) && searchValue.length)
|
|
562
|
+
props.items = props.items?.filter((item: MenuItem) =>
|
|
563
|
+
item.label.toLowerCase().includes(searchValue),
|
|
564
|
+
);
|
|
565
|
+
props.showMenu = !!searchValue;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return <Component {...props} items={props.items || []} />;
|
|
569
|
+
},
|
|
570
|
+
presets: setPresets<SearchBarProps>([
|
|
571
|
+
{
|
|
572
|
+
// we change the items and showMenu based on the input value
|
|
573
|
+
label: 'Show Filtered Items',
|
|
574
|
+
state: {
|
|
575
|
+
showMenu: false,
|
|
576
|
+
},
|
|
577
|
+
},
|
|
578
|
+
]),
|
|
579
|
+
},
|
|
553
580
|
TextInput: {
|
|
554
581
|
containerStyle: { width: '280px' },
|
|
555
582
|
presets: setPresets<TextInputProps>([
|
package/src/dropdown.scss
CHANGED
|
@@ -79,7 +79,8 @@ export function useKeyboardNavigation(
|
|
|
79
79
|
if (next >= itemElements.length) next = 0;
|
|
80
80
|
|
|
81
81
|
itemElements.forEach((el, index) => {
|
|
82
|
-
|
|
82
|
+
if (index === next) el.setAttribute('data-selected', 'true');
|
|
83
|
+
else el.removeAttribute('data-selected');
|
|
83
84
|
});
|
|
84
85
|
|
|
85
86
|
scrollElementIntoView(itemElements[next], containerElement);
|
|
@@ -90,7 +91,7 @@ export function useKeyboardNavigation(
|
|
|
90
91
|
return {
|
|
91
92
|
handleKeyNavigation: handleArrowKeyNavigation,
|
|
92
93
|
selectedIndex,
|
|
93
|
-
selectedId: selectedIndex === -1 ? undefined : containerElement.children[selectedIndex]
|
|
94
|
+
selectedId: selectedIndex === -1 ? undefined : containerElement.children[selectedIndex]?.id,
|
|
94
95
|
setSelectedIndex: setSelectedIndex,
|
|
95
96
|
};
|
|
96
97
|
}
|