UNPKG

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