@acorex/components 22.1.0-next.16 → 22.1.0-next.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/acorex-components-data-table.mjs +25 -12
- package/fesm2022/acorex-components-data-table.mjs.map +1 -1
- package/fesm2022/acorex-components-lookup.mjs +172 -159
- package/fesm2022/acorex-components-lookup.mjs.map +1 -1
- package/fesm2022/acorex-components-select-box.mjs +38 -11
- package/fesm2022/acorex-components-select-box.mjs.map +1 -1
- package/package.json +3 -3
- package/types/acorex-components-data-table.d.ts +1 -0
- package/types/acorex-components-select-box.d.ts +11 -0
|
@@ -575,32 +575,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
575
575
|
}], propDecorators: { dataSource: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataSource", required: true }] }], valueField: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueField", required: false }] }], textField: [{ type: i0.Input, args: [{ isSignal: true, alias: "textField", required: false }] }], disabledField: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledField", required: false }] }], selectedValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedValues", required: false }] }], nodeTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeTemplate", required: false }] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }] } });
|
|
576
576
|
|
|
577
577
|
/**
|
|
578
|
-
* Internal mini component for the `multi-column` lookup mode:
|
|
579
|
-
*
|
|
580
|
-
*
|
|
578
|
+
* Internal mini component for the `multi-column-tree` lookup mode:
|
|
579
|
+
* a multi-column `ax-data-table` with a drop-down tree inside it.
|
|
580
|
+
* Rows expand/collapse via the data-table hierarchy (`parentField` / nested `children`).
|
|
581
581
|
*
|
|
582
582
|
* @category Components
|
|
583
583
|
*/
|
|
584
|
-
class
|
|
585
|
-
/** Accumulated rows fed to the table (grows as pages are scrolled in). */
|
|
586
|
-
#loadedItems;
|
|
587
|
-
/** Total matching rows from the source (used to size the popup). */
|
|
588
|
-
#totalCount;
|
|
589
|
-
#host;
|
|
584
|
+
class AXLookupMultiColumnTreeComponent {
|
|
590
585
|
constructor() {
|
|
591
586
|
/**
|
|
592
|
-
* The datasource providing
|
|
593
|
-
* Pages are fetched on demand as the user scrolls the table body.
|
|
587
|
+
* The datasource providing hierarchical rows (roots on load, children via `parentField`).
|
|
594
588
|
*/
|
|
595
589
|
this.dataSource = input.required(/* @ts-ignore */
|
|
596
590
|
...(ngDevMode ? [{ debugName: "dataSource" }] : /* istanbul ignore next */ []));
|
|
597
591
|
/**
|
|
598
|
-
* The item property used as the selection value.
|
|
592
|
+
* The item property used as the selection value (also the data-table row key).
|
|
599
593
|
*/
|
|
600
594
|
this.valueField = input('id', /* @ts-ignore */
|
|
601
595
|
...(ngDevMode ? [{ debugName: "valueField" }] : /* istanbul ignore next */ []));
|
|
602
596
|
/**
|
|
603
|
-
* The item property used as the display text.
|
|
597
|
+
* The item property used as the display text. The matching column shows the expand control
|
|
598
|
+
* unless another column sets `expandHandler`.
|
|
604
599
|
*/
|
|
605
600
|
this.textField = input('text', /* @ts-ignore */
|
|
606
601
|
...(ngDevMode ? [{ debugName: "textField" }] : /* istanbul ignore next */ []));
|
|
@@ -609,6 +604,16 @@ class AXLookupMultiColumnComponent {
|
|
|
609
604
|
*/
|
|
610
605
|
this.disabledField = input('disabled', /* @ts-ignore */
|
|
611
606
|
...(ngDevMode ? [{ debugName: "disabledField" }] : /* istanbul ignore next */ []));
|
|
607
|
+
/**
|
|
608
|
+
* Field used by the data table to fetch children (`AXDataSource` filter on expand).
|
|
609
|
+
*/
|
|
610
|
+
this.parentField = input('parentId', /* @ts-ignore */
|
|
611
|
+
...(ngDevMode ? [{ debugName: "parentField" }] : /* istanbul ignore next */ []));
|
|
612
|
+
/**
|
|
613
|
+
* Field used by the data table to decide whether a row can expand.
|
|
614
|
+
*/
|
|
615
|
+
this.hasChildrenField = input('hasChild', /* @ts-ignore */
|
|
616
|
+
...(ngDevMode ? [{ debugName: "hasChildrenField" }] : /* istanbul ignore next */ []));
|
|
612
617
|
/**
|
|
613
618
|
* Currently selected values (always an array, even for single selection).
|
|
614
619
|
*/
|
|
@@ -651,40 +656,13 @@ class AXLookupMultiColumnComponent {
|
|
|
651
656
|
this.alternate = input(false, /* @ts-ignore */
|
|
652
657
|
...(ngDevMode ? [{ debugName: "alternate" }] : /* istanbul ignore next */ []));
|
|
653
658
|
/**
|
|
654
|
-
* Emitted when the user picks a row.
|
|
659
|
+
* Emitted when the user picks a row (not when expanding/collapsing).
|
|
655
660
|
*/
|
|
656
661
|
this.itemClick = output();
|
|
657
|
-
this.tableRef = viewChild(AXDataTableComponent, /* @ts-ignore */
|
|
658
|
-
...(ngDevMode ? [{ debugName: "tableRef" }] : /* istanbul ignore next */ []));
|
|
659
|
-
/** Accumulated rows fed to the table (grows as pages are scrolled in). */
|
|
660
|
-
this.#loadedItems = [];
|
|
661
|
-
/** Total matching rows from the source (used to size the popup). */
|
|
662
|
-
this.#totalCount = signal(0, /* @ts-ignore */
|
|
663
|
-
...(ngDevMode ? [{ debugName: "#totalCount" }] : /* istanbul ignore next */ []));
|
|
664
662
|
/**
|
|
665
|
-
*
|
|
666
|
-
* Kept as a stable instance so the table host is not recreated on each page load.
|
|
667
|
-
* `pageSize` grows with `#loadedItems` — never seed with a huge empty array (data-table
|
|
668
|
-
* initializes `displayedRows` as `new Array(pageSize)`, which caused NG0955-level jank).
|
|
669
|
-
*/
|
|
670
|
-
this.tableDataSource = new AXDataSource({
|
|
671
|
-
pageSize: 1,
|
|
672
|
-
key: 'id',
|
|
673
|
-
load: async () => ({
|
|
674
|
-
items: this.#loadedItems,
|
|
675
|
-
total: this.#loadedItems.length,
|
|
676
|
-
}),
|
|
677
|
-
});
|
|
678
|
-
/**
|
|
679
|
-
* Header (~40px) + body rows. Grows with results up to `maxVisibleItems`,
|
|
680
|
-
* then the table body scrolls for the rest.
|
|
663
|
+
* Header (~40px) + up to `maxVisibleItems` body rows. Expanded children scroll inside.
|
|
681
664
|
*/
|
|
682
|
-
this.tableHeight = computed(() =>
|
|
683
|
-
const header = 40;
|
|
684
|
-
const total = this.#totalCount();
|
|
685
|
-
const rows = total > 0 ? total : Math.min(3, this.maxVisibleItems());
|
|
686
|
-
return header + Math.min(rows, this.maxVisibleItems()) * this.itemHeight();
|
|
687
|
-
}, /* @ts-ignore */
|
|
665
|
+
this.tableHeight = computed(() => 40 + this.maxVisibleItems() * this.itemHeight(), /* @ts-ignore */
|
|
688
666
|
...(ngDevMode ? [{ debugName: "tableHeight" }] : /* istanbul ignore next */ []));
|
|
689
667
|
this.loadingConfig = computed(() => ({
|
|
690
668
|
enabled: true,
|
|
@@ -692,7 +670,6 @@ class AXLookupMultiColumnComponent {
|
|
|
692
670
|
loadingTemplate: this.loadingTemplate(),
|
|
693
671
|
}), /* @ts-ignore */
|
|
694
672
|
...(ngDevMode ? [{ debugName: "loadingConfig" }] : /* istanbul ignore next */ []));
|
|
695
|
-
this.#host = inject((ElementRef));
|
|
696
673
|
this.resolveRowCssClass = (row) => {
|
|
697
674
|
const item = row;
|
|
698
675
|
if (!item) {
|
|
@@ -707,44 +684,6 @@ class AXLookupMultiColumnComponent {
|
|
|
707
684
|
}
|
|
708
685
|
return classes.join(' ');
|
|
709
686
|
};
|
|
710
|
-
effect((onCleanup) => {
|
|
711
|
-
const source = this.dataSource();
|
|
712
|
-
this.tableDataSource.config.key = source.config.key || this.valueField();
|
|
713
|
-
untracked(() => {
|
|
714
|
-
this.#applyLoadedItems(source.cachedItems, source.totalCount);
|
|
715
|
-
});
|
|
716
|
-
const changedSub = source.onChanged.subscribe((event) => {
|
|
717
|
-
this.#applyLoadedItems(event.cachedItems, event.totalCount);
|
|
718
|
-
});
|
|
719
|
-
// Fetch page 0 only when the source has never loaded. Reopen remounts this
|
|
720
|
-
// view; setPage(0) is a no-op on a warm source and must not force a new API call.
|
|
721
|
-
queueMicrotask(() => {
|
|
722
|
-
if (!source.cachedItems.some((item) => item != null)) {
|
|
723
|
-
source.setPage(0);
|
|
724
|
-
}
|
|
725
|
-
});
|
|
726
|
-
onCleanup(() => changedSub.unsubscribe());
|
|
727
|
-
});
|
|
728
|
-
effect((onCleanup) => {
|
|
729
|
-
// Rebind when the table instance appears / source changes.
|
|
730
|
-
this.tableRef();
|
|
731
|
-
this.dataSource();
|
|
732
|
-
let scrollSub;
|
|
733
|
-
const timer = setTimeout(() => {
|
|
734
|
-
const wrapper = this.tableRef()?.getHostElement()?.querySelector('.ax-data-table-wrapper') ??
|
|
735
|
-
this.#host.nativeElement.querySelector('.ax-data-table-wrapper');
|
|
736
|
-
if (!wrapper) {
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
739
|
-
scrollSub = fromEvent(wrapper, 'scroll')
|
|
740
|
-
.pipe(auditTime(80))
|
|
741
|
-
.subscribe(() => this.#tryLoadMore(wrapper));
|
|
742
|
-
}, 0);
|
|
743
|
-
onCleanup(() => {
|
|
744
|
-
clearTimeout(timer);
|
|
745
|
-
scrollSub?.unsubscribe();
|
|
746
|
-
});
|
|
747
|
-
});
|
|
748
687
|
}
|
|
749
688
|
/** Stable unique key for `@for` — avoids NG0955 when `field` is missing or duplicated. */
|
|
750
689
|
trackColumnKey(index, column) {
|
|
@@ -757,14 +696,15 @@ class AXLookupMultiColumnComponent {
|
|
|
757
696
|
}
|
|
758
697
|
return width;
|
|
759
698
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
this.
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
this.
|
|
699
|
+
isExpandColumn(index, column) {
|
|
700
|
+
if (column.expandHandler === true) {
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
if (this.columns().some((item) => item.expandHandler === true)) {
|
|
704
|
+
return false;
|
|
705
|
+
}
|
|
706
|
+
const textIndex = this.columns().findIndex((item) => item.field === this.textField());
|
|
707
|
+
return (textIndex === -1 ? 0 : textIndex) === index;
|
|
768
708
|
}
|
|
769
709
|
handleRowClick(event) {
|
|
770
710
|
this.handleItemClick(event.data);
|
|
@@ -787,31 +727,17 @@ class AXLookupMultiColumnComponent {
|
|
|
787
727
|
isDisabled(item) {
|
|
788
728
|
return Boolean(item?.[this.disabledField()]);
|
|
789
729
|
}
|
|
790
|
-
#tryLoadMore(wrapper) {
|
|
791
|
-
const nearBottom = wrapper.scrollTop + wrapper.clientHeight >= wrapper.scrollHeight - 48;
|
|
792
|
-
if (!nearBottom) {
|
|
793
|
-
return;
|
|
794
|
-
}
|
|
795
|
-
const source = this.dataSource();
|
|
796
|
-
if (source.isLoading || source.totalCount === 0) {
|
|
797
|
-
return;
|
|
798
|
-
}
|
|
799
|
-
const loadedCount = source.cachedItems.filter((item) => item != null).length;
|
|
800
|
-
if (loadedCount >= source.totalCount) {
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
const nextPage = Math.floor(loadedCount / source.pageSize);
|
|
804
|
-
source.setPage(nextPage);
|
|
805
|
-
}
|
|
806
730
|
getValue(item) {
|
|
807
731
|
return item?.[this.valueField()];
|
|
808
732
|
}
|
|
809
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type:
|
|
810
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type:
|
|
733
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXLookupMultiColumnTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
734
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXLookupMultiColumnTreeComponent, isStandalone: true, selector: "ax-lookup-multi-column-tree", inputs: { dataSource: { classPropertyName: "dataSource", publicName: "dataSource", isSignal: true, isRequired: true, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, disabledField: { classPropertyName: "disabledField", publicName: "disabledField", isSignal: true, isRequired: false, transformFunction: null }, parentField: { classPropertyName: "parentField", publicName: "parentField", isSignal: true, isRequired: false, transformFunction: null }, hasChildrenField: { classPropertyName: "hasChildrenField", publicName: "hasChildrenField", isSignal: true, isRequired: false, transformFunction: null }, selectedValues: { classPropertyName: "selectedValues", publicName: "selectedValues", isSignal: true, isRequired: false, transformFunction: null }, itemHeight: { classPropertyName: "itemHeight", publicName: "itemHeight", isSignal: true, isRequired: false, transformFunction: null }, maxVisibleItems: { classPropertyName: "maxVisibleItems", publicName: "maxVisibleItems", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, itemTemplate: { classPropertyName: "itemTemplate", publicName: "itemTemplate", isSignal: true, isRequired: false, transformFunction: null }, emptyTemplate: { classPropertyName: "emptyTemplate", publicName: "emptyTemplate", isSignal: true, isRequired: false, transformFunction: null }, loadingTemplate: { classPropertyName: "loadingTemplate", publicName: "loadingTemplate", isSignal: true, isRequired: false, transformFunction: null }, alternate: { classPropertyName: "alternate", publicName: "alternate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick" }, host: { properties: { "style.height.px": "tableHeight()" }, classAttribute: "ax-lookup-multi-column ax-lookup-multi-column-tree" }, ngImport: i0, template: `
|
|
811
735
|
<ax-data-table
|
|
812
736
|
class="ax-lookup-multi-column-table"
|
|
813
737
|
look="minimal"
|
|
814
|
-
[dataSource]="
|
|
738
|
+
[dataSource]="dataSource()"
|
|
739
|
+
[parentField]="parentField()"
|
|
740
|
+
[hasChildrenField]="hasChildrenField()"
|
|
815
741
|
[itemHeight]="itemHeight()"
|
|
816
742
|
[alternative]="alternate()"
|
|
817
743
|
[emptyTemplate]="emptyTemplate()"
|
|
@@ -830,6 +756,7 @@ class AXLookupMultiColumnComponent {
|
|
|
830
756
|
[dataField]="column.field"
|
|
831
757
|
[caption]="column.title"
|
|
832
758
|
[width]="resolveColumnWidth(column)"
|
|
759
|
+
[expandHandler]="isExpandColumn($index, column)"
|
|
833
760
|
[allowSorting]="false"
|
|
834
761
|
[allowResizing]="false"
|
|
835
762
|
/>
|
|
@@ -856,22 +783,24 @@ class AXLookupMultiColumnComponent {
|
|
|
856
783
|
</ng-template>
|
|
857
784
|
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: AXDataTableComponent, selector: "ax-data-table", inputs: ["dataSource", "selectedRows", "parentField", "hasChildrenField", "rowDetailsTemplate", "rowCssClass", "cellCssClass", "highlightText", "look", "rowTemplate", "emptyTemplate", "noDataTemplate", "alternative", "showHeader", "fixedHeader", "showFooter", "fixedFooter", "itemHeight", "allowReordering", "paging", "fetchDataMode", "loading", "focusedRow"], outputs: ["selectedRowsChange", "focusedRowChange", "onRowClick", "onRowDbClick", "onColumnsOrderChanged", "onColumnSizeChanged", "onPageChanged"] }, { kind: "component", type: AXDataTableTextColumnComponent, selector: "ax-text-column", inputs: ["width", "caption", "allowSorting", "allowResizing", "fixed", "hasTitle", "customExpandIcon", "customCollapseIcon", "dataField", "expandHandler", "wrapText", "cellTemplate", "footerTemplate", "headerTemplate", "format", "formatOptions"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
858
785
|
}
|
|
859
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type:
|
|
786
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXLookupMultiColumnTreeComponent, decorators: [{
|
|
860
787
|
type: Component,
|
|
861
788
|
args: [{
|
|
862
|
-
selector: 'ax-lookup-multi-column',
|
|
789
|
+
selector: 'ax-lookup-multi-column-tree',
|
|
863
790
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
864
791
|
encapsulation: ViewEncapsulation.None,
|
|
865
792
|
imports: [NgTemplateOutlet, AXDataTableComponent, AXDataTableTextColumnComponent],
|
|
866
793
|
host: {
|
|
867
|
-
class: 'ax-lookup-multi-column',
|
|
794
|
+
class: 'ax-lookup-multi-column ax-lookup-multi-column-tree',
|
|
868
795
|
'[style.height.px]': 'tableHeight()',
|
|
869
796
|
},
|
|
870
797
|
template: `
|
|
871
798
|
<ax-data-table
|
|
872
799
|
class="ax-lookup-multi-column-table"
|
|
873
800
|
look="minimal"
|
|
874
|
-
[dataSource]="
|
|
801
|
+
[dataSource]="dataSource()"
|
|
802
|
+
[parentField]="parentField()"
|
|
803
|
+
[hasChildrenField]="hasChildrenField()"
|
|
875
804
|
[itemHeight]="itemHeight()"
|
|
876
805
|
[alternative]="alternate()"
|
|
877
806
|
[emptyTemplate]="emptyTemplate()"
|
|
@@ -890,6 +819,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
890
819
|
[dataField]="column.field"
|
|
891
820
|
[caption]="column.title"
|
|
892
821
|
[width]="resolveColumnWidth(column)"
|
|
822
|
+
[expandHandler]="isExpandColumn($index, column)"
|
|
893
823
|
[allowSorting]="false"
|
|
894
824
|
[allowResizing]="false"
|
|
895
825
|
/>
|
|
@@ -916,30 +846,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
916
846
|
</ng-template>
|
|
917
847
|
`,
|
|
918
848
|
}]
|
|
919
|
-
}],
|
|
849
|
+
}], propDecorators: { dataSource: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataSource", required: true }] }], valueField: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueField", required: false }] }], textField: [{ type: i0.Input, args: [{ isSignal: true, alias: "textField", required: false }] }], disabledField: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledField", required: false }] }], parentField: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentField", required: false }] }], hasChildrenField: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasChildrenField", required: false }] }], selectedValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedValues", required: false }] }], itemHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemHeight", required: false }] }], maxVisibleItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxVisibleItems", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], itemTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemTemplate", required: false }] }], emptyTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyTemplate", required: false }] }], loadingTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingTemplate", required: false }] }], alternate: [{ type: i0.Input, args: [{ isSignal: true, alias: "alternate", required: false }] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }] } });
|
|
920
850
|
|
|
921
851
|
/**
|
|
922
|
-
* Internal mini component for the `multi-column
|
|
923
|
-
*
|
|
924
|
-
*
|
|
852
|
+
* Internal mini component for the `multi-column` lookup mode:
|
|
853
|
+
* renders selectable rows in an `ax-data-table` grid and loads further
|
|
854
|
+
* pages as the user scrolls (no pager UI).
|
|
925
855
|
*
|
|
926
856
|
* @category Components
|
|
927
857
|
*/
|
|
928
|
-
class
|
|
858
|
+
class AXLookupMultiColumnComponent {
|
|
859
|
+
/** Accumulated rows fed to the table (grows as pages are scrolled in). */
|
|
860
|
+
#loadedItems;
|
|
861
|
+
/** Total matching rows from the source (used to size the popup). */
|
|
862
|
+
#totalCount;
|
|
863
|
+
#host;
|
|
929
864
|
constructor() {
|
|
930
865
|
/**
|
|
931
|
-
* The datasource providing
|
|
866
|
+
* The datasource providing the grid rows (paged, remote-capable).
|
|
867
|
+
* Pages are fetched on demand as the user scrolls the table body.
|
|
932
868
|
*/
|
|
933
869
|
this.dataSource = input.required(/* @ts-ignore */
|
|
934
870
|
...(ngDevMode ? [{ debugName: "dataSource" }] : /* istanbul ignore next */ []));
|
|
935
871
|
/**
|
|
936
|
-
* The item property used as the selection value
|
|
872
|
+
* The item property used as the selection value.
|
|
937
873
|
*/
|
|
938
874
|
this.valueField = input('id', /* @ts-ignore */
|
|
939
875
|
...(ngDevMode ? [{ debugName: "valueField" }] : /* istanbul ignore next */ []));
|
|
940
876
|
/**
|
|
941
|
-
* The item property used as the display text.
|
|
942
|
-
* unless another column sets `expandHandler`.
|
|
877
|
+
* The item property used as the display text.
|
|
943
878
|
*/
|
|
944
879
|
this.textField = input('text', /* @ts-ignore */
|
|
945
880
|
...(ngDevMode ? [{ debugName: "textField" }] : /* istanbul ignore next */ []));
|
|
@@ -948,16 +883,6 @@ class AXLookupMultiColumnTreeComponent {
|
|
|
948
883
|
*/
|
|
949
884
|
this.disabledField = input('disabled', /* @ts-ignore */
|
|
950
885
|
...(ngDevMode ? [{ debugName: "disabledField" }] : /* istanbul ignore next */ []));
|
|
951
|
-
/**
|
|
952
|
-
* Field used by the data table to fetch children (`AXDataSource` filter on expand).
|
|
953
|
-
*/
|
|
954
|
-
this.parentField = input('parentId', /* @ts-ignore */
|
|
955
|
-
...(ngDevMode ? [{ debugName: "parentField" }] : /* istanbul ignore next */ []));
|
|
956
|
-
/**
|
|
957
|
-
* Field used by the data table to decide whether a row can expand.
|
|
958
|
-
*/
|
|
959
|
-
this.hasChildrenField = input('hasChild', /* @ts-ignore */
|
|
960
|
-
...(ngDevMode ? [{ debugName: "hasChildrenField" }] : /* istanbul ignore next */ []));
|
|
961
886
|
/**
|
|
962
887
|
* Currently selected values (always an array, even for single selection).
|
|
963
888
|
*/
|
|
@@ -1000,13 +925,40 @@ class AXLookupMultiColumnTreeComponent {
|
|
|
1000
925
|
this.alternate = input(false, /* @ts-ignore */
|
|
1001
926
|
...(ngDevMode ? [{ debugName: "alternate" }] : /* istanbul ignore next */ []));
|
|
1002
927
|
/**
|
|
1003
|
-
* Emitted when the user picks a row
|
|
928
|
+
* Emitted when the user picks a row.
|
|
1004
929
|
*/
|
|
1005
930
|
this.itemClick = output();
|
|
931
|
+
this.tableRef = viewChild(AXDataTableComponent, /* @ts-ignore */
|
|
932
|
+
...(ngDevMode ? [{ debugName: "tableRef" }] : /* istanbul ignore next */ []));
|
|
933
|
+
/** Accumulated rows fed to the table (grows as pages are scrolled in). */
|
|
934
|
+
this.#loadedItems = [];
|
|
935
|
+
/** Total matching rows from the source (used to size the popup). */
|
|
936
|
+
this.#totalCount = signal(0, /* @ts-ignore */
|
|
937
|
+
...(ngDevMode ? [{ debugName: "#totalCount" }] : /* istanbul ignore next */ []));
|
|
1006
938
|
/**
|
|
1007
|
-
*
|
|
939
|
+
* Local table source: one page of whatever has been scrolled in so far.
|
|
940
|
+
* Kept as a stable instance so the table host is not recreated on each page load.
|
|
941
|
+
* `pageSize` grows with `#loadedItems` — never seed with a huge empty array (data-table
|
|
942
|
+
* initializes `displayedRows` as `new Array(pageSize)`, which caused NG0955-level jank).
|
|
1008
943
|
*/
|
|
1009
|
-
this.
|
|
944
|
+
this.tableDataSource = new AXDataSource({
|
|
945
|
+
pageSize: 1,
|
|
946
|
+
key: 'id',
|
|
947
|
+
load: async () => ({
|
|
948
|
+
items: this.#loadedItems,
|
|
949
|
+
total: this.#loadedItems.length,
|
|
950
|
+
}),
|
|
951
|
+
});
|
|
952
|
+
/**
|
|
953
|
+
* Header (~40px) + body rows. Grows with results up to `maxVisibleItems`,
|
|
954
|
+
* then the table body scrolls for the rest.
|
|
955
|
+
*/
|
|
956
|
+
this.tableHeight = computed(() => {
|
|
957
|
+
const header = 40;
|
|
958
|
+
const total = this.#totalCount();
|
|
959
|
+
const rows = total > 0 ? total : Math.min(3, this.maxVisibleItems());
|
|
960
|
+
return header + Math.min(rows, this.maxVisibleItems()) * this.itemHeight();
|
|
961
|
+
}, /* @ts-ignore */
|
|
1010
962
|
...(ngDevMode ? [{ debugName: "tableHeight" }] : /* istanbul ignore next */ []));
|
|
1011
963
|
this.loadingConfig = computed(() => ({
|
|
1012
964
|
enabled: true,
|
|
@@ -1014,6 +966,7 @@ class AXLookupMultiColumnTreeComponent {
|
|
|
1014
966
|
loadingTemplate: this.loadingTemplate(),
|
|
1015
967
|
}), /* @ts-ignore */
|
|
1016
968
|
...(ngDevMode ? [{ debugName: "loadingConfig" }] : /* istanbul ignore next */ []));
|
|
969
|
+
this.#host = inject((ElementRef));
|
|
1017
970
|
this.resolveRowCssClass = (row) => {
|
|
1018
971
|
const item = row;
|
|
1019
972
|
if (!item) {
|
|
@@ -1028,6 +981,44 @@ class AXLookupMultiColumnTreeComponent {
|
|
|
1028
981
|
}
|
|
1029
982
|
return classes.join(' ');
|
|
1030
983
|
};
|
|
984
|
+
effect((onCleanup) => {
|
|
985
|
+
const source = this.dataSource();
|
|
986
|
+
this.tableDataSource.config.key = source.config.key || this.valueField();
|
|
987
|
+
untracked(() => {
|
|
988
|
+
this.#applyLoadedItems(source.cachedItems, source.totalCount);
|
|
989
|
+
});
|
|
990
|
+
const changedSub = source.onChanged.subscribe((event) => {
|
|
991
|
+
this.#applyLoadedItems(event.cachedItems, event.totalCount);
|
|
992
|
+
});
|
|
993
|
+
// Fetch page 0 only when the source has never loaded. Reopen remounts this
|
|
994
|
+
// view; setPage(0) is a no-op on a warm source and must not force a new API call.
|
|
995
|
+
queueMicrotask(() => {
|
|
996
|
+
if (!source.cachedItems.some((item) => item != null)) {
|
|
997
|
+
source.setPage(0);
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
onCleanup(() => changedSub.unsubscribe());
|
|
1001
|
+
});
|
|
1002
|
+
effect((onCleanup) => {
|
|
1003
|
+
// Rebind when the table instance appears / source changes.
|
|
1004
|
+
this.tableRef();
|
|
1005
|
+
this.dataSource();
|
|
1006
|
+
let scrollSub;
|
|
1007
|
+
const timer = setTimeout(() => {
|
|
1008
|
+
const wrapper = this.tableRef()?.getHostElement()?.querySelector('.ax-data-table-wrapper') ??
|
|
1009
|
+
this.#host.nativeElement.querySelector('.ax-data-table-wrapper');
|
|
1010
|
+
if (!wrapper) {
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
scrollSub = fromEvent(wrapper, 'scroll')
|
|
1014
|
+
.pipe(auditTime(80))
|
|
1015
|
+
.subscribe(() => this.#tryLoadMore(wrapper));
|
|
1016
|
+
}, 0);
|
|
1017
|
+
onCleanup(() => {
|
|
1018
|
+
clearTimeout(timer);
|
|
1019
|
+
scrollSub?.unsubscribe();
|
|
1020
|
+
});
|
|
1021
|
+
});
|
|
1031
1022
|
}
|
|
1032
1023
|
/** Stable unique key for `@for` — avoids NG0955 when `field` is missing or duplicated. */
|
|
1033
1024
|
trackColumnKey(index, column) {
|
|
@@ -1040,15 +1031,14 @@ class AXLookupMultiColumnTreeComponent {
|
|
|
1040
1031
|
}
|
|
1041
1032
|
return width;
|
|
1042
1033
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
return (textIndex === -1 ? 0 : textIndex) === index;
|
|
1034
|
+
#applyLoadedItems(cachedItems, totalCount) {
|
|
1035
|
+
this.#loadedItems = cachedItems.filter((item) => item != null);
|
|
1036
|
+
this.#totalCount.set(totalCount);
|
|
1037
|
+
this.#syncTablePageSize();
|
|
1038
|
+
this.tableDataSource.refresh({ reset: true });
|
|
1039
|
+
}
|
|
1040
|
+
#syncTablePageSize() {
|
|
1041
|
+
this.tableDataSource.config.pageSize = Math.max(this.#loadedItems.length, 1);
|
|
1052
1042
|
}
|
|
1053
1043
|
handleRowClick(event) {
|
|
1054
1044
|
this.handleItemClick(event.data);
|
|
@@ -1071,17 +1061,31 @@ class AXLookupMultiColumnTreeComponent {
|
|
|
1071
1061
|
isDisabled(item) {
|
|
1072
1062
|
return Boolean(item?.[this.disabledField()]);
|
|
1073
1063
|
}
|
|
1064
|
+
#tryLoadMore(wrapper) {
|
|
1065
|
+
const nearBottom = wrapper.scrollTop + wrapper.clientHeight >= wrapper.scrollHeight - 48;
|
|
1066
|
+
if (!nearBottom) {
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
const source = this.dataSource();
|
|
1070
|
+
if (source.isLoading || source.totalCount === 0) {
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
const loadedCount = source.cachedItems.filter((item) => item != null).length;
|
|
1074
|
+
if (loadedCount >= source.totalCount) {
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
const nextPage = Math.floor(loadedCount / source.pageSize);
|
|
1078
|
+
source.setPage(nextPage);
|
|
1079
|
+
}
|
|
1074
1080
|
getValue(item) {
|
|
1075
1081
|
return item?.[this.valueField()];
|
|
1076
1082
|
}
|
|
1077
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type:
|
|
1078
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type:
|
|
1083
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXLookupMultiColumnComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1084
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXLookupMultiColumnComponent, isStandalone: true, selector: "ax-lookup-multi-column", inputs: { dataSource: { classPropertyName: "dataSource", publicName: "dataSource", isSignal: true, isRequired: true, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, disabledField: { classPropertyName: "disabledField", publicName: "disabledField", isSignal: true, isRequired: false, transformFunction: null }, selectedValues: { classPropertyName: "selectedValues", publicName: "selectedValues", isSignal: true, isRequired: false, transformFunction: null }, itemHeight: { classPropertyName: "itemHeight", publicName: "itemHeight", isSignal: true, isRequired: false, transformFunction: null }, maxVisibleItems: { classPropertyName: "maxVisibleItems", publicName: "maxVisibleItems", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, itemTemplate: { classPropertyName: "itemTemplate", publicName: "itemTemplate", isSignal: true, isRequired: false, transformFunction: null }, emptyTemplate: { classPropertyName: "emptyTemplate", publicName: "emptyTemplate", isSignal: true, isRequired: false, transformFunction: null }, loadingTemplate: { classPropertyName: "loadingTemplate", publicName: "loadingTemplate", isSignal: true, isRequired: false, transformFunction: null }, alternate: { classPropertyName: "alternate", publicName: "alternate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick" }, host: { properties: { "style.height.px": "tableHeight()" }, classAttribute: "ax-lookup-multi-column" }, viewQueries: [{ propertyName: "tableRef", first: true, predicate: AXDataTableComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
1079
1085
|
<ax-data-table
|
|
1080
1086
|
class="ax-lookup-multi-column-table"
|
|
1081
1087
|
look="minimal"
|
|
1082
|
-
[dataSource]="
|
|
1083
|
-
[parentField]="parentField()"
|
|
1084
|
-
[hasChildrenField]="hasChildrenField()"
|
|
1088
|
+
[dataSource]="tableDataSource"
|
|
1085
1089
|
[itemHeight]="itemHeight()"
|
|
1086
1090
|
[alternative]="alternate()"
|
|
1087
1091
|
[emptyTemplate]="emptyTemplate()"
|
|
@@ -1100,7 +1104,6 @@ class AXLookupMultiColumnTreeComponent {
|
|
|
1100
1104
|
[dataField]="column.field"
|
|
1101
1105
|
[caption]="column.title"
|
|
1102
1106
|
[width]="resolveColumnWidth(column)"
|
|
1103
|
-
[expandHandler]="isExpandColumn($index, column)"
|
|
1104
1107
|
[allowSorting]="false"
|
|
1105
1108
|
[allowResizing]="false"
|
|
1106
1109
|
/>
|
|
@@ -1127,24 +1130,22 @@ class AXLookupMultiColumnTreeComponent {
|
|
|
1127
1130
|
</ng-template>
|
|
1128
1131
|
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: AXDataTableComponent, selector: "ax-data-table", inputs: ["dataSource", "selectedRows", "parentField", "hasChildrenField", "rowDetailsTemplate", "rowCssClass", "cellCssClass", "highlightText", "look", "rowTemplate", "emptyTemplate", "noDataTemplate", "alternative", "showHeader", "fixedHeader", "showFooter", "fixedFooter", "itemHeight", "allowReordering", "paging", "fetchDataMode", "loading", "focusedRow"], outputs: ["selectedRowsChange", "focusedRowChange", "onRowClick", "onRowDbClick", "onColumnsOrderChanged", "onColumnSizeChanged", "onPageChanged"] }, { kind: "component", type: AXDataTableTextColumnComponent, selector: "ax-text-column", inputs: ["width", "caption", "allowSorting", "allowResizing", "fixed", "hasTitle", "customExpandIcon", "customCollapseIcon", "dataField", "expandHandler", "wrapText", "cellTemplate", "footerTemplate", "headerTemplate", "format", "formatOptions"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
1129
1132
|
}
|
|
1130
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type:
|
|
1133
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXLookupMultiColumnComponent, decorators: [{
|
|
1131
1134
|
type: Component,
|
|
1132
1135
|
args: [{
|
|
1133
|
-
selector: 'ax-lookup-multi-column
|
|
1136
|
+
selector: 'ax-lookup-multi-column',
|
|
1134
1137
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1135
1138
|
encapsulation: ViewEncapsulation.None,
|
|
1136
1139
|
imports: [NgTemplateOutlet, AXDataTableComponent, AXDataTableTextColumnComponent],
|
|
1137
1140
|
host: {
|
|
1138
|
-
class: 'ax-lookup-multi-column
|
|
1141
|
+
class: 'ax-lookup-multi-column',
|
|
1139
1142
|
'[style.height.px]': 'tableHeight()',
|
|
1140
1143
|
},
|
|
1141
1144
|
template: `
|
|
1142
1145
|
<ax-data-table
|
|
1143
1146
|
class="ax-lookup-multi-column-table"
|
|
1144
1147
|
look="minimal"
|
|
1145
|
-
[dataSource]="
|
|
1146
|
-
[parentField]="parentField()"
|
|
1147
|
-
[hasChildrenField]="hasChildrenField()"
|
|
1148
|
+
[dataSource]="tableDataSource"
|
|
1148
1149
|
[itemHeight]="itemHeight()"
|
|
1149
1150
|
[alternative]="alternate()"
|
|
1150
1151
|
[emptyTemplate]="emptyTemplate()"
|
|
@@ -1163,7 +1164,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
1163
1164
|
[dataField]="column.field"
|
|
1164
1165
|
[caption]="column.title"
|
|
1165
1166
|
[width]="resolveColumnWidth(column)"
|
|
1166
|
-
[expandHandler]="isExpandColumn($index, column)"
|
|
1167
1167
|
[allowSorting]="false"
|
|
1168
1168
|
[allowResizing]="false"
|
|
1169
1169
|
/>
|
|
@@ -1190,7 +1190,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
1190
1190
|
</ng-template>
|
|
1191
1191
|
`,
|
|
1192
1192
|
}]
|
|
1193
|
-
}], propDecorators: { dataSource: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataSource", required: true }] }], valueField: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueField", required: false }] }], textField: [{ type: i0.Input, args: [{ isSignal: true, alias: "textField", required: false }] }], disabledField: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledField", required: false }] }],
|
|
1193
|
+
}], ctorParameters: () => [], propDecorators: { dataSource: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataSource", required: true }] }], valueField: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueField", required: false }] }], textField: [{ type: i0.Input, args: [{ isSignal: true, alias: "textField", required: false }] }], disabledField: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledField", required: false }] }], selectedValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedValues", required: false }] }], itemHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemHeight", required: false }] }], maxVisibleItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxVisibleItems", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], itemTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemTemplate", required: false }] }], emptyTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyTemplate", required: false }] }], loadingTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingTemplate", required: false }] }], alternate: [{ type: i0.Input, args: [{ isSignal: true, alias: "alternate", required: false }] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }], tableRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXDataTableComponent), { isSignal: true }] }] } });
|
|
1194
1194
|
|
|
1195
1195
|
/**
|
|
1196
1196
|
* Internal mini component for the `multi-select-tree` lookup mode:
|
|
@@ -1466,6 +1466,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
1466
1466
|
class AXLookupComponent extends NXValueComponent {
|
|
1467
1467
|
#platform;
|
|
1468
1468
|
#destroyRef;
|
|
1469
|
+
/** Reused so closing/reopening the popup does not create a new AXDataSource (which would miss cache). */
|
|
1470
|
+
#multiColumnTreeTableSource;
|
|
1471
|
+
#multiColumnTreeTableKey;
|
|
1472
|
+
#multiColumnTreeInput;
|
|
1469
1473
|
/**
|
|
1470
1474
|
* Tree callback results keyed by parent id. Survives popup remount so
|
|
1471
1475
|
* drop-down-tree / multi-select-tree do not refetch on every open.
|
|
@@ -1713,15 +1717,24 @@ class AXLookupComponent extends NXValueComponent {
|
|
|
1713
1717
|
this.resolvedDataSource = computed(() => {
|
|
1714
1718
|
const tree = this.treeDataSource();
|
|
1715
1719
|
if (this.mode() === 'multi-column-tree' && tree) {
|
|
1720
|
+
const key = `${this.valueField()}|${this.textField()}|${this.treeParentField()}|${this.hasChildrenField()}`;
|
|
1721
|
+
if (this.#multiColumnTreeTableSource &&
|
|
1722
|
+
this.#multiColumnTreeInput === tree &&
|
|
1723
|
+
this.#multiColumnTreeTableKey === key) {
|
|
1724
|
+
return this.#multiColumnTreeTableSource;
|
|
1725
|
+
}
|
|
1716
1726
|
const cachedTree = Array.isArray(tree)
|
|
1717
1727
|
? tree
|
|
1718
1728
|
: (nodeId) => this.#loadCachedTreeNodes(tree, nodeId);
|
|
1719
|
-
|
|
1729
|
+
this.#multiColumnTreeInput = tree;
|
|
1730
|
+
this.#multiColumnTreeTableKey = key;
|
|
1731
|
+
this.#multiColumnTreeTableSource = createLookupTreeTableDataSource(cachedTree, {
|
|
1720
1732
|
valueField: this.valueField(),
|
|
1721
1733
|
textField: this.textField(),
|
|
1722
1734
|
parentField: this.treeParentField(),
|
|
1723
1735
|
hasChildrenField: this.hasChildrenField(),
|
|
1724
1736
|
});
|
|
1737
|
+
return this.#multiColumnTreeTableSource;
|
|
1725
1738
|
}
|
|
1726
1739
|
const source = this.dataSource();
|
|
1727
1740
|
if (!source) {
|