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

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