@elementor/editor-components 3.35.0-431 → 3.35.0-432

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-components",
3
3
  "description": "Elementor editor components",
4
- "version": "3.35.0-431",
4
+ "version": "3.35.0-432",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,30 +40,30 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/editor": "3.35.0-431",
44
- "@elementor/editor-canvas": "3.35.0-431",
45
- "@elementor/editor-controls": "3.35.0-431",
46
- "@elementor/editor-documents": "3.35.0-431",
47
- "@elementor/editor-editing-panel": "3.35.0-431",
48
- "@elementor/editor-elements": "3.35.0-431",
49
- "@elementor/editor-elements-panel": "3.35.0-431",
50
- "@elementor/editor-mcp": "3.35.0-431",
51
- "@elementor/editor-panels": "3.35.0-431",
52
- "@elementor/editor-props": "3.35.0-431",
53
- "@elementor/editor-styles-repository": "3.35.0-431",
54
- "@elementor/editor-ui": "3.35.0-431",
55
- "@elementor/editor-v1-adapters": "3.35.0-431",
56
- "@elementor/http-client": "3.35.0-431",
43
+ "@elementor/editor": "3.35.0-432",
44
+ "@elementor/editor-canvas": "3.35.0-432",
45
+ "@elementor/editor-controls": "3.35.0-432",
46
+ "@elementor/editor-documents": "3.35.0-432",
47
+ "@elementor/editor-editing-panel": "3.35.0-432",
48
+ "@elementor/editor-elements": "3.35.0-432",
49
+ "@elementor/editor-elements-panel": "3.35.0-432",
50
+ "@elementor/editor-mcp": "3.35.0-432",
51
+ "@elementor/editor-panels": "3.35.0-432",
52
+ "@elementor/editor-props": "3.35.0-432",
53
+ "@elementor/editor-styles-repository": "3.35.0-432",
54
+ "@elementor/editor-ui": "3.35.0-432",
55
+ "@elementor/editor-v1-adapters": "3.35.0-432",
56
+ "@elementor/http-client": "3.35.0-432",
57
57
  "@elementor/icons": "^1.63.0",
58
- "@elementor/mixpanel": "3.35.0-431",
59
- "@elementor/query": "3.35.0-431",
60
- "@elementor/schema": "3.35.0-431",
61
- "@elementor/store": "3.35.0-431",
58
+ "@elementor/mixpanel": "3.35.0-432",
59
+ "@elementor/query": "3.35.0-432",
60
+ "@elementor/schema": "3.35.0-432",
61
+ "@elementor/store": "3.35.0-432",
62
62
  "@elementor/ui": "1.36.17",
63
- "@elementor/utils": "3.35.0-431",
63
+ "@elementor/utils": "3.35.0-432",
64
64
  "@wordpress/i18n": "^5.13.0",
65
- "@elementor/editor-notifications": "3.35.0-431",
66
- "@elementor/editor-current-user": "3.35.0-431"
65
+ "@elementor/editor-notifications": "3.35.0-432",
66
+ "@elementor/editor-current-user": "3.35.0-432"
67
67
  },
