feat(plugin-md-power): improve plot behavior (#620)
This commit is contained in:
parent
a5c874cdcf
commit
9efa2c1a80
@ -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
|
||||
遮罩层效果,或者文本模糊效果
|
||||
:::
|
||||
|
||||
::::
|
||||
|
||||
@ -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}
|
||||
:::
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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'
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user