webdrive 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +620 -0
- package/dist/index.cjs +1426 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +267 -0
- package/dist/index.d.ts +267 -0
- package/dist/index.js +1395 -0
- package/dist/index.js.map +1 -0
- package/dist/webdrive.css +310 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 webdrive
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
# webdrive
|
|
2
|
+
|
|
3
|
+
> **A production-ready, framework-agnostic TypeScript UI tour and onboarding walkthrough library with zero runtime dependencies.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/webdrive)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
|
|
9
|
+
`webdrive` provides guided, step-by-step product walkthroughs and feature tours for modern web applications. It works directly with browser DOM APIs and does not depend on React, Vue, Angular, Svelte, Tailwind CSS, or any other UI framework.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## âĻ Features
|
|
14
|
+
|
|
15
|
+
- ðŊ **100% Framework-Agnostic** â Works seamlessly in Vanilla JavaScript, TypeScript, React, Next.js, Vue, Nuxt, Angular, Svelte, or any DOM environment.
|
|
16
|
+
- ðŠķ **Zero Dependencies** â Zero external runtime dependencies. Extremely lightweight and fast.
|
|
17
|
+
- ðĄïļ **Non-Destructive Highlighting** â Employs a full-screen SVG cutout mask to highlight elements without altering parent stacking contexts, `z-index`, `overflow`, or `transform` styles.
|
|
18
|
+
- ð **Dedicated Positioning Engine** â Automatic viewport edge collision detection, intelligent flip fallbacks, coordinate clamping, and arrow alignment.
|
|
19
|
+
- âŋ **Accessible by Design** â Proper dialog semantics (`role="dialog"`, `aria-modal="true"`, `aria-labelledby`, `aria-describedby`), focus trapping, automatic focus restoration, and keyboard navigation.
|
|
20
|
+
- âĻïļ **Keyboard Support** â `ArrowRight` (Next), `ArrowLeft` (Previous), and `Escape` (Close).
|
|
21
|
+
- ðū **Persistent State** â Remember completed tours across sessions with namespaced localStorage support and custom storage adapters.
|
|
22
|
+
- âģ **Dynamic Element Support** â Gracefully handles elements that load asynchronously (`missingElementBehavior: "skip" | "stop" | "wait"`).
|
|
23
|
+
- ð **Themeable & Dark Mode Ready** â Controlled entirely via CSS custom properties. Drop-in compatible with Tailwind CSS and shadcn/ui.
|
|
24
|
+
- ⥠**SSR Safe** â Zero access to `window`, `document`, or `localStorage` during module evaluation. Safe to import in Next.js Server Components and Node.js.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## ðĶ Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install webdrive
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
or with yarn / pnpm / bun:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pnpm add webdrive
|
|
38
|
+
# or
|
|
39
|
+
yarn add webdrive
|
|
40
|
+
# or
|
|
41
|
+
bun add webdrive
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## ð Quick Start
|
|
47
|
+
|
|
48
|
+
### 1. Import CSS & JavaScript
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { WebDrive } from "webdrive";
|
|
52
|
+
import "webdrive/styles.css";
|
|
53
|
+
|
|
54
|
+
const tour = new WebDrive({
|
|
55
|
+
id: "dashboard-tour",
|
|
56
|
+
steps: [
|
|
57
|
+
{
|
|
58
|
+
element: "#sidebar",
|
|
59
|
+
title: "Navigation",
|
|
60
|
+
description: "Use the sidebar to navigate across the application.",
|
|
61
|
+
position: "right",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
element: "#dashboard-stats",
|
|
65
|
+
title: "Dashboard Statistics",
|
|
66
|
+
description: "View real-time business metrics and transaction velocity.",
|
|
67
|
+
position: "bottom",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
element: "#profile-menu",
|
|
71
|
+
title: "User Profile",
|
|
72
|
+
description: "Configure your personal preferences and organization settings.",
|
|
73
|
+
position: "left",
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
showProgress: true,
|
|
77
|
+
animate: true,
|
|
78
|
+
smoothScroll: true,
|
|
79
|
+
remember: true,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
tour.start();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## ð ïļ Step Configuration (`WebDriveStep`)
|
|
88
|
+
|
|
89
|
+
Each step in the `steps` array can be configured with the following properties:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
interface WebDriveStep {
|
|
93
|
+
/** Target element selector string (e.g., "#sidebar") or HTMLElement */
|
|
94
|
+
element: string | HTMLElement;
|
|
95
|
+
|
|
96
|
+
/** Title displayed in the popover header */
|
|
97
|
+
title?: string;
|
|
98
|
+
|
|
99
|
+
/** Plain text description rendered safely inside the content area */
|
|
100
|
+
description?: string;
|
|
101
|
+
|
|
102
|
+
/** Optional custom HTML content (used when rich HTML is required) */
|
|
103
|
+
content?: string;
|
|
104
|
+
|
|
105
|
+
/** Preferred placement: "top" | "right" | "bottom" | "left" (default: "bottom") */
|
|
106
|
+
position?: "top" | "right" | "bottom" | "left";
|
|
107
|
+
|
|
108
|
+
/** Alignment along target axis: "start" | "center" | "end" (default: "center") */
|
|
109
|
+
align?: "start" | "center" | "end";
|
|
110
|
+
|
|
111
|
+
/** Extra padding around the highlighted cutout in pixels (default: 8) */
|
|
112
|
+
padding?: number;
|
|
113
|
+
|
|
114
|
+
/** Spacing between target and popover in pixels (default: 12) */
|
|
115
|
+
offset?: number;
|
|
116
|
+
|
|
117
|
+
/** Show/hide next button for this step */
|
|
118
|
+
showNextButton?: boolean;
|
|
119
|
+
|
|
120
|
+
/** Show/hide previous button for this step */
|
|
121
|
+
showPreviousButton?: boolean;
|
|
122
|
+
|
|
123
|
+
/** Show/hide close button for this step */
|
|
124
|
+
showCloseButton?: boolean;
|
|
125
|
+
|
|
126
|
+
/** Custom label for Next button */
|
|
127
|
+
nextButtonText?: string;
|
|
128
|
+
|
|
129
|
+
/** Custom label for Previous button */
|
|
130
|
+
previousButtonText?: string;
|
|
131
|
+
|
|
132
|
+
/** Custom label for Done button (on final step) */
|
|
133
|
+
doneButtonText?: string;
|
|
134
|
+
|
|
135
|
+
/** Custom label for Close button */
|
|
136
|
+
closeButtonText?: string;
|
|
137
|
+
|
|
138
|
+
/** Hook called when entering this step */
|
|
139
|
+
onEnter?: () => void | Promise<void>;
|
|
140
|
+
|
|
141
|
+
/** Hook called when leaving this step */
|
|
142
|
+
onLeave?: () => void | Promise<void>;
|
|
143
|
+
|
|
144
|
+
/** Extensible custom properties */
|
|
145
|
+
[key: string]: unknown;
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## âïļ WebDrive Configuration Options (`WebDriveOptions`)
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
interface WebDriveOptions {
|
|
155
|
+
/** Unique ID for the tour (required for persistent completion tracking) */
|
|
156
|
+
id?: string;
|
|
157
|
+
|
|
158
|
+
/** Array of tour steps */
|
|
159
|
+
steps: WebDriveStep[];
|
|
160
|
+
|
|
161
|
+
/** Automatically start the tour on instantiation (if not already completed) */
|
|
162
|
+
autoStart?: boolean;
|
|
163
|
+
|
|
164
|
+
/** Show progress counter in popover footer (e.g. "2 / 5") (default: true) */
|
|
165
|
+
showProgress?: boolean;
|
|
166
|
+
|
|
167
|
+
/** Allow user to close the tour via close button or backdrop click (default: true) */
|
|
168
|
+
allowClose?: boolean;
|
|
169
|
+
|
|
170
|
+
/** Smooth animated transitions between steps and cutout bounds (default: true) */
|
|
171
|
+
animate?: boolean;
|
|
172
|
+
|
|
173
|
+
/** Smoothly scroll target element into viewport center before highlighting (default: true) */
|
|
174
|
+
smoothScroll?: boolean;
|
|
175
|
+
|
|
176
|
+
/** Display darkened backdrop overlay (default: true) */
|
|
177
|
+
overlay?: boolean;
|
|
178
|
+
|
|
179
|
+
/** Opacity of backdrop overlay (default: 0.6) */
|
|
180
|
+
overlayOpacity?: number;
|
|
181
|
+
|
|
182
|
+
/** Backdrop overlay color (default: "rgba(0, 0, 0, 0.6)") */
|
|
183
|
+
overlayColor?: string;
|
|
184
|
+
|
|
185
|
+
/** Base z-index for tour layers (default: 100000) */
|
|
186
|
+
zIndex?: number;
|
|
187
|
+
|
|
188
|
+
/** Default padding around highlighted targets (default: 8) */
|
|
189
|
+
stagePadding?: number;
|
|
190
|
+
|
|
191
|
+
/** Border radius of the cutout hole in pixels (default: 6) */
|
|
192
|
+
stageRadius?: number;
|
|
193
|
+
|
|
194
|
+
/** Enable keyboard navigation: Arrow keys & Escape (default: true) */
|
|
195
|
+
keyboardNavigation?: boolean;
|
|
196
|
+
|
|
197
|
+
/** Close tour when pressing Escape (default: true) */
|
|
198
|
+
closeOnEscape?: boolean;
|
|
199
|
+
|
|
200
|
+
/** Show navigation buttons in footer (default: true) */
|
|
201
|
+
showButtons?: boolean;
|
|
202
|
+
|
|
203
|
+
/** Global label for Next button (default: "Next") */
|
|
204
|
+
nextButtonText?: string;
|
|
205
|
+
|
|
206
|
+
/** Global label for Previous button (default: "Previous") */
|
|
207
|
+
previousButtonText?: string;
|
|
208
|
+
|
|
209
|
+
/** Global label for Done button (default: "Done") */
|
|
210
|
+
doneButtonText?: string;
|
|
211
|
+
|
|
212
|
+
/** Global label for Close button (default: "Close tour") */
|
|
213
|
+
closeButtonText?: string;
|
|
214
|
+
|
|
215
|
+
/** Remember completion status in storage so tour doesn't repeat (default: false) */
|
|
216
|
+
remember?: boolean;
|
|
217
|
+
|
|
218
|
+
/** Custom storage provider adapter (defaults to window.localStorage) */
|
|
219
|
+
storage?: WebDriveStorage;
|
|
220
|
+
|
|
221
|
+
/** Strategy when a step target element is not found: "skip" | "stop" | "wait" (default: "skip") */
|
|
222
|
+
missingElementBehavior?: "skip" | "stop" | "wait";
|
|
223
|
+
|
|
224
|
+
/** Maximum time in milliseconds to wait for a missing element if behavior is "wait" (default: 3000) */
|
|
225
|
+
missingElementWaitTimeout?: number;
|
|
226
|
+
|
|
227
|
+
/** Custom progress text formatter function (e.g. (cur, total) => `Step ${cur} of ${total}`) */
|
|
228
|
+
renderProgress?: (current: number, total: number) => string;
|
|
229
|
+
|
|
230
|
+
/** Callback invoked when tour starts */
|
|
231
|
+
onStart?: () => void;
|
|
232
|
+
|
|
233
|
+
/** Callback invoked when advancing or reversing steps */
|
|
234
|
+
onStepChange?: (step: WebDriveStep, index: number) => void;
|
|
235
|
+
|
|
236
|
+
/** Callback invoked when final step is finished */
|
|
237
|
+
onComplete?: () => void;
|
|
238
|
+
|
|
239
|
+
/** Callback invoked when tour is closed before completion */
|
|
240
|
+
onClose?: () => void;
|
|
241
|
+
|
|
242
|
+
/** Callback invoked when tour is destroyed */
|
|
243
|
+
onDestroy?: () => void;
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## ðđïļ Public API Methods
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
const tour = new WebDrive(options);
|
|
253
|
+
|
|
254
|
+
// Starts the tour from the first step (or optional index)
|
|
255
|
+
await tour.start(startIndex?: number);
|
|
256
|
+
|
|
257
|
+
// Stops the active tour and cleans up UI
|
|
258
|
+
await tour.stop();
|
|
259
|
+
|
|
260
|
+
// Moves to the next step (or completes if on the final step)
|
|
261
|
+
await tour.next();
|
|
262
|
+
|
|
263
|
+
// Moves to the previous step
|
|
264
|
+
await tour.previous();
|
|
265
|
+
|
|
266
|
+
// Jumps directly to a specific step index
|
|
267
|
+
await tour.goTo(index: number);
|
|
268
|
+
|
|
269
|
+
// Recalculates positioning (call on window resize, layout shift, or dynamic content)
|
|
270
|
+
tour.refresh();
|
|
271
|
+
|
|
272
|
+
// Completely cleans up all DOM nodes, listeners, timers, and observers
|
|
273
|
+
tour.destroy();
|
|
274
|
+
|
|
275
|
+
// Returns true if the tour is currently active
|
|
276
|
+
tour.isActive(): boolean;
|
|
277
|
+
|
|
278
|
+
// Returns the current active step configuration object or null
|
|
279
|
+
tour.getCurrentStep(): WebDriveStep | null;
|
|
280
|
+
|
|
281
|
+
// Returns the zero-based index of the current step (-1 if inactive)
|
|
282
|
+
tour.getCurrentStepIndex(): number;
|
|
283
|
+
|
|
284
|
+
// Returns true if this tour has already been marked completed in storage
|
|
285
|
+
await tour.hasCompleted(): Promise<boolean>;
|
|
286
|
+
|
|
287
|
+
// Resets completion state for this tour ID
|
|
288
|
+
await tour.reset(): Promise<void>;
|
|
289
|
+
|
|
290
|
+
// Resets completion state for all WebDrive tours stored locally
|
|
291
|
+
await tour.resetAll(): Promise<void>;
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## ðĄ Event System
|
|
297
|
+
|
|
298
|
+
In addition to configuration callbacks, `WebDrive` provides a type-safe pub/sub event system:
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
tour.on("start", () => {
|
|
302
|
+
console.log("Tour started");
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
tour.on("stepChange", ({ step, index }) => {
|
|
306
|
+
console.log(`Current step index: ${index}, title: ${step.title}`);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
tour.on("complete", () => {
|
|
310
|
+
console.log("Tour completed");
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
tour.on("close", () => {
|
|
314
|
+
console.log("Tour was dismissed");
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
tour.on("destroy", () => {
|
|
318
|
+
console.log("Tour was destroyed");
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Remove listeners with tour.off
|
|
322
|
+
const handler = () => { /* ... */ };
|
|
323
|
+
tour.on("stepChange", handler);
|
|
324
|
+
tour.off("stepChange", handler);
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## ðïļ DOM Architecture & Selectors
|
|
330
|
+
|
|
331
|
+
WebDrive creates an isolated UI container with `data-webdrive-*` attributes and namespaced CSS classes:
|
|
332
|
+
|
|
333
|
+
```html
|
|
334
|
+
<div data-webdrive-root class="webdrive-root">
|
|
335
|
+
<!-- SVG Cutout Mask Overlay -->
|
|
336
|
+
<svg data-webdrive-overlay class="webdrive-overlay">
|
|
337
|
+
<defs>
|
|
338
|
+
<mask id="webdrive-mask-xyz">
|
|
339
|
+
<rect width="100%" height="100%" fill="#ffffff" />
|
|
340
|
+
<rect data-webdrive-cutout class="webdrive-cutout" rx="6" ry="6" fill="#000000" />
|
|
341
|
+
</mask>
|
|
342
|
+
</defs>
|
|
343
|
+
<rect width="100%" height="100%" mask="url(#webdrive-mask-xyz)" />
|
|
344
|
+
</svg>
|
|
345
|
+
|
|
346
|
+
<!-- Interactive Stage Boundary -->
|
|
347
|
+
<div data-webdrive-stage class="webdrive-stage"></div>
|
|
348
|
+
|
|
349
|
+
<!-- Popover Dialog Card -->
|
|
350
|
+
<div data-webdrive-popover class="webdrive-popover" role="dialog" aria-modal="true">
|
|
351
|
+
<div data-webdrive-header class="webdrive-header">
|
|
352
|
+
<h2 data-webdrive-title class="webdrive-title" id="webdrive-title"></h2>
|
|
353
|
+
<button data-webdrive-close class="webdrive-close" aria-label="Close tour">×</button>
|
|
354
|
+
</div>
|
|
355
|
+
<div data-webdrive-content class="webdrive-content" id="webdrive-description"></div>
|
|
356
|
+
<div data-webdrive-footer class="webdrive-footer">
|
|
357
|
+
<button data-webdrive-prev class="webdrive-button webdrive-prev"></button>
|
|
358
|
+
<div data-webdrive-progress class="webdrive-progress"></div>
|
|
359
|
+
<button data-webdrive-next class="webdrive-button webdrive-next"></button>
|
|
360
|
+
</div>
|
|
361
|
+
<div data-webdrive-arrow class="webdrive-arrow"></div>
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## ðĻ Styling & CSS Theme Variables
|
|
369
|
+
|
|
370
|
+
Override theme variables in your CSS to tailor WebDrive to your brand:
|
|
371
|
+
|
|
372
|
+
```css
|
|
373
|
+
:root {
|
|
374
|
+
--webdrive-background: #ffffff;
|
|
375
|
+
--webdrive-foreground: #111827;
|
|
376
|
+
--webdrive-border: #e5e7eb;
|
|
377
|
+
--webdrive-primary: #18181b;
|
|
378
|
+
--webdrive-primary-foreground: #ffffff;
|
|
379
|
+
--webdrive-muted: #6b7280;
|
|
380
|
+
--webdrive-overlay: rgba(0, 0, 0, 0.6);
|
|
381
|
+
--webdrive-overlay-opacity: 0.6;
|
|
382
|
+
--webdrive-radius: 0.5rem;
|
|
383
|
+
--webdrive-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
|
384
|
+
--webdrive-z-index: 100000;
|
|
385
|
+
--webdrive-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Dark Mode
|
|
390
|
+
|
|
391
|
+
WebDrive automatically responds to dark mode when `.dark` or `[data-theme="dark"]` is present on `<html>` or `<body>`, or via system `@media (prefers-color-scheme: dark)`:
|
|
392
|
+
|
|
393
|
+
```css
|
|
394
|
+
.dark {
|
|
395
|
+
--webdrive-background: #18181b;
|
|
396
|
+
--webdrive-foreground: #f4f4f5;
|
|
397
|
+
--webdrive-border: #27272a;
|
|
398
|
+
--webdrive-primary: #fafafa;
|
|
399
|
+
--webdrive-primary-foreground: #18181b;
|
|
400
|
+
--webdrive-muted: #a1a1aa;
|
|
401
|
+
--webdrive-overlay: rgba(0, 0, 0, 0.75);
|
|
402
|
+
--webdrive-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Tailwind CSS & shadcn/ui Integration
|
|
407
|
+
|
|
408
|
+
WebDrive does not bundle or require Tailwind CSS. You can easily style the exposed classes with `@apply` in your global CSS:
|
|
409
|
+
|
|
410
|
+
```css
|
|
411
|
+
.webdrive-popover {
|
|
412
|
+
@apply rounded-xl border border-border bg-card text-card-foreground shadow-2xl p-5;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.webdrive-title {
|
|
416
|
+
@apply text-base font-semibold text-foreground tracking-tight;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.webdrive-content {
|
|
420
|
+
@apply text-sm text-muted-foreground leading-relaxed;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.webdrive-next {
|
|
424
|
+
@apply rounded-md bg-primary px-3.5 py-2 text-sm font-medium text-primary-foreground shadow-sm hover:bg-primary/90;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.webdrive-prev {
|
|
428
|
+
@apply rounded-md border border-input bg-background px-3.5 py-2 text-sm font-medium text-foreground hover:bg-accent;
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## ð Framework Integrations
|
|
435
|
+
|
|
436
|
+
### React
|
|
437
|
+
|
|
438
|
+
```tsx
|
|
439
|
+
import { useEffect, useRef } from "react";
|
|
440
|
+
import { WebDrive } from "webdrive";
|
|
441
|
+
import "webdrive/styles.css";
|
|
442
|
+
|
|
443
|
+
export function AppTour() {
|
|
444
|
+
const tourRef = useRef<WebDrive | null>(null);
|
|
445
|
+
|
|
446
|
+
useEffect(() => {
|
|
447
|
+
tourRef.current = new WebDrive({
|
|
448
|
+
id: "react-app-tour",
|
|
449
|
+
remember: true,
|
|
450
|
+
steps: [
|
|
451
|
+
{ element: "#sidebar", title: "Sidebar", description: "Quick navigation links." },
|
|
452
|
+
{ element: "#search-bar", title: "Search", description: "Search across all assets." },
|
|
453
|
+
],
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
tourRef.current.start();
|
|
457
|
+
|
|
458
|
+
return () => {
|
|
459
|
+
tourRef.current?.destroy();
|
|
460
|
+
};
|
|
461
|
+
}, []);
|
|
462
|
+
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Next.js (App Router & Pages Router)
|
|
468
|
+
|
|
469
|
+
`webdrive` is completely safe for Next.js Server Components. In the App Router, initialize WebDrive inside a client component with `"use client"`:
|
|
470
|
+
|
|
471
|
+
```tsx
|
|
472
|
+
"use client";
|
|
473
|
+
|
|
474
|
+
import { useEffect } from "react";
|
|
475
|
+
import { WebDrive } from "webdrive";
|
|
476
|
+
import "webdrive/styles.css";
|
|
477
|
+
|
|
478
|
+
export function OnboardingTour() {
|
|
479
|
+
useEffect(() => {
|
|
480
|
+
const tour = new WebDrive({
|
|
481
|
+
id: "nextjs-onboarding",
|
|
482
|
+
steps: [
|
|
483
|
+
{ element: "#hero", title: "Welcome", description: "Welcome to our Next.js app!" },
|
|
484
|
+
],
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
tour.start();
|
|
488
|
+
|
|
489
|
+
return () => {
|
|
490
|
+
tour.destroy();
|
|
491
|
+
};
|
|
492
|
+
}, []);
|
|
493
|
+
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Vue 3 / Nuxt
|
|
499
|
+
|
|
500
|
+
```vue
|
|
501
|
+
<script setup>
|
|
502
|
+
import { onMounted, onUnmounted } from "vue";
|
|
503
|
+
import { WebDrive } from "webdrive";
|
|
504
|
+
import "webdrive/styles.css";
|
|
505
|
+
|
|
506
|
+
let tour = null;
|
|
507
|
+
|
|
508
|
+
onMounted(() => {
|
|
509
|
+
tour = new WebDrive({
|
|
510
|
+
steps: [
|
|
511
|
+
{ element: "#vue-nav", title: "Navigation", description: "Explore the app." },
|
|
512
|
+
{ element: "#vue-content", title: "Content", description: "Your main dashboard." },
|
|
513
|
+
],
|
|
514
|
+
});
|
|
515
|
+
tour.start();
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
onUnmounted(() => {
|
|
519
|
+
tour?.destroy();
|
|
520
|
+
});
|
|
521
|
+
</script>
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### Angular
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
import { Component, OnInit, OnDestroy } from "@angular/core";
|
|
528
|
+
import { WebDrive } from "webdrive";
|
|
529
|
+
|
|
530
|
+
@Component({
|
|
531
|
+
selector: "app-tour",
|
|
532
|
+
template: "",
|
|
533
|
+
styleUrls: ["node_modules/webdrive/dist/webdrive.css"],
|
|
534
|
+
})
|
|
535
|
+
export class TourComponent implements OnInit, OnDestroy {
|
|
536
|
+
private tour?: WebDrive;
|
|
537
|
+
|
|
538
|
+
ngOnInit(): void {
|
|
539
|
+
this.tour = new WebDrive({
|
|
540
|
+
steps: [
|
|
541
|
+
{ element: "#angular-header", title: "Header", description: "Main application header." },
|
|
542
|
+
],
|
|
543
|
+
});
|
|
544
|
+
this.tour.start();
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
ngOnDestroy(): void {
|
|
548
|
+
this.tour?.destroy();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
### Svelte
|
|
554
|
+
|
|
555
|
+
```svelte
|
|
556
|
+
<script>
|
|
557
|
+
import { onMount, onDestroy } from "svelte";
|
|
558
|
+
import { WebDrive } from "webdrive";
|
|
559
|
+
import "webdrive/styles.css";
|
|
560
|
+
|
|
561
|
+
let tour;
|
|
562
|
+
|
|
563
|
+
onMount(() => {
|
|
564
|
+
tour = new WebDrive({
|
|
565
|
+
steps: [
|
|
566
|
+
{ element: "#svelte-intro", title: "Intro", description: "Welcome to Svelte!" }
|
|
567
|
+
],
|
|
568
|
+
});
|
|
569
|
+
tour.start();
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
onDestroy(() => {
|
|
573
|
+
tour?.destroy();
|
|
574
|
+
});
|
|
575
|
+
</script>
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## ð Running the Demo & Tests
|
|
581
|
+
|
|
582
|
+
### Run the Interactive Demo Locally
|
|
583
|
+
|
|
584
|
+
To test WebDrive interactively in your browser:
|
|
585
|
+
|
|
586
|
+
```bash
|
|
587
|
+
# Start a local static web server
|
|
588
|
+
npx serve .
|
|
589
|
+
# Open http://localhost:3000/examples/ in your browser
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Run Automated Tests
|
|
593
|
+
|
|
594
|
+
```bash
|
|
595
|
+
npm test
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### Typecheck
|
|
599
|
+
|
|
600
|
+
```bash
|
|
601
|
+
npm run typecheck
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
### Build Production Bundles
|
|
605
|
+
|
|
606
|
+
```bash
|
|
607
|
+
npm run build
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
Generates:
|
|
611
|
+
- `dist/index.js` (ES Module)
|
|
612
|
+
- `dist/index.cjs` (CommonJS)
|
|
613
|
+
- `dist/index.d.ts` (TypeScript types)
|
|
614
|
+
- `dist/webdrive.css` (Styles)
|
|
615
|
+
|
|
616
|
+
---
|
|
617
|
+
|
|
618
|
+
## ð License
|
|
619
|
+
|
|
620
|
+
MIT ÂĐ [webdrive](https://github.com/Abhi-6284/webdrive)
|