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 / 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.49, at includes/widgets/Steps_Process_Timeline/Steps_Process_Timeline.php

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