68
68
  "peerDependencies": {
69
69
  "react": "^18.3.1",
@@ -149,6 +149,7 @@ export function PropertiesGroup( {
149
149
  sortableTriggerProps={ { ...triggerProps, style: triggerStyle } }
150
150
  isDragPlaceholder={ isItemDragPlaceholder }
151
151
  groups={ allGroups }
152
+ existingLabels={ Object.values( props ).map( ( p ) => p.label ) }
152
153
  onDelete={ onPropertyDelete }
153
154
  onUpdate={ ( data ) => onPropertyUpdate( prop.overrideKey, data ) }
154
155
  />
@@ -12,6 +12,7 @@ type PropertyItemProps = {
12
12
  sortableTriggerProps: SortableTriggerProps;
13
13
  isDragPlaceholder?: boolean;
14
14
  groups: { value: string; label: string }[];
15
+ existingLabels: string[];
15
16
  onDelete: ( propKey: string ) => void;
16
17
  onUpdate: ( data: { label: string; group: string | null } ) => void;
17
18
  };
@@ -21,6 +22,7 @@ export function PropertyItem( {
21
22
  sortableTriggerProps,
22
23
  isDragPlaceholder,
23
24
  groups,
25
+ existingLabels,
24
26
  onDelete,
25
27
  onUpdate,
26
28
  }: PropertyItemProps ) {
@@ -99,6 +101,7 @@ export function PropertyItem( {
99
101
  onSubmit={ handleSubmit }
100
102
  currentValue={ prop }
101
103
  groups={ groups }
104
+ existingLabels={ existingLabels }
102
105
  sx={ { width: '100%' } }
103
106
  />
104
107
  </Popover>
@@ -5,6 +5,7 @@ import { Button, FormLabel, Grid, Select, Stack, type SxProps, TextField, Typogr
5
5
  import { __ } from '@wordpress/i18n';
6
6
 
7
7
  import { type OverridableProp } from '../../types';
8
+ import { validatePropLabel } from './utils/validate-prop-label';
8
9
 
9
10
  const SIZE = 'tiny';
10
11
 
@@ -14,12 +15,14 @@ type Props = {
14
15
  onSubmit: ( data: { label: string; group: string | null } ) => void;
15
16
  currentValue?: OverridableProp;
16
17
  groups?: { value: string; label: string }[];
18
+ existingLabels?: string[];
17
19
  sx?: SxProps;
18
20
  };
19
21
 
20
- export function OverridablePropForm( { onSubmit, groups, currentValue, sx }: Props ) {
22
+ export function OverridablePropForm( { onSubmit, groups, currentValue, existingLabels = [], sx }: Props ) {
21
23
  const [ propLabel, setPropLabel ] = useState< string | null >( currentValue?.label ?? null );
22
24
  const [ group, setGroup ] = useState< string | null >( currentValue?.groupId ?? groups?.[ 0 ]?.value ?? null );
25
+ const [ error, setError ] = useState< string | null >( null );
23
26
 
24
27
  const name = __( 'Name', 'elementor' );
25
28
  const groupName = __( 'Group Name', 'elementor' );
@@ -29,8 +32,19 @@ export function OverridablePropForm( { onSubmit, groups, currentValue, sx }: Pro
29
32
  const title = isCreate ? __( 'Create new property', 'elementor' ) : __( 'Update property', 'elementor' );
30
33
  const ctaLabel = isCreate ? __( 'Create', 'elementor' ) : __( 'Update', 'elementor' );
31
34
 
35
+ const handleSubmit = () => {
36
+ const validationResult = validatePropLabel( propLabel ?? '', existingLabels, currentValue?.label );
37
+
38
+ if ( ! validationResult.isValid ) {
39
+ setError( validationResult.errorMessage );
40
+ return;
41
+ }
42
+
43
+ onSubmit( { label: propLabel ?? '', group } );
44
+ };
45
+
32
46
  return (
33
- <Form onSubmit={ () => onSubmit( { label: propLabel ?? '', group } ) }>
47
+ <Form onSubmit={ handleSubmit }>
34
48
  <Stack alignItems="start" sx={ { width: '268px', ...sx } }>
35
49
  <Stack
36
50
  direction="row"
@@ -54,7 +68,18 @@ export function OverridablePropForm( { onSubmit, groups, currentValue, sx }: Pro
54
68
  fullWidth
55
69
  placeholder={ __( 'Enter value', 'elementor' ) }
56
70
  value={ propLabel ?? '' }
57
- onChange={ ( e: React.ChangeEvent< HTMLInputElement > ) => setPropLabel( e.target.value ) }
71
+ onChange={ ( e: React.ChangeEvent< HTMLInputElement > ) => {
72
+ const newValue = e.target.value;
73
+ setPropLabel( newValue );
74
+ const validationResult = validatePropLabel(
75
+ newValue,
76
+ existingLabels,
77
+ currentValue?.label
78
+ );
79
+ setError( validationResult.errorMessage );
80
+ } }
81
+ error={ Boolean( error ) }
82
+ helperText={ error }
58
83
  />
59
84
  </Grid>
60
85
  </Grid>
@@ -91,7 +116,13 @@ export function OverridablePropForm( { onSubmit, groups, currentValue, sx }: Pro
91
116
  </Grid>
92
117
  </Grid>
93
118
  <Stack direction="row" justifyContent="flex-end" alignSelf="end" mt={ 1.5 } py={ 1 } px={ 1.5 }>
94
- <Button type="submit" disabled={ ! propLabel } variant="contained" color="primary" size="small">
119
+ <Button
120
+ type="submit"
121
+ disabled={ ! propLabel || Boolean( error ) }
122
+ variant="contained"
123
+ color="primary"
124
+ size="small"
125
+ >
95
126
  { ctaLabel }
96
127
  </Button>
97
128
  </Stack>
@@ -121,6 +121,7 @@ export function Content( { componentId, overridableProps }: Props ) {
121
121
  value: groupId,
122
122
  label: overridableProps.groups.items[ groupId ].label,
123
123
  } ) ) }
124
+ existingLabels={ Object.values( overridableProps?.props ?? {} ).map( ( prop ) => prop.label ) }
124
125
  currentValue={ overridableConfig }
125
126
  />
126
127
  </Popover>
@@ -0,0 +1,38 @@
1
+ import { __ } from '@wordpress/i18n';
2
+
3
+ export const ERROR_MESSAGES = {
4
+ EMPTY_NAME: __( 'Property name is required', 'elementor' ),
5
+ DUPLICATE_NAME: __( 'Property name already exists', 'elementor' ),
6
+ } as const;
7
+
8
+ type ValidationResult = {
9
+ isValid: boolean;
10
+ errorMessage: string | null;
11
+ };
12
+
13
+ export function validatePropLabel( label: string, existingLabels: string[], currentLabel?: string ): ValidationResult {
14
+ const trimmedLabel = label.trim();
15
+
16
+ if ( ! trimmedLabel ) {
17
+ return { isValid: false, errorMessage: ERROR_MESSAGES.EMPTY_NAME };
18
+ }
19
+
20
+ const normalizedLabel = trimmedLabel.toLowerCase();
21
+ const normalizedCurrentLabel = currentLabel?.trim().toLowerCase();
22
+
23
+ const isDuplicate = existingLabels.some( ( existingLabel ) => {
24
+ const normalizedExisting = existingLabel.trim().toLowerCase();
25
+
26
+ if ( normalizedCurrentLabel && normalizedExisting === normalizedCurrentLabel ) {
27
+ return false;
28
+ }
29
+
30
+ return normalizedExisting === normalizedLabel;
31
+ } );
32
+
33
+ if ( isDuplicate ) {
34
+ return { isValid: false, errorMessage: ERROR_MESSAGES.DUPLICATE_NAME };
35
+ }
36
+
37
+ return { isValid: true, errorMessage: null };
38
+ }