Add example for asynchronous tour
Browse files- index.html +114 -0
- src/driver.ts +43 -10
- src/state.ts +4 -4
index.html
CHANGED
|
@@ -192,6 +192,7 @@
|
|
| 192 |
<div class="buttons">
|
| 193 |
<button id="custom-classes">Custom Classes</button>
|
| 194 |
<button id="popover-hook">Popover Hook</button>
|
|
|
|
| 195 |
</div>
|
| 196 |
|
| 197 |
<h2>Tour Feature</h2>
|
|
@@ -199,6 +200,7 @@
|
|
| 199 |
<div class="buttons">
|
| 200 |
<button id="basic-tour">Animated Tour</button>
|
| 201 |
<button id="non-animated-tour">Non-Animated Tour</button>
|
|
|
|
| 202 |
</div>
|
| 203 |
|
| 204 |
<ul>
|
|
@@ -396,6 +398,97 @@ npm install driver.js</pre
|
|
| 396 |
driverObj.drive();
|
| 397 |
});
|
| 398 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 399 |
document.getElementById("basic-tour").addEventListener("click", () => {
|
| 400 |
const driverObj = driver({
|
| 401 |
steps: basicTourSteps,
|
|
@@ -524,6 +617,27 @@ npm install driver.js</pre
|
|
| 524 |
});
|
| 525 |
});
|
| 526 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 527 |
document.getElementById("custom-classes").addEventListener("click", () => {
|
| 528 |
const driverObj = driver({
|
| 529 |
popoverClass: "custom-driver-popover",
|
|
|
|
| 192 |
<div class="buttons">
|
| 193 |
<button id="custom-classes">Custom Classes</button>
|
| 194 |
<button id="popover-hook">Popover Hook</button>
|
| 195 |
+
<button id="padding-change">Padding Change</button>
|
| 196 |
</div>
|
| 197 |
|
| 198 |
<h2>Tour Feature</h2>
|
|
|
|
| 200 |
<div class="buttons">
|
| 201 |
<button id="basic-tour">Animated Tour</button>
|
| 202 |
<button id="non-animated-tour">Non-Animated Tour</button>
|
| 203 |
+
<button id="async-tour">Asynchronous Tour</button>
|
| 204 |
</div>
|
| 205 |
|
| 206 |
<ul>
|
|
|
|
| 398 |
driverObj.drive();
|
| 399 |
});
|
| 400 |
|
| 401 |
+
document.getElementById("async-tour").addEventListener("click", () => {
|
| 402 |
+
const driverObj = driver({
|
| 403 |
+
animate: true,
|
| 404 |
+
backdropColor: "blue",
|
| 405 |
+
opacity: 0.3,
|
| 406 |
+
onDeselected: (element => {
|
| 407 |
+
if (element?.classList?.contains('dynamic-el')) {
|
| 408 |
+
element?.parentElement?.removeChild(element);
|
| 409 |
+
}
|
| 410 |
+
}),
|
| 411 |
+
steps: [
|
| 412 |
+
{
|
| 413 |
+
element: ".page-header",
|
| 414 |
+
popover: {
|
| 415 |
+
title: "Async Driver.js",
|
| 416 |
+
description:
|
| 417 |
+
"Driver.js has been written from the ground up. The new version is more powerful, has less surprises, more customizable and tons of new features.",
|
| 418 |
+
side: "bottom",
|
| 419 |
+
align: "start",
|
| 420 |
+
},
|
| 421 |
+
},
|
| 422 |
+
{
|
| 423 |
+
element: ".page-header h1",
|
| 424 |
+
popover: {
|
| 425 |
+
title: "Async Test",
|
| 426 |
+
description: "By overriding `onNextClick` you get control over the tour.",
|
| 427 |
+
side: "left",
|
| 428 |
+
align: "start",
|
| 429 |
+
onNextClick: () => {
|
| 430 |
+
const newDiv = document.querySelector('.dynamic-el') || document.createElement('div');
|
| 431 |
+
|
| 432 |
+
newDiv.innerHTML = 'This is a new Element';
|
| 433 |
+
newDiv.style.display = 'block';
|
| 434 |
+
newDiv.style.padding = '20px';
|
| 435 |
+
newDiv.style.backgroundColor = 'black';
|
| 436 |
+
newDiv.style.color = 'white';
|
| 437 |
+
newDiv.style.fontSize = '14px';
|
| 438 |
+
newDiv.style.position = 'fixed';
|
| 439 |
+
newDiv.style.top = `${Math.random() * (500 - 30) + 30}px`;
|
| 440 |
+
newDiv.style.left = `${Math.random() * (500 - 30) + 30}px`;
|
| 441 |
+
newDiv.className = 'dynamic-el';
|
| 442 |
+
|
| 443 |
+
document.body.appendChild(newDiv);
|
| 444 |
+
|
| 445 |
+
driverObj.moveNext();
|
| 446 |
+
}
|
| 447 |
+
},
|
| 448 |
+
},
|
| 449 |
+
{
|
| 450 |
+
element: '.dynamic-el',
|
| 451 |
+
popover: {
|
| 452 |
+
title: "Dynamic Elements",
|
| 453 |
+
description: "This was created before we moved here"
|
| 454 |
+
}
|
| 455 |
+
},
|
| 456 |
+
{
|
| 457 |
+
element: ".page-header sup",
|
| 458 |
+
popover: {
|
| 459 |
+
title: "Improved Hooks",
|
| 460 |
+
description:
|
| 461 |
+
"Unlike the older version, new version doesn't work with z-indexes so no more positional issues.",
|
| 462 |
+
side: "bottom",
|
| 463 |
+
align: "start",
|
| 464 |
+
},
|
| 465 |
+
},
|
| 466 |
+
{
|
| 467 |
+
popover: {
|
| 468 |
+
title: "No Element",
|
| 469 |
+
description: "You can now have popovers without elements as well",
|
| 470 |
+
},
|
| 471 |
+
},
|
| 472 |
+
{
|
| 473 |
+
element: "#scrollable-area",
|
| 474 |
+
popover: {
|
| 475 |
+
title: "Scrollable Areas",
|
| 476 |
+
description: "There are no issues with scrollable element tours as well.",
|
| 477 |
+
},
|
| 478 |
+
},
|
| 479 |
+
{
|
| 480 |
+
element: "#third-scroll-paragraph",
|
| 481 |
+
popover: {
|
| 482 |
+
title: "Nested Scrolls",
|
| 483 |
+
description: "Even the nested scrollable elements work now.",
|
| 484 |
+
},
|
| 485 |
+
},
|
| 486 |
+
],
|
| 487 |
+
});
|
| 488 |
+
|
| 489 |
+
driverObj.drive();
|
| 490 |
+
});
|
| 491 |
+
|
| 492 |
document.getElementById("basic-tour").addEventListener("click", () => {
|
| 493 |
const driverObj = driver({
|
| 494 |
steps: basicTourSteps,
|
|
|
|
| 617 |
});
|
| 618 |
});
|
| 619 |
|
| 620 |
+
document.getElementById("padding-change").addEventListener("click", () => {
|
| 621 |
+
const driverObj = driver({
|
| 622 |
+
stagePadding: 0,
|
| 623 |
+
popoverOffset: 20,
|
| 624 |
+
stageRadius: 10
|
| 625 |
+
});
|
| 626 |
+
|
| 627 |
+
driverObj.highlight({
|
| 628 |
+
element: "#padding-change",
|
| 629 |
+
popover: {
|
| 630 |
+
title: "Page Title",
|
| 631 |
+
description: "Body of the popover",
|
| 632 |
+
side: "bottom",
|
| 633 |
+
align: "start",
|
| 634 |
+
onPopoverRendered: popover => {
|
| 635 |
+
popover.title.innerText = "Modified";
|
| 636 |
+
},
|
| 637 |
+
},
|
| 638 |
+
});
|
| 639 |
+
});
|
| 640 |
+
|
| 641 |
document.getElementById("custom-classes").addEventListener("click", () => {
|
| 642 |
const driverObj = driver({
|
| 643 |
popoverClass: "custom-driver-popover",
|
src/driver.ts
CHANGED
|
@@ -24,9 +24,39 @@ export function driver(options: Config = {}) {
|
|
| 24 |
destroy();
|
| 25 |
}
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
function handleArrowLeft() {
|
| 28 |
const steps = getConfig("steps") || [];
|
| 29 |
-
const currentStepIndex = getState("
|
| 30 |
if (typeof currentStepIndex === "undefined") {
|
| 31 |
return;
|
| 32 |
}
|
|
@@ -38,18 +68,19 @@ export function driver(options: Config = {}) {
|
|
| 38 |
}
|
| 39 |
|
| 40 |
function handleArrowRight() {
|
| 41 |
-
const
|
| 42 |
-
const
|
| 43 |
-
|
|
|
|
| 44 |
return;
|
| 45 |
}
|
| 46 |
|
| 47 |
-
const
|
| 48 |
-
if (
|
| 49 |
-
|
| 50 |
-
} else {
|
| 51 |
-
destroy();
|
| 52 |
}
|
|
|
|
|
|
|
| 53 |
}
|
| 54 |
|
| 55 |
function init() {
|
|
@@ -81,7 +112,7 @@ export function driver(options: Config = {}) {
|
|
| 81 |
destroy();
|
| 82 |
}
|
| 83 |
|
| 84 |
-
setState("
|
| 85 |
|
| 86 |
const currentStep = steps[stepIndex];
|
| 87 |
const hasNextStep = steps[stepIndex + 1];
|
|
@@ -152,6 +183,8 @@ export function driver(options: Config = {}) {
|
|
| 152 |
init();
|
| 153 |
drive(stepIndex);
|
| 154 |
},
|
|
|
|
|
|
|
| 155 |
highlight: (step: DriveStep) => {
|
| 156 |
init();
|
| 157 |
highlight({
|
|
|
|
| 24 |
destroy();
|
| 25 |
}
|
| 26 |
|
| 27 |
+
function moveNext() {
|
| 28 |
+
const activeIndex = getState('activeIndex');
|
| 29 |
+
const steps = getConfig('steps') || [];
|
| 30 |
+
if (typeof activeIndex === 'undefined') {
|
| 31 |
+
return;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
const nextStepIndex = activeIndex + 1;
|
| 35 |
+
if (steps[nextStepIndex]) {
|
| 36 |
+
drive(nextStepIndex);
|
| 37 |
+
} else {
|
| 38 |
+
destroy();
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
function movePrevious() {
|
| 43 |
+
const activeIndex = getState('activeIndex');
|
| 44 |
+
const steps = getConfig('steps') || [];
|
| 45 |
+
if (typeof activeIndex === 'undefined') {
|
| 46 |
+
return;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
const previousStepIndex = activeIndex - 1;
|
| 50 |
+
if (steps[previousStepIndex]) {
|
| 51 |
+
drive(previousStepIndex);
|
| 52 |
+
} else {
|
| 53 |
+
destroy();
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
function handleArrowLeft() {
|
| 58 |
const steps = getConfig("steps") || [];
|
| 59 |
+
const currentStepIndex = getState("activeIndex");
|
| 60 |
if (typeof currentStepIndex === "undefined") {
|
| 61 |
return;
|
| 62 |
}
|
|
|
|
| 68 |
}
|
| 69 |
|
| 70 |
function handleArrowRight() {
|
| 71 |
+
const activeIndex = getState("activeIndex");
|
| 72 |
+
const activeStep = getState("activeStep");
|
| 73 |
+
const activeElement = getState("activeElement");
|
| 74 |
+
if (typeof activeIndex === "undefined" || typeof activeStep === "undefined") {
|
| 75 |
return;
|
| 76 |
}
|
| 77 |
|
| 78 |
+
const onNextClick = activeStep.popover?.onNextClick || getConfig("onNextClick");
|
| 79 |
+
if (onNextClick) {
|
| 80 |
+
return onNextClick(activeElement, activeStep);
|
|
|
|
|
|
|
| 81 |
}
|
| 82 |
+
|
| 83 |
+
moveNext();
|
| 84 |
}
|
| 85 |
|
| 86 |
function init() {
|
|
|
|
| 112 |
destroy();
|
| 113 |
}
|
| 114 |
|
| 115 |
+
setState("activeIndex", stepIndex);
|
| 116 |
|
| 117 |
const currentStep = steps[stepIndex];
|
| 118 |
const hasNextStep = steps[stepIndex + 1];
|
|
|
|
| 183 |
init();
|
| 184 |
drive(stepIndex);
|
| 185 |
},
|
| 186 |
+
moveNext,
|
| 187 |
+
movePrevious,
|
| 188 |
highlight: (step: DriveStep) => {
|
| 189 |
init();
|
| 190 |
highlight({
|
src/state.ts
CHANGED
|
@@ -6,18 +6,18 @@ export type State = {
|
|
| 6 |
// Whether driver is initialized or not
|
| 7 |
isInitialized?: boolean;
|
| 8 |
// Index of the currently active step in driver tour
|
| 9 |
-
|
| 10 |
|
| 11 |
// Used to bounce the resize event
|
| 12 |
resizeTimeout?: number;
|
| 13 |
|
| 14 |
// Used while transitioning between stages
|
| 15 |
-
previousElement?: Element;
|
| 16 |
activeElement?: Element;
|
| 17 |
-
transitionCallback?: () => void;
|
| 18 |
-
|
| 19 |
activeStep?: DriveStep;
|
|
|
|
| 20 |
previousStep?: DriveStep;
|
|
|
|
|
|
|
| 21 |
|
| 22 |
activeStagePosition?: StageDefinition;
|
| 23 |
stageSvg?: SVGSVGElement;
|
|
|
|
| 6 |
// Whether driver is initialized or not
|
| 7 |
isInitialized?: boolean;
|
| 8 |
// Index of the currently active step in driver tour
|
| 9 |
+
activeIndex?: number;
|
| 10 |
|
| 11 |
// Used to bounce the resize event
|
| 12 |
resizeTimeout?: number;
|
| 13 |
|
| 14 |
// Used while transitioning between stages
|
|
|
|
| 15 |
activeElement?: Element;
|
|
|
|
|
|
|
| 16 |
activeStep?: DriveStep;
|
| 17 |
+
previousElement?: Element;
|
| 18 |
previousStep?: DriveStep;
|
| 19 |
+
transitionCallback?: () => void;
|
| 20 |
+
|
| 21 |
|
| 22 |
activeStagePosition?: StageDefinition;
|
| 23 |
stageSvg?: SVGSVGElement;
|