@dxos/react-ui-editor 0.8.4-main.937b3ca → 0.8.4-main.9be5663bfe
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 +310 -388
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +310 -388
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Editor/Editor.d.ts +9 -15
- package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
- package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
- package/dist/types/src/components/EditorContent/EditorContent.d.ts.map +1 -1
- 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/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.map +1 -1
- package/dist/types/src/components/EditorToolbar/blocks.d.ts +3 -17
- package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/formatting.d.ts +3 -17
- package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/headings.d.ts +3 -17
- 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 +0 -1
- 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/view-mode.d.ts +3 -17
- package/dist/types/src/components/EditorToolbar/view-mode.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 +1 -1
- package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/stories/EditorToolbar.stories.d.ts +26 -26
- package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Experimental.stories.d.ts +1 -1
- package/dist/types/src/stories/Markdown.stories.d.ts +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 +1 -1
- package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
- package/dist/types/src/stories/TextEditor.stories.d.ts +1 -1
- package/dist/types/src/stories/components/EditorStory.d.ts +3 -3
- package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
- package/dist/types/src/stories/components/util.d.ts +3 -3
- 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 +1 -4
- package/dist/types/src/util/react.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +45 -45
- package/src/components/Editor/Editor.stories.tsx +6 -7
- package/src/components/Editor/Editor.tsx +21 -27
- package/src/components/EditorContent/EditorContent.tsx +1 -2
- package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +21 -28
- package/src/components/EditorMenuProvider/menu-presets.ts +1 -0
- package/src/components/EditorMenuProvider/useEditorMenu.ts +8 -1
- package/src/components/EditorPreviewProvider/EditorPreviewProvider.tsx +5 -7
- package/src/components/EditorToolbar/EditorToolbar.tsx +24 -61
- package/src/components/EditorToolbar/blocks.ts +53 -46
- package/src/components/EditorToolbar/formatting.ts +44 -46
- package/src/components/EditorToolbar/headings.ts +42 -49
- package/src/components/EditorToolbar/image.ts +16 -21
- package/src/components/EditorToolbar/index.ts +0 -1
- package/src/components/EditorToolbar/lists.ts +57 -0
- package/src/components/EditorToolbar/search.ts +16 -21
- package/src/components/EditorToolbar/view-mode.ts +34 -41
- package/src/stories/Automerge.stories.tsx +10 -12
- package/src/stories/Comments.stories.tsx +4 -5
- package/src/stories/EditorToolbar.stories.tsx +32 -17
- package/src/stories/Experimental.stories.tsx +6 -6
- 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 +60 -51
- package/src/stories/Tags.stories.tsx +5 -5
- package/src/stories/TextEditor.stories.tsx +2 -2
- package/src/stories/Theme.stories.tsx +2 -2
- package/src/stories/components/EditorStory.tsx +17 -12
- package/src/stories/components/util.tsx +49 -50
- package/src/translations.ts +29 -24
- package/src/util/react.tsx +2 -11
- 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/stories/CommandDialog.stories.d.ts +0 -14
- package/dist/types/src/stories/CommandDialog.stories.d.ts.map +0 -1
- package/src/components/EditorToolbar/actions.ts +0 -87
- package/src/stories/CommandDialog.stories.tsx +0 -81
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.9be5663bfe",
|
|
4
4
|
"description": "Text editor components.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@replit/codemirror-vim": "^6.2.1",
|
|
56
56
|
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
|
57
57
|
"@uiw/codemirror-theme-vscode": "^4.25.2",
|
|
58
|
-
"ajv": "^8.
|
|
58
|
+
"ajv": "^8.18.0",
|
|
59
59
|
"bind-event-listener": "^3.0.0",
|
|
60
60
|
"codemirror": "^6.0.1",
|
|
61
61
|
"lib0": "^0.2.65",
|
|
@@ -63,32 +63,32 @@
|
|
|
63
63
|
"lodash.merge": "^4.6.2",
|
|
64
64
|
"lodash.sortby": "^4.7.0",
|
|
65
65
|
"style-mod": "^4.1.0",
|
|
66
|
-
"@dxos/app-graph": "0.8.4-main.
|
|
67
|
-
"@dxos/client": "0.8.4-main.
|
|
68
|
-
"@dxos/
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/echo-db": "0.8.4-main.
|
|
73
|
-
"@dxos/invariant": "0.8.4-main.
|
|
74
|
-
"@dxos/
|
|
75
|
-
"@dxos/
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/
|
|
78
|
-
"@dxos/react-
|
|
79
|
-
"@dxos/
|
|
80
|
-
"@dxos/ui": "0.8.4-main.
|
|
81
|
-
"@dxos/
|
|
82
|
-
"@dxos/ui-theme": "0.8.4-main.
|
|
83
|
-
"@dxos/
|
|
84
|
-
"@dxos/
|
|
66
|
+
"@dxos/app-graph": "0.8.4-main.9be5663bfe",
|
|
67
|
+
"@dxos/client": "0.8.4-main.9be5663bfe",
|
|
68
|
+
"@dxos/context": "0.8.4-main.9be5663bfe",
|
|
69
|
+
"@dxos/async": "0.8.4-main.9be5663bfe",
|
|
70
|
+
"@dxos/display-name": "0.8.4-main.9be5663bfe",
|
|
71
|
+
"@dxos/echo": "0.8.4-main.9be5663bfe",
|
|
72
|
+
"@dxos/echo-db": "0.8.4-main.9be5663bfe",
|
|
73
|
+
"@dxos/invariant": "0.8.4-main.9be5663bfe",
|
|
74
|
+
"@dxos/debug": "0.8.4-main.9be5663bfe",
|
|
75
|
+
"@dxos/log": "0.8.4-main.9be5663bfe",
|
|
76
|
+
"@dxos/lit-ui": "0.8.4-main.9be5663bfe",
|
|
77
|
+
"@dxos/react-hooks": "0.8.4-main.9be5663bfe",
|
|
78
|
+
"@dxos/react-ui-menu": "0.8.4-main.9be5663bfe",
|
|
79
|
+
"@dxos/protocols": "0.8.4-main.9be5663bfe",
|
|
80
|
+
"@dxos/react-ui-mosaic": "0.8.4-main.9be5663bfe",
|
|
81
|
+
"@dxos/ui": "0.8.4-main.9be5663bfe",
|
|
82
|
+
"@dxos/ui-theme": "0.8.4-main.9be5663bfe",
|
|
83
|
+
"@dxos/util": "0.8.4-main.9be5663bfe",
|
|
84
|
+
"@dxos/ui-editor": "0.8.4-main.9be5663bfe"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
87
|
"@automerge/automerge": "3.2.3",
|
|
88
88
|
"@automerge/automerge-repo": "2.5.1",
|
|
89
89
|
"@automerge/automerge-repo-network-broadcastchannel": "2.5.1",
|
|
90
|
-
"@effect-atom/atom-react": "^0.
|
|
91
|
-
"@effect/platform": "0.
|
|
90
|
+
"@effect-atom/atom-react": "^0.5.0",
|
|
91
|
+
"@effect/platform": "0.94.4",
|
|
92
92
|
"@types/chai": "^4.2.15",
|
|
93
93
|
"@types/chai-dom": "^1.11.0",
|
|
94
94
|
"@types/lodash.defaultsdeep": "^4.6.6",
|
|
@@ -99,39 +99,39 @@
|
|
|
99
99
|
"@types/react-test-renderer": "^17.0.2",
|
|
100
100
|
"chai": "^4.4.1",
|
|
101
101
|
"chai-dom": "^1.11.0",
|
|
102
|
-
"effect": "3.
|
|
103
|
-
"happy-dom": "^
|
|
102
|
+
"effect": "3.20.0",
|
|
103
|
+
"happy-dom": "^20.0.0",
|
|
104
104
|
"jsdom": "^27.0.0",
|
|
105
105
|
"mocha": "^10.6.0",
|
|
106
106
|
"react": "~19.2.3",
|
|
107
107
|
"react-dom": "~19.2.3",
|
|
108
108
|
"react-test-renderer": "~19.2.0",
|
|
109
|
-
"vite": "7.1.
|
|
109
|
+
"vite": "^7.1.11",
|
|
110
110
|
"vite-plugin-top-level-await": "^1.6.0",
|
|
111
111
|
"vite-plugin-wasm": "^3.5.0",
|
|
112
|
-
"@dxos/config": "0.8.4-main.
|
|
113
|
-
"@dxos/echo
|
|
114
|
-
"@dxos/echo": "0.8.4-main.
|
|
115
|
-
"@dxos/keyboard": "0.8.4-main.
|
|
116
|
-
"@dxos/random": "0.8.4-main.
|
|
117
|
-
"@dxos/react-
|
|
118
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
119
|
-
"@dxos/react-
|
|
120
|
-
"@dxos/schema": "0.8.4-main.
|
|
121
|
-
"@dxos/
|
|
122
|
-
"@dxos/ui-
|
|
123
|
-
"@dxos/
|
|
124
|
-
"@dxos/ui-
|
|
112
|
+
"@dxos/config": "0.8.4-main.9be5663bfe",
|
|
113
|
+
"@dxos/echo": "0.8.4-main.9be5663bfe",
|
|
114
|
+
"@dxos/echo-atom": "0.8.4-main.9be5663bfe",
|
|
115
|
+
"@dxos/keyboard": "0.8.4-main.9be5663bfe",
|
|
116
|
+
"@dxos/random": "0.8.4-main.9be5663bfe",
|
|
117
|
+
"@dxos/react-client": "0.8.4-main.9be5663bfe",
|
|
118
|
+
"@dxos/react-ui": "0.8.4-main.9be5663bfe",
|
|
119
|
+
"@dxos/react-ui-attention": "0.8.4-main.9be5663bfe",
|
|
120
|
+
"@dxos/schema": "0.8.4-main.9be5663bfe",
|
|
121
|
+
"@dxos/storybook-utils": "0.8.4-main.9be5663bfe",
|
|
122
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.9be5663bfe",
|
|
123
|
+
"@dxos/ui-types": "0.8.4-main.9be5663bfe",
|
|
124
|
+
"@dxos/ui-theme": "0.8.4-main.9be5663bfe"
|
|
125
125
|
},
|
|
126
126
|
"peerDependencies": {
|
|
127
|
-
"@effect-atom/atom-react": "^0.
|
|
128
|
-
"@effect/platform": "0.
|
|
129
|
-
"effect": "3.
|
|
127
|
+
"@effect-atom/atom-react": "^0.5.0",
|
|
128
|
+
"@effect/platform": "0.94.4",
|
|
129
|
+
"effect": "3.20.0",
|
|
130
130
|
"react": "~19.2.3",
|
|
131
131
|
"react-dom": "~19.2.3",
|
|
132
|
-
"@dxos/react-client": "0.8.4-main.
|
|
133
|
-
"@dxos/ui
|
|
134
|
-
"@dxos/
|
|
132
|
+
"@dxos/react-client": "0.8.4-main.9be5663bfe",
|
|
133
|
+
"@dxos/react-ui": "0.8.4-main.9be5663bfe",
|
|
134
|
+
"@dxos/ui-theme": "0.8.4-main.9be5663bfe"
|
|
135
135
|
},
|
|
136
136
|
"publishConfig": {
|
|
137
137
|
"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,18 +20,17 @@ import {
|
|
|
20
20
|
} from '@dxos/ui-editor';
|
|
21
21
|
|
|
22
22
|
import { createMenuGroup } from '../EditorMenuProvider';
|
|
23
|
-
|
|
24
23
|
import { Editor, type EditorContentProps } from './Editor';
|
|
25
24
|
|
|
26
25
|
// TODO(burdon): PreviewPopoverProvider (MarkdownStream, Preview story).
|
|
27
26
|
// TODO(burdon): Adapt Markdown plugin to use new Editor (plan first to check fit).
|
|
28
27
|
// TODO(burdon): Remove redundant hooks and simplify props.
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
random.seed(1234);
|
|
31
30
|
|
|
32
31
|
const initialValue = ['# Blue Monday', '', 'How does it **feel**?', ''].join('\n');
|
|
33
32
|
|
|
34
|
-
const items =
|
|
33
|
+
const items = random.helpers.multiple(random.commerce.productName, { count: 10 }).sort();
|
|
35
34
|
|
|
36
35
|
// TODO(burdon): Adapter other tests in react-ui-editor/stories to use this pattern.
|
|
37
36
|
const withExtensions: Decorator<EditorContentProps> = (Story, { args }) => {
|
|
@@ -42,7 +41,7 @@ const withExtensions: Decorator<EditorContentProps> = (Story, { args }) => {
|
|
|
42
41
|
createThemeExtensions({ themeMode }),
|
|
43
42
|
createMarkdownExtensions(),
|
|
44
43
|
decorateMarkdown(),
|
|
45
|
-
automerge(createDocAccessor(createObject(Text.make(args.initialValue)), ['content'])),
|
|
44
|
+
automerge(createDocAccessor(createObject(Text.make({ content: args.initialValue })), ['content'])),
|
|
46
45
|
],
|
|
47
46
|
[themeMode],
|
|
48
47
|
);
|
|
@@ -53,7 +52,7 @@ const withExtensions: Decorator<EditorContentProps> = (Story, { args }) => {
|
|
|
53
52
|
const meta = {
|
|
54
53
|
title: 'ui/react-ui-editor/Editor',
|
|
55
54
|
component: Editor.Content,
|
|
56
|
-
decorators: [withExtensions, withTheme, withLayout({ layout: 'column' }), withAttention],
|
|
55
|
+
decorators: [withExtensions, withTheme(), withLayout({ layout: 'column' }), withAttention()],
|
|
57
56
|
parameters: {
|
|
58
57
|
layout: 'fullscreen',
|
|
59
58
|
},
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Extension } from '@codemirror/state';
|
|
6
|
+
import { type 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
|
|
|
@@ -33,10 +34,16 @@ type EditorContextValue = {
|
|
|
33
34
|
controller?: EditorController;
|
|
34
35
|
setController: (controller: EditorController) => void;
|
|
35
36
|
extensions?: Extension[];
|
|
36
|
-
|
|
37
|
+
state: Atom.Writable<EditorToolbarState>;
|
|
38
|
+
};
|
|
37
39
|
|
|
38
40
|
const [EditorContextProvider, useEditorContext] = createContext<EditorContextValue>('Editor');
|
|
39
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Access the editor context. Must be used within `Editor.Root`.
|
|
44
|
+
*/
|
|
45
|
+
export { useEditorContext };
|
|
46
|
+
|
|
40
47
|
//
|
|
41
48
|
// Root
|
|
42
49
|
//
|
|
@@ -84,6 +91,8 @@ EditorRoot.displayName = 'Editor.Root';
|
|
|
84
91
|
// Viewport
|
|
85
92
|
//
|
|
86
93
|
|
|
94
|
+
const EDITOR_VIEWPORT_NAME = 'Editor.Viewport';
|
|
95
|
+
|
|
87
96
|
type EditorViewportProps = ThemedClassName<PropsWithChildren<{}>>;
|
|
88
97
|
|
|
89
98
|
/**
|
|
@@ -91,18 +100,20 @@ type EditorViewportProps = ThemedClassName<PropsWithChildren<{}>>;
|
|
|
91
100
|
*/
|
|
92
101
|
const EditorViewport = ({ classNames, children }: EditorViewportProps) => {
|
|
93
102
|
return (
|
|
94
|
-
<div role='none' className={mx('grid grid-rows-[min-content_1fr]
|
|
103
|
+
<div role='none' className={mx('grid grid-rows-[min-content_1fr] h-full overflow-hidden', classNames)}>
|
|
95
104
|
{children}
|
|
96
105
|
</div>
|
|
97
106
|
);
|
|
98
107
|
};
|
|
99
108
|
|
|
100
|
-
EditorViewport.displayName =
|
|
109
|
+
EditorViewport.displayName = EDITOR_VIEWPORT_NAME;
|
|
101
110
|
|
|
102
111
|
//
|
|
103
112
|
// Content
|
|
104
113
|
//
|
|
105
114
|
|
|
115
|
+
const EDITOR_CONTENT_NAME = 'Editor.Content';
|
|
116
|
+
|
|
106
117
|
type EditorContentProps = Omit<NaturalEditorContentProps, 'ref'>;
|
|
107
118
|
|
|
108
119
|
/**
|
|
@@ -110,7 +121,7 @@ type EditorContentProps = Omit<NaturalEditorContentProps, 'ref'>;
|
|
|
110
121
|
* Automatically registers the editor controller with the context.
|
|
111
122
|
*/
|
|
112
123
|
const EditorContent = ({ extensions: providedExtensions, ...props }: EditorContentProps) => {
|
|
113
|
-
const { extensions: additionalExtensions = [], setController } = useEditorContext(
|
|
124
|
+
const { extensions: additionalExtensions = [], setController } = useEditorContext(EDITOR_CONTENT_NAME);
|
|
114
125
|
|
|
115
126
|
const extensions = useMemo(
|
|
116
127
|
() => [additionalExtensions, providedExtensions].filter(isNonNullable).flat(),
|
|
@@ -120,12 +131,14 @@ const EditorContent = ({ extensions: providedExtensions, ...props }: EditorConte
|
|
|
120
131
|
return <NaturalEditorContent {...props} extensions={extensions} ref={setController} />;
|
|
121
132
|
};
|
|
122
133
|
|
|
123
|
-
EditorContent.displayName =
|
|
134
|
+
EditorContent.displayName = EDITOR_CONTENT_NAME;
|
|
124
135
|
|
|
125
136
|
//
|
|
126
137
|
// Toolbar
|
|
127
138
|
//
|
|
128
139
|
|
|
140
|
+
const EDITOR_TOOLBAR_NAME = 'Editor.Toolbar';
|
|
141
|
+
|
|
129
142
|
type EditorToolbarProps = Omit<NaturalEditorToolbarProps, 'getView' | 'state'>;
|
|
130
143
|
|
|
131
144
|
/**
|
|
@@ -133,7 +146,7 @@ type EditorToolbarProps = Omit<NaturalEditorToolbarProps, 'getView' | 'state'>;
|
|
|
133
146
|
* Automatically connects to the editor view through context.
|
|
134
147
|
*/
|
|
135
148
|
const EditorToolbar = (props: EditorToolbarProps) => {
|
|
136
|
-
const { controller, state } = useEditorContext(
|
|
149
|
+
const { controller, state } = useEditorContext(EDITOR_TOOLBAR_NAME);
|
|
137
150
|
|
|
138
151
|
// TODO(burdon): Fix invariant.
|
|
139
152
|
const getView = useCallback(() => {
|
|
@@ -144,25 +157,12 @@ const EditorToolbar = (props: EditorToolbarProps) => {
|
|
|
144
157
|
return <NaturalEditorToolbar {...props} getView={getView} state={state} />;
|
|
145
158
|
};
|
|
146
159
|
|
|
147
|
-
EditorToolbar.displayName =
|
|
160
|
+
EditorToolbar.displayName = EDITOR_TOOLBAR_NAME;
|
|
148
161
|
|
|
149
162
|
//
|
|
150
163
|
// Editor
|
|
151
164
|
//
|
|
152
165
|
|
|
153
|
-
/**
|
|
154
|
-
* Compound editor component following the Radix UI pattern.
|
|
155
|
-
*
|
|
156
|
-
* @example
|
|
157
|
-
* ```tsx
|
|
158
|
-
* EditorMenuGroup.Root>
|
|
159
|
-
* EditorMenuGroup.Toolbar />
|
|
160
|
-
* EditorMenuGroup.Viewport>
|
|
161
|
-
* EditorMenuGroup.Content extensions={[...]} />
|
|
162
|
-
* </Editor.Viewport>
|
|
163
|
-
* </Editor.Root>
|
|
164
|
-
* ```
|
|
165
|
-
*/
|
|
166
166
|
export const Editor = {
|
|
167
167
|
Root: EditorRoot,
|
|
168
168
|
Viewport: EditorViewport,
|
|
@@ -170,10 +170,4 @@ export const Editor = {
|
|
|
170
170
|
Toolbar: EditorToolbar,
|
|
171
171
|
};
|
|
172
172
|
|
|
173
|
-
export type {
|
|
174
|
-
EditorController,
|
|
175
|
-
EditorRootProps,
|
|
176
|
-
EditorViewportProps,
|
|
177
|
-
EditorContentProps,
|
|
178
|
-
// EditorToolbarProps, // TODO(burdon): Restore once removed deprecated props.
|
|
179
|
-
};
|
|
173
|
+
export type { EditorController, EditorRootProps, EditorViewportProps, EditorContentProps, EditorToolbarProps };
|
|
@@ -11,7 +11,6 @@ import { initialSync } from '@dxos/ui-editor';
|
|
|
11
11
|
import { mx } from '@dxos/ui-theme';
|
|
12
12
|
|
|
13
13
|
import { type UseTextEditorProps, useTextEditor } from '../../hooks';
|
|
14
|
-
|
|
15
14
|
import { type EditorController, createEditorController } from './controller';
|
|
16
15
|
|
|
17
16
|
export type EditorContentProps = ThemedClassName<
|
|
@@ -72,7 +71,7 @@ export const EditorContent = forwardRef<EditorController, EditorContentProps>(
|
|
|
72
71
|
<div
|
|
73
72
|
role='none'
|
|
74
73
|
className={mx(
|
|
75
|
-
'
|
|
74
|
+
'w-full outline-hidden focus:border-accent-surface focus-within:border-neutral-focus-indicator',
|
|
76
75
|
classNames,
|
|
77
76
|
)}
|
|
78
77
|
ref={parentRef}
|
|
@@ -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
|
*/
|
|
@@ -78,10 +77,8 @@ export const EditorMenuProvider = ({
|
|
|
78
77
|
root,
|
|
79
78
|
DX_ANCHOR_ACTIVATE as any,
|
|
80
79
|
(event: DxAnchorActivate) => {
|
|
81
|
-
const { trigger,
|
|
82
|
-
|
|
83
|
-
// If this has a `refId`, then it’s probably a URL or DXN and out of scope for this component.
|
|
84
|
-
if (!refId) {
|
|
80
|
+
const { trigger, dxn } = event;
|
|
81
|
+
if (!dxn) {
|
|
85
82
|
triggerRef.current = trigger as HTMLButtonElement;
|
|
86
83
|
if (onActivate) {
|
|
87
84
|
onActivate({ view: viewRef.current!, trigger: trigger.getAttribute('data-trigger') ?? undefined });
|
|
@@ -115,32 +112,32 @@ export const EditorMenuProvider = ({
|
|
|
115
112
|
<Popover.Portal>
|
|
116
113
|
<Popover.Content
|
|
117
114
|
align='start'
|
|
118
|
-
classNames={
|
|
119
|
-
'overflow-y-auto',
|
|
120
|
-
!menuGroups.length && 'hidden',
|
|
121
|
-
])}
|
|
115
|
+
classNames={['flex flex-col', !menuGroups.length && 'hidden']}
|
|
122
116
|
style={{
|
|
123
117
|
maxBlockSize: 36 * numItems + 10,
|
|
124
118
|
}}
|
|
125
|
-
|
|
126
|
-
* NOTE: We keep the focus in the editor, but Radix routes escape key.
|
|
127
|
-
*/
|
|
119
|
+
// NOTE: We keep the focus in the editor, but Radix routes escape key.
|
|
128
120
|
onEscapeKeyDown={() => {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
121
|
+
const currentView = viewRef.current;
|
|
122
|
+
if (currentView) {
|
|
123
|
+
onCancel?.({ view: currentView });
|
|
124
|
+
}
|
|
132
125
|
}}
|
|
133
126
|
onOpenAutoFocus={(event) => event.preventDefault()}
|
|
134
127
|
>
|
|
135
|
-
<Popover.Viewport classNames=
|
|
136
|
-
<
|
|
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>
|
|
137
134
|
</Popover.Viewport>
|
|
138
135
|
<Popover.Arrow />
|
|
139
136
|
</Popover.Content>
|
|
140
137
|
</Popover.Portal>
|
|
141
138
|
|
|
142
139
|
{/* Content */}
|
|
143
|
-
<div
|
|
140
|
+
<div role='none' className='contents' ref={setRoot}>
|
|
144
141
|
{children}
|
|
145
142
|
</div>
|
|
146
143
|
</Popover.Root>
|
|
@@ -162,7 +159,7 @@ const Menu = ({ groups, currentItem, onSelect }: MenuProps) => {
|
|
|
162
159
|
{groups.map((group, index) => (
|
|
163
160
|
<Fragment key={group.id}>
|
|
164
161
|
<MenuGroup group={group} currentItem={currentItem} onSelect={onSelect} />
|
|
165
|
-
{index < groups.length - 1 && <div className={tx('menu.separator',
|
|
162
|
+
{index < groups.length - 1 && <div className={tx('menu.separator', {})} />}
|
|
166
163
|
</Fragment>
|
|
167
164
|
))}
|
|
168
165
|
</ul>
|
|
@@ -185,7 +182,7 @@ const MenuGroup = ({ group, currentItem, onSelect }: MenuGroupProps) => {
|
|
|
185
182
|
return (
|
|
186
183
|
<>
|
|
187
184
|
{group.label && (
|
|
188
|
-
<div className={tx('menu.groupLabel',
|
|
185
|
+
<div className={tx('menu.groupLabel', {})}>
|
|
189
186
|
<span>{toLocalizedString(group.label, t)}</span>
|
|
190
187
|
</div>
|
|
191
188
|
)}
|
|
@@ -221,12 +218,8 @@ const MenuItem = ({ item, current, onSelect }: MenuItemProps) => {
|
|
|
221
218
|
const handleSelect = useCallback(() => onSelect?.(item), [item, onSelect]);
|
|
222
219
|
|
|
223
220
|
return (
|
|
224
|
-
<li
|
|
225
|
-
|
|
226
|
-
className={tx('menu.item', 'menu__item--exotic-unfocusable', {}, [current && 'bg-hoverSurface'])}
|
|
227
|
-
onClick={handleSelect}
|
|
228
|
-
>
|
|
229
|
-
{item.icon && <Icon icon={item.icon} size={5} />}
|
|
221
|
+
<li ref={listRef} className={tx('menu.item', {}, [current && 'bg-hover-surface'])} onClick={handleSelect}>
|
|
222
|
+
{item.icon && <Icon icon={item.icon} />}
|
|
230
223
|
<span className='grow truncate'>{toLocalizedString(item.label, t)}</span>
|
|
231
224
|
</li>
|
|
232
225
|
);
|
|
@@ -110,6 +110,7 @@ export const linkSlashCommands: EditorMenuGroup = {
|
|
|
110
110
|
label: 'Block embed',
|
|
111
111
|
icon: 'ph--lego--regular',
|
|
112
112
|
onSelect: ({ view, head }) => {
|
|
113
|
+
// Seed the same query shape as typing "@@" manually.
|
|
113
114
|
view.dispatch({
|
|
114
115
|
changes: { from: head, insert: '@@' },
|
|
115
116
|
selection: { anchor: head + 2, head: head + 2 },
|
|
@@ -62,7 +62,8 @@ export const useEditorMenu = ({
|
|
|
62
62
|
const getMenuOptions = useCallback<NonNullable<UseEditorMenuProps['getMenu']>>(
|
|
63
63
|
async ({ text, trigger, ...props }) => {
|
|
64
64
|
const groups = (await getMenu?.({ text, trigger, ...props })) ?? [];
|
|
65
|
-
|
|
65
|
+
// The "@" menu can use "@@" as syntax for block embeds, so it owns its own query filtering.
|
|
66
|
+
return filter && trigger !== '@'
|
|
66
67
|
? filterMenuGroups(groups, (item) =>
|
|
67
68
|
text ? (item.label as string).toLowerCase().startsWith(text.toLowerCase()) : true,
|
|
68
69
|
)
|
|
@@ -108,7 +109,13 @@ export const useEditorMenu = ({
|
|
|
108
109
|
);
|
|
109
110
|
|
|
110
111
|
const handleSelect = useCallback<NonNullable<UseEditorMenu['onSelect']>>(({ view, item }) => {
|
|
112
|
+
// Delete trigger range (e.g., "/" and any typed filter text).
|
|
113
|
+
const { range } = view.state.field(popoverStateField) ?? {};
|
|
114
|
+
if (range) {
|
|
115
|
+
view.dispatch({ changes: { from: range.from, to: range.to, insert: '' } });
|
|
116
|
+
}
|
|
111
117
|
void item.onSelect?.({ view, head: view.state.selection.main.head });
|
|
118
|
+
view.focus();
|
|
112
119
|
}, []);
|
|
113
120
|
|
|
114
121
|
const handleCancel = useCallback<NonNullable<UseEditorMenu['onCancel']>>(({ view }) => {
|
|
@@ -32,16 +32,16 @@ export const EditorPreviewProvider = ({ children, onLookup }: EditorPreviewProvi
|
|
|
32
32
|
|
|
33
33
|
const handleActivate = useCallback(
|
|
34
34
|
(event: DxAnchorActivate) => {
|
|
35
|
-
const {
|
|
35
|
+
const { dxn, label, trigger } = event;
|
|
36
36
|
setValue((value) => ({
|
|
37
37
|
...value,
|
|
38
|
-
link: { label,
|
|
38
|
+
link: { label, dxn },
|
|
39
39
|
pending: true,
|
|
40
40
|
}));
|
|
41
41
|
|
|
42
|
-
triggerRef.current =
|
|
42
|
+
triggerRef.current = trigger;
|
|
43
43
|
queueMicrotask(() => setOpen(true));
|
|
44
|
-
void onLookup?.({ label,
|
|
44
|
+
void onLookup?.({ label, dxn }).then((target) =>
|
|
45
45
|
setValue((value) => ({
|
|
46
46
|
...value,
|
|
47
47
|
target: target ?? undefined,
|
|
@@ -68,9 +68,7 @@ export const EditorPreviewProvider = ({ children, onLookup }: EditorPreviewProvi
|
|
|
68
68
|
<EditorPreviewContextProvider pending={value.pending} link={value.link} target={value.target}>
|
|
69
69
|
<Popover.Root open={open} onOpenChange={setOpen}>
|
|
70
70
|
<Popover.VirtualTrigger virtualRef={triggerRef as unknown as RefObject<HTMLButtonElement>} />
|
|
71
|
-
|
|
72
|
-
{/* Content */}
|
|
73
|
-
<div ref={setRoot} role='none' className='contents'>
|
|
71
|
+
<div role='none' className='contents' ref={setRoot}>
|
|
74
72
|
{children}
|
|
75
73
|
</div>
|
|
76
74
|
</Popover.Root>
|
|
@@ -8,24 +8,17 @@ import React, { memo, useMemo } from 'react';
|
|
|
8
8
|
|
|
9
9
|
import { type Node } from '@dxos/app-graph';
|
|
10
10
|
import { ElevationProvider, type ThemedClassName } from '@dxos/react-ui';
|
|
11
|
-
import {
|
|
12
|
-
type ActionGraphProps,
|
|
13
|
-
type MenuAction,
|
|
14
|
-
MenuProvider,
|
|
15
|
-
ToolbarMenu,
|
|
16
|
-
createGapSeparator,
|
|
17
|
-
useMenuActions,
|
|
18
|
-
} from '@dxos/react-ui-menu';
|
|
11
|
+
import { type ActionGraphProps, Menu, type MenuAction, MenuBuilder, useMenuActions } from '@dxos/react-ui-menu';
|
|
19
12
|
import { type EditorViewMode } from '@dxos/ui-editor';
|
|
20
13
|
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
14
|
+
import { addBlocks } from './blocks';
|
|
15
|
+
import { addFormatting } from './formatting';
|
|
16
|
+
import { addHeadings } from './headings';
|
|
17
|
+
import { addImageUpload } from './image';
|
|
18
|
+
import { addLists } from './lists';
|
|
19
|
+
import { addSearch } from './search';
|
|
27
20
|
import { type EditorToolbarState } from './useEditorToolbar';
|
|
28
|
-
import {
|
|
21
|
+
import { addViewMode } from './view-mode';
|
|
29
22
|
|
|
30
23
|
export type EditorToolbarFeatureFlags = Partial<{
|
|
31
24
|
showHeadings: boolean;
|
|
@@ -55,15 +48,14 @@ export type EditorToolbarProps = ThemedClassName<
|
|
|
55
48
|
} & (EditorToolbarActionGraphProps & EditorToolbarFeatureFlags)
|
|
56
49
|
>;
|
|
57
50
|
|
|
58
|
-
// TODO(burdon): Remove role dependency.
|
|
59
51
|
export const EditorToolbar = memo(({ classNames, role, attendableId, onAction, ...props }: EditorToolbarProps) => {
|
|
60
|
-
const
|
|
52
|
+
const menuActions = useEditorToolbarActionGraph(props);
|
|
61
53
|
|
|
62
54
|
return (
|
|
63
55
|
<ElevationProvider elevation={role === 'section' ? 'positioned' : 'base'}>
|
|
64
|
-
<
|
|
65
|
-
<
|
|
66
|
-
</
|
|
56
|
+
<Menu.Root {...menuActions} attendableId={attendableId} onAction={onAction}>
|
|
57
|
+
<Menu.Toolbar classNames={classNames} />
|
|
58
|
+
</Menu.Root>
|
|
67
59
|
</ElevationProvider>
|
|
68
60
|
);
|
|
69
61
|
});
|
|
@@ -74,6 +66,7 @@ type ToolbarActionsProps = Pick<EditorToolbarActionGraphProps, 'state' | 'getVie
|
|
|
74
66
|
// TODO(wittjosiah): Toolbar re-rendering is causing this graph to be recreated and breaking reactivity in some cases.
|
|
75
67
|
// E.g. for toolbar dropdowns which use active icon, the icon is not updated when the active item changes.
|
|
76
68
|
// This is currently only happening in the markdown plugin usage and should be reproduced in an editor story.
|
|
69
|
+
// TODO(burdon): Some actions should toggle the state (e.g., toggle bullets on/off depending on the current state).
|
|
77
70
|
const useEditorToolbarActionGraph = ({ state, getView, customActions, ...features }: ToolbarActionsProps) => {
|
|
78
71
|
const menuCreator = useMemo(
|
|
79
72
|
() => createToolbarActions({ state, getView, customActions, ...features }),
|
|
@@ -101,48 +94,18 @@ const createToolbarActions = ({
|
|
|
101
94
|
...features
|
|
102
95
|
}: ToolbarActionsProps): Atom.Atom<ActionGraphProps> => {
|
|
103
96
|
return Atom.make((get) => {
|
|
104
|
-
const graph: ActionGraphProps = {
|
|
105
|
-
nodes: [],
|
|
106
|
-
edges: [],
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
// TODO(burdon): Builder pattern?
|
|
110
|
-
const addSubGraph = (graph: ActionGraphProps, subGraph: ActionGraphProps) => {
|
|
111
|
-
graph.nodes.push(...subGraph.nodes);
|
|
112
|
-
graph.edges.push(...subGraph.edges);
|
|
113
|
-
};
|
|
114
|
-
|
|
115
97
|
// Subscribe to state changes.
|
|
116
98
|
const stateSnapshot = get(state);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
addSubGraph(graph, createBlocks(stateSnapshot, getView));
|
|
129
|
-
}
|
|
130
|
-
if (features?.onImageUpload) {
|
|
131
|
-
addSubGraph(graph, createImageUpload(features.onImageUpload!));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
addSubGraph(graph, createGapSeparator());
|
|
135
|
-
|
|
136
|
-
if (customActions) {
|
|
137
|
-
addSubGraph(graph, get(customActions));
|
|
138
|
-
}
|
|
139
|
-
if (features?.showSearch ?? true) {
|
|
140
|
-
addSubGraph(graph, createSearch(getView));
|
|
141
|
-
}
|
|
142
|
-
if (features?.onViewModeChange) {
|
|
143
|
-
addSubGraph(graph, createViewMode(stateSnapshot, features.onViewModeChange!));
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return graph;
|
|
99
|
+
return MenuBuilder.make()
|
|
100
|
+
.subgraph(features?.showHeadings !== false && addHeadings(stateSnapshot, getView))
|
|
101
|
+
.subgraph(features?.showFormatting !== false && addFormatting(stateSnapshot, getView))
|
|
102
|
+
.subgraph(features?.showLists !== false && addLists(stateSnapshot, getView))
|
|
103
|
+
.subgraph(features?.showBlocks !== false && addBlocks(stateSnapshot, getView))
|
|
104
|
+
.subgraph(features?.onImageUpload && addImageUpload(features.onImageUpload))
|
|
105
|
+
.separator('gap')
|
|
106
|
+
.subgraph(customActions && get(customActions))
|
|
107
|
+
.subgraph(features?.showSearch !== false && addSearch(getView))
|
|
108
|
+
.subgraph(features?.onViewModeChange && addViewMode(stateSnapshot, features.onViewModeChange))
|
|
109
|
+
.build();
|
|
147
110
|
});
|
|
148
111
|
};
|