UNPKG

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