overlay.js 4.4 KB
Newer Older
K
Kamran Ahmed 已提交
1
import Position from './position';
2
import { ANIMATION_DURATION_MS, CLASS_NO_ANIMATION, ID_OVERLAY, OVERLAY_HTML } from '../common/constants';
K
Kamran Ahmed 已提交
3
import { createNodeFromString } from '../common/utils';
K
Kamran Ahmed 已提交
4 5 6 7 8 9

/**
 * Responsible for overlay creation and manipulation i.e.
 * cutting out the visible part, animating between the sections etc
 */
export default class Overlay {
K
Kamran Ahmed 已提交
10
  /**
K
Kamran Ahmed 已提交
11 12 13
   * @param {Object} options
   * @param {Window} window
   * @param {Document} document
K
Kamran Ahmed 已提交
14
   */
K
Kamran Ahmed 已提交
15
  constructor(options, window, document) {
K
Kamran Ahmed 已提交
16
    this.options = options;
17 18 19

    this.positionToHighlight = new Position({}); // position at which layover is to be patched at
    this.highlightedElement = null;              // currently highlighted dom element (instance of Element)
20
    this.lastHighlightedElement = null;          // element that was highlighted before current one
K
Kamran Ahmed 已提交
21
    this.hideTimer = null;
K
Kamran Ahmed 已提交
22

K
Kamran Ahmed 已提交
23
    this.window = window;
K
Kamran Ahmed 已提交
24
    this.document = document;
K
Kamran Ahmed 已提交
25 26
  }

K
Kamran Ahmed 已提交
27 28 29
  /**
   * Prepares the overlay
   */
30 31 32 33 34 35
  makeNode() {
    let pageOverlay = this.document.getElementById(ID_OVERLAY);
    if (!pageOverlay) {
      pageOverlay = createNodeFromString(OVERLAY_HTML);
      document.body.appendChild(pageOverlay);
    }
K
Kamran Ahmed 已提交
36

37
    this.node = pageOverlay;
38
    this.node.style.opacity = '0';
39 40 41 42 43 44

    if (!this.options.animate) {
      this.node.classList.add(CLASS_NO_ANIMATION);
    } else {
      this.node.classList.remove(CLASS_NO_ANIMATION);
    }
K
Kamran Ahmed 已提交
45 46
  }

K
Kamran Ahmed 已提交
47 48
  /**
   * Highlights the dom element on the screen
K
Kamran Ahmed 已提交
49
   * @param {Element} element
K
Kamran Ahmed 已提交
50
   */
K
Kamran Ahmed 已提交
51
  highlight(element) {
K
Kamran Ahmed 已提交
52
    if (!element || !element.node) {
53
      console.warn('Invalid element to highlight. Must be an instance of `Element`');
K
Kamran Ahmed 已提交
54 55 56
      return;
    }

K
Kamran Ahmed 已提交
57 58 59 60 61
    // If highlighted element is not changed from last time
    if (this.highlightedElement && this.highlightedElement.isSame(this.lastHighlightedElement)) {
      return;
    }

K
Kamran Ahmed 已提交
62 63 64 65
    // There might be hide timer from last time
    // which might be getting triggered
    this.window.clearTimeout(this.hideTimer);

K
Kamran Ahmed 已提交
66 67 68 69
    // Trigger the hook for highlight started
    element.onHighlightStarted();

    // Old element has been deselected
K
Kamran Ahmed 已提交
70
    if (this.highlightedElement && !this.highlightedElement.isSame(this.lastHighlightedElement)) {
K
Kamran Ahmed 已提交
71 72 73
      this.highlightedElement.onDeselected();
    }

K
Kamran Ahmed 已提交
74
    // get the position of element around which we need to draw
K
Kamran Ahmed 已提交
75
    const position = element.getCalculatedPosition();
76
    if (!position.canHighlight()) {
K
Kamran Ahmed 已提交
77 78 79
      return;
    }

80
    this.lastHighlightedElement = this.highlightedElement;
81 82
    this.highlightedElement = element;
    this.positionToHighlight = position;
K
Kamran Ahmed 已提交
83

84
    this.show();
K
Kamran Ahmed 已提交
85 86 87 88 89

    // Element has been highlighted
    this.highlightedElement.onHighlighted();
  }

90 91 92 93 94
  show() {
    if (this.node && this.node.parentElement) {
      return;
    }

95 96 97 98 99 100 101 102 103 104
    this.makeNode();

    window.setTimeout(() => {
      this.node.style.opacity = `${this.options.opacity}`;
      this.node.style.position = 'fixed';
      this.node.style.left = '0';
      this.node.style.top = '0';
      this.node.style.bottom = '0';
      this.node.style.right = '0';
    });
K
Kamran Ahmed 已提交
105 106 107 108 109 110 111 112 113 114 115
  }

  hideOverlay() {
    this.node.style.opacity = '0';

    this.hideTimer = window.setTimeout(() => {
      this.node.style.position = 'absolute';
      this.node.style.left = '';
      this.node.style.top = '';
      this.node.style.bottom = '';
      this.node.style.right = '';
116 117

      this.node.parentElement.removeChild(this.node);
K
Kamran Ahmed 已提交
118
    }, ANIMATION_DURATION_MS);
K
Kamran Ahmed 已提交
119 120
  }

121 122 123 124 125 126 127 128
  /**
   * Returns the currently selected element
   * @returns {null|*}
   */
  getHighlightedElement() {
    return this.highlightedElement;
  }

129 130 131 132 133 134 135 136
  /**
   * Gets the element that was highlighted before current element
   * @returns {null|*}
   */
  getLastHighlightedElement() {
    return this.lastHighlightedElement;
  }

K
Kamran Ahmed 已提交
137 138 139
  /**
   * Removes the overlay and cancel any listeners
   */
K
Kamran Ahmed 已提交
140
  clear() {
141
    this.positionToHighlight = new Position();
K
Kamran Ahmed 已提交
142
    if (this.highlightedElement) {
K
Kamran Ahmed 已提交
143
      this.highlightedElement.onDeselected(true);
K
Kamran Ahmed 已提交
144 145
    }

146
    this.highlightedElement = null;
147
    this.lastHighlightedElement = null;
148

K
Kamran Ahmed 已提交
149
    this.hideOverlay();
K
Kamran Ahmed 已提交
150 151
  }

K
Kamran Ahmed 已提交
152 153 154 155
  /**
   * Refreshes the overlay i.e. sets the size according to current window size
   * And moves the highlight around if necessary
   */
K
Kamran Ahmed 已提交
156
  refresh() {
K
Kamran Ahmed 已提交
157 158 159
    // If no highlighted element, cancel the refresh
    if (!this.highlightedElement) {
      return;
160
    }
K
Kamran Ahmed 已提交
161 162 163 164

    // Reposition the stage and show popover
    this.highlightedElement.showPopover();
    this.highlightedElement.showStage();
K
Kamran Ahmed 已提交
165 166
  }
}