@dxos/react-ui-editor 0.3.10-main.90b298e → 0.3.10-main.c5193cc
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 +729 -153
- 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 +19 -1
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +2 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/basic.d.ts +2 -1
- package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/blast.d.ts +25 -0
- package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/comments.d.ts +1 -1
- package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/demo.d.ts +2 -1
- package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/image.d.ts +4 -0
- package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/index.d.ts +4 -0
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +2 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts +6 -2
- package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/mention.d.ts +6 -0
- package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/table.d.ts +8 -0
- package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +2 -1
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/themes/default.d.ts +5 -0
- package/dist/types/src/components/TextEditor/themes/default.d.ts.map +1 -1
- package/dist/types/src/hooks/automerge/automerge.stories.d.ts +8 -1
- package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/{components/TextEditor → hooks/yjs}/yjs.stories.d.ts +1 -1
- package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +1 -0
- package/dist/types/src/styles/markdown.d.ts +3 -2
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +101 -25
- package/src/components/TextEditor/TextEditor.tsx +13 -23
- package/src/components/TextEditor/extensions/autocomplete.ts +5 -20
- package/src/components/TextEditor/extensions/basic.ts +2 -1
- package/src/components/TextEditor/extensions/blast.ts +387 -0
- package/src/components/TextEditor/extensions/comments.ts +8 -7
- package/src/components/TextEditor/extensions/demo.ts +4 -1
- package/src/components/TextEditor/extensions/image.ts +67 -0
- package/src/components/TextEditor/extensions/index.ts +4 -0
- package/src/components/TextEditor/extensions/link.ts +16 -23
- package/src/components/TextEditor/extensions/listener.ts +3 -3
- package/src/components/TextEditor/extensions/markdown/bundle.ts +28 -17
- package/src/components/TextEditor/extensions/markdown/highlight.ts +118 -104
- package/src/components/TextEditor/extensions/mention.ts +31 -0
- package/src/components/TextEditor/extensions/table.ts +123 -0
- package/src/components/TextEditor/extensions/tasklist.ts +34 -20
- package/src/components/TextEditor/themes/default.ts +54 -2
- package/src/hooks/automerge/automerge.stories.tsx +3 -2
- package/src/{components/TextEditor → hooks/yjs}/yjs.stories.tsx +1 -1
- package/src/styles/markdown.ts +14 -8
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-editor",
|
|
3
|
-
"version": "0.3.10-main.
|
|
3
|
+
"version": "0.3.10-main.c5193cc",
|
|
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",
|
|
@@ -36,19 +36,19 @@
|
|
|
36
36
|
"y-codemirror.next": "^0.3.2",
|
|
37
37
|
"y-protocols": "^1.0.5",
|
|
38
38
|
"yjs": "^13.6.10",
|
|
39
|
-
"@dxos/automerge": "0.3.10-main.
|
|
40
|
-
"@dxos/async": "0.3.10-main.
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/echo-schema": "0.3.10-main.
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/log": "0.3.10-main.
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/react-ui
|
|
50
|
-
"@dxos/
|
|
51
|
-
"@dxos/
|
|
39
|
+
"@dxos/automerge": "0.3.10-main.c5193cc",
|
|
40
|
+
"@dxos/async": "0.3.10-main.c5193cc",
|
|
41
|
+
"@dxos/debug": "0.3.10-main.c5193cc",
|
|
42
|
+
"@dxos/display-name": "0.3.10-main.c5193cc",
|
|
43
|
+
"@dxos/echo-schema": "0.3.10-main.c5193cc",
|
|
44
|
+
"@dxos/protocols": "0.3.10-main.c5193cc",
|
|
45
|
+
"@dxos/invariant": "0.3.10-main.c5193cc",
|
|
46
|
+
"@dxos/log": "0.3.10-main.c5193cc",
|
|
47
|
+
"@dxos/react-async": "0.3.10-main.c5193cc",
|
|
48
|
+
"@dxos/text-model": "0.3.10-main.c5193cc",
|
|
49
|
+
"@dxos/react-ui": "0.3.10-main.c5193cc",
|
|
50
|
+
"@dxos/util": "0.3.10-main.c5193cc",
|
|
51
|
+
"@dxos/react-ui-theme": "0.3.10-main.c5193cc"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@automerge/automerge-repo-network-broadcastchannel": "^1.0.19",
|
|
@@ -61,17 +61,17 @@
|
|
|
61
61
|
"@types/react-dom": "^18.0.6",
|
|
62
62
|
"react": "^18.2.0",
|
|
63
63
|
"react-dom": "^18.2.0",
|
|
64
|
-
"@dxos/config": "0.3.10-main.
|
|
65
|
-
"@dxos/react-client": "0.3.10-main.
|
|
66
|
-
"@dxos/echo-typegen": "0.3.10-main.
|
|
67
|
-
"@dxos/
|
|
68
|
-
"@dxos/
|
|
64
|
+
"@dxos/config": "0.3.10-main.c5193cc",
|
|
65
|
+
"@dxos/react-client": "0.3.10-main.c5193cc",
|
|
66
|
+
"@dxos/echo-typegen": "0.3.10-main.c5193cc",
|
|
67
|
+
"@dxos/react-ui": "0.3.10-main.c5193cc",
|
|
68
|
+
"@dxos/storybook-utils": "0.3.10-main.c5193cc"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"@phosphor-icons/react": "^2.0.5",
|
|
72
72
|
"react": "^18.2.0",
|
|
73
73
|
"react-dom": "^18.2.0",
|
|
74
|
-
"@dxos/react-client": "0.3.10-main.
|
|
74
|
+
"@dxos/react-client": "0.3.10-main.c5193cc"
|
|
75
75
|
},
|
|
76
76
|
"publishConfig": {
|
|
77
77
|
"access": "public"
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import '@dxosTheme';
|
|
6
|
+
|
|
6
7
|
import { EditorView } from '@codemirror/view';
|
|
7
8
|
import { faker } from '@faker-js/faker';
|
|
8
9
|
import { ArrowSquareOut } from '@phosphor-icons/react';
|
|
10
|
+
import defaultsDeep from 'lodash.defaultsdeep';
|
|
9
11
|
import React, { StrictMode, useRef, useState } from 'react';
|
|
10
12
|
import { createRoot } from 'react-dom/client';
|
|
11
13
|
|
|
@@ -24,18 +26,19 @@ import {
|
|
|
24
26
|
type TooltipOptions,
|
|
25
27
|
type LinkOptions,
|
|
26
28
|
comments,
|
|
29
|
+
table,
|
|
30
|
+
image,
|
|
31
|
+
mention,
|
|
32
|
+
blast,
|
|
33
|
+
demo,
|
|
34
|
+
defaultOptions,
|
|
27
35
|
} from './extensions';
|
|
28
36
|
import { useTextModel } from '../../hooks';
|
|
29
37
|
|
|
30
|
-
// TODO(burdon): Read-only render mode (presentation).
|
|
31
|
-
// TODO(burdon): Slides.
|
|
32
|
-
// TODO(burdon): Autocomplete.
|
|
33
|
-
// TODO(burdon): Block quote.
|
|
34
|
-
// TODO(burdon): Images.
|
|
35
|
-
// TODO(burdon): Tables.
|
|
36
|
-
|
|
37
38
|
const str = (...lines: string[]) => lines.join('\n');
|
|
38
39
|
|
|
40
|
+
const num = () => faker.number.int({ min: 0, max: 9999 }).toLocaleString();
|
|
41
|
+
|
|
39
42
|
// prettier-ignore
|
|
40
43
|
const text = {
|
|
41
44
|
tasks: str(
|
|
@@ -77,6 +80,7 @@ const text = {
|
|
|
77
80
|
' const x = 100;',
|
|
78
81
|
' return () => <div>Test</div>;',
|
|
79
82
|
'};',
|
|
83
|
+
'',
|
|
80
84
|
'```'
|
|
81
85
|
),
|
|
82
86
|
|
|
@@ -93,7 +97,20 @@ const text = {
|
|
|
93
97
|
...[1, 2, 3, 4, 5, 6].map((level) => ['#'.repeat(level) + ` Heading ${level}`, faker.lorem.sentences(), '']).flat(),
|
|
94
98
|
),
|
|
95
99
|
|
|
96
|
-
paragraphs: str(...faker.helpers.multiple(() => [faker.lorem.paragraph(), ''], { count: 3 }).flat())
|
|
100
|
+
paragraphs: str(...faker.helpers.multiple(() => [faker.lorem.paragraph(), ''], { count: 3 }).flat()),
|
|
101
|
+
|
|
102
|
+
table: str(
|
|
103
|
+
'# Table',
|
|
104
|
+
'',
|
|
105
|
+
`| ${faker.lorem.word()} | ${faker.lorem.word()} | ${faker.lorem.word()} |`,
|
|
106
|
+
'|---|---|---|',
|
|
107
|
+
`| ${num()} | ${num()} | ${num()} |`,
|
|
108
|
+
`| ${num()} | ${num()} | ${num()} |`,
|
|
109
|
+
`| ${num()} | ${num()} | ${num()} |`,
|
|
110
|
+
'', // TODO(burdon): Possible GFM parsing bug if no newline?
|
|
111
|
+
),
|
|
112
|
+
|
|
113
|
+
image: str('# Image', '', ''),
|
|
97
114
|
};
|
|
98
115
|
|
|
99
116
|
const document = str(
|
|
@@ -103,9 +120,7 @@ const document = str(
|
|
|
103
120
|
'',
|
|
104
121
|
'This is all about https://dxos.org and related technologies.',
|
|
105
122
|
'',
|
|
106
|
-
'This this is **bold**,
|
|
107
|
-
'',
|
|
108
|
-
'__NOTE__: Fenced code uses the base font.',
|
|
123
|
+
'This this is **bold**, ~~strikethrough~~, _italic_, and `f(INLINE)`.',
|
|
109
124
|
'',
|
|
110
125
|
'---',
|
|
111
126
|
text.links,
|
|
@@ -119,6 +134,10 @@ const document = str(
|
|
|
119
134
|
text.code,
|
|
120
135
|
'---',
|
|
121
136
|
text.headings,
|
|
137
|
+
'---',
|
|
138
|
+
text.table,
|
|
139
|
+
'---',
|
|
140
|
+
text.image,
|
|
122
141
|
);
|
|
123
142
|
|
|
124
143
|
const links = [
|
|
@@ -158,7 +177,7 @@ const Story = ({
|
|
|
158
177
|
text,
|
|
159
178
|
automerge,
|
|
160
179
|
...props
|
|
161
|
-
}: { text?: string; automerge?: boolean } & Pick<TextEditorProps, 'extensions' | 'slots'>) => {
|
|
180
|
+
}: { text?: string; automerge?: boolean } & Pick<TextEditorProps, 'readonly' | 'extensions' | 'slots'>) => {
|
|
162
181
|
const ref = useRef<TextEditorRef>(null);
|
|
163
182
|
const [item] = useState({ text: new TextObject(text, undefined, undefined, { useAutomergeBackend: automerge }) });
|
|
164
183
|
const model = useTextModel({ text: item.text });
|
|
@@ -185,20 +204,23 @@ export default {
|
|
|
185
204
|
render: Story,
|
|
186
205
|
};
|
|
187
206
|
|
|
207
|
+
const extensions = [
|
|
208
|
+
link({ onRender }),
|
|
209
|
+
tooltip({ onHover }),
|
|
210
|
+
tasklist(),
|
|
211
|
+
table(),
|
|
212
|
+
image(),
|
|
213
|
+
autocomplete({
|
|
214
|
+
onSearch: (text) => links.filter(({ label }) => label.toLowerCase().includes(text.toLowerCase())),
|
|
215
|
+
}),
|
|
216
|
+
];
|
|
217
|
+
|
|
188
218
|
export const Default = {
|
|
189
|
-
render: () =>
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
tooltip({ onHover }),
|
|
195
|
-
tasklist(),
|
|
196
|
-
autocomplete({
|
|
197
|
-
onSearch: (text) => links.filter(({ label }) => label.toLowerCase().includes(text.toLowerCase())),
|
|
198
|
-
}),
|
|
199
|
-
]}
|
|
200
|
-
/>
|
|
201
|
-
),
|
|
219
|
+
render: () => <Story text={document} extensions={extensions} />,
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
export const Readonly = {
|
|
223
|
+
render: () => <Story text={document} extensions={extensions} readonly />,
|
|
202
224
|
};
|
|
203
225
|
|
|
204
226
|
export const Simple = {
|
|
@@ -213,6 +235,16 @@ export const Links = {
|
|
|
213
235
|
render: () => <Story text={text.links} extensions={[link({ onRender })]} />,
|
|
214
236
|
};
|
|
215
237
|
|
|
238
|
+
export const Table = {
|
|
239
|
+
render: () => (
|
|
240
|
+
<Story text={str('This is a table', '', text.table, '', text.table, '', '')} readonly extensions={[table()]} />
|
|
241
|
+
),
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
export const Image = {
|
|
245
|
+
render: () => <Story text={str('This is an image', '', text.image, '', '')} readonly extensions={[image()]} />,
|
|
246
|
+
};
|
|
247
|
+
|
|
216
248
|
export const TaskList = {
|
|
217
249
|
render: () => (
|
|
218
250
|
<Story
|
|
@@ -243,6 +275,21 @@ export const Autocomplete = {
|
|
|
243
275
|
),
|
|
244
276
|
};
|
|
245
277
|
|
|
278
|
+
const names = ['adam', 'alice', 'alison', 'bob', 'carol', 'charlie', 'sayuri', 'shoko'];
|
|
279
|
+
|
|
280
|
+
export const Mention = {
|
|
281
|
+
render: () => (
|
|
282
|
+
<Story
|
|
283
|
+
text={str('# Mention', '', '', '', '', '', '')}
|
|
284
|
+
extensions={[
|
|
285
|
+
mention({
|
|
286
|
+
onSearch: (text) => names.filter((name) => name.toLowerCase().startsWith(text.toLowerCase())),
|
|
287
|
+
}),
|
|
288
|
+
]}
|
|
289
|
+
/>
|
|
290
|
+
),
|
|
291
|
+
};
|
|
292
|
+
|
|
246
293
|
const mark = () => `[^${PublicKey.random().toHex()}]`;
|
|
247
294
|
export const Comments = {
|
|
248
295
|
render: () => (
|
|
@@ -273,3 +320,32 @@ export const Diagnostics = {
|
|
|
273
320
|
/>
|
|
274
321
|
),
|
|
275
322
|
};
|
|
323
|
+
|
|
324
|
+
export const Demo = {
|
|
325
|
+
render: () => <Story text={text.paragraphs} extensions={[demo()]} />,
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
export const Blast = {
|
|
329
|
+
render: () => (
|
|
330
|
+
<Story
|
|
331
|
+
text={str(text.paragraphs, text.code, text.paragraphs)}
|
|
332
|
+
extensions={[
|
|
333
|
+
demo({
|
|
334
|
+
items: localStorage.getItem('dxos.composer.extension.demo')?.split(','),
|
|
335
|
+
}),
|
|
336
|
+
blast(
|
|
337
|
+
defaultsDeep(
|
|
338
|
+
{
|
|
339
|
+
effect: 2,
|
|
340
|
+
particleGravity: 0.2,
|
|
341
|
+
particleShrinkRate: 0.995,
|
|
342
|
+
color: () => [faker.number.int({ min: 100, max: 200 }), 0, 0],
|
|
343
|
+
// color: () => [faker.number.int(256), faker.number.int(256), faker.number.int(256)],
|
|
344
|
+
},
|
|
345
|
+
defaultOptions,
|
|
346
|
+
),
|
|
347
|
+
),
|
|
348
|
+
]}
|
|
349
|
+
/>
|
|
350
|
+
),
|
|
351
|
+
};
|
|
@@ -21,7 +21,7 @@ import { generateName } from '@dxos/display-name';
|
|
|
21
21
|
import { useThemeContext } from '@dxos/react-ui';
|
|
22
22
|
import { getColorForValue, inputSurface, mx } from '@dxos/react-ui-theme';
|
|
23
23
|
|
|
24
|
-
import { basicBundle,
|
|
24
|
+
import { basicBundle, markdownBundle } from './extensions';
|
|
25
25
|
import { defaultTheme, markdownTheme, textTheme } from './themes';
|
|
26
26
|
import { type EditorModel } from '../../hooks';
|
|
27
27
|
import { type ThemeStyles } from '../../styles';
|
|
@@ -56,9 +56,10 @@ export type TextEditorSlots = {
|
|
|
56
56
|
|
|
57
57
|
export type TextEditorProps = {
|
|
58
58
|
model: EditorModel;
|
|
59
|
+
readonly?: boolean;
|
|
60
|
+
editorMode?: EditorMode;
|
|
59
61
|
extensions?: Extension[];
|
|
60
62
|
slots?: TextEditorSlots;
|
|
61
|
-
editorMode?: EditorMode;
|
|
62
63
|
};
|
|
63
64
|
|
|
64
65
|
/**
|
|
@@ -66,7 +67,7 @@ export type TextEditorProps = {
|
|
|
66
67
|
* NOTE: Rather than adding properties, try to create extensions that can be reused.
|
|
67
68
|
*/
|
|
68
69
|
export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
69
|
-
({ model, extensions = [], slots = defaultSlots
|
|
70
|
+
({ model, readonly, editorMode, extensions = [], slots = defaultSlots }, forwardedRef) => {
|
|
70
71
|
const { themeMode } = useThemeContext();
|
|
71
72
|
const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
|
|
72
73
|
|
|
@@ -95,6 +96,8 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
95
96
|
const state = EditorState.create({
|
|
96
97
|
doc: model.text(),
|
|
97
98
|
extensions: [
|
|
99
|
+
readonly && EditorState.readOnly.of(readonly),
|
|
100
|
+
|
|
98
101
|
// TODO(burdon): Factor out VIM mode?
|
|
99
102
|
editorMode === 'vim' && vim(),
|
|
100
103
|
|
|
@@ -124,7 +127,7 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
124
127
|
setView(undefined);
|
|
125
128
|
setState(undefined);
|
|
126
129
|
};
|
|
127
|
-
}, [root, model, themeMode, editorMode]);
|
|
130
|
+
}, [root, model, themeMode, readonly, editorMode]);
|
|
128
131
|
|
|
129
132
|
const handleKeyUp = useCallback(
|
|
130
133
|
(event: KeyboardEvent) => {
|
|
@@ -157,25 +160,15 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
157
160
|
},
|
|
158
161
|
);
|
|
159
162
|
|
|
160
|
-
// TODO(burdon): Allow plugins to set extensions (factory).
|
|
161
|
-
const maybeDebug = (): Extension => {
|
|
162
|
-
// TODO(burdon): Parse JSON script format (with key bindings?)
|
|
163
|
-
const items = localStorage.getItem('dxos.composer.demo');
|
|
164
|
-
if (items) {
|
|
165
|
-
return demo({ items: items.split(',') });
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return [];
|
|
169
|
-
};
|
|
170
|
-
|
|
171
163
|
export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
172
|
-
({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
164
|
+
({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
173
165
|
const { themeMode } = useThemeContext();
|
|
174
166
|
const slots = defaultsDeep({}, _slots, defaultTextSlots);
|
|
175
167
|
return (
|
|
176
168
|
<BaseTextEditor
|
|
177
169
|
ref={forwardedRef}
|
|
178
|
-
|
|
170
|
+
readonly={readonly}
|
|
171
|
+
extensions={[basicBundle({ readonly, themeMode, placeholder: slots?.editor?.placeholder }), ...extensions]}
|
|
179
172
|
slots={slots}
|
|
180
173
|
{...props}
|
|
181
174
|
/>
|
|
@@ -184,17 +177,14 @@ export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
184
177
|
);
|
|
185
178
|
|
|
186
179
|
export const MarkdownEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
187
|
-
({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
180
|
+
({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
188
181
|
const { themeMode } = useThemeContext();
|
|
189
182
|
const slots = defaultsDeep({}, _slots, defaultMarkdownSlots);
|
|
190
183
|
return (
|
|
191
184
|
<BaseTextEditor
|
|
192
185
|
ref={forwardedRef}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
maybeDebug(),
|
|
196
|
-
...extensions,
|
|
197
|
-
]}
|
|
186
|
+
readonly={readonly}
|
|
187
|
+
extensions={[markdownBundle({ readonly, themeMode, placeholder: slots?.editor?.placeholder }), ...extensions]}
|
|
198
188
|
slots={slots}
|
|
199
189
|
{...props}
|
|
200
190
|
/>
|
|
@@ -2,23 +2,15 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
// TODO(burdon): Automcomplete: https://codemirror.net/5/doc/manual.html#addon_runmode
|
|
6
|
-
// TODO(burdon): Modes: parallel parsing and decoration (e.g., associated with language).
|
|
7
|
-
// TODO(burdon): Add-on: runmode: run lexer over content (with rendering codemirror).
|
|
8
|
-
// https://codemirror.net/5/doc/manual.html#addon_runmode
|
|
9
|
-
// TODO(burdon): Add-on: dialog.
|
|
10
|
-
// TODO(burdon): Comments: https://codemirror.net/5/doc/manual.html#setBookmark
|
|
11
|
-
// TODO(burdon): Split view: https://codemirror.net/examples/split
|
|
12
|
-
|
|
13
5
|
// https://codemirror.net/examples/autocompletion
|
|
14
6
|
// https://codemirror.net/docs/ref/#autocomplete.autocompletion
|
|
15
7
|
// https://codemirror.net/docs/ref/#autocomplete.Completion
|
|
16
8
|
|
|
17
9
|
import {
|
|
18
10
|
autocompletion,
|
|
11
|
+
completionKeymap,
|
|
19
12
|
type Completion,
|
|
20
13
|
type CompletionContext,
|
|
21
|
-
completionKeymap,
|
|
22
14
|
type CompletionResult,
|
|
23
15
|
} from '@codemirror/autocomplete';
|
|
24
16
|
import { keymap } from '@codemirror/view';
|
|
@@ -51,21 +43,14 @@ export const autocomplete = ({ onSearch }: AutocompleteOptions) => {
|
|
|
51
43
|
// TODO(burdon): Optional decoration via addToOptions
|
|
52
44
|
override: [
|
|
53
45
|
(context: CompletionContext): CompletionResult | null => {
|
|
54
|
-
const
|
|
55
|
-
if (!
|
|
46
|
+
const match = context.matchBefore(/\w*/);
|
|
47
|
+
if (!match || (match.from === match.to && !context.explicit)) {
|
|
56
48
|
return null;
|
|
57
49
|
}
|
|
58
50
|
|
|
59
|
-
// TODO(burdon): Option to convert to links?
|
|
60
51
|
return {
|
|
61
|
-
from:
|
|
62
|
-
options: onSearch(
|
|
63
|
-
// options: [
|
|
64
|
-
// { label: 'apple', type: 'keyword' },
|
|
65
|
-
// { label: 'amazon', type: 'keyword' },
|
|
66
|
-
// { label: 'hello', type: 'variable', info: '(World)' },
|
|
67
|
-
// { label: 'magic', type: 'text', apply: '⠁⭒*.✩.*⭒⠁', detail: 'macro' },
|
|
68
|
-
// ],
|
|
52
|
+
from: match.from,
|
|
53
|
+
options: onSearch(match.text.toLowerCase()),
|
|
69
54
|
};
|
|
70
55
|
},
|
|
71
56
|
],
|
|
@@ -11,11 +11,12 @@ import { EditorView, placeholder } from '@codemirror/view';
|
|
|
11
11
|
import type { ThemeMode } from '@dxos/react-ui';
|
|
12
12
|
|
|
13
13
|
export type BasicBundleOptions = {
|
|
14
|
+
readonly?: boolean;
|
|
14
15
|
themeMode?: ThemeMode;
|
|
15
16
|
placeholder?: string;
|
|
16
17
|
};
|
|
17
18
|
|
|
18
|
-
export const basicBundle = ({ themeMode, placeholder: _placeholder }: BasicBundleOptions): Extension[] =>
|
|
19
|
+
export const basicBundle = ({ readonly, themeMode, placeholder: _placeholder }: BasicBundleOptions): Extension[] =>
|
|
19
20
|
[
|
|
20
21
|
// TODO(burdon): Compare with minimalSetup.
|
|
21
22
|
// https://codemirror.net/docs/ref/#codemirror.minimalSetup
|