element.js 4.5 KB
Newer Older
K
Kamran Ahmed 已提交
1 2
import Position from './position';

3 4 5 6
/**
 * Wrapper around DOMElements to enrich them
 * with the functionality necessary
 */
K
Kamran Ahmed 已提交
7
export default class Element {
K
Kamran Ahmed 已提交
8 9 10
  /**
   * DOM element object
   * @param node
K
Kamran Ahmed 已提交
11
   * @param options
12
   * @param popover
13 14 15
   * @param overlay
   * @param window
   * @param document
K
Kamran Ahmed 已提交
16
   */
17
  constructor(node, options, popover, overlay, window, document) {
K
Kamran Ahmed 已提交
18
    this.node = node;
K
Kamran Ahmed 已提交
19
    this.document = document;
K
Kamran Ahmed 已提交
20 21
    this.window = window;
    this.options = options;
22
    this.overlay = overlay;
23
    this.popover = popover;
K
Kamran Ahmed 已提交
24 25
  }

K
Kamran Ahmed 已提交
26 27 28 29
  /**
   * Gets the screen co-ordinates (x,y) for the current dom element
   * @returns {{x: number, y: number}}
   */
K
Kamran Ahmed 已提交
30
  getScreenCoordinates() {
K
Kamran Ahmed 已提交
31
    let tempNode = this.node;
K
Kamran Ahmed 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45

    let x = this.document.documentElement.offsetLeft;
    let y = this.document.documentElement.offsetTop;

    if (tempNode.offsetParent) {
      do {
        x += tempNode.offsetLeft;
        y += tempNode.offsetTop;
      } while (tempNode = tempNode.offsetParent);
    }

    return { x, y };
  }

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
  isInView() {
    let top = this.node.offsetTop;
    let left = this.node.offsetLeft;
    const width = this.node.offsetWidth;
    const height = this.node.offsetHeight;

    let el = this.node;

    while (el.offsetParent) {
      el = el.offsetParent;
      top += el.offsetTop;
      left += el.offsetLeft;
    }

    return (
      top >= this.window.pageYOffset &&
      left >= this.window.pageXOffset &&
      (top + height) <= (this.window.pageYOffset + this.window.innerHeight) &&
      (left + width) <= (this.window.pageXOffset + this.window.innerWidth)
    );
  }

  bringInView() {
    if (this.isInView()) {
      return;
    }

    const elementRect = this.getCalculatedPosition();
74 75
    const absoluteElementTop = elementRect.top + this.window.pageYOffset;
    const middle = absoluteElementTop - (this.window.innerHeight / 2);
76

77
    this.window.scrollTo(0, middle);
78 79
  }

K
Kamran Ahmed 已提交
80 81 82 83 84
  /**
   * Gets the calculated position on screen, around which
   * we need to draw
   */
  getCalculatedPosition() {
K
Kamran Ahmed 已提交
85 86 87 88 89 90 91 92 93 94
    const coordinates = this.getScreenCoordinates();
    const position = new Position({
      left: Number.MAX_VALUE,
      top: Number.MAX_VALUE,
      right: 0,
      bottom: 0,
    });

    // If we have the position for this element
    // and the element is visible on screen (has some height)
K
Kamran Ahmed 已提交
95
    if (typeof coordinates.x === 'number' && typeof coordinates.y === 'number' && (this.node.offsetWidth > 0 || this.node.offsetHeight > 0)) {
K
Kamran Ahmed 已提交
96 97
      position.left = Math.min(position.left, coordinates.x);
      position.top = Math.min(position.top, coordinates.y);
K
Kamran Ahmed 已提交
98 99
      position.right = Math.max(position.right, coordinates.x + this.node.offsetWidth);
      position.bottom = Math.max(position.bottom, coordinates.y + this.node.offsetHeight);
K
Kamran Ahmed 已提交
100 101 102 103
    }

    return position;
  }
104

K
Kamran Ahmed 已提交
105 106 107 108
  /**
   * Is called when element is about to be deselected
   * i.e. when moving the focus to next element of closing
   */
K
Kamran Ahmed 已提交
109
  onDeselected() {
K
Kamran Ahmed 已提交
110 111 112 113
    if (!this.popover) {
      return;
    }

114
    this.popover.hide();
K
Kamran Ahmed 已提交
115 116
  }

K
Kamran Ahmed 已提交
117 118 119 120 121
  /**
   * Is called when the element is about to be highlighted
   * i.e. either if overlay has started moving the highlight towards
   * this element of has just decided to highlight it
   */
K
Kamran Ahmed 已提交
122
  onHighlightStarted() {
K
Kamran Ahmed 已提交
123 124 125 126
    if (!this.popover) {
      return;
    }

K
Kamran Ahmed 已提交
127 128 129
    this.showPopover();
  }

130
  onHighlighted() {
K
Kamran Ahmed 已提交
131 132 133
    if (this.popover) {
      this.showPopover();
    }
134 135 136

    const highlightedElement = this;
    const lastHighlightedElement = this.overlay.getLastHighlightedElement();
K
Kamran Ahmed 已提交
137
    const popoverElement = this.popover;
138 139 140 141 142 143

    const highlightedNode = this.node;
    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
K
Kamran Ahmed 已提交
144 145 146 147 148
    if (highlightedNode !== lastHighlightedNode) {
      if (!highlightedElement.isInView()) {
        highlightedElement.bringInView();
      }

K
Kamran Ahmed 已提交
149
      if (popoverElement && !popoverElement.isInView()) {
K
Kamran Ahmed 已提交
150 151
        popoverElement.bringInView();
      }
152
    }
K
Kamran Ahmed 已提交
153 154 155 156 157
  }

  showPopover() {
    const position = this.getCalculatedPosition();

158
    this.popover.show(position);
159
  }
K
Kamran Ahmed 已提交
160

161
  getFullPageSize() {
K
Kamran Ahmed 已提交
162 163 164 165
    // eslint-disable-next-line prefer-destructuring
    const body = this.document.body;
    const html = this.document.documentElement;

166 167 168 169
    return {
      height: Math.max(body.scrollHeight, body.offsetHeight, html.scrollHeight, html.offsetHeight),
      width: Math.max(body.scrollWidth, body.offsetWidth, html.scrollWidth, html.offsetWidth),
    };
K
Kamran Ahmed 已提交
170
  }
K
Kamran Ahmed 已提交
171
}