PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.0-beta3
Elementor Website Builder – more than just a page builder v4.3.0-beta3
4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 All 452 releases
elementor / modules / atomic-widgets / css-converter / variable-prop-value-transformer.php

variable-prop-value-transformer.php in Elementor Website Builder – more than just a page builder 4.3.0-beta3, at modules/atomic-widgets/css-converter/variable-prop-value-transformer.php

432 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Modules\AtomicWidgets\CssConverter;
4
5 use Elementor\Modules\AtomicWidgets\CssConverter\Converter_Registry_Factory;
6 use Elementor\Modules\AtomicWidgets\PropTypes\Base\Array_Prop_Type;
7 use Elementor\Modules\AtomicWidgets\PropTypes\Base\Object_Prop_Type;
8 use Elementor\Modules\AtomicWidgets\PropTypes\Color_Prop_Type;
9 use Elementor\Modules\AtomicWidgets\PropTypes\Contracts\Prop_Type;
10 use Elementor\Modules\AtomicWidgets\PropTypes\Font_Family_Prop_Type;
11 use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\String_Prop_Type;
12 use Elementor\Modules\AtomicWidgets\PropTypes\Union_Prop_Type;
13 use Elementor\Modules\AtomicWidgets\Styles\Size_Constants;
14 use Elementor\Modules\Variables\Adapters\Prop_Type_Adapter;
15 use Elementor\Modules\Variables\PropTypes\Color_Variable_Prop_Type;
16 use Elementor\Modules\Variables\PropTypes\Size_Variable_Prop_Type;
17 use Elementor\Modules\Variables\Services\Variables_Service;
18 use Elementor\Modules\Variables\Utils\Variable_Type_Keys;
19
20 if ( ! defined( 'ABSPATH' ) ) {
21 exit; // Exit if accessed directly.
22 }
23
24 class Variable_Prop_Value_Transformer {
25 private Variables_Service $variables_service;
26
27 public function __construct( Variables_Service $variables_service ) {
28 $this->variables_service = $variables_service;
29 }
30
31 /**
32 * Resolves global-color-variable references inside gradient color stops to their actual color values.
33 * Variables cannot be used inside gradient stops because the gradient UI control does not support them.
34 * If a variable cannot be resolved, the entire background prop is ejected to custom CSS.
35 *
36 * @param array $props Mutated in place.
37 * @param array<int, array{property: string, value: string, declaration: string}> $rules
38 * @return string[] Extra custom CSS declarations produced by ejected props.
39 */
40 public function normalize_gradient_color_stops( array &$props, array $rules ): array {
41 $background = $props['background'] ?? null;
42
43 if ( ! is_array( $background ) || 'background' !== ( $background['$$type'] ?? null ) ) {
44 return [];
45 }
46
47 $overlays = $background['value']['background-overlay']['value'] ?? null;
48
49 if ( ! is_array( $overlays ) ) {
50 return [];
51 }
52
53 foreach ( $overlays as $i => $overlay ) {
54 if ( 'background-gradient-overlay' !== ( $overlay['$$type'] ?? null ) ) {
55 continue;
56 }
57
58 $stops = $overlay['value']['stops']['value'] ?? [];
59
60 foreach ( $stops as $j => $stop ) {
61 if ( 'color-stop' !== ( $stop['$$type'] ?? null ) ) {
62 continue;
63 }
64
65 $color = $stop['value']['color'] ?? null;
66
67 if ( ! is_array( $color ) || Color_Variable_Prop_Type::get_key() !== ( $color['$$type'] ?? null ) ) {
68 continue;
69 }
70
71 $variable = $this->variables_service->find_by_label_or_id( $color['value'] ?? '' );
72
73 if ( null === $variable || '' === ( $variable['value'] ?? '' ) ) {
74 unset( $props['background'] );
75 $declaration = $this->declaration_for_ejected_prop( $rules, 'background' );
76 return null !== $declaration ? [ $declaration . ';' ] : [];
77 }
78
79 $props['background']['value']['background-overlay']['value'][ $i ]['value']['stops']['value'][ $j ]['value']['color'] = Color_Prop_Type::generate( $variable['value'] );
80 }
81 }
82
83 return [];
84 }
85
86 /**
87 * @param array $props
88 * @param array $schema
89 * @param array<int, array{property: string, value: string, declaration: string}> $rules
90 * @return array{props: array, custom_css: string[], rejected: string[]}
91 */
92 public function eject_unresolved_var_props( array $props, array $schema, array $rules ): array {
93 $custom_css = [];
94 $rejected = [];
95
96 foreach ( $props as $key => $prop_value ) {
97 if ( ! isset( $schema[ $key ] ) || ! ( $schema[ $key ] instanceof Prop_Type ) ) {
98 continue;
99 }
100
101 $reference = $this->find_var_reference_in_value( $prop_value, $schema[ $key ] );
102
103 if ( null === $reference ) {
104 continue;
105 }
106
107 $variable = $this->variables_service->find_by_label_or_id( $reference );
108
109 if (
110 null !== $variable
111 && $this->variable_fits_prop_type( $schema[ $key ], $variable['type'] ?? '' )
112 ) {
113 continue;
114 }
115
116 unset( $props[ $key ] );
117
118 $declaration = $this->declaration_for_ejected_prop( $rules, $key );
119
120 if ( null === $declaration ) {
121 continue;
122 }
123
124 $declaration = $declaration . ';';
125
126 if ( null === $variable ) {
127 $custom_css[] = $declaration;
128 continue;
129 }
130
131 $rejected[] = $declaration;
132 }
133
134 return [
135 'props' => $props,
136 'custom_css' => $custom_css,
137 'rejected' => $rejected,
138 ];
139 }
140
141 public function transform( array $props, array $schema ): array {
142 $transformed = [];
143
144 foreach ( $props as $key => $prop_value ) {
145 if ( ! isset( $schema[ $key ] ) || ! ( $schema[ $key ] instanceof Prop_Type ) ) {
146 $transformed[ $key ] = $prop_value;
147 continue;
148 }
149
150 $transformed[ $key ] = $this->transform_value( $prop_value, $schema[ $key ] );
151 }
152
153 return $transformed;
154 }
155
156 /**
157 * @param mixed $prop_value
158 * @return mixed
159 */
160 private function transform_value( $prop_value, Prop_Type $prop_type ) {
161 if ( ! is_array( $prop_value ) || ! isset( $prop_value['$$type'] ) ) {
162 return $prop_value;
163 }
164
165 if ( $prop_type instanceof Union_Prop_Type ) {
166 $promoted = $this->try_promote_to_variable( $prop_value, $prop_type );
167
168 if ( null !== $promoted ) {
169 return $promoted;
170 }
171
172 $branch = $prop_type->get_prop_type( $prop_value['$$type'] );
173
174 if ( ! $branch ) {
175 return $prop_value;
176 }
177
178 return $this->transform_value( $prop_value, $branch );
179 }
180
181 if ( $prop_type instanceof Object_Prop_Type ) {
182 if ( ! is_array( $prop_value['value'] ?? null ) ) {
183 return $prop_value;
184 }
185
186 $shape = $prop_type->get_shape();
187
188 foreach ( $shape as $field => $field_type ) {
189 if ( ! isset( $prop_value['value'][ $field ] ) ) {
190 continue;
191 }
192
193 $prop_value['value'][ $field ] = $this->transform_value(
194 $prop_value['value'][ $field ],
195 $field_type
196 );
197 }
198
199 return $prop_value;
200 }
201
202 if ( $prop_type instanceof Array_Prop_Type ) {
203 if ( ! is_array( $prop_value['value'] ?? null ) ) {
204 return $prop_value;
205 }
206
207 foreach ( $prop_value['value'] as $index => $item ) {
208 $prop_value['value'][ $index ] = $this->transform_value(
209 $item,
210 $prop_type->get_item_type()
211 );
212 }
213
214 return $prop_value;
215 }
216
217 return $prop_value;
218 }
219
220 private function try_promote_to_variable( array $prop_value, Union_Prop_Type $union ): ?array {
221 $reference = $this->extract_var_reference( $prop_value );
222
223 if ( null === $reference ) {
224 return null;
225 }
226
227 $variable = $this->variables_service->find_by_label_or_id( $reference );
228
229 if ( null === $variable ) {
230 return null;
231 }
232
233 $variable_type = $this->resolve_variable_prop_type_key( $variable['type'] ?? '', $union );
234
235 if ( null === $variable_type ) {
236 return null;
237 }
238
239 $id = $variable['id'] ?? '';
240
241 if ( '' === $id ) {
242 return null;
243 }
244
245 return [
246 '$$type' => $variable_type,
247 'value' => $id,
248 ];
249 }
250
251 private function extract_var_reference( array $prop_value ): ?string {
252 $type = $prop_value['$$type'] ?? '';
253 $value = $prop_value['value'] ?? null;
254
255 $string_backed_types = [
256 Color_Prop_Type::get_key(),
257 String_Prop_Type::get_key(),
258 Font_Family_Prop_Type::get_key(),
259 ];
260
261 if ( in_array( $type, $string_backed_types, true ) && is_string( $value ) ) {
262 return Css_Var_Reference::parse( $value );
263 }
264
265 if ( 'size' === $type && is_array( $value ) ) {
266 $unit = $value['unit'] ?? '';
267
268 if ( Size_Constants::UNIT_CUSTOM !== $unit || ! is_string( $value['size'] ?? null ) ) {
269 return null;
270 }
271
272 return Css_Var_Reference::parse( $value['size'] );
273 }
274
275 return null;
276 }
277
278 /**
279 * @param mixed $prop_value
280 */
281 private function find_var_reference_in_value( $prop_value, Prop_Type $prop_type ): ?string {
282 if ( ! is_array( $prop_value ) || ! isset( $prop_value['$$type'] ) ) {
283 return null;
284 }
285
286 if ( Variable_Type_Keys::is_variable_type( $prop_value['$$type'] ) ) {
287 return null;
288 }
289
290 if ( $prop_type instanceof Union_Prop_Type ) {
291 $reference = $this->extract_var_reference( $prop_value );
292
293 if ( null !== $reference ) {
294 return $reference;
295 }
296
297 $branch = $prop_type->get_prop_type( $prop_value['$$type'] );
298
299 if ( ! $branch ) {
300 return null;
301 }
302
303 return $this->find_var_reference_in_value( $prop_value, $branch );
304 }
305
306 if ( $prop_type instanceof Object_Prop_Type ) {
307 if ( ! is_array( $prop_value['value'] ?? null ) ) {
308 return null;
309 }
310
311 foreach ( $prop_type->get_shape() as $field => $field_type ) {
312 if ( ! isset( $prop_value['value'][ $field ] ) ) {
313 continue;
314 }
315
316 $reference = $this->find_var_reference_in_value(
317 $prop_value['value'][ $field ],
318 $field_type
319 );
320
321 if ( null !== $reference ) {
322 return $reference;
323 }
324 }
325
326 return null;
327 }
328
329 if ( $prop_type instanceof Array_Prop_Type ) {
330 if ( ! is_array( $prop_value['value'] ?? null ) ) {
331 return null;
332 }
333
334 foreach ( $prop_value['value'] as $item ) {
335 $reference = $this->find_var_reference_in_value(
336 $item,
337 $prop_type->get_item_type()
338 );
339
340 if ( null !== $reference ) {
341 return $reference;
342 }
343 }
344
345 return null;
346 }
347
348 return $this->extract_var_reference( $prop_value );
349 }
350
351 private function variable_fits_prop_type( Prop_Type $prop_type, string $variable_type ): bool {
352 if ( '' === $variable_type ) {
353 return false;
354 }
355
356 if ( $prop_type instanceof Union_Prop_Type ) {
357 return null !== $this->resolve_variable_prop_type_key( $variable_type, $prop_type );
358 }
359
360 $resolved_type = Variable_Type_Keys::get_resolved_type( $variable_type );
361
362 return null !== $resolved_type && $resolved_type === $prop_type::get_key();
363 }
364
365 /**
366 * @param array<int, array{property: string, value: string, declaration: string}> $rules
367 */
368 private function declaration_for_ejected_prop( array $rules, string $property ): ?string {
369 $declaration = $this->declaration_for_property( $rules, $property );
370
371 if ( null !== $declaration ) {
372 return $declaration;
373 }
374
375 $longhands_by_aggregate = [
376 'background' => [
377 'background-color',
378 'background-clip',
379 'background-image',
380 'background-repeat',
381 'background-attachment',
382 'background-position',
383 'background-size',
384 ],
385 'padding' => array_keys( Converter_Registry_Factory::DIMENSIONS_SIDE_SPECS ),
386 'margin' => array_keys( Converter_Registry_Factory::DIMENSIONS_SIDE_SPECS ),
387 ];
388
389 foreach ( $longhands_by_aggregate[ $property ] ?? [] as $longhand ) {
390 $declaration = $this->declaration_for_property( $rules, $longhand );
391
392 if ( null !== $declaration ) {
393 return $declaration;
394 }
395 }
396
397 return null;
398 }
399
400 /**
401 * @param array<int, array{property: string, value: string, declaration: string}> $rules
402 */
403 private function declaration_for_property( array $rules, string $property ): ?string {
404 foreach ( $rules as $rule ) {
405 if ( $rule['property'] === $property ) {
406 return $rule['declaration'];
407 }
408 }
409
410 return null;
411 }
412
413 private function resolve_variable_prop_type_key( string $variable_type, Union_Prop_Type $union ): ?string {
414 if ( '' === $variable_type ) {
415 return null;
416 }
417
418 if ( $union->get_prop_type( $variable_type ) ) {
419 return $variable_type;
420 }
421
422 if (
423 Prop_Type_Adapter::GLOBAL_CUSTOM_SIZE_VARIABLE_KEY === $variable_type
424 && $union->get_prop_type( Size_Variable_Prop_Type::get_key() )
425 ) {
426 return Size_Variable_Prop_Type::get_key();
427 }
428
429 return null;
430 }
431 }
432