@dxos/react-ui-editor 0.8.3-main.7f5a14c → 0.8.3

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 (76) hide show
  1. package/dist/lib/browser/index.mjs +371 -375
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +502 -511
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +371 -375
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  11. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +1 -1
  12. package/dist/types/src/components/Popover/RefPopover.d.ts.map +1 -1
  13. package/dist/types/src/defaults.d.ts +0 -1
  14. package/dist/types/src/defaults.d.ts.map +1 -1
  15. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  16. package/dist/types/src/extensions/command/action.d.ts.map +1 -1
  17. package/dist/types/src/extensions/command/command-menu.d.ts +20 -0
  18. package/dist/types/src/extensions/command/command-menu.d.ts.map +1 -0
  19. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  20. package/dist/types/src/extensions/command/floating-menu.d.ts +7 -0
  21. package/dist/types/src/extensions/command/floating-menu.d.ts.map +1 -0
  22. package/dist/types/src/extensions/command/hint.d.ts +5 -2
  23. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  24. package/dist/types/src/extensions/command/index.d.ts +3 -1
  25. package/dist/types/src/extensions/command/index.d.ts.map +1 -1
  26. package/dist/types/src/extensions/command/placeholder.d.ts +10 -0
  27. package/dist/types/src/extensions/command/placeholder.d.ts.map +1 -0
  28. package/dist/types/src/extensions/command/state.d.ts +1 -1
  29. package/dist/types/src/extensions/command/state.d.ts.map +1 -1
  30. package/dist/types/src/extensions/command/useCommandMenu.d.ts +26 -0
  31. package/dist/types/src/extensions/command/useCommandMenu.d.ts.map +1 -0
  32. package/dist/types/src/extensions/index.d.ts +0 -1
  33. package/dist/types/src/extensions/index.d.ts.map +1 -1
  34. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  35. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
  36. package/dist/types/src/extensions/preview/preview.d.ts +12 -19
  37. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  38. package/dist/types/src/stories/CommandMenu.stories.d.ts +5 -4
  39. package/dist/types/src/stories/CommandMenu.stories.d.ts.map +1 -1
  40. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  41. package/dist/types/src/util/dom.d.ts +5 -0
  42. package/dist/types/src/util/dom.d.ts.map +1 -1
  43. package/dist/types/src/util/react.d.ts +2 -4
  44. package/dist/types/src/util/react.d.ts.map +1 -1
  45. package/package.json +31 -31
  46. package/src/components/EditorToolbar/EditorToolbar.tsx +5 -9
  47. package/src/components/Popover/RefDropdownMenu.tsx +5 -3
  48. package/src/components/Popover/RefPopover.tsx +5 -3
  49. package/src/defaults.ts +0 -6
  50. package/src/extensions/automerge/automerge.stories.tsx +5 -5
  51. package/src/extensions/command/action.ts +9 -2
  52. package/src/extensions/command/command-menu.ts +210 -0
  53. package/src/extensions/command/command.ts +8 -8
  54. package/src/extensions/command/floating-menu.ts +133 -0
  55. package/src/extensions/command/hint.ts +29 -9
  56. package/src/extensions/command/index.ts +3 -1
  57. package/src/extensions/command/placeholder.ts +113 -0
  58. package/src/extensions/command/state.ts +1 -2
  59. package/src/extensions/command/useCommandMenu.ts +118 -0
  60. package/src/extensions/index.ts +0 -1
  61. package/src/extensions/markdown/bundle.ts +0 -2
  62. package/src/extensions/outliner/tree.test.ts +13 -10
  63. package/src/extensions/outliner/tree.ts +5 -3
  64. package/src/extensions/preview/preview.ts +11 -86
  65. package/src/stories/Command.stories.tsx +1 -1
  66. package/src/stories/CommandMenu.stories.tsx +35 -19
  67. package/src/stories/Preview.stories.tsx +134 -57
  68. package/src/stories/components/util.tsx +2 -2
  69. package/src/util/dom.ts +20 -0
  70. package/src/util/react.tsx +3 -20
  71. package/dist/types/src/extensions/command/menu.d.ts +0 -47
  72. package/dist/types/src/extensions/command/menu.d.ts.map +0 -1
  73. package/dist/types/src/extensions/placeholder.d.ts +0 -4
  74. package/dist/types/src/extensions/placeholder.d.ts.map +0 -1
  75. package/src/extensions/command/menu.ts +0 -439
  76. package/src/extensions/placeholder.ts +0 -82
