2025-08-11 00:42:43 +08:00

181 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { ComputedRef, Ref } from 'vue'
import { onClickOutside, useDebounceFn, useEventListener, useLocalStorage } from '@vueuse/core'
import { computed, onMounted, readonly, ref, watch } from 'vue'
interface Feature {
label: string
value: string
}
interface SelectItem {
label: string
value: string
}
const api = 'https://caniuse.pengzhanbo.cn/features.json'
const pastVersions: SelectItem[] = [
{ label: '不显示旧版本', value: '0' },
...Array.from({ length: 5 }).fill(0).map((_, i) => ({
label: `旧版本(当前版本 - ${i + 1}`,
value: `${i + 1}`,
})),
]
const futureVersions: SelectItem[] = [
{ label: '不显示未来版本', value: '0' },
...Array.from({ length: 3 }).fill(0).map((_, i) => ({
label: `未来版本(当前版本 + ${i + 1}`,
value: `${i + 1}`,
})),
]
const embedTypes: SelectItem[] = [
{ label: 'iframe', value: '' },
{ label: 'image', value: 'image' },
]
export function useCaniuseVersionSelect(): {
past: Ref<string>
future: Ref<string>
embedType: Ref<string>
pastList: Readonly<SelectItem[]>
futureList: Readonly<SelectItem[]>
embedTypeList: Readonly<SelectItem[]>
} {
const past = ref('2')
const future = ref('1')
const embedType = ref('')
const pastList = readonly(pastVersions)
const futureList = readonly(futureVersions)
const embedTypeList = readonly(embedTypes)
return {
past,
future,
pastList,
futureList,
embedType,
embedTypeList,
}
}
export function useCaniuseFeaturesSearch(
inputEl: Ref<HTMLInputElement | null>,
listEl: Ref<HTMLUListElement | null>,
): {
featureList: Ref<Feature[] | undefined>
isFocus: Ref<boolean>
feature: ComputedRef<string>
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<Feature[]>()
const selected = ref<Feature | null>(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<string>
embedType: Ref<string>
past: Ref<string>
future: Ref<string>
}): {
output: ComputedRef<string>
rendered: ComputedRef<string>
} {
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 `<p><picture>
<source type="image/webp" srcset="${link}${feature}.webp">
<source type="image/png" srcset="${link}${feature}.png">
<img src="${link}${feature}.jpg" alt="${alt}" width="100%">
</picture></p>`
}