@dxos/react-ui-editor 0.4.2 → 0.4.3-main.085f4d3
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 +290 -212
- 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 +23 -0
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +0 -3
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +20 -0
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/automerge.stories.d.ts +22 -2
- package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/hooks.stories.d.ts +20 -0
- package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +7 -10
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +23 -6
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/{semaphore.d.ts → sync.d.ts} +6 -5
- package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -0
- 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/modes.d.ts +11 -0
- package/dist/types/src/extensions/modes.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/styles/index.d.ts +1 -0
- package/dist/types/src/styles/index.d.ts.map +1 -1
- package/dist/types/src/styles/layout.d.ts +2 -0
- package/dist/types/src/styles/layout.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +20 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/package.json +25 -25
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +23 -17
- package/src/components/TextEditor/TextEditor.stories.tsx +11 -14
- package/src/components/TextEditor/TextEditor.tsx +32 -53
- package/src/components/TextEditor/automerge.stories.tsx +38 -44
- package/src/components/TextEditor/hooks.stories.tsx +15 -16
- package/src/components/Toolbar/Toolbar.stories.tsx +16 -12
- package/src/components/Toolbar/Toolbar.tsx +156 -129
- package/src/extensions/automerge/automerge.ts +5 -21
- package/src/extensions/automerge/{semaphore.ts → sync.ts} +32 -34
- package/src/extensions/index.ts +1 -0
- package/src/extensions/modes.ts +36 -0
- package/src/index.ts +4 -3
- package/src/styles/index.ts +1 -0
- package/src/styles/layout.ts +6 -0
- package/src/translations.ts +25 -0
- package/dist/types/src/extensions/automerge/semaphore.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/automerge/sync.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,KAAK,EAIX,MAAM,QAAQ,CAAC;AAIhB;;GAEG;AACH,qBAAa,MAAM;IAKf,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IALzB,OAAO,CAAC,QAAQ,CAAS;gBAIN,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC;IAG5C,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO;IAe3C,cAAc,CAAC,IAAI,EAAE,UAAU;IAa/B,iBAAiB,CAAC,IAAI,EAAE,UAAU;CAenC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/extensions/index.ts"],"names":[],"mappings":"AAIA,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/extensions/index.ts"],"names":[],"mappings":"AAIA,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Extension, Facet } from '@codemirror/state';
|
|
2
|
+
export type EditorMode = 'default' | 'vim' | undefined;
|
|
3
|
+
export type EditorConfig = {
|
|
4
|
+
type: string;
|
|
5
|
+
noTabster?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare const editorMode: Facet<EditorConfig, EditorConfig>;
|
|
8
|
+
export declare const EditorModes: {
|
|
9
|
+
[mode: string]: Extension;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=modes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modes.d.ts","sourceRoot":"","sources":["../../../../src/extensions/modes.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAI1D,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,KAAK,GAAG,SAAS,CAAC;AAEvD,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,UAAU,mCAErB,CAAC;AAEH,eAAO,MAAM,WAAW,EAAE;IAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CAgBpD,CAAC"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
export { type Extension, type EditorState } from '@codemirror/state';
|
|
2
|
+
export { type EditorView, keymap } from '@codemirror/view';
|
|
1
3
|
export { TextKind } from '@dxos/protocols/proto/dxos/echo/model/text';
|
|
2
4
|
export { Doc, YText, YXmlFragment } from '@dxos/text-model';
|
|
3
|
-
export { type Extension } from '@codemirror/state';
|
|
4
|
-
export { type EditorView, keymap } from '@codemirror/view';
|
|
5
5
|
export * from './components';
|
|
6
6
|
export * from './extensions';
|
|
7
7
|
export * from './hooks';
|
|
8
|
-
export { getToken } from './styles';
|
|
8
|
+
export { getToken, editorWithToolbarLayout } from './styles';
|
|
9
9
|
export * from './themes';
|
|
10
10
|
export * from './util';
|
|
11
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAAE,QAAQ,EAAE,MAAM,4CAA4C,CAAC;AACtE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE5D,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAC7D,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/styles/index.ts"],"names":[],"mappings":"AAIA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/styles/index.ts"],"names":[],"mappings":"AAIA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../src/styles/layout.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,4GACuE,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const translationKey = "react-ui-editor";
|
|
2
|
+
declare const _default: {
|
|
3
|
+
'en-US': {
|
|
4
|
+
"react-ui-editor": {
|
|
5
|
+
'strong label': string;
|
|
6
|
+
'emphasis label': string;
|
|
7
|
+
'strikethrough label': string;
|
|
8
|
+
'code label': string;
|
|
9
|
+
'bullet label': string;
|
|
10
|
+
'ordered label': string;
|
|
11
|
+
'task label': string;
|
|
12
|
+
'blockquote label': string;
|
|
13
|
+
'codeblock label': string;
|
|
14
|
+
'comment label': string;
|
|
15
|
+
'heading label': string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
}[];
|
|
19
|
+
export default _default;
|
|
20
|
+
//# sourceMappingURL=translations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;AAEhD,wBAkBE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-editor",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3-main.085f4d3",
|
|
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",
|
|
@@ -36,23 +36,23 @@
|
|
|
36
36
|
"lodash.pick": "^4.4.0",
|
|
37
37
|
"lodash.sortby": "^4.7.0",
|
|
38
38
|
"style-mod": "^4.1.0",
|
|
39
|
-
"@dxos/async": "0.4.
|
|
40
|
-
"@dxos/context": "0.4.
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/debug": "0.4.
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/invariant": "0.4.
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/react-
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/react-ui-theme": "0.4.
|
|
51
|
-
"@dxos/text-model": "0.4.
|
|
52
|
-
"@dxos/util": "0.4.
|
|
39
|
+
"@dxos/async": "0.4.3-main.085f4d3",
|
|
40
|
+
"@dxos/context": "0.4.3-main.085f4d3",
|
|
41
|
+
"@dxos/automerge": "0.4.3-main.085f4d3",
|
|
42
|
+
"@dxos/debug": "0.4.3-main.085f4d3",
|
|
43
|
+
"@dxos/display-name": "0.4.3-main.085f4d3",
|
|
44
|
+
"@dxos/echo-schema": "0.4.3-main.085f4d3",
|
|
45
|
+
"@dxos/invariant": "0.4.3-main.085f4d3",
|
|
46
|
+
"@dxos/log": "0.4.3-main.085f4d3",
|
|
47
|
+
"@dxos/protocols": "0.4.3-main.085f4d3",
|
|
48
|
+
"@dxos/react-ui": "0.4.3-main.085f4d3",
|
|
49
|
+
"@dxos/react-async": "0.4.3-main.085f4d3",
|
|
50
|
+
"@dxos/react-ui-theme": "0.4.3-main.085f4d3",
|
|
51
|
+
"@dxos/text-model": "0.4.3-main.085f4d3",
|
|
52
|
+
"@dxos/util": "0.4.3-main.085f4d3"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@automerge/automerge-repo-network-broadcastchannel": "^1.1.
|
|
55
|
+
"@automerge/automerge-repo-network-broadcastchannel": "^1.1.1",
|
|
56
56
|
"@phosphor-icons/react": "^2.0.5",
|
|
57
57
|
"@preact/signals-react": "^1.3.6",
|
|
58
58
|
"@testing-library/dom": "^8.17.1",
|
|
@@ -76,20 +76,20 @@
|
|
|
76
76
|
"vite-plugin-top-level-await": "^1.4.1",
|
|
77
77
|
"vite-plugin-wasm": "^3.3.0",
|
|
78
78
|
"vitest": "^1.2.1",
|
|
79
|
-
"@dxos/config": "0.4.
|
|
80
|
-
"@dxos/echo-
|
|
81
|
-
"@dxos/echo-
|
|
82
|
-
"@dxos/
|
|
83
|
-
"@dxos/
|
|
84
|
-
"@dxos/react-
|
|
85
|
-
"@dxos/react-
|
|
86
|
-
"@dxos/storybook-utils": "0.4.
|
|
79
|
+
"@dxos/config": "0.4.3-main.085f4d3",
|
|
80
|
+
"@dxos/echo-signals": "0.4.3-main.085f4d3",
|
|
81
|
+
"@dxos/echo-typegen": "0.4.3-main.085f4d3",
|
|
82
|
+
"@dxos/keyboard": "0.4.3-main.085f4d3",
|
|
83
|
+
"@dxos/random": "0.4.3-main.085f4d3",
|
|
84
|
+
"@dxos/react-ui": "0.4.3-main.085f4d3",
|
|
85
|
+
"@dxos/react-client": "0.4.3-main.085f4d3",
|
|
86
|
+
"@dxos/storybook-utils": "0.4.3-main.085f4d3"
|
|
87
87
|
},
|
|
88
88
|
"peerDependencies": {
|
|
89
89
|
"@phosphor-icons/react": "^2.0.5",
|
|
90
90
|
"react": "^18.2.0",
|
|
91
91
|
"react-dom": "^18.2.0",
|
|
92
|
-
"@dxos/react-client": "0.4.
|
|
92
|
+
"@dxos/react-client": "0.4.3-main.085f4d3"
|
|
93
93
|
},
|
|
94
94
|
"publishConfig": {
|
|
95
95
|
"access": "public"
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import '@dxosTheme';
|
|
6
|
-
|
|
7
6
|
import { type EditorView } from '@codemirror/view';
|
|
8
7
|
import { ArrowSquareOut } from '@phosphor-icons/react';
|
|
9
8
|
import defaultsDeep from 'lodash.defaultsdeep';
|
|
@@ -14,7 +13,7 @@ import { TextObject } from '@dxos/echo-schema';
|
|
|
14
13
|
import { keySymbols, parseShortcut } from '@dxos/keyboard';
|
|
15
14
|
import { PublicKey } from '@dxos/keys';
|
|
16
15
|
import { faker } from '@dxos/random';
|
|
17
|
-
import {
|
|
16
|
+
import { getSize, mx, textBlockWidth } from '@dxos/react-ui-theme';
|
|
18
17
|
import { withTheme } from '@dxos/storybook-utils';
|
|
19
18
|
|
|
20
19
|
import { MarkdownEditor, type TextEditorProps } from './TextEditor';
|
|
@@ -37,8 +36,10 @@ import {
|
|
|
37
36
|
useComments,
|
|
38
37
|
formatting,
|
|
39
38
|
annotations,
|
|
39
|
+
EditorModes,
|
|
40
40
|
} from '../../extensions';
|
|
41
41
|
import { type Comment, useTextModel } from '../../hooks';
|
|
42
|
+
import translations from '../../translations';
|
|
42
43
|
|
|
43
44
|
faker.seed(101);
|
|
44
45
|
|
|
@@ -224,21 +225,16 @@ const Story = ({ text, comments, placeholder = 'New document.', ...props }: Stor
|
|
|
224
225
|
}
|
|
225
226
|
|
|
226
227
|
return (
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
{...props}
|
|
238
|
-
/>
|
|
239
|
-
</div>
|
|
240
|
-
</div>
|
|
241
|
-
</div>
|
|
228
|
+
<MarkdownEditor
|
|
229
|
+
ref={view}
|
|
230
|
+
model={model}
|
|
231
|
+
placeholder={placeholder}
|
|
232
|
+
slots={{
|
|
233
|
+
root: { className: mx(textBlockWidth, 'min-bs-dvh') },
|
|
234
|
+
editor: { className: 'min-bs-dvh p-2 bg-white dark:bg-black' },
|
|
235
|
+
}}
|
|
236
|
+
{...props}
|
|
237
|
+
/>
|
|
242
238
|
);
|
|
243
239
|
};
|
|
244
240
|
|
|
@@ -247,6 +243,7 @@ export default {
|
|
|
247
243
|
component: MarkdownEditor,
|
|
248
244
|
decorators: [withTheme],
|
|
249
245
|
render: Story,
|
|
246
|
+
parameters: { translations, layout: 'fullscreen' },
|
|
250
247
|
};
|
|
251
248
|
|
|
252
249
|
const defaults = [
|
|
@@ -393,6 +390,15 @@ export const HorizontalRule = {
|
|
|
393
390
|
),
|
|
394
391
|
};
|
|
395
392
|
|
|
393
|
+
export const Vim = {
|
|
394
|
+
render: () => (
|
|
395
|
+
<Story
|
|
396
|
+
text={str('# Vim Mode', '', 'The distant future. The year 2000.', '', text.paragraphs)}
|
|
397
|
+
extensions={[defaults, EditorModes.vim]}
|
|
398
|
+
/>
|
|
399
|
+
),
|
|
400
|
+
};
|
|
401
|
+
|
|
396
402
|
export const Annotations = {
|
|
397
403
|
render: () => <Story text={str('# Annotations', '', large)} extensions={[annotations({ match: /volup/gi })]} />,
|
|
398
404
|
};
|
|
@@ -7,12 +7,13 @@ import '@dxosTheme';
|
|
|
7
7
|
import React, { useState } from 'react';
|
|
8
8
|
|
|
9
9
|
import { TextObject } from '@dxos/echo-schema';
|
|
10
|
-
import {
|
|
10
|
+
import { mx, textBlockWidth } from '@dxos/react-ui-theme';
|
|
11
11
|
import { withTheme } from '@dxos/storybook-utils';
|
|
12
12
|
|
|
13
13
|
import { TextEditor, type TextEditorProps } from './TextEditor';
|
|
14
14
|
import { listener } from '../../extensions';
|
|
15
15
|
import { useTextModel } from '../../hooks';
|
|
16
|
+
import translations from '../../translations';
|
|
16
17
|
|
|
17
18
|
const initialText = [
|
|
18
19
|
'# TextEditor',
|
|
@@ -36,19 +37,14 @@ const Story = ({
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
return (
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
{...props}
|
|
48
|
-
/>
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
40
|
+
<TextEditor
|
|
41
|
+
model={model}
|
|
42
|
+
slots={{
|
|
43
|
+
root: { className: mx(textBlockWidth, 'min-bs-dvh') },
|
|
44
|
+
editor: { className: 'min-bs-dvh p-2 bg-white dark:bg-black' },
|
|
45
|
+
}}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
52
48
|
);
|
|
53
49
|
};
|
|
54
50
|
|
|
@@ -57,6 +53,7 @@ export default {
|
|
|
57
53
|
component: TextEditor,
|
|
58
54
|
decorators: [withTheme],
|
|
59
55
|
render: Story,
|
|
56
|
+
parameters: { translations, layout: 'fullscreen' },
|
|
60
57
|
};
|
|
61
58
|
|
|
62
59
|
export const Default = {
|
|
@@ -4,13 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import { EditorState, type Extension, type StateEffect } from '@codemirror/state';
|
|
6
6
|
import { EditorView } from '@codemirror/view';
|
|
7
|
-
|
|
8
|
-
import { vim } from '@replit/codemirror-vim';
|
|
7
|
+
import { useFocusableGroup } from '@fluentui/react-tabster';
|
|
9
8
|
import defaultsDeep from 'lodash.defaultsdeep';
|
|
10
9
|
import React, {
|
|
11
10
|
type ComponentProps,
|
|
12
|
-
type KeyboardEvent,
|
|
13
11
|
forwardRef,
|
|
12
|
+
type KeyboardEventHandler,
|
|
14
13
|
useCallback,
|
|
15
14
|
useEffect,
|
|
16
15
|
useImperativeHandle,
|
|
@@ -20,19 +19,15 @@ import React, {
|
|
|
20
19
|
|
|
21
20
|
import { log } from '@dxos/log';
|
|
22
21
|
import { useThemeContext } from '@dxos/react-ui';
|
|
23
|
-
import { focusRing
|
|
22
|
+
import { focusRing } from '@dxos/react-ui-theme';
|
|
24
23
|
import { isNotFalsy } from '@dxos/util';
|
|
25
24
|
|
|
26
|
-
import { createBasicBundle, createMarkdownExtensions } from '../../extensions';
|
|
25
|
+
import { createBasicBundle, createMarkdownExtensions, editorMode } from '../../extensions';
|
|
27
26
|
import { type EditorModel } from '../../hooks';
|
|
28
27
|
import { type ThemeStyles } from '../../styles';
|
|
29
28
|
import { defaultTheme, markdownTheme, textTheme } from '../../themes';
|
|
30
29
|
import { logChanges } from '../../util';
|
|
31
30
|
|
|
32
|
-
// TODO(burdon): Change to enum?
|
|
33
|
-
export const EditorModes = ['default', 'vim'] as const;
|
|
34
|
-
export type EditorMode = (typeof EditorModes)[number];
|
|
35
|
-
|
|
36
31
|
export type CursorInfo = {
|
|
37
32
|
from: number;
|
|
38
33
|
to: number;
|
|
@@ -52,7 +47,6 @@ export type TextEditorSlots = {
|
|
|
52
47
|
};
|
|
53
48
|
};
|
|
54
49
|
|
|
55
|
-
// TODO(burdon): Spellcheck?
|
|
56
50
|
export type TextEditorProps = {
|
|
57
51
|
model: EditorModel; // TODO(burdon): Optional (e.g., just provide content if readonly).
|
|
58
52
|
readonly?: boolean; // TODO(burdon): Move into model.
|
|
@@ -60,7 +54,6 @@ export type TextEditorProps = {
|
|
|
60
54
|
lineWrapping?: boolean;
|
|
61
55
|
scrollTo?: StateEffect<any>; // TODO(burdon): Restore scroll position: scrollTo EditorView.scrollSnapshot().
|
|
62
56
|
selection?: { anchor: number; head?: number };
|
|
63
|
-
editorMode?: EditorMode; // TODO(burdon): Factor out.
|
|
64
57
|
placeholder?: string;
|
|
65
58
|
theme?: ThemeStyles;
|
|
66
59
|
slots?: TextEditorSlots;
|
|
@@ -74,26 +67,10 @@ export type TextEditorProps = {
|
|
|
74
67
|
// TODO(burdon): Replace with useTextEditor.
|
|
75
68
|
export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
76
69
|
(
|
|
77
|
-
{
|
|
78
|
-
model,
|
|
79
|
-
readonly,
|
|
80
|
-
autoFocus,
|
|
81
|
-
scrollTo,
|
|
82
|
-
selection,
|
|
83
|
-
editorMode,
|
|
84
|
-
theme,
|
|
85
|
-
slots = defaultSlots,
|
|
86
|
-
extensions = [],
|
|
87
|
-
debug,
|
|
88
|
-
},
|
|
70
|
+
{ model, readonly, autoFocus, scrollTo, selection, theme, slots = defaultSlots, extensions = [], debug },
|
|
89
71
|
forwardedRef,
|
|
90
72
|
) => {
|
|
91
|
-
|
|
92
|
-
// Uncaught TypeError: Cannot read properties of undefined (reading 'relatedTarget')
|
|
93
|
-
// Uses event.detail, which is deprecated (not event.details). At runtime the event has a property `details`.
|
|
94
|
-
// https://github.com/microsoft/tabster/blob/master/src/State/FocusedElement.ts#L348 (e.detail.relatedTarget)
|
|
95
|
-
// https://github.com/microsoft/keyborg/blob/49e49b2c3ba0a5f6cc518ac46825d7551def8109/src/FocusEvent.ts#L58
|
|
96
|
-
// const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
|
|
73
|
+
const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
|
|
97
74
|
const { themeMode } = useThemeContext();
|
|
98
75
|
|
|
99
76
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
@@ -143,6 +120,15 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
143
120
|
EditorView.editorAttributes.of({ class: slots.editor?.className ?? '' }),
|
|
144
121
|
EditorView.contentAttributes.of({ class: slots.content?.className ?? '' }),
|
|
145
122
|
|
|
123
|
+
// Focus.
|
|
124
|
+
EditorView.updateListener.of((update) => {
|
|
125
|
+
update.transactions.forEach((transaction) => {
|
|
126
|
+
if (transaction.isUserEvent('focus.container')) {
|
|
127
|
+
rootRef.current?.focus();
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}),
|
|
131
|
+
|
|
146
132
|
// State.
|
|
147
133
|
EditorView.editable.of(!readonly),
|
|
148
134
|
EditorState.readOnly.of(!!readonly),
|
|
@@ -151,9 +137,6 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
151
137
|
// NOTE: This must come before user extensions.
|
|
152
138
|
model.extension,
|
|
153
139
|
|
|
154
|
-
// TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
|
|
155
|
-
editorMode === 'vim' && vim(),
|
|
156
|
-
|
|
157
140
|
// Custom.
|
|
158
141
|
...extensions,
|
|
159
142
|
].filter(isNotFalsy),
|
|
@@ -180,44 +163,40 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
180
163
|
view?.destroy();
|
|
181
164
|
setView(newView);
|
|
182
165
|
|
|
166
|
+
// Remove tabster attribute (rely on custom keymap).
|
|
167
|
+
if (state.facet(editorMode).noTabster) {
|
|
168
|
+
rootRef.current.removeAttribute('data-tabster');
|
|
169
|
+
}
|
|
170
|
+
|
|
183
171
|
return () => {
|
|
184
172
|
newView?.destroy();
|
|
185
173
|
setView(null);
|
|
186
174
|
};
|
|
187
|
-
}, [rootRef, model, readonly,
|
|
175
|
+
}, [rootRef, model, readonly, themeMode]);
|
|
188
176
|
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
(event: KeyboardEvent) => {
|
|
194
|
-
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
177
|
+
// Focus editor on Enter (e.g., when tabbing to this component).
|
|
178
|
+
const handleKeyUp = useCallback<KeyboardEventHandler<HTMLDivElement>>(
|
|
179
|
+
(event) => {
|
|
180
|
+
const { key } = event;
|
|
195
181
|
switch (key) {
|
|
196
182
|
case 'Enter': {
|
|
197
183
|
view?.focus();
|
|
198
184
|
break;
|
|
199
185
|
}
|
|
200
|
-
|
|
201
|
-
case 'Escape': {
|
|
202
|
-
if (editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey)) {
|
|
203
|
-
view?.focus();
|
|
204
|
-
}
|
|
205
|
-
break;
|
|
206
|
-
}
|
|
207
186
|
}
|
|
208
187
|
},
|
|
209
|
-
[view
|
|
188
|
+
[view],
|
|
210
189
|
);
|
|
211
190
|
|
|
212
191
|
return (
|
|
213
192
|
<div
|
|
214
|
-
key={model.id}
|
|
215
193
|
role='none'
|
|
194
|
+
ref={rootRef}
|
|
195
|
+
key={model.id}
|
|
216
196
|
tabIndex={0}
|
|
217
|
-
{...slots.root}
|
|
218
|
-
// {...(editorMode !== 'vim' && tabsterDOMAttribute)}
|
|
219
197
|
onKeyUp={handleKeyUp}
|
|
220
|
-
|
|
198
|
+
{...slots.root}
|
|
199
|
+
{...tabsterDOMAttribute}
|
|
221
200
|
/>
|
|
222
201
|
);
|
|
223
202
|
},
|
|
@@ -259,10 +238,10 @@ export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
|
|
|
259
238
|
|
|
260
239
|
export const defaultSlots: TextEditorSlots = {
|
|
261
240
|
root: {
|
|
262
|
-
className:
|
|
241
|
+
className: focusRing,
|
|
263
242
|
},
|
|
264
243
|
editor: {
|
|
265
|
-
className: 'bs-full',
|
|
244
|
+
className: 'min-bs-full',
|
|
266
245
|
},
|
|
267
246
|
};
|
|
268
247
|
|
|
@@ -5,11 +5,10 @@
|
|
|
5
5
|
import '@dxosTheme';
|
|
6
6
|
|
|
7
7
|
import { BroadcastChannelNetworkAdapter } from '@automerge/automerge-repo-network-broadcastchannel';
|
|
8
|
-
import
|
|
9
|
-
import '@
|
|
10
|
-
import { basicSetup } from 'codemirror';
|
|
8
|
+
import '@preact/signals-react';
|
|
9
|
+
import { EditorView } from '@codemirror/view'; // Register react integration.
|
|
11
10
|
import get from 'lodash.get';
|
|
12
|
-
import React, { useEffect,
|
|
11
|
+
import React, { useEffect, useState } from 'react';
|
|
13
12
|
|
|
14
13
|
import { type Prop } from '@dxos/automerge/automerge';
|
|
15
14
|
import { Repo, type DocHandle } from '@dxos/automerge/automerge-repo';
|
|
@@ -21,38 +20,42 @@ import { ClientRepeater } from '@dxos/react-client/testing';
|
|
|
21
20
|
import { withTheme } from '@dxos/storybook-utils';
|
|
22
21
|
|
|
23
22
|
import { MarkdownEditor } from './TextEditor';
|
|
24
|
-
import { type IDocHandle, automerge, awareness } from '../../extensions';
|
|
25
|
-
import { useTextModel } from '../../hooks';
|
|
23
|
+
import { type IDocHandle, automerge, awareness, createBasicBundle } from '../../extensions';
|
|
24
|
+
import { useTextEditor, useTextModel } from '../../hooks';
|
|
25
|
+
import { defaultTheme } from '../../themes';
|
|
26
|
+
import translations from '../../translations';
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
const initialContent = 'Hello world!';
|
|
29
|
+
|
|
30
|
+
type TestObject = {
|
|
31
|
+
text: string;
|
|
32
|
+
};
|
|
28
33
|
|
|
29
34
|
type EditorProps = {
|
|
30
35
|
handle: IDocHandle;
|
|
31
|
-
path
|
|
36
|
+
path?: Prop[];
|
|
32
37
|
};
|
|
33
38
|
|
|
34
|
-
const Editor = ({ handle, path }: EditorProps) => {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
};
|
|
48
|
-
}, []);
|
|
39
|
+
const Editor = ({ handle, path = ['text'] }: EditorProps) => {
|
|
40
|
+
const { parentRef } = useTextEditor({
|
|
41
|
+
autoFocus: true,
|
|
42
|
+
doc: get(handle.docSync()!, path),
|
|
43
|
+
extensions: [
|
|
44
|
+
//
|
|
45
|
+
EditorView.baseTheme(defaultTheme),
|
|
46
|
+
EditorView.editorAttributes.of({ class: 'p-2 bg-white' }),
|
|
47
|
+
createBasicBundle({ placeholder: 'Type here...' }),
|
|
48
|
+
automerge({ handle, path }),
|
|
49
|
+
awareness(),
|
|
50
|
+
],
|
|
51
|
+
});
|
|
49
52
|
|
|
50
|
-
return <div
|
|
53
|
+
return <div ref={parentRef} />;
|
|
51
54
|
};
|
|
52
55
|
|
|
53
56
|
const Story = () => {
|
|
54
|
-
const [object1, setObject1] = useState<DocHandle<
|
|
55
|
-
const [object2, setObject2] = useState<DocHandle<
|
|
57
|
+
const [object1, setObject1] = useState<DocHandle<TestObject>>();
|
|
58
|
+
const [object2, setObject2] = useState<DocHandle<TestObject>>();
|
|
56
59
|
|
|
57
60
|
useEffect(() => {
|
|
58
61
|
queueMicrotask(async () => {
|
|
@@ -64,8 +67,8 @@ const Story = () => {
|
|
|
64
67
|
});
|
|
65
68
|
|
|
66
69
|
const object1 = repo1.create();
|
|
67
|
-
object1.change((doc:
|
|
68
|
-
doc.text =
|
|
70
|
+
object1.change((doc: TestObject) => {
|
|
71
|
+
doc.text = initialContent;
|
|
69
72
|
});
|
|
70
73
|
|
|
71
74
|
const object2 = repo2.find(object1.url);
|
|
@@ -81,13 +84,9 @@ const Story = () => {
|
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
return (
|
|
84
|
-
<div
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
</div>
|
|
88
|
-
<div>
|
|
89
|
-
<Editor handle={object2} path={['text']} />
|
|
90
|
-
</div>
|
|
87
|
+
<div role='none' className='grid grid-cols-2 bs-full is-full gap-2'>
|
|
88
|
+
<Editor handle={object1} path={['text']} />
|
|
89
|
+
<Editor handle={object2} path={['text']} />
|
|
91
90
|
</div>
|
|
92
91
|
);
|
|
93
92
|
};
|
|
@@ -96,6 +95,7 @@ export default {
|
|
|
96
95
|
title: 'react-ui-editor/Automerge',
|
|
97
96
|
component: Editor,
|
|
98
97
|
render: () => <Story />,
|
|
98
|
+
parameters: { translations, layout: 'fullscreen' },
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
export const Default = {};
|
|
@@ -115,16 +115,11 @@ const EchoStory = ({ spaceKey }: { spaceKey: PublicKey }) => {
|
|
|
115
115
|
return null;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
return
|
|
119
|
-
<div className='flex overflow-hidden'>
|
|
120
|
-
<div className='flex flex-col w-[800px] overflow-y-scroll'>
|
|
121
|
-
<MarkdownEditor model={model} />
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
);
|
|
118
|
+
return <MarkdownEditor model={model} />;
|
|
125
119
|
};
|
|
126
120
|
|
|
127
121
|
export const WithEcho = {
|
|
122
|
+
decorators: [withTheme],
|
|
128
123
|
render: () => {
|
|
129
124
|
return (
|
|
130
125
|
<ClientRepeater
|
|
@@ -134,7 +129,7 @@ export const WithEcho = {
|
|
|
134
129
|
space.db.add(
|
|
135
130
|
new Expando({
|
|
136
131
|
type: 'test',
|
|
137
|
-
content: new TextObject(
|
|
132
|
+
content: new TextObject(initialContent),
|
|
138
133
|
}),
|
|
139
134
|
);
|
|
140
135
|
}}
|
|
@@ -142,5 +137,4 @@ export const WithEcho = {
|
|
|
142
137
|
/>
|
|
143
138
|
);
|
|
144
139
|
},
|
|
145
|
-
decorators: [withTheme],
|
|
146
140
|
};
|