merge in new changes
This commit is contained in:
61
src/components/blog/BlogCard.astro
Normal file
61
src/components/blog/BlogCard.astro
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
import type { Post } from '@lib/directusTypes';
|
||||
|
||||
import { getDirectusImageURL } from '@lib/directusFunctions';
|
||||
import Image from '@components/ui/images/Image.astro';
|
||||
import { formatDate } from '@support/time';
|
||||
|
||||
interface Props {
|
||||
post: Post;
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
|
||||
const baseClasses = 'group group-hover smooth-reveal-cards rounded-xl flex flex-col';
|
||||
const borderClasses = 'border border-stone-200/50 dark:border-stone-700/50';
|
||||
const bgColorClasses =
|
||||
'bg-neutral-100/80 hover:bg-neutral-100 dark:bg-neutral-800/60 dark:hover:bg-neutral-800/90';
|
||||
const shadowClasses = 'shadow-xs hover:shadow-md dark:shadow-md dark:hover:shadow-lg';
|
||||
---
|
||||
|
||||
<div class={`${baseClasses}`}>
|
||||
<a
|
||||
class={`rounded-xl duration-300 transition-all ${borderClasses} ${shadowClasses} ${bgColorClasses}`}
|
||||
href={`/blog/${post.slug}/`}
|
||||
data-astro-prefetch
|
||||
>
|
||||
<div
|
||||
class="relative w-full flex-shrink-0 overflow-hidden rounded-t-xl before:absolute before:inset-x-0 before:z-[1] before:size-full"
|
||||
>
|
||||
<Image
|
||||
class="h-auto w-full rounded-t-xl"
|
||||
src={getDirectusImageURL(post.image)}
|
||||
alt={post.image_alt}
|
||||
draggable="false"
|
||||
loading="eager"
|
||||
format="webp"
|
||||
width="800"
|
||||
height="460"
|
||||
/>
|
||||
</div>
|
||||
<div class="rounded-xl p-4 md:p-5">
|
||||
<h3 class="text-xl font-bold text-neutral-600 dark:text-neutral-200">
|
||||
{post.title}
|
||||
</h3>
|
||||
<div
|
||||
class="group-hover:text-steel dark:group-hover:text-bermuda transition-text relative z-10 mx-auto flex min-h-[44px] items-center font-medium text-neutral-600 decoration-2 duration-300 sm:mx-0 sm:mt-4 dark:text-neutral-400"
|
||||
>
|
||||
<span class="relative inline-block overflow-hidden"> Read more </span>
|
||||
<Icon
|
||||
name="mdi:keyboard-arrow-right"
|
||||
class="h-3 w-3 translate-y-0.25 transition duration-300 group-hover:translate-x-1 md:h-5 md:w-5"
|
||||
/>
|
||||
<p class="ml-auto text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{formatDate(post.published_date)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
82
src/components/blog/BlogCategoryCard.astro
Normal file
82
src/components/blog/BlogCategoryCard.astro
Normal file
@@ -0,0 +1,82 @@
|
||||
---
|
||||
interface Props {
|
||||
slug: string;
|
||||
title: string;
|
||||
description: string;
|
||||
count: number;
|
||||
publishDate: string;
|
||||
layoutPattern?: {
|
||||
smCol: number;
|
||||
mdCol: number;
|
||||
row: number;
|
||||
index: number;
|
||||
};
|
||||
}
|
||||
|
||||
const { slug, layoutPattern, title, description, count, publishDate } = Astro.props;
|
||||
const isSingleItem =
|
||||
layoutPattern &&
|
||||
layoutPattern.row === 1 &&
|
||||
(layoutPattern.smCol === 1 || layoutPattern.mdCol === 1);
|
||||
|
||||
const formatedDescription = isSingleItem ? `No description available` : description;
|
||||
|
||||
const baseClasses =
|
||||
'group group-hover rounded-xl flex h-full min-h-[220px] cursor-pointer flex-col overflow-hidden';
|
||||
const bgColorClasses =
|
||||
'bg-neutral-100/60 dark:bg-neutral-800/60 hover:bg-neutral-100 dark:hover:bg-neutral-800/90 ';
|
||||
---
|
||||
|
||||
<a class={`rounded-xl`} href={`/categories/${slug}/`} data-astro-prefetch="false">
|
||||
<div class={`${baseClasses}`}>
|
||||
<div
|
||||
class={`relative min-h-0 flex-grow overflow-hidden transition-all duration-300 ${bgColorClasses}`}
|
||||
>
|
||||
<div class="absolute inset-1 flex flex-col justify-end p-3 md:p-4 lg:p-5">
|
||||
<h2
|
||||
class="group-hover:text-steel dark:group-hover:text-bermuda transition-text mb-4 text-4xl font-extrabold tracking-tight text-balance text-neutral-800 duration-300 dark:text-neutral-200"
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
<p
|
||||
class=`mb-4 ${isSingleItem ? 'hidden lg:block' : ''} max-w-prose text-pretty font-light text-neutral-600 dark:text-neutral-400 sm:text-lg`
|
||||
>
|
||||
{formatedDescription}
|
||||
</p>
|
||||
<div
|
||||
class="mt-auto flex items-center justify-between pt-1 text-xs text-neutral-600 md:pt-2 dark:text-neutral-300"
|
||||
>
|
||||
<span class="inline-flex items-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
class="mr-1"
|
||||
>
|
||||
<path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20"></path>
|
||||
</svg>
|
||||
{count}
|
||||
</span>
|
||||
<span class="inline-flex items-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
class="mr-1"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
{publishDate}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
36
src/components/blog/BlogFeaturedArticle.astro
Normal file
36
src/components/blog/BlogFeaturedArticle.astro
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
import type { Post } from '@lib/directusTypes';
|
||||
import BlogCard from '@components/blog/BlogCard.astro';
|
||||
|
||||
interface Props {
|
||||
posts: Post[];
|
||||
}
|
||||
|
||||
const { posts } = Astro.props;
|
||||
|
||||
const description = 'Here are a few articles that I think are not bad, hope you like too.';
|
||||
---
|
||||
|
||||
<section class="mx-auto mb-10 max-w-[85rem] px-4 py-8 sm:px-6 lg:px-8 2xl:max-w-full">
|
||||
<div class="text-left">
|
||||
<h2
|
||||
id="selected-articel"
|
||||
class="smooth-reveal-2 mb-4 text-5xl font-extrabold tracking-tight text-balance text-neutral-800 dark:text-neutral-200"
|
||||
>
|
||||
Posts
|
||||
</h2>
|
||||
<p
|
||||
class="smooth-reveal-2 mb-8 max-w-prose font-light text-pretty text-neutral-600 sm:text-xl dark:text-neutral-400"
|
||||
>
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row md:space-x-12 lg:space-x-16">
|
||||
<div class="w-full">
|
||||
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{posts.map((b) => <BlogCard post={b} />)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
44
src/components/blog/BlogLeftSection.astro
Normal file
44
src/components/blog/BlogLeftSection.astro
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
import PrimaryCTA from '@components/ui/buttons/PrimaryCTA.astro';
|
||||
import Image from '@components/ui/images/Image.astro';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
subTitle: string;
|
||||
btnExists?: boolean;
|
||||
btnTitle?: string;
|
||||
btnURL?: string;
|
||||
img: any;
|
||||
imgAlt: any;
|
||||
}
|
||||
|
||||
const { title, subTitle, btnExists, btnTitle, btnURL, img, imgAlt } = Astro.props;
|
||||
---
|
||||
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] items-center gap-8 px-4 py-10 sm:px-6 sm:py-16 md:grid md:grid-cols-2 lg:grid lg:grid-cols-2 lg:px-8 lg:py-14 xl:gap-16 2xl:max-w-full"
|
||||
>
|
||||
<Image
|
||||
class="h-full w-full rounded-xl object-cover sm:max-h-[320px] md:max-h-[360px]"
|
||||
src={img}
|
||||
alt={imgAlt}
|
||||
draggable="false"
|
||||
loading="lazy"
|
||||
width="850"
|
||||
height="420"
|
||||
/>
|
||||
|
||||
<div class="mt-4 md:mt-0">
|
||||
<h2
|
||||
class="mb-4 text-4xl font-extrabold tracking-tight text-balance text-neutral-800 dark:text-neutral-200"
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
<p
|
||||
class="mb-4 max-w-prose font-light text-pretty text-neutral-600 sm:text-lg dark:text-neutral-300"
|
||||
>
|
||||
{subTitle}
|
||||
</p>
|
||||
{btnExists ? <PrimaryCTA title={btnTitle} url={btnURL} /> : null}
|
||||
</div>
|
||||
</section>
|
||||
45
src/components/blog/BlogRecentCard.astro
Normal file
45
src/components/blog/BlogRecentCard.astro
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
import type { Post } from '@lib/directusTypes';
|
||||
|
||||
import { getDirectusImageURL } from '@lib/directusFunctions';
|
||||
import BlogLeftSection from '@components/blog/BlogLeftSection.astro';
|
||||
import BlogRightSection from '@components/blog/BlogRightSection.astro';
|
||||
|
||||
interface Props {
|
||||
posts: Post[];
|
||||
}
|
||||
|
||||
const { posts } = Astro.props;
|
||||
const blogPosts = posts.slice(0, 5);
|
||||
---
|
||||
|
||||
<section class="smooth-reveal">
|
||||
{
|
||||
blogPosts.map((b, index) =>
|
||||
index % 2 === 0 ? (
|
||||
<BlogLeftSection
|
||||
title={b.title}
|
||||
subTitle={b.description}
|
||||
btnExists={true}
|
||||
btnTitle="Read More"
|
||||
btnURL={`/blog/${b.slug}`}
|
||||
img={getDirectusImageURL(b.image)}
|
||||
imgAlt={b.image_alt}
|
||||
/>
|
||||
) : (
|
||||
<BlogRightSection
|
||||
title={b.title}
|
||||
subTitle={b.description}
|
||||
btnExists={true}
|
||||
btnTitle="Read More"
|
||||
btnURL={`/blog/${b.slug}`}
|
||||
single={!b.image_second}
|
||||
imgOne={getDirectusImageURL(b.image)}
|
||||
imgOneAlt={b.image_alt}
|
||||
imgTwo={getDirectusImageURL(b?.image_second)}
|
||||
imgTwoAlt={b?.image_second_alt}
|
||||
/>
|
||||
)
|
||||
)
|
||||
}
|
||||
</section>
|
||||
87
src/components/blog/BlogRightSection.astro
Normal file
87
src/components/blog/BlogRightSection.astro
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
import PrimaryCTA from '@components/ui/buttons/PrimaryCTA.astro';
|
||||
import Image from '@components/ui/images/Image.astro';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
subTitle: string;
|
||||
btnExists?: boolean;
|
||||
btnTitle?: string;
|
||||
btnURL?: string;
|
||||
single?: boolean;
|
||||
imgOne?: any;
|
||||
imgOneAlt?: any;
|
||||
imgTwo?: any;
|
||||
imgTwoAlt?: any;
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
subTitle,
|
||||
btnExists,
|
||||
btnTitle,
|
||||
btnURL,
|
||||
single,
|
||||
imgOne,
|
||||
imgOneAlt,
|
||||
imgTwo,
|
||||
imgTwoAlt,
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] items-center gap-16 px-4 py-10 sm:px-6 lg:grid lg:grid-cols-2 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<div>
|
||||
<h2
|
||||
class="mb-4 text-4xl font-extrabold tracking-tight text-balance text-neutral-800 dark:text-neutral-200"
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
<p
|
||||
class="mb-4 max-w-prose font-light text-pretty text-neutral-600 sm:text-lg dark:text-neutral-400"
|
||||
>
|
||||
{subTitle}
|
||||
</p>
|
||||
{btnExists ? <PrimaryCTA title={btnTitle} url={btnURL} /> : null}
|
||||
</div>
|
||||
|
||||
{
|
||||
single ? (
|
||||
<div class="mt-8">
|
||||
<Image
|
||||
class="w-full rounded-lg"
|
||||
src={imgOne}
|
||||
alt={imgOneAlt}
|
||||
format="webp"
|
||||
loading="lazy"
|
||||
width="850"
|
||||
height="420"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div class="mt-8 grid grid-cols-2 gap-4">
|
||||
<Image
|
||||
class="w-full rounded-xl"
|
||||
src={imgOne}
|
||||
alt={imgOneAlt}
|
||||
draggable="false"
|
||||
format="webp"
|
||||
loading="lazy"
|
||||
width="400"
|
||||
height="230"
|
||||
/>
|
||||
<Image
|
||||
class="mt-4 w-full rounded-xl lg:mt-10"
|
||||
src={imgTwo}
|
||||
alt={imgTwoAlt}
|
||||
draggable="false"
|
||||
format="webp"
|
||||
loading="lazy"
|
||||
width="400"
|
||||
height="230"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</section>
|
||||
Reference in New Issue
Block a user