@elementor/editor-components 3.35.0-475 → 3.35.0-476

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-components",
3
3
  "description": "Elementor editor components",
4
- "version": "3.35.0-475",
4
+ "version": "3.35.0-476",
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-475",
44
- "@elementor/editor-canvas": "3.35.0-475",
45
- "@elementor/editor-controls": "3.35.0-475",
46
- "@elementor/editor-documents": "3.35.0-475",
47
- "@elementor/editor-editing-panel": "3.35.0-475",
48
- "@elementor/editor-elements": "3.35.0-475",
49
- "@elementor/editor-elements-panel": "3.35.0-475",
50
- "@elementor/editor-mcp": "3.35.0-475",
51
- "@elementor/editor-panels": "3.35.0-475",
52
- "@elementor/editor-props": "3.35.0-475",
53
- "@elementor/editor-styles-repository": "3.35.0-475",
54
- "@elementor/editor-ui": "3.35.0-475",
55
- "@elementor/editor-v1-adapters": "3.35.0-475",
56
- "@elementor/http-client": "3.35.0-475",
43
+ "@elementor/editor": "3.35.0-476",
44
+ "@elementor/editor-canvas": "3.35.0-476",
45
+ "@elementor/editor-controls": "3.35.0-476",
46
+ "@elementor/editor-documents": "3.35.0-476",
47
+ "@elementor/editor-editing-panel": "3.35.0-476",
48
+ "@elementor/editor-elements": "3.35.0-476",
49
+ "@elementor/editor-elements-panel": "3.35.0-476",
50
+ "@elementor/editor-mcp": "3.35.0-476",
51
+ "@elementor/editor-panels": "3.35.0-476",
52
+ "@elementor/editor-props": "3.35.0-476",
53
+ "@elementor/editor-styles-repository": "3.35.0-476",
54
+ "@elementor/editor-ui": "3.35.0-476",
55
+ "@elementor/editor-v1-adapters": "3.35.0-476",
56
+ "@elementor/http-client": "3.35.0-476",
57
57
  "@elementor/icons": "^1.63.0",
58
- "@elementor/mixpanel": "3.35.0-475",
59
- "@elementor/query": "3.35.0-475",
60
- "@elementor/schema": "3.35.0-475",
61
- "@elementor/store": "3.35.0-475",
58
+ "@elementor/mixpanel": "3.35.0-476",
59
+ "@elementor/query": "3.35.0-476",
60
+ "@elementor/schema": "3.35.0-476",
61
+ "@elementor/store": "3.35.0-476",
62
62
  "@elementor/ui": "1.36.17",
63
- "@elementor/utils": "3.35.0-475",
63
+ "@elementor/utils": "3.35.0-476",
64
64
  "@wordpress/i18n": "^5.13.0",
65
- "@elementor/editor-notifications": "3.35.0-475",
66
- "@elementor/editor-current-user": "3.35.0-475"
65
+ "@elementor/editor-notifications": "3.35.0-476",
66
+ "@elementor/editor-current-user": "3.35.0-476"
67
67
  },
