@dxos/react-ui-editor 0.3.11-main.cb8120f → 0.3.11-main.ccc0ca3
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 +362 -45
- 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 -2
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -1
- package/dist/types/src/components/TextEditor/hooks.stories.d.ts +23 -0
- package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +23 -7
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +10 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/index.d.ts +2 -0
- package/dist/types/src/components/Toolbar/index.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/extensions/automerge/update-automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/basic.d.ts +1 -1
- package/dist/types/src/extensions/basic.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts +1 -1
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts +2 -2
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +2 -0
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useActionHandler.d.ts +2 -2
- package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -1
- package/dist/types/src/hooks/useEditorView.d.ts +17 -0
- package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts +36 -14
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/themes/default.d.ts.map +1 -1
- package/package.json +26 -25
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +2 -3
- package/src/components/TextEditor/TextEditor.tsx +23 -13
- package/src/components/TextEditor/comments.stories.tsx +3 -3
- package/src/components/TextEditor/hooks.stories.tsx +106 -0
- package/src/components/Toolbar/Toolbar.stories.tsx +7 -3
- package/src/components/Toolbar/Toolbar.tsx +120 -69
- package/src/components/Toolbar/index.ts +5 -0
- package/src/components/index.ts +1 -0
- package/src/extensions/automerge/update-automerge.ts +7 -7
- package/src/extensions/basic.ts +15 -13
- package/src/extensions/comments.ts +2 -2
- package/src/extensions/markdown/bundle.ts +10 -5
- package/src/extensions/markdown/formatting.ts +31 -2
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useActionHandler.ts +22 -5
- package/src/hooks/useEditorView.ts +29 -0
- package/src/hooks/useTextEditor.ts +143 -19
- package/src/themes/default.ts +3 -1
|
@@ -1,17 +1,39 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import {
|
|
1
|
+
import { type EditorStateConfig, type Extension, type StateEffect } from '@codemirror/state';
|
|
2
|
+
import { EditorView } from '@codemirror/view';
|
|
3
|
+
import { type RefObject } from 'react';
|
|
4
|
+
import { type ThemeMode } from '@dxos/react-ui';
|
|
5
|
+
import { type ThemeStyles } from '..//styles';
|
|
6
|
+
export type UseTextEditorOptions = {
|
|
7
|
+
autoFocus?: boolean;
|
|
8
|
+
scrollTo?: StateEffect<any>;
|
|
9
|
+
debug?: boolean;
|
|
10
|
+
} & EditorStateConfig;
|
|
11
|
+
export type UseTextEditor = {
|
|
12
|
+
parentRef: RefObject<HTMLDivElement>;
|
|
13
|
+
view?: EditorView;
|
|
14
|
+
};
|
|
3
15
|
/**
|
|
4
|
-
* Hook for
|
|
5
|
-
*
|
|
6
|
-
* ```tsx
|
|
7
|
-
* const Test = () => {
|
|
8
|
-
* const [editorRef, editorView] = useTextEditor();
|
|
9
|
-
* useEffect(() => {
|
|
10
|
-
* editorView?.focus();
|
|
11
|
-
* }, [editorView]);
|
|
12
|
-
* return <TextEditor ref={editorRef} />;
|
|
13
|
-
* };
|
|
14
|
-
* ```
|
|
16
|
+
* Hook for creating editor.
|
|
15
17
|
*/
|
|
16
|
-
export declare const useTextEditor: (
|
|
18
|
+
export declare const useTextEditor: ({ autoFocus, scrollTo, debug, doc, selection, extensions, }?: UseTextEditorOptions) => UseTextEditor;
|
|
19
|
+
export type DataExtensionsOptions = {
|
|
20
|
+
readonly?: boolean;
|
|
21
|
+
};
|
|
22
|
+
export declare const createDataExtensions: ({ readonly }?: {
|
|
23
|
+
readonly?: boolean | undefined;
|
|
24
|
+
}) => Extension;
|
|
25
|
+
export type ThemeExtensionsOptions = {
|
|
26
|
+
theme?: ThemeStyles;
|
|
27
|
+
themeMode?: ThemeMode;
|
|
28
|
+
lineWrap?: boolean;
|
|
29
|
+
slots?: {
|
|
30
|
+
editor?: {
|
|
31
|
+
className?: string;
|
|
32
|
+
};
|
|
33
|
+
content?: {
|
|
34
|
+
className?: string;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
export declare const createThemeExtensions: ({ theme, themeMode, slots }?: ThemeExtensionsOptions) => Extension;
|
|
17
39
|
//# sourceMappingURL=useTextEditor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTextEditor.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTextEditor.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"useTextEditor.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTextEditor.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,KAAK,SAAS,EAA+B,MAAM,OAAO,CAAC;AAGpE,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,iBAAiB,CAAC;AAEtB,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,iEAOvB,oBAAoB,KAAQ,aAqE9B,CAAC;AAIF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAGF,eAAO,MAAM,oBAAoB;;MAAgC,SAMhE,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE;QACN,MAAM,CAAC,EAAE;YACP,SAAS,CAAC,EAAE,MAAM,CAAC;SACpB,CAAC;QACF,OAAO,CAAC,EAAE;YACR,SAAS,CAAC,EAAE,MAAM,CAAC;SACpB,CAAC;KACH,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,qBAAqB,iCAAiC,sBAAsB,KAAQ,SAShG,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../../src/themes/default.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,WAAW,EAAU,MAAM,WAAW,CAAC;AAKrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../../src/themes/default.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,WAAW,EAAU,MAAM,WAAW,CAAC;AAKrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,EAAE,WAiO1B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAOvB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,WAO3B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAOvB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-editor",
|
|
3
|
-
"version": "0.3.11-main.
|
|
3
|
+
"version": "0.3.11-main.ccc0ca3",
|
|
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",
|
|
@@ -23,10 +23,11 @@
|
|
|
23
23
|
"@codemirror/state": "^6.4.0",
|
|
24
24
|
"@codemirror/theme-one-dark": "^6.1.2",
|
|
25
25
|
"@codemirror/view": "^6.23.0",
|
|
26
|
-
"@fluentui/react-tabster": "^9.
|
|
26
|
+
"@fluentui/react-tabster": "^9.17.4",
|
|
27
27
|
"@lezer/common": "^1.2.1",
|
|
28
28
|
"@lezer/highlight": "^1.2.0",
|
|
29
29
|
"@lezer/markdown": "^1.2.0",
|
|
30
|
+
"@radix-ui/react-context": "^1.0.0",
|
|
30
31
|
"@replit/codemirror-vim": "^6.0.14",
|
|
31
32
|
"codemirror": "6.0.1",
|
|
32
33
|
"lib0": "^0.2.65",
|
|
@@ -35,20 +36,20 @@
|
|
|
35
36
|
"lodash.pick": "^4.4.0",
|
|
36
37
|
"lodash.sortby": "^4.7.0",
|
|
37
38
|
"style-mod": "^4.1.0",
|
|
38
|
-
"@dxos/async": "0.3.11-main.
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
41
|
-
"@dxos/display-name": "0.3.11-main.
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/echo-schema": "0.3.11-main.
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/log": "0.3.11-main.
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/react-
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/text-model": "0.3.11-main.
|
|
51
|
-
"@dxos/
|
|
39
|
+
"@dxos/async": "0.3.11-main.ccc0ca3",
|
|
40
|
+
"@dxos/automerge": "0.3.11-main.ccc0ca3",
|
|
41
|
+
"@dxos/context": "0.3.11-main.ccc0ca3",
|
|
42
|
+
"@dxos/display-name": "0.3.11-main.ccc0ca3",
|
|
43
|
+
"@dxos/debug": "0.3.11-main.ccc0ca3",
|
|
44
|
+
"@dxos/echo-schema": "0.3.11-main.ccc0ca3",
|
|
45
|
+
"@dxos/invariant": "0.3.11-main.ccc0ca3",
|
|
46
|
+
"@dxos/log": "0.3.11-main.ccc0ca3",
|
|
47
|
+
"@dxos/react-async": "0.3.11-main.ccc0ca3",
|
|
48
|
+
"@dxos/react-ui": "0.3.11-main.ccc0ca3",
|
|
49
|
+
"@dxos/protocols": "0.3.11-main.ccc0ca3",
|
|
50
|
+
"@dxos/react-ui-theme": "0.3.11-main.ccc0ca3",
|
|
51
|
+
"@dxos/text-model": "0.3.11-main.ccc0ca3",
|
|
52
|
+
"@dxos/util": "0.3.11-main.ccc0ca3"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"@automerge/automerge-repo-network-broadcastchannel": "^1.1.0",
|
|
@@ -76,24 +77,24 @@
|
|
|
76
77
|
"vite-plugin-top-level-await": "^1.4.1",
|
|
77
78
|
"vite-plugin-wasm": "^3.3.0",
|
|
78
79
|
"vitest": "^1.2.1",
|
|
79
|
-
"@dxos/config": "0.3.11-main.
|
|
80
|
-
"@dxos/echo-signals": "0.3.11-main.
|
|
81
|
-
"@dxos/
|
|
82
|
-
"@dxos/
|
|
83
|
-
"@dxos/
|
|
84
|
-
"@dxos/
|
|
85
|
-
"@dxos/
|
|
80
|
+
"@dxos/config": "0.3.11-main.ccc0ca3",
|
|
81
|
+
"@dxos/echo-signals": "0.3.11-main.ccc0ca3",
|
|
82
|
+
"@dxos/react-client": "0.3.11-main.ccc0ca3",
|
|
83
|
+
"@dxos/react-ui": "0.3.11-main.ccc0ca3",
|
|
84
|
+
"@dxos/storybook-utils": "0.3.11-main.ccc0ca3",
|
|
85
|
+
"@dxos/echo-typegen": "0.3.11-main.ccc0ca3",
|
|
86
|
+
"@dxos/keyboard": "0.3.11-main.ccc0ca3"
|
|
86
87
|
},
|
|
87
88
|
"peerDependencies": {
|
|
88
89
|
"@phosphor-icons/react": "^2.0.5",
|
|
89
90
|
"react": "^18.2.0",
|
|
90
91
|
"react-dom": "^18.2.0",
|
|
91
|
-
"@dxos/react-client": "0.3.11-main.
|
|
92
|
+
"@dxos/react-client": "0.3.11-main.ccc0ca3"
|
|
92
93
|
},
|
|
93
94
|
"publishConfig": {
|
|
94
95
|
"access": "public"
|
|
95
96
|
},
|
|
96
97
|
"scripts": {
|
|
97
|
-
"vitest": "vitest --ui
|
|
98
|
+
"vitest": "vitest --ui"
|
|
98
99
|
}
|
|
99
100
|
}
|
|
@@ -211,11 +211,10 @@ const onRenderLink: LinkOptions['onRender'] = (el, url) => {
|
|
|
211
211
|
type StoryProps = {
|
|
212
212
|
text?: string;
|
|
213
213
|
comments?: Comment[];
|
|
214
|
-
automerge?: boolean;
|
|
215
214
|
} & Pick<TextEditorProps, 'readonly' | 'placeholder' | 'slots' | 'extensions'>;
|
|
216
215
|
|
|
217
|
-
const Story = ({ text, comments,
|
|
218
|
-
const [item] = useState({ text: new TextObject(text
|
|
216
|
+
const Story = ({ text, comments, placeholder = 'New document.', ...props }: StoryProps) => {
|
|
217
|
+
const [item] = useState({ text: new TextObject(text) });
|
|
219
218
|
const view = useRef<EditorView>(null);
|
|
220
219
|
const model = useTextModel({ text: item.text });
|
|
221
220
|
useComments(view.current, comments);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { EditorState, type Extension, type StateEffect } from '@codemirror/state';
|
|
6
6
|
import { EditorView } from '@codemirror/view';
|
|
7
|
-
import { useFocusableGroup } from '@fluentui/react-tabster';
|
|
7
|
+
// import { useFocusableGroup } from '@fluentui/react-tabster';
|
|
8
8
|
import { vim } from '@replit/codemirror-vim';
|
|
9
9
|
import defaultsDeep from 'lodash.defaultsdeep';
|
|
10
10
|
import React, {
|
|
@@ -21,9 +21,9 @@ import React, {
|
|
|
21
21
|
import { log } from '@dxos/log';
|
|
22
22
|
import { useThemeContext } from '@dxos/react-ui';
|
|
23
23
|
import { attentionSurface, mx } from '@dxos/react-ui-theme';
|
|
24
|
-
import {
|
|
24
|
+
import { isNotFalsy } from '@dxos/util';
|
|
25
25
|
|
|
26
|
-
import {
|
|
26
|
+
import { createBasicBundle, createMarkdownExtensions } from '../../extensions';
|
|
27
27
|
import { type EditorModel } from '../../hooks';
|
|
28
28
|
import { type ThemeStyles } from '../../styles';
|
|
29
29
|
import { defaultTheme, markdownTheme, textTheme } from '../../themes';
|
|
@@ -87,7 +87,12 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
87
87
|
},
|
|
88
88
|
forwardedRef,
|
|
89
89
|
) => {
|
|
90
|
-
|
|
90
|
+
// TODO(burdon): Hook causes error even if properties are not spread into div.
|
|
91
|
+
// Uncaught TypeError: Cannot read properties of undefined (reading 'relatedTarget')
|
|
92
|
+
// Uses event.detail, which is deprecated (not event.details). At runtime the event has a property `details`.
|
|
93
|
+
// https://github.com/microsoft/tabster/blob/master/src/State/FocusedElement.ts#L348 (e.detail.relatedTarget)
|
|
94
|
+
// https://github.com/microsoft/keyborg/blob/49e49b2c3ba0a5f6cc518ac46825d7551def8109/src/FocusEvent.ts#L58
|
|
95
|
+
// const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
|
|
91
96
|
const { themeMode } = useThemeContext();
|
|
92
97
|
|
|
93
98
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
@@ -99,7 +104,12 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
99
104
|
|
|
100
105
|
// Set focus.
|
|
101
106
|
useEffect(() => {
|
|
102
|
-
if (autoFocus) {
|
|
107
|
+
if (autoFocus && !view?.hasFocus) {
|
|
108
|
+
if (view?.state.selection.main?.from === 0) {
|
|
109
|
+
// Start at end of line.
|
|
110
|
+
const { to } = view.state.doc.lineAt(0);
|
|
111
|
+
view?.dispatch({ selection: { anchor: to } });
|
|
112
|
+
}
|
|
103
113
|
view?.focus();
|
|
104
114
|
}
|
|
105
115
|
}, [view, autoFocus]);
|
|
@@ -140,12 +150,12 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
140
150
|
// NOTE: This must come before user extensions.
|
|
141
151
|
model.extension,
|
|
142
152
|
|
|
143
|
-
// TODO(burdon): Factor out (
|
|
153
|
+
// TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
|
|
144
154
|
editorMode === 'vim' && vim(),
|
|
145
155
|
|
|
146
156
|
// Custom.
|
|
147
157
|
...extensions,
|
|
148
|
-
].filter(
|
|
158
|
+
].filter(isNotFalsy),
|
|
149
159
|
});
|
|
150
160
|
|
|
151
161
|
//
|
|
@@ -177,6 +187,7 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
177
187
|
|
|
178
188
|
// Handles tab/focus.
|
|
179
189
|
// Pressing Escape focuses the outer div (to support tab navigation); pressing Enter refocuses the editor.
|
|
190
|
+
// TODO(burdon): Convert to keymap?
|
|
180
191
|
const handleKeyUp = useCallback(
|
|
181
192
|
(event: KeyboardEvent) => {
|
|
182
193
|
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
@@ -188,7 +199,7 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
188
199
|
|
|
189
200
|
case 'Escape': {
|
|
190
201
|
if (editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey)) {
|
|
191
|
-
|
|
202
|
+
view?.focus();
|
|
192
203
|
}
|
|
193
204
|
break;
|
|
194
205
|
}
|
|
@@ -202,16 +213,15 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
202
213
|
key={model.id}
|
|
203
214
|
role='none'
|
|
204
215
|
tabIndex={0}
|
|
205
|
-
onKeyUp={handleKeyUp}
|
|
206
216
|
{...slots.root}
|
|
207
|
-
{...(editorMode !== 'vim' && tabsterDOMAttribute)}
|
|
217
|
+
// {...(editorMode !== 'vim' && tabsterDOMAttribute)}
|
|
218
|
+
onKeyUp={handleKeyUp}
|
|
208
219
|
ref={rootRef}
|
|
209
220
|
/>
|
|
210
221
|
);
|
|
211
222
|
},
|
|
212
223
|
);
|
|
213
224
|
|
|
214
|
-
// TODO(burdon): Single-line/scroll.
|
|
215
225
|
export const TextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
216
226
|
({ readonly, placeholder, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
|
|
217
227
|
const { themeMode } = useThemeContext();
|
|
@@ -220,7 +230,7 @@ export const TextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
220
230
|
<BaseTextEditor
|
|
221
231
|
ref={forwardedRef}
|
|
222
232
|
readonly={readonly}
|
|
223
|
-
extensions={[
|
|
233
|
+
extensions={[createBasicBundle({ themeMode, placeholder, lineWrapping }), ...extensions]}
|
|
224
234
|
theme={theme}
|
|
225
235
|
slots={updatedSlots}
|
|
226
236
|
{...props}
|
|
@@ -237,7 +247,7 @@ export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
237
247
|
<BaseTextEditor
|
|
238
248
|
ref={forwardedRef}
|
|
239
249
|
readonly={readonly}
|
|
240
|
-
extensions={[
|
|
250
|
+
extensions={[createMarkdownExtensions({ themeMode, placeholder }), ...extensions]}
|
|
241
251
|
theme={theme}
|
|
242
252
|
slots={updatedSlots}
|
|
243
253
|
{...props}
|
|
@@ -17,7 +17,7 @@ import { fixedInsetFlexLayout, mx } from '@dxos/react-ui-theme';
|
|
|
17
17
|
import { withTheme } from '@dxos/storybook-utils';
|
|
18
18
|
|
|
19
19
|
import { MarkdownEditor, TextEditor } from './TextEditor';
|
|
20
|
-
import { comments, type CommentsOptions,
|
|
20
|
+
import { comments, type CommentsOptions, focusComment, useComments } from '../../extensions';
|
|
21
21
|
import { type Comment, type Range, useTextModel } from '../../hooks';
|
|
22
22
|
|
|
23
23
|
faker.seed(101);
|
|
@@ -50,7 +50,7 @@ const Editor: FC<{
|
|
|
50
50
|
if (!view.current?.hasFocus && selectedValue !== selected) {
|
|
51
51
|
setSelected(selectedValue);
|
|
52
52
|
if (selectedValue) {
|
|
53
|
-
|
|
53
|
+
focusComment(view.current!, selectedValue);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
}, [selected, commentRanges, selectedValue]);
|
|
@@ -150,7 +150,7 @@ const Thread: FC<{
|
|
|
150
150
|
</div>
|
|
151
151
|
))}
|
|
152
152
|
|
|
153
|
-
<div ref={containerRef} onClick={() => onSelect()}
|
|
153
|
+
<div ref={containerRef} className='flex' onClick={() => onSelect()}>
|
|
154
154
|
<TextEditor
|
|
155
155
|
ref={editorRef}
|
|
156
156
|
autoFocus={focus}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxosTheme';
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
import { useThemeContext } from '@dxos/react-ui';
|
|
10
|
+
import { fixedInsetFlexLayout, groupSurface, mx } from '@dxos/react-ui-theme';
|
|
11
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
12
|
+
|
|
13
|
+
import { TextEditor } from './TextEditor';
|
|
14
|
+
import {
|
|
15
|
+
code,
|
|
16
|
+
createMarkdownExtensions,
|
|
17
|
+
formatting,
|
|
18
|
+
heading,
|
|
19
|
+
hr,
|
|
20
|
+
image,
|
|
21
|
+
link,
|
|
22
|
+
table,
|
|
23
|
+
tasklist,
|
|
24
|
+
} from '../../extensions';
|
|
25
|
+
import { createDataExtensions, createThemeExtensions, useActionHandler, useTextEditor } from '../../hooks';
|
|
26
|
+
import { markdownTheme } from '../../themes';
|
|
27
|
+
import { Toolbar } from '../Toolbar';
|
|
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
|
+
|
|
35
|
+
type StoryProps = {
|
|
36
|
+
autoFocus?: boolean;
|
|
37
|
+
placeholder?: string;
|
|
38
|
+
doc?: string;
|
|
39
|
+
readonly?: boolean;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const Story = ({ autoFocus, placeholder, doc, readonly }: StoryProps) => {
|
|
43
|
+
const { themeMode } = useThemeContext();
|
|
44
|
+
const { parentRef, view } = useTextEditor({
|
|
45
|
+
autoFocus,
|
|
46
|
+
doc,
|
|
47
|
+
extensions: [
|
|
48
|
+
//
|
|
49
|
+
createDataExtensions({ readonly }),
|
|
50
|
+
createThemeExtensions({
|
|
51
|
+
themeMode,
|
|
52
|
+
theme: markdownTheme,
|
|
53
|
+
slots: {
|
|
54
|
+
// TODO(burdon): Document classes re base theme.
|
|
55
|
+
// Semantic tokens (e.g., replacement of input surface).
|
|
56
|
+
editor: {
|
|
57
|
+
className: 'h-full p-4 bg-white text-black dark:bg-black dark:text-white',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
// TODO(burdon): Move lineWrapping.
|
|
62
|
+
createMarkdownExtensions({ placeholder, lineWrapping: true }),
|
|
63
|
+
// TODO(burdon): Move into markdown bundle (with React callbacks).
|
|
64
|
+
code(),
|
|
65
|
+
formatting(),
|
|
66
|
+
heading(),
|
|
67
|
+
hr(),
|
|
68
|
+
image(),
|
|
69
|
+
link(),
|
|
70
|
+
table(),
|
|
71
|
+
tasklist(),
|
|
72
|
+
],
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const handleAction = useActionHandler(view);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div className={mx(fixedInsetFlexLayout, groupSurface)}>
|
|
79
|
+
<div className='flex h-full justify-center'>
|
|
80
|
+
<div className='flex flex-col h-full w-[800px]'>
|
|
81
|
+
<Toolbar.Root onAction={handleAction}>
|
|
82
|
+
<Toolbar.Markdown />
|
|
83
|
+
</Toolbar.Root>
|
|
84
|
+
|
|
85
|
+
{/* TODO(burdon): Handle scrolling in component wrapper (like this). */}
|
|
86
|
+
<div role='none' className='h-full overflow-y-auto' ref={parentRef} />
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default {
|
|
94
|
+
title: 'react-ui-editor/useTextEditor',
|
|
95
|
+
component: TextEditor,
|
|
96
|
+
decorators: [withTheme],
|
|
97
|
+
render: (args: StoryProps) => <Story {...args} />,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const Default = {
|
|
101
|
+
args: {
|
|
102
|
+
autoFocus: true,
|
|
103
|
+
placeholder: 'Text...',
|
|
104
|
+
doc: '# Demo\n\nThis is a document.\n\n',
|
|
105
|
+
},
|
|
106
|
+
};
|
|
@@ -14,7 +14,7 @@ import { withTheme } from '@dxos/storybook-utils';
|
|
|
14
14
|
|
|
15
15
|
import { Toolbar } from './Toolbar';
|
|
16
16
|
import { code, comments, formatting, heading, image, table, tasklist, useComments } from '../../extensions';
|
|
17
|
-
import { type Comment, useActionHandler,
|
|
17
|
+
import { type Comment, useActionHandler, useEditorView, useTextModel } from '../../hooks';
|
|
18
18
|
import { MarkdownEditor } from '../TextEditor';
|
|
19
19
|
|
|
20
20
|
faker.seed(101);
|
|
@@ -22,7 +22,7 @@ faker.seed(101);
|
|
|
22
22
|
const Story: FC<{ content: string }> = ({ content }) => {
|
|
23
23
|
const [item] = useState({ text: new TextObject(content) });
|
|
24
24
|
const [_comments, setComments] = useState<Comment[]>([]);
|
|
25
|
-
const [editorRef, editorView] =
|
|
25
|
+
const [editorRef, editorView] = useEditorView();
|
|
26
26
|
const model = useTextModel({ text: item.text });
|
|
27
27
|
const extensions = useMemo(
|
|
28
28
|
() => [
|
|
@@ -54,7 +54,11 @@ const Story: FC<{ content: string }> = ({ content }) => {
|
|
|
54
54
|
<div className={mx(fixedInsetFlexLayout, groupSurface)}>
|
|
55
55
|
<div className='flex h-full justify-center'>
|
|
56
56
|
<div className='flex flex-col h-full w-[800px]'>
|
|
57
|
-
<Toolbar onAction={handleAction}
|
|
57
|
+
<Toolbar.Root onAction={handleAction}>
|
|
58
|
+
<Toolbar.Markdown />
|
|
59
|
+
<Toolbar.Separator />
|
|
60
|
+
<Toolbar.Extended />
|
|
61
|
+
</Toolbar.Root>
|
|
58
62
|
<MarkdownEditor ref={editorRef} model={model} extensions={extensions} />
|
|
59
63
|
</div>
|
|
60
64
|
</div>
|