@opensig/opendesign
Version:
<p align="center"><img src="../docs/public/opendesign-logo-light.png"/></p> <h1 align="center">opendesign</h1> <p align="center">一个 Vue 3 组件库</p> <p align="center"><b>皮肤可定制,使用 TypeScript</b></p>
303 lines (302 loc) • 10.6 kB
JavaScript
import { defineComponent, ref, computed, watch, provide, createElementBlock, openBlock, normalizeClass, unref, createElementVNode, withDirectives, createCommentVNode, renderSlot, createVNode, normalizeStyle, nextTick } from "vue";
import { tabInjectKey } from "./provide.mjs";
import { IconChevronLeft, IconChevronRight, IconAdd } from "../_utils/icons.mjs";
import { tabProps } from "./types.mjs";
import { useScreen } from "../hooks/use-screen.mjs";
import { vOnResize } from "../directives/on-resize.mjs";
import { debounceRAF } from "../_utils/helper.mjs";
import { defaultSize } from "../_utils/global.mjs";
const _hoisted_1 = {
key: 0,
class: "o-tab-head-prefix"
};
const _hoisted_2 = { class: "o-tab-navs" };
const _hoisted_3 = {
key: 1,
class: "o-tab-head-suffix"
};
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "OTab",
props: tabProps,
emits: ["update:modelValue", "change", "delete", "add"],
setup(__props, { emit: __emit }) {
const props = __props;
const emits = __emit;
const { isPhonePad } = useScreen();
const activeKey = ref(props.modelValue);
const anchorStyle = ref({});
const navWrapRef = ref(null);
const navsRef = ref(null);
const bodyRef = ref(null);
const valueSet = [];
let activeNavEl = null;
const isScroll = ref(false);
const prevDisabled = ref(true);
const nextDisabled = ref(true);
const showArrow = computed(() => {
return !isPhonePad.value && isScroll.value;
});
const scrollActiveNavIntoView = () => {
var _a;
if (isScroll.value && activeNavEl && navWrapRef.value) {
const { offsetLeft } = activeNavEl;
const { scrollLeft, clientWidth } = navWrapRef.value;
const center = scrollLeft + clientWidth / 2;
if (offsetLeft > center || offsetLeft < center) {
(_a = navWrapRef.value) == null ? void 0 : _a.scrollTo({
left: offsetLeft - clientWidth / 2,
behavior: "smooth"
});
}
}
};
const onWrapScroll = debounceRAF(() => {
if (navWrapRef.value) {
const { scrollLeft, scrollWidth, clientWidth } = navWrapRef.value;
prevDisabled.value = scrollLeft === 0;
nextDisabled.value = scrollLeft + 1 >= scrollWidth - clientWidth;
}
});
const updateNavScroll = () => {
if (navWrapRef.value && navsRef.value) {
const { clientWidth: wrapWidth } = navWrapRef.value;
const { clientWidth: width } = navsRef.value;
isScroll.value = wrapWidth < width;
if (isScroll.value) {
nextTick(() => {
onWrapScroll();
scrollActiveNavIntoView();
});
}
}
};
watch(
() => props.modelValue,
(v) => {
activeKey.value = v;
}
);
const updateAnchor = () => {
if (!activeNavEl) {
return;
}
const { clientWidth, offsetLeft } = activeNavEl;
anchorStyle.value = {
transform: `translate3d(${offsetLeft}px, 0px, 0px)`,
width: `${clientWidth}px`
};
};
watch(() => isScroll.value, updateAnchor);
const updateValue = (value, navEl) => {
emits("update:modelValue", value);
activeNavEl = navEl;
if (activeKey.value !== value) {
emits("change", value, activeKey.value);
activeKey.value = value;
}
if (navEl) {
activeNavEl = navEl;
updateAnchor();
scrollActiveNavIntoView();
}
};
const isAdding = ref(false);
const initValue = (value, navEl) => {
if (!valueSet.includes(value)) {
valueSet.push(value);
}
if (activeKey.value === void 0 || isAdding.value) {
updateValue(value, navEl);
isAdding.value = false;
}
if (activeKey.value === value && navEl) {
activeNavEl = navEl;
updateAnchor();
}
};
const onDeletePane = (value) => {
emits("delete", value);
const idx = valueSet.indexOf(value);
if (activeKey.value === value) {
activeKey.value = valueSet[idx > 0 ? idx - 1 : 0];
emits("change", activeKey.value, value);
}
valueSet.splice(idx, 1);
};
const onAddNav = (e) => {
emits("add", e);
if (!props.addInactive) {
isAdding.value = true;
}
};
provide(tabInjectKey, {
lazy: props.lazy,
navsRef,
bodyRef,
activeValue: activeKey,
updateValue,
onDeletePane,
initValue
});
const onHeadResize = debounceRAF(() => {
updateAnchor();
updateNavScroll();
scrollActiveNavIntoView();
});
const navScroll = (to) => {
if (!navWrapRef.value) {
return;
}
const { clientWidth } = navWrapRef.value;
const i = to === "prev" ? -1 : to === "next" ? 1 : 0;
navWrapRef.value.scrollBy({ left: i * clientWidth, behavior: "smooth" });
};
return (_ctx, _cache) => {
return openBlock(), createElementBlock(
"div",
{
class: normalizeClass(["o-tab", [`o-tab-${props.variant}`, `o-tab-${props.size || unref(defaultSize)}`]])
},
[
createElementVNode(
"div",
{
class: normalizeClass(["o-tab-head", [
{
"with-act": _ctx.$slots.suffix || _ctx.$slots.prefix,
"show-line": props.line
},
props.headerClass
]])
},
[
_ctx.$slots.prefix ? withDirectives((openBlock(), createElementBlock("div", _hoisted_1, [
renderSlot(_ctx.$slots, "prefix")
])), [
[unref(vOnResize), unref(onHeadResize)]
]) : createCommentVNode("v-if", true),
createElementVNode("div", _hoisted_2, [
createElementVNode(
"div",
{
class: normalizeClass([{ "o-tab-navs-scrollable": isScroll.value }, "o-tab-navs-container"])
},
[
showArrow.value ? (openBlock(), createElementBlock(
"div",
{
key: 0,
class: normalizeClass(["o-tab-nav-btn prev", { "o-tab-nav-btn-disabled": prevDisabled.value }]),
onClick: _cache[0] || (_cache[0] = ($event) => navScroll("prev"))
},
[
createVNode(unref(IconChevronLeft))
],
2
/* CLASS */
)) : createCommentVNode("v-if", true),
createElementVNode(
"div",
{
ref_key: "navWrapRef",
ref: navWrapRef,
class: "o-tab-navs-wrap o-hide-scrollbar",
onScrollPassive: _cache[1] || (_cache[1] = //@ts-ignore
(...args) => unref(onWrapScroll) && unref(onWrapScroll)(...args))
},
[
withDirectives(createElementVNode(
"div",
{
class: "o-tab-nav-list",
ref_key: "navsRef",
ref: navsRef
},
null,
512
/* NEED_PATCH */
), [
[unref(vOnResize), unref(onHeadResize)]
]),
props.variant === "text" ? (openBlock(), createElementBlock(
"div",
{
key: 0,
class: "o-tab-nav-anchor",
style: normalizeStyle(anchorStyle.value)
},
[
renderSlot(_ctx.$slots, "anchor", {}, () => [
_cache[3] || (_cache[3] = createElementVNode(
"div",
{ class: "o-tab-nav-anchor-line" },
null,
-1
/* HOISTED */
))
])
],
4
/* STYLE */
)) : createCommentVNode("v-if", true)
],
544
/* NEED_HYDRATION, NEED_PATCH */
),
showArrow.value ? (openBlock(), createElementBlock(
"div",
{
key: 1,
class: normalizeClass(["o-tab-nav-btn next", { "o-tab-nav-btn-disabled": nextDisabled.value }]),
onClick: _cache[2] || (_cache[2] = ($event) => navScroll("next"))
},
[
createVNode(unref(IconChevronRight))
],
2
/* CLASS */
)) : createCommentVNode("v-if", true)
],
2
/* CLASS */
),
props.addable ? (openBlock(), createElementBlock("div", {
key: 0,
class: "o-tab-nav-add",
onClick: onAddNav
}, [
createVNode(unref(IconAdd))
])) : createCommentVNode("v-if", true)
]),
_ctx.$slots.suffix ? withDirectives((openBlock(), createElementBlock("div", _hoisted_3, [
renderSlot(_ctx.$slots, "suffix")
])), [
[unref(vOnResize), unref(onHeadResize)]
]) : createCommentVNode("v-if", true)
],
2
/* CLASS */
),
createElementVNode(
"div",
{
ref_key: "bodyRef",
ref: bodyRef,
class: "o-tab-body"
},
[
renderSlot(_ctx.$slots, "default")
],
512
/* NEED_PATCH */
)
],
2
/* CLASS */
);
};
}
});
export {
_sfc_main as default
};