diff --git a/index.html b/index.html index 29c3067ca941ddafb928530ef782b2f137373993..11f00b8cc7ccc3e20b77a7437b6f56645b8fe5d0 100644 --- a/index.html +++ b/index.html @@ -320,9 +320,6 @@ activeElement.getNode(); // Gets the DOM Element behind this element -
-
- diff --git a/src/common/constants.js b/src/common/constants.js index 3cc8119d7752511c221e2eb28a31ee7382c14516..327abf4310e083715c5845aa8b286090fbf55716 100644 --- a/src/common/constants.js +++ b/src/common/constants.js @@ -7,9 +7,10 @@ export const ESC_KEY_CODE = 27; export const LEFT_KEY_CODE = 37; export const RIGHT_KEY_CODE = 39; -export const ID_OVERLAY = 'driver-canvas-overlay'; - +export const ID_OVERLAY = 'driver-page-overlay'; +export const ID_STAGE = 'driver-highlighted-element-stage'; export const ID_POPOVER = 'driver-popover-item'; + export const CLASS_POPOVER_TIP = 'driver-popover-tip'; export const CLASS_POPOVER_TITLE = 'driver-popover-title'; export const CLASS_POPOVER_DESCRIPTION = 'driver-popover-description'; @@ -33,3 +34,6 @@ export const POPOVER_HTML = ` `; + +export const OVERLAY_HTML = `
`; +export const STAGE_HTML = `
`; \ No newline at end of file diff --git a/src/common/utils.js b/src/common/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..524217bfd002759eeb17ab0d394734970724fed8 --- /dev/null +++ b/src/common/utils.js @@ -0,0 +1,13 @@ +/** + * Turn a string into a node + * @param {String} htmlString to convert + * @return {Node} Converted node element + */ +// eslint-disable-next-line +export const createNodeFromString = (htmlString) => { + const div = document.createElement('div'); + div.innerHTML = htmlString.trim(); + + // Change this to div.childNodes to support multiple top-level nodes + return div.firstChild; +}; diff --git a/src/core/element.js b/src/core/element.js index ededa67b6ea42c0ea205ba9f36dde297bf0220c6..6dea8aa21f832d1d7f8f3f9f1730fc5abbc94a00 100644 --- a/src/core/element.js +++ b/src/core/element.js @@ -148,14 +148,6 @@ export default class Element { } } - getSize() { - const boundingRect = this.node.getBoundingClientRect(); - return { - width: boundingRect.width, - height: boundingRect.height - }; - } - /** * Is called when the element is about to be highlighted * i.e. either if overlay has started moving the highlight towards @@ -249,4 +241,15 @@ export default class Element { width: Math.max(body.scrollWidth, body.offsetWidth, html.scrollWidth, html.offsetWidth), }; } + + /** + * Gets the size for popover + * @returns {{height: number, width: number}} + */ + getSize() { + return { + height: Math.max(this.node.scrollHeight, this.node.offsetHeight), + width: Math.max(this.node.scrollWidth, this.node.offsetWidth), + }; + } } diff --git a/src/core/overlay.js b/src/core/overlay.js index 95d91d0a5acf9ee4d0e2540f07c504d6318253b5..e154b30b1898b4c1397aa5768130b097eae37153 100644 --- a/src/core/overlay.js +++ b/src/core/overlay.js @@ -1,4 +1,6 @@ import Position from './position'; +import { ID_OVERLAY, OVERLAY_HTML, POPOVER_HTML } from "../common/constants"; +import { createNodeFromString } from "../common/utils"; /** * Responsible for overlay creation and manipulation i.e. @@ -8,9 +10,10 @@ export default class Overlay { /** * @param {Object} options * @param {Window} window + * @param {Stage} stage * @param {Document} document */ - constructor(options, window, document) { + constructor(options, stage, window, document) { this.options = options; this.positionToHighlight = new Position({}); // position at which layover is to be patched at @@ -22,18 +25,22 @@ export default class Overlay { this.window = window; this.document = document; + this.stage = stage; - this.resetOverlay(); + this.makeNode(); } /** * Prepares the overlay */ - resetOverlay() { - // @todo: append the elements if not there already + makeNode() { + let pageOverlay = this.document.getElementById(ID_OVERLAY); + if (!pageOverlay) { + pageOverlay = createNodeFromString(OVERLAY_HTML); + document.body.appendChild(pageOverlay); + } - this.pageOverlay = this.document.getElementById('driver-page-overlay'); - this.highlightStage = this.document.getElementById('driver-highlighted-element-stage'); + this.node = pageOverlay; } /** @@ -102,8 +109,8 @@ export default class Overlay { this.highlightedElement = null; this.lastHighlightedElement = null; - this.pageOverlay.style.opacity = '0'; - this.highlightStage.style.display = 'none'; + this.node.style.opacity = '0'; + this.stage.hide(); } /** @@ -116,22 +123,11 @@ export default class Overlay { return; } - // Make it two times the padding because, half will be given on left and half on right - const requiredPadding = this.options.padding * 2; - // Show the overlay - this.pageOverlay.style.opacity = `${this.options.opacity}`; - - const width = (this.positionToHighlight.right - this.positionToHighlight.left) + (requiredPadding); - const height = (this.positionToHighlight.bottom - this.positionToHighlight.top) + (requiredPadding); + this.node.style.opacity = `${this.options.opacity}`; // Show the stage - this.highlightStage.style.display = 'block'; - this.highlightStage.style.position = 'absolute'; - this.highlightStage.style.width = `${width}px`; - this.highlightStage.style.height = `${height}px`; - this.highlightStage.style.top = `${this.positionToHighlight.top - (requiredPadding / 2)}px`; - this.highlightStage.style.left = `${this.positionToHighlight.left - (requiredPadding / 2)}px`; + this.stage.show(this.positionToHighlight); // Element has been highlighted this.highlightedElement.onHighlighted(); diff --git a/src/core/popover.js b/src/core/popover.js index 7fc60aab1138b2662b40e241166aefce4ee53e1e..cf845f11135c9d5332b67b4ad7b546f4809cc18e 100644 --- a/src/core/popover.js +++ b/src/core/popover.js @@ -9,6 +9,7 @@ import { ID_POPOVER, POPOVER_HTML, } from '../common/constants'; +import { createNodeFromString } from '../common/utils'; /** * Popover that is displayed on top of the highlighted element @@ -48,10 +49,11 @@ export default class Popover extends Element { makeNode() { let popover = this.document.getElementById(ID_POPOVER); if (!popover) { - popover = Popover.createFromString(POPOVER_HTML); + popover = createNodeFromString(POPOVER_HTML); document.body.appendChild(popover); } + this.node = popover; this.tipNode = popover.querySelector(`.${CLASS_POPOVER_TIP}`); this.titleNode = popover.querySelector(`.${CLASS_POPOVER_TITLE}`); @@ -62,30 +64,6 @@ export default class Popover extends Element { this.closeBtnNode = popover.querySelector(`.${CLASS_CLOSE_BTN}`); } - /** - * Turn a string into a node - * @param {String} htmlString to convert - * @return {Node} Converted node element - */ - static createFromString(htmlString) { - const div = document.createElement('div'); - div.innerHTML = htmlString.trim(); - - // Change this to div.childNodes to support multiple top-level nodes - return div.firstChild; - } - - /** - * Gets the size for popover - * @returns {{height: number, width: number}} - */ - getSize() { - return { - height: Math.max(this.node.scrollHeight, this.node.offsetHeight), - width: Math.max(this.node.scrollWidth, this.node.offsetWidth), - }; - } - hide() { this.node.style.display = 'none'; } diff --git a/src/core/stage.js b/src/core/stage.js new file mode 100644 index 0000000000000000000000000000000000000000..40b4d806c9acac0b5dbc419adf77a938a9352aa5 --- /dev/null +++ b/src/core/stage.js @@ -0,0 +1,75 @@ +import { ID_STAGE, POPOVER_HTML, STAGE_HTML } from '../common/constants'; +import { createNodeFromString } from '../common/utils'; +import Element from './element'; + +/** + * Stage behind the highlighted element to give it a little + * highlight from rest of the page + */ +export default class Stage extends Element { + /** + * @param {Object} options + * @param {Window} window + * @param {Document} document + */ + constructor(options, window, document) { + super(); + + this.options = options; + this.window = window; + this.document = document; + + this.makeNode(); + this.hide(); + } + + /** + * Prepares the DOM element if not already there + */ + makeNode() { + let stage = this.document.getElementById(ID_STAGE); + if (!stage) { + stage = createNodeFromString(STAGE_HTML); + document.body.appendChild(stage); + } + + this.node = stage; + } + + /** + * Simply hides the stage + */ + hide() { + this.node.style.display = 'none'; + } + + /** + * Makes it visible and sets the default properties + */ + reset() { + this.node.style.display = 'block'; + this.node.style.left = '0'; + this.node.style.top = '0'; + this.node.style.bottom = ''; + this.node.style.right = ''; + } + + show(position) { + this.reset(); + + // Make it two times the padding because, half will be given on left and half on right + const requiredPadding = this.options.padding * 2; + + const width = (position.right - position.left) + (requiredPadding); + const height = (position.bottom - position.top) + (requiredPadding); + + // Show the stage + this.node.style.display = 'block'; + this.node.style.position = 'absolute'; + this.node.style.width = `${width}px`; + this.node.style.height = `${height}px`; + this.node.style.top = `${position.top - (requiredPadding / 2)}px`; + this.node.style.left = `${position.left - (requiredPadding / 2)}px`; + } +} + diff --git a/src/index.js b/src/index.js index 2f5bda02caea46e5513d2cac3cdfbdf05d62b07b..712118fd312024cbbd7acc2707ba5425056d7b3a 100644 --- a/src/index.js +++ b/src/index.js @@ -14,6 +14,7 @@ import { OVERLAY_PADDING, RIGHT_KEY_CODE, } from './common/constants'; +import Stage from './core/stage'; /** * Plugin class that drives the plugin @@ -38,13 +39,13 @@ export default class Driver { this.document = document; this.window = window; - this.isActivated = false; - this.overlay = new Overlay(this.options, this.window, this.document); - 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.onResize = this.onResize.bind(this); this.onKeyUp = this.onKeyUp.bind(this); this.onClick = this.onClick.bind(this); @@ -242,11 +243,16 @@ export default class Driver { let querySelector = ''; let elementOptions = {}; + // If it is just a query selector string if (typeof currentStep === 'string') { querySelector = currentStep; } else { querySelector = currentStep.element; - elementOptions = Object.assign({}, this.options, currentStep); + elementOptions = Object.assign( + {}, + this.options, + currentStep, + ); } const domElement = this.document.querySelector(querySelector); @@ -260,7 +266,8 @@ export default class Driver { const popoverOptions = Object.assign( {}, this.options, - elementOptions.popover, { + elementOptions.popover, + { totalCount: allSteps.length, currentIndex: index, isFirst: index === 0,