@dxos/react-ui-editor 0.3.11-main.b00882f → 0.3.11-main.b3f97d8
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 +2386 -968
- 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 +1 -2
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -1
- package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/{yjs.stories.d.ts → hooks.stories.d.ts} +13 -2
- package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +29 -0
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +25 -0
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
- package/dist/types/src/components/Toolbar/index.d.ts +2 -0
- package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.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/defs.d.ts +5 -5
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/semaphore.d.ts +2 -1
- package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/awareness.d.ts +2 -4
- package/dist/types/src/extensions/awareness.d.ts.map +1 -1
- package/dist/types/src/extensions/basic.d.ts +1 -1
- package/dist/types/src/extensions/basic.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts +11 -3
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +0 -7
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts +2 -2
- 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 +57 -0
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/formatting.test.d.ts +3 -0
- package/dist/types/src/extensions/markdown/formatting.test.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/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 +1 -4
- package/dist/types/src/hooks/defs.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -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/useEditorView.d.ts +17 -0
- package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts +36 -14
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextModel.d.ts +12 -0
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- 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/themes/default.d.ts.map +1 -1
- package/package.json +39 -29
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +15 -19
- package/src/components/TextEditor/TextEditor.tsx +27 -19
- package/src/components/TextEditor/automerge.stories.tsx +1 -2
- package/src/components/TextEditor/hooks.stories.tsx +111 -0
- package/src/components/Toolbar/Toolbar.stories.tsx +102 -0
- package/src/components/Toolbar/Toolbar.tsx +245 -0
- package/src/components/Toolbar/index.ts +5 -0
- package/src/components/index.ts +1 -0
- package/src/extensions/automerge/automerge.spec.tsx +76 -0
- package/src/extensions/automerge/automerge.test.ts +13 -0
- package/src/extensions/automerge/automerge.ts +26 -11
- package/src/extensions/automerge/cursor.ts +3 -3
- package/src/extensions/automerge/defs.ts +9 -9
- package/src/extensions/automerge/semaphore.ts +17 -19
- package/src/extensions/automerge/update-automerge.ts +12 -6
- package/src/extensions/automerge/update-codemirror.ts +5 -5
- package/src/extensions/awareness.ts +21 -35
- package/src/extensions/basic.ts +15 -13
- package/src/extensions/blast.ts +4 -1
- package/src/extensions/comments.ts +126 -68
- package/src/extensions/index.ts +0 -7
- package/src/extensions/markdown/bundle.ts +9 -8
- package/src/extensions/markdown/code.ts +150 -0
- package/src/extensions/markdown/formatting.test.ts +481 -0
- package/src/extensions/markdown/formatting.ts +1198 -0
- package/src/extensions/markdown/heading.ts +59 -0
- package/src/extensions/markdown/highlight.ts +9 -15
- package/src/extensions/{hr.ts → markdown/hr.ts} +3 -4
- package/src/extensions/markdown/index.ts +8 -0
- package/src/extensions/{link.ts → markdown/link.ts} +13 -4
- package/src/extensions/{tasklist.ts → markdown/tasklist.ts} +2 -3
- package/src/hooks/awareness-provider.ts +24 -20
- package/src/hooks/defs.ts +1 -6
- package/src/hooks/index.ts +2 -2
- package/src/hooks/useActionHandler.ts +93 -0
- package/src/hooks/useEditorView.ts +29 -0
- package/src/hooks/useTextEditor.ts +143 -19
- package/src/hooks/useTextModel.ts +65 -12
- package/src/index.ts +1 -1
- package/src/styles/markdown.ts +9 -10
- package/src/themes/default.ts +11 -8
- 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/comments.stories.d.ts +0 -26
- package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +0 -1
- 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/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 -4
- package/dist/types/src/hooks/automerge.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs.d.ts +0 -28
- 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/src/components/TextEditor/codemirror.stories.ts +0 -115
- package/src/components/TextEditor/comments.stories.tsx +0 -357
- package/src/components/TextEditor/yjs.stories.tsx +0 -56
- package/src/extensions/code.ts +0 -104
- package/src/extensions/yjs/cursor.ts +0 -22
- 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 -53
- package/src/hooks/yjs.ts +0 -157
- 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/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
- /package/src/extensions/{image.ts → markdown/image.ts} +0 -0
- /package/src/extensions/{table.ts → markdown/table.ts} +0 -0
|
@@ -19,11 +19,14 @@ import { withTheme } from '@dxos/storybook-utils';
|
|
|
19
19
|
|
|
20
20
|
import { MarkdownEditor, type TextEditorProps } from './TextEditor';
|
|
21
21
|
import {
|
|
22
|
+
type CommentsOptions,
|
|
23
|
+
type LinkOptions,
|
|
22
24
|
autocomplete,
|
|
23
25
|
blast,
|
|
24
26
|
code,
|
|
25
27
|
comments,
|
|
26
28
|
defaultOptions,
|
|
29
|
+
heading,
|
|
27
30
|
hr,
|
|
28
31
|
image,
|
|
29
32
|
link,
|
|
@@ -31,16 +34,11 @@ import {
|
|
|
31
34
|
table,
|
|
32
35
|
tasklist,
|
|
33
36
|
typewriter,
|
|
34
|
-
type CommentsOptions,
|
|
35
|
-
type LinkOptions,
|
|
36
37
|
useComments,
|
|
38
|
+
formatting,
|
|
37
39
|
} from '../../extensions';
|
|
38
40
|
import { type Comment, useTextModel } from '../../hooks';
|
|
39
41
|
|
|
40
|
-
// Extensions:
|
|
41
|
-
// TODO(burdon): Table of contents.
|
|
42
|
-
// TODO(burdon): Front-matter
|
|
43
|
-
|
|
44
42
|
faker.seed(101);
|
|
45
43
|
|
|
46
44
|
const str = (...lines: string[]) => lines.join('\n');
|
|
@@ -82,7 +80,7 @@ const text = {
|
|
|
82
80
|
'## Code',
|
|
83
81
|
'',
|
|
84
82
|
'```',
|
|
85
|
-
'
|
|
83
|
+
'$ ls -las',
|
|
86
84
|
'```',
|
|
87
85
|
'',
|
|
88
86
|
'```tsx',
|
|
@@ -213,11 +211,10 @@ const onRenderLink: LinkOptions['onRender'] = (el, url) => {
|
|
|
213
211
|
type StoryProps = {
|
|
214
212
|
text?: string;
|
|
215
213
|
comments?: Comment[];
|
|
216
|
-
automerge?: boolean;
|
|
217
214
|
} & Pick<TextEditorProps, 'readonly' | 'placeholder' | 'slots' | 'extensions'>;
|
|
218
215
|
|
|
219
|
-
const Story = ({ text, comments,
|
|
220
|
-
const [item] = useState({ text: new TextObject(text
|
|
216
|
+
const Story = ({ text, comments, placeholder = 'New document.', ...props }: StoryProps) => {
|
|
217
|
+
const [item] = useState({ text: new TextObject(text) });
|
|
221
218
|
const view = useRef<EditorView>(null);
|
|
222
219
|
const model = useTextModel({ text: item.text });
|
|
223
220
|
useComments(view.current, comments);
|
|
@@ -248,12 +245,11 @@ const defaults = [
|
|
|
248
245
|
onSearch: (text) => links.filter(({ label }) => label.toLowerCase().includes(text.toLowerCase())),
|
|
249
246
|
}),
|
|
250
247
|
code(),
|
|
248
|
+
formatting(),
|
|
249
|
+
heading(),
|
|
251
250
|
hr(),
|
|
252
251
|
image(),
|
|
253
252
|
link({ onRender: onRenderLink, onHover: onHoverLinkTooltip }),
|
|
254
|
-
// mention({
|
|
255
|
-
// onSearch: (text) => names.filter((name) => name.toLowerCase().startsWith(text.toLowerCase())),
|
|
256
|
-
// }),
|
|
257
253
|
table(),
|
|
258
254
|
tasklist(),
|
|
259
255
|
];
|
|
@@ -290,11 +286,11 @@ export const Links = {
|
|
|
290
286
|
};
|
|
291
287
|
|
|
292
288
|
export const Code = {
|
|
293
|
-
render: () => <Story text={str(text.code, text.footer)} extensions={[code()]}
|
|
289
|
+
render: () => <Story text={str(text.code, text.footer)} extensions={[code()]} />,
|
|
294
290
|
};
|
|
295
291
|
|
|
296
292
|
export const Image = {
|
|
297
|
-
render: () => <Story text={str(text.image, text.footer)}
|
|
293
|
+
render: () => <Story text={str(text.image, text.footer)} extensions={[image()]} />,
|
|
298
294
|
};
|
|
299
295
|
|
|
300
296
|
export const Lists = {
|
|
@@ -345,18 +341,18 @@ export const Mention = {
|
|
|
345
341
|
|
|
346
342
|
export const Comments = {
|
|
347
343
|
render: () => {
|
|
348
|
-
const [
|
|
344
|
+
const [_comments, setComments] = useState<Comment[]>([]);
|
|
349
345
|
|
|
350
346
|
return (
|
|
351
347
|
<Story
|
|
352
348
|
text={str('# Comments', '', text.paragraphs, text.footer)}
|
|
353
|
-
comments={
|
|
349
|
+
comments={_comments}
|
|
354
350
|
extensions={[
|
|
355
351
|
comments({
|
|
356
352
|
onHover: onCommentsHover,
|
|
357
|
-
onCreate: (
|
|
353
|
+
onCreate: ({ cursor }) => {
|
|
358
354
|
const id = PublicKey.random().toHex();
|
|
359
|
-
|
|
355
|
+
setComments((commentRanges) => [...commentRanges, { id, cursor }]);
|
|
360
356
|
return id;
|
|
361
357
|
},
|
|
362
358
|
onSelect: (state) => {
|
|
@@ -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, {
|
|
@@ -20,9 +20,10 @@ import React, {
|
|
|
20
20
|
|
|
21
21
|
import { log } from '@dxos/log';
|
|
22
22
|
import { useThemeContext } from '@dxos/react-ui';
|
|
23
|
-
import {
|
|
23
|
+
import { focusRing, mx } from '@dxos/react-ui-theme';
|
|
24
|
+
import { isNotFalsy } from '@dxos/util';
|
|
24
25
|
|
|
25
|
-
import {
|
|
26
|
+
import { createBasicBundle, createMarkdownExtensions } from '../../extensions';
|
|
26
27
|
import { type EditorModel } from '../../hooks';
|
|
27
28
|
import { type ThemeStyles } from '../../styles';
|
|
28
29
|
import { defaultTheme, markdownTheme, textTheme } from '../../themes';
|
|
@@ -86,7 +87,12 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
86
87
|
},
|
|
87
88
|
forwardedRef,
|
|
88
89
|
) => {
|
|
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' });
|
|
90
96
|
const { themeMode } = useThemeContext();
|
|
91
97
|
|
|
92
98
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
@@ -98,14 +104,16 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
98
104
|
|
|
99
105
|
// Set focus.
|
|
100
106
|
useEffect(() => {
|
|
101
|
-
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
|
+
}
|
|
102
113
|
view?.focus();
|
|
103
114
|
}
|
|
104
115
|
}, [view, autoFocus]);
|
|
105
116
|
|
|
106
|
-
// Monitor awareness.
|
|
107
|
-
useAwareness(model);
|
|
108
|
-
|
|
109
117
|
// Create editor state and view.
|
|
110
118
|
// The view is recreated if the model or extensions are changed.
|
|
111
119
|
useEffect(() => {
|
|
@@ -135,18 +143,19 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
135
143
|
EditorView.contentAttributes.of({ class: slots.content?.className ?? '' }),
|
|
136
144
|
|
|
137
145
|
// State.
|
|
146
|
+
EditorView.editable.of(!readonly),
|
|
138
147
|
EditorState.readOnly.of(!!readonly),
|
|
139
148
|
|
|
140
149
|
// Storage and replication.
|
|
141
150
|
// NOTE: This must come before user extensions.
|
|
142
151
|
model.extension,
|
|
143
152
|
|
|
144
|
-
// TODO(burdon): Factor out (
|
|
153
|
+
// TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
|
|
145
154
|
editorMode === 'vim' && vim(),
|
|
146
155
|
|
|
147
156
|
// Custom.
|
|
148
157
|
...extensions,
|
|
149
|
-
].filter(
|
|
158
|
+
].filter(isNotFalsy),
|
|
150
159
|
});
|
|
151
160
|
|
|
152
161
|
//
|
|
@@ -178,6 +187,7 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
178
187
|
|
|
179
188
|
// Handles tab/focus.
|
|
180
189
|
// Pressing Escape focuses the outer div (to support tab navigation); pressing Enter refocuses the editor.
|
|
190
|
+
// TODO(burdon): Convert to keymap?
|
|
181
191
|
const handleKeyUp = useCallback(
|
|
182
192
|
(event: KeyboardEvent) => {
|
|
183
193
|
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
@@ -189,7 +199,7 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
189
199
|
|
|
190
200
|
case 'Escape': {
|
|
191
201
|
if (editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey)) {
|
|
192
|
-
|
|
202
|
+
view?.focus();
|
|
193
203
|
}
|
|
194
204
|
break;
|
|
195
205
|
}
|
|
@@ -203,16 +213,15 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
203
213
|
key={model.id}
|
|
204
214
|
role='none'
|
|
205
215
|
tabIndex={0}
|
|
206
|
-
onKeyUp={handleKeyUp}
|
|
207
216
|
{...slots.root}
|
|
208
|
-
{...(editorMode !== 'vim' && tabsterDOMAttribute)}
|
|
217
|
+
// {...(editorMode !== 'vim' && tabsterDOMAttribute)}
|
|
218
|
+
onKeyUp={handleKeyUp}
|
|
209
219
|
ref={rootRef}
|
|
210
220
|
/>
|
|
211
221
|
);
|
|
212
222
|
},
|
|
213
223
|
);
|
|
214
224
|
|
|
215
|
-
// TODO(burdon): Single-line/scroll.
|
|
216
225
|
export const TextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
217
226
|
({ readonly, placeholder, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
|
|
218
227
|
const { themeMode } = useThemeContext();
|
|
@@ -221,7 +230,7 @@ export const TextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
221
230
|
<BaseTextEditor
|
|
222
231
|
ref={forwardedRef}
|
|
223
232
|
readonly={readonly}
|
|
224
|
-
extensions={[
|
|
233
|
+
extensions={[createBasicBundle({ themeMode, placeholder, lineWrapping }), ...extensions]}
|
|
225
234
|
theme={theme}
|
|
226
235
|
slots={updatedSlots}
|
|
227
236
|
{...props}
|
|
@@ -238,7 +247,7 @@ export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
238
247
|
<BaseTextEditor
|
|
239
248
|
ref={forwardedRef}
|
|
240
249
|
readonly={readonly}
|
|
241
|
-
extensions={[
|
|
250
|
+
extensions={[createMarkdownExtensions({ themeMode, placeholder }), ...extensions]}
|
|
242
251
|
theme={theme}
|
|
243
252
|
slots={updatedSlots}
|
|
244
253
|
{...props}
|
|
@@ -249,11 +258,10 @@ export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
249
258
|
|
|
250
259
|
export const defaultSlots: TextEditorSlots = {
|
|
251
260
|
root: {
|
|
252
|
-
|
|
253
|
-
className: mx('flex flex-col grow overflow-y-auto', inputSurface),
|
|
261
|
+
className: mx('grow', focusRing),
|
|
254
262
|
},
|
|
255
263
|
editor: {
|
|
256
|
-
className: '
|
|
264
|
+
className: 'bs-full',
|
|
257
265
|
},
|
|
258
266
|
};
|
|
259
267
|
|
|
@@ -13,7 +13,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
|
|
13
13
|
|
|
14
14
|
import { type Prop } from '@dxos/automerge/automerge';
|
|
15
15
|
import { Repo, type DocHandle } from '@dxos/automerge/automerge-repo';
|
|
16
|
-
import { Filter
|
|
16
|
+
import { Filter } from '@dxos/echo-schema';
|
|
17
17
|
import { type PublicKey } from '@dxos/keys';
|
|
18
18
|
import { Expando, TextObject, useSpace } from '@dxos/react-client/echo';
|
|
19
19
|
import { useIdentity } from '@dxos/react-client/halo';
|
|
@@ -130,7 +130,6 @@ const EchoStory = ({ id, spaceKey }: { id: number; spaceKey: PublicKey }) => {
|
|
|
130
130
|
|
|
131
131
|
export const WithEcho = {
|
|
132
132
|
render: () => {
|
|
133
|
-
setGlobalAutomergePreference(true);
|
|
134
133
|
return (
|
|
135
134
|
<ClientRepeater
|
|
136
135
|
count={2}
|
|
@@ -0,0 +1,111 @@
|
|
|
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
|
+
useFormattingState,
|
|
25
|
+
} from '../../extensions';
|
|
26
|
+
import { createDataExtensions, createThemeExtensions, useActionHandler, useTextEditor } from '../../hooks';
|
|
27
|
+
import { markdownTheme } from '../../themes';
|
|
28
|
+
import { Toolbar } from '../Toolbar';
|
|
29
|
+
|
|
30
|
+
// TODO(burdon): Demo toolbar with hooks.
|
|
31
|
+
// TODO(burdon): Build components from hooks and adapters for model/extensions, etc.
|
|
32
|
+
// TODO(burdon): Remove BaseTextEditor.
|
|
33
|
+
// TODO(burdon): Move scrolling container layout/logic into TextEditor/MarkdownEditor components.
|
|
34
|
+
// TODO(burdon): Define keymap in composer framework format.
|
|
35
|
+
|
|
36
|
+
type StoryProps = {
|
|
37
|
+
autoFocus?: boolean;
|
|
38
|
+
placeholder?: string;
|
|
39
|
+
doc?: string;
|
|
40
|
+
readonly?: boolean;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const Story = ({ autoFocus, placeholder, doc, readonly }: StoryProps) => {
|
|
44
|
+
const { themeMode } = useThemeContext();
|
|
45
|
+
const [formattingState, trackFormatting] = useFormattingState();
|
|
46
|
+
const { parentRef, view } = useTextEditor({
|
|
47
|
+
autoFocus,
|
|
48
|
+
doc,
|
|
49
|
+
extensions: [
|
|
50
|
+
//
|
|
51
|
+
createDataExtensions({ readonly }),
|
|
52
|
+
createThemeExtensions({
|
|
53
|
+
themeMode,
|
|
54
|
+
theme: markdownTheme,
|
|
55
|
+
slots: {
|
|
56
|
+
// TODO(burdon): Document classes re base theme.
|
|
57
|
+
// Semantic tokens (e.g., replacement of input surface).
|
|
58
|
+
editor: {
|
|
59
|
+
className: 'h-full p-4 bg-white text-black dark:bg-black dark:text-white',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
// TODO(burdon): Move lineWrapping.
|
|
64
|
+
createMarkdownExtensions({ placeholder, lineWrapping: true }),
|
|
65
|
+
// TODO(burdon): Move into markdown bundle (with React callbacks).
|
|
66
|
+
code(),
|
|
67
|
+
formatting(),
|
|
68
|
+
heading(),
|
|
69
|
+
hr(),
|
|
70
|
+
image(),
|
|
71
|
+
link(),
|
|
72
|
+
table(),
|
|
73
|
+
tasklist(),
|
|
74
|
+
trackFormatting,
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const handleAction = useActionHandler(view);
|
|
79
|
+
|
|
80
|
+
// FIXME This doesn't update the state on view changes. Also not
|
|
81
|
+
// sure if view is even guaranteed to exist at this point.
|
|
82
|
+
return (
|
|
83
|
+
<div className={mx(fixedInsetFlexLayout, groupSurface)}>
|
|
84
|
+
<div className='flex h-full justify-center'>
|
|
85
|
+
<div className='flex flex-col h-full w-[800px]'>
|
|
86
|
+
<Toolbar.Root onAction={handleAction} state={formattingState}>
|
|
87
|
+
<Toolbar.Markdown />
|
|
88
|
+
</Toolbar.Root>
|
|
89
|
+
|
|
90
|
+
{/* TODO(burdon): Handle scrolling in component wrapper (like this). */}
|
|
91
|
+
<div role='none' className='h-full overflow-y-auto' ref={parentRef} />
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export default {
|
|
99
|
+
title: 'react-ui-editor/useTextEditor',
|
|
100
|
+
component: TextEditor,
|
|
101
|
+
decorators: [withTheme],
|
|
102
|
+
render: (args: StoryProps) => <Story {...args} />,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const Default = {
|
|
106
|
+
args: {
|
|
107
|
+
autoFocus: true,
|
|
108
|
+
placeholder: 'Text...',
|
|
109
|
+
doc: '# Demo\n\nThis is a document.\n\n',
|
|
110
|
+
},
|
|
111
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxosTheme';
|
|
6
|
+
|
|
7
|
+
import { faker } from '@faker-js/faker';
|
|
8
|
+
import React, { type FC, useMemo, useState } from 'react';
|
|
9
|
+
|
|
10
|
+
import { TextObject } from '@dxos/echo-schema';
|
|
11
|
+
import { PublicKey } from '@dxos/keys';
|
|
12
|
+
import { fixedInsetFlexLayout, groupSurface, mx } from '@dxos/react-ui-theme';
|
|
13
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
14
|
+
|
|
15
|
+
import { Toolbar } from './Toolbar';
|
|
16
|
+
import {
|
|
17
|
+
code,
|
|
18
|
+
comments,
|
|
19
|
+
formatting,
|
|
20
|
+
heading,
|
|
21
|
+
image,
|
|
22
|
+
table,
|
|
23
|
+
tasklist,
|
|
24
|
+
useComments,
|
|
25
|
+
useFormattingState,
|
|
26
|
+
} from '../../extensions';
|
|
27
|
+
import { type Comment, useActionHandler, useEditorView, useTextModel } from '../../hooks';
|
|
28
|
+
import { MarkdownEditor } from '../TextEditor';
|
|
29
|
+
|
|
30
|
+
faker.seed(101);
|
|
31
|
+
|
|
32
|
+
const Story: FC<{ content: string }> = ({ content }) => {
|
|
33
|
+
const [item] = useState({ text: new TextObject(content) });
|
|
34
|
+
const [_comments, setComments] = useState<Comment[]>([]);
|
|
35
|
+
const [editorRef, editorView] = useEditorView();
|
|
36
|
+
const model = useTextModel({ text: item.text });
|
|
37
|
+
const [formattingState, formattingObserver] = useFormattingState();
|
|
38
|
+
const extensions = useMemo(
|
|
39
|
+
() => [
|
|
40
|
+
code(),
|
|
41
|
+
comments({
|
|
42
|
+
onCreate: ({ cursor }) => {
|
|
43
|
+
const id = PublicKey.random().toHex();
|
|
44
|
+
setComments((comments) => [...comments, { id, cursor }]);
|
|
45
|
+
return id;
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
formatting(),
|
|
49
|
+
heading(),
|
|
50
|
+
image(),
|
|
51
|
+
table(),
|
|
52
|
+
tasklist(),
|
|
53
|
+
formattingObserver,
|
|
54
|
+
],
|
|
55
|
+
[],
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
useComments(editorView, _comments);
|
|
59
|
+
const handleAction = useActionHandler(editorView);
|
|
60
|
+
|
|
61
|
+
if (!model) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div className={mx(fixedInsetFlexLayout, groupSurface)}>
|
|
67
|
+
<div className='flex h-full justify-center'>
|
|
68
|
+
<div className='flex flex-col h-full w-[800px]'>
|
|
69
|
+
<Toolbar.Root onAction={handleAction} state={formattingState}>
|
|
70
|
+
<Toolbar.Markdown />
|
|
71
|
+
<Toolbar.Separator />
|
|
72
|
+
<Toolbar.Extended />
|
|
73
|
+
</Toolbar.Root>
|
|
74
|
+
<MarkdownEditor ref={editorRef} model={model} extensions={extensions} />
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export default {
|
|
82
|
+
title: 'react-ui-editor/Toolbar',
|
|
83
|
+
component: Toolbar,
|
|
84
|
+
render: (args: any) => <Story {...args} />,
|
|
85
|
+
decorators: [withTheme],
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const content = [
|
|
89
|
+
'Demo',
|
|
90
|
+
'',
|
|
91
|
+
'The editor supports **Markdown** styles.',
|
|
92
|
+
'',
|
|
93
|
+
faker.lorem.paragraph({ min: 5, max: 8 }),
|
|
94
|
+
'',
|
|
95
|
+
'',
|
|
96
|
+
].join('\n');
|
|
97
|
+
|
|
98
|
+
export const Default = {
|
|
99
|
+
args: {
|
|
100
|
+
content,
|
|
101
|
+
},
|
|
102
|
+
};
|