GPQAPP

Форк
0
/
popper-utils.js 
1062 строки · 33.1 Кб
1
/**!
2
 * @fileOverview Kickass library to create and place poppers near their reference elements.
3
 * @version 1.16.1
4
 * @license
5
 * Copyright (c) 2016 Federico Zivolo and contributors
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in all
15
 * copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
 * SOFTWARE.
24
 */
25
/**
26
 * Get CSS computed property of the given element
27
 * @method
28
 * @memberof Popper.Utils
29
 * @argument {Eement} element
30
 * @argument {String} property
31
 */
32
function getStyleComputedProperty(element, property) {
33
  if (element.nodeType !== 1) {
34
    return [];
35
  }
36
  // NOTE: 1 DOM access here
37
  const window = element.ownerDocument.defaultView;
38
  const css = window.getComputedStyle(element, null);
39
  return property ? css[property] : css;
40
}
41

42
/**
43
 * Returns the parentNode or the host of the element
44
 * @method
45
 * @memberof Popper.Utils
46
 * @argument {Element} element
47
 * @returns {Element} parent
48
 */
49
function getParentNode(element) {
50
  if (element.nodeName === 'HTML') {
51
    return element;
52
  }
53
  return element.parentNode || element.host;
54
}
55

56
/**
57
 * Returns the scrolling parent of the given element
58
 * @method
59
 * @memberof Popper.Utils
60
 * @argument {Element} element
61
 * @returns {Element} scroll parent
62
 */
63
function getScrollParent(element) {
64
  // Return body, `getScroll` will take care to get the correct `scrollTop` from it
65
  if (!element) {
66
    return document.body;
67
  }
68

69
  switch (element.nodeName) {
70
    case 'HTML':
71
    case 'BODY':
72
      return element.ownerDocument.body;
73
    case '#document':
74
      return element.body;
75
  }
76

77
  // Firefox want us to check `-x` and `-y` variations as well
78
  const { overflow, overflowX, overflowY } = getStyleComputedProperty(element);
79
  if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) {
80
    return element;
81
  }
82

83
  return getScrollParent(getParentNode(element));
84
}
85

86
/**
87
 * Returns the reference node of the reference object, or the reference object itself.
88
 * @method
89
 * @memberof Popper.Utils
90
 * @param {Element|Object} reference - the reference element (the popper will be relative to this)
91
 * @returns {Element} parent
92
 */
93
function getReferenceNode(reference) {
94
  return reference && reference.referenceNode ? reference.referenceNode : reference;
95
}
96

97
var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && typeof navigator !== 'undefined';
98

99
const isIE11 = isBrowser && !!(window.MSInputMethodContext && document.documentMode);
100
const isIE10 = isBrowser && /MSIE 10/.test(navigator.userAgent);
101

102
/**
103
 * Determines if the browser is Internet Explorer
104
 * @method
105
 * @memberof Popper.Utils
106
 * @param {Number} version to check
107
 * @returns {Boolean} isIE
108
 */
109
function isIE(version) {
110
  if (version === 11) {
111
    return isIE11;
112
  }
113
  if (version === 10) {
114
    return isIE10;
115
  }
116
  return isIE11 || isIE10;
117
}
118

119
/**
120
 * Returns the offset parent of the given element
121
 * @method
122
 * @memberof Popper.Utils
123
 * @argument {Element} element
124
 * @returns {Element} offset parent
125
 */
126
function getOffsetParent(element) {
127
  if (!element) {
128
    return document.documentElement;
129
  }
130

131
  const noOffsetParent = isIE(10) ? document.body : null;
132

133
  // NOTE: 1 DOM access here
134
  let offsetParent = element.offsetParent || null;
135
  // Skip hidden elements which don't have an offsetParent
136
  while (offsetParent === noOffsetParent && element.nextElementSibling) {
137
    offsetParent = (element = element.nextElementSibling).offsetParent;
138
  }
139

140
  const nodeName = offsetParent && offsetParent.nodeName;
141

142
  if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') {
143
    return element ? element.ownerDocument.documentElement : document.documentElement;
144
  }
145

146
  // .offsetParent will return the closest TH, TD or TABLE in case
147
  // no offsetParent is present, I hate this job...
148
  if (['TH', 'TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') {
149
    return getOffsetParent(offsetParent);
150
  }
151

152
  return offsetParent;
153
}
154

155
function isOffsetContainer(element) {
156
  const { nodeName } = element;
157
  if (nodeName === 'BODY') {
158
    return false;
159
  }
160
  return nodeName === 'HTML' || getOffsetParent(element.firstElementChild) === element;
161
}
162

