@elementor/editor-controls 4.1.0-797 → 4.1.0-798

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-controls",
3
3
  "description": "This package contains the controls model and utils for the Elementor editor",
4
- "version": "4.1.0-797",
4
+ "version": "4.1.0-798",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,22 +40,22 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/editor-current-user": "4.1.0-797",
44
- "@elementor/editor-elements": "4.1.0-797",
45
- "@elementor/editor-props": "4.1.0-797",
46
- "@elementor/editor-responsive": "4.1.0-797",
47
- "@elementor/editor-ui": "4.1.0-797",
48
- "@elementor/editor-v1-adapters": "4.1.0-797",
49
- "@elementor/env": "4.1.0-797",
50
- "@elementor/events": "4.1.0-797",
51
- "@elementor/http-client": "4.1.0-797",
43
+ "@elementor/editor-current-user": "4.1.0-798",
44
+ "@elementor/editor-elements": "4.1.0-798",
45
+ "@elementor/editor-props": "4.1.0-798",
46
+ "@elementor/editor-responsive": "4.1.0-798",
47
+ "@elementor/editor-ui": "4.1.0-798",
48
+ "@elementor/editor-v1-adapters": "4.1.0-798",
49
+ "@elementor/env": "4.1.0-798",
50
+ "@elementor/events": "4.1.0-798",
51
+ "@elementor/http-client": "4.1.0-798",
52
52
  "@elementor/icons": "^1.68.0",
53
- "@elementor/locations": "4.1.0-797",
54
- "@elementor/query": "4.1.0-797",
55
- "@elementor/session": "4.1.0-797",
53
+ "@elementor/locations": "4.1.0-798",
54
+ "@elementor/query": "4.1.0-798",
55
+ "@elementor/session": "4.1.0-798",
56
56
  "@elementor/ui": "1.37.5",
57
- "@elementor/utils": "4.1.0-797",
58
- "@elementor/wp-media": "4.1.0-797",
57
+ "@elementor/utils": "4.1.0-798",
58
+ "@elementor/wp-media": "4.1.0-798",
59
59
  "@monaco-editor/react": "^4.7.0",
60
60
  "@tiptap/extension-bold": "^3.11.1",
61
61
  "@tiptap/extension-document": "^3.11.1",
@@ -99,7 +99,27 @@ const FromNameField = () => (
99
99
  />
100
100
  );
101
101
 
102
- const ReplyToField = () => <EmailField bind="reply-to" label={ __( 'Reply-to', 'elementor' ) } />;
102
+ const ReplyToField = () => {
103
+ const emailSuggestions = useFormFieldSuggestions( { inputType: 'email' } );
104
+
105
+ return (
106
+ <PropKeyProvider bind="reply-to">
107
+ <Grid container direction="column" gap={ 0.5 }>
108
+ <Grid item>
109
+ <ControlFormLabel>{ __( 'Reply-to', 'elementor' ) }</ControlFormLabel>
110
+ </Grid>
111
+ <Grid item>
112
+ <MentionTextAreaControl
113
+ suggestions={ emailSuggestions }
114
+ rows={ 1 }
115
+ triggerPosition="start"
116
+ placeholder={ __( 'You can type @ to insert an email field', 'elementor' ) }
117
+ />
118
+ </Grid>
119
+ </Grid>
120
+ </PropKeyProvider>
121
+ );
122
+ };
103
123
 
104
124
  const CcField = () => <EmailField bind="cc" label={ __( 'Cc', 'elementor' ) } />;
105
125
 
@@ -76,16 +76,30 @@ const MentionWrapper = styled( 'div' )( ( { theme } ) => ( {
76
76
  backgroundColor: theme.palette.action.selected,
77
77
  },
78
78
  },
79
+ '&[data-single-line="true"] textarea': {
80
+ resize: 'none',
81
+ },
79
82
  } ) );
80
83
 
84
+ type TriggerPosition = 'start' | 'auto';
85
+
86
+ function createMentionPattern( value: string, triggerPosition: TriggerPosition ): RegExp {
87
+ const escaped = value.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
88
+ const prefix = 'start' === triggerPosition ? '^' : '';
89
+
90
+ return new RegExp( `${ prefix }@${ escaped }(?=\\s|$|[^a-zA-Z0-9_-])`, 'g' );
91
+ }
92
+
81
93
  type Props = {
82
94
  placeholder?: string;
83
95
  ariaLabel?: string;
84
96
  suggestions: Suggestion[];
97
+ rows?: number;
98
+ triggerPosition?: TriggerPosition;
85
99
  };
