@elementor/editor-elements 3.33.0-99 → 3.35.0-324
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 +200 -99
- package/dist/index.d.ts +200 -99
- package/dist/index.js +1161 -394
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1158 -401
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -5
- package/src/errors.ts +10 -0
- package/src/hooks/use-element-children.ts +12 -12
- package/src/hooks/use-element-editor-settings.ts +12 -0
- package/src/hooks/use-element-interactions.ts +25 -0
- package/src/hooks/use-element-setting.ts +1 -1
- package/src/hooks/use-selected-element.ts +2 -2
- package/src/index.ts +38 -27
- package/src/mcp/elements-tool.ts +345 -0
- package/src/mcp/handlers/common-style-utils.ts +23 -0
- package/src/mcp/handlers/create-element.ts +96 -0
- package/src/mcp/handlers/create-style.ts +42 -0
- package/src/mcp/handlers/delete-element.ts +17 -0
- package/src/mcp/handlers/delete-style.ts +22 -0
- package/src/mcp/handlers/deselect-element.ts +21 -0
- package/src/mcp/handlers/duplicate-element.ts +22 -0
- package/src/mcp/handlers/get-element-props.ts +28 -0
- package/src/mcp/handlers/get-element-schema.ts +17 -0
- package/src/mcp/handlers/get-selected.ts +5 -0
- package/src/mcp/handlers/get-styles.ts +26 -0
- package/src/mcp/handlers/list-available-types.ts +27 -0
- package/src/mcp/handlers/move-element.ts +30 -0
- package/src/mcp/handlers/select-element.ts +25 -0
- package/src/mcp/handlers/update-props.ts +22 -0
- package/src/mcp/handlers/update-styles.ts +45 -0
- package/src/mcp/index.ts +9 -0
- package/src/sync/delete-element.ts +8 -2
- package/src/sync/drop-element.ts +30 -0
- package/src/sync/duplicate-elements.ts +3 -4
- package/src/sync/get-current-document-container.ts +1 -1
- package/src/sync/get-element-editor-settings.ts +8 -0
- package/src/sync/get-element-interactions.ts +15 -0
- package/src/sync/get-element-label.ts +6 -1
- package/src/sync/get-element-type.ts +28 -0
- package/src/sync/get-elements.ts +1 -1
- package/src/sync/get-widgets-cache.ts +4 -3
- package/src/sync/move-elements.ts +9 -1
- package/src/sync/remove-elements.ts +11 -0
- package/src/sync/replace-element.ts +50 -12
- package/src/sync/types.ts +32 -3
- package/src/sync/update-element-editor-settings.ts +28 -0
- package/src/sync/update-element-interactions.ts +32 -0
- package/src/types.ts +16 -1
- package/src/hooks/use-element-type.ts +0 -35
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { getContainer } from '../../sync/get-container';
|
|
2
|
+
import { getElementSettings } from '../../sync/get-element-setting';
|
|
3
|
+
import { getElementType } from '../../sync/get-element-type';
|
|
4
|
+
|
|
5
|
+
export function handleGetElementProps( elementId: string ): Record< string, unknown > {
|
|
6
|
+
const container = getContainer( elementId );
|
|
7
|
+
|
|
8
|
+
if ( ! container ) {
|
|
9
|
+
throw new Error( `Element with ID "${ elementId }" not found` );
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const type = container.model.get( 'widgetType' ) || container.model.get( 'elType' );
|
|
13
|
+
|
|
14
|
+
if ( ! type ) {
|
|
15
|
+
throw new Error( `Element with ID "${ elementId }" has no type` );
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const elementType = getElementType( type );
|
|
19
|
+
|
|
20
|
+
if ( ! elementType ) {
|
|
21
|
+
throw new Error( `Element type "${ type }" is not atomic` );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const propsSchema = elementType.propsSchema;
|
|
25
|
+
const propKeys = Object.keys( propsSchema );
|
|
26
|
+
|
|
27
|
+
return getElementSettings( elementId, propKeys );
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type PropType } from '@elementor/editor-props';
|
|
2
|
+
import { getStylesSchema } from '@elementor/editor-styles';
|
|
3
|
+
|
|
4
|
+
import { getElementType } from '../../sync/get-element-type';
|
|
5
|
+
import type { ElementType } from '../../types';
|
|
6
|
+
|
|
7
|
+
export function handleGetElementSchema(
|
|
8
|
+
elementType: string
|
|
9
|
+
): ElementType & { stylesSchema: Record< string, PropType > } {
|
|
10
|
+
const elementTypeData = getElementType( elementType );
|
|
11
|
+
|
|
12
|
+
if ( ! elementTypeData ) {
|
|
13
|
+
throw new Error( `Element type "${ elementType }" not found or is not atomic` );
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return { ...elementTypeData, stylesSchema: getStylesSchema() };
|
|
17
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getElementStyles } from '../../sync/get-element-styles';
|
|
2
|
+
|
|
3
|
+
export function handleGetStyles( elementId: string ): Record< string, unknown > | null {
|
|
4
|
+
const styles = getElementStyles( elementId );
|
|
5
|
+
|
|
6
|
+
if ( ! styles ) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Convert to plain object for JSON serialization
|
|
11
|
+
return Object.fromEntries(
|
|
12
|
+
Object.entries( styles ).map( ( [ id, style ] ) => [
|
|
13
|
+
id,
|
|
14
|
+
{
|
|
15
|
+
id: style.id,
|
|
16
|
+
label: style.label,
|
|
17
|
+
type: style.type,
|
|
18
|
+
variants: style.variants.map( ( variant ) => ( {
|
|
19
|
+
meta: variant.meta,
|
|
20
|
+
props: variant.props,
|
|
21
|
+
custom_css: variant.custom_css,
|
|
22
|
+
} ) ),
|
|
23
|
+
},
|
|
24
|
+
] )
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getWidgetsCache } from '../../sync/get-widgets-cache';
|
|
2
|
+
|
|
3
|
+
export type AvailableElementType = {
|
|
4
|
+
type: string;
|
|
5
|
+
title: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function handleListAvailableTypes(): AvailableElementType[] {
|
|
9
|
+
const widgetsCache = getWidgetsCache();
|
|
10
|
+
|
|
11
|
+
if ( ! widgetsCache ) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const availableTypes: AvailableElementType[] = [];
|
|
16
|
+
|
|
17
|
+
Object.entries( widgetsCache ).forEach( ( [ type, config ] ) => {
|
|
18
|
+
if ( config?.atomic_controls && config?.atomic_props_schema ) {
|
|
19
|
+
availableTypes.push( {
|
|
20
|
+
type,
|
|
21
|
+
title: config.title || type,
|
|
22
|
+
} );
|
|
23
|
+
}
|
|
24
|
+
} );
|
|
25
|
+
|
|
26
|
+
return availableTypes;
|
|
27
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getContainer } from '../../sync/get-container';
|
|
2
|
+
import { moveElement } from '../../sync/move-element';
|
|
3
|
+
|
|
4
|
+
export function handleMoveElement( {
|
|
5
|
+
elementId,
|
|
6
|
+
targetContainerId,
|
|
7
|
+
}: {
|
|
8
|
+
elementId: string;
|
|
9
|
+
targetContainerId: string;
|
|
10
|
+
} ): { success: boolean } {
|
|
11
|
+
const container = getContainer( elementId );
|
|
12
|
+
|
|
13
|
+
if ( ! container ) {
|
|
14
|
+
throw new Error( `Element with ID "${ elementId }" not found` );
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const targetContainer = getContainer( targetContainerId );
|
|
18
|
+
|
|
19
|
+
if ( ! targetContainer ) {
|
|
20
|
+
throw new Error( `Target container with ID "${ targetContainerId }" not found` );
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
moveElement( {
|
|
24
|
+
elementId,
|
|
25
|
+
targetContainerId,
|
|
26
|
+
options: { useHistory: true },
|
|
27
|
+
} );
|
|
28
|
+
|
|
29
|
+
return { success: true };
|
|
30
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getContainer, selectElement } from '../../sync/get-container';
|
|
2
|
+
|
|
3
|
+
export function handleSelectElement( elementId: string ): { success: boolean } {
|
|
4
|
+
const container = getContainer( elementId );
|
|
5
|
+
|
|
6
|
+
if ( ! container ) {
|
|
7
|
+
throw new Error( `Element with ID "${ elementId }" not found` );
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
selectElement( elementId );
|
|
11
|
+
|
|
12
|
+
return { success: true };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function handleSelectMultipleElements( elementIds: string[] ): { success: boolean } {
|
|
16
|
+
elementIds.forEach( ( elementId ) => {
|
|
17
|
+
const container = getContainer( elementId );
|
|
18
|
+
|
|
19
|
+
if ( container ) {
|
|
20
|
+
selectElement( elementId );
|
|
21
|
+
}
|
|
22
|
+
} );
|
|
23
|
+
|
|
24
|
+
return { success: true };
|
|
25
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Props } from '@elementor/editor-props';
|
|
2
|
+
|
|
3
|
+
import { getContainer } from '../../sync/get-container';
|
|
4
|
+
import { updateElementSettings } from '../../sync/update-element-settings';
|
|
5
|
+
|
|
6
|
+
export function handleUpdateProps( { elementId, props }: { elementId: string; props: Record< string, unknown > } ): {
|
|
7
|
+
success: boolean;
|
|
8
|
+
} {
|
|
9
|
+
const container = getContainer( elementId );
|
|
10
|
+
|
|
11
|
+
if ( ! container ) {
|
|
12
|
+
throw new Error( `Element with ID "${ elementId }" not found` );
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
updateElementSettings( {
|
|
16
|
+
id: elementId,
|
|
17
|
+
props: props as Props,
|
|
18
|
+
withHistory: true,
|
|
19
|
+
} );
|
|
20
|
+
|
|
21
|
+
return { success: true };
|
|
22
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type Props } from '@elementor/editor-props';
|
|
2
|
+
import { type StyleDefinitionState } from '@elementor/editor-styles';
|
|
3
|
+
|
|
4
|
+
import { updateElementStyle } from '../../styles/update-element-style';
|
|
5
|
+
import { getElementStyles } from '../../sync/get-element-styles';
|
|
6
|
+
import { resolveBreakpointId } from './common-style-utils';
|
|
7
|
+
|
|
8
|
+
export function handleUpdateStyles( {
|
|
9
|
+
elementId,
|
|
10
|
+
styleId,
|
|
11
|
+
styles,
|
|
12
|
+
breakpoint = 'desktop',
|
|
13
|
+
state = null,
|
|
14
|
+
}: {
|
|
15
|
+
elementId: string;
|
|
16
|
+
styleId?: string;
|
|
17
|
+
styles: Props;
|
|
18
|
+
breakpoint?: string | null;
|
|
19
|
+
state?: string | null;
|
|
20
|
+
} ): { success: boolean } {
|
|
21
|
+
const resolvedBreakpoint = resolveBreakpointId( breakpoint );
|
|
22
|
+
|
|
23
|
+
const resolvedState: StyleDefinitionState =
|
|
24
|
+
state === null || state === undefined ? null : ( state as StyleDefinitionState );
|
|
25
|
+
const elementStyles = getElementStyles( elementId );
|
|
26
|
+
|
|
27
|
+
if ( ! elementStyles ) {
|
|
28
|
+
throw new Error( `Element with ID "${ elementId }" has no styles. Create a style first.` );
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const resolvedStyleId = styleId || Object.keys( elementStyles )[ 0 ];
|
|
32
|
+
|
|
33
|
+
if ( ! resolvedStyleId ) {
|
|
34
|
+
throw new Error( `Element with ID "${ elementId }" has no styles. Create a style first.` );
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
updateElementStyle( {
|
|
38
|
+
elementId,
|
|
39
|
+
styleId: resolvedStyleId,
|
|
40
|
+
meta: { breakpoint: resolvedBreakpoint, state: resolvedState },
|
|
41
|
+
props: styles,
|
|
42
|
+
} );
|
|
43
|
+
|
|
44
|
+
return { success: true };
|
|
45
|
+
}
|
package/src/mcp/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getMCPByDomain } from '@elementor/editor-mcp';
|
|
2
|
+
|
|
3
|
+
import { initElementsTool } from './elements-tool';
|
|
4
|
+
|
|
5
|
+
export function initMcp() {
|
|
6
|
+
const { setMCPDescription } = getMCPByDomain( 'elements' );
|
|
7
|
+
setMCPDescription( 'Tools for managing atomic elements in Elementor v4 editor' );
|
|
8
|
+
initElementsTool();
|
|
9
|
+
}
|
|
@@ -7,14 +7,20 @@ type Options = {
|
|
|
7
7
|
at?: number;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
export function deleteElement( {
|
|
10
|
+
export function deleteElement( {
|
|
11
|
+
elementId,
|
|
12
|
+
options = {},
|
|
13
|
+
}: {
|
|
14
|
+
elementId: string;
|
|
15
|
+
options?: Options;
|
|
16
|
+
} ): Promise< void > {
|
|
11
17
|
const container = getContainer( elementId );
|
|
12
18
|
|
|
13
19
|
if ( ! container ) {
|
|
14
20
|
throw new Error( `Element with ID "${ elementId }" not found` );
|
|
15
21
|
}
|
|
16
22
|
|
|
17
|
-
runCommand( 'document/elements/delete', {
|
|
23
|
+
return runCommand( 'document/elements/delete', {
|
|
18
24
|
container,
|
|
19
25
|
options,
|
|
20
26
|
} );
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { __privateRunCommandSync as runCommandSync } from '@elementor/editor-v1-adapters';
|
|
2
|
+
|
|
3
|
+
import { getContainer } from './get-container';
|
|
4
|
+
import { type V1Element, type V1ElementModelProps, type V1ElementSettingsProps } from './types';
|
|
5
|
+
|
|
6
|
+
type Options = {
|
|
7
|
+
useHistory?: boolean;
|
|
8
|
+
at?: number;
|
|
9
|
+
scrollIntoView: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type DropElementParams = {
|
|
13
|
+
containerId: string;
|
|
14
|
+
options?: Options;
|
|
15
|
+
model?: Omit< V1ElementModelProps, 'settings' | 'id' > & { settings?: V1ElementSettingsProps; id?: string };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function dropElement( { containerId, model, options }: DropElementParams ) {
|
|
19
|
+
const container = getContainer( containerId );
|
|
20
|
+
|
|
21
|
+
if ( ! container ) {
|
|
22
|
+
throw new Error( `Container with ID "${ containerId }" not found` );
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return runCommandSync< V1Element >( 'preview/drop', {
|
|
26
|
+
container,
|
|
27
|
+
model,
|
|
28
|
+
options,
|
|
29
|
+
} );
|
|
30
|
+
}
|
|
@@ -11,7 +11,7 @@ type DuplicateElementsParams = {
|
|
|
11
11
|
elementIds: string[];
|
|
12
12
|
title: string;
|
|
13
13
|
subtitle?: string;
|
|
14
|
-
onCreate?: ( duplicatedElements: DuplicatedElement[] ) =>
|
|
14
|
+
onCreate?: ( duplicatedElements: DuplicatedElement[] ) => void;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
type DuplicatedElement = {
|
|
@@ -33,7 +33,6 @@ export const duplicateElements = ( {
|
|
|
33
33
|
elementIds,
|
|
34
34
|
title,
|
|
35
35
|
subtitle = __( 'Item duplicated', 'elementor' ),
|
|
36
|
-
onCreate,
|
|
37
36
|
}: DuplicateElementsParams ): DuplicatedElementsResult => {
|
|
38
37
|
const undoableDuplicate = undoable(
|
|
39
38
|
{
|
|
@@ -60,7 +59,7 @@ export const duplicateElements = ( {
|
|
|
60
59
|
return acc;
|
|
61
60
|
}, [] as DuplicatedElement[] );
|
|
62
61
|
|
|
63
|
-
return { duplicatedElements
|
|
62
|
+
return { duplicatedElements };
|
|
64
63
|
},
|
|
65
64
|
undo: ( _: { elementIds: string[] }, { duplicatedElements }: DuplicatedElementsResult ) => {
|
|
66
65
|
// Delete duplicated elements in reverse order to avoid dependency issues
|
|
@@ -100,7 +99,7 @@ export const duplicateElements = ( {
|
|
|
100
99
|
return acc;
|
|
101
100
|
}, [] as DuplicatedElement[] );
|
|
102
101
|
|
|
103
|
-
return { duplicatedElements
|
|
102
|
+
return { duplicatedElements };
|
|
104
103
|
},
|
|
105
104
|
},
|
|
106
105
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ExtendedWindow } from './types';
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export function getCurrentDocumentContainer() {
|
|
4
4
|
const extendedWindow = window as unknown as ExtendedWindow;
|
|
5
5
|
|
|
6
6
|
return extendedWindow.elementor?.documents?.getCurrent?.()?.container ?? null;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ElementID } from '../types';
|
|
2
|
+
import { getContainer } from './get-container';
|
|
3
|
+
|
|
4
|
+
export function getElementEditorSettings( elementId: ElementID ) {
|
|
5
|
+
const container = getContainer( elementId );
|
|
6
|
+
|
|
7
|
+
return container?.model.get( 'editor_settings' ) ?? {};
|
|
8
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ElementID } from '../types';
|
|
2
|
+
import { getContainer } from './get-container';
|
|
3
|
+
import { type ElementInteractions } from './types';
|
|
4
|
+
|
|
5
|
+
export function getElementInteractions( elementId: ElementID ): ElementInteractions | undefined {
|
|
6
|
+
const container = getContainer( elementId );
|
|
7
|
+
|
|
8
|
+
const interactions = container?.model?.get( 'interactions' );
|
|
9
|
+
|
|
10
|
+
if ( typeof interactions === 'string' ) {
|
|
11
|
+
return JSON.parse( interactions ) as ElementInteractions;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return interactions;
|
|
15
|
+
}
|
|
@@ -2,8 +2,13 @@ import { ElementLabelNotExistsError, ElementTypeNotExistsError } from '../errors
|
|
|
2
2
|
import { getContainer } from '../sync/get-container';
|
|
3
3
|
import { getWidgetsCache } from '../sync/get-widgets-cache';
|
|
4
4
|
import { type ElementID } from '../types';
|
|
5
|
+
import { getSelectedElements } from './get-selected-elements';
|
|
6
|
+
|
|
7
|
+
export function getElementLabel( elementId?: ElementID ) {
|
|
8
|
+
if ( ! elementId ) {
|
|
9
|
+
elementId = getSelectedElements()?.[ 0 ]?.id;
|
|
10
|
+
}
|
|
5
11
|
|
|
6
|
-
export function getElementLabel( elementId: ElementID ) {
|
|
7
12
|
const container = getContainer( elementId );
|
|
8
13
|
|
|
9
14
|
const type = container?.model.get( 'widgetType' ) || container?.model.get( 'elType' );
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ElementType } from '../types';
|
|
2
|
+
import { getWidgetsCache } from './get-widgets-cache';
|
|
3
|
+
|
|
4
|
+
export function getElementType( type: string ): ElementType | null {
|
|
5
|
+
if ( ! type ) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const widgetsCache = getWidgetsCache();
|
|
10
|
+
const elementType = widgetsCache?.[ type ];
|
|
11
|
+
|
|
12
|
+
if ( ! elementType?.atomic_controls ) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if ( ! elementType?.atomic_props_schema ) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
key: type,
|
|
22
|
+
controls: elementType.atomic_controls,
|
|
23
|
+
propsSchema: elementType.atomic_props_schema,
|
|
24
|
+
dependenciesPerTargetMapping: elementType.dependencies_per_target_mapping ?? {},
|
|
25
|
+
title: elementType.title,
|
|
26
|
+
styleStates: elementType.atomic_style_states ?? [],
|
|
27
|
+
};
|
|
28
|
+
}
|
package/src/sync/get-elements.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ElementID } from '../types';
|
|
2
2
|
import { getContainer } from './get-container';
|
|
3
|
-
import getCurrentDocumentContainer from './get-current-document-container';
|
|
3
|
+
import { getCurrentDocumentContainer } from './get-current-document-container';
|
|
4
4
|
import { type V1Element } from './types';
|
|
5
5
|
|
|
6
6
|
export function getElements( root?: ElementID ): V1Element[] {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { type ExtendedWindow } from '
|
|
1
|
+
import { type ExtendedWindow, type V1ElementConfig } from '../sync/types.js';
|
|
2
|
+
type WidgetsCache< T > = Record< string, T >;
|
|
2
3
|
|
|
3
|
-
export function getWidgetsCache() {
|
|
4
|
+
export function getWidgetsCache< T extends V1ElementConfig >(): WidgetsCache< T > | null {
|
|
4
5
|
const extendedWindow = window as unknown as ExtendedWindow;
|
|
5
6
|
|
|
6
|
-
return extendedWindow?.elementor?.widgetsCache || null;
|
|
7
|
+
return ( extendedWindow?.elementor?.widgetsCache as WidgetsCache< T > ) || null;
|
|
7
8
|
}
|
|
@@ -9,6 +9,8 @@ type MoveElementsParams = {
|
|
|
9
9
|
moves: MoveElementParams[];
|
|
10
10
|
title: string;
|
|
11
11
|
subtitle?: string;
|
|
12
|
+
onMoveElements?: () => void;
|
|
13
|
+
onRestoreElements?: () => void;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
type OriginalPosition = {
|
|
@@ -34,12 +36,15 @@ export const moveElements = ( {
|
|
|
34
36
|
moves: movesToMake,
|
|
35
37
|
title,
|
|
36
38
|
subtitle = __( 'Elements moved', 'elementor' ),
|
|
39
|
+
onMoveElements,
|
|
40
|
+
onRestoreElements,
|
|
37
41
|
}: MoveElementsParams ): MovedElementsResult => {
|
|
38
42
|
const undoableMove = undoable(
|
|
39
43
|
{
|
|
40
44
|
do: ( { moves }: { moves: MoveElementParams[] } ): MovedElementsResult => {
|
|
41
45
|
const movedElements: MovedElement[] = [];
|
|
42
|
-
|
|
46
|
+
// Call onMoveElements before moving element to avoid conflicts between commands
|
|
47
|
+
onMoveElements?.();
|
|
43
48
|
moves.forEach( ( move ) => {
|
|
44
49
|
const { elementId } = move;
|
|
45
50
|
const sourceContainer = getContainer( elementId );
|
|
@@ -73,6 +78,8 @@ export const moveElements = ( {
|
|
|
73
78
|
return { movedElements };
|
|
74
79
|
},
|
|
75
80
|
undo: ( _: { moves: MoveElementParams[] }, { movedElements }: MovedElementsResult ) => {
|
|
81
|
+
onRestoreElements?.();
|
|
82
|
+
|
|
76
83
|
[ ...movedElements ].reverse().forEach( ( { originalPosition } ) => {
|
|
77
84
|
const { elementId, originalContainerId, originalIndex } = originalPosition;
|
|
78
85
|
|
|
@@ -91,6 +98,7 @@ export const moveElements = ( {
|
|
|
91
98
|
{ movedElements }: MovedElementsResult
|
|
92
99
|
): MovedElementsResult => {
|
|
93
100
|
const newMovedElements: MovedElement[] = [];
|
|
101
|
+
onMoveElements?.();
|
|
94
102
|
|
|
95
103
|
movedElements.forEach( ( { move, originalPosition } ) => {
|
|
96
104
|
const element = moveElement( {
|
|
@@ -10,6 +10,8 @@ type RemoveNestedElementsParams = {
|
|
|
10
10
|
elementIds: string[];
|
|
11
11
|
title: string;
|
|
12
12
|
subtitle?: string;
|
|
13
|
+
onRemoveElements?: () => void;
|
|
14
|
+
onRestoreElements?: () => void;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
type RemovedElement = {
|
|
@@ -30,6 +32,8 @@ export const removeElements = ( {
|
|
|
30
32
|
elementIds,
|
|
31
33
|
title,
|
|
32
34
|
subtitle = __( 'Item removed', 'elementor' ),
|
|
35
|
+
onRemoveElements,
|
|
36
|
+
onRestoreElements,
|
|
33
37
|
}: RemoveNestedElementsParams ): RemovedElementsResult => {
|
|
34
38
|
const undoableRemove = undoable(
|
|
35
39
|
{
|
|
@@ -54,6 +58,9 @@ export const removeElements = ( {
|
|
|
54
58
|
}
|
|
55
59
|
} );
|
|
56
60
|
|
|
61
|
+
// Call onRemoveElements before deleting elements to avoid conflicts between commands
|
|
62
|
+
onRemoveElements?.();
|
|
63
|
+
|
|
57
64
|
elementIdsParam.forEach( ( elementId ) => {
|
|
58
65
|
deleteElement( {
|
|
59
66
|
elementId,
|
|
@@ -64,6 +71,8 @@ export const removeElements = ( {
|
|
|
64
71
|
return { elementIds: elementIdsParam, removedElements };
|
|
65
72
|
},
|
|
66
73
|
undo: ( _: { elementIds: string[] }, { removedElements }: RemovedElementsResult ) => {
|
|
74
|
+
onRestoreElements?.();
|
|
75
|
+
|
|
67
76
|
// Restore elements in reverse order to maintain proper hierarchy
|
|
68
77
|
[ ...removedElements ].reverse().forEach( ( { model, parent, at } ) => {
|
|
69
78
|
if ( parent && model ) {
|
|
@@ -79,6 +88,8 @@ export const removeElements = ( {
|
|
|
79
88
|
_: { elementIds: string[] },
|
|
80
89
|
{ elementIds: originalElementIds, removedElements }: RemovedElementsResult
|
|
81
90
|
): RemovedElementsResult => {
|
|
91
|
+
onRemoveElements?.();
|
|
92
|
+
|
|
82
93
|
originalElementIds.forEach( ( elementId ) => {
|
|
83
94
|
deleteElement( {
|
|
84
95
|
elementId,
|
|
@@ -1,31 +1,69 @@
|
|
|
1
|
+
import { ElementIndexNotFoundError, ElementNotFoundError, ElementParentNotFoundError } from '../errors';
|
|
1
2
|
import { createElement } from './create-element';
|
|
2
3
|
import { deleteElement } from './delete-element';
|
|
3
4
|
import { getContainer } from './get-container';
|
|
4
|
-
import { type
|
|
5
|
+
import { type V1ElementData, type V1ElementModelProps } from './types';
|
|
6
|
+
|
|
7
|
+
type ElementLocation = {
|
|
8
|
+
containerId: string;
|
|
9
|
+
index: number;
|
|
10
|
+
};
|
|
5
11
|
|
|
6
12
|
type ReplaceElementArgs = {
|
|
7
|
-
currentElement:
|
|
13
|
+
currentElement: V1ElementData;
|
|
8
14
|
newElement: Omit< V1ElementModelProps, 'id' >;
|
|
9
15
|
withHistory?: boolean;
|
|
10
16
|
};
|
|
11
17
|
|
|
12
18
|
export const replaceElement = ( { currentElement, newElement, withHistory = true }: ReplaceElementArgs ) => {
|
|
13
|
-
const
|
|
19
|
+
const { containerId, index } = getNewElementLocation( currentElement, newElement );
|
|
20
|
+
|
|
21
|
+
createElement( {
|
|
22
|
+
containerId,
|
|
23
|
+
model: newElement,
|
|
24
|
+
options: { at: index, useHistory: withHistory },
|
|
25
|
+
} );
|
|
26
|
+
|
|
27
|
+
deleteElement( { elementId: currentElement.id, options: { useHistory: withHistory } } );
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function getNewElementLocation(
|
|
31
|
+
currentElement: V1ElementData,
|
|
32
|
+
newElement: Omit< V1ElementModelProps, 'id' >
|
|
33
|
+
): ElementLocation {
|
|
34
|
+
let location: ElementLocation;
|
|
14
35
|
|
|
36
|
+
const currentElementContainer = getContainer( currentElement.id );
|
|
37
|
+
if ( ! currentElementContainer ) {
|
|
38
|
+
throw new ElementNotFoundError( { context: { elementId: currentElement.id } } );
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const parent = currentElementContainer.parent;
|
|
15
42
|
if ( ! parent ) {
|
|
16
|
-
throw new
|
|
43
|
+
throw new ElementParentNotFoundError( { context: { elementId: currentElement.id } } );
|
|
17
44
|
}
|
|
18
45
|
|
|
19
|
-
const elementIndex =
|
|
46
|
+
const elementIndex = currentElementContainer.view?._index ?? 0;
|
|
20
47
|
if ( elementIndex === undefined || elementIndex === -1 ) {
|
|
21
|
-
throw new
|
|
48
|
+
throw new ElementIndexNotFoundError( { context: { elementId: currentElement.id } } );
|
|
22
49
|
}
|
|
23
50
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
51
|
+
location = { containerId: parent.id, index: elementIndex };
|
|
52
|
+
|
|
53
|
+
// If the element is at document top level and is a widget, wrap it with an empty container
|
|
54
|
+
if ( parent.id === 'document' && newElement.elType === 'widget' ) {
|
|
55
|
+
location = createWrapperForWidget( parent.id, elementIndex );
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return location;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function createWrapperForWidget( parentId: string, elementIndex: number ): ElementLocation {
|
|
62
|
+
const container = createElement( {
|
|
63
|
+
containerId: parentId,
|
|
64
|
+
model: { elType: 'container' },
|
|
65
|
+
options: { at: elementIndex, useHistory: false },
|
|
28
66
|
} );
|
|
29
67
|
|
|
30
|
-
|
|
31
|
-
}
|
|
68
|
+
return { containerId: container.id, index: 0 };
|
|
69
|
+
}
|
package/src/sync/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type PropsSchema, type PropValue } from '@elementor/editor-props';
|
|
2
|
-
import { type StyleDefinition, type StyleDefinitionID } from '@elementor/editor-styles';
|
|
2
|
+
import { type ClassState, type StyleDefinition, type StyleDefinitionID } from '@elementor/editor-styles';
|
|
3
3
|
|
|
4
4
|
import { type ControlItem } from '../types';
|
|
5
5
|
|
|
@@ -44,19 +44,47 @@ export type V1Element = {
|
|
|
44
44
|
parent?: V1Element;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
export type ElementInteractions = {
|
|
48
|
+
version: number;
|
|
49
|
+
items: InteractionItem[];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type InteractionItem = {
|
|
53
|
+
interaction_id?: string;
|
|
54
|
+
animation: {
|
|
55
|
+
animation_type: string;
|
|
56
|
+
animation_id: string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
47
60
|
export type V1ElementModelProps = {
|
|
61
|
+
isLocked?: boolean;
|
|
48
62
|
widgetType?: string;
|
|
49
63
|
elType: string;
|
|
50
64
|
id: string;
|
|
51
65
|
styles?: Record< StyleDefinitionID, StyleDefinition >;
|
|
52
66
|
elements?: V1Model< V1ElementModelProps >[];
|
|
53
67
|
settings?: V1ElementSettingsProps;
|
|
68
|
+
editor_settings?: V1ElementEditorSettingsProps;
|
|
69
|
+
interactions?: string | ElementInteractions;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export type V1ElementData = Omit< V1ElementModelProps, 'elements' > & {
|
|
73
|
+
elements?: V1ElementData[];
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type V1ElementEditorSettingsProps = {
|
|
77
|
+
title?: string;
|
|
78
|
+
initial_position?: number;
|
|
79
|
+
component_uid?: string;
|
|
54
80
|
};
|
|
55
81
|
|
|
56
82
|
export type V1ElementSettingsProps = Record< string, PropValue >;
|
|
57
83
|
|
|
58
|
-
export type V1ElementConfig = {
|
|
84
|
+
export type V1ElementConfig< T = object > = {
|
|
59
85
|
title: string;
|
|
86
|
+
widgetType?: string;
|
|
87
|
+
elType?: string;
|
|
60
88
|
controls: object;
|
|
61
89
|
atomic?: boolean;
|
|
62
90
|
atomic_controls?: ControlItem[];
|
|
@@ -66,7 +94,8 @@ export type V1ElementConfig = {
|
|
|
66
94
|
twig_main_template?: string;
|
|
67
95
|
base_styles?: Record< string, StyleDefinition >;
|
|
68
96
|
base_styles_dictionary?: Record< string, string >;
|
|
69
|
-
|
|
97
|
+
atomic_style_states?: ClassState[];
|
|
98
|
+
} & T;
|
|
70
99
|
|
|
71
100
|
type V1Model< T > = {
|
|
72
101
|
get: < K extends keyof T >( key: K ) => T[ K ];
|