@elementor/editor-variables 0.9.0 → 0.11.0

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.
@@ -42,14 +42,14 @@ export const ColorVariableCreation = ( { popupState }: { popupState: PopupState
42
42
  };
43
43
 
44
44
  const handleCreate = () => {
45
- const key = createVariable( {
45
+ createVariable( {
46
46
  value: color,
47
47
  label,
48
48
  type: colorVariablePropTypeUtil.key,
49
+ } ).then( ( key ) => {
50
+ setVariable( key );
51
+ closePopover();
49
52
  } );
50
-
51
- setVariable( key );
52
- closePopover();
53
53
  };
54
54
 
55
55
  const isInValidForm = () => {
@@ -15,6 +15,7 @@ import {
15
15
  } from '@elementor/ui';
16
16
  import { __ } from '@wordpress/i18n';
17
17
 
18
+ import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
18
19
  import { type Variable } from '../types';
19
20
  import { ColorVariableCreation } from './color-variable-creation';
20
21
 
@@ -45,6 +46,8 @@ export const VariablesSelectionPopover = ( {
45
46
  const anchorRef = useRef< HTMLDivElement >( null );
46
47
  const { label } = selectedVariable;
47
48
 
49
+ const colorCreationEnabled = colorVariablePropTypeUtil.key === selectedVariable.type;
50
+
48
51
  return (
49
52
  <Box ref={ anchorRef }>
50
53
  <Tag
@@ -81,20 +84,22 @@ export const VariablesSelectionPopover = ( {
81
84
  <ColorFilterIcon fontSize="tiny" sx={ { mr: 0.5 } } />
82
85
  <Typography variant="subtitle2">{ __( 'Variables', 'elementor' ) }</Typography>
83
86
  <Stack direction="row" sx={ { ml: 'auto' } }>
84
- <IconButton
85
- { ...bindTrigger( creationPopupState ) }
86
- size="tiny"
87
- onClick={ handleCreateButtonClick }
88
- >
89
- <PlusIcon fontSize="tiny" />
90
- </IconButton>
87
+ { colorCreationEnabled && (
88
+ <IconButton
89
+ { ...bindTrigger( creationPopupState ) }
90
+ size="tiny"
91
+ onClick={ handleCreateButtonClick }
92
+ >
93
+ <PlusIcon fontSize="tiny" />
94
+ </IconButton>
95
+ ) }
91
96
  <CloseButton slotProps={ { icon: { fontSize: 'tiny' } } } onClick={ closePopover } />
92
97
  </Stack>
93
98
  </Stack>
94
99
  { children?.( { closePopover } ) }
95
100
  </Popover>
96
101
 
97
- <ColorVariableCreation popupState={ creationPopupState } />
102
+ { colorCreationEnabled && <ColorVariableCreation popupState={ creationPopupState } /> }
98
103
  </Box>
99
104
  );
100
105
  };
@@ -1,14 +1,18 @@
1
1
  import { useMemo } from 'react';
2
2
  import { type PropKey } from '@elementor/editor-props';
3
3
 
4
+ import { service } from '../service';
5
+ import { type TVariable } from '../storage';
4
6
  import { styleVariablesRepository } from '../style-variables-repository';
5
- import { type Variable, type Variables } from '../types';
7
+ import { type Variable } from '../types';
6
8
 
7
9
  export const usePropVariables = ( propKey: PropKey ) => {
8
10
  return useMemo( () => normalizeVariables( propKey ), [ propKey ] );
9
11
  };
10
12
 
11
13
  export const useVariable = ( key: string ) => {
14
+ const variables = service.variables();
15
+
12
16
  if ( ! variables?.[ key ] ) {
13
17
  return null;
14
18
  }
@@ -20,6 +24,10 @@ export const useVariable = ( key: string ) => {
20
24
  };
21
25
 
22
26
  const normalizeVariables = ( propKey: string ) => {
27
+ const variables = service.variables();
28
+
29
+ styleVariablesRepository.update( variables );
30
+
23
31
  return Object.entries( variables )
24
32
  .filter( ( [ , { type } ] ) => type === propKey )
25
33
  .map( ( [ key, { label, value } ] ) => ( {
@@ -29,23 +37,11 @@ const normalizeVariables = ( propKey: string ) => {
29
37
  } ) );
30
38
  };
31
39
 
32
- export const createVariable = ( variable: Variable ) => {
33
- const id = generateId();
34
-
35
- variables[ id ] = variable;
36
-
37
- styleVariablesRepository.update( {
38
- [ id ]: variable,
40
+ export const createVariable = ( newVariable: Variable ): Promise< string > => {
41
+ return service.create( newVariable ).then( ( { id, variable }: { id: string; variable: TVariable } ) => {
42
+ styleVariablesRepository.update( {
43
+ [ id ]: variable,
44
+ } );
45
+ return id;
39
46
  } );
40
-
41
- return id;
42
- };
43
-
44
- // @ts-expect-error the temporary solution to get the list of variables from the server
45
- const variables: Variables = window?.ElementorV4Variables || {};
46
-
47
- const generateId = ( prefix = 'e-gv' ) => {
48
- const randomHex = Math.random().toString( 16 ).slice( 2, 9 );
49
-
50
- return `${ prefix }${ randomHex }`;
51
47
  };
package/src/init.ts CHANGED
@@ -3,12 +3,14 @@ import { injectIntoTop } from '@elementor/editor';
3
3
  import { initColorVariables } from './init-color-variables';
4
4
  import { initFontVariables } from './init-font-variables';
5
5
  import { StyleVariablesRenderer } from './renderers/style-variables-renderer';
6
+ import { service as variablesService } from './service';
6
7
 
7
8
  export function init() {
8
9
  initColorVariables();
9
-
10
10
  initFontVariables();
11
11
 
12
+ variablesService.init();
13
+
12
14
  injectIntoTop( {
13
15
  id: 'canvas-style-variables-render',
14
16
  component: StyleVariablesRenderer,
package/src/service.ts ADDED
@@ -0,0 +1,154 @@
1
+ import { apiClient } from './api';
2
+ import { OP_RW, Storage, type TVariable, type TVariablesList } from './storage';
3
+
4
+ const storage = new Storage();
5
+
6
+ export const service = {
7
+ variables: (): TVariablesList => {
8
+ return storage.load();
9
+ },
10
+
11
+ init: () => {
12
+ service.load();
13
+ },
14
+
15
+ load: () => {
16
+ return apiClient
17
+ .list()
18
+ .then( ( response ) => {
19
+ const { success, data: payload } = response.data;
20
+
21
+ if ( ! success ) {
22
+ throw new Error( 'Unexpected response from server' );
23
+ }
24
+
25
+ return payload;
26
+ } )
27
+ .then( ( data ) => {
28
+ const { variables, watermark } = data;
29
+
30
+ storage.fill( variables, watermark );
31
+
32
+ return variables;
33
+ } );
34
+ },
35
+
36
+ create: ( { type, label, value }: TVariable ) => {
37
+ return apiClient
38
+ .create( type, label, value )
39
+ .then( ( response ) => {
40
+ const { success, data: payload } = response.data;
41
+
42
+ if ( ! success ) {
43
+ throw new Error( 'Unexpected response from server' );
44
+ }
45
+
46
+ return payload;
47
+ } )
48
+ .then( ( data ) => {
49
+ const { variable, watermark } = data;
50
+
51
+ handleWatermark( OP_RW, watermark );
52
+
53
+ const { id: variableId, ...createdVariable } = variable;
54
+
55
+ storage.add( variableId, createdVariable );
56
+
57
+ return {
58
+ id: variableId,
59
+ variable: createdVariable,
60
+ };
61
+ } );
62
+ },
63
+
64
+ update: ( id: string, { label, value }: TVariable ) => {
65
+ return apiClient
66
+ .update( id, label, value )
67
+ .then( ( response ) => {
68
+ const { success, data: payload } = response.data;
69
+
70
+ if ( ! success ) {
71
+ throw new Error( 'Unexpected response from server' );
72
+ }
73
+
74
+ return payload;
75
+ } )
76
+ .then( ( data ) => {
77
+ const { variable, watermark } = data;
78
+
79
+ handleWatermark( OP_RW, watermark );
80
+
81
+ const { id: variableId, ...updatedVariable } = variable;
82
+
83
+ storage.update( variableId, updatedVariable );
84
+
85
+ return {
86
+ id: variableId,
87
+ variable: updatedVariable,
88
+ };
89
+ } );
90
+ },
91
+
92
+ delete: ( id: string ) => {
93
+ return apiClient
94
+ .delete( id )
95
+ .then( ( response ) => {
96
+ const { success, data: payload } = response.data;
97
+
98
+ if ( ! success ) {
99
+ throw new Error( 'Unexpected response from server' );
100
+ }
101
+
102
+ return payload;
103
+ } )
104
+ .then( ( data ) => {
105
+ const { variable, watermark } = data;
106
+
107
+ handleWatermark( OP_RW, watermark );
108
+
109
+ const { id: variableId, ...deletedVariable } = variable;
110
+
111
+ storage.update( variableId, deletedVariable );
112
+
113
+ return {
114
+ id: variableId,
115
+ variable: deletedVariable,
116
+ };
117
+ } );
118
+ },
119
+
120
+ restore: ( id: string ) => {
121
+ return apiClient
122
+ .restore( id )
123
+ .then( ( response ) => {
124
+ const { success, data: payload } = response.data;
125
+
126
+ if ( ! success ) {
127
+ throw new Error( 'Unexpected response from server' );
128
+ }
129
+
130
+ return payload;
131
+ } )
132
+ .then( ( data ) => {
133
+ const { variable, watermark } = data;
134
+
135
+ handleWatermark( OP_RW, watermark );
136
+
137
+ const { id: variableId, ...restoredVariable } = variable;
138
+
139
+ storage.update( variableId, restoredVariable );
140
+
141
+ return {
142
+ id: variableId,
143
+ variable: restoredVariable,
144
+ };
145
+ } );
146
+ },
147
+ };
148
+
149
+ const handleWatermark = ( operation: string, newWatermark: number ) => {
150
+ if ( storage.watermarkDiff( operation, newWatermark ) ) {
151
+ setTimeout( () => service.load(), 500 );
152
+ }
153
+ storage.watermark( newWatermark );
154
+ };
package/src/storage.ts ADDED
@@ -0,0 +1,74 @@
1
+ export type TVariable = {
2
+ type: string;
3
+ label: string;
4
+ value: string;
5
+ deleted?: boolean;
6
+ deleted_at?: string;
7
+ };
8
+
9
+ export type TVariablesList = Record< string, TVariable >;
10
+
11
+ const STORAGE_KEY = 'elementor-global-variables';
12
+ const STORAGE_WATERMARK_KEY = 'elementor-global-variables-watermark';
13
+
14
+ export const OP_RW = 'RW';
15
+ const OP_RO = 'RO';
16
+
17
+ export class Storage {
18
+ state: {
19
+ watermark: number;
20
+ variables: TVariablesList;
21
+ };
22
+
23
+ constructor() {
24
+ this.state = {
25
+ watermark: -1,
26
+ variables: {},
27
+ };
28
+ }
29
+
30
+ load() {
31
+ this.state.watermark = parseInt( localStorage.getItem( STORAGE_WATERMARK_KEY ) || '-1' );
32
+ this.state.variables = JSON.parse( localStorage.getItem( STORAGE_KEY ) || '{}' ) as TVariablesList;
33
+ return this.state.variables;
34
+ }
35
+
36
+ fill( variables: TVariablesList, watermark: number ) {
37
+ this.state.watermark = watermark;
38
+ this.state.variables = variables;
39
+
40
+ localStorage.setItem( STORAGE_WATERMARK_KEY, this.state.watermark.toString() );
41
+ localStorage.setItem( STORAGE_KEY, JSON.stringify( this.state.variables ) );
42
+ }
43
+
44
+ add( id: string, variable: TVariable ) {
45
+ this.load();
46
+ this.state.variables[ id ] = variable;
47
+ localStorage.setItem( STORAGE_KEY, JSON.stringify( this.state.variables ) );
48
+ }
49
+
50
+ update( id: string, variable: TVariable ) {
51
+ this.load();
52
+ this.state.variables[ id ] = variable;
53
+ localStorage.setItem( STORAGE_KEY, JSON.stringify( this.state.variables ) );
54
+ }
55
+
56
+ watermark( watermark: number ) {
57
+ this.state.watermark = watermark;
58
+ localStorage.setItem( STORAGE_WATERMARK_KEY, this.state.watermark.toString() );
59
+ }
60
+
61
+ watermarkDiff( operation: string, newWatermark: number ) {
62
+ const diff = newWatermark - this.state.watermark;
63
+
64
+ if ( OP_RW === operation ) {
65
+ return 1 !== diff;
66
+ }
67
+
68
+ if ( OP_RO === operation ) {
69
+ return 0 !== diff;
70
+ }
71
+
72
+ return false;
73
+ }
74
+ }