@elementor/editor-canvas 3.35.0-351 → 3.35.0-352
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 +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +351 -216
- package/dist/index.mjs +321 -184
- package/package.json +18 -17
- package/src/components/__tests__/elements-overlays.test.tsx +0 -26
- package/src/components/elements-overlays.tsx +0 -8
- package/src/legacy/create-inline-editing-element-type.tsx +199 -0
- package/src/legacy/create-templated-element-type.ts +2 -2
- package/src/legacy/init-legacy-views.ts +5 -1
- package/src/legacy/types.ts +16 -7
- package/src/utils/__tests__/inline-editing.test.ts +553 -0
- package/src/utils/inline-editing-utils.ts +101 -6
- package/src/components/__tests__/inline-editor-overlay.test.tsx +0 -245
- package/src/components/inline-editor-overlay.tsx +0 -79
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-canvas",
|
|
3
3
|
"description": "Elementor Editor Canvas",
|
|
4
|
-
"version": "3.35.0-
|
|
4
|
+
"version": "3.35.0-352",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -37,23 +37,24 @@
|
|
|
37
37
|
"react-dom": "^18.3.1"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@elementor/editor": "3.35.0-
|
|
41
|
-
"@elementor/editor-controls": "3.35.0-
|
|
42
|
-
"@elementor/editor-documents": "3.35.0-
|
|
43
|
-
"@elementor/editor-elements": "3.35.0-
|
|
44
|
-
"@elementor/editor-interactions": "3.35.0-
|
|
45
|
-
"@elementor/editor-
|
|
46
|
-
"@elementor/editor-
|
|
47
|
-
"@elementor/editor-
|
|
48
|
-
"@elementor/editor-
|
|
49
|
-
"@elementor/editor-styles
|
|
50
|
-
"@elementor/editor-
|
|
51
|
-
"@elementor/editor-
|
|
52
|
-
"@elementor/
|
|
53
|
-
"@elementor/
|
|
40
|
+
"@elementor/editor": "3.35.0-352",
|
|
41
|
+
"@elementor/editor-controls": "3.35.0-352",
|
|
42
|
+
"@elementor/editor-documents": "3.35.0-352",
|
|
43
|
+
"@elementor/editor-elements": "3.35.0-352",
|
|
44
|
+
"@elementor/editor-interactions": "3.35.0-352",
|
|
45
|
+
"@elementor/editor-mcp": "3.35.0-352",
|
|
46
|
+
"@elementor/editor-notifications": "3.35.0-352",
|
|
47
|
+
"@elementor/editor-props": "3.35.0-352",
|
|
48
|
+
"@elementor/editor-responsive": "3.35.0-352",
|
|
49
|
+
"@elementor/editor-styles": "3.35.0-352",
|
|
50
|
+
"@elementor/editor-styles-repository": "3.35.0-352",
|
|
51
|
+
"@elementor/editor-ui": "3.35.0-352",
|
|
52
|
+
"@elementor/editor-v1-adapters": "3.35.0-352",
|
|
53
|
+
"@elementor/schema": "3.35.0-352",
|
|
54
|
+
"@elementor/twing": "3.35.0-352",
|
|
54
55
|
"@elementor/ui": "1.36.17",
|
|
55
|
-
"@elementor/utils": "3.35.0-
|
|
56
|
-
"@elementor/wp-media": "3.35.0-
|
|
56
|
+
"@elementor/utils": "3.35.0-352",
|
|
57
|
+
"@elementor/wp-media": "3.35.0-352",
|
|
57
58
|
"@floating-ui/react": "^0.27.5",
|
|
58
59
|
"@wordpress/i18n": "^5.13.0"
|
|
59
60
|
},
|
|
@@ -186,30 +186,4 @@ describe( '<ElementsOverlays />', () => {
|
|
|
186
186
|
expect( overlay ).toBeInTheDocument();
|
|
187
187
|
expect( overlay ).toHaveAttribute( 'data-element-overlay', 'atomic1' );
|
|
188
188
|
} );
|
|
189
|
-
|
|
190
|
-
it( 'should render InlineEditorOverlay only for selected elements that support inline editing', async () => {
|
|
191
|
-
// Arrange
|
|
192
|
-
const headingEl = createDOMElement( { tag: 'div', attrs: { 'data-atomic': '', id: '50' } } );
|
|
193
|
-
|
|
194
|
-
jest.mocked( getElements ).mockReturnValue( [
|
|
195
|
-
createMockElement( {
|
|
196
|
-
model: { id: 'heading-element' },
|
|
197
|
-
view: { el: headingEl, getDomElement: () => ( { get: () => headingEl } ) },
|
|
198
|
-
} ),
|
|
199
|
-
] );
|
|
200
|
-
|
|
201
|
-
jest.mocked( useSelectedElement ).mockReturnValue( {
|
|
202
|
-
element: { id: 'heading-element', type: 'widget' },
|
|
203
|
-
elementType: createMockElementType(),
|
|
204
|
-
} );
|
|
205
|
-
|
|
206
|
-
// Act
|
|
207
|
-
renderWithTheme( <ElementsOverlays /> );
|
|
208
|
-
|
|
209
|
-
// Assert
|
|
210
|
-
await waitFor( () => {
|
|
211
|
-
const overlay = screen.getByRole( 'presentation' );
|
|
212
|
-
expect( overlay ).toHaveAttribute( 'data-element-overlay', 'heading-element' );
|
|
213
|
-
} );
|
|
214
|
-
} );
|
|
215
189
|
} );
|
|
@@ -3,14 +3,11 @@ import { getElements, useSelectedElement } from '@elementor/editor-elements';
|
|
|
3
3
|
import {
|
|
4
4
|
__privateUseIsRouteActive as useIsRouteActive,
|
|
5
5
|
__privateUseListenTo as useListenTo,
|
|
6
|
-
isExperimentActive,
|
|
7
6
|
useEditMode,
|
|
8
7
|
windowEvent,
|
|
9
8
|
} from '@elementor/editor-v1-adapters';
|
|
10
9
|
|
|
11
10
|
import type { ElementOverlayConfig } from '../types/element-overlay';
|
|
12
|
-
import { hasInlineEditableProperty } from '../utils/inline-editing-utils';
|
|
13
|
-
import { InlineEditorOverlay } from './inline-editor-overlay';
|
|
14
11
|
import { OutlineOverlay } from './outline-overlay';
|
|
15
12
|
|
|
16
13
|
const ELEMENTS_DATA_ATTR = 'atomic';
|
|
@@ -20,11 +17,6 @@ const overlayRegistry: ElementOverlayConfig[] = [
|
|
|
20
17
|
component: OutlineOverlay,
|
|
21
18
|
shouldRender: () => true,
|
|
22
19
|
},
|
|
23
|
-
{
|
|
24
|
-
component: InlineEditorOverlay,
|
|
25
|
-
shouldRender: ( { id, isSelected } ) =>
|
|
26
|
-
isSelected && hasInlineEditableProperty( id ) && isExperimentActive( 'v4-inline-text-editing' ),
|
|
27
|
-
},
|
|
28
20
|
];
|
|
29
21
|
|
|
30
22
|
export function ElementsOverlays() {
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createRoot, type Root } from 'react-dom/client';
|
|
3
|
+
import { InlineEditor } from '@elementor/editor-controls';
|
|
4
|
+
import { getElementType } from '@elementor/editor-elements';
|
|
5
|
+
import {
|
|
6
|
+
htmlPropTypeUtil,
|
|
7
|
+
stringPropTypeUtil,
|
|
8
|
+
type StringPropValue,
|
|
9
|
+
type TransformablePropValue,
|
|
10
|
+
} from '@elementor/editor-props';
|
|
11
|
+
import { ThemeProvider } from '@elementor/editor-ui';
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
getBlockedValue,
|
|
15
|
+
getHtmlPropType,
|
|
16
|
+
getInitialPopoverPosition,
|
|
17
|
+
getInlineEditablePropertyName,
|
|
18
|
+
getWidgetType,
|
|
19
|
+
legacyWindow,
|
|
20
|
+
} from '../utils/inline-editing-utils';
|
|
21
|
+
import { type CreateTemplatedElementTypeOptions, createTemplatedElementView } from './create-templated-element-type';
|
|
22
|
+
import { type ElementType, type ElementView } from './types';
|
|
23
|
+
|
|
24
|
+
export function createInlineEditingElementType( {
|
|
25
|
+
type,
|
|
26
|
+
renderer,
|
|
27
|
+
element,
|
|
28
|
+
}: CreateTemplatedElementTypeOptions ): typeof ElementType {
|
|
29
|
+
return class extends legacyWindow.elementor.modules.elements.types.Widget {
|
|
30
|
+
getType() {
|
|
31
|
+
return type;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getView() {
|
|
35
|
+
return createInlineEditingElementView( {
|
|
36
|
+
type,
|
|
37
|
+
renderer,
|
|
38
|
+
element,
|
|
39
|
+
} );
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function createInlineEditingElementView( {
|
|
45
|
+
type,
|
|
46
|
+
renderer,
|
|
47
|
+
element,
|
|
48
|
+
}: CreateTemplatedElementTypeOptions ): typeof ElementView {
|
|
49
|
+
const TemplatedView = createTemplatedElementView( { type, renderer, element } );
|
|
50
|
+
|
|
51
|
+
Object.entries( element.twig_templates ).forEach( ( [ key, template ] ) => {
|
|
52
|
+
renderer.register( key, template );
|
|
53
|
+
} );
|
|
54
|
+
|
|
55
|
+
return class extends TemplatedView {
|
|
56
|
+
inlineEditorRoot: Root | null = null;
|
|
57
|
+
handlerAttached = false;
|
|
58
|
+
|
|
59
|
+
render() {
|
|
60
|
+
if ( this.inlineEditorRoot ) {
|
|
61
|
+
this.resetInlineEditorRoot();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if ( ! this.isValueDynamic() && ! this.handlerAttached ) {
|
|
65
|
+
this.$el.on( 'dblclick', '*', this.handleRenderInlineEditor.bind( this ) );
|
|
66
|
+
this.handlerAttached = true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
TemplatedView.prototype.render.apply( this );
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
handleRenderInlineEditor( event: Event ) {
|
|
73
|
+
event.stopPropagation();
|
|
74
|
+
this.$el.off( 'dblclick', '*' );
|
|
75
|
+
this.handlerAttached = false;
|
|
76
|
+
|
|
77
|
+
if ( ! this.isValueDynamic() ) {
|
|
78
|
+
this.renderInlineEditor();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
handleUnmountInlineEditor( event: Event ) {
|
|
83
|
+
event.stopPropagation();
|
|
84
|
+
this.unmountInlineEditor();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
onDestroy( ...args: unknown[] ) {
|
|
88
|
+
this.resetInlineEditorRoot();
|
|
89
|
+
TemplatedView.prototype.onDestroy.apply( this, args );
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
resetInlineEditorRoot() {
|
|
93
|
+
this.$el.off( 'dblclick', '*' );
|
|
94
|
+
this.handlerAttached = false;
|
|
95
|
+
this.inlineEditorRoot?.unmount?.();
|
|
96
|
+
this.inlineEditorRoot = null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
unmountInlineEditor() {
|
|
100
|
+
this.resetInlineEditorRoot();
|
|
101
|
+
this.render();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
isValueDynamic() {
|
|
105
|
+
const settingKey = getInlineEditablePropertyName( this.container );
|
|
106
|
+
const propValue = this.model.get( 'settings' )?.get( settingKey ) as TransformablePropValue< string >;
|
|
107
|
+
|
|
108
|
+
return propValue?.$$type === 'dynamic';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getContentValue() {
|
|
112
|
+
const prop = getHtmlPropType( this.container );
|
|
113
|
+
const defaultValue = ( prop?.default as StringPropValue | null )?.value ?? '';
|
|
114
|
+
const settingKey = getInlineEditablePropertyName( this.container );
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
htmlPropTypeUtil.extract( this.model.get( 'settings' )?.get( settingKey ) ?? null ) ??
|
|
118
|
+
htmlPropTypeUtil.extract( prop?.default ?? null ) ??
|
|
119
|
+
defaultValue ??
|
|
120
|
+
''
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
setContentValue( value: string | null ) {
|
|
125
|
+
const settingKey = getInlineEditablePropertyName( this.container );
|
|
126
|
+
const valueToSave = value ? htmlPropTypeUtil.create( value ) : null;
|
|
127
|
+
|
|
128
|
+
this.model.get( 'settings' )?.set( settingKey, valueToSave );
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getExpectedTag() {
|
|
132
|
+
const widgetType = getWidgetType( this.container );
|
|
133
|
+
|
|
134
|
+
if ( ! widgetType ) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const propsSchema = getElementType( widgetType )?.propsSchema;
|
|
139
|
+
|
|
140
|
+
if ( ! propsSchema?.tag ) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
stringPropTypeUtil.extract( this.model.get( 'settings' ).get( 'tag' ) ?? null ) ??
|
|
146
|
+
stringPropTypeUtil.extract( propsSchema.tag.default ?? null ) ??
|
|
147
|
+
null
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
ensureProperValue() {
|
|
152
|
+
const actualValue = this.getContentValue();
|
|
153
|
+
const tagSettings = this.getExpectedTag();
|
|
154
|
+
const wrappedValue = getBlockedValue( actualValue, tagSettings );
|
|
155
|
+
|
|
156
|
+
if ( actualValue !== wrappedValue ) {
|
|
157
|
+
this.setContentValue( wrappedValue );
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
renderInlineEditor() {
|
|
162
|
+
this.ensureProperValue();
|
|
163
|
+
|
|
164
|
+
const propValue = this.getContentValue();
|
|
165
|
+
const settingKey = getInlineEditablePropertyName( this.container );
|
|
166
|
+
const classes = ( this.el?.children?.[ 0 ]?.classList.toString() ?? '' ) + ' strip-styles';
|
|
167
|
+
const expectedTag = this.getExpectedTag();
|
|
168
|
+
|
|
169
|
+
const setValue = ( value: string | null ) => {
|
|
170
|
+
const valueToSave = value ? htmlPropTypeUtil.create( value ) : null;
|
|
171
|
+
|
|
172
|
+
this.model.get( 'settings' )?.set( settingKey, valueToSave );
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
this.$el.html( '' );
|
|
176
|
+
|
|
177
|
+
if ( this.inlineEditorRoot ) {
|
|
178
|
+
this.resetInlineEditorRoot();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.inlineEditorRoot = createRoot( this.el );
|
|
182
|
+
|
|
183
|
+
this.inlineEditorRoot.render(
|
|
184
|
+
<ThemeProvider>
|
|
185
|
+
<InlineEditor
|
|
186
|
+
attributes={ { class: classes } }
|
|
187
|
+
value={ propValue }
|
|
188
|
+
setValue={ setValue }
|
|
189
|
+
onBlur={ this.handleUnmountInlineEditor.bind( this ) }
|
|
190
|
+
autofocus
|
|
191
|
+
showToolbar
|
|
192
|
+
getInitialPopoverPosition={ getInitialPopoverPosition }
|
|
193
|
+
expectedTag={ expectedTag }
|
|
194
|
+
/>
|
|
195
|
+
</ThemeProvider>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type V1ElementConfig } from '@elementor/editor-elements';
|
|
2
2
|
|
|
3
3
|
import { type DomRenderer } from '../renderers/create-dom-renderer';
|
|
4
4
|
import { createPropsResolver } from '../renderers/create-props-resolver';
|
|
@@ -13,7 +13,7 @@ export type CreateTemplatedElementTypeOptions = {
|
|
|
13
13
|
element: TemplatedElementConfig;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
type TemplatedElementConfig = Required<
|
|
16
|
+
export type TemplatedElementConfig = Required<
|
|
17
17
|
Pick< V1ElementConfig, 'twig_templates' | 'twig_main_template' | 'atomic_props_schema' | 'base_styles_dictionary' >
|
|
18
18
|
>;
|
|
19
19
|
|
|
@@ -2,7 +2,9 @@ import { getWidgetsCache } from '@elementor/editor-elements';
|
|
|
2
2
|
import { __privateListenTo, v1ReadyEvent } from '@elementor/editor-v1-adapters';
|
|
3
3
|
|
|
4
4
|
import { createDomRenderer } from '../renderers/create-dom-renderer';
|
|
5
|
+
import { shouldRenderInlineEditingView } from '../utils/inline-editing-utils';
|
|
5
6
|
import { createElementType } from './create-element-type';
|
|
7
|
+
import { createInlineEditingElementType } from './create-inline-editing-element-type';
|
|
6
8
|
import {
|
|
7
9
|
canBeTemplated,
|
|
8
10
|
createTemplatedElementType,
|
|
@@ -39,7 +41,9 @@ export function initLegacyViews() {
|
|
|
39
41
|
if ( !! elementsLegacyTypes[ type ] && canBeTemplated( element ) ) {
|
|
40
42
|
ElementType = elementsLegacyTypes[ type ]( { type, renderer, element } );
|
|
41
43
|
} else if ( canBeTemplated( element ) ) {
|
|
42
|
-
ElementType =
|
|
44
|
+
ElementType = shouldRenderInlineEditingView( type )
|
|
45
|
+
? createInlineEditingElementType( { type, renderer, element } )
|
|
46
|
+
: createTemplatedElementType( { type, renderer, element } );
|
|
43
47
|
} else {
|
|
44
48
|
ElementType = createElementType( type );
|
|
45
49
|
}
|
package/src/legacy/types.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type V1Element } from '@elementor/editor-elements';
|
|
1
2
|
import { type Props } from '@elementor/editor-props';
|
|
2
3
|
|
|
3
4
|
export type LegacyWindow = Window & {
|
|
@@ -17,13 +18,15 @@ export type LegacyWindow = Window & {
|
|
|
17
18
|
elementsManager: {
|
|
18
19
|
registerElementType: ( type: ElementType ) => void;
|
|
19
20
|
};
|
|
20
|
-
$preview:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
$preview: JQueryElement &
|
|
22
|
+
[
|
|
23
|
+
{
|
|
24
|
+
contentWindow: {
|
|
25
|
+
dispatchEvent: ( event: Event ) => void;
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
$previewWrapper: JQueryElement;
|
|
27
30
|
};
|
|
28
31
|
};
|
|
29
32
|
|
|
@@ -34,7 +37,10 @@ export declare class ElementType {
|
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
export declare class ElementView {
|
|
40
|
+
container: V1Element;
|
|
41
|
+
|
|
37
42
|
$el: JQueryElement;
|
|
43
|
+
el: HTMLElement;
|
|
38
44
|
|
|
39
45
|
model: BackboneModel< ElementModel >;
|
|
40
46
|
|
|
@@ -101,6 +107,9 @@ type JQueryElement = {
|
|
|
101
107
|
find: ( selector: string ) => JQueryElement;
|
|
102
108
|
html: ( html: string ) => void;
|
|
103
109
|
get: ( index: number ) => HTMLElement;
|
|
110
|
+
attr: ( name: string ) => string;
|
|
111
|
+
on: ( event: string, childrenSelectors: string, handler: ( event: Event ) => void ) => void;
|
|
112
|
+
off: ( event: string, childrenSelectors: string, handler?: ( event: Event ) => void ) => void;
|
|
104
113
|
};
|
|
105
114
|
|
|
106
115
|
export type BackboneModel< Model extends object > = {
|