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 |
}
|