mirror of
https://codeberg.org/HPCesia/AstralHalo.git
synced 2025-04-08 17:34:27 +08:00
refactor(side toolbar): Use Vue on the TOC Button
This will fix bug on toc button, and close #6
This commit is contained in:
parent
28b672503f
commit
e4f1dff4c1
@ -3,6 +3,7 @@ import { articleConfig } from '@/config';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import Button from './widgets/Button.astro';
|
||||
import DarkModeButton from './widgets/DarkModeButton.astro';
|
||||
import TocButton from './widgets/SideToolBar/TocButton.vue';
|
||||
---
|
||||
|
||||
<div id="side-toolbar" class="fixed right-0 bottom-10 z-30 grid grid-cols-1 gap-2">
|
||||
@ -20,19 +21,9 @@ import DarkModeButton from './widgets/DarkModeButton.astro';
|
||||
</Button>
|
||||
{
|
||||
articleConfig.toc && (
|
||||
<Fragment>
|
||||
<Button id="stb-toc" class="btn-circle btn-secondary btn-sm hidden xl:hidden!">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="peer absolute z-10 h-8 w-8 cursor-pointer appearance-none border-0"
|
||||
/>
|
||||
<Icon name="material-symbols:toc-rounded" />
|
||||
<div
|
||||
id="stb-toc-wrapper"
|
||||
class="rounded-box absolute w-[calc(100vw-4rem)] max-w-72 backdrop-blur-md duration-300 peer-checked:-translate-x-[calc(50%+1.5rem)] peer-[:not(:checked)]:scale-0"
|
||||
/>
|
||||
</Button>
|
||||
</Fragment>
|
||||
<TocButton client:media="(width <= 80rem)">
|
||||
<Icon name="material-symbols:toc-rounded" slot="icon" />
|
||||
</TocButton>
|
||||
)
|
||||
}
|
||||
<Button id="stb-back-to-top" class="group btn-circle btn-secondary btn-sm">
|
||||
@ -63,8 +54,6 @@ import DarkModeButton from './widgets/DarkModeButton.astro';
|
||||
const stbBackToTop = document.getElementById('stb-back-to-top');
|
||||
const stbBackToTopIcon = document.getElementById('stb-back-to-top-icon');
|
||||
const stbReadPercent = document.getElementById('stb-read-percentage');
|
||||
const stbToc = document.getElementById('stb-toc');
|
||||
const stbTocWrapper = document.getElementById('stb-toc-wrapper');
|
||||
|
||||
stbBackToTop?.addEventListener('click', () => {
|
||||
window.scrollTo({
|
||||
@ -73,29 +62,6 @@ import DarkModeButton from './widgets/DarkModeButton.astro';
|
||||
});
|
||||
});
|
||||
|
||||
// 清理可能存在的旧目录
|
||||
stbTocWrapper!.innerHTML = '';
|
||||
stbToc?.classList.add('hidden');
|
||||
|
||||
const toc = document.getElementById('toc');
|
||||
if (toc && stbTocWrapper) {
|
||||
const remainAttrs = ['class', 'style'];
|
||||
stbTocWrapper.appendChild(toc.cloneNode(true));
|
||||
stbTocWrapper.children[0].id = 'stb-toc-content';
|
||||
Array.from(stbTocWrapper.children[0].attributes).forEach((attr) => {
|
||||
if (!remainAttrs.includes(attr.name)) {
|
||||
stbTocWrapper.children[0].removeAttribute(attr.name);
|
||||
}
|
||||
});
|
||||
stbToc?.classList.remove('hidden');
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
if (window.innerWidth > 1280) {
|
||||
(stbToc?.children[0] as HTMLInputElement).checked = false;
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
// 控制工具栏显隐
|
||||
if (window.scrollY > 0) {
|
||||
@ -103,8 +69,6 @@ import DarkModeButton from './widgets/DarkModeButton.astro';
|
||||
} else {
|
||||
stbShow?.classList.add('translate-x-full');
|
||||
stbShowMore!.querySelector('input')!.checked = true;
|
||||
stbTocWrapper?.classList.add('scale-0');
|
||||
stbTocWrapper?.classList.remove('-translate-x-full');
|
||||
}
|
||||
// 控制进度条
|
||||
const scrolledPercentage = getReadingProgress();
|
||||
|
72
src/components/widgets/SideToolBar/TocButton.vue
Normal file
72
src/components/widgets/SideToolBar/TocButton.vue
Normal file
@ -0,0 +1,72 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
|
||||
const isOpen = ref(false);
|
||||
const isWideScreen = ref(false);
|
||||
const hasToc = ref(false);
|
||||
const tocWrapper = ref<HTMLElement | null>(null);
|
||||
|
||||
const handleResize = () => {
|
||||
if (window.innerWidth > 1280) {
|
||||
isWideScreen.value = true;
|
||||
} else {
|
||||
isWideScreen.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const setup = () => {
|
||||
const toc = document.getElementById('toc');
|
||||
if (toc && tocWrapper.value) {
|
||||
hasToc.value = true;
|
||||
const remainAttrs = ['class', 'style'];
|
||||
tocWrapper.value.innerHTML = '';
|
||||
tocWrapper.value.appendChild(toc.cloneNode(true));
|
||||
tocWrapper.value.children[0].id = 'stb-toc-content';
|
||||
Array.from(tocWrapper.value.children[0].attributes).forEach((attr) => {
|
||||
if (!remainAttrs.includes(attr.name)) {
|
||||
tocWrapper.value!.children[0].removeAttribute(attr.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
window.addEventListener('resize', handleResize);
|
||||
handleResize();
|
||||
};
|
||||
const cleanup = () => {
|
||||
isOpen.value = false;
|
||||
if (tocWrapper.value) tocWrapper.value.innerHTML = '';
|
||||
hasToc.value = false;
|
||||
window.removeEventListener('resize', handleResize);
|
||||
isWideScreen.value = false;
|
||||
};
|
||||
|
||||
document.addEventListener('astro:page-load', setup);
|
||||
setup();
|
||||
document.addEventListener('astro:before-swap', cleanup);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="{
|
||||
hidden: !hasToc || isWideScreen,
|
||||
}"
|
||||
>
|
||||
<button
|
||||
ref="buttonRef"
|
||||
class="btn btn-circle btn-secondary btn-sm"
|
||||
@click="isOpen = !isOpen"
|
||||
>
|
||||
<slot name="icon" />
|
||||
</button>
|
||||
<div
|
||||
ref="tocWrapper"
|
||||
class="rounded-box absolute w-[calc(100vw-4rem)] -translate-x-1/2 -translate-y-1/2 max-w-72 backdrop-blur-md duration-300 text-base-content text-start"
|
||||
:class="{
|
||||
'-translate-x-[calc(100%+0.5rem)]! -translate-y-[calc(100%-2.5rem)]!':
|
||||
isOpen && !isWideScreen,
|
||||
'scale-0 opacity-0': !isOpen || isWideScreen,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
Loading…
Reference in New Issue
Block a user