sholo.js 4.9 KB
Newer Older
K
Kamran Ahmed 已提交
1 2
import Overlay from './overlay';
import Element from './element';
K
Kamran Ahmed 已提交
3
import './polyfill';
4

K
Kamran Ahmed 已提交
5 6 7 8
/**
 * Plugin class that drives the plugin
 */
export default class Sholo {
K
Kamran Ahmed 已提交
9
  /**
K
Kamran Ahmed 已提交
10
   * @param options
K
Kamran Ahmed 已提交
11
   */
K
Kamran Ahmed 已提交
12 13 14 15 16 17 18 19
  constructor(options = {}) {
    this.options = Object.assign({
      padding: 10,
      animate: true,
      opacity: 0.75,
    }, options);

    this.overlay = new Overlay(options);
K
Kamran Ahmed 已提交
20

K
Kamran Ahmed 已提交
21 22 23
    this.document = document;
    this.window = window;

K
Kamran Ahmed 已提交
24 25 26
    this.steps = [];            // steps to be presented if any
    this.currentStep = 0;       // index for the currently highlighted step

K
Kamran Ahmed 已提交
27 28
    this.onScroll = this.onScroll.bind(this);
    this.onResize = this.onResize.bind(this);
K
Kamran Ahmed 已提交
29
    this.onKeyUp = this.onKeyUp.bind(this);
K
Kamran Ahmed 已提交
30
    this.onClick = this.onClick.bind(this);
K
Kamran Ahmed 已提交
31 32 33 34 35

    // Event bindings
    this.bind();
  }

K
Kamran Ahmed 已提交
36 37 38 39
  /**
   * Binds any DOM events listeners
   * @todo: add throttling in all the listeners
   */
K
Kamran Ahmed 已提交
40 41 42 43
  bind() {
    this.document.addEventListener('scroll', this.onScroll, false);
    this.document.addEventListener('DOMMouseScroll', this.onScroll, false);
    this.window.addEventListener('resize', this.onResize, false);
K
Kamran Ahmed 已提交
44
    this.window.addEventListener('keyup', this.onKeyUp, false);
K
Kamran Ahmed 已提交
45
    this.window.addEventListener('click', this.onClick, false);
46 47
  }

K
Kamran Ahmed 已提交
48 49 50 51 52
  /**
   * Removes the popover if clicked outside the highlighted element
   * or outside the
   * @param e
   */
K
Kamran Ahmed 已提交
53 54 55
  onClick(e) {
    if (!this.hasHighlightedElement()) {
      // Has no highlighted element so ignore the click
56 57 58
      return;
    }

K
Kamran Ahmed 已提交
59 60 61
    const highlightedElement = this.overlay.getHighlightedElement();
    const popover = document.getElementById('sholo-popover-item');

K
Kamran Ahmed 已提交
62 63 64
    const clickedHighlightedElement = highlightedElement.node.contains(e.target);
    const clickedPopover = popover && popover.contains(e.target);

65
    // Remove the overlay If clicked outside the highlighted element
K
Kamran Ahmed 已提交
66
    if (!clickedHighlightedElement && !clickedPopover) {
67
      this.overlay.clear();
K
Kamran Ahmed 已提交
68 69 70 71
      return;
    }

    const nextClicked = e.target.classList.contains('sholo-next-btn');
K
Kamran Ahmed 已提交
72 73 74
    const prevClicked = e.target.classList.contains('sholo-prev-btn');
    const closeClicked = e.target.classList.contains('sholo-close-btn');

K
Kamran Ahmed 已提交
75
    if (nextClicked) {
K
Kamran Ahmed 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
      this.moveNext();
    } else if (prevClicked) {
      this.movePrevious();
    } else if (closeClicked) {
      this.reset();
    }
  }

  /**
   * Moves to the previous step if possible
   * otherwise resets the overlay
   */
  movePrevious() {
    this.currentStep -= 1;
    if (this.steps[this.currentStep]) {
      this.overlay.highlight(this.steps[this.currentStep]);
    } else {
      this.reset();
    }
  }

  /**
   * Moves to the next step if possible
   * otherwise resets the overlay
   */
  moveNext() {
    this.currentStep += 1;
    if (this.steps[this.currentStep]) {
K
Kamran Ahmed 已提交
104
      this.overlay.highlight(this.steps[this.currentStep]);
K
Kamran Ahmed 已提交
105 106
    } else {
      this.reset();
107
    }
K
Kamran Ahmed 已提交
108 109
  }

K
Kamran Ahmed 已提交
110 111 112 113 114 115 116 117 118 119 120 121
  /**
   * Resets the steps if any and clears the overlay
   */
  reset() {
    this.currentStep = 0;
    this.overlay.clear();
  }

  /**
   * Checks if there is any highlighted element or not
   * @returns {boolean}
   */
K
Kamran Ahmed 已提交
122 123 124 125 126
  hasHighlightedElement() {
    const highlightedElement = this.overlay.getHighlightedElement();
    return highlightedElement && highlightedElement.node;
  }

K
Kamran Ahmed 已提交
127 128 129 130 131
  /**
   * Handler for the onScroll event on document
   * Refreshes without animation on scroll to make sure
   * that the highlighted part travels with the scroll
   */
K
Kamran Ahmed 已提交
132 133 134 135
  onScroll() {
    this.overlay.refresh(false);
  }

K
Kamran Ahmed 已提交
136 137 138 139 140
  /**
   * Handler for the onResize DOM event
   * Refreshes with animation on scroll to make sure that
   * the highlighted part travels with the width change of window
   */
K
Kamran Ahmed 已提交
141 142 143
  onResize() {
    // Refresh with animation
    this.overlay.refresh(true);
144 145
  }

K
Kamran Ahmed 已提交
146 147 148 149
  /**
   * Clears the overlay on escape key process
   * @param event
   */
K
Kamran Ahmed 已提交
150 151 152 153 154 155
  onKeyUp(event) {
    if (event.keyCode === 27) {
      this.overlay.clear();
    }
  }

K
Kamran Ahmed 已提交
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
  defineSteps(steps) {
    this.steps = [];

    steps.forEach((step, index) => {
      if (!step.element) {
        throw new Error(`Element (query selector or a dom element) missing in step ${index}`);
      }

      const domElement = Sholo.findDomElement(step.element);
      const element = new Element(domElement, Object.assign({}, this.options, step));

      this.steps.push(element);
    });
  }

  start() {
    if (!this.steps || this.steps.length === 0) {
      throw new Error('There are no steps defined to iterate');
    }

    this.currentStep = 0;
    this.overlay.highlight(this.steps[0]);
  }

K
Kamran Ahmed 已提交
180 181 182 183
  /**
   * Highlights the given selector
   * @param selector
   */
K
Kamran Ahmed 已提交
184
  highlight(selector) {
K
Kamran Ahmed 已提交
185 186 187 188 189
    const domElement = Sholo.findDomElement(selector);

    const element = new Element(domElement, this.options);
    this.overlay.highlight(element);
  }
190

K
Kamran Ahmed 已提交
191
  static findDomElement(selector) {
K
Kamran Ahmed 已提交
192
    if (typeof selector === 'string') {
K
Kamran Ahmed 已提交
193
      return document.querySelector(selector);
K
Kamran Ahmed 已提交
194
    }
195

K
Kamran Ahmed 已提交
196 197
    if (typeof selector === 'object') {
      return selector;
K
Kamran Ahmed 已提交
198
    }
K
Kamran Ahmed 已提交
199 200

    throw new Error('Element can only be string or the dom element');
201 202
  }
}