@elementor/editor-variables 0.12.0 → 0.14.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/dist/index.js +918 -332
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +961 -329
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +10 -9
  7. package/src/components/color-variable-creation.tsx +65 -64
  8. package/src/components/color-variable-edit.tsx +117 -0
  9. package/src/components/color-variables-selection.tsx +98 -52
  10. package/src/components/font-variable-creation.tsx +143 -0
  11. package/src/components/font-variable-edit.tsx +146 -0
  12. package/src/components/font-variables-selection.tsx +97 -51
  13. package/src/components/ui/menu-item-content.tsx +51 -0
  14. package/src/components/ui/no-search-results.tsx +38 -0
  15. package/src/components/ui/no-variables.tsx +35 -0
  16. package/src/components/ui/styled-menu-list.tsx +31 -0
  17. package/src/components/variable-selection-popover.tsx +133 -0
  18. package/src/components/variables-repeater-item-slot.tsx +29 -0
  19. package/src/controls/color-variable-control.tsx +90 -0
  20. package/src/controls/font-variable-control.tsx +88 -0
  21. package/src/create-style-variables-repository.ts +3 -2
  22. package/src/hooks/use-prop-color-variable-action.tsx +7 -2
  23. package/src/hooks/use-prop-font-variable-action.tsx +7 -2
  24. package/src/hooks/use-prop-variables.ts +31 -4
  25. package/src/init-color-variables.ts +51 -3
  26. package/src/init-font-variables.ts +2 -2
  27. package/src/service.ts +23 -3
  28. package/src/storage.ts +5 -1
  29. package/src/types.ts +12 -8
  30. package/src/components/styled-menu-item.tsx +0 -10
  31. package/src/components/variables-selection-popover.tsx +0 -106
  32. package/src/controls/color-variables-selection-control.tsx +0 -34
  33. package/src/controls/font-variables-selection-control.tsx +0 -29
  34. /package/src/components/{color-indicator.tsx → ui/color-indicator.tsx} +0 -0
@@ -3,7 +3,8 @@ import { type PopoverActionProps, useBoundProp } from '@elementor/editor-editing
3
3
  import { ColorFilterIcon } from '@elementor/icons';
4
4
  import { __ } from '@wordpress/i18n';
5
5
 
6
- import { ColorVariablesSelection } from '../components/color-variables-selection';
6
+ import { VariableSelectionPopover } from '../components/variable-selection-popover';
7
+ import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
7
8
  import { supportsColorVariables } from '../utils';
8
9
 
9
10
  export const usePropColorVariableAction = (): PopoverActionProps => {
@@ -15,6 +16,10 @@ export const usePropColorVariableAction = (): PopoverActionProps => {
15
16
  visible,
16
17
  icon: ColorFilterIcon,
17
18
  title: __( 'Variables', 'elementor' ),
18
- popoverContent: ( { closePopover } ) => <ColorVariablesSelection onSelect={ closePopover } />,
19
+ content: ( { close: closePopover } ) => {
20
+ return (
21
+ <VariableSelectionPopover closePopover={ closePopover } propTypeKey={ colorVariablePropTypeUtil.key } />
22
+ );
23
+ },
19
24
  };
20
25
  };
@@ -3,7 +3,8 @@ import { type PopoverActionProps, useBoundProp } from '@elementor/editor-editing
3
3
  import { ColorFilterIcon } from '@elementor/icons';
4
4
  import { __ } from '@wordpress/i18n';
5
5
 
6
- import { FontVariablesSelection } from '../components/font-variables-selection';
6
+ import { VariableSelectionPopover } from '../components/variable-selection-popover';
7
+ import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
7
8
  import { supportsFontVariables } from '../utils';
8
9
 
9
10
  export const usePropFontVariableAction = (): PopoverActionProps => {
@@ -15,6 +16,10 @@ export const usePropFontVariableAction = (): PopoverActionProps => {
15
16
  visible,
16
17
  icon: ColorFilterIcon,
17
18
  title: __( 'Variables', 'elementor' ),
18
- popoverContent: ( { closePopover } ) => <FontVariablesSelection onSelect={ closePopover } />,
19
+ content: ( { close: closePopover } ) => {
20
+ return (
21
+ <VariableSelectionPopover closePopover={ closePopover } propTypeKey={ fontVariablePropTypeUtil.key } />
22
+ );
23
+ },
19
24
  };
20
25
  };
@@ -6,10 +6,6 @@ import { type TVariable } from '../storage';
6
6
  import { styleVariablesRepository } from '../style-variables-repository';
7
7
  import { type Variable } from '../types';