163
/**
164
 * Finds the root node (document, shadowDOM root) of the given element
165
 * @method
166
 * @memberof Popper.Utils
167
 * @argument {Element} node
168
 * @returns {Element} root node
169
 */
170
function getRoot(node) {
171
  if (node.parentNode !== null) {
172
    return getRoot(node.parentNode);
173
  }
174

175
  return node;
176
}
177

178
/**
179
 * Finds the offset parent common to the two provided nodes
180
 * @method
181
 * @memberof Popper.Utils
182
 * @argument {Element} element1
183
 * @argument {Element} element2
184
 * @returns {Element} common offset parent
185
 */
186
function findCommonOffsetParent(element1, element2) {
187
  // This check is needed to avoid errors in case one of the elements isn't defined for any reason
188
  if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) {
189
    return document.documentElement;
190
  }
191

192
  // Here we make sure to give as "start" the element that comes first in the DOM
193
  const order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING;
194
  const start = order ? element1 : element2;
195
  const end = order ? element2 : element1;
196

197
  // Get common ancestor container
198
  const range = document.createRange();
199
  range.setStart(start, 0);
200
  range.setEnd(end, 0);
201
  const { commonAncestorContainer } = range;
202

203
  // Both nodes are inside #document
204
  if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) {
205
    if (isOffsetContainer(commonAncestorContainer)) {
206
      return commonAncestorContainer;
207
    }
208

209
    return getOffsetParent(commonAncestorContainer);
210
  }
211

212
  // one of the nodes is inside shadowDOM, find which one
213
  const element1root = getRoot(element1);
214
  if (element1root.host) {
215
    return findCommonOffsetParent(element1root.host, element2);
216
  } else {
217
    return findCommonOffsetParent(element1, getRoot(element2).host);
218
  }
219
}
220

221
/**
222
 * Gets the scroll value of the given element in the given side (top and left)
223
 * @method
224
 * @memberof Popper.Utils
225
 * @argument {Element} element
226
 * @argument {String} side `top` or `left`
227
 * @returns {number} amount of scrolled pixels
228
 */
229
function getScroll(element, side = 'top') {
230
  const upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft';
231
  const nodeName = element.nodeName;
232

233
  if (nodeName === 'BODY' || nodeName === 'HTML') {
234
    const html = element.ownerDocument.documentElement;
235
    const scrollingElement = element.ownerDocument.scrollingElement || html;
236
    return scrollingElement[upperSide];
237
  }
238

239
  return element[upperSide];
240
}
241

242
/*
243
 * Sum or subtract the element scroll values (left and top) from a given rect object
244
 * @method
245
 * @memberof Popper.Utils
246
 * @param {Object} rect - Rect object you want to change
247
 * @param {HTMLElement} element - The element from the function reads the scroll values
248
 * @param {Boolean} subtract - set to true if you want to subtract the scroll values
249
 * @return {Object} rect - The modifier rect object
250
 */
251
function includeScroll(rect, element, subtract = false) {
252
  const scrollTop = getScroll(element, 'top');
253
  const scrollLeft = getScroll(element, 'left');
254
  const modifier = subtract ? -1 : 1;
255
  rect.top += scrollTop * modifier;
256
  rect.bottom += scrollTop * modifier;
257
  rect.left += scrollLeft * modifier;
258
  rect.right += scrollLeft * modifier;
259
  return rect;
260
}
261

262
/*
263
 * Helper to detect borders of a given element
264
 * @method
265
 * @memberof Popper.Utils
266
 * @param {CSSStyleDeclaration} styles
267
 * Result of `getStyleComputedProperty` on the given element
268
 * @param {String} axis - `x` or `y`
269
 * @return {number} borders - The borders size of the given axis
270
 */
271

272
function getBordersSize(styles, axis) {
273
  const sideA = axis === 'x' ? 'Left' : 'Top';
274
  const sideB = sideA === 'Left' ? 'Right' : 'Bottom';
275

276
  return parseFloat(styles[`border${sideA}Width`]) + parseFloat(styles[`border${sideB}Width`]);
277
}
278

279
function getSize(axis, body, html, computedStyle) {
280
  return Math.max(body[`offset${axis}`], body[`scroll${axis}`], html[`client${axis}`], html[`offset${axis}`], html[`scroll${axis}`], isIE(10) ? parseInt(html[`offset${axis}`]) + parseInt(computedStyle[`margin${axis === 'Height' ? 'Top' : 'Left'}`]) + parseInt(computedStyle[`margin${axis === 'Height' ? 'Bottom' : 'Right'}`]) : 0);
281
}
282

