@dxos/react-ui-editor 0.3.11-main.cb8120f → 0.3.11-main.ccc0ca3

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 (54) hide show
  1. package/dist/lib/browser/index.mjs +362 -45
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +1 -2
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  7. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -1
  8. package/dist/types/src/components/TextEditor/hooks.stories.d.ts +23 -0
  9. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
  10. package/dist/types/src/components/Toolbar/Toolbar.d.ts +23 -7
  11. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  12. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +10 -1
  13. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  14. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  15. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  16. package/dist/types/src/components/index.d.ts +1 -0
  17. package/dist/types/src/components/index.d.ts.map +1 -1
  18. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  19. package/dist/types/src/extensions/basic.d.ts +1 -1
  20. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  21. package/dist/types/src/extensions/comments.d.ts +1 -1
  22. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  23. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -2
  24. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  25. package/dist/types/src/extensions/markdown/formatting.d.ts +2 -0
  26. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  27. package/dist/types/src/hooks/index.d.ts +1 -0
  28. package/dist/types/src/hooks/index.d.ts.map +1 -1
  29. package/dist/types/src/hooks/useActionHandler.d.ts +2 -2
  30. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -1
  31. package/dist/types/src/hooks/useEditorView.d.ts +17 -0
  32. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
  33. package/dist/types/src/hooks/useTextEditor.d.ts +36 -14
  34. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  35. package/dist/types/src/themes/default.d.ts.map +1 -1
  36. package/package.json +26 -25
  37. package/src/components/TextEditor/MarkdownEditor.stories.tsx +2 -3
  38. package/src/components/TextEditor/TextEditor.tsx +23 -13
  39. package/src/components/TextEditor/comments.stories.tsx +3 -3
  40. package/src/components/TextEditor/hooks.stories.tsx +106 -0
  41. package/src/components/Toolbar/Toolbar.stories.tsx +7 -3
  42. package/src/components/Toolbar/Toolbar.tsx +120 -69
  43. package/src/components/Toolbar/index.ts +5 -0
  44. package/src/components/index.ts +1 -0
  45. package/src/extensions/automerge/update-automerge.ts +7 -7
  46. package/src/extensions/basic.ts +15 -13
  47. package/src/extensions/comments.ts +2 -2
  48. package/src/extensions/markdown/bundle.ts +10 -5
  49. package/src/extensions/markdown/formatting.ts +31 -2
  50. package/src/hooks/index.ts +1 -0
  51. package/src/hooks/useActionHandler.ts +22 -5
  52. package/src/hooks/useEditorView.ts +29 -0
  53. package/src/hooks/useTextEditor.ts +143 -19
  54. package/src/themes/default.ts +3 -1
@@ -1,17 +1,39 @@
1
- import { type EditorView } from '@codemirror/view';
2
- import { type RefCallback } from 'react';
1
+ import { type EditorStateConfig, type Extension, type StateEffect } from '@codemirror/state';
2
+ import { EditorView } from '@codemirror/view';
3
+ import { type RefObject } from 'react';
4
+ import { type ThemeMode } from '@dxos/react-ui';
5
+ import { type ThemeStyles } from '..//styles';
6
+ export type UseTextEditorOptions = {
7
+ autoFocus?: boolean;
8
+ scrollTo?: StateEffect<any>;
9
+ debug?: boolean;
10
+ } & EditorStateConfig;
11
+ export type UseTextEditor = {
12
+ parentRef: RefObject<HTMLDivElement>;
13
+ view?: EditorView;
14
+ };
3
15
  /**
4
- * Hook for accessing the editor view.
5
- *
6
- * ```tsx
7
- * const Test = () => {
8
- * const [editorRef, editorView] = useTextEditor();
9
- * useEffect(() => {
10
- * editorView?.focus();
11
- * }, [editorView]);
12
- * return <TextEditor ref={editorRef} />;
13
- * };
14
- * ```
16
+ * Hook for creating editor.
15
17
  */
