PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.0
Elementor Website Builder – more than just a page builder v4.3.0
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 / background-layer-field-converter.php

background-layer-field-converter.php in Elementor Website Builder – more than just a page builder 4.3.0, at modules/atomic-widgets/css-converter/converters/background-layer-field-converter.php

176 lines 5.6 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\Background_Image_Overlay_Prop_Type;
10 use Elementor\Modules\AtomicWidgets\PropTypes\Background_Overlay_Prop_Type;
11 use Elementor\Modules\AtomicWidgets\PropTypes\Background_Prop_Type;
12 use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\String_Prop_Type;
13 use Elementor\Modules\AtomicWidgets\PropTypes\Size_Prop_Type;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 /**
20 * Converter for scalar sub-layer longhands of `background-image`: background-repeat,
21 * background-attachment, background-size, background-position. One instance per CSS property.
22 *
23 * Reads the ordered list of `background-image-overlay` items currently in the `background` context
24 * object. If no image layers exist yet the declaration is declined (-> custom_css) because there is
25 * nothing to attach the value to. CSS comma-separated lists are correlated to layers by position: a
26 * single value applies to all layers; multiple values are distributed 1:1. A mismatch in count
27 * declines the declaration.
28 *
29 * Each per-layer token is validated first against an optional string enum (e.g. repeat, cover).
30 * When a token is not in the enum and a pair prop type is configured, the token is re-parsed as two
31 * whitespace-separated Size values (e.g. `50% 50%` for size, `10px 20px` for position offset).
32 * Any token that fails both checks declines the whole declaration.
33 */
34 class Background_Layer_Field_Converter extends Property_Converter_Base {
35 private string $property;
36 private string $field_key;
37
38 /**
39 * @var string[]|null
40 */
41 private ?array $allowed_values;
42
43 /**
44 * @var class-string|null
45 */
46 private ?string $pair_prop_type;
47
48 /**
49 * @var string[]
50 */
51 private array $pair_keys;
52
53 /**
54 * @param string $property The CSS longhand property this converter owns.
55 * @param string $field_key The field key inside each background-image-overlay value.
56 * @param string[]|null $allowed_values String enum allowlist, or null to skip enum check.
57 * @param string|null $pair_prop_type Object_Prop_Type class for size-pair values, or null.
58 * @param string[] $pair_keys The two field keys of the pair type (e.g. ['x','y']).
59 */
60 public function __construct(
61 string $property,
62 string $field_key,
63 ?array $allowed_values,
64 ?string $pair_prop_type = null,
65 array $pair_keys = []
66 ) {
67 $this->property = $property;
68 $this->field_key = $field_key;
69 $this->allowed_values = $allowed_values;
70 $this->pair_prop_type = $pair_prop_type;
71 $this->pair_keys = $pair_keys;
72 }
73
74 protected function get_supported_properties(): array {
75 return [ $this->property ];
76 }
77
78 protected function convert_null( Conversion_Context $context, array $rule ): bool {
79 return true;
80 }
81
82 protected function do_convert( Conversion_Context $context, array $rule ): bool {
83 $overlay_items = $this->get_overlay_items( $context );
84 $image_indices = array_keys(
85 array_filter( $overlay_items, fn( $item ) => Background_Image_Overlay_Prop_Type::get_key() === ( $item['$$type'] ?? null ) )
86 );
87
88 if ( empty( $image_indices ) ) {
89 return false;
90 }
91
92 $tokens = Css_Token_Splitter::split_by_comma( trim( $rule['value'] ) );
93 $prop_values = [];
94
95 foreach ( $tokens as $token ) {
96 $leaf = $this->parse_token( $token );
97
98 if ( null === $leaf ) {
99 return false;
100 }
101
102 $prop_values[] = $leaf;
103 }
104
105 $layer_count = count( $image_indices );
106 $value_count = count( $prop_values );
107
108 if ( 1 !== $value_count && $layer_count !== $value_count ) {
109 return false;
110 }
111
112 foreach ( $image_indices as $order => $index ) {
113 $value_index = 1 === $value_count ? 0 : $order;
114 $overlay_items[ $index ]['value'][ $this->field_key ] = $prop_values[ $value_index ];
115 }
116
117 $background = $context->get_prop( 'background' );
118 $fields = is_array( $background ) && isset( $background['value'] ) ? $background['value'] : [];
119 $fields['background-overlay'] = Background_Overlay_Prop_Type::generate( array_values( $overlay_items ) );
120
121 $context->set_prop( 'background', Background_Prop_Type::generate( $fields ) );
122
123 return true;
124 }
125
126 private function get_overlay_items( Conversion_Context $context ): array {
127 $background = $context->get_prop( 'background' );
128
129 if ( ! is_array( $background ) ) {
130 return [];
131 }
132
133 $overlay = $background['value']['background-overlay'] ?? null;
134
135 if ( ! is_array( $overlay ) ) {
136 return [];
137 }
138
139 return $overlay['value'] ?? [];
140 }
141
142 protected function parse_token( string $token ): ?array {
143 $token = trim( $token );
144
145 if ( null !== $this->allowed_values && in_array( $token, $this->allowed_values, true ) ) {
146 return String_Prop_Type::generate( $token );
147 }
148
149 if ( null !== $this->pair_prop_type ) {
150 return $this->parse_size_pair( $token );
151 }
152
153 return null;
154 }
155
156 private function parse_size_pair( string $token ): ?array {
157 $parts = Css_Token_Splitter::split_by_whitespace( $token );
158
159 if ( 2 !== count( $parts ) ) {
160 return null;
161 }
162
163 $first = Size_Value_Parser::parse( $parts[0] );
164 $second = Size_Value_Parser::parse( $parts[1] );
165
166 if ( null === $first || null === $second ) {
167 return null;
168 }
169
170 return ( $this->pair_prop_type )::generate( [
171 $this->pair_keys[0] => Size_Prop_Type::generate( $first ),
172 $this->pair_keys[1] => Size_Prop_Type::generate( $second ),
173 ] );
174 }
175 }
176