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 / Steps_Process_Timeline / Steps_Process_Timeline.php

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

1,606 lines 55.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Steps Process Timeline Widget.
4 *
5 * @package King_Addons
6 */
7
8 namespace King_Addons;
9
10 use Elementor\Controls_Manager;
11 use Elementor\Group_Control_Background;
12 use Elementor\Group_Control_Border;
13 use Elementor\Group_Control_Box_Shadow;
14 use Elementor\Group_Control_Typography;
15 use Elementor\Icons_Manager;
16 use Elementor\Plugin;
17 use Elementor\Repeater;
18 use Elementor\Widget_Base;
19
20 if (!defined('ABSPATH')) {
21 exit; // Exit if accessed directly.
22 }
23
24 class Steps_Process_Timeline extends Widget_Base
25 {
26 public function get_name(): string
27 {
28 return 'king-addons-steps-process-timeline';
29 }
30
31 public function get_title(): string
32 {
33 return esc_html__('Steps Process Timeline', 'king-addons');
34 }
35
36 public function get_icon(): string
37 {
38 return 'king-addons-icon king-addons-icon-steps-process-timeline';
39 }
40
41 public function get_style_depends(): array
42 {
43 return [KING_ADDONS_ASSETS_UNIQUE_KEY . '-steps-process-timeline-style'];
44 }
45
46 public function get_script_depends(): array
47 {
48 if (!$this->is_pro_enabled()) {
49 return [];
50 }
51
52 // Elementor instantiates widget types without settings when enqueueing scripts.
53 return [KING_ADDONS_ASSETS_UNIQUE_KEY . '-steps-process-timeline-script'];
54 }
55
56 public function get_categories(): array
57 {
58 return ['king-addons'];
59 }
60
61 public function get_keywords(): array
62 {
63 return ['steps', 'process', 'timeline', 'how it works', 'workflow', 'milestones', 'king-addons'];
64 }
65
66 public function get_custom_help_url()
67 {
68 return 'mailto:bug@kingaddons.com?subject=Bug Report - King Addons&body=Please describe the issue';
69 }
70
71 protected function register_controls(): void
72 {
73 $this->register_content_controls();
74 $this->register_steps_controls();
75 $this->register_interaction_controls();
76 $this->register_style_container_controls();
77 $this->register_style_heading_controls();
78 $this->register_style_steps_controls();
79 $this->register_style_marker_controls();
80 $this->register_style_line_controls();
81 $this->register_style_card_controls();
82 $this->register_style_text_controls();
83 $this->register_pro_notice_controls();
84 }
85
86 protected function register_content_controls(): void
87 {
88 $this->start_controls_section(
89 'kng_steps_section',
90 [
91 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Content', 'king-addons'),
92 'tab' => Controls_Manager::TAB_CONTENT,
93 ]
94 );
95
96 $this->add_control(
97 'kng_steps_title',
98 [
99 'label' => esc_html__('Widget Title', 'king-addons'),
100 'type' => Controls_Manager::TEXT,
101 'default' => esc_html__('How It Works', 'king-addons'),
102 'dynamic' => [
103 'active' => true,
104 ],
105 ]
106 );
107
108 $this->add_control(
109 'kng_steps_subtitle',
110 [
111 'label' => esc_html__('Subtitle', 'king-addons'),
112 'type' => Controls_Manager::TEXTAREA,
113 'rows' => 3,
114 'default' => esc_html__('Clear, simple steps that guide customers from start to finish.', 'king-addons'),
115 'dynamic' => [
116 'active' => true,
117 ],
118 ]
119 );
120
121 $this->add_control(
122 'kng_steps_layout',
123 [
124 'label' => esc_html__('Layout', 'king-addons'),
125 'type' => Controls_Manager::SELECT,
126 'default' => 'vertical',
127 'options' => [
128 'vertical' => esc_html__('Vertical', 'king-addons'),
129 'vertical-compact' => esc_html__('Vertical Compact', 'king-addons'),
130 'horizontal' => esc_html__('Horizontal (Pro)', 'king-addons'),
131 ],
132 'prefix_class' => 'king-addons-steps-layout-',
133 'render_type' => 'template',
134 'separator' => 'before',
135 ]
136 );
137
138 Core::renderUpgradeProNotice($this, Controls_Manager::RAW_HTML, 'steps-process-timeline', 'kng_steps_layout', ['horizontal']);
139
140 $this->add_control(
141 'kng_steps_numbering_mode',
142 [
143 'label' => esc_html__('Step Numbering', 'king-addons'),
144 'type' => Controls_Manager::SELECT,
145 'default' => 'auto',
146 'options' => [
147 'auto' => esc_html__('Auto', 'king-addons'),
148 'manual' => esc_html__('Manual', 'king-addons'),
149 ],
150 'separator' => 'before',
151 ]
152 );
153
154 $this->add_control(
155 'kng_steps_number_format',
156 [
157 'label' => esc_html__('Number Format', 'king-addons'),
158 'type' => Controls_Manager::SELECT,
159 'default' => 'default',
160 'options' => [
161 'default' => esc_html__('1, 2, 3', 'king-addons'),
162 'leading' => esc_html__('01, 02, 03 (Pro)', 'king-addons'),
163 ],
164 ]
165 );
166
167 Core::renderUpgradeProNotice($this, Controls_Manager::RAW_HTML, 'steps-process-timeline', 'kng_steps_number_format', ['leading']);
168
169 $this->add_control(
170 'kng_steps_number_prefix',
171 [
172 'label' => esc_html__('Number Prefix (Pro)', 'king-addons'),
173 'type' => Controls_Manager::TEXT,
174 'default' => '',
175 'classes' => $this->get_pro_control_class(),
176 ]
177 );
178
179 $this->add_control(
180 'kng_steps_number_suffix',
181 [
182 'label' => esc_html__('Number Suffix (Pro)', 'king-addons'),
183 'type' => Controls_Manager::TEXT,
184 'default' => '',
185 'classes' => $this->get_pro_control_class(),
186 ]
187 );
188
189 $this->add_control(
190 'kng_steps_marker_type',
191 [
192 'label' => esc_html__('Marker Type', 'king-addons'),
193 'type' => Controls_Manager::SELECT,
194 'default' => 'number',
195 'options' => [
196 'number' => esc_html__('Number', 'king-addons'),
197 'icon' => esc_html__('Icon', 'king-addons'),
198 'number-icon' => esc_html__('Number + Icon (Pro)', 'king-addons'),
199 ],
200 'prefix_class' => 'king-addons-steps-marker-',
201 'render_type' => 'template',
202 ]
203 );
204
205 Core::renderUpgradeProNotice($this, Controls_Manager::RAW_HTML, 'steps-process-timeline', 'kng_steps_marker_type', ['number-icon']);
206
207 $this->add_control(
208 'kng_steps_line_position',
209 [
210 'label' => esc_html__('Line Position', 'king-addons'),
211 'type' => Controls_Manager::SELECT,
212 'default' => 'left',
213 'options' => [
214 'left' => esc_html__('Left', 'king-addons'),
215 'center' => esc_html__('Center', 'king-addons'),
216 ],
217 'prefix_class' => 'king-addons-steps-line-',
218 'render_type' => 'template',
219 ]
220 );
221
222 $this->add_control(
223 'kng_steps_alignment',
224 [
225 'label' => esc_html__('Content Alignment', 'king-addons'),
226 'type' => Controls_Manager::CHOOSE,
227 'default' => 'left',
228 'options' => [
229 'left' => [
230 'title' => esc_html__('Left', 'king-addons'),
231 'icon' => 'eicon-text-align-left',
232 ],
233 'center' => [
234 'title' => esc_html__('Center', 'king-addons'),
235 'icon' => 'eicon-text-align-center',
236 ],
237 'right' => [
238 'title' => esc_html__('Right', 'king-addons'),
239 'icon' => 'eicon-text-align-right',
240 ],
241 ],
242 'prefix_class' => 'king-addons-steps-align-',
243 'render_type' => 'template',
244 ]
245 );
246
247 $this->add_control(
248 'kng_steps_card',
249 [
250 'label' => esc_html__('Step Cards', 'king-addons'),
251 'type' => Controls_Manager::SWITCHER,
252 'return_value' => 'yes',
253 'default' => 'yes',
254 'prefix_class' => 'king-addons-steps-card-',
255 'render_type' => 'template',
256 ]
257 );
258
259 $this->add_control(
260 'kng_steps_breakpoint',
261 [
262 'label' => esc_html__('Stacking on Tablet', 'king-addons'),
263 'type' => Controls_Manager::SELECT,
264 'default' => 'tablet',
265 'options' => [
266 'tablet' => esc_html__('Switch to vertical on tablet', 'king-addons'),
267 'mobile' => esc_html__('Switch to vertical only on mobile', 'king-addons'),
268 ],
269 'prefix_class' => 'king-addons-steps-stack-',
270 'render_type' => 'template',
271 ]
272 );
273
274 $this->end_controls_section();
275 }
276
277 protected function register_steps_controls(): void
278 {
279 $this->start_controls_section(
280 'kng_steps_list_section',
281 [
282 'label' => KING_ADDONS_ELEMENTOR_ICON . esc_html__('Steps', 'king-addons'),
283 'tab' => Controls_Manager::TAB_CONTENT,
284 ]
285 );
286
287 $repeater = new Repeater();
288
289 $repeater->add_control(
290 'step_title',
291 [
292 'label' => esc_html__('Title', 'king-addons'),
293 'type' => Controls_Manager::TEXT,
294 'default' => esc_html__('Step Title', 'king-addons'),
295 'label_block' => true,
296 ]
297 );
298
299 $repeater->add_control(
300 'step_description',
301 [
302 'label' => esc_html__('Description', 'king-addons'),
303 'type' => Controls_Manager::TEXTAREA,
304 'rows' => 4,
305 'default' => esc_html__('Explain what happens during this step in a clear, concise way.', 'king-addons'),
306 ]
307 );
308
309 $repeater->add_control(
310 'step_icon',
311 [
312 'label' => esc_html__('Icon', 'king-addons'),
313 'type' => Controls_Manager::ICONS,
314 'skin' => 'inline',
315 'label_block' => true,
316 ]
317 );
318
319 $repeater->add_control(
320 'step_number',
321 [
322 'label' => esc_html__('Manual Number', 'king-addons'),
323 'type' => Controls_Manager::TEXT,
324 'condition' => [
325 'kng_steps_numbering_mode' => 'manual',
326 ],
327 ]
328 );
329
330 $repeater->add_control(
331 'step_cta_text',
332 [
333 'label' => esc_html__('CTA Label', 'king-addons'),
334 'type' => Controls_Manager::TEXT,
335 'default' => '',
336 'separator' => 'before',
337 ]
338 );
339
340 $repeater->add_control(
341 'step_link',
342 [
343 'label' => esc_html__('CTA Link', 'king-addons'),
344 'type' => Controls_Manager::URL,
345 'placeholder' => esc_html__('https://your-link.com', 'king-addons'),
346 ]
347 );
348
349 $repeater->add_control(
350 'step_anchor_id',
351 [
352 'label' => esc_html__('Anchor ID (Pro)', 'king-addons'),
353 'type' => Controls_Manager::TEXT,
354 'classes' => $this->get_pro_control_class(),
355 'description' => esc_html__('Use lowercase letters, numbers, dashes, or underscores only.', 'king-addons'),
356 ]
357 );
358
359 $repeater->add_control(
360 'step_state',
361 [
362 'label' => esc_html__('State (Pro)', 'king-addons'),
363 'type' => Controls_Manager::SELECT,
364 'default' => 'default',
365 'options' => [
366 'default' => esc_html__('Default', 'king-addons'),
367 'highlighted' => esc_html__('Highlighted', 'king-addons'),
368 'disabled' => esc_html__('Disabled', 'king-addons'),
369 ],
370 'classes' => $this->get_pro_control_class(),
371 ]
372 );
373
374 $this->add_control(
375 'kng_steps_list',
376 [
377 'label' => esc_html__('Steps', 'king-addons'),
378 'type' => Controls_Manager::REPEATER,
379 'fields' => $repeater->get_controls(),
380 'default' => $this->get_default_steps(),
381 'title_field' => '{{ step_title }}',
382 ]
383 );
384
385 $this->end_controls_section();
386 }
387
388 protected function register_interaction_controls(): void
389 {
390 $this->start_controls_section(
391 'kng_steps_interaction_section',
392 [
393 'label' => KING_ADDONS_ELEMENTOR_ICON_PRO . esc_html__('Interactions (Pro)', 'king-addons'),
394 'tab' => Controls_Manager::TAB_CONTENT,
395 ]
396 );
397
398 $this->add_control(
399 'kng_steps_active_on_scroll',
400 [
401 'label' => esc_html__('Active Step on Scroll (Pro)', 'king-addons'),
402 'type' => Controls_Manager::SWITCHER,
403 'return_value' => 'yes',
404 ]
405 );
406
407 Core::renderUpgradeProNotice($this, Controls_Manager::RAW_HTML, 'steps-process-timeline', 'kng_steps_active_on_scroll', ['yes']);
408
409 $this->add_control(
410 'kng_steps_anchor_linking',
411 [
412 'label' => esc_html__('Enable Anchor Linking (Pro)', 'king-addons'),
413 'type' => Controls_Manager::SWITCHER,
414 'return_value' => 'yes',
415 'separator' => 'before',
416 ]
417 );
418
419 Core::renderUpgradeProNotice($this, Controls_Manager::RAW_HTML, 'steps-process-timeline', 'kng_steps_anchor_linking', ['yes']);
420
421 $this->add_control(
422 'kng_steps_anchor_sync',
423 [
424 'label' => esc_html__('Sync Active Step with Anchors (Pro)', 'king-addons'),
425 'type' => Controls_Manager::SWITCHER,
426 'return_value' => 'yes',
427 'condition' => [
428 'kng_steps_anchor_linking' => 'yes',
429 ],
430 ]
431 );
432
433 Core::renderUpgradeProNotice($this, Controls_Manager::RAW_HTML, 'steps-process-timeline', 'kng_steps_anchor_sync', ['yes']);
434
435 $this->add_control(
436 'kng_steps_anchor_offset',
437 [
438 'label' => esc_html__('Anchor Offset (px) (Pro)', 'king-addons'),
439 'type' => Controls_Manager::NUMBER,
440 'default' => 0,
441 'condition' => [
442 'kng_steps_anchor_linking' => 'yes',
443 ],
444 ]
445 );
446
447 $this->add_control(
448 'kng_steps_deep_link',
449 [
450 'label' => esc_html__('Enable Deep Linking (Pro)', 'king-addons'),
451 'type' => Controls_Manager::SWITCHER,
452 'return_value' => 'yes',
453 'condition' => [
454 'kng_steps_anchor_linking' => 'yes',
455 ],
456 ]
457 );
458
459 Core::renderUpgradeProNotice($this, Controls_Manager::RAW_HTML, 'steps-process-timeline', 'kng_steps_deep_link', ['yes']);
460
461 $this->add_control(
462 'kng_steps_sticky_progress',
463 [
464 'label' => esc_html__('Enable Sticky Progress (Pro)', 'king-addons'),
465 'type' => Controls_Manager::SWITCHER,
466 'return_value' => 'yes',
467 'separator' => 'before',
468 ]
469 );
470
471 Core::renderUpgradeProNotice($this, Controls_Manager::RAW_HTML, 'steps-process-timeline', 'kng_steps_sticky_progress', ['yes']);
472
473 $this->add_control(
474 'kng_steps_sticky_offset',
475 [
476 'label' => esc_html__('Sticky Offset (px) (Pro)', 'king-addons'),
477 'type' => Controls_Manager::NUMBER,
478 'default' => 0,
479 'condition' => [
480 'kng_steps_sticky_progress' => 'yes',
481 ],
482 ]
483 );
484
485 $this->add_control(
486 'kng_steps_progress_mode',
487 [
488 'label' => esc_html__('Progress Calculation (Pro)', 'king-addons'),
489 'type' => Controls_Manager::SELECT,
490 'default' => 'steps',
491 'options' => [
492 'steps' => esc_html__('First to Last Step', 'king-addons'),
493 'widget' => esc_html__('Widget Top to Bottom', 'king-addons'),
494 ],
495 ]
496 );
497
498 $this->add_control(
499 'kng_steps_reveal',
500 [
501 'label' => esc_html__('Enable Reveal Animations (Pro)', 'king-addons'),
502 'type' => Controls_Manager::SWITCHER,
503 'return_value' => 'yes',
504 'separator' => 'before',
505 ]
506 );
507
508 Core::renderUpgradeProNotice($this, Controls_Manager::RAW_HTML, 'steps-process-timeline', 'kng_steps_reveal', ['yes']);
509
510 $this->add_control(
511 'kng_steps_reveal_type',
512 [
513 'label' => esc_html__('Reveal Type (Pro)', 'king-addons'),
514 'type' => Controls_Manager::SELECT,
515 'default' => 'fade-up',
516 'options' => [
517 'fade-up' => esc_html__('Fade Up', 'king-addons'),
518 'fade-in' => esc_html__('Fade In', 'king-addons'),
519 'slide-up' => esc_html__('Slide Up', 'king-addons'),
520 'scale-in' => esc_html__('Scale In', 'king-addons'),
521 ],
522 'condition' => [
523 'kng_steps_reveal' => 'yes',
524 ],
525 ]
526 );
527
528 $this->add_control(
529 'kng_steps_reveal_stagger',
530 [
531 'label' => esc_html__('Stagger Delay (ms) (Pro)', 'king-addons'),
532 'type' => Controls_Manager::NUMBER,
533 'default' => 120,
534 'condition' => [
535 'kng_steps_reveal' => 'yes',
536 ],
537 ]
538 );
539
540 $this->add_control(
541 'kng_steps_reveal_trigger',
542 [
543 'label' => esc_html__('Animation Trigger (Pro)', 'king-addons'),
544 'type' => Controls_Manager::SELECT,
545 'default' => 'viewport',
546 'options' => [
547 'viewport' => esc_html__('On Enter Viewport', 'king-addons'),
548 'active' => esc_html__('On Active Step', 'king-addons'),
549 ],
550 'condition' => [
551 'kng_steps_reveal' => 'yes',
552 ],
553 ]
554 );
555
556 $this->add_control(
557 'kng_steps_horizontal_wrap',
558 [
559 'label' => esc_html__('Wrap Steps into Rows (Pro)', 'king-addons'),
560 'type' => Controls_Manager::SWITCHER,
561 'return_value' => 'yes',
562 'condition' => [
563 'kng_steps_layout' => 'horizontal',
564 ],
565 ]
566 );
567
568 Core::renderUpgradeProNotice($this, Controls_Manager::RAW_HTML, 'steps-process-timeline', 'kng_steps_horizontal_wrap', ['yes']);
569
570 $this->end_controls_section();
571 }
572
573 protected function register_style_container_controls(): void
574 {
575 $this->start_controls_section(
576 'kng_steps_style_container',
577 [
578 'label' => esc_html__('Container', 'king-addons'),
579 'tab' => Controls_Manager::TAB_STYLE,
580 ]
581 );
582
583 $this->add_group_control(
584 Group_Control_Background::get_type(),
585 [
586 'name' => 'kng_steps_container_bg',
587 'selector' => '{{WRAPPER}} .king-addons-steps-timeline',
588 ]
589 );
590
591 $this->add_group_control(
592 Group_Control_Border::get_type(),
593 [
594 'name' => 'kng_steps_container_border',
595 'selector' => '{{WRAPPER}} .king-addons-steps-timeline',
596 ]
597 );
598
599 $this->add_control(
600 'kng_steps_container_radius',
601 [
602 'label' => esc_html__('Border Radius', 'king-addons'),
603 'type' => Controls_Manager::DIMENSIONS,
604 'size_units' => ['px', '%'],
605 'selectors' => [
606 '{{WRAPPER}} .king-addons-steps-timeline' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
607 ],
608 ]
609 );
610
611 $this->add_group_control(
612 Group_Control_Box_Shadow::get_type(),
613 [
614 'name' => 'kng_steps_container_shadow',
615 'selector' => '{{WRAPPER}} .king-addons-steps-timeline',
616 ]
617 );
618
619 $this->add_responsive_control(
620 'kng_steps_container_padding',
621 [
622 'label' => esc_html__('Padding', 'king-addons'),
623 'type' => Controls_Manager::DIMENSIONS,
624 'size_units' => ['px', 'em', '%'],
625 'selectors' => [
626 '{{WRAPPER}} .king-addons-steps-timeline' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
627 ],
628 ]
629 );
630
631 $this->end_controls_section();
632 }
633
634 protected function register_style_heading_controls(): void
635 {
636 $this->start_controls_section(
637 'kng_steps_style_heading',
638 [
639 'label' => esc_html__('Heading', 'king-addons'),
640 'tab' => Controls_Manager::TAB_STYLE,
641 ]
642 );
643
644 $this->add_group_control(
645 Group_Control_Typography::get_type(),
646 [
647 'name' => 'kng_steps_title_typography',
648 'selector' => '{{WRAPPER}} .king-addons-steps__title',
649 ]
650 );
651
652 $this->add_control(
653 'kng_steps_title_color',
654 [
655 'label' => esc_html__('Title Color', 'king-addons'),
656 'type' => Controls_Manager::COLOR,
657 'selectors' => [
658 '{{WRAPPER}} .king-addons-steps__title' => 'color: {{VALUE}};',
659 ],
660 ]
661 );
662
663 $this->add_group_control(
664 Group_Control_Typography::get_type(),
665 [
666 'name' => 'kng_steps_subtitle_typography',
667 'selector' => '{{WRAPPER}} .king-addons-steps__subtitle',
668 ]
669 );
670
671 $this->add_control(
672 'kng_steps_subtitle_color',
673 [
674 'label' => esc_html__('Subtitle Color', 'king-addons'),
675 'type' => Controls_Manager::COLOR,
676 'selectors' => [
677 '{{WRAPPER}} .king-addons-steps__subtitle' => 'color: {{VALUE}};',
678 ],
679 ]
680 );
681
682 $this->add_responsive_control(
683 'kng_steps_heading_gap',
684 [
685 'label' => esc_html__('Heading Spacing', 'king-addons'),
686 'type' => Controls_Manager::SLIDER,
687 'size_units' => ['px', 'em'],
688 'range' => [
689 'px' => ['min' => 0, 'max' => 80],
690 ],
691 'selectors' => [
692 '{{WRAPPER}} .king-addons-steps__heading' => 'margin-bottom: {{SIZE}}{{UNIT}};',
693 ],
694 ]
695 );
696
697 $this->end_controls_section();
698 }
699
700 protected function register_style_steps_controls(): void
701 {
702 $this->start_controls_section(
703 'kng_steps_style_steps',
704 [
705 'label' => esc_html__('Steps', 'king-addons'),
706 'tab' => Controls_Manager::TAB_STYLE,
707 ]
708 );
709
710 $this->add_responsive_control(
711 'kng_steps_gap',
712 [
713 'label' => esc_html__('Gap Between Steps', 'king-addons'),
714 'type' => Controls_Manager::SLIDER,
715 'size_units' => ['px', 'em'],
716 'range' => [
717 'px' => ['min' => 0, 'max' => 80],
718 ],
719 'selectors' => [
720 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-gap: {{SIZE}}{{UNIT}};',
721 ],
722 ]
723 );
724
725 $this->add_responsive_control(
726 'kng_steps_marker_gap',
727 [
728 'label' => esc_html__('Marker to Content Gap', 'king-addons'),
729 'type' => Controls_Manager::SLIDER,
730 'size_units' => ['px', 'em'],
731 'range' => [
732 'px' => ['min' => 0, 'max' => 60],
733 ],
734 'selectors' => [
735 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-marker-gap: {{SIZE}}{{UNIT}};',
736 ],
737 ]
738 );
739
740 $this->end_controls_section();
741 }
742
743 protected function register_style_marker_controls(): void
744 {
745 $this->start_controls_section(
746 'kng_steps_style_marker',
747 [
748 'label' => esc_html__('Marker', 'king-addons'),
749 'tab' => Controls_Manager::TAB_STYLE,
750 ]
751 );
752
753 $this->add_responsive_control(
754 'kng_steps_marker_size',
755 [
756 'label' => esc_html__('Marker Size', 'king-addons'),
757 'type' => Controls_Manager::SLIDER,
758 'size_units' => ['px'],
759 'range' => [
760 'px' => ['min' => 24, 'max' => 120],
761 ],
762 'selectors' => [
763 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-marker-size: {{SIZE}}{{UNIT}};',
764 ],
765 ]
766 );
767
768 $this->add_responsive_control(
769 'kng_steps_icon_size',
770 [
771 'label' => esc_html__('Icon Size', 'king-addons'),
772 'type' => Controls_Manager::SLIDER,
773 'size_units' => ['px'],
774 'range' => [
775 'px' => ['min' => 10, 'max' => 60],
776 ],
777 'selectors' => [
778 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-icon-size: {{SIZE}}{{UNIT}};',
779 ],
780 ]
781 );
782
783 $this->add_control(
784 'kng_steps_marker_bg',
785 [
786 'label' => esc_html__('Background', 'king-addons'),
787 'type' => Controls_Manager::COLOR,
788 'selectors' => [
789 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-marker-bg: {{VALUE}};',
790 ],
791 ]
792 );
793
794 $this->add_control(
795 'kng_steps_marker_color',
796 [
797 'label' => esc_html__('Text/Icon Color', 'king-addons'),
798 'type' => Controls_Manager::COLOR,
799 'selectors' => [
800 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-marker-color: {{VALUE}};',
801 ],
802 ]
803 );
804
805 $this->add_control(
806 'kng_steps_marker_border_color',
807 [
808 'label' => esc_html__('Border Color', 'king-addons'),
809 'type' => Controls_Manager::COLOR,
810 'selectors' => [
811 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-marker-border-color: {{VALUE}};',
812 ],
813 ]
814 );
815
816 $this->add_control(
817 'kng_steps_marker_border_width',
818 [
819 'label' => esc_html__('Border Width', 'king-addons'),
820 'type' => Controls_Manager::SLIDER,
821 'size_units' => ['px'],
822 'range' => [
823 'px' => ['min' => 0, 'max' => 6],
824 ],
825 'selectors' => [
826 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-marker-border-width: {{SIZE}}{{UNIT}};',
827 ],
828 ]
829 );
830
831 $this->add_control(
832 'kng_steps_marker_active_bg',
833 [
834 'label' => esc_html__('Active Background (Pro)', 'king-addons'),
835 'type' => Controls_Manager::COLOR,
836 'selectors' => [
837 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-marker-bg-active: {{VALUE}};',
838 ],
839 'classes' => $this->get_pro_control_class(),
840 ]
841 );
842
843 $this->add_control(
844 'kng_steps_marker_active_color',
845 [
846 'label' => esc_html__('Active Text/Icon Color (Pro)', 'king-addons'),
847 'type' => Controls_Manager::COLOR,
848 'selectors' => [
849 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-marker-color-active: {{VALUE}};',
850 ],
851 'classes' => $this->get_pro_control_class(),
852 ]
853 );
854
855 $this->add_control(
856 'kng_steps_marker_active_border_color',
857 [
858 'label' => esc_html__('Active Border Color (Pro)', 'king-addons'),
859 'type' => Controls_Manager::COLOR,
860 'selectors' => [
861 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-marker-border-color-active: {{VALUE}};',
862 ],
863 'classes' => $this->get_pro_control_class(),
864 ]
865 );
866
867 $this->add_control(
868 'kng_steps_marker_complete_bg',
869 [
870 'label' => esc_html__('Completed Background (Pro)', 'king-addons'),
871 'type' => Controls_Manager::COLOR,
872 'selectors' => [
873 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-marker-bg-complete: {{VALUE}};',
874 ],
875 'classes' => $this->get_pro_control_class(),
876 ]
877 );
878
879 $this->add_control(
880 'kng_steps_marker_complete_color',
881 [
882 'label' => esc_html__('Completed Text/Icon Color (Pro)', 'king-addons'),
883 'type' => Controls_Manager::COLOR,
884 'selectors' => [
885 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-marker-color-complete: {{VALUE}};',
886 ],
887 'classes' => $this->get_pro_control_class(),
888 ]
889 );
890
891 $this->end_controls_section();
892 }
893
894 protected function register_style_line_controls(): void
895 {
896 $this->start_controls_section(
897 'kng_steps_style_line',
898 [
899 'label' => esc_html__('Line & Progress', 'king-addons'),
900 'tab' => Controls_Manager::TAB_STYLE,
901 ]
902 );
903
904 $this->add_control(
905 'kng_steps_line_color',
906 [
907 'label' => esc_html__('Line Color', 'king-addons'),
908 'type' => Controls_Manager::COLOR,
909 'selectors' => [
910 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-line-color: {{VALUE}};',
911 ],
912 ]
913 );
914
915 $this->add_control(
916 'kng_steps_line_thickness',
917 [
918 'label' => esc_html__('Line Thickness', 'king-addons'),
919 'type' => Controls_Manager::SLIDER,
920 'size_units' => ['px'],
921 'range' => [
922 'px' => ['min' => 1, 'max' => 10],
923 ],
924 'selectors' => [
925 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-line-thickness: {{SIZE}}{{UNIT}};',
926 ],
927 ]
928 );
929
930 $this->add_control(
931 'kng_steps_line_style',
932 [
933 'label' => esc_html__('Line Style', 'king-addons'),
934 'type' => Controls_Manager::SELECT,
935 'default' => 'solid',
936 'options' => [
937 'solid' => esc_html__('Solid', 'king-addons'),
938 'dashed' => esc_html__('Dashed', 'king-addons'),
939 'gradient' => esc_html__('Gradient (Pro)', 'king-addons'),
940 ],
941 'prefix_class' => 'king-addons-steps-line-style-',
942 'render_type' => 'template',
943 ]
944 );
945
946 Core::renderUpgradeProNotice($this, Controls_Manager::RAW_HTML, 'steps-process-timeline', 'kng_steps_line_style', ['gradient']);
947
948 $this->add_control(
949 'kng_steps_progress_color',
950 [
951 'label' => esc_html__('Progress Fill Color (Pro)', 'king-addons'),
952 'type' => Controls_Manager::COLOR,
953 'selectors' => [
954 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-progress-color: {{VALUE}};',
955 ],
956 'classes' => $this->get_pro_control_class(),
957 ]
958 );
959
960 $this->add_control(
961 'kng_steps_progress_radius',
962 [
963 'label' => esc_html__('Rounded Edges (Pro)', 'king-addons'),
964 'type' => Controls_Manager::SLIDER,
965 'size_units' => ['px'],
966 'range' => [
967 'px' => ['min' => 0, 'max' => 20],
968 ],
969 'selectors' => [
970 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-progress-radius: {{SIZE}}{{UNIT}};',
971 ],
972 'classes' => $this->get_pro_control_class(),
973 ]
974 );
975
976 $this->end_controls_section();
977 }
978
979 protected function register_style_card_controls(): void
980 {
981 $this->start_controls_section(
982 'kng_steps_style_card',
983 [
984 'label' => esc_html__('Card', 'king-addons'),
985 'tab' => Controls_Manager::TAB_STYLE,
986 'condition' => [
987 'kng_steps_card' => 'yes',
988 ],
989 ]
990 );
991
992 $this->add_group_control(
993 Group_Control_Background::get_type(),
994 [
995 'name' => 'kng_steps_card_bg',
996 'selector' => '{{WRAPPER}} .king-addons-step__content',
997 ]
998 );
999
1000 $this->add_group_control(
1001 Group_Control_Border::get_type(),
1002 [
1003 'name' => 'kng_steps_card_border',
1004 'selector' => '{{WRAPPER}} .king-addons-step__content',
1005 ]
1006 );
1007
1008 $this->add_group_control(
1009 Group_Control_Box_Shadow::get_type(),
1010 [
1011 'name' => 'kng_steps_card_shadow',
1012 'selector' => '{{WRAPPER}} .king-addons-step__content',
1013 ]
1014 );
1015
1016 $this->add_responsive_control(
1017 'kng_steps_card_padding',
1018 [
1019 'label' => esc_html__('Padding', 'king-addons'),
1020 'type' => Controls_Manager::DIMENSIONS,
1021 'size_units' => ['px', 'em'],
1022 'selectors' => [
1023 '{{WRAPPER}} .king-addons-step__content' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1024 ],
1025 ]
1026 );
1027
1028 $this->add_control(
1029 'kng_steps_card_radius',
1030 [
1031 'label' => esc_html__('Border Radius', 'king-addons'),
1032 'type' => Controls_Manager::DIMENSIONS,
1033 'size_units' => ['px', '%'],
1034 'selectors' => [
1035 '{{WRAPPER}} .king-addons-step__content' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1036 ],
1037 ]
1038 );
1039
1040 $this->add_control(
1041 'kng_steps_card_active_border_color',
1042 [
1043 'label' => esc_html__('Active Border Color (Pro)', 'king-addons'),
1044 'type' => Controls_Manager::COLOR,
1045 'selectors' => [
1046 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-card-active-border-color: {{VALUE}};',
1047 ],
1048 'classes' => $this->get_pro_control_class(),
1049 ]
1050 );
1051
1052 $this->add_control(
1053 'kng_steps_card_active_shadow_color',
1054 [
1055 'label' => esc_html__('Active Shadow Color (Pro)', 'king-addons'),
1056 'type' => Controls_Manager::COLOR,
1057 'selectors' => [
1058 '{{WRAPPER}} .king-addons-steps-timeline' => '--kng-steps-card-active-shadow-color: {{VALUE}};',
1059 ],
1060 'classes' => $this->get_pro_control_class(),
1061 ]
1062 );
1063
1064 $this->end_controls_section();
1065 }
1066
1067 protected function register_style_text_controls(): void
1068 {
1069 $this->start_controls_section(
1070 'kng_steps_style_text',
1071 [
1072 'label' => esc_html__('Text', 'king-addons'),
1073 'tab' => Controls_Manager::TAB_STYLE,
1074 ]
1075 );
1076
1077 $this->add_group_control(
1078 Group_Control_Typography::get_type(),
1079 [
1080 'name' => 'kng_steps_step_title_typography',
1081 'selector' => '{{WRAPPER}} .king-addons-step__title',
1082 ]
1083 );
1084
1085 $this->add_control(
1086 'kng_steps_step_title_color',
1087 [
1088 'label' => esc_html__('Step Title Color', 'king-addons'),
1089 'type' => Controls_Manager::COLOR,
1090 'selectors' => [
1091 '{{WRAPPER}} .king-addons-step__title' => 'color: {{VALUE}};',
1092 ],
1093 ]
1094 );
1095
1096 $this->add_group_control(
1097 Group_Control_Typography::get_type(),
1098 [
1099 'name' => 'kng_steps_step_desc_typography',
1100 'selector' => '{{WRAPPER}} .king-addons-step__description',
1101 ]
1102 );
1103
1104 $this->add_control(
1105 'kng_steps_step_desc_color',
1106 [
1107 'label' => esc_html__('Step Description Color', 'king-addons'),
1108 'type' => Controls_Manager::COLOR,
1109 'selectors' => [
1110 '{{WRAPPER}} .king-addons-step__description' => 'color: {{VALUE}};',
1111 ],
1112 ]
1113 );
1114
1115 $this->add_group_control(
1116 Group_Control_Typography::get_type(),
1117 [
1118 'name' => 'kng_steps_step_number_typography',
1119 'selector' => '{{WRAPPER}} .king-addons-step__number',
1120 ]
1121 );
1122
1123 $this->add_control(
1124 'kng_steps_step_number_color',
1125 [
1126 'label' => esc_html__('Step Number Color', 'king-addons'),
1127 'type' => Controls_Manager::COLOR,
1128 'selectors' => [
1129 '{{WRAPPER}} .king-addons-step__number' => 'color: {{VALUE}};',
1130 ],
1131 ]
1132 );
1133
1134 $this->add_group_control(
1135 Group_Control_Typography::get_type(),
1136 [
1137 'name' => 'kng_steps_cta_typography',
1138 'selector' => '{{WRAPPER}} .king-addons-step__cta',
1139 ]
1140 );
1141
1142 $this->add_control(
1143 'kng_steps_cta_color',
1144 [
1145 'label' => esc_html__('CTA Color', 'king-addons'),
1146 'type' => Controls_Manager::COLOR,
1147 'selectors' => [
1148 '{{WRAPPER}} .king-addons-step__cta' => 'color: {{VALUE}};',
1149 ],
1150 ]
1151 );
1152
1153 $this->add_control(
1154 'kng_steps_cta_hover_color',
1155 [
1156 'label' => esc_html__('CTA Hover Color', 'king-addons'),
1157 'type' => Controls_Manager::COLOR,
1158 'selectors' => [
1159 '{{WRAPPER}} .king-addons-step__cta:hover' => 'color: {{VALUE}};',
1160 ],
1161 ]
1162 );
1163
1164 $this->add_control(
1165 'kng_steps_cta_bg',
1166 [
1167 'label' => esc_html__('CTA Background', 'king-addons'),
1168 'type' => Controls_Manager::COLOR,
1169 'selectors' => [
1170 '{{WRAPPER}} .king-addons-step__cta' => 'background-color: {{VALUE}};',
1171 ],
1172 ]
1173 );
1174
1175 $this->add_control(
1176 'kng_steps_cta_hover_bg',
1177 [
1178 'label' => esc_html__('CTA Hover Background', 'king-addons'),
1179 'type' => Controls_Manager::COLOR,
1180 'selectors' => [
1181 '{{WRAPPER}} .king-addons-step__cta:hover' => 'background-color: {{VALUE}};',
1182 ],
1183 ]
1184 );
1185
1186 $this->add_group_control(
1187 Group_Control_Border::get_type(),
1188 [
1189 'name' => 'kng_steps_cta_border',
1190 'selector' => '{{WRAPPER}} .king-addons-step__cta',
1191 ]
1192 );
1193
1194 $this->add_control(
1195 'kng_steps_cta_hover_border_color',
1196 [
1197 'label' => esc_html__('CTA Hover Border Color', 'king-addons'),
1198 'type' => Controls_Manager::COLOR,
1199 'selectors' => [
1200 '{{WRAPPER}} .king-addons-step__cta:hover' => 'border-color: {{VALUE}};',
1201 ],
1202 ]
1203 );
1204
1205 $this->add_control(
1206 'kng_steps_cta_radius',
1207 [
1208 'label' => esc_html__('CTA Border Radius', 'king-addons'),
1209 'type' => Controls_Manager::DIMENSIONS,
1210 'size_units' => ['px', '%'],
1211 'selectors' => [
1212 '{{WRAPPER}} .king-addons-step__cta' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1213 ],
1214 ]
1215 );
1216
1217 $this->add_responsive_control(
1218 'kng_steps_cta_padding',
1219 [
1220 'label' => esc_html__('CTA Padding', 'king-addons'),
1221 'type' => Controls_Manager::DIMENSIONS,
1222 'size_units' => ['px', 'em'],
1223 'selectors' => [
1224 '{{WRAPPER}} .king-addons-step__cta' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
1225 ],
1226 ]
1227 );
1228
1229 $this->end_controls_section();
1230 }
1231
1232 protected function register_pro_notice_controls(): void
1233 {
1234 Core::renderProFeaturesSection($this, '', Controls_Manager::RAW_HTML, 'steps-process-timeline', [
1235 'Horizontal timeline layout with progress line',
1236 'Active step sync with scroll and anchors',
1237 'Sticky progress bar with offset control',
1238 'Reveal animations with stagger and triggers',
1239 'Advanced numbering formats and prefixes',
1240 ]);
1241 }
1242
1243 protected function render(): void
1244 {
1245 $settings = $this->get_settings_for_display();
1246 $steps = $settings['kng_steps_list'] ?? [];
1247
1248 if (empty($steps)) {
1249 return;
1250 }
1251
1252 $is_pro = $this->is_pro_enabled();
1253 $layout = $settings['kng_steps_layout'] ?? 'vertical';
1254 if (!$is_pro && 'horizontal' === $layout) {
1255 $layout = 'vertical';
1256 }
1257
1258 $marker_type = $settings['kng_steps_marker_type'] ?? 'number';
1259 if (!$is_pro && 'number-icon' === $marker_type) {
1260 $marker_type = 'number';
1261 }
1262
1263 $use_templates = false;
1264
1265 $wrapper_classes = [
1266 'king-addons-steps-timeline',
1267 'king-addons-steps-layout-' . $layout,
1268 'king-addons-steps-marker-' . $marker_type,
1269 ];
1270
1271 if ($is_pro) {
1272 $wrapper_classes[] = 'king-addons-steps--pro';
1273 $this->add_render_attribute('_wrapper', 'class', 'king-addons-steps--pro');
1274 }
1275
1276 if (!empty($settings['kng_steps_line_position'])) {
1277 $wrapper_classes[] = 'king-addons-steps-line-' . $settings['kng_steps_line_position'];
1278 }
1279
1280 if (!empty($settings['kng_steps_card']) && 'yes' === $settings['kng_steps_card']) {
1281 $wrapper_classes[] = 'king-addons-steps--card';
1282 }
1283
1284 if ($is_pro && !empty($settings['kng_steps_sticky_progress']) && 'yes' === $settings['kng_steps_sticky_progress']) {
1285 $wrapper_classes[] = 'king-addons-steps--sticky';
1286 }
1287
1288 if ($is_pro && 'horizontal' === $layout && !empty($settings['kng_steps_horizontal_wrap']) && 'yes' === $settings['kng_steps_horizontal_wrap']) {
1289 $wrapper_classes[] = 'king-addons-steps--wrap';
1290 }
1291
1292 $anchor_linking_enabled = $is_pro && !empty($settings['kng_steps_anchor_linking']) && 'yes' === $settings['kng_steps_anchor_linking'];
1293
1294 $options = [
1295 'layout' => $layout,
1296 'activeOnScroll' => $is_pro && !empty($settings['kng_steps_active_on_scroll']) && 'yes' === $settings['kng_steps_active_on_scroll'],
1297 'anchorLinking' => $anchor_linking_enabled,
1298 'anchorSync' => $is_pro && !empty($settings['kng_steps_anchor_sync']) && 'yes' === $settings['kng_steps_anchor_sync'],
1299 'anchorOffset' => $is_pro ? (int) ($settings['kng_steps_anchor_offset'] ?? 0) : 0,
1300 'deepLink' => $is_pro && !empty($settings['kng_steps_deep_link']) && 'yes' === $settings['kng_steps_deep_link'],
1301 'sticky' => $is_pro && !empty($settings['kng_steps_sticky_progress']) && 'yes' === $settings['kng_steps_sticky_progress'],
1302 'stickyOffset' => $is_pro ? (int) ($settings['kng_steps_sticky_offset'] ?? 0) : 0,
1303 'progressMode' => $is_pro ? ($settings['kng_steps_progress_mode'] ?? 'steps') : 'steps',
1304 'reveal' => $is_pro && !empty($settings['kng_steps_reveal']) && 'yes' === $settings['kng_steps_reveal'],
1305 'revealType' => $settings['kng_steps_reveal_type'] ?? 'fade-up',
1306 'revealStagger' => $is_pro ? (int) ($settings['kng_steps_reveal_stagger'] ?? 0) : 0,
1307 'revealTrigger' => $settings['kng_steps_reveal_trigger'] ?? 'viewport',
1308 'wrap' => $is_pro && 'horizontal' === $layout && !empty($settings['kng_steps_horizontal_wrap']) && 'yes' === $settings['kng_steps_horizontal_wrap'],
1309 ];
1310
1311 $style_vars = [];
1312 if ($is_pro && !empty($settings['kng_steps_sticky_progress']) && 'yes' === $settings['kng_steps_sticky_progress']) {
1313 $style_vars[] = '--kng-steps-sticky-offset:' . (int) ($settings['kng_steps_sticky_offset'] ?? 0) . 'px';
1314 }
1315 if ('horizontal' === $layout) {
1316 $style_vars[] = '--kng-steps-wrap:' . (($is_pro && !empty($settings['kng_steps_horizontal_wrap']) && 'yes' === $settings['kng_steps_horizontal_wrap']) ? 'wrap' : 'nowrap');
1317 }
1318
1319 $this->add_render_attribute('steps_wrapper', [
1320 'class' => $wrapper_classes,
1321 'data-options' => wp_json_encode($options),
1322 ]);
1323
1324 if (!empty($style_vars)) {
1325 $this->add_render_attribute('steps_wrapper', 'style', implode(';', $style_vars) . ';');
1326 }
1327
1328 if ($is_pro && $this->should_enqueue_script($settings)) {
1329 wp_enqueue_script(KING_ADDONS_ASSETS_UNIQUE_KEY . '-steps-process-timeline-script');
1330 }
1331
1332 echo '<div ' . $this->get_render_attribute_string('steps_wrapper') . '>';
1333
1334 $this->render_heading($settings);
1335
1336 echo '<div class="king-addons-steps__track">';
1337 echo '<div class="king-addons-steps__progress-wrap">';
1338 echo '<span class="king-addons-steps__line"><span class="king-addons-steps__progress"></span></span>';
1339 echo '</div>';
1340 echo '<ol class="king-addons-steps__list" role="list">';
1341
1342 $active_index = $this->get_initial_active_index($steps, $is_pro);
1343
1344 foreach ($steps as $index => $step) {
1345 $this->render_step($settings, $step, $index, $active_index, $is_pro, $marker_type, $use_templates, $anchor_linking_enabled);
1346 }
1347
1348 echo '</ol>';
1349 echo '</div>';
1350 echo '</div>';
1351 }
1352
1353 protected function render_heading(array $settings): void
1354 {
1355 $title = trim((string) ($settings['kng_steps_title'] ?? ''));
1356 $subtitle = trim((string) ($settings['kng_steps_subtitle'] ?? ''));
1357
1358 if ('' === $title && '' === $subtitle) {
1359 return;
1360 }
1361
1362 echo '<div class="king-addons-steps__heading">';
1363
1364 if ('' !== $title) {
1365 echo '<h3 class="king-addons-steps__title">' . esc_html($title) . '</h3>';
1366 }
1367
1368 if ('' !== $subtitle) {
1369 echo '<div class="king-addons-steps__subtitle">' . esc_html($subtitle) . '</div>';
1370 }
1371
1372 echo '</div>';
1373 }
1374
1375 protected function render_step(array $settings, array $step, int $index, int $active_index, bool $is_pro, string $marker_type, bool $use_templates, bool $anchor_linking_enabled): void
1376 {
1377 $title = trim((string) ($step['step_title'] ?? ''));
1378 $description = trim((string) ($step['step_description'] ?? ''));
1379 $cta_label = trim((string) ($step['step_cta_text'] ?? ''));
1380 $link = $step['step_link'] ?? [];
1381
1382 $step_state = $is_pro ? ($step['step_state'] ?? 'default') : 'default';
1383 $anchor_id = '';
1384 if ($is_pro && !empty($step['step_anchor_id'])) {
1385 $anchor_id = $this->sanitize_anchor_id((string) $step['step_anchor_id']);
1386 }
1387
1388 $number_value = $index + 1;
1389 if (($settings['kng_steps_numbering_mode'] ?? 'auto') === 'manual' && !empty($step['step_number'])) {
1390 $number_value = sanitize_text_field((string) $step['step_number']);
1391 }
1392
1393 $number_text = $this->format_step_number($number_value, $settings, $is_pro);
1394 $step_classes = ['king-addons-step'];
1395
1396 if ($index === $active_index) {
1397 $step_classes[] = 'is-active';
1398 }
1399
1400 if ('highlighted' === $step_state) {
1401 $step_classes[] = 'is-highlighted';
1402 }
1403
1404 if ('disabled' === $step_state) {
1405 $step_classes[] = 'is-disabled';
1406 }
1407
1408 $attributes = [
1409 'class' => implode(' ', $step_classes),
1410 'data-step-index' => (string) $index,
1411 ];
1412
1413 if ('' !== $anchor_id) {
1414 $attributes['data-anchor'] = $anchor_id;
1415 }
1416
1417 if ('disabled' === $step_state) {
1418 $attributes['aria-disabled'] = 'true';
1419 }
1420
1421 if ($anchor_linking_enabled && '' !== $anchor_id && 'disabled' !== $step_state) {
1422 $attributes['tabindex'] = '0';
1423 $attributes['role'] = 'button';
1424 }
1425
1426 $attr_output = [];
1427 foreach ($attributes as $key => $value) {
1428 $attr_output[] = esc_attr($key) . '="' . esc_attr($value) . '"';
1429 }
1430
1431 echo '<li ' . implode(' ', $attr_output) . '>';
1432 $has_icon = !empty($step['step_icon']['value']);
1433 $show_number = 'icon' !== $marker_type || !$has_icon;
1434
1435 echo '<div class="king-addons-step__marker">';
1436
1437 if ($show_number && '' !== (string) $number_text) {
1438 echo '<span class="king-addons-step__number">' . esc_html($number_text) . '</span>';
1439 }
1440
1441 if ('number' !== $marker_type && $has_icon) {
1442 echo '<span class="king-addons-step__icon">';
1443 Icons_Manager::render_icon($step['step_icon'], ['aria-hidden' => 'true']);
1444 echo '</span>';
1445 }
1446
1447 echo '</div>';
1448 echo '<div class="king-addons-step__content">';
1449
1450 if ('' !== $title) {
1451 echo '<h4 class="king-addons-step__title">' . esc_html($title) . '</h4>';
1452 }
1453
1454 if ('' !== $description) {
1455 echo '<div class="king-addons-step__description">' . wp_kses_post($description) . '</div>';
1456 }
1457
1458 if ($use_templates && !empty($step['step_template_id'])) {
1459 $template_id = absint($step['step_template_id']);
1460 if ($template_id) {
1461 echo '<div class="king-addons-step__template">' . Plugin::instance()->frontend->get_builder_content_for_display($template_id) . '</div>';
1462 }
1463 }
1464
1465 if (!empty($link['url']) && '' !== $cta_label && 'disabled' !== $step_state) {
1466 $link_attrs = $this->get_link_attributes($link, 'king-addons-step__cta');
1467 if ('' !== $link_attrs) {
1468 echo '<a ' . $link_attrs . '>' . esc_html($cta_label) . '</a>';
1469 }
1470 }
1471
1472 echo '</div>';
1473 echo '</li>';
1474 }
1475
1476 protected function format_step_number($number_value, array $settings, bool $is_pro): string
1477 {
1478 if ('' === (string) $number_value) {
1479 return '';
1480 }
1481
1482 $number_text = (string) $number_value;
1483
1484 if ($is_pro && 'leading' === ($settings['kng_steps_number_format'] ?? 'default') && is_numeric($number_value)) {
1485 $number_text = str_pad((string) $number_value, 2, '0', STR_PAD_LEFT);
1486 }
1487
1488 if ($is_pro) {
1489 $prefix = (string) ($settings['kng_steps_number_prefix'] ?? '');
1490 $suffix = (string) ($settings['kng_steps_number_suffix'] ?? '');
1491 $number_text = $prefix . $number_text . $suffix;
1492 }
1493
1494 return $number_text;
1495 }
1496
1497 protected function get_link_attributes(array $link, string $class = ''): string
1498 {
1499 if (empty($link['url'])) {
1500 return '';
1501 }
1502
1503 $attributes = [
1504 'href' => esc_url($link['url']),
1505 ];
1506
1507 if ('' !== $class) {
1508 $attributes['class'] = $class;
1509 }
1510
1511 if (!empty($link['is_external'])) {
1512 $attributes['target'] = '_blank';
1513 $attributes['rel'] = 'noopener noreferrer';
1514 }
1515
1516 if (!empty($link['nofollow'])) {
1517 $attributes['rel'] = isset($attributes['rel']) ? $attributes['rel'] . ' nofollow' : 'nofollow';
1518 }
1519
1520 $output = [];
1521 foreach ($attributes as $key => $value) {
1522 $output[] = esc_attr($key) . '="' . esc_attr((string) $value) . '"';
1523 }
1524
1525 return implode(' ', $output);
1526 }
1527
1528 protected function sanitize_anchor_id(string $raw): string
1529 {
1530 $slug = strtolower($raw);
1531 $slug = preg_replace('/[^a-z0-9\-_]+/', '-', $slug);
1532 $slug = trim((string) $slug, '-_');
1533 return $slug;
1534 }
1535
1536 protected function is_pro_enabled(): bool
1537 {
1538 return function_exists('king_addons_freemius') && king_addons_freemius()->can_use_premium_code__premium_only();
1539 }
1540
1541 protected function should_enqueue_script(array $settings): bool
1542 {
1543 if (!$this->is_pro_enabled()) {
1544 return false;
1545 }
1546
1547 $flags = [
1548 $settings['kng_steps_active_on_scroll'] ?? '',
1549 $settings['kng_steps_anchor_linking'] ?? '',
1550 $settings['kng_steps_anchor_sync'] ?? '',
1551 $settings['kng_steps_sticky_progress'] ?? '',
1552 $settings['kng_steps_reveal'] ?? '',
1553 ];
1554
1555 foreach ($flags as $flag) {
1556 if ('yes' === $flag) {
1557 return true;
1558 }
1559 }
1560
1561 return false;
1562 }
1563
1564 protected function get_initial_active_index(array $steps, bool $is_pro): int
1565 {
1566 if (!$is_pro) {
1567 return 0;
1568 }
1569
1570 foreach ($steps as $index => $step) {
1571 if (($step['step_state'] ?? 'default') !== 'disabled') {
1572 return $index;
1573 }
1574 }
1575
1576 return 0;
1577 }
1578
1579 protected function get_default_steps(): array
1580 {
1581 return [
1582 [
1583 'step_title' => esc_html__('Share your goals', 'king-addons'),
1584 'step_description' => esc_html__('Tell us what you want to achieve and the timeline you have in mind.', 'king-addons'),
1585 ],
1586 [
1587 'step_title' => esc_html__('We map the process', 'king-addons'),
1588 'step_description' => esc_html__('Our team creates a clear plan with milestones and deliverables.', 'king-addons'),
1589 ],
1590 [
1591 'step_title' => esc_html__('Launch and iterate', 'king-addons'),
1592 'step_description' => esc_html__('We ship, measure results, and improve with every iteration.', 'king-addons'),
1593 ],
1594 ];
1595 }
1596
1597 protected function get_pro_control_class(string $extra = ''): string
1598 {
1599 if ($this->is_pro_enabled()) {
1600 return $extra;
1601 }
1602
1603 return trim('king-addons-pro-control ' . $extra);
1604 }
1605 }
1606