16
- export declare const useTextEditor: () => [RefCallback<EditorView | null>, EditorView | null];
18
+ export declare const useTextEditor: ({ autoFocus, scrollTo, debug, doc, selection, extensions, }?: UseTextEditorOptions) => UseTextEditor;
19
+ export type DataExtensionsOptions = {
20
+ readonly?: boolean;
21
+ };
22
+ export declare const createDataExtensions: ({ readonly }?: {
23
+ readonly?: boolean | undefined;
24
+ }) => Extension;
25
+ export type ThemeExtensionsOptions = {
26
+ theme?: ThemeStyles;
27
+ themeMode?: ThemeMode;
28
+ lineWrap?: boolean;
29
+ slots?: {
30
+ editor?: {
31
+ className?: string;
32
+ };
33
+ content?: {
34
+ className?: string;
35
+ };
36
+ };
37
+ };
38
+ export declare const createThemeExtensions: ({ theme, themeMode, slots }?: ThemeExtensionsOptions) => Extension;
17
39
  //# sourceMappingURL=useTextEditor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useTextEditor.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTextEditor.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,KAAK,WAAW,EAAY,MAAM,OAAO,CAAC;AAEnD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,QAAO,CAAC,YAAY,UAAU,GAAG,IAAI,CAAC,EAAE,UAAU,GAAG,IAAI,CAQlF,CAAC"}
1
+ {"version":3,"file":"useTextEditor.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTextEditor.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,KAAK,SAAS,EAA+B,MAAM,OAAO,CAAC;AAGpE,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,iBAAiB,CAAC;AAEtB,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,iEAOvB,oBAAoB,KAAQ,aAqE9B,CAAC;AAIF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAGF,eAAO,MAAM,oBAAoB;;MAAgC,SAMhE,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE;QACN,MAAM,CAAC,EAAE;YACP,SAAS,CAAC,EAAE,MAAM,CAAC;SACpB,CAAC;QACF,OAAO,CAAC,EAAE;YACR,SAAS,CAAC,EAAE,MAAM,CAAC;SACpB,CAAC;KACH,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,qBAAqB,iCAAiC,sBAAsB,KAAQ,SAShG,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../../src/themes/default.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,WAAW,EAAU,MAAM,WAAW,CAAC;AAKrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,EAAE,WA+N1B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAOvB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,WAO3B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAOvB,CAAC"}
1
+ {"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../../src/themes/default.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,WAAW,EAAU,MAAM,WAAW,CAAC;AAKrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,EAAE,WAiO1B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAOvB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,WAO3B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAOvB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-editor",
3
- "version": "0.3.11-main.cb8120f",
3
+ "version": "0.3.11-main.ccc0ca3",
4
4
  "description": "Document editing experience within a DXOS shell.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -23,10 +23,11 @@
23
23
  "@codemirror/state": "^6.4.0",
24
24
  "@codemirror/theme-one-dark": "^6.1.2",
25
25
  "@codemirror/view": "^6.23.0",
26
- "@fluentui/react-tabster": "^9.16.1",
26
+ "@fluentui/react-tabster": "^9.17.4",
27
27
  "@lezer/common": "^1.2.1",
28
28
  "@lezer/highlight": "^1.2.0",
29
29
  "@lezer/markdown": "^1.2.0",
30
+ "@radix-ui/react-context": "^1.0.0",
30
31
  "@replit/codemirror-vim": "^6.0.14",
31
32
  "codemirror": "6.0.1",
32
33
  "lib0": "^0.2.65",
@@ -35,20 +36,20 @@
35
36
  "lodash.pick": "^4.4.0",
36
37
  "lodash.sortby": "^4.7.0",
37
38
  "style-mod": "^4.1.0",
38
- "@dxos/async": "0.3.11-main.cb8120f",
39
- "@dxos/context": "0.3.11-main.cb8120f",
40
- "@dxos/automerge": "0.3.11-main.cb8120f",
41
- "@dxos/display-name": "0.3.11-main.cb8120f",
42
- "@dxos/invariant": "0.3.11-main.cb8120f",
43
- "@dxos/echo-schema": "0.3.11-main.cb8120f",
44
- "@dxos/debug": "0.3.11-main.cb8120f",
45
- "@dxos/log": "0.3.11-main.cb8120f",
46
- "@dxos/protocols": "0.3.11-main.cb8120f",
47
- "@dxos/react-async": "0.3.11-main.cb8120f",
48
- "@dxos/react-ui": "0.3.11-main.cb8120f",
49
- "@dxos/util": "0.3.11-main.cb8120f",
50
- "@dxos/text-model": "0.3.11-main.cb8120f",
51
- "@dxos/react-ui-theme": "0.3.11-main.cb8120f"
39
+ "@dxos/async": "0.3.11-main.ccc0ca3",
40
+ "@dxos/automerge": "0.3.11-main.ccc0ca3",
41
+ "@dxos/context": "0.3.11-main.ccc0ca3",
42
+ "@dxos/display-name": "0.3.11-main.ccc0ca3",
43
+ "@dxos/debug": "0.3.11-main.ccc0ca3",
44
+ "@dxos/echo-schema": "0.3.11-main.ccc0ca3",
45
+ "@dxos/invariant": "0.3.11-main.ccc0ca3",
46
+ "@dxos/log": "0.3.11-main.ccc0ca3",
47
+ "@dxos/react-async": "0.3.11-main.ccc0ca3",
48
+ "@dxos/react-ui": "0.3.11-main.ccc0ca3",
49
+ "@dxos/protocols": "0.3.11-main.ccc0ca3",
50
+ "@dxos/react-ui-theme": "0.3.11-main.ccc0ca3",
51
+ "@dxos/text-model": "0.3.11-main.ccc0ca3",
52
+ "@dxos/util": "0.3.11-main.ccc0ca3"
52
53
  },
