diff --git a/src/driver.ts b/src/driver.ts index 9108aff..bb05fd4 100644 --- a/src/driver.ts +++ b/src/driver.ts @@ -6,6 +6,8 @@ import { destroyHighlight, highlight } from "./highlight"; import { destroyEmitter, listen } from "./emitter"; import { getState, resetState, setState } from "./state"; import "./driver.css"; +import { InvalidDriverActionError } from "./errors"; +import { DRIVER_MARKER, isDriver } from "./utils"; export type DriveStep = { element?: string | Element | (() => Element); @@ -38,6 +40,7 @@ export interface Driver { hasPreviousStep: () => boolean; highlight: (step: DriveStep) => void; destroy: () => void; + [DRIVER_MARKER]: true; } export function driver(options: Config = {}): Driver { @@ -72,6 +75,10 @@ export function driver(options: Config = {}): Driver { } const nextStepIndex = activeIndex + 1; + // @ts-ignore + if (isDriver(this) && nextStepIndex >= steps.length) { + throw new InvalidDriverActionError("Cannot move to next step. Already at the last step."); + } if (steps[nextStepIndex]) { drive(nextStepIndex); } else { @@ -87,6 +94,9 @@ export function driver(options: Config = {}): Driver { } const previousStepIndex = activeIndex - 1; + if (previousStepIndex < 0) { + throw new InvalidDriverActionError("Cannot move to previous step. Already at the first step."); + } if (steps[previousStepIndex]) { drive(previousStepIndex); } else { @@ -97,6 +107,10 @@ export function driver(options: Config = {}): Driver { function moveTo(index: number) { const steps = getConfig("steps") || []; + if (index < 0 || index >= steps.length) { + throw new RangeError(); + } + if (steps[index]) { drive(index); } else { @@ -182,10 +196,11 @@ export function driver(options: Config = {}): Driver { destroy(); return; } - + if (stepIndex < 0 || stepIndex >= steps.length) { + throw new RangeError(); + } if (!steps[stepIndex]) { destroy(); - return; } @@ -370,6 +385,7 @@ export function driver(options: Config = {}): Driver { destroy: () => { destroy(false); }, + [DRIVER_MARKER]: true, }; setCurrentDriver(api); diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 0000000..3d25a81 --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,12 @@ +export class InvalidDriverActionError extends Error { + constructor(errorMessage: string) { + super(errorMessage); + this.name = "InvalidDriverActionError"; + + Object.setPrototypeOf(this, InvalidDriverActionError.prototype); + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, InvalidDriverActionError); + } + } +} diff --git a/src/utils.ts b/src/utils.ts index 1ff37da..25cf6f9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,5 @@ import { getConfig } from "./config"; +import { Driver } from "./driver"; export function easeInOutQuad(elapsed: number, initialValue: number, amountOfChange: number, duration: number): number { if ((elapsed /= duration / 2) < 1) { @@ -65,3 +66,9 @@ function isElementInView(element: Element) { export function isElementVisible(el: HTMLElement) { return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length); } + +export const DRIVER_MARKER = Symbol("Driver"); + +export function isDriver(obj: any): obj is Driver { + return obj && obj[DRIVER_MARKER] === true; +}