@contentgrowth/content-widget 1.3.4 → 1.3.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.
@@ -1,9 +1,23 @@
1
1
  ---
2
- import { marked } from 'marked';
3
2
  import { ContentGrowthClient } from '../core/client';
4
- import type { FeaturedContentProps } from '../types';
3
+ import type { FeaturedContentProps, Article, ArticleWithContent } from '../types';
5
4
 
6
5
  interface FeaturedCardProps extends Omit<FeaturedContentProps, 'showBackButton' | 'backUrl'> {
6
+ /**
7
+ * Pre-loaded article data (bypasses API fetch - used by ContentList)
8
+ */
9
+ article?: Article | ArticleWithContent;
10
+
11
+ /**
12
+ * Load specific article by slug (bypasses category/tags search)
13
+ */
14
+ slug?: string;
15
+
16
+ /**
17
+ * Load specific article by UUID (bypasses category/tags search)
18
+ */
19
+ uuid?: string;
20
+
7
21
  /**
8
22
  * URL pattern for the article link
9
23
  * Supports placeholders: {uuid}, {slug}, {category}
@@ -57,6 +71,18 @@ interface FeaturedCardProps extends Omit<FeaturedContentProps, 'showBackButton'
57
71
  * @default '#f3f4f6'
58
72
  */
59
73
  itemsBackground?: string;
74
+
75
+ /**
76
+ * Link target attribute
77
+ * @default undefined (same tab)
78
+ */
79
+ linkTarget?: string;
80
+
81
+ /**
82
+ * Custom padding for the card content
83
+ * @example "20px" or "0"
84
+ */
85
+ padding?: string;
60
86
  }
61
87
 
62
88
  type Props = FeaturedCardProps & { class?: string };