53
54
  "devDependencies": {
54
55
  "@automerge/automerge-repo-network-broadcastchannel": "^1.1.0",
@@ -76,24 +77,24 @@
76
77
  "vite-plugin-top-level-await": "^1.4.1",
77
78
  "vite-plugin-wasm": "^3.3.0",
78
79
  "vitest": "^1.2.1",
79
- "@dxos/config": "0.3.11-main.cb8120f",
80
- "@dxos/echo-signals": "0.3.11-main.cb8120f",
81
- "@dxos/echo-typegen": "0.3.11-main.cb8120f",
82
- "@dxos/keyboard": "0.3.11-main.cb8120f",
83
- "@dxos/react-ui": "0.3.11-main.cb8120f",
84
- "@dxos/react-client": "0.3.11-main.cb8120f",
85
- "@dxos/storybook-utils": "0.3.11-main.cb8120f"
80
+ "@dxos/config": "0.3.11-main.ccc0ca3",
81
+ "@dxos/echo-signals": "0.3.11-main.ccc0ca3",
82
+ "@dxos/react-client": "0.3.11-main.ccc0ca3",
83
+ "@dxos/react-ui": "0.3.11-main.ccc0ca3",
84
+ "@dxos/storybook-utils": "0.3.11-main.ccc0ca3",
85
+ "@dxos/echo-typegen": "0.3.11-main.ccc0ca3",
86
+ "@dxos/keyboard": "0.3.11-main.ccc0ca3"
86
87
  },
87
88
  "peerDependencies": {
88
89
  "@phosphor-icons/react": "^2.0.5",
89
90
  "react": "^18.2.0",
90
91
  "react-dom": "^18.2.0",
91
- "@dxos/react-client": "0.3.11-main.cb8120f"
92
+ "@dxos/react-client": "0.3.11-main.ccc0ca3"
92
93
  },
93
94
  "publishConfig": {
94
95
  "access": "public"
95
96
  },
96
97
  "scripts": {
97
- "vitest": "vitest --ui --dir ./src/extensions/automerge"
98
+ "vitest": "vitest --ui"
98
99
  }
99
100
  }
