@elementor/editor-components 3.35.0-355 → 3.35.0-357
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 +300 -44
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +298 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +21 -21
- package/src/api.ts +27 -5
- package/src/components/create-component-form/utils/replace-element-with-component.ts +4 -0
- package/src/components/instance-editing-panel/instance-editing-panel.tsx +1 -1
- package/src/mcp/save-as-component-tool.ts +333 -29
- package/src/store/actions/create-unpublished-component.ts +9 -6
- package/src/store/store.ts +5 -1
- package/src/sync/create-components-before-save.ts +2 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-components",
|
|
3
3
|
"description": "Elementor editor components",
|
|
4
|
-
"version": "3.35.0-
|
|
4
|
+
"version": "3.35.0-357",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -40,29 +40,29 @@
|
|
|
40
40
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@elementor/editor": "3.35.0-
|
|
44
|
-
"@elementor/editor-canvas": "3.35.0-
|
|
45
|
-
"@elementor/editor-controls": "3.35.0-
|
|
46
|
-
"@elementor/editor-documents": "3.35.0-
|
|
47
|
-
"@elementor/editor-editing-panel": "3.35.0-
|
|
48
|
-
"@elementor/editor-elements": "3.35.0-
|
|
49
|
-
"@elementor/editor-elements-panel": "3.35.0-
|
|
50
|
-
"@elementor/editor-mcp": "3.35.0-
|
|
51
|
-
"@elementor/editor-panels": "3.35.0-
|
|
52
|
-
"@elementor/editor-props": "3.35.0-
|
|
53
|
-
"@elementor/editor-styles-repository": "3.35.0-
|
|
54
|
-
"@elementor/editor-ui": "3.35.0-
|
|
55
|
-
"@elementor/editor-v1-adapters": "3.35.0-
|
|
56
|
-
"@elementor/http-client": "3.35.0-
|
|
43
|
+
"@elementor/editor": "3.35.0-357",
|
|
44
|
+
"@elementor/editor-canvas": "3.35.0-357",
|
|
45
|
+
"@elementor/editor-controls": "3.35.0-357",
|
|
46
|
+
"@elementor/editor-documents": "3.35.0-357",
|
|
47
|
+
"@elementor/editor-editing-panel": "3.35.0-357",
|
|
48
|
+
"@elementor/editor-elements": "3.35.0-357",
|
|
49
|
+
"@elementor/editor-elements-panel": "3.35.0-357",
|
|
50
|
+
"@elementor/editor-mcp": "3.35.0-357",
|
|
51
|
+
"@elementor/editor-panels": "3.35.0-357",
|
|
52
|
+
"@elementor/editor-props": "3.35.0-357",
|
|
53
|
+
"@elementor/editor-styles-repository": "3.35.0-357",
|
|
54
|
+
"@elementor/editor-ui": "3.35.0-357",
|
|
55
|
+
"@elementor/editor-v1-adapters": "3.35.0-357",
|
|
56
|
+
"@elementor/http-client": "3.35.0-357",
|
|
57
57
|
"@elementor/icons": "^1.62.0",
|
|
58
|
-
"@elementor/mixpanel": "3.35.0-
|
|
59
|
-
"@elementor/query": "3.35.0-
|
|
60
|
-
"@elementor/schema": "3.35.0-
|
|
61
|
-
"@elementor/store": "3.35.0-
|
|
58
|
+
"@elementor/mixpanel": "3.35.0-357",
|
|
59
|
+
"@elementor/query": "3.35.0-357",
|
|
60
|
+
"@elementor/schema": "3.35.0-357",
|
|
61
|
+
"@elementor/store": "3.35.0-357",
|
|
62
62
|
"@elementor/ui": "1.36.17",
|
|
63
|
-
"@elementor/utils": "3.35.0-
|
|
63
|
+
"@elementor/utils": "3.35.0-357",
|
|
64
64
|
"@wordpress/i18n": "^5.13.0",
|
|
65
|
-
"@elementor/editor-notifications": "3.35.0-
|
|
65
|
+
"@elementor/editor-notifications": "3.35.0-357"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"react": "^18.3.1",
|
package/src/api.ts
CHANGED
|
@@ -6,13 +6,18 @@ import { type DocumentSaveStatus, type OverridableProps, type PublishedComponent
|
|
|
6
6
|
|
|
7
7
|
const BASE_URL = 'elementor/v1/components';
|
|
8
8
|
|
|
9
|
+
export type ComponentItems = Array< {
|
|
10
|
+
uid: string;
|
|
11
|
+
title: string;
|
|
12
|
+
elements: V1ElementData[];
|
|
13
|
+
settings?: {
|
|
14
|
+
overridable_props?: OverridableProps;
|
|
15
|
+
};
|
|
16
|
+
} >;
|
|
17
|
+
|
|
9
18
|
export type CreateComponentPayload = {
|
|
10
19
|
status: DocumentSaveStatus;
|
|
11
|
-
items:
|
|
12
|
-
uid: string;
|
|
13
|
-
title: string;
|
|
14
|
-
elements: V1ElementData[];
|
|
15
|
-
} >;
|
|
20
|
+
items: ComponentItems;
|
|
16
21
|
};
|
|
17
22
|
|
|
18
23
|
type ComponentLockStatusResponse = {
|
|
@@ -24,6 +29,19 @@ type GetComponentResponse = Array< PublishedComponent >;
|
|
|
24
29
|
|
|
25
30
|
export type CreateComponentResponse = Record< string, number >;
|
|
26
31
|
|
|
32
|
+
export type ValidateComponentsPayload = {
|
|
33
|
+
items: ComponentItems;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type ValidateComponentsResponse = {
|
|
37
|
+
code: string;
|
|
38
|
+
message: string;
|
|
39
|
+
data: {
|
|
40
|
+
status: number;
|
|
41
|
+
meta: Record< string, unknown >;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
27
45
|
export const getParams = ( id: number ) => ( {
|
|
28
46
|
action: 'get_document_config',
|
|
29
47
|
unique_id: `document-config-${ id }`,
|
|
@@ -86,4 +104,8 @@ export const apiClient = {
|
|
|
86
104
|
}
|
|
87
105
|
)
|
|
88
106
|
.then( ( res ) => res.data.data ),
|
|
107
|
+
validate: async ( payload: ValidateComponentsPayload ) =>
|
|
108
|
+
await httpService()
|
|
109
|
+
.post< HttpResponse< ValidateComponentsResponse > >( `${ BASE_URL }/create-validate`, payload )
|
|
110
|
+
.then( ( res ) => res.data ),
|
|
89
111
|
};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { replaceElement, type V1ElementData, type V1ElementModelProps } from '@elementor/editor-elements';
|
|
2
2
|
|
|
3
|
+
import { type OverridableProps } from '../../../types';
|
|
4
|
+
|
|
3
5
|
type ComponentInstanceParams = {
|
|
4
6
|
id?: number;
|
|
5
7
|
name: string;
|
|
6
8
|
uid: string;
|
|
9
|
+
overridableProps?: OverridableProps;
|
|
7
10
|
};
|
|
8
11
|
|
|
9
12
|
export const replaceElementWithComponent = ( element: V1ElementData, component: ComponentInstanceParams ) => {
|
|
@@ -28,6 +31,7 @@ export const createComponentModel = ( component: ComponentInstanceParams ): Omit
|
|
|
28
31
|
},
|
|
29
32
|
},
|
|
30
33
|
},
|
|
34
|
+
overridable_props: component.overridableProps,
|
|
31
35
|
},
|
|
32
36
|
editor_settings: {
|
|
33
37
|
title: component.name,
|
|
@@ -44,7 +44,7 @@ export function InstanceEditingPanel() {
|
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
46
|
<>
|
|
47
|
-
<PanelHeader sx={ { justifyContent: 'start' } }>
|
|
47
|
+
<PanelHeader sx={ { justifyContent: 'start', px: 2 } }>
|
|
48
48
|
<Stack direction="row" alignContent="space-between" flexGrow={ 1 }>
|
|
49
49
|
<Stack direction="row" alignItems="center" justifyContent="start" gap={ 1 } flexGrow={ 1 }>
|
|
50
50
|
<ComponentsIcon fontSize="small" sx={ { color: 'text.tertiary' } } />
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { DOCUMENT_STRUCTURE_URI, WIDGET_SCHEMA_URI } from '@elementor/editor-canvas';
|
|
2
|
+
import { getContainer, getElementType, getWidgetsCache, type V1ElementData } from '@elementor/editor-elements';
|
|
3
|
+
import { getMCPByDomain, toolPrompts } from '@elementor/editor-mcp';
|
|
4
|
+
import { type PropValue } from '@elementor/editor-props';
|
|
5
|
+
import { AxiosError } from '@elementor/http-client';
|
|
3
6
|
import { z } from '@elementor/schema';
|
|
7
|
+
import { generateUniqueId } from '@elementor/utils';
|
|
4
8
|
|
|
9
|
+
import { apiClient } from '../api';
|
|
5
10
|
import { createUnpublishedComponent } from '../store/actions/create-unpublished-component';
|
|
11
|
+
import { type OverridableProps } from '../types';
|
|
6
12
|
|
|
7
13
|
const InputSchema = {
|
|
8
14
|
element_id: z
|
|
@@ -14,6 +20,26 @@ const InputSchema = {
|
|
|
14
20
|
component_name: z
|
|
15
21
|
.string()
|
|
16
22
|
.describe( 'The name for the new component. Should be descriptive and unique among existing components.' ),
|
|
23
|
+
overridable_props: z
|
|
24
|
+
.object( {
|
|
25
|
+
props: z.record(
|
|
26
|
+
z.object( {
|
|
27
|
+
elementId: z
|
|
28
|
+
.string()
|
|
29
|
+
.describe( 'The id of the child element that you want to override its settings' ),
|
|
30
|
+
propKey: z
|
|
31
|
+
.string()
|
|
32
|
+
.describe(
|
|
33
|
+
'The property key of the child element that you want to override its settings (e.g., "text", "url", "tag"). To get the available propKeys for an element, use the "get-element-type-config" tool.'
|
|
34
|
+
),
|
|
35
|
+
} )
|
|
36
|
+
),
|
|
37
|
+
} )
|
|
38
|
+
.optional()
|
|
39
|
+
.describe(
|
|
40
|
+
'Overridable properties configuration. Specify which CHILD element properties can be customized. ' +
|
|
41
|
+
'Only elementId and propKey are required; To get the available propKeys for a child element you must use the "get-element-type-config" tool.'
|
|
42
|
+
),
|
|
17
43
|
};
|
|
18
44
|
|
|
19
45
|
const OutputSchema = {
|
|
@@ -24,16 +50,16 @@ const OutputSchema = {
|
|
|
24
50
|
.describe( 'The unique identifier of the newly created component (only present on success)' ),
|
|
25
51
|
};
|
|
26
52
|
|
|
27
|
-
export const VALID_ELEMENT_TYPES = [ 'e-div-block', 'e-flexbox', 'e-tabs' ];
|
|
28
53
|
export const ERROR_MESSAGES = {
|
|
29
54
|
ELEMENT_NOT_FOUND: "Element not found. Use 'list-elements' to get valid element IDs.",
|
|
30
|
-
ELEMENT_NOT_ONE_OF_TYPES:
|
|
55
|
+
ELEMENT_NOT_ONE_OF_TYPES: ( validTypes: string[] ) =>
|
|
56
|
+
`Element is not one of the following types: ${ validTypes.join( ', ' ) }`,
|
|
31
57
|
ELEMENT_IS_LOCKED: 'Cannot save a locked element as a component.',
|
|
32
58
|
};
|
|
33
59
|
|
|
34
60
|
export const handleSaveAsComponent = async ( params: z.infer< z.ZodObject< typeof InputSchema > > ) => {
|
|
35
|
-
const { element_id: elementId, component_name: componentName } = params;
|
|
36
|
-
|
|
61
|
+
const { element_id: elementId, component_name: componentName, overridable_props: overridablePropsInput } = params;
|
|
62
|
+
const validElementTypes = getValidElementTypes();
|
|
37
63
|
const container = getContainer( elementId );
|
|
38
64
|
|
|
39
65
|
if ( ! container ) {
|
|
@@ -42,8 +68,8 @@ export const handleSaveAsComponent = async ( params: z.infer< z.ZodObject< typeo
|
|
|
42
68
|
|
|
43
69
|
const elType = container.model.get( 'elType' );
|
|
44
70
|
|
|
45
|
-
if ( !
|
|
46
|
-
throw new Error( ERROR_MESSAGES.ELEMENT_NOT_ONE_OF_TYPES );
|
|
71
|
+
if ( ! validElementTypes.includes( elType ) ) {
|
|
72
|
+
throw new Error( ERROR_MESSAGES.ELEMENT_NOT_ONE_OF_TYPES( validElementTypes ) );
|
|
47
73
|
}
|
|
48
74
|
|
|
49
75
|
const element = container.model.toJSON( { remove: [ 'default' ] } ) as V1ElementData;
|
|
@@ -52,7 +78,30 @@ export const handleSaveAsComponent = async ( params: z.infer< z.ZodObject< typeo
|
|
|
52
78
|
throw new Error( ERROR_MESSAGES.ELEMENT_IS_LOCKED );
|
|
53
79
|
}
|
|
54
80
|
|
|
55
|
-
const
|
|
81
|
+
const overridableProps = overridablePropsInput
|
|
82
|
+
? enrichOverridableProps( overridablePropsInput, element )
|
|
83
|
+
: undefined;
|
|
84
|
+
|
|
85
|
+
if ( overridableProps ) {
|
|
86
|
+
updateElementDataWithOverridableProps( element, overridableProps );
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const uid = generateUniqueId( 'component' );
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
await apiClient.validate( {
|
|
93
|
+
items: [
|
|
94
|
+
{ uid, title: componentName, elements: [ element ], settings: { overridable_props: overridableProps } },
|
|
95
|
+
],
|
|
96
|
+
} );
|
|
97
|
+
} catch ( error: unknown ) {
|
|
98
|
+
if ( error instanceof AxiosError ) {
|
|
99
|
+
throw new Error( error.response?.data.messge );
|
|
100
|
+
}
|
|
101
|
+
throw new Error( 'Unknown error' );
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
createUnpublishedComponent( componentName, element, null, overridableProps, uid );
|
|
56
105
|
|
|
57
106
|
return {
|
|
58
107
|
status: 'ok' as const,
|
|
@@ -61,32 +110,287 @@ export const handleSaveAsComponent = async ( params: z.infer< z.ZodObject< typeo
|
|
|
61
110
|
};
|
|
62
111
|
};
|
|
63
112
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
113
|
+
function enrichOverridableProps(
|
|
114
|
+
input: { props: Record< string, { elementId: string; propKey: string } > },
|
|
115
|
+
rootElement: V1ElementData
|
|
116
|
+
): OverridableProps {
|
|
117
|
+
const enrichedProps: OverridableProps[ 'props' ] = {};
|
|
118
|
+
const defaultGroupId = generateUniqueId( 'group' );
|
|
119
|
+
|
|
120
|
+
Object.entries( input.props ).forEach( ( [ , prop ] ) => {
|
|
121
|
+
const { elementId, propKey } = prop;
|
|
122
|
+
const element = findElementById( rootElement, elementId );
|
|
123
|
+
|
|
124
|
+
if ( ! element ) {
|
|
125
|
+
throw new Error( `Element with ID "${ elementId }" not found in component` );
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const elType = element.elType;
|
|
129
|
+
const widgetType = element.widgetType || element.elType;
|
|
130
|
+
|
|
131
|
+
// Validate that the propKey exists in the element's schema
|
|
132
|
+
const elementType = getElementType( widgetType );
|
|
70
133
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
134
|
+
if ( ! elementType ) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`Element type "${ widgetType }" is not atomic or does not have a settings schema. ` +
|
|
137
|
+
`Cannot expose property "${ propKey }" for element "${ elementId }".`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
75
140
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
141
|
+
if ( ! elementType.propsSchema[ propKey ] ) {
|
|
142
|
+
const availableProps = Object.keys( elementType.propsSchema ).join( ', ' );
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Property "${ propKey }" does not exist in element "${ elementId }" (type: ${ widgetType }). ` +
|
|
145
|
+
`Available properties: ${ availableProps }`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
80
148
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
149
|
+
const overrideKey = generateUniqueId( 'prop' );
|
|
150
|
+
const originValue = element.settings?.[ propKey ]
|
|
151
|
+
? ( element.settings[ propKey ] as PropValue )
|
|
152
|
+
: elementType.propsSchema[ propKey ].default ?? null;
|
|
84
153
|
|
|
85
|
-
|
|
154
|
+
const label = generateLabel( propKey );
|
|
155
|
+
|
|
156
|
+
enrichedProps[ overrideKey ] = {
|
|
157
|
+
overrideKey,
|
|
158
|
+
label,
|
|
159
|
+
elementId,
|
|
160
|
+
propKey,
|
|
161
|
+
elType,
|
|
162
|
+
widgetType,
|
|
163
|
+
originValue,
|
|
164
|
+
groupId: defaultGroupId,
|
|
165
|
+
};
|
|
166
|
+
} );
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
props: enrichedProps,
|
|
170
|
+
groups: {
|
|
171
|
+
items: {
|
|
172
|
+
[ defaultGroupId ]: {
|
|
173
|
+
id: defaultGroupId,
|
|
174
|
+
label: 'Default',
|
|
175
|
+
props: Object.keys( enrichedProps ),
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
order: [ defaultGroupId ],
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function updateElementDataWithOverridableProps( rootElement: V1ElementData, overridableProps: OverridableProps ) {
|
|
184
|
+
Object.values( overridableProps.props ).forEach( ( prop ) => {
|
|
185
|
+
const element = findElementById( rootElement, prop.elementId );
|
|
186
|
+
|
|
187
|
+
if ( ! element || ! element.settings ) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
element.settings[ prop.propKey ] = {
|
|
192
|
+
$$type: 'overridable',
|
|
193
|
+
value: {
|
|
194
|
+
override_key: prop.overrideKey,
|
|
195
|
+
origin_value: prop.originValue,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
} );
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function findElementById( root: V1ElementData, targetId: string ): V1ElementData | null {
|
|
202
|
+
if ( root.id === targetId ) {
|
|
203
|
+
return root;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if ( root.elements ) {
|
|
207
|
+
for ( const child of root.elements ) {
|
|
208
|
+
const found = findElementById( child, targetId );
|
|
209
|
+
if ( found ) {
|
|
210
|
+
return found;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function generateLabel( propKey: string ): string {
|
|
219
|
+
const uniqueId = generateUniqueId( 'prop' );
|
|
220
|
+
return `${ uniqueId } - ${ propKey }`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function getValidElementTypes(): string[] {
|
|
224
|
+
const types = getWidgetsCache();
|
|
225
|
+
|
|
226
|
+
if ( ! types ) {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return Object.entries( types ).reduce( ( acc, [ type, value ] ) => {
|
|
231
|
+
if ( ! value.atomic_props_schema || ! value.show_in_panel || value.elType === 'widget' ) {
|
|
232
|
+
return acc;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
acc.push( type );
|
|
236
|
+
return acc;
|
|
237
|
+
}, [] as string[] );
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const generatePrompt = () => {
|
|
241
|
+
const saveAsComponentPrompt = toolPrompts( 'save-as-component' );
|
|
242
|
+
|
|
243
|
+
saveAsComponentPrompt.description( `
|
|
244
|
+
Save an existing element as a reusable component in the Elementor editor.
|
|
245
|
+
|
|
246
|
+
# When to use this tool
|
|
247
|
+
Use this tool when the user wants to:
|
|
248
|
+
- Create a reusable component from an existing element structure
|
|
249
|
+
- Make specific child element properties customizable in component instances
|
|
250
|
+
- Build a library of reusable design patterns
|
|
251
|
+
|
|
252
|
+
# When NOT to use this tool
|
|
253
|
+
- Element is already a component (widgetType: 'e-component')
|
|
254
|
+
- Element is locked
|
|
255
|
+
- Element is not an atomic element (atomic_props_schema is not defined)
|
|
256
|
+
- Element elType is a 'widget'
|
|
257
|
+
|
|
258
|
+
# **CRITICAL - REQUIRED RESOURCES (Must read before using this tool)**
|
|
259
|
+
1. [${ DOCUMENT_STRUCTURE_URI }]
|
|
260
|
+
**MANDATORY** - Required to understand the document structure and identify child elements for overridable properties.
|
|
261
|
+
Use this resource to find element IDs and understand the element hierarchy.
|
|
262
|
+
|
|
263
|
+
2. [${ WIDGET_SCHEMA_URI }]
|
|
264
|
+
**MANDATORY** - Required to understand which properties are available for each widget type.
|
|
265
|
+
Use this to identify available propKeys in the atomic_props_schema for child elements.
|
|
266
|
+
|
|
267
|
+
# Instructions - MUST FOLLOW IN ORDER
|
|
268
|
+
## Step 1: Identify the Target Element
|
|
269
|
+
1. Read the [${ DOCUMENT_STRUCTURE_URI }] resource to understand the document structure
|
|
270
|
+
2. Locate the element you want to save as a component by its element_id
|
|
271
|
+
3. Verify the element type is a valid element type
|
|
272
|
+
4. Ensure the element is not locked and is not already a component
|
|
273
|
+
|
|
274
|
+
## Step 2: Define Overridable Properties
|
|
275
|
+
Do this step to make child element properties customizable.
|
|
276
|
+
Skip that step ONLY if the user explicitly requests to not make any properties customizable.
|
|
277
|
+
|
|
278
|
+
1. **Identify Child Elements**
|
|
279
|
+
- Use the [${ DOCUMENT_STRUCTURE_URI }] resource to find all child elements
|
|
280
|
+
- Note the elementId and widgetType/elType of each child element you want to customize
|
|
281
|
+
|
|
282
|
+
2. **Find Available Properties**
|
|
283
|
+
- Use the [${ WIDGET_SCHEMA_URI }] resource to find the child element's widget type schema
|
|
284
|
+
- Review the atomic_props_schema to find available propKeys (ONLY use top-level props)
|
|
285
|
+
- Common propKeys include: "text", "url", "tag", "size", etc.
|
|
286
|
+
- Use only the top level properties, do not use nested properties.
|
|
287
|
+
|
|
288
|
+
3. **Build the overridable_props Object**
|
|
289
|
+
- For each property you want to make overridable, add an entry:
|
|
290
|
+
\`{ "elementId": "<child-element-id>", "propKey": "<property-key>" }\`
|
|
291
|
+
- Group all entries under the "props" object
|
|
292
|
+
|
|
293
|
+
## Step 3: Execute the Tool
|
|
294
|
+
Call the tool with:
|
|
295
|
+
- element_id: The ID of the parent element to save as component
|
|
296
|
+
- component_name: A descriptive name for the component
|
|
297
|
+
- overridable_props: (Optional) The properties configuration from Step 2
|
|
298
|
+
|
|
299
|
+
# CONSTRAINTS
|
|
300
|
+
- NEVER try to override properties of the parent element itself - ONLY child elements
|
|
301
|
+
- NEVER use invalid propKeys - always verify against the widget's atomic_props_schema in [${ WIDGET_SCHEMA_URI }]
|
|
302
|
+
- Property keys must exist in the child element's atomic_props_schema
|
|
303
|
+
- Element IDs must exist within the target element's children
|
|
304
|
+
- When tool execution fails, read the error message and adjust accordingly
|
|
305
|
+
- The element being saved must not be inside another component
|
|
306
|
+
` );
|
|
307
|
+
|
|
308
|
+
saveAsComponentPrompt.parameter(
|
|
309
|
+
'element_id',
|
|
310
|
+
`**MANDATORY** The unique identifier of the element to save as a component.
|
|
311
|
+
Use the [${ DOCUMENT_STRUCTURE_URI }] resource to find available element IDs.`
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
saveAsComponentPrompt.parameter(
|
|
315
|
+
'component_name',
|
|
316
|
+
`**MANDATORY** A descriptive name for the new component.
|
|
317
|
+
Should be unique and clearly describe the component's purpose (e.g., "Hero Section", "Feature Card").`
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
saveAsComponentPrompt.parameter(
|
|
321
|
+
'overridable_props',
|
|
322
|
+
`**OPTIONAL** Configuration for which child element properties can be customized in component instances.
|
|
323
|
+
|
|
324
|
+
Structure:
|
|
325
|
+
\`\`\`json
|
|
326
|
+
{
|
|
327
|
+
"props": {
|
|
328
|
+
"<unique-key>": {
|
|
329
|
+
"elementId": "<child-element-id>",
|
|
330
|
+
"propKey": "<property-key>"
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
\`\`\`
|
|
335
|
+
|
|
336
|
+
To populate this correctly:
|
|
337
|
+
1. Use [${ DOCUMENT_STRUCTURE_URI }] to find child element IDs and their widgetType
|
|
338
|
+
2. Use [${ WIDGET_SCHEMA_URI }] to find the atomic_props_schema for each child element's widgetType
|
|
339
|
+
3. Only include properties you want to be customizable in component instances
|
|
340
|
+
4. Common propKeys: "text", "url", "tag", "size", "align", etc.`
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
saveAsComponentPrompt.example( `
|
|
344
|
+
Basic component without overridable properties:
|
|
86
345
|
\`\`\`json
|
|
87
|
-
{
|
|
346
|
+
{
|
|
347
|
+
"element_id": "abc123",
|
|
348
|
+
"component_name": "Hero Section"
|
|
349
|
+
}
|
|
88
350
|
\`\`\`
|
|
89
|
-
|
|
351
|
+
|
|
352
|
+
Component with overridable properties:
|
|
353
|
+
\`\`\`json
|
|
354
|
+
{
|
|
355
|
+
"element_id": "abc123",
|
|
356
|
+
"component_name": "Feature Card",
|
|
357
|
+
"overridable_props": {
|
|
358
|
+
"props": {
|
|
359
|
+
"heading-text": {
|
|
360
|
+
"elementId": "heading-123",
|
|
361
|
+
"propKey": "text"
|
|
362
|
+
},
|
|
363
|
+
"button-text": {
|
|
364
|
+
"elementId": "button-456",
|
|
365
|
+
"propKey": "text"
|
|
366
|
+
},
|
|
367
|
+
"button-link": {
|
|
368
|
+
"elementId": "button-456",
|
|
369
|
+
"propKey": "url"
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
\`\`\`
|
|
375
|
+
` );
|
|
376
|
+
|
|
377
|
+
saveAsComponentPrompt.instruction(
|
|
378
|
+
`After successful creation, the component will be available in the components library and can be inserted into any page or template.`
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
saveAsComponentPrompt.instruction(
|
|
382
|
+
`When overridable properties are defined, component instances will show customization controls for those specific properties in the editing panel.`
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
return saveAsComponentPrompt.prompt();
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
export const initSaveAsComponentTool = () => {
|
|
389
|
+
return getMCPByDomain( 'components' ).addTool( {
|
|
390
|
+
name: 'save-as-component',
|
|
391
|
+
schema: InputSchema,
|
|
392
|
+
outputSchema: OutputSchema,
|
|
393
|
+
description: generatePrompt(),
|
|
90
394
|
handler: handleSaveAsComponent,
|
|
91
395
|
} );
|
|
92
396
|
};
|
|
@@ -5,16 +5,19 @@ import { generateUniqueId } from '@elementor/utils';
|
|
|
5
5
|
|
|
6
6
|
import { type ComponentEventData } from '../../components/create-component-form/utils/get-component-event-data';
|
|
7
7
|
import { replaceElementWithComponent } from '../../components/create-component-form/utils/replace-element-with-component';
|
|
8
|
+
import { type OverridableProps } from '../../types';
|
|
8
9
|
import { trackComponentEvent } from '../../utils/tracking';
|
|
9
10
|
import { slice } from '../store';
|
|
10
11
|
|
|
11
12
|
export function createUnpublishedComponent(
|
|
12
13
|
name: string,
|
|
13
14
|
element: V1ElementData,
|
|
14
|
-
eventData: ComponentEventData | null
|
|
15
|
+
eventData: ComponentEventData | null,
|
|
16
|
+
overridableProps?: OverridableProps,
|
|
17
|
+
uid?: string | null
|
|
15
18
|
) {
|
|
16
|
-
const
|
|
17
|
-
const componentBase = { uid, name };
|
|
19
|
+
const generatedUid = uid ?? generateUniqueId( 'component' );
|
|
20
|
+
const componentBase = { uid: generatedUid, name, overridableProps };
|
|
18
21
|
|
|
19
22
|
dispatch(
|
|
20
23
|
slice.actions.addUnpublished( {
|
|
@@ -23,18 +26,18 @@ export function createUnpublishedComponent(
|
|
|
23
26
|
} )
|
|
24
27
|
);
|
|
25
28
|
|
|
26
|
-
dispatch( slice.actions.addCreatedThisSession(
|
|
29
|
+
dispatch( slice.actions.addCreatedThisSession( generatedUid ) );
|
|
27
30
|
|
|
28
31
|
replaceElementWithComponent( element, componentBase );
|
|
29
32
|
|
|
30
33
|
trackComponentEvent( {
|
|
31
34
|
action: 'created',
|
|
32
|
-
component_uid:
|
|
35
|
+
component_uid: generatedUid,
|
|
33
36
|
component_name: name,
|
|
34
37
|
...eventData,
|
|
35
38
|
} );
|
|
36
39
|
|
|
37
40
|
runCommand( 'document/save/auto' );
|
|
38
41
|
|
|
39
|
-
return
|
|
42
|
+
return generatedUid;
|
|
40
43
|
}
|
package/src/store/store.ts
CHANGED
|
@@ -140,7 +140,11 @@ export const selectComponents = createSelector(
|
|
|
140
140
|
selectData,
|
|
141
141
|
selectUnpublishedData,
|
|
142
142
|
( data: PublishedComponent[], unpublishedData: UnpublishedComponent[] ) => [
|
|
143
|
-
...unpublishedData.map( ( item ) => ( {
|
|
143
|
+
...unpublishedData.map( ( item ) => ( {
|
|
144
|
+
uid: item.uid,
|
|
145
|
+
name: item.name,
|
|
146
|
+
overridableProps: item.overridableProps,
|
|
147
|
+
} ) ),
|
|
144
148
|
...data,
|
|
145
149
|
]
|
|
146
150
|
);
|
|
@@ -29,6 +29,7 @@ export async function createComponentsBeforeSave( {
|
|
|
29
29
|
id: uidToComponentId.get( component.uid ) as number,
|
|
30
30
|
name: component.name,
|
|
31
31
|
uid: component.uid,
|
|
32
|
+
overridableProps: component.overridableProps ? component.overridableProps : undefined,
|
|
32
33
|
} ) )
|
|
33
34
|
)
|
|
34
35
|
);
|
|
@@ -48,6 +49,7 @@ async function createComponents(
|
|
|
48
49
|
uid: component.uid,
|
|
49
50
|
title: component.name,
|
|
50
51
|
elements: component.elements,
|
|
52
|
+
settings: component.overridableProps ? { overridable_props: component.overridableProps } : undefined,
|
|
51
53
|
} ) ),
|
|
52
54
|
} );
|
|
53
55
|
|