@dxos/react-ui-searchlist 0.8.4-main.b97322e → 0.8.4-main.bc674ce

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 (78) hide show
  1. package/dist/lib/browser/index.mjs +743 -292
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +743 -292
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/Combobox/Combobox.d.ts +84 -0
  8. package/dist/types/src/components/Combobox/Combobox.d.ts.map +1 -0
  9. package/dist/types/src/components/Combobox/Combobox.stories.d.ts +21 -0
  10. package/dist/types/src/components/Combobox/Combobox.stories.d.ts.map +1 -0
  11. package/dist/types/src/components/Combobox/index.d.ts +2 -0
  12. package/dist/types/src/components/Combobox/index.d.ts.map +1 -0
  13. package/dist/types/src/components/Listbox/Listbox.d.ts +31 -0
  14. package/dist/types/src/components/Listbox/Listbox.d.ts.map +1 -0
  15. package/dist/types/src/components/Listbox/Listbox.stories.d.ts +21 -0
  16. package/dist/types/src/components/Listbox/Listbox.stories.d.ts.map +1 -0
  17. package/dist/types/src/components/Listbox/index.d.ts +2 -0
  18. package/dist/types/src/components/Listbox/index.d.ts.map +1 -0
  19. package/dist/types/src/components/SearchList/SearchList.d.ts +87 -0
  20. package/dist/types/src/components/SearchList/SearchList.d.ts.map +1 -0
  21. package/dist/types/src/components/SearchList/SearchList.stories.d.ts +28 -0
  22. package/dist/types/src/components/SearchList/SearchList.stories.d.ts.map +1 -0
  23. package/dist/types/src/components/SearchList/context.d.ts +33 -0
  24. package/dist/types/src/components/SearchList/context.d.ts.map +1 -0
  25. package/dist/types/src/components/SearchList/hooks/index.d.ts +5 -0
  26. package/dist/types/src/components/SearchList/hooks/index.d.ts.map +1 -0
  27. package/dist/types/src/components/SearchList/hooks/useGlobalFilter.d.ts +34 -0
  28. package/dist/types/src/components/SearchList/hooks/useGlobalFilter.d.ts.map +1 -0
  29. package/dist/types/src/components/SearchList/hooks/useSearchListInput.d.ts +12 -0
  30. package/dist/types/src/components/SearchList/hooks/useSearchListInput.d.ts.map +1 -0
  31. package/dist/types/src/components/SearchList/hooks/useSearchListItem.d.ts +10 -0
  32. package/dist/types/src/components/SearchList/hooks/useSearchListItem.d.ts.map +1 -0
  33. package/dist/types/src/components/SearchList/hooks/useSearchListResults.d.ts +36 -0
  34. package/dist/types/src/components/SearchList/hooks/useSearchListResults.d.ts.map +1 -0
  35. package/dist/types/src/components/SearchList/index.d.ts +3 -0
  36. package/dist/types/src/components/SearchList/index.d.ts.map +1 -0
  37. package/dist/types/src/components/index.d.ts +2 -0
  38. package/dist/types/src/components/index.d.ts.map +1 -1
  39. package/dist/types/src/index.d.ts +0 -1
  40. package/dist/types/src/index.d.ts.map +1 -1
  41. package/dist/types/src/translations.d.ts +4 -2
  42. package/dist/types/src/translations.d.ts.map +1 -1
  43. package/dist/types/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +23 -17
  45. package/src/components/Combobox/Combobox.stories.tsx +62 -0
  46. package/src/components/Combobox/Combobox.tsx +356 -0
  47. package/src/components/Combobox/index.ts +5 -0
  48. package/src/components/Listbox/Listbox.stories.tsx +53 -0
  49. package/src/components/Listbox/Listbox.tsx +214 -0
  50. package/src/components/Listbox/index.ts +5 -0
  51. package/src/components/SearchList/SearchList.stories.tsx +534 -0
  52. package/src/components/SearchList/SearchList.tsx +559 -0
  53. package/src/components/SearchList/context.ts +43 -0
  54. package/src/components/SearchList/hooks/index.ts +8 -0
  55. package/src/components/SearchList/hooks/useGlobalFilter.tsx +61 -0
  56. package/src/components/SearchList/hooks/useSearchListInput.ts +14 -0
  57. package/src/components/SearchList/hooks/useSearchListItem.ts +14 -0
  58. package/src/components/SearchList/hooks/useSearchListResults.ts +104 -0
  59. package/src/components/SearchList/index.ts +6 -0
  60. package/src/components/index.ts +2 -0
  61. package/src/index.ts +0 -1
  62. package/src/translations.ts +4 -2
  63. package/src/types/command-score.d.ts +16 -0
  64. package/dist/types/src/components/SearchList.d.ts +0 -44
  65. package/dist/types/src/components/SearchList.d.ts.map +0 -1
  66. package/dist/types/src/components/SearchList.stories.d.ts +0 -15
  67. package/dist/types/src/components/SearchList.stories.d.ts.map +0 -1
  68. package/dist/types/src/composites/PopoverCombobox.d.ts +0 -32
  69. package/dist/types/src/composites/PopoverCombobox.d.ts.map +0 -1
  70. package/dist/types/src/composites/PopoverCombobox.stories.d.ts +0 -28
  71. package/dist/types/src/composites/PopoverCombobox.stories.d.ts.map +0 -1
  72. package/dist/types/src/composites/index.d.ts +0 -2
  73. package/dist/types/src/composites/index.d.ts.map +0 -1
  74. package/src/components/SearchList.stories.tsx +0 -47
  75. package/src/components/SearchList.tsx +0 -250
  76. package/src/composites/PopoverCombobox.stories.tsx +0 -44
  77. package/src/composites/PopoverCombobox.tsx +0 -208
  78. package/src/composites/index.ts +0 -5
