@contentgrowth/content-widget 1.3.5 → 1.3.7
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 +109 -36
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/inline-markdown.d.ts +13 -0
- package/dist/core/inline-markdown.d.ts.map +1 -0
- package/dist/core/inline-markdown.js +29 -0
- package/dist/react/FeaturedCard.d.ts +9 -0
- package/dist/react/FeaturedCard.d.ts.map +1 -1
- package/dist/react/FeaturedCard.js +67 -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,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { marked } from 'marked';
|
|
3
2
|
import { ContentGrowthClient } from '../core/client';
|
|
3
|
+
import { renderInlineMarkdownJS } from '../core/inline-markdown';
|
|
4
4
|
import type { FeaturedContentProps, Article, ArticleWithContent } from '../types';
|
|
5
5
|
|
|
6
6
|
interface FeaturedCardProps extends Omit<FeaturedContentProps, 'showBackButton' | 'backUrl'> {
|
|
@@ -78,6 +78,12 @@ interface FeaturedCardProps extends Omit<FeaturedContentProps, 'showBackButton'
|
|
|
78
78
|
* @default undefined (same tab)
|
|
79
79
|
*/
|
|
80
80
|
linkTarget?: string;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Custom padding for the card content
|
|
84
|
+
* @example "20px" or "0"
|
|
85
|
+
*/
|
|
86
|
+
padding?: string;
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
type Props = FeaturedCardProps & { class?: string };
|
|
@@ -102,6 +108,7 @@ const {
|
|
|
102
108
|
borderColor = '#e5e7eb',
|
|
103
109
|
cardBackground = 'none',
|
|
104
110
|
itemsBackground = '#f3f4f6',
|
|
111
|
+
padding,
|
|
105
112
|
class: className = ''
|
|
106
113
|
} = Astro.props;
|
|
107
114
|
|
|
@@ -146,13 +153,39 @@ const getArticleUrl = (article: any) => {
|
|
|
146
153
|
.replace('{category}', article.category || 'uncategorized');
|
|
147
154
|
};
|
|
148
155
|
|
|
149
|
-
//
|
|
150
|
-
|
|
156
|
+
// Parse featured summary - supports both JSON (new) and plain text (legacy)
|
|
157
|
+
interface SummaryData {
|
|
158
|
+
type: 'classic' | 'list' | 'steps' | 'quote' | 'legacy';
|
|
159
|
+
text?: string;
|
|
160
|
+
intro?: string;
|
|
161
|
+
items?: Array<{ title: string; description: string }>;
|
|
162
|
+
quote?: string;
|
|
163
|
+
highlight?: string;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const parseSummary = (article: any): SummaryData | null => {
|
|
151
167
|
const summaryText = article.featuredSummary || article.summary;
|
|
152
|
-
if (!summaryText) return
|
|
153
|
-
|
|
168
|
+
if (!summaryText) return null;
|
|
169
|
+
|
|
170
|
+
// Try to parse as JSON
|
|
171
|
+
try {
|
|
172
|
+
const parsed = JSON.parse(summaryText);
|
|
173
|
+
if (parsed.type) {
|
|
174
|
+
return parsed as SummaryData;
|
|
175
|
+
}
|
|
176
|
+
} catch (e) {
|
|
177
|
+
// Not JSON, treat as legacy markdown/plain text
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Legacy fallback - render as plain text
|
|
181
|
+
return {
|
|
182
|
+
type: 'legacy',
|
|
183
|
+
text: summaryText
|
|
184
|
+
};
|
|
154
185
|
};
|
|
155
186
|
|
|
187
|
+
const summaryData = article ? parseSummary(article) : null;
|
|
188
|
+
|
|
156
189
|
const layout = propLayout || (article as any)?.featuredSummaryLayout || 'vertical';
|
|
157
190
|
const layoutClass = layout !== 'vertical' ? `cg-layout-${layout}` : '';
|
|
158
191
|
const borderClass = borderStyle !== 'none' ? `cg-border-${borderStyle}` : '';
|
|
@@ -165,6 +198,7 @@ const customStyles = [
|
|
|
165
198
|
borderColor !== '#e5e7eb' ? `--cg-card-border-color: ${borderColor}` : '',
|
|
166
199
|
cardBackground !== 'none' ? `--cg-card-bg: ${cardBackground}` : '',
|
|
167
200
|
itemsBackground !== '#f3f4f6' ? `--cg-items-bg: ${itemsBackground}` : '',
|
|
201
|
+
padding ? `--cg-card-padding: ${padding}` : '',
|
|
168
202
|
].filter(Boolean).join('; ');
|
|
169
203
|
---
|
|
170
204
|
|
|
@@ -178,42 +212,81 @@ const customStyles = [
|
|
|
178
212
|
rel={linkTarget === '_blank' ? 'noopener noreferrer' : undefined}
|
|
179
213
|
>
|
|
180
214
|
<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
|
-
|
|
215
|
+
<div class="cg-card-primary">
|
|
216
|
+
{/* Header with category badge */}
|
|
217
|
+
{showCategory && article.category && (
|
|
218
|
+
<div class="cg-featured-card-category">
|
|
219
|
+
<span class="cg-category-badge">{article.category}</span>
|
|
220
|
+
</div>
|
|
221
|
+
)}
|
|
222
|
+
|
|
223
|
+
{/* Title */}
|
|
224
|
+
<h3 class="cg-featured-card-title">{article.title}</h3>
|
|
225
|
+
|
|
226
|
+
{/* Featured Summary - Intro / Text Part */}
|
|
227
|
+
{summaryData && (
|
|
228
|
+
<div class="cg-featured-card-summary">
|
|
229
|
+
{/* Intro for structured types */}
|
|
230
|
+
{(summaryData.type === 'list' || summaryData.type === 'steps' || summaryData.type === 'quote') && summaryData.intro && (
|
|
231
|
+
<p set:html={renderInlineMarkdownJS(summaryData.intro)} />
|
|
232
|
+
)}
|
|
233
|
+
|
|
234
|
+
{/* Classic type - just text */}
|
|
235
|
+
{summaryData.type === 'classic' && (
|
|
236
|
+
<p set:html={renderInlineMarkdownJS(summaryData.text || '')} />
|
|
237
|
+
)}
|
|
238
|
+
|
|
239
|
+
{/* Legacy markdown - render as plain text */}
|
|
240
|
+
{summaryData.type === 'legacy' && (
|
|
241
|
+
<p>{summaryData.text}</p>
|
|
242
|
+
)}
|
|
243
|
+
</div>
|
|
244
|
+
)}
|
|
245
|
+
|
|
246
|
+
{/* Footer with meta info */}
|
|
247
|
+
{(showAuthor || showReadingTime) && (
|
|
248
|
+
<div class="cg-featured-card-footer">
|
|
249
|
+
{showAuthor && <span class="cg-featured-card-author">{article.authorName}</span>}
|
|
250
|
+
|
|
251
|
+
{showAuthor && showReadingTime && (
|
|
252
|
+
<span class="cg-featured-card-separator">•</span>
|
|
253
|
+
)}
|
|
254
|
+
|
|
255
|
+
{showReadingTime && (
|
|
256
|
+
<span class="cg-featured-card-reading-time">
|
|
257
|
+
{Math.ceil(article.wordCount / 200)} min read
|
|
258
|
+
</span>
|
|
259
|
+
)}
|
|
260
|
+
</div>
|
|
261
|
+
)}
|
|
262
|
+
|
|
263
|
+
{/* Read more indicator */}
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
{/* Right Panel - Structured Visual Items (List/Quote) */}
|
|
267
|
+
{summaryData && (summaryData.type === 'list' || summaryData.type === 'steps' || summaryData.type === 'quote') && (
|
|
268
|
+
<div class="cg-card-secondary">
|
|
269
|
+
{(summaryData.type === 'list' || summaryData.type === 'steps') && (
|
|
270
|
+
<ul class="cg-summary-items">
|
|
271
|
+
{summaryData.items?.map((item, index) => (
|
|
272
|
+
<li>
|
|
273
|
+
<span class="cg-item-number">{index + 1}</span>
|
|
274
|
+
<div class="cg-item-content">
|
|
275
|
+
<strong class="cg-item-title" set:html={renderInlineMarkdownJS(item.title)} />
|
|
276
|
+
<span class="cg-item-description" set:html={renderInlineMarkdownJS(item.description)} />
|
|
277
|
+
</div>
|
|
278
|
+
</li>
|
|
279
|
+
))}
|
|
280
|
+
</ul>
|
|
206
281
|
)}
|
|
207
282
|
|
|
208
|
-
{
|
|
209
|
-
<
|
|
210
|
-
{Math.ceil(article.wordCount / 200)} min read
|
|
211
|
-
</span>
|
|
283
|
+
{summaryData.type === 'quote' && (
|
|
284
|
+
<blockquote set:html={renderInlineMarkdownJS(summaryData.quote || '')} />
|
|
212
285
|
)}
|
|
213
286
|
</div>
|
|
214
287
|
)}
|
|
215
|
-
|
|
216
|
-
{/* Read more indicator */}
|
|
288
|
+
|
|
289
|
+
{/* Read more indicator - Now at bottom */}
|
|
217
290
|
<div class="cg-featured-card-cta">
|
|
218
291
|
<span>{ctaText}</span>
|
|
219
292
|
<svg class="cg-featured-card-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24" width="16" height="16">
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC"}
|
package/dist/core/index.js
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders inline markdown syntax (bold and italic only) to HTML.
|
|
3
|
+
* Supports: **bold**, *italic*, ***bold italic***
|
|
4
|
+
*
|
|
5
|
+
* @param text - The text to parse
|
|
6
|
+
* @returns HTML string with <strong> and <em> tags
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderInlineMarkdown(text: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Plain JavaScript version for use in Astro components
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderInlineMarkdownJS(text: string): string;
|
|
13
|
+
//# sourceMappingURL=inline-markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-markdown.d.ts","sourceRoot":"","sources":["../../src/core/inline-markdown.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUzD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO3D"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders inline markdown syntax (bold and italic only) to HTML.
|
|
3
|
+
* Supports: **bold**, *italic*, ***bold italic***
|
|
4
|
+
*
|
|
5
|
+
* @param text - The text to parse
|
|
6
|
+
* @returns HTML string with <strong> and <em> tags
|
|
7
|
+
*/
|
|
8
|
+
export function renderInlineMarkdown(text) {
|
|
9
|
+
if (!text)
|
|
10
|
+
return '';
|
|
11
|
+
return text
|
|
12
|
+
// Bold + Italic: ***text*** -> <strong><em>text</em></strong>
|
|
13
|
+
.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>')
|
|
14
|
+
// Bold: **text** -> <strong>text</strong>
|
|
15
|
+
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
|
16
|
+
// Italic: *text* -> <em>text</em>
|
|
17
|
+
.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Plain JavaScript version for use in Astro components
|
|
21
|
+
*/
|
|
22
|
+
export function renderInlineMarkdownJS(text) {
|
|
23
|
+
if (!text)
|
|
24
|
+
return '';
|
|
25
|
+
return text
|
|
26
|
+
.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>')
|
|
27
|
+
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
|
28
|
+
.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
|
29
|
+
}
|
|
@@ -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;AAGnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;
|
|
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;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,28 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import { marked } from 'marked';
|
|
3
2
|
import { ContentGrowthClient } from '../core/client';
|
|
4
|
-
|
|
3
|
+
import { renderInlineMarkdown } from '../core/inline-markdown';
|
|
4
|
+
// Parse featured summary - supports both JSON (new) and plain text (legacy)
|
|
5
|
+
const parseSummary = (article) => {
|
|
6
|
+
const summaryText = article.featuredSummary || article.summary;
|
|
7
|
+
if (!summaryText)
|
|
8
|
+
return null;
|
|
9
|
+
// Try to parse as JSON
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(summaryText);
|
|
12
|
+
if (parsed.type) {
|
|
13
|
+
return parsed;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
// Not JSON, treat as legacy markdown/plain text
|
|
18
|
+
}
|
|
19
|
+
// Legacy fallback - render as plain text
|
|
20
|
+
return {
|
|
21
|
+
type: 'legacy',
|
|
22
|
+
text: summaryText
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
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
26
|
const [article, setArticle] = useState(providedArticle || null);
|
|
6
27
|
const [loading, setLoading] = useState(!providedArticle);
|
|
7
28
|
const [error, setError] = useState(null);
|
|
@@ -56,13 +77,6 @@ export const FeaturedCard = ({ apiKey, baseUrl, article: providedArticle, slug,
|
|
|
56
77
|
.replace('{slug}', article.slug || article.uuid || '')
|
|
57
78
|
.replace('{category}', article.category || 'uncategorized');
|
|
58
79
|
};
|
|
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
80
|
if (loading) {
|
|
67
81
|
return (React.createElement("div", { className: `cg-widget cg-loading ${className}` },
|
|
68
82
|
React.createElement("div", { className: "cg-spinner" })));
|
|
@@ -70,7 +84,6 @@ export const FeaturedCard = ({ apiKey, baseUrl, article: providedArticle, slug,
|
|
|
70
84
|
if (error || !article) {
|
|
71
85
|
return (React.createElement("div", { className: `cg-widget cg-error ${className}` }, error || 'No featured content found'));
|
|
72
86
|
}
|
|
73
|
-
const summaryHtml = getSummaryHtml(article);
|
|
74
87
|
const layout = propLayout || article.featuredSummaryLayout || 'vertical';
|
|
75
88
|
const readingTime = Math.ceil(article.wordCount / 200);
|
|
76
89
|
const borderClass = borderStyle !== 'none' ? `cg-border-${borderStyle}` : '';
|
|
@@ -84,18 +97,52 @@ export const FeaturedCard = ({ apiKey, baseUrl, article: providedArticle, slug,
|
|
|
84
97
|
customStyles['--cg-card-bg'] = cardBackground;
|
|
85
98
|
if (itemsBackground !== '#f3f4f6')
|
|
86
99
|
customStyles['--cg-items-bg'] = itemsBackground;
|
|
100
|
+
if (padding)
|
|
101
|
+
customStyles['--cg-card-padding'] = padding;
|
|
87
102
|
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
103
|
React.createElement("article", { className: "cg-featured-card-inner" },
|
|
89
|
-
|
|
90
|
-
React.createElement("
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
104
|
+
React.createElement("div", { className: "cg-card-primary" },
|
|
105
|
+
showCategory && article.category && (React.createElement("div", { className: "cg-featured-card-category" },
|
|
106
|
+
React.createElement("span", { className: "cg-category-badge" }, article.category))),
|
|
107
|
+
React.createElement("h3", { className: "cg-featured-card-title" }, article.title),
|
|
108
|
+
parseSummary(article) && (React.createElement("div", { className: "cg-featured-card-summary" }, (() => {
|
|
109
|
+
const summaryData = parseSummary(article);
|
|
110
|
+
if (summaryData.type === 'classic') {
|
|
111
|
+
return React.createElement("p", { dangerouslySetInnerHTML: { __html: renderInlineMarkdown(summaryData.text || '') } });
|
|
112
|
+
}
|
|
113
|
+
if ((summaryData.type === 'list' || summaryData.type === 'steps') && summaryData.intro) {
|
|
114
|
+
return React.createElement("p", { dangerouslySetInnerHTML: { __html: renderInlineMarkdown(summaryData.intro) } });
|
|
115
|
+
}
|
|
116
|
+
if (summaryData.type === 'quote' && summaryData.quote) {
|
|
117
|
+
return React.createElement("p", { dangerouslySetInnerHTML: { __html: renderInlineMarkdown(summaryData.intro || '') } });
|
|
118
|
+
}
|
|
119
|
+
// Legacy
|
|
120
|
+
return React.createElement("p", null, summaryData.text);
|
|
121
|
+
})())),
|
|
122
|
+
(showAuthor || showReadingTime) && (React.createElement("div", { className: "cg-featured-card-footer" },
|
|
123
|
+
showAuthor && React.createElement("span", { className: "cg-featured-card-author" }, article.authorName),
|
|
124
|
+
showAuthor && showReadingTime && (React.createElement("span", { className: "cg-featured-card-separator" }, "\u2022")),
|
|
125
|
+
showReadingTime && (React.createElement("span", { className: "cg-featured-card-reading-time" },
|
|
126
|
+
readingTime,
|
|
127
|
+
" min read"))))),
|
|
128
|
+
(() => {
|
|
129
|
+
const summaryData = parseSummary(article);
|
|
130
|
+
if (!summaryData)
|
|
131
|
+
return null;
|
|
132
|
+
if ((summaryData.type === 'list' || summaryData.type === 'steps') && summaryData.items) {
|
|
133
|
+
return (React.createElement("div", { className: "cg-card-secondary" },
|
|
134
|
+
React.createElement("ul", { className: "cg-summary-items" }, summaryData.items.map((item, index) => (React.createElement("li", { key: index },
|
|
135
|
+
React.createElement("span", { className: "cg-item-number" }, index + 1),
|
|
136
|
+
React.createElement("div", { className: "cg-item-content" },
|
|
137
|
+
React.createElement("strong", { className: "cg-item-title", dangerouslySetInnerHTML: { __html: renderInlineMarkdown(item.title) } }),
|
|
138
|
+
React.createElement("span", { className: "cg-item-description", dangerouslySetInnerHTML: { __html: renderInlineMarkdown(item.description) } }))))))));
|
|
139
|
+
}
|
|
140
|
+
if (summaryData.type === 'quote' && summaryData.quote) {
|
|
141
|
+
return (React.createElement("div", { className: "cg-card-secondary" },
|
|
142
|
+
React.createElement("blockquote", { dangerouslySetInnerHTML: { __html: renderInlineMarkdown(summaryData.quote) } })));
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
})(),
|
|
99
146
|
React.createElement("div", { className: "cg-featured-card-cta" },
|
|
100
147
|
React.createElement("span", null, ctaText),
|
|
101
148
|
React.createElement("svg", { className: "cg-featured-card-arrow", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", width: "16", height: "16" },
|