mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 22:49:31 -07:00
FEAT: OCTOBER UPDATE
This commit is contained in:
@@ -1,70 +1,52 @@
|
||||
<template>
|
||||
<div ref="scrollRef">
|
||||
<div class="page-transition-fade">
|
||||
<h2 class="sub-category">{{ decodedLabel }}</h2>
|
||||
<IndexPage v-if="isIndexPage" />
|
||||
|
||||
<Suspense>
|
||||
<template #default>
|
||||
<component :is="SubcategoryComponent" v-if="SubcategoryComponent" />
|
||||
</template>
|
||||
<div class="page-transition-fade" v-else>
|
||||
<h2 class="sub-category">{{ decodedLabel }}</h2>
|
||||
|
||||
<template #fallback>
|
||||
<div class="loading-placeholder"></div>
|
||||
</template>
|
||||
</Suspense>
|
||||
<Suspense v-if="SubcategoryComponent">
|
||||
<template #default>
|
||||
<component :is="SubcategoryComponent" v-if="SubcategoryComponent" />
|
||||
</template>
|
||||
|
||||
<template #fallback>
|
||||
<div class="loading-placeholder"></div>
|
||||
</template>
|
||||
</Suspense>
|
||||
<div v-else class="p-6">
|
||||
<h3 class="font-semibold text-white text-lg">Not Found</h3>
|
||||
<p class="text-[#a6a6a6] text-sm">This section is unavailable.</p>
|
||||
</div>
|
||||
|
||||
<BackToTopButton />
|
||||
</div>
|
||||
|
||||
<BackToTopButton />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, watch, onMounted, nextTick, defineAsyncComponent, useTemplateRef } from 'vue';
|
||||
import BackToTopButton from '@/components/common/BackToTopButton.vue';
|
||||
import { computed, defineAsyncComponent, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { componentMap } from '../constants/Components';
|
||||
import { decodeLabel } from '../utils/utils';
|
||||
import BackToTopButton from '@/components/common/BackToTopButton.vue';
|
||||
import IndexPage from './IndexPage.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const scrollRef = useTemplateRef<HTMLDivElement>('scrollRef');
|
||||
|
||||
const subcategory = computed(() => route.params.subcategory as string);
|
||||
const decodedLabel = computed(() => decodeLabel(subcategory.value));
|
||||
const isIndexPage = computed(() => subcategory.value === 'index');
|
||||
|
||||
const SubcategoryComponent = computed(() => {
|
||||
if (!subcategory.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const componentLoader = componentMap[subcategory.value as keyof typeof componentMap];
|
||||
|
||||
if (!componentLoader) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return defineAsyncComponent(componentLoader);
|
||||
const key = subcategory.value as keyof typeof componentMap;
|
||||
const loader = componentMap[key];
|
||||
return loader ? defineAsyncComponent(loader) : null;
|
||||
});
|
||||
|
||||
watch(
|
||||
decodedLabel,
|
||||
label => {
|
||||
if (label) {
|
||||
document.title = `Vue Bits - ${label}`;
|
||||
}
|
||||
if (label) document.title = `Vue Bits - ${label}`;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(subcategory, async () => {
|
||||
if (scrollRef.value) {
|
||||
await nextTick();
|
||||
scrollRef.value.scrollTo(0, 0);
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (scrollRef.value) {
|
||||
scrollRef.value.scrollTo(0, 0);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
46
src/pages/FavoritesPage.vue
Normal file
46
src/pages/FavoritesPage.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<main class="app-container">
|
||||
<Header />
|
||||
|
||||
<section class="category-wrapper">
|
||||
<Sidebar />
|
||||
|
||||
<div class="category-page">
|
||||
<ComponentList :list="savedList" title="Favorites" sorting="none" has-delete-button />
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ComponentList from '@/components/common/ComponentList.vue';
|
||||
import Header from '@/components/navs/Header.vue';
|
||||
import Sidebar from '@/components/navs/Sidebar.vue';
|
||||
import { componentMetadata } from '@/constants/Information';
|
||||
import { getSavedComponents } from '@/utils/favorites';
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
|
||||
const savedKeys = ref<string[]>(getSavedComponents());
|
||||
|
||||
const update = () => (savedKeys.value = getSavedComponents());
|
||||
const onStorage = (e?: StorageEvent | null) => {
|
||||
if (!e || e.key === 'savedComponents') update();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('favorites:updated', update);
|
||||
window.addEventListener('storage', onStorage);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('favorites:updated', update);
|
||||
window.removeEventListener('storage', onStorage);
|
||||
});
|
||||
|
||||
const savedList = computed(() => {
|
||||
const entries = (savedKeys.value || [])
|
||||
.filter(k => typeof k === 'string' && k.includes('/') && componentMetadata?.[k])
|
||||
.map(k => [k, componentMetadata[k]]);
|
||||
return Object.fromEntries(entries);
|
||||
});
|
||||
</script>
|
||||
11
src/pages/IndexPage.vue
Normal file
11
src/pages/IndexPage.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<title>Vue Bits - Component Index</title>
|
||||
<ComponentList :list="componentMetadata" title="Index" sorting="alphabetical" has-favorite-button />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ComponentList from '@/components/common/ComponentList.vue';
|
||||
import { componentMetadata } from '@/constants/Information';
|
||||
</script>
|
||||
Reference in New Issue
Block a user