feat(plugin-md-power): add field and field-group container, close #566 (#579)

This commit is contained in:
pengzhanbo 2025-04-30 03:01:06 +08:00 committed by GitHub
parent 16c768f3a9
commit 56c5eb5257
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 280 additions and 0 deletions

View File

@ -42,6 +42,7 @@ export const themeGuide = defineNoteConfig({
'card',
'steps',
'file-tree',
'field',
'tabs',
'timeline',
'demo-wrapper',

View File

@ -31,6 +31,7 @@ export const theme: Theme = plumeTheme({
timeline: true,
collapse: true,
chat: true,
field: true,
imageSize: 'all',
pdf: true,
caniuse: true,

View File

@ -155,6 +155,12 @@ export default defineUserConfig({
- **默认值**: `true`
- **详情**: 是否启用文件树容器语法
### field
- **类型**: `boolean`
- **默认值**: `false`
- **详情**: 是否启用字段容器
### timeline
- **类型**: `boolean`

View File

@ -0,0 +1,131 @@
---
title: 字段容器
icon: solar:text-field-linear
createTime: 2025/04/29 09:55:17
permalink: /guide/markdown/field/
badge: 新
---
## 概述
在 markdown 中,使用 `::: field` 容器,用于描述字段信息,包括字段名称、字段类型、是否必填、默认值、详情等信息。
它适用于 描述配置中的字段、组件的 Props 等场景。
还可以使用额外的 `:::: field-group` 容器,用于组合多个 `::: field`
## 启用
该功能默认不启用,您需要在 `theme` 配置中启用它。
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
field: true, // [!code ++]
},
})
})
```
## 语法
```md
<!-- 单个字段 -->
::: field name="字段名" type="类型" required default="默认值"
字段描述信息
:::
<!-- 字段组合 -->
:::: field-group
::: field name="字段名" type="类型" required default="默认值"
字段描述信息
:::
::: field name="字段名" type="类型" required default="默认值"
字段描述信息
:::
::::
```
## 属性
::: field name="name" required type="string"
字段名称
:::
::: field name="type" type="string" optional
字段类型
:::
::: field name="required" type="boolean" optional
是否必填
:::
::: field name="optional" type="boolean" optional
是否可选
:::
::: field name="default" type="string" optional
默认值
:::
## 示例
**输入:**
```md
::: field name="theme" type="ThemeConfig" required default="{}"
主题配置
:::
::: field name="enabled" type="boolean" optional default="true"
是否启用
:::
```
**输出:**
::: field name="theme" type="ThemeConfig" required default="{}"
主题配置
:::
::: field name="enabled" type="boolean" optional default="true"
是否启用
:::
**输入:**
```md
:::: field-group
::: field name="theme" type="ThemeConfig" required default="{ base: '/' }"
主题配置
:::
::: field name="enabled" type="boolean" optional default="true"
是否启用
:::
::: field name="callback" type="(...args: any[]) => void" optional default="() => {}"
回调函数
:::
::::
```
**输出:**
:::: field-group
::: field name="theme" type="ThemeConfig" required default="{ base: '/' }"
主题配置
:::
::: field name="enabled" type="boolean" optional default="true"
是否启用
:::
::: field name="callback" type="(...args: any[]) => void" optional default="() => {}"
回调函数
:::
::::

View File

@ -0,0 +1,92 @@
<script lang="ts" setup>
defineProps<{
name: string
type?: string
required?: boolean
optional?: boolean
defaultValue?: string
}>()
</script>
<template>
<div class="vp-field">
<p class="field-meta">
<span class="name">{{ name }}</span>
<span v-if="required || optional" :class="{ required, optional }">{{ required ? 'Required' : optional ? 'Optional' : '' }}</span>
<span v-if="type" class="type"><code>{{ type }}</code></span>
</p>
<p v-if="defaultValue" class="default-value">
<code>{{ defaultValue }}</code>
</p>
<div v-if="$slots.default" class="description">
<slot />
</div>
</div>
</template>
<style>
.vp-field {
width: 100%;
margin: 16px 0;
border-bottom: solid 1px var(--vp-c-divider);
transition: border-color var(--vp-t-color);
}
.vp-field:last-of-type {
border-bottom: none;
}
.vp-field .field-meta {
display: flex;
gap: 8px;
align-items: flex-start;
margin: 8px 0;
}
.vp-field .field-meta .name {
font-size: 20px;
font-weight: 500;
}
.vp-field .field-meta .required,
.vp-field .field-meta .optional {
display: inline-block;
padding: 2px 8px;
font-size: 12px;
font-style: italic;
line-height: 1;
border-radius: 8px;
}
.vp-field .field-meta .required {
color: var(--vp-c-success-2);
border: solid 1px var(--vp-c-success-2);
}
.vp-field .field-meta .optional {
color: var(--vp-c-text-3);
border: solid 1px var(--vp-c-divider);
}
.vp-field .field-meta .type {
flex: 1 2;
text-align: right;
}
.vp-field .default-value {
margin: 0;
font-size: 14px;
line-height: 1;
}
.vp-field .description :where(p, ul, ol) {
color: var(--vp-c-text-2);
}
.vp-field-group {
padding: 0 20px;
margin: 16px 0;
border: solid 1px var(--vp-c-divider);
border-radius: 6px;
}
</style>

View File

@ -0,0 +1,33 @@
import type { Markdown } from 'vuepress/markdown'
import { isUndefined } from '@pengzhanbo/utils'
import { resolveAttrs } from '../utils/resolveAttrs.js'
import { stringifyAttrs } from '../utils/stringifyAttrs.js'
import { createContainerPlugin } from './createContainer.js'
interface FieldAttrs {
name: string
type?: string
required?: boolean
optional?: boolean
default?: string
}
export function fieldPlugin(md: Markdown) {
createContainerPlugin(md, 'field', {
before: (info) => {
const { attrs } = resolveAttrs<FieldAttrs>(info)
const { name, type, required, optional, default: defaultValue } = attrs
const props = stringifyAttrs({ name, required, optional })
return `<VPField${props}${
!isUndefined(type) ? ` type="${type}"` : ''
}${
!isUndefined(defaultValue) ? ` default-value="${defaultValue}"` : ''
}>`
},
after: () => '</VPField>',
})
createContainerPlugin(md, 'field-group', {
before: () => '<div class="vp-field-group">',
})
}

View File

@ -8,6 +8,7 @@ import { chatPlugin } from './chat.js'
import { codeTabs } from './codeTabs.js'
import { collapsePlugin } from './collapse.js'
import { demoWrapperPlugin } from './demoWrapper.js'
import { fieldPlugin } from './field.js'
import { fileTreePlugin } from './fileTree.js'
import { langReplPlugin } from './langRepl.js'
import { npmToPlugins } from './npmTo.js'
@ -58,4 +59,7 @@ export async function containerPlugin(
if (options.chat)
chatPlugin(md)
if (options.field)
fieldPlugin(md)
}

View File

@ -120,6 +120,11 @@ export async function prepareConfigFile(app: App, options: MarkdownPowerPluginOp
imports.add(`import '${CLIENT_FOLDER}styles/chat.css'`)
}
if (options.field) {
imports.add(`import VPField from '${CLIENT_FOLDER}components/VPField.vue'`)
enhances.add(`app.component('VPField', VPField)`)
}
return app.writeTemp(
'md-power/config.js',
`\

View File

@ -109,6 +109,12 @@ export interface MarkdownPowerPluginOptions {
*/
chat?: boolean
/**
* field / field-group
*
* @default false
*/
field?: boolean
// video embed
/**
* bilibili

View File

@ -50,6 +50,7 @@ export const MARKDOWN_POWER_FIELDS: (keyof MarkdownPowerPluginOptions)[] = [
'codepen',
'demo',
'fileTree',
'field',
'icons',
'imageSize',
'jsfiddle',