@elementor/editor-controls 4.0.0-512 → 4.0.0-514
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/index.d.mts +11 -12
- package/dist/index.d.ts +11 -12
- package/dist/index.js +434 -480
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +448 -496
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -15
- package/src/components/inline-editor-toolbar.tsx +78 -74
- package/src/components/inline-editor.tsx +157 -231
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-controls",
|
|
3
3
|
"description": "This package contains the controls model and utils for the Elementor editor",
|
|
4
|
-
"version": "4.0.0-
|
|
4
|
+
"version": "4.0.0-514",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -40,22 +40,22 @@
|
|
|
40
40
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@elementor/editor-current-user": "4.0.0-
|
|
44
|
-
"@elementor/editor-elements": "4.0.0-
|
|
45
|
-
"@elementor/editor-props": "4.0.0-
|
|
46
|
-
"@elementor/editor-responsive": "4.0.0-
|
|
47
|
-
"@elementor/editor-ui": "4.0.0-
|
|
48
|
-
"@elementor/editor-v1-adapters": "4.0.0-
|
|
49
|
-
"@elementor/env": "4.0.0-
|
|
50
|
-
"@elementor/http-client": "4.0.0-
|
|
43
|
+
"@elementor/editor-current-user": "4.0.0-514",
|
|
44
|
+
"@elementor/editor-elements": "4.0.0-514",
|
|
45
|
+
"@elementor/editor-props": "4.0.0-514",
|
|
46
|
+
"@elementor/editor-responsive": "4.0.0-514",
|
|
47
|
+
"@elementor/editor-ui": "4.0.0-514",
|
|
48
|
+
"@elementor/editor-v1-adapters": "4.0.0-514",
|
|
49
|
+
"@elementor/env": "4.0.0-514",
|
|
50
|
+
"@elementor/http-client": "4.0.0-514",
|
|
51
51
|
"@elementor/icons": "^1.63.0",
|
|
52
|
-
"@elementor/locations": "4.0.0-
|
|
53
|
-
"@elementor/mixpanel": "4.0.0-
|
|
54
|
-
"@elementor/query": "4.0.0-
|
|
55
|
-
"@elementor/session": "4.0.0-
|
|
52
|
+
"@elementor/locations": "4.0.0-514",
|
|
53
|
+
"@elementor/mixpanel": "4.0.0-514",
|
|
54
|
+
"@elementor/query": "4.0.0-514",
|
|
55
|
+
"@elementor/session": "4.0.0-514",
|
|
56
56
|
"@elementor/ui": "1.36.17",
|
|
57
|
-
"@elementor/utils": "4.0.0-
|
|
58
|
-
"@elementor/wp-media": "4.0.0-
|
|
57
|
+
"@elementor/utils": "4.0.0-514",
|
|
58
|
+
"@elementor/wp-media": "4.0.0-514",
|
|
59
59
|
"@wordpress/i18n": "^5.13.0",
|
|
60
60
|
"@monaco-editor/react": "^4.7.0",
|
|
61
61
|
"dayjs": "^1.11.18",
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
Box,
|
|
17
17
|
IconButton,
|
|
18
|
+
type SxProps,
|
|
19
|
+
type Theme,
|
|
18
20
|
ToggleButton,
|
|
19
21
|
ToggleButtonGroup,
|
|
20
22
|
toggleButtonGroupClasses,
|
|
@@ -26,88 +28,17 @@ import { __ } from '@wordpress/i18n';
|
|
|
26
28
|
|
|
27
29
|
import { UrlPopover } from './url-popover';
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
!! getElementSetting< LinkPropValue >( elementId, 'link' )?.value?.destination;
|
|
31
|
-
|
|
32
|
-
type InlineEditorToolbarProps = {
|
|
31
|
+
export type InlineEditorToolbarProps = {
|
|
33
32
|
editor: Editor;
|
|
34
33
|
elementId?: ElementID;
|
|
34
|
+
sx?: SxProps< Theme >;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
const toolbarButtons = {
|
|
38
|
-
clear: {
|
|
39
|
-
label: __( 'Clear', 'elementor' ),
|
|
40
|
-
icon: <MinusIcon fontSize="tiny" />,
|
|
41
|
-
action: 'clear',
|
|
42
|
-
method: ( editor: Editor ) => {
|
|
43
|
-
editor.chain().focus().clearNodes().unsetAllMarks().run();
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
bold: {
|
|
47
|
-
label: __( 'Bold', 'elementor' ),
|
|
48
|
-
icon: <BoldIcon fontSize="tiny" />,
|
|
49
|
-
action: 'bold',
|
|
50
|
-
method: ( editor: Editor ) => {
|
|
51
|
-
editor.chain().focus().toggleBold().run();
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
italic: {
|
|
55
|
-
label: __( 'Italic', 'elementor' ),
|
|
56
|
-
icon: <ItalicIcon fontSize="tiny" />,
|
|
57
|
-
action: 'italic',
|
|
58
|
-
method: ( editor: Editor ) => {
|
|
59
|
-
editor.chain().focus().toggleItalic().run();
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
underline: {
|
|
63
|
-
label: __( 'Underline', 'elementor' ),
|
|
64
|
-
icon: <UnderlineIcon fontSize="tiny" />,
|
|
65
|
-
action: 'underline',
|
|
66
|
-
method: ( editor: Editor ) => {
|
|
67
|
-
editor.chain().focus().toggleUnderline().run();
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
strike: {
|
|
71
|
-
label: __( 'Strikethrough', 'elementor' ),
|
|
72
|
-
icon: <StrikethroughIcon fontSize="tiny" />,
|
|
73
|
-
action: 'strike',
|
|
74
|
-
method: ( editor: Editor ) => {
|
|
75
|
-
editor.chain().focus().toggleStrike().run();
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
superscript: {
|
|
79
|
-
label: __( 'Superscript', 'elementor' ),
|
|
80
|
-
icon: <SuperscriptIcon fontSize="tiny" />,
|
|
81
|
-
action: 'superscript',
|
|
82
|
-
method: ( editor: Editor ) => {
|
|
83
|
-
editor.chain().focus().toggleSuperscript().run();
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
subscript: {
|
|
87
|
-
label: __( 'Subscript', 'elementor' ),
|
|
88
|
-
icon: <SubscriptIcon fontSize="tiny" />,
|
|
89
|
-
action: 'subscript',
|
|
90
|
-
method: ( editor: Editor ) => {
|
|
91
|
-
editor.chain().focus().toggleSubscript().run();
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
link: {
|
|
95
|
-
label: __( 'Link', 'elementor' ),
|
|
96
|
-
icon: <LinkIcon fontSize="tiny" />,
|
|
97
|
-
action: 'link',
|
|
98
|
-
method: null,
|
|
99
|
-
},
|
|
100
|
-
} as const;
|
|
101
|
-
|
|
102
37
|
type ToolbarButtonKeys = keyof typeof toolbarButtons;
|
|
103
38
|
|
|
104
39
|
type FormatAction = Omit< ToolbarButtonKeys, 'clear' >;
|
|
105
40
|
|
|
106
|
-
const {
|
|
107
|
-
|
|
108
|
-
const possibleFormats: FormatAction[] = Object.keys( formatButtons ) as FormatAction[];
|
|
109
|
-
|
|
110
|
-
export const InlineEditorToolbar = ( { editor, elementId }: InlineEditorToolbarProps ) => {
|
|
41
|
+
export const InlineEditorToolbar = ( { editor, elementId, sx = {} }: InlineEditorToolbarProps ) => {
|
|
111
42
|
const [ urlValue, setUrlValue ] = useState( '' );
|
|
112
43
|
const [ openInNewTab, setOpenInNewTab ] = useState( false );
|
|
113
44
|
const toolbarRef = useRef< HTMLDivElement >( null );
|
|
@@ -186,6 +117,7 @@ export const InlineEditorToolbar = ( { editor, elementId }: InlineEditorToolbarP
|
|
|
186
117
|
alignItems: 'center',
|
|
187
118
|
visibility: linkPopupState.isOpen ? 'hidden' : 'visible',
|
|
188
119
|
pointerEvents: linkPopupState.isOpen ? 'none' : 'all',
|
|
120
|
+
...sx,
|
|
189
121
|
} }
|
|
190
122
|
>
|
|
191
123
|
<Tooltip title={ clearButton.label } placement="top" sx={ { borderRadius: '8px' } }>
|
|
@@ -247,3 +179,75 @@ export const InlineEditorToolbar = ( { editor, elementId }: InlineEditorToolbarP
|
|
|
247
179
|
</Box>
|
|
248
180
|
);
|
|
249
181
|
};
|
|
182
|
+
|
|
183
|
+
const checkIfElementHasLink = ( elementId: ElementID ): boolean =>
|
|
184
|
+
!! getElementSetting< LinkPropValue >( elementId, 'link' )?.value?.destination;
|
|
185
|
+
|
|
186
|
+
const toolbarButtons = {
|
|
187
|
+
clear: {
|
|
188
|
+
label: __( 'Clear', 'elementor' ),
|
|
189
|
+
icon: <MinusIcon fontSize="tiny" />,
|
|
190
|
+
action: 'clear',
|
|
191
|
+
method: ( editor: Editor ) => {
|
|
192
|
+
editor.chain().focus().clearNodes().unsetAllMarks().run();
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
bold: {
|
|
196
|
+
label: __( 'Bold', 'elementor' ),
|
|
197
|
+
icon: <BoldIcon fontSize="tiny" />,
|
|
198
|
+
action: 'bold',
|
|
199
|
+
method: ( editor: Editor ) => {
|
|
200
|
+
editor.chain().focus().toggleBold().run();
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
italic: {
|
|
204
|
+
label: __( 'Italic', 'elementor' ),
|
|
205
|
+
icon: <ItalicIcon fontSize="tiny" />,
|
|
206
|
+
action: 'italic',
|
|
207
|
+
method: ( editor: Editor ) => {
|
|
208
|
+
editor.chain().focus().toggleItalic().run();
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
underline: {
|
|
212
|
+
label: __( 'Underline', 'elementor' ),
|
|
213
|
+
icon: <UnderlineIcon fontSize="tiny" />,
|
|
214
|
+
action: 'underline',
|
|
215
|
+
method: ( editor: Editor ) => {
|
|
216
|
+
editor.chain().focus().toggleUnderline().run();
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
strike: {
|
|
220
|
+
label: __( 'Strikethrough', 'elementor' ),
|
|
221
|
+
icon: <StrikethroughIcon fontSize="tiny" />,
|
|
222
|
+
action: 'strike',
|
|
223
|
+
method: ( editor: Editor ) => {
|
|
224
|
+
editor.chain().focus().toggleStrike().run();
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
superscript: {
|
|
228
|
+
label: __( 'Superscript', 'elementor' ),
|
|
229
|
+
icon: <SuperscriptIcon fontSize="tiny" />,
|
|
230
|
+
action: 'superscript',
|
|
231
|
+
method: ( editor: Editor ) => {
|
|
232
|
+
editor.chain().focus().toggleSuperscript().run();
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
subscript: {
|
|
236
|
+
label: __( 'Subscript', 'elementor' ),
|
|
237
|
+
icon: <SubscriptIcon fontSize="tiny" />,
|
|
238
|
+
action: 'subscript',
|
|
239
|
+
method: ( editor: Editor ) => {
|
|
240
|
+
editor.chain().focus().toggleSubscript().run();
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
link: {
|
|
244
|
+
label: __( 'Link', 'elementor' ),
|
|
245
|
+
icon: <LinkIcon fontSize="tiny" />,
|
|
246
|
+
action: 'link',
|
|
247
|
+
method: null,
|
|
248
|
+
},
|
|
249
|
+
} as const;
|
|
250
|
+
|
|
251
|
+
const { clear: clearButton, ...formatButtons } = toolbarButtons;
|
|
252
|
+
|
|
253
|
+
const possibleFormats: FormatAction[] = Object.keys( formatButtons ) as FormatAction[];
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import {
|
|
3
3
|
type DependencyList,
|
|
4
|
-
|
|
4
|
+
type Dispatch,
|
|
5
5
|
type PropsWithChildren,
|
|
6
6
|
type RefObject,
|
|
7
|
+
type SetStateAction,
|
|
7
8
|
useEffect,
|
|
8
9
|
useRef,
|
|
9
|
-
useState,
|
|
10
10
|
} from 'react';
|
|
11
|
-
import {
|
|
11
|
+
import { Box, ClickAwayListener, type SxProps, type Theme } from '@elementor/ui';
|
|
12
12
|
import Bold from '@tiptap/extension-bold';
|
|
13
13
|
import Document from '@tiptap/extension-document';
|
|
14
14
|
import HardBreak from '@tiptap/extension-hard-break';
|
|
@@ -21,75 +21,173 @@ import Subscript from '@tiptap/extension-subscript';
|
|
|
21
21
|
import Superscript from '@tiptap/extension-superscript';
|
|
22
22
|
import Text from '@tiptap/extension-text';
|
|
23
23
|
import Underline from '@tiptap/extension-underline';
|
|
24
|
-
import { type EditorView } from '@tiptap/pm/view';
|
|
24
|
+
import { type EditorProps, type EditorView } from '@tiptap/pm/view';
|
|
25
25
|
import { type Editor, EditorContent, useEditor } from '@tiptap/react';
|
|
26
26
|
|
|
27
27
|
import { isEmpty } from '../utils/inline-editing';
|
|
28
|
-
import { InlineEditorToolbar } from './inline-editor-toolbar';
|
|
29
28
|
|
|
30
29
|
const ITALIC_KEYBOARD_SHORTCUT = 'i';
|
|
31
30
|
const BOLD_KEYBOARD_SHORTCUT = 'b';
|
|
32
31
|
const UNDERLINE_KEYBOARD_SHORTCUT = 'u';
|
|
33
32
|
|
|
34
|
-
import type { ElementID } from '@elementor/editor-elements';
|
|
35
|
-
|
|
36
33
|
type InlineEditorProps = {
|
|
37
34
|
value: string | null;
|
|
38
35
|
setValue: ( value: string | null ) => void;
|
|
39
|
-
|
|
36
|
+
editorProps?: EditorProps;
|
|
40
37
|
elementClasses?: string;
|
|
41
38
|
sx?: SxProps< Theme >;
|
|
42
|
-
onBlur?: (
|
|
43
|
-
showToolbar?: boolean;
|
|
39
|
+
onBlur?: () => void;
|
|
44
40
|
autofocus?: boolean;
|
|
45
|
-
getInitialPopoverPosition?: () => { left: number; top: number };
|
|
46
41
|
expectedTag?: string | null;
|
|
47
|
-
|
|
42
|
+
onEditorCreate?: Dispatch< SetStateAction< Editor | null > >;
|
|
43
|
+
wrapperClassName?: string;
|
|
44
|
+
onSelectionEnd?: ( view: EditorView ) => void;
|
|
48
45
|
};
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
type WrapperProps = PropsWithChildren< {
|
|
48
|
+
containerRef: RefObject< HTMLDivElement >;
|
|
49
|
+
editor: ReturnType< typeof useEditor >;
|
|
50
|
+
sx: SxProps< Theme >;
|
|
51
|
+
onBlur?: () => void;
|
|
52
|
+
className?: string;
|
|
53
|
+
} >;
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
export const InlineEditor = React.forwardRef( ( props: InlineEditorProps, ref ) => {
|
|
56
|
+
const {
|
|
57
|
+
value,
|
|
58
|
+
setValue,
|
|
59
|
+
editorProps = {},
|
|
60
|
+
elementClasses = '',
|
|
61
|
+
autofocus = false,
|
|
62
|
+
sx = {},
|
|
63
|
+
onBlur = undefined,
|
|
64
|
+
expectedTag = null,
|
|
65
|
+
onEditorCreate,
|
|
66
|
+
wrapperClassName,
|
|
67
|
+
onSelectionEnd,
|
|
68
|
+
} = props;
|
|
69
|
+
|
|
70
|
+
const containerRef = useRef< HTMLDivElement >( null );
|
|
71
|
+
const documentContentSettings = !! expectedTag ? 'block+' : 'inline*';
|
|
72
|
+
|
|
73
|
+
const onUpdate = ( { editor: updatedEditor }: { editor: Editor } ) => {
|
|
74
|
+
const newValue: string | null = updatedEditor.getHTML();
|
|
75
|
+
|
|
76
|
+
setValue( isEmpty( newValue ) ? null : newValue );
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const onKeyDown = ( _: Editor[ 'view' ], event: KeyboardEvent ) => {
|
|
80
|
+
if ( event.key === 'Escape' ) {
|
|
81
|
+
onBlur?.();
|
|
60
82
|
}
|
|
61
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
62
|
-
}, dependencies );
|
|
63
|
-
};
|
|
64
83
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
): { left: number; top: number } | null => {
|
|
69
|
-
if ( ! container ) {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
84
|
+
if ( ( ! event.metaKey && ! event.ctrlKey ) || event.altKey ) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
72
87
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
88
|
+
if ( [ ITALIC_KEYBOARD_SHORTCUT, BOLD_KEYBOARD_SHORTCUT, UNDERLINE_KEYBOARD_SHORTCUT ].includes( event.key ) ) {
|
|
89
|
+
event.stopPropagation();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const editedElementAttributes = ( HTMLAttributes: Record< string, unknown > ) => ( {
|
|
94
|
+
...HTMLAttributes,
|
|
95
|
+
class: elementClasses,
|
|
96
|
+
} );
|
|
97
|
+
|
|
98
|
+
const editor = useEditor( {
|
|
99
|
+
extensions: [
|
|
100
|
+
Document.extend( {
|
|
101
|
+
content: documentContentSettings,
|
|
102
|
+
} ),
|
|
103
|
+
Paragraph.extend( {
|
|
104
|
+
renderHTML( { HTMLAttributes } ) {
|
|
105
|
+
const tag = expectedTag ?? 'p';
|
|
106
|
+
return [ tag, editedElementAttributes( HTMLAttributes ), 0 ];
|
|
107
|
+
},
|
|
108
|
+
} ),
|
|
109
|
+
Heading.extend( {
|
|
110
|
+
renderHTML( { node, HTMLAttributes } ) {
|
|
111
|
+
if ( expectedTag ) {
|
|
112
|
+
return [ expectedTag, editedElementAttributes( HTMLAttributes ), 0 ];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const level = this.options.levels.includes( node.attrs.level )
|
|
116
|
+
? node.attrs.level
|
|
117
|
+
: this.options.levels[ 0 ];
|
|
118
|
+
|
|
119
|
+
return [ `h${ level }`, editedElementAttributes( HTMLAttributes ), 0 ];
|
|
120
|
+
},
|
|
121
|
+
} ).configure( {
|
|
122
|
+
levels: [ 1, 2, 3, 4, 5, 6 ],
|
|
123
|
+
} ),
|
|
124
|
+
Link.configure( {
|
|
125
|
+
openOnClick: false,
|
|
126
|
+
} ),
|
|
127
|
+
Text,
|
|
128
|
+
Bold,
|
|
129
|
+
Italic,
|
|
130
|
+
Strike,
|
|
131
|
+
Superscript,
|
|
132
|
+
Subscript,
|
|
133
|
+
Underline,
|
|
134
|
+
HardBreak.extend( {
|
|
135
|
+
addKeyboardShortcuts() {
|
|
136
|
+
return {
|
|
137
|
+
Enter: () => this.editor.commands.setHardBreak(),
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
} ),
|
|
141
|
+
],
|
|
142
|
+
content: value,
|
|
143
|
+
onUpdate,
|
|
144
|
+
autofocus,
|
|
145
|
+
editorProps: {
|
|
146
|
+
...editorProps,
|
|
147
|
+
handleDOMEvents: {
|
|
148
|
+
keydown: onKeyDown,
|
|
149
|
+
},
|
|
150
|
+
attributes: {
|
|
151
|
+
...( editorProps.attributes ?? {} ),
|
|
152
|
+
role: 'textbox',
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
onCreate: onEditorCreate ? ( { editor: mountedEditor } ) => onEditorCreate( mountedEditor ) : undefined,
|
|
156
|
+
onSelectionUpdate: onSelectionEnd
|
|
157
|
+
? ( { editor: updatedEditor } ) => onSelectionEnd( updatedEditor.view )
|
|
158
|
+
: undefined,
|
|
159
|
+
} );
|
|
160
|
+
|
|
161
|
+
useOnUpdate( () => {
|
|
162
|
+
if ( ! editor ) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
76
165
|
|
|
77
|
-
|
|
78
|
-
const top = Math.min( start.top, end.top ) - container.top;
|
|
166
|
+
const currentContent = editor.getHTML();
|
|
79
167
|
|
|
80
|
-
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
168
|
+
if ( currentContent !== value ) {
|
|
169
|
+
editor.commands.setContent( value, { emitUpdate: false } );
|
|
170
|
+
}
|
|
171
|
+
}, [ editor, value ] );
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<>
|
|
175
|
+
<Wrapper
|
|
176
|
+
containerRef={ containerRef }
|
|
177
|
+
editor={ editor }
|
|
178
|
+
sx={ sx }
|
|
179
|
+
onBlur={ onBlur }
|
|
180
|
+
className={ wrapperClassName }
|
|
181
|
+
>
|
|
182
|
+
<EditorContent ref={ ref } editor={ editor } />
|
|
183
|
+
</Wrapper>
|
|
184
|
+
</>
|
|
185
|
+
);
|
|
186
|
+
} );
|
|
89
187
|
|
|
90
|
-
const Wrapper = ( { children, containerRef, editor, sx, onBlur }: WrapperProps ) => {
|
|
188
|
+
const Wrapper = ( { children, containerRef, editor, sx, onBlur, className }: WrapperProps ) => {
|
|
91
189
|
const wrappedChildren = (
|
|
92
|
-
<Box ref={ containerRef } { ...sx }>
|
|
190
|
+
<Box ref={ containerRef } { ...sx } className={ className }>
|
|
93
191
|
{ children }
|
|
94
192
|
</Box>
|
|
95
193
|
);
|
|
@@ -104,7 +202,7 @@ const Wrapper = ( { children, containerRef, editor, sx, onBlur }: WrapperProps )
|
|
|
104
202
|
return;
|
|
105
203
|
}
|
|
106
204
|
|
|
107
|
-
onBlur?.(
|
|
205
|
+
onBlur?.();
|
|
108
206
|
} }
|
|
109
207
|
>
|
|
110
208
|
{ wrappedChildren }
|
|
@@ -114,187 +212,15 @@ const Wrapper = ( { children, containerRef, editor, sx, onBlur }: WrapperProps )
|
|
|
114
212
|
);
|
|
115
213
|
};
|
|
116
214
|
|
|
117
|
-
|
|
118
|
-
(
|
|
119
|
-
{
|
|
120
|
-
value,
|
|
121
|
-
setValue,
|
|
122
|
-
attributes = {},
|
|
123
|
-
elementClasses = '',
|
|
124
|
-
showToolbar = false,
|
|
125
|
-
autofocus = false,
|
|
126
|
-
sx = {},
|
|
127
|
-
onBlur = undefined,
|
|
128
|
-
getInitialPopoverPosition = undefined,
|
|
129
|
-
expectedTag = null,
|
|
130
|
-
elementId = undefined,
|
|
131
|
-
}: InlineEditorProps,
|
|
132
|
-
ref
|
|
133
|
-
) => {
|
|
134
|
-
const containerRef = useRef< HTMLDivElement >( null );
|
|
135
|
-
const popupState = usePopupState( { variant: 'popover', disableAutoFocus: true } );
|
|
136
|
-
const [ hasSelectedContent, setHasSelectedContent ] = useState( false );
|
|
137
|
-
const documentContentSettings = !! expectedTag ? 'block+' : 'inline*';
|
|
138
|
-
const [ selectionRect, setSelectionRect ] = useState< { left: number; top: number } | null >( null );
|
|
139
|
-
|
|
140
|
-
const onSelectionEnd = ( view: EditorView ) => {
|
|
141
|
-
const hasSelection = ! view.state.selection.empty;
|
|
142
|
-
setHasSelectedContent( hasSelection );
|
|
143
|
-
|
|
144
|
-
if ( hasSelection ) {
|
|
145
|
-
const container = containerRef.current?.getBoundingClientRect();
|
|
146
|
-
setSelectionRect( calcSelectionCenter( view, container ) );
|
|
147
|
-
} else {
|
|
148
|
-
setSelectionRect( null );
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
queueMicrotask( () => view.focus() );
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const onKeyDown = ( _: EditorView, event: KeyboardEvent ) => {
|
|
155
|
-
if ( event.key === 'Escape' ) {
|
|
156
|
-
onBlur?.( event );
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if ( ( ! event.metaKey && ! event.ctrlKey ) || event.altKey ) {
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (
|
|
164
|
-
[ ITALIC_KEYBOARD_SHORTCUT, BOLD_KEYBOARD_SHORTCUT, UNDERLINE_KEYBOARD_SHORTCUT ].includes( event.key )
|
|
165
|
-
) {
|
|
166
|
-
event.stopPropagation();
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const toolbarRelatedListeners = showToolbar
|
|
171
|
-
? {
|
|
172
|
-
mouseup: onSelectionEnd,
|
|
173
|
-
keyup: onSelectionEnd,
|
|
174
|
-
keydown: onKeyDown,
|
|
175
|
-
}
|
|
176
|
-
: undefined;
|
|
177
|
-
|
|
178
|
-
const onUpdate = ( { editor: updatedEditor }: { editor: Editor } ) => {
|
|
179
|
-
const newValue: string | null = updatedEditor.getHTML();
|
|
180
|
-
|
|
181
|
-
setValue( isEmpty( newValue ) ? null : newValue );
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
const classes = `${ INLINE_EDITOR_RESET_CLASS } ${ elementClasses }`;
|
|
185
|
-
|
|
186
|
-
const editor = useEditor( {
|
|
187
|
-
extensions: [
|
|
188
|
-
Document.extend( {
|
|
189
|
-
content: documentContentSettings,
|
|
190
|
-
} ),
|
|
191
|
-
Paragraph.extend( {
|
|
192
|
-
renderHTML( { HTMLAttributes } ) {
|
|
193
|
-
const tag = expectedTag ?? 'p';
|
|
194
|
-
return [ tag, { ...HTMLAttributes, class: classes }, 0 ];
|
|
195
|
-
},
|
|
196
|
-
} ),
|
|
197
|
-
Heading.extend( {
|
|
198
|
-
renderHTML( { node, HTMLAttributes } ) {
|
|
199
|
-
if ( expectedTag ) {
|
|
200
|
-
return [ expectedTag, { ...HTMLAttributes, class: classes }, 0 ];
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const level = this.options.levels.includes( node.attrs.level )
|
|
204
|
-
? node.attrs.level
|
|
205
|
-
: this.options.levels[ 0 ];
|
|
206
|
-
|
|
207
|
-
return [ `h${ level }`, { ...HTMLAttributes, class: classes }, 0 ];
|
|
208
|
-
},
|
|
209
|
-
} ).configure( {
|
|
210
|
-
levels: [ 1, 2, 3, 4, 5, 6 ],
|
|
211
|
-
} ),
|
|
212
|
-
Link.configure( {
|
|
213
|
-
openOnClick: false,
|
|
214
|
-
} ),
|
|
215
|
-
Text,
|
|
216
|
-
Bold,
|
|
217
|
-
Italic,
|
|
218
|
-
Strike,
|
|
219
|
-
Superscript,
|
|
220
|
-
Subscript,
|
|
221
|
-
Underline,
|
|
222
|
-
HardBreak.extend( {
|
|
223
|
-
addKeyboardShortcuts() {
|
|
224
|
-
return {
|
|
225
|
-
Enter: () => this.editor.commands.setHardBreak(),
|
|
226
|
-
};
|
|
227
|
-
},
|
|
228
|
-
} ),
|
|
229
|
-
],
|
|
230
|
-
content: value,
|
|
231
|
-
onUpdate,
|
|
232
|
-
autofocus,
|
|
233
|
-
editorProps: {
|
|
234
|
-
attributes: {
|
|
235
|
-
...attributes,
|
|
236
|
-
class: attributes.class ?? '',
|
|
237
|
-
role: 'textbox',
|
|
238
|
-
},
|
|
239
|
-
handleDOMEvents: toolbarRelatedListeners,
|
|
240
|
-
},
|
|
241
|
-
} );
|
|
242
|
-
|
|
243
|
-
useOnUpdate( () => {
|
|
244
|
-
if ( ! editor ) {
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const currentContent = editor.getHTML();
|
|
249
|
-
|
|
250
|
-
if ( currentContent !== value ) {
|
|
251
|
-
editor.commands.setContent( value, { emitUpdate: false } );
|
|
252
|
-
}
|
|
253
|
-
}, [ editor, value ] );
|
|
254
|
-
|
|
255
|
-
const computePopupPosition = () => {
|
|
256
|
-
if ( ! selectionRect ) {
|
|
257
|
-
return { left: 0, top: 0 };
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const container = containerRef.current?.getBoundingClientRect();
|
|
261
|
-
if ( ! container ) {
|
|
262
|
-
return { left: 0, top: 0 };
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const initial = getInitialPopoverPosition?.() ?? { left: 0, top: 0 };
|
|
266
|
-
|
|
267
|
-
return {
|
|
268
|
-
left: container.left + selectionRect.left + initial.left,
|
|
269
|
-
top: container.top + selectionRect.top + initial.top,
|
|
270
|
-
};
|
|
271
|
-
};
|
|
215
|
+
const useOnUpdate = ( callback: () => void, dependencies: DependencyList ): void => {
|
|
216
|
+
const hasMounted = useRef( false );
|
|
272
217
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
sx: {
|
|
283
|
-
pointerEvents: 'none',
|
|
284
|
-
},
|
|
285
|
-
},
|
|
286
|
-
} }
|
|
287
|
-
{ ...bindPopover( popupState ) }
|
|
288
|
-
open={ hasSelectedContent && selectionRect !== null }
|
|
289
|
-
anchorReference="anchorPosition"
|
|
290
|
-
anchorPosition={ computePopupPosition() }
|
|
291
|
-
anchorOrigin={ { vertical: 'top', horizontal: 'center' } }
|
|
292
|
-
transformOrigin={ { vertical: 'bottom', horizontal: 'center' } }
|
|
293
|
-
>
|
|
294
|
-
<InlineEditorToolbar editor={ editor } elementId={ elementId } />
|
|
295
|
-
</Popover>
|
|
296
|
-
) }
|
|
297
|
-
</>
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
);
|
|
218
|
+
useEffect( () => {
|
|
219
|
+
if ( hasMounted.current ) {
|
|
220
|
+
callback();
|
|
221
|
+
} else {
|
|
222
|
+
hasMounted.current = true;
|
|
223
|
+
}
|
|
224
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
225
|
+
}, dependencies );
|
|
226
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -63,6 +63,7 @@ export type { SetValue, SetValueMeta } from './bound-prop-context/prop-context';
|
|
|
63
63
|
export type { ExtendedOption, Unit, LengthUnit, AngleUnit, TimeUnit } from './utils/size-control';
|
|
64
64
|
export type { ToggleControlProps } from './controls/toggle-control';
|
|
65
65
|
export type { FontCategory } from './controls/font-family-control/font-family-control';
|
|
66
|
+
export type { InlineEditorToolbarProps } from './components/inline-editor-toolbar';
|
|
66
67
|
|
|
67
68
|
// providers
|
|
68
69
|
export {
|