@egose/shadcn-theme-ng-tw 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1449 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, computed, ChangeDetectionStrategy, Component, model, output, signal, linkedSignal, effect, NgModule } from '@angular/core';
3
+ import { NgTemplateOutlet } from '@angular/common';
4
+ import { HlmButton } from '@egose/shadcn-theme-ng-tw/button';
5
+ import { HlmCard, HlmCardContent } from '@egose/shadcn-theme-ng-tw/card';
6
+ import { HlmIcon } from '@egose/shadcn-theme-ng-tw/icon';
7
+ import { HlmInput } from '@egose/shadcn-theme-ng-tw/input';
8
+ import { HlmSpinner } from '@egose/shadcn-theme-ng-tw/spinner';
9
+ import * as i2 from '@egose/shadcn-theme-ng-tw/table';
10
+ import { HlmTableImports } from '@egose/shadcn-theme-ng-tw/table';
11
+ import { HlmToggleGroup, HlmToggleGroupItem } from '@egose/shadcn-theme-ng-tw/toggle-group';
12
+ import { hlm } from '@egose/shadcn-theme-ng-tw/utils';
13
+ import { NgIcon, provideIcons } from '@ng-icons/core';
14
+ import { lucideChevronsRight, lucideChevronsLeft, lucideChevronRight, lucideChevronLeft, lucideSettings2, lucideLayoutList, lucideLayoutGrid, lucideChevronsUpDown, lucideArrowUp, lucideArrowDown, lucideEyeOff } from '@ng-icons/lucide';
15
+ import * as i1$2 from '@tanstack/angular-table';
16
+ import { createFilteredRowModel, createPaginatedRowModel, createSortedRowModel, filterFn_includesString, sortFn_alphanumeric, sortFn_text, tableFeatures, rowSortingFeature, rowSelectionFeature, rowPaginationFeature, columnVisibilityFeature, columnFilteringFeature, injectTableContext, createColumnHelper, injectTable, TanStackTable, FlexRender } from '@tanstack/angular-table';
17
+ import * as i1 from '@egose/shadcn-theme-ng-tw/select';
18
+ import { HlmSelectImports } from '@egose/shadcn-theme-ng-tw/select';
19
+ import * as i1$1 from '@egose/shadcn-theme-ng-tw/dropdown-menu';
20
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng-tw/dropdown-menu';
21
+ import { HlmCheckbox } from '@egose/shadcn-theme-ng-tw/checkbox';
22
+
23
+ // Explicit instantiations keep the inferred feature-registry type free of `any`
24
+ // (the factories default their generics to `any` when called bare, which would
25
+ // leak into our emitted declarations and trip package validation).
26
+ const filteredRowModel = createFilteredRowModel();
27
+ const paginatedRowModel = createPaginatedRowModel();
28
+ const sortedRowModel = createSortedRowModel();
29
+ const includesString = filterFn_includesString;
30
+ const alphanumeric = sortFn_alphanumeric;
31
+ const text = sortFn_text;
32
+ /**
33
+ * Default feature set for `EgDataTable`.
34
+ *
35
+ * Mirrors the Spartan data-table guide: filtering, visibility, pagination,
36
+ * selection and sorting are opted in, with the built-in `includesString`
37
+ * filter and `alphanumeric` / `text` sort functions registered. Anything a
38
+ * caller does not need can be dropped by passing a custom `features` input.
39
+ *
40
+ * Keep this reference stable (module scope) — `injectTable()` re-evaluates
41
+ * its initializer on every signal read, so recreating features per render
42
+ * would thrash the table instance.
43
+ */
44
+ const defaultEgDataTableFeatures = tableFeatures({
45
+ columnFilteringFeature,
46
+ columnVisibilityFeature,
47
+ rowPaginationFeature,
48
+ rowSelectionFeature,
49
+ rowSortingFeature,
50
+ filteredRowModel,
51
+ paginatedRowModel,
52
+ sortedRowModel,
53
+ filterFns: { includesString },
54
+ sortFns: { alphanumeric, text },
55
+ columnMeta: {},
56
+ });
57
+
58
+ class EgDataTablePagination {
59
+ /** Show the `selected of total` status text. Set false when the host renders its own status. */
60
+ showStatus = input(true, /* @ts-ignore */
61
+ ...(ngDevMode ? [{ debugName: "showStatus" }] : /* istanbul ignore next */ []));
62
+ /** Show the rows-per-page selector. */
63
+ showPageSize = input(true, /* @ts-ignore */
64
+ ...(ngDevMode ? [{ debugName: "showPageSize" }] : /* istanbul ignore next */ []));
65
+ /** Page-size options. The table's current size is appended when absent. */
66
+ pageSizes = input([10, 20, 30, 40, 50], /* @ts-ignore */
67
+ ...(ngDevMode ? [{ debugName: "pageSizes" }] : /* istanbul ignore next */ []));
68
+ /** Navigation-button content: icons only, text labels only, or both. */
69
+ navMode = input('icons', /* @ts-ignore */
70
+ ...(ngDevMode ? [{ debugName: "navMode" }] : /* istanbul ignore next */ []));
71
+ /** Density of buttons, trigger, and meta texts. Type scale of buttons is untouched. */
72
+ size = input('default', /* @ts-ignore */
73
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
74
+ _table = injectTableContext();
75
+ _pageCount = computed(() => this._table().getPageCount(), /* @ts-ignore */
76
+ ...(ngDevMode ? [{ debugName: "_pageCount" }] : /* istanbul ignore next */ []));
77
+ // `atoms` are signal-backed, so the pager stays in sync with the table.
78
+ _pageIndex = computed(() => this._table().atoms.pagination.get().pageIndex, /* @ts-ignore */
79
+ ...(ngDevMode ? [{ debugName: "_pageIndex" }] : /* istanbul ignore next */ []));
80
+ _pageSize = computed(() => this._table().atoms.pagination.get().pageSize, /* @ts-ignore */
81
+ ...(ngDevMode ? [{ debugName: "_pageSize" }] : /* istanbul ignore next */ []));
82
+ _canPrevious = computed(() => this._table().getCanPreviousPage(), /* @ts-ignore */
83
+ ...(ngDevMode ? [{ debugName: "_canPrevious" }] : /* istanbul ignore next */ []));
84
+ _canNext = computed(() => this._table().getCanNextPage(), /* @ts-ignore */
85
+ ...(ngDevMode ? [{ debugName: "_canNext" }] : /* istanbul ignore next */ []));
86
+ _showPager = computed(() => this._pageCount() > 1, /* @ts-ignore */
87
+ ...(ngDevMode ? [{ debugName: "_showPager" }] : /* istanbul ignore next */ []));
88
+ /** Icon-only buttons stay square; labeled buttons size to their text. Merged by `hlmButton` via the `class` binding. */
89
+ _navButtonClass = computed(() => hlm(this.size() === 'sm' ? 'tw:h-7 tw:p-0' : this.size() === 'lg' ? 'tw:h-9 tw:p-0' : 'tw:h-8 tw:p-0', this.navMode() === 'icons'
90
+ ? this.size() === 'sm'
91
+ ? 'tw:w-7'
92
+ : this.size() === 'lg'
93
+ ? 'tw:w-9'
94
+ : 'tw:w-8'
95
+ : 'tw:gap-1 tw:px-2'), /* @ts-ignore */
96
+ ...(ngDevMode ? [{ debugName: "_navButtonClass" }] : /* istanbul ignore next */ []));
97
+ /** First/last buttons stay desktop-only. */
98
+ _edgeNavButtonClass = computed(() => hlm('tw:hidden tw:lg:flex', this._navButtonClass()), /* @ts-ignore */
99
+ ...(ngDevMode ? [{ debugName: "_edgeNavButtonClass" }] : /* istanbul ignore next */ []));
100
+ /** Meta-text size per density (status, page indicator, rows-per-page label). */
101
+ _metaTextClass = computed(() => this.size() === 'sm' ? 'tw:text-xs' : this.size() === 'lg' ? 'tw:text-base' : 'tw:text-sm', /* @ts-ignore */
102
+ ...(ngDevMode ? [{ debugName: "_metaTextClass" }] : /* istanbul ignore next */ []));
103
+ _pageSizeOptions = computed(() => {
104
+ const sizes = this.pageSizes();
105
+ const current = this._pageSize();
106
+ if (sizes.includes(current))
107
+ return sizes;
108
+ return [...sizes, current].sort((left, right) => left - right);
109
+ }, /* @ts-ignore */
110
+ ...(ngDevMode ? [{ debugName: "_pageSizeOptions" }] : /* istanbul ignore next */ []));
111
+ _statusText = computed(() => {
112
+ const table = this._table();
113
+ const selected = table.getFilteredSelectedRowModel().rows.length;
114
+ const total = table.getFilteredRowModel().rows.length;
115
+ if (selected > 0)
116
+ return `${selected} of ${total} row(s) selected.`;
117
+ return `${total} row(s).`;
118
+ }, /* @ts-ignore */
119
+ ...(ngDevMode ? [{ debugName: "_statusText" }] : /* istanbul ignore next */ []));
120
+ _onPageSizeChange(value) {
121
+ this._table().setPageSize(Number(value));
122
+ }
123
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgDataTablePagination, deps: [], target: i0.ɵɵFactoryTarget.Component });
124
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgDataTablePagination, isStandalone: true, selector: "eg-data-table-pagination", inputs: { showStatus: { classPropertyName: "showStatus", publicName: "showStatus", isSignal: true, isRequired: false, transformFunction: null }, showPageSize: { classPropertyName: "showPageSize", publicName: "showPageSize", isSignal: true, isRequired: false, transformFunction: null }, pageSizes: { classPropertyName: "pageSizes", publicName: "pageSizes", isSignal: true, isRequired: false, transformFunction: null }, navMode: { classPropertyName: "navMode", publicName: "navMode", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideIcons({ lucideChevronLeft, lucideChevronRight, lucideChevronsLeft, lucideChevronsRight })], ngImport: i0, template: `
125
+ <div class="tw:flex tw:flex-wrap tw:items-center tw:justify-between tw:gap-2 tw:px-2">
126
+ @if (showStatus()) {
127
+ <div class="tw:text-muted-foreground tw:flex-1 {{ _metaTextClass() }}">{{ _statusText() }}</div>
128
+ }
129
+ <div class="tw:flex tw:flex-wrap tw:items-center tw:gap-x-6 tw:gap-y-2 tw:lg:gap-x-8">
130
+ @if (showPageSize()) {
131
+ <div class="tw:flex tw:items-center tw:gap-2">
132
+ <p class="tw:font-medium {{ _metaTextClass() }}">Rows per page</p>
133
+ <div hlmSelect class="tw:inline-flex" [value]="_pageSize()" (valueChange)="_onPageSizeChange($event)">
134
+ <hlm-select-trigger class="tw:w-[70px]" [size]="size() === 'sm' ? 'sm' : 'default'">
135
+ <hlm-select-value />
136
+ </hlm-select-trigger>
137
+ <hlm-select-content *hlmSelectPortal>
138
+ @for (size of _pageSizeOptions(); track size) {
139
+ <hlm-select-item [value]="size">{{ size }}</hlm-select-item>
140
+ }
141
+ </hlm-select-content>
142
+ </div>
143
+ </div>
144
+ }
145
+ @if (_showPager()) {
146
+ <div class="tw:flex tw:w-[100px] tw:items-center tw:justify-center tw:font-medium {{ _metaTextClass() }}">
147
+ Page {{ _pageIndex() + 1 }} of {{ _pageCount() }}
148
+ </div>
149
+ <div class="tw:flex tw:items-center tw:gap-2">
150
+ <button
151
+ hlmButton
152
+ variant="secondary"
153
+ appearance="outline"
154
+ size="sm"
155
+ [class]="_edgeNavButtonClass()"
156
+ type="button"
157
+ [disabled]="!_canPrevious()"
158
+ (click)="_table().setPageIndex(0)"
159
+ aria-label="Go to first page"
160
+ >
161
+ @if (navMode() !== 'text') {
162
+ <ng-icon hlm size="sm" name="lucideChevronsLeft" />
163
+ }
164
+ @if (navMode() !== 'icons') {
165
+ <span>First</span>
166
+ }
167
+ </button>
168
+ <button
169
+ hlmButton
170
+ variant="secondary"
171
+ appearance="outline"
172
+ size="sm"
173
+ [class]="_navButtonClass()"
174
+ type="button"
175
+ [disabled]="!_canPrevious()"
176
+ (click)="_table().previousPage()"
177
+ aria-label="Go to previous page"
178
+ >
179
+ @if (navMode() !== 'text') {
180
+ <ng-icon hlm size="sm" name="lucideChevronLeft" />
181
+ }
182
+ @if (navMode() !== 'icons') {
183
+ <span>Previous</span>
184
+ }
185
+ </button>
186
+ <button
187
+ hlmButton
188
+ variant="secondary"
189
+ appearance="outline"
190
+ size="sm"
191
+ [class]="_navButtonClass()"
192
+ type="button"
193
+ [disabled]="!_canNext()"
194
+ (click)="_table().nextPage()"
195
+ aria-label="Go to next page"
196
+ >
197
+ @if (navMode() !== 'icons') {
198
+ <span>Next</span>
199
+ }
200
+ @if (navMode() !== 'text') {
201
+ <ng-icon hlm size="sm" name="lucideChevronRight" />
202
+ }
203
+ </button>
204
+ <button
205
+ hlmButton
206
+ variant="secondary"
207
+ appearance="outline"
208
+ size="sm"
209
+ [class]="_edgeNavButtonClass()"
210
+ type="button"
211
+ [disabled]="!_canNext()"
212
+ (click)="_table().setPageIndex(_pageCount() - 1)"
213
+ aria-label="Go to last page"
214
+ >
215
+ @if (navMode() !== 'icons') {
216
+ <span>Last</span>
217
+ }
218
+ @if (navMode() !== 'text') {
219
+ <ng-icon hlm size="sm" name="lucideChevronsRight" />
220
+ }
221
+ </button>
222
+ </div>
223
+ }
224
+ </div>
225
+ </div>
226
+ `, isInline: true, dependencies: [{ kind: "component", type: HlmButton, selector: "button[hlmButton], a[hlmButton]", inputs: ["variant", "size", "appearance", "loading", "icon", "iconPosition", "class", "spinnerUserClass", "disabled", "type"] }, { kind: "directive", type: HlmIcon, selector: "ng-icon[hlm]", inputs: ["size"] }, { kind: "directive", type: i1.HlmSelect, selector: "[hlmSelect],hlm-select" }, { kind: "component", type: i1.HlmSelectContent, selector: "hlm-select-content", inputs: ["showScroll"] }, { kind: "component", type: i1.HlmSelectItem, selector: "hlm-select-item" }, { kind: "directive", type: i1.HlmSelectPortal, selector: "[hlmSelectPortal]" }, { kind: "component", type: i1.HlmSelectTrigger, selector: "hlm-select-trigger", inputs: ["class", "buttonId", "ariaDescribedby", "wrapperDisabled", "size", "forceInvalid"] }, { kind: "directive", type: i1.HlmSelectValue, selector: "[hlmSelectValue],hlm-select-value" }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
227
+ }
228
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgDataTablePagination, decorators: [{
229
+ type: Component,
230
+ args: [{
231
+ selector: 'eg-data-table-pagination',
232
+ imports: [HlmButton, HlmIcon, HlmSelectImports, NgIcon],
233
+ providers: [provideIcons({ lucideChevronLeft, lucideChevronRight, lucideChevronsLeft, lucideChevronsRight })],
234
+ template: `
235
+ <div class="tw:flex tw:flex-wrap tw:items-center tw:justify-between tw:gap-2 tw:px-2">
236
+ @if (showStatus()) {
237
+ <div class="tw:text-muted-foreground tw:flex-1 {{ _metaTextClass() }}">{{ _statusText() }}</div>
238
+ }
239
+ <div class="tw:flex tw:flex-wrap tw:items-center tw:gap-x-6 tw:gap-y-2 tw:lg:gap-x-8">
240
+ @if (showPageSize()) {
241
+ <div class="tw:flex tw:items-center tw:gap-2">
242
+ <p class="tw:font-medium {{ _metaTextClass() }}">Rows per page</p>
243
+ <div hlmSelect class="tw:inline-flex" [value]="_pageSize()" (valueChange)="_onPageSizeChange($event)">
244
+ <hlm-select-trigger class="tw:w-[70px]" [size]="size() === 'sm' ? 'sm' : 'default'">
245
+ <hlm-select-value />
246
+ </hlm-select-trigger>
247
+ <hlm-select-content *hlmSelectPortal>
248
+ @for (size of _pageSizeOptions(); track size) {
249
+ <hlm-select-item [value]="size">{{ size }}</hlm-select-item>
250
+ }
251
+ </hlm-select-content>
252
+ </div>
253
+ </div>
254
+ }
255
+ @if (_showPager()) {
256
+ <div class="tw:flex tw:w-[100px] tw:items-center tw:justify-center tw:font-medium {{ _metaTextClass() }}">
257
+ Page {{ _pageIndex() + 1 }} of {{ _pageCount() }}
258
+ </div>
259
+ <div class="tw:flex tw:items-center tw:gap-2">
260
+ <button
261
+ hlmButton
262
+ variant="secondary"
263
+ appearance="outline"
264
+ size="sm"
265
+ [class]="_edgeNavButtonClass()"
266
+ type="button"
267
+ [disabled]="!_canPrevious()"
268
+ (click)="_table().setPageIndex(0)"
269
+ aria-label="Go to first page"
270
+ >
271
+ @if (navMode() !== 'text') {
272
+ <ng-icon hlm size="sm" name="lucideChevronsLeft" />
273
+ }
274
+ @if (navMode() !== 'icons') {
275
+ <span>First</span>
276
+ }
277
+ </button>
278
+ <button
279
+ hlmButton
280
+ variant="secondary"
281
+ appearance="outline"
282
+ size="sm"
283
+ [class]="_navButtonClass()"
284
+ type="button"
285
+ [disabled]="!_canPrevious()"
286
+ (click)="_table().previousPage()"
287
+ aria-label="Go to previous page"
288
+ >
289
+ @if (navMode() !== 'text') {
290
+ <ng-icon hlm size="sm" name="lucideChevronLeft" />
291
+ }
292
+ @if (navMode() !== 'icons') {
293
+ <span>Previous</span>
294
+ }
295
+ </button>
296
+ <button
297
+ hlmButton
298
+ variant="secondary"
299
+ appearance="outline"
300
+ size="sm"
301
+ [class]="_navButtonClass()"
302
+ type="button"
303
+ [disabled]="!_canNext()"
304
+ (click)="_table().nextPage()"
305
+ aria-label="Go to next page"
306
+ >
307
+ @if (navMode() !== 'icons') {
308
+ <span>Next</span>
309
+ }
310
+ @if (navMode() !== 'text') {
311
+ <ng-icon hlm size="sm" name="lucideChevronRight" />
312
+ }
313
+ </button>
314
+ <button
315
+ hlmButton
316
+ variant="secondary"
317
+ appearance="outline"
318
+ size="sm"
319
+ [class]="_edgeNavButtonClass()"
320
+ type="button"
321
+ [disabled]="!_canNext()"
322
+ (click)="_table().setPageIndex(_pageCount() - 1)"
323
+ aria-label="Go to last page"
324
+ >
325
+ @if (navMode() !== 'icons') {
326
+ <span>Last</span>
327
+ }
328
+ @if (navMode() !== 'text') {
329
+ <ng-icon hlm size="sm" name="lucideChevronsRight" />
330
+ }
331
+ </button>
332
+ </div>
333
+ }
334
+ </div>
335
+ </div>
336
+ `,
337
+ changeDetection: ChangeDetectionStrategy.OnPush,
338
+ }]
339
+ }], propDecorators: { showStatus: [{ type: i0.Input, args: [{ isSignal: true, alias: "showStatus", required: false }] }], showPageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPageSize", required: false }] }], pageSizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizes", required: false }] }], navMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "navMode", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
340
+
341
+ /**
342
+ * Column-visibility toggle.
343
+ *
344
+ * Reads the table from the nearest `[tanStackTable]` directive — no table
345
+ * input or generics needed. Only columns with an `accessorFn` that can hide
346
+ * are listed (display columns like `select` / `actions` stay fixed).
347
+ */
348
+ class EgDataTableViewOptions {
349
+ /** Density of the toggle button. */
350
+ size = input('default', /* @ts-ignore */
351
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
352
+ _table = injectTableContext();
353
+ /** Height per density; hidden below `lg` like the pager edge buttons. Merged by `hlmButton`. */
354
+ _buttonClass = computed(() => hlm('tw:ml-auto tw:hidden tw:lg:flex', this.size() === 'sm' ? 'tw:h-7' : this.size() === 'lg' ? 'tw:h-9' : 'tw:h-8'), /* @ts-ignore */
355
+ ...(ngDevMode ? [{ debugName: "_buttonClass" }] : /* istanbul ignore next */ []));
356
+ _hidableColumns = computed(() => this._table()
357
+ .getAllColumns()
358
+ .filter((column) => typeof column.accessorFn !== 'undefined' && column.getCanHide()), /* @ts-ignore */
359
+ ...(ngDevMode ? [{ debugName: "_hidableColumns" }] : /* istanbul ignore next */ []));
360
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgDataTableViewOptions, deps: [], target: i0.ɵɵFactoryTarget.Component });
361
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgDataTableViewOptions, isStandalone: true, selector: "eg-data-table-view-options", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideIcons({ lucideSettings2 })], ngImport: i0, template: `
362
+ <button
363
+ hlmButton
364
+ variant="secondary"
365
+ appearance="outline"
366
+ size="sm"
367
+ [class]="_buttonClass()"
368
+ type="button"
369
+ [hlmDropdownMenuTrigger]="_menu"
370
+ >
371
+ <ng-icon hlm name="lucideSettings2" />
372
+ View
373
+ </button>
374
+ <ng-template #_menu>
375
+ <div hlmDropdownMenu class="tw:w-[150px]">
376
+ <div hlmDropdownMenuLabel>Toggle columns</div>
377
+ <div hlmDropdownMenuSeparator></div>
378
+ @for (column of _hidableColumns(); track column.id) {
379
+ <button
380
+ hlmDropdownMenuCheckbox
381
+ class="tw:capitalize"
382
+ [checked]="column.getIsVisible()"
383
+ (triggered)="column.toggleVisibility()"
384
+ >
385
+ <hlm-dropdown-menu-checkbox-indicator />
386
+ {{ column.id }}
387
+ </button>
388
+ }
389
+ </div>
390
+ </ng-template>
391
+ `, isInline: true, dependencies: [{ kind: "component", type: HlmButton, selector: "button[hlmButton], a[hlmButton]", inputs: ["variant", "size", "appearance", "loading", "icon", "iconPosition", "class", "spinnerUserClass", "disabled", "type"] }, { kind: "directive", type: i1$1.HlmDropdownMenu, selector: "[hlmDropdownMenu],hlm-dropdown-menu", inputs: ["sideOffset"] }, { kind: "directive", type: i1$1.HlmDropdownMenuCheckbox, selector: "[hlmDropdownMenuCheckbox],[hlmDropdownMenuCheckboxItem]", inputs: ["inset"] }, { kind: "component", type: i1$1.HlmDropdownMenuCheckboxIndicator, selector: "hlm-dropdown-menu-checkbox-indicator" }, { kind: "directive", type: i1$1.HlmDropdownMenuLabel, selector: "[hlmDropdownMenuLabel],hlm-dropdown-menu-label", inputs: ["inset"] }, { kind: "directive", type: i1$1.HlmDropdownMenuSeparator, selector: "[hlmDropdownMenuSeparator],hlm-dropdown-menu-separator" }, { kind: "directive", type: i1$1.HlmDropdownMenuTrigger, selector: "[hlmDropdownMenuTrigger]", inputs: ["align", "side"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: HlmIcon, selector: "ng-icon[hlm]", inputs: ["size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
392
+ }
393
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgDataTableViewOptions, decorators: [{
394
+ type: Component,
395
+ args: [{
396
+ selector: 'eg-data-table-view-options',
397
+ imports: [HlmButton, HlmDropdownMenuImports, NgIcon, HlmIcon],
398
+ providers: [provideIcons({ lucideSettings2 })],
399
+ template: `
400
+ <button
401
+ hlmButton
402
+ variant="secondary"
403
+ appearance="outline"
404
+ size="sm"
405
+ [class]="_buttonClass()"
406
+ type="button"
407
+ [hlmDropdownMenuTrigger]="_menu"
408
+ >
409
+ <ng-icon hlm name="lucideSettings2" />
410
+ View
411
+ </button>
412
+ <ng-template #_menu>
413
+ <div hlmDropdownMenu class="tw:w-[150px]">
414
+ <div hlmDropdownMenuLabel>Toggle columns</div>
415
+ <div hlmDropdownMenuSeparator></div>
416
+ @for (column of _hidableColumns(); track column.id) {
417
+ <button
418
+ hlmDropdownMenuCheckbox
419
+ class="tw:capitalize"
420
+ [checked]="column.getIsVisible()"
421
+ (triggered)="column.toggleVisibility()"
422
+ >
423
+ <hlm-dropdown-menu-checkbox-indicator />
424
+ {{ column.id }}
425
+ </button>
426
+ }
427
+ </div>
428
+ </ng-template>
429
+ `,
430
+ changeDetection: ChangeDetectionStrategy.OnPush,
431
+ }]
432
+ }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
433
+
434
+ /**
435
+ * Header selection checkbox: toggles all page rows.
436
+ *
437
+ * Generic over `TData` so the same component works with any table; the table
438
+ * instance is passed explicitly (no DI needed inside column definitions).
439
+ * `HlmCheckbox.checked` accepts `'indeterminate'`, so the tri-state is derived
440
+ * via `computed` instead of a separate input.
441
+ */
442
+ class EgTableHeadSelection {
443
+ table = input.required(/* @ts-ignore */
444
+ ...(ngDevMode ? [{ debugName: "table" }] : /* istanbul ignore next */ []));
445
+ ariaLabel = input('Select all', /* @ts-ignore */
446
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
447
+ _checked = computed(() => {
448
+ const table = this.table();
449
+ if (table.getIsAllPageRowsSelected())
450
+ return true;
451
+ if (table.getIsSomePageRowsSelected())
452
+ return 'indeterminate';
453
+ return false;
454
+ }, /* @ts-ignore */
455
+ ...(ngDevMode ? [{ debugName: "_checked" }] : /* istanbul ignore next */ []));
456
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgTableHeadSelection, deps: [], target: i0.ɵɵFactoryTarget.Component });
457
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: EgTableHeadSelection, isStandalone: true, selector: "eg-table-head-selection", inputs: { table: { classPropertyName: "table", publicName: "table", isSignal: true, isRequired: true, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-label": "ariaLabel()" }, classAttribute: "tw:flex" }, ngImport: i0, template: ` <hlm-checkbox [checked]="_checked()" (changed)="table().toggleAllPageRowsSelected($event)" /> `, isInline: true, dependencies: [{ kind: "component", type: HlmCheckbox, selector: "hlm-checkbox", inputs: ["class", "id", "aria-label", "aria-labelledby", "aria-describedby", "checked", "name", "required", "disabled", "wrapperDisabled"], outputs: ["checkedChange", "changed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
458
+ }
459
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgTableHeadSelection, decorators: [{
460
+ type: Component,
461
+ args: [{
462
+ selector: 'eg-table-head-selection',
463
+ imports: [HlmCheckbox],
464
+ host: { class: 'tw:flex', '[attr.aria-label]': 'ariaLabel()' },
465
+ template: ` <hlm-checkbox [checked]="_checked()" (changed)="table().toggleAllPageRowsSelected($event)" /> `,
466
+ changeDetection: ChangeDetectionStrategy.OnPush,
467
+ }]
468
+ }], propDecorators: { table: [{ type: i0.Input, args: [{ isSignal: true, alias: "table", required: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
469
+ /**
470
+ * Row selection checkbox: toggles a single row.
471
+ */
472
+ class EgTableRowSelection {
473
+ row = input.required(/* @ts-ignore */
474
+ ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
475
+ ariaLabel = input('Select row', /* @ts-ignore */
476
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
477
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgTableRowSelection, deps: [], target: i0.ɵɵFactoryTarget.Component });
478
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: EgTableRowSelection, isStandalone: true, selector: "eg-table-row-selection", inputs: { row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: true, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-label": "ariaLabel()" }, classAttribute: "tw:flex" }, ngImport: i0, template: ` <hlm-checkbox [checked]="row().getIsSelected()" (changed)="row().toggleSelected($event)" /> `, isInline: true, dependencies: [{ kind: "component", type: HlmCheckbox, selector: "hlm-checkbox", inputs: ["class", "id", "aria-label", "aria-labelledby", "aria-describedby", "checked", "name", "required", "disabled", "wrapperDisabled"], outputs: ["checkedChange", "changed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
479
+ }
480
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgTableRowSelection, decorators: [{
481
+ type: Component,
482
+ args: [{
483
+ selector: 'eg-table-row-selection',
484
+ imports: [HlmCheckbox],
485
+ host: { class: 'tw:flex', '[attr.aria-label]': 'ariaLabel()' },
486
+ template: ` <hlm-checkbox [checked]="row().getIsSelected()" (changed)="row().toggleSelected($event)" /> `,
487
+ changeDetection: ChangeDetectionStrategy.OnPush,
488
+ }]
489
+ }], propDecorators: { row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
490
+
491
+ /**
492
+ * Generic data table with TanStack-powered sorting, filtering, pagination,
493
+ * selection, column visibility, plus a React-parity grid layout.
494
+ *
495
+ * The caller passes `columns` (and optionally `gridColumns`) — everything else
496
+ * is owned internally with change notifications via outputs:
497
+ *
498
+ * - Client mode: pass `data` as an array. Pagination/sorting/filtering run in-browser.
499
+ * `filterRows` can pre-filter the array before the built-in pipeline.
500
+ * - Server mode: pass `data` as a page slice (`{items,total,limit,offset,hasPrevious,hasNext}`)
501
+ * with `manualPagination` set. The slice renders as-is — sorting/filtering
502
+ * are not applied client-side. Page, sort and filter interactions emit
503
+ * `pageChange` / `pageSizeChange` / `sortingChange` / `filterChange`
504
+ * (`columnFiltersChange`) so the caller can fetch the next slice.
505
+ * - Selection: set `enableSelection` to auto-prepend a checkbox column;
506
+ * selected rows are emitted via `selectionChange` (array of `TData`).
507
+ * - Grid mode: pass `gridColumns` to enable the layout toggle. Columns with
508
+ * `meta: { hideInTable: true }` are table-hidden; `meta: { thumbnail: true }`
509
+ * renders as the card banner.
510
+ */
511
+ class EgDataTable {
512
+ /** Column definitions for table layout (required). */
513
+ columns = input.required(/* @ts-ignore */
514
+ ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
515
+ /** Column definitions for grid layout; falls back to `columns` when omitted. */
516
+ gridColumns = input(/* @ts-ignore */
517
+ ...(ngDevMode ? [undefined, { debugName: "gridColumns" }] : /* istanbul ignore next */ []));
518
+ /** Client rows, or a server page slice when `manualPagination` is set (`null` while loading). */
519
+ data = input([], /* @ts-ignore */
520
+ ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
521
+ /** When true, `data` is a server page slice and the table uses TanStack `manualPagination`. Sorting and filtering are then display-only: the slice is shown as-is while `sortingChange` / `filterChange` (`columnFiltersChange`) notify the caller to refetch. */
522
+ manualPagination = input(false, /* @ts-ignore */
523
+ ...(ngDevMode ? [{ debugName: "manualPagination" }] : /* istanbul ignore next */ []));
524
+ /** TanStack feature registry. Defaults to filtering + visibility + pagination + selection + sorting. */
525
+ features = input(defaultEgDataTableFeatures, /* @ts-ignore */
526
+ ...(ngDevMode ? [{ debugName: "features" }] : /* istanbul ignore next */ []));
527
+ isLoading = input(false, /* @ts-ignore */
528
+ ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
529
+ emptyMessage = input('No results found.', /* @ts-ignore */
530
+ ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
531
+ /** When true, a checkbox column is auto-prepended and `selectionChange` emits the selected rows. */
532
+ enableSelection = input(false, /* @ts-ignore */
533
+ ...(ngDevMode ? [{ debugName: "enableSelection" }] : /* istanbul ignore next */ []));
534
+ hideFooter = input(false, /* @ts-ignore */
535
+ ...(ngDevMode ? [{ debugName: "hideFooter" }] : /* istanbul ignore next */ []));
536
+ /** Density of toolbar, table, and pagination chrome. Type scale is untouched. */
537
+ size = input('default', /* @ts-ignore */
538
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
539
+ /** Presentation mode. Two-way bindable: `[(layout)]`. Grid requires `gridColumns`. */
540
+ layout = model('table', /* @ts-ignore */
541
+ ...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
542
+ /** Show the table/grid toggle (only rendered when `gridColumns` is set). */
543
+ showLayoutToggle = input(true, /* @ts-ignore */
544
+ ...(ngDevMode ? [{ debugName: "showLayoutToggle" }] : /* istanbul ignore next */ []));
545
+ /** Show the toolbar (filter input, plus the column-visibility dropdown when `showColumnToggle` is set). */
546
+ showToolbar = input(false, /* @ts-ignore */
547
+ ...(ngDevMode ? [{ debugName: "showToolbar" }] : /* istanbul ignore next */ []));
548
+ /** Column id the toolbar filter input writes to (e.g. `'email'`). Empty means client-side filtering is skipped and keystrokes only emit `filterChange` (server-side pattern). */
549
+ filterColumnId = input('', /* @ts-ignore */
550
+ ...(ngDevMode ? [{ debugName: "filterColumnId" }] : /* istanbul ignore next */ []));
551
+ filterPlaceholder = input('Filter...', /* @ts-ignore */
552
+ ...(ngDevMode ? [{ debugName: "filterPlaceholder" }] : /* istanbul ignore next */ []));
553
+ /** Show the column-visibility dropdown inside the toolbar. Off by default; opt in per table. */
554
+ showColumnToggle = input(false, /* @ts-ignore */
555
+ ...(ngDevMode ? [{ debugName: "showColumnToggle" }] : /* istanbul ignore next */ []));
556
+ /** Custom right-side toolbar content (e.g. action buttons) as a template. Renders the toolbar row even when `showToolbar` is false; built-ins stay on the left. */
557
+ toolbarActions = input(undefined, /* @ts-ignore */
558
+ ...(ngDevMode ? [{ debugName: "toolbarActions" }] : /* istanbul ignore next */ []));
559
+ /** Default client-side page size. Changing it later resets the pager to the new size. */
560
+ defaultPageSize = input(10, /* @ts-ignore */
561
+ ...(ngDevMode ? [{ debugName: "defaultPageSize" }] : /* istanbul ignore next */ []));
562
+ /** Caller-provided frontend filter: receives all client rows, returns the kept rows. Runs before built-in filtering/sorting/pagination; ignored in server mode. Must be pure. */
563
+ filterRows = input(null, /* @ts-ignore */
564
+ ...(ngDevMode ? [{ debugName: "filterRows" }] : /* istanbul ignore next */ []));
565
+ /** Seeds the sorting state once at creation. Later changes are ignored — use `sortingChange` to stay in sync. */
566
+ initialSorting = input([], /* @ts-ignore */
567
+ ...(ngDevMode ? [{ debugName: "initialSorting" }] : /* istanbul ignore next */ []));
568
+ /** Seeds the column-filter state once at creation. Later changes are ignored. */
569
+ initialColumnFilters = input([], /* @ts-ignore */
570
+ ...(ngDevMode ? [{ debugName: "initialColumnFilters" }] : /* istanbul ignore next */ []));
571
+ /** Seeds the column-visibility state once at creation. Later changes are ignored. */
572
+ initialColumnVisibility = input({}, /* @ts-ignore */
573
+ ...(ngDevMode ? [{ debugName: "initialColumnVisibility" }] : /* istanbul ignore next */ []));
574
+ /** Seeds the row-selection state once at creation (keys are row ids). Later changes are ignored. */
575
+ initialRowSelection = input({}, /* @ts-ignore */
576
+ ...(ngDevMode ? [{ debugName: "initialRowSelection" }] : /* istanbul ignore next */ []));
577
+ /** Show the rows-per-page selector in the footer pager. */
578
+ showPageSize = input(true, /* @ts-ignore */
579
+ ...(ngDevMode ? [{ debugName: "showPageSize" }] : /* istanbul ignore next */ []));
580
+ /** Page-size options for the footer selector. The current size is appended when absent. */
581
+ pageSizes = input([10, 20, 30, 40, 50], /* @ts-ignore */
582
+ ...(ngDevMode ? [{ debugName: "pageSizes" }] : /* istanbul ignore next */ []));
583
+ /** Footer navigation-button content: icons only, text labels only, or both. */
584
+ navMode = input('icons', /* @ts-ignore */
585
+ ...(ngDevMode ? [{ debugName: "navMode" }] : /* istanbul ignore next */ []));
586
+ userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
587
+ /** Emits the selected rows whenever selection changes (requires `enableSelection`). */
588
+ selectionChange = output();
589
+ /** Emits the target page index on pager navigation (primary hook for server fetching). */
590
+ pageChange = output();
591
+ pageSizeChange = output();
592
+ /** Emits the row's data when a row (table) or card (grid) is clicked. Clicks from nested interactive elements (buttons, links, inputs, checkboxes, selects) are ignored. */
593
+ rowClick = output();
594
+ sortingChange = output();
595
+ columnFiltersChange = output();
596
+ /** Emits the toolbar filter string whenever it changes. Empty when cleared or unset. */
597
+ filterChange = output();
598
+ columnVisibilityChange = output();
599
+ _containerClass = computed(() => hlm('tw:space-y-2', this.userClass()), /* @ts-ignore */
600
+ ...(ngDevMode ? [{ debugName: "_containerClass" }] : /* istanbul ignore next */ []));
601
+ /** Inline TH sort-button footprint per density. Merged by `hlmButton` via the `class` binding. */
602
+ _sortButtonClass = computed(() => hlm('tw:p-0', this.size() === 'sm' ? 'tw:h-5 tw:w-5' : this.size() === 'lg' ? 'tw:h-7 tw:w-7' : 'tw:h-6 tw:w-6'), /* @ts-ignore */
603
+ ...(ngDevMode ? [{ debugName: "_sortButtonClass" }] : /* istanbul ignore next */ []));
604
+ _sorting = signal([], /* @ts-ignore */
605
+ ...(ngDevMode ? [{ debugName: "_sorting" }] : /* istanbul ignore next */ []));
606
+ _columnFilters = signal([], /* @ts-ignore */
607
+ ...(ngDevMode ? [{ debugName: "_columnFilters" }] : /* istanbul ignore next */ []));
608
+ _columnVisibility = signal({}, /* @ts-ignore */
609
+ ...(ngDevMode ? [{ debugName: "_columnVisibility" }] : /* istanbul ignore next */ []));
610
+ _rowSelection = signal({}, /* @ts-ignore */
611
+ ...(ngDevMode ? [{ debugName: "_rowSelection" }] : /* istanbul ignore next */ []));
612
+ _clientPageIndex = signal(0, /* @ts-ignore */
613
+ ...(ngDevMode ? [{ debugName: "_clientPageIndex" }] : /* istanbul ignore next */ []));
614
+ // Linked so the bound `defaultPageSize` applies once inputs are set, while
615
+ // pager-driven `set()` writes below are preserved until it changes again.
616
+ _clientPageSize = linkedSignal(() => this.defaultPageSize(), /* @ts-ignore */
617
+ ...(ngDevMode ? [{ debugName: "_clientPageSize" }] : /* istanbul ignore next */ []));
618
+ _isServer = computed(() => this.manualPagination(), /* @ts-ignore */
619
+ ...(ngDevMode ? [{ debugName: "_isServer" }] : /* istanbul ignore next */ []));
620
+ /** The server page slice when in server mode; `null` for client data, misuse shapes, or `null` while loading. */
621
+ _page = computed(() => {
622
+ if (!this._isServer())
623
+ return null;
624
+ const data = this.data();
625
+ if (data !== null && typeof data === 'object' && 'items' in data) {
626
+ return data;
627
+ }
628
+ return null;
629
+ }, /* @ts-ignore */
630
+ ...(ngDevMode ? [{ debugName: "_page" }] : /* istanbul ignore next */ []));
631
+ _items = computed(() => {
632
+ const page = this._page();
633
+ if (page)
634
+ return page.items;
635
+ const data = this.data();
636
+ const rows = Array.isArray(data)
637
+ ? data
638
+ : (data?.items ?? []);
639
+ const filter = this.filterRows();
640
+ return filter ? filter(rows) : rows;
641
+ }, /* @ts-ignore */
642
+ ...(ngDevMode ? [{ debugName: "_items" }] : /* istanbul ignore next */ []));
643
+ _serverPageCount = computed(() => {
644
+ const paginated = this._page();
645
+ if (!paginated || paginated.limit <= 0)
646
+ return 1;
647
+ return Math.max(1, Math.ceil(paginated.total / paginated.limit));
648
+ }, /* @ts-ignore */
649
+ ...(ngDevMode ? [{ debugName: "_serverPageCount" }] : /* istanbul ignore next */ []));
650
+ _serverPageIndex = computed(() => {
651
+ const paginated = this._page();
652
+ if (!paginated || paginated.limit <= 0)
653
+ return 0;
654
+ return Math.floor(paginated.offset / paginated.limit);
655
+ }, /* @ts-ignore */
656
+ ...(ngDevMode ? [{ debugName: "_serverPageIndex" }] : /* istanbul ignore next */ []));
657
+ _paginationState = computed(() => {
658
+ const paginated = this._page();
659
+ if (paginated)
660
+ return { pageIndex: this._serverPageIndex(), pageSize: paginated.limit };
661
+ return { pageIndex: this._clientPageIndex(), pageSize: this._clientPageSize() };
662
+ }, /* @ts-ignore */
663
+ ...(ngDevMode ? [{ debugName: "_paginationState" }] : /* istanbul ignore next */ []));
664
+ _selectionHelper = computed(() => createColumnHelper(), /* @ts-ignore */
665
+ ...(ngDevMode ? [{ debugName: "_selectionHelper" }] : /* istanbul ignore next */ []));
666
+ /** Caller columns + auto-injected selection column + layout filtering. */
667
+ _finalColumns = computed(() => {
668
+ const isGrid = this._effectiveLayout() === 'grid';
669
+ const base = isGrid ? (this.gridColumns() ?? this.columns()) : this.columns();
670
+ const visible = isGrid ? base : base.filter((column) => column.meta?.hideInTable !== true);
671
+ if (!this.enableSelection())
672
+ return [...visible];
673
+ const helper = this._selectionHelper();
674
+ const selectionColumn = helper.display({
675
+ id: 'select',
676
+ header: () => EgTableHeadSelection,
677
+ cell: () => EgTableRowSelection,
678
+ enableSorting: false,
679
+ enableHiding: false,
680
+ });
681
+ return [selectionColumn, ...visible];
682
+ }, /* @ts-ignore */
683
+ ...(ngDevMode ? [{ debugName: "_finalColumns" }] : /* istanbul ignore next */ []));
684
+ _table = injectTable(() => {
685
+ const server = this._isServer();
686
+ return {
687
+ features: this.features(),
688
+ columns: this._finalColumns(),
689
+ data: [...this._items()],
690
+ manualPagination: server,
691
+ manualSorting: server,
692
+ manualFiltering: server,
693
+ ...(server ? { pageCount: this._serverPageCount() } : {}),
694
+ state: {
695
+ sorting: this._sorting(),
696
+ columnFilters: this._columnFilters(),
697
+ columnVisibility: this._columnVisibility(),
698
+ rowSelection: this._rowSelection(),
699
+ pagination: this._paginationState(),
700
+ },
701
+ onSortingChange: (updater) => {
702
+ const next = typeof updater === 'function' ? updater(this._sorting()) : updater;
703
+ this._sorting.set(next);
704
+ this.sortingChange.emit(next);
705
+ },
706
+ onColumnFiltersChange: (updater) => {
707
+ const next = typeof updater === 'function' ? updater(this._columnFilters()) : updater;
708
+ this._columnFilters.set(next);
709
+ this.columnFiltersChange.emit(next);
710
+ const filterId = this.filterColumnId();
711
+ if (filterId) {
712
+ const value = next.find((filter) => filter.id === filterId)?.value;
713
+ this.filterChange.emit(typeof value === 'string' ? value : '');
714
+ }
715
+ },
716
+ onColumnVisibilityChange: (updater) => {
717
+ const next = typeof updater === 'function' ? updater(this._columnVisibility()) : updater;
718
+ this._columnVisibility.set(next);
719
+ this.columnVisibilityChange.emit(next);
720
+ },
721
+ onRowSelectionChange: (updater) => {
722
+ const next = typeof updater === 'function' ? updater(this._rowSelection()) : updater;
723
+ this._rowSelection.set(next);
724
+ },
725
+ onPaginationChange: (updater) => {
726
+ const current = this._paginationState();
727
+ const next = typeof updater === 'function' ? updater(current) : updater;
728
+ if (next.pageIndex !== current.pageIndex)
729
+ this.pageChange.emit(next.pageIndex);
730
+ if (next.pageSize !== current.pageSize)
731
+ this.pageSizeChange.emit(next.pageSize);
732
+ if (!this._isServer()) {
733
+ this._clientPageIndex.set(next.pageIndex);
734
+ this._clientPageSize.set(next.pageSize);
735
+ }
736
+ },
737
+ };
738
+ });
739
+ _emitSelection = effect(() => {
740
+ this._rowSelection();
741
+ const selected = this._table.getSelectedRowModel().rows.map((row) => row.original);
742
+ this.selectionChange.emit(selected);
743
+ }, /* @ts-ignore */
744
+ ...(ngDevMode ? [{ debugName: "_emitSelection" }] : /* istanbul ignore next */ []));
745
+ /**
746
+ * Seeds internal state from the `initial*` inputs. Runs in `ngOnInit` (after
747
+ * first bindings, before first render) exactly once, so inline-literal
748
+ * bindings (new references every change detection) cannot clobber user
749
+ * interaction afterwards. Values are copied to avoid aliasing caller data.
750
+ */
751
+ ngOnInit() {
752
+ this._sorting.set([...this.initialSorting()]);
753
+ this._columnFilters.set([...this.initialColumnFilters()]);
754
+ this._columnVisibility.set({ ...this.initialColumnVisibility() });
755
+ this._rowSelection.set({ ...this.initialRowSelection() });
756
+ }
757
+ _hasGrid = computed(() => this.gridColumns() !== undefined, /* @ts-ignore */
758
+ ...(ngDevMode ? [{ debugName: "_hasGrid" }] : /* istanbul ignore next */ []));
759
+ _effectiveLayout = computed(() => this.layout() === 'grid' && this._hasGrid() ? 'grid' : 'table', /* @ts-ignore */
760
+ ...(ngDevMode ? [{ debugName: "_effectiveLayout" }] : /* istanbul ignore next */ []));
761
+ _showLayoutToggle = computed(() => this.showLayoutToggle() && this._hasGrid(), /* @ts-ignore */
762
+ ...(ngDevMode ? [{ debugName: "_showLayoutToggle" }] : /* istanbul ignore next */ []));
763
+ _visibleColumnCount = computed(() => {
764
+ const count = this._table.getVisibleLeafColumns().length;
765
+ return count > 0 ? count : 1;
766
+ }, /* @ts-ignore */
767
+ ...(ngDevMode ? [{ debugName: "_visibleColumnCount" }] : /* istanbul ignore next */ []));
768
+ _statusText = computed(() => {
769
+ const paginated = this._page();
770
+ const table = this._table;
771
+ const selected = table.getFilteredSelectedRowModel().rows.length;
772
+ if (this.enableSelection() && selected > 0) {
773
+ const total = paginated ? paginated.total : table.getFilteredRowModel().rows.length;
774
+ return `${selected} of ${total} row(s) selected`;
775
+ }
776
+ if (paginated) {
777
+ const from = paginated.items.length === 0 ? 0 : paginated.offset + 1;
778
+ const to = paginated.offset + paginated.items.length;
779
+ return `Showing ${from} to ${to} of ${paginated.total} records`;
780
+ }
781
+ const total = table.getFilteredRowModel().rows.length;
782
+ return `${total} row(s).`;
783
+ }, /* @ts-ignore */
784
+ ...(ngDevMode ? [{ debugName: "_statusText" }] : /* istanbul ignore next */ []));
785
+ _onLayoutToggle(value) {
786
+ if (value === 'table' || value === 'grid')
787
+ this.layout.set(value);
788
+ }
789
+ _onFilterInput(event) {
790
+ const value = event.target.value;
791
+ const id = this.filterColumnId();
792
+ if (!id) {
793
+ this.filterChange.emit(value);
794
+ return;
795
+ }
796
+ this._table.getColumn(id)?.setFilterValue(value);
797
+ }
798
+ /**
799
+ * Row/card click handler. Ignores clicks originating from nested interactive
800
+ * elements (selection checkboxes, action buttons, links, inputs) so those
801
+ * controls keep working without also firing `rowClick`.
802
+ */
803
+ _onRowClick(event, row) {
804
+ const target = event.target;
805
+ if (target instanceof Element && target.closest('button, a, input, select, textarea, [role="checkbox"]')) {
806
+ return;
807
+ }
808
+ this.rowClick.emit(row.original);
809
+ }
810
+ /** Show the inline TH sort button only for sortable plain-string headers (component headers own their UI). */
811
+ _showHeaderSortButton(header) {
812
+ return header.column.getCanSort() && typeof header.column.columnDef.header === 'string';
813
+ }
814
+ _toggleHeaderSorting(header) {
815
+ header.column.toggleSorting(header.column.getIsSorted() === 'asc');
816
+ }
817
+ _selectionCell(row) {
818
+ return row.getVisibleCells().find((cell) => cell.column.id === 'select');
819
+ }
820
+ _thumbnailCell(row) {
821
+ return row.getVisibleCells().find((cell) => cell.column.columnDef.meta?.thumbnail === true);
822
+ }
823
+ _contentCells(row) {
824
+ return row
825
+ .getVisibleCells()
826
+ .filter((cell) => cell.column.id !== 'select' && cell.column.columnDef.meta?.thumbnail !== true);
827
+ }
828
+ _cellLabel(cell) {
829
+ const header = cell.column.columnDef.header;
830
+ if (typeof header === 'string')
831
+ return header;
832
+ return cell.column.id.replace(/_/g, ' ');
833
+ }
834
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgDataTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
835
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgDataTable, isStandalone: true, selector: "eg-data-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, gridColumns: { classPropertyName: "gridColumns", publicName: "gridColumns", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, manualPagination: { classPropertyName: "manualPagination", publicName: "manualPagination", isSignal: true, isRequired: false, transformFunction: null }, features: { classPropertyName: "features", publicName: "features", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, enableSelection: { classPropertyName: "enableSelection", publicName: "enableSelection", isSignal: true, isRequired: false, transformFunction: null }, hideFooter: { classPropertyName: "hideFooter", publicName: "hideFooter", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, showLayoutToggle: { classPropertyName: "showLayoutToggle", publicName: "showLayoutToggle", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, filterColumnId: { classPropertyName: "filterColumnId", publicName: "filterColumnId", isSignal: true, isRequired: false, transformFunction: null }, filterPlaceholder: { classPropertyName: "filterPlaceholder", publicName: "filterPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, showColumnToggle: { classPropertyName: "showColumnToggle", publicName: "showColumnToggle", isSignal: true, isRequired: false, transformFunction: null }, toolbarActions: { classPropertyName: "toolbarActions", publicName: "toolbarActions", isSignal: true, isRequired: false, transformFunction: null }, defaultPageSize: { classPropertyName: "defaultPageSize", publicName: "defaultPageSize", isSignal: true, isRequired: false, transformFunction: null }, filterRows: { classPropertyName: "filterRows", publicName: "filterRows", isSignal: true, isRequired: false, transformFunction: null }, initialSorting: { classPropertyName: "initialSorting", publicName: "initialSorting", isSignal: true, isRequired: false, transformFunction: null }, initialColumnFilters: { classPropertyName: "initialColumnFilters", publicName: "initialColumnFilters", isSignal: true, isRequired: false, transformFunction: null }, initialColumnVisibility: { classPropertyName: "initialColumnVisibility", publicName: "initialColumnVisibility", isSignal: true, isRequired: false, transformFunction: null }, initialRowSelection: { classPropertyName: "initialRowSelection", publicName: "initialRowSelection", isSignal: true, isRequired: false, transformFunction: null }, showPageSize: { classPropertyName: "showPageSize", publicName: "showPageSize", isSignal: true, isRequired: false, transformFunction: null }, pageSizes: { classPropertyName: "pageSizes", publicName: "pageSizes", isSignal: true, isRequired: false, transformFunction: null }, navMode: { classPropertyName: "navMode", publicName: "navMode", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { layout: "layoutChange", selectionChange: "selectionChange", pageChange: "pageChange", pageSizeChange: "pageSizeChange", rowClick: "rowClick", sortingChange: "sortingChange", columnFiltersChange: "columnFiltersChange", filterChange: "filterChange", columnVisibilityChange: "columnVisibilityChange" }, providers: [
836
+ provideIcons({ lucideArrowDown, lucideArrowUp, lucideChevronsUpDown, lucideLayoutGrid, lucideLayoutList }),
837
+ ], ngImport: i0, template: `
838
+ <div [class]="_containerClass()">
839
+ @if (_showLayoutToggle()) {
840
+ <div class="tw:flex tw:items-center tw:justify-between">
841
+ <div hlmToggleGroup type="single" [value]="_effectiveLayout()" (valueChange)="_onLayoutToggle($event)">
842
+ <button hlmToggleGroupItem value="table" aria-label="Table view">
843
+ <ng-icon hlm name="lucideLayoutList" />
844
+ </button>
845
+ <button hlmToggleGroupItem value="grid" aria-label="Grid view">
846
+ <ng-icon hlm name="lucideLayoutGrid" />
847
+ </button>
848
+ </div>
849
+ <div></div>
850
+ </div>
851
+ }
852
+
853
+ @if (showToolbar() || toolbarActions()) {
854
+ <div class="tw:flex tw:flex-wrap tw:items-center tw:justify-between tw:gap-2 tw:py-1" [tanStackTable]="_table">
855
+ @if (showToolbar()) {
856
+ <div class="tw:flex tw:flex-wrap tw:items-center tw:gap-2">
857
+ <input
858
+ hlmInput
859
+ class="tw:w-full tw:min-w-0 tw:md:w-80"
860
+ [size]="size()"
861
+ [placeholder]="filterPlaceholder()"
862
+ (input)="_onFilterInput($event)"
863
+ />
864
+ @if (showColumnToggle()) {
865
+ <eg-data-table-view-options [size]="size()" />
866
+ }
867
+ </div>
868
+ }
869
+ @if (toolbarActions()) {
870
+ <div class="tw:flex tw:flex-wrap tw:items-center tw:gap-2">
871
+ <ng-container *ngTemplateOutlet="toolbarActions()" />
872
+ </div>
873
+ }
874
+ </div>
875
+ }
876
+
877
+ @if (_effectiveLayout() === 'grid') {
878
+ <ng-container *ngTemplateOutlet="_gridView" />
879
+ } @else {
880
+ <ng-container *ngTemplateOutlet="_tableView" />
881
+ }
882
+
883
+ <ng-template #_tableView>
884
+ <div class="tw:overflow-hidden tw:rounded-md tw:border">
885
+ <div hlmTableContainer>
886
+ <table hlmTable>
887
+ <thead hlmTHead>
888
+ @for (headerGroup of _table.getHeaderGroups(); track headerGroup.id) {
889
+ <tr hlmTr>
890
+ @for (header of headerGroup.headers; track header.id) {
891
+ <th hlmTh [size]="size()" [attr.colSpan]="header.colSpan">
892
+ @if (!header.isPlaceholder) {
893
+ <div class="tw:flex tw:items-center tw:gap-1">
894
+ <ng-container
895
+ *flexRender="header.column.columnDef.header; props: header.getContext(); let headerText"
896
+ >
897
+ <div [innerHTML]="headerText"></div>
898
+ </ng-container>
899
+ @if (_showHeaderSortButton(header)) {
900
+ <button
901
+ hlmButton
902
+ variant="ghost"
903
+ size="sm"
904
+ [class]="_sortButtonClass()"
905
+ type="button"
906
+ [attr.aria-label]="'Sort by ' + header.column.id"
907
+ (click)="_toggleHeaderSorting(header)"
908
+ >
909
+ @switch (header.column.getIsSorted()) {
910
+ @case ('asc') {
911
+ <ng-icon hlm size="sm" name="lucideArrowUp" />
912
+ }
913
+ @case ('desc') {
914
+ <ng-icon hlm size="sm" name="lucideArrowDown" />
915
+ }
916
+ @default {
917
+ <ng-icon hlm size="sm" name="lucideChevronsUpDown" />
918
+ }
919
+ }
920
+ </button>
921
+ }
922
+ </div>
923
+ }
924
+ </th>
925
+ }
926
+ </tr>
927
+ }
928
+ </thead>
929
+ <tbody hlmTBody>
930
+ @if (isLoading()) {
931
+ <tr hlmTr>
932
+ <td hlmTd [attr.colspan]="_visibleColumnCount()" class="tw:h-24 tw:text-center">
933
+ <hlm-spinner class="tw:inline-flex" />
934
+ </td>
935
+ </tr>
936
+ } @else if (_table.getRowModel().rows.length === 0) {
937
+ <tr hlmTr>
938
+ <td hlmTd [attr.colspan]="_visibleColumnCount()" class="tw:h-24 tw:text-center">
939
+ {{ emptyMessage() }}
940
+ </td>
941
+ </tr>
942
+ } @else {
943
+ @for (row of _table.getRowModel().rows; track row.id) {
944
+ <tr
945
+ hlmTr
946
+ [attr.data-state]="row.getIsSelected() ? 'selected' : null"
947
+ (click)="_onRowClick($event, row)"
948
+ >
949
+ @for (cell of row.getVisibleCells(); track cell.id) {
950
+ <td hlmTd [size]="size()">
951
+ <ng-container
952
+ *flexRender="cell.column.columnDef.cell; props: cell.getContext(); let cellText"
953
+ >
954
+ <div [innerHTML]="cellText"></div>
955
+ </ng-container>
956
+ </td>
957
+ }
958
+ </tr>
959
+ }
960
+ }
961
+ </tbody>
962
+ </table>
963
+ </div>
964
+ </div>
965
+ </ng-template>
966
+
967
+ <ng-template #_gridView>
968
+ @if (isLoading()) {
969
+ <div
970
+ class="tw:flex tw:h-64 tw:items-center tw:justify-center tw:rounded-lg tw:border tw:border-dashed"
971
+ role="status"
972
+ >
973
+ <hlm-spinner />
974
+ </div>
975
+ } @else if (_table.getRowModel().rows.length === 0) {
976
+ <div
977
+ class="tw:text-muted-foreground tw:flex tw:h-64 tw:items-center tw:justify-center tw:rounded-lg tw:border tw:border-dashed"
978
+ >
979
+ {{ emptyMessage() }}
980
+ </div>
981
+ } @else {
982
+ <div class="tw:grid tw:grid-cols-1 tw:gap-4 tw:md:grid-cols-2 tw:xl:grid-cols-3 tw:2xl:grid-cols-4">
983
+ @for (row of _table.getRowModel().rows; track row.id) {
984
+ <div
985
+ hlmCard
986
+ class="tw:group tw:relative tw:transition-all tw:hover:shadow-md tw:data-[state=selected]:border-primary tw:data-[state=selected]:ring-1 tw:data-[state=selected]:ring-primary/20"
987
+ [attr.data-state]="row.getIsSelected() ? 'selected' : null"
988
+ (click)="_onRowClick($event, row)"
989
+ >
990
+ @if (_thumbnailCell(row); as thumbnail) {
991
+ <div class="tw:border-b tw:bg-muted/30">
992
+ <ng-container
993
+ *flexRender="thumbnail.column.columnDef.cell; props: thumbnail.getContext(); let thumbHtml"
994
+ >
995
+ <div [innerHTML]="thumbHtml"></div>
996
+ </ng-container>
997
+ </div>
998
+ }
999
+ @if (_selectionCell(row); as selection) {
1000
+ <div class="tw:absolute tw:top-4 tw:right-4">
1001
+ <ng-container
1002
+ *flexRender="selection.column.columnDef.cell; props: selection.getContext(); let selectHtml"
1003
+ >
1004
+ <div [innerHTML]="selectHtml"></div>
1005
+ </ng-container>
1006
+ </div>
1007
+ }
1008
+ <div hlmCardContent class="tw:space-y-3">
1009
+ @for (cell of _contentCells(row); track cell.id) {
1010
+ <div class="tw:flex tw:flex-col">
1011
+ <dt
1012
+ class="tw:text-[10px] tw:font-bold tw:tracking-wider tw:uppercase tw:text-muted-foreground/80"
1013
+ >
1014
+ {{ _cellLabel(cell) }}
1015
+ </dt>
1016
+ <dd class="tw:mt-0.5 tw:text-sm tw:leading-none tw:font-medium">
1017
+ <ng-container *flexRender="cell.column.columnDef.cell; props: cell.getContext(); let valueHtml">
1018
+ <div [innerHTML]="valueHtml"></div>
1019
+ </ng-container>
1020
+ </dd>
1021
+ </div>
1022
+ }
1023
+ </div>
1024
+ </div>
1025
+ }
1026
+ </div>
1027
+ }
1028
+ </ng-template>
1029
+
1030
+ @if (!hideFooter()) {
1031
+ <div
1032
+ class="tw:flex tw:flex-col tw:gap-2 tw:px-2 tw:py-4 tw:sm:flex-row tw:sm:items-center tw:sm:justify-between"
1033
+ [tanStackTable]="_table"
1034
+ >
1035
+ <div class="tw:text-muted-foreground tw:flex-1 tw:text-sm">{{ _statusText() }}</div>
1036
+ <eg-data-table-pagination
1037
+ [showStatus]="false"
1038
+ [showPageSize]="showPageSize()"
1039
+ [pageSizes]="pageSizes()"
1040
+ [navMode]="navMode()"
1041
+ [size]="size()"
1042
+ />
1043
+ </div>
1044
+ }
1045
+ </div>
1046
+ `, isInline: true, dependencies: [{ kind: "directive", type: i1$2.FlexRenderDirective, selector: "ng-template[flexRender]", inputs: ["flexRender", "flexRenderProps", "flexRenderInjector"] }, { kind: "directive", type: TanStackTable, selector: "[tanStackTable]", inputs: ["tanStackTable"], exportAs: ["table"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.HlmTableContainer, selector: "div[hlmTableContainer]" }, { kind: "directive", type: i2.HlmTable, selector: "table[hlmTable]" }, { kind: "directive", type: i2.HlmTBody, selector: "tbody[hlmTBody],tbody[hlmTableBody]" }, { kind: "directive", type: i2.HlmTd, selector: "td[hlmTd],td[hlmTableCell]", inputs: ["size"] }, { kind: "directive", type: i2.HlmTh, selector: "th[hlmTh],th[hlmTableHead]", inputs: ["size"] }, { kind: "directive", type: i2.HlmTHead, selector: "thead[hlmTHead],thead[hlmTableHeader]" }, { kind: "directive", type: i2.HlmTr, selector: "tr[hlmTr],tr[hlmTableRow]" }, { kind: "component", type: HlmButton, selector: "button[hlmButton], a[hlmButton]", inputs: ["variant", "size", "appearance", "loading", "icon", "iconPosition", "class", "spinnerUserClass", "disabled", "type"] }, { kind: "directive", type: HlmCard, selector: "[hlmCard],hlm-card", inputs: ["size"] }, { kind: "directive", type: HlmCardContent, selector: "[hlmCardContent]" }, { kind: "directive", type: HlmIcon, selector: "ng-icon[hlm]", inputs: ["size"] }, { kind: "directive", type: HlmInput, selector: "[hlmInput]", inputs: ["ariaDescribedby", "size"] }, { kind: "component", type: HlmSpinner, selector: "hlm-spinner", inputs: ["size", "show", "class", "spinnerClass"] }, { kind: "directive", type: HlmToggleGroup, selector: "[hlmToggleGroup],hlm-toggle-group", inputs: ["variant", "size", "spacing", "orientation"] }, { kind: "directive", type: HlmToggleGroupItem, selector: "button[hlmToggleGroupItem]", inputs: ["variant", "size"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: EgDataTablePagination, selector: "eg-data-table-pagination", inputs: ["showStatus", "showPageSize", "pageSizes", "navMode", "size"] }, { kind: "component", type: EgDataTableViewOptions, selector: "eg-data-table-view-options", inputs: ["size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1047
+ }
1048
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgDataTable, decorators: [{
1049
+ type: Component,
1050
+ args: [{
1051
+ selector: 'eg-data-table',
1052
+ imports: [
1053
+ FlexRender,
1054
+ TanStackTable,
1055
+ NgTemplateOutlet,
1056
+ HlmTableImports,
1057
+ HlmButton,
1058
+ HlmCard,
1059
+ HlmCardContent,
1060
+ HlmIcon,
1061
+ HlmInput,
1062
+ HlmSpinner,
1063
+ HlmToggleGroup,
1064
+ HlmToggleGroupItem,
1065
+ NgIcon,
1066
+ EgDataTablePagination,
1067
+ EgDataTableViewOptions,
1068
+ ],
1069
+ providers: [
1070
+ provideIcons({ lucideArrowDown, lucideArrowUp, lucideChevronsUpDown, lucideLayoutGrid, lucideLayoutList }),
1071
+ ],
1072
+ template: `
1073
+ <div [class]="_containerClass()">
1074
+ @if (_showLayoutToggle()) {
1075
+ <div class="tw:flex tw:items-center tw:justify-between">
1076
+ <div hlmToggleGroup type="single" [value]="_effectiveLayout()" (valueChange)="_onLayoutToggle($event)">
1077
+ <button hlmToggleGroupItem value="table" aria-label="Table view">
1078
+ <ng-icon hlm name="lucideLayoutList" />
1079
+ </button>
1080
+ <button hlmToggleGroupItem value="grid" aria-label="Grid view">
1081
+ <ng-icon hlm name="lucideLayoutGrid" />
1082
+ </button>
1083
+ </div>
1084
+ <div></div>
1085
+ </div>
1086
+ }
1087
+
1088
+ @if (showToolbar() || toolbarActions()) {
1089
+ <div class="tw:flex tw:flex-wrap tw:items-center tw:justify-between tw:gap-2 tw:py-1" [tanStackTable]="_table">
1090
+ @if (showToolbar()) {
1091
+ <div class="tw:flex tw:flex-wrap tw:items-center tw:gap-2">
1092
+ <input
1093
+ hlmInput
1094
+ class="tw:w-full tw:min-w-0 tw:md:w-80"
1095
+ [size]="size()"
1096
+ [placeholder]="filterPlaceholder()"
1097
+ (input)="_onFilterInput($event)"
1098
+ />
1099
+ @if (showColumnToggle()) {
1100
+ <eg-data-table-view-options [size]="size()" />
1101
+ }
1102
+ </div>
1103
+ }
1104
+ @if (toolbarActions()) {
1105
+ <div class="tw:flex tw:flex-wrap tw:items-center tw:gap-2">
1106
+ <ng-container *ngTemplateOutlet="toolbarActions()" />
1107
+ </div>
1108
+ }
1109
+ </div>
1110
+ }
1111
+
1112
+ @if (_effectiveLayout() === 'grid') {
1113
+ <ng-container *ngTemplateOutlet="_gridView" />
1114
+ } @else {
1115
+ <ng-container *ngTemplateOutlet="_tableView" />
1116
+ }
1117
+
1118
+ <ng-template #_tableView>
1119
+ <div class="tw:overflow-hidden tw:rounded-md tw:border">
1120
+ <div hlmTableContainer>
1121
+ <table hlmTable>
1122
+ <thead hlmTHead>
1123
+ @for (headerGroup of _table.getHeaderGroups(); track headerGroup.id) {
1124
+ <tr hlmTr>
1125
+ @for (header of headerGroup.headers; track header.id) {
1126
+ <th hlmTh [size]="size()" [attr.colSpan]="header.colSpan">
1127
+ @if (!header.isPlaceholder) {
1128
+ <div class="tw:flex tw:items-center tw:gap-1">
1129
+ <ng-container
1130
+ *flexRender="header.column.columnDef.header; props: header.getContext(); let headerText"
1131
+ >
1132
+ <div [innerHTML]="headerText"></div>
1133
+ </ng-container>
1134
+ @if (_showHeaderSortButton(header)) {
1135
+ <button
1136
+ hlmButton
1137
+ variant="ghost"
1138
+ size="sm"
1139
+ [class]="_sortButtonClass()"
1140
+ type="button"
1141
+ [attr.aria-label]="'Sort by ' + header.column.id"
1142
+ (click)="_toggleHeaderSorting(header)"
1143
+ >
1144
+ @switch (header.column.getIsSorted()) {
1145
+ @case ('asc') {
1146
+ <ng-icon hlm size="sm" name="lucideArrowUp" />
1147
+ }
1148
+ @case ('desc') {
1149
+ <ng-icon hlm size="sm" name="lucideArrowDown" />
1150
+ }
1151
+ @default {
1152
+ <ng-icon hlm size="sm" name="lucideChevronsUpDown" />
1153
+ }
1154
+ }
1155
+ </button>
1156
+ }
1157
+ </div>
1158
+ }
1159
+ </th>
1160
+ }
1161
+ </tr>
1162
+ }
1163
+ </thead>
1164
+ <tbody hlmTBody>
1165
+ @if (isLoading()) {
1166
+ <tr hlmTr>
1167
+ <td hlmTd [attr.colspan]="_visibleColumnCount()" class="tw:h-24 tw:text-center">
1168
+ <hlm-spinner class="tw:inline-flex" />
1169
+ </td>
1170
+ </tr>
1171
+ } @else if (_table.getRowModel().rows.length === 0) {
1172
+ <tr hlmTr>
1173
+ <td hlmTd [attr.colspan]="_visibleColumnCount()" class="tw:h-24 tw:text-center">
1174
+ {{ emptyMessage() }}
1175
+ </td>
1176
+ </tr>
1177
+ } @else {
1178
+ @for (row of _table.getRowModel().rows; track row.id) {
1179
+ <tr
1180
+ hlmTr
1181
+ [attr.data-state]="row.getIsSelected() ? 'selected' : null"
1182
+ (click)="_onRowClick($event, row)"
1183
+ >
1184
+ @for (cell of row.getVisibleCells(); track cell.id) {
1185
+ <td hlmTd [size]="size()">
1186
+ <ng-container
1187
+ *flexRender="cell.column.columnDef.cell; props: cell.getContext(); let cellText"
1188
+ >
1189
+ <div [innerHTML]="cellText"></div>
1190
+ </ng-container>
1191
+ </td>
1192
+ }
1193
+ </tr>
1194
+ }
1195
+ }
1196
+ </tbody>
1197
+ </table>
1198
+ </div>
1199
+ </div>
1200
+ </ng-template>
1201
+
1202
+ <ng-template #_gridView>
1203
+ @if (isLoading()) {
1204
+ <div
1205
+ class="tw:flex tw:h-64 tw:items-center tw:justify-center tw:rounded-lg tw:border tw:border-dashed"
1206
+ role="status"
1207
+ >
1208
+ <hlm-spinner />
1209
+ </div>
1210
+ } @else if (_table.getRowModel().rows.length === 0) {
1211
+ <div
1212
+ class="tw:text-muted-foreground tw:flex tw:h-64 tw:items-center tw:justify-center tw:rounded-lg tw:border tw:border-dashed"
1213
+ >
1214
+ {{ emptyMessage() }}
1215
+ </div>
1216
+ } @else {
1217
+ <div class="tw:grid tw:grid-cols-1 tw:gap-4 tw:md:grid-cols-2 tw:xl:grid-cols-3 tw:2xl:grid-cols-4">
1218
+ @for (row of _table.getRowModel().rows; track row.id) {
1219
+ <div
1220
+ hlmCard
1221
+ class="tw:group tw:relative tw:transition-all tw:hover:shadow-md tw:data-[state=selected]:border-primary tw:data-[state=selected]:ring-1 tw:data-[state=selected]:ring-primary/20"
1222
+ [attr.data-state]="row.getIsSelected() ? 'selected' : null"
1223
+ (click)="_onRowClick($event, row)"
1224
+ >
1225
+ @if (_thumbnailCell(row); as thumbnail) {
1226
+ <div class="tw:border-b tw:bg-muted/30">
1227
+ <ng-container
1228
+ *flexRender="thumbnail.column.columnDef.cell; props: thumbnail.getContext(); let thumbHtml"
1229
+ >
1230
+ <div [innerHTML]="thumbHtml"></div>
1231
+ </ng-container>
1232
+ </div>
1233
+ }
1234
+ @if (_selectionCell(row); as selection) {
1235
+ <div class="tw:absolute tw:top-4 tw:right-4">
1236
+ <ng-container
1237
+ *flexRender="selection.column.columnDef.cell; props: selection.getContext(); let selectHtml"
1238
+ >
1239
+ <div [innerHTML]="selectHtml"></div>
1240
+ </ng-container>
1241
+ </div>
1242
+ }
1243
+ <div hlmCardContent class="tw:space-y-3">
1244
+ @for (cell of _contentCells(row); track cell.id) {
1245
+ <div class="tw:flex tw:flex-col">
1246
+ <dt
1247
+ class="tw:text-[10px] tw:font-bold tw:tracking-wider tw:uppercase tw:text-muted-foreground/80"
1248
+ >
1249
+ {{ _cellLabel(cell) }}
1250
+ </dt>
1251
+ <dd class="tw:mt-0.5 tw:text-sm tw:leading-none tw:font-medium">
1252
+ <ng-container *flexRender="cell.column.columnDef.cell; props: cell.getContext(); let valueHtml">
1253
+ <div [innerHTML]="valueHtml"></div>
1254
+ </ng-container>
1255
+ </dd>
1256
+ </div>
1257
+ }
1258
+ </div>
1259
+ </div>
1260
+ }
1261
+ </div>
1262
+ }
1263
+ </ng-template>
1264
+
1265
+ @if (!hideFooter()) {
1266
+ <div
1267
+ class="tw:flex tw:flex-col tw:gap-2 tw:px-2 tw:py-4 tw:sm:flex-row tw:sm:items-center tw:sm:justify-between"
1268
+ [tanStackTable]="_table"
1269
+ >
1270
+ <div class="tw:text-muted-foreground tw:flex-1 tw:text-sm">{{ _statusText() }}</div>
1271
+ <eg-data-table-pagination
1272
+ [showStatus]="false"
1273
+ [showPageSize]="showPageSize()"
1274
+ [pageSizes]="pageSizes()"
1275
+ [navMode]="navMode()"
1276
+ [size]="size()"
1277
+ />
1278
+ </div>
1279
+ }
1280
+ </div>
1281
+ `,
1282
+ changeDetection: ChangeDetectionStrategy.OnPush,
1283
+ }]
1284
+ }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], gridColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridColumns", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], manualPagination: [{ type: i0.Input, args: [{ isSignal: true, alias: "manualPagination", required: false }] }], features: [{ type: i0.Input, args: [{ isSignal: true, alias: "features", required: false }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], enableSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableSelection", required: false }] }], hideFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideFooter", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }, { type: i0.Output, args: ["layoutChange"] }], showLayoutToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLayoutToggle", required: false }] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], filterColumnId: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterColumnId", required: false }] }], filterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPlaceholder", required: false }] }], showColumnToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showColumnToggle", required: false }] }], toolbarActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbarActions", required: false }] }], defaultPageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultPageSize", required: false }] }], filterRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterRows", required: false }] }], initialSorting: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialSorting", required: false }] }], initialColumnFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialColumnFilters", required: false }] }], initialColumnVisibility: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialColumnVisibility", required: false }] }], initialRowSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialRowSelection", required: false }] }], showPageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPageSize", required: false }] }], pageSizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizes", required: false }] }], navMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "navMode", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], pageSizeChange: [{ type: i0.Output, args: ["pageSizeChange"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], sortingChange: [{ type: i0.Output, args: ["sortingChange"] }], columnFiltersChange: [{ type: i0.Output, args: ["columnFiltersChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], columnVisibilityChange: [{ type: i0.Output, args: ["columnVisibilityChange"] }] } });
1285
+
1286
+ /**
1287
+ * Sortable + hideable column header.
1288
+ *
1289
+ * Render via `flexRenderComponent(EgDataTableColumnHeader, { inputs: { column, title } })`
1290
+ * so the extra `title` input flows alongside the flex-render `column` context.
1291
+ * Non-sortable columns render a plain label.
1292
+ */
1293
+ class EgDataTableColumnHeader {
1294
+ column = input.required(/* @ts-ignore */
1295
+ ...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
1296
+ title = input.required(/* @ts-ignore */
1297
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
1298
+ /** Density of the trigger button. */
1299
+ size = input('default', /* @ts-ignore */
1300
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
1301
+ _sorted = computed(() => this.column().getIsSorted(), /* @ts-ignore */
1302
+ ...(ngDevMode ? [{ debugName: "_sorted" }] : /* istanbul ignore next */ []));
1303
+ /** Height per density. Merged by `hlmButton`. */
1304
+ _buttonClass = computed(() => hlm('tw:-ml-3', this.size() === 'sm' ? 'tw:h-7' : this.size() === 'lg' ? 'tw:h-9' : 'tw:h-8'), /* @ts-ignore */
1305
+ ...(ngDevMode ? [{ debugName: "_buttonClass" }] : /* istanbul ignore next */ []));
1306
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgDataTableColumnHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
1307
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgDataTableColumnHeader, isStandalone: true, selector: "eg-data-table-column-header", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideIcons({ lucideArrowDown, lucideArrowUp, lucideChevronsUpDown, lucideEyeOff })], ngImport: i0, template: `
1308
+ @if (column().getCanSort()) {
1309
+ <div class="tw:flex tw:items-center tw:gap-2">
1310
+ <button
1311
+ hlmButton
1312
+ variant="ghost"
1313
+ size="sm"
1314
+ [class]="_buttonClass()"
1315
+ type="button"
1316
+ [hlmDropdownMenuTrigger]="_menu"
1317
+ >
1318
+ <span>{{ title() }}</span>
1319
+ @switch (_sorted()) {
1320
+ @case ('desc') {
1321
+ <ng-icon hlm size="sm" name="lucideArrowDown" />
1322
+ }
1323
+ @case ('asc') {
1324
+ <ng-icon hlm size="sm" name="lucideArrowUp" />
1325
+ }
1326
+ @default {
1327
+ <ng-icon hlm size="sm" name="lucideChevronsUpDown" />
1328
+ }
1329
+ }
1330
+ </button>
1331
+ <ng-template #_menu>
1332
+ <div hlmDropdownMenu>
1333
+ <button hlmDropdownMenuItem type="button" (click)="column().toggleSorting(false)">
1334
+ <ng-icon hlm size="sm" name="lucideArrowUp" />
1335
+ Asc
1336
+ </button>
1337
+ <button hlmDropdownMenuItem type="button" (click)="column().toggleSorting(true)">
1338
+ <ng-icon hlm size="sm" name="lucideArrowDown" />
1339
+ Desc
1340
+ </button>
1341
+ <div hlmDropdownMenuSeparator></div>
1342
+ <button hlmDropdownMenuItem type="button" (click)="column().toggleVisibility(false)">
1343
+ <ng-icon hlm size="sm" name="lucideEyeOff" />
1344
+ Hide
1345
+ </button>
1346
+ </div>
1347
+ </ng-template>
1348
+ </div>
1349
+ } @else {
1350
+ <div>{{ title() }}</div>
1351
+ }
1352
+ `, isInline: true, dependencies: [{ kind: "component", type: HlmButton, selector: "button[hlmButton], a[hlmButton]", inputs: ["variant", "size", "appearance", "loading", "icon", "iconPosition", "class", "spinnerUserClass", "disabled", "type"] }, { kind: "directive", type: i1$1.HlmDropdownMenu, selector: "[hlmDropdownMenu],hlm-dropdown-menu", inputs: ["sideOffset"] }, { kind: "directive", type: i1$1.HlmDropdownMenuItem, selector: "[hlmDropdownMenuItem],hlm-dropdown-menu-item", inputs: ["disabled", "variant", "inset"] }, { kind: "directive", type: i1$1.HlmDropdownMenuSeparator, selector: "[hlmDropdownMenuSeparator],hlm-dropdown-menu-separator" }, { kind: "directive", type: i1$1.HlmDropdownMenuTrigger, selector: "[hlmDropdownMenuTrigger]", inputs: ["align", "side"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: HlmIcon, selector: "ng-icon[hlm]", inputs: ["size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1353
+ }
1354
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgDataTableColumnHeader, decorators: [{
1355
+ type: Component,
1356
+ args: [{
1357
+ selector: 'eg-data-table-column-header',
1358
+ imports: [HlmButton, HlmDropdownMenuImports, NgIcon, HlmIcon],
1359
+ providers: [provideIcons({ lucideArrowDown, lucideArrowUp, lucideChevronsUpDown, lucideEyeOff })],
1360
+ template: `
1361
+ @if (column().getCanSort()) {
1362
+ <div class="tw:flex tw:items-center tw:gap-2">
1363
+ <button
1364
+ hlmButton
1365
+ variant="ghost"
1366
+ size="sm"
1367
+ [class]="_buttonClass()"
1368
+ type="button"
1369
+ [hlmDropdownMenuTrigger]="_menu"
1370
+ >
1371
+ <span>{{ title() }}</span>
1372
+ @switch (_sorted()) {
1373
+ @case ('desc') {
1374
+ <ng-icon hlm size="sm" name="lucideArrowDown" />
1375
+ }
1376
+ @case ('asc') {
1377
+ <ng-icon hlm size="sm" name="lucideArrowUp" />
1378
+ }
1379
+ @default {
1380
+ <ng-icon hlm size="sm" name="lucideChevronsUpDown" />
1381
+ }
1382
+ }
1383
+ </button>
1384
+ <ng-template #_menu>
1385
+ <div hlmDropdownMenu>
1386
+ <button hlmDropdownMenuItem type="button" (click)="column().toggleSorting(false)">
1387
+ <ng-icon hlm size="sm" name="lucideArrowUp" />
1388
+ Asc
1389
+ </button>
1390
+ <button hlmDropdownMenuItem type="button" (click)="column().toggleSorting(true)">
1391
+ <ng-icon hlm size="sm" name="lucideArrowDown" />
1392
+ Desc
1393
+ </button>
1394
+ <div hlmDropdownMenuSeparator></div>
1395
+ <button hlmDropdownMenuItem type="button" (click)="column().toggleVisibility(false)">
1396
+ <ng-icon hlm size="sm" name="lucideEyeOff" />
1397
+ Hide
1398
+ </button>
1399
+ </div>
1400
+ </ng-template>
1401
+ </div>
1402
+ } @else {
1403
+ <div>{{ title() }}</div>
1404
+ }
1405
+ `,
1406
+ changeDetection: ChangeDetectionStrategy.OnPush,
1407
+ }]
1408
+ }], propDecorators: { column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
1409
+
1410
+ /*
1411
+ * Public API Surface of data-table
1412
+ */
1413
+ const EgDataTableImports = [
1414
+ EgDataTable,
1415
+ EgDataTableColumnHeader,
1416
+ EgDataTablePagination,
1417
+ EgDataTableViewOptions,
1418
+ EgTableHeadSelection,
1419
+ EgTableRowSelection,
1420
+ ];
1421
+ class EgDataTableModule {
1422
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgDataTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1423
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.3", ngImport: i0, type: EgDataTableModule, imports: [EgDataTable,
1424
+ EgDataTableColumnHeader,
1425
+ EgDataTablePagination,
1426
+ EgDataTableViewOptions,
1427
+ EgTableHeadSelection,
1428
+ EgTableRowSelection], exports: [EgDataTable,
1429
+ EgDataTableColumnHeader,
1430
+ EgDataTablePagination,
1431
+ EgDataTableViewOptions,
1432
+ EgTableHeadSelection,
1433
+ EgTableRowSelection] });
1434
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgDataTableModule, imports: [EgDataTableImports] });
1435
+ }
1436
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgDataTableModule, decorators: [{
1437
+ type: NgModule,
1438
+ args: [{
1439
+ imports: [...EgDataTableImports],
1440
+ exports: [...EgDataTableImports],
1441
+ }]
1442
+ }] });
1443
+
1444
+ /**
1445
+ * Generated bundle index. Do not edit.
1446
+ */
1447
+
1448
+ export { EgDataTable, EgDataTableColumnHeader, EgDataTableImports, EgDataTableModule, EgDataTablePagination, EgDataTableViewOptions, EgTableHeadSelection, EgTableRowSelection, defaultEgDataTableFeatures };
1449
+