@bardoui/vpopper
Version:
PopperJs powered popup for vue 3
486 lines (465 loc) • 15.7 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var vue = require('vue');
var core = require('@popperjs/core');
var anime = require('animejs');
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;
//# sourceMappingURL=vpopper.cjs.js.map