@elementor/editor-variables 3.33.0-99 → 3.35.0-325
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 +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +1907 -810
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1854 -748
- package/dist/index.mjs.map +1 -1
- package/package.json +16 -14
- package/src/api.ts +24 -0
- package/src/batch-operations.ts +86 -0
- package/src/components/fields/color-field.tsx +1 -0
- package/src/components/fields/font-field.tsx +2 -1
- package/src/components/fields/label-field.tsx +42 -6
- package/src/components/ui/deleted-variable-alert.tsx +14 -10
- package/src/components/ui/{no-variables.tsx → empty-state.tsx} +8 -13
- package/src/components/ui/menu-item-content.tsx +14 -11
- package/src/components/ui/mismatch-variable-alert.tsx +5 -9
- package/src/components/ui/missing-variable-alert.tsx +8 -9
- package/src/components/ui/no-search-results.tsx +1 -2
- package/src/components/ui/tags/assigned-tag.tsx +6 -3
- package/src/components/ui/tags/warning-variable-tag.tsx +44 -0
- package/src/components/ui/variable/deleted-variable.tsx +13 -6
- package/src/components/ui/variable/mismatch-variable.tsx +11 -4
- package/src/components/ui/variable/missing-variable.tsx +2 -2
- package/src/components/variable-creation.tsx +13 -4
- package/src/components/variable-edit.tsx +12 -12
- package/src/components/variable-restore.tsx +3 -2
- package/src/components/variables-manager/hooks/use-auto-edit.ts +21 -0
- package/src/components/variables-manager/hooks/use-error-navigation.ts +49 -0
- package/src/components/variables-manager/hooks/use-variables-manager-state.ts +89 -0
- package/src/components/variables-manager/variable-editable-cell.tsx +131 -67
- package/src/components/variables-manager/variables-manager-create-menu.tsx +118 -0
- package/src/components/variables-manager/variables-manager-panel.tsx +290 -59
- package/src/components/variables-manager/variables-manager-table.tsx +137 -15
- package/src/components/variables-selection.tsx +61 -15
- package/src/controls/variable-control.tsx +1 -1
- package/src/hooks/use-prop-variables.ts +28 -9
- package/src/hooks/use-variable-bound-prop.ts +42 -0
- package/src/index.ts +1 -0
- package/src/init.ts +9 -6
- package/src/mcp/create-variable-tool.ts +70 -0
- package/src/mcp/delete-variable-tool.ts +50 -0
- package/src/mcp/index.ts +17 -0
- package/src/mcp/list-variables-tool.ts +58 -0
- package/src/mcp/update-variable-tool.ts +81 -0
- package/src/mcp/variables-resource.ts +28 -0
- package/src/register-variable-types.tsx +4 -0
- package/src/service.ts +60 -1
- package/src/storage.ts +8 -0
- package/src/types.ts +1 -0
- package/src/utils/filter-by-search.ts +5 -0
- package/src/utils/tracking.ts +37 -22
- package/src/utils/unlink-variable.ts +1 -1
- package/src/utils/validations.ts +72 -3
- package/src/variables-registry/create-variable-type-registry.ts +20 -8
- package/src/variables-registry/variable-type-registry.ts +2 -1
- package/src/components/ui/tags/deleted-tag.tsx +0 -37
- package/src/components/ui/tags/mismatch-tag.tsx +0 -37
- package/src/components/ui/tags/missing-tag.tsx +0 -25
- /package/src/components/variables-manager/{variable-edit-menu.tsx → ui/variable-edit-menu.tsx} +0 -0
- /package/src/components/variables-manager/{variable-table-cell.tsx → ui/variable-table-cell.tsx} +0 -0
package/src/mcp/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getMCPByDomain } from '@elementor/editor-mcp';
|
|
2
|
+
|
|
3
|
+
import { initCreateVariableTool } from './create-variable-tool';
|
|
4
|
+
import { initDeleteVariableTool } from './delete-variable-tool';
|
|
5
|
+
import { initListVariablesTool } from './list-variables-tool';
|
|
6
|
+
import { initUpdateVariableTool } from './update-variable-tool';
|
|
7
|
+
import { initVariablesResource } from './variables-resource';
|
|
8
|
+
|
|
9
|
+
export function initMcp() {
|
|
10
|
+
const { setMCPDescription } = getMCPByDomain( 'variables' );
|
|
11
|
+
setMCPDescription( `Elementor Editor Variables MCP` );
|
|
12
|
+
initListVariablesTool();
|
|
13
|
+
initCreateVariableTool();
|
|
14
|
+
initUpdateVariableTool();
|
|
15
|
+
initDeleteVariableTool();
|
|
16
|
+
initVariablesResource();
|
|
17
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getMCPByDomain } from '@elementor/editor-mcp';
|
|
2
|
+
import { z } from '@elementor/schema';
|
|
3
|
+
|
|
4
|
+
import { service } from '../service';
|
|
5
|
+
import { type TVariable } from '../storage';
|
|
6
|
+
|
|
7
|
+
const VariableSchema = {
|
|
8
|
+
type: z.string().describe( 'The type of the variable.' ),
|
|
9
|
+
label: z.string().describe( 'The label of the variable, displayed to the user' ),
|
|
10
|
+
value: z.string().describe( 'The value of the variable.' ),
|
|
11
|
+
id: z
|
|
12
|
+
.string()
|
|
13
|
+
.describe(
|
|
14
|
+
'The unique identifier of the variable. Used for internal reference, not to be exposed to end users'
|
|
15
|
+
),
|
|
16
|
+
};
|
|
17
|
+
const VariableListSchema = {
|
|
18
|
+
variables: z.array( z.object( VariableSchema ) ).describe( 'List of variables' ),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const initListVariablesTool = () => {
|
|
22
|
+
getMCPByDomain( 'variables' ).addTool( {
|
|
23
|
+
name: 'list-global-variables',
|
|
24
|
+
description: `List editor global variables
|
|
25
|
+
|
|
26
|
+
## When to use this tool:
|
|
27
|
+
- When a user requests to see all available global variables in the Elementor editor.
|
|
28
|
+
- When you need to be exact on a variable label, to avoid any mistakes.
|
|
29
|
+
- When you want to see the most up-to-date list of global variables.
|
|
30
|
+
- Before using any other variables related tools that makes changes, such as deletion, creation, or updates. This ensures you have the latest information and there is no naming collision or mismatching.
|
|
31
|
+
|
|
32
|
+
## Example tool response (JSON format):
|
|
33
|
+
\`\`\`json
|
|
34
|
+
{ variables: [
|
|
35
|
+
{ type: 'global-color-variable', label: 'Cool', value: 'rgb(1,2,3)', id: 'some-unique-id' },
|
|
36
|
+
{ type: 'global-font-variable', label: 'Headline', value: 'serif', id: 'some-other-unique-id' },
|
|
37
|
+
] }
|
|
38
|
+
\`\`\`
|
|
39
|
+
|
|
40
|
+
Once you get the response, please display the variables in a user-friendly way, unless explicitly requested otherwise.
|
|
41
|
+
Unless explicitly requested otherwise, response in HTML Format, prefer to use tables or unordered lists.
|
|
42
|
+
|
|
43
|
+
Note: **The label is most improtant to be seen as-is without any changes.**
|
|
44
|
+
|
|
45
|
+
<important>
|
|
46
|
+
**Do not omit the label**. This is important for the user to identify the variable.
|
|
47
|
+
**Do not change the label**, it must be displayed exactly as it is, in it's original characters as received from this tool.
|
|
48
|
+
</important>
|
|
49
|
+
`,
|
|
50
|
+
outputSchema: VariableListSchema,
|
|
51
|
+
handler: async () => {
|
|
52
|
+
const variables = service.variables() as Record< string, TVariable >;
|
|
53
|
+
return {
|
|
54
|
+
variables: Object.entries( variables ).map( ( [ id, varData ] ) => ( { id, ...varData } ) ),
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
} );
|
|
58
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { getMCPByDomain } from '@elementor/editor-mcp';
|
|
2
|
+
import { z } from '@elementor/schema';
|
|
3
|
+
|
|
4
|
+
import { service } from '../service';
|
|
5
|
+
|
|
6
|
+
export const initUpdateVariableTool = () => {
|
|
7
|
+
getMCPByDomain( 'variables' ).addTool( {
|
|
8
|
+
schema: {
|
|
9
|
+
id: z.string().describe( 'The unique identifier of the variable to be updated or renamed.' ),
|
|
10
|
+
label: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe(
|
|
13
|
+
'The label of the variable to be stored after the change. If the user only wishes to update the value, this must be strictly equal to the current label.'
|
|
14
|
+
),
|
|
15
|
+
value: z
|
|
16
|
+
.string()
|
|
17
|
+
.describe(
|
|
18
|
+
"The new value for the variable. For color variables, this should be a valid CSS color (e.g., 'rgb(255,0,0)', '#ff0000', 'red'). For font variables, this should be a valid font family (e.g., 'Arial', 'serif'). If the user wishes to rename only, make sure you provide the existing value."
|
|
19
|
+
),
|
|
20
|
+
},
|
|
21
|
+
outputSchema: {
|
|
22
|
+
status: z.enum( [ 'ok', 'error' ] ).describe( 'The status of the operation' ),
|
|
23
|
+
message: z
|
|
24
|
+
.string()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe( 'Optional message providing additional information about the operation' ),
|
|
27
|
+
},
|
|
28
|
+
name: 'update-global-variable',
|
|
29
|
+
description: `Update an existing global variable
|
|
30
|
+
|
|
31
|
+
## When to use this tool:
|
|
32
|
+
- When a user requests to update an existing global variable in the Elementor editor.
|
|
33
|
+
- When you need to modify the value of an existing variable.
|
|
34
|
+
- When you want to rename an existing variable (change its label).
|
|
35
|
+
- When you want to both rename and modify the value of an existing variable.
|
|
36
|
+
|
|
37
|
+
## Prerequisites:
|
|
38
|
+
- Ensure you have the most up-to-date list of existing global variables to avoid label duplication. You can use the "list-global-variables" tool to fetch the current variables.
|
|
39
|
+
- Make sure when updating a variable, the new label is unique and not already in use by another variable.
|
|
40
|
+
- Make sure you understand whether you are updating a value, renaming, or both.
|
|
41
|
+
- Reference the variable by the "id" property, given from the "list-global-variables" tool.
|
|
42
|
+
- If the user wishes to rename, make sure you have the existing value.
|
|
43
|
+
- If the user wishes to update the value, make sure you have to **correct label**.
|
|
44
|
+
- You must have the unique identifier, the current label, the current value, and the new value or label or both, before using this tool.
|
|
45
|
+
|
|
46
|
+
## Required parameters:
|
|
47
|
+
- id: The unique identifier of the variable to be updated or renamed.
|
|
48
|
+
- label: The label of the variable to be stored after the change. If the user only wishes to update the value, this must be strictly equal to the current label.
|
|
49
|
+
- value: The new value for the variable. For color variables, this should be a valid CSS color (e.g., 'rgb(255,0,0)', '#ff0000', 'red'). For font variables, this should be a valid font family (e.g., 'Arial', 'serif'). If the user wishes to rename only, make sure you provide the existing value.
|
|
50
|
+
|
|
51
|
+
## Example tool call (JSON format):
|
|
52
|
+
\`\`\`json
|
|
53
|
+
{ "id": "some-unique-id", "label": "Cool", "value": "rgb(0,140,250)" }
|
|
54
|
+
\`\`\`
|
|
55
|
+
|
|
56
|
+
## Example responses (JSON format):
|
|
57
|
+
Successful update:
|
|
58
|
+
\`\`\`json
|
|
59
|
+
{ "status": "ok" }
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
Failed update, which must be displayed to the end user. If the error message is not plain, attempt to find the most useful part of the message and display it.
|
|
63
|
+
\`\`\`json
|
|
64
|
+
{ "status": "error", "message": "Label 'Cool' is already in use by another variable." }
|
|
65
|
+
\`\`\`
|
|
66
|
+
`,
|
|
67
|
+
handler: async ( params ) => {
|
|
68
|
+
const { id, label, value } = params;
|
|
69
|
+
try {
|
|
70
|
+
await service.update( id, { label, value } );
|
|
71
|
+
return { status: 'ok' };
|
|
72
|
+
} catch ( error ) {
|
|
73
|
+
const message: string = ( error as Error ).message || 'Unknown server error';
|
|
74
|
+
return {
|
|
75
|
+
status: 'error',
|
|
76
|
+
message: `There was an error creating the variable: ${ message }`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
} );
|
|
81
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { getMCPByDomain } from '@elementor/editor-mcp';
|
|
2
|
+
|
|
3
|
+
export const GLOBAL_VARIABLES_URI = 'elementor://variables';
|
|
4
|
+
|
|
5
|
+
export const initVariablesResource = () => {
|
|
6
|
+
const { mcpServer } = getMCPByDomain( 'variables' );
|
|
7
|
+
|
|
8
|
+
mcpServer.resource(
|
|
9
|
+
'global-variables',
|
|
10
|
+
GLOBAL_VARIABLES_URI,
|
|
11
|
+
{
|
|
12
|
+
description:
|
|
13
|
+
'Global variables list. Variables are being used in this way: If it is directly in the schema, you need to put the ID which is the key inside the object.',
|
|
14
|
+
},
|
|
15
|
+
async () => {
|
|
16
|
+
return {
|
|
17
|
+
contents: [ { uri: GLOBAL_VARIABLES_URI, text: localStorage[ 'elementor-global-variables' ] } ],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
window.addEventListener( 'variables:updated', () => {
|
|
23
|
+
mcpServer.server.sendResourceUpdated( {
|
|
24
|
+
uri: GLOBAL_VARIABLES_URI,
|
|
25
|
+
contents: [ { uri: GLOBAL_VARIABLES_URI, text: localStorage[ 'elementor-global-variables' ] } ],
|
|
26
|
+
} );
|
|
27
|
+
} );
|
|
28
|
+
};
|
|
@@ -11,19 +11,23 @@ import { registerVariableType } from './variables-registry/variable-type-registr
|
|
|
11
11
|
|
|
12
12
|
export function registerVariableTypes() {
|
|
13
13
|
registerVariableType( {
|
|
14
|
+
key: colorVariablePropTypeUtil.key,
|
|
14
15
|
valueField: ColorField,
|
|
15
16
|
icon: BrushIcon,
|
|
16
17
|
propTypeUtil: colorVariablePropTypeUtil,
|
|
17
18
|
fallbackPropTypeUtil: colorPropTypeUtil,
|
|
18
19
|
variableType: 'color',
|
|
19
20
|
startIcon: ( { value } ) => <ColorIndicator size="inherit" component="span" value={ value } />,
|
|
21
|
+
defaultValue: '#ffffff',
|
|
20
22
|
} );
|
|
21
23
|
|
|
22
24
|
registerVariableType( {
|
|
25
|
+
key: fontVariablePropTypeUtil.key,
|
|
23
26
|
valueField: FontField,
|
|
24
27
|
icon: TextIcon,
|
|
25
28
|
propTypeUtil: fontVariablePropTypeUtil,
|
|
26
29
|
fallbackPropTypeUtil: stringPropTypeUtil,
|
|
27
30
|
variableType: 'font',
|
|
31
|
+
defaultValue: 'Roboto',
|
|
28
32
|
} );
|
|
29
33
|
}
|
package/src/service.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { __ } from '@wordpress/i18n';
|
|
2
2
|
|
|
3
3
|
import { apiClient } from './api';
|
|
4
|
+
import { buildOperationsArray, type OperationResult } from './batch-operations';
|
|
4
5
|
import { OP_RW, Storage, type TVariablesList } from './storage';
|
|
5
6
|
import { styleVariablesRepository } from './style-variables-repository';
|
|
6
7
|
import { type Variable } from './types';
|
|
@@ -12,8 +13,12 @@ export const service = {
|
|
|
12
13
|
return storage.load();
|
|
13
14
|
},
|
|
14
15
|
|
|
16
|
+
getWatermark: (): number => {
|
|
17
|
+
return storage.state.watermark;
|
|
18
|
+
},
|
|
19
|
+
|
|
15
20
|
init: () => {
|
|
16
|
-
service.load();
|
|
21
|
+
return service.load();
|
|
17
22
|
},
|
|
18
23
|
|
|
19
24
|
load: () => {
|
|
@@ -168,6 +173,60 @@ export const service = {
|
|
|
168
173
|
};
|
|
169
174
|
} );
|
|
170
175
|
},
|
|
176
|
+
|
|
177
|
+
batchSave: ( originalVariables: TVariablesList, currentVariables: TVariablesList ) => {
|
|
178
|
+
const operations = buildOperationsArray( originalVariables, currentVariables );
|
|
179
|
+
const batchPayload = { operations, watermark: storage.state.watermark };
|
|
180
|
+
|
|
181
|
+
if ( operations.length === 0 ) {
|
|
182
|
+
return Promise.resolve( {
|
|
183
|
+
success: true,
|
|
184
|
+
watermark: storage.state.watermark,
|
|
185
|
+
operations: 0,
|
|
186
|
+
} );
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return apiClient
|
|
190
|
+
.batch( batchPayload )
|
|
191
|
+
.then( ( response ) => {
|
|
192
|
+
const { success, data: payload } = response.data;
|
|
193
|
+
|
|
194
|
+
if ( ! success ) {
|
|
195
|
+
throw new Error( 'Unexpected response from server' );
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return payload;
|
|
199
|
+
} )
|
|
200
|
+
.then( ( data ) => {
|
|
201
|
+
const { results, watermark } = data;
|
|
202
|
+
|
|
203
|
+
handleWatermark( OP_RW, watermark );
|
|
204
|
+
|
|
205
|
+
if ( results ) {
|
|
206
|
+
results.forEach( ( result: OperationResult ) => {
|
|
207
|
+
if ( result.variable ) {
|
|
208
|
+
const { id: variableId, ...variableData } = result.variable;
|
|
209
|
+
|
|
210
|
+
if ( result.type === 'create' ) {
|
|
211
|
+
storage.add( variableId, variableData );
|
|
212
|
+
} else {
|
|
213
|
+
storage.update( variableId, variableData );
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
styleVariablesRepository.update( {
|
|
217
|
+
[ variableId ]: variableData,
|
|
218
|
+
} );
|
|
219
|
+
}
|
|
220
|
+
} );
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
success: true,
|
|
225
|
+
watermark,
|
|
226
|
+
operations: operations.length,
|
|
227
|
+
};
|
|
228
|
+
} );
|
|
229
|
+
},
|
|
171
230
|
};
|
|
172
231
|
|
|
173
232
|
const handleWatermark = ( operation: string, newWatermark: number ) => {
|
package/src/storage.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type TVariable = {
|
|
|
2
2
|
type: string;
|
|
3
3
|
label: string;
|
|
4
4
|
value: string;
|
|
5
|
+
order?: number;
|
|
5
6
|
deleted?: boolean;
|
|
6
7
|
deleted_at?: string;
|
|
7
8
|
};
|
|
@@ -20,6 +21,10 @@ export class Storage {
|
|
|
20
21
|
variables: TVariablesList;
|
|
21
22
|
};
|
|
22
23
|
|
|
24
|
+
notifyChange() {
|
|
25
|
+
window.dispatchEvent( new Event( 'variables:updated' ) );
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
constructor() {
|
|
24
29
|
this.state = {
|
|
25
30
|
watermark: -1,
|
|
@@ -43,18 +48,21 @@ export class Storage {
|
|
|
43
48
|
|
|
44
49
|
localStorage.setItem( STORAGE_WATERMARK_KEY, this.state.watermark.toString() );
|
|
45
50
|
localStorage.setItem( STORAGE_KEY, JSON.stringify( this.state.variables ) );
|
|
51
|
+
this.notifyChange();
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
add( id: string, variable: TVariable ) {
|
|
49
55
|
this.load();
|
|
50
56
|
this.state.variables[ id ] = variable;
|
|
51
57
|
localStorage.setItem( STORAGE_KEY, JSON.stringify( this.state.variables ) );
|
|
58
|
+
this.notifyChange();
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
update( id: string, variable: TVariable ) {
|
|
55
62
|
this.load();
|
|
56
63
|
this.state.variables[ id ] = variable;
|
|
57
64
|
localStorage.setItem( STORAGE_KEY, JSON.stringify( this.state.variables ) );
|
|
65
|
+
this.notifyChange();
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
watermark( watermark: number ) {
|
package/src/types.ts
CHANGED
package/src/utils/tracking.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getMixpanel } from '@elementor/mixpanel';
|
|
2
|
+
|
|
1
3
|
type VariableEventData = {
|
|
2
4
|
varType: string;
|
|
3
5
|
controlPath: string;
|
|
@@ -5,35 +7,48 @@ type VariableEventData = {
|
|
|
5
7
|
};
|
|
6
8
|
|
|
7
9
|
export const trackVariableEvent = ( { varType, controlPath, action }: VariableEventData ) => {
|
|
8
|
-
const
|
|
9
|
-
elementorCommon?: {
|
|
10
|
-
eventsManager?: {
|
|
11
|
-
dispatchEvent: ( name: string, data: Record< string, string > ) => void;
|
|
12
|
-
config?: {
|
|
13
|
-
locations: Record< string, string >;
|
|
14
|
-
secondaryLocations: Record< string, string >;
|
|
15
|
-
names: {
|
|
16
|
-
variables?: Record< string, string >;
|
|
17
|
-
};
|
|
18
|
-
triggers: Record< string, string >;
|
|
19
|
-
elements?: Record< string, string >;
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const config = extendedWindow?.elementorCommon?.eventsManager?.config;
|
|
10
|
+
const { dispatchEvent, config } = getMixpanel();
|
|
26
11
|
if ( ! config?.names?.variables?.[ action ] ) {
|
|
27
12
|
return;
|
|
28
13
|
}
|
|
29
14
|
|
|
30
15
|
const name = config.names.variables[ action ];
|
|
31
|
-
|
|
32
|
-
location: config
|
|
33
|
-
secondaryLocation: config
|
|
34
|
-
trigger: config
|
|
16
|
+
dispatchEvent?.( name, {
|
|
17
|
+
location: config?.locations?.variables || '',
|
|
18
|
+
secondaryLocation: config?.secondaryLocations?.variablesPopover || '',
|
|
19
|
+
trigger: config?.triggers?.click || '',
|
|
35
20
|
var_type: varType,
|
|
36
21
|
control_path: controlPath,
|
|
37
22
|
action_type: name,
|
|
38
23
|
} );
|
|
39
24
|
};
|
|
25
|
+
|
|
26
|
+
type VariablesManagerEventData = {
|
|
27
|
+
action: 'openManager' | 'add' | 'saveChanges' | 'delete';
|
|
28
|
+
varType?: string;
|
|
29
|
+
controlPath?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const trackVariablesManagerEvent = ( { action, varType, controlPath }: VariablesManagerEventData ) => {
|
|
33
|
+
const { dispatchEvent, config } = getMixpanel();
|
|
34
|
+
if ( ! config?.names?.variables?.[ action ] ) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const name = config.names.variables[ action ];
|
|
39
|
+
const eventData: Record< string, string > = {
|
|
40
|
+
location: config?.locations?.variablesManager || '',
|
|
41
|
+
trigger: config?.triggers?.click || '',
|
|
42
|
+
action_type: name,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
if ( varType ) {
|
|
46
|
+
eventData.var_type = varType;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if ( controlPath ) {
|
|
50
|
+
eventData.style_control_path = controlPath;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
dispatchEvent?.( name, eventData );
|
|
54
|
+
};
|
|
@@ -7,7 +7,7 @@ export function transformValueBeforeUnlink( variable: Variable, propTypeKey: str
|
|
|
7
7
|
const { valueTransformer } = getVariableType( propTypeKey );
|
|
8
8
|
|
|
9
9
|
if ( valueTransformer ) {
|
|
10
|
-
return valueTransformer( variable
|
|
10
|
+
return valueTransformer( variable );
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
return variable.value;
|
package/src/utils/validations.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type * as React from 'react';
|
|
2
|
+
import { AlertTriangleFilledIcon, InfoCircleFilledIcon } from '@elementor/icons';
|
|
3
|
+
import { __, sprintf } from '@wordpress/i18n';
|
|
4
|
+
|
|
5
|
+
import { type TVariable, type TVariablesList } from '../storage';
|
|
2
6
|
|
|
3
7
|
export const ERROR_MESSAGES = {
|
|
4
8
|
MISSING_VARIABLE_NAME: __( 'Give your variable a name.', 'elementor' ),
|
|
@@ -8,21 +12,49 @@ export const ERROR_MESSAGES = {
|
|
|
8
12
|
VARIABLE_LABEL_MAX_LENGTH: __( 'Keep names up to 50 characters.', 'elementor' ),
|
|
9
13
|
DUPLICATED_LABEL: __( 'This variable name already exists. Please choose a unique name.', 'elementor' ),
|
|
10
14
|
UNEXPECTED_ERROR: __( 'There was a glitch. Try saving your variable again.', 'elementor' ),
|
|
15
|
+
BATCH: {
|
|
16
|
+
DUPLICATED_LABELS: ( count: number, name: string ) =>
|
|
17
|
+
// eslint-disable-next-line @wordpress/i18n-translator-comments
|
|
18
|
+
sprintf( __( 'We found %1$d duplicated %2$s.', 'elementor' ), count, name ),
|
|
19
|
+
UNEXPECTED_ERROR: __( 'There was a glitch.', 'elementor' ),
|
|
20
|
+
DUPLICATED_LABEL_ACTION: __( 'Take me there', 'elementor' ),
|
|
21
|
+
DUPLICATED_LABEL_ACTION_MESSAGE: __( 'Please rename the variables.', 'elementor' ),
|
|
22
|
+
UNEXPECTED_ERROR_ACTION_MESSAGE: __( 'Try saving your variables again.', 'elementor' ),
|
|
23
|
+
},
|
|
11
24
|
} as const;
|
|
12
25
|
|
|
13
26
|
export const VARIABLE_LABEL_MAX_LENGTH = 50;
|
|
14
27
|
|
|
15
|
-
type
|
|
28
|
+
type BatchErrorData = {
|
|
29
|
+
[ id: string ]: {
|
|
30
|
+
status?: number;
|
|
31
|
+
message?: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export type ErrorResponse = {
|
|
16
35
|
response?: {
|
|
17
36
|
data?: {
|
|
18
37
|
code?: string;
|
|
38
|
+
data?: BatchErrorData;
|
|
19
39
|
};
|
|
20
40
|
};
|
|
21
41
|
};
|
|
22
42
|
|
|
43
|
+
export type ErrorAction = {
|
|
44
|
+
label?: string;
|
|
45
|
+
message?: string;
|
|
46
|
+
callback?: () => void;
|
|
47
|
+
data?: {
|
|
48
|
+
duplicatedIds?: string[];
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
23
52
|
export type MappedError = {
|
|
24
53
|
field: string;
|
|
25
54
|
message: string;
|
|
55
|
+
action?: ErrorAction;
|
|
56
|
+
severity?: 'error' | 'secondary';
|
|
57
|
+
IconComponent?: React.ElementType;
|
|
26
58
|
};
|
|
27
59
|
|
|
28
60
|
export const mapServerError = ( error: ErrorResponse ): MappedError | undefined => {
|
|
@@ -33,10 +65,43 @@ export const mapServerError = ( error: ErrorResponse ): MappedError | undefined
|
|
|
33
65
|
};
|
|
34
66
|
}
|
|
35
67
|
|
|
68
|
+
if ( error?.response?.data?.code === 'batch_duplicated_label' ) {
|
|
69
|
+
const errorData = error?.response?.data?.data ?? {};
|
|
70
|
+
const count = Object.keys( errorData ).length;
|
|
71
|
+
const name = count === 1 ? 'name' : 'names';
|
|
72
|
+
const duplicatedIds = Object.keys( errorData );
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
field: 'label',
|
|
76
|
+
message: ERROR_MESSAGES.BATCH.DUPLICATED_LABELS( count, name ),
|
|
77
|
+
severity: 'error',
|
|
78
|
+
IconComponent: AlertTriangleFilledIcon,
|
|
79
|
+
action: {
|
|
80
|
+
label: ERROR_MESSAGES.BATCH.DUPLICATED_LABEL_ACTION,
|
|
81
|
+
message: ERROR_MESSAGES.BATCH.DUPLICATED_LABEL_ACTION_MESSAGE,
|
|
82
|
+
data: {
|
|
83
|
+
duplicatedIds,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if ( error?.response?.data?.code === 'batch_operation_failed' ) {
|
|
90
|
+
return {
|
|
91
|
+
field: 'label',
|
|
92
|
+
message: ERROR_MESSAGES.BATCH.UNEXPECTED_ERROR,
|
|
93
|
+
severity: 'secondary',
|
|
94
|
+
IconComponent: InfoCircleFilledIcon,
|
|
95
|
+
action: {
|
|
96
|
+
message: ERROR_MESSAGES.BATCH.UNEXPECTED_ERROR_ACTION_MESSAGE,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
36
101
|
return undefined;
|
|
37
102
|
};
|
|
38
103
|
|
|
39
|
-
export const validateLabel = ( name: string ): string => {
|
|
104
|
+
export const validateLabel = ( name: string, variables?: TVariablesList ): string => {
|
|
40
105
|
if ( ! name.trim() ) {
|
|
41
106
|
return ERROR_MESSAGES.MISSING_VARIABLE_NAME;
|
|
42
107
|
}
|
|
@@ -55,6 +120,10 @@ export const validateLabel = ( name: string ): string => {
|
|
|
55
120
|
return ERROR_MESSAGES.VARIABLE_LABEL_MAX_LENGTH;
|
|
56
121
|
}
|
|
57
122
|
|
|
123
|
+
if ( Object.values( variables ?? {} ).some( ( variable: TVariable ) => variable.label === name ) ) {
|
|
124
|
+
return ERROR_MESSAGES.DUPLICATED_LABEL;
|
|
125
|
+
}
|
|
126
|
+
|
|
58
127
|
return '';
|
|
59
128
|
};
|
|
60
129
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ForwardRefExoticComponent, type JSX, type RefAttributes } from 'react';
|
|
1
|
+
import { type ForwardRefExoticComponent, type JSX, type RefAttributes, type RefObject } from 'react';
|
|
2
2
|
import { styleTransformersRegistry } from '@elementor/editor-canvas';
|
|
3
3
|
import { stylesInheritanceTransformersRegistry } from '@elementor/editor-editing-panel';
|
|
4
4
|
import {
|
|
@@ -17,8 +17,12 @@ import { type NormalizedVariable, type Variable } from '../types';
|
|
|
17
17
|
export type ValueFieldProps = {
|
|
18
18
|
value: string;
|
|
19
19
|
onChange: ( value: string ) => void;
|
|
20
|
+
onPropTypeKeyChange?: ( key: string ) => void;
|
|
21
|
+
propTypeKey?: string;
|
|
20
22
|
onValidationChange?: ( value: string ) => void;
|
|
21
23
|
propType?: PropType;
|
|
24
|
+
error?: { value: string; message: string };
|
|
25
|
+
ref?: RefObject< HTMLElement | null >;
|
|
22
26
|
};
|
|
23
27
|
|
|
24
28
|
type FallbackPropTypeUtil = ReturnType< typeof createPropUtils >;
|
|
@@ -26,34 +30,36 @@ type FallbackPropTypeUtil = ReturnType< typeof createPropUtils >;
|
|
|
26
30
|
type VariableTypeOptions = {
|
|
27
31
|
icon: ForwardRefExoticComponent< Omit< SvgIconProps, 'ref' > & RefAttributes< SVGSVGElement > >;
|
|
28
32
|
startIcon?: ( { value }: { value: string } ) => JSX.Element;
|
|
29
|
-
valueField: (
|
|
33
|
+
valueField: ( props: ValueFieldProps ) => JSX.Element;
|
|
30
34
|
variableType: string;
|
|
35
|
+
key?: string;
|
|
36
|
+
defaultValue?: string;
|
|
31
37
|
fallbackPropTypeUtil: FallbackPropTypeUtil;
|
|
32
38
|
propTypeUtil: PropTypeUtil< string, string >;
|
|
33
39
|
selectionFilter?: ( variables: NormalizedVariable[], propType: PropType ) => NormalizedVariable[];
|
|
34
|
-
valueTransformer?: (
|
|
40
|
+
valueTransformer?: ( variable: Variable ) => PropValue;
|
|
35
41
|
isCompatible?: ( propType: PropType, variable: Variable ) => boolean;
|
|
36
42
|
};
|
|
37
43
|
|
|
38
|
-
export type VariableTypesMap = Record< string, VariableTypeOptions >;
|
|
44
|
+
export type VariableTypesMap = Record< string, Omit< VariableTypeOptions, 'key' > >;
|
|
39
45
|
|
|
40
46
|
export function createVariableTypeRegistry() {
|
|
41
47
|
const variableTypes: VariableTypesMap = {};
|
|
42
48
|
|
|
43
49
|
const registerVariableType = ( {
|
|
50
|
+
key,
|
|
44
51
|
icon,
|
|
45
52
|
startIcon,
|
|
46
53
|
valueField,
|
|
47
54
|
propTypeUtil,
|
|
48
55
|
variableType,
|
|
56
|
+
defaultValue,
|
|
49
57
|
selectionFilter,
|
|
50
58
|
valueTransformer,
|
|
51
59
|
fallbackPropTypeUtil,
|
|
52
60
|
isCompatible,
|
|
53
61
|
}: VariableTypeOptions ) => {
|
|
54
|
-
|
|
55
|
-
throw new Error( `Variable with key "${ propTypeUtil.key }" is already registered.` );
|
|
56
|
-
}
|
|
62
|
+
const variableTypeKey = key ?? propTypeUtil.key;
|
|
57
63
|
|
|
58
64
|
if ( ! isCompatible ) {
|
|
59
65
|
isCompatible = ( propType, variable: Variable ) => {
|
|
@@ -66,12 +72,13 @@ export function createVariableTypeRegistry() {
|
|
|
66
72
|
};
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
variableTypes[
|
|
75
|
+
variableTypes[ variableTypeKey ] = {
|
|
70
76
|
icon,
|
|
71
77
|
startIcon,
|
|
72
78
|
valueField,
|
|
73
79
|
propTypeUtil,
|
|
74
80
|
variableType,
|
|
81
|
+
defaultValue,
|
|
75
82
|
selectionFilter,
|
|
76
83
|
valueTransformer,
|
|
77
84
|
fallbackPropTypeUtil,
|
|
@@ -94,6 +101,10 @@ export function createVariableTypeRegistry() {
|
|
|
94
101
|
return variableTypes[ key ];
|
|
95
102
|
};
|
|
96
103
|
|
|
104
|
+
const getVariableTypes = () => {
|
|
105
|
+
return variableTypes;
|
|
106
|
+
};
|
|
107
|
+
|
|
97
108
|
const hasVariableType = ( key: string ) => {
|
|
98
109
|
return key in variableTypes;
|
|
99
110
|
};
|
|
@@ -101,6 +112,7 @@ export function createVariableTypeRegistry() {
|
|
|
101
112
|
return {
|
|
102
113
|
registerVariableType,
|
|
103
114
|
getVariableType,
|
|
115
|
+
getVariableTypes,
|
|
104
116
|
hasVariableType,
|
|
105
117
|
};
|
|
106
118
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { createVariableTypeRegistry } from './create-variable-type-registry';
|
|
2
2
|
|
|
3
|
-
export const { registerVariableType, getVariableType, hasVariableType } =
|
|
3
|
+
export const { registerVariableType, getVariableType, getVariableTypes, hasVariableType } =
|
|
4
|
+
createVariableTypeRegistry();
|