@@ -64,6 +90,9 @@ type Props = FeaturedCardProps & { class?: string };
64
90
  const {
65
91
  apiKey,
66
92
  baseUrl,
93
+ article: providedArticle,
94
+ slug,
95
+ uuid,
67
96
  tags = [],
68
97
  category,
69
98
  excludeTags = [],
@@ -72,29 +101,46 @@ const {
72
101
  showAuthor = false,
73
102
  ctaText: propCtaText,
74
103
  linkPattern = '/articles/{slug}',
104
+ linkTarget,
75
105
  layout: propLayout,
76
106
  borderStyle = 'none',
77
107
  borderColor = '#e5e7eb',
78
108
  cardBackground = 'none',
79
109
  itemsBackground = '#f3f4f6',
110
+ padding,
80
111
  class: className = ''
81
112
  } = Astro.props;
82
113
 
83
- // Fetch featured article
84
- const client = new ContentGrowthClient({ apiKey, baseUrl });
85
- let article = null;
86
- let error = null;
87
-
88
- try {
89
- article = await client.getFeaturedArticle({
90
- tags,
91
- category,
92
- excludeTags
93
- });
94
- } catch (e) {
95
- // If 404, article remains null
96
- console.error('Failed to fetch featured article:', e);
97
- error = e instanceof Error ? e.message : 'Failed to load featured article';
114
+ // Determine article source
115
+ let article: Article | ArticleWithContent | null = null;
116
+ let error: string | null = null;
117
+
118
+ if (providedArticle) {
119
+ // Mode 1: Article provided directly (from ContentList)
120
+ article = providedArticle;
121
+ } else if (apiKey) {
122
+ // Need to fetch article from API
123
+ const client = new ContentGrowthClient({ apiKey, baseUrl });
124
+
125
+ try {
126
+ if (uuid) {
127
+ // Mode 2: Load by UUID
128
+ article = await client.getArticle(uuid, { excludeTags });
129
+ } else if (slug) {
130
+ // Mode 3: Load by slug
131
+ article = await client.getArticleBySlug(slug, { excludeTags });
132
+ } else {
133
+ // Mode 4: Find featured article by category/tags (original behavior)
134
+ article = await client.getFeaturedArticle({
135
+ tags,
136
+ category,
137
+ excludeTags
138
+ });
139
+ }
140
+ } catch (e) {
141
+ console.error('Failed to fetch article:', e);
142
+ error = e instanceof Error ? e.message : 'Failed to load article';
143
+ }
98
144
  }
99
145
 
100
146
  // Generate article URL
@@ -106,25 +152,52 @@ const getArticleUrl = (article: any) => {
106
152
  .replace('{category}', article.category || 'uncategorized');
107
153
  };
108
154
 
109
- // Render featured summary (or fallback to regular summary) as HTML
110
- const getSummaryHtml = (article: any) => {
155
+ // Parse featured summary - supports both JSON (new) and plain text (legacy)
156
+ interface SummaryData {
157
+ type: 'classic' | 'list' | 'steps' | 'quote' | 'legacy';
158
+ text?: string;
159
+ intro?: string;
160
+ items?: Array<{ title: string; description: string }>;
161
+ quote?: string;
162
+ highlight?: string;
163
+ }
164
+
165
+ const parseSummary = (article: any): SummaryData | null => {
111
166
  const summaryText = article.featuredSummary || article.summary;
112
- if (!summaryText) return '';
113
- return marked.parse(summaryText, { async: false }) as string;
167
+ if (!summaryText) return null;
168
+
169
+ // Try to parse as JSON
170
+ try {
171
+ const parsed = JSON.parse(summaryText);
172
+ if (parsed.type) {
173
+ return parsed as SummaryData;
174
+ }
175
+ } catch (e) {
176
+ // Not JSON, treat as legacy markdown/plain text
177
+ }
178
+
179
+ // Legacy fallback - render as plain text
180
+ return {
181
+ type: 'legacy',
182
+ text: summaryText
183
+ };
114
184
  };
115
185
 
116
- const layout = propLayout || article?.featuredSummaryLayout || 'standard';
117
- const layoutClass = layout !== 'standard' ? `cg-layout-${layout}` : '';
186
+ const summaryData = article ? parseSummary(article) : null;
187
+
188
+ const layout = propLayout || (article as any)?.featuredSummaryLayout || 'vertical';
189
+ const layoutClass = layout !== 'vertical' ? `cg-layout-${layout}` : '';
118
190
  const borderClass = borderStyle !== 'none' ? `cg-border-${borderStyle}` : '';
119
191
 
120
192
  // Use ctaText from prop, or from article data, or fallback to default
121
- const ctaText = propCtaText || article?.featuredCtaText || 'Read full story';
193
+ const ctaText = propCtaText || (article as any)?.featuredCtaText || 'Read full story';
122
194
 
123
195
  // Custom CSS properties for styling
124
196
  const customStyles = [
125
197
  borderColor !== '#e5e7eb' ? `--cg-card-border-color: ${borderColor}` : '',
126
198
  cardBackground !== 'none' ? `--cg-card-bg: ${cardBackground}` : '',
127
199
  itemsBackground !== '#f3f4f6' ? `--cg-items-bg: ${itemsBackground}` : '',
200
+ padding ? `--cg-card-padding: ${padding}` : '',
128
201
  ].filter(Boolean).join('; ');
129
202
  ---
130
203
 
@@ -134,44 +207,87 @@ const customStyles = [
134
207
  class={`cg-widget cg-featured-card ${className} ${layoutClass} ${borderClass}`}
135
208
  style={customStyles || undefined}
136
209
  data-cg-widget="featured-card"
210
+ target={linkTarget}
211
+ rel={linkTarget === '_blank' ? 'noopener noreferrer' : undefined}
137
212
  >
138
213
  <article class="cg-featured-card-inner">
139
- {/* Header with category badge */}
140
- {showCategory && article.category && (
141
- <div class="cg-featured-card-category">
142
- <span class="cg-category-badge">{article.category}</span>
143
- </div>
144
- )}
145
-
146
- {/* Title */}
147
- <h3 class="cg-featured-card-title">{article.title}</h3>
148
-
149
- {/* Featured Summary */}
150
- {(article.featuredSummary || article.summary) && (
151
- <div
152
- class="cg-featured-card-summary"
153
- set:html={getSummaryHtml(article)}
154
- />
155
- )}
156
-
157
- {/* Footer with meta info */}
158
- {(showAuthor || showReadingTime) && (
159
- <div class="cg-featured-card-footer">
160
- {showAuthor && <span class="cg-featured-card-author">{article.authorName}</span>}
161
-
162
- {showAuthor && showReadingTime && (
163
- <span class="cg-featured-card-separator">•</span>
214
+ <div class="cg-card-primary">
215
+ {/* Header with category badge */}
216
+ {showCategory && article.category && (
217
+ <div class="cg-featured-card-category">
218
+ <span class="cg-category-badge">{article.category}</span>
219
+ </div>
220
+ )}
221
+
222
+ {/* Title */}
223
+ <h3 class="cg-featured-card-title">{article.title}</h3>
224
+
225
+ {/* Featured Summary - Intro / Text Part */}
226
+ {summaryData && (
227
+ <div class="cg-featured-card-summary">
228
+ {/* Intro for structured types */}
229
+ {(summaryData.type === 'list' || summaryData.type === 'steps' || summaryData.type === 'quote') && summaryData.intro && (
230
+ <p>{summaryData.intro}</p>
231
+ )}
232
+
233
+ {/* Classic type - just text */}
234
+ {summaryData.type === 'classic' && (
235
+ <p>{summaryData.text}</p>
236
+ )}
237
+
238
+ {/* Legacy markdown - render as plain text */}
239
+ {summaryData.type === 'legacy' && (
240
+ <p>{summaryData.text}</p>
241
+ )}
242
+ </div>
243
+ )}
244
+
245
+ {/* Footer with meta info */}
246
+ {(showAuthor || showReadingTime) && (
247
+ <div class="cg-featured-card-footer">
248
+ {showAuthor && <span class="cg-featured-card-author">{article.authorName}</span>}
249
+
250
+ {showAuthor && showReadingTime && (
251
+ <span class="cg-featured-card-separator">•</span>
252
+ )}
253
+
254
+ {showReadingTime && (
255
+ <span class="cg-featured-card-reading-time">
256
+ {Math.ceil(article.wordCount / 200)} min read
257
+ </span>
258
+ )}
259
+ </div>
260
+ )}
261
+
262
+ {/* Read more indicator */}
263
+ </div>
264
+
265
+ {/* Right Panel - Structured Visual Items (List/Quote) */}
266
+ {summaryData && (summaryData.type === 'list' || summaryData.type === 'steps' || summaryData.type === 'quote') && (
267
+ <div class="cg-card-secondary">
268
+ {(summaryData.type === 'list' || summaryData.type === 'steps') && (
269
+ <ul class="cg-summary-items">
270
+ {summaryData.items?.map((item, index) => (
271
+ <li>
272
+ <span class="cg-item-number">{index + 1}</span>
273
+ <div class="cg-item-content">
274
+ <strong class="cg-item-title">{item.title}</strong>
275
+ <span class="cg-item-description">{item.description}</span>
276
+ </div>
277
+ </li>
278
+ ))}
279
+ </ul>
164
280
  )}
165
281
 
166
- {showReadingTime && (
167
- <span class="cg-featured-card-reading-time">
168
- {Math.ceil(article.wordCount / 200)} min read
169
- </span>
282
+ {summaryData.type === 'quote' && (
283
+ <blockquote>
284
+ {summaryData.quote}
285
+ </blockquote>
170
286
  )}
171
287
  </div>
172
288
  )}
173
-
174
- {/* Read more indicator */}
289
+
290
+ {/* Read more indicator - Now at bottom */}
175
291
  <div class="cg-featured-card-cta">
176
292
  <span>{ctaText}</span>
177
293
  <svg class="cg-featured-card-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24" width="16" height="16">
@@ -187,5 +303,3 @@ const customStyles = [
187
303
  </div>
188
304
  ) : null
189
305
  )}
190
-
191
-
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+ import type { Article, ArticleWithContent } from '../types';
3
+ export interface ContentCardProps {
4
+ /**
5
+ * Pre-loaded article data (bypasses API fetch)
6
+ */
7
+ article?: Article | ArticleWithContent;
8
+ /**
9
+ * Load specific article by slug
10
+ */
11
+ slug?: string;
12
+ /**
13
+ * Load specific article by UUID
14
+ */
15
+ uuid?: string;
16
+ /**
17
+ * API key (required if fetching article)
18
+ */
19
+ apiKey?: string;
20
+ /**
21
+ * API base URL
22
+ */
23
+ baseUrl?: string;
24
+ /**
25
+ * URL pattern for the article link
26
+ * @default '/articles/{slug}'
27
+ */
28
+ linkPattern?: string;
29
+ /**
30
+ * Link target attribute
31
+ */
32
+ linkTarget?: string;
33
+ /**
34
+ * Show article summary
35
+ * @default true
36
+ */
37
+ showSummary?: boolean;
38
+ /**
39
+ * Maximum length of summary text
40
+ */
41
+ summaryMaxLength?: number;
42
+ /**
43
+ * Show article tags
44
+ * @default false
45
+ */
46
+ showTags?: boolean;
47
+ /**
48
+ * Show article category
49
+ * @default true
50
+ */
51
+ showCategory?: boolean;
52
+ /**
53
+ * Additional CSS class
54
+ */
55
+ className?: string;
56
+ }
57
+ export declare const ContentCard: React.FC<ContentCardProps>;
58
+ export default ContentCard;
59
+ //# sourceMappingURL=ContentCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContentCard.d.ts","sourceRoot":"","sources":["../../src/react/ContentCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC7B;;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;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAgIlD,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -0,0 +1,84 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { ContentGrowthClient } from '../core/client';
3
+ import { formatDate, calculateReadingTime } from '../core/utils';
4
+ export const ContentCard = ({ apiKey, baseUrl, article: providedArticle, slug, uuid, linkPattern = '/articles/{slug}', linkTarget, showSummary = true, summaryMaxLength, showTags = false, showCategory = true, className = '' }) => {
5
+ const [article, setArticle] = useState(providedArticle || null);
6
+ const [loading, setLoading] = useState(!providedArticle && !!(apiKey && (slug || uuid)));
7
+ const [error, setError] = useState(null);
8
+ useEffect(() => {
9
+ // If article is provided, use it directly
10
+ if (providedArticle) {
11
+ setArticle(providedArticle);
12
+ setLoading(false);
13
+ return;
14
+ }
15
+ // Need API key and identifier to fetch
16
+ if (!apiKey || (!slug && !uuid)) {
17
+ setLoading(false);
18
+ return;
19
+ }
20
+ const fetchArticle = async () => {
21
+ setLoading(true);
22
+ try {
23
+ const client = new ContentGrowthClient({ apiKey, baseUrl });
24
+ if (uuid) {
25
+ const fetchedArticle = await client.getArticle(uuid);
26
+ setArticle(fetchedArticle);
27
+ }
28
+ else if (slug) {
29
+ const fetchedArticle = await client.getArticleBySlug(slug);
30
+ setArticle(fetchedArticle);
31
+ }
32
+ }
33
+ catch (err) {
34
+ setError(err instanceof Error ? err.message : 'Failed to load article');
35
+ }
36
+ finally {
37
+ setLoading(false);
38
+ }
39
+ };
40
+ fetchArticle();
41
+ }, [apiKey, baseUrl, providedArticle, uuid, slug]);
42
+ // Generate article URL
43
+ const getArticleUrl = (article) => {
44
+ return linkPattern
45
+ .replace('{uuid}', article.uuid || '')
46
+ .replace('{slug}', article.slug || article.uuid || '')
47
+ .replace('{category}', article.category || 'uncategorized');
48
+ };
49
+ // Truncate summary text
50
+ const truncateSummary = (text, maxLength) => {
51
+ if (!text)
52
+ return '';
53
+ if (!maxLength || text.length <= maxLength)
54
+ return text;
55
+ return text.substring(0, maxLength).trim() + '...';
56
+ };
57
+ if (loading) {
58
+ return (React.createElement("div", { className: `cg-widget cg-loading ${className}` },
59
+ React.createElement("div", { className: "cg-spinner" })));
60
+ }
61
+ if (error || !article) {
62
+ if (error) {
63
+ return (React.createElement("div", { className: `cg-widget cg-error ${className}` }, error));
64
+ }
65
+ return null;
66
+ }
67
+ const readingTime = calculateReadingTime(article.wordCount);
68
+ const publishedDate = formatDate(article.publishedAt);
69
+ return (React.createElement("article", { className: `cg-article-card ${className}` },
70
+ React.createElement("a", { href: getArticleUrl(article), target: linkTarget, className: "cg-card-link" },
71
+ React.createElement("div", { className: "cg-card-content" },
72
+ showCategory && article.category && (React.createElement("div", { className: "cg-card-category" },
73
+ React.createElement("span", { className: "cg-category-badge" }, article.category))),
74
+ React.createElement("h2", { className: "cg-card-title" }, article.title),
75
+ showSummary && article.summary && (React.createElement("p", { className: "cg-card-summary" }, truncateSummary(article.summary, summaryMaxLength))),
76
+ React.createElement("div", { className: "cg-card-meta" },
77
+ React.createElement("span", { className: "cg-meta-author" }, article.authorName),
78
+ React.createElement("span", { className: "cg-meta-separator" }, "\u2022"),
79
+ React.createElement("time", { className: "cg-meta-date", dateTime: new Date(article.publishedAt * 1000).toISOString() }, publishedDate),
80
+ React.createElement("span", { className: "cg-meta-separator" }, "\u2022"),
81
+ React.createElement("span", { className: "cg-meta-reading-time" }, readingTime)),
82
+ 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)))))))));
83
+ };
84
+ export default ContentCard;
@@ -1 +1 @@
1
- {"version":3,"file":"ContentList.d.ts","sourceRoot":"","sources":["../../src/react/ContentList.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,KAAK,EAAE,gBAAgB,EAAW,MAAM,mBAAmB,CAAC;AAEnE,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAiLvD,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ContentList.d.ts","sourceRoot":"","sources":["../../src/react/ContentList.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,KAAK,EAAE,gBAAgB,EAAW,MAAM,mBAAmB,CAAC;AAEnE,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAsKvD,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -4,12 +4,14 @@
4
4
  */
5
5
  import React, { useEffect, useState } from 'react';
6
6
  import { ContentGrowthClient } from '../core/client.js';
7
- import { formatDate, calculateReadingTime } from '../core/utils.js';
8
- export const ContentList = ({ apiKey, baseUrl, layout = 'cards', displayMode = 'comfortable', theme = 'light', pageSize = 12, tags = [], category, showPagination = true, linkPattern = '/articles/{uuid}', showTags = false, showAiSummary = true, summaryMaxLength, linkTarget, className = '' }) => {
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
- const readingTime = calculateReadingTime(article.wordCount);
82
- const publishedDate = formatDate(article.publishedAt);
83
- return (React.createElement("article", { key: article.uuid, className: "cg-article-card" },
84
- React.createElement("a", { href: articleUrl, target: articleTarget, className: "cg-card-link" },
85
- React.createElement("div", { className: "cg-card-content" },
86
- article.category && (React.createElement("div", { className: "cg-card-category" },
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}
@@ -46,11 +58,20 @@ interface FeaturedCardProps extends Omit<FeaturedContentProps, 'showBackButton'
46
58
  * @default 'none'
47
59
  */
48
60
  cardBackground?: string;
61
+ /**
62
+ * Background color for list/quote section (the items area)
63
+ * @default '#f3f4f6'
64
+ */
49
65
  /**
50
66
  * Background color for list/quote section (the items area)
51
67
  * @default '#f3f4f6'
52
68
  */
53
69
  itemsBackground?: string;
70
+ /**
71
+ * Custom padding for the card content
72
+ * @example "20px" or "0"
73
+ */
74
+ padding?: string;
54
75
  }
55
76
  export declare const FeaturedCard: React.FC<FeaturedCardProps>;
56
77
  export {};
@@ -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,EAAsB,MAAM,UAAU,CAAC;AAEzE,UAAU,iBAAkB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,GAAG,SAAS,GAAG,eAAe,GAAG,UAAU,CAAC;IACvH;;;;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,CA8IpD,CAAC"}
1
+ {"version":3,"file":"FeaturedCard.d.ts","sourceRoot":"","sources":["../../src/react/FeaturedCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAkClF,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;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA4NpD,CAAC"}