@styloviz/data-table 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +81 -0
- package/fesm2022/styloviz-data-table.mjs +704 -0
- package/fesm2022/styloviz-data-table.mjs.map +1 -0
- package/package.json +43 -0
- package/types/styloviz-data-table.d.ts +305 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styloviz-data-table.mjs","sources":["../../../../projects/data-table-free/src/lib/data-table.component.ts","../../../../projects/data-table-free/src/lib/data-table.component.html","../../../../projects/data-table-free/src/styloviz-data-table.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n Directive,\n ElementRef,\n Input,\n input,\n model,\n OnInit,\n output,\n signal,\n} from '@angular/core';\nimport { NgClass } from '@angular/common';\n\n// ─── Auto-focus directive (colocated) ────────────────────────────────────────\n\n@Directive({ selector: '[dtAutoFocus]', standalone: true })\nexport class DtAutoFocusDirective implements OnInit {\n @Input('dtAutoFocus') enabled: boolean = true;\n constructor(private readonly el: ElementRef<HTMLInputElement>) {}\n ngOnInit(): void { if (this.enabled) setTimeout(() => this.el.nativeElement.focus(), 0); }\n}\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type SortDirection = 'asc' | 'desc' | null;\nexport type ColumnAlign = 'left' | 'center' | 'right';\nexport type TableVariant = 'default' | 'striped' | 'bordered' | 'borderless';\nexport type TableDensity = 'compact' | 'comfortable' | 'spacious';\nexport type SelectMode = 'multi' | 'single';\nexport type FilterType = 'text' | 'select' | 'none';\nexport type LoadingStyle = 'skeleton' | 'overlay';\n\nexport interface TableColumn<T = Record<string, unknown>> {\n /** Unique key matching the row data property. */\n key: string;\n /** Column header label. */\n label: string;\n /** Column horizontal alignment. @default 'left' */\n align?: ColumnAlign;\n /** Allow sorting on this column. @default false */\n sortable?: boolean;\n /** Minimum column width (e.g., '8rem'). */\n minWidth?: string;\n /** Maximum column width with text truncation. */\n maxWidth?: string;\n /** Custom cell render function returning a plain-text string. */\n render?: (row: T, col: TableColumn<T>) => string;\n /** Render cell value as a status badge pill. */\n badge?: boolean;\n /** Map of cell values → Tailwind color classes for badge rendering. */\n badgeColors?: Record<string, string>;\n // ── v2 additions ──────────────────────────────────────────────────────────\n /** Pin column to 'left' or 'right' (sticky positioning). */\n pinned?: 'left' | 'right';\n /** Tooltip text rendered on the column header info icon. */\n description?: string;\n /** Filter input type shown in the column filter row. @default 'text' */\n filterType?: FilterType;\n /** Options for select-type column filters. */\n filterOptions?: string[];\n /** Summary function shown in the sticky tfoot row. Receives ALL filtered rows. */\n footerFn?: (rows: T[]) => string;\n /** Allow double-click inline cell editing. Emit editComplete to persist. */\n editable?: boolean;\n /** Initially hidden — user can show via the column-toggle panel. */\n hidden?: boolean;\n}\n\nexport interface TableRowAction<T = Record<string, unknown>> {\n /** Display label. */\n label: string;\n /** SVG icon path string. */\n iconPath?: string;\n /** Called with the row when the action is triggered. */\n handler: (row: T) => void;\n /** Return true to hide this action for a specific row. */\n hidden?: (row: T) => boolean;\n /** Renders with red / danger styling. */\n danger?: boolean;\n}\n\nexport interface BulkAction {\n /** Display label. */\n label: string;\n /** SVG icon path string. */\n iconPath?: string;\n /** Renders with red / danger styling. */\n danger?: boolean;\n /** Optional inline handler called with the selected IDs. */\n handler?: (ids: Set<string | number>) => void;\n}\n\nexport interface SortState {\n key: string;\n direction: SortDirection;\n}\n\nexport interface CellClickEvent<T = Record<string, unknown>> {\n row: T;\n column: TableColumn<T>;\n value: string;\n}\n\nexport interface EditCompleteEvent<T = Record<string, unknown>> {\n row: T;\n column: TableColumn<T>;\n oldValue: string;\n newValue: string;\n}\n\nexport interface ServerPageEvent {\n page: number;\n pageSize: number;\n sorts: SortState[];\n filters: Record<string, string>;\n search: string;\n}\n\n// ─── Default badge colors ─────────────────────────────────────────────────────\n\nconst DEFAULT_BADGE_COLORS: Record<string, string> = {\n active: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400',\n inactive: 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400',\n pending: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400',\n error: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400',\n success: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400',\n warning: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400',\n info: 'bg-sky-100 text-sky-700 dark:bg-sky-900/30 dark:text-sky-400',\n draft: 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400',\n published: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400',\n on_leave: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400',\n};\n\n// ─── Density icon paths (16×16 SVG viewBox) ────────────────────────────────────\n\nconst DENSITY_OPTIONS: ReadonlyArray<{ value: TableDensity; label: string; icon: string }> = [\n { value: 'compact', label: 'Compact', icon: 'M1 3h14M1 5.5h14M1 8h14M1 10.5h14M1 13h14' },\n { value: 'comfortable', label: 'Comfortable', icon: 'M1 3h14M1 7.5h14M1 12h14' },\n { value: 'spacious', label: 'Spacious', icon: 'M1 4h14M1 12h14' },\n];\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\n/**\n * DataTableComponent v2 — Premium feature-rich data table.\n *\n * New in v2:\n * - Visual variants: default | striped | bordered | borderless\n * - Density presets: compact | comfortable | spacious (+ toolbar toggle)\n * - Built-in toolbar: title, global search, column toggle, density picker, CSV export\n * - Multi-column sort via Shift+click\n * - Per-column text/select filters with active filter chips\n * - Expandable rows with auto key-value detail panel\n * - Inline cell editing (double-click) → emits editComplete\n * - Bulk actions toolbar\n * - Footer summary row via column footerFn\n * - Single-select mode (radio buttons)\n * - Status-based row coloring via rowClassFn\n * - Sticky / pinned columns (left or right)\n * - Server-side mode (pagination + sort + filter emitted)\n * - Loading overlay (alternative to skeleton)\n * - Mobile card layout\n * - First/Last page buttons + page-number pills\n * - CSV export\n */\n@Component({\n selector: 'sv-data-table',\n standalone: true,\n imports: [NgClass, DtAutoFocusDirective],\n templateUrl: './data-table.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SvDataTableComponent<T extends Record<string, unknown> = Record<string, unknown>> {\n\n // ── Inputs — Data ─────────────────────────────────────────────────────────\n\n /** Column definitions (key, header, alignment, optional cell template). */\n columns = input<TableColumn<T>[]>([]);\n /** Row data to display. */\n rows = input<T[]>([]);\n /** Property used as each row's unique id (for selection/tracking). @default 'id' */\n rowKey = input<string>('id');\n\n // ── Inputs — Selection ────────────────────────────────────────────────────\n\n /** Enable row selection. @default false */\n selectable = input<boolean>(false);\n /** 'multi' = checkboxes, 'single' = radio buttons. @default 'multi' */\n selectMode = input<SelectMode>('multi');\n /** Selected row ids (two-way). */\n selectedIds = model<Set<string | number>>(new Set());\n\n // ── Inputs — Actions ──────────────────────────────────────────────────────\n\n /** Per-row action buttons. */\n actions = input<TableRowAction<T>[]>([]);\n /** Bulk actions applied to selected rows. */\n bulkActions = input<BulkAction[]>([]);\n\n // ── Inputs — Loading ──────────────────────────────────────────────────────\n\n /** Show a loading state. @default false */\n loading = input<boolean>(false);\n /** 'skeleton' rows or full-table 'overlay' spinner. @default 'skeleton' */\n loadingStyle = input<LoadingStyle>('skeleton');\n /** Number of skeleton rows while loading. @default 5 */\n skeletonRows = input<number>(5);\n\n // ── Inputs — Pagination ───────────────────────────────────────────────────\n\n /** Enable client-side pagination. @default true */\n paginated = input<boolean>(true);\n /** Rows per page (two-way). @default 10 */\n pageSize = model<number>(10);\n /** Page-size options offered in the toolbar. @default [10,25,50] */\n pageSizeOptions = input<number[]>([10, 25, 50]);\n /** Server-side mode: disables client filtering/sorting/paging. */\n serverSide = input<boolean>(false);\n /** Total row count when in server-side mode. */\n totalCount = input<number | null>(null);\n\n // ── Inputs — Visual ───────────────────────────────────────────────────────\n\n /** Table style variant. @default 'default' */\n variant = input<TableVariant>('default');\n /** Row density: comfortable, compact, etc. @default 'comfortable' */\n density = input<TableDensity>('comfortable');\n /** Deprecated shorthand for compact density. @deprecated Use density=\"compact\". */\n compact = input<boolean>(false);\n /** Max body height (CSS length) before the table scrolls. */\n maxHeight = input<string | null>(null);\n /** Show a hint when the table is horizontally scrollable. @default true */\n showScrollHint = input<boolean>(true);\n /** Callback returning extra Tailwind classes for a given row. */\n rowClassFn = input<((row: T) => string) | null>(null);\n\n // ── Inputs — Toolbar ──────────────────────────────────────────────────────\n\n /** Show the toolbar above the table. @default false */\n showToolbar = input<boolean>(false);\n /** Title shown in the toolbar. */\n tableTitle = input<string>('');\n /** Show a search box in the toolbar. @default false */\n searchable = input<boolean>(false);\n /** Show the column visibility toggle. @default false */\n showColumnToggle = input<boolean>(false);\n /** Show an export (CSV) control. @default false */\n exportable = input<boolean>(false);\n /** Show the density toggle. @default false */\n showDensityToggle = input<boolean>(false);\n\n // ── Inputs — Features ─────────────────────────────────────────────────────\n\n /** Enable per-column filters. @default false */\n columnFilters = input<boolean>(false);\n /** Allow sorting by multiple columns. @default false */\n multiSort = input<boolean>(false);\n /** Enable expandable rows. @default false */\n expandable = input<boolean>(false);\n /** Renderer for the expanded row detail. */\n expandRender = input<((row: T) => string) | null>(null);\n /** Show a table footer row. @default false */\n showFooter = input<boolean>(false);\n /** Render rows as cards on small screens. @default false */\n cardOnMobile = input<boolean>(false);\n\n // ── Inputs — Other ────────────────────────────────────────────────────────\n\n /** Message shown when there are no rows. @default 'No data to display.' */\n emptyMessage = input<string>('No data to display.');\n /** Extra CSS classes for the root element. */\n customClass = input<string>('');\n\n // ── Outputs ───────────────────────────────────────────────────────────────\n\n /** Emitted with the new sort state when sorting changes. */\n sortChange = output<SortState[]>();\n /** Emitted with the set of selected row ids. */\n selectionChange = output<Set<string | number>>();\n /** Emitted with the clicked row. */\n rowClick = output<T>();\n /** Emitted with the clicked cell context. */\n cellClick = output<CellClickEvent<T>>();\n /** Emitted when an inline cell edit completes (Pro). */\n editComplete = output<EditCompleteEvent<T>>();\n /** Emitted in server-side mode when the page/size changes. */\n serverPageChange = output<ServerPageEvent>();\n /** Emitted with the row being expanded. */\n rowExpand = output<T>();\n /** Emitted with the bulk action and the selected row ids. */\n bulkActionClick = output<{ action: BulkAction; selectedIds: Set<string | number> }>();\n\n // ── Internal signals ──────────────────────────────────────────────────────\n\n readonly sorts = signal<SortState[]>([]);\n readonly currentPage = signal<number>(1);\n readonly openActionRowId = signal<string | number | null>(null);\n readonly searchQuery = signal<string>('');\n readonly columnFiltersRecord = signal<Record<string, string>>({});\n readonly hiddenColumnKeys = signal<Set<string>>(new Set());\n readonly expandedRowIds = signal<Set<string | number>>(new Set());\n readonly editingCell = signal<{ rowId: string | number; key: string } | null>(null);\n readonly editDraft = signal<string>('');\n readonly columnToggleOpen = signal<boolean>(false);\n readonly densityOverride = signal<TableDensity | null>(null);\n\n readonly densityOptions = DENSITY_OPTIONS;\n\n // ── Computed — columns ────────────────────────────────────────────────────\n\n visibleColumns = computed<TableColumn<T>[]>(() => {\n const hidden = this.hiddenColumnKeys();\n return this.columns().filter(col => !col.hidden && !hidden.has(col.key));\n });\n\n // ── Computed — density ────────────────────────────────────────────────────\n\n effectiveDensity = computed<TableDensity>(() =>\n this.densityOverride() ?? (this.compact() ? 'compact' : this.density())\n );\n\n rowPadding = computed<string>(() => {\n switch (this.effectiveDensity()) {\n case 'compact': return 'px-3 py-1.5';\n case 'spacious': return 'px-5 py-4';\n default: return 'px-4 py-2.5';\n }\n });\n\n headerPadding = computed<string>(() => {\n switch (this.effectiveDensity()) {\n case 'compact': return 'px-3 py-2';\n case 'spacious': return 'px-5 py-3.5';\n default: return 'px-4 py-3';\n }\n });\n\n cellTextSize = computed<string>(() =>\n this.effectiveDensity() === 'compact' ? 'text-xs' : 'text-[13px]'\n );\n\n // ── Computed — data pipeline ──────────────────────────────────────────────\n\n readonly filteredRows = computed<T[]>(() => {\n if (this.serverSide()) return this.rows();\n const q = this.searchQuery().toLowerCase().trim();\n const colFltrs = this.columnFiltersRecord();\n const cols = this.columns();\n\n return this.rows().filter(row => {\n if (q) {\n const hit = cols.some(col =>\n String(row[col.key] ?? '').toLowerCase().includes(q)\n );\n if (!hit) return false;\n }\n for (const [key, val] of Object.entries(colFltrs)) {\n if (!val) continue;\n if (!String(row[key] ?? '').toLowerCase().includes(val.toLowerCase()))\n return false;\n }\n return true;\n });\n });\n\n readonly sortedRows = computed<T[]>(() => {\n if (this.serverSide()) return this.filteredRows();\n const sorts = this.sorts().filter(s => s.direction !== null);\n const data = this.filteredRows().slice();\n if (sorts.length === 0) return data;\n\n return data.sort((a, b) => {\n for (const s of sorts) {\n const av = a[s.key], bv = b[s.key];\n if (av == null && bv == null) continue;\n if (av == null) return 1;\n if (bv == null) return -1;\n const cmp = String(av).localeCompare(String(bv), undefined, {\n numeric: true, sensitivity: 'base',\n });\n if (cmp !== 0) return s.direction === 'asc' ? cmp : -cmp;\n }\n return 0;\n });\n });\n\n pagedRows = computed<T[]>(() => {\n if (this.serverSide()) return this.rows();\n if (!this.paginated()) return this.sortedRows();\n const ps = this.pageSize();\n const page = this.currentPage();\n return this.sortedRows().slice((page - 1) * ps, page * ps);\n });\n\n totalDisplayCount = computed<number>(() =>\n this.serverSide()\n ? (this.totalCount() ?? this.rows().length)\n : this.sortedRows().length\n );\n\n totalPages = computed<number>(() =>\n Math.max(1, Math.ceil(this.totalDisplayCount() / this.pageSize()))\n );\n\n rangeStart = computed<number>(() =>\n this.totalDisplayCount() === 0\n ? 0\n : (this.currentPage() - 1) * this.pageSize() + 1\n );\n\n rangeEnd = computed<number>(() =>\n Math.min(this.currentPage() * this.pageSize(), this.totalDisplayCount())\n );\n\n allSelected = computed<boolean>(() => {\n const paged = this.pagedRows();\n if (!paged.length) return false;\n const sel = this.selectedIds();\n return paged.every(row => sel.has(this.getRowId(row)));\n });\n\n someSelected = computed<boolean>(() => {\n const paged = this.pagedRows();\n const sel = this.selectedIds();\n return paged.some(row => sel.has(this.getRowId(row))) && !this.allSelected();\n });\n\n skeletonArray = computed<number[]>(() =>\n Array.from({ length: this.skeletonRows() }, (_, i) => i)\n );\n\n activeFilterChips = computed<Array<{ key: string; label: string; value: string }>>(() => {\n const chips: Array<{ key: string; label: string; value: string }> = [];\n const q = this.searchQuery().trim();\n if (q) chips.push({ key: '__search__', label: 'Search', value: q });\n const cols = this.columns();\n for (const [key, value] of Object.entries(this.columnFiltersRecord())) {\n if (!value) continue;\n const col = cols.find(c => c.key === key);\n chips.push({ key, label: col?.label ?? key, value });\n }\n return chips;\n });\n\n hasFooterFns = computed<boolean>(() =>\n this.columns().some(col => !!col.footerFn)\n );\n\n totalColspan = computed<number>(() => {\n let n = this.visibleColumns().length;\n if (this.selectable()) n++;\n if (this.expandable()) n++;\n if (this.actions().length > 0) n++;\n return n;\n });\n\n pageNumbers = computed<number[]>(() => {\n const total = this.totalPages();\n const current = this.currentPage();\n if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);\n const pages: number[] = [1];\n if (current > 3) pages.push(-1);\n const start = Math.max(2, current - 1);\n const end = Math.min(total - 1, current + 1);\n for (let i = start; i <= end; i++) pages.push(i);\n if (current < total - 2) pages.push(-1);\n pages.push(total);\n return pages;\n });\n\n // ── Computed — CSS helpers ─────────────────────────────────────────────────\n\n rootClass = computed<string>(() => {\n const v = this.variant();\n const base = 'flex flex-col overflow-hidden rounded-xl shadow-sm ';\n const border = v === 'borderless'\n ? ''\n : 'border border-gray-200/80 dark:border-gray-800/60 ';\n return base + border + 'bg-white dark:bg-gray-900 ' + this.customClass();\n });\n\n tableClass = computed<string>(() =>\n this.variant() === 'bordered'\n ? 'min-w-full border-collapse'\n : 'min-w-full divide-y divide-gray-200 dark:divide-gray-700'\n );\n\n tbodyClass = computed<string>(() => {\n const v = this.variant();\n const base = 'bg-white dark:bg-gray-900 ';\n return (v === 'bordered' || v === 'borderless')\n ? base\n : base + 'divide-y divide-gray-100 dark:divide-gray-800/60';\n });\n\n // ── Sort ──────────────────────────────────────────────────────────────────\n\n onSort(col: TableColumn<T>, event: MouseEvent): void {\n if (!col.sortable) return;\n const isMulti = event.shiftKey && this.multiSort();\n const current = this.sorts();\n const existing = current.findIndex(s => s.key === col.key);\n\n if (isMulti) {\n const newSorts = [...current];\n if (existing >= 0) {\n const cur = newSorts[existing];\n if (cur.direction === 'asc') newSorts[existing] = { ...cur, direction: 'desc' };\n else newSorts.splice(existing, 1);\n } else {\n newSorts.push({ key: col.key, direction: 'asc' });\n }\n this.sorts.set(newSorts.filter(s => s.direction !== null));\n } else {\n let next: SortDirection;\n if (existing < 0 || current.length > 1) next = 'asc';\n else if (current[0].direction === 'asc') next = 'desc';\n else if (current[0].direction === 'desc') next = null;\n else next = 'asc';\n this.sorts.set(next ? [{ key: col.key, direction: next }] : []);\n }\n\n this.currentPage.set(1);\n this.sortChange.emit(this.sorts());\n if (this.serverSide()) this.emitServerEvent();\n }\n\n sortIcon(col: TableColumn<T>): string {\n const s = this.sorts().find(x => x.key === col.key);\n if (!s || !s.direction) return 'M8 9l4-4 4 4m0 6l-4 4-4-4';\n return s.direction === 'asc' ? 'M5 15l7-7 7 7' : 'M19 9l-7 7-7-7';\n }\n\n isColumnSorted(col: TableColumn<T>): boolean {\n return this.sorts().some(s => s.key === col.key && !!s.direction);\n }\n\n getSortIndex(col: TableColumn<T>): number {\n const idx = this.sorts().findIndex(s => s.key === col.key && !!s.direction);\n return idx >= 0 ? idx + 1 : 0;\n }\n\n getSortAria(col: TableColumn<T>): string | null {\n if (!col.sortable) return null;\n const s = this.sorts().find(x => x.key === col.key);\n if (!s || !s.direction) return 'none';\n return s.direction === 'asc' ? 'ascending' : 'descending';\n }\n\n // ── Selection ─────────────────────────────────────────────────────────────\n\n getRowId(row: T): string | number {\n const id = row[this.rowKey()] as string | number | undefined | null;\n return id ?? JSON.stringify(row);\n }\n\n isSelected(row: T): boolean {\n return this.selectedIds().has(this.getRowId(row));\n }\n\n toggleRow(row: T): void {\n const id = this.getRowId(row);\n const set = new Set(this.selectedIds());\n if (this.selectMode() === 'single') {\n if (set.has(id)) { set.clear(); } else { set.clear(); set.add(id); }\n } else {\n if (set.has(id)) set.delete(id); else set.add(id);\n }\n this.selectedIds.set(set);\n this.selectionChange.emit(set);\n }\n\n toggleAll(): void {\n if (this.selectMode() === 'single') return;\n const paged = this.pagedRows();\n const all = this.allSelected();\n const set = new Set(this.selectedIds());\n if (all) paged.forEach(row => set.delete(this.getRowId(row)));\n else paged.forEach(row => set.add(this.getRowId(row)));\n this.selectedIds.set(set);\n this.selectionChange.emit(set);\n }\n\n clearSelection(): void {\n this.selectedIds.set(new Set());\n this.selectionChange.emit(new Set());\n }\n\n // ── Row actions ───────────────────────────────────────────────────────────\n\n visibleActions(row: T): TableRowAction<T>[] {\n return this.actions().filter(a => !a.hidden?.(row));\n }\n\n toggleActionMenu(row: T, event: MouseEvent): void {\n event.stopPropagation();\n const id = this.getRowId(row);\n this.openActionRowId.update(cur => cur === id ? null : id);\n }\n\n isActionMenuOpen(row: T): boolean {\n return this.openActionRowId() === this.getRowId(row);\n }\n\n runAction(action: TableRowAction<T>, row: T, event: MouseEvent): void {\n event.stopPropagation();\n action.handler(row);\n this.openActionRowId.set(null);\n }\n\n closeAllMenus(): void {\n this.openActionRowId.set(null);\n this.columnToggleOpen.set(false);\n }\n\n // ── Bulk actions ──────────────────────────────────────────────────────────\n\n runBulkAction(action: BulkAction): void {\n const ids = new Set(this.selectedIds());\n action.handler?.(ids);\n this.bulkActionClick.emit({ action, selectedIds: ids });\n }\n\n // ── Cell rendering ────────────────────────────────────────────────────────\n\n cellValue(row: T, col: TableColumn<T>): string {\n if (col.render) return col.render(row, col);\n const val = row[col.key];\n return val == null ? '' : String(val);\n }\n\n badgeClass(row: T, col: TableColumn<T>): string {\n const val = String(row[col.key] ?? '').toLowerCase().replace(/\\s+/g, '_');\n const colors = { ...DEFAULT_BADGE_COLORS, ...col.badgeColors };\n return colors[val] ?? 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400';\n }\n\n alignClass(col: TableColumn<T>): string {\n return { left: 'text-left', center: 'text-center', right: 'text-right' }[col.align ?? 'left'];\n }\n\n // ── CSS class helpers ─────────────────────────────────────────────────────\n\n getRowClass(row: T, index: number): string[] {\n const classes: string[] = ['group', 'transition-colors', 'duration-75'];\n\n if (this.variant() === 'striped') {\n classes.push(\n index % 2 === 0\n ? 'bg-white dark:bg-gray-900'\n : 'bg-gray-50/60 dark:bg-gray-800/25',\n );\n } else {\n classes.push('bg-white dark:bg-gray-900');\n }\n\n if (this.isSelected(row)) {\n classes.push('!bg-primary-50/60 dark:!bg-primary-950/25');\n } else {\n classes.push('hover:bg-gray-50/80 dark:hover:bg-gray-800/50');\n }\n\n const custom = this.rowClassFn()?.(row);\n if (custom) classes.push(custom);\n\n return classes;\n }\n\n getHeaderCellClass(col: TableColumn<T>): string[] {\n const classes: string[] = [\n this.headerPadding(),\n 'text-[11px] font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400',\n this.alignClass(col),\n ];\n if (col.sortable) {\n classes.push('cursor-pointer select-none hover:text-gray-600 dark:hover:text-gray-300');\n }\n if (this.variant() === 'bordered') {\n classes.push('border border-gray-200 dark:border-gray-700');\n }\n if (col.pinned === 'left') {\n classes.push('sticky left-0 z-[25] bg-gray-50/95 dark:bg-gray-800/95 shadow-[2px_0_4px_-1px_rgba(0,0,0,0.07)]');\n } else if (col.pinned === 'right') {\n classes.push('sticky right-0 z-[25] bg-gray-50/95 dark:bg-gray-800/95 shadow-[-2px_0_4px_-1px_rgba(0,0,0,0.07)]');\n }\n return classes;\n }\n\n getCellClass(col: TableColumn<T>): string[] {\n const classes: string[] = [\n this.rowPadding(),\n this.cellTextSize(),\n 'text-gray-700 dark:text-gray-300',\n this.alignClass(col),\n ];\n if (this.variant() === 'bordered') {\n classes.push('border border-gray-200 dark:border-gray-700');\n }\n if (col.pinned === 'left') {\n classes.push('sticky left-0 z-[5] bg-inherit shadow-[2px_0_4px_-1px_rgba(0,0,0,0.07)]');\n } else if (col.pinned === 'right') {\n classes.push('sticky right-0 z-[5] bg-inherit shadow-[-2px_0_4px_-1px_rgba(0,0,0,0.07)]');\n }\n if (col.editable) classes.push('cursor-pointer');\n return classes;\n }\n\n // ── Row expand ────────────────────────────────────────────────────────────\n\n toggleExpand(row: T, event: MouseEvent): void {\n event.stopPropagation();\n const id = this.getRowId(row);\n const set = new Set(this.expandedRowIds());\n if (set.has(id)) set.delete(id); else set.add(id);\n this.expandedRowIds.set(set);\n this.rowExpand.emit(row);\n }\n\n isExpanded(row: T): boolean {\n return this.expandedRowIds().has(this.getRowId(row));\n }\n\n // ── Inline editing ────────────────────────────────────────────────────────\n\n startEdit(row: T, col: TableColumn<T>, event: MouseEvent): void {\n if (!col.editable) return;\n event.stopPropagation();\n this.editingCell.set({ rowId: this.getRowId(row), key: col.key });\n this.editDraft.set(this.cellValue(row, col));\n }\n\n isEditing(row: T, col: TableColumn<T>): boolean {\n const cell = this.editingCell();\n return !!cell && cell.rowId === this.getRowId(row) && cell.key === col.key;\n }\n\n onEditInput(event: Event): void {\n this.editDraft.set((event.target as HTMLInputElement).value);\n }\n\n saveEdit(row: T, col: TableColumn<T>): void {\n const cell = this.editingCell();\n if (!cell || cell.rowId !== this.getRowId(row) || cell.key !== col.key) return;\n this.editComplete.emit({\n row,\n column: col,\n oldValue: this.cellValue(row, col),\n newValue: this.editDraft(),\n });\n this.editingCell.set(null);\n }\n\n cancelEdit(): void { this.editingCell.set(null); }\n\n // ── Row events ────────────────────────────────────────────────────────────\n\n onRowClick(row: T): void { this.rowClick.emit(row); }\n\n onCellClick(row: T, col: TableColumn<T>, event: MouseEvent): void {\n event.stopPropagation();\n this.cellClick.emit({ row, column: col, value: this.cellValue(row, col) });\n this.rowClick.emit(row);\n }\n\n // ── Column filters ────────────────────────────────────────────────────────\n\n getColFilter(key: string): string {\n return this.columnFiltersRecord()[key] ?? '';\n }\n\n setColFilter(key: string, value: string): void {\n const rec = { ...this.columnFiltersRecord() };\n if (value) rec[key] = value; else delete rec[key];\n this.columnFiltersRecord.set(rec);\n this.currentPage.set(1);\n if (this.serverSide()) this.emitServerEvent();\n }\n\n onColumnFilterChange(key: string, event: Event): void {\n this.setColFilter(key, (event.target as HTMLInputElement | HTMLSelectElement).value);\n }\n\n clearFilter(key: string): void {\n if (key === '__search__') this.searchQuery.set('');\n else this.setColFilter(key, '');\n this.currentPage.set(1);\n if (this.serverSide()) this.emitServerEvent();\n }\n\n clearAllFilters(): void {\n this.columnFiltersRecord.set({});\n this.searchQuery.set('');\n this.currentPage.set(1);\n if (this.serverSide()) this.emitServerEvent();\n }\n\n // ── Global search ─────────────────────────────────────────────────────────\n\n onSearchInput(event: Event): void {\n this.searchQuery.set((event.target as HTMLInputElement).value);\n this.currentPage.set(1);\n if (this.serverSide()) this.emitServerEvent();\n }\n\n // ── Column visibility ─────────────────────────────────────────────────────\n\n toggleColumnVisibility(key: string): void {\n const set = new Set(this.hiddenColumnKeys());\n if (set.has(key)) set.delete(key); else set.add(key);\n this.hiddenColumnKeys.set(set);\n }\n\n // ── Pagination ────────────────────────────────────────────────────────────\n\n goTo(page: number): void {\n const clamped = Math.max(1, Math.min(page, this.totalPages()));\n this.currentPage.set(clamped);\n this.openActionRowId.set(null);\n if (this.serverSide()) this.emitServerEvent();\n }\n\n onPageSizeChange(event: Event): void {\n this.pageSize.set(+(event.target as HTMLSelectElement).value);\n this.currentPage.set(1);\n if (this.serverSide()) this.emitServerEvent();\n }\n\n // ── Server-side ───────────────────────────────────────────────────────────\n\n private emitServerEvent(): void {\n this.serverPageChange.emit({\n page: this.currentPage(),\n pageSize: this.pageSize(),\n sorts: this.sorts().filter(s => !!s.direction),\n filters: this.columnFiltersRecord(),\n search: this.searchQuery(),\n });\n }\n\n // ── CSV export ────────────────────────────────────────────────────────────\n\n exportCsv(): void {\n const cols = this.visibleColumns();\n const data = this.serverSide() ? this.rows() : this.sortedRows();\n const header = cols.map(c => `\"${c.label}\"`).join(',');\n const rowsCsv = data.map(row =>\n cols.map(col => `\"${this.cellValue(row, col).replace(/\"/g, '\"\"')}\"`).join(',')\n );\n const csv = [header, ...rowsCsv].join('\\n');\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = `export-${Date.now()}.csv`;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n }\n\n // ── Track helpers ─────────────────────────────────────────────────────────\n\n trackByKey(_: number, col: TableColumn<T>): string { return col.key; }\n trackByRow(_: number, row: T): string | number { return this.getRowId(row); }\n}\n","<!-- Root wrapper -->\n<div [ngClass]=\"rootClass()\" (click)=\"closeAllMenus()\">\n\n <!-- ═══════════ TOOLBAR ═══════════════════════════════════════════════════ -->\n @if (showToolbar()) {\n <div class=\"flex flex-wrap items-center justify-between gap-3 border-b border-gray-200/80 dark:border-gray-800/60 px-4 py-2.5 bg-white dark:bg-gray-900\">\n\n <!-- Left: title + row count badge -->\n <div class=\"flex items-center gap-2.5 min-w-0\">\n @if (tableTitle()) {\n <h3 class=\"text-sm font-semibold text-gray-800 dark:text-gray-100 truncate\">{{ tableTitle() }}</h3>\n }\n <span class=\"inline-flex items-center rounded-full bg-gray-100 dark:bg-gray-700/60 px-2 py-0.5 text-[11px] font-medium text-gray-500 dark:text-gray-400 tabular-nums\">\n {{ totalDisplayCount() }}\n </span>\n </div>\n\n <!-- Right: search + controls -->\n <div class=\"flex items-center gap-2 flex-wrap\">\n\n <!-- Global search -->\n @if (searchable()) {\n <div class=\"relative\">\n <svg class=\"pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-gray-400\"\n viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/>\n </svg>\n <input\n type=\"search\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($event)\"\n placeholder=\"Search…\"\n aria-label=\"Search table\"\n class=\"h-8 w-44 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 pl-8 pr-3 text-xs text-gray-900 dark:text-gray-100 placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-primary-500/30 focus:border-primary-400 dark:focus:border-primary-500 transition-all\"\n />\n </div>\n }\n\n <!-- Density toggle -->\n @if (showDensityToggle()) {\n <div class=\"flex items-center rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden\"\n role=\"group\" aria-label=\"Row density\">\n @for (d of densityOptions; track d.value) {\n <button\n type=\"button\"\n (click)=\"densityOverride.set(d.value); $event.stopPropagation()\"\n [title]=\"d.label\"\n [attr.aria-pressed]=\"effectiveDensity() === d.value\"\n [ngClass]=\"[\n 'h-7 w-8 flex items-center justify-center border-r border-gray-200 dark:border-gray-700 last:border-r-0 transition-colors focus:outline-none',\n effectiveDensity() === d.value\n ? 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200'\n : 'bg-white dark:bg-gray-800 text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-750 hover:text-gray-600'\n ]\"\n >\n <svg class=\"h-3.5 w-3.5\" viewBox=\"0 0 16 16\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" aria-hidden=\"true\">\n <path [attr.d]=\"d.icon\" />\n </svg>\n </button>\n }\n </div>\n }\n\n <!-- Column visibility toggle -->\n @if (showColumnToggle()) {\n <div class=\"relative\">\n <button\n type=\"button\"\n (click)=\"columnToggleOpen.update(v => !v); $event.stopPropagation()\"\n [attr.aria-expanded]=\"columnToggleOpen()\"\n aria-label=\"Toggle columns\"\n class=\"inline-flex h-8 items-center gap-1.5 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 px-2.5 text-xs text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500\"\n >\n <svg class=\"h-3.5 w-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M9 3H5a2 2 0 0 0-2 2v4m6-6h10a2 2 0 0 1 2 2v4M9 3v18m0 0h10a2 2 0 0 0 2-2V9M9 21H5a2 2 0 0 1-2-2V9m0 0h18\"/>\n </svg>\n Columns\n </button>\n\n @if (columnToggleOpen()) {\n <div\n class=\"absolute right-0 top-full z-40 mt-1 w-48 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-xl py-1.5\"\n (click)=\"$event.stopPropagation()\"\n >\n @for (col of columns(); track col.key) {\n <label class=\"flex cursor-pointer items-center gap-2.5 px-3 py-1.5 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumnKeys().has(col.key)\"\n (change)=\"toggleColumnVisibility(col.key)\"\n class=\"h-3.5 w-3.5 rounded border-gray-300 text-primary-600 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700\"\n />\n <span class=\"text-xs text-gray-700 dark:text-gray-300\">{{ col.label }}</span>\n </label>\n }\n </div>\n }\n </div>\n }\n\n <!-- CSV export -->\n @if (exportable()) {\n <button\n type=\"button\"\n (click)=\"exportCsv()\"\n class=\"inline-flex h-8 items-center gap-1.5 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 px-2.5 text-xs text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500\"\n >\n <svg class=\"h-3.5 w-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M4 16v1a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"/>\n </svg>\n Export\n </button>\n }\n\n </div>\n </div>\n }\n\n <!-- ═══════════ ACTIVE FILTER CHIPS ══════════════════════════════════════ -->\n @if (activeFilterChips().length > 0) {\n <div class=\"flex flex-wrap items-center gap-2 border-b border-gray-200/80 dark:border-gray-800/60 px-4 py-2 bg-gray-50/60 dark:bg-gray-800/20\">\n <span class=\"text-[11px] font-medium text-gray-500 dark:text-gray-400 shrink-0\">Active filters:</span>\n @for (chip of activeFilterChips(); track chip.key) {\n <span class=\"inline-flex items-center gap-1 rounded-full bg-primary-100 dark:bg-primary-900/30 pl-2.5 pr-1 py-0.5 text-[11px] font-medium text-primary-700 dark:text-primary-400\">\n <span class=\"font-semibold\">{{ chip.label }}:</span> {{ chip.value }}\n <button\n type=\"button\"\n (click)=\"clearFilter(chip.key)\"\n class=\"ml-0.5 inline-flex h-4 w-4 items-center justify-center rounded-full hover:bg-primary-200 dark:hover:bg-primary-800/50 transition-colors\"\n [attr.aria-label]=\"'Remove ' + chip.label + ' filter'\"\n >\n <svg class=\"h-2.5 w-2.5\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"3\" aria-hidden=\"true\">\n <path d=\"M6 18L18 6M6 6l12 12\"/>\n </svg>\n </button>\n </span>\n }\n <button\n type=\"button\"\n (click)=\"clearAllFilters()\"\n class=\"text-[11px] text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 underline underline-offset-2 transition-colors\"\n >\n Clear all\n </button>\n </div>\n }\n\n <!-- ═══════════ BULK ACTIONS BAR ═════════════════════════════════════════ -->\n @if (selectedIds().size > 0 && bulkActions().length > 0) {\n <div class=\"flex flex-wrap items-center gap-3 border-b border-primary-200/80 dark:border-primary-800/40 bg-primary-50/80 dark:bg-primary-950/30 px-4 py-2\">\n <span class=\"text-xs font-semibold text-primary-700 dark:text-primary-400 shrink-0\">\n {{ selectedIds().size }} {{ selectedIds().size === 1 ? 'row' : 'rows' }} selected\n </span>\n <div class=\"flex items-center gap-1.5 flex-wrap\">\n @for (action of bulkActions(); track action.label) {\n <button\n type=\"button\"\n (click)=\"runBulkAction(action)\"\n [ngClass]=\"[\n 'inline-flex h-7 items-center gap-1.5 rounded-lg px-3 text-xs font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',\n action.danger\n ? 'bg-red-100 text-red-700 hover:bg-red-200 dark:bg-red-900/30 dark:text-red-400 dark:hover:bg-red-900/50'\n : 'bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-200 border border-gray-200 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-600'\n ]\"\n >\n @if (action.iconPath) {\n <svg class=\"h-3.5 w-3.5 shrink-0\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path [attr.d]=\"action.iconPath\" />\n </svg>\n }\n {{ action.label }}\n </button>\n }\n </div>\n <button\n type=\"button\"\n (click)=\"clearSelection()\"\n class=\"ml-auto text-xs text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors\"\n aria-label=\"Clear selection\"\n >\n Clear\n </button>\n </div>\n }\n\n <!-- ═══════════ TABLE AREA ════════════════════════════════════════════════ -->\n <div\n class=\"relative flex-1 overflow-x-auto\"\n [ngClass]=\"{ 'overflow-y-auto': !!maxHeight() }\"\n [style.max-height]=\"maxHeight() ?? null\"\n >\n\n <!-- Loading overlay -->\n @if (loading() && loadingStyle() === 'overlay') {\n <div class=\"absolute inset-0 z-30 flex items-center justify-center bg-white/70 dark:bg-gray-900/70 backdrop-blur-[2px]\"\n aria-busy=\"true\" aria-label=\"Loading\">\n <svg class=\"h-8 w-8 animate-spin text-primary-500\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"3\" opacity=\"0.2\"/>\n <path d=\"M12 2a10 10 0 0 1 10 10\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n }\n\n <!-- Horizontal scroll gradient hint -->\n @if (showScrollHint()) {\n <div class=\"pointer-events-none absolute inset-y-0 right-0 z-10 w-10 bg-linear-to-l from-white dark:from-gray-900 to-transparent\"\n aria-hidden=\"true\"></div>\n }\n\n <!-- ─── DESKTOP TABLE (hidden on xs when cardOnMobile) ─────────────── -->\n <div [ngClass]=\"cardOnMobile() ? 'hidden sm:block' : 'block'\">\n <table class=\"w-full\" [ngClass]=\"tableClass()\">\n\n <!-- THEAD ─────────────────────────────────────────────────────────── -->\n <thead class=\"bg-gray-50/80 dark:bg-gray-800/40 sticky top-0 z-20\">\n <tr>\n\n <!-- Expand column -->\n @if (expandable()) {\n <th scope=\"col\" [ngClass]=\"[headerPadding(), 'w-10']\"></th>\n }\n\n <!-- Select column -->\n @if (selectable()) {\n <th scope=\"col\" [ngClass]=\"[headerPadding(), 'w-10']\">\n @if (selectMode() === 'multi') {\n <input\n type=\"checkbox\"\n [checked]=\"allSelected()\"\n [indeterminate]=\"someSelected()\"\n (change)=\"toggleAll()\"\n aria-label=\"Select all rows on this page\"\n class=\"h-3.5 w-3.5 rounded border-gray-300 text-primary-600 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700\"\n />\n }\n </th>\n }\n\n <!-- Data column headers -->\n @for (col of visibleColumns(); track trackByKey($index, col)) {\n <th\n scope=\"col\"\n [ngClass]=\"getHeaderCellClass(col)\"\n [style.min-width]=\"col.minWidth ?? null\"\n [style.max-width]=\"col.maxWidth ?? null\"\n (click)=\"onSort(col, $event)\"\n [attr.aria-sort]=\"getSortAria(col)\"\n >\n <span class=\"inline-flex items-center gap-1.5\">\n {{ col.label }}\n\n @if (col.description) {\n <svg class=\"h-3 w-3 shrink-0 text-gray-400\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" [title]=\"col.description\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4m0-4h.01\"/>\n </svg>\n }\n\n @if (col.sortable) {\n <span class=\"inline-flex items-center gap-0.5 shrink-0\">\n <svg class=\"h-3 w-3 transition-opacity\"\n [ngClass]=\"isColumnSorted(col) ? 'opacity-100 text-primary-600 dark:text-primary-400' : 'opacity-30'\"\n viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path [attr.d]=\"sortIcon(col)\" />\n </svg>\n @if (multiSort() && sorts().length > 1 && getSortIndex(col) > 0) {\n <span class=\"text-[9px] font-bold leading-none text-primary-500 dark:text-primary-400\">{{ getSortIndex(col) }}</span>\n }\n </span>\n }\n </span>\n </th>\n }\n\n <!-- Actions column -->\n @if (actions().length > 0) {\n <th scope=\"col\" [ngClass]=\"[headerPadding(), 'w-12']\">\n <span class=\"sr-only\">Actions</span>\n </th>\n }\n\n </tr>\n\n <!-- COLUMN FILTER ROW ─────────────────────────────────────────── -->\n @if (columnFilters()) {\n <tr class=\"border-t border-gray-200/60 dark:border-gray-700/50\">\n @if (expandable()) { <th [ngClass]=\"headerPadding()\"></th> }\n @if (selectable()) { <th [ngClass]=\"headerPadding()\"></th> }\n @for (col of visibleColumns(); track col.key) {\n <th [ngClass]=\"[headerPadding(), 'font-normal']\">\n @if (col.filterType !== 'none') {\n @if (col.filterType === 'select' && col.filterOptions?.length) {\n <select\n [value]=\"getColFilter(col.key)\"\n (change)=\"onColumnFilterChange(col.key, $event)\"\n [attr.aria-label]=\"'Filter ' + col.label\"\n class=\"w-full h-6 rounded-md border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 px-1.5 text-[11px] text-gray-700 dark:text-gray-300 focus:outline-none focus:ring-1 focus:ring-primary-400 transition-colors\"\n >\n <option value=\"\">All</option>\n @for (opt of col.filterOptions!; track opt) {\n <option [value]=\"opt\" [selected]=\"opt === getColFilter(col.key)\">{{ opt }}</option>\n }\n </select>\n } @else {\n <input\n type=\"text\"\n [value]=\"getColFilter(col.key)\"\n (input)=\"onColumnFilterChange(col.key, $event)\"\n placeholder=\"Filter…\"\n [attr.aria-label]=\"'Filter ' + col.label\"\n class=\"w-full h-6 rounded-md border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 px-2 text-[11px] text-gray-700 dark:text-gray-300 placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-primary-400 transition-colors\"\n />\n }\n }\n </th>\n }\n @if (actions().length > 0) { <th [ngClass]=\"headerPadding()\"></th> }\n </tr>\n }\n </thead>\n\n <!-- TBODY ─────────────────────────────────────────────────────────── -->\n <tbody [ngClass]=\"tbodyClass()\">\n\n @if (loading() && loadingStyle() === 'skeleton') {\n <!-- Skeleton rows -->\n @for (i of skeletonArray(); track i) {\n <tr>\n @if (expandable()) {\n <td [ngClass]=\"[rowPadding(), 'w-10']\">\n <div class=\"skeleton-wave h-4 w-4 rounded\"></div>\n </td>\n }\n @if (selectable()) {\n <td [ngClass]=\"[rowPadding(), 'w-10']\">\n <div class=\"skeleton-wave h-4 w-4 rounded\"></div>\n </td>\n }\n @for (col of visibleColumns(); track col.key) {\n <td [ngClass]=\"rowPadding()\">\n <div class=\"skeleton-wave h-4 rounded-md\" [style.width]=\"(55 + (i * 11 + $index * 7) % 35) + '%'\"></div>\n </td>\n }\n @if (actions().length > 0) {\n <td [ngClass]=\"[rowPadding(), 'w-12']\">\n <div class=\"skeleton-wave mx-auto h-6 w-6 rounded-md\"></div>\n </td>\n }\n </tr>\n }\n\n } @else if (pagedRows().length === 0) {\n <!-- Empty state -->\n <tr>\n <td [attr.colspan]=\"totalColspan()\" class=\"px-6 py-16 text-center\">\n <div class=\"flex flex-col items-center gap-2.5\">\n <svg class=\"h-10 w-10 text-gray-300 dark:text-gray-600\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <path d=\"M3 9h18M9 21V9\"/>\n </svg>\n <p class=\"text-sm text-gray-500 dark:text-gray-400\">{{ emptyMessage() }}</p>\n @if (activeFilterChips().length > 0) {\n <button type=\"button\" (click)=\"clearAllFilters()\"\n class=\"mt-1 text-xs text-primary-600 dark:text-primary-400 hover:underline transition-colors\">\n Clear filters\n </button>\n }\n </div>\n </td>\n </tr>\n\n } @else {\n <!-- Data rows -->\n @for (row of pagedRows(); track trackByRow($index, row); let i = $index) {\n\n <!-- ─── Main data row ─────────────────────────────────── -->\n <tr [ngClass]=\"getRowClass(row, i)\" (click)=\"onRowClick(row)\">\n\n <!-- Expand toggle -->\n @if (expandable()) {\n <td [ngClass]=\"[rowPadding(), 'w-10']\" (click)=\"toggleExpand(row, $event)\">\n <button\n type=\"button\"\n class=\"inline-flex h-5 w-5 items-center justify-center rounded text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition-all focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500\"\n [ngClass]=\"{ 'rotate-90': isExpanded(row) }\"\n [attr.aria-expanded]=\"isExpanded(row)\"\n aria-label=\"Toggle row details\"\n >\n <svg class=\"h-3.5 w-3.5 transition-transform\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M9 18l6-6-6-6\"/>\n </svg>\n </button>\n </td>\n }\n\n <!-- Checkbox / radio -->\n @if (selectable()) {\n <td [ngClass]=\"[rowPadding(), 'w-10']\" (click)=\"$event.stopPropagation()\">\n @if (selectMode() === 'single') {\n <input type=\"radio\" [checked]=\"isSelected(row)\" (change)=\"toggleRow(row)\"\n aria-label=\"Select row\"\n class=\"h-4 w-4 border-gray-300 text-primary-600 focus:ring-primary-500 dark:border-gray-600\" />\n } @else {\n <input type=\"checkbox\" [checked]=\"isSelected(row)\" (change)=\"toggleRow(row)\"\n aria-label=\"Select row\"\n class=\"h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700\" />\n }\n </td>\n }\n\n <!-- Data cells -->\n @for (col of visibleColumns(); track col.key) {\n <td\n [ngClass]=\"getCellClass(col)\"\n [style.max-width]=\"col.maxWidth ?? null\"\n (click)=\"onCellClick(row, col, $event)\"\n (dblclick)=\"startEdit(row, col, $event)\"\n >\n @if (isEditing(row, col)) {\n <!-- Inline edit input -->\n <input\n type=\"text\"\n [dtAutoFocus]=\"true\"\n [value]=\"editDraft()\"\n (input)=\"onEditInput($event)\"\n (blur)=\"saveEdit(row, col)\"\n (keydown.enter)=\"saveEdit(row, col)\"\n (keydown.escape)=\"cancelEdit()\"\n aria-label=\"Edit cell value\"\n class=\"w-full rounded-md border border-primary-400 dark:border-primary-500 bg-white dark:bg-gray-800 px-2 py-0.5 text-sm text-gray-900 dark:text-gray-100 outline-none ring-2 ring-primary-500/20 transition-all\"\n />\n } @else if (col.badge) {\n <span [ngClass]=\"['inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium', badgeClass(row, col)]\">\n {{ cellValue(row, col) }}\n </span>\n } @else {\n {{ cellValue(row, col) }}\n }\n </td>\n }\n\n <!-- Per-row action menu -->\n @if (actions().length > 0) {\n <td [ngClass]=\"[rowPadding(), 'w-12 relative']\" (click)=\"$event.stopPropagation()\">\n @if (visibleActions(row).length > 0) {\n <button\n type=\"button\"\n (click)=\"toggleActionMenu(row, $event)\"\n [attr.aria-expanded]=\"isActionMenuOpen(row)\"\n aria-label=\"Row actions\"\n class=\"inline-flex h-7 w-7 items-center justify-center rounded-md text-gray-500 dark:text-gray-400 opacity-0 group-hover:opacity-100 focus:opacity-100 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-600 dark:hover:text-gray-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 transition-all duration-100\"\n >\n <svg class=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"5\" r=\"1.5\"/>\n <circle cx=\"12\" cy=\"12\" r=\"1.5\"/>\n <circle cx=\"12\" cy=\"19\" r=\"1.5\"/>\n </svg>\n </button>\n\n @if (isActionMenuOpen(row)) {\n <div\n class=\"absolute right-0 z-40 mt-1 w-44 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-xl py-1\"\n (click)=\"$event.stopPropagation()\"\n >\n @for (action of visibleActions(row); track action.label) {\n <button\n type=\"button\"\n (click)=\"runAction(action, row, $event)\"\n [ngClass]=\"[\n 'flex w-full items-center gap-2.5 px-3.5 py-2 text-sm text-left transition-colors duration-75',\n action.danger\n ? 'text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20'\n : 'text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700/50'\n ]\"\n >\n @if (action.iconPath) {\n <svg class=\"h-4 w-4 shrink-0\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\n stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path [attr.d]=\"action.iconPath\" />\n </svg>\n }\n {{ action.label }}\n </button>\n }\n </div>\n }\n }\n </td>\n }\n </tr>\n\n <!-- ─── Expand detail panel ───────────────────────────── -->\n @if (expandable() && isExpanded(row)) {\n <tr class=\"bg-gray-50/80 dark:bg-gray-800/40 border-b border-gray-100 dark:border-gray-800/60\">\n <td [attr.colspan]=\"totalColspan()\" class=\"px-6 py-4\">\n @if (expandRender()) {\n <p class=\"text-sm text-gray-700 dark:text-gray-300 whitespace-pre-wrap\">{{ expandRender()!(row) }}</p>\n } @else {\n <dl class=\"grid grid-cols-2 gap-x-6 gap-y-3 sm:grid-cols-3 lg:grid-cols-4\">\n @for (col of columns(); track col.key) {\n <div class=\"min-w-0\">\n <dt class=\"text-[10px] font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400\">{{ col.label }}</dt>\n <dd class=\"mt-0.5 truncate text-sm font-medium text-gray-800 dark:text-gray-100\">\n @if (col.badge) {\n <span [ngClass]=\"['inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium', badgeClass(row, col)]\">\n {{ cellValue(row, col) }}\n </span>\n } @else {\n {{ cellValue(row, col) }}\n }\n </dd>\n </div>\n }\n </dl>\n }\n </td>\n </tr>\n }\n\n } <!-- end @for -->\n }\n\n </tbody>\n\n <!-- TFOOT — Summary row ────────────────────────────────────────── -->\n @if (showFooter() && hasFooterFns() && !loading() && pagedRows().length > 0) {\n <tfoot class=\"border-t-2 border-gray-200 dark:border-gray-700 bg-gray-50/80 dark:bg-gray-800/40 sticky bottom-0 z-20\">\n <tr>\n @if (expandable()) { <td [ngClass]=\"rowPadding()\"></td> }\n @if (selectable()) { <td [ngClass]=\"rowPadding()\"></td> }\n @for (col of visibleColumns(); track col.key) {\n <td [ngClass]=\"[rowPadding(), 'text-xs font-semibold text-gray-600 dark:text-gray-300', alignClass(col)]\">\n {{ col.footerFn ? col.footerFn(sortedRows()) : '' }}\n </td>\n }\n @if (actions().length > 0) { <td [ngClass]=\"rowPadding()\"></td> }\n </tr>\n </tfoot>\n }\n\n </table>\n </div>\n\n <!-- ─── MOBILE CARD VIEW ────────────────────────────────────────────── -->\n @if (cardOnMobile()) {\n <div class=\"block sm:hidden\">\n @if (loading()) {\n @for (i of skeletonArray(); track i) {\n <div class=\"border-b border-gray-100 dark:border-gray-800/60 p-4 space-y-2\">\n <div class=\"skeleton-wave h-4 w-3/4 rounded\"></div>\n <div class=\"skeleton-wave h-3 w-1/2 rounded mt-2\"></div>\n <div class=\"skeleton-wave h-3 w-2/3 rounded mt-1\"></div>\n </div>\n }\n } @else if (pagedRows().length === 0) {\n <div class=\"px-6 py-12 text-center\">\n <p class=\"text-sm text-gray-500 dark:text-gray-400\">{{ emptyMessage() }}</p>\n </div>\n } @else {\n @for (row of pagedRows(); track trackByRow($index, row)) {\n <div\n class=\"group border-b border-gray-100 dark:border-gray-800/60 p-4 cursor-pointer transition-colors hover:bg-gray-50/80 dark:hover:bg-gray-800/50\"\n [ngClass]=\"isSelected(row) ? 'bg-primary-50/50! dark:bg-primary-950/20!' : ''\"\n (click)=\"onRowClick(row)\"\n >\n <div class=\"flex items-start gap-3\">\n <dl class=\"grid grid-cols-2 gap-x-4 gap-y-2.5 flex-1 min-w-0\">\n @for (col of visibleColumns(); track col.key) {\n <div class=\"min-w-0\">\n <dt class=\"text-[10px] font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400\">{{ col.label }}</dt>\n <dd class=\"mt-0.5 text-sm text-gray-800 dark:text-gray-200 truncate\">\n @if (col.badge) {\n <span [ngClass]=\"['inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium', badgeClass(row, col)]\">\n {{ cellValue(row, col) }}\n </span>\n } @else {\n {{ cellValue(row, col) }}\n }\n </dd>\n </div>\n }\n </dl>\n @if (selectable()) {\n <div class=\"shrink-0 pt-0.5\" (click)=\"$event.stopPropagation()\">\n <input\n [type]=\"selectMode() === 'single' ? 'radio' : 'checkbox'\"\n [checked]=\"isSelected(row)\"\n (change)=\"toggleRow(row)\"\n aria-label=\"Select row\"\n class=\"h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500 dark:border-gray-600\"\n />\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n }\n\n </div> <!-- end table area -->\n\n <!-- ═══════════ PAGINATION FOOTER ═══════════════════════════════════════ -->\n @if (paginated() && !loading()) {\n <div class=\"flex flex-wrap items-center justify-between gap-4 border-t border-gray-200/80 dark:border-gray-800/60 px-4 py-2.5 bg-gray-50/50 dark:bg-gray-800/30\">\n\n <!-- Left: range info + page size -->\n <div class=\"flex flex-wrap items-center gap-3\">\n @if (totalDisplayCount() > 0) {\n <p class=\"text-[12px] text-gray-500 dark:text-gray-400\">\n Showing\n <span class=\"font-medium text-gray-800 dark:text-gray-200\">{{ rangeStart() }}</span>\n –\n <span class=\"font-medium text-gray-800 dark:text-gray-200\">{{ rangeEnd() }}</span>\n of\n <span class=\"font-medium text-gray-800 dark:text-gray-200\">{{ totalDisplayCount() }}</span>\n </p>\n }\n @if (pageSizeOptions().length > 0) {\n <div class=\"flex items-center gap-1.5\">\n <label class=\"text-[12px] text-gray-500 dark:text-gray-400 whitespace-nowrap\"\n for=\"dt-page-size\">Rows</label>\n <select\n id=\"dt-page-size\"\n (change)=\"onPageSizeChange($event)\"\n class=\"h-7 rounded-lg border border-gray-200/80 dark:border-gray-700/60 bg-white dark:bg-gray-800 px-2 text-[12px] text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-primary-500/20\"\n >\n @for (opt of pageSizeOptions(); track opt) {\n <option [value]=\"opt\" [selected]=\"opt === pageSize()\">{{ opt }}</option>\n }\n </select>\n </div>\n }\n </div>\n\n <!-- Right: page navigation -->\n <nav aria-label=\"Table pagination\" class=\"flex items-center gap-1\">\n\n <!-- First page -->\n <button type=\"button\" (click)=\"goTo(1)\" [disabled]=\"currentPage() <= 1\"\n aria-label=\"First page\"\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-lg transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 disabled:cursor-not-allowed disabled:text-gray-300 dark:disabled:text-gray-600 enabled:text-gray-600 enabled:hover:bg-gray-100 dark:enabled:text-gray-300 dark:enabled:hover:bg-gray-700\">\n <svg class=\"h-3.5 w-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M11 17l-5-5 5-5M18 17l-5-5 5-5\"/>\n </svg>\n </button>\n\n <!-- Previous page -->\n <button type=\"button\" (click)=\"goTo(currentPage() - 1)\" [disabled]=\"currentPage() <= 1\"\n aria-label=\"Previous page\"\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-lg transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 disabled:cursor-not-allowed disabled:text-gray-300 dark:disabled:text-gray-600 enabled:text-gray-600 enabled:hover:bg-gray-100 dark:enabled:text-gray-300 dark:enabled:hover:bg-gray-700\">\n <svg class=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M15 18l-6-6 6-6\"/>\n </svg>\n </button>\n\n <!-- Page number pills -->\n @for (page of pageNumbers(); track page) {\n @if (page === -1) {\n <span class=\"px-1 text-xs text-gray-500 dark:text-gray-400\" aria-hidden=\"true\">…</span>\n } @else {\n <button\n type=\"button\"\n (click)=\"goTo(page)\"\n [attr.aria-label]=\"'Page ' + page\"\n [attr.aria-current]=\"currentPage() === page ? 'page' : null\"\n [ngClass]=\"[\n 'h-8 min-w-8 rounded-lg px-1.5 text-sm tabular-nums transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',\n currentPage() === page\n ? 'bg-primary-600 text-white font-semibold shadow-sm'\n : 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'\n ]\"\n >{{ page }}</button>\n }\n }\n\n <!-- Next page -->\n <button type=\"button\" (click)=\"goTo(currentPage() + 1)\" [disabled]=\"currentPage() >= totalPages()\"\n aria-label=\"Next page\"\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-lg transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 disabled:cursor-not-allowed disabled:text-gray-300 dark:disabled:text-gray-600 enabled:text-gray-600 enabled:hover:bg-gray-100 dark:enabled:text-gray-300 dark:enabled:hover:bg-gray-700\">\n <svg class=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M9 18l6-6-6-6\"/>\n </svg>\n </button>\n\n <!-- Last page -->\n <button type=\"button\" (click)=\"goTo(totalPages())\" [disabled]=\"currentPage() >= totalPages()\"\n aria-label=\"Last page\"\n class=\"inline-flex h-8 w-8 items-center justify-center rounded-lg transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 disabled:cursor-not-allowed disabled:text-gray-300 dark:disabled:text-gray-600 enabled:text-gray-600 enabled:hover:bg-gray-100 dark:enabled:text-gray-300 dark:enabled:hover:bg-gray-700\">\n <svg class=\"h-3.5 w-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M13 17l5-5-5-5M6 17l5-5-5-5\"/>\n </svg>\n </button>\n\n </nav>\n </div>\n }\n\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@styloviz/data-table",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Sortable, paginated table with row selection, badge cells and a loading skeleton — plus premium toolbar, column filters, inline editing, expandable rows and bulk actions.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "sazzad-bs23",
|
|
7
|
+
"homepage": "https://styloviz.dev",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/rhossain/angular-tailwind-dashboard-components.git",
|
|
11
|
+
"directory": "projects/data-table-free"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/rhossain/angular-tailwind-dashboard-components/issues"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"@angular/common": ">=21.0.0",
|
|
18
|
+
"@angular/core": ">=21.0.0",
|
|
19
|
+
"@angular/forms": ">=21.0.0"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
|
26
|
+
},
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"module": "fesm2022/styloviz-data-table.mjs",
|
|
29
|
+
"typings": "types/styloviz-data-table.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
"./package.json": {
|
|
32
|
+
"default": "./package.json"
|
|
33
|
+
},
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./types/styloviz-data-table.d.ts",
|
|
36
|
+
"default": "./fesm2022/styloviz-data-table.mjs"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"type": "module",
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"tslib": "^2.3.0"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { OnInit, ElementRef } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
declare class DtAutoFocusDirective implements OnInit {
|
|
5
|
+
private readonly el;
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
constructor(el: ElementRef<HTMLInputElement>);
|
|
8
|
+
ngOnInit(): void;
|
|
9
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DtAutoFocusDirective, never>;
|
|
10
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<DtAutoFocusDirective, "[dtAutoFocus]", never, { "enabled": { "alias": "dtAutoFocus"; "required": false; }; }, {}, never, never, true, never>;
|
|
11
|
+
}
|
|
12
|
+
type SortDirection = 'asc' | 'desc' | null;
|
|
13
|
+
type ColumnAlign = 'left' | 'center' | 'right';
|
|
14
|
+
type TableVariant = 'default' | 'striped' | 'bordered' | 'borderless';
|
|
15
|
+
type TableDensity = 'compact' | 'comfortable' | 'spacious';
|
|
16
|
+
type SelectMode = 'multi' | 'single';
|
|
17
|
+
type FilterType = 'text' | 'select' | 'none';
|
|
18
|
+
type LoadingStyle = 'skeleton' | 'overlay';
|
|
19
|
+
interface TableColumn<T = Record<string, unknown>> {
|
|
20
|
+
/** Unique key matching the row data property. */
|
|
21
|
+
key: string;
|
|
22
|
+
/** Column header label. */
|
|
23
|
+
label: string;
|
|
24
|
+
/** Column horizontal alignment. @default 'left' */
|
|
25
|
+
align?: ColumnAlign;
|
|
26
|
+
/** Allow sorting on this column. @default false */
|
|
27
|
+
sortable?: boolean;
|
|
28
|
+
/** Minimum column width (e.g., '8rem'). */
|
|
29
|
+
minWidth?: string;
|
|
30
|
+
/** Maximum column width with text truncation. */
|
|
31
|
+
maxWidth?: string;
|
|
32
|
+
/** Custom cell render function returning a plain-text string. */
|
|
33
|
+
render?: (row: T, col: TableColumn<T>) => string;
|
|
34
|
+
/** Render cell value as a status badge pill. */
|
|
35
|
+
badge?: boolean;
|
|
36
|
+
/** Map of cell values → Tailwind color classes for badge rendering. */
|
|
37
|
+
badgeColors?: Record<string, string>;
|
|
38
|
+
/** Pin column to 'left' or 'right' (sticky positioning). */
|
|
39
|
+
pinned?: 'left' | 'right';
|
|
40
|
+
/** Tooltip text rendered on the column header info icon. */
|
|
41
|
+
description?: string;
|
|
42
|
+
/** Filter input type shown in the column filter row. @default 'text' */
|
|
43
|
+
filterType?: FilterType;
|
|
44
|
+
/** Options for select-type column filters. */
|
|
45
|
+
filterOptions?: string[];
|
|
46
|
+
/** Summary function shown in the sticky tfoot row. Receives ALL filtered rows. */
|
|
47
|
+
footerFn?: (rows: T[]) => string;
|
|
48
|
+
/** Allow double-click inline cell editing. Emit editComplete to persist. */
|
|
49
|
+
editable?: boolean;
|
|
50
|
+
/** Initially hidden — user can show via the column-toggle panel. */
|
|
51
|
+
hidden?: boolean;
|
|
52
|
+
}
|
|
53
|
+
interface TableRowAction<T = Record<string, unknown>> {
|
|
54
|
+
/** Display label. */
|
|
55
|
+
label: string;
|
|
56
|
+
/** SVG icon path string. */
|
|
57
|
+
iconPath?: string;
|
|
58
|
+
/** Called with the row when the action is triggered. */
|
|
59
|
+
handler: (row: T) => void;
|
|
60
|
+
/** Return true to hide this action for a specific row. */
|
|
61
|
+
hidden?: (row: T) => boolean;
|
|
62
|
+
/** Renders with red / danger styling. */
|
|
63
|
+
danger?: boolean;
|
|
64
|
+
}
|
|
65
|
+
interface BulkAction {
|
|
66
|
+
/** Display label. */
|
|
67
|
+
label: string;
|
|
68
|
+
/** SVG icon path string. */
|
|
69
|
+
iconPath?: string;
|
|
70
|
+
/** Renders with red / danger styling. */
|
|
71
|
+
danger?: boolean;
|
|
72
|
+
/** Optional inline handler called with the selected IDs. */
|
|
73
|
+
handler?: (ids: Set<string | number>) => void;
|
|
74
|
+
}
|
|
75
|
+
interface SortState {
|
|
76
|
+
key: string;
|
|
77
|
+
direction: SortDirection;
|
|
78
|
+
}
|
|
79
|
+
interface CellClickEvent<T = Record<string, unknown>> {
|
|
80
|
+
row: T;
|
|
81
|
+
column: TableColumn<T>;
|
|
82
|
+
value: string;
|
|
83
|
+
}
|
|
84
|
+
interface EditCompleteEvent<T = Record<string, unknown>> {
|
|
85
|
+
row: T;
|
|
86
|
+
column: TableColumn<T>;
|
|
87
|
+
oldValue: string;
|
|
88
|
+
newValue: string;
|
|
89
|
+
}
|
|
90
|
+
interface ServerPageEvent {
|
|
91
|
+
page: number;
|
|
92
|
+
pageSize: number;
|
|
93
|
+
sorts: SortState[];
|
|
94
|
+
filters: Record<string, string>;
|
|
95
|
+
search: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* DataTableComponent v2 — Premium feature-rich data table.
|
|
99
|
+
*
|
|
100
|
+
* New in v2:
|
|
101
|
+
* - Visual variants: default | striped | bordered | borderless
|
|
102
|
+
* - Density presets: compact | comfortable | spacious (+ toolbar toggle)
|
|
103
|
+
* - Built-in toolbar: title, global search, column toggle, density picker, CSV export
|
|
104
|
+
* - Multi-column sort via Shift+click
|
|
105
|
+
* - Per-column text/select filters with active filter chips
|
|
106
|
+
* - Expandable rows with auto key-value detail panel
|
|
107
|
+
* - Inline cell editing (double-click) → emits editComplete
|
|
108
|
+
* - Bulk actions toolbar
|
|
109
|
+
* - Footer summary row via column footerFn
|
|
110
|
+
* - Single-select mode (radio buttons)
|
|
111
|
+
* - Status-based row coloring via rowClassFn
|
|
112
|
+
* - Sticky / pinned columns (left or right)
|
|
113
|
+
* - Server-side mode (pagination + sort + filter emitted)
|
|
114
|
+
* - Loading overlay (alternative to skeleton)
|
|
115
|
+
* - Mobile card layout
|
|
116
|
+
* - First/Last page buttons + page-number pills
|
|
117
|
+
* - CSV export
|
|
118
|
+
*/
|
|
119
|
+
declare class SvDataTableComponent<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
120
|
+
/** Column definitions (key, header, alignment, optional cell template). */
|
|
121
|
+
columns: _angular_core.InputSignal<TableColumn<T>[]>;
|
|
122
|
+
/** Row data to display. */
|
|
123
|
+
rows: _angular_core.InputSignal<T[]>;
|
|
124
|
+
/** Property used as each row's unique id (for selection/tracking). @default 'id' */
|
|
125
|
+
rowKey: _angular_core.InputSignal<string>;
|
|
126
|
+
/** Enable row selection. @default false */
|
|
127
|
+
selectable: _angular_core.InputSignal<boolean>;
|
|
128
|
+
/** 'multi' = checkboxes, 'single' = radio buttons. @default 'multi' */
|
|
129
|
+
selectMode: _angular_core.InputSignal<SelectMode>;
|
|
130
|
+
/** Selected row ids (two-way). */
|
|
131
|
+
selectedIds: _angular_core.ModelSignal<Set<string | number>>;
|
|
132
|
+
/** Per-row action buttons. */
|
|
133
|
+
actions: _angular_core.InputSignal<TableRowAction<T>[]>;
|
|
134
|
+
/** Bulk actions applied to selected rows. */
|
|
135
|
+
bulkActions: _angular_core.InputSignal<BulkAction[]>;
|
|
136
|
+
/** Show a loading state. @default false */
|
|
137
|
+
loading: _angular_core.InputSignal<boolean>;
|
|
138
|
+
/** 'skeleton' rows or full-table 'overlay' spinner. @default 'skeleton' */
|
|
139
|
+
loadingStyle: _angular_core.InputSignal<LoadingStyle>;
|
|
140
|
+
/** Number of skeleton rows while loading. @default 5 */
|
|
141
|
+
skeletonRows: _angular_core.InputSignal<number>;
|
|
142
|
+
/** Enable client-side pagination. @default true */
|
|
143
|
+
paginated: _angular_core.InputSignal<boolean>;
|
|
144
|
+
/** Rows per page (two-way). @default 10 */
|
|
145
|
+
pageSize: _angular_core.ModelSignal<number>;
|
|
146
|
+
/** Page-size options offered in the toolbar. @default [10,25,50] */
|
|
147
|
+
pageSizeOptions: _angular_core.InputSignal<number[]>;
|
|
148
|
+
/** Server-side mode: disables client filtering/sorting/paging. */
|
|
149
|
+
serverSide: _angular_core.InputSignal<boolean>;
|
|
150
|
+
/** Total row count when in server-side mode. */
|
|
151
|
+
totalCount: _angular_core.InputSignal<number | null>;
|
|
152
|
+
/** Table style variant. @default 'default' */
|
|
153
|
+
variant: _angular_core.InputSignal<TableVariant>;
|
|
154
|
+
/** Row density: comfortable, compact, etc. @default 'comfortable' */
|
|
155
|
+
density: _angular_core.InputSignal<TableDensity>;
|
|
156
|
+
/** Deprecated shorthand for compact density. @deprecated Use density="compact". */
|
|
157
|
+
compact: _angular_core.InputSignal<boolean>;
|
|
158
|
+
/** Max body height (CSS length) before the table scrolls. */
|
|
159
|
+
maxHeight: _angular_core.InputSignal<string | null>;
|
|
160
|
+
/** Show a hint when the table is horizontally scrollable. @default true */
|
|
161
|
+
showScrollHint: _angular_core.InputSignal<boolean>;
|
|
162
|
+
/** Callback returning extra Tailwind classes for a given row. */
|
|
163
|
+
rowClassFn: _angular_core.InputSignal<((row: T) => string) | null>;
|
|
164
|
+
/** Show the toolbar above the table. @default false */
|
|
165
|
+
showToolbar: _angular_core.InputSignal<boolean>;
|
|
166
|
+
/** Title shown in the toolbar. */
|
|
167
|
+
tableTitle: _angular_core.InputSignal<string>;
|
|
168
|
+
/** Show a search box in the toolbar. @default false */
|
|
169
|
+
searchable: _angular_core.InputSignal<boolean>;
|
|
170
|
+
/** Show the column visibility toggle. @default false */
|
|
171
|
+
showColumnToggle: _angular_core.InputSignal<boolean>;
|
|
172
|
+
/** Show an export (CSV) control. @default false */
|
|
173
|
+
exportable: _angular_core.InputSignal<boolean>;
|
|
174
|
+
/** Show the density toggle. @default false */
|
|
175
|
+
showDensityToggle: _angular_core.InputSignal<boolean>;
|
|
176
|
+
/** Enable per-column filters. @default false */
|
|
177
|
+
columnFilters: _angular_core.InputSignal<boolean>;
|
|
178
|
+
/** Allow sorting by multiple columns. @default false */
|
|
179
|
+
multiSort: _angular_core.InputSignal<boolean>;
|
|
180
|
+
/** Enable expandable rows. @default false */
|
|
181
|
+
expandable: _angular_core.InputSignal<boolean>;
|
|
182
|
+
/** Renderer for the expanded row detail. */
|
|
183
|
+
expandRender: _angular_core.InputSignal<((row: T) => string) | null>;
|
|
184
|
+
/** Show a table footer row. @default false */
|
|
185
|
+
showFooter: _angular_core.InputSignal<boolean>;
|
|
186
|
+
/** Render rows as cards on small screens. @default false */
|
|
187
|
+
cardOnMobile: _angular_core.InputSignal<boolean>;
|
|
188
|
+
/** Message shown when there are no rows. @default 'No data to display.' */
|
|
189
|
+
emptyMessage: _angular_core.InputSignal<string>;
|
|
190
|
+
/** Extra CSS classes for the root element. */
|
|
191
|
+
customClass: _angular_core.InputSignal<string>;
|
|
192
|
+
/** Emitted with the new sort state when sorting changes. */
|
|
193
|
+
sortChange: _angular_core.OutputEmitterRef<SortState[]>;
|
|
194
|
+
/** Emitted with the set of selected row ids. */
|
|
195
|
+
selectionChange: _angular_core.OutputEmitterRef<Set<string | number>>;
|
|
196
|
+
/** Emitted with the clicked row. */
|
|
197
|
+
rowClick: _angular_core.OutputEmitterRef<T>;
|
|
198
|
+
/** Emitted with the clicked cell context. */
|
|
199
|
+
cellClick: _angular_core.OutputEmitterRef<CellClickEvent<T>>;
|
|
200
|
+
/** Emitted when an inline cell edit completes (Pro). */
|
|
201
|
+
editComplete: _angular_core.OutputEmitterRef<EditCompleteEvent<T>>;
|
|
202
|
+
/** Emitted in server-side mode when the page/size changes. */
|
|
203
|
+
serverPageChange: _angular_core.OutputEmitterRef<ServerPageEvent>;
|
|
204
|
+
/** Emitted with the row being expanded. */
|
|
205
|
+
rowExpand: _angular_core.OutputEmitterRef<T>;
|
|
206
|
+
/** Emitted with the bulk action and the selected row ids. */
|
|
207
|
+
bulkActionClick: _angular_core.OutputEmitterRef<{
|
|
208
|
+
action: BulkAction;
|
|
209
|
+
selectedIds: Set<string | number>;
|
|
210
|
+
}>;
|
|
211
|
+
readonly sorts: _angular_core.WritableSignal<SortState[]>;
|
|
212
|
+
readonly currentPage: _angular_core.WritableSignal<number>;
|
|
213
|
+
readonly openActionRowId: _angular_core.WritableSignal<string | number | null>;
|
|
214
|
+
readonly searchQuery: _angular_core.WritableSignal<string>;
|
|
215
|
+
readonly columnFiltersRecord: _angular_core.WritableSignal<Record<string, string>>;
|
|
216
|
+
readonly hiddenColumnKeys: _angular_core.WritableSignal<Set<string>>;
|
|
217
|
+
readonly expandedRowIds: _angular_core.WritableSignal<Set<string | number>>;
|
|
218
|
+
readonly editingCell: _angular_core.WritableSignal<{
|
|
219
|
+
rowId: string | number;
|
|
220
|
+
key: string;
|
|
221
|
+
} | null>;
|
|
222
|
+
readonly editDraft: _angular_core.WritableSignal<string>;
|
|
223
|
+
readonly columnToggleOpen: _angular_core.WritableSignal<boolean>;
|
|
224
|
+
readonly densityOverride: _angular_core.WritableSignal<TableDensity | null>;
|
|
225
|
+
readonly densityOptions: readonly {
|
|
226
|
+
value: TableDensity;
|
|
227
|
+
label: string;
|
|
228
|
+
icon: string;
|
|
229
|
+
}[];
|
|
230
|
+
visibleColumns: _angular_core.Signal<TableColumn<T>[]>;
|
|
231
|
+
effectiveDensity: _angular_core.Signal<TableDensity>;
|
|
232
|
+
rowPadding: _angular_core.Signal<string>;
|
|
233
|
+
headerPadding: _angular_core.Signal<string>;
|
|
234
|
+
cellTextSize: _angular_core.Signal<string>;
|
|
235
|
+
readonly filteredRows: _angular_core.Signal<T[]>;
|
|
236
|
+
readonly sortedRows: _angular_core.Signal<T[]>;
|
|
237
|
+
pagedRows: _angular_core.Signal<T[]>;
|
|
238
|
+
totalDisplayCount: _angular_core.Signal<number>;
|
|
239
|
+
totalPages: _angular_core.Signal<number>;
|
|
240
|
+
rangeStart: _angular_core.Signal<number>;
|
|
241
|
+
rangeEnd: _angular_core.Signal<number>;
|
|
242
|
+
allSelected: _angular_core.Signal<boolean>;
|
|
243
|
+
someSelected: _angular_core.Signal<boolean>;
|
|
244
|
+
skeletonArray: _angular_core.Signal<number[]>;
|
|
245
|
+
activeFilterChips: _angular_core.Signal<{
|
|
246
|
+
key: string;
|
|
247
|
+
label: string;
|
|
248
|
+
value: string;
|
|
249
|
+
}[]>;
|
|
250
|
+
hasFooterFns: _angular_core.Signal<boolean>;
|
|
251
|
+
totalColspan: _angular_core.Signal<number>;
|
|
252
|
+
pageNumbers: _angular_core.Signal<number[]>;
|
|
253
|
+
rootClass: _angular_core.Signal<string>;
|
|
254
|
+
tableClass: _angular_core.Signal<string>;
|
|
255
|
+
tbodyClass: _angular_core.Signal<string>;
|
|
256
|
+
onSort(col: TableColumn<T>, event: MouseEvent): void;
|
|
257
|
+
sortIcon(col: TableColumn<T>): string;
|
|
258
|
+
isColumnSorted(col: TableColumn<T>): boolean;
|
|
259
|
+
getSortIndex(col: TableColumn<T>): number;
|
|
260
|
+
getSortAria(col: TableColumn<T>): string | null;
|
|
261
|
+
getRowId(row: T): string | number;
|
|
262
|
+
isSelected(row: T): boolean;
|
|
263
|
+
toggleRow(row: T): void;
|
|
264
|
+
toggleAll(): void;
|
|
265
|
+
clearSelection(): void;
|
|
266
|
+
visibleActions(row: T): TableRowAction<T>[];
|
|
267
|
+
toggleActionMenu(row: T, event: MouseEvent): void;
|
|
268
|
+
isActionMenuOpen(row: T): boolean;
|
|
269
|
+
runAction(action: TableRowAction<T>, row: T, event: MouseEvent): void;
|
|
270
|
+
closeAllMenus(): void;
|
|
271
|
+
runBulkAction(action: BulkAction): void;
|
|
272
|
+
cellValue(row: T, col: TableColumn<T>): string;
|
|
273
|
+
badgeClass(row: T, col: TableColumn<T>): string;
|
|
274
|
+
alignClass(col: TableColumn<T>): string;
|
|
275
|
+
getRowClass(row: T, index: number): string[];
|
|
276
|
+
getHeaderCellClass(col: TableColumn<T>): string[];
|
|
277
|
+
getCellClass(col: TableColumn<T>): string[];
|
|
278
|
+
toggleExpand(row: T, event: MouseEvent): void;
|
|
279
|
+
isExpanded(row: T): boolean;
|
|
280
|
+
startEdit(row: T, col: TableColumn<T>, event: MouseEvent): void;
|
|
281
|
+
isEditing(row: T, col: TableColumn<T>): boolean;
|
|
282
|
+
onEditInput(event: Event): void;
|
|
283
|
+
saveEdit(row: T, col: TableColumn<T>): void;
|
|
284
|
+
cancelEdit(): void;
|
|
285
|
+
onRowClick(row: T): void;
|
|
286
|
+
onCellClick(row: T, col: TableColumn<T>, event: MouseEvent): void;
|
|
287
|
+
getColFilter(key: string): string;
|
|
288
|
+
setColFilter(key: string, value: string): void;
|
|
289
|
+
onColumnFilterChange(key: string, event: Event): void;
|
|
290
|
+
clearFilter(key: string): void;
|
|
291
|
+
clearAllFilters(): void;
|
|
292
|
+
onSearchInput(event: Event): void;
|
|
293
|
+
toggleColumnVisibility(key: string): void;
|
|
294
|
+
goTo(page: number): void;
|
|
295
|
+
onPageSizeChange(event: Event): void;
|
|
296
|
+
private emitServerEvent;
|
|
297
|
+
exportCsv(): void;
|
|
298
|
+
trackByKey(_: number, col: TableColumn<T>): string;
|
|
299
|
+
trackByRow(_: number, row: T): string | number;
|
|
300
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SvDataTableComponent<any>, never>;
|
|
301
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SvDataTableComponent<any>, "sv-data-table", never, { "columns": { "alias": "columns"; "required": false; "isSignal": true; }; "rows": { "alias": "rows"; "required": false; "isSignal": true; }; "rowKey": { "alias": "rowKey"; "required": false; "isSignal": true; }; "selectable": { "alias": "selectable"; "required": false; "isSignal": true; }; "selectMode": { "alias": "selectMode"; "required": false; "isSignal": true; }; "selectedIds": { "alias": "selectedIds"; "required": false; "isSignal": true; }; "actions": { "alias": "actions"; "required": false; "isSignal": true; }; "bulkActions": { "alias": "bulkActions"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "loadingStyle": { "alias": "loadingStyle"; "required": false; "isSignal": true; }; "skeletonRows": { "alias": "skeletonRows"; "required": false; "isSignal": true; }; "paginated": { "alias": "paginated"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "serverSide": { "alias": "serverSide"; "required": false; "isSignal": true; }; "totalCount": { "alias": "totalCount"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "density": { "alias": "density"; "required": false; "isSignal": true; }; "compact": { "alias": "compact"; "required": false; "isSignal": true; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "showScrollHint": { "alias": "showScrollHint"; "required": false; "isSignal": true; }; "rowClassFn": { "alias": "rowClassFn"; "required": false; "isSignal": true; }; "showToolbar": { "alias": "showToolbar"; "required": false; "isSignal": true; }; "tableTitle": { "alias": "tableTitle"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "showColumnToggle": { "alias": "showColumnToggle"; "required": false; "isSignal": true; }; "exportable": { "alias": "exportable"; "required": false; "isSignal": true; }; "showDensityToggle": { "alias": "showDensityToggle"; "required": false; "isSignal": true; }; "columnFilters": { "alias": "columnFilters"; "required": false; "isSignal": true; }; "multiSort": { "alias": "multiSort"; "required": false; "isSignal": true; }; "expandable": { "alias": "expandable"; "required": false; "isSignal": true; }; "expandRender": { "alias": "expandRender"; "required": false; "isSignal": true; }; "showFooter": { "alias": "showFooter"; "required": false; "isSignal": true; }; "cardOnMobile": { "alias": "cardOnMobile"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "customClass": { "alias": "customClass"; "required": false; "isSignal": true; }; }, { "selectedIds": "selectedIdsChange"; "pageSize": "pageSizeChange"; "sortChange": "sortChange"; "selectionChange": "selectionChange"; "rowClick": "rowClick"; "cellClick": "cellClick"; "editComplete": "editComplete"; "serverPageChange": "serverPageChange"; "rowExpand": "rowExpand"; "bulkActionClick": "bulkActionClick"; }, never, never, true, never>;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export { DtAutoFocusDirective, SvDataTableComponent };
|
|
305
|
+
export type { BulkAction, CellClickEvent, ColumnAlign, EditCompleteEvent, FilterType, LoadingStyle, SelectMode, ServerPageEvent, SortDirection, SortState, TableColumn, TableDensity, TableRowAction, TableVariant };
|