* feat(plugin-md-power): add diff syntax in file-tree container, close #577 * chore: tweak
This commit is contained in:
parent
1bc33acb2a
commit
10708c97b4
@ -18,18 +18,20 @@ permalink: /guide/markdown/file-tree/
|
||||
|
||||
- 通过加粗文件名或目录名来突出显示,例如 `**README.md**`
|
||||
- 通过在名称后添加更多文本来为文件或目录添加注释
|
||||
- 通过在名称前添加 `++` 或 `--` 来标记文件或目录为 **新增** 或 **删除**
|
||||
- 使用 `...` 或 `…` 作为名称来添加占位符文件和目录。
|
||||
- 在 `:::file-tree` 后添加 `icon="simple"` 或 添加 `icon="colored"` 可以切换为简单图标或彩色图标,默认为彩色图标。
|
||||
- 在 `:::file-tree` 后添加 `title="xxxx"` 可以为文件树添加标题。
|
||||
|
||||
**输入:**
|
||||
|
||||
```md
|
||||
```md /++/ /--/
|
||||
::: file-tree
|
||||
|
||||
- docs
|
||||
- .vuepress
|
||||
- config.ts
|
||||
- page1.md
|
||||
- ++ config.ts
|
||||
- -- page1.md
|
||||
- README.md
|
||||
- theme 一个 **主题** 目录
|
||||
- client
|
||||
@ -55,8 +57,8 @@ permalink: /guide/markdown/file-tree/
|
||||
|
||||
- docs
|
||||
- .vuepress
|
||||
- config.ts
|
||||
- page1.md
|
||||
- ++ config.ts
|
||||
- -- page1.md
|
||||
- README.md
|
||||
- theme 一个 **主题** 目录
|
||||
- client
|
||||
|
||||
@ -71,79 +71,93 @@ exports[`fileTree > parseFileTreeRawContent > should work 1`] = `
|
||||
`;
|
||||
|
||||
exports[`fileTreePlugin > should work with default options 1`] = `
|
||||
"<div class="vp-file-tree"><FileTreeNode expanded type="folder" filename="docs">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-docs" /></template><FileTreeNode type="file" filename="README.md">
|
||||
"<div class="vp-file-tree"><FileTreeNode expanded type="folder" filename="docs" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-docs" /></template><FileTreeNode type="file" filename="README.md" :level="1">
|
||||
<template #icon><VPIcon name="flat-color-icons:info" /></template>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode type="file" filename="foo.md">
|
||||
<FileTreeNode type="file" filename="foo.md" :level="1">
|
||||
<template #icon><VPIcon name="vscode-icons:file-type-markdown" /></template>
|
||||
</FileTreeNode>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode expanded type="folder" filename="src">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-src" /></template><FileTreeNode expanded type="folder" filename="client">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-client" /></template><FileTreeNode expanded type="folder" filename="components">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-component" /></template><FileTreeNode focus type="file" filename="Navbar.vue">
|
||||
<FileTreeNode expanded type="folder" filename="src" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-src" /></template><FileTreeNode expanded type="folder" filename="client" :level="1">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-client" /></template><FileTreeNode expanded type="folder" filename="components" :level="2">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-component" /></template><FileTreeNode focus type="file" filename="Navbar.vue" :level="3">
|
||||
<template #icon><VPIcon name="vscode-icons:file-type-vue" /></template>
|
||||
</FileTreeNode>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode type="file" filename="index.ts">
|
||||
<FileTreeNode type="file" filename="index.ts" :level="2">
|
||||
<template #icon><VPIcon name="vscode-icons:file-type-typescript" /></template><template #comment># comment</template>
|
||||
</FileTreeNode>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode expanded type="folder" filename="node">
|
||||
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="index.ts">
|
||||
<FileTreeNode expanded type="folder" filename="node" :level="1">
|
||||
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="index.ts" :level="2">
|
||||
<template #icon><VPIcon name="vscode-icons:file-type-typescript" /></template>
|
||||
</FileTreeNode>
|
||||
</FileTreeNode>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode type="file" filename=".gitignore">
|
||||
<FileTreeNode type="file" filename=".gitignore" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:file-type-git" /></template>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode type="file" filename="package.json">
|
||||
<FileTreeNode type="file" filename="package.json" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:file-type-node" /></template>
|
||||
</FileTreeNode></div>
|
||||
<div class="vp-file-tree"><p class="vp-file-tree-title">files</p><FileTreeNode expanded type="folder" filename="src">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-src" /></template><FileTreeNode expanded type="folder" filename="js">
|
||||
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="…">
|
||||
<div class="vp-file-tree"><p class="vp-file-tree-title">files</p><FileTreeNode expanded type="folder" filename="src" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-src" /></template><FileTreeNode expanded type="folder" filename="js" :level="1">
|
||||
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="…" :level="2">
|
||||
|
||||
</FileTreeNode>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode type="folder" filename="vue">
|
||||
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="…">
|
||||
<FileTreeNode type="folder" filename="vue" :level="1">
|
||||
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="…" :level="2">
|
||||
|
||||
</FileTreeNode>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode type="folder" filename="css">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-css" /></template><FileTreeNode type="file" filename="…">
|
||||
<FileTreeNode type="folder" filename="css" :level="1">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-css" /></template><FileTreeNode type="file" filename="…" :level="2">
|
||||
|
||||
</FileTreeNode>
|
||||
</FileTreeNode>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode type="file" filename="README.md">
|
||||
<FileTreeNode type="file" filename="README.md" :level="0">
|
||||
<template #icon><VPIcon name="flat-color-icons:info" /></template>
|
||||
</FileTreeNode></div>
|
||||
<div class="vp-file-tree"><FileTreeNode type="file" filename="docs">
|
||||
<div class="vp-file-tree"><FileTreeNode type="file" filename="docs" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode expanded type="folder" filename="src">
|
||||
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="a.js">
|
||||
<FileTreeNode expanded type="folder" filename="src" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="a.js" :level="1">
|
||||
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode type="file" filename="b.ts">
|
||||
<FileTreeNode type="file" filename="b.ts" :level="1">
|
||||
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||
</FileTreeNode>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode type="file" filename="README.md">
|
||||
<FileTreeNode type="file" filename="README.md" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||
</FileTreeNode></div>
|
||||
<div class="vp-file-tree"><FileTreeNode type="file" filename="">
|
||||
<div class="vp-file-tree"><FileTreeNode type="file" filename="" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode expanded type="folder" filename="">
|
||||
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="">
|
||||
<FileTreeNode expanded type="folder" filename="" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:default-folder" /></template><FileTreeNode type="file" filename="" :level="1">
|
||||
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||
</FileTreeNode>
|
||||
</FileTreeNode></div>
|
||||
<div class="vp-file-tree"><FileTreeNode expanded type="folder" filename="docs" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:folder-type-docs" /></template><FileTreeNode type="file" diff="add" filename="added.md" :level="1">
|
||||
<template #icon><VPIcon name="vscode-icons:file-type-markdown" /></template>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode type="file" diff="remove" filename="remove.md" :level="1">
|
||||
<template #icon><VPIcon name="vscode-icons:file-type-markdown" /></template>
|
||||
</FileTreeNode>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode type="file" diff="add" filename="src" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||
</FileTreeNode>
|
||||
<FileTreeNode type="file" diff="remove" filename="source" :level="0">
|
||||
<template #icon><VPIcon name="vscode-icons:default-file" /></template>
|
||||
</FileTreeNode></div>
|
||||
<div class="vp-file-tree"></div>
|
||||
"
|
||||
`;
|
||||
|
||||
@ -99,6 +99,14 @@ describe('fileTreePlugin', () => {
|
||||
-
|
||||
:::
|
||||
|
||||
::: file-tree
|
||||
- docs
|
||||
- ++ added.md
|
||||
- -- remove.md
|
||||
- ++ src
|
||||
- -- source
|
||||
:::
|
||||
|
||||
::: file-tree
|
||||
:::
|
||||
`
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import { FadeInExpandTransition } from '@vuepress/helper/client'
|
||||
import { inject, ref } from 'vue'
|
||||
|
||||
import '@vuepress/helper/transition/fade-in-height-expand.css'
|
||||
|
||||
const props = defineProps<{
|
||||
type: 'file' | 'folder'
|
||||
filename: string
|
||||
level: number
|
||||
diff?: 'add' | 'remove'
|
||||
expanded?: boolean
|
||||
focus?: boolean
|
||||
}>()
|
||||
@ -49,18 +48,20 @@ function toggle(ev: MouseEvent) {
|
||||
focus,
|
||||
expanded: type === 'folder' ? active : false,
|
||||
active: type === 'file' ? activeFileTreeNode === filename : false,
|
||||
diff,
|
||||
add: diff === 'add',
|
||||
remove: diff === 'remove',
|
||||
}"
|
||||
:style="{ '--file-tree-level': -level }"
|
||||
@click="toggle"
|
||||
>
|
||||
<slot name="icon" />
|
||||
<span class="name" :class="[type]">{{ filename }}</span>
|
||||
<span v-if="$slots.comment" class="comment"><slot name="comment" /></span>
|
||||
</p>
|
||||
<FadeInExpandTransition>
|
||||
<div v-if="type === 'folder'" v-show="active" class="group">
|
||||
<slot />
|
||||
</div>
|
||||
</FadeInExpandTransition>
|
||||
<div v-if="type === 'folder'" v-show="active" class="group">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -100,24 +101,40 @@ function toggle(ev: MouseEvent) {
|
||||
|
||||
.vp-file-tree .vp-file-tree-info::after {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
right: 0;
|
||||
bottom: 1px;
|
||||
left: -16px;
|
||||
top: 0;
|
||||
right: -16px;
|
||||
bottom: 0;
|
||||
left: calc(var(--file-tree-level) * 28px - 32px);
|
||||
z-index: 0;
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
background-color: transparent;
|
||||
border-radius: 6px;
|
||||
transition: background-color var(--vp-t-color);
|
||||
}
|
||||
|
||||
.vp-file-tree .vp-file-tree-info.active::after,
|
||||
.vp-file-tree .vp-file-tree-info:hover::after {
|
||||
.vp-file-tree .vp-file-tree-info:not(.diff):hover::after {
|
||||
background-color: var(--vp-c-default-soft);
|
||||
}
|
||||
|
||||
.vp-file-tree .vp-file-tree-info.diff::after {
|
||||
padding-left: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.vp-file-tree .vp-file-tree-info.diff.add::after {
|
||||
color: var(--vp-c-success-1);
|
||||
content: "+";
|
||||
background-color: var(--vp-c-success-soft);
|
||||
}
|
||||
|
||||
.vp-file-tree .vp-file-tree-info.diff.remove::after {
|
||||
color: var(--vp-c-danger-1);
|
||||
content: "-";
|
||||
background-color: var(--vp-c-danger-soft);
|
||||
}
|
||||
|
||||
.vp-file-tree .vp-file-tree-info.folder {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -16,6 +16,16 @@ interface FileTreeAttrs {
|
||||
icon?: FileTreeIconMode
|
||||
}
|
||||
|
||||
interface FileTreeNodeProps {
|
||||
filename: string
|
||||
comment?: string
|
||||
focus?: boolean
|
||||
expanded?: boolean
|
||||
type: 'folder' | 'file'
|
||||
diff?: 'add' | 'remove'
|
||||
level?: number
|
||||
}
|
||||
|
||||
export function parseFileTreeRawContent(content: string): FileTreeNode[] {
|
||||
const root: FileTreeNode = { info: '', level: -1, children: [] }
|
||||
const stack: FileTreeNode[] = [root]
|
||||
@ -44,12 +54,22 @@ export function parseFileTreeRawContent(content: string): FileTreeNode[] {
|
||||
|
||||
const RE_FOCUS = /^\*\*(.*)\*\*(?:$|\s+)/
|
||||
|
||||
export function parseFileTreeNodeInfo(info: string) {
|
||||
export function parseFileTreeNodeInfo(info: string): FileTreeNodeProps {
|
||||
let filename = ''
|
||||
let comment = ''
|
||||
let focus = false
|
||||
let expanded: boolean | undefined = true
|
||||
let type: 'folder' | 'file' = 'file'
|
||||
let diff: 'add' | 'remove' | undefined
|
||||
|
||||
if (info.startsWith('++')) {
|
||||
info = info.slice(2).trim()
|
||||
diff = 'add'
|
||||
}
|
||||
else if (info.startsWith('--')) {
|
||||
info = info.slice(2).trim()
|
||||
diff = 'remove'
|
||||
}
|
||||
|
||||
info = info.replace(RE_FOCUS, (_, matched) => {
|
||||
filename = matched
|
||||
@ -71,7 +91,7 @@ export function parseFileTreeNodeInfo(info: string) {
|
||||
filename = removeEndingSlash(filename)
|
||||
}
|
||||
|
||||
return { filename, comment, focus, expanded, type }
|
||||
return { filename, comment, focus, expanded, type, diff }
|
||||
}
|
||||
|
||||
export function fileTreePlugin(md: Markdown, options: FileTreeOptions = {}) {
|
||||
@ -85,7 +105,7 @@ export function fileTreePlugin(md: Markdown, options: FileTreeOptions = {}) {
|
||||
const renderFileTree = (nodes: FileTreeNode[], meta: FileTreeAttrs): string =>
|
||||
nodes.map((node) => {
|
||||
const { info, level, children } = node
|
||||
const { filename, comment, focus, expanded, type } = parseFileTreeNodeInfo(info)
|
||||
const { filename, comment, focus, expanded, type, diff } = parseFileTreeNodeInfo(info)
|
||||
const isOmit = filename === '…' || filename === '...' /* fallback */
|
||||
|
||||
if (children.length === 0 && type === 'folder') {
|
||||
@ -99,11 +119,13 @@ export function fileTreePlugin(md: Markdown, options: FileTreeOptions = {}) {
|
||||
const renderedIcon = !isOmit
|
||||
? `<template #icon><VPIcon name="${getIcon(filename, nodeType, meta.icon)}" /></template>`
|
||||
: ''
|
||||
const props = {
|
||||
const props: FileTreeNodeProps = {
|
||||
expanded: nodeType === 'folder' ? expanded : false,
|
||||
focus,
|
||||
type: nodeType,
|
||||
diff,
|
||||
filename,
|
||||
level,
|
||||
}
|
||||
return `<FileTreeNode${stringifyAttrs(props)}>
|
||||
${renderedIcon}${renderedComment}${children.length > 0 ? renderFileTree(children, meta) : ''}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user