8
8
 
9
- export const usePropVariables = ( propKey: PropKey ) => {
10
- return useMemo( () => normalizeVariables( propKey ), [ propKey ] );
11
- };
12
-
13
9
  export const useVariable = ( key: string ) => {
14
10
  const variables = service.variables();
15
11
 
@@ -23,6 +19,24 @@ export const useVariable = ( key: string ) => {
23
19
  };
24
20
  };
25
21
 
22
+ export const useFilteredVariables = ( searchValue: string, propTypeKey: string ) => {
23
+ const variables = usePropVariables( propTypeKey );
24
+
25
+ const filteredVariables = variables.filter( ( { label } ) => {
26
+ return label.toLowerCase().includes( searchValue.toLowerCase() );
27
+ } );
28
+
29
+ return {
30
+ list: filteredVariables,
31
+ hasMatches: filteredVariables.length > 0,
32
+ isSourceNotEmpty: variables.length > 0,
33
+ };
34
+ };
35
+
36
+ const usePropVariables = ( propKey: PropKey ) => {
37
+ return useMemo( () => normalizeVariables( propKey ), [ propKey ] );
38
+ };
39
+
26
40
  const normalizeVariables = ( propKey: string ) => {
27
41
  const variables = service.variables();
28
42
 
@@ -42,6 +56,19 @@ export const createVariable = ( newVariable: Variable ): Promise< string > => {
42
56
  styleVariablesRepository.update( {
43
57
  [ id ]: variable,
44
58
  } );
59
+
45
60
  return id;
46
61
  } );
47
62
  };
63
+
64
+ export const updateVariable = ( updateId: string, { value, label }: { value: string; label: string } ) => {
65
+ return service
66
+ .update( updateId, { value, label } )
67
+ .then( ( { id, variable }: { id: string; variable: TVariable } ) => {
68
+ styleVariablesRepository.update( {
69
+ [ id ]: variable,
70
+ } );
71
+
72
+ return id;
73
+ } );
74
+ };
@@ -1,7 +1,14 @@
1
1
  import { styleTransformersRegistry } from '@elementor/editor-canvas';
2
+ import { injectIntoRepeaterItemIcon, injectIntoRepeaterItemLabel } from '@elementor/editor-controls';
2
3
  import { controlActionsMenu, registerControlReplacement } from '@elementor/editor-editing-panel';
4
+ import { backgroundColorOverlayPropTypeUtil, type PropValue, shadowPropTypeUtil } from '@elementor/editor-props';
3
5
 
4
- import { ColorVariablesSelectionControl } from './controls/color-variables-selection-control';
6
+ import {
7
+ BackgroundRepeaterColorIndicator,
8
+ BackgroundRepeaterLabel,
9
+ BoxShadowRepeaterColorIndicator,
10
+ } from './components/variables-repeater-item-slot';
11
+ import { ColorVariableControl } from './controls/color-variable-control';
5
12
  import { usePropColorVariableAction } from './hooks/use-prop-color-variable-action';
6
13
  import { colorVariablePropTypeUtil } from './prop-types/color-variable-prop-type';
7
14
  import { variableTransformer } from './transformers/variable-transformer';
@@ -9,9 +16,19 @@ import { hasAssignedColorVariable } from './utils';
9
16
 
10
17
  const { registerPopoverAction } = controlActionsMenu;
11
18
 
12
- export function initColorVariables() {
19
+ const conditions = {
20
+ backgroundOverlay: ( { value: prop }: { value: PropValue } ) => {
21
+ return hasAssignedColorVariable( backgroundColorOverlayPropTypeUtil.extract( prop )?.color );
22
+ },
23
+
24
+ boxShadow: ( { value: prop }: { value: PropValue } ) => {
25
+ return hasAssignedColorVariable( shadowPropTypeUtil.extract( prop )?.color );
26
+ },
27
+ };
28
+
29
+ function registerControlsAndActions() {
13
30
  registerControlReplacement( {
14
- component: ColorVariablesSelectionControl,
31
+ component: ColorVariableControl,
15
32
  condition: ( { value } ) => hasAssignedColorVariable( value ),
16
33
  } );
17
34
 
@@ -19,6 +36,37 @@ export function initColorVariables() {
19
36
  id: 'color-variables',
20
37
  useProps: usePropColorVariableAction,
21
38
  } );
39
+ }
40
+
41
+ function registerRepeaterItemIcons() {
42
+ injectIntoRepeaterItemIcon( {
43
+ id: 'color-variables-background-icon',
44
+ component: BackgroundRepeaterColorIndicator,
45
+ condition: conditions.backgroundOverlay,
46
+ } );
22
47
 
48
+ injectIntoRepeaterItemIcon( {
49
+ id: 'color-variables-icon',
50
+ component: BoxShadowRepeaterColorIndicator,
51
+ condition: conditions.boxShadow,
52
+ } );
53
+ }
54
+
55
+ function registerRepeaterItemLabels() {
56
+ injectIntoRepeaterItemLabel( {
57
+ id: 'color-variables-label',
58
+ component: BackgroundRepeaterLabel,
59
+ condition: conditions.backgroundOverlay,
60
+ } );
61
+ }
62
+
63
+ function registerStyleTransformers() {
23
64
  styleTransformersRegistry.register( colorVariablePropTypeUtil.key, variableTransformer );
24
65
  }
