@contentgrowth/content-widget 1.3.4 → 1.3.5
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 +76 -2
- package/dist/astro/ContentCard.astro +170 -0
- package/dist/astro/ContentList.astro +30 -47
- package/dist/astro/FeaturedCard.astro +61 -21
- package/dist/react/ContentCard.d.ts +59 -0
- package/dist/react/ContentCard.d.ts.map +1 -0
- package/dist/react/ContentCard.js +84 -0
- package/dist/react/ContentList.d.ts.map +1 -1
- package/dist/react/ContentList.js +12 -21
- package/dist/react/FeaturedCard.d.ts +14 -2
- package/dist/react/FeaturedCard.d.ts.map +1 -1
- package/dist/react/FeaturedCard.js +36 -13
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/styles.css +17 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/vue/ContentCard.vue +148 -0
- package/dist/vue/ContentList.vue +35 -33
- package/dist/vue/FeaturedCard.vue +88 -42
- package/dist/widget/widget.css +1 -1
- package/dist/widget/widget.dev.css +17 -0
- package/dist/widget/widget.dev.js +3 -3
- package/dist/widget/widget.js +1 -1
- package/package.json +1 -1
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import React, { useEffect, useState } from 'react';
|
|
6
6
|
import { ContentGrowthClient } from '../core/client.js';
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import { FeaturedCard } from './FeaturedCard.js';
|
|
8
|
+
import { ContentCard } from './ContentCard.js';
|
|
9
|
+
export const ContentList = ({ apiKey, baseUrl, layout = 'cards', displayMode = 'comfortable', displayAs = 'default', theme = 'light', pageSize = 12, tags = [], category, showPagination = true, linkPattern = '/articles/{uuid}', showTags = false, showAiSummary = true, summaryMaxLength, linkTarget, className = '' }) => {
|
|
9
10
|
const [articles, setArticles] = useState([]);
|
|
10
11
|
const [currentPage, setCurrentPage] = useState(1);
|
|
11
12
|
const [totalPages, setTotalPages] = useState(1);
|
|
12
13
|
const [loading, setLoading] = useState(true);
|
|
14
|
+
const isFeaturedCardsMode = displayAs === 'featured-cards';
|
|
13
15
|
useEffect(() => {
|
|
14
16
|
const fetchArticles = async () => {
|
|
15
17
|
setLoading(true);
|
|
@@ -73,27 +75,16 @@ export const ContentList = ({ apiKey, baseUrl, layout = 'cards', displayMode = '
|
|
|
73
75
|
React.createElement("div", { className: "cg-empty-state" },
|
|
74
76
|
React.createElement("p", null, "Loading..."))));
|
|
75
77
|
}
|
|
76
|
-
return (React.createElement("div", { className: `cg-content-list cg-layout-${layout} cg-display-${displayMode} cg-theme-${theme} ${className}`, "data-cg-widget": "list" }, articles.length === 0 ? (React.createElement("div", { className: "cg-empty-state" },
|
|
78
|
+
return (React.createElement("div", { className: `cg-content-list cg-layout-${layout} cg-display-${displayMode} cg-theme-${theme} ${isFeaturedCardsMode ? 'cg-featured-cards-list' : ''} ${className}`, "data-cg-widget": "list" }, articles.length === 0 ? (React.createElement("div", { className: "cg-empty-state" },
|
|
77
79
|
React.createElement("p", null, "No articles found."))) : (React.createElement(React.Fragment, null,
|
|
78
|
-
React.createElement("div", { className: `cg-articles-grid ${layout === 'cards' ? 'cg-grid' : 'cg-list'}` }, articles.map((article) => {
|
|
79
|
-
const articleUrl = buildArticleUrl(article);
|
|
80
|
+
React.createElement("div", { className: `cg-articles-grid ${isFeaturedCardsMode ? 'cg-featured-cards-grid' : (layout === 'cards' ? 'cg-grid' : 'cg-list')}` }, articles.map((article) => {
|
|
80
81
|
const articleTarget = buildLinkTarget(article);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
React.createElement("span", { className: "cg-category-badge" }, article.category))),
|
|
88
|
-
React.createElement("h2", { className: "cg-card-title" }, article.title),
|
|
89
|
-
showAiSummary && article.summary && (React.createElement("p", { className: "cg-card-summary" }, truncateSummary(article.summary, summaryMaxLength))),
|
|
90
|
-
React.createElement("div", { className: "cg-card-meta" },
|
|
91
|
-
React.createElement("span", { className: "cg-meta-author" }, article.authorName),
|
|
92
|
-
React.createElement("span", { className: "cg-meta-separator" }, "\u2022"),
|
|
93
|
-
React.createElement("time", { className: "cg-meta-date", dateTime: new Date(article.publishedAt * 1000).toISOString() }, publishedDate),
|
|
94
|
-
React.createElement("span", { className: "cg-meta-separator" }, "\u2022"),
|
|
95
|
-
React.createElement("span", { className: "cg-meta-reading-time" }, readingTime)),
|
|
96
|
-
showTags && article.tags && article.tags.length > 0 && (React.createElement("div", { className: "cg-card-tags" }, article.tags.map((tag) => (React.createElement("span", { key: tag, className: "cg-tag" }, tag)))))))));
|
|
82
|
+
// Featured Cards Mode - Use FeaturedCard component
|
|
83
|
+
if (isFeaturedCardsMode) {
|
|
84
|
+
return (React.createElement(FeaturedCard, { key: article.uuid, article: article, linkPattern: linkPattern, linkTarget: articleTarget, showCategory: true }));
|
|
85
|
+
}
|
|
86
|
+
// Default Card Mode - Use ContentCard component
|
|
87
|
+
return (React.createElement(ContentCard, { key: article.uuid, article: article, linkPattern: linkPattern, linkTarget: articleTarget, showSummary: showAiSummary, summaryMaxLength: summaryMaxLength, showTags: showTags, showCategory: true }));
|
|
97
88
|
})),
|
|
98
89
|
showPagination && totalPages > 1 && (React.createElement("div", { className: "cg-pagination" },
|
|
99
90
|
React.createElement("button", { className: "cg-pagination-btn", onClick: () => setCurrentPage(p => Math.max(1, p - 1)), disabled: currentPage === 1 }, "Previous"),
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { FeaturedContentProps } from '../types';
|
|
3
|
-
interface FeaturedCardProps extends Omit<FeaturedContentProps, 'showBackButton' | 'backUrl' | 'showAiSummary' | 'showTags'
|
|
2
|
+
import type { FeaturedContentProps, ArticleWithContent, Article } from '../types';
|
|
3
|
+
interface FeaturedCardProps extends Partial<Omit<FeaturedContentProps, 'showBackButton' | 'backUrl' | 'showAiSummary' | 'showTags'>> {
|
|
4
|
+
/**
|
|
5
|
+
* Pre-loaded article data (bypasses API fetch - used by ContentList)
|
|
6
|
+
*/
|
|
7
|
+
article?: Article | ArticleWithContent;
|
|
8
|
+
/**
|
|
9
|
+
* Load specific article by slug (bypasses category/tags search)
|
|
10
|
+
*/
|
|
11
|
+
slug?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Load specific article by UUID (bypasses category/tags search)
|
|
14
|
+
*/
|
|
15
|
+
uuid?: string;
|
|
4
16
|
/**
|
|
5
17
|
* URL pattern for the article link
|
|
6
18
|
* Supports placeholders: {uuid}, {slug}, {category}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeaturedCard.d.ts","sourceRoot":"","sources":["../../src/react/FeaturedCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,KAAK,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"FeaturedCard.d.ts","sourceRoot":"","sources":["../../src/react/FeaturedCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAElF,UAAU,iBAAkB,SAAQ,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,GAAG,SAAS,GAAG,eAAe,GAAG,UAAU,CAAC,CAAC;IAChI;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAAC;IAEvC;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IAEnC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAEzC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA0KpD,CAAC"}
|
|
@@ -1,31 +1,54 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import { marked } from 'marked';
|
|
3
3
|
import { ContentGrowthClient } from '../core/client';
|
|
4
|
-
export const FeaturedCard = ({ apiKey, baseUrl, tags = [], category, excludeTags = [], showCategory = true, showReadingTime = false, showAuthor = false, ctaText: propCtaText, linkPattern = '/articles/{slug}', linkTarget, layout: propLayout, borderStyle = 'none', borderColor = '#e5e7eb', cardBackground = 'none', itemsBackground = '#f3f4f6', className = '' }) => {
|
|
5
|
-
const [article, setArticle] = useState(null);
|
|
6
|
-
const [loading, setLoading] = useState(
|
|
4
|
+
export const FeaturedCard = ({ apiKey, baseUrl, article: providedArticle, slug, uuid, tags = [], category, excludeTags = [], showCategory = true, showReadingTime = false, showAuthor = false, ctaText: propCtaText, linkPattern = '/articles/{slug}', linkTarget, layout: propLayout, borderStyle = 'none', borderColor = '#e5e7eb', cardBackground = 'none', itemsBackground = '#f3f4f6', className = '' }) => {
|
|
5
|
+
const [article, setArticle] = useState(providedArticle || null);
|
|
6
|
+
const [loading, setLoading] = useState(!providedArticle);
|
|
7
7
|
const [error, setError] = useState(null);
|
|
8
8
|
useEffect(() => {
|
|
9
|
+
// If article is provided, use it directly (no fetch needed)
|
|
10
|
+
if (providedArticle) {
|
|
11
|
+
setArticle(providedArticle);
|
|
12
|
+
setLoading(false);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
// Need API key to fetch
|
|
16
|
+
if (!apiKey) {
|
|
17
|
+
setLoading(false);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
9
20
|
const fetchArticle = async () => {
|
|
10
21
|
setLoading(true);
|
|
11
22
|
try {
|
|
12
23
|
const client = new ContentGrowthClient({ apiKey, baseUrl });
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
excludeTags
|
|
17
|
-
}
|
|
24
|
+
let fetchedArticle;
|
|
25
|
+
if (uuid) {
|
|
26
|
+
// Mode 2: Load by UUID
|
|
27
|
+
fetchedArticle = await client.getArticle(uuid, { excludeTags });
|
|
28
|
+
}
|
|
29
|
+
else if (slug) {
|
|
30
|
+
// Mode 3: Load by slug
|
|
31
|
+
fetchedArticle = await client.getArticleBySlug(slug, { excludeTags });
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Mode 4: Find featured article by category/tags (original behavior)
|
|
35
|
+
fetchedArticle = await client.getFeaturedArticle({
|
|
36
|
+
tags,
|
|
37
|
+
category,
|
|
38
|
+
excludeTags
|
|
39
|
+
});
|
|
40
|
+
}
|
|
18
41
|
setArticle(fetchedArticle);
|
|
19
42
|
}
|
|
20
43
|
catch (err) {
|
|
21
|
-
setError(err instanceof Error ? err.message : 'Failed to load
|
|
44
|
+
setError(err instanceof Error ? err.message : 'Failed to load article');
|
|
22
45
|
}
|
|
23
46
|
finally {
|
|
24
47
|
setLoading(false);
|
|
25
48
|
}
|
|
26
49
|
};
|
|
27
50
|
fetchArticle();
|
|
28
|
-
}, [apiKey, baseUrl, category, JSON.stringify(tags), JSON.stringify(excludeTags)]);
|
|
51
|
+
}, [apiKey, baseUrl, providedArticle, uuid, slug, category, JSON.stringify(tags), JSON.stringify(excludeTags)]);
|
|
29
52
|
// Generate article URL
|
|
30
53
|
const getArticleUrl = (article) => {
|
|
31
54
|
return linkPattern
|
|
@@ -48,10 +71,10 @@ export const FeaturedCard = ({ apiKey, baseUrl, tags = [], category, excludeTags
|
|
|
48
71
|
return (React.createElement("div", { className: `cg-widget cg-error ${className}` }, error || 'No featured content found'));
|
|
49
72
|
}
|
|
50
73
|
const summaryHtml = getSummaryHtml(article);
|
|
51
|
-
|
|
52
|
-
const layout = propLayout || article.featuredSummaryLayout || 'standard';
|
|
74
|
+
const layout = propLayout || article.featuredSummaryLayout || 'vertical';
|
|
53
75
|
const readingTime = Math.ceil(article.wordCount / 200);
|
|
54
76
|
const borderClass = borderStyle !== 'none' ? `cg-border-${borderStyle}` : '';
|
|
77
|
+
const layoutClass = layout !== 'vertical' ? `cg-layout-${layout}` : '';
|
|
55
78
|
// Use ctaText from prop, or from article data, or fallback to default
|
|
56
79
|
const ctaText = propCtaText || article.featuredCtaText || 'Read full story';
|
|
57
80
|
const customStyles = {};
|
|
@@ -61,7 +84,7 @@ export const FeaturedCard = ({ apiKey, baseUrl, tags = [], category, excludeTags
|
|
|
61
84
|
customStyles['--cg-card-bg'] = cardBackground;
|
|
62
85
|
if (itemsBackground !== '#f3f4f6')
|
|
63
86
|
customStyles['--cg-items-bg'] = itemsBackground;
|
|
64
|
-
return (React.createElement("a", { href: getArticleUrl(article), className: `cg-widget cg-featured-card ${className} ${
|
|
87
|
+
return (React.createElement("a", { href: getArticleUrl(article), className: `cg-widget cg-featured-card ${className} ${layoutClass} ${borderClass}`, style: Object.keys(customStyles).length > 0 ? customStyles : undefined, "data-cg-widget": "featured-card", target: linkTarget, rel: linkTarget === '_blank' ? 'noopener noreferrer' : undefined },
|
|
65
88
|
React.createElement("article", { className: "cg-featured-card-inner" },
|
|
66
89
|
showCategory && article.category && (React.createElement("div", { className: "cg-featured-card-category" },
|
|
67
90
|
React.createElement("span", { className: "cg-category-badge" }, article.category))),
|
package/dist/react/index.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ export { ContentList } from './ContentList.js';
|
|
|
5
5
|
export { ContentViewer } from './ContentViewer.js';
|
|
6
6
|
export { FeaturedContent } from './FeaturedContent.js';
|
|
7
7
|
export { FeaturedCard } from './FeaturedCard.js';
|
|
8
|
+
export { ContentCard } from './ContentCard.js';
|
|
8
9
|
export * from './hooks.js';
|
|
9
10
|
export type { ReactContentListProps } from './ContentList.js';
|
|
10
11
|
export type { ReactContentViewerProps } from './ContentViewer.js';
|
|
12
|
+
export type { ContentCardProps } from './ContentCard.js';
|
|
11
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,cAAc,YAAY,CAAC;AAE3B,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9D,YAAY,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,cAAc,YAAY,CAAC;AAE3B,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9D,YAAY,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/react/index.js
CHANGED
|
@@ -5,4 +5,5 @@ export { ContentList } from './ContentList.js';
|
|
|
5
5
|
export { ContentViewer } from './ContentViewer.js';
|
|
6
6
|
export { FeaturedContent } from './FeaturedContent.js';
|
|
7
7
|
export { FeaturedCard } from './FeaturedCard.js';
|
|
8
|
+
export { ContentCard } from './ContentCard.js';
|
|
8
9
|
export * from './hooks.js';
|
package/dist/styles.css
CHANGED
|
@@ -720,10 +720,27 @@ a.cg-card {
|
|
|
720
720
|
margin-bottom: 2rem;
|
|
721
721
|
}
|
|
722
722
|
|
|
723
|
+
/* Featured Cards Grid for displayAs="featured-cards" */
|
|
724
|
+
.cg-articles-grid.cg-featured-cards-grid {
|
|
725
|
+
display: grid;
|
|
726
|
+
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
|
|
727
|
+
gap: 2rem;
|
|
728
|
+
margin-bottom: 2rem;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
.cg-articles-grid.cg-featured-cards-grid .cg-featured-card {
|
|
732
|
+
height: 100%;
|
|
733
|
+
}
|
|
734
|
+
|
|
723
735
|
@media (max-width: 768px) {
|
|
724
736
|
.cg-articles-grid.cg-grid {
|
|
725
737
|
grid-template-columns: 1fr;
|
|
726
738
|
}
|
|
739
|
+
|
|
740
|
+
.cg-articles-grid.cg-featured-cards-grid {
|
|
741
|
+
grid-template-columns: 1fr;
|
|
742
|
+
gap: 1.5rem;
|
|
743
|
+
}
|
|
727
744
|
}
|
|
728
745
|
|
|
729
746
|
/* Article Card */
|
package/dist/types/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export interface Article {
|
|
|
13
13
|
publishedAt: number;
|
|
14
14
|
summary: string | null;
|
|
15
15
|
featuredSummary: string | null;
|
|
16
|
+
featuredSummaryLayout?: 'vertical' | 'horizontal';
|
|
17
|
+
featuredCtaText?: string;
|
|
16
18
|
tags: string[];
|
|
17
19
|
wordCount: number;
|
|
18
20
|
}
|
|
@@ -126,6 +128,10 @@ export type DisplayMode = 'compact' | 'comfortable' | 'spacious';
|
|
|
126
128
|
* Theme mode
|
|
127
129
|
*/
|
|
128
130
|
export type Theme = 'light' | 'dark';
|
|
131
|
+
/**
|
|
132
|
+
* Display style for content list
|
|
133
|
+
*/
|
|
134
|
+
export type DisplayAs = 'default' | 'featured-cards';
|
|
129
135
|
/**
|
|
130
136
|
* Component props for ContentList
|
|
131
137
|
*/
|
|
@@ -149,6 +155,13 @@ export interface ContentListProps {
|
|
|
149
155
|
* @default 'comfortable'
|
|
150
156
|
*/
|
|
151
157
|
displayMode?: DisplayMode;
|
|
158
|
+
/**
|
|
159
|
+
* Display style - how to render each article
|
|
160
|
+
* 'default' = standard card/row layout
|
|
161
|
+
* 'featured-cards' = FeaturedCard style with summary and CTA
|
|
162
|
+
* @default 'default'
|
|
163
|
+
*/
|
|
164
|
+
displayAs?: DisplayAs;
|
|
152
165
|
/**
|
|
153
166
|
* Theme
|
|
154
167
|
* @default 'light'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, onMounted, computed, watch } from 'vue';
|
|
3
|
+
import { ContentGrowthClient } from '../core/client';
|
|
4
|
+
import { formatDate, calculateReadingTime } from '../core/utils';
|
|
5
|
+
import type { Article, ArticleWithContent } from '../types';
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
// Pre-loaded article data (bypasses API fetch)
|
|
9
|
+
article?: Article | ArticleWithContent;
|
|
10
|
+
// Load specific article by slug
|
|
11
|
+
slug?: string;
|
|
12
|
+
// Load specific article by UUID
|
|
13
|
+
uuid?: string;
|
|
14
|
+
// API config (required if fetching)
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
baseUrl?: string;
|
|
17
|
+
// Display options
|
|
18
|
+
linkPattern?: string;
|
|
19
|
+
linkTarget?: string;
|
|
20
|
+
showSummary?: boolean;
|
|
21
|
+
summaryMaxLength?: number;
|
|
22
|
+
showTags?: boolean;
|
|
23
|
+
showCategory?: boolean;
|
|
24
|
+
className?: string;
|
|
25
|
+
}>(), {
|
|
26
|
+
linkPattern: '/articles/{slug}',
|
|
27
|
+
showSummary: true,
|
|
28
|
+
showTags: false,
|
|
29
|
+
showCategory: true,
|
|
30
|
+
className: ''
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const loadedArticle = ref<Article | ArticleWithContent | null>(props.article || null);
|
|
34
|
+
const loading = ref(!props.article && !!(props.apiKey && (props.slug || props.uuid)));
|
|
35
|
+
const error = ref<string | null>(null);
|
|
36
|
+
|
|
37
|
+
// Watch for provided article prop changes
|
|
38
|
+
watch(() => props.article, (newArticle) => {
|
|
39
|
+
if (newArticle) {
|
|
40
|
+
loadedArticle.value = newArticle;
|
|
41
|
+
loading.value = false;
|
|
42
|
+
}
|
|
43
|
+
}, { immediate: true });
|
|
44
|
+
|
|
45
|
+
// Generate article URL
|
|
46
|
+
const articleUrl = computed(() => {
|
|
47
|
+
if (!loadedArticle.value) return '#';
|
|
48
|
+
return props.linkPattern
|
|
49
|
+
.replace('{uuid}', loadedArticle.value.uuid || '')
|
|
50
|
+
.replace('{slug}', loadedArticle.value.slug || loadedArticle.value.uuid || '')
|
|
51
|
+
.replace('{category}', loadedArticle.value.category || 'uncategorized');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Truncate summary text
|
|
55
|
+
const truncateSummary = (text: string | null, maxLength?: number): string => {
|
|
56
|
+
if (!text) return '';
|
|
57
|
+
if (!maxLength || text.length <= maxLength) return text;
|
|
58
|
+
return text.substring(0, maxLength).trim() + '...';
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const readingTime = computed(() => {
|
|
62
|
+
if (!loadedArticle.value) return '';
|
|
63
|
+
return calculateReadingTime(loadedArticle.value.wordCount);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const publishedDate = computed(() => {
|
|
67
|
+
if (!loadedArticle.value) return '';
|
|
68
|
+
return formatDate(loadedArticle.value.publishedAt);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const publishedDateTime = computed(() => {
|
|
72
|
+
if (!loadedArticle.value) return '';
|
|
73
|
+
return new Date(loadedArticle.value.publishedAt * 1000).toISOString();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
onMounted(async () => {
|
|
77
|
+
// If article is already provided, no need to fetch
|
|
78
|
+
if (props.article) {
|
|
79
|
+
loadedArticle.value = props.article;
|
|
80
|
+
loading.value = false;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Need API key and identifier to fetch
|
|
85
|
+
if (!props.apiKey || (!props.slug && !props.uuid)) {
|
|
86
|
+
loading.value = false;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
loading.value = true;
|
|
91
|
+
try {
|
|
92
|
+
const client = new ContentGrowthClient({
|
|
93
|
+
apiKey: props.apiKey,
|
|
94
|
+
baseUrl: props.baseUrl
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (props.uuid) {
|
|
98
|
+
loadedArticle.value = await client.getArticle(props.uuid);
|
|
99
|
+
} else if (props.slug) {
|
|
100
|
+
loadedArticle.value = await client.getArticleBySlug(props.slug);
|
|
101
|
+
}
|
|
102
|
+
} catch (err) {
|
|
103
|
+
error.value = err instanceof Error ? err.message : 'Failed to load article';
|
|
104
|
+
} finally {
|
|
105
|
+
loading.value = false;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<template>
|
|
111
|
+
<div v-if="loading" class="cg-widget cg-loading" :class="className">
|
|
112
|
+
<div class="cg-spinner"></div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div v-else-if="error" class="cg-widget cg-error" :class="className">
|
|
116
|
+
{{ error }}
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<article v-else-if="loadedArticle" class="cg-article-card" :class="className">
|
|
120
|
+
<a :href="articleUrl" :target="linkTarget" class="cg-card-link">
|
|
121
|
+
<div class="cg-card-content">
|
|
122
|
+
<div v-if="showCategory && loadedArticle.category" class="cg-card-category">
|
|
123
|
+
<span class="cg-category-badge">{{ loadedArticle.category }}</span>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<h2 class="cg-card-title">{{ loadedArticle.title }}</h2>
|
|
127
|
+
|
|
128
|
+
<p v-if="showSummary && loadedArticle.summary" class="cg-card-summary">
|
|
129
|
+
{{ truncateSummary(loadedArticle.summary, summaryMaxLength) }}
|
|
130
|
+
</p>
|
|
131
|
+
|
|
132
|
+
<div class="cg-card-meta">
|
|
133
|
+
<span class="cg-meta-author">{{ loadedArticle.authorName }}</span>
|
|
134
|
+
<span class="cg-meta-separator">•</span>
|
|
135
|
+
<time class="cg-meta-date" :datetime="publishedDateTime">
|
|
136
|
+
{{ publishedDate }}
|
|
137
|
+
</time>
|
|
138
|
+
<span class="cg-meta-separator">•</span>
|
|
139
|
+
<span class="cg-meta-reading-time">{{ readingTime }}</span>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div v-if="showTags && loadedArticle.tags && loadedArticle.tags.length > 0" class="cg-card-tags">
|
|
143
|
+
<span v-for="tag in loadedArticle.tags" :key="tag" class="cg-tag">{{ tag }}</span>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</a>
|
|
147
|
+
</article>
|
|
148
|
+
</template>
|
package/dist/vue/ContentList.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
:class="`cg-content-list cg-layout-${layout} cg-display-${displayMode} cg-theme-${theme} ${className}`"
|
|
3
|
+
:class="`cg-content-list cg-layout-${layout} cg-display-${displayMode} cg-theme-${theme} ${isFeaturedCardsMode ? 'cg-featured-cards-list' : ''} ${className}`"
|
|
4
4
|
data-cg-widget="list"
|
|
5
5
|
>
|
|
6
6
|
<div v-if="loading" class="cg-empty-state">
|
|
@@ -10,36 +10,33 @@
|
|
|
10
10
|
<p>No articles found.</p>
|
|
11
11
|
</div>
|
|
12
12
|
<template v-else>
|
|
13
|
-
<div :class="`cg-articles-grid ${layout === 'cards' ? 'cg-grid' : 'cg-list'}`">
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
</div>
|
|
41
|
-
</a>
|
|
42
|
-
</article>
|
|
13
|
+
<div :class="`cg-articles-grid ${isFeaturedCardsMode ? 'cg-featured-cards-grid' : (layout === 'cards' ? 'cg-grid' : 'cg-list')}`">
|
|
14
|
+
<!-- Featured Cards Mode -->
|
|
15
|
+
<template v-if="isFeaturedCardsMode">
|
|
16
|
+
<FeaturedCard
|
|
17
|
+
v-for="article in articles"
|
|
18
|
+
:key="article.uuid"
|
|
19
|
+
:article="article"
|
|
20
|
+
:linkPattern="linkPattern"
|
|
21
|
+
:linkTarget="buildLinkTarget(article)"
|
|
22
|
+
:showCategory="true"
|
|
23
|
+
/>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<!-- Default Card Mode - Use ContentCard component -->
|
|
27
|
+
<template v-else>
|
|
28
|
+
<ContentCard
|
|
29
|
+
v-for="article in articles"
|
|
30
|
+
:key="article.uuid"
|
|
31
|
+
:article="article"
|
|
32
|
+
:linkPattern="linkPattern"
|
|
33
|
+
:linkTarget="buildLinkTarget(article)"
|
|
34
|
+
:showSummary="showAiSummary"
|
|
35
|
+
:summaryMaxLength="summaryMaxLength"
|
|
36
|
+
:showTags="showTags"
|
|
37
|
+
:showCategory="true"
|
|
38
|
+
/>
|
|
39
|
+
</template>
|
|
43
40
|
</div>
|
|
44
41
|
|
|
45
42
|
<div v-if="showPagination && totalPages > 1" class="cg-pagination">
|
|
@@ -68,9 +65,10 @@
|
|
|
68
65
|
</template>
|
|
69
66
|
|
|
70
67
|
<script setup lang="ts">
|
|
71
|
-
import { ref, watch, onMounted } from 'vue';
|
|
68
|
+
import { ref, watch, onMounted, computed } from 'vue';
|
|
72
69
|
import { ContentGrowthClient } from '../core/client.js';
|
|
73
|
-
import
|
|
70
|
+
import FeaturedCard from './FeaturedCard.vue';
|
|
71
|
+
import ContentCard from './ContentCard.vue';
|
|
74
72
|
import type { ContentListProps, Article } from '../types/index.js';
|
|
75
73
|
|
|
76
74
|
export interface VueContentListProps extends Omit<ContentListProps, 'class'> {
|
|
@@ -80,6 +78,7 @@ export interface VueContentListProps extends Omit<ContentListProps, 'class'> {
|
|
|
80
78
|
const props = withDefaults(defineProps<VueContentListProps>(), {
|
|
81
79
|
layout: 'cards',
|
|
82
80
|
displayMode: 'comfortable',
|
|
81
|
+
displayAs: 'default',
|
|
83
82
|
theme: 'light',
|
|
84
83
|
pageSize: 12,
|
|
85
84
|
tags: () => [],
|
|
@@ -95,6 +94,8 @@ const currentPage = ref(1);
|
|
|
95
94
|
const totalPages = ref(1);
|
|
96
95
|
const loading = ref(true);
|
|
97
96
|
|
|
97
|
+
const isFeaturedCardsMode = computed(() => props.displayAs === 'featured-cards');
|
|
98
|
+
|
|
98
99
|
const fetchArticles = async () => {
|
|
99
100
|
loading.value = true;
|
|
100
101
|
try {
|
|
@@ -171,3 +172,4 @@ watch(
|
|
|
171
172
|
}
|
|
172
173
|
);
|
|
173
174
|
</script>
|
|
175
|
+
|