@dxos/react-ui-searchlist 0.8.4-main.406dc2a → 0.8.4-main.40e3dcdf1b

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.
Files changed (50) hide show
  1. package/README.md +1 -1
  2. package/dist/lib/browser/index.mjs +667 -336
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/node-esm/index.mjs +667 -336
  6. package/dist/lib/node-esm/index.mjs.map +4 -4
  7. package/dist/lib/node-esm/meta.json +1 -1
  8. package/dist/types/src/components/Combobox/Combobox.d.ts +51 -10
  9. package/dist/types/src/components/Combobox/Combobox.d.ts.map +1 -1
  10. package/dist/types/src/components/Combobox/Combobox.stories.d.ts +1 -1
  11. package/dist/types/src/components/Combobox/Combobox.stories.d.ts.map +1 -1
  12. package/dist/types/src/components/Listbox/Listbox.d.ts.map +1 -1
  13. package/dist/types/src/components/Listbox/Listbox.stories.d.ts +1 -1
  14. package/dist/types/src/components/SearchList/SearchList.d.ts +86 -20
  15. package/dist/types/src/components/SearchList/SearchList.d.ts.map +1 -1
  16. package/dist/types/src/components/SearchList/SearchList.stories.d.ts +10 -7
  17. package/dist/types/src/components/SearchList/SearchList.stories.d.ts.map +1 -1
  18. package/dist/types/src/components/SearchList/context.d.ts +33 -0
  19. package/dist/types/src/components/SearchList/context.d.ts.map +1 -0
  20. package/dist/types/src/components/SearchList/hooks/index.d.ts +5 -0
  21. package/dist/types/src/components/SearchList/hooks/index.d.ts.map +1 -0
  22. package/dist/types/src/components/SearchList/hooks/useGlobalFilter.d.ts +34 -0
  23. package/dist/types/src/components/SearchList/hooks/useGlobalFilter.d.ts.map +1 -0
  24. package/dist/types/src/components/SearchList/hooks/useSearchListInput.d.ts +12 -0
  25. package/dist/types/src/components/SearchList/hooks/useSearchListInput.d.ts.map +1 -0
  26. package/dist/types/src/components/SearchList/hooks/useSearchListItem.d.ts +10 -0
  27. package/dist/types/src/components/SearchList/hooks/useSearchListItem.d.ts.map +1 -0
  28. package/dist/types/src/components/SearchList/hooks/useSearchListResults.d.ts +36 -0
  29. package/dist/types/src/components/SearchList/hooks/useSearchListResults.d.ts.map +1 -0
  30. package/dist/types/src/components/SearchList/index.d.ts +1 -0
  31. package/dist/types/src/components/SearchList/index.d.ts.map +1 -1
  32. package/dist/types/src/translations.d.ts +2 -2
  33. package/dist/types/src/translations.d.ts.map +1 -1
  34. package/dist/types/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +21 -18
  36. package/src/components/Combobox/Combobox.stories.tsx +9 -4
  37. package/src/components/Combobox/Combobox.tsx +40 -32
  38. package/src/components/Listbox/Listbox.stories.tsx +1 -1
  39. package/src/components/Listbox/Listbox.tsx +9 -4
  40. package/src/components/SearchList/SearchList.stories.tsx +496 -28
  41. package/src/components/SearchList/SearchList.tsx +451 -60
  42. package/src/components/SearchList/context.ts +43 -0
  43. package/src/components/SearchList/hooks/index.ts +8 -0
  44. package/src/components/SearchList/hooks/useGlobalFilter.tsx +61 -0
  45. package/src/components/SearchList/hooks/useSearchListInput.ts +14 -0
  46. package/src/components/SearchList/hooks/useSearchListItem.ts +14 -0
  47. package/src/components/SearchList/hooks/useSearchListResults.ts +104 -0
  48. package/src/components/SearchList/index.ts +1 -0
  49. package/src/translations.ts +1 -1
  50. package/src/types/command-score.d.ts +16 -0
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-searchlist",
3
- "version": "0.8.4-main.406dc2a",
3
+ "version": "0.8.4-main.40e3dcdf1b",
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",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "DXOS.org",
9
- "sideEffects": true,
13
+ "sideEffects": false,
10
14
  "type": "module",
