@dxos/react-ui-editor 0.3.10-main.cbe7692 → 0.3.10-main.dcfebef
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 +123 -689
- 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 -19
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +1 -2
- 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 +1 -2
- package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/demo.d.ts +1 -2
- package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/index.d.ts +0 -4
- 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 +1 -2
- 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 +2 -6
- package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +1 -2
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/themes/default.d.ts +0 -5
- package/dist/types/src/components/TextEditor/themes/default.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/styles/markdown.d.ts +2 -3
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +25 -98
- package/src/components/TextEditor/TextEditor.tsx +7 -16
- package/src/components/TextEditor/extensions/autocomplete.ts +20 -5
- package/src/components/TextEditor/extensions/basic.ts +1 -2
- package/src/components/TextEditor/extensions/demo.ts +1 -4
- package/src/components/TextEditor/extensions/index.ts +0 -4
- package/src/components/TextEditor/extensions/link.ts +23 -15
- package/src/components/TextEditor/extensions/listener.ts +3 -3
- package/src/components/TextEditor/extensions/markdown/bundle.ts +5 -10
- package/src/components/TextEditor/extensions/markdown/highlight.ts +104 -118
- package/src/components/TextEditor/extensions/tasklist.ts +20 -34
- package/src/components/TextEditor/themes/default.ts +2 -54
- package/src/{hooks/yjs → components/TextEditor}/yjs.stories.tsx +1 -1
- package/src/styles/markdown.ts +8 -14
- package/dist/types/src/components/TextEditor/extensions/blast.d.ts +0 -25
- package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/image.d.ts +0 -4
- package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/mention.d.ts +0 -6
- package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/table.d.ts +0 -8
- package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +0 -1
- package/src/components/TextEditor/extensions/blast.ts +0 -375
- package/src/components/TextEditor/extensions/image.ts +0 -68
- package/src/components/TextEditor/extensions/mention.ts +0 -31
- package/src/components/TextEditor/extensions/table.ts +0 -123
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import '@dxosTheme';
|
|
6
|
-
|
|
7
6
|
import { EditorView } from '@codemirror/view';
|
|
8
7
|
import { faker } from '@faker-js/faker';
|
|
9
8
|
import { ArrowSquareOut } from '@phosphor-icons/react';
|
|
10
|
-
import defaultsDeep from 'lodash.defaultsdeep';
|
|
11
9
|
import React, { StrictMode, useRef, useState } from 'react';
|
|
12
10
|
import { createRoot } from 'react-dom/client';
|
|
13
11
|
|
|
@@ -26,18 +24,17 @@ import {
|
|
|
26
24
|
type TooltipOptions,
|
|
27
25
|
type LinkOptions,
|
|
28
26
|
comments,
|
|
29
|
-
table,
|
|
30
|
-
image,
|
|
31
|
-
mention,
|
|
32
|
-
blast,
|
|
33
|
-
demo,
|
|
34
|
-
defaultOptions,
|
|
35
27
|
} from './extensions';
|
|
36
28
|
import { useTextModel } from '../../hooks';
|
|
37
29
|
|
|
38
|
-
|
|
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.
|
|
39
36
|
|
|
40
|
-
const
|
|
37
|
+
const str = (...lines: string[]) => lines.join('\n');
|
|
41
38
|
|
|
42
39
|
// prettier-ignore
|
|
43
40
|
const text = {
|
|
@@ -80,7 +77,6 @@ const text = {
|
|
|
80
77
|
' const x = 100;',
|
|
81
78
|
' return () => <div>Test</div>;',
|
|
82
79
|
'};',
|
|
83
|
-
'',
|
|
84
80
|
'```'
|
|
85
81
|
),
|
|
86
82
|
|
|
@@ -97,20 +93,7 @@ const text = {
|
|
|
97
93
|
...[1, 2, 3, 4, 5, 6].map((level) => ['#'.repeat(level) + ` Heading ${level}`, faker.lorem.sentences(), '']).flat(),
|
|
98
94
|
),
|
|
99
95
|
|
|
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', '', ''),
|
|
96
|
+
paragraphs: str(...faker.helpers.multiple(() => [faker.lorem.paragraph(), ''], { count: 3 }).flat())
|
|
114
97
|
};
|
|
115
98
|
|
|
116
99
|
const document = str(
|
|
@@ -120,7 +103,9 @@ const document = str(
|
|
|
120
103
|
'',
|
|
121
104
|
'This is all about https://dxos.org and related technologies.',
|
|
122
105
|
'',
|
|
123
|
-
'This this is **bold**,
|
|
106
|
+
'This this is **bold**, __underlined__, _italic_, and `f(INLINE)`.',
|
|
107
|
+
'',
|
|
108
|
+
'__NOTE__: Fenced code uses the base font.',
|
|
124
109
|
'',
|
|
125
110
|
'---',
|
|
126
111
|
text.links,
|
|
@@ -134,10 +119,6 @@ const document = str(
|
|
|
134
119
|
text.code,
|
|
135
120
|
'---',
|
|
136
121
|
text.headings,
|
|
137
|
-
'---',
|
|
138
|
-
text.table,
|
|
139
|
-
'---',
|
|
140
|
-
text.image,
|
|
141
122
|
);
|
|
142
123
|
|
|
143
124
|
const links = [
|
|
@@ -177,7 +158,7 @@ const Story = ({
|
|
|
177
158
|
text,
|
|
178
159
|
automerge,
|
|
179
160
|
...props
|
|
180
|
-
}: { text?: string; automerge?: boolean } & Pick<TextEditorProps, '
|
|
161
|
+
}: { text?: string; automerge?: boolean } & Pick<TextEditorProps, 'extensions' | 'slots'>) => {
|
|
181
162
|
const ref = useRef<TextEditorRef>(null);
|
|
182
163
|
const [item] = useState({ text: new TextObject(text, undefined, undefined, { useAutomergeBackend: automerge }) });
|
|
183
164
|
const model = useTextModel({ text: item.text });
|
|
@@ -204,23 +185,20 @@ export default {
|
|
|
204
185
|
render: Story,
|
|
205
186
|
};
|
|
206
187
|
|
|
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
|
-
|
|
218
188
|
export const Default = {
|
|
219
|
-
render: () =>
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
189
|
+
render: () => (
|
|
190
|
+
<Story
|
|
191
|
+
text={document}
|
|
192
|
+
extensions={[
|
|
193
|
+
link({ onRender }),
|
|
194
|
+
tooltip({ onHover }),
|
|
195
|
+
tasklist(),
|
|
196
|
+
autocomplete({
|
|
197
|
+
onSearch: (text) => links.filter(({ label }) => label.toLowerCase().includes(text.toLowerCase())),
|
|
198
|
+
}),
|
|
199
|
+
]}
|
|
200
|
+
/>
|
|
201
|
+
),
|
|
224
202
|
};
|
|
225
203
|
|
|
226
204
|
export const Simple = {
|
|
@@ -235,16 +213,6 @@ export const Links = {
|
|
|
235
213
|
render: () => <Story text={text.links} extensions={[link({ onRender })]} />,
|
|
236
214
|
};
|
|
237
215
|
|
|
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
|
-
|
|
248
216
|
export const TaskList = {
|
|
249
217
|
render: () => (
|
|
250
218
|
<Story
|
|
@@ -275,21 +243,6 @@ export const Autocomplete = {
|
|
|
275
243
|
),
|
|
276
244
|
};
|
|
277
245
|
|
|
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
|
-
|
|
293
246
|
const mark = () => `[^${PublicKey.random().toHex()}]`;
|
|
294
247
|
export const Comments = {
|
|
295
248
|
render: () => (
|
|
@@ -320,29 +273,3 @@ export const Diagnostics = {
|
|
|
320
273
|
/>
|
|
321
274
|
),
|
|
322
275
|
};
|
|
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
|
-
blast(
|
|
334
|
-
defaultsDeep(
|
|
335
|
-
{
|
|
336
|
-
effect: 2,
|
|
337
|
-
particleGravity: 0.2,
|
|
338
|
-
particleShrinkRate: 0.995,
|
|
339
|
-
color: () => [faker.number.int({ min: 100, max: 200 }), 0, 0],
|
|
340
|
-
// color: () => [faker.number.int(256), faker.number.int(256), faker.number.int(256)],
|
|
341
|
-
},
|
|
342
|
-
defaultOptions,
|
|
343
|
-
),
|
|
344
|
-
),
|
|
345
|
-
]}
|
|
346
|
-
/>
|
|
347
|
-
),
|
|
348
|
-
};
|
|
@@ -56,10 +56,9 @@ export type TextEditorSlots = {
|
|
|
56
56
|
|
|
57
57
|
export type TextEditorProps = {
|
|
58
58
|
model: EditorModel;
|
|
59
|
-
readonly?: boolean;
|
|
60
|
-
editorMode?: EditorMode;
|
|
61
59
|
extensions?: Extension[];
|
|
62
60
|
slots?: TextEditorSlots;
|
|
61
|
+
editorMode?: EditorMode;
|
|
63
62
|
};
|
|
64
63
|
|
|
65
64
|
/**
|
|
@@ -67,7 +66,7 @@ export type TextEditorProps = {
|
|
|
67
66
|
* NOTE: Rather than adding properties, try to create extensions that can be reused.
|
|
68
67
|
*/
|
|
69
68
|
export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
70
|
-
({ model,
|
|
69
|
+
({ model, extensions = [], slots = defaultSlots, editorMode }, forwardedRef) => {
|
|
71
70
|
const { themeMode } = useThemeContext();
|
|
72
71
|
const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
|
|
73
72
|
|
|
@@ -96,8 +95,6 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
96
95
|
const state = EditorState.create({
|
|
97
96
|
doc: model.text(),
|
|
98
97
|
extensions: [
|
|
99
|
-
readonly && EditorState.readOnly.of(readonly),
|
|
100
|
-
|
|
101
98
|
// TODO(burdon): Factor out VIM mode?
|
|
102
99
|
editorMode === 'vim' && vim(),
|
|
103
100
|
|
|
@@ -127,7 +124,7 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
127
124
|
setView(undefined);
|
|
128
125
|
setState(undefined);
|
|
129
126
|
};
|
|
130
|
-
}, [root, model, themeMode,
|
|
127
|
+
}, [root, model, themeMode, editorMode]);
|
|
131
128
|
|
|
132
129
|
const handleKeyUp = useCallback(
|
|
133
130
|
(event: KeyboardEvent) => {
|
|
@@ -172,18 +169,13 @@ const maybeDebug = (): Extension => {
|
|
|
172
169
|
};
|
|
173
170
|
|
|
174
171
|
export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
175
|
-
({
|
|
172
|
+
({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
176
173
|
const { themeMode } = useThemeContext();
|
|
177
174
|
const slots = defaultsDeep({}, _slots, defaultTextSlots);
|
|
178
175
|
return (
|
|
179
176
|
<BaseTextEditor
|
|
180
177
|
ref={forwardedRef}
|
|
181
|
-
|
|
182
|
-
extensions={[
|
|
183
|
-
basicBundle({ readonly, themeMode, placeholder: slots?.editor?.placeholder }),
|
|
184
|
-
maybeDebug(),
|
|
185
|
-
...extensions,
|
|
186
|
-
]}
|
|
178
|
+
extensions={[basicBundle({ themeMode, placeholder: slots?.editor?.placeholder }), maybeDebug(), ...extensions]}
|
|
187
179
|
slots={slots}
|
|
188
180
|
{...props}
|
|
189
181
|
/>
|
|
@@ -192,15 +184,14 @@ export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
|
192
184
|
);
|
|
193
185
|
|
|
194
186
|
export const MarkdownEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
195
|
-
({
|
|
187
|
+
({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
196
188
|
const { themeMode } = useThemeContext();
|
|
197
189
|
const slots = defaultsDeep({}, _slots, defaultMarkdownSlots);
|
|
198
190
|
return (
|
|
199
191
|
<BaseTextEditor
|
|
200
192
|
ref={forwardedRef}
|
|
201
|
-
readonly={readonly}
|
|
202
193
|
extensions={[
|
|
203
|
-
markdownBundle({
|
|
194
|
+
markdownBundle({ themeMode, placeholder: slots?.editor?.placeholder }),
|
|
204
195
|
maybeDebug(),
|
|
205
196
|
...extensions,
|
|
206
197
|
]}
|
|
@@ -2,15 +2,23 @@
|
|
|
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
|
+
|
|
5
13
|
// https://codemirror.net/examples/autocompletion
|
|
6
14
|
// https://codemirror.net/docs/ref/#autocomplete.autocompletion
|
|
7
15
|
// https://codemirror.net/docs/ref/#autocomplete.Completion
|
|
8
16
|
|
|
9
17
|
import {
|
|
10
18
|
autocompletion,
|
|
11
|
-
completionKeymap,
|
|
12
19
|
type Completion,
|
|
13
20
|
type CompletionContext,
|
|
21
|
+
completionKeymap,
|
|
14
22
|
type CompletionResult,
|
|
15
23
|
} from '@codemirror/autocomplete';
|
|
16
24
|
import { keymap } from '@codemirror/view';
|
|
@@ -43,14 +51,21 @@ export const autocomplete = ({ onSearch }: AutocompleteOptions) => {
|
|
|
43
51
|
// TODO(burdon): Optional decoration via addToOptions
|
|
44
52
|
override: [
|
|
45
53
|
(context: CompletionContext): CompletionResult | null => {
|
|
46
|
-
const
|
|
47
|
-
if (!
|
|
54
|
+
const word = context.matchBefore(/\w*/);
|
|
55
|
+
if (!word || (word.from === word.to && !context.explicit)) {
|
|
48
56
|
return null;
|
|
49
57
|
}
|
|
50
58
|
|
|
59
|
+
// TODO(burdon): Option to convert to links?
|
|
51
60
|
return {
|
|
52
|
-
from:
|
|
53
|
-
options: onSearch(
|
|
61
|
+
from: word.from,
|
|
62
|
+
options: onSearch(word.text.toLowerCase()),
|
|
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
|
+
// ],
|
|
54
69
|
};
|
|
55
70
|
},
|
|
56
71
|
],
|
|
@@ -11,12 +11,11 @@ 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;
|
|
15
14
|
themeMode?: ThemeMode;
|
|
16
15
|
placeholder?: string;
|
|
17
16
|
};
|
|
18
17
|
|
|
19
|
-
export const basicBundle = ({
|
|
18
|
+
export const basicBundle = ({ themeMode, placeholder: _placeholder }: BasicBundleOptions): Extension[] =>
|
|
20
19
|
[
|
|
21
20
|
// TODO(burdon): Compare with minimalSetup.
|
|
22
21
|
// https://codemirror.net/docs/ref/#codemirror.minimalSetup
|
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type Extension } from '@codemirror/state';
|
|
6
5
|
import { keymap } from '@codemirror/view';
|
|
7
6
|
|
|
8
|
-
// TODO(burdon): Review https://github.com/sergeche/codemirror-movie?tab=readme-ov-file
|
|
9
|
-
|
|
10
7
|
export type DemoOptions = {
|
|
11
8
|
delay?: number;
|
|
12
9
|
items?: string[];
|
|
@@ -18,7 +15,7 @@ const defaultItems = ['hello world!', 'this is a test.', 'this is [DXOS](https:/
|
|
|
18
15
|
* Demo script.
|
|
19
16
|
* Configurable plugin that let's user cycle through pre-configured input script.
|
|
20
17
|
*/
|
|
21
|
-
export const demo = ({ delay = 75, items = defaultItems }: DemoOptions = {})
|
|
18
|
+
export const demo = ({ delay = 75, items = defaultItems }: DemoOptions = {}) => {
|
|
22
19
|
let t: any;
|
|
23
20
|
let idx = 0;
|
|
24
21
|
|
|
@@ -4,14 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
export * from './autocomplete';
|
|
6
6
|
export * from './basic';
|
|
7
|
-
export * from './blast';
|
|
8
7
|
export * from './demo';
|
|
9
8
|
export * from './comments';
|
|
10
|
-
export * from './image';
|
|
11
9
|
export * from './link';
|
|
12
10
|
export * from './listener';
|
|
13
11
|
export * from './markdown';
|
|
14
|
-
export * from './mention';
|
|
15
|
-
export * from './table';
|
|
16
12
|
export * from './tasklist';
|
|
17
13
|
export * from './tooltip';
|
|
@@ -18,22 +18,13 @@ import { Decoration, EditorView, WidgetType } from '@codemirror/view';
|
|
|
18
18
|
// https://codemirror.net/try/?c=aW1wb3J0IHsgYmFzaWNTZXR1cCwgRWRpdG9yVmlldyB9IGZyb20gImNvZGVtaXJyb3IiCmltcG9ydCB7IG1hcmtkb3duIH0gZnJvbSAiQGNvZGVtaXJyb3IvbGFuZy1tYXJrZG93biIKaW1wb3J0IHsgc3ludGF4VHJlZSB9IGZyb20gIkBjb2RlbWlycm9yL2xhbmd1YWdlIjsKaW1wb3J0IHsgRWRpdG9yU3RhdGUsIFJhbmdlU2V0QnVpbGRlciwgU3RhdGVGaWVsZCB9IGZyb20gIkBjb2RlbWlycm9yL3N0YXRlIjsKaW1wb3J0IHsgRGVjb3JhdGlvbiwgV2lkZ2V0VHlwZSB9IGZyb20gIkBjb2RlbWlycm9yL3ZpZXciOwoKY2xhc3MgTGluayBleHRlbmRzIFdpZGdldFR5cGUgewogIGNvbnN0cnVjdG9yKHRleHQsIHVybCkgewogICAgc3VwZXIoKQogICAgdGhpcy50ZXh0ID0gdGV4dAogICAgdGhpcy51cmwgPSB1cmwKICB9CiAgZXEob3RoZXIpIHsgcmV0dXJuIHRoaXMudGV4dCA9PSBvdGhlci50ZXh0ICYmIHRoaXMudXJsID09IG90aGVyLnVybCB9CiAgdG9ET00oKSB7CiAgICBsZXQgbGluayA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImEiKQogICAgbGluay50ZXh0Q29udGVudCA9IHRoaXMudGV4dAogICAgbGluay5ocmVmID0gdGhpcy51cmwKICAgIHJldHVybiBsaW5rOwogIH0KfQoKbGV0IGRlY29yYXRpb25zRmllbGQgPSBTdGF0ZUZpZWxkLmRlZmluZSh7CiAgY3JlYXRlKCkgewogICAgcmV0dXJuIERlY29yYXRpb24ubm9uZTsKICB9LAogIHVwZGF0ZShfLCB0cikgewogICAgY29uc3QgYnVpbGRlciA9IG5ldyBSYW5nZVNldEJ1aWxkZXIoKTsKICAgIGxldCBjdXJzb3IgPSB0ci5zdGF0ZS5zZWxlY3Rpb24ubWFpbi5oZWFkOwogICAgc3ludGF4VHJlZSh0ci5zdGF0ZSkuaXRlcmF0ZSh7CiAgICAgIGVudGVyOiAobm9kZSkgPT4gewogICAgICAgIGlmICgoY3Vyc29yIDwgbm9kZS5mcm9tIHx8IGN1cnNvciA+IG5vZGUudG8pICYmIG5vZGUubmFtZSA9PSAiTGluayIpIHsKICAgICAgICAgIGxldCBtYXJrcyA9IG5vZGUubm9kZS5nZXRDaGlsZHJlbigiTGlua01hcmsiKTsKICAgICAgICAgIGxldCB0ZXh0ID0gbWFya3MubGVuZ3RoID49IDIgPyB0ci5zdGF0ZS5zbGljZURvYyhtYXJrc1swXS50bywgbWFya3NbMV0uZnJvbSkgOiAiIgoKICAgICAgICAgIGxldCB1cmxOb2RlID0gbm9kZS5ub2RlLmdldENoaWxkKCJVUkwiKTsKICAgICAgICAgIGxldCB1cmwgPSB1cmxOb2RlID8gdHIuc3RhdGUuc2xpY2VEb2ModXJsTm9kZS5mcm9tLCB1cmxOb2RlLnRvKSA6ICIiCiAgICAgICAgICBidWlsZGVyLmFkZChub2RlLmZyb20sIG5vZGUudG8sIERlY29yYXRpb24ucmVwbGFjZSh7CiAgICAgICAgICAgIHdpZGdldDogbmV3IExpbmsodGV4dCwgdXJsKSwKICAgICAgICAgIH0pLAogICAgICAgICAgICAgICAgICAgICApOwogICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfSwKICAgIH0pOwogICAgcmV0dXJuIGJ1aWxkZXIuZmluaXNoKCk7CiAgfSwKICBwcm92aWRlOiAoZikgPT4gRWRpdG9yVmlldy5kZWNvcmF0aW9ucy5mcm9tKGYpLAp9KTsKCmxldCB2aWV3ID0gbmV3IEVkaXRvclZpZXcoewogIGRvYzogIiMgSGVsbG8gV29ybGRcblxuW1Rlc3RdKGh0dHBzOi8vZXhhbXBsZS5jb20pXG5cblxuIiwKICBleHRlbnNpb25zOiBbZGVjb3JhdGlvbnNGaWVsZCwgbWFya2Rvd24oKV0sCiAgcGFyZW50OiBkb2N1bWVudC5ib2R5LAp9KTsKCnZpZXcuZm9jdXMoKTs=
|
|
19
19
|
// https://discuss.codemirror.net/t/link-widget-is-clicked-only-if-editor-is-out-of-focus/7433
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
link?: boolean;
|
|
23
|
-
onRender?: (el: Element, url: string) => void;
|
|
24
|
-
};
|
|
21
|
+
// TODO(burdon): Adapt to make all markdown editable?
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return StateField.define<RangeSet<any>>({
|
|
32
|
-
create: (state) => update(state, options),
|
|
33
|
-
update: (_: RangeSet<any>, tr: Transaction) => update(tr.state, options),
|
|
34
|
-
provide: (field) => EditorView.decorations.from(field),
|
|
35
|
-
});
|
|
36
|
-
};
|
|
23
|
+
// TODO(burdon): Research overlays.
|
|
24
|
+
// https://stackoverflow.com/questions/26933344/codemirror-detect-and-create-links-inside-editor
|
|
25
|
+
// https://codemirror.net/5/doc/manual.html#addOverlay
|
|
26
|
+
// https://codemirror.net/5/demo/simplemode.html
|
|
27
|
+
// https://codemirror.net/5/demo/search.html (Search dialog).
|
|
37
28
|
|
|
38
29
|
class LinkButton extends WidgetType {
|
|
39
30
|
constructor(
|
|
@@ -134,3 +125,20 @@ const update = (state: EditorState, options: LinkOptions) => {
|
|
|
134
125
|
|
|
135
126
|
return builder.finish();
|
|
136
127
|
};
|
|
128
|
+
|
|
129
|
+
export type LinkOptions = {
|
|
130
|
+
link?: boolean;
|
|
131
|
+
onRender?: (el: Element, url: string) => void;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Creates a state field to replace AST elements with a hyperlink widget.
|
|
136
|
+
* https://codemirror.net/docs/ref/#state.StateField
|
|
137
|
+
*/
|
|
138
|
+
export const link = (options: LinkOptions = {}): Extension => {
|
|
139
|
+
return StateField.define<RangeSet<any>>({
|
|
140
|
+
create: (state) => update(state, options),
|
|
141
|
+
update: (_: RangeSet<any>, tr: Transaction) => update(tr.state, options),
|
|
142
|
+
provide: (field) => EditorView.decorations.from(field),
|
|
143
|
+
});
|
|
144
|
+
};
|
|
@@ -9,8 +9,9 @@ export type ListenerOptions = { onChange: (text: string) => void };
|
|
|
9
9
|
/**
|
|
10
10
|
* Based on https://github.com/codemirror/dev/issues/44#issuecomment-789093799
|
|
11
11
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// TODO(burdon): Expose options.
|
|
13
|
+
export const listener = ({ onChange }: ListenerOptions) =>
|
|
14
|
+
StateField.define({
|
|
14
15
|
create: () => null,
|
|
15
16
|
update: (_value, transaction) => {
|
|
16
17
|
if (transaction.docChanged && onChange) {
|
|
@@ -20,4 +21,3 @@ export const listener = ({ onChange }: ListenerOptions) => {
|
|
|
20
21
|
return null;
|
|
21
22
|
},
|
|
22
23
|
});
|
|
23
|
-
};
|
|
@@ -28,16 +28,11 @@ import type { ThemeMode } from '@dxos/react-ui';
|
|
|
28
28
|
import { markdownHighlightStyle, markdownTagsExtension } from './highlight';
|
|
29
29
|
|
|
30
30
|
export type MarkdownBundleOptions = {
|
|
31
|
-
readonly?: boolean;
|
|
32
31
|
themeMode?: ThemeMode;
|
|
33
32
|
placeholder?: string;
|
|
34
33
|
};
|
|
35
34
|
|
|
36
|
-
export const markdownBundle = ({
|
|
37
|
-
readonly,
|
|
38
|
-
themeMode,
|
|
39
|
-
placeholder: _placeholder,
|
|
40
|
-
}: MarkdownBundleOptions): Extension[] => {
|
|
35
|
+
export const markdownBundle = ({ themeMode, placeholder: _placeholder }: MarkdownBundleOptions): Extension[] => {
|
|
41
36
|
// All of https://github.com/codemirror/basic-setup minus line numbers and fold gutter.
|
|
42
37
|
// https://codemirror.net/docs/ref/#codemirror.basicSetup
|
|
43
38
|
return [
|
|
@@ -94,10 +89,10 @@ export const markdownBundle = ({
|
|
|
94
89
|
],
|
|
95
90
|
}),
|
|
96
91
|
|
|
97
|
-
// https://github.com/codemirror/theme-one-dark
|
|
98
|
-
themeMode === 'dark' ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
|
|
99
|
-
|
|
100
92
|
// Custom styles.
|
|
101
|
-
syntaxHighlighting(markdownHighlightStyle
|
|
93
|
+
syntaxHighlighting(markdownHighlightStyle),
|
|
94
|
+
|
|
95
|
+
// TODO(thure): All but one rule here apply to both themes; rename or refactor.
|
|
96
|
+
themeMode === 'dark' ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
|
|
102
97
|
].filter(Boolean) as Extension[];
|
|
103
98
|
};
|