vitepress-plugin-tabs
Version:
A plugin that adds syntax for showing content in tabs.
97 lines (86 loc) • 2.44 kB
text/typescript
import { ref, computed, reactive, inject, watch, onMounted } from 'vue'
import type { App, Ref, InjectionKey } from 'vue'
type TabsSharedState = {
content?: TabsSharedStateContent
}
type TabsSharedStateContent = Record<string, string>
const injectionKey: InjectionKey<TabsSharedState> =
'vitepress:tabSharedState' as unknown as symbol
const ls = typeof localStorage !== 'undefined' ? localStorage : null
const localStorageKey = 'vitepress:tabsSharedState'
const getLocalStorageValue = (): TabsSharedStateContent => {
const rawValue = ls?.getItem(localStorageKey)
if (rawValue) {
try {
return JSON.parse(rawValue)
} catch {}
}
return {}
}
const setLocalStorageValue = (v: TabsSharedStateContent) => {
if (!ls) return
ls.setItem(localStorageKey, JSON.stringify(v))
}
export const provideTabsSharedState = (app: App) => {
const state = reactive<TabsSharedState>({})
watch(
() => state.content,
(newStateContent, oldStateContent) => {
// skip initialize
if (newStateContent && oldStateContent) {
setLocalStorageValue(newStateContent)
}
},
{ deep: true }
)
app.provide(injectionKey, state)
}
export const useTabsSelectedState = <T extends string>(
acceptValues: Ref<T[]>,
sharedStateKey: Ref<string | undefined>
) => {
const sharedState = inject(injectionKey)
if (!sharedState) {
throw new Error(
'[vitepress-plugin-tabs] TabsSharedState should be injected'
)
}
onMounted(() => {
if (!sharedState.content) {
sharedState.content = getLocalStorageValue()
}
})
const nonSharedState = ref<T | undefined>()
const selected = computed({
get() {
const key = sharedStateKey.value
const acceptVals = acceptValues.value
if (key) {
const value = sharedState.content?.[key]
if (value && (acceptVals as string[]).includes(value)) {
return value as T
}
} 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: T) => {
selected.value = newValue
}
return { selected, select }
}