@elementor/editor-variables 0.16.0 → 3.32.0-20
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/CHANGELOG.md +22 -0
- package/dist/index.d.mts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +1397 -885
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1399 -871
- package/dist/index.mjs.map +1 -1
- package/package.json +16 -14
- package/src/api.ts +18 -2
- package/src/components/fields/color-field.tsx +3 -3
- package/src/components/fields/font-field.tsx +21 -10
- package/src/components/fields/label-field.tsx +31 -5
- package/src/components/ui/delete-confirmation-dialog.tsx +4 -7
- package/src/components/ui/deleted-variable-alert.tsx +47 -0
- package/src/components/ui/edit-confirmation-dialog.tsx +75 -0
- package/src/components/ui/missing-variable-alert.tsx +39 -0
- package/src/components/ui/no-variables.tsx +59 -26
- package/src/components/ui/tags/assigned-tag.tsx +21 -19
- package/src/components/ui/tags/deleted-tag.tsx +29 -18
- package/src/components/ui/tags/missing-tag.tsx +25 -0
- package/src/components/ui/variable/assigned-variable.tsx +11 -14
- package/src/components/ui/variable/deleted-variable.tsx +115 -7
- package/src/components/ui/variable/missing-variable.tsx +44 -0
- package/src/components/variable-creation.tsx +135 -0
- package/src/components/variable-edit.tsx +221 -0
- package/src/components/variable-restore.tsx +117 -0
- package/src/components/variable-selection-popover.tsx +91 -92
- package/src/components/variables-manager/variables-manager-panel.tsx +115 -0
- package/src/components/variables-selection.tsx +148 -0
- package/src/context/variable-selection-popover.context.tsx +19 -0
- package/src/context/variable-type-context.tsx +23 -0
- package/src/controls/variable-control.tsx +26 -0
- package/src/create-style-variables-repository.ts +44 -5
- package/src/hooks/use-initial-value.ts +22 -0
- package/src/hooks/use-permissions.ts +15 -0
- package/src/hooks/use-prop-variable-action.tsx +53 -0
- package/src/hooks/use-prop-variables.ts +6 -0
- package/src/index.ts +1 -0
- package/src/init.ts +33 -4
- package/src/register-variable-types.tsx +29 -0
- package/src/renderers/style-variables-renderer.tsx +10 -4
- package/src/repeater-injections.ts +5 -1
- package/src/service.ts +8 -4
- package/src/sync/enqueue-font.ts +7 -0
- package/src/sync/types.ts +5 -0
- package/src/transformers/inheritance-transformer.tsx +30 -0
- package/src/transformers/utils/resolve-css-variable.ts +24 -0
- package/src/transformers/variable-transformer.ts +8 -3
- package/src/types.ts +1 -1
- package/src/utils/tracking.ts +39 -0
- package/src/utils/validations.ts +40 -6
- package/src/variables-registry/create-variable-type-registry.ts +77 -0
- package/src/variables-registry/variable-type-registry.ts +3 -0
- package/src/components/color-variable-creation.tsx +0 -86
- package/src/components/color-variable-edit.tsx +0 -138
- package/src/components/color-variables-selection.tsx +0 -130
- package/src/components/font-variable-creation.tsx +0 -86
- package/src/components/font-variable-edit.tsx +0 -138
- package/src/components/font-variables-selection.tsx +0 -129
- package/src/components/variable-selection-popover.context.ts +0 -7
- package/src/controls/color-variable-control.tsx +0 -33
- package/src/controls/font-variable-control.tsx +0 -31
- package/src/hooks/use-prop-color-variable-action.tsx +0 -25
- package/src/hooks/use-prop-font-variable-action.tsx +0 -25
- package/src/init-color-variables.ts +0 -27
- package/src/init-font-variables.ts +0 -24
- package/src/utils.ts +0 -20
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { fontVariablePropTypeUtil } from './prop-types/font-variable-prop-type';
|
|
2
|
+
import { enqueueFont } from './sync/enqueue-font';
|
|
1
3
|
import { type StyleVariables, type Variable } from './types';
|
|
2
4
|
|
|
3
5
|
type VariablesChangeCallback = ( variables: StyleVariables ) => void;
|
|
@@ -21,16 +23,41 @@ export const createStyleVariablesRepository = () => {
|
|
|
21
23
|
}
|
|
22
24
|
};
|
|
23
25
|
|
|
24
|
-
const shouldUpdate = ( key: string,
|
|
25
|
-
|
|
26
|
+
const shouldUpdate = ( key: string, maybeUpdated: Variable ): boolean => {
|
|
27
|
+
if ( ! ( key in variables ) ) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if ( variables[ key ].label !== maybeUpdated.label ) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if ( variables[ key ].value !== maybeUpdated.value ) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if ( ! variables[ key ]?.deleted && maybeUpdated?.deleted ) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if ( variables[ key ]?.deleted && ! maybeUpdated?.deleted ) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return false;
|
|
26
48
|
};
|
|
27
49
|
|
|
28
50
|
const applyUpdates = ( updatedVars: Variables ): boolean => {
|
|
29
51
|
let hasChanges = false;
|
|
30
52
|
|
|
31
|
-
for ( const [ key,
|
|
32
|
-
if ( shouldUpdate( key,
|
|
33
|
-
variables[ key ] =
|
|
53
|
+
for ( const [ key, variable ] of Object.entries( updatedVars ) ) {
|
|
54
|
+
if ( shouldUpdate( key, variable ) ) {
|
|
55
|
+
variables[ key ] = variable;
|
|
56
|
+
|
|
57
|
+
if ( variable.type === fontVariablePropTypeUtil.key ) {
|
|
58
|
+
fontEnqueue( variable.value );
|
|
59
|
+
}
|
|
60
|
+
|
|
34
61
|
hasChanges = true;
|
|
35
62
|
}
|
|
36
63
|
}
|
|
@@ -38,6 +65,18 @@ export const createStyleVariablesRepository = () => {
|
|
|
38
65
|
return hasChanges;
|
|
39
66
|
};
|
|
40
67
|
|
|
68
|
+
const fontEnqueue = ( value: string ): void => {
|
|
69
|
+
if ( ! value ) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
enqueueFont( value );
|
|
75
|
+
} catch {
|
|
76
|
+
// This prevents font enqueueing failures from breaking variable updates
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
41
80
|
const update = ( updatedVars: Variables ) => {
|
|
42
81
|
if ( applyUpdates( updatedVars ) ) {
|
|
43
82
|
notify();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useBoundProp } from '@elementor/editor-controls';
|
|
2
|
+
|
|
3
|
+
import { hasVariableType } from '../variables-registry/variable-type-registry';
|
|
4
|
+
import { useVariable } from './use-prop-variables';
|
|
5
|
+
|
|
6
|
+
type PropValue = {
|
|
7
|
+
$$type: string;
|
|
8
|
+
value: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const useInitialValue = () => {
|
|
12
|
+
const { value: initial }: { value: PropValue } = useBoundProp();
|
|
13
|
+
|
|
14
|
+
const hasAssignedVariable = hasVariableType( initial?.$$type ) && Boolean( initial?.value );
|
|
15
|
+
const variable = useVariable( hasAssignedVariable ? initial.value : '' );
|
|
16
|
+
|
|
17
|
+
if ( hasAssignedVariable ) {
|
|
18
|
+
return variable ? variable.value : '';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return initial?.value ?? '';
|
|
22
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useCurrentUserCapabilities } from '@elementor/editor-current-user';
|
|
2
|
+
|
|
3
|
+
export const usePermissions = () => {
|
|
4
|
+
const { canUser } = useCurrentUserCapabilities();
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
canAssign: () => canUser( 'edit_posts' ),
|
|
8
|
+
canUnlink: () => canUser( 'edit_posts' ),
|
|
9
|
+
canAdd: () => canUser( 'manage_options' ),
|
|
10
|
+
canDelete: () => canUser( 'manage_options' ),
|
|
11
|
+
canEdit: () => canUser( 'manage_options' ),
|
|
12
|
+
canRestore: () => canUser( 'manage_options' ),
|
|
13
|
+
canManageSettings: () => canUser( 'manage_options' ),
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type PopoverActionProps, useBoundProp } from '@elementor/editor-editing-panel';
|
|
3
|
+
import { type PropType } from '@elementor/editor-props';
|
|
4
|
+
import { ColorFilterIcon } from '@elementor/icons';
|
|
5
|
+
import { __ } from '@wordpress/i18n';
|
|
6
|
+
|
|
7
|
+
import { VariableSelectionPopover } from '../components/variable-selection-popover';
|
|
8
|
+
import { trackVariableEvent } from '../utils/tracking';
|
|
9
|
+
import { getVariableType } from '../variables-registry/variable-type-registry';
|
|
10
|
+
|
|
11
|
+
export const usePropVariableAction = (): PopoverActionProps => {
|
|
12
|
+
const { propType, path } = useBoundProp();
|
|
13
|
+
const variable = resolveVariableFromPropType( propType );
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
visible: Boolean( variable ),
|
|
17
|
+
icon: ColorFilterIcon,
|
|
18
|
+
title: __( 'Variables', 'elementor' ),
|
|
19
|
+
content: ( { close: closePopover } ) => {
|
|
20
|
+
if ( ! variable ) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
trackOpenVariablePopover( path, variable.variableType );
|
|
25
|
+
|
|
26
|
+
return <VariableSelectionPopover closePopover={ closePopover } propTypeKey={ variable.propTypeUtil.key } />;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const resolveVariableFromPropType = ( propType: PropType ) => {
|
|
32
|
+
if ( propType.kind !== 'union' ) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for ( const key of Object.keys( propType.prop_types ) ) {
|
|
37
|
+
const variable = getVariableType( key );
|
|
38
|
+
|
|
39
|
+
if ( variable ) {
|
|
40
|
+
return variable;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return undefined;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const trackOpenVariablePopover = ( path: string[], variableType: string ) => {
|
|
48
|
+
trackVariableEvent( {
|
|
49
|
+
varType: variableType,
|
|
50
|
+
controlPath: path.join( '.' ),
|
|
51
|
+
action: 'open',
|
|
52
|
+
} );
|
|
53
|
+
};
|
|
@@ -66,3 +66,9 @@ export const deleteVariable = ( deleteId: string ) => {
|
|
|
66
66
|
return id;
|
|
67
67
|
} );
|
|
68
68
|
};
|
|
69
|
+
|
|
70
|
+
export const restoreVariable = ( restoreId: string, label?: string, value?: string ) => {
|
|
71
|
+
return service.restore( restoreId, label, value ).then( ( { id }: { id: string } ) => {
|
|
72
|
+
return id;
|
|
73
|
+
} );
|
|
74
|
+
};
|
package/src/index.ts
CHANGED
package/src/init.ts
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
import { injectIntoTop } from '@elementor/editor';
|
|
2
|
+
import { controlActionsMenu, registerControlReplacement } from '@elementor/editor-editing-panel';
|
|
3
|
+
import { __registerPanel as registerPanel } from '@elementor/editor-panels';
|
|
4
|
+
import type { PropValue } from '@elementor/editor-props';
|
|
2
5
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
6
|
+
import { panel } from './components/variables-manager/variables-manager-panel';
|
|
7
|
+
import { VariableControl } from './controls/variable-control';
|
|
8
|
+
import { usePropVariableAction } from './hooks/use-prop-variable-action';
|
|
9
|
+
import { registerVariableTypes } from './register-variable-types';
|
|
5
10
|
import { StyleVariablesRenderer } from './renderers/style-variables-renderer';
|
|
11
|
+
import { registerRepeaterInjections } from './repeater-injections';
|
|
6
12
|
import { service as variablesService } from './service';
|
|
13
|
+
import { hasVariableType } from './variables-registry/variable-type-registry';
|
|
14
|
+
|
|
15
|
+
const { registerPopoverAction } = controlActionsMenu;
|
|
7
16
|
|
|
8
17
|
export function init() {
|
|
9
|
-
|
|
10
|
-
|
|
18
|
+
registerVariableTypes();
|
|
19
|
+
registerRepeaterInjections();
|
|
20
|
+
|
|
21
|
+
registerControlReplacement( {
|
|
22
|
+
component: VariableControl,
|
|
23
|
+
condition: ( { value } ) => hasAssignedVariable( value ),
|
|
24
|
+
} );
|
|
25
|
+
|
|
26
|
+
registerPopoverAction( {
|
|
27
|
+
id: 'variables',
|
|
28
|
+
useProps: usePropVariableAction,
|
|
29
|
+
} );
|
|
11
30
|
|
|
12
31
|
variablesService.init();
|
|
13
32
|
|
|
@@ -15,4 +34,14 @@ export function init() {
|
|
|
15
34
|
id: 'canvas-style-variables-render',
|
|
16
35
|
component: StyleVariablesRenderer,
|
|
17
36
|
} );
|
|
37
|
+
|
|
38
|
+
registerPanel( panel );
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function hasAssignedVariable( propValue: PropValue ) {
|
|
42
|
+
if ( propValue && typeof propValue === 'object' && '$$type' in propValue ) {
|
|
43
|
+
return hasVariableType( propValue.$$type );
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return false;
|
|
18
47
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { colorPropTypeUtil, stringPropTypeUtil } from '@elementor/editor-props';
|
|
3
|
+
import { BrushIcon, TextIcon } from '@elementor/icons';
|
|
4
|
+
|
|
5
|
+
import { ColorField } from './components/fields/color-field';
|
|
6
|
+
import { FontField } from './components/fields/font-field';
|
|
7
|
+
import { ColorIndicator } from './components/ui/color-indicator';
|
|
8
|
+
import { colorVariablePropTypeUtil } from './prop-types/color-variable-prop-type';
|
|
9
|
+
import { fontVariablePropTypeUtil } from './prop-types/font-variable-prop-type';
|
|
10
|
+
import { registerVariableType } from './variables-registry/variable-type-registry';
|
|
11
|
+
|
|
12
|
+
export function registerVariableTypes() {
|
|
13
|
+
registerVariableType( {
|
|
14
|
+
valueField: ColorField,
|
|
15
|
+
icon: BrushIcon,
|
|
16
|
+
propTypeUtil: colorVariablePropTypeUtil,
|
|
17
|
+
fallbackPropTypeUtil: colorPropTypeUtil,
|
|
18
|
+
variableType: 'color',
|
|
19
|
+
startIcon: ( { value } ) => <ColorIndicator size="inherit" component="span" value={ value } />,
|
|
20
|
+
} );
|
|
21
|
+
|
|
22
|
+
registerVariableType( {
|
|
23
|
+
valueField: FontField,
|
|
24
|
+
icon: TextIcon,
|
|
25
|
+
propTypeUtil: fontVariablePropTypeUtil,
|
|
26
|
+
fallbackPropTypeUtil: stringPropTypeUtil,
|
|
27
|
+
variableType: 'font',
|
|
28
|
+
} );
|
|
29
|
+
}
|
|
@@ -5,7 +5,7 @@ import { Portal } from '@elementor/ui';
|
|
|
5
5
|
|
|
6
6
|
import { styleVariablesRepository } from '../style-variables-repository';
|
|
7
7
|
import { getCanvasIframeDocument } from '../sync/get-canvas-iframe-document';
|
|
8
|
-
import { type StyleVariables } from '../types';
|
|
8
|
+
import { type StyleVariables, type Variable } from '../types';
|
|
9
9
|
|
|
10
10
|
const VARIABLES_WRAPPER = 'body';
|
|
11
11
|
|
|
@@ -49,8 +49,14 @@ function useStyleVariables() {
|
|
|
49
49
|
return variables;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
function cssVariableDeclaration( key: string, variable: Variable ) {
|
|
53
|
+
const variableName = variable?.deleted ? key : variable.label;
|
|
54
|
+
const value = variable.value;
|
|
55
|
+
|
|
56
|
+
return `--${ variableName }:${ value };`;
|
|
57
|
+
}
|
|
58
|
+
|
|
52
59
|
function convertToCssVariables( variables: StyleVariables ): string {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
.join( '' );
|
|
60
|
+
const listOfVariables = Object.entries( variables );
|
|
61
|
+
return listOfVariables.map( ( [ key, variable ] ) => cssVariableDeclaration( key, variable ) ).join( '' );
|
|
56
62
|
}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
BackgroundRepeaterLabel,
|
|
7
7
|
BoxShadowRepeaterColorIndicator,
|
|
8
8
|
} from './components/variables-repeater-item-slot';
|
|
9
|
-
import {
|
|
9
|
+
import { colorVariablePropTypeUtil } from './prop-types/color-variable-prop-type';
|
|
10
10
|
|
|
11
11
|
export function registerRepeaterInjections() {
|
|
12
12
|
injectIntoRepeaterItemIcon( {
|
|
@@ -33,3 +33,7 @@ export function registerRepeaterInjections() {
|
|
|
33
33
|
},
|
|
34
34
|
} );
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
const hasAssignedColorVariable = ( propValue: PropValue ): boolean => {
|
|
38
|
+
return !! colorVariablePropTypeUtil.isValid( propValue );
|
|
39
|
+
};
|
package/src/service.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { __ } from '@wordpress/i18n';
|
|
2
|
+
|
|
1
3
|
import { apiClient } from './api';
|
|
2
4
|
import { OP_RW, Storage, type TVariablesList } from './storage';
|
|
3
5
|
import { styleVariablesRepository } from './style-variables-repository';
|
|
@@ -44,7 +46,8 @@ export const service = {
|
|
|
44
46
|
const { success, data: payload } = response.data;
|
|
45
47
|
|
|
46
48
|
if ( ! success ) {
|
|
47
|
-
|
|
49
|
+
const errorMessage = payload?.message || __( 'Unexpected response from server', 'elementor' );
|
|
50
|
+
throw new Error( errorMessage );
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
return payload;
|
|
@@ -76,7 +79,8 @@ export const service = {
|
|
|
76
79
|
const { success, data: payload } = response.data;
|
|
77
80
|
|
|
78
81
|
if ( ! success ) {
|
|
79
|
-
|
|
82
|
+
const errorMessage = payload?.message || __( 'Unexpected response from server', 'elementor' );
|
|
83
|
+
throw new Error( errorMessage );
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
return payload;
|
|
@@ -133,9 +137,9 @@ export const service = {
|
|
|
133
137
|
} );
|
|
134
138
|
},
|
|
135
139
|
|
|
136
|
-
restore: ( id: string ) => {
|
|
140
|
+
restore: ( id: string, label?: string, value?: string ) => {
|
|
137
141
|
return apiClient
|
|
138
|
-
.restore( id )
|
|
142
|
+
.restore( id, label, value )
|
|
139
143
|
.then( ( response ) => {
|
|
140
144
|
const { success, data: payload } = response.data;
|
|
141
145
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type CanvasExtendedWindow, type EnqueueFont } from './types';
|
|
2
|
+
|
|
3
|
+
export const enqueueFont: EnqueueFont = ( fontFamily, context = 'preview' ) => {
|
|
4
|
+
const extendedWindow = window as unknown as CanvasExtendedWindow;
|
|
5
|
+
|
|
6
|
+
return extendedWindow.elementor?.helpers?.enqueueFont?.( fontFamily, context ) ?? null;
|
|
7
|
+
};
|
package/src/sync/types.ts
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createTransformer } from '@elementor/editor-canvas';
|
|
3
|
+
import { Stack, Typography } from '@elementor/ui';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
|
|
6
|
+
import { ColorIndicator } from '../components/ui/color-indicator';
|
|
7
|
+
import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
|
|
8
|
+
import { service } from '../service';
|
|
9
|
+
import { resolveCssVariable } from './utils/resolve-css-variable';
|
|
10
|
+
|
|
11
|
+
export const inheritanceTransformer = createTransformer( ( id: string ) => {
|
|
12
|
+
const variables = service.variables();
|
|
13
|
+
const variable = variables[ id ];
|
|
14
|
+
|
|
15
|
+
if ( ! variable ) {
|
|
16
|
+
return <span>{ __( 'Missing variable', 'elementor' ) }</span>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const showColorIndicator = variable.type === colorVariablePropTypeUtil.key;
|
|
20
|
+
const css = resolveCssVariable( id, variable );
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Stack direction="row" spacing={ 0.5 } sx={ { paddingInline: '1px' } } alignItems="center">
|
|
24
|
+
{ showColorIndicator && <ColorIndicator size="inherit" value={ variable.value } /> }
|
|
25
|
+
<Typography variant="caption" overflow="hidden" whiteSpace="nowrap" textOverflow="ellipsis">
|
|
26
|
+
{ css }
|
|
27
|
+
</Typography>
|
|
28
|
+
</Stack>
|
|
29
|
+
);
|
|
30
|
+
} );
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type TVariable } from '../../storage';
|
|
2
|
+
|
|
3
|
+
export const resolveCssVariable = ( id: string, variable: TVariable ) => {
|
|
4
|
+
let name = id;
|
|
5
|
+
let fallbackValue = '';
|
|
6
|
+
|
|
7
|
+
if ( variable ) {
|
|
8
|
+
fallbackValue = variable.value;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if ( variable && ! variable.deleted ) {
|
|
12
|
+
name = variable.label;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if ( ! name.trim() ) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if ( ! fallbackValue.trim() ) {
|
|
20
|
+
return `var(--${ name })`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return `var(--${ name }, ${ fallbackValue })`;
|
|
24
|
+
};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { createTransformer } from '@elementor/editor-canvas';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { service } from '../service';
|
|
4
|
+
import { resolveCssVariable } from './utils/resolve-css-variable';
|
|
5
|
+
|
|
6
|
+
export const variableTransformer = createTransformer( ( id: string ) => {
|
|
7
|
+
const variables = service.variables();
|
|
8
|
+
|
|
9
|
+
if ( ! variables[ id ] ) {
|
|
5
10
|
return null;
|
|
6
11
|
}
|
|
7
12
|
|
|
8
|
-
return
|
|
13
|
+
return resolveCssVariable( id, variables[ id ] );
|
|
9
14
|
} );
|
package/src/types.ts
CHANGED
|
@@ -10,7 +10,7 @@ export type Variable = {
|
|
|
10
10
|
deleted_at?: string;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
export type StyleVariables = Record< string,
|
|
13
|
+
export type StyleVariables = Record< string, Variable >;
|
|
14
14
|
|
|
15
15
|
export type ExtendedVirtualizedItem = VirtualizedItem< 'item', string > & {
|
|
16
16
|
icon: React.ReactNode;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
type VariableEventData = {
|
|
2
|
+
varType: string;
|
|
3
|
+
controlPath: string;
|
|
4
|
+
action: 'open' | 'add' | 'connect' | 'save';
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const trackVariableEvent = ( { varType, controlPath, action }: VariableEventData ) => {
|
|
8
|
+
const extendedWindow = window as unknown as Window & {
|
|
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;
|
|
26
|
+
if ( ! config?.names?.variables?.[ action ] ) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const name = config.names.variables[ action ];
|
|
31
|
+
extendedWindow.elementorCommon?.eventsManager?.dispatchEvent( name, {
|
|
32
|
+
location: config.locations.variables,
|
|
33
|
+
secondaryLocation: config.secondaryLocations.variablesPopover,
|
|
34
|
+
trigger: config.triggers.click,
|
|
35
|
+
var_type: varType,
|
|
36
|
+
control_path: controlPath,
|
|
37
|
+
action_type: name,
|
|
38
|
+
} );
|
|
39
|
+
};
|
package/src/utils/validations.ts
CHANGED
|
@@ -1,24 +1,58 @@
|
|
|
1
1
|
import { __ } from '@wordpress/i18n';
|
|
2
2
|
|
|
3
|
+
export const ERROR_MESSAGES = {
|
|
4
|
+
MISSING_VARIABLE_NAME: __( 'Give your variable a name.', 'elementor' ),
|
|
5
|
+
MISSING_VARIABLE_VALUE: __( 'Add a value to complete your variable.', 'elementor' ),
|
|
6
|
+
INVALID_CHARACTERS: __( 'Use letters, numbers, dashes (-), or underscores (_) for the name.', 'elementor' ),
|
|
7
|
+
NO_NON_SPECIAL_CHARACTER: __( 'Names have to include at least one non-special character.', 'elementor' ),
|
|
8
|
+
VARIABLE_LABEL_MAX_LENGTH: __( 'Keep names up to 50 characters.', 'elementor' ),
|
|
9
|
+
DUPLICATED_LABEL: __( 'This variable name already exists. Please choose a unique name.', 'elementor' ),
|
|
10
|
+
UNEXPECTED_ERROR: __( 'There was a glitch. Try saving your variable again.', 'elementor' ),
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
3
13
|
export const VARIABLE_LABEL_MAX_LENGTH = 50;
|
|
4
14
|
|
|
15
|
+
type ErrorResponse = {
|
|
16
|
+
response?: {
|
|
17
|
+
data?: {
|
|
18
|
+
code?: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type MappedError = {
|
|
24
|
+
field: string;
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const mapServerError = ( error: ErrorResponse ): MappedError | undefined => {
|
|
29
|
+
if ( error?.response?.data?.code === 'duplicated_label' ) {
|
|
30
|
+
return {
|
|
31
|
+
field: 'label',
|
|
32
|
+
message: ERROR_MESSAGES.DUPLICATED_LABEL,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return undefined;
|
|
37
|
+
};
|
|
38
|
+
|
|
5
39
|
export const validateLabel = ( name: string ): string => {
|
|
6
40
|
if ( ! name.trim() ) {
|
|
7
|
-
return
|
|
41
|
+
return ERROR_MESSAGES.MISSING_VARIABLE_NAME;
|
|
8
42
|
}
|
|
9
43
|
|
|
10
44
|
const allowedChars = /^[a-zA-Z0-9_-]+$/;
|
|
11
45
|
if ( ! allowedChars.test( name ) ) {
|
|
12
|
-
return
|
|
46
|
+
return ERROR_MESSAGES.INVALID_CHARACTERS;
|
|
13
47
|
}
|
|
14
48
|
|
|
15
49
|
const hasAlphanumeric = /[a-zA-Z0-9]/;
|
|
16
50
|
if ( ! hasAlphanumeric.test( name ) ) {
|
|
17
|
-
return
|
|
51
|
+
return ERROR_MESSAGES.NO_NON_SPECIAL_CHARACTER;
|
|
18
52
|
}
|
|
19
53
|
|
|
20
54
|
if ( VARIABLE_LABEL_MAX_LENGTH < name.length ) {
|
|
21
|
-
return
|
|
55
|
+
return ERROR_MESSAGES.VARIABLE_LABEL_MAX_LENGTH;
|
|
22
56
|
}
|
|
23
57
|
|
|
24
58
|
return '';
|
|
@@ -27,7 +61,7 @@ export const validateLabel = ( name: string ): string => {
|
|
|
27
61
|
export const labelHint = ( name: string ): string => {
|
|
28
62
|
const hintThreshold = VARIABLE_LABEL_MAX_LENGTH * 0.8 - 1;
|
|
29
63
|
if ( hintThreshold < name.length ) {
|
|
30
|
-
return
|
|
64
|
+
return ERROR_MESSAGES.VARIABLE_LABEL_MAX_LENGTH;
|
|
31
65
|
}
|
|
32
66
|
|
|
33
67
|
return '';
|
|
@@ -35,7 +69,7 @@ export const labelHint = ( name: string ): string => {
|
|
|
35
69
|
|
|
36
70
|
export const validateValue = ( value: string ): string => {
|
|
37
71
|
if ( ! value.trim() ) {
|
|
38
|
-
return
|
|
72
|
+
return ERROR_MESSAGES.MISSING_VARIABLE_VALUE;
|
|
39
73
|
}
|
|
40
74
|
|
|
41
75
|
return '';
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { type ForwardRefExoticComponent, type JSX, type RefAttributes } from 'react';
|
|
2
|
+
import { styleTransformersRegistry } from '@elementor/editor-canvas';
|
|
3
|
+
import { stylesInheritanceTransformersRegistry } from '@elementor/editor-editing-panel';
|
|
4
|
+
import { type createPropUtils, type PropTypeKey, type PropTypeUtil } from '@elementor/editor-props';
|
|
5
|
+
import { type SvgIconProps } from '@elementor/ui';
|
|
6
|
+
|
|
7
|
+
import { inheritanceTransformer } from '../transformers/inheritance-transformer';
|
|
8
|
+
import { variableTransformer } from '../transformers/variable-transformer';
|
|
9
|
+
|
|
10
|
+
type ValueFieldProps = {
|
|
11
|
+
value: string;
|
|
12
|
+
onChange: ( value: string ) => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type FallbackPropTypeUtil = ReturnType< typeof createPropUtils >;
|
|
16
|
+
|
|
17
|
+
type VariableTypeOptions = {
|
|
18
|
+
icon: ForwardRefExoticComponent< Omit< SvgIconProps, 'ref' > & RefAttributes< SVGSVGElement > >;
|
|
19
|
+
startIcon?: ( { value }: { value: string } ) => JSX.Element;
|
|
20
|
+
valueField: ( { value, onChange }: ValueFieldProps ) => JSX.Element;
|
|
21
|
+
variableType: string;
|
|
22
|
+
fallbackPropTypeUtil: FallbackPropTypeUtil;
|
|
23
|
+
propTypeUtil: PropTypeUtil< string, string >;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type VariableTypesMap = Record< string, VariableTypeOptions >;
|
|
27
|
+
|
|
28
|
+
export function createVariableTypeRegistry() {
|
|
29
|
+
const variableTypes: VariableTypesMap = {};
|
|
30
|
+
|
|
31
|
+
const registerVariableType = ( {
|
|
32
|
+
icon,
|
|
33
|
+
startIcon,
|
|
34
|
+
valueField,
|
|
35
|
+
propTypeUtil,
|
|
36
|
+
variableType,
|
|
37
|
+
fallbackPropTypeUtil,
|
|
38
|
+
}: VariableTypeOptions ) => {
|
|
39
|
+
if ( variableTypes[ propTypeUtil.key ] ) {
|
|
40
|
+
throw new Error( `Variable with key "${ propTypeUtil.key }" is already registered.` );
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
variableTypes[ propTypeUtil.key ] = {
|
|
44
|
+
icon,
|
|
45
|
+
startIcon,
|
|
46
|
+
valueField,
|
|
47
|
+
propTypeUtil,
|
|
48
|
+
variableType,
|
|
49
|
+
fallbackPropTypeUtil,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
registerTransformer( propTypeUtil.key );
|
|
53
|
+
registerInheritanceTransformer( propTypeUtil.key );
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const registerTransformer = ( key: PropTypeKey ) => {
|
|
57
|
+
styleTransformersRegistry.register( key, variableTransformer );
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const registerInheritanceTransformer = ( key: PropTypeKey ) => {
|
|
61
|
+
stylesInheritanceTransformersRegistry.register( key, inheritanceTransformer );
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const getVariableType = ( key: string ) => {
|
|
65
|
+
return variableTypes[ key ];
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const hasVariableType = ( key: string ) => {
|
|
69
|
+
return key in variableTypes;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
registerVariableType,
|
|
74
|
+
getVariableType,
|
|
75
|
+
hasVariableType,
|
|
76
|
+
};
|
|
77
|
+
}
|