feat(theme): add support iconify for home features icon (#186)

This commit is contained in:
pengzhanbo 2024-09-17 04:33:23 +08:00 committed by GitHub
parent 5b5409d2ef
commit 3e9ef80e3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 44 additions and 10 deletions

View File

@ -1,9 +1,21 @@
<script setup lang="ts">
import VPIcon from '@theme/VPIcon.vue'
import VPImage from '@theme/VPImage.vue'
import VPLink from '@theme/VPLink.vue'
import { isLinkAbsolute, isLinkHttp } from '@vuepress/helper/client'
import { computed } from 'vue'
import type { PlumeThemeHomeFeature } from '../../../shared/index.js'
defineProps<PlumeThemeHomeFeature>()
const props = defineProps<PlumeThemeHomeFeature>()
const ICONIFY_NAME = /^[\w-]+:[\w-]+$/
const isIconify = computed(() => {
if (typeof props.icon !== 'string' || isLinkAbsolute(props.icon) || isLinkHttp(props.icon)) {
return false
}
return ICONIFY_NAME.test(props.icon)
})
</script>
<template>
@ -31,6 +43,9 @@ defineProps<PlumeThemeHomeFeature>()
:height="icon.height || 48"
:width="icon.width || 48"
/>
<div v-else-if="icon && isIconify" class="icon">
<VPIcon :name="icon" />
</div>
<div v-else-if="icon" class="icon" v-html="icon" />
<h2 class="title" v-html="title" />
<p v-if="details" class="details" v-html="details" />

View File

@ -1,11 +1,11 @@
import { getIconContentCSS, getIconData } from '@iconify/utils'
import { isArray, isString, uniq } from '@pengzhanbo/utils'
import { isArray, uniq } from '@pengzhanbo/utils'
import { entries, isLinkAbsolute, isLinkHttp, isPlainObject } from '@vuepress/helper'
import { isPackageExists } from 'local-pkg'
import { fs } from 'vuepress/utils'
import type { App, Page } from 'vuepress'
import { interopDefault, logger, nanoid, resolveContent, writeTemp } from '../utils/index.js'
import type { NavItem, PlumeThemeLocaleOptions, Sidebar } from '../../shared/index.js'
import type { NavItem, PlumeThemeHomeConfig, PlumeThemeLocaleOptions, Sidebar } from '../../shared/index.js'
interface IconData {
className: string
@ -19,6 +19,7 @@ type IconDataMap = Record<string, IconData>
const ICON_REGEXP = /<(?:VP)?(Icon|Card|LinkCard)([^>]*)>/g
const ICON_NAME_REGEXP = /(?:name|icon)="([^"]+)"/
const URL_CONTENT_REGEXP = /(url\([\s\S]+\))/
const ICONIFY_NAME = /^[\w-]+:[\w-]+$/
const JS_FILENAME = 'internal/iconify.js'
const CSS_FILENAME = 'internal/iconify.css'
@ -28,6 +29,12 @@ let locate!: ((name: string) => any)
// { iconName: { className, content } }
const cache: IconDataMap = {}
function isIconify(icon: any): icon is string {
if (!icon || typeof icon !== 'string' || isLinkAbsolute(icon) || isLinkHttp(icon))
return false
return icon[0] !== '{' && ICONIFY_NAME.test(icon)
}
export async function prepareIcons(app: App, localeOptions: PlumeThemeLocaleOptions) {
if (!isInstalled) {
await writeTemp(app, JS_FILENAME, resolveContent(app, { name: 'icons', content: '{}' }))
@ -81,13 +88,25 @@ function getIconsWithPage(page: Page): string[] {
const list = page.contentRendered
.match(ICON_REGEXP)
?.map(match => match.match(ICON_NAME_REGEXP)?.[1])
.filter(Boolean) as string[] || []
.filter(isIconify) as string[] || []
if (page.frontmatter.icon && isString(page.frontmatter.icon)) {
list.push(page.frontmatter.icon)
const fm = page.frontmatter
if (fm.icon && isIconify(fm.icon)) {
list.push(fm.icon)
}
return list.filter(icon => !isLinkHttp(icon) && !isLinkAbsolute(icon) && icon[0] !== '{')
if ((fm.home || fm.pageLayout === 'home') && (fm.config as PlumeThemeHomeConfig[])?.length) {
for (const config of (fm.config as PlumeThemeHomeConfig[])) {
if (config.type === 'features' && config.features.length) {
for (const feature of config.features) {
if (feature.icon && isIconify(feature.icon))
list.push(feature.icon)
}
}
}
}
return list
}
function getIconWithThemeConfig(localeOptions: PlumeThemeLocaleOptions): string[] {
@ -108,14 +127,14 @@ function getIconWithThemeConfig(localeOptions: PlumeThemeLocaleOptions): string[
sidebarList.forEach(sidebar => list.push(...getIconWithSidebar(sidebar)))
})
return list
return list.filter(isIconify)
}
function getIconWithNavbar(navbar: NavItem[]): string[] {
const list: string[] = []
navbar.forEach((item) => {
if (typeof item !== 'string') {
if (typeof item.icon === 'string' && !isLinkHttp(item.icon) && !isLinkAbsolute(item.icon))
if (isIconify(item.icon))
list.push(item.icon)
if (item.items?.length)
list.push(...getIconWithNavbar(item.items))
@ -129,7 +148,7 @@ function getIconWithSidebar(sidebar: Sidebar): string[] {
if (isArray(sidebar)) {
sidebar.forEach((item) => {
if (typeof item !== 'string') {
if (typeof item.icon === 'string' && !isLinkHttp(item.icon) && !isLinkAbsolute(item.icon))
if (isIconify(item.icon))
list.push(item.icon)
if (item.items?.length)
list.push(...getIconWithSidebar(item.items))