@weekend-studio/easyblog-next 0.0.1

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/README.md ADDED
@@ -0,0 +1,16 @@
1
+ ## Easyblog
2
+
3
+ - `npm install @weekend-studio/easyblog`
4
+ - Publish: `npm publish --access public`
5
+
6
+ ## NextJS with App Router
7
+
8
+
9
+ ## NextJS with Page Router
10
+
11
+ ## Release
12
+
13
+ - Create a release branch from main: `git checkout -b release/v1.0.0`
14
+ - Update version in package.json: `npm version patch/minor/major`
15
+ - Push release branch: `git push -u origin release/v1.0.0`
16
+ - Create a Pull Request to merge into main
@@ -0,0 +1,39 @@
1
+ import { EasyBlogConfig, IQueryParams } from './models.js';
2
+ import { Metadata } from 'next';
3
+ import React from 'react';
4
+
5
+ declare function getBlogPosts(config: EasyBlogConfig, params?: IQueryParams): Promise<any>;
6
+ declare function getBlogPost(config: EasyBlogConfig, slug: string): Promise<any>;
7
+ declare function getBlogPaths(config: EasyBlogConfig): Promise<any>;
8
+ declare function generateMetadata(config: EasyBlogConfig, slug?: string): Promise<Metadata>;
9
+
10
+ interface NextBlogPageProps {
11
+ config: EasyBlogConfig;
12
+ slug: string;
13
+ style?: React.CSSProperties;
14
+ }
15
+ declare function NextBlogPage(props: NextBlogPageProps): Promise<React.JSX.Element>;
16
+
17
+ interface NextBlogListPageProps {
18
+ config: EasyBlogConfig;
19
+ type: 'grid' | 'list';
20
+ searchParams?: {
21
+ page?: string;
22
+ category?: string;
23
+ tags?: string;
24
+ };
25
+ displayOptions?: {
26
+ showThumbnail?: boolean;
27
+ showReadingTime?: boolean;
28
+ showExcerpt?: boolean;
29
+ showTags?: boolean;
30
+ showDate?: boolean;
31
+ showAuthor?: boolean;
32
+ showCategory?: boolean;
33
+ isNextPage?: boolean;
34
+ blogPerPage?: number;
35
+ };
36
+ }
37
+ declare function NextBlogListPage(props: NextBlogListPageProps): Promise<React.JSX.Element>;
38
+
39
+ export { NextBlogListPage, NextBlogPage, generateMetadata, getBlogPaths, getBlogPost, getBlogPosts };
@@ -0,0 +1,585 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __spreadValues = (a, b) => {
7
+ for (var prop in b || (b = {}))
8
+ if (__hasOwnProp.call(b, prop))
9
+ __defNormalProp(a, prop, b[prop]);
10
+ if (__getOwnPropSymbols)
11
+ for (var prop of __getOwnPropSymbols(b)) {
12
+ if (__propIsEnum.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ }
15
+ return a;
16
+ };
17
+ var __objRest = (source, exclude) => {
18
+ var target = {};
19
+ for (var prop in source)
20
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
21
+ target[prop] = source[prop];
22
+ if (source != null && __getOwnPropSymbols)
23
+ for (var prop of __getOwnPropSymbols(source)) {
24
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
25
+ target[prop] = source[prop];
26
+ }
27
+ return target;
28
+ };
29
+
30
+ // src/app/NextAppRouterUtils.ts
31
+ import { cache } from "react";
32
+ import { notFound } from "next/navigation";
33
+
34
+ // src/app/BaseUtils.ts
35
+ async function exchangeToken(config) {
36
+ try {
37
+ const response = await fetch(`${config.apiUrl}/exchange-token`, {
38
+ method: "POST",
39
+ headers: {
40
+ "Authorization": `Bearer ${config.apiKey}`,
41
+ "x-project-id": config.projectId
42
+ }
43
+ });
44
+ if (!response.ok) {
45
+ console.error("Failed to exchange token:", response);
46
+ throw new Error("Failed to authenticate");
47
+ }
48
+ const data = await response.json();
49
+ return data.token;
50
+ } catch (error) {
51
+ console.error("Failed to exchange token:", error);
52
+ return null;
53
+ }
54
+ }
55
+
56
+ // src/app/lib/utils.ts
57
+ import { clsx } from "clsx";
58
+ import { twMerge } from "tailwind-merge";
59
+ function cn(...inputs) {
60
+ return twMerge(clsx(inputs));
61
+ }
62
+ var getFormattedDate = (date) => {
63
+ if (!date) return "";
64
+ return new Date(date).toISOString().split("T")[0];
65
+ };
66
+ var getVisiblePages = (current, total) => {
67
+ if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);
68
+ if (current <= 3) return [1, 2, 3, 4, 5, null, total];
69
+ if (current >= total - 2) return [1, null, total - 4, total - 3, total - 2, total - 1, total];
70
+ return [1, null, current - 1, current, current + 1, null, total];
71
+ };
72
+
73
+ // src/app/NextAppRouterUtils.ts
74
+ var getAuthToken = cache(async (config) => {
75
+ return exchangeToken(config);
76
+ });
77
+ async function getBlogPosts(config, params = {}) {
78
+ const token = await getAuthToken(config);
79
+ const queryString = new URLSearchParams(
80
+ Object.entries(params).map(([key, value]) => [key, String(value)])
81
+ ).toString();
82
+ const url = `${config.apiUrl}/sdk/blogs${queryString ? `?${queryString}` : ""}`;
83
+ const response = await fetch(url, {
84
+ headers: {
85
+ "Authorization": `Bearer ${token}`,
86
+ "x-project-id": config.projectId
87
+ },
88
+ next: { revalidate: 3600 }
89
+ });
90
+ if (!response.ok) {
91
+ console.error("Failed to fetch blog posts:", response);
92
+ throw new Error(`Failed to fetch blog posts: ${response.status} ${response.statusText}`);
93
+ }
94
+ const data = await response.json();
95
+ return data;
96
+ }
97
+ async function getBlogPost(config, slug) {
98
+ const token = await getAuthToken(config);
99
+ const response = await fetch(`${config.apiUrl}/sdk/blogs/${slug}`, {
100
+ headers: {
101
+ "Authorization": `Bearer ${token}`,
102
+ "x-project-id": config.projectId
103
+ },
104
+ next: { revalidate: 3600 }
105
+ });
106
+ if (!response.ok) {
107
+ throw new Error(`Failed to fetch blog post: ${response.status} ${response.statusText}`);
108
+ }
109
+ return response.json();
110
+ }
111
+ async function getBlogPaths(config) {
112
+ const { blogs } = await getBlogPosts(config, { index: 0, limit: 1e4 });
113
+ return blogs.map((blog) => ({ slug: blog.slug }));
114
+ }
115
+ async function generateMetadata(config, slug) {
116
+ var _a, _b;
117
+ if (slug) {
118
+ const post = await getBlogPost(config, slug);
119
+ if (!post) return notFound();
120
+ return {
121
+ title: post.title,
122
+ description: (_a = post.excerpt) == null ? void 0 : _a.substring(0, 160),
123
+ openGraph: {
124
+ title: post.title,
125
+ description: (_b = post.excerpt) == null ? void 0 : _b.substring(0, 160),
126
+ type: "article",
127
+ publishedTime: getFormattedDate(post.created_at),
128
+ tags: post.tags
129
+ }
130
+ };
131
+ }
132
+ return {
133
+ title: "Blog",
134
+ description: "Read our latest blog posts",
135
+ openGraph: {
136
+ title: "Blog",
137
+ description: "Read our latest blog posts",
138
+ type: "website"
139
+ }
140
+ };
141
+ }
142
+
143
+ // src/app/components/NextBlogPage.tsx
144
+ import React4 from "react";
145
+ import { notFound as notFound2 } from "next/navigation";
146
+
147
+ // src/app/components/Article.tsx
148
+ import React3 from "react";
149
+ import { lowlight as low } from "lowlight/lib/core";
150
+
151
+ // src/app/components/ui/linkBadge.tsx
152
+ import Link from "next/link";
153
+
154
+ // src/app/components/ui/badge.tsx
155
+ import * as React from "react";
156
+ import { cva } from "class-variance-authority";
157
+ var badgeVariants = cva(
158
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
159
+ {
160
+ variants: {
161
+ variant: {
162
+ default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
163
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
164
+ destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
165
+ outline: "text-foreground"
166
+ }
167
+ },
168
+ defaultVariants: {
169
+ variant: "default"
170
+ }
171
+ }
172
+ );
173
+ function Badge(_a) {
174
+ var _b = _a, { className, variant } = _b, props = __objRest(_b, ["className", "variant"]);
175
+ return /* @__PURE__ */ React.createElement("div", __spreadValues({ className: cn(badgeVariants({ variant }), className) }, props));
176
+ }
177
+
178
+ // src/app/components/ui/linkBadge.tsx
179
+ import React2 from "react";
180
+ var LinkBadge = ({ children, href }) => {
181
+ return /* @__PURE__ */ React2.createElement(Link, { href }, /* @__PURE__ */ React2.createElement(Badge, { variant: "outline", className: "text-xs hover:cursor-pointer hover:bg-primary hover:text-primary-foreground" }, children));
182
+ };
183
+
184
+ // src/app/components/Article.tsx
185
+ import javascript from "highlight.js/lib/languages/javascript";
186
+ import typescript from "highlight.js/lib/languages/typescript";
187
+ import css from "highlight.js/lib/languages/css";
188
+ import html from "highlight.js/lib/languages/xml";
189
+ var Article = ({ post, content, style }) => {
190
+ low.registerLanguage("css", css);
191
+ low.registerLanguage("js", javascript);
192
+ low.registerLanguage("ts", typescript);
193
+ low.registerLanguage("html", html);
194
+ const contentHtml = {
195
+ __html: content
196
+ };
197
+ return post && /* @__PURE__ */ React3.createElement("article", { style, className: "easyblog" }, post.thumbnail && /* @__PURE__ */ React3.createElement("img", { src: post.thumbnail, alt: post.title, className: "w-full h-64 object-cover mb-8 rounded-lg" }), /* @__PURE__ */ React3.createElement("header", null, /* @__PURE__ */ React3.createElement("h1", { className: "title" }, post.title), /* @__PURE__ */ React3.createElement("div", { className: "info" }, /* @__PURE__ */ React3.createElement("div", { className: "author" }, "Created by ", /* @__PURE__ */ React3.createElement("span", { className: "font-semibold" }, post.author)), /* @__PURE__ */ React3.createElement("div", { className: "date" }, "Published at ", /* @__PURE__ */ React3.createElement("span", { className: "font-semibold" }, getFormattedDate(post.created_at))))), /* @__PURE__ */ React3.createElement("div", { className: "content", dangerouslySetInnerHTML: contentHtml }), /* @__PURE__ */ React3.createElement("footer", { className: "mt-8 pt-4 border-t border-gray-200" }, post.tags && post.tags.length > 0 && /* @__PURE__ */ React3.createElement("div", { className: "flex flex-wrap gap-2 my-4" }, /* @__PURE__ */ React3.createElement("span", { className: "text-sm font-semibold " }, "Tags"), /* @__PURE__ */ React3.createElement("div", { className: "flex flex-wrap gap-2" }, post.tags.map((tag, index) => /* @__PURE__ */ React3.createElement(LinkBadge, { href: `/blogs?tags=${tag}`, key: index }, tag)))), post.category && /* @__PURE__ */ React3.createElement("div", { className: "flex flex-wrap gap-2 my-4" }, /* @__PURE__ */ React3.createElement("span", { className: "text-sm font-semibold" }, "Category"), /* @__PURE__ */ React3.createElement(LinkBadge, { href: `/blogs?category=${post.category}` }, post.category))));
198
+ };
199
+
200
+ // src/app/components/NextBlogPage.tsx
201
+ async function NextBlogPage(props) {
202
+ try {
203
+ const response = await getBlogPost({
204
+ apiKey: props.config.apiKey,
205
+ projectId: props.config.projectId,
206
+ apiUrl: props.config.apiUrl
207
+ }, props.slug);
208
+ if (!response.blog) return notFound2();
209
+ const post = response.blog;
210
+ const htmlContent = response.html_content;
211
+ return /* @__PURE__ */ React4.createElement(
212
+ Article,
213
+ {
214
+ post,
215
+ content: htmlContent,
216
+ style: props.style
217
+ }
218
+ );
219
+ } catch (error) {
220
+ console.error("Error fetching blog post:", error);
221
+ return notFound2();
222
+ }
223
+ }
224
+
225
+ // src/app/components/NextBlogListPage.tsx
226
+ import React10 from "react";
227
+
228
+ // src/app/components/ArticleGroup.tsx
229
+ import React7 from "react";
230
+
231
+ // src/app/components/ArticleCard.tsx
232
+ import React6 from "react";
233
+
234
+ // src/app/components/ui/card.tsx
235
+ import * as React5 from "react";
236
+ var Card = React5.forwardRef((_a, ref) => {
237
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
238
+ return /* @__PURE__ */ React5.createElement(
239
+ "div",
240
+ __spreadValues({
241
+ ref,
242
+ className: cn(
243
+ "rounded-xl border bg-card text-card-foreground shadow",
244
+ className
245
+ )
246
+ }, props)
247
+ );
248
+ });
249
+ Card.displayName = "Card";
250
+ var CardHeader = React5.forwardRef((_a, ref) => {
251
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
252
+ return /* @__PURE__ */ React5.createElement(
253
+ "div",
254
+ __spreadValues({
255
+ ref,
256
+ className: cn("flex flex-col space-y-1.5 p-6", className)
257
+ }, props)
258
+ );
259
+ });
260
+ CardHeader.displayName = "CardHeader";
261
+ var CardTitle = React5.forwardRef((_a, ref) => {
262
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
263
+ return /* @__PURE__ */ React5.createElement(
264
+ "div",
265
+ __spreadValues({
266
+ ref,
267
+ className: cn("font-semibold leading-none tracking-tight", className)
268
+ }, props)
269
+ );
270
+ });
271
+ CardTitle.displayName = "CardTitle";
272
+ var CardDescription = React5.forwardRef((_a, ref) => {
273
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
274
+ return /* @__PURE__ */ React5.createElement(
275
+ "div",
276
+ __spreadValues({
277
+ ref,
278
+ className: cn("text-sm text-muted-foreground", className)
279
+ }, props)
280
+ );
281
+ });
282
+ CardDescription.displayName = "CardDescription";
283
+ var CardContent = React5.forwardRef((_a, ref) => {
284
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
285
+ return /* @__PURE__ */ React5.createElement("div", __spreadValues({ ref, className: cn("p-6 pt-0", className) }, props));
286
+ });
287
+ CardContent.displayName = "CardContent";
288
+ var CardFooter = React5.forwardRef((_a, ref) => {
289
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
290
+ return /* @__PURE__ */ React5.createElement(
291
+ "div",
292
+ __spreadValues({
293
+ ref,
294
+ className: cn("flex items-center p-6 pt-0", className)
295
+ }, props)
296
+ );
297
+ });
298
+ CardFooter.displayName = "CardFooter";
299
+
300
+ // src/app/components/ArticleCard.tsx
301
+ import Link2 from "next/link.js";
302
+ var ArticleCard = ({
303
+ blog,
304
+ showThumbnail = true,
305
+ showReadingTime = true,
306
+ showExcerpt = true,
307
+ showTags = true,
308
+ showDate = true,
309
+ horizontal = false,
310
+ showAuthor = true,
311
+ showCategory = true,
312
+ isNextPage = false
313
+ }) => {
314
+ const renderTags = (tags) => {
315
+ return tags.map((tag, index) => {
316
+ return isNextPage ? /* @__PURE__ */ React6.createElement(LinkBadge, { key: index, href: `/blogs/?tags=${tag}` }, tag) : /* @__PURE__ */ React6.createElement(Badge, { key: index, variant: "outline", className: "text-xs rounded-full hover:cursor-pointer hover:bg-primary hover:text-primary-foreground" }, tag);
317
+ });
318
+ };
319
+ const renderCategory = () => {
320
+ return isNextPage ? /* @__PURE__ */ React6.createElement(Link2, { href: `/blogs/?category=${blog.category}`, className: "hover:underline" }, " | ", blog.category) : /* @__PURE__ */ React6.createElement("span", null, " | ", blog.category);
321
+ };
322
+ const renderThumbnail = () => {
323
+ return isNextPage ? /* @__PURE__ */ React6.createElement(Link2, { href: `/blogs/${blog.slug}` }, /* @__PURE__ */ React6.createElement("img", { className: `${horizontal ? "w-48 h-full" : "w-full h-48"} object-cover`, src: blog.thumbnail, alt: blog.title })) : /* @__PURE__ */ React6.createElement("img", { className: `${horizontal ? "w-48 h-full" : "w-full h-48"} object-cover`, src: blog.thumbnail, alt: blog.title });
324
+ };
325
+ const renderTitle = () => {
326
+ return isNextPage ? /* @__PURE__ */ React6.createElement(Link2, { href: `/blogs/${blog.slug}`, className: "hover:underline text-primary" }, blog.title) : /* @__PURE__ */ React6.createElement("h3", { className: "font-bold text-xl mb-2 text-wrap" }, blog.title);
327
+ };
328
+ return /* @__PURE__ */ React6.createElement(Card, { className: `max-w-full overflow-hidden ${horizontal ? "flex flex-row" : ""}` }, blog.thumbnail && showThumbnail && /* @__PURE__ */ React6.createElement(CardHeader, { className: "p-0" }, renderThumbnail()), /* @__PURE__ */ React6.createElement(CardContent, { className: "p-6" }, renderTitle(), (showAuthor || showDate || showCategory) && /* @__PURE__ */ React6.createElement("p", { className: "text-muted-foreground" }, showAuthor && /* @__PURE__ */ React6.createElement("span", null, blog.author), " ", showDate && /* @__PURE__ */ React6.createElement("span", null, "| ", getFormattedDate(blog.created_at)), " ", blog.category && showCategory && renderCategory()), blog.reading_time && showReadingTime && /* @__PURE__ */ React6.createElement("p", { className: "text-muted-foreground text-sm mt-2" }, blog.reading_time, " minutes to read"), showExcerpt && /* @__PURE__ */ React6.createElement("p", { className: "text-muted-foreground text-sm mt-2" }, blog.excerpt), horizontal && /* @__PURE__ */ React6.createElement("div", { className: "mt-4 flex flex-wrap gap-2 items-start" }, showTags && blog.tags.map((tag, index) => /* @__PURE__ */ React6.createElement(LinkBadge, { key: index, href: `/blogs?tags=${tag}` }, tag)))), !horizontal && /* @__PURE__ */ React6.createElement(CardFooter, { className: "flex flex-wrap gap-2 items-start" }, showTags && renderTags(blog.tags)));
329
+ };
330
+
331
+ // src/app/components/ArticleGroup.tsx
332
+ var ArticleGroup = ({
333
+ type,
334
+ blogs,
335
+ showThumbnail = true,
336
+ showReadingTime = true,
337
+ showExcerpt = true,
338
+ showTags = true,
339
+ showDate = true,
340
+ showAuthor = true,
341
+ showCategory = true,
342
+ isNextPage = false
343
+ }) => {
344
+ if (!blogs || blogs.length === 0) {
345
+ return /* @__PURE__ */ React7.createElement("div", null, "No blogs found. Please refresh the page.");
346
+ }
347
+ const renderGridView = () => {
348
+ return /* @__PURE__ */ React7.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 lg:grid-cols-3 gap-6" }, blogs.map((blog) => /* @__PURE__ */ React7.createElement(
349
+ ArticleCard,
350
+ {
351
+ key: blog._id,
352
+ blog,
353
+ showThumbnail,
354
+ showReadingTime,
355
+ showExcerpt,
356
+ showTags,
357
+ showDate,
358
+ showAuthor,
359
+ showCategory,
360
+ isNextPage
361
+ }
362
+ )));
363
+ };
364
+ const renderListView = () => {
365
+ return /* @__PURE__ */ React7.createElement("div", { className: "flex flex-col gap-4 min-w-[800px]" }, blogs.map((blog) => /* @__PURE__ */ React7.createElement(
366
+ ArticleCard,
367
+ {
368
+ key: blog._id,
369
+ blog,
370
+ showThumbnail,
371
+ showReadingTime,
372
+ showExcerpt,
373
+ showTags,
374
+ showDate,
375
+ showAuthor,
376
+ showCategory,
377
+ horizontal: true,
378
+ isNextPage
379
+ }
380
+ )));
381
+ };
382
+ return /* @__PURE__ */ React7.createElement("div", null, type === "grid" ? renderGridView() : renderListView());
383
+ };
384
+
385
+ // src/app/components/ui/pagination.tsx
386
+ import * as React9 from "react";
387
+ import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
388
+
389
+ // src/app/components/ui/button.tsx
390
+ import * as React8 from "react";
391
+ import { Slot } from "@radix-ui/react-slot";
392
+ import { cva as cva2 } from "class-variance-authority";
393
+ var buttonVariants = cva2(
394
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
395
+ {
396
+ variants: {
397
+ variant: {
398
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
399
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
400
+ outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
401
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
402
+ ghost: "hover:bg-accent hover:text-accent-foreground",
403
+ link: "text-primary underline-offset-4 hover:underline"
404
+ },
405
+ size: {
406
+ default: "h-9 px-4 py-2",
407
+ sm: "h-8 rounded-md px-3 text-xs",
408
+ lg: "h-10 rounded-md px-8",
409
+ icon: "h-9 w-9"
410
+ }
411
+ },
412
+ defaultVariants: {
413
+ variant: "default",
414
+ size: "default"
415
+ }
416
+ }
417
+ );
418
+ var Button = React8.forwardRef(
419
+ (_a, ref) => {
420
+ var _b = _a, { className, variant, size, asChild = false } = _b, props = __objRest(_b, ["className", "variant", "size", "asChild"]);
421
+ const Comp = asChild ? Slot : "button";
422
+ return /* @__PURE__ */ React8.createElement(
423
+ Comp,
424
+ __spreadValues({
425
+ className: cn(buttonVariants({ variant, size, className })),
426
+ ref
427
+ }, props)
428
+ );
429
+ }
430
+ );
431
+ Button.displayName = "Button";
432
+
433
+ // src/app/components/ui/pagination.tsx
434
+ import Link3 from "next/link";
435
+ var Pagination = (_a) => {
436
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
437
+ return /* @__PURE__ */ React9.createElement(
438
+ "nav",
439
+ __spreadValues({
440
+ role: "navigation",
441
+ "aria-label": "pagination",
442
+ className: cn("mx-auto flex w-full justify-center", className)
443
+ }, props)
444
+ );
445
+ };
446
+ Pagination.displayName = "Pagination";
447
+ var PaginationContent = React9.forwardRef((_a, ref) => {
448
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
449
+ return /* @__PURE__ */ React9.createElement(
450
+ "ul",
451
+ __spreadValues({
452
+ ref,
453
+ className: cn("flex flex-row items-center gap-1", className)
454
+ }, props)
455
+ );
456
+ });
457
+ PaginationContent.displayName = "PaginationContent";
458
+ var PaginationItem = React9.forwardRef((_a, ref) => {
459
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
460
+ return /* @__PURE__ */ React9.createElement("li", __spreadValues({ ref, className: cn("", className) }, props));
461
+ });
462
+ PaginationItem.displayName = "PaginationItem";
463
+ var PaginationLink = (_a) => {
464
+ var _b = _a, {
465
+ className,
466
+ isActive,
467
+ size = "icon"
468
+ } = _b, props = __objRest(_b, [
469
+ "className",
470
+ "isActive",
471
+ "size"
472
+ ]);
473
+ return /* @__PURE__ */ React9.createElement(
474
+ Link3,
475
+ __spreadValues({
476
+ "aria-current": isActive ? "page" : void 0,
477
+ className: cn(
478
+ buttonVariants({
479
+ variant: isActive ? "outline" : "ghost",
480
+ size
481
+ }),
482
+ className
483
+ )
484
+ }, props)
485
+ );
486
+ };
487
+ PaginationLink.displayName = "PaginationLink";
488
+ var PaginationPrevious = (_a) => {
489
+ var _b = _a, {
490
+ className
491
+ } = _b, props = __objRest(_b, [
492
+ "className"
493
+ ]);
494
+ return /* @__PURE__ */ React9.createElement(
495
+ PaginationLink,
496
+ __spreadValues({
497
+ "aria-label": "Go to previous page",
498
+ size: "default",
499
+ className: cn("gap-1 pl-2.5", className)
500
+ }, props),
501
+ /* @__PURE__ */ React9.createElement(ChevronLeft, { className: "h-4 w-4" }),
502
+ /* @__PURE__ */ React9.createElement("span", null, "Previous")
503
+ );
504
+ };
505
+ PaginationPrevious.displayName = "PaginationPrevious";
506
+ var PaginationNext = (_a) => {
507
+ var _b = _a, {
508
+ className
509
+ } = _b, props = __objRest(_b, [
510
+ "className"
511
+ ]);
512
+ return /* @__PURE__ */ React9.createElement(
513
+ PaginationLink,
514
+ __spreadValues({
515
+ "aria-label": "Go to next page",
516
+ size: "default",
517
+ className: cn("gap-1 pr-2.5", className)
518
+ }, props),
519
+ /* @__PURE__ */ React9.createElement("span", null, "Next"),
520
+ /* @__PURE__ */ React9.createElement(ChevronRight, { className: "h-4 w-4" })
521
+ );
522
+ };
523
+ PaginationNext.displayName = "PaginationNext";
524
+ var PaginationEllipsis = (_a) => {
525
+ var _b = _a, {
526
+ className
527
+ } = _b, props = __objRest(_b, [
528
+ "className"
529
+ ]);
530
+ return /* @__PURE__ */ React9.createElement(
531
+ "span",
532
+ __spreadValues({
533
+ "aria-hidden": true,
534
+ className: cn("flex h-9 w-9 items-center justify-center", className)
535
+ }, props),
536
+ /* @__PURE__ */ React9.createElement(MoreHorizontal, { className: "h-4 w-4" }),
537
+ /* @__PURE__ */ React9.createElement("span", { className: "sr-only" }, "More pages")
538
+ );
539
+ };
540
+ PaginationEllipsis.displayName = "PaginationEllipsis";
541
+
542
+ // src/app/components/NextBlogListPage.tsx
543
+ var DEFAULT_BLOG_PER_PAGE = 9;
544
+ async function NextBlogListPage(props) {
545
+ var _a, _b;
546
+ const postsPerPage = ((_a = props.displayOptions) == null ? void 0 : _a.blogPerPage) || DEFAULT_BLOG_PER_PAGE;
547
+ const searchParams = props.searchParams || {};
548
+ const currentPage = Number(searchParams.page) || 1;
549
+ const queryParams = {
550
+ index: (currentPage - 1) * postsPerPage,
551
+ limit: postsPerPage,
552
+ category: searchParams.category || "",
553
+ tags: ((_b = searchParams.tags) == null ? void 0 : _b.split(",")) || []
554
+ };
555
+ const res = await getBlogPosts(
556
+ props.config,
557
+ queryParams
558
+ );
559
+ const { blogs, total } = res;
560
+ const totalPages = Math.ceil(total / queryParams.limit);
561
+ const visiblePages = getVisiblePages(currentPage, totalPages);
562
+ return /* @__PURE__ */ React10.createElement("div", null, /* @__PURE__ */ React10.createElement(
563
+ ArticleGroup,
564
+ __spreadValues({
565
+ blogs,
566
+ type: props.type
567
+ }, props.displayOptions)
568
+ ), totalPages > 1 && /* @__PURE__ */ React10.createElement(Pagination, { className: "mt-8" }, /* @__PURE__ */ React10.createElement(PaginationContent, null, currentPage > 1 && /* @__PURE__ */ React10.createElement(PaginationItem, null, /* @__PURE__ */ React10.createElement(PaginationPrevious, { href: `/blogs?page=${currentPage - 1}` })), visiblePages.map((pageNum, idx) => pageNum === null ? /* @__PURE__ */ React10.createElement(PaginationItem, { key: `ellipsis-${idx}` }, /* @__PURE__ */ React10.createElement(PaginationEllipsis, null)) : /* @__PURE__ */ React10.createElement(PaginationItem, { key: pageNum }, /* @__PURE__ */ React10.createElement(
569
+ PaginationLink,
570
+ {
571
+ href: `/blogs?page=${pageNum}`,
572
+ isActive: pageNum === currentPage
573
+ },
574
+ pageNum
575
+ ))), currentPage < totalPages && /* @__PURE__ */ React10.createElement(PaginationItem, null, /* @__PURE__ */ React10.createElement(PaginationNext, { href: `/blogs?page=${currentPage + 1}` })))));
576
+ }
577
+ export {
578
+ NextBlogListPage,
579
+ NextBlogPage,
580
+ generateMetadata,
581
+ getBlogPaths,
582
+ getBlogPost,
583
+ getBlogPosts
584
+ };
585
+ //# sourceMappingURL=app-index.mjs.map