PluginProbe
Elementor Website Builder – more than just a page builder / 3.35.9
Elementor Website Builder – more than just a page builder v3.35.9
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.35.9, at modules/atomic-widgets/styles/styles-renderer.php

193 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 if ( isset( $variant['meta']['state'] ) ) {
125 $selector = Style_States::get_selector_with_state( $base_selector, $variant['meta']['state'] );
126 } else {
127 $selector = $base_selector;
128 }
129
130 $style_declaration = $selector . '{' . $css . $custom_css . '}';
131
132 if ( isset( $variant['meta']['breakpoint'] ) ) {
133 $style_declaration = $this->wrap_with_media_query( $variant['meta']['breakpoint'], $style_declaration );
134 }
135
136 return $style_declaration;
137 }
138
139
140 private function props_to_css_string( array $props ): string {
141 $schema = Style_Schema::get();
142
143 return Collection::make( Render_Props_Resolver::for_styles()->resolve( $schema, $props ) )
144 ->filter()
145 ->map( function ( $value, $prop ) {
146 if ( $this->on_prop_transform ) {
147 call_user_func( $this->on_prop_transform, $prop, $value );
148 }
149
150 return $prop . ':' . $value . ';';
151 } )
152 ->implode( '' );
153 }
154
155 private function custom_css_to_css_string( ?array $custom_css ): string {
156 return ! empty( $custom_css['raw'] )
157 ? Utils::decode_string( $custom_css['raw'], '' ) . '\n'
158 : '';
159 }
160
161 private function wrap_with_media_query( string $breakpoint_id, string $css ): string {
162 if ( ! isset( $this->breakpoints[ $breakpoint_id ] ) ) {
163 return $css;
164 }
165
166 $breakpoint = $this->breakpoints[ $breakpoint_id ];
167 if ( isset( $breakpoint['is_enabled'] ) && ! $breakpoint['is_enabled'] ) {
168 return '';
169 }
170
171 $query = $this->get_media_query( $this->breakpoints[ $breakpoint_id ] );
172
173 return $query ? $query . '{' . $css . '}' : $css;
174 }
175
176 public static function get_media_query( $breakpoint ): ?string {
177 if ( isset( $breakpoint['is_enabled'] ) && ! $breakpoint['is_enabled'] ) {
178 return null;
179 }
180
181 $size = self::get_breakpoint_size( $breakpoint );
182
183 return $size ? '@media(' . $size . ')' : null;
184 }
185
186 private static function get_breakpoint_size( array $breakpoint ): ?string {
187 $bound = 'min' === $breakpoint['direction'] ? 'min-width' : 'max-width';
188 $width = $breakpoint['value'] . 'px';
189
190 return "{$bound}:{$width}";
191 }
192 }
193