@stackshift-ui/blog 6.0.4 → 6.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blog.d.ts +1 -1
- package/dist/blog.js +1 -1
- package/dist/blog.mjs +1 -1
- package/dist/chunk-25V6I4CL.mjs +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/types.d.ts +1 -0
- package/package.json +18 -17
- package/src/blog.test.tsx +13 -0
- package/src/blog.tsx +32 -0
- package/src/blog_a.tsx +121 -0
- package/src/blog_b.tsx +158 -0
- package/src/blog_c.tsx +142 -0
- package/src/blog_d.tsx +349 -0
- package/src/hooks/useMediaQuery.ts +27 -0
- package/src/index.ts +8 -0
- package/src/types.ts +413 -0
- package/dist/chunk-VDHKVAJM.mjs +0 -1
package/dist/types.d.ts
CHANGED
|
@@ -181,6 +181,7 @@ export interface Variants {
|
|
|
181
181
|
signinLink?: LabeledRoute | null;
|
|
182
182
|
tags?: string[] | null;
|
|
183
183
|
posts?: BlogPost[] | null;
|
|
184
|
+
blogPosts?: BlogPost[] | null;
|
|
184
185
|
blogsPerPage?: number | null;
|
|
185
186
|
form?: Form | null;
|
|
186
187
|
collections?: Collection | null;
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackshift-ui/blog",
|
|
3
3
|
"description": "",
|
|
4
|
-
"version": "6.0.
|
|
4
|
+
"version": "6.0.6",
|
|
5
5
|
"private": false,
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"module": "./dist/index.mjs",
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"files": [
|
|
11
|
-
"dist/**"
|
|
11
|
+
"dist/**",
|
|
12
|
+
"src"
|
|
12
13
|
],
|
|
13
14
|
"author": "WebriQ <info@webriq.com>",
|
|
14
15
|
"devDependencies": {
|
|
@@ -29,24 +30,24 @@
|
|
|
29
30
|
"typescript": "^5.6.2",
|
|
30
31
|
"vite-tsconfig-paths": "^5.0.1",
|
|
31
32
|
"vitest": "^2.1.1",
|
|
32
|
-
"@stackshift-ui/
|
|
33
|
-
"@stackshift-ui/
|
|
33
|
+
"@stackshift-ui/typescript-config": "6.0.2",
|
|
34
|
+
"@stackshift-ui/eslint-config": "6.0.2"
|
|
34
35
|
},
|
|
35
36
|
"dependencies": {
|
|
36
37
|
"classnames": "^2.5.1",
|
|
37
|
-
"@stackshift-ui/
|
|
38
|
-
"@stackshift-ui/button": "6.0.
|
|
39
|
-
"@stackshift-ui/
|
|
40
|
-
"@stackshift-ui/
|
|
41
|
-
"@stackshift-ui/
|
|
42
|
-
"@stackshift-ui/
|
|
43
|
-
"@stackshift-ui/
|
|
44
|
-
"@stackshift-ui/input": "6.0.
|
|
45
|
-
"@stackshift-ui/
|
|
46
|
-
"@stackshift-ui/
|
|
47
|
-
"@stackshift-ui/
|
|
48
|
-
"@stackshift-ui/
|
|
49
|
-
"@stackshift-ui/
|
|
38
|
+
"@stackshift-ui/container": "6.0.3",
|
|
39
|
+
"@stackshift-ui/button": "6.0.3",
|
|
40
|
+
"@stackshift-ui/flex": "6.0.3",
|
|
41
|
+
"@stackshift-ui/link": "6.0.3",
|
|
42
|
+
"@stackshift-ui/heading": "6.0.3",
|
|
43
|
+
"@stackshift-ui/scripts": "6.0.2",
|
|
44
|
+
"@stackshift-ui/section": "6.0.3",
|
|
45
|
+
"@stackshift-ui/input": "6.0.4",
|
|
46
|
+
"@stackshift-ui/system": "6.0.3",
|
|
47
|
+
"@stackshift-ui/text": "6.0.3",
|
|
48
|
+
"@stackshift-ui/image": "6.0.3",
|
|
49
|
+
"@stackshift-ui/badge": "6.0.3",
|
|
50
|
+
"@stackshift-ui/card": "6.0.3"
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
|
52
53
|
"@stackshift-ui/system": ">=0.0.0",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
2
|
+
import { afterEach, describe, test } from "vitest";
|
|
3
|
+
import { Blog } from "./blog";
|
|
4
|
+
|
|
5
|
+
describe.concurrent("blog", () => {
|
|
6
|
+
afterEach(cleanup);
|
|
7
|
+
|
|
8
|
+
test.skip("Dummy test - test if renders without errors", ({ expect }) => {
|
|
9
|
+
const clx = "my-class";
|
|
10
|
+
render(<Blog />);
|
|
11
|
+
expect(screen.getByTestId("{ kebabCase name }}").classList).toContain(clx);
|
|
12
|
+
});
|
|
13
|
+
});
|
package/src/blog.tsx
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { lazy } from "react";
|
|
2
|
+
import { BlogPost, LabeledRoute, SectionsProps } from "./types";
|
|
3
|
+
|
|
4
|
+
const Variants = {
|
|
5
|
+
variant_a: lazy(() => import("./blog_a")),
|
|
6
|
+
variant_b: lazy(() => import("./blog_b")),
|
|
7
|
+
variant_c: lazy(() => import("./blog_c")),
|
|
8
|
+
variant_d: lazy(() => import("./blog_d")),
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface BlogProps {
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
title?: string;
|
|
14
|
+
posts?: BlogPost[];
|
|
15
|
+
primaryButton?: LabeledRoute;
|
|
16
|
+
}
|
|
17
|
+
const displayName = "Blog";
|
|
18
|
+
|
|
19
|
+
export const Blog: React.FC<SectionsProps> = ({ data }) => {
|
|
20
|
+
const variant = data?.variant;
|
|
21
|
+
const Variant = variant && Variants[variant as keyof typeof Variants];
|
|
22
|
+
|
|
23
|
+
const props = {
|
|
24
|
+
subtitle: data?.variants?.subtitle ?? undefined,
|
|
25
|
+
title: data?.variants?.title ?? undefined,
|
|
26
|
+
posts: (data?.variants?.posts || data?.variants?.blogPosts) ?? undefined,
|
|
27
|
+
primaryButton: data?.variants?.primaryButton ?? undefined,
|
|
28
|
+
};
|
|
29
|
+
return Variant ? <Variant {...props} /> : null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
Blog.displayName = displayName;
|
package/src/blog_a.tsx
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Button } from "@stackshift-ui/button";
|
|
2
|
+
import { Container } from "@stackshift-ui/container";
|
|
3
|
+
import { Flex } from "@stackshift-ui/flex";
|
|
4
|
+
import { Heading } from "@stackshift-ui/heading";
|
|
5
|
+
import { Image } from "@stackshift-ui/image";
|
|
6
|
+
import { Link } from "@stackshift-ui/link";
|
|
7
|
+
import { Section } from "@stackshift-ui/section";
|
|
8
|
+
import { Text } from "@stackshift-ui/text";
|
|
9
|
+
import { format } from "date-fns";
|
|
10
|
+
|
|
11
|
+
import { BlogProps } from ".";
|
|
12
|
+
import { BlogPost, Category, LabeledRoute } from "./types";
|
|
13
|
+
|
|
14
|
+
export default function Blog_A({ subtitle, title, posts, primaryButton }: BlogProps) {
|
|
15
|
+
return (
|
|
16
|
+
<Section className="py-20 bg-background">
|
|
17
|
+
<Container maxWidth={1280}>
|
|
18
|
+
<SubtitleAndTitleText subtitle={subtitle} title={title} />
|
|
19
|
+
<BlogPosts posts={posts} />
|
|
20
|
+
<PrimaryButton primaryButton={primaryButton} />
|
|
21
|
+
</Container>
|
|
22
|
+
</Section>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function SubtitleAndTitleText({ subtitle, title }: { subtitle?: string; title?: string }) {
|
|
27
|
+
return (
|
|
28
|
+
<div className="mb-16 text-center">
|
|
29
|
+
{subtitle ? (
|
|
30
|
+
<Text weight="bold" className="text-secondary">
|
|
31
|
+
{subtitle}
|
|
32
|
+
</Text>
|
|
33
|
+
) : null}
|
|
34
|
+
{title ? <Heading fontSize="3xl">{title}</Heading> : null}
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function BlogPosts({ posts }: { posts?: BlogPost[] }) {
|
|
40
|
+
if (!posts) return null;
|
|
41
|
+
|
|
42
|
+
let blogsPerPage = 6,
|
|
43
|
+
count = 0;
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Flex gap={4} className="flex-col lg:flex-row">
|
|
47
|
+
<div className="w-full space-y-5 lg:w-1/2">
|
|
48
|
+
{posts?.slice(count, count + 1)?.map((post, key) => <BlogItem post={post} key={key} />)}
|
|
49
|
+
<Flex gap={4} className="flex-col lg:flex-row">
|
|
50
|
+
{posts
|
|
51
|
+
?.slice(count + 1, count + 3)
|
|
52
|
+
?.map((post, key) => <BlogItem post={post} key={key} />)}
|
|
53
|
+
</Flex>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div className="w-full space-y-5 lg:w-1/2">
|
|
57
|
+
<Flex gap={4} className="flex-col lg:flex-row">
|
|
58
|
+
{posts
|
|
59
|
+
?.slice(count + 3, count + 5)
|
|
60
|
+
?.map((post, key) => <BlogItem post={post} key={key} />)}
|
|
61
|
+
</Flex>
|
|
62
|
+
{posts
|
|
63
|
+
?.slice(count + 5, blogsPerPage)
|
|
64
|
+
?.map((post, key) => <BlogItem post={post} key={key} />)}
|
|
65
|
+
</div>
|
|
66
|
+
</Flex>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function BlogItem({ post }: { post?: BlogPost }) {
|
|
71
|
+
return (
|
|
72
|
+
<div className="relative w-full h-64 rounded">
|
|
73
|
+
{post?.mainImage ? (
|
|
74
|
+
<Image
|
|
75
|
+
className="relative object-cover w-full h-full overflow-hidden rounded-global"
|
|
76
|
+
src={`${post?.mainImage}`}
|
|
77
|
+
alt={`blog-variantA-image`}
|
|
78
|
+
sizes="(min-width: 1540px) 740px, (min-width: 1280px) 612px, (min-width: 1040px) 484px, (min-width: 780px) 736px, (min-width: 680px) 608px, calc(94.44vw - 15px)"
|
|
79
|
+
/>
|
|
80
|
+
) : null}
|
|
81
|
+
<div className="absolute inset-0 bg-gray-900 rounded-global opacity-75" />
|
|
82
|
+
<div className="absolute inset-0 flex flex-col items-start p-6">
|
|
83
|
+
{post?.categories ? (
|
|
84
|
+
<div className="absolute flex left-5 top-5">
|
|
85
|
+
{post?.categories?.map((category: Category, index: number) => (
|
|
86
|
+
<span
|
|
87
|
+
className="px-3 py-1 mb-auto mr-3 text-sm font-bold uppercase bg-white rounded-full text-primary"
|
|
88
|
+
key={index}>
|
|
89
|
+
{category?.title}
|
|
90
|
+
</span>
|
|
91
|
+
))}
|
|
92
|
+
</div>
|
|
93
|
+
) : null}
|
|
94
|
+
<span className="mt-auto text-sm text-gray-500">
|
|
95
|
+
{post?.publishedAt ? format(new Date(post?.publishedAt), "dd MMM, yyyy") : ""}
|
|
96
|
+
</span>
|
|
97
|
+
{post?.title ? (
|
|
98
|
+
<Link
|
|
99
|
+
className="text-lg font-bold text-white transform hover:scale-110 hover:text-secondary motion-reduce:transform-none"
|
|
100
|
+
href={`/${post?.link}`}>
|
|
101
|
+
{post?.title?.length > 40 ? post?.title.substring(0, 40) + "..." : post?.title}
|
|
102
|
+
</Link>
|
|
103
|
+
) : null}
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function PrimaryButton({ primaryButton }: { primaryButton?: LabeledRoute }) {
|
|
110
|
+
if (!primaryButton?.label) return null;
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div className="mt-10 text-center">
|
|
114
|
+
<Button as="link" link={primaryButton} ariaLabel={primaryButton?.label}>
|
|
115
|
+
{primaryButton?.label}
|
|
116
|
+
</Button>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export { Blog_A };
|
package/src/blog_b.tsx
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { Button } from "@stackshift-ui/button";
|
|
2
|
+
import { Container } from "@stackshift-ui/container";
|
|
3
|
+
import { Flex } from "@stackshift-ui/flex";
|
|
4
|
+
import { Heading } from "@stackshift-ui/heading";
|
|
5
|
+
import { Image } from "@stackshift-ui/image";
|
|
6
|
+
import { Link } from "@stackshift-ui/link";
|
|
7
|
+
import { Section } from "@stackshift-ui/section";
|
|
8
|
+
import { Text } from "@stackshift-ui/text";
|
|
9
|
+
import { format } from "date-fns";
|
|
10
|
+
import { BlogProps } from ".";
|
|
11
|
+
import { useMediaQuery } from "./hooks/useMediaQuery";
|
|
12
|
+
import { BlogPost, LabeledRoute } from "./types";
|
|
13
|
+
|
|
14
|
+
export default function Blog_B({ subtitle, title, posts, primaryButton }: BlogProps) {
|
|
15
|
+
const blogsPerPage = 5;
|
|
16
|
+
const count = 0;
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Section className="py-20 bg-background">
|
|
20
|
+
<Container maxWidth={1280}>
|
|
21
|
+
<SubtitleAndTitle subtitle={subtitle} title={title} />
|
|
22
|
+
<BlogPosts posts={posts} count={count} blogsPerPage={blogsPerPage} />
|
|
23
|
+
<PrimaryButton primaryButton={primaryButton} />
|
|
24
|
+
</Container>
|
|
25
|
+
</Section>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function SubtitleAndTitle({ subtitle, title }: { subtitle?: string; title?: string }) {
|
|
30
|
+
return (
|
|
31
|
+
<div className="w-full mb-16 text-center">
|
|
32
|
+
{subtitle ? (
|
|
33
|
+
<Text weight="bold" className="text-secondary">
|
|
34
|
+
{subtitle}
|
|
35
|
+
</Text>
|
|
36
|
+
) : null}
|
|
37
|
+
{title ? <Heading fontSize="3xl">{title}</Heading> : null}
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function BlogPosts({
|
|
43
|
+
posts,
|
|
44
|
+
count,
|
|
45
|
+
blogsPerPage,
|
|
46
|
+
}: {
|
|
47
|
+
posts?: BlogPost[];
|
|
48
|
+
count: number;
|
|
49
|
+
blogsPerPage: number;
|
|
50
|
+
}) {
|
|
51
|
+
return (
|
|
52
|
+
<Flex wrap justify="center" className="mb-16" gap={4}>
|
|
53
|
+
<div className="w-full lg:w-[45%]">
|
|
54
|
+
{posts
|
|
55
|
+
?.slice(count, count + 1)
|
|
56
|
+
.map((post, key) => <BlogItem size="lg" post={post} key={key} />)}
|
|
57
|
+
</div>
|
|
58
|
+
<Flex wrap className="w-full lg:w-[45%]" gap={4}>
|
|
59
|
+
{posts?.slice(count + 1, blogsPerPage).map((post, key) => (
|
|
60
|
+
<div className="w-full lg:basis-[45%]" key={key}>
|
|
61
|
+
<BlogItem post={post} size="sm" />
|
|
62
|
+
</div>
|
|
63
|
+
))}
|
|
64
|
+
</Flex>
|
|
65
|
+
</Flex>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function BlogItem({ post, size }: { post: BlogPost; size?: string }) {
|
|
70
|
+
const breakpoints = useMediaQuery("1024");
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div className="overflow-hidden rounded-global shadow">
|
|
74
|
+
{post?.mainImage ? (
|
|
75
|
+
<ImageContainer post={post} size={size} breakpoints={breakpoints} />
|
|
76
|
+
) : null}
|
|
77
|
+
<div className="p-6 bg-white flex flex-col justify-between" style={{ height: "295px" }}>
|
|
78
|
+
<div>
|
|
79
|
+
{post?.publishedAt ? (
|
|
80
|
+
<Text muted className="text-sm">
|
|
81
|
+
{format(new Date(post.publishedAt), " dd MMM, yyyy")}
|
|
82
|
+
</Text>
|
|
83
|
+
) : null}
|
|
84
|
+
{post?.title ? (
|
|
85
|
+
<Heading type="h4" className="my-2">
|
|
86
|
+
{post.title.length > 25 ? `${post.title.substring(0, 25)}...` : post.title}
|
|
87
|
+
</Heading>
|
|
88
|
+
) : null}
|
|
89
|
+
{post?.excerpt ? (
|
|
90
|
+
<Text muted className="mb-6 text-justify">
|
|
91
|
+
{post.excerpt.length > 41 ? `${post.excerpt.substring(0, 41)}...` : post.excerpt}
|
|
92
|
+
</Text>
|
|
93
|
+
) : null}
|
|
94
|
+
</div>
|
|
95
|
+
{post?.link ? (
|
|
96
|
+
<Link
|
|
97
|
+
aria-label="View Blog Post"
|
|
98
|
+
className="font-bold text-primary hover:text-secondary"
|
|
99
|
+
href={`/${post?.link}`}>
|
|
100
|
+
View Blog Post
|
|
101
|
+
</Link>
|
|
102
|
+
) : null}
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function ImageContainer({
|
|
109
|
+
post,
|
|
110
|
+
size,
|
|
111
|
+
breakpoints,
|
|
112
|
+
}: {
|
|
113
|
+
post: BlogPost;
|
|
114
|
+
size?: string;
|
|
115
|
+
breakpoints: boolean;
|
|
116
|
+
}) {
|
|
117
|
+
return (
|
|
118
|
+
<>
|
|
119
|
+
{breakpoints ? (
|
|
120
|
+
<Image
|
|
121
|
+
className="object-cover w-full overflow-hidden"
|
|
122
|
+
src={`${post.mainImage}`}
|
|
123
|
+
sizes="100vw"
|
|
124
|
+
style={{ width: "100%", height: "auto", objectFit: "cover" }}
|
|
125
|
+
width={271}
|
|
126
|
+
height={248}
|
|
127
|
+
alt={`blog-variantB-image-${post.title}`}
|
|
128
|
+
/>
|
|
129
|
+
) : (
|
|
130
|
+
<div className={`${size === "lg" ? "h-[44.5rem]" : "h-[12.5rem]"}`}>
|
|
131
|
+
<Image
|
|
132
|
+
className="object-cover w-full overflow-hidden rounded-t-global"
|
|
133
|
+
src={`${post.mainImage}`}
|
|
134
|
+
sizes="100vw"
|
|
135
|
+
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
|
136
|
+
width={271}
|
|
137
|
+
height={248}
|
|
138
|
+
alt={`blog-variantB-image-${post.title}`}
|
|
139
|
+
/>
|
|
140
|
+
</div>
|
|
141
|
+
)}
|
|
142
|
+
</>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function PrimaryButton({ primaryButton }: { primaryButton?: LabeledRoute }) {
|
|
147
|
+
if (!primaryButton?.label) return null;
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<div className="text-center">
|
|
151
|
+
<Button as="link" link={primaryButton} ariaLabel={primaryButton?.label}>
|
|
152
|
+
{primaryButton?.label}
|
|
153
|
+
</Button>
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export { Blog_B };
|
package/src/blog_c.tsx
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Badge } from "@stackshift-ui/badge";
|
|
2
|
+
import { Button } from "@stackshift-ui/button";
|
|
3
|
+
import { Container } from "@stackshift-ui/container";
|
|
4
|
+
import { Flex } from "@stackshift-ui/flex";
|
|
5
|
+
import { Heading } from "@stackshift-ui/heading";
|
|
6
|
+
import { Image } from "@stackshift-ui/image";
|
|
7
|
+
import { Link } from "@stackshift-ui/link";
|
|
8
|
+
import { Section } from "@stackshift-ui/section";
|
|
9
|
+
import { Text } from "@stackshift-ui/text";
|
|
10
|
+
import { format } from "date-fns";
|
|
11
|
+
import React from "react";
|
|
12
|
+
import { BlogProps } from ".";
|
|
13
|
+
import { useMediaQuery } from "./hooks/useMediaQuery";
|
|
14
|
+
import { BlogPost, LabeledRoute } from "./types";
|
|
15
|
+
|
|
16
|
+
export default function Blog_C({ subtitle, title, posts, primaryButton }: BlogProps) {
|
|
17
|
+
let blogsPerPage = 3;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Section className="py-20 bg-background">
|
|
21
|
+
<Container maxWidth={1280}>
|
|
22
|
+
<Flex align="center" justify="between" className="flex-col mb-16 md:flex-row" gap={4}>
|
|
23
|
+
<SubtitleAndTitleText subtitle={subtitle} title={title} />
|
|
24
|
+
<PrimaryButton primaryButton={primaryButton} />
|
|
25
|
+
</Flex>
|
|
26
|
+
<BlogPosts posts={posts} blogsPerPage={blogsPerPage} />
|
|
27
|
+
<PrimaryButton primaryButton={primaryButton} />
|
|
28
|
+
</Container>
|
|
29
|
+
</Section>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function SubtitleAndTitleText({ subtitle, title }: { subtitle?: string; title?: string }) {
|
|
34
|
+
return (
|
|
35
|
+
<div className="text-center md:text-left">
|
|
36
|
+
{subtitle ? (
|
|
37
|
+
<Text weight="bold" className="text-primary">
|
|
38
|
+
{subtitle}
|
|
39
|
+
</Text>
|
|
40
|
+
) : null}
|
|
41
|
+
{title ? <Heading fontSize="3xl">{title}</Heading> : null}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function BlogPosts({ posts, blogsPerPage }: { posts?: BlogPost[]; blogsPerPage?: number }) {
|
|
47
|
+
if (!posts) return null;
|
|
48
|
+
return (
|
|
49
|
+
<div>
|
|
50
|
+
{posts?.slice(0, blogsPerPage)?.map((post, key) => (
|
|
51
|
+
<div className="flex flex-wrap mb-8 overflow-hidden rounded-global shadow" key={key}>
|
|
52
|
+
<BlogItem
|
|
53
|
+
post={post}
|
|
54
|
+
className={`${key % 2 === 0 ? "flex-row" : "flex-row-reverse"}`}
|
|
55
|
+
key={key}
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
))}
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function BlogItem({ post, className }: { post: BlogPost; className?: string }) {
|
|
64
|
+
const breakpoints = useMediaQuery("1100");
|
|
65
|
+
const maxExcerptLength = breakpoints ? 70 : 200;
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Flex wrap className={`bg-white overflow-hidden rounded-lg shadow w-full ${className}`}>
|
|
69
|
+
{post?.mainImage && (
|
|
70
|
+
<Image
|
|
71
|
+
className="object-cover w-full h-auto rounded-l lg:w-1/2"
|
|
72
|
+
src={`${post?.mainImage}`}
|
|
73
|
+
sizes="100vw"
|
|
74
|
+
width={554}
|
|
75
|
+
height={416}
|
|
76
|
+
alt={`blog-variantC-image-`}
|
|
77
|
+
/>
|
|
78
|
+
)}
|
|
79
|
+
<div className="w-full px-6 py-6 rounded-r lg:w-1/2 lg:pt-10">
|
|
80
|
+
<Flex gap={2}>
|
|
81
|
+
{post?.categories &&
|
|
82
|
+
post?.categories?.map((category, index) => (
|
|
83
|
+
<Badge className=" bg-secondary-foreground text-primary" key={index}>
|
|
84
|
+
{category?.title}
|
|
85
|
+
</Badge>
|
|
86
|
+
))}
|
|
87
|
+
</Flex>
|
|
88
|
+
|
|
89
|
+
{post?.publishedAt && (
|
|
90
|
+
<Text muted className="m-1">
|
|
91
|
+
{format(new Date(post?.publishedAt), " dd MMM, yyyy")}
|
|
92
|
+
</Text>
|
|
93
|
+
)}
|
|
94
|
+
{post?.title && (
|
|
95
|
+
<Heading className="my-4" type="h3">
|
|
96
|
+
{post?.title?.length > 40 ? post?.title?.substring(0, 40) + "..." : post?.title}
|
|
97
|
+
</Heading>
|
|
98
|
+
)}
|
|
99
|
+
{post?.authors && (
|
|
100
|
+
<div className="flex mb-10 flex-wrap">
|
|
101
|
+
<span className="italic text-primary">By </span>
|
|
102
|
+
{post?.authors?.map((author, index, { length }) => (
|
|
103
|
+
<>
|
|
104
|
+
<Text className="italic text-primary">{author?.name}</Text>
|
|
105
|
+
{index + 1 !== length ? <span> , </span> : null}
|
|
106
|
+
</>
|
|
107
|
+
))}
|
|
108
|
+
</div>
|
|
109
|
+
)}
|
|
110
|
+
{post?.excerpt && (
|
|
111
|
+
<Text muted className="mb-6 leading-loose text-justify">
|
|
112
|
+
{post?.excerpt?.length > maxExcerptLength
|
|
113
|
+
? post?.excerpt?.substring(0, maxExcerptLength) + "..."
|
|
114
|
+
: post?.excerpt}
|
|
115
|
+
</Text>
|
|
116
|
+
)}
|
|
117
|
+
{post?.link && (
|
|
118
|
+
<Link
|
|
119
|
+
aria-label="View Blog Post"
|
|
120
|
+
className="font-bold text-primary hover:text-primary-foreground"
|
|
121
|
+
href={`/${post?.link}`}>
|
|
122
|
+
View Blog Post
|
|
123
|
+
</Link>
|
|
124
|
+
)}
|
|
125
|
+
</div>
|
|
126
|
+
</Flex>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function PrimaryButton({ primaryButton }: { primaryButton?: LabeledRoute }) {
|
|
131
|
+
if (!primaryButton?.label) return null;
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<React.Fragment>
|
|
135
|
+
<Button as="link" link={primaryButton} ariaLabel={primaryButton?.label}>
|
|
136
|
+
{primaryButton?.label}
|
|
137
|
+
</Button>
|
|
138
|
+
</React.Fragment>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export { Blog_C };
|