主页添加絮语卡片 归档部分初见雏形

This commit is contained in:
li-chx
2025-08-25 01:24:31 +08:00
parent 1fb97e1cc2
commit 363eeb74dd
20 changed files with 752 additions and 213 deletions
+13 -19
View File
@@ -1,15 +1,7 @@
<script setup lang="ts">
// onMounted(() =>
// useColorMode().preference = 'dark',
// );
// function clickHandler() {
// console.log('Button clicked');
// useColorMode().preference = useColorMode().preference === 'light'? 'dark' : 'light';
// }
import useColorModeStore from '~/stores/colorModeStore';
import breakpointsHelper from '~/utils/BreakpointsHelper';
// const { colorMode } = storeToRefs(useColorModeStore());
</script>
<template>
@@ -23,7 +15,9 @@ import useColorModeStore from '~/stores/colorModeStore';
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<div v-if="isScrollDown" class="pl-5 text-xl h-12 leading-11 flex">
<div
v-if="isScrollDown && breakpointsHelper.greaterOrEqual('xl').value"
class="pl-5 text-xl h-12 leading-11 flex">
随机存取
</div>
</Transition>
@@ -61,7 +55,7 @@ import useColorModeStore from '~/stores/colorModeStore';
</template>
<template #header>
<div class="w-full flex-1 justify-center flex items-center">
<p class="text-8xl">
<p class="text-8xl mt-12">
随机存取
</p>
</div>
@@ -70,17 +64,17 @@ import useColorModeStore from '~/stores/colorModeStore';
<div>
<NuxtRouteAnnouncer/>
<NuxtPage/>
<button
@click="() => {
useColorModeStore().toggleColorMode();
}
">swap Theme
</button>
<div class="min-h-[100vh]"></div>
</div>
</template>
<template #footer>
<div class="w-full flex-1 justify-center flex items-center"></div>
<div class="w-full flex flex-col justify-center items-center p-10 text-old-neutral-500">
<div>
© 2025 随机存取. 由Lichx制作
</div>
<div>
蒙ICP备2025022865号
</div>
</div>
</template>
</NuxtLayout>
</template>
+115
View File
@@ -0,0 +1,115 @@
<script setup lang="ts">
import { DataAnomaly } from '~/types/PostMetaData';
import type { PostMetaData } from '~/types/PostMetaData';
const articles = defineModel<PostMetaData[]>('articles', {
default: () => [],
});
const props = withDefaults(defineProps<{
currentChoice?: 'time' | 'category';
}>(),
{
currentChoice: 'time',
});
watch(() => props.currentChoice, (newChoice) => {
if (newChoice === 'time') {
articles.value.sort((a, b) => new Date(b.published_at || '2000-01-01').getTime() - new Date(a.published_at || '2000-01-01').getTime());
} else {
articles.value.sort((a, b) => {
if (a.category === b.category) {
return new Date(b.published_at || '2000-01-01').getTime() - new Date(a.published_at || '2000-01-01').getTime();
}
return a.category.localeCompare(b.category);
});
}
});
function toArticlePage(article: PostMetaData) {
navigateTo(`/article/${encodeURIComponent(article.id)}`);
}
function getYear(article: PostMetaData) {
return new Date(article.published_at).getFullYear();
}
function dateFormatToTime(date: Date | DataAnomaly) {
if (date === DataAnomaly.DataNotFound || date === DataAnomaly.Invalid) {
return date;
}
return new Date(date).toLocaleDateString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
});
}
function dateFormatToDate(date: Date | DataAnomaly) {
if (date === DataAnomaly.DataNotFound || date === DataAnomaly.Invalid) {
return date;
}
return new Date(date).toLocaleDateString('zh-CN', {
month: '2-digit',
day: '2-digit',
}).replace(/\//g, '-');
}
</script>
<template>
<div>
<div
v-for="(article,index) of articles" :key="article.id"
class="border-l-2 border-l-old-neutral-400 dark:border-l-old-neutral-500 pl-4">
<Transition
enter-active-class="transition-opacity duration-500 ease-in-out"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition-opacity duration-500 ease-in-out"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<div
v-if="currentChoice==='time' && index == 0 || getYear(article) != getYear(articles[index-1])"
class="year-marker relative text-indigo-300 text-2xl pt-3 pb-3">
{{ getYear(article) }}
</div>
<div
v-else-if="currentChoice==='category' && (index == 0 || article.category != articles[index-1].category)"
class="year-marker relative text-indigo-300 text-2xl pt-3 pb-3">
{{ article.category }}
</div>
</Transition>
<div class="flex items-center" @click="toArticlePage(article)">
<div :title="dateFormatToTime(article.published_at)" class="text-sm w-12">
{{ dateFormatToDate(article.published_at) }}
</div>
<div :title="dateFormatToTime(article.published_at)" class="text-md pl-5">
{{ article.title }}
</div>
</div>
</div>
</div>
</template>
<style scoped lang="less">
.year-marker::before {
content: '';
position: absolute;
width: 12px; // w-3
height: 12px; // h-3
background-color: #000;
border-radius: 9999px; // rounded-full
border: 2px solid black; // border-2 border-black
left: -17px; // pl-3 12px + 1px border
top: 50%;
transform: translateY(-50%) translateX(-50%);
transition: background-color 0.5s, border-color 0.5s;
}
.dark .year-marker::before {
background-color: #fff;
border: 2px solid white; // border-2 border-white
}
</style>
+31 -3
View File
@@ -1,11 +1,39 @@
<script setup lang="ts">
import { toMetaDataType } from '~/types/PostMetaData';
import TimeLine from '~/pages/index/archive/components/TimeLine.vue';
const { data: articles } = useAsyncData(async () => (await queryCollection('content').order('published_at', 'DESC').all()).map((article) => toMetaDataType(article)));
const currentChoice = ref('时间');
// onMounted(() => {
// setTimeout(() => {
// console.log(articles.value);
// }, 2000);
// });
</script>
<template>
<div>
<div class="table w-full mt-6">
<div class="sticky top-16 float-left bg-old-neutral-200 dark:bg-old-neutral-800 max-h-[calc(100vh-4rem)]">
<div class="relative duration-500 transition-all xl:w-80 w-0 mr-2/3 overflow-hidden">
<div class="w-80 top-0 left-0 text-gray-800 dark:text-white p-5">
test123456
</div>
</div>
</div>
<div
class="transition-all duration-500 float-right xl:w-[calc(100%-20rem-40px)] w-full bg-old-neutral-200 dark:bg-old-neutral-800 p-5">
<URadioGroup
v-model="currentChoice" orientation="horizontal" variant="table" :items="['时间', '类别']" size="sm"
class="mb-5"/>
<TimeLine v-if="articles" v-model:articles="articles!" :current-choice="currentChoice=== '时间'? 'time' : 'category'"/>
</div>
</div>
</div>
</template>
<style scoped>
</style>
</style>
@@ -0,0 +1,156 @@
<script setup lang="ts">
import { DataAnomaly, defaultMetaData } from '~/types/PostMetaData';
import type { PostMetaData } from '~/types/PostMetaData';
import breakpointsHelper from '~/utils/BreakpointsHelper';
const props = withDefaults(defineProps<{
article?: PostMetaData;
}>(), {
article: () => defaultMetaData,
});
function dateFormat(date: Date | DataAnomaly) {
if (date === DataAnomaly.DataNotFound || date === DataAnomaly.Invalid) {
return date;
}
return new Date(date).toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
});
}
function getCostTime(length: number | DataAnomaly) {
if (length === DataAnomaly.DataNotFound || length === DataAnomaly.Invalid) {
return length;
}
let time = length / 250;
time = Math.ceil(time);
const hours = Math.floor(time / 60);
const minutes = time % 60;
if (hours > 0) {
return `${hours}小时${minutes}分钟`;
} else {
return `${minutes}分钟`;
}
}
const open = ref(false);
onMounted(() => {
setTimeout(() => {
open.value = true;
}, 100);
setTimeout(() => {
open.value = false;
}, 4000);
});
</script>
<template>
<div class="p-6 pb-0 light:bg-old-neutral-200 dark:bg-old-neutral-800">
<UCollapsible v-model:open="open" :unmount-on-hide="false" class="flex flex-col gap-2 w-full">
<div class="text-4xl flex justify-between items-center w-full">
<div class="mb-0 pb-0">
{{ props.article.title }}
</div>
<Icon
name="lucide:chevron-down" class="text-2xl transition-transform duration-300 mr-5"
:class="{ 'rotate-180': open }"/>
</div>
<template #content>
<div class="flex mt-2 justify-between">
<div>
<div class="flex items-center mt-2 max-w-96 overflow-hidden">
<div title="发布时间" class="flex items-center">
<Icon name="lucide:clock-arrow-up"/>
<div class="ml-1">
{{ dateFormat(props.article.published_at) }}
</div>
</div>
<div title="分类" class="flex items-center ml-2">
<Icon name="material-symbols:category"/>
<div class="ml-1 inline">
{{ props.article.category }}
</div>
</div>
</div>
<div class="flex items-center max-w-96 overflow-hidden">
<div title="字数" class="flex items-center">
<Icon name="fluent:text-word-count-20-filled"/>
<div class="ml-1 inline">
{{ props.article.word_count }}
</div>
</div>
<div title="预计阅读时间" class="flex items-center ml-2">
<Icon name="octicon:stopwatch-16"/>
<div class="ml-1 inline">
{{ getCostTime(props.article.word_count) }}
</div>
</div>
</div>
<div class="flex">
<div title="创建时间" class="flex items-center">
<Icon name="lucide:file-clock"/>
<div class="ml-1">{{ dateFormat(props.article.created_at) }}</div>
</div>
<div v-if="Array.isArray(props.article.updated_at)" class="flex items-center ml-2">
<Icon name="lucide:clock-alert" title="上次更新时间"/>
<HoverContent>
<template #content>
<div class="ml-1">
{{ dateFormat(props.article.updated_at[props.article.updated_at.length - 1]) }}
</div>
</template>
<template #hoverContent>
<div class="p-1 pr-2">
<div v-for="(date,index) of props.article.updated_at" :key="index">
<div class="block whitespace-nowrap">
&nbsp;{{ '第' + index + '次更新' + dateFormat(date) }}
</div>
</div>
</div>
</template>
</HoverContent>
</div>
</div>
<div v-if="Array.isArray(props.article.tags)" class="flex items-center">
<Icon name="clarity:tags-solid"/>
<div v-for="(tag,index) of props.article.tags" :key="index">
<div class="ml-1">{{ tag }}</div>
</div>
</div>
</div>
<Transition
enter-active-class="transition-opacity duration-500 ease-in-out"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition-opacity duration-500 ease-in-out"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<TechStackCard
v-if="breakpointsHelper.greater('lg').value"
:async-key="'stack:' + props.article.id"
:tech-stack="props.article.tech_stack"
:tech-stack-icon-names="props.article.tech_stack_icon_names"
:tech-stack-theme-colors="props.article.tech_stack_theme_colors"
:tech-stack-percent="props.article.tech_stack_percent"
class="w-64"
/>
</Transition>
</div>
</template>
</UCollapsible>
</div>
</template>
+22 -2
View File
@@ -1,14 +1,34 @@
<script setup lang="ts">
import { toMetaDataType } from '~/types/PostMetaData';
import { MdCatalog } from 'md-editor-v3';
import ArticleHeader from '~/pages/index/article/[articleID]/components/ArticleHeader.vue';
const articleId = useRoute().params.articleID as string;
const { data: article } = useAsyncData(async () => await queryCollection('content').where('id', '=', articleId).first());
const editorId = 'article-previewer';
</script>
<template>
<div>
{{useRoute().params.articleID}}
<div class="table w-full mt-6">
<div class="sticky top-16 float-left bg-old-neutral-200 dark:bg-old-neutral-800 max-h-[calc(100vh-4rem)]">
<div class="relative duration-500 transition-all xl:w-80 w-0 mr-2/3 overflow-hidden">
<div class="w-80 top-0 left-0 text-gray-800 dark:text-white p-5">
<MdCatalog :editor-id="editorId" :scroll-element="'html'"/>
</div>
</div>
</div>
<div class="transition-all duration-500 float-right xl:w-[calc(100%-20rem-40px)] w-full">
<ArticleHeader v-if="article" class="w-full" :article="toMetaDataType(article)"/>
<!-- <ArticleCard v-if="article" class=" w-full" :article="toArticleMetaDataType(article)"/>-->
<ReadonlyMdEditor :editor-id="editorId" :markdown="article?.rawbody"/>
</div>
</div>
</div>
</template>
<style scoped>
<style scoped lang="less">
</style>
-13
View File
@@ -1,13 +0,0 @@
<script setup lang="ts">
</script>
<template>
<div>
Article
</div>
</template>
<style scoped>
</style>
+142
View File
@@ -0,0 +1,142 @@
<script setup lang="ts">
import { DataAnomaly, defaultMetaData } from '~/types/PostMetaData';
import type { PostMetaData } from '~/types/PostMetaData';
import breakpointsHelper from '~/utils/BreakpointsHelper';
const props = withDefaults(defineProps<{
article?: PostMetaData;
}>(),
{
article: () => defaultMetaData,
});
function dateFormat(date: Date | DataAnomaly) {
if (date === DataAnomaly.DataNotFound || date === DataAnomaly.Invalid) {
return date;
}
return new Date(date).toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
});
}
function getCostTime(length: number | DataAnomaly) {
if (length === DataAnomaly.DataNotFound || length === DataAnomaly.Invalid) {
return length;
}
let time = length / 250;
time = Math.ceil(time);
const hours = Math.floor(time / 60);
const minutes = time % 60;
if (hours > 0) {
return `${hours}小时${minutes}分钟`;
} else {
return `${minutes}分钟`;
}
}
</script>
<template>
<div class="p-5 light:bg-old-neutral-200 dark:bg-old-neutral-800 min-h-64 transition-all duration-500">
<div class="text-4xl">
{{ props.article.title }}
</div>
<div class="flex items-center mt-2 max-w-96 overflow-hidden">
<div title="发布时间" class="flex items-center">
<Icon name="lucide:clock-arrow-up"/>
<div class="ml-1 text-nowrap">
{{ dateFormat(props.article.published_at) }}
</div>
</div>
<div title="分类" class="flex items-center ml-2">
<Icon name="material-symbols:category"/>
<div class="ml-1 text-nowrap">
{{ props.article.category }}
</div>
</div>
<div title="字数" class="flex items-center ml-2">
<Icon name="fluent:text-word-count-20-filled"/>
<div class="ml-1 text-nowrap">
{{ props.article.word_count }}
</div>
</div>
<div title="预计阅读时间" class="flex items-center ml-2">
<Icon name="octicon:stopwatch-16"/>
<div class="ml-1 text-nowrap">
{{ getCostTime(props.article.word_count) }}
</div>
</div>
</div>
<div class="flex mt-2 justify-between h-28">
<div>
<div class="">
{{ props.article.description }}
</div>
</div>
<Transition
enter-active-class="transition-opacity duration-500 ease-in-out"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition-opacity duration-500 ease-in-out"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<TechStackCard
v-if="breakpointsHelper.greater('lg').value"
:async-key="'stack:' + props.article.id"
:tech-stack="props.article.tech_stack"
:tech-stack-icon-names="props.article.tech_stack_icon_names"
:tech-stack-theme-colors="props.article.tech_stack_theme_colors"
:tech-stack-percent="props.article.tech_stack_percent"
class="w-64"
/>
</Transition>
</div>
<hr/>
<div class="flex mt-2">
<div title="创建时间" class="flex items-center">
<Icon name="lucide:file-clock"/>
<div class="ml-1">{{ dateFormat(props.article.created_at) }}</div>
</div>
<div v-if="Array.isArray(props.article.updated_at)" class="flex items-center ml-2">
<Icon name="lucide:clock-alert" title="上次更新时间"/>
<HoverContent>
<template #content>
<div class="ml-1">
{{ dateFormat(props.article.updated_at[props.article.updated_at.length - 1]) }}
</div>
</template>
<template #hoverContent>
<div class="p-1 pr-2">
<div v-for="(date,index) of props.article.updated_at" :key="index">
<div class="block whitespace-nowrap">
&nbsp;{{ '第' + index + '次更新' + dateFormat(date) }}
</div>
</div>
</div>
</template>
</HoverContent>
</div>
</div>
<div v-if="Array.isArray(props.article.tags)" class="flex items-top">
<Icon name="clarity:tags-solid" class="mt-1"/>
<div>
<div v-for="(tag,index) of props.article.tags" :key="index" class="inline-block">
<div class="ml-1 inline">{{ tag }}</div>
<div v-if="index !== props.article.tags.length - 1" class="inline">,</div>
</div>
</div>
</div>
</div>
</template>
+176
View File
@@ -0,0 +1,176 @@
<script setup lang="ts">
import { DataAnomaly, defaultMetaData } from '~/types/PostMetaData';
import type { PostMetaData } from '~/types/PostMetaData';
import useColorModeStore from '~/stores/colorModeStore';
const props = withDefaults(defineProps<{
rambling?: PostMetaData;
}>(),
{
rambling: () => defaultMetaData,
});
const { data: rawbody } = useAsyncData(async () => (await queryCollection('content').where('id', '=', props.rambling.id).first())?.rawbody);
const collapsed = ref(false);
function dateFormat(date: Date | DataAnomaly) {
if (date === DataAnomaly.DataNotFound || date === DataAnomaly.Invalid) {
return date;
}
return new Date(date).toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
});
}
function getCostTime(length: number | DataAnomaly) {
if (length === DataAnomaly.DataNotFound || length === DataAnomaly.Invalid) {
return length;
}
let time = length / 250;
time = Math.ceil(time);
const hours = Math.floor(time / 60);
const minutes = time % 60;
if (hours > 0) {
return `${hours}小时${minutes}分钟`;
} else {
return `${minutes}分钟`;
}
}
const safeEditorId = computed(() => {
const encoded = btoa(encodeURIComponent(props.rambling.id))
.replace(/[+/=]/g, '_'); // 替换 Base64 中的特殊字符
return `rambling_${encoded}`;
});
const showLightShadow = ref(false);
const showDarkShadow = ref(false);
function reverseCollapsed() {
if (collapsed.value) {
collapsed.value = false;
return;
}
const showLight = showLightShadow.value;
const showDark = showDarkShadow.value;
showLightShadow.value = false;
showDarkShadow.value = false;
collapsed.value = true;
setTimeout(() => {
showLightShadow.value = showLight;
showDarkShadow.value = showDark;
}, 500);
}
const colorModeStore = useColorModeStore();
let colorModeCallBackKey = '';
onMounted(() => {
if (colorModeStore.colorMode === 'light') {
showLightShadow.value = true;
} else {
showDarkShadow.value = true;
}
colorModeCallBackKey = colorModeStore.registerCallBack(() => {
if (colorModeStore.colorMode === 'light') {
setTimeout(() => {
showLightShadow.value = true;
}, 500);
showDarkShadow.value = false;
} else {
showLightShadow.value = false;
setTimeout(() => {
showDarkShadow.value = true;
}, 500);
}
});
});
onUnmounted(() => {
colorModeStore.unregisterCallBack(colorModeCallBackKey);
});
</script>
<template>
<div
class="p-5 light:bg-old-neutral-200 dark:bg-old-neutral-800 min-h-64 transition-all duration-500"
@click="reverseCollapsed">
<div class="text-4xl">
絮语:{{ props.rambling.title }}
</div>
<div class="flex items-center mt-2 max-w-96 overflow-hidden">
<div title="发布时间" class="flex items-center">
<Icon name="lucide:clock-arrow-up"/>
<div class="ml-1 text-nowrap">
{{ dateFormat(props.rambling.published_at) }}
</div>
</div>
<div title="分类" class="flex items-center ml-2">
<Icon name="material-symbols:category"/>
<div class="ml-1 text-nowrap">
{{ props.rambling.category }}
</div>
</div>
<div title="字数" class="flex items-center ml-2">
<Icon name="fluent:text-word-count-20-filled"/>
<div class="ml-1 text-nowrap">
{{ props.rambling.word_count }}
</div>
</div>
<div title="预计阅读时间" class="flex items-center ml-2">
<Icon name="octicon:stopwatch-16"/>
<div class="ml-1 text-nowrap">
{{ getCostTime(props.rambling.word_count) }}
</div>
</div>
</div>
<div
class="relative flex mt-2 justify-between overflow-hidden transition-all duration-300 ease-in-out dura"
:class="{'max-h-[8.5rem]' : collapsed, 'max-h-[100vh]':!collapsed}">
<ReadonlyMdEditor
v-if="rawbody" :editor-id="safeEditorId" :markdown="rawbody!"
class="transition-all duration-500 w-full"/>
<div
class="absolute bottom-0 left-0 right-0 h-14 bg-gradient-to-t from-old-neutral-200 to-transparent pointer-events-none transition-opacity duration-300"
:class="collapsed&&showLightShadow?'opacity-100':'opacity-0'"
/>
<div
class="absolute bottom-0 left-0 right-0 h-14 bg-gradient-to-t from-old-neutral-800 to-transparent pointer-events-none transition-opacity duration-300"
:class="collapsed&&showDarkShadow?'opacity-100':'opacity-0'"
/>
</div>
<hr/>
<div class="flex mt-2">
<div title="创建时间" class="flex items-center">
<Icon name="lucide:file-clock"/>
<div class="ml-1">{{ dateFormat(props.rambling.created_at) }}</div>
</div>
<div v-if="Array.isArray(props.rambling.updated_at)" class="flex items-center ml-2">
<Icon name="lucide:clock-alert" title="上次更新时间"/>
<HoverContent>
<template #content>
<div class="ml-1">
{{ dateFormat(props.rambling.updated_at[props.rambling.updated_at.length - 1]) }}
</div>
</template>
<template #hoverContent>
<div class="p-1 pr-2">
<div v-for="(date,index) of props.rambling.updated_at" :key="index">
<div class="block whitespace-nowrap">
&nbsp;{{ '第' + index + '次更新' + dateFormat(date) }}
</div>
</div>
</div>
</template>
</HoverContent>
</div>
</div>
</div>
</template>
+29 -40
View File
@@ -1,57 +1,46 @@
<script setup lang="ts">
import { toArticleMetaDataType } from '~/types/ArticleMetaData';
import { toMetaDataType } from '~/types/PostMetaData';
import RamblingCard from '~/pages/index/components/RamblingCard.vue';
import ArticleCard from '~/pages/index/components/ArticleCard.vue';
const { data: articles } = useAsyncData(async () => await queryCollection('content').all());
const { data: posts } = useAsyncData(async () => await queryCollection('content').order('published_at', 'DESC').all());
onMounted(() => {
console.log(articles.value);
});
// // 监听断点变化并设置延迟策略
// watch(showLeftDetailInfo, (newValue, oldValue) => {
// if (newValue && !oldValue) {
// // xl -> 2xl (显示) - 需要延迟
// shouldDelay.value = true;
// isTransitioning.value = true;
// } else if (!newValue && oldValue) {
// // 2xl -> xl (隐藏) - 不延迟
// shouldDelay.value = false;
// isTransitioning.value = true;
// }
//
// // 动画完成后重置状态
// onMounted(() => {
// setTimeout(() => {
// isTransitioning.value = false;
// }, 400); // 根据你的动画时长调整
// console.log(articles.value);
// }, 2000);
// });
type PostItem = NonNullable<typeof posts.value>[number];
function toArticlePage(article: PostItem) {
navigateTo(`/article/${encodeURIComponent(article.id)}`);
}
</script>
<template>
<div>
<div class="mt-4">
<div class="table w-full">
<div class="sticky top-16 float-left bg-old-neutral-500 h-[100vh]">
<div class="relative duration-500 transition-all xl:w-80 w-0 mr-2/3 h-full overflow-hidden">
<div class="absolute top-0 left-0 w-80 text-white z-50">
<div class="">
test123456
</div>
</div>
<div class="table w-full mt-6">
<div class="sticky top-16 float-left bg-old-neutral-200 dark:bg-old-neutral-800 max-h-[calc(100vh-4rem)]">
<div class="relative duration-500 transition-all xl:w-80 w-0 mr-2/3 overflow-hidden">
<div class="w-80 top-0 left-0 text-gray-800 dark:text-white p-5">
test123456
</div>
</div>
<div class="transition-all duration-500 float-right xl:w-[calc(100%-20rem-40px)] w-full">
<ArticleCard
v-for="article in articles" :key="article.id"
class="mb-4 w-full"
:article="toArticleMetaDataType(article)"/>
<ArticleCard/>
</div>
</div>
test
<div class="transition-all duration-500 float-right xl:w-[calc(100%-20rem-40px)] w-full">
<ArticleCard class="mb-6 w-full transition-shadow duration-300 shadow-lg hover:shadow-old-neutral-600"/>
<div v-for="article in posts" :key="article.id" class="mb-6 w-full transition-shadow duration-300 shadow-lg hover:shadow-old-neutral-600 hover:cursor-pointer">
<ArticleCard
v-if="!article.draft && article.type === 'article'"
:article="toMetaDataType(article)"
@click="toArticlePage(article)"/>
<RamblingCard
v-else-if="!article.draft && article.type === 'rambling'"
:rambling="toMetaDataType(article)"/>
</div>
</div>
</div>
</div>
</template>