UNPKG

vue-devui

Version:

DevUI components based on Vite and Vue3

461 lines (460 loc) 13.7 kB
import { toRefs, computed, defineComponent, shallowRef, reactive, inject, onUpdated, onBeforeMount, onMounted, onUnmounted, watch, nextTick, createVNode, resolveComponent, provide, mergeProps, getCurrentInstance } from "vue"; const tabsProps = { modelValue: { type: [String, Number], default: null }, type: { type: String, default: "tabs" }, showContent: { type: Boolean, default: true }, reactivable: { type: Boolean, default: true }, customWidth: { type: String, default: "" }, cssClass: { type: String, default: "" }, beforeChange: { type: Function, default: null }, closeable: { type: Boolean, default: false }, addable: { type: Boolean, default: false }, tabPosition: { type: String, default: "top" } }; function createBem(namespace, element, modifier) { let cls = namespace; if (element) { cls += `__${element}`; } if (modifier) { cls += `--${modifier}`; } return cls; } function useNamespace(block, needDot = false) { const namespace = needDot ? `.devui-${block}` : `devui-${block}`; const b = () => createBem(namespace); const e = (element) => element ? createBem(namespace, element) : ""; const m = (modifier) => modifier ? createBem(namespace, "", modifier) : ""; const em = (element, modifier) => element && modifier ? createBem(namespace, element, modifier) : ""; return { b, e, m, em }; } const ns$1 = useNamespace("tabs"); function useTabNavRender(props, data) { const { cssClass, tabPosition, customWidth } = toRefs(props); const ulClasses = computed(() => ({ [ns$1.e("nav")]: true, [ns$1.em("nav", props.type)]: true, [cssClass.value]: Boolean(cssClass.value), [ns$1.em("nav", "top")]: tabPosition.value === "top", [ns$1.em("nav", "right")]: tabPosition.value === "right", [ns$1.em("nav", "bottom")]: tabPosition.value === "bottom", [ns$1.em("nav", "left")]: tabPosition.value === "left" })); const aClasses = computed(() => ({ ["custom-width"]: Boolean(customWidth.value) })); const customStyle = { width: props.customWidth ? props.customWidth : "" }; const sliderAnimationStyle = computed(() => { if (["top", "bottom"].includes(props.tabPosition)) { return { left: data.offsetLeft + "px", width: data.offsetWidth + "px" }; } else { return { top: data.offsetTop + "px", height: data.offsetHeight + "px", width: data.offsetWidth + "px" }; } }); return { ulClasses, aClasses, customStyle, sliderAnimationStyle }; } function useTabNavFunction(props, tabs, tabsList, data, ctx, tabsEle) { const update = () => { if (props.type === "slider") { setTimeout(() => { var _a; const tabEle = (_a = tabsEle.value) == null ? void 0 : _a.querySelector("#" + props.modelValue + ".active"); if (tabEle && tabsEle.value) { if (["top", "bottom"].includes(props.tabPosition)) { data.offsetLeft = tabEle.getBoundingClientRect().left - tabsEle.value.getBoundingClientRect().left; } else { data.offsetTop = tabEle.getBoundingClientRect().top - tabsEle.value.getBoundingClientRect().top; data.offsetHeight = tabEle.getBoundingClientRect().height; } data.offsetWidth = tabEle.getBoundingClientRect().width; } }); } }; const canChange = (currentTab) => { let changeResult = Promise.resolve(true); if (typeof props.beforeChange === "function") { const result = props.beforeChange(currentTab); if (typeof result !== "undefined") { if (result.then) { changeResult = result; } else { changeResult = Promise.resolve(result); } } } return changeResult; }; const activeClick = (item, tabEl) => { const id = item.props.id; if (!props.reactivable && props.modelValue === id) { return; } canChange(id).then((change) => { if (!change) { return; } const tab2 = tabsList.value.find((itemOption) => itemOption.props.id === id); if (tabs && tab2 && !tab2.props.disabled) { tabs.state.active = id; if (props.type === "slider" && tabEl && tabsEle && tabsEle.value) { if (["left", "right"].includes(props.tabPosition)) { data.offsetLeft = tabEl.getBoundingClientRect().left - tabsEle.value.nativeElement.getBoundingClientRect().left; } else { data.offsetTop = tabEl.getBoundingClientRect().top - tabsEle.value.nativeElement.getBoundingClientRect().top; data.offsetHeight = tabEl.getBoundingClientRect().height; } data.offsetWidth = tabEl.getBoundingClientRect().width; } ctx.emit("active-tab-change", tab2.props.id); } }); }; const beforeMount = () => { if (props.type !== "slider" && props.modelValue === void 0 && tabsList.value && tabsList.value.length > 0) { activeClick(tabsList.value[0]); } }; const mounted = () => { var _a; if (props.type === "slider" && props.modelValue === void 0 && tabsList.value && tabsList.value.length > 0 && tabsList.value[0]) { const tabsStateData = tabsList.value[0]; const dom = (_a = tabsStateData.tabsEle) == null ? void 0 : _a.value; const ele = dom == null ? void 0 : dom.getElementById(tabsStateData.tabId); activeClick(ele); } }; const tabCanClose = (item) => { return (props.closeable || item.closeable) && !item.disabled; }; return { update, activeClick, beforeMount, mounted, tabCanClose }; } function useTabNavEvent(ctx) { const onTabRemove = (item, ev) => { ev.stopPropagation(); ctx.emit("tab-remove", item.props, ev); ctx.emit("tab-change", item.props.id, "delete"); }; const onTabAdd = () => { ctx.emit("tab-add"); ctx.emit("tab-change", void 0, "add"); }; return { onTabRemove, onTabAdd }; } var tabNav = ""; var TabNav = defineComponent({ name: "DTabNav", props: tabsProps, emits: ["active-tab-change", "tab-remove", "tab-add", "tab-change"], setup(props, ctx) { const ns2 = useNamespace("tabs"); const tabsEle = shallowRef(); const data = reactive({ offsetLeft: 0, offsetWidth: 0, offsetTop: 0, offsetHeight: 0, id: null }); const tabs = inject("tabs"); const tabsList = computed(() => Object.values((tabs == null ? void 0 : tabs.state.data) || {})); const { ulClasses, aClasses, customStyle, sliderAnimationStyle } = useTabNavRender(props, data); const { update, beforeMount, mounted, activeClick, tabCanClose } = useTabNavFunction(props, tabs, tabsList, data, ctx, tabsEle); const { onTabRemove, onTabAdd } = useTabNavEvent(ctx); const handleTabAdd = () => { onTabAdd(); nextTick(() => { if (tabsEle.value) { tabsEle.value.scrollLeft = tabsEle.value.scrollWidth; } }); }; let isSlide = false; const handleSlideTab = (mousedownEvent) => { if (tabsEle.value) { const mousedownX = mousedownEvent.clientX; const scrollLeft = tabsEle.value.scrollLeft; isSlide = true; tabsEle.value.addEventListener("mousemove", (mousemoveEvent) => { if (isSlide && tabsEle.value) { const mousemoveX = mousemoveEvent.clientX; const scrollWidth = mousemoveX - mousedownX; tabsEle.value.scrollLeft = scrollLeft - scrollWidth; } }); tabsEle.value.addEventListener("mouseup", () => { isSlide = false; }); tabsEle.value.addEventListener("mouseleave", () => { isSlide = false; }); } }; onUpdated(() => update()); onBeforeMount(() => beforeMount()); onMounted(() => { mounted(); if (tabsEle.value) { tabsEle.value.addEventListener("mousedown", handleSlideTab); } }); onUnmounted(() => { if (tabsEle.value) { tabsEle.value.removeEventListener("mousedown", handleSlideTab); } }); watch(() => props.modelValue, () => { nextTick(() => { const tab2 = tabsList.value.find((item) => item.props.id === props.modelValue); if (tab2) { activeClick(tab2); } }); }); return () => { const closeIconEl = (item) => { return tabCanClose(item) ? createVNode("span", { "class": ns2.e("close-btn"), "onClick": (ev) => onTabRemove(item, ev) }, [createVNode(resolveComponent("d-icon"), { "size": "12px", "name": "error-o" }, null)]) : null; }; const newButton = props.addable ? createVNode("li", { "class": ns2.e("new-tab"), "onClick": handleTabAdd }, [createVNode(resolveComponent("d-icon"), { "name": "add" }, null)]) : null; return createVNode("ul", { "ref": tabsEle, "role": "tablist", "class": ulClasses.value }, [(tabsList.value || []).map((item) => { return createVNode("li", { "role": "presentation", "onClick": () => { activeClick(item); }, "class": (props.modelValue === item.props.id ? "active" : "") + (item.props.disabled ? " disabled" : ""), "id": item.props.id }, [createVNode("span", { "class": ns2.e("nav-content") }, [createVNode("a", { "role": "tab", "data-toggle": item.props.id, "aria-expanded": props.modelValue === item.props.id, "class": aClasses.value, "style": customStyle }, [item.slots.title ? item.slots.title() : createVNode("span", null, [item.props.title])]), closeIconEl(item)])]); }), newButton, createVNode("div", { "class": ns2.e(`nav-${props.type}-animation`), "style": sliderAnimationStyle.value }, null)]); }; } }); const ns = useNamespace("tabs"); function useTabsEvent(ctx) { const onUpdateModelValue = (value) => { ctx.emit("update:modelValue", value); }; const onActiveTabChange = (value) => { ctx.emit("active-tab-change", value); }; const onTabRemove = (item, ev) => { ctx.emit("tab-remove", item, ev); }; const onTabAdd = () => { ctx.emit("tab-add"); }; const onTabChange = (id, type) => { ctx.emit("tab-change", id, type); }; return { onUpdateModelValue, onActiveTabChange, onTabRemove, onTabAdd, onTabChange }; } function useTabsRender(props) { const tabsClasses = computed(() => ({ [ns.b()]: true, [ns.m(props.tabPosition)]: true })); return { tabsClasses }; } var Tabs = defineComponent({ name: "DTabs", props: tabsProps, emits: ["update:modelValue", "active-tab-change", "tab-remove", "tab-add", "tab-change"], setup(props, ctx) { const state = reactive({ data: {}, active: props.modelValue, showContent: props.showContent }); const addTab = (tabCtx) => { if (tabCtx.uid) { state.data[tabCtx.uid] = tabCtx; } }; const deleteTab = (uid) => { if (uid) { delete state.data[uid]; } }; provide("tabs", { state, addTab, deleteTab }); const { onUpdateModelValue, onActiveTabChange, onTabRemove, onTabAdd, onTabChange } = useTabsEvent(ctx); const { tabsClasses } = useTabsRender(props); watch(() => state.active, () => { onUpdateModelValue(state.active); }); return () => { var _a, _b; const tabNav2 = createVNode(TabNav, mergeProps(props, { "onActiveTabChange": onActiveTabChange, "onTabRemove": onTabRemove, "onTabAdd": onTabAdd, "onTabChange": onTabChange }), null); const content = (_b = (_a = ctx.slots).default) == null ? void 0 : _b.call(_a); return createVNode("div", { "class": tabsClasses.value }, [props.tabPosition === "bottom" ? [content, tabNav2] : [tabNav2, content], createVNode("div", { "style": "clear: both" }, null)]); }; } }); const tabProps = { title: { type: [String, Number], default: null }, id: { type: String, default: null }, disabled: { type: Boolean, default: false }, closeable: { type: Boolean, default: false } }; var tab = ""; var Tab = defineComponent({ name: "DTab", props: tabProps, setup(props, { slots }) { const tabs = inject("tabs"); const ns2 = useNamespace("tab"); const instance = getCurrentInstance(); const tabContext = reactive({ uid: instance == null ? void 0 : instance.uid, slots, props }); onMounted(() => { tabs == null ? void 0 : tabs.addTab(tabContext); }); onUnmounted(() => { tabs == null ? void 0 : tabs.deleteTab(tabContext.uid); }); return () => { var _a; const { id } = props; const content = (tabs == null ? void 0 : tabs.state.showContent) && tabs.state.active === id ? createVNode("div", { "class": ns2.e("content") }, [createVNode("div", { "role": "tabpanel" }, [(_a = slots.default) == null ? void 0 : _a.call(slots)])]) : null; return content; }; } }); var index = { title: "Tabs \u9009\u9879\u5361", category: "\u5BFC\u822A", status: "100%", install(app) { app.component(Tabs.name, Tabs); app.component(Tab.name, Tab); } }; export { Tab, Tabs, index as default, tabsProps };