UNPKG

9.79 kBJavaScriptView Raw
1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3/**
4 * The class name added to all hover boxes.
5 */
6const HOVERBOX_CLASS = 'jp-HoverBox';
7/**
8 * The class name added to a hovering node that is scrolled out of view.
9 */
10const OUTOFVIEW_CLASS = 'jp-mod-outofview';
11/**
12 * A namespace for `HoverBox` members.
13 */
14export var HoverBox;
15(function (HoverBox) {
16 /**
17 * Set the visible dimensions of a hovering box anchored to an editor cursor.
18 *
19 * @param options - The hover box geometry calculation options.
20 */
21 function setGeometry(options) {
22 const { anchor, host, node, privilege, outOfViewDisplay } = options;
23 // Add hover box class if it does not exist.
24 node.classList.add(HOVERBOX_CLASS);
25 // Make sure the node is visible so that its dimensions can be queried.
26 node.classList.remove(OUTOFVIEW_CLASS);
27 // Clear any previously set max-height.
28 node.style.maxHeight = '';
29 // Clear any programmatically set margin-top.
30 node.style.marginTop = '';
31 const style = options.style || window.getComputedStyle(node);
32 const innerHeight = window.innerHeight;
33 const spaceAbove = anchor.top;
34 const spaceBelow = innerHeight - anchor.bottom;
35 const marginTop = parseInt(style.marginTop, 10) || 0;
36 const marginLeft = parseInt(style.marginLeft, 10) || 0;
37 const minHeight = parseInt(style.minHeight, 10) || options.minHeight;
38 let maxHeight = parseInt(style.maxHeight, 10) || options.maxHeight;
39 // Determine whether to render above or below; check privilege.
40 const renderBelow = privilege === 'forceAbove'
41 ? false
42 : privilege === 'forceBelow'
43 ? true
44 : privilege === 'above'
45 ? spaceAbove < maxHeight && spaceAbove < spaceBelow
46 : spaceBelow >= maxHeight || spaceBelow >= spaceAbove;
47 if (renderBelow) {
48 maxHeight = Math.min(spaceBelow - marginTop, maxHeight);
49 }
50 else {
51 maxHeight = Math.min(spaceAbove, maxHeight);
52 // If the box renders above the text, its top margin is irrelevant.
53 node.style.marginTop = '0px';
54 }
55 node.style.maxHeight = `${maxHeight}px`;
56 // Make sure the box ought to be visible.
57 const withinBounds = maxHeight > minHeight &&
58 (spaceBelow >= minHeight || spaceAbove >= minHeight);
59 if (!withinBounds) {
60 node.classList.add(OUTOFVIEW_CLASS);
61 return;
62 }
63 // Position the box vertically.
64 const offsetAbove = (options.offset &&
65 options.offset.vertical &&
66 options.offset.vertical.above) ||
67 0;
68 const offsetBelow = (options.offset &&
69 options.offset.vertical &&
70 options.offset.vertical.below) ||
71 0;
72 let top = renderBelow
73 ? innerHeight - spaceBelow + offsetBelow
74 : spaceAbove - node.getBoundingClientRect().height + offsetAbove;
75 node.style.top = `${Math.floor(top)}px`;
76 // Position the box horizontally.
77 const offsetHorizontal = (options.offset && options.offset.horizontal) || 0;
78 let left = anchor.left + offsetHorizontal;
79 node.style.left = `${Math.ceil(left)}px`;
80 node.style.width = 'auto';
81 // Expand the menu width by the scrollbar size, if present.
82 if (node.scrollHeight >= maxHeight) {
83 node.style.width = `${2 * node.offsetWidth - node.clientWidth}`;
84 node.scrollTop = 0;
85 }
86 // Move left to fit in the window.
87 const rect = node.getBoundingClientRect();
88 const hostRect = host.getBoundingClientRect(); // Query together to avoid extra layout recalculation
89 const right = rect.right;
90 if (right > window.innerWidth) {
91 left -= right - window.innerWidth;
92 node.style.left = `${Math.ceil(left)}px`;
93 }
94 // Move right to fit in the window
95 if (left < offsetHorizontal - marginLeft) {
96 left = offsetHorizontal - marginLeft;
97 node.style.left = `${Math.ceil(left)}px`;
98 }
99 // Hide the hover box before querying the DOM for the anchor coordinates.
100 node.classList.add(OUTOFVIEW_CLASS);
101 const bottom = rect.bottom;
102 const includesLeftTop = host.contains(document.elementFromPoint(left, top));
103 const includesRightTop = host.contains(document.elementFromPoint(right, top));
104 const includesRightBottom = host.contains(document.elementFromPoint(right, bottom));
105 const includesLeftBottom = host.contains(document.elementFromPoint(left, bottom));
106 const topEdgeInside = includesLeftTop || includesRightTop;
107 const bottomEdgeInside = includesLeftBottom || includesRightBottom;
108 const leftEdgeInside = includesLeftTop || includesLeftBottom;
109 const rightEdgeInside = includesRightBottom || includesRightTop;
110 const height = bottom - top;
111 const width = right - left;
112 const overTheTop = top < hostRect.top;
113 const belowTheBottom = bottom > hostRect.bottom;
114 const beforeTheLeft = left + marginLeft < hostRect.left;
115 const afterTheRight = right > hostRect.right;
116 let hide = false;
117 let leftChanged = false;
118 let topChanged = false;
119 if (overTheTop) {
120 switch ((outOfViewDisplay === null || outOfViewDisplay === void 0 ? void 0 : outOfViewDisplay.top) || 'hidden-inside') {
121 case 'hidden-inside':
122 if (!topEdgeInside) {
123 hide = true;
124 }
125 break;
126 case 'hidden-outside':
127 if (!bottomEdgeInside) {
128 hide = true;
129 }
130 break;
131 case 'stick-inside':
132 if (hostRect.top > top) {
133 top = hostRect.top;
134 topChanged = true;
135 }
136 break;
137 case 'stick-outside':
138 if (hostRect.top > bottom) {
139 top = hostRect.top - height;
140 topChanged = true;
141 }
142 break;
143 }
144 }
145 if (belowTheBottom) {
146 switch ((outOfViewDisplay === null || outOfViewDisplay === void 0 ? void 0 : outOfViewDisplay.bottom) || 'hidden-outside') {
147 case 'hidden-inside':
148 if (!bottomEdgeInside) {
149 hide = true;
150 }
151 break;
152 case 'hidden-outside':
153 if (!topEdgeInside) {
154 hide = true;
155 }
156 break;
157 case 'stick-inside':
158 if (hostRect.bottom < bottom) {
159 top = hostRect.bottom - height;
160 topChanged = true;
161 }
162 break;
163 case 'stick-outside':
164 if (hostRect.bottom < top) {
165 top = hostRect.bottom;
166 topChanged = true;
167 }
168 break;
169 }
170 }
171 if (beforeTheLeft) {
172 switch ((outOfViewDisplay === null || outOfViewDisplay === void 0 ? void 0 : outOfViewDisplay.left) || 'hidden-inside') {
173 case 'hidden-inside':
174 if (!leftEdgeInside) {
175 hide = true;
176 }
177 break;
178 case 'hidden-outside':
179 if (!rightEdgeInside) {
180 hide = true;
181 }
182 break;
183 case 'stick-inside':
184 if (hostRect.left > left + marginLeft) {
185 left = hostRect.left - marginLeft;
186 leftChanged = true;
187 }
188 break;
189 case 'stick-outside':
190 if (hostRect.left > right) {
191 left = hostRect.left - marginLeft - width;
192 leftChanged = true;
193 }
194 break;
195 }
196 }
197 if (afterTheRight) {
198 switch ((outOfViewDisplay === null || outOfViewDisplay === void 0 ? void 0 : outOfViewDisplay.right) || 'hidden-outside') {
199 case 'hidden-inside':
200 if (!rightEdgeInside) {
201 hide = true;
202 }
203 break;
204 case 'hidden-outside':
205 if (!leftEdgeInside) {
206 hide = true;
207 }
208 break;
209 case 'stick-inside':
210 if (hostRect.right < right) {
211 left = hostRect.right - width;
212 leftChanged = true;
213 }
214 break;
215 case 'stick-outside':
216 if (hostRect.right < left) {
217 left = hostRect.right;
218 leftChanged = true;
219 }
220 break;
221 }
222 }
223 if (!hide) {
224 node.classList.remove(OUTOFVIEW_CLASS);
225 }
226 if (leftChanged) {
227 node.style.left = `${Math.ceil(left)}px`;
228 }
229 if (topChanged) {
230 node.style.top = `${Math.ceil(top)}px`;
231 }
232 }
233 HoverBox.setGeometry = setGeometry;
234})(HoverBox || (HoverBox = {}));
235//# sourceMappingURL=hoverbox.js.map
\No newline at end of file