refactor: use deconstruct syntax to handle component props (#744)

This commit is contained in:
pengzhanbo 2025-10-31 17:42:28 +08:00 committed by GitHub
parent 0e38265f96
commit d4ad65a1ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
73 changed files with 320 additions and 350 deletions

View File

@ -4,13 +4,13 @@ import { computed, nextTick, ref, useTemplateRef, watch } from 'vue'
import '@vuepress/helper/transition/fade-in.css' import '@vuepress/helper/transition/fade-in.css'
const props = defineProps<{ const { label, total } = defineProps<{
label: string label: string
total: number total: number
}>() }>()
const active = ref(false) const active = ref(false)
const list = computed(() => Array.from({ length: props.total }, (_, i) => i)) const list = computed(() => Array.from({ length: total }, (_, i) => i))
const position = ref({ x: 0, y: 0 }) const position = ref({ x: 0, y: 0 })
const popover = useTemplateRef<HTMLDivElement>('popover') const popover = useTemplateRef<HTMLDivElement>('popover')

View File

@ -3,7 +3,7 @@ import { useInterval } from '@vueuse/core'
import { onMounted, toRef, watch } from 'vue' import { onMounted, toRef, watch } from 'vue'
import { useAudioPlayer } from '../composables/audio.js' import { useAudioPlayer } from '../composables/audio.js'
const props = defineProps<{ const { src, autoplay, type, volume, startTime, endTime } = defineProps<{
src: string src: string
autoplay?: boolean autoplay?: boolean
type?: string type?: string
@ -13,20 +13,19 @@ const props = defineProps<{
}>() }>()
const { paused, play, pause, seek, setVolume } = useAudioPlayer( const { paused, play, pause, seek, setVolume } = useAudioPlayer(
toRef(() => props.src), toRef(() => src),
{ {
type: toRef(() => props.type || ''), type: toRef(() => type || ''),
autoplay: props.autoplay, autoplay,
oncanplay: () => { oncanplay: () => {
if (props.startTime) { if (startTime)
seek(props.startTime) seek(startTime)
}
}, },
ontimeupdate: (time) => { ontimeupdate: (time) => {
if (props.endTime && time >= props.endTime) { if (endTime && time >= endTime) {
pause() pause()
if (props.startTime) { if (startTime) {
seek(props.startTime) seek(startTime)
} }
} }
}, },
@ -56,7 +55,7 @@ function toggle() {
} }
onMounted(() => { onMounted(() => {
watch(() => props.volume, (volume) => { watch(() => volume, (volume) => {
if (typeof volume !== 'undefined') { if (typeof volume !== 'undefined') {
setVolume(volume) setVolume(volume)
} }

View File

@ -12,16 +12,12 @@ interface MessageData {
} }
} }
const props = withDefaults(defineProps<{ const { feature, past = 2, future = 1, meta = '' } = defineProps<{
feature: string feature: string
past?: number past?: number
future?: number future?: number
meta?: string meta?: string
}>(), { }>()
past: 2,
future: 1,
meta: '',
})
const url = 'https://caniuse.pengzhanbo.cn/' const url = 'https://caniuse.pengzhanbo.cn/'
@ -29,7 +25,7 @@ const height = ref('330px')
const isDark = useDarkMode() const isDark = useDarkMode()
const source = computed(() => { const source = computed(() => {
const source = `${url}${props.feature}#past=${props.past}&future=${props.future}&meta=${props.meta}&theme=${isDark.value ? 'dark' : 'light'}` const source = `${url}${feature}#past=${past}&future=${future}&meta=${meta}&theme=${isDark.value ? 'dark' : 'light'}`
return source return source
}) })
@ -40,8 +36,8 @@ useEventListener('message', (event) => {
if ( if (
type === 'ciu_embed' type === 'ciu_embed'
&& payload && payload
&& payload.feature === props.feature && payload.feature === feature
&& payload.meta === props.meta && payload.meta === meta
) { ) {
height.value = `${Math.ceil(payload.height)}px` height.value = `${Math.ceil(payload.height)}px`
} }

View File

@ -2,7 +2,7 @@
import { useDarkMode } from '@vuepress/helper/client' import { useDarkMode } from '@vuepress/helper/client'
import { computed } from 'vue' import { computed } from 'vue'
const props = defineProps<{ const { user, slash, title, preview, editable, tab, theme, width, height } = defineProps<{
user: string user: string
slash: string slash: string
title?: string title?: string
@ -19,16 +19,16 @@ const CODEPEN_LINK = 'https://codepen.io/'
const isDark = useDarkMode() const isDark = useDarkMode()
const link = computed(() => { const link = computed(() => {
const middle = props.preview ? '/embed/preview/' : '/embed/' const middle = preview ? '/embed/preview/' : '/embed/'
const params = new URLSearchParams() const params = new URLSearchParams()
props.editable && params.set('editable', 'true') editable && params.set('editable', 'true')
props.tab && params.set('default-tab', props.tab) tab && params.set('default-tab', tab)
const theme = props.theme ?? (isDark.value ? 'dark' : 'light') const themeMode = theme ?? (isDark.value ? 'dark' : 'light')
theme && params.set('theme-id', theme) themeMode && params.set('theme-id', themeMode)
return `${CODEPEN_LINK}${props.user}${middle}${props.slash}?${params.toString()}` return `${CODEPEN_LINK}${user}${middle}${slash}?${params.toString()}`
}) })
</script> </script>

View File

@ -2,7 +2,7 @@
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { inject, ref } from 'vue' import { inject, ref } from 'vue'
const props = defineProps<{ const { type, filename, level, diff, expanded, focus, filepath } = defineProps<{
type: 'file' | 'folder' type: 'file' | 'folder'
filename: string filename: string
level: number level: number
@ -18,17 +18,17 @@ const onNodeClick = inject<
(filename: string, type: 'file' | 'folder') => void (filename: string, type: 'file' | 'folder') => void
>('on-file-tree-node-click', () => {}) >('on-file-tree-node-click', () => {})
const active = ref(props.expanded) const active = ref(expanded)
function nodeClick() { function nodeClick() {
if (props.filename === '…' || props.filename === '...') if (filename === '…' || filename === '...')
return return
onNodeClick(props.filepath || props.filename, props.type) onNodeClick(filepath || filename, type)
} }
function toggle(ev: MouseEvent) { function toggle(ev: MouseEvent) {
if (props.type === 'folder') { if (type === 'folder') {
const el = ev.target as HTMLElement const el = ev.target as HTMLElement
if (!el.matches('.comment, .comment *')) { if (!el.matches('.comment, .comment *')) {
active.value = !active.value active.value = !active.value

View File

@ -2,7 +2,7 @@
import { useDarkMode } from '@vuepress/helper/client' import { useDarkMode } from '@vuepress/helper/client'
import { computed } from 'vue' import { computed } from 'vue'
const props = defineProps<{ const { source, title, tab, theme, width, height } = defineProps<{
source: string source: string
title?: string title?: string
tab: string tab: string
@ -14,8 +14,8 @@ const props = defineProps<{
const isDark = useDarkMode() const isDark = useDarkMode()
const link = computed(() => { const link = computed(() => {
const theme = props.theme === 'dark' ? '/dark/' : isDark.value ? '/dark/' : '' const themeMode = theme === 'dark' ? '/dark/' : isDark.value ? '/dark/' : ''
return `https://jsfiddle.net/${props.source}/embedded/${props.tab}${theme}` return `https://jsfiddle.net/${source}/embedded/${tab}${themeMode}`
}) })
</script> </script>

View File

@ -8,7 +8,7 @@ defineOptions({
inheritAttrs: false, inheritAttrs: false,
}) })
const props = defineProps<ReplitTokenMeta>() const { source, theme, width, height: h, title } = defineProps<ReplitTokenMeta>()
// magic height // magic height
const height = ref('47px') const height = ref('47px')
@ -19,18 +19,18 @@ const REPLIT_LINK = 'https://replit.com/'
const isDark = useDarkMode() const isDark = useDarkMode()
const link = computed(() => { const link = computed(() => {
const url = new URL(`/${props.source}`, REPLIT_LINK) const url = new URL(`/${source}`, REPLIT_LINK)
url.searchParams.set('embed', 'true') url.searchParams.set('embed', 'true')
const theme = props.theme || (isDark.value ? 'dark' : 'light') const themeMode = theme || (isDark.value ? 'dark' : 'light')
url.searchParams.set('theme', theme) url.searchParams.set('theme', themeMode)
return url.toString() return url.toString()
}) })
function onload() { function onload() {
loaded.value = true loaded.value = true
height.value = props.height || '450px' height.value = h || '450px'
} }
</script> </script>

View File

@ -5,27 +5,27 @@ import { onMounted, ref, shallowRef, watch } from 'vue'
interface TabProps extends Record<string, unknown> { interface TabProps extends Record<string, unknown> {
id: string id: string
} }
const props = withDefaults(defineProps<{ const { id, tabId = '', active = 0, data } = defineProps<{
id: string id: string
tabId?: string tabId?: string
active?: number active?: number
data: TabProps[] data: TabProps[]
}>(), { active: 0, tabId: '' }) }>()
const TAB_STORE_NAME = 'VUEPRESS_TAB_STORE' const TAB_STORE_NAME = 'VUEPRESS_TAB_STORE'
const tabStore = useStorage<Record<string, string>>(TAB_STORE_NAME, {}) const tabStore = useStorage<Record<string, string>>(TAB_STORE_NAME, {})
// Index of current active item // Index of current active item
const activeIndex = ref(props.active) const activeIndex = ref(active)
// Refs of the tab buttons // Refs of the tab buttons
const tabRefs = shallowRef<HTMLUListElement[]>([]) const tabRefs = shallowRef<HTMLUListElement[]>([])
// Update store // Update store
function updateStore(): void { function updateStore(): void {
if (props.tabId) if (tabId)
tabStore.value[props.tabId] = props.data[activeIndex.value].id tabStore.value[tabId] = data[activeIndex.value]?.id
} }
// Activate next tab // Activate next tab
@ -59,26 +59,26 @@ function keyboardHandler(event: KeyboardEvent, index: number): void {
} }
function getInitialIndex(): number { function getInitialIndex(): number {
if (props.tabId) { if (tabId) {
const valueIndex = props.data.findIndex( const valueIndex = data.findIndex(
({ id }) => tabStore.value[props.tabId] === id, ({ id }) => tabStore.value[tabId] === id,
) )
if (valueIndex !== -1) if (valueIndex !== -1)
return valueIndex return valueIndex
} }
return props.active return active
} }
onMounted(() => { onMounted(() => {
activeIndex.value = getInitialIndex() activeIndex.value = getInitialIndex()
watch( watch(
() => tabStore.value[props.tabId], () => tabStore.value[tabId],
(newValue, oldValue) => { (newValue, oldValue) => {
if (props.tabId && newValue !== oldValue) { if (tabId && newValue !== oldValue) {
const index = props.data.findIndex(({ id }) => id === newValue) const index = data.findIndex(({ id }) => id === newValue)
if (index !== -1) if (index !== -1)
activeIndex.value = index activeIndex.value = index

View File

@ -1,13 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, provide, ref, useTemplateRef, watch } from 'vue' import { onMounted, provide, ref, useTemplateRef, watch } from 'vue'
const props = withDefaults(defineProps<{ const { title, height = '320px', entryFile } = defineProps<{
title?: string title?: string
height?: string height?: string
entryFile?: string entryFile?: string
}>(), { height: '320px' }) }>()
const activeNode = ref(props.entryFile || '') const activeNode = ref(entryFile || '')
const isEmpty = ref(true) const isEmpty = ref(true)
const codePanel = useTemplateRef<HTMLDivElement>('codePanel') const codePanel = useTemplateRef<HTMLDivElement>('codePanel')
@ -44,7 +44,7 @@ onMounted(() => {
<template> <template>
<div class="vp-code-tree"> <div class="vp-code-tree">
<div class="code-tree-panel" :style="{ 'max-height': props.height }"> <div class="code-tree-panel" :style="{ 'max-height': height }">
<div v-if="title" class="code-tree-title" :title="title"> <div v-if="title" class="code-tree-title" :title="title">
<span>{{ title }}</span> <span>{{ title }}</span>
</div> </div>
@ -52,7 +52,7 @@ onMounted(() => {
<slot name="file-tree" /> <slot name="file-tree" />
</div> </div>
</div> </div>
<div ref="codePanel" class="code-panel" :style="{ height: props.height }"> <div ref="codePanel" class="code-panel" :style="{ height }">
<slot /> <slot />
<div v-if="isEmpty" class="code-tree-empty"> <div v-if="isEmpty" class="code-tree-empty">
<span class="vpi-code-tree-empty" /> <span class="vpi-code-tree-empty" />

View File

@ -2,15 +2,15 @@
import { provide, ref } from 'vue' import { provide, ref } from 'vue'
import { INJECT_COLLAPSE_KEY } from '../options.js' import { INJECT_COLLAPSE_KEY } from '../options.js'
const props = defineProps<{ const { accordion, index } = defineProps<{
accordion?: boolean accordion?: boolean
index?: number index?: number
}>() }>()
const currentIndex = ref<number | undefined>(props.index) const currentIndex = ref<number | undefined>(index)
provide(INJECT_COLLAPSE_KEY, { provide(INJECT_COLLAPSE_KEY, {
accordion: props.accordion ?? false, accordion: accordion ?? false,
index: currentIndex, index: currentIndex,
}) })
</script> </script>

View File

@ -6,7 +6,7 @@ import { INJECT_COLLAPSE_KEY } from '../options.js'
import '@vuepress/helper/transition/fade-in-height-expand.css' import '@vuepress/helper/transition/fade-in-height-expand.css'
const props = defineProps<{ const { expand, index } = defineProps<{
expand?: boolean expand?: boolean
index: number index: number
}>() }>()
@ -20,36 +20,36 @@ if (__VUEPRESS_DEV__ && !collapse) {
throw new Error('<VPCollapseItem /> must be used inside <VPCollapse />') throw new Error('<VPCollapseItem /> must be used inside <VPCollapse />')
} }
const expand = ref( const expanded = ref(
collapse?.accordion && typeof collapse.index.value !== 'undefined' collapse?.accordion && typeof collapse.index.value !== 'undefined'
? props.index === collapse.index.value ? index === collapse.index.value
: props.expand, : expand,
) )
if (collapse?.accordion) { if (collapse?.accordion) {
watch(collapse?.index, () => { watch(collapse?.index, () => {
expand.value = collapse?.index.value === props.index expanded.value = collapse?.index.value === index
}) })
} }
function toggle() { function toggle() {
if (collapse?.accordion) { if (collapse?.accordion) {
if (collapse.index.value === props.index && expand.value) { if (collapse.index.value === index && expanded.value) {
expand.value = false expanded.value = false
} }
else { else {
collapse!.index.value = props.index! collapse!.index.value = index!
expand.value = true expanded.value = true
} }
} }
else { else {
expand.value = !expand.value expanded.value = !expanded.value
} }
} }
</script> </script>
<template> <template>
<div class="vp-collapse-item" :class="{ expand }"> <div class="vp-collapse-item" :class="{ expanded }">
<div class="vp-collapse-header" @click="toggle"> <div class="vp-collapse-header" @click="toggle">
<span class="vpi-chevron-right" /> <span class="vpi-chevron-right" />
<p class="vp-collapse-title"> <p class="vp-collapse-title">
@ -57,7 +57,7 @@ function toggle() {
</p> </p>
</div> </div>
<FadeInExpandTransition> <FadeInExpandTransition>
<div v-show="expand" class="vp-collapse-content"> <div v-show="expanded" class="vp-collapse-content">
<div class="vp-collapse-content-inner"> <div class="vp-collapse-content-inner">
<slot /> <slot />
</div> </div>
@ -95,7 +95,7 @@ function toggle() {
transform: rotate(0deg); transform: rotate(0deg);
} }
.vp-collapse-item.expand .vpi-chevron-right { .vp-collapse-item.expanded .vpi-chevron-right {
transform: rotate(90deg); transform: rotate(90deg);
} }

View File

@ -8,14 +8,14 @@ import { useExpand } from '../composables/demo.js'
import '@vuepress/helper/transition/fade-in-height-expand.css' import '@vuepress/helper/transition/fade-in-height-expand.css'
import '../styles/demo.css' import '../styles/demo.css'
const props = defineProps<{ const { type, title, desc, expanded } = defineProps<{
type?: 'vue' | 'markdown' type?: 'vue' | 'markdown'
title?: string title?: string
desc?: string desc?: string
expanded?: boolean expanded?: boolean
}>() }>()
const [showCode, toggleCode] = useExpand(props.expanded) const [showCode, toggleCode] = useExpand(expanded)
const draw = useTemplateRef<HTMLIFrameElement>('draw') const draw = useTemplateRef<HTMLIFrameElement>('draw')
const vueDraw = useTemplateRef<HTMLIFrameElement>('draw-vue') const vueDraw = useTemplateRef<HTMLIFrameElement>('draw-vue')
@ -30,7 +30,7 @@ function resizeAndPositionVueDraw() {
vueDraw.value.style.left = `${rect.x + scrollLeft}px` vueDraw.value.style.left = `${rect.x + scrollLeft}px`
} }
if (props.type === 'vue' && !__VUEPRESS_SSR__) { if (type === 'vue' && !__VUEPRESS_SSR__) {
watch([draw, vueDraw], () => { watch([draw, vueDraw], () => {
resizeAndPositionVueDraw() resizeAndPositionVueDraw()
if (draw.value && vueDraw.value) { if (draw.value && vueDraw.value) {

View File

@ -8,29 +8,29 @@ import '@vuepress/helper/transition/fade-in.css'
import '@vuepress/helper/transition/fade-in-height-expand.css' import '@vuepress/helper/transition/fade-in-height-expand.css'
import '../styles/demo.css' import '../styles/demo.css'
const props = defineProps<{ const { title, desc, expanded, config } = defineProps<{
title?: string title?: string
desc?: string desc?: string
expanded?: boolean expanded?: boolean
config?: DemoConfig config?: DemoConfig
}>() }>()
const [showCode, toggleCode] = useExpand(props.expanded) const [showCode, toggleCode] = useExpand(expanded)
const { resources, showResources, toggleResources } = useResources( const { resources, showResources, toggleResources } = useResources(
useTemplateRef<HTMLDivElement>('resourcesEl'), useTemplateRef<HTMLDivElement>('resourcesEl'),
() => props.config, () => config,
) )
const { id, height } = useNormalDemo( const { id, height } = useNormalDemo(
useTemplateRef<HTMLIFrameElement>('draw'), useTemplateRef<HTMLIFrameElement>('draw'),
() => props.title, () => title,
() => props.config, () => config,
) )
const data = useFence( const data = useFence(
useTemplateRef<HTMLDivElement>('fence'), useTemplateRef<HTMLDivElement>('fence'),
() => props.config, () => config,
) )
</script> </script>

View File

@ -3,7 +3,7 @@ import { decodeData } from '@vuepress/helper/client'
import { useClipboard, useToggle } from '@vueuse/core' import { useClipboard, useToggle } from '@vueuse/core'
import { computed, useTemplateRef } from 'vue' import { computed, useTemplateRef } from 'vue'
const props = defineProps<{ const { title, align = 'left', copy, maxContent, fullWidth, markdown } = defineProps<{
/** 表格标题 */ /** 表格标题 */
title?: string title?: string
/** 对其方式 */ /** 对其方式 */
@ -19,7 +19,7 @@ const props = defineProps<{
}>() }>()
const tableEl = useTemplateRef('table') const tableEl = useTemplateRef('table')
const rawContent = computed(() => props.markdown ? decodeData(props.markdown) : '') const rawContent = computed(() => markdown ? decodeData(markdown) : '')
const [isHTMLCopied, toggleHTMLCopy] = useToggle() const [isHTMLCopied, toggleHTMLCopy] = useToggle()
const [isMDCopied, toggleMDCopy] = useToggle() const [isMDCopied, toggleMDCopy] = useToggle()
@ -35,7 +35,7 @@ function onCopy(type: 'html' | 'md') {
</script> </script>
<template> <template>
<div class="vp-table" :class="{ [align || 'left']: true, full: fullWidth }"> <div class="vp-table" :class="{ [align]: true, full: fullWidth }">
<div class="table-container"> <div class="table-container">
<div class="table-content"> <div class="table-content">
<div v-if="copy" class="table-toolbar"> <div v-if="copy" class="table-toolbar">

View File

@ -2,7 +2,7 @@
import { computed, provide } from 'vue' import { computed, provide } from 'vue'
import { INJECT_TIMELINE_KEY } from '../options.js' import { INJECT_TIMELINE_KEY } from '../options.js'
const props = defineProps<{ const { horizontal, card, placement, line } = defineProps<{
horizontal?: boolean horizontal?: boolean
card?: boolean card?: boolean
placement?: 'left' | 'right' | 'between' placement?: 'left' | 'right' | 'between'
@ -10,10 +10,10 @@ const props = defineProps<{
}>() }>()
provide(INJECT_TIMELINE_KEY, computed(() => ({ provide(INJECT_TIMELINE_KEY, computed(() => ({
line: props.line || 'solid', line: line || 'solid',
card: props.card ?? false, card: card ?? false,
horizontal: props.horizontal ?? false, horizontal: horizontal ?? false,
placement: props.placement || 'left', placement: placement || 'left',
}))) })))
</script> </script>

View File

@ -4,7 +4,7 @@ import { useMediaQuery } from '@vueuse/core'
import { computed, inject } from 'vue' import { computed, inject } from 'vue'
import { INJECT_TIMELINE_KEY } from '../options.js' import { INJECT_TIMELINE_KEY } from '../options.js'
const props = defineProps<{ const { time, type, card, line, icon, color, placement } = defineProps<{
time?: string time?: string
type?: 'info' | 'tip' | 'success' | 'warning' | 'danger' | 'caution' | 'important' | (string & {}) type?: 'info' | 'tip' | 'success' | 'warning' | 'danger' | 'caution' | 'important' | (string & {})
card?: boolean card?: boolean
@ -25,17 +25,17 @@ const defaultOptions = inject<ComputedRef<{
const timeline = computed(() => { const timeline = computed(() => {
const between = defaultOptions?.value.placement === 'between' && !is639.value const between = defaultOptions?.value.placement === 'between' && !is639.value
const placement = defaultOptions?.value.placement === 'between' ? 'left' : defaultOptions?.value.placement const defaultPlacement = defaultOptions?.value.placement === 'between' ? 'left' : defaultOptions?.value.placement
return { return {
time: props.time, time,
type: props.type || 'info', type: type || 'info',
line: props.line || defaultOptions?.value.line || 'solid', line: line || defaultOptions?.value.line || 'solid',
icon: props.icon, icon,
color: props.color, color,
horizontal: defaultOptions?.value.horizontal ?? false, horizontal: defaultOptions?.value.horizontal ?? false,
between: between ? props.placement || 'left' : false, between: between ? placement || 'left' : false,
placement: between ? '' : (placement || 'left'), placement: between ? '' : (defaultPlacement || 'left'),
card: props.card ?? defaultOptions?.value.card ?? false, card: card ?? defaultOptions?.value.card ?? false,
} }
}) })
</script> </script>

View File

@ -32,7 +32,7 @@ import BackIcon from './icons/BackIcon.vue'
import ClearIcon from './icons/ClearIcon.vue' import ClearIcon from './icons/ClearIcon.vue'
import SearchIcon from './icons/SearchIcon.vue' import SearchIcon from './icons/SearchIcon.vue'
const props = defineProps<{ const { locales, options } = defineProps<{
locales: SearchBoxLocales locales: SearchBoxLocales
options: SearchOptions options: SearchOptions
}>() }>()
@ -42,7 +42,7 @@ const emit = defineEmits<{
}>() }>()
const routeLocale = useRouteLocale() const routeLocale = useRouteLocale()
const locale = useLocale(toRef(props.locales)) const locale = useLocale(toRef(() => locales))
const el = shallowRef<HTMLElement>() const el = shallowRef<HTMLElement>()
const resultsEl = shallowRef<HTMLElement>() const resultsEl = shallowRef<HTMLElement>()
@ -71,15 +71,15 @@ const searchIndex = computedAsync(async () =>
prefix: true, prefix: true,
boost: { title: 4, text: 2, titles: 1 }, boost: { title: 4, text: 2, titles: 1 },
}, },
...props.options.miniSearch?.searchOptions, ...options.miniSearch?.searchOptions,
...props.options.miniSearch?.options, ...options.miniSearch?.options,
}, },
), ),
), ),
) )
const disableQueryPersistence = computed(() => const disableQueryPersistence = computed(() =>
props.options?.disableQueryPersistence === true, options?.disableQueryPersistence === true,
) )
const filterText = disableQueryPersistence.value const filterText = disableQueryPersistence.value
? ref('') ? ref('')

View File

@ -3,11 +3,11 @@ import type { SearchBoxLocales } from '../../shared/index.js'
import { toRef } from 'vue' import { toRef } from 'vue'
import { useLocale } from '../composables/index.js' import { useLocale } from '../composables/index.js'
const props = defineProps<{ const { locales } = defineProps<{
locales: SearchBoxLocales locales: SearchBoxLocales
}>() }>()
const locale = useLocale(toRef(props.locales)) const locale = useLocale(toRef(() => locales))
</script> </script>
<template> <template>

View File

@ -6,14 +6,14 @@ import { withBase } from 'vuepress/client'
import { isLinkHttp } from 'vuepress/shared' import { isLinkHttp } from 'vuepress/shared'
import { useData } from '../../composables/index.js' import { useData } from '../../composables/index.js'
const props = defineProps<ThemeHomeBanner>() const { banner, bannerMask, hero } = defineProps<ThemeHomeBanner>()
const DEFAULT_BANNER = 'https://api.pengzhanbo.cn/wallpaper/bing' const DEFAULT_BANNER = 'https://api.pengzhanbo.cn/wallpaper/bing'
const { isDark, frontmatter: matter } = useData<'home'>() const { isDark, frontmatter: matter } = useData<'home'>()
const mask = computed(() => { const mask = computed(() => {
const mask = props.bannerMask ?? matter.value.bannerMask const mask = bannerMask ?? matter.value.bannerMask
if (typeof mask !== 'object') if (typeof mask !== 'object')
return mask || 0 return mask || 0
@ -21,17 +21,17 @@ const mask = computed(() => {
}) })
const bannerStyle = computed(() => { const bannerStyle = computed(() => {
const banner = props.banner ?? matter.value.banner const _banner = banner ?? matter.value.banner
const link = banner ? isLinkHttp(banner) ? banner : withBase(banner) : DEFAULT_BANNER const link = _banner ? isLinkHttp(_banner) ? _banner : withBase(_banner) : DEFAULT_BANNER
return { return {
'background-image': `url(${link})`, 'background-image': `url(${link})`,
} }
}) })
const name = computed(() => props.hero?.name ?? matter.value.hero?.name ?? 'Plume') const name = computed(() => hero?.name ?? matter.value.hero?.name ?? 'Plume')
const tagline = computed(() => props.hero?.tagline ?? matter.value.hero?.tagline ?? 'A VuePress Theme') const tagline = computed(() => hero?.tagline ?? matter.value.hero?.tagline ?? 'A VuePress Theme')
const text = computed(() => props.hero?.text ?? matter.value.hero?.text) const text = computed(() => hero?.text ?? matter.value.hero?.text)
const actions = computed(() => props.hero?.actions ?? matter.value.hero?.actions ?? []) const actions = computed(() => hero?.actions ?? matter.value.hero?.actions ?? [])
</script> </script>
<template> <template>

View File

@ -5,38 +5,38 @@ import { withBase } from 'vuepress/client'
import { isLinkHttp } from 'vuepress/shared' import { isLinkHttp } from 'vuepress/shared'
import { useDarkMode } from '../../composables/index.js' import { useDarkMode } from '../../composables/index.js'
const props = defineProps<ThemeHomeConfigBase & { const { backgroundAttachment, backgroundImage, containerClass, full } = defineProps<ThemeHomeConfigBase & {
containerClass?: any containerClass?: any
}>() }>()
const isDark = useDarkMode() const isDark = useDarkMode()
const styles = computed(() => { const styles = computed(() => {
if (!props.backgroundImage) if (!backgroundImage)
return null return null
const image = typeof props.backgroundImage === 'string' ? props.backgroundImage : (props.backgroundImage[isDark.value ? 'dark' : 'light'] ?? props.backgroundImage.light) const image = typeof backgroundImage === 'string' ? backgroundImage : (backgroundImage[isDark.value ? 'dark' : 'light'] ?? backgroundImage.light)
if (!image) if (!image)
return null return null
const link = isLinkHttp(image) ? props.backgroundImage : withBase(image) const link = isLinkHttp(image) ? backgroundImage : withBase(image)
return { return {
'background-image': `url(${link})`, 'background-image': `url(${link})`,
'background-size': 'cover', 'background-size': 'cover',
'background-position': 'center', 'background-position': 'center',
'background-repeat': 'no-repeat', 'background-repeat': 'no-repeat',
'background-attachment': props.backgroundAttachment || '', 'background-attachment': backgroundAttachment || '',
} }
}) })
const containerClass = computed(() => normalizeClass(props.containerClass || '')) const containerClasses = computed(() => normalizeClass(containerClass || ''))
</script> </script>
<template> <template>
<div class="vp-home-box" :class="{ full: props.full }" :style="styles"> <div class="vp-home-box" :class="{ full }" :style="styles">
<slot name="before" /> <slot name="before" />
<div class="container" :class="containerClass"> <div class="container" :class="containerClasses">
<slot /> <slot />
</div> </div>
<slot name="after" /> <slot name="after" />

View File

@ -6,15 +6,15 @@ import VPLink from '@theme/VPLink.vue'
import { isLinkAbsolute, isLinkHttp } from '@vuepress/helper/client' import { isLinkAbsolute, isLinkHttp } from '@vuepress/helper/client'
import { computed } from 'vue' import { computed } from 'vue'
const props = defineProps<ThemeHomeFeature>() const { icon, link, linkText, rel, target, title, details } = defineProps<ThemeHomeFeature>()
const ICONIFY_NAME = /^[\w-]+:[\w-]+$/ const ICONIFY_NAME = /^[\w-]+:[\w-]+$/
const isIconify = computed(() => { const isIconify = computed(() => {
if (typeof props.icon !== 'string' || isLinkAbsolute(props.icon) || isLinkHttp(props.icon)) { if (typeof icon !== 'string' || isLinkAbsolute(icon) || isLinkHttp(icon)) {
return false return false
} }
return ICONIFY_NAME.test(props.icon) return ICONIFY_NAME.test(icon)
}) })
</script> </script>

View File

@ -4,10 +4,10 @@ import VPHomeBox from '@theme/Home/VPHomeBox.vue'
import VPHomeFeature from '@theme/Home/VPHomeFeature.vue' import VPHomeFeature from '@theme/Home/VPHomeFeature.vue'
import { computed } from 'vue' import { computed } from 'vue'
const props = defineProps<ThemeHomeFeatures>() const { features, title, description, type, backgroundImage, backgroundAttachment, full, index } = defineProps<ThemeHomeFeatures>()
const grid = computed(() => { const grid = computed(() => {
const length = props.features?.length const length = features?.length
if (!length) if (!length)
return undefined return undefined
@ -32,11 +32,13 @@ const grid = computed(() => {
<VPHomeBox <VPHomeBox
v-if="features" v-if="features"
class="vp-home-features" class="vp-home-features"
:type="type" v-bind="{
:background-image="backgroundImage" type,
:background-attachment="backgroundAttachment" backgroundAttachment,
:full="full" backgroundImage,
:index="index" full,
index,
}"
> >
<h2 v-if="title" class="title" v-html="title" /> <h2 v-if="title" class="title" v-html="title" />
<p v-if="description" class="description" v-html="description" /> <p v-if="description" class="description" v-html="description" />
@ -48,15 +50,7 @@ const grid = computed(() => {
class="item" class="item"
:class="[grid]" :class="[grid]"
> >
<VPHomeFeature <VPHomeFeature v-bind="feature" />
:icon="feature.icon"
:title="feature.title"
:details="feature.details"
:link="feature.link"
:link-text="feature.linkText"
:rel="feature.rel"
:target="feature.target"
/>
</div> </div>
</div> </div>
</VPHomeBox> </VPHomeBox>

View File

@ -3,7 +3,8 @@ import type { ThemeHomeHero } from '../../../shared/index.js'
import { effectComponents, effects } from '@internal/home-hero-effects' import { effectComponents, effects } from '@internal/home-hero-effects'
import ImageBg from '@theme/background/ImageBg.vue' import ImageBg from '@theme/background/ImageBg.vue'
import VPButton from '@theme/VPButton.vue' import VPButton from '@theme/VPButton.vue'
import { computed, nextTick, onMounted, onUnmounted, watch } from 'vue' import { hasGlobalComponent } from '@vuepress/helper/client'
import { computed, markRaw, nextTick, onMounted, onUnmounted, resolveComponent, watch } from 'vue'
import { isPlainObject } from 'vuepress/shared' import { isPlainObject } from 'vuepress/shared'
import { useData } from '../../composables/index.js' import { useData } from '../../composables/index.js'
import { inBrowser } from '../../utils/index.js' import { inBrowser } from '../../utils/index.js'
@ -15,10 +16,11 @@ const hero = computed(() => props.hero ?? frontmatter.value.hero ?? {})
const actions = computed(() => hero.value.actions ?? []) const actions = computed(() => hero.value.actions ?? [])
const effect = computed(() => { const effect = computed(() => {
const effect = props.effect || props.background if (props.effect)
if (!effect || !effects.includes(effect)) return props.effect
return null if (props.background && effects.includes(props.background))
return effect as typeof effects[number] return props.background
return null
}) })
const effectConfig = computed(() => { const effectConfig = computed(() => {
@ -37,6 +39,17 @@ const effectConfig = computed(() => {
return props.effectConfig return props.effectConfig
}) })
const realEffectComponent = computed(() => {
if (!effect.value)
return null
if (effectComponents[effect.value])
return markRaw(effectComponents[effect.value])
if (hasGlobalComponent(effect.value))
return resolveComponent(effect.value)
return null
})
function noTransition() { function noTransition() {
document.documentElement.classList.add('no-transition') document.documentElement.classList.add('no-transition')
setTimeout(() => { setTimeout(() => {
@ -85,7 +98,7 @@ onUnmounted(() => {
[effect ?? '']: !!effect, [effect ?? '']: !!effect,
}" }"
> >
<component :is="effectComponents[effect]" v-if="effect" v-bind="effectConfig" /> <component :is="realEffectComponent" v-if="realEffectComponent" v-bind="effectConfig" />
<ImageBg v-else v-bind="props" /> <ImageBg v-else v-bind="props" />
<div class="hero-container"> <div class="hero-container">

View File

@ -5,7 +5,7 @@ import VPImage from '@theme/VPImage.vue'
import { computed } from 'vue' import { computed } from 'vue'
import { useData } from '../../composables/index.js' import { useData } from '../../composables/index.js'
const props = defineProps<ThemeHomeProfile>() const { name, description, avatar, circle, type, backgroundImage, backgroundAttachment, full, index } = defineProps<ThemeHomeProfile>()
const { theme } = useData() const { theme } = useData()
@ -13,10 +13,10 @@ const rawProfile = computed(() => theme.value.profile)
const profile = computed(() => { const profile = computed(() => {
return { return {
name: props.name || rawProfile.value?.name, name: name || rawProfile.value?.name,
description: props.description || rawProfile.value?.description, description: description || rawProfile.value?.description,
avatar: props.avatar || rawProfile.value?.avatar || rawProfile.value?.url, avatar: avatar || rawProfile.value?.avatar || rawProfile.value?.url,
circle: props.circle || rawProfile.value?.circle, circle: circle || rawProfile.value?.circle,
} }
}) })
</script> </script>
@ -24,11 +24,7 @@ const profile = computed(() => {
<template> <template>
<VPHomeBox <VPHomeBox
class="vp-home-profile" class="vp-home-profile"
:type="type" v-bind="{ type, backgroundAttachment, backgroundImage, full, index }"
:background-image="backgroundImage"
:background-attachment="backgroundAttachment"
:full="full"
:index="index"
> >
<VPImage v-if="profile.avatar" :image="profile.avatar" :class="{ circle: profile.circle }" /> <VPImage v-if="profile.avatar" :image="profile.avatar" :class="{ circle: profile.circle }" />

View File

@ -4,11 +4,9 @@ import VPHomeBox from '@theme/Home/VPHomeBox.vue'
import VPImage from '@theme/VPImage.vue' import VPImage from '@theme/VPImage.vue'
import { computed } from 'vue' import { computed } from 'vue'
const props = defineProps<ThemeHomeTextImage>() const { width, title, description, list, image, type, backgroundImage, backgroundAttachment, full, index } = defineProps<ThemeHomeTextImage>()
const maxWidth = computed(() => { const maxWidth = computed(() => {
const width = props.width
if (typeof width === 'number') if (typeof width === 'number')
return `${width}px` return `${width}px`
@ -19,12 +17,8 @@ const maxWidth = computed(() => {
<template> <template>
<VPHomeBox <VPHomeBox
class="vp-home-text-image" class="vp-home-text-image"
:type="type"
:background-image="backgroundImage"
:background-attachment="backgroundAttachment"
:full="full"
:container-class="{ reverse: type === 'text-image' }" :container-class="{ reverse: type === 'text-image' }"
:index="index" v-bind="{ type, backgroundAttachment, backgroundImage, full, index }"
> >
<div class="content-image"> <div class="content-image">
<VPImage :image="image" :style="{ maxWidth }" /> <VPImage :image="image" :style="{ maxWidth }" />
@ -39,7 +33,7 @@ const maxWidth = computed(() => {
<p v-if="description" class="description" v-html="description" /> <p v-if="description" class="description" v-html="description" />
<ul v-if="list && list.length" class="list"> <ul v-if="list && list.length" class="list">
<li v-for="(item, index) in list" :key="index"> <li v-for="(item, i) in list" :key="i">
<template v-if="typeof item === 'object'"> <template v-if="typeof item === 'object'">
<h3 v-if="item.title" v-html="item.title" /> <h3 v-if="item.title" v-html="item.title" />
<p v-if="item.description" v-html="item.description" /> <p v-if="item.description" v-html="item.description" />

View File

@ -11,7 +11,7 @@ import { useWindowScroll } from '@vueuse/core'
import { ref, watchPostEffect } from 'vue' import { ref, watchPostEffect } from 'vue'
import { useData, useSidebar } from '../../composables/index.js' import { useData, useSidebar } from '../../composables/index.js'
const props = defineProps<{ const { isScreenOpen } = defineProps<{
isScreenOpen: boolean isScreenOpen: boolean
}>() }>()
defineEmits<(e: 'toggleScreen') => void>() defineEmits<(e: 'toggleScreen') => void>()
@ -27,7 +27,7 @@ watchPostEffect(() => {
'has-sidebar': hasSidebar.value, 'has-sidebar': hasSidebar.value,
'home': frontmatter.value.pageLayout === 'home', 'home': frontmatter.value.pageLayout === 'home',
'top': y.value === 0, 'top': y.value === 0,
'screen-open': props.isScreenOpen, 'screen-open': isScreenOpen,
} }
}) })
</script> </script>

View File

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
defineProps<{ const { active } = defineProps<{
active: boolean active: boolean
}>() }>()

View File

@ -9,7 +9,7 @@ import { resolveRouteFullPath } from 'vuepress/client'
import { useData } from '../../composables/index.js' import { useData } from '../../composables/index.js'
import { isActive } from '../../utils/index.js' import { isActive } from '../../utils/index.js'
const props = defineProps<{ const { item } = defineProps<{
item: ResolvedNavItemWithChildren item: ResolvedNavItemWithChildren
}>() }>()
@ -20,14 +20,14 @@ function isChildActive(navItem: ResolvedNavItem): boolean {
return isActive( return isActive(
page.value.path, page.value.path,
resolveRouteFullPath(navItem.link), resolveRouteFullPath(navItem.link),
!!props.item.activeMatch, !!item.activeMatch,
) )
} }
else { else {
return navItem.items.some(isChildActive) return navItem.items.some(isChildActive)
} }
} }
const childrenActive = computed(() => isChildActive(props.item)) const childrenActive = computed(() => isChildActive(item))
</script> </script>
<template> <template>

View File

@ -7,7 +7,7 @@ import { resolveRouteFullPath } from 'vuepress/client'
import { useData } from '../../composables/index.js' import { useData } from '../../composables/index.js'
import { isActive } from '../../utils/index.js' import { isActive } from '../../utils/index.js'
defineProps<{ const { item } = defineProps<{
item: ResolvedNavItemWithLink item: ResolvedNavItemWithLink
}>() }>()

View File

@ -8,7 +8,7 @@ import { inBrowser } from '../../utils/index.js'
import '@vuepress/helper/transition/fade-in.css' import '@vuepress/helper/transition/fade-in.css'
defineProps<{ const { open } = defineProps<{
open: boolean open: boolean
}>() }>()

View File

@ -9,7 +9,7 @@ import { computed, ref } from 'vue'
import '@vuepress/helper/transition/fade-in-height-expand.css' import '@vuepress/helper/transition/fade-in-height-expand.css'
const props = defineProps<{ const { text, icon, badge, items } = defineProps<{
text: string text: string
icon?: ThemeIcon icon?: ThemeIcon
badge?: string | ThemeBadge badge?: string | ThemeBadge
@ -19,7 +19,7 @@ const props = defineProps<{
const isOpen = ref(false) const isOpen = ref(false)
const groupId = computed( const groupId = computed(
() => `nav-screen-menu-group-${props.text.replace(' ', '-').toLowerCase()}`, () => `nav-screen-menu-group-${text.replace(' ', '-').toLowerCase()}`,
) )
function toggle() { function toggle() {

View File

@ -5,7 +5,7 @@ import VPIcon from '@theme/VPIcon.vue'
import VPLink from '@theme/VPLink.vue' import VPLink from '@theme/VPLink.vue'
import { inject } from 'vue' import { inject } from 'vue'
defineProps<{ const { item } = defineProps<{
item: ResolvedNavItemWithLink item: ResolvedNavItemWithLink
}>() }>()

View File

@ -3,7 +3,7 @@ import type { NavItemWithLink, ThemeIcon } from '../../../shared/index.js'
import VPNavScreenMenuGroupLink from '@theme/Nav/VPNavScreenMenuGroupLink.vue' import VPNavScreenMenuGroupLink from '@theme/Nav/VPNavScreenMenuGroupLink.vue'
import VPIcon from '@theme/VPIcon.vue' import VPIcon from '@theme/VPIcon.vue'
defineProps<{ const { icon, text, items } = defineProps<{
icon?: ThemeIcon icon?: ThemeIcon
text?: string text?: string
items: NavItemWithLink[] items: NavItemWithLink[]

View File

@ -5,7 +5,7 @@ import VPIcon from '@theme/VPIcon.vue'
import VPLink from '@theme/VPLink.vue' import VPLink from '@theme/VPLink.vue'
import { inject } from 'vue' import { inject } from 'vue'
defineProps<{ const { item } = defineProps<{
item: ResolvedNavItemWithLink item: ResolvedNavItemWithLink
}>() }>()

View File

@ -3,12 +3,10 @@ import type { CategoryItem, CategoryItemWithPost } from '../../composables/index
import VPCategoriesGroup from '@theme/Posts/VPCategoriesGroup.vue' import VPCategoriesGroup from '@theme/Posts/VPCategoriesGroup.vue'
import VPLink from '@theme/VPLink.vue' import VPLink from '@theme/VPLink.vue'
withDefaults(defineProps<{ const { items, depth = 0 } = defineProps<{
items: (CategoryItem | CategoryItemWithPost)[] items: (CategoryItem | CategoryItemWithPost)[]
depth?: number depth?: number
}>(), { }>()
depth: 0,
})
</script> </script>
<template> <template>

View File

@ -5,12 +5,11 @@ import { computed, onMounted, ref, watch } from 'vue'
import { useRoute } from 'vuepress/client' import { useRoute } from 'vuepress/client'
import { useData } from '../../composables/index.js' import { useData } from '../../composables/index.js'
const props = withDefaults(defineProps<{ const { item, depth = 0 } = defineProps<{
item: CategoryItem item: CategoryItem
depth?: number depth?: number
}>(), { }>()
depth: 0,
})
const { collection } = useData<'page', 'post'>() const { collection } = useData<'page', 'post'>()
const route = useRoute() const route = useRoute()
const el = ref<HTMLDivElement | null>(null) const el = ref<HTMLDivElement | null>(null)
@ -28,16 +27,16 @@ const expandDepth = computed(() => {
}) })
watch( watch(
() => [route.query, props.item, expandDepth.value], () => [route.query, item, expandDepth.value],
() => { () => {
const id = route.query.id as string const id = route.query.id as string
if (!id) { if (!id) {
expand.value = props.depth <= expandDepth.value expand.value = depth <= expandDepth.value
} }
else { else {
expand.value = hasExpand(props.item, id) expand.value = hasExpand(item, id)
} }
isExpand.value = id ? props.item.id === id : false isExpand.value = id ? item.id === id : false
}, },
{ immediate: true }, { immediate: true },
) )

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useData } from '../../composables/index.js' import { useData } from '../../composables/index.js'
defineProps<{ const { page, isFirstPage, isLastPage, pageRange } = defineProps<{
page: number page: number
totalPage: number totalPage: number
isFirstPage: boolean isFirstPage: boolean

View File

@ -6,7 +6,7 @@ import { computed, onMounted, ref } from 'vue'
import { withBase } from 'vuepress/client' import { withBase } from 'vuepress/client'
import { useData, useInternalLink, useTagColors } from '../../composables/index.js' import { useData, useInternalLink, useTagColors } from '../../composables/index.js'
const props = defineProps<{ const { post, index } = defineProps<{
post: ThemePostsItem post: ThemePostsItem
index: number index: number
}>() }>()
@ -14,9 +14,9 @@ const props = defineProps<{
const isMobile = ref(false) const isMobile = ref(false)
onMounted(() => { onMounted(() => {
isMobile.value = _isMobile(navigator.userAgent) isMobile.value = _isMobile()
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
isMobile.value = _isMobile(navigator.userAgent) isMobile.value = _isMobile()
}) })
}) })
@ -24,15 +24,15 @@ const { collection } = useData<'page', 'post'>()
const colors = useTagColors() const colors = useTagColors()
const { categories: categoriesLink, tags: tagsLink } = useInternalLink() const { categories: categoriesLink, tags: tagsLink } = useInternalLink()
const createTime = computed(() => props.post.createTime?.split(/\s|T/)[0].replace(/\//g, '-')) const createTime = computed(() => post.createTime?.split(/\s|T/)[0].replace(/\//g, '-'))
const categoryList = computed(() => props.post.categoryList ?? []) const categoryList = computed(() => post.categoryList ?? [])
const sticky = computed(() => { const sticky = computed(() => {
if (typeof props.post.sticky === 'boolean') { if (typeof post.sticky === 'boolean') {
return props.post.sticky return post.sticky
} }
else if (typeof props.post.sticky === 'number') { else if (typeof post.sticky === 'number') {
return props.post.sticky >= 0 return post.sticky >= 0
} }
return false return false
}) })
@ -40,7 +40,7 @@ const sticky = computed(() => {
const tags = computed(() => { const tags = computed(() => {
const tagTheme = collection.value?.tagsTheme ?? 'colored' const tagTheme = collection.value?.tagsTheme ?? 'colored'
return (props.post.tags ?? []) return (post.tags ?? [])
.slice(0, 4) .slice(0, 4)
.map(tag => ({ .map(tag => ({
name: tag, name: tag,
@ -49,18 +49,18 @@ const tags = computed(() => {
}) })
const cover = computed<PostsCoverStyle | null>(() => { const cover = computed<PostsCoverStyle | null>(() => {
if (!props.post.cover) if (!post.cover)
return null return null
const opt = collection.value?.postCover ?? 'right' const opt = collection.value?.postCover ?? 'right'
const options = typeof opt === 'string' ? { layout: opt } : opt const options = typeof opt === 'string' ? { layout: opt } : opt
return { layout: 'right', ratio: '4:3', ...options, ...props.post.coverStyle } return { layout: 'right', ratio: '4:3', ...options, ...post.coverStyle }
}) })
const coverLayout = computed(() => { const coverLayout = computed(() => {
if (isMobile.value) if (isMobile.value)
return 'top' return 'top'
const layout = cover.value?.layout ?? 'right' const layout = cover.value?.layout ?? 'right'
const odd = (props.index + 1) % 2 === 1 const odd = (index + 1) % 2 === 1
if (layout === 'odd-left') if (layout === 'odd-left')
return odd ? 'left' : 'right' return odd ? 'left' : 'right'
if (layout === 'odd-right') if (layout === 'odd-right')
@ -69,7 +69,7 @@ const coverLayout = computed(() => {
}) })
const coverCompact = computed(() => { const coverCompact = computed(() => {
if (props.post.excerpt || coverLayout.value === 'top') if (post.excerpt || coverLayout.value === 'top')
return false return false
return cover.value?.compact ?? false return cover.value?.compact ?? false
}) })

View File

@ -5,7 +5,7 @@ import VPTransitionDrop from '@theme/VPTransitionDrop.vue'
import { computed } from 'vue' import { computed } from 'vue'
import { usePostListControl } from '../../composables/index.js' import { usePostListControl } from '../../composables/index.js'
const props = defineProps<{ const { homePosts } = defineProps<{
homePosts?: boolean homePosts?: boolean
}>() }>()
@ -18,7 +18,7 @@ const {
isFirstPage, isFirstPage,
isPaginationEnabled, isPaginationEnabled,
changePage, changePage,
} = usePostListControl(computed(() => !!props.homePosts)) } = usePostListControl(computed(() => !!homePosts))
</script> </script>
<template> <template>

View File

@ -10,7 +10,7 @@ import VPTransitionFadeSlideY from '@theme/VPTransitionFadeSlideY.vue'
import { onBeforeUnmount, watch } from 'vue' import { onBeforeUnmount, watch } from 'vue'
import { forceUpdateCollection, useData } from '../../composables/index.js' import { forceUpdateCollection, useData } from '../../composables/index.js'
const props = defineProps<{ const { homePosts, collection } = defineProps<{
homePosts?: boolean homePosts?: boolean
type?: string type?: string
onlyOnce?: boolean onlyOnce?: boolean
@ -20,8 +20,8 @@ const props = defineProps<{
const { theme, page } = useData() const { theme, page } = useData()
watch( watch(
() => [props.homePosts, props.collection], () => [homePosts, collection],
() => forceUpdateCollection(props.homePosts ? (props.collection || true) : undefined), () => forceUpdateCollection(homePosts ? (collection || true) : undefined),
{ immediate: true }, { immediate: true },
) )

View File

@ -3,7 +3,7 @@ import VPLink from '@theme/VPLink.vue'
import { useRoute } from 'vuepress/client' import { useRoute } from 'vuepress/client'
import { usePostsExtract } from '../../composables/index.js' import { usePostsExtract } from '../../composables/index.js'
const props = defineProps<{ const { isLocal } = defineProps<{
isLocal?: boolean isLocal?: boolean
}>() }>()
@ -13,7 +13,7 @@ const { hasPostsExtract, tags, archives, categories } = usePostsExtract()
</script> </script>
<template> <template>
<div v-if="hasPostsExtract" class="vp-posts-nav" :class="{ local: props.isLocal }"> <div v-if="hasPostsExtract" class="vp-posts-nav" :class="{ local: isLocal }">
<VPLink <VPLink
v-if="tags.link" v-if="tags.link"
class="nav-link" class="nav-link"

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import VPLink from '@theme/VPLink.vue' import VPLink from '@theme/VPLink.vue'
defineProps<{ const { postList } = defineProps<{
postList: { postList: {
title: string title: string
path: string path: string

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import '@vuepress/helper/transition/fade-in.css' import '@vuepress/helper/transition/fade-in.css'
defineProps<{ const { show } = defineProps<{
show: boolean show: boolean
}>() }>()
</script> </script>

View File

@ -9,7 +9,7 @@ import { useRoute } from 'vuepress/client'
import { useData, usePostsPageData, useSidebar } from '../composables/index.js' import { useData, usePostsPageData, useSidebar } from '../composables/index.js'
import { inBrowser } from '../utils/index.js' import { inBrowser } from '../utils/index.js'
const props = defineProps<{ const { isNotFound } = defineProps<{
isNotFound?: boolean isNotFound?: boolean
}>() }>()
@ -43,7 +43,7 @@ watch(
<template> <template>
<div <div
id="VPContent" vp-content class="vp-content" :class="{ id="VPContent" vp-content class="vp-content" :class="{
'has-sidebar': hasSidebar && !props.isNotFound, 'has-sidebar': hasSidebar && !isNotFound,
'is-home': frontmatter.pageLayout === 'home', 'is-home': frontmatter.pageLayout === 'home',
}" }"
> >

View File

@ -2,7 +2,7 @@
import { computed } from 'vue' import { computed } from 'vue'
import { useData } from '../composables/index.js' import { useData } from '../composables/index.js'
defineProps<{ const { title, anchor } = defineProps<{
title?: string title?: string
anchor: string anchor: string
}>() }>()

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { MenuItem } from '../composables/index.js' import type { MenuItem } from '../composables/index.js'
defineProps<{ const { headers, root } = defineProps<{
headers: MenuItem[] headers: MenuItem[]
root?: boolean root?: boolean
}>() }>()

View File

@ -2,7 +2,7 @@
import { ref } from 'vue' import { ref } from 'vue'
import { useData, useEncryptCompare } from '../composables/index.js' import { useData, useEncryptCompare } from '../composables/index.js'
const props = defineProps<{ const { global, info } = defineProps<{
global?: boolean global?: boolean
info?: string info?: string
}>() }>()
@ -18,7 +18,7 @@ async function onSubmit() {
if (unlocking.value) if (unlocking.value)
return return
const compare = props.global ? compareGlobal : comparePage const compare = global ? compareGlobal : comparePage
unlocking.value = true unlocking.value = true
const result = await compare(password.value) const result = await compare(password.value)
unlocking.value = false unlocking.value = false

View File

@ -6,7 +6,7 @@ import VPMenu from '@theme/VPMenu.vue'
import { ref } from 'vue' import { ref } from 'vue'
import { useFlyout } from '../composables/index.js' import { useFlyout } from '../composables/index.js'
defineProps<{ const { prefixIcon, icon, button, label, items, badge } = defineProps<{
prefixIcon?: ThemeIcon prefixIcon?: ThemeIcon
icon?: any icon?: any
button?: string button?: string

View File

@ -2,7 +2,7 @@
import type { FriendGroup } from '../../shared/index.js' import type { FriendGroup } from '../../shared/index.js'
import VPFriendsItem from '@theme/VPFriendsItem.vue' import VPFriendsItem from '@theme/VPFriendsItem.vue'
defineProps<{ const { group } = defineProps<{
group: FriendGroup group: FriendGroup
}>() }>()
</script> </script>

View File

@ -6,7 +6,7 @@ import { computed } from 'vue'
import { useDarkMode } from '../composables/index.js' import { useDarkMode } from '../composables/index.js'
import VPSocialLinks from './VPSocialLinks.vue' import VPSocialLinks from './VPSocialLinks.vue'
const props = defineProps<{ const { friend } = defineProps<{
friend: FriendsItem friend: FriendsItem
}>() }>()
@ -21,9 +21,9 @@ function getStyle(name: string, color?: string | { light: string, dark: string }
const friendStyle = computed(() => { const friendStyle = computed(() => {
return { return {
...getStyle('--vp-friends-text-color', props.friend.color), ...getStyle('--vp-friends-text-color', friend.color),
...getStyle('--vp-friends-bg-color', props.friend.backgroundColor), ...getStyle('--vp-friends-bg-color', friend.backgroundColor),
...getStyle('--vp-friends-name-color', props.friend.nameColor), ...getStyle('--vp-friends-name-color', friend.nameColor),
} }
}) })
</script> </script>

View File

@ -6,7 +6,7 @@ import VPIconImage from '@theme/VPIconImage.vue'
import { computed } from 'vue' import { computed } from 'vue'
import { isLinkHttp } from 'vuepress/shared' import { isLinkHttp } from 'vuepress/shared'
const props = defineProps<{ const { provider, name, size, color, extra } = defineProps<{
provider?: 'iconify' | 'iconfont' | 'fontawesome' provider?: 'iconify' | 'iconfont' | 'fontawesome'
name: string | { svg: string } name: string | { svg: string }
size?: string | number size?: string | number
@ -18,20 +18,20 @@ declare const __MD_POWER_ICON_PROVIDER__: 'iconify' | 'iconfont' | 'fontawesome'
declare const __MD_POWER_ICON_PREFIX__: string declare const __MD_POWER_ICON_PREFIX__: string
const type = computed(() => { const type = computed(() => {
const provider = props.provider || __MD_POWER_ICON_PROVIDER__
// name -> https://example.com/icon.svg // name -> https://example.com/icon.svg
// name -> /icon.svg // name -> /icon.svg
if (typeof props.name === 'string' && (isLinkHttp(props.name) || props.name[0] === '/')) { if (typeof name === 'string' && (isLinkHttp(name) || name[0] === '/')) {
return 'link' return 'link'
} }
// name -> { svg: '<svg></svg>' } // name -> { svg: '<svg></svg>' }
if (typeof props.name === 'object' && !!props.name.svg) { if (typeof name === 'object' && !!name.svg) {
return 'svg' return 'svg'
} }
if (provider === 'iconfont' || provider === 'fontawesome') { const _provider = provider || __MD_POWER_ICON_PROVIDER__
return provider if (_provider === 'iconfont' || _provider === 'fontawesome') {
return _provider
} }
return 'iconify' return 'iconify'
@ -43,8 +43,7 @@ function parseSize(size: string | number): string {
return String(size) return String(size)
} }
const size = computed(() => { const rect = computed(() => {
const size = props.size
if (!size) if (!size)
return undefined return undefined
@ -56,9 +55,9 @@ const size = computed(() => {
return { width, height: height || width } return { width, height: height || width }
}) })
const binding = computed(() => ({ const binding = computed(() => ({
name: props.name as string, name: name as string,
color: props.color, color,
size: size.value, size: rect.value,
prefix: __MD_POWER_ICON_PREFIX__ as any, prefix: __MD_POWER_ICON_PREFIX__ as any,
})) }))
</script> </script>
@ -66,7 +65,7 @@ const binding = computed(() => ({
<template> <template>
<VPIconImage <VPIconImage
v-if="type === 'link' || type === 'svg'" v-if="type === 'link' || type === 'svg'"
:type="type" :name="name" :color="color" :size="size" :type="type" :name="name" :color="color" :size="rect"
/> />
<VPIconfont <VPIconfont
v-else-if="type === 'iconfont'" v-else-if="type === 'iconfont'"

View File

@ -2,7 +2,7 @@
import type { FontAwesomePrefix } from 'vuepress-plugin-md-power/client' import type { FontAwesomePrefix } from 'vuepress-plugin-md-power/client'
import { computed } from 'vue' import { computed } from 'vue'
const props = defineProps<{ const { name, size, color, prefix, extra } = defineProps<{
name: string name: string
size?: { width?: string, height?: string } size?: { width?: string, height?: string }
color?: string color?: string
@ -25,20 +25,19 @@ const configs: Record<string, FontAwesomePrefix[]> = {
} }
const iconName = computed(() => { const iconName = computed(() => {
const icon = props.name.includes(':') ? props.name : `${props.prefix || 'fas'}:${props.name}` const icon = name.includes(':') ? name : `${prefix || 'fas'}:${name}`
const [type, name] = icon.split(':') const [type, iconName] = icon.split(':')
let prefix = 'solid' let _prefix = 'solid'
for (const [key, alias] of Object.entries(configs)) { for (const [key, alias] of Object.entries(configs)) {
if (alias.includes(type as FontAwesomePrefix)) { if (alias.includes(type as FontAwesomePrefix)) {
prefix = key _prefix = key
break break
} }
} }
return `${prefix.split(' ').map(v => `fa-${v.trim()}`).join(' ')} fa-${name}` return `${_prefix.split(' ').map(v => `fa-${v.trim()}`).join(' ')} fa-${iconName}`
}) })
const extraClasses = computed(() => { const extraClasses = computed(() => {
const extra = props.extra
if (!extra) if (!extra)
return [] return []
return extra.split(' ').map(v => v.trim().startsWith('fa-') ? v : `fa-${v}`) return extra.split(' ').map(v => v.trim().startsWith('fa-') ? v : `fa-${v}`)

View File

@ -3,7 +3,7 @@ import { computed } from 'vue'
import { withBase } from 'vuepress/client' import { withBase } from 'vuepress/client'
import { isLinkHttp } from 'vuepress/shared' import { isLinkHttp } from 'vuepress/shared'
const props = defineProps<{ const { type, name, color, size } = defineProps<{
type: 'link' | 'svg' type: 'link' | 'svg'
name: string | { svg: string } name: string | { svg: string }
color?: string color?: string
@ -11,14 +11,14 @@ const props = defineProps<{
}>() }>()
const svg = computed(() => { const svg = computed(() => {
if (props.type === 'svg' && typeof props.name === 'object' && 'svg' in props.name) { if (type === 'svg' && typeof name === 'object' && 'svg' in name) {
return props.name.svg return name.svg
} }
return '' return ''
}) })
const link = computed(() => { const link = computed(() => {
if (props.type === 'link') { if (type === 'link') {
const link = props.name as string const link = name as string
return isLinkHttp(link) ? link : withBase(link) return isLinkHttp(link) ? link : withBase(link)
} }
return '' return ''

View File

@ -1,20 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' const { name, size, color, prefix } = defineProps<{
const props = defineProps<{
name: string name: string
size?: { width?: string, height?: string } size?: { width?: string, height?: string }
color?: string color?: string
prefix?: string prefix?: string
}>() }>()
const prefix = computed(() => props.prefix || 'iconfont icon-')
</script> </script>
<template> <template>
<i <i
class="vp-icon" class="vp-icon"
:class="`${prefix}${name}`" :class="`${prefix || 'iconfont icon-'}${name}`"
:style="{ color, 'font-size': size?.height || '1em' }" :style="{ color, 'font-size': size?.height || '1em' }"
data-provider="iconfont" data-provider="iconfont"
aria-hidden aria-hidden

View File

@ -9,7 +9,7 @@ defineOptions({
inheritAttrs: false, inheritAttrs: false,
}) })
const props = defineProps<{ const { name, size, color, prefix, extra } = defineProps<{
name: string name: string
size?: { width?: string, height?: string } size?: { width?: string, height?: string }
color?: string color?: string
@ -23,10 +23,9 @@ const loaded = ref(false)
const iconsData = useIconsData() const iconsData = useIconsData()
const iconName = computed(() => { const iconName = computed(() => {
const name = props.name
if (name.includes(':')) if (name.includes(':'))
return name return name
return props.prefix ? `${props.prefix}:${name}` : name return prefix ? `${prefix}:${name}` : name
}) })
const localIconName = computed(() => iconsData.value[iconName.value]) const localIconName = computed(() => iconsData.value[iconName.value])
@ -37,13 +36,13 @@ async function loadRemoteIcon() {
if (!localIconName.value) { if (!localIconName.value) {
loaded.value = false loaded.value = false
icon.value = await loadIcon(props.name) icon.value = await loadIcon(name)
} }
loaded.value = true loaded.value = true
} }
if (!__VUEPRESS_SSR__) if (!__VUEPRESS_SSR__)
watch(() => props.name, loadRemoteIcon, { immediate: true }) watch(() => name, loadRemoteIcon, { immediate: true })
</script> </script>
<template> <template>

View File

@ -4,13 +4,12 @@ import { computed } from 'vue'
import { withBase } from 'vuepress/client' import { withBase } from 'vuepress/client'
import { numToUnit } from '../utils/index.js' import { numToUnit } from '../utils/index.js'
const props = defineProps<{ const { image, alt } = defineProps<{
image: ThemeImage image: ThemeImage
alt?: string alt?: string
}>() }>()
const styles = computed(() => { const styles = computed(() => {
const image = props.image
if (!image || typeof image === 'string') if (!image || typeof image === 'string')
return '' return ''
if (!image.width || !image.height) if (!image.width || !image.height)

View File

@ -4,7 +4,7 @@ import { useWindowScroll } from '@vueuse/core'
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
import { useData, useHeaders, usePostsPageData, useSidebar } from '../composables/index.js' import { useData, useHeaders, usePostsPageData, useSidebar } from '../composables/index.js'
const props = defineProps<{ const { open, showOutline } = defineProps<{
open: boolean open: boolean
showOutline: boolean showOutline: boolean
}>() }>()
@ -39,7 +39,7 @@ const classes = computed(() => {
'fixed': empty.value, 'fixed': empty.value,
'reached-top': y.value >= navHeight.value, 'reached-top': y.value >= navHeight.value,
'is-posts': isPosts.value && !isPostsLayout.value, 'is-posts': isPosts.value && !isPostsLayout.value,
'with-outline': !props.showOutline, 'with-outline': !showOutline,
} }
}) })

View File

@ -7,7 +7,7 @@ import { useData } from '../composables/index.js'
import '@vuepress/helper/transition/fade-in-scale-up.css' import '@vuepress/helper/transition/fade-in-scale-up.css'
const props = defineProps<{ const { headers, navHeight } = defineProps<{
headers: MenuItem[] headers: MenuItem[]
navHeight: number navHeight: number
}>() }>()
@ -18,7 +18,7 @@ const vh = ref(0)
const items = ref<HTMLDivElement>() const items = ref<HTMLDivElement>()
const btn = ref<HTMLButtonElement>() const btn = ref<HTMLButtonElement>()
watch(() => props.headers, () => { watch(() => headers, () => {
open.value = false open.value = false
}) })
@ -28,7 +28,7 @@ onClickOutside(items, () => {
function toggle() { function toggle() {
open.value = !open.value open.value = !open.value
vh.value = window.innerHeight + Math.min(window.scrollY - props.navHeight, 0) vh.value = window.innerHeight + Math.min(window.scrollY - navHeight, 0)
} }
function onItemClick(e: Event) { function onItemClick(e: Event) {

View File

@ -2,7 +2,7 @@
import VPMenuGroup from '@theme/VPMenuGroup.vue' import VPMenuGroup from '@theme/VPMenuGroup.vue'
import VPMenuLink from '@theme/VPMenuLink.vue' import VPMenuLink from '@theme/VPMenuLink.vue'
defineProps<{ const { items } = defineProps<{
items?: any[] items?: any[]
}>() }>()
</script> </script>

View File

@ -2,7 +2,7 @@
import VPIcon from '@theme/VPIcon.vue' import VPIcon from '@theme/VPIcon.vue'
import VPMenuLink from '@theme/VPMenuLink.vue' import VPMenuLink from '@theme/VPMenuLink.vue'
defineProps<{ const { text, icon, items } = defineProps<{
text?: string text?: string
icon?: string | { svg: string } icon?: string | { svg: string }
items: any[] items: any[]

View File

@ -6,7 +6,7 @@ import { resolveRouteFullPath } from 'vuepress/client'
import { useData } from '../composables/index.js' import { useData } from '../composables/index.js'
import { isActive } from '../utils/index.js' import { isActive } from '../utils/index.js'
defineProps<{ const { item } = defineProps<{
item: any item: any
}>() }>()

View File

@ -7,7 +7,7 @@ import { useRoutePath } from 'vuepress/client'
import { useData, useSidebar } from '../composables/index.js' import { useData, useSidebar } from '../composables/index.js'
import { inBrowser } from '../utils/index.js' import { inBrowser } from '../utils/index.js'
const props = defineProps<{ const { open } = defineProps<{
open: boolean open: boolean
}>() }>()
@ -20,9 +20,9 @@ const navEl = ref<HTMLElement | null>(null)
const isLocked = useScrollLock(inBrowser ? document.body : null) const isLocked = useScrollLock(inBrowser ? document.body : null)
watch( watch(
[() => props.open, navEl], [() => open, navEl],
() => { () => {
if (props.open) { if (open) {
isLocked.value = true isLocked.value = true
navEl.value?.focus() navEl.value?.focus()
} }

View File

@ -3,7 +3,7 @@ import type { ResolvedSidebarItem } from '../../shared/index.js'
import VPSidebarItem from '@theme/VPSidebarItem.vue' import VPSidebarItem from '@theme/VPSidebarItem.vue'
import { onBeforeUnmount, onMounted, ref } from 'vue' import { onBeforeUnmount, onMounted, ref } from 'vue'
defineProps<{ const { items } = defineProps<{
items: ResolvedSidebarItem[] items: ResolvedSidebarItem[]
}>() }>()

View File

@ -9,7 +9,7 @@ import { useSidebarControl } from '../composables/index.js'
import '@vuepress/helper/transition/fade-in-height-expand.css' import '@vuepress/helper/transition/fade-in-height-expand.css'
const props = defineProps<{ const { item, depth } = defineProps<{
item: ResolvedSidebarItem item: ResolvedSidebarItem
depth: number depth: number
}>() }>()
@ -22,7 +22,7 @@ const {
hasActiveLink, hasActiveLink,
hasChildren, hasChildren,
toggle, toggle,
} = useSidebarControl(computed(() => props.item)) } = useSidebarControl(computed(() => item))
const sectionTag = computed(() => (hasChildren.value ? 'section' : `div`)) const sectionTag = computed(() => (hasChildren.value ? 'section' : `div`))
@ -31,17 +31,17 @@ const linkTag = computed(() => (isLink.value ? 'a' : 'div'))
const textTag = computed(() => { const textTag = computed(() => {
return !hasChildren.value return !hasChildren.value
? 'p' ? 'p'
: props.depth + 2 === 7 : depth + 2 === 7
? 'p' ? 'p'
: `h${props.depth + 2}` : `h${depth + 2}`
}) })
const itemRole = computed(() => (isLink.value ? undefined : 'button')) const itemRole = computed(() => (isLink.value ? undefined : 'button'))
const isSeparator = computed(() => props.item.link?.startsWith('---')) const isSeparator = computed(() => item.link?.startsWith('---'))
const classes = computed(() => [ const classes = computed(() => [
[`level-${props.depth}`], [`level-${depth}`],
{ collapsible: collapsible.value }, { collapsible: collapsible.value },
{ collapsed: collapsed.value }, { collapsed: collapsed.value },
{ 'is-link': isLink.value }, { 'is-link': isLink.value },
@ -53,13 +53,13 @@ function onItemInteraction(e: MouseEvent | Event) {
if ('key' in e && e.key !== 'Enter') if ('key' in e && e.key !== 'Enter')
return return
if (!props.item.link) { if (!item.link) {
toggle() toggle()
} }
} }
function onCaretClick() { function onCaretClick() {
if (props.item.link) { if (item.link) {
toggle() toggle()
} }
} }

View File

@ -2,16 +2,16 @@
import type { SocialLinkIcon } from '../../shared/index.js' import type { SocialLinkIcon } from '../../shared/index.js'
import { computed } from 'vue' import { computed } from 'vue'
const props = defineProps<{ const { icon, link, ariaLabel } = defineProps<{
icon: SocialLinkIcon icon: SocialLinkIcon
link: string link: string
ariaLabel?: string ariaLabel?: string
}>() }>()
const svg = computed(() => { const svg = computed(() => {
if (typeof props.icon === 'object') if (typeof icon === 'object')
return props.icon.svg return icon.svg
return `<span class="vpi-social-${props.icon}" />` return `<span class="vpi-social-${icon}" />`
}) })
</script> </script>

View File

@ -2,7 +2,7 @@
import type { SocialLink as SocialLinkType } from '../../shared/index.js' import type { SocialLink as SocialLinkType } from '../../shared/index.js'
import VPSocialLink from '@theme/VPSocialLink.vue' import VPSocialLink from '@theme/VPSocialLink.vue'
defineProps<{ const { links } = defineProps<{
links: SocialLinkType[] links: SocialLinkType[]
}>() }>()
</script> </script>

View File

@ -7,10 +7,7 @@ interface Props {
duration?: number duration?: number
appear?: boolean appear?: boolean
} }
const props = withDefaults(defineProps<Props>(), { const { delay = 0, duration = 0.25, appear } = defineProps<Props>()
delay: 0,
duration: 0.25,
})
const { theme } = useData() const { theme } = useData()
@ -36,7 +33,7 @@ function setStyle(item: Element) {
_transition = value && !value.includes('all') ? `${value || ''}, ` : ' ' _transition = value && !value.includes('all') ? `${value || ''}, ` : ' '
} }
el.style.transition = `${_transition}transform ${props.duration}s ease-in-out ${props.delay}s, opacity ${props.duration}s ease-in-out ${props.delay}s` el.style.transition = `${_transition}transform ${duration}s ease-in-out ${delay}s, opacity ${duration}s ease-in-out ${delay}s`
} }
function unsetStyle(item: Element) { function unsetStyle(item: Element) {

View File

@ -2,17 +2,14 @@
import type { ThemeBadge } from '../../../shared/index.js' import type { ThemeBadge } from '../../../shared/index.js'
import { computed } from 'vue' import { computed } from 'vue'
const props = withDefaults(defineProps<ThemeBadge>(), { const { type = 'tip', text, color, bgColor, borderColor = 'transparent' } = defineProps<ThemeBadge>()
type: 'tip',
borderColor: 'transparent',
})
const customStyle = computed(() => { const customStyle = computed(() => {
if (props.color || props.bgColor) { if (color || bgColor) {
return { return {
color: props.color, color,
backgroundColor: props.bgColor, backgroundColor: bgColor,
borderColor: props.borderColor, borderColor,
} }
} }
return {} return {}

View File

@ -2,19 +2,19 @@
import VPIcon from '@theme/VPIcon.vue' import VPIcon from '@theme/VPIcon.vue'
import { computed } from 'vue' import { computed } from 'vue'
const props = defineProps<{ const { title, icon = '' } = defineProps<{
title?: string title?: string
icon?: string | { svg: string } icon?: string | { svg: string }
}>() }>()
const icon = computed<string | { svg: string } | undefined>(() => { const iconName = computed<string | { svg: string }>(() => {
if (props.icon?.[0] === '{') { if (typeof icon === 'string' && icon?.[0] === '{') {
try { try {
return JSON.parse(icon) as { svg: string } return JSON.parse(icon) as { svg: string }
} }
catch {} catch {}
} }
return props.icon return icon
}) })
</script> </script>
@ -22,7 +22,7 @@ const icon = computed<string | { svg: string } | undefined>(() => {
<article class="vp-card-wrapper"> <article class="vp-card-wrapper">
<slot name="title"> <slot name="title">
<p v-if="title || icon" class="title"> <p v-if="title || icon" class="title">
<VPIcon v-if="icon" :name="icon" /> <VPIcon v-if="icon" :name="iconName" />
<span v-if="title" class="text" v-html="title" /> <span v-if="title" class="text" v-html="title" />
</p> </p>
</slot> </slot>

View File

@ -2,7 +2,7 @@
import { useMediaQuery } from '@vueuse/core' import { useMediaQuery } from '@vueuse/core'
import { onMounted, ref, toValue, watch } from 'vue' import { onMounted, ref, toValue, watch } from 'vue'
const props = defineProps<{ const { cols } = defineProps<{
cols?: string | number | { sm?: number, md?: number, lg?: number } cols?: string | number | { sm?: number, md?: number, lg?: number }
}>() }>()
@ -12,13 +12,13 @@ const repeat = ref(1)
function resolveCols() { function resolveCols() {
const reset = { sm: 1, md: 2, lg: 2 } const reset = { sm: 1, md: 2, lg: 2 }
if (!props.cols) if (!cols)
return reset return reset
if (typeof props.cols === 'number' || typeof props.cols === 'string') { if (typeof cols === 'number' || typeof cols === 'string') {
const cols = Number(props.cols) const res = Number(cols)
return { sm: cols, md: cols, lg: cols } return { sm: res, md: res, lg: res }
} }
return { ...reset, ...toValue(props.cols) } return { ...reset, ...toValue(cols) }
} }
function getRepeat() { function getRepeat() {
@ -30,7 +30,7 @@ function getRepeat() {
return cols.sm return cols.sm
} }
watch(() => [md.value, lg.value, props.cols], () => { watch([md, lg, () => cols], () => {
repeat.value = getRepeat() repeat.value = getRepeat()
}) })

View File

@ -3,13 +3,10 @@ import type { VNode } from 'vue'
import { useDebounceFn, useMediaQuery, useResizeObserver } from '@vueuse/core' import { useDebounceFn, useMediaQuery, useResizeObserver } from '@vueuse/core'
import { cloneVNode, computed, markRaw, mergeProps, nextTick, onMounted, ref, shallowRef, useId, watch } from 'vue' import { cloneVNode, computed, markRaw, mergeProps, nextTick, onMounted, ref, shallowRef, useId, watch } from 'vue'
const props = withDefaults(defineProps<{ const { cols = { sm: 2, md: 2, lg: 3 }, gap = 16 } = defineProps<{
cols?: number | { sm?: number, md?: number, lg?: number } cols?: number | { sm?: number, md?: number, lg?: number }
gap?: number gap?: number
}>(), { }>()
cols: () => ({ sm: 2, md: 2, lg: 3 }),
gap: 16,
})
const slots = defineSlots<{ default: () => VNode[] | null }>() const slots = defineSlots<{ default: () => VNode[] | null }>()
const uuid = useId() const uuid = useId()
@ -29,16 +26,16 @@ const rawList = computed(() => {
function resolveColumnsLength() { function resolveColumnsLength() {
let length = 1 let length = 1
if (typeof props.cols === 'number') { if (typeof cols === 'number') {
length = props.cols length = cols
} }
else if (typeof props.cols === 'object') { else if (typeof cols === 'object') {
if (isLg.value) if (isLg.value)
length = props.cols.lg || 3 length = cols.lg || 3
else if (isMd.value) else if (isMd.value)
length = props.cols.md || 2 length = cols.md || 2
else else
length = props.cols.sm || 2 length = cols.sm || 2
} }
columnsLength.value = Number(length) columnsLength.value = Number(length)
@ -62,7 +59,7 @@ async function drawColumns() {
const index = heights.indexOf(Math.min(...heights)) const index = heights.indexOf(Math.min(...heights))
columns[index].push(item) columns[index].push(item)
heights[index] += height + props.gap heights[index] += height + gap
} }
columnsList.value = columns columnsList.value = columns
} }
@ -71,7 +68,7 @@ onMounted(() => {
if (__VUEPRESS_SSR__) if (__VUEPRESS_SSR__)
return return
watch(() => [isMd.value, isLg.value, props.cols], resolveColumnsLength, { immediate: true }) watch([isMd, isLg, () => cols], resolveColumnsLength, { immediate: true })
drawColumns() drawColumns()
const debounceDraw = useDebounceFn(drawColumns) const debounceDraw = useDebounceFn(drawColumns)
@ -81,9 +78,9 @@ onMounted(() => {
</script> </script>
<template> <template>
<div ref="masonry" class="vp-card-masonry" :class="[`cols-${columnsLength}`]" :style="{ 'grid-gap': `${props.gap}px`, '--card-masonry-cols': columnsLength }" data-allow-mismatch> <div ref="masonry" class="vp-card-masonry" :class="[`cols-${columnsLength}`]" :style="{ 'grid-gap': `${gap}px`, '--card-masonry-cols': columnsLength }" data-allow-mismatch>
<ClientOnly> <ClientOnly>
<div v-for="(column, index) in columnsList" :key="`${uuid}-${index}`" class="card-masonry-item" :style="{ gap: `${props.gap}px` }"> <div v-for="(column, index) in columnsList" :key="`${uuid}-${index}`" class="card-masonry-item" :style="{ gap: `${gap}px` }">
<component :is="item" v-for="item in column" :key="item.props!.class" /> <component :is="item" v-for="item in column" :key="item.props!.class" />
</div> </div>
</ClientOnly> </ClientOnly>

View File

@ -2,7 +2,7 @@
import { computed } from 'vue' import { computed } from 'vue'
import { usePageLang, withBase } from 'vuepress/client' import { usePageLang, withBase } from 'vuepress/client'
const props = defineProps<{ const { image, title, description, href, author, date, width, center } = defineProps<{
image: string image: string
title?: string title?: string
description?: string description?: string
@ -15,42 +15,40 @@ const props = defineProps<{
const lang = usePageLang() const lang = usePageLang()
const date = computed(() => { const dateStr = computed(() => {
if (!props.date) if (!date)
return '' return ''
const date = props.date instanceof Date ? props.date : new Date(props.date) const instance = date instanceof Date ? date : new Date(date)
const intl = new Intl.DateTimeFormat( const intl = new Intl.DateTimeFormat(
lang.value, lang.value,
{ year: 'numeric', month: 'short', day: 'numeric' }, { year: 'numeric', month: 'short', day: 'numeric' },
) )
return intl.format(date) return intl.format(instance)
}) })
const styles = computed(() => { const styles = computed(() => ({
const width = props.width width: width
? String(Number(props.width)) === String(props.width) ? String(Number(width)) === String(width)
? `${props.width}px` ? `${width}px`
: props.width : width
: undefined : undefined,
}))
return { width }
})
</script> </script>
<template> <template>
<div class="vp-image-card" :style="styles" :class="{ center }"> <div class="vp-image-card" :style="styles" :class="{ center }">
<div class="image-container"> <div class="image-container">
<img :src="withBase(image)" :alt="title" loading="lazy"> <img :src="withBase(image)" :alt="title" loading="lazy">
<div v-if="title || author || date || description" class="image-info"> <div v-if="title || author || dateStr || description" class="image-info">
<h3 v-if="title" class="title"> <h3 v-if="title" class="title">
<a v-if="href" :href="href" target="_blank" rel="noopener noreferrer" class="no-icon">{{ title }}</a> <a v-if="href" :href="href" target="_blank" rel="noopener noreferrer" class="no-icon">{{ title }}</a>
<span v-else>{{ title }}</span> <span v-else>{{ title }}</span>
</h3> </h3>
<p v-if="author || date" class="copyright"> <p v-if="author || date" class="copyright">
<span v-if="author">{{ author }}</span> <span v-if="author">{{ author }}</span>
<span v-if="author && date"> | </span> <span v-if="author && dateStr"> | </span>
<span v-if="date">{{ date }}</span> <span v-if="dateStr">{{ dateStr }}</span>
</p> </p>
<p v-if="description" class="description"> <p v-if="description" class="description">
{{ description }} {{ description }}

View File

@ -2,7 +2,7 @@
import VPIcon from '@theme/VPIcon.vue' import VPIcon from '@theme/VPIcon.vue'
import VPLink from '@theme/VPLink.vue' import VPLink from '@theme/VPLink.vue'
defineProps<{ const { href, title, icon, description, target, rel } = defineProps<{
href: string href: string
title?: string title?: string
icon?: string | { svg: string } icon?: string | { svg: string }