PluginProbe
MarqueeX – Smooth Marquee Slider, News Ticker & Post Marquee for Block Editor & Elementor / 0.6.0
MarqueeX – Smooth Marquee Slider, News Ticker & Post Marquee for Block Editor & Elementor v0.6.0
0.6.0 0.5.4 0.5.3 0.5.2 0.5.1 0.5.0 0.4.0 0.3.3 0.3.2 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.1.0 0.1.1 0.1.2 0.2.0 0.2.1 0.2.2 0.2.3 0.3.0 0.3.1
marqueex / includes / Integrations / Elementor / Widgets / TestimonialMarquee.php

TestimonialMarquee.php in MarqueeX – Smooth Marquee Slider, News Ticker & Post Marquee for Block Editor & Elementor 0.6.0, at includes/Integrations/Elementor/Widgets/TestimonialMarquee.php

437 lines 14.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Wpxero\Marqueex\Integrations\Elementor\Widgets;
4
5 use Elementor\Widget_Base;
6 use Elementor\Controls_Manager;
7 use Elementor\Repeater;
8 use Elementor\Group_Control_Background;
9 use Elementor\Group_Control_Typography;
10
11 if (!defined('ABSPATH')) {
12 exit;
13 }
14
15 class TestimonialMarquee extends Widget_Base {
16
17 public function get_name() {
18 return 'marqueex-testimonial-marquee';
19 }
20
21 public function get_title() {
22 return __('Testimonial Marquee', 'marqueex');
23 }
24
25 public function get_icon() {
26 return 'eicon-testimonial-carousel eicon-marqueex';
27 }
28
29 public function get_categories() {
30 return ['marqueex'];
31 }
32
33 public function get_style_depends() {
34 return ['marqueex-testimonial-marquee'];
35 }
36
37 public function get_script_depends() {
38 return ['marqueex-testimonial-marquee'];
39 }
40
41 public function get_keywords() {
42 return ['testimonial', 'marquee', 'review', 'feedback', 'slider', 'rating'];
43 }
44
45 protected function register_controls() {
46 $this->start_controls_section(
47 'content',
48 ['label' => __('Testimonials', 'marqueex')]
49 );
50
51 $repeater = new Repeater();
52
53 $repeater->add_control(
54 'content',
55 [
56 'label' => __('Content', 'marqueex'),
57 'type' => Controls_Manager::TEXTAREA,
58 'default' => __('This is a testimonial text', 'marqueex'),
59 'rows' => 5,
60 ]
61 );
62
63 $repeater->add_control(
64 'avatar',
65 [
66 'label' => __('Avatar', 'marqueex'),
67 'type' => Controls_Manager::MEDIA,
68 'default' => [
69 'url' => \Elementor\Utils::get_placeholder_image_src(),
70 ],
71 ]
72 );
73
74 $repeater->add_control(
75 'name',
76 [
77 'label' => __('Name', 'marqueex'),
78 'type' => Controls_Manager::TEXT,
79 'default' => __('John Doe', 'marqueex'),
80 ]
81 );
82
83 $repeater->add_control(
84 'role',
85 [
86 'label' => __('Role/Designation', 'marqueex'),
87 'type' => Controls_Manager::TEXT,
88 'default' => __('Project Manager', 'marqueex'),
89 ]
90 );
91
92 $repeater->add_control(
93 'rating',
94 [
95 'label' => __('Rating', 'marqueex'),
96 'type' => Controls_Manager::NUMBER,
97 'min' => 1,
98 'max' => 5,
99 'step' => 1,
100 'default' => 5,
101 ]
102 );
103
104 $this->add_control(
105 'items',
106 [
107 'label' => __('Testimonials List', 'marqueex'),
108 'type' => Controls_Manager::REPEATER,
109 'fields' => $repeater->get_controls(),
110 'default' => [
111 [
112 'content' => __('MarqueeX is the perfect tool for anyone using Elementor.', 'marqueex'),
113 'name' => __('Wade Warren', 'marqueex'),
114 'role' => __('Project Manager', 'marqueex'),
115 'rating' => 5,
116 ],
117 [
118 'content' => __('Amazing features and great support. Highly recommended!', 'marqueex'),
119 'name' => __('Sarah Johnson', 'marqueex'),
120 'role' => __('Designer', 'marqueex'),
121 'rating' => 5,
122 ],
123 ],
124 'title_field' => '{{{ name }}}',
125 ]
126 );
127
128 $this->add_control(
129 'marqueex_testimonial_tip',
130 [
131 'type' => Controls_Manager::RAW_HTML,
132 'raw' => '<strong>💡 Tip:</strong> For a smoother slider experience, add <strong>8–10 items</strong>.',
133 'content_classes' => 'elementor-panel-alert elementor-panel-alert-info',
134 ]
135 );
136
137 $this->end_controls_section();
138
139 $this->start_controls_section(
140 'settings',
141 ['label' => __('Marquee Settings', 'marqueex')]
142 );
143
144 $this->add_control(
145 'orientation',
146 [
147 'label' => __('Orientation', 'marqueex'),
148 'type' => Controls_Manager::SELECT,
149 'default' => 'horizontal',
150 'options' => [
151 'horizontal' => __('Horizontal', 'marqueex'),
152 'vertical' => __('Vertical', 'marqueex'),
153 ],
154 ]
155 );
156
157 $this->add_control(
158 'vertical_height',
159 [
160 'label' => __('Height', 'marqueex'),
161 'type' => Controls_Manager::SLIDER,
162 'size_units' => ['vh', 'px'],
163 'range' => [
164 'vh' => ['min' => 20, 'max' => 100],
165 'px' => ['min' => 200, 'max' => 1000],
166 ],
167 'default' => ['size' => 60, 'unit' => 'vh'],
168 'condition' => ['orientation' => 'vertical'],
169 'selectors' => [
170 '{{WRAPPER}} .marqueex-marquee-block' => 'height: {{SIZE}}{{UNIT}};',
171 ],
172 ]
173 );
174
175 $this->add_control(
176 'reverse',
177 [
178 'label' => __('Reverse Direction', 'marqueex'),
179 'type' => Controls_Manager::SWITCHER,
180 'return_value' => 'yes',
181 ]
182 );
183
184 $this->add_control(
185 'pause',
186 [
187 'label' => __('Pause on Hover', 'marqueex'),
188 'type' => Controls_Manager::SWITCHER,
189 'return_value' => 'yes',
190 'default' => 'yes',
191 ]
192 );
193
194 $this->add_control(
195 'speed',
196 [
197 'label' => __('Speed (px/s)', 'marqueex'),
198 'type' => Controls_Manager::SLIDER,
199 'range' => ['px' => ['min' => 1, 'max' => 200]],
200 'default' => ['size' => 20, 'unit' => 'px'],
201 ]
202 );
203
204 $this->add_control(
205 'gap',
206 [
207 'label' => __('Gap (px)', 'marqueex'),
208 'type' => Controls_Manager::NUMBER,
209 'default' => 24,
210 'min' => 0,
211 'max' => 100,
212 ]
213 );
214
215 $this->add_control(
216 'mask_edges',
217 [
218 'label' => __('Soft Edge Mask', 'marqueex'),
219 'type' => Controls_Manager::SWITCHER,
220 'return_value' => 'yes',
221 ]
222 );
223
224 $this->end_controls_section();
225
226 $this->start_controls_section(
227 'style_section',
228 [
229 'label' => __('Card Style', 'marqueex'),
230 'tab' => Controls_Manager::TAB_STYLE,
231 ]
232 );
233
234 $this->add_group_control(
235 Group_Control_Background::get_type(),
236 [
237 'name' => 'card_background',
238 'label' => __('Card Background', 'marqueex'),
239 'types' => ['classic', 'gradient'],
240 'selector' => '{{WRAPPER}} .marqueex-testimonial .marqueex-card',
241 ]
242 );
243
244 $this->add_control(
245 'content_color',
246 [
247 'label' => __('Content Color', 'marqueex'),
248 'type' => Controls_Manager::COLOR,
249 'selectors' => [
250 '{{WRAPPER}} .marqueex-testimonial .marqueex-content' => 'color: {{VALUE}};',
251 ],
252 ]
253 );
254
255 $this->add_control(
256 'name_color',
257 [
258 'label' => __('Name Color', 'marqueex'),
259 'type' => Controls_Manager::COLOR,
260 'selectors' => [
261 '{{WRAPPER}} .marqueex-testimonial .marqueex-name' => 'color: {{VALUE}};',
262 ],
263 ]
264 );
265
266 $this->add_control(
267 'role_color',
268 [
269 'label' => __('Role Color', 'marqueex'),
270 'type' => Controls_Manager::COLOR,
271 'selectors' => [
272 '{{WRAPPER}} .marqueex-testimonial .marqueex-role' => 'color: {{VALUE}};',
273 ],
274 ]
275 );
276
277 $this->add_control(
278 'stars_color',
279 [
280 'label' => __('Stars Color', 'marqueex'),
281 'type' => Controls_Manager::COLOR,
282 'default' => '#f1c40f',
283 'selectors' => [
284 '{{WRAPPER}} .marqueex-testimonial .marqueex-star' => 'color: {{VALUE}};',
285 ],
286 ]
287 );
288
289 $this->add_group_control(
290 Group_Control_Typography::get_type(),
291 [
292 'name' => 'content_typo',
293 'selector' => '{{WRAPPER}} .marqueex-testimonial .marqueex-content',
294 ]
295 );
296
297 $this->end_controls_section();
298 }
299
300 /**
301 * Ensure minimum items for smooth marquee animation
302 */
303 private function ensure_minimum_posts($posts) {
304 if (empty($posts)) {
305 return [];
306 }
307
308 // Duplicate posts if we have less than 10 for smooth marquee
309 while (count($posts) < 10) {
310 $posts = array_merge($posts, $posts);
311 }
312
313 return $posts;
314 }
315
316 /**
317 * Prepare widget configuration from settings
318 */
319 private function prepare_widget_config($settings) {
320 return [
321 'unique_id' => 'marqueex-testimonial-' . $this->get_id(),
322 'speed' => max(10, min(400, intval($settings['speed']['size'] ?? 60))),
323 'orientation' => $settings['orientation'] ?? 'horizontal',
324 'pause_on_hover' => $settings['pause'] === 'yes' ? 'true' : 'false',
325 'reverse_direction' => $settings['reverse'] === 'yes',
326 ];
327 }
328
329 /**
330 * Render testimonials content
331 */
332 private function render_testimonials_content($settings) {
333 if (empty($settings['items'])) {
334 return;
335 }
336
337 $testimonials = $this->ensure_minimum_posts($settings['items']);
338
339 echo '<div class="marqueex-marquee-wrapper">';
340 foreach ($testimonials as $index => $item) {
341 $this->render_single_testimonial($item, $settings, $index);
342 }
343 echo '</div>';
344 }
345
346 /**
347 * Render single testimonial
348 */
349 private function render_single_testimonial($item, $settings, $index) {
350 $rating = isset($item['rating']) ? intval($item['rating']) : 5;
351 $rating = max(1, min(5, $rating));
352 ?>
353 <div class="marqueex-item marqueex-testimonial">
354 <div class="marqueex-card">
355 <?php if (!empty($item['content'])) : ?>
356 <div class="marqueex-content">
357 <?php echo wp_kses_post(nl2br($item['content'])); ?>
358 </div>
359 <?php endif; ?>
360
361 <div class="marqueex-author">
362 <?php if (!empty($item['avatar']['url'])) : ?>
363 <div class="marqueex-avatar">
364 <img src="<?php echo esc_url($item['avatar']['url']); ?>"
365 alt="<?php echo esc_attr($item['name']); ?>"
366 loading="lazy" />
367 </div>
368 <?php endif; ?>
369
370 <div class="marqueex-author-info">
371 <?php if (!empty($item['name'])) : ?>
372 <div class="marqueex-name">
373 <?php echo esc_html($item['name']); ?>
374 </div>
375 <?php endif; ?>
376
377 <?php if (!empty($item['role'])) : ?>
378 <div class="marqueex-role">
379 <?php echo esc_html($item['role']); ?>
380 </div>
381 <?php endif; ?>
382
383 <div class="marqueex-rating">
384 <?php for ($i = 0; $i < 5; $i++) : ?>
385 <span class="marqueex-star<?php echo $i < $rating ? '' : ' inactive'; ?>">�
386 </span>
387 <?php endfor; ?>
388 </div>
389 </div>
390 </div>
391 </div>
392 </div>
393 <?php
394 }
395
396 /**
397 * Render widget output on the frontend
398 */
399 protected function render() {
400 $settings = $this->get_settings_for_display();
401 $widget_config = $this->prepare_widget_config($settings);
402
403 $this->add_render_attribute(
404 [
405 'marqueex-marquee-block' => [
406 'class' => [
407 'marqueex-marquee-block',
408 'marqueex-testimonial-marquee',
409 $settings['pause'] === 'yes' ? 'is-paused' : '',
410 $settings['reverse'] === 'yes' ? 'is-reversed' : '',
411 $settings['orientation'] === 'vertical' ? 'marqueex-marquee-vertical' : 'marqueex-marquee-horizontal',
412 $settings['mask_edges'] === 'yes' ? 'marqueex-mask-edges' : '',
413 ],
414 'data-speed' => $widget_config['speed'],
415 'role' => 'region',
416 'aria-label' => __('Testimonial Marquee', 'marqueex'),
417 ]
418 ]
419 );
420
421 ?>
422 <?php // get_render_attribute_string() returns markup already escaped by Elementor. ?>
423 <div <?php echo $this->get_render_attribute_string('marqueex-marquee-block'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
424 <div class="marqueex-marquee-track-wrapper">
425 <div class="marqueex-marquee-track">
426 <?php $this->render_testimonials_content($settings); ?>
427 </div>
428 <div aria-hidden="true" class="marqueex-marquee-track">
429 <?php $this->render_testimonials_content($settings); ?>
430 </div>
431 </div>
432 </div>
433
434 <?php
435 }
436 }
437