@dxos/react-ui-editor 0.3.11-main.008b7c6 → 0.3.11-main.0252f93
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 +1510 -1369
- 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 +16 -14
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +20 -17
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +6 -6
- package/dist/types/src/components/TextEditor/TextEditor.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/Toolbar/Toolbar.d.ts +9 -0
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +14 -0
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
- package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
- package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/defs.d.ts +9 -11
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/semaphore.d.ts +8 -12
- package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts +2 -2
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/awareness.d.ts +2 -2
- package/dist/types/src/extensions/awareness.d.ts.map +1 -1
- package/dist/types/src/extensions/basic.d.ts +4 -5
- package/dist/types/src/extensions/basic.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts +37 -20
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/{util → extensions}/cursor.d.ts +10 -2
- package/dist/types/src/extensions/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/index.d.ts +2 -8
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts +3 -6
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/formatting.d.ts +11 -0
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
- package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/index.d.ts +8 -0
- package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
- package/dist/types/src/extensions/mention.d.ts.map +1 -1
- 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/hooks/awareness-provider.d.ts +7 -4
- package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -1
- package/dist/types/src/hooks/defs.d.ts +25 -0
- package/dist/types/src/hooks/defs.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +3 -2
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
- package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts +17 -0
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextModel.d.ts +2 -32
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts +2 -2
- 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/themes/default.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +7 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/package.json +47 -37
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +84 -91
- package/src/components/TextEditor/TextEditor.stories.tsx +12 -46
- package/src/components/TextEditor/TextEditor.tsx +131 -95
- package/src/components/TextEditor/comments.stories.tsx +357 -0
- package/src/components/Toolbar/Toolbar.stories.tsx +86 -0
- package/src/components/Toolbar/Toolbar.tsx +115 -0
- package/src/extensions/autocomplete.ts +23 -19
- package/src/extensions/automerge/automerge.spec.tsx +76 -0
- package/src/extensions/automerge/automerge.test.ts +13 -0
- package/src/extensions/automerge/automerge.ts +55 -46
- package/src/extensions/automerge/cursor.ts +5 -4
- package/src/extensions/automerge/defs.ts +13 -34
- package/src/extensions/automerge/semaphore.ts +33 -32
- package/src/extensions/automerge/update-automerge.ts +14 -8
- package/src/extensions/automerge/update-codemirror.ts +5 -5
- package/src/extensions/awareness.ts +43 -49
- package/src/extensions/basic.ts +22 -17
- package/src/extensions/comments.ts +323 -268
- package/src/extensions/cursor.ts +50 -0
- package/src/extensions/index.ts +2 -8
- package/src/extensions/markdown/bundle.ts +20 -30
- package/src/extensions/markdown/code.ts +142 -0
- package/src/extensions/markdown/formatting.ts +159 -0
- package/src/extensions/markdown/heading.ts +59 -0
- package/src/extensions/markdown/highlight.ts +11 -26
- package/src/extensions/markdown/hr.ts +71 -0
- package/src/extensions/markdown/image.ts +87 -0
- package/src/extensions/markdown/index.ts +8 -0
- package/src/extensions/{link.ts → markdown/link.ts} +87 -86
- package/src/extensions/{table.ts → markdown/table.ts} +10 -4
- package/src/extensions/markdown/tasklist.ts +110 -0
- package/src/extensions/mention.ts +9 -2
- package/src/extensions/outliner.ts +12 -0
- package/src/hooks/awareness-provider.ts +24 -20
- package/src/hooks/defs.ts +42 -0
- package/src/hooks/index.ts +4 -2
- package/src/hooks/useActionHandler.ts +45 -0
- package/src/hooks/useTextEditor.ts +29 -0
- package/src/hooks/useTextModel.ts +50 -59
- package/src/index.ts +3 -0
- package/src/styles/markdown.ts +9 -12
- package/src/styles/tokens.ts +3 -0
- package/src/themes/default.ts +50 -79
- package/src/util.ts +41 -0
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts +0 -12
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
- package/dist/types/src/extensions/code.d.ts.map +0 -1
- package/dist/types/src/extensions/hr.d.ts.map +0 -1
- package/dist/types/src/extensions/image.d.ts.map +0 -1
- package/dist/types/src/extensions/link.d.ts.map +0 -1
- package/dist/types/src/extensions/mermaid.d.ts +0 -3
- package/dist/types/src/extensions/mermaid.d.ts.map +0 -1
- package/dist/types/src/extensions/table.d.ts.map +0 -1
- package/dist/types/src/extensions/tasklist.d.ts.map +0 -1
- package/dist/types/src/extensions/yjs/cursor.d.ts +0 -4
- package/dist/types/src/extensions/yjs/cursor.d.ts.map +0 -1
- package/dist/types/src/extensions/yjs/index.d.ts +0 -2
- package/dist/types/src/extensions/yjs/index.d.ts.map +0 -1
- package/dist/types/src/extensions/yjs/yjs.d.ts +0 -4
- package/dist/types/src/extensions/yjs/yjs.d.ts.map +0 -1
- package/dist/types/src/extensions/yjs/yjs.test.d.ts +0 -2
- package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge.d.ts +0 -3
- package/dist/types/src/hooks/automerge.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs.d.ts +0 -27
- package/dist/types/src/hooks/yjs.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -3
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
- package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
- package/dist/types/src/testing/proto/index.d.ts +0 -2
- package/dist/types/src/testing/proto/index.d.ts.map +0 -1
- package/dist/types/src/testing/replicator.d.ts +0 -45
- package/dist/types/src/testing/replicator.d.ts.map +0 -1
- package/dist/types/src/util/cursor.d.ts.map +0 -1
- package/dist/types/src/util/index.d.ts +0 -3
- package/dist/types/src/util/index.d.ts.map +0 -1
- package/dist/types/src/util/util.d.ts +0 -5
- package/dist/types/src/util/util.d.ts.map +0 -1
- package/src/components/TextEditor/codemirror.stories.ts +0 -115
- package/src/components/TextEditor/yjs.stories.tsx +0 -56
- package/src/extensions/code.ts +0 -99
- package/src/extensions/hr.ts +0 -74
- package/src/extensions/image.ts +0 -69
- package/src/extensions/mermaid.ts +0 -11
- package/src/extensions/tasklist.ts +0 -121
- package/src/extensions/yjs/cursor.ts +0 -21
- package/src/extensions/yjs/index.ts +0 -5
- package/src/extensions/yjs/yjs.test.ts +0 -35
- package/src/extensions/yjs/yjs.ts +0 -16
- package/src/hooks/automerge.ts +0 -52
- package/src/hooks/yjs.ts +0 -178
- package/src/testing/index.ts +0 -6
- package/src/testing/proto/gen/schema.ts +0 -49
- package/src/testing/proto/index.ts +0 -5
- package/src/testing/proto/schema.proto +0 -15
- package/src/testing/replicator.ts +0 -184
- package/src/util/cursor.ts +0 -29
- package/src/util/index.ts +0 -6
- package/src/util/util.ts +0 -18
- /package/dist/types/src/extensions/{code.d.ts → markdown/code.d.ts} +0 -0
- /package/dist/types/src/extensions/{hr.d.ts → markdown/hr.d.ts} +0 -0
- /package/dist/types/src/extensions/{image.d.ts → markdown/image.d.ts} +0 -0
- /package/dist/types/src/extensions/{link.d.ts → markdown/link.d.ts} +0 -0
- /package/dist/types/src/extensions/{table.d.ts → markdown/table.d.ts} +0 -0
- /package/dist/types/src/extensions/{tasklist.d.ts → markdown/tasklist.d.ts} +0 -0
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { EditorState, type Extension } from '@codemirror/state';
|
|
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, {
|
|
@@ -14,18 +14,22 @@ import React, {
|
|
|
14
14
|
useCallback,
|
|
15
15
|
useEffect,
|
|
16
16
|
useImperativeHandle,
|
|
17
|
+
useRef,
|
|
17
18
|
useState,
|
|
18
19
|
} from 'react';
|
|
19
20
|
|
|
20
|
-
import {
|
|
21
|
+
import { log } from '@dxos/log';
|
|
21
22
|
import { useThemeContext } from '@dxos/react-ui';
|
|
22
|
-
import {
|
|
23
|
+
import { attentionSurface, mx } from '@dxos/react-ui-theme';
|
|
24
|
+
import { isNotFalsy } from '@dxos/util';
|
|
23
25
|
|
|
24
|
-
import { basicBundle, markdownBundle
|
|
25
|
-
import { type
|
|
26
|
+
import { basicBundle, markdownBundle } from '../../extensions';
|
|
27
|
+
import { type EditorModel } from '../../hooks';
|
|
26
28
|
import { type ThemeStyles } from '../../styles';
|
|
27
29
|
import { defaultTheme, markdownTheme, textTheme } from '../../themes';
|
|
30
|
+
import { logChanges } from '../../util';
|
|
28
31
|
|
|
32
|
+
// TODO(burdon): Change to enum?
|
|
29
33
|
export const EditorModes = ['default', 'vim'] as const;
|
|
30
34
|
export type EditorMode = (typeof EditorModes)[number];
|
|
31
35
|
|
|
@@ -34,133 +38,164 @@ export type CursorInfo = {
|
|
|
34
38
|
to: number;
|
|
35
39
|
line: number;
|
|
36
40
|
lines: number;
|
|
41
|
+
length: number;
|
|
37
42
|
after?: string;
|
|
38
43
|
};
|
|
39
44
|
|
|
40
|
-
export type TextEditorRef = {
|
|
41
|
-
root: HTMLDivElement | null;
|
|
42
|
-
state?: EditorState;
|
|
43
|
-
view?: EditorView;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
45
|
export type TextEditorSlots = {
|
|
47
46
|
root?: Omit<ComponentProps<'div'>, 'ref'>;
|
|
48
47
|
editor?: {
|
|
49
48
|
className?: string;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
theme?: ThemeStyles;
|
|
49
|
+
};
|
|
50
|
+
content?: {
|
|
51
|
+
className?: string;
|
|
54
52
|
};
|
|
55
53
|
};
|
|
56
54
|
|
|
55
|
+
// TODO(burdon): Spellcheck?
|
|
57
56
|
export type TextEditorProps = {
|
|
58
|
-
model: EditorModel;
|
|
57
|
+
model: EditorModel; // TODO(burdon): Optional (e.g., just provide content if readonly).
|
|
59
58
|
readonly?: boolean; // TODO(burdon): Move into model.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
autoFocus?: boolean;
|
|
60
|
+
lineWrapping?: boolean;
|
|
61
|
+
scrollTo?: StateEffect<any>; // TODO(burdon): Restore scroll position: scrollTo EditorView.scrollSnapshot().
|
|
62
|
+
selection?: { anchor: number; head?: number };
|
|
63
|
+
editorMode?: EditorMode; // TODO(burdon): Factor out.
|
|
64
|
+
placeholder?: string;
|
|
65
|
+
theme?: ThemeStyles;
|
|
63
66
|
slots?: TextEditorSlots;
|
|
67
|
+
extensions?: Extension[];
|
|
68
|
+
debug?: boolean;
|
|
64
69
|
};
|
|
65
70
|
|
|
66
71
|
/**
|
|
67
72
|
* Base text editor.
|
|
68
|
-
* NOTE: Rather than adding properties, try to create extensions that can be reused.
|
|
69
73
|
*/
|
|
70
|
-
export const BaseTextEditor = forwardRef<
|
|
71
|
-
(
|
|
74
|
+
export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
75
|
+
(
|
|
76
|
+
{
|
|
77
|
+
model,
|
|
78
|
+
readonly,
|
|
79
|
+
autoFocus,
|
|
80
|
+
scrollTo,
|
|
81
|
+
selection,
|
|
82
|
+
editorMode,
|
|
83
|
+
theme,
|
|
84
|
+
slots = defaultSlots,
|
|
85
|
+
extensions = [],
|
|
86
|
+
debug,
|
|
87
|
+
},
|
|
88
|
+
forwardedRef,
|
|
89
|
+
) => {
|
|
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' });
|
|
72
96
|
const { themeMode } = useThemeContext();
|
|
73
|
-
const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
|
|
74
|
-
const [root, setRoot] = useState<HTMLDivElement | null>(null);
|
|
75
|
-
const [{ state = undefined, view = undefined } = {}, setEditor] = useState<
|
|
76
|
-
{ state?: EditorState; view?: EditorView } | undefined
|
|
77
|
-
>();
|
|
78
|
-
useImperativeHandle(forwardedRef, () => ({ root, state, view }), [view, state, root]);
|
|
79
|
-
|
|
80
|
-
// TODO(burdon): Factor out as extension.
|
|
81
|
-
const { awareness, peer } = model;
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
if (awareness && peer) {
|
|
84
|
-
awareness.setLocalStateField('user', {
|
|
85
|
-
name: peer.name ?? generateName(peer.id),
|
|
86
|
-
color: getColorForValue({ value: peer.id, type: 'color' }),
|
|
87
|
-
colorLight: getColorForValue({ value: peer.id, themeMode, type: 'highlight' }),
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
}, [awareness, peer, themeMode]);
|
|
91
97
|
|
|
92
|
-
|
|
98
|
+
const rootRef = useRef<HTMLDivElement>(null);
|
|
99
|
+
const [view, setView] = useState<EditorView | null>(null);
|
|
100
|
+
|
|
101
|
+
// The view ref can be used to focus the editor.
|
|
102
|
+
// NOTE: This does not cause the parent to re-render, so the ref is not available immediately.
|
|
103
|
+
useImperativeHandle<EditorView | null, EditorView | null>(forwardedRef, () => view, [view]);
|
|
104
|
+
|
|
105
|
+
// Set focus.
|
|
93
106
|
useEffect(() => {
|
|
94
|
-
if (
|
|
95
|
-
view
|
|
96
|
-
effects: setCommentRange.of({ model, comments }),
|
|
97
|
-
});
|
|
107
|
+
if (autoFocus) {
|
|
108
|
+
view?.focus();
|
|
98
109
|
}
|
|
99
|
-
}, [view,
|
|
110
|
+
}, [view, autoFocus]);
|
|
100
111
|
|
|
112
|
+
// Create editor state and view.
|
|
113
|
+
// The view is recreated if the model or extensions are changed.
|
|
101
114
|
useEffect(() => {
|
|
102
|
-
if (!
|
|
115
|
+
if (!model || !rootRef.current) {
|
|
103
116
|
return;
|
|
104
117
|
}
|
|
105
118
|
|
|
106
|
-
//
|
|
119
|
+
//
|
|
120
|
+
// EditorState
|
|
107
121
|
// https://codemirror.net/docs/ref/#state.EditorStateConfig
|
|
122
|
+
//
|
|
108
123
|
const state = EditorState.create({
|
|
109
124
|
doc: model.text(),
|
|
125
|
+
selection,
|
|
110
126
|
extensions: [
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
127
|
+
// TODO(burdon): Doesn't catch errors in keymap functions.
|
|
128
|
+
EditorView.exceptionSink.of((err) => {
|
|
129
|
+
log.catch(err);
|
|
130
|
+
}),
|
|
115
131
|
|
|
116
132
|
// Theme.
|
|
117
|
-
// TODO(burdon): Make
|
|
133
|
+
// TODO(burdon): Make configurable.
|
|
118
134
|
EditorView.baseTheme(defaultTheme),
|
|
119
|
-
EditorView.theme(
|
|
135
|
+
EditorView.theme(theme ?? {}),
|
|
120
136
|
EditorView.darkTheme.of(themeMode === 'dark'),
|
|
137
|
+
EditorView.editorAttributes.of({ class: slots.editor?.className ?? '' }),
|
|
138
|
+
EditorView.contentAttributes.of({ class: slots.content?.className ?? '' }),
|
|
139
|
+
|
|
140
|
+
// State.
|
|
141
|
+
EditorView.editable.of(!readonly),
|
|
142
|
+
EditorState.readOnly.of(!!readonly),
|
|
121
143
|
|
|
122
144
|
// Storage and replication.
|
|
145
|
+
// NOTE: This must come before user extensions.
|
|
123
146
|
model.extension,
|
|
124
147
|
|
|
148
|
+
// TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
|
|
149
|
+
editorMode === 'vim' && vim(),
|
|
150
|
+
|
|
125
151
|
// Custom.
|
|
126
152
|
...extensions,
|
|
127
|
-
].filter(
|
|
153
|
+
].filter(isNotFalsy),
|
|
128
154
|
});
|
|
129
155
|
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
156
|
+
//
|
|
157
|
+
// EditorView
|
|
158
|
+
// https://codemirror.net/docs/ref/#view.EditorViewConfig
|
|
159
|
+
//
|
|
160
|
+
const newView = new EditorView({
|
|
135
161
|
state,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
162
|
+
parent: rootRef.current,
|
|
163
|
+
scrollTo,
|
|
164
|
+
// NOTE: Uncomment to debug/monitor all transactions.
|
|
165
|
+
// https://codemirror.net/docs/ref/#view.EditorView.dispatch
|
|
166
|
+
dispatchTransactions: (trs, view) => {
|
|
167
|
+
if (debug) {
|
|
168
|
+
logChanges(trs);
|
|
169
|
+
}
|
|
170
|
+
view.update(trs);
|
|
171
|
+
},
|
|
145
172
|
});
|
|
146
173
|
|
|
174
|
+
view?.destroy();
|
|
175
|
+
setView(newView);
|
|
176
|
+
|
|
147
177
|
return () => {
|
|
148
|
-
|
|
149
|
-
|
|
178
|
+
newView?.destroy();
|
|
179
|
+
setView(null);
|
|
150
180
|
};
|
|
151
|
-
}, [
|
|
181
|
+
}, [rootRef, model, readonly, editorMode, themeMode]);
|
|
152
182
|
|
|
183
|
+
// Handles tab/focus.
|
|
184
|
+
// Pressing Escape focuses the outer div (to support tab navigation); pressing Enter refocuses the editor.
|
|
185
|
+
// TODO(burdon): Convert to keymap?
|
|
153
186
|
const handleKeyUp = useCallback(
|
|
154
187
|
(event: KeyboardEvent) => {
|
|
155
188
|
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
156
189
|
switch (key) {
|
|
157
190
|
case 'Enter': {
|
|
158
|
-
view?.
|
|
191
|
+
view?.focus();
|
|
159
192
|
break;
|
|
160
193
|
}
|
|
161
194
|
|
|
162
195
|
case 'Escape': {
|
|
163
|
-
editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey)
|
|
196
|
+
if (editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey)) {
|
|
197
|
+
view?.focus();
|
|
198
|
+
}
|
|
164
199
|
break;
|
|
165
200
|
}
|
|
166
201
|
}
|
|
@@ -171,42 +206,45 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
171
206
|
return (
|
|
172
207
|
<div
|
|
173
208
|
key={model.id}
|
|
174
|
-
|
|
209
|
+
role='none'
|
|
175
210
|
tabIndex={0}
|
|
176
|
-
{...slots
|
|
177
|
-
{...(editorMode !== 'vim' && tabsterDOMAttribute)}
|
|
211
|
+
{...slots.root}
|
|
212
|
+
// {...(editorMode !== 'vim' && tabsterDOMAttribute)}
|
|
178
213
|
onKeyUp={handleKeyUp}
|
|
214
|
+
ref={rootRef}
|
|
179
215
|
/>
|
|
180
216
|
);
|
|
181
217
|
},
|
|
182
218
|
);
|
|
183
219
|
|
|
184
|
-
export const TextEditor = forwardRef<
|
|
185
|
-
({ readonly,
|
|
220
|
+
export const TextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
221
|
+
({ readonly, placeholder, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
|
|
186
222
|
const { themeMode } = useThemeContext();
|
|
187
|
-
const
|
|
223
|
+
const updatedSlots = defaultsDeep({}, slots, defaultTextSlots);
|
|
188
224
|
return (
|
|
189
225
|
<BaseTextEditor
|
|
190
226
|
ref={forwardedRef}
|
|
191
227
|
readonly={readonly}
|
|
192
|
-
extensions={[basicBundle({
|
|
193
|
-
|
|
228
|
+
extensions={[basicBundle({ themeMode, placeholder, lineWrapping }), ...extensions]}
|
|
229
|
+
theme={theme}
|
|
230
|
+
slots={updatedSlots}
|
|
194
231
|
{...props}
|
|
195
232
|
/>
|
|
196
233
|
);
|
|
197
234
|
},
|
|
198
235
|
);
|
|
199
236
|
|
|
200
|
-
export const MarkdownEditor = forwardRef<
|
|
201
|
-
({ readonly,
|
|
237
|
+
export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
|
|
238
|
+
({ readonly, placeholder, theme = markdownTheme, slots, extensions = [], ...props }, forwardedRef) => {
|
|
202
239
|
const { themeMode } = useThemeContext();
|
|
203
|
-
const
|
|
240
|
+
const updatedSlots = defaultsDeep({}, slots, defaultMarkdownSlots);
|
|
204
241
|
return (
|
|
205
242
|
<BaseTextEditor
|
|
206
243
|
ref={forwardedRef}
|
|
207
244
|
readonly={readonly}
|
|
208
|
-
extensions={[markdownBundle({
|
|
209
|
-
|
|
245
|
+
extensions={[markdownBundle({ themeMode, placeholder }), ...extensions]}
|
|
246
|
+
theme={theme}
|
|
247
|
+
slots={updatedSlots}
|
|
210
248
|
{...props}
|
|
211
249
|
/>
|
|
212
250
|
);
|
|
@@ -215,20 +253,18 @@ export const MarkdownEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
215
253
|
|
|
216
254
|
export const defaultSlots: TextEditorSlots = {
|
|
217
255
|
root: {
|
|
218
|
-
|
|
256
|
+
// TODO(burdon): Add focusRing by default/as property?
|
|
257
|
+
className: mx('flex flex-col grow overflow-y-auto', attentionSurface),
|
|
258
|
+
},
|
|
259
|
+
editor: {
|
|
260
|
+
className: 'h-full p-2',
|
|
219
261
|
},
|
|
220
262
|
};
|
|
221
263
|
|
|
222
264
|
export const defaultTextSlots: TextEditorSlots = {
|
|
223
265
|
...defaultSlots,
|
|
224
|
-
editor: {
|
|
225
|
-
theme: textTheme,
|
|
226
|
-
},
|
|
227
266
|
};
|
|
228
267
|
|
|
229
268
|
export const defaultMarkdownSlots: TextEditorSlots = {
|
|
230
269
|
...defaultSlots,
|
|
231
|
-
editor: {
|
|
232
|
-
theme: markdownTheme,
|
|
233
|
-
},
|
|
234
270
|
};
|