@arco-design/web-vue
Version:
Arco Design Vue 2.0: A Vue.js 3 UI Library
322 lines (321 loc) • 10.9 kB
JavaScript
import { defineComponent, toRefs, inject, computed, ref, watch, nextTick, onMounted, onUnmounted, createVNode } from "vue";
import { updateScrollOffset } from "./utils.js";
import { getPrefixCls } from "../_utils/global-config.js";
import TabsTab from "./tabs-tab.js";
import TabsButton from "./tabs-button.js";
import TabsNavInk from "./tabs-nav-ink.js";
import IconHover from "../_components/icon-hover.js";
import IconPlus from "../icon/icon-plus/index.js";
import ResizeObserver from "../_components/resize-observer.js";
import { isUndefined, isNumber } from "../_utils/is.js";
import { on, off } from "../_utils/dom.js";
import { configProviderInjectionKey } from "../config-provider/context.js";
var TabsNav = defineComponent({
name: "TabsNav",
props: {
tabs: {
type: Array,
required: true
},
direction: {
type: String,
required: true
},
type: {
type: String,
required: true
},
activeKey: {
type: [String, Number]
},
activeIndex: {
type: Number,
required: true
},
position: {
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
},
scrollPosition: {
type: String,
default: "auto"
}
},
emits: ["click", "add", "delete"],
setup(props, {
emit,
slots
}) {
const {
tabs,
activeKey,
activeIndex,
direction,
scrollPosition
} = toRefs(props);
const prefixCls = getPrefixCls("tabs-nav");
const configCtx = inject(configProviderInjectionKey, void 0);
const rtl = computed(() => {
var _a;
return (_a = configCtx == null ? void 0 : configCtx.rtl) != null ? _a : false;
});
const wrapperRef = ref();
const listRef = ref();
const tabsRef = ref({});
const activeTabRef = computed(() => {
if (!isUndefined(activeKey.value)) {
return tabsRef.value[activeKey.value];
}
return void 0;
});
const isRtlHorizontal = computed(() => rtl.value && direction.value === "horizontal");
const inkRef = ref();
const mergedEditable = computed(() => props.editable && ["line", "card", "card-gutter"].includes(props.type));
const isScroll = ref(false);
const wrapperLength = ref(0);
const maxOffset = ref(0);
const offset = 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 getSize = () => {
isScroll.value = isOverflow();
if (isScroll.value) {
wrapperLength.value = getWrapperLength();
maxOffset.value = getMaxOffset();
if (offset.value > maxOffset.value) {
offset.value = maxOffset.value;
}
} else {
offset.value = 0;
}
};
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 setOffset = (newOffset) => {
if (!wrapperRef.value || !listRef.value || newOffset < 0) {
newOffset = 0;
}
offset.value = Math.min(newOffset, maxOffset.value);
};
const setActiveTabOffset = () => {
var _a;
if (!activeTabRef.value || !wrapperRef.value || !isScroll.value)
return;
updateScrollOffset(wrapperRef.value, direction.value);
const isHorizontal = direction.value === "horizontal";
const sizeProperty = isHorizontal ? "offsetWidth" : "offsetHeight";
const wrapperSize = wrapperRef.value[sizeProperty];
const tabSize = activeTabRef.value[sizeProperty];
let tabPosition = 0;
if (isRtlHorizontal.value) {
const listWidth = ((_a = listRef.value) == null ? void 0 : _a.offsetWidth) || 0;
const {
offsetLeft
} = activeTabRef.value;
tabPosition = listWidth - offsetLeft - tabSize;
} else {
tabPosition = isHorizontal ? activeTabRef.value.offsetLeft : activeTabRef.value.offsetTop;
}
const marginSide = isHorizontal ? isRtlHorizontal.value ? scrollPosition.value === "end" ? "marginLeft" : "marginRight" : scrollPosition.value === "end" ? "marginRight" : "marginLeft" : scrollPosition.value === "end" ? "marginBottom" : "marginTop";
const tabStyle = window.getComputedStyle(activeTabRef.value);
const tabMargin = parseFloat(tabStyle[marginSide]) || 0;
let targetOffset = 0;
switch (scrollPosition.value) {
case "auto":
if (tabPosition < offset.value) {
targetOffset = tabPosition - tabMargin;
} else if (tabPosition + tabSize > offset.value + wrapperSize) {
targetOffset = tabPosition + tabSize - wrapperSize + tabMargin;
}
break;
case "center":
targetOffset = tabPosition + (tabSize - wrapperSize + tabMargin) / 2;
break;
case "start":
targetOffset = tabPosition - tabMargin;
break;
case "end":
targetOffset = tabPosition + tabSize - wrapperSize + tabMargin;
break;
default:
if (isNumber(scrollPosition.value)) {
targetOffset = tabPosition - scrollPosition.value;
}
}
setOffset(targetOffset);
};
const handleWheel = (ev) => {
if (!isScroll.value)
return;
ev.preventDefault();
const {
deltaX,
deltaY
} = ev;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
setOffset(offset.value + deltaX);
} else {
setOffset(offset.value + deltaY);
}
};
const handleClick = (key, ev) => {
emit("click", key, ev);
};
const handleDelete = (key, ev) => {
emit("delete", key, ev);
nextTick(() => {
delete tabsRef.value[key];
});
};
const handleButtonClick = (type) => {
const scrollDirection = type === "previous" !== isRtlHorizontal.value ? -1 : 1;
const nextOffset = offset.value + scrollDirection * wrapperLength.value;
setOffset(nextOffset);
};
const handleResize = () => {
getSize();
if (inkRef.value) {
inkRef.value.$forceUpdate();
}
};
watch(tabs, () => {
nextTick(() => {
getSize();
});
});
watch([activeIndex, scrollPosition, rtl], () => {
setTimeout(() => {
setActiveTabOffset();
if (inkRef.value) {
inkRef.value.getInkStyle();
}
}, 0);
});
onMounted(() => {
getSize();
if (wrapperRef.value) {
on(wrapperRef.value, "wheel", handleWheel, {
passive: false
});
}
});
onUnmounted(() => {
if (wrapperRef.value) {
off(wrapperRef.value, "wheel", handleWheel);
}
});
const renderAddBtn = () => {
if (!mergedEditable.value || !props.showAddButton) {
return null;
}
return createVNode("div", {
"class": `${prefixCls}-add-btn`,
"onClick": (ev) => emit("add", ev)
}, [createVNode(IconHover, null, {
default: () => [createVNode(IconPlus, null, null)]
})]);
};
const cls = computed(() => [prefixCls, `${prefixCls}-${props.direction}`, `${prefixCls}-${props.position}`, `${prefixCls}-size-${props.size}`, `${prefixCls}-type-${props.type}`]);
const listCls = computed(() => [`${prefixCls}-tab-list`, {
[`${prefixCls}-tab-list-no-padding`]: !props.headerPadding && ["line", "text"].includes(props.type) && props.direction === "horizontal"
}]);
const listStyle = computed(() => ({
transform: direction.value === "vertical" ? `translateY(${-offset.value}px)` : `translateX(${rtl.value ? offset.value : -offset.value}px)`
}));
const tabCls = computed(() => [`${prefixCls}-tab`, {
[`${prefixCls}-tab-scroll`]: isScroll.value
}]);
return () => {
var _a;
return createVNode("div", {
"class": cls.value
}, [isScroll.value && createVNode(TabsButton, {
"type": isRtlHorizontal.value ? "next" : "previous",
"direction": props.direction,
"disabled": offset.value <= 0,
"onClick": handleButtonClick
}, null), createVNode(ResizeObserver, {
"onResize": () => getSize()
}, {
default: () => [createVNode("div", {
"class": tabCls.value,
"ref": wrapperRef
}, [createVNode(ResizeObserver, {
"onResize": handleResize
}, {
default: () => [createVNode("div", {
"ref": listRef,
"class": listCls.value,
"style": listStyle.value
}, [props.tabs.map((tab) => createVNode(TabsTab, {
"key": tab.key,
"ref": (component) => {
if (component == null ? void 0 : component.$el) {
tabsRef.value[tab.key] = component.$el;
}
},
"active": tab.key === activeKey.value,
"tab": tab,
"editable": props.editable,
"onClick": handleClick,
"onDelete": handleDelete
}, {
default: () => {
var _a2, _b, _c;
return [(_c = (_b = (_a2 = tab.slots).title) == null ? void 0 : _b.call(_a2)) != null ? _c : tab.title];
}
})), props.type === "line" && activeTabRef.value && createVNode(TabsNavInk, {
"ref": inkRef,
"activeTabRef": activeTabRef.value,
"direction": props.direction,
"disabled": false,
"animation": props.animation
}, null)])]
}), !isScroll.value && renderAddBtn()])]
}), isScroll.value && createVNode(TabsButton, {
"type": isRtlHorizontal.value ? "previous" : "next",
"direction": props.direction,
"disabled": offset.value >= maxOffset.value,
"onClick": handleButtonClick
}, null), createVNode("div", {
"class": `${prefixCls}-extra`
}, [isScroll.value && renderAddBtn(), (_a = slots.extra) == null ? void 0 : _a.call(slots)])]);
};
}
});
export { TabsNav as default };