PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.49
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.49
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.49, at includes/widgets/Image_Marquee/Image_Marquee.php

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