283
function getWindowSizes(document) {
284
  const body = document.body;
285
  const html = document.documentElement;
286
  const computedStyle = isIE(10) && getComputedStyle(html);
287

288
  return {
289
    height: getSize('Height', body, html, computedStyle),
290
    width: getSize('Width', body, html, computedStyle)
291
  };
292
}
293

294
var _extends = Object.assign || function (target) {
295
  for (var i = 1; i < arguments.length; i++) {
296
    var source = arguments[i];
297

298
    for (var key in source) {
299
      if (Object.prototype.hasOwnProperty.call(source, key)) {
300
        target[key] = source[key];
301
      }
302
    }
303
  }
304

305
  return target;
306
};
307

308
/**
309
 * Given element offsets, generate an output similar to getBoundingClientRect
310
 * @method
311
 * @memberof Popper.Utils
312
 * @argument {Object} offsets
313
 * @returns {Object} ClientRect like output
314
 */
315
function getClientRect(offsets) {
316
  return _extends({}, offsets, {
317
    right: offsets.left + offsets.width,
318
    bottom: offsets.top + offsets.height
319
  });
320
}
321

322
/**
323
 * Get bounding client rect of given element
324
 * @method
325
 * @memberof Popper.Utils
326
 * @param {HTMLElement} element
327
 * @return {Object} client rect
328
 */
329
function getBoundingClientRect(element) {
330
  let rect = {};
331

332
  // IE10 10 FIX: Please, don't ask, the element isn't
333
  // considered in DOM in some circumstances...
334
  // This isn't reproducible in IE10 compatibility mode of IE11
335
  try {
336
    if (isIE(10)) {
337
      rect = element.getBoundingClientRect();
338
      const scrollTop = getScroll(element, 'top');
339
      const scrollLeft = getScroll(element, 'left');
340
      rect.top += scrollTop;
341
      rect.left += scrollLeft;
342
      rect.bottom += scrollTop;
343
      rect.right += scrollLeft;
344
    } else {
345
      rect = element.getBoundingClientRect();
346
    }
347
  } catch (e) {}
348

349
  const result = {
350
    left: rect.left,
351
    top: rect.top,
352
    width: rect.right - rect.left,
353
    height: rect.bottom - rect.top
354
  };
355

356
  // subtract scrollbar size from sizes
357
  const sizes = element.nodeName === 'HTML' ? getWindowSizes(element.ownerDocument) : {};
358
  const width = sizes.width || element.clientWidth || result.width;
359
  const height = sizes.height || element.clientHeight || result.height;
360

361
  let horizScrollbar = element.offsetWidth - width;
362
  let vertScrollbar = element.offsetHeight - height;
363

364
  // if an hypothetical scrollbar is detected, we must be sure it's not a `border`
365
  // we make this check conditional for performance reasons
366
  if (horizScrollbar || vertScrollbar) {
367
    const styles = getStyleComputedProperty(element);
368
    horizScrollbar -= getBordersSize(styles, 'x');
369
    vertScrollbar -= getBordersSize(styles, 'y');
370

371
    result.width -= horizScrollbar;
372
    result.height -= vertScrollbar;
373
  }
374

375
  return getClientRect(result);
376
}
377

