PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.83
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.83
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 / Quick_Card_Grid / Quick_Card_Grid.php

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

1,017 lines 32.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Quick Card Grid 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_Image_Size;
14 use Elementor\Group_Control_Typography;
15 use Elementor\Repeater;
16 use Elementor\Utils;
17 use Elementor\Widget_Base;
18
19 if (!defined('ABSPATH')) {
20 exit;
21 }
22
23 /**
24 * Renders the Quick Card Grid widget.
25 */
26 class Quick_Card_Grid extends Widget_Base
27 {
28 /**
29 * Get widget slug.
30 *
31 * @return string
32 */
33 public function get_name(): string
34 {
35 return 'king-addons-quick-card-grid';
36 }
37
38 /**
39 * Get widget title.
40 *
41 * @return string
42 */
43 public function get_title(): string
44 {
45 return esc_html__('Quick Card Grid', 'king-addons');
46 }
47
48 /**
49 * Get widget icon class.
50 *
51 * @return string
52 */
53 public function get_icon(): string
54 {
55 return 'king-addons-icon king-addons-simple-card-grid';
56 }
57
58 /**
59 * Enqueue script dependencies.
60 *
61 * @return array<int, string>
62 */
63 public function get_script_depends(): array
64 {
65 return [
66 KING_ADDONS_ASSETS_UNIQUE_KEY . '-quick-card-grid-script',
67 ];
68 }
69
70 /**
71 * Enqueue style dependencies.
72 *
73 * @return array<int, string>
74 */
75 public function get_style_depends(): array
76 {
77 return [
78 KING_ADDONS_ASSETS_UNIQUE_KEY . '-quick-card-grid-style',
79 ];
80 }
81
82 /**
83 * Get widget categories.
84 *
85 * @return array<int, string>
86 */
87 public function get_categories(): array
88 {
89 return ['king-addons'];
90 }
91
92 /**
93 * Get widget keywords.
94 *
95 * @return array<int, string>
96 */
97 public function get_keywords(): array
98 {
99 return ['grid', 'card', 'content', 'king-addons'];
100 }
101
102 public function get_custom_help_url()
103 {
104 return 'mailto:bug@kingaddons.com?subject=Bug Report - King Addons&body=Please describe the issue';
105 }
106
107 /**
108 * Register widget controls.
109 *
110 * @return void
111 */
112 public function register_controls(): void
113 {
114 $this->register_content_controls();
115 $this->register_layout_controls();
116 $this->register_style_card_controls();
117 $this->register_style_text_controls();
118 $this->register_style_button_controls();
119 $this->register_pro_notice_controls();
120 }
121
122 /**
123 * Render widget output.
124 *
125 * @return void
126 */
127 public function render(): void
128 {
129 $settings = $this->get_settings_for_display();
130 $this->render_output($settings);
131 }
132
133 /**
134 * Render grid output for the frontend.
135 *
136 * @param array<string, mixed> $settings Widget settings.
137 *
138 * @return void
139 */
140 public function render_output(array $settings): void
141 {
142 $cards = $settings['kng_cards'] ?? [];
143 if (empty($cards)) {
144 return;
145 }
146
147 $wrapper_classes = $this->get_wrapper_classes($settings);
148 $wrapper_style = $this->get_wrapper_style($settings);
149
150 ?>
151 <div class="<?php echo esc_attr(implode(' ', $wrapper_classes)); ?>"<?php echo $wrapper_style; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
152 <div class="king-addons-card-grid__grid">
153 <?php foreach ($cards as $card) : ?>
154 <?php $this->render_card($settings, $card); ?>
155 <?php endforeach; ?>
156 </div>
157 </div>
158 <?php
159 }
160
161 protected function register_content_controls(): void
162 {
163 $this->start_controls_section(
164 'kng_content_section',
165 [
166 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Cards', 'king-addons'),
167 'tab' => Controls_Manager::TAB_CONTENT,
168 ]
169 );
170
171 $repeater = new Repeater();
172
173 $repeater->add_control(
174 'kng_card_image',
175 [
176 'label' => esc_html__('Image', 'king-addons'),
177 'type' => Controls_Manager::MEDIA,
178 'default' => [
179 'url' => Utils::get_placeholder_image_src(),
180 ],
181 ]
182 );
183
184 $repeater->add_control(
185 'kng_card_title',
186 [
187 'label' => esc_html__('Title', 'king-addons'),
188 'type' => Controls_Manager::TEXT,
189 'default' => esc_html__('Card title', 'king-addons'),
190 'label_block' => true,
191 ]
192 );
193
194 $repeater->add_control(
195 'kng_card_description',
196 [
197 'label' => esc_html__('Description', 'king-addons'),
198 'type' => Controls_Manager::TEXTAREA,
199 'rows' => 3,
200 'default' => esc_html__('Card description goes here.', 'king-addons'),
201 ]
202 );
203
204 $repeater->add_control(
205 'kng_card_button_text',
206 [
207 'label' => esc_html__('Button Text', 'king-addons'),
208 'type' => Controls_Manager::TEXT,
209 'default' => esc_html__('Learn more', 'king-addons'),
210 'label_block' => true,
211 ]
212 );
213
214 $repeater->add_control(
215 'kng_card_button_link',
216 [
217 'label' => esc_html__('Button Link', 'king-addons'),
218 'type' => Controls_Manager::URL,
219 'placeholder' => esc_html__('https://your-link.com', 'king-addons'),
220 ]
221 );
222
223 $repeater->add_control(
224 'kng_card_link_enable',
225 [
226 'label' => esc_html__('Make Whole Card Clickable', 'king-addons'),
227 'type' => Controls_Manager::SWITCHER,
228 'label_on' => esc_html__('Yes', 'king-addons'),
229 'label_off' => esc_html__('No', 'king-addons'),
230 'return_value' => 'yes',
231 'description' => esc_html__('Editor safety: the card link is disabled in the Elementor editor to prevent accidental navigation.', 'king-addons'),
232 ]
233 );
234
235 $repeater->add_control(
236 'kng_card_link',
237 [
238 'label' => esc_html__('Card Link', 'king-addons'),
239 'type' => Controls_Manager::URL,
240 'placeholder' => esc_html__('https://your-link.com', 'king-addons'),
241 'condition' => [
242 'kng_card_link_enable' => 'yes',
243 ],
244 ]
245 );
246
247 $this->add_group_control(
248 Group_Control_Image_Size::get_type(),
249 [
250 'name' => 'kng_card_image',
251 'default' => 'medium',
252 'separator' => 'before',
253 ]
254 );
255
256 $this->add_control(
257 'kng_cards',
258 [
259 'label' => esc_html__('Cards', 'king-addons'),
260 'type' => Controls_Manager::REPEATER,
261 'fields' => $repeater->get_controls(),
262 'default' => [
263 [
264 'kng_card_title' => esc_html__('Modern layout', 'king-addons'),
265 'kng_card_description' => esc_html__('Clean card grid with safe spacing.', 'king-addons'),
266 'kng_card_button_text' => esc_html__('Read more', 'king-addons'),
267 'kng_card_image' => ['url' => Utils::get_placeholder_image_src()],
268 ],
269 [
270 'kng_card_title' => esc_html__('Responsive by default', 'king-addons'),
271 'kng_card_description' => esc_html__('Columns adapt to devices.', 'king-addons'),
272 'kng_card_button_text' => esc_html__('Details', 'king-addons'),
273 'kng_card_image' => ['url' => Utils::get_placeholder_image_src()],
274 ],
275 [
276 'kng_card_title' => esc_html__('Ready to use', 'king-addons'),
277 'kng_card_description' => esc_html__('Balanced paddings and typography.', 'king-addons'),
278 'kng_card_button_text' => esc_html__('Explore', 'king-addons'),
279 'kng_card_image' => ['url' => Utils::get_placeholder_image_src()],
280 ],
281 ],
282 'title_field' => '{{ kng_card_title }}',
283 ]
284 );
285
286 $this->end_controls_section();
287 }
288
289 protected function register_layout_controls(): void
290 {
291 $this->start_controls_section(
292 'kng_layout_section',
293 [
294 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Layout', 'king-addons'),
295 'tab' => Controls_Manager::TAB_CONTENT,
296 ]
297 );
298
299 $this->add_responsive_control(
300 'kng_columns',
301 [
302 'label' => esc_html__('Columns', 'king-addons'),
303 'type' => Controls_Manager::NUMBER,
304 'min' => 1,
305 'max' => 6,
306 'step' => 1,
307 'default' => 3,
308 'tablet_default' => 2,
309 'mobile_default' => 1,
310 'selectors' => [
311 '{{WRAPPER}} .king-addons-card-grid__grid' => 'grid-template-columns: repeat({{VALUE}}, minmax(0, 1fr));',
312 ],
313 ]
314 );
315
316 $this->add_responsive_control(
317 'kng_grid_gap',
318 [
319 'label' => esc_html__('Gap', 'king-addons'),
320 'type' => Controls_Manager::SLIDER,
321 'size_units' => ['px'],
322 'range' => [
323 'px' => [
324 'min' => 0,
325 'max' => 80,
326 ],
327 ],
328 'default' => [
329 'size' => 20,
330 'unit' => 'px',
331 ],
332 'selectors' => [
333 '{{WRAPPER}} .king-addons-card-grid__grid' => 'gap: {{SIZE}}{{UNIT}};',
334 ],
335 ]
336 );
337
338 $this->end_controls_section();
339 }
340
341 protected function register_style_card_controls(): void
342 {
343 $this->start_controls_section(
344 'kng_style_card_section',
345 [
346 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Card', 'king-addons'),
347 'tab' => Controls_Manager::TAB_STYLE,
348 ]
349 );
350
351 $this->add_control(
352 'kng_card_background',
353 [
354 'label' => esc_html__('Background', 'king-addons'),
355 'type' => Controls_Manager::COLOR,
356 'selectors' => [
357 '{{WRAPPER}} .king-addons-card-grid__card' => 'background-color: {{VALUE}};',
358 ],
359 ]
360 );
361
362 $this->add_group_control(
363 Group_Control_Border::get_type(),
364 [
365 'name' => 'kng_card_border',
366 'selector' => '{{WRAPPER}} .king-addons-card-grid__card',
367 ]
368 );
369
370 $this->add_control(
371 'kng_card_radius',
372 [
373 'label' => esc_html__('Border Radius', 'king-addons'),
374 'type' => Controls_Manager::SLIDER,
375 'size_units' => ['px', '%'],
376 'range' => [
377 'px' => ['min' => 0, 'max' => 60],
378 '%' => ['min' => 0, 'max' => 100],
379 ],
380 'selectors' => [
381 '{{WRAPPER}} .king-addons-card-grid__card' => 'border-radius: {{SIZE}}{{UNIT}};',
382 ],
383 ]
384 );
385
386 $this->add_group_control(
387 Group_Control_Box_Shadow::get_type(),
388 [
389 'name' => 'kng_card_shadow',
390 'selector' => '{{WRAPPER}} .king-addons-card-grid__card',
391 ]
392 );
393
394 $this->add_control(
395 'kng_card_shadow_disable',
396 [
397 'label' => esc_html__('Disable Box Shadow', 'king-addons'),
398 'type' => Controls_Manager::SWITCHER,
399 'return_value' => 'yes',
400 'default' => '',
401 'selectors' => [
402 '{{WRAPPER}} .king-addons-card-grid__card' => 'box-shadow: none !important;',
403 ],
404 ]
405 );
406
407 $this->add_control(
408 'kng_card_shadow_hover_disable',
409 [
410 'label' => esc_html__('Disable Box Shadow on Hover', 'king-addons'),
411 'type' => Controls_Manager::SWITCHER,
412 'return_value' => 'yes',
413 'default' => '',
414 'selectors' => [
415 '{{WRAPPER}} .king-addons-card-grid__card:hover' => 'box-shadow: none !important;',
416 ],
417 ]
418 );
419
420 $this->add_responsive_control(
421 'kng_card_padding',
422 [
423 'label' => esc_html__('Card Padding', 'king-addons'),
424 'type' => Controls_Manager::DIMENSIONS,
425 'size_units' => ['px', '%', 'em'],
426 'selectors' => [
427 '{{WRAPPER}} .king-addons-card-grid__card' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
428 ],
429 ]
430 );
431
432 $this->end_controls_section();
433 }
434
435 protected function register_style_text_controls(): void
436 {
437 $this->start_controls_section(
438 'kng_style_text_section',
439 [
440 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Text', 'king-addons'),
441 'tab' => Controls_Manager::TAB_STYLE,
442 ]
443 );
444
445 $this->add_group_control(
446 Group_Control_Typography::get_type(),
447 [
448 'name' => 'kng_title_typography',
449 'selector' => '{{WRAPPER}} .king-addons-card-grid__title',
450 ]
451 );
452
453 $this->add_control(
454 'kng_title_color',
455 [
456 'label' => esc_html__('Title Color', 'king-addons'),
457 'type' => Controls_Manager::COLOR,
458 'selectors' => [
459 '{{WRAPPER}} .king-addons-card-grid__title' => 'color: {{VALUE}};',
460 ],
461 ]
462 );
463
464 $this->add_responsive_control(
465 'kng_title_spacing',
466 [
467 'label' => esc_html__('Title Bottom Spacing', 'king-addons'),
468 'type' => Controls_Manager::SLIDER,
469 'size_units' => ['px'],
470 'range' => [
471 'px' => ['min' => 0, 'max' => 60],
472 ],
473 'selectors' => [
474 '{{WRAPPER}} .king-addons-card-grid__title' => 'margin-bottom: {{SIZE}}{{UNIT}};',
475 ],
476 ]
477 );
478
479 $this->add_group_control(
480 Group_Control_Typography::get_type(),
481 [
482 'name' => 'kng_description_typography',
483 'selector' => '{{WRAPPER}} .king-addons-card-grid__description',
484 ]
485 );
486
487 $this->add_control(
488 'kng_description_color',
489 [
490 'label' => esc_html__('Description Color', 'king-addons'),
491 'type' => Controls_Manager::COLOR,
492 'selectors' => [
493 '{{WRAPPER}} .king-addons-card-grid__description' => 'color: {{VALUE}};',
494 ],
495 ]
496 );
497
498 $this->add_responsive_control(
499 'kng_description_spacing',
500 [
501 'label' => esc_html__('Description Bottom Spacing', 'king-addons'),
502 'type' => Controls_Manager::SLIDER,
503 'size_units' => ['px'],
504 'range' => [
505 'px' => ['min' => 0, 'max' => 80],
506 ],
507 'selectors' => [
508 '{{WRAPPER}} .king-addons-card-grid__description' => 'margin-bottom: {{SIZE}}{{UNIT}};',
509 ],
510 ]
511 );
512
513 $this->add_control(
514 'kng_text_alignment',
515 [
516 'label' => esc_html__('Text Alignment', 'king-addons'),
517 'type' => Controls_Manager::CHOOSE,
518 'options' => [
519 'left' => [
520 'title' => esc_html__('Left', 'king-addons'),
521 'icon' => 'eicon-text-align-left',
522 ],
523 'center' => [
524 'title' => esc_html__('Center', 'king-addons'),
525 'icon' => 'eicon-text-align-center',
526 ],
527 'right' => [
528 'title' => esc_html__('Right', 'king-addons'),
529 'icon' => 'eicon-text-align-right',
530 ],
531 ],
532 'toggle' => false,
533 'default' => 'left',
534 'prefix_class' => 'king-addons-card-grid--text-align-',
535 ]
536 );
537
538 $this->end_controls_section();
539 }
540
541 protected function register_style_button_controls(): void
542 {
543 $this->start_controls_section(
544 'kng_style_button_section',
545 [
546 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Button', 'king-addons'),
547 'tab' => Controls_Manager::TAB_STYLE,
548 ]
549 );
550
551 $this->add_control(
552 'kng_button_alignment',
553 [
554 'label' => esc_html__('Button Alignment', 'king-addons'),
555 'type' => Controls_Manager::CHOOSE,
556 'options' => [
557 'left' => [
558 'title' => esc_html__('Left', 'king-addons'),
559 'icon' => 'eicon-h-align-left',
560 ],
561 'center' => [
562 'title' => esc_html__('Center', 'king-addons'),
563 'icon' => 'eicon-h-align-center',
564 ],
565 'right' => [
566 'title' => esc_html__('Right', 'king-addons'),
567 'icon' => 'eicon-h-align-right',
568 ],
569 ],
570 'toggle' => false,
571 'default' => 'left',
572 'prefix_class' => 'king-addons-card-grid--button-align-',
573 ]
574 );
575
576 $this->add_control(
577 'kng_button_text_alignment',
578 [
579 'label' => esc_html__('Button Text Alignment', 'king-addons'),
580 'type' => Controls_Manager::CHOOSE,
581 'options' => [
582 'left' => [
583 'title' => esc_html__('Left', 'king-addons'),
584 'icon' => 'eicon-h-align-left',
585 ],
586 'center' => [
587 'title' => esc_html__('Center', 'king-addons'),
588 'icon' => 'eicon-h-align-center',
589 ],
590 'right' => [
591 'title' => esc_html__('Right', 'king-addons'),
592 'icon' => 'eicon-h-align-right',
593 ],
594 ],
595 'toggle' => false,
596 'default' => 'center',
597 'prefix_class' => 'king-addons-card-grid--button-text-align-',
598 ]
599 );
600
601 $this->add_group_control(
602 Group_Control_Typography::get_type(),
603 [
604 'name' => 'kng_button_typography',
605 'selector' => '{{WRAPPER}} .king-addons-card-grid__button',
606 ]
607 );
608
609 $this->start_controls_tabs('kng_button_tabs');
610
611 $this->start_controls_tab(
612 'kng_button_tab_normal',
613 [
614 'label' => esc_html__('Normal', 'king-addons'),
615 ]
616 );
617
618 $this->add_control(
619 'kng_button_text_color',
620 [
621 'label' => esc_html__('Text Color', 'king-addons'),
622 'type' => Controls_Manager::COLOR,
623 'selectors' => [
624 '{{WRAPPER}} .king-addons-card-grid__button' => 'color: {{VALUE}};',
625 ],
626 ]
627 );
628
629 $this->add_control(
630 'kng_button_background',
631 [
632 'label' => esc_html__('Background', 'king-addons'),
633 'type' => Controls_Manager::COLOR,
634 'selectors' => [
635 '{{WRAPPER}} .king-addons-card-grid__button' => 'background-color: {{VALUE}};',
636 ],
637 ]
638 );
639
640 $this->add_group_control(
641 Group_Control_Border::get_type(),
642 [
643 'name' => 'kng_button_border',
644 'selector' => '{{WRAPPER}} .king-addons-card-grid__button',
645 ]
646 );
647
648 $this->add_group_control(
649 Group_Control_Box_Shadow::get_type(),
650 [
651 'name' => 'kng_button_shadow',
652 'selector' => '{{WRAPPER}} .king-addons-card-grid__button',
653 ]
654 );
655
656 $this->end_controls_tab();
657
658 $this->start_controls_tab(
659 'kng_button_tab_hover',
660 [
661 'label' => esc_html__('Hover', 'king-addons'),
662 ]
663 );
664
665 $this->add_control(
666 'kng_button_text_color_hover',
667 [
668 'label' => esc_html__('Text Color', 'king-addons'),
669 'type' => Controls_Manager::COLOR,
670 'selectors' => [
671 '{{WRAPPER}} .king-addons-card-grid__button:hover' => 'color: {{VALUE}};',
672 ],
673 ]
674 );
675
676 $this->add_control(
677 'kng_button_background_hover',
678 [
679 'label' => esc_html__('Background', 'king-addons'),
680 'type' => Controls_Manager::COLOR,
681 'selectors' => [
682 '{{WRAPPER}} .king-addons-card-grid__button:hover' => 'background-color: {{VALUE}};',
683 ],
684 ]
685 );
686
687 $this->add_control(
688 'kng_button_border_color_hover',
689 [
690 'label' => esc_html__('Border Color', 'king-addons'),
691 'type' => Controls_Manager::COLOR,
692 'selectors' => [
693 '{{WRAPPER}} .king-addons-card-grid__button:hover' => 'border-color: {{VALUE}};',
694 ],
695 ]
696 );
697
698 $this->add_group_control(
699 Group_Control_Box_Shadow::get_type(),
700 [
701 'name' => 'kng_button_shadow_hover',
702 'selector' => '{{WRAPPER}} .king-addons-card-grid__button:hover',
703 ]
704 );
705
706 $this->end_controls_tab();
707 $this->end_controls_tabs();
708
709 $this->add_responsive_control(
710 'kng_button_padding',
711 [
712 'label' => esc_html__('Padding', 'king-addons'),
713 'type' => Controls_Manager::DIMENSIONS,
714 'size_units' => ['px', '%', 'em'],
715 'selectors' => [
716 '{{WRAPPER}} .king-addons-card-grid__button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
717 ],
718 'separator' => 'before',
719 ]
720 );
721
722 $this->add_control(
723 'kng_button_radius',
724 [
725 'label' => esc_html__('Border Radius', 'king-addons'),
726 'type' => Controls_Manager::SLIDER,
727 'size_units' => ['px', '%'],
728 'range' => [
729 'px' => ['min' => 0, 'max' => 80],
730 '%' => ['min' => 0, 'max' => 100],
731 ],
732 'selectors' => [
733 '{{WRAPPER}} .king-addons-card-grid__button' => 'border-radius: {{SIZE}}{{UNIT}};',
734 ],
735 ]
736 );
737
738 $this->add_control(
739 'kng_button_shadow_disable',
740 [
741 'label' => esc_html__('Disable Box Shadow', 'king-addons'),
742 'type' => Controls_Manager::SWITCHER,
743 'label_on' => esc_html__('Yes', 'king-addons'),
744 'label_off' => esc_html__('No', 'king-addons'),
745 'return_value' => 'yes',
746 'default' => '',
747 'selectors' => [
748 '{{WRAPPER}} .king-addons-card-grid__button' => 'box-shadow: none !important;',
749 ],
750 ]
751 );
752
753 $this->add_control(
754 'kng_button_shadow_hover_disable',
755 [
756 'label' => esc_html__('Disable Box Shadow on Hover', 'king-addons'),
757 'type' => Controls_Manager::SWITCHER,
758 'label_on' => esc_html__('Yes', 'king-addons'),
759 'label_off' => esc_html__('No', 'king-addons'),
760 'return_value' => 'yes',
761 'default' => '',
762 'selectors' => [
763 '{{WRAPPER}} .king-addons-card-grid__button:hover' => 'box-shadow: none !important;',
764 ],
765 ]
766 );
767
768 $this->end_controls_section();
769 }
770
771 /**
772 * Render a single card item.
773 *
774 * @param array<string, mixed> $settings Widget settings.
775 * @param array<string, mixed> $card Card data.
776 *
777 * @return void
778 */
779 protected function render_card(array $settings, array $card): void
780 {
781 $card_classes = ['king-addons-card-grid__card'];
782 $card_classes = array_merge($card_classes, $this->get_additional_card_classes($settings, $card));
783
784 $card_link_enabled = ($card['kng_card_link_enable'] ?? '') === 'yes';
785 $card_link = $card['kng_card_link'] ?? [];
786 $card_link_attributes = $this->get_card_link_attributes($card_link_enabled, $card_link);
787 $card_attribute_string = $card_link_attributes ? ' ' . $card_link_attributes : '';
788
789 $image_html = $this->get_card_image($card);
790
791 $title = $card['kng_card_title'] ?? '';
792 $description = $card['kng_card_description'] ?? '';
793 $button_text = $card['kng_card_button_text'] ?? '';
794 $button_link = $card['kng_card_button_link'] ?? [];
795
796 ?>
797 <div class="king-addons-card-grid__item">
798 <article class="<?php echo esc_attr(implode(' ', array_filter($card_classes))); ?>"<?php echo $card_attribute_string; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
799 <?php if (!empty($image_html)) : ?>
800 <div class="king-addons-card-grid__media">
801 <?php echo $image_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
802 </div>
803 <?php endif; ?>
804
805 <div class="king-addons-card-grid__body">
806 <?php if (!empty($title)) : ?>
807 <h3 class="king-addons-card-grid__title"><?php echo esc_html($title); ?></h3>
808 <?php endif; ?>
809 <?php if (!empty($description)) : ?>
810 <div class="king-addons-card-grid__description"><?php echo esc_html($description); ?></div>
811 <?php endif; ?>
812 <?php if (!empty($button_text)) : ?>
813 <?php $this->render_button($button_text, $button_link); ?>
814 <?php endif; ?>
815 </div>
816 </article>
817 </div>
818 <?php
819 }
820
821 /**
822 * Build wrapper classes.
823 *
824 * @param array<string, mixed> $settings Widget settings.
825 *
826 * @return array<int, string>
827 */
828 protected function get_wrapper_classes(array $settings): array
829 {
830 $classes = ['king-addons-card-grid'];
831
832 $text_align = $settings['kng_text_alignment'] ?? 'left';
833 $classes[] = 'king-addons-card-grid--text-align-' . sanitize_html_class((string) $text_align);
834
835 $button_align = $settings['kng_button_alignment'] ?? 'left';
836 $classes[] = 'king-addons-card-grid--button-align-' . sanitize_html_class((string) $button_align);
837
838 $button_text_align = $settings['kng_button_text_alignment'] ?? 'center';
839 $classes[] = 'king-addons-card-grid--button-text-align-' . sanitize_html_class((string) $button_text_align);
840
841 $classes = array_merge($classes, $this->get_additional_wrapper_classes($settings));
842
843 return array_filter($classes);
844 }
845
846 /**
847 * Build wrapper inline styles.
848 *
849 * @param array<string, mixed> $settings Widget settings.
850 *
851 * @return string
852 */
853 protected function get_wrapper_style(array $settings): string
854 {
855 $style_parts = [];
856
857 if (isset($settings['kng_grid_gap']['size'])) {
858 $style_parts[] = '--kng-card-grid-gap:' . (float) $settings['kng_grid_gap']['size'] . ($settings['kng_grid_gap']['unit'] ?? 'px') . ';';
859 }
860
861 if (!empty($style_parts)) {
862 return ' style="' . esc_attr(implode(' ', $style_parts)) . '"';
863 }
864
865 return '';
866 }
867
868 /**
869 * Get card image HTML.
870 *
871 * @param array<string, mixed> $card Card data.
872 *
873 * @return string
874 */
875 protected function get_card_image(array $card): string
876 {
877 if (empty($card['kng_card_image']['id']) && empty($card['kng_card_image']['url'])) {
878 return '';
879 }
880
881 $image_html = Group_Control_Image_Size::get_attachment_image_html($card, 'kng_card_image');
882
883 if (!empty($image_html)) {
884 return $image_html;
885 }
886
887 return '<img src="' . esc_url(Utils::get_placeholder_image_src()) . '" alt="' . esc_attr($card['kng_card_title'] ?? '') . '"/>';
888 }
889
890 /**
891 * Render button element.
892 *
893 * @param string $text Button text.
894 * @param array<string, mixed> $link Link data.
895 *
896 * @return void
897 */
898 protected function render_button(string $text, array $link): void
899 {
900 $url = $link['url'] ?? '';
901 $rel = [];
902
903 if (!empty($link['nofollow'])) {
904 $rel[] = 'nofollow';
905 }
906
907 if (!empty($link['is_external'])) {
908 $rel[] = 'noopener';
909 $rel[] = 'noreferrer';
910 }
911
912 $attributes = '';
913
914 if (!empty($url)) {
915 $attributes .= ' href="' . esc_url($url) . '"';
916 }
917
918 if (!empty($link['is_external'])) {
919 $attributes .= ' target="_blank"';
920 }
921
922 if (!empty($rel)) {
923 $attributes .= ' rel="' . esc_attr(implode(' ', array_unique($rel))) . '"';
924 }
925
926 ?>
927 <a class="king-addons-card-grid__button"<?php echo $attributes; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
928 <?php echo esc_html($text); ?>
929 </a>
930 <?php
931 }
932
933 /**
934 * Build card-level link data attributes.
935 *
936 * @param bool $enabled Whether link is enabled.
937 * @param array<string, mixed> $link Link data.
938 *
939 * @return string
940 */
941 protected function get_card_link_attributes(bool $enabled, array $link): string
942 {
943 if (!$enabled || empty($link['url'])) {
944 return '';
945 }
946
947 $attributes = [
948 'data-card-link' => esc_url_raw($link['url']),
949 ];
950
951 if (!empty($link['is_external'])) {
952 $attributes['data-card-link-target'] = '_blank';
953 }
954
955 if (!empty($link['nofollow'])) {
956 $attributes['data-card-link-rel'] = 'nofollow';
957 }
958
959 $output = [];
960 foreach ($attributes as $key => $value) {
961 $output[] = esc_attr($key) . '="' . esc_attr((string) $value) . '"';
962 }
963
964 return implode(' ', $output);
965 }
966
967 /**
968 * Placeholder for pro wrapper classes.
969 *
970 * @param array<string, mixed> $settings Widget settings.
971 *
972 * @return array<int, string>
973 */
974 protected function get_additional_wrapper_classes(array $settings): array
975 {
976 unset($settings);
977 return [];
978 }
979
980 /**
981 * Placeholder for pro card classes.
982 *
983 * @param array<string, mixed> $settings Widget settings.
984 * @param array<string, mixed> $card Card data.
985 *
986 * @return array<int, string>
987 */
988 protected function get_additional_card_classes(array $settings, array $card): array
989 {
990 unset($settings, $card);
991 return [];
992 }
993
994 /**
995 * Render premium upgrade notice.
996 *
997 * @return void
998 */
999 public function register_pro_notice_controls(): void
1000 {
1001 if (!king_addons_freemius()->can_use_premium_code__premium_only()) {
1002 Core::renderProFeaturesSection($this, '', Controls_Manager::RAW_HTML, 'quick-card-grid', [
1003 'Advanced hover animations for cards',
1004 'Extended styling controls and presets',
1005 'Additional layout polish for cards and buttons',
1006 ]);
1007 }
1008 }
1009 }
1010
1011
1012
1013
1014
1015
1016
1017