This repository has been archived by the owner on Jul 7, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
borderDistance.js
110 lines (96 loc) · 3.05 KB
/
borderDistance.js
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
import React from 'react';
/**
* Higher-order component (HOC) to get the distance to window border or scrollable element.
* @param {object} [style=null] Style for wrapper element
* @param {string} [className] className for wrapper element
* @returns {Component}
*/
export default function borderDistance(style = null, className) {
return function(BaseComponent) {
return class WrappedComponent extends React.Component {
constructor() {
super();
this.wrapperNode = undefined;
this.setWrapperNode = this.setWrapperNode.bind(this);
}
/**
* Set wrapperNode.
* @param {HTMLElement} ref
*/
setWrapperNode(ref) {
this.wrapperNode = ref;
this.forceUpdate();
}
/**
* Get distance.
* @returns {object}
*/
getDistance() {
const { wrapperNode } = this;
if (wrapperNode) {
// Get scrollable parent element.
const parentNode = this.getScrollParent(wrapperNode.parentNode);
// Get distance to parent or window border.
const distance = this.getDistanceToParent(wrapperNode, parentNode);
return distance;
}
return null;
}
/**
* Get parent scroll node.
* @param {HTMLElement} node
* @returns {HTMLElement}
*/
getScrollParent(node) {
if (node === null) {
return null;
}
if (node === document.body) {
return node;
}
const overflowY = window.getComputedStyle(node).overflowY;
const isScrollable = overflowY !== 'visible' && overflowY !== 'hidden';
if (isScrollable && node.scrollHeight > node.clientHeight) {
return node;
}
return this.getScrollParent(node.parentNode);
}
/**
* Get distance of children to parent element.
* @param {HTMLElement} childrenNode
* @param {HTMLElement} parentNode
* @returns {object} distance
*/
getDistanceToParent(childrenNode, parentNode) {
// Get rect of children and parent.
const childrenPos = childrenNode.getBoundingClientRect();
const parentPos =
parentNode && parentNode !== document.body
? parentNode.getBoundingClientRect()
: {
top: 0,
right: window.innerWidth,
bottom: window.innerHeight,
left: 0
};
const top = childrenPos.top - parentPos.top;
const right = parentPos.right - childrenPos.right;
const bottom = parentPos.bottom - childrenPos.bottom;
const left = childrenPos.left - parentPos.left;
return { top, right, bottom, left };
}
render() {
const distance = this.getDistance();
return (
<span
ref={this.setWrapperNode}
style={{ display: 'inline-block', ...style }}
className={className}
>
<BaseComponent distance={distance} {...this.props} />
</span>
);
}
};
};
}