@elementor/editor-variables 0.14.0 → 0.16.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 (32) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/dist/index.js +743 -500
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +720 -528
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +10 -10
  7. package/src/components/color-variable-creation.tsx +18 -57
  8. package/src/components/color-variable-edit.tsx +100 -79
  9. package/src/components/color-variables-selection.tsx +33 -20
  10. package/src/components/fields/color-field.tsx +54 -0
  11. package/src/components/fields/font-field.tsx +85 -0
  12. package/src/components/fields/label-field.tsx +54 -0
  13. package/src/components/font-variable-creation.tsx +19 -76
  14. package/src/components/font-variable-edit.tsx +98 -106
  15. package/src/components/font-variables-selection.tsx +33 -20
  16. package/src/components/ui/color-indicator.tsx +1 -0
  17. package/src/components/ui/delete-confirmation-dialog.tsx +55 -0
  18. package/src/components/ui/menu-item-content.tsx +29 -23
  19. package/src/components/ui/no-search-results.tsx +6 -8
  20. package/src/components/ui/no-variables.tsx +3 -2
  21. package/src/components/ui/tags/assigned-tag.tsx +43 -0
  22. package/src/components/ui/tags/deleted-tag.tsx +26 -0
  23. package/src/components/ui/variable/assigned-variable.tsx +70 -0
  24. package/src/components/ui/variable/deleted-variable.tsx +20 -0
  25. package/src/components/variable-selection-popover.context.ts +7 -0
  26. package/src/components/variable-selection-popover.tsx +17 -8
  27. package/src/controls/color-variable-control.tsx +15 -72
  28. package/src/controls/font-variable-control.tsx +14 -71
  29. package/src/hooks/use-prop-variables.ts +12 -18
  30. package/src/init-color-variables.ts +3 -48
  31. package/src/repeater-injections.ts +35 -0
  32. package/src/utils/validations.ts +42 -0
