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 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
+ [![npm version](https://img.shields.io/npm/v/webdrive.svg?style=flat-square)](https://www.npmjs.com/package/webdrive)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](LICENSE)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Strict-blue?style=flat-square)](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">&times;</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)