@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/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-512",
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-512",
44
- "@elementor/editor-elements": "4.0.0-512",
45
- "@elementor/editor-props": "4.0.0-512",
46
- "@elementor/editor-responsive": "4.0.0-512",
47
- "@elementor/editor-ui": "4.0.0-512",
48
- "@elementor/editor-v1-adapters": "4.0.0-512",
49
- "@elementor/env": "4.0.0-512",
50
- "@elementor/http-client": "4.0.0-512",
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-512",
53
- "@elementor/mixpanel": "4.0.0-512",
54
- "@elementor/query": "4.0.0-512",
55
- "@elementor/session": "4.0.0-512",
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-512",
58
- "@elementor/wp-media": "4.0.0-512",
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
- const checkIfElementHasLink = ( elementId: ElementID ): boolean =>
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 { clear: clearButton, ...formatButtons } = toolbarButtons;
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
- forwardRef,
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 { bindPopover, Box, ClickAwayListener, Popover, type SxProps, type Theme, usePopupState } from '@elementor/ui';
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
- attributes?: Record< string, string >;
36
+ editorProps?: EditorProps;
40
37
  elementClasses?: string;
41
38
  sx?: SxProps< Theme >;
42
- onBlur?: ( event: Event ) => void;
43
- showToolbar?: boolean;
39
+ onBlur?: () => void;
44
40
  autofocus?: boolean;
45
- getInitialPopoverPosition?: () => { left: number; top: number };
46
41
  expectedTag?: string | null;
47
- elementId?: ElementID;
42
+ onEditorCreate?: Dispatch< SetStateAction< Editor | null > >;
43
+ wrapperClassName?: string;
44
+ onSelectionEnd?: ( view: EditorView ) => void;
48
45
  };
49
46
 
50
- const INLINE_EDITOR_RESET_CLASS = 'elementor-inline-editor-reset';
51
-
52
- const useOnUpdate = ( callback: () => void, dependencies: DependencyList ): void => {
53
- const hasMounted = useRef( false );
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
- useEffect( () => {
56
- if ( hasMounted.current ) {
57
- callback();
58
- } else {
59
- hasMounted.current = true;
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
- const calcSelectionCenter = (
66
- view: EditorView,
67
- container: { left: number; top: number } | undefined
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
- const { from, to } = view.state.selection;
74
- const start = view.coordsAtPos( from );
75
- const end = view.coordsAtPos( to );
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
- const left = ( start.left + end.left ) / 2 - container.left;
78
- const top = Math.min( start.top, end.top ) - container.top;
166
+ const currentContent = editor.getHTML();
79
167
 
80
- return { left, top };
81
- };
82
-
83
- type WrapperProps = PropsWithChildren< {
84
- containerRef: RefObject< HTMLDivElement >;
85
- editor: ReturnType< typeof useEditor >;
86
- sx: SxProps< Theme >;
87
- onBlur?: ( event: Event ) => void;
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?.( event );
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
- export const InlineEditor = forwardRef(
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
- return (
274
- <>
275
- <Wrapper containerRef={ containerRef } editor={ editor } sx={ sx } onBlur={ onBlur }>
276
- <EditorContent ref={ ref } editor={ editor } />
277
- </Wrapper>
278
- { showToolbar && containerRef.current && (
279
- <Popover
280
- slotProps={ {
281
- root: {
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 {