86
100
 
87
101
  export const MentionTextAreaControl = createControl(
88
- ( { placeholder, ariaLabel, suggestions: allSuggestions }: Props ) => {
102
+ ( { placeholder, ariaLabel, suggestions: allSuggestions, rows = 5, triggerPosition = 'auto' }: Props ) => {
89
103
  const { value, setValue, disabled } = useBoundProp( stringPropTypeUtil );
90
104
  const [ filteredSuggestions, setFilteredSuggestions ] = useState< Suggestion[] >( [] );
91
105
 
@@ -94,16 +108,13 @@ export const MentionTextAreaControl = createControl(
94
108
  let result = text;
95
109
 
96
110
  for ( const suggestion of allSuggestions ) {
97
- const mentionPattern = new RegExp(
98
- `@${ suggestion.value.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' ) }(?=\\s|$|[^a-zA-Z0-9_-])`,
99
- 'g'
100
- );
101
- result = result.replace( mentionPattern, `[${ suggestion.value }]` );
111
+ const pattern = createMentionPattern( suggestion.value, triggerPosition );
112
+ result = result.replace( pattern, `[${ suggestion.value }]` );
102
113
  }
103
114
 
104
115
  return result;
105
116
  },
106
- [ allSuggestions ]
117
+ [ allSuggestions, triggerPosition ]
107
118
  );
108
119
 
109
120
  const handleChange = useCallback(
@@ -116,19 +127,29 @@ export const MentionTextAreaControl = createControl(
116
127
  );
117
128
 
118
129
  const handleSearch = useCallback(
119
- ( event: { query: string } ) => {
130
+ ( event: { originalEvent: React.SyntheticEvent; trigger: string; query: string } ) => {
131
+ if ( 'start' === triggerPosition ) {
132
+ const target = event.originalEvent.target as HTMLTextAreaElement;
133
+ const triggerIndex = target.selectionStart - event.query.length - event.trigger.length;
134
+
135
+ if ( triggerIndex !== 0 ) {
136
+ setFilteredSuggestions( [] );
137
+ return;
138
+ }
139
+ }
140
+
120
141
  const query = event.query.toLowerCase();
121
142
  const filtered = allSuggestions.filter(
122
143
  ( item ) => item.label.toLowerCase().includes( query ) || item.value.toLowerCase().includes( query )
123
144
  );
124
145
  setFilteredSuggestions( filtered );
125
146
  },
126
- [ allSuggestions ]
147
+ [ allSuggestions, triggerPosition ]
127
148
  );
128
149
 
129
150
  return (
130
151
  <ControlActions>
131
- <MentionWrapper>
152
+ <MentionWrapper data-single-line={ rows === 1 ? 'true' : undefined }>
132
153
  <Mention
133
154
  value={ value ?? '' }
134
155
  onChange={ handleChange }
@@ -136,7 +157,7 @@ export const MentionTextAreaControl = createControl(
136
157
  onSearch={ handleSearch }
137
158
  field="value"
138
159
  trigger="@"
139
- rows={ 5 }
160
+ rows={ rows }
140
161
  disabled={ disabled }
141
162
  placeholder={ placeholder }
142
163
  itemTemplate={ SuggestionItem }
@@ -9,7 +9,11 @@ export type Suggestion = {
9
9
 
10
10
  const FORM_FIELD_WIDGET_TYPES = [ 'e-form-input', 'e-form-textarea', 'e-form-checkbox' ];
11
11
 
12
- export function useFormFieldSuggestions(): Suggestion[] {
12
+ type Options = {
13
+ inputType?: string;
14
+ };
15
+
16
+ export function useFormFieldSuggestions( options?: Options ): Suggestion[] {
13
17
  return useListenTo(
14
18
  [
15
19
  v1ReadyEvent(),
@@ -40,6 +44,15 @@ export function useFormFieldSuggestions(): Suggestion[] {
40
44
  return;
41
45
  }
42
46
 
47
+ if ( options?.inputType ) {
48
+ const typeProp = child.settings.get( 'type' );
49
+ const typeValue = isTransformable( typeProp ) ? typeProp.value : typeProp;
50
+
51
+ if ( typeValue !== options.inputType ) {
52
+ return;
53
+ }
54
+ }
55
+
43
56
  const cssIdProp = child.settings.get( '_cssid' );
44
57
  const fieldId = isTransformable( cssIdProp ) ? cssIdProp.value : cssIdProp;
45
58