11
15
  "exports": {
12
16
  ".": {
@@ -25,29 +29,28 @@
25
29
  "src"
26
30
  ],
27
31
  "dependencies": {
28
- "@fluentui/react-tabster": "^9.24.2",
29
- "@preact-signals/safe-react": "^0.9.0",
32
+ "@fluentui/react-tabster": "9.26.11",
30
33
  "@radix-ui/react-compose-refs": "1.1.1",
31
34
  "@radix-ui/react-context": "1.1.1",
32
35
  "@radix-ui/react-use-controllable-state": "1.1.0",
33
- "cmdk": "^0.2.0"
36
+ "command-score": "0.1.2"
34
37
  },
35
38
  "devDependencies": {
36
- "@types/react": "~19.2.2",
37
- "@types/react-dom": "~19.2.1",
38
- "react": "~19.2.0",
39
- "react-dom": "~19.2.0",
40
- "vite": "7.1.9",
41
- "@dxos/random": "0.8.4-main.406dc2a",
42
- "@dxos/react-ui": "0.8.4-main.406dc2a",
43
- "@dxos/react-ui-theme": "0.8.4-main.406dc2a",
44
- "@dxos/storybook-utils": "0.8.4-main.406dc2a"
39
+ "@types/react": "~19.2.7",
40
+ "@types/react-dom": "~19.2.3",
41
+ "react": "~19.2.3",
42
+ "react-dom": "~19.2.3",
43
+ "vite": "^7.1.11",
44
+ "@dxos/random": "0.8.4-main.40e3dcdf1b",
45
+ "@dxos/storybook-utils": "0.8.4-main.40e3dcdf1b",
46
+ "@dxos/react-ui": "0.8.4-main.40e3dcdf1b",
47
+ "@dxos/ui-theme": "0.8.4-main.40e3dcdf1b"
45
48
  },
46
49
  "peerDependencies": {
47
- "react": "^19.0.0",
48
- "react-dom": "^19.0.0",
49
- "@dxos/react-ui": "0.8.4-main.406dc2a",
50
- "@dxos/react-ui-theme": "0.8.4-main.406dc2a"
50
+ "react": "~19.2.3",
51
+ "react-dom": "~19.2.3",
52
+ "@dxos/react-ui": "0.8.4-main.40e3dcdf1b",
53
+ "@dxos/ui-theme": "0.8.4-main.40e3dcdf1b"
51
54
  },
52
55
  "publishConfig": {
53
56
  "access": "public"
@@ -9,6 +9,7 @@ import { faker } from '@dxos/random';
9
9
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
10
10
 
11
11
  import { translations } from '../../translations';
12
+ import { useSearchListResults } from '../SearchList/hooks';
12
13
 
13
14
  import { Combobox } from './Combobox';
14
15
 
@@ -17,6 +18,10 @@ faker.seed(1234);
17
18
  const items = faker.helpers.uniqueArray(faker.commerce.productName, 16).sort();
18
19
 
19
20
  const DefaultStory = () => {
21
+ const { results, handleSearch } = useSearchListResults({
22
+ items,
23
+ });
24
+
20
25
  return (
21
26
  <Combobox.Root
22
27
  placeholder='Nothing selected'
@@ -25,11 +30,11 @@ const DefaultStory = () => {
25
30
  }}
26
31
  >
27
32
  <Combobox.Trigger />
28
- <Combobox.Content filter={(value, search) => (value.includes(search) ? 1 : 0)}>
33
+ <Combobox.Content onSearch={handleSearch}>
29
34
  <Combobox.Input placeholder='Search...' />
30
35
  <Combobox.List>
31
- {items.map((value) => (
32
- <Combobox.Item key={value}>{value}</Combobox.Item>
36
+ {results.map((value) => (
37
+ <Combobox.Item key={value} value={value} label={value} />
33
38
  ))}
34
39
  </Combobox.List>
35
40
  <Combobox.Arrow />
@@ -42,7 +47,7 @@ const meta = {
42
47
  title: 'ui/react-ui-searchlist/Combobox',
43
48
  component: Combobox.Root as any,
44
49
  render: DefaultStory,
45
- decorators: [withTheme, withLayout({ container: 'column', classNames: 'p-2' })],
50
+ decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'p-2' })],
46
51
  parameters: {
47
52
  translations,
48
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, staticPlaceholderText } from '@dxos/react-ui-theme';
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';
@@ -101,7 +101,7 @@ const ComboboxRoot = ({
101
101
  // ContentProps
102
102
  //
103
103
 
104
- type ComboboxContentProps = SearchListRootProps & PopoverContentProps;
104
+ type ComboboxContentProps = SearchListRootProps & PopoverContentProps & { label?: string };
105
105
 
106
106
  const ComboboxContent = forwardRef<HTMLDivElement, ComboboxContentProps>(
107
107
  (
@@ -125,7 +125,11 @@ const ComboboxContent = forwardRef<HTMLDivElement, ComboboxContentProps>(
125
125
  forceMount,
126
126
  children,
127
127
  classNames,
128
- ...props
128
+ onSearch,
129
+ value,
130
+ defaultValue,
131
+ debounceMs,
132
+ label,
129
133
  },
130
134
  forwardedRef,
131
135
  ) => {
@@ -153,14 +157,14 @@ const ComboboxContent = forwardRef<HTMLDivElement, ComboboxContentProps>(
153
157
  forceMount,
154
158
  }}
155
159
  classNames={[
156
- 'is-[--radix-popover-trigger-width] max-bs-[--radix-popover-content-available-height] grid grid-rows-[min-content_1fr]',
160
+ 'w-(--radix-popover-trigger-width) max-h-(--radix-popover-content-available-height) grid grid-rows-[min-content_1fr]',
157
161
  classNames,
158
162
  ]}
159
163
  id={modalId}
160
164
  ref={forwardedRef}
161
165
  >
162
- <SearchList.Root {...props} classNames='contents density-fine' role='none'>
163
- {children}
166
+ <SearchList.Root onSearch={onSearch} value={value} defaultValue={defaultValue} debounceMs={debounceMs}>
167
+ <SearchList.Content>{children}</SearchList.Content>
164
168
  </SearchList.Root>
165
169
  </Popover.Content>
166
170
  );
@@ -199,9 +203,7 @@ const ComboboxTrigger = forwardRef<HTMLButtonElement, ComboboxTriggerProps>(
199
203
  >
200
204
  {children ?? (
201
205
  <>
202
- <span
203
- className={mx('font-normal text-start flex-1 min-is-0 truncate mie-2', !value && staticPlaceholderText)}
204
- >
206
+ <span className={mx('font-normal text-start flex-1 min-w-0 truncate me-2', !value && 'text-subdued')}>
205
207
  {value || placeholder}
206
208
  </span>
207
209
  <Icon icon='ph--caret-down--bold' size={3} />
@@ -233,10 +235,7 @@ const ComboboxInput = forwardRef<HTMLInputElement, ComboboxInputProps>(({ classN
233
235
  return (
234
236
  <SearchList.Input
235
237
  {...props}
236
- classNames={[
237
- 'mli-cardSpacingChrome mbs-cardSpacingChrome mbe-0 is-[calc(100%-2*var(--dx-cardSpacingChrome))]',
238
- classNames,
239
- ]}
238
+ classNames={['m-form-chrome mb-0 w-[calc(100%-2*var(--spacing-form-chrome))]', classNames]}
240
239
  ref={forwardedRef}
241
240
  />
242
241
  );
@@ -246,40 +245,39 @@ const ComboboxInput = forwardRef<HTMLInputElement, ComboboxInputProps>(({ classN
246
245
  // List
247
246
  //
248
247
 
249
- type ComboboxListProps = SearchListContentProps;
248
+ type ComboboxListProps = SearchListViewportProps;
250
249
 
251
250
  const ComboboxList = forwardRef<HTMLDivElement, ComboboxListProps>(({ classNames, ...props }, forwardedRef) => {
252
- return (
253
- <SearchList.Content
254
- {...props}
255
- classNames={['min-bs-0 overflow-y-auto plb-cardSpacingChrome', classNames]}
256
- ref={forwardedRef}
257
- />
258
- );
251
+ return <SearchList.Viewport {...props} classNames={['py-form-chrome', classNames]} ref={forwardedRef} />;
259
252
  });
260
253
 
261
254
  //
262
255
  // Item
263
256
  //
264
257
 
265
- type ComboboxItemProps = SearchListItemProps;
258
+ type ComboboxItemProps = SearchListItemProps & {
259
+ /** Whether to close the popover when this item is selected. Defaults to true. */
260
+ closeOnSelect?: boolean;
261
+ };
266
262
 
267
263
  const ComboboxItem = forwardRef<HTMLDivElement, ComboboxItemProps>(
268
- ({ classNames, onSelect, ...props }, forwardedRef) => {
264
+ ({ classNames, onSelect, value, closeOnSelect = true, ...props }, forwardedRef) => {
269
265
  const { onValueChange, onOpenChange } = useComboboxContext(COMBOBOX_ITEM_NAME);
270
- const handleSelect = useCallback<NonNullable<SearchListItemProps['onSelect']>>(
271
- (nextValue) => {
272
- onSelect?.(nextValue);
273
- onValueChange?.(nextValue);
266
+ const handleSelect = useCallback<NonNullable<SearchListItemProps['onSelect']>>(() => {
267
+ onSelect?.();
268
+ if (value !== undefined) {
269
+ onValueChange?.(value);
270
+ }
271
+ if (closeOnSelect) {
274
272
  onOpenChange?.(false);
275
- },
276
- [onSelect, onValueChange, onOpenChange],
277
- );
273
+ }
274
+ }, [onSelect, onValueChange, onOpenChange, value, closeOnSelect]);
278
275
 
279
276
  return (
280
277
  <SearchList.Item
281
278
  {...props}
282
- classNames={['mli-cardSpacingChrome pli-cardSpacingChrome', classNames]}
279
+ value={value}
280
+ classNames={['mx-form-chrome px-form-chrome', classNames]}
283
281
  onSelect={handleSelect}
284
282
  ref={forwardedRef}
285
283
  />
@@ -310,8 +308,17 @@ const ComboboxEmpty = SearchList.Empty;
310
308
  // https://www.w3.org/WAI/ARIA/apg/patterns/combobox
311
309
  //
312
310
 
311
+ //
312
+ // Portal
313
+ //
314
+
315
+ type ComboboxPortalProps = React.ComponentPropsWithoutRef<typeof Popover.Portal>;
316
+
317
+ const ComboboxPortal = Popover.Portal;
318
+
313
319
  export const Combobox = {
314
320
  Root: ComboboxRoot,
321
+ Portal: ComboboxPortal,
315
322
  Content: ComboboxContent,
316
323
  Trigger: ComboboxTrigger,
317
324
  VirtualTrigger: ComboboxVirtualTrigger,
@@ -324,6 +331,7 @@ export const Combobox = {
324
331
 
325
332
  export type {
326
333
  ComboboxRootProps,
334
+ ComboboxPortalProps,
327
335
  ComboboxContentProps,
328
336
  ComboboxTriggerProps,
329
337
  ComboboxVirtualTriggerProps,
@@ -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({ container: 'column', classNames: 'p-2' })],
43
+ decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'p-2' })],
44
44
  parameters: {
45
45
  translations,
46
46
  },
@@ -9,9 +9,9 @@ import { useControllableState } from '@radix-ui/react-use-controllable-state';
9
9
  import React, { type ComponentPropsWithRef, forwardRef, useCallback, useEffect, useRef } from 'react';
10
10
 
11
11
  import { Icon, type IconProps, type ThemedClassName } from '@dxos/react-ui';
12
- import { mx } from '@dxos/react-ui-theme';
12
+ import { mx } from '@dxos/ui-theme';
13
13
 
14
- import { commandItem, searchListItem } from '../SearchList';
14
+ const commandItem = 'flex items-center overflow-hidden';
15
15
 
16
16
  const LISTBOX_NAME = 'Listbox';
17
17
  const LISTBOX_OPTION_NAME = 'ListboxOption';
@@ -100,7 +100,7 @@ const ListboxRoot = forwardRef<HTMLUListElement, ListboxRootProps>(
100
100
  <ul
101
101
  role='listbox'
102
102
  {...rootProps}
103
- className={mx('is-full p-cardSpacingChrome', classNames)}
103
+ className={mx('w-full p-form-chrome', classNames)}
104
104
  ref={rootRef}
105
105
  {...arrowGroup}
106
106
  >
@@ -135,7 +135,12 @@ const ListboxOption = forwardRef<HTMLLIElement, ListboxOptionProps>(
135
135
  {...rootProps}
136
136
  aria-selected={isSelected}
137
137
  tabIndex={0}
138
- className={mx('dx-focus-ring', commandItem, searchListItem, classNames)}
138
+ className={mx(
139
+ 'dx-focus-ring',
140
+ 'py-1 px-2 rounded-xs select-none cursor-pointer data-[selected=true]:bg-hover-overlay hover:bg-hover-overlay',
141
+ commandItem,
142
+ classNames,
143
+ )}
139
144
  onClick={handleSelect}
140
145
  onKeyDown={({ key }) => {
141
146
  if (['Enter', ' '].includes(key)) {