Композиция API

Shepherd.js построен вокруг принципа композиции: сложные сценарии пользовательских туров формируются из небольших, независимых сущностей — туров, шагов и конфигураций. Такой подход позволяет гибко управлять поведением интерфейса, повторно использовать логику и масштабировать решения без усложнения кода.

Композиция API означает, что каждый элемент можно собирать как «конструктор»: объединять шаги, переиспользовать настройки, расширять поведение через функции и хуки.


Основные сущности композиции

Tour (Тур)

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

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

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

Пример создания:

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

Step (Шаг)

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

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

  • id — уникальный идентификатор
  • text — содержимое
  • attachTo — привязка к DOM-элементу
  • buttons — элементы управления
  • when — события жизненного цикла

Пример:

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

Композиция шагов

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

Общие параметры можно вынести в отдельный объект и комбинировать:

const baseStep = {
  classes: 'custom-step',
  scrollTo: true
};

tour.addStep({
  ...baseStep,
  text: 'Первый шаг',
  attachTo: { element: '.one', on: 'bottom' }
});

tour.addStep({
  ...baseStep,
  text: 'Второй шаг',
  attachTo: { element: '.two', on: 'top' }
});

Функциональная композиция

Shepherd позволяет задавать значения как функции, что делает шаги динамическими.

tour.addStep({
  text: () => {
    return document.querySelector('.dynamic').innerText;
  }
});

Это позволяет:

  • подстраивать контент под состояние страницы
  • вычислять позиции и данные на лету

Композиция поведения через события

Каждый шаг поддерживает хуки жизненного цикла.

Основные события:

  • show
  • hide
  • cancel
  • complete

Пример:

tour.addStep({
  id: 'step-with-events',
  text: 'Шаг с событиями',
  when: {
    show() {
      console.log('Шаг показан');
    },
    hide() {
      console.log('Шаг скрыт');
    }
  }
});

Композиция через события позволяет:

  • внедрять стороннюю логику
  • управлять состоянием приложения
  • синхронизировать тур с UI

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

Связывание шагов

Шаги автоматически образуют цепочку, но поведение можно кастомизировать:

buttons: [
  {
    text: 'Назад',
    action: tour.back
  },
  {
    text: 'Вперёд',
    action: () => {
      if (condition) {
        tour.next();
      }
    }
  }
]

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

Композиция позволяет внедрять условия:

action: () => {
  if (user.isAdmin) {
    tour.show('admin-step');
  } else {
    tour.next();
  }
}

Композиция через defaultStepOptions

Глобальные настройки позволяют избежать дублирования:

const tour = new Shepherd.Tour({
  defaultStepOptions: {
    cancelIcon: {
      enabled: true
    },
    classes: 'global-class',
    scrollTo: { beh * avior: 'smooth', block: 'center' }
  }
});

Каждый шаг может переопределить эти параметры.


Расширение через пользовательские функции

Shepherd легко комбинируется с внешними функциями:

function createStep(selector, text) {
  return {
    text,
    attachTo: {
      element: selector,
      on: 'bottom'
    }
  };
}

tour.addStep(createStep('.btn', 'Кнопка действия'));

Это позволяет:

  • создавать фабрики шагов
  • стандартизировать структуру
  • ускорять разработку

Композиция с асинхронностью

Шаги могут зависеть от асинхронных операций:

tour.addStep({
  id: 'async-step',
  beforeShowPromise: () => {
    return fetch('/data')
      .then(res => res.json())
      .then(data => {
        document.querySelector('.target').innerText = data.value;
      });
  },
  text: 'Данные загружены'
});

Это важно для:

  • API-запросов
  • ленивой загрузки UI
  • ожидания DOM-изменений

Работа с attachTo как композицией

attachTo может быть динамическим:

attachTo: {
  element: () => document.querySelector('.dynamic-element'),
  on: 'right'
}

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


Композиция кнопок

Кнопки — независимые объекты, которые можно переиспользовать:

const nextButton = {
  text: 'Далее',
  action: tour.next
};

const backButton = {
  text: 'Назад',
  action: tour.back
};

tour.addStep({
  text: 'Шаг',
  buttons: [backButton, nextButton]
});

Декларативный подход к построению тура

Можно описывать весь тур как структуру данных:

const steps = [
  {
    text: 'Шаг 1',
    selector: '.one'
  },
  {
    text: 'Шаг 2',
    selector: '.two'
  }
];

steps.forEach(step => {
  tour.addStep({
    text: step.text,
    attachTo: {
      element: step.selector,
      on: 'bottom'
    }
  });
});

Композиция с состоянием приложения

Shepherd можно интегрировать с состоянием (например, Redux, Vuex):

when: {
  show() {
    store.dispatch({ type: 'TOUR_STEP_SHOWN' });
  }
}

Изоляция и модульность

Хорошая практика — разбивать туры на модули:

export function createUserTour() {
  const tour = new Shepherd.Tour();

  tour.addStep({ text: 'Профиль' });
  tour.addStep({ text: 'Настройки' });

  return tour;
}

Это позволяет:

  • разделять ответственность
  • тестировать отдельно
  • подключать по требованию

Управление жизненным циклом тура

Основные методы:

  • tour.start() — запуск
  • tour.next() — следующий шаг
  • tour.back() — предыдущий
  • tour.cancel() — отмена
  • tour.complete() — завершение

Композиция позволяет вызывать их в любом месте приложения.


Комбинирование нескольких туров

Можно создавать несколько независимых туров:

const onboardingT our = new Shepherd.Tour();
const featureTour = new Shepherd.Tour();

И управлять ими отдельно:

  • запускать по событиям
  • переключать в зависимости от пользователя

Паттерны композиции

1. Фабрика шагов

Создание шагов через функции

2. Конфигурационные объекты

Хранение структуры в JSON

3. Расширение через события

Интеграция с внешним кодом

4. Динамическая генерация

Создание шагов на основе данных


Ошибки при композиции

  • Жёсткая привязка к DOM без проверок
  • Дублирование настроек
  • Отсутствие модульности
  • Игнорирование асинхронности
  • Смешивание логики UI и тура

Практический пример композиции

function buildTour(stepsConfig) {
  const tour = new Shepherd.Tour({
    defaultStepOptions: {
      scrollTo: true
    }
  });

  stepsConfig.forEach(config => {
    tour.addStep({
      text: config.text,
      attachTo: {
        element: config.selector,
        on: config.position || 'bottom'
      }
    });
  });

  return tour;
}

const tour = buildTour([
  { text: 'Приветствие', selector: '.start' },
  { text: 'Меню', selector: '.menu' }
]);

tour.start();

Композиция API в Shepherd.js делает библиотеку мощным инструментом для построения сложных сценариев обучения пользователей, позволяя комбинировать поведение, данные и интерфейс в гибкую и расширяемую систему.