66
+
67
+ export function initColorVariables() {
68
+ registerControlsAndActions();
69
+ registerRepeaterItemIcons();
70
+ registerRepeaterItemLabels();
71
+ registerStyleTransformers();
72
+ }
@@ -1,7 +1,7 @@
1
1
  import { styleTransformersRegistry } from '@elementor/editor-canvas';
2
2
  import { controlActionsMenu, registerControlReplacement } from '@elementor/editor-editing-panel';
3
3
 
4
- import { FontVariablesSelectionControl } from './controls/font-variables-selection-control';
4
+ import { FontVariableControl } from './controls/font-variable-control';
5
5
  import { usePropFontVariableAction } from './hooks/use-prop-font-variable-action';
6
6
  import { fontVariablePropTypeUtil } from './prop-types/font-variable-prop-type';
7
7
  import { variableTransformer } from './transformers/variable-transformer';
@@ -11,7 +11,7 @@ const { registerPopoverAction } = controlActionsMenu;
11
11
 
12
12
  export function initFontVariables() {
13
13
  registerControlReplacement( {
14
- component: FontVariablesSelectionControl,
14
+ component: FontVariableControl,
15
15
  condition: ( { value } ) => hasAssignedFontVariable( value ),
16
16
  } );
17
17
 
package/src/service.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { apiClient } from './api';
2
- import { OP_RW, Storage, type TVariable, type TVariablesList } from './storage';
2
+ import { OP_RW, Storage, type TVariablesList } from './storage';
3
+ import { styleVariablesRepository } from './style-variables-repository';
4
+ import { type Variable } from './types';
3
5
 
4
6
  const storage = new Storage();
5
7
 
@@ -29,11 +31,13 @@ export const service = {
29
31
 
30
32
  storage.fill( variables, watermark );
31
33
 
34
+ styleVariablesRepository.update( variables );
35
+
32
36
  return variables;
33
37
  } );
34
38
  },
35
39
 
