@contentgrowth/content-widget 1.1.0 → 1.1.2
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/dist/astro/ContentViewer.astro +38 -38
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +0 -7
- package/dist/react/ContentViewer.js +7 -7
- package/dist/styles.css +29 -29
- package/dist/vue/ContentViewer.vue +7 -7
- package/dist/widget/widget.css +1 -0
- package/dist/widget/widget.dev.css +970 -0
- package/dist/widget/widget.dev.js +3805 -0
- package/dist/widget/widget.js +1 -240
- package/package.json +5 -2
- package/dist/widget/content-card.js +0 -190
- package/dist/widget/content-list.js +0 -289
- package/dist/widget/content-viewer.js +0 -230
- package/dist/widget/index.js +0 -40
- package/dist/widget/utils/api-client.js +0 -154
- package/dist/widget/utils/helpers.js +0 -71
- package/dist/widget/widget-js/content-card.js +0 -190
- package/dist/widget/widget-js/content-list.js +0 -289
- package/dist/widget/widget-js/content-viewer.js +0 -230
- package/dist/widget/widget-js/index.js +0 -40
- package/dist/widget/widget-js/utils/api-client.js +0 -154
- package/dist/widget/widget-js/utils/helpers.js +0 -71
- package/dist/widget/widget-js/widget.d.ts +0 -24
- package/dist/widget/widget-js/widget.js +0 -240
- package/dist/widget/widget.d.ts +0 -24
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Content List Component
|
|
3
|
-
* Displays a list of content items with pagination
|
|
4
|
-
*/
|
|
5
|
-
import { ContentCard } from './content-card.js';
|
|
6
|
-
|
|
7
|
-
export class ContentList {
|
|
8
|
-
constructor(container, api, options = {}) {
|
|
9
|
-
console.log('[ContentList] Constructor called with options:', options);
|
|
10
|
-
this.container = container;
|
|
11
|
-
this.api = api;
|
|
12
|
-
this.options = {
|
|
13
|
-
layoutMode: options.layoutMode || 'cards', // 'cards' or 'rows'
|
|
14
|
-
displayMode: options.displayMode || 'comfortable',
|
|
15
|
-
pageSize: parseInt(options.pageSize) || 12,
|
|
16
|
-
tags: options.tags || [],
|
|
17
|
-
viewerMode: options.viewerMode || 'inline', // 'inline' | 'modal' | 'external'
|
|
18
|
-
externalUrlPattern: options.externalUrlPattern || '/article/{id}',
|
|
19
|
-
externalTarget: options.externalTarget || 'article-{id}',
|
|
20
|
-
onArticleClick: options.onArticleClick || null
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
console.log('[ContentList] Final options:', this.options);
|
|
24
|
-
|
|
25
|
-
this.currentPage = 1;
|
|
26
|
-
this.totalPages = 1;
|
|
27
|
-
this.articles = [];
|
|
28
|
-
this.loading = false;
|
|
29
|
-
this.expandedCards = new Set();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Initialize and render the list
|
|
34
|
-
*/
|
|
35
|
-
async init() {
|
|
36
|
-
console.log('[ContentList] Initializing...');
|
|
37
|
-
this.render();
|
|
38
|
-
await this.loadArticles();
|
|
39
|
-
console.log('[ContentList] Initialization complete');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Render the list container
|
|
44
|
-
*/
|
|
45
|
-
render() {
|
|
46
|
-
const layoutClass = this.options.layoutMode === 'rows' ? 'cg-content-rows' : 'cg-content-grid';
|
|
47
|
-
|
|
48
|
-
this.container.innerHTML = `
|
|
49
|
-
<div class="cg-content-list">
|
|
50
|
-
<div class="cg-list-header">
|
|
51
|
-
<button class="cg-display-toggle" title="Toggle display mode">
|
|
52
|
-
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
|
53
|
-
<rect x="2" y="2" width="16" height="4" rx="1" stroke="currentColor" stroke-width="1.5"/>
|
|
54
|
-
<rect x="2" y="8" width="16" height="4" rx="1" stroke="currentColor" stroke-width="1.5"/>
|
|
55
|
-
<rect x="2" y="14" width="16" height="4" rx="1" stroke="currentColor" stroke-width="1.5"/>
|
|
56
|
-
</svg>
|
|
57
|
-
<span>${this.options.displayMode === 'compact' ? 'Show summaries' : 'Hide summaries'}</span>
|
|
58
|
-
</button>
|
|
59
|
-
</div>
|
|
60
|
-
<div class="${layoutClass}"></div>
|
|
61
|
-
<div class="cg-pagination"></div>
|
|
62
|
-
</div>
|
|
63
|
-
`;
|
|
64
|
-
|
|
65
|
-
// Add toggle handler
|
|
66
|
-
const toggle = this.container.querySelector('.cg-display-toggle');
|
|
67
|
-
toggle.addEventListener('click', () => this.toggleDisplayMode());
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Load articles from API
|
|
72
|
-
*/
|
|
73
|
-
async loadArticles(page = 1) {
|
|
74
|
-
if (this.loading) {
|
|
75
|
-
console.log('[ContentList] Already loading, skipping...');
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
console.log('[ContentList] Loading articles for page:', page);
|
|
80
|
-
this.loading = true;
|
|
81
|
-
this.showLoading();
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
console.log('[ContentList] Calling api.fetchArticles with:', {
|
|
85
|
-
page,
|
|
86
|
-
limit: this.options.pageSize,
|
|
87
|
-
tags: this.options.tags
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const data = await this.api.fetchArticles({
|
|
91
|
-
page,
|
|
92
|
-
limit: this.options.pageSize,
|
|
93
|
-
tags: this.options.tags
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
console.log('[ContentList] Received data:', data);
|
|
97
|
-
|
|
98
|
-
this.articles = data.articles || [];
|
|
99
|
-
this.currentPage = data.pagination?.page || 1;
|
|
100
|
-
this.totalPages = data.pagination?.totalPages || 1;
|
|
101
|
-
|
|
102
|
-
console.log('[ContentList] Loaded', this.articles.length, 'articles');
|
|
103
|
-
|
|
104
|
-
this.renderArticles();
|
|
105
|
-
this.renderPagination();
|
|
106
|
-
} catch (error) {
|
|
107
|
-
console.error('[ContentList] Error loading articles:', error);
|
|
108
|
-
this.showError('Failed to load articles. Please try again.');
|
|
109
|
-
} finally {
|
|
110
|
-
this.loading = false;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Render content grid
|
|
116
|
-
*/
|
|
117
|
-
renderArticles() {
|
|
118
|
-
const grid = this.container.querySelector('.cg-content-grid, .cg-content-rows');
|
|
119
|
-
|
|
120
|
-
if (this.articles.length === 0) {
|
|
121
|
-
grid.innerHTML = `
|
|
122
|
-
<div class="cg-empty-state">
|
|
123
|
-
<svg width="64" height="64" viewBox="0 0 64 64" fill="none">
|
|
124
|
-
<rect x="8" y="12" width="48" height="40" rx="4" stroke="currentColor" stroke-width="2"/>
|
|
125
|
-
<path d="M16 24H48M16 32H48M16 40H32" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
126
|
-
</svg>
|
|
127
|
-
<p>No articles found</p>
|
|
128
|
-
</div>
|
|
129
|
-
`;
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
grid.innerHTML = '';
|
|
134
|
-
|
|
135
|
-
this.articles.forEach(article => {
|
|
136
|
-
const isExpanded = this.expandedCards.has(article.uuid);
|
|
137
|
-
const card = new ContentCard(article, {
|
|
138
|
-
displayMode: isExpanded ? 'expanded' : this.options.displayMode,
|
|
139
|
-
viewerMode: this.options.viewerMode,
|
|
140
|
-
externalUrlPattern: this.options.externalUrlPattern,
|
|
141
|
-
externalTarget: this.options.externalTarget,
|
|
142
|
-
onExpand: (article, cardElement) => this.handleExpand(article, cardElement),
|
|
143
|
-
onClick: (article) => this.handleArticleClick(article)
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
grid.appendChild(card.render());
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Handle expand/collapse of a card
|
|
152
|
-
*/
|
|
153
|
-
handleExpand(article, cardElement) {
|
|
154
|
-
const isExpanded = this.expandedCards.has(article.uuid);
|
|
155
|
-
|
|
156
|
-
if (isExpanded) {
|
|
157
|
-
this.expandedCards.delete(article.uuid);
|
|
158
|
-
} else {
|
|
159
|
-
this.expandedCards.add(article.uuid);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Re-render just this card
|
|
163
|
-
const newCard = new ContentCard(article, {
|
|
164
|
-
displayMode: isExpanded ? this.options.displayMode : 'expanded',
|
|
165
|
-
viewerMode: this.options.viewerMode,
|
|
166
|
-
externalUrlPattern: this.options.externalUrlPattern,
|
|
167
|
-
externalTarget: this.options.externalTarget,
|
|
168
|
-
onExpand: (article, cardElement) => this.handleExpand(article, cardElement),
|
|
169
|
-
onClick: (article) => this.handleArticleClick(article)
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
cardElement.replaceWith(newCard.render());
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Handle article click
|
|
177
|
-
*/
|
|
178
|
-
handleArticleClick(article) {
|
|
179
|
-
if (this.options.onArticleClick) {
|
|
180
|
-
this.options.onArticleClick(article);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Toggle display mode for all cards
|
|
186
|
-
*/
|
|
187
|
-
toggleDisplayMode() {
|
|
188
|
-
this.options.displayMode = this.options.displayMode === 'compact' ? 'expanded' : 'compact';
|
|
189
|
-
this.expandedCards.clear(); // Reset individual expansions
|
|
190
|
-
this.renderArticles();
|
|
191
|
-
|
|
192
|
-
// Update button text
|
|
193
|
-
const toggle = this.container.querySelector('.cg-display-toggle span');
|
|
194
|
-
toggle.textContent = this.options.displayMode === 'compact' ? 'Show summaries' : 'Hide summaries';
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Render pagination controls
|
|
199
|
-
*/
|
|
200
|
-
renderPagination() {
|
|
201
|
-
const pagination = this.container.querySelector('.cg-pagination');
|
|
202
|
-
|
|
203
|
-
if (this.totalPages <= 1) {
|
|
204
|
-
pagination.innerHTML = '';
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const prevDisabled = this.currentPage === 1;
|
|
209
|
-
const nextDisabled = this.currentPage === this.totalPages;
|
|
210
|
-
|
|
211
|
-
pagination.innerHTML = `
|
|
212
|
-
<button class="cg-btn-prev" ${prevDisabled ? 'disabled' : ''}>
|
|
213
|
-
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
214
|
-
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
215
|
-
</svg>
|
|
216
|
-
Previous
|
|
217
|
-
</button>
|
|
218
|
-
<span class="cg-page-info">Page ${this.currentPage} of ${this.totalPages}</span>
|
|
219
|
-
<button class="cg-btn-next" ${nextDisabled ? 'disabled' : ''}>
|
|
220
|
-
Next
|
|
221
|
-
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
222
|
-
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
223
|
-
</svg>
|
|
224
|
-
</button>
|
|
225
|
-
`;
|
|
226
|
-
|
|
227
|
-
// Add event listeners
|
|
228
|
-
const prevBtn = pagination.querySelector('.cg-btn-prev');
|
|
229
|
-
const nextBtn = pagination.querySelector('.cg-btn-next');
|
|
230
|
-
|
|
231
|
-
prevBtn.addEventListener('click', () => {
|
|
232
|
-
if (this.currentPage > 1) {
|
|
233
|
-
this.loadArticles(this.currentPage - 1);
|
|
234
|
-
this.scrollToTop();
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
nextBtn.addEventListener('click', () => {
|
|
239
|
-
if (this.currentPage < this.totalPages) {
|
|
240
|
-
this.loadArticles(this.currentPage + 1);
|
|
241
|
-
this.scrollToTop();
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Show loading state
|
|
248
|
-
*/
|
|
249
|
-
showLoading() {
|
|
250
|
-
const grid = this.container.querySelector('.cg-content-grid, .cg-content-rows');
|
|
251
|
-
if (grid) {
|
|
252
|
-
grid.innerHTML = `
|
|
253
|
-
<div class="cg-loading">
|
|
254
|
-
<div class="cg-spinner"></div>
|
|
255
|
-
<p>Loading articles...</p>
|
|
256
|
-
</div>
|
|
257
|
-
`;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Show error message
|
|
263
|
-
*/
|
|
264
|
-
showError(message) {
|
|
265
|
-
const grid = this.container.querySelector('.cg-content-grid, .cg-content-rows');
|
|
266
|
-
if (grid) {
|
|
267
|
-
grid.innerHTML = `
|
|
268
|
-
<div class="cg-error">
|
|
269
|
-
<svg width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
270
|
-
<circle cx="24" cy="24" r="20" stroke="currentColor" stroke-width="2"/>
|
|
271
|
-
<path d="M24 16V26M24 32V32.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
272
|
-
</svg>
|
|
273
|
-
<p>${message}</p>
|
|
274
|
-
<button class="cg-retry-btn">Try Again</button>
|
|
275
|
-
</div>
|
|
276
|
-
`;
|
|
277
|
-
|
|
278
|
-
const retryBtn = grid.querySelector('.cg-retry-btn');
|
|
279
|
-
retryBtn.addEventListener('click', () => this.loadArticles(this.currentPage));
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Scroll to top of widget
|
|
285
|
-
*/
|
|
286
|
-
scrollToTop() {
|
|
287
|
-
this.container.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
288
|
-
}
|
|
289
|
-
}
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Content Viewer Component
|
|
3
|
-
* Displays full content with markdown rendering
|
|
4
|
-
*/
|
|
5
|
-
import { formatDate, calculateReadingTime, escapeHtml } from '../utils/helpers.js';
|
|
6
|
-
import { marked } from 'marked';
|
|
7
|
-
|
|
8
|
-
export class ContentViewer {
|
|
9
|
-
constructor(container, api, options = {}) {
|
|
10
|
-
this.container = container;
|
|
11
|
-
this.api = api;
|
|
12
|
-
this.options = {
|
|
13
|
-
displayMode: options.displayMode || 'inline', // 'inline' or 'modal'
|
|
14
|
-
showBackButton: options.showBackButton !== false, // Default true, can be disabled
|
|
15
|
-
showSummary: options.showSummary !== false, // Default true, can be disabled
|
|
16
|
-
onBack: options.onBack || null
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
this.article = null;
|
|
20
|
-
this.loading = false;
|
|
21
|
-
this.summaryExpanded = true; // Summary visible by default
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Load and display an article
|
|
26
|
-
*/
|
|
27
|
-
async loadArticle(uuid) {
|
|
28
|
-
if (this.loading) return;
|
|
29
|
-
|
|
30
|
-
this.loading = true;
|
|
31
|
-
this.showLoading();
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
this.article = await this.api.fetchArticle(uuid);
|
|
35
|
-
this.render();
|
|
36
|
-
} catch (error) {
|
|
37
|
-
this.showError('Failed to load article. Please try again.');
|
|
38
|
-
console.error(error);
|
|
39
|
-
} finally {
|
|
40
|
-
this.loading = false;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Render the article
|
|
46
|
-
*/
|
|
47
|
-
render() {
|
|
48
|
-
if (!this.article) return;
|
|
49
|
-
|
|
50
|
-
const readingTime = calculateReadingTime(this.article.wordCount || this.article.content);
|
|
51
|
-
const content = this.renderMarkdown(this.article.content || '');
|
|
52
|
-
|
|
53
|
-
this.container.innerHTML = `
|
|
54
|
-
<div class="cg-content-viewer">
|
|
55
|
-
${this.options.showBackButton ? `
|
|
56
|
-
<div class="cg-viewer-header">
|
|
57
|
-
<button class="cg-back-btn">
|
|
58
|
-
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
|
59
|
-
<path d="M12 16L6 10L12 4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
60
|
-
</svg>
|
|
61
|
-
Back to list
|
|
62
|
-
</button>
|
|
63
|
-
</div>
|
|
64
|
-
` : ''}
|
|
65
|
-
|
|
66
|
-
<article class="cg-viewer-content">
|
|
67
|
-
<header class="cg-post-title-section">
|
|
68
|
-
<h1 class="cg-post-title">${escapeHtml(this.article.title)}</h1>
|
|
69
|
-
|
|
70
|
-
${this.options.showSummary && this.article.summary && this.article.category !== 'announce' ? `
|
|
71
|
-
<div class="cg-ai-summary ${this.summaryExpanded ? 'expanded' : 'collapsed'}">
|
|
72
|
-
<div class="cg-ai-summary-header">
|
|
73
|
-
<div class="cg-ai-summary-label">
|
|
74
|
-
<svg class="cg-ai-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
75
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
|
76
|
-
</svg>
|
|
77
|
-
<span>AI Generated Summary</span>
|
|
78
|
-
</div>
|
|
79
|
-
<button class="cg-summary-toggle" aria-label="Toggle summary">
|
|
80
|
-
<svg class="cg-chevron" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor">
|
|
81
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6l4 4 4-4" />
|
|
82
|
-
</svg>
|
|
83
|
-
</button>
|
|
84
|
-
</div>
|
|
85
|
-
<div class="cg-ai-summary-content">
|
|
86
|
-
<p>${escapeHtml(this.article.summary)}</p>
|
|
87
|
-
</div>
|
|
88
|
-
</div>
|
|
89
|
-
` : ''}
|
|
90
|
-
|
|
91
|
-
<div class="cg-post-meta">
|
|
92
|
-
<span class="cg-meta-item">
|
|
93
|
-
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
94
|
-
<path d="M8 8C9.65685 8 11 6.65685 11 5C11 3.34315 9.65685 2 8 2C6.34315 2 5 3.34315 5 5C5 6.65685 6.34315 8 8 8Z" stroke="currentColor" stroke-width="1.5"/>
|
|
95
|
-
<path d="M14 14C14 11.7909 11.3137 10 8 10C4.68629 10 2 11.7909 2 14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
96
|
-
</svg>
|
|
97
|
-
${escapeHtml(this.article.authorName)}
|
|
98
|
-
</span>
|
|
99
|
-
<span class="cg-meta-item">
|
|
100
|
-
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
101
|
-
<rect x="2" y="3" width="12" height="11" rx="2" stroke="currentColor" stroke-width="1.5"/>
|
|
102
|
-
<path d="M5 2V4M11 2V4M2 6H14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
103
|
-
</svg>
|
|
104
|
-
${formatDate(this.article.publishedAt)}
|
|
105
|
-
</span>
|
|
106
|
-
<span class="cg-meta-item">
|
|
107
|
-
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
108
|
-
<circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="1.5"/>
|
|
109
|
-
<path d="M8 4V8L10.5 10.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
110
|
-
</svg>
|
|
111
|
-
${readingTime}
|
|
112
|
-
</span>
|
|
113
|
-
</div>
|
|
114
|
-
</header>
|
|
115
|
-
|
|
116
|
-
<div class="cg-post-body">
|
|
117
|
-
${content}
|
|
118
|
-
</div>
|
|
119
|
-
</article>
|
|
120
|
-
</div>
|
|
121
|
-
`;
|
|
122
|
-
|
|
123
|
-
// Add back button handler if button exists
|
|
124
|
-
if (this.options.showBackButton) {
|
|
125
|
-
const backBtn = this.container.querySelector('.cg-back-btn');
|
|
126
|
-
if (backBtn) {
|
|
127
|
-
backBtn.addEventListener('click', () => this.handleBack());
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Add summary toggle handler
|
|
132
|
-
const summaryToggle = this.container.querySelector('.cg-summary-toggle');
|
|
133
|
-
if (summaryToggle) {
|
|
134
|
-
summaryToggle.addEventListener('click', () => this.toggleSummary());
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Toggle AI summary visibility
|
|
140
|
-
*/
|
|
141
|
-
toggleSummary() {
|
|
142
|
-
this.summaryExpanded = !this.summaryExpanded;
|
|
143
|
-
const summaryEl = this.container.querySelector('.cg-ai-summary');
|
|
144
|
-
if (summaryEl) {
|
|
145
|
-
if (this.summaryExpanded) {
|
|
146
|
-
summaryEl.classList.add('expanded');
|
|
147
|
-
summaryEl.classList.remove('collapsed');
|
|
148
|
-
} else {
|
|
149
|
-
summaryEl.classList.add('collapsed');
|
|
150
|
-
summaryEl.classList.remove('expanded');
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Render markdown content to HTML
|
|
157
|
-
*/
|
|
158
|
-
renderMarkdown(markdown) {
|
|
159
|
-
// Configure marked
|
|
160
|
-
marked.setOptions({
|
|
161
|
-
breaks: true,
|
|
162
|
-
gfm: true,
|
|
163
|
-
headerIds: true,
|
|
164
|
-
mangle: false
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
return marked.parse(markdown);
|
|
169
|
-
} catch (error) {
|
|
170
|
-
console.error('Markdown parsing error:', error);
|
|
171
|
-
return `<p>${escapeHtml(markdown)}</p>`;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Handle back button click
|
|
177
|
-
*/
|
|
178
|
-
handleBack() {
|
|
179
|
-
if (this.options.onBack) {
|
|
180
|
-
this.options.onBack();
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Show loading state
|
|
186
|
-
*/
|
|
187
|
-
showLoading() {
|
|
188
|
-
this.container.innerHTML = `
|
|
189
|
-
<div class="cg-content-viewer">
|
|
190
|
-
<div class="cg-viewer-loading">
|
|
191
|
-
<div class="cg-spinner"></div>
|
|
192
|
-
<p>Loading article...</p>
|
|
193
|
-
</div>
|
|
194
|
-
</div>
|
|
195
|
-
`;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Show error message
|
|
200
|
-
*/
|
|
201
|
-
showError(message) {
|
|
202
|
-
this.container.innerHTML = `
|
|
203
|
-
<div class="cg-content-viewer">
|
|
204
|
-
<div class="cg-viewer-error">
|
|
205
|
-
<svg width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
206
|
-
<circle cx="24" cy="24" r="20" stroke="currentColor" stroke-width="2"/>
|
|
207
|
-
<path d="M24 16V26M24 32V32.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
208
|
-
</svg>
|
|
209
|
-
<p>${message}</p>
|
|
210
|
-
${this.options.showBackButton ? '<button class="cg-back-btn">Back to articles</button>' : ''}
|
|
211
|
-
</div>
|
|
212
|
-
</div>
|
|
213
|
-
`;
|
|
214
|
-
|
|
215
|
-
if (this.options.showBackButton) {
|
|
216
|
-
const backBtn = this.container.querySelector('.cg-back-btn');
|
|
217
|
-
if (backBtn) {
|
|
218
|
-
backBtn.addEventListener('click', () => this.handleBack());
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Clear the post view
|
|
225
|
-
*/
|
|
226
|
-
clear() {
|
|
227
|
-
this.container.innerHTML = '';
|
|
228
|
-
this.article = null;
|
|
229
|
-
}
|
|
230
|
-
}
|
package/dist/widget/index.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Content Growth Widget - Vanilla JavaScript
|
|
3
|
-
* Can be used in any framework or vanilla JS project
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { ContentGrowthWidget } from './widget.js';
|
|
7
|
-
import { ContentGrowthAPI } from './utils/api-client.js';
|
|
8
|
-
|
|
9
|
-
// Export main widget class
|
|
10
|
-
export { ContentGrowthWidget };
|
|
11
|
-
|
|
12
|
-
// Export API client for advanced usage
|
|
13
|
-
export { ContentGrowthAPI };
|
|
14
|
-
|
|
15
|
-
// Export helper functions
|
|
16
|
-
export * from './utils/helpers.js';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Auto-initialize widgets on page load
|
|
20
|
-
* Only runs if this is loaded as a script (not imported as module)
|
|
21
|
-
*/
|
|
22
|
-
if (typeof document !== 'undefined' && !import.meta.url.includes('node_modules')) {
|
|
23
|
-
if (document.readyState === 'loading') {
|
|
24
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
25
|
-
initializeWidgets();
|
|
26
|
-
});
|
|
27
|
-
} else {
|
|
28
|
-
initializeWidgets();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function initializeWidgets() {
|
|
33
|
-
const containers = document.querySelectorAll('[data-cg-content]');
|
|
34
|
-
containers.forEach(container => {
|
|
35
|
-
new ContentGrowthWidget(container);
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Default export for convenience
|
|
40
|
-
export default ContentGrowthWidget;
|