@lifecorner/angular-ui-kit 3.21.0 → 3.22.0

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 (41) hide show
  1. package/fesm2022/lifecorner-angular-ui-kit-card.mjs +2 -2
  2. package/fesm2022/lifecorner-angular-ui-kit-card.mjs.map +1 -1
  3. package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs +3 -3
  4. package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs.map +1 -1
  5. package/fesm2022/lifecorner-angular-ui-kit-copy-field.mjs +3 -3
  6. package/fesm2022/lifecorner-angular-ui-kit-copy-field.mjs.map +1 -1
  7. package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs +11 -11
  8. package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs.map +1 -1
  9. package/fesm2022/lifecorner-angular-ui-kit-diff-viewer.mjs +2 -2
  10. package/fesm2022/lifecorner-angular-ui-kit-diff-viewer.mjs.map +1 -1
  11. package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs +2 -2
  12. package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs.map +1 -1
  13. package/fesm2022/lifecorner-angular-ui-kit-icon.mjs +2 -2
  14. package/fesm2022/lifecorner-angular-ui-kit-icon.mjs.map +1 -1
  15. package/fesm2022/lifecorner-angular-ui-kit-icons.mjs +80 -2
  16. package/fesm2022/lifecorner-angular-ui-kit-icons.mjs.map +1 -1
  17. package/fesm2022/lifecorner-angular-ui-kit-internal-icon-set.mjs +34 -2
  18. package/fesm2022/lifecorner-angular-ui-kit-internal-icon-set.mjs.map +1 -1
  19. package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs +9 -9
  20. package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs.map +1 -1
  21. package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs +19 -5
  22. package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs.map +1 -1
  23. package/fesm2022/lifecorner-angular-ui-kit-markdown.mjs +2 -2
  24. package/fesm2022/lifecorner-angular-ui-kit-markdown.mjs.map +1 -1
  25. package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs +2 -2
  26. package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs.map +1 -1
  27. package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs +2 -2
  28. package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs.map +1 -1
  29. package/fesm2022/lifecorner-angular-ui-kit-progress-bar.mjs +2 -2
  30. package/fesm2022/lifecorner-angular-ui-kit-progress-bar.mjs.map +1 -1
  31. package/fesm2022/lifecorner-angular-ui-kit-rating.mjs +2 -2
  32. package/fesm2022/lifecorner-angular-ui-kit-rating.mjs.map +1 -1
  33. package/fesm2022/lifecorner-angular-ui-kit-search-input.mjs +3 -3
  34. package/fesm2022/lifecorner-angular-ui-kit-search-input.mjs.map +1 -1
  35. package/fesm2022/lifecorner-angular-ui-kit-status-dot.mjs +2 -2
  36. package/fesm2022/lifecorner-angular-ui-kit-status-dot.mjs.map +1 -1
  37. package/fesm2022/lifecorner-angular-ui-kit-tree-view.mjs +3 -1
  38. package/fesm2022/lifecorner-angular-ui-kit-tree-view.mjs.map +1 -1
  39. package/package.json +1 -1
  40. package/src/styles/theme-contrast.spec.ts +239 -0
  41. package/types/lifecorner-angular-ui-kit-icons.d.ts +14 -1
@@ -265,7 +265,7 @@ var DiffViewerComponent = class DiffViewerComponent {
265
265
  },
266
266
  ngImport: i0,
267
267
  template: "<div class=\"lc-diff\">\n <!-- Stats bar -->\n <div class=\"lc-diff__stats\">\n <span class=\"lc-diff__stat lc-diff__stat--add\">+{{ stats().additions }}</span>\n <span class=\"lc-diff__stat lc-diff__stat--del\">−{{ stats().deletions }}</span>\n </div>\n\n @if (tooLarge()) {\n <!-- Size cap hit: no diff is computed -->\n <p class=\"lc-diff__too-large\" role=\"status\">\n Diff too large to display ({{ oldLabel() }}: {{ oldLineCount() }} lines,\n {{ newLabel() }}: {{ newLineCount() }} lines; limit {{ maxLines() }} lines per side).\n </p>\n } @else if (mode() === 'inline') {\n <!-- Inline mode -->\n <div class=\"lc-diff__inline\">\n <div class=\"lc-diff__headers\">\n <span class=\"lc-diff__file-label\">{{ oldLabel() }} → {{ newLabel() }}</span>\n </div>\n <div class=\"lc-diff__body\">\n @for (line of filteredLines(); track $index) {\n <div\n class=\"lc-diff__line\"\n [class.lc-diff__line--added]=\"line.type === 'added'\"\n [class.lc-diff__line--removed]=\"line.type === 'removed'\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-diff__num lc-diff__num--left\">{{ line.leftNum ?? '' }}</span>\n <span class=\"lc-diff__num lc-diff__num--right\">{{ line.rightNum ?? '' }}</span>\n }\n <span class=\"lc-diff__prefix\">{{ line.type === 'added' ? '+' : line.type === 'removed' ? '−' : ' ' }}</span>\n <span class=\"lc-diff__content\">{{ line.content }}</span>\n </div>\n }\n </div>\n </div>\n } @else {\n <!-- Side-by-side mode -->\n <div class=\"lc-diff__side-by-side\">\n <div class=\"lc-diff__pane lc-diff__pane--left\">\n <div class=\"lc-diff__pane-header\">{{ oldLabel() }}</div>\n @for (line of sideBySide().left; track $index) {\n <div\n class=\"lc-diff__line\"\n [class.lc-diff__line--removed]=\"line.type === 'removed'\"\n [class.lc-diff__line--empty]=\"line.type === 'empty'\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-diff__num\">{{ line.num ?? '' }}</span>\n }\n <span class=\"lc-diff__content\">{{ line.content }}</span>\n </div>\n }\n </div>\n <div class=\"lc-diff__pane lc-diff__pane--right\">\n <div class=\"lc-diff__pane-header\">{{ newLabel() }}</div>\n @for (line of sideBySide().right; track $index) {\n <div\n class=\"lc-diff__line\"\n [class.lc-diff__line--added]=\"line.type === 'added'\"\n [class.lc-diff__line--empty]=\"line.type === 'empty'\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-diff__num\">{{ line.num ?? '' }}</span>\n }\n <span class=\"lc-diff__content\">{{ line.content }}</span>\n </div>\n }\n </div>\n </div>\n }\n</div>\n",
268
- styles: [".lc-diff{border:1px solid var(--color-border);border-radius:8px;overflow:hidden;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.75rem;line-height:1.5}.lc-diff__stats{display:flex;gap:8px;padding:6px 12px;background:var(--color-surface-2);border-bottom:1px solid var(--color-border)}.lc-diff__stat{font-size:.6875rem;font-weight:600}.lc-diff__stat--add{color:var(--color-success-default)}.lc-diff__stat--del{color:var(--color-error-default)}.lc-diff__headers,.lc-diff__pane-header{padding:4px 12px;background:var(--color-surface-2);font-size:.6875rem;font-weight:600;color:var(--color-text-secondary);border-bottom:1px solid var(--color-border)}.lc-diff__body{overflow-x:auto}.lc-diff__side-by-side{display:grid;grid-template-columns:1fr 1fr}.lc-diff__pane{overflow-x:auto;min-width:0}.lc-diff__pane--left{border-right:1px solid var(--color-border)}.lc-diff__line{display:flex;min-height:1.5em;padding:0 8px}.lc-diff__line--added{background:var(--color-success-50, #f0fdf4)}.lc-diff__line--removed{background:var(--color-error-50, #fef2f2)}.lc-diff__line--empty{background:var(--color-surface-2)}.lc-diff__num{flex-shrink:0;width:36px;text-align:right;padding-right:8px;color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none}.lc-diff__num--left,.lc-diff__num--right{width:36px}.lc-diff__prefix{flex-shrink:0;width:16px;text-align:center;color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none;font-weight:600}.lc-diff__line--added .lc-diff__prefix{color:var(--color-success-default)}.lc-diff__line--removed .lc-diff__prefix{color:var(--color-error-default)}.lc-diff__content{flex:1;white-space:pre;color:var(--color-text-primary)}.lc-diff__file-label{color:var(--color-text-secondary)}.lc-diff__too-large{margin:0;padding:12px;font-family:var(--font-family-sans, sans-serif);font-size:.8125rem;color:var(--color-text-secondary);background:var(--color-surface-2)}\n"],
268
+ styles: [".lc-diff{border:1px solid var(--color-border);border-radius:8px;overflow:hidden;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.75rem;line-height:1.5}.lc-diff__stats{display:flex;gap:8px;padding:6px 12px;background:var(--color-surface-2);border-bottom:1px solid var(--color-border)}.lc-diff__stat{font-size:.6875rem;font-weight:600}.lc-diff__stat--add{color:var(--color-on-success-subtle)}.lc-diff__stat--del{color:var(--color-on-error-subtle)}.lc-diff__headers,.lc-diff__pane-header{padding:4px 12px;background:var(--color-surface-2);font-size:.6875rem;font-weight:600;color:var(--color-text-secondary);border-bottom:1px solid var(--color-border)}.lc-diff__body{overflow-x:auto}.lc-diff__side-by-side{display:grid;grid-template-columns:1fr 1fr}.lc-diff__pane{overflow-x:auto;min-width:0}.lc-diff__pane--left{border-right:1px solid var(--color-border)}.lc-diff__line{display:flex;min-height:1.5em;padding:0 8px}.lc-diff__line--added{background:var(--color-success-50, #f0fdf4)}.lc-diff__line--removed{background:var(--color-error-50, #fef2f2)}.lc-diff__line--empty{background:var(--color-surface-2)}.lc-diff__num{flex-shrink:0;width:36px;text-align:right;padding-right:8px;color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none}.lc-diff__num--left,.lc-diff__num--right{width:36px}.lc-diff__prefix{flex-shrink:0;width:16px;text-align:center;color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none;font-weight:600}.lc-diff__line--added .lc-diff__prefix{color:var(--color-on-success-subtle)}.lc-diff__line--removed .lc-diff__prefix{color:var(--color-on-error-subtle)}.lc-diff__content{flex:1;white-space:pre;color:var(--color-text-primary)}.lc-diff__file-label{color:var(--color-text-secondary)}.lc-diff__too-large{margin:0;padding:12px;font-family:var(--font-family-sans, sans-serif);font-size:.8125rem;color:var(--color-text-secondary);background:var(--color-surface-2)}\n"],
269
269
  changeDetection: i0.ChangeDetectionStrategy.OnPush