@@ -0,0 +1,118 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type EditorView } from '@codemirror/view';
6
+ import { type RefObject, useCallback, useMemo, useRef, useState } from 'react';
7
+
8
+ import { type DxRefTag, type DxRefTagActivate } from '@dxos/lit-ui';
9
+ import { type MaybePromise } from '@dxos/util';
10
+
11
+ import { commandMenu, commandRangeEffect } from './command-menu';
12
+ import { type PlaceholderOptions } from './placeholder';
13
+ import { getItem, getNextItem, getPreviousItem, type CommandMenuGroup, type CommandMenuItem } from '../../components';
14
+
15
+ export type UseCommandMenuOptions = {
16
+ viewRef: RefObject<EditorView | undefined>;
17
+ trigger: string | string[];
18
+ placeholder?: Partial<PlaceholderOptions>;
19
+ getMenu: (trigger: string, query?: string) => MaybePromise<CommandMenuGroup[]>;
20
+ };
21
+
22
+ export const useCommandMenu = ({ viewRef, trigger, placeholder, getMenu }: UseCommandMenuOptions) => {
23
+ const triggerRef = useRef<DxRefTag | null>(null);
24
+ const currentRef = useRef<CommandMenuItem | null>(null);
25
+ const groupsRef = useRef<CommandMenuGroup[]>([]);
26
+ const [currentItem, setCurrentItem] = useState<string>();
27
+ const [open, setOpen] = useState(false);
28
+ const [_, refresh] = useState({});
29
+
30
+ const handleOpenChange = useCallback(
31
+ async (open: boolean, trigger?: string) => {
32
+ if (open && trigger) {
33
+ groupsRef.current = await getMenu(trigger);
34
+ }
35
+ setOpen(open);
36
+ if (!open) {
37
+ triggerRef.current = null;
38
+ setCurrentItem(undefined);
39
+ viewRef.current?.dispatch({ effects: [commandRangeEffect.of(null)] });
40
+ }
41
+ },
42
+ [getMenu],
43
+ );
44
+
45
+ const handleActivate = useCallback(
46
+ async (event: DxRefTagActivate) => {
47
+ const item = getItem(groupsRef.current, currentItem);
48
+ if (item) {
49
+ currentRef.current = item;
50
+ }
51
+
52
+ triggerRef.current = event.trigger;
53
+ const triggerKey = event.trigger.getAttribute('data-trigger');
54
+ if (!open && triggerKey) {
55
+ await handleOpenChange(true, triggerKey);
56
+ }
57
+ },
58
+ [open, handleOpenChange],
59
+ );
60
+
61
+ const handleSelect = useCallback((item: CommandMenuItem) => {
62
+ const view = viewRef.current;
63
+ if (!view) {
64
+ return;
65
+ }
66
+
67
+ const selection = view.state.selection.main;
68
+ void item.onSelect?.(view, selection.head);
69
+ }, []);
70
+
71
+ const serializedTrigger = Array.isArray(trigger) ? trigger.join(',') : trigger;
72
+ const _commandMenu = useMemo(() => {
73
+ return commandMenu({
74
+ trigger,
75
+ placeholder,
76
+ onClose: () => handleOpenChange(false),
77
+ onArrowDown: () => {
78
+ setCurrentItem((currentItem) => {
79
+ const next = getNextItem(groupsRef.current, currentItem);
80
+ currentRef.current = next;
81
+ return next.id;
82
+ });
83
+ },
84
+ onArrowUp: () => {
85
+ setCurrentItem((currentItem) => {
86
+ const previous = getPreviousItem(groupsRef.current, currentItem);
87
+ currentRef.current = previous;
88
+ return previous.id;
89
+ });
90
+ },
91
+ onEnter: () => {
92
+ if (currentRef.current) {
93
+ handleSelect(currentRef.current);
94
+ }
95
+ },
96
+ onTextChange: async (trigger, text) => {
97
+ groupsRef.current = await getMenu(trigger, text);
98
+ const firstItem = groupsRef.current.filter((group) => group.items.length > 0)[0]?.items[0];
99
+ if (firstItem) {
100
+ setCurrentItem(firstItem.id);
101
+ currentRef.current = firstItem;
102
+ }
103
+ refresh({});
104
+ },
105
+ });
106
+ }, [handleOpenChange, getMenu, serializedTrigger, placeholder]);
107
+
108
+ return {
109
+ commandMenu: _commandMenu,
110
+ currentItem,
111
+ groupsRef,
112
+ ref: triggerRef,
113
+ open,
114
+ onActivate: handleActivate,
115
+ onOpenChange: setOpen,
116
+ onSelect: handleSelect,
117
+ };
118
+ };
@@ -21,7 +21,6 @@ export * from './markdown';
21
21
  export * from './mention';
