docs: update docs

This commit is contained in:
pengzhanbo 2024-04-29 00:37:25 +08:00
parent 536ed6110a
commit b600e20ba9
12 changed files with 272 additions and 181 deletions

View File

@ -29,13 +29,13 @@ export const zhNotes = definePlumeNotesConfig({
text: '代码块',
dir: '代码',
icon: 'ph:code-bold',
items: ['介绍', '特性支持', '代码组', '导入代码', 'codepen', 'jsFiddle', 'codeSandbox', 'replit', 'twoslash', '代码演示'],
items: ['介绍', '特性支持', '代码组', '导入代码', 'twoslash'],
},
{
text: '代码演示',
dir: '代码演示',
icon: 'carbon:demo',
items: ['前端', 'rust', 'golang', 'kotlin'],
items: ['前端', 'rust', 'golang', 'kotlin', 'codepen', 'jsFiddle', 'codeSandbox', 'replit'],
},
{
text: '图表',

View File

@ -1,101 +1,14 @@
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import { onClickOutside, useLocalStorage, useThrottleFn } from '@vueuse/core'
import { resolveCanIUse } from '../composables/caniuse.js'
import { shallowRef } from 'vue'
import { useCaniuse, useCaniuseFeaturesSearch, useCaniuseVersionSelect } from '../composables/caniuse.js'
import CodeViewer from './CodeViewer.vue'
interface Feature {
label: string
value: string
}
const listEl = shallowRef<HTMLUListElement | null>(null)
const inputEl = shallowRef<HTMLInputElement | null>(null)
const api = 'https://api.pengzhanbo.cn/caniuse/features'
const features = useLocalStorage('caniuse-features', [] as Feature[])
onMounted(async () => {
const res = await fetch(api)
const data = await res.json()
features.value = data || features.value || []
})
const browserVersionList = ref([
{ label: '新版本(当前版本 + 3', value: 3, checked: false },
{ label: '新版本(当前版本 + 2', value: 2, checked: false },
{ label: '新版本(当前版本 + 1', value: 1, checked: false },
{ label: '当前版本', value: 0, disabled: true, checked: true },
{ label: '旧版本(当前版本 - 1', value: -1, checked: false },
{ label: '旧版本(当前版本 - 2', value: -2, checked: false },
{ label: '旧版本(当前版本 - 3', value: -3, checked: false },
{ label: '旧版本(当前版本 - 4', value: -4, checked: false },
{ label: '旧版本(当前版本 - 5', value: -5, checked: false },
])
const input = ref('')
const isFocus = ref(false)
const searched = ref<Feature[]>()
const selected = ref<Feature | null>(null)
const embedType = ref('')
const browserVersion = computed(() => {
const values = browserVersionList.value.filter(item => item.checked).map(item => item.value)
if (values.length === 1 && values[0] === 0)
return ''
return values.join(',')
})
watch(() => [features.value, isFocus.value], () => {
if (!isFocus.value)
searched.value = features.value
}, { immediate: true })
const listEl = ref<HTMLUListElement | null>(null)
const inputEl = ref<HTMLInputElement | null>(null)
onClickOutside(listEl, () => {
isFocus.value = false
}, { ignore: [inputEl] })
const onInput = useThrottleFn(() => {
selected.value = null
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]
}
}, 300)
function onSelect(item: Feature) {
selected.value = item
input.value = item.label
isFocus.value = false
}
const output = computed(() => {
let content = '@[caniuse'
if (embedType.value)
content += ` ${embedType.value}`
if (browserVersion.value && !embedType.value)
content += `{${browserVersion.value}}`
content += ']('
if (selected.value)
content += selected.value.value
return `${content})`
})
const rendered = ref('')
function render() {
if (!selected.value)
return
rendered.value = resolveCanIUse(selected.value.value, embedType.value, browserVersion.value)
}
const { feature, featureList, onSelect, isFocus } = useCaniuseFeaturesSearch(inputEl, listEl)
const { past, pastList, future, futureList, embedType, embedTypeList } = useCaniuseVersionSelect()
const { output, rendered } = useCaniuse({ feature, embedType, past, future })
</script>
<template>
@ -105,12 +18,19 @@ function render() {
<label for="feature">选择特性</label>
<div class="feature-input">
<input
ref="inputEl" v-model="input" class="feature-input__input" type="text" name="feature"
placeholder="输入特性" @focus="isFocus = true" @input="onInput"
ref="inputEl"
class="feature-input__input"
type="text"
name="feature"
placeholder="输入特性"
>
<span class="vpi-chevron-down" />
<ul v-show="isFocus" ref="listEl" class="feature-list">
<li v-for="item in searched" :key="item.value" @click="onSelect(item)">
<li
v-for="item in featureList"
:key="item.value"
@click="onSelect(item)"
>
{{ item.label }}
</li>
</ul>
@ -119,41 +39,35 @@ function render() {
<div class="caniuse-form-item">
<label for="embedType">嵌入方式</label>
<div class="caniuse-embed-type">
<label>
<input type="radio" name="embedType" value="" :checked="embedType === ''" @click="embedType = ''">
<span>iframe</span>
</label>
<label>
<input
type="radio" name="embedType" value="image" :checked="embedType === 'image'"
@click="embedType = 'image'"
> <span>image</span>
</label>
</div>
</div>
<div v-if="!embedType" class="caniuse-form-item">
<label for="browserVersion">浏览器版本</label>
<div class="caniuse-browser-version">
<label v-for="item in browserVersionList" :key="item.value">
<input
v-model="item.checked" type="checkbox" name="browserVersion" :checked="item.checked"
:disabled="item.disabled"
>
<label v-for="item in embedTypeList" :key="item.label">
<input v-model="embedType" type="radio" name="embedType" :value="item.value">
<span>{{ item.label }}</span>
</label>
</div>
</div>
<div class="caniuse-render">
<button class="caniuse-render-button" type="button" :disabled="!selected" @click="render">
生成预览
</button>
<div v-if="!embedType" class="caniuse-form-item">
<span>浏览器版本</span>
<div class="caniuse-browser-version">
<select v-model="past">
<option v-for="item in pastList" :key="item.value" :value="item.value">
{{ item.label }}
</option>
</select>
<span>-</span>
<select v-model="future">
<option v-for="item in futureList" :key="item.value" :value="item.value">
{{ item.label }}
</option>
</select>
</div>
</div>
</form>
<div class="caniuse-output">
<h4>输出</h4>
<CodeViewer lang="md" :content="output" />
</div>
<div v-html="rendered" />
<div v-if="embedType === 'image'" v-html="rendered" />
<CanIUseViewer v-else-if="feature" :feature="feature" :past="past" :future="future" />
</div>
</template>
@ -183,6 +97,7 @@ function render() {
.caniuse-form-item:nth-child(3) {
align-items: baseline;
margin-bottom: 0;
}
.feature-input {
@ -251,24 +166,40 @@ function render() {
.caniuse-browser-version {
flex: 1;
flex-wrap: wrap;
margin-left: 10px;
}
.caniuse-browser-version label {
display: block;
.caniuse-browser-version span {
display: none;
}
.caniuse-browser-version select {
flex: 1;
width: 100%;
cursor: pointer;
padding: 3px 16px;
background-color: var(--vp-c-bg);
border: solid 1px var(--vp-c-divider);
transition: border var(--t-color), background-color var(--t-color);
}
.caniuse-browser-version select:first-of-type {
margin-bottom: 16px;
}
@media (min-width: 768px) {
.caniuse-browser-version {
display: flex;
gap: 10px 0;
gap: 10px;
align-items: center;
justify-content: center;
}
.caniuse-browser-version label {
width: 50%;
.caniuse-browser-version span {
display: block;
}
.caniuse-browser-version select:first-of-type {
margin-bottom: 0;
}
}

View File

@ -1,46 +1,164 @@
export function resolveCanIUse(feature: string, mode: string, versions: string): string {
if (!feature)
return ''
import { type Ref, computed, onMounted, readonly, ref, watch } from 'vue'
import { onClickOutside, useDebounceFn, useEventListener, useLocalStorage } from '@vueuse/core'
if (mode === 'image') {
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>`
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(5).fill(0).map((_, i) => ({
label: `旧版本(当前版本 - ${i + 1}`,
value: `${i + 1}`,
})),
]
const futureVersions: SelectItem[] = [
{ label: '不显示未来版本', value: '0' },
...Array(3).fill(0).map((_, i) => ({
label: `未来版本(当前版本 + ${i + 1}`,
value: `${i + 1}`,
})),
]
const embedTypes: SelectItem[] = [
{ label: 'iframe', value: '' },
{ label: 'image', value: 'image' },
]
export function useCaniuseVersionSelect() {
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>,
) {
const features = useLocalStorage('caniuse-features', [] as Feature[])
const featuresUpdated = useLocalStorage('caniuse-features-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) {
selected.value = item
isFocus.value = false
if (inputEl.value)
inputEl.value.value = item.label
}
const periods = resolveVersions(versions)
const accessible = 'false'
const image = 'none'
const url = 'https://caniuse.bitsofco.de/embed/index.html'
const src = `${url}?feat=${feature}&periods=${periods}&accessible-colours=${accessible}&image-base=${image}`
return `<div class="ciu_embed" style="margin:16px 0" data-feature="${feature}"><iframe src="${src}" frameborder="0" width="100%" height="400px" title="Can I use ${feature}"></iframe></div>`
return {
featureList: searched,
isFocus,
onSelect,
feature: computed(() => selected.value?.value || ''),
}
}
function resolveVersions(versions: string): string {
if (!versions)
return 'future_1,current,past_1,past_2'
export function useCaniuse({ feature, embedType, past, future }: {
feature: Ref<string>
embedType: Ref<string>
past: Ref<string>
future: Ref<string>
}) {
const output = computed(() => {
let content = '@[caniuse'
if (embedType.value)
content += ` ${embedType.value}`
const list = versions
.split(',')
.map(v => Number(v.trim()))
.filter(v => !Number.isNaN(v) && v >= -5 && v <= 3)
if (past.value !== '-2' || future.value !== '1') {
if (past.value === '0' && future.value === '0')
content += '{0}'
else
content += `{-${past.value},${future.value}}`
}
list.push(0)
content += ']('
const uniq = [...new Set(list)].sort((a, b) => b - a)
const result: string[] = []
uniq.forEach((v) => {
if (v < 0)
result.push(`past_${Math.abs(v)}`)
if (v === 0)
result.push('current')
if (v > 0)
result.push(`future_${v}`)
if (feature.value)
content += feature.value
return `${content})`
})
return result.join(',')
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>`
}

View File

@ -5,6 +5,11 @@ createTime: 2024/03/11 17:22:52
permalink: /plugins/plugin-caniuse/
---
:::warning Deprecated
该插件功能已整合到 [vuepress-plugin-md-power](/plugins/plugin-md-power) 。
因此,此插件不再更新维护,并标记为 弃用。
:::
## 指南
为你的 vuepress 站点,在编写文章时, 提供嵌入 [can-i-use](https://caniuse.com/) WEB feature 各平台支持说明 的功能。

View File

@ -41,7 +41,8 @@ pnpm add vuepress-plugin-md-power
```ts
// .vuepress/config.ts
import { markdownPowerPlugin } from 'vuepress-plugin-md-power'
module.exports = {
export default {
// ...
plugins: [
markdownPowerPlugin({
@ -72,6 +73,7 @@ interface MarkdownPowerPluginOptions {
jsfiddle?: boolean
caniuse?: boolean | CanIUseOptions
repl?: boolean
}
```
@ -85,23 +87,23 @@ interface MarkdownPowerPluginOptions {
```md
@[caniuse](feature)
@[caniuse image](feature)
@[caniuse embed{versions}](feature)
@[caniuse image](feature) // 不再推荐使用
@[caniuse embed{versionRange}](feature)
```
你可以从 [caniuse](https://caniuse.bitsofco.de/) 获取 feature 的值。
你可以从 [caniuse](https://caniuse.pengzhanbo.cn/) 获取 feature 的值。
默认情况下,插件通过 `iframe` 嵌入 `caniuse` 的支持情况查看器。
你也可以使用 `@[caniuse image](feature)` 直接嵌入图片。
~~你也可以使用 `@[caniuse image](feature)` 直接嵌入图片。~~
caniuse 默认查看最近的5个浏览器版本。你可以通过 `{versions}` 手动设置查看的浏览器版本。
格式为 `{number,number,...}`。取值范围为 `-5 ~ 3`
caniuse 默认查看最近的5个浏览器版本。你可以通过 `{versionRange}` 手动设置查看的浏览器版本。
格式为 `{past,future}` 表示 `{过去版本,未来版本}`。取值范围为 `-5 ~ 3`
- 小于0 表示低于当前浏览器版本的支持情况
- 0 表示当前浏览器版本的支持情况
- 大于0 表示高于当前浏览器版本的支持情况
`{-2,-1,1,2}` 表示查看低于当前 2 个版本 到 高于当前 2 个版本的支持情况。
`{-2,2}` 表示查看低于当前 2 个版本 到 高于当前 2 个版本的支持情况。
### pdf
@ -280,3 +282,32 @@ pnpm add @iconify/json
- `tab`: 选项卡, 可选值:`"js" | "css" | "html" | "result"`, 多个用 `","` 分割,
顺序将决定选项卡的排序,默认为 `js,css,html,result`
- `height`: 高度
### Repl
插件默认不启用该功能,你需要手动设置 `repl``true`
提供在 markdown 中为 `golang``kotlin``rust` 语言的 在线代码演示 支持。
在线编译执行代码,并输出结果。
#### 语法
````md
::: go-repl
```go
// your go lang code
```
:::
::: kotlin-repl
```kotlin
// your kotlin code
```
:::
:::rust-repl
```rust
// your rust code
```
:::
````

View File

@ -31,6 +31,7 @@ export default defineUserConfig({
// codeSandbox: true, // @[codesandbox](id) 嵌入 CodeSandbox
// jsfiddle: true, // @[jsfiddle](id) 嵌入 jsfiddle
// caniuse: true, // @[caniuse](feature) 嵌入 caniuse
// repl: true, // :::go-repl :::kotlin-repl :::rust-repl
}
}
}),

View File

@ -431,15 +431,15 @@ export default defineUserConfig({
- `feature`
必填。 正确取值请参考 [https://caniuse.bitsofco.de/](https://caniuse.bitsofco.de/)
必填。 正确取值请参考 [caniuse-embed.vercel.app](https://caniuse-embed.vercel.app/zh-CN)
- `{browser_versions}`
可选。当前特性在多个版本中的支持情况。
默认值为: `{-2,-1,1}`
默认值为: `{-2,1}`
格式: `{number,number,...}` 取值范围为 `-5 ~ 3`
格式: `{past,future}` 取值范围为 `-5 ~ 3`
- 小于`0` 表示低于当前浏览器版本的支持情况
- `0` 表示当前浏览器版本的支持情况
@ -453,6 +453,11 @@ export default defineUserConfig({
默认值为:`'embed'`
:::caution
不再推荐使用 image 类型,建议使用 embed 类型,主题更换了 embed 实现技术方案,
现在的 embed 类型优势已远远超过 image 类型,加载速度更快,体积更小,交互体验更好。
:::
### 示例
**获取 css 伪类选择器 `:dir()` 在各个浏览器的支持情况:**
@ -478,12 +483,12 @@ export default defineUserConfig({
**获取 css 伪类选择器 `:dir()` 特定范围浏览器的支持情况:**
```md
@[caniuse{-2,-1,1,2,3}](css-matches-pseudo)
@[caniuse{-2,3}](css-matches-pseudo)
```
效果:
@[caniuse{-2,-1,1,2,3}](css-matches-pseudo)
@[caniuse{-2,3}](css-matches-pseudo)
## 导入文件

View File

@ -24,7 +24,7 @@
- 👀 支持 搜索、文章评论
- 👨‍💻‍ 支持 浅色/深色 主题 (包括代码高亮)
- 📠 markdown 增强,支持 代码块分组、提示容器、任务列表、数学公式、代码演示 等
- 📚 代码演示,支持 CodePen, Replit
- 📚 代码演示,支持 CodePen, Replit, JSFiddle, CodeSandbox 等
- 📊 嵌入图标,支持 chart.jsEchartsMermaidflowchart
- 🎛 资源嵌入,支持 PDF, bilibili视频youtube视频等