@lifecorner/angular-ui-kit 3.20.0 → 3.21.1

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 (29) hide show
  1. package/fesm2022/lifecorner-angular-ui-kit-bar-chart.mjs +67 -8
  2. package/fesm2022/lifecorner-angular-ui-kit-bar-chart.mjs.map +1 -1
  3. package/fesm2022/lifecorner-angular-ui-kit-card.mjs +2 -2
  4. package/fesm2022/lifecorner-angular-ui-kit-card.mjs.map +1 -1
  5. package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs +11 -11
  6. package/fesm2022/lifecorner-angular-ui-kit-dependency-viewer.mjs.map +1 -1
  7. package/fesm2022/lifecorner-angular-ui-kit-diff-viewer.mjs +2 -2
  8. package/fesm2022/lifecorner-angular-ui-kit-diff-viewer.mjs.map +1 -1
  9. package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs +2 -2
  10. package/fesm2022/lifecorner-angular-ui-kit-file-upload.mjs.map +1 -1
  11. package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs +6 -6
  12. package/fesm2022/lifecorner-angular-ui-kit-kanban-board.mjs.map +1 -1
  13. package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs +2 -2
  14. package/fesm2022/lifecorner-angular-ui-kit-log-viewer.mjs.map +1 -1
  15. package/fesm2022/lifecorner-angular-ui-kit-markdown.mjs +2 -2
  16. package/fesm2022/lifecorner-angular-ui-kit-markdown.mjs.map +1 -1
  17. package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs +2 -2
  18. package/fesm2022/lifecorner-angular-ui-kit-notification-center.mjs.map +1 -1
  19. package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs +2 -2
  20. package/fesm2022/lifecorner-angular-ui-kit-pipeline.mjs.map +1 -1
  21. package/fesm2022/lifecorner-angular-ui-kit-progress-bar.mjs +2 -2
  22. package/fesm2022/lifecorner-angular-ui-kit-progress-bar.mjs.map +1 -1
  23. package/fesm2022/lifecorner-angular-ui-kit-rating.mjs +2 -2
  24. package/fesm2022/lifecorner-angular-ui-kit-rating.mjs.map +1 -1
  25. package/fesm2022/lifecorner-angular-ui-kit-status-dot.mjs +2 -2
  26. package/fesm2022/lifecorner-angular-ui-kit-status-dot.mjs.map +1 -1
  27. package/package.json +1 -1
  28. package/src/styles/theme-contrast.spec.ts +239 -0
  29. package/types/lifecorner-angular-ui-kit-bar-chart.d.ts +105 -10
