UNPKG

bootstrap-vue-next

Version:

Seamless integration of Vue 3, Bootstrap 5, and TypeScript for modern, type-safe UI development

567 lines (566 loc) 21.3 kB
import { C as tabsInjectionKey } from "./keys-CQKrwmvN.mjs"; import { n as createReusableTemplate } from "./dist-Dn5blevd.mjs"; import { c as sortSlotElementsByPosition, i as getSafeDocument } from "./dom-AhkaSoh8.mjs"; import { t as useId$1 } from "./useId-CCwnEmGh.mjs"; import { t as useDefaults } from "./useDefaults-CCWS15M8.mjs"; import { t as flattenFragments } from "./flattenFragments-B1W_x5L2.mjs"; import { n as BvEvent } from "./classes-D6JE1Dex.mjs"; import { t as useAlignment } from "./useAlignment-CwuLc9mf.mjs"; import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, mergeModels, mergeProps, nextTick, normalizeClass, onMounted, onUnmounted, openBlock, provide, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, toRef, unref, useAttrs, useModel, useSlots, useTemplateRef, watch, withCtx, withKeys, withModifiers } from "vue"; //#endregion //#region src/components/BTabs/BTab.vue var BTab_default = /* @__PURE__ */ defineComponent({ inheritAttrs: false, __name: "BTab", props: /* @__PURE__ */ mergeModels({ buttonId: { default: void 0 }, disabled: { type: Boolean, default: false }, id: { default: void 0 }, lazy: { type: Boolean, default: void 0 }, unmountLazy: { type: Boolean, default: void 0 }, noBody: { type: Boolean, default: false }, tag: { default: "div" }, title: { default: void 0 }, titleItemClass: { default: void 0 }, titleLinkAttrs: { default: void 0 }, titleLinkClass: { default: void 0 } }, { "active": { type: Boolean, default: false }, "activeModifiers": {} }), emits: ["update:active"], setup(__props, { expose: __expose }) { const props = useDefaults(__props, "BTab"); const slots = useSlots(); const attrs = useAttrs(); const activeModel = useModel(__props, "active"); const parentData = inject(tabsInjectionKey, null); const localId = ref(props.id); const internalId = useId$1("", "tabpane"); const computedId = computed(() => props.id ?? localId.value ?? internalId.value); const computedButtonId = useId$1(() => props.buttonId, "tab"); const lazyRenderCompleted = ref(false); const el = useTemplateRef("_el"); const processedAttrs = computed(() => { const { onClick: _, ...tabAttrs } = attrs; return tabAttrs; }); function updateTab() { if (!parentData) return; const newId = parentData.registerTab(computed(() => ({ internalId: internalId.value, id: computedId.value, active: activeModel.value, buttonId: computedButtonId.value, disabled: props.disabled, title: props.title, titleComponent: slots.title, titleItemClass: props.titleItemClass, titleLinkAttrs: props.titleLinkAttrs, titleLinkClass: props.titleLinkClass, onClick: attrs.onClick, el }))); if (newId !== localId.value) localId.value = newId; } if (parentData) { updateTab(); if (activeModel.value) parentData.activateTab(internalId.value); } onUnmounted(() => { if (!parentData) return; parentData.unregisterTab(internalId.value); }); const isActive = computed(() => parentData?.activeId.value === computedId.value); const show = ref(isActive.value); const computedLazy = computed(() => !!(parentData?.lazy.value || props.lazy)); const computedActive = computed(() => isActive.value && !props.disabled); const showSlot = computed(() => computedActive.value || !computedLazy.value || computedLazy.value && !props.unmountLazy && lazyRenderCompleted.value); watch(showSlot, (shown) => { if (shown && !lazyRenderCompleted.value) lazyRenderCompleted.value = true; }); watch(isActive, (active) => { if (active) { activeModel.value = true; setTimeout(() => { show.value = true; }, 0); return; } show.value = false; activeModel.value = false; }); watch(activeModel, (active) => { if (props.disabled) { activeModel.value = false; return; } if (!parentData) return; if (!active) { if (isActive.value) parentData.activateTab(void 0); return; } if (!isActive.value) parentData.activateTab(internalId.value); }); const computedClasses = computed(() => [ { "active": isActive.value, "show": show.value, "card-body": parentData?.card.value && props.noBody === false, "fade": !parentData?.noFade.value }, show.value ? parentData?.activeTabClass.value : parentData?.inactiveTabClass.value, parentData?.tabClass.value ]); __expose({ activate: () => { activeModel.value = true; }, deactivate: () => { activeModel.value = false; } }); return (_ctx, _cache) => { return openBlock(), createBlock(resolveDynamicComponent(unref(props).tag), mergeProps({ id: computedId.value, ref: "_el", class: ["tab-pane", computedClasses.value], role: "tabpanel", "aria-labelledby": unref(computedButtonId) }, processedAttrs.value), { default: withCtx(() => [showSlot.value ? renderSlot(_ctx.$slots, "default", { key: 0 }) : createCommentVNode("", true)]), _: 3 }, 16, [ "id", "class", "aria-labelledby" ]); }; } }); //#endregion //#region src/components/BTabs/BTabs.vue?vue&type=script&setup=true&lang.ts var _hoisted_1 = ["aria-orientation"]; var _hoisted_2 = [ "id", "aria-controls", "aria-selected", "disabled", "tabindex", "onClick" ]; //#endregion //#region src/components/BTabs/BTabs.vue var BTabs_default = /* @__PURE__ */ defineComponent({ __name: "BTabs", props: /* @__PURE__ */ mergeModels({ activeNavItemClass: { default: void 0 }, activeTabClass: { default: void 0 }, align: { default: void 0 }, card: { type: Boolean, default: false }, contentClass: { default: void 0 }, end: { type: Boolean, default: false }, fill: { type: Boolean, default: false }, id: { default: void 0 }, index: {}, inactiveNavItemClass: { default: void 0 }, inactiveTabClass: { default: void 0 }, justified: { type: Boolean, default: false }, lazy: { type: Boolean, default: false }, navClass: { default: void 0 }, navItemClass: { default: void 0 }, navWrapperClass: { default: void 0 }, noFade: { type: Boolean, default: false }, noKeyNav: { type: Boolean, default: false }, noNavStyle: { type: Boolean, default: false }, pills: { type: Boolean, default: false }, small: { type: Boolean, default: false }, tag: { default: "div" }, tabClass: { default: void 0 }, underline: { type: Boolean, default: false }, vertical: { type: Boolean, default: false } }, { "index": { default: -1 }, "indexModifiers": {}, "modelValue": { default: void 0 }, "modelModifiers": {} }), emits: /* @__PURE__ */ mergeModels(["activate-tab"], ["update:index", "update:modelValue"]), setup(__props, { emit: __emit }) { const props = useDefaults(__props, "BTabs"); const emit = __emit; const slots = useSlots(); const activeIndex = useModel(__props, "index"); const activeId = useModel(__props, "modelValue"); const ReusableEmptyTab = createReusableTemplate(); const tabsInternal = ref([]); const tabElementsArray = ref([]); const isChildActive = ref(false); const initialIds = ref([]); const hasExplicitIds = ref(false); const updateTabElementsArray = () => { const tabElements = flattenFragments(slots.default?.({}) ?? []); tabElementsArray.value = (Array.isArray(tabElements) ? tabElements : [tabElements]).filter((tab) => tab.type === BTab_default); if (initialIds.value.length === 0) { initialIds.value = tabElementsArray.value.map((tab) => unref(useId$1(() => tab.props?.id, "tabpane"))); hasExplicitIds.value = tabElementsArray.value.some((tab) => tab.props?.id !== void 0); } isChildActive.value = tabElementsArray.value.some((tab) => tab.props?.active !== void 0 && tab.props?.active !== false); }; updateTabElementsArray(); watch(() => slots.default?.({}), () => { updateTabElementsArray(); nextTick(() => { sortTabs(); }); }); const tabs = computed(() => { if (tabsInternal.value.length === 0) { const _activeIndex = tabElementsArray.value.findIndex((tab) => tab.props?.active !== void 0 && (tab.props.disabled === false || tab.props.disabled === void 0) || activeId.value && tab.props?.id === activeId.value); return tabElementsArray.value.map((tab, index) => { const active = _activeIndex !== -1 ? index === _activeIndex : activeIndex.value > -1 ? index === activeIndex.value : index === 0; return { id: tab.props?.id ?? initialIds.value[index], internalId: `premount-${index}`, buttonId: tab.props?.buttonId, disabled: tab.props?.disabled, title: tab.props?.title, titleComponent: tab.children?.title, titleItemClass: tab.props?.titleItemClass, titleLinkAttrs: tab.props?.titleLinkAttrs, titleLinkClass: tab.props?.titleLinkClass, onClick: tab.props?.onClick, active, navItemClasses: [ { active, disabled: !(tab.props?.disabled === false || tab.props?.disabled === void 0) }, active ? props.activeNavItemClass : props.inactiveNavItemClass, props.navItemClass ] }; }); } return tabsInternal.value.map((_tab) => { const tab = unref(_tab); const active = tab.id === activeId.value; return { ...tab, active, navItemClasses: [ { active, disabled: tab.disabled }, active ? props.activeNavItemClass : props.inactiveNavItemClass, props.navItemClass ] }; }); }); let initialized = false; let updateInitialActiveIndex = false; let updateInitialActiveId = false; let delayedTabSelection = false; if (activeIndex.value > -1 && !activeId.value && !hasExplicitIds.value && tabElementsArray.value.length > 0) { delayedTabSelection = true; updateInitialActiveId = true; } else if (activeIndex.value === -1 && activeId.value) if (tabs.value.findIndex((t) => t.id === activeId.value) !== -1) activeIndex.value = tabs.value.findIndex((t) => t.id === activeId.value); else updateInitialActiveIndex = true; else if (activeIndex.value > -1 && !activeId.value) if (tabs.value[activeIndex.value]?.id) activeId.value = tabs.value[activeIndex.value]?.id; else updateInitialActiveId = true; else if (activeIndex.value === -1 && !activeId.value && !isChildActive.value) { activeIndex.value = tabs.value.findIndex((t) => t.disabled === void 0 || t.disabled === false); activeId.value = tabs.value[activeIndex.value]?.id; } else if (activeIndex.value === -1 && !activeId.value && isChildActive.value) { activeIndex.value = tabs.value.findIndex((t) => t.active !== void 0 && t.active !== false && (t.disabled === void 0 || t.disabled === false)); activeId.value = tabs.value[activeIndex.value]?.id; } function updateInitialIndexAndId() { if (updateInitialActiveIndex) { const index = tabs.value.findIndex((t) => t.id === activeId.value); if (index !== -1) nextTick(() => { activeIndex.value = index; updateInitialActiveIndex = false; }); } if (updateInitialActiveId) { if (delayedTabSelection && tabsInternal.value.length === 0) return; if (activeIndex.value > -1 && tabs.value[activeIndex.value]?.id) nextTick(() => { activeId.value = tabs.value[activeIndex.value]?.id; updateInitialActiveId = false; delayedTabSelection = false; }); } } updateInitialIndexAndId(); const showEmpty = computed(() => !(tabs?.value && tabs.value.length > 0)); const computedClasses = computed(() => ({ "d-flex": props.vertical, "align-items-start": props.vertical })); const alignment = useAlignment(() => props.align); const navTabsClasses = computed(() => ({ "nav-pills": props.pills, "nav-underline": props.underline, "flex-column me-3": props.vertical, [alignment.value]: props.align !== void 0, "nav-fill": props.fill, "card-header-tabs": props.card && !props.pills && !props.underline, "card-header-pills": props.card && props.pills, "nav-justified": props.justified, "nav-tabs": !props.noNavStyle && !props.pills && !props.underline, "small": props.small })); const handleClick = (event, index) => { if (index >= 0 && !tabs.value[index]?.disabled && tabs.value[index]?.onClick && typeof tabs.value[index].onClick === "function") { tabs.value[index].onClick?.(event); if (event.defaultPrevented) { getSafeDocument()?.getElementById(tabs.value[index].buttonId)?.blur(); return; } } activeIndex.value = index; }; const keynav = (e, direction) => { if (tabs.value.length <= 0 || props.noKeyNav) return; e.preventDefault(); e.stopPropagation(); activeIndex.value = nextIndex(activeIndex.value + direction, direction); nextTick(() => { if (activeIndex.value >= 0) getSafeDocument()?.getElementById(tabs.value[activeIndex.value]?.buttonId)?.focus(); }); }; const nextIndex = (start, direction) => { let index = start; let minIdx = -1; let maxIdx = -1; for (let i = 0; i < tabs.value.length; i++) if (!tabs.value[i]?.disabled) { if (minIdx === -1) minIdx = i; maxIdx = i; } while (index >= minIdx && index <= maxIdx && tabs.value[index]?.disabled) index += direction; if (index < minIdx) index = minIdx; if (index > maxIdx) index = maxIdx; return index; }; let previousIndex; let isReverting = false; watch(activeIndex, (newValue, oldValue) => { if (tabs.value.length <= 0 || tabs.value.filter((t) => !t.disabled).length <= 0) return; if (isReverting) { isReverting = false; return; } const index = nextIndex(newValue, newValue > oldValue ? 1 : -1); if (index !== newValue) { previousIndex = oldValue; activeIndex.value = index; return; } const tabEvent = new BvEvent("activate-tab", { cancelable: true }); emit("activate-tab", { newTabId: tabs.value[index]?.id, prevTabId: tabs.value[previousIndex ?? oldValue]?.id, newTabIndex: index, prevTabIndex: previousIndex ?? oldValue, event: tabEvent }); if (tabEvent.defaultPrevented) { isReverting = true; const prev = previousIndex ?? oldValue ?? nextIndex(0, 1); previousIndex = void 0; if (activeId.value !== tabs.value[prev]?.id) activeId.value = tabs.value[prev]?.id; nextTick(() => { if (prev >= 0) getSafeDocument()?.getElementById(tabs.value[prev]?.buttonId)?.focus(); }); return; } if (activeId.value !== tabs.value[index]?.id) activeId.value = tabs.value[index]?.id; previousIndex = void 0; }); watch(activeId, (newValue, oldValue) => { if (tabs.value.length <= 0 || tabs.value.filter((t) => !t.disabled).length <= 0) return; const index = tabs.value.findIndex((t) => t.id === newValue); if (index === activeIndex.value) return; const oldIndex = tabs.value.findIndex((t) => t.id === oldValue); if (tabs.value[index]?.disabled) { activeIndex.value = nextIndex(index, index > oldIndex ? 1 : -1); return; } if (index === -1) { activeIndex.value = nextIndex(0, 1); nextTick(() => { activeId.value = tabs.value[activeIndex.value]?.id; }); return; } activeIndex.value = index; }); const registerTab = (tab) => { const idx = tabsInternal.value.findIndex((t) => t.value.internalId === tab.value.internalId); if (idx === -1) { tabsInternal.value.push(tab); if (initialized) nextTick(() => { sortTabs(); }); else if (delayedTabSelection) nextTick(() => { updateInitialIndexAndId(); }); } else { tabsInternal.value[idx] = tab; if (initialized) sortTabs(); } const idx2 = tabsInternal.value.findIndex((t) => t.value.internalId === tab.value.internalId); return tab.value.id ?? (!initialized ? initialIds.value[idx2] : tab.value.internalId); }; onMounted(() => { updateInitialIndexAndId(); sortTabs(); initialized = true; }); const sortTabs = () => { tabsInternal.value.sort((a, b) => sortSlotElementsByPosition(a.value.el.value, b.value.el.value)); if (activeId.value && activeIndex.value !== tabs.value.findIndex((t) => t.id === activeId.value)) activeIndex.value = tabs.value.findIndex((t) => t.id === activeId.value); }; const unregisterTab = (id) => { tabsInternal.value = tabsInternal.value.filter((t) => t.value.internalId !== id); }; provide(tabsInjectionKey, { lazy: toRef(() => props.lazy), card: toRef(() => props.card), noFade: toRef(() => props.noFade), activeTabClass: toRef(() => props.activeTabClass), inactiveTabClass: toRef(() => props.inactiveTabClass), tabClass: toRef(() => props.tabClass), registerTab, unregisterTab, activeId, activateTab: (internalId) => { const idx = tabs.value.findIndex((t) => t.internalId === internalId); if (internalId === void 0 || idx === -1) { activeIndex.value = nextIndex(0, 1); return; } activeIndex.value = idx; } }); return (_ctx, _cache) => { return openBlock(), createBlock(resolveDynamicComponent(unref(props).tag), { id: unref(props).id, class: normalizeClass(["tabs", computedClasses.value]) }, { default: withCtx(() => [ createVNode(unref(ReusableEmptyTab).define, null, { default: withCtx(() => [createElementVNode("div", { class: normalizeClass(["tab-content", unref(props).contentClass]) }, [renderSlot(_ctx.$slots, "default"), showEmpty.value ? (openBlock(), createElementBlock("div", { key: "bv-empty-tab", class: normalizeClass(["tab-pane active", { "card-body": unref(props).card }]) }, [renderSlot(_ctx.$slots, "empty")], 2)) : createCommentVNode("", true)], 2)]), _: 3 }), unref(props).end ? (openBlock(), createBlock(unref(ReusableEmptyTab).reuse, { key: 0 })) : createCommentVNode("", true), createElementVNode("div", { class: normalizeClass([unref(props).navWrapperClass, { "card-header": unref(props).card, "ms-auto": __props.vertical && unref(props).end }]) }, [createElementVNode("ul", { class: normalizeClass(["nav", [navTabsClasses.value, unref(props).navClass]]), role: "tablist", "aria-orientation": unref(props).vertical ? "vertical" : "horizontal" }, [ renderSlot(_ctx.$slots, "tabs-start"), (openBlock(true), createElementBlock(Fragment, null, renderList(tabs.value, (tab, idx) => { return openBlock(), createElementBlock("li", { key: tab.id ?? tab.internalId, class: normalizeClass(["nav-item", tab.titleItemClass]), role: "presentation" }, [createElementVNode("button", mergeProps({ id: tab.buttonId, class: ["nav-link", [tab.navItemClasses, tab.titleLinkClass]], role: "tab", "aria-controls": tab.id, "aria-selected": tab.active, disabled: tab.disabled, tabindex: unref(props).noKeyNav ? void 0 : tab.active ? void 0 : -1, type: "button" }, { ref_for: true }, tab.titleLinkAttrs, { onKeydown: [ _cache[0] || (_cache[0] = withKeys(withModifiers(($event) => !unref(props).vertical && keynav($event, -1), ["exact"]), ["left"])), _cache[1] || (_cache[1] = withKeys(withModifiers(($event) => !unref(props).vertical && keynav($event, -999), ["shift"]), ["left"])), _cache[2] || (_cache[2] = withKeys(withModifiers(($event) => unref(props).vertical && keynav($event, -1), ["exact"]), ["up"])), _cache[3] || (_cache[3] = withKeys(withModifiers(($event) => unref(props).vertical && keynav($event, -999), ["shift"]), ["up"])), _cache[4] || (_cache[4] = withKeys(withModifiers(($event) => !unref(props).vertical && keynav($event, 1), ["exact"]), ["right"])), _cache[5] || (_cache[5] = withKeys(withModifiers(($event) => !unref(props).vertical && keynav($event, 999), ["shift"]), ["right"])), _cache[6] || (_cache[6] = withKeys(withModifiers(($event) => unref(props).vertical && keynav($event, 1), ["exact"]), ["down"])), _cache[7] || (_cache[7] = withKeys(withModifiers(($event) => unref(props).vertical && keynav($event, 999), ["shift"]), ["down"])), _cache[8] || (_cache[8] = withKeys(($event) => keynav($event, -999), ["page-up"])), _cache[9] || (_cache[9] = withKeys(($event) => keynav($event, 999), ["page-down"])), _cache[10] || (_cache[10] = withKeys(($event) => keynav($event, -999), ["home"])), _cache[11] || (_cache[11] = withKeys(($event) => keynav($event, 999), ["end"])) ], onClick: withModifiers((e) => handleClick(e, idx), ["stop"]) }), [tab.titleComponent ? (openBlock(), createBlock(resolveDynamicComponent(tab.titleComponent), { key: 0 })) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(tab.title), 1)], 64))], 16, _hoisted_2)], 2); }), 128)), renderSlot(_ctx.$slots, "tabs-end") ], 10, _hoisted_1)], 2), !unref(props).end ? (openBlock(), createBlock(unref(ReusableEmptyTab).reuse, { key: 1 })) : createCommentVNode("", true) ]), _: 3 }, 8, ["id", "class"]); }; } }); //#endregion export { BTab_default as n, BTabs_default as t }; //# sourceMappingURL=BTabs-7IT0dhVr.mjs.map