378
function getOffsetRectRelativeToArbitraryNode(children, parent, fixedPosition = false) {
379
  const isIE10 = isIE(10);
380
  const isHTML = parent.nodeName === 'HTML';
381
  const childrenRect = getBoundingClientRect(children);
382
  const parentRect = getBoundingClientRect(parent);
383
  const scrollParent = getScrollParent(children);
384

385
  const styles = getStyleComputedProperty(parent);
386
  const borderTopWidth = parseFloat(styles.borderTopWidth);
387
  const borderLeftWidth = parseFloat(styles.borderLeftWidth);
388

389
  // In cases where the parent is fixed, we must ignore negative scroll in offset calc
390
  if (fixedPosition && isHTML) {
391
    parentRect.top = Math.max(parentRect.top, 0);
392
    parentRect.left = Math.max(parentRect.left, 0);
393
  }
394
  let offsets = getClientRect({
395
    top: childrenRect.top - parentRect.top - borderTopWidth,
396
    left: childrenRect.left - parentRect.left - borderLeftWidth,
397
    width: childrenRect.width,
398
    height: childrenRect.height
399
  });
400
  offsets.marginTop = 0;
401
  offsets.marginLeft = 0;
402

403
  // Subtract margins of documentElement in case it's being used as parent
404
  // we do this only on HTML because it's the only element that behaves
405
  // differently when margins are applied to it. The margins are included in
406
  // the box of the documentElement, in the other cases not.
407
  if (!isIE10 && isHTML) {
408
    const marginTop = parseFloat(styles.marginTop);
409
    const marginLeft = parseFloat(styles.marginLeft);
410

411
    offsets.top -= borderTopWidth - marginTop;
412
    offsets.bottom -= borderTopWidth - marginTop;
413
    offsets.left -= borderLeftWidth - marginLeft;
414
    offsets.right -= borderLeftWidth - marginLeft;
415

416
    // Attach marginTop and marginLeft because in some circumstances we may need them
417
    offsets.marginTop = marginTop;
418
    offsets.marginLeft = marginLeft;
419
  }
420

421
  if (isIE10 && !fixedPosition ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') {
422
    offsets = includeScroll(offsets, parent);
423
  }
424

425
  return offsets;
426
}
427

428
function getViewportOffsetRectRelativeToArtbitraryNode(element, excludeScroll = false) {
429
  const html = element.ownerDocument.documentElement;
430
  const relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html);
431
  const width = Math.max(html.clientWidth, window.innerWidth || 0);
432
  const height = Math.max(html.clientHeight, window.innerHeight || 0);
433

434
  const scrollTop = !excludeScroll ? getScroll(html) : 0;
435
  const scrollLeft = !excludeScroll ? getScroll(html, 'left') : 0;
436

437
  const offset = {
438
    top: scrollTop - relativeOffset.top + relativeOffset.marginTop,
439
    left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft,
440
    width,
441
    height
442
  };
443

444
  return getClientRect(offset);
445
}
446

447
/**
448
 * Check if the given element is fixed or is inside a fixed parent
449
 * @method
450
 * @memberof Popper.Utils
451
 * @argument {Element} element
452
 * @argument {Element} customContainer
453
 * @returns {Boolean} answer to "isFixed?"
454
 */
455
function isFixed(element) {
456
  const nodeName = element.nodeName;
457
  if (nodeName === 'BODY' || nodeName === 'HTML') {
458
    return false;
459
  }
460
  if (getStyleComputedProperty(element, 'position') === 'fixed') {
461
    return true;
462
  }
463
  const parentNode = getParentNode(element);
464
  if (!parentNode) {
465
    return false;
466
  }
467
  return isFixed(parentNode);
468
}
469

470
/**
471
 * Finds the first parent of an element that has a transformed property defined
472
 * @method
473
 * @memberof Popper.Utils
474
 * @argument {Element} element
475
 * @returns {Element} first transformed parent or documentElement
476
 */
477

478
function getFixedPositionOffsetParent(element) {
479
  // This check is needed to avoid errors in case one of the elements isn't defined for any reason
480
  if (!element || !element.parentElement || isIE()) {
481
    return document.documentElement;
482
  }
483
  let el = element.parentElement;
484
  while (el && getStyleComputedProperty(el, 'transform') === 'none') {
485
    el = el.parentElement;
486
  }
487
  return el || document.documentElement;
488
}
489

490
/**
491
 * Computed the boundaries limits and return them
492
 * @method
493
 * @memberof Popper.Utils
494
 * @param {HTMLElement} popper
495
 * @param {HTMLElement} reference
496
 * @param {number} padding
497
 * @param {HTMLElement} boundariesElement - Element used to define the boundaries
498
 * @param {Boolean} fixedPosition - Is in fixed position mode
499
 * @returns {Object} Coordinates of the boundaries
500
 */
501
function getBoundaries(popper, reference, padding, boundariesElement, fixedPosition = false) {
502
  // NOTE: 1 DOM access here
503

504
  let boundaries = { top: 0, left: 0 };
505
  const offsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, getReferenceNode(reference));
506

507
  // Handle viewport case
508
  if (boundariesElement === 'viewport') {
509
    boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent, fixedPosition);
510
  } else {
511
    // Handle other cases based on DOM element used as boundaries
512
    let boundariesNode;
513
    if (boundariesElement === 'scrollParent') {
514
      boundariesNode = getScrollParent(getParentNode(reference));
515
      if (boundariesNode.nodeName === 'BODY') {
516
        boundariesNode = popper.ownerDocument.documentElement;
517
      }
518
    } else if (boundariesElement === 'window') {
519
      boundariesNode = popper.ownerDocument.documentElement;
520
    } else {
521
      boundariesNode = boundariesElement;
522
    }
523

524
    const offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent, fixedPosition);
