PluginProbe
UiCore Elements – Free widgets and templates for Elementor / 1.3.18
UiCore Elements – Free widgets and templates for Elementor v1.3.18
1.3.18 1.3.17 1.3.16 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.2.0 1.2.1 1.2.2 1.2.3 All 42 releases
← All changes | includes/utils/product-component.php +442 -274 1.0.12 → 1.3.18 View file →
@@ -1,274 +1,442 @@
1 -<?php
2 -namespace UiCoreElements\Utils;
3 -
4 -use UiCoreElements\Helper;
5 -use UiCoreElements\Controls\Query;
6 -
7 -use UiCoreElements\Utils\Meta_Trait;
8 -use UiCoreElements\Utils\Post_Trait;
9 -
10 -defined('ABSPATH') || exit();
11 -
12 -/**
13 - * Product Component
14 - *
15 - * @since 1.0.11
16 - */
17 -
18 -trait Product_Trait {
19 -
20 - use Post_Trait,
21 - Meta_Trait;
22 -
23 - /**
24 - * Returns the proper query args for the product loop.
25 - *
26 - * @param array $settings Elementor controls settings.
27 - * @param array|WP_Query $default_query The default query variables.
28 - *
29 - * @return array|stdClass Return the products data, prepared for loop.
30 - */
31 - function TRAIT_query_products($settings, $default_query)
32 - {
33 - $post_type = $settings['product-filter_post_type'];
34 -
35 - switch ($post_type){
36 - case 'current' :
37 - $query_args = $default_query;
38 -
39 - // Set pagination
40 - $query_args['paged'] = Query::get_queried_page($settings);
41 -
42 - // Set tax filters for filter component, if enabled
43 - $queried_filters = Query::get_queried_filters($settings, 'product', 'product-filter');
44 - $query_args['tax_query'] = empty($queried_filters['tax_query']) ? [] : $queried_filters['tax_query'];
45 - break;
46 -
47 - //
48 - case 'related' :
49 - return Helper::get_product_related($settings['item_limit']['size']);
50 - break;
51 -
52 - default :
53 - $query_args = Query::get_query_args('product-filter', $settings, true);
54 - break;
55 - }
56 -
57 - // Set total pages
58 - $query_args['total_pages'] = Query::get_total_pages($query_args);
59 -
60 - // Hide out of stock products
61 - if( $settings['hide_out_of_stock'] === 'yes' ){
62 - $query_args['meta_query'] = [
63 - [
64 - 'key' => '_stock_status',
65 - 'value' => 'instock',
66 - 'compare' => '=',
67 - ]
68 - ];
69 - }
70 -
71 - $this->_query = $query_args;
72 -
73 - return wc_get_products($query_args);
74 - }
75 -
76 -
77 - // Render functions
78 - function get_product_image(object $product)
79 - {
80 - if ($this->get_settings_for_display('image') !== 'yes') {
81 - return;
82 - }
83 -
84 - // Get product image ID
85 - $pic_id = $product->get_image_id();
86 - if (!$pic_id) {
87 - return;
88 - }
89 -
90 - // Get image size
91 - $size = $this->get_settings_for_display('image_size_select') ?? 'uicore-medium';
92 - ?>
93 - <a class="ui-e-post-img-wrapp"
94 - href="<?php echo esc_url($product->get_permalink()); ?>"
95 - title="<?php echo esc_attr__('View Product:', 'uicore-elements') . ' ' . esc_attr($product->get_name()); ?>">
96 -
97 - <?php if ($this->get_settings_for_display('masonry') === 'ui-e-maso') { ?>
98 - <?php
99 - // Get secondary image if available
100 - $sec_img = $this->get_product_secondary_image( $product );
101 - $classes = $sec_img ? 'ui-e-post-img ui-e-hover-hide' : 'ui-e-post-img';
102 -
103 - // Print secondary image markup if exists
104 - echo $sec_img ? wp_kses_post($sec_img) : '';
105 -
106 - // Print main product image
107 - echo wp_get_attachment_image($pic_id, $size, false, ['class' => $classes]);
108 - ?>
109 - <?php } else { ?>
110 - <?php
111 - // Get secondary image with size
112 - $sec_img = $this->get_product_secondary_image( $product, $size, true );
113 - echo $sec_img ? wp_kses_post($sec_img) : '';
114 - ?>
115 - <div class="ui-e-post-img <?php echo $sec_img ? 'ui-e-hover-hide' : ''; ?>"
116 - style="background-image:url(<?php echo wp_get_attachment_image_url($pic_id, $size); ?>)">
117 - </div>
118 - <?php } ?>
119 - </a>
120 - <?php
121 - }
122 - function get_product_secondary_image($product, $size = 'woocommerce_thumbnail', $bg_output = false)
123 - {
124 - // Requested only for products with change-image animation
125 - if( 'ui-e-img-anim-change' !== $this->get_settings_for_display('anim_image') ) {
126 - return false;
127 - }
128 -
129 - // Get product gallery image IDs
130 - $gallery_image_ids = $product->get_gallery_image_ids();
131 -
132 - // Check if there is at least one gallery image
133 - if (empty($gallery_image_ids)) {
134 - return false;
135 - }
136 -
137 - // Get the first image from the gallery
138 - $secondary_image_id = $gallery_image_ids[0];
139 - $secondary_image_url = wp_get_attachment_image_url($secondary_image_id, $size);
140 -
141 - // Output secondary image as <img> or background <div>
142 - if ($bg_output) {
143 - return sprintf(
144 - '<div class="ui-e-post-img ui-e-post-img-secondary" style="background-image: url(%s);"></div>',
145 - esc_url($secondary_image_url)
146 - );
147 - } else {
148 - return wp_get_attachment_image($secondary_image_id, $size, false, [
149 - 'class' => 'ui-e-post-img ui-e-post-img-secondary'
150 - ]);
151 - }
152 - }
153 - function get_product_title($product)
154 - {
155 - if ($this->get_settings_for_display('title') !== 'yes') {
156 - return;
157 - }
158 -
159 - ?>
160 - <a href="<?php echo esc_url($product->get_permalink()); ?>"
161 - title="<?php echo esc_attr__('View Product:', 'uicore-elements') . ' ' . esc_html($product->get_name()); ?>">
162 - <h4 class="ui-e-post-title"><span><?php echo esc_html($product->get_name()); ?></span></h4>
163 - </a>
164 - <?php
165 - }
166 -
167 - /**
168 - * Renders a product item with various settings and options.
169 - * Important: any changes here should also be considered to `TRAIT_render_item()` from Post Component.
170 - *
171 - * @param WC_Product $product The product object.
172 - * @param bool $carousel Indicates if the item needs carousel classnames.
173 - * @param bool $legacy Indicates if the item is using legacy classnames.
174 - * @param bool $is_ajax Indicates if the item is being rendered through REST API.
175 - *
176 - * @return void
177 - */
178 - function TRAIT_render_product($product, $carousel = false, $legacy = false, $is_ajax = false)
179 - {
180 - $settings = $this->get_settings_for_display();
181 - $excerpt_length = $settings['excerpt_trim'];
182 -
183 - // Classnames but checking if we the widget is APG (legacy version)
184 - $item_classes = $legacy ? ['ui-e-post-item', 'ui-e-item'] : ['ui-e-item'] ; // Single item lower wrap class receptor
185 - $hover_item_class = $legacy ? 'anim_item' : 'item_hover_animation'; // item hover animation class
186 -
187 - // If widget is not carousel type, we set animations classes directly on item selector
188 - if(!$carousel) {
189 - $item_classes[] = 'ui-e-animations-wrp';
190 - $item_classes[] = $settings['animate_items'] === 'ui-e-grid-animate' ? 'elementor-invisible' : '';
191 - $item_classes[] = $settings[$hover_item_class] !== '' ? $settings[$hover_item_class] : '';
192 -
193 - } else {
194 - // Get entrance and item hover animation classes
195 - $entrance = (isset($settings['animate_items']) && $settings['animate_items'] == 'ui-e-grid-animate') ? 'elementor-invisible' : '';
196 - $hover = isset($settings[$hover_item_class]) ? $settings[$hover_item_class] : ''; //$settings[$hover_item_class] : null;
197 - $animations = sprintf('%s %s', $entrance, $hover);
198 -
199 - // Check if entrance or hover animation are set
200 - $has_animation = !empty($entrance) || !empty($hover)
201 -
202 - // Prints extra wrappers required by the carousel script
203 - ?>
204 - <div class="ui-e-wrp swiper-slide">
205 - <?php if($has_animation) : ?>
206 - <div class="ui-e-animations-wrp <?php echo esc_attr($animations);?>">
207 - <?php endif; ?>
208 -
209 - <?php } ?>
210 -
211 - <div class="<?php echo esc_attr( implode(' ', $item_classes));?>">
212 - <article <?php post_class('product'); ?>>
213 - <div class="ui-e-post-top">
214 - <?php $this->get_product_image($product); ?>
215 - <?php $this->get_post_meta('top'); ?>
216 - <?php
217 - if($settings['button_position'] === 'ui-e-button-placement-image') {
218 - $this->TRAIT_get_button(true);
219 - }
220 - ?>
221 - </div>
222 - <div class="ui-e-post-content">
223 - <?php $this->get_post_meta('before_title'); ?>
224 - <?php
225 - if ($this->get_settings_for_display('title') === 'yes')
226 - $this->get_product_title($product);
227 - ?>
228 - <?php $this->get_post_meta('after_title'); ?>
229 - <?php
230 - if ($this->get_settings_for_display('excerpt'))
231 - echo wp_kses_post('<div class="ui-e-post-text">' . wp_trim_words(get_the_excerpt(), $excerpt_length) . '</div>');
232 - ?>
233 - <?php $this->get_post_meta('bottom'); // button
234 - ?>
235 - <?php
236 - if( defined('UICORE_VERSION') && version_compare(UICORE_VERSION, '6.0.1', '>=') && $this->get_settings_for_display('show_swatches') === 'yes' ){
237 - // ajax requests works under minimal conditions, wich means Swatches class is not present
238 - if ( $is_ajax ) {
239 - require_once UICORE_INCLUDES . '/woocommerce/components/class-swatches.php';
240 - }
241 -
242 - \UiCore\WooCommerce\Swatches::print_swatches($product); // from Uicore Framework
243 - }
244 - if ( $this->get_settings_for_display('show_button') === 'yes' ) {
245 - // Add match height spacing element if carousel. May look intrusive, but is the simplest method compared
246 - // to absolute positioning or catching last content element to apply margin.
247 - if($carousel && $settings['match_height'] === 'yes'){
248 - ?>
249 - <span class="ui-e-match-height"></span>
250 - <?php
251 - }
252 -
253 - if( !isset($settings['button_position']) || empty($settings['button_position']) ) {
254 - $this->TRAIT_get_button(true);
255 - }
256 - }
257 - ?>
258 - </div>
259 - </article>
260 - </div>
261 -
262 - <?php if($carousel) { ?>
263 - </div>
264 - <?php if($has_animation) : ?>
265 - </div>
266 - <?php endif; ?>
267 -
268 - <?php }
269 - }
270 -
271 - // TODO: discover why using Post_Trait here forces us to have
272 - // this public content_template() to avoid fatal error
273 - public function content_template() {}
274 -}
1 +<?php
2 +
3 +namespace UiCoreElements\Utils;
4 +
5 +use Elementor\Icons_Manager;
6 +
7 +use UiCoreElements\Helper;
8 +use UiCoreElements\Controls\Query;
9 +
10 +use UiCoreElements\Utils\Meta_Trait;
11 +use UiCoreElements\Utils\Post_Trait;
12 +
13 +defined('ABSPATH') || exit();
14 +
15 +/**
16 + * Product Component
17 + *
18 + * @since 1.0.11
19 + */
20 +
21 +trait Product_Trait
22 +{
23 +
24 + use Post_Trait,
25 + Meta_Trait;
26 +
27 + /**
28 + * Returns the proper query args for the product loop.
29 + *
30 + * @param array $settings Elementor controls settings.
31 + * @param array|WP_Query $default_query The default query variables.
32 + *
33 + * @return array|stdClass Return the products data, prepared for loop.
34 + */
35 + function TRAIT_query_products($settings, $default_query)
36 + {
37 + $post_type = $settings['product-filter_post_type'];
38 +
39 + switch ($post_type) {
40 + case 'current':
41 + // Makes sure widget will render some products at editor screen
42 + if ($this->is_edit_mode()) {
43 + $query_args['post_type'] = 'product';
44 + } else {
45 + $query_args = $default_query;
46 +
47 + // Set pagination
48 + $query_args['paged'] = Query::get_queried_page($settings);
49 +
50 + // Set tax filters for filter component, if enabled
51 + $queried_filters = Query::get_queried_filters($settings, 'product', 'product-filter');
52 + $query_args['tax_query'] = empty($queried_filters['tax_query']) ? [] : $queried_filters['tax_query'];
53 + }
54 +
55 + // Set products per page
56 + $query_args['limit'] = Helper::get_framework_visible_posts('product');
57 + break;
58 +
59 + case 'related':
60 + $current_product_id = $settings['__current_post_id'] ?? [];
61 + $total_products = Query::get_posts_per_page('product', $settings);
62 + $query_args['limit'] = $total_products;
63 + $query_args['posts__in'] = Helper::get_product_related($total_products, $current_product_id);
64 + break;
65 +
66 + default:
67 + $query_args = Query::get_query_args('product-filter', $settings, true);
68 + break;
69 + }
70 +
71 + $query_args['status'] = 'publish';
72 + $query_args['total_pages'] = Query::get_total_pages($query_args);
73 +
74 + // Hide out of stock products
75 + if ($this->is_option('hide_out_of_stock', 'yes')) {
76 + $query_args['stock_status'] = 'instock';
77 + }
78 +
79 + $this->_query = $query_args;
80 +
81 + return wc_get_products($query_args);
82 + }
83 +
84 +
85 + // Render functions
86 + function get_product_image(object $product)
87 + {
88 + if ($this->is_option('image', 'yes', '!==')) {
89 + return;
90 + }
91 +
92 + // Get product image ID
93 + $pic_id = $product->get_image_id();
94 + if (!$pic_id) {
95 + return;
96 + }
97 +
98 + // Get image size
99 + $size = $this->get_settings_for_display('image_size_select') ?? 'uicore-medium';
100 +?>
101 + <a class="ui-e-post-img-wrapp"
102 + href="<?php echo esc_url($product->get_permalink()); ?>"
103 + title="<?php echo esc_attr__('View Product:', 'uicore-elements') . ' ' . esc_attr($product->get_name()); ?>">
104 +
105 + <?php if ($this->get_settings_for_display('masonry') === 'ui-e-maso') { ?>
106 + <?php
107 + // Get secondary image if available
108 + $sec_img = $this->get_product_secondary_image($product);
109 + $classes = $sec_img ? 'ui-e-post-img ui-e-hover-hide' : 'ui-e-post-img';
110 +
111 + // Print secondary image markup if exists
112 + echo $sec_img ? wp_kses_post($sec_img) : '';
113 +
114 + // Print main product image
115 + echo wp_get_attachment_image($pic_id, $size, false, ['class' => $classes]);
116 + ?>
117 + <?php } else { ?>
118 + <?php
119 + // Get secondary image with size
120 + $sec_img = $this->get_product_secondary_image($product, $size, true);
121 + echo $sec_img ? wp_kses_post($sec_img) : '';
122 + ?>
123 + <div class="ui-e-post-img <?php echo $sec_img ? 'ui-e-hover-hide' : ''; ?>"
124 + style="background-image:url(<?php echo esc_url(wp_get_attachment_image_url($pic_id, $size)); ?>)">
125 + </div>
126 + <?php } ?>
127 + </a>
128 + <?php
129 + }
130 + function get_product_secondary_image($product, $size = 'woocommerce_thumbnail', $bg_output = false)
131 + {
132 + // Requested only for products with change-image animation
133 + if ('ui-e-img-anim-change' !== $this->get_settings_for_display('anim_image')) {
134 + return false;
135 + }
136 +
137 + // Get product gallery image IDs
138 + $gallery_image_ids = $product->get_gallery_image_ids();
139 +
140 + // Check if there is at least one gallery image
141 + if (empty($gallery_image_ids)) {
142 + return false;
143 + }
144 +
145 + // Get the first image from the gallery
146 + $secondary_image_id = $gallery_image_ids[0];
147 + $secondary_image_url = wp_get_attachment_image_url($secondary_image_id, $size);
148 +
149 + // Output secondary image as <img> or background <div>
150 + if ($bg_output) {
151 + return sprintf(
152 + '<div class="ui-e-post-img ui-e-post-img-secondary" style="background-image: url(%s);"></div>',
153 + esc_url($secondary_image_url)
154 + );
155 + } else {
156 + return wp_get_attachment_image($secondary_image_id, $size, false, [
157 + 'class' => 'ui-e-post-img ui-e-post-img-secondary'
158 + ]);
159 + }
160 + }
161 + function get_product_title($product, $tag)
162 + {
163 + if ($this->is_option('title', 'yes', '!==')) {
164 + return;
165 + }
166 +
167 + ?>
168 + <a href="<?php echo esc_url($product->get_permalink()); ?>"
169 + title="<?php echo esc_attr__('View Product:', 'uicore-elements') . ' ' . esc_html($product->get_name()); ?>">
170 + <<?php echo Helper::esc_tag($tag); ?> class="ui-e-post-title">
171 + <span> <?php echo esc_html($product->get_name()); ?> </span>
172 + </<?php echo Helper::esc_tag($tag); ?>>
173 + </a>
174 + <?php
175 + }
176 +
177 + function get_product_summary($product, $length = 55)
178 + {
179 + if (! $this->get_settings_for_display('excerpt')) {
180 + return;
181 + }
182 +
183 + $summary = $product->get_short_description();
184 +
185 + // Get product description if no summary
186 + if (empty($summary)) {
187 + $summary = $product->get_description();
188 + }
189 +
190 + // returns if no description also
191 + if (empty($summary)) {
192 + return;
193 + }
194 +
195 + ?>
196 + <div class="ui-e-post-text">
197 + <?php echo wp_kses_post(wp_trim_words($summary, $length)); ?>
198 + </div>
199 + <?php
200 + }
201 +
202 + /**
203 + * Render the product purchase button. Based on `TRAIT_get_button()` method.
204 + *
205 + * @param int $index The loop index.
206 + * @param bool $is_product. If true, will render an add to cart button.
207 + *
208 + * @return void
209 + * @since 1.2.1
210 + */
211 + function get_purchase_button($index)
212 + {
213 + global $product;
214 + $settings = $this->get_settings_for_display();
215 +
216 + $can_purchase = $product->is_purchasable();
217 + $button_classes = 'elementor-button ui-e-readmore ';
218 +
219 + // Get purchase button text
220 + if ($can_purchase) {
221 +
222 + // Direct add to cart text
223 + if ($product->is_type('simple')) {
224 + $button_text = empty($settings['text'])
225 + ? $product->add_to_cart_text()
226 + : $settings['text'];
227 +
228 + // Select options text
229 + } else {
230 + $button_text = empty($settings['variations_text'])
231 + ? $product->add_to_cart_text()
232 + : $settings['variations_text'];
233 + }
234 +
235 + // Unpurchasable product text
236 + } else {
237 + $button_text = empty($settings['unavailable_text'])
238 + ? $product->add_to_cart_text()
239 + : $settings['unavailable_text'];
240 + }
241 +
242 + // Render att slugs
243 + $button_slug = 'button_' . $index;
244 + $content_slug = 'content-wrapper_' . $index;
245 + $icon_slug = 'icon_' . $index;
246 + $text_slug = 'text_' . $index;
247 +
248 + // Only simple products should be ajax added to cart for UX purposes.
249 + // Update button classes and adds all atts used by woo on product ajax add to cart.
250 + if ($can_purchase && $product->is_type('simple')) {
251 +
252 + $button_classes .= 'button product_type_simple add_to_cart_button ajax_add_to_cart';
253 +
254 + $this->add_render_attribute($button_slug, [
255 + 'data-quantity' => '1',
256 + 'aria-describedby' => 'woocommerce_loop_add_to_cart_link_describedby_' . $product->get_id(),
257 + 'data-product_id' => $product->get_id(),
258 + 'data-product_sku' => $product->get_sku(),
259 + 'aria-label' => sprintf(__('Add to cart: “%s”', 'uicore-elements'), $product->get_name()),
260 + 'data-success-message' => sprintf(__('“%s” has been added to your cart', 'uicore-elements'), $product->get_name()),
261 + ]);
262 + }
263 +
264 + // Button and wrapper classes
265 + $this->add_render_attribute($button_slug, 'class', $button_classes);
266 + $this->add_render_attribute($content_slug, 'class', 'elementor-button-content-wrapper');
267 +
268 + if (!empty($settings['hover_animation'])) {
269 + $this->add_render_attribute($button_slug, 'class', 'elementor-animation-' . $settings['hover_animation']);
270 + }
271 +
272 + $btn_classes = isset($settings['icon_align'])
273 + ? ['elementor-button-icon', 'elementor-align-icon-' . $settings['icon_align']]
274 + : 'elementor-button-icon';
275 +
276 + // Button text and icon classes
277 + $this->add_render_attribute([
278 + $icon_slug => [
279 + 'class' => $btn_classes
280 + ],
281 + $text_slug => [
282 + 'class' => 'elementor-button-text',
283 + ],
284 + ]);
285 +
286 + // Icon alignment
287 + if (isset($settings['icon_align'])) {
288 + $this->add_render_attribute($content_slug, 'class', 'elementor-button-content');
289 + }
290 +
291 + $tbn_content = '<span ' . $this->get_render_attribute_string($content_slug) . '>';
292 +
293 + if (!empty($settings['icon']) || !empty($settings['selected_icon']['value'])) {
294 + $tbn_content .= '<span ' . $this->get_render_attribute_string($icon_slug) . '>';
295 + \ob_start();
296 + Icons_Manager::render_icon($settings['selected_icon'], ['aria-hidden' => 'true']);
297 + $tbn_content .= \ob_get_clean();
298 + $tbn_content .= '</span>';
299 + }
300 +
301 + $tbn_content .= '<span ' . $this->get_render_attribute_string($text_slug) . '>';
302 + $tbn_content .= $button_text;
303 + $tbn_content .= '</span> </span>';
304 +
305 + // WooCommerce add to cart button
306 + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
307 + echo Helper::esc_svg(apply_filters(
308 + 'woocommerce_loop_add_to_cart_link', // WPCS: XSS ok.
309 + sprintf(
310 + '<a href="%1$s" %2$s> %3$s </a>',
311 + esc_url($product->add_to_cart_url()),
312 + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
313 + $this->get_render_attribute_string($button_slug),
314 + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
315 + Helper::esc_svg($tbn_content)
316 + ),
317 + //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
318 + $product
319 + ));
320 + }
321 +
322 + /**
323 + * Renders a product item with various settings and options.
324 + * Important: any changes here should also be considered to `TRAIT_render_item()` from Post Component.
325 + *
326 + * @param WC_Product $product The product object.
327 + * @param int $index The loop index.
328 + * @param bool $carousel Indicates if the item needs carousel classnames.
329 + * @param bool $legacy Indicates if the item is using legacy classnames.
330 + * @param bool $is_ajax Indicates if the item is being rendered through REST API.
331 + *
332 + * @return void
333 + */
334 + function TRAIT_render_product($product, $index, $carousel = false, $legacy = false, $is_ajax = false)
335 + {
336 + $settings = $this->get_settings_for_display();
337 +
338 + $product_id = is_object($product)
339 + ? $product->get_id()
340 + : null;
341 +
342 + // Classnames but checking if we the widget is APG (legacy version)
343 + $item_classes = $legacy ? ['ui-e-post-item', 'ui-e-item'] : ['ui-e-item']; // Single item lower wrap class receptor
344 + $hover_item_class = $legacy ? 'anim_item' : 'item_hover_animation'; // item hover animation class
345 +
346 + // Build content atts
347 + $content_tag = 'div';
348 + $this->add_render_attribute('content_' . $index, 'class', ['ui-e-post-content']);
349 +
350 + // Global link
351 + if ($this->is_option('global_link', 'yes')) {
352 + $content_tag = 'a';
353 + $this->add_render_attribute('content_' . $index, 'href', esc_url(get_permalink()));
354 + }
355 +
356 + // If widget is not carousel type, we set animations classes directly on item selector
357 + if (!$carousel) {
358 + $item_classes[] = 'ui-e-animations-wrp';
359 + $item_classes[] = $settings['animate_items'] === 'ui-e-grid-animate' ? 'elementor-invisible' : '';
360 + $item_classes[] = $settings[$hover_item_class] !== '' ? $settings[$hover_item_class] : '';
361 + } else {
362 + // Get entrance and item hover animation classes
363 + $entrance = $this->is_option('animate_items', 'ui-e-grid-animate') ? 'elementor-invisible' : '';
364 + $hover = isset($settings[$hover_item_class]) ? $settings[$hover_item_class] : '';
365 + $animations = sprintf('%s %s', $entrance, $hover);
366 +
367 + // Check if entrance or hover animation are set
368 + $has_animation = !empty($entrance) || !empty($hover);
369 +
370 + // Prints extra wrappers required by the carousel script
371 + ?>
372 + <div class="ui-e-wrp swiper-slide">
373 + <?php if ($has_animation) : ?>
374 + <div class="ui-e-animations-wrp <?php echo esc_attr($animations); ?>">
375 + <?php endif; ?>
376 +
377 + <?php } ?>
378 +
379 + <div class="<?php echo esc_attr(implode(' ', $item_classes)); ?>">
380 + <article <?php post_class('product'); ?>>
381 + <div class="ui-e-post-top">
382 + <?php $this->get_product_image($product); ?>
383 + <?php $this->get_post_meta('top', $product_id); ?>
384 + <?php
385 + if ($settings['button_position'] === 'ui-e-button-placement-image') {
386 + $this->get_purchase_button($index);
387 + }
388 + ?>
389 + </div>
390 + <<?php echo Helper::esc_tag($content_tag, 'div', true); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
391 + $this->print_render_attribute_string('content_' . $index); ?>>
392 +
393 + <?php $this->get_post_meta('before_title', $product_id); ?>
394 + <?php $this->get_product_title($product, $settings['title_tag']); ?>
395 + <?php $this->get_post_meta('after_title', $product_id); ?>
396 + <?php $this->get_product_summary($product, $settings['excerpt_trim']); ?>
397 + <?php $this->get_post_meta('bottom', $product_id); // button
398 + ?>
399 + <?php
400 + if (defined('UICORE_VERSION') && version_compare(UICORE_VERSION, '6.0.1', '>=') && $this->is_option('show_swatches', 'yes')) {
401 + // ajax requests works under minimal conditions, wich means Swatches class is not present
402 + if ($is_ajax) {
403 + require_once UICORE_INCLUDES . '/woocommerce/components/class-swatches.php';
404 + }
405 +
406 + // In guterberg we might experience an `Class not found` error, so we insist in checking it even
407 + // though we just required the file. For more see https://uicore.atlassian.net/browse/ELM-432
408 + if (class_exists('\UiCore\WooCommerce\Swatches')) {
409 + \UiCore\WooCommerce\Swatches::print_swatches($product);
410 + }
411 + }
412 + if ($this->is_option('show_button', 'yes')) {
413 + // Add match height spacing element if carousel. May look intrusive, but is the simplest method compared
414 + // to absolute positioning or catching last content element to apply margin.
415 + if ($carousel && $this->is_option('match_height', 'yes')) {
416 + ?>
417 + <span class="ui-e-match-height"></span>
418 + <?php
419 + }
420 +
421 + if (!isset($settings['button_position']) || empty($settings['button_position'])) {
422 + $this->get_purchase_button($index);
423 + }
424 + }
425 + ?>
426 + </<?php echo Helper::esc_tag($content_tag, 'div'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
427 + ?>>
428 + </article>
429 + </div>
430 +
431 + <?php if ($carousel) { ?>
432 + </div>
433 + <?php if ($has_animation) : ?>
434 + </div>
435 + <?php endif; ?>
436 +
437 +<?php }
438 + }
439 +
440 + // keep it to avoid fatal error
441 + public function content_template() {}
442 + }