270
270
  });
271
271
  };
@@ -281,7 +281,7 @@ i0.ɵɵngDeclareClassMetadata({
281
281
  standalone: true,
282
282
  changeDetection: ChangeDetectionStrategy.OnPush,
283
283
  template: "<div class=\"lc-diff\">\n <!-- Stats bar -->\n <div class=\"lc-diff__stats\">\n <span class=\"lc-diff__stat lc-diff__stat--add\">+{{ stats().additions }}</span>\n <span class=\"lc-diff__stat lc-diff__stat--del\">−{{ stats().deletions }}</span>\n </div>\n\n @if (tooLarge()) {\n <!-- Size cap hit: no diff is computed -->\n <p class=\"lc-diff__too-large\" role=\"status\">\n Diff too large to display ({{ oldLabel() }}: {{ oldLineCount() }} lines,\n {{ newLabel() }}: {{ newLineCount() }} lines; limit {{ maxLines() }} lines per side).\n </p>\n } @else if (mode() === 'inline') {\n <!-- Inline mode -->\n <div class=\"lc-diff__inline\">\n <div class=\"lc-diff__headers\">\n <span class=\"lc-diff__file-label\">{{ oldLabel() }} → {{ newLabel() }}</span>\n </div>\n <div class=\"lc-diff__body\">\n @for (line of filteredLines(); track $index) {\n <div\n class=\"lc-diff__line\"\n [class.lc-diff__line--added]=\"line.type === 'added'\"\n [class.lc-diff__line--removed]=\"line.type === 'removed'\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-diff__num lc-diff__num--left\">{{ line.leftNum ?? '' }}</span>\n <span class=\"lc-diff__num lc-diff__num--right\">{{ line.rightNum ?? '' }}</span>\n }\n <span class=\"lc-diff__prefix\">{{ line.type === 'added' ? '+' : line.type === 'removed' ? '−' : ' ' }}</span>\n <span class=\"lc-diff__content\">{{ line.content }}</span>\n </div>\n }\n </div>\n </div>\n } @else {\n <!-- Side-by-side mode -->\n <div class=\"lc-diff__side-by-side\">\n <div class=\"lc-diff__pane lc-diff__pane--left\">\n <div class=\"lc-diff__pane-header\">{{ oldLabel() }}</div>\n @for (line of sideBySide().left; track $index) {\n <div\n class=\"lc-diff__line\"\n [class.lc-diff__line--removed]=\"line.type === 'removed'\"\n [class.lc-diff__line--empty]=\"line.type === 'empty'\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-diff__num\">{{ line.num ?? '' }}</span>\n }\n <span class=\"lc-diff__content\">{{ line.content }}</span>\n </div>\n }\n </div>\n <div class=\"lc-diff__pane lc-diff__pane--right\">\n <div class=\"lc-diff__pane-header\">{{ newLabel() }}</div>\n @for (line of sideBySide().right; track $index) {\n <div\n class=\"lc-diff__line\"\n [class.lc-diff__line--added]=\"line.type === 'added'\"\n [class.lc-diff__line--empty]=\"line.type === 'empty'\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-diff__num\">{{ line.num ?? '' }}</span>\n }\n <span class=\"lc-diff__content\">{{ line.content }}</span>\n </div>\n }\n </div>\n </div>\n }\n</div>\n",
284
- styles: [".lc-diff{border:1px solid var(--color-border);border-radius:8px;overflow:hidden;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.75rem;line-height:1.5}.lc-diff__stats{display:flex;gap:8px;padding:6px 12px;background:var(--color-surface-2);border-bottom:1px solid var(--color-border)}.lc-diff__stat{font-size:.6875rem;font-weight:600}.lc-diff__stat--add{color:var(--color-success-default)}.lc-diff__stat--del{color:var(--color-error-default)}.lc-diff__headers,.lc-diff__pane-header{padding:4px 12px;background:var(--color-surface-2);font-size:.6875rem;font-weight:600;color:var(--color-text-secondary);border-bottom:1px solid var(--color-border)}.lc-diff__body{overflow-x:auto}.lc-diff__side-by-side{display:grid;grid-template-columns:1fr 1fr}.lc-diff__pane{overflow-x:auto;min-width:0}.lc-diff__pane--left{border-right:1px solid var(--color-border)}.lc-diff__line{display:flex;min-height:1.5em;padding:0 8px}.lc-diff__line--added{background:var(--color-success-50, #f0fdf4)}.lc-diff__line--removed{background:var(--color-error-50, #fef2f2)}.lc-diff__line--empty{background:var(--color-surface-2)}.lc-diff__num{flex-shrink:0;width:36px;text-align:right;padding-right:8px;color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none}.lc-diff__num--left,.lc-diff__num--right{width:36px}.lc-diff__prefix{flex-shrink:0;width:16px;text-align:center;color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none;font-weight:600}.lc-diff__line--added .lc-diff__prefix{color:var(--color-success-default)}.lc-diff__line--removed .lc-diff__prefix{color:var(--color-error-default)}.lc-diff__content{flex:1;white-space:pre;color:var(--color-text-primary)}.lc-diff__file-label{color:var(--color-text-secondary)}.lc-diff__too-large{margin:0;padding:12px;font-family:var(--font-family-sans, sans-serif);font-size:.8125rem;color:var(--color-text-secondary);background:var(--color-surface-2)}\n"]
284
+ styles: [".lc-diff{border:1px solid var(--color-border);border-radius:8px;overflow:hidden;font-family:SF Mono,Fira Code,Cascadia Code,monospace;font-size:.75rem;line-height:1.5}.lc-diff__stats{display:flex;gap:8px;padding:6px 12px;background:var(--color-surface-2);border-bottom:1px solid var(--color-border)}.lc-diff__stat{font-size:.6875rem;font-weight:600}.lc-diff__stat--add{color:var(--color-on-success-subtle)}.lc-diff__stat--del{color:var(--color-on-error-subtle)}.lc-diff__headers,.lc-diff__pane-header{padding:4px 12px;background:var(--color-surface-2);font-size:.6875rem;font-weight:600;color:var(--color-text-secondary);border-bottom:1px solid var(--color-border)}.lc-diff__body{overflow-x:auto}.lc-diff__side-by-side{display:grid;grid-template-columns:1fr 1fr}.lc-diff__pane{overflow-x:auto;min-width:0}.lc-diff__pane--left{border-right:1px solid var(--color-border)}.lc-diff__line{display:flex;min-height:1.5em;padding:0 8px}.lc-diff__line--added{background:var(--color-success-50, #f0fdf4)}.lc-diff__line--removed{background:var(--color-error-50, #fef2f2)}.lc-diff__line--empty{background:var(--color-surface-2)}.lc-diff__num{flex-shrink:0;width:36px;text-align:right;padding-right:8px;color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none}.lc-diff__num--left,.lc-diff__num--right{width:36px}.lc-diff__prefix{flex-shrink:0;width:16px;text-align:center;color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none;font-weight:600}.lc-diff__line--added .lc-diff__prefix{color:var(--color-on-success-subtle)}.lc-diff__line--removed .lc-diff__prefix{color:var(--color-on-error-subtle)}.lc-diff__content{flex:1;white-space:pre;color:var(--color-text-primary)}.lc-diff__file-label{color:var(--color-text-secondary)}.lc-diff__too-large{margin:0;padding:12px;font-family:var(--font-family-sans, sans-serif);font-size:.8125rem;color:var(--color-text-secondary);background:var(--color-surface-2)}\n"]
285
285
  }]
286
286
  }],
