UNPKG

vitepress-plugin-tabs

Version:

A plugin that adds syntax for showing content in tabs.

206 lines (205 loc) 7.33 kB
import { computed, defineComponent, inject, mergeProps, nextTick, onBeforeMount, onMounted, onUnmounted, provide, reactive, ref, toRef, unref, useId, useSSRContext, useSlots, watch } from "vue"; import { ssrInterpolate, ssrRenderAttr, ssrRenderAttrs, ssrRenderList, ssrRenderSlot } from "vue/server-renderer"; //#region src/client/useStabilizeScrollPosition.ts const useStabilizeScrollPosition = (targetEle) => { if (typeof document === "undefined") { const mock = (f) => async (...args) => f(...args); return { stabilizeScrollPosition: mock }; } const scrollableEleVal = document.documentElement; const stabilizeScrollPosition = (func) => async (...args) => { const result = func(...args); const eleVal = targetEle.value; if (!eleVal) return result; const offset = eleVal.offsetTop - scrollableEleVal.scrollTop; await nextTick(); scrollableEleVal.scrollTop = eleVal.offsetTop - offset; return result; }; return { stabilizeScrollPosition }; }; //#endregion //#region src/client/useTabsSelectedState.ts const injectionKey$1 = "vitepress:tabSharedState"; const ls = typeof localStorage !== "undefined" ? localStorage : null; const localStorageKey = "vitepress:tabsSharedState"; const getLocalStorageValue = () => { const rawValue = ls?.getItem(localStorageKey); if (rawValue) try { return JSON.parse(rawValue); } catch {} return {}; }; const setLocalStorageValue = (v) => { if (!ls) return; ls.setItem(localStorageKey, JSON.stringify(v)); }; const provideTabsSharedState = (app) => { const state = reactive({}); watch(() => state.content, (newStateContent, oldStateContent) => { if (newStateContent && oldStateContent) setLocalStorageValue(newStateContent); }, { deep: true }); app.provide(injectionKey$1, state); }; const useTabsSelectedState = (acceptValues, sharedStateKey) => { const sharedState = inject(injectionKey$1); if (!sharedState) throw new Error("[vitepress-plugin-tabs] TabsSharedState should be injected"); onMounted(() => { if (!sharedState.content) sharedState.content = getLocalStorageValue(); }); const nonSharedState = ref(); const selected = computed({ get() { const key = sharedStateKey.value; const acceptVals = acceptValues.value; if (key) { const value = sharedState.content?.[key]; if (value && acceptVals.includes(value)) return value; } else { const nonSharedStateVal = nonSharedState.value; if (nonSharedStateVal) return nonSharedStateVal; } return acceptVals[0]; }, set(v) { const key = sharedStateKey.value; if (key) { if (sharedState.content) sharedState.content[key] = v; } else nonSharedState.value = v; } }); const select = (newValue) => { selected.value = newValue; }; return { selected, select }; }; //#endregion //#region src/client/useTabLabels.ts function useTabLabels() { const slots = useSlots(); return computed(() => { const defaultSlot = slots.default?.(); if (!defaultSlot) return []; return defaultSlot.filter((vnode) => typeof vnode.type === "object" && "__name" in vnode.type && vnode.type.__name === "PluginTabsTab" && vnode.props).map((vnode) => vnode.props?.label); }); } //#endregion //#region src/client/useTabsSingleState.ts const injectionKey = "vitepress:tabSingleState"; const provideTabsSingleState = (state) => { provide(injectionKey, state); }; const useTabsSingleState = () => { const singleState = inject(injectionKey); if (!singleState) throw new Error("[vitepress-plugin-tabs] TabsSingleState should be injected"); return singleState; }; //#endregion //#region src/client/useIsPrint.ts const useIsPrint = () => { const matchMedia = typeof window !== "undefined" ? window.matchMedia("print") : void 0; const value = ref(matchMedia?.matches); const listener = () => { value.value = matchMedia?.matches; }; onBeforeMount(() => { matchMedia?.addEventListener("change", listener); }); onUnmounted(() => { matchMedia?.removeEventListener("change", listener); }); return value; }; //#endregion //#region src/client/PluginTabs.vue const _sfc_main$1 = /* @__PURE__ */ defineComponent({ __name: "PluginTabs", __ssrInlineRender: true, props: { sharedStateKey: {}, variant: {} }, setup(__props) { const props = __props; const isPrint = useIsPrint(); const tabLabels = useTabLabels(); const { selected, select } = useTabsSelectedState(tabLabels, toRef(props, "sharedStateKey")); const { stabilizeScrollPosition } = useStabilizeScrollPosition(ref()); stabilizeScrollPosition(select); ref([]); const uid = useId(); provideTabsSingleState({ uid, selected }); return (_ctx, _push, _parent, _attrs) => { _push(`<div${ssrRenderAttrs(mergeProps({ class: "plugin-tabs", "data-variant": props.variant }, _attrs))}><div class="plugin-tabs--tab-list" role="tablist"><!--[-->`); ssrRenderList(unref(tabLabels), (tabLabel) => { _push(`<button${ssrRenderAttr("id", `tab-${tabLabel}-${unref(uid)}`)} role="tab" class="plugin-tabs--tab"${ssrRenderAttr("aria-selected", tabLabel === unref(selected) && !unref(isPrint))}${ssrRenderAttr("aria-controls", `panel-${tabLabel}-${unref(uid)}`)}${ssrRenderAttr("tabindex", tabLabel === unref(selected) ? 0 : -1)}>${ssrInterpolate(tabLabel)}</button>`); }); _push(`<!--]--></div>`); ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent); _push(`</div>`); }; } }); const _sfc_setup$1 = _sfc_main$1.setup; _sfc_main$1.setup = (props, ctx) => { const ssrContext = useSSRContext(); (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/client/PluginTabs.vue"); return _sfc_setup$1 ? _sfc_setup$1(props, ctx) : void 0; }; //#endregion //#region \0/plugin-vue/export-helper var export_helper_default = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) target[key] = val; return target; }; //#endregion //#region src/client/PluginTabsTab.vue const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "PluginTabsTab", __ssrInlineRender: true, props: { label: {} }, setup(__props) { const { uid, selected } = useTabsSingleState(); const isPrint = useIsPrint(); return (_ctx, _push, _parent, _attrs) => { if (unref(selected) === __props.label || unref(isPrint)) { _push(`<div${ssrRenderAttrs(mergeProps({ id: `panel-${__props.label}-${unref(uid)}`, class: "plugin-tabs--content", role: "tabpanel", tabindex: "0", "aria-labelledby": `tab-${__props.label}-${unref(uid)}`, "data-is-print": unref(isPrint) }, _attrs))} data-v-9f355b7c>`); ssrRenderSlot(_ctx.$slots, "default", {}, null, _push, _parent); _push(`</div>`); } else _push(`<!---->`); }; } }); const _sfc_setup = _sfc_main.setup; _sfc_main.setup = (props, ctx) => { const ssrContext = useSSRContext(); (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/client/PluginTabsTab.vue"); return _sfc_setup ? _sfc_setup(props, ctx) : void 0; }; var PluginTabsTab_default = /* @__PURE__ */ export_helper_default(_sfc_main, [["__scopeId", "data-v-9f355b7c"]]); //#endregion //#region src/client/index.ts const enhanceAppWithTabs = (app) => { provideTabsSharedState(app); app.component("PluginTabs", _sfc_main$1); app.component("PluginTabsTab", PluginTabsTab_default); }; //#endregion export { enhanceAppWithTabs };