@elementor/editor-variables 4.3.0-948 → 4.3.0-949

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor-variables",
3
- "version": "4.3.0-948",
3
+ "version": "4.3.0-949",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,22 +39,22 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "4.3.0-948",
43
- "@elementor/editor-canvas": "4.3.0-948",
44
- "@elementor/editor-controls": "4.3.0-948",
45
- "@elementor/editor-current-user": "4.3.0-948",
46
- "@elementor/editor-mcp": "4.3.0-948",
47
- "@elementor/editor-panels": "4.3.0-948",
48
- "@elementor/editor-props": "4.3.0-948",
49
- "@elementor/editor-ui": "4.3.0-948",
50
- "@elementor/editor-v1-adapters": "4.3.0-948",
51
- "@elementor/menus": "4.3.0-948",
52
- "@elementor/http-client": "4.3.0-948",
42
+ "@elementor/editor": "4.3.0-949",
43
+ "@elementor/editor-canvas": "4.3.0-949",
44
+ "@elementor/editor-controls": "4.3.0-949",
45
+ "@elementor/editor-current-user": "4.3.0-949",
46
+ "@elementor/editor-mcp": "4.3.0-949",
47
+ "@elementor/editor-panels": "4.3.0-949",
48
+ "@elementor/editor-props": "4.3.0-949",
49
+ "@elementor/editor-ui": "4.3.0-949",
50
+ "@elementor/editor-v1-adapters": "4.3.0-949",
51
+ "@elementor/menus": "4.3.0-949",
52
+ "@elementor/http-client": "4.3.0-949",
53
53
  "@elementor/icons": "~1.75.1",
54
- "@elementor/events": "4.3.0-948",
55
- "@elementor/schema": "4.3.0-948",
54
+ "@elementor/events": "4.3.0-949",
55
+ "@elementor/schema": "4.3.0-949",
56
56
  "@elementor/ui": "1.37.5",
57
- "@elementor/utils": "4.3.0-948",
57
+ "@elementor/utils": "4.3.0-949",
58
58
  "@wordpress/i18n": "^5.13.0"
59
59
  },
