@feedlog-ai/webcomponents 0.0.25 → 0.0.27

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.
Files changed (51) hide show
  1. package/dist/cjs/feedlog-badge.cjs.entry.js +1 -1
  2. package/dist/cjs/feedlog-button_3.cjs.entry.js +65 -10
  3. package/dist/cjs/feedlog-card.cjs.entry.js +1 -1
  4. package/dist/cjs/feedlog-issues-client.cjs.entry.js +2 -2
  5. package/dist/cjs/feedlog-issues.cjs.entry.js +4 -4
  6. package/dist/cjs/feedlog-toolkit.cjs.js +2 -2
  7. package/dist/cjs/{index-SWDeFUqw.js → index-Bk1-M_w_.js} +48 -1
  8. package/dist/cjs/loader.cjs.js +2 -2
  9. package/dist/collection/components/feedlog-issue/feedlog-issue.css +180 -33
  10. package/dist/collection/components/feedlog-issue/feedlog-issue.js +6 -4
  11. package/dist/collection/components/feedlog-issues/feedlog-issues.css +3 -1
  12. package/dist/collection/components/feedlog-issues/feedlog-issues.js +21 -2
  13. package/dist/collection/components/feedlog-issues/feedlog-issues.stories.js +28 -0
  14. package/dist/collection/components/feedlog-issues-client/feedlog-issues-client.js +1 -1
  15. package/dist/collection/components/feedlog-issues-list/feedlog-issues-list.css +58 -0
  16. package/dist/collection/components/feedlog-issues-list/feedlog-issues-list.js +80 -2
  17. package/dist/collection/test/mocks/feedlog-core.js +10 -0
  18. package/dist/components/feedlog-issue.js +1 -1
  19. package/dist/components/feedlog-issues-client.js +1 -1
  20. package/dist/components/feedlog-issues-list.js +1 -1
  21. package/dist/components/feedlog-issues.js +1 -1
  22. package/dist/components/index.js +1 -1
  23. package/dist/components/{p-BBPFf6g7.js → p-BuX7UXwe.js} +2 -2
  24. package/dist/components/p-DMKFbFjj.js +1 -0
  25. package/dist/components/p-GzOCQT_k.js +1 -0
  26. package/dist/esm/feedlog-badge.entry.js +1 -1
  27. package/dist/esm/feedlog-button_3.entry.js +65 -10
  28. package/dist/esm/feedlog-card.entry.js +1 -1
  29. package/dist/esm/feedlog-issues-client.entry.js +2 -2
  30. package/dist/esm/feedlog-issues.entry.js +4 -4
  31. package/dist/esm/feedlog-toolkit.js +3 -3
  32. package/dist/esm/{index-B0OkcXfW.js → index-CHawAwGP.js} +48 -1
  33. package/dist/esm/loader.js +3 -3
  34. package/dist/feedlog-toolkit/feedlog-toolkit.esm.js +1 -1
  35. package/dist/feedlog-toolkit/{p-6576aefa.entry.js → p-13089dc5.entry.js} +1 -1
  36. package/dist/feedlog-toolkit/p-7f8133b3.entry.js +1 -0
  37. package/dist/feedlog-toolkit/{p-B0OkcXfW.js → p-CHawAwGP.js} +2 -2
  38. package/dist/feedlog-toolkit/{p-1e3dd1c6.entry.js → p-bc48ec0d.entry.js} +1 -1
  39. package/dist/feedlog-toolkit/{p-25d89169.entry.js → p-c974a35e.entry.js} +1 -1
  40. package/dist/feedlog-toolkit/p-f868da29.entry.js +3 -0
  41. package/dist/types/components/feedlog-issue/feedlog-issue.d.ts +2 -1
  42. package/dist/types/components/feedlog-issues/feedlog-issues.d.ts +4 -0
  43. package/dist/types/components/feedlog-issues/feedlog-issues.stories.d.ts +1 -0
  44. package/dist/types/components/feedlog-issues-list/feedlog-issues-list.d.ts +10 -0
  45. package/dist/types/components.d.ts +16 -0
  46. package/dist/types/test/mocks/feedlog-core.d.ts +5 -0
  47. package/package.json +2 -2
  48. package/dist/components/p-BR3ookRG.js +0 -1
  49. package/dist/components/p-DKGIKLzX.js +0 -1
  50. package/dist/feedlog-toolkit/p-0733934f.entry.js +0 -3
  51. package/dist/feedlog-toolkit/p-191fc966.entry.js +0 -1
@@ -149,14 +149,15 @@
149
149
 
150
150
  .issue-main {
151
151
  display: flex;
152
- align-items: flex-start;
153
- justify-content: space-between;
154
- gap: 1rem;
152
+ flex-direction: column;
153
+ gap: 0;
155
154
  }
156
155
 
