Dependency injection and move bringInView to element
Browse files- assets/scripts/src/element.js +20 -4
- assets/scripts/src/overlay.js +18 -4
- assets/scripts/src/sholo.js +22 -29
assets/scripts/src/element.js
CHANGED
@@ -5,12 +5,16 @@ export default class Element {
|
|
5 |
* DOM element object
|
6 |
* @param node
|
7 |
* @param options
|
|
|
|
|
|
|
8 |
*/
|
9 |
-
constructor(node, options) {
|
10 |
this.node = node;
|
11 |
this.document = document;
|
12 |
this.window = window;
|
13 |
this.options = options;
|
|
|
14 |
this.popover = this.getPopover();
|
15 |
}
|
16 |
|
@@ -62,10 +66,10 @@ export default class Element {
|
|
62 |
}
|
63 |
|
64 |
const elementRect = this.getCalculatedPosition();
|
65 |
-
const absoluteElementTop = elementRect.top + window.pageYOffset;
|
66 |
-
const middle = absoluteElementTop - (window.innerHeight / 2);
|
67 |
|
68 |
-
window.scrollTo(0, middle);
|
69 |
}
|
70 |
|
71 |
/**
|
@@ -106,6 +110,18 @@ export default class Element {
|
|
106 |
|
107 |
onHighlighted() {
|
108 |
this.showPopover();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
109 |
}
|
110 |
|
111 |
showPopover() {
|
|
|
5 |
* DOM element object
|
6 |
* @param node
|
7 |
* @param options
|
8 |
+
* @param overlay
|
9 |
+
* @param window
|
10 |
+
* @param document
|
11 |
*/
|
12 |
+
constructor(node, options, overlay, window, document) {
|
13 |
this.node = node;
|
14 |
this.document = document;
|
15 |
this.window = window;
|
16 |
this.options = options;
|
17 |
+
this.overlay = overlay;
|
18 |
this.popover = this.getPopover();
|
19 |
}
|
20 |
|
|
|
66 |
}
|
67 |
|
68 |
const elementRect = this.getCalculatedPosition();
|
69 |
+
const absoluteElementTop = elementRect.top + this.window.pageYOffset;
|
70 |
+
const middle = absoluteElementTop - (this.window.innerHeight / 2);
|
71 |
|
72 |
+
this.window.scrollTo(0, middle);
|
73 |
}
|
74 |
|
75 |
/**
|
|
|
110 |
|
111 |
onHighlighted() {
|
112 |
this.showPopover();
|
113 |
+
|
114 |
+
const highlightedElement = this;
|
115 |
+
const lastHighlightedElement = this.overlay.getLastHighlightedElement();
|
116 |
+
|
117 |
+
const highlightedNode = this.node;
|
118 |
+
const lastHighlightedNode = lastHighlightedElement && lastHighlightedElement.node;
|
119 |
+
|
120 |
+
// If this element is not already highlighted (because this call could
|
121 |
+
// be from the resize or scroll) and is not in view
|
122 |
+
if (highlightedNode !== lastHighlightedNode && !highlightedElement.isInView()) {
|
123 |
+
highlightedElement.bringInView();
|
124 |
+
}
|
125 |
}
|
126 |
|
127 |
showPopover() {
|
assets/scripts/src/overlay.js
CHANGED
@@ -9,12 +9,14 @@ export default class Overlay {
|
|
9 |
* @param opacity number
|
10 |
* @param padding number
|
11 |
* @param animate bool
|
|
|
|
|
12 |
*/
|
13 |
constructor({
|
14 |
opacity = 0.75,
|
15 |
padding = 10,
|
16 |
animate = true,
|
17 |
-
}) {
|
18 |
this.opacity = opacity; // Fixed opacity for the layover
|
19 |
this.padding = padding; // Padding around the highlighted item
|
20 |
this.animate = animate; // Should animate between the transitions
|
@@ -24,6 +26,7 @@ export default class Overlay {
|
|
24 |
this.highlightedPosition = new Position({}); // position at which layover is patched currently
|
25 |
this.redrawAnimation = null; // used to cancel the redraw animation
|
26 |
this.highlightedElement = null; // currently highlighted dom element (instance of Element)
|
|
|
27 |
|
28 |
this.draw = this.draw.bind(this); // To pass the context of class, as it is to be used in redraw animation callback
|
29 |
|
@@ -58,6 +61,7 @@ export default class Overlay {
|
|
58 |
*/
|
59 |
highlight(element, animate = true) {
|
60 |
if (!element || !element.node) {
|
|
|
61 |
return;
|
62 |
}
|
63 |
|
@@ -75,6 +79,7 @@ export default class Overlay {
|
|
75 |
return;
|
76 |
}
|
77 |
|
|
|
78 |
this.highlightedElement = element;
|
79 |
this.positionToHighlight = position;
|
80 |
|
@@ -95,6 +100,14 @@ export default class Overlay {
|
|
95 |
return this.highlightedElement;
|
96 |
}
|
97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
/**
|
99 |
* Removes the overlay and cancel any listeners
|
100 |
*/
|
@@ -102,6 +115,7 @@ export default class Overlay {
|
|
102 |
this.positionToHighlight = new Position();
|
103 |
this.highlightedElement.onDeselected();
|
104 |
this.highlightedElement = null;
|
|
|
105 |
|
106 |
this.draw();
|
107 |
}
|
@@ -138,8 +152,8 @@ export default class Overlay {
|
|
138 |
|
139 |
// Cut the chunk of overlay that is over the highlighted item
|
140 |
this.removeCloak({
|
141 |
-
posX: this.highlightedPosition.left - window.scrollX - this.padding,
|
142 |
-
posY: this.highlightedPosition.top - window.scrollY - this.padding,
|
143 |
width: (this.highlightedPosition.right - this.highlightedPosition.left) + (this.padding * 2),
|
144 |
height: (this.highlightedPosition.bottom - this.highlightedPosition.top) + (this.padding * 2),
|
145 |
});
|
@@ -164,7 +178,7 @@ export default class Overlay {
|
|
164 |
if (canHighlight || this.overlayAlpha > 0) {
|
165 |
// Add the overlay if not already there
|
166 |
if (!this.overlay.parentNode) {
|
167 |
-
document.body.appendChild(this.overlay);
|
168 |
}
|
169 |
|
170 |
// Stage a new animation frame only if the position has not been reached
|
|
|
9 |
* @param opacity number
|
10 |
* @param padding number
|
11 |
* @param animate bool
|
12 |
+
* @param window
|
13 |
+
* @param document
|
14 |
*/
|
15 |
constructor({
|
16 |
opacity = 0.75,
|
17 |
padding = 10,
|
18 |
animate = true,
|
19 |
+
}, window, document) {
|
20 |
this.opacity = opacity; // Fixed opacity for the layover
|
21 |
this.padding = padding; // Padding around the highlighted item
|
22 |
this.animate = animate; // Should animate between the transitions
|
|
|
26 |
this.highlightedPosition = new Position({}); // position at which layover is patched currently
|
27 |
this.redrawAnimation = null; // used to cancel the redraw animation
|
28 |
this.highlightedElement = null; // currently highlighted dom element (instance of Element)
|
29 |
+
this.lastHighlightedElement = null; // element that was highlighted before current one
|
30 |
|
31 |
this.draw = this.draw.bind(this); // To pass the context of class, as it is to be used in redraw animation callback
|
32 |
|
|
|
61 |
*/
|
62 |
highlight(element, animate = true) {
|
63 |
if (!element || !element.node) {
|
64 |
+
console.warn('Invalid element to highlight. Must be an instance of `Element`');
|
65 |
return;
|
66 |
}
|
67 |
|
|
|
79 |
return;
|
80 |
}
|
81 |
|
82 |
+
this.lastHighlightedElement = this.highlightedElement;
|
83 |
this.highlightedElement = element;
|
84 |
this.positionToHighlight = position;
|
85 |
|
|
|
100 |
return this.highlightedElement;
|
101 |
}
|
102 |
|
103 |
+
/**
|
104 |
+
* Gets the element that was highlighted before current element
|
105 |
+
* @returns {null|*}
|
106 |
+
*/
|
107 |
+
getLastHighlightedElement() {
|
108 |
+
return this.lastHighlightedElement;
|
109 |
+
}
|
110 |
+
|
111 |
/**
|
112 |
* Removes the overlay and cancel any listeners
|
113 |
*/
|
|
|
115 |
this.positionToHighlight = new Position();
|
116 |
this.highlightedElement.onDeselected();
|
117 |
this.highlightedElement = null;
|
118 |
+
this.lastHighlightedElement = null;
|
119 |
|
120 |
this.draw();
|
121 |
}
|
|
|
152 |
|
153 |
// Cut the chunk of overlay that is over the highlighted item
|
154 |
this.removeCloak({
|
155 |
+
posX: this.highlightedPosition.left - this.window.scrollX - this.padding,
|
156 |
+
posY: this.highlightedPosition.top - this.window.scrollY - this.padding,
|
157 |
width: (this.highlightedPosition.right - this.highlightedPosition.left) + (this.padding * 2),
|
158 |
height: (this.highlightedPosition.bottom - this.highlightedPosition.top) + (this.padding * 2),
|
159 |
});
|
|
|
178 |
if (canHighlight || this.overlayAlpha > 0) {
|
179 |
// Add the overlay if not already there
|
180 |
if (!this.overlay.parentNode) {
|
181 |
+
this.document.body.appendChild(this.overlay);
|
182 |
}
|
183 |
|
184 |
// Stage a new animation frame only if the position has not been reached
|
assets/scripts/src/sholo.js
CHANGED
@@ -16,11 +16,11 @@ export default class Sholo {
|
|
16 |
opacity: 0.75,
|
17 |
}, options);
|
18 |
|
19 |
-
this.overlay = new Overlay(options);
|
20 |
-
|
21 |
this.document = document;
|
22 |
this.window = window;
|
23 |
|
|
|
|
|
24 |
this.steps = []; // steps to be presented if any
|
25 |
this.currentStep = 0; // index for the currently highlighted step
|
26 |
|
@@ -57,7 +57,7 @@ export default class Sholo {
|
|
57 |
}
|
58 |
|
59 |
const highlightedElement = this.overlay.getHighlightedElement();
|
60 |
-
const popover = document.getElementById('sholo-popover-item');
|
61 |
|
62 |
const clickedHighlightedElement = highlightedElement.node.contains(e.target);
|
63 |
const clickedPopover = popover && popover.contains(e.target);
|
@@ -82,13 +82,6 @@ export default class Sholo {
|
|
82 |
} else if (prevClicked) {
|
83 |
this.movePrevious();
|
84 |
}
|
85 |
-
|
86 |
-
// @todo - move to onHighlighted hook and add the check if not visible then do this
|
87 |
-
if (this.overlay.highlightedElement) {
|
88 |
-
window.setTimeout(() => {
|
89 |
-
this.overlay.highlightedElement.bringInView();
|
90 |
-
}, 800);
|
91 |
-
}
|
92 |
}
|
93 |
|
94 |
/**
|
@@ -167,12 +160,19 @@ export default class Sholo {
|
|
167 |
this.steps = [];
|
168 |
|
169 |
steps.forEach((step, index) => {
|
170 |
-
if (!step.element) {
|
171 |
-
throw new Error(`Element (query selector
|
172 |
}
|
173 |
|
174 |
-
const
|
175 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
176 |
|
177 |
this.steps.push(element);
|
178 |
});
|
@@ -189,24 +189,17 @@ export default class Sholo {
|
|
189 |
|
190 |
/**
|
191 |
* Highlights the given selector
|
192 |
-
* @param selector
|
|
|
193 |
*/
|
194 |
highlight(selector) {
|
195 |
-
const domElement =
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
}
|
200 |
-
|
201 |
-
static findDomElement(selector) {
|
202 |
-
if (typeof selector === 'string') {
|
203 |
-
return document.querySelector(selector);
|
204 |
-
}
|
205 |
-
|
206 |
-
if (typeof selector === 'object') {
|
207 |
-
return selector;
|
208 |
}
|
209 |
|
210 |
-
|
|
|
211 |
}
|
212 |
}
|
|
|
16 |
opacity: 0.75,
|
17 |
}, options);
|
18 |
|
|
|
|
|
19 |
this.document = document;
|
20 |
this.window = window;
|
21 |
|
22 |
+
this.overlay = new Overlay(options, window, document);
|
23 |
+
|
24 |
this.steps = []; // steps to be presented if any
|
25 |
this.currentStep = 0; // index for the currently highlighted step
|
26 |
|
|
|
57 |
}
|
58 |
|
59 |
const highlightedElement = this.overlay.getHighlightedElement();
|
60 |
+
const popover = this.document.getElementById('sholo-popover-item');
|
61 |
|
62 |
const clickedHighlightedElement = highlightedElement.node.contains(e.target);
|
63 |
const clickedPopover = popover && popover.contains(e.target);
|
|
|
82 |
} else if (prevClicked) {
|
83 |
this.movePrevious();
|
84 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
}
|
86 |
|
87 |
/**
|
|
|
160 |
this.steps = [];
|
161 |
|
162 |
steps.forEach((step, index) => {
|
163 |
+
if (!step.element || typeof step.element !== 'string') {
|
164 |
+
throw new Error(`Element (query selector string) missing in step ${index}`);
|
165 |
}
|
166 |
|
167 |
+
const elementOptions = Object.assign({}, this.options, step);
|
168 |
+
|
169 |
+
const domElement = this.document.querySelector(step.element);
|
170 |
+
if (!domElement) {
|
171 |
+
console.warn(`Element to highlight ${step.element} not found`);
|
172 |
+
return;
|
173 |
+
}
|
174 |
+
|
175 |
+
const element = new Element(domElement, elementOptions, this.overlay, this.window, this.document);
|
176 |
|
177 |
this.steps.push(element);
|
178 |
});
|
|
|
189 |
|
190 |
/**
|
191 |
* Highlights the given selector
|
192 |
+
* @param selector string query selector
|
193 |
+
* @todo make it accept json or query selector
|
194 |
*/
|
195 |
highlight(selector) {
|
196 |
+
const domElement = this.document.querySelector(selector);
|
197 |
+
if (!domElement) {
|
198 |
+
console.warn(`Element to highlight ${selector} not found`);
|
199 |
+
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
200 |
}
|
201 |
|
202 |
+
const element = new Element(domElement, this.options, this.overlay, this.window, this.document);
|
203 |
+
this.overlay.highlight(element);
|
204 |
}
|
205 |
}
|