kamrify commited on
Commit
e5cb552
·
1 Parent(s): 9bcba14

Refactor driver hooks

Browse files
Files changed (7) hide show
  1. index.html +1 -1
  2. src/config.ts +11 -9
  3. src/driver.ts +16 -4
  4. src/events.ts +2 -2
  5. src/highlight.ts +18 -6
  6. src/stage.ts +8 -8
  7. src/state.ts +5 -8
index.html CHANGED
@@ -931,7 +931,7 @@ npm install driver.js</pre
931
  onDeselected: (element, step) => {
932
  console.log("Deselected element", element, step);
933
  },
934
- onHighlightStarted: (element, step) => {
935
  console.log("Started highlighting element", element, step);
936
  },
937
  onHighlighted: (element, step) => {
 
931
  onDeselected: (element, step) => {
932
  console.log("Deselected element", element, step);
933
  },
934
+ onHighlightStarted: (element, step, { config, state }) => {
935
  console.log("Started highlighting element", element, step);
936
  },
937
  onHighlighted: (element, step) => {
src/config.ts CHANGED
@@ -1,5 +1,8 @@
1
  import { DriveStep } from "./driver";
2
  import { AllowedButtons, PopoverDOM } from "./popover";
 
 
 
3
 
4
  export type Config = {
5
  steps?: DriveStep[];
@@ -31,17 +34,16 @@ export type Config = {
31
  onPopoverRendered?: (popover: PopoverDOM) => void;
32
 
33
  // State based callbacks, called upon state changes
34
- onOverlayClick?: (element: Element | undefined, step: DriveStep) => void;
35
- onHighlightStarted?: (element: Element | undefined, step: DriveStep) => void;
36
- onHighlighted?: (element: Element | undefined, step: DriveStep) => void;
37
- onDeselected?: (element: Element | undefined, step: DriveStep) => void;
38
- onDestroyStarted?: (element: Element | undefined, step: DriveStep) => void;
39
- onDestroyed?: (element: Element | undefined, step: DriveStep) => void;
40
 
41
  // Event based callbacks, called upon events
42
- onNextClick?: (element: Element | undefined, step: DriveStep) => void;
43
- onPrevClick?: (element: Element | undefined, step: DriveStep) => void;
44
- onCloseClick?: (element: Element | undefined, step: DriveStep) => void;
45
  };
46
 
47
  let currentConfig: Config = {};
 
1
  import { DriveStep } from "./driver";
2
  import { AllowedButtons, PopoverDOM } from "./popover";
3
+ import { State } from "./state";
4
+
5
+ type DriverHook = (element: Element | undefined, step: DriveStep, opts: { config: Config; state: State }) => void;
6
 
7
  export type Config = {
8
  steps?: DriveStep[];
 
34
  onPopoverRendered?: (popover: PopoverDOM) => void;
35
 
36
  // State based callbacks, called upon state changes
37
+ onHighlightStarted?: DriverHook;
38
+ onHighlighted?: DriverHook;
39
+ onDeselected?: DriverHook;
40
+ onDestroyStarted?: DriverHook;
41
+ onDestroyed?: DriverHook;
 
42
 
43
  // Event based callbacks, called upon events
44
+ onNextClick?: DriverHook;
45
+ onPrevClick?: DriverHook;
46
+ onCloseClick?: DriverHook;
47
  };
48
 
49
  let currentConfig: Config = {};
src/driver.ts CHANGED
@@ -78,7 +78,10 @@ export function driver(options: Config = {}) {
78
 
79
  const onNextClick = activeStep.popover?.onNextClick || getConfig("onNextClick");
80
  if (onNextClick) {
81
- return onNextClick(activeElement, activeStep);
 
 
 
82
  }
83
 
84
  moveNext();
@@ -166,7 +169,10 @@ export function driver(options: Config = {}) {
166
  // the hook for when user calls `destroy`, driver will get into infinite loop
167
  // not causing tour to be destroyed.
168
  if (withOnDestroyStartedHook && onDestroyStarted) {
169
- onDestroyStarted(activeElement, activeStep!);
 
 
 
170
  return;
171
  }
172
 
@@ -186,11 +192,17 @@ export function driver(options: Config = {}) {
186
  if (activeElement && activeStep) {
187
  const isActiveDummyElement = activeElement.id === "driver-dummy-element";
188
  if (onDeselected) {
189
- onDeselected(isActiveDummyElement ? undefined : activeElement, activeStep);
 
 
 
190
  }
191
 
192
  if (onDestroyed) {
193
- onDestroyed(isActiveDummyElement ? undefined : activeElement, activeStep);
 
 
 
194
  }
195
  }
196
  }
 
78
 
79
  const onNextClick = activeStep.popover?.onNextClick || getConfig("onNextClick");
80
  if (onNextClick) {
81
+ return onNextClick(activeElement, activeStep, {
82
+ config: getConfig(),
83
+ state: getState(),
84
+ });
85
  }
86
 
87
  moveNext();
 
169
  // the hook for when user calls `destroy`, driver will get into infinite loop
170
  // not causing tour to be destroyed.
171
  if (withOnDestroyStartedHook && onDestroyStarted) {
172
+ onDestroyStarted(activeElement, activeStep!, {
173
+ config: getConfig(),
174
+ state: getState(),
175
+ });
176
  return;
177
  }
178
 
 
192
  if (activeElement && activeStep) {
193
  const isActiveDummyElement = activeElement.id === "driver-dummy-element";
194
  if (onDeselected) {
195
+ onDeselected(isActiveDummyElement ? undefined : activeElement, activeStep, {
196
+ config: getConfig(),
197
+ state: getState(),
198
+ });
199
  }
200
 
201
  if (onDestroyed) {
202
+ onDestroyed(isActiveDummyElement ? undefined : activeElement, activeStep, {
203
+ config: getConfig(),
204
+ state: getState(),
205
+ });
206
  }
207
  }
208
  }
src/events.ts CHANGED
@@ -4,12 +4,12 @@ import { getState, setState } from "./state";
4
  import { getConfig } from "./config";
5
 
6
  export function requireRefresh() {
7
- const resizeTimeout = getState("resizeTimeout");
8
  if (resizeTimeout) {
9
  window.cancelAnimationFrame(resizeTimeout);
10
  }
11
 
12
- setState("resizeTimeout", window.requestAnimationFrame(refreshActiveHighlight));
13
  }
14
 
15
  function onKeyup(e: KeyboardEvent) {
 
4
  import { getConfig } from "./config";
5
 
6
  export function requireRefresh() {
7
+ const resizeTimeout = getState("__resizeTimeout");
8
  if (resizeTimeout) {
9
  window.cancelAnimationFrame(resizeTimeout);
10
  }
11
 
12
+ setState("__resizeTimeout", window.requestAnimationFrame(refreshActiveHighlight));
13
  }
14
 
15
  function onKeyup(e: KeyboardEvent) {
src/highlight.ts CHANGED
@@ -74,12 +74,21 @@ function transferHighlight(toElement: Element, toStep: DriveStep) {
74
  const highlightedHook = getConfig("onHighlighted");
75
  const deselectedHook = fromStep?.onDeselected || getConfig("onDeselected");
76
 
 
 
 
77
  if (!isFirstHighlight && deselectedHook) {
78
- deselectedHook(isFromDummyElement ? undefined : fromElement, fromStep!);
 
 
 
79
  }
80
 
81
  if (highlightStartedHook) {
82
- highlightStartedHook(isToDummyElement ? undefined : toElement, toStep);
 
 
 
83
  }
84
 
85
  const hasDelayedPopover = !isFirstHighlight && isAnimatedTour;
@@ -88,7 +97,7 @@ function transferHighlight(toElement: Element, toStep: DriveStep) {
88
  hidePopover();
89
 
90
  const animate = () => {
91
- const transitionCallback = getState("transitionCallback");
92
 
93
  // This makes sure that the repeated calls to transferHighlight
94
  // don't interfere with each other. Only the last call will be
@@ -112,10 +121,13 @@ function transferHighlight(toElement: Element, toStep: DriveStep) {
112
  trackActiveElement(toElement);
113
 
114
  if (highlightedHook) {
115
- highlightedHook(isToDummyElement ? undefined : toElement, toStep);
 
 
 
116
  }
117
 
118
- setState("transitionCallback", undefined);
119
  setState("previousStep", fromStep);
120
  setState("previousElement", fromElement);
121
  setState("activeStep", toStep);
@@ -125,7 +137,7 @@ function transferHighlight(toElement: Element, toStep: DriveStep) {
125
  window.requestAnimationFrame(animate);
126
  };
127
 
128
- setState("transitionCallback", animate);
129
  window.requestAnimationFrame(animate);
130
 
131
  bringInView(toElement);
 
74
  const highlightedHook = getConfig("onHighlighted");
75
  const deselectedHook = fromStep?.onDeselected || getConfig("onDeselected");
76
 
77
+ const config = getConfig();
78
+ const state = getState();
79
+
80
  if (!isFirstHighlight && deselectedHook) {
81
+ deselectedHook(isFromDummyElement ? undefined : fromElement, fromStep!, {
82
+ config,
83
+ state,
84
+ });
85
  }
86
 
87
  if (highlightStartedHook) {
88
+ highlightStartedHook(isToDummyElement ? undefined : toElement, toStep, {
89
+ config,
90
+ state,
91
+ });
92
  }
93
 
94
  const hasDelayedPopover = !isFirstHighlight && isAnimatedTour;
 
97
  hidePopover();
98
 
99
  const animate = () => {
100
+ const transitionCallback = getState("__transitionCallback");
101
 
102
  // This makes sure that the repeated calls to transferHighlight
103
  // don't interfere with each other. Only the last call will be
 
121
  trackActiveElement(toElement);
122
 
123
  if (highlightedHook) {
124
+ highlightedHook(isToDummyElement ? undefined : toElement, toStep, {
125
+ config: getConfig(),
126
+ state: getState(),
127
+ });
128
  }
129
 
130
+ setState("__transitionCallback", undefined);
131
  setState("previousStep", fromStep);
132
  setState("previousElement", fromElement);
133
  setState("activeStep", toStep);
 
137
  window.requestAnimationFrame(animate);
138
  };
139
 
140
+ setState("__transitionCallback", animate);
141
  window.requestAnimationFrame(animate);
142
 
143
  bringInView(toElement);
src/stage.ts CHANGED
@@ -14,7 +14,7 @@ export type StageDefinition = {
14
  // This method calculates the animated new position of the
15
  // stage (called for each frame by requestAnimationFrame)
16
  export function transitionStage(elapsed: number, duration: number, from: Element, to: Element) {
17
- let activeStagePosition = getState("activeStagePosition");
18
 
19
  const fromDefinition = activeStagePosition ? activeStagePosition : from.getBoundingClientRect();
20
  const toDefinition = to.getBoundingClientRect();
@@ -32,7 +32,7 @@ export function transitionStage(elapsed: number, duration: number, from: Element
32
  };
33
 
34
  renderStage(activeStagePosition);
35
- setState("activeStagePosition", activeStagePosition);
36
  }
37
 
38
  export function trackActiveElement(element: Element) {
@@ -49,14 +49,14 @@ export function trackActiveElement(element: Element) {
49
  height: definition.height,
50
  };
51
 
52
- setState("activeStagePosition", activeStagePosition);
53
 
54
  renderStage(activeStagePosition);
55
  }
56
 
57
  export function refreshStage() {
58
- const activeStagePosition = getState("activeStagePosition");
59
- const stageSvg = getState("stageSvg");
60
 
61
  if (!activeStagePosition) {
62
  return;
@@ -86,11 +86,11 @@ function mountStage(stagePosition: StageDefinition) {
86
  emit("overlayClick");
87
  });
88
 
89
- setState("stageSvg", stageSvg);
90
  }
91
 
92
  function renderStage(stagePosition: StageDefinition) {
93
- const stageSvg = getState("stageSvg");
94
 
95
  // TODO: cancel rendering if element is not visible
96
  if (!stageSvg) {
@@ -171,7 +171,7 @@ function generateStageSvgPathString(stage: StageDefinition) {
171
  }
172
 
173
  export function destroyStage() {
174
- const stageSvg = getState("stageSvg");
175
  if (stageSvg) {
176
  stageSvg.remove();
177
  }
 
14
  // This method calculates the animated new position of the
15
  // stage (called for each frame by requestAnimationFrame)
16
  export function transitionStage(elapsed: number, duration: number, from: Element, to: Element) {
17
+ let activeStagePosition = getState("__activeStagePosition");
18
 
19
  const fromDefinition = activeStagePosition ? activeStagePosition : from.getBoundingClientRect();
20
  const toDefinition = to.getBoundingClientRect();
 
32
  };
33
 
34
  renderStage(activeStagePosition);
35
+ setState("__activeStagePosition", activeStagePosition);
36
  }
37
 
38
  export function trackActiveElement(element: Element) {
 
49
  height: definition.height,
50
  };
51
 
52
+ setState("__activeStagePosition", activeStagePosition);
53
 
54
  renderStage(activeStagePosition);
55
  }
56
 
57
  export function refreshStage() {
58
+ const activeStagePosition = getState("__activeStagePosition");
59
+ const stageSvg = getState("__stageSvg");
60
 
61
  if (!activeStagePosition) {
62
  return;
 
86
  emit("overlayClick");
87
  });
88
 
89
+ setState("__stageSvg", stageSvg);
90
  }
91
 
92
  function renderStage(stagePosition: StageDefinition) {
93
+ const stageSvg = getState("__stageSvg");
94
 
95
  // TODO: cancel rendering if element is not visible
96
  if (!stageSvg) {
 
171
  }
172
 
173
  export function destroyStage() {
174
+ const stageSvg = getState("__stageSvg");
175
  if (stageSvg) {
176
  stageSvg.remove();
177
  }
src/state.ts CHANGED
@@ -3,10 +3,7 @@ import { PopoverDOM } from "./popover";
3
  import { DriveStep } from "./driver";
4
 
5
  export type State = {
6
- // Whether driver is initialized or not
7
  isInitialized?: boolean;
8
- // Used to bounce the resize event
9
- resizeTimeout?: number;
10
 
11
  activeIndex?: number;
12
  activeElement?: Element;
@@ -15,12 +12,12 @@ export type State = {
15
  previousElement?: Element;
16
  previousStep?: DriveStep;
17
 
18
- transitionCallback?: () => void;
19
-
20
- activeStagePosition?: StageDefinition;
21
- stageSvg?: SVGSVGElement;
22
-
23
  popover?: PopoverDOM;
 
 
 
 
 
24
  };
25
 
26
  let currentState: State = {};
 
3
  import { DriveStep } from "./driver";
4
 
5
  export type State = {
 
6
  isInitialized?: boolean;
 
 
7
 
8
  activeIndex?: number;
9
  activeElement?: Element;
 
12
  previousElement?: Element;
13
  previousStep?: DriveStep;
14
 
 
 
 
 
 
15
  popover?: PopoverDOM;
16
+
17
+ __resizeTimeout?: number;
18
+ __transitionCallback?: () => void;
19
+ __activeStagePosition?: StageDefinition;
20
+ __stageSvg?: SVGSVGElement;
21
  };
22
 
23
  let currentState: State = {};