22
22
  export * from './modes';
23
23
  export * from './outliner';
24
- export * from './placeholder';
25
24
  export * from './preview';
26
25
  export * from './selection';
27
26
  export * from './typewriter';
@@ -7,7 +7,6 @@ import { defaultKeymap, indentWithTab } from '@codemirror/commands';
7
7
  import { markdownLanguage, markdown } from '@codemirror/lang-markdown';
8
8
  import { syntaxHighlighting } from '@codemirror/language';
9
9
  import { languages } from '@codemirror/language-data';
10
- import { lintKeymap } from '@codemirror/lint';
11
10
  import { type Extension } from '@codemirror/state';
12
11
  import { keymap } from '@codemirror/view';
13
12
 
@@ -66,7 +65,6 @@ export const createMarkdownExtensions = (options: MarkdownBundleOptions = {}): E
66
65
  // https://codemirror.net/docs/ref/#commands.defaultKeymap
67
66
  ...defaultKeymap,
68
67
  ...completionKeymap,
69
- ...lintKeymap,
70
68
  ].filter(isNotFalsy),
71
69
  ),
72
70
  ];
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
6
6
  import { EditorState } from '@codemirror/state';
7
- import { describe, test } from 'vitest';
7
+ import { beforeEach, describe, test } from 'vitest';
8
8
 
9
9
  import { outlinerTree, treeFacet, listItemToString, type Item } from './tree';
10
10
  import { str } from '../../testing';
@@ -71,7 +71,11 @@ describe('tree (boundary conditions)', () => {
71
71
  });
72
72
 
