@elementor/editor-components 3.35.0-395 → 3.35.0-396
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 +157 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +145 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +22 -22
- package/src/hooks/regenerate-override-keys.ts +2 -8
- package/src/init.ts +3 -0
- package/src/prevent-circular-nesting.ts +214 -0
- package/src/utils/tracking.ts +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-components",
|
|
3
3
|
"description": "Elementor editor components",
|
|
4
|
-
"version": "3.35.0-
|
|
4
|
+
"version": "3.35.0-396",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -40,30 +40,30 @@
|
|
|
40
40
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@elementor/editor": "3.35.0-
|
|
44
|
-
"@elementor/editor-canvas": "3.35.0-
|
|
45
|
-
"@elementor/editor-controls": "3.35.0-
|
|
46
|
-
"@elementor/editor-documents": "3.35.0-
|
|
47
|
-
"@elementor/editor-editing-panel": "3.35.0-
|
|
48
|
-
"@elementor/editor-elements": "3.35.0-
|
|
49
|
-
"@elementor/editor-elements-panel": "3.35.0-
|
|
50
|
-
"@elementor/editor-mcp": "3.35.0-
|
|
51
|
-
"@elementor/editor-panels": "3.35.0-
|
|
52
|
-
"@elementor/editor-props": "3.35.0-
|
|
53
|
-
"@elementor/editor-styles-repository": "3.35.0-
|
|
54
|
-
"@elementor/editor-ui": "3.35.0-
|
|
55
|
-
"@elementor/editor-v1-adapters": "3.35.0-
|
|
56
|
-
"@elementor/http-client": "3.35.0-
|
|
43
|
+
"@elementor/editor": "3.35.0-396",
|
|
44
|
+
"@elementor/editor-canvas": "3.35.0-396",
|
|
45
|
+
"@elementor/editor-controls": "3.35.0-396",
|
|
46
|
+
"@elementor/editor-documents": "3.35.0-396",
|
|
47
|
+
"@elementor/editor-editing-panel": "3.35.0-396",
|
|
48
|
+
"@elementor/editor-elements": "3.35.0-396",
|
|
49
|
+
"@elementor/editor-elements-panel": "3.35.0-396",
|
|
50
|
+
"@elementor/editor-mcp": "3.35.0-396",
|
|
51
|
+
"@elementor/editor-panels": "3.35.0-396",
|
|
52
|
+
"@elementor/editor-props": "3.35.0-396",
|
|
53
|
+
"@elementor/editor-styles-repository": "3.35.0-396",
|
|
54
|
+
"@elementor/editor-ui": "3.35.0-396",
|
|
55
|
+
"@elementor/editor-v1-adapters": "3.35.0-396",
|
|
56
|
+
"@elementor/http-client": "3.35.0-396",
|
|
57
57
|
"@elementor/icons": "^1.63.0",
|
|
58
|
-
"@elementor/mixpanel": "3.35.0-
|
|
59
|
-
"@elementor/query": "3.35.0-
|
|
60
|
-
"@elementor/schema": "3.35.0-
|
|
61
|
-
"@elementor/store": "3.35.0-
|
|
58
|
+
"@elementor/mixpanel": "3.35.0-396",
|
|
59
|
+
"@elementor/query": "3.35.0-396",
|
|
60
|
+
"@elementor/schema": "3.35.0-396",
|
|
61
|
+
"@elementor/store": "3.35.0-396",
|
|
62
62
|
"@elementor/ui": "1.36.17",
|
|
63
|
-
"@elementor/utils": "3.35.0-
|
|
63
|
+
"@elementor/utils": "3.35.0-396",
|
|
64
64
|
"@wordpress/i18n": "^5.13.0",
|
|
65
|
-
"@elementor/editor-notifications": "3.35.0-
|
|
66
|
-
"@elementor/editor-current-user": "3.35.0-
|
|
65
|
+
"@elementor/editor-notifications": "3.35.0-396",
|
|
66
|
+
"@elementor/editor-current-user": "3.35.0-396"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
69
|
"react": "^18.3.1",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getContainer, updateElementSettings, type V1Element } from '@elementor/editor-elements';
|
|
1
|
+
import { getAllDescendants, getContainer, updateElementSettings, type V1Element } from '@elementor/editor-elements';
|
|
2
2
|
import { registerDataHook } from '@elementor/editor-v1-adapters';
|
|
3
3
|
import { generateUniqueId } from '@elementor/utils';
|
|
4
4
|
|
|
@@ -42,13 +42,7 @@ function regenerateOverrideKeysRecursive( elementId: string ) {
|
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function getAllElements( container: V1Element ): V1Element[] {
|
|
49
|
-
const children = ( container.children ?? [] ).flatMap( getAllElements ) ?? [];
|
|
50
|
-
|
|
51
|
-
return [ container, ...children ];
|
|
45
|
+
getAllDescendants( container ).forEach( regenerateOverrideKeys );
|
|
52
46
|
}
|
|
53
47
|
|
|
54
48
|
function regenerateOverrideKeys( element: V1Element ) {
|
package/src/init.ts
CHANGED
|
@@ -37,6 +37,7 @@ import { COMPONENT_WIDGET_TYPE, createComponentType } from './create-component-t
|
|
|
37
37
|
import { initRegenerateOverrideKeys } from './hooks/regenerate-override-keys';
|
|
38
38
|
import { initMcp } from './mcp';
|
|
39
39
|
import { PopulateStore } from './populate-store';
|
|
40
|
+
import { initCircularNestingPrevention } from './prevent-circular-nesting';
|
|
40
41
|
import { componentOverridablePropTypeUtil } from './prop-types/component-overridable-prop-type';
|
|
41
42
|
import { loadComponentsAssets } from './store/actions/load-components-assets';
|
|
42
43
|
import { removeComponentStyles } from './store/actions/remove-component-styles';
|
|
@@ -129,4 +130,6 @@ export function init() {
|
|
|
129
130
|
initRegenerateOverrideKeys();
|
|
130
131
|
|
|
131
132
|
initMcp();
|
|
133
|
+
|
|
134
|
+
initCircularNestingPrevention();
|
|
132
135
|
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { getAllDescendants, type V1Element } from '@elementor/editor-elements';
|
|
2
|
+
import { type NotificationData, notify } from '@elementor/editor-notifications';
|
|
3
|
+
import { blockCommand } from '@elementor/editor-v1-adapters';
|
|
4
|
+
import { __getState as getState } from '@elementor/store';
|
|
5
|
+
import { __ } from '@wordpress/i18n';
|
|
6
|
+
|
|
7
|
+
import { type ComponentInstanceProp } from './prop-types/component-instance-prop-type';
|
|
8
|
+
import { type ComponentsSlice, selectCurrentComponentId, selectPath } from './store/store';
|
|
9
|
+
import { type ExtendedWindow } from './types';
|
|
10
|
+
|
|
11
|
+
const COMPONENT_TYPE = 'e-component';
|
|
12
|
+
|
|
13
|
+
type CreateArgs = {
|
|
14
|
+
container?: V1Element;
|
|
15
|
+
model?: {
|
|
16
|
+
elType?: string;
|
|
17
|
+
widgetType?: string;
|
|
18
|
+
settings?: {
|
|
19
|
+
component_instance?: ComponentInstanceProp;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type MoveArgs = {
|
|
25
|
+
containers?: V1Element[];
|
|
26
|
+
container?: V1Element;
|
|
27
|
+
target?: V1Element;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type PasteArgs = {
|
|
31
|
+
containers?: V1Element[];
|
|
32
|
+
container?: V1Element;
|
|
33
|
+
storageType?: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type ClipboardElement = {
|
|
37
|
+
widgetType?: string;
|
|
38
|
+
settings?: {
|
|
39
|
+
component_instance?: ComponentInstanceProp;
|
|
40
|
+
};
|
|
41
|
+
elements?: ClipboardElement[];
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type StorageContent = {
|
|
45
|
+
clipboard?: {
|
|
46
|
+
elements?: ClipboardElement[];
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const COMPONENT_CIRCULAR_NESTING_ALERT: NotificationData = {
|
|
51
|
+
type: 'default',
|
|
52
|
+
message: __( 'Cannot add this component here - it would create a circular reference.', 'elementor' ),
|
|
53
|
+
id: 'circular-component-nesting-blocked',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export function initCircularNestingPrevention() {
|
|
57
|
+
blockCommand( {
|
|
58
|
+
command: 'document/elements/create',
|
|
59
|
+
condition: blockCircularCreate,
|
|
60
|
+
} );
|
|
61
|
+
|
|
62
|
+
blockCommand( {
|
|
63
|
+
command: 'document/elements/move',
|
|
64
|
+
condition: blockCircularMove,
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
blockCommand( {
|
|
68
|
+
command: 'document/elements/paste',
|
|
69
|
+
condition: blockCircularPaste,
|
|
70
|
+
} );
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Note that this function only checks for direct circular nesting, not indirect nesting
|
|
74
|
+
export function wouldCreateCircularNesting( componentIdToAdd: number | string | undefined ): boolean {
|
|
75
|
+
if ( componentIdToAdd === undefined ) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const state = getState() as ComponentsSlice;
|
|
80
|
+
const currentComponentId = selectCurrentComponentId( state );
|
|
81
|
+
const path = selectPath( state );
|
|
82
|
+
|
|
83
|
+
if ( currentComponentId === null ) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if ( componentIdToAdd === currentComponentId ) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return path.some( ( item ) => item.componentId === componentIdToAdd );
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function extractComponentIdFromModel( model: CreateArgs[ 'model' ] ): number | string | null {
|
|
95
|
+
if ( ! model ) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const isComponent = model.widgetType === COMPONENT_TYPE;
|
|
100
|
+
|
|
101
|
+
if ( ! isComponent ) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return model.settings?.component_instance?.value?.component_id?.value ?? null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function extractComponentIdFromElement( element: ClipboardElement ): number | string | null {
|
|
109
|
+
if ( element.widgetType !== COMPONENT_TYPE ) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return element.settings?.component_instance?.value?.component_id?.value ?? null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function extractComponentIdsFromElements( elements: ClipboardElement[] ): Array< number | string > {
|
|
117
|
+
const ids: Array< number | string > = [];
|
|
118
|
+
|
|
119
|
+
for ( const element of elements ) {
|
|
120
|
+
const componentId = extractComponentIdFromElement( element );
|
|
121
|
+
|
|
122
|
+
if ( componentId !== null ) {
|
|
123
|
+
ids.push( componentId );
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if ( element.elements?.length ) {
|
|
127
|
+
ids.push( ...extractComponentIdsFromElements( element.elements ) );
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return ids;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function extractComponentIdFromContainer( container: V1Element ): number | string | null {
|
|
135
|
+
const widgetType = container.model?.get?.( 'widgetType' );
|
|
136
|
+
|
|
137
|
+
if ( widgetType !== COMPONENT_TYPE ) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const settings = container.model?.get?.( 'settings' ) as { get?: ( key: string ) => unknown } | undefined;
|
|
142
|
+
const componentInstance = settings?.get?.( 'component_instance' ) as ComponentInstanceProp | undefined;
|
|
143
|
+
|
|
144
|
+
return componentInstance?.value?.component_id?.value ?? null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function blockCircularCreate( args: CreateArgs ): boolean {
|
|
148
|
+
const componentId = extractComponentIdFromModel( args.model );
|
|
149
|
+
|
|
150
|
+
if ( componentId === null ) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const isBlocked = wouldCreateCircularNesting( componentId );
|
|
155
|
+
|
|
156
|
+
if ( isBlocked ) {
|
|
157
|
+
notify( COMPONENT_CIRCULAR_NESTING_ALERT );
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return isBlocked;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function blockCircularMove( args: MoveArgs ): boolean {
|
|
164
|
+
const { containers = [ args.container ] } = args;
|
|
165
|
+
|
|
166
|
+
const hasCircularComponent = containers.some( ( container ) => {
|
|
167
|
+
if ( ! container ) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const allElements = getAllDescendants( container );
|
|
172
|
+
|
|
173
|
+
return allElements.some( ( element ) => {
|
|
174
|
+
const componentId = extractComponentIdFromContainer( element );
|
|
175
|
+
|
|
176
|
+
if ( componentId === null ) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return wouldCreateCircularNesting( componentId );
|
|
181
|
+
} );
|
|
182
|
+
} );
|
|
183
|
+
|
|
184
|
+
if ( hasCircularComponent ) {
|
|
185
|
+
notify( COMPONENT_CIRCULAR_NESTING_ALERT );
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return hasCircularComponent;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function blockCircularPaste( args: PasteArgs ): boolean {
|
|
192
|
+
const { storageType } = args;
|
|
193
|
+
|
|
194
|
+
if ( storageType !== 'localstorage' ) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const data = (
|
|
199
|
+
window as unknown as ExtendedWindow & { elementorCommon?: { storage?: { get: () => StorageContent } } }
|
|
200
|
+
)?.elementorCommon?.storage?.get();
|
|
201
|
+
|
|
202
|
+
if ( ! data?.clipboard?.elements ) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const allComponentIds = extractComponentIdsFromElements( data.clipboard.elements );
|
|
207
|
+
const hasCircularComponent = allComponentIds.some( wouldCreateCircularNesting );
|
|
208
|
+
|
|
209
|
+
if ( hasCircularComponent ) {
|
|
210
|
+
notify( COMPONENT_CIRCULAR_NESTING_ALERT );
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return hasCircularComponent;
|
|
214
|
+
}
|
package/src/utils/tracking.ts
CHANGED
|
@@ -20,7 +20,7 @@ export const trackComponentEvent = ( { action, ...data }: ComponentEventData ) =
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
export const onElementDrop = ( _args: unknown, element: V1Element ) => {
|
|
23
|
-
if ( ! ( element
|
|
23
|
+
if ( ! ( element?.model?.get( 'widgetType' ) === 'e-component' ) ) {
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
|