feat(plugin-md-power): improve plot behavior (#620)

This commit is contained in:
pengzhanbo 2025-06-19 00:12:22 +08:00 committed by GitHub
parent a5c874cdcf
commit 9efa2c1a80
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 139 additions and 108 deletions

View File

@ -29,12 +29,8 @@ export default defineUserConfig({
鼠标悬停触发,或者点击触发
:::
::: field name="mask" type="string | { light: string, dark: string }" default="'#000'" optional
遮罩颜色
:::
::: field name="color" type="string | { light: string, dark: string }" default="'#fff'" optional
文本颜色
::: field name="effect" type="'blur' | 'mask'" default="'mask'" optional
遮罩层效果,或者文本模糊效果
:::
::::

View File

@ -33,33 +33,23 @@ export default defineUserConfig({
})
```
`markdownPower.plot` 支持传入 `boolean | PlotOptions` 类型
`markdownPower.plot` 支持传入 `boolean | PlotOptions` 类型,该配置用于控制该功能的默认行为。
```ts
interface PlotOptions {
/**
* 是否启用 `!! !!` markdown (该标记为非标准标记,脱离插件将不生效)
* 如果设置为 false 则表示不启用该标记,只能使用 <Plot /> 组件
* @default true
*/
tag?: boolean
/**
* 遮罩层颜色
*/
mask?: string | { light: string, dark: string }
/**
* 文本颜色
*/
color?: string | { light: string, dark: string }
/**
* 触发方式
*
* @default 'hover'
*/
trigger?: 'hover' | 'click'
/**
* 遮罩层效果
*
* @default 'mask'
*/
effect?: 'mask' | 'blur'
}
```
@ -69,21 +59,71 @@ interface PlotOptions {
!!需要隐秘的内容!!
```
如果不想使用 非标准的 `!! !!` 标记语法,你可以将 `plot.tag` 设置为 `false`
然后使用 [`<Plot />`](../components/plot.md) 组件替代。
还可以通过属性语法控制行为:
```md
!!需要隐秘的内容!!{.click}
!!需要隐秘的内容!!{.hover}
!!需要隐秘的内容!!{.mask}
!!需要隐秘的内容!!{.blur}
!!需要隐秘的内容!!{.blur .click}
```
- `.click` - 点击触发
- `.hover` - 鼠标悬停触发
- `.mask` - 遮罩层效果
- `.blur` - 文本模糊效果
::: info 你也可以使用 [`<Plot />`](../components/plot.md) 组件替代。
:::
## Frontmatter
在 Frontmatter 中使用 `plot` 选项来控制在当前页面中该功能的默认行为:
```
---
plot:
trigger: hover
effect: blur
---
```
## 示例
输入:
**输入**
```md
你知道吗, !!鲁迅!! 曾说过:“ !!我没说过这句话!!! ” 令我醍醐灌顶,深受启发,浑身迸发出无可匹敌的
力量!于是,!!我在床上翻了个身!!
```
输出:
**输出**
:::demo-wrapper
你知道吗, !!鲁迅!! 曾说过:“ !!我没说过这句话!!! ” 令我醍醐灌顶,深受启发,浑身迸发出无可匹敌的
你知道吗, !!鲁迅!! 曾说过:“ !!我没说过这句话!!!” 令我醍醐灌顶,深受启发,浑身迸发出无可匹敌的
力量!于是,!!我在床上翻了个身!!
:::
**输入**
```md
遮罩层效果 + 鼠标悬停:!!鼠标悬停看到我了!!{.mask .hover}
遮罩层效果 + 点击:!!点击看到我了!!{.mask .click}
文本模糊效果 + 鼠标悬停:!!鼠标悬停看到我了!!{.blur .hover}
文本模糊效果 + 点击:!!点击看到我了!!{.blur .click}
```
**输出**
:::demo-wrapper
遮罩层效果 + 鼠标悬停:!!鼠标悬停看到我了!!{.mask .hover}
遮罩层效果 + 点击:!!点击看到我了!!{.mask .click}
文本模糊效果 + 鼠标悬停:!!鼠标悬停看到我了!!{.blur .hover}
文本模糊效果 + 点击:!!点击看到我了!!{.blur .click}
:::

View File

@ -1,61 +1,46 @@
<script setup lang="ts">
import type { PlotOptions } from '../../shared/index.js'
import { onClickOutside, useMediaQuery } from '@vueuse/core'
import { computed, ref, shallowRef } from 'vue'
import { computed, onMounted, ref, useTemplateRef } from 'vue'
import { usePageFrontmatter } from 'vuepress/client'
import { pluginOptions } from '../options.js'
const props = defineProps<Omit<PlotOptions, 'tag'>>()
const props = defineProps<PlotOptions>()
const matter = usePageFrontmatter()
const options = computed(() => {
const plot = typeof pluginOptions.plot === 'object' ? pluginOptions.plot : {}
return {
trigger: props.trigger || matter.value.plotTrigger || plot.trigger || 'hover',
color: props.color || plot.color,
mask: props.mask || plot.mask,
}
})
const styles = computed(() => {
const plot = options.value
if (!plot.color && !plot.mask)
return {}
const style: Record<string, string> = {}
if (plot.color) {
if (typeof plot.color === 'string') {
style['--vp-c-plot-light'] = plot.color
}
else {
style['--vp-c-plot-light'] = plot.color.light
style['--vp-c-plot-dark'] = plot.color.dark
}
}
if (plot.mask) {
if (typeof plot.mask === 'string') {
style['--vp-c-bg-plot-light'] = plot.mask
}
else {
style['--vp-c-bg-plot-light'] = plot.mask.light
style['--vp-c-bg-plot-dark'] = plot.mask.dark
}
}
return style
})
const isMobile = useMediaQuery('(max-width: 768px)')
const plot = computed(() => {
const global = typeof pluginOptions.plot === 'object' ? pluginOptions.plot : {}
const current = (typeof matter.value.plot === 'object' ? matter.value.plot : {}) as PlotOptions
return {
trigger: isMobile.value ? 'click' : props.trigger ?? current.trigger ?? global.trigger ?? 'hover',
effect: props.effect ?? current.effect ?? global.effect ?? 'mask',
}
})
const active = ref(false)
const el = shallowRef<HTMLElement>()
const el = useTemplateRef<HTMLElement>('el')
const classes = ref<string[]>([])
onMounted(() => {
if (!el.value)
return
const classList = el.value.classList
if (!classList.contains('hover') && !classList.contains('click')) {
classes.value.push(plot.value.trigger)
}
if (!classList.contains('mask') && !classList.contains('blur')) {
classes.value.push(plot.value.effect)
}
})
onClickOutside(el, () => {
if (options.value.trigger === 'click' || isMobile.value)
if (plot.value.trigger === 'click' || el.value?.classList.contains('click'))
active.value = false
})
function onClick() {
if (props.trigger === 'click' || isMobile.value)
if (plot.value.trigger === 'click' || el.value?.classList.contains('click'))
active.value = !active.value
}
</script>
@ -64,8 +49,7 @@ function onClick() {
<span
ref="el"
class="vp-plot"
:class="{ hover: options.trigger !== 'click', active }"
:style="styles"
:class="[{ active }, ...classes]"
@click="onClick"
>
<slot />
@ -73,25 +57,39 @@ function onClick() {
</template>
<style>
:root {
--vp-plot-bg: var(--vp-c-text-1);
--vp-plot-c-text: var(--vp-c-neutral-inverse);
--vp-plot-blur: 0.2rem;
}
.vp-plot {
padding-right: 2px;
padding-left: 2px;
}
.vp-plot.click {
cursor: pointer;
}
.vp-plot:where(.blur) {
filter: blur(var(--vp-plot-blur));
transition: filter var(--vp-t-color);
}
.vp-plot:where(.mask) {
color: transparent;
background-color: var(--vp-c-bg-plot-light, #000);
transition: color ease 0.25s, background-color ease 0.25s;
background-color: var(--vp-plot-bg);
transition: color var(--vp-t-color), background-color var(--vp-t-color);
}
[data-theme="dark"] .vp-plot {
background-color: var(--vp-c-bg-plot-dark, #fff);
.vp-plot:where(.blur.hover):hover,
.vp-plot:where(.blur).active {
filter: blur(0);
}
.vp-plot.hover:hover,
.vp-plot.active {
color: var(--vp-c-plot-light, #fff);
}
[data-theme="dark"] .vp-plot.hover:hover,
[data-theme="dark"] .vp-plot.active {
color: var(--vp-c-plot-dark, #000);
.vp-plot:where(.mask.hover):hover,
.vp-plot:where(.mask).active {
color: var(--vp-plot-c-text);
}
</style>

View File

@ -60,9 +60,15 @@ const plotDef: RuleInline = (state, silent) => {
state.posMax = state.pos
state.pos = start + 2
const token = state.push('plot_inline', 'Plot', 0)
token.markup = '!!'
token.content = content
const openToken = state.push('plot_inline_open', 'Plot', 1)
openToken.markup = '!!'
openToken.content = content
const contentToken = state.push('text', '', 0)
contentToken.content = content
const closeToken = state.push('plot_inline_close', 'Plot', -1)
closeToken.markup = '!!'
state.pos = state.posMax + 2
state.posMax = max
@ -71,9 +77,5 @@ const plotDef: RuleInline = (state, silent) => {
}
export const plotPlugin: PluginWithOptions<never> = (md) => {
md.renderer.rules.plot_inline = (tokens, idx) => {
const token = tokens[idx]
return `<Plot>${token.content}</Plot>`
}
md.inline.ruler.before('emphasis', 'plot', plotDef)
}

View File

@ -1,24 +1,19 @@
/**
* `!! !!` markdown
*/
export interface PlotOptions {
/**
* `!! !!` markdown
* @default true
*/
tag?: boolean
/**
*
*/
mask?: string | { light: string, dark: string }
/**
*
*/
color?: string | { light: string, dark: string }
/**
*
*
* @default 'hover'
*/
trigger?: 'hover' | 'click'
/**
*
*
* @default 'mask'
*/
effect?: 'mask' | 'blur'
}