UNPKG

@opensig/opendesign

Version:

668 lines (667 loc) 27.7 kB
"use strict"; const vue = require("vue"); const core = require("@vueuse/core"); const index$2 = require("../option/index.js"); const index$1 = require("../popup/index.js"); const icons = require("../_utils/icons.js"); const is = require("../_utils/is.js"); const dom = require("../_utils/dom.js"); require("../hooks/use-theme.js"); const useScreen = require("../hooks/use-screen.js"); const useResponseCssVar = require("../hooks/use-response-css-var.js"); const useSortedTeleportChildren = require("../hooks/useSortedTeleportChildren.js"); const onResize = require("../directives/on-resize.js"); const helper = require("../_utils/helper.js"); const global = require("../_utils/global.js"); const clientOnly = require("../_components/client-only.js"); const vueUtils = require("../_utils/vue-utils.js"); const styleClass = require("../_utils/style-class.js"); const index = require("../locale/index.js"); const provide = require("./provide.js"); const types = require("./types.js"); const OTabPane_vue_vue_type_script_setup_true_lang = require("./OTabPane.vue.js"); const OOptionList_vue_vue_type_script_setup_true_lang = require("../option/OOptionList.vue.js"); const _hoisted_1 = ["onClick"]; const _hoisted_2 = ["onClick"]; const _hoisted_3 = { key: 0, class: "o-tab-head-prefix" }; const _hoisted_4 = { key: 1, class: "o-tab-head-suffix" }; const _hoisted_5 = { class: "o-tab-body" }; const _sfc_main = /* @__PURE__ */ vue.defineComponent({ __name: "OTab", props: types.tabProps, emits: ["update:modelValue", "change", "delete", "add"], setup(__props, { emit: __emit }) { const props = __props; const emits = __emit; const round = styleClass.getRoundClass(props, "tab-btn"); const { lePadV } = useScreen.useScreen(); const { t } = index.useI18n(); const moreLabel = vue.computed(() => props.moreLabel || t("common.more")); const activeKey = vue.ref(props.modelValue); const anchorStyle = vue.ref({}); const navsContainerRef = vue.ref(); const { width: navsContainerWidth } = core.useElementBounding(navsContainerRef); const navGapNumber = useResponseCssVar.useResponseCssVar("--tab-nav-gap", navsContainerRef, { initialValue: "32px", transform(value) { return Number.parseInt(value); } }); const tabNavMeasurementRef = vue.ref(); const { width: tabNavMeasurementWidth } = core.useElementBounding(tabNavMeasurementRef); const ellipsisRef = vue.ref(); const { width: ellipsisWidth } = core.useElementBounding(ellipsisRef); const navEllipsisShadowWidthNumber = useResponseCssVar.useResponseCssVar("--tab-nav-ellipsis-shadow-width", navsContainerRef, { initialValue: "8px", transform(value) { return Number.parseInt(value); } }); const navListStyle = vue.computed(() => { if (props.variant === "button" || lePadV.value) { return; } if (props.maxShow && props.maxShow > 0) { return { width: `${navsContainerWidth.value}px` }; } if (ellipsisWidth.value) { return { width: `${navsContainerWidth.value - ellipsisWidth.value - navEllipsisShadowWidthNumber.value}px` }; } return void 0; }); const tabNavRef = vue.ref(); const tabNavLeftOverflown = vue.ref(false); const tabNavRightOverflown = vue.ref(false); const checkOverflow = helper.debounceRAF(() => { if (!lePadV.value || !tabNavRef.value) { return; } const result = dom.checkElementOverflowHorizontal({ element: tabNavRef.value, threshold: 1 }); tabNavLeftOverflown.value = result.isOverflowLeft; tabNavRightOverflown.value = result.isOverflowRight; }); core.until(tabNavRef).toBeTruthy().then(checkOverflow); const instance = vue.getCurrentInstance(); const { children: sortedChildren, childMap, addChild, OTeleportWrapper } = useSortedTeleportChildren.useSortedTeleportChildren(instance, OTabPane_vue_vue_type_script_setup_true_lang); const uidSet = vue.computed(() => sortedChildren.value.map((c) => c.uid)); const showUids = vue.ref([]); const hiddenUids = vue.ref([]); const paneKeyToUid = vue.computed(() => { const map = /* @__PURE__ */ new Map(); sortedChildren.value.forEach((c) => { map.set(vue.toValue(c.paneKey), c.uid); }); return map; }); vue.watch(uidSet, () => { showUids.value = showUids.value.filter((uid) => uidSet.value.includes(uid)); hiddenUids.value = hiddenUids.value.filter((uid) => uidSet.value.includes(uid)); }); const getChildData = (uid) => childMap[uid]; const pushShowUid = (localShow, uid, widthCount) => { const item = getChildData(uid); const { navElWidth } = item; const gap = widthCount > 0 ? navGapNumber.value : 0; const isWidthWillExceed = widthCount + gap + navElWidth > navsContainerWidth.value; const isShowNumWillExceed = is.isUndefined(props.maxShow) ? false : localShow.length + 1 > props.maxShow; if (!isWidthWillExceed && !isShowNumWillExceed) { localShow.push(uid); return widthCount + gap + navElWidth; } return widthCount; }; const sortUidList = helper.debounceRAF(() => { if (props.variant === "button" || lePadV.value) { showUids.value = uidSet.value; hiddenUids.value = []; return; } uidSet.value.forEach((uid) => { const item = getChildData(uid); if (item == null ? void 0 : item.navMeasureEl) { item.navElWidth = item.navMeasureEl.clientWidth; } }); let localShow = [...showUids.value]; let widthCount = 0; if (!localShow.length) { uidSet.value.forEach((uid) => { widthCount = pushShowUid(localShow, uid, widthCount); }); } widthCount = localShow.reduce((count, uid) => { const item = getChildData(uid); return count + item.navElWidth + navGapNumber.value; }, -navGapNumber.value); const isTotalWidthExceed = tabNavMeasurementWidth.value > navsContainerWidth.value; const isTotalNumExceed = is.isUndefined(props.maxShow) ? false : uidSet.value.length > props.maxShow; if (isTotalWidthExceed || isTotalNumExceed) { widthCount += is.isUndefined(props.maxShow) ? navEllipsisShadowWidthNumber.value : navGapNumber.value; widthCount += ellipsisWidth.value; } const activeUid = activeKey.value != null ? paneKeyToUid.value.get(activeKey.value) : void 0; let activeItemIndex = localShow.findIndex((uid) => uid === activeUid); if (activeItemIndex === -1 && activeUid != null) { const activeItemIndexInTotal = uidSet.value.findIndex((uid) => uid === activeUid); const targetIndex = activeItemIndexInTotal === 0 ? 0 : localShow.findIndex((uid, i) => { if (i === localShow.length - 1) { return true; } const curIndexInTotal = uidSet.value.findIndex((v) => v === uid); const nextIndexInTotal = uidSet.value.findIndex((v) => v === localShow[i + 1]); if (curIndexInTotal < activeItemIndexInTotal && nextIndexInTotal > activeItemIndexInTotal) { return true; } return false; }) + 1; const activeItem = getChildData(activeUid); localShow.splice(targetIndex, 0, activeUid); widthCount += activeItem.navElWidth; widthCount += navGapNumber.value; activeItemIndex = localShow.length - 1; } const getIsShowWidthExceed = () => widthCount > navsContainerWidth.value; const getIsShowNumExceed = () => is.isUndefined(props.maxShow) ? false : localShow.length > props.maxShow; while ((getIsShowWidthExceed() || getIsShowNumExceed()) && localShow.length > 1) { activeItemIndex = localShow.findIndex((uid) => uid === activeUid); const removedUid = activeItemIndex === localShow.length - 1 ? localShow.shift() : localShow.pop(); const shiftedItem = getChildData(removedUid); widthCount -= shiftedItem.navElWidth; widthCount -= navGapNumber.value; } let localHidden = uidSet.value.filter((uid) => !localShow.includes(uid)); localHidden.forEach((uid) => { widthCount = pushShowUid(localShow, uid, widthCount); }); localHidden = uidSet.value.filter((uid) => !localShow.includes(uid)); localShow.sort((a, b) => { return uidSet.value.findIndex((v) => v === a) - uidSet.value.findIndex((v) => v === b); }); localHidden.sort((a, b) => { return uidSet.value.findIndex((v) => v === a) - uidSet.value.findIndex((v) => v === b); }); showUids.value = localShow; hiddenUids.value = localHidden; }); vue.onMounted(() => { vue.watch( [ uidSet, activeKey, navsContainerWidth, tabNavMeasurementWidth, () => props.maxShow, () => props.variant, ellipsisWidth, navGapNumber, navEllipsisShadowWidthNumber ], () => { sortUidList(); }, { immediate: true, deep: true } ); }); const [DefineTabNavTemplate, ReuseTabNavTemplate] = core.createReusableTemplate(); const isEllipsisOptionShow = vue.ref(false); const updateAnchor = async () => { if (is.isUndefined(activeKey.value)) { return; } const activeUid = paneKeyToUid.value.get(activeKey.value); if (activeUid == null) { return; } const activeItem = getChildData(activeUid); await core.until(() => (activeItem == null ? void 0 : activeItem.navEl) && activeItem.navMeasureEl).toBeTruthy(); const { clientWidth, offsetLeft } = activeItem.navEl; anchorStyle.value = { transform: `translate3d(${offsetLeft}px, 0px, 0px)`, width: `${clientWidth}px` }; }; const scrollActiveIntoView = async () => { var _a; if (!lePadV.value || !navsContainerRef.value) { return; } if (is.isUndefined(activeKey.value)) { return; } const activeUid = paneKeyToUid.value.get(activeKey.value); if (activeUid == null) { return; } const activeItem = getChildData(activeUid); await core.until(() => activeItem == null ? void 0 : activeItem.navEl).toBeTruthy(); (_a = activeItem.navEl) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" }); }; vue.onMounted(() => { scrollActiveIntoView(); }); vue.watch( [showUids, activeKey], () => { updateAnchor(); scrollActiveIntoView(); }, { immediate: true, deep: true } ); const setNavEl = (el, uid) => { if (el == null) { return; } const item = getChildData(uid); item.navEl = el; }; const setNavMeasureEl = async (el, uid) => { if (el == null) { return; } const item = getChildData(uid); item.navMeasureEl = el; if (el) { item.navElWidth = el.clientWidth; } }; const updateValue = async (uid) => { const child = childMap[uid]; if (child.props.disabled) { return; } const _value = vue.toValue(child.paneKey); emits("update:modelValue", _value); if (activeKey.value !== _value) { emits("change", _value, activeKey.value); activeKey.value = _value; } isEllipsisOptionShow.value = false; }; vue.watch( () => props.modelValue, (v) => { activeKey.value = v; if (v != null) { const uid = paneKeyToUid.value.get(v); if (uid != null) { updateValue(uid); } } } ); const onDeletePane = (e, uid) => { var _a; e.stopImmediatePropagation(); const child = childMap[uid]; const _value = vue.toValue(child.paneKey); emits("delete", _value); const idx = uidSet.value.indexOf(uid); if (activeKey.value === _value) { const targetUid = uidSet.value[idx > 0 ? idx - 1 : idx + 1]; activeKey.value = vue.toValue((_a = childMap[targetUid]) == null ? void 0 : _a.paneKey); emits("change", activeKey.value, _value); } }; const isAdding = vue.ref(false); const onAddNav = (e) => { emits("add", e); if (!props.addInactive) { isAdding.value = true; } }; vue.provide(provide.tabInjectKey, { lazy: props.lazy, activeValue: activeKey, addChild: (child) => { addChild(child); if (activeKey.value === void 0 || isAdding.value) { updateValue(child.uid); } } }); const onHeadItemResize = helper.debounceRAF((uid) => { const item = getChildData(uid); if (!(item == null ? void 0 : item.navMeasureEl)) { return; } item.navElWidth = item.navMeasureEl.clientWidth; }); const onHeadResize = helper.debounceRAF(() => { checkOverflow(); updateAnchor(); scrollActiveIntoView(); }); core.useMutationObserver( tabNavRef, (mutations) => { if (mutations[0]) { onHeadResize(); } }, { childList: true } ); vue.watch(lePadV, () => { onHeadResize(); }); return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock( "div", { class: vue.normalizeClass(["o-tab", [ `o-tab-${props.variant}`, { "o-tab-button-inverse": props.variant === "button" && props.buttonInverse }, `o-tab-${props.size || vue.unref(global.defaultSize)}`, vue.unref(round).class.value ]]), style: vue.normalizeStyle(vue.unref(round).style.value) }, [ vue.createVNode(vue.unref(DefineTabNavTemplate), null, { default: vue.withCtx(({ uid, measurement }) => [ vue.withDirectives((vue.openBlock(), vue.createElementBlock("div", { ref: (el) => measurement ? setNavMeasureEl(el, uid) : setNavEl(el, uid), class: vue.normalizeClass([ "o-tab-nav", { "o-tab-nav-active": vue.toValue(vue.unref(childMap)[uid].paneKey) === activeKey.value, "o-tab-nav-disabled": vue.unref(childMap)[uid].props.disabled, "o-tab-nav-closable": vue.unref(childMap)[uid].props.closable } ]), onClick: () => updateValue(uid) }, [ vue.unref(childMap)[uid].navRenderer ? (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(vue.unref(childMap)[uid].navRenderer), { key: 0 })) : (vue.openBlock(), vue.createElementBlock( vue.Fragment, { key: 1 }, [ vue.createTextVNode( vue.toDisplayString(vue.unref(childMap)[uid].props.label || vue.unref(childMap)[uid].props.value), 1 /* TEXT */ ) ], 64 /* STABLE_FRAGMENT */ )), vue.unref(childMap)[uid].props.closable ? (vue.openBlock(), vue.createElementBlock("div", { key: 2, class: "o-tab-nav-close", onClick: (e) => onDeletePane(e, uid) }, [ vue.createVNode(vue.unref(icons.IconClose)) ], 8, _hoisted_2)) : vue.createCommentVNode("v-if", true) ], 10, _hoisted_1)), [ [vue.unref(onResize.vOnResize), measurement ? () => vue.unref(onHeadItemResize)(uid) : () => { }] ]) ]), _: 1 /* STABLE */ }), vue.createElementVNode( "div", { class: vue.normalizeClass([ "o-tab-head", vue.unref(vueUtils.mergeClass)( { "with-act": !!_ctx.$slots.suffix || !!_ctx.$slots.prefix, "show-line": !!props.line && props.variant !== "button" }, props.headerClass ) ]) }, [ _ctx.$slots.prefix ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3, [ vue.renderSlot(_ctx.$slots, "prefix") ])) : vue.createCommentVNode("v-if", true), vue.createElementVNode( "div", { class: vue.normalizeClass({ "o-tab-navs": true, "o-tab-navs-overflown-left": tabNavLeftOverflown.value, "o-tab-navs-overflown-right": tabNavRightOverflown.value }) }, [ vue.withDirectives((vue.openBlock(), vue.createElementBlock( "div", { ref_key: "navsContainerRef", ref: navsContainerRef, class: vue.normalizeClass({ "o-tab-navs-container": true, overflown: hiddenUids.value.length, "o-tab-navs-container-mb-overflown": tabNavLeftOverflown.value || tabNavRightOverflown.value }), onScroll: _cache[0] || (_cache[0] = //@ts-ignore (...args) => vue.unref(checkOverflow) && vue.unref(checkOverflow)(...args)) }, [ vue.createCommentVNode(" 渲染一个全宽但是零高度的节点来测量每个节点的宽度,以计算溢出情况 "), vue.withDirectives((vue.openBlock(), vue.createElementBlock("div", { ref_key: "tabNavMeasurementRef", ref: tabNavMeasurementRef, class: "o-tab-nav-list width-measurement" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList(uidSet.value, (uid) => { return vue.openBlock(), vue.createBlock(vue.unref(ReuseTabNavTemplate), { key: vue.unref(childMap)[uid].paneKey, uid, measurement: true }, null, 8, ["uid"]); }), 128 /* KEYED_FRAGMENT */ )) ])), [ [vue.vShow, !vue.unref(lePadV)], [vue.unref(onResize.vOnResize), vue.unref(onHeadResize)] ]), vue.withDirectives((vue.openBlock(), vue.createElementBlock( "div", { ref_key: "tabNavRef", ref: tabNavRef, class: "o-tab-nav-list", style: vue.normalizeStyle(navListStyle.value) }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList(showUids.value, (uid) => { return vue.openBlock(), vue.createBlock(vue.unref(ReuseTabNavTemplate), { key: vue.unref(childMap)[uid].paneKey, uid }, null, 8, ["uid"]); }), 128 /* KEYED_FRAGMENT */ )), props.variant !== "button" ? vue.withDirectives((vue.openBlock(), vue.createElementBlock( "div", { key: 0, ref_key: "ellipsisRef", ref: ellipsisRef, class: vue.normalizeClass([ "o-tab-nav", { "o-tab-nav-active": props.maxShow && isEllipsisOptionShow.value, "o-tab-nav-ellipsis": vue.unref(is.isUndefined)(props.maxShow) } ]) }, [ props.maxShow ? (vue.openBlock(), vue.createElementBlock( vue.Fragment, { key: 0 }, [ vue.createTextVNode( vue.toDisplayString(moreLabel.value) + " ", 1 /* TEXT */ ), vue.createVNode(vue.unref(icons.IconChevronDown), { class: vue.normalizeClass({ "o-tab-nav-more-arrow": true, active: isEllipsisOptionShow.value }) }, null, 8, ["class"]) ], 64 /* STABLE_FRAGMENT */ )) : (vue.openBlock(), vue.createElementBlock( vue.Fragment, { key: 1 }, [ vue.createTextVNode("...") ], 64 /* STABLE_FRAGMENT */ )) ], 2 /* CLASS */ )), [ [vue.vShow, hiddenUids.value.length] ]) : vue.createCommentVNode("v-if", true) ], 4 /* STYLE */ )), [ [vue.unref(onResize.vOnResize), vue.unref(onHeadResize)] ]), props.variant === "text" ? (vue.openBlock(), vue.createElementBlock( "div", { key: 0, class: "o-tab-nav-anchor", style: vue.normalizeStyle(anchorStyle.value) }, [ vue.renderSlot(_ctx.$slots, "anchor", {}, () => [ _cache[2] || (_cache[2] = vue.createElementVNode( "div", { class: "o-tab-nav-anchor-line" }, null, -1 /* CACHED */ )) ]) ], 4 /* STYLE */ )) : vue.createCommentVNode("v-if", true) ], 34 /* CLASS, NEED_HYDRATION */ )), [ [vue.unref(onResize.vOnResize), vue.unref(onHeadResize)] ]), props.addable ? (vue.openBlock(), vue.createElementBlock("div", { key: 0, class: "o-tab-nav-add", onClick: onAddNav }, [ vue.createVNode(vue.unref(icons.IconAdd)) ])) : vue.createCommentVNode("v-if", true) ], 2 /* CLASS */ ), _ctx.$slots.suffix ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_4, [ vue.renderSlot(_ctx.$slots, "suffix") ])) : vue.createCommentVNode("v-if", true) ], 2 /* CLASS */ ), vue.createElementVNode("div", _hoisted_5, [ vue.createVNode(vue.unref(OTeleportWrapper), null, { default: vue.withCtx(() => [ vue.renderSlot(_ctx.$slots, "default") ]), _: 3 /* FORWARDED */ }) ]), vue.createVNode(vue.unref(clientOnly), null, { default: vue.withCtx(() => [ vue.createVNode(vue.unref(index$1.OPopup), { visible: isEllipsisOptionShow.value, "onUpdate:visible": _cache[1] || (_cache[1] = ($event) => isEllipsisOptionShow.value = $event), "wrap-class": "o-options-popup o-tab-more-popup", "body-class": "o-popup-body", position: "bl", wrapper: "body", target: ellipsisRef.value, trigger: ["click-outclick", "hover"], offset: 4 }, { default: vue.withCtx(() => [ vue.createVNode(vue.unref(OOptionList_vue_vue_type_script_setup_true_lang), { "wrap-class": "o-scrollbar-container" }, { default: vue.withCtx(() => [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList(hiddenUids.value, (uid) => { return vue.openBlock(), vue.createBlock(vue.unref(index$2.OOption), { key: uid, value: vue.toValue(vue.unref(childMap)[uid].paneKey), onClick: ($event) => updateValue(uid) }, { default: vue.withCtx(() => [ vue.unref(childMap)[uid].navRenderer ? (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(vue.unref(childMap)[uid].navRenderer), { key: 0 })) : (vue.openBlock(), vue.createElementBlock( vue.Fragment, { key: 1 }, [ vue.createTextVNode( vue.toDisplayString(vue.unref(childMap)[uid].props.label || vue.unref(childMap)[uid].props.value), 1 /* TEXT */ ) ], 64 /* STABLE_FRAGMENT */ )) ]), _: 2 /* DYNAMIC */ }, 1032, ["value", "onClick"]); }), 128 /* KEYED_FRAGMENT */ )) ]), _: 1 /* STABLE */ }) ]), _: 1 /* STABLE */ }, 8, ["visible", "target"]) ]), _: 1 /* STABLE */ }) ], 6 /* CLASS, STYLE */ ); }; } }); module.exports = _sfc_main;