PluginProbe
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder / 2.16.0
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder v2.16.0
2.16.0 2.15.0 2.14.0 2.13.0 2.13.1 2.12.0 2.11.1 2.11.0 2.10.0 2.9.0 2.7.4 2.7.5 2.7.6 2.7.7 2.8.0 2.8.1 2.9.1 trunk 1.0 1.0-beta1 1.0-beta2 1.0-beta3 1.0.1 1.0.2 1.0.3 All 83 releases
ablocks / includes / blocks / price-menu-item / block.php

block.php in aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder 2.16.0, at includes/blocks/price-menu-item/block.php

353 lines 16.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace ABlocks\Blocks\PriceMenuItem;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 use ABlocks\Classes\BlockBaseAbstract;
9 use ABlocks\Classes\CssGenerator;
10 use ABlocks\Controls\Icon;
11 use ABlocks\Controls\Alignment;
12 use ABlocks\Controls\Typography;
13 use ABlocks\Controls\TextShadow;
14 use ABlocks\Controls\TextStroke;
15 use ABlocks\Controls\Range;
16 use ABlocks\Controls\Color;
17 use ABlocks\Helper;
18
19 class Block extends BlockBaseAbstract {
20 protected $parent_block_name = 'price-menu';
21 protected $block_name = 'price-menu-item';
22
23 public function build_css( $attributes ) {
24
25 $css_generator = new CssGenerator( $attributes, $this->block_name );
26 // Icon Style
27 $css_generator->add_class_styles(
28 '{{WRAPPER}} .ablocks-icon-wrap',
29 Icon::get_wrapper_css( $attributes ),
30 Icon::get_wrapper_css( $attributes, 'Tablet' ),
31 Icon::get_wrapper_css( $attributes, 'Mobile' ),
32 $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return Icon::get_wrapper_css( $attributes, $device ); } )
33 );
34 $css_generator->add_class_styles(
35 '{{WRAPPER}} .ablocks-icon-wrap:hover',
36 Icon::get_wrapper_hover_css( $attributes ),
37 Icon::get_wrapper_hover_css( $attributes, 'Tablet' ),
38 Icon::get_wrapper_hover_css( $attributes, 'Mobile' ),
39 $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return Icon::get_wrapper_hover_css( $attributes, $device ); } )
40 );
41 $css_generator->add_class_styles(
42 '{{WRAPPER}} .ablocks-icon-wrap img.ablocks-image-icon',
43 Icon::get_element_image_css( $attributes ),
44 Icon::get_element_image_css( $attributes, 'Tablet' ),
45 Icon::get_element_image_css( $attributes, 'Mobile' ),
46 $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return Icon::get_element_image_css( $attributes, $device ); } )
47 );
48 $css_generator->add_class_styles(
49 '{{WRAPPER}} .ablocks-icon-wrap img.ablocks-image-icon:hover',
50 Icon::get_element_image_hover_css( $attributes ),
51 Icon::get_element_image_hover_css( $attributes, 'Tablet' ),
52 Icon::get_element_image_hover_css( $attributes, 'Mobile' ),
53 $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return Icon::get_element_image_hover_css( $attributes, $device ); } )
54 );
55 $css_generator->add_class_styles(
56 '{{WRAPPER}} .ablocks-icon-wrap svg.ablocks-svg-icon',
57 Icon::get_element_css( $attributes ),
58 Icon::get_element_css( $attributes, 'Tablet' ),
59 Icon::get_element_css( $attributes, 'Mobile' ),
60 $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return Icon::get_element_css( $attributes, $device ); } )
61 );
62 $css_generator->add_class_styles(
63 '{{WRAPPER}} .ablocks-icon-wrap svg.ablocks-svg-icon:hover',
64 Icon::get_element_image_hover_css( $attributes ),
65 Icon::get_element_image_hover_css( $attributes, 'Tablet' ),
66 Icon::get_element_image_hover_css( $attributes, 'Mobile' ),
67 $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return Icon::get_element_image_hover_css( $attributes, $device ); } )
68 );
69 // TitleText CSS
70 $css_generator->add_class_styles(
71 '{{WRAPPER}} .ablocks-price-menu-item-details-title',
72 $this->get_title_text_css( $attributes, '' ),
73 $this->get_title_text_css( $attributes, 'Tablet' ),
74 $this->get_title_text_css( $attributes, 'Mobile' ),
75 $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_title_text_css( $attributes, $device ); } )
76 );
77 // DescriptionText CSS
78 $css_generator->add_class_styles(
79 '{{WRAPPER}} .ablocks-price-menu-item-details-des',
80 $this->get_description_text_css( $attributes, '' ),
81 $this->get_description_text_css( $attributes, 'Tablet' ),
82 $this->get_description_text_css( $attributes, 'Mobile' ),
83 $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_description_text_css( $attributes, $device ); } )
84 );
85 // SeparatorText CSS
86 $css_generator->add_class_styles(
87 '{{WRAPPER}} .ablocks-price-menu-divider__pattern-' . ( $attributes['dividerType'] === 'mask-style' ? 'mask' : 'css' ),
88 $this->get_divider_css( $attributes, '' ),
89 $this->get_divider_css( $attributes, 'Tablet' ),
90 $this->get_divider_css( $attributes, 'Mobile' ),
91 $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_divider_css( $attributes, $device ); } )
92 );
93 // PriceText CSS
94 $css_generator->add_class_styles(
95 '{{WRAPPER}} .ablocks-price-menu-item-price',
96 $this->get_price_text_css( $attributes, '' ),
97 $this->get_price_text_css( $attributes, 'Tablet' ),
98 $this->get_price_text_css( $attributes, 'Mobile' ),
99 $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return $this->get_price_text_css( $attributes, $device ); } )
100 );
101 // Divider width (item override of the parent's width)
102 $css_generator->add_class_styles(
103 '{{WRAPPER}} .ablocks-divider',
104 self::get_divider_width_css( $attributes, '' ),
105 self::get_divider_width_css( $attributes, 'Tablet' ),
106 self::get_divider_width_css( $attributes, 'Mobile' ),
107 $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return self::get_divider_width_css( $attributes, $device ); } )
108 );
109 $css_generator->add_class_styles(
110 '{{WRAPPER}} .ablocks-price-menu-item',
111 self::get_item_width_css( $attributes, '' ),
112 self::get_item_width_css( $attributes, 'Tablet' ),
113 self::get_item_width_css( $attributes, 'Mobile' ),
114 $css_generator->custom_device_map( function ( $device ) use ( $attributes ) { return self::get_item_width_css( $attributes, $device ); } )
115 );
116 $css_generator->add_class_styles(
117 '{{WRAPPER}} .ablocks-price-menu-item-details-brief > .ablocks-price-menu-item-price',
118 self::get_price_row_css( $attributes )
119 );
120 // Responsive placements: hide the slots not used on each device.
121 foreach ( [
122 'divider' => isset( $attributes['placeDivider'] ) ? $attributes['placeDivider'] : '',
123 'price' => isset( $attributes['placePrice'] ) ? $attributes['placePrice'] : '',
124 ] as $kind => $placement ) {
125 foreach ( self::get_used_placements( $placement ) as $slot ) {
126 $css_generator->add_class_styles(
127 '{{WRAPPER}} .' . self::get_slot_class_name( $kind, $slot ),
128 self::get_slot_css( $placement, $slot, '' ),
129 self::get_slot_css( $placement, $slot, 'Tablet' ),
130 self::get_slot_css( $placement, $slot, 'Mobile' ),
131 $css_generator->custom_device_map( function ( $device ) use ( $placement, $slot ) { return self::get_slot_css( $placement, $slot, $device ); } )
132 );
133 }
134 }
135
136 return $css_generator->generate_css();
137 }
138
139 /**
140 * Divider width as a custom property; mirrors get_divider_width_css() in
141 * src/blocks/price-menu-item/styling.js. The parent passes its own property
142 * and default. A width without a stored unit keeps the legacy unit: % with a
143 * description, px without.
144 */
145 public static function get_divider_width_css( $attributes, $device = '', $property = '--ablocks-pm-item-divider-width', $default_value = null ) {
146 return Range::get_css([
147 'attributeValue' => isset( $attributes['width'] ) ? $attributes['width'] : [],
148 'attributeObjectKey' => 'value',
149 'isResponsive' => true,
150 'defaultValue' => $default_value,
151 'hasUnit' => true,
152 'unitDefaultValue' => ! empty( $attributes['allowDescription'] ) ? '%' : 'px',
153 'property' => $property,
154 'device' => $device,
155 ]);
156 }
157
158 // A responsive member for the device, inherited from wider devices; '' when unset.
159 private static function resolve_member( $value, $key, $device ) {
160 $target = 'Desktop' === $device ? '' : (string) $device;
161 foreach ( array_merge( [ $target ], Helper::get_responsive_ancestors( $target ) ) as $suffix ) {
162 $member = is_array( $value ) && isset( $value[ $key . $suffix ] ) && ! is_array( $value[ $key . $suffix ] ) ? (string) $value[ $key . $suffix ] : '';
163 if ( Helper::has_responsive_value( $member ) ) {
164 return $member;
165 }
166 }
167 return '';
168 }
169
170 // A % Title & price gap in effect on this device (unit default px).
171 public static function is_percent_title_price_gap( $attributes, $device = '' ) {
172 $gap = isset( $attributes['titlePriceGap'] ) ? $attributes['titlePriceGap'] : [];
173 return '' !== self::resolve_member( $gap, 'value', $device ) && '%' === Range::get_unit( [
174 'attributeValue' => $gap,
175 'attributeObjectKey' => 'value',
176 'unitDefaultValue' => 'px',
177 'device' => $device,
178 ] );
179 }
180
181 // A % on the title row; mirrors isPercentTitleRow() in styling.js. The legacy
182 // implicit % (Allow description, no stored unit) is left alone.
183 public static function is_percent_title_row( $attributes, $device = '' ) {
184 return self::is_percent_title_price_gap( $attributes, $device ) || (
185 ! empty( $attributes['allowDivider'] ) &&
186 'near title' === self::get_placement( isset( $attributes['placeDivider'] ) ? $attributes['placeDivider'] : '', $device ) &&
187 '%' === self::resolve_member( isset( $attributes['width'] ) ? $attributes['width'] : [], 'valueUnit', $device )
188 );
189 }
190
191 private static function has_percent_unit( $value ) {
192 if ( ! is_array( $value ) ) {
193 return false;
194 }
195 foreach ( $value as $key => $member ) {
196 if ( 0 === strpos( $key, 'valueUnit' ) && '%' === $member ) {
197 return true;
198 }
199 }
200 return false;
201 }
202
203 public static function has_percent_title_price_gap( $attributes ) {
204 return self::has_percent_unit( isset( $attributes['titlePriceGap'] ) ? $attributes['titlePriceGap'] : [] );
205 }
206
207 // Whether any device can use the % title row, so narrower devices that do
208 // not must reset the item width they would otherwise inherit.
209 public static function has_percent_title_row( $attributes ) {
210 return self::has_percent_title_price_gap( $attributes ) || (
211 ! empty( $attributes['allowDivider'] ) &&
212 in_array( 'near title', self::get_used_placements( isset( $attributes['placeDivider'] ) ? $attributes['placeDivider'] : '' ), true ) &&
213 self::has_percent_unit( isset( $attributes['width'] ) ? $attributes['width'] : [] )
214 );
215 }
216
217 // Once Price alignment is chosen the item spans its row and the price fills
218 // the space after the title/divider so text-align can position it. A % on
219 // the title row needs the same; mirrors get_item_width_css() in styling.js.
220 public static function get_item_width_css( $attributes, $device = '' ) {
221 if ( ! empty( $attributes['priceAlignmentCustom'] ) ) {
222 return $device ? [] : [ 'width' => '100%' ];
223 }
224 if ( self::is_percent_title_row( $attributes, $device ) ) {
225 return [ 'width' => '100%' ];
226 }
227 return $device && self::has_percent_title_row( $attributes ) ? [ 'width' => 'fit-content' ] : [];
228 }
229
230 public static function get_price_row_css( $attributes ) {
231 return ! empty( $attributes['priceAlignmentCustom'] ) ? [ 'flex-grow' => '1' ] : [];
232 }
233
234 /**
235 * placeDivider / placePrice: a legacy string or a responsive array
236 * ( value, valueTablet, valueMobile, … ). Mirrors placement.js.
237 */
238 public static function get_placement( $placement, $device = '' ) {
239 $placement = is_array( $placement ) ? $placement : [ 'value' => (string) $placement ];
240 $target = 'Desktop' === $device ? '' : (string) $device;
241 foreach ( array_merge( [ $target ], Helper::get_responsive_ancestors( $target ) ) as $suffix ) {
242 $value = isset( $placement[ 'value' . $suffix ] ) ? $placement[ 'value' . $suffix ] : '';
243 if ( Helper::has_responsive_value( $value ) ) {
244 return $value;
245 }
246 }
247 return '';
248 }
249
250 public static function get_used_placements( $placement ) {
251 if ( ! is_array( $placement ) ) {
252 return Helper::has_responsive_value( $placement ) ? [ $placement ] : [];
253 }
254 $used = [];
255 foreach ( $placement as $key => $value ) {
256 if ( 0 === strpos( $key, 'value' ) && is_string( $value ) && Helper::has_responsive_value( $value ) ) {
257 $used[] = $value;
258 }
259 }
260 return array_values( array_unique( $used ) );
261 }
262
263 // The placement is a block attribute and this name lands in a selector of
264 // an inline <style>, which esc_css_value() does not cover; keep it a class.
265 public static function get_slot_class_name( $kind, $placement ) {
266 return 'ablocks-price-menu-' . $kind . '-slot--' . sanitize_html_class( preg_replace( '/\s+/', '-', $placement ) );
267 }
268
269 public static function get_slot_css( $placement, $slot, $device = '' ) {
270 if ( count( self::get_used_placements( $placement ) ) < 2 ) {
271 return [];
272 }
273 return [ 'display' => self::get_placement( $placement, $device ) === $slot ? 'block' : 'none' ];
274 }
275
276 public function get_title_text_css( $attributes, $device = '' ) {
277 $typographyValueGlobal = ! empty( $attributes['titleTypographyGlobal'] ) ? $attributes['titleTypographyGlobal'] : array();
278 return array_merge(
279 [ 'color' => Color::get_css( isset( $attributes['titleColor'] ) ? $attributes['titleColor'] : '' ) ],
280 isset( $attributes['titleTypography'] ) ? Typography::get_css( $attributes['titleTypography'], '', $device, $typographyValueGlobal ) : [],
281 isset( $attributes['titleTextStroke'] ) ? TextStroke::get_css( $attributes['titleTextStroke'], '', $device ) : [],
282 isset( $attributes['titleTextShadow'] ) ? TextShadow::get_css( $attributes['titleTextShadow'], '', $device ) : [],
283 isset( $attributes['titleAlignment'] ) ? Alignment::get_css( $attributes['titleAlignment'], 'text-align', $device ) : [],
284 );
285 }
286 public function get_description_text_css( $attributes, $device = '' ) {
287 $typographyValueGlobal = ! empty( $attributes['descriptionTypographyGlobal'] ) ? $attributes['descriptionTypographyGlobal'] : array();
288
289 return array_merge(
290 [ 'color' => Color::get_css( isset( $attributes['descriptionColor'] ) ? $attributes['descriptionColor'] : '' ) ],
291 isset( $attributes['descriptionTypography'] ) ? Typography::get_css( $attributes['descriptionTypography'], '', $device, $typographyValueGlobal ) : [],
292 isset( $attributes['descriptionTextStroke'] ) ? TextStroke::get_css( $attributes['descriptionTextStroke'], '', $device ) : [],
293 isset( $attributes['descriptionTextShadow'] ) ? TextShadow::get_css( $attributes['descriptionTextShadow'], '', $device ) : [],
294 isset( $attributes['descriptionAlignment'] ) ? Alignment::get_css( $attributes['descriptionAlignment'], 'text-align', $device ) : [],
295 );
296 }
297 public function get_divider_css( $attributes, $device = '' ) {
298 $css = [];
299
300 if ( ! empty( $attributes['color'] ) ) {
301 $css['--ablocks-divider-pattern-color'] = Color::get_css(
302 isset( $attributes['color'] ) ? $attributes['color'] : '#000000');
303
304 }
305
306 $moreRangeCSS = [];
307 if ( isset( $attributes['dividerType'] ) && $attributes['dividerType'] === 'mask-style' && isset( $attributes['size'] ) && ! empty( $attributes['size'] ) ) {
308 $moreRangeCSS = Range::get_css([
309 'attributeValue' => $attributes['size'],
310 'attribute_object_key' => 'value',
311 'isResponsive' => false,
312 'defaultValue' => null,
313 'hasUnit' => false,
314 'unitDefaultValue' => 'px',
315 'property' => '--ablocks-divider-pattern-height',
316 ]);
317 } elseif ( isset( $attributes['weight'] ) && ! empty( $attributes['weight'] ) ) {
318 $moreRangeCSS = Range::get_css([
319 'attributeValue' => $attributes['weight'],
320 'attribute_object_key' => 'value',
321 'isResponsive' => false,
322 'defaultValue' => null,
323 'hasUnit' => false,
324 'unitDefaultValue' => 'px',
325 'property' => '--ablocks-divider-pattern-weight',
326 ]);
327 }//end if
328
329 if ( ! empty( $attributes['dividerPatternUrl'] ) ) {
330 if ( $attributes['dividerType'] === 'mask-style' ) {
331 $css['--ablocks-divider-pattern-url'] = 'url(' . $attributes['dividerPatternUrl'] . ')';
332 } else {
333 $css['--ablocks-divider-pattern-style'] = $attributes['dividerPatternUrl'];
334 }
335 }
336
337 return array_merge(
338 $css,
339 $moreRangeCSS,
340 );
341 }
342 public function get_price_text_css( $attributes, $device = '' ) {
343 $typographyValueGlobal = ! empty( $attributes['priceTypographyGlobal'] ) ? $attributes['priceTypographyGlobal'] : array();
344 return array_merge(
345 [ 'color' => Color::get_css( isset( $attributes['priceColor'] ) ? $attributes['priceColor'] : '' ) ],
346 isset( $attributes['priceTypography'] ) ? Typography::get_css( $attributes['priceTypography'], '', $device, $typographyValueGlobal ) : [],
347 isset( $attributes['priceTextStroke'] ) ? TextStroke::get_css( $attributes['priceTextStroke'], '', $device ) : [],
348 isset( $attributes['priceTextShadow'] ) ? TextShadow::get_css( $attributes['priceTextShadow'], '', $device ) : [],
349 isset( $attributes['priceAlignment'] ) ? Alignment::get_css( $attributes['priceAlignment'], 'text-align', $device ) : [],
350 );
351 }
352 }
353