Skip to content

Commit

Permalink
feat: file tree
Browse files Browse the repository at this point in the history
Signed-off-by: ZTL-UwU <zhangtianli2006@163.com>
  • Loading branch information
ZTL-UwU committed Jan 11, 2025
1 parent 9fa7c92 commit 0e52e16
Show file tree
Hide file tree
Showing 17 changed files with 365 additions and 24 deletions.
Binary file modified bun.lockb
Binary file not shown.
2 changes: 2 additions & 0 deletions components/content/AccordionItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@ defineProps<{
content?: string;
}>();
defineSlots();
const autoValue = useId();
</script>
3 changes: 3 additions & 0 deletions components/content/Card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,8 @@ const {
showLinkIcon?: boolean;
horizontal?: boolean;
}>();
defineSlots();
const [UseTemplate, CardInner] = createReusableTemplate();
</script>
3 changes: 2 additions & 1 deletion components/content/CodeGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
</template>

<script setup lang="ts">
import type { SetupContext } from 'vue';
import Tabs from './Tabs.vue';
const { inStack = false, sync } = defineProps<{
inStack?: boolean;
sync?: string;
}>();
const _slots = useSlots();
const _slots: SetupContext['slots'] = useSlots();
function render() {
const slots = _slots?.default?.() || [];
return h(
Expand Down
1 change: 1 addition & 0 deletions components/content/Collapsible.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,6 @@ const {
title?: string;
defaultOpen?: boolean;
}>();
defineSlots();
const isOpen = ref(defaultOpen);
</script>
90 changes: 90 additions & 0 deletions components/content/FileTree.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<template>
<UiCard
class="relative overflow-hidden [&:not(:first-child)]:mt-5 [&:not(:last-child)]:mb-5"
>
<div v-if="title" class="flex items-center border-b p-3 font-mono text-sm">
<SmartIcon v-if="icon" :name="icon" class="mr-1.5" />
<span>{{ title }}</span>
</div>

<div class="w-auto bg-muted/30 p-2">
<FileTreeRoot :tree="parsedTree" :show-arrow :show-icon :level="0" />
</div>
</UiCard>
</template>

<script setup lang="ts">
type InputTreeItem = string | {
[key: string]: InputTreeItem[];
};
const {
tree,
autoSlash = true,
showArrow = false,
showIcon = true,
} = defineProps<{
title?: string;
icon?: string;
autoSlash?: boolean;
showArrow?: boolean;
showIcon?: boolean;
tree: InputTreeItem[];
}>();
const iconMap = new Map(Object.entries(useConfig().value.main.codeIcon));
function getIcon(filename: string, type: 'folder' | 'file') {
if (filename === '...')
return;
if (filename.endsWith('/'))
return 'lucide:folder';
return iconMap.get(filename.split('.')[filename.split('.').length - 1])
|| iconMap.get(filename.toLowerCase())
|| (type === 'file' ? 'lucide:file' : 'lucide:folder');
}
function getItem(key: string, type: 'folder' | 'file', children?: InputTreeItem[]): FileTreeItem {
let title = key;
let highlighted = false;
if (title.startsWith('^') && title.endsWith('^')) {
title = title.substring(1, title.length - 1);
highlighted = true;
}
if (type === 'file') {
return {
title,
icon: getIcon(title, 'file'),
highlighted,
};
} else {
return {
title: `${title}${autoSlash ? '/' : ''}`,
icon: getIcon(title, 'folder'),
children: children && getTree(children),
highlighted,
};
}
}
function getTree(tree: InputTreeItem[]) {
const res: FileTreeItem[] = [];
for (const item of tree) {
if (typeof item === 'string') {
res.push(getItem(item, 'file'));
} else if (typeof item === 'object') {
for (const key of Object.keys(item))
res.push(getItem(key, 'folder', item[key]));
}
}
return res;
}
const parsedTree = computed(() => {
return getTree(tree);
});
</script>
58 changes: 58 additions & 0 deletions components/content/FileTreeItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>
<li>
<!-- Folder -->
<div v-if="tree.children">
<a
class="flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-left text-sm font-medium text-foreground/80 hover:bg-muted hover:text-primary"
:class="[tree.highlighted && 'underline underline-offset-4']"
@click="isOpen = !isOpen"
>
<SmartIcon
v-if="showArrow"
name="lucide:chevron-down"
class="transition-transform"
:class="[!isOpen && '-rotate-90']"
/>

<SmartIcon
v-if="showIcon && tree.icon"
:name="tree.icon"
class="min-w-4"
/>

<span :class="[tree.highlighted && 'font-bold text-primary']">
{{ tree.title }}
</span>
</a>
<div v-show="isOpen">
<FileTreeRoot :show-icon :show-arrow :tree="tree.children" :level="level + 1" />
</div>
</div>
<!-- File -->
<div
v-else
class="flex w-full items-center gap-2 rounded-md px-2 py-1 text-sm text-foreground/80 hover:bg-muted hover:text-primary"
:class="[tree.highlighted && 'underline underline-offset-4']"
>
<SmartIcon
v-if="showIcon && tree.icon"
:name="tree.icon"
class="min-w-4"
/>

<span :class="[tree.highlighted && 'font-bold text-primary']">
{{ tree.title }}
</span>
</div>
</li>
</template>

<script setup lang="ts">
const { tree, level } = defineProps<{
showArrow: boolean;
tree: FileTreeItem;
showIcon: boolean;
level: number;
}>();
const isOpen = ref(true);
</script>
24 changes: 24 additions & 0 deletions components/content/FileTreeRoot.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<ul
class="flex flex-col font-mono"
:class="[level > 0 && 'mx-3.5 border-l px-2']"
>
<template v-for="link in tree" :key="link._id">
<FileTreeItem
:show-icon
:show-arrow
:tree="link"
:level="level"
/>
</template>
</ul>
</template>

<script setup lang="ts">
defineProps<{
tree: FileTreeItem[];
level: number;
showArrow: boolean;
showIcon: boolean;
}>();
</script>
1 change: 1 addition & 0 deletions components/content/Hero.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,5 @@ defineProps<{
target?: Target;
}];
}>();
defineSlots();
</script>
1 change: 1 addition & 0 deletions components/content/HeroAlt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,5 @@ defineProps<{
}];
mobileRight?: 'top' | 'bottom';
}>();
defineSlots();
</script>
4 changes: 4 additions & 0 deletions components/content/Stack.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@
</div>
</UiCard>
</template>

<script setup lang="ts">
defineSlots();
</script>
3 changes: 2 additions & 1 deletion components/content/Tabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
</template>

<script setup lang="ts">
import type { SetupContext } from 'vue';
import TabsInner from './TabsInner.vue';
const {
Expand All @@ -23,7 +24,7 @@ const {
sync?: string;
}>();
const _slots = useSlots();
const _slots: SetupContext['slots'] = useSlots();
function render() {
const slots = _slots?.default?.() || [];
Expand Down
1 change: 1 addition & 0 deletions components/content/TabsInner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ const { sync, slotsData } = defineProps<{
searchEmpty?: string;
sync?: string;
}>();
defineSlots();
const syncState = useCookie<{ scope: string; value?: string }[]>('tabs-sync-state', {
default: () => [],
Expand Down
Loading

0 comments on commit 0e52e16

Please sign in to comment.