287
287
  propDecorators: {
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-diff-viewer.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/diff-viewer/diff-viewer.component.ts","../../../../libs/angular-ui-kit/src/lib/diff-viewer/diff-viewer.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n computed,\n} from '@angular/core';\n\nexport type DiffViewMode = 'side-by-side' | 'inline';\n\ninterface DiffLine {\n type: 'unchanged' | 'added' | 'removed';\n leftNum: number | null;\n rightNum: number | null;\n content: string;\n}\n\n/** Edit-script op produced by the line diff (before numbering). */\ntype DiffOp = readonly [type: DiffLine['type'], lines: readonly string[]];\n\n/**\n * Line diff: common prefix/suffix are peeled off cheaply, the remainder goes\n * through Myers' O((N+M)·D) \"bisect\" (find where the forward and reverse\n * D-paths meet, split there, recurse) — linear space, no N×M matrix. Two\n * 5 000-line inputs used to allocate ~25M numbers and freeze the tab.\n */\nfunction diffLines(a: readonly string[], b: readonly string[]): DiffOp[] {\n if (a.length === 0 && b.length === 0) return [];\n if (a.length === 0) return [['added', b]];\n if (b.length === 0) return [['removed', a]];\n\n // Common prefix / suffix.\n let start = 0;\n const maxPrefix = Math.min(a.length, b.length);\n while (start < maxPrefix && a[start] === b[start]) start++;\n let end = 0;\n const maxSuffix = maxPrefix - start;\n while (end < maxSuffix && a[a.length - 1 - end] === b[b.length - 1 - end]) end++;\n\n const ops: DiffOp[] = [];\n if (start > 0) ops.push(['unchanged', a.slice(0, start)]);\n const midA = a.slice(start, a.length - end);\n const midB = b.slice(start, b.length - end);\n if (midA.length && midB.length) {\n // Disjoint inputs are Myers' worst case (D = N + M); answer them in linear time.\n if (sharesLine(midA, midB)) ops.push(...bisect(midA, midB));\n else ops.push(['removed', midA], ['added', midB]);\n } else if (midA.length) ops.push(['removed', midA]);\n else if (midB.length) ops.push(['added', midB]);\n if (end > 0) ops.push(['unchanged', a.slice(a.length - end)]);\n return ops;\n}\n\nfunction sharesLine(a: readonly string[], b: readonly string[]): boolean {\n const seen = new Set(a);\n return b.some(line => seen.has(line));\n}\n\n/** Myers middle-snake bisection (as in diff-match-patch), on line arrays. */\nfunction bisect(a: readonly string[], b: readonly string[]): DiffOp[] {\n const n = a.length;\n const m = b.length;\n const maxD = Math.ceil((n + m) / 2);\n const offset = maxD;\n const size = 2 * maxD;\n const v1 = new Int32Array(size).fill(-1);\n const v2 = new Int32Array(size).fill(-1);\n v1[offset + 1] = 0;\n v2[offset + 1] = 0;\n const delta = n - m;\n // With an odd delta the paths can only overlap on the forward pass.\n const front = delta % 2 !== 0;\n let k1start = 0, k1end = 0, k2start = 0, k2end = 0;\n\n for (let d = 0; d < maxD; d++) {\n // Forward path.\n for (let k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n const k1o = offset + k1;\n let x1 = k1 === -d || (k1 !== d && v1[k1o - 1] < v1[k1o + 1]) ? v1[k1o + 1] : v1[k1o - 1] + 1;\n let y1 = x1 - k1;\n while (x1 < n && y1 < m && a[x1] === b[y1]) { x1++; y1++; }\n v1[k1o] = x1;\n if (x1 > n) k1end += 2;\n else if (y1 > m) k1start += 2;\n else if (front) {\n const k2o = offset + delta - k1;\n if (k2o >= 0 && k2o < size && v2[k2o] !== -1 && x1 >= n - v2[k2o]) {\n return splitAt(a, b, x1, y1);\n }\n }\n }\n // Reverse path.\n for (let k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n const k2o = offset + k2;\n let x2 = k2 === -d || (k2 !== d && v2[k2o - 1] < v2[k2o + 1]) ? v2[k2o + 1] : v2[k2o - 1] + 1;\n let y2 = x2 - k2;\n while (x2 < n && y2 < m && a[n - x2 - 1] === b[m - y2 - 1]) { x2++; y2++; }\n v2[k2o] = x2;\n if (x2 > n) k2end += 2;\n else if (y2 > m) k2start += 2;\n else if (!front) {\n const k1o = offset + delta - k2;\n if (k1o >= 0 && k1o < size && v1[k1o] !== -1) {\n const x1 = v1[k1o];\n if (x1 >= n - x2) return splitAt(a, b, x1, offset + x1 - k1o);\n }\n }\n }\n }\n // Nothing in common at all.\n return [['removed', a], ['added', b]];\n}\n\nfunction splitAt(a: readonly string[], b: readonly string[], x: number, y: number): DiffOp[] {\n return [...diffLines(a.slice(0, x), b.slice(0, y)), ...diffLines(a.slice(x), b.slice(y))];\n}\n\n@Component({\n selector: 'lc-diff-viewer',\n standalone: true,\n templateUrl: './diff-viewer.component.html',\n styleUrls: ['./diff-viewer.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class DiffViewerComponent {\n readonly oldText = input.required<string>();\n readonly newText = input.required<string>();\n readonly oldLabel = input('Original');\n readonly newLabel = input('Modified');\n readonly mode = input<DiffViewMode>('side-by-side');\n readonly showLineNumbers = input(true);\n readonly contextLines = input(Infinity);\n\n /**\n * Upper bound (per side, in lines) above which no diff is computed and a\n * \"too large\" notice is rendered instead — a UI diff of that size is neither\n * readable nor cheap. Raise it deliberately when you know the inputs.\n */\n readonly maxLines = input(20_000);\n\n private readonly oldLines = computed(() => this.oldText().split('\\n'));\n private readonly newLines = computed(() => this.newText().split('\\n'));\n protected readonly oldLineCount = computed(() => this.oldLines().length);\n protected readonly newLineCount = computed(() => this.newLines().length);\n\n /** True when either side exceeds `maxLines`; the diff is then skipped. */\n protected readonly tooLarge = computed(\n () => Math.max(this.oldLines().length, this.newLines().length) > this.maxLines(),\n );\n\n protected readonly diffLines = computed<DiffLine[]>(() => {\n if (this.tooLarge()) return [];\n return this.numberOps(diffLines(this.oldLines(), this.newLines()));\n });\n\n protected readonly filteredLines = computed(() => {\n const ctx = this.contextLines();\n const lines = this.diffLines();\n if (ctx === Infinity) return lines;\n\n const changed = new Set<number>();\n lines.forEach((l, i) => { if (l.type !== 'unchanged') changed.add(i); });\n\n const visible = new Set<number>();\n for (const idx of changed) {\n for (let j = Math.max(0, idx - ctx); j <= Math.min(lines.length - 1, idx + ctx); j++) {\n visible.add(j);\n }\n }\n\n return lines.filter((_, i) => visible.has(i));\n });\n\n protected readonly sideBySide = computed(() => {\n if (this.mode() !== 'side-by-side') return { left: [], right: [] };\n\n const lines = this.filteredLines();\n const left: { num: number | null; content: string; type: string }[] = [];\n const right: { num: number | null; content: string; type: string }[] = [];\n\n for (const line of lines) {\n if (line.type === 'unchanged') {\n left.push({ num: line.leftNum, content: line.content, type: 'unchanged' });\n right.push({ num: line.rightNum, content: line.content, type: 'unchanged' });\n } else if (line.type === 'removed') {\n left.push({ num: line.leftNum, content: line.content, type: 'removed' });\n right.push({ num: null, content: '', type: 'empty' });\n } else {\n left.push({ num: null, content: '', type: 'empty' });\n right.push({ num: line.rightNum, content: line.content, type: 'added' });\n }\n }\n\n return { left, right };\n });\n\n protected readonly stats = computed(() => {\n let additions = 0;\n let deletions = 0;\n for (const l of this.diffLines()) {\n if (l.type === 'added') additions++;\n else if (l.type === 'removed') deletions++;\n }\n return { additions, deletions };\n });\n\n /** Expand the edit script into numbered lines (removed before added). */\n private numberOps(ops: readonly DiffOp[]): DiffLine[] {\n const result: DiffLine[] = [];\n let i = 0, j = 0;\n for (const [type, lines] of ops) {\n for (const content of lines) {\n if (type === 'unchanged') result.push({ type, leftNum: ++i, rightNum: ++j, content });\n else if (type === 'removed') result.push({ type, leftNum: ++i, rightNum: null, content });\n else result.push({ type, leftNum: null, rightNum: ++j, content });\n }\n }\n return result;\n }\n}\n","<div class=\"lc-diff\">\n <!-- Stats bar -->\n <div class=\"lc-diff__stats\">\n <span class=\"lc-diff__stat lc-diff__stat--add\">+{{ stats().additions }}</span>\n <span class=\"lc-diff__stat lc-diff__stat--del\">−{{ stats().deletions }}</span>\n </div>\n\n @if (tooLarge()) {\n <!-- Size cap hit: no diff is computed -->\n <p class=\"lc-diff__too-large\" role=\"status\">\n Diff too large to display ({{ oldLabel() }}: {{ oldLineCount() }} lines,\n {{ newLabel() }}: {{ newLineCount() }} lines; limit {{ maxLines() }} lines per side).\n </p>\n } @else if (mode() === 'inline') {\n <!-- Inline mode -->\n <div class=\"lc-diff__inline\">\n <div class=\"lc-diff__headers\">\n <span class=\"lc-diff__file-label\">{{ oldLabel() }} → {{ newLabel() }}</span>\n </div>\n <div class=\"lc-diff__body\">\n @for (line of filteredLines(); track $index) {\n <div\n class=\"lc-diff__line\"\n [class.lc-diff__line--added]=\"line.type === 'added'\"\n [class.lc-diff__line--removed]=\"line.type === 'removed'\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-diff__num lc-diff__num--left\">{{ line.leftNum ?? '' }}</span>\n <span class=\"lc-diff__num lc-diff__num--right\">{{ line.rightNum ?? '' }}</span>\n }\n <span class=\"lc-diff__prefix\">{{ line.type === 'added' ? '+' : line.type === 'removed' ? '−' : ' ' }}</span>\n <span class=\"lc-diff__content\">{{ line.content }}</span>\n </div>\n }\n </div>\n </div>\n } @else {\n <!-- Side-by-side mode -->\n <div class=\"lc-diff__side-by-side\">\n <div class=\"lc-diff__pane lc-diff__pane--left\">\n <div class=\"lc-diff__pane-header\">{{ oldLabel() }}</div>\n @for (line of sideBySide().left; track $index) {\n <div\n class=\"lc-diff__line\"\n [class.lc-diff__line--removed]=\"line.type === 'removed'\"\n [class.lc-diff__line--empty]=\"line.type === 'empty'\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-diff__num\">{{ line.num ?? '' }}</span>\n }\n <span class=\"lc-diff__content\">{{ line.content }}</span>\n </div>\n }\n </div>\n <div class=\"lc-diff__pane lc-diff__pane--right\">\n <div class=\"lc-diff__pane-header\">{{ newLabel() }}</div>\n @for (line of sideBySide().right; track $index) {\n <div\n class=\"lc-diff__line\"\n [class.lc-diff__line--added]=\"line.type === 'added'\"\n [class.lc-diff__line--empty]=\"line.type === 'empty'\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-diff__num\">{{ line.num ?? '' }}</span>\n }\n <span class=\"lc-diff__content\">{{ line.content }}</span>\n </div>\n }\n </div>\n </div>\n }\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-diff-viewer.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/diff-viewer/diff-viewer.component.ts","../../../../libs/angular-ui-kit/src/lib/diff-viewer/diff-viewer.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n computed,\n} from '@angular/core';\n\nexport type DiffViewMode = 'side-by-side' | 'inline';\n\ninterface DiffLine {\n type: 'unchanged' | 'added' | 'removed';\n leftNum: number | null;\n rightNum: number | null;\n content: string;\n}\n\n/** Edit-script op produced by the line diff (before numbering). */\ntype DiffOp = readonly [type: DiffLine['type'], lines: readonly string[]];\n\n/**\n * Line diff: common prefix/suffix are peeled off cheaply, the remainder goes\n * through Myers' O((N+M)·D) \"bisect\" (find where the forward and reverse\n * D-paths meet, split there, recurse) — linear space, no N×M matrix. Two\n * 5 000-line inputs used to allocate ~25M numbers and freeze the tab.\n */\nfunction diffLines(a: readonly string[], b: readonly string[]): DiffOp[] {\n if (a.length === 0 && b.length === 0) return [];\n if (a.length === 0) return [['added', b]];\n if (b.length === 0) return [['removed', a]];\n\n // Common prefix / suffix.\n let start = 0;\n const maxPrefix = Math.min(a.length, b.length);\n while (start < maxPrefix && a[start] === b[start]) start++;\n let end = 0;\n const maxSuffix = maxPrefix - start;\n while (end < maxSuffix && a[a.length - 1 - end] === b[b.length - 1 - end]) end++;\n\n const ops: DiffOp[] = [];\n if (start > 0) ops.push(['unchanged', a.slice(0, start)]);\n const midA = a.slice(start, a.length - end);\n const midB = b.slice(start, b.length - end);\n if (midA.length && midB.length) {\n // Disjoint inputs are Myers' worst case (D = N + M); answer them in linear time.\n if (sharesLine(midA, midB)) ops.push(...bisect(midA, midB));\n else ops.push(['removed', midA], ['added', midB]);\n } else if (midA.length) ops.push(['removed', midA]);\n else if (midB.length) ops.push(['added', midB]);\n if (end > 0) ops.push(['unchanged', a.slice(a.length - end)]);\n return ops;\n}\n\nfunction sharesLine(a: readonly string[], b: readonly string[]): boolean {\n const seen = new Set(a);\n return b.some(line => seen.has(line));\n}\n\n/** Myers middle-snake bisection (as in diff-match-patch), on line arrays. */\nfunction bisect(a: readonly string[], b: readonly string[]): DiffOp[] {\n const n = a.length;\n const m = b.length;\n const maxD = Math.ceil((n + m) / 2);\n const offset = maxD;\n const size = 2 * maxD;\n const v1 = new Int32Array(size).fill(-1);\n const v2 = new Int32Array(size).fill(-1);\n v1[offset + 1] = 0;\n v2[offset + 1] = 0;\n const delta = n - m;\n // With an odd delta the paths can only overlap on the forward pass.\n const front = delta % 2 !== 0;\n let k1start = 0, k1end = 0, k2start = 0, k2end = 0;\n\n for (let d = 0; d < maxD; d++) {\n // Forward path.\n for (let k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n const k1o = offset + k1;\n let x1 = k1 === -d || (k1 !== d && v1[k1o - 1] < v1[k1o + 1]) ? v1[k1o + 1] : v1[k1o - 1] + 1;\n let y1 = x1 - k1;\n while (x1 < n && y1 < m && a[x1] === b[y1]) { x1++; y1++; }\n v1[k1o] = x1;\n if (x1 > n) k1end += 2;\n else if (y1 > m) k1start += 2;\n else if (front) {\n const k2o = offset + delta - k1;\n if (k2o >= 0 && k2o < size && v2[k2o] !== -1 && x1 >= n - v2[k2o]) {\n return splitAt(a, b, x1, y1);\n }\n }\n }\n // Reverse path.\n for (let k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n const k2o = offset + k2;\n let x2 = k2 === -d || (k2 !== d && v2[k2o - 1] < v2[k2o + 1]) ? v2[k2o + 1] : v2[k2o - 1] + 1;\n let y2 = x2 - k2;\n while (x2 < n && y2 < m && a[n - x2 - 1] === b[m - y2 - 1]) { x2++; y2++; }\n v2[k2o] = x2;\n if (x2 > n) k2end += 2;\n else if (y2 > m) k2start += 2;\n else if (!front) {\n const k1o = offset + delta - k2;\n if (k1o >= 0 && k1o < size && v1[k1o] !== -1) {\n const x1 = v1[k1o];\n if (x1 >= n - x2) return splitAt(a, b, x1, offset + x1 - k1o);\n }\n }\n }\n }\n // Nothing in common at all.\n return [['removed', a], ['added', b]];\n}\n\nfunction splitAt(a: readonly string[], b: readonly string[], x: number, y: number): DiffOp[] {\n return [...diffLines(a.slice(0, x), b.slice(0, y)), ...diffLines(a.slice(x), b.slice(y))];\n}\n\n@Component({\n selector: 'lc-diff-viewer',\n standalone: true,\n templateUrl: './diff-viewer.component.html',\n styleUrls: ['./diff-viewer.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class DiffViewerComponent {\n readonly oldText = input.required<string>();\n readonly newText = input.required<string>();\n readonly oldLabel = input('Original');\n readonly newLabel = input('Modified');\n readonly mode = input<DiffViewMode>('side-by-side');\n readonly showLineNumbers = input(true);\n readonly contextLines = input(Infinity);\n\n /**\n * Upper bound (per side, in lines) above which no diff is computed and a\n * \"too large\" notice is rendered instead — a UI diff of that size is neither\n * readable nor cheap. Raise it deliberately when you know the inputs.\n */\n readonly maxLines = input(20_000);\n\n private readonly oldLines = computed(() => this.oldText().split('\\n'));\n private readonly newLines = computed(() => this.newText().split('\\n'));\n protected readonly oldLineCount = computed(() => this.oldLines().length);\n protected readonly newLineCount = computed(() => this.newLines().length);\n\n /** True when either side exceeds `maxLines`; the diff is then skipped. */\n protected readonly tooLarge = computed(\n () => Math.max(this.oldLines().length, this.newLines().length) > this.maxLines(),\n );\n\n protected readonly diffLines = computed<DiffLine[]>(() => {\n if (this.tooLarge()) return [];\n return this.numberOps(diffLines(this.oldLines(), this.newLines()));\n });\n\n protected readonly filteredLines = computed(() => {\n const ctx = this.contextLines();\n const lines = this.diffLines();\n if (ctx === Infinity) return lines;\n\n const changed = new Set<number>();\n lines.forEach((l, i) => { if (l.type !== 'unchanged') changed.add(i); });\n\n const visible = new Set<number>();\n for (const idx of changed) {\n for (let j = Math.max(0, idx - ctx); j <= Math.min(lines.length - 1, idx + ctx); j++) {\n visible.add(j);\n }\n }\n\n return lines.filter((_, i) => visible.has(i));\n });\n\n protected readonly sideBySide = computed(() => {\n if (this.mode() !== 'side-by-side') return { left: [], right: [] };\n\n const lines = this.filteredLines();\n const left: { num: number | null; content: string; type: string }[] = [];\n const right: { num: number | null; content: string; type: string }[] = [];\n\n for (const line of lines) {\n if (line.type === 'unchanged') {\n left.push({ num: line.leftNum, content: line.content, type: 'unchanged' });\n right.push({ num: line.rightNum, content: line.content, type: 'unchanged' });\n } else if (line.type === 'removed') {\n left.push({ num: line.leftNum, content: line.content, type: 'removed' });\n right.push({ num: null, content: '', type: 'empty' });\n } else {\n left.push({ num: null, content: '', type: 'empty' });\n right.push({ num: line.rightNum, content: line.content, type: 'added' });\n }\n }\n\n return { left, right };\n });\n\n protected readonly stats = computed(() => {\n let additions = 0;\n let deletions = 0;\n for (const l of this.diffLines()) {\n if (l.type === 'added') additions++;\n else if (l.type === 'removed') deletions++;\n }\n return { additions, deletions };\n });\n\n /** Expand the edit script into numbered lines (removed before added). */\n private numberOps(ops: readonly DiffOp[]): DiffLine[] {\n const result: DiffLine[] = [];\n let i = 0, j = 0;\n for (const [type, lines] of ops) {\n for (const content of lines) {\n if (type === 'unchanged') result.push({ type, leftNum: ++i, rightNum: ++j, content });\n else if (type === 'removed') result.push({ type, leftNum: ++i, rightNum: null, content });\n else result.push({ type, leftNum: null, rightNum: ++j, content });\n }\n }\n return result;\n }\n}\n","<div class=\"lc-diff\">\n <!-- Stats bar -->\n <div class=\"lc-diff__stats\">\n <span class=\"lc-diff__stat lc-diff__stat--add\">+{{ stats().additions }}</span>\n <span class=\"lc-diff__stat lc-diff__stat--del\">−{{ stats().deletions }}</span>\n </div>\n\n @if (tooLarge()) {\n <!-- Size cap hit: no diff is computed -->\n <p class=\"lc-diff__too-large\" role=\"status\">\n Diff too large to display ({{ oldLabel() }}: {{ oldLineCount() }} lines,\n {{ newLabel() }}: {{ newLineCount() }} lines; limit {{ maxLines() }} lines per side).\n </p>\n } @else if (mode() === 'inline') {\n <!-- Inline mode -->\n <div class=\"lc-diff__inline\">\n <div class=\"lc-diff__headers\">\n <span class=\"lc-diff__file-label\">{{ oldLabel() }} → {{ newLabel() }}</span>\n </div>\n <div class=\"lc-diff__body\">\n @for (line of filteredLines(); track $index) {\n <div\n class=\"lc-diff__line\"\n [class.lc-diff__line--added]=\"line.type === 'added'\"\n [class.lc-diff__line--removed]=\"line.type === 'removed'\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-diff__num lc-diff__num--left\">{{ line.leftNum ?? '' }}</span>\n <span class=\"lc-diff__num lc-diff__num--right\">{{ line.rightNum ?? '' }}</span>\n }\n <span class=\"lc-diff__prefix\">{{ line.type === 'added' ? '+' : line.type === 'removed' ? '−' : ' ' }}</span>\n <span class=\"lc-diff__content\">{{ line.content }}</span>\n </div>\n }\n </div>\n </div>\n } @else {\n <!-- Side-by-side mode -->\n <div class=\"lc-diff__side-by-side\">\n <div class=\"lc-diff__pane lc-diff__pane--left\">\n <div class=\"lc-diff__pane-header\">{{ oldLabel() }}</div>\n @for (line of sideBySide().left; track $index) {\n <div\n class=\"lc-diff__line\"\n [class.lc-diff__line--removed]=\"line.type === 'removed'\"\n [class.lc-diff__line--empty]=\"line.type === 'empty'\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-diff__num\">{{ line.num ?? '' }}</span>\n }\n <span class=\"lc-diff__content\">{{ line.content }}</span>\n </div>\n }\n </div>\n <div class=\"lc-diff__pane lc-diff__pane--right\">\n <div class=\"lc-diff__pane-header\">{{ newLabel() }}</div>\n @for (line of sideBySide().right; track $index) {\n <div\n class=\"lc-diff__line\"\n [class.lc-diff__line--added]=\"line.type === 'added'\"\n [class.lc-diff__line--empty]=\"line.type === 'empty'\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-diff__num\">{{ line.num ?? '' }}</span>\n }\n <span class=\"lc-diff__content\">{{ line.content }}</span>\n </div>\n }\n </div>\n </div>\n }\n</div>\n"],"mappings":"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"}
@@ -169,7 +169,7 @@ var FileUploadComponent = class FileUploadComponent {
169
169
  }],
