@elementor/editor-elements 3.33.0-98 → 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 +204 -69
- package/dist/index.d.ts +204 -69
- package/dist/index.js +1163 -293
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1149 -291
- 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 -20
- 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-element.ts +45 -0
- package/src/sync/move-elements.ts +127 -0
- 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
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createElement } from './create-element';
|
|
2
|
+
import { deleteElement } from './delete-element';
|
|
3
|
+
import { getContainer } from './get-container';
|
|
4
|
+
|
|
5
|
+
type Options = {
|
|
6
|
+
useHistory?: boolean;
|
|
7
|
+
at?: number;
|
|
8
|
+
edit?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type MoveElementParams = {
|
|
12
|
+
elementId: string;
|
|
13
|
+
targetContainerId: string;
|
|
14
|
+
options?: Options;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function moveElement( { elementId, targetContainerId, options = {} }: MoveElementParams ) {
|
|
18
|
+
const container = getContainer( elementId );
|
|
19
|
+
const target = getContainer( targetContainerId );
|
|
20
|
+
|
|
21
|
+
if ( ! container ) {
|
|
22
|
+
throw new Error( `Element with ID "${ elementId }" not found` );
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if ( ! target ) {
|
|
26
|
+
throw new Error( `Target container with ID "${ targetContainerId }" not found` );
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const modelToRecreate = container.model.toJSON();
|
|
30
|
+
|
|
31
|
+
deleteElement( {
|
|
32
|
+
elementId,
|
|
33
|
+
// prevent inner history from being created
|
|
34
|
+
options: { ...options, useHistory: false },
|
|
35
|
+
} );
|
|
36
|
+
|
|
37
|
+
const newContainer = createElement( {
|
|
38
|
+
containerId: targetContainerId,
|
|
39
|
+
model: modelToRecreate,
|
|
40
|
+
// prevent inner history from being created
|
|
41
|
+
options: { edit: false, ...options, useHistory: false },
|
|
42
|
+
} );
|
|
43
|
+
|
|
44
|
+
return newContainer;
|
|
45
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { undoable } from '@elementor/editor-v1-adapters';
|
|
2
|
+
import { __ } from '@wordpress/i18n';
|
|
3
|
+
|
|
4
|
+
import { getContainer } from './get-container';
|
|
5
|
+
import { moveElement, type MoveElementParams } from './move-element';
|
|
6
|
+
import { type V1Element } from './types';
|
|
7
|
+
|
|
8
|
+
type MoveElementsParams = {
|
|
9
|
+
moves: MoveElementParams[];
|
|
10
|
+
title: string;
|
|
11
|
+
subtitle?: string;
|
|
12
|
+
onMoveElements?: () => void;
|
|
13
|
+
onRestoreElements?: () => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type OriginalPosition = {
|
|
17
|
+
elementId: string;
|
|
18
|
+
originalContainerId: string;
|
|
19
|
+
originalIndex: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type MovedElement = {
|
|
23
|
+
elementId: string;
|
|
24
|
+
originalPosition: OriginalPosition;
|
|
25
|
+
move: MoveElementParams;
|
|
26
|
+
element: V1Element;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type MovedElementsResult = {
|
|
30
|
+
movedElements: MovedElement[];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type { MoveElementsParams, MovedElement, MovedElementsResult };
|
|
34
|
+
|
|
35
|
+
export const moveElements = ( {
|
|
36
|
+
moves: movesToMake,
|
|
37
|
+
title,
|
|
38
|
+
subtitle = __( 'Elements moved', 'elementor' ),
|
|
39
|
+
onMoveElements,
|
|
40
|
+
onRestoreElements,
|
|
41
|
+
}: MoveElementsParams ): MovedElementsResult => {
|
|
42
|
+
const undoableMove = undoable(
|
|
43
|
+
{
|
|
44
|
+
do: ( { moves }: { moves: MoveElementParams[] } ): MovedElementsResult => {
|
|
45
|
+
const movedElements: MovedElement[] = [];
|
|
46
|
+
// Call onMoveElements before moving element to avoid conflicts between commands
|
|
47
|
+
onMoveElements?.();
|
|
48
|
+
moves.forEach( ( move ) => {
|
|
49
|
+
const { elementId } = move;
|
|
50
|
+
const sourceContainer = getContainer( elementId );
|
|
51
|
+
|
|
52
|
+
if ( ! sourceContainer ) {
|
|
53
|
+
throw new Error( `Element with ID "${ elementId }" not found` );
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const originalContainerId = sourceContainer.parent?.id || '';
|
|
57
|
+
const originalIndex = sourceContainer.parent?.children?.indexOf( sourceContainer ) ?? -1;
|
|
58
|
+
|
|
59
|
+
const originalPosition: OriginalPosition = {
|
|
60
|
+
elementId,
|
|
61
|
+
originalContainerId,
|
|
62
|
+
originalIndex,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const element = moveElement( {
|
|
66
|
+
...move,
|
|
67
|
+
options: { ...move.options, useHistory: false },
|
|
68
|
+
} );
|
|
69
|
+
|
|
70
|
+
movedElements.push( {
|
|
71
|
+
elementId,
|
|
72
|
+
originalPosition,
|
|
73
|
+
move,
|
|
74
|
+
element,
|
|
75
|
+
} );
|
|
76
|
+
} );
|
|
77
|
+
|
|
78
|
+
return { movedElements };
|
|
79
|
+
},
|
|
80
|
+
undo: ( _: { moves: MoveElementParams[] }, { movedElements }: MovedElementsResult ) => {
|
|
81
|
+
onRestoreElements?.();
|
|
82
|
+
|
|
83
|
+
[ ...movedElements ].reverse().forEach( ( { originalPosition } ) => {
|
|
84
|
+
const { elementId, originalContainerId, originalIndex } = originalPosition;
|
|
85
|
+
|
|
86
|
+
moveElement( {
|
|
87
|
+
elementId,
|
|
88
|
+
targetContainerId: originalContainerId,
|
|
89
|
+
options: {
|
|
90
|
+
useHistory: false,
|
|
91
|
+
at: originalIndex >= 0 ? originalIndex : undefined,
|
|
92
|
+
},
|
|
93
|
+
} );
|
|
94
|
+
} );
|
|
95
|
+
},
|
|
96
|
+
redo: (
|
|
97
|
+
_: { moves: MoveElementParams[] },
|
|
98
|
+
{ movedElements }: MovedElementsResult
|
|
99
|
+
): MovedElementsResult => {
|
|
100
|
+
const newMovedElements: MovedElement[] = [];
|
|
101
|
+
onMoveElements?.();
|
|
102
|
+
|
|
103
|
+
movedElements.forEach( ( { move, originalPosition } ) => {
|
|
104
|
+
const element = moveElement( {
|
|
105
|
+
...move,
|
|
106
|
+
options: { ...move.options, useHistory: false },
|
|
107
|
+
} );
|
|
108
|
+
|
|
109
|
+
newMovedElements.push( {
|
|
110
|
+
elementId: move.elementId,
|
|
111
|
+
originalPosition,
|
|
112
|
+
move,
|
|
113
|
+
element,
|
|
114
|
+
} );
|
|
115
|
+
} );
|
|
116
|
+
|
|
117
|
+
return { movedElements: newMovedElements };
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
title,
|
|
122
|
+
subtitle,
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
return undoableMove( { moves: movesToMake } );
|
|
127
|
+
};
|
|
@@ -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,
|