Структура кода туров

Структура кода туров в Shepherd.js строится вокруг трёх ключевых сущностей: тур (Tour), шаг (Step) и конфигурация (Options). Правильная организация этих элементов определяет читаемость, расширяемость и устойчивость кода.


Объект тура (Tour)

Тур — это основной управляющий объект, который инкапсулирует последовательность шагов и их поведение.

Создание тура

const tour = new Shepherd.Tour({
  defaultStepOptions: {
    cancelIcon: {
      enabled: true
    },
    classes: 'shepherd-theme-default',
    scrollTo: true
  },
  useModalOverlay: true
});

Основные параметры

  • defaultStepOptions — глобальные настройки для всех шагов
  • useModalOverlay — затемнение фона
  • exitOnEsc — выход по клавише Escape
  • keyboardNavigation — управление стрелками клавиатуры

Структура шага (Step)

Шаг представляет собой единичный элемент тура — подсказку, привязанную к элементу интерфейса.

Базовый пример

tour.addStep({
  id: 'example-step',
  text: 'Описание шага',
  attachTo: {
    element: '.example-selector',
    on: 'bottom'
  },
  buttons: [
    {
      text: 'Далее',
      action: tour.next
    }
  ]
});

Ключевые свойства шага

1. text

Содержимое подсказки:

text: 'Текст шага'

Допускается:

  • строка
  • HTML
  • функция, возвращающая строку или DOM

2. attachTo

Привязка к элементу:

attachTo: {
  element: '.selector',
  on: 'top'
}

Позиции:

  • top
  • bottom
  • left
  • right
  • auto

Если элемент отсутствует — шаг может не отображаться.


3. buttons

Кнопки управления:

buttons: [
  {
    text: 'Назад',
    action: tour.back
  },
  {
    text: 'Далее',
    action: tour.next
  }
]

Возможные действия:

  • tour.next
  • tour.back
  • tour.cancel
  • кастомная функция

4. id

Уникальный идентификатор шага:

id: 'step-1'

Используется для:

  • навигации
  • отладки
  • управления состоянием

5. classes

CSS-классы:

classes: 'custom-step-class'

Позволяет:

  • кастомизировать внешний вид
  • управлять стилями отдельных шагов

6. scrollTo

Автоматическая прокрутка:

scrollTo: true

Или с параметрами:

scrollTo: {
  beh * avior: 'smooth',
  block: 'center'
}

Порядок шагов

Шаги добавляются последовательно:

tour.addStep({...});
tour.addStep({...});
tour.addStep({...});

Порядок добавления = порядок показа.


Декомпозиция кода

Для масштабируемых проектов туры разбиваются на модули.

Вариант 1: функции генерации шагов

function createIntroStep(tour) {
  return {
    id: 'intro',
    text: 'Добро пожаловать',
    buttons: [
      {
        text: 'Далее',
        action: tour.next
      }
    ]
  };
}

Использование:

tour.addStep(createIntroStep(tour));

Вариант 2: массив конфигураций

const steps = [
  {
    id: 'step1',
    text: 'Шаг 1'
  },
  {
    id: 'step2',
    text: 'Шаг 2'
  }
];

steps.forEach(step => tour.addStep(step));

Вариант 3: классы

class TourBuilder {
  constructor() {
    this.tour = new Shepherd.Tour();
  }

  addSteps() {
    this.tour.addStep({
      id: 'step1',
      text: 'Шаг 1'
    });
  }

  build() {
    this.addSteps();
    return this.tour;
  }
}

Управление состоянием

Методы тура

tour.start();
tour.next();
tour.back();
tour.cancel();
tour.complete();

События

tour.on('start', () => {});
tour.on('complete', () => {});
tour.on('cancel', () => {});
tour.on('show', (event) => {});

Событие show даёт доступ к текущему шагу:

tour.on('show', (event) => {
  console.log(event.step.id);
});

Условная логика

Шаги могут отображаться динамически.

Пример с проверкой

tour.addStep({
  id: 'conditional-step',
  text: 'Появляется при условии',
  when: {
    show: function () {
      return someCondition;
    }
  }
});

Асинхронные шаги

Поддержка ожидания перед показом:

tour.addStep({
  id: 'async-step',
  beforeShowPromise: function () {
    return new Promise(resolve => {
      setTimeout(resolve, 1000);
    });
  }
});

Используется для:

  • загрузки данных
  • ожидания рендера DOM
  • API-запросов

Работа с DOM

Если элемент появляется динамически:

beforeShowPromise: function () {
  return new Promise(resolve => {
    const interval = setInterval(() => {
      if (document.querySelector('.dynamic')) {
        clearInterval(interval);
        resolve();
      }
    }, 100);
  });
}

Переиспользование конфигураций

Общие настройки

const defaultOptions = {
  classes: 'custom-theme',
  scrollTo: true
};

const tour = new Shepherd.Tour({
  defaultStepOptions: defaultOptions
});

Расширение шагов

const baseStep = {
  buttons: [
    {
      text: 'Далее',
      action: tour.next
    }
  ]
};

tour.addStep({
  ...baseStep,
  id: 'step1',
  text: 'Шаг 1'
});

Изоляция логики

Рекомендуется разделять:

  • конфигурацию шагов
  • бизнес-логику
  • инициализацию тура

Пример структуры файлов

/tour
  steps.js
  tour.js
  conditions.js

Управление несколькими турами

const tours = {
  onboarding: new Shepherd.Tour(),
  advanced: new Shepherd.Tour()
};

Выбор тура:

tours.onboarding.start();

Инкапсуляция и повторное использование

Туры удобно оборачивать в фабрики:

function createTour(config) {
  const tour = new Shepherd.Tour(config);

  // добавление шагов

  return tour;
}

Обработка ошибок

Частые проблемы:

  • элемент не найден
  • шаг не отображается
  • конфликт CSS

Проверка:

if (!document.querySelector('.selector')) {
  console.warn('Элемент не найден');
}

Оптимизация структуры

Практики:

  • избегать дублирования шагов
  • выносить повторяющиеся части
  • использовать именованные шаги
  • группировать связанные шаги

Глубокая вложенность логики

Для сложных сценариев:

tour.addStep({
  id: 'complex-step',
  text: () => {
    if (condition) {
      return 'Вариант A';
    }
    return 'Вариант B';
  }
});

Связь с состоянием приложения

Интеграция с фреймворками:

if (store.user.isNew) {
  tour.start();
}

Итоговая структура

Минимальная, но масштабируемая организация:

const tour = new Shepherd.Tour({
  defaultStepOptions: {...}
});

const steps = [ ... ];

steps.forEach(step => tour.addStep(step));

export default tour;

Такая структура обеспечивает:

  • читаемость
  • гибкость
  • возможность расширения без переписывания кода