@haroonwaves/blog-kit-react 1.4.0 → 2.0.0
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 +2 -2
- package/dist/index.cjs +46 -46
- package/dist/index.d.cts +29 -29
- package/dist/index.d.ts +29 -29
- package/dist/index.js +40 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,8 +22,8 @@ For complete documentation, API reference, and examples, please visit the
|
|
|
22
22
|
|
|
23
23
|
## Quick Links
|
|
24
24
|
|
|
25
|
-
- [
|
|
26
|
-
- [
|
|
25
|
+
- [Getting Started](https://blog-kit.haroonwaves.com/docs/getting-started)
|
|
26
|
+
- [Guides](https://blog-kit.haroonwaves.com/docs/guides)
|
|
27
27
|
- [API Reference](https://blog-kit.haroonwaves.com/docs/api-reference)
|
|
28
28
|
|
|
29
29
|
## Related Packages
|
package/dist/index.cjs
CHANGED
|
@@ -30,18 +30,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
ContentCard: () => ContentCard,
|
|
34
|
+
ContentList: () => ContentList,
|
|
35
|
+
ContentPlaceholder: () => ContentPlaceholder,
|
|
36
|
+
ContentRenderer: () => ContentRenderer,
|
|
37
37
|
Filter: () => Filter,
|
|
38
|
-
|
|
38
|
+
filterContent: () => filterContent,
|
|
39
39
|
getAvailableCategories: () => getAvailableCategories,
|
|
40
|
-
|
|
40
|
+
useContent: () => useContent
|
|
41
41
|
});
|
|
42
42
|
module.exports = __toCommonJS(src_exports);
|
|
43
43
|
|
|
44
|
-
// src/components/
|
|
44
|
+
// src/components/ContentRenderer.tsx
|
|
45
45
|
var import_react = require("react");
|
|
46
46
|
var import_react_markdown = __toESM(require("react-markdown"), 1);
|
|
47
47
|
var import_remark_gfm = __toESM(require("remark-gfm"), 1);
|
|
@@ -81,7 +81,7 @@ function Badge({ children, className }) {
|
|
|
81
81
|
);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
// src/components/
|
|
84
|
+
// src/components/ContentRenderer.tsx
|
|
85
85
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
86
86
|
var defaultComponents = {
|
|
87
87
|
h1: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
@@ -209,8 +209,8 @@ var defaultComponents = {
|
|
|
209
209
|
td: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { className: "bk:px-4 bk:py-3 bk:text-gray-700 bk:dark:text-gray-300", ...props }),
|
|
210
210
|
input: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", disabled: true, className: "bk:mr-2 bk:accent-blue-600", ...props })
|
|
211
211
|
};
|
|
212
|
-
function
|
|
213
|
-
|
|
212
|
+
function ContentRenderer({
|
|
213
|
+
body,
|
|
214
214
|
metadata,
|
|
215
215
|
className = "",
|
|
216
216
|
components,
|
|
@@ -238,16 +238,16 @@ function BlogRenderer({
|
|
|
238
238
|
remarkPlugins: [import_remark_gfm.default],
|
|
239
239
|
rehypePlugins: [import_rehype_raw.default, import_rehype_prism_plus.default, import_rehype_slug_custom_id.default],
|
|
240
240
|
components: mergedComponents,
|
|
241
|
-
children:
|
|
241
|
+
children: body
|
|
242
242
|
}
|
|
243
243
|
) })
|
|
244
244
|
] });
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
// src/components/
|
|
247
|
+
// src/components/ContentCard.tsx
|
|
248
248
|
var import_react2 = require("react");
|
|
249
249
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
250
|
-
function
|
|
250
|
+
function ContentCard({
|
|
251
251
|
metadata,
|
|
252
252
|
basePath = "/blog",
|
|
253
253
|
renderLink,
|
|
@@ -290,17 +290,17 @@ function BlogCard({
|
|
|
290
290
|
);
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
// src/components/
|
|
293
|
+
// src/components/ContentList.tsx
|
|
294
294
|
var import_react3 = require("react");
|
|
295
295
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
296
|
-
function
|
|
296
|
+
function ContentList({
|
|
297
297
|
metadata,
|
|
298
298
|
title,
|
|
299
299
|
description,
|
|
300
300
|
basePath = "/blog",
|
|
301
301
|
renderLink,
|
|
302
302
|
className = "",
|
|
303
|
-
emptyMessage = "No
|
|
303
|
+
emptyMessage = "No content found.",
|
|
304
304
|
cardProps,
|
|
305
305
|
classNames = {}
|
|
306
306
|
}) {
|
|
@@ -326,7 +326,7 @@ function BlogList({
|
|
|
326
326
|
children: emptyMessage
|
|
327
327
|
}
|
|
328
328
|
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `bk:flex bk:flex-col bk:gap-6 ${className}`, children: metadata.map((meta) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
329
|
-
|
|
329
|
+
ContentCard,
|
|
330
330
|
{
|
|
331
331
|
metadata: meta,
|
|
332
332
|
basePath,
|
|
@@ -347,9 +347,9 @@ function Filter({
|
|
|
347
347
|
selectedCategory,
|
|
348
348
|
setSelectedCategory,
|
|
349
349
|
categories,
|
|
350
|
-
|
|
350
|
+
contentCount,
|
|
351
351
|
className = "",
|
|
352
|
-
placeholder = "Search
|
|
352
|
+
placeholder = "Search content...",
|
|
353
353
|
classNames = {}
|
|
354
354
|
}) {
|
|
355
355
|
const pillBase = `bk:px-2.5 bk:py-1 bk:cursor-pointer bk:rounded-md bk:text-xs bk:font-medium bk:transition-colors ${classNames.pill ?? ""}`;
|
|
@@ -379,7 +379,7 @@ function Filter({
|
|
|
379
379
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
380
380
|
"input",
|
|
381
381
|
{
|
|
382
|
-
id: "
|
|
382
|
+
id: "content-search",
|
|
383
383
|
type: "search",
|
|
384
384
|
value: searchTerm,
|
|
385
385
|
onChange: (e) => setSearchTerm(e.target.value),
|
|
@@ -414,14 +414,14 @@ function Filter({
|
|
|
414
414
|
}
|
|
415
415
|
)
|
|
416
416
|
] }),
|
|
417
|
-
|
|
417
|
+
contentCount !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
418
418
|
"p",
|
|
419
419
|
{
|
|
420
|
-
className: `bk:text-[10px] bk:uppercase bk:tracking-wider bk:text-gray-400 bk:dark:text-gray-500 ${classNames.
|
|
420
|
+
className: `bk:text-[10px] bk:uppercase bk:tracking-wider bk:text-gray-400 bk:dark:text-gray-500 ${classNames.contentCount ?? ""}`,
|
|
421
421
|
children: [
|
|
422
|
-
|
|
422
|
+
contentCount,
|
|
423
423
|
" ",
|
|
424
|
-
|
|
424
|
+
contentCount === 1 ? "item" : "items",
|
|
425
425
|
" found"
|
|
426
426
|
]
|
|
427
427
|
}
|
|
@@ -429,9 +429,9 @@ function Filter({
|
|
|
429
429
|
] });
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
-
// src/components/
|
|
432
|
+
// src/components/ContentPlaceholder.tsx
|
|
433
433
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
434
|
-
function
|
|
434
|
+
function ContentPlaceholder({ count = 3, className = "" }) {
|
|
435
435
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: `${className}`, children: Array.from({ length: count }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
436
436
|
"div",
|
|
437
437
|
{
|
|
@@ -446,7 +446,7 @@ function BlogPlaceholder({ count = 3, className = "" }) {
|
|
|
446
446
|
)) });
|
|
447
447
|
}
|
|
448
448
|
|
|
449
|
-
// src/hooks/
|
|
449
|
+
// src/hooks/useContent.ts
|
|
450
450
|
var import_react6 = require("react");
|
|
451
451
|
|
|
452
452
|
// src/hooks/useDebounce.ts
|
|
@@ -465,37 +465,37 @@ function useDebounce(value, delay) {
|
|
|
465
465
|
}
|
|
466
466
|
|
|
467
467
|
// src/utils/filter-utils.ts
|
|
468
|
-
function
|
|
469
|
-
let filtered =
|
|
468
|
+
function filterContent(items, searchTerm = "", selectedCategory = null) {
|
|
469
|
+
let filtered = items;
|
|
470
470
|
if (searchTerm) {
|
|
471
471
|
const lowerSearch = searchTerm.toLowerCase();
|
|
472
472
|
filtered = filtered.filter(
|
|
473
|
-
(
|
|
473
|
+
(item) => (item.title?.toLowerCase() || "").includes(lowerSearch) || (item.description?.toLowerCase() || "").includes(lowerSearch)
|
|
474
474
|
);
|
|
475
475
|
}
|
|
476
476
|
if (selectedCategory) {
|
|
477
|
-
filtered = filtered.filter((
|
|
478
|
-
const categories =
|
|
477
|
+
filtered = filtered.filter((item) => {
|
|
478
|
+
const categories = item.categories ?? [];
|
|
479
479
|
return categories.includes(selectedCategory);
|
|
480
480
|
});
|
|
481
481
|
}
|
|
482
482
|
return filtered;
|
|
483
483
|
}
|
|
484
|
-
function getAvailableCategories(
|
|
485
|
-
return Array.from(new Set(
|
|
484
|
+
function getAvailableCategories(items) {
|
|
485
|
+
return Array.from(new Set(items.flatMap((item) => item.categories ?? [])));
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
-
// src/hooks/
|
|
489
|
-
function
|
|
488
|
+
// src/hooks/useContent.ts
|
|
489
|
+
function useContent(allContentMeta) {
|
|
490
490
|
const [searchTerm, setSearchTerm] = (0, import_react6.useState)("");
|
|
491
491
|
const [selectedCategory, setSelectedCategory] = (0, import_react6.useState)(null);
|
|
492
492
|
const debouncedSearchTerm = useDebounce(searchTerm, 500);
|
|
493
493
|
const metadata = (0, import_react6.useMemo)(() => {
|
|
494
|
-
return
|
|
495
|
-
}, [
|
|
494
|
+
return filterContent(allContentMeta, debouncedSearchTerm, selectedCategory);
|
|
495
|
+
}, [allContentMeta, debouncedSearchTerm, selectedCategory]);
|
|
496
496
|
const categories = (0, import_react6.useMemo)(() => {
|
|
497
|
-
return getAvailableCategories(
|
|
498
|
-
}, [
|
|
497
|
+
return getAvailableCategories(allContentMeta);
|
|
498
|
+
}, [allContentMeta]);
|
|
499
499
|
return {
|
|
500
500
|
metadata,
|
|
501
501
|
searchTerm,
|
|
@@ -507,12 +507,12 @@ function useBlogs(blogsMeta) {
|
|
|
507
507
|
}
|
|
508
508
|
// Annotate the CommonJS export names for ESM import in node:
|
|
509
509
|
0 && (module.exports = {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
510
|
+
ContentCard,
|
|
511
|
+
ContentList,
|
|
512
|
+
ContentPlaceholder,
|
|
513
|
+
ContentRenderer,
|
|
514
514
|
Filter,
|
|
515
|
-
|
|
515
|
+
filterContent,
|
|
516
516
|
getAvailableCategories,
|
|
517
|
-
|
|
517
|
+
useContent
|
|
518
518
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import react__default from 'react';
|
|
4
4
|
|
|
5
|
-
interface
|
|
5
|
+
interface ContentMeta {
|
|
6
6
|
title: string;
|
|
7
7
|
description: string;
|
|
8
8
|
date: string;
|
|
@@ -10,28 +10,28 @@ interface BlogMeta {
|
|
|
10
10
|
slug: string;
|
|
11
11
|
readingTime: string;
|
|
12
12
|
}
|
|
13
|
-
interface
|
|
14
|
-
metadata:
|
|
15
|
-
|
|
13
|
+
interface Content {
|
|
14
|
+
metadata: ContentMeta;
|
|
15
|
+
body: string;
|
|
16
16
|
}
|
|
17
|
-
interface
|
|
17
|
+
interface ContentConfig {
|
|
18
18
|
contentDirectory: string;
|
|
19
|
-
|
|
19
|
+
contentSubdirectory?: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
interface
|
|
23
|
-
|
|
24
|
-
metadata:
|
|
22
|
+
interface ContentRendererProps {
|
|
23
|
+
body: string;
|
|
24
|
+
metadata: ContentMeta;
|
|
25
25
|
className?: string;
|
|
26
26
|
components?: Record<string, react__default.ComponentType<any>>;
|
|
27
27
|
showCategory?: boolean;
|
|
28
28
|
showReadingTime?: boolean;
|
|
29
29
|
showDate?: boolean;
|
|
30
30
|
}
|
|
31
|
-
declare function
|
|
31
|
+
declare function ContentRenderer({ body, metadata, className, components, showCategory, showReadingTime, showDate, }: ContentRendererProps): react_jsx_runtime.JSX.Element;
|
|
32
32
|
|
|
33
|
-
interface
|
|
34
|
-
metadata:
|
|
33
|
+
interface ContentCardProps {
|
|
34
|
+
metadata: ContentMeta;
|
|
35
35
|
basePath?: string;
|
|
36
36
|
renderLink?: (href: string, children: react__default.ReactNode) => react__default.ReactNode;
|
|
37
37
|
className?: string;
|
|
@@ -39,27 +39,27 @@ interface BlogCardProps {
|
|
|
39
39
|
showReadingTime?: boolean;
|
|
40
40
|
showDate?: boolean;
|
|
41
41
|
}
|
|
42
|
-
declare function
|
|
42
|
+
declare function ContentCard({ metadata, basePath, renderLink, className, showCategory, showReadingTime, showDate, }: ContentCardProps): react_jsx_runtime.JSX.Element;
|
|
43
43
|
|
|
44
|
-
interface
|
|
45
|
-
metadata:
|
|
44
|
+
interface ContentListProps {
|
|
45
|
+
metadata: ContentMeta[];
|
|
46
46
|
title?: string;
|
|
47
47
|
description?: string;
|
|
48
48
|
basePath?: string;
|
|
49
|
-
renderLink?:
|
|
49
|
+
renderLink?: ContentCardProps['renderLink'];
|
|
50
50
|
className?: string;
|
|
51
51
|
emptyMessage?: string;
|
|
52
|
-
cardProps?: Omit<
|
|
52
|
+
cardProps?: Omit<ContentCardProps, 'metadata' | 'basePath' | 'renderLink'>;
|
|
53
53
|
classNames?: {
|
|
54
54
|
title?: string;
|
|
55
55
|
description?: string;
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
|
-
* A pure, stateless
|
|
59
|
+
* A pure, stateless ContentList component that renders a list of content cards.
|
|
60
60
|
* Accepts metadata and renders it exactly as provided.
|
|
61
61
|
*/
|
|
62
|
-
declare function
|
|
62
|
+
declare function ContentList({ metadata, title, description, basePath, renderLink, className, emptyMessage, cardProps, classNames, }: ContentListProps): react_jsx_runtime.JSX.Element;
|
|
63
63
|
|
|
64
64
|
interface FilterProps {
|
|
65
65
|
searchTerm: string;
|
|
@@ -67,7 +67,7 @@ interface FilterProps {
|
|
|
67
67
|
selectedCategory: string | null;
|
|
68
68
|
setSelectedCategory: (selectedCategory: string | null) => void;
|
|
69
69
|
categories: string[];
|
|
70
|
-
|
|
70
|
+
contentCount?: number;
|
|
71
71
|
className?: string;
|
|
72
72
|
placeholder?: string;
|
|
73
73
|
classNames?: {
|
|
@@ -76,19 +76,19 @@ interface FilterProps {
|
|
|
76
76
|
pill?: string;
|
|
77
77
|
activePill?: string;
|
|
78
78
|
inactivePill?: string;
|
|
79
|
-
|
|
79
|
+
contentCount?: string;
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
|
-
declare function Filter({ searchTerm, setSearchTerm, selectedCategory, setSelectedCategory, categories,
|
|
82
|
+
declare function Filter({ searchTerm, setSearchTerm, selectedCategory, setSelectedCategory, categories, contentCount, className, placeholder, classNames, }: FilterProps): react_jsx_runtime.JSX.Element;
|
|
83
83
|
|
|
84
|
-
interface
|
|
84
|
+
interface ContentPlaceholderProps {
|
|
85
85
|
count?: number;
|
|
86
86
|
className?: string;
|
|
87
87
|
}
|
|
88
|
-
declare function
|
|
88
|
+
declare function ContentPlaceholder({ count, className }: ContentPlaceholderProps): react_jsx_runtime.JSX.Element;
|
|
89
89
|
|
|
90
|
-
declare function
|
|
91
|
-
metadata:
|
|
90
|
+
declare function useContent(allContentMeta: ContentMeta[]): {
|
|
91
|
+
metadata: ContentMeta[];
|
|
92
92
|
searchTerm: string;
|
|
93
93
|
setSearchTerm: react.Dispatch<react.SetStateAction<string>>;
|
|
94
94
|
selectedCategory: string | null;
|
|
@@ -96,7 +96,7 @@ declare function useBlogs(blogsMeta: BlogMeta[]): {
|
|
|
96
96
|
categories: string[];
|
|
97
97
|
};
|
|
98
98
|
|
|
99
|
-
declare function
|
|
100
|
-
declare function getAvailableCategories(
|
|
99
|
+
declare function filterContent(items: ContentMeta[], searchTerm?: string, selectedCategory?: string | null): ContentMeta[];
|
|
100
|
+
declare function getAvailableCategories(items: ContentMeta[]): string[];
|
|
101
101
|
|
|
102
|
-
export { type
|
|
102
|
+
export { type Content, ContentCard, type ContentCardProps, type ContentConfig, ContentList, type ContentListProps, type ContentMeta, ContentPlaceholder, type ContentPlaceholderProps, ContentRenderer, type ContentRendererProps, Filter, type FilterProps, filterContent, getAvailableCategories, useContent };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import react__default from 'react';
|
|
4
4
|
|
|
5
|
-
interface
|
|
5
|
+
interface ContentMeta {
|
|
6
6
|
title: string;
|
|
7
7
|
description: string;
|
|
8
8
|
date: string;
|
|
@@ -10,28 +10,28 @@ interface BlogMeta {
|
|
|
10
10
|
slug: string;
|
|
11
11
|
readingTime: string;
|
|
12
12
|
}
|
|
13
|
-
interface
|
|
14
|
-
metadata:
|
|
15
|
-
|
|
13
|
+
interface Content {
|
|
14
|
+
metadata: ContentMeta;
|
|
15
|
+
body: string;
|
|
16
16
|
}
|
|
17
|
-
interface
|
|
17
|
+
interface ContentConfig {
|
|
18
18
|
contentDirectory: string;
|
|
19
|
-
|
|
19
|
+
contentSubdirectory?: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
interface
|
|
23
|
-
|
|
24
|
-
metadata:
|
|
22
|
+
interface ContentRendererProps {
|
|
23
|
+
body: string;
|
|
24
|
+
metadata: ContentMeta;
|
|
25
25
|
className?: string;
|
|
26
26
|
components?: Record<string, react__default.ComponentType<any>>;
|
|
27
27
|
showCategory?: boolean;
|
|
28
28
|
showReadingTime?: boolean;
|
|
29
29
|
showDate?: boolean;
|
|
30
30
|
}
|
|
31
|
-
declare function
|
|
31
|
+
declare function ContentRenderer({ body, metadata, className, components, showCategory, showReadingTime, showDate, }: ContentRendererProps): react_jsx_runtime.JSX.Element;
|
|
32
32
|
|
|
33
|
-
interface
|
|
34
|
-
metadata:
|
|
33
|
+
interface ContentCardProps {
|
|
34
|
+
metadata: ContentMeta;
|
|
35
35
|
basePath?: string;
|
|
36
36
|
renderLink?: (href: string, children: react__default.ReactNode) => react__default.ReactNode;
|
|
37
37
|
className?: string;
|
|
@@ -39,27 +39,27 @@ interface BlogCardProps {
|
|
|
39
39
|
showReadingTime?: boolean;
|
|
40
40
|
showDate?: boolean;
|
|
41
41
|
}
|
|
42
|
-
declare function
|
|
42
|
+
declare function ContentCard({ metadata, basePath, renderLink, className, showCategory, showReadingTime, showDate, }: ContentCardProps): react_jsx_runtime.JSX.Element;
|
|
43
43
|
|
|
44
|
-
interface
|
|
45
|
-
metadata:
|
|
44
|
+
interface ContentListProps {
|
|
45
|
+
metadata: ContentMeta[];
|
|
46
46
|
title?: string;
|
|
47
47
|
description?: string;
|
|
48
48
|
basePath?: string;
|
|
49
|
-
renderLink?:
|
|
49
|
+
renderLink?: ContentCardProps['renderLink'];
|
|
50
50
|
className?: string;
|
|
51
51
|
emptyMessage?: string;
|
|
52
|
-
cardProps?: Omit<
|
|
52
|
+
cardProps?: Omit<ContentCardProps, 'metadata' | 'basePath' | 'renderLink'>;
|
|
53
53
|
classNames?: {
|
|
54
54
|
title?: string;
|
|
55
55
|
description?: string;
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
|
-
* A pure, stateless
|
|
59
|
+
* A pure, stateless ContentList component that renders a list of content cards.
|
|
60
60
|
* Accepts metadata and renders it exactly as provided.
|
|
61
61
|
*/
|
|
62
|
-
declare function
|
|
62
|
+
declare function ContentList({ metadata, title, description, basePath, renderLink, className, emptyMessage, cardProps, classNames, }: ContentListProps): react_jsx_runtime.JSX.Element;
|
|
63
63
|
|
|
64
64
|
interface FilterProps {
|
|
65
65
|
searchTerm: string;
|
|
@@ -67,7 +67,7 @@ interface FilterProps {
|
|
|
67
67
|
selectedCategory: string | null;
|
|
68
68
|
setSelectedCategory: (selectedCategory: string | null) => void;
|
|
69
69
|
categories: string[];
|
|
70
|
-
|
|
70
|
+
contentCount?: number;
|
|
71
71
|
className?: string;
|
|
72
72
|
placeholder?: string;
|
|
73
73
|
classNames?: {
|
|
@@ -76,19 +76,19 @@ interface FilterProps {
|
|
|
76
76
|
pill?: string;
|
|
77
77
|
activePill?: string;
|
|
78
78
|
inactivePill?: string;
|
|
79
|
-
|
|
79
|
+
contentCount?: string;
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
|
-
declare function Filter({ searchTerm, setSearchTerm, selectedCategory, setSelectedCategory, categories,
|
|
82
|
+
declare function Filter({ searchTerm, setSearchTerm, selectedCategory, setSelectedCategory, categories, contentCount, className, placeholder, classNames, }: FilterProps): react_jsx_runtime.JSX.Element;
|
|
83
83
|
|
|
84
|
-
interface
|
|
84
|
+
interface ContentPlaceholderProps {
|
|
85
85
|
count?: number;
|
|
86
86
|
className?: string;
|
|
87
87
|
}
|
|
88
|
-
declare function
|
|
88
|
+
declare function ContentPlaceholder({ count, className }: ContentPlaceholderProps): react_jsx_runtime.JSX.Element;
|
|
89
89
|
|
|
90
|
-
declare function
|
|
91
|
-
metadata:
|
|
90
|
+
declare function useContent(allContentMeta: ContentMeta[]): {
|
|
91
|
+
metadata: ContentMeta[];
|
|
92
92
|
searchTerm: string;
|
|
93
93
|
setSearchTerm: react.Dispatch<react.SetStateAction<string>>;
|
|
94
94
|
selectedCategory: string | null;
|
|
@@ -96,7 +96,7 @@ declare function useBlogs(blogsMeta: BlogMeta[]): {
|
|
|
96
96
|
categories: string[];
|
|
97
97
|
};
|
|
98
98
|
|
|
99
|
-
declare function
|
|
100
|
-
declare function getAvailableCategories(
|
|
99
|
+
declare function filterContent(items: ContentMeta[], searchTerm?: string, selectedCategory?: string | null): ContentMeta[];
|
|
100
|
+
declare function getAvailableCategories(items: ContentMeta[]): string[];
|
|
101
101
|
|
|
102
|
-
export { type
|
|
102
|
+
export { type Content, ContentCard, type ContentCardProps, type ContentConfig, ContentList, type ContentListProps, type ContentMeta, ContentPlaceholder, type ContentPlaceholderProps, ContentRenderer, type ContentRendererProps, Filter, type FilterProps, filterContent, getAvailableCategories, useContent };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/components/
|
|
1
|
+
// src/components/ContentRenderer.tsx
|
|
2
2
|
import "react";
|
|
3
3
|
import ReactMarkdown from "react-markdown";
|
|
4
4
|
import remarkGfm from "remark-gfm";
|
|
@@ -38,7 +38,7 @@ function Badge({ children, className }) {
|
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
// src/components/
|
|
41
|
+
// src/components/ContentRenderer.tsx
|
|
42
42
|
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
43
43
|
var defaultComponents = {
|
|
44
44
|
h1: (props) => /* @__PURE__ */ jsx2(
|
|
@@ -166,8 +166,8 @@ var defaultComponents = {
|
|
|
166
166
|
td: (props) => /* @__PURE__ */ jsx2("td", { className: "bk:px-4 bk:py-3 bk:text-gray-700 bk:dark:text-gray-300", ...props }),
|
|
167
167
|
input: (props) => /* @__PURE__ */ jsx2("input", { type: "checkbox", disabled: true, className: "bk:mr-2 bk:accent-blue-600", ...props })
|
|
168
168
|
};
|
|
169
|
-
function
|
|
170
|
-
|
|
169
|
+
function ContentRenderer({
|
|
170
|
+
body,
|
|
171
171
|
metadata,
|
|
172
172
|
className = "",
|
|
173
173
|
components,
|
|
@@ -195,16 +195,16 @@ function BlogRenderer({
|
|
|
195
195
|
remarkPlugins: [remarkGfm],
|
|
196
196
|
rehypePlugins: [rehypeRaw, rehypePrismPlus, rehypeSlugCustomId],
|
|
197
197
|
components: mergedComponents,
|
|
198
|
-
children:
|
|
198
|
+
children: body
|
|
199
199
|
}
|
|
200
200
|
) })
|
|
201
201
|
] });
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
// src/components/
|
|
204
|
+
// src/components/ContentCard.tsx
|
|
205
205
|
import "react";
|
|
206
206
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
207
|
-
function
|
|
207
|
+
function ContentCard({
|
|
208
208
|
metadata,
|
|
209
209
|
basePath = "/blog",
|
|
210
210
|
renderLink,
|
|
@@ -247,17 +247,17 @@ function BlogCard({
|
|
|
247
247
|
);
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
// src/components/
|
|
250
|
+
// src/components/ContentList.tsx
|
|
251
251
|
import "react";
|
|
252
252
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
253
|
-
function
|
|
253
|
+
function ContentList({
|
|
254
254
|
metadata,
|
|
255
255
|
title,
|
|
256
256
|
description,
|
|
257
257
|
basePath = "/blog",
|
|
258
258
|
renderLink,
|
|
259
259
|
className = "",
|
|
260
|
-
emptyMessage = "No
|
|
260
|
+
emptyMessage = "No content found.",
|
|
261
261
|
cardProps,
|
|
262
262
|
classNames = {}
|
|
263
263
|
}) {
|
|
@@ -283,7 +283,7 @@ function BlogList({
|
|
|
283
283
|
children: emptyMessage
|
|
284
284
|
}
|
|
285
285
|
) : /* @__PURE__ */ jsx4("div", { className: `bk:flex bk:flex-col bk:gap-6 ${className}`, children: metadata.map((meta) => /* @__PURE__ */ jsx4(
|
|
286
|
-
|
|
286
|
+
ContentCard,
|
|
287
287
|
{
|
|
288
288
|
metadata: meta,
|
|
289
289
|
basePath,
|
|
@@ -304,9 +304,9 @@ function Filter({
|
|
|
304
304
|
selectedCategory,
|
|
305
305
|
setSelectedCategory,
|
|
306
306
|
categories,
|
|
307
|
-
|
|
307
|
+
contentCount,
|
|
308
308
|
className = "",
|
|
309
|
-
placeholder = "Search
|
|
309
|
+
placeholder = "Search content...",
|
|
310
310
|
classNames = {}
|
|
311
311
|
}) {
|
|
312
312
|
const pillBase = `bk:px-2.5 bk:py-1 bk:cursor-pointer bk:rounded-md bk:text-xs bk:font-medium bk:transition-colors ${classNames.pill ?? ""}`;
|
|
@@ -336,7 +336,7 @@ function Filter({
|
|
|
336
336
|
/* @__PURE__ */ jsx5(
|
|
337
337
|
"input",
|
|
338
338
|
{
|
|
339
|
-
id: "
|
|
339
|
+
id: "content-search",
|
|
340
340
|
type: "search",
|
|
341
341
|
value: searchTerm,
|
|
342
342
|
onChange: (e) => setSearchTerm(e.target.value),
|
|
@@ -371,14 +371,14 @@ function Filter({
|
|
|
371
371
|
}
|
|
372
372
|
)
|
|
373
373
|
] }),
|
|
374
|
-
|
|
374
|
+
contentCount !== void 0 && /* @__PURE__ */ jsxs4(
|
|
375
375
|
"p",
|
|
376
376
|
{
|
|
377
|
-
className: `bk:text-[10px] bk:uppercase bk:tracking-wider bk:text-gray-400 bk:dark:text-gray-500 ${classNames.
|
|
377
|
+
className: `bk:text-[10px] bk:uppercase bk:tracking-wider bk:text-gray-400 bk:dark:text-gray-500 ${classNames.contentCount ?? ""}`,
|
|
378
378
|
children: [
|
|
379
|
-
|
|
379
|
+
contentCount,
|
|
380
380
|
" ",
|
|
381
|
-
|
|
381
|
+
contentCount === 1 ? "item" : "items",
|
|
382
382
|
" found"
|
|
383
383
|
]
|
|
384
384
|
}
|
|
@@ -386,9 +386,9 @@ function Filter({
|
|
|
386
386
|
] });
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
// src/components/
|
|
389
|
+
// src/components/ContentPlaceholder.tsx
|
|
390
390
|
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
391
|
-
function
|
|
391
|
+
function ContentPlaceholder({ count = 3, className = "" }) {
|
|
392
392
|
return /* @__PURE__ */ jsx6("div", { className: `${className}`, children: Array.from({ length: count }).map((_, i) => /* @__PURE__ */ jsxs5(
|
|
393
393
|
"div",
|
|
394
394
|
{
|
|
@@ -403,7 +403,7 @@ function BlogPlaceholder({ count = 3, className = "" }) {
|
|
|
403
403
|
)) });
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
-
// src/hooks/
|
|
406
|
+
// src/hooks/useContent.ts
|
|
407
407
|
import { useState as useState2, useMemo } from "react";
|
|
408
408
|
|
|
409
409
|
// src/hooks/useDebounce.ts
|
|
@@ -422,37 +422,37 @@ function useDebounce(value, delay) {
|
|
|
422
422
|
}
|
|
423
423
|
|
|
424
424
|
// src/utils/filter-utils.ts
|
|
425
|
-
function
|
|
426
|
-
let filtered =
|
|
425
|
+
function filterContent(items, searchTerm = "", selectedCategory = null) {
|
|
426
|
+
let filtered = items;
|
|
427
427
|
if (searchTerm) {
|
|
428
428
|
const lowerSearch = searchTerm.toLowerCase();
|
|
429
429
|
filtered = filtered.filter(
|
|
430
|
-
(
|
|
430
|
+
(item) => (item.title?.toLowerCase() || "").includes(lowerSearch) || (item.description?.toLowerCase() || "").includes(lowerSearch)
|
|
431
431
|
);
|
|
432
432
|
}
|
|
433
433
|
if (selectedCategory) {
|
|
434
|
-
filtered = filtered.filter((
|
|
435
|
-
const categories =
|
|
434
|
+
filtered = filtered.filter((item) => {
|
|
435
|
+
const categories = item.categories ?? [];
|
|
436
436
|
return categories.includes(selectedCategory);
|
|
437
437
|
});
|
|
438
438
|
}
|
|
439
439
|
return filtered;
|
|
440
440
|
}
|
|
441
|
-
function getAvailableCategories(
|
|
442
|
-
return Array.from(new Set(
|
|
441
|
+
function getAvailableCategories(items) {
|
|
442
|
+
return Array.from(new Set(items.flatMap((item) => item.categories ?? [])));
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
-
// src/hooks/
|
|
446
|
-
function
|
|
445
|
+
// src/hooks/useContent.ts
|
|
446
|
+
function useContent(allContentMeta) {
|
|
447
447
|
const [searchTerm, setSearchTerm] = useState2("");
|
|
448
448
|
const [selectedCategory, setSelectedCategory] = useState2(null);
|
|
449
449
|
const debouncedSearchTerm = useDebounce(searchTerm, 500);
|
|
450
450
|
const metadata = useMemo(() => {
|
|
451
|
-
return
|
|
452
|
-
}, [
|
|
451
|
+
return filterContent(allContentMeta, debouncedSearchTerm, selectedCategory);
|
|
452
|
+
}, [allContentMeta, debouncedSearchTerm, selectedCategory]);
|
|
453
453
|
const categories = useMemo(() => {
|
|
454
|
-
return getAvailableCategories(
|
|
455
|
-
}, [
|
|
454
|
+
return getAvailableCategories(allContentMeta);
|
|
455
|
+
}, [allContentMeta]);
|
|
456
456
|
return {
|
|
457
457
|
metadata,
|
|
458
458
|
searchTerm,
|
|
@@ -463,12 +463,12 @@ function useBlogs(blogsMeta) {
|
|
|
463
463
|
};
|
|
464
464
|
}
|
|
465
465
|
export {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
466
|
+
ContentCard,
|
|
467
|
+
ContentList,
|
|
468
|
+
ContentPlaceholder,
|
|
469
|
+
ContentRenderer,
|
|
470
470
|
Filter,
|
|
471
|
-
|
|
471
|
+
filterContent,
|
|
472
472
|
getAvailableCategories,
|
|
473
|
-
|
|
473
|
+
useContent
|
|
474
474
|
};
|