@@ -0,0 +1,43 @@
1
+ import * as React from 'react';
2
+ import { DetachIcon } from '@elementor/icons';
3
+ import { Box, IconButton, Stack, Typography, UnstableTag as Tag, type UnstableTagProps } from '@elementor/ui';
4
+ import { __ } from '@wordpress/i18n';
5
+
6
+ export const SIZE = 'tiny';
7
+
8
+ interface VariableTagProps extends UnstableTagProps {
9
+ onUnlink?: () => void;
10
+ }
11
+
12
+ export const AssignedTag = ( { startIcon, label, onUnlink, ...props }: VariableTagProps ) => {
13
+ const actions = [];
14
+
15
+ if ( onUnlink ) {
16
+ actions.push(
17
+ <IconButton key="unlink" size={ SIZE } onClick={ onUnlink } aria-label={ __( 'Unlink', 'elementor' ) }>
18
+ <DetachIcon fontSize={ SIZE } />
19
+ </IconButton>
20
+ );
21
+ }
22
+
23
+ return (
24
+ <Tag
25
+ fullWidth
26
+ showActionsOnHover
27
+ startIcon={
28
+ <Stack gap={ 0.5 } direction="row" alignItems="center">
29
+ { startIcon }
30
+ </Stack>
31
+ }
32
+ label={
33
+ <Box sx={ { display: 'inline-grid', minWidth: 0 } }>
34
+ <Typography sx={ { lineHeight: 1.34 } } variant="caption" noWrap>
35
+ { label }
36
+ </Typography>
37
+ </Box>
38
+ }
39
+ actions={ actions }
40
+ { ...props }
41
+ />
42
+ );
43
+ };
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+ import { ColorFilterIcon } from '@elementor/icons';
3
+ import { Box, Typography, UnstableTag as Tag, type UnstableTagProps } from '@elementor/ui';
4
+ import { __ } from '@wordpress/i18n';
5
+
6
+ export const DeletedTag = ( { label }: UnstableTagProps ) => {
7
+ return (
8
+ <Tag
9
+ showActionsOnHover
10
+ fullWidth
11
+ label={
12
+ <Box sx={ { display: 'inline-grid', minWidth: 0 } }>
13
+ <Typography sx={ { lineHeight: 1.34 } } variant="caption" noWrap>
14
+ { label }
15
+ </Typography>
16
+ </Box>
17
+ }
18
+ startIcon={ <ColorFilterIcon fontSize="tiny" /> }
19
+ endAdornment={
20
+ <Typography sx={ { lineHeight: 1.34 } } variant="caption" noWrap>
21
+ ({ __( 'deleted', 'elementor' ) })
22
+ </Typography>
23
+ }
24
+ />
25
+ );
26
+ };
@@ -0,0 +1,70 @@
1
+ import { useId, useRef } from 'react';
2
+ import * as React from 'react';
3
+ import { useBoundProp } from '@elementor/editor-controls';
4
+ import { type PropTypeUtil } from '@elementor/editor-props';
5
+ import { ColorFilterIcon } from '@elementor/icons';
6
+ import { bindPopover, bindTrigger, Box, Popover, usePopupState } from '@elementor/ui';
7
+
8
+ import { type Variable } from '../../../types';
9
+ import { VariableSelectionPopover } from '../../variable-selection-popover';
10
+ import { AssignedTag, SIZE } from '../tags/assigned-tag';
11
+
12
+ type Props = {
13
+ variablePropTypeUtil: PropTypeUtil< string, string >;
14
+ fallbackPropTypeUtil: PropTypeUtil< string, string | null > | PropTypeUtil< string, string >;
15
+ additionalStartIcon?: React.ReactNode;
16
+ variable: Variable;
17
+ };
18
+
19
+ export const AssignedVariable = ( {
20
+ variable,
21
+ variablePropTypeUtil,
22
+ fallbackPropTypeUtil,
23
+ additionalStartIcon,
24
+ }: Props ) => {
25
+ const { setValue } = useBoundProp();
26
+ const anchorRef = useRef< HTMLDivElement >( null );
27
+
28
+ const popupId = useId();
29
+ const popupState = usePopupState( {
30
+ variant: 'popover',
31
+ popupId: `elementor-variables-list-${ popupId }`,
32
+ } );
33
+
34
+ const unlinkVariable = () => {
35
+ setValue( fallbackPropTypeUtil.create( variable.value ) );
36
+ };
37
+
38
+ return (
39
+ <Box ref={ anchorRef }>
40
+ <AssignedTag
41
+ label={ variable.label }
42
+ startIcon={
43
+ <>
44
+ <ColorFilterIcon fontSize={ SIZE } />
45
+
46
+ { additionalStartIcon }
47
+ </>
48
+ }
49
+ onUnlink={ unlinkVariable }
50
+ { ...bindTrigger( popupState ) }
51
+ />
52
+ <Popover
53
+ disableScrollLock
54
+ anchorEl={ anchorRef.current }
55
+ anchorOrigin={ { vertical: 'bottom', horizontal: 'right' } }
56
+ transformOrigin={ { vertical: 'top', horizontal: 'right' } }
57
+ PaperProps={ {
58
+ sx: { my: 1 },
59
+ } }
60
+ { ...bindPopover( popupState ) }
61
+ >
62
+ <VariableSelectionPopover
63
+ selectedVariable={ variable }
64
+ closePopover={ popupState.close }
65
+ propTypeKey={ variablePropTypeUtil.key }
66
+ />
67
+ </Popover>
68
+ </Box>
69
+ );
70
+ };
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+ import { useRef } from 'react';
3
+ import { Box } from '@elementor/ui';
4
+
5
+ import { type Variable } from '../../../types';
6
+ import { DeletedTag } from '../tags/deleted-tag';
7
+
8
+ type Props = {
9
+ variable: Variable;
10
+ };
11
+
12
+ export const DeletedVariable = ( { variable }: Props ) => {
13
+ const anchorRef = useRef< HTMLDivElement >( null );
14
+
15
+ return (
16
+ <Box ref={ anchorRef }>
17
+ <DeletedTag label={ variable.label } />
18
+ </Box>
19
+ );
20
+ };
@@ -0,0 +1,7 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ export const PopoverContentRefContext = createContext< React.RefObject< HTMLDivElement > | null >( null );
4
+
5
+ export const usePopoverContentRef = () => {
6
+ return useContext( PopoverContentRefContext );
7
+ };
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { useRef, useState } from 'react';
3
+ import { Box } from '@elementor/ui';
3
4
 
