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

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

976 lines 29.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Image Marquee 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 Image Marquee widget.
25 */
26 class Image_Marquee extends Widget_Base
27 {
28 /**
29 * Widget slug.
30 *
31 * @return string
32 */
33 public function get_name(): string
34 {
35 return 'king-addons-image-marquee';
36 }
37
38 /**
39 * Widget title.
40 *
41 * @return string
42 */
43 public function get_title(): string
44 {
45 return esc_html__('Image Marquee', 'king-addons');
46 }
47
48 /**
49 * Widget icon.
50 *
51 * @return string
52 */
53 public function get_icon(): string
54 {
55 return 'king-addons-icon king-addons-image-marquee';
56 }
57
58 /**
59 * 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 . '-image-marquee-script',
67 ];
68 }
69
70 /**
71 * 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 . '-image-marquee-style',
79 ];
80 }
81
82 /**
83 * Widget categories.
84 *
85 * @return array<int, string>
86 */
87 public function get_categories(): array
88 {
89 return ['king-addons'];
90 }
91
92 /**
93 * Widget keywords.
94 *
95 * @return array<int, string>
96 */
97 public function get_keywords(): array
98 {
99 return ['marquee', 'logo', 'slider', 'scroll', 'image'];
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_images_controls();
115 $this->register_marquee_settings_controls();
116 $this->register_style_item_controls();
117 $this->register_style_image_controls();
118 $this->register_style_overlay_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 $items = $settings['kng_items'] ?? [];
131
132 if (empty($items)) {
133 return;
134 }
135
136 $this->render_output($settings, $items);
137 }
138
139 /**
140 * Register images repeater controls.
141 *
142 * @return void
143 */
144 protected function register_content_images_controls(): void
145 {
146 $this->start_controls_section(
147 'kng_images_section',
148 [
149 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Images', 'king-addons'),
150 'tab' => Controls_Manager::TAB_CONTENT,
151 ]
152 );
153
154 $repeater = new Repeater();
155
156 $repeater->add_control(
157 'item_image',
158 [
159 'label' => esc_html__('Image', 'king-addons'),
160 'type' => Controls_Manager::MEDIA,
161 'default' => [
162 'url' => Utils::get_placeholder_image_src(),
163 ],
164 ]
165 );
166
167 $repeater->add_control(
168 'item_title',
169 [
170 'label' => esc_html__('Title', 'king-addons'),
171 'type' => Controls_Manager::TEXT,
172 'label_block' => true,
173 'default' => esc_html__('Brand Name', 'king-addons'),
174 ]
175 );
176
177 $repeater->add_control(
178 'item_alt',
179 [
180 'label' => esc_html__('Alt Text (optional)', 'king-addons'),
181 'type' => Controls_Manager::TEXT,
182 'label_block' => true,
183 ]
184 );
185
186 $repeater->add_control(
187 'item_hover_label',
188 [
189 'label' => esc_html__('Hover Label', 'king-addons'),
190 'type' => Controls_Manager::TEXT,
191 'label_block' => true,
192 ]
193 );
194
195 $repeater->add_control(
196 'item_link',
197 [
198 'label' => esc_html__('Link', 'king-addons'),
199 'type' => Controls_Manager::URL,
200 'placeholder' => esc_html__('https://example.com', 'king-addons'),
201 'label_block' => true,
202 'dynamic' => [
203 'active' => true,
204 ],
205 ]
206 );
207
208 $this->add_group_control(
209 Group_Control_Image_Size::get_type(),
210 [
211 'name' => 'item_image',
212 'default' => 'medium',
213 'separator' => 'before',
214 ]
215 );
216
217 $this->add_control(
218 'kng_items',
219 [
220 'label' => esc_html__('Items', 'king-addons'),
221 'type' => Controls_Manager::REPEATER,
222 'fields' => $repeater->get_controls(),
223 'default' => $this->get_default_items(),
224 'title_field' => '{{ item_title }}',
225 ]
226 );
227
228 $this->end_controls_section();
229 }
230
231 /**
232 * Register marquee behavior controls.
233 *
234 * @return void
235 */
236 protected function register_marquee_settings_controls(): void
237 {
238 $this->start_controls_section(
239 'kng_marquee_section',
240 [
241 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Marquee Settings', 'king-addons'),
242 'tab' => Controls_Manager::TAB_CONTENT,
243 ]
244 );
245
246 $this->add_control(
247 'kng_direction',
248 [
249 'label' => esc_html__('Direction', 'king-addons'),
250 'type' => Controls_Manager::SELECT,
251 'default' => 'right_to_left',
252 'options' => [
253 'left_to_right' => esc_html__('Left to Right', 'king-addons'),
254 'right_to_left' => esc_html__('Right to Left', 'king-addons'),
255 ],
256 ]
257 );
258
259 $this->add_responsive_control(
260 'kng_speed',
261 [
262 'label' => esc_html__('Speed', 'king-addons'),
263 'type' => Controls_Manager::SLIDER,
264 'size_units' => ['px'],
265 'range' => [
266 'px' => [
267 'min' => 1,
268 'max' => 200,
269 ],
270 ],
271 'default' => [
272 'size' => 50,
273 'unit' => 'px',
274 ],
275 ]
276 );
277
278 $this->add_responsive_control(
279 'kng_gap',
280 [
281 'label' => esc_html__('Gap Between Items', 'king-addons'),
282 'type' => Controls_Manager::SLIDER,
283 'size_units' => ['px'],
284 'range' => [
285 'px' => [
286 'min' => 0,
287 'max' => 100,
288 ],
289 ],
290 'default' => [
291 'size' => 30,
292 'unit' => 'px',
293 ],
294 'selectors' => [
295 '{{WRAPPER}} .king-addons-image-marquee' => '--kng-image-marquee-gap: {{SIZE}}{{UNIT}};',
296 ],
297 ]
298 );
299
300 $this->add_control(
301 'kng_pause_on_hover',
302 [
303 'label' => esc_html__('Pause on Hover', 'king-addons'),
304 'type' => Controls_Manager::SWITCHER,
305 'return_value' => 'yes',
306 'default' => 'yes',
307 ]
308 );
309
310 $this->add_control(
311 'kng_pause_on_touch',
312 [
313 'label' => esc_html__('Pause on Touch (Mobile)', 'king-addons'),
314 'type' => Controls_Manager::SWITCHER,
315 'return_value' => 'yes',
316 'default' => 'yes',
317 ]
318 );
319
320 $this->add_control(
321 'kng_duplicate_items',
322 [
323 'label' => esc_html__('Duplicate Items for Smooth Loop', 'king-addons'),
324 'type' => Controls_Manager::SWITCHER,
325 'return_value' => 'yes',
326 'default' => 'yes',
327 ]
328 );
329
330 $this->end_controls_section();
331 }
332
333 /**
334 * Register style controls for items.
335 *
336 * @return void
337 */
338 protected function register_style_item_controls(): void
339 {
340 $this->start_controls_section(
341 'kng_style_items_section',
342 [
343 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Items', 'king-addons'),
344 'tab' => Controls_Manager::TAB_STYLE,
345 ]
346 );
347
348 $this->add_responsive_control(
349 'kng_container_height',
350 [
351 'label' => esc_html__('Container Height', 'king-addons'),
352 'type' => Controls_Manager::SLIDER,
353 'size_units' => ['px'],
354 'range' => [
355 'px' => [
356 'min' => 0,
357 'max' => 600,
358 ],
359 ],
360 'selectors' => [
361 '{{WRAPPER}} .king-addons-image-marquee' => 'min-height: {{SIZE}}{{UNIT}};',
362 ],
363 ]
364 );
365
366 $this->add_responsive_control(
367 'kng_item_padding',
368 [
369 'label' => esc_html__('Item Padding', 'king-addons'),
370 'type' => Controls_Manager::DIMENSIONS,
371 'size_units' => ['px', '%', 'em'],
372 'selectors' => [
373 '{{WRAPPER}} .king-addons-image-marquee__item' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
374 ],
375 ]
376 );
377
378 $this->add_group_control(
379 Group_Control_Border::get_type(),
380 [
381 'name' => 'kng_item_border',
382 'selector' => '{{WRAPPER}} .king-addons-image-marquee__item',
383 ]
384 );
385
386 $this->add_control(
387 'kng_item_radius',
388 [
389 'label' => esc_html__('Item Border Radius', 'king-addons'),
390 'type' => Controls_Manager::DIMENSIONS,
391 'size_units' => ['px', '%'],
392 'selectors' => [
393 '{{WRAPPER}} .king-addons-image-marquee__item' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
394 ],
395 ]
396 );
397
398 $this->add_group_control(
399 Group_Control_Box_Shadow::get_type(),
400 [
401 'name' => 'kng_item_shadow',
402 'selector' => '{{WRAPPER}} .king-addons-image-marquee__item',
403 ]
404 );
405
406 $this->add_control(
407 'kng_container_background',
408 [
409 'label' => esc_html__('Container Background', 'king-addons'),
410 'type' => Controls_Manager::COLOR,
411 'selectors' => [
412 '{{WRAPPER}} .king-addons-image-marquee' => 'background-color: {{VALUE}};',
413 ],
414 ]
415 );
416
417 $this->end_controls_section();
418 }
419
420 /**
421 * Register style controls for images.
422 *
423 * @return void
424 */
425 protected function register_style_image_controls(): void
426 {
427 $this->start_controls_section(
428 'kng_style_image_section',
429 [
430 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Images', 'king-addons'),
431 'tab' => Controls_Manager::TAB_STYLE,
432 ]
433 );
434
435 $this->add_responsive_control(
436 'kng_image_width',
437 [
438 'label' => esc_html__('Image Width', 'king-addons'),
439 'type' => Controls_Manager::SLIDER,
440 'size_units' => ['px', '%'],
441 'range' => [
442 'px' => ['min' => 20, 'max' => 600],
443 '%' => ['min' => 5, 'max' => 100],
444 ],
445 'selectors' => [
446 '{{WRAPPER}} .king-addons-image-marquee__img' => 'width: {{SIZE}}{{UNIT}};',
447 ],
448 ]
449 );
450
451 $this->add_responsive_control(
452 'kng_image_height',
453 [
454 'label' => esc_html__('Image Height', 'king-addons'),
455 'type' => Controls_Manager::SLIDER,
456 'size_units' => ['px'],
457 'range' => [
458 'px' => ['min' => 20, 'max' => 400],
459 ],
460 'selectors' => [
461 '{{WRAPPER}} .king-addons-image-marquee__img' => 'height: {{SIZE}}{{UNIT}};',
462 ],
463 ]
464 );
465
466 $this->add_control(
467 'kng_image_object_fit',
468 [
469 'label' => esc_html__('Image Fit', 'king-addons'),
470 'type' => Controls_Manager::SELECT,
471 'default' => 'cover',
472 'options' => [
473 'cover' => esc_html__('Cover', 'king-addons'),
474 'contain' => esc_html__('Contain', 'king-addons'),
475 'fill' => esc_html__('Fill', 'king-addons'),
476 ],
477 'selectors' => [
478 '{{WRAPPER}} .king-addons-image-marquee__img' => 'object-fit: {{VALUE}};',
479 ],
480 ]
481 );
482
483 $this->add_control(
484 'kng_image_radius',
485 [
486 'label' => esc_html__('Image Border Radius', 'king-addons'),
487 'type' => Controls_Manager::DIMENSIONS,
488 'size_units' => ['px', '%'],
489 'selectors' => [
490 '{{WRAPPER}} .king-addons-image-marquee__img' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
491 ],
492 'separator' => 'before',
493 ]
494 );
495
496 $this->start_controls_tabs('kng_image_tabs');
497
498 $this->start_controls_tab(
499 'kng_image_tab_normal',
500 [
501 'label' => esc_html__('Normal', 'king-addons'),
502 ]
503 );
504
505 $this->add_control(
506 'kng_image_opacity',
507 [
508 'label' => esc_html__('Opacity', 'king-addons'),
509 'type' => Controls_Manager::SLIDER,
510 'size_units' => ['%'],
511 'range' => [
512 '%' => ['min' => 10, 'max' => 100],
513 ],
514 'default' => [
515 'size' => 100,
516 'unit' => '%',
517 ],
518 'selectors' => [
519 '{{WRAPPER}} .king-addons-image-marquee__img' => 'opacity: {{SIZE}}%;',
520 ],
521 ]
522 );
523
524 $this->add_control(
525 'kng_image_filter_normal',
526 [
527 'label' => esc_html__('Filter', 'king-addons'),
528 'type' => Controls_Manager::SELECT,
529 'default' => 'none',
530 'options' => [
531 'none' => esc_html__('None', 'king-addons'),
532 'grayscale(100%)' => esc_html__('Grayscale', 'king-addons'),
533 'blur(2px)' => esc_html__('Blur', 'king-addons'),
534 'brightness(1.1)' => esc_html__('Brightness', 'king-addons'),
535 ],
536 'selectors' => [
537 '{{WRAPPER}} .king-addons-image-marquee__img' => 'filter: {{VALUE}};',
538 ],
539 ]
540 );
541
542 $this->end_controls_tab();
543
544 $this->start_controls_tab(
545 'kng_image_tab_hover',
546 [
547 'label' => esc_html__('Hover', 'king-addons'),
548 ]
549 );
550
551 $this->add_control(
552 'kng_image_opacity_hover',
553 [
554 'label' => esc_html__('Opacity', 'king-addons'),
555 'type' => Controls_Manager::SLIDER,
556 'size_units' => ['%'],
557 'range' => [
558 '%' => ['min' => 10, 'max' => 100],
559 ],
560 'selectors' => [
561 '{{WRAPPER}} .king-addons-image-marquee__item:hover .king-addons-image-marquee__img' => 'opacity: {{SIZE}}%;',
562 ],
563 ]
564 );
565
566 $this->add_control(
567 'kng_image_filter_hover',
568 [
569 'label' => esc_html__('Filter', 'king-addons'),
570 'type' => Controls_Manager::SELECT,
571 'default' => 'none',
572 'options' => [
573 'none' => esc_html__('None', 'king-addons'),
574 'grayscale(100%)' => esc_html__('Grayscale', 'king-addons'),
575 'blur(2px)' => esc_html__('Blur', 'king-addons'),
576 'brightness(1.1)' => esc_html__('Brightness', 'king-addons'),
577 ],
578 'selectors' => [
579 '{{WRAPPER}} .king-addons-image-marquee__item:hover .king-addons-image-marquee__img' => 'filter: {{VALUE}};',
580 ],
581 ]
582 );
583
584 $this->end_controls_tab();
585 $this->end_controls_tabs();
586
587 $this->end_controls_section();
588 }
589
590 /**
591 * Register overlay and hover label controls.
592 *
593 * @return void
594 */
595 protected function register_style_overlay_controls(): void
596 {
597 $this->start_controls_section(
598 'kng_style_overlay_section',
599 [
600 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Overlay & Hover', 'king-addons'),
601 'tab' => Controls_Manager::TAB_STYLE,
602 ]
603 );
604
605 $this->start_controls_tabs('kng_overlay_tabs');
606
607 $this->start_controls_tab(
608 'kng_overlay_tab_normal',
609 [
610 'label' => esc_html__('Normal', 'king-addons'),
611 ]
612 );
613
614 $this->add_control(
615 'kng_overlay_color',
616 [
617 'label' => esc_html__('Overlay Color', 'king-addons'),
618 'type' => Controls_Manager::COLOR,
619 'selectors' => [
620 '{{WRAPPER}} .king-addons-image-marquee__overlay' => 'background-color: {{VALUE}};',
621 ],
622 ]
623 );
624
625 $this->end_controls_tab();
626
627 $this->start_controls_tab(
628 'kng_overlay_tab_hover',
629 [
630 'label' => esc_html__('Hover', 'king-addons'),
631 ]
632 );
633
634 $this->add_control(
635 'kng_overlay_color_hover',
636 [
637 'label' => esc_html__('Overlay Color', 'king-addons'),
638 'type' => Controls_Manager::COLOR,
639 'selectors' => [
640 '{{WRAPPER}} .king-addons-image-marquee__item:hover .king-addons-image-marquee__overlay' => 'background-color: {{VALUE}};',
641 ],
642 ]
643 );
644
645 $this->add_control(
646 'kng_hover_scale',
647 [
648 'label' => esc_html__('Hover Scale', 'king-addons'),
649 'type' => Controls_Manager::SLIDER,
650 'size_units' => ['px'],
651 'range' => [
652 'px' => [
653 'min' => 1,
654 'max' => 1.2,
655 'step' => 0.01,
656 ],
657 ],
658 'default' => [
659 'size' => 1.02,
660 'unit' => 'px',
661 ],
662 'selectors' => [
663 '{{WRAPPER}} .king-addons-image-marquee__item:hover .king-addons-image-marquee__img' => 'transform: scale({{SIZE}});',
664 ],
665 ]
666 );
667
668 $this->end_controls_tab();
669 $this->end_controls_tabs();
670
671 $this->add_group_control(
672 Group_Control_Typography::get_type(),
673 [
674 'name' => 'kng_hover_label_typography',
675 'selector' => '{{WRAPPER}} .king-addons-image-marquee__hover-label',
676 'separator' => 'before',
677 ]
678 );
679
680 $this->add_control(
681 'kng_hover_label_color',
682 [
683 'label' => esc_html__('Hover Label Color', 'king-addons'),
684 'type' => Controls_Manager::COLOR,
685 'selectors' => [
686 '{{WRAPPER}} .king-addons-image-marquee__hover-label' => 'color: {{VALUE}};',
687 ],
688 ]
689 );
690
691 $this->end_controls_section();
692 }
693
694 /**
695 * Render marquee output.
696 *
697 * @param array<string, mixed> $settings Widget settings.
698 * @param array<int, array<mixed>> $items Repeater items.
699 *
700 * @return void
701 */
702 protected function render_output(array $settings, array $items): void
703 {
704 $duplicate = ($settings['kng_duplicate_items'] ?? 'yes') === 'yes';
705 $wrapper_classes = ['king-addons-image-marquee'];
706 $track_attributes = $this->get_data_attributes($settings);
707
708 ?>
709 <div class="<?php echo esc_attr(implode(' ', $wrapper_classes)); ?>">
710 <?php $this->render_track($items, $track_attributes, $duplicate); ?>
711 </div>
712 <?php
713 }
714
715 /**
716 * Render track markup with items.
717 *
718 * @param array<int, array<string, mixed>> $items Items.
719 * @param string $attributes Track data attributes.
720 * @param bool $duplicate Whether to duplicate list.
721 *
722 * @return void
723 */
724 protected function render_track(array $items, string $attributes, bool $duplicate): void
725 {
726 ?>
727 <div class="king-addons-image-marquee__track" <?php echo $attributes; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
728 <div class="king-addons-image-marquee__list">
729 <?php foreach ($items as $item) : ?>
730 <?php $this->render_item($item); ?>
731 <?php endforeach; ?>
732 </div>
733 <?php if ($duplicate) : ?>
734 <div class="king-addons-image-marquee__list is-duplicate">
735 <?php foreach ($items as $item) : ?>
736 <?php $this->render_item($item); ?>
737 <?php endforeach; ?>
738 </div>
739 <?php endif; ?>
740 </div>
741 <?php
742 }
743
744 /**
745 * Render single item.
746 *
747 * @param array<string, mixed> $item Repeater item.
748 *
749 * @return void
750 */
751 protected function render_item(array $item): void
752 {
753 $image_html = $this->get_item_image_html($item);
754 if (empty($image_html)) {
755 return;
756 }
757
758 $hover_label = $item['item_hover_label'] ?? '';
759 $link = $item['item_link'] ?? [];
760 $link_attrs = $this->get_link_attributes($link);
761
762 $item_open = '<div class="king-addons-image-marquee__item">';
763 $item_close = '</div>';
764
765 if (!empty($link_attrs)) {
766 $item_open = '<a class="king-addons-image-marquee__item" ' . $link_attrs . '>';
767 $item_close = '</a>';
768 }
769
770 echo $item_open; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
771 ?>
772 <span class="king-addons-image-marquee__media">
773 <?php echo $image_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
774 <span class="king-addons-image-marquee__overlay"></span>
775 <?php if (!empty($hover_label)) : ?>
776 <span class="king-addons-image-marquee__hover-label"><?php echo esc_html($hover_label); ?></span>
777 <?php endif; ?>
778 </span>
779 <?php
780 echo $item_close; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
781 }
782
783 /**
784 * Build data attributes for JS (applied to track).
785 *
786 * @param array<string, mixed> $settings Widget settings.
787 *
788 * @return string
789 */
790 protected function get_data_attributes(array $settings): string
791 {
792 return $this->get_data_attributes_base($settings);
793 }
794
795 /**
796 * Base data attributes builder for JS.
797 *
798 * This method exists to allow the Pro version to extend behavior without
799 * calling `parent::` methods.
800 *
801 * @param array<string, mixed> $settings Widget settings.
802 *
803 * @return string
804 */
805 protected function get_data_attributes_base(array $settings): string
806 {
807 $speed = $settings['kng_speed']['size'] ?? 50;
808 $direction = $settings['kng_direction'] ?? 'right_to_left';
809 $pause_hover = $settings['kng_pause_on_hover'] ?? 'yes';
810 $pause_touch = $settings['kng_pause_on_touch'] ?? 'yes';
811 $duplicate = $settings['kng_duplicate_items'] ?? 'yes';
812 $orientation = $settings['kng_orientation'] ?? 'horizontal';
813 $reverse_hover = $settings['kng_reverse_on_hover'] ?? 'no';
814
815 $attributes = [
816 'data-direction' => esc_attr($direction),
817 'data-speed' => esc_attr((string) $speed),
818 'data-pause-hover' => esc_attr($pause_hover),
819 'data-pause-touch' => esc_attr($pause_touch),
820 'data-duplicate' => esc_attr($duplicate),
821 'data-orientation' => esc_attr($orientation),
822 'data-reverse-hover' => esc_attr($reverse_hover),
823 ];
824
825 $prepared = [];
826 foreach ($attributes as $key => $value) {
827 $prepared[] = $key . '="' . $value . '"';
828 }
829
830 return implode(' ', $prepared);
831 }
832
833 /**
834 * Render image HTML with alt/title fallbacks.
835 *
836 * @param array<string, mixed> $item Repeater item.
837 *
838 * @return string
839 */
840 protected function get_item_image_html(array $item): string
841 {
842 $image = $item['item_image'] ?? [];
843 $alt = $this->get_item_alt($item);
844 $title = $item['item_title'] ?? '';
845
846 if (!empty($image['id'])) {
847 $image_src = Group_Control_Image_Size::get_attachment_image_src($image['id'], 'item_image', $item);
848
849 if (!empty($image_src)) {
850 return '<img class="king-addons-image-marquee__img" src="' . esc_url($image_src) . '" alt="' . esc_attr($alt) . '" title="' . esc_attr($title) . '" />';
851 }
852 }
853
854 if (!empty($image['url'])) {
855 return '<img class="king-addons-image-marquee__img" src="' . esc_url($image['url']) . '" alt="' . esc_attr($alt) . '" title="' . esc_attr($title) . '" />';
856 }
857
858 return '';
859 }
860
861 /**
862 * Get computed alt text.
863 *
864 * @param array<string, mixed> $item Repeater item.
865 *
866 * @return string
867 */
868 protected function get_item_alt(array $item): string
869 {
870 $alt = $item['item_alt'] ?? '';
871 if (!empty($alt)) {
872 return $alt;
873 }
874
875 $title = $item['item_title'] ?? '';
876 if (!empty($title)) {
877 return $title;
878 }
879
880 $image = $item['item_image']['url'] ?? '';
881 if (!empty($image)) {
882 return wp_basename((string) $image);
883 }
884
885 return '';
886 }
887
888 /**
889 * Build link attributes.
890 *
891 * @param array<string, mixed> $link Link settings.
892 *
893 * @return string
894 */
895 protected function get_link_attributes(array $link): string
896 {
897 if (empty($link['url'])) {
898 return '';
899 }
900
901 $attributes = [
902 'href' => esc_url($link['url']),
903 'class' => 'king-addons-image-marquee__link',
904 ];
905
906 if (!empty($link['is_external'])) {
907 $attributes['target'] = '_blank';
908 $attributes['rel'] = 'noopener noreferrer';
909 }
910
911 if (!empty($link['nofollow'])) {
912 $attributes['rel'] = isset($attributes['rel']) ? $attributes['rel'] . ' nofollow' : 'nofollow';
913 }
914
915 $output = [];
916 foreach ($attributes as $key => $value) {
917 $output[] = esc_attr($key) . '="' . esc_attr((string) $value) . '"';
918 }
919
920 return implode(' ', $output);
921 }
922
923 /**
924 * Default items for the repeater.
925 *
926 * @return array<int, array<string, mixed>>
927 */
928 protected function get_default_items(): array
929 {
930 $defaults = [];
931 $placeholder = Utils::get_placeholder_image_src();
932
933 $labels = [
934 esc_html__('Modern Brand', 'king-addons'),
935 esc_html__('Creative Studio', 'king-addons'),
936 esc_html__('Product Line', 'king-addons'),
937 esc_html__('Trusted Partner', 'king-addons'),
938 esc_html__('Design Agency', 'king-addons'),
939 ];
940
941 foreach ($labels as $label) {
942 $defaults[] = [
943 'item_title' => $label,
944 'item_image' => ['url' => $placeholder],
945 'item_hover_label' => esc_html__('View', 'king-addons'),
946 ];
947 }
948
949 return $defaults;
950 }
951
952 /**
953 * Render pro notice.
954 *
955 * @return void
956 */
957 public function register_pro_notice_controls(): void
958 {
959 if (!king_addons_freemius()->can_use_premium_code__premium_only()) {
960 Core::renderProFeaturesSection($this, '', Controls_Manager::RAW_HTML, 'image-marquee', [
961 'Vertical marquee directions',
962 'Multiple rows with alternating directions',
963 'Advanced hover overlays and masks',
964 'Per-row speed and reverse-on-hover',
965 ]);
966 }
967 }
968 }
969
970
971
972
973
974
975
976