kui-vue
Version:
A lightweight desktop UI component library suitable for Vue.js 2.
234 lines (231 loc) • 6.53 kB
JSX
import {
defineComponent,
/*Transition,*/ ref,
/*cloneVNode,*/ nextTick,
watch,
onMounted,
onBeforeMount,
} from "vue";
import transfer from "../directives/transfer";
import { getChildren } from "../utils/vnode";
import { setPlacement } from "../utils/placement";
import { withInstall, cloneVNode } from "../utils/vue";
import { placements } from "../const/var";
const Poptip = defineComponent({
name: "Poptip",
directives: {
transfer,
},
props: {
dark: Boolean,
show: Boolean,
title: [String, Number, Object, Array],
size: String,
width: [Number, String],
trigger: {
validator(value) {
return ["click", "hover", "focus"].includes(value);
},
default: "hover",
},
placement: {
validator(value) {
return placements.includes(value);
},
default: "top",
},
},
setup(ps, { slots, attrs, emit }) {
// const te = {...attrs}
// console.log(te)
const rendered = ref(ps.show);
const visible = ref(ps.show);
const refPopper = ref();
const refSelection = ref();
const left = ref(0);
const top = ref(0);
const currentPlacement = ref(ps.placement);
const transOrigin = ref("bottom");
const hideTimer = ref();
const showTimer = ref();
const updatePosition = () => {
nextTick(() => {
setPlacement({
refSelection,
refPopper,
currentPlacement,
transOrigin,
top,
left,
});
});
};
onMounted(() => {
if (ps.show) {
updatePosition();
}
});
onBeforeMount(() => {
document.removeEventListener("click", outsideClick);
});
watch(
() => ps.show,
(nv, no) => {
visible.value = nv;
}
// { immediate: true }
);
// 监听 title 的变化
watch(
() => ps.title,
() => {
if (visible.value) {
updatePosition();
}
}
);
const updateShow = (value) => {
visible.value = value;
emit("update:show", value);
};
const outsideClick = (e) => {
const ctx = refSelection.value?.$el || refSelection.value;
if (
refPopper.value &&
!refPopper.value.contains(e.target) &&
ctx &&
!ctx.contains(e.target)
) {
updateShow(false);
}
};
const show = () => {
if (!rendered.value) {
rendered.value = true;
document.addEventListener("click", outsideClick);
nextTick(() => {
updateShow(true);
updatePosition();
});
} else {
clearTimeout(showTimer.value);
updateShow(true);
updatePosition();
}
};
const hide = () => {
hideTimer.value = setTimeout(() => {
if (!ps.show) {
updateShow(false);
}
}, 300);
};
return () => {
const title = slots.title?.() || ps.title;
const content = slots.content?.() || ps.content;
const preCls = "poptip";
const cls = [
`k-${preCls}`,
{
[`k-${preCls}-has-arrow`]: true,
[`k-${preCls}-dark`]: ps.dark,
},
];
const wpProps = {
ref: refSelection,
// onMouseleave: hide, //for 3
on: { mouseleave: hide },
};
if (ps.trigger === "click") {
// wpProps.onClick = show; for
wpProps.on.click = show;
} else if (ps.trigger === "hover") {
// wpProps.onMouseenter = show;
wpProps.on.mouseenter = show;
} else if (ps.trigger === "focus") {
// wpProps.onFocus = show;
// wpProps.onBlur = hide;
wpProps.on.focus = show;
wpProps.on.blur = hide;
}
const children = getChildren(slots.default?.());
const nodes = children?.map((node) => {
// let pp = { ...attrs };
let pp = { attrs: { ...attrs } };
if (children.length == 1) {
pp = { ...pp, ...wpProps };
}
// return cloneVNode(node, pp, true, true); //for 3
return cloneVNode(node, pp, true);
});
const nodeWrapper =
nodes.length > 1 ? <span {...wpProps}>{...nodes}</span> : nodes[0];
const styles = {
left: `${left.value}px`,
top: `${top.value}px`,
transformOrigin: transOrigin.value,
};
// const childNodes = [nodeWrapper];
const props = {
// "k-placement": currentPlacement.value, //for 3
attrs: { "k-placement": currentPlacement.value },
style: styles,
ref: refPopper,
on: {
mouseenter: () => {
clearTimeout(hideTimer.value);
updateShow(true);
},
mouseleave: () => {
showTimer.value = setTimeout(() => {
if (!ps.show) {
updateShow(false);
}
}, 300);
},
},
// onMouseenter: () => {
// clearTimeout(hideTimer.value);
// updateShow(true);
// },
// onMouseleave: () => {
// showTimer.value = setTimeout(() => {
// if (!ps.show) {
// updateShow(false);
// }
// }, 300);
// },
};
// if (rendered.value) {
// childNodes.push(
const overlay = rendered.value ? (
<transition name={`k-${preCls}`}>
<div class={cls} v-transfer={true} v-show={visible.value} {...props}>
<div class={`k-${preCls}-content`}>
{title ? <div class={`k-${preCls}-title`}>{title}</div> : null}
<div class={`k-${preCls}-body`}>{content}</div>
<div class={`k-${preCls}-arrow`}>
<svg style={{ fill: "currentcolor" }} viewBox="0 0 24 8">
<path
id="ot"
d="m24,0.97087l0,1c-4,0 -5.5,1 -7.5,3c-2,2 -2.5,3 -4.5,3c-2,0 -2.5,-1 -4.5,-3c-2,-2 -3.5,-3 -7.5,-3l0,-1l24,0z"
/>
<path
stroke="currentcolor"
id="in"
d="m24,0l0,1c-4,0 -5.5,1 -7.5,3c-2,2 -2.5,3 -4.5,3c-2,0 -2.5,-1 -4.5,-3c-2,-2 -3.5,-3 -7.5,-3l0,-1l24,0z"
/>
</svg>
</div>
</div>
</div>
</transition>
) : null;
// );
// }
// return <>{...childNodes}</>; //for 3
return cloneVNode(nodeWrapper, {}, true, overlay);
};
},
});
export default withInstall(Poptip);