winbox-ui-next
Version:
We Design Vue 2.0: A Vue.js 3 UI Library
258 lines (257 loc) • 8.4 kB
JavaScript
;
var vue = require("vue");
var utils = require("./utils.js");
var globalConfig = require("../_utils/global-config.js");
var tabsTab = require("./tabs-tab.js");
var tabsButton = require("./tabs-button.js");
var tabsNavInk = require("./tabs-nav-ink.js");
var iconHover = require("../_components/icon-hover.js");
var index = require("../icon/icon-plus/index.js");
var resizeObserver = require("../_components/resize-observer.js");
var TabsNav = vue.defineComponent({
name: "TabsNav",
props: {
direction: {
type: String,
required: true
},
type: {
type: String,
required: true
},
activeTab: {
type: String,
required: true
},
activeIndex: {
type: Number,
required: true
},
tabs: {
type: Array,
required: true
},
tabKeys: {
type: Array,
required: true
},
tabPosition: {
type: String,
required: true
},
size: {
type: String,
required: true
},
showAddButton: {
type: Boolean,
default: false
},
editable: {
type: Boolean,
default: false
},
animation: {
type: Boolean,
required: true
},
headerPadding: {
type: Boolean,
default: true
}
},
emits: ["click", "add", "delete"],
setup(props, {
emit,
slots
}) {
const prefixCls = globalConfig.getPrefixCls("tabs-nav");
const {
tabs,
tabKeys,
activeIndex,
direction
} = vue.toRefs(props);
const wrapperRef = vue.ref();
const listRef = vue.ref();
const tabsRef = vue.ref({});
const wrapperLength = vue.ref(0);
const maxOffset = vue.ref(0);
const getWrapperLength = () => {
var _a, _b, _c;
return (_c = direction.value === "vertical" ? (_a = wrapperRef.value) == null ? void 0 : _a.offsetHeight : (_b = wrapperRef.value) == null ? void 0 : _b.offsetWidth) != null ? _c : 0;
};
const getMaxOffset = () => {
if (!listRef.value || !wrapperRef.value) {
return 0;
}
if (direction.value === "vertical") {
return listRef.value.offsetHeight - wrapperRef.value.offsetHeight;
}
return listRef.value.offsetWidth - wrapperRef.value.offsetWidth;
};
const getTabEndOffsets = () => {
return tabs.value.map((item) => {
const ele = tabsRef.value[item.key];
if (direction.value === "vertical") {
return ele.offsetTop + ele.offsetHeight;
}
return ele.offsetLeft + ele.offsetWidth;
});
};
const getSize = () => {
isScroll.value = isOverflow();
if (isScroll.value) {
wrapperLength.value = getWrapperLength();
maxOffset.value = getMaxOffset();
tabEndOffsets.value = getTabEndOffsets();
if (offset.value > maxOffset.value) {
offset.value = maxOffset.value;
}
} else {
offset.value = 0;
}
};
const activeTabRef = vue.computed(() => tabsRef.value[props.activeTab]);
const isOverflow = () => {
if (wrapperRef.value && listRef.value) {
return props.direction === "vertical" ? listRef.value.offsetHeight > wrapperRef.value.offsetHeight : listRef.value.offsetWidth > wrapperRef.value.offsetWidth;
}
return false;
};
const isScroll = vue.ref(false);
vue.onMounted(() => {
getSize();
});
const tabEndOffsets = vue.ref([]);
const offset = vue.ref(0);
const isInView = (index2) => {
var _a;
return ((_a = tabEndOffsets.value[index2 - 1]) != null ? _a : 0) >= offset.value && tabEndOffsets.value[index2] <= offset.value + wrapperLength.value;
};
vue.watch(activeIndex, (current, pre) => {
vue.nextTick(() => {
var _a;
if (isScroll.value) {
if (current >= pre) {
const offsetIndex = current < tabEndOffsets.value.length - 1 ? current + 1 : current;
if (!isInView(offsetIndex)) {
offset.value = tabEndOffsets.value[offsetIndex] - wrapperLength.value;
}
} else {
const offsetIndex = current > 0 ? current - 1 : current;
if (!isInView(offsetIndex)) {
offset.value = (_a = tabEndOffsets.value[offsetIndex - 1]) != null ? _a : 0;
}
}
}
});
});
const mergedEditable = vue.computed(() => props.editable && ["line", "card", "card-gutter"].includes(props.type));
const getNextOffset = (type) => {
if (!wrapperRef.value) {
return 0;
}
if (props.type === "capsule") {
return type === "previous" ? offset.value + wrapperLength.value : offset.value - wrapperLength.value;
}
return type === "previous" ? offset.value - wrapperLength.value : offset.value + wrapperLength.value;
};
const handleButtonClick = (type) => {
offset.value = getValidOffset(getNextOffset(type));
};
const getValidOffset = (offset2) => {
if (!wrapperRef.value || !listRef.value || offset2 < 0) {
return 0;
}
if (offset2 > maxOffset.value) {
return maxOffset.value;
}
return offset2;
};
vue.watch(tabs, () => {
vue.nextTick(() => {
getSize();
});
});
const handleResize = () => {
getSize();
if (inkRef.value) {
inkRef.value.$forceUpdate();
}
};
const renderAddBtn = () => {
if (!mergedEditable.value || !props.showAddButton) {
return null;
}
return vue.createVNode("div", {
"class": `${prefixCls}-add-btn`,
"onClick": () => emit("add")
}, [vue.createVNode(iconHover, null, {
default: () => [vue.createVNode(index, null, null)]
})]);
};
const cls = vue.computed(() => [prefixCls, `${prefixCls}-${props.direction}`, `${prefixCls}-${props.tabPosition}`, `${prefixCls}-size-${props.size}`, `${prefixCls}-type-${props.type}`]);
const inkRef = vue.ref();
return () => {
var _a;
return vue.createVNode("div", {
"class": cls.value
}, [isScroll.value && vue.createVNode(tabsButton, {
"type": "previous",
"direction": props.direction,
"disabled": offset.value <= 0,
"onClick": handleButtonClick
}, null), vue.createVNode(resizeObserver, {
"onResize": () => getSize()
}, {
default: () => [vue.createVNode("div", {
"class": `${prefixCls}-tab`,
"ref": wrapperRef
}, [vue.createVNode(resizeObserver, {
"onResize": handleResize
}, {
default: () => [vue.createVNode("div", {
"class": [`${prefixCls}-tab-list`, {
[`${prefixCls}-tab-list-no-padding`]: !props.headerPadding && props.direction === "horizontal" && ["line", "text"].includes(props.type)
}],
"style": utils.getTabListStyle({
direction: props.direction,
type: props.type,
offset: offset.value
}),
"ref": listRef
}, [props.tabs.map((tab, index2) => vue.createVNode(tabsTab, {
"ref": (component) => {
if (component == null ? void 0 : component.$el) {
tabsRef.value[tab.key] = component.$el;
}
},
"isActive": props.activeIndex === index2,
"key": tab.key,
"tab": tab,
"editable": props.editable,
"onClick": (key, e) => emit("click", key, e),
"onDelete": (key) => emit("delete", key)
}, {
title: () => tab.title()
})), props.type === "line" && activeTabRef.value && vue.createVNode(tabsNavInk, {
"ref": inkRef,
"activeTabRef": activeTabRef.value,
"direction": props.direction,
"disabled": false,
"animation": props.animation
}, null)])]
}), !isScroll.value && renderAddBtn()])]
}), isScroll.value && vue.createVNode(tabsButton, {
"type": "next",
"direction": props.direction,
"disabled": offset.value >= maxOffset.value,
"onClick": handleButtonClick
}, null), vue.createVNode("div", {
"class": `${prefixCls}-extra`
}, [isScroll.value && renderAddBtn(), (_a = slots.extra) == null ? void 0 : _a.call(slots)])]);
};
}
});
module.exports = TabsNav;