525

526
    // In case of HTML, we need a different computation
527
    if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) {
528
      const { height, width } = getWindowSizes(popper.ownerDocument);
529
      boundaries.top += offsets.top - offsets.marginTop;
530
      boundaries.bottom = height + offsets.top;
531
      boundaries.left += offsets.left - offsets.marginLeft;
532
      boundaries.right = width + offsets.left;
533
    } else {
534
      // for all the other DOM elements, this one is good
535
      boundaries = offsets;
536
    }
537
  }
538

539
  // Add paddings
540
  padding = padding || 0;
541
  const isPaddingNumber = typeof padding === 'number';
542
  boundaries.left += isPaddingNumber ? padding : padding.left || 0;
543
  boundaries.top += isPaddingNumber ? padding : padding.top || 0;
544
  boundaries.right -= isPaddingNumber ? padding : padding.right || 0;
545
  boundaries.bottom -= isPaddingNumber ? padding : padding.bottom || 0;
546

547
  return boundaries;
548
}
549

550
function getArea({ width, height }) {
551
  return width * height;
552
}
553

554
/**
555
 * Utility used to transform the `auto` placement to the placement with more
556
 * available space.
557
 * @method
558
 * @memberof Popper.Utils
559
 * @argument {Object} data - The data object generated by update method
560
 * @argument {Object} options - Modifiers configuration and options
561
 * @returns {Object} The data object, properly modified
562
 */
563
function computeAutoPlacement(placement, refRect, popper, reference, boundariesElement, padding = 0) {
564
  if (placement.indexOf('auto') === -1) {
565
    return placement;
566
  }
567

568
  const boundaries = getBoundaries(popper, reference, padding, boundariesElement);
569

570
  const rects = {
571
    top: {
572
      width: boundaries.width,
573
      height: refRect.top - boundaries.top
574
    },
575
    right: {
576
      width: boundaries.right - refRect.right,
577
      height: boundaries.height
578
    },
579
    bottom: {
580
      width: boundaries.width,
581
      height: boundaries.bottom - refRect.bottom
582
    },
583
    left: {
584
      width: refRect.left - boundaries.left,
585
      height: boundaries.height
586
    }
587
  };
588

589
  const sortedAreas = Object.keys(rects).map(key => _extends({
590
    key
591
  }, rects[key], {
592
    area: getArea(rects[key])
593
  })).sort((a, b) => b.area - a.area);
594

595
  const filteredAreas = sortedAreas.filter(({ width, height }) => width >= popper.clientWidth && height >= popper.clientHeight);
596

597
  const computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key;
598

599
  const variation = placement.split('-')[1];
600

601
  return computedPlacement + (variation ? `-${variation}` : '');
602
}
603

604
const timeoutDuration = function () {
605
  const longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox'];
606
  for (let i = 0; i < longerTimeoutBrowsers.length; i += 1) {
607
    if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) {
608
      return 1;
609
    }
610
  }
611
  return 0;
612
}();
613

614
function microtaskDebounce(fn) {
615
  let called = false;
616
  return () => {
617
    if (called) {
618
      return;
619
    }
620
    called = true;
621
    window.Promise.resolve().then(() => {
622
      called = false;
623
      fn();
624
    });
625
  };
626
}
627

628
function taskDebounce(fn) {
629
  let scheduled = false;
630
  return () => {
631
    if (!scheduled) {
632
      scheduled = true;
633
      setTimeout(() => {
634
        scheduled = false;
635
        fn();
636
      }, timeoutDuration);
637
    }
638
  };
639
}
640

641
const supportsMicroTasks = isBrowser && window.Promise;
642

643
/**
644
* Create a debounced version of a method, that's asynchronously deferred
645
* but called in the minimum time possible.
646
*
647
* @method
648
* @memberof Popper.Utils
649
* @argument {Function} fn
650
* @returns {Function}
651
*/
652
var debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce;
653

654
/**
655
 * Mimics the `find` method of Array
656
 * @method
657
 * @memberof Popper.Utils
658
 * @argument {Array} arr
659
 * @argument prop
660
 * @argument value
661
 * @returns index or -1
662
 */
663
function find(arr, check) {
664
  // use native find if supported
665
  if (Array.prototype.find) {
666
    return arr.find(check);
667
  }
668

669
  // use `filter` to obtain the same behavior of `find`
670
  return arr.filter(check)[0];
671
}
672

