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: '代码块', text: '代码块',
dir: '代码', dir: '代码',
icon: 'ph:code-bold', icon: 'ph:code-bold',
items: ['介绍', '特性支持', '代码组', '导入代码', 'codepen', 'jsFiddle', 'codeSandbox', 'replit', 'twoslash', '代码演示'], items: ['介绍', '特性支持', '代码组', '导入代码', 'twoslash'],
}, },
{ {
text: '代码演示', text: '代码演示',
dir: '代码演示', dir: '代码演示',
icon: 'carbon:demo', icon: 'carbon:demo',
items: ['前端', 'rust', 'golang', 'kotlin'], items: ['前端', 'rust', 'golang', 'kotlin', 'codepen', 'jsFiddle', 'codeSandbox', 'replit'],
}, },
{ {
text: '图表', text: '图表',

View File

@ -1,101 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue' import { shallowRef } from 'vue'
import { onClickOutside, useLocalStorage, useThrottleFn } from '@vueuse/core' import { useCaniuse, useCaniuseFeaturesSearch, useCaniuseVersionSelect } from '../composables/caniuse.js'
import { resolveCanIUse } from '../composables/caniuse.js'
import CodeViewer from './CodeViewer.vue' import CodeViewer from './CodeViewer.vue'
interface Feature { const listEl = shallowRef<HTMLUListElement | null>(null)
label: string const inputEl = shallowRef<HTMLInputElement | null>(null)
value: string
}
const api = 'https://api.pengzhanbo.cn/caniuse/features' const { feature, featureList, onSelect, isFocus } = useCaniuseFeaturesSearch(inputEl, listEl)
const { past, pastList, future, futureList, embedType, embedTypeList } = useCaniuseVersionSelect()
const features = useLocalStorage('caniuse-features', [] as Feature[]) const { output, rendered } = useCaniuse({ feature, embedType, past, future })
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)
}
</script> </script>
<template> <template>
@ -105,12 +18,19 @@ function render() {
<label for="feature">选择特性</label> <label for="feature">选择特性</label>
<div class="feature-input"> <div class="feature-input">
<input <input
ref="inputEl" v-model="input" class="feature-input__input" type="text" name="feature" ref="inputEl"
placeholder="输入特性" @focus="isFocus = true" @input="onInput" class="feature-input__input"
type="text"
name="feature"
placeholder="输入特性"
> >
<span class="vpi-chevron-down" /> <span class="vpi-chevron-down" />
<ul v-show="isFocus" ref="listEl" class="feature-list"> <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 }} {{ item.label }}
</li> </li>
</ul> </ul>
@ -119,41 +39,35 @@ function render() {
<div class="caniuse-form-item"> <div class="caniuse-form-item">
<label for="embedType">嵌入方式</label> <label for="embedType">嵌入方式</label>
<div class="caniuse-embed-type"> <div class="caniuse-embed-type">
<label> <label v-for="item in embedTypeList" :key="item.label">
<input type="radio" name="embedType" value="" :checked="embedType === ''" @click="embedType = ''"> <input v-model="embedType" type="radio" name="embedType" :value="item.value">
<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"
>
<span>{{ item.label }}</span> <span>{{ item.label }}</span>
</label> </label>
</div> </div>
</div> </div>
<div class="caniuse-render"> <div v-if="!embedType" class="caniuse-form-item">
<button class="caniuse-render-button" type="button" :disabled="!selected" @click="render"> <span>浏览器版本</span>
生成预览 <div class="caniuse-browser-version">
</button> <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> </div>
</form> </form>
<div class="caniuse-output"> <div class="caniuse-output">
<h4>输出</h4> <h4>输出</h4>
<CodeViewer lang="md" :content="output" /> <CodeViewer lang="md" :content="output" />
</div> </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> </div>
</template> </template>
@ -183,6 +97,7 @@ function render() {
.caniuse-form-item:nth-child(3) { .caniuse-form-item:nth-child(3) {
align-items: baseline; align-items: baseline;
margin-bottom: 0;
} }
.feature-input { .feature-input {
@ -251,24 +166,40 @@ function render() {
.caniuse-browser-version { .caniuse-browser-version {
flex: 1; flex: 1;
flex-wrap: wrap;
margin-left: 10px; margin-left: 10px;
} }
.caniuse-browser-version label { .caniuse-browser-version span {
display: block; display: none;
}
.caniuse-browser-version select {
flex: 1;
width: 100%; 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) { @media (min-width: 768px) {
.caniuse-browser-version { .caniuse-browser-version {
display: flex; display: flex;
gap: 10px 0; gap: 10px;
align-items: center;
justify-content: center;
} }
.caniuse-browser-version label { .caniuse-browser-version span {
width: 50%; 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 { import { type Ref, computed, onMounted, readonly, ref, watch } from 'vue'
if (!feature) import { onClickOutside, useDebounceFn, useEventListener, useLocalStorage } from '@vueuse/core'
return ''
if (mode === 'image') { interface Feature {
const link = 'https://caniuse.bitsofco.de/image/' label: string
const alt = `Data on support for the ${feature} feature across the major browsers from caniuse.com` value: string
return `<p><picture> }
<source type="image/webp" srcset="${link}${feature}.webp">
<source type="image/png" srcset="${link}${feature}.png"> interface SelectItem {
<img src="${link}${feature}.jpg" alt="${alt}" width="100%"> label: string
</picture></p>` 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) return {
const accessible = 'false' featureList: searched,
const image = 'none' isFocus,
const url = 'https://caniuse.bitsofco.de/embed/index.html' onSelect,
const src = `${url}?feat=${feature}&periods=${periods}&accessible-colours=${accessible}&image-base=${image}` feature: computed(() => selected.value?.value || ''),
}
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>`
} }
function resolveVersions(versions: string): string { export function useCaniuse({ feature, embedType, past, future }: {
if (!versions) feature: Ref<string>
return 'future_1,current,past_1,past_2' embedType: Ref<string>
past: Ref<string>
future: Ref<string>
}) {
const output = computed(() => {
let content = '@[caniuse'
if (embedType.value)
content += ` ${embedType.value}`
const list = versions if (past.value !== '-2' || future.value !== '1') {
.split(',') if (past.value === '0' && future.value === '0')
.map(v => Number(v.trim())) content += '{0}'
.filter(v => !Number.isNaN(v) && v >= -5 && v <= 3) else
content += `{-${past.value},${future.value}}`
}
list.push(0) content += ']('
const uniq = [...new Set(list)].sort((a, b) => b - a) if (feature.value)
const result: string[] = [] content += feature.value
uniq.forEach((v) => {
if (v < 0) return `${content})`
result.push(`past_${Math.abs(v)}`)
if (v === 0)
result.push('current')
if (v > 0)
result.push(`future_${v}`)
}) })
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/ permalink: /plugins/plugin-caniuse/
--- ---
:::warning Deprecated
该插件功能已整合到 [vuepress-plugin-md-power](/plugins/plugin-md-power) 。
因此,此插件不再更新维护,并标记为 弃用。
:::
## 指南 ## 指南
为你的 vuepress 站点,在编写文章时, 提供嵌入 [can-i-use](https://caniuse.com/) WEB feature 各平台支持说明 的功能。 为你的 vuepress 站点,在编写文章时, 提供嵌入 [can-i-use](https://caniuse.com/) WEB feature 各平台支持说明 的功能。

View File

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

View File

@ -431,15 +431,15 @@ export default defineUserConfig({
- `feature` - `feature`
必填。 正确取值请参考 [https://caniuse.bitsofco.de/](https://caniuse.bitsofco.de/) 必填。 正确取值请参考 [caniuse-embed.vercel.app](https://caniuse-embed.vercel.app/zh-CN)
- `{browser_versions}` - `{browser_versions}`
可选。当前特性在多个版本中的支持情况。 可选。当前特性在多个版本中的支持情况。
默认值为: `{-2,-1,1}` 默认值为: `{-2,1}`
格式: `{number,number,...}` 取值范围为 `-5 ~ 3` 格式: `{past,future}` 取值范围为 `-5 ~ 3`
- 小于`0` 表示低于当前浏览器版本的支持情况 - 小于`0` 表示低于当前浏览器版本的支持情况
- `0` 表示当前浏览器版本的支持情况 - `0` 表示当前浏览器版本的支持情况
@ -453,6 +453,11 @@ export default defineUserConfig({
默认值为:`'embed'` 默认值为:`'embed'`
:::caution
不再推荐使用 image 类型,建议使用 embed 类型,主题更换了 embed 实现技术方案,
现在的 embed 类型优势已远远超过 image 类型,加载速度更快,体积更小,交互体验更好。
:::
### 示例 ### 示例
**获取 css 伪类选择器 `:dir()` 在各个浏览器的支持情况:** **获取 css 伪类选择器 `:dir()` 在各个浏览器的支持情况:**
@ -478,12 +483,12 @@ export default defineUserConfig({
**获取 css 伪类选择器 `:dir()` 特定范围浏览器的支持情况:** **获取 css 伪类选择器 `:dir()` 特定范围浏览器的支持情况:**
```md ```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 增强,支持 代码块分组、提示容器、任务列表、数学公式、代码演示 等 - 📠 markdown 增强,支持 代码块分组、提示容器、任务列表、数学公式、代码演示 等
- 📚 代码演示,支持 CodePen, Replit - 📚 代码演示,支持 CodePen, Replit, JSFiddle, CodeSandbox 等
- 📊 嵌入图标,支持 chart.jsEchartsMermaidflowchart - 📊 嵌入图标,支持 chart.jsEchartsMermaidflowchart
- 🎛 资源嵌入,支持 PDF, bilibili视频youtube视频等 - 🎛 资源嵌入,支持 PDF, bilibili视频youtube视频等