170
170
  ngImport: i0,
171
171
  template: "<div\n [class]=\"dropzoneClasses()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n (click)=\"openFilePicker()\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openFilePicker()\"\n (keydown.space)=\"openFilePicker()\"\n [attr.aria-label]=\"dropzoneAriaLabel()\"\n [attr.aria-disabled]=\"disabled()\"\n>\n <input\n #fileInput\n type=\"file\"\n class=\"file-upload__input\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n [disabled]=\"disabled()\"\n (change)=\"onFileChange($event)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n <div class=\"file-upload__content\">\n <lc-icon name=\"cloud-arrow-up\" class=\"file-upload__icon\" [decorative]=\"true\"></lc-icon>\n <!-- Real spaces between the lines, so the visible text reads as words\n that the drop zone's name contains (WCAG 2.5.3). -->\n <p class=\"file-upload__hint\">{{ hint() }}</p>\n @if (accept()) {\n &ngsp;<p class=\"file-upload__accept\">{{ acceptedLabel() }} {{ accept() }}</p>\n }\n @if (maxSizeBytes() > 0) {\n &ngsp;<p class=\"file-upload__accept\">{{ maxSizeLabel() }} {{ formatSize(maxSizeBytes()) }}</p>\n }\n </div>\n</div>\n\n@if (selectedFiles().length > 0) {\n <ul class=\"file-upload__list\">\n @for (f of selectedFiles(); track f.name; let i = $index) {\n <li class=\"file-upload__file\">\n <lc-icon name=\"document\" class=\"file-upload__file-icon\" [decorative]=\"true\"></lc-icon>\n <span class=\"file-upload__file-name\">{{ f.name }}</span>\n <span class=\"file-upload__file-size\">{{ formatSize(f.size) }}</span>\n <button\n type=\"button\"\n class=\"file-upload__file-remove\"\n (click)=\"removeFile(i); $event.stopPropagation()\"\n [attr.aria-label]=\"removeFileAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" [decorative]=\"true\"></lc-icon>\n </button>\n </li>\n }\n </ul>\n}\n",