36
- create: ( { type, label, value }: TVariable ) => {
40
+ create: ( { type, label, value }: Variable ) => {
37
41
  return apiClient
38
42
  .create( type, label, value )
39
43
  .then( ( response ) => {
@@ -54,6 +58,10 @@ export const service = {
54
58
 
55
59
  storage.add( variableId, createdVariable );
56
60
 
61
+ styleVariablesRepository.update( {
62
+ [ variableId ]: createdVariable,
63
+ } );
64
+
57
65
  return {
58
66
  id: variableId,
59
67
  variable: createdVariable,
@@ -61,7 +69,7 @@ export const service = {
61
69
  } );
62
70
  },
63
71
 
64
- update: ( id: string, { label, value }: TVariable ) => {
72
+ update: ( id: string, { label, value }: Omit< Variable, 'type' > ) => {
65
73
  return apiClient
66
74
  .update( id, label, value )
67
75
  .then( ( response ) => {
@@ -82,6 +90,10 @@ export const service = {
82
90
 
83
91
  storage.update( variableId, updatedVariable );
84
92
 
93
+ styleVariablesRepository.update( {
94
+ [ variableId ]: updatedVariable,
95
+ } );
96
+
85
97
  return {
86
98
  id: variableId,
87
99
  variable: updatedVariable,
@@ -110,6 +122,10 @@ export const service = {
110
122
 
111
123
  storage.update( variableId, deletedVariable );
112
124
 
125
+ styleVariablesRepository.update( {
126
+ [ variableId ]: deletedVariable,
127
+ } );
128
+
113
129
  return {
114
130
  id: variableId,
115
131
  variable: deletedVariable,
@@ -138,6 +154,10 @@ export const service = {
138
154
 
139
155
  storage.update( variableId, restoredVariable );
140
156
 
157
+ styleVariablesRepository.update( {
158
+ [ variableId ]: restoredVariable,
159
+ } );
160
+
141
161
  return {
142
162
  id: variableId,
143
163
  variable: restoredVariable,
package/src/storage.ts CHANGED
@@ -34,8 +34,12 @@ export class Storage {
34
34
  }
35
35
 
36
36
  fill( variables: TVariablesList, watermark: number ) {
37
+ this.state.variables = {};
38
+ if ( variables && Object.keys( variables ).length ) {
39
+ this.state.variables = variables;
40
+ }
41
+
37
42
  this.state.watermark = watermark;
38
- this.state.variables = variables;
39
43
 
40
44
  localStorage.setItem( STORAGE_WATERMARK_KEY, this.state.watermark.toString() );
41
45
  localStorage.setItem( STORAGE_KEY, JSON.stringify( this.state.variables ) );
package/src/types.ts CHANGED
@@ -1,15 +1,19 @@
1
- export type VariableKey = string;
2
-
3
- export type VariableValue = string;
1
+ import type * as React from 'react';
2
+ import { type VirtualizedItem } from '@elementor/editor-ui';
4
3
 
5
4
  export type Variable = {
6
- value: VariableValue;
5
+ key?: string;
7
6
  label: string;
7
+ value: string;
8
8
  type: string;
9
+ deleted?: boolean;
10
+ deleted_at?: string;
9
11
  };
10
12
 
11
- export type Variables = {
12
- [ key: VariableKey ]: Variable;
13
- };
13
+ export type StyleVariables = Record< string, string >;
14
14
 
15
- export type StyleVariables = Record< VariableKey, VariableValue >;
15
+ export type ExtendedVirtualizedItem = VirtualizedItem< 'item', string > & {
16
+ icon: React.ReactNode;
17
+ secondaryText: string;
18
+ onEdit?: () => void;
19
+ };
@@ -1,10 +0,0 @@
1
- import { MenuItem, styled } from '@elementor/ui';
2
-
3
- export const StyledMenuItem = styled( MenuItem )( () => ( {
4
- pl: 2,
5
- pr: 1,
6
- py: 0.5,
7
- '&:hover .MuiSvgIcon-root': {
8
- opacity: 1,
9
- },
10
- } ) );
@@ -1,106 +0,0 @@
1
- import * as React from 'react';
2
- import { useId, useRef } from 'react';
3
- import { PopoverHeader } from '@elementor/editor-ui';
4
- import { ColorFilterIcon, DetachIcon, PlusIcon } from '@elementor/icons';
5
- import {
6
- bindPopover,
7
- bindTrigger,
8
- Box,
9
- IconButton,
10
- Popover,
11
- Stack,
12
- Typography,
13
- UnstableTag as Tag,
14
- usePopupState,
15
- } from '@elementor/ui';
16
- import { __ } from '@wordpress/i18n';
17
-
18
- import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
19
- import { type Variable } from '../types';
20
- import { ColorVariableCreation } from './color-variable-creation';
21
-
22
- type Props = {
23
- selectedVariable: Variable;
24
- unlinkVariable: () => void;
25
- children: ( { closePopover }: { closePopover: () => void } ) => React.ReactNode;
26
- startTagAdornment?: React.ReactNode;
27
- };
28
-
29
- const SIZE = 'tiny';
30
-
31
- export const VariablesSelectionPopover = ( {
32
- selectedVariable,
33
- unlinkVariable,
34
- startTagAdornment,
35
- children,
36
- }: Props ) => {
37
- const id = useId();
38
- const popupState = usePopupState( { variant: 'popover', popupId: `elementor-variables-action-${ id }` } );
39
- const creationPopupState = usePopupState( { variant: 'popover', popupId: `elementor-variables-creation-${ id }` } );
40
-
41
- const closePopover = () => popupState.close();
42
-
43
- const handleCreateButtonClick = ( event: React.MouseEvent ) => {
44
- closePopover();
45
- bindTrigger( creationPopupState ).onClick( event );
46
- };
47
-
48
- const anchorRef = useRef< HTMLDivElement >( null );
49
- const { label } = selectedVariable;
50
-
51
- const colorCreationEnabled = colorVariablePropTypeUtil.key === selectedVariable.type;
52
-
53
- return (
54
- <Box ref={ anchorRef }>
55
- <Tag
56
- fullWidth
57
- showActionsOnHover
58
- { ...bindTrigger( popupState ) }
59
- startIcon={
60
- <Stack spacing={ 1 } direction="row" alignItems="center">
61
- { startTagAdornment }
62
- <ColorFilterIcon fontSize={ 'inherit' } sx={ { mr: 1 } } />
63
- </Stack>
64
- }
65
- label={
66
- <Box sx={ { display: 'inline-grid' } }>
67
- <Typography sx={ { textOverflow: 'ellipsis', overflowX: 'hidden' } } variant="subtitle2">
68
- { label }
69
- </Typography>
70
- </Box>
71
- }
72
- actions={
73
- <IconButton size={ SIZE } onClick={ unlinkVariable } aria-label={ __( 'Unlink', 'elementor' ) }>
74
- <DetachIcon fontSize={ SIZE } />
75
- </IconButton>
76
- }
77
- />
78
- <Popover
79
- { ...bindPopover( popupState ) }
80
- disableScrollLock
81
- anchorEl={ anchorRef.current }
82
- anchorOrigin={ { vertical: 'bottom', horizontal: 'right' } }
83
- transformOrigin={ { vertical: 'top', horizontal: 'right' } }
84
- >
85
- <PopoverHeader
86
- title={ __( 'Variables', 'elementor' ) }
87
- onClose={ closePopover }
88
- icon={ <ColorFilterIcon fontSize={ SIZE } /> }
89
- actions={ [
90
- <IconButton
91
- key="createVariable"
92
- { ...bindTrigger( creationPopupState ) }
93
- size={ SIZE }
94
- onClick={ handleCreateButtonClick }
95
- >
96
- <PlusIcon fontSize={ SIZE } />
97
- </IconButton>,
98
- ] }
99
- />
100
- { children?.( { closePopover } ) }
101
- </Popover>
102
-
103
- { colorCreationEnabled && <ColorVariableCreation popupState={ creationPopupState } /> }
104
- </Box>
105
- );
106
- };
@@ -1,34 +0,0 @@
1
- import * as React from 'react';
2
- import { useBoundProp } from '@elementor/editor-controls';
3
- import { colorPropTypeUtil } from '@elementor/editor-props';
4
-
5
- import { ColorIndicator } from '../components/color-indicator';
6
- import { ColorVariablesSelection } from '../components/color-variables-selection';
7
- import { VariablesSelectionPopover } from '../components/variables-selection-popover';
8
- import { useVariable } from '../hooks/use-prop-variables';
9
- import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
10
-
11
- export const ColorVariablesSelectionControl = () => {
12
- const { setValue } = useBoundProp();
13
- const { value: variableValue } = useBoundProp( colorVariablePropTypeUtil );
14
-
15
- const selectedVariable = useVariable( variableValue );
16
-
17
- if ( ! selectedVariable ) {
18
- throw new Error( `Global color variable ${ variableValue } not found` );
19
- }
20
-
21
- const unlinkVariable = () => {
22
- setValue( colorPropTypeUtil.create( selectedVariable.value ) );
23
- };
24
-
25
- return (
26
- <VariablesSelectionPopover
27
- selectedVariable={ selectedVariable }
28
- unlinkVariable={ unlinkVariable }
29
- startTagAdornment={ <ColorIndicator size="inherit" component="span" value={ selectedVariable.value } /> }
30
- >
31
- { ( { closePopover } ) => <ColorVariablesSelection onSelect={ closePopover } /> }
32
- </VariablesSelectionPopover>
33
- );
34
- };
@@ -1,29 +0,0 @@
1
- import * as React from 'react';
2
- import { useBoundProp } from '@elementor/editor-controls';
3
- import { stringPropTypeUtil } from '@elementor/editor-props';
4
-
5
- import { FontVariablesSelection } from '../components/font-variables-selection';
6
- import { VariablesSelectionPopover } from '../components/variables-selection-popover';
7
- import { useVariable } from '../hooks/use-prop-variables';
8
- import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
9
-
10
- export const FontVariablesSelectionControl = () => {
11
- const { setValue: setFontFamily } = useBoundProp();
12
- const { value: variableValue } = useBoundProp( fontVariablePropTypeUtil );
13
-
14
- const selectedVariable = useVariable( variableValue );
15
-
16
- if ( ! selectedVariable ) {
17
- throw new Error( `Global font variable ${ variableValue } not found` );
18
- }
19
-
20
- const unlinkVariable = () => {
21
- setFontFamily( stringPropTypeUtil.create( selectedVariable.value ) );
22
- };
23
-
24
- return (
25
- <VariablesSelectionPopover selectedVariable={ selectedVariable } unlinkVariable={ unlinkVariable }>
26
- { ( { closePopover } ) => <FontVariablesSelection onSelect={ closePopover } /> }
27
- </VariablesSelectionPopover>
28
- );
29
- };