Wrapper-компоненты — это абстракции, позволяющие инкапсулировать работу с библиотекой Driver.js внутри переиспользуемых модулей. Их основная задача — скрыть низкоуровневые детали настройки и управления турами, предоставляя удобный интерфейс для интеграции в приложение.
Такие компоненты особенно актуальны при использовании современных фреймворков (React, Vue, Angular), где важна декларативность и повторное использование логики.
Основные функции wrapper-слоя:
Wrapper-компонент строится вокруг экземпляра Driver:
import Driver from "driver.js";
import "driver.js/dist/driver.css";
class DriverWrapper {
constructor(options = {}) {
this.driver = new Driver({
animate: true,
opacity: 0.75,
...options
});
}
defineSteps(steps) {
this.driver.defineSteps(steps);
}
start() {
this.driver.start();
}
reset() {
this.driver.reset();
}
}
export default DriverWrapper;
Ключевые элементы:
start, reset)Wrapper позволяет вынести конфигурацию в единое место:
const defaultSteps = [
{
element: "#header",
popover: {
title: "Заголовок",
description: "Описание заголовка"
}
},
{
element: "#button",
popover: {
title: "Кнопка",
description: "Нажмите сюда"
}
}
];
Использование:
const tour = new DriverWrapper();
tour.defineSteps(defaultSteps);
tour.start();
В более сложных приложениях wrapper реализуется как сервис:
class TourService {
constructor() {
this.driver = null;
}
init(options) {
this.driver = new Driver(options);
}
start(steps) {
if (!this.driver) return;
this.driver.defineSteps(steps);
this.driver.start();
}
stop() {
this.driver?.reset();
}
}
export const tourService = new TourService();
Преимущества:
В React wrapper обычно реализуется как компонент или hook.
import { useRef } from "react";
import Driver from "driver.js";
export function useDriver(options) {
const driverRef = useRef(null);
const init = () => {
driverRef.current = new Driver(options);
};
const start = (steps) => {
if (!driverRef.current) init();
driverRef.current.defineSteps(steps);
driverRef.current.start();
};
const reset = () => {
driverRef.current?.reset();
};
return { start, reset };
}
Использование:
const { start } = useDriver();
start(steps);
import { useEffect } from "react";
import Driver from "driver.js";
const DriverComponent = ({ steps, run }) => {
useEffect(() => {
if (!run) return;
const driver = new Driver();
driver.defineSteps(steps);
driver.start();
return () => driver.reset();
}, [run, steps]);
return null;
};
export default DriverComponent;
В Vue wrapper реализуется через composable или компонент.
import Driver from "driver.js";
export function useDriver(options = {}) {
const driver = new Driver(options);
const start = (steps) => {
driver.defineSteps(steps);
driver.start();
};
const reset = () => driver.reset();
return { start, reset };
}
В Angular используется сервис:
import { Injectable } from "@angular/core";
import Driver from "driver.js";
@Injectable({
providedIn: "root"
})
export class DriverService {
private driver: any;
init(options: any) {
this.driver = new Driver(options);
}
start(steps: any[]) {
this.driver.defineSteps(steps);
this.driver.start();
}
reset() {
this.driver.reset();
}
}
Wrapper-компонент может быть интегрирован с состоянием приложения:
class DriverWithState {
constructor(store) {
this.store = store;
this.driver = new Driver();
}
start(steps) {
this.store.setTourActive(true);
this.driver.defineSteps(steps);
this.driver.start();
}
stop() {
this.store.setTourActive(false);
this.driver.reset();
}
}
Wrapper может генерировать шаги на основе состояния:
generateSteps(userRole) {
if (userRole === "admin") {
return [
{ element: "#adminPanel", popover: { title: "Админка" } }
];
}
return [
{ element: "#dashboard", popover: { title: "Главная" } }
];
}
Добавление дополнительных возможностей:
start(steps) {
console.log("Tour started");
this.driver.defineSteps(steps);
this.driver.start();
}
this.driver = new Driver({
onReset: () => console.log("Tour ended"),
onNext: () => console.log("Next step")
});
Оптимизация через отложенное создание:
getDriver() {
if (!this.driver) {
this.driver = new Driver();
}
return this.driver;
}
Wrapper может поддерживать несколько сценариев:
class MultiTour {
constructor() {
this.tours = {};
}
create(name, steps) {
const driver = new Driver();
driver.defineSteps(steps);
this.tours[name] = driver;
}
start(name) {
this.tours[name]?.start();
}
}
Wrapper-компоненты часто реализуют:
1. Повторная инициализация Driver
start() {
const driver = new Driver(); // ошибка
}
Решение — хранить экземпляр.
2. Отсутствие очистки
componentWillUnmount() {
// driver.reset() не вызван
}
3. Жёсткая привязка к DOM
Wrapper должен учитывать, что элементы могут появляться асинхронно.
Оптимальная структура:
/tour
├── driverWrapper.js
├── steps.js
├── tourService.js
└── hooks/
Wrapper-компонент должен поддерживать:
start(steps) {
analytics.track("tour_started");
this.driver.defineSteps(steps);
this.driver.start();
}
Wrapper может ожидать загрузку данных:
async startAsync(getSteps) {
const steps = await getSteps();
this.driver.defineSteps(steps);
this.driver.start();
}
startIfAllowed(user) {
if (!user.hasSeenTour) {
this.start(this.getSteps());
}
}
Wrapper может централизованно задавать стили:
this.driver = new Driver({
className: "custom-theme",
nextBtnText: "Далее",
prevBtnText: "Назад"
});
Wrapper может взаимодействовать с интерфейсом:
onStepChange(callback) {
this.driver = new Driver({
onHighlighted: callback
});
}
Главная ценность wrapper-компонентов: