PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.65
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.65
51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / widgets / Compare_Table / Compare_Table.php

Compare_Table.php in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.65, at includes/widgets/Compare_Table/Compare_Table.php

1,152 lines 36.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Compare Products Table Widget (Free).
4 *
5 * @package King_Addons
6 */
7
8 namespace King_Addons;
9
10 use Elementor\Controls_Manager;
11 use Elementor\Group_Control_Border;
12 use Elementor\Group_Control_Box_Shadow;
13 use Elementor\Group_Control_Typography;
14 use Elementor\Widget_Base;
15 use WC_Product;
16 use WP_Query;
17
18 if (!defined('ABSPATH')) {
19 exit;
20 }
21
22 /**
23 * Renders a WooCommerce products compare table.
24 */
25 class Compare_Table extends Widget_Base
26 {
27 /**
28 * Widget slug.
29 *
30 * @return string
31 */
32 public function get_name(): string
33 {
34 return 'king-addons-compare-table';
35 }
36
37 /**
38 * Widget title.
39 *
40 * @return string
41 */
42 public function get_title(): string
43 {
44 return esc_html__('Compare Products Table', 'king-addons');
45 }
46
47 /**
48 * Widget icon.
49 *
50 * @return string
51 */
52 public function get_icon(): string
53 {
54 return 'king-addons-icon king-addons-compare-table';
55 }
56
57 /**
58 * Style dependencies.
59 *
60 * @return array<int, string>
61 */
62 public function get_style_depends(): array
63 {
64 return [
65 KING_ADDONS_ASSETS_UNIQUE_KEY . '-compare-table-style',
66 ];
67 }
68
69 /**
70 * Script dependencies.
71 *
72 * @return array<int, string>
73 */
74 public function get_script_depends(): array
75 {
76 $deps = [
77 KING_ADDONS_ASSETS_UNIQUE_KEY . '-compare-table-script',
78 ];
79
80 if (class_exists('\WooCommerce') && function_exists('wp_script_is') && wp_script_is('wc-add-to-cart', 'registered')) {
81 $deps[] = 'wc-add-to-cart';
82 }
83
84 return $deps;
85 }
86
87 /**
88 * Categories.
89 *
90 * @return array<int, string>
91 */
92 public function get_categories(): array
93 {
94 return ['king-addons'];
95 }
96
97 /**
98 * Keywords.
99 *
100 * @return array<int, string>
101 */
102 public function get_keywords(): array
103 {
104 return ['woocommerce', 'compare', 'table', 'product'];
105 }
106
107 public function get_custom_help_url()
108 {
109 return 'mailto:bug@kingaddons.com?subject=Bug Report - King Addons&body=Please describe the issue';
110 }
111
112 /**
113 * Register controls.
114 *
115 * @return void
116 */
117 public function register_controls(): void
118 {
119 $this->register_query_controls();
120 $this->register_display_controls();
121 $this->register_layout_controls();
122 $this->register_style_header_controls();
123 $this->register_style_cell_controls();
124 $this->register_style_button_controls();
125 $this->register_style_view_cart_controls();
126 $this->register_pro_controls();
127 $this->register_pro_notice_controls();
128 }
129
130 /**
131 * Register Pro-only controls placeholder.
132 *
133 * The Pro version overrides this method to add premium controls without
134 * overriding the full `register_controls()` flow.
135 *
136 * @return void
137 */
138 public function register_pro_controls(): void
139 {
140 // Intentionally empty in Free.
141 }
142
143 /**
144 * Render widget output.
145 *
146 * @return void
147 */
148 public function render(): void
149 {
150 if (!class_exists('\WooCommerce')) {
151 return;
152 }
153
154 $settings = $this->get_settings_for_display();
155 $products = $this->get_products($settings);
156
157 if (empty($products)) {
158 return;
159 }
160
161 $rows = $this->get_rows_config($settings);
162 $wrapper_attrs = $this->get_wrapper_attributes($settings);
163
164 ?>
165 <div class="king-addons-compare-table" <?php echo $wrapper_attrs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
166 <div class="king-addons-compare-table__scroll">
167 <table class="king-addons-compare-table__table">
168 <thead class="king-addons-compare-table__head">
169 <tr class="king-addons-compare-table__row king-addons-compare-table__row--head">
170 <th class="king-addons-compare-table__cell king-addons-compare-table__cell--label">
171 <?php echo esc_html__('Feature', 'king-addons'); ?>
172 </th>
173 <?php foreach ($products as $product) : ?>
174 <th class="king-addons-compare-table__cell king-addons-compare-table__cell--product">
175 <div class="king-addons-compare-table__product-head">
176 <div class="king-addons-compare-table__product-thumb">
177 <a href="<?php echo esc_url(get_permalink($product->get_id())); ?>">
178 <?php echo $product->get_image('thumbnail'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
179 </a>
180 </div>
181 <div class="king-addons-compare-table__product-title">
182 <a href="<?php echo esc_url(get_permalink($product->get_id())); ?>">
183 <?php echo esc_html($product->get_name()); ?>
184 </a>
185 </div>
186 </div>
187 </th>
188 <?php endforeach; ?>
189 </tr>
190 </thead>
191 <tbody class="king-addons-compare-table__body">
192 <?php foreach ($rows as $row_id => $row_label) : ?>
193 <tr class="king-addons-compare-table__row" data-row-id="<?php echo esc_attr($row_id); ?>">
194 <th class="king-addons-compare-table__cell king-addons-compare-table__cell--label">
195 <?php echo esc_html($row_label); ?>
196 </th>
197 <?php foreach ($products as $product) : ?>
198 <td class="king-addons-compare-table__cell king-addons-compare-table__cell--value">
199 <?php echo $this->get_cell_content($row_id, $product, $settings); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
200 </td>
201 <?php endforeach; ?>
202 </tr>
203 <?php endforeach; ?>
204 </tbody>
205 </table>
206 </div>
207 </div>
208 <?php
209 }
210
211 /**
212 * Query controls.
213 *
214 * @return void
215 */
216 protected function register_query_controls(): void
217 {
218 $this->start_controls_section(
219 'kng_query_section',
220 [
221 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Products', 'king-addons'),
222 'tab' => Controls_Manager::TAB_CONTENT,
223 ]
224 );
225
226 $this->add_control(
227 'kng_product_ids',
228 [
229 'label' => esc_html__('Product IDs', 'king-addons'),
230 'type' => Controls_Manager::TEXT,
231 'placeholder' => esc_html__('12, 34, 56', 'king-addons'),
232 'description' => esc_html__('Comma-separated product IDs to compare.', 'king-addons'),
233 ]
234 );
235
236 $this->add_control(
237 'kng_products_limit',
238 [
239 'label' => esc_html__('Max Products', 'king-addons'),
240 'type' => Controls_Manager::NUMBER,
241 'min' => 2,
242 'max' => 4,
243 'step' => 1,
244 'default' => 3,
245 'description' => esc_html__('Free version limited to 4 products.', 'king-addons'),
246 ]
247 );
248
249 $this->add_control(
250 'kng_categories',
251 [
252 'label' => sprintf(__('Categories %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
253 'type' => Controls_Manager::TEXT,
254 'description' => esc_html__('Comma-separated slugs (Pro).', 'king-addons'),
255 'classes' => 'king-addons-pro-control no-distance',
256 ]
257 );
258
259 $this->end_controls_section();
260 }
261
262 /**
263 * Display toggles.
264 *
265 * @return void
266 */
267 protected function register_display_controls(): void
268 {
269 $this->start_controls_section(
270 'kng_display_section',
271 [
272 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Display', 'king-addons'),
273 'tab' => Controls_Manager::TAB_CONTENT,
274 ]
275 );
276
277 $this->add_control(
278 'kng_show_image',
279 [
280 'label' => esc_html__('Show Image', 'king-addons'),
281 'type' => Controls_Manager::SWITCHER,
282 'return_value' => 'yes',
283 'default' => 'yes',
284 ]
285 );
286
287 $this->add_control(
288 'kng_show_price',
289 [
290 'label' => esc_html__('Show Price', 'king-addons'),
291 'type' => Controls_Manager::SWITCHER,
292 'return_value' => 'yes',
293 'default' => 'yes',
294 ]
295 );
296
297 $this->add_control(
298 'kng_show_rating',
299 [
300 'label' => esc_html__('Show Rating', 'king-addons'),
301 'type' => Controls_Manager::SWITCHER,
302 'return_value' => 'yes',
303 'default' => 'yes',
304 ]
305 );
306
307 $this->add_control(
308 'kng_show_excerpt',
309 [
310 'label' => esc_html__('Show Short Description', 'king-addons'),
311 'type' => Controls_Manager::SWITCHER,
312 'return_value' => 'yes',
313 'default' => 'yes',
314 ]
315 );
316
317 $this->add_control(
318 'kng_show_sku',
319 [
320 'label' => esc_html__('Show SKU', 'king-addons'),
321 'type' => Controls_Manager::SWITCHER,
322 'return_value' => 'yes',
323 'default' => 'yes',
324 ]
325 );
326
327 $this->add_control(
328 'kng_show_stock',
329 [
330 'label' => esc_html__('Show Stock', 'king-addons'),
331 'type' => Controls_Manager::SWITCHER,
332 'return_value' => 'yes',
333 'default' => 'yes',
334 ]
335 );
336
337 $this->add_control(
338 'kng_show_add_to_cart',
339 [
340 'label' => esc_html__('Show Add to Cart', 'king-addons'),
341 'type' => Controls_Manager::SWITCHER,
342 'return_value' => 'yes',
343 'default' => 'yes',
344 ]
345 );
346
347 $this->add_control(
348 'kng_hide_equal_rows',
349 [
350 'label' => sprintf(__('Hide Identical Rows %s', 'king-addons'), '<i class="eicon-pro-icon"></i>'),
351 'type' => Controls_Manager::SWITCHER,
352 'return_value' => 'yes',
353 'default' => '',
354 'classes' => 'king-addons-pro-control no-distance',
355 ]
356 );
357
358 $this->end_controls_section();
359 }
360
361 /**
362 * Layout controls.
363 *
364 * @return void
365 */
366 protected function register_layout_controls(): void
367 {
368 $this->start_controls_section(
369 'kng_layout_section',
370 [
371 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Layout', 'king-addons'),
372 'tab' => Controls_Manager::TAB_CONTENT,
373 ]
374 );
375
376 $this->add_control(
377 'kng_cell_alignment',
378 [
379 'label' => esc_html__('Cell Alignment', 'king-addons'),
380 'type' => Controls_Manager::CHOOSE,
381 'options' => [
382 'left' => [
383 'title' => esc_html__('Left', 'king-addons'),
384 'icon' => 'eicon-text-align-left',
385 ],
386 'center' => [
387 'title' => esc_html__('Center', 'king-addons'),
388 'icon' => 'eicon-text-align-center',
389 ],
390 'right' => [
391 'title' => esc_html__('Right', 'king-addons'),
392 'icon' => 'eicon-text-align-right',
393 ],
394 ],
395 'default' => 'left',
396 'selectors' => [
397 '{{WRAPPER}} .king-addons-compare-table__cell' => 'text-align: {{VALUE}};',
398 ],
399 ]
400 );
401
402 $this->add_responsive_control(
403 'kng_cell_padding',
404 [
405 'label' => esc_html__('Cell Padding', 'king-addons'),
406 'type' => Controls_Manager::DIMENSIONS,
407 'size_units' => ['px', 'em'],
408 'selectors' => [
409 '{{WRAPPER}} .king-addons-compare-table__cell' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
410 ],
411 ]
412 );
413
414 $this->end_controls_section();
415 }
416
417 /**
418 * Header styles.
419 *
420 * @return void
421 */
422 protected function register_style_header_controls(): void
423 {
424 $this->start_controls_section(
425 'kng_style_header_section',
426 [
427 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Header', 'king-addons'),
428 'tab' => Controls_Manager::TAB_STYLE,
429 ]
430 );
431
432 $this->add_group_control(
433 Group_Control_Typography::get_type(),
434 [
435 'name' => 'kng_header_typography',
436 'selector' => '{{WRAPPER}} .king-addons-compare-table__head .king-addons-compare-table__cell',
437 ]
438 );
439
440 $this->add_control(
441 'kng_header_color',
442 [
443 'label' => esc_html__('Text Color', 'king-addons'),
444 'type' => Controls_Manager::COLOR,
445 'selectors' => [
446 '{{WRAPPER}} .king-addons-compare-table__head .king-addons-compare-table__cell' => 'color: {{VALUE}};',
447 ],
448 ]
449 );
450
451 $this->add_control(
452 'kng_header_bg',
453 [
454 'label' => esc_html__('Background', 'king-addons'),
455 'type' => Controls_Manager::COLOR,
456 'selectors' => [
457 '{{WRAPPER}} .king-addons-compare-table__head .king-addons-compare-table__row' => 'background-color: {{VALUE}};',
458 ],
459 ]
460 );
461
462 $this->add_group_control(
463 Group_Control_Border::get_type(),
464 [
465 'name' => 'kng_header_border',
466 'selector' => '{{WRAPPER}} .king-addons-compare-table__head .king-addons-compare-table__cell',
467 ]
468 );
469
470 $this->add_control(
471 'kng_header_radius',
472 [
473 'label' => esc_html__('Border Radius', 'king-addons'),
474 'type' => Controls_Manager::SLIDER,
475 'size_units' => ['px', '%'],
476 'range' => [
477 'px' => ['min' => 0, 'max' => 40],
478 '%' => ['min' => 0, 'max' => 100],
479 ],
480 'selectors' => [
481 '{{WRAPPER}} .king-addons-compare-table__row--head .king-addons-compare-table__cell' => 'border-radius: {{SIZE}}{{UNIT}};',
482 ],
483 ]
484 );
485
486 $this->add_group_control(
487 Group_Control_Box_Shadow::get_type(),
488 [
489 'name' => 'kng_header_shadow',
490 'selector' => '{{WRAPPER}} .king-addons-compare-table__head',
491 ]
492 );
493
494 $this->end_controls_section();
495 }
496
497 /**
498 * Cell styles.
499 *
500 * @return void
501 */
502 protected function register_style_cell_controls(): void
503 {
504 $this->start_controls_section(
505 'kng_style_cell_section',
506 [
507 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Body', 'king-addons'),
508 'tab' => Controls_Manager::TAB_STYLE,
509 ]
510 );
511
512 $this->add_group_control(
513 Group_Control_Typography::get_type(),
514 [
515 'name' => 'kng_body_typography',
516 'selector' => '{{WRAPPER}} .king-addons-compare-table__body .king-addons-compare-table__cell',
517 ]
518 );
519
520 $this->add_control(
521 'kng_body_color',
522 [
523 'label' => esc_html__('Text Color', 'king-addons'),
524 'type' => Controls_Manager::COLOR,
525 'selectors' => [
526 '{{WRAPPER}} .king-addons-compare-table__body .king-addons-compare-table__cell' => 'color: {{VALUE}};',
527 ],
528 ]
529 );
530
531 $this->add_control(
532 'kng_body_bg',
533 [
534 'label' => esc_html__('Background', 'king-addons'),
535 'type' => Controls_Manager::COLOR,
536 'selectors' => [
537 '{{WRAPPER}} .king-addons-compare-table__body .king-addons-compare-table__row' => 'background-color: {{VALUE}};',
538 ],
539 ]
540 );
541
542 $this->add_control(
543 'kng_body_stripe_bg',
544 [
545 'label' => esc_html__('Stripe Background', 'king-addons'),
546 'type' => Controls_Manager::COLOR,
547 'selectors' => [
548 '{{WRAPPER}} .king-addons-compare-table__row:nth-child(2n) .king-addons-compare-table__cell' => 'background-color: {{VALUE}};',
549 ],
550 ]
551 );
552
553 $this->add_group_control(
554 Group_Control_Border::get_type(),
555 [
556 'name' => 'kng_body_border',
557 'selector' => '{{WRAPPER}} .king-addons-compare-table__cell',
558 ]
559 );
560
561 $this->add_control(
562 'kng_body_radius',
563 [
564 'label' => esc_html__('Cell Border Radius', 'king-addons'),
565 'type' => Controls_Manager::SLIDER,
566 'size_units' => ['px', '%'],
567 'range' => [
568 'px' => ['min' => 0, 'max' => 30],
569 '%' => ['min' => 0, 'max' => 100],
570 ],
571 'selectors' => [
572 '{{WRAPPER}} .king-addons-compare-table__cell' => 'border-radius: {{SIZE}}{{UNIT}};',
573 ],
574 ]
575 );
576
577 $this->end_controls_section();
578 }
579
580 /**
581 * Button styles.
582 *
583 * @return void
584 */
585 protected function register_style_button_controls(): void
586 {
587 $this->start_controls_section(
588 'kng_style_button_section',
589 [
590 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Add to Cart', 'king-addons'),
591 'tab' => Controls_Manager::TAB_STYLE,
592 ]
593 );
594
595 $this->add_group_control(
596 Group_Control_Typography::get_type(),
597 [
598 'name' => 'kng_button_typography',
599 'selector' => '{{WRAPPER}} .king-addons-compare-table__cell .button',
600 ]
601 );
602
603 $this->add_responsive_control(
604 'kng_button_padding',
605 [
606 'label' => esc_html__('Padding', 'king-addons'),
607 'type' => Controls_Manager::DIMENSIONS,
608 'size_units' => ['px', 'em'],
609 'selectors' => [
610 '{{WRAPPER}} .king-addons-compare-table__cell .button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
611 ],
612 ]
613 );
614
615 $this->add_responsive_control(
616 'kng_button_indicator_size',
617 [
618 'label' => esc_html__('Indicator Size', 'king-addons'),
619 'type' => Controls_Manager::SLIDER,
620 'size_units' => ['px'],
621 'range' => [
622 'px' => ['min' => 8, 'max' => 40],
623 ],
624 'selectors' => [
625 '{{WRAPPER}} .king-addons-compare-table__cell .king-addons-compare-table__add-to-cart' => '--king-addons-atc-indicator-size: {{SIZE}}{{UNIT}};',
626 ],
627 ]
628 );
629
630 $this->start_controls_tabs('kng_button_style_tabs');
631
632 $this->start_controls_tab(
633 'kng_button_style_tab_normal',
634 [
635 'label' => esc_html__('Normal', 'king-addons'),
636 ]
637 );
638
639 $this->add_control(
640 'kng_button_color',
641 [
642 'label' => esc_html__('Text Color', 'king-addons'),
643 'type' => Controls_Manager::COLOR,
644 'selectors' => [
645 '{{WRAPPER}} .king-addons-compare-table__cell .button' => 'color: {{VALUE}};',
646 ],
647 ]
648 );
649
650 $this->add_control(
651 'kng_button_bg',
652 [
653 'label' => esc_html__('Background', 'king-addons'),
654 'type' => Controls_Manager::COLOR,
655 'selectors' => [
656 '{{WRAPPER}} .king-addons-compare-table__cell .button' => 'background-color: {{VALUE}};',
657 ],
658 ]
659 );
660
661 $this->add_group_control(
662 Group_Control_Border::get_type(),
663 [
664 'name' => 'kng_button_border',
665 'selector' => '{{WRAPPER}} .king-addons-compare-table__cell .button',
666 ]
667 );
668
669 $this->add_group_control(
670 Group_Control_Box_Shadow::get_type(),
671 [
672 'name' => 'kng_button_shadow',
673 'selector' => '{{WRAPPER}} .king-addons-compare-table__cell .button',
674 ]
675 );
676
677 $this->end_controls_tab();
678
679 $this->start_controls_tab(
680 'kng_button_style_tab_hover',
681 [
682 'label' => esc_html__('Hover', 'king-addons'),
683 ]
684 );
685
686 $this->add_control(
687 'kng_button_color_hover',
688 [
689 'label' => esc_html__('Hover Text Color', 'king-addons'),
690 'type' => Controls_Manager::COLOR,
691 'selectors' => [
692 '{{WRAPPER}} .king-addons-compare-table__cell .button:hover' => 'color: {{VALUE}};',
693 ],
694 ]
695 );
696
697 $this->add_control(
698 'kng_button_bg_hover',
699 [
700 'label' => esc_html__('Hover Background', 'king-addons'),
701 'type' => Controls_Manager::COLOR,
702 'selectors' => [
703 '{{WRAPPER}} .king-addons-compare-table__cell .button:hover' => 'background-color: {{VALUE}};',
704 ],
705 ]
706 );
707
708 $this->add_group_control(
709 Group_Control_Border::get_type(),
710 [
711 'name' => 'kng_button_border_hover',
712 'selector' => '{{WRAPPER}} .king-addons-compare-table__cell .button:hover',
713 ]
714 );
715
716 $this->add_group_control(
717 Group_Control_Box_Shadow::get_type(),
718 [
719 'name' => 'kng_button_shadow_hover',
720 'selector' => '{{WRAPPER}} .king-addons-compare-table__cell .button:hover',
721 ]
722 );
723
724 $this->end_controls_tab();
725
726 $this->end_controls_tabs();
727
728 $this->add_control(
729 'kng_button_radius',
730 [
731 'label' => esc_html__('Border Radius', 'king-addons'),
732 'type' => Controls_Manager::SLIDER,
733 'size_units' => ['px', '%'],
734 'range' => [
735 'px' => ['min' => 0, 'max' => 40],
736 '%' => ['min' => 0, 'max' => 100],
737 ],
738 'selectors' => [
739 '{{WRAPPER}} .king-addons-compare-table__cell .button' => 'border-radius: {{SIZE}}{{UNIT}};',
740 ],
741 ]
742 );
743
744 $this->end_controls_section();
745 }
746
747 /**
748 * View cart link styles (WooCommerce injects `.added_to_cart` link after AJAX add to cart).
749 *
750 * @return void
751 */
752 protected function register_style_view_cart_controls(): void
753 {
754 $this->start_controls_section(
755 'kng_style_view_cart_section',
756 [
757 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('View Cart Link', 'king-addons'),
758 'tab' => Controls_Manager::TAB_STYLE,
759 ]
760 );
761
762 $this->add_responsive_control(
763 'kng_view_cart_gap',
764 [
765 'label' => esc_html__('Spacing From Button', 'king-addons'),
766 'type' => Controls_Manager::SLIDER,
767 'size_units' => ['px'],
768 'range' => [
769 'px' => ['min' => 0, 'max' => 60],
770 ],
771 'selectors' => [
772 '{{WRAPPER}} .king-addons-compare-table__cell .king-addons-compare-table__add-to-cart + .added_to_cart' => 'margin-left: {{SIZE}}{{UNIT}};',
773 ],
774 ]
775 );
776
777 $this->add_group_control(
778 Group_Control_Typography::get_type(),
779 [
780 'name' => 'kng_view_cart_typography',
781 'selector' => '{{WRAPPER}} .king-addons-compare-table__cell .added_to_cart',
782 ]
783 );
784
785 $this->add_responsive_control(
786 'kng_view_cart_padding',
787 [
788 'label' => esc_html__('Padding', 'king-addons'),
789 'type' => Controls_Manager::DIMENSIONS,
790 'size_units' => ['px', 'em'],
791 'selectors' => [
792 '{{WRAPPER}} .king-addons-compare-table__cell .added_to_cart' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
793 ],
794 ]
795 );
796
797 $this->start_controls_tabs('kng_view_cart_style_tabs');
798
799 $this->start_controls_tab(
800 'kng_view_cart_style_tab_normal',
801 [
802 'label' => esc_html__('Normal', 'king-addons'),
803 ]
804 );
805
806 $this->add_control(
807 'kng_view_cart_color',
808 [
809 'label' => esc_html__('Text Color', 'king-addons'),
810 'type' => Controls_Manager::COLOR,
811 'selectors' => [
812 '{{WRAPPER}} .king-addons-compare-table__cell .added_to_cart' => 'color: {{VALUE}};',
813 ],
814 ]
815 );
816
817 $this->add_control(
818 'kng_view_cart_bg',
819 [
820 'label' => esc_html__('Background', 'king-addons'),
821 'type' => Controls_Manager::COLOR,
822 'selectors' => [
823 '{{WRAPPER}} .king-addons-compare-table__cell .added_to_cart' => 'background-color: {{VALUE}};',
824 ],
825 ]
826 );
827
828 $this->add_group_control(
829 Group_Control_Border::get_type(),
830 [
831 'name' => 'kng_view_cart_border',
832 'selector' => '{{WRAPPER}} .king-addons-compare-table__cell .added_to_cart',
833 ]
834 );
835
836 $this->add_group_control(
837 Group_Control_Box_Shadow::get_type(),
838 [
839 'name' => 'kng_view_cart_shadow',
840 'selector' => '{{WRAPPER}} .king-addons-compare-table__cell .added_to_cart',
841 ]
842 );
843
844 $this->end_controls_tab();
845
846 $this->start_controls_tab(
847 'kng_view_cart_style_tab_hover',
848 [
849 'label' => esc_html__('Hover', 'king-addons'),
850 ]
851 );
852
853 $this->add_control(
854 'kng_view_cart_color_hover',
855 [
856 'label' => esc_html__('Hover Text Color', 'king-addons'),
857 'type' => Controls_Manager::COLOR,
858 'selectors' => [
859 '{{WRAPPER}} .king-addons-compare-table__cell .added_to_cart:hover' => 'color: {{VALUE}};',
860 ],
861 ]
862 );
863
864 $this->add_control(
865 'kng_view_cart_bg_hover',
866 [
867 'label' => esc_html__('Hover Background', 'king-addons'),
868 'type' => Controls_Manager::COLOR,
869 'selectors' => [
870 '{{WRAPPER}} .king-addons-compare-table__cell .added_to_cart:hover' => 'background-color: {{VALUE}};',
871 ],
872 ]
873 );
874
875 $this->add_group_control(
876 Group_Control_Border::get_type(),
877 [
878 'name' => 'kng_view_cart_border_hover',
879 'selector' => '{{WRAPPER}} .king-addons-compare-table__cell .added_to_cart:hover',
880 ]
881 );
882
883 $this->add_group_control(
884 Group_Control_Box_Shadow::get_type(),
885 [
886 'name' => 'kng_view_cart_shadow_hover',
887 'selector' => '{{WRAPPER}} .king-addons-compare-table__cell .added_to_cart:hover',
888 ]
889 );
890
891 $this->end_controls_tab();
892
893 $this->end_controls_tabs();
894
895 $this->add_control(
896 'kng_view_cart_radius',
897 [
898 'label' => esc_html__('Border Radius', 'king-addons'),
899 'type' => Controls_Manager::SLIDER,
900 'size_units' => ['px', '%'],
901 'range' => [
902 'px' => ['min' => 0, 'max' => 40],
903 '%' => ['min' => 0, 'max' => 100],
904 ],
905 'selectors' => [
906 '{{WRAPPER}} .king-addons-compare-table__cell .added_to_cart' => 'border-radius: {{SIZE}}{{UNIT}};',
907 ],
908 ]
909 );
910
911 $this->end_controls_section();
912 }
913
914 /**
915 * Pro notice section.
916 *
917 * @return void
918 */
919 public function register_pro_notice_controls(): void
920 {
921 if (!king_addons_can_use_pro()) {
922 Core::renderProFeaturesSection($this, '', Controls_Manager::RAW_HTML, 'compare-table', [
923 'Unlimited products and category filters',
924 'Hide identical rows and sticky header',
925 'Extra attributes (dimensions, weight, taxonomy)',
926 'Highlight differences and advanced styling',
927 ]);
928 }
929 }
930
931 /**
932 * Collect products based on settings.
933 *
934 * @param array<string, mixed> $settings Settings.
935 *
936 * @return array<int, WC_Product>
937 */
938 protected function get_products(array $settings): array
939 {
940 $limit = isset($settings['kng_products_limit']) ? (int) $settings['kng_products_limit'] : 3;
941 $limit = min(max($limit, 2), 4);
942
943 $ids_raw = $settings['kng_product_ids'] ?? '';
944 $ids = array_filter(array_map('absint', explode(',', (string) $ids_raw)));
945 $ids = array_slice($ids, 0, $limit);
946
947 if (empty($ids)) {
948 return [];
949 }
950
951 $query = new WP_Query(
952 [
953 'post_type' => 'product',
954 'post__in' => $ids,
955 'posts_per_page' => $limit,
956 'orderby' => 'post__in',
957 'post_status' => 'publish',
958 ]
959 );
960
961 $products = [];
962
963 while ($query->have_posts()) {
964 $query->the_post();
965 $product = wc_get_product(get_the_ID());
966 if ($product) {
967 $products[] = $product;
968 }
969 }
970
971 wp_reset_postdata();
972
973 return $products;
974 }
975
976 /**
977 * Row labels map.
978 *
979 * @param array<string, mixed> $settings Settings.
980 *
981 * @return array<string, string>
982 */
983 protected function get_rows_config(array $settings): array
984 {
985 return $this->get_rows_config_base($settings);
986 }
987
988 /**
989 * Base row labels map.
990 *
991 * This method exists to allow the Pro version to extend row configuration
992 * without calling `parent::` methods.
993 *
994 * @param array<string, mixed> $settings Settings.
995 *
996 * @return array<string, string>
997 */
998 protected function get_rows_config_base(array $settings): array
999 {
1000 $rows = [];
1001
1002 if (($settings['kng_show_image'] ?? 'yes') === 'yes') {
1003 $rows['image'] = esc_html__('Image', 'king-addons');
1004 }
1005
1006 if (($settings['kng_show_price'] ?? 'yes') === 'yes') {
1007 $rows['price'] = esc_html__('Price', 'king-addons');
1008 }
1009
1010 if (($settings['kng_show_rating'] ?? 'yes') === 'yes') {
1011 $rows['rating'] = esc_html__('Rating', 'king-addons');
1012 }
1013
1014 if (($settings['kng_show_sku'] ?? 'yes') === 'yes') {
1015 $rows['sku'] = esc_html__('SKU', 'king-addons');
1016 }
1017
1018 if (($settings['kng_show_stock'] ?? 'yes') === 'yes') {
1019 $rows['stock'] = esc_html__('Stock', 'king-addons');
1020 }
1021
1022 if (($settings['kng_show_excerpt'] ?? 'yes') === 'yes') {
1023 $rows['excerpt'] = esc_html__('Short Description', 'king-addons');
1024 }
1025
1026 if (($settings['kng_show_add_to_cart'] ?? 'yes') === 'yes') {
1027 $rows['add_to_cart'] = esc_html__('Add to Cart', 'king-addons');
1028 }
1029
1030 return $rows;
1031 }
1032
1033 /**
1034 * Build cell content by row.
1035 *
1036 * @param string $row_id Row id.
1037 * @param WC_Product $product Product.
1038 * @param array<string, mixed> $settings Settings.
1039 *
1040 * @return string
1041 */
1042 protected function get_cell_content(string $row_id, WC_Product $product, array $settings): string
1043 {
1044 return $this->get_cell_content_base($row_id, $product, $settings);
1045 }
1046
1047 /**
1048 * Base cell content renderer.
1049 *
1050 * This method exists to allow the Pro version to extend row rendering
1051 * without calling `parent::` methods.
1052 *
1053 * @param string $row_id Row id.
1054 * @param WC_Product $product Product.
1055 * @param array<string, mixed> $settings Settings.
1056 *
1057 * @return string
1058 */
1059 protected function get_cell_content_base(string $row_id, WC_Product $product, array $settings): string
1060 {
1061 switch ($row_id) {
1062 case 'image':
1063 return $product->get_image('woocommerce_thumbnail');
1064 case 'price':
1065 return wp_kses_post($product->get_price_html());
1066 case 'rating':
1067 if (wc_review_ratings_enabled()) {
1068 return (string) wc_get_rating_html($product->get_average_rating());
1069 }
1070 return '';
1071 case 'sku':
1072 return $product->get_sku() ? esc_html($product->get_sku()) : esc_html__('N/A', 'king-addons');
1073 case 'stock':
1074 return $product->is_in_stock()
1075 ? '<span class="king-addons-compare-table__stock is-in">' . esc_html__('In stock', 'king-addons') . '</span>'
1076 : '<span class="king-addons-compare-table__stock is-out">' . esc_html__('Out of stock', 'king-addons') . '</span>';
1077 case 'excerpt':
1078 return wp_kses_post(wp_trim_words($product->get_short_description(), 20));
1079 case 'add_to_cart':
1080 return $this->render_add_to_cart_button($product);
1081 default:
1082 return '';
1083 }
1084 }
1085
1086 /**
1087 * Render a controlled Add to Cart button without Woo shortcode.
1088 *
1089 * @param WC_Product $product Product instance.
1090 *
1091 * @return string
1092 */
1093 protected function render_add_to_cart_button(WC_Product $product): string
1094 {
1095 if (!$product->is_purchasable() || !$product->is_in_stock()) {
1096 return '<span class="king-addons-compare-table__add-to-cart is-disabled">' . esc_html__('Unavailable', 'king-addons') . '</span>';
1097 }
1098
1099 $classes = [
1100 'king-addons-compare-table__add-to-cart',
1101 'button',
1102 'product_type_' . $product->get_type(),
1103 ];
1104
1105 if ($product->supports('ajax_add_to_cart')) {
1106 $classes[] = 'ajax_add_to_cart';
1107 $classes[] = 'add_to_cart_button';
1108 }
1109
1110 $attributes = [
1111 'href' => $product->add_to_cart_url(),
1112 'data-quantity' => 1,
1113 'data-product_id' => $product->get_id(),
1114 'data-product_sku' => $product->get_sku(),
1115 'rel' => 'nofollow',
1116 'class' => implode(' ', array_filter($classes)),
1117 'aria-label' => wp_strip_all_tags($product->add_to_cart_description()),
1118 ];
1119
1120 return '<a ' . wc_implode_html_attributes($attributes) . '><span class="king-addons-compare-table__add-to-cart-label">' . esc_html($product->add_to_cart_text()) . '</span></a>';
1121 }
1122
1123 /**
1124 * Wrapper data attributes.
1125 *
1126 * @param array<string, mixed> $settings Settings.
1127 *
1128 * @return string
1129 */
1130 protected function get_wrapper_attributes(array $settings): string
1131 {
1132 $attrs = [
1133 'data-hide-equal' => 'no',
1134 'data-sticky-header' => 'no',
1135 ];
1136
1137 $compiled = [];
1138 foreach ($attrs as $key => $value) {
1139 $compiled[] = esc_attr($key) . '="' . esc_attr($value) . '"';
1140 }
1141
1142 return implode(' ', $compiled);
1143 }
1144 }
1145
1146
1147
1148
1149
1150
1151
1152