4
5
  import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
5
6
  import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
@@ -10,6 +11,7 @@ import { ColorVariablesSelection } from './color-variables-selection';
10
11
  import { FontVariableCreation } from './font-variable-creation';
11
12
  import { FontVariableEdit } from './font-variable-edit';
12
13
  import { FontVariablesSelection } from './font-variables-selection';
14
+ import { PopoverContentRefContext } from './variable-selection-popover.context';
13
15
 
14
16
  const VIEW_LIST = 'list';
15
17
  const VIEW_ADD = 'add';
@@ -26,15 +28,22 @@ type Props = {
26
28
  export const VariableSelectionPopover = ( { closePopover, propTypeKey, selectedVariable }: Props ) => {
27
29
  const [ currentView, setCurrentView ] = useState< View >( VIEW_LIST );
28
30
  const editIdRef = useRef< string >( '' );
31
+ const anchorRef = useRef< HTMLDivElement >( null );
29
32
 
30
- return renderStage( {
31
- propTypeKey,
32
- currentView,
33
- selectedVariable,
34
- editIdRef,
35
- setCurrentView,
36
- closePopover,
37
- } );
33
+ return (
34
+ <PopoverContentRefContext.Provider value={ anchorRef }>
35
+ <Box ref={ anchorRef }>
36
+ { renderStage( {
37
+ propTypeKey,
38
+ currentView,
39
+ selectedVariable,
40
+ editIdRef,
41
+ setCurrentView,
42
+ closePopover,
43
+ } ) }
44
+ </Box>
45
+ </PopoverContentRefContext.Provider>
46
+ );
38
47
  };
39
48
 
40
49
  type StageProps = {
@@ -1,90 +1,33 @@
1
1
  import * as React from 'react';
2
- import { useId, useRef } from 'react';
3
2
  import { useBoundProp } from '@elementor/editor-controls';
4
3
  import { colorPropTypeUtil } from '@elementor/editor-props';
5
- import { ColorFilterIcon, DetachIcon } from '@elementor/icons';
6
- import {
7
- bindPopover,
8
- bindTrigger,
9
- Box,
10
- IconButton,
11
- Popover,
12
- Stack,
13
- Typography,
14
- UnstableTag as Tag,
15
- usePopupState,
16
- } from '@elementor/ui';
17
- import { __ } from '@wordpress/i18n';
18
4
 
19
5
  import { ColorIndicator } from '../components/ui/color-indicator';
20
- import { VariableSelectionPopover } from '../components/variable-selection-popover';
6
+ import { AssignedVariable } from '../components/ui/variable/assigned-variable';
7
+ import { DeletedVariable } from '../components/ui/variable/deleted-variable';
21
8
  import { useVariable } from '../hooks/use-prop-variables';
22
9
  import { colorVariablePropTypeUtil } from '../prop-types/color-variable-prop-type';
23
10
 
24
- const SIZE = 'tiny';
25
-
26
11
  export const ColorVariableControl = () => {
27
- const { setValue: setColor } = useBoundProp();
28
12
  const { value: variableValue } = useBoundProp( colorVariablePropTypeUtil );
13
+ const assignedVariable = useVariable( variableValue );
29
14
 
30
- const anchorRef = useRef< HTMLDivElement >( null );
31
-
32
- const popupId = useId();
33
- const popupState = usePopupState( {
34
- variant: 'popover',
35
- popupId: `elementor-variables-list-${ popupId }`,
36
- } );
37
-
38
- const selectedVariable = useVariable( variableValue );
39
- if ( ! selectedVariable ) {
15
+ if ( ! assignedVariable ) {
40
16
  throw new Error( `Global color variable ${ variableValue } not found` );
41
17
  }
42
18
 
43
- const unlinkVariable = () => {
44
- setColor( colorPropTypeUtil.create( selectedVariable.value ) );
45
- };
19
+ const isVariableDeleted = assignedVariable?.deleted;
20
+
21
+ if ( isVariableDeleted ) {
22
+ return <DeletedVariable variable={ assignedVariable } />;
23
+ }
46
24
 
47
25
  return (
48
- <Box ref={ anchorRef }>
49
- <Tag
50
- fullWidth
51
- showActionsOnHover
52
- startIcon={
53
- <Stack spacing={ 0.75 } direction="row" alignItems="center">
54
- <ColorIndicator size="inherit" value={ selectedVariable.value } component="span" />
55
- <ColorFilterIcon fontSize="inherit" sx={ { mr: 1 } } />
56
- </Stack>
57
- }
58
- label={
59
- <Box sx={ { display: 'inline-grid', minWidth: 0 } }>
60
- <Typography
61
- sx={ { textOverflow: 'ellipsis', overflowX: 'hidden', lineHeight: 1 } }
62
- variant="caption"
63
- >
64
- { selectedVariable.label }
65
- </Typography>
66
- </Box>
67
- }
68
- actions={
69
- <IconButton size={ SIZE } onClick={ unlinkVariable } aria-label={ __( 'Unlink', 'elementor' ) }>
70
- <DetachIcon fontSize={ SIZE } />
71
- </IconButton>
72
- }
73
- { ...bindTrigger( popupState ) }
74
- />
75
- <Popover
76
- disableScrollLock
77
- anchorEl={ anchorRef.current }
78
- anchorOrigin={ { vertical: 'bottom', horizontal: 'right' } }
79
- transformOrigin={ { vertical: 'top', horizontal: 'right' } }
80
- { ...bindPopover( popupState ) }
81
- >
82
- <VariableSelectionPopover
83
- selectedVariable={ selectedVariable }
84
- closePopover={ popupState.close }
85
- propTypeKey={ colorVariablePropTypeUtil.key }
86
- />
87
- </Popover>
88
- </Box>
26
+ <AssignedVariable
27
+ variable={ assignedVariable }
28
+ variablePropTypeUtil={ colorVariablePropTypeUtil }
29
+ fallbackPropTypeUtil={ colorPropTypeUtil }
30
+ additionalStartIcon={ <ColorIndicator size="inherit" value={ assignedVariable.value } component="span" /> }
31
+ />
89
32
  );
90
33
  };
@@ -1,88 +1,31 @@
1
1
  import * as React from 'react';
2
- import { useId, useRef } from 'react';
3
2
  import { useBoundProp } from '@elementor/editor-controls';
4
3
  import { stringPropTypeUtil } from '@elementor/editor-props';
5
- import { ColorFilterIcon, DetachIcon } from '@elementor/icons';
6
- import {
7
- bindPopover,
8
- bindTrigger,
9
- Box,
10
- IconButton,
11
- Popover,
12
- Stack,
13
- Typography,
14
- UnstableTag as Tag,
15
- usePopupState,
16
- } from '@elementor/ui';
17
- import { __ } from '@wordpress/i18n';
18
4
 
19
- import { VariableSelectionPopover } from '../components/variable-selection-popover';
5
+ import { AssignedVariable } from '../components/ui/variable/assigned-variable';
6
+ import { DeletedVariable } from '../components/ui/variable/deleted-variable';
20
7
  import { useVariable } from '../hooks/use-prop-variables';
21
8
  import { fontVariablePropTypeUtil } from '../prop-types/font-variable-prop-type';
22
9
 
23
- const SIZE = 'tiny';
24
-
25
10
  export const FontVariableControl = () => {
26
- const { setValue: setFontFamily } = useBoundProp();
27
11
  const { value: variableValue } = useBoundProp( fontVariablePropTypeUtil );
12
+ const assignedVariable = useVariable( variableValue );
28
13
 
29
- const anchorRef = useRef< HTMLDivElement >( null );
30
-
31
- const popupId = useId();
32
- const popupState = usePopupState( {
33
- variant: 'popover',
34
- popupId: `elementor-variables-list-${ popupId }`,
35
- } );
36
-
37
- const selectedVariable = useVariable( variableValue );
38
- if ( ! selectedVariable ) {
14
+ if ( ! assignedVariable ) {
39
15
  throw new Error( `Global font variable ${ variableValue } not found` );
40
16
  }
41
17
 
42
- const unlinkVariable = () => {
43
- setFontFamily( stringPropTypeUtil.create( selectedVariable.value ) );
44
- };
18
+ const isVariableDeleted = assignedVariable?.deleted;
19
+
20
+ if ( isVariableDeleted ) {
21
+ return <DeletedVariable variable={ assignedVariable } />;
22
+ }
45
23
 
46
24
  return (
47
- <Box ref={ anchorRef }>
48
- <Tag
49
- fullWidth
50
- showActionsOnHover
51
- startIcon={
52
- <Stack spacing={ 0.75 } direction="row" alignItems="center">
53
- <ColorFilterIcon fontSize={ 'inherit' } sx={ { mr: 1 } } />
54
- </Stack>
55
- }
56
- label={
57
- <Box sx={ { display: 'inline-grid', minWidth: 0 } }>
58
- <Typography
59
- sx={ { textOverflow: 'ellipsis', overflowX: 'hidden', lineHeight: 1 } }
60
- variant="caption"
61
- >
62
- { selectedVariable.label }
63
- </Typography>
64
- </Box>
65
- }
66
- actions={
67
- <IconButton size={ SIZE } onClick={ unlinkVariable } aria-label={ __( 'Unlink', 'elementor' ) }>
68
- <DetachIcon fontSize={ SIZE } />
69
- </IconButton>
70
- }
71
- { ...bindTrigger( popupState ) }
72
- />
73
- <Popover
74
- disableScrollLock
75
- anchorEl={ anchorRef.current }
76
- anchorOrigin={ { vertical: 'bottom', horizontal: 'right' } }
77
- transformOrigin={ { vertical: 'top', horizontal: 'right' } }
78
- { ...bindPopover( popupState ) }
79
- >
80
- <VariableSelectionPopover
81
- selectedVariable={ selectedVariable }
82
- closePopover={ popupState.close }
83
- propTypeKey={ fontVariablePropTypeUtil.key }
84
- />
85
- </Popover>
86
- </Box>
25
+ <AssignedVariable
26
+ variable={ assignedVariable }
27
+ variablePropTypeUtil={ fontVariablePropTypeUtil }
28
+ fallbackPropTypeUtil={ stringPropTypeUtil }
29
+ />
87
30
  );
88
31
  };
@@ -2,8 +2,6 @@ import { useMemo } from 'react';
2
2
  import { type PropKey } from '@elementor/editor-props';
3
3
 
4
4
  import { service } from '../service';
5
- import { type TVariable } from '../storage';
6
- import { styleVariablesRepository } from '../style-variables-repository';
7
5
  import { type Variable } from '../types';
8
6
 
9
7
  export const useVariable = ( key: string ) => {
@@ -37,13 +35,13 @@ const usePropVariables = ( propKey: PropKey ) => {
37
35
  return useMemo( () => normalizeVariables( propKey ), [ propKey ] );
38
36
  };
39
37
 
38
+ const isNotDeleted = ( { deleted }: { deleted?: boolean } ) => ! deleted;
39
+
40
40
  const normalizeVariables = ( propKey: string ) => {
41
41
  const variables = service.variables();
42
42
 
43
- styleVariablesRepository.update( variables );
44
-
45
43
  return Object.entries( variables )
46
- .filter( ( [ , { type } ] ) => type === propKey )
44
+ .filter( ( [ , variable ] ) => variable.type === propKey && isNotDeleted( variable ) )
47
45
  .map( ( [ key, { label, value } ] ) => ( {
48
46
  key,
49
47
  label,
@@ -52,23 +50,19 @@ const normalizeVariables = ( propKey: string ) => {
52
50
  };
53
51
 
54
52
  export const createVariable = ( newVariable: Variable ): Promise< string > => {
55
- return service.create( newVariable ).then( ( { id, variable }: { id: string; variable: TVariable } ) => {
56
- styleVariablesRepository.update( {
57
- [ id ]: variable,
58
- } );
59
-
53
+ return service.create( newVariable ).then( ( { id }: { id: string } ) => {
60
54
  return id;
61
55
  } );
62
56
  };
63
57
 
64
58
  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
- } );
59
+ return service.update( updateId, { value, label } ).then( ( { id }: { id: string } ) => {
60
+ return id;
61
+ } );
62
+ };
71
63
 
72
- return id;
73
- } );
64
+ export const deleteVariable = ( deleteId: string ) => {
65
+ return service.delete( deleteId ).then( ( { id }: { id: string } ) => {
66
+ return id;
67
+ } );
74
68
  };
@@ -1,32 +1,16 @@
1
1
  import { styleTransformersRegistry } from '@elementor/editor-canvas';
2
- import { injectIntoRepeaterItemIcon, injectIntoRepeaterItemLabel } from '@elementor/editor-controls';
3
2
  import { controlActionsMenu, registerControlReplacement } from '@elementor/editor-editing-panel';
4
- import { backgroundColorOverlayPropTypeUtil, type PropValue, shadowPropTypeUtil } from '@elementor/editor-props';
5
3
 
6
- import {
7
- BackgroundRepeaterColorIndicator,
8
- BackgroundRepeaterLabel,
9
- BoxShadowRepeaterColorIndicator,
10
- } from './components/variables-repeater-item-slot';
11
4
  import { ColorVariableControl } from './controls/color-variable-control';
12
5
  import { usePropColorVariableAction } from './hooks/use-prop-color-variable-action';
13
6
  import { colorVariablePropTypeUtil } from './prop-types/color-variable-prop-type';
7
+ import { registerRepeaterInjections } from './repeater-injections';
14
8
  import { variableTransformer } from './transformers/variable-transformer';
15
9
  import { hasAssignedColorVariable } from './utils';
16
10
 
17
11
  const { registerPopoverAction } = controlActionsMenu;
18
12
 
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
+ export function initColorVariables() {
30
14
  registerControlReplacement( {
31
15
  component: ColorVariableControl,
32
16
  condition: ( { value } ) => hasAssignedColorVariable( value ),
@@ -36,37 +20,8 @@ function registerControlsAndActions() {
36
20
  id: 'color-variables',
37
21
  useProps: usePropColorVariableAction,
38
22
  } );
39
- }
40
-
41
- function registerRepeaterItemIcons() {
42
- injectIntoRepeaterItemIcon( {
43
- id: 'color-variables-background-icon',
44
- component: BackgroundRepeaterColorIndicator,
45
- condition: conditions.backgroundOverlay,
46
- } );
47
-
48
- injectIntoRepeaterItemIcon( {
49
- id: 'color-variables-icon',
50
- component: BoxShadowRepeaterColorIndicator,
51
- condition: conditions.boxShadow,
52
- } );
53
- }
54
23
 
55
- function registerRepeaterItemLabels() {
56
- injectIntoRepeaterItemLabel( {
57
- id: 'color-variables-label',
58
- component: BackgroundRepeaterLabel,
59
- condition: conditions.backgroundOverlay,
60
- } );
61
- }
62
-
63
- function registerStyleTransformers() {
64
24
  styleTransformersRegistry.register( colorVariablePropTypeUtil.key, variableTransformer );
65
- }
66
25
 
67
- export function initColorVariables() {
68
- registerControlsAndActions();
69
- registerRepeaterItemIcons();
70
- registerRepeaterItemLabels();
71
- registerStyleTransformers();
26
+ registerRepeaterInjections();
72
27
  }
@@ -0,0 +1,35 @@
1
+ import { injectIntoRepeaterItemIcon, injectIntoRepeaterItemLabel } from '@elementor/editor-controls';
2
+ import { backgroundColorOverlayPropTypeUtil, type PropValue, shadowPropTypeUtil } from '@elementor/editor-props';
3
+
4
+ import {
5
+ BackgroundRepeaterColorIndicator,
6
+ BackgroundRepeaterLabel,
7
+ BoxShadowRepeaterColorIndicator,
8
+ } from './components/variables-repeater-item-slot';
9
+ import { hasAssignedColorVariable } from './utils';
10
+
11
+ export function registerRepeaterInjections() {
12
+ injectIntoRepeaterItemIcon( {
13
+ id: 'color-variables-background-icon',
14
+ component: BackgroundRepeaterColorIndicator,
15
+ condition: ( { value: prop }: { value: PropValue } ) => {
16
+ return hasAssignedColorVariable( backgroundColorOverlayPropTypeUtil.extract( prop )?.color );
17
+ },
18
+ } );
19
+
20
+ injectIntoRepeaterItemIcon( {
21
+ id: 'color-variables-icon',
22
+ component: BoxShadowRepeaterColorIndicator,
23
+ condition: ( { value: prop }: { value: PropValue } ) => {
24
+ return hasAssignedColorVariable( shadowPropTypeUtil.extract( prop )?.color );
25
+ },
26
+ } );
27
+
28
+ injectIntoRepeaterItemLabel( {
29
+ id: 'color-variables-label',
30
+ component: BackgroundRepeaterLabel,
31
+ condition: ( { value: prop }: { value: PropValue } ) => {
32
+ return hasAssignedColorVariable( backgroundColorOverlayPropTypeUtil.extract( prop )?.color );
33
+ },
34
+ } );
35
+ }
@@ -0,0 +1,42 @@
1
+ import { __ } from '@wordpress/i18n';
2
+
3
+ export const VARIABLE_LABEL_MAX_LENGTH = 50;
4
+
5
+ export const validateLabel = ( name: string ): string => {
6
+ if ( ! name.trim() ) {
7
+ return __( 'Missing variable name.', 'elementor' );
8
+ }
9
+
10
+ const allowedChars = /^[a-zA-Z0-9_-]+$/;
11
+ if ( ! allowedChars.test( name ) ) {
12
+ return __( 'Names can only use letters, numbers, dashes (-) and underscores (_).', 'elementor' );
13
+ }
14
+
15
+ const hasAlphanumeric = /[a-zA-Z0-9]/;
16
+ if ( ! hasAlphanumeric.test( name ) ) {
17
+ return __( 'Names have to include at least one non-special character.', 'elementor' );
18
+ }
19
+
20
+ if ( VARIABLE_LABEL_MAX_LENGTH < name.length ) {
21
+ return __( 'Variable names can contain up to 50 characters.', 'elementor' );
22
+ }
23
+
24
+ return '';
25
+ };
26
+
27
+ export const labelHint = ( name: string ): string => {
28
+ const hintThreshold = VARIABLE_LABEL_MAX_LENGTH * 0.8 - 1;
29
+ if ( hintThreshold < name.length ) {
30
+ return __( 'Variable names can contain up to 50 characters.', 'elementor' );
31
+ }
32
+
33
+ return '';
34
+ };
35
+
36
+ export const validateValue = ( value: string ): string => {
37
+ if ( ! value.trim() ) {
38
+ return __( 'Missing variable value.', 'elementor' );
39
+ }
40
+
41
+ return '';
42
+ };