kamrify commited on
Commit
66a740c
·
1 Parent(s): 8ff0f16

Add configuration option

Browse files
Files changed (7) hide show
  1. index.html +12 -11
  2. src/config.ts +18 -0
  3. src/driver.ts +15 -3
  4. src/events.ts +53 -0
  5. src/highlight.ts +3 -1
  6. src/stage.ts +17 -2
  7. src/style.css +0 -1
index.html CHANGED
@@ -77,7 +77,7 @@
77
  <div class="buttons">
78
  <button id="highlight-btn">Simple Highlight</button>
79
  <button id="tour-btn">Start Tour</button>
80
- <button id="highlight-destroy">Destroy after 2s</button>
81
  </div>
82
 
83
  <ul>
@@ -90,24 +90,25 @@
90
  <script type="module">
91
  import { driver } from "./src/driver.ts";
92
 
93
- const driverObj = driver();
 
 
94
 
95
  document.getElementById("highlight-btn").addEventListener("click", () => {
96
  driverObj.highlight({
97
  element: "h1",
98
  });
99
- });
100
 
101
- document
102
- .getElementById("highlight-destroy")
103
- .addEventListener("click", () => {
104
  driverObj.highlight({
105
- element: "h1",
106
  });
107
- setTimeout(() => {
108
- driverObj.destroy();
109
- }, 2000);
110
- });
 
 
111
  </script>
112
  </body>
113
  </html>
 
77
  <div class="buttons">
78
  <button id="highlight-btn">Simple Highlight</button>
79
  <button id="tour-btn">Start Tour</button>
80
+ <button id="destroy-btn">Destroy</button>
81
  </div>
82
 
83
  <ul>
 
90
  <script type="module">
91
  import { driver } from "./src/driver.ts";
92
 
93
+ const driverObj = driver({
94
+ animate: true,
95
+ });
96
 
97
  document.getElementById("highlight-btn").addEventListener("click", () => {
98
  driverObj.highlight({
99
  element: "h1",
100
  });
 
101
 
102
+ window.setTimeout(() => {
 
 
103
  driverObj.highlight({
104
+ element: ".buttons",
105
  });
106
+ }, 1000);
107
+ });
108
+
109
+ document.getElementById("destroy-btn").addEventListener("click", () => {
110
+ driverObj.destroy();
111
+ });
112
  </script>
113
  </body>
114
  </html>
src/config.ts ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export type Config = {
2
+ animate?: boolean;
3
+ };
4
+
5
+ let currentConfig: Config = {};
6
+
7
+ export function configure(config: Config = {}) {
8
+ currentConfig = {
9
+ animate: true,
10
+ ...config,
11
+ };
12
+ }
13
+
14
+ export function getConfig(): Config;
15
+ export function getConfig<K extends keyof Config>(key: K): Config[K];
16
+ export function getConfig<K extends keyof Config>(key?: K) {
17
+ return key ? currentConfig[key] : currentConfig;
18
+ }
src/driver.ts CHANGED
@@ -1,21 +1,33 @@
1
  import { initEvents, destroyEvents } from "./events";
2
  import { destroyHighlight, highlight } from "./highlight";
3
  import { destroyStage } from "./stage";
 
 
4
  import "./style.css";
5
 
6
  export type DriveStep = {
7
  element?: string | Element;
8
  };
9
 
