@elementor/editor-components 4.0.0-666 → 4.0.0-668
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 +191 -3870
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +184 -3904
- package/dist/index.mjs.map +1 -1
- package/package.json +23 -23
- package/src/init.ts +0 -13
- package/src/extended/components/component-introduction.tsx +0 -77
- package/src/extended/components/component-panel-header/component-badge.tsx +0 -73
- package/src/extended/components/component-panel-header/component-panel-header.tsx +0 -98
- package/src/extended/components/component-properties-panel/component-properties-panel-content.tsx +0 -176
- package/src/extended/components/component-properties-panel/component-properties-panel.tsx +0 -43
- package/src/extended/components/component-properties-panel/properties-empty-state.tsx +0 -51
- package/src/extended/components/component-properties-panel/properties-group.tsx +0 -196
- package/src/extended/components/component-properties-panel/property-item.tsx +0 -124
- package/src/extended/components/component-properties-panel/sortable.tsx +0 -92
- package/src/extended/components/component-properties-panel/use-current-editable-item.ts +0 -73
- package/src/extended/components/component-properties-panel/utils/generate-unique-label.ts +0 -21
- package/src/extended/components/component-properties-panel/utils/validate-group-label.ts +0 -24
- package/src/extended/components/components-tab/component-item.tsx +0 -180
- package/src/extended/components/components-tab/components.tsx +0 -58
- package/src/extended/components/components-tab/delete-confirmation-dialog.tsx +0 -26
- package/src/extended/components/create-component-form/create-component-form.tsx +0 -281
- package/src/extended/components/create-component-form/hooks/use-form.ts +0 -72
- package/src/extended/components/create-component-form/utils/get-component-event-data.ts +0 -54
- package/src/extended/components/edit-component/component-modal.tsx +0 -133
- package/src/extended/components/edit-component/edit-component.tsx +0 -166
- package/src/extended/components/edit-component/use-canvas-document.ts +0 -9
- package/src/extended/components/edit-component/use-element-rect.ts +0 -81
- package/src/extended/components/instance-editing-panel/instance-editing-panel.tsx +0 -60
- package/src/extended/components/overridable-props/indicator.tsx +0 -83
- package/src/extended/components/overridable-props/overridable-prop-control.tsx +0 -127
- package/src/extended/components/overridable-props/overridable-prop-form.tsx +0 -135
- package/src/extended/components/overridable-props/overridable-prop-indicator.tsx +0 -138
- package/src/extended/components/overridable-props/utils/validate-prop-label.ts +0 -38
- package/src/extended/consts.ts +0 -3
- package/src/extended/hooks/use-navigate-back.ts +0 -24
- package/src/extended/init.ts +0 -108
- package/src/extended/mcp/index.ts +0 -14
- package/src/extended/mcp/save-as-component-tool.ts +0 -436
- package/src/extended/shortcuts/create-component-shortcut.ts +0 -121
- package/src/extended/store/actions/add-overridable-group.ts +0 -53
- package/src/extended/store/actions/archive-component.ts +0 -18
- package/src/extended/store/actions/create-unpublished-component.ts +0 -99
- package/src/extended/store/actions/delete-overridable-group.ts +0 -32
- package/src/extended/store/actions/delete-overridable-prop.ts +0 -64
- package/src/extended/store/actions/rename-component.ts +0 -48
- package/src/extended/store/actions/rename-overridable-group.ts +0 -33
- package/src/extended/store/actions/reorder-group-props.ts +0 -37
- package/src/extended/store/actions/reorder-overridable-groups.ts +0 -24
- package/src/extended/store/actions/reset-sanitized-components.ts +0 -5
- package/src/extended/store/actions/set-overridable-prop.ts +0 -109
- package/src/extended/store/actions/update-component-sanitized-attribute.ts +0 -7
- package/src/extended/store/actions/update-current-component.ts +0 -12
- package/src/extended/store/actions/update-overridable-prop-params.ts +0 -52
- package/src/extended/store/utils/groups-transformers.ts +0 -187
- package/src/extended/sync/before-save.ts +0 -52
- package/src/extended/sync/cleanup-overridable-props-on-delete.ts +0 -78
- package/src/extended/sync/create-components-before-save.ts +0 -111
- package/src/extended/sync/handle-component-edit-mode-container.ts +0 -114
- package/src/extended/sync/prevent-non-atomic-nesting.ts +0 -198
- package/src/extended/sync/revert-overridables-on-copy-or-duplicate.ts +0 -66
- package/src/extended/sync/sanitize-overridable-props.ts +0 -32
- package/src/extended/sync/set-component-overridable-props-settings-before-save.ts +0 -22
- package/src/extended/sync/update-archived-component-before-save.ts +0 -31
- package/src/extended/sync/update-component-title-before-save.ts +0 -18
- package/src/extended/utils/component-form-schema.ts +0 -32
- package/src/extended/utils/component-name-validation.ts +0 -25
- package/src/extended/utils/create-component-model.ts +0 -28
- package/src/extended/utils/get-container-for-new-element.ts +0 -49
- package/src/extended/utils/is-editing-component.ts +0 -5
- package/src/extended/utils/replace-element-with-component.ts +0 -11
- package/src/extended/utils/revert-overridable-settings.ts +0 -207
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { type CSSProperties, useEffect } from 'react';
|
|
3
|
-
import { createPortal } from 'react-dom';
|
|
4
|
-
import { __ } from '@wordpress/i18n';
|
|
5
|
-
|
|
6
|
-
import { useCanvasDocument } from './use-canvas-document';
|
|
7
|
-
import { useElementRect } from './use-element-rect';
|
|
8
|
-
|
|
9
|
-
type ModalProps = {
|
|
10
|
-
topLevelElementDom: HTMLElement | null;
|
|
11
|
-
onClose: () => void;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export function ComponentModal( { topLevelElementDom, onClose }: ModalProps ) {
|
|
15
|
-
const canvasDocument = useCanvasDocument();
|
|
16
|
-
|
|
17
|
-
useEffect( () => {
|
|
18
|
-
const handleEsc = ( event: KeyboardEvent ) => {
|
|
19
|
-
if ( event.key === 'Escape' ) {
|
|
20
|
-
onClose();
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
canvasDocument?.body.addEventListener( 'keydown', handleEsc );
|
|
25
|
-
|
|
26
|
-
return () => {
|
|
27
|
-
canvasDocument?.body.removeEventListener( 'keydown', handleEsc );
|
|
28
|
-
};
|
|
29
|
-
}, [ canvasDocument, onClose ] );
|
|
30
|
-
|
|
31
|
-
if ( ! canvasDocument?.body ) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return createPortal(
|
|
36
|
-
<>
|
|
37
|
-
<BlockEditPage />
|
|
38
|
-
<Backdrop canvas={ canvasDocument } element={ topLevelElementDom } onClose={ onClose } />
|
|
39
|
-
</>,
|
|
40
|
-
canvasDocument.body
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function Backdrop( {
|
|
45
|
-
canvas,
|
|
46
|
-
element,
|
|
47
|
-
onClose,
|
|
48
|
-
}: {
|
|
49
|
-
canvas: HTMLDocument;
|
|
50
|
-
element: HTMLElement | null;
|
|
51
|
-
onClose: () => void;
|
|
52
|
-
} ) {
|
|
53
|
-
const rect = useElementRect( element );
|
|
54
|
-
const clipPath = element ? getRectPath( rect, canvas.defaultView as Window ) : undefined;
|
|
55
|
-
const backdropStyle: CSSProperties = {
|
|
56
|
-
position: 'fixed',
|
|
57
|
-
top: 0,
|
|
58
|
-
left: 0,
|
|
59
|
-
width: '100vw',
|
|
60
|
-
height: '100vh',
|
|
61
|
-
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
62
|
-
zIndex: 999,
|
|
63
|
-
pointerEvents: 'painted',
|
|
64
|
-
cursor: 'pointer',
|
|
65
|
-
clipPath,
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const handleKeyDown = ( event: React.KeyboardEvent ) => {
|
|
69
|
-
if ( event.key === 'Enter' || event.key === ' ' ) {
|
|
70
|
-
event.preventDefault();
|
|
71
|
-
onClose();
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
return (
|
|
76
|
-
<div
|
|
77
|
-
style={ backdropStyle }
|
|
78
|
-
onClick={ onClose }
|
|
79
|
-
onKeyDown={ handleKeyDown }
|
|
80
|
-
role="button"
|
|
81
|
-
tabIndex={ 0 }
|
|
82
|
-
aria-label={ __( 'Exit component editing mode', 'elementor' ) }
|
|
83
|
-
/>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function getRectPath( rect: DOMRect, viewport: Window ) {
|
|
88
|
-
const { x, y, width, height } = rect;
|
|
89
|
-
const { innerWidth: vw, innerHeight: vh } = viewport;
|
|
90
|
-
|
|
91
|
-
const path = `path(evenodd, 'M 0 0
|
|
92
|
-
L ${ vw } 0
|
|
93
|
-
L ${ vw } ${ vh }
|
|
94
|
-
L 0 ${ vh }
|
|
95
|
-
Z
|
|
96
|
-
M ${ x } ${ y }
|
|
97
|
-
L ${ x + width } ${ y }
|
|
98
|
-
L ${ x + width } ${ y + height }
|
|
99
|
-
L ${ x } ${ y + height }
|
|
100
|
-
L ${ x } ${ y }
|
|
101
|
-
Z'
|
|
102
|
-
)`;
|
|
103
|
-
|
|
104
|
-
return path.replace( /\s{2,}/g, ' ' );
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* when switching to another document id, we get a document handler when hovering
|
|
109
|
-
* this functionality originates in Pro, and is intended for editing templates, e.g. header/footer
|
|
110
|
-
* in components we don't want that, so the easy way out is to prevent it of being displayed via a CSS rule
|
|
111
|
-
*/
|
|
112
|
-
function BlockEditPage() {
|
|
113
|
-
const blockV3DocumentHandlesStyles = `
|
|
114
|
-
.elementor-editor-active {
|
|
115
|
-
& .elementor-section-wrap.ui-sortable {
|
|
116
|
-
display: contents;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
& *[data-editable-elementor-document]:not(.elementor-edit-mode):hover {
|
|
120
|
-
& .elementor-document-handle:not(.elementor-document-save-back-handle) {
|
|
121
|
-
display: none;
|
|
122
|
-
|
|
123
|
-
&::before,
|
|
124
|
-
& .elementor-document-handle__inner {
|
|
125
|
-
display: none;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
`;
|
|
131
|
-
|
|
132
|
-
return <style data-e-style-id="e-block-v3-document-handles-styles">{ blockV3DocumentHandlesStyles }</style>;
|
|
133
|
-
}
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
3
|
-
import { getV1DocumentsManager, type V1Document } from '@elementor/editor-documents';
|
|
4
|
-
import { type V1Element } from '@elementor/editor-elements';
|
|
5
|
-
import { __privateListenTo as listenTo, commandEndEvent } from '@elementor/editor-v1-adapters';
|
|
6
|
-
import { __useSelector as useSelector } from '@elementor/store';
|
|
7
|
-
import { throttle } from '@elementor/utils';
|
|
8
|
-
|
|
9
|
-
import { apiClient } from '../../../api';
|
|
10
|
-
import { type ComponentsPathItem, selectPath, useCurrentComponentId } from '../../../store/store';
|
|
11
|
-
import { COMPONENT_DOCUMENT_TYPE } from '../../consts';
|
|
12
|
-
import { useNavigateBack } from '../../hooks/use-navigate-back';
|
|
13
|
-
import { resetSanitizedComponents } from '../../store/actions/reset-sanitized-components';
|
|
14
|
-
import { updateCurrentComponent } from '../../store/actions/update-current-component';
|
|
15
|
-
import { ComponentModal } from './component-modal';
|
|
16
|
-
|
|
17
|
-
export function EditComponent() {
|
|
18
|
-
const currentComponentId = useCurrentComponentId();
|
|
19
|
-
|
|
20
|
-
useHandleDocumentSwitches();
|
|
21
|
-
|
|
22
|
-
const navigateBack = useNavigateBack();
|
|
23
|
-
|
|
24
|
-
const onClose = throttle( navigateBack, 100 );
|
|
25
|
-
|
|
26
|
-
const topLevelElementDom = useComponentDOMElement( currentComponentId ?? undefined );
|
|
27
|
-
|
|
28
|
-
if ( ! currentComponentId ) {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return <ComponentModal topLevelElementDom={ topLevelElementDom } onClose={ onClose } />;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function useHandleDocumentSwitches() {
|
|
36
|
-
const documentsManager = getV1DocumentsManager();
|
|
37
|
-
const currentComponentId = useCurrentComponentId();
|
|
38
|
-
const path = useSelector( selectPath );
|
|
39
|
-
|
|
40
|
-
useEffect( () => {
|
|
41
|
-
return listenTo( commandEndEvent( 'editor/documents/open' ), () => {
|
|
42
|
-
const nextDocument = documentsManager.getCurrent();
|
|
43
|
-
|
|
44
|
-
if ( nextDocument.id === currentComponentId ) {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if ( currentComponentId ) {
|
|
49
|
-
apiClient.unlockComponent( currentComponentId );
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
resetSanitizedComponents();
|
|
53
|
-
|
|
54
|
-
const isComponent = nextDocument.config.type === COMPONENT_DOCUMENT_TYPE;
|
|
55
|
-
|
|
56
|
-
if ( ! isComponent ) {
|
|
57
|
-
updateCurrentComponent( { path: [], currentComponentId: null } );
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
updateCurrentComponent( {
|
|
62
|
-
path: getUpdatedComponentPath( path, nextDocument ),
|
|
63
|
-
currentComponentId: nextDocument.id,
|
|
64
|
-
} );
|
|
65
|
-
} );
|
|
66
|
-
}, [ path, documentsManager, currentComponentId ] );
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function getUpdatedComponentPath( path: ComponentsPathItem[], nextDocument: V1Document ): ComponentsPathItem[] {
|
|
70
|
-
const componentIndex = path.findIndex( ( { componentId } ) => componentId === nextDocument.id );
|
|
71
|
-
|
|
72
|
-
if ( componentIndex >= 0 ) {
|
|
73
|
-
// When exiting the editing of a nested component - we in fact go back a step
|
|
74
|
-
// so we need to make sure the path is cleaned up of any newer items
|
|
75
|
-
// By doing it with the slice and not a simple pop() - we could jump to any component in the path and make sure it becomes the current one
|
|
76
|
-
return path.slice( 0, componentIndex + 1 );
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const instanceId = nextDocument?.container.view?.el?.dataset.id;
|
|
80
|
-
const instanceTitle = getInstanceTitle( instanceId, path );
|
|
81
|
-
|
|
82
|
-
return [
|
|
83
|
-
...path,
|
|
84
|
-
{
|
|
85
|
-
instanceId,
|
|
86
|
-
instanceTitle,
|
|
87
|
-
componentId: nextDocument.id,
|
|
88
|
-
},
|
|
89
|
-
];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function getInstanceTitle( instanceId: string | undefined, path: ComponentsPathItem[] ): string | undefined {
|
|
93
|
-
if ( ! instanceId ) {
|
|
94
|
-
return undefined;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const documentsManager = getV1DocumentsManager();
|
|
98
|
-
const parentDocId = path.at( -1 )?.componentId ?? documentsManager.getInitialId();
|
|
99
|
-
const parentDoc = documentsManager.get( parentDocId );
|
|
100
|
-
|
|
101
|
-
type EditorSettings = { title?: string };
|
|
102
|
-
type ContainerWithChildren = V1Element & {
|
|
103
|
-
children?: {
|
|
104
|
-
findRecursive?: ( predicate: ( child: V1Element ) => boolean ) => V1Element | undefined;
|
|
105
|
-
};
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const parentContainer = parentDoc?.container as unknown as ContainerWithChildren | undefined;
|
|
109
|
-
const widget = parentContainer?.children?.findRecursive?.(
|
|
110
|
-
( container: V1Element ) => container.id === instanceId
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
const editorSettings = widget?.model?.get?.( 'editor_settings' ) as EditorSettings | undefined;
|
|
114
|
-
|
|
115
|
-
return editorSettings?.title;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function useComponentDOMElement( id: V1Document[ 'id' ] | undefined ) {
|
|
119
|
-
const { componentContainerDomElement, topLevelElementDom } = getComponentDOMElements( id );
|
|
120
|
-
|
|
121
|
-
const [ currentElementDom, setCurrentElementDom ] = useState< HTMLElement | null >( topLevelElementDom );
|
|
122
|
-
|
|
123
|
-
useEffect( () => {
|
|
124
|
-
setCurrentElementDom( topLevelElementDom );
|
|
125
|
-
}, [ topLevelElementDom ] );
|
|
126
|
-
|
|
127
|
-
useEffect( () => {
|
|
128
|
-
if ( ! componentContainerDomElement ) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const mutationObserver = new MutationObserver( () => {
|
|
133
|
-
const newElementDom = componentContainerDomElement.children[ 0 ] as HTMLElement | null;
|
|
134
|
-
setCurrentElementDom( newElementDom );
|
|
135
|
-
} );
|
|
136
|
-
|
|
137
|
-
mutationObserver.observe( componentContainerDomElement, { childList: true } );
|
|
138
|
-
|
|
139
|
-
return () => {
|
|
140
|
-
mutationObserver.disconnect();
|
|
141
|
-
};
|
|
142
|
-
}, [ componentContainerDomElement ] );
|
|
143
|
-
|
|
144
|
-
return currentElementDom;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
type ComponentDOMElements = {
|
|
148
|
-
componentContainerDomElement: HTMLElement | null;
|
|
149
|
-
topLevelElementDom: HTMLElement | null;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
function getComponentDOMElements( id: V1Document[ 'id' ] | undefined ): ComponentDOMElements {
|
|
153
|
-
if ( ! id ) {
|
|
154
|
-
return { componentContainerDomElement: null, topLevelElementDom: null };
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const documentsManager = getV1DocumentsManager();
|
|
158
|
-
|
|
159
|
-
const currentComponent = documentsManager.get( id );
|
|
160
|
-
|
|
161
|
-
const componentContainer = currentComponent?.container as V1Element;
|
|
162
|
-
const componentContainerDomElement = ( componentContainer?.view?.el?.children?.[ 0 ] as HTMLElement ) ?? null;
|
|
163
|
-
const topLevelElementDom = ( componentContainerDomElement?.children[ 0 ] as HTMLElement ) ?? null;
|
|
164
|
-
|
|
165
|
-
return { componentContainerDomElement, topLevelElementDom };
|
|
166
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__privateUseListenTo as useListenTo,
|
|
3
|
-
commandEndEvent,
|
|
4
|
-
getCanvasIframeDocument,
|
|
5
|
-
} from '@elementor/editor-v1-adapters';
|
|
6
|
-
|
|
7
|
-
export function useCanvasDocument() {
|
|
8
|
-
return useListenTo( commandEndEvent( 'editor/documents/attach-preview' ), () => getCanvasIframeDocument() );
|
|
9
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
import { throttle } from '@elementor/utils';
|
|
3
|
-
|
|
4
|
-
export function useElementRect( element: HTMLElement | null ) {
|
|
5
|
-
const [ rect, setRect ] = useState< DOMRect >( new DOMRect( 0, 0, 0, 0 ) );
|
|
6
|
-
|
|
7
|
-
const onChange = throttle(
|
|
8
|
-
() => {
|
|
9
|
-
setRect( element?.getBoundingClientRect() ?? new DOMRect( 0, 0, 0, 0 ) );
|
|
10
|
-
},
|
|
11
|
-
20,
|
|
12
|
-
true
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
useScrollListener( { element, onChange } );
|
|
16
|
-
useResizeListener( { element, onChange } );
|
|
17
|
-
useMutationsListener( { element, onChange } );
|
|
18
|
-
|
|
19
|
-
useEffect(
|
|
20
|
-
() => () => {
|
|
21
|
-
onChange.cancel();
|
|
22
|
-
},
|
|
23
|
-
[ onChange ]
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
return rect;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
type ListenerProps = {
|
|
30
|
-
element: HTMLElement | null;
|
|
31
|
-
onChange: () => void;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
function useScrollListener( { element, onChange }: ListenerProps ) {
|
|
35
|
-
useEffect( () => {
|
|
36
|
-
if ( ! element ) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const win = element.ownerDocument?.defaultView;
|
|
41
|
-
win?.addEventListener( 'scroll', onChange, { passive: true } );
|
|
42
|
-
|
|
43
|
-
return () => {
|
|
44
|
-
win?.removeEventListener( 'scroll', onChange );
|
|
45
|
-
};
|
|
46
|
-
}, [ element, onChange ] );
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function useResizeListener( { element, onChange }: ListenerProps ) {
|
|
50
|
-
useEffect( () => {
|
|
51
|
-
if ( ! element ) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const resizeObserver = new ResizeObserver( onChange );
|
|
56
|
-
resizeObserver.observe( element );
|
|
57
|
-
|
|
58
|
-
const win = element.ownerDocument?.defaultView;
|
|
59
|
-
win?.addEventListener( 'resize', onChange, { passive: true } );
|
|
60
|
-
|
|
61
|
-
return () => {
|
|
62
|
-
resizeObserver.disconnect();
|
|
63
|
-
win?.removeEventListener( 'resize', onChange );
|
|
64
|
-
};
|
|
65
|
-
}, [ element, onChange ] );
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function useMutationsListener( { element, onChange }: ListenerProps ) {
|
|
69
|
-
useEffect( () => {
|
|
70
|
-
if ( ! element ) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const mutationObserver = new MutationObserver( onChange );
|
|
75
|
-
mutationObserver.observe( element, { childList: true, subtree: true } );
|
|
76
|
-
|
|
77
|
-
return () => {
|
|
78
|
-
mutationObserver.disconnect();
|
|
79
|
-
};
|
|
80
|
-
}, [ element, onChange ] );
|
|
81
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { PencilIcon } from '@elementor/icons';
|
|
3
|
-
import { Box } from '@elementor/ui';
|
|
4
|
-
import { __ } from '@wordpress/i18n';
|
|
5
|
-
|
|
6
|
-
import { EmptyState } from '../../../components/instance-editing-panel/empty-state';
|
|
7
|
-
import { InstancePanelBody } from '../../../components/instance-editing-panel/instance-panel-body';
|
|
8
|
-
import {
|
|
9
|
-
EditComponentAction,
|
|
10
|
-
InstancePanelHeader,
|
|
11
|
-
} from '../../../components/instance-editing-panel/instance-panel-header';
|
|
12
|
-
import { useInstancePanelData } from '../../../components/instance-editing-panel/use-instance-panel-data';
|
|
13
|
-
import { useComponentsPermissions } from '../../../hooks/use-components-permissions';
|
|
14
|
-
import { ComponentInstanceProvider } from '../../../provider/component-instance-context';
|
|
15
|
-
import { switchToComponent } from '../../../utils/switch-to-component';
|
|
16
|
-
|
|
17
|
-
export function ExtendedInstanceEditingPanel() {
|
|
18
|
-
const { canEdit } = useComponentsPermissions();
|
|
19
|
-
const data = useInstancePanelData();
|
|
20
|
-
|
|
21
|
-
if ( ! data ) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const { componentId, component, overrides, overridableProps, groups, isEmpty, componentInstanceId } = data;
|
|
26
|
-
|
|
27
|
-
/* translators: %s: component name. */
|
|
28
|
-
const panelTitle = __( 'Edit %s', 'elementor' ).replace( '%s', component.name );
|
|
29
|
-
|
|
30
|
-
const handleEditComponent = () => switchToComponent( componentId, componentInstanceId );
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<Box data-testid="instance-editing-panel">
|
|
34
|
-
<ComponentInstanceProvider
|
|
35
|
-
componentId={ componentId }
|
|
36
|
-
overrides={ overrides }
|
|
37
|
-
overridableProps={ overridableProps }
|
|
38
|
-
>
|
|
39
|
-
<InstancePanelHeader
|
|
40
|
-
componentName={ component.name }
|
|
41
|
-
actions={
|
|
42
|
-
canEdit ? (
|
|
43
|
-
<EditComponentAction
|
|
44
|
-
label={ panelTitle }
|
|
45
|
-
onClick={ handleEditComponent }
|
|
46
|
-
icon={ PencilIcon }
|
|
47
|
-
/>
|
|
48
|
-
) : undefined
|
|
49
|
-
}
|
|
50
|
-
/>
|
|
51
|
-
<InstancePanelBody
|
|
52
|
-
groups={ groups }
|
|
53
|
-
isEmpty={ isEmpty }
|
|
54
|
-
emptyState={ <EmptyState onEditComponent={ canEdit ? handleEditComponent : undefined } /> }
|
|
55
|
-
componentInstanceId={ componentInstanceId }
|
|
56
|
-
/>
|
|
57
|
-
</ComponentInstanceProvider>
|
|
58
|
-
</Box>
|
|
59
|
-
);
|
|
60
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { forwardRef } from 'react';
|
|
3
|
-
import { CheckIcon, PlusIcon } from '@elementor/icons';
|
|
4
|
-
import { Box, styled } from '@elementor/ui';
|
|
5
|
-
import { __ } from '@wordpress/i18n';
|
|
6
|
-
|
|
7
|
-
const SIZE = 'tiny';
|
|
8
|
-
|
|
9
|
-
const IconContainer = styled( Box )`
|
|
10
|
-
pointer-events: none;
|
|
11
|
-
opacity: 0;
|
|
12
|
-
transition: opacity 0.2s ease-in-out;
|
|
13
|
-
|
|
14
|
-
& > svg {
|
|
15
|
-
position: absolute;
|
|
16
|
-
top: 50%;
|
|
17
|
-
left: 50%;
|
|
18
|
-
transform: translate( -50%, -50% );
|
|
19
|
-
width: 10px;
|
|
20
|
-
height: 10px;
|
|
21
|
-
fill: ${ ( { theme } ) => theme.palette.primary.contrastText };
|
|
22
|
-
stroke: ${ ( { theme } ) => theme.palette.primary.contrastText };
|
|
23
|
-
stroke-width: 2px;
|
|
24
|
-
}
|
|
25
|
-
`;
|
|
26
|
-
|
|
27
|
-
const Content = styled( Box )`
|
|
28
|
-
position: relative;
|
|
29
|
-
display: flex;
|
|
30
|
-
align-items: center;
|
|
31
|
-
justify-content: center;
|
|
32
|
-
cursor: pointer;
|
|
33
|
-
width: 16px;
|
|
34
|
-
height: 16px;
|
|
35
|
-
margin-inline: ${ ( { theme } ) => theme.spacing( 0.5 ) };
|
|
36
|
-
|
|
37
|
-
&:before {
|
|
38
|
-
content: '';
|
|
39
|
-
display: block;
|
|
40
|
-
position: absolute;
|
|
41
|
-
top: 50%;
|
|
42
|
-
left: 50%;
|
|
43
|
-
transform: translate( -50%, -50% ) rotate( 45deg );
|
|
44
|
-
width: 5px;
|
|
45
|
-
height: 5px;
|
|
46
|
-
border-radius: 1px;
|
|
47
|
-
background-color: ${ ( { theme } ) => theme.palette.primary.main };
|
|
48
|
-
transition: all 0.1s ease-in-out;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
&:hover,
|
|
52
|
-
&.enlarged {
|
|
53
|
-
&:before {
|
|
54
|
-
width: 12px;
|
|
55
|
-
height: 12px;
|
|
56
|
-
border-radius: 2px;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.icon {
|
|
60
|
-
opacity: 1;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
`;
|
|
64
|
-
|
|
65
|
-
type Props = {
|
|
66
|
-
isOverridable: boolean;
|
|
67
|
-
isOpen: boolean;
|
|
68
|
-
};
|
|
69
|
-
export const Indicator = forwardRef< HTMLDivElement, Props >( ( { isOpen, isOverridable, ...props }, ref ) => (
|
|
70
|
-
<Content
|
|
71
|
-
role="button"
|
|
72
|
-
ref={ ref }
|
|
73
|
-
{ ...props }
|
|
74
|
-
className={ isOpen || isOverridable ? 'enlarged' : '' }
|
|
75
|
-
aria-label={
|
|
76
|
-
isOverridable ? __( 'Overridable property', 'elementor' ) : __( 'Make prop overridable', 'elementor' )
|
|
77
|
-
}
|
|
78
|
-
>
|
|
79
|
-
<IconContainer className="icon">
|
|
80
|
-
{ isOverridable ? <CheckIcon fontSize={ SIZE } /> : <PlusIcon fontSize={ SIZE } /> }
|
|
81
|
-
</IconContainer>
|
|
82
|
-
</Content>
|
|
83
|
-
) );
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { type ComponentType } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
ControlReplacementsProvider,
|
|
5
|
-
getControlReplacements,
|
|
6
|
-
PropKeyProvider,
|
|
7
|
-
PropProvider,
|
|
8
|
-
useBoundProp,
|
|
9
|
-
useControlReplacement,
|
|
10
|
-
} from '@elementor/editor-controls';
|
|
11
|
-
import { createTopLevelObjectType, useElement } from '@elementor/editor-editing-panel';
|
|
12
|
-
import { type PropValue } from '@elementor/editor-props';
|
|
13
|
-
|
|
14
|
-
import { type ComponentInstanceOverridePropValue } from '../../../prop-types/component-instance-override-prop-type';
|
|
15
|
-
import {
|
|
16
|
-
componentOverridablePropTypeUtil,
|
|
17
|
-
type ComponentOverridablePropValue,
|
|
18
|
-
} from '../../../prop-types/component-overridable-prop-type';
|
|
19
|
-
import { OverridablePropProvider } from '../../../provider/overridable-prop-context';
|
|
20
|
-
import { updateOverridableProp } from '../../../store/actions/update-overridable-prop';
|
|
21
|
-
import { useCurrentComponentId, useOverridableProps } from '../../../store/store';
|
|
22
|
-
import { getPropTypeForComponentOverride } from '../../../utils/get-prop-type-for-component-override';
|
|
23
|
-
import { OVERRIDABLE_PROP_REPLACEMENT_ID } from '../../consts';
|
|
24
|
-
|
|
25
|
-
export function OverridablePropControl< T extends object >( {
|
|
26
|
-
OriginalControl,
|
|
27
|
-
...props
|
|
28
|
-
}: T & { OriginalControl: ComponentType< T > } ) {
|
|
29
|
-
const { elementType } = useElement();
|
|
30
|
-
|
|
31
|
-
const { value, bind, setValue, placeholder, ...propContext } = useBoundProp( componentOverridablePropTypeUtil );
|
|
32
|
-
const componentId = useCurrentComponentId();
|
|
33
|
-
const overridableProps = useOverridableProps( componentId );
|
|
34
|
-
const filteredReplacements = getControlReplacements().filter(
|
|
35
|
-
( r ) => ! r.id || r.id !== OVERRIDABLE_PROP_REPLACEMENT_ID
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
if ( ! componentId ) {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if ( ! value?.override_key ) {
|
|
43
|
-
throw new Error( 'Override key is required' );
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const isComponentInstance = elementType.key === 'e-component';
|
|
47
|
-
const overridablePropData = overridableProps?.props?.[ value.override_key ];
|
|
48
|
-
|
|
49
|
-
const setOverridableValue = ( newValue: Record< typeof bind, PropValue | null > ) => {
|
|
50
|
-
const propValue = {
|
|
51
|
-
...value,
|
|
52
|
-
origin_value: newValue[ bind ],
|
|
53
|
-
} as ComponentOverridablePropValue;
|
|
54
|
-
|
|
55
|
-
setValue( propValue );
|
|
56
|
-
|
|
57
|
-
if ( ! isComponentInstance ) {
|
|
58
|
-
updateOverridableProp( componentId, propValue, overridablePropData?.originPropFields );
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const defaultPropType = elementType.propsSchema[ bind ];
|
|
63
|
-
const overridePropType = overridablePropData ? getPropTypeForComponentOverride( overridablePropData ) : undefined;
|
|
64
|
-
|
|
65
|
-
const resolvedPropType = overridePropType ?? defaultPropType;
|
|
66
|
-
|
|
67
|
-
if ( ! resolvedPropType ) {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const propType = createTopLevelObjectType( {
|
|
72
|
-
schema: {
|
|
73
|
-
[ bind ]: resolvedPropType,
|
|
74
|
-
},
|
|
75
|
-
} );
|
|
76
|
-
|
|
77
|
-
const propValue = (
|
|
78
|
-
isComponentInstance
|
|
79
|
-
? ( value.origin_value?.value as ComponentInstanceOverridePropValue ).override_value
|
|
80
|
-
: value.origin_value
|
|
81
|
-
) as PropValue;
|
|
82
|
-
|
|
83
|
-
const objectPlaceholder: Record< string, PropValue > | undefined = placeholder
|
|
84
|
-
? { [ bind ]: placeholder }
|
|
85
|
-
: undefined;
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<OverridablePropProvider value={ value }>
|
|
89
|
-
<PropProvider
|
|
90
|
-
{ ...propContext }
|
|
91
|
-
propType={ propType }
|
|
92
|
-
setValue={ setOverridableValue }
|
|
93
|
-
value={ {
|
|
94
|
-
[ bind ]: propValue,
|
|
95
|
-
} }
|
|
96
|
-
placeholder={ objectPlaceholder }
|
|
97
|
-
>
|
|
98
|
-
<PropKeyProvider bind={ bind }>
|
|
99
|
-
<ControlReplacementsProvider replacements={ filteredReplacements }>
|
|
100
|
-
<ControlWithReplacements OriginalControl={ OriginalControl } props={ props as T } />
|
|
101
|
-
</ControlReplacementsProvider>
|
|
102
|
-
</PropKeyProvider>
|
|
103
|
-
</PropProvider>
|
|
104
|
-
</OverridablePropProvider>
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
type ControlComponentType = ComponentType< object & { OriginalControl: ComponentType } >;
|
|
109
|
-
|
|
110
|
-
function ControlWithReplacements< T extends object >( {
|
|
111
|
-
OriginalControl,
|
|
112
|
-
props,
|
|
113
|
-
}: {
|
|
114
|
-
OriginalControl: ComponentType< T >;
|
|
115
|
-
props: T;
|
|
116
|
-
} ) {
|
|
117
|
-
const { ControlToRender, isReplaced } = useControlReplacement( OriginalControl as ControlComponentType );
|
|
118
|
-
|
|
119
|
-
if ( isReplaced ) {
|
|
120
|
-
const ReplacementControl = ControlToRender as unknown as ComponentType<
|
|
121
|
-
T & { OriginalControl: ComponentType< T > }
|
|
122
|
-
>;
|
|
123
|
-
return <ReplacementControl { ...props } OriginalControl={ OriginalControl } />;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return <OriginalControl { ...props } />;
|
|
127
|
-
}
|