@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/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-351",
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-351",
41
- "@elementor/editor-controls": "3.35.0-351",
42
- "@elementor/editor-documents": "3.35.0-351",
43
- "@elementor/editor-elements": "3.35.0-351",
44
- "@elementor/editor-interactions": "3.35.0-351",
45
- "@elementor/editor-notifications": "3.35.0-351",
46
- "@elementor/editor-props": "3.35.0-351",
47
- "@elementor/editor-responsive": "3.35.0-351",
48
- "@elementor/editor-styles": "3.35.0-351",
49
- "@elementor/editor-styles-repository": "3.35.0-351",
50
- "@elementor/editor-v1-adapters": "3.35.0-351",
51
- "@elementor/editor-mcp": "3.35.0-351",
52
- "@elementor/schema": "3.35.0-351",
53
- "@elementor/twing": "3.35.0-351",
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-351",
56
- "@elementor/wp-media": "3.35.0-351",
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 { V1ElementConfig } from '@elementor/editor-elements';
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 = createTemplatedElementType( { type, renderer, element } );
44
+ ElementType = shouldRenderInlineEditingView( type )
45
+ ? createInlineEditingElementType( { type, renderer, element } )
46
+ : createTemplatedElementType( { type, renderer, element } );
43
47
  } else {
44
48
  ElementType = createElementType( type );
45
49
  }
@@ -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
- contentWindow: {
23
- dispatchEvent: ( event: Event ) => void;
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 > = {