10
- export function driver() {
 
 
 
 
11
  function init() {
12
- document.body.classList.add("driver-active", "driver-fade");
 
 
 
13
 
14
  initEvents();
15
  }
16
 
17
  function destroy() {
18
- document.body.classList.remove("driver-active", "driver-fade");
 
 
 
19
 
20
  destroyEvents();
21
  destroyHighlight();
 
1
  import { initEvents, destroyEvents } from "./events";
2
  import { destroyHighlight, highlight } from "./highlight";
3
  import { destroyStage } from "./stage";
4
+ import { configure, Config, getConfig } from "./config";
5
+
6
  import "./style.css";
7
 
8
  export type DriveStep = {
9
  element?: string | Element;
10
  };
11
 
12
+ export function driver(options: Config = {}) {
13
+ configure(options);
14
+
15
+ const shouldAnimate = getConfig("animate");
16
+
17
  function init() {
18
+ document.body.classList.add(
19
+ "driver-active",
20
+ shouldAnimate ? "driver-fade" : "driver-simple"
21
+ );
22
 
23
  initEvents();
24
  }
25
 
26
  function destroy() {
27
+ document.body.classList.remove(
28
+ "driver-active",
29
+ shouldAnimate ? "driver-fade" : "driver-simple"
30
+ );
31
 
32
  destroyEvents();
33
  destroyHighlight();
src/events.ts CHANGED
@@ -10,6 +10,59 @@ function onResize() {
10
  resizeTimeout = window.requestAnimationFrame(refreshActiveHighlight);
11
  }
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  export function initEvents() {
14
  window.addEventListener("resize", onResize);
15
  }
 
10
  resizeTimeout = window.requestAnimationFrame(refreshActiveHighlight);
11
  }
12
 
13
+ /**
14
+ * Attaches click handler to the elements created by driver.js. It makes
15
+ * sure to give the listener the first chance to handle the event, and
16
+ * prevents all other pointer-events to make sure no external-library
17
+ * ever knows the click happened.
18
+ *
19
+ * @param {Element} element Element to listen for click events
20
+ * @param {(pointer: MouseEvent | PointerEvent) => void} listener Click handler
21
+ * @param {(target: HTMLElement) => boolean} shouldPreventDefault Whether to prevent default action i.e. link clicks etc
22
+ */
23
+ export function onDriverClick(
24
+ element: Element,
25
+ listener: (pointer: MouseEvent | PointerEvent) => void,
26
+ shouldPreventDefault?: (target: HTMLElement) => boolean
27
+ ) {
28
+ const listenerWrapper = (
29
+ e: MouseEvent | PointerEvent,
30
+ listener?: (pointer: MouseEvent | PointerEvent) => void
31
+ ) => {
32
+ const target = e.target as HTMLElement;
33
+ if (!element.contains(target)) {
34
+ return;
35
+ }
36
+
37
+ if (!shouldPreventDefault || shouldPreventDefault(target)) {
38
+ e.preventDefault();
39
+ }
40
+
41
+ e.stopPropagation();
42
+ e.stopImmediatePropagation();
43
+
44
+ listener?.(e);
45
+ };
46
+
47
+ // We want to be the absolute first one to hear about the event
48
+ const useCapture = true;
49
+
50
+ // Events to disable
51
+ document.addEventListener("pointerdown", listenerWrapper, useCapture);
52
+ document.addEventListener("mousedown", listenerWrapper, useCapture);
53
+ document.addEventListener("pointerup", listenerWrapper, useCapture);
54
+ document.addEventListener("mouseup", listenerWrapper, useCapture);
55
+
56
+ // Actual click handler
57
+ document.addEventListener(
58
+ "click",
59
+ e => {
60
+ listenerWrapper(e, listener);
61
+ },
62
+ useCapture
63
+ );
64
+ }
65
+
66
  export function initEvents() {
67
  window.addEventListener("resize", onResize);
68
  }
src/highlight.ts CHANGED
@@ -1,5 +1,6 @@
1
  import { DriveStep } from "./driver";
2
  import { refreshStage, trackActiveElement, transitionStage } from "./stage";
 
3
 
4
  let previousHighlight: Element | undefined;
5
  let activeHighlight: Element | undefined;
@@ -32,6 +33,7 @@ export function refreshActiveHighlight() {
32
  function transferHighlight(from: Element, to: Element) {
33
  const duration = 400;
34
  const start = Date.now();
 
35
  isTransitioning = true;
36
 
37
  const animate = () => {
@@ -41,7 +43,7 @@ function transferHighlight(from: Element, to: Element) {
41
 
42
  const elapsed = Date.now() - start;
43
 
44
- if (elapsed < duration) {
45
  transitionStage(elapsed, duration, from, to);
46
  } else {
47
  trackActiveElement(to);
 
1
  import { DriveStep } from "./driver";
2
  import { refreshStage, trackActiveElement, transitionStage } from "./stage";
3
+ import { getConfig } from "./config";
4
 
5
  let previousHighlight: Element | undefined;
6
  let activeHighlight: Element | undefined;
 
33
  function transferHighlight(from: Element, to: Element) {
34
  const duration = 400;
35
  const start = Date.now();
36
+
37
  isTransitioning = true;
38
 
39
  const animate = () => {
 
43
 
44
  const elapsed = Date.now() - start;
45
 
46
+ if (getConfig("animate") && elapsed < duration) {
47
  transitionStage(elapsed, duration, from, to);
48
  } else {
49
  trackActiveElement(to);
src/stage.ts CHANGED
@@ -1,4 +1,5 @@
1
  import { easeInOutQuad } from "./math";
 
2
 
3
  export type StageDefinition = {
4
  x: number;
@@ -95,10 +96,24 @@ export function refreshStage() {
95
  stageSvg.setAttribute("viewBox", `0 0 ${windowX} ${windowY}`);
96
  }
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  function renderStage(stagePosition: StageDefinition) {
99
  if (!stageSvg) {
100
- stageSvg = createStageSvg(stagePosition);
101
- document.body.appendChild(stageSvg);
102
 
103
  return;
104
  }
 
1
  import { easeInOutQuad } from "./math";
2
+ import { onDriverClick } from "./events";
3
 
4
  export type StageDefinition = {
5
  x: number;
 
96
  stageSvg.setAttribute("viewBox", `0 0 ${windowX} ${windowY}`);
97
  }
98
 
99
+ function mountStage(stagePosition: StageDefinition) {
100
+ stageSvg = createStageSvg(stagePosition);
101
+
102
+ document.body.appendChild(stageSvg);
103
+
104
+ onDriverClick(stageSvg, e => {
105
+ const target = e.target as SVGElement;
106
+ if (target.tagName !== "path") {
107
+ return;
108
+ }
109
+
110
+ console.log("Overlay clicked");
111
+ });
112
+ }
113
+
114
  function renderStage(stagePosition: StageDefinition) {
115
  if (!stageSvg) {
116
+ mountStage(stagePosition);
 
117
 
118
  return;
119
  }
src/style.css CHANGED
@@ -9,7 +9,6 @@
9
  .driver-active .driver-active-element,
10
  .driver-active .driver-active-element * {
11
  pointer-events: auto;
12
- cursor: auto;
13
  }
14
 
15
  @keyframes animate-fade-in {
 
9
  .driver-active .driver-active-element,
10
  .driver-active .driver-active-element * {
11
  pointer-events: auto;
 
12
  }
13
 
14
  @keyframes animate-fade-in {