@@ -0,0 +1,104 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import commandScore from 'command-score';
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
+
8
+ export type UseSearchListResultsOptions<T> = {
9
+ /** Items to filter. */
10
+ items: T[];
11
+ /** Custom filter function. Defaults to filtering by extracted string. */
12
+ filter?: (item: T, query: string) => boolean;
13
+ /** Enable fuzzy filtering using command-score algorithm. Defaults to true. */
14
+ fuzzy?: boolean;
15
+ /** Custom function to extract the searchable string from an item. Defaults to accessing 'label' property if it exists. */
16
+ extract?: (item: T) => string;
17
+ /** Minimum score threshold for fuzzy matches (0-1). Defaults to 0. */
18
+ minScore?: number;
19
+ };
20
+
21
+ /**
22
+ * Hook to manage search results with fuzzy filtering (enabled by default).
23
+ * Returns filtered results and a handleSearch function to pass to SearchList.Root.
24
+ *
25
+ * @example
26
+ * // Default fuzzy filtering using command-score (tries to extract from 'label' property)
27
+ * const { results, handleSearch } = useSearchListResults({ items });
28
+ *
29
+ * @example
30
+ * // Disable fuzzy for basic case-insensitive substring match
31
+ * const { results, handleSearch } = useSearchListResults({ items, fuzzy: false });
32
+ *
33
+ * @example
34
+ * // Custom extraction for fuzzy filtering
35
+ * const { results, handleSearch } = useSearchListResults({
36
+ * items,
37
+ * extract: (item) => `${item.name} ${item.description}`,
38
+ * });
39
+ */
40
+ export const useSearchListResults = <T = unknown>({
41
+ items,
42
+ filter,
43
+ fuzzy = true,
44
+ extract,
45
+ minScore = 0,
46
+ }: UseSearchListResultsOptions<T>) => {
47
+ const [query, setQuery] = useState<string>('');
48
+ const queryRef = useRef<string>('');
49
+
50
+ // Update results when items change.
51
+ useEffect(() => {
52
+ queryRef.current = '';
53
+ setQuery('');
54
+ }, [items]);
55
+
56
+ const defaultExtract = useCallback((item: T) => {
57
+ // If item is a string, return it directly
58
+ if (typeof item === 'string') {
59
+ return item;
60
+ }
61
+ // Otherwise, try to access 'label' property
62
+ return (item as any)?.label ?? '';
63
+ }, []);
64
+ const extractFn = extract ?? defaultExtract;
65
+
66
+ const defaultFilter = useCallback(
67
+ (item: T, query: string) => {
68
+ const searchable = extractFn(item);
69
+ return searchable.toLowerCase().includes(query.toLowerCase());
70
+ },
71
+ [extractFn],
72
+ );
73
+
74
+ const filterFn = filter ?? defaultFilter;
75
+
76
+ const handleSearch = useCallback((searchQuery: string) => {
77
+ queryRef.current = searchQuery;
78
+ setQuery(searchQuery);
79
+ }, []);
80
+
81
+ const results = useMemo(() => {
82
+ const currentQuery = queryRef.current;
83
+ if (!currentQuery) {
84
+ return items;
85
+ }
86
+
87
+ if (fuzzy) {
88
+ // Score and filter items using command-score.
89
+ const scored = items
90
+ .map((item) => ({
91
+ item,
92
+ score: commandScore(extractFn(item), currentQuery) as number,
93
+ }))
94
+ .filter(({ score }) => score > minScore)
95
+ .sort((a, b) => b.score - a.score);
96
+
97
+ return scored.map(({ item }) => item);
98
+ } else {
99
+ return items.filter((item) => filterFn(item, currentQuery));
100
+ }
101
+ }, [items, query, filterFn, fuzzy, extractFn, minScore]);
102
+
103
+ return { results, handleSearch };
104
+ };
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ export * from './SearchList';
6
+ export * from './hooks';
@@ -2,4 +2,6 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
+ export * from './Combobox';
6
+ export * from './Listbox';
5
7
  export * from './SearchList';
