steps_section.blade.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. {{--
  2. Blade-шаблон блока «Этапы работ» (JS-степпер).
  3. $data:
  4. label — надпись над заголовком
  5. heading — заголовок секции
  6. subtext — описание секции
  7. steps[] — массив шагов: title, desc, image_url, image_alt
  8. Все панели шагов рендерятся сервером в Blade.
  9. JS только переключает видимость — никаких данных в скрипте.
  10. --}}
  11. @php
  12. $steps = $data['steps'] ?? [];
  13. // Абсолютный URL: хранимые пути (blocks/...) → через storage, внешние — как есть
  14. $toUrl = fn(?string $p): string =>
  15. $p ? (str_starts_with($p, 'http') ? $p : asset('storage/' . $p)) : '';
  16. @endphp
  17. <section class="steps-section" id="steps">
  18. <div class="container">
  19. <div class="sh reveal">
  20. <div class="sh-label">{{ $data['label'] ?? '' }}</div>
  21. <h2>{{ $data['heading'] ?? '' }}</h2>
  22. @if(!empty($data['subtext']))
  23. <p>{{ $data['subtext'] }}</p>
  24. @endif
  25. </div>
  26. {{-- Горизонтальный степпер: кнопки и линии --}}
  27. <nav class="stepper" id="stepper" aria-label="{{ $data['heading'] ?? 'Этапы' }}">
  28. @foreach($steps as $i => $step)
  29. <div class="stepper-item">
  30. <button class="stepper-dot{{ $i === 0 ? ' active' : '' }}"
  31. data-step="{{ $i }}"
  32. aria-label="Шаг {{ $i + 1 }}">{{ $i + 1 }}</button>
  33. </div>
  34. @if(!$loop->last)
  35. <div class="stepper-item">
  36. <div class="stepper-line{{ $i === 0 ? ' done' : '' }}" id="line-{{ $i }}"></div>
  37. </div>
  38. @endif
  39. @endforeach
  40. </nav>
  41. {{-- Все панели шагов — рендерит сервер, JS только скрывает/показывает --}}
  42. <div id="step-panel" style="transition:opacity .25s ease">
  43. @foreach($steps as $i => $step)
  44. <div class="step-content" data-step-panel="{{ $i }}"{{ $i !== 0 ? ' style="display:none"' : '' }}>
  45. <div class="step-left">
  46. <div class="step-badge">{{ $i + 1 }}</div>
  47. <div class="step-title">{{ $step['title'] ?? '' }}</div>
  48. <div class="step-desc">{{ $step['desc'] ?? '' }}</div>
  49. </div>
  50. <div class="step-right">
  51. @php $imgUrl = $toUrl($step['image_url'] ?? ''); @endphp
  52. @if($imgUrl)
  53. <img src="{{ $imgUrl }}" alt="{{ $step['image_alt'] ?? '' }}" loading="lazy"/>
  54. @endif
  55. </div>
  56. </div>
  57. @endforeach
  58. </div>
  59. </div>
  60. </section>
  61. <script>
  62. (function () {
  63. var current = 0;
  64. var dots, lines, panels, wrapper;
  65. function goTo(idx) {
  66. if (idx === current) return;
  67. wrapper.style.opacity = '0';
  68. setTimeout(function () {
  69. panels[current].style.display = 'none';
  70. current = idx;
  71. panels[current].style.display = '';
  72. wrapper.style.opacity = '1';
  73. }, 150);
  74. dots.forEach(function (d, i) { d.classList.toggle('active', i === current); });
  75. lines.forEach(function (l, i) { l.classList.toggle('done', i < current); });
  76. }
  77. document.addEventListener('DOMContentLoaded', function () {
  78. wrapper = document.getElementById('step-panel');
  79. if (!wrapper) return;
  80. dots = Array.from(document.querySelectorAll('.stepper-dot'));
  81. lines = Array.from(document.querySelectorAll('.stepper-line'));
  82. panels = Array.from(document.querySelectorAll('[data-step-panel]'));
  83. if (!panels.length) return;
  84. dots.forEach(function (d) {
  85. d.addEventListener('click', function () {
  86. goTo(parseInt(d.getAttribute('data-step'), 10));
  87. });
  88. });
  89. document.getElementById('stepper').addEventListener('keydown', function (e) {
  90. if (e.key === 'ArrowRight' && current < panels.length - 1) goTo(current + 1);
  91. if (e.key === 'ArrowLeft' && current > 0) goTo(current - 1);
  92. });
  93. });
  94. })();
  95. </script>