import type { ComputedRef, Ref } from 'vue' import type { LocaleConfig } from 'vuepress' import { onClickOutside, useDebounceFn, useEventListener, useLocalStorage } from '@vueuse/core' import { computed, onMounted, readonly, ref, watch } from 'vue' import { useRouteLocale } from 'vuepress/client' interface Feature { label: string value: string } interface SelectItem { label: string value: string } const api = 'https://caniuse.pengzhanbo.cn/features.json' const locales: LocaleConfig< Record<'past0' | 'pastIndex' | 'future0' | 'futureIndex', string> > = { '/': { past0: '不显示旧版本', pastIndex: '旧版本(当前版本 - {index})', future0: '不显示未来版本', futureIndex: '未来版本(当前版本 + {index})', }, '/en/': { past0: 'Do not show old versions', pastIndex: 'Old version (current version - {index})', future0: 'Do not show future versions', futureIndex: 'Future version (current version + {index})', }, } const embedTypes: SelectItem[] = [ { label: 'iframe', value: '' }, { label: 'image', value: 'image' }, ] export function useCaniuseVersionSelect(): { past: Ref future: Ref embedType: Ref pastList: ComputedRef futureList: ComputedRef embedTypeList: Readonly } { const routeLocale = useRouteLocale() const past = ref('2') const future = ref('1') const embedType = ref('') const pastList = computed(() => { return [ { label: locales[routeLocale.value].past0 || '', value: '0' }, ...Array.from({ length: 5 }).fill(0).map((_, i) => ({ label: locales[routeLocale.value]?.pastIndex?.replace('{index}', `${i + 1}`) ?? '', value: `${i + 1}`, })), ] }) const futureList = computed(() => { return [ { label: locales[routeLocale.value].future0 || '', value: '0' }, ...Array.from({ length: 3 }).fill(0).map((_, i) => ({ label: locales[routeLocale.value]?.futureIndex?.replace('{index}', `${i + 1}`) ?? '', value: `${i + 1}`, })), ] }) const embedTypeList = readonly(embedTypes) return { past, future, pastList, futureList, embedType, embedTypeList, } } export function useCaniuseFeaturesSearch( inputEl: Ref, listEl: Ref, ): { featureList: Ref isFocus: Ref feature: ComputedRef onSelect: (item: Feature) => void } { const features = useLocalStorage('plume:caniuse-feature-list', [] as Feature[]) const featuresUpdated = useLocalStorage('plume:caniuse-feature-list-updated', Date.now()) const maxAge = 1000 * 60 * 60 * 24 * 3 // 3 days onMounted(async () => { if (typeof document === 'undefined') return if (features.value.length && Date.now() - featuresUpdated.value < maxAge) return const data = await fetch(api).then(res => res.json()) features.value = data || features.value || [] }) const input = ref('') const isFocus = ref(false) const searched = ref() const selected = ref(null) watch(() => [features.value, isFocus.value], () => { if (!isFocus.value) searched.value = features.value }, { immediate: true }) onClickOutside(listEl, () => { isFocus.value = false }, { ignore: [inputEl] }) useEventListener(inputEl, 'input', useDebounceFn(() => { selected.value = null input.value = inputEl.value?.value || '' if (!input.value) { searched.value = features.value } else { searched.value = features.value.filter(item => item.label.includes(input.value) || item.value.includes(input.value)) if (searched.value.length === 1) selected.value = searched.value[0] } }, 500)) useEventListener(inputEl, 'focus', () => { isFocus.value = true }) function onSelect(item: Feature): void { selected.value = item isFocus.value = false if (inputEl.value) inputEl.value.value = item.label } return { featureList: searched, isFocus, onSelect, feature: computed(() => selected.value?.value || ''), } } export function useCaniuse({ feature, embedType, past, future }: { feature: Ref embedType: Ref past: Ref future: Ref }): { output: ComputedRef rendered: ComputedRef } { const output = computed(() => { let content = '@[caniuse' if (embedType.value) content += ` ${embedType.value}` if (past.value !== '-2' || future.value !== '1') { if (past.value === '0' && future.value === '0') content += '{0}' else content += `{-${past.value},${future.value}}` } content += '](' if (feature.value) content += feature.value return `${content})` }) const rendered = computed(() => { if (!feature.value || !embedType.value) return '' return resolveCanIUse(feature.value) }) return { output, rendered } } function resolveCanIUse(feature: string): string { const link = 'https://caniuse.bitsofco.de/image/' const alt = `Data on support for the ${feature} feature across the major browsers from caniuse.com` return `

${alt}

` }