PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.2
Elementor Website Builder – more than just a page builder v4.3.2
4.3.2 4.3.1 4.3.0 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 All 455 releases
elementor / modules / atomic-widgets / css-converter / converters / transform-property-converter.php

transform-property-converter.php in Elementor Website Builder – more than just a page builder 4.3.2, at modules/atomic-widgets/css-converter/converters/transform-property-converter.php

386 lines 10.4 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\Converters;
4
5 use Elementor\Modules\AtomicWidgets\CssConverter\Conversion_Context;
6 use Elementor\Modules\AtomicWidgets\CssConverter\Property_Converter_Base;
7 use Elementor\Modules\AtomicWidgets\CssConverter\ValueParsers\Css_Token_Splitter;
8 use Elementor\Modules\AtomicWidgets\CssConverter\ValueParsers\Size_Value_Parser;
9 use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\Number_Prop_Type;
10 use Elementor\Modules\AtomicWidgets\PropTypes\Size_Prop_Type;
11 use Elementor\Modules\AtomicWidgets\PropTypes\Transform\Functions\Transform_Move_Prop_Type;
12 use Elementor\Modules\AtomicWidgets\PropTypes\Transform\Functions\Transform_Rotate_Prop_Type;
13 use Elementor\Modules\AtomicWidgets\PropTypes\Transform\Functions\Transform_Scale_Prop_Type;
14 use Elementor\Modules\AtomicWidgets\PropTypes\Transform\Transform_Functions_Prop_Type;
15 use Elementor\Modules\AtomicWidgets\PropTypes\Transform\Transform_Prop_Type;
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit; // Exit if accessed directly.
19 }
20
21 /**
22 * Converter for the `transform` CSS property -> Transform_Prop_Type.
23 *
24 * Supported CSS functions -> schema type:
25 * translate(x) -> transform-move {x, y:0, z:0}
26 * translate(x, y) -> transform-move {x, y, z:0}
27 * translateX(n) -> transform-move {x:n, y:0, z:0}
28 * translateY(n) -> transform-move {x:0, y:n, z:0}
29 * translateZ(n) -> transform-move {x:0, y:0, z:n}
30 * translate3d(x, y, z) -> transform-move {x, y, z}
31 * scale(n) -> transform-scale {x:n, y:n, z:1}
32 * scale(x, y) -> transform-scale {x, y, z:1}
33 * scaleX(n) -> transform-scale {x:n, y:1, z:1}
34 * scaleY(n) -> transform-scale {x:1, y:n, z:1}
35 * scaleZ(n) -> transform-scale {x:1, y:1, z:n}
36 * scale3d(x, y, z) -> transform-scale {x, y, z}
37 * rotate(a) -> transform-rotate {x:0, y:0, z:a}
38 * rotateX(a) -> transform-rotate {x:a, y:0, z:0}
39 * rotateY(a) -> transform-rotate {x:0, y:a, z:0}
40 * rotateZ(a) -> transform-rotate {x:0, y:0, z:a}
41 * rotate3d not supported -> decline
42 * matrix / perspective / skew / etc -> decline
43 *
44 * Move units: % px em rem vw custom
45 * Rotate units: deg rad grad turn custom
46 * Scale values: unitless numbers (floats)
47 *
48 * Any unrecognised function declines the entire declaration to customCss.
49 */
50 class Transform_Property_Converter extends Property_Converter_Base {
51 const MOVE_UNITS = [ '%', 'px', 'em', 'rem', 'vw', 'custom' ];
52 const ROTATE_UNITS = [ 'deg', 'rad', 'grad', 'turn', 'custom' ];
53
54 const ZERO_MOVE = [
55 'size' => 0,
56 'unit' => 'px',
57 ];
58 const ZERO_ROTATE = [
59 'size' => 0,
60 'unit' => 'deg',
61 ];
62 const ONE_SCALE = 1.0;
63
64 protected function get_supported_properties(): array {
65 return [ 'transform' ];
66 }
67
68 protected function do_convert( Conversion_Context $context, array $rule ): bool {
69 $functions = $this->parse_functions( trim( $rule['value'] ) );
70
71 if ( null === $functions ) {
72 return false;
73 }
74
75 $existing = $context->get_prop( 'transform' );
76 $fields = $this->current_fields( $existing );
77 $fields['transform-functions'] = Transform_Functions_Prop_Type::generate( $functions );
78
79 $context->set_prop( 'transform', Transform_Prop_Type::generate( $fields ) );
80
81 return true;
82 }
83
84 private function parse_functions( string $value ): ?array {
85 if ( '' === $value || 'none' === strtolower( $value ) ) {
86 return [];
87 }
88
89 $raw_functions = $this->split_functions( $value );
90
91 if ( null === $raw_functions ) {
92 return null;
93 }
94
95 $items = [];
96
97 foreach ( $raw_functions as $fn ) {
98 $item = $this->parse_function( $fn );
99
100 if ( null === $item ) {
101 return null;
102 }
103
104 $items[] = $item;
105 }
106
107 return $items;
108 }
109
110 private function split_functions( string $value ): ?array {
111 $functions = [];
112 $current = '';
113 $depth = 0;
114
115 for ( $i = 0, $len = strlen( $value ); $i < $len; $i++ ) {
116 $char = $value[ $i ];
117
118 if ( '(' === $char ) {
119 ++$depth;
120 } elseif ( ')' === $char ) {
121 --$depth;
122
123 if ( 0 === $depth ) {
124 $current .= $char;
125 $functions[] = trim( $current );
126 $current = '';
127 ++$i;
128
129 while ( $i < $len && ' ' === $value[ $i ] ) {
130 ++$i;
131 }
132
133 --$i;
134 continue;
135 }
136 }
137
138 $current .= $char;
139 }
140
141 if ( '' !== trim( $current ) || 0 !== $depth ) {
142 return null;
143 }
144
145 return $functions;
146 }
147
148 private function parse_function( string $callback ): ?array {
149 if ( ! preg_match( '/^([\w-]+)\s*\((.+)\)$/s', $callback, $m ) ) {
150 return null;
151 }
152
153 $name = strtolower( $m[1] );
154 $args_str = $m[2];
155 $args = array_map( 'trim', Css_Token_Splitter::split_by_comma( $args_str ) );
156
157 switch ( $name ) {
158 case 'translate':
159 return $this->parse_translate( $args );
160 case 'translatex':
161 return $this->parse_translate_axis( $args, 'x' );
162 case 'translatey':
163 return $this->parse_translate_axis( $args, 'y' );
164 case 'translatez':
165 return $this->parse_translate_axis( $args, 'z' );
166 case 'translate3d':
167 return $this->parse_translate_3d( $args );
168 case 'scale':
169 return $this->parse_scale( $args );
170 case 'scalex':
171 return $this->parse_scale_axis( $args, 'x' );
172 case 'scaley':
173 return $this->parse_scale_axis( $args, 'y' );
174 case 'scalez':
175 return $this->parse_scale_axis( $args, 'z' );
176 case 'scale3d':
177 return $this->parse_scale_3d( $args );
178 case 'rotate':
179 case 'rotatez':
180 return $this->parse_rotate_axis( $args, 'z' );
181 case 'rotatex':
182 return $this->parse_rotate_axis( $args, 'x' );
183 case 'rotatey':
184 return $this->parse_rotate_axis( $args, 'y' );
185 default:
186 return null;
187 }
188 }
189
190 private function parse_translate( array $args ): ?array {
191 if ( 1 === count( $args ) ) {
192 $x = $this->move_size( $args[0] );
193
194 if ( null === $x ) {
195 return null;
196 }
197
198 return Transform_Move_Prop_Type::generate( [
199 'x' => Size_Prop_Type::generate( $x ),
200 'y' => Size_Prop_Type::generate( self::ZERO_MOVE ),
201 'z' => Size_Prop_Type::generate( self::ZERO_MOVE ),
202 ] );
203 }
204
205 if ( 2 === count( $args ) ) {
206 $x = $this->move_size( $args[0] );
207 $y = $this->move_size( $args[1] );
208
209 if ( null === $x || null === $y ) {
210 return null;
211 }
212
213 return Transform_Move_Prop_Type::generate( [
214 'x' => Size_Prop_Type::generate( $x ),
215 'y' => Size_Prop_Type::generate( $y ),
216 'z' => Size_Prop_Type::generate( self::ZERO_MOVE ),
217 ] );
218 }
219
220 return null;
221 }
222
223 private function parse_translate_axis( array $args, string $axis ): ?array {
224 if ( 1 !== count( $args ) ) {
225 return null;
226 }
227
228 $val = $this->move_size( $args[0] );
229
230 if ( null === $val ) {
231 return null;
232 }
233
234 return Transform_Move_Prop_Type::generate( [
235 'x' => Size_Prop_Type::generate( 'x' === $axis ? $val : self::ZERO_MOVE ),
236 'y' => Size_Prop_Type::generate( 'y' === $axis ? $val : self::ZERO_MOVE ),
237 'z' => Size_Prop_Type::generate( 'z' === $axis ? $val : self::ZERO_MOVE ),
238 ] );
239 }
240
241 private function parse_translate_3d( array $args ): ?array {
242 if ( 3 !== count( $args ) ) {
243 return null;
244 }
245
246 $x = $this->move_size( $args[0] );
247 $y = $this->move_size( $args[1] );
248 $z = $this->move_size( $args[2] );
249
250 if ( null === $x || null === $y || null === $z ) {
251 return null;
252 }
253
254 return Transform_Move_Prop_Type::generate( [
255 'x' => Size_Prop_Type::generate( $x ),
256 'y' => Size_Prop_Type::generate( $y ),
257 'z' => Size_Prop_Type::generate( $z ),
258 ] );
259 }
260
261 private function parse_scale( array $args ): ?array {
262 if ( 1 === count( $args ) ) {
263 $n = $this->scale_number( $args[0] );
264
265 if ( null === $n ) {
266 return null;
267 }
268
269 return Transform_Scale_Prop_Type::generate( [
270 'x' => Number_Prop_Type::generate( $n ),
271 'y' => Number_Prop_Type::generate( $n ),
272 'z' => Number_Prop_Type::generate( self::ONE_SCALE ),
273 ] );
274 }
275
276 if ( 2 === count( $args ) ) {
277 $x = $this->scale_number( $args[0] );
278 $y = $this->scale_number( $args[1] );
279
280 if ( null === $x || null === $y ) {
281 return null;
282 }
283
284 return Transform_Scale_Prop_Type::generate( [
285 'x' => Number_Prop_Type::generate( $x ),
286 'y' => Number_Prop_Type::generate( $y ),
287 'z' => Number_Prop_Type::generate( self::ONE_SCALE ),
288 ] );
289 }
290
291 return null;
292 }
293
294 private function parse_scale_axis( array $args, string $axis ): ?array {
295 if ( 1 !== count( $args ) ) {
296 return null;
297 }
298
299 $n = $this->scale_number( $args[0] );
300
301 if ( null === $n ) {
302 return null;
303 }
304
305 return Transform_Scale_Prop_Type::generate( [
306 'x' => Number_Prop_Type::generate( 'x' === $axis ? $n : self::ONE_SCALE ),
307 'y' => Number_Prop_Type::generate( 'y' === $axis ? $n : self::ONE_SCALE ),
308 'z' => Number_Prop_Type::generate( 'z' === $axis ? $n : self::ONE_SCALE ),
309 ] );
310 }
311
312 private function parse_scale_3d( array $args ): ?array {
313 if ( 3 !== count( $args ) ) {
314 return null;
315 }
316
317 $x = $this->scale_number( $args[0] );
318 $y = $this->scale_number( $args[1] );
319 $z = $this->scale_number( $args[2] );
320
321 if ( null === $x || null === $y || null === $z ) {
322 return null;
323 }
324
325 return Transform_Scale_Prop_Type::generate( [
326 'x' => Number_Prop_Type::generate( $x ),
327 'y' => Number_Prop_Type::generate( $y ),
328 'z' => Number_Prop_Type::generate( $z ),
329 ] );
330 }
331
332 private function parse_rotate_axis( array $args, string $axis ): ?array {
333 if ( 1 !== count( $args ) ) {
334 return null;
335 }
336
337 $val = $this->rotate_size( $args[0] );
338
339 if ( null === $val ) {
340 return null;
341 }
342
343 return Transform_Rotate_Prop_Type::generate( [
344 'x' => Size_Prop_Type::generate( 'x' === $axis ? $val : self::ZERO_ROTATE ),
345 'y' => Size_Prop_Type::generate( 'y' === $axis ? $val : self::ZERO_ROTATE ),
346 'z' => Size_Prop_Type::generate( 'z' === $axis ? $val : self::ZERO_ROTATE ),
347 ] );
348 }
349
350 private function move_size( string $token ): ?array {
351 $parsed = Size_Value_Parser::parse( $token );
352
353 if ( null === $parsed || ! in_array( $parsed['unit'], self::MOVE_UNITS, true ) ) {
354 return null;
355 }
356
357 return $parsed;
358 }
359
360 private function rotate_size( string $token ): ?array {
361 $parsed = Size_Value_Parser::parse( $token );
362
363 if ( null === $parsed || ! in_array( $parsed['unit'], self::ROTATE_UNITS, true ) ) {
364 return null;
365 }
366
367 return $parsed;
368 }
369
370 private function scale_number( string $token ): ?float {
371 if ( ! is_numeric( $token ) ) {
372 return null;
373 }
374
375 return (float) $token;
376 }
377
378 private function current_fields( $existing ): array {
379 if ( is_array( $existing ) && isset( $existing['value'] ) ) {
380 return $existing['value'];
381 }
382
383 return [];
384 }
385 }
386