diff --git a/demo/styles/demo.scss b/demo/styles/demo.scss index e51184c9a0c7d16f9242d08a1338fe84ec2160d0..a7d334e1b7eeadbb065002f12bd8d4728cee09c6 100644 --- a/demo/styles/demo.scss +++ b/demo/styles/demo.scss @@ -14,10 +14,10 @@ img.emoji { background: white; padding: 10px 30px; border-radius: 5px; - color: #333333; + color: #1f1f1f; a, a:visited, a:focus { - color: #333333; + color: #1f1f1f; text-decoration: underline; } diff --git a/src/core/element.js b/src/core/element.js index f5939b1039fed31b43f338d4a70c79debac27615..0f6b99a0b20836b7ab9bd3702d44f50f78eebc76 100644 --- a/src/core/element.js +++ b/src/core/element.js @@ -11,17 +11,27 @@ export default class Element { * @param {Node|HTMLElement} node * @param {Object} options * @param {Popover} popover + * @param {Stage} stage * @param {Overlay} overlay * @param {Window} window * @param {Document} document */ - constructor(node, options, popover, overlay, window, document) { + constructor({ + node, + options, + popover, + stage, + overlay, + window, + document, + } = {}) { this.node = node; this.document = document; this.window = window; this.options = options; this.overlay = overlay; this.popover = popover; + this.stage = stage; this.animationTimeout = null; @@ -139,9 +149,13 @@ export default class Element { * Is called when element is about to be deselected * i.e. when moving the focus to next element of closing */ - onDeselected() { + onDeselected(hideStage = false) { this.hidePopover(); + if (hideStage) { + this.hideStage(); + } + this.node.classList.remove(CLASS_DRIVER_HIGHLIGHTED_ELEMENT); // If there was any animation in progress, cancel that @@ -154,6 +168,19 @@ export default class Element { } } + /** + * Checks if the given element is same as the current element + * @param {Element} element + * @returns {boolean} + */ + isSame(element) { + if (!element || !element.node) { + return false; + } + + return element.node === this.node; + } + /** * Is called when the element is about to be highlighted * i.e. either if overlay has started moving the highlight towards @@ -174,29 +201,21 @@ export default class Element { */ onHighlighted() { this.showPopover(); + this.showStage(); this.node.classList.add(CLASS_DRIVER_HIGHLIGHTED_ELEMENT); this.highlightFinished = true; const highlightedElement = this; - const highlightedNode = this.node; + const popoverElement = this.popover; - const lastHighlightedElement = this.overlay.getLastHighlightedElement(); - const lastHighlightedNode = lastHighlightedElement && lastHighlightedElement.node; - - // If this element is not already highlighted (because this call could - // be from the resize or scroll) and is not in view - if (highlightedNode !== lastHighlightedNode) { - const popoverElement = this.popover; - - if (popoverElement && !popoverElement.isInView()) { - popoverElement.bringInView(); - } + if (popoverElement && !popoverElement.isInView()) { + popoverElement.bringInView(); + } - if (!highlightedElement.isInView()) { - highlightedElement.bringInView(); - } + if (!highlightedElement.isInView()) { + highlightedElement.bringInView(); } if (this.options.onHighlighted) { @@ -204,6 +223,13 @@ export default class Element { } } + /** + * Shows the stage behind the element + */ + showStage() { + this.stage.show(this.getCalculatedPosition()); + } + /** * Gets the DOM Element behind this element * @returns {Node|HTMLElement|*} @@ -212,6 +238,10 @@ export default class Element { return this.node; } + hideStage() { + this.stage.hide(); + } + hidePopover() { if (!this.popover) { return; diff --git a/src/core/overlay.js b/src/core/overlay.js index 689f533193016b6747c71cb453aff0b7490db373..ae4b9f245c3e9a4671c4764664c4ba3fd6863f76 100644 --- a/src/core/overlay.js +++ b/src/core/overlay.js @@ -10,10 +10,9 @@ export default class Overlay { /** * @param {Object} options * @param {Window} window - * @param {Stage} stage * @param {Document} document */ - constructor(options, stage, window, document) { + constructor(options, window, document) { this.options = options; this.positionToHighlight = new Position({}); // position at which layover is to be patched at @@ -23,7 +22,6 @@ export default class Overlay { this.window = window; this.document = document; - this.stage = stage; this.makeNode(); } @@ -52,6 +50,11 @@ export default class Overlay { return; } + // If highlighted element is not changed from last time + if (this.highlightedElement && this.highlightedElement.isSame(this.lastHighlightedElement)) { + return; + } + // There might be hide timer from last time // which might be getting triggered this.window.clearTimeout(this.hideTimer); @@ -60,7 +63,7 @@ export default class Overlay { element.onHighlightStarted(); // Old element has been deselected - if (this.highlightedElement) { + if (this.highlightedElement && !this.highlightedElement.isSame(this.lastHighlightedElement)) { this.highlightedElement.onDeselected(); } @@ -76,9 +79,6 @@ export default class Overlay { this.showOverlay(); - // Show the stage - this.stage.show(this.positionToHighlight); - // Element has been highlighted this.highlightedElement.onHighlighted(); } @@ -126,14 +126,13 @@ export default class Overlay { clear() { this.positionToHighlight = new Position(); if (this.highlightedElement) { - this.highlightedElement.onDeselected(); + this.highlightedElement.onDeselected(true); } this.highlightedElement = null; this.lastHighlightedElement = null; this.hideOverlay(); - this.stage.hide(); } /** @@ -141,12 +140,13 @@ export default class Overlay { * And moves the highlight around if necessary */ refresh() { - // If the highlighted element was there Cancel the - // existing animation frame if any and highlight it again - // as its position might have been changed - if (this.highlightedElement) { - this.highlight(this.highlightedElement); - this.highlightedElement.onHighlighted(); + // If no highlighted element, cancel the refresh + if (!this.highlightedElement) { + return; } + + // Reposition the stage and show popover + this.highlightedElement.showPopover(); + this.highlightedElement.showStage(); } } diff --git a/src/core/stage.js b/src/core/stage.js index 40b4d806c9acac0b5dbc419adf77a938a9352aa5..42c658bbe481484e8ddee4963048e50186f78a1e 100644 --- a/src/core/stage.js +++ b/src/core/stage.js @@ -46,7 +46,7 @@ export default class Stage extends Element { /** * Makes it visible and sets the default properties */ - reset() { + setInitialStyle() { this.node.style.display = 'block'; this.node.style.left = '0'; this.node.style.top = '0'; @@ -55,7 +55,7 @@ export default class Stage extends Element { } show(position) { - this.reset(); + this.setInitialStyle(); // Make it two times the padding because, half will be given on left and half on right const requiredPadding = this.options.padding * 2; diff --git a/src/index.js b/src/index.js index 712118fd312024cbbd7acc2707ba5425056d7b3a..3b2c6ef164293e0eb2874d0de087938fd81c4d53 100644 --- a/src/index.js +++ b/src/index.js @@ -43,8 +43,7 @@ export default class Driver { this.steps = []; // steps to be presented if any this.currentStep = 0; // index for the currently highlighted step - const stage = new Stage(this.options, window, document); - this.overlay = new Overlay(this.options, stage, window, document); + this.overlay = new Overlay(this.options, window, document); this.onResize = this.onResize.bind(this); this.onKeyUp = this.onKeyUp.bind(this); @@ -70,8 +69,7 @@ export default class Driver { * @param e */ onClick(e) { - if (!this.hasHighlightedElement() || !this.isActivated) { - // Has no highlighted element so ignore the click + if (!this.isActivated || !this.hasHighlightedElement()) { return; } @@ -188,7 +186,7 @@ export default class Driver { } // Refresh with animation - this.overlay.refresh(true); + this.overlay.refresh(); } /** @@ -278,7 +276,17 @@ export default class Driver { popover = new Popover(popoverOptions, this.window, this.document); } - return new Element(domElement, elementOptions, popover, this.overlay, this.window, this.document); + const stage = new Stage(this.options, this.window, this.document); + + return new Element({ + node: domElement, + options: elementOptions, + popover, + stage, + overlay: this.overlay, + window: this.window, + document: this.document, + }); } /**