package/src/index.ts CHANGED
@@ -3,5 +3,4 @@
3
3
  //
4
4
 
5
5
  export * from './components';
6
- export * from './composites';
7
6
  export * from './translations';
@@ -4,12 +4,14 @@
4
4
 
5
5
  import { type Resource } from '@dxos/react-ui';
6
6
 
7
- export const translationKey = 'react-ui-searchlist';
7
+ export const translationKey = '@dxos/react-ui-searchlist';
8
8
 
9
9
  export const translations = [
10
10
  {
11
11
  'en-US': {
12
- [translationKey]: {},
12
+ [translationKey]: {
13
+ 'search.placeholder': 'Search...',
14
+ },
13
15
  },
14
16
  },
15
17
  ] as const satisfies Resource[];
@@ -0,0 +1,16 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ declare module 'command-score' {
6
+ /**
7
+ * Scores how well a string matches a query using a fuzzy matching algorithm.
8
+ * Used by cmdk for its built-in filtering.
9
+ *
10
+ * @param string - The string to score against.
11
+ * @param query - The search query.
12
+ * @returns A score between 0 and 1, where higher values indicate better matches.
13
+ */
14
+ function commandScore(string: string, query: string): number;
15
+ export default commandScore;
16
+ }
@@ -1,44 +0,0 @@
1
- import { CommandEmpty, CommandInput, CommandItem, CommandList, CommandRoot } from 'cmdk';
2
- import React, { type ComponentPropsWithRef, type PropsWithChildren } from 'react';
3
- import { type ButtonProps, type TextInputProps, type ThemedClassName } from '@dxos/react-ui';
4
- type SearchListVariant = 'list' | 'menu' | 'listbox';
5
- type SearchListRootProps = ThemedClassName<ComponentPropsWithRef<typeof CommandRoot>> & {
6
- variant?: SearchListVariant;
7
- };
8
- type ComboboxContextValue = {
9
- isCombobox: true;
10
- modalId: string;
11
- open: boolean;
12
- onOpenChange: (nextOpen: boolean) => void;
13
- value: string;
14
- onValueChange: (nextValue: string) => void;
15
- placeholder?: string;
16
- };
17
- type ComboboxRootProps = PropsWithChildren<Partial<ComboboxContextValue & {
18
- defaultOpen: boolean;
19
- defaultValue: string;
20
- placeholder: string;
21
- }>>;
22
- type CommandInputPrimitiveProps = ComponentPropsWithRef<typeof CommandInput>;
23
- type SearchListInputProps = Omit<TextInputProps, 'value' | 'defaultValue' | 'onChange'> & Pick<CommandInputPrimitiveProps, 'value' | 'onValueChange' | 'defaultValue'>;
24
- type SearchListContentProps = ThemedClassName<ComponentPropsWithRef<typeof CommandList>>;
25
- type SearchListEmptyProps = ThemedClassName<ComponentPropsWithRef<typeof CommandEmpty>>;
26
- type SearchListItemProps = ThemedClassName<ComponentPropsWithRef<typeof CommandItem>>;
27
- type ComboboxTriggerProps = ButtonProps;
28
- export declare const SearchList: {
29
- Root: React.ForwardRefExoticComponent<Omit<SearchListRootProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
30
- Input: React.ForwardRefExoticComponent<Omit<SearchListInputProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
31
- Content: React.ForwardRefExoticComponent<Omit<SearchListContentProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
32
- Empty: React.ForwardRefExoticComponent<Omit<SearchListEmptyProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
33
- Item: React.ForwardRefExoticComponent<Omit<SearchListItemProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
34
- };
35
- export declare const Combobox: {
36
- Root: {
37
- ({ modalId: propsModalId, open: propsOpen, defaultOpen, onOpenChange: propsOnOpenChange, value: propsValue, defaultValue, onValueChange: propsOnValueChange, placeholder, children, }: ComboboxRootProps): React.JSX.Element;
38
- displayName: string;
39
- };
40
- Trigger: React.ForwardRefExoticComponent<Omit<ButtonProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
41
- useComboboxContext: (consumerName: string) => Partial<ComboboxContextValue>;
42
- };
43
- export type { SearchListRootProps, SearchListInputProps, SearchListContentProps, SearchListEmptyProps, SearchListItemProps, ComboboxRootProps, ComboboxTriggerProps, };
44
- //# sourceMappingURL=SearchList.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SearchList.d.ts","sourceRoot":"","sources":["../../../../src/components/SearchList.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AACzF,OAAO,KAAK,EAAE,EAAE,KAAK,qBAAqB,EAAc,KAAK,iBAAiB,EAAe,MAAM,OAAO,CAAC;AAE3G,OAAO,EAEL,KAAK,WAAW,EAEhB,KAAK,cAAc,EACnB,KAAK,eAAe,EAKrB,MAAM,gBAAgB,CAAC;AAGxB,KAAK,iBAAiB,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAErD,KAAK,mBAAmB,GAAG,eAAe,CAAC,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,GAAG;IACtF,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,UAAU,EAAE,IAAI,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AASF,KAAK,iBAAiB,GAAG,iBAAiB,CACxC,OAAO,CAAC,oBAAoB,GAAG;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CACpG,CAAC;AAcF,KAAK,0BAA0B,GAAG,qBAAqB,CAAC,OAAO,YAAY,CAAC,CAAC;AAG7E,KAAK,oBAAoB,GAAG,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,cAAc,GAAG,UAAU,CAAC,GACrF,IAAI,CAAC,0BAA0B,EAAE,OAAO,GAAG,eAAe,GAAG,cAAc,CAAC,CAAC;AAgC/E,KAAK,sBAAsB,GAAG,eAAe,CAAC,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC;AAYzF,KAAK,oBAAoB,GAAG,eAAe,CAAC,qBAAqB,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC;AAYxF,KAAK,mBAAmB,GAAG,eAAe,CAAC,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC;AAmEtF,KAAK,oBAAoB,GAAG,WAAW,CAAC;AAuCxC,eAAO,MAAM,UAAU;;;;;;CAMtB,CAAC;AAEF,eAAO,MAAM,QAAQ;;+LA5ElB,iBAAiB;;;;;CAgFnB,CAAC;AAEF,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,GACrB,CAAC"}
@@ -1,15 +0,0 @@
1
- import '@dxos-theme';
2
- import React from 'react';
3
- type StoryItems = Record<string, string>;
4
- declare const _default: {
5
- title: string;
6
- component: React.FC<{
7
- items: StoryItems;
8
- }>;
9
- decorators: import("@storybook/react").Decorator[];
10
- };
11
- export default _default;
12
- export declare const Default: {
13
- args: {};
14
- };
15
- //# sourceMappingURL=SearchList.stories.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SearchList.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/SearchList.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,KAAkB,MAAM,OAAO,CAAC;AAOvC,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;;;;eAUN,UAAU;;;;AAe7C,wBAIE;AAEF,eAAO,MAAM,OAAO;;CAEnB,CAAC"}
@@ -1,32 +0,0 @@
1
- import React from 'react';
2
- import { type PopoverArrowProps, type PopoverContentProps, type PopoverViewportProps, type PopoverVirtualTriggerProps } from '@dxos/react-ui';
3
- import { type ComboboxRootProps, type ComboboxTriggerProps, type SearchListContentProps, type SearchListEmptyProps, type SearchListInputProps, type SearchListItemProps, type SearchListRootProps } from '../components';
4
- type PopoverComboboxRootProps = ComboboxRootProps & {
5
- modal?: boolean;
6
- };
7
- type PopoverComboboxContentProps = SearchListRootProps & PopoverContentProps;
8
- type PopoverComboboxTriggerProps = ComboboxTriggerProps;
9
- type PopoverComboboxVirtualTriggerProps = PopoverVirtualTriggerProps;
10
- type PopoverComboboxInputProps = SearchListInputProps;
11
- type PopoverComboboxListProps = SearchListContentProps & Pick<PopoverViewportProps, 'constrainBlock' | 'constrainInline'>;
12
- type PopoverComboboxItemProps = SearchListItemProps;
13
- type PopoverComboboxArrowProps = PopoverArrowProps;
14
- type PopoverComboboxEmptyProps = SearchListEmptyProps;
15
- export declare const PopoverCombobox: {
16
- Root: ({ modal, children, open: propsOpen, onOpenChange: propsOnOpenChange, defaultOpen, ...props }: PopoverComboboxRootProps) => React.JSX.Element;
17
- Content: React.ForwardRefExoticComponent<Omit<PopoverComboboxContentProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
18
- Trigger: React.ForwardRefExoticComponent<Omit<import("@dxos/react-ui").ButtonProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
19
- VirtualTrigger: {
20
- (props: PopoverVirtualTriggerProps & {
21
- __scopePopover?: import("@radix-ui/react-context").Scope;
22
- }): React.JSX.Element;
23
- displayName: string;
24
- };
25
- Input: React.ForwardRefExoticComponent<Omit<SearchListInputProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
26
- List: React.ForwardRefExoticComponent<Omit<PopoverComboboxListProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
27
- Item: React.ForwardRefExoticComponent<Omit<SearchListItemProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
28
- Arrow: React.ForwardRefExoticComponent<PopoverArrowProps & React.RefAttributes<SVGSVGElement>>;
29
- Empty: React.ForwardRefExoticComponent<Omit<SearchListEmptyProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
30
- };
31
- export type { PopoverComboboxRootProps, PopoverComboboxContentProps, PopoverComboboxTriggerProps, PopoverComboboxVirtualTriggerProps, PopoverComboboxInputProps, PopoverComboboxListProps, PopoverComboboxItemProps, PopoverComboboxArrowProps, PopoverComboboxEmptyProps, };
32
- //# sourceMappingURL=PopoverCombobox.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PopoverCombobox.d.ts","sourceRoot":"","sources":["../../../../src/composites/PopoverCombobox.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAE1C,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAChC,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAEzB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACzB,MAAM,eAAe,CAAC;AAEvB,KAAK,wBAAwB,GAAG,iBAAiB,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAwBxE,KAAK,2BAA2B,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AAoE7E,KAAK,2BAA2B,GAAG,oBAAoB,CAAC;AAUxD,KAAK,kCAAkC,GAAG,0BAA0B,CAAC;AAIrE,KAAK,yBAAyB,GAAG,oBAAoB,CAAC;AAiBtD,KAAK,wBAAwB,GAAG,sBAAsB,GACpD,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,GAAG,iBAAiB,CAAC,CAAC;AAYnE,KAAK,wBAAwB,GAAG,mBAAmB,CAAC;AAcpD,KAAK,yBAAyB,GAAG,iBAAiB,CAAC;AAInD,KAAK,yBAAyB,GAAG,oBAAoB,CAAC;AAItD,eAAO,MAAM,eAAe;yGArJzB,wBAAwB;;;;;;;;;;;;;;CA+J1B,CAAC;AAEF,YAAY,EACV,wBAAwB,EACxB,2BAA2B,EAC3B,2BAA2B,EAC3B,kCAAkC,EAClC,yBAAyB,EACzB,wBAAwB,EACxB,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,GAC1B,CAAC"}
@@ -1,28 +0,0 @@
1
- import '@dxos-theme';
2
- import React from 'react';
3
- declare const _default: {
4
- title: string;
5
- component: {
6
- Root: ({ modal, children, open: propsOpen, onOpenChange: propsOnOpenChange, defaultOpen, ...props }: import("./PopoverCombobox").PopoverComboboxRootProps) => React.JSX.Element;
7
- Content: React.ForwardRefExoticComponent<Omit<import("./PopoverCombobox").PopoverComboboxContentProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
8
- Trigger: React.ForwardRefExoticComponent<Omit<import("@dxos/react-ui").ButtonProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
9
- VirtualTrigger: {
10
- (props: import("@dxos/react-ui").PopoverVirtualTriggerProps & {
11
- __scopePopover?: import("@radix-ui/react-context").Scope;
12
- }): React.JSX.Element;
13
- displayName: string;
14
- };
15
- Input: React.ForwardRefExoticComponent<Omit<import("..").SearchListInputProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
16
- List: React.ForwardRefExoticComponent<Omit<import("./PopoverCombobox").PopoverComboboxListProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
17
- Item: React.ForwardRefExoticComponent<Omit<import("..").SearchListItemProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
18
- Arrow: React.ForwardRefExoticComponent<import("@dxos/react-ui").PopoverArrowProps & React.RefAttributes<SVGSVGElement>>;
19
- Empty: React.ForwardRefExoticComponent<Omit<import("..").SearchListEmptyProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
20
- };
21
- render: () => React.JSX.Element;
22
- decorators: import("@storybook/react").Decorator[];
23
- };
24
- export default _default;
25
- export declare const Default: {
26
- args: {};
27
- };
28
- //# sourceMappingURL=PopoverCombobox.stories.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PopoverCombobox.stories.d.ts","sourceRoot":"","sources":["../../../../src/composites/PopoverCombobox.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,KAAK,MAAM,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;AA4B1B,wBAKE;AAEF,eAAO,MAAM,OAAO;;CAEnB,CAAC"}
@@ -1,2 +0,0 @@
1
- export * from './PopoverCombobox';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/composites/index.ts"],"names":[],"mappings":"AAIA,cAAc,mBAAmB,CAAC"}
@@ -1,47 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import '@dxos-theme';
6
-
7
- import React, { type FC } from 'react';
8
-
9
- import { faker } from '@dxos/random';
10
- import { withTheme } from '@dxos/storybook-utils';
11
-
12
- import { SearchList } from './SearchList';
13
-
14
- type StoryItems = Record<string, string>;
15
-
16
- const defaultItems: StoryItems = faker.helpers
17
- .uniqueArray(faker.commerce.productName, 16)
18
- .sort()
19
- .reduce((acc: StoryItems, label) => {
20
- acc[faker.string.uuid()] = label;
21
- return acc;
22
- }, {});
23
-
24
- const SearchListStory: FC<{ items: StoryItems }> = ({ items = defaultItems }) => {
25
- return (
26
- <SearchList.Root filter={(value, search) => (items[value].includes(search) ? 1 : 0)}>
27
- <SearchList.Input placeholder='Search...' />
28
- <SearchList.Content>
29
- {Object.entries(items).map(([value, label]) => (
30
- <SearchList.Item key={value} value={value} onSelect={(value) => console.log('[item select]', value)}>
31
- {label}
32
- </SearchList.Item>
33
- ))}
34
- </SearchList.Content>
35
- </SearchList.Root>
36
- );
37
- };
38
-
39
- export default {
40
- title: 'ui/react-ui-searchlist/SearchList',
41
- component: SearchListStory,
42
- decorators: [withTheme],
43
- };
44
-
45
- export const Default = {
46
- args: {},
47
- };
@@ -1,250 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { createContext } from '@radix-ui/react-context';
6
- import { useControllableState } from '@radix-ui/react-use-controllable-state';
7
- import { CommandEmpty, CommandInput, CommandItem, CommandList, CommandRoot } from 'cmdk';
8
- import React, { type ComponentPropsWithRef, forwardRef, type PropsWithChildren, useCallback } from 'react';
9
-
10
- import {
11
- Button,
12
- type ButtonProps,
13
- Icon,
14
- type TextInputProps,
15
- type ThemedClassName,
16
- useDensityContext,
17
- useElevationContext,
18
- useId,
19
- useThemeContext,
20
- } from '@dxos/react-ui';
21
- import { mx, staticPlaceholderText } from '@dxos/react-ui-theme';
22
-
23
- type SearchListVariant = 'list' | 'menu' | 'listbox';
24
-
25
- type SearchListRootProps = ThemedClassName<ComponentPropsWithRef<typeof CommandRoot>> & {
26
- variant?: SearchListVariant;
27
- };
28
-
29
- type ComboboxContextValue = {
30
- isCombobox: true;
31
- modalId: string;
32
- open: boolean;
33
- onOpenChange: (nextOpen: boolean) => void;
34
- value: string;
35
- onValueChange: (nextValue: string) => void;
36
- placeholder?: string;
37
- };
38
-
39
- const COMBOBOX_NAME = 'Combobox';
40
- const COMBOBOX_TRIGGER_NAME = 'ComboboxTrigger';
41
- const SEARCHLIST_NAME = 'SearchList';
42
- const SEARCHLIST_ITEM_NAME = 'SearchListItem';
43
-
44
- const [ComboboxProvider, useComboboxContext] = createContext<Partial<ComboboxContextValue>>(COMBOBOX_NAME, {});
45
-
46
- type ComboboxRootProps = PropsWithChildren<
47
- Partial<ComboboxContextValue & { defaultOpen: boolean; defaultValue: string; placeholder: string }>
48
- >;
49
-
50
- const SearchListRoot = forwardRef<HTMLDivElement, SearchListRootProps>(
51
- ({ children, classNames, ...props }, forwardedRef) => {
52
- return (
53
- <CommandRoot {...props} className={mx('', classNames)} ref={forwardedRef}>
54
- {children}
55
- </CommandRoot>
56
- );
57
- },
58
- );
59
-
60
- SearchListRoot.displayName = SEARCHLIST_NAME;
61
-
62
- type CommandInputPrimitiveProps = ComponentPropsWithRef<typeof CommandInput>;
63
-
64
- // TODO: Harmonize with other inputs’ `onChange` prop.
65
- type SearchListInputProps = Omit<TextInputProps, 'value' | 'defaultValue' | 'onChange'> &
66
- Pick<CommandInputPrimitiveProps, 'value' | 'onValueChange' | 'defaultValue'>;
67
-
68
- const SearchListInput = forwardRef<HTMLInputElement, SearchListInputProps>(
69
- ({ children, classNames, density: propsDensity, elevation: propsElevation, variant, ...props }, forwardedRef) => {
70
- // CHORE(thure): Keep this in-sync with `TextInput`, or submit a PR for `cmdk` to support `asChild` so we don’t have to.
71
- const { hasIosKeyboard } = useThemeContext();
72
- const { tx } = useThemeContext();
73
- const density = useDensityContext(propsDensity);
74
- const elevation = useElevationContext(propsElevation);
75
-
76
- return (
77
- <CommandInput
78
- {...props}
79
- className={tx(
80
- 'input.input',
81
- 'input',
82
- {
83
- variant,
84
- disabled: props.disabled,
85
- density,
86
- elevation,
87
- },
88
- 'mbe-cardSpacingBlock',
89
- classNames,
90
- )}
91
- {...(props.autoFocus && !hasIosKeyboard && { autoFocus: true })}
92
- ref={forwardedRef}
93
- />
94
- );
95
- },
96
- );
97
-
98
- type SearchListContentProps = ThemedClassName<ComponentPropsWithRef<typeof CommandList>>;
99
-
100
- const SearchListContent = forwardRef<HTMLDivElement, SearchListContentProps>(
101
- ({ children, classNames, ...props }, forwardedRef) => {
102
- return (
103
- <CommandList {...props} className={mx(classNames)} ref={forwardedRef}>
104
- {children}
105
- </CommandList>
106
- );
107
- },
108
- );
109
-
110
- type SearchListEmptyProps = ThemedClassName<ComponentPropsWithRef<typeof CommandEmpty>>;
111
-
112
- const SearchListEmpty = forwardRef<HTMLDivElement, SearchListEmptyProps>(
113
- ({ children, classNames, ...props }, forwardedRef) => {
114
- return (
115
- <CommandEmpty {...props} className={mx(classNames)} ref={forwardedRef}>
116
- {children}
117
- </CommandEmpty>
118
- );
119
- },
120
- );
121
-
122
- type SearchListItemProps = ThemedClassName<ComponentPropsWithRef<typeof CommandItem>>;
123
-
124
- const SearchListItem = forwardRef<HTMLDivElement, SearchListItemProps>(
125
- ({ children, classNames, onSelect, ...props }, forwardedRef) => {
126
- const { onValueChange, onOpenChange } = useComboboxContext(SEARCHLIST_ITEM_NAME);
127
- const handleSelect = useCallback(
128
- (nextValue: string) => {
129
- onValueChange?.(nextValue);
130
- onOpenChange?.(false);
131
- onSelect?.(nextValue);
132
- },
133
- [onValueChange, onOpenChange, onSelect],
134
- );
135
- return (
136
- <CommandItem
137
- {...props}
138
- onSelect={handleSelect}
139
- className={mx('p-1 rounded select-none cursor-pointer data-[selected]:bg-hoverOverlay', classNames)}
140
- ref={forwardedRef}
141
- >
142
- {children}
143
- </CommandItem>
144
- );
145
- },
146
- );
147
-
148
- SearchListItem.displayName = SEARCHLIST_ITEM_NAME;
149
-
150
- const ComboboxRoot = ({
151
- modalId: propsModalId,
152
- open: propsOpen,
153
- defaultOpen,
154
- onOpenChange: propsOnOpenChange,
155
- value: propsValue,
156
- defaultValue,
157
- onValueChange: propsOnValueChange,
158
- placeholder,
159
- children,
160
- }: ComboboxRootProps) => {
161
- const modalId = useId(COMBOBOX_NAME, propsModalId);
162
- const [open = false, onOpenChange] = useControllableState({
163
- prop: propsOpen,
164
- onChange: propsOnOpenChange,
165
- defaultProp: defaultOpen,
166
- });
167
- const [value = '', onValueChange] = useControllableState({
168
- prop: propsValue,
169
- onChange: propsOnValueChange,
170
- defaultProp: defaultValue,
171
- });
172
- return (
173
- <ComboboxProvider
174
- isCombobox
175
- modalId={modalId}
176
- open={open}
177
- onOpenChange={onOpenChange}
178
- value={value}
179
- onValueChange={onValueChange}
180
- placeholder={placeholder}
181
- >
182
- {children}
183
- </ComboboxProvider>
184
- );
185
- };
186
-
187
- ComboboxRoot.displayName = COMBOBOX_NAME;
188
-
189
- type ComboboxTriggerProps = ButtonProps;
190
-
191
- const ComboboxTrigger = forwardRef<HTMLButtonElement, ComboboxTriggerProps>(
192
- ({ children, onClick, ...props }, forwardedRef) => {
193
- const { modalId, open, onOpenChange, placeholder, value } = useComboboxContext(COMBOBOX_TRIGGER_NAME);
194
- const handleClick = useCallback(
195
- (event: Parameters<Exclude<ButtonProps['onClick'], undefined>>[0]) => {
196
- onClick?.(event);
197
- onOpenChange?.(true);
198
- },
199
- [onClick, onOpenChange],
200
- );
201
- return (
202
- <Button
203
- {...props}
204
- role='combobox'
205
- aria-expanded={open}
206
- aria-controls={modalId}
207
- aria-haspopup='dialog'
208
- onClick={handleClick}
209
- ref={forwardedRef}
210
- >
211
- {children ?? (
212
- <>
213
- <span
214
- className={mx('font-normal text-start flex-1 min-is-0 truncate mie-2', !value && staticPlaceholderText)}
215
- >
216
- {value || placeholder}
217
- </span>
218
- <Icon icon='ph--caret-down--bold' size={3} />
219
- </>
220
- )}
221
- </Button>
222
- );
223
- },
224
- );
225
-
226
- ComboboxTrigger.displayName = COMBOBOX_TRIGGER_NAME;
227
-
228
- export const SearchList = {
229
- Root: SearchListRoot,
230
- Input: SearchListInput,
231
- Content: SearchListContent,
232
- Empty: SearchListEmpty,
233
- Item: SearchListItem,
234
- };
235
-
236
- export const Combobox = {
237
- Root: ComboboxRoot,
238
- Trigger: ComboboxTrigger,
239
- useComboboxContext,
240
- };
241
-
242
- export type {
243
- SearchListRootProps,
244
- SearchListInputProps,
245
- SearchListContentProps,
246
- SearchListEmptyProps,
247
- SearchListItemProps,
248
- ComboboxRootProps,
249
- ComboboxTriggerProps,
250
- };
@@ -1,44 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import '@dxos-theme';
6
-
7
- import React from 'react';
8
-
9
- import { faker } from '@dxos/random';
10
- import { withTheme } from '@dxos/storybook-utils';
11
-
12
- import { PopoverCombobox } from './PopoverCombobox';
13
-
14
- faker.seed(1234);
15
-
16
- const storybookItems = faker.helpers.uniqueArray(faker.commerce.productName, 16);
17
-
18
- const DefaultStory = () => {
19
- return (
20
- <PopoverCombobox.Root placeholder='Nothing selected'>
21
- <PopoverCombobox.Trigger />
22
- <PopoverCombobox.Content filter={(value, search) => (value.includes(search) ? 1 : 0)}>
23
- <PopoverCombobox.Input placeholder='Search...' />
24
- <PopoverCombobox.List>
25
- {storybookItems.map((value) => (
26
- <PopoverCombobox.Item key={value}>{value}</PopoverCombobox.Item>
27
- ))}
28
- </PopoverCombobox.List>
29
- <PopoverCombobox.Arrow />
30
- </PopoverCombobox.Content>
31
- </PopoverCombobox.Root>
32
- );
33
- };
34
-
35
- export default {
36
- title: 'ui/react-ui-searchlist/PopoverCombobox',
37
- component: PopoverCombobox,
38
- render: DefaultStory,
39
- decorators: [withTheme],
40
- };
41
-
42
- export const Default = {
43
- args: {},
44
- };