ngx-kit 8.3.9 → 8.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, makeEnvironmentProviders, input, inject, TemplateRef, Directive, output, ElementRef, HostListener, model, computed, effect, ChangeDetectionStrategy, Component, signal, DestroyRef, ContentChildren } from '@angular/core';
2
+ import { InjectionToken, makeEnvironmentProviders, input, inject, TemplateRef, Directive, output, ElementRef, HostListener, model, computed, effect, ChangeDetectionStrategy, Component, Injectable, signal, DestroyRef, ContentChildren } from '@angular/core';
3
3
  import * as i1$1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@@ -33,6 +33,25 @@ function defineFields(_renderers, fields) {
33
33
  return fields;
34
34
  }
35
35
 
36
+ var DynamicDataFilterOperator;
37
+ (function (DynamicDataFilterOperator) {
38
+ DynamicDataFilterOperator[DynamicDataFilterOperator["Equal"] = 0] = "Equal";
39
+ DynamicDataFilterOperator[DynamicDataFilterOperator["NotEqual"] = 1] = "NotEqual";
40
+ DynamicDataFilterOperator[DynamicDataFilterOperator["Contains"] = 2] = "Contains";
41
+ DynamicDataFilterOperator[DynamicDataFilterOperator["StartsWith"] = 3] = "StartsWith";
42
+ DynamicDataFilterOperator[DynamicDataFilterOperator["EndsWith"] = 4] = "EndsWith";
43
+ DynamicDataFilterOperator[DynamicDataFilterOperator["GreaterThan"] = 5] = "GreaterThan";
44
+ DynamicDataFilterOperator[DynamicDataFilterOperator["GreaterThanOrEqual"] = 6] = "GreaterThanOrEqual";
45
+ DynamicDataFilterOperator[DynamicDataFilterOperator["LessThan"] = 7] = "LessThan";
46
+ DynamicDataFilterOperator[DynamicDataFilterOperator["LessThanOrEqual"] = 8] = "LessThanOrEqual";
47
+ DynamicDataFilterOperator[DynamicDataFilterOperator["Between"] = 9] = "Between";
48
+ DynamicDataFilterOperator[DynamicDataFilterOperator["In"] = 10] = "In";
49
+ DynamicDataFilterOperator[DynamicDataFilterOperator["IsNull"] = 11] = "IsNull";
50
+ DynamicDataFilterOperator[DynamicDataFilterOperator["IsNotNull"] = 12] = "IsNotNull";
51
+ DynamicDataFilterOperator[DynamicDataFilterOperator["IsEmpty"] = 13] = "IsEmpty";
52
+ DynamicDataFilterOperator[DynamicDataFilterOperator["IsNotEmpty"] = 14] = "IsNotEmpty";
53
+ })(DynamicDataFilterOperator || (DynamicDataFilterOperator = {}));
54
+
36
55
  const NGX_PAGINATION_CONFIG_DEFAULT = {
37
56
  defaultPageSize: 10,
38
57
  pageSizeOptions: [5, 10, 20, 30, 50, 100],
@@ -54,6 +73,31 @@ class TableLocalization {
54
73
  noData = 'There is no data to display!';
55
74
  loading = 'Loading...';
56
75
  operation = 'Operation';
76
+ // Filters
77
+ filter = 'Filter';
78
+ clearFilter = 'Clear';
79
+ applyFilter = 'Apply';
80
+ search = 'Search...';
81
+ selectAll = 'Select All';
82
+ from = 'From';
83
+ to = 'To';
84
+ trueText = 'Yes';
85
+ falseText = 'No';
86
+ // Operators
87
+ opEqual = 'Equals';
88
+ opNotEqual = 'Does not equal';
89
+ opContains = 'Contains';
90
+ opStartsWith = 'Starts with';
91
+ opEndsWith = 'Ends with';
92
+ opGreaterThan = 'Greater than';
93
+ opGreaterThanOrEqual = 'Greater than or equal';
94
+ opLessThan = 'Less than';
95
+ opLessThanOrEqual = 'Less than or equal';
96
+ opBetween = 'Between';
97
+ opIsNull = 'Is null';
98
+ opIsNotNull = 'Is not null';
99
+ opIsEmpty = 'Is empty';
100
+ opIsNotEmpty = 'Is not empty';
57
101
  }
58
102
 
59
103
  const NGX_TABLE_CONFIG = new InjectionToken('NGX_TABLE_CONFIG', {
@@ -326,24 +370,373 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
326
370
  args: [{ selector: 'ngx-pagination', standalone: true, imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav class=\"ngx-pagination\">\n <div class=\"ngx-pagination__summary\">\n {{ lables.showing }}\n <span class=\"fw-semibold\">{{ firstItem() }}</span>\n {{ lables.to }}\n <span class=\"fw-semibold\">{{ lastItem() }}</span>\n {{ lables.of }}\n <span class=\"fw-semibold\">{{ total() }}</span>\n {{ lables.results }}\n </div>\n\n <div class=\"ngx-pagination__controls\">\n <ul class=\"ngx-pagination__list\">\n <li [class.disabled]=\"page() === 1\" [title]=\"lables.first\">\n <a (click)=\"first()\" class=\"icon\">\u00AB</a>\n </li>\n <li [class.disabled]=\"page() === 1\" [title]=\"lables.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]=\"lables.next\">\n <a (click)=\"next()\" class=\"icon\">\u203A</a>\n </li>\n <li [class.disabled]=\"page() === totalPages()\" [title]=\"lables.last\">\n <a (click)=\"last()\" class=\"icon\">\u00BB</a>\n </li>\n </ul>\n\n <div class=\"ngx-pagination__page-size\">\n <span>{{ lables.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"] }]
327
371
  }], 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 }] }], 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"] }] } });
328
372
 
