feat: initial commit

(cherry picked from commit 44c4d7b9521fe449e61edc614446195861932f8c)
This commit is contained in:
saicaca
2023-09-26 14:27:38 +08:00
parent 02b0a65314
commit 124843848f
58 changed files with 13083 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
---
interface Props {
keyword: string;
tags: string[];
categories: string[];
}
const { keyword, tags, categories} = Astro.props;
import Button from "./control/Button.astro";
import {getPostUrlBySlug, getSortedPosts} from "../utils/content-utils";
let posts = await getSortedPosts()
if (Array.isArray(tags) && tags.length > 0) {
posts = posts.filter(post =>
Array.isArray(post.data.tags) && post.data.tags.some(tag => tags.includes(tag))
);
}
if (Array.isArray(categories) && categories.length > 0) {
posts = posts.filter(post =>
Array.isArray(post.data.categories) && post.data.categories.some(category => categories.includes(category))
);
}
const groups = function () {
const groupedPosts = posts.reduce((grouped, post) => {
const year = post.data.pubDate.getFullYear()
if (!grouped[year]) {
grouped[year] = []
}
grouped[year].push(post)
return grouped
}, {})
// convert the object to an array
const groupedPostsArray = Object.keys(groupedPosts).map(key => ({
year: key,
posts: groupedPosts[key]
}))
// sort years by latest first
groupedPostsArray.sort((a, b) => b.year - a.year)
return groupedPostsArray;
}();
function formatDate(date: Date) {
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${month}-${day}`;
}
// console.log(groups)
---
<div class="card-base px-8 py-6">
{
groups.map(group => (
<div>
<div class="flex flex-row w-full items-center h-[60px]">
<div class="w-[10%] transition text-2xl font-bold text-right text-black/75 dark:text-white/75">{group.year}</div>
<div class="w-[10%]">
<div class="h-3 w-3 bg-none rounded-full outline outline-[var(--primary)] mx-auto -outline-offset-[2px] z-50 outline-3"></div>
</div>
<div class="w-[80%] transition text-left text-black/50 dark:text-white/50">{group.posts.length} Articles</div>
</div>
{group.posts.map(post => (
<a href={getPostUrlBySlug(post.slug)} class="group">
<Button light height="40px" class="w-full hover:text-[initial]">
<div class="flex flex-row justify-start items-center h-full">
<!-- date -->
<div class="w-[10%] transition text-sm text-right text-black/50 dark:text-white/50">{formatDate(post.data.pubDate)}</div>
<!-- dot and line -->
<div class="w-[10%] relative dash-line h-full flex items-center">
<div class="transition-all mx-auto w-1 h-1 rounded group-hover:h-5
bg-[oklch(0.5_0.05_var(--hue))] group-hover:bg-[var(--primary)]
outline outline-4 z-50
outline-[var(--card-bg)]
group-hover:outline-[var(--btn-plain-bg-hover)]
group-active:outline-[var(--btn-plain-bg-active)]
"
></div>
</div>
<!-- post title -->
<div class="max-w-[65%] w-[65%] transition text-left font-bold">
<div class="group-hover:ml-1 transition-all group-hover:text-[var(--primary)]
text-black/80 dark:text-white/80 pr-8 whitespace-nowrap overflow-ellipsis overflow-hidden">
{post.data.title}
</div>
</div>
<!-- tag list -->
<div class="w-[15%] text-left text-sm transition
whitespace-nowrap overflow-ellipsis overflow-hidden
text-black/30 dark:text-white/30"
>#Test #Markdown</div>
</div>
</Button>
</a>
))}
</div>
))
}
</div>
<style>
@tailwind components;
@tailwind utilities;
@layer components {
.dash-line {
}
.dash-line::before {
content: "";
@apply w-[10%] h-full absolute -top-1/2 left-[calc(50%_-_1px)] -top-[50%] border-l-[2px]
border-dashed pointer-events-none border-[var(--line-color)] transition
}
}
</style>

View File

@@ -0,0 +1,6 @@
---
---
<div class="rounded-2xl drop-shadow-2xl bg-white">
<slot />
</div>

View File

@@ -0,0 +1,7 @@
---
import { Icon } from 'astro-icon/components';
import ButtonLight from "./control/Button.astro";
---
<ButtonLight class="fill-black">
<Icon name="material-symbols:nightlight-badge-outline" class="w-6 h-6"/>
</ButtonLight>

60
src/components/Card.astro Normal file
View File

@@ -0,0 +1,60 @@
---
interface Props {
title: string;
body: string;
href: string;
}
const { href, title, body } = Astro.props;
---
<li class="link-card">
<a href={href}>
<h2>
{title}
<span class="">&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
<style>
.link-card {
list-style: none;
display: flex;
padding: 1px;
background-color: #23262d;
background-image: none;
background-size: 400%;
border-radius: 7px;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: calc(1.5rem - 1px);
border-radius: 8px;
color: white;
background-color: #23262d;
opacity: 0.8;
}
h2 {
margin: 0;
font-size: 1.25rem;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
p {
margin-top: 0.5rem;
margin-bottom: 0;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
background-image: var(--accent-gradient);
}
.link-card:is(:hover, :focus-within) h2 {
color: rgb(var(--accent-light));
}
</style>

View File

@@ -0,0 +1,92 @@
---
---
<div>
<slot/>
</div>
<style is:global lang="stylus">
/* utils */
white(a)
rgba(255, 255, 255, a)
black(a)
rgba(0, 0, 0, a)
isOklch(c)
return substr(c, 0, 5) == 'oklch'
oklch_fallback(c)
str = '' + c // convert color value to string
if isOklch(str)
return convert(oklchToHex(str))
return c
color_set(colors)
@supports (color: oklch(0 0 0))
:root
for key, value in colors
{key}: value[0]
:root.dark
for key, value in colors
if length(value) > 1
{key}: value[1]
/* provide fallback color for oklch */
@supports not (color: oklch(0 0 0))
:root
for key, value in colors
{key}: oklch_fallback(value[0])
:root.dark
for key, value in colors
if length(value) > 1
{key}: oklch_fallback(value[1])
:root
--radius-large 16px
--banner-height-home 60vh
--banner-height 50vh
color_set({
--primary: oklch(0.70 0.14 var(--hue))
--card-bg: white oklch(0.25 0.02 var(--hue))
--btn-content: oklch(0.55 0.12 var(--hue))
--btn-regular-bg: oklch(0.95 0.025 var(--hue)) oklch(0.38 0.04 var(--hue))
--btn-plain-bg-hover: oklch(0.95 0.025 var(--hue)) oklch(0.2 0.02 var(--hue))
--btn-plain-bg-active: oklch(0.98 0.01 var(--hue)) oklch(0.17 0.017 var(--hue))
--btn-card-bg-hover: oklch(0.96 0.015 var(--hue)) oklch(0.3 0.03 var(--hue))
--btn-card-bg-active: oklch(0.9 0.03 var(--hue)) oklch(0.35 0.035 var(--hue))
--deep-text: oklch(0.25 0.02 var(--hue))
--line-color: black(0.1) white(0.1)
--meta-divider: black(0.2) white(0.2)
--selection-bg: oklch(0.90 0.05 var(--hue)) oklch(0.40 0.08 var(--hue))
})
/* some global styles */
::selection
background-color: var(--selection-bg)
</style>
<style is:global>
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.card-base {
@apply rounded-[var(--radius-large)] overflow-hidden bg-[var(--card-bg)] transition;
}
h1, h2, h3, h4, h5, h6, p, a, span, li, ul, ol, blockquote, code, pre, table, th, td, strong {
@apply transition;
}
}
</style>

View File

@@ -0,0 +1,60 @@
---
import Button from "./control/Button.astro";
import { Icon } from 'astro-icon/components';
const className = Astro.props.class;
---
<div class:list={[
className,
"card-base max-w-[var(--page-width)] h-[72px] rounded-t-none mx-auto flex items-center justify-between px-4"]}>
<a href="/"><Button height="52px" class="px-5 font-bold" light>
<div class="flex flex-row text-[var(--primary)] items-center text-md">
<Icon name="material-symbols:home-outline-rounded" size={28} class="mb-1 mr-2" />
<div class="top-2"></div>Vivia Preview
</div>
</Button></a>
<div>
<a href="/"><Button light class="font-bold px-5">Home</Button></a>
<a href="/archive"><Button light class="font-bold px-5">Archive</Button></a>
<Button light class="font-bold px-5">About</Button>
</div>
<div>
<Button id="scheme-switch" iconName="material-symbols:wb-sunny-outline-rounded" iconSize={20} isIcon light></Button>
</div>
</div>
<style lang="stylus">
</style>
<script>
function switchTheme() {
if (localStorage.theme === 'dark') {
document.documentElement.classList.remove('dark');
localStorage.theme = 'light';
} else {
document.documentElement.classList.add('dark');
localStorage.theme = 'dark';
}
}
function loadThemeSwitchScript() {
let switchBtn = document.getElementById("scheme-switch");
if (switchBtn === null) {
console.log("test")
}
switchBtn.addEventListener("click", function () {
console.log("test")
switchTheme()
});
}
loadThemeSwitchScript();
document.addEventListener('astro:after-swap', () => {
loadThemeSwitchScript();
}, { once: false });
</script>

View File

@@ -0,0 +1,78 @@
---
import {formatDateToYYYYMMDD} from "../utils/date-utils";
import { Icon } from 'astro-icon/components';
interface Props {
class: string;
pubDate: Date;
tags: string[];
categories: string[];
}
const {pubDate, tags, categories} = Astro.props;
const className = Astro.props.class;
---
<div class:list={["flex flex-wrap text-neutral-500 dark:text-neutral-400 items-center gap-4 gap-x-4 gap-y-3", className]}>
<!-- publish date -->
<div class="flex items-center">
<div class="meta-icon"
>
<Icon name="material-symbols:calendar-today-outline-rounded" class="text-xl"></Icon>
</div>
<span class="text-black/50 dark:text-white/50 text-sm font-medium">{formatDateToYYYYMMDD(pubDate)}</span>
</div>
<!-- categories -->
<div class="flex items-center">
<div class="meta-icon"
>
<Icon name="material-symbols:menu-rounded" class="text-xl"></Icon>
</div>
<div class="flex flex-row">
{categories && categories.map(category => <div
class="with-divider"
>
<a href=`/archive/category/${category}`
class="transition text-black/50 dark:text-white/50 text-sm font-medium
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]">
{category}
</a>
</div>)}
{!categories && <div class="transition text-black/50 dark:text-white/50 text-sm font-medium">Uncategorized</div>}
</div>
</div>
<!-- tags -->
<div class="flex items-center">
<div class="meta-icon"
>
<Icon name="material-symbols:tag-rounded" class="text-xl"></Icon>
</div>
<div class="flex flex-row">
{tags.map(tag => <div
class="with-divider"
>
<a href=`/archive/tag/${tag}`
class="transition text-black/50 dark:text-white/50 text-sm font-medium
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]">
{tag}
</a>
</div>)}
</div>
</div>
</div>
<style>
@tailwind components;
@layer components {
.meta-icon {
@apply w-8 h-8 transition rounded-md flex items-center justify-center bg-[var(--btn-regular-bg)]
text-[var(--btn-content)] dark:text-[var(--primary)] mr-2
}
.with-divider {
@apply before:content-['/'] before:mx-[6px] before:text-[var(--meta-divider)] before:text-sm
before:font-medium before:first-of-type:hidden before:transition
}
}
</style>

View File

@@ -0,0 +1,83 @@
---
import {formatDateToYYYYMMDD} from "../utils/date-utils";
interface Props {
title: string;
url: string;
pubDate: Date;
tags: string[];
cover: string;
description: string;
words: number;
}
const { title, url, pubDate, tags, cover, description, words } = Astro.props;
// console.log(Astro.props);
import ImageBox from "./misc/ImageBox.astro";
import ButtonTag from "./control/ButtonTag.astro";
import { Icon } from 'astro-icon/components';
// tags = ['Foo', 'Bar', 'Baz', 'Qux', 'Quux'];
// const cover = 'https://saicaca.github.io/vivia-preview/assets/79905307_p0.jpg';
// cover = null;
const hasCover = cover !== undefined && cover !== null && cover !== '';
---
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative">
<div class:list={["card-base z-30 px-8 py-6 relative ",
{
'w-[calc(70%_+_var(--radius-large))]': hasCover,
'w-[calc(100%_-_76px_+_var(--radius-large))]': !hasCover,
}
]}>
<a href={url}
class="transition w-full block font-bold mb-1 text-3xl
text-neutral-900 dark:text-neutral-100
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)]
before:absolute before:top-8 before:left-4
">
This is a very long title
</a>
<div class="flex text-neutral-500 dark:text-neutral-400 items-center mb-1">
<div>{formatDateToYYYYMMDD(pubDate)}</div>
<div class="transition h-1 w-1 rounded-sm bg-neutral-400 dark:bg-neutral-600 mx-3"></div>
<div>Uncategorized</div>
<div class="transition h-1 w-1 rounded-sm bg-neutral-400 dark:bg-neutral-600 mx-3"></div>
<div>{words} words</div>
</div>
<div class="flex gap-2 mb-4">
{tags.map(t => (
<ButtonTag dot>{t}</ButtonTag>
))}
</div>
<div class="transition text-neutral-700 dark:text-neutral-300">This is the description of the article</div>
</div>
{!hasCover && <a href={url}
class="transition w-[72px]
bg-[var(--btn-enter-bg)] dark:bg-[var(--btn-enter-bg-dark)]
hover:bg-[var(--btn-card-bg-hover)] active:bg-[var(--btn-card-bg-active)]
absolute top-0 bottom-0 right-0 flex items-center">
<Icon name="material-symbols:chevron-right-rounded"
class="transition text-4xl text-[var(--primary)] ml-[22px]"></Icon>
</a>}
{hasCover && <a href={url}
class="group w-[30%] absolute top-0 bottom-0 right-0">
<div class="absolute z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition"></div>
<div class="absolute z-20 w-full h-full flex items-center justify-center ">
<Icon name="material-symbols:chevron-right-rounded"
class="transition opacity-0 group-hover:opacity-100 text-white text-5xl"></Icon>
</div>
<ImageBox src="https://saicaca.github.io/vivia-preview/assets/79905307_p0.jpg"
class="w-full h-full">
</ImageBox>
</a>}
</div>
<style lang="stylus">
:root
--btn-enter-bg oklch(0.98 0.005 var(--hue))
--btn-enter-bg-dark oklch(0.2 0.02 var(--hue))
</style>

View File

@@ -0,0 +1,78 @@
---
import PostMetadata from "./PostMetadata.astro";
interface Props {
class: string;
entry: any;
title: string;
url: string;
pubDate: Date;
tags: string[];
categories: string[];
cover: string;
description: string;
words: number;
}
const { entry, title, url, pubDate, tags, categories, cover, description, words } = Astro.props;
const className = Astro.props.class;
// console.log(Astro.props);
import ImageBox from "./misc/ImageBox.astro";
import ButtonTag from "./control/ButtonTag.astro";
import { Icon } from 'astro-icon/components';
// tags = ['Foo', 'Bar', 'Baz', 'Qux', 'Quux'];
// const cover = 'https://saicaca.github.io/vivia-preview/assets/79905307_p0.jpg';
// cover = null;
const hasCover = cover !== undefined && cover !== null && cover !== '';
const coverWidth = "30%";
const { remarkPluginFrontmatter } = await entry.render();
---
<div class:list={["card-base flex w-full rounded-[var(--radius-large)] overflow-hidden relative", className]}>
<div class:list={[" px-10 pt-7 pb-6 relative", {'w-full': !hasCover, "w-[calc(100%_-_var(--coverWidth))]": hasCover}]}>
<a href={url}
class="transition w-full block font-bold mb-3 text-4xl
text-black/90 dark:text-white/90
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
before:absolute before:top-[38px] before:left-5
">
{title}
</a>
<!-- metadata -->
<PostMetadata pubDate={pubDate} tags={tags} categories={categories} class="mb-4"></PostMetadata>
<div class="transition text-black/75 dark:text-white/75 mb-4">
{ description }
</div>
<div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition">
<div>{remarkPluginFrontmatter.words} words</div>
<div>|</div>
<div>{remarkPluginFrontmatter.minutes} minutes</div>
</div>
</div>
{hasCover && <a href={url}
class=`group w-[var(--coverWidth)] absolute top-3 bottom-3 right-3 rounded-xl overflow-hidden`>
<div class="absolute z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition"></div>
<div class="absolute z-20 w-full h-full flex items-center justify-center ">
<Icon name="material-symbols:chevron-right-rounded"
class="transition opacity-0 group-hover:opacity-100 text-white text-5xl"></Icon>
</div>
<ImageBox src="https://saicaca.github.io/vivia-preview/assets/79905307_p0.jpg"
class="w-full h-full">
</ImageBox>
</a>}
</div>
<style lang="stylus" define:vars={{coverWidth}}>
:root
--btn-enter-bg oklch(0.98 0.005 var(--hue))
--btn-enter-bg-dark oklch(0.2 0.02 var(--hue))
</style>

View File

@@ -0,0 +1,16 @@
---
interface Props {
name: string;
}
const {name} = Astro.props;
import BasicCard from "./BasicCard.astro";
---
<BasicCard >
<div class="p-4">
<div>{name}</div>
</div>
</BasicCard>

View File

@@ -0,0 +1,77 @@
---
interface Props {
id?: string;
isIcon?: boolean;
iconName?: string;
width?: string;
height?: string;
regular?: boolean;
light?: boolean
card?: boolean;
iconSize?: number,
class?: string
disabled?: boolean
}
const props = Astro.props;
const {
id,
isIcon = false,
iconName,
width,
height = '44px',
regular,
light,
iconSize = 24,
card,
disabled = false,
} = Astro.props;
const className = Astro.props.class;
import { Icon } from 'astro-icon/components';
---
<button id={id}
disabled={disabled}
class:list={[
className,
`
rounded-lg
transition
h-[var(--height)]
`,
{
'w-[var(--width)]': width,
'w-[var(--height)]': isIcon,
'bg-none': light,
'hover:bg-[var(--btn-plain-bg-hover)]': light,
'active:bg-[var(--btn-plain-bg-active)]': light,
'text-neutral-900': light,
'hover:text-[var(--primary)]': light,
'dark:text-neutral-300': light || regular,
'dark:hover:text-[var(--primary)]': light,
'bg-[var(--btn-regular-bg)]': regular,
'hover:bg-[oklch(0.9_0.05_var(--hue))]': regular,
'active:bg-[oklch(0.85_0.08_var(--hue))]': regular,
'text-[var(--btn-content)]': regular,
'dark:bg-[oklch(0.38_0.04_var(--hue))]': regular,
'dark:hover:bg-[oklch(0.45_0.045_var(--hue))]': regular,
'dark:active:bg-[oklch(0.5_0.05_var(--hue))]': regular,
'card-base': card,
'enabled:hover:bg-[var(--btn-card-bg-hover)]': card,
'enabled:active:bg-[var(--btn-card-bg-active)]': card,
'disabled:text-black/10': card,
'disabled:dark:text-white/10': card,
}
]}
>
{props.isIcon && <Icon class="mx-auto" name={props.iconName} size={iconSize}></Icon> }
<slot />
</button>
<style define:vars={{ height, width, iconSize }}>
</style>

View File

@@ -0,0 +1,42 @@
---
interface Props {
badge?: string
url?: string
}
const { badge, url } = Astro.props
---
<a href={url}>
<button
class:list={`
w-full
h-10
rounded-lg
bg-none
hover:bg-[var(--btn-plain-bg-hover)]
active:bg-[var(--btn-plain-bg-active)]
transition-all
pl-2
hover:pl-3
text-neutral-700
hover:text-[var(--primary)]
dark:text-neutral-300
dark:hover:text-[var(--primary)]
`
}
>
<div class="flex items-center justify-between relative mr-2">
<div class="overflow-hidden text-left whitespace-nowrap overflow-ellipsis ">
<slot></slot>
</div>
{ badge !== undefined && badge !== null && badge !== '' &&
<div class="transition h-[28px] ml-4 min-w-[32px] rounded-lg text-sm font-bold
text-[var(--btn-content)] dark:text-[var(--deep-text)]
bg-[oklch(0.95_0.025_var(--hue))] dark:bg-[var(--primary)]
flex items-center justify-center">
{ badge }
</div>
}
</div>
</button>
</a>

View File

@@ -0,0 +1,15 @@
---
import Button from "./Button.astro";
interface Props {
size?: string;
dot?: boolean;
href?: string;
}
const { size, dot, href }: Props = Astro.props;
---
<a href={href}>
<Button regular height="32px" class="text-[15px] px-3 flex flex-row items-center">
{dot && <div class="h-1 w-1 bg-[var(--btn-content)] dark:bg-[var(--card-bg)] transition rounded-md mr-2"></div>}
<slot></slot>
</Button>
</a>

View File

@@ -0,0 +1,84 @@
---
import type { Page } from "astro";
import { Icon } from 'astro-icon/components';
interface Props {
page: Page;
class?: string;
}
const {page} = Astro.props;
const HIDDEN = -1;
const className = Astro.props.class;
import Button from "./Button.astro";
const ADJ_DIST = 2;
const VISIBLE = ADJ_DIST * 2 + 1;
// for test
let count = 1;
let l = page.currentPage, r = page.currentPage;
while (0 < l - 1 && r + 1 <= page.lastPage && count + 2 <= VISIBLE) {
count += 2;
l--;
r++;
}
while (0 < l - 1 && count < VISIBLE) {
count++;
l--;
}
while (r + 1 <= page.lastPage && count < VISIBLE) {
count++;
r++;
}
let pages: number[] = [];
if (l > 1)
pages.push(1);
if (l == 3)
pages.push(2);
if (l > 3)
pages.push(HIDDEN);
for (let i = l; i <= r; i++)
pages.push(i);
if (r < page.lastPage - 2)
pages.push(HIDDEN);
if (r == page.lastPage - 2)
pages.push(page.lastPage - 1);
if (r < page.lastPage)
pages.push(page.lastPage);
const parts: string[] = page.url.current.split('/');
const commonUrl: string = parts.slice(0, -1).join('/') + '/';
---
<div class:list={[className, "flex flex-row gap-3 justify-center"]}>
<a href={page.url.prev}>
<Button isIcon card iconName="material-symbols:chevron-left-rounded" class="text-[var(--primary)]" iconSize={28}
disabled = {page.url.prev == undefined}
></Button>
</a>
<div class="bg-[var(--card-bg)] flex flex-row rounded-lg items-center text-neutral-700 dark:text-neutral-300 font-bold">
{pages.map((p) => {
if (p == HIDDEN)
return <Icon name="material-symbols:more-horiz" class="mx-1"/>;
if (p == page.currentPage)
return <div class="h-[44px] w-[44px] rounded-lg bg-[var(--primary)] flex items-center justify-center
font-bold text-white dark:text-black/70"
>
{p}
</div>
return <a href={commonUrl + p}>
<Button card iconName="material-symbols:chevron-left-rounded" height="44px" width="44px">
{p}
</Button>
</a>
})}
</div>
<a href={page.url.next}>
<Button isIcon card iconName="material-symbols:chevron-right-rounded" class="text-[var(--primary)]" iconSize={28}
disabled = {page.url.next == undefined}
></Button>
</a>
</div>

View File

@@ -0,0 +1,15 @@
---
interface Props {
id?: string
src: string;
class?: string;
alt?: string
}
const {id, src, alt} = Astro.props;
const className = Astro.props.class;
---
<div class:list={[className, 'overflow-hidden relative']}>
<div class="transition absolute top-0 bottom-0 left-0 right-0 dark:bg-black/10 bg-opacity-50"></div>
<img src={src} alt={alt} class="w-full h-full object-center object-cover">
</div>

View File

@@ -0,0 +1,26 @@
---
import ImageBox from "../misc/ImageBox.astro";
import ButtonLight from "../control/Button.astro";
import {getConfig} from "../../utils/config-utils";
interface props {
}
const className = Astro.props
const vConf = getConfig();
---
<div class="card-base" transition:persist>
<ImageBox src={vConf.profile.avatar} class="w-full rounded-2xl mb-3"></ImageBox>
<div class="font-bold text-lg text-center mb-1 dark:text-neutral-50 transition">{vConf.profile.author}</div>
<div class="h-1 w-5 bg-[var(--primary)] mx-auto rounded-full mb-3 transition"></div>
<div class="text-center text-neutral-400 mb-2 transition">{vConf.profile.subtitle}</div>
<div class="flex gap-2 mx-2 justify-center mb-4">
{vConf.profile.links.map(item =>
<a href={item.url} target="_blank">
<ButtonLight isIcon iconName={item.icon} regular height="40px"></ButtonLight>
</a>
)}
</div>
</div>

View File

@@ -0,0 +1,18 @@
---
import WidgetLayout from "./WidgetLayout.astro";
import ButtonLink from "../control/ButtonLink.astro";
import {getPostUrlBySlug, getSortedPosts} from "../../utils/content-utils";
let posts = await getSortedPosts()
const LIMIT = 5;
posts = posts.slice(0, LIMIT)
// console.log(posts)
---
<WidgetLayout name="Recent Posts">
{posts.map(post =>
<ButtonLink url={getPostUrlBySlug(post.slug)}>{post.data.title}</ButtonLink>
)}
</WidgetLayout>

View File

@@ -0,0 +1,27 @@
---
import Profile from "./Profile.astro";
import RecentPost from "./RecentPost.astro";
import Tag from "./Tag.astro";
const className = Astro.props.class;
---
<div id="sidebar" class:list={[className, "flex flex-col w-full gap-4"]} transition:persist>
<Profile></Profile>
<RecentPost></RecentPost>
<Tag></Tag>
</div>
<style>
#sidebar {
view-transition-name: ssss;
}
/* TODO temporarily */
html::view-transition-old(ssss) {
mix-blend-mode: normal;
animation: none;
}
html::view-transition-new(ssss) {
mix-blend-mode: normal;
animation: none;
}
</style>

View File

@@ -0,0 +1,18 @@
---
import WidgetLayout from "./WidgetLayout.astro";
import ButtonTag from "../control/ButtonTag.astro";
import {getTagList} from "../../utils/content-utils";
const tags = await getTagList();
---
<WidgetLayout name="Tags">
<div class="flex gap-2 flex-wrap">
{tags.map(t => (
<ButtonTag href={`/archive/tag/${t.name}`}>
{t.name}
</ButtonTag>
))}
</div>
</WidgetLayout>

View File

@@ -0,0 +1,18 @@
---
interface Props {
name?: string;
}
const props = Astro.props;
const {
name,
} = Astro.props
---
<div class="pb-4 card-base">
<div class="font-bold text-lg text-neutral-900 dark:text-neutral-100 transition relative ml-8 mt-4 mb-2
before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)]
before:absolute before:left-[-16px] before:top-[5.5px]">{name}</div>
<div class="px-4">
<slot></slot>
</div>
</div>