@dxos/react-ui-editor 0.4.8-main.97e86fe → 0.4.8-main.a4c603b
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 +941 -776
- 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 +103 -0
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +34 -18
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +5 -79
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/defs.d.ts +15 -1
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/index.d.ts +1 -0
- package/dist/types/src/extensions/automerge/index.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/sync.d.ts +1 -2
- package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts +1 -2
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/awareness.d.ts.map +1 -0
- package/dist/types/src/extensions/comments.d.ts +2 -2
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/cursor-line-margin.d.ts +7 -6
- package/dist/types/src/extensions/cursor-line-margin.d.ts.map +1 -1
- package/dist/types/src/extensions/cursor.d.ts +1 -1
- package/dist/types/src/extensions/cursor.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +1 -3
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts +4 -5
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/table.d.ts +2 -2
- package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner.d.ts +4 -0
- package/dist/types/src/extensions/outliner.d.ts.map +1 -0
- package/dist/types/src/{extensions/awareness → hooks}/awareness-provider.d.ts +1 -1
- package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -0
- package/dist/types/src/hooks/defs.d.ts +20 -0
- package/dist/types/src/hooks/defs.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +4 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useEditorView.d.ts +20 -0
- package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts +44 -3
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.stories.d.ts +0 -6
- package/dist/types/src/hooks/useTextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextModel.d.ts +33 -0
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -0
- package/dist/types/src/themes/default.d.ts +3 -0
- package/dist/types/src/themes/default.d.ts.map +1 -1
- package/package.json +24 -24
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +524 -0
- package/src/components/TextEditor/TextEditor.stories.tsx +23 -508
- package/src/components/TextEditor/TextEditor.tsx +149 -37
- package/src/components/Toolbar/Toolbar.stories.tsx +42 -42
- package/src/extensions/automerge/automerge.stories.tsx +17 -16
- package/src/extensions/automerge/automerge.ts +1 -2
- package/src/extensions/automerge/cursor.ts +1 -1
- package/src/extensions/automerge/defs.ts +22 -1
- package/src/extensions/automerge/index.ts +1 -0
- package/src/extensions/automerge/sync.ts +9 -2
- package/src/extensions/automerge/update-automerge.ts +1 -2
- package/src/extensions/{awareness/awareness.ts → awareness.ts} +1 -1
- package/src/extensions/command/state.ts +1 -1
- package/src/extensions/comments.ts +5 -9
- package/src/extensions/cursor-line-margin.ts +18 -17
- package/src/extensions/cursor.ts +1 -1
- package/src/extensions/index.ts +1 -3
- package/src/extensions/markdown/bundle.ts +28 -11
- package/src/extensions/markdown/decorate.ts +4 -2
- package/src/extensions/markdown/formatting.ts +10 -15
- package/src/extensions/markdown/image.ts +0 -1
- package/src/extensions/markdown/table.ts +3 -11
- package/src/extensions/outliner.ts +12 -0
- package/src/{extensions/awareness → hooks}/awareness-provider.ts +1 -1
- package/src/hooks/defs.ts +30 -0
- package/src/hooks/index.ts +5 -1
- package/src/hooks/useEditorView.ts +32 -0
- package/src/hooks/useTextEditor.stories.tsx +34 -39
- package/src/hooks/useTextEditor.ts +120 -11
- package/src/hooks/useTextModel.ts +121 -0
- package/src/index.ts +1 -1
- package/src/themes/default.ts +28 -1
- package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +0 -1
- package/dist/types/src/extensions/awareness/awareness.d.ts.map +0 -1
- package/dist/types/src/extensions/awareness/index.d.ts +0 -3
- package/dist/types/src/extensions/awareness/index.d.ts.map +0 -1
- package/dist/types/src/extensions/doc.d.ts +0 -3
- package/dist/types/src/extensions/doc.d.ts.map +0 -1
- package/dist/types/src/extensions/factories.d.ts +0 -49
- package/dist/types/src/extensions/factories.d.ts.map +0 -1
- package/dist/types/src/extensions/types.d.ts +0 -9
- package/dist/types/src/extensions/types.d.ts.map +0 -1
- package/dist/types/src/hooks/useDocAccessor.d.ts +0 -7
- package/dist/types/src/hooks/useDocAccessor.d.ts.map +0 -1
- package/src/extensions/awareness/index.ts +0 -6
- package/src/extensions/doc.ts +0 -14
- package/src/extensions/factories.ts +0 -167
- package/src/extensions/types.ts +0 -16
- package/src/hooks/useDocAccessor.ts +0 -21
- /package/dist/types/src/extensions/{awareness/awareness.d.ts → awareness.d.ts} +0 -0
|
@@ -5,26 +5,26 @@
|
|
|
5
5
|
// Based upon @acheronfail’s implementation, fetched 16 Feb 2024
|
|
6
6
|
// https://discuss.codemirror.net/t/cursorscrollmargin-for-v6/7448/5
|
|
7
7
|
|
|
8
|
-
import { type EditorState, type Extension, Transaction } from '@codemirror/state';
|
|
8
|
+
import { type EditorState, type Extension, Facet, Transaction } from '@codemirror/state';
|
|
9
9
|
import { EditorView } from '@codemirror/view';
|
|
10
10
|
|
|
11
11
|
import { log } from '@dxos/log';
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Number of lines above/below the cursor to keep visible in the viewport.
|
|
15
|
+
* Defaults to 3.
|
|
16
|
+
*/
|
|
17
|
+
export const cursorLineMarginFacet = Facet.define<number, number>({
|
|
18
|
+
combine: (input) => input[0] ?? 3,
|
|
19
|
+
});
|
|
20
|
+
|
|
13
21
|
const annotation = 'cursorLineMargin';
|
|
14
22
|
const lineAtPos = (s: EditorState, pos: number) => s.doc.lineAt(pos).number;
|
|
15
23
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// TODO(burdon): Remove. Buggy and creates scrolling problems. Find better (more general) way to deal with HALO notch.
|
|
21
|
-
export const cursorLineMargin = (options: Options = { lines: 3 }): Extension => {
|
|
22
|
-
const { lines } = options;
|
|
23
|
-
if (!lines || lines <= 0) {
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return EditorView.updateListener.of(({ transactions, state, selectionSet, startState, view }) => {
|
|
24
|
+
// Seems to the best approximation of CM5's `cursorScrollMargin`
|
|
25
|
+
// https://discuss.codemirror.net/t/cursorscrollmargin-for-v6/7448
|
|
26
|
+
export const cursorLineMargin: Extension = EditorView.updateListener.of(
|
|
27
|
+
({ transactions, state, selectionSet, startState, view }) => {
|
|
28
28
|
// make sure we don't trigger an infinite loop and ignore our own changes
|
|
29
29
|
if (transactions.length < 1 || transactions.some((tr) => tr.isUserEvent(annotation))) {
|
|
30
30
|
return;
|
|
@@ -32,6 +32,7 @@ export const cursorLineMargin = (options: Options = { lines: 3 }): Extension =>
|
|
|
32
32
|
|
|
33
33
|
const s = state;
|
|
34
34
|
const { main } = s.selection;
|
|
35
|
+
const cursorLineMargin = s.facet(cursorLineMarginFacet);
|
|
35
36
|
|
|
36
37
|
// editor rect
|
|
37
38
|
const rect = view.dom.getBoundingClientRect();
|
|
@@ -58,7 +59,7 @@ export const cursorLineMargin = (options: Options = { lines: 3 }): Extension =>
|
|
|
58
59
|
|
|
59
60
|
// const needsScrollTop = mainLine <= visTopLine + cursorLineMargin;
|
|
60
61
|
const needsScrollTop = false;
|
|
61
|
-
const needsScrollBot = mainLine >= visBotLine -
|
|
62
|
+
const needsScrollBot = mainLine >= visBotLine - cursorLineMargin;
|
|
62
63
|
|
|
63
64
|
// the scroll margins are overlapping
|
|
64
65
|
if (needsScrollTop && needsScrollBot) {
|
|
@@ -87,8 +88,8 @@ export const cursorLineMargin = (options: Options = { lines: 3 }): Extension =>
|
|
|
87
88
|
annotations: Transaction.userEvent.of(annotation),
|
|
88
89
|
effects: EditorView.scrollIntoView(s.selection.main.head, {
|
|
89
90
|
y: needsScrollTop ? 'start' : 'end',
|
|
90
|
-
yMargin: view.defaultLineHeight *
|
|
91
|
+
yMargin: view.defaultLineHeight * cursorLineMargin,
|
|
91
92
|
}),
|
|
92
93
|
});
|
|
93
|
-
}
|
|
94
|
-
|
|
94
|
+
},
|
|
95
|
+
);
|
package/src/extensions/cursor.ts
CHANGED
package/src/extensions/index.ts
CHANGED
|
@@ -11,12 +11,10 @@ export * from './command';
|
|
|
11
11
|
export * from './comments';
|
|
12
12
|
export * from './cursor';
|
|
13
13
|
export * from './cursor-line-margin';
|
|
14
|
-
export * from './doc';
|
|
15
14
|
export * from './dnd';
|
|
16
|
-
export * from './factories';
|
|
17
15
|
export * from './listener';
|
|
18
16
|
export * from './markdown';
|
|
19
17
|
export * from './mention';
|
|
20
18
|
export * from './modes';
|
|
21
|
-
export * from './
|
|
19
|
+
export * from './outliner';
|
|
22
20
|
export * from './typewriter';
|
|
@@ -2,34 +2,51 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { closeBracketsKeymap } from '@codemirror/autocomplete';
|
|
6
|
-
import { historyKeymap, indentWithTab, standardKeymap } from '@codemirror/commands';
|
|
5
|
+
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
|
|
6
|
+
import { history, historyKeymap, indentWithTab, standardKeymap } from '@codemirror/commands';
|
|
7
7
|
import { markdownLanguage, markdown } from '@codemirror/lang-markdown';
|
|
8
|
-
import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
|
8
|
+
import { bracketMatching, defaultHighlightStyle, indentOnInput, syntaxHighlighting } from '@codemirror/language';
|
|
9
9
|
import { languages } from '@codemirror/language-data';
|
|
10
10
|
import { searchKeymap } from '@codemirror/search';
|
|
11
|
-
import { type Extension } from '@codemirror/state';
|
|
11
|
+
import { EditorState, type Extension } from '@codemirror/state';
|
|
12
12
|
import { oneDarkHighlightStyle } from '@codemirror/theme-one-dark';
|
|
13
|
-
import { keymap } from '@codemirror/view';
|
|
13
|
+
import { EditorView, drawSelection, dropCursor, keymap, placeholder } from '@codemirror/view';
|
|
14
14
|
|
|
15
15
|
import { type ThemeMode } from '@dxos/react-ui';
|
|
16
|
+
import { isNotFalsy } from '@dxos/util';
|
|
16
17
|
|
|
17
18
|
import { markdownHighlightStyle, markdownTagsExtensions } from './highlight';
|
|
19
|
+
import { type TextEditorProps } from '../../components';
|
|
18
20
|
|
|
19
21
|
export type MarkdownBundleOptions = {
|
|
20
22
|
themeMode?: ThemeMode;
|
|
21
|
-
}
|
|
23
|
+
} & Pick<TextEditorProps, 'lineWrapping' | 'placeholder'>;
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
|
-
*
|
|
25
|
-
* To be used in conjunction with createBasicExtensions.
|
|
26
|
-
*
|
|
26
|
+
* Markdown bundle.
|
|
27
27
|
* Refs:
|
|
28
28
|
* https://codemirror.net/docs/community
|
|
29
29
|
* https://codemirror.net/docs/ref/#codemirror.basicSetup
|
|
30
30
|
*/
|
|
31
|
-
export const createMarkdownExtensions = ({
|
|
31
|
+
export const createMarkdownExtensions = ({
|
|
32
|
+
themeMode,
|
|
33
|
+
placeholder: _placeholder,
|
|
34
|
+
lineWrapping = true,
|
|
35
|
+
}: MarkdownBundleOptions = {}): Extension[] => {
|
|
32
36
|
return [
|
|
37
|
+
// TODO(burdon): Reconcile with createBasicExtensions.
|
|
38
|
+
lineWrapping && EditorView.lineWrapping,
|
|
39
|
+
EditorState.allowMultipleSelections.of(true),
|
|
40
|
+
EditorState.tabSize.of(2),
|
|
41
|
+
|
|
42
|
+
bracketMatching(),
|
|
43
|
+
closeBrackets(),
|
|
44
|
+
dropCursor(),
|
|
45
|
+
drawSelection(),
|
|
46
|
+
history(),
|
|
47
|
+
indentOnInput(),
|
|
48
|
+
_placeholder && placeholder(_placeholder),
|
|
49
|
+
|
|
33
50
|
// Main extension.
|
|
34
51
|
// https://github.com/codemirror/lang-markdown
|
|
35
52
|
// https://codemirror.net/5/mode/markdown/index.html (demo).
|
|
@@ -69,5 +86,5 @@ export const createMarkdownExtensions = ({ themeMode }: MarkdownBundleOptions =
|
|
|
69
86
|
// https://codemirror.net/docs/ref/#commands.standardKeymap
|
|
70
87
|
...standardKeymap,
|
|
71
88
|
]),
|
|
72
|
-
];
|
|
89
|
+
].filter(isNotFalsy);
|
|
73
90
|
};
|
|
@@ -48,7 +48,7 @@ class CheckboxWidget extends WidgetType {
|
|
|
48
48
|
|
|
49
49
|
override toDOM(view: EditorView) {
|
|
50
50
|
const input = document.createElement('input');
|
|
51
|
-
input.className = 'cm-task-checkbox
|
|
51
|
+
input.className = 'cm-task-checkbox';
|
|
52
52
|
input.type = 'checkbox';
|
|
53
53
|
input.checked = this._checked;
|
|
54
54
|
if (view.state.readOnly) {
|
|
@@ -148,7 +148,9 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
|
148
148
|
marks[1].from,
|
|
149
149
|
node.to,
|
|
150
150
|
options.renderLinkButton
|
|
151
|
-
? Decoration.replace({
|
|
151
|
+
? Decoration.replace({
|
|
152
|
+
widget: new LinkButton(url, options.renderLinkButton),
|
|
153
|
+
})
|
|
152
154
|
: hide,
|
|
153
155
|
);
|
|
154
156
|
}
|
|
@@ -161,7 +161,7 @@ export const setStyle =
|
|
|
161
161
|
({ state, dispatch }) => {
|
|
162
162
|
const marker = inlineMarkerText(type);
|
|
163
163
|
const changes = state.changeByRange((range) => {
|
|
164
|
-
// Special case for markers directly around the cursor, which will often not be parsed as valid styling
|
|
164
|
+
// Special case for markers directly around the cursor, which will often not be parsed as valid styling
|
|
165
165
|
if (!enable && range.empty) {
|
|
166
166
|
const after = state.doc.sliceString(range.head, range.head + 6);
|
|
167
167
|
const found = after.indexOf(marker);
|
|
@@ -190,16 +190,16 @@ export const setStyle =
|
|
|
190
190
|
let startCovered: boolean | { from: number; to: number } = false;
|
|
191
191
|
let endCovered: boolean | { from: number; to: number } = false;
|
|
192
192
|
let { from, to } = range;
|
|
193
|
-
|
|
194
|
-
//
|
|
195
|
-
// the
|
|
193
|
+
// Iterate the selected range. For each textblock, determine a
|
|
194
|
+
// start and end position, the overlap of the selected range and
|
|
195
|
+
// the block's extent, that should be styled/unstyled.
|
|
196
196
|
syntaxTree(state).iterate({
|
|
197
197
|
from,
|
|
198
198
|
to,
|
|
199
199
|
enter: (node) => {
|
|
200
200
|
const { name } = node;
|
|
201
201
|
if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== 'codeblock') {
|
|
202
|
-
// Set up for this textblock
|
|
202
|
+
// Set up for this textblock
|
|
203
203
|
blockStart = blockContentStart(node);
|
|
204
204
|
blockEnd = blockContentEnd(node, state.doc);
|
|
205
205
|
startCovered = endCovered = false;
|
|
@@ -243,7 +243,8 @@ export const setStyle =
|
|
|
243
243
|
: true;
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
|
-
// Marks of the same type in range, or any mark if we're
|
|
246
|
+
// Marks of the same type in range, or any mark if we're
|
|
247
|
+
// adding code style, need to be removed.
|
|
247
248
|
if (markType === type || (type === Inline.Code && enable)) {
|
|
248
249
|
if (node.from >= from && openEnd <= to) {
|
|
249
250
|
changes.push({ from: node.from, to: openEnd });
|
|
@@ -270,7 +271,7 @@ export const setStyle =
|
|
|
270
271
|
},
|
|
271
272
|
leave: (node) => {
|
|
272
273
|
if (Object.hasOwn(Textblocks, node.name) && Textblocks[node.name] !== 'codeblock') {
|
|
273
|
-
// Finish opening/closing the marks for this textblock
|
|
274
|
+
// Finish opening/closing the marks for this textblock
|
|
274
275
|
const rangeStart = Math.max(from, blockStart);
|
|
275
276
|
const rangeEnd = Math.min(to, blockEnd);
|
|
276
277
|
if (enable) {
|
|
@@ -295,14 +296,12 @@ export const setStyle =
|
|
|
295
296
|
}
|
|
296
297
|
},
|
|
297
298
|
});
|
|
298
|
-
|
|
299
299
|
if (blockStart < 0 && range.empty && enable && !/\S/.test(state.doc.lineAt(range.from).text)) {
|
|
300
300
|
return {
|
|
301
301
|
changes: { from: range.head, insert: marker + marker },
|
|
302
302
|
range: EditorSelection.cursor(range.head + marker.length),
|
|
303
303
|
};
|
|
304
304
|
}
|
|
305
|
-
|
|
306
305
|
const changeSet = state.changes(changes.concat(changesAtEnd));
|
|
307
306
|
return {
|
|
308
307
|
changes: changeSet,
|
|
@@ -314,12 +313,8 @@ export const setStyle =
|
|
|
314
313
|
});
|
|
315
314
|
|
|
316
315
|
dispatch(
|
|
317
|
-
state.update(changes, {
|
|
318
|
-
userEvent: enable ? 'format.style.add' : 'format.style.remove',
|
|
319
|
-
scrollIntoView: true,
|
|
320
|
-
}),
|
|
316
|
+
state.update(changes, { userEvent: enable ? 'format.style.add' : 'format.style.remove', scrollIntoView: true }),
|
|
321
317
|
);
|
|
322
|
-
|
|
323
318
|
return true;
|
|
324
319
|
};
|
|
325
320
|
|
|
@@ -992,7 +987,7 @@ export const toggleCodeblock: StateCommand = (target) => {
|
|
|
992
987
|
|
|
993
988
|
export type FormattingOptions = {};
|
|
994
989
|
|
|
995
|
-
export const
|
|
990
|
+
export const formatting = (options: FormattingOptions = {}): Extension => {
|
|
996
991
|
return [
|
|
997
992
|
keymap.of([
|
|
998
993
|
{
|
|
@@ -61,7 +61,6 @@ const buildDecorations = (from: number, to: number, state: EditorState) => {
|
|
|
61
61
|
if (urlNode) {
|
|
62
62
|
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
63
63
|
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
64
|
-
// TODO(burdon): Doesn't load if scrolling with mouse.
|
|
65
64
|
preloadImage(url);
|
|
66
65
|
decorations.push(
|
|
67
66
|
Decoration.replace({
|
|
@@ -3,14 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { syntaxTree } from '@codemirror/language';
|
|
6
|
-
import {
|
|
7
|
-
type EditorState,
|
|
8
|
-
type Extension,
|
|
9
|
-
type RangeSet,
|
|
10
|
-
RangeSetBuilder,
|
|
11
|
-
StateField,
|
|
12
|
-
type Transaction,
|
|
13
|
-
} from '@codemirror/state';
|
|
6
|
+
import { type EditorState, type RangeSet, RangeSetBuilder, StateField, type Transaction } from '@codemirror/state';
|
|
14
7
|
import { Decoration, EditorView, WidgetType } from '@codemirror/view';
|
|
15
8
|
|
|
16
9
|
// TODO(burdon): Snippet to create basic table.
|
|
@@ -25,7 +18,7 @@ export type TableOptions = {};
|
|
|
25
18
|
* GFM tables.
|
|
26
19
|
* https://github.github.com/gfm/#tables-extension
|
|
27
20
|
*/
|
|
28
|
-
export const table = (options: TableOptions = {})
|
|
21
|
+
export const table = (options: TableOptions = {}) => {
|
|
29
22
|
return StateField.define<RangeSet<any>>({
|
|
30
23
|
create: (state) => update(state, options),
|
|
31
24
|
update: (_: RangeSet<any>, tr: Transaction) => update(tr.state, options),
|
|
@@ -83,7 +76,7 @@ const update = (state: EditorState, options: TableOptions) => {
|
|
|
83
76
|
|
|
84
77
|
tables.forEach((table) => {
|
|
85
78
|
const hide = state.readOnly || cursor < table.from || cursor > table.to;
|
|
86
|
-
|
|
79
|
+
hide &&
|
|
87
80
|
builder.add(
|
|
88
81
|
table.from,
|
|
89
82
|
table.to,
|
|
@@ -91,7 +84,6 @@ const update = (state: EditorState, options: TableOptions) => {
|
|
|
91
84
|
widget: new TableWidget(table),
|
|
92
85
|
}),
|
|
93
86
|
);
|
|
94
|
-
}
|
|
95
87
|
|
|
96
88
|
// Add class for styling.
|
|
97
89
|
builder.add(table.from, table.to, Decoration.mark({ class: 'cm-table' }));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Extension } from '@codemirror/state';
|
|
6
|
+
|
|
7
|
+
export type OutlinerOptions = {};
|
|
8
|
+
|
|
9
|
+
// TODO(burdon): Experimental component: multiple hierarchical draggable editor sections.
|
|
10
|
+
export const outliner = (options: OutlinerOptions = {}): Extension => {
|
|
11
|
+
return [];
|
|
12
|
+
};
|
|
@@ -9,7 +9,7 @@ import { log } from '@dxos/log';
|
|
|
9
9
|
import { type Space } from '@dxos/react-client/echo';
|
|
10
10
|
import { type GossipMessage } from '@dxos/react-client/mesh';
|
|
11
11
|
|
|
12
|
-
import { type AwarenessInfo, type AwarenessPosition, type AwarenessProvider, type AwarenessState } from '
|
|
12
|
+
import { type AwarenessInfo, type AwarenessPosition, type AwarenessProvider, type AwarenessState } from '../extensions';
|
|
13
13
|
|
|
14
14
|
type ProtocolMessage =
|
|
15
15
|
| {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Extension } from '@codemirror/state';
|
|
6
|
+
|
|
7
|
+
import { type DocAccessor } from '@dxos/echo-schema';
|
|
8
|
+
|
|
9
|
+
// Runtime data structure.
|
|
10
|
+
export type Range = {
|
|
11
|
+
from: number;
|
|
12
|
+
to: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Persistent data structure.
|
|
16
|
+
export type Comment = {
|
|
17
|
+
id: string;
|
|
18
|
+
cursor?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated
|
|
23
|
+
*/
|
|
24
|
+
// TODO(burdon): Remove.
|
|
25
|
+
export type EditorModel = {
|
|
26
|
+
id: string;
|
|
27
|
+
text: () => string;
|
|
28
|
+
content: string | DocAccessor;
|
|
29
|
+
extension?: Extension;
|
|
30
|
+
};
|
package/src/hooks/index.ts
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
export * from './awareness-provider';
|
|
6
|
+
export * from './defs';
|
|
7
|
+
|
|
5
8
|
export * from './useActionHandler';
|
|
6
|
-
export * from './
|
|
9
|
+
export * from './useEditorView';
|
|
7
10
|
export * from './useTextEditor';
|
|
11
|
+
export * from './useTextModel';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type EditorView } from '@codemirror/view';
|
|
6
|
+
import { type MutableRefObject, useRef, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hook for accessing the editor view.
|
|
10
|
+
* @deprecated
|
|
11
|
+
*
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const Test = () => {
|
|
14
|
+
* const [editorRef, viewInvalidated] = useEditorView();
|
|
15
|
+
* useEffect(() => {
|
|
16
|
+
* if (editorRef.current && !viewInvalidated) {
|
|
17
|
+
* editorView?.focus();
|
|
18
|
+
* }
|
|
19
|
+
* }, [editorView]);
|
|
20
|
+
* return <TextEditor ref={editorRef} />;
|
|
21
|
+
* };
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export const useEditorView = (id: string): [MutableRefObject<EditorView | null>, boolean] => {
|
|
25
|
+
const editorRef = useRef<EditorView | null>(null);
|
|
26
|
+
const [prev, setPrev] = useState<[EditorView | null, string]>([null, '']);
|
|
27
|
+
if (prev[0] !== editorRef.current) {
|
|
28
|
+
setPrev([editorRef.current, id]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return [editorRef, prev[1] !== id];
|
|
32
|
+
};
|
|
@@ -4,28 +4,34 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxosTheme';
|
|
6
6
|
|
|
7
|
-
import React, { useState } from 'react';
|
|
7
|
+
import React, { useMemo, useState } from 'react';
|
|
8
8
|
|
|
9
9
|
import { Toolbar as NaturalToolbar, Select, useThemeContext, Tooltip } from '@dxos/react-ui';
|
|
10
10
|
import { attentionSurface, mx, textBlockWidth } from '@dxos/react-ui-theme';
|
|
11
11
|
import { withTheme } from '@dxos/storybook-utils';
|
|
12
12
|
|
|
13
13
|
import { useActionHandler } from './useActionHandler';
|
|
14
|
-
import { useTextEditor } from './useTextEditor';
|
|
14
|
+
import { createBasicExtensions, createThemeExtensions, useTextEditor } from './useTextEditor';
|
|
15
15
|
import { Toolbar } from '../components';
|
|
16
|
-
import { createBasicExtensions, createThemeExtensions } from '../extensions';
|
|
17
16
|
import {
|
|
18
17
|
type EditorMode,
|
|
19
18
|
EditorModes,
|
|
20
19
|
decorateMarkdown,
|
|
21
20
|
createMarkdownExtensions,
|
|
22
|
-
|
|
21
|
+
formatting,
|
|
22
|
+
image,
|
|
23
23
|
table,
|
|
24
24
|
useFormattingState,
|
|
25
|
-
image,
|
|
26
25
|
} from '../extensions';
|
|
26
|
+
import { markdownTheme } from '../themes';
|
|
27
27
|
import translations from '../translations';
|
|
28
28
|
|
|
29
|
+
// TODO(burdon): Demo toolbar with hooks.
|
|
30
|
+
// TODO(burdon): Build components from hooks and adapters for model/extensions, etc.
|
|
31
|
+
// TODO(burdon): Remove BaseTextEditor.
|
|
32
|
+
// TODO(burdon): Move scrolling container layout/logic into TextEditor/MarkdownEditor components.
|
|
33
|
+
// TODO(burdon): Define keymap in composer framework format.
|
|
34
|
+
|
|
29
35
|
type StoryProps = {
|
|
30
36
|
autoFocus?: boolean;
|
|
31
37
|
placeholder?: string;
|
|
@@ -37,24 +43,29 @@ const Story = ({ autoFocus, placeholder, doc, readonly }: StoryProps) => {
|
|
|
37
43
|
const { themeMode } = useThemeContext();
|
|
38
44
|
const [formattingState, trackFormatting] = useFormattingState();
|
|
39
45
|
const [editorMode, setEditorMode] = useState<EditorMode>('default');
|
|
40
|
-
const
|
|
41
|
-
() =>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
const extensions = useMemo(
|
|
47
|
+
() => [
|
|
48
|
+
editorMode ? EditorModes[editorMode] : [],
|
|
49
|
+
createBasicExtensions({ readonly }),
|
|
50
|
+
createThemeExtensions({
|
|
51
|
+
themeMode,
|
|
52
|
+
theme: markdownTheme,
|
|
53
|
+
slots: {
|
|
54
|
+
editor: { className: 'p-2' },
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
// TODO(burdon): Move lineWrapping.
|
|
58
|
+
createMarkdownExtensions({ placeholder, lineWrapping: true }),
|
|
59
|
+
// TODO(burdon): Move into markdown bundle (with React callbacks).
|
|
60
|
+
decorateMarkdown(),
|
|
61
|
+
formatting(),
|
|
62
|
+
image(),
|
|
63
|
+
table(),
|
|
64
|
+
trackFormatting,
|
|
65
|
+
],
|
|
66
|
+
[editorMode, themeMode, placeholder, trackFormatting, readonly],
|
|
57
67
|
);
|
|
68
|
+
const { parentRef, view } = useTextEditor({ autoFocus, doc, extensions });
|
|
58
69
|
|
|
59
70
|
const handleAction = useActionHandler(view);
|
|
60
71
|
|
|
@@ -105,6 +116,7 @@ const EditorModeToolbar = ({
|
|
|
105
116
|
|
|
106
117
|
export default {
|
|
107
118
|
title: 'react-ui-editor/useTextEditor',
|
|
119
|
+
// component: TextEditor,
|
|
108
120
|
decorators: [withTheme],
|
|
109
121
|
render: (args: StoryProps) => (
|
|
110
122
|
<Tooltip.Provider>
|
|
@@ -115,23 +127,6 @@ export default {
|
|
|
115
127
|
};
|
|
116
128
|
|
|
117
129
|
export const Default = {
|
|
118
|
-
render: () => {
|
|
119
|
-
const { parentRef } = useTextEditor();
|
|
120
|
-
return <div role='textbox' className={mx(textBlockWidth, attentionSurface)} ref={parentRef} />;
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
export const Basic = {
|
|
125
|
-
render: () => {
|
|
126
|
-
const { themeMode } = useThemeContext();
|
|
127
|
-
const { parentRef } = useTextEditor(() => ({
|
|
128
|
-
extensions: [createBasicExtensions({ placeholder: 'Enter text...' }), createThemeExtensions({ themeMode })],
|
|
129
|
-
}));
|
|
130
|
-
return <div role='textbox' className={mx(textBlockWidth, attentionSurface)} ref={parentRef} />;
|
|
131
|
-
},
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
export const Markdown = {
|
|
135
130
|
args: {
|
|
136
131
|
autoFocus: true,
|
|
137
132
|
placeholder: 'Text...',
|