@dxos/react-ui-searchlist 0.8.4-main.69d29f4 → 0.8.4-main.6fa680abb7
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/README.md +1 -1
- package/dist/lib/browser/index.mjs +36 -37
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +36 -37
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Combobox/Combobox.d.ts +3 -2
- package/dist/types/src/components/Combobox/Combobox.d.ts.map +1 -1
- package/dist/types/src/components/SearchList/SearchList.d.ts +12 -9
- package/dist/types/src/components/SearchList/SearchList.d.ts.map +1 -1
- package/dist/types/src/components/SearchList/SearchList.stories.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/components/Combobox/Combobox.stories.tsx +1 -1
- package/src/components/Combobox/Combobox.tsx +9 -22
- package/src/components/Listbox/Listbox.stories.tsx +1 -1
- package/src/components/Listbox/Listbox.tsx +2 -2
- package/src/components/SearchList/SearchList.stories.tsx +33 -35
- package/src/components/SearchList/SearchList.tsx +39 -44
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-searchlist",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.6fa680abb7",
|
|
4
4
|
"description": "A themed ⌘K-style combobox component, triggered by a button (or keyboard shortcut), where values are queried only within the invoked modal.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -40,17 +40,17 @@
|
|
|
40
40
|
"@types/react-dom": "~19.2.3",
|
|
41
41
|
"react": "~19.2.3",
|
|
42
42
|
"react-dom": "~19.2.3",
|
|
43
|
-
"vite": "7.1.
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/ui-theme": "0.8.4-main.
|
|
43
|
+
"vite": "^7.1.11",
|
|
44
|
+
"@dxos/storybook-utils": "0.8.4-main.6fa680abb7",
|
|
45
|
+
"@dxos/random": "0.8.4-main.6fa680abb7",
|
|
46
|
+
"@dxos/react-ui": "0.8.4-main.6fa680abb7",
|
|
47
|
+
"@dxos/ui-theme": "0.8.4-main.6fa680abb7"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"react": "~19.2.3",
|
|
51
51
|
"react-dom": "~19.2.3",
|
|
52
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
53
|
-
"@dxos/ui-theme": "0.8.4-main.
|
|
52
|
+
"@dxos/react-ui": "0.8.4-main.6fa680abb7",
|
|
53
|
+
"@dxos/ui-theme": "0.8.4-main.6fa680abb7"
|
|
54
54
|
},
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"access": "public"
|
|
@@ -47,7 +47,7 @@ const meta = {
|
|
|
47
47
|
title: 'ui/react-ui-searchlist/Combobox',
|
|
48
48
|
component: Combobox.Root as any,
|
|
49
49
|
render: DefaultStory,
|
|
50
|
-
decorators: [withTheme, withLayout({ layout: 'column', classNames: 'p-2' })],
|
|
50
|
+
decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'p-2' })],
|
|
51
51
|
parameters: {
|
|
52
52
|
translations,
|
|
53
53
|
},
|
|
@@ -16,15 +16,15 @@ import {
|
|
|
16
16
|
type PopoverVirtualTriggerProps,
|
|
17
17
|
} from '@dxos/react-ui';
|
|
18
18
|
import { useId } from '@dxos/react-ui';
|
|
19
|
-
import { mx
|
|
19
|
+
import { mx } from '@dxos/ui-theme';
|
|
20
20
|
|
|
21
21
|
import {
|
|
22
22
|
SearchList,
|
|
23
|
-
type SearchListContentProps,
|
|
24
23
|
type SearchListEmptyProps,
|
|
25
24
|
type SearchListInputProps,
|
|
26
25
|
type SearchListItemProps,
|
|
27
26
|
type SearchListRootProps,
|
|
27
|
+
type SearchListViewportProps,
|
|
28
28
|
} from '../SearchList';
|
|
29
29
|
|
|
30
30
|
const COMBOBOX_NAME = 'Combobox';
|
|
@@ -157,16 +157,14 @@ const ComboboxContent = forwardRef<HTMLDivElement, ComboboxContentProps>(
|
|
|
157
157
|
forceMount,
|
|
158
158
|
}}
|
|
159
159
|
classNames={[
|
|
160
|
-
'
|
|
160
|
+
'w-(--radix-popover-trigger-width) max-h-(--radix-popover-content-available-height) grid grid-rows-[min-content_1fr]',
|
|
161
161
|
classNames,
|
|
162
162
|
]}
|
|
163
163
|
id={modalId}
|
|
164
164
|
ref={forwardedRef}
|
|
165
165
|
>
|
|
166
166
|
<SearchList.Root onSearch={onSearch} value={value} defaultValue={defaultValue} debounceMs={debounceMs}>
|
|
167
|
-
<
|
|
168
|
-
{children}
|
|
169
|
-
</div>
|
|
167
|
+
<SearchList.Content>{children}</SearchList.Content>
|
|
170
168
|
</SearchList.Root>
|
|
171
169
|
</Popover.Content>
|
|
172
170
|
);
|
|
@@ -205,9 +203,7 @@ const ComboboxTrigger = forwardRef<HTMLButtonElement, ComboboxTriggerProps>(
|
|
|
205
203
|
>
|
|
206
204
|
{children ?? (
|
|
207
205
|
<>
|
|
208
|
-
<span
|
|
209
|
-
className={mx('font-normal text-start flex-1 min-is-0 truncate mie-2', !value && staticPlaceholderText)}
|
|
210
|
-
>
|
|
206
|
+
<span className={mx('font-normal text-start flex-1 min-w-0 truncate me-2', !value && 'text-subdued')}>
|
|
211
207
|
{value || placeholder}
|
|
212
208
|
</span>
|
|
213
209
|
<Icon icon='ph--caret-down--bold' size={3} />
|
|
@@ -239,10 +235,7 @@ const ComboboxInput = forwardRef<HTMLInputElement, ComboboxInputProps>(({ classN
|
|
|
239
235
|
return (
|
|
240
236
|
<SearchList.Input
|
|
241
237
|
{...props}
|
|
242
|
-
classNames={[
|
|
243
|
-
'mli-cardSpacingChrome mbs-cardSpacingChrome mbe-0 is-[calc(100%-2*var(--dx-cardSpacingChrome))]',
|
|
244
|
-
classNames,
|
|
245
|
-
]}
|
|
238
|
+
classNames={['m-form-chrome mb-0 w-[calc(100%-2*var(--spacing-form-chrome))]', classNames]}
|
|
246
239
|
ref={forwardedRef}
|
|
247
240
|
/>
|
|
248
241
|
);
|
|
@@ -252,16 +245,10 @@ const ComboboxInput = forwardRef<HTMLInputElement, ComboboxInputProps>(({ classN
|
|
|
252
245
|
// List
|
|
253
246
|
//
|
|
254
247
|
|
|
255
|
-
type ComboboxListProps =
|
|
248
|
+
type ComboboxListProps = SearchListViewportProps;
|
|
256
249
|
|
|
257
250
|
const ComboboxList = forwardRef<HTMLDivElement, ComboboxListProps>(({ classNames, ...props }, forwardedRef) => {
|
|
258
|
-
return
|
|
259
|
-
<SearchList.Content
|
|
260
|
-
{...props}
|
|
261
|
-
classNames={['min-bs-0 overflow-y-auto plb-cardSpacingChrome', classNames]}
|
|
262
|
-
ref={forwardedRef}
|
|
263
|
-
/>
|
|
264
|
-
);
|
|
251
|
+
return <SearchList.Viewport {...props} classNames={['py-form-chrome', classNames]} ref={forwardedRef} />;
|
|
265
252
|
});
|
|
266
253
|
|
|
267
254
|
//
|
|
@@ -290,7 +277,7 @@ const ComboboxItem = forwardRef<HTMLDivElement, ComboboxItemProps>(
|
|
|
290
277
|
<SearchList.Item
|
|
291
278
|
{...props}
|
|
292
279
|
value={value}
|
|
293
|
-
classNames={['
|
|
280
|
+
classNames={['mx-form-chrome px-form-chrome', classNames]}
|
|
294
281
|
onSelect={handleSelect}
|
|
295
282
|
ref={forwardedRef}
|
|
296
283
|
/>
|
|
@@ -40,7 +40,7 @@ const meta = {
|
|
|
40
40
|
title: 'ui/react-ui-searchlist/Listbox',
|
|
41
41
|
component: Listbox.Root,
|
|
42
42
|
render: DefaultStory,
|
|
43
|
-
decorators: [withTheme, withLayout({ layout: 'column', classNames: 'p-2' })],
|
|
43
|
+
decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'p-2' })],
|
|
44
44
|
parameters: {
|
|
45
45
|
translations,
|
|
46
46
|
},
|
|
@@ -100,7 +100,7 @@ const ListboxRoot = forwardRef<HTMLUListElement, ListboxRootProps>(
|
|
|
100
100
|
<ul
|
|
101
101
|
role='listbox'
|
|
102
102
|
{...rootProps}
|
|
103
|
-
className={mx('
|
|
103
|
+
className={mx('w-full p-form-chrome', classNames)}
|
|
104
104
|
ref={rootRef}
|
|
105
105
|
{...arrowGroup}
|
|
106
106
|
>
|
|
@@ -137,7 +137,7 @@ const ListboxOption = forwardRef<HTMLLIElement, ListboxOptionProps>(
|
|
|
137
137
|
tabIndex={0}
|
|
138
138
|
className={mx(
|
|
139
139
|
'dx-focus-ring',
|
|
140
|
-
'
|
|
140
|
+
'py-1 px-2 rounded-xs select-none cursor-pointer data-[selected=true]:bg-hover-overlay hover:bg-hover-overlay',
|
|
141
141
|
commandItem,
|
|
142
142
|
classNames,
|
|
143
143
|
)}
|
|
@@ -39,28 +39,26 @@ const DefaultStory = ({ items = defaultItems }: DefaultStoryProps) => {
|
|
|
39
39
|
const { results, handleSearch } = useSearchListResults({ items });
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
|
-
<
|
|
43
|
-
<SearchList.
|
|
42
|
+
<SearchList.Root onSearch={handleSearch}>
|
|
43
|
+
<SearchList.Content classNames='h-[400px]'>
|
|
44
44
|
<SearchList.Input placeholder='Search items...' autoFocus />
|
|
45
|
-
<SearchList.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
</SearchList.Root>
|
|
63
|
-
</div>
|
|
45
|
+
<SearchList.Viewport>
|
|
46
|
+
{results.length > 0 ? (
|
|
47
|
+
results.map((item) => (
|
|
48
|
+
<SearchList.Item
|
|
49
|
+
key={item.id}
|
|
50
|
+
value={item.id}
|
|
51
|
+
label={item.label}
|
|
52
|
+
icon={item.icon}
|
|
53
|
+
onSelect={() => console.log('[SearchList.Item.onSelect]', item.id, item.label)}
|
|
54
|
+
/>
|
|
55
|
+
))
|
|
56
|
+
) : (
|
|
57
|
+
<SearchList.Empty>No results found</SearchList.Empty>
|
|
58
|
+
)}
|
|
59
|
+
</SearchList.Viewport>
|
|
60
|
+
</SearchList.Content>
|
|
61
|
+
</SearchList.Root>
|
|
64
62
|
);
|
|
65
63
|
};
|
|
66
64
|
|
|
@@ -87,7 +85,7 @@ const ControlledStory = ({ items = defaultItems }: DefaultStoryProps) => {
|
|
|
87
85
|
};
|
|
88
86
|
|
|
89
87
|
return (
|
|
90
|
-
<div className='
|
|
88
|
+
<div className='w-full h-[400px] flex flex-col gap-2'>
|
|
91
89
|
<div className='text-sm text-description'>Controlled query: "{query}"</div>
|
|
92
90
|
<SearchList.Root onSearch={handleSearch} value={query}>
|
|
93
91
|
<SearchList.Input placeholder='Controlled search...' onChange={(e) => handleQueryChange(e.target.value)} />
|
|
@@ -105,7 +103,7 @@ const ControlledStory = ({ items = defaultItems }: DefaultStoryProps) => {
|
|
|
105
103
|
</SearchList.Viewport>
|
|
106
104
|
</SearchList.Content>
|
|
107
105
|
</SearchList.Root>
|
|
108
|
-
<button className='
|
|
106
|
+
<button className='px-2 py-1 rounded-sm bg-accent-surface text-accent-text' onClick={() => handleQueryChange('')}>
|
|
109
107
|
Clear Query
|
|
110
108
|
</button>
|
|
111
109
|
</div>
|
|
@@ -146,7 +144,7 @@ const CustomItem = ({ value, label, description, onSelect }: CustomItemProps) =>
|
|
|
146
144
|
role='option'
|
|
147
145
|
aria-selected={isSelected}
|
|
148
146
|
data-selected={isSelected}
|
|
149
|
-
className={`p-2 border-
|
|
147
|
+
className={`p-2 border-b border-separator cursor-pointer ${isSelected ? 'bg-hover-overlay' : 'hover:bg-hover-overlay'}`}
|
|
150
148
|
onClick={onSelect}
|
|
151
149
|
>
|
|
152
150
|
<div className='font-medium'>{label}</div>
|
|
@@ -159,7 +157,7 @@ const CustomRenderingStory = ({ items = defaultItems }: DefaultStoryProps) => {
|
|
|
159
157
|
const { results, handleSearch } = useSearchListResults({ items });
|
|
160
158
|
|
|
161
159
|
return (
|
|
162
|
-
<div className='
|
|
160
|
+
<div className='w-full h-[400px] flex flex-col'>
|
|
163
161
|
<SearchList.Root onSearch={handleSearch}>
|
|
164
162
|
<SearchList.Input placeholder='Search with custom rendering...' autoFocus />
|
|
165
163
|
<SearchList.Content>
|
|
@@ -192,7 +190,7 @@ const WithEmptyStory = () => {
|
|
|
192
190
|
};
|
|
193
191
|
|
|
194
192
|
return (
|
|
195
|
-
<div className='
|
|
193
|
+
<div className='w-full h-[400px] flex flex-col'>
|
|
196
194
|
<SearchList.Root onSearch={handleSearch}>
|
|
197
195
|
<SearchList.Input placeholder='Try searching for anything...' />
|
|
198
196
|
<SearchList.Content>
|
|
@@ -221,7 +219,7 @@ const WithoutViewportStory = ({ items = defaultItems }: DefaultStoryProps) => {
|
|
|
221
219
|
const { results, handleSearch } = useSearchListResults({ items });
|
|
222
220
|
|
|
223
221
|
return (
|
|
224
|
-
<div className='
|
|
222
|
+
<div className='w-full h-[300px] flex flex-col'>
|
|
225
223
|
<SearchList.Root onSearch={handleSearch}>
|
|
226
224
|
<SearchList.Input placeholder='Search without viewport (no scroll)...' classNames='shrink-0' />
|
|
227
225
|
<SearchList.Content>
|
|
@@ -254,7 +252,7 @@ const iconsItems: StoryItem[] = [
|
|
|
254
252
|
|
|
255
253
|
const WithIconsStory = () => {
|
|
256
254
|
return (
|
|
257
|
-
<div className='
|
|
255
|
+
<div className='w-full flex flex-col'>
|
|
258
256
|
<SearchList.Root>
|
|
259
257
|
<SearchList.Input placeholder='Search items with icons...' />
|
|
260
258
|
<SearchList.Content>
|
|
@@ -331,17 +329,17 @@ const CustomInput = () => {
|
|
|
331
329
|
};
|
|
332
330
|
|
|
333
331
|
return (
|
|
334
|
-
<div className='flex gap-2 items-center p-2 bg-input rounded'>
|
|
332
|
+
<div className='flex gap-2 items-center p-2 bg-input rounded-sm'>
|
|
335
333
|
<input
|
|
336
334
|
type='text'
|
|
337
335
|
value={query}
|
|
338
336
|
onChange={(ev) => onQueryChange(ev.target.value)}
|
|
339
337
|
onKeyDown={handleKeyDown}
|
|
340
338
|
placeholder='Custom input...'
|
|
341
|
-
className='bg-transparent outline-
|
|
339
|
+
className='bg-transparent outline-hidden grow'
|
|
342
340
|
/>
|
|
343
341
|
{query && (
|
|
344
|
-
<button onClick={() => onQueryChange('')} className='text-description hover:text-
|
|
342
|
+
<button onClick={() => onQueryChange('')} className='text-description hover:text-base-surface-text'>
|
|
345
343
|
✕
|
|
346
344
|
</button>
|
|
347
345
|
)}
|
|
@@ -353,7 +351,7 @@ const CustomInputStory = ({ items = defaultItems }: DefaultStoryProps) => {
|
|
|
353
351
|
const { results, handleSearch } = useSearchListResults({ items });
|
|
354
352
|
|
|
355
353
|
return (
|
|
356
|
-
<div className='
|
|
354
|
+
<div className='w-full h-[400px] flex flex-col border border-separator'>
|
|
357
355
|
<SearchList.Root onSearch={handleSearch}>
|
|
358
356
|
<CustomInput />
|
|
359
357
|
<SearchList.Content>
|
|
@@ -388,7 +386,7 @@ const disabledItems: StoryItem[] = [
|
|
|
388
386
|
|
|
389
387
|
const WithDisabledItemsStory = () => {
|
|
390
388
|
return (
|
|
391
|
-
<div className='
|
|
389
|
+
<div className='w-full flex flex-col'>
|
|
392
390
|
<SearchList.Root>
|
|
393
391
|
<SearchList.Input placeholder='Arrow keys skip disabled items...' autoFocus />
|
|
394
392
|
<SearchList.Content>
|
|
@@ -438,7 +436,7 @@ const WithGroupsStory = () => {
|
|
|
438
436
|
);
|
|
439
437
|
|
|
440
438
|
return (
|
|
441
|
-
<div className='
|
|
439
|
+
<div className='w-full h-[400px] flex flex-col'>
|
|
442
440
|
<SearchList.Root onSearch={handleSearch}>
|
|
443
441
|
<SearchList.Input placeholder='Search grouped items...' autoFocus />
|
|
444
442
|
<SearchList.Content>
|
|
@@ -471,7 +469,7 @@ const WithGroupsStory = () => {
|
|
|
471
469
|
const meta = {
|
|
472
470
|
title: 'ui/react-ui-searchlist/SearchList',
|
|
473
471
|
component: SearchList.Root as any,
|
|
474
|
-
decorators: [withTheme, withLayout({ layout: 'column' })],
|
|
472
|
+
decorators: [withTheme(), withLayout({ layout: 'column' })],
|
|
475
473
|
parameters: {
|
|
476
474
|
layout: 'fullscreen',
|
|
477
475
|
translations,
|
|
@@ -6,6 +6,7 @@ import { useControllableState } from '@radix-ui/react-use-controllable-state';
|
|
|
6
6
|
import React, {
|
|
7
7
|
type ChangeEvent,
|
|
8
8
|
type ComponentPropsWithRef,
|
|
9
|
+
type ComponentPropsWithoutRef,
|
|
9
10
|
type KeyboardEvent,
|
|
10
11
|
type PropsWithChildren,
|
|
11
12
|
type ReactNode,
|
|
@@ -21,13 +22,14 @@ import {
|
|
|
21
22
|
type Density,
|
|
22
23
|
type Elevation,
|
|
23
24
|
Icon,
|
|
25
|
+
ScrollArea,
|
|
24
26
|
type ThemedClassName,
|
|
25
27
|
useDensityContext,
|
|
26
28
|
useElevationContext,
|
|
27
29
|
useThemeContext,
|
|
28
30
|
useTranslation,
|
|
29
31
|
} from '@dxos/react-ui';
|
|
30
|
-
import {
|
|
32
|
+
import { mx } from '@dxos/ui-theme';
|
|
31
33
|
|
|
32
34
|
import { translationKey } from '../../translations';
|
|
33
35
|
|
|
@@ -55,10 +57,13 @@ type ItemData = {
|
|
|
55
57
|
type SearchListRootProps = PropsWithChildren<{
|
|
56
58
|
/** Controlled query value. */
|
|
57
59
|
value?: string;
|
|
60
|
+
|
|
58
61
|
/** Default query value for uncontrolled mode. */
|
|
59
62
|
defaultValue?: string;
|
|
63
|
+
|
|
60
64
|
/** Debounce delay in milliseconds. */
|
|
61
65
|
debounceMs?: number;
|
|
66
|
+
|
|
62
67
|
/** Callback when search query changes (debounced). */
|
|
63
68
|
onSearch?: (query: string) => void;
|
|
64
69
|
}>;
|
|
@@ -223,45 +228,16 @@ const SearchListRoot = ({
|
|
|
223
228
|
|
|
224
229
|
SearchListRoot.displayName = 'SearchList.Root';
|
|
225
230
|
|
|
226
|
-
//
|
|
227
|
-
// Viewport
|
|
228
|
-
//
|
|
229
|
-
|
|
230
|
-
type SearchListViewportProps = ThemedClassName<PropsWithChildren<{}>>;
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Scrollable viewport wrapper for Content.
|
|
234
|
-
* Only Content wrapped in Viewport will be scrollable.
|
|
235
|
-
*/
|
|
236
|
-
// TODO(burdon): Reconcile with Mosaic.Viewport.
|
|
237
|
-
const SearchListViewport = ({ classNames, children }: SearchListViewportProps) => {
|
|
238
|
-
return (
|
|
239
|
-
<div role='none' className={mx('is-full min-bs-0 grow overflow-y-auto', classNames)}>
|
|
240
|
-
{children}
|
|
241
|
-
</div>
|
|
242
|
-
);
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
SearchListViewport.displayName = 'SearchList.Viewport';
|
|
246
|
-
|
|
247
231
|
//
|
|
248
232
|
// Content
|
|
249
233
|
//
|
|
250
234
|
|
|
251
|
-
type SearchListContentProps = ThemedClassName<
|
|
235
|
+
type SearchListContentProps = ThemedClassName<ComponentPropsWithoutRef<'div'>>;
|
|
252
236
|
|
|
253
|
-
/**
|
|
254
|
-
* Container for search results. Does not scroll by default.
|
|
255
|
-
* Wrap in Viewport for scrollable content.
|
|
256
|
-
*/
|
|
257
237
|
const SearchListContent = forwardRef<HTMLDivElement, SearchListContentProps>(
|
|
258
|
-
({ classNames, children }, forwardedRef) => {
|
|
238
|
+
({ classNames, children, ...props }, forwardedRef) => {
|
|
259
239
|
return (
|
|
260
|
-
<div
|
|
261
|
-
ref={forwardedRef}
|
|
262
|
-
role='listbox'
|
|
263
|
-
className={mx('flex flex-col is-full min-bs-0 grow overflow-hidden', classNames)}
|
|
264
|
-
>
|
|
240
|
+
<div role='none' {...props} className={mx('dx-container flex flex-col gap-3', classNames)} ref={forwardedRef}>
|
|
265
241
|
{children}
|
|
266
242
|
</div>
|
|
267
243
|
);
|
|
@@ -378,11 +354,9 @@ const SearchListInput = forwardRef<HTMLInputElement, SearchListInputProps>(
|
|
|
378
354
|
{...props}
|
|
379
355
|
{...(props.autoFocus && !hasIosKeyboard && { autoFocus: true })}
|
|
380
356
|
type='text'
|
|
381
|
-
value={query}
|
|
382
357
|
placeholder={placeholder ?? defaultPlaceholder}
|
|
383
358
|
className={tx(
|
|
384
359
|
'input.input',
|
|
385
|
-
'input',
|
|
386
360
|
{
|
|
387
361
|
variant,
|
|
388
362
|
disabled: props.disabled,
|
|
@@ -391,6 +365,7 @@ const SearchListInput = forwardRef<HTMLInputElement, SearchListInputProps>(
|
|
|
391
365
|
},
|
|
392
366
|
classNames,
|
|
393
367
|
)}
|
|
368
|
+
value={query}
|
|
394
369
|
onChange={handleChange}
|
|
395
370
|
onKeyDown={handleKeyDown}
|
|
396
371
|
ref={forwardedRef}
|
|
@@ -401,6 +376,24 @@ const SearchListInput = forwardRef<HTMLInputElement, SearchListInputProps>(
|
|
|
401
376
|
|
|
402
377
|
SearchListInput.displayName = 'SearchList.Input';
|
|
403
378
|
|
|
379
|
+
//
|
|
380
|
+
// Viewport
|
|
381
|
+
//
|
|
382
|
+
|
|
383
|
+
type SearchListViewportProps = ThemedClassName<PropsWithChildren>;
|
|
384
|
+
|
|
385
|
+
const SearchListViewport = forwardRef<HTMLDivElement, SearchListViewportProps>(
|
|
386
|
+
({ classNames, children }, forwardedRef) => {
|
|
387
|
+
return (
|
|
388
|
+
<ScrollArea.Root role='listbox' classNames={mx(classNames)} ref={forwardedRef} thin>
|
|
389
|
+
<ScrollArea.Viewport>{children}</ScrollArea.Viewport>
|
|
390
|
+
</ScrollArea.Root>
|
|
391
|
+
);
|
|
392
|
+
},
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
SearchListViewport.displayName = 'SearchList.Viewport';
|
|
396
|
+
|
|
404
397
|
//
|
|
405
398
|
// Item
|
|
406
399
|
//
|
|
@@ -412,6 +405,8 @@ type SearchListItemProps = ThemedClassName<{
|
|
|
412
405
|
label: string;
|
|
413
406
|
/** Icon to display (string identifier for Icon component). */
|
|
414
407
|
icon?: string;
|
|
408
|
+
/** Additional class names for the icon element. */
|
|
409
|
+
iconClassNames?: string;
|
|
415
410
|
/** Whether to show a check icon. */
|
|
416
411
|
checked?: boolean;
|
|
417
412
|
/** Suffix text to display after the label. */
|
|
@@ -423,7 +418,7 @@ type SearchListItemProps = ThemedClassName<{
|
|
|
423
418
|
}>;
|
|
424
419
|
|
|
425
420
|
const SearchListItem = forwardRef<HTMLDivElement, SearchListItemProps>(
|
|
426
|
-
({ classNames, value, label, icon, checked, suffix, onSelect, disabled }, forwardedRef) => {
|
|
421
|
+
({ classNames, value, label, icon, iconClassNames, checked, suffix, onSelect, disabled }, forwardedRef) => {
|
|
427
422
|
const { selectedValue, registerItem, unregisterItem } = useSearchListItemContext('SearchList.Item');
|
|
428
423
|
const internalRef = useRef<HTMLDivElement>(null);
|
|
429
424
|
|
|
@@ -470,15 +465,15 @@ const SearchListItem = forwardRef<HTMLDivElement, SearchListItemProps>(
|
|
|
470
465
|
tabIndex={-1}
|
|
471
466
|
className={mx(
|
|
472
467
|
'flex gap-2 items-center',
|
|
473
|
-
'
|
|
468
|
+
'py-1 px-2 rounded-xs select-none cursor-pointer data-[selected=true]:bg-hover-overlay hover:bg-hover-overlay',
|
|
474
469
|
disabled && 'opacity-50 cursor-not-allowed hover:bg-transparent data-[selected=true]:bg-transparent',
|
|
475
470
|
classNames,
|
|
476
471
|
)}
|
|
477
472
|
onClick={handleClick}
|
|
478
473
|
>
|
|
479
|
-
{icon && <Icon icon={icon} size={5} />}
|
|
480
|
-
<span className='
|
|
481
|
-
{suffix && <span className=
|
|
474
|
+
{icon && <Icon icon={icon} size={5} classNames={iconClassNames} />}
|
|
475
|
+
<span className='w-0 grow truncate'>{label}</span>
|
|
476
|
+
{suffix && <span className='shrink-0 text-description'>{suffix}</span>}
|
|
482
477
|
{checked && <Icon icon='ph--check--regular' size={5} />}
|
|
483
478
|
</div>
|
|
484
479
|
);
|
|
@@ -495,7 +490,7 @@ type SearchListEmptyProps = ThemedClassName<PropsWithChildren<{}>>;
|
|
|
495
490
|
|
|
496
491
|
const SearchListEmpty = ({ classNames, children }: SearchListEmptyProps) => {
|
|
497
492
|
return (
|
|
498
|
-
<div role='status' className={mx('flex flex-col
|
|
493
|
+
<div role='status' className={mx('flex flex-col w-full px-2 py-1', classNames)}>
|
|
499
494
|
{children}
|
|
500
495
|
</div>
|
|
501
496
|
);
|
|
@@ -522,7 +517,7 @@ const SearchListGroup = forwardRef<HTMLDivElement, SearchListGroupProps>(
|
|
|
522
517
|
return (
|
|
523
518
|
<div ref={forwardedRef} role='group' className={mx('flex flex-col', classNames)}>
|
|
524
519
|
{heading && (
|
|
525
|
-
<div role='presentation' className='
|
|
520
|
+
<div role='presentation' className='px-2 py-1 text-xs font-medium text-description'>
|
|
526
521
|
{heading}
|
|
527
522
|
</div>
|
|
528
523
|
)}
|
|
@@ -540,8 +535,8 @@ SearchListGroup.displayName = 'SearchList.Group';
|
|
|
540
535
|
|
|
541
536
|
export const SearchList = {
|
|
542
537
|
Root: SearchListRoot,
|
|
543
|
-
Viewport: SearchListViewport,
|
|
544
538
|
Content: SearchListContent,
|
|
539
|
+
Viewport: SearchListViewport,
|
|
545
540
|
Input: SearchListInput,
|
|
546
541
|
Item: SearchListItem,
|
|
547
542
|
Empty: SearchListEmpty,
|
|
@@ -550,8 +545,8 @@ export const SearchList = {
|
|
|
550
545
|
|
|
551
546
|
export type {
|
|
552
547
|
SearchListRootProps,
|
|
553
|
-
SearchListViewportProps,
|
|
554
548
|
SearchListContentProps,
|
|
549
|
+
SearchListViewportProps,
|
|
555
550
|
SearchListInputProps,
|
|
556
551
|
SearchListItemProps,
|
|
557
552
|
SearchListEmptyProps,
|