@dxos/react-ui-editor 0.3.10 → 0.3.11-main.00a28cb
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.
- package/dist/lib/browser/index.mjs +1724 -1105
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +24 -13
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +15 -19
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +6 -3
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/{hooks/automerge → components/TextEditor}/automerge.stories.d.ts +6 -2
- package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
- package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/index.d.ts +0 -2
- package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
- package/dist/types/src/{hooks/yjs → components/TextEditor}/yjs.stories.d.ts +1 -1
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts +9 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts +5 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/defs.d.ts +26 -0
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/index.d.ts +3 -0
- package/dist/types/src/extensions/automerge/index.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/semaphore.d.ts +14 -0
- package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/update-automerge.d.ts +5 -0
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/update-codemirror.d.ts +5 -0
- package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -0
- package/dist/types/src/extensions/awareness.d.ts +46 -0
- package/dist/types/src/extensions/awareness.d.ts.map +1 -0
- package/dist/types/src/extensions/basic.d.ts.map +1 -0
- package/dist/types/src/extensions/blast.d.ts.map +1 -0
- package/dist/types/src/extensions/code.d.ts.map +1 -0
- package/dist/types/src/extensions/comments.d.ts +59 -0
- package/dist/types/src/extensions/comments.d.ts.map +1 -0
- package/dist/types/src/extensions/cursor.d.ts +25 -0
- package/dist/types/src/extensions/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/hr.d.ts +2 -0
- package/dist/types/src/extensions/hr.d.ts.map +1 -0
- package/dist/types/src/extensions/image.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/index.d.ts +7 -3
- package/dist/types/src/extensions/index.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/link.d.ts +1 -0
- package/dist/types/src/extensions/link.d.ts.map +1 -0
- package/dist/types/src/extensions/listener.d.ts +10 -0
- package/dist/types/src/extensions/listener.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/highlight.d.ts +0 -1
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/index.d.ts.map +1 -0
- package/dist/types/src/extensions/mention.d.ts.map +1 -0
- package/dist/types/src/extensions/outliner.d.ts +4 -0
- package/dist/types/src/extensions/outliner.d.ts.map +1 -0
- package/dist/types/src/extensions/table.d.ts.map +1 -0
- package/dist/types/src/extensions/tasklist.d.ts +7 -0
- package/dist/types/src/extensions/tasklist.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions/demo.d.ts → extensions/typewriter.d.ts} +2 -3
- package/dist/types/src/extensions/typewriter.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/cursor.d.ts +4 -0
- package/dist/types/src/extensions/yjs/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/index.d.ts +2 -0
- package/dist/types/src/extensions/yjs/index.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/yjs.d.ts +4 -0
- package/dist/types/src/extensions/yjs/yjs.d.ts.map +1 -0
- package/dist/types/src/extensions/yjs/yjs.test.d.ts +2 -0
- package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +1 -0
- package/dist/types/src/hooks/automerge.d.ts +3 -0
- package/dist/types/src/hooks/automerge.d.ts.map +1 -0
- package/dist/types/src/hooks/awareness-provider.d.ts +28 -0
- package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +3 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextModel.d.ts +15 -11
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/hooks/{yjs/space-provider.d.ts → yjs.d.ts} +8 -8
- package/dist/types/src/hooks/yjs.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +6 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/styles/cursors.d.ts.map +1 -0
- package/dist/types/src/styles/index.d.ts +1 -0
- package/dist/types/src/styles/index.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/styles/tokens.d.ts +1 -0
- package/dist/types/src/styles/tokens.d.ts.map +1 -1
- package/dist/types/src/testing/replicator.d.ts +2 -2
- package/dist/types/src/testing/replicator.d.ts.map +1 -1
- package/dist/types/src/{components/TextEditor/themes → themes}/default.d.ts +5 -13
- package/dist/types/src/themes/default.d.ts.map +1 -0
- package/dist/types/src/themes/index.d.ts.map +1 -0
- package/dist/types/src/util.d.ts +5 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/package.json +28 -22
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +150 -87
- package/src/components/TextEditor/TextEditor.stories.tsx +14 -35
- package/src/components/TextEditor/TextEditor.tsx +87 -79
- package/src/{hooks/automerge → components/TextEditor}/automerge.stories.tsx +67 -8
- package/src/components/TextEditor/codemirror.stories.ts +3 -2
- package/src/components/TextEditor/comments.stories.tsx +355 -0
- package/src/components/TextEditor/index.ts +0 -3
- package/src/{hooks/yjs → components/TextEditor}/yjs.stories.tsx +1 -1
- package/src/{components/TextEditor/extensions → extensions}/autocomplete.ts +18 -17
- package/src/extensions/automerge/automerge.ts +92 -0
- package/src/extensions/automerge/cursor.ts +52 -0
- package/src/extensions/automerge/defs.ts +66 -0
- package/src/extensions/automerge/index.ts +6 -0
- package/src/{hooks/automerge/PatchSemaphore.ts → extensions/automerge/semaphore.ts} +26 -25
- package/src/{hooks/automerge/translation/codemirror-to-automerge.ts → extensions/automerge/update-automerge.ts} +6 -7
- package/src/{hooks/automerge/translation/automerge-to-codemirror.ts → extensions/automerge/update-codemirror.ts} +14 -11
- package/src/extensions/awareness.ts +310 -0
- package/src/extensions/code.ts +104 -0
- package/src/extensions/comments.ts +408 -0
- package/src/extensions/cursor.ts +46 -0
- package/src/extensions/hr.ts +72 -0
- package/src/extensions/image.ts +87 -0
- package/src/{components/TextEditor/extensions → extensions}/index.ts +7 -3
- package/src/{components/TextEditor/extensions → extensions}/link.ts +116 -62
- package/src/extensions/listener.ts +35 -0
- package/src/{components/TextEditor/extensions → extensions}/markdown/highlight.ts +4 -16
- package/src/extensions/outliner.ts +12 -0
- package/src/{components/TextEditor/extensions → extensions}/table.ts +23 -4
- package/src/extensions/tasklist.ts +96 -0
- package/src/{components/TextEditor/extensions/demo.ts → extensions/typewriter.ts} +2 -3
- package/src/extensions/yjs/cursor.ts +22 -0
- package/src/extensions/yjs/index.ts +5 -0
- package/src/extensions/yjs/yjs.test.ts +35 -0
- package/src/extensions/yjs/yjs.ts +16 -0
- package/src/hooks/automerge.ts +52 -0
- package/src/hooks/awareness-provider.ts +120 -0
- package/src/hooks/index.ts +4 -1
- package/src/hooks/useTextModel.ts +40 -79
- package/src/hooks/{yjs/space-provider.ts → yjs.ts} +49 -19
- package/src/index.ts +6 -0
- package/src/styles/index.ts +1 -0
- package/src/styles/markdown.ts +0 -2
- package/src/styles/tokens.ts +3 -0
- package/src/testing/replicator.ts +10 -16
- package/src/themes/default.ts +275 -0
- package/src/util.ts +18 -0
- package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/comments.d.ts +0 -18
- package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/experimental.d.ts +0 -10
- package/dist/types/src/components/TextEditor/extensions/experimental.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/highlight.d.ts +0 -24
- package/dist/types/src/components/TextEditor/extensions/highlight.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts +0 -9
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +0 -3
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts +0 -12
- package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/themes/default.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/themes/index.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/PatchSemaphore.d.ts +0 -19
- package/dist/types/src/hooks/automerge/PatchSemaphore.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/handle.d.ts +0 -9
- package/dist/types/src/hooks/automerge/handle.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/index.d.ts +0 -3
- package/dist/types/src/hooks/automerge/index.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/plugin.d.ts +0 -26
- package/dist/types/src/hooks/automerge/plugin.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts +0 -5
- package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts +0 -6
- package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/translation/index.d.ts +0 -3
- package/dist/types/src/hooks/automerge/translation/index.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/cursors.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/index.d.ts +0 -3
- package/dist/types/src/hooks/yjs/index.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/space-provider.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +0 -1
- package/src/components/TextEditor/extensions/code.ts +0 -99
- package/src/components/TextEditor/extensions/comments.ts +0 -173
- package/src/components/TextEditor/extensions/experimental.tsx +0 -114
- package/src/components/TextEditor/extensions/highlight.ts +0 -128
- package/src/components/TextEditor/extensions/image.ts +0 -67
- package/src/components/TextEditor/extensions/listener.ts +0 -23
- package/src/components/TextEditor/extensions/tasklist.ts +0 -121
- package/src/components/TextEditor/extensions/tooltip.tsx +0 -61
- package/src/components/TextEditor/themes/default.ts +0 -279
- package/src/hooks/automerge/handle.ts +0 -14
- package/src/hooks/automerge/index.ts +0 -6
- package/src/hooks/automerge/plugin.ts +0 -134
- package/src/hooks/automerge/translation/index.ts +0 -6
- package/src/hooks/yjs/index.ts +0 -6
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/autocomplete.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/basic.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/blast.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/code.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/image.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/bundle.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/index.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/mention.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/table.d.ts +0 -0
- /package/dist/types/src/{hooks/yjs → styles}/cursors.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/themes → themes}/index.d.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/basic.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/blast.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/markdown/bundle.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/markdown/index.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/mention.ts +0 -0
- /package/src/{hooks/yjs → styles}/cursors.ts +0 -0
- /package/src/{components/TextEditor/themes → themes}/index.ts +0 -0
|
@@ -15,16 +15,17 @@ import React, {
|
|
|
15
15
|
useEffect,
|
|
16
16
|
useImperativeHandle,
|
|
17
17
|
useState,
|
|
18
|
+
useRef,
|
|
18
19
|
} from 'react';
|
|
19
20
|
|
|
20
21
|
import { generateName } from '@dxos/display-name';
|
|
21
22
|
import { useThemeContext } from '@dxos/react-ui';
|
|
22
23
|
import { getColorForValue, inputSurface, mx } from '@dxos/react-ui-theme';
|
|
23
24
|
|
|
24
|
-
import { basicBundle, markdownBundle } from '
|
|
25
|
-
import {
|
|
26
|
-
import { type EditorModel } from '../../hooks';
|
|
25
|
+
import { basicBundle, markdownBundle, setCommentRange } from '../../extensions';
|
|
26
|
+
import { type CommentRange, type EditorModel } from '../../hooks';
|
|
27
27
|
import { type ThemeStyles } from '../../styles';
|
|
28
|
+
import { defaultTheme, markdownTheme, textTheme } from '../../themes';
|
|
28
29
|
|
|
29
30
|
export const EditorModes = ['default', 'vim'] as const;
|
|
30
31
|
export type EditorMode = (typeof EditorModes)[number];
|
|
@@ -34,49 +35,55 @@ export type CursorInfo = {
|
|
|
34
35
|
to: number;
|
|
35
36
|
line: number;
|
|
36
37
|
lines: number;
|
|
38
|
+
length: number;
|
|
37
39
|
after?: string;
|
|
38
40
|
};
|
|
39
41
|
|
|
40
|
-
export type TextEditorRef = {
|
|
41
|
-
root: HTMLDivElement | null;
|
|
42
|
-
state?: EditorState;
|
|
43
|
-
view?: EditorView;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
42
|
export type TextEditorSlots = {
|
|
47
43
|
root?: Omit<ComponentProps<'div'>, 'ref'>;
|
|
48
|
-
editor?: {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
spellCheck?: boolean;
|
|
52
|
-
tabIndex?: number;
|
|
53
|
-
theme?: ThemeStyles;
|
|
54
|
-
};
|
|
44
|
+
// editor?: {
|
|
45
|
+
// className?: string;
|
|
46
|
+
// };
|
|
55
47
|
};
|
|
56
48
|
|
|
49
|
+
// TODO(burdon): Spellcheck?
|
|
57
50
|
export type TextEditorProps = {
|
|
58
51
|
model: EditorModel;
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
autofocus?: boolean;
|
|
53
|
+
readonly?: boolean; // TODO(burdon): Move into model.
|
|
54
|
+
selection?: { anchor: number; head?: number };
|
|
55
|
+
comments?: CommentRange[]; // TODO(burdon): Move into extension?
|
|
61
56
|
extensions?: Extension[];
|
|
57
|
+
editorMode?: EditorMode;
|
|
58
|
+
placeholder?: string;
|
|
59
|
+
theme?: ThemeStyles;
|
|
62
60
|
slots?: TextEditorSlots;
|
|
63
61
|
};
|
|
64
62
|
|
|
65
63
|
/**
|
|
66
64
|
* Base text editor.
|
|
67
|
-
* NOTE: Rather than adding properties, try to create extensions that can be reused.
|
|
68
65
|
*/
|
|
69
|
-
export const BaseTextEditor = forwardRef<
|
|
70
|
-
(
|
|
71
|
-
|
|
66
|
+
export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
67
|
+
(
|
|
68
|
+
{ model, autofocus, readonly, selection, comments, extensions = [], editorMode, theme, slots = defaultSlots },
|
|
69
|
+
forwardedRef,
|
|
70
|
+
) => {
|
|
72
71
|
const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
|
|
72
|
+
const { themeMode } = useThemeContext();
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
const [view, setView] = useState<EditorView>();
|
|
77
|
-
|
|
74
|
+
// The editor view ref should only be used as an escape hatch.
|
|
75
|
+
const rootRef = useRef<HTMLDivElement>(null);
|
|
76
|
+
const [view, setView] = useState<EditorView | null>(null);
|
|
77
|
+
// NOTE: This does not cause the parent to re-render, so the ref is not available immediately.
|
|
78
|
+
useImperativeHandle<EditorView | null, EditorView | null>(forwardedRef, () => view, [view]);
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (autofocus) {
|
|
82
|
+
view?.focus();
|
|
83
|
+
}
|
|
84
|
+
}, [autofocus, view]);
|
|
85
|
+
|
|
86
|
+
// TODO(burdon): Factor out as extension/hook.
|
|
80
87
|
const { awareness, peer } = model;
|
|
81
88
|
useEffect(() => {
|
|
82
89
|
if (awareness && peer) {
|
|
@@ -88,30 +95,35 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
88
95
|
}
|
|
89
96
|
}, [awareness, peer, themeMode]);
|
|
90
97
|
|
|
91
|
-
// TODO(burdon):
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
// TODO(burdon): Factor out as extension/hook.
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (view && comments !== undefined) {
|
|
101
|
+
view.dispatch({
|
|
102
|
+
effects: setCommentRange.of({ model, comments }),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}, [view, comments]);
|
|
96
106
|
|
|
97
107
|
useEffect(() => {
|
|
98
|
-
if (!
|
|
108
|
+
if (!model || !rootRef.current) {
|
|
99
109
|
return;
|
|
100
110
|
}
|
|
101
111
|
|
|
112
|
+
// https://codemirror.net/docs/ref/#state.EditorStateConfig
|
|
102
113
|
const state = EditorState.create({
|
|
103
114
|
doc: model.text(),
|
|
115
|
+
// TODO(burdon): Composer should store and set selection when switching documents.
|
|
116
|
+
selection,
|
|
104
117
|
extensions: [
|
|
105
118
|
readonly && EditorState.readOnly.of(readonly),
|
|
106
119
|
|
|
107
|
-
// TODO(burdon): Factor out VIM mode?
|
|
120
|
+
// TODO(burdon): Factor out VIM mode? (manage via MarkdownPlugin).
|
|
108
121
|
editorMode === 'vim' && vim(),
|
|
109
122
|
|
|
110
123
|
// Theme.
|
|
111
|
-
// TODO(burdon): Make
|
|
124
|
+
// TODO(burdon): Make theme configurable.
|
|
112
125
|
EditorView.baseTheme(defaultTheme),
|
|
113
|
-
EditorView.theme(
|
|
114
|
-
// TODO(burdon): themeMode doesn't change in storybooks.
|
|
126
|
+
EditorView.theme(theme ?? {}),
|
|
115
127
|
EditorView.darkTheme.of(themeMode === 'dark'),
|
|
116
128
|
|
|
117
129
|
// Storage and replication.
|
|
@@ -124,39 +136,37 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
124
136
|
|
|
125
137
|
// NOTE: This repaints the editor.
|
|
126
138
|
// If the new state is derived from the old state, it will likely not be visible other than the cursor resetting.
|
|
127
|
-
// Ideally this should not
|
|
139
|
+
// Ideally this should not happen except when changing between text objects.
|
|
128
140
|
view?.destroy();
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}),
|
|
139
|
-
);
|
|
140
|
-
setState(state);
|
|
141
|
+
const newView = new EditorView({
|
|
142
|
+
parent: rootRef.current,
|
|
143
|
+
state,
|
|
144
|
+
// NOTE: Uncomment to spy on all transactions.
|
|
145
|
+
// https://codemirror.net/docs/ref/#view.EditorView.dispatch
|
|
146
|
+
// dispatch: (transaction, view) => {
|
|
147
|
+
// view.update([transaction]);
|
|
148
|
+
// },
|
|
149
|
+
});
|
|
141
150
|
|
|
151
|
+
setView(newView);
|
|
142
152
|
return () => {
|
|
143
|
-
|
|
144
|
-
setView(
|
|
145
|
-
setState(undefined);
|
|
153
|
+
newView?.destroy();
|
|
154
|
+
setView(null);
|
|
146
155
|
};
|
|
147
|
-
}, [
|
|
156
|
+
}, [rootRef, model, readonly, editorMode, themeMode]);
|
|
148
157
|
|
|
149
158
|
const handleKeyUp = useCallback(
|
|
150
159
|
(event: KeyboardEvent) => {
|
|
151
160
|
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
152
161
|
switch (key) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
162
|
+
// TODO(burdon): Is this required (for vim mode?)
|
|
163
|
+
// case 'Enter': {
|
|
164
|
+
// view?.contentDOM.focus();
|
|
165
|
+
// break;
|
|
166
|
+
// }
|
|
157
167
|
|
|
158
168
|
case 'Escape': {
|
|
159
|
-
editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey) &&
|
|
169
|
+
editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey) && rootRef.current?.focus();
|
|
160
170
|
break;
|
|
161
171
|
}
|
|
162
172
|
}
|
|
@@ -167,42 +177,46 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
167
177
|
return (
|
|
168
178
|
<div
|
|
169
179
|
key={model.id}
|
|
170
|
-
|
|
180
|
+
role='none'
|
|
171
181
|
tabIndex={0}
|
|
172
|
-
{...slots?.root}
|
|
173
|
-
{...(editorMode !== 'vim' && tabsterDOMAttribute)}
|
|
174
182
|
onKeyUp={handleKeyUp}
|
|
183
|
+
{...slots.root}
|
|
184
|
+
{...(editorMode !== 'vim' && tabsterDOMAttribute)}
|
|
185
|
+
ref={rootRef}
|
|
175
186
|
/>
|
|
176
187
|
);
|
|
177
188
|
},
|
|
178
189
|
);
|
|
179
190
|
|
|
180
|
-
export const TextEditor = forwardRef<
|
|
181
|
-
({ readonly, extensions = [],
|
|
191
|
+
export const TextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
192
|
+
({ readonly, placeholder, extensions = [], theme = textTheme, slots, ...props }, forwardedRef) => {
|
|
182
193
|
const { themeMode } = useThemeContext();
|
|
183
|
-
const
|
|
194
|
+
const updatedSlots = defaultsDeep({}, slots, defaultTextSlots);
|
|
184
195
|
return (
|
|
185
196
|
<BaseTextEditor
|
|
186
197
|
ref={forwardedRef}
|
|
187
198
|
readonly={readonly}
|
|
188
|
-
extensions={[basicBundle({ readonly, themeMode, placeholder
|
|
189
|
-
|
|
199
|
+
extensions={[basicBundle({ readonly, themeMode, placeholder }), ...extensions]}
|
|
200
|
+
theme={theme}
|
|
201
|
+
slots={updatedSlots}
|
|
190
202
|
{...props}
|
|
191
203
|
/>
|
|
192
204
|
);
|
|
193
205
|
},
|
|
194
206
|
);
|
|
195
207
|
|
|
196
|
-
|
|
197
|
-
|
|
208
|
+
// TODO(burdon): Remove (Just provide bundle, slots).
|
|
209
|
+
export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
|
|
210
|
+
({ readonly, placeholder, extensions = [], theme = markdownTheme, slots, ...props }, forwardedRef) => {
|
|
198
211
|
const { themeMode } = useThemeContext();
|
|
199
|
-
const
|
|
212
|
+
const updatedSlots = defaultsDeep({}, slots, defaultMarkdownSlots);
|
|
200
213
|
return (
|
|
201
214
|
<BaseTextEditor
|
|
202
215
|
ref={forwardedRef}
|
|
203
216
|
readonly={readonly}
|
|
204
|
-
extensions={[markdownBundle({ readonly, themeMode, placeholder
|
|
205
|
-
|
|
217
|
+
extensions={[markdownBundle({ readonly, themeMode, placeholder }), ...extensions]}
|
|
218
|
+
theme={theme}
|
|
219
|
+
slots={updatedSlots}
|
|
206
220
|
{...props}
|
|
207
221
|
/>
|
|
208
222
|
);
|
|
@@ -211,20 +225,14 @@ export const MarkdownEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
211
225
|
|
|
212
226
|
export const defaultSlots: TextEditorSlots = {
|
|
213
227
|
root: {
|
|
214
|
-
className: mx('p-2', inputSurface),
|
|
228
|
+
className: mx('w-full p-2 overflow-y-auto', inputSurface),
|
|
215
229
|
},
|
|
216
230
|
};
|
|
217
231
|
|
|
218
232
|
export const defaultTextSlots: TextEditorSlots = {
|
|
219
233
|
...defaultSlots,
|
|
220
|
-
editor: {
|
|
221
|
-
theme: textTheme,
|
|
222
|
-
},
|
|
223
234
|
};
|
|
224
235
|
|
|
225
236
|
export const defaultMarkdownSlots: TextEditorSlots = {
|
|
226
237
|
...defaultSlots,
|
|
227
|
-
editor: {
|
|
228
|
-
theme: markdownTheme,
|
|
229
|
-
},
|
|
230
238
|
};
|
|
@@ -2,19 +2,29 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
5
|
+
import '@dxosTheme';
|
|
6
|
+
|
|
6
7
|
import { BroadcastChannelNetworkAdapter } from '@automerge/automerge-repo-network-broadcastchannel';
|
|
7
8
|
import { EditorView } from '@codemirror/view';
|
|
9
|
+
import '@preact/signals-react'; // Register react integration
|
|
8
10
|
import { basicSetup } from 'codemirror';
|
|
9
11
|
import get from 'lodash.get';
|
|
10
12
|
import React, { useEffect, useRef, useState } from 'react';
|
|
11
13
|
|
|
12
|
-
// TODO(burdon): Why separate imports?
|
|
13
14
|
import { type Prop } from '@dxos/automerge/automerge';
|
|
14
|
-
import { type DocHandle
|
|
15
|
+
import { Repo, type DocHandle } from '@dxos/automerge/automerge-repo';
|
|
16
|
+
import { Filter, setGlobalAutomergePreference } from '@dxos/echo-schema';
|
|
17
|
+
import { type PublicKey } from '@dxos/keys';
|
|
18
|
+
import { Expando, TextObject, useSpace } from '@dxos/react-client/echo';
|
|
19
|
+
import { useIdentity } from '@dxos/react-client/halo';
|
|
20
|
+
import { ClientRepeater } from '@dxos/react-client/testing';
|
|
21
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
15
22
|
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
23
|
+
import { MarkdownEditor } from './TextEditor';
|
|
24
|
+
import { type IDocHandle, automerge, awareness } from '../../extensions';
|
|
25
|
+
import { useTextModel } from '../../hooks';
|
|
26
|
+
|
|
27
|
+
// TODO(burdon): Move to components.
|
|
18
28
|
|
|
19
29
|
type EditorProps = {
|
|
20
30
|
handle: IDocHandle;
|
|
@@ -28,7 +38,7 @@ const Editor = ({ handle, path }: EditorProps) => {
|
|
|
28
38
|
useEffect(() => {
|
|
29
39
|
const view = (editorRoot.current = new EditorView({
|
|
30
40
|
doc: get(handle.docSync()!, path),
|
|
31
|
-
extensions: [basicSetup,
|
|
41
|
+
extensions: [basicSetup, automerge({ handle, path }), awareness()],
|
|
32
42
|
parent: containerRef.current as any,
|
|
33
43
|
}));
|
|
34
44
|
|
|
@@ -85,8 +95,57 @@ const Story = () => {
|
|
|
85
95
|
export default {
|
|
86
96
|
title: 'react-ui-editor/Automerge',
|
|
87
97
|
component: Editor,
|
|
98
|
+
render: () => <Story />,
|
|
88
99
|
};
|
|
89
100
|
|
|
90
|
-
export const Default = {
|
|
91
|
-
|
|
101
|
+
export const Default = {};
|
|
102
|
+
|
|
103
|
+
const EchoStory = ({ id, spaceKey }: { id: number; spaceKey: PublicKey }) => {
|
|
104
|
+
const identity = useIdentity();
|
|
105
|
+
const space = useSpace(spaceKey);
|
|
106
|
+
// TODO(dmaretskyi): useQuery doesn't work.
|
|
107
|
+
const [obj] = space?.db.query(Filter.from({ type: 'test' })).objects ?? [];
|
|
108
|
+
|
|
109
|
+
const model = useTextModel({
|
|
110
|
+
text: obj?.content,
|
|
111
|
+
identity,
|
|
112
|
+
space,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (!model) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
// <div className={mx(fixedInsetFlexLayout, groupSurface)}>
|
|
121
|
+
<div className='flex justify-center overflow-y-scroll'>
|
|
122
|
+
<div className='flex flex-col w-[800px] py-16'>
|
|
123
|
+
<MarkdownEditor model={model} />
|
|
124
|
+
<div className='flex shrink-0 h-[300px]'></div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
// </div>
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const WithEcho = {
|
|
132
|
+
render: () => {
|
|
133
|
+
setGlobalAutomergePreference(true);
|
|
134
|
+
return (
|
|
135
|
+
<ClientRepeater
|
|
136
|
+
count={2}
|
|
137
|
+
createSpace
|
|
138
|
+
onCreateSpace={async (space) => {
|
|
139
|
+
space.db.add(
|
|
140
|
+
new Expando({
|
|
141
|
+
type: 'test',
|
|
142
|
+
content: new TextObject('Hello world!'),
|
|
143
|
+
}),
|
|
144
|
+
);
|
|
145
|
+
}}
|
|
146
|
+
Component={EchoStory}
|
|
147
|
+
/>
|
|
148
|
+
);
|
|
149
|
+
},
|
|
150
|
+
decorators: [withTheme],
|
|
92
151
|
};
|
|
@@ -17,7 +17,8 @@ import { type Identity } from '@dxos/react-client/halo';
|
|
|
17
17
|
import { joinCommonSpace, TestBuilder, textGenerator } from '@dxos/react-client/testing';
|
|
18
18
|
import { YText } from '@dxos/text-model';
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { YAwarenessProvider } from '../../hooks';
|
|
21
|
+
import { cursorColor } from '../../styles';
|
|
21
22
|
import { EditorDocument, types as schema } from '../../testing';
|
|
22
23
|
|
|
23
24
|
export default {
|
|
@@ -35,7 +36,7 @@ const updateEditor = async (id: number, editor: HTMLDivElement, identity: Identi
|
|
|
35
36
|
return;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
const { awareness } = new
|
|
39
|
+
const { awareness } = new YAwarenessProvider({ space, doc, channel: `yjs.awareness.${text.id}` });
|
|
39
40
|
awareness.setLocalStateField('user', {
|
|
40
41
|
name: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
|
|
41
42
|
// TODO(wittjosiah): Pick colours from theme based on identity key.
|