kamrify commited on
Commit
e459aae
·
1 Parent(s): a0a08e9

Driver refactoring

Browse files
docs/src/content/guides/theming.mdx ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: "Theming"
3
+ groupTitle: "Introduction"
4
+ sort: 3
5
+ ---
6
+
7
+ You can customize the look and feel of the driver by applying CSS to following classes.
8
+
9
+ ```css
10
+ /* Applied to the `body` when the driver: */
11
+ .driver-active {} /* is active */
12
+ .driver-fade {} /* is animated */
13
+ .driver-simple {} /* is not animated */
14
+ ```
15
+
16
+ Following classes are applied to the overlay element (i.e. called stage.
17
+
18
+ ```css
19
+ .driver-stage {}
src/driver.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { AllowedButtons, destroyPopover, Popover } from "./popover";
2
- import { destroyStage } from "./stage";
3
  import { destroyEvents, initEvents, requireRefresh } from "./events";
4
  import { Config, configure, DriverHook, getConfig } from "./config";
5
  import { destroyHighlight, highlight } from "./highlight";
@@ -57,19 +57,40 @@ export function driver(options: Config = {}) {
57
  }
58
 
59
  function handleArrowLeft() {
60
- const steps = getConfig("steps") || [];
 
 
 
 
 
 
 
 
 
 
 
61
  const currentStepIndex = getState("activeIndex");
62
  if (typeof currentStepIndex === "undefined") {
63
  return;
64
  }
65
 
66
- const previousStepIndex = currentStepIndex - 1;
67
- if (steps[previousStepIndex]) {
68
- drive(previousStepIndex);
 
 
 
69
  }
 
 
70
  }
71
 
72
  function handleArrowRight() {
 
 
 
 
 
73
  const activeIndex = getState("activeIndex");
74
  const activeStep = getState("activeStep");
75
  const activeElement = getState("activeElement");
@@ -113,8 +134,9 @@ export function driver(options: Config = {}) {
113
  }
114
 
115
  if (!steps[stepIndex]) {
116
- console.warn(`Step not found at index: ${stepIndex}`);
117
  destroy();
 
 
118
  }
119
 
120
  setState("activeIndex", stepIndex);
@@ -185,7 +207,7 @@ export function driver(options: Config = {}) {
185
  destroyEvents();
186
  destroyPopover();
187
  destroyHighlight();
188
- destroyStage();
189
  destroyEmitter();
190
 
191
  resetState();
 
1
  import { AllowedButtons, destroyPopover, Popover } from "./popover";
2
+ import { destroyOverlay } from "./overlay";
3
  import { destroyEvents, initEvents, requireRefresh } from "./events";
4
  import { Config, configure, DriverHook, getConfig } from "./config";
5
  import { destroyHighlight, highlight } from "./highlight";
 
57
  }
58
 
59
  function handleArrowLeft() {
60
+ const isTransitioning = getState("__transitionCallback");
61
+ if (isTransitioning) {
62
+ return;
63
+ }
64
+
65
+ const activeIndex = getState("activeIndex");
66
+ const activeStep = getState("activeStep");
67
+ const activeElement = getState("activeElement");
68
+ if (typeof activeIndex === "undefined" || typeof activeStep === "undefined") {
69
+ return;
70
+ }
71
+
72
  const currentStepIndex = getState("activeIndex");
73
  if (typeof currentStepIndex === "undefined") {
74
  return;
75
  }
76
 
77
+ const onPrevClick = activeStep.popover?.onPrevClick || getConfig("onPrevClick");
78
+ if (onPrevClick) {
79
+ return onPrevClick(activeElement, activeStep, {
80
+ config: getConfig(),
81
+ state: getState(),
82
+ });
83
  }
84
+
85
+ movePrevious();
86
  }
87
 
88
  function handleArrowRight() {
89
+ const isTransitioning = getState("__transitionCallback");
90
+ if (isTransitioning) {
91
+ return;
92
+ }
93
+
94
  const activeIndex = getState("activeIndex");
95
  const activeStep = getState("activeStep");
96
  const activeElement = getState("activeElement");
 
134
  }
135
 
136
  if (!steps[stepIndex]) {
 
137
  destroy();
138
+
139
+ return;
140
  }
141
 
142
  setState("activeIndex", stepIndex);
 
207
  destroyEvents();
208
  destroyPopover();
209
  destroyHighlight();
210
+ destroyOverlay();
211
  destroyEmitter();
212
 
213
  resetState();
src/highlight.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { DriveStep } from "./driver";
2
- import { refreshStage, trackActiveElement, transitionStage } from "./stage";
3
  import { getConfig } from "./config";
4
  import { repositionPopover, renderPopover, hidePopover } from "./popover";
5
  import { bringInView } from "./utils";
@@ -51,7 +51,7 @@ export function refreshActiveHighlight() {
51
  }
52
 
53
  trackActiveElement(activeHighlight);
54
- refreshStage();
55
  repositionPopover(activeHighlight, activeStep);
56
  }
57
 
 
1
  import { DriveStep } from "./driver";
2
+ import { refreshOverlay, trackActiveElement, transitionStage } from "./overlay";
3
  import { getConfig } from "./config";
4
  import { repositionPopover, renderPopover, hidePopover } from "./popover";
5
  import { bringInView } from "./utils";
 
51
  }
52
 
53
  trackActiveElement(activeHighlight);
54
+ refreshOverlay();
55
  repositionPopover(activeHighlight, activeStep);
56
  }
57
 
src/{stage.ts → overlay.ts} RENAMED
@@ -31,7 +31,7 @@ export function transitionStage(elapsed: number, duration: number, from: Element
31
  height,
32
  };
33
 
34
- renderStage(activeStagePosition);
35
  setState("__activeStagePosition", activeStagePosition);
36
  }
37
 
@@ -51,18 +51,18 @@ export function trackActiveElement(element: Element) {
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;
63
  }
64
 
65
- if (!stageSvg) {
66
  console.warn("No stage svg found.");
67
  return;
68
  }
@@ -70,14 +70,14 @@ export function refreshStage() {
70
  const windowX = window.innerWidth;
71
  const windowY = window.innerHeight;
72
 
73
- stageSvg.setAttribute("viewBox", `0 0 ${windowX} ${windowY}`);
74
  }
75
 
76
- function mountStage(stagePosition: StageDefinition) {
77
- const stageSvg = createStageSvg(stagePosition);
78
- document.body.appendChild(stageSvg);
79
 
80
- onDriverClick(stageSvg, e => {
81
  const target = e.target as SVGElement;
82
  if (target.tagName !== "path") {
83
  return;
@@ -86,20 +86,20 @@ 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) {
97
- mountStage(stagePosition);
98
 
99
  return;
100
  }
101
 
102
- const pathElement = stageSvg.firstElementChild as SVGPathElement | null;
103
  if (pathElement?.tagName !== "path") {
104
  throw new Error("no path element found in stage svg");
105
  }
@@ -107,7 +107,7 @@ function renderStage(stagePosition: StageDefinition) {
107
  pathElement.setAttribute("d", generateStageSvgPathString(stagePosition));
108
  }
109
 
110
- function createStageSvg(stage: StageDefinition): SVGSVGElement {
111
  const windowX = window.innerWidth;
112
  const windowY = window.innerHeight;
113
 
@@ -170,9 +170,9 @@ function generateStageSvgPathString(stage: StageDefinition) {
170
  M${highlightBoxX},${highlightBoxY} h${highlightBoxWidth} a${normalizedRadius},${normalizedRadius} 0 0 1 ${normalizedRadius},${normalizedRadius} v${highlightBoxHeight} a${normalizedRadius},${normalizedRadius} 0 0 1 -${normalizedRadius},${normalizedRadius} h-${highlightBoxWidth} a${normalizedRadius},${normalizedRadius} 0 0 1 -${normalizedRadius},-${normalizedRadius} v-${highlightBoxHeight} a${normalizedRadius},${normalizedRadius} 0 0 1 ${normalizedRadius},-${normalizedRadius} z`;
171
  }
