ngx-kit 3.2.0 → 5.0.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,619 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, makeEnvironmentProviders, input, inject, TemplateRef, Directive, output, HostListener, model, computed, effect, ChangeDetectionStrategy, Component, signal, ContentChildren } from '@angular/core';
3
+ import * as i1$1 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import * as i1 from '@angular/forms';
6
+ import { FormsModule } from '@angular/forms';
7
+ import { DirectionService } from 'ngx-kit/shared';
8
+
9
+ /**
10
+ * Identity helper: یک رجیستری رندرر را با inference کامل تعریف می‌کند.
11
+ * همین آبجکت باید هم در provideTable({ renderers }) و هم در defineFields(renderers, ...)
12
+ * استفاده شود تا کلیدها (و ورودی‌های اضافه‌ی هر رندرر) در کل پروژه type-check شوند.
13
+ */
14
+ function defineRenderers(registry) {
15
+ return registry;
16
+ }
17
+ /**
18
+ * Identity helper برای تعریف ستون‌های یک جدول با inference و type-check کامل
19
+ * نسبت به هم مدل داده (T) و هم رجیستری رندررها (R).
20
+ *
21
+ * @example
22
+ * const renderers = defineRenderers({ avatar: AvatarCellRenderer, boolean: BooleanCellRenderer });
23
+ *
24
+ * const fields = defineFields<Tenant, typeof renderers>(renderers, [
25
+ * { column: 'avatarUrl', title: 'کاربر', renderer: 'avatar', rendererInputs: { showName: true } }, // ✅
26
+ * { column: 'avatarUrl', title: 'کاربر', renderer: 'avatar', rendererInputs: { showName: 'x' } }, // ❌ باید boolean باشد
27
+ * { column: 'isActive', title: 'فعال', renderer: 'bool' }, // ❌ کلید رجیستری نیست
28
+ * { column: 'ageXYZ', title: '...' }, // ❌ فیلد Tenant نیست
29
+ * ]);
30
+ */
31
+ function defineFields(_renderers, fields) {
32
+ return fields;
33
+ }
34
+
35
+ const NGX_TABLE_CONFIG_DEFAULT = {
36
+ showRecordNumber: true,
37
+ hoverable: true,
38
+ multiSort: true,
39
+ resizable: true,
40
+ stickyHeader: true,
41
+ stripedRows: true,
42
+ column: { minWidth: 60, maxWidth: 600, defaultWidth: 150 },
43
+ labels: {
44
+ noData: 'There is no data to display!',
45
+ loading: 'Loading...',
46
+ operation: 'Operation',
47
+ },
48
+ renderers: {},
49
+ formatters: {},
50
+ };
51
+ /** به هر نمونه‌ی ngx-table تزریق می‌شود. با provideTable() مقداردهی کنید. */
52
+ const NGX_TABLE_CONFIG = new InjectionToken('ngx-table-config', {
53
+ factory: () => NGX_TABLE_CONFIG_DEFAULT,
54
+ });
55
+
56
+ const NGX_PAGINATION_CONFIG_DEFAULT = {
57
+ defaultPageSize: 10,
58
+ pageSizeOptions: [5, 10, 20, 30, 50, 100],
59
+ labels: {
60
+ first: 'First',
61
+ last: 'Last',
62
+ next: 'Next',
63
+ previous: 'Previous',
64
+ showing: 'Showing',
65
+ to: 'to',
66
+ of: 'of',
67
+ results: 'results',
68
+ pageSize: 'Page Size',
69
+ },
70
+ };
71
+ const NGX_PAGINATION_CONFIG = new InjectionToken('ngx-pagination-config', {
72
+ factory: () => NGX_PAGINATION_CONFIG_DEFAULT,
73
+ });
74
+
75
+ function mergeTableConfig(base, override) {
76
+ if (!override)
77
+ return base;
78
+ return {
79
+ ...base,
80
+ ...override,
81
+ column: { ...base.column, ...override.column },
82
+ labels: { ...base.labels, ...override.labels },
83
+ renderers: { ...base.renderers, ...override.renderers },
84
+ formatters: { ...base.formatters, ...override.formatters },
85
+ };
86
+ }
87
+ /**
88
+ * کانفیگ سراسری <ngx-table>: جهت پیش‌فرض، حداقل/حداکثر عرض ستون، فعال بودن
89
+ * پیش‌فرض سورت چندستونه، و از همه مهم‌تر رجیستری رندررها.
90
+ *
91
+ * @example
92
+ * provideTable({
93
+ * renderers: defineRenderers({ avatar: AvatarCellRenderer, boolean: BooleanCellRenderer }),
94
+ * column: { minWidth: 80, maxWidth: 480, defaultWidth: 160 },
95
+ * })
96
+ */
97
+ function provideTable(options) {
98
+ return makeEnvironmentProviders([
99
+ { provide: NGX_TABLE_CONFIG, useValue: mergeTableConfig(NGX_TABLE_CONFIG_DEFAULT, options) },
100
+ ]);
101
+ }
102
+
103
+ function mergePaginationConfig(base, override) {
104
+ if (!override)
105
+ return base;
106
+ return {
107
+ ...base,
108
+ ...override,
109
+ labels: { ...base.labels, ...override.labels },
110
+ };
111
+ }
112
+ function providePagination(options) {
113
+ return makeEnvironmentProviders([
114
+ {
115
+ provide: NGX_PAGINATION_CONFIG,
116
+ useValue: mergePaginationConfig(NGX_PAGINATION_CONFIG_DEFAULT, options),
117
+ },
118
+ ]);
119
+ }
120
+
121
+ /**
122
+ * برای projection قالب سفارشی یک ستون بدون این‌که مجبور باشید cellTemplate را
123
+ * از طریق ViewChild به آبجکت ستون وصل کنید:
124
+ *
125
+ * <ngx-table [fields]="fields" [data]="rows()">
126
+ * <ng-template ngxTableCell="userName" let-row let-value="value">
127
+ * <strong>{{ value }}</strong>
128
+ * </ng-template>
129
+ * </ngx-table>
130
+ *
131
+ * مهم: چون این دایرکتیو در تمپلیت *کامپوننت مصرف‌کننده* (نه ngx-table) استفاده
132
+ * می‌شود، باید آن را در آرایه‌ی imports همان کامپوننت (standalone) وارد کنید:
133
+ * imports: [NgxTable, TableCell]
134
+ *
135
+ * محدودیت شناخته‌شده: context این تمپلیت (row/value) از نوع T دقیق نیست
136
+ * (چون TemplateRef اینجا به‌صورت پویا از ContentChildren جمع‌آوری می‌شود و
137
+ * اطلاعات جنریک T در آن نقطه از بین می‌رود). برای type-safety کامل روی
138
+ * row/value، از field.cellTemplate به همراه @ViewChild استفاده کنید (به README
139
+ * بخش «Cell Template کاملاً Type-Safe» مراجعه کنید).
140
+ */
141
+ class TableCell {
142
+ column = input.required({ ...(ngDevMode ? { debugName: "column" } : /* istanbul ignore next */ {}), alias: 'ngxTableCell' });
143
+ templateRef = inject((TemplateRef));
144
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: TableCell, deps: [], target: i0.ɵɵFactoryTarget.Directive });
145
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.4", type: TableCell, isStandalone: true, selector: "ng-template[ngxTableCell]", inputs: { column: { classPropertyName: "column", publicName: "ngxTableCell", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
146
+ }
147
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: TableCell, decorators: [{
148
+ type: Directive,
149
+ args: [{
150
+ selector: 'ng-template[ngxTableCell]',
151
+ standalone: true,
152
+ }]
153
+ }], propDecorators: { column: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngxTableCell", required: true }] }] } });
154
+
155
+ /**
156
+ * دستگیره‌ی درگ برای ریسایز ستون. روی لبه‌ی «انتهای منطقی» (inline-end) قرار
157
+ * می‌گیرد، یعنی خودش زیر [dir="rtl"] جابه‌جا می‌شود؛ فقط جهت محاسبه‌ی دلتا هم
158
+ * برعکس می‌شود تا حس درگ همیشه طبیعی باشد.
159
+ *
160
+ * مقدار نهایی توسط جدول در یک Map از عرض‌ها ذخیره می‌شود (نه در استایل مستقیم
161
+ * ستون)، به همراه علامت‌گذاری «دستی ریسایز شده» — یعنی بعد از این، هیچ
162
+ * re-render یا تغییر داده‌ای این عرض را از بین نمی‌برد.
163
+ */
164
+ class ColumnResize {
165
+ startWidth = input.required({ ...(ngDevMode ? { debugName: "startWidth" } : /* istanbul ignore next */ {}), alias: 'ngxColumnResizeWidth' });
166
+ minWidth = input(40, { ...(ngDevMode ? { debugName: "minWidth" } : /* istanbul ignore next */ {}), alias: 'ngxColumnResizeMin' });
167
+ maxWidth = input(1000, { ...(ngDevMode ? { debugName: "maxWidth" } : /* istanbul ignore next */ {}), alias: 'ngxColumnResizeMax' });
168
+ direction = input('ltr', { ...(ngDevMode ? { debugName: "direction" } : /* istanbul ignore next */ {}), alias: 'ngxColumnResizeDir' });
169
+ resizing = output();
170
+ resizeEnd = output();
171
+ dragging = false;
172
+ startX = 0;
173
+ baseWidth = 0;
174
+ onPointerDown(event) {
175
+ event.preventDefault();
176
+ event.stopPropagation();
177
+ this.dragging = true;
178
+ this.startX = event.clientX;
179
+ this.baseWidth = this.startWidth();
180
+ event.target.setPointerCapture(event.pointerId);
181
+ }
182
+ onPointerMove(event) {
183
+ if (!this.dragging)
184
+ return;
185
+ this.resizing.emit(this.computeWidth(event.clientX));
186
+ }
187
+ onPointerUp(event) {
188
+ if (!this.dragging)
189
+ return;
190
+ this.dragging = false;
191
+ this.resizeEnd.emit(this.computeWidth(event.clientX));
192
+ }
193
+ computeWidth(clientX) {
194
+ const delta = clientX - this.startX;
195
+ const signedDelta = this.direction() === 'rtl' ? -delta : delta;
196
+ const raw = this.baseWidth + signedDelta;
197
+ return Math.min(this.maxWidth(), Math.max(this.minWidth(), raw));
198
+ }
199
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ColumnResize, deps: [], target: i0.ɵɵFactoryTarget.Directive });
200
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.4", type: ColumnResize, isStandalone: true, selector: "[ngxColumnResize]", inputs: { startWidth: { classPropertyName: "startWidth", publicName: "ngxColumnResizeWidth", isSignal: true, isRequired: true, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "ngxColumnResizeMin", isSignal: true, isRequired: false, transformFunction: null }, maxWidth: { classPropertyName: "maxWidth", publicName: "ngxColumnResizeMax", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "ngxColumnResizeDir", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { resizing: "resizing", resizeEnd: "resizeEnd" }, host: { attributes: { "role": "separator", "aria-orientation": "vertical" }, listeners: { "pointerdown": "onPointerDown($event)", "pointermove": "onPointerMove($event)", "pointerup": "onPointerUp($event)", "pointercancel": "onPointerUp($event)" }, classAttribute: "ngx-table__resize-handle" }, ngImport: i0 });
201
+ }
202
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ColumnResize, decorators: [{
203
+ type: Directive,
204
+ args: [{
205
+ selector: '[ngxColumnResize]',
206
+ standalone: true,
207
+ host: {
208
+ class: 'ngx-table__resize-handle',
209
+ role: 'separator',
210
+ 'aria-orientation': 'vertical',
211
+ },
212
+ }]
213
+ }], propDecorators: { startWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngxColumnResizeWidth", required: true }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngxColumnResizeMin", required: false }] }], maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngxColumnResizeMax", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngxColumnResizeDir", required: false }] }], resizing: [{ type: i0.Output, args: ["resizing"] }], resizeEnd: [{ type: i0.Output, args: ["resizeEnd"] }], onPointerDown: [{
214
+ type: HostListener,
215
+ args: ['pointerdown', ['$event']]
216
+ }], onPointerMove: [{
217
+ type: HostListener,
218
+ args: ['pointermove', ['$event']]
219
+ }], onPointerUp: [{
220
+ type: HostListener,
221
+ args: ['pointerup', ['$event']]
222
+ }, {
223
+ type: HostListener,
224
+ args: ['pointercancel', ['$event']]
225
+ }] } });
226
+
227
+ class NgxPagination {
228
+ config = inject(NGX_PAGINATION_CONFIG);
229
+ total = input.required(/* @ts-ignore */
230
+ ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
231
+ page = model(1, /* @ts-ignore */
232
+ ...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
233
+ pageSize = model(this.config.defaultPageSize, /* @ts-ignore */
234
+ ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
235
+ pageSizeOptions = input(this.config.pageSizeOptions, /* @ts-ignore */
236
+ ...(ngDevMode ? [{ debugName: "pageSizeOptions" }] : /* istanbul ignore next */ []));
237
+ labels = input(this.config.labels, /* @ts-ignore */
238
+ ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
239
+ siblingCount = input(1, /* @ts-ignore */
240
+ ...(ngDevMode ? [{ debugName: "siblingCount" }] : /* istanbul ignore next */ []));
241
+ boundaryCount = input(1, /* @ts-ignore */
242
+ ...(ngDevMode ? [{ debugName: "boundaryCount" }] : /* istanbul ignore next */ []));
243
+ paginate = output();
244
+ totalPages = computed(() => Math.max(1, Math.ceil(this.total() / this.pageSize())), /* @ts-ignore */
245
+ ...(ngDevMode ? [{ debugName: "totalPages" }] : /* istanbul ignore next */ []));
246
+ firstItem = computed(() => this.total() === 0 ? 0 : (this.page() - 1) * this.pageSize() + 1, /* @ts-ignore */
247
+ ...(ngDevMode ? [{ debugName: "firstItem" }] : /* istanbul ignore next */ []));
248
+ lastItem = computed(() => Math.min(this.firstItem() + this.pageSize() - 1, this.total()), /* @ts-ignore */
249
+ ...(ngDevMode ? [{ debugName: "lastItem" }] : /* istanbul ignore next */ []));
250
+ pages = computed(() => this.buildPages(this.page(), this.totalPages(), this.siblingCount(), this.boundaryCount()), /* @ts-ignore */
251
+ ...(ngDevMode ? [{ debugName: "pages" }] : /* istanbul ignore next */ []));
252
+ constructor() {
253
+ // هر تغییری در page/pageSize را برای مصرف‌کننده امیت کن.
254
+ effect(() => {
255
+ this.paginate.emit({ page: this.page(), pageSize: this.pageSize() });
256
+ });
257
+ // اگر بعد از کوچک‌شدن total، صفحه‌ی جاری خارج از بازه افتاد، اصلاحش کن.
258
+ effect(() => {
259
+ const max = this.totalPages();
260
+ if (this.page() > max)
261
+ this.page.set(max);
262
+ });
263
+ }
264
+ buildPages(current, total, sibling = 1, boundary = 1) {
265
+ const range = (start, end) => Array.from({ length: end - start + 1 }, (_, i) => start + i);
266
+ const totalNumbers = sibling * 2 + boundary * 2 + 3;
267
+ if (total <= totalNumbers)
268
+ return range(1, total);
269
+ const leftSibling = Math.max(current - sibling, boundary + 2);
270
+ const rightSibling = Math.min(current + sibling, total - boundary - 1);
271
+ const showLeftDots = leftSibling > boundary + 2;
272
+ const showRightDots = rightSibling < total - boundary - 1;
273
+ const pages = [];
274
+ pages.push(...range(1, boundary));
275
+ if (showLeftDots)
276
+ pages.push('...');
277
+ else
278
+ pages.push(...range(boundary + 1, leftSibling - 1));
279
+ pages.push(...range(leftSibling, rightSibling));
280
+ if (showRightDots)
281
+ pages.push('...');
282
+ else
283
+ pages.push(...range(rightSibling + 1, total - boundary));
284
+ pages.push(...range(total - boundary + 1, total));
285
+ return pages;
286
+ }
287
+ next() {
288
+ this.page.update((p) => Math.min(p + 1, this.totalPages()));
289
+ }
290
+ previous() {
291
+ this.page.update((p) => Math.max(1, p - 1));
292
+ }
293
+ first() {
294
+ this.page.set(1);
295
+ }
296
+ last() {
297
+ this.page.set(this.totalPages());
298
+ }
299
+ goto(page) {
300
+ if (typeof page !== 'number' || Number.isNaN(page))
301
+ return;
302
+ this.page.set(page);
303
+ }
304
+ onPageSizeChange(value) {
305
+ this.pageSize.set(Number(value));
306
+ this.page.set(1);
307
+ }
308
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxPagination, deps: [], target: i0.ɵɵFactoryTarget.Component });
309
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: NgxPagination, isStandalone: true, selector: "ngx-pagination", inputs: { total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: true, transformFunction: null }, page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, siblingCount: { classPropertyName: "siblingCount", publicName: "siblingCount", isSignal: true, isRequired: false, transformFunction: null }, boundaryCount: { classPropertyName: "boundaryCount", publicName: "boundaryCount", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { page: "pageChange", pageSize: "pageSizeChange", paginate: "paginate" }, ngImport: i0, template: "<nav class=\"ngx-pagination\">\n <div class=\"ngx-pagination__summary\">\n {{ labels().showing }}\n <span class=\"fw-semibold\">{{ firstItem() }}</span>\n {{ labels().to }}\n <span class=\"fw-semibold\">{{ lastItem() }}</span>\n {{ labels().of }}\n <span class=\"fw-semibold\">{{ total() }}</span>\n {{ labels().results }}\n </div>\n\n <div class=\"ngx-pagination__controls\">\n <ul class=\"ngx-pagination__list\">\n <li [class.disabled]=\"page() === 1\" [title]=\"labels().first\">\n <a (click)=\"first()\" class=\"icon\">\u00AB</a>\n </li>\n <li [class.disabled]=\"page() === 1\" [title]=\"labels().previous\">\n <a (click)=\"previous()\" class=\"icon\">\u2039</a>\n </li>\n\n @for (item of pages(); track $index) {\n <li class=\"ngx-pagination__page-num\" [class.active]=\"item === page()\">\n @if (item !== page() && item !== '...') {\n <a (click)=\"goto(+item)\">{{ item }}</a>\n }\n @if (item === page() || item === '...') {\n <span>{{ item }}</span>\n }\n </li>\n }\n\n <li [class.disabled]=\"page() === totalPages()\" [title]=\"labels().next\">\n <a (click)=\"next()\" class=\"icon\">\u203A</a>\n </li>\n <li [class.disabled]=\"page() === totalPages()\" [title]=\"labels().last\">\n <a (click)=\"last()\" class=\"icon\">\u00BB</a>\n </li>\n </ul>\n\n <div class=\"ngx-pagination__page-size\">\n <span>{{ labels().pageSize }}:</span>\n <select [ngModel]=\"pageSize()\" (ngModelChange)=\"onPageSizeChange($event)\">\n @for (opt of pageSizeOptions(); track opt) {\n <option [value]=\"opt\" [selected]=\"opt === pageSize()\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n</nav>\n", styles: [".ngx-pagination{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;padding:.625rem 1rem;font-size:.8125rem}.ngx-pagination__controls{display:flex;align-items:center;gap:1rem;flex-wrap:wrap}.ngx-pagination__list{display:flex;align-items:center;gap:.25rem;list-style:none;margin:0;padding:0}.ngx-pagination__list li{min-width:1.75rem;height:1.75rem;display:inline-flex;align-items:center;justify-content:center;border-radius:6px}.ngx-pagination__list li a,.ngx-pagination__list li span{display:inline-flex;align-items:center;justify-content:center;width:100%;height:100%;cursor:pointer;border-radius:6px;-webkit-user-select:none;user-select:none}.ngx-pagination__list li a.icon,.ngx-pagination__list li span.icon{font-size:20px;font-family:Courier New,Courier,monospace}.ngx-pagination__list li.active a,.ngx-pagination__list li.active span{background:var(--ngx-table-accent, #4f46e5);color:#fff;cursor:default}.ngx-pagination__list li.disabled{opacity:.4;pointer-events:none}.ngx-pagination__list li:hover:not(.active):not(.disabled) a{background:color-mix(in srgb,var(--ngx-table-accent, #4f46e5) 12%,transparent)}.ngx-pagination__page-size{display:flex;align-items:center;gap:.5rem}.ngx-pagination__page-size select{border-radius:6px;padding:.25rem .5rem;font-size:.8125rem}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
310
+ }
311
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxPagination, decorators: [{
312
+ type: Component,
313
+ args: [{ selector: 'ngx-pagination', standalone: true, imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav class=\"ngx-pagination\">\n <div class=\"ngx-pagination__summary\">\n {{ labels().showing }}\n <span class=\"fw-semibold\">{{ firstItem() }}</span>\n {{ labels().to }}\n <span class=\"fw-semibold\">{{ lastItem() }}</span>\n {{ labels().of }}\n <span class=\"fw-semibold\">{{ total() }}</span>\n {{ labels().results }}\n </div>\n\n <div class=\"ngx-pagination__controls\">\n <ul class=\"ngx-pagination__list\">\n <li [class.disabled]=\"page() === 1\" [title]=\"labels().first\">\n <a (click)=\"first()\" class=\"icon\">\u00AB</a>\n </li>\n <li [class.disabled]=\"page() === 1\" [title]=\"labels().previous\">\n <a (click)=\"previous()\" class=\"icon\">\u2039</a>\n </li>\n\n @for (item of pages(); track $index) {\n <li class=\"ngx-pagination__page-num\" [class.active]=\"item === page()\">\n @if (item !== page() && item !== '...') {\n <a (click)=\"goto(+item)\">{{ item }}</a>\n }\n @if (item === page() || item === '...') {\n <span>{{ item }}</span>\n }\n </li>\n }\n\n <li [class.disabled]=\"page() === totalPages()\" [title]=\"labels().next\">\n <a (click)=\"next()\" class=\"icon\">\u203A</a>\n </li>\n <li [class.disabled]=\"page() === totalPages()\" [title]=\"labels().last\">\n <a (click)=\"last()\" class=\"icon\">\u00BB</a>\n </li>\n </ul>\n\n <div class=\"ngx-pagination__page-size\">\n <span>{{ labels().pageSize }}:</span>\n <select [ngModel]=\"pageSize()\" (ngModelChange)=\"onPageSizeChange($event)\">\n @for (opt of pageSizeOptions(); track opt) {\n <option [value]=\"opt\" [selected]=\"opt === pageSize()\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n</nav>\n", styles: [".ngx-pagination{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;padding:.625rem 1rem;font-size:.8125rem}.ngx-pagination__controls{display:flex;align-items:center;gap:1rem;flex-wrap:wrap}.ngx-pagination__list{display:flex;align-items:center;gap:.25rem;list-style:none;margin:0;padding:0}.ngx-pagination__list li{min-width:1.75rem;height:1.75rem;display:inline-flex;align-items:center;justify-content:center;border-radius:6px}.ngx-pagination__list li a,.ngx-pagination__list li span{display:inline-flex;align-items:center;justify-content:center;width:100%;height:100%;cursor:pointer;border-radius:6px;-webkit-user-select:none;user-select:none}.ngx-pagination__list li a.icon,.ngx-pagination__list li span.icon{font-size:20px;font-family:Courier New,Courier,monospace}.ngx-pagination__list li.active a,.ngx-pagination__list li.active span{background:var(--ngx-table-accent, #4f46e5);color:#fff;cursor:default}.ngx-pagination__list li.disabled{opacity:.4;pointer-events:none}.ngx-pagination__list li:hover:not(.active):not(.disabled) a{background:color-mix(in srgb,var(--ngx-table-accent, #4f46e5) 12%,transparent)}.ngx-pagination__page-size{display:flex;align-items:center;gap:.5rem}.ngx-pagination__page-size select{border-radius:6px;padding:.25rem .5rem;font-size:.8125rem}\n"] }]
314
+ }], ctorParameters: () => [], propDecorators: { total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: true }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }, { type: i0.Output, args: ["pageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], siblingCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "siblingCount", required: false }] }], boundaryCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "boundaryCount", required: false }] }], paginate: [{ type: i0.Output, args: ["paginate"] }] } });
315
+
316
+ /**
317
+ * جدول داده‌ی جنریک و Type-Safe.
318
+ *
319
+ * `T` از روی چیزی که به [data] بایند می‌کنید استنتاج می‌شود؛ `field.column` در
320
+ * `TableField<T, R>` محدود به `keyof T` است، و اگر از defineFields(renderers, ...)
321
+ * استفاده کنید، `field.renderer` محدود به کلیدهای رجیستری و `rendererInputs`
322
+ * دقیقاً همان ورودی‌های اضافه‌ی همان renderer است.
323
+ *
324
+ * دو حالت:
325
+ * - کلاینت‌ساید (پیش‌فرض): [data] کل آرایه است؛ سورت/صفحه‌بندی داخلی و روی
326
+ * یک computed خالص انجام می‌شود (بدون mutation روی آبجکت‌های شما).
327
+ * - lazy: [lazy]="true" + [totalRecords]؛ [data] فقط صفحه‌ی جاری است؛ به
328
+ * (lazyLoad) گوش بدهید.
329
+ */
330
+ class NgxTable {
331
+ config = inject(NGX_TABLE_CONFIG);
332
+ paginationConfig = inject(NGX_PAGINATION_CONFIG);
333
+ // ---------------------------------------------------------------- inputs
334
+ fields = input.required(/* @ts-ignore */
335
+ ...(ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []));
336
+ data = input.required(/* @ts-ignore */
337
+ ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
338
+ totalRecords = input.required(/* @ts-ignore */
339
+ ...(ngDevMode ? [{ debugName: "totalRecords" }] : /* istanbul ignore next */ []));
340
+ showRecordNumber = input(this.config.showRecordNumber, /* @ts-ignore */
341
+ ...(ngDevMode ? [{ debugName: "showRecordNumber" }] : /* istanbul ignore next */ []));
342
+ operations = input(/* @ts-ignore */
343
+ ...(ngDevMode ? [undefined, { debugName: "operations" }] : /* istanbul ignore next */ []));
344
+ operationWidth = input(60, /* @ts-ignore */
345
+ ...(ngDevMode ? [{ debugName: "operationWidth" }] : /* istanbul ignore next */ []));
346
+ numWidth = input(60, /* @ts-ignore */
347
+ ...(ngDevMode ? [{ debugName: "numWidth" }] : /* istanbul ignore next */ []));
348
+ pageSize = input(this.paginationConfig.defaultPageSize, /* @ts-ignore */
349
+ ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
350
+ loading = input(false, /* @ts-ignore */
351
+ ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
352
+ multiSort = input(undefined, /* @ts-ignore */
353
+ ...(ngDevMode ? [{ debugName: "multiSort" }] : /* istanbul ignore next */ []));
354
+ lazy = input(false, /* @ts-ignore */
355
+ ...(ngDevMode ? [{ debugName: "lazy" }] : /* istanbul ignore next */ []));
356
+ /** پیش‌فرض بر اساس ایندکس؛ برای جلوگیری از گم‌شدن state سطرها هنگام سورت،
357
+ * یک شناسه‌ی یکتا بدهید: [trackBy]="(i, row) => row.id" */
358
+ trackBy = input((index) => index, /* @ts-ignore */
359
+ ...(ngDevMode ? [{ debugName: "trackBy" }] : /* istanbul ignore next */ []));
360
+ // --------------------------------------------------------------- outputs
361
+ lazyLoad = output();
362
+ sortChange = output();
363
+ columnResize = output();
364
+ // ------------------------------------------------------------ int. state
365
+ page = signal(1, /* @ts-ignore */
366
+ ...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
367
+ pageSizeS = signal(this.paginationConfig.defaultPageSize, /* @ts-ignore */
368
+ ...(ngDevMode ? [{ debugName: "pageSizeS" }] : /* istanbul ignore next */ []));
369
+ sorts = signal([], /* @ts-ignore */
370
+ ...(ngDevMode ? [{ debugName: "sorts" }] : /* istanbul ignore next */ []));
371
+ widths = signal({}, /* @ts-ignore */
372
+ ...(ngDevMode ? [{ debugName: "widths" }] : /* istanbul ignore next */ []));
373
+ manuallyResized = new Set();
374
+ rendererInputsCache = new Map();
375
+ cellTemplates;
376
+ templateMap = new Map();
377
+ resolvedMultiSort = computed(() => this.multiSort() ?? this.config.multiSort, /* @ts-ignore */
378
+ ...(ngDevMode ? [{ debugName: "resolvedMultiSort" }] : /* istanbul ignore next */ []));
379
+ directionService = inject(DirectionService);
380
+ resolvedDirection = computed(() => this.directionService.direction(), /* @ts-ignore */
381
+ ...(ngDevMode ? [{ debugName: "resolvedDirection" }] : /* istanbul ignore next */ []));
382
+ constructor() {
383
+ effect(() => {
384
+ const ps = this.pageSize();
385
+ if (ps)
386
+ this.pageSizeS.set(ps);
387
+ });
388
+ // برای هر ستونِ تازه‌دیده‌شده یک عرض seed کن؛ ستون‌هایی که کاربر دستی
389
+ // ریسایز کرده دیگر هیچ‌وقت دوباره نوشته نمی‌شوند.
390
+ effect(() => {
391
+ const cols = this.fields();
392
+ const current = this.widths();
393
+ let changed = false;
394
+ const next = { ...current };
395
+ for (const f of cols) {
396
+ if (!(f.column in next) && !this.manuallyResized.has(f.column)) {
397
+ next[f.column] = f.width ?? this.config.column.defaultWidth;
398
+ changed = true;
399
+ }
400
+ }
401
+ if (changed)
402
+ this.widths.set(next);
403
+ });
404
+ }
405
+ ngOnInit() {
406
+ if (this.lazy()) {
407
+ this.lazyLoad.emit({ first: 0, pageIndex: 1, pageSize: this.pageSizeS(), sorts: [] });
408
+ }
409
+ }
410
+ ngAfterContentInit() {
411
+ this.syncTemplateMap();
412
+ this.cellTemplates.changes.subscribe(() => this.syncTemplateMap());
413
+ }
414
+ syncTemplateMap() {
415
+ this.templateMap.clear();
416
+ this.cellTemplates.forEach((cell) => {
417
+ // نکته‌ی مهم: cell.column() باید فراخوانی شود (سیگنال است)، نه cell.column.name.
418
+ this.templateMap.set(cell.column(), cell.templateRef);
419
+ });
420
+ }
421
+ // ------------------------------------------------------------- داده‌ها
422
+ sortedData = computed(() => {
423
+ if (this.lazy())
424
+ return this.data();
425
+ const sorts = this.sorts();
426
+ if (sorts.length === 0)
427
+ return this.data();
428
+ const copy = [...this.data()];
429
+ copy.sort((a, b) => {
430
+ for (const s of sorts) {
431
+ const cmp = this.compare(a[s.field], b[s.field]);
432
+ if (cmp !== 0)
433
+ return s.direction === 'asc' ? cmp : -cmp;
434
+ }
435
+ return 0;
436
+ });
437
+ return copy;
438
+ }, /* @ts-ignore */
439
+ ...(ngDevMode ? [{ debugName: "sortedData" }] : /* istanbul ignore next */ []));
440
+ totalCount = computed(() => this.lazy() ? this.totalRecords() : this.data().length, /* @ts-ignore */
441
+ ...(ngDevMode ? [{ debugName: "totalCount" }] : /* istanbul ignore next */ []));
442
+ pagedData = computed(() => {
443
+ if (this.lazy())
444
+ return this.sortedData();
445
+ const start = (this.page() - 1) * this.pageSizeS();
446
+ return this.sortedData().slice(start, start + this.pageSizeS());
447
+ }, /* @ts-ignore */
448
+ ...(ngDevMode ? [{ debugName: "pagedData" }] : /* istanbul ignore next */ []));
449
+ compare(a, b) {
450
+ if (a == null && b == null)
451
+ return 0;
452
+ if (a == null)
453
+ return -1;
454
+ if (b == null)
455
+ return 1;
456
+ if (typeof a === 'number' && typeof b === 'number')
457
+ return a - b;
458
+ if (typeof a === 'boolean' && typeof b === 'boolean')
459
+ return a === b ? 0 : a ? 1 : -1;
460
+ if (a instanceof Date && b instanceof Date)
461
+ return a.getTime() - b.getTime();
462
+ return String(a).localeCompare(String(b));
463
+ }
464
+ /** بدون mutation روی داده‌ی ورودی: شماره‌ی ردیف صرفاً از روی page/pageSize/rowIndex محاسبه می‌شود. */
465
+ recordNumber(rowIndex) {
466
+ return (this.page() - 1) * this.pageSizeS() + rowIndex + 1;
467
+ }
468
+ // -------------------------------------------------------------- سورت
469
+ sortDirectionFor(column) {
470
+ return this.sorts().find((s) => s.field === column)?.direction ?? null;
471
+ }
472
+ onHeaderClick(field, event) {
473
+ if (!field.sortable)
474
+ return;
475
+ const column = field.column;
476
+ const current = this.sorts();
477
+ const existingIndex = current.findIndex((s) => s.field === column);
478
+ const isMultiGesture = this.resolvedMultiSort() && (event.shiftKey || event.ctrlKey || event.metaKey);
479
+ let next;
480
+ if (isMultiGesture) {
481
+ next = [...current];
482
+ if (existingIndex === -1) {
483
+ next.push({ field: column, direction: 'asc' });
484
+ }
485
+ else if (next[existingIndex].direction === 'asc') {
486
+ next[existingIndex] = { field: column, direction: 'desc' };
487
+ }
488
+ else {
489
+ next.splice(existingIndex, 1);
490
+ }
491
+ }
492
+ else if (existingIndex === -1 || current.length > 1) {
493
+ next = [{ field: column, direction: 'asc' }];
494
+ }
495
+ else if (current[0].direction === 'asc') {
496
+ next = [{ field: column, direction: 'desc' }];
497
+ }
498
+ else {
499
+ next = [];
500
+ }
501
+ this.sorts.set(next);
502
+ this.page.set(1);
503
+ this.sortChange.emit(next);
504
+ this.emitLazyIfNeeded();
505
+ }
506
+ // ------------------------------------------------------------ صفحه‌بندی
507
+ onPaginate(event) {
508
+ this.page.set(event.page);
509
+ this.pageSizeS.set(event.pageSize);
510
+ this.emitLazyIfNeeded();
511
+ }
512
+ emitLazyIfNeeded() {
513
+ if (!this.lazy())
514
+ return;
515
+ const pageIndex = this.page();
516
+ const pageSize = this.pageSizeS();
517
+ this.lazyLoad.emit({
518
+ pageIndex,
519
+ pageSize,
520
+ first: (pageIndex - 1) * pageSize,
521
+ sorts: this.sorts(),
522
+ });
523
+ }
524
+ // -------------------------------------------------------------- ریسایز
525
+ columnWidth(column) {
526
+ return this.widths()[column] ?? this.config.column.defaultWidth;
527
+ }
528
+ columnMin(field) {
529
+ return field.minWidth ?? this.config.column.minWidth;
530
+ }
531
+ columnMax(field) {
532
+ return field.maxWidth ?? this.config.column.maxWidth;
533
+ }
534
+ isResizable(field) {
535
+ return (field.resizable ?? this.config.resizable) !== false;
536
+ }
537
+ onColumnResizing(column, width) {
538
+ this.widths.update((w) => ({ ...w, [column]: width }));
539
+ }
540
+ onColumnResizeEnd(column, width) {
541
+ this.manuallyResized.add(column);
542
+ this.widths.update((w) => ({ ...w, [column]: width }));
543
+ this.columnResize.emit({ field: column, width });
544
+ }
545
+ // --------------------------------------------------------------- سلول‌ها
546
+ getTemplate(column) {
547
+ const fromDirective = this.templateMap.get(column);
548
+ if (fromDirective)
549
+ return fromDirective;
550
+ const fromFieldConfig = this.fields().find((f) => f.column === column)?.cellTemplate;
551
+ return fromFieldConfig ?? null;
552
+ }
553
+ getRenderer(field) {
554
+ if (!field.renderer)
555
+ return null;
556
+ // نکته: نوع دقیق field.renderer (که به کلیدهای رجیستری محدود است) در
557
+ // defineFields() چک می‌شود؛ اینجا فقط یک lookup رانتایم ساده لازم است.
558
+ return this.config.renderers[field.renderer] ?? null;
559
+ }
560
+ getCellValue(row, field) {
561
+ return row[field.column];
562
+ }
563
+ formatCellValue(row, field) {
564
+ const value = this.getCellValue(row, field);
565
+ if (typeof field.formatter === 'function') {
566
+ return this.stringify(field.formatter(value, row, field));
567
+ }
568
+ if (typeof field.formatter === 'string') {
569
+ const formatter = this.config.formatters[field.formatter];
570
+ if (formatter)
571
+ return this.stringify(formatter(value, row, field));
572
+ }
573
+ return this.stringify(value);
574
+ }
575
+ stringify(value) {
576
+ return value == null ? '' : String(value);
577
+ }
578
+ /** ورودی‌های renderer را به‌ازای هر (field, row) کش می‌کند تا هر Change
579
+ * Detection یک آبجکت جدید ساخته نشود؛ با WeakMap، وقتی row از حافظه خارج
580
+ * شود (مثلاً بعد از عوض‌شدن صفحه)، entry آن هم خودکار پاک می‌شود. */
581
+ getRendererInputs(row, field) {
582
+ let fieldCache = this.rendererInputsCache.get(field);
583
+ if (!fieldCache) {
584
+ fieldCache = new WeakMap();
585
+ this.rendererInputsCache.set(field, fieldCache);
586
+ }
587
+ let inputs = fieldCache.get(row);
588
+ if (!inputs) {
589
+ inputs = {
590
+ value: this.getCellValue(row, field),
591
+ row,
592
+ field,
593
+ ...(field.renderer ? field.rendererInputs : undefined),
594
+ };
595
+ fieldCache.set(row, inputs);
596
+ }
597
+ return inputs;
598
+ }
599
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
600
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: NgxTable, isStandalone: true, selector: "ngx-table", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, totalRecords: { classPropertyName: "totalRecords", publicName: "totalRecords", isSignal: true, isRequired: true, transformFunction: null }, showRecordNumber: { classPropertyName: "showRecordNumber", publicName: "showRecordNumber", isSignal: true, isRequired: false, transformFunction: null }, operations: { classPropertyName: "operations", publicName: "operations", isSignal: true, isRequired: false, transformFunction: null }, operationWidth: { classPropertyName: "operationWidth", publicName: "operationWidth", isSignal: true, isRequired: false, transformFunction: null }, numWidth: { classPropertyName: "numWidth", publicName: "numWidth", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, multiSort: { classPropertyName: "multiSort", publicName: "multiSort", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { lazyLoad: "lazyLoad", sortChange: "sortChange", columnResize: "columnResize" }, host: { classAttribute: "ngx-table-host" }, queries: [{ propertyName: "cellTemplates", predicate: TableCell }], ngImport: i0, template: "<div class=\"ngx-table-wrapper\" [class.ngx-table--loading]=\"loading()\">\n <div class=\"ngx-table-scroll\">\n <div\n class=\"ngx-table\"\n [class.ngx-table--striped]=\"config.stripedRows\"\n [class.ngx-table--hover]=\"config.hoverable\"\n role=\"table\">\n <div class=\"thead-mrs\" [class.thead-mrs--sticky]=\"config.stickyHeader\" role=\"rowgroup\">\n <div class=\"tr-mrs\" role=\"row\">\n @if (operations()) {\n <div\n class=\"th-mrs operations\"\n role=\"columnheader\"\n [style.flex]=\"'0 0 ' + operationWidth() + 'px'\">\n {{ config.labels.operation }}\n </div>\n }\n @if (showRecordNumber()) {\n <div\n class=\"th-mrs operations\"\n role=\"columnheader\"\n [style.flex]=\"'0 0 ' + numWidth() + 'px'\">\n #\n </div>\n }\n\n @for (f of fields(); track f.column) {\n <div\n class=\"th-mrs\"\n role=\"columnheader\"\n [class.th-mrs--sortable]=\"f.sortable\"\n [style.flex]=\"'0 0 ' + columnWidth(f.column) + 'px'\"\n [attr.aria-sort]=\"\n sortDirectionFor(f.column) === 'asc'\n ? 'ascending'\n : sortDirectionFor(f.column) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(f, $event)\">\n <span class=\"th-mrs__title\">{{ f.title }}</span>\n\n @if (f.sortable) {\n <span class=\"sort-icon\" [class.active]=\"sortDirectionFor(f.column) !== null\">\n @if (sortDirectionFor(f.column) === 'asc') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\">\n <path\n fill=\"currentColor\"\n d=\"M182.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8H96V448c0 17.7 14.3 32 32 32s32-14.3 32-32V224h64c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-128-128z\" />\n </svg>\n } @else if (sortDirectionFor(f.column) === 'desc') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\">\n <path\n fill=\"currentColor\"\n d=\"M137.4 470.6c12.5 12.5 32.8 12.5 45.3 0l128-128c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8H224V64c0-17.7-14.3-32-32-32s-32 14.3-32 32V288H96c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9l128 128z\" />\n </svg>\n } @else {\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\">\n <path\n fill=\"currentColor\"\n d=\"M137.4 41.4c12.5-12.5 32.8-12.5 45.3 0l96 96c9.2 9.2 11.9 22.9 6.9 34.9s-16.6 19.8-29.6 19.8H224v96c0 17.7-14.3 32-32 32s-32-14.3-32-32V192H96c-12.9 0-24.6-7.8-29.6-19.8s-2.3-25.7 6.9-34.9l96-96zM32 400c0-17.7 14.3-32 32-32H256c17.7 0 32 14.3 32 32s-14.3 32-32 32H64c-17.7 0-32-14.3-32-32z\"\n opacity=\".35\" />\n </svg>\n }\n </span>\n }\n\n @if (isResizable(f)) {\n <span\n ngxColumnResize\n [ngxColumnResizeWidth]=\"columnWidth(f.column)\"\n [ngxColumnResizeMin]=\"columnMin(f)\"\n [ngxColumnResizeMax]=\"columnMax(f)\"\n [ngxColumnResizeDir]=\"resolvedDirection()\"\n (resizing)=\"onColumnResizing(f.column, $event)\"\n (resizeEnd)=\"onColumnResizeEnd(f.column, $event)\"\n (click)=\"$event.stopPropagation()\"></span>\n }\n </div>\n }\n </div>\n </div>\n\n <div class=\"tbody-mrs\" role=\"rowgroup\">\n @if (!loading() && pagedData().length === 0) {\n <div class=\"no-data\">{{ config.labels.noData }}</div>\n }\n\n @for (row of pagedData(); track trackBy()($index, row); let rowIndex = $index) {\n <div class=\"tr-mrs\" role=\"row\">\n @if (operations()) {\n <div\n class=\"td-mrs operations\"\n role=\"cell\"\n [style.flex]=\"'0 0 ' + operationWidth() + 'px'\">\n <div class=\"cel\">\n <span class=\"cell-title\">{{ config.labels.operation }}:</span>\n <ng-container\n [ngTemplateOutlet]=\"operations()!\"\n [ngTemplateOutletContext]=\"{ $implicit: row }\"></ng-container>\n </div>\n </div>\n }\n\n @if (showRecordNumber()) {\n <div class=\"td-mrs operations\" role=\"cell\" [style.flex]=\"'0 0 ' + numWidth() + 'px'\">\n <div class=\"cel\">{{ recordNumber(rowIndex) }}</div>\n </div>\n }\n\n @for (f of fields(); track f.column) {\n @let template = getTemplate(f.column);\n @let renderer = getRenderer(f);\n\n <div class=\"td-mrs\" role=\"cell\" [style.flex]=\"'0 0 ' + columnWidth(f.column) + 'px'\">\n <div class=\"cel\" [class.wrap]=\"f.wrap\">\n <span class=\"cell-title\">{{ f.title }}</span>\n\n @if (template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n row: row,\n value: getCellValue(row, f),\n rowIndex: rowIndex,\n field: f,\n }\"></ng-container>\n } @else if (renderer) {\n <ng-container\n *ngComponentOutlet=\"\n renderer;\n inputs: getRendererInputs(row, f)\n \"></ng-container>\n } @else {\n {{ formatCellValue(row, f) }}\n }\n\n @if (f.prefix) {\n <span>{{ f.prefix }}</span>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (loading()) {\n <div class=\"loading-container\">\n <div class=\"loading\">{{ config.labels.loading }}</div>\n </div>\n }\n </div>\n</div>\n\n<ngx-pagination\n [page]=\"page()\"\n [pageSize]=\"pageSizeS()\"\n [total]=\"totalCount()\"\n (paginate)=\"onPaginate($event)\"></ngx-pagination>\n", styles: [":host{--ngx-table-accent: light-dark(#4f46e5, #818cf8);--ngx-table-border: light-dark(#e5e7eb, #27272a);--ngx-table-header-bg: light-dark(#f8fafc, #18181b);--ngx-table-header-fg: light-dark(#334155, #d4d4d8);--ngx-table-row-bg: light-dark(#ffffff, #111114);--ngx-table-row-alt-bg: light-dark(#f9fafb, #17171b);--ngx-table-row-hover-bg: light-dark(#eef2ff, #1e1b4b);--ngx-table-text: light-dark(#1e293b, #e4e4e7);--ngx-table-radius: 12px;display:block;color:var(--ngx-table-text)}.ngx-table-wrapper{position:relative;border:1px solid var(--ngx-table-border);border-radius:var(--ngx-table-radius);overflow:hidden;background:var(--ngx-table-row-bg)}.ngx-table-scroll{overflow:auto;max-height:75vh}.ngx-table-scroll::-webkit-scrollbar{height:10px;width:10px}.ngx-table-scroll::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--ngx-table-border) 60%,gray);border-radius:8px}.ngx-table{display:flex;flex-direction:column;min-width:100%;width:max-content}.thead-mrs{display:flex;flex-direction:column;z-index:2}.thead-mrs--sticky{position:sticky;top:0}.tr-mrs{display:flex;align-items:stretch}.th-mrs,.td-mrs{position:relative;box-sizing:border-box;overflow:hidden;border-bottom:1px solid var(--ngx-table-border);border-inline-end:1px solid var(--ngx-table-border)}.th-mrs:last-child,.td-mrs:last-child{border-inline-end:none}.th-mrs{display:flex;align-items:center;gap:.375rem;background:var(--ngx-table-header-bg);color:var(--ngx-table-header-fg);font-weight:600;font-size:.8125rem;letter-spacing:.02em;padding:.75rem 1rem;-webkit-user-select:none;user-select:none;white-space:nowrap;text-overflow:ellipsis}.th-mrs--sortable{cursor:pointer}.th-mrs--sortable:hover{color:var(--ngx-table-accent)}.th-mrs.operations{justify-content:center}.th-mrs__title{overflow:hidden;text-overflow:ellipsis}.sort-icon{width:.75rem;height:.75rem;flex:none;opacity:.4;display:inline-flex}.sort-icon svg{width:100%;height:100%}.sort-icon.active{opacity:1}.ngx-table__resize-handle,[ngxColumnResize]{position:absolute;top:0;bottom:0;inset-inline-end:-3px;width:6px;cursor:col-resize;touch-action:none;z-index:3}.ngx-table__resize-handle:hover,.ngx-table__resize-handle:active,[ngxColumnResize]:hover,[ngxColumnResize]:active{background:var(--ngx-table-accent);opacity:.5}.td-mrs{display:flex;align-items:center;padding:.75rem 1rem;font-size:.875rem}.td-mrs.operations{justify-content:center}.cel{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:flex;align-items:center;gap:.25rem}.cel.wrap{white-space:normal}.cell-title{display:none;font-weight:600;color:var(--ngx-table-header-fg)}.ngx-table--striped .tbody-mrs .tr-mrs:nth-child(2n){background:var(--ngx-table-row-alt-bg)}.ngx-table--hover .tbody-mrs .tr-mrs:hover{background:var(--ngx-table-row-hover-bg)}.tr-mrs{transition:background-color .15s ease}.no-data{padding:2.5rem 1rem;text-align:center;color:#94a3b8;font-size:.875rem}.loading-container{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--ngx-table-row-bg) 75%,transparent);-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}.loading{font-size:.875rem;color:var(--ngx-table-accent)}@media(max-width:768px){.ngx-table-scroll{overflow:visible;max-height:none}.ngx-table{width:100%}.thead-mrs{display:none}.tbody-mrs{display:flex;flex-direction:column;gap:.75rem;padding:.75rem}.tr-mrs{flex-direction:column;border:1px solid var(--ngx-table-border);border-radius:10px;overflow:hidden}.td-mrs{flex:1 1 auto!important;width:100%;border-inline-end:none;justify-content:space-between}.cel{justify-content:flex-end;white-space:normal;text-align:end}.cell-title{display:inline}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NgxPagination, selector: "ngx-pagination", inputs: ["total", "page", "pageSize", "pageSizeOptions", "labels", "siblingCount", "boundaryCount"], outputs: ["pageChange", "pageSizeChange", "paginate"] }, { kind: "directive", type: ColumnResize, selector: "[ngxColumnResize]", inputs: ["ngxColumnResizeWidth", "ngxColumnResizeMin", "ngxColumnResizeMax", "ngxColumnResizeDir"], outputs: ["resizing", "resizeEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
601
+ }
602
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxTable, decorators: [{
603
+ type: Component,
604
+ args: [{ selector: 'ngx-table', standalone: true, imports: [CommonModule, NgxPagination, ColumnResize], changeDetection: ChangeDetectionStrategy.OnPush, host: {
605
+ class: 'ngx-table-host',
606
+ }, template: "<div class=\"ngx-table-wrapper\" [class.ngx-table--loading]=\"loading()\">\n <div class=\"ngx-table-scroll\">\n <div\n class=\"ngx-table\"\n [class.ngx-table--striped]=\"config.stripedRows\"\n [class.ngx-table--hover]=\"config.hoverable\"\n role=\"table\">\n <div class=\"thead-mrs\" [class.thead-mrs--sticky]=\"config.stickyHeader\" role=\"rowgroup\">\n <div class=\"tr-mrs\" role=\"row\">\n @if (operations()) {\n <div\n class=\"th-mrs operations\"\n role=\"columnheader\"\n [style.flex]=\"'0 0 ' + operationWidth() + 'px'\">\n {{ config.labels.operation }}\n </div>\n }\n @if (showRecordNumber()) {\n <div\n class=\"th-mrs operations\"\n role=\"columnheader\"\n [style.flex]=\"'0 0 ' + numWidth() + 'px'\">\n #\n </div>\n }\n\n @for (f of fields(); track f.column) {\n <div\n class=\"th-mrs\"\n role=\"columnheader\"\n [class.th-mrs--sortable]=\"f.sortable\"\n [style.flex]=\"'0 0 ' + columnWidth(f.column) + 'px'\"\n [attr.aria-sort]=\"\n sortDirectionFor(f.column) === 'asc'\n ? 'ascending'\n : sortDirectionFor(f.column) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(f, $event)\">\n <span class=\"th-mrs__title\">{{ f.title }}</span>\n\n @if (f.sortable) {\n <span class=\"sort-icon\" [class.active]=\"sortDirectionFor(f.column) !== null\">\n @if (sortDirectionFor(f.column) === 'asc') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\">\n <path\n fill=\"currentColor\"\n d=\"M182.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8H96V448c0 17.7 14.3 32 32 32s32-14.3 32-32V224h64c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-128-128z\" />\n </svg>\n } @else if (sortDirectionFor(f.column) === 'desc') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\">\n <path\n fill=\"currentColor\"\n d=\"M137.4 470.6c12.5 12.5 32.8 12.5 45.3 0l128-128c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8H224V64c0-17.7-14.3-32-32-32s-32 14.3-32 32V288H96c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9l128 128z\" />\n </svg>\n } @else {\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\">\n <path\n fill=\"currentColor\"\n d=\"M137.4 41.4c12.5-12.5 32.8-12.5 45.3 0l96 96c9.2 9.2 11.9 22.9 6.9 34.9s-16.6 19.8-29.6 19.8H224v96c0 17.7-14.3 32-32 32s-32-14.3-32-32V192H96c-12.9 0-24.6-7.8-29.6-19.8s-2.3-25.7 6.9-34.9l96-96zM32 400c0-17.7 14.3-32 32-32H256c17.7 0 32 14.3 32 32s-14.3 32-32 32H64c-17.7 0-32-14.3-32-32z\"\n opacity=\".35\" />\n </svg>\n }\n </span>\n }\n\n @if (isResizable(f)) {\n <span\n ngxColumnResize\n [ngxColumnResizeWidth]=\"columnWidth(f.column)\"\n [ngxColumnResizeMin]=\"columnMin(f)\"\n [ngxColumnResizeMax]=\"columnMax(f)\"\n [ngxColumnResizeDir]=\"resolvedDirection()\"\n (resizing)=\"onColumnResizing(f.column, $event)\"\n (resizeEnd)=\"onColumnResizeEnd(f.column, $event)\"\n (click)=\"$event.stopPropagation()\"></span>\n }\n </div>\n }\n </div>\n </div>\n\n <div class=\"tbody-mrs\" role=\"rowgroup\">\n @if (!loading() && pagedData().length === 0) {\n <div class=\"no-data\">{{ config.labels.noData }}</div>\n }\n\n @for (row of pagedData(); track trackBy()($index, row); let rowIndex = $index) {\n <div class=\"tr-mrs\" role=\"row\">\n @if (operations()) {\n <div\n class=\"td-mrs operations\"\n role=\"cell\"\n [style.flex]=\"'0 0 ' + operationWidth() + 'px'\">\n <div class=\"cel\">\n <span class=\"cell-title\">{{ config.labels.operation }}:</span>\n <ng-container\n [ngTemplateOutlet]=\"operations()!\"\n [ngTemplateOutletContext]=\"{ $implicit: row }\"></ng-container>\n </div>\n </div>\n }\n\n @if (showRecordNumber()) {\n <div class=\"td-mrs operations\" role=\"cell\" [style.flex]=\"'0 0 ' + numWidth() + 'px'\">\n <div class=\"cel\">{{ recordNumber(rowIndex) }}</div>\n </div>\n }\n\n @for (f of fields(); track f.column) {\n @let template = getTemplate(f.column);\n @let renderer = getRenderer(f);\n\n <div class=\"td-mrs\" role=\"cell\" [style.flex]=\"'0 0 ' + columnWidth(f.column) + 'px'\">\n <div class=\"cel\" [class.wrap]=\"f.wrap\">\n <span class=\"cell-title\">{{ f.title }}</span>\n\n @if (template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n row: row,\n value: getCellValue(row, f),\n rowIndex: rowIndex,\n field: f,\n }\"></ng-container>\n } @else if (renderer) {\n <ng-container\n *ngComponentOutlet=\"\n renderer;\n inputs: getRendererInputs(row, f)\n \"></ng-container>\n } @else {\n {{ formatCellValue(row, f) }}\n }\n\n @if (f.prefix) {\n <span>{{ f.prefix }}</span>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (loading()) {\n <div class=\"loading-container\">\n <div class=\"loading\">{{ config.labels.loading }}</div>\n </div>\n }\n </div>\n</div>\n\n<ngx-pagination\n [page]=\"page()\"\n [pageSize]=\"pageSizeS()\"\n [total]=\"totalCount()\"\n (paginate)=\"onPaginate($event)\"></ngx-pagination>\n", styles: [":host{--ngx-table-accent: light-dark(#4f46e5, #818cf8);--ngx-table-border: light-dark(#e5e7eb, #27272a);--ngx-table-header-bg: light-dark(#f8fafc, #18181b);--ngx-table-header-fg: light-dark(#334155, #d4d4d8);--ngx-table-row-bg: light-dark(#ffffff, #111114);--ngx-table-row-alt-bg: light-dark(#f9fafb, #17171b);--ngx-table-row-hover-bg: light-dark(#eef2ff, #1e1b4b);--ngx-table-text: light-dark(#1e293b, #e4e4e7);--ngx-table-radius: 12px;display:block;color:var(--ngx-table-text)}.ngx-table-wrapper{position:relative;border:1px solid var(--ngx-table-border);border-radius:var(--ngx-table-radius);overflow:hidden;background:var(--ngx-table-row-bg)}.ngx-table-scroll{overflow:auto;max-height:75vh}.ngx-table-scroll::-webkit-scrollbar{height:10px;width:10px}.ngx-table-scroll::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--ngx-table-border) 60%,gray);border-radius:8px}.ngx-table{display:flex;flex-direction:column;min-width:100%;width:max-content}.thead-mrs{display:flex;flex-direction:column;z-index:2}.thead-mrs--sticky{position:sticky;top:0}.tr-mrs{display:flex;align-items:stretch}.th-mrs,.td-mrs{position:relative;box-sizing:border-box;overflow:hidden;border-bottom:1px solid var(--ngx-table-border);border-inline-end:1px solid var(--ngx-table-border)}.th-mrs:last-child,.td-mrs:last-child{border-inline-end:none}.th-mrs{display:flex;align-items:center;gap:.375rem;background:var(--ngx-table-header-bg);color:var(--ngx-table-header-fg);font-weight:600;font-size:.8125rem;letter-spacing:.02em;padding:.75rem 1rem;-webkit-user-select:none;user-select:none;white-space:nowrap;text-overflow:ellipsis}.th-mrs--sortable{cursor:pointer}.th-mrs--sortable:hover{color:var(--ngx-table-accent)}.th-mrs.operations{justify-content:center}.th-mrs__title{overflow:hidden;text-overflow:ellipsis}.sort-icon{width:.75rem;height:.75rem;flex:none;opacity:.4;display:inline-flex}.sort-icon svg{width:100%;height:100%}.sort-icon.active{opacity:1}.ngx-table__resize-handle,[ngxColumnResize]{position:absolute;top:0;bottom:0;inset-inline-end:-3px;width:6px;cursor:col-resize;touch-action:none;z-index:3}.ngx-table__resize-handle:hover,.ngx-table__resize-handle:active,[ngxColumnResize]:hover,[ngxColumnResize]:active{background:var(--ngx-table-accent);opacity:.5}.td-mrs{display:flex;align-items:center;padding:.75rem 1rem;font-size:.875rem}.td-mrs.operations{justify-content:center}.cel{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:flex;align-items:center;gap:.25rem}.cel.wrap{white-space:normal}.cell-title{display:none;font-weight:600;color:var(--ngx-table-header-fg)}.ngx-table--striped .tbody-mrs .tr-mrs:nth-child(2n){background:var(--ngx-table-row-alt-bg)}.ngx-table--hover .tbody-mrs .tr-mrs:hover{background:var(--ngx-table-row-hover-bg)}.tr-mrs{transition:background-color .15s ease}.no-data{padding:2.5rem 1rem;text-align:center;color:#94a3b8;font-size:.875rem}.loading-container{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--ngx-table-row-bg) 75%,transparent);-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}.loading{font-size:.875rem;color:var(--ngx-table-accent)}@media(max-width:768px){.ngx-table-scroll{overflow:visible;max-height:none}.ngx-table{width:100%}.thead-mrs{display:none}.tbody-mrs{display:flex;flex-direction:column;gap:.75rem;padding:.75rem}.tr-mrs{flex-direction:column;border:1px solid var(--ngx-table-border);border-radius:10px;overflow:hidden}.td-mrs{flex:1 1 auto!important;width:100%;border-inline-end:none;justify-content:space-between}.cel{justify-content:flex-end;white-space:normal;text-align:end}.cell-title{display:inline}}\n"] }]
607
+ }], ctorParameters: () => [], propDecorators: { fields: [{ type: i0.Input, args: [{ isSignal: true, alias: "fields", required: true }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], totalRecords: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalRecords", required: true }] }], showRecordNumber: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRecordNumber", required: false }] }], operations: [{ type: i0.Input, args: [{ isSignal: true, alias: "operations", required: false }] }], operationWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "operationWidth", required: false }] }], numWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "numWidth", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], multiSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiSort", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], trackBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackBy", required: false }] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], columnResize: [{ type: i0.Output, args: ["columnResize"] }], cellTemplates: [{
608
+ type: ContentChildren,
609
+ args: [TableCell]
610
+ }] } });
611
+
612
+ // types
613
+
614
+ /**
615
+ * Generated bundle index. Do not edit.
616
+ */
617
+
618
+ export { ColumnResize, NGX_PAGINATION_CONFIG, NGX_PAGINATION_CONFIG_DEFAULT, NGX_TABLE_CONFIG, NGX_TABLE_CONFIG_DEFAULT, NgxPagination, NgxTable, TableCell, defineFields, defineRenderers, providePagination, provideTable };
619
+ //# sourceMappingURL=ngx-kit-data-table.mjs.map