@@ -211,11 +211,10 @@ const onRenderLink: LinkOptions['onRender'] = (el, url) => {
211
211
  type StoryProps = {
212
212
  text?: string;
213
213
  comments?: Comment[];
214
- automerge?: boolean;
215
214
  } & Pick<TextEditorProps, 'readonly' | 'placeholder' | 'slots' | 'extensions'>;
216
215
 
217
- const Story = ({ text, comments, automerge, placeholder = 'New document.', ...props }: StoryProps) => {
218
- const [item] = useState({ text: new TextObject(text, undefined, undefined, { automerge }) });
216
+ const Story = ({ text, comments, placeholder = 'New document.', ...props }: StoryProps) => {
217
+ const [item] = useState({ text: new TextObject(text) });
219
218
  const view = useRef<EditorView>(null);
220
219
  const model = useTextModel({ text: item.text });
221
220
  useComments(view.current, comments);
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { EditorState, type Extension, type StateEffect } from '@codemirror/state';
6
6
  import { EditorView } from '@codemirror/view';
7
- import { useFocusableGroup } from '@fluentui/react-tabster';
7
+ // import { useFocusableGroup } from '@fluentui/react-tabster';
8
8
  import { vim } from '@replit/codemirror-vim';
9
9
  import defaultsDeep from 'lodash.defaultsdeep';
10
10
  import React, {
@@ -21,9 +21,9 @@ import React, {
21
21
  import { log } from '@dxos/log';
22
22
  import { useThemeContext } from '@dxos/react-ui';
23
23
  import { attentionSurface, mx } from '@dxos/react-ui-theme';
24
- import { nonNullable } from '@dxos/util';
24
+ import { isNotFalsy } from '@dxos/util';
25
25
 
26
- import { basicBundle, markdownBundle } from '../../extensions';
26
+ import { createBasicBundle, createMarkdownExtensions } from '../../extensions';
27
27
  import { type EditorModel } from '../../hooks';
28
28
  import { type ThemeStyles } from '../../styles';
29
29
  import { defaultTheme, markdownTheme, textTheme } from '../../themes';
@@ -87,7 +87,12 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
87
87
  },
88
88
  forwardedRef,
89
89
  ) => {
90
- const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
90
+ // TODO(burdon): Hook causes error even if properties are not spread into div.
91
+ // Uncaught TypeError: Cannot read properties of undefined (reading 'relatedTarget')
92
+ // Uses event.detail, which is deprecated (not event.details). At runtime the event has a property `details`.
93
+ // https://github.com/microsoft/tabster/blob/master/src/State/FocusedElement.ts#L348 (e.detail.relatedTarget)
94
+ // https://github.com/microsoft/keyborg/blob/49e49b2c3ba0a5f6cc518ac46825d7551def8109/src/FocusEvent.ts#L58
95
+ // const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
91
96
  const { themeMode } = useThemeContext();
92
97
 
93
98
  const rootRef = useRef<HTMLDivElement>(null);
@@ -99,7 +104,12 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
99
104
 
100
105
  // Set focus.
101
106
  useEffect(() => {
102
- if (autoFocus) {
107
+ if (autoFocus && !view?.hasFocus) {
108
+ if (view?.state.selection.main?.from === 0) {
109
+ // Start at end of line.
110
+ const { to } = view.state.doc.lineAt(0);
111
+ view?.dispatch({ selection: { anchor: to } });
112
+ }
103
113
  view?.focus();
104
114
  }
105
115
  }, [view, autoFocus]);
@@ -140,12 +150,12 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
140
150
  // NOTE: This must come before user extensions.
141
151
  model.extension,
142
152
 
143
- // TODO(burdon): Factor out (requires special handling for Escape/focus).
153
+ // TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
144
154
  editorMode === 'vim' && vim(),
145
155
 
146
156
  // Custom.
147
157
  ...extensions,
148
- ].filter(nonNullable) as Extension[], // TODO(burdon): ???
158
+ ].filter(isNotFalsy),
149
159
  });
150
160
 
151
161
  //
@@ -177,6 +187,7 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
177
187
 
178
188
  // Handles tab/focus.
179
189
  // Pressing Escape focuses the outer div (to support tab navigation); pressing Enter refocuses the editor.
190
+ // TODO(burdon): Convert to keymap?
180
191
  const handleKeyUp = useCallback(
181
192
  (event: KeyboardEvent) => {
182
193
  const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
@@ -188,7 +199,7 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
188
199
 
189
200
  case 'Escape': {
190
201
  if (editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey)) {
191
- rootRef.current?.focus();
202
+ view?.focus();
192
203
  }
193
204
  break;
194
205
  }
@@ -202,16 +213,15 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
202
213
  key={model.id}
203
214
  role='none'
204
215
  tabIndex={0}
205
- onKeyUp={handleKeyUp}
206
216
  {...slots.root}
207
- {...(editorMode !== 'vim' && tabsterDOMAttribute)}
217
+ // {...(editorMode !== 'vim' && tabsterDOMAttribute)}
218
+ onKeyUp={handleKeyUp}
208
219
  ref={rootRef}
209
220
  />
210
221
  );
211
222
  },