172
- styles: [":host{display:block;width:100%}.file-upload{display:flex;align-items:center;justify-content:center;padding:2rem 1.5rem;border:2px dashed var(--color-border-strong);border-radius:var(--border-radius-lg);background-color:var(--color-surface-2);cursor:pointer;transition:border-color .15s ease,background-color .15s ease;text-align:center}.file-upload:hover{border-color:var(--color-primary-500);background-color:var(--color-surface-hover)}.file-upload:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.file-upload--drag-over{border-color:var(--color-primary-500);background-color:var(--color-surface-selected)}.file-upload--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.file-upload__input{position:absolute;width:0;height:0;overflow:hidden;opacity:0}.file-upload__content{display:flex;flex-direction:column;align-items:center;gap:.5rem}.file-upload__icon{--icon-size: 2rem;color:var(--color-primary-500)}.file-upload__hint{margin:0;font-size:.875rem;color:var(--color-text-secondary)}.file-upload__accept{margin:0;font-size:.75rem;color:var(--color-text-tertiary)}.file-upload__list{list-style:none;margin:.75rem 0 0;padding:0;display:flex;flex-direction:column;gap:.5rem}.file-upload__file{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:var(--border-radius-md);font-size:.8125rem}.file-upload__file-icon{--icon-size: 1rem;color:var(--color-text-tertiary);flex-shrink:0}.file-upload__file-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--color-text-primary)}.file-upload__file-size{flex-shrink:0;color:var(--color-text-tertiary);font-size:.75rem}.file-upload__file-remove{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;border:none;border-radius:50%;background:var(--color-surface-hover);cursor:pointer;padding:0;--icon-size: .75rem;color:var(--color-text-secondary)}.file-upload__file-remove:hover{background:#9d0e0e2e;color:var(--color-error)}\n"],
172
+ styles: [":host{display:block;width:100%}.file-upload{display:flex;align-items:center;justify-content:center;padding:2rem 1.5rem;border:2px dashed var(--color-border-strong);border-radius:var(--border-radius-lg);background-color:var(--color-surface-2);cursor:pointer;transition:border-color .15s ease,background-color .15s ease;text-align:center}.file-upload:hover{border-color:var(--color-primary-500);background-color:var(--color-surface-hover)}.file-upload:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.file-upload--drag-over{border-color:var(--color-primary-500);background-color:var(--color-surface-selected)}.file-upload--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.file-upload__input{position:absolute;width:0;height:0;overflow:hidden;opacity:0}.file-upload__content{display:flex;flex-direction:column;align-items:center;gap:.5rem}.file-upload__icon{--icon-size: 2rem;color:var(--color-primary-500)}.file-upload__hint{margin:0;font-size:.875rem;color:var(--color-text-secondary)}.file-upload__accept{margin:0;font-size:.75rem;color:var(--color-text-tertiary)}.file-upload__list{list-style:none;margin:.75rem 0 0;padding:0;display:flex;flex-direction:column;gap:.5rem}.file-upload__file{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:var(--border-radius-md);font-size:.8125rem}.file-upload__file-icon{--icon-size: 1rem;color:var(--color-text-tertiary);flex-shrink:0}.file-upload__file-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--color-text-primary)}.file-upload__file-size{flex-shrink:0;color:var(--color-text-tertiary);font-size:.75rem}.file-upload__file-remove{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;border:none;border-radius:50%;background:var(--color-surface-hover);cursor:pointer;padding:0;--icon-size: .75rem;color:var(--color-text-secondary)}.file-upload__file-remove:hover{background:#9d0e0e2e;color:var(--color-on-error-subtle)}\n"],
173
173
  dependencies: [{
174
174
  kind: "component",
175
175
  type: IconComponent,
@@ -202,7 +202,7 @@ i0.ɵɵngDeclareClassMetadata({
202
202
  viewProviders: [ɵprovideKitIcons([ICON_FILE, ICON_X_MARK])],
203
203
  changeDetection: ChangeDetectionStrategy.OnPush,
204
204
  template: "<div\n [class]=\"dropzoneClasses()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n (click)=\"openFilePicker()\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openFilePicker()\"\n (keydown.space)=\"openFilePicker()\"\n [attr.aria-label]=\"dropzoneAriaLabel()\"\n [attr.aria-disabled]=\"disabled()\"\n>\n <input\n #fileInput\n type=\"file\"\n class=\"file-upload__input\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n [disabled]=\"disabled()\"\n (change)=\"onFileChange($event)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n <div class=\"file-upload__content\">\n <lc-icon name=\"cloud-arrow-up\" class=\"file-upload__icon\" [decorative]=\"true\"></lc-icon>\n <!-- Real spaces between the lines, so the visible text reads as words\n that the drop zone's name contains (WCAG 2.5.3). -->\n <p class=\"file-upload__hint\">{{ hint() }}</p>\n @if (accept()) {\n &ngsp;<p class=\"file-upload__accept\">{{ acceptedLabel() }} {{ accept() }}</p>\n }\n @if (maxSizeBytes() > 0) {\n &ngsp;<p class=\"file-upload__accept\">{{ maxSizeLabel() }} {{ formatSize(maxSizeBytes()) }}</p>\n }\n </div>\n</div>\n\n@if (selectedFiles().length > 0) {\n <ul class=\"file-upload__list\">\n @for (f of selectedFiles(); track f.name; let i = $index) {\n <li class=\"file-upload__file\">\n <lc-icon name=\"document\" class=\"file-upload__file-icon\" [decorative]=\"true\"></lc-icon>\n <span class=\"file-upload__file-name\">{{ f.name }}</span>\n <span class=\"file-upload__file-size\">{{ formatSize(f.size) }}</span>\n <button\n type=\"button\"\n class=\"file-upload__file-remove\"\n (click)=\"removeFile(i); $event.stopPropagation()\"\n [attr.aria-label]=\"removeFileAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" [decorative]=\"true\"></lc-icon>\n </button>\n </li>\n }\n </ul>\n}\n",
205
- styles: [":host{display:block;width:100%}.file-upload{display:flex;align-items:center;justify-content:center;padding:2rem 1.5rem;border:2px dashed var(--color-border-strong);border-radius:var(--border-radius-lg);background-color:var(--color-surface-2);cursor:pointer;transition:border-color .15s ease,background-color .15s ease;text-align:center}.file-upload:hover{border-color:var(--color-primary-500);background-color:var(--color-surface-hover)}.file-upload:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.file-upload--drag-over{border-color:var(--color-primary-500);background-color:var(--color-surface-selected)}.file-upload--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.file-upload__input{position:absolute;width:0;height:0;overflow:hidden;opacity:0}.file-upload__content{display:flex;flex-direction:column;align-items:center;gap:.5rem}.file-upload__icon{--icon-size: 2rem;color:var(--color-primary-500)}.file-upload__hint{margin:0;font-size:.875rem;color:var(--color-text-secondary)}.file-upload__accept{margin:0;font-size:.75rem;color:var(--color-text-tertiary)}.file-upload__list{list-style:none;margin:.75rem 0 0;padding:0;display:flex;flex-direction:column;gap:.5rem}.file-upload__file{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:var(--border-radius-md);font-size:.8125rem}.file-upload__file-icon{--icon-size: 1rem;color:var(--color-text-tertiary);flex-shrink:0}.file-upload__file-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--color-text-primary)}.file-upload__file-size{flex-shrink:0;color:var(--color-text-tertiary);font-size:.75rem}.file-upload__file-remove{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;border:none;border-radius:50%;background:var(--color-surface-hover);cursor:pointer;padding:0;--icon-size: .75rem;color:var(--color-text-secondary)}.file-upload__file-remove:hover{background:#9d0e0e2e;color:var(--color-error)}\n"]
205
+ styles: [":host{display:block;width:100%}.file-upload{display:flex;align-items:center;justify-content:center;padding:2rem 1.5rem;border:2px dashed var(--color-border-strong);border-radius:var(--border-radius-lg);background-color:var(--color-surface-2);cursor:pointer;transition:border-color .15s ease,background-color .15s ease;text-align:center}.file-upload:hover{border-color:var(--color-primary-500);background-color:var(--color-surface-hover)}.file-upload:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.file-upload--drag-over{border-color:var(--color-primary-500);background-color:var(--color-surface-selected)}.file-upload--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.file-upload__input{position:absolute;width:0;height:0;overflow:hidden;opacity:0}.file-upload__content{display:flex;flex-direction:column;align-items:center;gap:.5rem}.file-upload__icon{--icon-size: 2rem;color:var(--color-primary-500)}.file-upload__hint{margin:0;font-size:.875rem;color:var(--color-text-secondary)}.file-upload__accept{margin:0;font-size:.75rem;color:var(--color-text-tertiary)}.file-upload__list{list-style:none;margin:.75rem 0 0;padding:0;display:flex;flex-direction:column;gap:.5rem}.file-upload__file{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:var(--border-radius-md);font-size:.8125rem}.file-upload__file-icon{--icon-size: 1rem;color:var(--color-text-tertiary);flex-shrink:0}.file-upload__file-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--color-text-primary)}.file-upload__file-size{flex-shrink:0;color:var(--color-text-tertiary);font-size:.75rem}.file-upload__file-remove{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;border:none;border-radius:50%;background:var(--color-surface-hover);cursor:pointer;padding:0;--icon-size: .75rem;color:var(--color-text-secondary)}.file-upload__file-remove:hover{background:#9d0e0e2e;color:var(--color-on-error-subtle)}\n"]
206
206
  }]
207
207
  }],