673
/**
674
 * Return the index of the matching object
675
 * @method
676
 * @memberof Popper.Utils
677
 * @argument {Array} arr
678
 * @argument prop
679
 * @argument value
680
 * @returns index or -1
681
 */
682
function findIndex(arr, prop, value) {
683
  // use native findIndex if supported
684
  if (Array.prototype.findIndex) {
685
    return arr.findIndex(cur => cur[prop] === value);
686
  }
687

688
  // use `find` + `indexOf` if `findIndex` isn't supported
689
  const match = find(arr, obj => obj[prop] === value);
690
  return arr.indexOf(match);
691
}
692

693
/**
694
 * Get the position of the given element, relative to its offset parent
695
 * @method
696
 * @memberof Popper.Utils
697
 * @param {Element} element
698
 * @return {Object} position - Coordinates of the element and its `scrollTop`
699
 */
700
function getOffsetRect(element) {
701
  let elementRect;
702
  if (element.nodeName === 'HTML') {
703
    const { width, height } = getWindowSizes(element.ownerDocument);
704
    elementRect = {
705
      width,
706
      height,
707
      left: 0,
708
      top: 0
709
    };
710
  } else {
711
    elementRect = {
712
      width: element.offsetWidth,
713
      height: element.offsetHeight,
714
      left: element.offsetLeft,
715
      top: element.offsetTop
716
    };
717
  }
718

719
  // position
720
  return getClientRect(elementRect);
721
}
722

723
/**
724
 * Get the outer sizes of the given element (offset size + margins)
725
 * @method
726
 * @memberof Popper.Utils
727
 * @argument {Element} element
728
 * @returns {Object} object containing width and height properties
729
 */
730
function getOuterSizes(element) {
731
  const window = element.ownerDocument.defaultView;
732
  const styles = window.getComputedStyle(element);
733
  const x = parseFloat(styles.marginTop || 0) + parseFloat(styles.marginBottom || 0);
734
  const y = parseFloat(styles.marginLeft || 0) + parseFloat(styles.marginRight || 0);
735
  const result = {
736
    width: element.offsetWidth + y,
737
    height: element.offsetHeight + x
738
  };
739
  return result;
740
}
741

742
/**
743
 * Get the opposite placement of the given one
744
 * @method
745
 * @memberof Popper.Utils
746
 * @argument {String} placement
747
 * @returns {String} flipped placement
748
 */
749
function getOppositePlacement(placement) {
750
  const hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' };
751
  return placement.replace(/left|right|bottom|top/g, matched => hash[matched]);
752
}
753

754
/**
755
 * Get offsets to the popper
756
 * @method
757
 * @memberof Popper.Utils
758
 * @param {Object} position - CSS position the Popper will get applied
759
 * @param {HTMLElement} popper - the popper element
760
 * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this)
761
 * @param {String} placement - one of the valid placement options
762
 * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper
763
 */
764
function getPopperOffsets(popper, referenceOffsets, placement) {
765
  placement = placement.split('-')[0];
766

767
  // Get popper node sizes
768
  const popperRect = getOuterSizes(popper);
769

770
  // Add position, width and height to our offsets object
771
  const popperOffsets = {
772
    width: popperRect.width,
773
    height: popperRect.height
774
  };
775

776
  // depending by the popper placement we have to compute its offsets slightly differently
777
  const isHoriz = ['right', 'left'].indexOf(placement) !== -1;
778
  const mainSide = isHoriz ? 'top' : 'left';
779
  const secondarySide = isHoriz ? 'left' : 'top';
780
  const measurement = isHoriz ? 'height' : 'width';
781
  const secondaryMeasurement = !isHoriz ? 'height' : 'width';
782

783
  popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2;
784
  if (placement === secondarySide) {
785
    popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement];
786
  } else {
787
    popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)];
788
  }
789

790
  return popperOffsets;
791
}
792

793
/**
794
 * Get offsets to the reference element
795
 * @method
796
 * @memberof Popper.Utils
797
 * @param {Object} state
798
 * @param {Element} popper - the popper element
799
 * @param {Element} reference - the reference element (the popper will be relative to this)
800
 * @param {Element} fixedPosition - is in fixed position mode
801
 * @returns {Object} An object containing the offsets which will be applied to the popper
802
 */
803
function getReferenceOffsets(state, popper, reference, fixedPosition = null) {
804
  const commonOffsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, getReferenceNode(reference));
