UNPKG

21.9 kBJavaScriptView Raw
1/**!
2* tippy.js v5.2.1
3* (c) 2017-2020 atomiks
4* MIT License
5*/
6import { e as errorWhen, _ as _extends, d as defaultProps, t as tippy, a as div, r as removeProperties, n as normalizeToArray, i as includes, s as setVisibilityState, w as warnWhen, B as BACKDROP_CLASS, g as getOwnerDocument, b as isMouseEvent, u as useIfDefined, c as currentInput, f as closestCallback, h as getBasePlacement, j as arrayFrom } from './tippy.chunk.esm.js';
7export { l as createTippyWithPlugins, t as default, k as hideAll, R as roundArrow } from './tippy.chunk.esm.js';
8import 'popper.js';
9
10/**
11 * Re-uses a single tippy element for many different tippy instances.
12 * Replaces v4's `tippy.group()`.
13 */
14
15var createSingleton = function createSingleton(tippyInstances, optionalProps,
16/** @deprecated use Props.plugins */
17plugins) {
18 if (optionalProps === void 0) {
19 optionalProps = {};
20 }
21
22 if (plugins === void 0) {
23 plugins = [];
24 }
25
26 if (process.env.NODE_ENV !== "production") {
27 errorWhen(!Array.isArray(tippyInstances), ['The first argument passed to createSingleton() must be an array of tippy', 'instances. The passed value was', String(tippyInstances)].join(' '));
28 }
29
30 plugins = optionalProps.plugins || plugins;
31 tippyInstances.forEach(function (instance) {
32 instance.disable();
33 });
34
35 var userAria = _extends({}, defaultProps, {}, optionalProps).aria;
36
37 var currentAria;
38 var currentTarget;
39 var shouldSkipUpdate = false;
40 var references = tippyInstances.map(function (instance) {
41 return instance.reference;
42 });
43 var singleton = {
44 fn: function fn(instance) {
45 function handleAriaDescribedByAttribute(isShow) {
46 if (!currentAria) {
47 return;
48 }
49
50 var attr = "aria-" + currentAria;
51
52 if (isShow && !instance.props.interactive) {
53 currentTarget.setAttribute(attr, instance.popperChildren.tooltip.id);
54 } else {
55 currentTarget.removeAttribute(attr);
56 }
57 }
58
59 return {
60 onAfterUpdate: function onAfterUpdate(_, _ref) {
61 var aria = _ref.aria;
62
63 // Ensure `aria` for the singleton instance stays `null`, while
64 // changing the `userAria` value
65 if (aria !== undefined && aria !== userAria) {
66 if (!shouldSkipUpdate) {
67 userAria = aria;
68 } else {
69 shouldSkipUpdate = true;
70 instance.setProps({
71 aria: null
72 });
73 shouldSkipUpdate = false;
74 }
75 }
76 },
77 onDestroy: function onDestroy() {
78 tippyInstances.forEach(function (instance) {
79 instance.enable();
80 });
81 },
82 onMount: function onMount() {
83 handleAriaDescribedByAttribute(true);
84 },
85 onUntrigger: function onUntrigger() {
86 handleAriaDescribedByAttribute(false);
87 },
88 onTrigger: function onTrigger(_, event) {
89 var target = event.currentTarget;
90 var index = references.indexOf(target); // bail-out
91
92 if (target === currentTarget) {
93 return;
94 }
95
96 currentTarget = target;
97 currentAria = userAria;
98
99 if (instance.state.isVisible) {
100 handleAriaDescribedByAttribute(true);
101 }
102
103 instance.popperInstance.reference = target;
104 instance.setContent(tippyInstances[index].props.content);
105 }
106 };
107 }
108 };
109 return tippy(div(), _extends({}, optionalProps, {
110 plugins: [singleton].concat(plugins),
111 aria: null,
112 triggerTarget: references
113 }));
114};
115
116var BUBBLING_EVENTS_MAP = {
117 mouseover: 'mouseenter',
118 focusin: 'focus',
119 click: 'click'
120};
121/**
122 * Creates a delegate instance that controls the creation of tippy instances
123 * for child elements (`target` CSS selector).
124 */
125
126function delegate(targets, props,
127/** @deprecated use Props.plugins */
128plugins) {
129 if (plugins === void 0) {
130 plugins = [];
131 }
132
133 if (process.env.NODE_ENV !== "production") {
134 errorWhen(!(props && props.target), ['You must specity a `target` prop indicating a CSS selector string matching', 'the target elements that should receive a tippy.'].join(' '));
135 }
136
137 plugins = props.plugins || plugins;
138 var listeners = [];
139 var childTippyInstances = [];
140 var target = props.target;
141 var nativeProps = removeProperties(props, ['target']);
142
143 var parentProps = _extends({}, nativeProps, {
144 plugins: plugins,
145 trigger: 'manual'
146 });
147
148 var childProps = _extends({}, nativeProps, {
149 plugins: plugins,
150 showOnCreate: true
151 });
152
153 var returnValue = tippy(targets, parentProps);
154 var normalizedReturnValue = normalizeToArray(returnValue);
155
156 function onTrigger(event) {
157 if (!event.target) {
158 return;
159 }
160
161 var targetNode = event.target.closest(target);
162
163 if (!targetNode) {
164 return;
165 } // Get relevant trigger with fallbacks:
166 // 1. Check `data-tippy-trigger` attribute on target node
167 // 2. Fallback to `trigger` passed to `delegate()`
168 // 3. Fallback to `defaultProps.trigger`
169
170
171 var trigger = targetNode.getAttribute('data-tippy-trigger') || props.trigger || defaultProps.trigger; // Only create the instance if the bubbling event matches the trigger type
172
173 if (!includes(trigger, BUBBLING_EVENTS_MAP[event.type])) {
174 return;
175 }
176
177 var instance = tippy(targetNode, childProps);
178
179 if (instance) {
180 childTippyInstances = childTippyInstances.concat(instance);
181 }
182 }
183
184 function on(node, eventType, handler, options) {
185 if (options === void 0) {
186 options = false;
187 }
188
189 node.addEventListener(eventType, handler, options);
190 listeners.push({
191 node: node,
192 eventType: eventType,
193 handler: handler,
194 options: options
195 });
196 }
197
198 function addEventListeners(instance) {
199 var reference = instance.reference;
200 on(reference, 'mouseover', onTrigger);
201 on(reference, 'focusin', onTrigger);
202 on(reference, 'click', onTrigger);
203 }
204
205 function removeEventListeners() {
206 listeners.forEach(function (_ref) {
207 var node = _ref.node,
208 eventType = _ref.eventType,
209 handler = _ref.handler,
210 options = _ref.options;
211 node.removeEventListener(eventType, handler, options);
212 });
213 listeners = [];
214 }
215
216 function applyMutations(instance) {
217 var originalDestroy = instance.destroy;
218
219 instance.destroy = function (shouldDestroyChildInstances) {
220 if (shouldDestroyChildInstances === void 0) {
221 shouldDestroyChildInstances = true;
222 }
223
224 if (shouldDestroyChildInstances) {
225 childTippyInstances.forEach(function (instance) {
226 instance.destroy();
227 });
228 }
229
230 childTippyInstances = [];
231 removeEventListeners();
232 originalDestroy();
233 };
234
235 addEventListeners(instance);
236 }
237
238 normalizedReturnValue.forEach(applyMutations);
239 return returnValue;
240}
241
242var animateFill = {
243 name: 'animateFill',
244 defaultValue: false,
245 fn: function fn(instance) {
246 var _instance$popperChild = instance.popperChildren,
247 tooltip = _instance$popperChild.tooltip,
248 content = _instance$popperChild.content;
249 var backdrop = instance.props.animateFill ? createBackdropElement() : null;
250
251 function addBackdropToPopperChildren() {
252 instance.popperChildren.backdrop = backdrop;
253 }
254
255 return {
256 onCreate: function onCreate() {
257 if (backdrop) {
258 addBackdropToPopperChildren();
259 tooltip.insertBefore(backdrop, tooltip.firstElementChild);
260 tooltip.setAttribute('data-animatefill', '');
261 tooltip.style.overflow = 'hidden';
262 instance.setProps({
263 animation: 'shift-away',
264 arrow: false
265 });
266 }
267 },
268 onMount: function onMount() {
269 if (backdrop) {
270 var transitionDuration = tooltip.style.transitionDuration;
271 var duration = Number(transitionDuration.replace('ms', '')); // The content should fade in after the backdrop has mostly filled the
272 // tooltip element. `clip-path` is the other alternative but is not
273 // well-supported and is buggy on some devices.
274
275 content.style.transitionDelay = Math.round(duration / 10) + "ms";
276 backdrop.style.transitionDuration = transitionDuration;
277 setVisibilityState([backdrop], 'visible'); // Warn if the stylesheets are not loaded
278
279 if (process.env.NODE_ENV !== "production") {
280 warnWhen(getComputedStyle(backdrop).position !== 'absolute', "The `tippy.js/dist/backdrop.css` stylesheet has not been\n imported!\n \n The `animateFill` plugin requires this stylesheet to work.");
281 warnWhen(getComputedStyle(tooltip).transform === 'none', "The `tippy.js/animations/shift-away.css` stylesheet has not\n been imported!\n \n The `animateFill` plugin requires this stylesheet to work.");
282 }
283 }
284 },
285 onShow: function onShow() {
286 if (backdrop) {
287 backdrop.style.transitionDuration = '0ms';
288 }
289 },
290 onHide: function onHide() {
291 if (backdrop) {
292 setVisibilityState([backdrop], 'hidden');
293 }
294 },
295 onAfterUpdate: function onAfterUpdate() {
296 // With this type of prop, it's highly unlikely it will be changed
297 // dynamically. We'll leave out the diff/update logic it to save bytes.
298 // `popperChildren` is assigned a new object onAfterUpdate
299 addBackdropToPopperChildren();
300 }
301 };
302 }
303};
304
305function createBackdropElement() {
306 var backdrop = div();
307 backdrop.className = BACKDROP_CLASS;
308 setVisibilityState([backdrop], 'hidden');
309 return backdrop;
310}
311
312var followCursor = {
313 name: 'followCursor',
314 defaultValue: false,
315 fn: function fn(instance) {
316 var reference = instance.reference,
317 popper = instance.popper;
318 var originalReference = null; // Support iframe contexts
319 // Static check that assumes any of the `triggerTarget` or `reference`
320 // nodes will never change documents, even when they are updated
321
322 var doc = getOwnerDocument(instance.props.triggerTarget || reference); // Internal state
323
324 var lastMouseMoveEvent;
325 var mouseCoords = null;
326 var isInternallySettingControlledProp = false; // These are controlled by this plugin, so we need to store the user's
327 // original prop value
328
329 var userProps = instance.props;
330
331 function setUserProps(props) {
332 var keys = Object.keys(props);
333 keys.forEach(function (prop) {
334 userProps[prop] = useIfDefined(props[prop], userProps[prop]);
335 });
336 }
337
338 function getIsManual() {
339 return instance.props.trigger.trim() === 'manual';
340 }
341
342 function getIsEnabled() {
343 // #597
344 var isValidMouseEvent = getIsManual() ? true : // Check if a keyboard "click"
345 mouseCoords !== null && !(mouseCoords.clientX === 0 && mouseCoords.clientY === 0);
346 return instance.props.followCursor && isValidMouseEvent;
347 }
348
349 function getIsInitialBehavior() {
350 return currentInput.isTouch || instance.props.followCursor === 'initial' && instance.state.isVisible;
351 }
352
353 function resetReference() {
354 if (instance.popperInstance && originalReference) {
355 instance.popperInstance.reference = originalReference;
356 }
357 }
358
359 function handlePlacement() {
360 // Due to `getVirtualOffsets()`, we need to reverse the placement if it's
361 // shifted (start -> end, and vice-versa)
362 // Early bail-out
363 if (!getIsEnabled() && instance.props.placement === userProps.placement) {
364 return;
365 }
366
367 var placement = userProps.placement;
368 var shift = placement.split('-')[1];
369 isInternallySettingControlledProp = true;
370 instance.setProps({
371 placement: getIsEnabled() && shift ? placement.replace(shift, shift === 'start' ? 'end' : 'start') : placement
372 });
373 isInternallySettingControlledProp = false;
374 }
375
376 function handlePopperListeners() {
377 if (!instance.popperInstance) {
378 return;
379 } // Popper's scroll listeners make sense for `true` only. TODO: work out
380 // how to only listen horizontal scroll for "horizontal" and vertical
381 // scroll for "vertical"
382
383
384 if (getIsEnabled() && getIsInitialBehavior()) {
385 instance.popperInstance.disableEventListeners();
386 }
387 }
388
389 function handleMouseMoveListener() {
390 if (getIsEnabled()) {
391 addListener();
392 } else {
393 resetReference();
394 }
395 }
396
397 function triggerLastMouseMove() {
398 if (getIsEnabled()) {
399 onMouseMove(lastMouseMoveEvent);
400 }
401 }
402
403 function addListener() {
404 doc.addEventListener('mousemove', onMouseMove);
405 }
406
407 function removeListener() {
408 doc.removeEventListener('mousemove', onMouseMove);
409 }
410
411 function onMouseMove(event) {
412 var _lastMouseMoveEvent = lastMouseMoveEvent = event,
413 clientX = _lastMouseMoveEvent.clientX,
414 clientY = _lastMouseMoveEvent.clientY;
415
416 if (!instance.popperInstance || !instance.state.currentPlacement) {
417 return;
418 } // If the instance is interactive, avoid updating the position unless it's
419 // over the reference element
420
421
422 var isCursorOverReference = closestCallback(event.target, function (el) {
423 return el === reference;
424 });
425 var followCursor = instance.props.followCursor;
426 var isHorizontal = followCursor === 'horizontal';
427 var isVertical = followCursor === 'vertical';
428 var isVerticalPlacement = includes(['top', 'bottom'], getBasePlacement(instance.state.currentPlacement)); // The virtual reference needs some size to prevent itself from overflowing
429
430 var _getVirtualOffsets = getVirtualOffsets(popper, isVerticalPlacement),
431 size = _getVirtualOffsets.size,
432 x = _getVirtualOffsets.x,
433 y = _getVirtualOffsets.y;
434
435 if (isCursorOverReference || !instance.props.interactive) {
436 // Preserve custom position ReferenceObjects, which may not be the
437 // original targets reference passed as an argument
438 if (originalReference === null) {
439 originalReference = instance.popperInstance.reference;
440 }
441
442 instance.popperInstance.reference = {
443 referenceNode: reference,
444 // These `client` values don't get used by Popper.js if they are 0
445 clientWidth: 0,
446 clientHeight: 0,
447 getBoundingClientRect: function getBoundingClientRect() {
448 var rect = reference.getBoundingClientRect();
449 return {
450 width: isVerticalPlacement ? size : 0,
451 height: isVerticalPlacement ? 0 : size,
452 top: (isHorizontal ? rect.top : clientY) - y,
453 bottom: (isHorizontal ? rect.bottom : clientY) + y,
454 left: (isVertical ? rect.left : clientX) - x,
455 right: (isVertical ? rect.right : clientX) + x
456 };
457 }
458 };
459 instance.popperInstance.update();
460 }
461
462 if (getIsInitialBehavior()) {
463 removeListener();
464 }
465 }
466
467 return {
468 onAfterUpdate: function onAfterUpdate(_, partialProps) {
469 if (!isInternallySettingControlledProp) {
470 setUserProps(partialProps);
471
472 if (partialProps.placement) {
473 handlePlacement();
474 }
475 } // A new placement causes the popperInstance to be recreated
476
477
478 if (partialProps.placement) {
479 handlePopperListeners();
480 } // Wait for `.update()` to set `instance.state.currentPlacement` to
481 // the new placement
482
483
484 requestAnimationFrame(triggerLastMouseMove);
485 },
486 onMount: function onMount() {
487 triggerLastMouseMove();
488 handlePopperListeners();
489 },
490 onShow: function onShow() {
491 if (getIsManual()) {
492 // Since there's no trigger event to use, we have to use these as
493 // baseline coords
494 mouseCoords = {
495 clientX: 0,
496 clientY: 0
497 }; // Ensure `lastMouseMoveEvent` doesn't access any other properties
498 // of a MouseEvent here
499
500 lastMouseMoveEvent = mouseCoords;
501 handlePlacement();
502 handleMouseMoveListener();
503 }
504 },
505 onTrigger: function onTrigger(_, event) {
506 // Tapping on touch devices can trigger `mouseenter` then `focus`
507 if (mouseCoords) {
508 return;
509 }
510
511 if (isMouseEvent(event)) {
512 mouseCoords = {
513 clientX: event.clientX,
514 clientY: event.clientY
515 };
516 lastMouseMoveEvent = event;
517 }
518
519 handlePlacement();
520 handleMouseMoveListener();
521 },
522 onUntrigger: function onUntrigger() {
523 // If untriggered before showing (`onHidden` will never be invoked)
524 if (!instance.state.isVisible) {
525 removeListener();
526 mouseCoords = null;
527 }
528 },
529 onHidden: function onHidden() {
530 removeListener();
531 resetReference();
532 mouseCoords = null;
533 }
534 };
535 }
536};
537function getVirtualOffsets(popper, isVerticalPlacement) {
538 var size = isVerticalPlacement ? popper.offsetWidth : popper.offsetHeight;
539 return {
540 size: size,
541 x: isVerticalPlacement ? size : 0,
542 y: isVerticalPlacement ? 0 : size
543 };
544}
545
546// position. This will require the `followCursor` plugin's fixes for overflow
547// due to using event.clientX/Y values. (normalizedPlacement, getVirtualOffsets)
548
549var inlinePositioning = {
550 name: 'inlinePositioning',
551 defaultValue: false,
552 fn: function fn(instance) {
553 var reference = instance.reference;
554
555 function getIsEnabled() {
556 return !!instance.props.inlinePositioning;
557 }
558
559 return {
560 onHidden: function onHidden() {
561 if (getIsEnabled()) {
562 instance.popperInstance.reference = reference;
563 }
564 },
565 onShow: function onShow() {
566 if (!getIsEnabled()) {
567 return;
568 }
569
570 instance.popperInstance.reference = {
571 referenceNode: reference,
572 // These `client` values don't get used by Popper.js if they are 0
573 clientWidth: 0,
574 clientHeight: 0,
575 getBoundingClientRect: function getBoundingClientRect() {
576 return getInlineBoundingClientRect(instance.state.currentPlacement && getBasePlacement(instance.state.currentPlacement), reference.getBoundingClientRect(), arrayFrom(reference.getClientRects()));
577 }
578 };
579 }
580 };
581 }
582};
583function getInlineBoundingClientRect(currentBasePlacement, boundingRect, clientRects) {
584 // Not an inline element, or placement is not yet known
585 if (clientRects.length < 2 || currentBasePlacement === null) {
586 return boundingRect;
587 }
588
589 switch (currentBasePlacement) {
590 case 'top':
591 case 'bottom':
592 {
593 var firstRect = clientRects[0];
594 var lastRect = clientRects[clientRects.length - 1];
595 var isTop = currentBasePlacement === 'top';
596 var top = firstRect.top;
597 var bottom = lastRect.bottom;
598 var left = isTop ? firstRect.left : lastRect.left;
599 var right = isTop ? firstRect.right : lastRect.right;
600 var width = right - left;
601 var height = bottom - top;
602 return {
603 top: top,
604 bottom: bottom,
605 left: left,
606 right: right,
607 width: width,
608 height: height
609 };
610 }
611
612 case 'left':
613 case 'right':
614 {
615 var minLeft = Math.min.apply(Math, clientRects.map(function (rects) {
616 return rects.left;
617 }));
618 var maxRight = Math.max.apply(Math, clientRects.map(function (rects) {
619 return rects.right;
620 }));
621 var measureRects = clientRects.filter(function (rect) {
622 return currentBasePlacement === 'left' ? rect.left === minLeft : rect.right === maxRight;
623 });
624 var _top = measureRects[0].top;
625 var _bottom = measureRects[measureRects.length - 1].bottom;
626 var _left = minLeft;
627 var _right = maxRight;
628
629 var _width = _right - _left;
630
631 var _height = _bottom - _top;
632
633 return {
634 top: _top,
635 bottom: _bottom,
636 left: _left,
637 right: _right,
638 width: _width,
639 height: _height
640 };
641 }
642
643 default:
644 {
645 return boundingRect;
646 }
647 }
648}
649
650var sticky = {
651 name: 'sticky',
652 defaultValue: false,
653 fn: function fn(instance) {
654 var reference = instance.reference,
655 popper = instance.popper;
656
657 function getReference() {
658 return instance.popperInstance ? instance.popperInstance.reference : reference;
659 }
660
661 function shouldCheck(value) {
662 return instance.props.sticky === true || instance.props.sticky === value;
663 }
664
665 var prevRefRect = null;
666 var prevPopRect = null;
667
668 function updatePosition() {
669 var currentRefRect = shouldCheck('reference') ? getReference().getBoundingClientRect() : null;
670 var currentPopRect = shouldCheck('popper') ? popper.getBoundingClientRect() : null;
671
672 if (currentRefRect && areRectsDifferent(prevRefRect, currentRefRect) || currentPopRect && areRectsDifferent(prevPopRect, currentPopRect)) {
673 instance.popperInstance.update();
674 }
675
676 prevRefRect = currentRefRect;
677 prevPopRect = currentPopRect;
678
679 if (instance.state.isMounted) {
680 requestAnimationFrame(updatePosition);
681 }
682 }
683
684 return {
685 onMount: function onMount() {
686 if (instance.props.sticky) {
687 updatePosition();
688 }
689 }
690 };
691 }
692};
693
694function areRectsDifferent(rectA, rectB) {
695 if (rectA && rectB) {
696 return rectA.top !== rectB.top || rectA.right !== rectB.right || rectA.bottom !== rectB.bottom || rectA.left !== rectB.left;
697 }
698
699 return true;
700}
701
702export { animateFill, createSingleton, delegate, followCursor, inlinePositioning, sticky };
703//# sourceMappingURL=tippy.esm.js.map