73
73
  describe('tree (advanced)', () => {
74
- const state = EditorState.create({ doc: str(...lines), extensions });
74
+ let state: EditorState;
75
+
76
+ beforeEach(() => {
77
+ state = EditorState.create({ doc: str(...lines), extensions });
78
+ });
75
79
 
76
80
  test('traverse', ({ expect }) => {
77
81
  const tree = state.facet(treeFacet);
@@ -80,15 +84,15 @@ describe('tree (advanced)', () => {
80
84
  console.log(listItemToString(item, level));
81
85
  count++;
82
86
  });
83
- expect(count).toBe(9);
87
+ expect(count).to.eq(9);
84
88
  });
85
89
 
86
90
  test('continguous', ({ expect }) => {
87
91
  const tree = state.facet(treeFacet);
88
92
  const ranges: Range[] = [];
89
93
  tree.traverse((item) => {
90
- ranges.push(item.lineRange);
91
94
  console.log(listItemToString(item));
95
+ ranges.push(item.lineRange);
92
96
  });
93
97
 
94
98
  // Check no gaps between ranges.
@@ -97,18 +101,17 @@ describe('tree (advanced)', () => {
97
101
  for (let i = 0; i < ranges.length - 1; i++) {
98
102
  const current = ranges[i];
99
103
  const next = ranges[i + 1];
100
- expect(current.to + 1).toBe(next.from);
104
+ expect(current.to + 1).to.eq(next.from);
101
105
  }
102
106
  });
103
107
 
104
108
  test('find', ({ expect }) => {
105
109
  const tree = state.facet(treeFacet);
106
-
107
110
  expect(tree.find(0)).to.include({ type: 'task' });
108
111
  expect(tree.find(state.doc.length)).to.include({ type: 'task' });
109
112
 
110
113
  expect(tree.find(getPos(1))).to.include({ type: 'task' });
111
- expect(tree.find(getPos(1))).toBe(tree.find(getPos(1) + 4));
114
+ expect(tree.find(getPos(1))).to.eq(tree.find(getPos(1) + 4));
112
115
  expect(tree.find(getPos(5))).to.include({ type: 'bullet' });
113
116
  });
114
117
 
@@ -148,17 +151,17 @@ describe('tree (advanced)', () => {
148
151
  const tree = state.facet(treeFacet);
149
152
  {
150
153
  const item = tree.find(getPos(0))!;
151
- expect(tree.lastDescendant(item).index).toBe(item.index);
154
+ expect(tree.lastDescendant(item).index).to.eq(item.index);
152
155
  }
153
156
  {
154
157
  const item = tree.find(getPos(1))!;
155
158
  const last = tree.find(getPos(7))!;
156
- expect(tree.lastDescendant(item).index).toBe(last.index);
159
+ expect(tree.lastDescendant(item).index).to.eq(last.index);
157
160
  }
158
161
  {
159
162
  const item = tree.find(getPos(3))!;
160
163
  const last = tree.find(getPos(6))!;
161
- expect(tree.lastDescendant(item).index).toBe(last.index);
164
+ expect(tree.lastDescendant(item).index).to.eq(last.index);
162
165
  }
163
166
  });
164
167
  });
@@ -69,8 +69,8 @@ export class Tree implements Item {
69
69
  traverse<T = any>(cb: (item: Item, level: number) => T | void): T | undefined;
70
70
  traverse<T = any>(item: Item, cb: (item: Item, level: number) => T | void): T | undefined;
71
71
  traverse<T = any>(
72
- itemOrCb: Item | ((item: Item, level: number) => T | void),
73
- maybeCb?: (item: Item, level: number) => T | void,
72
+ itemOrCb: Item | ((item: Item, level: number) => T | undefined | void),
73
+ maybeCb?: (item: Item, level: number) => T | undefined | void,
74
74
  ): T | undefined {
75
75
  if (typeof itemOrCb === 'function') {
76
76
  return traverse<T>(this, itemOrCb);
@@ -83,7 +83,7 @@ export class Tree implements Item {
83
83
  * Return the closest item.
84
84
  */
85
85
  find(pos: number): Item | undefined {
86
- return this.traverse((item) => (item.lineRange.from <= pos && item.lineRange.to >= pos ? item : undefined));
86
+ return this.traverse<Item>((item) => (item.lineRange.from <= pos && item.lineRange.to >= pos ? item : undefined));
87
87
  }
88
88
 
89
89
  /**
@@ -148,6 +148,8 @@ export const traverse = <T = any>(root: Item, cb: (item: Item, level: number) =>
148
148
  return value;
149
149
  }
150
150
  }
151
+
152
+ return undefined;
151
153
  };
152
154
 
153
155
  return t(root, root.type === 'root' ? -1 : 0);
@@ -16,8 +16,6 @@ import {
16
16
  import { Decoration, type DecorationSet, EditorView, WidgetType } from '@codemirror/view';
17
17
  import { type SyntaxNode } from '@lezer/common';
18
18
 
19
- import { type RenderCallback } from '../../types';
20
-
21
19
  export type PreviewLinkRef = {
22
20
  suggest?: boolean;
23
21
  block?: boolean;
@@ -31,32 +29,13 @@ export type PreviewLinkTarget = {
31
29
  object?: any;
32
30
  };
33
31
 
34
- export type PreviewAction =
35
- | {
36
- type: 'insert';
37
- link: PreviewLinkRef;
38
- target: PreviewLinkTarget;
39
- }
40
- | {
41
- type: 'delete';
42
- link: PreviewLinkRef;
43
- };
44
-
32
+ // TODO(wittjosiah): Remove.
45
33
  // TODO(burdon): Handle error.
46
34
  export type PreviewLookup = (link: PreviewLinkRef) => Promise<PreviewLinkTarget | null | undefined>;
47
35
 
48
- export type PreviewActionHandler = (action: PreviewAction) => void;
49
-
50
- export type PreviewRenderProps = {
51
- readonly: boolean;
52
- link: PreviewLinkRef;
53
- onAction: PreviewActionHandler;
54
- onLookup?: PreviewLookup;
55
- };
56
-
57
36
  export type PreviewOptions = {
58
- renderBlock?: RenderCallback<PreviewRenderProps>;
59
- onLookup?: PreviewLookup;
37
+ addBlockContainer?: (link: PreviewLinkRef, el: HTMLElement) => void;
38
+ removeBlockContainer?: (link: PreviewLinkRef) => void;
60
39
  };
61
40
 
62
41
  /**
@@ -74,14 +53,6 @@ export const preview = (options: PreviewOptions = {}): Extension => {
74
53
  EditorView.atomicRanges.of((view) => view.state.field(field)),
75
54
  ],
76
55
  }),
77
-
78
- EditorView.theme({
79
- '.cm-preview-block': {
80
- '--dx-card-spacing-inline': 'var(--dx-trimMd)',
81
- '--dx-card-spacing-block': 'var(--dx-trimMd)',
82
- marginInline: 'calc(-1*var(--dx-trimMd))',
83
- },
84
- }),
85
56
  ];
86
57
  };
87
58
 
@@ -92,7 +63,7 @@ export const preview = (options: PreviewOptions = {}): Extension => {
92
63
  * ![Label][dxn:echo:123] Block reference
93
64
  * ![Label][?dxn:echo:123] Suggestion
94
65
  */
95
- const getLinkRef = (state: EditorState, node: SyntaxNode): PreviewLinkRef | undefined => {
66
+ export const getLinkRef = (state: EditorState, node: SyntaxNode): PreviewLinkRef | undefined => {
96
67
  const mark = node.getChild('LinkMark');
97
68
  const label = node.getChild('LinkLabel');
98
69
  if (mark && label) {
@@ -141,7 +112,7 @@ const buildDecorations = (state: EditorState, options: PreviewOptions) => {
141
112
  //
142
113
  case 'Image': {
143
114
  const link = getLinkRef(state, node.node);
144
- if (options.renderBlock && link) {
115
+ if (options.addBlockContainer && options.removeBlockContainer && link) {
145
116
  builder.add(
146
117
  node.from,
147
118
  node.to,
@@ -211,58 +182,12 @@ class PreviewBlockWidget extends WidgetType {
211
182
 
212
183
  override toDOM(view: EditorView): HTMLDivElement {
213
184
  const root = document.createElement('div');
214
- root.classList.add('cm-preview-block');
215
-
216
- // TODO(burdon): Inject handler.
217
- const handleAction: PreviewActionHandler = (action) => {
218
- const pos = view.posAtDOM(root);
219
- const node = syntaxTree(view.state).resolve(pos + 1).node.parent;
220
- if (!node) {
221
- return;
222
- }
223
-
224
- const link = getLinkRef(view.state, node);
225
- if (link?.ref !== action.link.ref) {
226
- return;
227
- }
228
-
229
- switch (action.type) {
230
- // TODO(burdon): Should we dispatch to the view or mutate the document? (i.e., handle externally?)
231
- // Insert ref text.
232
- case 'insert': {
233
- view.dispatch({
234
- changes: {
235
- from: node.from,
236
- to: node.to,
237
- insert: action.target.text,
238
- },
239
- });
240
- break;
241
- }
242
- // Remove ref.
243
- case 'delete': {
244
- view.dispatch({
245
- changes: {
246
- from: node.from,
247
- to: node.to,
248
- },
249
- });
250
- break;
251
- }
252
- }
253
- };
254
-
255
- this._options.renderBlock!(
256
- root,
257
- {
258
- readonly: view.state.readOnly,
259
- link: this._link,
260
- onAction: handleAction,
261
- onLookup: this._options.onLookup,
262
- },
263
- view,
264
- );
265
-
185
+ root.classList.add('cm-preview-block', 'density-coarse');
186
+ this._options.addBlockContainer?.(this._link, root);
266
187
  return root;
267
188
  }
189
+
190
+ override destroy() {
191
+ this._options.removeBlockContainer?.(this._link);
192
+ }
268
193
  }
@@ -75,7 +75,7 @@ const meta: Meta<typeof EditorStory> = {
75
75
  floatingMenu(),
76
76
  command({
77
77
  renderDialog: createRenderer(CommandDialog),
78
- onHint: () => 'Press / for commands.',
78
+ onHint: () => "Press '/' for commands",
79
79
  }),
80
80
  ]}
81
81
  />
@@ -16,24 +16,25 @@ import { withLayout, withTheme, type Meta } from '@dxos/storybook-utils';
16
16
 
17
17
  import { EditorStory, names } from './components';
18
18
  import {
19
- coreSlashCommands,
20
- filterItems,
21
- RefPopover,
22
- type CommandMenuGroup,
23
19
  CommandMenu,
20
+ type CommandMenuGroup,
24
21
  type CommandMenuItem,
22
+ RefPopover,
23
+ coreSlashCommands,
24
+ filterItems,
25
25
  insertAtCursor,
26
26
  insertAtLineStart,
27
27
  linkSlashCommands,
28
28
  } from '../components';
29
29
  import { useCommandMenu, type UseCommandMenuOptions } from '../extensions';
30
30
  import { str } from '../testing';
31
+ import { createElement } from '../util';
31
32
 
32
33
  const generator: ValueGenerator = faker as any;
33
34
 
34
- type Args = Omit<UseCommandMenuOptions, 'viewRef'> & { text: string };
35
+ type StoryProps = Omit<UseCommandMenuOptions, 'viewRef'> & { text: string };
35
36
 
36
- const Story = ({ text, ...options }: Args) => {
37
+ const DefaultStory = ({ text, ...options }: StoryProps) => {
37
38
  const viewRef = useRef<EditorView>();
38
39
  const { commandMenu, groupsRef, currentItem, onSelect, ...props } = useCommandMenu({ viewRef, ...options });
39
40
 
@@ -62,30 +63,45 @@ const groups: CommandMenuGroup[] = [
62
63
  },
63
64
  ];
64
65
 
65
- const meta: Meta<Args> = {
66
+ const meta: Meta<StoryProps> = {
66
67
  title: 'ui/react-ui-editor/CommandMenu',
67
68
  decorators: [withTheme, withLayout({ fullscreen: true })],
68
- render: (args) => <Story {...args} />,
69
- parameters: { layout: 'fullscreen' },
69
+ render: (args) => <DefaultStory {...args} />,
70
+ parameters: {
71
+ layout: 'fullscreen',
72
+ },
70
73
  };
71
74
 
72
75
  export default meta;
73
76
 
74
- export const Slash: StoryObj<Args> = {
77
+ type Story = StoryObj<StoryProps>;
78
+
79
+ // TODO(burdon): Not working.
80
+ export const Slash: Story = {
75
81
  args: {
76
- trigger: '/',
77
- getGroups: (query) =>
78
- filterItems(groups, (item) =>
79
- query ? (item.label as string).toLowerCase().includes(query.toLowerCase()) : true,
80
- ),
81
82
  text: str('# Slash', '', names.join(' '), ''),
83
+ trigger: '/',
84
+ placeholder: {
85
+ content: () => {
86
+ return createElement('div', undefined, [
87
+ createElement('span', { text: 'Press' }),
88
+ createElement('span', { className: 'border border-separator rounded-sm mx-1 px-1', text: '/' }),
89
+ createElement('span', { text: 'for commands' }),
90
+ ]);
91
+ },
92
+ },
93
+ getMenu: (text) => {
94
+ return filterItems(groups, (item) =>
95
+ text ? (item.label as string).toLowerCase().includes(text.toLowerCase()) : true,
96
+ );
97
+ },
82
98
  },
83
99
  };
84
100
 
85
- export const Link: StoryObj<Args> = {
101
+ export const Link: Story = {
86
102
  render: (args) => {
87
103
  const { space } = useClientProvider();
88
- const getGroups = useCallback(
104
+ const getMenu = useCallback(
89
105
  async (trigger: string, query?: string): Promise<CommandMenuGroup[]> => {
90
106
  if (trigger === '/') {
91
107
  return filterItems(groups, (item) =>
@@ -121,11 +137,11 @@ export const Link: StoryObj<Args> = {
121
137
  [space],
122
138
  );
123
139
 
124
- return <Story {...args} getGroups={getGroups} />;
140
+ return <DefaultStory {...args} getMenu={getMenu} />;
125
141
  },
126
142
  args: {
127
- trigger: ['/', '@'],
128
143
  text: str('# Link', '', names.join(' '), ''),
144
+ trigger: ['/', '@'],
129
145
  },
130
146
  decorators: [
131
147
  withClientProvider({