@dxos/react-ui-editor 0.8.1 → 0.8.2-main.12df754
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 +499 -371
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +67 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/node/index.cjs +515 -379
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +101 -0
- package/dist/lib/node/testing/index.cjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +499 -371
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +69 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/components/EditorToolbar/util.d.ts +3 -3
- package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/{viewMode.d.ts → view-mode.d.ts} +1 -1
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -0
- package/dist/types/src/defaults.d.ts +1 -0
- package/dist/types/src/defaults.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/command/action.d.ts +17 -0
- package/dist/types/src/extensions/command/action.d.ts.map +1 -0
- package/dist/types/src/extensions/command/command.d.ts +5 -10
- package/dist/types/src/extensions/command/command.d.ts.map +1 -1
- package/dist/types/src/extensions/command/hint.d.ts +4 -2
- package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
- package/dist/types/src/extensions/command/index.d.ts +1 -0
- package/dist/types/src/extensions/command/index.d.ts.map +1 -1
- package/dist/types/src/extensions/command/menu.d.ts +7 -2
- package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
- package/dist/types/src/extensions/command/state.d.ts +9 -11
- package/dist/types/src/extensions/command/state.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts +9 -7
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +1 -0
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts +4 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +2 -2
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/link.d.ts +4 -1
- package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
- package/dist/types/src/extensions/preview/index.d.ts +2 -0
- package/dist/types/src/extensions/preview/index.d.ts.map +1 -0
- package/dist/types/src/extensions/preview/preview.d.ts +39 -0
- package/dist/types/src/extensions/preview/preview.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts +2 -1
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/{InputMode.stories.d.ts → stories/InputMode.stories.d.ts} +1 -1
- package/dist/types/src/stories/InputMode.stories.d.ts.map +1 -0
- package/dist/types/src/{TextEditor.stories.d.ts → stories/TextEditorBasic.stories.d.ts} +2 -35
- package/dist/types/src/stories/TextEditorBasic.stories.d.ts.map +1 -0
- package/dist/types/src/stories/TextEditorComments.stories.d.ts +13 -0
- package/dist/types/src/stories/TextEditorComments.stories.d.ts.map +1 -0
- package/dist/types/src/stories/TextEditorPreview.stories.d.ts +13 -0
- package/dist/types/src/stories/TextEditorPreview.stories.d.ts.map +1 -0
- package/dist/types/src/stories/TextEditorSpecial.stories.d.ts +19 -0
- package/dist/types/src/stories/TextEditorSpecial.stories.d.ts.map +1 -0
- package/dist/types/src/stories/story-utils.d.ts +53 -0
- package/dist/types/src/stories/story-utils.d.ts.map +1 -0
- package/dist/types/src/testing/RefPopover.d.ts +21 -0
- package/dist/types/src/testing/RefPopover.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +5 -0
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/util/react.d.ts +6 -1
- package/dist/types/src/util/react.d.ts.map +1 -1
- package/package.json +33 -27
- package/src/components/EditorToolbar/EditorToolbar.tsx +2 -2
- package/src/components/EditorToolbar/util.ts +3 -3
- package/src/defaults.ts +5 -3
- package/src/extensions/automerge/automerge.stories.tsx +3 -11
- package/src/extensions/command/action.ts +49 -0
- package/src/extensions/command/command.ts +9 -27
- package/src/extensions/command/hint.ts +33 -30
- package/src/extensions/command/index.ts +1 -0
- package/src/extensions/command/menu.ts +11 -8
- package/src/extensions/command/state.ts +41 -61
- package/src/extensions/comments.ts +9 -9
- package/src/extensions/folding.tsx +1 -1
- package/src/extensions/index.ts +1 -0
- package/src/extensions/markdown/decorate.ts +4 -3
- package/src/extensions/markdown/formatting.ts +2 -2
- package/src/extensions/markdown/image.ts +12 -11
- package/src/extensions/markdown/link.ts +33 -24
- package/src/extensions/preview/index.ts +5 -0
- package/src/extensions/preview/preview.ts +271 -0
- package/src/hooks/useTextEditor.ts +4 -3
- package/src/{InputMode.stories.tsx → stories/InputMode.stories.tsx} +4 -4
- package/src/stories/TextEditorBasic.stories.tsx +289 -0
- package/src/stories/TextEditorComments.stories.tsx +99 -0
- package/src/stories/TextEditorPreview.stories.tsx +239 -0
- package/src/stories/TextEditorSpecial.stories.tsx +107 -0
- package/src/stories/story-utils.tsx +329 -0
- package/src/testing/RefPopover.tsx +74 -0
- package/src/testing/index.ts +5 -0
- package/src/types.ts +7 -0
- package/src/util/react.tsx +20 -2
- package/dist/types/src/InputMode.stories.d.ts.map +0 -1
- package/dist/types/src/TextEditor.stories.d.ts.map +0 -1
- package/dist/types/src/components/EditorToolbar/viewMode.d.ts.map +0 -1
- package/dist/types/src/extensions/command/preview.d.ts +0 -12
- package/dist/types/src/extensions/command/preview.d.ts.map +0 -1
- package/dist/types/src/fragments.d.ts +0 -3
- package/dist/types/src/fragments.d.ts.map +0 -1
- package/src/TextEditor.stories.tsx +0 -856
- package/src/extensions/command/preview.ts +0 -79
- package/src/fragments.ts +0 -19
- /package/src/components/EditorToolbar/{viewMode.ts → view-mode.ts} +0 -0
@@ -15,6 +15,7 @@ import { image } from './image';
|
|
15
15
|
import { formattingStyles, bulletListIndentationWidth, orderedListIndentationWidth } from './styles';
|
16
16
|
import { table } from './table';
|
17
17
|
import { theme, type HeadingLevel } from '../../styles';
|
18
|
+
import { type RenderCallback } from '../../types';
|
18
19
|
import { wrapWithCatch } from '../../util';
|
19
20
|
|
20
21
|
/**
|
@@ -45,7 +46,7 @@ class HorizontalRuleWidget extends WidgetType {
|
|
45
46
|
class LinkButton extends WidgetType {
|
46
47
|
constructor(
|
47
48
|
private readonly url: string,
|
48
|
-
private readonly render:
|
49
|
+
private readonly render: RenderCallback<{ url: string }>,
|
49
50
|
) {
|
50
51
|
super();
|
51
52
|
}
|
@@ -57,7 +58,7 @@ class LinkButton extends WidgetType {
|
|
57
58
|
// TODO(burdon): Create icon and link directly without react?
|
58
59
|
override toDOM(view: EditorView) {
|
59
60
|
const el = document.createElement('span');
|
60
|
-
this.render(el, this.url);
|
61
|
+
this.render(el, { url: this.url }, view);
|
61
62
|
return el;
|
62
63
|
}
|
63
64
|
}
|
@@ -519,7 +520,7 @@ export interface DecorateOptions {
|
|
519
520
|
*/
|
520
521
|
selectionChangeDelay?: number;
|
521
522
|
numberedHeadings?: { from: number; to?: number };
|
522
|
-
renderLinkButton?:
|
523
|
+
renderLinkButton?: RenderCallback<{ url: string }>;
|
523
524
|
}
|
524
525
|
|
525
526
|
export const decorateMarkdown = (options: DecorateOptions = {}) => {
|
@@ -17,7 +17,7 @@ import { EditorView, keymap } from '@codemirror/view';
|
|
17
17
|
import { type SyntaxNodeRef, type SyntaxNode } from '@lezer/common';
|
18
18
|
import { useMemo } from 'react';
|
19
19
|
|
20
|
-
import { type
|
20
|
+
import { type Live } from '@dxos/live-object';
|
21
21
|
|
22
22
|
import { type EditorToolbarState } from '../../components';
|
23
23
|
|
@@ -1250,7 +1250,7 @@ export const getFormatting = (state: EditorState): Formatting => {
|
|
1250
1250
|
/**
|
1251
1251
|
* Hook provides an extension to compute the current formatting state.
|
1252
1252
|
*/
|
1253
|
-
export const useFormattingState = (state:
|
1253
|
+
export const useFormattingState = (state: Live<EditorToolbarState>): Extension => {
|
1254
1254
|
return useMemo(
|
1255
1255
|
() =>
|
1256
1256
|
EditorView.updateListener.of((update) => {
|
@@ -51,16 +51,6 @@ export const image = (_options: ImageOptions = {}): Extension => {
|
|
51
51
|
];
|
52
52
|
};
|
53
53
|
|
54
|
-
const preloaded = new Set<string>();
|
55
|
-
|
56
|
-
const preloadImage = (url: string) => {
|
57
|
-
if (!preloaded.has(url)) {
|
58
|
-
const img = document.createElement('img');
|
59
|
-
img.src = url;
|
60
|
-
preloaded.add(url);
|
61
|
-
}
|
62
|
-
};
|
63
|
-
|
64
54
|
const buildDecorations = (from: number, to: number, state: EditorState) => {
|
65
55
|
const decorations: Range<Decoration>[] = [];
|
66
56
|
const cursor = state.selection.main.head;
|
@@ -94,13 +84,23 @@ const buildDecorations = (from: number, to: number, state: EditorState) => {
|
|
94
84
|
return decorations;
|
95
85
|
};
|
96
86
|
|
87
|
+
const preloaded = new Set<string>();
|
88
|
+
|
89
|
+
const preloadImage = (url: string) => {
|
90
|
+
if (!preloaded.has(url)) {
|
91
|
+
const img = document.createElement('img');
|
92
|
+
img.src = url;
|
93
|
+
preloaded.add(url);
|
94
|
+
}
|
95
|
+
};
|
96
|
+
|
97
97
|
class ImageWidget extends WidgetType {
|
98
98
|
constructor(readonly _url: string) {
|
99
99
|
super();
|
100
100
|
}
|
101
101
|
|
102
102
|
override eq(other: this) {
|
103
|
-
return this._url ===
|
103
|
+
return this._url === other._url;
|
104
104
|
}
|
105
105
|
|
106
106
|
override toDOM(view: EditorView) {
|
@@ -113,6 +113,7 @@ class ImageWidget extends WidgetType {
|
|
113
113
|
} else {
|
114
114
|
img.classList.add('cm-loaded-image');
|
115
115
|
}
|
116
|
+
|
116
117
|
return img;
|
117
118
|
}
|
118
119
|
}
|
@@ -9,30 +9,39 @@ import { type SyntaxNode } from '@lezer/common';
|
|
9
9
|
|
10
10
|
import { tooltipContent } from '@dxos/react-ui-theme';
|
11
11
|
|
12
|
-
|
13
|
-
return hoverTooltip((view, pos, side) => {
|
14
|
-
const syntax = syntaxTree(view.state).resolveInner(pos, side);
|
15
|
-
let link = null;
|
16
|
-
for (let i = 0, node: SyntaxNode | null = syntax; !link && node && i < 5; node = node.parent, i++) {
|
17
|
-
link = node.name === 'Link' ? node : null;
|
18
|
-
}
|
12
|
+
import { type RenderCallback } from '../../types';
|
19
13
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
14
|
+
export const linkTooltip = (renderTooltip: RenderCallback<{ url: string }>) => {
|
15
|
+
return hoverTooltip(
|
16
|
+
(view, pos, side) => {
|
17
|
+
const syntax = syntaxTree(view.state).resolveInner(pos, side);
|
18
|
+
let link = null;
|
19
|
+
for (let i = 0, node: SyntaxNode | null = syntax; !link && node && i < 5; node = node.parent, i++) {
|
20
|
+
link = node.name === 'Link' ? node : null;
|
21
|
+
}
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
23
|
+
const url = link && link.getChild('URL');
|
24
|
+
if (!url || !link) {
|
25
|
+
return null;
|
26
|
+
}
|
27
|
+
|
28
|
+
const urlText = view.state.sliceDoc(url.from, url.to);
|
29
|
+
return {
|
30
|
+
pos: link.from,
|
31
|
+
end: link.to,
|
32
|
+
// NOTE: Forcing above causes the tooltip to flicker.
|
33
|
+
// above: true,
|
34
|
+
create: () => {
|
35
|
+
const el = document.createElement('div');
|
36
|
+
el.className = tooltipContent({});
|
37
|
+
renderTooltip(el, { url: urlText }, view);
|
38
|
+
return { dom: el, offset: { x: 0, y: 4 } };
|
39
|
+
},
|
40
|
+
};
|
41
|
+
},
|
42
|
+
{
|
43
|
+
// NOTE: 0 = default of 300ms.
|
44
|
+
hoverTime: 1,
|
45
|
+
},
|
46
|
+
);
|
38
47
|
};
|
@@ -0,0 +1,271 @@
|
|
1
|
+
//
|
2
|
+
// Copyright 2023 DXOS.org
|
3
|
+
//
|
4
|
+
|
5
|
+
import '@dxos/lit-ui/dx-ref-tag.pcss';
|
6
|
+
|
7
|
+
import { syntaxTree } from '@codemirror/language';
|
8
|
+
import {
|
9
|
+
type EditorState,
|
10
|
+
type Extension,
|
11
|
+
type RangeSet,
|
12
|
+
RangeSetBuilder,
|
13
|
+
StateField,
|
14
|
+
type Transaction,
|
15
|
+
} from '@codemirror/state';
|
16
|
+
import { Decoration, type DecorationSet, EditorView, WidgetType } from '@codemirror/view';
|
17
|
+
import { type SyntaxNode } from '@lezer/common';
|
18
|
+
|
19
|
+
import { type RenderCallback } from '../../types';
|
20
|
+
|
21
|
+
export type PreviewLinkRef = {
|
22
|
+
suggest?: boolean;
|
23
|
+
block?: boolean;
|
24
|
+
label: string;
|
25
|
+
ref: string;
|
26
|
+
};
|
27
|
+
|
28
|
+
export type PreviewLinkTarget = {
|
29
|
+
label: string;
|
30
|
+
text?: string;
|
31
|
+
object?: any;
|
32
|
+
};
|
33
|
+
|
34
|
+
export type PreviewAction =
|
35
|
+
| {
|
36
|
+
type: 'insert';
|
37
|
+
link: PreviewLinkRef;
|
38
|
+
target: PreviewLinkTarget;
|
39
|
+
}
|
40
|
+
| {
|
41
|
+
type: 'delete';
|
42
|
+
link: PreviewLinkRef;
|
43
|
+
};
|
44
|
+
|
45
|
+
// TODO(burdon): Handle error.
|
46
|
+
export type PreviewLookup = (link: PreviewLinkRef) => Promise<PreviewLinkTarget | null | undefined>;
|
47
|
+
|
48
|
+
export type PreviewActionHandler = (action: PreviewAction) => void;
|
49
|
+
|
50
|
+
export type PreviewRenderProps = {
|
51
|
+
readonly: boolean;
|
52
|
+
link: PreviewLinkRef;
|
53
|
+
onAction: PreviewActionHandler;
|
54
|
+
onLookup?: PreviewLookup;
|
55
|
+
};
|
56
|
+
|
57
|
+
export type PreviewOptions = {
|
58
|
+
renderBlock?: RenderCallback<PreviewRenderProps>;
|
59
|
+
onLookup?: PreviewLookup;
|
60
|
+
};
|
61
|
+
|
62
|
+
/**
|
63
|
+
* Create preview decorations.
|
64
|
+
*/
|
65
|
+
export const preview = (options: PreviewOptions = {}): Extension => {
|
66
|
+
return [
|
67
|
+
// NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
|
68
|
+
// "Block decorations may not be specified via plugins"
|
69
|
+
StateField.define<DecorationSet>({
|
70
|
+
create: (state) => buildDecorations(state, options),
|
71
|
+
update: (_: RangeSet<Decoration>, tr: Transaction) => buildDecorations(tr.state, options),
|
72
|
+
provide: (field) => [
|
73
|
+
EditorView.decorations.from(field),
|
74
|
+
EditorView.atomicRanges.of((view) => view.state.field(field)),
|
75
|
+
],
|
76
|
+
}),
|
77
|
+
|
78
|
+
EditorView.theme({
|
79
|
+
'.cm-preview-block': {
|
80
|
+
marginLeft: '-1rem',
|
81
|
+
marginRight: '-1rem',
|
82
|
+
padding: '1rem',
|
83
|
+
borderRadius: '0.5rem',
|
84
|
+
background: 'var(--dx-modalSurface)',
|
85
|
+
border: '1px solid var(--dx-separator)',
|
86
|
+
},
|
87
|
+
}),
|
88
|
+
];
|
89
|
+
};
|
90
|
+
|
91
|
+
/**
|
92
|
+
* Link references.
|
93
|
+
*
|
94
|
+
* [Label][dxn:echo:123] Inline reference
|
95
|
+
* ![Label][dxn:echo:123] Block reference
|
96
|
+
* ![Label][?dxn:echo:123] Suggestion
|
97
|
+
*/
|
98
|
+
const getLinkRef = (state: EditorState, node: SyntaxNode): PreviewLinkRef | undefined => {
|
99
|
+
const mark = node.getChild('LinkMark');
|
100
|
+
const label = node.getChild('LinkLabel');
|
101
|
+
if (mark && label) {
|
102
|
+
const ref = state.sliceDoc(label.from + 1, label.to - 1);
|
103
|
+
return {
|
104
|
+
suggest: ref.startsWith('?'),
|
105
|
+
block: state.sliceDoc(mark.from, mark.from + 1) === '!',
|
106
|
+
label: state.sliceDoc(mark.to, label.from - 1),
|
107
|
+
ref: ref.startsWith('?') ? ref.slice(1) : ref,
|
108
|
+
};
|
109
|
+
}
|
110
|
+
};
|
111
|
+
|
112
|
+
/**
|
113
|
+
* Echo references are represented as markdown reference links.
|
114
|
+
* https://www.markdownguide.org/basic-syntax/#reference-style-links
|
115
|
+
* [Label|block][dxn:echo:123]
|
116
|
+
* [Label|inline][dxn:echo:123]
|
117
|
+
*/
|
118
|
+
const buildDecorations = (state: EditorState, options: PreviewOptions) => {
|
119
|
+
const builder = new RangeSetBuilder<Decoration>();
|
120
|
+
|
121
|
+
syntaxTree(state).iterate({
|
122
|
+
enter: (node) => {
|
123
|
+
switch (node.name) {
|
124
|
+
//
|
125
|
+
// Decoration.
|
126
|
+
// [Label][dxn:echo:123]
|
127
|
+
//
|
128
|
+
case 'Link': {
|
129
|
+
const link = getLinkRef(state, node.node);
|
130
|
+
if (link) {
|
131
|
+
builder.add(
|
132
|
+
node.from,
|
133
|
+
node.to,
|
134
|
+
Decoration.replace({
|
135
|
+
widget: new PreviewInlineWidget(options, link),
|
136
|
+
}),
|
137
|
+
);
|
138
|
+
}
|
139
|
+
break;
|
140
|
+
}
|
141
|
+
//
|
142
|
+
// Block widget.
|
143
|
+
// ![Label][dxn:echo:123]
|
144
|
+
//
|
145
|
+
case 'Image': {
|
146
|
+
const link = getLinkRef(state, node.node);
|
147
|
+
if (options.renderBlock && link) {
|
148
|
+
builder.add(
|
149
|
+
node.from,
|
150
|
+
node.to,
|
151
|
+
Decoration.replace({
|
152
|
+
block: true,
|
153
|
+
// atomic: true,
|
154
|
+
widget: new PreviewBlockWidget(options, link),
|
155
|
+
}),
|
156
|
+
);
|
157
|
+
}
|
158
|
+
break;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
},
|
162
|
+
});
|
163
|
+
|
164
|
+
return builder.finish();
|
165
|
+
};
|
166
|
+
|
167
|
+
/**
|
168
|
+
* Inline widget.
|
169
|
+
* [Label][dxn:echo:123]
|
170
|
+
*/
|
171
|
+
class PreviewInlineWidget extends WidgetType {
|
172
|
+
constructor(
|
173
|
+
readonly _options: PreviewOptions,
|
174
|
+
readonly _link: PreviewLinkRef,
|
175
|
+
) {
|
176
|
+
super();
|
177
|
+
}
|
178
|
+
|
179
|
+
// override ignoreEvent() {
|
180
|
+
// return false;
|
181
|
+
// }
|
182
|
+
|
183
|
+
override eq(other: this) {
|
184
|
+
return this._link.ref === other._link.ref && this._link.label === other._link.label;
|
185
|
+
}
|
186
|
+
|
187
|
+
override toDOM(view: EditorView) {
|
188
|
+
const root = document.createElement('dx-ref-tag');
|
189
|
+
root.setAttribute('label', this._link.label);
|
190
|
+
root.setAttribute('ref', this._link.ref);
|
191
|
+
return root;
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
/**
|
196
|
+
* Block widget.
|
197
|
+
* ![Label][dxn:echo:123]
|
198
|
+
*/
|
199
|
+
class PreviewBlockWidget extends WidgetType {
|
200
|
+
constructor(
|
201
|
+
readonly _options: PreviewOptions,
|
202
|
+
readonly _link: PreviewLinkRef,
|
203
|
+
) {
|
204
|
+
super();
|
205
|
+
}
|
206
|
+
|
207
|
+
// override ignoreEvent() {
|
208
|
+
// return true;
|
209
|
+
// }
|
210
|
+
|
211
|
+
override eq(other: this) {
|
212
|
+
return this._link.ref === other._link.ref;
|
213
|
+
}
|
214
|
+
|
215
|
+
override toDOM(view: EditorView) {
|
216
|
+
const root = document.createElement('div');
|
217
|
+
root.classList.add('cm-preview-block');
|
218
|
+
|
219
|
+
// TODO(burdon): Inject handler.
|
220
|
+
const handleAction: PreviewActionHandler = (action) => {
|
221
|
+
const pos = view.posAtDOM(root);
|
222
|
+
const node = syntaxTree(view.state).resolve(pos + 1).node.parent;
|
223
|
+
if (!node) {
|
224
|
+
return;
|
225
|
+
}
|
226
|
+
|
227
|
+
const link = getLinkRef(view.state, node);
|
228
|
+
if (link?.ref !== action.link.ref) {
|
229
|
+
return;
|
230
|
+
}
|
231
|
+
|
232
|
+
switch (action.type) {
|
233
|
+
// TODO(burdon): Should we dispatch to the view or mutate the document? (i.e., handle externally?)
|
234
|
+
// Insert ref text.
|
235
|
+
case 'insert': {
|
236
|
+
view.dispatch({
|
237
|
+
changes: {
|
238
|
+
from: node.from,
|
239
|
+
to: node.to,
|
240
|
+
insert: action.target.text,
|
241
|
+
},
|
242
|
+
});
|
243
|
+
break;
|
244
|
+
}
|
245
|
+
// Remove ref.
|
246
|
+
case 'delete': {
|
247
|
+
view.dispatch({
|
248
|
+
changes: {
|
249
|
+
from: node.from,
|
250
|
+
to: node.to,
|
251
|
+
},
|
252
|
+
});
|
253
|
+
break;
|
254
|
+
}
|
255
|
+
}
|
256
|
+
};
|
257
|
+
|
258
|
+
this._options.renderBlock!(
|
259
|
+
root,
|
260
|
+
{
|
261
|
+
readonly: view.state.readOnly,
|
262
|
+
link: this._link,
|
263
|
+
onAction: handleAction,
|
264
|
+
onLookup: this._options.onLookup,
|
265
|
+
},
|
266
|
+
view,
|
267
|
+
);
|
268
|
+
|
269
|
+
return root;
|
270
|
+
}
|
271
|
+
}
|
@@ -2,7 +2,7 @@
|
|
2
2
|
// Copyright 2024 DXOS.org
|
3
3
|
//
|
4
4
|
|
5
|
-
import { EditorState, type EditorStateConfig } from '@codemirror/state';
|
5
|
+
import { EditorState, type EditorStateConfig, type Text } from '@codemirror/state';
|
6
6
|
import { EditorView } from '@codemirror/view';
|
7
7
|
import { useFocusableGroup, type TabsterTypes } from '@fluentui/react-tabster';
|
8
8
|
import {
|
@@ -43,6 +43,7 @@ export type CursorInfo = {
|
|
43
43
|
|
44
44
|
export type UseTextEditorProps = Pick<EditorStateConfig, 'extensions'> & {
|
45
45
|
id?: string;
|
46
|
+
doc?: Text;
|
46
47
|
initialValue?: string;
|
47
48
|
className?: string;
|
48
49
|
autoFocus?: boolean;
|
@@ -61,7 +62,7 @@ export const useTextEditor = (
|
|
61
62
|
props: MaybeProvider<UseTextEditorProps> = {},
|
62
63
|
deps: DependencyList = [],
|
63
64
|
): UseTextEditor => {
|
64
|
-
const { id, initialValue, extensions, autoFocus, scrollTo, selection, moveToEndOfLine, debug } =
|
65
|
+
const { id, doc, initialValue, extensions, autoFocus, scrollTo, selection, moveToEndOfLine, debug } =
|
65
66
|
useMemo<UseTextEditorProps>(() => getProviderValue(props), deps ?? []);
|
66
67
|
|
67
68
|
// NOTE: Increments by 2 in strict mode.
|
@@ -87,7 +88,7 @@ export const useTextEditor = (
|
|
87
88
|
|
88
89
|
// https://codemirror.net/docs/ref/#state.EditorStateConfig
|
89
90
|
const state = EditorState.create({
|
90
|
-
doc: initialValue,
|
91
|
+
doc: doc ?? initialValue,
|
91
92
|
// selection: initialSelection,
|
92
93
|
extensions: [
|
93
94
|
id && documentId.of(id),
|
@@ -10,7 +10,7 @@ import { Toolbar as NaturalToolbar, Select, useThemeContext } from '@dxos/react-
|
|
10
10
|
import { attentionSurface, mx } from '@dxos/react-ui-theme';
|
11
11
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
12
12
|
|
13
|
-
import { EditorToolbar, useEditorToolbarState } from '
|
13
|
+
import { EditorToolbar, useEditorToolbarState } from '../components';
|
14
14
|
import {
|
15
15
|
type EditorInputMode,
|
16
16
|
decorateMarkdown,
|
@@ -20,9 +20,9 @@ import {
|
|
20
20
|
createBasicExtensions,
|
21
21
|
createThemeExtensions,
|
22
22
|
InputModeExtensions,
|
23
|
-
} from '
|
24
|
-
import { useActionHandler, useTextEditor, type UseTextEditorProps } from '
|
25
|
-
import translations from '
|
23
|
+
} from '../extensions';
|
24
|
+
import { useActionHandler, useTextEditor, type UseTextEditorProps } from '../hooks';
|
25
|
+
import translations from '../translations';
|
26
26
|
|
27
27
|
type StoryProps = { placeholder?: string; readOnly?: boolean } & UseTextEditorProps;
|
28
28
|
|