@contentgrowth/content-widget 1.3.5 → 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.
- package/README.md +14 -0
- package/dist/astro/FeaturedCard.astro +110 -36
- package/dist/react/FeaturedCard.d.ts +9 -0
- package/dist/react/FeaturedCard.d.ts.map +1 -1
- package/dist/react/FeaturedCard.js +66 -20
- package/dist/styles.css +174 -68
- package/dist/vue/FeaturedCard.vue +104 -28
- package/dist/widget/widget.css +1 -1
- package/dist/widget/widget.dev.css +174 -68
- package/dist/widget/widget.dev.js +3 -3
- package/dist/widget/widget.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -320,6 +320,8 @@ A compact card displaying the Featured Summary with customizable styling. Perfec
|
|
|
320
320
|
linkPattern="/articles/{slug}"
|
|
321
321
|
layout="horizontal"
|
|
322
322
|
borderStyle="dashed"
|
|
323
|
+
borderColor="#e5e7eb"
|
|
324
|
+
padding="20px"
|
|
323
325
|
ctaText="Read full story"
|
|
324
326
|
/>
|
|
325
327
|
|
|
@@ -350,11 +352,23 @@ A compact card displaying the Featured Summary with customizable styling. Perfec
|
|
|
350
352
|
| `borderColor` | string | '#e5e7eb' | Border color (CSS value) |
|
|
351
353
|
| `cardBackground` | string | 'none' | Card background ('none' = transparent) |
|
|
352
354
|
| `itemsBackground` | string | '#f3f4f6' | Background for list/quote section |
|
|
355
|
+
| `padding` | string | - | Custom padding (e.g., '10px', '2rem 3rem') |
|
|
353
356
|
| `ctaText` | string | 'Read full story' | Call-to-action text |
|
|
354
357
|
| `showAuthor` | boolean | false | Show author name |
|
|
355
358
|
| `showReadingTime` | boolean | false | Show reading time |
|
|
356
359
|
| `linkPattern` | string | '/articles/{slug}' | URL pattern for link |
|
|
357
360
|
|
|
361
|
+
**Featured Summary Types:**
|
|
362
|
+
|
|
363
|
+
FeaturedCard supports structured JSON summaries generated via the portal wizard:
|
|
364
|
+
|
|
365
|
+
| Type | Description |
|
|
366
|
+
|------|-------------|
|
|
367
|
+
| `list` | Intro text on left, bulleted key points on right |
|
|
368
|
+
| `steps` | Intro text on left, numbered action steps on right |
|
|
369
|
+
| `quote` | Intro text on left, styled pullquote on right |
|
|
370
|
+
| `classic` | Simple text summary (legacy) |
|
|
371
|
+
|
|
358
372
|
**Featured Cards List:**
|
|
359
373
|
|
|
360
374
|
Display all articles as FeaturedCards in a grid using `displayAs`:
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { marked } from 'marked';
|
|
3
2
|
import { ContentGrowthClient } from '../core/client';
|
|
4
3
|
import type { FeaturedContentProps, Article, ArticleWithContent } from '../types';
|
|
5
4
|
|
|
@@ -78,6 +77,12 @@ interface FeaturedCardProps extends Omit<FeaturedContentProps, 'showBackButton'
|
|
|
78
77
|
* @default undefined (same tab)
|
|
79
78
|
*/
|
|
80
79
|
linkTarget?: string;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Custom padding for the card content
|
|
83
|
+
* @example "20px" or "0"
|
|
84
|
+
*/
|
|
85
|
+
padding?: string;
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
type Props = FeaturedCardProps & { class?: string };
|
|
@@ -102,6 +107,7 @@ const {
|
|
|
102
107
|
borderColor = '#e5e7eb',
|
|
103
108
|
cardBackground = 'none',
|
|
104
109
|
itemsBackground = '#f3f4f6',
|
|
110
|
+
padding,
|
|
105
111
|
class: className = ''
|
|
106
112
|
} = Astro.props;
|
|
107
113
|
|
|
@@ -146,13 +152,39 @@ const getArticleUrl = (article: any) => {
|
|
|
146
152
|
.replace('{category}', article.category || 'uncategorized');
|
|
147
153
|
};
|
|
148
154
|
|
|
149
|
-
//
|
|
150
|
-
|
|
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 => {
|
|
151
166
|
const summaryText = article.featuredSummary || article.summary;
|
|
152
|
-
if (!summaryText) return
|
|
153
|
-
|
|
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
|
+
};
|
|
154
184
|
};
|
|
155
185
|
|
|
186
|
+
const summaryData = article ? parseSummary(article) : null;
|
|
187
|
+
|
|
156
188
|
const layout = propLayout || (article as any)?.featuredSummaryLayout || 'vertical';
|
|
157
189
|
const layoutClass = layout !== 'vertical' ? `cg-layout-${layout}` : '';
|
|
158
190
|
const borderClass = borderStyle !== 'none' ? `cg-border-${borderStyle}` : '';
|
|
@@ -165,6 +197,7 @@ const customStyles = [
|
|
|
165
197
|
borderColor !== '#e5e7eb' ? `--cg-card-border-color: ${borderColor}` : '',
|
|
166
198
|
cardBackground !== 'none' ? `--cg-card-bg: ${cardBackground}` : '',
|
|
167
199
|
itemsBackground !== '#f3f4f6' ? `--cg-items-bg: ${itemsBackground}` : '',
|
|
200
|
+
padding ? `--cg-card-padding: ${padding}` : '',
|
|
168
201
|
].filter(Boolean).join('; ');
|
|
169
202
|
---
|
|
170
203
|
|
|
@@ -178,42 +211,83 @@ const customStyles = [
|
|
|
178
211
|
rel={linkTarget === '_blank' ? 'noopener noreferrer' : undefined}
|
|
179
212
|
>
|
|
180
213
|
<article class="cg-featured-card-inner">
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
class="cg-featured-card-summary"
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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>
|
|
206
280
|
)}
|
|
207
281
|
|
|
208
|
-
{
|
|
209
|
-
<
|
|
210
|
-
{
|
|
211
|
-
</
|
|
282
|
+
{summaryData.type === 'quote' && (
|
|
283
|
+
<blockquote>
|
|
284
|
+
{summaryData.quote}
|
|
285
|
+
</blockquote>
|
|
212
286
|
)}
|
|
213
287
|
</div>
|
|
214
288
|
)}
|
|
215
|
-
|
|
216
|
-
{/* Read more indicator */}
|
|
289
|
+
|
|
290
|
+
{/* Read more indicator - Now at bottom */}
|
|
217
291
|
<div class="cg-featured-card-cta">
|
|
218
292
|
<span>{ctaText}</span>
|
|
219
293
|
<svg class="cg-featured-card-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24" width="16" height="16">
|
|
@@ -58,11 +58,20 @@ interface FeaturedCardProps extends Partial<Omit<FeaturedContentProps, 'showBack
|
|
|
58
58
|
* @default 'none'
|
|
59
59
|
*/
|
|
60
60
|
cardBackground?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Background color for list/quote section (the items area)
|
|
63
|
+
* @default '#f3f4f6'
|
|
64
|
+
*/
|
|
61
65
|
/**
|
|
62
66
|
* Background color for list/quote section (the items area)
|
|
63
67
|
* @default '#f3f4f6'
|
|
64
68
|
*/
|
|
65
69
|
itemsBackground?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Custom padding for the card content
|
|
72
|
+
* @example "20px" or "0"
|
|
73
|
+
*/
|
|
74
|
+
padding?: string;
|
|
66
75
|
}
|
|
67
76
|
export declare const FeaturedCard: React.FC<FeaturedCardProps>;
|
|
68
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;
|
|
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"}
|
|
@@ -1,7 +1,27 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import { marked } from 'marked';
|
|
3
2
|
import { ContentGrowthClient } from '../core/client';
|
|
4
|
-
|
|
3
|
+
// Parse featured summary - supports both JSON (new) and plain text (legacy)
|
|
4
|
+
const parseSummary = (article) => {
|
|
5
|
+
const summaryText = article.featuredSummary || article.summary;
|
|
6
|
+
if (!summaryText)
|
|
7
|
+
return null;
|
|
8
|
+
// Try to parse as JSON
|
|
9
|
+
try {
|
|
10
|
+
const parsed = JSON.parse(summaryText);
|
|
11
|
+
if (parsed.type) {
|
|
12
|
+
return parsed;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
catch (e) {
|
|
16
|
+
// Not JSON, treat as legacy markdown/plain text
|
|
17
|
+
}
|
|
18
|
+
// Legacy fallback - render as plain text
|
|
19
|
+
return {
|
|
20
|
+
type: 'legacy',
|
|
21
|
+
text: summaryText
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
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', padding, className = '' }) => {
|
|
5
25
|
const [article, setArticle] = useState(providedArticle || null);
|
|
6
26
|
const [loading, setLoading] = useState(!providedArticle);
|
|
7
27
|
const [error, setError] = useState(null);
|
|
@@ -56,13 +76,6 @@ export const FeaturedCard = ({ apiKey, baseUrl, article: providedArticle, slug,
|
|
|
56
76
|
.replace('{slug}', article.slug || article.uuid || '')
|
|
57
77
|
.replace('{category}', article.category || 'uncategorized');
|
|
58
78
|
};
|
|
59
|
-
// Render featured summary (or fallback to regular summary) as HTML
|
|
60
|
-
const getSummaryHtml = (article) => {
|
|
61
|
-
const summaryText = article.featuredSummary || article.summary;
|
|
62
|
-
if (!summaryText)
|
|
63
|
-
return '';
|
|
64
|
-
return marked.parse(summaryText, { async: false });
|
|
65
|
-
};
|
|
66
79
|
if (loading) {
|
|
67
80
|
return (React.createElement("div", { className: `cg-widget cg-loading ${className}` },
|
|
68
81
|
React.createElement("div", { className: "cg-spinner" })));
|
|
@@ -70,7 +83,6 @@ export const FeaturedCard = ({ apiKey, baseUrl, article: providedArticle, slug,
|
|
|
70
83
|
if (error || !article) {
|
|
71
84
|
return (React.createElement("div", { className: `cg-widget cg-error ${className}` }, error || 'No featured content found'));
|
|
72
85
|
}
|
|
73
|
-
const summaryHtml = getSummaryHtml(article);
|
|
74
86
|
const layout = propLayout || article.featuredSummaryLayout || 'vertical';
|
|
75
87
|
const readingTime = Math.ceil(article.wordCount / 200);
|
|
76
88
|
const borderClass = borderStyle !== 'none' ? `cg-border-${borderStyle}` : '';
|
|
@@ -84,18 +96,52 @@ export const FeaturedCard = ({ apiKey, baseUrl, article: providedArticle, slug,
|
|
|
84
96
|
customStyles['--cg-card-bg'] = cardBackground;
|
|
85
97
|
if (itemsBackground !== '#f3f4f6')
|
|
86
98
|
customStyles['--cg-items-bg'] = itemsBackground;
|
|
99
|
+
if (padding)
|
|
100
|
+
customStyles['--cg-card-padding'] = padding;
|
|
87
101
|
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 },
|
|
88
102
|
React.createElement("article", { className: "cg-featured-card-inner" },
|
|
89
|
-
|
|
90
|
-
React.createElement("
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
React.createElement("div", { className: "cg-card-primary" },
|
|
104
|
+
showCategory && article.category && (React.createElement("div", { className: "cg-featured-card-category" },
|
|
105
|
+
React.createElement("span", { className: "cg-category-badge" }, article.category))),
|
|
106
|
+
React.createElement("h3", { className: "cg-featured-card-title" }, article.title),
|
|
107
|
+
parseSummary(article) && (React.createElement("div", { className: "cg-featured-card-summary" }, (() => {
|
|
108
|
+
const summaryData = parseSummary(article);
|
|
109
|
+
if (summaryData.type === 'classic') {
|
|
110
|
+
return React.createElement("p", null, summaryData.text);
|
|
111
|
+
}
|
|
112
|
+
if ((summaryData.type === 'list' || summaryData.type === 'steps') && summaryData.intro) {
|
|
113
|
+
return React.createElement("p", null, summaryData.intro);
|
|
114
|
+
}
|
|
115
|
+
if (summaryData.type === 'quote' && summaryData.quote) {
|
|
116
|
+
return React.createElement("p", null, summaryData.intro);
|
|
117
|
+
}
|
|
118
|
+
// Legacy
|
|
119
|
+
return React.createElement("p", null, summaryData.text);
|
|
120
|
+
})())),
|
|
121
|
+
(showAuthor || showReadingTime) && (React.createElement("div", { className: "cg-featured-card-footer" },
|
|
122
|
+
showAuthor && React.createElement("span", { className: "cg-featured-card-author" }, article.authorName),
|
|
123
|
+
showAuthor && showReadingTime && (React.createElement("span", { className: "cg-featured-card-separator" }, "\u2022")),
|
|
124
|
+
showReadingTime && (React.createElement("span", { className: "cg-featured-card-reading-time" },
|
|
125
|
+
readingTime,
|
|
126
|
+
" min read"))))),
|
|
127
|
+
(() => {
|
|
128
|
+
const summaryData = parseSummary(article);
|
|
129
|
+
if (!summaryData)
|
|
130
|
+
return null;
|
|
131
|
+
if ((summaryData.type === 'list' || summaryData.type === 'steps') && summaryData.items) {
|
|
132
|
+
return (React.createElement("div", { className: "cg-card-secondary" },
|
|
133
|
+
React.createElement("ul", { className: "cg-summary-items" }, summaryData.items.map((item, index) => (React.createElement("li", { key: index },
|
|
134
|
+
React.createElement("span", { className: "cg-item-number" }, index + 1),
|
|
135
|
+
React.createElement("div", { className: "cg-item-content" },
|
|
136
|
+
React.createElement("strong", { className: "cg-item-title" }, item.title),
|
|
137
|
+
React.createElement("span", { className: "cg-item-description" }, item.description))))))));
|
|
138
|
+
}
|
|
139
|
+
if (summaryData.type === 'quote' && summaryData.quote) {
|
|
140
|
+
return (React.createElement("div", { className: "cg-card-secondary" },
|
|
141
|
+
React.createElement("blockquote", null, summaryData.quote)));
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
})(),
|
|
99
145
|
React.createElement("div", { className: "cg-featured-card-cta" },
|
|
100
146
|
React.createElement("span", null, ctaText),
|
|
101
147
|
React.createElement("svg", { className: "cg-featured-card-arrow", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", width: "16", height: "16" },
|