@eduboxpro/studio 0.1.16 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/eduboxpro-studio.mjs +448 -2
- package/fesm2022/eduboxpro-studio.mjs.map +1 -1
- package/index.d.ts +279 -2
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, signal, effect, Injectable, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, input, computed, ChangeDetectionStrategy, Component, output, PLATFORM_ID, ElementRef, contentChild, viewChild, forwardRef, DOCUMENT as DOCUMENT$1, DestroyRef, Injector, model, afterNextRender, HostListener,
|
|
2
|
+
import { InjectionToken, inject, signal, effect, Injectable, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, input, computed, ChangeDetectionStrategy, Component, output, PLATFORM_ID, ElementRef, contentChild, viewChild, forwardRef, DOCUMENT as DOCUMENT$1, DestroyRef, Injector, model, afterNextRender, HostListener, TemplateRef, ContentChild, Input, Directive, contentChildren, Renderer2 } from '@angular/core';
|
|
3
3
|
import * as i1$1 from '@angular/common';
|
|
4
4
|
import { DOCUMENT, CommonModule, isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
|
|
5
5
|
import * as LucideIcons from 'lucide-angular';
|
|
@@ -5029,6 +5029,452 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
5029
5029
|
* Switch component
|
|
5030
5030
|
*/
|
|
5031
5031
|
|
|
5032
|
+
/**
|
|
5033
|
+
* Directive for declarative column definition
|
|
5034
|
+
*
|
|
5035
|
+
* @example
|
|
5036
|
+
* <studio-table-column
|
|
5037
|
+
* key="name"
|
|
5038
|
+
* label="Name"
|
|
5039
|
+
* [sortable]="true">
|
|
5040
|
+
* <ng-template let-row>{{ row.name }}</ng-template>
|
|
5041
|
+
* </studio-table-column>
|
|
5042
|
+
*/
|
|
5043
|
+
class TableColumnDirective {
|
|
5044
|
+
key;
|
|
5045
|
+
label;
|
|
5046
|
+
field;
|
|
5047
|
+
width;
|
|
5048
|
+
minWidth;
|
|
5049
|
+
maxWidth;
|
|
5050
|
+
sortable;
|
|
5051
|
+
sortFn;
|
|
5052
|
+
filterable;
|
|
5053
|
+
align;
|
|
5054
|
+
fixed;
|
|
5055
|
+
hideOnMobile;
|
|
5056
|
+
priority;
|
|
5057
|
+
cellClass;
|
|
5058
|
+
headerClass;
|
|
5059
|
+
formatter;
|
|
5060
|
+
resizable;
|
|
5061
|
+
template;
|
|
5062
|
+
/**
|
|
5063
|
+
* Convert directive inputs to TableColumn config
|
|
5064
|
+
*/
|
|
5065
|
+
toColumnConfig() {
|
|
5066
|
+
return {
|
|
5067
|
+
key: this.key,
|
|
5068
|
+
label: this.label,
|
|
5069
|
+
field: this.field,
|
|
5070
|
+
width: this.width,
|
|
5071
|
+
minWidth: this.minWidth,
|
|
5072
|
+
maxWidth: this.maxWidth,
|
|
5073
|
+
sortable: this.sortable,
|
|
5074
|
+
sortFn: this.sortFn,
|
|
5075
|
+
filterable: this.filterable,
|
|
5076
|
+
align: this.align,
|
|
5077
|
+
fixed: this.fixed,
|
|
5078
|
+
hideOnMobile: this.hideOnMobile,
|
|
5079
|
+
priority: this.priority,
|
|
5080
|
+
cellTemplate: this.template,
|
|
5081
|
+
cellClass: this.cellClass,
|
|
5082
|
+
headerClass: this.headerClass,
|
|
5083
|
+
formatter: this.formatter,
|
|
5084
|
+
resizable: this.resizable
|
|
5085
|
+
};
|
|
5086
|
+
}
|
|
5087
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: TableColumnDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
5088
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.12", type: TableColumnDirective, isStandalone: true, selector: "studio-table-column", inputs: { key: "key", label: "label", field: "field", width: "width", minWidth: "minWidth", maxWidth: "maxWidth", sortable: "sortable", sortFn: "sortFn", filterable: "filterable", align: "align", fixed: "fixed", hideOnMobile: "hideOnMobile", priority: "priority", cellClass: "cellClass", headerClass: "headerClass", formatter: "formatter", resizable: "resizable" }, queries: [{ propertyName: "template", first: true, predicate: TemplateRef, descendants: true }], ngImport: i0 });
|
|
5089
|
+
}
|
|
5090
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: TableColumnDirective, decorators: [{
|
|
5091
|
+
type: Directive,
|
|
5092
|
+
args: [{
|
|
5093
|
+
selector: 'studio-table-column',
|
|
5094
|
+
standalone: true
|
|
5095
|
+
}]
|
|
5096
|
+
}], propDecorators: { key: [{
|
|
5097
|
+
type: Input,
|
|
5098
|
+
args: [{ required: true }]
|
|
5099
|
+
}], label: [{
|
|
5100
|
+
type: Input,
|
|
5101
|
+
args: [{ required: true }]
|
|
5102
|
+
}], field: [{
|
|
5103
|
+
type: Input
|
|
5104
|
+
}], width: [{
|
|
5105
|
+
type: Input
|
|
5106
|
+
}], minWidth: [{
|
|
5107
|
+
type: Input
|
|
5108
|
+
}], maxWidth: [{
|
|
5109
|
+
type: Input
|
|
5110
|
+
}], sortable: [{
|
|
5111
|
+
type: Input
|
|
5112
|
+
}], sortFn: [{
|
|
5113
|
+
type: Input
|
|
5114
|
+
}], filterable: [{
|
|
5115
|
+
type: Input
|
|
5116
|
+
}], align: [{
|
|
5117
|
+
type: Input
|
|
5118
|
+
}], fixed: [{
|
|
5119
|
+
type: Input
|
|
5120
|
+
}], hideOnMobile: [{
|
|
5121
|
+
type: Input
|
|
5122
|
+
}], priority: [{
|
|
5123
|
+
type: Input
|
|
5124
|
+
}], cellClass: [{
|
|
5125
|
+
type: Input
|
|
5126
|
+
}], headerClass: [{
|
|
5127
|
+
type: Input
|
|
5128
|
+
}], formatter: [{
|
|
5129
|
+
type: Input
|
|
5130
|
+
}], resizable: [{
|
|
5131
|
+
type: Input
|
|
5132
|
+
}], template: [{
|
|
5133
|
+
type: ContentChild,
|
|
5134
|
+
args: [TemplateRef]
|
|
5135
|
+
}] } });
|
|
5136
|
+
|
|
5137
|
+
/**
|
|
5138
|
+
* Enterprise-grade Table component with full feature set
|
|
5139
|
+
*
|
|
5140
|
+
* @example
|
|
5141
|
+
* <studio-table
|
|
5142
|
+
* [data]="users"
|
|
5143
|
+
* [columns]="columns"
|
|
5144
|
+
* selectionMode="multiple"
|
|
5145
|
+
* [(selected)]="selectedUsers">
|
|
5146
|
+
* </studio-table>
|
|
5147
|
+
*/
|
|
5148
|
+
class TableComponent {
|
|
5149
|
+
configService = inject(StudioConfigService);
|
|
5150
|
+
tableDefaults = computed(() => this.configService.config().components?.table, ...(ngDevMode ? [{ debugName: "tableDefaults" }] : []));
|
|
5151
|
+
// Config inputs with defaults
|
|
5152
|
+
variantInput = input(undefined, ...(ngDevMode ? [{ debugName: "variantInput", alias: 'variant' }] : [{ alias: 'variant' }]));
|
|
5153
|
+
densityInput = input(undefined, ...(ngDevMode ? [{ debugName: "densityInput", alias: 'density' }] : [{ alias: 'density' }]));
|
|
5154
|
+
variant = withConfigDefault(this.variantInput, computed(() => this.tableDefaults()?.variant), 'default');
|
|
5155
|
+
density = withConfigDefault(this.densityInput, computed(() => this.tableDefaults()?.density), 'comfortable');
|
|
5156
|
+
// Data inputs
|
|
5157
|
+
data = input([], ...(ngDevMode ? [{ debugName: "data" }] : []));
|
|
5158
|
+
columns = input([], ...(ngDevMode ? [{ debugName: "columns" }] : []));
|
|
5159
|
+
rowKey = input('id', ...(ngDevMode ? [{ debugName: "rowKey" }] : []));
|
|
5160
|
+
// Column directives (declarative mode)
|
|
5161
|
+
columnDirectives = contentChildren(TableColumnDirective, ...(ngDevMode ? [{ debugName: "columnDirectives" }] : []));
|
|
5162
|
+
// Features
|
|
5163
|
+
selectionMode = input('none', ...(ngDevMode ? [{ debugName: "selectionMode" }] : []));
|
|
5164
|
+
sortable = input(true, ...(ngDevMode ? [{ debugName: "sortable" }] : []));
|
|
5165
|
+
sortMode = input('client', ...(ngDevMode ? [{ debugName: "sortMode" }] : []));
|
|
5166
|
+
hoverable = input(true, ...(ngDevMode ? [{ debugName: "hoverable" }] : []));
|
|
5167
|
+
stickyHeader = input(false, ...(ngDevMode ? [{ debugName: "stickyHeader" }] : []));
|
|
5168
|
+
showHeader = input(true, ...(ngDevMode ? [{ debugName: "showHeader" }] : []));
|
|
5169
|
+
// Row expansion
|
|
5170
|
+
expandable = input(false, ...(ngDevMode ? [{ debugName: "expandable" }] : []));
|
|
5171
|
+
expandedRowTemplate = contentChild('expandedRow', ...(ngDevMode ? [{ debugName: "expandedRowTemplate" }] : []));
|
|
5172
|
+
expandMultiple = input(false, ...(ngDevMode ? [{ debugName: "expandMultiple" }] : []));
|
|
5173
|
+
// Loading & Empty states
|
|
5174
|
+
loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
5175
|
+
loadingRows = input(5, ...(ngDevMode ? [{ debugName: "loadingRows" }] : []));
|
|
5176
|
+
emptyState = input({
|
|
5177
|
+
icon: 'inbox',
|
|
5178
|
+
title: 'No data',
|
|
5179
|
+
message: 'No data available to display'
|
|
5180
|
+
}, ...(ngDevMode ? [{ debugName: "emptyState" }] : []));
|
|
5181
|
+
// Row actions
|
|
5182
|
+
rowActions = input([], ...(ngDevMode ? [{ debugName: "rowActions" }] : []));
|
|
5183
|
+
// Responsive
|
|
5184
|
+
responsive = input(true, ...(ngDevMode ? [{ debugName: "responsive" }] : []));
|
|
5185
|
+
mobileBreakpoint = input(768, ...(ngDevMode ? [{ debugName: "mobileBreakpoint" }] : []));
|
|
5186
|
+
// State models
|
|
5187
|
+
selected = model([], ...(ngDevMode ? [{ debugName: "selected" }] : []));
|
|
5188
|
+
sort = model(null, ...(ngDevMode ? [{ debugName: "sort" }] : []));
|
|
5189
|
+
expanded = model(new Set(), ...(ngDevMode ? [{ debugName: "expanded" }] : []));
|
|
5190
|
+
// Outputs
|
|
5191
|
+
sortChange = output();
|
|
5192
|
+
selectionChange = output();
|
|
5193
|
+
rowClick = output();
|
|
5194
|
+
rowDblClick = output();
|
|
5195
|
+
// Internal state
|
|
5196
|
+
currentSort = signal(null, ...(ngDevMode ? [{ debugName: "currentSort" }] : []));
|
|
5197
|
+
selectedRows = signal(new Set(), ...(ngDevMode ? [{ debugName: "selectedRows" }] : []));
|
|
5198
|
+
expandedRows = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedRows" }] : []));
|
|
5199
|
+
// Computed columns (merge programmatic + declarative)
|
|
5200
|
+
finalColumns = computed(() => {
|
|
5201
|
+
const programmatic = this.columns();
|
|
5202
|
+
const declarative = this.columnDirectives().map(dir => dir.toColumnConfig());
|
|
5203
|
+
return declarative.length > 0 ? declarative : programmatic;
|
|
5204
|
+
}, ...(ngDevMode ? [{ debugName: "finalColumns" }] : []));
|
|
5205
|
+
// Computed data (sorted, filtered)
|
|
5206
|
+
processedData = computed(() => {
|
|
5207
|
+
let result = [...this.data()];
|
|
5208
|
+
// Apply sorting (client-side only)
|
|
5209
|
+
if (this.sortMode() === 'client' && this.currentSort()) {
|
|
5210
|
+
result = this.applySorting(result);
|
|
5211
|
+
}
|
|
5212
|
+
return result;
|
|
5213
|
+
}, ...(ngDevMode ? [{ debugName: "processedData" }] : []));
|
|
5214
|
+
// Selection helpers
|
|
5215
|
+
allSelected = computed(() => {
|
|
5216
|
+
const data = this.processedData();
|
|
5217
|
+
const selected = this.selectedRows();
|
|
5218
|
+
return data.length > 0 && data.every(row => selected.has(this.getRowKey(row)));
|
|
5219
|
+
}, ...(ngDevMode ? [{ debugName: "allSelected" }] : []));
|
|
5220
|
+
someSelected = computed(() => {
|
|
5221
|
+
const data = this.processedData();
|
|
5222
|
+
const selected = this.selectedRows();
|
|
5223
|
+
return data.some(row => selected.has(this.getRowKey(row))) && !this.allSelected();
|
|
5224
|
+
}, ...(ngDevMode ? [{ debugName: "someSelected" }] : []));
|
|
5225
|
+
hostClasses = computed(() => classNames('studio-table', `studio-table--${this.variant()}`, `studio-table--${this.density()}`, this.hoverable() && 'studio-table--hoverable', this.stickyHeader() && 'studio-table--sticky-header', this.loading() && 'studio-table--loading', this.responsive() && 'studio-table--responsive'), ...(ngDevMode ? [{ debugName: "hostClasses" }] : []));
|
|
5226
|
+
constructor() {
|
|
5227
|
+
// Sync model signals
|
|
5228
|
+
effect(() => {
|
|
5229
|
+
const sortVal = this.sort();
|
|
5230
|
+
if (sortVal)
|
|
5231
|
+
this.currentSort.set(sortVal);
|
|
5232
|
+
});
|
|
5233
|
+
effect(() => {
|
|
5234
|
+
const current = this.currentSort();
|
|
5235
|
+
if (current)
|
|
5236
|
+
this.sort.set(current);
|
|
5237
|
+
});
|
|
5238
|
+
effect(() => {
|
|
5239
|
+
const selected = this.selected();
|
|
5240
|
+
const keys = new Set(selected.map(row => this.getRowKey(row)));
|
|
5241
|
+
this.selectedRows.set(keys);
|
|
5242
|
+
});
|
|
5243
|
+
effect(() => {
|
|
5244
|
+
const expanded = this.expanded();
|
|
5245
|
+
this.expandedRows.set(expanded);
|
|
5246
|
+
});
|
|
5247
|
+
}
|
|
5248
|
+
// Public API
|
|
5249
|
+
sortBy(column, direction) {
|
|
5250
|
+
if (!column.sortable && !this.sortable())
|
|
5251
|
+
return;
|
|
5252
|
+
const currentSort = this.currentSort();
|
|
5253
|
+
const prevDirection = currentSort?.column === column.key ? currentSort.direction : null;
|
|
5254
|
+
// Toggle: asc -> desc -> null
|
|
5255
|
+
let newDirection = direction || 'asc';
|
|
5256
|
+
if (!direction) {
|
|
5257
|
+
if (prevDirection === 'asc')
|
|
5258
|
+
newDirection = 'desc';
|
|
5259
|
+
else if (prevDirection === 'desc')
|
|
5260
|
+
newDirection = null;
|
|
5261
|
+
}
|
|
5262
|
+
if (newDirection) {
|
|
5263
|
+
this.currentSort.set({ column: column.key, direction: newDirection });
|
|
5264
|
+
this.sortChange.emit({
|
|
5265
|
+
column,
|
|
5266
|
+
direction: newDirection,
|
|
5267
|
+
previousDirection: prevDirection
|
|
5268
|
+
});
|
|
5269
|
+
}
|
|
5270
|
+
else {
|
|
5271
|
+
this.currentSort.set(null);
|
|
5272
|
+
}
|
|
5273
|
+
}
|
|
5274
|
+
selectRow(row) {
|
|
5275
|
+
if (this.selectionMode() === 'none')
|
|
5276
|
+
return;
|
|
5277
|
+
const rowKey = this.getRowKey(row);
|
|
5278
|
+
const selected = new Set(this.selectedRows());
|
|
5279
|
+
if (this.selectionMode() === 'single') {
|
|
5280
|
+
selected.clear();
|
|
5281
|
+
selected.add(rowKey);
|
|
5282
|
+
}
|
|
5283
|
+
else {
|
|
5284
|
+
if (selected.has(rowKey)) {
|
|
5285
|
+
selected.delete(rowKey);
|
|
5286
|
+
}
|
|
5287
|
+
else {
|
|
5288
|
+
selected.add(rowKey);
|
|
5289
|
+
}
|
|
5290
|
+
}
|
|
5291
|
+
this.selectedRows.set(selected);
|
|
5292
|
+
this.updateSelectedModel();
|
|
5293
|
+
this.selectionChange.emit({
|
|
5294
|
+
selected: this.getSelectedRows(),
|
|
5295
|
+
row,
|
|
5296
|
+
isSelected: selected.has(rowKey)
|
|
5297
|
+
});
|
|
5298
|
+
}
|
|
5299
|
+
selectAll(select) {
|
|
5300
|
+
if (this.selectionMode() !== 'multiple')
|
|
5301
|
+
return;
|
|
5302
|
+
const selected = new Set();
|
|
5303
|
+
if (select) {
|
|
5304
|
+
this.processedData().forEach(row => {
|
|
5305
|
+
selected.add(this.getRowKey(row));
|
|
5306
|
+
});
|
|
5307
|
+
}
|
|
5308
|
+
this.selectedRows.set(selected);
|
|
5309
|
+
this.updateSelectedModel();
|
|
5310
|
+
this.selectionChange.emit({
|
|
5311
|
+
selected: this.getSelectedRows(),
|
|
5312
|
+
isSelected: select
|
|
5313
|
+
});
|
|
5314
|
+
}
|
|
5315
|
+
toggleRowExpansion(row) {
|
|
5316
|
+
if (!this.expandable())
|
|
5317
|
+
return;
|
|
5318
|
+
const rowKey = this.getRowKey(row);
|
|
5319
|
+
const expanded = new Set(this.expandedRows());
|
|
5320
|
+
if (!this.expandMultiple()) {
|
|
5321
|
+
expanded.clear();
|
|
5322
|
+
}
|
|
5323
|
+
if (expanded.has(rowKey)) {
|
|
5324
|
+
expanded.delete(rowKey);
|
|
5325
|
+
}
|
|
5326
|
+
else {
|
|
5327
|
+
expanded.add(rowKey);
|
|
5328
|
+
}
|
|
5329
|
+
this.expandedRows.set(expanded);
|
|
5330
|
+
this.expanded.set(expanded);
|
|
5331
|
+
}
|
|
5332
|
+
isRowExpanded(row) {
|
|
5333
|
+
return this.expandedRows().has(this.getRowKey(row));
|
|
5334
|
+
}
|
|
5335
|
+
isRowSelected(row) {
|
|
5336
|
+
return this.selectedRows().has(this.getRowKey(row));
|
|
5337
|
+
}
|
|
5338
|
+
getCellValue(row, column) {
|
|
5339
|
+
if (column.field) {
|
|
5340
|
+
if (typeof column.field === 'function') {
|
|
5341
|
+
return column.field(row);
|
|
5342
|
+
}
|
|
5343
|
+
return row[column.field];
|
|
5344
|
+
}
|
|
5345
|
+
return row[column.key];
|
|
5346
|
+
}
|
|
5347
|
+
getHeaderClass(column) {
|
|
5348
|
+
const classes = ['studio-table__th'];
|
|
5349
|
+
if (column.headerClass)
|
|
5350
|
+
classes.push(column.headerClass);
|
|
5351
|
+
if (column.sortable)
|
|
5352
|
+
classes.push('studio-table__th--sortable');
|
|
5353
|
+
if (this.currentSort()?.column === column.key)
|
|
5354
|
+
classes.push('studio-table__th--sorted');
|
|
5355
|
+
const align = column.align || 'left';
|
|
5356
|
+
classes.push(`studio-table__th--align-${align}`);
|
|
5357
|
+
if (column.fixed)
|
|
5358
|
+
classes.push(`studio-table__th--fixed-${column.fixed}`);
|
|
5359
|
+
if (column.hideOnMobile)
|
|
5360
|
+
classes.push('studio-table__th--hide-mobile');
|
|
5361
|
+
return classes.join(' ');
|
|
5362
|
+
}
|
|
5363
|
+
getCellClass(row, column) {
|
|
5364
|
+
if (typeof column.cellClass === 'function') {
|
|
5365
|
+
return column.cellClass(row);
|
|
5366
|
+
}
|
|
5367
|
+
return column.cellClass || '';
|
|
5368
|
+
}
|
|
5369
|
+
getFullCellClass(row, column) {
|
|
5370
|
+
const classes = ['studio-table__td'];
|
|
5371
|
+
const customClass = this.getCellClass(row, column);
|
|
5372
|
+
if (customClass)
|
|
5373
|
+
classes.push(customClass);
|
|
5374
|
+
const align = column.align || 'left';
|
|
5375
|
+
classes.push(`studio-table__td--align-${align}`);
|
|
5376
|
+
if (column.fixed)
|
|
5377
|
+
classes.push(`studio-table__td--fixed-${column.fixed}`);
|
|
5378
|
+
if (column.hideOnMobile)
|
|
5379
|
+
classes.push('studio-table__td--hide-mobile');
|
|
5380
|
+
return classes.join(' ');
|
|
5381
|
+
}
|
|
5382
|
+
getCellContext(row, column, index) {
|
|
5383
|
+
const value = this.getCellValue(row, column);
|
|
5384
|
+
return {
|
|
5385
|
+
$implicit: value,
|
|
5386
|
+
row,
|
|
5387
|
+
column,
|
|
5388
|
+
index
|
|
5389
|
+
};
|
|
5390
|
+
}
|
|
5391
|
+
getHeaderContext(column) {
|
|
5392
|
+
const currentSort = this.currentSort();
|
|
5393
|
+
return {
|
|
5394
|
+
column,
|
|
5395
|
+
sorted: currentSort?.column === column.key,
|
|
5396
|
+
direction: currentSort?.column === column.key ? currentSort.direction : null
|
|
5397
|
+
};
|
|
5398
|
+
}
|
|
5399
|
+
getSortIcon(column) {
|
|
5400
|
+
const currentSort = this.currentSort();
|
|
5401
|
+
if (currentSort?.column !== column.key)
|
|
5402
|
+
return 'arrow-up-down';
|
|
5403
|
+
return currentSort.direction === 'asc' ? 'arrow-up' : 'arrow-down';
|
|
5404
|
+
}
|
|
5405
|
+
handleRowClick(row, event) {
|
|
5406
|
+
if (event.target.closest('.studio-table__actions'))
|
|
5407
|
+
return;
|
|
5408
|
+
this.rowClick.emit(row);
|
|
5409
|
+
}
|
|
5410
|
+
handleRowDblClick(row) {
|
|
5411
|
+
this.rowDblClick.emit(row);
|
|
5412
|
+
}
|
|
5413
|
+
executeRowAction(action, row, event) {
|
|
5414
|
+
event.stopPropagation();
|
|
5415
|
+
if (action.disabled && action.disabled(row))
|
|
5416
|
+
return;
|
|
5417
|
+
action.handler(row);
|
|
5418
|
+
}
|
|
5419
|
+
isActionVisible(action, row) {
|
|
5420
|
+
return !action.visible || action.visible(row);
|
|
5421
|
+
}
|
|
5422
|
+
isActionDisabled(action, row) {
|
|
5423
|
+
return action.disabled ? action.disabled(row) : false;
|
|
5424
|
+
}
|
|
5425
|
+
// Public helpers for template
|
|
5426
|
+
getRowKey(row) {
|
|
5427
|
+
const keyFn = this.rowKey();
|
|
5428
|
+
if (typeof keyFn === 'function') {
|
|
5429
|
+
return keyFn(row);
|
|
5430
|
+
}
|
|
5431
|
+
return row[keyFn];
|
|
5432
|
+
}
|
|
5433
|
+
getSelectedRows() {
|
|
5434
|
+
const selected = this.selectedRows();
|
|
5435
|
+
return this.processedData().filter(row => selected.has(this.getRowKey(row)));
|
|
5436
|
+
}
|
|
5437
|
+
updateSelectedModel() {
|
|
5438
|
+
this.selected.set(this.getSelectedRows());
|
|
5439
|
+
}
|
|
5440
|
+
applySorting(data) {
|
|
5441
|
+
const sortConfig = this.currentSort();
|
|
5442
|
+
if (!sortConfig)
|
|
5443
|
+
return data;
|
|
5444
|
+
const column = this.finalColumns().find(col => col.key === sortConfig.column);
|
|
5445
|
+
if (!column)
|
|
5446
|
+
return data;
|
|
5447
|
+
return [...data].sort((a, b) => {
|
|
5448
|
+
if (column.sortFn) {
|
|
5449
|
+
return column.sortFn(a, b, sortConfig.direction);
|
|
5450
|
+
}
|
|
5451
|
+
const aVal = this.getCellValue(a, column);
|
|
5452
|
+
const bVal = this.getCellValue(b, column);
|
|
5453
|
+
if (aVal === bVal)
|
|
5454
|
+
return 0;
|
|
5455
|
+
if (aVal == null)
|
|
5456
|
+
return 1;
|
|
5457
|
+
if (bVal == null)
|
|
5458
|
+
return -1;
|
|
5459
|
+
const comparison = aVal < bVal ? -1 : 1;
|
|
5460
|
+
return sortConfig.direction === 'asc' ? comparison : -comparison;
|
|
5461
|
+
});
|
|
5462
|
+
}
|
|
5463
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5464
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: TableComponent, isStandalone: true, selector: "studio-table", inputs: { variantInput: { classPropertyName: "variantInput", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, densityInput: { classPropertyName: "densityInput", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rowKey: { classPropertyName: "rowKey", publicName: "rowKey", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, sortMode: { classPropertyName: "sortMode", publicName: "sortMode", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, stickyHeader: { classPropertyName: "stickyHeader", publicName: "stickyHeader", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, expandMultiple: { classPropertyName: "expandMultiple", publicName: "expandMultiple", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingRows: { classPropertyName: "loadingRows", publicName: "loadingRows", isSignal: true, isRequired: false, transformFunction: null }, emptyState: { classPropertyName: "emptyState", publicName: "emptyState", isSignal: true, isRequired: false, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, mobileBreakpoint: { classPropertyName: "mobileBreakpoint", publicName: "mobileBreakpoint", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selectedChange", sort: "sortChange", expanded: "expandedChange", sortChange: "sortChange", selectionChange: "selectionChange", rowClick: "rowClick", rowDblClick: "rowDblClick" }, host: { properties: { "class": "hostClasses()" } }, queries: [{ propertyName: "columnDirectives", predicate: TableColumnDirective, isSignal: true }, { propertyName: "expandedRowTemplate", first: true, predicate: ["expandedRow"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"studio-table__wrapper\">\n <!-- Loading Overlay -->\n @if (loading()) {\n <div class=\"studio-table__loading-overlay\">\n <studio-icon name=\"loader-2\" [size]=\"32\" class=\"studio-table__spinner\" />\n </div>\n }\n\n <!-- Table Container -->\n <div class=\"studio-table__container\">\n <table class=\"studio-table__table\">\n <!-- Header -->\n @if (showHeader()) {\n <thead class=\"studio-table__thead\">\n <tr class=\"studio-table__header-row\">\n <!-- Selection Column -->\n @if (selectionMode() === 'multiple') {\n <th class=\"studio-table__th studio-table__th--selection\">\n <studio-checkbox\n [value]=\"allSelected()\"\n [indeterminate]=\"someSelected()\"\n (changed)=\"selectAll($event)\"\n />\n </th>\n }\n\n <!-- Expansion Column -->\n @if (expandable()) {\n <th class=\"studio-table__th studio-table__th--expand\"></th>\n }\n\n <!-- Data Columns -->\n @for (column of finalColumns(); track column.key) {\n <th\n [class]=\"getHeaderClass(column)\"\n [style.width]=\"column.width\"\n [style.min-width]=\"column.minWidth\"\n [style.max-width]=\"column.maxWidth\"\n (click)=\"column.sortable && sortBy(column)\">\n\n <div class=\"studio-table__header-content\">\n @if (column.headerTemplate) {\n <ng-container *ngTemplateOutlet=\"column.headerTemplate; context: getHeaderContext(column)\" />\n } @else {\n <span>{{ column.label }}</span>\n @if (column.sortable && sortable()) {\n <studio-icon\n [name]=\"getSortIcon(column)\"\n [size]=\"16\"\n class=\"studio-table__sort-icon\"\n />\n }\n }\n </div>\n </th>\n }\n\n <!-- Actions Column -->\n @if (rowActions().length > 0) {\n <th class=\"studio-table__th studio-table__th--actions\">Actions</th>\n }\n </tr>\n </thead>\n }\n\n <!-- Body -->\n <tbody class=\"studio-table__tbody\">\n <!-- Loading Skeleton -->\n @if (loading()) {\n @for (i of [].constructor(loadingRows()); track i) {\n <tr class=\"studio-table__row studio-table__row--loading\">\n @if (selectionMode() === 'multiple') {\n <td class=\"studio-table__td\">\n <div class=\"studio-table__skeleton studio-table__skeleton--checkbox\"></div>\n </td>\n }\n @if (expandable()) {\n <td class=\"studio-table__td\">\n <div class=\"studio-table__skeleton studio-table__skeleton--icon\"></div>\n </td>\n }\n @for (column of finalColumns(); track column.key) {\n <td class=\"studio-table__td\" [class.studio-table__th--hide-mobile]=\"column.hideOnMobile\">\n <div class=\"studio-table__skeleton\"></div>\n </td>\n }\n @if (rowActions().length > 0) {\n <td class=\"studio-table__td\">\n <div class=\"studio-table__skeleton studio-table__skeleton--icon\"></div>\n </td>\n }\n </tr>\n }\n }\n\n <!-- Empty State -->\n @if (!loading() && processedData().length === 0) {\n <tr class=\"studio-table__row studio-table__row--empty\">\n <td [attr.colspan]=\"finalColumns().length + (selectionMode() === 'multiple' ? 1 : 0) + (expandable() ? 1 : 0) + (rowActions().length > 0 ? 1 : 0)\" class=\"studio-table__td\">\n <div class=\"studio-table__empty\">\n <studio-icon [name]=\"emptyState().icon || 'inbox'\" [size]=\"48\" class=\"studio-table__empty-icon\" />\n <h3 class=\"studio-table__empty-title\">{{ emptyState().title }}</h3>\n <p class=\"studio-table__empty-message\">{{ emptyState().message }}</p>\n @if (emptyState().action) {\n <studio-button\n size=\"sm\"\n (clicked)=\"emptyState().action!.handler()\">\n {{ emptyState().action!.label }}\n </studio-button>\n }\n </div>\n </td>\n </tr>\n }\n\n <!-- Data Rows -->\n @if (!loading()) {\n @for (row of processedData(); track getRowKey(row); let rowIndex = $index) {\n <!-- Main Row -->\n <tr\n class=\"studio-table__row\"\n [class.studio-table__row--selected]=\"isRowSelected(row)\"\n [class.studio-table__row--expanded]=\"isRowExpanded(row)\"\n [class.studio-table__row--clickable]=\"selectionMode() === 'single'\"\n (click)=\"selectionMode() === 'single' && selectRow(row); handleRowClick(row, $event)\"\n (dblclick)=\"handleRowDblClick(row)\">\n\n <!-- Selection Cell -->\n @if (selectionMode() === 'multiple') {\n <td class=\"studio-table__td studio-table__td--selection\">\n <studio-checkbox\n [value]=\"isRowSelected(row)\"\n (changed)=\"selectRow(row)\"\n (click)=\"$event.stopPropagation()\"\n />\n </td>\n }\n\n <!-- Expansion Cell -->\n @if (expandable()) {\n <td class=\"studio-table__td studio-table__td--expand\">\n <studio-button\n variant=\"ghost\"\n size=\"sm\"\n iconPosition=\"only\"\n [icon]=\"isRowExpanded(row) ? 'chevron-down' : 'chevron-right'\"\n (clicked)=\"toggleRowExpansion(row); $event.stopPropagation()\"\n />\n </td>\n }\n\n <!-- Data Cells -->\n @for (column of finalColumns(); track column.key) {\n <td\n [class]=\"getFullCellClass(row, column)\"\n [attr.data-label]=\"column.label\">\n\n @if (column.cellTemplate) {\n <ng-container *ngTemplateOutlet=\"column.cellTemplate; context: getCellContext(row, column, rowIndex)\" />\n } @else {\n <span>{{ column.formatter ? column.formatter(getCellValue(row, column), row) : getCellValue(row, column) }}</span>\n }\n </td>\n }\n\n <!-- Actions Cell -->\n @if (rowActions().length > 0) {\n <td class=\"studio-table__td studio-table__td--actions\">\n <div class=\"studio-table__actions\">\n @for (action of rowActions(); track action.label) {\n @if (isActionVisible(action, row)) {\n @if (action.divider) {\n <div class=\"studio-table__action-divider\"></div>\n }\n <studio-button\n variant=\"ghost\"\n size=\"sm\"\n [icon]=\"action.icon\"\n [disabled]=\"isActionDisabled(action, row)\"\n [color]=\"action.variant === 'danger' ? 'error' : 'primary'\"\n (clicked)=\"executeRowAction(action, row, $event)\">\n {{ action.label }}\n </studio-button>\n }\n }\n </div>\n </td>\n }\n </tr>\n\n <!-- Expanded Row -->\n @if (expandable() && isRowExpanded(row) && expandedRowTemplate()) {\n <tr class=\"studio-table__row studio-table__row--expansion\">\n <td [attr.colspan]=\"finalColumns().length + (selectionMode() === 'multiple' ? 1 : 0) + (expandable() ? 1 : 0) + (rowActions().length > 0 ? 1 : 0)\" class=\"studio-table__td\">\n <div class=\"studio-table__expansion-content\">\n <ng-container *ngTemplateOutlet=\"expandedRowTemplate(); context: { $implicit: row, index: rowIndex }\" />\n </div>\n </td>\n </tr>\n }\n }\n }\n </tbody>\n </table>\n </div>\n</div>\n", styles: [":host{display:block;font-family:var(--studio-font-family);position:relative}.studio-table__wrapper{position:relative;width:100%;overflow:hidden}.studio-table__container{width:100%;overflow-x:auto;overflow-y:visible;-webkit-overflow-scrolling:touch}.studio-table__container::-webkit-scrollbar{height:8px}.studio-table__container::-webkit-scrollbar-track{background:var(--studio-bg-secondary);border-radius:var(--studio-radius-sm)}.studio-table__container::-webkit-scrollbar-thumb{background:var(--studio-border-primary);border-radius:var(--studio-radius-sm)}.studio-table__container::-webkit-scrollbar-thumb:hover{background:var(--studio-text-tertiary)}.studio-table__table{width:100%;border-collapse:separate;border-spacing:0;background:var(--studio-bg-primary)}.studio-table__thead{background:var(--studio-bg-secondary)}.studio-table__header-row{border-bottom:1px solid var(--studio-border-primary)}.studio-table__th{padding:.75rem 1rem;text-align:left;font-weight:var(--studio-font-weight-semibold);font-size:var(--studio-font-size-sm);color:var(--studio-text-primary);white-space:nowrap;-webkit-user-select:none;user-select:none;position:relative}.studio-table__th--sortable{cursor:pointer;transition:background-color var(--studio-transition-fast)}.studio-table__th--sortable:hover{background:var(--studio-bg-tertiary)}.studio-table__th--sorted{color:var(--studio-primary)}.studio-table__th--selection,.studio-table__th--expand{width:48px;padding:.75rem .5rem}.studio-table__th--actions{width:auto;min-width:100px}.studio-table__th--align-center{text-align:center}.studio-table__th--align-right{text-align:right}.studio-table__th--fixed-left{position:sticky;left:0;z-index:2;background:var(--studio-bg-secondary);box-shadow:2px 0 4px #0000000d}.studio-table__th--fixed-right{position:sticky;right:0;z-index:2;background:var(--studio-bg-secondary);box-shadow:-2px 0 4px #0000000d}.studio-table__header-content{display:flex;align-items:center;gap:.5rem}.studio-table__sort-icon{flex-shrink:0;color:var(--studio-text-tertiary);transition:color var(--studio-transition-fast)}.studio-table__th--sorted .studio-table__sort-icon{color:var(--studio-primary)}.studio-table__row{border-bottom:1px solid var(--studio-border-primary);transition:background-color var(--studio-transition-fast)}.studio-table__row--clickable{cursor:pointer}.studio-table__row--selected{background:var(--studio-primary-bg)}.studio-table__row--expansion{background:var(--studio-bg-secondary)}.studio-table__row--loading{pointer-events:none}.studio-table__td{padding:.75rem 1rem;font-size:var(--studio-font-size-base);color:var(--studio-text-primary);vertical-align:middle}.studio-table__td--selection,.studio-table__td--expand{width:48px;padding:.75rem .5rem}.studio-table__td--actions{width:auto}.studio-table__td--align-center{text-align:center}.studio-table__td--align-right{text-align:right}.studio-table__td--fixed-left{position:sticky;left:0;z-index:1;background:var(--studio-bg-primary);box-shadow:2px 0 4px #0000000d}.studio-table__row--selected .studio-table__td--fixed-left{background:var(--studio-primary-bg)}.studio-table__td--fixed-right{position:sticky;right:0;z-index:1;background:var(--studio-bg-primary);box-shadow:-2px 0 4px #0000000d}.studio-table__row--selected .studio-table__td--fixed-right{background:var(--studio-primary-bg)}:host(.studio-table--compact) .studio-table__th,:host(.studio-table--compact) .studio-table__td{padding:.5rem .75rem}:host(.studio-table--compact) .studio-table__th{font-size:.813rem}:host(.studio-table--compact) .studio-table__td{font-size:.875rem}:host(.studio-table--spacious) .studio-table__th,:host(.studio-table--spacious) .studio-table__td{padding:1rem 1.5rem}:host(.studio-table--spacious) .studio-table__th{font-size:var(--studio-font-size-base)}:host(.studio-table--spacious) .studio-table__td{font-size:var(--studio-font-size-lg)}:host(.studio-table--striped) .studio-table__row:nth-child(2n){background:var(--studio-bg-secondary)}:host(.studio-table--bordered) .studio-table__table{border:1px solid var(--studio-border-primary);border-radius:var(--studio-radius-md)}:host(.studio-table--bordered) .studio-table__th,:host(.studio-table--bordered) .studio-table__td{border-right:1px solid var(--studio-border-primary)}:host(.studio-table--bordered) .studio-table__th:last-child,:host(.studio-table--bordered) .studio-table__td:last-child{border-right:none}:host(.studio-table--minimal) .studio-table__thead{background:transparent}:host(.studio-table--minimal) .studio-table__header-row{border-bottom:2px solid var(--studio-border-primary)}:host(.studio-table--minimal) .studio-table__row{border-bottom-style:dashed}:host(.studio-table--hoverable) .studio-table__row:not(.studio-table__row--empty):not(.studio-table__row--loading):not(.studio-table__row--expansion):hover{background:var(--studio-bg-secondary)}:host(.studio-table--hoverable) .studio-table__row--selected:hover{background:var(--studio-primary-bg);filter:brightness(.98)}:host(.studio-table--sticky-header) .studio-table__thead{position:sticky;top:0;z-index:3;box-shadow:0 2px 4px #0000000d}.studio-table__actions{display:flex;align-items:center;gap:.25rem;flex-wrap:wrap}.studio-table__action-divider{width:1px;height:1.5rem;background:var(--studio-border-primary);margin:0 .25rem}.studio-table__expansion-content{padding:1rem;background:var(--studio-bg-tertiary);border-radius:var(--studio-radius-sm)}.studio-table__loading-overlay{position:absolute;inset:0;background:#fffc;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:10}.studio-table__spinner{animation:spin 1s linear infinite;color:var(--studio-primary)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.studio-table__skeleton{height:1rem;background:linear-gradient(90deg,var(--studio-bg-secondary) 0%,var(--studio-bg-tertiary) 50%,var(--studio-bg-secondary) 100%);background-size:200% 100%;animation:skeleton-loading 1.5s ease-in-out infinite;border-radius:var(--studio-radius-sm)}.studio-table__skeleton--checkbox{width:1.25rem;height:1.25rem}.studio-table__skeleton--icon{width:2rem;height:1rem}@keyframes skeleton-loading{0%{background-position:200% 0}to{background-position:-200% 0}}.studio-table__empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem 1rem;text-align:center}.studio-table__empty-icon{color:var(--studio-text-tertiary);margin-bottom:1rem}.studio-table__empty-title{margin:0 0 .5rem;font-size:var(--studio-font-size-lg);font-weight:var(--studio-font-weight-semibold);color:var(--studio-text-primary)}.studio-table__empty-message{margin:0 0 1.5rem;font-size:var(--studio-font-size-base);color:var(--studio-text-secondary)}@media (max-width: 768px){:host(.studio-table--responsive) .studio-table__container{overflow-x:visible}:host(.studio-table--responsive) .studio-table__table{display:block}:host(.studio-table--responsive) .studio-table__thead{display:none}:host(.studio-table--responsive) .studio-table__tbody{display:block}:host(.studio-table--responsive) .studio-table__row{display:block;margin-bottom:1rem;border:1px solid var(--studio-border-primary);border-radius:var(--studio-radius-md);box-shadow:var(--studio-shadow-sm);overflow:hidden}:host(.studio-table--responsive) .studio-table__row--empty,:host(.studio-table--responsive) .studio-table__row--loading{display:table-row}:host(.studio-table--responsive) .studio-table__row--expansion{margin-top:-1rem;border-top:none;border-top-left-radius:0;border-top-right-radius:0}:host(.studio-table--responsive) .studio-table__td{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;border-bottom:1px solid var(--studio-border-primary)}:host(.studio-table--responsive) .studio-table__td:last-child{border-bottom:none}:host(.studio-table--responsive) .studio-table__td:before{content:attr(data-label);font-weight:var(--studio-font-weight-semibold);color:var(--studio-text-secondary);min-width:100px;flex-shrink:0}:host(.studio-table--responsive) .studio-table__td--selection,:host(.studio-table--responsive) .studio-table__td--expand{display:flex;justify-content:center;padding:.5rem 1rem}:host(.studio-table--responsive) .studio-table__td--selection:before,:host(.studio-table--responsive) .studio-table__td--expand:before{display:none}:host(.studio-table--responsive) .studio-table__td--actions{display:block;padding:.75rem 1rem}:host(.studio-table--responsive) .studio-table__td--actions:before{display:block;margin-bottom:.5rem}:host(.studio-table--responsive) .studio-table__td--actions .studio-table__actions{justify-content:flex-start}:host(.studio-table--responsive) .studio-table__td--hide-mobile{display:none!important}:host(.studio-table--responsive) .studio-table__expansion-content{padding:.75rem 1rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconComponent, selector: "studio-icon", inputs: ["name", "size", "color", "strokeWidth", "absoluteStrokeWidth", "showFallback", "fallbackIcon"] }, { kind: "component", type: CheckboxComponent, selector: "studio-checkbox", inputs: ["size", "color", "variant", "radius", "label", "labelPosition", "description", "hint", "required", "error", "errorMessage", "disabled", "readonly", "indeterminate", "name", "tabIndex", "value"], outputs: ["changed"] }, { kind: "component", type: ButtonComponent, selector: "studio-button", inputs: ["variant", "size", "color", "radius", "shadow", "compact", "disabled", "loading", "loadingText", "fullWidth", "type", "icon", "iconPosition", "href", "target", "badge", "badgeColor", "ariaLabel"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5465
|
+
}
|
|
5466
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: TableComponent, decorators: [{
|
|
5467
|
+
type: Component,
|
|
5468
|
+
args: [{ selector: 'studio-table', standalone: true, imports: [CommonModule, IconComponent, CheckboxComponent, ButtonComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
5469
|
+
'[class]': 'hostClasses()',
|
|
5470
|
+
}, template: "<div class=\"studio-table__wrapper\">\n <!-- Loading Overlay -->\n @if (loading()) {\n <div class=\"studio-table__loading-overlay\">\n <studio-icon name=\"loader-2\" [size]=\"32\" class=\"studio-table__spinner\" />\n </div>\n }\n\n <!-- Table Container -->\n <div class=\"studio-table__container\">\n <table class=\"studio-table__table\">\n <!-- Header -->\n @if (showHeader()) {\n <thead class=\"studio-table__thead\">\n <tr class=\"studio-table__header-row\">\n <!-- Selection Column -->\n @if (selectionMode() === 'multiple') {\n <th class=\"studio-table__th studio-table__th--selection\">\n <studio-checkbox\n [value]=\"allSelected()\"\n [indeterminate]=\"someSelected()\"\n (changed)=\"selectAll($event)\"\n />\n </th>\n }\n\n <!-- Expansion Column -->\n @if (expandable()) {\n <th class=\"studio-table__th studio-table__th--expand\"></th>\n }\n\n <!-- Data Columns -->\n @for (column of finalColumns(); track column.key) {\n <th\n [class]=\"getHeaderClass(column)\"\n [style.width]=\"column.width\"\n [style.min-width]=\"column.minWidth\"\n [style.max-width]=\"column.maxWidth\"\n (click)=\"column.sortable && sortBy(column)\">\n\n <div class=\"studio-table__header-content\">\n @if (column.headerTemplate) {\n <ng-container *ngTemplateOutlet=\"column.headerTemplate; context: getHeaderContext(column)\" />\n } @else {\n <span>{{ column.label }}</span>\n @if (column.sortable && sortable()) {\n <studio-icon\n [name]=\"getSortIcon(column)\"\n [size]=\"16\"\n class=\"studio-table__sort-icon\"\n />\n }\n }\n </div>\n </th>\n }\n\n <!-- Actions Column -->\n @if (rowActions().length > 0) {\n <th class=\"studio-table__th studio-table__th--actions\">Actions</th>\n }\n </tr>\n </thead>\n }\n\n <!-- Body -->\n <tbody class=\"studio-table__tbody\">\n <!-- Loading Skeleton -->\n @if (loading()) {\n @for (i of [].constructor(loadingRows()); track i) {\n <tr class=\"studio-table__row studio-table__row--loading\">\n @if (selectionMode() === 'multiple') {\n <td class=\"studio-table__td\">\n <div class=\"studio-table__skeleton studio-table__skeleton--checkbox\"></div>\n </td>\n }\n @if (expandable()) {\n <td class=\"studio-table__td\">\n <div class=\"studio-table__skeleton studio-table__skeleton--icon\"></div>\n </td>\n }\n @for (column of finalColumns(); track column.key) {\n <td class=\"studio-table__td\" [class.studio-table__th--hide-mobile]=\"column.hideOnMobile\">\n <div class=\"studio-table__skeleton\"></div>\n </td>\n }\n @if (rowActions().length > 0) {\n <td class=\"studio-table__td\">\n <div class=\"studio-table__skeleton studio-table__skeleton--icon\"></div>\n </td>\n }\n </tr>\n }\n }\n\n <!-- Empty State -->\n @if (!loading() && processedData().length === 0) {\n <tr class=\"studio-table__row studio-table__row--empty\">\n <td [attr.colspan]=\"finalColumns().length + (selectionMode() === 'multiple' ? 1 : 0) + (expandable() ? 1 : 0) + (rowActions().length > 0 ? 1 : 0)\" class=\"studio-table__td\">\n <div class=\"studio-table__empty\">\n <studio-icon [name]=\"emptyState().icon || 'inbox'\" [size]=\"48\" class=\"studio-table__empty-icon\" />\n <h3 class=\"studio-table__empty-title\">{{ emptyState().title }}</h3>\n <p class=\"studio-table__empty-message\">{{ emptyState().message }}</p>\n @if (emptyState().action) {\n <studio-button\n size=\"sm\"\n (clicked)=\"emptyState().action!.handler()\">\n {{ emptyState().action!.label }}\n </studio-button>\n }\n </div>\n </td>\n </tr>\n }\n\n <!-- Data Rows -->\n @if (!loading()) {\n @for (row of processedData(); track getRowKey(row); let rowIndex = $index) {\n <!-- Main Row -->\n <tr\n class=\"studio-table__row\"\n [class.studio-table__row--selected]=\"isRowSelected(row)\"\n [class.studio-table__row--expanded]=\"isRowExpanded(row)\"\n [class.studio-table__row--clickable]=\"selectionMode() === 'single'\"\n (click)=\"selectionMode() === 'single' && selectRow(row); handleRowClick(row, $event)\"\n (dblclick)=\"handleRowDblClick(row)\">\n\n <!-- Selection Cell -->\n @if (selectionMode() === 'multiple') {\n <td class=\"studio-table__td studio-table__td--selection\">\n <studio-checkbox\n [value]=\"isRowSelected(row)\"\n (changed)=\"selectRow(row)\"\n (click)=\"$event.stopPropagation()\"\n />\n </td>\n }\n\n <!-- Expansion Cell -->\n @if (expandable()) {\n <td class=\"studio-table__td studio-table__td--expand\">\n <studio-button\n variant=\"ghost\"\n size=\"sm\"\n iconPosition=\"only\"\n [icon]=\"isRowExpanded(row) ? 'chevron-down' : 'chevron-right'\"\n (clicked)=\"toggleRowExpansion(row); $event.stopPropagation()\"\n />\n </td>\n }\n\n <!-- Data Cells -->\n @for (column of finalColumns(); track column.key) {\n <td\n [class]=\"getFullCellClass(row, column)\"\n [attr.data-label]=\"column.label\">\n\n @if (column.cellTemplate) {\n <ng-container *ngTemplateOutlet=\"column.cellTemplate; context: getCellContext(row, column, rowIndex)\" />\n } @else {\n <span>{{ column.formatter ? column.formatter(getCellValue(row, column), row) : getCellValue(row, column) }}</span>\n }\n </td>\n }\n\n <!-- Actions Cell -->\n @if (rowActions().length > 0) {\n <td class=\"studio-table__td studio-table__td--actions\">\n <div class=\"studio-table__actions\">\n @for (action of rowActions(); track action.label) {\n @if (isActionVisible(action, row)) {\n @if (action.divider) {\n <div class=\"studio-table__action-divider\"></div>\n }\n <studio-button\n variant=\"ghost\"\n size=\"sm\"\n [icon]=\"action.icon\"\n [disabled]=\"isActionDisabled(action, row)\"\n [color]=\"action.variant === 'danger' ? 'error' : 'primary'\"\n (clicked)=\"executeRowAction(action, row, $event)\">\n {{ action.label }}\n </studio-button>\n }\n }\n </div>\n </td>\n }\n </tr>\n\n <!-- Expanded Row -->\n @if (expandable() && isRowExpanded(row) && expandedRowTemplate()) {\n <tr class=\"studio-table__row studio-table__row--expansion\">\n <td [attr.colspan]=\"finalColumns().length + (selectionMode() === 'multiple' ? 1 : 0) + (expandable() ? 1 : 0) + (rowActions().length > 0 ? 1 : 0)\" class=\"studio-table__td\">\n <div class=\"studio-table__expansion-content\">\n <ng-container *ngTemplateOutlet=\"expandedRowTemplate(); context: { $implicit: row, index: rowIndex }\" />\n </div>\n </td>\n </tr>\n }\n }\n }\n </tbody>\n </table>\n </div>\n</div>\n", styles: [":host{display:block;font-family:var(--studio-font-family);position:relative}.studio-table__wrapper{position:relative;width:100%;overflow:hidden}.studio-table__container{width:100%;overflow-x:auto;overflow-y:visible;-webkit-overflow-scrolling:touch}.studio-table__container::-webkit-scrollbar{height:8px}.studio-table__container::-webkit-scrollbar-track{background:var(--studio-bg-secondary);border-radius:var(--studio-radius-sm)}.studio-table__container::-webkit-scrollbar-thumb{background:var(--studio-border-primary);border-radius:var(--studio-radius-sm)}.studio-table__container::-webkit-scrollbar-thumb:hover{background:var(--studio-text-tertiary)}.studio-table__table{width:100%;border-collapse:separate;border-spacing:0;background:var(--studio-bg-primary)}.studio-table__thead{background:var(--studio-bg-secondary)}.studio-table__header-row{border-bottom:1px solid var(--studio-border-primary)}.studio-table__th{padding:.75rem 1rem;text-align:left;font-weight:var(--studio-font-weight-semibold);font-size:var(--studio-font-size-sm);color:var(--studio-text-primary);white-space:nowrap;-webkit-user-select:none;user-select:none;position:relative}.studio-table__th--sortable{cursor:pointer;transition:background-color var(--studio-transition-fast)}.studio-table__th--sortable:hover{background:var(--studio-bg-tertiary)}.studio-table__th--sorted{color:var(--studio-primary)}.studio-table__th--selection,.studio-table__th--expand{width:48px;padding:.75rem .5rem}.studio-table__th--actions{width:auto;min-width:100px}.studio-table__th--align-center{text-align:center}.studio-table__th--align-right{text-align:right}.studio-table__th--fixed-left{position:sticky;left:0;z-index:2;background:var(--studio-bg-secondary);box-shadow:2px 0 4px #0000000d}.studio-table__th--fixed-right{position:sticky;right:0;z-index:2;background:var(--studio-bg-secondary);box-shadow:-2px 0 4px #0000000d}.studio-table__header-content{display:flex;align-items:center;gap:.5rem}.studio-table__sort-icon{flex-shrink:0;color:var(--studio-text-tertiary);transition:color var(--studio-transition-fast)}.studio-table__th--sorted .studio-table__sort-icon{color:var(--studio-primary)}.studio-table__row{border-bottom:1px solid var(--studio-border-primary);transition:background-color var(--studio-transition-fast)}.studio-table__row--clickable{cursor:pointer}.studio-table__row--selected{background:var(--studio-primary-bg)}.studio-table__row--expansion{background:var(--studio-bg-secondary)}.studio-table__row--loading{pointer-events:none}.studio-table__td{padding:.75rem 1rem;font-size:var(--studio-font-size-base);color:var(--studio-text-primary);vertical-align:middle}.studio-table__td--selection,.studio-table__td--expand{width:48px;padding:.75rem .5rem}.studio-table__td--actions{width:auto}.studio-table__td--align-center{text-align:center}.studio-table__td--align-right{text-align:right}.studio-table__td--fixed-left{position:sticky;left:0;z-index:1;background:var(--studio-bg-primary);box-shadow:2px 0 4px #0000000d}.studio-table__row--selected .studio-table__td--fixed-left{background:var(--studio-primary-bg)}.studio-table__td--fixed-right{position:sticky;right:0;z-index:1;background:var(--studio-bg-primary);box-shadow:-2px 0 4px #0000000d}.studio-table__row--selected .studio-table__td--fixed-right{background:var(--studio-primary-bg)}:host(.studio-table--compact) .studio-table__th,:host(.studio-table--compact) .studio-table__td{padding:.5rem .75rem}:host(.studio-table--compact) .studio-table__th{font-size:.813rem}:host(.studio-table--compact) .studio-table__td{font-size:.875rem}:host(.studio-table--spacious) .studio-table__th,:host(.studio-table--spacious) .studio-table__td{padding:1rem 1.5rem}:host(.studio-table--spacious) .studio-table__th{font-size:var(--studio-font-size-base)}:host(.studio-table--spacious) .studio-table__td{font-size:var(--studio-font-size-lg)}:host(.studio-table--striped) .studio-table__row:nth-child(2n){background:var(--studio-bg-secondary)}:host(.studio-table--bordered) .studio-table__table{border:1px solid var(--studio-border-primary);border-radius:var(--studio-radius-md)}:host(.studio-table--bordered) .studio-table__th,:host(.studio-table--bordered) .studio-table__td{border-right:1px solid var(--studio-border-primary)}:host(.studio-table--bordered) .studio-table__th:last-child,:host(.studio-table--bordered) .studio-table__td:last-child{border-right:none}:host(.studio-table--minimal) .studio-table__thead{background:transparent}:host(.studio-table--minimal) .studio-table__header-row{border-bottom:2px solid var(--studio-border-primary)}:host(.studio-table--minimal) .studio-table__row{border-bottom-style:dashed}:host(.studio-table--hoverable) .studio-table__row:not(.studio-table__row--empty):not(.studio-table__row--loading):not(.studio-table__row--expansion):hover{background:var(--studio-bg-secondary)}:host(.studio-table--hoverable) .studio-table__row--selected:hover{background:var(--studio-primary-bg);filter:brightness(.98)}:host(.studio-table--sticky-header) .studio-table__thead{position:sticky;top:0;z-index:3;box-shadow:0 2px 4px #0000000d}.studio-table__actions{display:flex;align-items:center;gap:.25rem;flex-wrap:wrap}.studio-table__action-divider{width:1px;height:1.5rem;background:var(--studio-border-primary);margin:0 .25rem}.studio-table__expansion-content{padding:1rem;background:var(--studio-bg-tertiary);border-radius:var(--studio-radius-sm)}.studio-table__loading-overlay{position:absolute;inset:0;background:#fffc;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:10}.studio-table__spinner{animation:spin 1s linear infinite;color:var(--studio-primary)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.studio-table__skeleton{height:1rem;background:linear-gradient(90deg,var(--studio-bg-secondary) 0%,var(--studio-bg-tertiary) 50%,var(--studio-bg-secondary) 100%);background-size:200% 100%;animation:skeleton-loading 1.5s ease-in-out infinite;border-radius:var(--studio-radius-sm)}.studio-table__skeleton--checkbox{width:1.25rem;height:1.25rem}.studio-table__skeleton--icon{width:2rem;height:1rem}@keyframes skeleton-loading{0%{background-position:200% 0}to{background-position:-200% 0}}.studio-table__empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem 1rem;text-align:center}.studio-table__empty-icon{color:var(--studio-text-tertiary);margin-bottom:1rem}.studio-table__empty-title{margin:0 0 .5rem;font-size:var(--studio-font-size-lg);font-weight:var(--studio-font-weight-semibold);color:var(--studio-text-primary)}.studio-table__empty-message{margin:0 0 1.5rem;font-size:var(--studio-font-size-base);color:var(--studio-text-secondary)}@media (max-width: 768px){:host(.studio-table--responsive) .studio-table__container{overflow-x:visible}:host(.studio-table--responsive) .studio-table__table{display:block}:host(.studio-table--responsive) .studio-table__thead{display:none}:host(.studio-table--responsive) .studio-table__tbody{display:block}:host(.studio-table--responsive) .studio-table__row{display:block;margin-bottom:1rem;border:1px solid var(--studio-border-primary);border-radius:var(--studio-radius-md);box-shadow:var(--studio-shadow-sm);overflow:hidden}:host(.studio-table--responsive) .studio-table__row--empty,:host(.studio-table--responsive) .studio-table__row--loading{display:table-row}:host(.studio-table--responsive) .studio-table__row--expansion{margin-top:-1rem;border-top:none;border-top-left-radius:0;border-top-right-radius:0}:host(.studio-table--responsive) .studio-table__td{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;border-bottom:1px solid var(--studio-border-primary)}:host(.studio-table--responsive) .studio-table__td:last-child{border-bottom:none}:host(.studio-table--responsive) .studio-table__td:before{content:attr(data-label);font-weight:var(--studio-font-weight-semibold);color:var(--studio-text-secondary);min-width:100px;flex-shrink:0}:host(.studio-table--responsive) .studio-table__td--selection,:host(.studio-table--responsive) .studio-table__td--expand{display:flex;justify-content:center;padding:.5rem 1rem}:host(.studio-table--responsive) .studio-table__td--selection:before,:host(.studio-table--responsive) .studio-table__td--expand:before{display:none}:host(.studio-table--responsive) .studio-table__td--actions{display:block;padding:.75rem 1rem}:host(.studio-table--responsive) .studio-table__td--actions:before{display:block;margin-bottom:.5rem}:host(.studio-table--responsive) .studio-table__td--actions .studio-table__actions{justify-content:flex-start}:host(.studio-table--responsive) .studio-table__td--hide-mobile{display:none!important}:host(.studio-table--responsive) .studio-table__expansion-content{padding:.75rem 1rem}}\n"] }]
|
|
5471
|
+
}], ctorParameters: () => [], propDecorators: { variantInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], densityInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], columnDirectives: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TableColumnDirective), { isSignal: true }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], sortMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortMode", required: false }] }], hoverable: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverable", required: false }] }], stickyHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "stickyHeader", required: false }] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], expandedRowTemplate: [{ type: i0.ContentChild, args: ['expandedRow', { isSignal: true }] }], expandMultiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandMultiple", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingRows", required: false }] }], emptyState: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyState", required: false }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], mobileBreakpoint: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileBreakpoint", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }, { type: i0.Output, args: ["selectedChange"] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }, { type: i0.Output, args: ["sortChange"] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], rowDblClick: [{ type: i0.Output, args: ["rowDblClick"] }] } });
|
|
5472
|
+
|
|
5473
|
+
/**
|
|
5474
|
+
* Table component types and interfaces
|
|
5475
|
+
* Enterprise-grade type-safe table with full feature set
|
|
5476
|
+
*/
|
|
5477
|
+
|
|
5032
5478
|
class TabsComponent {
|
|
5033
5479
|
tabs = input.required(...(ngDevMode ? [{ debugName: "tabs" }] : []));
|
|
5034
5480
|
activeTab = signal('', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
|
|
@@ -6425,5 +6871,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
6425
6871
|
* Generated bundle index. Do not edit.
|
|
6426
6872
|
*/
|
|
6427
6873
|
|
|
6428
|
-
export { BadgeComponent, BadgeWrapperComponent, BottomNavigationComponent, ButtonComponent, ButtonGroupComponent, ButtonToggleGroupComponent, CardComponent, ChatComponent, ChatInputComponent, ChatMessageComponent, CheckboxComponent, ColorPickerCompactComponent, ColorPickerComponent, DEFAULT_COLOR_PRESETS, DrawerComponent, DrawerService, DropdownComponent, IconComponent, InputComponent, InspectorComponent, MASK_PRESETS, MaskDirective, MaskEngine, MenuComponent, ModalComponent, NavbarComponent, PopoverComponent, RadioButtonComponent, STUDIO_CONFIG, SelectComponent, SidebarComponent, StudioConfigService, SwitchComponent, TabsComponent, TextareaComponent, ThemeSwitchComponent, TooltipComponent, classNames, isSafeUrl, loadGoogleFont, loadGoogleFonts, provideStudioConfig, provideStudioIcons, sanitizeUrl, withConfigDefault };
|
|
6874
|
+
export { BadgeComponent, BadgeWrapperComponent, BottomNavigationComponent, ButtonComponent, ButtonGroupComponent, ButtonToggleGroupComponent, CardComponent, ChatComponent, ChatInputComponent, ChatMessageComponent, CheckboxComponent, ColorPickerCompactComponent, ColorPickerComponent, DEFAULT_COLOR_PRESETS, DrawerComponent, DrawerService, DropdownComponent, IconComponent, InputComponent, InspectorComponent, MASK_PRESETS, MaskDirective, MaskEngine, MenuComponent, ModalComponent, NavbarComponent, PopoverComponent, RadioButtonComponent, STUDIO_CONFIG, SelectComponent, SidebarComponent, StudioConfigService, SwitchComponent, TableColumnDirective, TableComponent, TabsComponent, TextareaComponent, ThemeSwitchComponent, TooltipComponent, classNames, isSafeUrl, loadGoogleFont, loadGoogleFonts, provideStudioConfig, provideStudioIcons, sanitizeUrl, withConfigDefault };
|
|
6429
6875
|
//# sourceMappingURL=eduboxpro-studio.mjs.map
|