172
 
173
- export function destroyStage() {
174
- const stageSvg = getState("__stageSvg");
175
- if (stageSvg) {
176
- stageSvg.remove();
177
  }
178
  }
 
31
  height,
32
  };
33
 
34
+ renderOverlay(activeStagePosition);
35
  setState("__activeStagePosition", activeStagePosition);
36
  }
37
 
 
51
 
52
  setState("__activeStagePosition", activeStagePosition);
53
 
54
+ renderOverlay(activeStagePosition);
55
  }
56
 
57
+ export function refreshOverlay() {
58
  const activeStagePosition = getState("__activeStagePosition");
59
+ const overlaySvg = getState("__overlaySvg");
60
 
61
  if (!activeStagePosition) {
62
  return;
63
  }
64
 
65
+ if (!overlaySvg) {
66
  console.warn("No stage svg found.");
67
  return;
68
  }
 
70
  const windowX = window.innerWidth;
71
  const windowY = window.innerHeight;
72
 
73
+ overlaySvg.setAttribute("viewBox", `0 0 ${windowX} ${windowY}`);
74
  }
75
 
76
+ function mountOverlay(stagePosition: StageDefinition) {
77
+ const overlaySvg = createOverlaySvg(stagePosition);
78
+ document.body.appendChild(overlaySvg);
79
 
80
+ onDriverClick(overlaySvg, e => {
81
  const target = e.target as SVGElement;
82
  if (target.tagName !== "path") {
83
  return;
 
86
  emit("overlayClick");
87
  });
88
 
89
+ setState("__overlaySvg", overlaySvg);
90
  }
91
 
92
+ function renderOverlay(stagePosition: StageDefinition) {
93
+ const overlaySvg = getState("__overlaySvg");
94
 
95
  // TODO: cancel rendering if element is not visible
96
+ if (!overlaySvg) {
97
+ mountOverlay(stagePosition);
98
 
99
  return;
100
  }
101
 
102
+ const pathElement = overlaySvg.firstElementChild as SVGPathElement | null;
103
  if (pathElement?.tagName !== "path") {
104
  throw new Error("no path element found in stage svg");
105
  }
 
107
  pathElement.setAttribute("d", generateStageSvgPathString(stagePosition));
108
  }
109
 
110
+ function createOverlaySvg(stage: StageDefinition): SVGSVGElement {
111
  const windowX = window.innerWidth;
112
  const windowY = window.innerHeight;
113
 
 
170
  M${highlightBoxX},${highlightBoxY} h${highlightBoxWidth} a${normalizedRadius},${normalizedRadius} 0 0 1 ${normalizedRadius},${normalizedRadius} v${highlightBoxHeight} a${normalizedRadius},${normalizedRadius} 0 0 1 -${normalizedRadius},${normalizedRadius} h-${highlightBoxWidth} a${normalizedRadius},${normalizedRadius} 0 0 1 -${normalizedRadius},-${normalizedRadius} v-${highlightBoxHeight} a${normalizedRadius},${normalizedRadius} 0 0 1 ${normalizedRadius},-${normalizedRadius} z`;
171
  }
172
 
173
+ export function destroyOverlay() {
174
+ const overlaySvg = getState("__overlaySvg");
175
+ if (overlaySvg) {
176
+ overlaySvg.remove();
177
  }
178
  }
src/state.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { StageDefinition } from "./stage";
2
  import { PopoverDOM } from "./popover";
3
  import { DriveStep } from "./driver";
4
 
@@ -17,7 +17,7 @@ export type State = {
17
  __resizeTimeout?: number;
18
  __transitionCallback?: () => void;
19
  __activeStagePosition?: StageDefinition;
20
- __stageSvg?: SVGSVGElement;
21
  };
22
 
23
  let currentState: State = {};
 
1
+ import { StageDefinition } from "./overlay";
2
  import { PopoverDOM } from "./popover";
3
  import { DriveStep } from "./driver";
4
 
 
17
  __resizeTimeout?: number;
18
  __transitionCallback?: () => void;
19
  __activeStagePosition?: StageDefinition;
20
+ __overlaySvg?: SVGSVGElement;
21
  };
22
 
23
  let currentState: State = {};