@dxos/react-ui-editor 0.8.4-main.e098934 → 0.8.4-main.ead640a

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 (205) hide show
  1. package/dist/lib/browser/{chunk-22UMM3QJ.mjs → chunk-HL3YF6WC.mjs} +2 -2
  2. package/dist/lib/browser/chunk-HL3YF6WC.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +3555 -3484
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs.map +2 -2
  7. package/dist/lib/browser/types/index.mjs +1 -1
  8. package/dist/lib/node-esm/{chunk-YXYQPV6R.mjs → chunk-YJZGD3LY.mjs} +2 -2
  9. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs.map +7 -0
  10. package/dist/lib/node-esm/index.mjs +3555 -3484
  11. package/dist/lib/node-esm/index.mjs.map +4 -4
  12. package/dist/lib/node-esm/meta.json +1 -1
  13. package/dist/lib/node-esm/testing/index.mjs.map +2 -2
  14. package/dist/lib/node-esm/types/index.mjs +1 -1
  15. package/dist/types/src/components/Editor/Editor.d.ts +24 -9
  16. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  17. package/dist/types/src/components/Editor/Editor.stories.d.ts +27 -0
  18. package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -0
  19. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  20. package/dist/types/src/components/EditorToolbar/util.d.ts +1 -1
  21. package/dist/types/src/components/index.d.ts +0 -1
  22. package/dist/types/src/components/index.d.ts.map +1 -1
  23. package/dist/types/src/extensions/{autocomplete.d.ts → autocomplete/autocomplete.d.ts} +1 -1
  24. package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +1 -0
  25. package/dist/types/src/extensions/autocomplete/index.d.ts +5 -0
  26. package/dist/types/src/extensions/autocomplete/index.d.ts.map +1 -0
  27. package/dist/types/src/extensions/autocomplete/match.d.ts +13 -0
  28. package/dist/types/src/extensions/autocomplete/match.d.ts.map +1 -0
  29. package/dist/types/src/extensions/autocomplete/placeholder.d.ts +20 -0
  30. package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -0
  31. package/dist/types/src/extensions/autocomplete/typeahead.d.ts +10 -0
  32. package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +1 -0
  33. package/dist/types/src/extensions/automerge/automerge.d.ts +1 -1
  34. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  35. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +1 -1
  36. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  37. package/dist/types/src/extensions/automerge/sync.d.ts +2 -2
  38. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  39. package/dist/types/src/extensions/autoscroll.d.ts +2 -2
  40. package/dist/types/src/extensions/autoscroll.d.ts.map +1 -1
  41. package/dist/types/src/extensions/factories.d.ts +7 -2
  42. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  43. package/dist/types/src/extensions/focus.d.ts.map +1 -1
  44. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  45. package/dist/types/src/extensions/index.d.ts +2 -1
  46. package/dist/types/src/extensions/index.d.ts.map +1 -1
  47. package/dist/types/src/extensions/json.d.ts +1 -1
  48. package/dist/types/src/extensions/json.d.ts.map +1 -1
  49. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  50. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  51. package/dist/types/src/extensions/modes.d.ts +1 -1
  52. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  53. package/dist/types/src/extensions/outliner/menu.d.ts +8 -0
  54. package/dist/types/src/extensions/outliner/menu.d.ts.map +1 -0
  55. package/dist/types/src/extensions/popover/PopoverMenuProvider.d.ts +36 -0
  56. package/dist/types/src/extensions/popover/PopoverMenuProvider.d.ts.map +1 -0
  57. package/dist/types/src/extensions/popover/index.d.ts +8 -0
  58. package/dist/types/src/extensions/popover/index.d.ts.map +1 -0
  59. package/dist/types/src/extensions/popover/menu-presets.d.ts +4 -0
  60. package/dist/types/src/extensions/popover/menu-presets.d.ts.map +1 -0
  61. package/dist/types/src/extensions/popover/menu.d.ts +24 -0
  62. package/dist/types/src/extensions/popover/menu.d.ts.map +1 -0
  63. package/dist/types/src/extensions/popover/modal.d.ts +7 -0
  64. package/dist/types/src/extensions/popover/modal.d.ts.map +1 -0
  65. package/dist/types/src/extensions/popover/popover.d.ts +47 -0
  66. package/dist/types/src/extensions/popover/popover.d.ts.map +1 -0
  67. package/dist/types/src/extensions/popover/usePopoverMenu.d.ts +34 -0
  68. package/dist/types/src/extensions/popover/usePopoverMenu.d.ts.map +1 -0
  69. package/dist/types/src/extensions/popover/util.d.ts +8 -0
  70. package/dist/types/src/extensions/popover/util.d.ts.map +1 -0
  71. package/dist/types/src/extensions/preview/preview.d.ts +0 -1
  72. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  73. package/dist/types/src/extensions/state.d.ts +2 -0
  74. package/dist/types/src/extensions/state.d.ts.map +1 -0
  75. package/dist/types/src/extensions/tags/streamer.d.ts.map +1 -1
  76. package/dist/types/src/extensions/tags/xml-tags.d.ts +1 -0
  77. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
  78. package/dist/types/src/hooks/useTextEditor.d.ts +4 -8
  79. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  80. package/dist/types/src/stories/{Command.stories.d.ts → CommandDialog.stories.d.ts} +2 -3
  81. package/dist/types/src/stories/CommandDialog.stories.d.ts.map +1 -0
  82. package/dist/types/src/stories/Comments.stories.d.ts +3 -4
  83. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  84. package/dist/types/src/stories/EditorToolbar.stories.d.ts +1 -2
  85. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  86. package/dist/types/src/stories/Experimental.stories.d.ts +3 -4
  87. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
  88. package/dist/types/src/stories/Markdown.stories.d.ts +3 -4
  89. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  90. package/dist/types/src/stories/Outliner.stories.d.ts +0 -1
  91. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  92. package/dist/types/src/stories/{CommandMenu.stories.d.ts → Popover.stories.d.ts} +6 -6
  93. package/dist/types/src/stories/Popover.stories.d.ts.map +1 -0
  94. package/dist/types/src/stories/Preview.stories.d.ts +3 -4
  95. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
  96. package/dist/types/src/stories/Tags.stories.d.ts +0 -1
  97. package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
  98. package/dist/types/src/stories/TextEditor.stories.d.ts +3 -5
  99. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
  100. package/dist/types/src/stories/components/EditorStory.d.ts +5 -5
  101. package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
  102. package/dist/types/src/styles/theme.d.ts.map +1 -1
  103. package/dist/types/src/testing/PreviewPopover.d.ts.map +1 -1
  104. package/dist/types/src/types/types.d.ts +1 -1
  105. package/dist/types/src/types/types.d.ts.map +1 -1
  106. package/dist/types/src/util/index.d.ts +0 -1
  107. package/dist/types/src/util/index.d.ts.map +1 -1
  108. package/dist/types/tsconfig.tsbuildinfo +1 -1
  109. package/package.json +54 -51
  110. package/src/components/Editor/Editor.stories.tsx +69 -0
  111. package/src/components/Editor/Editor.tsx +57 -14
  112. package/src/components/EditorToolbar/EditorToolbar.tsx +1 -0
  113. package/src/components/index.ts +0 -1
  114. package/src/extensions/{autocomplete.ts → autocomplete/autocomplete.ts} +2 -1
  115. package/src/extensions/autocomplete/index.ts +8 -0
  116. package/src/extensions/autocomplete/match.ts +46 -0
  117. package/src/extensions/{command → autocomplete}/placeholder.ts +21 -17
  118. package/src/extensions/{command → autocomplete}/typeahead.ts +6 -48
  119. package/src/extensions/automerge/automerge.stories.tsx +8 -8
  120. package/src/extensions/automerge/automerge.ts +28 -9
  121. package/src/extensions/automerge/sync.ts +7 -3
  122. package/src/extensions/autoscroll.ts +29 -29
  123. package/src/extensions/factories.ts +41 -12
  124. package/src/extensions/focus.ts +5 -4
  125. package/src/extensions/folding.tsx +4 -6
  126. package/src/extensions/hashtag.tsx +2 -2
  127. package/src/extensions/index.ts +2 -1
  128. package/src/extensions/json.ts +1 -1
  129. package/src/extensions/markdown/bundle.ts +16 -4
  130. package/src/extensions/markdown/decorate.ts +1 -0
  131. package/src/extensions/modes.ts +2 -2
  132. package/src/extensions/{command/floating-menu.ts → outliner/menu.ts} +9 -9
  133. package/src/extensions/outliner/outliner.ts +2 -2
  134. package/src/extensions/popover/PopoverMenuProvider.tsx +221 -0
  135. package/src/extensions/popover/index.ts +12 -0
  136. package/src/extensions/popover/menu-presets.ts +124 -0
  137. package/src/extensions/popover/menu.ts +67 -0
  138. package/src/extensions/popover/modal.ts +24 -0
  139. package/src/extensions/popover/popover.ts +291 -0
  140. package/src/extensions/popover/usePopoverMenu.ts +173 -0
  141. package/src/extensions/popover/util.ts +29 -0
  142. package/src/extensions/preview/index.ts +1 -1
  143. package/src/extensions/preview/preview.ts +0 -2
  144. package/src/extensions/selection.ts +2 -2
  145. package/src/extensions/state.ts +7 -0
  146. package/src/extensions/tags/streamer.ts +4 -5
  147. package/src/extensions/tags/xml-tags.ts +59 -1
  148. package/src/hooks/useTextEditor.ts +27 -27
  149. package/src/stories/{Command.stories.tsx → CommandDialog.stories.tsx} +10 -22
  150. package/src/stories/Comments.stories.tsx +5 -5
  151. package/src/stories/EditorToolbar.stories.tsx +6 -5
  152. package/src/stories/Experimental.stories.tsx +6 -6
  153. package/src/stories/Markdown.stories.tsx +5 -5
  154. package/src/stories/Outliner.stories.tsx +21 -14
  155. package/src/stories/Popover.stories.tsx +163 -0
  156. package/src/stories/Preview.stories.tsx +5 -5
  157. package/src/stories/Tags.stories.tsx +5 -5
  158. package/src/stories/TextEditor.stories.tsx +7 -32
  159. package/src/stories/components/EditorStory.tsx +7 -5
  160. package/src/styles/theme.ts +12 -10
  161. package/src/testing/PreviewPopover.tsx +2 -0
  162. package/src/types/types.ts +1 -1
  163. package/src/util/index.ts +0 -1
  164. package/dist/lib/browser/chunk-22UMM3QJ.mjs.map +0 -7
  165. package/dist/lib/node-esm/chunk-YXYQPV6R.mjs.map +0 -7
  166. package/dist/types/src/components/CommandMenu/CommandMenu.d.ts +0 -38
  167. package/dist/types/src/components/CommandMenu/CommandMenu.d.ts.map +0 -1
  168. package/dist/types/src/components/CommandMenu/index.d.ts +0 -2
  169. package/dist/types/src/components/CommandMenu/index.d.ts.map +0 -1
  170. package/dist/types/src/extensions/autocomplete.d.ts.map +0 -1
  171. package/dist/types/src/extensions/command/action.d.ts +0 -17
  172. package/dist/types/src/extensions/command/action.d.ts.map +0 -1
  173. package/dist/types/src/extensions/command/command-menu.d.ts +0 -20
  174. package/dist/types/src/extensions/command/command-menu.d.ts.map +0 -1
  175. package/dist/types/src/extensions/command/command.d.ts +0 -6
  176. package/dist/types/src/extensions/command/command.d.ts.map +0 -1
  177. package/dist/types/src/extensions/command/floating-menu.d.ts +0 -7
  178. package/dist/types/src/extensions/command/floating-menu.d.ts.map +0 -1
  179. package/dist/types/src/extensions/command/hint.d.ts +0 -19
  180. package/dist/types/src/extensions/command/hint.d.ts.map +0 -1
  181. package/dist/types/src/extensions/command/index.d.ts +0 -7
  182. package/dist/types/src/extensions/command/index.d.ts.map +0 -1
  183. package/dist/types/src/extensions/command/placeholder.d.ts +0 -10
  184. package/dist/types/src/extensions/command/placeholder.d.ts.map +0 -1
  185. package/dist/types/src/extensions/command/state.d.ts +0 -16
  186. package/dist/types/src/extensions/command/state.d.ts.map +0 -1
  187. package/dist/types/src/extensions/command/typeahead.d.ts +0 -22
  188. package/dist/types/src/extensions/command/typeahead.d.ts.map +0 -1
  189. package/dist/types/src/extensions/command/useCommandMenu.d.ts +0 -25
  190. package/dist/types/src/extensions/command/useCommandMenu.d.ts.map +0 -1
  191. package/dist/types/src/stories/Command.stories.d.ts.map +0 -1
  192. package/dist/types/src/stories/CommandMenu.stories.d.ts.map +0 -1
  193. package/dist/types/src/util/domino.d.ts +0 -18
  194. package/dist/types/src/util/domino.d.ts.map +0 -1
  195. package/src/components/CommandMenu/CommandMenu.tsx +0 -346
  196. package/src/components/CommandMenu/index.ts +0 -5
  197. package/src/extensions/command/action.ts +0 -55
  198. package/src/extensions/command/command-menu.ts +0 -211
  199. package/src/extensions/command/command.ts +0 -34
  200. package/src/extensions/command/hint.ts +0 -103
  201. package/src/extensions/command/index.ts +0 -10
  202. package/src/extensions/command/state.ts +0 -90
  203. package/src/extensions/command/useCommandMenu.ts +0 -115
  204. package/src/stories/CommandMenu.stories.tsx +0 -158
  205. package/src/util/domino.ts +0 -51