157
- .issue-details {
158
- flex: 1;
159
- min-width: 0;
156
+ .issue-footer {
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: flex-end;
160
+ margin-top: 0.75rem;
160
161
  }
161
162
 
162
163
  .issue-title {
@@ -294,62 +295,208 @@
294
295
  }
295
296
 
296
297
  .upvote-button {
297
- display: flex;
298
- flex-direction: column;
298
+ display: inline-flex;
299
+ flex-direction: row;
299
300
  align-items: center;
300
- gap: 0.125rem;
301
- padding: 0.5rem 0.75rem;
302
- border-radius: 0.5rem;
303
- background-color: var(--feedlog-muted);
304
- border: 1px solid transparent;
301
+ border-radius: 0.375rem; /* 6px */
302
+ background-color: #ffffff;
303
+ border: 1px solid #e2e8f0;
305
304
  cursor: pointer;
306
- transition: all 0.2s ease;
305
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
307
306
  flex-shrink: 0;
308
- font-size: 0.75rem;
309
- font-weight: 600;
307
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
308
+ overflow: hidden;
309
+ padding: 0.375rem 0.75rem;
310
+ gap: 0.5rem;
311
+ font-family: inherit;
312
+ line-height: 1;
310
313
  }
311
314
 
312
315
  .upvote-button:hover {
313
- background-color: var(--feedlog-blue-100);
314
- border-color: var(--feedlog-blue-400);
316
+ background-color: #f8fafc;
317
+ border-color: #cbd5e1;
318
+ transform: translateY(-1px);
319
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -2px rgba(0, 0, 0, 0.04);
320
+ }
321
+
322
+ .upvote-button:active {
323
+ transform: translateY(0);
324
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
315
325
  }
316
326
 
317
327
  .upvote-button.upvoted {
318
- background-color: var(--feedlog-red-100);
319
- border-color: var(--feedlog-red-400);
328
+ background-color: #eff6ff;
329
+ border-color: #bfdbfe;
320
330
  }
321
331
 
322
332
  .upvote-button.upvoted:hover {
323
- background-color: var(--feedlog-red-100);
324
- border-color: var(--feedlog-red-600);
333
+ background-color: #dbeafe;
334
+ border-color: #93c5fd;
325
335
  }
326
336
 
327
- :host(.dark) .upvote-button:hover {
328
- background-color: var(--feedlog-blue-900-30);
329
- border-color: var(--feedlog-blue-600);
337
+ .upvote-action {
338
+ display: flex;
339
+ flex-direction: row;
340
+ align-items: center;
341
+ gap: 0.375rem;
342
+ color: #475569;
343
+ font-size: 0.875rem;
344
+ font-weight: 600;
345
+ transition: color 0.2s ease;
330
346
  }
331
347
 
332
- :host(.dark) .upvote-button.upvoted {
333
- background-color: var(--feedlog-red-900-30);
334
- border-color: var(--feedlog-red-600);
348
+ .upvote-button:hover .upvote-action {
349
+ color: #0f172a;
350
+ }
351
+
352
+ .upvote-button.upvoted .upvote-action {
353
+ color: #2563eb;
354
+ }
355
+
356
+ .upvote-button.upvoted:hover .upvote-action {
357
+ color: #1d4ed8;
335
358
  }
336
359
 
337
360
  .upvote-icon {
338
- width: 1rem;
339
- height: 1rem;
361
+ width: 1.125rem;
362
+ height: 1.125rem;
363
+ min-width: 1.125rem;
364
+ min-height: 1.125rem;
340
365
  color: inherit;
366
+ flex-shrink: 0;
367
+ display: block;
368
+ vertical-align: middle;
369
+ transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
370
+ }
371
+
372
+ .upvote-button:hover .upvote-icon {
373
+ transform: translateY(-2px);
374
+ }
375
+
376
+ .upvote-button:active .upvote-icon {
377
+ transform: translateY(0) scale(0.9);
341
378
  }
342
379
 
343
380
  .upvote-icon.filled {
344
- color: var(--feedlog-upvote-icon-filled-color);
381
+ color: inherit;
345
382
  }
346
383
 
347
384
  .upvote-icon.outline {
348
- color: var(--feedlog-upvote-icon-color);
385
+ color: inherit;
386
+ }
387
+
388
+ .reel-container {
389
+ position: relative;
390
+ display: inline-flex;
391
+ align-items: center;
392
+ height: 1.25rem;
393
+ overflow: hidden;
394
+ border-left: 1px solid #e2e8f0;
395
+ padding-left: 0.5rem;
396
+ transition: border-color 0.2s ease;
397
+ }
398
+
399
+ .upvote-button:hover .reel-container {
400
+ border-color: #cbd5e1;
401
+ }
402
+
403
+ .upvote-button.upvoted .reel-container {
404
+ border-color: #bfdbfe;
405
+ }
406
+
407
+ .upvote-button.upvoted:hover .reel-container {
408
+ border-color: #93c5fd;
349
409
  }
350
410
 
351
411
  .upvote-count {
352
- font-size: 0.75rem;
412
+ font-size: 0.875rem;
353
413
  font-weight: 600;
414
+ color: #475569;
415
+ transition: color 0.2s ease;
416
+ line-height: 1.25rem;
417
+ }
418
+
419
+ .upvote-button:hover .upvote-count {
420
+ color: #0f172a;
421
+ }
422
+
423
+ .upvote-button.upvoted .upvote-count {
424
+ color: #2563eb;
425
+ }
426
+
427
+ .upvote-button.upvoted:hover .upvote-count {
428
+ color: #1d4ed8;
429
+ }
430
+
431
+ .reel-number {
432
+ display: inline-block;
433
+ animation: reelIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
434
+ }
435
+
436
+ @keyframes reelIn {
437
+ 0% {
438
+ transform: translateY(100%);
439
+ opacity: 0;
440
+ }
441
+ 100% {
442
+ transform: translateY(0);
443
+ opacity: 1;
444
+ }
445
+ }
446
+
447
+ /* Dark theme overrides */
448
+ :host(.dark) .upvote-button {
449
+ background-color: var(--feedlog-card);
450
+ border-color: var(--feedlog-border);
451
+ }
452
+
453
+ :host(.dark) .upvote-button:hover {
454
+ background-color: var(--feedlog-muted);
455
+ border-color: var(--feedlog-muted-foreground);
456
+ }
457
+
458
+ :host(.dark) .upvote-button.upvoted {
459
+ background-color: rgba(37, 99, 235, 0.15);
460
+ border-color: rgba(37, 99, 235, 0.3);
461
+ }
462
+
463
+ :host(.dark) .upvote-button.upvoted:hover {
464
+ background-color: rgba(37, 99, 235, 0.25);
465
+ border-color: rgba(37, 99, 235, 0.4);
466
+ }
467
+
468
+ :host(.dark) .upvote-action,
469
+ :host(.dark) .upvote-count {
470
+ color: var(--feedlog-muted-foreground);
471
+ }
472
+
473
+ :host(.dark) .upvote-button:hover .upvote-action,
474
+ :host(.dark) .upvote-button:hover .upvote-count {
354
475
  color: var(--feedlog-card-foreground);
355
476
  }
477
+
478
+ :host(.dark) .upvote-button.upvoted .upvote-action,
479
+ :host(.dark) .upvote-button.upvoted .upvote-count {
480
+ color: var(--feedlog-blue-400);
481
+ }
482
+
483
+ :host(.dark) .upvote-button.upvoted:hover .upvote-action,
484
+ :host(.dark) .upvote-button.upvoted:hover .upvote-count {
485
+ color: var(--feedlog-blue-300);
486
+ }
487
+
488
+ :host(.dark) .reel-container {
489
+ border-color: var(--feedlog-border);
490
+ }
491
+
492
+ :host(.dark) .upvote-button:hover .reel-container {
493
+ border-color: var(--feedlog-muted-foreground);
494
+ }
495
+
496
+ :host(.dark) .upvote-button.upvoted .reel-container {
497
+ border-color: rgba(37, 99, 235, 0.3);
498
+ }
499
+
500
+ :host(.dark) .upvote-button.upvoted:hover .reel-container {
501
+ border-color: rgba(37, 99, 235, 0.4);
502
+ }
@@ -27,13 +27,15 @@ export class FeedlogIssueComponent {
27
27
  return (h("svg", { class: "pin-icon", xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("path", { d: "M12 17v5" }), h("path", { d: "M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16h1v2a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-2h1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z" })));
28
28
  }
29
29
  /**
30
- * Renders the upvote (thumbs-up) icon SVG
30
+ * Renders the upvote (caret-up / arrow) icon SVG
31
+ * Path centered in 24x24 viewBox for proper vertical alignment
31
32
  */
32
33
  renderUpvoteIcon(filled) {
34
+ const path = 'M12 7l7 10H5l7-10z';
33
35
  if (filled) {
34
- return (h("svg", { class: "upvote-icon filled", xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor" }, h("path", { d: "M7 10v12" }), h("path", { d: "M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z" })));
36
+ return (h("svg", { class: "upvote-icon filled", xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor" }, h("path", { d: path })));
35
37
  }
36
- return (h("svg", { class: "upvote-icon outline", xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("path", { d: "M7 10v12" }), h("path", { d: "M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z" })));
38
+ return (h("svg", { class: "upvote-icon outline", xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("path", { d: path })));
37
39
  }
38
40
  /**
39
41
  * Format an ISO date string to a relative time string
@@ -102,7 +104,7 @@ export class FeedlogIssueComponent {
102
104
  const statusBadgeLabel = this.getStatusBadgeLabel();
103
105
  const githubUrl = (_b = issue.githubIssueLink) !== null && _b !== void 0 ? _b : issueUrl;
104
106
  const showGithubButton = githubUrl != null && githubUrl !== '';
105
- return (h(Host, { class: this.theme === 'dark' ? 'dark' : '', "data-upvoted": issue.hasUpvoted ? 'true' : 'false' }, h("div", { class: `issue-card issue-type-${issue.type}` }, h("div", { class: "issue-content" }, h("div", { class: "issue-header" }, h("div", { class: "issue-header-left" }, h("div", { class: "issue-type-badge" }, issue.type === 'bug' ? (h("feedlog-badge", { variant: "destructive" }, "Bug")) : (h("feedlog-badge", { variant: "enhancement" }, "Enhancement"))), statusBadgeLabel && (h("feedlog-badge", { variant: "secondary" }, statusBadgeLabel)), issue.pinnedAt && (h("div", { class: "pinned-indicator", title: "Pinned issue" }, this.renderPinIcon()))), h("span", { class: "issue-timestamp", title: timestampTitle }, timestampLabel, " ", this.formatDate(timestampDate))), h("div", { class: "issue-main" }, h("div", { class: "issue-details" }, h("h3", { class: "issue-title" }, displayTitle), issue.body != null && issue.body !== '' && (h("div", { class: "issue-body", innerHTML: parseMarkdown(issue.body) })), h("div", { class: "issue-repository" }, repoName != null && (h("span", { class: "repo-name", title: repoTooltip }, repoName)), showGithubButton && (h("a", { part: "github-link", class: "github-link", href: githubUrl, target: "_blank", rel: "noopener noreferrer", title: "View on GitHub" }, this.renderExternalLinkIcon(), h("span", { class: "github-link-text" }, "View on GitHub"))))), issue.type !== 'bug' && (h("button", { part: "upvote-button", class: `upvote-button ${issue.hasUpvoted ? 'upvoted' : ''}`, onClick: (e) => this.handleUpvote(e), title: issue.hasUpvoted ? 'Remove upvote' : 'Upvote this issue' }, h("slot", { name: "upvote-icon" }, this.renderUpvoteIcon(issue.hasUpvoted)), h("span", { class: "upvote-count" }, issue.upvoteCount))))))));
107
+ return (h(Host, { class: this.theme === 'dark' ? 'dark' : '', "data-upvoted": issue.hasUpvoted ? 'true' : 'false' }, h("div", { class: `issue-card issue-type-${issue.type}` }, h("div", { class: "issue-content" }, h("div", { class: "issue-header" }, h("div", { class: "issue-header-left" }, h("div", { class: "issue-type-badge" }, issue.type === 'bug' ? (h("feedlog-badge", { variant: "destructive" }, "Bug")) : (h("feedlog-badge", { variant: "enhancement" }, "Enhancement"))), statusBadgeLabel && (h("feedlog-badge", { variant: "secondary" }, statusBadgeLabel)), issue.pinnedAt && (h("div", { class: "pinned-indicator", title: "Pinned issue" }, this.renderPinIcon()))), h("span", { class: "issue-timestamp", title: timestampTitle }, timestampLabel, " ", this.formatDate(timestampDate))), h("div", { class: "issue-main" }, h("h3", { class: "issue-title" }, displayTitle), issue.body != null && issue.body !== '' && (h("div", { class: "issue-body", innerHTML: parseMarkdown(issue.body) })), h("div", { class: "issue-repository" }, repoName != null && (h("span", { class: "repo-name", title: repoTooltip }, repoName)), showGithubButton && (h("a", { part: "github-link", class: "github-link", href: githubUrl, target: "_blank", rel: "noopener noreferrer", title: "View on GitHub" }, this.renderExternalLinkIcon(), h("span", { class: "github-link-text" }, "View on GitHub")))), issue.type !== 'bug' && (h("div", { class: "issue-footer" }, h("button", { part: "upvote-button", class: `upvote-button ${issue.hasUpvoted ? 'upvoted' : ''}`, onClick: (e) => this.handleUpvote(e), title: issue.hasUpvoted ? 'Remove upvote' : 'Upvote this issue' }, h("div", { class: "upvote-action" }, h("slot", { name: "upvote-icon" }, this.renderUpvoteIcon(issue.hasUpvoted)), h("span", { class: "upvote-label" }, "Upvote")), h("div", { class: "reel-container" }, h("span", { class: "upvote-count reel-number", key: issue.upvoteCount }, issue.upvoteCount))))))))));
106
108
  }
107
109
  static get is() { return "feedlog-issue"; }
108
110
  static get encapsulation() { return "shadow"; }
@@ -22,6 +22,7 @@
22
22
  --feedlog-radius: 0.625rem;
23
23
  --feedlog-gap: 0.5rem;
24
24
  --feedlog-padding: 2rem;
25
+ --feedlog-min-height: 100%;
25
26
  --feedlog-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.08), 0 1px 2px -1px rgba(0, 0, 0, 0.08);
26
27
  }
27
28
 
@@ -45,11 +46,12 @@
45
46
 
46
47
 
47
48
  .issues-container {
48
- min-height: 100vh;
49
+ min-height: var(--feedlog-min-height);
49
50
  /* Parent can override via --feedlog-background (e.g. transparent); fallback to theme default */
50
51
  background-color: var(--feedlog-background, var(--feedlog-theme-bg, #ffffff));
51
52
  padding: var(--feedlog-padding);
52
53
  margin: 0 auto;
54
+ border-radius: var(--feedlog-radius);
53
55
  }
54
56
 
55
57
  .issues-header {
@@ -55,13 +55,13 @@ export class FeedlogIssues {
55
55
  }
56
56
  renderIssuesList() {
57
57
  var _a, _b;
58
- return (h("feedlog-issues-list", { issues: this.issues, theme: this.currentTheme, getIssueUrl: this.getIssueUrl, emptyStateTitle: (_a = this.emptyStateTitle) !== null && _a !== void 0 ? _a : 'No updates yet', emptyStateMessage: (_b = this.emptyStateMessage) !== null && _b !== void 0 ? _b : 'Check back later for new updates.', onFeedlogUpvote: (e) => this.handleUpvote(e) }));
58
+ return (h("feedlog-issues-list", { issues: this.issues, limit: this.limit, theme: this.currentTheme, getIssueUrl: this.getIssueUrl, emptyStateTitle: (_a = this.emptyStateTitle) !== null && _a !== void 0 ? _a : 'No updates yet', emptyStateMessage: (_b = this.emptyStateMessage) !== null && _b !== void 0 ? _b : 'Check back later for new updates.', onFeedlogUpvote: (e) => this.handleUpvote(e) }));
59
59
  }
60
60
  render() {
61
61
  const containerStyle = {
62
62
  maxWidth: this.maxWidth,
63
63
  };
64
- return (h(Host, { key: '812719c5d6f25108834d1a422dee280c18033e36', class: this.currentTheme === 'dark' ? 'dark' : '' }, h("div", { key: '1e415969878bad8fb14ea9e648d2c2d2e0d492a1', class: "issues-container", style: containerStyle }, (this.heading || this.subtitle) && (h("header", { key: '5bf1308396ff3bc04a57763317fa71a34d48cc95', class: "issues-header" }, h("div", { key: '19494458a1bcd11c3d91c08e98887f9d6ed33314', class: "header-content" }, this.heading && h("h1", { key: 'f89bd679a9dd9262c93573ef0bfbbb23bd892a87', class: "issues-title" }, this.heading), this.subtitle && h("p", { key: '2bcc73dfb95d7ae8971004522fe44cca5128701b', class: "issues-subtitle" }, this.subtitle)))), this.loading && (h("div", { key: '85c06c80b624cd16d532bea5f0ad6fb7cc06f692', class: "loading-state", role: "status", "aria-label": "Loading issues" }, h("div", { key: '766cd9e7c18586f937a1b864e7bcfce60fe79707', class: "loading-skeletons" }, [1, 2, 3].map(i => (h("div", { key: i, class: "skeleton-card" }, h("div", { class: "skeleton-content" }, h("div", { class: "skeleton-header" }, h("div", { class: "skeleton-badge" }), h("div", { class: "skeleton-timestamp" })), h("div", { class: "skeleton-main" }, h("div", { class: "skeleton-title" }), h("div", { class: "skeleton-body" }, h("div", { class: "skeleton-line" }), h("div", { class: "skeleton-line short" })), h("div", { class: "skeleton-repo" })), h("div", { class: "skeleton-footer" }, h("div", { class: "skeleton-upvote" }))))))))), this.error && (h("div", { key: '5ca08a9ddc95b4ef59d6c776a24e08b5ac65c0b3', class: "error-state", role: "alert" }, h("div", { key: '93218f816564cff58f591b53d1aad6b6bc8a1c64', class: "error-state-content" }, this.renderErrorIcon(), h("h2", { key: 'f141f75b92164af3f4d139a17937b0533641bcc0', class: "error-state-title" }, "Something went wrong"), h("p", { key: '61225c7f4f46944787d4a6448386c8209cdaf74c', class: "error-state-message" }, this.error)))), !this.loading && !this.error && (h("div", { key: '5ac699f4c5d0e5b5d20f1546efe24f80cba70748' }, this.renderIssuesList(), this.hasMore && (h("div", { key: '9ad22c48dac0d8874dfae9735c850b2ca52836e7', class: "load-more-container" }, h("feedlog-button", { key: 'de55c05ca8b80ec5df98051653ca135874edc98b', onFeedlogClick: this.handleLoadMore, disabled: this.isLoadingMore, variant: "outline" }, this.isLoadingMore ? 'Loading...' : 'Load More Issues'))))))));
64
+ return (h(Host, { key: '96686863c5eb831e64993c681c7780e035c0e2b2', class: this.currentTheme === 'dark' ? 'dark' : '' }, h("div", { key: '54d01678053f4fe58eb547f9954f497402495a83', class: "issues-container", style: containerStyle }, (this.heading || this.subtitle) && (h("header", { key: '889aa47cb4ceea60ca4a5615f512793d726059d9', class: "issues-header" }, h("div", { key: '8b4ac6a9ed0c6120ec838a1329416f3471cd2751', class: "header-content" }, this.heading && h("h1", { key: 'cfc52f28bc5131715a5c7a0b9231e440b4c0b67c', class: "issues-title" }, this.heading), this.subtitle && h("p", { key: '8b4488546ceb6e807d689b8fca2b2fd85722f824', class: "issues-subtitle" }, this.subtitle)))), this.loading && (h("div", { key: '35c382f4417893c17951b73dedb12452e805668c', class: "loading-state", role: "status", "aria-label": "Loading issues" }, h("div", { key: '2d83dd9d40bf7b2a14744ee4a715108a9cdebd60', class: "loading-skeletons" }, [1, 2, 3].map(i => (h("div", { key: i, class: "skeleton-card" }, h("div", { class: "skeleton-content" }, h("div", { class: "skeleton-header" }, h("div", { class: "skeleton-badge" }), h("div", { class: "skeleton-timestamp" })), h("div", { class: "skeleton-main" }, h("div", { class: "skeleton-title" }), h("div", { class: "skeleton-body" }, h("div", { class: "skeleton-line" }), h("div", { class: "skeleton-line short" })), h("div", { class: "skeleton-repo" })), h("div", { class: "skeleton-footer" }, h("div", { class: "skeleton-upvote" }))))))))), this.error && (h("div", { key: '43efd42ce0d213131ba97d42d1f98c52515ea9fd', class: "error-state", role: "alert" }, h("div", { key: 'ad22b0378e290556c896c628dadabf6091bbf972', class: "error-state-content" }, this.renderErrorIcon(), h("h2", { key: '9a9779f52de12aa2241ba9ddda6cf8297a4af84d', class: "error-state-title" }, "Something went wrong"), h("p", { key: 'c697040d37909d882730e5d9ab3a3b147061fc51', class: "error-state-message" }, this.error)))), !this.loading && !this.error && (h("div", { key: 'afc12552ff7ccc808c7191c1dd1e26d3b95bf2fd' }, this.renderIssuesList(), this.hasMore && (h("div", { key: '0f6c84bdeda386e4eec439fb80f4ee1606b9b559', class: "load-more-container" }, h("feedlog-button", { key: 'e6983bd889a6cdfd4a4b8a06f3a6a97c2d276243', onFeedlogClick: this.handleLoadMore, disabled: this.isLoadingMore, variant: "outline" }, this.isLoadingMore ? 'Loading...' : 'Load More Issues'))))))));
65
65
  }
66
66
  static get is() { return "feedlog-issues"; }
67
67
  static get encapsulation() { return "shadow"; }
@@ -121,6 +121,25 @@ export class FeedlogIssues {
121
121
  "attribute": "max-width",
122
122
  "defaultValue": "'42rem'"
123
123
  },
124
+ "limit": {
125
+ "type": "number",
126
+ "mutable": false,
127
+ "complexType": {
128
+ "original": "number",
129
+ "resolved": "number | undefined",
130
+ "references": {}
131
+ },
132
+ "required": false,
133
+ "optional": true,
134
+ "docs": {
135
+ "tags": [],
136
+ "text": "Page size for issues list pagination. When set, enables pagination when issues exceed this limit."
137
+ },
138
+ "getter": false,
139
+ "setter": false,
140
+ "reflect": false,
141
+ "attribute": "limit"
142
+ },
124
143
  "theme": {
125
144
  "type": "string",
126
145
  "mutable": true,
@@ -96,6 +96,7 @@ const sampleIssues = [
96
96
  hasUpvoted: false,
97
97
  },
98
98
  ];
99
+ const manyIssues = Array.from({ length: 200 }, (_, i) => (Object.assign(Object.assign({}, sampleIssues[0]), { id: `issue-${i + 1}`, title: `Issue ${i + 1}` })));
99
100
  const meta = {
100
101
  title: 'Components/Issues',
101
102
  component: 'feedlog-issues',
@@ -132,6 +133,10 @@ const meta = {
132
133
  control: 'text',
133
134
  description: 'Empty state message',
134
135
  },
136
+ limit: {
137
+ control: 'number',
138
+ description: 'Page size for pagination',
139
+ },
135
140
  },
136
141
  args: {
137
142
  issues: sampleIssues,
@@ -250,6 +255,29 @@ export const TransparentBackground = {
250
255
  }
251
256
  },
252
257
  };
258
+ export const Paginated = {
259
+ args: {
260
+ issues: manyIssues,
261
+ limit: 10,
262
+ heading: 'Community feedback',
263
+ subtitle: 'Upvote issues you care about',
264
+ },
265
+ parameters: {
266
+ docs: {
267
+ description: {
268
+ story: 'Pagination with 200 issues, 10 per page. Shows first/last pages, 3 pages around current, and prev/next arrows.',
269
+ },
270
+ },
271
+ },
272
+ render: props => h("feedlog-issues", Object.assign({}, props)),
273
+ play: async ({ canvasElement, args }) => {
274
+ const element = canvasElement.querySelector('feedlog-issues');
275
+ if (element && args.issues) {
276
+ element.issues = args.issues;
277
+ element.limit = args.limit;
278
+ }
279
+ },
280
+ };
253
281
  export const CustomCSSVars = {
254
282
  args: {
255
283
  issues: sampleIssues,
@@ -219,7 +219,7 @@ export class FeedlogIssuesClient {
219
219
  const style = hostBg
220
220
  ? { '--feedlog-background': hostBg }
221
221
  : undefined;
222
- return (h("feedlog-issues", { key: '85e92e5ad7b556d97f4d6b94e308c08070e1ff0f', style: style, issues: this.issues, maxWidth: this.maxWidth, theme: this.theme, heading: this.heading, subtitle: this.subtitle, emptyStateTitle: this.emptyStateTitle, emptyStateMessage: this.emptyStateMessage, getIssueUrl: this.getIssueUrl, loading: this.loading, error: this.error, hasMore: this.hasMore, isLoadingMore: this.isLoadingMore, onFeedlogUpvote: this.handleUpvote, onFeedlogLoadMore: async () => this.loadMore() }));
222
+ return (h("feedlog-issues", { key: '15ae96d7d7b51964026f873f8b97e10530ed02b4', style: style, issues: this.issues, limit: this.limit, maxWidth: this.maxWidth, theme: this.theme, heading: this.heading, subtitle: this.subtitle, emptyStateTitle: this.emptyStateTitle, emptyStateMessage: this.emptyStateMessage, getIssueUrl: this.getIssueUrl, loading: this.loading, error: this.error, hasMore: this.hasMore, isLoadingMore: this.isLoadingMore, onFeedlogUpvote: this.handleUpvote, onFeedlogLoadMore: async () => this.loadMore() }));
223
223
  }
224
224
  static get is() { return "feedlog-issues-client"; }
225
225
  static get encapsulation() { return "shadow"; }
@@ -34,6 +34,7 @@
34
34
  align-items: center;
35
35
  text-align: center;
36
36
  max-width: 20rem;
37
+ margin-inline: auto;
37
38
  }
38
39
 
39
40
  .empty-state-illustration {
@@ -55,3 +56,60 @@
55
56
  color: var(--feedlog-muted-foreground);
56
57
  line-height: 1.5;
57
58
  }
59
+
60
+ .pagination {
61
+ display: flex;
62
+ align-items: center;
63
+ justify-content: center;
64
+ gap: 0.25rem;
65
+ margin-top: 1.5rem;
66
+ flex-wrap: wrap;
67
+ }
68
+
69
+ .pagination-btn {
70
+ min-width: 2rem;
71
+ height: 2rem;
72
+ padding: 0 0.5rem;
73
+ color: var(--feedlog-foreground);
74
+ background: transparent;
75
+ border: 1px solid var(--feedlog-border, oklch(0.9 0 0));
76
+ border-radius: var(--feedlog-radius, 0.375rem);
77
+ font-size: 0.875rem;
78
+ font-weight: 500;
79
+ cursor: pointer;
80
+ transition: background-color 0.15s, border-color 0.15s;
81
+ }
82
+
83
+ .pagination-btn:hover:not(:disabled) {
84
+ background: var(--feedlog-muted, oklch(0.96 0 0));
85
+ }
86
+
87
+ .pagination-btn[aria-current='page'] {
88
+ background: var(--feedlog-accent-color, oklch(0.55 0.2 250));
89
+ color: white;
90
+ border-color: var(--feedlog-accent-color, oklch(0.55 0.2 250));
91
+ }
92
+
93
+ .pagination-btn:disabled {
94
+ opacity: 0.5;
95
+ cursor: not-allowed;
96
+ }
97
+
98
+ .pagination-arrow {
99
+ font-size: 1.25rem;
100
+ line-height: 1;
101
+ }
102
+
103
+ .pagination-ellipsis {
104
+ padding: 0 0.25rem;
105
+ color: var(--feedlog-muted-foreground);
106
+ font-size: 0.875rem;
107
+ }
108
+
109
+ :host(.dark) .pagination-btn {
110
+ border-color: oklch(0.4 0.02 260);
111
+ }
112
+
113
+ :host(.dark) .pagination-btn:hover:not(:disabled) {
114
+ background: oklch(0.35 0.02 260);
115
+ }
@@ -14,19 +14,64 @@ export class FeedlogIssuesList {
14
14
  * Theme variant: 'light' or 'dark'
15
15
  */
16
16
  this.theme = 'light';
17
+ this.currentPage = 1;
17
18
  this.handleUpvote = (event) => {
18
19
  event.stopPropagation();
19
20
  this.feedlogUpvote.emit(event.detail);
20
21
  };
21
22
  }
23
+ resetPage() {
24
+ this.currentPage = 1;
25
+ }
22
26
  renderEmptyStateIllustration() {
23
27
  return (h("svg", { class: "empty-state-illustration", xmlns: "http://www.w3.org/2000/svg", width: "120", height: "96", viewBox: "0 0 120 96", fill: "none", "aria-hidden": "true" }, h("path", { d: "M20 36h80v44c0 4.4-3.6 8-8 8H28c-4.4 0-8-3.6-8-8V36z", fill: "var(--feedlog-empty-illustration-bg)", stroke: "var(--feedlog-empty-illustration-stroke)", "stroke-width": "1.5", "stroke-linejoin": "round" }), h("path", { d: "M20 36l20-24h40l20 24", fill: "none", stroke: "var(--feedlog-empty-illustration-stroke)", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }), h("path", { d: "M44 52h32M44 60h24M44 68h28", stroke: "var(--feedlog-empty-illustration-muted)", "stroke-width": "1.25", "stroke-linecap": "round" })));
24
28
  }
29
+ getVisibleIssues() {
30
+ if (this.issues.length === 0)
31
+ return [];
32
+ if (this.limit == null || this.issues.length <= this.limit) {
33
+ return this.issues;
34
+ }
35
+ const offset = (this.currentPage - 1) * this.limit;
36
+ return this.issues.slice(offset, offset + this.limit);
37
+ }
38
+ getPageNumbers() {
39
+ if (this.limit == null)
40
+ return [];
41
+ const totalPages = Math.ceil(this.issues.length / this.limit);
42
+ if (totalPages <= 1)
43
+ return [];
44
+ const toShow = new Set([1, totalPages]);
45
+ const start = Math.max(1, this.currentPage - 1);
46
+ const end = Math.min(totalPages, this.currentPage + 1);
47
+ for (let i = start; i <= end; i++)
48
+ toShow.add(i);
49
+ const sorted = Array.from(toShow).sort((a, b) => a - b);
50
+ const result = [];
51
+ for (let i = 0; i < sorted.length; i++) {
52
+ if (i > 0 && sorted[i] - sorted[i - 1] > 1)
53
+ result.push('ellipsis');
54
+ result.push(sorted[i]);
55
+ }
56
+ return result;
57
+ }
58
+ goToPage(page) {
59
+ const totalPages = this.limit != null ? Math.ceil(this.issues.length / this.limit) : 1;
60
+ this.currentPage = Math.max(1, Math.min(page, totalPages));
61
+ }
62
+ renderPagination() {
63
+ if (this.limit == null || this.issues.length <= this.limit)
64
+ return null;
65
+ const totalPages = Math.ceil(this.issues.length / this.limit);
66
+ const pageNumbers = this.getPageNumbers();
67
+ return (h("nav", { class: "pagination", "aria-label": "Issues pagination" }, h("button", { type: "button", class: "pagination-btn pagination-arrow", "aria-label": "Previous page", disabled: this.currentPage <= 1, onClick: () => this.goToPage(this.currentPage - 1) }, "\u2039"), pageNumbers.map((p, i) => p === 'ellipsis' ? (h("span", { key: i, class: "pagination-ellipsis", "aria-hidden": "true" }, "\u2026")) : (h("button", { key: i, type: "button", class: "pagination-btn", "aria-current": p === this.currentPage ? 'page' : undefined, onClick: () => this.goToPage(p) }, p))), h("button", { type: "button", class: "pagination-btn pagination-arrow", "aria-label": "Next page", disabled: this.currentPage >= totalPages, onClick: () => this.goToPage(this.currentPage + 1) }, "\u203A")));
68
+ }
25
69
  render() {
26
- return (h(Host, { key: 'd797fd4978179c9081678e2fb3e7e2be756b4ea2', class: this.theme === 'dark' ? 'dark' : '' }, h("div", { key: '1fe373bbad55cf718d65279ae718f9b95e89e37f', class: "issues-list" }, this.issues.length === 0 ? (h("div", { class: "empty-state" }, this.emptyStateTitle && this.emptyStateMessage ? (h("div", { class: "empty-state-content" }, this.renderEmptyStateIllustration(), h("h2", { class: "empty-state-title" }, this.emptyStateTitle), h("p", { class: "empty-state-message" }, this.emptyStateMessage))) : (h("p", null, "No issues found")))) : (this.issues.map(issue => {
70
+ const visibleIssues = this.getVisibleIssues();
71
+ return (h(Host, { key: '474f5ca0d8edc94a8e9a03d83c6a977270391d65', class: this.theme === 'dark' ? 'dark' : '' }, h("div", { key: '8f2f8125653ec5f80b167945a33cefe483c1a001', class: "issues-list" }, visibleIssues.length === 0 ? (h("div", { class: "empty-state" }, this.emptyStateTitle && this.emptyStateMessage ? (h("div", { class: "empty-state-content" }, this.renderEmptyStateIllustration(), h("h2", { class: "empty-state-title" }, this.emptyStateTitle), h("p", { class: "empty-state-message" }, this.emptyStateMessage))) : (h("p", null, "No issues found")))) : (visibleIssues.map(issue => {
27
72
  var _a, _b;
28
73
  return (h("feedlog-issue", { key: issue.id, issue: issue, issueUrl: (_b = (_a = this.getIssueUrl) === null || _a === void 0 ? void 0 : _a.call(this, issue)) !== null && _b !== void 0 ? _b : undefined, theme: this.theme, onFeedlogUpvote: (e) => this.handleUpvote(e) }));
29
- })))));
74
+ }))), this.renderPagination()));
30
75
  }
31
76
  static get is() { return "feedlog-issues-list"; }
32
77
  static get encapsulation() { return "shadow"; }
@@ -66,6 +111,25 @@ export class FeedlogIssuesList {
66
111
  "setter": false,
67
112
  "defaultValue": "[]"
68
113
  },
114
+ "limit": {
115
+ "type": "number",
116
+ "mutable": false,
117
+ "complexType": {
118
+ "original": "number",
119
+ "resolved": "number | undefined",
120
+ "references": {}
121
+ },
122
+ "required": false,
123
+ "optional": true,
124
+ "docs": {
125
+ "tags": [],
126
+ "text": "Page size (items per page). When set, enables pagination when issues exceed this limit."
127
+ },
128
+ "getter": false,
129
+ "setter": false,
130
+ "reflect": false,
131
+ "attribute": "limit"
132
+ },
69
133
  "theme": {
70
134
  "type": "string",
71
135
  "mutable": false,
@@ -149,6 +213,11 @@ export class FeedlogIssuesList {
149
213
  }
150
214
  };
151
215
  }
216
+ static get states() {
217
+ return {
218
+ "currentPage": {}
219
+ };
220
+ }
152
221
  static get events() {
153
222
  return [{
154
223
  "method": "feedlogUpvote",
@@ -167,4 +236,13 @@ export class FeedlogIssuesList {
167
236
  }
168
237
  }];
169
238
  }
239
+ static get watchers() {
240
+ return [{
241
+ "propName": "issues",
242
+ "methodName": "resetPage"
243
+ }, {
244
+ "propName": "limit",
245
+ "methodName": "resetPage"
246
+ }];
247
+ }
170
248
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Mock for @feedlog-ai/core used in tests.
3
+ * Mapped via Stencil testing config moduleNameMapper to avoid CI resolution issues.
4
+ */
5
+ export const FeedlogSDK = jest.fn().mockImplementation(() => ({
6
+ fetchIssues: jest
7
+ .fn()
8
+ .mockResolvedValue({ issues: [], pagination: { cursor: null, hasMore: false } }),
9
+ toggleUpvote: jest.fn(),
10
+ }));
@@ -1 +1 @@
1
- import{F as o,d as s}from"./p-BBPFf6g7.js";const p=o,r=s;export{p as FeedlogIssue,r as defineCustomElement}
1
+ import{F as o,d as s}from"./p-BuX7UXwe.js";const p=o,r=s;export{p as FeedlogIssue,r as defineCustomElement}