File size: 4,031 Bytes
22c7264
 
 
8755ee2
 
 
0c119f7
8755ee2
0c119f7
 
22c7264
0c119f7
 
ede8cb9
22c7264
 
8755ee2
 
 
 
22c7264
0c119f7
22c7264
 
 
 
 
 
 
 
 
 
 
 
 
 
8755ee2
 
 
 
 
22c7264
 
 
 
 
 
 
 
 
 
0c119f7
22c7264
 
0c119f7
 
22c7264
 
 
 
b1c5f8c
ede8cb9
 
 
 
 
 
 
 
 
 
 
b1c5f8c
ede8cb9
 
 
 
ec45071
 
 
ede8cb9
 
ec45071
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ede8cb9
 
 
 
 
 
 
 
 
 
 
 
b1c5f8c
ec45071
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22c7264
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
74
75
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import Position from './position';

export default class Element {
  /**
   * DOM element object
   * @param node
   * @param options
   */
  constructor(node, options) {
    this.node = node;
    this.document = document;
    this.window = window;
    this.options = options;
    this.popover = this.getPopover();
  }

  /**
   * Gets the screen co-ordinates (x,y) for the current dom element
   * @returns {{x: number, y: number}}
   */
  getScreenCoordinates() {
    let tempNode = this.node;

    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 };
  }

  /**
   * Gets the calculated position on screen, around which
   * we need to draw
   */
  getCalculatedPosition() {
    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)
    if (typeof coordinates.x === 'number' && typeof coordinates.y === 'number' && (this.node.offsetWidth > 0 || this.node.offsetHeight > 0)) {
      position.left = Math.min(position.left, coordinates.x);
      position.top = Math.min(position.top, coordinates.y);
      position.right = Math.max(position.right, coordinates.x + this.node.offsetWidth);
      position.bottom = Math.max(position.bottom, coordinates.y + this.node.offsetHeight);
    }

    return position;
  }

  onDeselected() {
    // Will be called when element is about to be deselected
    this.hidePopover();
  }

  onHighlightStarted() {
    // Will be triggered when the element is about to be highlighted
    // i.e. overlay has started transitioning towards this element
    this.showPopover();
  }

  onHighlighted() {
    this.showPopover();
  }

  showPopover() {
    this.resetPopover();

    // Position at which the element is
    const position = this.getCalculatedPosition();

    const popoverTip = this.popover.querySelector('.sholo-popover-tip');

    const documentHeight = this.getDocumentHeight();
    const popoverHeight = this.getPopoverHeight();
    const popoverMargin = this.options.padding + 10;

    this.popover.style.left = `${position.left - this.options.padding}px`;

    // Calculate different dimensions after attaching popover
    const documentHeightAfterPopOver = position.bottom + popoverHeight + popoverMargin;

    // If adding popover would go out of the window height, then show it to the top
    if (documentHeightAfterPopOver >= documentHeight) {
      this.popover.style.top = `${position.top - popoverHeight - popoverMargin}px`;
      popoverTip.classList.add('bottom');
    } else {
      this.popover.style.top = `${position.bottom + popoverMargin}px`;
      popoverTip.classList.add('top');
    }
  }

  getPopover() {
    // @todo: Create if not there
    const popover = this.document.getElementById('sholo-popover-item');
    popover.style.position = 'absolute';

    return popover;
  }

  hidePopover() {
    this.popover.style.display = 'none';
  }

  getDocumentHeight() {
    // eslint-disable-next-line prefer-destructuring
    const body = this.document.body;
    const html = this.document.documentElement;

    return Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
  }

  getPopoverHeight() {
    return Math.max(this.popover.scrollHeight, this.popover.offsetHeight);
  }

  resetPopover() {
    this.popover.style.display = 'block';
    this.popover.style.left = '';
    this.popover.style.top = '';
    this.popover.style.bottom = '';
    this.popover.style.right = '';

    // Remove the positional classes from tip
    this.popover
      .querySelector('.sholo-popover-tip')
      .className = 'sholo-popover-tip';
  }
}