@dxos/react-ui-editor 0.8.4-main.1068cf700f → 0.8.4-main.1c7ec43d41
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 +794 -749
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/translations.mjs +39 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +794 -749
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/translations.mjs +41 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- package/dist/types/src/components/Editor/Editor.d.ts +36 -25
- package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
- package/dist/types/src/components/Editor/Editor.stories.d.ts +4 -4
- package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
- package/dist/types/src/components/{EditorContent/EditorContent.d.ts → Editor/EditorView.d.ts} +5 -5
- package/dist/types/src/components/Editor/EditorView.d.ts.map +1 -0
- package/dist/types/src/components/Editor/controller.d.ts.map +1 -0
- package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts +1 -3
- package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/menu-presets.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/menu.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/popover.d.ts +2 -1
- package/dist/types/src/components/EditorMenuProvider/popover.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -1
- package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +2 -2
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/blocks.d.ts +4 -18
- package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/formatting.d.ts +4 -18
- package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/headings.d.ts +4 -18
- package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/image.d.ts +3 -8
- package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/index.d.ts +1 -2
- package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/lists.d.ts +6 -0
- package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -0
- package/dist/types/src/components/EditorToolbar/search.d.ts +3 -8
- package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/types.d.ts +6 -0
- package/dist/types/src/components/EditorToolbar/types.d.ts.map +1 -0
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts +5 -19
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/extensions/Assistant.stories.d.ts +10 -0
- package/dist/types/src/extensions/Assistant.stories.d.ts.map +1 -0
- package/dist/types/src/extensions/assistant-extension.d.ts +24 -0
- package/dist/types/src/extensions/assistant-extension.d.ts.map +1 -0
- package/dist/types/src/extensions/index.d.ts +2 -0
- package/dist/types/src/extensions/index.d.ts.map +1 -0
- 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/useBasicMarkdownExtensions.d.ts +25 -0
- package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/stories/Automerge.stories.d.ts +25 -24
- package/dist/types/src/stories/Automerge.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Comments.stories.d.ts +2 -2
- package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/stories/EditorToolbar.stories.d.ts +28 -26
- package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Experimental.stories.d.ts +3 -3
- package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Markdown.stories.d.ts +2 -2
- package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Outliner.stories.d.ts +2 -2
- package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Popover.stories.d.ts +2 -2
- package/dist/types/src/stories/Popover.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Preview.stories.d.ts +2 -2
- package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Tags.stories.d.ts.map +1 -1
- package/dist/types/src/stories/TextEditor.stories.d.ts +2 -2
- package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Theme.stories.d.ts.map +1 -1
- package/dist/types/src/stories/components/EditorStory.d.ts +4 -4
- package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
- package/dist/types/src/stories/components/util.d.ts +3 -2
- package/dist/types/src/stories/components/util.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +24 -24
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/react.d.ts +2 -5
- package/dist/types/src/util/react.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +59 -50
- package/src/components/Editor/Editor.stories.tsx +15 -21
- package/src/components/Editor/Editor.tsx +54 -49
- package/src/components/Editor/EditorView.tsx +103 -0
- package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +17 -18
- package/src/components/EditorMenuProvider/menu-presets.ts +1 -0
- package/src/components/EditorMenuProvider/popover.ts +3 -1
- package/src/components/EditorMenuProvider/useEditorMenu.ts +8 -1
- package/src/components/EditorToolbar/EditorToolbar.tsx +31 -65
- package/src/components/EditorToolbar/blocks.ts +54 -46
- package/src/components/EditorToolbar/formatting.ts +44 -45
- package/src/components/EditorToolbar/headings.ts +44 -50
- package/src/components/EditorToolbar/image.ts +16 -21
- package/src/components/EditorToolbar/index.ts +2 -3
- package/src/components/EditorToolbar/lists.ts +58 -0
- package/src/components/EditorToolbar/search.ts +16 -21
- package/src/components/EditorToolbar/types.ts +8 -0
- package/src/components/EditorToolbar/view-mode.ts +37 -43
- package/src/components/index.ts +0 -3
- package/src/extensions/Assistant.stories.tsx +112 -0
- package/src/extensions/assistant-extension.tsx +223 -0
- package/src/extensions/index.ts +5 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useBasicMarkdownExtensions.ts +55 -0
- package/src/index.ts +1 -4
- package/src/stories/Automerge.stories.tsx +12 -13
- package/src/stories/Comments.stories.tsx +6 -6
- package/src/stories/EditorToolbar.stories.tsx +37 -65
- package/src/stories/Experimental.stories.tsx +12 -12
- package/src/stories/Markdown.stories.tsx +2 -2
- package/src/stories/Outliner.stories.tsx +4 -5
- package/src/stories/Popover.stories.tsx +9 -10
- package/src/stories/Preview.stories.tsx +49 -41
- package/src/stories/Tags.stories.tsx +5 -5
- package/src/stories/TextEditor.stories.tsx +2 -2
- package/src/stories/Theme.stories.tsx +4 -4
- package/src/stories/components/EditorStory.tsx +19 -12
- package/src/stories/components/util.tsx +49 -50
- package/src/translations.ts +29 -24
- package/src/util/react.tsx +3 -12
- package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +0 -1
- package/dist/types/src/components/EditorContent/controller.d.ts.map +0 -1
- package/dist/types/src/components/EditorContent/index.d.ts +0 -3
- package/dist/types/src/components/EditorContent/index.d.ts.map +0 -1
- package/dist/types/src/components/EditorToolbar/actions.d.ts +0 -24
- package/dist/types/src/components/EditorToolbar/actions.d.ts.map +0 -1
- package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts +0 -11
- package/dist/types/src/components/EditorToolbar/useEditorToolbar.d.ts.map +0 -1
- package/dist/types/src/stories/CommandDialog.stories.d.ts +0 -14
- package/dist/types/src/stories/CommandDialog.stories.d.ts.map +0 -1
- package/src/components/EditorContent/EditorContent.tsx +0 -83
- package/src/components/EditorContent/index.ts +0 -6
- package/src/components/EditorToolbar/actions.ts +0 -87
- package/src/components/EditorToolbar/useEditorToolbar.ts +0 -20
- package/src/stories/CommandDialog.stories.tsx +0 -81
- /package/dist/types/src/components/{EditorContent → Editor}/controller.d.ts +0 -0
- /package/src/components/{EditorContent → Editor}/controller.ts +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-editor",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.1c7ec43d41",
|
|
4
4
|
"description": "Text editor components.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -12,24 +12,30 @@
|
|
|
12
12
|
"author": "DXOS.org",
|
|
13
13
|
"sideEffects": false,
|
|
14
14
|
"type": "module",
|
|
15
|
+
"imports": {
|
|
16
|
+
"#translations": "./src/translations.ts"
|
|
17
|
+
},
|
|
15
18
|
"exports": {
|
|
16
19
|
".": {
|
|
17
20
|
"source": "./src/index.ts",
|
|
18
21
|
"types": "./dist/types/src/index.d.ts",
|
|
19
22
|
"browser": "./dist/lib/browser/index.mjs",
|
|
20
23
|
"node": "./dist/lib/node-esm/index.mjs"
|
|
24
|
+
},
|
|
25
|
+
"./translations": {
|
|
26
|
+
"source": "./src/translations.ts",
|
|
27
|
+
"types": "./dist/types/src/translations.d.ts",
|
|
28
|
+
"browser": "./dist/lib/browser/translations.mjs",
|
|
29
|
+
"node": "./dist/lib/node-esm/translations.mjs"
|
|
21
30
|
}
|
|
22
31
|
},
|
|
23
32
|
"types": "dist/types/src/index.d.ts",
|
|
24
|
-
"typesVersions": {
|
|
25
|
-
"*": {}
|
|
26
|
-
},
|
|
27
33
|
"files": [
|
|
28
34
|
"dist",
|
|
29
35
|
"src"
|
|
30
36
|
],
|
|
31
37
|
"dependencies": {
|
|
32
|
-
"@automerge/automerge": "3.2.
|
|
38
|
+
"@automerge/automerge": "3.2.6",
|
|
33
39
|
"@codemirror/autocomplete": "^6.19.0",
|
|
34
40
|
"@codemirror/commands": "^6.8.1",
|
|
35
41
|
"@codemirror/lang-html": "^6.4.11",
|
|
@@ -44,7 +50,7 @@
|
|
|
44
50
|
"@codemirror/search": "^6.5.11",
|
|
45
51
|
"@codemirror/state": "^6.5.2",
|
|
46
52
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
47
|
-
"@codemirror/view": "^6.38.
|
|
53
|
+
"@codemirror/view": "^6.38.5",
|
|
48
54
|
"@fluentui/react-tabster": "9.26.11",
|
|
49
55
|
"@lezer/common": "^1.2.2",
|
|
50
56
|
"@lezer/generator": "^1.7.1",
|
|
@@ -55,7 +61,7 @@
|
|
|
55
61
|
"@replit/codemirror-vim": "^6.2.1",
|
|
56
62
|
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
|
57
63
|
"@uiw/codemirror-theme-vscode": "^4.25.2",
|
|
58
|
-
"ajv": "^8.
|
|
64
|
+
"ajv": "^8.18.0",
|
|
59
65
|
"bind-event-listener": "^3.0.0",
|
|
60
66
|
"codemirror": "^6.0.1",
|
|
61
67
|
"lib0": "^0.2.65",
|
|
@@ -63,31 +69,33 @@
|
|
|
63
69
|
"lodash.merge": "^4.6.2",
|
|
64
70
|
"lodash.sortby": "^4.7.0",
|
|
65
71
|
"style-mod": "^4.1.0",
|
|
66
|
-
"@dxos/
|
|
67
|
-
"@dxos/
|
|
68
|
-
"@dxos/
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/echo": "0.8.4-main.
|
|
73
|
-
"@dxos/
|
|
74
|
-
"@dxos/
|
|
75
|
-
"@dxos/
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/
|
|
78
|
-
"@dxos/react-
|
|
79
|
-
"@dxos/
|
|
80
|
-
"@dxos/react-ui-
|
|
81
|
-
"@dxos/ui-
|
|
82
|
-
"@dxos/ui-
|
|
83
|
-
"@dxos/ui": "0.8.4-main.
|
|
84
|
-
"@dxos/
|
|
72
|
+
"@dxos/async": "0.8.4-main.1c7ec43d41",
|
|
73
|
+
"@dxos/app-graph": "0.8.4-main.1c7ec43d41",
|
|
74
|
+
"@dxos/client": "0.8.4-main.1c7ec43d41",
|
|
75
|
+
"@dxos/debug": "0.8.4-main.1c7ec43d41",
|
|
76
|
+
"@dxos/context": "0.8.4-main.1c7ec43d41",
|
|
77
|
+
"@dxos/echo": "0.8.4-main.1c7ec43d41",
|
|
78
|
+
"@dxos/echo-db": "0.8.4-main.1c7ec43d41",
|
|
79
|
+
"@dxos/display-name": "0.8.4-main.1c7ec43d41",
|
|
80
|
+
"@dxos/effect": "0.8.4-main.1c7ec43d41",
|
|
81
|
+
"@dxos/invariant": "0.8.4-main.1c7ec43d41",
|
|
82
|
+
"@dxos/lit-ui": "0.8.4-main.1c7ec43d41",
|
|
83
|
+
"@dxos/protocols": "0.8.4-main.1c7ec43d41",
|
|
84
|
+
"@dxos/react-hooks": "0.8.4-main.1c7ec43d41",
|
|
85
|
+
"@dxos/log": "0.8.4-main.1c7ec43d41",
|
|
86
|
+
"@dxos/react-ui-menu": "0.8.4-main.1c7ec43d41",
|
|
87
|
+
"@dxos/react-ui-mosaic": "0.8.4-main.1c7ec43d41",
|
|
88
|
+
"@dxos/ui-editor": "0.8.4-main.1c7ec43d41",
|
|
89
|
+
"@dxos/ui": "0.8.4-main.1c7ec43d41",
|
|
90
|
+
"@dxos/ui-theme": "0.8.4-main.1c7ec43d41",
|
|
91
|
+
"@dxos/util": "0.8.4-main.1c7ec43d41"
|
|
85
92
|
},
|
|
86
93
|
"devDependencies": {
|
|
87
|
-
"@automerge/automerge": "3.2.
|
|
88
|
-
"@automerge/automerge-repo": "2.
|
|
89
|
-
"@automerge/automerge-repo-network-broadcastchannel": "2.
|
|
94
|
+
"@automerge/automerge": "3.2.6",
|
|
95
|
+
"@automerge/automerge-repo": "2.6.0-subduction.17",
|
|
96
|
+
"@automerge/automerge-repo-network-broadcastchannel": "2.6.0-subduction.17",
|
|
90
97
|
"@effect-atom/atom-react": "^0.5.0",
|
|
98
|
+
"@effect/ai": "0.33.2",
|
|
91
99
|
"@effect/platform": "0.94.4",
|
|
92
100
|
"@types/chai": "^4.2.15",
|
|
93
101
|
"@types/chai-dom": "^1.11.0",
|
|
@@ -99,39 +107,40 @@
|
|
|
99
107
|
"@types/react-test-renderer": "^17.0.2",
|
|
100
108
|
"chai": "^4.4.1",
|
|
101
109
|
"chai-dom": "^1.11.0",
|
|
102
|
-
"effect": "3.
|
|
103
|
-
"happy-dom": "^
|
|
110
|
+
"effect": "3.20.0",
|
|
111
|
+
"happy-dom": "^20.0.0",
|
|
104
112
|
"jsdom": "^27.0.0",
|
|
105
113
|
"mocha": "^10.6.0",
|
|
106
114
|
"react": "~19.2.3",
|
|
107
115
|
"react-dom": "~19.2.3",
|
|
108
116
|
"react-test-renderer": "~19.2.0",
|
|
109
|
-
"vite": "
|
|
117
|
+
"vite": "^8.0.10",
|
|
110
118
|
"vite-plugin-top-level-await": "^1.6.0",
|
|
111
|
-
"vite-plugin-wasm": "^3.
|
|
112
|
-
"@dxos/
|
|
113
|
-
"@dxos/
|
|
114
|
-
"@dxos/
|
|
115
|
-
"@dxos/random": "0.8.4-main.
|
|
116
|
-
"@dxos/react-client": "0.8.4-main.
|
|
117
|
-
"@dxos/
|
|
118
|
-
"@dxos/
|
|
119
|
-
"@dxos/react-ui-attention": "0.8.4-main.
|
|
120
|
-
"@dxos/schema": "0.8.4-main.
|
|
121
|
-
"@dxos/
|
|
122
|
-
"@dxos/
|
|
123
|
-
"@dxos/
|
|
124
|
-
"@dxos/ui-
|
|
119
|
+
"vite-plugin-wasm": "^3.6.0",
|
|
120
|
+
"@dxos/ai": "0.8.4-main.1c7ec43d41",
|
|
121
|
+
"@dxos/config": "0.8.4-main.1c7ec43d41",
|
|
122
|
+
"@dxos/echo": "0.8.4-main.1c7ec43d41",
|
|
123
|
+
"@dxos/random": "0.8.4-main.1c7ec43d41",
|
|
124
|
+
"@dxos/react-client": "0.8.4-main.1c7ec43d41",
|
|
125
|
+
"@dxos/react-ui": "0.8.4-main.1c7ec43d41",
|
|
126
|
+
"@dxos/keyboard": "0.8.4-main.1c7ec43d41",
|
|
127
|
+
"@dxos/react-ui-attention": "0.8.4-main.1c7ec43d41",
|
|
128
|
+
"@dxos/schema": "0.8.4-main.1c7ec43d41",
|
|
129
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.1c7ec43d41",
|
|
130
|
+
"@dxos/ui-types": "0.8.4-main.1c7ec43d41",
|
|
131
|
+
"@dxos/storybook-utils": "0.8.4-main.1c7ec43d41",
|
|
132
|
+
"@dxos/ui-theme": "0.8.4-main.1c7ec43d41",
|
|
133
|
+
"@dxos/echo-atom": "0.8.4-main.1c7ec43d41"
|
|
125
134
|
},
|
|
126
135
|
"peerDependencies": {
|
|
127
136
|
"@effect-atom/atom-react": "^0.5.0",
|
|
128
137
|
"@effect/platform": "0.94.4",
|
|
129
|
-
"effect": "3.
|
|
138
|
+
"effect": "3.20.0",
|
|
130
139
|
"react": "~19.2.3",
|
|
131
140
|
"react-dom": "~19.2.3",
|
|
132
|
-
"@dxos/react-client": "0.8.4-main.
|
|
133
|
-
"@dxos/
|
|
134
|
-
"@dxos/ui
|
|
141
|
+
"@dxos/react-client": "0.8.4-main.1c7ec43d41",
|
|
142
|
+
"@dxos/ui-theme": "0.8.4-main.1c7ec43d41",
|
|
143
|
+
"@dxos/react-ui": "0.8.4-main.1c7ec43d41"
|
|
135
144
|
},
|
|
136
145
|
"publishConfig": {
|
|
137
146
|
"access": "public"
|
|
@@ -6,10 +6,10 @@ import { type Decorator, type Meta, type StoryObj } from '@storybook/react-vite'
|
|
|
6
6
|
import React, { useMemo } from 'react';
|
|
7
7
|
|
|
8
8
|
import { createDocAccessor, createObject } from '@dxos/echo-db';
|
|
9
|
-
import {
|
|
9
|
+
import { random } from '@dxos/random';
|
|
10
10
|
import { useThemeContext } from '@dxos/react-ui';
|
|
11
|
-
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
12
11
|
import { withAttention } from '@dxos/react-ui-attention/testing';
|
|
12
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
13
13
|
import { Text } from '@dxos/schema';
|
|
14
14
|
import {
|
|
15
15
|
automerge,
|
|
@@ -20,21 +20,15 @@ import {
|
|
|
20
20
|
} from '@dxos/ui-editor';
|
|
21
21
|
|
|
22
22
|
import { createMenuGroup } from '../EditorMenuProvider';
|
|
23
|
+
import { Editor, type EditorViewProps } from './Editor';
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// TODO(burdon): PreviewPopoverProvider (MarkdownStream, Preview story).
|
|
27
|
-
// TODO(burdon): Adapt Markdown plugin to use new Editor (plan first to check fit).
|
|
28
|
-
// TODO(burdon): Remove redundant hooks and simplify props.
|
|
29
|
-
|
|
30
|
-
faker.seed(1234);
|
|
25
|
+
random.seed(1234);
|
|
31
26
|
|
|
32
27
|
const initialValue = ['# Blue Monday', '', 'How does it **feel**?', ''].join('\n');
|
|
33
28
|
|
|
34
|
-
const items =
|
|
29
|
+
const items = random.helpers.multiple(random.commerce.productName, { count: 10 }).sort();
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
const withExtensions: Decorator<EditorContentProps> = (Story, { args }) => {
|
|
31
|
+
const withExtensions: Decorator<EditorViewProps> = (Story, { args }) => {
|
|
38
32
|
const { themeMode } = useThemeContext();
|
|
39
33
|
const extensions = useMemo(
|
|
40
34
|
() => [
|
|
@@ -42,7 +36,7 @@ const withExtensions: Decorator<EditorContentProps> = (Story, { args }) => {
|
|
|
42
36
|
createThemeExtensions({ themeMode }),
|
|
43
37
|
createMarkdownExtensions(),
|
|
44
38
|
decorateMarkdown(),
|
|
45
|
-
automerge(createDocAccessor(createObject(Text.make(args.initialValue)), ['content'])),
|
|
39
|
+
automerge(createDocAccessor(createObject(Text.make({ content: args.initialValue })), ['content'])),
|
|
46
40
|
],
|
|
47
41
|
[themeMode],
|
|
48
42
|
);
|
|
@@ -52,7 +46,7 @@ const withExtensions: Decorator<EditorContentProps> = (Story, { args }) => {
|
|
|
52
46
|
|
|
53
47
|
const meta = {
|
|
54
48
|
title: 'ui/react-ui-editor/Editor',
|
|
55
|
-
component: Editor.
|
|
49
|
+
component: Editor.View,
|
|
56
50
|
decorators: [withExtensions, withTheme(), withLayout({ layout: 'column' }), withAttention()],
|
|
57
51
|
parameters: {
|
|
58
52
|
layout: 'fullscreen',
|
|
@@ -60,16 +54,16 @@ const meta = {
|
|
|
60
54
|
args: {
|
|
61
55
|
initialValue,
|
|
62
56
|
},
|
|
63
|
-
} satisfies Meta<typeof Editor.
|
|
57
|
+
} satisfies Meta<typeof Editor.View>;
|
|
64
58
|
|
|
65
59
|
export default meta;
|
|
66
60
|
|
|
67
|
-
type Story = StoryObj<
|
|
61
|
+
type Story = StoryObj<EditorViewProps>;
|
|
68
62
|
|
|
69
63
|
export const Default: Story = {
|
|
70
64
|
render: (args) => (
|
|
71
65
|
<Editor.Root>
|
|
72
|
-
<Editor.
|
|
66
|
+
<Editor.View {...args} />
|
|
73
67
|
</Editor.Root>
|
|
74
68
|
),
|
|
75
69
|
};
|
|
@@ -78,7 +72,7 @@ export const WithToolbar: Story = {
|
|
|
78
72
|
render: (args) => (
|
|
79
73
|
<Editor.Root>
|
|
80
74
|
<Editor.Toolbar />
|
|
81
|
-
<Editor.
|
|
75
|
+
<Editor.View {...args} />
|
|
82
76
|
</Editor.Root>
|
|
83
77
|
),
|
|
84
78
|
};
|
|
@@ -86,10 +80,10 @@ export const WithToolbar: Story = {
|
|
|
86
80
|
export const WithPopover: Story = {
|
|
87
81
|
render: (args) => (
|
|
88
82
|
<Editor.Root trigger={['@']} getMenu={({ text }) => [createMenuGroup({ items, filter: text })]}>
|
|
89
|
-
<Editor.
|
|
83
|
+
<Editor.Content>
|
|
90
84
|
<Editor.Toolbar />
|
|
91
|
-
<Editor.
|
|
92
|
-
</Editor.
|
|
85
|
+
<Editor.View {...args} />
|
|
86
|
+
</Editor.Content>
|
|
93
87
|
</Editor.Root>
|
|
94
88
|
),
|
|
95
89
|
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Extension } from '@codemirror/state';
|
|
6
|
+
import { Atom } from '@effect-atom/atom-react';
|
|
6
7
|
import { createContext } from '@radix-ui/react-context';
|
|
7
8
|
import React, { type PropsWithChildren, forwardRef, useCallback, useImperativeHandle, useMemo, useState } from 'react';
|
|
8
9
|
|
|
@@ -12,18 +13,23 @@ import { mx } from '@dxos/ui-theme';
|
|
|
12
13
|
import { isNonNullable } from '@dxos/util';
|
|
13
14
|
|
|
14
15
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
type
|
|
18
|
-
|
|
19
|
-
} from '../
|
|
20
|
-
import { EditorMenuProvider, type UseEditorMenuProps, useEditorMenu } from '../EditorMenuProvider';
|
|
16
|
+
EditorMenuProvider,
|
|
17
|
+
type EditorMenuProviderProps,
|
|
18
|
+
type UseEditorMenuProps,
|
|
19
|
+
useEditorMenu,
|
|
20
|
+
} from '../EditorMenuProvider';
|
|
21
21
|
import {
|
|
22
22
|
type EditorToolbarState,
|
|
23
23
|
EditorToolbar as NaturalEditorToolbar,
|
|
24
24
|
type EditorToolbarProps as NaturalEditorToolbarProps,
|
|
25
|
-
useEditorToolbar,
|
|
26
25
|
} from '../EditorToolbar';
|
|
26
|
+
import {
|
|
27
|
+
type EditorController,
|
|
28
|
+
EditorView as NaturalEditorContent,
|
|
29
|
+
type EditorViewProps as NaturalEditorContentProps,
|
|
30
|
+
createEditorController,
|
|
31
|
+
noopController,
|
|
32
|
+
} from './EditorView';
|
|
27
33
|
|
|
28
34
|
//
|
|
29
35
|
// Context
|
|
@@ -33,16 +39,26 @@ type EditorContextValue = {
|
|
|
33
39
|
controller?: EditorController;
|
|
34
40
|
setController: (controller: EditorController) => void;
|
|
35
41
|
extensions?: Extension[];
|
|
36
|
-
|
|
42
|
+
state: Atom.Writable<EditorToolbarState>;
|
|
43
|
+
};
|
|
37
44
|
|
|
38
45
|
const [EditorContextProvider, useEditorContext] = createContext<EditorContextValue>('Editor');
|
|
39
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Access the editor context. Must be used within `Editor.Root`.
|
|
49
|
+
*/
|
|
50
|
+
export { useEditorContext };
|
|
51
|
+
|
|
40
52
|
//
|
|
41
53
|
// Root
|
|
42
54
|
//
|
|
43
55
|
|
|
44
56
|
type EditorRootProps = PropsWithChildren<
|
|
45
|
-
Pick<EditorContextValue, 'extensions'> &
|
|
57
|
+
Pick<EditorContextValue, 'extensions'> &
|
|
58
|
+
Omit<UseEditorMenuProps, 'viewRef'> &
|
|
59
|
+
Pick<EditorMenuProviderProps, 'numItems'> & {
|
|
60
|
+
viewMode?: EditorToolbarState['viewMode'];
|
|
61
|
+
}
|
|
46
62
|
>;
|
|
47
63
|
|
|
48
64
|
/**
|
|
@@ -50,11 +66,9 @@ type EditorRootProps = PropsWithChildren<
|
|
|
50
66
|
* Provides context for all child components and manages the editor controller state.
|
|
51
67
|
*/
|
|
52
68
|
const EditorRoot = forwardRef<EditorController | null, EditorRootProps>(
|
|
53
|
-
({ children, extensions: extensionsProp, viewMode, ...props }, forwardedRef) => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const [controller, setController] = useState<EditorController>();
|
|
57
|
-
useImperativeHandle(forwardedRef, () => controller ?? noopController, [controller]);
|
|
69
|
+
({ children, extensions: extensionsProp, viewMode, numItems, ...props }, forwardedRef) => {
|
|
70
|
+
// TODO(wittjosiah): Including initialState in the deps causes reactivity issues.
|
|
71
|
+
const state = useMemo(() => Atom.make<EditorToolbarState>({ viewMode }), [viewMode]);
|
|
58
72
|
|
|
59
73
|
// TODO(burdon): Consider lighter-weight approach if EditorMenuProvider is not needed.
|
|
60
74
|
const { groupsRef, extension, ...menuProps } = useEditorMenu(props);
|
|
@@ -63,6 +77,10 @@ const EditorRoot = forwardRef<EditorController | null, EditorRootProps>(
|
|
|
63
77
|
[extension, extensionsProp],
|
|
64
78
|
);
|
|
65
79
|
|
|
80
|
+
// External controller.
|
|
81
|
+
const [controller, setController] = useState<EditorController>(noopController);
|
|
82
|
+
useImperativeHandle(forwardedRef, () => controller, [controller]);
|
|
83
|
+
|
|
66
84
|
return (
|
|
67
85
|
<EditorContextProvider
|
|
68
86
|
controller={controller}
|
|
@@ -70,7 +88,7 @@ const EditorRoot = forwardRef<EditorController | null, EditorRootProps>(
|
|
|
70
88
|
extensions={extensions}
|
|
71
89
|
state={state}
|
|
72
90
|
>
|
|
73
|
-
<EditorMenuProvider view={controller?.view} groups={groupsRef.current} {...menuProps}>
|
|
91
|
+
<EditorMenuProvider view={controller?.view} groups={groupsRef.current} numItems={numItems} {...menuProps}>
|
|
74
92
|
{children}
|
|
75
93
|
</EditorMenuProvider>
|
|
76
94
|
</EditorContextProvider>
|
|
@@ -81,41 +99,40 @@ const EditorRoot = forwardRef<EditorController | null, EditorRootProps>(
|
|
|
81
99
|
EditorRoot.displayName = 'Editor.Root';
|
|
82
100
|
|
|
83
101
|
//
|
|
84
|
-
//
|
|
102
|
+
// Content
|
|
85
103
|
//
|
|
86
104
|
|
|
87
|
-
const
|
|
105
|
+
const EDITOR_CONTENT_NAME = 'Editor.Content';
|
|
88
106
|
|
|
89
|
-
type
|
|
107
|
+
type EditorContentProps = ThemedClassName<PropsWithChildren<{}>>;
|
|
90
108
|
|
|
91
109
|
/**
|
|
92
|
-
*
|
|
110
|
+
* Content component that wraps the toolbar and editor view area.
|
|
93
111
|
*/
|
|
94
|
-
const
|
|
112
|
+
const EditorContent = ({ classNames, children }: EditorContentProps) => {
|
|
95
113
|
return (
|
|
96
|
-
<div role='none' className={mx('grid grid-rows-[min-content_1fr]
|
|
114
|
+
<div role='none' className={mx('grid grid-rows-[min-content_1fr] h-full overflow-hidden', classNames)}>
|
|
97
115
|
{children}
|
|
98
116
|
</div>
|
|
99
117
|
);
|
|
100
118
|
};
|
|
101
119
|
|
|
102
|
-
|
|
120
|
+
EditorContent.displayName = EDITOR_CONTENT_NAME;
|
|
103
121
|
|
|
104
122
|
//
|
|
105
|
-
//
|
|
123
|
+
// View
|
|
106
124
|
//
|
|
107
125
|
|
|
108
|
-
const
|
|
126
|
+
const EDITOR_VIEW_NAME = 'Editor.View';
|
|
109
127
|
|
|
110
|
-
type
|
|
128
|
+
type EditorViewProps = Omit<NaturalEditorContentProps, 'ref'>;
|
|
111
129
|
|
|
112
130
|
/**
|
|
113
|
-
*
|
|
131
|
+
* View component that renders the actual CodeMirror editor.
|
|
114
132
|
* Automatically registers the editor controller with the context.
|
|
115
133
|
*/
|
|
116
|
-
const
|
|
117
|
-
const { extensions: additionalExtensions = [], setController } = useEditorContext(
|
|
118
|
-
|
|
134
|
+
const EditorView = ({ extensions: providedExtensions, ...props }: EditorViewProps) => {
|
|
135
|
+
const { extensions: additionalExtensions = [], setController } = useEditorContext(EDITOR_VIEW_NAME);
|
|
119
136
|
const extensions = useMemo(
|
|
120
137
|
() => [additionalExtensions, providedExtensions].filter(isNonNullable).flat(),
|
|
121
138
|
[providedExtensions, additionalExtensions],
|
|
@@ -124,7 +141,7 @@ const EditorContent = ({ extensions: providedExtensions, ...props }: EditorConte
|
|
|
124
141
|
return <NaturalEditorContent {...props} extensions={extensions} ref={setController} />;
|
|
125
142
|
};
|
|
126
143
|
|
|
127
|
-
|
|
144
|
+
EditorView.displayName = EDITOR_VIEW_NAME;
|
|
128
145
|
|
|
129
146
|
//
|
|
130
147
|
// Toolbar
|
|
@@ -140,8 +157,6 @@ type EditorToolbarProps = Omit<NaturalEditorToolbarProps, 'getView' | 'state'>;
|
|
|
140
157
|
*/
|
|
141
158
|
const EditorToolbar = (props: EditorToolbarProps) => {
|
|
142
159
|
const { controller, state } = useEditorContext(EDITOR_TOOLBAR_NAME);
|
|
143
|
-
|
|
144
|
-
// TODO(burdon): Fix invariant.
|
|
145
160
|
const getView = useCallback(() => {
|
|
146
161
|
invariant(controller?.view);
|
|
147
162
|
return controller?.view;
|
|
@@ -156,30 +171,20 @@ EditorToolbar.displayName = EDITOR_TOOLBAR_NAME;
|
|
|
156
171
|
// Editor
|
|
157
172
|
//
|
|
158
173
|
|
|
159
|
-
/**
|
|
160
|
-
* Compound editor component following the Radix UI pattern.
|
|
161
|
-
*
|
|
162
|
-
* @example
|
|
163
|
-
* ```tsx
|
|
164
|
-
* EditorMenuGroup.Root>
|
|
165
|
-
* EditorMenuGroup.Toolbar />
|
|
166
|
-
* EditorMenuGroup.Viewport>
|
|
167
|
-
* EditorMenuGroup.Content extensions={[...]} />
|
|
168
|
-
* </Editor.Viewport>
|
|
169
|
-
* </Editor.Root>
|
|
170
|
-
* ```
|
|
171
|
-
*/
|
|
172
174
|
export const Editor = {
|
|
173
175
|
Root: EditorRoot,
|
|
174
|
-
Viewport: EditorViewport,
|
|
175
|
-
Content: EditorContent,
|
|
176
176
|
Toolbar: EditorToolbar,
|
|
177
|
+
Content: EditorContent,
|
|
178
|
+
View: EditorView,
|
|
177
179
|
};
|
|
178
180
|
|
|
179
181
|
export type {
|
|
180
182
|
EditorController,
|
|
181
183
|
EditorRootProps,
|
|
182
|
-
EditorViewportProps,
|
|
183
184
|
EditorContentProps,
|
|
184
|
-
|
|
185
|
+
EditorViewProps,
|
|
186
|
+
EditorToolbarProps,
|
|
187
|
+
EditorToolbarState,
|
|
185
188
|
};
|
|
189
|
+
|
|
190
|
+
export { createEditorController };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Transaction } from '@codemirror/state';
|
|
6
|
+
import { EditorView as NaturalEditorView } from '@codemirror/view';
|
|
7
|
+
import React, { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
|
|
8
|
+
|
|
9
|
+
import { type ThemedClassName } from '@dxos/react-ui';
|
|
10
|
+
import { initialSync } from '@dxos/ui-editor';
|
|
11
|
+
import { mx } from '@dxos/ui-theme';
|
|
12
|
+
|
|
13
|
+
import { type UseTextEditorProps, useTextEditor } from '../../hooks';
|
|
14
|
+
import { type EditorController, createEditorController, noopController } from './controller';
|
|
15
|
+
|
|
16
|
+
export type EditorViewProps = ThemedClassName<
|
|
17
|
+
{
|
|
18
|
+
focusable?: boolean;
|
|
19
|
+
value?: string;
|
|
20
|
+
onChange?: (value: string) => void;
|
|
21
|
+
} & UseTextEditorProps
|
|
22
|
+
>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Minimal text editor.
|
|
26
|
+
* NOTE: This shouold not be used with the automerge extension.
|
|
27
|
+
*/
|
|
28
|
+
// TODO(burdon): Move controller to Root component, then make composable.
|
|
29
|
+
export const EditorView = forwardRef<EditorController, EditorViewProps>(
|
|
30
|
+
({ classNames, id, extensions, selectionEnd, focusable = true, value, onChange, ...props }, forwardedRef) => {
|
|
31
|
+
// Hold the latest onChange in a ref so callers may pass an inline callback
|
|
32
|
+
// without forcing the underlying editor to be destroyed and recreated on
|
|
33
|
+
// every render — which would blur the focused input on each keystroke.
|
|
34
|
+
const onChangeRef = useRef(onChange);
|
|
35
|
+
onChangeRef.current = onChange;
|
|
36
|
+
|
|
37
|
+
const { parentRef, focusAttributes, view } = useTextEditor(
|
|
38
|
+
() => ({
|
|
39
|
+
id,
|
|
40
|
+
initialValue: value,
|
|
41
|
+
selectionEnd,
|
|
42
|
+
extensions: [
|
|
43
|
+
extensions ?? [],
|
|
44
|
+
NaturalEditorView.updateListener.of(({ view, docChanged, transactions }) => {
|
|
45
|
+
const isInitialSync = transactions.some((tr) => tr.annotation(Transaction.userEvent) === initialSync.value);
|
|
46
|
+
if (!isInitialSync && docChanged) {
|
|
47
|
+
onChangeRef.current?.(view.state.doc.toString());
|
|
48
|
+
}
|
|
49
|
+
}),
|
|
50
|
+
],
|
|
51
|
+
...props,
|
|
52
|
+
}),
|
|
53
|
+
[id, extensions, selectionEnd],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// External controller.
|
|
57
|
+
useImperativeHandle(forwardedRef, () => {
|
|
58
|
+
return createEditorController(view);
|
|
59
|
+
}, [id, view]);
|
|
60
|
+
|
|
61
|
+
// Sync the editor doc to the controlled `value` prop, but only when they
|
|
62
|
+
// disagree. After internal typing the prop will already match the editor's
|
|
63
|
+
// doc, and dispatching anyway would race fast keystrokes — a stale rAF
|
|
64
|
+
// closure can replace doc content with an older value, dropping characters.
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (!view) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const next = value ?? '';
|
|
70
|
+
if (view.state.doc.toString() === next) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
requestAnimationFrame(() => {
|
|
74
|
+
if (view.state.doc.toString() === next) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
view.dispatch({
|
|
78
|
+
annotations: initialSync,
|
|
79
|
+
changes: [{ from: 0, to: view.state.doc.length, insert: next }],
|
|
80
|
+
selection: selectionEnd ? { anchor: next.length } : undefined,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (selectionEnd) {
|
|
84
|
+
view.focus();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}, [view, value, selectionEnd]);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div
|
|
91
|
+
role='none'
|
|
92
|
+
className={mx(
|
|
93
|
+
'w-full outline-hidden focus:border-accent-surface focus-within:border-neutral-focus-indicator',
|
|
94
|
+
classNames,
|
|
95
|
+
)}
|
|
96
|
+
{...(focusable ? focusAttributes : {})}
|
|
97
|
+
ref={parentRef}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
export { type EditorController, createEditorController, noopController };
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type DxAnchorActivate,
|
|
14
14
|
Icon,
|
|
15
15
|
Popover,
|
|
16
|
+
ScrollArea,
|
|
16
17
|
toLocalizedString,
|
|
17
18
|
useDynamicRef,
|
|
18
19
|
useThemeContext,
|
|
@@ -35,9 +36,7 @@ export type EditorMenuProviderProps = PropsWithChildren<{
|
|
|
35
36
|
}>;
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
|
-
* Implements the Popover and listens for the `dx-anchor-activate` event from the
|
|
39
|
-
* `popover` extension's decoration.
|
|
40
|
-
*
|
|
39
|
+
* Implements the Popover and listens for the `dx-anchor-activate` event from the `popover` extension's decoration.
|
|
41
40
|
* NOTE: We don't use DropdownMenu because the command menu needs to manage focus explicitly.
|
|
42
41
|
* I.e., focus must remain in the editor while displaying the menu (for type-ahead).
|
|
43
42
|
*/
|
|
@@ -113,32 +112,32 @@ export const EditorMenuProvider = ({
|
|
|
113
112
|
<Popover.Portal>
|
|
114
113
|
<Popover.Content
|
|
115
114
|
align='start'
|
|
116
|
-
classNames={
|
|
117
|
-
'overflow-y-auto',
|
|
118
|
-
!menuGroups.length && 'hidden',
|
|
119
|
-
])}
|
|
115
|
+
classNames={['flex flex-col', !menuGroups.length && 'hidden']}
|
|
120
116
|
style={{
|
|
121
117
|
maxBlockSize: 36 * numItems + 10,
|
|
122
118
|
}}
|
|
123
|
-
|
|
124
|
-
* NOTE: We keep the focus in the editor, but Radix routes escape key.
|
|
125
|
-
*/
|
|
119
|
+
// NOTE: We keep the focus in the editor, but Radix routes escape key.
|
|
126
120
|
onEscapeKeyDown={() => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
121
|
+
const currentView = viewRef.current;
|
|
122
|
+
if (currentView) {
|
|
123
|
+
onCancel?.({ view: currentView });
|
|
124
|
+
}
|
|
130
125
|
}}
|
|
131
126
|
onOpenAutoFocus={(event) => event.preventDefault()}
|
|
132
127
|
>
|
|
133
|
-
<Popover.Viewport classNames=
|
|
134
|
-
<
|
|
128
|
+
<Popover.Viewport asChild classNames='dx-container'>
|
|
129
|
+
<ScrollArea.Root thin>
|
|
130
|
+
<ScrollArea.Viewport>
|
|
131
|
+
<Menu groups={menuGroups} currentItem={currentItem} onSelect={handleSelect} />
|
|
132
|
+
</ScrollArea.Viewport>
|
|
133
|
+
</ScrollArea.Root>
|
|
135
134
|
</Popover.Viewport>
|
|
136
135
|
<Popover.Arrow />
|
|
137
136
|
</Popover.Content>
|
|
138
137
|
</Popover.Portal>
|
|
139
138
|
|
|
140
139
|
{/* Content */}
|
|
141
|
-
<div
|
|
140
|
+
<div role='none' className='contents' ref={setRoot}>
|
|
142
141
|
{children}
|
|
143
142
|
</div>
|
|
144
143
|
</Popover.Root>
|
|
@@ -219,8 +218,8 @@ const MenuItem = ({ item, current, onSelect }: MenuItemProps) => {
|
|
|
219
218
|
const handleSelect = useCallback(() => onSelect?.(item), [item, onSelect]);
|
|
220
219
|
|
|
221
220
|
return (
|
|
222
|
-
<li ref={listRef} className={tx('menu.item', {}, [current && 'bg-
|
|
223
|
-
{item.icon && <Icon icon={item.icon}
|
|
221
|
+
<li ref={listRef} className={tx('menu.item', {}, [current && 'bg-hover-surface'])} onClick={handleSelect}>
|
|
222
|
+
{item.icon && <Icon icon={item.icon} />}
|
|
224
223
|
<span className='grow truncate'>{toLocalizedString(item.label, t)}</span>
|
|
225
224
|
</li>
|
|
226
225
|
);
|