@@ -4,8 +4,8 @@
4
4
 
5
5
  import { EditorState, type EditorStateConfig, type Text } from '@codemirror/state';
6
6
  import { EditorView } from '@codemirror/view';
7
- import { type TabsterTypes, useFocusableGroup } from '@fluentui/react-tabster';
8
7
  import {
8
+ type ComponentPropsWithoutRef,
9
9
  type DependencyList,
10
10
  type KeyboardEventHandler,
11
11
  type RefObject,
@@ -17,9 +17,9 @@ import {
17
17
  } from 'react';
18
18
 
19
19
  import { log } from '@dxos/log';
20
- import { type MaybeProvider, getProviderValue, isNotFalsy } from '@dxos/util';
20
+ import { type MaybeProvider, getProviderValue, isTruthy } from '@dxos/util';
21
21
 
22
- import { type EditorSelection, createEditorStateTransaction, documentId, editorInputMode } from '../extensions';
22
+ import { type EditorSelection, createEditorStateTransaction, documentId, modalStateField } from '../extensions';
23
23
  import { debugDispatcher } from '../util';
24
24
 
25
25
  let instanceCount = 0;
@@ -34,13 +34,9 @@ export type CursorInfo = {
34
34
  };
35
35
 
36
36
  export type UseTextEditor = {
37
- // TODO(burdon): Rename.
38
- parentRef: RefObject<HTMLDivElement>;
39
- view?: EditorView;
40
- focusAttributes?: TabsterTypes.TabsterDOMAttribute & {
41
- tabIndex: 0;
42
- onKeyUp: KeyboardEventHandler<HTMLDivElement>;
43
- };
37
+ parentRef: RefObject<HTMLDivElement | null>;
38
+ view: EditorView | null;
39
+ focusAttributes?: ComponentPropsWithoutRef<'div'>;
44
40
  };
45
41
 
46
42
  export type UseTextEditorProps = Pick<EditorStateConfig, 'extensions'> & {
@@ -66,11 +62,11 @@ export const useTextEditor = (
66
62
 
67
63
  // NOTE: Increments by 2 in strict mode.
68
64
  const [instanceId] = useState(() => `text-editor-${++instanceCount}`);
69
- const [view, setView] = useState<EditorView>();
65
+ const [view, setView] = useState<EditorView | null>(null);
70
66
  const parentRef = useRef<HTMLDivElement>(null);
71
67
 
72
68
  useEffect(() => {
73
- let view: EditorView;
69
+ let view: EditorView | null = null;
74
70
  if (parentRef.current) {
75
71
  log('create', { id, instanceId, doc: initialValue?.length ?? 0 });
76
72
 
@@ -96,7 +92,7 @@ export const useTextEditor = (
96
92
  EditorView.exceptionSink.of((err) => {
97
93
  log.catch(err);
98
94
  }),
99
- ].filter(isNotFalsy),
95
+ ].filter(isTruthy),
100
96
  });
101
97
 
102
98
  // https://codemirror.net/docs/ref/#view.EditorViewConfig
@@ -143,23 +139,28 @@ export const useTextEditor = (
143
139
  }
144
140
  }, [autoFocus, view]);
145
141
 
146
- const focusableGroupAttrs = useFocusableGroup({
147
- tabBehavior: 'limited',
148
- ignoreDefaultKeydown: {
149
- Escape: view?.state.facet(editorInputMode).noTabster,
150
- },
151
- });
152
-
153
142
  // Focus editor on Enter (e.g., when tabbing to this component).
154
- const handleKeyUp = useCallback<KeyboardEventHandler<HTMLDivElement>>(
143
+ const handleKeyDown = useCallback<KeyboardEventHandler<HTMLDivElement>>(
155
144
  (event) => {
156
145
  const { key, target, currentTarget } = event;
157
- if (target === currentTarget) {
158
- switch (key) {
159
- case 'Enter': {
146
+ switch (key) {
147
+ case 'Escape': {
148
+ // Check if popover is open.
149
+ const modal = view?.state.field(modalStateField, false);
150
+ if (modal) {
151
+ return;
152
+ }
153
+
154
+ // Focus the closest focusable parent.
155
+ const element = view?.contentDOM.closest('[tabindex="0"]') as HTMLDivElement | null;
156
+ element?.focus();
157
+ break;
158
+ }
159
+ case 'Enter': {
160
+ if (target === currentTarget) {
160
161
  view?.focus();
161
- break;
162
162
  }
163
+ break;
163
164
  }
164
165
  }
165
166
  },
@@ -171,8 +172,7 @@ export const useTextEditor = (
171
172
  view,
172
173
  focusAttributes: {
173
174
  tabIndex: 0 as const,
174
- ...focusableGroupAttrs,
175
- onKeyUp: handleKeyUp,
175
+ onKeyDown: handleKeyDown,
176
176
  },
177
177
  };
178
178
  };
@@ -2,28 +2,25 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { type KeyboardEvent, useState } from 'react';
9
7
 
10
8
  import { Button, Icon, Input } from '@dxos/react-ui';
9
+ import { withTheme } from '@dxos/react-ui/testing';
11
10
  import { mx } from '@dxos/react-ui-theme';
12
- import { withLayout, withTheme } from '@dxos/storybook-utils';
13
11
 
14
12
  import { editorWidth } from '../defaults';
15
- import { type Action, command, floatingMenu } from '../extensions';
16
13
  import { str } from '../testing';
17
- import { createRenderer } from '../util';
18
14
 
19
15
  import { EditorStory } from './components';
20
16
 
21
- const CommandDialog = ({ onAction }: { onAction: (action?: Action) => void }) => {
17
+ // TODO(burdon): Reimplement with Popover.
18
+ const CommandDialog = ({ onAction }: { onAction: (action?: any) => void }) => {
22
19
  const [text, setText] = useState('');
23
20
 
24
21
  const handleInsert = () => {
25
22
  // TODO(burdon): Use queue ref.
26
- const link = `[${text}](dxn:queue:data:123)`;
23
+ const link = `![${text}](dxn:queue:data:123)`;
27
24
  onAction(text.length ? { type: 'insert', text: link } : undefined);
28
25
  };
29
26
 
@@ -66,21 +63,12 @@ const CommandDialog = ({ onAction }: { onAction: (action?: Action) => void }) =>
66
63
  };
67
64
 
68
65
  const meta = {
69
- title: 'ui/react-ui-editor/Command',
70
- decorators: [withTheme, withLayout({ fullscreen: true })],
71
- render: () => (
72
- <EditorStory
73
- text={str('# Command', '', '', '')}
74
- extensions={[
75
- floatingMenu(),
76
- command({
77
- renderDialog: createRenderer(CommandDialog),
78
- onHint: () => "Press '/' for commands",
79
- }),
80
- ]}
81
- />
82
- ),
83
- parameters: { layout: 'fullscreen' },
66
+ title: 'ui/react-ui-editor/CommandDialog',
67
+ render: () => <EditorStory text={str('# Command', '', '')} extensions={[]} />,
68
+ decorators: [withTheme],
69
+ parameters: {
70
+ layout: 'fullscreen',
71
+ },
84
72
  } satisfies Meta<typeof Button>;
85
73
 
86
74
  export default meta;
@@ -2,8 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { effect, useSignal } from '@preact/signals-react';
8
6
  import { type Meta, type StoryObj } from '@storybook/react-vite';
9
7
  import React, { type FC } from 'react';
@@ -11,7 +9,7 @@ import React, { type FC } from 'react';
11
9
  import { keySymbols, parseShortcut } from '@dxos/keyboard';
12
10
  import { PublicKey } from '@dxos/keys';
13
11
  import { log } from '@dxos/log';
14
- import { withLayout, withTheme } from '@dxos/storybook-utils';
12
+ import { withTheme } from '@dxos/react-ui/testing';
15
13
 
16
14
  import { annotations, comments, createExternalCommentSync } from '../extensions';
17
15
  import { str } from '../testing';
@@ -23,8 +21,10 @@ import { EditorStory, content, longText } from './components';
23
21
  const meta = {
24
22
  title: 'ui/react-ui-editor/Comments',
25
23
  component: EditorStory,
26
- decorators: [withTheme, withLayout({ fullscreen: true })],
27
- parameters: { layout: 'fullscreen' },
24
+ decorators: [withTheme],
25
+ parameters: {
26
+ layout: 'fullscreen',
27
+ },
28
28
  } satisfies Meta<typeof EditorStory>;
29
29
 
30
30
  export default meta;
@@ -2,15 +2,13 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useCallback, useState } from 'react';
9
7
 
10
8
  import { invariant } from '@dxos/invariant';
11
9
  import { useThemeContext } from '@dxos/react-ui';
10
+ import { withTheme } from '@dxos/react-ui/testing';
12
11
  import { attentionSurface, mx } from '@dxos/react-ui-theme';
13
- import { withLayout, withTheme } from '@dxos/storybook-utils';
14
12
 
15
13
  import { EditorToolbar, useEditorToolbarState } from '../components';
16
14
  import { editorWidth } from '../defaults';
@@ -78,8 +76,11 @@ const DefaultStory = ({ autoFocus, initialValue, placeholder }: StoryProps) => {
78
76
  const meta = {
79
77
  title: 'ui/react-ui-editor/EditorToolbar',
80
78
  render: DefaultStory,
81
- decorators: [withTheme, withLayout({ fullscreen: true })],
82
- parameters: { translations, layout: 'fullscreen' },
79
+ decorators: [withTheme],
80
+ parameters: {
81
+ layout: 'fullscreen',
82
+ translations,
83
+ },
83
84
  } satisfies Meta<typeof DefaultStory>;
84
85
 
85
86
  export default meta;
@@ -2,15 +2,13 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import defaultsDeep from 'lodash.defaultsdeep';
9
7
  import React from 'react';
10
8
 
11
9
  import { log } from '@dxos/log';
12
10
  import { faker } from '@dxos/random';
13
- import { withLayout, withTheme } from '@dxos/storybook-utils';
11
+ import { withTheme } from '@dxos/react-ui/testing';
14
12
 
15
13
  import { blast, defaultOptions, dropFile, typewriter } from '../extensions';
16
14
  import { str } from '../testing';
@@ -20,8 +18,10 @@ import { EditorStory, content } from './components';
20
18
  const meta = {
21
19
  title: 'ui/react-ui-editor/Experimental',
22
20
  component: EditorStory,
23
- decorators: [withTheme, withLayout({ fullscreen: true })],
24
- parameters: { layout: 'fullscreen' },
21
+ decorators: [withTheme],
22
+ parameters: {
23
+ layout: 'fullscreen',
24
+ },
25
25
  } satisfies Meta<typeof EditorStory>;
26
26
 
27
27
  export default meta;
@@ -32,7 +32,7 @@ type Story = StoryObj<typeof meta>;
32
32
  // Typewriter
33
33
  //
34
34
 
35
- const typewriterItems = localStorage.getItem('dxos.org/plugin/markdown/typewriter')?.split(',');
35
+ const typewriterItems = localStorage.getItem('dxos.org/testing/typewriter')?.split(',');
36
36
 
37
37
  export const Typewriter: Story = {
38
38
  render: () => (
@@ -2,13 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { markdown } from '@codemirror/lang-markdown';
8
6
  import { type Meta, type StoryObj } from '@storybook/react-vite';
9
7
  import React from 'react';
10
8
 
11
- import { withLayout, withTheme } from '@dxos/storybook-utils';
9
+ import { withTheme } from '@dxos/react-ui/testing';
12
10
 
13
11
  import { decorateMarkdown, image, linkTooltip, table } from '../extensions';
14
12
  import { str } from '../testing';
@@ -18,8 +16,10 @@ import { EditorStory, content, defaultExtensions, headings, renderLinkTooltip, t
18
16
  const meta = {
19
17
  title: 'ui/react-ui-editor/Markdown',
20
18
  component: EditorStory,
21
- decorators: [withTheme, withLayout({ fullscreen: true })],
22
- parameters: { layout: 'fullscreen' },
19
+ decorators: [withTheme],
20
+ parameters: {
21
+ layout: 'fullscreen',
22
+ },
23
23
  } satisfies Meta<typeof EditorStory>;
24
24
 
25
25
  export default meta;
@@ -2,17 +2,22 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type EditorView } from '@codemirror/view';
8
6
  import { type Meta, type StoryObj } from '@storybook/react-vite';
9
7
  import React, { useMemo, useRef } from 'react';
10
8
 
9
+ import { withTheme } from '@dxos/react-ui/testing';
11
10
  import { withAttention } from '@dxos/react-ui-attention/testing';
12
- import { withLayout, withTheme } from '@dxos/storybook-utils';
13
11
 
14
- import { type CommandMenuGroup, type CommandMenuItem, CommandMenuProvider } from '../components';
15
- import { deleteItem, hashtag, listItemToString, outliner, treeFacet } from '../extensions';
12
+ import {
13
+ type PopoverMenuGroup,
14
+ PopoverMenuProvider,
15
+ deleteItem,
16
+ hashtag,
17
+ listItemToString,
18
+ outliner,
19
+ treeFacet,
20
+ } from '../extensions';
16
21
  import { str } from '../testing';
17
22
 
18
23
  import { EditorStory } from './components';
@@ -24,7 +29,7 @@ type StoryProps = {
24
29
  const DefaultStory = ({ text }: StoryProps) => {
25
30
  const viewRef = useRef<EditorView>(null);
26
31
 
27
- const commandGroups: CommandMenuGroup[] = useMemo(
32
+ const commandGroups: PopoverMenuGroup[] = useMemo(
28
33
  () => [
29
34
  {
30
35
  id: 'outliner-actions',
@@ -43,11 +48,12 @@ const DefaultStory = ({ text }: StoryProps) => {
43
48
  );
44
49
 
45
50
  return (
46
- <CommandMenuProvider
51
+ <PopoverMenuProvider
52
+ view={viewRef.current}
47
53
  groups={commandGroups}
48
- onSelect={(item: CommandMenuItem) => {
49
- if (viewRef.current && item.onSelect) {
50
- return item.onSelect(viewRef.current, viewRef.current.state.selection.main.head);
54
+ onSelect={({ view, item }) => {
55
+ if (item.onSelect) {
56
+ return item.onSelect(view, view.state.selection.main.head);
51
57
  }
52
58
  }}
53
59
  >
@@ -55,7 +61,6 @@ const DefaultStory = ({ text }: StoryProps) => {
55
61
  ref={viewRef}
56
62
  text={text}
57
63
  extensions={[outliner(), hashtag()]}
58
- placeholder=''
59
64
  debug='raw+tree'
60
65
  debugCustom={(view) => {
61
66
  const tree = view.state.facet(treeFacet);
@@ -64,15 +69,17 @@ const DefaultStory = ({ text }: StoryProps) => {
64
69
  return <pre className='p-1 overflow-auto text-xs text-green-800 dark:text-green-200'>{lines.join('\n')}</pre>;
65
70
  }}
66
71
  />
67
- </CommandMenuProvider>
72
+ </PopoverMenuProvider>
68
73
  );
69
74
  };
70
75
 
71
76
  const meta = {
72
77
  title: 'ui/react-ui-editor/Outliner',
73
78
  render: DefaultStory,
74
- decorators: [withAttention, withTheme, withLayout({ fullscreen: true })],
75
- parameters: { layout: 'fullscreen' },
79
+ decorators: [withTheme, withAttention],
80
+ parameters: {
81
+ layout: 'fullscreen',
82
+ },
76
83
  } satisfies Meta<typeof DefaultStory>;
77
84
 
78
85
  export default meta;
@@ -0,0 +1,163 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { type EditorView } from '@codemirror/view';
6
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
+ import React, { useCallback, useState } from 'react';
8
+
9
+ import { Obj, Query } from '@dxos/echo';
10
+ import { faker } from '@dxos/random';
11
+ import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
12
+ import { Domino } from '@dxos/react-ui';
13
+ import { withTheme } from '@dxos/react-ui/testing';
14
+ import { Testing, type ValueGenerator, createObjectFactory } from '@dxos/schema/testing';
15
+
16
+ import {
17
+ type PopoverMenuGroup,
18
+ type PopoverMenuItem,
19
+ PopoverMenuProvider,
20
+ type UsePopoverMenuProps,
21
+ createMenuGroup,
22
+ filterMenuGroups,
23
+ formattingCommands,
24
+ insertAtCursor,
25
+ insertAtLineStart,
26
+ linkSlashCommands,
27
+ usePopoverMenu,
28
+ } from '../extensions';
29
+ import { str } from '../testing';
30
+
31
+ import { EditorStory } from './components';
32
+
33
+ const generator: ValueGenerator = faker as any;
34
+
35
+ const customCompletions: PopoverMenuGroup = createMenuGroup({
36
+ id: 'test',
37
+ items: ['Hello world!', 'Hello DXOS', 'Hello Composer', 'https://dxos.org'],
38
+ });
39
+
40
+ const placeholder = (trigger: string[]) =>
41
+ Domino.of('div')
42
+ .children(
43
+ Domino.of('span').text('Press'),
44
+ ...trigger.map((trigger) =>
45
+ Domino.of('span')
46
+ .text(trigger)
47
+ .classNames('border border-separator rounded-sm mx-1 pis-1 pie-1 pbs-[2px] pbe-[3px]'),
48
+ ),
49
+ Domino.of('span').text('for commands'),
50
+ )
51
+ .build();
52
+
53
+ type StoryProps = Omit<UsePopoverMenuProps, 'viewRef'> & { text: string };
54
+
55
+ const DefaultStory = ({ text, ...props }: StoryProps) => {
56
+ const [view, setView] = useState<EditorView | null>(null);
57
+ const { groupsRef, extension, ...menuProps } = usePopoverMenu(props);
58
+
59
+ return (
60
+ <PopoverMenuProvider view={view} groups={groupsRef.current} {...menuProps}>
61
+ <EditorStory ref={setView} text={text} extensions={extension} />
62
+ </PopoverMenuProvider>
63
+ );
64
+ };
65
+
66
+ const LinkStory = (args: StoryProps) => {
67
+ const { space } = useClientProvider();
68
+ const getMenu = useCallback<NonNullable<UsePopoverMenuProps['getMenu']>>(
69
+ async ({ text, trigger }): Promise<PopoverMenuGroup[]> => {
70
+ if (trigger === '/') {
71
+ return filterMenuGroups([linkSlashCommands], (item) =>
72
+ text ? (item.label as string).toLowerCase().includes(text.toLowerCase()) : true,
73
+ );
74
+ }
75
+
76
+ if (!space) {
77
+ return [];
78
+ }
79
+
80
+ const name = text?.startsWith('@') ? text.slice(1).toLowerCase() : (text?.toLowerCase() ?? '');
81
+ const result = await space?.db.query(Query.type(Testing.Contact)).run();
82
+ const items = result.objects
83
+ .filter((object) => object.name.toLowerCase().includes(name))
84
+ .map(
85
+ (object): PopoverMenuItem => ({
86
+ id: object.id,
87
+ label: object.name,
88
+ icon: 'ph--user--regular',
89
+ onSelect: (view, head) => {
90
+ const link = `[${object.name}](${Obj.getDXN(object)})`;
91
+ if (text?.startsWith('@')) {
92
+ insertAtLineStart(view, head, `!${link}\n`);
93
+ } else {
94
+ insertAtCursor(view, head, `${link} `);
95
+ }
96
+ },
97
+ }),
98
+ );
99
+
100
+ return [{ id: 'test', items }];
101
+ },
102
+ [space],
103
+ );
104
+
105
+ return <DefaultStory {...args} getMenu={getMenu} />;
106
+ };
107
+
108
+ const meta = {
109
+ title: 'ui/react-ui-editor/Popover',
110
+ render: DefaultStory,
111
+ decorators: [withTheme],
112
+ parameters: {
113
+ layout: 'fullscreen',
114
+ },
115
+ } satisfies Meta<typeof DefaultStory>;
116
+
117
+ export default meta;
118
+
119
+ type Story = StoryObj<typeof meta>;
120
+
121
+ export const Default: Story = {
122
+ args: {
123
+ text: str('# Autocomplete', '', ''),
124
+ triggerKey: 'Ctrl-Space',
125
+ filter: true,
126
+ getMenu: () => [customCompletions],
127
+ },
128
+ };
129
+
130
+ export const Formatting: Story = {
131
+ args: {
132
+ text: str('# Slash command', '', ''),
133
+ trigger: '/',
134
+ placeholder: {
135
+ content: () => placeholder(['/']),
136
+ },
137
+ getMenu: () => [formattingCommands],
138
+ },
139
+ };
140
+
141
+ export const Link: Story = {
142
+ render: LinkStory,
143
+ decorators: [
144
+ withClientProvider({
145
+ createSpace: true,
146
+ onInitialized: async (client) => {
147
+ client.addTypes([Testing.Contact]);
148
+ },
149
+ onCreateSpace: async ({ space }) => {
150
+ const createObjects = createObjectFactory(space.db, generator);
151
+ await createObjects([{ type: Testing.Contact, count: 10 }]);
152
+ await space.db.flush({ indexes: true });
153
+ },
154
+ }),
155
+ ],
156
+ args: {
157
+ text: str('# Links', '', ''),
158
+ trigger: ['/', '@'],
159
+ placeholder: {
160
+ content: () => placeholder(['/', '@']),
161
+ },
162
+ },
163
+ };
@@ -2,8 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { syntaxTree } from '@codemirror/language';
8
6
  import { type EditorView } from '@codemirror/view';
9
7
  import { type Meta, type StoryObj } from '@storybook/react-vite';
@@ -13,9 +11,9 @@ import { createPortal } from 'react-dom';
13
11
  import { invariant } from '@dxos/invariant';
14
12
  import { faker } from '@dxos/random';
15
13
  import { Popover } from '@dxos/react-ui';
14
+ import { withTheme } from '@dxos/react-ui/testing';
16
15
  import { Card } from '@dxos/react-ui-stack';
17
16
  import { hoverableControlItem, hoverableControlItemTransition, hoverableControls } from '@dxos/react-ui-theme';
18
- import { withLayout, withTheme } from '@dxos/storybook-utils';
19
17
  import { trim } from '@dxos/util';
20
18
 
21
19
  import { type PreviewLinkRef, type PreviewLinkTarget, getLinkRef, image, preview } from '../extensions';
@@ -169,8 +167,10 @@ const PreviewBlock = ({ link, el, view }: { link: PreviewLinkRef; el: HTMLElemen
169
167
  const meta = {
170
168
  title: 'ui/react-ui-editor/Preview',
171
169
  component: EditorStory,
172
- decorators: [withTheme, withLayout({ fullscreen: true })],
173
- parameters: { layout: 'fullscreen' },
170
+ decorators: [withTheme],
171
+ parameters: {
172
+ layout: 'fullscreen',
173
+ },
174
174
  } satisfies Meta<typeof EditorStory>;
175
175
 
176
176
  export default meta;
@@ -2,14 +2,12 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useState } from 'react';
9
7
  import { createPortal } from 'react-dom';
10
8
 
11
9
  import { useThemeContext } from '@dxos/react-ui';
12
- import { withLayout, withTheme } from '@dxos/storybook-utils';
10
+ import { withTheme } from '@dxos/react-ui/testing';
13
11
  import { trim } from '@dxos/util';
14
12
 
15
13
  import {
@@ -66,8 +64,10 @@ const text = trim`
66
64
  const meta = {
67
65
  title: 'ui/react-ui-editor/Tags',
68
66
  render: DefaultStory,
69
- decorators: [withTheme, withLayout({ fullscreen: true })],
70
- parameters: { layout: 'fullscreen' },
67
+ decorators: [withTheme],
68
+ parameters: {
69
+ layout: 'fullscreen',
70
+ },
71
71
  } satisfies Meta<typeof DefaultStory>;
72
72
 
73
73
  export default meta;