@@ -21,6 +21,7 @@ var BarChartComponent = class BarChartComponent {
21
21
  orientation = input("vertical", ...ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []);
22
22
  showValues = input(true, ...ngDevMode ? [{ debugName: "showValues" }] : /* istanbul ignore next */ []);
23
23
  showLabels = input(true, ...ngDevMode ? [{ debugName: "showLabels" }] : /* istanbul ignore next */ []);
24
+ labelLayout = input("axis", ...ngDevMode ? [{ debugName: "labelLayout" }] : /* istanbul ignore next */ []);
24
25
  showGrid = input(true, ...ngDevMode ? [{ debugName: "showGrid" }] : /* istanbul ignore next */ []);
25
26
  barGap = input(.3, ...ngDevMode ? [{ debugName: "barGap" }] : /* istanbul ignore next */ []);
26
27
  color = input("", ...ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []);
@@ -33,6 +34,13 @@ var BarChartComponent = class BarChartComponent {
33
34
  effectiveWidth = computed(() => this._containerWidth() || this.width() || DEFAULT_WIDTH, ...ngDevMode ? [{ debugName: "effectiveWidth" }] : /* istanbul ignore next */ []);
34
35
  viewBox = computed(() => `0 0 ${this.effectiveWidth()} ${this.height()}`, ...ngDevMode ? [{ debugName: "viewBox" }] : /* istanbul ignore next */ []);
35
36
  isVertical = computed(() => this.orientation() === "vertical", ...ngDevMode ? [{ debugName: "isVertical" }] : /* istanbul ignore next */ []);
37
+ wrapRows = computed(() => this.labelLayout() === "wrap" && !this.isVertical(), ...ngDevMode ? [{ debugName: "wrapRows" }] : /* istanbul ignore next */ []);
38
+ wrapColumns = computed(() => this.labelLayout() === "wrap" && this.isVertical(), ...ngDevMode ? [{ debugName: "wrapColumns" }] : /* istanbul ignore next */ []);
39
+ paddingBottom = computed(() => {
40
+ if (!this.wrapColumns()) return this.PADDING_BOTTOM;
41
+ const negative = (this.data() ?? []).some((i) => toFinite(i.value) < 0);
42
+ return this.showValues() && negative ? 18 : 2;
43
+ }, ...ngDevMode ? [{ debugName: "paddingBottom" }] : /* istanbul ignore next */ []);
36
44
  plot = computed(() => {
37
45
  const w = this.effectiveWidth();
38
46
  const h = this.height();
@@ -40,9 +48,12 @@ var BarChartComponent = class BarChartComponent {
40
48
  x: this.PADDING_LEFT,
41
49
  y: this.PADDING_TOP,
42
50
  w: w - this.PADDING_LEFT - this.PADDING_RIGHT,
43
- h: h - this.PADDING_TOP - this.PADDING_BOTTOM
51
+ h: h - this.PADDING_TOP - this.paddingBottom()
44
52
  };
45
53
  }, ...ngDevMode ? [{ debugName: "plot" }] : /* istanbul ignore next */ []);
54
+ textOf(item, value, fmt) {
55
+ return item.valueText ?? fmt(value, item);
56
+ }
46
57
  scale = computed(() => {
47
58
  const values = (this.data() ?? []).map((i) => toFinite(i.value));
48
59
  return niceScale(Math.min(0, ...values), Math.max(0, ...values), 4);
@@ -88,7 +99,7 @@ var BarChartComponent = class BarChartComponent {
88
99
  color: singleColor || item.color || chartColor(i),
89
100
  label: item.label || "",
90
101
  value,
91
- valueText: fmt(value),
102
+ valueText: this.textOf(item, value, fmt),
92
103
  labelX: p.x + i * totalBarWidth + totalBarWidth / 2,
93
104
  labelY: h - this.PADDING_BOTTOM + 16,
94
105
  valueX: p.x + i * totalBarWidth + totalBarWidth / 2,
@@ -113,7 +124,7 @@ var BarChartComponent = class BarChartComponent {
113
124
  color: singleColor || item.color || chartColor(i),
114
125
  label: item.label || "",
115
126
  value,
116
- valueText: fmt(value),
127
+ valueText: this.textOf(item, value, fmt),
117
128
  labelX: p.x - 6,
118
129
  labelY: p.y + i * totalBarHeight + totalBarHeight / 2,
119
130
  valueX: value >= 0 ? x + barW + 6 : x - 6,
@@ -139,13 +150,46 @@ var BarChartComponent = class BarChartComponent {
139
150
  };
140
151
  }, ...ngDevMode ? [{ debugName: "axisLine" }] : /* istanbul ignore next */ []);
141
152
  plotBottom = computed(() => this.plot().y + this.plot().h, ...ngDevMode ? [{ debugName: "plotBottom" }] : /* istanbul ignore next */ []);
153
+ rowBounds = computed(() => {
154
+ const values = (this.data() ?? []).map((i) => toFinite(i.value));
155
+ const lo = Math.min(0, ...values);
156
+ return {
157
+ values,
158
+ lo,
159
+ range: Math.max(0, ...values) - lo || 1
160
+ };
161
+ }, ...ngDevMode ? [{ debugName: "rowBounds" }] : /* istanbul ignore next */ []);
162
+ rows = computed(() => {
163
+ const d = this.data() ?? [];
164
+ const { values, lo, range } = this.rowBounds();
165
+ const singleColor = this.color();
166
+ const fmt = this.formatValue();
167
+ return d.map((item, i) => {
168
+ const value = values[i];
169
+ return {
170
+ label: item.label || "",
171
+ valueText: this.textOf(item, value, fmt),
172
+ color: singleColor || item.color || chartColor(i),
173
+ x: `${(Math.min(value, 0) - lo) / range * 100}%`,
174
+ width: `${Math.abs(value) / range * 100}%`
175
+ };
176
+ });
177
+ }, ...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
178
+ rowZero = computed(() => {
179
+ const { lo, range } = this.rowBounds();
180
+ return lo < 0 ? `${-lo / range * 100}%` : null;
181
+ }, ...ngDevMode ? [{ debugName: "rowZero" }] : /* istanbul ignore next */ []);
142
182
  effectiveAriaLabel = computed(() => {
143
183
  const explicit = this.ariaLabel();
144
184
  if (explicit) return explicit;
145
185
  const d = this.data() ?? [];
146
186
  if (!d.length) return "Bar chart: no data";
147
187
  const fmt = this.formatValue();
148
- return `Bar chart: ${d.map((item, i) => `${item.label || `Bar ${i + 1}`} ${fmt(toFinite(item.value))}`).join(", ")}`;
188
+ return `Bar chart: ${d.map((item, i) => {
189
+ const label = item.label || `Bar ${i + 1}`;
190
+ const text = this.textOf(item, toFinite(item.value), fmt);
191
+ return text === "" ? label : `${label} ${text}`;
192
+ }).join(", ")}`;
149
193
  }, ...ngDevMode ? [{ debugName: "effectiveAriaLabel" }] : /* istanbul ignore next */ []);
150
194
  static ɵfac = i0.ɵɵngDeclareFactory({
151
195
  minVersion: "12.0.0",
@@ -204,6 +248,13 @@ var BarChartComponent = class BarChartComponent {
204
248
  isRequired: false,
205
249
  transformFunction: null
206
250
  },
251
+ labelLayout: {
252
+ classPropertyName: "labelLayout",
253
+ publicName: "labelLayout",
254
+ isSignal: true,
255
+ isRequired: false,
256
+ transformFunction: null
257
+ },
207
258
  showGrid: {
208
259
  classPropertyName: "showGrid",
209
260
  publicName: "showGrid",
@@ -242,8 +293,8 @@ var BarChartComponent = class BarChartComponent {
242
293
  },
243
294
  host: { properties: { "style.max-width.px": "width()" } },
244
295
  ngImport: i0,
245
- template: "<svg\n width=\"100%\"\n [attr.height]=\"height()\"\n [attr.viewBox]=\"viewBox()\"\n role=\"img\"\n [attr.aria-label]=\"effectiveAriaLabel()\"\n class=\"lc-bar-chart\"\n>\n <title>{{ effectiveAriaLabel() }}</title>\n\n <!-- Grid lines -->\n @if (showGrid()) {\n @for (line of gridLines(); track line.pos) {\n @if (isVertical()) {\n <line\n [attr.x1]=\"axisLine().bx1\"\n [attr.y1]=\"line.pos\"\n [attr.x2]=\"axisLine().bx2\"\n [attr.y2]=\"line.pos\"\n class=\"lc-bar-chart__grid-line\"\n />\n <text\n [attr.x]=\"axisLine().x1 - 6\"\n [attr.y]=\"line.pos\"\n text-anchor=\"end\"\n dominant-baseline=\"central\"\n class=\"lc-bar-chart__grid-label\"\n >\n {{ line.label }}\n </text>\n } @else {\n <line\n [attr.x1]=\"line.pos\"\n [attr.y1]=\"axisLine().y1\"\n [attr.x2]=\"line.pos\"\n [attr.y2]=\"axisLine().y2\"\n class=\"lc-bar-chart__grid-line\"\n />\n <text\n [attr.x]=\"line.pos\"\n [attr.y]=\"plotBottom() + 12\"\n text-anchor=\"middle\"\n class=\"lc-bar-chart__grid-label\"\n >\n {{ line.label }}\n </text>\n }\n }\n }\n\n <!-- Axes -->\n <line\n [attr.x1]=\"axisLine().x1\"\n [attr.y1]=\"axisLine().y1\"\n [attr.x2]=\"axisLine().x2\"\n [attr.y2]=\"axisLine().y2\"\n class=\"lc-bar-chart__axis\"\n />\n <line\n [attr.x1]=\"axisLine().bx1\"\n [attr.y1]=\"axisLine().by1\"\n [attr.x2]=\"axisLine().bx2\"\n [attr.y2]=\"axisLine().by2\"\n class=\"lc-bar-chart__axis\"\n />\n\n <!-- Bars -->\n @for (bar of bars(); track $index) {\n <rect\n [attr.x]=\"bar.x\"\n [attr.y]=\"bar.y\"\n [attr.width]=\"bar.width\"\n [attr.height]=\"bar.height\"\n [attr.fill]=\"bar.color\"\n rx=\"2\"\n class=\"lc-bar-chart__bar\"\n />\n\n @if (showValues()) {\n <text\n [attr.x]=\"bar.valueX\"\n [attr.y]=\"bar.valueY\"\n [attr.text-anchor]=\"bar.valueAnchor\"\n [attr.dominant-baseline]=\"isVertical() ? 'auto' : 'central'\"\n class=\"lc-bar-chart__value\"\n >\n {{ bar.valueText }}\n </text>\n }\n\n @if (showLabels()) {\n <text\n [attr.x]=\"bar.labelX\"\n [attr.y]=\"bar.labelY\"\n [attr.text-anchor]=\"isVertical() ? 'middle' : 'end'\"\n [attr.dominant-baseline]=\"isVertical() ? 'auto' : 'central'\"\n class=\"lc-bar-chart__label\"\n >\n {{ bar.label }}\n </text>\n }\n }\n</svg>\n",
246
- styles: [":host{display:block;width:100%}.lc-bar-chart{overflow:visible}.lc-bar-chart__axis{stroke:var(--color-border);stroke-width:1}.lc-bar-chart__grid-line{stroke:var(--color-border);stroke-width:1;stroke-dasharray:3 3}.lc-bar-chart__grid-label{font-size:.625rem;fill:var(--color-text-tertiary)}.lc-bar-chart__bar{transition:opacity .15s ease}.lc-bar-chart__bar:hover{opacity:.8}.lc-bar-chart__value{font-size:.625rem;font-weight:600;fill:var(--color-text-secondary)}.lc-bar-chart__label{font-size:.6875rem;fill:var(--color-text-secondary)}\n"],
296
+ template: "@if (wrapRows()) {\n <!--\n Horizontal 'wrap' (3.21, request 028): HTML rows, so labels wrap and follow\n the text size. Bar x/width are percentages: final in the server HTML.\n No inline style (CSP). The children of role=\"img\" are presentational.\n -->\n <div\n class=\"lc-bar-chart lc-bar-chart--rows\"\n role=\"img\"\n [attr.aria-label]=\"effectiveAriaLabel()\"\n >\n @for (row of rows(); track $index) {\n <div class=\"lc-bar-chart__row\">\n @if ((showLabels() && row.label) || (showValues() && row.valueText)) {\n <div class=\"lc-bar-chart__row-head\">\n @if (showLabels() && row.label) {\n <span class=\"lc-bar-chart__row-label\">{{ row.label }}</span>\n }\n @if (showValues() && row.valueText) {\n <span class=\"lc-bar-chart__row-value\">{{ row.valueText }}</span>\n }\n </div>\n }\n <svg\n width=\"100%\"\n aria-hidden=\"true\"\n focusable=\"false\"\n class=\"lc-bar-chart__row-track\"\n >\n @if (rowZero(); as zero) {\n <line\n [attr.x1]=\"zero\"\n y1=\"-25%\"\n [attr.x2]=\"zero\"\n y2=\"125%\"\n class=\"lc-bar-chart__zero-line\"\n />\n }\n <rect\n [attr.x]=\"row.x\"\n y=\"0\"\n [attr.width]=\"row.width\"\n height=\"100%\"\n [attr.fill]=\"row.color\"\n rx=\"2\"\n class=\"lc-bar-chart__row-bar\"\n />\n </svg>\n </div>\n }\n </div>\n} @else {\n <svg\n width=\"100%\"\n [attr.height]=\"height()\"\n [attr.viewBox]=\"viewBox()\"\n role=\"img\"\n [attr.aria-label]=\"effectiveAriaLabel()\"\n class=\"lc-bar-chart\"\n >\n <title>{{ effectiveAriaLabel() }}</title>\n\n <!-- Grid lines -->\n @if (showGrid()) {\n @for (line of gridLines(); track line.pos) {\n @if (isVertical()) {\n <line\n [attr.x1]=\"axisLine().bx1\"\n [attr.y1]=\"line.pos\"\n [attr.x2]=\"axisLine().bx2\"\n [attr.y2]=\"line.pos\"\n class=\"lc-bar-chart__grid-line\"\n />\n <text\n [attr.x]=\"axisLine().x1 - 6\"\n [attr.y]=\"line.pos\"\n text-anchor=\"end\"\n dominant-baseline=\"central\"\n class=\"lc-bar-chart__grid-label\"\n >\n {{ line.label }}\n </text>\n } @else {\n <line\n [attr.x1]=\"line.pos\"\n [attr.y1]=\"axisLine().y1\"\n [attr.x2]=\"line.pos\"\n [attr.y2]=\"axisLine().y2\"\n class=\"lc-bar-chart__grid-line\"\n />\n <text\n [attr.x]=\"line.pos\"\n [attr.y]=\"plotBottom() + 12\"\n text-anchor=\"middle\"\n class=\"lc-bar-chart__grid-label\"\n >\n {{ line.label }}\n </text>\n }\n }\n }\n\n <!-- Axes -->\n <line\n [attr.x1]=\"axisLine().x1\"\n [attr.y1]=\"axisLine().y1\"\n [attr.x2]=\"axisLine().x2\"\n [attr.y2]=\"axisLine().y2\"\n class=\"lc-bar-chart__axis\"\n />\n <line\n [attr.x1]=\"axisLine().bx1\"\n [attr.y1]=\"axisLine().by1\"\n [attr.x2]=\"axisLine().bx2\"\n [attr.y2]=\"axisLine().by2\"\n class=\"lc-bar-chart__axis\"\n />\n\n <!-- Bars -->\n @for (bar of bars(); track $index) {\n <rect\n [attr.x]=\"bar.x\"\n [attr.y]=\"bar.y\"\n [attr.width]=\"bar.width\"\n [attr.height]=\"bar.height\"\n [attr.fill]=\"bar.color\"\n rx=\"2\"\n class=\"lc-bar-chart__bar\"\n />\n\n @if (showValues()) {\n <text\n [attr.x]=\"bar.valueX\"\n [attr.y]=\"bar.valueY\"\n [attr.text-anchor]=\"bar.valueAnchor\"\n [attr.dominant-baseline]=\"isVertical() ? 'auto' : 'central'\"\n class=\"lc-bar-chart__value\"\n >\n {{ bar.valueText }}\n </text>\n }\n\n <!-- One block (not nested), so the SVG's comment anchors stay as before 3.21. -->\n @if (showLabels() && !wrapColumns()) {\n <text\n [attr.x]=\"bar.labelX\"\n [attr.y]=\"bar.labelY\"\n [attr.text-anchor]=\"isVertical() ? 'middle' : 'end'\"\n [attr.dominant-baseline]=\"isVertical() ? 'auto' : 'central'\"\n class=\"lc-bar-chart__label\"\n >\n {{ bar.label }}\n </text>\n }\n }\n </svg>\n\n <!--\n Vertical 'wrap' (3.21, request 028): the category labels as HTML under the\n plot, one cell per column, wrapping within it. Hidden from assistive\n technology: the names are in the SVG's accessible name.\n -->\n @if (wrapColumns() && showLabels()) {\n <div class=\"lc-bar-chart__category-labels\" aria-hidden=\"true\">\n @for (bar of bars(); track $index) {\n <span class=\"lc-bar-chart__category-label\">{{ bar.label }}</span>\n }\n </div>\n }\n}\n",
297
+ styles: [":host{display:block;width:100%}.lc-bar-chart{overflow:visible}.lc-bar-chart__axis{stroke:var(--color-border);stroke-width:1}.lc-bar-chart__grid-line{stroke:var(--color-border);stroke-width:1;stroke-dasharray:3 3}.lc-bar-chart__grid-label{font-size:.625rem;fill:var(--color-text-tertiary)}.lc-bar-chart__bar{transition:opacity .15s ease}.lc-bar-chart__bar:hover{opacity:.8}.lc-bar-chart__value{font-size:.625rem;font-weight:600;fill:var(--color-text-secondary)}.lc-bar-chart__label{font-size:.6875rem;fill:var(--color-text-secondary)}.lc-bar-chart--rows{display:flex;flex-direction:column;gap:var(--lc-bar-chart-row-gap, var(--spacing-4))}.lc-bar-chart__row{display:flex;flex-direction:column;gap:var(--spacing-1)}.lc-bar-chart__row-head{display:flex;flex-wrap:wrap;align-items:baseline;column-gap:var(--spacing-2);font-size:var(--typography-font-size-sm);line-height:var(--typography-line-height-normal)}.lc-bar-chart__row-label{flex:1 1 auto;min-width:0;color:var(--color-text-primary);font-weight:var(--typography-font-weight-normal);overflow-wrap:break-word;hyphens:auto;hyphenate-limit-chars:12 4 3}.lc-bar-chart__row-value{margin-inline-start:auto;min-width:0;text-align:end;color:var(--color-text-primary);font-weight:var(--typography-font-weight-semibold);font-variant-numeric:tabular-nums;overflow-wrap:break-word}.lc-bar-chart__row-track{display:block;width:100%;height:var(--lc-bar-chart-bar-size, .75rem);overflow:visible}.lc-bar-chart__zero-line{stroke:var(--color-text-tertiary);stroke-width:1}.lc-bar-chart__category-labels{display:flex;padding:var(--spacing-1) 10px 0 40px}.lc-bar-chart__category-label{flex:1 1 0%;min-width:0;padding-inline:2px;text-align:center;font-size:.6875rem;line-height:var(--typography-line-height-tight);color:var(--color-text-secondary);overflow-wrap:break-word;hyphens:auto;hyphenate-limit-chars:12 4 3}@media(forced-colors:active){.lc-bar-chart__row-track{forced-color-adjust:none}.lc-bar-chart__row-bar{fill:Highlight}.lc-bar-chart__zero-line{stroke:CanvasText}}\n"],
247
298
  changeDetection: i0.ChangeDetectionStrategy.OnPush
248
299
  });
249
300
  };
@@ -259,8 +310,8 @@ i0.ɵɵngDeclareClassMetadata({
259
310
  standalone: true,
260
311
  changeDetection: ChangeDetectionStrategy.OnPush,
261
312
  host: { "[style.max-width.px]": "width()" },
262
- template: "<svg\n width=\"100%\"\n [attr.height]=\"height()\"\n [attr.viewBox]=\"viewBox()\"\n role=\"img\"\n [attr.aria-label]=\"effectiveAriaLabel()\"\n class=\"lc-bar-chart\"\n>\n <title>{{ effectiveAriaLabel() }}</title>\n\n <!-- Grid lines -->\n @if (showGrid()) {\n @for (line of gridLines(); track line.pos) {\n @if (isVertical()) {\n <line\n [attr.x1]=\"axisLine().bx1\"\n [attr.y1]=\"line.pos\"\n [attr.x2]=\"axisLine().bx2\"\n [attr.y2]=\"line.pos\"\n class=\"lc-bar-chart__grid-line\"\n />\n <text\n [attr.x]=\"axisLine().x1 - 6\"\n [attr.y]=\"line.pos\"\n text-anchor=\"end\"\n dominant-baseline=\"central\"\n class=\"lc-bar-chart__grid-label\"\n >\n {{ line.label }}\n </text>\n } @else {\n <line\n [attr.x1]=\"line.pos\"\n [attr.y1]=\"axisLine().y1\"\n [attr.x2]=\"line.pos\"\n [attr.y2]=\"axisLine().y2\"\n class=\"lc-bar-chart__grid-line\"\n />\n <text\n [attr.x]=\"line.pos\"\n [attr.y]=\"plotBottom() + 12\"\n text-anchor=\"middle\"\n class=\"lc-bar-chart__grid-label\"\n >\n {{ line.label }}\n </text>\n }\n }\n }\n\n <!-- Axes -->\n <line\n [attr.x1]=\"axisLine().x1\"\n [attr.y1]=\"axisLine().y1\"\n [attr.x2]=\"axisLine().x2\"\n [attr.y2]=\"axisLine().y2\"\n class=\"lc-bar-chart__axis\"\n />\n <line\n [attr.x1]=\"axisLine().bx1\"\n [attr.y1]=\"axisLine().by1\"\n [attr.x2]=\"axisLine().bx2\"\n [attr.y2]=\"axisLine().by2\"\n class=\"lc-bar-chart__axis\"\n />\n\n <!-- Bars -->\n @for (bar of bars(); track $index) {\n <rect\n [attr.x]=\"bar.x\"\n [attr.y]=\"bar.y\"\n [attr.width]=\"bar.width\"\n [attr.height]=\"bar.height\"\n [attr.fill]=\"bar.color\"\n rx=\"2\"\n class=\"lc-bar-chart__bar\"\n />\n\n @if (showValues()) {\n <text\n [attr.x]=\"bar.valueX\"\n [attr.y]=\"bar.valueY\"\n [attr.text-anchor]=\"bar.valueAnchor\"\n [attr.dominant-baseline]=\"isVertical() ? 'auto' : 'central'\"\n class=\"lc-bar-chart__value\"\n >\n {{ bar.valueText }}\n </text>\n }\n\n @if (showLabels()) {\n <text\n [attr.x]=\"bar.labelX\"\n [attr.y]=\"bar.labelY\"\n [attr.text-anchor]=\"isVertical() ? 'middle' : 'end'\"\n [attr.dominant-baseline]=\"isVertical() ? 'auto' : 'central'\"\n class=\"lc-bar-chart__label\"\n >\n {{ bar.label }}\n </text>\n }\n }\n</svg>\n",
263
- styles: [":host{display:block;width:100%}.lc-bar-chart{overflow:visible}.lc-bar-chart__axis{stroke:var(--color-border);stroke-width:1}.lc-bar-chart__grid-line{stroke:var(--color-border);stroke-width:1;stroke-dasharray:3 3}.lc-bar-chart__grid-label{font-size:.625rem;fill:var(--color-text-tertiary)}.lc-bar-chart__bar{transition:opacity .15s ease}.lc-bar-chart__bar:hover{opacity:.8}.lc-bar-chart__value{font-size:.625rem;font-weight:600;fill:var(--color-text-secondary)}.lc-bar-chart__label{font-size:.6875rem;fill:var(--color-text-secondary)}\n"]
313
+ template: "@if (wrapRows()) {\n <!--\n Horizontal 'wrap' (3.21, request 028): HTML rows, so labels wrap and follow\n the text size. Bar x/width are percentages: final in the server HTML.\n No inline style (CSP). The children of role=\"img\" are presentational.\n -->\n <div\n class=\"lc-bar-chart lc-bar-chart--rows\"\n role=\"img\"\n [attr.aria-label]=\"effectiveAriaLabel()\"\n >\n @for (row of rows(); track $index) {\n <div class=\"lc-bar-chart__row\">\n @if ((showLabels() && row.label) || (showValues() && row.valueText)) {\n <div class=\"lc-bar-chart__row-head\">\n @if (showLabels() && row.label) {\n <span class=\"lc-bar-chart__row-label\">{{ row.label }}</span>\n }\n @if (showValues() && row.valueText) {\n <span class=\"lc-bar-chart__row-value\">{{ row.valueText }}</span>\n }\n </div>\n }\n <svg\n width=\"100%\"\n aria-hidden=\"true\"\n focusable=\"false\"\n class=\"lc-bar-chart__row-track\"\n >\n @if (rowZero(); as zero) {\n <line\n [attr.x1]=\"zero\"\n y1=\"-25%\"\n [attr.x2]=\"zero\"\n y2=\"125%\"\n class=\"lc-bar-chart__zero-line\"\n />\n }\n <rect\n [attr.x]=\"row.x\"\n y=\"0\"\n [attr.width]=\"row.width\"\n height=\"100%\"\n [attr.fill]=\"row.color\"\n rx=\"2\"\n class=\"lc-bar-chart__row-bar\"\n />\n </svg>\n </div>\n }\n </div>\n} @else {\n <svg\n width=\"100%\"\n [attr.height]=\"height()\"\n [attr.viewBox]=\"viewBox()\"\n role=\"img\"\n [attr.aria-label]=\"effectiveAriaLabel()\"\n class=\"lc-bar-chart\"\n >\n <title>{{ effectiveAriaLabel() }}</title>\n\n <!-- Grid lines -->\n @if (showGrid()) {\n @for (line of gridLines(); track line.pos) {\n @if (isVertical()) {\n <line\n [attr.x1]=\"axisLine().bx1\"\n [attr.y1]=\"line.pos\"\n [attr.x2]=\"axisLine().bx2\"\n [attr.y2]=\"line.pos\"\n class=\"lc-bar-chart__grid-line\"\n />\n <text\n [attr.x]=\"axisLine().x1 - 6\"\n [attr.y]=\"line.pos\"\n text-anchor=\"end\"\n dominant-baseline=\"central\"\n class=\"lc-bar-chart__grid-label\"\n >\n {{ line.label }}\n </text>\n } @else {\n <line\n [attr.x1]=\"line.pos\"\n [attr.y1]=\"axisLine().y1\"\n [attr.x2]=\"line.pos\"\n [attr.y2]=\"axisLine().y2\"\n class=\"lc-bar-chart__grid-line\"\n />\n <text\n [attr.x]=\"line.pos\"\n [attr.y]=\"plotBottom() + 12\"\n text-anchor=\"middle\"\n class=\"lc-bar-chart__grid-label\"\n >\n {{ line.label }}\n </text>\n }\n }\n }\n\n <!-- Axes -->\n <line\n [attr.x1]=\"axisLine().x1\"\n [attr.y1]=\"axisLine().y1\"\n [attr.x2]=\"axisLine().x2\"\n [attr.y2]=\"axisLine().y2\"\n class=\"lc-bar-chart__axis\"\n />\n <line\n [attr.x1]=\"axisLine().bx1\"\n [attr.y1]=\"axisLine().by1\"\n [attr.x2]=\"axisLine().bx2\"\n [attr.y2]=\"axisLine().by2\"\n class=\"lc-bar-chart__axis\"\n />\n\n <!-- Bars -->\n @for (bar of bars(); track $index) {\n <rect\n [attr.x]=\"bar.x\"\n [attr.y]=\"bar.y\"\n [attr.width]=\"bar.width\"\n [attr.height]=\"bar.height\"\n [attr.fill]=\"bar.color\"\n rx=\"2\"\n class=\"lc-bar-chart__bar\"\n />\n\n @if (showValues()) {\n <text\n [attr.x]=\"bar.valueX\"\n [attr.y]=\"bar.valueY\"\n [attr.text-anchor]=\"bar.valueAnchor\"\n [attr.dominant-baseline]=\"isVertical() ? 'auto' : 'central'\"\n class=\"lc-bar-chart__value\"\n >\n {{ bar.valueText }}\n </text>\n }\n\n <!-- One block (not nested), so the SVG's comment anchors stay as before 3.21. -->\n @if (showLabels() && !wrapColumns()) {\n <text\n [attr.x]=\"bar.labelX\"\n [attr.y]=\"bar.labelY\"\n [attr.text-anchor]=\"isVertical() ? 'middle' : 'end'\"\n [attr.dominant-baseline]=\"isVertical() ? 'auto' : 'central'\"\n class=\"lc-bar-chart__label\"\n >\n {{ bar.label }}\n </text>\n }\n }\n </svg>\n\n <!--\n Vertical 'wrap' (3.21, request 028): the category labels as HTML under the\n plot, one cell per column, wrapping within it. Hidden from assistive\n technology: the names are in the SVG's accessible name.\n -->\n @if (wrapColumns() && showLabels()) {\n <div class=\"lc-bar-chart__category-labels\" aria-hidden=\"true\">\n @for (bar of bars(); track $index) {\n <span class=\"lc-bar-chart__category-label\">{{ bar.label }}</span>\n }\n </div>\n }\n}\n",
314
+ styles: [":host{display:block;width:100%}.lc-bar-chart{overflow:visible}.lc-bar-chart__axis{stroke:var(--color-border);stroke-width:1}.lc-bar-chart__grid-line{stroke:var(--color-border);stroke-width:1;stroke-dasharray:3 3}.lc-bar-chart__grid-label{font-size:.625rem;fill:var(--color-text-tertiary)}.lc-bar-chart__bar{transition:opacity .15s ease}.lc-bar-chart__bar:hover{opacity:.8}.lc-bar-chart__value{font-size:.625rem;font-weight:600;fill:var(--color-text-secondary)}.lc-bar-chart__label{font-size:.6875rem;fill:var(--color-text-secondary)}.lc-bar-chart--rows{display:flex;flex-direction:column;gap:var(--lc-bar-chart-row-gap, var(--spacing-4))}.lc-bar-chart__row{display:flex;flex-direction:column;gap:var(--spacing-1)}.lc-bar-chart__row-head{display:flex;flex-wrap:wrap;align-items:baseline;column-gap:var(--spacing-2);font-size:var(--typography-font-size-sm);line-height:var(--typography-line-height-normal)}.lc-bar-chart__row-label{flex:1 1 auto;min-width:0;color:var(--color-text-primary);font-weight:var(--typography-font-weight-normal);overflow-wrap:break-word;hyphens:auto;hyphenate-limit-chars:12 4 3}.lc-bar-chart__row-value{margin-inline-start:auto;min-width:0;text-align:end;color:var(--color-text-primary);font-weight:var(--typography-font-weight-semibold);font-variant-numeric:tabular-nums;overflow-wrap:break-word}.lc-bar-chart__row-track{display:block;width:100%;height:var(--lc-bar-chart-bar-size, .75rem);overflow:visible}.lc-bar-chart__zero-line{stroke:var(--color-text-tertiary);stroke-width:1}.lc-bar-chart__category-labels{display:flex;padding:var(--spacing-1) 10px 0 40px}.lc-bar-chart__category-label{flex:1 1 0%;min-width:0;padding-inline:2px;text-align:center;font-size:.6875rem;line-height:var(--typography-line-height-tight);color:var(--color-text-secondary);overflow-wrap:break-word;hyphens:auto;hyphenate-limit-chars:12 4 3}@media(forced-colors:active){.lc-bar-chart__row-track{forced-color-adjust:none}.lc-bar-chart__row-bar{fill:Highlight}.lc-bar-chart__zero-line{stroke:CanvasText}}\n"]
264
315
  }]
265
316
  }],
266
317
  ctorParameters: () => [],
@@ -313,6 +364,14 @@ i0.ɵɵngDeclareClassMetadata({
313
364
  required: false
314
365
  }]
315
366
  }],
367
+ labelLayout: [{
368
+ type: i0.Input,
369
+ args: [{
370
+ isSignal: true,
371
+ alias: "labelLayout",
372
+ required: false
373
+ }]
374
+ }],
316
375
  showGrid: [{
317
376
  type: i0.Input,
318
377
  args: [{
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-bar-chart.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/bar-chart/bar-chart.component.ts","../../../../libs/angular-ui-kit/src/lib/bar-chart/bar-chart.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n ElementRef,\n afterNextRender,\n inject,\n input,\n computed,\n signal,\n} from '@angular/core';\nimport { chartColor } from '@lifecorner/angular-ui-kit/internal/chart';\nimport {\n ChartValueFormatter,\n formatChartValue,\n niceScale,\n toFinite,\n} from '@lifecorner/angular-ui-kit/internal/chart';\n\nexport interface BarChartItem {\n /** Bar value. Negative values draw downward (left) from the zero baseline. */\n value: number;\n label?: string;\n color?: string;\n}\n\nexport type BarChartOrientation = 'vertical' | 'horizontal';\n\n/** viewBox width used until the container has been measured. */\nconst DEFAULT_WIDTH = 400;\n\n@Component({\n selector: 'lc-bar-chart',\n standalone: true,\n templateUrl: './bar-chart.component.html',\n styleUrls: ['./bar-chart.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[style.max-width.px]': 'width()',\n },\n})\n/**\n * Bar chart component for comparing categorical data.\n *\n * Features:\n * - Vertical and horizontal orientation\n * - Per-bar or uniform color support\n * - Negative values drawn from a zero baseline\n * - Optional value labels on bars\n * - Configurable grid and axis labels on a nice tick scale\n * - Adjustable bar gap spacing\n * - Responsive SVG rendering\n *\n * @example\n * ```html\n * <lc-bar-chart [data]=\"items\" orientation=\"vertical\" [showValues]=\"true\" />\n * ```\n */\nexport class BarChartComponent {\n private readonly _el = inject(ElementRef);\n private readonly _destroyRef = inject(DestroyRef);\n private readonly _containerWidth = signal<number>(0);\n\n constructor() {\n afterNextRender(() => {\n const obs = new ResizeObserver(([entry]) => {\n this._containerWidth.set(entry.contentRect.width);\n });\n obs.observe(this._el.nativeElement);\n this._destroyRef.onDestroy(() => obs.disconnect());\n });\n }\n\n /** Data items. */\n data = input.required<BarChartItem[]>();\n\n /**\n * Intrinsic chart width in pixels. The chart fills its container (the SVG is\n * `width=\"100%\"` and its viewBox follows the measured container width);\n * `width` is the viewBox width until the container has been measured and,\n * when set, the host's `max-width`, so the chart never grows past it. Leave\n * unset for a fully fluid chart.\n */\n width = input<number | undefined>(undefined);\n\n /** Chart height in pixels. */\n height = input<number>(200);\n\n /** Bar orientation. */\n orientation = input<BarChartOrientation>('vertical');\n\n /** Show value labels on bars. */\n showValues = input<boolean>(true);\n\n /** Show axis labels. */\n showLabels = input<boolean>(true);\n\n /** Show grid lines. */\n showGrid = input<boolean>(true);\n\n /** Gap between bars as ratio (0-1). */\n barGap = input<number>(0.3);\n\n /** Use a single color for all bars. */\n color = input<string>('');\n\n /** Formats value labels and axis ticks. Defaults to a float-safe `String(value)`. */\n formatValue = input<ChartValueFormatter>(formatChartValue);\n\n /**\n * Accessible name of the chart. Defaults to a generated summary that lists\n * every bar's label and value, so assistive technology hears the data and\n * not just \"Bar chart\".\n */\n ariaLabel = input<string>('');\n\n // Layout constants\n private readonly PADDING_LEFT = 40;\n private readonly PADDING_RIGHT = 10;\n private readonly PADDING_TOP = 10;\n private readonly PADDING_BOTTOM = 30;\n\n protected readonly effectiveWidth = computed(\n () => this._containerWidth() || this.width() || DEFAULT_WIDTH\n );\n\n protected readonly viewBox = computed(\n () => `0 0 ${this.effectiveWidth()} ${this.height()}`\n );\n\n protected readonly isVertical = computed(() => this.orientation() === 'vertical');\n\n private readonly plot = computed(() => {\n const w = this.effectiveWidth();\n const h = this.height();\n return {\n x: this.PADDING_LEFT,\n y: this.PADDING_TOP,\n w: w - this.PADDING_LEFT - this.PADDING_RIGHT,\n h: h - this.PADDING_TOP - this.PADDING_BOTTOM,\n };\n });\n\n /**\n * One scale for grid *and* bars: bounds are nice multiples of the tick step,\n * always include zero, and extend below zero when the data does — so the top\n * tick is the value at the plot edge and negative bars hang off the baseline.\n */\n protected readonly scale = computed(() => {\n const values = (this.data() ?? []).map((i) => toFinite(i.value));\n return niceScale(Math.min(0, ...values), Math.max(0, ...values), 4);\n });\n\n /** Maps a data value to a pixel position along the value axis. */\n private valueToPos(value: number): number {\n const { min, max } = this.scale();\n const p = this.plot();\n const frac = (value - min) / (max - min || 1);\n return this.isVertical() ? p.y + p.h - frac * p.h : p.x + frac * p.w;\n }\n\n protected readonly gridLines = computed(() => {\n if (!this.showGrid() || !this.data()?.length) return [];\n const fmt = this.formatValue();\n return this.scale().ticks.map((tick) => ({\n pos: this.valueToPos(tick),\n label: fmt(tick),\n }));\n });\n\n protected readonly bars = computed(() => {\n const d = this.data();\n if (!d || d.length === 0) return [];\n\n const isVertical = this.isVertical();\n const p = this.plot();\n const gap = this.barGap();\n const singleColor = this.color();\n const fmt = this.formatValue();\n const zero = this.valueToPos(0);\n const h = this.height();\n\n if (isVertical) {\n const totalBarWidth = p.w / d.length;\n const barWidth = totalBarWidth * (1 - gap);\n const barOffset = (totalBarWidth - barWidth) / 2;\n\n return d.map((item, i) => {\n const value = toFinite(item.value);\n const end = this.valueToPos(value);\n const y = Math.min(zero, end);\n const barH = Math.abs(end - zero);\n return {\n x: p.x + i * totalBarWidth + barOffset,\n y,\n width: barWidth,\n height: barH,\n color: singleColor || item.color || chartColor(i),\n label: item.label || '',\n value,\n valueText: fmt(value),\n labelX: p.x + i * totalBarWidth + totalBarWidth / 2,\n labelY: h - this.PADDING_BOTTOM + 16,\n valueX: p.x + i * totalBarWidth + totalBarWidth / 2,\n // Above a positive bar, below a negative one.\n valueY: value >= 0 ? y - 6 : y + barH + 12,\n valueAnchor: 'middle',\n };\n });\n } else {\n const totalBarHeight = p.h / d.length;\n const barHeight = totalBarHeight * (1 - gap);\n const barOffset = (totalBarHeight - barHeight) / 2;\n\n return d.map((item, i) => {\n const value = toFinite(item.value);\n const end = this.valueToPos(value);\n const x = Math.min(zero, end);\n const barW = Math.abs(end - zero);\n return {\n x,\n y: p.y + i * totalBarHeight + barOffset,\n width: barW,\n height: barHeight,\n color: singleColor || item.color || chartColor(i),\n label: item.label || '',\n value,\n valueText: fmt(value),\n labelX: p.x - 6,\n labelY: p.y + i * totalBarHeight + totalBarHeight / 2,\n // Right of a positive bar, left of a negative one.\n valueX: value >= 0 ? x + barW + 6 : x - 6,\n valueY: p.y + i * totalBarHeight + totalBarHeight / 2,\n valueAnchor: value >= 0 ? 'start' : 'end',\n };\n });\n }\n });\n\n /**\n * Value axis along the plot edge plus the zero baseline, which sits inside\n * the plot when the data goes negative.\n */\n protected readonly axisLine = computed(() => {\n const p = this.plot();\n const isVertical = this.isVertical();\n const zero = this.data()?.length ? this.valueToPos(0) : (isVertical ? p.y + p.h : p.x);\n return {\n x1: isVertical ? p.x : zero,\n y1: p.y,\n x2: isVertical ? p.x : zero,\n y2: p.y + p.h,\n bx1: p.x,\n by1: isVertical ? zero : p.y + p.h,\n bx2: p.x + p.w,\n by2: isVertical ? zero : p.y + p.h,\n };\n });\n\n /** Bottom edge of the plot — where horizontal-orientation tick labels sit. */\n protected readonly plotBottom = computed(() => this.plot().y + this.plot().h);\n\n protected readonly effectiveAriaLabel = computed(() => {\n const explicit = this.ariaLabel();\n if (explicit) return explicit;\n const d = this.data() ?? [];\n if (!d.length) return 'Bar chart: no data';\n const fmt = this.formatValue();\n return `Bar chart: ${d\n .map((item, i) => `${item.label || `Bar ${i + 1}`} ${fmt(toFinite(item.value))}`)\n .join(', ')}`;\n });\n}\n","<svg\n width=\"100%\"\n [attr.height]=\"height()\"\n [attr.viewBox]=\"viewBox()\"\n role=\"img\"\n [attr.aria-label]=\"effectiveAriaLabel()\"\n class=\"lc-bar-chart\"\n>\n <title>{{ effectiveAriaLabel() }}</title>\n\n <!-- Grid lines -->\n @if (showGrid()) {\n @for (line of gridLines(); track line.pos) {\n @if (isVertical()) {\n <line\n [attr.x1]=\"axisLine().bx1\"\n [attr.y1]=\"line.pos\"\n [attr.x2]=\"axisLine().bx2\"\n [attr.y2]=\"line.pos\"\n class=\"lc-bar-chart__grid-line\"\n />\n <text\n [attr.x]=\"axisLine().x1 - 6\"\n [attr.y]=\"line.pos\"\n text-anchor=\"end\"\n dominant-baseline=\"central\"\n class=\"lc-bar-chart__grid-label\"\n >\n {{ line.label }}\n </text>\n } @else {\n <line\n [attr.x1]=\"line.pos\"\n [attr.y1]=\"axisLine().y1\"\n [attr.x2]=\"line.pos\"\n [attr.y2]=\"axisLine().y2\"\n class=\"lc-bar-chart__grid-line\"\n />\n <text\n [attr.x]=\"line.pos\"\n [attr.y]=\"plotBottom() + 12\"\n text-anchor=\"middle\"\n class=\"lc-bar-chart__grid-label\"\n >\n {{ line.label }}\n </text>\n }\n }\n }\n\n <!-- Axes -->\n <line\n [attr.x1]=\"axisLine().x1\"\n [attr.y1]=\"axisLine().y1\"\n [attr.x2]=\"axisLine().x2\"\n [attr.y2]=\"axisLine().y2\"\n class=\"lc-bar-chart__axis\"\n />\n <line\n [attr.x1]=\"axisLine().bx1\"\n [attr.y1]=\"axisLine().by1\"\n [attr.x2]=\"axisLine().bx2\"\n [attr.y2]=\"axisLine().by2\"\n class=\"lc-bar-chart__axis\"\n />\n\n <!-- Bars -->\n @for (bar of bars(); track $index) {\n <rect\n [attr.x]=\"bar.x\"\n [attr.y]=\"bar.y\"\n [attr.width]=\"bar.width\"\n [attr.height]=\"bar.height\"\n [attr.fill]=\"bar.color\"\n rx=\"2\"\n class=\"lc-bar-chart__bar\"\n />\n\n @if (showValues()) {\n <text\n [attr.x]=\"bar.valueX\"\n [attr.y]=\"bar.valueY\"\n [attr.text-anchor]=\"bar.valueAnchor\"\n [attr.dominant-baseline]=\"isVertical() ? 'auto' : 'central'\"\n class=\"lc-bar-chart__value\"\n >\n {{ bar.valueText }}\n </text>\n }\n\n @if (showLabels()) {\n <text\n [attr.x]=\"bar.labelX\"\n [attr.y]=\"bar.labelY\"\n [attr.text-anchor]=\"isVertical() ? 'middle' : 'end'\"\n [attr.dominant-baseline]=\"isVertical() ? 'auto' : 'central'\"\n class=\"lc-bar-chart__label\"\n >\n {{ bar.label }}\n </text>\n }\n }\n</svg>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-bar-chart.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/bar-chart/bar-chart.component.ts","../../../../libs/angular-ui-kit/src/lib/bar-chart/bar-chart.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n ElementRef,\n afterNextRender,\n inject,\n input,\n computed,\n signal,\n} from '@angular/core';\nimport { chartColor } from '@lifecorner/angular-ui-kit/internal/chart';\nimport {\n formatChartValue,\n niceScale,\n toFinite,\n} from '@lifecorner/angular-ui-kit/internal/chart';\n\nexport interface BarChartItem {\n /** Bar value. Negative values draw downward (left) from the zero baseline. */\n value: number;\n label?: string;\n color?: string;\n /**\n * The text shown as this bar's value and read in the generated aria label,\n * instead of `formatValue(value, item)`, for example \"313.000,00 · 54,8 %\".\n * The kit shows it as given and never formats or computes it. An empty\n * string shows no value for this bar.\n */\n valueText?: string;\n}\n\nexport type BarChartOrientation = 'vertical' | 'horizontal';\n\n/**\n * How the category labels are set: `'axis'` (one line of SVG text on the\n * category axis) or `'wrap'` (HTML text that wraps and follows the text size).\n */\nexport type BarChartLabelLayout = 'axis' | 'wrap';\n\n/**\n * Signature of `lc-bar-chart`'s `formatValue`. A bar's value label passes the\n * bar's item; an axis tick passes the tick only (`item` is `undefined`).\n * One-argument formatters keep working.\n */\nexport type BarChartValueFormatter = (value: number, item?: BarChartItem) => string;\n\n/** viewBox width used until the container has been measured. */\nconst DEFAULT_WIDTH = 400;\n\n@Component({\n selector: 'lc-bar-chart',\n standalone: true,\n templateUrl: './bar-chart.component.html',\n styleUrls: ['./bar-chart.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[style.max-width.px]': 'width()',\n },\n})\n/**\n * Bar chart component for comparing categorical data.\n *\n * Features:\n * - Vertical and horizontal orientation\n * - Per-bar or uniform color support\n * - Negative values drawn from a zero baseline\n * - Optional value labels on bars\n * - Configurable grid and axis labels on a nice tick scale\n * - Adjustable bar gap spacing\n * - Responsive SVG rendering\n *\n * @example\n * ```html\n * <lc-bar-chart [data]=\"items\" orientation=\"vertical\" [showValues]=\"true\" />\n * ```\n */\nexport class BarChartComponent {\n private readonly _el = inject(ElementRef);\n private readonly _destroyRef = inject(DestroyRef);\n private readonly _containerWidth = signal<number>(0);\n\n constructor() {\n afterNextRender(() => {\n const obs = new ResizeObserver(([entry]) => {\n this._containerWidth.set(entry.contentRect.width);\n });\n obs.observe(this._el.nativeElement);\n this._destroyRef.onDestroy(() => obs.disconnect());\n });\n }\n\n /** Data items. */\n data = input.required<BarChartItem[]>();\n\n /**\n * Intrinsic chart width in pixels. The chart fills its container (the SVG is\n * `width=\"100%\"` and its viewBox follows the measured container width);\n * `width` is the viewBox width until the container has been measured and,\n * when set, the host's `max-width`, so the chart never grows past it. Leave\n * unset for a fully fluid chart.\n * @default undefined\n */\n width = input<number | undefined>(undefined);\n\n /**\n * Chart height in pixels. A horizontal `'wrap'` chart takes the height of\n * its rows; a vertical `'wrap'` chart adds its label row below this height.\n * @default 200\n */\n height = input<number>(200);\n\n /**\n * Bar orientation.\n * @default 'vertical'\n */\n orientation = input<BarChartOrientation>('vertical');\n\n /**\n * Show value labels on bars. In horizontal `'wrap'` rows the value sits at\n * the end of the label line.\n * @default true\n */\n showValues = input<boolean>(true);\n\n /**\n * Show axis labels.\n * @default true\n */\n showLabels = input<boolean>(true);\n\n /**\n * How the category labels are set. `'axis'`: one line of SVG text on the\n * category axis, in a fixed 40 px band left of a horizontal bar or centred\n * under a vertical column. Long labels can run out of the chart. `'wrap'`:\n * HTML text that wraps to the room it needs and follows the text size. A\n * horizontal chart becomes a list of rows: the label and the value on one\n * line (the value moves under the label when both don't fit), and below\n * them the bar across the full width. The longest bar fills the width, and\n * `height`, `barGap` and `showGrid` don't apply. A vertical chart keeps its\n * plot and wraps each label within its column, under the chart.\n * @default 'axis'\n */\n labelLayout = input<BarChartLabelLayout>('axis');\n\n /**\n * Show grid lines. Not used by horizontal `'wrap'` rows.\n * @default true\n */\n showGrid = input<boolean>(true);\n\n /**\n * Gap between bars as ratio (0-1). Not used by horizontal `'wrap'` rows.\n * @default 0.3\n */\n barGap = input<number>(0.3);\n\n /**\n * Use a single color for all bars.\n * @default ''\n */\n color = input<string>('');\n\n /**\n * Formats the value labels and the axis ticks. For a bar it gets the bar's\n * item as the second argument; for an axis tick it gets the tick only\n * (`item` is `undefined`). An item's `valueText` takes precedence.\n * @default float-safe `String(value)`\n */\n formatValue = input<BarChartValueFormatter>(formatChartValue);\n\n /**\n * Accessible name of the chart. Defaults to a generated summary that lists\n * every bar's label and value, so assistive technology hears the data and\n * not just \"Bar chart\".\n * @default ''\n */\n ariaLabel = input<string>('');\n\n // Layout constants\n private readonly PADDING_LEFT = 40;\n private readonly PADDING_RIGHT = 10;\n private readonly PADDING_TOP = 10;\n private readonly PADDING_BOTTOM = 30;\n\n protected readonly effectiveWidth = computed(\n () => this._containerWidth() || this.width() || DEFAULT_WIDTH\n );\n\n protected readonly viewBox = computed(\n () => `0 0 ${this.effectiveWidth()} ${this.height()}`\n );\n\n protected readonly isVertical = computed(() => this.orientation() === 'vertical');\n\n /** Horizontal `'wrap'`: HTML rows instead of the SVG plot. */\n protected readonly wrapRows = computed(\n () => this.labelLayout() === 'wrap' && !this.isVertical()\n );\n\n /** Vertical `'wrap'`: the SVG plot, with the category labels as an HTML row under it. */\n protected readonly wrapColumns = computed(\n () => this.labelLayout() === 'wrap' && this.isVertical()\n );\n\n /**\n * Room under the plot. `'axis'` keeps 30 for the SVG labels; vertical\n * `'wrap'` labels sit under the SVG, so only 2 are left, or 18 when a\n * negative bar's value label hangs below it.\n */\n private readonly paddingBottom = computed(() => {\n if (!this.wrapColumns()) return this.PADDING_BOTTOM;\n const negative = (this.data() ?? []).some((i) => toFinite(i.value) < 0);\n return this.showValues() && negative ? 18 : 2;\n });\n\n private readonly plot = computed(() => {\n const w = this.effectiveWidth();\n const h = this.height();\n return {\n x: this.PADDING_LEFT,\n y: this.PADDING_TOP,\n w: w - this.PADDING_LEFT - this.PADDING_RIGHT,\n h: h - this.PADDING_TOP - this.paddingBottom(),\n };\n });\n\n /** A bar's text: `valueText`, else `formatValue(value, item)`. */\n private textOf(item: BarChartItem, value: number, fmt: BarChartValueFormatter): string {\n return item.valueText ?? fmt(value, item);\n }\n\n /**\n * One scale for grid *and* bars: bounds are nice multiples of the tick step,\n * always include zero, and extend below zero when the data does — so the top\n * tick is the value at the plot edge and negative bars hang off the baseline.\n */\n protected readonly scale = computed(() => {\n const values = (this.data() ?? []).map((i) => toFinite(i.value));\n return niceScale(Math.min(0, ...values), Math.max(0, ...values), 4);\n });\n\n /** Maps a data value to a pixel position along the value axis. */\n private valueToPos(value: number): number {\n const { min, max } = this.scale();\n const p = this.plot();\n const frac = (value - min) / (max - min || 1);\n return this.isVertical() ? p.y + p.h - frac * p.h : p.x + frac * p.w;\n }\n\n protected readonly gridLines = computed(() => {\n if (!this.showGrid() || !this.data()?.length) return [];\n const fmt = this.formatValue();\n return this.scale().ticks.map((tick) => ({\n pos: this.valueToPos(tick),\n label: fmt(tick),\n }));\n });\n\n protected readonly bars = computed(() => {\n const d = this.data();\n if (!d || d.length === 0) return [];\n\n const isVertical = this.isVertical();\n const p = this.plot();\n const gap = this.barGap();\n const singleColor = this.color();\n const fmt = this.formatValue();\n const zero = this.valueToPos(0);\n const h = this.height();\n\n if (isVertical) {\n const totalBarWidth = p.w / d.length;\n const barWidth = totalBarWidth * (1 - gap);\n const barOffset = (totalBarWidth - barWidth) / 2;\n\n return d.map((item, i) => {\n const value = toFinite(item.value);\n const end = this.valueToPos(value);\n const y = Math.min(zero, end);\n const barH = Math.abs(end - zero);\n return {\n x: p.x + i * totalBarWidth + barOffset,\n y,\n width: barWidth,\n height: barH,\n color: singleColor || item.color || chartColor(i),\n label: item.label || '',\n value,\n valueText: this.textOf(item, value, fmt),\n labelX: p.x + i * totalBarWidth + totalBarWidth / 2,\n labelY: h - this.PADDING_BOTTOM + 16,\n valueX: p.x + i * totalBarWidth + totalBarWidth / 2,\n // Above a positive bar, below a negative one.\n valueY: value >= 0 ? y - 6 : y + barH + 12,\n valueAnchor: 'middle',\n };\n });\n } else {\n const totalBarHeight = p.h / d.length;\n const barHeight = totalBarHeight * (1 - gap);\n const barOffset = (totalBarHeight - barHeight) / 2;\n\n return d.map((item, i) => {\n const value = toFinite(item.value);\n const end = this.valueToPos(value);\n const x = Math.min(zero, end);\n const barW = Math.abs(end - zero);\n return {\n x,\n y: p.y + i * totalBarHeight + barOffset,\n width: barW,\n height: barHeight,\n color: singleColor || item.color || chartColor(i),\n label: item.label || '',\n value,\n valueText: this.textOf(item, value, fmt),\n labelX: p.x - 6,\n labelY: p.y + i * totalBarHeight + totalBarHeight / 2,\n // Right of a positive bar, left of a negative one.\n valueX: value >= 0 ? x + barW + 6 : x - 6,\n valueY: p.y + i * totalBarHeight + totalBarHeight / 2,\n valueAnchor: value >= 0 ? 'start' : 'end',\n };\n });\n }\n });\n\n /**\n * Value axis along the plot edge plus the zero baseline, which sits inside\n * the plot when the data goes negative.\n */\n protected readonly axisLine = computed(() => {\n const p = this.plot();\n const isVertical = this.isVertical();\n const zero = this.data()?.length ? this.valueToPos(0) : (isVertical ? p.y + p.h : p.x);\n return {\n x1: isVertical ? p.x : zero,\n y1: p.y,\n x2: isVertical ? p.x : zero,\n y2: p.y + p.h,\n bx1: p.x,\n by1: isVertical ? zero : p.y + p.h,\n bx2: p.x + p.w,\n by2: isVertical ? zero : p.y + p.h,\n };\n });\n\n /** Bottom edge of the plot — where horizontal-orientation tick labels sit. */\n protected readonly plotBottom = computed(() => this.plot().y + this.plot().h);\n\n /**\n * Bounds of the horizontal `'wrap'` rows: zero is always inside, and the\n * longest bar fills the track.\n */\n private readonly rowBounds = computed(() => {\n const values = (this.data() ?? []).map((i) => toFinite(i.value));\n const lo = Math.min(0, ...values);\n const hi = Math.max(0, ...values);\n return { values, lo, range: hi - lo || 1 };\n });\n\n /**\n * Horizontal `'wrap'` rows. Bar `x` and `width` are percentages of the\n * track, so the server HTML is final before the container is measured.\n */\n protected readonly rows = computed(() => {\n const d = this.data() ?? [];\n const { values, lo, range } = this.rowBounds();\n const singleColor = this.color();\n const fmt = this.formatValue();\n return d.map((item, i) => {\n const value = values[i];\n return {\n label: item.label || '',\n valueText: this.textOf(item, value, fmt),\n color: singleColor || item.color || chartColor(i),\n x: `${((Math.min(value, 0) - lo) / range) * 100}%`,\n width: `${(Math.abs(value) / range) * 100}%`,\n };\n });\n });\n\n /** Position of the zero line in the `'wrap'` rows; `null` without negative values. */\n protected readonly rowZero = computed(() => {\n const { lo, range } = this.rowBounds();\n return lo < 0 ? `${(-lo / range) * 100}%` : null;\n });\n\n protected readonly effectiveAriaLabel = computed(() => {\n const explicit = this.ariaLabel();\n if (explicit) return explicit;\n const d = this.data() ?? [];\n if (!d.length) return 'Bar chart: no data';\n const fmt = this.formatValue();\n return `Bar chart: ${d\n .map((item, i) => {\n const label = item.label || `Bar ${i + 1}`;\n const text = this.textOf(item, toFinite(item.value), fmt);\n return text === '' ? label : `${label} ${text}`;\n })\n .join(', ')}`;\n });\n}\n","@if (wrapRows()) {\n <!--\n Horizontal 'wrap' (3.21, request 028): HTML rows, so labels wrap and follow\n the text size. Bar x/width are percentages: final in the server HTML.\n No inline style (CSP). The children of role=\"img\" are presentational.\n -->\n <div\n class=\"lc-bar-chart lc-bar-chart--rows\"\n role=\"img\"\n [attr.aria-label]=\"effectiveAriaLabel()\"\n >\n @for (row of rows(); track $index) {\n <div class=\"lc-bar-chart__row\">\n @if ((showLabels() && row.label) || (showValues() && row.valueText)) {\n <div class=\"lc-bar-chart__row-head\">\n @if (showLabels() && row.label) {\n <span class=\"lc-bar-chart__row-label\">{{ row.label }}</span>\n }\n @if (showValues() && row.valueText) {\n <span class=\"lc-bar-chart__row-value\">{{ row.valueText }}</span>\n }\n </div>\n }\n <svg\n width=\"100%\"\n aria-hidden=\"true\"\n focusable=\"false\"\n class=\"lc-bar-chart__row-track\"\n >\n @if (rowZero(); as zero) {\n <line\n [attr.x1]=\"zero\"\n y1=\"-25%\"\n [attr.x2]=\"zero\"\n y2=\"125%\"\n class=\"lc-bar-chart__zero-line\"\n />\n }\n <rect\n [attr.x]=\"row.x\"\n y=\"0\"\n [attr.width]=\"row.width\"\n height=\"100%\"\n [attr.fill]=\"row.color\"\n rx=\"2\"\n class=\"lc-bar-chart__row-bar\"\n />\n </svg>\n </div>\n }\n </div>\n} @else {\n <svg\n width=\"100%\"\n [attr.height]=\"height()\"\n [attr.viewBox]=\"viewBox()\"\n role=\"img\"\n [attr.aria-label]=\"effectiveAriaLabel()\"\n class=\"lc-bar-chart\"\n >\n <title>{{ effectiveAriaLabel() }}</title>\n\n <!-- Grid lines -->\n @if (showGrid()) {\n @for (line of gridLines(); track line.pos) {\n @if (isVertical()) {\n <line\n [attr.x1]=\"axisLine().bx1\"\n [attr.y1]=\"line.pos\"\n [attr.x2]=\"axisLine().bx2\"\n [attr.y2]=\"line.pos\"\n class=\"lc-bar-chart__grid-line\"\n />\n <text\n [attr.x]=\"axisLine().x1 - 6\"\n [attr.y]=\"line.pos\"\n text-anchor=\"end\"\n dominant-baseline=\"central\"\n class=\"lc-bar-chart__grid-label\"\n >\n {{ line.label }}\n </text>\n } @else {\n <line\n [attr.x1]=\"line.pos\"\n [attr.y1]=\"axisLine().y1\"\n [attr.x2]=\"line.pos\"\n [attr.y2]=\"axisLine().y2\"\n class=\"lc-bar-chart__grid-line\"\n />\n <text\n [attr.x]=\"line.pos\"\n [attr.y]=\"plotBottom() + 12\"\n text-anchor=\"middle\"\n class=\"lc-bar-chart__grid-label\"\n >\n {{ line.label }}\n </text>\n }\n }\n }\n\n <!-- Axes -->\n <line\n [attr.x1]=\"axisLine().x1\"\n [attr.y1]=\"axisLine().y1\"\n [attr.x2]=\"axisLine().x2\"\n [attr.y2]=\"axisLine().y2\"\n class=\"lc-bar-chart__axis\"\n />\n <line\n [attr.x1]=\"axisLine().bx1\"\n [attr.y1]=\"axisLine().by1\"\n [attr.x2]=\"axisLine().bx2\"\n [attr.y2]=\"axisLine().by2\"\n class=\"lc-bar-chart__axis\"\n />\n\n <!-- Bars -->\n @for (bar of bars(); track $index) {\n <rect\n [attr.x]=\"bar.x\"\n [attr.y]=\"bar.y\"\n [attr.width]=\"bar.width\"\n [attr.height]=\"bar.height\"\n [attr.fill]=\"bar.color\"\n rx=\"2\"\n class=\"lc-bar-chart__bar\"\n />\n\n @if (showValues()) {\n <text\n [attr.x]=\"bar.valueX\"\n [attr.y]=\"bar.valueY\"\n [attr.text-anchor]=\"bar.valueAnchor\"\n [attr.dominant-baseline]=\"isVertical() ? 'auto' : 'central'\"\n class=\"lc-bar-chart__value\"\n >\n {{ bar.valueText }}\n </text>\n }\n\n <!-- One block (not nested), so the SVG's comment anchors stay as before 3.21. -->\n @if (showLabels() && !wrapColumns()) {\n <text\n [attr.x]=\"bar.labelX\"\n [attr.y]=\"bar.labelY\"\n [attr.text-anchor]=\"isVertical() ? 'middle' : 'end'\"\n [attr.dominant-baseline]=\"isVertical() ? 'auto' : 'central'\"\n class=\"lc-bar-chart__label\"\n >\n {{ bar.label }}\n </text>\n }\n }\n </svg>\n\n <!--\n Vertical 'wrap' (3.21, request 028): the category labels as HTML under the\n plot, one cell per column, wrapping within it. Hidden from assistive\n technology: the names are in the SVG's accessible name.\n -->\n @if (wrapColumns() && showLabels()) {\n <div class=\"lc-bar-chart__category-labels\" aria-hidden=\"true\">\n @for (bar of bars(); track $index) {\n <span class=\"lc-bar-chart__category-label\">{{ bar.label }}</span>\n }\n </div>\n }\n}\n"],"mappings":"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"}
@@ -161,7 +161,7 @@ var CardComponent = class CardComponent {
161
161
  host: { listeners: { "keydown": "handleKeydown($event)" } },
162
162
  ngImport: i0,
163
163
  template: "<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -- Enter/Space are handled by the host keydown listener (role=button, tabindex=0 when clickable) -->\n<div\n [class]=\"cardClasses()\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.tabindex]=\"isClickable() ? 0 : null\"\n (click)=\"handleClick($event)\"\n>\n @if (title()) {\n <div class=\"card__header\">\n @if (icon()) {\n <div\n class=\"card__icon card__icon--{{ iconVariant() }} card__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"card__header-left\">\n <div class=\"card__title-row\">\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\">{{ title() }}</h2>\n }\n @case (4) {\n <h4 class=\"card__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 class=\"card__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 class=\"card__title\">{{ title() }}</h6>\n }\n @default {\n <h3 class=\"card__title\">{{ title() }}</h3>\n }\n }\n <!-- Rich status badge: project an lc-badge here (takes visual\n precedence over the `badge` string). Wrapper hides when empty. -->\n <span class=\"card__badge-slot\"><ng-content select=\"[card-badge]\"></ng-content></span>\n @if (badge()) {\n <span class=\"card__badge card__badge--{{ badgeVariant() }}\">{{ badge() }}</span>\n }\n </div>\n @if (subtitle()) {\n <p class=\"card__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n <div class=\"card__header-action\">\n <ng-content select=\"[card-header-action]\"></ng-content>\n </div>\n </div>\n }\n <ng-content select=\"[card-header]\"></ng-content>\n <div class=\"card__body\">\n <ng-content></ng-content>\n </div>\n <ng-content select=\"[card-footer]\"></ng-content>\n</div>\n",
164
- styles: [":host{display:block;height:100%}.card{position:relative;display:flex;flex-direction:column;height:100%;background-color:var(--color-surface);color:var(--color-text-primary);transition:box-shadow .15s ease-in-out,transform .15s ease-in-out,border-color .15s ease-in-out}.card__body{flex:1;display:flex;flex-direction:column}.card-elevated{position:relative;overflow:hidden;background-image:var(--gradient-surface);border:1px solid var(--color-border);box-shadow:var(--elevation-1)}.card-elevated:after{content:\"\";position:absolute;inset:0 0 auto;height:3px;background:var(--accent-line);opacity:.7;pointer-events:none}.card-elevated:hover{box-shadow:var(--elevation-2)}.card-outlined{background-image:none;border:1px solid var(--color-border)}.card-flat{background-image:none}.card-raised{background-image:none;border:1px solid var(--color-border);box-shadow:var(--shadow-auth-card, var(--elevation-3))}.card-dashed{background-color:var(--color-muted-tile, var(--color-surface-muted));background-image:none;border:1px dashed var(--color-muted-tile-border, var(--color-border));box-shadow:none;color:var(--color-text-secondary)}.card-padding-none{padding:0}.card-padding-xs{padding:var(--lc-density-padding-xs, var(--spacing-2))}.card-padding-sm{padding:var(--lc-density-padding-sm, var(--spacing-3))}.card-padding-md{padding:var(--lc-density-padding-md, var(--spacing-4))}.card-padding-lg{padding:var(--lc-density-padding-lg, var(--spacing-6))}.card-radius-none{border-radius:var(--border-radius-none)}.card-radius-sm{border-radius:var(--border-radius-sm)}.card-radius-md{border-radius:var(--border-radius-md)}.card-radius-lg{border-radius:var(--border-radius-lg)}.card-radius-xl{border-radius:var(--border-radius-xl, 1.5rem)}.card-radius-full{border-radius:var(--border-radius-full)}.card-clickable{cursor:pointer;-webkit-user-select:none;user-select:none}.card-clickable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.card-clickable:hover{background-color:var(--color-surface-hover);border-color:var(--color-border-strong)}.card-clickable:active{transform:scale(.98)}.card-selected{border:1px solid var(--color-primary);background-color:var(--color-surface-selected);box-shadow:var(--shadow-brand-glow)}.card-selected.card-elevated{box-shadow:var(--shadow-brand-glow),var(--elevation-2)}.card__header{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--lc-density-gap-md, var(--spacing-4));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-border, #e5e7eb)}.card__icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:var(--border-radius-md)}.card__icon--size-sm{width:1.75rem;height:1.75rem;border-radius:var(--border-radius-sm)}.card__icon--brand{background-image:var(--gradient-brand-tile);color:var(--color-on-primary, #fff);box-shadow:var(--shadow-brand-glow)}.card__icon--subtle{background-color:#20849724;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.card__header-left{flex:1;min-width:0}.card__title-row{display:flex;align-items:center;gap:.5rem}.card__badge-slot{display:inline-flex;align-items:center}.card__badge-slot:empty{display:none}.card__title{margin:0;font-weight:600;font-size:var(--lc-card-title-size, var(--font-size-lg, 1.125rem));line-height:1.3;color:var(--color-text-primary)}.card__badge{display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 .375rem;border-radius:9999px;font-size:.75rem;font-weight:600;line-height:1}.card__badge--default{background-color:var(--color-surface-hover);color:var(--color-text-secondary);border:1px solid var(--color-border)}.card__badge--primary{background-color:#20849729;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.card__badge--success{background-color:#8ea4752e;color:var(--color-success)}.card__badge--warning{background-color:#e1a0402e;color:var(--color-warning)}.card__badge--error{background-color:#9d0e0e2e;color:var(--color-error)}.card__subtitle{margin:.25rem 0 0;font-size:.8125rem;color:var(--color-text-secondary);line-height:1.4}.card__header-action{flex-shrink:0;display:flex;align-items:center;gap:.5rem}:host ::ng-deep [card-header],:host ::ng-deep .card-header{display:flex;align-items:center;justify-content:space-between;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-divider, #e5e7eb);font-weight:600;font-size:1.125rem;color:var(--color-text-primary)}:host ::ng-deep .card-content{color:var(--color-text-primary)}:host ::ng-deep [card-footer],:host ::ng-deep .card-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-top:var(--lc-density-padding-md, var(--spacing-4));padding-top:var(--lc-density-padding-sm, var(--spacing-3));border-top:1px solid var(--color-divider, #e5e7eb);color:var(--color-text-secondary)}:host ::ng-deep .card-dashed lc-icon{color:var(--color-muted-tile-icon, var(--color-text-tertiary))}\n"],
164
+ styles: [":host{display:block;height:100%}.card{position:relative;display:flex;flex-direction:column;height:100%;background-color:var(--color-surface);color:var(--color-text-primary);transition:box-shadow .15s ease-in-out,transform .15s ease-in-out,border-color .15s ease-in-out}.card__body{flex:1;display:flex;flex-direction:column}.card-elevated{position:relative;overflow:hidden;background-image:var(--gradient-surface);border:1px solid var(--color-border);box-shadow:var(--elevation-1)}.card-elevated:after{content:\"\";position:absolute;inset:0 0 auto;height:3px;background:var(--accent-line);opacity:.7;pointer-events:none}.card-elevated:hover{box-shadow:var(--elevation-2)}.card-outlined{background-image:none;border:1px solid var(--color-border)}.card-flat{background-image:none}.card-raised{background-image:none;border:1px solid var(--color-border);box-shadow:var(--shadow-auth-card, var(--elevation-3))}.card-dashed{background-color:var(--color-muted-tile, var(--color-surface-muted));background-image:none;border:1px dashed var(--color-muted-tile-border, var(--color-border));box-shadow:none;color:var(--color-text-secondary)}.card-padding-none{padding:0}.card-padding-xs{padding:var(--lc-density-padding-xs, var(--spacing-2))}.card-padding-sm{padding:var(--lc-density-padding-sm, var(--spacing-3))}.card-padding-md{padding:var(--lc-density-padding-md, var(--spacing-4))}.card-padding-lg{padding:var(--lc-density-padding-lg, var(--spacing-6))}.card-radius-none{border-radius:var(--border-radius-none)}.card-radius-sm{border-radius:var(--border-radius-sm)}.card-radius-md{border-radius:var(--border-radius-md)}.card-radius-lg{border-radius:var(--border-radius-lg)}.card-radius-xl{border-radius:var(--border-radius-xl, 1.5rem)}.card-radius-full{border-radius:var(--border-radius-full)}.card-clickable{cursor:pointer;-webkit-user-select:none;user-select:none}.card-clickable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.card-clickable:hover{background-color:var(--color-surface-hover);border-color:var(--color-border-strong)}.card-clickable:active{transform:scale(.98)}.card-selected{border:1px solid var(--color-primary);background-color:var(--color-surface-selected);box-shadow:var(--shadow-brand-glow)}.card-selected.card-elevated{box-shadow:var(--shadow-brand-glow),var(--elevation-2)}.card__header{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--lc-density-gap-md, var(--spacing-4));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-border, #e5e7eb)}.card__icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:var(--border-radius-md)}.card__icon--size-sm{width:1.75rem;height:1.75rem;border-radius:var(--border-radius-sm)}.card__icon--brand{background-image:var(--gradient-brand-tile);color:var(--color-on-primary, #fff);box-shadow:var(--shadow-brand-glow)}.card__icon--subtle{background-color:#20849724;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.card__header-left{flex:1;min-width:0}.card__title-row{display:flex;align-items:center;gap:.5rem}.card__badge-slot{display:inline-flex;align-items:center}.card__badge-slot:empty{display:none}.card__title{margin:0;font-weight:600;font-size:var(--lc-card-title-size, var(--font-size-lg, 1.125rem));line-height:1.3;color:var(--color-text-primary)}.card__badge{display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 .375rem;border-radius:9999px;font-size:.75rem;font-weight:600;line-height:1}.card__badge--default{background-color:var(--color-surface-hover);color:var(--color-text-secondary);border:1px solid var(--color-border)}.card__badge--primary{background-color:#20849729;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.card__badge--success{background-color:var(--color-success-subtle);color:var(--color-on-success-subtle)}.card__badge--warning{background-color:var(--color-warning-subtle);color:var(--color-on-warning-subtle)}.card__badge--error{background-color:var(--color-error-subtle);color:var(--color-on-error-subtle)}.card__subtitle{margin:.25rem 0 0;font-size:.8125rem;color:var(--color-text-secondary);line-height:1.4}.card__header-action{flex-shrink:0;display:flex;align-items:center;gap:.5rem}:host ::ng-deep [card-header],:host ::ng-deep .card-header{display:flex;align-items:center;justify-content:space-between;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-divider, #e5e7eb);font-weight:600;font-size:1.125rem;color:var(--color-text-primary)}:host ::ng-deep .card-content{color:var(--color-text-primary)}:host ::ng-deep [card-footer],:host ::ng-deep .card-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-top:var(--lc-density-padding-md, var(--spacing-4));padding-top:var(--lc-density-padding-sm, var(--spacing-3));border-top:1px solid var(--color-divider, #e5e7eb);color:var(--color-text-secondary)}:host ::ng-deep .card-dashed lc-icon{color:var(--color-muted-tile-icon, var(--color-text-tertiary))}\n"],
165
165
  dependencies: [{
166
166
  kind: "component",
167
167
  type: IconComponent,
@@ -192,7 +192,7 @@ i0.ɵɵngDeclareClassMetadata({
192
192
  imports: [IconComponent],
193
193
  changeDetection: ChangeDetectionStrategy.OnPush,
194
194
  template: "<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -- Enter/Space are handled by the host keydown listener (role=button, tabindex=0 when clickable) -->\n<div\n [class]=\"cardClasses()\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.tabindex]=\"isClickable() ? 0 : null\"\n (click)=\"handleClick($event)\"\n>\n @if (title()) {\n <div class=\"card__header\">\n @if (icon()) {\n <div\n class=\"card__icon card__icon--{{ iconVariant() }} card__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"card__header-left\">\n <div class=\"card__title-row\">\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\">{{ title() }}</h2>\n }\n @case (4) {\n <h4 class=\"card__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 class=\"card__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 class=\"card__title\">{{ title() }}</h6>\n }\n @default {\n <h3 class=\"card__title\">{{ title() }}</h3>\n }\n }\n <!-- Rich status badge: project an lc-badge here (takes visual\n precedence over the `badge` string). Wrapper hides when empty. -->\n <span class=\"card__badge-slot\"><ng-content select=\"[card-badge]\"></ng-content></span>\n @if (badge()) {\n <span class=\"card__badge card__badge--{{ badgeVariant() }}\">{{ badge() }}</span>\n }\n </div>\n @if (subtitle()) {\n <p class=\"card__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n <div class=\"card__header-action\">\n <ng-content select=\"[card-header-action]\"></ng-content>\n </div>\n </div>\n }\n <ng-content select=\"[card-header]\"></ng-content>\n <div class=\"card__body\">\n <ng-content></ng-content>\n </div>\n <ng-content select=\"[card-footer]\"></ng-content>\n</div>\n",
195
- styles: [":host{display:block;height:100%}.card{position:relative;display:flex;flex-direction:column;height:100%;background-color:var(--color-surface);color:var(--color-text-primary);transition:box-shadow .15s ease-in-out,transform .15s ease-in-out,border-color .15s ease-in-out}.card__body{flex:1;display:flex;flex-direction:column}.card-elevated{position:relative;overflow:hidden;background-image:var(--gradient-surface);border:1px solid var(--color-border);box-shadow:var(--elevation-1)}.card-elevated:after{content:\"\";position:absolute;inset:0 0 auto;height:3px;background:var(--accent-line);opacity:.7;pointer-events:none}.card-elevated:hover{box-shadow:var(--elevation-2)}.card-outlined{background-image:none;border:1px solid var(--color-border)}.card-flat{background-image:none}.card-raised{background-image:none;border:1px solid var(--color-border);box-shadow:var(--shadow-auth-card, var(--elevation-3))}.card-dashed{background-color:var(--color-muted-tile, var(--color-surface-muted));background-image:none;border:1px dashed var(--color-muted-tile-border, var(--color-border));box-shadow:none;color:var(--color-text-secondary)}.card-padding-none{padding:0}.card-padding-xs{padding:var(--lc-density-padding-xs, var(--spacing-2))}.card-padding-sm{padding:var(--lc-density-padding-sm, var(--spacing-3))}.card-padding-md{padding:var(--lc-density-padding-md, var(--spacing-4))}.card-padding-lg{padding:var(--lc-density-padding-lg, var(--spacing-6))}.card-radius-none{border-radius:var(--border-radius-none)}.card-radius-sm{border-radius:var(--border-radius-sm)}.card-radius-md{border-radius:var(--border-radius-md)}.card-radius-lg{border-radius:var(--border-radius-lg)}.card-radius-xl{border-radius:var(--border-radius-xl, 1.5rem)}.card-radius-full{border-radius:var(--border-radius-full)}.card-clickable{cursor:pointer;-webkit-user-select:none;user-select:none}.card-clickable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.card-clickable:hover{background-color:var(--color-surface-hover);border-color:var(--color-border-strong)}.card-clickable:active{transform:scale(.98)}.card-selected{border:1px solid var(--color-primary);background-color:var(--color-surface-selected);box-shadow:var(--shadow-brand-glow)}.card-selected.card-elevated{box-shadow:var(--shadow-brand-glow),var(--elevation-2)}.card__header{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--lc-density-gap-md, var(--spacing-4));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-border, #e5e7eb)}.card__icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:var(--border-radius-md)}.card__icon--size-sm{width:1.75rem;height:1.75rem;border-radius:var(--border-radius-sm)}.card__icon--brand{background-image:var(--gradient-brand-tile);color:var(--color-on-primary, #fff);box-shadow:var(--shadow-brand-glow)}.card__icon--subtle{background-color:#20849724;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.card__header-left{flex:1;min-width:0}.card__title-row{display:flex;align-items:center;gap:.5rem}.card__badge-slot{display:inline-flex;align-items:center}.card__badge-slot:empty{display:none}.card__title{margin:0;font-weight:600;font-size:var(--lc-card-title-size, var(--font-size-lg, 1.125rem));line-height:1.3;color:var(--color-text-primary)}.card__badge{display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 .375rem;border-radius:9999px;font-size:.75rem;font-weight:600;line-height:1}.card__badge--default{background-color:var(--color-surface-hover);color:var(--color-text-secondary);border:1px solid var(--color-border)}.card__badge--primary{background-color:#20849729;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.card__badge--success{background-color:#8ea4752e;color:var(--color-success)}.card__badge--warning{background-color:#e1a0402e;color:var(--color-warning)}.card__badge--error{background-color:#9d0e0e2e;color:var(--color-error)}.card__subtitle{margin:.25rem 0 0;font-size:.8125rem;color:var(--color-text-secondary);line-height:1.4}.card__header-action{flex-shrink:0;display:flex;align-items:center;gap:.5rem}:host ::ng-deep [card-header],:host ::ng-deep .card-header{display:flex;align-items:center;justify-content:space-between;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-divider, #e5e7eb);font-weight:600;font-size:1.125rem;color:var(--color-text-primary)}:host ::ng-deep .card-content{color:var(--color-text-primary)}:host ::ng-deep [card-footer],:host ::ng-deep .card-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-top:var(--lc-density-padding-md, var(--spacing-4));padding-top:var(--lc-density-padding-sm, var(--spacing-3));border-top:1px solid var(--color-divider, #e5e7eb);color:var(--color-text-secondary)}:host ::ng-deep .card-dashed lc-icon{color:var(--color-muted-tile-icon, var(--color-text-tertiary))}\n"]
195
+ styles: [":host{display:block;height:100%}.card{position:relative;display:flex;flex-direction:column;height:100%;background-color:var(--color-surface);color:var(--color-text-primary);transition:box-shadow .15s ease-in-out,transform .15s ease-in-out,border-color .15s ease-in-out}.card__body{flex:1;display:flex;flex-direction:column}.card-elevated{position:relative;overflow:hidden;background-image:var(--gradient-surface);border:1px solid var(--color-border);box-shadow:var(--elevation-1)}.card-elevated:after{content:\"\";position:absolute;inset:0 0 auto;height:3px;background:var(--accent-line);opacity:.7;pointer-events:none}.card-elevated:hover{box-shadow:var(--elevation-2)}.card-outlined{background-image:none;border:1px solid var(--color-border)}.card-flat{background-image:none}.card-raised{background-image:none;border:1px solid var(--color-border);box-shadow:var(--shadow-auth-card, var(--elevation-3))}.card-dashed{background-color:var(--color-muted-tile, var(--color-surface-muted));background-image:none;border:1px dashed var(--color-muted-tile-border, var(--color-border));box-shadow:none;color:var(--color-text-secondary)}.card-padding-none{padding:0}.card-padding-xs{padding:var(--lc-density-padding-xs, var(--spacing-2))}.card-padding-sm{padding:var(--lc-density-padding-sm, var(--spacing-3))}.card-padding-md{padding:var(--lc-density-padding-md, var(--spacing-4))}.card-padding-lg{padding:var(--lc-density-padding-lg, var(--spacing-6))}.card-radius-none{border-radius:var(--border-radius-none)}.card-radius-sm{border-radius:var(--border-radius-sm)}.card-radius-md{border-radius:var(--border-radius-md)}.card-radius-lg{border-radius:var(--border-radius-lg)}.card-radius-xl{border-radius:var(--border-radius-xl, 1.5rem)}.card-radius-full{border-radius:var(--border-radius-full)}.card-clickable{cursor:pointer;-webkit-user-select:none;user-select:none}.card-clickable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.card-clickable:hover{background-color:var(--color-surface-hover);border-color:var(--color-border-strong)}.card-clickable:active{transform:scale(.98)}.card-selected{border:1px solid var(--color-primary);background-color:var(--color-surface-selected);box-shadow:var(--shadow-brand-glow)}.card-selected.card-elevated{box-shadow:var(--shadow-brand-glow),var(--elevation-2)}.card__header{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--lc-density-gap-md, var(--spacing-4));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-border, #e5e7eb)}.card__icon{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:var(--border-radius-md)}.card__icon--size-sm{width:1.75rem;height:1.75rem;border-radius:var(--border-radius-sm)}.card__icon--brand{background-image:var(--gradient-brand-tile);color:var(--color-on-primary, #fff);box-shadow:var(--shadow-brand-glow)}.card__icon--subtle{background-color:#20849724;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.card__header-left{flex:1;min-width:0}.card__title-row{display:flex;align-items:center;gap:.5rem}.card__badge-slot{display:inline-flex;align-items:center}.card__badge-slot:empty{display:none}.card__title{margin:0;font-weight:600;font-size:var(--lc-card-title-size, var(--font-size-lg, 1.125rem));line-height:1.3;color:var(--color-text-primary)}.card__badge{display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 .375rem;border-radius:9999px;font-size:.75rem;font-weight:600;line-height:1}.card__badge--default{background-color:var(--color-surface-hover);color:var(--color-text-secondary);border:1px solid var(--color-border)}.card__badge--primary{background-color:#20849729;color:var(--color-primary-500);border:1px solid var(--color-border-strong)}.card__badge--success{background-color:var(--color-success-subtle);color:var(--color-on-success-subtle)}.card__badge--warning{background-color:var(--color-warning-subtle);color:var(--color-on-warning-subtle)}.card__badge--error{background-color:var(--color-error-subtle);color:var(--color-on-error-subtle)}.card__subtitle{margin:.25rem 0 0;font-size:.8125rem;color:var(--color-text-secondary);line-height:1.4}.card__header-action{flex-shrink:0;display:flex;align-items:center;gap:.5rem}:host ::ng-deep [card-header],:host ::ng-deep .card-header{display:flex;align-items:center;justify-content:space-between;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-bottom:var(--lc-density-padding-md, var(--spacing-4));padding-bottom:var(--lc-density-padding-sm, var(--spacing-3));border-bottom:1px solid var(--color-divider, #e5e7eb);font-weight:600;font-size:1.125rem;color:var(--color-text-primary)}:host ::ng-deep .card-content{color:var(--color-text-primary)}:host ::ng-deep [card-footer],:host ::ng-deep .card-footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--lc-density-gap-sm, var(--spacing-2));margin-top:var(--lc-density-padding-md, var(--spacing-4));padding-top:var(--lc-density-padding-sm, var(--spacing-3));border-top:1px solid var(--color-divider, #e5e7eb);color:var(--color-text-secondary)}:host ::ng-deep .card-dashed lc-icon{color:var(--color-muted-tile-icon, var(--color-text-tertiary))}\n"]
196
196
  }]
197
197
  }],
198
198
  propDecorators: {
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-card.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/card/card.component.ts","../../../../libs/angular-ui-kit/src/lib/card/card.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n output,\n HostListener,\n computed,\n} from '@angular/core';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\n\n/** Visual variant of `lc-card`. */\nexport type CardVariant = 'elevated' | 'outlined' | 'flat' | 'raised' | 'dashed';\n\n/** Heading level of `lc-card`'s built-in title. */\nexport type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;\n\n/**\n * Card component for content grouping and visual hierarchy.\n *\n * Features:\n * - Variant styles (elevated, outlined, flat, raised, dashed)\n * - Configurable padding (none, xs, sm, md, lg)\n * - Border radius options (none, sm, md, lg, xl, full)\n * - Optional title display, with an optional leading icon tile\n * - Content projection for flexible body content\n *\n * @example\n * ```html\n * <lc-card title=\"Card Title\" variant=\"elevated\" padding=\"md\">\n * <p>Card body content</p>\n * </lc-card>\n *\n * <!-- With a leading brand-tile icon in the header -->\n * <lc-card title=\"Repository\" subtitle=\"Checkout &amp; profile\" icon=\"git-branch\">\n * ...\n * </lc-card>\n * ```\n */\n@Component({\n selector: 'lc-card',\n standalone: true,\n imports: [IconComponent],\n templateUrl: './card.component.html',\n styleUrls: ['./card.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CardComponent {\n /**\n * Optional title rendered as a styled header.\n * For complex headers (e.g. title + action button), use content projection instead.\n */\n title = input<string | undefined>(undefined);\n\n /**\n * Optional subtitle rendered below the title.\n */\n subtitle = input<string | undefined>(undefined);\n\n /**\n * Optional leading icon (Tabler icon name) rendered as a teal brand tile to\n * the left of the title. Only shown when `title` is set.\n */\n icon = input<string | undefined>(undefined);\n\n /**\n * Visual treatment of the leading icon tile.\n * - brand: gradient teal tile with light ink (default)\n * - subtle: translucent surface tile with brand-teal ink\n * @default 'brand'\n */\n iconVariant = input<'brand' | 'subtle'>('brand');\n\n /**\n * Size of the leading icon tile.\n * - md: 2.25rem tile (default)\n * - sm: compact ~1.75rem chip for dense card headers\n * @default 'md'\n */\n iconSize = input<'sm' | 'md'>('md');\n\n /**\n * Optional badge text/count shown next to the title (e.g. item count).\n * For a richer status badge (e.g. a leading status dot), project an\n * `lc-badge` into the `card-badge` slot instead — the slot takes precedence.\n */\n badge = input<string | undefined>(undefined);\n\n /**\n * Badge color variant.\n * @default 'default'\n */\n badgeVariant = input<'default' | 'primary' | 'success' | 'warning' | 'error'>('default');\n\n /**\n * Visual variant of the card\n * - elevated: Box shadow (default)\n * - outlined: Border only (the hairline `--color-border`, no shadow) — the\n * content card on the \"Weite\" sky with `borderRadius=\"xl\"`\n * - flat: No shadow or border\n * - raised: plain surface, hairline border and the long, soft\n * `--shadow-auth-card`, no accent line — the sign-in card\n * - dashed: the muted \"coming soon\" tile — `--color-muted-tile` fill, 1 px\n * dashed `--color-muted-tile-border`, secondary ink, icons in\n * `--color-muted-tile-icon`. Never clickable: `clickable` is ignored\n * @default 'elevated'\n */\n variant = input<CardVariant>('elevated');\n\n /**\n * Padding inside the card\n * @default 'md'\n */\n padding = input<'none' | 'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the card is clickable (hover effects, cursor pointer)\n * @default false\n */\n clickable = input<boolean>(false);\n\n /**\n * Whether the card is selected (if clickable)\n * @default false\n */\n selected = input<boolean>(false);\n\n /**\n * Border radius size: `none`, `sm`, `md`, `lg` (18 px), `xl` (24 px, the\n * \"Weite\" content card) or `full`.\n * @default 'md'\n */\n borderRadius = input<'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full'>('md');\n\n /**\n * Heading level of the built-in `title` (`h2`–`h6`), so the card fits the\n * page's outline. Semantics only, the look stays the same.\n * @default 3\n */\n headingLevel = input<CardHeadingLevel>(3);\n\n /**\n * ARIA label for accessibility\n */\n ariaLabel = input<string | undefined>(undefined);\n\n /**\n * Emitted when card is clicked (if clickable)\n */\n readonly cardClick = output<MouseEvent>();\n\n /**\n * Whether the card acts as a button: `clickable`, except for the dashed\n * \"coming soon\" tile, which is never interactive.\n */\n protected isClickable = computed(() => this.clickable() && this.variant() !== 'dashed');\n\n /**\n * Compute card CSS classes based on inputs\n */\n protected cardClasses = computed(() => {\n const classes = [\n 'card',\n `card-${this.variant()}`,\n `card-padding-${this.padding()}`,\n `card-radius-${this.borderRadius()}`,\n ];\n\n if (this.isClickable()) {\n classes.push('card-clickable');\n }\n\n // The dashed tile is never interactive, so it is never selected either.\n if (this.selected() && this.variant() !== 'dashed') {\n classes.push('card-selected');\n }\n\n return classes.join(' ');\n });\n\n /**\n * Compute ARIA role based on clickable state\n */\n protected role = computed(() => (this.isClickable() ? 'button' : 'article'));\n\n /**\n * Handle card click events\n */\n protected handleClick(event: MouseEvent): void {\n if (this.isClickable()) {\n this.cardClick.emit(event);\n }\n }\n\n /**\n * Handle keyboard events for accessibility\n */\n @HostListener('keydown', ['$event'])\n protected handleKeydown(event: KeyboardEvent): void {\n if (this.isClickable() && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n const mouseEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n });\n this.cardClick.emit(mouseEvent);\n }\n }\n}\n","<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -- Enter/Space are handled by the host keydown listener (role=button, tabindex=0 when clickable) -->\n<div\n [class]=\"cardClasses()\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.tabindex]=\"isClickable() ? 0 : null\"\n (click)=\"handleClick($event)\"\n>\n @if (title()) {\n <div class=\"card__header\">\n @if (icon()) {\n <div\n class=\"card__icon card__icon--{{ iconVariant() }} card__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"card__header-left\">\n <div class=\"card__title-row\">\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\">{{ title() }}</h2>\n }\n @case (4) {\n <h4 class=\"card__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 class=\"card__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 class=\"card__title\">{{ title() }}</h6>\n }\n @default {\n <h3 class=\"card__title\">{{ title() }}</h3>\n }\n }\n <!-- Rich status badge: project an lc-badge here (takes visual\n precedence over the `badge` string). Wrapper hides when empty. -->\n <span class=\"card__badge-slot\"><ng-content select=\"[card-badge]\"></ng-content></span>\n @if (badge()) {\n <span class=\"card__badge card__badge--{{ badgeVariant() }}\">{{ badge() }}</span>\n }\n </div>\n @if (subtitle()) {\n <p class=\"card__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n <div class=\"card__header-action\">\n <ng-content select=\"[card-header-action]\"></ng-content>\n </div>\n </div>\n }\n <ng-content select=\"[card-header]\"></ng-content>\n <div class=\"card__body\">\n <ng-content></ng-content>\n </div>\n <ng-content select=\"[card-footer]\"></ng-content>\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-card.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/card/card.component.ts","../../../../libs/angular-ui-kit/src/lib/card/card.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n output,\n HostListener,\n computed,\n} from '@angular/core';\nimport { IconComponent } from '@lifecorner/angular-ui-kit/icon';\n\n/** Visual variant of `lc-card`. */\nexport type CardVariant = 'elevated' | 'outlined' | 'flat' | 'raised' | 'dashed';\n\n/** Heading level of `lc-card`'s built-in title. */\nexport type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;\n\n/**\n * Card component for content grouping and visual hierarchy.\n *\n * Features:\n * - Variant styles (elevated, outlined, flat, raised, dashed)\n * - Configurable padding (none, xs, sm, md, lg)\n * - Border radius options (none, sm, md, lg, xl, full)\n * - Optional title display, with an optional leading icon tile\n * - Content projection for flexible body content\n *\n * @example\n * ```html\n * <lc-card title=\"Card Title\" variant=\"elevated\" padding=\"md\">\n * <p>Card body content</p>\n * </lc-card>\n *\n * <!-- With a leading brand-tile icon in the header -->\n * <lc-card title=\"Repository\" subtitle=\"Checkout &amp; profile\" icon=\"git-branch\">\n * ...\n * </lc-card>\n * ```\n */\n@Component({\n selector: 'lc-card',\n standalone: true,\n imports: [IconComponent],\n templateUrl: './card.component.html',\n styleUrls: ['./card.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CardComponent {\n /**\n * Optional title rendered as a styled header.\n * For complex headers (e.g. title + action button), use content projection instead.\n */\n title = input<string | undefined>(undefined);\n\n /**\n * Optional subtitle rendered below the title.\n */\n subtitle = input<string | undefined>(undefined);\n\n /**\n * Optional leading icon (Tabler icon name) rendered as a teal brand tile to\n * the left of the title. Only shown when `title` is set.\n */\n icon = input<string | undefined>(undefined);\n\n /**\n * Visual treatment of the leading icon tile.\n * - brand: gradient teal tile with light ink (default)\n * - subtle: translucent surface tile with brand-teal ink\n * @default 'brand'\n */\n iconVariant = input<'brand' | 'subtle'>('brand');\n\n /**\n * Size of the leading icon tile.\n * - md: 2.25rem tile (default)\n * - sm: compact ~1.75rem chip for dense card headers\n * @default 'md'\n */\n iconSize = input<'sm' | 'md'>('md');\n\n /**\n * Optional badge text/count shown next to the title (e.g. item count).\n * For a richer status badge (e.g. a leading status dot), project an\n * `lc-badge` into the `card-badge` slot instead — the slot takes precedence.\n */\n badge = input<string | undefined>(undefined);\n\n /**\n * Badge color variant.\n * @default 'default'\n */\n badgeVariant = input<'default' | 'primary' | 'success' | 'warning' | 'error'>('default');\n\n /**\n * Visual variant of the card\n * - elevated: Box shadow (default)\n * - outlined: Border only (the hairline `--color-border`, no shadow) — the\n * content card on the \"Weite\" sky with `borderRadius=\"xl\"`\n * - flat: No shadow or border\n * - raised: plain surface, hairline border and the long, soft\n * `--shadow-auth-card`, no accent line — the sign-in card\n * - dashed: the muted \"coming soon\" tile — `--color-muted-tile` fill, 1 px\n * dashed `--color-muted-tile-border`, secondary ink, icons in\n * `--color-muted-tile-icon`. Never clickable: `clickable` is ignored\n * @default 'elevated'\n */\n variant = input<CardVariant>('elevated');\n\n /**\n * Padding inside the card\n * @default 'md'\n */\n padding = input<'none' | 'xs' | 'sm' | 'md' | 'lg'>('md');\n\n /**\n * Whether the card is clickable (hover effects, cursor pointer)\n * @default false\n */\n clickable = input<boolean>(false);\n\n /**\n * Whether the card is selected (if clickable)\n * @default false\n */\n selected = input<boolean>(false);\n\n /**\n * Border radius size: `none`, `sm`, `md`, `lg` (18 px), `xl` (24 px, the\n * \"Weite\" content card) or `full`.\n * @default 'md'\n */\n borderRadius = input<'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full'>('md');\n\n /**\n * Heading level of the built-in `title` (`h2`–`h6`), so the card fits the\n * page's outline. Semantics only, the look stays the same.\n * @default 3\n */\n headingLevel = input<CardHeadingLevel>(3);\n\n /**\n * ARIA label for accessibility\n */\n ariaLabel = input<string | undefined>(undefined);\n\n /**\n * Emitted when card is clicked (if clickable)\n */\n readonly cardClick = output<MouseEvent>();\n\n /**\n * Whether the card acts as a button: `clickable`, except for the dashed\n * \"coming soon\" tile, which is never interactive.\n */\n protected isClickable = computed(() => this.clickable() && this.variant() !== 'dashed');\n\n /**\n * Compute card CSS classes based on inputs\n */\n protected cardClasses = computed(() => {\n const classes = [\n 'card',\n `card-${this.variant()}`,\n `card-padding-${this.padding()}`,\n `card-radius-${this.borderRadius()}`,\n ];\n\n if (this.isClickable()) {\n classes.push('card-clickable');\n }\n\n // The dashed tile is never interactive, so it is never selected either.\n if (this.selected() && this.variant() !== 'dashed') {\n classes.push('card-selected');\n }\n\n return classes.join(' ');\n });\n\n /**\n * Compute ARIA role based on clickable state\n */\n protected role = computed(() => (this.isClickable() ? 'button' : 'article'));\n\n /**\n * Handle card click events\n */\n protected handleClick(event: MouseEvent): void {\n if (this.isClickable()) {\n this.cardClick.emit(event);\n }\n }\n\n /**\n * Handle keyboard events for accessibility\n */\n @HostListener('keydown', ['$event'])\n protected handleKeydown(event: KeyboardEvent): void {\n if (this.isClickable() && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n const mouseEvent = new MouseEvent('click', {\n bubbles: true,\n cancelable: true,\n });\n this.cardClick.emit(mouseEvent);\n }\n }\n}\n","<!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events -- Enter/Space are handled by the host keydown listener (role=button, tabindex=0 when clickable) -->\n<div\n [class]=\"cardClasses()\"\n [attr.role]=\"role()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.tabindex]=\"isClickable() ? 0 : null\"\n (click)=\"handleClick($event)\"\n>\n @if (title()) {\n <div class=\"card__header\">\n @if (icon()) {\n <div\n class=\"card__icon card__icon--{{ iconVariant() }} card__icon--size-{{ iconSize() }}\"\n aria-hidden=\"true\"\n >\n <lc-icon [name]=\"icon()!\" [size]=\"iconSize() === 'sm' ? 'xs' : 'sm'\" [decorative]=\"true\" />\n </div>\n }\n <div class=\"card__header-left\">\n <div class=\"card__title-row\">\n @switch (headingLevel()) {\n @case (2) {\n <h2 class=\"card__title\">{{ title() }}</h2>\n }\n @case (4) {\n <h4 class=\"card__title\">{{ title() }}</h4>\n }\n @case (5) {\n <h5 class=\"card__title\">{{ title() }}</h5>\n }\n @case (6) {\n <h6 class=\"card__title\">{{ title() }}</h6>\n }\n @default {\n <h3 class=\"card__title\">{{ title() }}</h3>\n }\n }\n <!-- Rich status badge: project an lc-badge here (takes visual\n precedence over the `badge` string). Wrapper hides when empty. -->\n <span class=\"card__badge-slot\"><ng-content select=\"[card-badge]\"></ng-content></span>\n @if (badge()) {\n <span class=\"card__badge card__badge--{{ badgeVariant() }}\">{{ badge() }}</span>\n }\n </div>\n @if (subtitle()) {\n <p class=\"card__subtitle\">{{ subtitle() }}</p>\n }\n </div>\n <div class=\"card__header-action\">\n <ng-content select=\"[card-header-action]\"></ng-content>\n </div>\n </div>\n }\n <ng-content select=\"[card-header]\"></ng-content>\n <div class=\"card__body\">\n <ng-content></ng-content>\n </div>\n <ng-content select=\"[card-footer]\"></ng-content>\n</div>\n"],"mappings":"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"}