@elementor/editor-canvas 3.35.0-493 → 3.35.1
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.js +230 -92
- package/dist/index.mjs +235 -97
- package/package.json +18 -18
- package/src/legacy/create-element-type.ts +7 -1
- package/src/legacy/replacements/inline-editing/canvas-inline-editor.tsx +215 -0
- package/src/legacy/replacements/inline-editing/inline-editing-elements.tsx +17 -83
- package/src/legacy/replacements/inline-editing/inline-editing-utils.ts +65 -12
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { InlineEditor, InlineEditorToolbar } from '@elementor/editor-controls';
|
|
4
|
+
import { Box, ThemeProvider } from '@elementor/ui';
|
|
5
|
+
import { FloatingPortal, useInteractions } from '@floating-ui/react';
|
|
6
|
+
|
|
7
|
+
import { CANVAS_WRAPPER_ID, OutlineOverlay } from '../../../components/outline-overlay';
|
|
8
|
+
import { useBindReactPropsToElement } from '../../../hooks/use-bind-react-props-to-element';
|
|
9
|
+
import { useFloatingOnElement } from '../../../hooks/use-floating-on-element';
|
|
10
|
+
import {
|
|
11
|
+
calcSelectionCenterOffsets,
|
|
12
|
+
type Editor,
|
|
13
|
+
type EditorView,
|
|
14
|
+
getComputedStyle,
|
|
15
|
+
type Offsets,
|
|
16
|
+
} from './inline-editing-utils';
|
|
17
|
+
|
|
18
|
+
const TOP_BAR_SELECTOR = '#elementor-editor-wrapper-v2';
|
|
19
|
+
const NAVIGATOR_SELECTOR = '#elementor-navigator';
|
|
20
|
+
const EDITING_PANEL = '#elementor-panel';
|
|
21
|
+
|
|
22
|
+
const EDITOR_ELEMENTS_OUT_OF_IFRAME = [ TOP_BAR_SELECTOR, NAVIGATOR_SELECTOR, EDITING_PANEL ];
|
|
23
|
+
|
|
24
|
+
const EDITOR_WRAPPER_SELECTOR = 'inline-editor-wrapper';
|
|
25
|
+
|
|
26
|
+
export const CanvasInlineEditor = ( {
|
|
27
|
+
elementClasses,
|
|
28
|
+
initialValue,
|
|
29
|
+
expectedTag,
|
|
30
|
+
rootElement,
|
|
31
|
+
id,
|
|
32
|
+
setValue,
|
|
33
|
+
onBlur,
|
|
34
|
+
}: {
|
|
35
|
+
elementClasses: string;
|
|
36
|
+
initialValue: string | null;
|
|
37
|
+
expectedTag: string | null;
|
|
38
|
+
rootElement: HTMLElement;
|
|
39
|
+
id: string;
|
|
40
|
+
setValue: ( value: string | null ) => void;
|
|
41
|
+
onBlur: () => void;
|
|
42
|
+
} ) => {
|
|
43
|
+
const [ selectionOffsets, setSelectionOffsets ] = useState< Offsets | null >( null );
|
|
44
|
+
const [ editor, setEditor ] = useState< Editor | null >( null );
|
|
45
|
+
|
|
46
|
+
const onSelectionEnd = ( view: EditorView ) => {
|
|
47
|
+
const hasSelection = ! view.state.selection.empty;
|
|
48
|
+
|
|
49
|
+
setSelectionOffsets( hasSelection ? calcSelectionCenterOffsets( view ) : null );
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
useOnClickOutsideIframe( onBlur );
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<ThemeProvider>
|
|
56
|
+
<InlineEditingOverlay expectedTag={ expectedTag } rootElement={ rootElement } id={ id } />
|
|
57
|
+
<style>
|
|
58
|
+
{ `
|
|
59
|
+
.${ EDITOR_WRAPPER_SELECTOR }, .${ EDITOR_WRAPPER_SELECTOR } > * {
|
|
60
|
+
height: 100%;
|
|
61
|
+
}
|
|
62
|
+
.ProseMirror > * {
|
|
63
|
+
height: 100%;
|
|
64
|
+
}
|
|
65
|
+
` }
|
|
66
|
+
</style>
|
|
67
|
+
<InlineEditor
|
|
68
|
+
onEditorCreate={ setEditor }
|
|
69
|
+
editorProps={ {
|
|
70
|
+
attributes: {
|
|
71
|
+
style: 'outline: none;overflow-wrap: normal;height:100%',
|
|
72
|
+
},
|
|
73
|
+
} }
|
|
74
|
+
elementClasses={ elementClasses }
|
|
75
|
+
value={ initialValue }
|
|
76
|
+
setValue={ setValue }
|
|
77
|
+
onBlur={ onBlur }
|
|
78
|
+
autofocus
|
|
79
|
+
expectedTag={ expectedTag }
|
|
80
|
+
wrapperClassName={ EDITOR_WRAPPER_SELECTOR }
|
|
81
|
+
onSelectionEnd={ onSelectionEnd }
|
|
82
|
+
/>
|
|
83
|
+
{ selectionOffsets && editor && (
|
|
84
|
+
<InlineEditingToolbarWrapper
|
|
85
|
+
expectedTag={ expectedTag }
|
|
86
|
+
editor={ editor }
|
|
87
|
+
rootElement={ rootElement }
|
|
88
|
+
id={ id }
|
|
89
|
+
selectionOffsets={ selectionOffsets }
|
|
90
|
+
/>
|
|
91
|
+
) }
|
|
92
|
+
</ThemeProvider>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const InlineEditingOverlay = ( {
|
|
97
|
+
expectedTag,
|
|
98
|
+
rootElement,
|
|
99
|
+
id,
|
|
100
|
+
}: {
|
|
101
|
+
expectedTag: string | null;
|
|
102
|
+
rootElement: HTMLElement;
|
|
103
|
+
id: string;
|
|
104
|
+
} ) => {
|
|
105
|
+
const inlineEditedElement = getInlineEditorElement( rootElement, expectedTag );
|
|
106
|
+
const [ overlayRefElement, setOverlayElement ] = useState< HTMLDivElement | null >( inlineEditedElement );
|
|
107
|
+
|
|
108
|
+
useEffect( () => {
|
|
109
|
+
setOverlayElement( getInlineEditorElement( rootElement, expectedTag ) );
|
|
110
|
+
}, [ expectedTag, rootElement ] );
|
|
111
|
+
|
|
112
|
+
return overlayRefElement ? <OutlineOverlay element={ overlayRefElement } id={ id } isSelected /> : null;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const InlineEditingToolbarWrapper = ( {
|
|
116
|
+
expectedTag,
|
|
117
|
+
editor,
|
|
118
|
+
rootElement,
|
|
119
|
+
id,
|
|
120
|
+
selectionOffsets,
|
|
121
|
+
}: {
|
|
122
|
+
expectedTag: string | null;
|
|
123
|
+
editor: Editor;
|
|
124
|
+
rootElement: HTMLElement;
|
|
125
|
+
id: string;
|
|
126
|
+
selectionOffsets: Offsets;
|
|
127
|
+
} ) => {
|
|
128
|
+
const [ element, setElement ] = useState< HTMLElement | null >( null );
|
|
129
|
+
|
|
130
|
+
useEffect( () => {
|
|
131
|
+
setElement( getInlineEditorElement( rootElement, expectedTag ) );
|
|
132
|
+
}, [ expectedTag, rootElement ] );
|
|
133
|
+
|
|
134
|
+
return element ? (
|
|
135
|
+
<InlineEditingToolbar element={ element } editor={ editor } id={ id } selectionOffsets={ selectionOffsets } />
|
|
136
|
+
) : null;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const InlineEditingToolbar = ( {
|
|
140
|
+
element,
|
|
141
|
+
editor,
|
|
142
|
+
id,
|
|
143
|
+
selectionOffsets,
|
|
144
|
+
}: {
|
|
145
|
+
element: HTMLElement;
|
|
146
|
+
editor: Editor;
|
|
147
|
+
id: string;
|
|
148
|
+
selectionOffsets: Offsets;
|
|
149
|
+
} ) => {
|
|
150
|
+
const { floating } = useFloatingOnElement( {
|
|
151
|
+
element,
|
|
152
|
+
isSelected: true,
|
|
153
|
+
} );
|
|
154
|
+
const { getFloatingProps, getReferenceProps } = useInteractions();
|
|
155
|
+
const style = getComputedStyle( floating.styles, selectionOffsets );
|
|
156
|
+
|
|
157
|
+
useBindReactPropsToElement( element, getReferenceProps );
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<FloatingPortal id={ CANVAS_WRAPPER_ID }>
|
|
161
|
+
<Box
|
|
162
|
+
ref={ floating.setRef }
|
|
163
|
+
style={ {
|
|
164
|
+
...floating.styles,
|
|
165
|
+
pointerEvents: 'none',
|
|
166
|
+
} }
|
|
167
|
+
role="presentation"
|
|
168
|
+
{ ...getFloatingProps( { style } ) }
|
|
169
|
+
>
|
|
170
|
+
{ floating.styles.transform && (
|
|
171
|
+
<Box
|
|
172
|
+
sx={ {
|
|
173
|
+
position: 'relative',
|
|
174
|
+
transform: 'translateY(-100%)',
|
|
175
|
+
height: 'max-content',
|
|
176
|
+
} }
|
|
177
|
+
>
|
|
178
|
+
<InlineEditorToolbar
|
|
179
|
+
editor={ editor }
|
|
180
|
+
elementId={ id }
|
|
181
|
+
sx={ {
|
|
182
|
+
transform: 'translateX(-50%)',
|
|
183
|
+
} }
|
|
184
|
+
/>
|
|
185
|
+
</Box>
|
|
186
|
+
) }
|
|
187
|
+
</Box>
|
|
188
|
+
</FloatingPortal>
|
|
189
|
+
);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const getInlineEditorElement = ( elementWrapper: HTMLElement, expectedTag: string | null ) => {
|
|
193
|
+
return ! expectedTag ? null : ( elementWrapper.querySelector( expectedTag ) as HTMLDivElement );
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// Elements out of iframe and canvas don't trigger "onClickAway" which unmounts the editor
|
|
197
|
+
// since they are not part of the iframes owner document.
|
|
198
|
+
// We need to manually add listeners to these elements to unmount the editor when they are clicked.
|
|
199
|
+
const useOnClickOutsideIframe = ( handleUnmount: () => void ) => {
|
|
200
|
+
const asyncUnmountInlineEditor = React.useCallback( () => queueMicrotask( handleUnmount ), [ handleUnmount ] );
|
|
201
|
+
|
|
202
|
+
useEffect( () => {
|
|
203
|
+
EDITOR_ELEMENTS_OUT_OF_IFRAME.forEach(
|
|
204
|
+
( selector ) =>
|
|
205
|
+
document?.querySelector( selector )?.addEventListener( 'mousedown', asyncUnmountInlineEditor )
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
return () =>
|
|
209
|
+
EDITOR_ELEMENTS_OUT_OF_IFRAME.forEach(
|
|
210
|
+
( selector ) =>
|
|
211
|
+
document?.querySelector( selector )?.removeEventListener( 'mousedown', asyncUnmountInlineEditor )
|
|
212
|
+
);
|
|
213
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
214
|
+
}, [] );
|
|
215
|
+
};
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useEffect, useRef, useState } from 'react';
|
|
3
2
|
import { createRoot, type Root } from 'react-dom/client';
|
|
4
|
-
import { InlineEditor } from '@elementor/editor-controls';
|
|
5
3
|
import { getContainer, getElementLabel, getElementType } from '@elementor/editor-elements';
|
|
6
4
|
import { htmlPropTypeUtil, type PropType, type PropValue, stringPropTypeUtil } from '@elementor/editor-props';
|
|
7
|
-
import { __privateRunCommandSync as runCommandSync,
|
|
8
|
-
import { Box, ThemeProvider } from '@elementor/ui';
|
|
5
|
+
import { __privateRunCommandSync as runCommandSync, getCurrentEditMode, undoable } from '@elementor/editor-v1-adapters';
|
|
9
6
|
import { __ } from '@wordpress/i18n';
|
|
10
7
|
|
|
11
|
-
import { OutlineOverlay } from '../../../components/outline-overlay';
|
|
12
8
|
import { ReplacementBase, TRIGGER_TIMING } from '../base';
|
|
9
|
+
import { CanvasInlineEditor } from './canvas-inline-editor';
|
|
13
10
|
import { isInlineEditingAllowed } from './inline-editing-eligibility';
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
const EXPERIMENT_KEY = 'v4-inline-text-editing';
|
|
11
|
+
import { INLINE_EDITING_PROPERTY_PER_TYPE } from './inline-editing-utils';
|
|
17
12
|
|
|
18
13
|
type TagPropType = PropType< 'tag' > & {
|
|
19
14
|
settings?: {
|
|
@@ -23,13 +18,6 @@ type TagPropType = PropType< 'tag' > & {
|
|
|
23
18
|
|
|
24
19
|
const HISTORY_DEBOUNCE_WAIT = 800;
|
|
25
20
|
|
|
26
|
-
const TOP_BAR_SELECTOR = '#elementor-editor-wrapper-v2';
|
|
27
|
-
const NAVIGATOR_SELECTOR = '#elementor-navigator';
|
|
28
|
-
const V4_EDITING_PANEL = 'main.MuiBox-root';
|
|
29
|
-
const V3_EDITING_PANEL = '#elementor-panel-content-wrapper';
|
|
30
|
-
|
|
31
|
-
const BLUR_TRIGGERING_SELECTORS = [ TOP_BAR_SELECTOR, NAVIGATOR_SELECTOR, V4_EDITING_PANEL, V3_EDITING_PANEL ];
|
|
32
|
-
|
|
33
21
|
export default class InlineEditingReplacement extends ReplacementBase {
|
|
34
22
|
private inlineEditorRoot: Root | null = null;
|
|
35
23
|
private handlerAttached = false;
|
|
@@ -47,7 +35,7 @@ export default class InlineEditingReplacement extends ReplacementBase {
|
|
|
47
35
|
}
|
|
48
36
|
|
|
49
37
|
shouldRenderReplacement() {
|
|
50
|
-
return
|
|
38
|
+
return this.isInlineEditingEligible() && getCurrentEditMode() === 'edit';
|
|
51
39
|
}
|
|
52
40
|
|
|
53
41
|
handleRenderInlineEditor = () => {
|
|
@@ -134,12 +122,12 @@ export default class InlineEditingReplacement extends ReplacementBase {
|
|
|
134
122
|
getExtractedContentValue() {
|
|
135
123
|
const propValue = this.getInlineEditablePropValue();
|
|
136
124
|
|
|
137
|
-
return htmlPropTypeUtil.extract( propValue ) ??
|
|
125
|
+
return htmlPropTypeUtil.extract( propValue ) ?? '';
|
|
138
126
|
}
|
|
139
127
|
|
|
140
128
|
setContentValue( value: string | null ) {
|
|
141
129
|
const settingKey = this.getInlineEditablePropertyName();
|
|
142
|
-
const valueToSave =
|
|
130
|
+
const valueToSave = htmlPropTypeUtil.create( value || '' );
|
|
143
131
|
|
|
144
132
|
undoable(
|
|
145
133
|
{
|
|
@@ -238,77 +226,23 @@ export default class InlineEditingReplacement extends ReplacementBase {
|
|
|
238
226
|
this.resetInlineEditorRoot();
|
|
239
227
|
}
|
|
240
228
|
|
|
241
|
-
const InlineEditorApp = this.InlineEditorApp;
|
|
242
|
-
const wrapperClasses = 'elementor';
|
|
243
229
|
const elementClasses = this.element.children?.[ 0 ]?.classList.toString() ?? '';
|
|
230
|
+
const propValue = this.getExtractedContentValue();
|
|
231
|
+
const expectedTag = this.getExpectedTag();
|
|
244
232
|
|
|
245
233
|
this.element.innerHTML = '';
|
|
246
234
|
|
|
247
235
|
this.inlineEditorRoot = createRoot( this.element );
|
|
248
236
|
this.inlineEditorRoot.render(
|
|
249
|
-
<
|
|
237
|
+
<CanvasInlineEditor
|
|
238
|
+
elementClasses={ elementClasses }
|
|
239
|
+
initialValue={ propValue }
|
|
240
|
+
expectedTag={ expectedTag }
|
|
241
|
+
rootElement={ this.element }
|
|
242
|
+
id={ this.id }
|
|
243
|
+
setValue={ this.setContentValue.bind( this ) }
|
|
244
|
+
onBlur={ this.unmountInlineEditor.bind( this ) }
|
|
245
|
+
/>
|
|
250
246
|
);
|
|
251
247
|
}
|
|
252
|
-
|
|
253
|
-
InlineEditorApp = ( { wrapperClasses, elementClasses }: { wrapperClasses: string; elementClasses: string } ) => {
|
|
254
|
-
const propValue = this.getExtractedContentValue();
|
|
255
|
-
const expectedTag = this.getExpectedTag();
|
|
256
|
-
const wrapperRef = useRef< HTMLDivElement | null >( null );
|
|
257
|
-
const [ isWrapperRendered, setIsWrapperRendered ] = useState( false );
|
|
258
|
-
|
|
259
|
-
useEffect( () => {
|
|
260
|
-
setIsWrapperRendered( !! wrapperRef.current );
|
|
261
|
-
BLUR_TRIGGERING_SELECTORS.forEach(
|
|
262
|
-
( selector ) =>
|
|
263
|
-
document?.querySelector( selector )?.addEventListener( 'mousedown', asyncUnmountInlineEditor )
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
return () =>
|
|
267
|
-
BLUR_TRIGGERING_SELECTORS.forEach(
|
|
268
|
-
( selector ) =>
|
|
269
|
-
document
|
|
270
|
-
?.querySelector( selector )
|
|
271
|
-
?.removeEventListener( 'mousedown', asyncUnmountInlineEditor )
|
|
272
|
-
);
|
|
273
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
274
|
-
}, [] );
|
|
275
|
-
|
|
276
|
-
const asyncUnmountInlineEditor = React.useCallback(
|
|
277
|
-
() => queueMicrotask( this.unmountInlineEditor.bind( this ) ),
|
|
278
|
-
[]
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
return (
|
|
282
|
-
<ThemeProvider>
|
|
283
|
-
<Box
|
|
284
|
-
ref={ wrapperRef }
|
|
285
|
-
sx={ {
|
|
286
|
-
'& .elementor-inline-editor-reset': {
|
|
287
|
-
margin: 0,
|
|
288
|
-
padding: 0,
|
|
289
|
-
},
|
|
290
|
-
} }
|
|
291
|
-
>
|
|
292
|
-
{ isWrapperRendered && (
|
|
293
|
-
<OutlineOverlay element={ wrapperRef.current as HTMLDivElement } id={ this.id } isSelected />
|
|
294
|
-
) }
|
|
295
|
-
<InlineEditor
|
|
296
|
-
attributes={ {
|
|
297
|
-
class: wrapperClasses,
|
|
298
|
-
style: 'outline: none;',
|
|
299
|
-
} }
|
|
300
|
-
elementClasses={ elementClasses }
|
|
301
|
-
value={ propValue }
|
|
302
|
-
setValue={ this.setContentValue.bind( this ) }
|
|
303
|
-
onBlur={ this.unmountInlineEditor.bind( this ) }
|
|
304
|
-
autofocus
|
|
305
|
-
showToolbar
|
|
306
|
-
getInitialPopoverPosition={ getInitialPopoverPosition }
|
|
307
|
-
expectedTag={ expectedTag }
|
|
308
|
-
elementId={ this.id }
|
|
309
|
-
/>
|
|
310
|
-
</Box>
|
|
311
|
-
</ThemeProvider>
|
|
312
|
-
);
|
|
313
|
-
};
|
|
314
248
|
}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
import { type CSSProperties } from 'react';
|
|
2
|
+
import { type InlineEditorToolbarProps } from '@elementor/editor-controls';
|
|
1
3
|
import { type V1Element } from '@elementor/editor-elements';
|
|
2
4
|
|
|
3
5
|
import { type LegacyWindow } from '../../types';
|
|
4
6
|
|
|
7
|
+
export type Editor = InlineEditorToolbarProps[ 'editor' ];
|
|
8
|
+
export type EditorView = Editor[ 'view' ];
|
|
9
|
+
|
|
10
|
+
export type Offsets = {
|
|
11
|
+
left: number;
|
|
12
|
+
top: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
5
15
|
export const INLINE_EDITING_PROPERTY_PER_TYPE: Record< string, string > = {
|
|
6
16
|
'e-form-label': 'text',
|
|
7
17
|
'e-heading': 'title',
|
|
@@ -14,19 +24,62 @@ export const getWidgetType = ( container: V1Element | null ) => {
|
|
|
14
24
|
return container?.model?.get( 'widgetType' ) ?? container?.model?.get( 'elType' ) ?? null;
|
|
15
25
|
};
|
|
16
26
|
|
|
17
|
-
export const
|
|
18
|
-
const
|
|
27
|
+
export const calcSelectionCenterOffsets = ( view: EditorView ): Offsets | null => {
|
|
28
|
+
const frameWindow = ( view.root as Document )?.defaultView;
|
|
29
|
+
const selection = frameWindow?.getSelection();
|
|
30
|
+
const editorContainer = view.dom;
|
|
31
|
+
|
|
32
|
+
if ( ! selection || ! editorContainer ) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const range = selection.getRangeAt( 0 );
|
|
37
|
+
const selectionRect = range.getBoundingClientRect();
|
|
38
|
+
const editorContainerRect = editorContainer.getBoundingClientRect();
|
|
39
|
+
|
|
40
|
+
if ( ! selectionRect || ! editorContainerRect ) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const verticalOffset = selectionRect.top - editorContainerRect.top;
|
|
45
|
+
|
|
46
|
+
const selectionCenter = selectionRect?.left + selectionRect?.width / 2;
|
|
47
|
+
const horizontalOffset = selectionCenter - editorContainerRect.left;
|
|
48
|
+
|
|
49
|
+
return { left: horizontalOffset, top: verticalOffset };
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const getComputedStyle = ( styles: CSSProperties, offsets: Offsets ): CSSProperties => {
|
|
53
|
+
const transform = extractTransformValue( styles );
|
|
54
|
+
|
|
55
|
+
return transform
|
|
56
|
+
? {
|
|
57
|
+
...styles,
|
|
58
|
+
marginLeft: `${ offsets.left }px`,
|
|
59
|
+
marginTop: `${ offsets.top }px`,
|
|
60
|
+
pointerEvents: 'none',
|
|
61
|
+
}
|
|
62
|
+
: {
|
|
63
|
+
display: 'none',
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const extractTransformValue = ( styles: CSSProperties ) => {
|
|
68
|
+
const translateRegex = /translate\([^)]*\)\s?/g;
|
|
69
|
+
const numericValuesRegex = /(-?\d+\.?\d*)/g;
|
|
70
|
+
|
|
71
|
+
const translateValue = styles?.transform?.match( translateRegex )?.[ 0 ];
|
|
72
|
+
const values = translateValue?.match( numericValuesRegex );
|
|
73
|
+
|
|
74
|
+
if ( ! translateValue || ! values ) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
19
77
|
|
|
20
|
-
const
|
|
21
|
-
const iFramePosition = iFrameElement?.getBoundingClientRect() ?? positionFallback;
|
|
78
|
+
const [ numericX, numericY ] = values.map( Number );
|
|
22
79
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
: positionFallback;
|
|
80
|
+
if ( ! numericX || ! numericY ) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
27
83
|
|
|
28
|
-
return
|
|
29
|
-
left: iFramePosition.left + previewPosition.left,
|
|
30
|
-
top: iFramePosition.top + previewPosition.top,
|
|
31
|
-
};
|
|
84
|
+
return styles.transform;
|
|
32
85
|
};
|