@dxos/react-ui-editor 0.3.9-main.7cfe653 → 0.3.9-main.88fb1d9
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 +134 -14
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/automerge/automerge-plugin/index.d.ts +1 -1
- package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/components/Editor/Editor.stories.d.ts +3 -2
- package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Markdown/Markdown.d.ts.map +1 -1
- package/dist/types/src/components/Markdown/Markdown.stories.d.ts +2 -1
- package/dist/types/src/components/Markdown/Markdown.stories.d.ts.map +1 -1
- package/dist/types/src/components/RichText/RichText.stories.d.ts +2 -1
- package/dist/types/src/components/RichText/RichText.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +39 -0
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +18 -0
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/index.d.ts +5 -0
- package/dist/types/src/components/TextEditor/index.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/theme.d.ts +8 -0
- package/dist/types/src/components/TextEditor/theme.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/testing/replicator.d.ts.map +1 -1
- package/package.json +19 -17
- package/src/automerge/automerge-plugin/index.ts +1 -1
- package/src/automerge/automerge.stories.tsx +1 -2
- package/src/components/Editor/Editor.stories.tsx +4 -2
- package/src/components/Editor/Editor.tsx +2 -2
- package/src/components/Markdown/Markdown.stories.tsx +3 -0
- package/src/components/Markdown/Markdown.tsx +12 -8
- package/src/components/RichText/RichText.stories.tsx +2 -0
- package/src/components/TextEditor/TextEditor.stories.tsx +33 -0
- package/src/components/TextEditor/TextEditor.tsx +130 -0
- package/src/components/TextEditor/index.ts +9 -0
- package/src/components/TextEditor/theme.ts +29 -0
- package/src/components/index.ts +1 -0
- package/src/testing/replicator.ts +0 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { closeBrackets } from '@codemirror/autocomplete';
|
|
6
|
+
import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
|
7
|
+
import { EditorState, type Extension } from '@codemirror/state';
|
|
8
|
+
import { oneDarkHighlightStyle } from '@codemirror/theme-one-dark';
|
|
9
|
+
import { EditorView, placeholder } from '@codemirror/view';
|
|
10
|
+
import React, {
|
|
11
|
+
type KeyboardEvent,
|
|
12
|
+
forwardRef,
|
|
13
|
+
useEffect,
|
|
14
|
+
useImperativeHandle,
|
|
15
|
+
useState,
|
|
16
|
+
useCallback,
|
|
17
|
+
type HTMLAttributes,
|
|
18
|
+
} from 'react';
|
|
19
|
+
import { type StyleSpec } from 'style-mod';
|
|
20
|
+
import { yCollab } from 'y-codemirror.next';
|
|
21
|
+
|
|
22
|
+
import { useThemeContext } from '@dxos/react-ui';
|
|
23
|
+
import { YText } from '@dxos/text-model';
|
|
24
|
+
|
|
25
|
+
import { defaultStyles } from './theme';
|
|
26
|
+
import { type EditorModel, type EditorSlots } from '../../model';
|
|
27
|
+
|
|
28
|
+
export type CursorInfo = {
|
|
29
|
+
from: number;
|
|
30
|
+
to: number;
|
|
31
|
+
line: number;
|
|
32
|
+
lines: number;
|
|
33
|
+
after?: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type TextEditorRef = {
|
|
37
|
+
editor: HTMLDivElement | null;
|
|
38
|
+
state?: EditorState;
|
|
39
|
+
view?: EditorView;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type TextEditorProps = {
|
|
43
|
+
model?: EditorModel;
|
|
44
|
+
extensions?: Extension[];
|
|
45
|
+
theme?: {
|
|
46
|
+
[selector: string]: StyleSpec;
|
|
47
|
+
};
|
|
48
|
+
slots?: EditorSlots;
|
|
49
|
+
onKeyDown?: (event: KeyboardEvent, info: CursorInfo) => void;
|
|
50
|
+
} & Pick<HTMLAttributes<HTMLDivElement>, 'onBlur' | 'onFocus'>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Simple text editor.
|
|
54
|
+
*/
|
|
55
|
+
export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
|
|
56
|
+
({ model, extensions = [], theme = defaultStyles, slots = {}, onKeyDown, ...props }, forwardedRef) => {
|
|
57
|
+
const { id, content } = model ?? {};
|
|
58
|
+
const { themeMode } = useThemeContext();
|
|
59
|
+
|
|
60
|
+
const [parent, setParent] = useState<HTMLDivElement | null>(null);
|
|
61
|
+
const [state, setState] = useState<EditorState>();
|
|
62
|
+
const [view, setView] = useState<EditorView>();
|
|
63
|
+
|
|
64
|
+
// TODO(burdon): The ref may be instantiated before the view is created.
|
|
65
|
+
useImperativeHandle(
|
|
66
|
+
forwardedRef,
|
|
67
|
+
() => {
|
|
68
|
+
return {
|
|
69
|
+
editor: parent,
|
|
70
|
+
state,
|
|
71
|
+
view,
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
[view],
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (!parent) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
view?.destroy();
|
|
83
|
+
|
|
84
|
+
const state = EditorState.create({
|
|
85
|
+
doc: content?.toString(),
|
|
86
|
+
extensions: [
|
|
87
|
+
bracketMatching(),
|
|
88
|
+
closeBrackets(),
|
|
89
|
+
placeholder(slots.editor?.placeholder ?? ''),
|
|
90
|
+
EditorView.lineWrapping,
|
|
91
|
+
|
|
92
|
+
// Themes.
|
|
93
|
+
EditorView.theme(theme),
|
|
94
|
+
...(themeMode === 'dark'
|
|
95
|
+
? [syntaxHighlighting(oneDarkHighlightStyle)]
|
|
96
|
+
: [syntaxHighlighting(defaultHighlightStyle)]),
|
|
97
|
+
|
|
98
|
+
// Replication.
|
|
99
|
+
...(content instanceof YText ? [yCollab(content, undefined)] : []),
|
|
100
|
+
|
|
101
|
+
// Custom.
|
|
102
|
+
...extensions,
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
setState(state);
|
|
107
|
+
setView(new EditorView({ state, parent }));
|
|
108
|
+
|
|
109
|
+
return () => {
|
|
110
|
+
view?.destroy();
|
|
111
|
+
setView(undefined);
|
|
112
|
+
setState(undefined);
|
|
113
|
+
};
|
|
114
|
+
}, [parent, content, themeMode]);
|
|
115
|
+
|
|
116
|
+
const handleKeyDown = useCallback(
|
|
117
|
+
(event: KeyboardEvent) => {
|
|
118
|
+
if (view) {
|
|
119
|
+
const { head, from, to } = view.state.selection.ranges[0];
|
|
120
|
+
const { number } = view.state.doc.lineAt(head);
|
|
121
|
+
const after = view.state.sliceDoc(from);
|
|
122
|
+
onKeyDown?.(event, { from, to, line: number, lines: view.state.doc.lines, after });
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
[view],
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
return <div key={id} ref={setParent} {...slots.root} onKeyDown={handleKeyDown} {...props} />;
|
|
129
|
+
},
|
|
130
|
+
);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import get from 'lodash.get';
|
|
6
|
+
import { type StyleSpec } from 'style-mod';
|
|
7
|
+
|
|
8
|
+
import { tailwindConfig, type TailwindConfig } from '@dxos/react-ui-theme';
|
|
9
|
+
|
|
10
|
+
const tokens: TailwindConfig['theme'] = tailwindConfig({}).theme;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* https://codemirror.net/examples/styling
|
|
14
|
+
*/
|
|
15
|
+
// TODO(burdon): If given a "theme" suffix, `__docgen` properties are added to the object.
|
|
16
|
+
export const defaultStyles: {
|
|
17
|
+
[selector: string]: StyleSpec;
|
|
18
|
+
} = {
|
|
19
|
+
'&.cm-focused': {
|
|
20
|
+
outline: 'none',
|
|
21
|
+
},
|
|
22
|
+
'.cm-placeholder': {
|
|
23
|
+
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
24
|
+
},
|
|
25
|
+
'& .cm-scroller': {
|
|
26
|
+
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
27
|
+
overflow: 'visible',
|
|
28
|
+
},
|
|
29
|
+
};
|
package/src/components/index.ts
CHANGED