329
- /**
330
- * جدول داده‌ی جنریک و Type-Safe.
331
- *
332
- * `T` از روی چیزی که به [data] بایند می‌کنید استنتاج می‌شود؛ `field.column` در
333
- * `TableField<T, R>` محدود به `keyof T` است، و اگر از defineFields(renderers, ...)
334
- * استفاده کنید، `field.renderer` محدود به کلیدهای رجیستری و `rendererInputs`
335
- * دقیقاً همان ورودی‌های اضافه‌ی همان renderer است.
336
- *
337
- * دو حالت:
338
- * - کلاینت‌ساید (پیش‌فرض): [data] کل آرایه است؛ سورت/صفحه‌بندی داخلی و روی
339
- * یک computed خالص انجام می‌شود (بدون mutation روی آبجکت‌های شما).
340
- * - lazy: [lazy]="true" + [totalRecords]؛ [data] فقط صفحه‌ی جاری است؛ به
341
- * (lazyLoad) گوش بدهید.
342
- */
373
+ class ColumnFilterService {
374
+ applyFilters(data, filters) {
375
+ const activeFilters = Object.values(filters).filter((f) => this.isFilterActive(f));
376
+ if (activeFilters.length === 0)
377
+ return [...data];
378
+ return data.filter((item) => activeFilters.every((filter) => this.evaluateFilter(item, filter)));
379
+ }
380
+ isFilterActive(filter) {
381
+ if (!filter)
382
+ return false;
383
+ const { operator, value, valueTo, values } = filter;
384
+ const noValueOps = [
385
+ DynamicDataFilterOperator.IsNull,
386
+ DynamicDataFilterOperator.IsNotNull,
387
+ DynamicDataFilterOperator.IsEmpty,
388
+ DynamicDataFilterOperator.IsNotEmpty,
389
+ ];
390
+ if (noValueOps.includes(operator))
391
+ return true;
392
+ if (operator === DynamicDataFilterOperator.Between) {
393
+ return (value !== undefined &&
394
+ value !== null &&
395
+ value !== '' &&
396
+ valueTo !== undefined &&
397
+ valueTo !== null &&
398
+ valueTo !== '');
399
+ }
400
+ if (operator === DynamicDataFilterOperator.In) {
401
+ return Array.isArray(values) && values.length > 0;
402
+ }
403
+ return value !== undefined && value !== null && value !== '';
404
+ }
405
+ evaluateFilter(item, filter) {
406
+ const rawVal = item?.[filter.field];
407
+ const op = filter.operator;
408
+ const target = filter.value;
409
+ const targetTo = filter.valueTo;
410
+ const targets = filter.values;
411
+ if (op === DynamicDataFilterOperator.IsNull)
412
+ return rawVal === null || rawVal === undefined;
413
+ if (op === DynamicDataFilterOperator.IsNotNull)
414
+ return rawVal !== null && rawVal !== undefined;
415
+ if (op === DynamicDataFilterOperator.IsEmpty)
416
+ return rawVal === '' || rawVal === null || rawVal === undefined;
417
+ if (op === DynamicDataFilterOperator.IsNotEmpty)
418
+ return rawVal !== '' && rawVal !== null && rawVal !== undefined;
419
+ if (rawVal === null || rawVal === undefined)
420
+ return false;
421
+ if (rawVal instanceof Date ||
422
+ (typeof rawVal === 'string' && !isNaN(Date.parse(rawVal)) && isNaN(Number(rawVal)))) {
423
+ const itemTime = new Date(rawVal).getTime();
424
+ const targetTime = target ? new Date(target).getTime() : NaN;
425
+ const targetToTime = targetTo ? new Date(targetTo).getTime() : NaN;
426
+ if (!isNaN(itemTime)) {
427
+ switch (op) {
428
+ case DynamicDataFilterOperator.Equal:
429
+ return new Date(rawVal).toDateString() === new Date(target).toDateString();
430
+ case DynamicDataFilterOperator.GreaterThan:
431
+ return itemTime > targetTime;
432
+ case DynamicDataFilterOperator.GreaterThanOrEqual:
433
+ return itemTime >= targetTime;
434
+ case DynamicDataFilterOperator.LessThan:
435
+ return itemTime < targetTime;
436
+ case DynamicDataFilterOperator.LessThanOrEqual:
437
+ return itemTime <= targetTime;
438
+ case DynamicDataFilterOperator.Between:
439
+ return itemTime >= targetTime && itemTime <= targetToTime;
440
+ }
441
+ }
442
+ }
443
+ switch (op) {
444
+ case DynamicDataFilterOperator.Equal:
445
+ return String(rawVal).trim().toLowerCase() === String(target).trim().toLowerCase();
446
+ case DynamicDataFilterOperator.NotEqual:
447
+ return String(rawVal).trim().toLowerCase() !== String(target).trim().toLowerCase();
448
+ case DynamicDataFilterOperator.Contains:
449
+ return String(rawVal).toLowerCase().includes(String(target).toLowerCase());
450
+ case DynamicDataFilterOperator.StartsWith:
451
+ return String(rawVal).toLowerCase().startsWith(String(target).toLowerCase());
452
+ case DynamicDataFilterOperator.EndsWith:
453
+ return String(rawVal).toLowerCase().endsWith(String(target).toLowerCase());
454
+ case DynamicDataFilterOperator.GreaterThan:
455
+ return Number(rawVal) > Number(target);
456
+ case DynamicDataFilterOperator.GreaterThanOrEqual:
457
+ return Number(rawVal) >= Number(target);
458
+ case DynamicDataFilterOperator.LessThan:
459
+ return Number(rawVal) < Number(target);
460
+ case DynamicDataFilterOperator.LessThanOrEqual:
461
+ return Number(rawVal) <= Number(target);
462
+ case DynamicDataFilterOperator.Between:
463
+ return Number(rawVal) >= Number(target) && Number(rawVal) <= Number(targetTo);
464
+ case DynamicDataFilterOperator.In:
465
+ return Array.isArray(targets)
466
+ ? targets.some((t) => String(t).toLowerCase() === String(rawVal).toLowerCase())
467
+ : false;
468
+ default:
469
+ return true;
470
+ }
471
+ }
472
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ColumnFilterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
473
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ColumnFilterService, providedIn: 'root' });
474
+ }
475
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ColumnFilterService, decorators: [{
476
+ type: Injectable,
477
+ args: [{ providedIn: 'root' }]
478
+ }] });
479
+
480
+ class NgxColumnFilter {
481
+ labels = inject(NGX_TABLE_I18N);
482
+ elementRef = inject(ElementRef);
483
+ tableEl = input.required(/* @ts-ignore */
484
+ ...(ngDevMode ? [{ debugName: "tableEl" }] : /* istanbul ignore next */ []));
485
+ field = input.required(/* @ts-ignore */
486
+ ...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
487
+ config = input(/* @ts-ignore */
488
+ ...(ngDevMode ? [undefined, { debugName: "config" }] : /* istanbul ignore next */ []));
489
+ currentFilter = input(null, /* @ts-ignore */
490
+ ...(ngDevMode ? [{ debugName: "currentFilter" }] : /* istanbul ignore next */ []));
491
+ distinctValues = input([], /* @ts-ignore */
492
+ ...(ngDevMode ? [{ debugName: "distinctValues" }] : /* istanbul ignore next */ []));
493
+ applyFilter = output();
494
+ clearFilter = output();
495
+ isOpen = signal(false, /* @ts-ignore */
496
+ ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
497
+ filterType = computed(() => this.config()?.type ?? 'text', /* @ts-ignore */
498
+ ...(ngDevMode ? [{ debugName: "filterType" }] : /* istanbul ignore next */ []));
499
+ selectedOperator = signal(DynamicDataFilterOperator.Contains, /* @ts-ignore */
500
+ ...(ngDevMode ? [{ debugName: "selectedOperator" }] : /* istanbul ignore next */ []));
501
+ filterValue = signal('', /* @ts-ignore */
502
+ ...(ngDevMode ? [{ debugName: "filterValue" }] : /* istanbul ignore next */ []));
503
+ filterValueTo = signal('', /* @ts-ignore */
504
+ ...(ngDevMode ? [{ debugName: "filterValueTo" }] : /* istanbul ignore next */ []));
505
+ selectedEnumValues = signal(new Set(), /* @ts-ignore */
506
+ ...(ngDevMode ? [{ debugName: "selectedEnumValues" }] : /* istanbul ignore next */ []));
507
+ enumSearchQuery = signal('', /* @ts-ignore */
508
+ ...(ngDevMode ? [{ debugName: "enumSearchQuery" }] : /* istanbul ignore next */ []));
509
+ operators = computed(() => {
510
+ const type = this.filterType();
511
+ const l = this.labels;
512
+ if (type === 'number') {
513
+ return [
514
+ { label: l.opEqual, value: DynamicDataFilterOperator.Equal },
515
+ { label: l.opNotEqual, value: DynamicDataFilterOperator.NotEqual },
516
+ { label: l.opGreaterThan, value: DynamicDataFilterOperator.GreaterThan },
517
+ { label: l.opGreaterThanOrEqual, value: DynamicDataFilterOperator.GreaterThanOrEqual },
518
+ { label: l.opLessThan, value: DynamicDataFilterOperator.LessThan },
519
+ { label: l.opLessThanOrEqual, value: DynamicDataFilterOperator.LessThanOrEqual },
520
+ { label: l.opBetween, value: DynamicDataFilterOperator.Between },
521
+ { label: l.opIsNull, value: DynamicDataFilterOperator.IsNull },
522
+ { label: l.opIsNotNull, value: DynamicDataFilterOperator.IsNotNull },
523
+ ];
524
+ }
525
+ if (type === 'date') {
526
+ return [
527
+ { label: l.opEqual, value: DynamicDataFilterOperator.Equal },
528
+ { label: l.opGreaterThan, value: DynamicDataFilterOperator.GreaterThan },
529
+ { label: l.opLessThan, value: DynamicDataFilterOperator.LessThan },
530
+ { label: l.opBetween, value: DynamicDataFilterOperator.Between },
531
+ { label: l.opIsNull, value: DynamicDataFilterOperator.IsNull },
532
+ { label: l.opIsNotNull, value: DynamicDataFilterOperator.IsNotNull },
533
+ ];
534
+ }
535
+ if (type === 'boolean') {
536
+ return [
537
+ { label: l.opEqual, value: DynamicDataFilterOperator.Equal },
538
+ { label: l.opIsNull, value: DynamicDataFilterOperator.IsNull },
539
+ { label: l.opIsNotNull, value: DynamicDataFilterOperator.IsNotNull },
540
+ ];
541
+ }
542
+ // Default: text
543
+ return [
544
+ { label: l.opContains, value: DynamicDataFilterOperator.Contains },
545
+ { label: l.opStartsWith, value: DynamicDataFilterOperator.StartsWith },
546
+ { label: l.opEndsWith, value: DynamicDataFilterOperator.EndsWith },
547
+ { label: l.opEqual, value: DynamicDataFilterOperator.Equal },
548
+ { label: l.opNotEqual, value: DynamicDataFilterOperator.NotEqual },
549
+ { label: l.opIsEmpty, value: DynamicDataFilterOperator.IsEmpty },
550
+ { label: l.opIsNotEmpty, value: DynamicDataFilterOperator.IsNotEmpty },
551
+ { label: l.opIsNull, value: DynamicDataFilterOperator.IsNull },
552
+ { label: l.opIsNotNull, value: DynamicDataFilterOperator.IsNotNull },
553
+ ];
554
+ }, /* @ts-ignore */
555
+ ...(ngDevMode ? [{ debugName: "operators" }] : /* istanbul ignore next */ []));
556
+ enumOptions = computed(() => {
557
+ const cfg = this.config();
558
+ if (cfg?.options && cfg.options.length > 0)
559
+ return cfg.options;
560
+ const values = this.distinctValues();
561
+ return values
562
+ .filter((v) => v !== null && v !== undefined && v !== '')
563
+ .map((v) => ({ label: String(v), value: v }));
564
+ }, /* @ts-ignore */
565
+ ...(ngDevMode ? [{ debugName: "enumOptions" }] : /* istanbul ignore next */ []));
566
+ filteredEnumOptions = computed(() => {
567
+ const q = this.enumSearchQuery().trim().toLowerCase();
568
+ const opts = this.enumOptions();
569
+ if (!q)
570
+ return opts;
571
+ return opts.filter((o) => o.label.toLowerCase().includes(q));
572
+ }, /* @ts-ignore */
573
+ ...(ngDevMode ? [{ debugName: "filteredEnumOptions" }] : /* istanbul ignore next */ []));
574
+ isNoValueOperator = computed(() => {
575
+ const op = this.selectedOperator();
576
+ return [
577
+ DynamicDataFilterOperator.IsNull,
578
+ DynamicDataFilterOperator.IsNotNull,
579
+ DynamicDataFilterOperator.IsEmpty,
580
+ DynamicDataFilterOperator.IsNotEmpty,
581
+ ].includes(op);
582
+ }, /* @ts-ignore */
583
+ ...(ngDevMode ? [{ debugName: "isNoValueOperator" }] : /* istanbul ignore next */ []));
584
+ toggleDropdown(event) {
585
+ event.stopPropagation();
586
+ const nextState = !this.isOpen();
587
+ if (nextState) {
588
+ this.syncStateFromInput();
589
+ this.isOpen.set(true);
590
+ this.updatePosition();
591
+ }
592
+ else {
593
+ this.isOpen.set(false);
594
+ }
595
+ }
596
+ updatePosition() {
597
+ if (!this.isOpen())
598
+ return;
599
+ setTimeout(() => {
600
+ const popover = this.elementRef.nativeElement.querySelector('.filter-popover');
601
+ if (!popover)
602
+ return;
603
+ // پیدا کردن کانتینر اسکرول جدول
604
+ const containerEl = this.tableEl()?.nativeElement ??
605
+ this.elementRef.nativeElement.closest('.ngx-table-scroll') ??
606
+ this.elementRef.nativeElement.closest('.ngx-table-wrapper');
607
+ if (!containerEl) {
608
+ popover.style.visibility = 'visible';
609
+ return;
610
+ }
611
+ // ۱. ریست کردن ترنسفورم قبلی تا موقعیت واقعی بدون تغییر سنجیده شود
612
+ popover.style.transform = 'none';
613
+ const rect = popover.getBoundingClientRect();
614
+ const container = containerEl.getBoundingClientRect();
615
+ const margin = 6; // فاصله ایمن از لبه جدول
616
+ let x = 0;
617
+ const threshold = 10;
618
+ // اگر از لبه سمت راست کانتینر بیرون زده باشد (حالت ستون‌های آخر):
619
+ if (rect.right > container.right - margin) {
620
+ // شیفت به سمت چپ (مقدار منفی)
621
+ x = container.right - margin - rect.right - threshold;
622
+ }
623
+ // اگر از لبه سمت چپ بیرون زده باشد:
624
+ else if (rect.left < container.left + margin) {
625
+ // شیفت به سمت راست (مقدار مثبت)
626
+ x = container.left + margin - rect.left + threshold;
627
+ }
628
+ // اطمینان از اینکه شیفت به چپ، باعث خروج از سمت چپ کانتینر نشود
629
+ if (rect.left + x < container.left + margin) {
630
+ x = container.left + margin - rect.left;
631
+ }
632
+ // ۲. اعمال جابجایی
633
+ if (x !== 0) {
634
+ popover.style.transform = `translateX(${Math.round(x)}px)`;
635
+ }
636
+ else {
637
+ popover.style.transform = 'none';
638
+ }
639
+ // ۳. نمایش پاپ‌آپ پس از تنظیم موقعیت بدون پرش تصویر (Flicker)
640
+ popover.style.visibility = 'visible';
641
+ }, 0);
642
+ }
643
+ syncStateFromInput() {
644
+ const cur = this.currentFilter();
645
+ const type = this.filterType();
646
+ if (cur) {
647
+ this.selectedOperator.set(cur.operator);
648
+ if (type === 'enum' && Array.isArray(cur.values)) {
649
+ this.selectedEnumValues.set(new Set(cur.values));
650
+ }
651
+ else {
652
+ this.filterValue.set(cur.value ?? '');
653
+ this.filterValueTo.set(cur.valueTo ?? '');
654
+ }
655
+ }
656
+ else {
657
+ const defaultOp = this.config()?.defaultOperator ??
658
+ (type === 'number' || type === 'date' || type === 'boolean'
659
+ ? DynamicDataFilterOperator.Equal
660
+ : DynamicDataFilterOperator.Contains);
661
+ this.selectedOperator.set(defaultOp);
662
+ this.filterValue.set('');
663
+ this.filterValueTo.set('');
664
+ if (type === 'enum') {
665
+ this.selectedEnumValues.set(new Set(this.enumOptions().map((o) => o.value)));
666
+ }
667
+ }
668
+ }
669
+ onApply() {
670
+ const type = this.filterType();
671
+ let value = this.filterValue();
672
+ let values = [];
673
+ let operator = this.selectedOperator();
674
+ if (type === 'enum') {
675
+ operator = DynamicDataFilterOperator.In;
676
+ values = Array.from(this.selectedEnumValues());
677
+ }
678
+ this.applyFilter.emit({
679
+ field: this.field(),
680
+ operator,
681
+ value,
682
+ valueTo: this.selectedOperator() === DynamicDataFilterOperator.Between
683
+ ? this.filterValueTo()
684
+ : undefined,
685
+ values,
686
+ });
687
+ this.isOpen.set(false);
688
+ }
689
+ onClear() {
690
+ this.filterValue.set('');
691
+ this.filterValueTo.set('');
692
+ this.selectedEnumValues.set(new Set());
693
+ this.clearFilter.emit(this.field());
694
+ this.isOpen.set(false);
695
+ }
696
+ toggleEnumSelection(val) {
697
+ const current = new Set(this.selectedEnumValues());
698
+ if (current.has(val))
699
+ current.delete(val);
700
+ else
701
+ current.add(val);
702
+ this.selectedEnumValues.set(current);
703
+ }
704
+ toggleSelectAll(checked) {
705
+ if (checked) {
706
+ this.selectedEnumValues.set(new Set(this.enumOptions().map((o) => o.value)));
707
+ }
708
+ else {
709
+ this.selectedEnumValues.set(new Set());
710
+ }
711
+ }
712
+ onDocumentClick(event) {
713
+ if (this.isOpen() && !this.elementRef.nativeElement.contains(event.target)) {
714
+ this.isOpen.set(false);
715
+ }
716
+ }
717
+ onEscape() {
718
+ if (this.isOpen())
719
+ this.isOpen.set(false);
720
+ }
721
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxColumnFilter, deps: [], target: i0.ɵɵFactoryTarget.Component });
722
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: NgxColumnFilter, isStandalone: true, selector: "ngx-column-filter", inputs: { tableEl: { classPropertyName: "tableEl", publicName: "tableEl", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, currentFilter: { classPropertyName: "currentFilter", publicName: "currentFilter", isSignal: true, isRequired: false, transformFunction: null }, distinctValues: { classPropertyName: "distinctValues", publicName: "distinctValues", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { applyFilter: "applyFilter", clearFilter: "clearFilter" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "<div class=\"ngx-col-filter\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"filter-trigger\"\n [class.active]=\"!!currentFilter()\"\n [title]=\"labels.filter\"\n (click)=\"toggleDropdown($event)\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\" class=\"funnel-icon\">\n <path\n fill=\"currentColor\"\n d=\"M3.9 54.9C10.5 36.5 28 24 47.6 24H464.4c19.6 0 37.1 12.5 43.7 30.9c6.6 18.4 0 38.8-16.1 49.9L320 223.5V432c0 13.5-8.4 25.5-21.1 30.2s-27 1.1-35.8-9.1l-64-75.1c-4.7-5.5-7.1-12.6-7.1-19.8V223.5L4.1 104.8C-1.9 93.7-2.6 73.3 3.9 54.9z\" />\n </svg>\n </button>\n\n @if (isOpen()) {\n <div class=\"filter-popover\" role=\"dialog\">\n @if (filterType() !== 'enum') {\n <div class=\"filter-section\">\n <label class=\"filter-label\">{{ labels.filter }}:</label>\n <select\n class=\"filter-select\"\n [ngModel]=\"selectedOperator()\"\n (ngModelChange)=\"selectedOperator.set($event)\">\n @for (op of operators(); track op.value) {\n <option [ngValue]=\"op.value\">{{ op.label }}</option>\n }\n </select>\n </div>\n\n @if (!isNoValueOperator()) {\n <div class=\"filter-section\">\n @if (filterType() === 'number') {\n <input\n type=\"number\"\n class=\"filter-input\"\n [placeholder]=\"labels.search\"\n [ngModel]=\"filterValue()\"\n (ngModelChange)=\"filterValue.set($event)\" />\n @if (selectedOperator() === 9) {\n <input\n type=\"number\"\n class=\"filter-input mt-1\"\n [placeholder]=\"labels.to\"\n [ngModel]=\"filterValueTo()\"\n (ngModelChange)=\"filterValueTo.set($event)\" />\n }\n } @else if (filterType() === 'date') {\n <input\n type=\"date\"\n class=\"filter-input\"\n [ngModel]=\"filterValue()\"\n (ngModelChange)=\"filterValue.set($event)\" />\n @if (selectedOperator() === 9) {\n <input\n type=\"date\"\n class=\"filter-input mt-1\"\n [ngModel]=\"filterValueTo()\"\n (ngModelChange)=\"filterValueTo.set($event)\" />\n }\n } @else if (filterType() === 'boolean') {\n <select\n class=\"filter-select\"\n [ngModel]=\"filterValue()\"\n (ngModelChange)=\"filterValue.set($event)\">\n <option [ngValue]=\"true\">{{ labels.trueText }}</option>\n <option [ngValue]=\"false\">{{ labels.falseText }}</option>\n </select>\n } @else {\n <input\n type=\"text\"\n class=\"filter-input\"\n [placeholder]=\"labels.search\"\n [ngModel]=\"filterValue()\"\n (ngModelChange)=\"filterValue.set($event)\" />\n }\n </div>\n }\n } @else {\n <!-- Excel-like Set Filter (Enum) -->\n <div class=\"filter-section\">\n <input\n type=\"text\"\n class=\"filter-input\"\n [placeholder]=\"labels.search\"\n [ngModel]=\"enumSearchQuery()\"\n (ngModelChange)=\"enumSearchQuery.set($event)\" />\n </div>\n <div class=\"filter-set-list\">\n <label class=\"filter-set-item font-bold\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedEnumValues().size === enumOptions().length\"\n (change)=\"toggleSelectAll($any($event.target).checked)\" />\n <span>({{ labels.selectAll }})</span>\n </label>\n @for (opt of filteredEnumOptions(); track opt.value) {\n <label class=\"filter-set-item\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedEnumValues().has(opt.value)\"\n (change)=\"toggleEnumSelection(opt.value)\" />\n <span>{{ opt.label }}</span>\n </label>\n }\n </div>\n }\n\n <div class=\"filter-actions\">\n <button type=\"button\" class=\"btn-clear\" (click)=\"onClear()\">\n {{ labels.clearFilter }}\n </button>\n <button type=\"button\" class=\"btn-apply\" (click)=\"onApply()\">\n {{ labels.applyFilter }}\n </button>\n </div>\n </div>\n }\n</div>\n", styles: [".ngx-col-filter{position:relative;display:inline-flex;align-items:center}.filter-trigger{background:none;border:none;cursor:pointer;padding:2px 4px;border-radius:4px;display:inline-flex;align-items:center;justify-content:center;color:inherit;opacity:.35;transition:opacity .15s ease,color .15s ease}.filter-trigger:hover{opacity:.8}.filter-trigger.active{opacity:1;color:var(--ngx-table-accent, #4f46e5)}.filter-trigger .funnel-icon{width:.8rem;height:.8rem}.filter-popover{position:absolute;top:calc(100% + 6px);inset-inline-start:-10px;z-index:100;width:220px;background:var(--ngx-table-row-bg, #ffffff);border:1px solid var(--ngx-table-border, #e5e7eb);box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;border-radius:8px;padding:.75rem;display:flex;flex-direction:column;gap:.625rem;font-size:.8125rem;color:var(--ngx-table-text, #1e293b);cursor:default;box-sizing:border-box;visibility:hidden}.filter-section{display:flex;flex-direction:column;gap:.25rem}.filter-label{font-weight:500;font-size:.75rem;color:#64748b}.filter-select,.filter-input{width:100%;box-sizing:border-box;padding:.375rem .5rem;border-radius:6px;border:1px solid var(--ngx-table-border, #d1d5db);background:var(--ngx-table-row-bg, #ffffff);color:inherit;font-size:.8125rem}.filter-select:focus,.filter-input:focus{outline:none;border-color:var(--ngx-table-accent, #4f46e5)}.mt-1{margin-top:.375rem}.filter-set-list{max-height:140px;overflow-y:auto;display:flex;flex-direction:column;gap:.25rem;padding:.25rem;border:1px solid var(--ngx-table-border, #e5e7eb);border-radius:6px}.filter-set-item{display:flex;align-items:center;gap:.375rem;cursor:pointer;-webkit-user-select:none;user-select:none;font-size:.75rem}.filter-set-item.font-bold{font-weight:600}.filter-actions{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.25rem;border-top:1px solid var(--ngx-table-border, #e5e7eb)}.filter-actions button{padding:.3rem .6rem;border-radius:4px;font-size:.75rem;cursor:pointer;border:none;font-weight:500}.filter-actions .btn-clear{background:transparent;color:#64748b}.filter-actions .btn-clear:hover{background:#f1f5f9}.filter-actions .btn-apply{background:var(--ngx-table-accent, #4f46e5);color:#fff}.filter-actions .btn-apply:hover{filter:brightness(.95)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number]:not([ngNoCva])[formControlName],input[type=number]:not([ngNoCva])[formControl],input[type=number]:not([ngNoCva])[ngModel]" }, { 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 });
723
+ }
724
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxColumnFilter, decorators: [{
725
+ type: Component,
726
+ args: [{ selector: 'ngx-column-filter', standalone: true, imports: [CommonModule, FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ngx-col-filter\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"filter-trigger\"\n [class.active]=\"!!currentFilter()\"\n [title]=\"labels.filter\"\n (click)=\"toggleDropdown($event)\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\" class=\"funnel-icon\">\n <path\n fill=\"currentColor\"\n d=\"M3.9 54.9C10.5 36.5 28 24 47.6 24H464.4c19.6 0 37.1 12.5 43.7 30.9c6.6 18.4 0 38.8-16.1 49.9L320 223.5V432c0 13.5-8.4 25.5-21.1 30.2s-27 1.1-35.8-9.1l-64-75.1c-4.7-5.5-7.1-12.6-7.1-19.8V223.5L4.1 104.8C-1.9 93.7-2.6 73.3 3.9 54.9z\" />\n </svg>\n </button>\n\n @if (isOpen()) {\n <div class=\"filter-popover\" role=\"dialog\">\n @if (filterType() !== 'enum') {\n <div class=\"filter-section\">\n <label class=\"filter-label\">{{ labels.filter }}:</label>\n <select\n class=\"filter-select\"\n [ngModel]=\"selectedOperator()\"\n (ngModelChange)=\"selectedOperator.set($event)\">\n @for (op of operators(); track op.value) {\n <option [ngValue]=\"op.value\">{{ op.label }}</option>\n }\n </select>\n </div>\n\n @if (!isNoValueOperator()) {\n <div class=\"filter-section\">\n @if (filterType() === 'number') {\n <input\n type=\"number\"\n class=\"filter-input\"\n [placeholder]=\"labels.search\"\n [ngModel]=\"filterValue()\"\n (ngModelChange)=\"filterValue.set($event)\" />\n @if (selectedOperator() === 9) {\n <input\n type=\"number\"\n class=\"filter-input mt-1\"\n [placeholder]=\"labels.to\"\n [ngModel]=\"filterValueTo()\"\n (ngModelChange)=\"filterValueTo.set($event)\" />\n }\n } @else if (filterType() === 'date') {\n <input\n type=\"date\"\n class=\"filter-input\"\n [ngModel]=\"filterValue()\"\n (ngModelChange)=\"filterValue.set($event)\" />\n @if (selectedOperator() === 9) {\n <input\n type=\"date\"\n class=\"filter-input mt-1\"\n [ngModel]=\"filterValueTo()\"\n (ngModelChange)=\"filterValueTo.set($event)\" />\n }\n } @else if (filterType() === 'boolean') {\n <select\n class=\"filter-select\"\n [ngModel]=\"filterValue()\"\n (ngModelChange)=\"filterValue.set($event)\">\n <option [ngValue]=\"true\">{{ labels.trueText }}</option>\n <option [ngValue]=\"false\">{{ labels.falseText }}</option>\n </select>\n } @else {\n <input\n type=\"text\"\n class=\"filter-input\"\n [placeholder]=\"labels.search\"\n [ngModel]=\"filterValue()\"\n (ngModelChange)=\"filterValue.set($event)\" />\n }\n </div>\n }\n } @else {\n <!-- Excel-like Set Filter (Enum) -->\n <div class=\"filter-section\">\n <input\n type=\"text\"\n class=\"filter-input\"\n [placeholder]=\"labels.search\"\n [ngModel]=\"enumSearchQuery()\"\n (ngModelChange)=\"enumSearchQuery.set($event)\" />\n </div>\n <div class=\"filter-set-list\">\n <label class=\"filter-set-item font-bold\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedEnumValues().size === enumOptions().length\"\n (change)=\"toggleSelectAll($any($event.target).checked)\" />\n <span>({{ labels.selectAll }})</span>\n </label>\n @for (opt of filteredEnumOptions(); track opt.value) {\n <label class=\"filter-set-item\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedEnumValues().has(opt.value)\"\n (change)=\"toggleEnumSelection(opt.value)\" />\n <span>{{ opt.label }}</span>\n </label>\n }\n </div>\n }\n\n <div class=\"filter-actions\">\n <button type=\"button\" class=\"btn-clear\" (click)=\"onClear()\">\n {{ labels.clearFilter }}\n </button>\n <button type=\"button\" class=\"btn-apply\" (click)=\"onApply()\">\n {{ labels.applyFilter }}\n </button>\n </div>\n </div>\n }\n</div>\n", styles: [".ngx-col-filter{position:relative;display:inline-flex;align-items:center}.filter-trigger{background:none;border:none;cursor:pointer;padding:2px 4px;border-radius:4px;display:inline-flex;align-items:center;justify-content:center;color:inherit;opacity:.35;transition:opacity .15s ease,color .15s ease}.filter-trigger:hover{opacity:.8}.filter-trigger.active{opacity:1;color:var(--ngx-table-accent, #4f46e5)}.filter-trigger .funnel-icon{width:.8rem;height:.8rem}.filter-popover{position:absolute;top:calc(100% + 6px);inset-inline-start:-10px;z-index:100;width:220px;background:var(--ngx-table-row-bg, #ffffff);border:1px solid var(--ngx-table-border, #e5e7eb);box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;border-radius:8px;padding:.75rem;display:flex;flex-direction:column;gap:.625rem;font-size:.8125rem;color:var(--ngx-table-text, #1e293b);cursor:default;box-sizing:border-box;visibility:hidden}.filter-section{display:flex;flex-direction:column;gap:.25rem}.filter-label{font-weight:500;font-size:.75rem;color:#64748b}.filter-select,.filter-input{width:100%;box-sizing:border-box;padding:.375rem .5rem;border-radius:6px;border:1px solid var(--ngx-table-border, #d1d5db);background:var(--ngx-table-row-bg, #ffffff);color:inherit;font-size:.8125rem}.filter-select:focus,.filter-input:focus{outline:none;border-color:var(--ngx-table-accent, #4f46e5)}.mt-1{margin-top:.375rem}.filter-set-list{max-height:140px;overflow-y:auto;display:flex;flex-direction:column;gap:.25rem;padding:.25rem;border:1px solid var(--ngx-table-border, #e5e7eb);border-radius:6px}.filter-set-item{display:flex;align-items:center;gap:.375rem;cursor:pointer;-webkit-user-select:none;user-select:none;font-size:.75rem}.filter-set-item.font-bold{font-weight:600}.filter-actions{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.25rem;border-top:1px solid var(--ngx-table-border, #e5e7eb)}.filter-actions button{padding:.3rem .6rem;border-radius:4px;font-size:.75rem;cursor:pointer;border:none;font-weight:500}.filter-actions .btn-clear{background:transparent;color:#64748b}.filter-actions .btn-clear:hover{background:#f1f5f9}.filter-actions .btn-apply{background:var(--ngx-table-accent, #4f46e5);color:#fff}.filter-actions .btn-apply:hover{filter:brightness(.95)}\n"] }]
727
+ }], propDecorators: { tableEl: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableEl", required: true }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], currentFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentFilter", required: false }] }], distinctValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "distinctValues", required: false }] }], applyFilter: [{ type: i0.Output, args: ["applyFilter"] }], clearFilter: [{ type: i0.Output, args: ["clearFilter"] }], onDocumentClick: [{
728
+ type: HostListener,
729
+ args: ['document:click', ['$event']]
730
+ }], onEscape: [{
731
+ type: HostListener,
732
+ args: ['document:keydown.escape']
733
+ }] } });
734
+
343
735
  class NgxTable {
344
736
  config = inject(NGX_TABLE_CONFIG);
345
737
  labels = inject(NGX_TABLE_I18N);
346
738
  paginationConfig = inject(NGX_PAGINATION_CONFIG);
739
+ columnFilterService = inject(ColumnFilterService);
347
740
  // ---------------------------------------------------------------- inputs
348
741
  fields = input.required(/* @ts-ignore */
349
742
  ...(ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []));
@@ -374,15 +767,15 @@ class NgxTable {
374
767
  ...(ngDevMode ? [{ debugName: "resizable" }] : /* istanbul ignore next */ []));
375
768
  height = input('75dvh', /* @ts-ignore */
376
769
  ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
377
- /** پیش‌فرض بر اساس ایندکس؛ برای جلوگیری از گم‌شدن state سطرها هنگام سورت،
378
- * یک شناسه‌ی یکتا بدهید: [trackBy]="(i, row) => row.id" */
379
770
  trackBy = input((index) => index, /* @ts-ignore */
380
771
  ...(ngDevMode ? [{ debugName: "trackBy" }] : /* istanbul ignore next */ []));
381
772
  // --------------------------------------------------------------- outputs
382
773
  lazyLoad = output();
383
774
  sortChange = output();
384
775
  columnResize = output();
776
+ filterChange = output();
385
777
  // ------------------------------------------------------------ int. state
778
+ elementRef = inject(ElementRef);
386
779
  pageIndex = signal(1, /* @ts-ignore */
387
780
  ...(ngDevMode ? [{ debugName: "pageIndex" }] : /* istanbul ignore next */ []));
388
781
  pageSizeS = signal(this.paginationConfig.defaultPageSize, /* @ts-ignore */
@@ -391,6 +784,8 @@ class NgxTable {
391
784
  ...(ngDevMode ? [{ debugName: "sorts" }] : /* istanbul ignore next */ []));
392
785
  widths = signal({}, /* @ts-ignore */
393
786
  ...(ngDevMode ? [{ debugName: "widths" }] : /* istanbul ignore next */ []));
787
+ activeFilters = signal({}, /* @ts-ignore */
788
+ ...(ngDevMode ? [{ debugName: "activeFilters" }] : /* istanbul ignore next */ []));
394
789
  manuallyResized = new Set();
395
790
  rendererInputsCache = new Map();
396
791
  cellTemplates;
@@ -403,18 +798,9 @@ class NgxTable {
403
798
  ...(ngDevMode ? [{ debugName: "resolvedDirection" }] : /* istanbul ignore next */ []));
404
799
  constructor() {
405
800
  effect(() => {
406
- const ps = this.pageSize();
407
- this.pageSizeS.set(ps);
408
- const pi = this.page();
409
- this.pageIndex.set(pi);
801
+ this.pageSizeS.set(this.pageSize());
802
+ this.pageIndex.set(this.page());
410
803
  });
411
- // برای هر ستونِ تازه‌دیده‌شده که width عددیِ صریح دارد، یک عرض seed کن؛
412
- // ستون‌هایی که کاربر دستی ریسایز کرده دیگر هیچ‌وقت دوباره نوشته نمی‌شوند.
413
- // مهم: ستون‌هایی که اصلاً width ندارند (یا width رشته‌ای مثل '20%' دارند)
414
- // عمداً اینجا seed نمی‌شوند — قبلاً همه‌ی ستون‌ها با یک defaultWidth ثابت
415
- // seed می‌شدند که باعث می‌شد جدول با flex:0 0 روی هر ستون هیچ‌وقت واقعاً
416
- // 100% عرض رو پر نکنه، حتی وقتی کاربر عمداً یک ستون رو بدون width گذاشته
417
- // بود تا فضای باقی‌مونده رو خودش پر کنه.
418
804
  effect(() => {
419
805
  const cols = this.fields();
420
806
  const current = this.widths();
@@ -434,17 +820,9 @@ class NgxTable {
434
820
  this.widths.set(next);
435
821
  });
436
822
  }
437
- ngOnInit() {
438
- // نیاز به این نیست در لود خود صفحه بندی paginate را صدا میزند
439
- // if (this.lazy()) {
440
- // this.lazyLoad.emit({ first: 0, pageIndex: 1, pageSize: this.pageSizeS(), sorts: [] });
441
- // }
442
- }
823
+ ngOnInit() { }
443
824
  ngAfterContentInit() {
444
825
  this.syncTemplateMap();
445
- // این Observable خودش کامل نمی‌شه (EventEmitter ساده‌ست، نه یه stream که
446
- // روی destroy کامپوننت تموم بشه)؛ بدون takeUntilDestroyed، این subscribe
447
- // برای همیشه فعال می‌موند و رفرنس کامپوننت رو زنده نگه می‌داشت.
448
826
  this.cellTemplates.changes
449
827
  .pipe(takeUntilDestroyed(this.destroyRef))
450
828
  .subscribe(() => this.syncTemplateMap());
@@ -452,18 +830,25 @@ class NgxTable {
452
830
  syncTemplateMap() {
453
831
  this.templateMap.clear();
454
832
  this.cellTemplates.forEach((cell) => {
455
- // نکته‌ی مهم: cell.column() باید فراخوانی شود (سیگنال است)، نه cell.column.name.
456
833
  this.templateMap.set(cell.column(), cell.templateRef);
457
834
  });
458
835
  }
459
- // ------------------------------------------------------------- داده‌ها
836
+ // ------------------------------------------------------------- داده‌ها (فیلتر ➔ سورت ➔ صفحه‌بندی)
837
+ filteredData = computed(() => {
838
+ const raw = this.data();
839
+ if (this.lazy())
840
+ return raw;
841
+ return this.columnFilterService.applyFilters(raw, this.activeFilters());
842
+ }, /* @ts-ignore */
843
+ ...(ngDevMode ? [{ debugName: "filteredData" }] : /* istanbul ignore next */ []));
460
844
  sortedData = computed(() => {
845
+ const source = this.filteredData();
461
846
  if (this.lazy())
462
- return this.data();
847
+ return source;
463
848
  const sorts = this.sorts();
464
849
  if (sorts.length === 0)
465
- return this.data();
466
- const copy = [...this.data()];
850
+ return source;
851
+ const copy = [...source];
467
852
  copy.sort((a, b) => {
468
853
  for (const s of sorts) {
469
854
  const cmp = this.compare(a[s.field], b[s.field]);
@@ -475,7 +860,7 @@ class NgxTable {
475
860
  return copy;
476
861
  }, /* @ts-ignore */
477
862
  ...(ngDevMode ? [{ debugName: "sortedData" }] : /* istanbul ignore next */ []));
478
- totalCount = computed(() => this.lazy() ? this.totalRecords() : this.data().length, /* @ts-ignore */
863
+ totalCount = computed(() => this.lazy() ? this.totalRecords() : this.filteredData().length, /* @ts-ignore */
479
864
  ...(ngDevMode ? [{ debugName: "totalCount" }] : /* istanbul ignore next */ []));
480
865
  pagedData = computed(() => {
481
866
  if (this.lazy())
@@ -499,7 +884,6 @@ class NgxTable {
499
884
  return a.getTime() - b.getTime();
500
885
  return String(a).localeCompare(String(b));
501
886
  }
502
- /** بدون mutation روی داده‌ی ورودی: شماره‌ی ردیف صرفاً از روی page/pageSize/rowIndex محاسبه می‌شود. */
503
887
  recordNumber(rowIndex) {
504
888
  return (this.pageIndex() - 1) * this.pageSizeS() + rowIndex + 1;
505
889
  }
@@ -542,6 +926,40 @@ class NgxTable {
542
926
  this.sortChange.emit(next);
543
927
  this.emitLazyIfNeeded();
544
928
  }
929
+ // ------------------------------------------------------------ فیلتر
930
+ isFilterable(field) {
931
+ return field.filterable === true || !!field.filterConfig?.enabled;
932
+ }
933
+ getFilterState(column) {
934
+ const f = this.activeFilters()[column];
935
+ return this.columnFilterService.isFilterActive(f) ? f : null;
936
+ }
937
+ getDistinctValues(column) {
938
+ const raw = this.data();
939
+ if (!raw || raw.length === 0)
940
+ return [];
941
+ return Array.from(new Set(raw.map((r) => r[column])));
942
+ }
943
+ onFilterApply(state) {
944
+ this.activeFilters.update((prev) => ({ ...prev, [state.field]: state }));
945
+ this.pageIndex.set(1);
946
+ this.pageChange.emit(1);
947
+ const activeList = Object.values(this.activeFilters()).filter((f) => this.columnFilterService.isFilterActive(f));
948
+ this.filterChange.emit(activeList);
949
+ this.emitLazyIfNeeded();
950
+ }
951
+ onFilterClear(field) {
952
+ this.activeFilters.update((prev) => {
953
+ const copy = { ...prev };
954
+ delete copy[field];
955
+ return copy;
956
+ });
957
+ this.pageIndex.set(1);
958
+ this.pageChange.emit(1);
959
+ const activeList = Object.values(this.activeFilters()).filter((f) => this.columnFilterService.isFilterActive(f));
960
+ this.filterChange.emit(activeList);
961
+ this.emitLazyIfNeeded();
962
+ }
545
963
  // ------------------------------------------------------------ صفحه‌بندی
546
964
  onPaginate(event) {
547
965
  this.pageIndex.set(event.page);
@@ -554,6 +972,7 @@ class NgxTable {
554
972
  return;
555
973
  const pageIndex = this.pageIndex();
556
974
  const pageSize = this.pageSizeS();
975
+ const activeFilterList = Object.values(this.activeFilters()).filter((f) => this.columnFilterService.isFilterActive(f));
557
976
  this.lazyLoad.emit({
558
977
  pageIndex,
559
978
  pageSize,
@@ -562,18 +981,13 @@ class NgxTable {
562
981
  sorting: this.sorts()
563
982
  ?.map((s) => `${s.field} ${s.direction}`)
564
983
  .join(','),
984
+ filters: activeFilterList,
565
985
  });
566
986
  }
567
987
  // -------------------------------------------------------------- ریسایز
568
988
  columnWidth(column) {
569
989
  return this.widths()[column] ?? this.config.column.defaultWidth;
570
990
  }
571
- /**
572
- * مقدار CSS `flex` واقعیِ این ستون:
573
- * - اگه دستی ریسایز شده یا width عددی داره → `0 0 <px>` (ثابت، بدون رشد/کوچک‌شدن)
574
- * - اگه width رشته‌ای داره (مثلاً '20%') → `0 0 <رشته>` (ثابت، هر واحد CSS معتبری)
575
- * - اگه اصلاً width نداره → `1 1 0` (رشد می‌کنه تا فضای باقی‌مونده‌ی جدول رو پر کنه)
576
- */
577
991
  columnFlex(field) {
578
992
  const resized = this.widths()[field.column];
579
993
  if (resized != null)
@@ -610,8 +1024,6 @@ class NgxTable {
610
1024
  getRenderer(field) {
611
1025
  if (!field.renderer)
612
1026
  return null;
613
- // نکته: نوع دقیق field.renderer (که به کلیدهای رجیستری محدود است) در
614
- // defineFields() چک می‌شود؛ اینجا فقط یک lookup رانتایم ساده لازم است.
615
1027
  return this.config.renderers[field.renderer] ?? null;
616
1028
  }
617
1029
  getCellValue(row, field) {
@@ -632,9 +1044,6 @@ class NgxTable {
632
1044
  stringify(value) {
633
1045
  return value == null ? '' : String(value);
634
1046
  }
635
- /** ورودی‌های renderer را به‌ازای هر (field, row) کش می‌کند تا هر Change
636
- * Detection یک آبجکت جدید ساخته نشود؛ با WeakMap، وقتی row از حافظه خارج
637
- * شود (مثلاً بعد از عوض‌شدن صفحه)، entry آن هم خودکار پاک می‌شود. */
638
1047
  getRendererInputs(row, field) {
639
1048
  let fieldCache = this.rendererInputsCache.get(field);
640
1049
  if (!fieldCache) {
@@ -654,15 +1063,15 @@ class NgxTable {
654
1063
  return inputs;
655
1064
  }
656
1065
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
657
- 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 }, page: { classPropertyName: "page", publicName: "page", 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 }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange", lazyLoad: "lazyLoad", sortChange: "sortChange", columnResize: "columnResize" }, host: { properties: { "style.--ngx-table-scroll-height": "height()" }, 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 {{ 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 !== false\"\n [style.flex]=\"columnFlex(f)\"\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 !== false) {\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\">{{ 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\">{{ 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]=\"columnFlex(f)\">\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\">{{ 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: ["@charset \"UTF-8\";:host{--ngx-table-accent: light-dark(#4f46e5, #818cf8);--ngx-table-border: light-dark(#e5e7eb, #27272a);--ngx-table-header-bg: light-dark(#f8fafc, #1a1a26);--ngx-table-header-fg: light-dark(#334155, #d4d4d8);--ngx-table-row-bg: light-dark(#ffffff, #111118);--ngx-table-row-alt-bg: light-dark(#f9fafb, #17171b);--ngx-table-row-hover-bg: light-dark(#eef2ff, #7dc4fb27);--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:var(--ngx-table-scroll-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;background:var(--ngx-table-header-bg);color:var(--ngx-table-header-fg)}.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;min-inline-size:0;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;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{-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.loading-container .loading{border:5px rgba(88,84,84,.82) solid;border-radius:10px;padding:18px 25px;background:#3b3b3bab;color:#fff}.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", "siblingCount", "boundaryCount"], outputs: ["pageChange", "pageSizeChange", "paginate"] }, { kind: "directive", type: ColumnResize, selector: "[ngxColumnResize]", inputs: ["ngxColumnResizeWidth", "ngxColumnResizeMin", "ngxColumnResizeMax", "ngxColumnResizeDir"], outputs: ["resizing", "resizeEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1066
+ 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 }, page: { classPropertyName: "page", publicName: "page", 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 }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange", lazyLoad: "lazyLoad", sortChange: "sortChange", columnResize: "columnResize", filterChange: "filterChange" }, host: { properties: { "style.--ngx-table-scroll-height": "height()" }, 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 {{ 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 !== false\"\n [style.flex]=\"columnFlex(f)\"\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 !== false) {\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 (isFilterable(f)) {\n <ngx-column-filter\n [tableEl]=\"elementRef\"\n [field]=\"f.column\"\n [config]=\"f.filterConfig\"\n [currentFilter]=\"getFilterState(f.column)\"\n [distinctValues]=\"getDistinctValues(f.column)\"\n (applyFilter)=\"onFilterApply($event)\"\n (clearFilter)=\"onFilterClear($event)\"></ngx-column-filter>\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\">{{ 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\">{{ 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]=\"columnFlex(f)\">\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\">{{ labels.loading }}</div>\n </div>\n }\n </div>\n</div>\n\n<ngx-pagination\n [page]=\"pageIndex()\"\n [pageSize]=\"pageSizeS()\"\n [total]=\"totalCount()\"\n (paginate)=\"onPaginate($event)\"></ngx-pagination>\n", styles: ["@charset \"UTF-8\";:host{--ngx-table-accent: light-dark(#4f46e5, #818cf8);--ngx-table-border: light-dark(#e5e7eb, #27272a);--ngx-table-header-bg: light-dark(#f8fafc, #1a1a26);--ngx-table-header-fg: light-dark(#334155, #d4d4d8);--ngx-table-row-bg: light-dark(#ffffff, #111118);--ngx-table-row-alt-bg: light-dark(#f9fafb, #17171b);--ngx-table-row-hover-bg: light-dark(#eef2ff, #7dc4fb27);--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;height:var(--ngx-table-scroll-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;background:var(--ngx-table-header-bg);color:var(--ngx-table-header-fg)}.thead-mrs--sticky{position:sticky;top:0}.tr-mrs{display:flex;align-items:stretch}.th-mrs,.td-mrs{position:relative;box-sizing:border-box;min-inline-size:0;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;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{-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.loading-container .loading{border:5px rgba(88,84,84,.82) solid;border-radius:10px;padding:18px 25px;background:#3b3b3bab;color:#fff}.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", "siblingCount", "boundaryCount"], outputs: ["pageChange", "pageSizeChange", "paginate"] }, { kind: "directive", type: ColumnResize, selector: "[ngxColumnResize]", inputs: ["ngxColumnResizeWidth", "ngxColumnResizeMin", "ngxColumnResizeMax", "ngxColumnResizeDir"], outputs: ["resizing", "resizeEnd"] }, { kind: "component", type: NgxColumnFilter, selector: "ngx-column-filter", inputs: ["tableEl", "field", "config", "currentFilter", "distinctValues"], outputs: ["applyFilter", "clearFilter"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
658
1067
  }
659
1068
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxTable, decorators: [{
660
1069
  type: Component,
661
- args: [{ selector: 'ngx-table', standalone: true, imports: [CommonModule, NgxPagination, ColumnResize], changeDetection: ChangeDetectionStrategy.OnPush, host: {
1070
+ args: [{ selector: 'ngx-table', standalone: true, imports: [CommonModule, NgxPagination, ColumnResize, NgxColumnFilter], changeDetection: ChangeDetectionStrategy.OnPush, host: {
662
1071
  class: 'ngx-table-host',
663
1072
  '[style.--ngx-table-scroll-height]': 'height()',
664
- }, 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 {{ 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 !== false\"\n [style.flex]=\"columnFlex(f)\"\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 !== false) {\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\">{{ 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\">{{ 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]=\"columnFlex(f)\">\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\">{{ 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: ["@charset \"UTF-8\";:host{--ngx-table-accent: light-dark(#4f46e5, #818cf8);--ngx-table-border: light-dark(#e5e7eb, #27272a);--ngx-table-header-bg: light-dark(#f8fafc, #1a1a26);--ngx-table-header-fg: light-dark(#334155, #d4d4d8);--ngx-table-row-bg: light-dark(#ffffff, #111118);--ngx-table-row-alt-bg: light-dark(#f9fafb, #17171b);--ngx-table-row-hover-bg: light-dark(#eef2ff, #7dc4fb27);--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:var(--ngx-table-scroll-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;background:var(--ngx-table-header-bg);color:var(--ngx-table-header-fg)}.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;min-inline-size:0;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;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{-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.loading-container .loading{border:5px rgba(88,84,84,.82) solid;border-radius:10px;padding:18px 25px;background:#3b3b3bab;color:#fff}.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"] }]
665
- }], 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 }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], 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 }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", 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: [{
1073
+ }, 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 {{ 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 !== false\"\n [style.flex]=\"columnFlex(f)\"\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 !== false) {\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 (isFilterable(f)) {\n <ngx-column-filter\n [tableEl]=\"elementRef\"\n [field]=\"f.column\"\n [config]=\"f.filterConfig\"\n [currentFilter]=\"getFilterState(f.column)\"\n [distinctValues]=\"getDistinctValues(f.column)\"\n (applyFilter)=\"onFilterApply($event)\"\n (clearFilter)=\"onFilterClear($event)\"></ngx-column-filter>\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\">{{ 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\">{{ 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]=\"columnFlex(f)\">\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\">{{ labels.loading }}</div>\n </div>\n }\n </div>\n</div>\n\n<ngx-pagination\n [page]=\"pageIndex()\"\n [pageSize]=\"pageSizeS()\"\n [total]=\"totalCount()\"\n (paginate)=\"onPaginate($event)\"></ngx-pagination>\n", styles: ["@charset \"UTF-8\";:host{--ngx-table-accent: light-dark(#4f46e5, #818cf8);--ngx-table-border: light-dark(#e5e7eb, #27272a);--ngx-table-header-bg: light-dark(#f8fafc, #1a1a26);--ngx-table-header-fg: light-dark(#334155, #d4d4d8);--ngx-table-row-bg: light-dark(#ffffff, #111118);--ngx-table-row-alt-bg: light-dark(#f9fafb, #17171b);--ngx-table-row-hover-bg: light-dark(#eef2ff, #7dc4fb27);--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;height:var(--ngx-table-scroll-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;background:var(--ngx-table-header-bg);color:var(--ngx-table-header-fg)}.thead-mrs--sticky{position:sticky;top:0}.tr-mrs{display:flex;align-items:stretch}.th-mrs,.td-mrs{position:relative;box-sizing:border-box;min-inline-size:0;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;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{-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);position:absolute;inset:0;display:flex;align-items:center;justify-content:center}.loading-container .loading{border:5px rgba(88,84,84,.82) solid;border-radius:10px;padding:18px 25px;background:#3b3b3bab;color:#fff}.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"] }]
1074
+ }], 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 }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], 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 }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", 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"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], cellTemplates: [{
666
1075
  type: ContentChildren,
667
1076
  args: [TableCell]
668
1077
  }] } });
@@ -673,5 +1082,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
673
1082
  * Generated bundle index. Do not edit.
674
1083
  */
675
1084
 
676
- export { ColumnResize, NGX_PAGINATION_CONFIG, NGX_PAGINATION_I18N, NGX_TABLE_CONFIG, NGX_TABLE_I18N, NgxPagination, NgxTable, PaginationLocalization, TableCell, TableLocalization, defineFields, defineRenderers, providePagination, provideTable };
1085
+ export { ColumnFilterService, ColumnResize, DynamicDataFilterOperator, NGX_PAGINATION_CONFIG, NGX_PAGINATION_I18N, NGX_TABLE_CONFIG, NGX_TABLE_I18N, NgxColumnFilter, NgxPagination, NgxTable, PaginationLocalization, TableCell, TableLocalization, defineFields, defineRenderers, providePagination, provideTable };
677
1086
  //# sourceMappingURL=ngx-kit-data-table.mjs.map