805
  return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent, fixedPosition);
806
}
807

808
/**
809
 * Get the prefixed supported property name
810
 * @method
811
 * @memberof Popper.Utils
812
 * @argument {String} property (camelCase)
813
 * @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix)
814
 */
815
function getSupportedPropertyName(property) {
816
  const prefixes = [false, 'ms', 'Webkit', 'Moz', 'O'];
817
  const upperProp = property.charAt(0).toUpperCase() + property.slice(1);
818

819
  for (let i = 0; i < prefixes.length; i++) {
820
    const prefix = prefixes[i];
821
    const toCheck = prefix ? `${prefix}${upperProp}` : property;
822
    if (typeof document.body.style[toCheck] !== 'undefined') {
823
      return toCheck;
824
    }
825
  }
826
  return null;
827
}
828

829
/**
830
 * Check if the given variable is a function
831
 * @method
832
 * @memberof Popper.Utils
833
 * @argument {Any} functionToCheck - variable to check
834
 * @returns {Boolean} answer to: is a function?
835
 */
836
function isFunction(functionToCheck) {
837
  const getType = {};
838
  return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
839
}
840

841
/**
842
 * Helper used to know if the given modifier is enabled.
843
 * @method
844
 * @memberof Popper.Utils
845
 * @returns {Boolean}
846
 */
847
function isModifierEnabled(modifiers, modifierName) {
848
  return modifiers.some(({ name, enabled }) => enabled && name === modifierName);
849
}
850

851
/**
852
 * Helper used to know if the given modifier depends from another one.<br />
853
 * It checks if the needed modifier is listed and enabled.
854
 * @method
855
 * @memberof Popper.Utils
856
 * @param {Array} modifiers - list of modifiers
857
 * @param {String} requestingName - name of requesting modifier
858
 * @param {String} requestedName - name of requested modifier
859
 * @returns {Boolean}
860
 */
861
function isModifierRequired(modifiers, requestingName, requestedName) {
862
  const requesting = find(modifiers, ({ name }) => name === requestingName);
863

864
  const isRequired = !!requesting && modifiers.some(modifier => {
865
    return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order;
866
  });
867

868
  if (!isRequired) {
869
    const requesting = `\`${requestingName}\``;
870
    const requested = `\`${requestedName}\``;
871
    console.warn(`${requested} modifier is required by ${requesting} modifier in order to work, be sure to include it before ${requesting}!`);
872
  }
873
  return isRequired;
874
}
875

876
/**
877
 * Tells if a given input is a number
878
 * @method
879
 * @memberof Popper.Utils
880
 * @param {*} input to check
881
 * @return {Boolean}
882
 */
883
function isNumeric(n) {
884
  return n !== '' && !isNaN(parseFloat(n)) && isFinite(n);
885
}
886

887
/**
888
 * Get the window associated with the element
889
 * @argument {Element} element
890
 * @returns {Window}
891
 */
892
function getWindow(element) {
893
  const ownerDocument = element.ownerDocument;
894
  return ownerDocument ? ownerDocument.defaultView : window;
895
}
896

897
/**
898
 * Remove event listeners used to update the popper position
899
 * @method
900
 * @memberof Popper.Utils
901
 * @private
902
 */
903
function removeEventListeners(reference, state) {
904
  // Remove resize event listener on window
905
  getWindow(reference).removeEventListener('resize', state.updateBound);
906

907
  // Remove scroll event listener on scroll parents
908
  state.scrollParents.forEach(target => {
909
    target.removeEventListener('scroll', state.updateBound);
910
  });
911

912
  // Reset state
913
  state.updateBound = null;
914
  state.scrollParents = [];
915
  state.scrollElement = null;
916
  state.eventsEnabled = false;
917
  return state;
918
}
919

920
/**
921
 * Loop trough the list of modifiers and run them in order,
922
 * each of them will then edit the data object.
923
 * @method
924
 * @memberof Popper.Utils
925
 * @param {dataObject} data
926
 * @param {Array} modifiers
927
 * @param {String} ends - Optional modifier name used as stopper
928
 * @returns {dataObject}
929
 */
930
function runModifiers(modifiers, data, ends) {
931
  const modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends));
932

