mirror of
https://github.com/kamranahmedse/driver.js.git
synced 2026-10-31 03:52:13 +08:00
Add tour functionality
This commit is contained in:
+87
-22
@@ -183,6 +183,7 @@
|
||||
<button id="next-prev-button">Next Previous Buttons</button>
|
||||
<button id="close-button">Close Buttons</button>
|
||||
<button id="button-texts">Button Texts</button>
|
||||
<button id="disabled-buttons">Disabled Buttons</button>
|
||||
<button id="button-config-events">Button Listeners</button>
|
||||
</div>
|
||||
|
||||
@@ -193,6 +194,12 @@
|
||||
<button id="popover-hook">Popover Hook</button>
|
||||
</div>
|
||||
|
||||
<h2>Tour Feature</h2>
|
||||
<p>Examples below show the tour usage of driver.js.</p>
|
||||
<div class="buttons">
|
||||
<button id="basic-tour">Basic Tour</button>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<li>Written in TypeScript</li>
|
||||
<li>Lightweight — only 5kb gzipped</li>
|
||||
@@ -324,6 +331,51 @@ npm install driver.js</pre
|
||||
<script type="module">
|
||||
import { driver } from "./src/driver.ts";
|
||||
|
||||
document.getElementById("basic-tour").addEventListener("click", () => {
|
||||
const driverObj = driver({
|
||||
steps: [
|
||||
{
|
||||
element: ".page-header",
|
||||
popover: {
|
||||
title: "New and Improved Driver.js",
|
||||
description:
|
||||
"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.",
|
||||
side: "bottom",
|
||||
align: "start",
|
||||
},
|
||||
},
|
||||
{
|
||||
element: ".page-header h1",
|
||||
popover: {
|
||||
title: "No Stacking Issues",
|
||||
description:
|
||||
"Unlike the older version, new version doesn't work with z-indexes so no more positional issues.",
|
||||
side: "left",
|
||||
align: "start",
|
||||
},
|
||||
},
|
||||
{
|
||||
element: ".page-header sup",
|
||||
popover: {
|
||||
title: "Improved Hooks",
|
||||
description:
|
||||
"Unlike the older version, new version doesn't work with z-indexes so no more positional issues.",
|
||||
side: "bottom",
|
||||
align: "start",
|
||||
},
|
||||
},
|
||||
{
|
||||
popover: {
|
||||
title: "No Element",
|
||||
description: "You can now have popovers without elements as well",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
driverObj.drive();
|
||||
});
|
||||
|
||||
document.getElementById("no-buttons").addEventListener("click", () => {
|
||||
const driverObj = driver({});
|
||||
|
||||
@@ -426,37 +478,50 @@ npm install driver.js</pre
|
||||
|
||||
document.getElementById("popover-hook").addEventListener("click", () => {
|
||||
const driverObj = driver({
|
||||
onPopoverRendered: (popover) => {
|
||||
popover.title.innerText = 'Modified Parent';
|
||||
}
|
||||
onPopoverRendered: popover => {
|
||||
popover.title.innerText = "Modified Parent";
|
||||
},
|
||||
});
|
||||
driverObj.highlight({
|
||||
element: '.page-header',
|
||||
element: ".page-header",
|
||||
popover: {
|
||||
title: 'Page Title',
|
||||
description: 'Body of the popover',
|
||||
side: 'bottom',
|
||||
align: 'start',
|
||||
onPopoverRendered: (popover) => {
|
||||
popover.title.innerText = 'Modified';
|
||||
}
|
||||
}
|
||||
})
|
||||
title: "Page Title",
|
||||
description: "Body of the popover",
|
||||
side: "bottom",
|
||||
align: "start",
|
||||
onPopoverRendered: popover => {
|
||||
popover.title.innerText = "Modified";
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("custom-classes").addEventListener("click", () => {
|
||||
const driverObj = driver({
|
||||
popoverClass: "custom-driver-popover"
|
||||
})
|
||||
popoverClass: "custom-driver-popover",
|
||||
});
|
||||
|
||||
driverObj.highlight({
|
||||
popover: {
|
||||
popoverClass: "custom-driver-popover",
|
||||
title: "Custom Classes",
|
||||
description: "Popover and buttons have custom classes",
|
||||
showButtons: ["close", "next", "previous"]
|
||||
}
|
||||
})
|
||||
showButtons: ["close", "next", "previous"],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("disabled-buttons").addEventListener("click", () => {
|
||||
const driverObj = driver();
|
||||
driverObj.highlight({
|
||||
element: "#disabled-buttons",
|
||||
popover: {
|
||||
title: "Disable Buttons",
|
||||
description: "You can selectively disable buttons as well",
|
||||
showButtons: ["next", "previous", "close"],
|
||||
disableButtons: ["next", "previous"],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("button-config-events").addEventListener("click", () => {
|
||||
@@ -623,19 +688,19 @@ npm install driver.js</pre
|
||||
const driverObj = driver({
|
||||
animate: true,
|
||||
onDeselected: (element, step) => {
|
||||
const elementText = element?.textContent?.slice(0, 10) || ' - N/A -';
|
||||
const elementText = element?.textContent?.slice(0, 10) || " - N/A -";
|
||||
console.log(`Deselected: ${elementText}\n${JSON.stringify(step)}`);
|
||||
},
|
||||
onHighlightStarted: (element, step) => {
|
||||
const elementText = element?.textContent?.slice(0, 10) || ' - N/A -';
|
||||
const elementText = element?.textContent?.slice(0, 10) || " - N/A -";
|
||||
console.log(`Highlight Started: ${elementText}\n${JSON.stringify(step)}`);
|
||||
},
|
||||
onHighlighted: (element, step) => {
|
||||
const elementText = element?.textContent?.slice(0, 10) || ' - N/A -';
|
||||
const elementText = element?.textContent?.slice(0, 10) || " - N/A -";
|
||||
console.log(`Highlighted: ${elementText}\n${JSON.stringify(step)}`);
|
||||
},
|
||||
onDestroyed: (element, step) => {
|
||||
const elementText = element?.textContent?.slice(0, 10) || ' - N/A -';
|
||||
const elementText = element?.textContent?.slice(0, 10) || " - N/A -";
|
||||
console.log(`Destroyed: ${elementText}\n${JSON.stringify(step)}`);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@ import { DriveStep } from "./driver";
|
||||
import {AllowedButtons, PopoverDOM} from "./popover";
|
||||
|
||||
export type Config = {
|
||||
steps?: DriveStep[];
|
||||
|
||||
animate?: boolean;
|
||||
backdropColor?: string;
|
||||
smoothScroll?: boolean;
|
||||
@@ -15,11 +17,13 @@ export type Config = {
|
||||
popoverClass?: string;
|
||||
popoverOffset?: number;
|
||||
showButtons?: AllowedButtons[];
|
||||
disableButtons?: AllowedButtons[];
|
||||
|
||||
// Button texts
|
||||
nextBtnText?: string;
|
||||
prevBtnText?: string;
|
||||
closeBtnText?: string;
|
||||
doneBtnText?: string;
|
||||
|
||||
// Called after the popover is rendered
|
||||
onPopoverRendered?: (popover: PopoverDOM) => void;
|
||||
@@ -49,6 +53,7 @@ export function configure(config: Config = {}) {
|
||||
stageRadius: 5,
|
||||
popoverOffset: 10,
|
||||
showButtons: ["next", "previous", "close"],
|
||||
disableButtons: [],
|
||||
backdropColor: "#000",
|
||||
...config,
|
||||
};
|
||||
|
||||
+48
-2
@@ -1,4 +1,4 @@
|
||||
import { destroyPopover, Popover } from "./popover";
|
||||
import { AllowedButtons, destroyPopover, Popover } from "./popover";
|
||||
import { destroyStage } from "./stage";
|
||||
import { destroyEvents, initEvents, requireRefresh } from "./events";
|
||||
import { Config, configure, getConfig } from "./config";
|
||||
@@ -38,6 +38,49 @@ export function driver(options: Config = {}) {
|
||||
listen("escapePress", handleClose);
|
||||
}
|
||||
|
||||
function drive(stepIndex: number = 0) {
|
||||
const steps = getConfig("steps");
|
||||
if (!steps) {
|
||||
console.error("No steps to drive through");
|
||||
destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!steps[stepIndex]) {
|
||||
console.warn(`Step not found at index: ${stepIndex}`);
|
||||
destroy();
|
||||
}
|
||||
|
||||
const currentStep = steps[stepIndex];
|
||||
const hasNextStep = steps[stepIndex + 1];
|
||||
const hasPreviousStep = steps[stepIndex - 1];
|
||||
|
||||
const doneBtnText = currentStep.popover?.doneBtnText || getConfig("doneBtnText") || "Done";
|
||||
|
||||
highlight({
|
||||
...currentStep,
|
||||
popover: {
|
||||
showButtons: ["next", "previous", "close"],
|
||||
nextBtnText: !hasNextStep ? doneBtnText : undefined,
|
||||
disableButtons: [...(!hasPreviousStep ? ["previous" as AllowedButtons] : [])],
|
||||
onNextClick: () => {
|
||||
if (!hasNextStep) {
|
||||
destroy();
|
||||
} else {
|
||||
drive(stepIndex + 1);
|
||||
}
|
||||
},
|
||||
onPrevClick: () => {
|
||||
drive(stepIndex - 1);
|
||||
},
|
||||
onCloseClick: () => {
|
||||
destroy();
|
||||
},
|
||||
...(currentStep?.popover || {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
const activeElement = getState("activeElement");
|
||||
const activeStep = getState("activeStep");
|
||||
@@ -72,7 +115,10 @@ export function driver(options: Config = {}) {
|
||||
refresh: () => {
|
||||
requireRefresh();
|
||||
},
|
||||
drive: (steps: DriveStep[]) => console.log(steps),
|
||||
drive: (stepIndex: number = 0) => {
|
||||
init();
|
||||
drive(stepIndex);
|
||||
},
|
||||
highlight: (step: DriveStep) => {
|
||||
init();
|
||||
highlight({
|
||||
|
||||
+27
-18
@@ -11,11 +11,12 @@ export type AllowedButtons = "next" | "previous" | "close";
|
||||
|
||||
export type Popover = {
|
||||
title?: string;
|
||||
description: string;
|
||||
description?: string;
|
||||
side?: Side;
|
||||
align?: Alignment;
|
||||
|
||||
showButtons?: AllowedButtons[];
|
||||
disableButtons?: AllowedButtons[];
|
||||
|
||||
popoverClass?: string;
|
||||
|
||||
@@ -57,15 +58,19 @@ export function hidePopover() {
|
||||
|
||||
export function renderPopover(element: Element, step: DriveStep) {
|
||||
let popover = getState("popover");
|
||||
if (!popover) {
|
||||
popover = createPopover();
|
||||
document.body.appendChild(popover.wrapper);
|
||||
if (popover) {
|
||||
document.body.removeChild(popover.wrapper);
|
||||
}
|
||||
|
||||
popover = createPopover();
|
||||
document.body.appendChild(popover.wrapper);
|
||||
|
||||
const {
|
||||
title,
|
||||
description,
|
||||
showButtons: popoverShowButtons = undefined,
|
||||
showButtons,
|
||||
disableButtons,
|
||||
|
||||
// doneBtnText = 'Done',
|
||||
closeBtnText = getConfig("closeBtnText") || "Close",
|
||||
nextBtnText = getConfig("nextBtnText") || "Next →",
|
||||
@@ -90,27 +95,31 @@ export function renderPopover(element: Element, step: DriveStep) {
|
||||
popover.description.style.display = "none";
|
||||
}
|
||||
|
||||
const showButtonsConfig: AllowedButtons[] =
|
||||
popoverShowButtons !== undefined ? popoverShowButtons : getConfig("showButtons")!;
|
||||
const showButtonsConfig: AllowedButtons[] = showButtons || getConfig("showButtons")!;
|
||||
|
||||
if (showButtonsConfig?.length! > 0) {
|
||||
popover.footer.style.display = "flex";
|
||||
|
||||
if (!showButtonsConfig.includes("next")) {
|
||||
popover.nextButton.style.display = "none";
|
||||
}
|
||||
|
||||
if (!showButtonsConfig.includes("previous")) {
|
||||
popover.previousButton.style.display = "none";
|
||||
}
|
||||
|
||||
if (!showButtonsConfig.includes("close")) {
|
||||
popover.closeButton.style.display = "none";
|
||||
}
|
||||
popover.nextButton.style.display = showButtonsConfig.includes("next") ? 'block' : 'none';
|
||||
popover.previousButton.style.display = showButtonsConfig.includes("previous") ? 'block' : 'none';
|
||||
popover.closeButton.style.display = showButtonsConfig.includes("close") ? 'block' : 'none';
|
||||
} else {
|
||||
popover.footer.style.display = "none";
|
||||
}
|
||||
|
||||
const disabledButtonsConfig: AllowedButtons[] = disableButtons || getConfig('disableButtons')! || [];
|
||||
if (disabledButtonsConfig?.includes('next')) {
|
||||
popover.nextButton.classList.add("driver-popover-btn-disabled")
|
||||
}
|
||||
|
||||
if (disabledButtonsConfig?.includes('previous')) {
|
||||
popover.previousButton.classList.add("driver-popover-btn-disabled")
|
||||
}
|
||||
|
||||
if (disabledButtonsConfig?.includes('close')) {
|
||||
popover.closeButton.classList.add("driver-popover-btn-disabled")
|
||||
}
|
||||
|
||||
// Reset the popover position
|
||||
const popoverWrapper = popover.wrapper;
|
||||
popoverWrapper.style.display = "block";
|
||||
|
||||
@@ -97,6 +97,11 @@
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.driver-popover-footer .driver-popover-btn-disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Disable the scrolling of parent element if it has an active element*/
|
||||
:not(body):has(> .driver-active-element) {
|
||||
overflow: hidden !important;
|
||||
|
||||
Reference in New Issue
Block a user