kui-vue
Version:
A lightweight desktop UI component library suitable for Vue.js 2.
285 lines (274 loc) • 7.85 kB
JSX
import { Button } from "../button";
import transfer from "../directives/transfer";
import { Close } from "kui-icons";
import {
defineComponent,
ref,
watch,
onMounted,
nextTick,
computed,
/*Transition,*/ onBeforeMount,
inject,
} from "vue";
import zhCN from "../locale/zh-CN";
import { withInstall } from "../utils/vue";
const Modal = defineComponent({
name: "Modal",
directives: { transfer },
props: {
// show: Boolean, //for 3
value: Boolean, //for 2
title: String,
okText: String,
cancelText: String,
top: Number,
width: Number,
mask: { type: Boolean, default: true },
maskClosable: { type: Boolean, default: false },
maximized: Boolean,
centered: Boolean,
draggable: Boolean,
showClose: { type: Boolean, default: true },
loading: Boolean,
footer: String,
transfer: { type: Boolean, default: true },
escKey: { type: Boolean, default: true },
},
setup(ps, { slots, emit }) {
// const visible = ref(ps.show); // for3
const visible = ref(ps.value);
const rendered = ref(false);
// const showInner = ref(ps.show); // for 3
const showInner = ref(ps.value);
const left = ref(0);
const currentTop = ref(ps.top);
const isMousePressed = ref(false);
const mousedownIn = ref(false);
const startPos = ref({ x: 0, y: 0 });
const refModal = ref();
const refHeader = ref();
const injectedLocale = inject("locale", zhCN);
const locale = computed(() => {
return injectedLocale instanceof Object && "value" in injectedLocale
? injectedLocale.value
: injectedLocale;
});
const escToClose = (event) => {
if (event.key === "Escape") {
close();
}
};
onBeforeMount(() => {
document.removeEventListener("mousedown", mousedown);
ps.escKey && document.removeEventListener("keydown", escToClose);
});
onMounted(() => {
document.addEventListener("mousedown", mousedown);
ps.escKey && document.addEventListener("keydown", escToClose);
if (ps.draggable) {
left.value = (document.body.offsetWidth - (ps.width || 480)) / 2;
}
// if (ps.show) { //for 3
if (ps.value) {
toggle(true);
}
});
watch(
// () => ps.show, //for 3
() => ps.value,
(nv, ov) => {
toggle(nv);
}
);
const getOffset = (el) => {
return el
? {
left: el.offsetLeft,
top: el.offsetTop,
}
: { left: 0, top: 0 };
};
const toggle = (value) => {
if (!rendered.value && value) {
rendered.value = true;
toggle(true);
} else {
if (value) {
nextTick((e) => {
visible.value = value;
showInner.value = value;
// emit("update:show", true); // for 3
emit("input", true);
nextTick(() => {
updateOrigin();
});
});
} else {
visible.value = false;
setTimeout(() => {
showInner.value = false;
}, 300);
// emit("update:show", false);// for 3
emit("input", false); // for 2
}
}
};
const updateOrigin = () => {
if (refModal.value) {
let { x, y } = window.__kui__point || { x: 0, y: 0 };
let { left, top } = getOffset(refModal.value);
refModal.value.style["transform-origin"] = `${x - left}px ${y - top}px`;
}
};
const ok = () => {
emit("ok");
};
const cancel = () => {
toggle(false);
emit("cancel");
};
const close = () => {
toggle(false);
emit("close");
};
const clickMaskToClose = (e) => {
if (
!ps.loading &&
ps.maskClosable &&
!refModal.value.contains(e.target) &&
!mousedownIn.value
) {
close();
}
};
const mousemove = (e) => {
if (isMousePressed.value && ps.draggable) {
let { x, y } = startPos.value;
left.value += e.clientX - x;
currentTop.value = currentTop.value || 100;
currentTop.value += e.clientY - y;
startPos.value = { x: e.clientX, y: e.clientY };
updateOrigin();
e.preventDefault();
}
};
const mouseup = (e) => {
isMousePressed.value = false;
document.removeEventListener("mousemove", mousemove);
document.removeEventListener("mouseup", mouseup);
};
const mousedown = (e) => {
if (
e.button == 0 &&
ps.draggable === true &&
refHeader.value &&
refHeader.value.contains(e.target)
) {
isMousePressed.value = true;
startPos.value = { x: e.clientX, y: e.clientY };
mousemove(e);
document.addEventListener("mousemove", mousemove);
document.addEventListener("mouseup", mouseup);
}
mousedownIn.value =
visible.value && refModal.value && refModal.value.contains(e.target);
};
return () => {
let { draggable, width } = ps;
//mask
let maskNode = null;
if (ps.mask) {
maskNode = (
<transition name="k-modal-fade">
<div class="k-modal-mask" v-show={visible.value} />
</transition>
);
}
let okText = ps.okText || locale?.value.k.common.ok;
let cancelText = ps.cancelText || locale?.value.k.common.cancel;
//content
let contentNode = slots.content?.();
if (!contentNode) {
const contents = [];
ps.showClose &&
contents.push(
<Button
icon={Close}
size="small"
onClick={close}
class="k-modal-close"
type="text"
></Button>
);
ps.title !== null &&
contents.push(
<div class="k-modal-header" ref={refHeader}>
<div class="k-modal-header-inner">{ps.title}</div>
</div>
);
contents.push(<div class="k-modal-body">{slots.default?.()}</div>);
//footer
if (ps.footer !== null) {
let footer = slots.footer?.();
if (!footer) {
footer = [
<Button onClick={cancel}>{cancelText}</Button>,
<Button onClick={ok} type="primary" loading={ps.loading}>
{okText}
</Button>,
];
}
const footerNode = footer ? (
<div class="k-modal-footer">{footer}</div>
) : null;
contents.push(footerNode);
}
contentNode = (
<div class="k-modal-content" tabindex="0">
{contents}
</div>
);
}
const style = {
width: `${width}px`,
top: `${currentTop.value}px`,
left: `${left.value}px`,
};
const classes = [
"k-modal",
{
"k-modal-draggable": draggable,
"k-modal-maximized": ps.maximized,
"k-modal-centered": ps.centered,
"k-modal-has-footer": ps.footer !== null,
},
];
return rendered.value ? (
<div class={classes} v-transfer={ps.transfer}>
{maskNode}
<div
class="k-modal-wrap"
tabindex="-1"
role="dialog"
v-show={showInner.value}
onClick={clickMaskToClose}
>
<transition name="k-modal-zoom">
<div
class="k-modal-inner"
ref={refModal}
v-show={visible.value}
style={style}
>
{contentNode}
<div tabindex="0"></div>
</div>
</transition>
</div>
</div>
) : null;
};
},
});
export default withInstall(Modal);