68
68
  "peerDependencies": {
69
69
  "react": "^18.3.1",
@@ -7,11 +7,11 @@ import { useCanvasDocument } from '../../hooks/use-canvas-document';
7
7
  import { useElementRect } from '../../hooks/use-element-rect';
8
8
 
9
9
  type ModalProps = {
10
- element: HTMLElement;
10
+ topLevelElementDom: HTMLElement | null;
11
11
  onClose: () => void;
12
12
  };
13
13
 
14
- export function ComponentModal( { element, onClose }: ModalProps ) {
14
+ export function ComponentModal( { topLevelElementDom, onClose }: ModalProps ) {
15
15
  const canvasDocument = useCanvasDocument();
16
16
 
17
17
  useEffect( () => {
@@ -35,14 +35,23 @@ export function ComponentModal( { element, onClose }: ModalProps ) {
35
35
  return createPortal(
36
36
  <>
37
37
  <BlockEditPage />
38
- <Backdrop canvas={ canvasDocument } element={ element } onClose={ onClose } />
38
+ <Backdrop canvas={ canvasDocument } element={ topLevelElementDom } onClose={ onClose } />
39
39
  </>,
40
40
  canvasDocument.body
41
41
  );
42
42
  }
43
43
 
44
- function Backdrop( { canvas, element, onClose }: { canvas: HTMLDocument; element: HTMLElement; onClose: () => void } ) {
44
+ function Backdrop( {
45
+ canvas,
46
+ element,
47
+ onClose,
48
+ }: {
49
+ canvas: HTMLDocument;
50
+ element: HTMLElement | null;
51
+ onClose: () => void;
52
+ } ) {
45
53
  const rect = useElementRect( element );
54
+ const clipPath = element ? getRectPath( rect, canvas.defaultView as Window ) : undefined;
46
55
  const backdropStyle: CSSProperties = {
47
56
  position: 'fixed',
48
57
  top: 0,
@@ -53,7 +62,7 @@ function Backdrop( { canvas, element, onClose }: { canvas: HTMLDocument; element
53
62
  zIndex: 999,
54
63
  pointerEvents: 'painted',
55
64
  cursor: 'pointer',
56
- clipPath: getRectPath( rect, canvas.defaultView as Window ),
65
+ clipPath,
57
66
  };
58
67
 
59
68
  const handleKeyDown = ( event: React.KeyboardEvent ) => {
@@ -76,12 +85,7 @@ function Backdrop( { canvas, element, onClose }: { canvas: HTMLDocument; element
76
85
  }
77
86
 
78
87
  function getRectPath( rect: DOMRect, viewport: Window ) {
79
- const { x: originalX, y: originalY, width: originalWidth, height: originalHeight } = rect;
80
- const x = originalX;
81
- const y = originalY;
82
- const width = originalWidth;
83
- const height = originalHeight;
84
-
88
+ const { x, y, width, height } = rect;
85
89
  const { innerWidth: vw, innerHeight: vh } = viewport;
86
90
 
87
91
  const path = `path(evenodd, 'M 0 0
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { useEffect } from 'react';
2
+ import { useEffect, useState } from 'react';
3
3
  import { getV1DocumentsManager, type V1Document } from '@elementor/editor-documents';
4
4
  import { type V1Element } from '@elementor/editor-elements';
5
5
  import { __privateListenTo as listenTo, commandEndEvent } from '@elementor/editor-v1-adapters';
@@ -22,13 +22,13 @@ export function EditComponent() {
22
22
 
23
23
  const onClose = throttle( navigateBack, 100 );
24
24
 
25
- const elementDom = getComponentDOMElement( currentComponentId ?? undefined );
25
+ const topLevelElementDom = useComponentDOMElement( currentComponentId ?? undefined );
26
26
 
27
- if ( ! elementDom ) {
27
+ if ( ! currentComponentId ) {
28
28
  return null;
29
29
  }
30
30
 
31
- return <ComponentModal element={ elementDom } onClose={ onClose } />;
31
+ return <ComponentModal topLevelElementDom={ topLevelElementDom } onClose={ onClose } />;
32
32
  }
33
33
 
34
34
  function useHandleDocumentSwitches() {
@@ -112,18 +112,52 @@ function getInstanceTitle( instanceId: string | undefined, path: ComponentsPathI
112
112
  return editorSettings?.title;
113
113
  }
114
114
 
115
- function getComponentDOMElement( id: V1Document[ 'id' ] | undefined ) {
115
+ function useComponentDOMElement( id: V1Document[ 'id' ] | undefined ) {
116
+ const { componentContainerDomElement, topLevelElementDom } = getComponentDOMElements( id );
117
+
118
+ const [ currentElementDom, setCurrentElementDom ] = useState< HTMLElement | null >( topLevelElementDom );
119
+
120
+ useEffect( () => {
121
+ setCurrentElementDom( topLevelElementDom );
122
+ }, [ topLevelElementDom ] );
123
+
124
+ useEffect( () => {
125
+ if ( ! componentContainerDomElement ) {
126
+ return;
127
+ }
128
+
129
+ const mutationObserver = new MutationObserver( () => {
130
+ const newElementDom = componentContainerDomElement.children[ 0 ] as HTMLElement | null;
131
+ setCurrentElementDom( newElementDom );
132
+ } );
133
+
134
+ mutationObserver.observe( componentContainerDomElement, { childList: true } );
135
+
136
+ return () => {
137
+ mutationObserver.disconnect();
138
+ };
139
+ }, [ componentContainerDomElement ] );
140
+
141
+ return currentElementDom;
142
+ }
143
+
144
+ type ComponentDOMElements = {
145
+ componentContainerDomElement: HTMLElement | null;
146
+ topLevelElementDom: HTMLElement | null;
147
+ };
148
+
149
+ function getComponentDOMElements( id: V1Document[ 'id' ] | undefined ): ComponentDOMElements {
116
150
  if ( ! id ) {
117
- return null;
151
+ return { componentContainerDomElement: null, topLevelElementDom: null };
118
152
  }
119
153
 
120
154
  const documentsManager = getV1DocumentsManager();
121
155
 
122
156
  const currentComponent = documentsManager.get( id );
123
157
 
124
- const widget = currentComponent?.container as V1Element;
125
- const container = ( widget?.view?.el?.children?.[ 0 ] ?? null ) as HTMLElement | null;
126
- const elementDom = container?.children[ 0 ] as HTMLElement | null;
158
+ const componentContainer = currentComponent?.container as V1Element;
159
+ const componentContainerDomElement = ( componentContainer?.view?.el?.children?.[ 0 ] as HTMLElement ) ?? null;
160
+ const topLevelElementDom = ( componentContainerDomElement?.children[ 0 ] as HTMLElement ) ?? null;
127
161
 
128
- return elementDom ?? null;
162
+ return { componentContainerDomElement, topLevelElementDom };
129
163
  }
package/src/init.ts CHANGED
@@ -45,6 +45,7 @@ import { componentsStylesProvider } from './store/components-styles-provider';
45
45
  import { slice } from './store/store';
46
46
  import { beforeSave } from './sync/before-save';
47
47
  import { initCleanupOverridablePropsOnDelete } from './sync/cleanup-overridable-props-on-delete';
48
+ import { initHandleComponentEditModeContainer } from './sync/handle-component-edit-mode-container';
48
49
  import { initLoadComponentDataAfterInstanceAdded } from './sync/load-component-data-after-instance-added';
49
50
  import { initRegenerateOverrideKeys } from './sync/regenerate-override-keys';
50
51
  import { type ExtendedWindow } from './types';
@@ -143,4 +144,6 @@ export function init() {
143
144
  initNonAtomicNestingPrevention();
144
145
 
145
146
  initLoadComponentDataAfterInstanceAdded();
147
+
148
+ initHandleComponentEditModeContainer();
146
149
  }
@@ -2,11 +2,10 @@ import { isAtomicWidget } from '@elementor/editor-canvas';
2
2
  import { getAllDescendants, getElementType, type V1Element } from '@elementor/editor-elements';
3
3
  import { type NotificationData, notify } from '@elementor/editor-notifications';
4
4
  import { blockCommand } from '@elementor/editor-v1-adapters';
5
- import { __getStore as getStore } from '@elementor/store';
6
5
  import { __ } from '@wordpress/i18n';
7
6
 
8
- import { type ComponentsSlice, selectCurrentComponentId } from './store/store';
9
7
  import { type ExtendedWindow } from './types';
8
+ import { isEditingComponent } from './utils/is-editing-component';
10
9
 
11
10
  type CreateArgs = {
12
11
  container?: V1Element;
@@ -63,16 +62,6 @@ export function initNonAtomicNestingPrevention() {
63
62
  } );
64
63
  }
65
64
 
66
- function isEditingComponent(): boolean {
67
- const state = getStore()?.getState() as ComponentsSlice | undefined;
68
-
69
- if ( ! state ) {
70
- return false;
71
- }
72
-
73
- return selectCurrentComponentId( state ) !== null;
74
- }
75
-
76
65
  export function isElementAtomic( elementType: string ): boolean {
77
66
  return getElementType( elementType ) !== null;
78
67
  }
@@ -0,0 +1,114 @@
1
+ import { type V1Document } from '@elementor/editor-documents';
2
+ import { createElement, selectElement, type V1Element } from '@elementor/editor-elements';
3
+ import { registerDataHook } from '@elementor/editor-v1-adapters';
4
+
5
+ import { COMPONENT_DOCUMENT_TYPE } from '../components/consts';
6
+ import { isEditingComponent } from '../utils/is-editing-component';
7
+
8
+ const V4_DEFAULT_CONTAINER_TYPE = 'e-flexbox';
9
+
10
+ type Container = Omit< V1Element, 'children' | 'parent' > & {
11
+ document?: V1Document;
12
+ parent?: Container;
13
+ children?: Container[];
14
+ };
15
+
16
+ export function initHandleComponentEditModeContainer() {
17
+ initRedirectDropIntoComponent();
18
+ initHandleTopLevelElementDelete();
19
+ }
20
+
21
+ export type DeleteArgs = {
22
+ container?: Container;
23
+ containers?: Container[];
24
+ };
25
+
26
+ function initHandleTopLevelElementDelete() {
27
+ registerDataHook( 'after', 'document/elements/delete', ( args: DeleteArgs ) => {
28
+ if ( ! isEditingComponent() ) {
29
+ return;
30
+ }
31
+
32
+ const containers = args.containers ?? ( args.container ? [ args.container ] : [] );
33
+
34
+ for ( const container of containers ) {
35
+ if ( ! container.parent || ! isComponent( container.parent ) ) {
36
+ continue;
37
+ }
38
+
39
+ const component = container.parent;
40
+ const isComponentEmpty = component.children?.length === 0;
41
+
42
+ if ( isComponentEmpty ) {
43
+ createEmptyTopLevelContainer( container.parent );
44
+ }
45
+ }
46
+ } );
47
+ }
48
+
49
+ type DropArgs = {
50
+ container?: Container;
51
+ containers?: Container[];
52
+ model?: unknown;
53
+ options?: unknown;
54
+ };
55
+
56
+ function initRedirectDropIntoComponent() {
57
+ registerDataHook( 'dependency', 'preview/drop', ( args: DropArgs ) => {
58
+ if ( ! isEditingComponent() ) {
59
+ return true;
60
+ }
61
+
62
+ const containers = args.containers ?? ( args.container ? [ args.container ] : [] );
63
+
64
+ for ( const container of containers ) {
65
+ if ( ! isComponent( container ) ) {
66
+ continue;
67
+ }
68
+
69
+ const { shouldRedirect, container: redirectedContainer } = getComponentContainer( container );
70
+
71
+ if ( ! shouldRedirect ) {
72
+ continue;
73
+ }
74
+
75
+ if ( args.containers ) {
76
+ const index = args.containers.indexOf( container );
77
+ args.containers[ index ] = redirectedContainer;
78
+ } else {
79
+ args.container = redirectedContainer;
80
+ }
81
+ }
82
+
83
+ return true;
84
+ } );
85
+ }
86
+
87
+ function createEmptyTopLevelContainer( container: Container ) {
88
+ const newContainer = createElement( {
89
+ containerId: container.id,
90
+ model: { elType: V4_DEFAULT_CONTAINER_TYPE },
91
+ } );
92
+
93
+ selectElement( newContainer.id );
94
+ }
95
+
96
+ function getComponentContainer( container: Container ): { shouldRedirect: boolean; container: Container } {
97
+ const topLevelElement = container.children?.[ 0 ];
98
+
99
+ if ( topLevelElement ) {
100
+ return { shouldRedirect: true, container: topLevelElement };
101
+ }
102
+
103
+ return { shouldRedirect: false, container };
104
+ }
105
+
106
+ function isComponent( container: Container ): boolean {
107
+ const isDocument = container.id === 'document';
108
+
109
+ if ( ! isDocument ) {
110
+ return false;
111
+ }
112
+
113
+ return container.document?.config.type === COMPONENT_DOCUMENT_TYPE;
114
+ }
@@ -0,0 +1,13 @@
1
+ import { __getStore as getStore } from '@elementor/store';
2
+
3
+ import { type ComponentsSlice, selectCurrentComponentId } from '../store/store';
4
+
5
+ export function isEditingComponent(): boolean {
6
+ const state = getStore()?.getState() as ComponentsSlice | undefined;
7
+
8
+ if ( ! state ) {
9
+ return false;
10
+ }
11
+
12
+ return selectCurrentComponentId( state ) !== null;
13
+ }