1 | import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
|
2 | import _typeof from 'babel-runtime/helpers/typeof';
|
3 |
|
4 | var _class, _temp, _initialiseProps;
|
5 |
|
6 | import { dom } from '../../util';
|
7 | import findNode from './find-node';
|
8 |
|
9 | var VIEWPORT = 'viewport';
|
10 |
|
11 |
|
12 | var getPageX = function getPageX() {
|
13 | return window.pageXOffset || document.documentElement.scrollLeft;
|
14 | };
|
15 | var getPageY = function getPageY() {
|
16 | return window.pageYOffset || document.documentElement.scrollTop;
|
17 | };
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 | function _getSize(element) {
|
25 |
|
26 |
|
27 | if ('offsetWidth' in element && 'offsetHeight' in element) {
|
28 | return {
|
29 | width: element.offsetWidth,
|
30 | height: element.offsetHeight
|
31 | };
|
32 | } else {
|
33 | var _element$getBoundingC = element.getBoundingClientRect(),
|
34 | width = _element$getBoundingC.width,
|
35 | height = _element$getBoundingC.height;
|
36 |
|
37 | return {
|
38 | width: width,
|
39 | height: height
|
40 | };
|
41 | }
|
42 | }
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 | function _getElementRect(elem, container) {
|
50 | var offsetTop = 0,
|
51 | offsetLeft = 0,
|
52 | scrollTop = 0,
|
53 | scrollLeft = 0;
|
54 |
|
55 | var _getSize2 = _getSize(elem),
|
56 | width = _getSize2.width,
|
57 | height = _getSize2.height;
|
58 |
|
59 | do {
|
60 | if (!isNaN(elem.offsetTop)) {
|
61 | offsetTop += elem.offsetTop;
|
62 | }
|
63 | if (!isNaN(elem.offsetLeft)) {
|
64 | offsetLeft += elem.offsetLeft;
|
65 | }
|
66 | if (elem && elem.offsetParent) {
|
67 | if (!isNaN(elem.offsetParent.scrollLeft) && elem.offsetParent !== document.body) {
|
68 | scrollLeft += elem.offsetParent.scrollLeft;
|
69 | }
|
70 |
|
71 | if (!isNaN(elem.offsetParent.scrollTop) && elem.offsetParent !== document.body) {
|
72 | scrollTop += elem.offsetParent.scrollTop;
|
73 | }
|
74 | }
|
75 |
|
76 | elem = elem.offsetParent;
|
77 | } while (elem !== null && elem !== container);
|
78 |
|
79 |
|
80 | var treatAsWindow = !container || container === document.body;
|
81 |
|
82 | return {
|
83 | top: offsetTop - scrollTop - (treatAsWindow ? document.documentElement.scrollTop || document.body.scrollTop : 0),
|
84 | left: offsetLeft - scrollLeft - (treatAsWindow ? document.documentElement.scrollLeft || document.body.scrollLeft : 0),
|
85 | width: width,
|
86 | height: height
|
87 | };
|
88 | }
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | function _getViewportSize(container) {
|
95 | if (!container || container === document.body) {
|
96 | return {
|
97 | width: document.documentElement.clientWidth,
|
98 | height: document.documentElement.clientHeight
|
99 | };
|
100 | }
|
101 |
|
102 | var _container$getBoundin = container.getBoundingClientRect(),
|
103 | width = _container$getBoundin.width,
|
104 | height = _container$getBoundin.height;
|
105 |
|
106 | return {
|
107 | width: width,
|
108 | height: height
|
109 | };
|
110 | }
|
111 |
|
112 | var getContainer = function getContainer(_ref) {
|
113 | var container = _ref.container,
|
114 | baseElement = _ref.baseElement;
|
115 |
|
116 |
|
117 | if ((typeof document === 'undefined' ? 'undefined' : _typeof(document)) === undefined) {
|
118 | return container;
|
119 | }
|
120 |
|
121 | var calcContainer = findNode(container, baseElement);
|
122 |
|
123 | if (!calcContainer) {
|
124 | calcContainer = document.body;
|
125 | }
|
126 |
|
127 | while (dom.getStyle(calcContainer, 'position') === 'static') {
|
128 | if (!calcContainer || calcContainer === document.body) {
|
129 | return document.body;
|
130 | }
|
131 | calcContainer = calcContainer.parentNode;
|
132 | }
|
133 |
|
134 | return calcContainer;
|
135 | };
|
136 |
|
137 | var Position = (_temp = _class = function () {
|
138 | function Position(props) {
|
139 | _classCallCheck(this, Position);
|
140 |
|
141 | _initialiseProps.call(this);
|
142 |
|
143 | this.pinElement = props.pinElement;
|
144 | this.baseElement = props.baseElement;
|
145 | this.pinFollowBaseElementWhenFixed = props.pinFollowBaseElementWhenFixed;
|
146 | this.container = getContainer(props);
|
147 | this.autoFit = props.autoFit || false;
|
148 | this.align = props.align || 'tl tl';
|
149 | this.offset = props.offset || [0, 0];
|
150 | this.needAdjust = props.needAdjust || false;
|
151 | this.isRtl = props.isRtl || false;
|
152 | }
|
153 |
|
154 | |
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 | Position.prototype.setPosition = function setPosition() {
|
168 | var pinElement = this.pinElement;
|
169 | var baseElement = this.baseElement;
|
170 | var pinFollowBaseElementWhenFixed = this.pinFollowBaseElementWhenFixed;
|
171 | var expectedAlign = this._getExpectedAlign();
|
172 | var isPinFixed = void 0,
|
173 | isBaseFixed = void 0,
|
174 | firstPositionResult = void 0;
|
175 | if (pinElement === VIEWPORT) {
|
176 | return;
|
177 | }
|
178 | if (dom.getStyle(pinElement, 'position') !== 'fixed') {
|
179 | dom.setStyle(pinElement, 'position', 'absolute');
|
180 | isPinFixed = false;
|
181 | } else {
|
182 | isPinFixed = true;
|
183 | }
|
184 | if (baseElement === VIEWPORT || dom.getStyle(baseElement, 'position') !== 'fixed') {
|
185 | isBaseFixed = false;
|
186 | } else {
|
187 | isBaseFixed = true;
|
188 | }
|
189 |
|
190 |
|
191 | for (var i = 0; i < expectedAlign.length; i++) {
|
192 | var align = expectedAlign[i];
|
193 | var pinElementPoints = this._normalizePosition(pinElement, align.split(' ')[0], isPinFixed);
|
194 | var baseElementPoints = this._normalizePosition(baseElement, align.split(' ')[1],
|
195 |
|
196 | isPinFixed && !pinFollowBaseElementWhenFixed);
|
197 |
|
198 | var pinElementParentOffset = this._getParentOffset(pinElement);
|
199 | var pinElementParentScrollOffset = this._getParentScrollOffset(pinElement);
|
200 |
|
201 | var baseElementOffset = isPinFixed && isBaseFixed ? this._getLeftTop(baseElement) :
|
202 | baseElementPoints.offset(isPinFixed && pinFollowBaseElementWhenFixed);
|
203 | var top = baseElementOffset.top + baseElementPoints.y - pinElementParentOffset.top - pinElementPoints.y + pinElementParentScrollOffset.top;
|
204 | var left = baseElementOffset.left + baseElementPoints.x - pinElementParentOffset.left - pinElementPoints.x + pinElementParentScrollOffset.left;
|
205 |
|
206 | this._setPinElementPostion(pinElement, { left: left, top: top }, this.offset);
|
207 |
|
208 | if (this._isInViewport(pinElement, align)) {
|
209 | return align;
|
210 | } else if (!firstPositionResult) {
|
211 | if (this.needAdjust && !this.autoFit) {
|
212 | var _getViewportOffset2 = this._getViewportOffset(pinElement, align),
|
213 | right = _getViewportOffset2.right;
|
214 |
|
215 | firstPositionResult = {
|
216 | left: right < 0 ? left + right : left,
|
217 | top: top
|
218 | };
|
219 | } else {
|
220 | firstPositionResult = { left: left, top: top };
|
221 | }
|
222 | }
|
223 | }
|
224 |
|
225 |
|
226 | var inViewportLeft = this._makeElementInViewport(pinElement, firstPositionResult.left, 'Left', isPinFixed);
|
227 | var inViewportTop = this._makeElementInViewport(pinElement, firstPositionResult.top, 'Top', isPinFixed);
|
228 |
|
229 | this._setPinElementPostion(pinElement, { left: inViewportLeft, top: inViewportTop }, this._calPinOffset(expectedAlign[0]));
|
230 |
|
231 | return expectedAlign[0];
|
232 | };
|
233 |
|
234 | Position.prototype._getParentOffset = function _getParentOffset(element) {
|
235 | var parent = element.offsetParent || document.documentElement;
|
236 | var offset = void 0;
|
237 | if (parent === document.body && dom.getStyle(parent, 'position') === 'static') {
|
238 | offset = {
|
239 | top: 0,
|
240 | left: 0
|
241 | };
|
242 | } else {
|
243 | offset = this._getElementOffset(parent);
|
244 | }
|
245 |
|
246 | offset.top += parseFloat(dom.getStyle(parent, 'border-top-width'), 10);
|
247 | offset.left += parseFloat(dom.getStyle(parent, 'border-left-width'), 10);
|
248 | offset.offsetParent = parent;
|
249 | return offset;
|
250 | };
|
251 |
|
252 | Position.prototype._makeElementInViewport = function _makeElementInViewport(pinElement, number, type, isPinFixed) {
|
253 |
|
254 |
|
255 | var result = number;
|
256 | var docElement = document.documentElement;
|
257 | var offsetParent = pinElement.offsetParent || document.documentElement;
|
258 |
|
259 | if (result < 0) {
|
260 | if (isPinFixed) {
|
261 | result = 0;
|
262 | } else if (offsetParent === document.body && dom.getStyle(offsetParent, 'position') === 'static') {
|
263 |
|
264 | result = Math.max(docElement['scroll' + type], document.body['scroll' + type]);
|
265 | }
|
266 | }
|
267 | return result;
|
268 | };
|
269 |
|
270 |
|
271 |
|
272 |
|
273 | Position.prototype._normalizePosition = function _normalizePosition(element, align, ignoreElementOffset) {
|
274 | var points = this._normalizeElement(element, ignoreElementOffset);
|
275 | this._normalizeXY(points, align);
|
276 |
|
277 | return points;
|
278 | };
|
279 |
|
280 | Position.prototype._normalizeXY = function _normalizeXY(points, align) {
|
281 | var x = align.split('')[1];
|
282 | var y = align.split('')[0];
|
283 |
|
284 | points.x = this._xyConverter(x, points, 'width');
|
285 | points.y = this._xyConverter(y, points, 'height');
|
286 |
|
287 | return points;
|
288 | };
|
289 |
|
290 | Position.prototype._xyConverter = function _xyConverter(align, points, type) {
|
291 | var res = align.replace(/t|l/gi, '0%').replace(/c/gi, '50%').replace(/b|r/gi, '100%').replace(/(\d+)%/gi, function (m, d) {
|
292 | return points.size()[type] * (d / 100);
|
293 | });
|
294 |
|
295 | return parseFloat(res, 10) || 0;
|
296 | };
|
297 |
|
298 | Position.prototype._getLeftTop = function _getLeftTop(element) {
|
299 | return {
|
300 | left: parseFloat(dom.getStyle(element, 'left')) || 0,
|
301 | top: parseFloat(dom.getStyle(element, 'top')) || 0
|
302 | };
|
303 | };
|
304 |
|
305 | Position.prototype._normalizeElement = function _normalizeElement(element, ignoreElementOffset) {
|
306 | var _this = this;
|
307 |
|
308 | var result = {
|
309 | element: element,
|
310 | x: 0,
|
311 | y: 0
|
312 | },
|
313 | isViewport = element === VIEWPORT,
|
314 | docElement = document.documentElement;
|
315 |
|
316 | result.offset = function (ignoreScroll) {
|
317 |
|
318 | if (ignoreElementOffset) {
|
319 | return {
|
320 | left: 0,
|
321 | top: 0
|
322 | };
|
323 | } else if (isViewport) {
|
324 | return {
|
325 | left: getPageX(),
|
326 | top: getPageY()
|
327 | };
|
328 | } else {
|
329 | return _this._getElementOffset(element, ignoreScroll);
|
330 | }
|
331 | };
|
332 |
|
333 | result.size = function () {
|
334 | if (isViewport) {
|
335 | return {
|
336 | width: docElement.clientWidth,
|
337 | height: docElement.clientHeight
|
338 | };
|
339 | } else {
|
340 | return _getSize(element);
|
341 | }
|
342 | };
|
343 |
|
344 | return result;
|
345 | };
|
346 |
|
347 |
|
348 |
|
349 |
|
350 |
|
351 | Position.prototype._getElementOffset = function _getElementOffset(element, ignoreScroll) {
|
352 | var rect = element.getBoundingClientRect();
|
353 | var docElement = document.documentElement;
|
354 | var body = document.body;
|
355 | var docClientLeft = docElement.clientLeft || body.clientLeft || 0;
|
356 | var docClientTop = docElement.clientTop || body.clientTop || 0;
|
357 |
|
358 | return {
|
359 | left: rect.left + (ignoreScroll ? 0 : getPageX()) - docClientLeft,
|
360 | top: rect.top + (ignoreScroll ? 0 : getPageY()) - docClientTop
|
361 | };
|
362 | };
|
363 |
|
364 |
|
365 |
|
366 |
|
367 | Position.prototype._getExpectedAlign = function _getExpectedAlign() {
|
368 | var align = this.isRtl ? this._replaceAlignDir(this.align, /l|r/g, { l: 'r', r: 'l' }) : this.align;
|
369 | var expectedAlign = [align];
|
370 | if (this.needAdjust) {
|
371 | if (/t|b/g.test(align)) {
|
372 | expectedAlign.push(this._replaceAlignDir(align, /t|b/g, { t: 'b', b: 't' }));
|
373 | }
|
374 | if (/l|r/g.test(align)) {
|
375 | expectedAlign.push(this._replaceAlignDir(align, /l|r/g, { l: 'r', r: 'l' }));
|
376 | }
|
377 | if (/c/g.test(align)) {
|
378 | expectedAlign.push(this._replaceAlignDir(align, /c(?= |$)/g, { c: 'l' }));
|
379 | expectedAlign.push(this._replaceAlignDir(align, /c(?= |$)/g, { c: 'r' }));
|
380 | }
|
381 | expectedAlign.push(this._replaceAlignDir(align, /l|r|t|b/g, {
|
382 | l: 'r',
|
383 | r: 'l',
|
384 | t: 'b',
|
385 | b: 't'
|
386 | }));
|
387 | }
|
388 | return expectedAlign;
|
389 | };
|
390 |
|
391 |
|
392 |
|
393 |
|
394 | Position.prototype._replaceAlignDir = function _replaceAlignDir(align, regExp, map) {
|
395 | return align.replace(regExp, function (res) {
|
396 | return map[res];
|
397 | });
|
398 | };
|
399 |
|
400 |
|
401 |
|
402 |
|
403 | Position.prototype._isRightAligned = function _isRightAligned(align) {
|
404 | var _align$split = align.split(' '),
|
405 | pinAlign = _align$split[0],
|
406 | baseAlign = _align$split[1];
|
407 |
|
408 | return pinAlign[1] === 'r' && pinAlign[1] === baseAlign[1];
|
409 | };
|
410 |
|
411 |
|
412 |
|
413 |
|
414 | Position.prototype._isBottomAligned = function _isBottomAligned(align) {
|
415 | var _align$split2 = align.split(' '),
|
416 | pinAlign = _align$split2[0],
|
417 | baseAlign = _align$split2[1];
|
418 |
|
419 | return pinAlign[0] === 'b' && pinAlign[0] === baseAlign[0];
|
420 | };
|
421 |
|
422 |
|
423 |
|
424 |
|
425 | Position.prototype._isInViewport = function _isInViewport(element, align) {
|
426 | var viewportSize = _getViewportSize(this.container);
|
427 | var elementRect = _getElementRect(element, this.container);
|
428 | var elementSize = _getSize(element);
|
429 |
|
430 |
|
431 |
|
432 |
|
433 | var viewportWidth = this._isRightAligned(align) ? viewportSize.width : viewportSize.width - 1;
|
434 | var viewportHeight = this._isBottomAligned(align) ? viewportSize.height : viewportSize.height - 1;
|
435 |
|
436 |
|
437 |
|
438 | if (this.autoFit) {
|
439 | return elementRect.top >= 0 && elementRect.top + element.offsetHeight <= viewportHeight;
|
440 | }
|
441 |
|
442 |
|
443 | return elementRect.left >= 0 && elementRect.left + elementSize.width <= viewportWidth && elementRect.top >= 0 && elementRect.top + elementSize.height <= viewportHeight;
|
444 | };
|
445 |
|
446 | Position.prototype._getViewportOffset = function _getViewportOffset(element, align) {
|
447 | var viewportSize = _getViewportSize(this.container);
|
448 | var elementRect = _getElementRect(element, this.container);
|
449 | var elementSize = _getSize(element);
|
450 |
|
451 | var viewportWidth = this._isRightAligned(align) ? viewportSize.width : viewportSize.width - 1;
|
452 | var viewportHeight = this._isBottomAligned(align) ? viewportSize.height : viewportSize.height - 1;
|
453 |
|
454 | return {
|
455 | top: elementRect.top,
|
456 | right: viewportWidth - (elementRect.left + elementSize.width),
|
457 | bottom: viewportHeight - (elementRect.top + elementSize.height),
|
458 | left: elementRect.left
|
459 | };
|
460 | };
|
461 |
|
462 |
|
463 |
|
464 |
|
465 | Position.prototype._setPinElementPostion = function _setPinElementPostion(pinElement, postion) {
|
466 | var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [0, 0];
|
467 | var top = postion.top,
|
468 | left = postion.left;
|
469 |
|
470 | if (!this.isRtl) {
|
471 | dom.setStyle(pinElement, {
|
472 | left: left + offset[0] + 'px',
|
473 | top: top + offset[1] + 'px'
|
474 | });
|
475 | return;
|
476 | }
|
477 |
|
478 |
|
479 | var pinElementParentOffset = this._getParentOffset(pinElement);
|
480 |
|
481 | var _getElementRect2 = _getElementRect(pinElementParentOffset.offsetParent),
|
482 | offsetParentWidth = _getElementRect2.width;
|
483 |
|
484 | var _getElementRect3 = _getElementRect(pinElement),
|
485 | width = _getElementRect3.width;
|
486 |
|
487 | var right = offsetParentWidth - (left + width);
|
488 | dom.setStyle(pinElement, {
|
489 | left: 'auto',
|
490 | right: right + offset[0] + 'px',
|
491 | top: top + offset[1] + 'px'
|
492 | });
|
493 | };
|
494 |
|
495 | return Position;
|
496 | }(), _class.VIEWPORT = VIEWPORT, _class.place = function (props) {
|
497 | return new Position(props).setPosition();
|
498 | }, _initialiseProps = function _initialiseProps() {
|
499 | var _this2 = this;
|
500 |
|
501 | this._calPinOffset = function (align) {
|
502 | var offset = [].concat(_this2.offset);
|
503 |
|
504 | if (_this2.autoFit && align && _this2.container && _this2.container !== document.body) {
|
505 | var baseElementRect = _getElementRect(_this2.baseElement, _this2.container);
|
506 | var pinElementRect = _getElementRect(_this2.pinElement, _this2.container);
|
507 | var viewportSize = _getViewportSize(_this2.container);
|
508 | var pinAlign = align.split(' ')[0];
|
509 | var x = pinAlign.charAt(1);
|
510 | var y = pinAlign.charAt(0);
|
511 |
|
512 | if (pinElementRect.top < 0 || pinElementRect.top + pinElementRect.height > viewportSize.height) {
|
513 | offset[1] = -baseElementRect.top - (y === 't' ? baseElementRect.height : 0);
|
514 | }
|
515 | }
|
516 | return offset;
|
517 | };
|
518 |
|
519 | this._getParentScrollOffset = function (elem) {
|
520 | var top = 0;
|
521 | var left = 0;
|
522 |
|
523 | if (elem && elem.offsetParent && elem.offsetParent !== document.body) {
|
524 | if (!isNaN(elem.offsetParent.scrollTop)) {
|
525 | top += elem.offsetParent.scrollTop;
|
526 | }
|
527 | if (!isNaN(elem.offsetParent.scrollLeft)) {
|
528 | left += elem.offsetParent.scrollLeft;
|
529 | }
|
530 | }
|
531 |
|
532 | return {
|
533 | top: top,
|
534 | left: left
|
535 | };
|
536 | };
|
537 | }, _temp);
|
538 | export { Position as default }; |
\ | No newline at end of file |