@life-cockpit/angular-ui-kit 2.2.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, PLATFORM_ID, signal, computed, Injectable, input, effect, ChangeDetectionStrategy, Component, model, contentChildren, untracked, output, ViewChild, ViewEncapsulation, HostListener, forwardRef, InjectionToken, ElementRef, viewChild, ChangeDetectorRef, EventEmitter, Output, Input, ViewChildren, HostBinding, ViewContainerRef, Renderer2, Directive, TemplateRef, ContentChildren, ContentChild, DestroyRef, afterNextRender, SecurityContext, NgZone, ApplicationRef, EnvironmentInjector, createComponent } from '@angular/core';
|
|
2
|
+
import { inject, PLATFORM_ID, signal, computed, Injectable, input, effect, ChangeDetectionStrategy, Component, model, contentChildren, untracked, output, ViewChild, ViewEncapsulation, HostListener, forwardRef, InjectionToken, ElementRef, viewChild, ChangeDetectorRef, EventEmitter, Output, Input, ViewChildren, HostBinding, ViewContainerRef, Renderer2, Directive, TemplateRef, ContentChildren, ContentChild, linkedSignal, DestroyRef, afterNextRender, SecurityContext, NgZone, ApplicationRef, EnvironmentInjector, createComponent } from '@angular/core';
|
|
3
3
|
import { isPlatformBrowser, NgClass, CommonModule, NgTemplateOutlet, NgStyle, SlicePipe } from '@angular/common';
|
|
4
4
|
import * as i1$4 from '@angular/platform-browser';
|
|
5
5
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
@@ -7121,6 +7121,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
|
|
|
7121
7121
|
* - Per-cell class/style callbacks for conditional styling
|
|
7122
7122
|
* - Custom cell templates via content projection
|
|
7123
7123
|
* - Composed cells (e.g. avatar + badge + actions)
|
|
7124
|
+
* - Hierarchical "tree" rows (grouped / expandable) via `idKey` + `parentKey`
|
|
7124
7125
|
* - Responsive horizontal scrolling
|
|
7125
7126
|
* - Empty state text for no data
|
|
7126
7127
|
* - Accessible with proper table semantics
|
|
@@ -7190,6 +7191,31 @@ class TableComponent {
|
|
|
7190
7191
|
actionsWidth = input(undefined, ...(ngDevMode ? [{ debugName: "actionsWidth" }] : /* istanbul ignore next */ []));
|
|
7191
7192
|
/** Horizontal alignment of the action buttons (and header label) within the cell */
|
|
7192
7193
|
actionsAlign = input('start', ...(ngDevMode ? [{ debugName: "actionsAlign" }] : /* istanbul ignore next */ []));
|
|
7194
|
+
// -- Tree / grouped rows --
|
|
7195
|
+
/**
|
|
7196
|
+
* Row field holding the unique id. Required (together with {@link parentKey})
|
|
7197
|
+
* to enable hierarchical "tree mode". Absent ⇒ today's flat table.
|
|
7198
|
+
*/
|
|
7199
|
+
idKey = input(undefined, ...(ngDevMode ? [{ debugName: "idKey" }] : /* istanbul ignore next */ []));
|
|
7200
|
+
/**
|
|
7201
|
+
* Row field referencing the parent row's id. Presence of both `idKey` and
|
|
7202
|
+
* `parentKey` enables tree mode. Rows with an absent/empty or unknown parent
|
|
7203
|
+
* id are treated as roots.
|
|
7204
|
+
*/
|
|
7205
|
+
parentKey = input(undefined, ...(ngDevMode ? [{ debugName: "parentKey" }] : /* istanbul ignore next */ []));
|
|
7206
|
+
/** Column key the chevron + indentation attach to. Default: first column. */
|
|
7207
|
+
treeColumn = input(undefined, ...(ngDevMode ? [{ debugName: "treeColumn" }] : /* istanbul ignore next */ []));
|
|
7208
|
+
/** Initial expand state for all parent rows when uncontrolled. Default: true. */
|
|
7209
|
+
defaultExpanded = input(true, ...(ngDevMode ? [{ debugName: "defaultExpanded" }] : /* istanbul ignore next */ []));
|
|
7210
|
+
/**
|
|
7211
|
+
* Controlled set of expanded parent ids. When provided, the table renders
|
|
7212
|
+
* exactly these as expanded; combine with {@link expandedIdsChange} for
|
|
7213
|
+
* `[(expandedIds)]`. When omitted, expansion is uncontrolled and seeded by
|
|
7214
|
+
* {@link defaultExpanded}.
|
|
7215
|
+
*/
|
|
7216
|
+
expandedIds = input(undefined, ...(ngDevMode ? [{ debugName: "expandedIds" }] : /* istanbul ignore next */ []));
|
|
7217
|
+
/** Indentation per depth level, in px. Default: 20. */
|
|
7218
|
+
indentSize = input(20, ...(ngDevMode ? [{ debugName: "indentSize" }] : /* istanbul ignore next */ []));
|
|
7193
7219
|
/** Emitted when a sortable column header is clicked */
|
|
7194
7220
|
sort = output();
|
|
7195
7221
|
/** Emitted when a row is clicked */
|
|
@@ -7200,14 +7226,24 @@ class TableComponent {
|
|
|
7200
7226
|
selectionChange = output();
|
|
7201
7227
|
/** Emitted when a row action button is clicked */
|
|
7202
7228
|
actionClick = output();
|
|
7229
|
+
/** Emitted when a parent row is expanded/collapsed (tree mode). */
|
|
7230
|
+
rowToggle = output();
|
|
7231
|
+
/** Emitted with the full list of expanded parent ids; enables `[(expandedIds)]`. */
|
|
7232
|
+
expandedIdsChange = output();
|
|
7203
7233
|
// -- Internal state --
|
|
7204
7234
|
currentSort = signal(null, ...(ngDevMode ? [{ debugName: "currentSort" }] : /* istanbul ignore next */ []));
|
|
7205
7235
|
currentPage = signal(0, ...(ngDevMode ? [{ debugName: "currentPage" }] : /* istanbul ignore next */ []));
|
|
7206
|
-
|
|
7236
|
+
/**
|
|
7237
|
+
* Effective page size. Seeded from the `pageSize` input (resetting if it
|
|
7238
|
+
* changes) and overridable by the user via the page-size dropdown.
|
|
7239
|
+
*/
|
|
7240
|
+
internalPageSize = linkedSignal(() => this.pageSize(), ...(ngDevMode ? [{ debugName: "internalPageSize" }] : /* istanbul ignore next */ []));
|
|
7207
7241
|
selectedRows = signal(new Set(), ...(ngDevMode ? [{ debugName: "selectedRows" }] : /* istanbul ignore next */ []));
|
|
7208
7242
|
columnFilters = signal({}, ...(ngDevMode ? [{ debugName: "columnFilters" }] : /* istanbul ignore next */ []));
|
|
7209
7243
|
editingCell = signal(null, ...(ngDevMode ? [{ debugName: "editingCell" }] : /* istanbul ignore next */ []));
|
|
7210
7244
|
editValue = signal('', ...(ngDevMode ? [{ debugName: "editValue" }] : /* istanbul ignore next */ []));
|
|
7245
|
+
/** User expand/collapse overrides keyed by row id (uncontrolled mode). */
|
|
7246
|
+
expandOverrides = signal(new Map(), ...(ngDevMode ? [{ debugName: "expandOverrides" }] : /* istanbul ignore next */ []));
|
|
7211
7247
|
/**
|
|
7212
7248
|
* Filtered data (applies column filters)
|
|
7213
7249
|
*/
|
|
@@ -7223,15 +7259,9 @@ class TableComponent {
|
|
|
7223
7259
|
}
|
|
7224
7260
|
return items;
|
|
7225
7261
|
}, ...(ngDevMode ? [{ debugName: "filteredData" }] : /* istanbul ignore next */ []));
|
|
7226
|
-
/**
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
sortedData = computed(() => {
|
|
7230
|
-
const items = [...this.filteredData()];
|
|
7231
|
-
const sortState = this.currentSort();
|
|
7232
|
-
if (!sortState)
|
|
7233
|
-
return items;
|
|
7234
|
-
return items.sort((a, b) => {
|
|
7262
|
+
/** Comparator for the active sort, shared by flat sorting and tree sibling sorting. */
|
|
7263
|
+
rowComparator(sortState) {
|
|
7264
|
+
return (a, b) => {
|
|
7235
7265
|
const aVal = a[sortState.column];
|
|
7236
7266
|
const bVal = b[sortState.column];
|
|
7237
7267
|
let cmp = 0;
|
|
@@ -7246,10 +7276,20 @@ class TableComponent {
|
|
|
7246
7276
|
else
|
|
7247
7277
|
cmp = String(aVal).localeCompare(String(bVal));
|
|
7248
7278
|
return sortState.direction === 'asc' ? cmp : -cmp;
|
|
7249
|
-
}
|
|
7279
|
+
};
|
|
7280
|
+
}
|
|
7281
|
+
/**
|
|
7282
|
+
* Sorted data
|
|
7283
|
+
*/
|
|
7284
|
+
sortedData = computed(() => {
|
|
7285
|
+
const items = [...this.filteredData()];
|
|
7286
|
+
const sortState = this.currentSort();
|
|
7287
|
+
if (!sortState)
|
|
7288
|
+
return items;
|
|
7289
|
+
return items.sort(this.rowComparator(sortState));
|
|
7250
7290
|
}, ...(ngDevMode ? [{ debugName: "sortedData" }] : /* istanbul ignore next */ []));
|
|
7251
7291
|
/**
|
|
7252
|
-
* Paginated data (or all if pagination disabled)
|
|
7292
|
+
* Paginated data (or all if pagination disabled). Flat mode only.
|
|
7253
7293
|
*/
|
|
7254
7294
|
displayData = computed(() => {
|
|
7255
7295
|
const items = this.sortedData();
|
|
@@ -7259,21 +7299,178 @@ class TableComponent {
|
|
|
7259
7299
|
const start = this.currentPage() * ps;
|
|
7260
7300
|
return items.slice(start, start + ps);
|
|
7261
7301
|
}, ...(ngDevMode ? [{ debugName: "displayData" }] : /* istanbul ignore next */ []));
|
|
7302
|
+
// -- Tree / grouped rows -----------------------------------------------------
|
|
7303
|
+
/** Whether hierarchical tree mode is active. */
|
|
7304
|
+
treeMode = computed(() => !!this.idKey() && !!this.parentKey(), ...(ngDevMode ? [{ debugName: "treeMode" }] : /* istanbul ignore next */ []));
|
|
7305
|
+
/** Column the chevron + indentation attach to (defaults to the first column). */
|
|
7306
|
+
effectiveTreeColumn = computed(() => this.treeColumn() || this.columns()[0]?.key, ...(ngDevMode ? [{ debugName: "effectiveTreeColumn" }] : /* istanbul ignore next */ []));
|
|
7307
|
+
/** Stable map of row object → its index in `data()` (row identity for tree mode). */
|
|
7308
|
+
dataIndexMap = computed(() => {
|
|
7309
|
+
const map = new Map();
|
|
7310
|
+
this.data().forEach((row, i) => map.set(row, i));
|
|
7311
|
+
return map;
|
|
7312
|
+
}, ...(ngDevMode ? [{ debugName: "dataIndexMap" }] : /* istanbul ignore next */ []));
|
|
7313
|
+
/** Parent → ordered children index built from `idKey`/`parentKey`. */
|
|
7314
|
+
treeBuild = computed(() => {
|
|
7315
|
+
const idKey = this.idKey();
|
|
7316
|
+
const parentKey = this.parentKey();
|
|
7317
|
+
const byId = new Map();
|
|
7318
|
+
const childrenOf = new Map();
|
|
7319
|
+
const roots = [];
|
|
7320
|
+
if (!idKey || !parentKey)
|
|
7321
|
+
return { byId, childrenOf, roots };
|
|
7322
|
+
const rows = this.data();
|
|
7323
|
+
for (const row of rows) {
|
|
7324
|
+
const id = String(row[idKey] ?? '');
|
|
7325
|
+
if (id)
|
|
7326
|
+
byId.set(id, row);
|
|
7327
|
+
}
|
|
7328
|
+
for (const row of rows) {
|
|
7329
|
+
const parentRaw = row[parentKey];
|
|
7330
|
+
const parentId = parentRaw == null ? '' : String(parentRaw);
|
|
7331
|
+
if (parentId && byId.has(parentId)) {
|
|
7332
|
+
const siblings = childrenOf.get(parentId) ?? [];
|
|
7333
|
+
siblings.push(row);
|
|
7334
|
+
childrenOf.set(parentId, siblings);
|
|
7335
|
+
}
|
|
7336
|
+
else {
|
|
7337
|
+
// No parent, empty parent, or orphaned (unknown parent) ⇒ root.
|
|
7338
|
+
roots.push(row);
|
|
7339
|
+
}
|
|
7340
|
+
}
|
|
7341
|
+
return { byId, childrenOf, roots };
|
|
7342
|
+
}, ...(ngDevMode ? [{ debugName: "treeBuild" }] : /* istanbul ignore next */ []));
|
|
7343
|
+
/**
|
|
7344
|
+
* Tree restricted to rows that match the active filters together with their
|
|
7345
|
+
* ancestor chain (so the path to a match stays visible).
|
|
7346
|
+
*/
|
|
7347
|
+
treeFiltered = computed(() => {
|
|
7348
|
+
const { byId, childrenOf, roots } = this.treeBuild();
|
|
7349
|
+
const parentKey = this.parentKey();
|
|
7350
|
+
const filters = this.columnFilters();
|
|
7351
|
+
const active = this.filterable() && parentKey && Object.values(filters).some((v) => v.trim());
|
|
7352
|
+
if (!active)
|
|
7353
|
+
return { childrenOf, roots };
|
|
7354
|
+
const matches = (row) => Object.entries(filters).every(([key, value]) => !value.trim() ||
|
|
7355
|
+
String(row[key] ?? '').toLowerCase().includes(value.toLowerCase()));
|
|
7356
|
+
// Visible = every matching row plus all of its ancestors.
|
|
7357
|
+
const visible = new Set();
|
|
7358
|
+
for (const row of this.data()) {
|
|
7359
|
+
if (!matches(row))
|
|
7360
|
+
continue;
|
|
7361
|
+
let current = row;
|
|
7362
|
+
while (current && !visible.has(current)) {
|
|
7363
|
+
visible.add(current);
|
|
7364
|
+
const parentRaw = current[parentKey];
|
|
7365
|
+
const parentId = parentRaw == null ? '' : String(parentRaw);
|
|
7366
|
+
current = parentId ? byId.get(parentId) : undefined;
|
|
7367
|
+
}
|
|
7368
|
+
}
|
|
7369
|
+
const visibleChildrenOf = new Map();
|
|
7370
|
+
const visibleRoots = [];
|
|
7371
|
+
for (const row of this.data()) {
|
|
7372
|
+
if (!visible.has(row))
|
|
7373
|
+
continue;
|
|
7374
|
+
const parentRaw = row[parentKey];
|
|
7375
|
+
const parentId = parentRaw == null ? '' : String(parentRaw);
|
|
7376
|
+
const parent = parentId ? byId.get(parentId) : undefined;
|
|
7377
|
+
if (parent && visible.has(parent)) {
|
|
7378
|
+
const siblings = visibleChildrenOf.get(parentId) ?? [];
|
|
7379
|
+
siblings.push(row);
|
|
7380
|
+
visibleChildrenOf.set(parentId, siblings);
|
|
7381
|
+
}
|
|
7382
|
+
else {
|
|
7383
|
+
visibleRoots.push(row);
|
|
7384
|
+
}
|
|
7385
|
+
}
|
|
7386
|
+
return { childrenOf: visibleChildrenOf, roots: visibleRoots };
|
|
7387
|
+
}, ...(ngDevMode ? [{ debugName: "treeFiltered" }] : /* istanbul ignore next */ []));
|
|
7388
|
+
/** Tree with each sibling group sorted by the active sort (structure kept intact). */
|
|
7389
|
+
treeSorted = computed(() => {
|
|
7390
|
+
const { childrenOf, roots } = this.treeFiltered();
|
|
7391
|
+
const sortState = this.currentSort();
|
|
7392
|
+
if (!sortState)
|
|
7393
|
+
return { childrenOf, roots };
|
|
7394
|
+
const cmp = this.rowComparator(sortState);
|
|
7395
|
+
const sortedChildrenOf = new Map();
|
|
7396
|
+
for (const [parentId, siblings] of childrenOf) {
|
|
7397
|
+
sortedChildrenOf.set(parentId, [...siblings].sort(cmp));
|
|
7398
|
+
}
|
|
7399
|
+
return { childrenOf: sortedChildrenOf, roots: [...roots].sort(cmp) };
|
|
7400
|
+
}, ...(ngDevMode ? [{ debugName: "treeSorted" }] : /* istanbul ignore next */ []));
|
|
7401
|
+
/** Root rows after filter/sort — the unit of pagination in tree mode. */
|
|
7402
|
+
treeRoots = computed(() => this.treeSorted().roots, ...(ngDevMode ? [{ debugName: "treeRoots" }] : /* istanbul ignore next */ []));
|
|
7403
|
+
/**
|
|
7404
|
+
* Render-ready rows. In flat mode this wraps the existing paginated data; in
|
|
7405
|
+
* tree mode it paginates roots and flattens each visible (expanded) subtree.
|
|
7406
|
+
*/
|
|
7407
|
+
displayRows = computed(() => {
|
|
7408
|
+
if (!this.treeMode()) {
|
|
7409
|
+
return this.displayData().map((row, i) => ({
|
|
7410
|
+
row,
|
|
7411
|
+
absIndex: this.getAbsoluteIndex(i),
|
|
7412
|
+
id: null,
|
|
7413
|
+
depth: 0,
|
|
7414
|
+
level: 0,
|
|
7415
|
+
hasChildren: false,
|
|
7416
|
+
expanded: false,
|
|
7417
|
+
posInSet: 0,
|
|
7418
|
+
setSize: 0,
|
|
7419
|
+
}));
|
|
7420
|
+
}
|
|
7421
|
+
const idKey = this.idKey();
|
|
7422
|
+
const { childrenOf } = this.treeSorted();
|
|
7423
|
+
let roots = this.treeRoots();
|
|
7424
|
+
if (this.paginate()) {
|
|
7425
|
+
const ps = this.internalPageSize();
|
|
7426
|
+
const start = this.currentPage() * ps;
|
|
7427
|
+
roots = roots.slice(start, start + ps);
|
|
7428
|
+
}
|
|
7429
|
+
const dataIndex = this.dataIndexMap();
|
|
7430
|
+
const out = [];
|
|
7431
|
+
const walk = (group, depth) => {
|
|
7432
|
+
group.forEach((row, idx) => {
|
|
7433
|
+
const id = String(row[idKey] ?? '');
|
|
7434
|
+
const children = childrenOf.get(id) ?? [];
|
|
7435
|
+
const hasChildren = children.length > 0;
|
|
7436
|
+
const expanded = hasChildren && this.isExpanded(id);
|
|
7437
|
+
out.push({
|
|
7438
|
+
row,
|
|
7439
|
+
absIndex: dataIndex.get(row) ?? -1,
|
|
7440
|
+
id,
|
|
7441
|
+
depth,
|
|
7442
|
+
level: depth + 1,
|
|
7443
|
+
hasChildren,
|
|
7444
|
+
expanded,
|
|
7445
|
+
posInSet: idx + 1,
|
|
7446
|
+
setSize: group.length,
|
|
7447
|
+
});
|
|
7448
|
+
if (expanded)
|
|
7449
|
+
walk(children, depth + 1);
|
|
7450
|
+
});
|
|
7451
|
+
};
|
|
7452
|
+
walk(roots, 0);
|
|
7453
|
+
return out;
|
|
7454
|
+
}, ...(ngDevMode ? [{ debugName: "displayRows" }] : /* istanbul ignore next */ []));
|
|
7262
7455
|
/** Total pages */
|
|
7263
7456
|
totalPages = computed(() => {
|
|
7264
7457
|
if (!this.paginate())
|
|
7265
7458
|
return 1;
|
|
7266
|
-
|
|
7459
|
+
const total = this.treeMode() ? this.treeRoots().length : this.filteredData().length;
|
|
7460
|
+
return Math.max(1, Math.ceil(total / this.internalPageSize()));
|
|
7267
7461
|
}, ...(ngDevMode ? [{ debugName: "totalPages" }] : /* istanbul ignore next */ []));
|
|
7268
|
-
/**
|
|
7269
|
-
|
|
7462
|
+
/**
|
|
7463
|
+
* Pagination row count. Tree mode counts root rows (the paginated unit); flat
|
|
7464
|
+
* mode counts filtered rows.
|
|
7465
|
+
*/
|
|
7466
|
+
totalRows = computed(() => this.treeMode() ? this.treeRoots().length : this.filteredData().length, ...(ngDevMode ? [{ debugName: "totalRows" }] : /* istanbul ignore next */ []));
|
|
7270
7467
|
/** Whether all visible rows are selected */
|
|
7271
7468
|
allSelected = computed(() => {
|
|
7272
|
-
const
|
|
7273
|
-
if (
|
|
7469
|
+
const rows = this.displayRows();
|
|
7470
|
+
if (rows.length === 0)
|
|
7274
7471
|
return false;
|
|
7275
7472
|
const selected = this.selectedRows();
|
|
7276
|
-
return
|
|
7473
|
+
return rows.every((r) => selected.has(r.absIndex));
|
|
7277
7474
|
}, ...(ngDevMode ? [{ debugName: "allSelected" }] : /* istanbul ignore next */ []));
|
|
7278
7475
|
pageSizeSelectOptions = computed(() => this.pageSizeOptions().map((opt) => ({
|
|
7279
7476
|
value: opt,
|
|
@@ -7338,13 +7535,13 @@ class TableComponent {
|
|
|
7338
7535
|
getCellValue(row, columnKey) {
|
|
7339
7536
|
return row[columnKey];
|
|
7340
7537
|
}
|
|
7341
|
-
getFormattedCellValue(row, column,
|
|
7538
|
+
getFormattedCellValue(row, column, rowIndex) {
|
|
7342
7539
|
const value = this.getCellValue(row, column.key);
|
|
7343
7540
|
if (!column.formatter)
|
|
7344
7541
|
return value;
|
|
7345
|
-
return column.formatter(value, row, column,
|
|
7542
|
+
return column.formatter(value, row, column, rowIndex);
|
|
7346
7543
|
}
|
|
7347
|
-
getCellClasses(row, column,
|
|
7544
|
+
getCellClasses(row, column, rowIndex) {
|
|
7348
7545
|
const classes = [];
|
|
7349
7546
|
if (column.cssClass)
|
|
7350
7547
|
classes.push(column.cssClass);
|
|
@@ -7352,20 +7549,33 @@ class TableComponent {
|
|
|
7352
7549
|
classes.push(column.cellClass);
|
|
7353
7550
|
}
|
|
7354
7551
|
else if (typeof column.cellClass === 'function') {
|
|
7355
|
-
const resolved = column.cellClass(this.getCellValue(row, column.key), row, column,
|
|
7552
|
+
const resolved = column.cellClass(this.getCellValue(row, column.key), row, column, rowIndex);
|
|
7356
7553
|
if (resolved)
|
|
7357
7554
|
classes.push(resolved);
|
|
7358
7555
|
}
|
|
7359
7556
|
return classes.join(' ').trim();
|
|
7360
7557
|
}
|
|
7361
|
-
getCellStyles(row, column,
|
|
7558
|
+
getCellStyles(row, column, rowIndex) {
|
|
7362
7559
|
if (!column.cellStyle)
|
|
7363
7560
|
return null;
|
|
7364
7561
|
if (typeof column.cellStyle === 'function') {
|
|
7365
|
-
return column.cellStyle(this.getCellValue(row, column.key), row, column,
|
|
7562
|
+
return column.cellStyle(this.getCellValue(row, column.key), row, column, rowIndex);
|
|
7366
7563
|
}
|
|
7367
7564
|
return column.cellStyle;
|
|
7368
7565
|
}
|
|
7566
|
+
/** Whether the given column is the disclosure/indent column in tree mode. */
|
|
7567
|
+
isTreeColumn(columnKey) {
|
|
7568
|
+
return this.treeMode() && columnKey === this.effectiveTreeColumn();
|
|
7569
|
+
}
|
|
7570
|
+
/** Indent levels to render before a row's chevron (one guide per ancestor depth). */
|
|
7571
|
+
indentLevels(depth) {
|
|
7572
|
+
return Array.from({ length: depth }, (_, i) => i);
|
|
7573
|
+
}
|
|
7574
|
+
/** Accessible name for a row's disclosure button (the tree column's value). */
|
|
7575
|
+
treeCellLabel(row) {
|
|
7576
|
+
const col = this.effectiveTreeColumn();
|
|
7577
|
+
return col ? String(row[col] ?? '') : '';
|
|
7578
|
+
}
|
|
7369
7579
|
getCellTemplate(columnKey) {
|
|
7370
7580
|
return this.cellTemplates?.find((template) => template.columnKey() === columnKey);
|
|
7371
7581
|
}
|
|
@@ -7378,21 +7588,62 @@ class TableComponent {
|
|
|
7378
7588
|
// -- Row Actions --
|
|
7379
7589
|
/** Whether the trailing actions column should be rendered */
|
|
7380
7590
|
hasActions = computed(() => this.actions().length > 0, ...(ngDevMode ? [{ debugName: "hasActions" }] : /* istanbul ignore next */ []));
|
|
7381
|
-
isActionHidden(action, row,
|
|
7382
|
-
return action.hidden ? action.hidden(row,
|
|
7591
|
+
isActionHidden(action, row, rowIndex) {
|
|
7592
|
+
return action.hidden ? action.hidden(row, rowIndex) : false;
|
|
7383
7593
|
}
|
|
7384
|
-
isActionDisabled(action, row,
|
|
7385
|
-
return action.disabled ? action.disabled(row,
|
|
7594
|
+
isActionDisabled(action, row, rowIndex) {
|
|
7595
|
+
return action.disabled ? action.disabled(row, rowIndex) : false;
|
|
7386
7596
|
}
|
|
7387
7597
|
/**
|
|
7388
7598
|
* Handles an action button click. The DOM click is stopped from propagating
|
|
7389
7599
|
* (see template `$event.stopPropagation()`), so the row's `rowClick` never fires.
|
|
7390
7600
|
*/
|
|
7391
|
-
onActionClick(action, row,
|
|
7601
|
+
onActionClick(action, row, rowIndex) {
|
|
7392
7602
|
this.actionClick.emit({
|
|
7393
7603
|
action: action.key,
|
|
7394
7604
|
row,
|
|
7395
|
-
rowIndex
|
|
7605
|
+
rowIndex,
|
|
7606
|
+
});
|
|
7607
|
+
}
|
|
7608
|
+
// -- Tree expand / collapse --------------------------------------------------
|
|
7609
|
+
/** Whether the parent row with the given id is currently expanded. */
|
|
7610
|
+
isExpanded(id) {
|
|
7611
|
+
const controlled = this.expandedIds();
|
|
7612
|
+
if (controlled !== undefined)
|
|
7613
|
+
return controlled.includes(id);
|
|
7614
|
+
const override = this.expandOverrides().get(id);
|
|
7615
|
+
if (override !== undefined)
|
|
7616
|
+
return override;
|
|
7617
|
+
return this.defaultExpanded();
|
|
7618
|
+
}
|
|
7619
|
+
/** Chevron click: toggle expand state without triggering the row click. */
|
|
7620
|
+
onChevronClick(displayRow, event) {
|
|
7621
|
+
event.stopPropagation();
|
|
7622
|
+
if (displayRow.id)
|
|
7623
|
+
this.toggleRow(displayRow.id, displayRow.row);
|
|
7624
|
+
}
|
|
7625
|
+
toggleRow(id, row) {
|
|
7626
|
+
const expanded = !this.isExpanded(id);
|
|
7627
|
+
this.expandOverrides.update((map) => {
|
|
7628
|
+
const next = new Map(map);
|
|
7629
|
+
next.set(id, expanded);
|
|
7630
|
+
return next;
|
|
7631
|
+
});
|
|
7632
|
+
this.rowToggle.emit({ id, expanded, row });
|
|
7633
|
+
this.expandedIdsChange.emit(this.nextExpandedIds(id, expanded));
|
|
7634
|
+
}
|
|
7635
|
+
/** The full list of expanded parent ids after applying a single toggle. */
|
|
7636
|
+
nextExpandedIds(changedId, changedExpanded) {
|
|
7637
|
+
const controlled = this.expandedIds();
|
|
7638
|
+
const overrides = this.expandOverrides();
|
|
7639
|
+
const parentIds = Array.from(this.treeBuild().childrenOf.keys());
|
|
7640
|
+
return parentIds.filter((pid) => {
|
|
7641
|
+
if (pid === changedId)
|
|
7642
|
+
return changedExpanded;
|
|
7643
|
+
if (controlled !== undefined)
|
|
7644
|
+
return controlled.includes(pid);
|
|
7645
|
+
const override = overrides.get(pid);
|
|
7646
|
+
return override !== undefined ? override : this.defaultExpanded();
|
|
7396
7647
|
});
|
|
7397
7648
|
}
|
|
7398
7649
|
// -- Pagination --
|
|
@@ -7426,30 +7677,28 @@ class TableComponent {
|
|
|
7426
7677
|
// -- Selection --
|
|
7427
7678
|
toggleSelectAll() {
|
|
7428
7679
|
const selected = new Set(this.selectedRows());
|
|
7429
|
-
const
|
|
7680
|
+
const rows = this.displayRows();
|
|
7430
7681
|
const allSelected = this.allSelected();
|
|
7431
|
-
|
|
7432
|
-
const absIdx = this.getAbsoluteIndex(i);
|
|
7682
|
+
rows.forEach((r) => {
|
|
7433
7683
|
if (allSelected)
|
|
7434
|
-
selected.delete(
|
|
7684
|
+
selected.delete(r.absIndex);
|
|
7435
7685
|
else
|
|
7436
|
-
selected.add(
|
|
7686
|
+
selected.add(r.absIndex);
|
|
7437
7687
|
});
|
|
7438
7688
|
this.selectedRows.set(selected);
|
|
7439
7689
|
this.emitSelectionChange();
|
|
7440
7690
|
}
|
|
7441
|
-
toggleRowSelect(
|
|
7442
|
-
const absIdx = this.getAbsoluteIndex(relativeIndex);
|
|
7691
|
+
toggleRowSelect(absIndex) {
|
|
7443
7692
|
const selected = new Set(this.selectedRows());
|
|
7444
|
-
if (selected.has(
|
|
7445
|
-
selected.delete(
|
|
7693
|
+
if (selected.has(absIndex))
|
|
7694
|
+
selected.delete(absIndex);
|
|
7446
7695
|
else
|
|
7447
|
-
selected.add(
|
|
7696
|
+
selected.add(absIndex);
|
|
7448
7697
|
this.selectedRows.set(selected);
|
|
7449
7698
|
this.emitSelectionChange();
|
|
7450
7699
|
}
|
|
7451
|
-
isRowSelected(
|
|
7452
|
-
return this.selectedRows().has(
|
|
7700
|
+
isRowSelected(absIndex) {
|
|
7701
|
+
return this.selectedRows().has(absIndex);
|
|
7453
7702
|
}
|
|
7454
7703
|
getAbsoluteIndex(relativeIndex) {
|
|
7455
7704
|
if (!this.paginate())
|
|
@@ -7471,37 +7720,36 @@ class TableComponent {
|
|
|
7471
7720
|
return this.columnFilters()[columnKey] || '';
|
|
7472
7721
|
}
|
|
7473
7722
|
// -- Inline Editing --
|
|
7474
|
-
startEdit(
|
|
7723
|
+
startEdit(absIndex, column, currentValue) {
|
|
7475
7724
|
if (!this.editable())
|
|
7476
7725
|
return;
|
|
7477
7726
|
const col = this.columns().find(c => c.key === column);
|
|
7478
7727
|
if (col && col.editable === false)
|
|
7479
7728
|
return;
|
|
7480
|
-
this.editingCell.set({ rowIndex, column });
|
|
7729
|
+
this.editingCell.set({ rowIndex: absIndex, column });
|
|
7481
7730
|
this.editValue.set(String(currentValue ?? ''));
|
|
7482
7731
|
}
|
|
7483
|
-
isEditing(
|
|
7732
|
+
isEditing(absIndex, column) {
|
|
7484
7733
|
const cell = this.editingCell();
|
|
7485
|
-
return cell !== null && cell.rowIndex ===
|
|
7734
|
+
return cell !== null && cell.rowIndex === absIndex && cell.column === column;
|
|
7486
7735
|
}
|
|
7487
|
-
commitEdit(
|
|
7488
|
-
const
|
|
7489
|
-
const row = this.data()[absIdx];
|
|
7736
|
+
commitEdit(absIndex, column) {
|
|
7737
|
+
const row = this.data()[absIndex];
|
|
7490
7738
|
if (!row)
|
|
7491
7739
|
return;
|
|
7492
7740
|
const oldValue = row[column];
|
|
7493
7741
|
const newValue = this.editValue();
|
|
7494
7742
|
this.editingCell.set(null);
|
|
7495
7743
|
if (String(oldValue ?? '') !== newValue) {
|
|
7496
|
-
this.cellEdit.emit({ row, column, oldValue, newValue, rowIndex:
|
|
7744
|
+
this.cellEdit.emit({ row, column, oldValue, newValue, rowIndex: absIndex });
|
|
7497
7745
|
}
|
|
7498
7746
|
}
|
|
7499
7747
|
cancelEdit() {
|
|
7500
7748
|
this.editingCell.set(null);
|
|
7501
7749
|
}
|
|
7502
|
-
onEditKeydown(event,
|
|
7750
|
+
onEditKeydown(event, absIndex, column) {
|
|
7503
7751
|
if (event.key === 'Enter') {
|
|
7504
|
-
this.commitEdit(
|
|
7752
|
+
this.commitEdit(absIndex, column);
|
|
7505
7753
|
}
|
|
7506
7754
|
else if (event.key === 'Escape') {
|
|
7507
7755
|
this.cancelEdit();
|
|
@@ -7511,15 +7759,15 @@ class TableComponent {
|
|
|
7511
7759
|
return event.target.value;
|
|
7512
7760
|
}
|
|
7513
7761
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7514
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: TableComponent, isStandalone: true, selector: "lc-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, paginate: { classPropertyName: "paginate", publicName: "paginate", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, actionsLabel: { classPropertyName: "actionsLabel", publicName: "actionsLabel", isSignal: true, isRequired: false, transformFunction: null }, actionsWidth: { classPropertyName: "actionsWidth", publicName: "actionsWidth", isSignal: true, isRequired: false, transformFunction: null }, actionsAlign: { classPropertyName: "actionsAlign", publicName: "actionsAlign", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sort: "sort", rowClick: "rowClick", cellEdit: "cellEdit", selectionChange: "selectionChange", actionClick: "actionClick" }, queries: [{ propertyName: "cellTemplates", predicate: TableCellDirective }], ngImport: i0, template: "<div [class]=\"wrapperClasses()\">\n <table [class]=\"tableClasses()\" role=\"table\">\n <thead>\n <!-- Filter row -->\n @if (filterable()) {\n <tr class=\"lc-table__filter-row\">\n @if (selectable()) {\n <th class=\"lc-table__select-cell\"></th>\n }\n @for (column of columns(); track column.key) {\n <th>\n @if (column.filterable !== false) {\n <lc-input\n class=\"lc-table__filter-input\"\n size=\"xs\"\n [placeholder]=\"'Filter ' + column.label\"\n [ariaLabel]=\"'Filter by ' + column.label\"\n [ngModel]=\"getFilterValue(column.key)\"\n (ngModelChange)=\"onFilterChange(column.key, $event)\"\n />\n }\n </th>\n }\n @if (hasActions()) {\n <th class=\"lc-table__actions-cell\"></th>\n }\n </tr>\n }\n <!-- Header row -->\n <tr>\n @if (selectable()) {\n <th class=\"lc-table__select-cell\" scope=\"col\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"allSelected()\"\n (change)=\"toggleSelectAll()\"\n aria-label=\"Select all rows\"\n />\n </th>\n }\n @for (column of columns(); track column.key) {\n <th\n scope=\"col\"\n [class]=\"getHeaderClasses(column) + (column.cssClass ? ' ' + column.cssClass : '')\"\n [attr.aria-sort]=\"getAriaSort(column.key)\"\n [style.width]=\"column.width\"\n [title]=\"column.tooltip || ''\"\n (click)=\"handleSort(column.key)\">\n {{ column.label }}\n @if (column.sortable && getSortState(column.key)) {\n <span class=\"sort-indicator\" [attr.aria-label]=\"getSortState(column.key) === 'asc' ? 'sorted ascending' : 'sorted descending'\">\n @if (getSortState(column.key) === 'asc') {\n \u2191\n } @else {\n \u2193\n }\n </span>\n }\n </th>\n }\n @if (hasActions()) {\n <th\n scope=\"col\"\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [style.width]=\"actionsWidth()\"\n >\n {{ actionsLabel() }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (displayData().length === 0) {\n <tr>\n <td [attr.colspan]=\"columns().length + (selectable() ? 1 : 0) + (hasActions() ? 1 : 0)\" class=\"lc-table__empty\">\n {{ emptyText() }}\n </td>\n </tr>\n } @else {\n @for (row of displayData(); track $index; let i = $index) {\n <tr\n [class.lc-table__row--selected]=\"selectable() && isRowSelected(i)\"\n (click)=\"onRowClick(row)\"\n >\n @if (selectable()) {\n <td class=\"lc-table__select-cell\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"isRowSelected($index)\"\n (change)=\"toggleRowSelect($index)\"\n (click)=\"$event.stopPropagation()\"\n [attr.aria-label]=\"'Select row ' + ($index + 1)\"\n />\n </td>\n }\n @for (column of columns(); track column.key) {\n <td\n [class]=\"getCellClasses(row, column, i)\"\n [ngStyle]=\"getCellStyles(row, column, i)\"\n (dblclick)=\"editable() ? startEdit($index, column.key, getCellValue(row, column.key)) : null\"\n >\n @if (isEditing($index, column.key)) {\n <input\n type=\"text\"\n class=\"lc-table__edit-input\"\n [value]=\"editValue()\"\n (input)=\"editValue.set(getInputValue($event))\"\n (blur)=\"commitEdit($index, column.key)\"\n (keydown)=\"onEditKeydown($event, $index, column.key)\"\n autofocus\n />\n } @else if (hasCustomTemplate(column.key)) {\n <ng-container *ngTemplateOutlet=\"getCellTemplate(column.key)!.template; context: { $implicit: row }\"></ng-container>\n } @else {\n {{ getFormattedCellValue(row, column, i) }}\n }\n </td>\n }\n @if (hasActions()) {\n <td\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"lc-table__actions\">\n @for (action of actions(); track action.key) {\n @if (!isActionHidden(action, row, i)) {\n <lc-button\n [variant]=\"action.variant ?? 'ghost'\"\n size=\"xs\"\n [iconOnly]=\"!action.label\"\n [disabled]=\"isActionDisabled(action, row, i)\"\n [ariaLabel]=\"action.tooltip ?? action.label ?? action.key\"\n (clicked)=\"onActionClick(action, row, i)\"\n >\n @if (action.icon) {\n <lc-icon [name]=\"action.icon\" size=\"sm\" [decorative]=\"true\" />\n }\n @if (action.label) {\n {{ action.label }}\n }\n </lc-button>\n }\n }\n </div>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n<!-- Pagination -->\n@if (paginate()) {\n <div class=\"lc-table-pagination\">\n <div class=\"lc-table-pagination__info\">\n {{ paginationStart }}\u2013{{ paginationEnd }} of {{ totalRows() }}\n </div>\n <div class=\"lc-table-pagination__controls\">\n <lc-select\n class=\"lc-table-pagination__size\"\n [size]=\"size() === 'sm' ? 'sm' : size() === 'lg' ? 'lg' : 'md'\"\n [options]=\"pageSizeSelectOptions()\"\n [ngModel]=\"internalPageSize()\"\n [ariaLabel]=\"'Rows per page'\"\n (ngModelChange)=\"onPageSizeModelChange($event)\"\n />\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() === 0\"\n (click)=\"goToPage(currentPage() - 1)\"\n aria-label=\"Previous page\"\n >\u2039</button>\n <span class=\"lc-table-pagination__page\">\n {{ currentPage() + 1 }} / {{ totalPages() }}\n </span>\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() >= totalPages() - 1\"\n (click)=\"goToPage(currentPage() + 1)\"\n aria-label=\"Next page\"\n >\u203A</button>\n </div>\n </div>\n}\n", styles: [".lc-table-wrapper{width:100%}.lc-table-wrapper--responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:768px){.lc-table-wrapper--responsive::-webkit-scrollbar{height:8px}.lc-table-wrapper--responsive::-webkit-scrollbar-thumb{background-color:var(--color-border);border-radius:var(--border-radius-sm)}}.lc-table{width:100%;border-collapse:collapse;border-spacing:0;background-color:var(--color-surface);color:var(--color-text-primary);font-family:var(--typography-font-family)}.lc-table thead{background-color:var(--color-surface-2);border-bottom:2px solid var(--color-border-strong)}.lc-table thead tr th{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));text-align:left;font-weight:var(--typography-font-weight-semibold);font-size:var(--typography-font-size-sm);text-transform:uppercase;letter-spacing:.04em;color:var(--color-text-secondary);white-space:nowrap;-webkit-user-select:none;user-select:none}.lc-table thead tr th.sortable{cursor:pointer;position:relative;padding-right:var(--spacing-8);transition:background-color .2s ease}.lc-table thead tr th.sortable:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table thead tr th.sortable .sort-indicator{position:absolute;right:var(--spacing-3);top:50%;transform:translateY(-50%);font-size:var(--typography-font-size-xs);color:var(--color-primary)}.lc-table thead tr th.sorted-asc,.lc-table thead tr th.sorted-desc{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table tbody tr{border-bottom:1px solid var(--color-divider)}.lc-table tbody tr:last-child{border-bottom:none}.lc-table tbody tr td{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));font-size:var(--typography-font-size-sm);color:var(--color-text-primary)}.lc-table__empty{text-align:center;padding:var(--spacing-8) var(--spacing-4)!important;color:var(--color-text-secondary);font-style:italic}.lc-table--striped tbody tr:nth-child(2n){background-color:var(--color-surface-2)}.lc-table--bordered{border:1px solid var(--color-border)}.lc-table--bordered thead tr th,.lc-table--bordered tbody tr td{border-right:1px solid var(--color-divider)}.lc-table--bordered thead tr th:last-child,.lc-table--bordered tbody tr td:last-child{border-right:none}.lc-table--bordered tbody tr{border-bottom:1px solid var(--color-border)}.lc-table--hoverable tbody tr{transition:background-color .15s ease;cursor:pointer}.lc-table--hoverable tbody tr:hover{background-color:var(--color-surface-hover)}.lc-table--sm thead tr th,.lc-table--sm tbody tr td{padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--typography-font-size-xs)}.lc-table--lg thead tr th,.lc-table--lg tbody tr td{padding:var(--lc-density-padding-md, var(--spacing-4)) var(--spacing-5);font-size:var(--typography-font-size-base)}.lc-table__filter-row th{padding:var(--spacing-2) var(--spacing-4)!important;background-color:var(--color-background)!important;border-bottom:1px solid var(--color-divider)!important}.lc-table__filter-input{width:100%;display:block}.lc-table__filter-input .input-field{font-size:var(--typography-font-size-xs)}.lc-table__select-cell{width:40px;text-align:center!important;padding:var(--spacing-2)!important}.lc-table__checkbox{width:16px;height:16px;cursor:pointer;accent-color:var(--color-primary)}.lc-table__row--selected{background-color:var(--color-surface-selected)!important}.lc-table__actions-cell{white-space:nowrap}.lc-table th.lc-table__actions-cell--start,.lc-table td.lc-table__actions-cell--start{text-align:left}.lc-table th.lc-table__actions-cell--center,.lc-table td.lc-table__actions-cell--center{text-align:center}.lc-table th.lc-table__actions-cell--end,.lc-table td.lc-table__actions-cell--end{text-align:right}.lc-table__actions{display:flex;align-items:center;gap:var(--spacing-2);justify-content:flex-start}.lc-table__actions-cell--center .lc-table__actions{justify-content:center}.lc-table__actions-cell--end .lc-table__actions{justify-content:flex-end}.lc-table__edit-input{width:100%;padding:var(--spacing-1) var(--spacing-2);border:2px solid var(--color-primary);border-radius:var(--border-radius-sm);font-size:inherit;background-color:var(--color-surface-2);color:var(--color-text-primary);outline:none}.lc-table-pagination{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-3) var(--spacing-1);font-size:var(--typography-font-size-sm);color:var(--color-text-secondary)}.lc-table-pagination__info{white-space:nowrap}.lc-table-pagination__controls{display:flex;align-items:center;gap:var(--spacing-2)}.lc-table-pagination__size{display:block;min-width:110px}.lc-table-pagination__btn{appearance:none;border:1px solid var(--color-border);background-color:var(--color-surface);color:var(--color-text-primary);width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:var(--border-radius-sm);font-size:var(--typography-font-size-base);cursor:pointer;transition:background-color .15s ease}.lc-table-pagination__btn:hover:not(:disabled){background-color:var(--color-surface-hover)}.lc-table-pagination__btn:disabled{opacity:.4;cursor:not-allowed}.lc-table-pagination__page{white-space:nowrap;min-width:60px;text-align:center}@media(max-width:640px){.lc-table-wrapper--stack .lc-table thead{display:none}.lc-table-wrapper--stack .lc-table tbody tr{display:block;margin-bottom:var(--spacing-4);border:1px solid var(--color-border);border-radius:var(--border-radius-md)}.lc-table-wrapper--stack .lc-table tbody tr td{display:block;text-align:right;padding:var(--spacing-2) var(--spacing-4);border-bottom:1px solid var(--color-divider)}.lc-table-wrapper--stack .lc-table tbody tr td:last-child{border-bottom:none}.lc-table-wrapper--stack .lc-table tbody tr td:before{content:attr(data-label);float:left;font-weight:var(--typography-font-weight-semibold);color:var(--color-text-secondary)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: SelectComponent, selector: "lc-select", inputs: ["variant", "size", "disabled", "error", "required", "loading", "searchable", "multiple", "placeholder", "helperText", "errorMessage", "ariaLabel", "options"], outputs: ["selectionChange", "opened", "closed"] }, { kind: "component", type: InputComponent, selector: "lc-input", inputs: ["label", "placeholder", "type", "size", "disabled", "readonly", "required", "error", "helperText", "iconBefore", "iconAfter", "maxLength", "showCharCount", "ariaLabel"], outputs: ["valueChange", "focused", "blurred", "enterPressed"] }, { kind: "component", type: ButtonComponent, selector: "lc-button", inputs: ["variant", "size", "disabled", "loading", "iconOnly", "fullWidth", "ariaLabel", "type"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: IconComponent, selector: "lc-icon", inputs: ["name", "variant", "size", "color", "ariaLabel", "decorative"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7762
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: TableComponent, isStandalone: true, selector: "lc-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, paginate: { classPropertyName: "paginate", publicName: "paginate", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, actionsLabel: { classPropertyName: "actionsLabel", publicName: "actionsLabel", isSignal: true, isRequired: false, transformFunction: null }, actionsWidth: { classPropertyName: "actionsWidth", publicName: "actionsWidth", isSignal: true, isRequired: false, transformFunction: null }, actionsAlign: { classPropertyName: "actionsAlign", publicName: "actionsAlign", isSignal: true, isRequired: false, transformFunction: null }, idKey: { classPropertyName: "idKey", publicName: "idKey", isSignal: true, isRequired: false, transformFunction: null }, parentKey: { classPropertyName: "parentKey", publicName: "parentKey", isSignal: true, isRequired: false, transformFunction: null }, treeColumn: { classPropertyName: "treeColumn", publicName: "treeColumn", isSignal: true, isRequired: false, transformFunction: null }, defaultExpanded: { classPropertyName: "defaultExpanded", publicName: "defaultExpanded", isSignal: true, isRequired: false, transformFunction: null }, expandedIds: { classPropertyName: "expandedIds", publicName: "expandedIds", isSignal: true, isRequired: false, transformFunction: null }, indentSize: { classPropertyName: "indentSize", publicName: "indentSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sort: "sort", rowClick: "rowClick", cellEdit: "cellEdit", selectionChange: "selectionChange", actionClick: "actionClick", rowToggle: "rowToggle", expandedIdsChange: "expandedIdsChange" }, queries: [{ propertyName: "cellTemplates", predicate: TableCellDirective }], ngImport: i0, template: "<div [class]=\"wrapperClasses()\">\n <table [class]=\"tableClasses()\" [attr.role]=\"treeMode() ? 'treegrid' : 'table'\">\n <thead>\n <!-- Filter row -->\n @if (filterable()) {\n <tr class=\"lc-table__filter-row\">\n @if (selectable()) {\n <th class=\"lc-table__select-cell\"></th>\n }\n @for (column of columns(); track column.key) {\n <th>\n @if (column.filterable !== false) {\n <lc-input\n class=\"lc-table__filter-input\"\n size=\"xs\"\n [placeholder]=\"'Filter ' + column.label\"\n [ariaLabel]=\"'Filter by ' + column.label\"\n [ngModel]=\"getFilterValue(column.key)\"\n (ngModelChange)=\"onFilterChange(column.key, $event)\"\n />\n }\n </th>\n }\n @if (hasActions()) {\n <th class=\"lc-table__actions-cell\"></th>\n }\n </tr>\n }\n <!-- Header row -->\n <tr>\n @if (selectable()) {\n <th class=\"lc-table__select-cell\" scope=\"col\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"allSelected()\"\n (change)=\"toggleSelectAll()\"\n aria-label=\"Select all rows\"\n />\n </th>\n }\n @for (column of columns(); track column.key) {\n <th\n scope=\"col\"\n [class]=\"getHeaderClasses(column) + (column.cssClass ? ' ' + column.cssClass : '')\"\n [attr.aria-sort]=\"getAriaSort(column.key)\"\n [style.width]=\"column.width\"\n [title]=\"column.tooltip || ''\"\n (click)=\"handleSort(column.key)\">\n {{ column.label }}\n @if (column.sortable && getSortState(column.key)) {\n <span class=\"sort-indicator\" [attr.aria-label]=\"getSortState(column.key) === 'asc' ? 'sorted ascending' : 'sorted descending'\">\n @if (getSortState(column.key) === 'asc') {\n \u2191\n } @else {\n \u2193\n }\n </span>\n }\n </th>\n }\n @if (hasActions()) {\n <th\n scope=\"col\"\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [style.width]=\"actionsWidth()\"\n >\n {{ actionsLabel() }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (displayRows().length === 0) {\n <tr>\n <td [attr.colspan]=\"columns().length + (selectable() ? 1 : 0) + (hasActions() ? 1 : 0)\" class=\"lc-table__empty\">\n {{ emptyText() }}\n </td>\n </tr>\n } @else {\n @for (dr of displayRows(); track dr.absIndex) {\n <tr\n [class.lc-table__row--selected]=\"selectable() && isRowSelected(dr.absIndex)\"\n [class.lc-table__row--group]=\"dr.hasChildren\"\n [attr.role]=\"treeMode() ? 'row' : null\"\n [attr.aria-level]=\"treeMode() ? dr.level : null\"\n [attr.aria-expanded]=\"treeMode() && dr.hasChildren ? dr.expanded : null\"\n [attr.aria-posinset]=\"treeMode() ? dr.posInSet : null\"\n [attr.aria-setsize]=\"treeMode() ? dr.setSize : null\"\n (click)=\"onRowClick(dr.row)\"\n >\n @if (selectable()) {\n <td class=\"lc-table__select-cell\" [attr.role]=\"treeMode() ? 'gridcell' : null\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"isRowSelected(dr.absIndex)\"\n (change)=\"toggleRowSelect(dr.absIndex)\"\n (click)=\"$event.stopPropagation()\"\n [attr.aria-label]=\"'Select row ' + (dr.absIndex + 1)\"\n />\n </td>\n }\n @for (column of columns(); track column.key) {\n <td\n [class]=\"getCellClasses(dr.row, column, dr.absIndex)\"\n [ngStyle]=\"getCellStyles(dr.row, column, dr.absIndex)\"\n [attr.role]=\"treeMode() ? 'gridcell' : null\"\n (dblclick)=\"editable() ? startEdit(dr.absIndex, column.key, getCellValue(dr.row, column.key)) : null\"\n >\n @if (isTreeColumn(column.key)) {\n <span class=\"lc-table__tree-cell\">\n @for (lvl of indentLevels(dr.depth); track lvl) {\n <span class=\"lc-table__tree-indent\" [style.width.px]=\"indentSize()\"></span>\n }\n <span class=\"lc-table__tree-chevron\" [style.width.px]=\"indentSize()\">\n @if (dr.hasChildren) {\n <button\n type=\"button\"\n class=\"lc-table__chevron-btn\"\n [class.lc-table__chevron-btn--expanded]=\"dr.expanded\"\n [attr.aria-label]=\"(dr.expanded ? 'Einklappen: ' : 'Aufklappen: ') + treeCellLabel(dr.row)\"\n [attr.aria-expanded]=\"dr.expanded\"\n (click)=\"onChevronClick(dr, $event)\"\n >\n <lc-icon name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </span>\n <span class=\"lc-table__tree-content\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { row: dr.row, column: column, absIndex: dr.absIndex }\"\n ></ng-container>\n </span>\n </span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { row: dr.row, column: column, absIndex: dr.absIndex }\"\n ></ng-container>\n }\n </td>\n }\n @if (hasActions()) {\n <td\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [attr.role]=\"treeMode() ? 'gridcell' : null\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"lc-table__actions\">\n @for (action of actions(); track action.key) {\n @if (!isActionHidden(action, dr.row, dr.absIndex)) {\n <lc-button\n [variant]=\"action.variant ?? 'ghost'\"\n size=\"xs\"\n [iconOnly]=\"!action.label\"\n [disabled]=\"isActionDisabled(action, dr.row, dr.absIndex)\"\n [ariaLabel]=\"action.tooltip ?? action.label ?? action.key\"\n (clicked)=\"onActionClick(action, dr.row, dr.absIndex)\"\n >\n @if (action.icon) {\n <lc-icon [name]=\"action.icon\" size=\"sm\" [decorative]=\"true\" />\n }\n @if (action.label) {\n {{ action.label }}\n }\n </lc-button>\n }\n }\n </div>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n<!-- Shared cell content (edit input / custom template / formatted value) -->\n<ng-template #cellContent let-row=\"row\" let-column=\"column\" let-absIndex=\"absIndex\">\n @if (isEditing(absIndex, column.key)) {\n <input\n type=\"text\"\n class=\"lc-table__edit-input\"\n [value]=\"editValue()\"\n (input)=\"editValue.set(getInputValue($event))\"\n (blur)=\"commitEdit(absIndex, column.key)\"\n (keydown)=\"onEditKeydown($event, absIndex, column.key)\"\n autofocus\n />\n } @else if (hasCustomTemplate(column.key)) {\n <ng-container *ngTemplateOutlet=\"getCellTemplate(column.key)!.template; context: { $implicit: row }\"></ng-container>\n } @else {\n {{ getFormattedCellValue(row, column, absIndex) }}\n }\n</ng-template>\n\n<!-- Pagination -->\n@if (paginate()) {\n <div class=\"lc-table-pagination\">\n <div class=\"lc-table-pagination__info\">\n {{ paginationStart }}\u2013{{ paginationEnd }} of {{ totalRows() }}\n </div>\n <div class=\"lc-table-pagination__controls\">\n <lc-select\n class=\"lc-table-pagination__size\"\n [size]=\"size() === 'sm' ? 'sm' : size() === 'lg' ? 'lg' : 'md'\"\n [options]=\"pageSizeSelectOptions()\"\n [ngModel]=\"internalPageSize()\"\n [ariaLabel]=\"'Rows per page'\"\n (ngModelChange)=\"onPageSizeModelChange($event)\"\n />\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() === 0\"\n (click)=\"goToPage(currentPage() - 1)\"\n aria-label=\"Previous page\"\n >\u2039</button>\n <span class=\"lc-table-pagination__page\">\n {{ currentPage() + 1 }} / {{ totalPages() }}\n </span>\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() >= totalPages() - 1\"\n (click)=\"goToPage(currentPage() + 1)\"\n aria-label=\"Next page\"\n >\u203A</button>\n </div>\n </div>\n}\n", styles: [".lc-table-wrapper{width:100%}.lc-table-wrapper--responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:768px){.lc-table-wrapper--responsive::-webkit-scrollbar{height:8px}.lc-table-wrapper--responsive::-webkit-scrollbar-thumb{background-color:var(--color-border);border-radius:var(--border-radius-sm)}}.lc-table{width:100%;border-collapse:collapse;border-spacing:0;background-color:var(--color-surface);color:var(--color-text-primary);font-family:var(--typography-font-family)}.lc-table thead{background-color:var(--color-surface-2);border-bottom:2px solid var(--color-border-strong)}.lc-table thead tr th{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));text-align:left;font-weight:var(--typography-font-weight-semibold);font-size:var(--typography-font-size-sm);text-transform:uppercase;letter-spacing:.04em;color:var(--color-text-secondary);white-space:nowrap;-webkit-user-select:none;user-select:none}.lc-table thead tr th.sortable{cursor:pointer;position:relative;padding-right:var(--spacing-8);transition:background-color .2s ease}.lc-table thead tr th.sortable:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table thead tr th.sortable .sort-indicator{position:absolute;right:var(--spacing-3);top:50%;transform:translateY(-50%);font-size:var(--typography-font-size-xs);color:var(--color-primary)}.lc-table thead tr th.sorted-asc,.lc-table thead tr th.sorted-desc{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table tbody tr{border-bottom:1px solid var(--color-divider)}.lc-table tbody tr:last-child{border-bottom:none}.lc-table tbody tr td{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));font-size:var(--typography-font-size-sm);color:var(--color-text-primary)}.lc-table__empty{text-align:center;padding:var(--spacing-8) var(--spacing-4)!important;color:var(--color-text-secondary);font-style:italic}.lc-table--striped tbody tr:nth-child(2n){background-color:var(--color-surface-2)}.lc-table--bordered{border:1px solid var(--color-border)}.lc-table--bordered thead tr th,.lc-table--bordered tbody tr td{border-right:1px solid var(--color-divider)}.lc-table--bordered thead tr th:last-child,.lc-table--bordered tbody tr td:last-child{border-right:none}.lc-table--bordered tbody tr{border-bottom:1px solid var(--color-border)}.lc-table--hoverable tbody tr{transition:background-color .15s ease;cursor:pointer}.lc-table--hoverable tbody tr:hover{background-color:var(--color-surface-hover)}.lc-table--sm thead tr th,.lc-table--sm tbody tr td{padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--typography-font-size-xs)}.lc-table--lg thead tr th,.lc-table--lg tbody tr td{padding:var(--lc-density-padding-md, var(--spacing-4)) var(--spacing-5);font-size:var(--typography-font-size-base)}.lc-table__filter-row th{padding:var(--spacing-2) var(--spacing-4)!important;background-color:var(--color-background)!important;border-bottom:1px solid var(--color-divider)!important}.lc-table__filter-input{width:100%;display:block}.lc-table__filter-input .input-field{font-size:var(--typography-font-size-xs)}.lc-table__select-cell{width:40px;text-align:center!important;padding:var(--spacing-2)!important}.lc-table__checkbox{width:16px;height:16px;cursor:pointer;accent-color:var(--color-primary)}.lc-table__row--selected{background-color:var(--color-surface-selected)!important}.lc-table__actions-cell{white-space:nowrap}.lc-table th.lc-table__actions-cell--start,.lc-table td.lc-table__actions-cell--start{text-align:left}.lc-table th.lc-table__actions-cell--center,.lc-table td.lc-table__actions-cell--center{text-align:center}.lc-table th.lc-table__actions-cell--end,.lc-table td.lc-table__actions-cell--end{text-align:right}.lc-table__actions{display:flex;align-items:center;gap:var(--spacing-2);justify-content:flex-start}.lc-table__actions-cell--center .lc-table__actions{justify-content:center}.lc-table__actions-cell--end .lc-table__actions{justify-content:flex-end}.lc-table__tree-cell{display:flex;align-items:center;min-width:0}.lc-table__tree-indent{flex:0 0 auto;align-self:stretch;position:relative}.lc-table__tree-indent:before{content:\"\";position:absolute;left:50%;top:0;bottom:0;width:1px;background-color:var(--color-border)}.lc-table__tree-chevron{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center}.lc-table__chevron-btn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--color-text-secondary);cursor:pointer;border-radius:var(--border-radius-sm);transition:transform .2s ease,background-color .15s ease,color .15s ease}.lc-table__chevron-btn:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table__chevron-btn:focus-visible{outline:2px solid var(--color-primary);outline-offset:1px}.lc-table__chevron-btn--expanded{transform:rotate(90deg)}.lc-table__tree-content{flex:1 1 auto;min-width:0}.lc-table__row--group .lc-table__tree-content{font-weight:var(--typography-font-weight-semibold)}.lc-table__edit-input{width:100%;padding:var(--spacing-1) var(--spacing-2);border:2px solid var(--color-primary);border-radius:var(--border-radius-sm);font-size:inherit;background-color:var(--color-surface-2);color:var(--color-text-primary);outline:none}.lc-table-pagination{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-3) var(--spacing-1);font-size:var(--typography-font-size-sm);color:var(--color-text-secondary)}.lc-table-pagination__info{white-space:nowrap}.lc-table-pagination__controls{display:flex;align-items:center;gap:var(--spacing-2)}.lc-table-pagination__size{display:block;min-width:110px}.lc-table-pagination__btn{appearance:none;border:1px solid var(--color-border);background-color:var(--color-surface);color:var(--color-text-primary);width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:var(--border-radius-sm);font-size:var(--typography-font-size-base);cursor:pointer;transition:background-color .15s ease}.lc-table-pagination__btn:hover:not(:disabled){background-color:var(--color-surface-hover)}.lc-table-pagination__btn:disabled{opacity:.4;cursor:not-allowed}.lc-table-pagination__page{white-space:nowrap;min-width:60px;text-align:center}@media(max-width:640px){.lc-table-wrapper--stack .lc-table thead{display:none}.lc-table-wrapper--stack .lc-table tbody tr{display:block;margin-bottom:var(--spacing-4);border:1px solid var(--color-border);border-radius:var(--border-radius-md)}.lc-table-wrapper--stack .lc-table tbody tr td{display:block;text-align:right;padding:var(--spacing-2) var(--spacing-4);border-bottom:1px solid var(--color-divider)}.lc-table-wrapper--stack .lc-table tbody tr td:last-child{border-bottom:none}.lc-table-wrapper--stack .lc-table tbody tr td:before{content:attr(data-label);float:left;font-weight:var(--typography-font-weight-semibold);color:var(--color-text-secondary)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: SelectComponent, selector: "lc-select", inputs: ["variant", "size", "disabled", "error", "required", "loading", "searchable", "multiple", "placeholder", "helperText", "errorMessage", "ariaLabel", "options"], outputs: ["selectionChange", "opened", "closed"] }, { kind: "component", type: InputComponent, selector: "lc-input", inputs: ["label", "placeholder", "type", "size", "disabled", "readonly", "required", "error", "helperText", "iconBefore", "iconAfter", "maxLength", "showCharCount", "ariaLabel"], outputs: ["valueChange", "focused", "blurred", "enterPressed"] }, { kind: "component", type: ButtonComponent, selector: "lc-button", inputs: ["variant", "size", "disabled", "loading", "iconOnly", "fullWidth", "ariaLabel", "type"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: IconComponent, selector: "lc-icon", inputs: ["name", "variant", "size", "color", "ariaLabel", "decorative"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7515
7763
|
}
|
|
7516
7764
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: TableComponent, decorators: [{
|
|
7517
7765
|
type: Component,
|
|
7518
|
-
args: [{ selector: 'lc-table', standalone: true, imports: [NgTemplateOutlet, NgStyle, FormsModule, SelectComponent, InputComponent, ButtonComponent, IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"wrapperClasses()\">\n <table [class]=\"tableClasses()\" role=\"table\">\n <thead>\n <!-- Filter row -->\n @if (filterable()) {\n <tr class=\"lc-table__filter-row\">\n @if (selectable()) {\n <th class=\"lc-table__select-cell\"></th>\n }\n @for (column of columns(); track column.key) {\n <th>\n @if (column.filterable !== false) {\n <lc-input\n class=\"lc-table__filter-input\"\n size=\"xs\"\n [placeholder]=\"'Filter ' + column.label\"\n [ariaLabel]=\"'Filter by ' + column.label\"\n [ngModel]=\"getFilterValue(column.key)\"\n (ngModelChange)=\"onFilterChange(column.key, $event)\"\n />\n }\n </th>\n }\n @if (hasActions()) {\n <th class=\"lc-table__actions-cell\"></th>\n }\n </tr>\n }\n <!-- Header row -->\n <tr>\n @if (selectable()) {\n <th class=\"lc-table__select-cell\" scope=\"col\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"allSelected()\"\n (change)=\"toggleSelectAll()\"\n aria-label=\"Select all rows\"\n />\n </th>\n }\n @for (column of columns(); track column.key) {\n <th\n scope=\"col\"\n [class]=\"getHeaderClasses(column) + (column.cssClass ? ' ' + column.cssClass : '')\"\n [attr.aria-sort]=\"getAriaSort(column.key)\"\n [style.width]=\"column.width\"\n [title]=\"column.tooltip || ''\"\n (click)=\"handleSort(column.key)\">\n {{ column.label }}\n @if (column.sortable && getSortState(column.key)) {\n <span class=\"sort-indicator\" [attr.aria-label]=\"getSortState(column.key) === 'asc' ? 'sorted ascending' : 'sorted descending'\">\n @if (getSortState(column.key) === 'asc') {\n \u2191\n } @else {\n \u2193\n }\n </span>\n }\n </th>\n }\n @if (hasActions()) {\n <th\n scope=\"col\"\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [style.width]=\"actionsWidth()\"\n >\n {{ actionsLabel() }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (displayData().length === 0) {\n <tr>\n <td [attr.colspan]=\"columns().length + (selectable() ? 1 : 0) + (hasActions() ? 1 : 0)\" class=\"lc-table__empty\">\n {{ emptyText() }}\n </td>\n </tr>\n } @else {\n @for (row of displayData(); track $index; let i = $index) {\n <tr\n [class.lc-table__row--selected]=\"selectable() && isRowSelected(i)\"\n (click)=\"onRowClick(row)\"\n >\n @if (selectable()) {\n <td class=\"lc-table__select-cell\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"isRowSelected($index)\"\n (change)=\"toggleRowSelect($index)\"\n (click)=\"$event.stopPropagation()\"\n [attr.aria-label]=\"'Select row ' + ($index + 1)\"\n />\n </td>\n }\n @for (column of columns(); track column.key) {\n <td\n [class]=\"getCellClasses(row, column, i)\"\n [ngStyle]=\"getCellStyles(row, column, i)\"\n (dblclick)=\"editable() ? startEdit($index, column.key, getCellValue(row, column.key)) : null\"\n >\n @if (isEditing($index, column.key)) {\n <input\n type=\"text\"\n class=\"lc-table__edit-input\"\n [value]=\"editValue()\"\n (input)=\"editValue.set(getInputValue($event))\"\n (blur)=\"commitEdit($index, column.key)\"\n (keydown)=\"onEditKeydown($event, $index, column.key)\"\n autofocus\n />\n } @else if (hasCustomTemplate(column.key)) {\n <ng-container *ngTemplateOutlet=\"getCellTemplate(column.key)!.template; context: { $implicit: row }\"></ng-container>\n } @else {\n {{ getFormattedCellValue(row, column, i) }}\n }\n </td>\n }\n @if (hasActions()) {\n <td\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"lc-table__actions\">\n @for (action of actions(); track action.key) {\n @if (!isActionHidden(action, row, i)) {\n <lc-button\n [variant]=\"action.variant ?? 'ghost'\"\n size=\"xs\"\n [iconOnly]=\"!action.label\"\n [disabled]=\"isActionDisabled(action, row, i)\"\n [ariaLabel]=\"action.tooltip ?? action.label ?? action.key\"\n (clicked)=\"onActionClick(action, row, i)\"\n >\n @if (action.icon) {\n <lc-icon [name]=\"action.icon\" size=\"sm\" [decorative]=\"true\" />\n }\n @if (action.label) {\n {{ action.label }}\n }\n </lc-button>\n }\n }\n </div>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n<!-- Pagination -->\n@if (paginate()) {\n <div class=\"lc-table-pagination\">\n <div class=\"lc-table-pagination__info\">\n {{ paginationStart }}\u2013{{ paginationEnd }} of {{ totalRows() }}\n </div>\n <div class=\"lc-table-pagination__controls\">\n <lc-select\n class=\"lc-table-pagination__size\"\n [size]=\"size() === 'sm' ? 'sm' : size() === 'lg' ? 'lg' : 'md'\"\n [options]=\"pageSizeSelectOptions()\"\n [ngModel]=\"internalPageSize()\"\n [ariaLabel]=\"'Rows per page'\"\n (ngModelChange)=\"onPageSizeModelChange($event)\"\n />\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() === 0\"\n (click)=\"goToPage(currentPage() - 1)\"\n aria-label=\"Previous page\"\n >\u2039</button>\n <span class=\"lc-table-pagination__page\">\n {{ currentPage() + 1 }} / {{ totalPages() }}\n </span>\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() >= totalPages() - 1\"\n (click)=\"goToPage(currentPage() + 1)\"\n aria-label=\"Next page\"\n >\u203A</button>\n </div>\n </div>\n}\n", styles: [".lc-table-wrapper{width:100%}.lc-table-wrapper--responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:768px){.lc-table-wrapper--responsive::-webkit-scrollbar{height:8px}.lc-table-wrapper--responsive::-webkit-scrollbar-thumb{background-color:var(--color-border);border-radius:var(--border-radius-sm)}}.lc-table{width:100%;border-collapse:collapse;border-spacing:0;background-color:var(--color-surface);color:var(--color-text-primary);font-family:var(--typography-font-family)}.lc-table thead{background-color:var(--color-surface-2);border-bottom:2px solid var(--color-border-strong)}.lc-table thead tr th{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));text-align:left;font-weight:var(--typography-font-weight-semibold);font-size:var(--typography-font-size-sm);text-transform:uppercase;letter-spacing:.04em;color:var(--color-text-secondary);white-space:nowrap;-webkit-user-select:none;user-select:none}.lc-table thead tr th.sortable{cursor:pointer;position:relative;padding-right:var(--spacing-8);transition:background-color .2s ease}.lc-table thead tr th.sortable:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table thead tr th.sortable .sort-indicator{position:absolute;right:var(--spacing-3);top:50%;transform:translateY(-50%);font-size:var(--typography-font-size-xs);color:var(--color-primary)}.lc-table thead tr th.sorted-asc,.lc-table thead tr th.sorted-desc{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table tbody tr{border-bottom:1px solid var(--color-divider)}.lc-table tbody tr:last-child{border-bottom:none}.lc-table tbody tr td{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));font-size:var(--typography-font-size-sm);color:var(--color-text-primary)}.lc-table__empty{text-align:center;padding:var(--spacing-8) var(--spacing-4)!important;color:var(--color-text-secondary);font-style:italic}.lc-table--striped tbody tr:nth-child(2n){background-color:var(--color-surface-2)}.lc-table--bordered{border:1px solid var(--color-border)}.lc-table--bordered thead tr th,.lc-table--bordered tbody tr td{border-right:1px solid var(--color-divider)}.lc-table--bordered thead tr th:last-child,.lc-table--bordered tbody tr td:last-child{border-right:none}.lc-table--bordered tbody tr{border-bottom:1px solid var(--color-border)}.lc-table--hoverable tbody tr{transition:background-color .15s ease;cursor:pointer}.lc-table--hoverable tbody tr:hover{background-color:var(--color-surface-hover)}.lc-table--sm thead tr th,.lc-table--sm tbody tr td{padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--typography-font-size-xs)}.lc-table--lg thead tr th,.lc-table--lg tbody tr td{padding:var(--lc-density-padding-md, var(--spacing-4)) var(--spacing-5);font-size:var(--typography-font-size-base)}.lc-table__filter-row th{padding:var(--spacing-2) var(--spacing-4)!important;background-color:var(--color-background)!important;border-bottom:1px solid var(--color-divider)!important}.lc-table__filter-input{width:100%;display:block}.lc-table__filter-input .input-field{font-size:var(--typography-font-size-xs)}.lc-table__select-cell{width:40px;text-align:center!important;padding:var(--spacing-2)!important}.lc-table__checkbox{width:16px;height:16px;cursor:pointer;accent-color:var(--color-primary)}.lc-table__row--selected{background-color:var(--color-surface-selected)!important}.lc-table__actions-cell{white-space:nowrap}.lc-table th.lc-table__actions-cell--start,.lc-table td.lc-table__actions-cell--start{text-align:left}.lc-table th.lc-table__actions-cell--center,.lc-table td.lc-table__actions-cell--center{text-align:center}.lc-table th.lc-table__actions-cell--end,.lc-table td.lc-table__actions-cell--end{text-align:right}.lc-table__actions{display:flex;align-items:center;gap:var(--spacing-2);justify-content:flex-start}.lc-table__actions-cell--center .lc-table__actions{justify-content:center}.lc-table__actions-cell--end .lc-table__actions{justify-content:flex-end}.lc-table__edit-input{width:100%;padding:var(--spacing-1) var(--spacing-2);border:2px solid var(--color-primary);border-radius:var(--border-radius-sm);font-size:inherit;background-color:var(--color-surface-2);color:var(--color-text-primary);outline:none}.lc-table-pagination{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-3) var(--spacing-1);font-size:var(--typography-font-size-sm);color:var(--color-text-secondary)}.lc-table-pagination__info{white-space:nowrap}.lc-table-pagination__controls{display:flex;align-items:center;gap:var(--spacing-2)}.lc-table-pagination__size{display:block;min-width:110px}.lc-table-pagination__btn{appearance:none;border:1px solid var(--color-border);background-color:var(--color-surface);color:var(--color-text-primary);width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:var(--border-radius-sm);font-size:var(--typography-font-size-base);cursor:pointer;transition:background-color .15s ease}.lc-table-pagination__btn:hover:not(:disabled){background-color:var(--color-surface-hover)}.lc-table-pagination__btn:disabled{opacity:.4;cursor:not-allowed}.lc-table-pagination__page{white-space:nowrap;min-width:60px;text-align:center}@media(max-width:640px){.lc-table-wrapper--stack .lc-table thead{display:none}.lc-table-wrapper--stack .lc-table tbody tr{display:block;margin-bottom:var(--spacing-4);border:1px solid var(--color-border);border-radius:var(--border-radius-md)}.lc-table-wrapper--stack .lc-table tbody tr td{display:block;text-align:right;padding:var(--spacing-2) var(--spacing-4);border-bottom:1px solid var(--color-divider)}.lc-table-wrapper--stack .lc-table tbody tr td:last-child{border-bottom:none}.lc-table-wrapper--stack .lc-table tbody tr td:before{content:attr(data-label);float:left;font-weight:var(--typography-font-weight-semibold);color:var(--color-text-secondary)}}\n"] }]
|
|
7766
|
+
args: [{ selector: 'lc-table', standalone: true, imports: [NgTemplateOutlet, NgStyle, FormsModule, SelectComponent, InputComponent, ButtonComponent, IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"wrapperClasses()\">\n <table [class]=\"tableClasses()\" [attr.role]=\"treeMode() ? 'treegrid' : 'table'\">\n <thead>\n <!-- Filter row -->\n @if (filterable()) {\n <tr class=\"lc-table__filter-row\">\n @if (selectable()) {\n <th class=\"lc-table__select-cell\"></th>\n }\n @for (column of columns(); track column.key) {\n <th>\n @if (column.filterable !== false) {\n <lc-input\n class=\"lc-table__filter-input\"\n size=\"xs\"\n [placeholder]=\"'Filter ' + column.label\"\n [ariaLabel]=\"'Filter by ' + column.label\"\n [ngModel]=\"getFilterValue(column.key)\"\n (ngModelChange)=\"onFilterChange(column.key, $event)\"\n />\n }\n </th>\n }\n @if (hasActions()) {\n <th class=\"lc-table__actions-cell\"></th>\n }\n </tr>\n }\n <!-- Header row -->\n <tr>\n @if (selectable()) {\n <th class=\"lc-table__select-cell\" scope=\"col\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"allSelected()\"\n (change)=\"toggleSelectAll()\"\n aria-label=\"Select all rows\"\n />\n </th>\n }\n @for (column of columns(); track column.key) {\n <th\n scope=\"col\"\n [class]=\"getHeaderClasses(column) + (column.cssClass ? ' ' + column.cssClass : '')\"\n [attr.aria-sort]=\"getAriaSort(column.key)\"\n [style.width]=\"column.width\"\n [title]=\"column.tooltip || ''\"\n (click)=\"handleSort(column.key)\">\n {{ column.label }}\n @if (column.sortable && getSortState(column.key)) {\n <span class=\"sort-indicator\" [attr.aria-label]=\"getSortState(column.key) === 'asc' ? 'sorted ascending' : 'sorted descending'\">\n @if (getSortState(column.key) === 'asc') {\n \u2191\n } @else {\n \u2193\n }\n </span>\n }\n </th>\n }\n @if (hasActions()) {\n <th\n scope=\"col\"\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [style.width]=\"actionsWidth()\"\n >\n {{ actionsLabel() }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @if (displayRows().length === 0) {\n <tr>\n <td [attr.colspan]=\"columns().length + (selectable() ? 1 : 0) + (hasActions() ? 1 : 0)\" class=\"lc-table__empty\">\n {{ emptyText() }}\n </td>\n </tr>\n } @else {\n @for (dr of displayRows(); track dr.absIndex) {\n <tr\n [class.lc-table__row--selected]=\"selectable() && isRowSelected(dr.absIndex)\"\n [class.lc-table__row--group]=\"dr.hasChildren\"\n [attr.role]=\"treeMode() ? 'row' : null\"\n [attr.aria-level]=\"treeMode() ? dr.level : null\"\n [attr.aria-expanded]=\"treeMode() && dr.hasChildren ? dr.expanded : null\"\n [attr.aria-posinset]=\"treeMode() ? dr.posInSet : null\"\n [attr.aria-setsize]=\"treeMode() ? dr.setSize : null\"\n (click)=\"onRowClick(dr.row)\"\n >\n @if (selectable()) {\n <td class=\"lc-table__select-cell\" [attr.role]=\"treeMode() ? 'gridcell' : null\">\n <input\n type=\"checkbox\"\n class=\"lc-table__checkbox\"\n [checked]=\"isRowSelected(dr.absIndex)\"\n (change)=\"toggleRowSelect(dr.absIndex)\"\n (click)=\"$event.stopPropagation()\"\n [attr.aria-label]=\"'Select row ' + (dr.absIndex + 1)\"\n />\n </td>\n }\n @for (column of columns(); track column.key) {\n <td\n [class]=\"getCellClasses(dr.row, column, dr.absIndex)\"\n [ngStyle]=\"getCellStyles(dr.row, column, dr.absIndex)\"\n [attr.role]=\"treeMode() ? 'gridcell' : null\"\n (dblclick)=\"editable() ? startEdit(dr.absIndex, column.key, getCellValue(dr.row, column.key)) : null\"\n >\n @if (isTreeColumn(column.key)) {\n <span class=\"lc-table__tree-cell\">\n @for (lvl of indentLevels(dr.depth); track lvl) {\n <span class=\"lc-table__tree-indent\" [style.width.px]=\"indentSize()\"></span>\n }\n <span class=\"lc-table__tree-chevron\" [style.width.px]=\"indentSize()\">\n @if (dr.hasChildren) {\n <button\n type=\"button\"\n class=\"lc-table__chevron-btn\"\n [class.lc-table__chevron-btn--expanded]=\"dr.expanded\"\n [attr.aria-label]=\"(dr.expanded ? 'Einklappen: ' : 'Aufklappen: ') + treeCellLabel(dr.row)\"\n [attr.aria-expanded]=\"dr.expanded\"\n (click)=\"onChevronClick(dr, $event)\"\n >\n <lc-icon name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </span>\n <span class=\"lc-table__tree-content\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { row: dr.row, column: column, absIndex: dr.absIndex }\"\n ></ng-container>\n </span>\n </span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { row: dr.row, column: column, absIndex: dr.absIndex }\"\n ></ng-container>\n }\n </td>\n }\n @if (hasActions()) {\n <td\n class=\"lc-table__actions-cell\"\n [class]=\"'lc-table__actions-cell--' + actionsAlign()\"\n [attr.role]=\"treeMode() ? 'gridcell' : null\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"lc-table__actions\">\n @for (action of actions(); track action.key) {\n @if (!isActionHidden(action, dr.row, dr.absIndex)) {\n <lc-button\n [variant]=\"action.variant ?? 'ghost'\"\n size=\"xs\"\n [iconOnly]=\"!action.label\"\n [disabled]=\"isActionDisabled(action, dr.row, dr.absIndex)\"\n [ariaLabel]=\"action.tooltip ?? action.label ?? action.key\"\n (clicked)=\"onActionClick(action, dr.row, dr.absIndex)\"\n >\n @if (action.icon) {\n <lc-icon [name]=\"action.icon\" size=\"sm\" [decorative]=\"true\" />\n }\n @if (action.label) {\n {{ action.label }}\n }\n </lc-button>\n }\n }\n </div>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n<!-- Shared cell content (edit input / custom template / formatted value) -->\n<ng-template #cellContent let-row=\"row\" let-column=\"column\" let-absIndex=\"absIndex\">\n @if (isEditing(absIndex, column.key)) {\n <input\n type=\"text\"\n class=\"lc-table__edit-input\"\n [value]=\"editValue()\"\n (input)=\"editValue.set(getInputValue($event))\"\n (blur)=\"commitEdit(absIndex, column.key)\"\n (keydown)=\"onEditKeydown($event, absIndex, column.key)\"\n autofocus\n />\n } @else if (hasCustomTemplate(column.key)) {\n <ng-container *ngTemplateOutlet=\"getCellTemplate(column.key)!.template; context: { $implicit: row }\"></ng-container>\n } @else {\n {{ getFormattedCellValue(row, column, absIndex) }}\n }\n</ng-template>\n\n<!-- Pagination -->\n@if (paginate()) {\n <div class=\"lc-table-pagination\">\n <div class=\"lc-table-pagination__info\">\n {{ paginationStart }}\u2013{{ paginationEnd }} of {{ totalRows() }}\n </div>\n <div class=\"lc-table-pagination__controls\">\n <lc-select\n class=\"lc-table-pagination__size\"\n [size]=\"size() === 'sm' ? 'sm' : size() === 'lg' ? 'lg' : 'md'\"\n [options]=\"pageSizeSelectOptions()\"\n [ngModel]=\"internalPageSize()\"\n [ariaLabel]=\"'Rows per page'\"\n (ngModelChange)=\"onPageSizeModelChange($event)\"\n />\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() === 0\"\n (click)=\"goToPage(currentPage() - 1)\"\n aria-label=\"Previous page\"\n >\u2039</button>\n <span class=\"lc-table-pagination__page\">\n {{ currentPage() + 1 }} / {{ totalPages() }}\n </span>\n <button\n type=\"button\"\n class=\"lc-table-pagination__btn\"\n [disabled]=\"currentPage() >= totalPages() - 1\"\n (click)=\"goToPage(currentPage() + 1)\"\n aria-label=\"Next page\"\n >\u203A</button>\n </div>\n </div>\n}\n", styles: [".lc-table-wrapper{width:100%}.lc-table-wrapper--responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:768px){.lc-table-wrapper--responsive::-webkit-scrollbar{height:8px}.lc-table-wrapper--responsive::-webkit-scrollbar-thumb{background-color:var(--color-border);border-radius:var(--border-radius-sm)}}.lc-table{width:100%;border-collapse:collapse;border-spacing:0;background-color:var(--color-surface);color:var(--color-text-primary);font-family:var(--typography-font-family)}.lc-table thead{background-color:var(--color-surface-2);border-bottom:2px solid var(--color-border-strong)}.lc-table thead tr th{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));text-align:left;font-weight:var(--typography-font-weight-semibold);font-size:var(--typography-font-size-sm);text-transform:uppercase;letter-spacing:.04em;color:var(--color-text-secondary);white-space:nowrap;-webkit-user-select:none;user-select:none}.lc-table thead tr th.sortable{cursor:pointer;position:relative;padding-right:var(--spacing-8);transition:background-color .2s ease}.lc-table thead tr th.sortable:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table thead tr th.sortable .sort-indicator{position:absolute;right:var(--spacing-3);top:50%;transform:translateY(-50%);font-size:var(--typography-font-size-xs);color:var(--color-primary)}.lc-table thead tr th.sorted-asc,.lc-table thead tr th.sorted-desc{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table tbody tr{border-bottom:1px solid var(--color-divider)}.lc-table tbody tr:last-child{border-bottom:none}.lc-table tbody tr td{padding:var(--lc-density-padding-sm, var(--spacing-3)) var(--lc-density-padding-md, var(--spacing-4));font-size:var(--typography-font-size-sm);color:var(--color-text-primary)}.lc-table__empty{text-align:center;padding:var(--spacing-8) var(--spacing-4)!important;color:var(--color-text-secondary);font-style:italic}.lc-table--striped tbody tr:nth-child(2n){background-color:var(--color-surface-2)}.lc-table--bordered{border:1px solid var(--color-border)}.lc-table--bordered thead tr th,.lc-table--bordered tbody tr td{border-right:1px solid var(--color-divider)}.lc-table--bordered thead tr th:last-child,.lc-table--bordered tbody tr td:last-child{border-right:none}.lc-table--bordered tbody tr{border-bottom:1px solid var(--color-border)}.lc-table--hoverable tbody tr{transition:background-color .15s ease;cursor:pointer}.lc-table--hoverable tbody tr:hover{background-color:var(--color-surface-hover)}.lc-table--sm thead tr th,.lc-table--sm tbody tr td{padding:var(--lc-density-padding-xs, var(--spacing-2)) var(--lc-density-padding-sm, var(--spacing-3));font-size:var(--typography-font-size-xs)}.lc-table--lg thead tr th,.lc-table--lg tbody tr td{padding:var(--lc-density-padding-md, var(--spacing-4)) var(--spacing-5);font-size:var(--typography-font-size-base)}.lc-table__filter-row th{padding:var(--spacing-2) var(--spacing-4)!important;background-color:var(--color-background)!important;border-bottom:1px solid var(--color-divider)!important}.lc-table__filter-input{width:100%;display:block}.lc-table__filter-input .input-field{font-size:var(--typography-font-size-xs)}.lc-table__select-cell{width:40px;text-align:center!important;padding:var(--spacing-2)!important}.lc-table__checkbox{width:16px;height:16px;cursor:pointer;accent-color:var(--color-primary)}.lc-table__row--selected{background-color:var(--color-surface-selected)!important}.lc-table__actions-cell{white-space:nowrap}.lc-table th.lc-table__actions-cell--start,.lc-table td.lc-table__actions-cell--start{text-align:left}.lc-table th.lc-table__actions-cell--center,.lc-table td.lc-table__actions-cell--center{text-align:center}.lc-table th.lc-table__actions-cell--end,.lc-table td.lc-table__actions-cell--end{text-align:right}.lc-table__actions{display:flex;align-items:center;gap:var(--spacing-2);justify-content:flex-start}.lc-table__actions-cell--center .lc-table__actions{justify-content:center}.lc-table__actions-cell--end .lc-table__actions{justify-content:flex-end}.lc-table__tree-cell{display:flex;align-items:center;min-width:0}.lc-table__tree-indent{flex:0 0 auto;align-self:stretch;position:relative}.lc-table__tree-indent:before{content:\"\";position:absolute;left:50%;top:0;bottom:0;width:1px;background-color:var(--color-border)}.lc-table__tree-chevron{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center}.lc-table__chevron-btn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;background:transparent;color:var(--color-text-secondary);cursor:pointer;border-radius:var(--border-radius-sm);transition:transform .2s ease,background-color .15s ease,color .15s ease}.lc-table__chevron-btn:hover{background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-table__chevron-btn:focus-visible{outline:2px solid var(--color-primary);outline-offset:1px}.lc-table__chevron-btn--expanded{transform:rotate(90deg)}.lc-table__tree-content{flex:1 1 auto;min-width:0}.lc-table__row--group .lc-table__tree-content{font-weight:var(--typography-font-weight-semibold)}.lc-table__edit-input{width:100%;padding:var(--spacing-1) var(--spacing-2);border:2px solid var(--color-primary);border-radius:var(--border-radius-sm);font-size:inherit;background-color:var(--color-surface-2);color:var(--color-text-primary);outline:none}.lc-table-pagination{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-3) var(--spacing-1);font-size:var(--typography-font-size-sm);color:var(--color-text-secondary)}.lc-table-pagination__info{white-space:nowrap}.lc-table-pagination__controls{display:flex;align-items:center;gap:var(--spacing-2)}.lc-table-pagination__size{display:block;min-width:110px}.lc-table-pagination__btn{appearance:none;border:1px solid var(--color-border);background-color:var(--color-surface);color:var(--color-text-primary);width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:var(--border-radius-sm);font-size:var(--typography-font-size-base);cursor:pointer;transition:background-color .15s ease}.lc-table-pagination__btn:hover:not(:disabled){background-color:var(--color-surface-hover)}.lc-table-pagination__btn:disabled{opacity:.4;cursor:not-allowed}.lc-table-pagination__page{white-space:nowrap;min-width:60px;text-align:center}@media(max-width:640px){.lc-table-wrapper--stack .lc-table thead{display:none}.lc-table-wrapper--stack .lc-table tbody tr{display:block;margin-bottom:var(--spacing-4);border:1px solid var(--color-border);border-radius:var(--border-radius-md)}.lc-table-wrapper--stack .lc-table tbody tr td{display:block;text-align:right;padding:var(--spacing-2) var(--spacing-4);border-bottom:1px solid var(--color-divider)}.lc-table-wrapper--stack .lc-table tbody tr td:last-child{border-bottom:none}.lc-table-wrapper--stack .lc-table tbody tr td:before{content:attr(data-label);float:left;font-weight:var(--typography-font-weight-semibold);color:var(--color-text-secondary)}}\n"] }]
|
|
7519
7767
|
}], propDecorators: { cellTemplates: [{
|
|
7520
7768
|
type: ContentChildren,
|
|
7521
7769
|
args: [TableCellDirective]
|
|
7522
|
-
}], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], hoverable: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverable", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], paginate: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginate", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], actionsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionsLabel", required: false }] }], actionsWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionsWidth", required: false }] }], actionsAlign: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionsAlign", required: false }] }], sort: [{ type: i0.Output, args: ["sort"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], cellEdit: [{ type: i0.Output, args: ["cellEdit"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], actionClick: [{ type: i0.Output, args: ["actionClick"] }] } });
|
|
7770
|
+
}], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], hoverable: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverable", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], paginate: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginate", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], actionsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionsLabel", required: false }] }], actionsWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionsWidth", required: false }] }], actionsAlign: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionsAlign", required: false }] }], idKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "idKey", required: false }] }], parentKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentKey", required: false }] }], treeColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "treeColumn", required: false }] }], defaultExpanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultExpanded", required: false }] }], expandedIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedIds", required: false }] }], indentSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "indentSize", required: false }] }], sort: [{ type: i0.Output, args: ["sort"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], cellEdit: [{ type: i0.Output, args: ["cellEdit"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], actionClick: [{ type: i0.Output, args: ["actionClick"] }], rowToggle: [{ type: i0.Output, args: ["rowToggle"] }], expandedIdsChange: [{ type: i0.Output, args: ["expandedIdsChange"] }] } });
|
|
7523
7771
|
|
|
7524
7772
|
/**
|
|
7525
7773
|
* Field group component for displaying label-value pairs.
|
|
@@ -11484,6 +11732,63 @@ class ChatComponent {
|
|
|
11484
11732
|
return true;
|
|
11485
11733
|
return role === mode;
|
|
11486
11734
|
}
|
|
11735
|
+
/** Whether a message carries a semantic (non-`default`) status. */
|
|
11736
|
+
isSemanticStatus(status) {
|
|
11737
|
+
return !!status && status !== 'default';
|
|
11738
|
+
}
|
|
11739
|
+
/** Rail icon for a semantic status — reuses the `lc-alert` icon names. */
|
|
11740
|
+
statusIcon(status) {
|
|
11741
|
+
switch (status) {
|
|
11742
|
+
case 'info':
|
|
11743
|
+
return 'information-circle';
|
|
11744
|
+
case 'success':
|
|
11745
|
+
return 'check-circle';
|
|
11746
|
+
case 'warning':
|
|
11747
|
+
return 'exclamation-triangle';
|
|
11748
|
+
case 'error':
|
|
11749
|
+
return 'x-circle';
|
|
11750
|
+
default:
|
|
11751
|
+
return '';
|
|
11752
|
+
}
|
|
11753
|
+
}
|
|
11754
|
+
/** Semantic token colour for a status dot/icon. */
|
|
11755
|
+
statusColor(status) {
|
|
11756
|
+
switch (status) {
|
|
11757
|
+
case 'info':
|
|
11758
|
+
return 'var(--color-info-500)';
|
|
11759
|
+
case 'success':
|
|
11760
|
+
return 'var(--color-success-500)';
|
|
11761
|
+
case 'warning':
|
|
11762
|
+
return 'var(--color-warning-500)';
|
|
11763
|
+
case 'error':
|
|
11764
|
+
return 'var(--color-error-500)';
|
|
11765
|
+
default:
|
|
11766
|
+
return 'currentColor';
|
|
11767
|
+
}
|
|
11768
|
+
}
|
|
11769
|
+
/** Visually-hidden prefix so colour/icon is never the only signal. */
|
|
11770
|
+
statusLabel(status) {
|
|
11771
|
+
switch (status) {
|
|
11772
|
+
case 'info':
|
|
11773
|
+
return 'Info:';
|
|
11774
|
+
case 'success':
|
|
11775
|
+
return 'Erfolg:';
|
|
11776
|
+
case 'warning':
|
|
11777
|
+
return 'Warnung:';
|
|
11778
|
+
case 'error':
|
|
11779
|
+
return 'Fehler:';
|
|
11780
|
+
default:
|
|
11781
|
+
return '';
|
|
11782
|
+
}
|
|
11783
|
+
}
|
|
11784
|
+
/** ARIA live priority: assertive for `error`, polite for other statuses. */
|
|
11785
|
+
statusAriaLive(status) {
|
|
11786
|
+
if (status === 'error')
|
|
11787
|
+
return 'assertive';
|
|
11788
|
+
if (this.isSemanticStatus(status))
|
|
11789
|
+
return 'polite';
|
|
11790
|
+
return null;
|
|
11791
|
+
}
|
|
11487
11792
|
scrollToBottom() {
|
|
11488
11793
|
if (this.scrollContainer) {
|
|
11489
11794
|
const el = this.scrollContainer.nativeElement;
|
|
@@ -11491,11 +11796,11 @@ class ChatComponent {
|
|
|
11491
11796
|
}
|
|
11492
11797
|
}
|
|
11493
11798
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11494
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: ChatComponent, isStandalone: true, selector: "lc-chat", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, isStreaming: { classPropertyName: "isStreaming", publicName: "isStreaming", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, messageAnchor: { classPropertyName: "messageAnchor", publicName: "messageAnchor", isSignal: true, isRequired: false, transformFunction: null }, contentWidth: { classPropertyName: "contentWidth", publicName: "contentWidth", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showAvatars: { classPropertyName: "showAvatars", publicName: "showAvatars", isSignal: true, isRequired: false, transformFunction: null }, showTimestamps: { classPropertyName: "showTimestamps", publicName: "showTimestamps", isSignal: true, isRequired: false, transformFunction: null }, renderMarkdown: { classPropertyName: "renderMarkdown", publicName: "renderMarkdown", isSignal: true, isRequired: false, transformFunction: null }, allowFileUpload: { classPropertyName: "allowFileUpload", publicName: "allowFileUpload", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxFileSize: { classPropertyName: "maxFileSize", publicName: "maxFileSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { messageSend: "messageSend", fileAttach: "fileAttach" }, queries: [{ propertyName: "messageTemplate", first: true, predicate: ["messageTemplate"], descendants: true }], viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true }, { propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }, { propertyName: "composerInput", first: true, predicate: ["composerInput"], descendants: true }], ngImport: i0, template: "<div\n class=\"lc-chat\"\n [class.lc-chat--borderless]=\"!bordered()\"\n [class.lc-chat--width-narrow]=\"contentWidth() === 'narrow'\"\n [class.lc-chat--width-full]=\"contentWidth() === 'full'\"\n>\n @if (showHeader()) {\n <div class=\"lc-chat__header\">\n <span class=\"lc-chat__title\">{{ title() }}</span>\n @if (isStreaming()) {\n <span class=\"lc-chat__streaming-badge\">Streaming\u2026</span>\n }\n </div>\n }\n\n <div\n class=\"lc-chat__messages\"\n [class.lc-chat__messages--anchor-bottom]=\"messageAnchor() === 'bottom'\"\n #scrollContainer\n >\n <div class=\"lc-chat__thread\">\n @for (msg of formattedMessages(); track msg.id) {\n <div\n class=\"lc-chat__message\"\n [class.lc-chat__message--user]=\"msg.role === 'user'\"\n [class.lc-chat__message--agent]=\"msg.role === 'agent'\"\n [class.lc-chat__message--system]=\"msg.role === 'system'\"\n [class.lc-chat__message--streaming]=\"msg.streaming\"\n >\n @if (showAvatars() && msg.role !== 'user') {\n <div class=\"lc-chat__marker\">\n @if (msg.avatar) {\n <img [src]=\"msg.avatar\" [alt]=\"msg.name || msg.role\" class=\"lc-chat__avatar-img\" />\n } @else {\n <span\n class=\"lc-chat__dot\"\n [class.lc-chat__dot--agent]=\"msg.role === 'agent'\"\n [class.lc-chat__dot--system]=\"msg.role === 'system'\"\n ></span>\n }\n </div>\n }\n\n <div class=\"lc-chat__bubble\" [class.lc-chat__bubble--streaming]=\"msg.streaming\">\n @if (msg.role === 'user') {\n <div class=\"lc-chat__meta\">\n <span class=\"lc-chat__name\">{{ msg.name || 'Du' }}</span>\n @if (showTimestamps() && msg.timestamp) {\n <span class=\"lc-chat__time\">{{ formatTime(msg.timestamp) }}</span>\n }\n </div>\n } @else if (msg.name) {\n <div class=\"lc-chat__name\">{{ msg.name }}</div>\n }\n <div class=\"lc-chat__content\">\n @if (messageTemplate) {\n <ng-container *ngTemplateOutlet=\"messageTemplate; context: { $implicit: msg }\" />\n } @else if (shouldRenderMarkdown(msg.role)) {\n <lc-markdown [content]=\"msg.content\" variant=\"chat\" />\n } @else {\n {{ msg.content }}\n }\n <span class=\"lc-chat__cursor\" [class.lc-chat__cursor--visible]=\"msg.streaming\"></span>\n </div>\n @if (msg.attachments?.length) {\n <div class=\"lc-chat__attachments\">\n @for (att of msg.attachments; track att.id) {\n @if (isImage(att) && att.url) {\n <a class=\"lc-chat__attachment lc-chat__attachment--image\" [href]=\"att.url\" target=\"_blank\" rel=\"noopener\">\n <img [src]=\"att.url\" [alt]=\"att.name\" />\n </a>\n } @else {\n <a\n class=\"lc-chat__attachment lc-chat__attachment--file\"\n [href]=\"att.url || null\"\n [attr.download]=\"att.url ? att.name : null\"\n target=\"_blank\"\n rel=\"noopener\"\n >\n <svg class=\"lc-chat__attachment-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M6 2.5h6L16 6.5v10A1.5 1.5 0 0 1 14.5 18h-9A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3H6V2.5Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M12 2.5V6.5H16\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n <span class=\"lc-chat__attachment-name\">{{ att.name }}</span>\n @if (att.size) {\n <span class=\"lc-chat__attachment-size\">{{ formatBytes(att.size) }}</span>\n }\n </a>\n }\n }\n </div>\n }\n @if (showTimestamps() && msg.timestamp && msg.role !== 'user') {\n <div class=\"lc-chat__time\">{{ formatTime(msg.timestamp) }}</div>\n }\n </div>\n </div>\n }\n\n @if (isStreaming() && !(formattedMessages().length && formattedMessages()[formattedMessages().length - 1].streaming)) {\n <div class=\"lc-chat__message lc-chat__message--agent\">\n @if (showAvatars()) {\n <div class=\"lc-chat__marker\">\n <span class=\"lc-chat__dot lc-chat__dot--agent\"></span>\n </div>\n }\n <div class=\"lc-chat__bubble\">\n <div class=\"lc-chat__typing\">\n <span></span><span></span><span></span>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <div class=\"lc-chat__input-area\">\n <div class=\"lc-chat__composer\">\n @if (allowFileUpload() && pendingAttachments().length) {\n <div class=\"lc-chat__pending-attachments\">\n @for (att of pendingAttachments(); track att.id) {\n <div class=\"lc-chat__pending-attachment\" [class.lc-chat__pending-attachment--image]=\"isImage(att)\">\n @if (isImage(att) && att.url) {\n <img class=\"lc-chat__pending-thumb\" [src]=\"att.url\" [alt]=\"att.name\" />\n } @else {\n <svg class=\"lc-chat__attachment-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M6 2.5h6L16 6.5v10A1.5 1.5 0 0 1 14.5 18h-9A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3H6V2.5Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M12 2.5V6.5H16\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n }\n <span class=\"lc-chat__pending-name\">{{ att.name }}</span>\n @if (att.size) {\n <span class=\"lc-chat__pending-size\">{{ formatBytes(att.size) }}</span>\n }\n <button\n type=\"button\"\n class=\"lc-chat__pending-remove\"\n (click)=\"removeAttachment(att.id)\"\n [attr.aria-label]=\"'Entfernen: ' + att.name\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M3 3l6 6M9 3l-6 6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </button>\n </div>\n }\n </div>\n }\n\n <textarea\n #composerInput\n class=\"lc-chat__input\"\n [placeholder]=\"placeholder()\"\n [value]=\"inputValue()\"\n [disabled]=\"disabled()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n rows=\"1\"\n ></textarea>\n\n <div class=\"lc-chat__composer-actions\">\n @if (allowFileUpload()) {\n <input\n #fileInput\n type=\"file\"\n class=\"lc-chat__file-input\"\n [accept]=\"accept() || null\"\n [multiple]=\"multiple()\"\n (change)=\"onFilesSelected($event)\"\n />\n <button\n type=\"button\"\n class=\"lc-chat__attach-btn\"\n [disabled]=\"disabled()\"\n (click)=\"openFilePicker()\"\n aria-label=\"Datei anh\u00E4ngen\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M14.5 9.5l-5 5a3 3 0 0 1-4.243-4.243l6.364-6.364a2 2 0 0 1 2.829 2.829l-6.364 6.364a1 1 0 1 1-1.415-1.415L11.5 6.5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n }\n <button\n class=\"lc-chat__send-btn\"\n [disabled]=\"(!inputValue().trim() && !pendingAttachments().length) || disabled()\"\n (click)=\"send()\"\n aria-label=\"Send\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M3 10l7-7m0 0l7 7m-7-7v14\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" transform=\"rotate(90 10 10)\"/>\n </svg>\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: [".lc-chat{--lc-chat-content-width: 46rem;display:flex;flex-direction:column;height:100%;min-height:20rem;border:1px solid var(--color-border);border-radius:var(--border-radius-lg);overflow:hidden;background-color:var(--color-surface);color:var(--color-text-primary);font-family:var(--font-family-sans, sans-serif)}.lc-chat--borderless{border:none;border-radius:0}.lc-chat--width-full{--lc-chat-content-width: 100%}.lc-chat__header{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-3) var(--spacing-4);border-bottom:1px solid var(--color-divider);background:var(--color-surface)}.lc-chat__title{font-size:var(--font-size-sm, .875rem);font-weight:var(--font-weight-semibold, 600);color:var(--color-text-primary)}.lc-chat__streaming-badge{font-size:var(--font-size-xs, .6875rem);padding:.125rem .5rem;background:color-mix(in srgb,var(--color-primary-500) 15%,transparent);color:var(--color-primary-500);border-radius:var(--border-radius-full, 1rem);font-weight:var(--font-weight-medium, 500);animation:pulse-badge 1.5s infinite}@keyframes pulse-badge{0%,to{opacity:1}50%{opacity:.6}}.lc-chat__messages{flex:1;overflow-y:auto;display:flex;flex-direction:column;padding-block:var(--spacing-6, 1.5rem);padding-inline:var(--spacing-4, 1rem)}.lc-chat__thread{position:relative;width:100%;max-width:var(--lc-chat-content-width, 46rem);margin-inline:auto;flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:var(--spacing-5, 1.25rem)}.lc-chat__messages--anchor-bottom .lc-chat__thread>.lc-chat__message:first-child{margin-top:auto}.lc-chat__message{position:relative;display:flex;align-items:stretch;gap:var(--spacing-3, .75rem);align-self:stretch;max-width:100%}.lc-chat__marker{position:relative;z-index:1;flex-shrink:0;width:.875rem;display:flex;flex-direction:column;align-items:center}.lc-chat__marker:before{content:\"\";position:absolute;left:50%;top:.55rem;bottom:calc(-1 * var(--spacing-5, 1.25rem));width:1.5px;transform:translate(-50%);background:var(--color-divider);z-index:-1}.lc-chat__message:last-child .lc-chat__marker:before{display:none}.lc-chat__dot{position:relative;z-index:1;width:.625rem;height:.625rem;margin-top:.25rem;border-radius:50%;background:var(--color-text-tertiary);box-shadow:0 0 0 3px var(--color-surface)}.lc-chat__dot--agent{background:var(--color-primary-500)}.lc-chat__dot--system{background:var(--color-text-tertiary);opacity:.6}.lc-chat__message--streaming .lc-chat__dot--agent{animation:dot-pulse 1.4s ease-out infinite}@keyframes dot-pulse{0%{box-shadow:0 0 0 3px var(--color-surface),0 0 0 3px color-mix(in srgb,var(--color-primary-500) 45%,transparent)}70%,to{box-shadow:0 0 0 3px var(--color-surface),0 0 0 9px color-mix(in srgb,var(--color-primary-500) 0%,transparent)}}.lc-chat__avatar-img{width:1.5rem;height:1.5rem;border-radius:50%;object-fit:cover;box-shadow:0 0 0 3px var(--color-surface)}.lc-chat__bubble{flex:1;min-width:0;font-size:var(--font-size-base, .9375rem);line-height:var(--line-height-relaxed, 1.6);word-break:break-word;color:var(--color-text-primary)}.lc-chat__message--user .lc-chat__bubble{position:relative;z-index:1;padding:var(--spacing-3, .75rem) var(--spacing-4, 1rem);border:1px solid var(--color-border);border-radius:var(--border-radius-lg);background:var(--color-surface-2)}.lc-chat__message--agent .lc-chat__bubble{padding:0;background:transparent}.lc-chat__message--system .lc-chat__bubble{padding:0;background:transparent;color:var(--color-text-tertiary);font-size:var(--font-size-sm, .8125rem)}.lc-chat__meta{display:flex;align-items:baseline;justify-content:space-between;gap:var(--spacing-2, .5rem);margin-bottom:var(--spacing-1, .25rem)}.lc-chat__meta .lc-chat__name{margin-bottom:0}.lc-chat__meta .lc-chat__time{margin-top:0}.lc-chat__name{font-size:var(--font-size-xs, .75rem);font-weight:var(--font-weight-semibold, 600);color:var(--color-text-secondary);margin-bottom:var(--spacing-1, .25rem)}.lc-chat__content{white-space:pre-wrap}.lc-chat__cursor{display:none}.lc-chat__cursor--visible{display:inline-block;width:2px;height:1em;background:currentColor;margin-left:1px;vertical-align:text-bottom;animation:blink-cursor .8s step-end infinite}@keyframes blink-cursor{0%,to{opacity:1}50%{opacity:0}}.lc-chat__time{font-size:var(--font-size-xs, .75rem);color:var(--color-text-tertiary);margin-top:var(--spacing-1, .25rem);flex-shrink:0}.lc-chat__typing{display:flex;gap:.25rem;padding:.25rem 0}.lc-chat__typing span{width:.375rem;height:.375rem;border-radius:50%;background:var(--color-text-tertiary);animation:typing-bounce 1.4s ease-in-out infinite}.lc-chat__typing span:nth-child(2){animation-delay:.2s}.lc-chat__typing span:nth-child(3){animation-delay:.4s}@keyframes typing-bounce{0%,60%,to{transform:translateY(0)}30%{transform:translateY(-.375rem)}}.lc-chat__input-area{padding:var(--spacing-4, .75rem);border-top:1px solid var(--color-divider);background:var(--color-surface)}.lc-chat__composer{width:100%;max-width:var(--lc-chat-content-width, 46rem);margin-inline:auto;display:flex;flex-direction:column;gap:var(--spacing-2, .5rem);padding:var(--spacing-3, .75rem);background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:var(--border-radius-xl);transition:border-color .15s,box-shadow .15s}.lc-chat__composer:focus-within{border-color:var(--color-primary-500);box-shadow:var(--elevation-1)}.lc-chat__input{display:block;width:100%;box-sizing:border-box;resize:none;border:none;background:transparent;padding:var(--spacing-1, .25rem) var(--spacing-2, .5rem);font-size:var(--font-size-base, .875rem);font-family:inherit;line-height:var(--line-height-normal, 1.4);color:var(--color-text-primary);outline:none;height:auto;max-height:12rem;overflow-y:auto}.lc-chat__input:disabled{opacity:.5;cursor:not-allowed}.lc-chat__input::placeholder{color:var(--color-text-tertiary)}.lc-chat__composer-actions{display:flex;align-items:center;gap:var(--spacing-2, .5rem)}.lc-chat__send-btn{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;margin-left:auto;border:none;border-radius:var(--border-radius-md);background:var(--color-primary-fill);color:var(--color-on-primary);cursor:pointer;flex-shrink:0;transition:background .15s,opacity .15s}.lc-chat__send-btn:hover:not(:disabled){background:var(--color-primary-fill-hover)}.lc-chat__send-btn:disabled{opacity:.4;cursor:not-allowed}.lc-chat__file-input{display:none}.lc-chat__attach-btn{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:none;border-radius:var(--border-radius-md);background:transparent;color:var(--color-text-tertiary);cursor:pointer;flex-shrink:0;transition:background .15s,color .15s}.lc-chat__attach-btn:hover:not(:disabled){background:var(--color-surface-hover);color:var(--color-text-primary)}.lc-chat__attach-btn:disabled{opacity:.4;cursor:not-allowed}.lc-chat__pending-attachments{display:flex;flex-wrap:wrap;gap:.375rem}.lc-chat__pending-attachment{display:inline-flex;align-items:center;gap:.375rem;padding:.25rem .375rem .25rem .5rem;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--border-radius-md, .5rem);font-size:var(--font-size-xs, .75rem);color:var(--color-text-primary);max-width:16rem}.lc-chat__pending-thumb{width:1.75rem;height:1.75rem;object-fit:cover;border-radius:.25rem;flex-shrink:0}.lc-chat__pending-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:9rem}.lc-chat__pending-size{color:var(--color-text-tertiary);font-variant-numeric:tabular-nums}.lc-chat__pending-remove{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;border:none;border-radius:50%;background:transparent;color:var(--color-text-tertiary);cursor:pointer;flex-shrink:0}.lc-chat__pending-remove:hover{background:var(--color-surface-hover);color:var(--color-text-primary)}.lc-chat__attachments{display:flex;flex-wrap:wrap;gap:.375rem;margin-top:.5rem}.lc-chat__attachment{display:inline-flex;align-items:center;gap:.375rem;text-decoration:none;color:inherit;font-size:var(--font-size-xs, .75rem)}.lc-chat__attachment--file{padding:.25rem .5rem;background:color-mix(in srgb,currentColor 8%,transparent);border-radius:var(--border-radius-md, .375rem);max-width:16rem}.lc-chat__attachment--file:hover{background:color-mix(in srgb,currentColor 16%,transparent)}.lc-chat__attachment--image img{max-width:12rem;max-height:8rem;border-radius:var(--border-radius-md, .375rem);object-fit:cover;display:block}.lc-chat__attachment-icon{flex-shrink:0}.lc-chat__attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-chat__attachment-size{opacity:.7;font-variant-numeric:tabular-nums}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MarkdownComponent, selector: "lc-markdown", inputs: ["src", "content", "variant", "linkTarget", "sanitize", "syntaxHighlight", "showHeadingAnchors", "baseUrl"], outputs: ["linkClick", "rendered"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
11799
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: ChatComponent, isStandalone: true, selector: "lc-chat", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, isStreaming: { classPropertyName: "isStreaming", publicName: "isStreaming", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, messageAnchor: { classPropertyName: "messageAnchor", publicName: "messageAnchor", isSignal: true, isRequired: false, transformFunction: null }, contentWidth: { classPropertyName: "contentWidth", publicName: "contentWidth", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showAvatars: { classPropertyName: "showAvatars", publicName: "showAvatars", isSignal: true, isRequired: false, transformFunction: null }, showTimestamps: { classPropertyName: "showTimestamps", publicName: "showTimestamps", isSignal: true, isRequired: false, transformFunction: null }, renderMarkdown: { classPropertyName: "renderMarkdown", publicName: "renderMarkdown", isSignal: true, isRequired: false, transformFunction: null }, allowFileUpload: { classPropertyName: "allowFileUpload", publicName: "allowFileUpload", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxFileSize: { classPropertyName: "maxFileSize", publicName: "maxFileSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { messageSend: "messageSend", fileAttach: "fileAttach" }, queries: [{ propertyName: "messageTemplate", first: true, predicate: ["messageTemplate"], descendants: true }], viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true }, { propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }, { propertyName: "composerInput", first: true, predicate: ["composerInput"], descendants: true }], ngImport: i0, template: "<div\n class=\"lc-chat\"\n [class.lc-chat--borderless]=\"!bordered()\"\n [class.lc-chat--width-narrow]=\"contentWidth() === 'narrow'\"\n [class.lc-chat--width-full]=\"contentWidth() === 'full'\"\n>\n @if (showHeader()) {\n <div class=\"lc-chat__header\">\n <span class=\"lc-chat__title\">{{ title() }}</span>\n @if (isStreaming()) {\n <span class=\"lc-chat__streaming-badge\">Streaming\u2026</span>\n }\n </div>\n }\n\n <div\n class=\"lc-chat__messages\"\n [class.lc-chat__messages--anchor-bottom]=\"messageAnchor() === 'bottom'\"\n #scrollContainer\n >\n <div class=\"lc-chat__thread\">\n @for (msg of formattedMessages(); track msg.id) {\n <div\n class=\"lc-chat__message\"\n [class.lc-chat__message--user]=\"msg.role === 'user'\"\n [class.lc-chat__message--agent]=\"msg.role === 'agent'\"\n [class.lc-chat__message--system]=\"msg.role === 'system'\"\n [class.lc-chat__message--streaming]=\"msg.streaming\"\n [attr.role]=\"msg.status === 'error' ? 'alert' : null\"\n [attr.aria-live]=\"statusAriaLive(msg.status)\"\n >\n @if (msg.role !== 'user' && (showAvatars() || isSemanticStatus(msg.status))) {\n <div class=\"lc-chat__marker\">\n @if (isSemanticStatus(msg.status)) {\n <span class=\"lc-chat__status-icon\">\n <lc-icon [name]=\"statusIcon(msg.status)\" size=\"xs\" [color]=\"statusColor(msg.status)\" [decorative]=\"true\" />\n </span>\n } @else if (msg.avatar) {\n <img [src]=\"msg.avatar\" [alt]=\"msg.name || msg.role\" class=\"lc-chat__avatar-img\" />\n } @else {\n <span\n class=\"lc-chat__dot\"\n [class.lc-chat__dot--agent]=\"msg.role === 'agent'\"\n [class.lc-chat__dot--system]=\"msg.role === 'system'\"\n ></span>\n }\n </div>\n }\n\n <div class=\"lc-chat__bubble\" [class.lc-chat__bubble--streaming]=\"msg.streaming\">\n @if (msg.role === 'user') {\n <div class=\"lc-chat__meta\">\n <span class=\"lc-chat__name\">{{ msg.name || 'Du' }}</span>\n @if (showTimestamps() && msg.timestamp) {\n <span class=\"lc-chat__time\">{{ formatTime(msg.timestamp) }}</span>\n }\n </div>\n } @else if (msg.name) {\n <div class=\"lc-chat__name\">{{ msg.name }}</div>\n }\n <div class=\"lc-chat__content\">\n @if (isSemanticStatus(msg.status)) {\n <span class=\"lc-chat__sr-only\">{{ statusLabel(msg.status) }} </span>\n }\n @if (messageTemplate) {\n <ng-container *ngTemplateOutlet=\"messageTemplate; context: { $implicit: msg }\" />\n } @else if (shouldRenderMarkdown(msg.role)) {\n <lc-markdown [content]=\"msg.content\" variant=\"chat\" />\n } @else {\n {{ msg.content }}\n }\n <span class=\"lc-chat__cursor\" [class.lc-chat__cursor--visible]=\"msg.streaming\"></span>\n </div>\n @if (msg.attachments?.length) {\n <div class=\"lc-chat__attachments\">\n @for (att of msg.attachments; track att.id) {\n @if (isImage(att) && att.url) {\n <a class=\"lc-chat__attachment lc-chat__attachment--image\" [href]=\"att.url\" target=\"_blank\" rel=\"noopener\">\n <img [src]=\"att.url\" [alt]=\"att.name\" />\n </a>\n } @else {\n <a\n class=\"lc-chat__attachment lc-chat__attachment--file\"\n [href]=\"att.url || null\"\n [attr.download]=\"att.url ? att.name : null\"\n target=\"_blank\"\n rel=\"noopener\"\n >\n <svg class=\"lc-chat__attachment-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M6 2.5h6L16 6.5v10A1.5 1.5 0 0 1 14.5 18h-9A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3H6V2.5Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M12 2.5V6.5H16\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n <span class=\"lc-chat__attachment-name\">{{ att.name }}</span>\n @if (att.size) {\n <span class=\"lc-chat__attachment-size\">{{ formatBytes(att.size) }}</span>\n }\n </a>\n }\n }\n </div>\n }\n @if (showTimestamps() && msg.timestamp && msg.role !== 'user') {\n <div class=\"lc-chat__time\">{{ formatTime(msg.timestamp) }}</div>\n }\n </div>\n </div>\n }\n\n @if (isStreaming() && !(formattedMessages().length && formattedMessages()[formattedMessages().length - 1].streaming)) {\n <div class=\"lc-chat__message lc-chat__message--agent\">\n @if (showAvatars()) {\n <div class=\"lc-chat__marker\">\n <span class=\"lc-chat__dot lc-chat__dot--agent\"></span>\n </div>\n }\n <div class=\"lc-chat__bubble\">\n <div class=\"lc-chat__typing\">\n <span></span><span></span><span></span>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <div class=\"lc-chat__input-area\">\n <div class=\"lc-chat__composer\">\n @if (allowFileUpload() && pendingAttachments().length) {\n <div class=\"lc-chat__pending-attachments\">\n @for (att of pendingAttachments(); track att.id) {\n <div class=\"lc-chat__pending-attachment\" [class.lc-chat__pending-attachment--image]=\"isImage(att)\">\n @if (isImage(att) && att.url) {\n <img class=\"lc-chat__pending-thumb\" [src]=\"att.url\" [alt]=\"att.name\" />\n } @else {\n <svg class=\"lc-chat__attachment-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M6 2.5h6L16 6.5v10A1.5 1.5 0 0 1 14.5 18h-9A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3H6V2.5Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M12 2.5V6.5H16\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n }\n <span class=\"lc-chat__pending-name\">{{ att.name }}</span>\n @if (att.size) {\n <span class=\"lc-chat__pending-size\">{{ formatBytes(att.size) }}</span>\n }\n <button\n type=\"button\"\n class=\"lc-chat__pending-remove\"\n (click)=\"removeAttachment(att.id)\"\n [attr.aria-label]=\"'Entfernen: ' + att.name\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M3 3l6 6M9 3l-6 6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </button>\n </div>\n }\n </div>\n }\n\n <textarea\n #composerInput\n class=\"lc-chat__input\"\n [placeholder]=\"placeholder()\"\n [value]=\"inputValue()\"\n [disabled]=\"disabled()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n rows=\"1\"\n ></textarea>\n\n <div class=\"lc-chat__composer-actions\">\n @if (allowFileUpload()) {\n <input\n #fileInput\n type=\"file\"\n class=\"lc-chat__file-input\"\n [accept]=\"accept() || null\"\n [multiple]=\"multiple()\"\n (change)=\"onFilesSelected($event)\"\n />\n <button\n type=\"button\"\n class=\"lc-chat__attach-btn\"\n [disabled]=\"disabled()\"\n (click)=\"openFilePicker()\"\n aria-label=\"Datei anh\u00E4ngen\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M14.5 9.5l-5 5a3 3 0 0 1-4.243-4.243l6.364-6.364a2 2 0 0 1 2.829 2.829l-6.364 6.364a1 1 0 1 1-1.415-1.415L11.5 6.5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n }\n <button\n class=\"lc-chat__send-btn\"\n [disabled]=\"(!inputValue().trim() && !pendingAttachments().length) || disabled()\"\n (click)=\"send()\"\n aria-label=\"Send\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M3 10l7-7m0 0l7 7m-7-7v14\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" transform=\"rotate(90 10 10)\"/>\n </svg>\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: [".lc-chat{--lc-chat-content-width: 46rem;display:flex;flex-direction:column;height:100%;min-height:20rem;border:1px solid var(--color-border);border-radius:var(--border-radius-lg);overflow:hidden;background-color:var(--color-surface);color:var(--color-text-primary);font-family:var(--font-family-sans, sans-serif)}.lc-chat--borderless{border:none;border-radius:0}.lc-chat--width-full{--lc-chat-content-width: 100%}.lc-chat__header{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-3) var(--spacing-4);border-bottom:1px solid var(--color-divider);background:var(--color-surface)}.lc-chat__title{font-size:var(--font-size-sm, .875rem);font-weight:var(--font-weight-semibold, 600);color:var(--color-text-primary)}.lc-chat__streaming-badge{font-size:var(--font-size-xs, .6875rem);padding:.125rem .5rem;background:color-mix(in srgb,var(--color-primary-500) 15%,transparent);color:var(--color-primary-500);border-radius:var(--border-radius-full, 1rem);font-weight:var(--font-weight-medium, 500);animation:pulse-badge 1.5s infinite}@keyframes pulse-badge{0%,to{opacity:1}50%{opacity:.6}}.lc-chat__messages{flex:1;overflow-y:auto;display:flex;flex-direction:column;padding-block:var(--spacing-6, 1.5rem);padding-inline:var(--spacing-4, 1rem)}.lc-chat__thread{position:relative;width:100%;max-width:var(--lc-chat-content-width, 46rem);margin-inline:auto;flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:var(--spacing-5, 1.25rem)}.lc-chat__messages--anchor-bottom .lc-chat__thread>.lc-chat__message:first-child{margin-top:auto}.lc-chat__message{position:relative;display:flex;align-items:stretch;gap:var(--spacing-3, .75rem);align-self:stretch;max-width:100%}.lc-chat__marker{position:relative;z-index:1;flex-shrink:0;width:.875rem;display:flex;flex-direction:column;align-items:center}.lc-chat__marker:before{content:\"\";position:absolute;left:50%;top:.55rem;bottom:calc(-1 * var(--spacing-5, 1.25rem));width:1.5px;transform:translate(-50%);background:var(--color-divider);z-index:-1}.lc-chat__message:last-child .lc-chat__marker:before{display:none}.lc-chat__dot{position:relative;z-index:1;width:.625rem;height:.625rem;margin-top:.25rem;border-radius:50%;background:var(--color-text-tertiary);box-shadow:0 0 0 3px var(--color-surface)}.lc-chat__dot--agent{background:var(--color-primary-500)}.lc-chat__dot--system{background:var(--color-text-tertiary);opacity:.6}.lc-chat__message--streaming .lc-chat__dot--agent{animation:dot-pulse 1.4s ease-out infinite}@keyframes dot-pulse{0%{box-shadow:0 0 0 3px var(--color-surface),0 0 0 3px color-mix(in srgb,var(--color-primary-500) 45%,transparent)}70%,to{box-shadow:0 0 0 3px var(--color-surface),0 0 0 9px color-mix(in srgb,var(--color-primary-500) 0%,transparent)}}.lc-chat__avatar-img{width:1.5rem;height:1.5rem;border-radius:50%;object-fit:cover;box-shadow:0 0 0 3px var(--color-surface)}.lc-chat__status-icon{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;width:1rem;height:1rem;margin-top:.0625rem;border-radius:50%;background:var(--color-surface);box-shadow:0 0 0 2px var(--color-surface)}.lc-chat__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.lc-chat__bubble{flex:1;min-width:0;font-size:var(--font-size-base, .9375rem);line-height:var(--line-height-relaxed, 1.6);word-break:break-word;color:var(--color-text-primary)}.lc-chat__message--user .lc-chat__bubble{position:relative;z-index:1;padding:var(--spacing-3, .75rem) var(--spacing-4, 1rem);border:1px solid var(--color-border);border-radius:var(--border-radius-lg);background:var(--color-surface-2)}.lc-chat__message--agent .lc-chat__bubble{padding:0;background:transparent}.lc-chat__message--system .lc-chat__bubble{padding:0;background:transparent;color:var(--color-text-tertiary);font-size:var(--font-size-sm, .8125rem)}.lc-chat__meta{display:flex;align-items:baseline;justify-content:space-between;gap:var(--spacing-2, .5rem);margin-bottom:var(--spacing-1, .25rem)}.lc-chat__meta .lc-chat__name{margin-bottom:0}.lc-chat__meta .lc-chat__time{margin-top:0}.lc-chat__name{font-size:var(--font-size-xs, .75rem);font-weight:var(--font-weight-semibold, 600);color:var(--color-text-secondary);margin-bottom:var(--spacing-1, .25rem)}.lc-chat__content{white-space:pre-wrap}.lc-chat__cursor{display:none}.lc-chat__cursor--visible{display:inline-block;width:2px;height:1em;background:currentColor;margin-left:1px;vertical-align:text-bottom;animation:blink-cursor .8s step-end infinite}@keyframes blink-cursor{0%,to{opacity:1}50%{opacity:0}}.lc-chat__time{font-size:var(--font-size-xs, .75rem);color:var(--color-text-tertiary);margin-top:var(--spacing-1, .25rem);flex-shrink:0}.lc-chat__typing{display:flex;gap:.25rem;padding:.25rem 0}.lc-chat__typing span{width:.375rem;height:.375rem;border-radius:50%;background:var(--color-text-tertiary);animation:typing-bounce 1.4s ease-in-out infinite}.lc-chat__typing span:nth-child(2){animation-delay:.2s}.lc-chat__typing span:nth-child(3){animation-delay:.4s}@keyframes typing-bounce{0%,60%,to{transform:translateY(0)}30%{transform:translateY(-.375rem)}}.lc-chat__input-area{padding:var(--spacing-4, .75rem);border-top:1px solid var(--color-divider);background:var(--color-surface)}.lc-chat__composer{width:100%;max-width:var(--lc-chat-content-width, 46rem);margin-inline:auto;display:flex;flex-direction:column;gap:var(--spacing-2, .5rem);padding:var(--spacing-3, .75rem);background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:var(--border-radius-xl);transition:border-color .15s,box-shadow .15s}.lc-chat__composer:focus-within{border-color:var(--color-primary-500);box-shadow:var(--elevation-1)}.lc-chat__input{display:block;width:100%;box-sizing:border-box;resize:none;border:none;background:transparent;padding:var(--spacing-1, .25rem) var(--spacing-2, .5rem);font-size:var(--font-size-base, .875rem);font-family:inherit;line-height:var(--line-height-normal, 1.4);color:var(--color-text-primary);outline:none;height:auto;max-height:12rem;overflow-y:auto}.lc-chat__input:disabled{opacity:.5;cursor:not-allowed}.lc-chat__input::placeholder{color:var(--color-text-tertiary)}.lc-chat__composer-actions{display:flex;align-items:center;gap:var(--spacing-2, .5rem)}.lc-chat__send-btn{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;margin-left:auto;border:none;border-radius:var(--border-radius-md);background:var(--color-primary-fill);color:var(--color-on-primary);cursor:pointer;flex-shrink:0;transition:background .15s,opacity .15s}.lc-chat__send-btn:hover:not(:disabled){background:var(--color-primary-fill-hover)}.lc-chat__send-btn:disabled{opacity:.4;cursor:not-allowed}.lc-chat__file-input{display:none}.lc-chat__attach-btn{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:none;border-radius:var(--border-radius-md);background:transparent;color:var(--color-text-tertiary);cursor:pointer;flex-shrink:0;transition:background .15s,color .15s}.lc-chat__attach-btn:hover:not(:disabled){background:var(--color-surface-hover);color:var(--color-text-primary)}.lc-chat__attach-btn:disabled{opacity:.4;cursor:not-allowed}.lc-chat__pending-attachments{display:flex;flex-wrap:wrap;gap:.375rem}.lc-chat__pending-attachment{display:inline-flex;align-items:center;gap:.375rem;padding:.25rem .375rem .25rem .5rem;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--border-radius-md, .5rem);font-size:var(--font-size-xs, .75rem);color:var(--color-text-primary);max-width:16rem}.lc-chat__pending-thumb{width:1.75rem;height:1.75rem;object-fit:cover;border-radius:.25rem;flex-shrink:0}.lc-chat__pending-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:9rem}.lc-chat__pending-size{color:var(--color-text-tertiary);font-variant-numeric:tabular-nums}.lc-chat__pending-remove{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;border:none;border-radius:50%;background:transparent;color:var(--color-text-tertiary);cursor:pointer;flex-shrink:0}.lc-chat__pending-remove:hover{background:var(--color-surface-hover);color:var(--color-text-primary)}.lc-chat__attachments{display:flex;flex-wrap:wrap;gap:.375rem;margin-top:.5rem}.lc-chat__attachment{display:inline-flex;align-items:center;gap:.375rem;text-decoration:none;color:inherit;font-size:var(--font-size-xs, .75rem)}.lc-chat__attachment--file{padding:.25rem .5rem;background:color-mix(in srgb,currentColor 8%,transparent);border-radius:var(--border-radius-md, .375rem);max-width:16rem}.lc-chat__attachment--file:hover{background:color-mix(in srgb,currentColor 16%,transparent)}.lc-chat__attachment--image img{max-width:12rem;max-height:8rem;border-radius:var(--border-radius-md, .375rem);object-fit:cover;display:block}.lc-chat__attachment-icon{flex-shrink:0}.lc-chat__attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-chat__attachment-size{opacity:.7;font-variant-numeric:tabular-nums}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MarkdownComponent, selector: "lc-markdown", inputs: ["src", "content", "variant", "linkTarget", "sanitize", "syntaxHighlight", "showHeadingAnchors", "baseUrl"], outputs: ["linkClick", "rendered"] }, { kind: "component", type: IconComponent, selector: "lc-icon", inputs: ["name", "variant", "size", "color", "ariaLabel", "decorative"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
11495
11800
|
}
|
|
11496
11801
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ChatComponent, decorators: [{
|
|
11497
11802
|
type: Component,
|
|
11498
|
-
args: [{ selector: 'lc-chat', standalone: true, imports: [NgTemplateOutlet, MarkdownComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"lc-chat\"\n [class.lc-chat--borderless]=\"!bordered()\"\n [class.lc-chat--width-narrow]=\"contentWidth() === 'narrow'\"\n [class.lc-chat--width-full]=\"contentWidth() === 'full'\"\n>\n @if (showHeader()) {\n <div class=\"lc-chat__header\">\n <span class=\"lc-chat__title\">{{ title() }}</span>\n @if (isStreaming()) {\n <span class=\"lc-chat__streaming-badge\">Streaming\u2026</span>\n }\n </div>\n }\n\n <div\n class=\"lc-chat__messages\"\n [class.lc-chat__messages--anchor-bottom]=\"messageAnchor() === 'bottom'\"\n #scrollContainer\n >\n <div class=\"lc-chat__thread\">\n @for (msg of formattedMessages(); track msg.id) {\n <div\n class=\"lc-chat__message\"\n [class.lc-chat__message--user]=\"msg.role === 'user'\"\n [class.lc-chat__message--agent]=\"msg.role === 'agent'\"\n [class.lc-chat__message--system]=\"msg.role === 'system'\"\n [class.lc-chat__message--streaming]=\"msg.streaming\"\n >\n @if (showAvatars() && msg.role !== 'user') {\n <div class=\"lc-chat__marker\">\n @if (msg.avatar) {\n <img [src]=\"msg.avatar\" [alt]=\"msg.name || msg.role\" class=\"lc-chat__avatar-img\" />\n } @else {\n <span\n class=\"lc-chat__dot\"\n [class.lc-chat__dot--agent]=\"msg.role === 'agent'\"\n [class.lc-chat__dot--system]=\"msg.role === 'system'\"\n ></span>\n }\n </div>\n }\n\n <div class=\"lc-chat__bubble\" [class.lc-chat__bubble--streaming]=\"msg.streaming\">\n @if (msg.role === 'user') {\n <div class=\"lc-chat__meta\">\n <span class=\"lc-chat__name\">{{ msg.name || 'Du' }}</span>\n @if (showTimestamps() && msg.timestamp) {\n <span class=\"lc-chat__time\">{{ formatTime(msg.timestamp) }}</span>\n }\n </div>\n } @else if (msg.name) {\n <div class=\"lc-chat__name\">{{ msg.name }}</div>\n }\n <div class=\"lc-chat__content\">\n @if (messageTemplate) {\n <ng-container *ngTemplateOutlet=\"messageTemplate; context: { $implicit: msg }\" />\n } @else if (shouldRenderMarkdown(msg.role)) {\n <lc-markdown [content]=\"msg.content\" variant=\"chat\" />\n } @else {\n {{ msg.content }}\n }\n <span class=\"lc-chat__cursor\" [class.lc-chat__cursor--visible]=\"msg.streaming\"></span>\n </div>\n @if (msg.attachments?.length) {\n <div class=\"lc-chat__attachments\">\n @for (att of msg.attachments; track att.id) {\n @if (isImage(att) && att.url) {\n <a class=\"lc-chat__attachment lc-chat__attachment--image\" [href]=\"att.url\" target=\"_blank\" rel=\"noopener\">\n <img [src]=\"att.url\" [alt]=\"att.name\" />\n </a>\n } @else {\n <a\n class=\"lc-chat__attachment lc-chat__attachment--file\"\n [href]=\"att.url || null\"\n [attr.download]=\"att.url ? att.name : null\"\n target=\"_blank\"\n rel=\"noopener\"\n >\n <svg class=\"lc-chat__attachment-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M6 2.5h6L16 6.5v10A1.5 1.5 0 0 1 14.5 18h-9A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3H6V2.5Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M12 2.5V6.5H16\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n <span class=\"lc-chat__attachment-name\">{{ att.name }}</span>\n @if (att.size) {\n <span class=\"lc-chat__attachment-size\">{{ formatBytes(att.size) }}</span>\n }\n </a>\n }\n }\n </div>\n }\n @if (showTimestamps() && msg.timestamp && msg.role !== 'user') {\n <div class=\"lc-chat__time\">{{ formatTime(msg.timestamp) }}</div>\n }\n </div>\n </div>\n }\n\n @if (isStreaming() && !(formattedMessages().length && formattedMessages()[formattedMessages().length - 1].streaming)) {\n <div class=\"lc-chat__message lc-chat__message--agent\">\n @if (showAvatars()) {\n <div class=\"lc-chat__marker\">\n <span class=\"lc-chat__dot lc-chat__dot--agent\"></span>\n </div>\n }\n <div class=\"lc-chat__bubble\">\n <div class=\"lc-chat__typing\">\n <span></span><span></span><span></span>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <div class=\"lc-chat__input-area\">\n <div class=\"lc-chat__composer\">\n @if (allowFileUpload() && pendingAttachments().length) {\n <div class=\"lc-chat__pending-attachments\">\n @for (att of pendingAttachments(); track att.id) {\n <div class=\"lc-chat__pending-attachment\" [class.lc-chat__pending-attachment--image]=\"isImage(att)\">\n @if (isImage(att) && att.url) {\n <img class=\"lc-chat__pending-thumb\" [src]=\"att.url\" [alt]=\"att.name\" />\n } @else {\n <svg class=\"lc-chat__attachment-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M6 2.5h6L16 6.5v10A1.5 1.5 0 0 1 14.5 18h-9A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3H6V2.5Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M12 2.5V6.5H16\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n }\n <span class=\"lc-chat__pending-name\">{{ att.name }}</span>\n @if (att.size) {\n <span class=\"lc-chat__pending-size\">{{ formatBytes(att.size) }}</span>\n }\n <button\n type=\"button\"\n class=\"lc-chat__pending-remove\"\n (click)=\"removeAttachment(att.id)\"\n [attr.aria-label]=\"'Entfernen: ' + att.name\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M3 3l6 6M9 3l-6 6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </button>\n </div>\n }\n </div>\n }\n\n <textarea\n #composerInput\n class=\"lc-chat__input\"\n [placeholder]=\"placeholder()\"\n [value]=\"inputValue()\"\n [disabled]=\"disabled()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n rows=\"1\"\n ></textarea>\n\n <div class=\"lc-chat__composer-actions\">\n @if (allowFileUpload()) {\n <input\n #fileInput\n type=\"file\"\n class=\"lc-chat__file-input\"\n [accept]=\"accept() || null\"\n [multiple]=\"multiple()\"\n (change)=\"onFilesSelected($event)\"\n />\n <button\n type=\"button\"\n class=\"lc-chat__attach-btn\"\n [disabled]=\"disabled()\"\n (click)=\"openFilePicker()\"\n aria-label=\"Datei anh\u00E4ngen\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M14.5 9.5l-5 5a3 3 0 0 1-4.243-4.243l6.364-6.364a2 2 0 0 1 2.829 2.829l-6.364 6.364a1 1 0 1 1-1.415-1.415L11.5 6.5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n }\n <button\n class=\"lc-chat__send-btn\"\n [disabled]=\"(!inputValue().trim() && !pendingAttachments().length) || disabled()\"\n (click)=\"send()\"\n aria-label=\"Send\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M3 10l7-7m0 0l7 7m-7-7v14\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" transform=\"rotate(90 10 10)\"/>\n </svg>\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: [".lc-chat{--lc-chat-content-width: 46rem;display:flex;flex-direction:column;height:100%;min-height:20rem;border:1px solid var(--color-border);border-radius:var(--border-radius-lg);overflow:hidden;background-color:var(--color-surface);color:var(--color-text-primary);font-family:var(--font-family-sans, sans-serif)}.lc-chat--borderless{border:none;border-radius:0}.lc-chat--width-full{--lc-chat-content-width: 100%}.lc-chat__header{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-3) var(--spacing-4);border-bottom:1px solid var(--color-divider);background:var(--color-surface)}.lc-chat__title{font-size:var(--font-size-sm, .875rem);font-weight:var(--font-weight-semibold, 600);color:var(--color-text-primary)}.lc-chat__streaming-badge{font-size:var(--font-size-xs, .6875rem);padding:.125rem .5rem;background:color-mix(in srgb,var(--color-primary-500) 15%,transparent);color:var(--color-primary-500);border-radius:var(--border-radius-full, 1rem);font-weight:var(--font-weight-medium, 500);animation:pulse-badge 1.5s infinite}@keyframes pulse-badge{0%,to{opacity:1}50%{opacity:.6}}.lc-chat__messages{flex:1;overflow-y:auto;display:flex;flex-direction:column;padding-block:var(--spacing-6, 1.5rem);padding-inline:var(--spacing-4, 1rem)}.lc-chat__thread{position:relative;width:100%;max-width:var(--lc-chat-content-width, 46rem);margin-inline:auto;flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:var(--spacing-5, 1.25rem)}.lc-chat__messages--anchor-bottom .lc-chat__thread>.lc-chat__message:first-child{margin-top:auto}.lc-chat__message{position:relative;display:flex;align-items:stretch;gap:var(--spacing-3, .75rem);align-self:stretch;max-width:100%}.lc-chat__marker{position:relative;z-index:1;flex-shrink:0;width:.875rem;display:flex;flex-direction:column;align-items:center}.lc-chat__marker:before{content:\"\";position:absolute;left:50%;top:.55rem;bottom:calc(-1 * var(--spacing-5, 1.25rem));width:1.5px;transform:translate(-50%);background:var(--color-divider);z-index:-1}.lc-chat__message:last-child .lc-chat__marker:before{display:none}.lc-chat__dot{position:relative;z-index:1;width:.625rem;height:.625rem;margin-top:.25rem;border-radius:50%;background:var(--color-text-tertiary);box-shadow:0 0 0 3px var(--color-surface)}.lc-chat__dot--agent{background:var(--color-primary-500)}.lc-chat__dot--system{background:var(--color-text-tertiary);opacity:.6}.lc-chat__message--streaming .lc-chat__dot--agent{animation:dot-pulse 1.4s ease-out infinite}@keyframes dot-pulse{0%{box-shadow:0 0 0 3px var(--color-surface),0 0 0 3px color-mix(in srgb,var(--color-primary-500) 45%,transparent)}70%,to{box-shadow:0 0 0 3px var(--color-surface),0 0 0 9px color-mix(in srgb,var(--color-primary-500) 0%,transparent)}}.lc-chat__avatar-img{width:1.5rem;height:1.5rem;border-radius:50%;object-fit:cover;box-shadow:0 0 0 3px var(--color-surface)}.lc-chat__bubble{flex:1;min-width:0;font-size:var(--font-size-base, .9375rem);line-height:var(--line-height-relaxed, 1.6);word-break:break-word;color:var(--color-text-primary)}.lc-chat__message--user .lc-chat__bubble{position:relative;z-index:1;padding:var(--spacing-3, .75rem) var(--spacing-4, 1rem);border:1px solid var(--color-border);border-radius:var(--border-radius-lg);background:var(--color-surface-2)}.lc-chat__message--agent .lc-chat__bubble{padding:0;background:transparent}.lc-chat__message--system .lc-chat__bubble{padding:0;background:transparent;color:var(--color-text-tertiary);font-size:var(--font-size-sm, .8125rem)}.lc-chat__meta{display:flex;align-items:baseline;justify-content:space-between;gap:var(--spacing-2, .5rem);margin-bottom:var(--spacing-1, .25rem)}.lc-chat__meta .lc-chat__name{margin-bottom:0}.lc-chat__meta .lc-chat__time{margin-top:0}.lc-chat__name{font-size:var(--font-size-xs, .75rem);font-weight:var(--font-weight-semibold, 600);color:var(--color-text-secondary);margin-bottom:var(--spacing-1, .25rem)}.lc-chat__content{white-space:pre-wrap}.lc-chat__cursor{display:none}.lc-chat__cursor--visible{display:inline-block;width:2px;height:1em;background:currentColor;margin-left:1px;vertical-align:text-bottom;animation:blink-cursor .8s step-end infinite}@keyframes blink-cursor{0%,to{opacity:1}50%{opacity:0}}.lc-chat__time{font-size:var(--font-size-xs, .75rem);color:var(--color-text-tertiary);margin-top:var(--spacing-1, .25rem);flex-shrink:0}.lc-chat__typing{display:flex;gap:.25rem;padding:.25rem 0}.lc-chat__typing span{width:.375rem;height:.375rem;border-radius:50%;background:var(--color-text-tertiary);animation:typing-bounce 1.4s ease-in-out infinite}.lc-chat__typing span:nth-child(2){animation-delay:.2s}.lc-chat__typing span:nth-child(3){animation-delay:.4s}@keyframes typing-bounce{0%,60%,to{transform:translateY(0)}30%{transform:translateY(-.375rem)}}.lc-chat__input-area{padding:var(--spacing-4, .75rem);border-top:1px solid var(--color-divider);background:var(--color-surface)}.lc-chat__composer{width:100%;max-width:var(--lc-chat-content-width, 46rem);margin-inline:auto;display:flex;flex-direction:column;gap:var(--spacing-2, .5rem);padding:var(--spacing-3, .75rem);background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:var(--border-radius-xl);transition:border-color .15s,box-shadow .15s}.lc-chat__composer:focus-within{border-color:var(--color-primary-500);box-shadow:var(--elevation-1)}.lc-chat__input{display:block;width:100%;box-sizing:border-box;resize:none;border:none;background:transparent;padding:var(--spacing-1, .25rem) var(--spacing-2, .5rem);font-size:var(--font-size-base, .875rem);font-family:inherit;line-height:var(--line-height-normal, 1.4);color:var(--color-text-primary);outline:none;height:auto;max-height:12rem;overflow-y:auto}.lc-chat__input:disabled{opacity:.5;cursor:not-allowed}.lc-chat__input::placeholder{color:var(--color-text-tertiary)}.lc-chat__composer-actions{display:flex;align-items:center;gap:var(--spacing-2, .5rem)}.lc-chat__send-btn{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;margin-left:auto;border:none;border-radius:var(--border-radius-md);background:var(--color-primary-fill);color:var(--color-on-primary);cursor:pointer;flex-shrink:0;transition:background .15s,opacity .15s}.lc-chat__send-btn:hover:not(:disabled){background:var(--color-primary-fill-hover)}.lc-chat__send-btn:disabled{opacity:.4;cursor:not-allowed}.lc-chat__file-input{display:none}.lc-chat__attach-btn{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:none;border-radius:var(--border-radius-md);background:transparent;color:var(--color-text-tertiary);cursor:pointer;flex-shrink:0;transition:background .15s,color .15s}.lc-chat__attach-btn:hover:not(:disabled){background:var(--color-surface-hover);color:var(--color-text-primary)}.lc-chat__attach-btn:disabled{opacity:.4;cursor:not-allowed}.lc-chat__pending-attachments{display:flex;flex-wrap:wrap;gap:.375rem}.lc-chat__pending-attachment{display:inline-flex;align-items:center;gap:.375rem;padding:.25rem .375rem .25rem .5rem;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--border-radius-md, .5rem);font-size:var(--font-size-xs, .75rem);color:var(--color-text-primary);max-width:16rem}.lc-chat__pending-thumb{width:1.75rem;height:1.75rem;object-fit:cover;border-radius:.25rem;flex-shrink:0}.lc-chat__pending-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:9rem}.lc-chat__pending-size{color:var(--color-text-tertiary);font-variant-numeric:tabular-nums}.lc-chat__pending-remove{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;border:none;border-radius:50%;background:transparent;color:var(--color-text-tertiary);cursor:pointer;flex-shrink:0}.lc-chat__pending-remove:hover{background:var(--color-surface-hover);color:var(--color-text-primary)}.lc-chat__attachments{display:flex;flex-wrap:wrap;gap:.375rem;margin-top:.5rem}.lc-chat__attachment{display:inline-flex;align-items:center;gap:.375rem;text-decoration:none;color:inherit;font-size:var(--font-size-xs, .75rem)}.lc-chat__attachment--file{padding:.25rem .5rem;background:color-mix(in srgb,currentColor 8%,transparent);border-radius:var(--border-radius-md, .375rem);max-width:16rem}.lc-chat__attachment--file:hover{background:color-mix(in srgb,currentColor 16%,transparent)}.lc-chat__attachment--image img{max-width:12rem;max-height:8rem;border-radius:var(--border-radius-md, .375rem);object-fit:cover;display:block}.lc-chat__attachment-icon{flex-shrink:0}.lc-chat__attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-chat__attachment-size{opacity:.7;font-variant-numeric:tabular-nums}\n"] }]
|
|
11803
|
+
args: [{ selector: 'lc-chat', standalone: true, imports: [NgTemplateOutlet, MarkdownComponent, IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"lc-chat\"\n [class.lc-chat--borderless]=\"!bordered()\"\n [class.lc-chat--width-narrow]=\"contentWidth() === 'narrow'\"\n [class.lc-chat--width-full]=\"contentWidth() === 'full'\"\n>\n @if (showHeader()) {\n <div class=\"lc-chat__header\">\n <span class=\"lc-chat__title\">{{ title() }}</span>\n @if (isStreaming()) {\n <span class=\"lc-chat__streaming-badge\">Streaming\u2026</span>\n }\n </div>\n }\n\n <div\n class=\"lc-chat__messages\"\n [class.lc-chat__messages--anchor-bottom]=\"messageAnchor() === 'bottom'\"\n #scrollContainer\n >\n <div class=\"lc-chat__thread\">\n @for (msg of formattedMessages(); track msg.id) {\n <div\n class=\"lc-chat__message\"\n [class.lc-chat__message--user]=\"msg.role === 'user'\"\n [class.lc-chat__message--agent]=\"msg.role === 'agent'\"\n [class.lc-chat__message--system]=\"msg.role === 'system'\"\n [class.lc-chat__message--streaming]=\"msg.streaming\"\n [attr.role]=\"msg.status === 'error' ? 'alert' : null\"\n [attr.aria-live]=\"statusAriaLive(msg.status)\"\n >\n @if (msg.role !== 'user' && (showAvatars() || isSemanticStatus(msg.status))) {\n <div class=\"lc-chat__marker\">\n @if (isSemanticStatus(msg.status)) {\n <span class=\"lc-chat__status-icon\">\n <lc-icon [name]=\"statusIcon(msg.status)\" size=\"xs\" [color]=\"statusColor(msg.status)\" [decorative]=\"true\" />\n </span>\n } @else if (msg.avatar) {\n <img [src]=\"msg.avatar\" [alt]=\"msg.name || msg.role\" class=\"lc-chat__avatar-img\" />\n } @else {\n <span\n class=\"lc-chat__dot\"\n [class.lc-chat__dot--agent]=\"msg.role === 'agent'\"\n [class.lc-chat__dot--system]=\"msg.role === 'system'\"\n ></span>\n }\n </div>\n }\n\n <div class=\"lc-chat__bubble\" [class.lc-chat__bubble--streaming]=\"msg.streaming\">\n @if (msg.role === 'user') {\n <div class=\"lc-chat__meta\">\n <span class=\"lc-chat__name\">{{ msg.name || 'Du' }}</span>\n @if (showTimestamps() && msg.timestamp) {\n <span class=\"lc-chat__time\">{{ formatTime(msg.timestamp) }}</span>\n }\n </div>\n } @else if (msg.name) {\n <div class=\"lc-chat__name\">{{ msg.name }}</div>\n }\n <div class=\"lc-chat__content\">\n @if (isSemanticStatus(msg.status)) {\n <span class=\"lc-chat__sr-only\">{{ statusLabel(msg.status) }} </span>\n }\n @if (messageTemplate) {\n <ng-container *ngTemplateOutlet=\"messageTemplate; context: { $implicit: msg }\" />\n } @else if (shouldRenderMarkdown(msg.role)) {\n <lc-markdown [content]=\"msg.content\" variant=\"chat\" />\n } @else {\n {{ msg.content }}\n }\n <span class=\"lc-chat__cursor\" [class.lc-chat__cursor--visible]=\"msg.streaming\"></span>\n </div>\n @if (msg.attachments?.length) {\n <div class=\"lc-chat__attachments\">\n @for (att of msg.attachments; track att.id) {\n @if (isImage(att) && att.url) {\n <a class=\"lc-chat__attachment lc-chat__attachment--image\" [href]=\"att.url\" target=\"_blank\" rel=\"noopener\">\n <img [src]=\"att.url\" [alt]=\"att.name\" />\n </a>\n } @else {\n <a\n class=\"lc-chat__attachment lc-chat__attachment--file\"\n [href]=\"att.url || null\"\n [attr.download]=\"att.url ? att.name : null\"\n target=\"_blank\"\n rel=\"noopener\"\n >\n <svg class=\"lc-chat__attachment-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M6 2.5h6L16 6.5v10A1.5 1.5 0 0 1 14.5 18h-9A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3H6V2.5Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M12 2.5V6.5H16\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n <span class=\"lc-chat__attachment-name\">{{ att.name }}</span>\n @if (att.size) {\n <span class=\"lc-chat__attachment-size\">{{ formatBytes(att.size) }}</span>\n }\n </a>\n }\n }\n </div>\n }\n @if (showTimestamps() && msg.timestamp && msg.role !== 'user') {\n <div class=\"lc-chat__time\">{{ formatTime(msg.timestamp) }}</div>\n }\n </div>\n </div>\n }\n\n @if (isStreaming() && !(formattedMessages().length && formattedMessages()[formattedMessages().length - 1].streaming)) {\n <div class=\"lc-chat__message lc-chat__message--agent\">\n @if (showAvatars()) {\n <div class=\"lc-chat__marker\">\n <span class=\"lc-chat__dot lc-chat__dot--agent\"></span>\n </div>\n }\n <div class=\"lc-chat__bubble\">\n <div class=\"lc-chat__typing\">\n <span></span><span></span><span></span>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <div class=\"lc-chat__input-area\">\n <div class=\"lc-chat__composer\">\n @if (allowFileUpload() && pendingAttachments().length) {\n <div class=\"lc-chat__pending-attachments\">\n @for (att of pendingAttachments(); track att.id) {\n <div class=\"lc-chat__pending-attachment\" [class.lc-chat__pending-attachment--image]=\"isImage(att)\">\n @if (isImage(att) && att.url) {\n <img class=\"lc-chat__pending-thumb\" [src]=\"att.url\" [alt]=\"att.name\" />\n } @else {\n <svg class=\"lc-chat__attachment-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M6 2.5h6L16 6.5v10A1.5 1.5 0 0 1 14.5 18h-9A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3H6V2.5Z\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M12 2.5V6.5H16\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n }\n <span class=\"lc-chat__pending-name\">{{ att.name }}</span>\n @if (att.size) {\n <span class=\"lc-chat__pending-size\">{{ formatBytes(att.size) }}</span>\n }\n <button\n type=\"button\"\n class=\"lc-chat__pending-remove\"\n (click)=\"removeAttachment(att.id)\"\n [attr.aria-label]=\"'Entfernen: ' + att.name\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M3 3l6 6M9 3l-6 6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n </button>\n </div>\n }\n </div>\n }\n\n <textarea\n #composerInput\n class=\"lc-chat__input\"\n [placeholder]=\"placeholder()\"\n [value]=\"inputValue()\"\n [disabled]=\"disabled()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n rows=\"1\"\n ></textarea>\n\n <div class=\"lc-chat__composer-actions\">\n @if (allowFileUpload()) {\n <input\n #fileInput\n type=\"file\"\n class=\"lc-chat__file-input\"\n [accept]=\"accept() || null\"\n [multiple]=\"multiple()\"\n (change)=\"onFilesSelected($event)\"\n />\n <button\n type=\"button\"\n class=\"lc-chat__attach-btn\"\n [disabled]=\"disabled()\"\n (click)=\"openFilePicker()\"\n aria-label=\"Datei anh\u00E4ngen\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M14.5 9.5l-5 5a3 3 0 0 1-4.243-4.243l6.364-6.364a2 2 0 0 1 2.829 2.829l-6.364 6.364a1 1 0 1 1-1.415-1.415L11.5 6.5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n }\n <button\n class=\"lc-chat__send-btn\"\n [disabled]=\"(!inputValue().trim() && !pendingAttachments().length) || disabled()\"\n (click)=\"send()\"\n aria-label=\"Send\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M3 10l7-7m0 0l7 7m-7-7v14\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" transform=\"rotate(90 10 10)\"/>\n </svg>\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: [".lc-chat{--lc-chat-content-width: 46rem;display:flex;flex-direction:column;height:100%;min-height:20rem;border:1px solid var(--color-border);border-radius:var(--border-radius-lg);overflow:hidden;background-color:var(--color-surface);color:var(--color-text-primary);font-family:var(--font-family-sans, sans-serif)}.lc-chat--borderless{border:none;border-radius:0}.lc-chat--width-full{--lc-chat-content-width: 100%}.lc-chat__header{display:flex;align-items:center;justify-content:space-between;padding:var(--spacing-3) var(--spacing-4);border-bottom:1px solid var(--color-divider);background:var(--color-surface)}.lc-chat__title{font-size:var(--font-size-sm, .875rem);font-weight:var(--font-weight-semibold, 600);color:var(--color-text-primary)}.lc-chat__streaming-badge{font-size:var(--font-size-xs, .6875rem);padding:.125rem .5rem;background:color-mix(in srgb,var(--color-primary-500) 15%,transparent);color:var(--color-primary-500);border-radius:var(--border-radius-full, 1rem);font-weight:var(--font-weight-medium, 500);animation:pulse-badge 1.5s infinite}@keyframes pulse-badge{0%,to{opacity:1}50%{opacity:.6}}.lc-chat__messages{flex:1;overflow-y:auto;display:flex;flex-direction:column;padding-block:var(--spacing-6, 1.5rem);padding-inline:var(--spacing-4, 1rem)}.lc-chat__thread{position:relative;width:100%;max-width:var(--lc-chat-content-width, 46rem);margin-inline:auto;flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:var(--spacing-5, 1.25rem)}.lc-chat__messages--anchor-bottom .lc-chat__thread>.lc-chat__message:first-child{margin-top:auto}.lc-chat__message{position:relative;display:flex;align-items:stretch;gap:var(--spacing-3, .75rem);align-self:stretch;max-width:100%}.lc-chat__marker{position:relative;z-index:1;flex-shrink:0;width:.875rem;display:flex;flex-direction:column;align-items:center}.lc-chat__marker:before{content:\"\";position:absolute;left:50%;top:.55rem;bottom:calc(-1 * var(--spacing-5, 1.25rem));width:1.5px;transform:translate(-50%);background:var(--color-divider);z-index:-1}.lc-chat__message:last-child .lc-chat__marker:before{display:none}.lc-chat__dot{position:relative;z-index:1;width:.625rem;height:.625rem;margin-top:.25rem;border-radius:50%;background:var(--color-text-tertiary);box-shadow:0 0 0 3px var(--color-surface)}.lc-chat__dot--agent{background:var(--color-primary-500)}.lc-chat__dot--system{background:var(--color-text-tertiary);opacity:.6}.lc-chat__message--streaming .lc-chat__dot--agent{animation:dot-pulse 1.4s ease-out infinite}@keyframes dot-pulse{0%{box-shadow:0 0 0 3px var(--color-surface),0 0 0 3px color-mix(in srgb,var(--color-primary-500) 45%,transparent)}70%,to{box-shadow:0 0 0 3px var(--color-surface),0 0 0 9px color-mix(in srgb,var(--color-primary-500) 0%,transparent)}}.lc-chat__avatar-img{width:1.5rem;height:1.5rem;border-radius:50%;object-fit:cover;box-shadow:0 0 0 3px var(--color-surface)}.lc-chat__status-icon{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;width:1rem;height:1rem;margin-top:.0625rem;border-radius:50%;background:var(--color-surface);box-shadow:0 0 0 2px var(--color-surface)}.lc-chat__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.lc-chat__bubble{flex:1;min-width:0;font-size:var(--font-size-base, .9375rem);line-height:var(--line-height-relaxed, 1.6);word-break:break-word;color:var(--color-text-primary)}.lc-chat__message--user .lc-chat__bubble{position:relative;z-index:1;padding:var(--spacing-3, .75rem) var(--spacing-4, 1rem);border:1px solid var(--color-border);border-radius:var(--border-radius-lg);background:var(--color-surface-2)}.lc-chat__message--agent .lc-chat__bubble{padding:0;background:transparent}.lc-chat__message--system .lc-chat__bubble{padding:0;background:transparent;color:var(--color-text-tertiary);font-size:var(--font-size-sm, .8125rem)}.lc-chat__meta{display:flex;align-items:baseline;justify-content:space-between;gap:var(--spacing-2, .5rem);margin-bottom:var(--spacing-1, .25rem)}.lc-chat__meta .lc-chat__name{margin-bottom:0}.lc-chat__meta .lc-chat__time{margin-top:0}.lc-chat__name{font-size:var(--font-size-xs, .75rem);font-weight:var(--font-weight-semibold, 600);color:var(--color-text-secondary);margin-bottom:var(--spacing-1, .25rem)}.lc-chat__content{white-space:pre-wrap}.lc-chat__cursor{display:none}.lc-chat__cursor--visible{display:inline-block;width:2px;height:1em;background:currentColor;margin-left:1px;vertical-align:text-bottom;animation:blink-cursor .8s step-end infinite}@keyframes blink-cursor{0%,to{opacity:1}50%{opacity:0}}.lc-chat__time{font-size:var(--font-size-xs, .75rem);color:var(--color-text-tertiary);margin-top:var(--spacing-1, .25rem);flex-shrink:0}.lc-chat__typing{display:flex;gap:.25rem;padding:.25rem 0}.lc-chat__typing span{width:.375rem;height:.375rem;border-radius:50%;background:var(--color-text-tertiary);animation:typing-bounce 1.4s ease-in-out infinite}.lc-chat__typing span:nth-child(2){animation-delay:.2s}.lc-chat__typing span:nth-child(3){animation-delay:.4s}@keyframes typing-bounce{0%,60%,to{transform:translateY(0)}30%{transform:translateY(-.375rem)}}.lc-chat__input-area{padding:var(--spacing-4, .75rem);border-top:1px solid var(--color-divider);background:var(--color-surface)}.lc-chat__composer{width:100%;max-width:var(--lc-chat-content-width, 46rem);margin-inline:auto;display:flex;flex-direction:column;gap:var(--spacing-2, .5rem);padding:var(--spacing-3, .75rem);background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:var(--border-radius-xl);transition:border-color .15s,box-shadow .15s}.lc-chat__composer:focus-within{border-color:var(--color-primary-500);box-shadow:var(--elevation-1)}.lc-chat__input{display:block;width:100%;box-sizing:border-box;resize:none;border:none;background:transparent;padding:var(--spacing-1, .25rem) var(--spacing-2, .5rem);font-size:var(--font-size-base, .875rem);font-family:inherit;line-height:var(--line-height-normal, 1.4);color:var(--color-text-primary);outline:none;height:auto;max-height:12rem;overflow-y:auto}.lc-chat__input:disabled{opacity:.5;cursor:not-allowed}.lc-chat__input::placeholder{color:var(--color-text-tertiary)}.lc-chat__composer-actions{display:flex;align-items:center;gap:var(--spacing-2, .5rem)}.lc-chat__send-btn{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;margin-left:auto;border:none;border-radius:var(--border-radius-md);background:var(--color-primary-fill);color:var(--color-on-primary);cursor:pointer;flex-shrink:0;transition:background .15s,opacity .15s}.lc-chat__send-btn:hover:not(:disabled){background:var(--color-primary-fill-hover)}.lc-chat__send-btn:disabled{opacity:.4;cursor:not-allowed}.lc-chat__file-input{display:none}.lc-chat__attach-btn{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:none;border-radius:var(--border-radius-md);background:transparent;color:var(--color-text-tertiary);cursor:pointer;flex-shrink:0;transition:background .15s,color .15s}.lc-chat__attach-btn:hover:not(:disabled){background:var(--color-surface-hover);color:var(--color-text-primary)}.lc-chat__attach-btn:disabled{opacity:.4;cursor:not-allowed}.lc-chat__pending-attachments{display:flex;flex-wrap:wrap;gap:.375rem}.lc-chat__pending-attachment{display:inline-flex;align-items:center;gap:.375rem;padding:.25rem .375rem .25rem .5rem;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--border-radius-md, .5rem);font-size:var(--font-size-xs, .75rem);color:var(--color-text-primary);max-width:16rem}.lc-chat__pending-thumb{width:1.75rem;height:1.75rem;object-fit:cover;border-radius:.25rem;flex-shrink:0}.lc-chat__pending-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:9rem}.lc-chat__pending-size{color:var(--color-text-tertiary);font-variant-numeric:tabular-nums}.lc-chat__pending-remove{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;border:none;border-radius:50%;background:transparent;color:var(--color-text-tertiary);cursor:pointer;flex-shrink:0}.lc-chat__pending-remove:hover{background:var(--color-surface-hover);color:var(--color-text-primary)}.lc-chat__attachments{display:flex;flex-wrap:wrap;gap:.375rem;margin-top:.5rem}.lc-chat__attachment{display:inline-flex;align-items:center;gap:.375rem;text-decoration:none;color:inherit;font-size:var(--font-size-xs, .75rem)}.lc-chat__attachment--file{padding:.25rem .5rem;background:color-mix(in srgb,currentColor 8%,transparent);border-radius:var(--border-radius-md, .375rem);max-width:16rem}.lc-chat__attachment--file:hover{background:color-mix(in srgb,currentColor 16%,transparent)}.lc-chat__attachment--image img{max-width:12rem;max-height:8rem;border-radius:var(--border-radius-md, .375rem);object-fit:cover;display:block}.lc-chat__attachment-icon{flex-shrink:0}.lc-chat__attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-chat__attachment-size{opacity:.7;font-variant-numeric:tabular-nums}\n"] }]
|
|
11499
11804
|
}], propDecorators: { messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], isStreaming: [{ type: i0.Input, args: [{ isSignal: true, alias: "isStreaming", required: false }] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], bordered: [{ type: i0.Input, args: [{ isSignal: true, alias: "bordered", required: false }] }], messageAnchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "messageAnchor", required: false }] }], contentWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentWidth", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showAvatars: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAvatars", required: false }] }], showTimestamps: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTimestamps", required: false }] }], renderMarkdown: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderMarkdown", required: false }] }], allowFileUpload: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFileUpload", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], maxFileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFileSize", required: false }] }], messageSend: [{ type: i0.Output, args: ["messageSend"] }], fileAttach: [{ type: i0.Output, args: ["fileAttach"] }], messageTemplate: [{
|
|
11500
11805
|
type: ContentChild,
|
|
11501
11806
|
args: ['messageTemplate']
|