212
223
  );
213
224
 
214
- // TODO(burdon): Single-line/scroll.
215
225
  export const TextEditor = forwardRef<EditorView, TextEditorProps>(
216
226
  ({ readonly, placeholder, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
217
227
  const { themeMode } = useThemeContext();
@@ -220,7 +230,7 @@ export const TextEditor = forwardRef<EditorView, TextEditorProps>(
220
230
  <BaseTextEditor
221
231
  ref={forwardedRef}
222
232
  readonly={readonly}
223
- extensions={[basicBundle({ themeMode, placeholder, lineWrapping }), ...extensions]}
233
+ extensions={[createBasicBundle({ themeMode, placeholder, lineWrapping }), ...extensions]}
224
234
  theme={theme}
225
235
  slots={updatedSlots}
226
236
  {...props}
@@ -237,7 +247,7 @@ export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
237
247
  <BaseTextEditor
238
248
  ref={forwardedRef}
239
249
  readonly={readonly}
240
- extensions={[markdownBundle({ themeMode, placeholder }), ...extensions]}
250
+ extensions={[createMarkdownExtensions({ themeMode, placeholder }), ...extensions]}
241
251
  theme={theme}
242
252
  slots={updatedSlots}
243
253
  {...props}
@@ -17,7 +17,7 @@ import { fixedInsetFlexLayout, mx } from '@dxos/react-ui-theme';
17
17
  import { withTheme } from '@dxos/storybook-utils';
18
18
 
19
19
  import { MarkdownEditor, TextEditor } from './TextEditor';
20
- import { comments, type CommentsOptions, setFocus, useComments } from '../../extensions';
20
+ import { comments, type CommentsOptions, focusComment, useComments } from '../../extensions';
21
21
  import { type Comment, type Range, useTextModel } from '../../hooks';
22
22
 
23
23
  faker.seed(101);
@@ -50,7 +50,7 @@ const Editor: FC<{
50
50
  if (!view.current?.hasFocus && selectedValue !== selected) {
51
51
  setSelected(selectedValue);
52
52
  if (selectedValue) {
53
- setFocus(view.current!, selectedValue);
53
+ focusComment(view.current!, selectedValue);
54
54
  }
55
55
  }
56
56
  }, [selected, commentRanges, selectedValue]);
@@ -150,7 +150,7 @@ const Thread: FC<{
150
150
  </div>
151
151
  ))}
152
152
 
153
- <div ref={containerRef} onClick={() => onSelect()} className='flex'>
153
+ <div ref={containerRef} className='flex' onClick={() => onSelect()}>
154
154
  <TextEditor
155
155
  ref={editorRef}
156
156
  autoFocus={focus}
@@ -0,0 +1,106 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import '@dxosTheme';
6
+
7
+ import React from 'react';
8
+
9
+ import { useThemeContext } from '@dxos/react-ui';
10
+ import { fixedInsetFlexLayout, groupSurface, mx } from '@dxos/react-ui-theme';
11
+ import { withTheme } from '@dxos/storybook-utils';
12
+
13
+ import { TextEditor } from './TextEditor';
14
+ import {
15
+ code,
16
+ createMarkdownExtensions,
17
+ formatting,
18
+ heading,
19
+ hr,
20
+ image,
21
+ link,
22
+ table,
23
+ tasklist,
24
+ } from '../../extensions';
25
+ import { createDataExtensions, createThemeExtensions, useActionHandler, useTextEditor } from '../../hooks';
26
+ import { markdownTheme } from '../../themes';
27
+ import { Toolbar } from '../Toolbar';
28
+
29
+ // TODO(burdon): Demo toolbar with hooks.
30
+ // TODO(burdon): Build components from hooks and adapters for model/extensions, etc.
31
+ // TODO(burdon): Remove BaseTextEditor.
32
+ // TODO(burdon): Move scrolling container layout/logic into TextEditor/MarkdownEditor components.
33
+ // TODO(burdon): Define keymap in composer framework format.
34
+
35
+ type StoryProps = {
36
+ autoFocus?: boolean;
37
+ placeholder?: string;
38
+ doc?: string;
39
+ readonly?: boolean;
40
+ };
41
+
42
+ const Story = ({ autoFocus, placeholder, doc, readonly }: StoryProps) => {
43
+ const { themeMode } = useThemeContext();
44
+ const { parentRef, view } = useTextEditor({
45
+ autoFocus,
46
+ doc,
47
+ extensions: [
48
+ //
49
+ createDataExtensions({ readonly }),
50
+ createThemeExtensions({
51
+ themeMode,
52
+ theme: markdownTheme,
53
+ slots: {
54
+ // TODO(burdon): Document classes re base theme.
55
+ // Semantic tokens (e.g., replacement of input surface).
56
+ editor: {
57
+ className: 'h-full p-4 bg-white text-black dark:bg-black dark:text-white',
58
+ },
59
+ },
60
+ }),
61
+ // TODO(burdon): Move lineWrapping.
62
+ createMarkdownExtensions({ placeholder, lineWrapping: true }),
63
+ // TODO(burdon): Move into markdown bundle (with React callbacks).
64
+ code(),
65
+ formatting(),
66
+ heading(),
67
+ hr(),
68
+ image(),
69
+ link(),
70
+ table(),
71
+ tasklist(),
72
+ ],
73
+ });
74
+
75
+ const handleAction = useActionHandler(view);
76
+
77
+ return (
78
+ <div className={mx(fixedInsetFlexLayout, groupSurface)}>
79
+ <div className='flex h-full justify-center'>
80
+ <div className='flex flex-col h-full w-[800px]'>
81
+ <Toolbar.Root onAction={handleAction}>
82
+ <Toolbar.Markdown />
83
+ </Toolbar.Root>
84
+
85
+ {/* TODO(burdon): Handle scrolling in component wrapper (like this). */}
86
+ <div role='none' className='h-full overflow-y-auto' ref={parentRef} />
87
+ </div>
88
+ </div>
89
+ </div>
90
+ );
91
+ };
92
+
93
+ export default {
94
+ title: 'react-ui-editor/useTextEditor',
95
+ component: TextEditor,
96
+ decorators: [withTheme],
97
+ render: (args: StoryProps) => <Story {...args} />,
98
+ };
99
+
100
+ export const Default = {
101
+ args: {
102
+ autoFocus: true,
103
+ placeholder: 'Text...',
104
+ doc: '# Demo\n\nThis is a document.\n\n',
105
+ },
106
+ };
@@ -14,7 +14,7 @@ import { withTheme } from '@dxos/storybook-utils';
14
14
 
15
15
  import { Toolbar } from './Toolbar';
16
16
  import { code, comments, formatting, heading, image, table, tasklist, useComments } from '../../extensions';
17
- import { type Comment, useActionHandler, useTextEditor, useTextModel } from '../../hooks';
17
+ import { type Comment, useActionHandler, useEditorView, useTextModel } from '../../hooks';
18
18
  import { MarkdownEditor } from '../TextEditor';
19
19
 
20
20
  faker.seed(101);
@@ -22,7 +22,7 @@ faker.seed(101);
22
22
  const Story: FC<{ content: string }> = ({ content }) => {
23
23
  const [item] = useState({ text: new TextObject(content) });
24
24
  const [_comments, setComments] = useState<Comment[]>([]);
25
- const [editorRef, editorView] = useTextEditor();
25
+ const [editorRef, editorView] = useEditorView();
26
26
  const model = useTextModel({ text: item.text });
27
27
  const extensions = useMemo(
28
28
  () => [
@@ -54,7 +54,11 @@ const Story: FC<{ content: string }> = ({ content }) => {
54
54
  <div className={mx(fixedInsetFlexLayout, groupSurface)}>
55
55
  <div className='flex h-full justify-center'>
56
56
  <div className='flex flex-col h-full w-[800px]'>
57
- <Toolbar onAction={handleAction} />
57
+ <Toolbar.Root onAction={handleAction}>
58
+ <Toolbar.Markdown />
59
+ <Toolbar.Separator />
60
+ <Toolbar.Extended />
61
+ </Toolbar.Root>
58
62
  <MarkdownEditor ref={editorRef} model={model} extensions={extensions} />
59
63
  </div>
60
64
  </div>