UNPKG

@bardoui/vpopper

Version:

PopperJs powered popup for vue 3

488 lines (467 loc) 17.9 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue'), require('@popperjs/core'), require('animejs')) : typeof define === 'function' && define.amd ? define(['exports', 'vue', '@popperjs/core', 'animejs'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.vPopper = {}, global.Vue, global.Popper, global.anime)); })(this, (function (exports, vue, core, anime) { 'use strict'; function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var anime__default = /*#__PURE__*/_interopDefaultLegacy(anime); /** * Default popper options */ const GlobalOptions = vue.reactive({ enterAnimation: { top: { translateY: [-20, 0], opacity: [0, 1], easing: "cubicBezier(0.165, 0.840, 0.440, 1.000)", duration: 200, }, left: { translateX: [-20, 0], opacity: [0, 1], easing: "cubicBezier(0.165, 0.840, 0.440, 1.000)", duration: 200, }, bottom: { translateY: [20, 0], opacity: [0, 1], easing: "cubicBezier(0.165, 0.840, 0.440, 1.000)", duration: 200, }, right: { translateX: [20, 0], opacity: [0, 1], easing: "cubicBezier(0.165, 0.840, 0.440, 1.000)", duration: 200, }, }, leaveAnimation: { top: { translateY: [0, -20], opacity: [1, 0], easing: "cubicBezier(0.165, 0.840, 0.440, 1.000)", duration: 200, }, left: { translateX: [0, -20], opacity: [1, 0], easing: "cubicBezier(0.165, 0.840, 0.440, 1.000)", duration: 200, }, bottom: { translateY: [0, 20], opacity: [1, 0], easing: "cubicBezier(0.165, 0.840, 0.440, 1.000)", duration: 200, }, right: { translateX: [0, 20], opacity: [1, 0], easing: "cubicBezier(0.165, 0.840, 0.440, 1.000)", duration: 200, }, }, }); /** * set global popper options * @param option new options */ function setGlobalOptions(option) { const res = mergeOptions(GlobalOptions, option); GlobalOptions.enterAnimation = res.enterAnimation; GlobalOptions.leaveAnimation = res.leaveAnimation; } /** * merge multiple popper options * ignore undefined value * latest option value is selected * * @param options options list to merge * @returns merged options */ function mergeOptions(...options) { const res = {}; for (const option of options) { if (option.enterAnimation) { res.enterAnimation = option.enterAnimation; } if (option.leaveAnimation) { res.leaveAnimation = option.leaveAnimation; } } return res; } // default create function let instance = core.popperGenerator(); /** * register popperjs creator instance * @param instance createFunction instance */ function registerCreator(ins = core.popperGenerator()) { instance = ins; } /** * get default create function */ function createFunction() { return instance; } /** * Create default popperJs instance * this instance includes this modifiers: * popperOffsets * preventOverflow * arrow * flip * computeStyles * applyStyles * eventListeners */ function createDefaultPopper() { registerCreator(core.popperGenerator({ defaultOptions: { modifiers: [ { name: "arrow", options: { padding: 7, }, }, ], }, defaultModifiers: [ core.popperOffsets, core.preventOverflow, core.arrow, core.flip, core.computeStyles, core.applyStyles, core.eventListeners, ], })); } /** * check if animation object is of type PopupFullAnimation * @param obj popup animation object */ function isPopperFullAnimation(obj) { return (obj && obj.top && typeof obj.top === "object" && obj.left && typeof obj.left === "object" && obj.bottom && typeof obj.bottom === "object" && obj.right && typeof obj.right === "object"); } /** * get popperJs placement and return vPopper placement * for auto and default state returns bottom * * @param placement popperJs placement */ function resolvePlacement(placement) { if (placement.includes("top")) return "top"; if (placement.includes("left")) return "left"; if (placement.includes("right")) return "right"; return "bottom"; } /** * get current animation based on placement * undefined animation skipped * * @param placement popper placement * @param animations animations list to search for animation state */ function resolveAnimation(placement, ...animations) { let animation = {}; for (const anim of animations) { if (anim) { animation = anim; } } if (isPopperFullAnimation(animation)) { return animation[resolvePlacement(placement)]; } return animation || {}; } var script$2 = vue.defineComponent({ name: "vPopper", inheritAttrs: false, emits: ["initialized", "show", "hide"], props: { tag: String, trigger: String, onAction: Function, closable: { type: Boolean, default: true, }, options: Object, config: Object, }, setup(props, { attrs, slots, emit }) { // Stats and refs const target = vue.ref(); const popup = vue.ref(); const animWrapper = vue.ref(); const shown = vue.ref(false); const loading = vue.ref(false); const trigger = vue.computed(() => props.trigger || "hover"); const option = vue.computed(() => mergeOptions(vue.toRaw(GlobalOptions), props.options || {})); // Animations let showAnim; let hideAnim; vue.onMounted(() => { if (!target.value || !popup.value || !animWrapper.value) return; const instance = createFunction()(target.value, popup.value, vue.toRaw(props.config || {})); emit("initialized", instance); showAnim = anime__default["default"](Object.assign(Object.assign({}, resolveAnimation(instance.state.options.placement, option.value.enterAnimation)), { targets: animWrapper.value, autoplay: false, })); hideAnim = anime__default["default"](Object.assign(Object.assign({}, resolveAnimation(instance.state.options.placement, option.value.leaveAnimation)), { targets: animWrapper.value, autoplay: false, })); }); // Internals let hiding = false; let showCallback; let hideCallback; function doShow() { if (!shown.value) { shown.value = true; hiding = false; hideAnim === null || hideAnim === void 0 ? void 0 : hideAnim.pause(); if (showAnim) { showAnim === null || showAnim === void 0 ? void 0 : showAnim.restart(); showAnim.finished.then(() => { showCallback && showCallback(); emit("show"); }); } else { showCallback && showCallback(); emit("show"); } } } function doHide(mode) { if (shown.value && !hiding) { hiding = true; showAnim === null || showAnim === void 0 ? void 0 : showAnim.pause(); if (hideAnim) { hideAnim.restart(); hideAnim.finished.then(() => { shown.value = false; hiding = false; hideCallback && hideCallback(mode); emit("hide", mode); }); } else { shown.value = false; hiding = false; hideCallback && hideCallback(mode); emit("hide", mode); } } } function doAction(key, data) { if (props.onAction) { loading.value = true; props .onAction(key, data) .then((res) => { loading.value = false; if (res) { doHide("action"); } }) .catch(() => { loading.value = false; }); } } // Provide for child components vue.provide("v-popper-close", doHide); vue.provide("v-popper-action", doAction); vue.provide("v-popper-loading", loading); vue.provide("v-popper-on-show", (cb) => (showCallback = cb)); vue.provide("v-popper-on-hide", (cb) => (hideCallback = cb)); // Handle Functionalities const onTargetFocus = () => { setTimeout(() => { if (target.value && trigger.value === "focus") { if (target.value.contains(document.activeElement)) { doShow(); } else { doHide("blur"); } } }); }; const onOutsideClick = (e) => { if (target.value && e.target) { if (target.value.contains(e.target)) { trigger.value === "click" && doShow(); } else { props.closable && doHide("blur"); } } }; const onTargetMouseLeave = () => trigger.value === "hover" && doHide("blur"); vue.onMounted(() => { if (target.value) { document.addEventListener("focusin", onTargetFocus); document.addEventListener("focusout", onTargetFocus); document.addEventListener("click", onOutsideClick); document.addEventListener("mousemove", onTargetMouseLeave); } }); vue.onUnmounted(() => { document.removeEventListener("focusin", onTargetFocus); document.removeEventListener("focusout", onTargetFocus); document.removeEventListener("click", onOutsideClick); document.removeEventListener("mousemove", onTargetMouseLeave); }); // Render return () => [ vue.h(props.tag || "span", Object.assign(Object.assign({ ref: target }, attrs), { onMousemove: vue.withModifiers(() => trigger.value === "hover" && doShow(), ["stop"]) }), slots.default ? slots.default({ open: doShow, close: doHide, action: doAction, loading: loading, }) : vue.h("span", "Put Content here!")), vue.h("div", { ref: popup, class: "v-popper-container", style: { visibility: shown.value ? undefined : "hidden" }, onMousemove: vue.withModifiers(() => ({}), ["stop"]), onClick: vue.withModifiers(() => ({}), ["stop"]), }, vue.h("div", { ref: animWrapper, }, slots.popup ? slots.popup() : vue.h("span", "put popup data in popup slot"))), ]; }, }); script$2.__file = "src/Popper.vue"; function usePopup() { // Stats const fallbackCB = () => console.error("Use popup plugin inside popper component!"); // injects const close = vue.inject("v-popper-close", fallbackCB); const action = vue.inject("v-popper-action", fallbackCB); const loading = vue.inject("v-popper-loading", vue.ref(false)); const onShow = vue.inject("v-popper-on-show", fallbackCB); const onHide = vue.inject("v-popper-on-hide", fallbackCB); return { close, action, loading, onShow, onHide }; } var script$1 = vue.defineComponent({ name: "vPopup", emits: ["show", "hide"], props: { closable: { type: Boolean, default: true }, }, setup(props, { emit }) { const { close, action, loading, onShow, onHide } = usePopup(); onShow(() => emit("show")); onHide((mode) => emit("hide", mode)); function dismiss() { props.closable && close("click"); } return { loading, action, close, dismiss }; }, }); const _hoisted_1$1 = /*#__PURE__*/vue.createElementVNode("div", { class: "arrow", "data-popper-arrow": "" }, null, -1 /* HOISTED */); function render$1(_ctx, _cache, $props, $setup, $data, $options) { return (vue.openBlock(), vue.createElementBlock("div", { class: vue.normalizeClass(["v-popup", { 'is-loading': _ctx.loading }]), onClick: _cache[0] || (_cache[0] = vue.withModifiers((...args) => (_ctx.dismiss && _ctx.dismiss(...args)), ["stop"])) }, [ vue.renderSlot(_ctx.$slots, "default", { action: _ctx.action, close: _ctx.close, loading: _ctx.loading }), _hoisted_1$1 ], 2 /* CLASS */)) } script$1.render = render$1; script$1.__file = "src/Popup.vue"; var script = vue.defineComponent({ name: "vPopup", emits: ["show", "hide"], props: { closable: { type: Boolean, default: true }, }, setup(props, { emit }) { const { close, action, loading, onShow, onHide } = usePopup(); onShow(() => emit("show")); onHide((mode) => emit("hide", mode)); function dismiss() { props.closable && close("click"); } return { loading, action, close, dismiss }; }, }); const _hoisted_1 = /*#__PURE__*/vue.createElementVNode("div", { class: "arrow", "data-popper-arrow": "" }, null, -1 /* HOISTED */); function render(_ctx, _cache, $props, $setup, $data, $options) { return (vue.openBlock(), vue.createElementBlock("div", { class: vue.normalizeClass(["v-tooltip", { 'is-loading': _ctx.loading }]), onClick: _cache[0] || (_cache[0] = vue.withModifiers((...args) => (_ctx.dismiss && _ctx.dismiss(...args)), ["stop"])) }, [ vue.renderSlot(_ctx.$slots, "default", { action: _ctx.action, close: _ctx.close, loading: _ctx.loading }), _hoisted_1 ], 2 /* CLASS */)) } script.render = render; script.__file = "src/Tooltip.vue"; /** * Create default popperJs instance by default */ createDefaultPopper(); /** * install popper plugin */ var vPopper = { install: (app) => { app.component("popper", script$2); app.component("popup", script$1); app.component("tooltip", script); }, }; exports.Popper = script$2; exports.Popup = script$1; exports.Tooltip = script; exports.createDefaultPopper = createDefaultPopper; exports.createFunction = createFunction; exports["default"] = vPopper; exports.registerCreator = registerCreator; exports.setGlobalOptions = setGlobalOptions; exports.usePopup = usePopup; Object.defineProperty(exports, '__esModule', { value: true }); })); //# sourceMappingURL=vpopper.global.js.map