60
60
  "peerDependencies": {
@@ -0,0 +1,31 @@
1
+ import { useEffect } from 'react';
2
+ import { MCP_STYLES_APPLIED_EVENT, type McpStylesAppliedPayload } from '@elementor/editor-mcp';
3
+
4
+ import { extractVariablesFromStyleValue } from '../utils/extract-variables-from-style-value';
5
+ import { trackVariableEvent } from '../utils/tracking';
6
+
7
+ export function McpVariableConnectListener() {
8
+ useEffect( () => {
9
+ const handleMcpStylesApplied = ( event: CustomEvent< McpStylesAppliedPayload > ) => {
10
+ const { styleValue } = event.detail;
11
+ const variables = extractVariablesFromStyleValue( styleValue );
12
+
13
+ variables.forEach( ( { type, controlPath } ) => {
14
+ trackVariableEvent( {
15
+ varType: type,
16
+ controlPath,
17
+ action: 'connect',
18
+ executedBy: 'mcp_tool',
19
+ } );
20
+ } );
21
+ };
22
+
23
+ window.addEventListener( MCP_STYLES_APPLIED_EVENT, handleMcpStylesApplied as EventListener );
24
+
25
+ return () => {
26
+ window.removeEventListener( MCP_STYLES_APPLIED_EVENT, handleMcpStylesApplied as EventListener );
27
+ };
28
+ }, [] );
29
+
30
+ return null;
31
+ }
@@ -10,7 +10,6 @@ import { useVariableType } from '../context/variable-type-context';
10
10
  import { useInitialValue } from '../hooks/use-initial-value';
11
11
  import { createVariable } from '../hooks/use-prop-variables';
12
12
  import { useVariableBoundProp } from '../hooks/use-variable-bound-prop';
13
- import { trackVariableEvent } from '../utils/tracking';
14
13
  import { ERROR_MESSAGES, labelHint, mapServerError } from '../utils/validations';
15
14
  import { LabelField, useLabelError } from './fields/label-field';
16
15
  import { FormField } from './ui/form-field';
@@ -23,7 +22,7 @@ type Props = {
23
22
  };
24
23
 
25
24
  export const VariableCreation = ( { onGoBack, onClose }: Props ) => {
26
- const { icon: VariableIcon, valueField: ValueField, variableType, propTypeUtil } = useVariableType();
25
+ const { icon: VariableIcon, valueField: ValueField, propTypeUtil } = useVariableType();
27
26
 
28
27
  const { setVariableValue: setVariable, path } = useVariableBoundProp();
29
28
  const { propType } = useBoundProp();
@@ -51,11 +50,14 @@ export const VariableCreation = ( { onGoBack, onClose }: Props ) => {
51
50
  };
52
51
 
53
52
  const handleCreateAndTrack = () => {
54
- createVariable( {
55
- value,
56
- label,
57
- type: propTypeKey,
58
- } )
53
+ createVariable(
54
+ {
55
+ value,
56
+ label,
57
+ type: propTypeKey,
58
+ },
59
+ { eventData: { controlPath: path.join( '.' ) } }
60
+ )
59
61
  .then( ( key ) => {
60
62
  setVariable( key );
61
63
  closePopover();
@@ -73,12 +75,6 @@ export const VariableCreation = ( { onGoBack, onClose }: Props ) => {
73
75
 
74
76
  setErrorMessage( ERROR_MESSAGES.UNEXPECTED_ERROR );
75
77
  } );
76
-
77
- trackVariableEvent( {
78
- varType: variableType,
79
- controlPath: path.join( '.' ),
80
- action: 'save',
81
- } );
82
78
  };
83
79
 
84
80
  const hasEmptyFields = () => {
@@ -31,7 +31,7 @@ type Props = {
31
31
  export const VariableEdit = ( { onClose, onGoBack, onSubmit, editId }: Props ) => {
32
32
  const { icon: VariableIcon, valueField: ValueField, variableType, propTypeUtil } = useVariableType();
33
33
 
34
- const { setVariableValue: notifyBoundPropChange, variableId } = useVariableBoundProp();
34
+ const { setVariableValue: notifyBoundPropChange, variableId, path } = useVariableBoundProp();
35
35
  const { propType } = useBoundProp();
36
36
  const [ isMessageSuppressed, suppressMessage ] = useSuppressedMessage( EDIT_CONFIRMATION_DIALOG_ID );
37
37
  const [ deleteConfirmation, setDeleteConfirmation ] = useState( false );
@@ -80,7 +80,7 @@ export const VariableEdit = ( { onClose, onGoBack, onSubmit, editId }: Props ) =
80
80
  const typeChanged = propTypeKey !== variable.type;
81
81
  const updatePayload = typeChanged ? { value, label, type: propTypeKey } : { value, label };
82
82
 
83
- updateVariable( editId, updatePayload )
83
+ updateVariable( editId, updatePayload, { eventData: { controlPath: path.join( '.' ) } } )
84
84
  .then( () => {
85
85
  maybeTriggerBoundPropChange();
86
86
  onSubmit?.();
@@ -3,7 +3,7 @@ import { useBoundProp } from '@elementor/editor-controls';
3
3
  import { type PropKey } from '@elementor/editor-props';
4
4
 
5
5
  import { useVariableType } from '../context/variable-type-context';
6
- import { service } from '../service';
6
+ import { type CreateVariableOptions, service, type UpdateVariableOptions } from '../service';
7
7
  import { type NormalizedVariable, type Variable } from '../types';
8
8
  import { filterBySearch } from '../utils/filter-by-search';
9
9
  import { toNormalizedVariable, variablesToList } from '../utils/variables-to-list';
@@ -100,15 +100,16 @@ const normalizeVariables = ( propKey: string ): NormalizedVariable[] => {
100
100
 
101
101
  const extractId = ( { id }: { id: string } ): string => id;
102
102
 
103
- export const createVariable = ( newVariable: Variable ): Promise< string > => {
104
- return service.create( newVariable ).then( extractId );
103
+ export const createVariable = ( newVariable: Variable, options: CreateVariableOptions ): Promise< string > => {
104
+ return service.create( newVariable, options ).then( extractId );
105
105
  };
106
106
 
107
107
  export const updateVariable = (
108
108
  updateId: string,
109
- { value, label, type }: { value: string; label: string; type?: string }
109
+ { value, label, type }: { value: string; label: string; type?: string },
110
+ options?: UpdateVariableOptions
110
111
  ): Promise< string > => {
111
- return service.update( updateId, { value, label, type } ).then( extractId );
112
+ return service.update( updateId, { value, label, type }, options ).then( extractId );
112
113
  };
113
114
 
114
115
  export const deleteVariable = ( deleteId: string ): Promise< string > => {
package/src/init.ts CHANGED
@@ -5,6 +5,7 @@ import { isTransformable, type PropValue } from '@elementor/editor-props';
5
5
  import { controlActionsMenu } from '@elementor/menus';
6
6
 
7
7
  import { GlobalStylesImportListener } from './components/global-styles-import-listener';
8
+ import { McpVariableConnectListener } from './components/mcp-variable-connect-listener';
8
9
  import { VariableControl } from './controls/variable-control';
9
10
  import { usePropVariableAction } from './hooks/use-prop-variable-action';
10
11
  import { initMcp } from './mcp';
@@ -63,6 +64,11 @@ export function init() {
63
64
  id: 'variables-import-listener',
64
65
  component: GlobalStylesImportListener,
65
66
  } );
67
+
68
+ injectIntoLogic( {
69
+ id: 'mcp-variable-connect-listener',
70
+ component: McpVariableConnectListener,
71
+ } );
66
72
  }
67
73
 
68
74
  function hasVariableAssigned( value: PropValue ) {
@@ -128,7 +128,7 @@ function getServiceActions( svc: typeof service ) {
128
128
  if ( valueError ) {
129
129
  throw new Error( valueError );
130
130
  }
131
- return svc.create( { type, label, value } );
131
+ return svc.create( { type, label, value }, { eventData: { executedBy: 'mcp_tool' } } );
132
132
  },
133
133
  update( { id, label, value }: Opts< { id: string; label: string; value: string } > ) {
134
134
  if ( ! id || ! label || ! value ) {
@@ -145,7 +145,7 @@ function getServiceActions( svc: typeof service ) {
145
145
  throw new Error( valueError );
146
146
  }
147
147
  }
148
- return svc.update( id, { label, value } );
148
+ return svc.update( id, { label, value }, { eventData: { executedBy: 'mcp_tool' } } );
149
149
  },
150
150
  delete( { id }: Opts< { id: string } > ) {
151
151
  if ( ! id ) {
package/src/service.ts CHANGED
@@ -5,6 +5,20 @@ import { buildOperationsArray, type OperationResult } from './batch-operations';
5
5
  import { OP_RW, Storage, type TVariable, type TVariablesList } from './storage';
6
6
  import { styleVariablesRepository } from './style-variables-repository';
7
7
  import { type Variable } from './types';
8
+ import { trackVariableEvent, type VariableEventData } from './utils/tracking';
9
+
10
+ type EventData = {
11
+ controlPath?: VariableEventData[ 'controlPath' ];
12
+ executedBy?: VariableEventData[ 'executedBy' ];
13
+ };
14
+
15
+ export type CreateVariableOptions = {
16
+ eventData?: EventData;
17
+ };
18
+
19
+ export type UpdateVariableOptions = {
20
+ eventData?: EventData;
21
+ };
8
22
 
9
23
  const storage = new Storage();
10
24
 
@@ -56,7 +70,7 @@ export const service = {
56
70
  } );
57
71
  },
58
72
 
59
- create: ( { type, label, value }: Variable ) => {
73
+ create: ( { type, label, value }: Variable, options: CreateVariableOptions = {} ) => {
60
74
  return apiClient
61
75
  .create( type, label, value )
62
76
  .then( ( response ) => {
@@ -82,6 +96,12 @@ export const service = {
82
96
  [ variableId ]: createdVariable,
83
97
  } );
84
98
 
99
+ trackVariableEvent( {
100
+ varType: type,
101
+ action: 'save',
102
+ ...options.eventData,
103
+ } );
104
+
85
105
  return {
86
106
  id: variableId,
87
107
  variable: createdVariable,
@@ -89,7 +109,11 @@ export const service = {
89
109
  } );
90
110
  },
91
111
 
92
- update: ( id: string, { label, value, type }: Omit< Variable, 'type' > & { type?: Variable[ 'type' ] } ) => {
112
+ update: (
113
+ id: string,
114
+ { label, value, type }: Omit< Variable, 'type' > & { type?: Variable[ 'type' ] },
115
+ options: UpdateVariableOptions = {}
116
+ ) => {
93
117
  return apiClient
94
118
  .update( id, label, value, type )
95
119
  .then( ( response ) => {
@@ -115,6 +139,12 @@ export const service = {
115
139
  [ variableId ]: updatedVariable,
116
140
  } );
117
141
 
142
+ trackVariableEvent( {
143
+ varType: updatedVariable.type,
144
+ action: 'update',
145
+ ...options.eventData,
146
+ } );
147
+
118
148
  return {
119
149
  id: variableId,
120
150
  variable: updatedVariable,
@@ -0,0 +1,55 @@
1
+ import { getPropSchemaFromCache, isTransformable } from '@elementor/editor-props';
2
+
3
+ type VariableInfo = {
4
+ type: string;
5
+ variableId: string;
6
+ controlPath: string;
7
+ };
8
+
9
+ const VARIABLE_TYPE_KEYS = [
10
+ 'global-color-variable',
11
+ 'global-font-variable',
12
+ 'global-size-variable',
13
+ 'global-custom-size-variable',
14
+ ] as const;
15
+
16
+ function tryExtractVariable( value: unknown ): { type: string; variableId: string } | null {
17
+ for ( const key of VARIABLE_TYPE_KEYS ) {
18
+ const propUtil = getPropSchemaFromCache( key );
19
+ if ( propUtil?.isValid( value ) ) {
20
+ return {
21
+ type: key,
22
+ variableId: propUtil.extract( value ) as string,
23
+ };
24
+ }
25
+ }
26
+ return null;
27
+ }
28
+
29
+ function traverse( value: unknown, path: string[], result: VariableInfo[] ): void {
30
+ const extracted = tryExtractVariable( value );
31
+ if ( extracted ) {
32
+ result.push( {
33
+ ...extracted,
34
+ controlPath: path.join( '.' ),
35
+ } );
36
+ return;
37
+ }
38
+
39
+ if ( isTransformable( value ) ) {
40
+ traverse( value.value, path, result );
41
+ return;
42
+ }
43
+
44
+ if ( value && typeof value === 'object' ) {
45
+ for ( const [ key, val ] of Object.entries( value ) ) {
46
+ traverse( val, [ ...path, key ], result );
47
+ }
48
+ }
49
+ }
50
+
51
+ export function extractVariablesFromStyleValue( styleValue: Record< string, unknown > ): VariableInfo[] {
52
+ const result: VariableInfo[] = [];
53
+ traverse( styleValue, [], result );
54
+ return result;
55
+ }
@@ -1,26 +1,44 @@
1
1
  import { getMixpanel } from '@elementor/events';
2
2
 
3
- type VariableEventData = {
3
+ export type VariableEventData = {
4
4
  varType: string;
5
- controlPath: string;
6
- action: 'open' | 'add' | 'connect' | 'save';
5
+ controlPath?: string;
6
+ action: 'open' | 'add' | 'connect' | 'save' | 'update';
7
+ executedBy?: 'mcp_tool' | 'user';
7
8
  };
8
9
 
9
- export const trackVariableEvent = ( { varType, controlPath, action }: VariableEventData ) => {
10
+ export const trackVariableEvent = ( { varType, controlPath, action, executedBy }: VariableEventData ) => {
10
11
  const { dispatchEvent, config } = getMixpanel();
11
12
  if ( ! config?.names?.variables?.[ action ] ) {
12
13
  return;
13
14
  }
14
15
 
15
16
  const name = config.names.variables[ action ];
16
- dispatchEvent?.( name, {
17
+
18
+ let eventData: Record< string, string > = {
19
+ var_type: varType,
20
+ action_type: name,
21
+ };
22
+
23
+ if ( executedBy ) {
24
+ eventData.executed_by = executedBy;
25
+ }
26
+
27
+ const defaultLocationInfo = {
17
28
  location: config?.locations?.variables || '',
18
29
  secondaryLocation: config?.secondaryLocations?.variablesPopover || '',
19
30
  trigger: config?.triggers?.click || '',
20
- var_type: varType,
21
- control_path: controlPath,
22
- action_type: name,
23
- } );
31
+ };
32
+
33
+ if ( ! executedBy || executedBy !== 'mcp_tool' ) {
34
+ eventData = { ...defaultLocationInfo, ...eventData };
35
+ }
36
+
37
+ if ( controlPath ) {
38
+ eventData.control_path = controlPath;
39
+ }
40
+
41
+ dispatchEvent?.( name, eventData );
24
42
  };
25
43
 
26
44
  export type VariablesManagerOpenSource = 'vars-popover' | 'system-panel';