208
208
  propDecorators: {
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-file-upload.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/file-upload/file-upload.component.ts","../../../../libs/angular-ui-kit/src/lib/file-upload/file-upload.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n output,\n signal,\n computed,\n ElementRef,\n viewChild,\n} from '@angular/core';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_FILE, ICON_X_MARK } from '@lifecorner/angular-ui-kit/icons';\n\nexport interface FileUploadFile {\n file: File;\n name: string;\n size: number;\n type: string;\n}\n\n/**\n * File upload / dropzone component with drag & drop support.\n *\n * @example\n * ```html\n * <lc-file-upload\n * accept=\".pdf,.png,.jpg\"\n * [multiple]=\"true\"\n * [maxSizeBytes]=\"5242880\"\n * (filesSelected)=\"onFiles($event)\"\n * ></lc-file-upload>\n * ```\n */\n@Component({\n selector: 'lc-file-upload',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_FILE, ICON_X_MARK])],\n templateUrl: './file-upload.component.html',\n styleUrls: ['./file-upload.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\n/**\n * File upload component with drag-and-drop support.\n *\n * Features:\n * - Drag-and-drop file dropzone with visual feedback\n * - Click-to-browse file selection\n * - File type and size validation\n * - Multi-file upload support\n * - File list display with removal\n * - Disabled state support\n *\n * @example\n * ```html\n * <lc-file-upload accept=\".pdf,.jpg\" [multiple]=\"true\" [maxSizeBytes]=\"5242880\" />\n * ```\n */\nexport class FileUploadComponent {\n /**\n * Accepted file types (e.g. \".pdf,.jpg,image/*\").\n */\n accept = input<string>('');\n\n /**\n * Allow multiple files.\n * @default false\n */\n multiple = input<boolean>(false);\n\n /**\n * Maximum file size in bytes. 0 for unlimited.\n * @default 0\n */\n maxSizeBytes = input<number>(0);\n\n /**\n * Whether the dropzone is disabled.\n * @default false\n */\n disabled = input<boolean>(false);\n\n /**\n * Hint text shown in the dropzone. The drop zone's accessible name is\n * `hint`, then the shown accepted and max lines, comma-separated (WCAG 2.5.3\n * Label in Name).\n * @default 'Drag & drop files here, or click to browse'\n */\n hint = input<string>('Drag & drop files here, or click to browse');\n\n /**\n * Prefix of the accepted file types line (`accept`). The shown line is part\n * of the drop zone's accessible name, after `hint`.\n * @default 'Accepted:'\n */\n acceptedLabel = input('Accepted:');\n\n /**\n * Prefix of the maximum file size line (`maxSizeBytes`). The shown line is\n * part of the drop zone's accessible name, after `hint`.\n * @default 'Max:'\n */\n maxSizeLabel = input('Max:');\n\n /** Accessible name of a selected file's remove button. */\n removeFileAriaLabel = input('Remove file');\n\n /**\n * Emitted with selected files after validation.\n */\n readonly filesSelected = output<FileUploadFile[]>();\n\n /**\n * Emitted when a file is rejected (too large or wrong type).\n */\n readonly fileRejected = output<{ file: File; reason: string }>();\n\n protected isDragOver = signal(false);\n protected selectedFiles = signal<FileUploadFile[]>([]);\n protected readonly fileInputRef = viewChild<ElementRef<HTMLInputElement>>('fileInput');\n\n protected dropzoneClasses = computed(() => {\n return [\n 'file-upload',\n this.isDragOver() ? 'file-upload--drag-over' : '',\n this.disabled() ? 'file-upload--disabled' : '',\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n /**\n * Name of the drop zone (AS-33, WCAG 2.5.3 Label in Name): `hint`, then the\n * accepted and max lines it shows, comma-separated.\n */\n protected readonly dropzoneAriaLabel = computed(() =>\n [\n this.hint(),\n this.accept() ? `${this.acceptedLabel()} ${this.accept()}` : '',\n this.maxSizeBytes() > 0 ? `${this.maxSizeLabel()} ${this.formatSize(this.maxSizeBytes())}` : '',\n ]\n .filter(Boolean)\n .join(', '),\n );\n\n protected onDragOver(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n if (!this.disabled()) {\n this.isDragOver.set(true);\n }\n }\n\n protected onDragLeave(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n this.isDragOver.set(false);\n }\n\n protected onDrop(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n this.isDragOver.set(false);\n if (this.disabled() || !event.dataTransfer?.files.length) return;\n this.processFiles(event.dataTransfer.files);\n }\n\n protected onFileChange(event: Event): void {\n const input = event.target as HTMLInputElement;\n if (input.files?.length) {\n this.processFiles(input.files);\n }\n // Reset so the same file can be re-selected\n input.value = '';\n }\n\n protected openFilePicker(): void {\n if (!this.disabled()) {\n this.fileInputRef()?.nativeElement.click();\n }\n }\n\n protected removeFile(index: number): void {\n this.selectedFiles.update((files) => files.filter((_, i) => i !== index));\n this.filesSelected.emit(this.selectedFiles());\n }\n\n protected formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n }\n\n private processFiles(fileList: FileList): void {\n const files = Array.from(fileList);\n const accepted: FileUploadFile[] = [];\n\n for (const file of files) {\n if (this.maxSizeBytes() > 0 && file.size > this.maxSizeBytes()) {\n this.fileRejected.emit({ file, reason: 'File too large' });\n continue;\n }\n accepted.push({\n file,\n name: file.name,\n size: file.size,\n type: file.type,\n });\n }\n\n if (this.multiple()) {\n this.selectedFiles.update((prev) => [...prev, ...accepted]);\n } else {\n this.selectedFiles.set(accepted.slice(0, 1));\n }\n\n this.filesSelected.emit(this.selectedFiles());\n }\n}\n","<div\n [class]=\"dropzoneClasses()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n (click)=\"openFilePicker()\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openFilePicker()\"\n (keydown.space)=\"openFilePicker()\"\n [attr.aria-label]=\"dropzoneAriaLabel()\"\n [attr.aria-disabled]=\"disabled()\"\n>\n <input\n #fileInput\n type=\"file\"\n class=\"file-upload__input\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n [disabled]=\"disabled()\"\n (change)=\"onFileChange($event)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n <div class=\"file-upload__content\">\n <lc-icon name=\"cloud-arrow-up\" class=\"file-upload__icon\" [decorative]=\"true\"></lc-icon>\n <!-- Real spaces between the lines, so the visible text reads as words\n that the drop zone's name contains (WCAG 2.5.3). -->\n <p class=\"file-upload__hint\">{{ hint() }}</p>\n @if (accept()) {\n &ngsp;<p class=\"file-upload__accept\">{{ acceptedLabel() }} {{ accept() }}</p>\n }\n @if (maxSizeBytes() > 0) {\n &ngsp;<p class=\"file-upload__accept\">{{ maxSizeLabel() }} {{ formatSize(maxSizeBytes()) }}</p>\n }\n </div>\n</div>\n\n@if (selectedFiles().length > 0) {\n <ul class=\"file-upload__list\">\n @for (f of selectedFiles(); track f.name; let i = $index) {\n <li class=\"file-upload__file\">\n <lc-icon name=\"document\" class=\"file-upload__file-icon\" [decorative]=\"true\"></lc-icon>\n <span class=\"file-upload__file-name\">{{ f.name }}</span>\n <span class=\"file-upload__file-size\">{{ formatSize(f.size) }}</span>\n <button\n type=\"button\"\n class=\"file-upload__file-remove\"\n (click)=\"removeFile(i); $event.stopPropagation()\"\n [attr.aria-label]=\"removeFileAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" [decorative]=\"true\"></lc-icon>\n </button>\n </li>\n }\n </ul>\n}\n"],"mappings":";;;;AA0DA,IAAa,sBAAb,MAAa,oBAAmB;CAI9B,SAAS,MAAc,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CAM1B,WAAW,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAMhC,eAAe,MAAc,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAM/B,WAAW,MAAe,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAQhC,OAAO,MAAc,8CAA6C,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAOlE,gBAAgB,MAAM,aAAY,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CAOlC,eAAe,MAAM,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAG5B,sBAAsB,MAAM,eAAc,GAAA,YAAA,CAAA,EAAA,WAAA,sBAAA,CAAA,+BAAA,CAAA,CAAA;CAKjC,gBAAgB,OAAM;CAKtB,eAAe,OAAM;CAEpB,aAAa,OAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAC1B,gBAAgB,OAAyB,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,gBAAA,CAAA,+BAAA,CAAA,CAAA;CACnC,eAAe,UAAwC,aAAY,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAE5E,kBAAkB,eAAc;EACxC,OAAO;GACL;GACA,KAAK,WAAU,IAAK,2BAA2B;GAC/C,KAAK,SAAQ,IAAK,0BAA0B;EAC7C,CAAA,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CACb,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,+BAAA,CAAA,CAAA;CAMiB,oBAAoB,eACrC;EACE,KAAK,KAAI;EACT,KAAK,OAAM,IAAK,GAAG,KAAK,cAAa,EAAE,GAAI,KAAK,OAAM,MAAO;EAC7D,KAAK,aAAY,IAAK,IAAI,GAAG,KAAK,aAAY,EAAE,GAAI,KAAK,WAAW,KAAK,aAAY,CAAE,MAAM;CAC9F,CAAA,CACE,OAAO,OAAO,CAAC,CACf,KAAK,IAAI,GACb,GAAA,YAAA,CAAA,EAAA,WAAA,oBAAA,CAAA,+BAAA,CAAA,CAAA;CAES,WAAW,OAAgB;EACnC,MAAM,eAAc;EACpB,MAAM,gBAAe;EACrB,IAAI,CAAC,KAAK,SAAQ,GAChB,KAAK,WAAW,IAAI,IAAI;CAE5B;CAEU,YAAY,OAAgB;EACpC,MAAM,eAAc;EACpB,MAAM,gBAAe;EACrB,KAAK,WAAW,IAAI,KAAK;CAC3B;CAEU,OAAO,OAAgB;EAC/B,MAAM,eAAc;EACpB,MAAM,gBAAe;EACrB,KAAK,WAAW,IAAI,KAAK;EACzB,IAAI,KAAK,SAAQ,KAAM,CAAC,MAAM,cAAc,MAAM,QAAQ;EAC1D,KAAK,aAAa,MAAM,aAAa,KAAK;CAC5C;CAEU,aAAa,OAAY;EACjC,MAAM,QAAQ,MAAM;EACpB,IAAI,MAAM,OAAO,QACf,KAAK,aAAa,MAAM,KAAK;EAG/B,MAAM,QAAQ;CAChB;CAEU,iBAAc;EACtB,IAAI,CAAC,KAAK,SAAQ,GAChB,KAAK,aAAY,CAAE,EAAE,cAAc,MAAK;CAE5C;CAEU,WAAW,OAAa;EAChC,KAAK,cAAc,QAAQ,UAAU,MAAM,QAAQ,GAAG,MAAM,MAAM,KAAK,CAAC;EACxE,KAAK,cAAc,KAAK,KAAK,cAAa,CAAE;CAC9C;CAEU,WAAW,OAAa;EAChC,IAAI,QAAQ,MAAM,OAAO,GAAG,MAAK;EACjC,IAAI,QAAQ,SAAa,OAAO,IAAI,QAAQ,KAAA,CAAM,QAAQ,CAAC,EAAC;EAC5D,OAAO,IAAI,QAAS,QAAO,CAAO,QAAQ,CAAC,EAAC;CAC9C;CAEQ,aAAa,UAAkB;EACrC,MAAM,QAAQ,MAAM,KAAK,QAAQ;EACjC,MAAM,WAA6B,CAAA;EAEnC,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,KAAK,aAAY,IAAK,KAAK,KAAK,OAAO,KAAK,aAAY,GAAI;IAC9D,KAAK,aAAa,KAAK;KAAE;KAAM,QAAQ;IAAgB,CAAE;IACzD;GACF;GACA,SAAS,KAAK;IACZ;IACA,MAAM,KAAK;IACX,MAAM,KAAK;IACX,MAAM,KAAK;GACZ,CAAA;EACH;EAEA,IAAI,KAAK,SAAQ,GACf,KAAK,cAAc,QAAQ,SAAS,CAAC,GAAG,MAAM,GAAG,QAAQ,CAAC;OAE1D,KAAK,cAAc,IAAI,SAAS,MAAM,GAAG,CAAC,CAAC;EAG7C,KAAK,cAAc,KAAK,KAAK,cAAa,CAAE;CAC9C;CA/JW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAmB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAnB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAmB,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,QAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,eAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,cAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,qBAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,SAAA;GAAA,eAAA;GAAA,cAAA;EAAA;EAAA,aAAA,CAAA;GAAA,cAAA;GAAA,OAAA;GAAA,WAAA,CAAA,WAAA;GAAA,aAAA;GAAA,UAAA;EAAA,CAAA;EAAA,UAAA;EAAA,UC1DhC;EAyDA,QAAA,CAAA,oiEAAA;EAAA,cAAA,CAAA;GAAA,MAAA;GAAA,MDrBY;GAAa,UAAA;GAAA,QAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;EAAA,CAAA;EAAA,eACR,CAAC,iBAAiB,CAAC,WAAW,WAAW,CAAC,CAAC;EAAC,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAqBhD,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAmB,YAAA,CAAA;EAzB/B,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAgB,YACd;GAAI,SACP,CAAC,aAAa;GAAC,eACT,CAAC,iBAAiB,CAAC,WAAW,WAAW,CAAC,CAAC;GAAC,iBAG1C,wBAAwB;GAAM,UAAA;GAAA,QAAA,CAAA,oiEAAA;EAAA,CAAA;CA+E2B,CAAA;CAAA,gBAAA;EAAA,QAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,cAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,UAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,MAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,eAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,cAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,qBAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA;IAAA,UAAA;IAAA,OAAA;IAAA,UAAA;GAAA,CAAA;EAAA,CAAA;EAAA,eAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,eAAA;EAAA,CAAA;EAAA,cAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,cAAA;EAAA,CAAA;EAAA,cAAA,CAAA;GAAA,MAAA,GAAA;GAAA,MAAA,CAAA,aAAW,EAAA,UAAA,KAAA,CAAA;EAAA,CAAA;CAAA;AAAA,CAAA"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-file-upload.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/file-upload/file-upload.component.ts","../../../../libs/angular-ui-kit/src/lib/file-upload/file-upload.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n output,\n signal,\n computed,\n ElementRef,\n viewChild,\n} from '@angular/core';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_FILE, ICON_X_MARK } from '@lifecorner/angular-ui-kit/icons';\n\nexport interface FileUploadFile {\n file: File;\n name: string;\n size: number;\n type: string;\n}\n\n/**\n * File upload / dropzone component with drag & drop support.\n *\n * @example\n * ```html\n * <lc-file-upload\n * accept=\".pdf,.png,.jpg\"\n * [multiple]=\"true\"\n * [maxSizeBytes]=\"5242880\"\n * (filesSelected)=\"onFiles($event)\"\n * ></lc-file-upload>\n * ```\n */\n@Component({\n selector: 'lc-file-upload',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_FILE, ICON_X_MARK])],\n templateUrl: './file-upload.component.html',\n styleUrls: ['./file-upload.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\n/**\n * File upload component with drag-and-drop support.\n *\n * Features:\n * - Drag-and-drop file dropzone with visual feedback\n * - Click-to-browse file selection\n * - File type and size validation\n * - Multi-file upload support\n * - File list display with removal\n * - Disabled state support\n *\n * @example\n * ```html\n * <lc-file-upload accept=\".pdf,.jpg\" [multiple]=\"true\" [maxSizeBytes]=\"5242880\" />\n * ```\n */\nexport class FileUploadComponent {\n /**\n * Accepted file types (e.g. \".pdf,.jpg,image/*\").\n */\n accept = input<string>('');\n\n /**\n * Allow multiple files.\n * @default false\n */\n multiple = input<boolean>(false);\n\n /**\n * Maximum file size in bytes. 0 for unlimited.\n * @default 0\n */\n maxSizeBytes = input<number>(0);\n\n /**\n * Whether the dropzone is disabled.\n * @default false\n */\n disabled = input<boolean>(false);\n\n /**\n * Hint text shown in the dropzone. The drop zone's accessible name is\n * `hint`, then the shown accepted and max lines, comma-separated (WCAG 2.5.3\n * Label in Name).\n * @default 'Drag & drop files here, or click to browse'\n */\n hint = input<string>('Drag & drop files here, or click to browse');\n\n /**\n * Prefix of the accepted file types line (`accept`). The shown line is part\n * of the drop zone's accessible name, after `hint`.\n * @default 'Accepted:'\n */\n acceptedLabel = input('Accepted:');\n\n /**\n * Prefix of the maximum file size line (`maxSizeBytes`). The shown line is\n * part of the drop zone's accessible name, after `hint`.\n * @default 'Max:'\n */\n maxSizeLabel = input('Max:');\n\n /** Accessible name of a selected file's remove button. */\n removeFileAriaLabel = input('Remove file');\n\n /**\n * Emitted with selected files after validation.\n */\n readonly filesSelected = output<FileUploadFile[]>();\n\n /**\n * Emitted when a file is rejected (too large or wrong type).\n */\n readonly fileRejected = output<{ file: File; reason: string }>();\n\n protected isDragOver = signal(false);\n protected selectedFiles = signal<FileUploadFile[]>([]);\n protected readonly fileInputRef = viewChild<ElementRef<HTMLInputElement>>('fileInput');\n\n protected dropzoneClasses = computed(() => {\n return [\n 'file-upload',\n this.isDragOver() ? 'file-upload--drag-over' : '',\n this.disabled() ? 'file-upload--disabled' : '',\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n /**\n * Name of the drop zone (AS-33, WCAG 2.5.3 Label in Name): `hint`, then the\n * accepted and max lines it shows, comma-separated.\n */\n protected readonly dropzoneAriaLabel = computed(() =>\n [\n this.hint(),\n this.accept() ? `${this.acceptedLabel()} ${this.accept()}` : '',\n this.maxSizeBytes() > 0 ? `${this.maxSizeLabel()} ${this.formatSize(this.maxSizeBytes())}` : '',\n ]\n .filter(Boolean)\n .join(', '),\n );\n\n protected onDragOver(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n if (!this.disabled()) {\n this.isDragOver.set(true);\n }\n }\n\n protected onDragLeave(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n this.isDragOver.set(false);\n }\n\n protected onDrop(event: DragEvent): void {\n event.preventDefault();\n event.stopPropagation();\n this.isDragOver.set(false);\n if (this.disabled() || !event.dataTransfer?.files.length) return;\n this.processFiles(event.dataTransfer.files);\n }\n\n protected onFileChange(event: Event): void {\n const input = event.target as HTMLInputElement;\n if (input.files?.length) {\n this.processFiles(input.files);\n }\n // Reset so the same file can be re-selected\n input.value = '';\n }\n\n protected openFilePicker(): void {\n if (!this.disabled()) {\n this.fileInputRef()?.nativeElement.click();\n }\n }\n\n protected removeFile(index: number): void {\n this.selectedFiles.update((files) => files.filter((_, i) => i !== index));\n this.filesSelected.emit(this.selectedFiles());\n }\n\n protected formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n }\n\n private processFiles(fileList: FileList): void {\n const files = Array.from(fileList);\n const accepted: FileUploadFile[] = [];\n\n for (const file of files) {\n if (this.maxSizeBytes() > 0 && file.size > this.maxSizeBytes()) {\n this.fileRejected.emit({ file, reason: 'File too large' });\n continue;\n }\n accepted.push({\n file,\n name: file.name,\n size: file.size,\n type: file.type,\n });\n }\n\n if (this.multiple()) {\n this.selectedFiles.update((prev) => [...prev, ...accepted]);\n } else {\n this.selectedFiles.set(accepted.slice(0, 1));\n }\n\n this.filesSelected.emit(this.selectedFiles());\n }\n}\n","<div\n [class]=\"dropzoneClasses()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n (click)=\"openFilePicker()\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openFilePicker()\"\n (keydown.space)=\"openFilePicker()\"\n [attr.aria-label]=\"dropzoneAriaLabel()\"\n [attr.aria-disabled]=\"disabled()\"\n>\n <input\n #fileInput\n type=\"file\"\n class=\"file-upload__input\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n [disabled]=\"disabled()\"\n (change)=\"onFileChange($event)\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n <div class=\"file-upload__content\">\n <lc-icon name=\"cloud-arrow-up\" class=\"file-upload__icon\" [decorative]=\"true\"></lc-icon>\n <!-- Real spaces between the lines, so the visible text reads as words\n that the drop zone's name contains (WCAG 2.5.3). -->\n <p class=\"file-upload__hint\">{{ hint() }}</p>\n @if (accept()) {\n &ngsp;<p class=\"file-upload__accept\">{{ acceptedLabel() }} {{ accept() }}</p>\n }\n @if (maxSizeBytes() > 0) {\n &ngsp;<p class=\"file-upload__accept\">{{ maxSizeLabel() }} {{ formatSize(maxSizeBytes()) }}</p>\n }\n </div>\n</div>\n\n@if (selectedFiles().length > 0) {\n <ul class=\"file-upload__list\">\n @for (f of selectedFiles(); track f.name; let i = $index) {\n <li class=\"file-upload__file\">\n <lc-icon name=\"document\" class=\"file-upload__file-icon\" [decorative]=\"true\"></lc-icon>\n <span class=\"file-upload__file-name\">{{ f.name }}</span>\n <span class=\"file-upload__file-size\">{{ formatSize(f.size) }}</span>\n <button\n type=\"button\"\n class=\"file-upload__file-remove\"\n (click)=\"removeFile(i); $event.stopPropagation()\"\n [attr.aria-label]=\"removeFileAriaLabel()\"\n >\n <lc-icon name=\"x-mark\" [decorative]=\"true\"></lc-icon>\n </button>\n </li>\n }\n </ul>\n}\n"],"mappings":"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"}
@@ -100,8 +100,8 @@ const ICON_ALIASES = {
100
100
  "minus-circle": "circle-minus"
101
101
  };
102
102
  const INLINE_HAND_KEYS = "home chart-bar-square light-bulb banknotes play-circle x-mark bars-3 globe-alt calendar hashtag sun moon user chevron-down chevron-up eye eye-slash check-circle exclamation-triangle information-circle exclamation-circle clock arrow-path building-library arrow-trending-up arrow-trending-down percent-badge presentation-chart-line bell-alert pencil play check document-duplicate chart-bar bolt star plus bell-slash";
103
- const INLINE_TABLER_OUTLINE_KEYS = "mail file-text file arrow-right arrow-left external-link world menu-2 x users user-plus settings logout dots wifi-off chevron-right chevron-left login key password shield-check robot plug-connected device-mobile device-desktop device-laptop copy share link trash info-circle circle-check alert-triangle alert-circle circle-x wallet building-bank shield-lock";
104
- const INLINE_TABLER_SOLID_KEYS = "mail file-text file external-link world menu-2 x settings dots chevron-right key shield-check device-mobile device-desktop copy link trash info-circle circle-check alert-triangle alert-circle circle-x shield-lock";
103
+ const INLINE_TABLER_OUTLINE_KEYS = "mail file-text file arrow-right arrow-left external-link world menu-2 x users user-plus settings logout dots wifi-off chevron-right chevron-left login key password shield-check robot plug-connected device-mobile device-desktop device-laptop copy share link trash info-circle circle-check alert-triangle alert-circle circle-x wallet building-bank shield-lock search lock device-tablet player-play player-pause clipboard arrow-down";
104
+ const INLINE_TABLER_SOLID_KEYS = "mail file-text file external-link world menu-2 x settings dots chevron-right key shield-check device-mobile device-desktop copy link trash info-circle circle-check alert-triangle alert-circle circle-x shield-lock search lock device-tablet player-play player-pause clipboard";
105
105
  let index;
106
106
  function inlineIndex() {
107
107
  if (!index) {