PluginProbe
Elementor Website Builder – more than just a page builder / 3.32.3
Elementor Website Builder – more than just a page builder v3.32.3
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 4.0.7 All 451 releases
elementor / modules / atomic-widgets / styles / styles-renderer.php

styles-renderer.php in Elementor Website Builder – more than just a page builder 3.32.3, at modules/atomic-widgets/styles/styles-renderer.php

191 lines 5.0 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\Styles;
4
5 use Elementor\Core\Utils\Collection;
6 use Elementor\Modules\AtomicWidgets\Module;
7 use Elementor\Plugin;
8 use Elementor\Utils;
9 use Elementor\Modules\AtomicWidgets\PropsResolver\Render_Props_Resolver;
10
11 class Styles_Renderer {
12 const DEFAULT_SELECTOR_PREFIX = '.elementor';
13
14 /**
15 * @var array<string, array{direction: 'min' | 'max', value: int, is_enabled: boolean}>
16 */
17 private array $breakpoints;
18
19 private $on_prop_transform;
20
21 private string $selector_prefix;
22
23 /**
24 * @param array<string, array{direction: 'min' | 'max', value: int, is_enabled: boolean}> $breakpoints
25 * @param string $selector_prefix
26 */
27 private function __construct( array $breakpoints, string $selector_prefix = self::DEFAULT_SELECTOR_PREFIX ) {
28 $this->breakpoints = $breakpoints;
29 $this->selector_prefix = $selector_prefix;
30 }
31
32 public static function make( array $breakpoints, string $selector_prefix = self::DEFAULT_SELECTOR_PREFIX ): self {
33 return new self( $breakpoints, $selector_prefix );
34 }
35
36 /**
37 * Render the styles to a CSS string.
38 *
39 * Styles format:
40 * array<int, array{
41 * id: string,
42 * type: string,
43 * cssName: string | null,
44 * variants: array<int, array{
45 * props: array<string, mixed>,
46 * meta: array<string, mixed>
47 * }>
48 * }>
49 *
50 * @param array $styles Array of style definitions.
51 *
52 * @return string Rendered CSS string.
53 */
54 public function render( array $styles ): string {
55 $css_style = [];
56
57 foreach ( $styles as $style_def ) {
58 $style = $this->style_definition_to_css_string( $style_def );
59 $css_style[] = $style;
60 }
61
62 return implode( '', $css_style );
63 }
64
65 public function on_prop_transform( callable $callback ): self {
66 $this->on_prop_transform = $callback;
67
68 return $this;
69 }
70
71 private function style_definition_to_css_string( array $style ): string {
72 $base_selector = $this->get_base_selector( $style );
73
74 if ( ! $base_selector ) {
75 return '';
76 }
77
78 $stylesheet = [];
79
80 foreach ( $style['variants'] as $variant ) {
81 $style_declaration = $this->variant_to_css_string( $base_selector, $variant );
82
83 if ( $style_declaration ) {
84 $stylesheet[] = $style_declaration;
85 }
86 }
87
88 return implode( '', $stylesheet );
89 }
90
91 private function get_base_selector( array $style_def ): ?string {
92 $map = [
93 'class' => '.',
94 ];
95
96 if (
97 isset( $style_def['type'] ) &&
98 isset( $style_def['id'] ) &&
99 isset( $map[ $style_def['type'] ] ) &&
100 $style_def['id']
101 ) {
102 $type = $map[ $style_def['type'] ];
103 $name = $style_def['cssName'] ?? $style_def['id'];
104
105 $selector_parts = array_filter( [
106 $this->selector_prefix,
107 "{$type}{$name}",
108 ] );
109
110 return implode( ' ', $selector_parts );
111 }
112
113 return null;
114 }
115
116 private function variant_to_css_string( string $base_selector, array $variant ): string {
117 $css = $this->props_to_css_string( $variant['props'] ) ?? '';
118 $custom_css = $this->custom_css_to_css_string( $variant['custom_css'] ?? null );
119
120 if ( ! $css && ! $custom_css ) {
121 return '';
122 }
123
124 $state = isset( $variant['meta']['state'] ) ? ':' . $variant['meta']['state'] : '';
125 $selector = $base_selector . $state;
126
127 $style_declaration = $selector . '{' . $css . $custom_css . '}';
128
129 if ( isset( $variant['meta']['breakpoint'] ) ) {
130 $style_declaration = $this->wrap_with_media_query( $variant['meta']['breakpoint'], $style_declaration );
131 }
132
133 return $style_declaration;
134 }
135
136 private function props_to_css_string( array $props ): string {
137 $schema = Style_Schema::get();
138
139 return Collection::make( Render_Props_Resolver::for_styles()->resolve( $schema, $props ) )
140 ->filter()
141 ->map( function ( $value, $prop ) {
142 if ( $this->on_prop_transform ) {
143 call_user_func( $this->on_prop_transform, $prop, $value );
144 }
145
146 return $prop . ':' . $value . ';';
147 } )
148 ->implode( '' );
149 }
150
151 private function custom_css_to_css_string( ?array $custom_css ): string {
152 $is_feature_active = Plugin::$instance->experiments->is_feature_active( Module::EXPERIMENT_CUSTOM_CSS );
153
154 return $is_feature_active && ! empty( $custom_css['raw'] )
155 ? Utils::decode_string( $custom_css['raw'], '' ) . '\n'
156 : '';
157 }
158
159 private function wrap_with_media_query( string $breakpoint_id, string $css ): string {
160 if ( ! isset( $this->breakpoints[ $breakpoint_id ] ) ) {
161 return $css;
162 }
163
164 $breakpoint = $this->breakpoints[ $breakpoint_id ];
165 if ( isset( $breakpoint['is_enabled'] ) && ! $breakpoint['is_enabled'] ) {
166 return '';
167 }
168
169 $query = $this->get_media_query( $this->breakpoints[ $breakpoint_id ] );
170
171 return $query ? $query . '{' . $css . '}' : $css;
172 }
173
174 public static function get_media_query( $breakpoint ): ?string {
175 if ( isset( $breakpoint['is_enabled'] ) && ! $breakpoint['is_enabled'] ) {
176 return null;
177 }
178
179 $size = self::get_breakpoint_size( $breakpoint );
180
181 return $size ? '@media(' . $size . ')' : null;
182 }
183
184 private static function get_breakpoint_size( array $breakpoint ): ?string {
185 $bound = 'min' === $breakpoint['direction'] ? 'min-width' : 'max-width';
186 $width = $breakpoint['value'] . 'px';
187
188 return "{$bound}:{$width}";
189 }
190 }
191