933
  modifiersToRun.forEach(modifier => {
934
    if (modifier['function']) {
935
      // eslint-disable-line dot-notation
936
      console.warn('`modifier.function` is deprecated, use `modifier.fn`!');
937
    }
938
    const fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation
939
    if (modifier.enabled && isFunction(fn)) {
940
      // Add properties to offsets to make them a complete clientRect object
941
      // we do this before each modifier to make sure the previous one doesn't
942
      // mess with these values
943
      data.offsets.popper = getClientRect(data.offsets.popper);
944
      data.offsets.reference = getClientRect(data.offsets.reference);
945

946
      data = fn(data, modifier);
947
    }
948
  });
949

950
  return data;
951
}
952

953
/**
954
 * Set the attributes to the given popper
955
 * @method
956
 * @memberof Popper.Utils
957
 * @argument {Element} element - Element to apply the attributes to
958
 * @argument {Object} styles
959
 * Object with a list of properties and values which will be applied to the element
960
 */
961
function setAttributes(element, attributes) {
962
  Object.keys(attributes).forEach(function (prop) {
963
    const value = attributes[prop];
964
    if (value !== false) {
965
      element.setAttribute(prop, attributes[prop]);
966
    } else {
967
      element.removeAttribute(prop);
968
    }
969
  });
970
}
971

972
/**
973
 * Set the style to the given popper
974
 * @method
975
 * @memberof Popper.Utils
976
 * @argument {Element} element - Element to apply the style to
977
 * @argument {Object} styles
978
 * Object with a list of properties and values which will be applied to the element
979
 */
980
function setStyles(element, styles) {
981
  Object.keys(styles).forEach(prop => {
982
    let unit = '';
983
    // add unit if the value is numeric and is one of the following
984
    if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) {
985
      unit = 'px';
986
    }
987
    element.style[prop] = styles[prop] + unit;
988
  });
989
}
990

991
function attachToScrollParents(scrollParent, event, callback, scrollParents) {
992
  const isBody = scrollParent.nodeName === 'BODY';
993
  const target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent;
994
  target.addEventListener(event, callback, { passive: true });
995

996
  if (!isBody) {
997
    attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents);
998
  }
999
  scrollParents.push(target);
1000
}
1001

1002
/**
1003
 * Setup needed event listeners used to update the popper position
1004
 * @method
1005
 * @memberof Popper.Utils
1006
 * @private
1007
 */
1008
function setupEventListeners(reference, options, state, updateBound) {
1009
  // Resize event listener on window
1010
  state.updateBound = updateBound;
1011
  getWindow(reference).addEventListener('resize', state.updateBound, { passive: true });
1012

1013
  // Scroll event listener on scroll parents
1014
  const scrollElement = getScrollParent(reference);
1015
  attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents);
1016
  state.scrollElement = scrollElement;
1017
  state.eventsEnabled = true;
1018

1019
  return state;
1020
}
1021

1022
// This is here just for backward compatibility with versions lower than v1.10.3
1023
// you should import the utilities using named exports, if you want them all use:
1024
// ```
1025
// import * as PopperUtils from 'popper-utils';
1026
// ```
1027
// The default export will be removed in the next major version.
1028
var index = {
1029
  computeAutoPlacement,
1030
  debounce,
1031
  findIndex,
1032
  getBordersSize,
1033
  getBoundaries,
1034
  getBoundingClientRect,
1035
  getClientRect,
1036
  getOffsetParent,
1037
  getOffsetRect,
1038
  getOffsetRectRelativeToArbitraryNode,
1039
  getOuterSizes,
1040
  getParentNode,
1041
  getPopperOffsets,
1042
  getReferenceOffsets,
1043
  getScroll,
1044
  getScrollParent,
1045
  getStyleComputedProperty,
1046
  getSupportedPropertyName,
1047
  getWindowSizes,
1048
  isFixed,
1049
  isFunction,
1050
  isModifierEnabled,
1051
  isModifierRequired,
1052
  isNumeric,
1053
  removeEventListeners,
1054
  runModifiers,
1055
  setAttributes,
1056
  setStyles,
1057
  setupEventListeners
1058
};
1059

1060
export { computeAutoPlacement, debounce, findIndex, getBordersSize, getBoundaries, getBoundingClientRect, getClientRect, getOffsetParent, getOffsetRect, getOffsetRectRelativeToArbitraryNode, getOuterSizes, getParentNode, getPopperOffsets, getReferenceOffsets, getScroll, getScrollParent, getStyleComputedProperty, getSupportedPropertyName, getWindowSizes, isFixed, isFunction, isModifierEnabled, isModifierRequired, isNumeric, removeEventListeners, runModifiers, setAttributes, setStyles, setupEventListeners };
1061
export default index;
1062
//# sourceMappingURL=popper-utils.js.map
1063

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.