@sdcorejs/angular 19.0.0 → 19.0.3
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/README.md +36 -19
- package/assets/scss/core/_inline-edit.scss +105 -0
- package/assets/scss/core/form.scss +15 -0
- package/components/autoid-inspector/src/models/autoid-element.model.d.ts +11 -0
- package/components/autoid-inspector/src/models/autoid-export-format.model.d.ts +19 -0
- package/components/autoid-inspector/src/services/autoid-export.service.d.ts +7 -1
- package/components/autoid-inspector/src/services/autoid-scanner.service.d.ts +15 -2
- package/components/form-generic/src/components/form-builder/form-builder.component.d.ts +12 -13
- package/components/index.d.ts +1 -0
- package/components/modal-resizable/index.d.ts +1 -0
- package/components/modal-resizable/src/modal-resizable.component.d.ts +29 -0
- package/components/query-bar/src/components/inline-chip/inline-chip.component.d.ts +11 -22
- package/components/query-bar/src/components/inline-value-chip/inline-value-chip.component.d.ts +3 -7
- package/components/table/src/components/config/config.component.d.ts +3 -1
- package/components/table/src/components/selector-action/selector-action.component.d.ts +3 -1
- package/components/table/src/models/table-option-tree.model.d.ts +96 -8
- package/components/table/src/services/tree/tree.util.d.ts +43 -2
- package/components/view/src/view.component.d.ts +33 -0
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +97 -10
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-avatar.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-avatar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-button.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs +102 -189
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +158 -0
- package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-operator.mjs +3 -1
- package/fesm2022/sdcorejs-angular-components-operator.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-preview.mjs +4 -4
- package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs +41 -87
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-table.mjs +234 -107
- package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-view.mjs +43 -11
- package/fesm2022/sdcorejs-angular-components-view.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components.mjs +1 -0
- package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +25 -5
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +9 -4
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +10 -5
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip.mjs +10 -5
- package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs +18 -9
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date.mjs +20 -8
- package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs +20 -8
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-inline-text.mjs +167 -0
- package/fesm2022/sdcorejs-angular-forms-inline-text.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs +5 -3
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs +34 -7
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input.mjs +35 -9
- package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-models.mjs +32 -2
- package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-radio.mjs +9 -4
- package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-select.mjs +33 -9
- package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-switch.mjs +9 -4
- package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs +15 -5
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-i18n.mjs +20 -0
- package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-confirm.mjs +1 -1
- package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-notify.mjs +15 -2
- package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
- package/forms/autocomplete/src/autocomplete.component.d.ts +16 -3
- package/forms/checkbox/src/checkbox.component.d.ts +5 -1
- package/forms/chip/src/chip.component.d.ts +5 -2
- package/forms/chip-calendar/src/chip-calendar.component.d.ts +5 -2
- package/forms/date/src/date.component.d.ts +14 -5
- package/forms/date-range/src/date-range.component.d.ts +12 -5
- package/forms/datetime/src/datetime.component.d.ts +14 -5
- package/forms/inline-text/index.d.ts +1 -0
- package/forms/inline-text/src/inline-text.component.d.ts +76 -0
- package/forms/input/src/input.component.d.ts +14 -2
- package/forms/input-color/src/input-color.component.d.ts +3 -1
- package/forms/input-number/src/input-number.component.d.ts +14 -2
- package/forms/models/index.d.ts +1 -0
- package/forms/models/src/sd-viewed.d.ts +38 -0
- package/forms/radio/src/radio.component.d.ts +5 -2
- package/forms/select/src/select.component.d.ts +23 -5
- package/forms/switch/src/switch.component.d.ts +5 -2
- package/forms/textarea/src/textarea.component.d.ts +9 -2
- package/i18n/src/en.d.ts +4 -0
- package/package.json +10 -1
- package/services/notify/src/components/toast/toast.component.d.ts +4 -0
|
@@ -280,7 +280,7 @@ class ColumnFilterComponent {
|
|
|
280
280
|
// Blur input: commit giá trị KHÔNG trigger reload.
|
|
281
281
|
onFilterCommit = () => this.filterCommit.emit();
|
|
282
282
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ColumnFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
283
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: ColumnFilterComponent, isStandalone: true, selector: "column-filter", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, columnFilter: { classPropertyName: "columnFilter", publicName: "columnFilter", isSignal: true, isRequired: false, transformFunction: null }, cacheValues: { classPropertyName: "cacheValues", publicName: "cacheValues", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, operator: { classPropertyName: "operator", publicName: "operator", isSignal: true, isRequired: false, transformFunction: null }, isMobile: { classPropertyName: "isMobile", publicName: "isMobile", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { operator: "operatorChange", filterChange: "filterChange", filterCommit: "filterCommit" }, ngImport: i0, template: "@let _column = column();\n@let _columnFilter = columnFilter();\n@let _autoId = autoId();\n@let _templateRef = templateRef();\n@let _operators = operators();\n@let _items = items();\n@let _label = label();\n@let _context = { columnFilter: _columnFilter, autoId: _autoId };\n\n<div class=\"d-flex c-inline-column align-items-end\" style=\"width: 100%; max-width: 100%\">\n @if (\n _column.type === 'string' ||\n _column.type === 'number' ||\n _column.type === 'boolean' ||\n _column.type === 'values' ||\n _column.type === 'lazy-values' ||\n _column.type === 'date' ||\n _column.type === 'datetime' ||\n _column.type === 'time'\n ) {\n <!-- Operator filter -->\n @if (_operators.length) {\n <div class=\"d-flex align-items-center\" [ngClass]=\"operatorWrapperClass()\">\n <sd-operator [(model)]=\"operator\" [operators]=\"operatorValues()\" />\n </div>\n }\n\n <!-- N\u1EBFu filter nh\u1EADn v\u00E0o template -->\n @if (_templateRef) {\n <ng-container *ngTemplateOutlet=\"_templateRef; context: _context\"></ng-container>\n }\n <!-- M\u1EB7c \u0111\u1ECBnh theo type v\u00E0 gi\u00E1 tr\u1ECB values defined c\u1EE7a column -->\n @else {\n @if (_column.type === 'string') {\n <sd-input\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n type=\"text\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[_column.field]\"\n (keyupEnter)=\"onFilterChange()\"\n (sdBlur)=\"onFilterCommit()\"\n (cleared)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n </sd-input>\n } @else if (_column.type === 'number') {\n @if (!_column.filter?.type) {\n <sd-input-number\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[_column.field]\"\n (keyupEnter)=\"onFilterChange()\"\n (sdBlur)=\"onFilterCommit()\"\n (cleared)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n </sd-input-number>\n } @else if (_column.filter?.type === 'split-number') {\n <ng-container\n *ngTemplateOutlet=\"splitNumberTpl; context: { field: _column.field, disabled: _column.filter?.disabled }\">\n </ng-container>\n }\n } @else if (_column.type === 'boolean') {\n <sd-select\n minWidthPanel=\"200px\"\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n [style.width]=\"'100%'\"\n size=\"sm\"\n [label]=\"_label\"\n [items]=\"[\n { value: '1', display: _column.option?.displayOnTrue || 'True' },\n { value: '0', display: _column.option?.displayOnFalse || 'False' },\n ]\"\n valueField=\"value\"\n displayField=\"display\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n <ng-template sdItemDef let-item=\"item\">\n @if (item.value === '1') {\n <sd-badge color=\"success\" [title]=\"_column.option?.displayOnTrue || 'True'\"> </sd-badge>\n } @else {\n <sd-badge color=\"error\" [title]=\"_column.option?.displayOnFalse || 'False'\"> </sd-badge>\n }\n </ng-template>\n </sd-select>\n } @else if (_column.type === 'values') {\n <sd-select\n minWidthPanel=\"200px\"\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n [style.width]=\"'100%'\"\n size=\"sm\"\n [label]=\"_label\"\n [items]=\"_items\"\n [valueField]=\"_column.option.valueField\"\n [displayField]=\"_column.option.displayField\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n [multiple]=\"_column.option.selection === 'MULTIPLE'\"\n hideInlineError>\n </sd-select>\n } @else if (_column.type === 'lazy-values') {\n <sd-select\n minWidthPanel=\"200px\"\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n [style.width]=\"'100%'\"\n size=\"sm\"\n [label]=\"_label\"\n [items]=\"_items\"\n [valueField]=\"_column.option.valueField\"\n [displayField]=\"_column.option.displayField\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n [multiple]=\"_column.option.selection === 'MULTIPLE'\"\n hideInlineError>\n </sd-select>\n } @else if (_column.type === 'date' || _column.type === 'datetime' || _column.type === 'time') {\n @if (!_column.filter?.type || _column.filter?.type === 'daterange') {\n <sd-date-range\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n </sd-date-range>\n } @else if (_column.filter?.type === 'date') {\n <sd-date\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n type=\"date\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n </sd-date>\n } @else if (_column.filter?.type === 'split-date') {\n <ng-container\n *ngTemplateOutlet=\"splitDateTpl; context: { field: _column.field, disabled: _column.filter?.disabled }\">\n </ng-container>\n }\n }\n }\n } @else {\n <sd-input [autoId]=\"_autoId\" style=\"flex: 1 1 0%; min-width: 0\" type=\"text\" size=\"sm\" disabled></sd-input>\n }\n</div>\n\n<!-- ================================================ -->\n<!-- Split from/to templates (\u0111\u01B0a xu\u1ED1ng cu\u1ED1i file) -->\n<!-- ================================================ -->\n<ng-template #splitNumberTpl let-field=\"field\" let-disabled=\"disabled\">\n <div class=\"d-flex align-items-center\" style=\"flex: 1 1 0%; min-width: 0\">\n <sd-input-number\n [autoId]=\"_autoId + '-from'\"\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[field].from\"\n (sdChange)=\"onFilterChange()\"\n (sdBlur)=\"onFilterCommit()\"\n (cleared)=\"onFilterChange()\"\n [disabled]=\"disabled\"\n hideInlineError>\n </sd-input-number>\n <div class=\"mx-4\">-</div>\n <sd-input-number\n [autoId]=\"_autoId + '-to'\"\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n [(model)]=\"_columnFilter[field].to\"\n (sdChange)=\"onFilterChange()\"\n (sdBlur)=\"onFilterCommit()\"\n (cleared)=\"onFilterChange()\"\n [disabled]=\"disabled\"\n hideInlineError>\n </sd-input-number>\n </div>\n</ng-template>\n\n<ng-template #splitDateTpl let-field=\"field\" let-disabled=\"disabled\">\n <div class=\"d-flex align-items-center\" style=\"flex: 1 1 0%; min-width: 0\">\n <sd-date\n [autoId]=\"_autoId + '-from'\"\n style=\"flex: 1 1 0%; min-width: 0\"\n type=\"date\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[field].from\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"disabled\"\n hideInlineError>\n </sd-date>\n <div class=\"mx-4\">-</div>\n <sd-date\n [autoId]=\"_autoId + '-to'\"\n style=\"flex: 1 1 0%; min-width: 0\"\n type=\"date\"\n size=\"sm\"\n [(model)]=\"_columnFilter[field].to\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"disabled\"\n hideInlineError>\n </sd-date>\n </div>\n</ng-template>\n", styles: [":host ::ng-deep .c-inline-column .mat-mdc-text-field-wrapper{background-color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdInputNumber, selector: "sd-input-number", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "type", "precision", "format", "min", "max", "validator", "inlineError", "hyperlink", "appearance", "floatLabel", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "bare", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "bare", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "bare", "viewed", "appearance", "floatLabel", "min", "max", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "component", type: SdBadge, selector: "sd-badge", inputs: ["type", "color", "primary", "secondary", "success", "info", "warning", "error", "fontSet", "title", "description", "tooltip", "icon", "size"], outputs: ["click"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
283
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: ColumnFilterComponent, isStandalone: true, selector: "column-filter", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, columnFilter: { classPropertyName: "columnFilter", publicName: "columnFilter", isSignal: true, isRequired: false, transformFunction: null }, cacheValues: { classPropertyName: "cacheValues", publicName: "cacheValues", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, operator: { classPropertyName: "operator", publicName: "operator", isSignal: true, isRequired: false, transformFunction: null }, isMobile: { classPropertyName: "isMobile", publicName: "isMobile", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { operator: "operatorChange", filterChange: "filterChange", filterCommit: "filterCommit" }, ngImport: i0, template: "@let _column = column();\n@let _columnFilter = columnFilter();\n@let _autoId = autoId();\n@let _templateRef = templateRef();\n@let _operators = operators();\n@let _items = items();\n@let _label = label();\n@let _context = { columnFilter: _columnFilter, autoId: _autoId };\n\n<div class=\"d-flex c-inline-column align-items-end\" style=\"width: 100%; max-width: 100%\">\n @if (\n _column.type === 'string' ||\n _column.type === 'number' ||\n _column.type === 'boolean' ||\n _column.type === 'values' ||\n _column.type === 'lazy-values' ||\n _column.type === 'date' ||\n _column.type === 'datetime' ||\n _column.type === 'time'\n ) {\n <!-- Operator filter -->\n @if (_operators.length) {\n <div class=\"d-flex align-items-center\" [ngClass]=\"operatorWrapperClass()\">\n <sd-operator [(model)]=\"operator\" [operators]=\"operatorValues()\" />\n </div>\n }\n\n <!-- N\u1EBFu filter nh\u1EADn v\u00E0o template -->\n @if (_templateRef) {\n <ng-container *ngTemplateOutlet=\"_templateRef; context: _context\"></ng-container>\n }\n <!-- M\u1EB7c \u0111\u1ECBnh theo type v\u00E0 gi\u00E1 tr\u1ECB values defined c\u1EE7a column -->\n @else {\n @if (_column.type === 'string') {\n <sd-input\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n type=\"text\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[_column.field]\"\n (keyupEnter)=\"onFilterChange()\"\n (sdBlur)=\"onFilterCommit()\"\n (cleared)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n </sd-input>\n } @else if (_column.type === 'number') {\n @if (!_column.filter?.type) {\n <sd-input-number\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[_column.field]\"\n (keyupEnter)=\"onFilterChange()\"\n (sdBlur)=\"onFilterCommit()\"\n (cleared)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n </sd-input-number>\n } @else if (_column.filter?.type === 'split-number') {\n <ng-container\n *ngTemplateOutlet=\"splitNumberTpl; context: { field: _column.field, disabled: _column.filter?.disabled }\">\n </ng-container>\n }\n } @else if (_column.type === 'boolean') {\n <sd-select\n minWidthPanel=\"200px\"\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n [style.width]=\"'100%'\"\n size=\"sm\"\n [label]=\"_label\"\n [items]=\"[\n { value: '1', display: _column.option?.displayOnTrue || 'True' },\n { value: '0', display: _column.option?.displayOnFalse || 'False' },\n ]\"\n valueField=\"value\"\n displayField=\"display\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n <ng-template sdItemDef let-item=\"item\">\n @if (item.value === '1') {\n <sd-badge color=\"success\" [title]=\"_column.option?.displayOnTrue || 'True'\"> </sd-badge>\n } @else {\n <sd-badge color=\"error\" [title]=\"_column.option?.displayOnFalse || 'False'\"> </sd-badge>\n }\n </ng-template>\n </sd-select>\n } @else if (_column.type === 'values') {\n <sd-select\n minWidthPanel=\"200px\"\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n [style.width]=\"'100%'\"\n size=\"sm\"\n [label]=\"_label\"\n [items]=\"_items\"\n [valueField]=\"_column.option.valueField\"\n [displayField]=\"_column.option.displayField\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n [multiple]=\"_column.option.selection === 'MULTIPLE'\"\n hideInlineError>\n </sd-select>\n } @else if (_column.type === 'lazy-values') {\n <sd-select\n minWidthPanel=\"200px\"\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n [style.width]=\"'100%'\"\n size=\"sm\"\n [label]=\"_label\"\n [items]=\"_items\"\n [valueField]=\"_column.option.valueField\"\n [displayField]=\"_column.option.displayField\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n [multiple]=\"_column.option.selection === 'MULTIPLE'\"\n hideInlineError>\n </sd-select>\n } @else if (_column.type === 'date' || _column.type === 'datetime' || _column.type === 'time') {\n @if (!_column.filter?.type || _column.filter?.type === 'daterange') {\n <sd-date-range\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n </sd-date-range>\n } @else if (_column.filter?.type === 'date') {\n <sd-date\n [autoId]=\"_autoId\"\n style=\"flex: 1 1 0%; min-width: 0\"\n type=\"date\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[_column.field]\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"_column.filter?.disabled\"\n hideInlineError>\n </sd-date>\n } @else if (_column.filter?.type === 'split-date') {\n <ng-container\n *ngTemplateOutlet=\"splitDateTpl; context: { field: _column.field, disabled: _column.filter?.disabled }\">\n </ng-container>\n }\n }\n }\n } @else {\n <sd-input [autoId]=\"_autoId\" style=\"flex: 1 1 0%; min-width: 0\" type=\"text\" size=\"sm\" disabled></sd-input>\n }\n</div>\n\n<!-- ================================================ -->\n<!-- Split from/to templates (\u0111\u01B0a xu\u1ED1ng cu\u1ED1i file) -->\n<!-- ================================================ -->\n<ng-template #splitNumberTpl let-field=\"field\" let-disabled=\"disabled\">\n <div class=\"d-flex align-items-center\" style=\"flex: 1 1 0%; min-width: 0\">\n <sd-input-number\n [autoId]=\"_autoId + '-from'\"\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[field].from\"\n (sdChange)=\"onFilterChange()\"\n (sdBlur)=\"onFilterCommit()\"\n (cleared)=\"onFilterChange()\"\n [disabled]=\"disabled\"\n hideInlineError>\n </sd-input-number>\n <div class=\"mx-4\">-</div>\n <sd-input-number\n [autoId]=\"_autoId + '-to'\"\n style=\"flex: 1 1 0%; min-width: 0\"\n size=\"sm\"\n [(model)]=\"_columnFilter[field].to\"\n (sdChange)=\"onFilterChange()\"\n (sdBlur)=\"onFilterCommit()\"\n (cleared)=\"onFilterChange()\"\n [disabled]=\"disabled\"\n hideInlineError>\n </sd-input-number>\n </div>\n</ng-template>\n\n<ng-template #splitDateTpl let-field=\"field\" let-disabled=\"disabled\">\n <div class=\"d-flex align-items-center\" style=\"flex: 1 1 0%; min-width: 0\">\n <sd-date\n [autoId]=\"_autoId + '-from'\"\n style=\"flex: 1 1 0%; min-width: 0\"\n type=\"date\"\n size=\"sm\"\n [label]=\"_label\"\n [(model)]=\"_columnFilter[field].from\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"disabled\"\n hideInlineError>\n </sd-date>\n <div class=\"mx-4\">-</div>\n <sd-date\n [autoId]=\"_autoId + '-to'\"\n style=\"flex: 1 1 0%; min-width: 0\"\n type=\"date\"\n size=\"sm\"\n [(model)]=\"_columnFilter[field].to\"\n (sdChange)=\"onFilterChange()\"\n [disabled]=\"disabled\"\n hideInlineError>\n </sd-date>\n </div>\n</ng-template>\n", styles: [":host ::ng-deep .c-inline-column .mat-mdc-text-field-wrapper{background-color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdInputNumber, selector: "sd-input-number", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "type", "precision", "format", "min", "max", "validator", "inlineError", "hyperlink", "appearance", "floatLabel", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "clearable", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "viewed", "clearable", "appearance", "floatLabel", "min", "max", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "component", type: SdBadge, selector: "sd-badge", inputs: ["type", "color", "primary", "secondary", "success", "info", "warning", "error", "fontSet", "title", "description", "tooltip", "icon", "size"], outputs: ["click"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
284
284
|
}
|
|
285
285
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ColumnFilterComponent, decorators: [{
|
|
286
286
|
type: Component,
|
|
@@ -312,14 +312,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
312
312
|
}]
|
|
313
313
|
}], ctorParameters: () => [] });
|
|
314
314
|
|
|
315
|
-
|
|
315
|
+
/**
|
|
316
|
+
* Type-guard: tree có nạp children lười (lazy) hay không.
|
|
317
|
+
* Dùng để narrow discriminated union `SdTableOptionTree` một cách an toàn —
|
|
318
|
+
* thay vì truy cập `option.onExpandChildren` trực tiếp trên union (chỉ tồn tại
|
|
319
|
+
* ở nhánh lazy).
|
|
320
|
+
*/
|
|
321
|
+
const isLazyTree = (option) => option?.loadType === 'lazy';
|
|
322
|
+
/**
|
|
323
|
+
* Key chứa mảng children trên object row.
|
|
324
|
+
* - `static`: lấy `childrenKey` đã cấu hình, mặc định `'children'`.
|
|
325
|
+
* - `lazy`: không có `childrenKey` riêng → luôn dùng `'children'` làm nơi lưu
|
|
326
|
+
* kết quả nạp lười.
|
|
327
|
+
*/
|
|
328
|
+
const getChildrenKey = (option) => (option?.loadType === 'static' ? option.childrenKey : undefined) ?? 'children';
|
|
316
329
|
const getChildrenFromData = (data, option) => {
|
|
317
330
|
const key = getChildrenKey(option);
|
|
318
331
|
const raw = data?.[key];
|
|
319
332
|
return Array.isArray(raw) ? raw : [];
|
|
320
333
|
};
|
|
334
|
+
/**
|
|
335
|
+
* Trạng thái bung mặc định cho một cấp.
|
|
336
|
+
* Chỉ `static` mới có `defaultExpanded`; lazy luôn trả `false` (không thể bung
|
|
337
|
+
* mặc định nhánh chưa nạp).
|
|
338
|
+
*/
|
|
321
339
|
const resolveDefaultExpanded = (level, option) => {
|
|
322
|
-
const def = option?.defaultExpanded ?? false;
|
|
340
|
+
const def = option?.loadType === 'static' ? option.defaultExpanded ?? false : false;
|
|
323
341
|
if (def === true)
|
|
324
342
|
return true;
|
|
325
343
|
if (def === false)
|
|
@@ -328,17 +346,53 @@ const resolveDefaultExpanded = (level, option) => {
|
|
|
328
346
|
return level < def;
|
|
329
347
|
return false;
|
|
330
348
|
};
|
|
349
|
+
/**
|
|
350
|
+
* Row có khả năng có children hay không (để hiện nút bung).
|
|
351
|
+
* - Có children embedded → true.
|
|
352
|
+
* - Lazy tree: dùng `hasChildren(row.data)` nếu được cấu hình; không thì luôn
|
|
353
|
+
* true (chưa biết cho tới khi nạp).
|
|
354
|
+
*/
|
|
331
355
|
const resolveHasChildren = (row, option) => {
|
|
332
356
|
const embedded = getChildrenFromData(row.data, option);
|
|
333
357
|
if (embedded.length > 0)
|
|
334
358
|
return true;
|
|
335
|
-
|
|
359
|
+
if (isLazyTree(option))
|
|
360
|
+
return option.hasChildren ? option.hasChildren(row.data) : true;
|
|
361
|
+
return false;
|
|
336
362
|
};
|
|
363
|
+
/** Row cần nạp lười (lazy) khi bung: là lazy tree và chưa có children embedded. */
|
|
337
364
|
const hasLazyChildren = (row, option) => {
|
|
338
|
-
if (!option
|
|
365
|
+
if (!isLazyTree(option))
|
|
339
366
|
return false;
|
|
340
367
|
return getChildrenFromData(row.data, option).length === 0;
|
|
341
368
|
};
|
|
369
|
+
/**
|
|
370
|
+
* True nếu `data` HOẶC bất kỳ hậu duệ nào (theo `childrenKey`) thoả `predicate`.
|
|
371
|
+
*
|
|
372
|
+
* Dùng cho search ở cấp con (static tree, table `type: 'local'`): một nhánh
|
|
373
|
+
* gốc được giữ lại trong kết quả lọc nếu subtree của nó có ít nhất một node
|
|
374
|
+
* khớp từ khoá. `visited` chống đệ quy vô hạn khi data có circular reference.
|
|
375
|
+
*/
|
|
376
|
+
const subtreeMatches = (data, predicate, option, visited = new Set()) => {
|
|
377
|
+
if (data == null || visited.has(data))
|
|
378
|
+
return false;
|
|
379
|
+
visited.add(data);
|
|
380
|
+
if (predicate(data))
|
|
381
|
+
return true;
|
|
382
|
+
for (const child of getChildrenFromData(data, option)) {
|
|
383
|
+
if (subtreeMatches(child, predicate, option, visited))
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
386
|
+
return false;
|
|
387
|
+
};
|
|
388
|
+
/**
|
|
389
|
+
* Lọc mảng children: chỉ giữ child có subtree khớp `predicate`.
|
|
390
|
+
*
|
|
391
|
+
* Trả về MẢNG MỚI nhưng GIỮ NGUYÊN object reference của từng child (không clone)
|
|
392
|
+
* — nhờ vậy `meta.id` (hash từ data) ổn định, selection / trạng thái bung không
|
|
393
|
+
* bị mất khi từ khoá search đổi. Đây là bước "prune" của tính năng search-con.
|
|
394
|
+
*/
|
|
395
|
+
const filterMatchingChildren = (children, predicate, option) => children.filter(child => subtreeMatches(child, predicate, option));
|
|
342
396
|
const flattenTree = (roots, option, visited = new Set()) => {
|
|
343
397
|
if (!option)
|
|
344
398
|
return roots;
|
|
@@ -565,7 +619,7 @@ class ExternalFilterComponent {
|
|
|
565
619
|
});
|
|
566
620
|
};
|
|
567
621
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ExternalFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
568
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: ExternalFilterComponent, isStandalone: true, selector: "external-filter", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, externalFilters: { classPropertyName: "externalFilters", publicName: "externalFilters", isSignal: true, isRequired: false, transformFunction: null }, filterRegister: { classPropertyName: "filterRegister", publicName: "filterRegister", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@let _filter = filter();\n@let _autoId = autoId();\n@let _items = filterItems();\n@let _externalFilter = externalFilter();\n@let _inlineExternal = inlineExternal();\n@let _filtered = filtered();\n@let _col = col();\n\n@if (!_filter?.disabled) {\n @if (!isMobileOrTablet && _items.length) {\n <sd-section\n [hideHeader]=\"_filter?.hideExternalFilterToolbar\"\n icon=\"filter_alt\"\n iconColor=\"secondary\"\n [title]=\"'core.component.table.filter' | translate\"\n collapsable>\n <div class=\"d-flex mr-4\" sdHeaderRight>\n <sd-button\n [autoId]=\"_autoId + 'clear'\"\n [disabled]=\"!_filtered\"\n type=\"link\"\n prefixIcon=\"cleaning_services\"\n [tooltip]=\"'core.component.table.clear-selection' | translate\"\n (click)=\"$event.stopPropagation(); clearFilter($event)\"></sd-button>\n <sd-button\n [autoId]=\"_autoId + 'setting'\"\n type=\"link\"\n prefixIcon=\"settings\"\n [tooltip]=\"'core.component.table.customize' | translate\"\n [matMenuTriggerFor]=\"menu\"\n (click)=\"$event.stopPropagation()\"></sd-button>\n <mat-menu #menu=\"matMenu\" class=\"sd-custom-panel-filter-configuration\" [attr.data-autoid]=\"_autoId + 'setting-menu'\">\n @for (externalFilter of _items; track externalFilter.field) {\n <button\n [attr.data-autoid]=\"_autoId + 'setting-' + externalFilter.field\"\n mat-menu-item\n (click)=\"onCheckboxChange($event, externalFilter)\"\n [disabled]=\"externalFilter.required\">\n <sd-checkbox\n [autoId]=\"_autoId + 'setting-' + externalFilter.field + '-checkbox'\"\n [label]=\"externalFilter.title\"\n [model]=\"_inlineExternal[externalFilter.field]\"\n [disabled]=\"externalFilter.required\" />\n </button>\n }\n </mat-menu>\n </div>\n <div class=\"row mx-0\" style=\"row-gap: 16px\">\n @for (item of _items; track item.field) {\n @if (_inlineExternal[item.field]) {\n <div class=\"col-md-3 col-sm-6 px-8\" [class.col-lg-2]=\"_col === 2\" [class.col-lg-3]=\"_col === 3\">\n @if (item.type === 'string') {\n <sd-input\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n type=\"text\"\n [(model)]=\"_externalFilter[item.field]\"\n (keyupEnter)=\"onKeyupEnter()\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-input>\n } @else if (item.type === 'number') {\n <sd-input-number\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [(model)]=\"_externalFilter[item.field]\"\n (keyupEnter)=\"onKeyupEnter()\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-input-number>\n } @else if (item.type === 'boolean') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"[\n { value: true, display: item.option?.displayOnTrue || 'True' },\n { value: false, display: item.option?.displayOnFalse || 'False' },\n ]\"\n valueField=\"value\"\n displayField=\"display\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.type === 'date') {\n <sd-date\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n hideInlineError>\n </sd-date>\n } @else if (item.type === 'datetime') {\n <sd-datetime\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n hideInlineError>\n </sd-datetime>\n } @else if (item.type === 'daterange') {\n <sd-date-range\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n [form]=\"form\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n appearance=\"outline\"\n hideInlineError>\n </sd-date-range>\n } @else if (item.type === 'values') {\n @if (item.option.selection === 'MULTIPLE') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n multiple\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.option.selection === 'AUTOCOMPLETE') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.option.selection === 'MULTIPLEAUTOCOMPLETE') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n multiple\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n }\n } @else if (item.type === 'lazy-values') {\n @if (item.option.selection === 'MULTIPLE') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n multiple\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n }\n } @else if (item.type === 'custom' && item.filterDef) {\n <ng-container\n *ngTemplateOutlet=\"\n item.filterDef;\n context: { form: form, externalFilter: _externalFilter, autoId: _autoId + item.field }\n \">\n </ng-container>\n }\n </div>\n }\n }\n </div>\n @if (_filter?.manualFilter) {\n <div class=\"d-flex justify-content-end mt-16 px-8\">\n <sd-button\n [autoId]=\"_autoId + 'submit'\"\n [disabled]=\"form.invalid\"\n type=\"fill\"\n color=\"primary\"\n [title]=\"'core.component.table.search' | translate\"\n prefixIcon=\"filter_alt\"\n (click)=\"onSubmit()\">\n </sd-button>\n </div>\n }\n </sd-section>\n }\n}\n", styles: [":host{display:block;padding-left:0;padding-right:0}::ng-deep .sd-custom-panel-filter-configuration{width:240px;max-height:550px;overflow-y:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: SdSection, selector: "sd-section", inputs: ["title", "subTitle", "icon", "iconColor", "collapsed", "collapsable", "hideHeader", "noPaddingBody"], outputs: ["collapsedChange"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdInputNumber, selector: "sd-input-number", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "type", "precision", "format", "min", "max", "validator", "inlineError", "hyperlink", "appearance", "floatLabel", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "bare", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "bare", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "bare", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange$1, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "bare", "viewed", "appearance", "floatLabel", "min", "max", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "pipe", type: FilterValuesPipe, name: "filterValues" }, { kind: "component", type: SdCheckbox, selector: "sd-checkbox", inputs: ["autoId", "name", "form", "label", "color", "disabled", "viewed", "inlineError", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
622
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: ExternalFilterComponent, isStandalone: true, selector: "external-filter", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, externalFilters: { classPropertyName: "externalFilters", publicName: "externalFilters", isSignal: true, isRequired: false, transformFunction: null }, filterRegister: { classPropertyName: "filterRegister", publicName: "filterRegister", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@let _filter = filter();\n@let _autoId = autoId();\n@let _items = filterItems();\n@let _externalFilter = externalFilter();\n@let _inlineExternal = inlineExternal();\n@let _filtered = filtered();\n@let _col = col();\n\n@if (!_filter?.disabled) {\n @if (!isMobileOrTablet && _items.length) {\n <sd-section\n [hideHeader]=\"_filter?.hideExternalFilterToolbar\"\n icon=\"filter_alt\"\n iconColor=\"secondary\"\n [title]=\"'core.component.table.filter' | translate\"\n collapsable>\n <div class=\"d-flex mr-4\" sdHeaderRight>\n <sd-button\n [autoId]=\"_autoId + 'clear'\"\n [disabled]=\"!_filtered\"\n type=\"link\"\n prefixIcon=\"cleaning_services\"\n [tooltip]=\"'core.component.table.clear-selection' | translate\"\n (click)=\"$event.stopPropagation(); clearFilter($event)\"></sd-button>\n <sd-button\n [autoId]=\"_autoId + 'setting'\"\n type=\"link\"\n prefixIcon=\"settings\"\n [tooltip]=\"'core.component.table.customize' | translate\"\n [matMenuTriggerFor]=\"menu\"\n (click)=\"$event.stopPropagation()\"></sd-button>\n <mat-menu #menu=\"matMenu\" class=\"sd-custom-panel-filter-configuration\" [attr.data-autoid]=\"_autoId + 'setting-menu'\">\n @for (externalFilter of _items; track externalFilter.field) {\n <button\n [attr.data-autoid]=\"_autoId + 'setting-' + externalFilter.field\"\n mat-menu-item\n (click)=\"onCheckboxChange($event, externalFilter)\"\n [disabled]=\"externalFilter.required\">\n <sd-checkbox\n [autoId]=\"_autoId + 'setting-' + externalFilter.field + '-checkbox'\"\n [label]=\"externalFilter.title\"\n [model]=\"_inlineExternal[externalFilter.field]\"\n [disabled]=\"externalFilter.required\" />\n </button>\n }\n </mat-menu>\n </div>\n <div class=\"row mx-0\" style=\"row-gap: 16px\">\n @for (item of _items; track item.field) {\n @if (_inlineExternal[item.field]) {\n <div class=\"col-md-3 col-sm-6 px-8\" [class.col-lg-2]=\"_col === 2\" [class.col-lg-3]=\"_col === 3\">\n @if (item.type === 'string') {\n <sd-input\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n type=\"text\"\n [(model)]=\"_externalFilter[item.field]\"\n (keyupEnter)=\"onKeyupEnter()\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-input>\n } @else if (item.type === 'number') {\n <sd-input-number\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [(model)]=\"_externalFilter[item.field]\"\n (keyupEnter)=\"onKeyupEnter()\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-input-number>\n } @else if (item.type === 'boolean') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"[\n { value: true, display: item.option?.displayOnTrue || 'True' },\n { value: false, display: item.option?.displayOnFalse || 'False' },\n ]\"\n valueField=\"value\"\n displayField=\"display\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.type === 'date') {\n <sd-date\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n hideInlineError>\n </sd-date>\n } @else if (item.type === 'datetime') {\n <sd-datetime\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n hideInlineError>\n </sd-datetime>\n } @else if (item.type === 'daterange') {\n <sd-date-range\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n [form]=\"form\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n appearance=\"outline\"\n hideInlineError>\n </sd-date-range>\n } @else if (item.type === 'values') {\n @if (item.option.selection === 'MULTIPLE') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n multiple\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.option.selection === 'AUTOCOMPLETE') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.option.selection === 'MULTIPLEAUTOCOMPLETE') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n multiple\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n }\n } @else if (item.type === 'lazy-values') {\n @if (item.option.selection === 'MULTIPLE') {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n multiple\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else {\n <sd-select\n [autoId]=\"_autoId + item.field\"\n [label]=\"item.title\"\n [items]=\"item.option.items\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_externalFilter[item.field]\"\n (sdChange)=\"onFilter(item)\"\n appearance=\"outline\"\n [form]=\"form\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n }\n } @else if (item.type === 'custom' && item.filterDef) {\n <ng-container\n *ngTemplateOutlet=\"\n item.filterDef;\n context: { form: form, externalFilter: _externalFilter, autoId: _autoId + item.field }\n \">\n </ng-container>\n }\n </div>\n }\n }\n </div>\n @if (_filter?.manualFilter) {\n <div class=\"d-flex justify-content-end mt-16 px-8\">\n <sd-button\n [autoId]=\"_autoId + 'submit'\"\n [disabled]=\"form.invalid\"\n type=\"fill\"\n color=\"primary\"\n [title]=\"'core.component.table.search' | translate\"\n prefixIcon=\"filter_alt\"\n (click)=\"onSubmit()\">\n </sd-button>\n </div>\n }\n </sd-section>\n }\n}\n", styles: [":host{display:block;padding-left:0;padding-right:0}::ng-deep .sd-custom-panel-filter-configuration{width:240px;max-height:550px;overflow-y:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: SdSection, selector: "sd-section", inputs: ["title", "subTitle", "icon", "iconColor", "collapsed", "collapsable", "hideHeader", "noPaddingBody"], outputs: ["collapsedChange"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdInputNumber, selector: "sd-input-number", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "type", "precision", "format", "min", "max", "validator", "inlineError", "hyperlink", "appearance", "floatLabel", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "clearable", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange$1, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "viewed", "clearable", "appearance", "floatLabel", "min", "max", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "pipe", type: FilterValuesPipe, name: "filterValues" }, { kind: "component", type: SdCheckbox, selector: "sd-checkbox", inputs: ["autoId", "name", "form", "label", "color", "disabled", "viewed", "inlineError", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
569
623
|
}
|
|
570
624
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ExternalFilterComponent, decorators: [{
|
|
571
625
|
type: Component,
|
|
@@ -661,7 +715,7 @@ class MobileFilterComponent {
|
|
|
661
715
|
this.drawer().close();
|
|
662
716
|
};
|
|
663
717
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: MobileFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
664
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: MobileFilterComponent, isStandalone: true, selector: "mobile-filter", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, externalFilters: { classPropertyName: "externalFilters", publicName: "externalFilters", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, filterDefs: { classPropertyName: "filterDefs", publicName: "filterDefs", isSignal: true, isRequired: false, transformFunction: null }, filterRegister: { classPropertyName: "filterRegister", publicName: "filterRegister", isSignal: true, isRequired: false, transformFunction: null }, cacheValues: { classPropertyName: "cacheValues", publicName: "cacheValues", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "drawer", first: true, predicate: SdSideDrawer, descendants: true, isSignal: true }], ngImport: i0, template: "@let _autoId = autoId();\n@let _externalFilters = externalFilterItems();\n@let _columns = columnItems();\n@let _filterDefs = filterDefItems();\n@let _workingColumnFilter = workingColumnFilter();\n@let _workingColumnOperator = workingColumnOperator();\n@let _workingExternalFilter = workingExternalFilter();\n@let _cacheValues = cacheValues();\n\n<sd-side-drawer\n #drawer\n [title]=\"'core.component.table.filter' | translate\"\n width=\"90vw\">\n <div class=\"mobile-filter-body\">\n <!-- ============================ -->\n <!-- 1. External filters (inline) -->\n <!-- ============================ -->\n @if (_externalFilters.length) {\n <section class=\"mobile-filter-section\">\n @for (item of _externalFilters; track item.field) {\n <div class=\"mobile-filter-row\">\n @if (item.type === 'string') {\n <sd-input\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n type=\"text\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-input>\n } @else if (item.type === 'number') {\n <sd-input-number\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-input-number>\n } @else if (item.type === 'boolean') {\n <sd-select\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [items]=\"[\n { value: true, display: item.option?.displayOnTrue || 'True' },\n { value: false, display: item.option?.displayOnFalse || 'False' },\n ]\"\n valueField=\"value\"\n displayField=\"display\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.type === 'date') {\n <sd-date\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n hideInlineError>\n </sd-date>\n } @else if (item.type === 'datetime') {\n <sd-datetime\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n hideInlineError>\n </sd-datetime>\n } @else if (item.type === 'daterange') {\n <sd-date-range\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n appearance=\"outline\"\n hideInlineError>\n </sd-date-range>\n } @else if (item.type === 'values') {\n <sd-select\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [multiple]=\"item.option.selection === 'MULTIPLE' || item.option.selection === 'MULTIPLEAUTOCOMPLETE'\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.type === 'lazy-values') {\n <sd-select\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [items]=\"item.option.items\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [multiple]=\"item.option.selection === 'MULTIPLE'\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.type === 'custom' && item.filterDef) {\n <ng-container\n *ngTemplateOutlet=\"\n item.filterDef;\n context: { externalFilter: _workingExternalFilter, autoId: _autoId + 'ext-' + item.field }\n \">\n </ng-container>\n }\n </div>\n }\n </section>\n }\n\n <!-- ============================ -->\n <!-- 2. Custom filterDef templates -->\n <!-- ============================ -->\n @if (_filterDefs.length) {\n <section class=\"mobile-filter-section\">\n @for (def of _filterDefs; track def.sdTableFilterDef()) {\n <div class=\"mobile-filter-row\" [attr.data-autoid]=\"_autoId + 'def-' + def.sdTableFilterDef()\">\n <ng-container\n *ngTemplateOutlet=\"def.templateRef!; context: { externalFilter: _workingExternalFilter }\">\n </ng-container>\n </div>\n }\n </section>\n }\n\n <!-- ============================ -->\n <!-- 3. Column filters -->\n <!-- ============================ -->\n @if (_columns.length) {\n <section class=\"mobile-filter-section\">\n @for (column of _columns; track column.field) {\n <div class=\"mobile-filter-row\" [attr.data-autoid]=\"_autoId + 'col-' + column.field\">\n <column-filter\n [autoId]=\"_autoId + 'col'\"\n [column]=\"column\"\n [columnFilter]=\"_workingColumnFilter\"\n [operator]=\"$any(_workingColumnOperator[column.field])\"\n (operatorChange)=\"_workingColumnOperator[column.field] = $event!\"\n [cacheValues]=\"_cacheValues\"\n isMobile>\n </column-filter>\n </div>\n }\n </section>\n }\n </div>\n\n <ng-container sdFooter>\n <div class=\"mobile-filter-footer\">\n <sd-button\n [autoId]=\"_autoId + 'cancel'\"\n class=\"mobile-filter-action\"\n (click)=\"onCancel()\"\n [title]=\"'core.confirm.no' | translate\"\n type=\"link\"\n color=\"secondary\">\n </sd-button>\n <sd-button\n [autoId]=\"_autoId + 'apply'\"\n class=\"mobile-filter-action\"\n (click)=\"onApply()\"\n [title]=\"'core.confirm.yes' | translate\"\n type=\"fill\"\n color=\"primary\">\n </sd-button>\n </div>\n </ng-container>\n</sd-side-drawer>\n", styles: [".mobile-filter-body{display:flex;flex-direction:column;gap:16px;padding:8px 0}.mobile-filter-section{display:flex;flex-direction:column;gap:12px}.mobile-filter-row{display:flex;flex-direction:column}.mobile-filter-footer{display:flex;gap:8px;width:100%}.mobile-filter-action{flex:1 1 50%}.mobile-filter-action ::ng-deep button{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "component", type: SdSideDrawer, selector: "sd-side-drawer", inputs: ["title", "width", "hideClose", "disableBackdropClose", "drawerClass", "autoId"], outputs: ["sdClosed"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: ColumnFilterComponent, selector: "column-filter", inputs: ["autoId", "column", "columnFilter", "cacheValues", "value", "operator", "isMobile"], outputs: ["operatorChange", "filterChange", "filterCommit"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdInputNumber, selector: "sd-input-number", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "type", "precision", "format", "min", "max", "validator", "inlineError", "hyperlink", "appearance", "floatLabel", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "bare", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "bare", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "bare", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange$1, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "bare", "viewed", "appearance", "floatLabel", "min", "max", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "pipe", type: FilterValuesPipe, name: "filterValues" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
718
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: MobileFilterComponent, isStandalone: true, selector: "mobile-filter", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, externalFilters: { classPropertyName: "externalFilters", publicName: "externalFilters", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, filterDefs: { classPropertyName: "filterDefs", publicName: "filterDefs", isSignal: true, isRequired: false, transformFunction: null }, filterRegister: { classPropertyName: "filterRegister", publicName: "filterRegister", isSignal: true, isRequired: false, transformFunction: null }, cacheValues: { classPropertyName: "cacheValues", publicName: "cacheValues", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "drawer", first: true, predicate: SdSideDrawer, descendants: true, isSignal: true }], ngImport: i0, template: "@let _autoId = autoId();\n@let _externalFilters = externalFilterItems();\n@let _columns = columnItems();\n@let _filterDefs = filterDefItems();\n@let _workingColumnFilter = workingColumnFilter();\n@let _workingColumnOperator = workingColumnOperator();\n@let _workingExternalFilter = workingExternalFilter();\n@let _cacheValues = cacheValues();\n\n<sd-side-drawer\n #drawer\n [title]=\"'core.component.table.filter' | translate\"\n width=\"90vw\">\n <div class=\"mobile-filter-body\">\n <!-- ============================ -->\n <!-- 1. External filters (inline) -->\n <!-- ============================ -->\n @if (_externalFilters.length) {\n <section class=\"mobile-filter-section\">\n @for (item of _externalFilters; track item.field) {\n <div class=\"mobile-filter-row\">\n @if (item.type === 'string') {\n <sd-input\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n type=\"text\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-input>\n } @else if (item.type === 'number') {\n <sd-input-number\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-input-number>\n } @else if (item.type === 'boolean') {\n <sd-select\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [items]=\"[\n { value: true, display: item.option?.displayOnTrue || 'True' },\n { value: false, display: item.option?.displayOnFalse || 'False' },\n ]\"\n valueField=\"value\"\n displayField=\"display\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.type === 'date') {\n <sd-date\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n hideInlineError>\n </sd-date>\n } @else if (item.type === 'datetime') {\n <sd-datetime\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n hideInlineError>\n </sd-datetime>\n } @else if (item.type === 'daterange') {\n <sd-date-range\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n [required]=\"item.required\"\n [min]=\"item.minDate\"\n [max]=\"item.maxDate\"\n appearance=\"outline\"\n hideInlineError>\n </sd-date-range>\n } @else if (item.type === 'values') {\n <sd-select\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [items]=\"item.option.items | filterValues | async\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [multiple]=\"item.option.selection === 'MULTIPLE' || item.option.selection === 'MULTIPLEAUTOCOMPLETE'\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.type === 'lazy-values') {\n <sd-select\n [autoId]=\"_autoId + 'ext-' + item.field\"\n size=\"sm\"\n [label]=\"item.title\"\n [items]=\"item.option.items\"\n [valueField]=\"item.option.valueField\"\n [displayField]=\"item.option.displayField\"\n [(model)]=\"_workingExternalFilter[item.field]\"\n appearance=\"outline\"\n [multiple]=\"item.option.selection === 'MULTIPLE'\"\n [required]=\"item.required\"\n hideInlineError>\n </sd-select>\n } @else if (item.type === 'custom' && item.filterDef) {\n <ng-container\n *ngTemplateOutlet=\"\n item.filterDef;\n context: { externalFilter: _workingExternalFilter, autoId: _autoId + 'ext-' + item.field }\n \">\n </ng-container>\n }\n </div>\n }\n </section>\n }\n\n <!-- ============================ -->\n <!-- 2. Custom filterDef templates -->\n <!-- ============================ -->\n @if (_filterDefs.length) {\n <section class=\"mobile-filter-section\">\n @for (def of _filterDefs; track def.sdTableFilterDef()) {\n <div class=\"mobile-filter-row\" [attr.data-autoid]=\"_autoId + 'def-' + def.sdTableFilterDef()\">\n <ng-container\n *ngTemplateOutlet=\"def.templateRef!; context: { externalFilter: _workingExternalFilter }\">\n </ng-container>\n </div>\n }\n </section>\n }\n\n <!-- ============================ -->\n <!-- 3. Column filters -->\n <!-- ============================ -->\n @if (_columns.length) {\n <section class=\"mobile-filter-section\">\n @for (column of _columns; track column.field) {\n <div class=\"mobile-filter-row\" [attr.data-autoid]=\"_autoId + 'col-' + column.field\">\n <column-filter\n [autoId]=\"_autoId + 'col'\"\n [column]=\"column\"\n [columnFilter]=\"_workingColumnFilter\"\n [operator]=\"$any(_workingColumnOperator[column.field])\"\n (operatorChange)=\"_workingColumnOperator[column.field] = $event!\"\n [cacheValues]=\"_cacheValues\"\n isMobile>\n </column-filter>\n </div>\n }\n </section>\n }\n </div>\n\n <ng-container sdFooter>\n <div class=\"mobile-filter-footer\">\n <sd-button\n [autoId]=\"_autoId + 'cancel'\"\n class=\"mobile-filter-action\"\n (click)=\"onCancel()\"\n [title]=\"'core.confirm.no' | translate\"\n type=\"link\"\n color=\"secondary\">\n </sd-button>\n <sd-button\n [autoId]=\"_autoId + 'apply'\"\n class=\"mobile-filter-action\"\n (click)=\"onApply()\"\n [title]=\"'core.confirm.yes' | translate\"\n type=\"fill\"\n color=\"primary\">\n </sd-button>\n </div>\n </ng-container>\n</sd-side-drawer>\n", styles: [".mobile-filter-body{display:flex;flex-direction:column;gap:16px;padding:8px 0}.mobile-filter-section{display:flex;flex-direction:column;gap:12px}.mobile-filter-row{display:flex;flex-direction:column}.mobile-filter-footer{display:flex;gap:8px;width:100%}.mobile-filter-action{flex:1 1 50%}.mobile-filter-action ::ng-deep button{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "component", type: SdSideDrawer, selector: "sd-side-drawer", inputs: ["title", "width", "hideClose", "disableBackdropClose", "drawerClass", "autoId"], outputs: ["sdClosed"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: ColumnFilterComponent, selector: "column-filter", inputs: ["autoId", "column", "columnFilter", "cacheValues", "value", "operator", "isMobile"], outputs: ["operatorChange", "filterChange", "filterCommit"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdInputNumber, selector: "sd-input-number", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "type", "precision", "format", "min", "max", "validator", "inlineError", "hyperlink", "appearance", "floatLabel", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "clearable", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange$1, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "viewed", "clearable", "appearance", "floatLabel", "min", "max", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "pipe", type: FilterValuesPipe, name: "filterValues" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
665
719
|
}
|
|
666
720
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: MobileFilterComponent, decorators: [{
|
|
667
721
|
type: Component,
|
|
@@ -756,9 +810,12 @@ class SelectorActionComponent {
|
|
|
756
810
|
// ==========================================
|
|
757
811
|
// 1. SIGNAL INPUTS / OUTPUTS
|
|
758
812
|
// ==========================================
|
|
813
|
+
autoIdInput = input(undefined, { alias: 'autoId' });
|
|
759
814
|
tableOption = input(undefined);
|
|
760
815
|
selectedTableItems = input(undefined);
|
|
761
816
|
clear = output();
|
|
817
|
+
// Base autoId (đã là `components-table-<scope>` từ parent), '' khi parent không set.
|
|
818
|
+
autoId = computed(() => this.autoIdInput() || '');
|
|
762
819
|
// ==========================================
|
|
763
820
|
// 2. INJECT
|
|
764
821
|
// ==========================================
|
|
@@ -786,11 +843,11 @@ class SelectorActionComponent {
|
|
|
786
843
|
}
|
|
787
844
|
};
|
|
788
845
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SelectorActionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
789
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SelectorActionComponent, isStandalone: true, selector: "selector-action", inputs: { tableOption: { classPropertyName: "tableOption", publicName: "tableOption", isSignal: true, isRequired: false, transformFunction: null }, selectedTableItems: { classPropertyName: "selectedTableItems", publicName: "selectedTableItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clear: "clear" }, ngImport: i0, template: "@let _actions = selectedTableItems() | actionFilter: tableOption()?.selector?.actions;\n@let _selected = selectedTableItems();\n\n<sd-quick-action [opened]=\"opened()\">\n <div class=\"d-flex align-items-center\" sdMessage>\n <div class=\"c-bg-length\">\n <span class=\"c-length\">{{ _selected?.length }}</span>\n </div>\n <div class=\"c-message\">\n {{ message() }}\n </div>\n </div>\n\n <div class=\"d-flex align-items-center mr-8\" sdAction>\n @for (action of _actions; track $index) {\n @if (action.variant === 'normal') {\n <sd-button\n class=\"ml-4\"\n [tooltip]=\"action.tooltip\"\n [prefixIcon]=\"action.icon\"\n [color]=\"action.color\"\n [type]=\"action.type || 'light'\"\n [fontSet]=\"action.fontSet\"\n [title]=\"action.title\"\n (click)=\"onClickAction(action)\"></sd-button>\n } @else if (action.variant === 'children') {\n <sd-button\n class=\"ml-4\"\n [tooltip]=\"action.tooltip\"\n [prefixIcon]=\"action.icon || 'more_vert'\"\n [color]=\"action.color || 'secondary'\"\n [type]=\"action.type || 'light'\"\n [fontSet]=\"action.fontSet || 'material-icons-outlined'\"\n [title]=\"action.title\"\n [matMenuTriggerFor]=\"menu\">\n </sd-button>\n <mat-menu #menu=\"matMenu\">\n <span>\n @for (child of action.children; track $index) {\n <button
|
|
846
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SelectorActionComponent, isStandalone: true, selector: "selector-action", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, tableOption: { classPropertyName: "tableOption", publicName: "tableOption", isSignal: true, isRequired: false, transformFunction: null }, selectedTableItems: { classPropertyName: "selectedTableItems", publicName: "selectedTableItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clear: "clear" }, ngImport: i0, template: "@let _actions = selectedTableItems() | actionFilter: tableOption()?.selector?.actions;\n@let _selected = selectedTableItems();\n@let _autoId = autoId();\n\n<sd-quick-action [opened]=\"opened()\">\n <div class=\"d-flex align-items-center\" sdMessage>\n <div class=\"c-bg-length\">\n <span class=\"c-length\">{{ _selected?.length }}</span>\n </div>\n <div class=\"c-message\">\n {{ message() }}\n </div>\n </div>\n\n <div class=\"d-flex align-items-center mr-8\" sdAction>\n @for (action of _actions; track $index) {\n @if (action.variant === 'normal') {\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-action-' + $index : null\"\n class=\"ml-4\"\n [tooltip]=\"action.tooltip\"\n [prefixIcon]=\"action.icon\"\n [color]=\"action.color\"\n [type]=\"action.type || 'light'\"\n [fontSet]=\"action.fontSet\"\n [title]=\"action.title\"\n (click)=\"onClickAction(action)\"></sd-button>\n } @else if (action.variant === 'children') {\n @let _actionIndex = $index;\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-action-' + _actionIndex : null\"\n class=\"ml-4\"\n [tooltip]=\"action.tooltip\"\n [prefixIcon]=\"action.icon || 'more_vert'\"\n [color]=\"action.color || 'secondary'\"\n [type]=\"action.type || 'light'\"\n [fontSet]=\"action.fontSet || 'material-icons-outlined'\"\n [title]=\"action.title\"\n [matMenuTriggerFor]=\"menu\">\n </sd-button>\n <mat-menu #menu=\"matMenu\">\n <span>\n @for (child of action.children; track $index) {\n <button\n mat-menu-item\n [attr.data-autoid]=\"_autoId ? _autoId + '-action-' + _actionIndex + '-' + $index : null\"\n (click)=\"onClickAction(child)\">\n @if (child.icon) {\n <mat-icon [fontSet]=\"child.fontSet || 'material-icons-outlined'\" class=\"c-icon\">{{ child.icon }}</mat-icon>\n }\n <span> {{ child.title }}</span>\n </button>\n }\n </span>\n </mat-menu>\n }\n }\n <!-- N\u00FAt close lu\u00F4n hi\u1EC7n k\u1EC3 c\u1EA3 kh\u00F4ng c\u00F3 action \u2014 \u0111\u1EA3m b\u1EA3o user c\u00F3 l\u1ED1i tho\u00E1t clear selection -->\n <sd-button [autoId]=\"_autoId ? _autoId + '-clear-selection' : null\" class=\"ml-4\" prefixIcon=\"close\" color=\"secondary\" type=\"outline\" (click)=\"onClear()\" width=\"35px\"></sd-button>\n </div>\n</sd-quick-action>\n", styles: [".c-bg-length{display:flex;flex-direction:column;align-items:flex-start;padding:12px 8px;position:static;width:auto;min-width:48px;height:48px;left:0;top:0;background:var(--sd-primary);border-radius:4px 0 0 4px}.c-bg-length .c-length{position:static;width:auto;min-width:32px;height:24px;left:8px;top:12px;font-family:Roboto;font-style:normal;font-weight:500;font-size:16px;line-height:24px;text-align:center;color:#fff}.c-message{position:static;min-width:200px;height:20px;left:0;top:6px;font-family:Roboto;font-style:normal;font-weight:400;font-size:14px;line-height:20px;color:#000;margin:0 0 0 8px}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: SdQuickAction, selector: "sd-quick-action", inputs: ["opened"] }, { kind: "pipe", type: ActionFilterPipe, name: "actionFilter" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
790
847
|
}
|
|
791
848
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SelectorActionComponent, decorators: [{
|
|
792
849
|
type: Component,
|
|
793
|
-
args: [{ selector: 'selector-action', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatIconModule, MatMenuModule, SdButton, SdQuickAction, ActionFilterPipe], template: "@let _actions = selectedTableItems() | actionFilter: tableOption()?.selector?.actions;\n@let _selected = selectedTableItems();\n\n<sd-quick-action [opened]=\"opened()\">\n <div class=\"d-flex align-items-center\" sdMessage>\n <div class=\"c-bg-length\">\n <span class=\"c-length\">{{ _selected?.length }}</span>\n </div>\n <div class=\"c-message\">\n {{ message() }}\n </div>\n </div>\n\n <div class=\"d-flex align-items-center mr-8\" sdAction>\n @for (action of _actions; track $index) {\n @if (action.variant === 'normal') {\n <sd-button\n class=\"ml-4\"\n [tooltip]=\"action.tooltip\"\n [prefixIcon]=\"action.icon\"\n [color]=\"action.color\"\n [type]=\"action.type || 'light'\"\n [fontSet]=\"action.fontSet\"\n [title]=\"action.title\"\n (click)=\"onClickAction(action)\"></sd-button>\n } @else if (action.variant === 'children') {\n <sd-button\n class=\"ml-4\"\n [tooltip]=\"action.tooltip\"\n [prefixIcon]=\"action.icon || 'more_vert'\"\n [color]=\"action.color || 'secondary'\"\n [type]=\"action.type || 'light'\"\n [fontSet]=\"action.fontSet || 'material-icons-outlined'\"\n [title]=\"action.title\"\n [matMenuTriggerFor]=\"menu\">\n </sd-button>\n <mat-menu #menu=\"matMenu\">\n <span>\n @for (child of action.children; track $index) {\n <button
|
|
850
|
+
args: [{ selector: 'selector-action', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatIconModule, MatMenuModule, SdButton, SdQuickAction, ActionFilterPipe], template: "@let _actions = selectedTableItems() | actionFilter: tableOption()?.selector?.actions;\n@let _selected = selectedTableItems();\n@let _autoId = autoId();\n\n<sd-quick-action [opened]=\"opened()\">\n <div class=\"d-flex align-items-center\" sdMessage>\n <div class=\"c-bg-length\">\n <span class=\"c-length\">{{ _selected?.length }}</span>\n </div>\n <div class=\"c-message\">\n {{ message() }}\n </div>\n </div>\n\n <div class=\"d-flex align-items-center mr-8\" sdAction>\n @for (action of _actions; track $index) {\n @if (action.variant === 'normal') {\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-action-' + $index : null\"\n class=\"ml-4\"\n [tooltip]=\"action.tooltip\"\n [prefixIcon]=\"action.icon\"\n [color]=\"action.color\"\n [type]=\"action.type || 'light'\"\n [fontSet]=\"action.fontSet\"\n [title]=\"action.title\"\n (click)=\"onClickAction(action)\"></sd-button>\n } @else if (action.variant === 'children') {\n @let _actionIndex = $index;\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-action-' + _actionIndex : null\"\n class=\"ml-4\"\n [tooltip]=\"action.tooltip\"\n [prefixIcon]=\"action.icon || 'more_vert'\"\n [color]=\"action.color || 'secondary'\"\n [type]=\"action.type || 'light'\"\n [fontSet]=\"action.fontSet || 'material-icons-outlined'\"\n [title]=\"action.title\"\n [matMenuTriggerFor]=\"menu\">\n </sd-button>\n <mat-menu #menu=\"matMenu\">\n <span>\n @for (child of action.children; track $index) {\n <button\n mat-menu-item\n [attr.data-autoid]=\"_autoId ? _autoId + '-action-' + _actionIndex + '-' + $index : null\"\n (click)=\"onClickAction(child)\">\n @if (child.icon) {\n <mat-icon [fontSet]=\"child.fontSet || 'material-icons-outlined'\" class=\"c-icon\">{{ child.icon }}</mat-icon>\n }\n <span> {{ child.title }}</span>\n </button>\n }\n </span>\n </mat-menu>\n }\n }\n <!-- N\u00FAt close lu\u00F4n hi\u1EC7n k\u1EC3 c\u1EA3 kh\u00F4ng c\u00F3 action \u2014 \u0111\u1EA3m b\u1EA3o user c\u00F3 l\u1ED1i tho\u00E1t clear selection -->\n <sd-button [autoId]=\"_autoId ? _autoId + '-clear-selection' : null\" class=\"ml-4\" prefixIcon=\"close\" color=\"secondary\" type=\"outline\" (click)=\"onClear()\" width=\"35px\"></sd-button>\n </div>\n</sd-quick-action>\n", styles: [".c-bg-length{display:flex;flex-direction:column;align-items:flex-start;padding:12px 8px;position:static;width:auto;min-width:48px;height:48px;left:0;top:0;background:var(--sd-primary);border-radius:4px 0 0 4px}.c-bg-length .c-length{position:static;width:auto;min-width:32px;height:24px;left:8px;top:12px;font-family:Roboto;font-style:normal;font-weight:500;font-size:16px;line-height:24px;text-align:center;color:#fff}.c-message{position:static;min-width:200px;height:20px;left:0;top:6px;font-family:Roboto;font-style:normal;font-weight:400;font-size:14px;line-height:20px;color:#000;margin:0 0 0 8px}\n"] }]
|
|
794
851
|
}] });
|
|
795
852
|
|
|
796
853
|
const DEFAULT_TABLE_CONFIG = {
|
|
@@ -811,7 +868,6 @@ class ConfigService {
|
|
|
811
868
|
GROUP: 'sdGroup',
|
|
812
869
|
REORDER: 'sdReorder',
|
|
813
870
|
INDEX: 'sdIndex',
|
|
814
|
-
TREE_TOGGLE: 'sdTreeToggle',
|
|
815
871
|
FILLER: 'sdFiller',
|
|
816
872
|
};
|
|
817
873
|
#prefix = 'TABLE_CONFIG';
|
|
@@ -863,10 +919,8 @@ class ConfigService {
|
|
|
863
919
|
result.firstHeaders.push(this.#COLUMNS.SELECTION);
|
|
864
920
|
result.displayedColumns.push(this.#COLUMNS.SELECTION);
|
|
865
921
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
result.displayedColumns.push(this.#COLUMNS.TREE_TOGGLE);
|
|
869
|
-
}
|
|
922
|
+
// why: tree toggle KHÔNG còn là cột riêng — icon expand được nhúng vào cột
|
|
923
|
+
// đầu tiên (cột Index nếu có, ngược lại là cột data đầu). Xem table.component.html.
|
|
870
924
|
// 👇 Chỉ push vào đầu nếu là left (mặc định)
|
|
871
925
|
if (commands?.length && !isCommandRight) {
|
|
872
926
|
result.firstHeaders.push(this.#COLUMNS.COMMAND);
|
|
@@ -959,7 +1013,7 @@ class ConfigService {
|
|
|
959
1013
|
}
|
|
960
1014
|
result.multipleHeader = result.secondHeaders.length > 0;
|
|
961
1015
|
// Sub infomation không thể có footer; filler cũng không cần footer cell.
|
|
962
|
-
result.displayedFooters = result.displayedColumns.filter(val => val !== this.#COLUMNS.SUBINFORMATION && val !== this.#COLUMNS.
|
|
1016
|
+
result.displayedFooters = result.displayedColumns.filter(val => val !== this.#COLUMNS.SUBINFORMATION && val !== this.#COLUMNS.FILLER);
|
|
963
1017
|
if (option.filler?.enabled) {
|
|
964
1018
|
// Footer cũng cần filler cuối để giữ width đồng bộ với data row
|
|
965
1019
|
result.displayedFooters.push(this.#COLUMNS.FILLER);
|
|
@@ -1030,8 +1084,11 @@ class ConfigComponent {
|
|
|
1030
1084
|
// ==========================================
|
|
1031
1085
|
// 1. SIGNAL INPUTS / OUTPUTS
|
|
1032
1086
|
// ==========================================
|
|
1087
|
+
autoIdInput = input(undefined, { alias: 'autoId' });
|
|
1033
1088
|
tableOption = input(undefined);
|
|
1034
1089
|
changes = output();
|
|
1090
|
+
// Base autoId (đã là `components-table-<scope>` từ parent), '' khi parent không set.
|
|
1091
|
+
autoId = computed(() => this.autoIdInput() || '');
|
|
1035
1092
|
// ==========================================
|
|
1036
1093
|
// 2. SIGNAL QUERIES
|
|
1037
1094
|
// ==========================================
|
|
@@ -1103,7 +1160,7 @@ class ConfigComponent {
|
|
|
1103
1160
|
this.dragDisabled = true;
|
|
1104
1161
|
}
|
|
1105
1162
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ConfigComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1106
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: ConfigComponent, isStandalone: true, selector: "config", inputs: { tableOption: { classPropertyName: "tableOption", publicName: "tableOption", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changes: "changes" }, providers: [ConfigService], viewQueries: [{ propertyName: "modal", first: true, predicate: SdModal, descendants: true, isSignal: true }, { propertyName: "table", first: true, predicate: MatTable, descendants: true, isSignal: true }], ngImport: i0, template: "@let _configuration = configuration();\n\n<sd-modal width=\"sm\" [title]=\"'core.component.table.setup' | translate\" #modal>\n @if (_configuration) {\n <div class=\"c-container\">\n <div class=\"c-table\" style=\"max-height: 70vh\">\n <table\n mat-table\n [dataSource]=\"_configuration.columns!\"\n cdkDropList\n [cdkDropListData]=\"_configuration.columns\"\n (cdkDropListDropped)=\"dropTable($event)\">\n <ng-container matColumnDef=\"invisible\">\n <th mat-header-cell class=\"c-th text-center px-4\" *matHeaderCellDef style=\"width: 96px\">\n {{ 'core.component.form-builder.display' | translate }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item\">\n <div class=\"d-flex\" style=\"gap: 8px\">\n <div class=\"c-handle\" (mousedown)=\"handleMouseDown()\" (mouseup)=\"handleMouseUp()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 0 24 24\" width=\"24px\" fill=\"#5f6368\">\n <path d=\"M0 0h24v24H0V0z\" fill=\"none\" />\n <path\n d=\"M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z\" />\n </svg>\n </div>\n <sd-switch [model]=\"!item.invisible\" (modelChange)=\"item.invisible = !item.invisible\" />\n </div>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"title\">\n <th mat-header-cell class=\"c-th px-4\" *matHeaderCellDef>\n {{ 'core.component.table.column-header' | translate }}\n </th>\n <td class=\"px-4\" mat-cell *matCellDef=\"let item\">\n <sd-input class=\"pb-4\" [placeholder]=\"item?.origin?.title\" [(model)]=\"item.title\" hideInlineError></sd-input>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"width\">\n <th mat-header-cell class=\"c-th px-4\" *matHeaderCellDef style=\"width: 80px\">\n {{ 'core.component.table.width' | translate }}\n </th>\n <td class=\"px-4\" mat-cell *matCellDef=\"let item\">\n <sd-input class=\"pb-4\" [placeholder]=\"item?.origin?.width\" [(model)]=\"item.width\" hideInlineError></sd-input>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"fixed\">\n <th mat-header-cell class=\"c-th text-center px-4\" *matHeaderCellDef style=\"width: 80px\">\n {{ 'core.component.table.fixed' | translate }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item\">\n <sd-switch [(model)]=\"item.fixed\" />\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"truncate\">\n <th mat-header-cell class=\"c-th text-center px-4\" *matHeaderCellDef style=\"width: 100px\">\n {{ 'core.component.table.char-limit' | translate }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item\">\n <sd-switch [(model)]=\"item.truncate\" />\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"['invisible', 'title', 'width', 'fixed', 'truncate']; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: ['invisible', 'title', 'width', 'fixed', 'truncate']\"\n cdkDrag\n [cdkDragData]=\"row\"\n cdkDragLockAxis=\"y\"\n [cdkDragDisabled]=\"dragDisabled\">\n </tr>\n </table>\n </div>\n </div>\n }\n <ng-container sdFooter>\n <sd-button class=\"mr-8\" (click)=\"close()\" [title]=\"'core.component.table.skip' | translate\" color=\"secondary\"> </sd-button>\n <sd-button class=\"mr-8\" (click)=\"onReset()\" [title]=\"'core.component.form-builder.default' | translate\" color=\"primary\"> </sd-button>\n <sd-button (click)=\"onSave()\" [title]=\"'core.component.table.apply' | translate\" type=\"fill\" color=\"primary\"> </sd-button>\n </ng-container>\n</sd-modal>\n", styles: [".c-table{position:relative;overflow:auto}.c-table table{width:100%}.c-table .c-th{font-weight:500;font-size:14px;line-height:20px;color:#000}.c-container{position:relative;min-height:200px}.c-handle{color:#ccc;cursor:move}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "ngmodule", type: CdkTableModule }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i11.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i11.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdModal, selector: "sd-modal", inputs: ["autoId", "title", "color", "width", "height", "view", "modalClass", "lazyLoadContent", "hideClose", "disableBackdropClose"], outputs: ["sdClosed"] }, { kind: "component", type: SdSwitch, selector: "sd-switch", inputs: ["autoId", "name", "form", "label", "color", "disabled", "viewed", "hideInlineError", "required", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
1163
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: ConfigComponent, isStandalone: true, selector: "config", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, tableOption: { classPropertyName: "tableOption", publicName: "tableOption", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changes: "changes" }, providers: [ConfigService], viewQueries: [{ propertyName: "modal", first: true, predicate: SdModal, descendants: true, isSignal: true }, { propertyName: "table", first: true, predicate: MatTable, descendants: true, isSignal: true }], ngImport: i0, template: "@let _configuration = configuration();\n@let _autoId = autoId();\n\n<sd-modal width=\"sm\" [title]=\"'core.component.table.setup' | translate\" #modal>\n @if (_configuration) {\n <div class=\"c-container\">\n <div class=\"c-table\" style=\"max-height: 70vh\">\n <table\n mat-table\n [dataSource]=\"_configuration.columns!\"\n cdkDropList\n [cdkDropListData]=\"_configuration.columns\"\n (cdkDropListDropped)=\"dropTable($event)\">\n <ng-container matColumnDef=\"invisible\">\n <th mat-header-cell class=\"c-th text-center px-4\" *matHeaderCellDef style=\"width: 96px\">\n {{ 'core.component.form-builder.display' | translate }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item\">\n <div class=\"d-flex\" style=\"gap: 8px\">\n <div class=\"c-handle\" (mousedown)=\"handleMouseDown()\" (mouseup)=\"handleMouseUp()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 0 24 24\" width=\"24px\" fill=\"#5f6368\">\n <path d=\"M0 0h24v24H0V0z\" fill=\"none\" />\n <path\n d=\"M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z\" />\n </svg>\n </div>\n <sd-switch [model]=\"!item.invisible\" (modelChange)=\"item.invisible = !item.invisible\" />\n </div>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"title\">\n <th mat-header-cell class=\"c-th px-4\" *matHeaderCellDef>\n {{ 'core.component.table.column-header' | translate }}\n </th>\n <td class=\"px-4\" mat-cell *matCellDef=\"let item\">\n <sd-input class=\"pb-4\" [placeholder]=\"item?.origin?.title\" [(model)]=\"item.title\" hideInlineError></sd-input>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"width\">\n <th mat-header-cell class=\"c-th px-4\" *matHeaderCellDef style=\"width: 80px\">\n {{ 'core.component.table.width' | translate }}\n </th>\n <td class=\"px-4\" mat-cell *matCellDef=\"let item\">\n <sd-input class=\"pb-4\" [placeholder]=\"item?.origin?.width\" [(model)]=\"item.width\" hideInlineError></sd-input>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"fixed\">\n <th mat-header-cell class=\"c-th text-center px-4\" *matHeaderCellDef style=\"width: 80px\">\n {{ 'core.component.table.fixed' | translate }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item\">\n <sd-switch [(model)]=\"item.fixed\" />\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"truncate\">\n <th mat-header-cell class=\"c-th text-center px-4\" *matHeaderCellDef style=\"width: 100px\">\n {{ 'core.component.table.char-limit' | translate }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item\">\n <sd-switch [(model)]=\"item.truncate\" />\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"['invisible', 'title', 'width', 'fixed', 'truncate']; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: ['invisible', 'title', 'width', 'fixed', 'truncate']\"\n cdkDrag\n [cdkDragData]=\"row\"\n cdkDragLockAxis=\"y\"\n [cdkDragDisabled]=\"dragDisabled\">\n </tr>\n </table>\n </div>\n </div>\n }\n <ng-container sdFooter>\n <sd-button [autoId]=\"_autoId ? _autoId + '-config-skip' : null\" class=\"mr-8\" (click)=\"close()\" [title]=\"'core.component.table.skip' | translate\" color=\"secondary\"> </sd-button>\n <sd-button [autoId]=\"_autoId ? _autoId + '-config-reset' : null\" class=\"mr-8\" (click)=\"onReset()\" [title]=\"'core.component.form-builder.default' | translate\" color=\"primary\"> </sd-button>\n <sd-button [autoId]=\"_autoId ? _autoId + '-config-apply' : null\" (click)=\"onSave()\" [title]=\"'core.component.table.apply' | translate\" type=\"fill\" color=\"primary\"> </sd-button>\n </ng-container>\n</sd-modal>\n", styles: [".c-table{position:relative;overflow:auto}.c-table table{width:100%}.c-table .c-th{font-weight:500;font-size:14px;line-height:20px;color:#000}.c-container{position:relative;min-height:200px}.c-handle{color:#ccc;cursor:move}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "ngmodule", type: CdkTableModule }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i11.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i11.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "hideInlineError", "blurOnEnter", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdModal, selector: "sd-modal", inputs: ["autoId", "title", "color", "width", "height", "view", "modalClass", "lazyLoadContent", "hideClose", "disableBackdropClose"], outputs: ["sdClosed"] }, { kind: "component", type: SdSwitch, selector: "sd-switch", inputs: ["autoId", "name", "form", "label", "color", "disabled", "viewed", "hideInlineError", "required", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
1107
1164
|
}
|
|
1108
1165
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ConfigComponent, decorators: [{
|
|
1109
1166
|
type: Component,
|
|
@@ -1119,7 +1176,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
1119
1176
|
SdModal,
|
|
1120
1177
|
SdSwitch,
|
|
1121
1178
|
TranslatePipe,
|
|
1122
|
-
], providers: [ConfigService], template: "@let _configuration = configuration();\n\n<sd-modal width=\"sm\" [title]=\"'core.component.table.setup' | translate\" #modal>\n @if (_configuration) {\n <div class=\"c-container\">\n <div class=\"c-table\" style=\"max-height: 70vh\">\n <table\n mat-table\n [dataSource]=\"_configuration.columns!\"\n cdkDropList\n [cdkDropListData]=\"_configuration.columns\"\n (cdkDropListDropped)=\"dropTable($event)\">\n <ng-container matColumnDef=\"invisible\">\n <th mat-header-cell class=\"c-th text-center px-4\" *matHeaderCellDef style=\"width: 96px\">\n {{ 'core.component.form-builder.display' | translate }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item\">\n <div class=\"d-flex\" style=\"gap: 8px\">\n <div class=\"c-handle\" (mousedown)=\"handleMouseDown()\" (mouseup)=\"handleMouseUp()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 0 24 24\" width=\"24px\" fill=\"#5f6368\">\n <path d=\"M0 0h24v24H0V0z\" fill=\"none\" />\n <path\n d=\"M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z\" />\n </svg>\n </div>\n <sd-switch [model]=\"!item.invisible\" (modelChange)=\"item.invisible = !item.invisible\" />\n </div>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"title\">\n <th mat-header-cell class=\"c-th px-4\" *matHeaderCellDef>\n {{ 'core.component.table.column-header' | translate }}\n </th>\n <td class=\"px-4\" mat-cell *matCellDef=\"let item\">\n <sd-input class=\"pb-4\" [placeholder]=\"item?.origin?.title\" [(model)]=\"item.title\" hideInlineError></sd-input>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"width\">\n <th mat-header-cell class=\"c-th px-4\" *matHeaderCellDef style=\"width: 80px\">\n {{ 'core.component.table.width' | translate }}\n </th>\n <td class=\"px-4\" mat-cell *matCellDef=\"let item\">\n <sd-input class=\"pb-4\" [placeholder]=\"item?.origin?.width\" [(model)]=\"item.width\" hideInlineError></sd-input>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"fixed\">\n <th mat-header-cell class=\"c-th text-center px-4\" *matHeaderCellDef style=\"width: 80px\">\n {{ 'core.component.table.fixed' | translate }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item\">\n <sd-switch [(model)]=\"item.fixed\" />\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"truncate\">\n <th mat-header-cell class=\"c-th text-center px-4\" *matHeaderCellDef style=\"width: 100px\">\n {{ 'core.component.table.char-limit' | translate }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item\">\n <sd-switch [(model)]=\"item.truncate\" />\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"['invisible', 'title', 'width', 'fixed', 'truncate']; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: ['invisible', 'title', 'width', 'fixed', 'truncate']\"\n cdkDrag\n [cdkDragData]=\"row\"\n cdkDragLockAxis=\"y\"\n [cdkDragDisabled]=\"dragDisabled\">\n </tr>\n </table>\n </div>\n </div>\n }\n <ng-container sdFooter>\n <sd-button class=\"mr-8\" (click)=\"close()\" [title]=\"'core.component.table.skip' | translate\" color=\"secondary\"> </sd-button>\n <sd-button class=\"mr-8\" (click)=\"onReset()\" [title]=\"'core.component.form-builder.default' | translate\" color=\"primary\"> </sd-button>\n <sd-button (click)=\"onSave()\" [title]=\"'core.component.table.apply' | translate\" type=\"fill\" color=\"primary\"> </sd-button>\n </ng-container>\n</sd-modal>\n", styles: [".c-table{position:relative;overflow:auto}.c-table table{width:100%}.c-table .c-th{font-weight:500;font-size:14px;line-height:20px;color:#000}.c-container{position:relative;min-height:200px}.c-handle{color:#ccc;cursor:move}\n"] }]
|
|
1179
|
+
], providers: [ConfigService], template: "@let _configuration = configuration();\n@let _autoId = autoId();\n\n<sd-modal width=\"sm\" [title]=\"'core.component.table.setup' | translate\" #modal>\n @if (_configuration) {\n <div class=\"c-container\">\n <div class=\"c-table\" style=\"max-height: 70vh\">\n <table\n mat-table\n [dataSource]=\"_configuration.columns!\"\n cdkDropList\n [cdkDropListData]=\"_configuration.columns\"\n (cdkDropListDropped)=\"dropTable($event)\">\n <ng-container matColumnDef=\"invisible\">\n <th mat-header-cell class=\"c-th text-center px-4\" *matHeaderCellDef style=\"width: 96px\">\n {{ 'core.component.form-builder.display' | translate }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item\">\n <div class=\"d-flex\" style=\"gap: 8px\">\n <div class=\"c-handle\" (mousedown)=\"handleMouseDown()\" (mouseup)=\"handleMouseUp()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 0 24 24\" width=\"24px\" fill=\"#5f6368\">\n <path d=\"M0 0h24v24H0V0z\" fill=\"none\" />\n <path\n d=\"M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z\" />\n </svg>\n </div>\n <sd-switch [model]=\"!item.invisible\" (modelChange)=\"item.invisible = !item.invisible\" />\n </div>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"title\">\n <th mat-header-cell class=\"c-th px-4\" *matHeaderCellDef>\n {{ 'core.component.table.column-header' | translate }}\n </th>\n <td class=\"px-4\" mat-cell *matCellDef=\"let item\">\n <sd-input class=\"pb-4\" [placeholder]=\"item?.origin?.title\" [(model)]=\"item.title\" hideInlineError></sd-input>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"width\">\n <th mat-header-cell class=\"c-th px-4\" *matHeaderCellDef style=\"width: 80px\">\n {{ 'core.component.table.width' | translate }}\n </th>\n <td class=\"px-4\" mat-cell *matCellDef=\"let item\">\n <sd-input class=\"pb-4\" [placeholder]=\"item?.origin?.width\" [(model)]=\"item.width\" hideInlineError></sd-input>\n </td>\n </ng-container>\n <ng-container matColumnDef=\"fixed\">\n <th mat-header-cell class=\"c-th text-center px-4\" *matHeaderCellDef style=\"width: 80px\">\n {{ 'core.component.table.fixed' | translate }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item\">\n <sd-switch [(model)]=\"item.fixed\" />\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"truncate\">\n <th mat-header-cell class=\"c-th text-center px-4\" *matHeaderCellDef style=\"width: 100px\">\n {{ 'core.component.table.char-limit' | translate }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item\">\n <sd-switch [(model)]=\"item.truncate\" />\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"['invisible', 'title', 'width', 'fixed', 'truncate']; sticky: true\"></tr>\n <tr\n mat-row\n *matRowDef=\"let row; columns: ['invisible', 'title', 'width', 'fixed', 'truncate']\"\n cdkDrag\n [cdkDragData]=\"row\"\n cdkDragLockAxis=\"y\"\n [cdkDragDisabled]=\"dragDisabled\">\n </tr>\n </table>\n </div>\n </div>\n }\n <ng-container sdFooter>\n <sd-button [autoId]=\"_autoId ? _autoId + '-config-skip' : null\" class=\"mr-8\" (click)=\"close()\" [title]=\"'core.component.table.skip' | translate\" color=\"secondary\"> </sd-button>\n <sd-button [autoId]=\"_autoId ? _autoId + '-config-reset' : null\" class=\"mr-8\" (click)=\"onReset()\" [title]=\"'core.component.form-builder.default' | translate\" color=\"primary\"> </sd-button>\n <sd-button [autoId]=\"_autoId ? _autoId + '-config-apply' : null\" (click)=\"onSave()\" [title]=\"'core.component.table.apply' | translate\" type=\"fill\" color=\"primary\"> </sd-button>\n </ng-container>\n</sd-modal>\n", styles: [".c-table{position:relative;overflow:auto}.c-table table{width:100%}.c-table .c-th{font-weight:500;font-size:14px;line-height:20px;color:#000}.c-container{position:relative;min-height:200px}.c-handle{color:#ccc;cursor:move}\n"] }]
|
|
1123
1180
|
}], ctorParameters: () => [] });
|
|
1124
1181
|
|
|
1125
1182
|
class ToStringPipe {
|
|
@@ -1175,11 +1232,11 @@ class ViewComponent {
|
|
|
1175
1232
|
this.isCollapsed.update(current => !current);
|
|
1176
1233
|
};
|
|
1177
1234
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1178
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: ViewComponent, isStandalone: true, selector: "view", inputs: { autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "contentContainer", first: true, predicate: ["contentContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "@let _column = column();\n@let _item = item();\n@let _autoId = autoId();\n\n@let view = _item.meta.display[_column.field];\n@let _truncateEnable = truncateEnable();\n@let _truncateWidth = truncateWidth();\n@let isExpandByMore = isOverflowing() && _column?.cell?.truncate?.type === 'more';\n@let isExpandByTooltip = isOverflowing() && _column?.cell?.truncate?.type === 'tooltip';\n\n@if (view) {\n @if (view.isHtml) {\n @if (view.click) {\n <div\n #contentContainer\n [attr.data-autoId]=\"
|
|
1235
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: ViewComponent, isStandalone: true, selector: "view", inputs: { autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "contentContainer", first: true, predicate: ["contentContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "@let _column = column();\n@let _item = item();\n@let _autoId = autoId();\n\n@let view = _item.meta.display[_column.field];\n@let _truncateEnable = truncateEnable();\n@let _truncateWidth = truncateWidth();\n@let _isCollapsed = isCollapsed();\n@let isExpandByMore = isOverflowing() && _column?.cell?.truncate?.type === 'more';\n@let isExpandByTooltip = isOverflowing() && _column?.cell?.truncate?.type === 'tooltip';\n\n@if (view) {\n @if (view.isHtml) {\n @if (view.click) {\n <div\n #contentContainer\n [attr.data-autoId]=\"_autoId\"\n (click)=\"view.click()\"\n class=\"text-break cursor-pointer\"\n [sdTooltip]=\"view.tooltip || (isExpandByTooltip ? htmlClickTooltip : '')\"\n [style]=\"view.cellStyle\"\n [style.max-width]=\"_truncateWidth\"\n [class.c-ellipsis-html]=\"_truncateEnable\"\n [innerHTML]=\"view.data | sdSafeHtml\"\n aria-hidden=\"true\"></div>\n\n <ng-template #htmlClickTooltip>\n <div\n class=\"c-tooltip-inner-content\"\n [style.max-width]=\"'calc(' + _truncateWidth + ' * 1.5)'\"\n style=\"max-height: 200px; overflow-y: auto; scrollbar-width: thin\">\n <div [innerHTML]=\"view.data | sdSafeHtml\"></div>\n </div>\n </ng-template>\n } @else {\n <!-- N\u1EBFu column c\u00F3 b\u1EADt \"gi\u1EDBi h\u1EA1n k\u00FD t\u1EF1\" -->\n @if (_truncateEnable) {\n <div\n #contentContainer\n [style.max-width]=\"_truncateWidth\"\n [class.c-ellipsis-html]=\"_isCollapsed\"\n [attr.data-autoId]=\"_autoId\"\n class=\"text-break\"\n [sdTooltip]=\"view.tooltip || (isExpandByTooltip ? htmlTooltip : '')\"\n containerClass=\"c-tooltip-html-d374bd93-f136-425c-bcc2-2d88cb163e44\"\n container=\"body\"\n placement=\"bottom\"\n [sdTooltipDelay]=\"100\"\n [style]=\"view.cellStyle\"\n [innerHTML]=\"view.data | sdSafeHtml\"></div>\n\n <ng-template #htmlTooltip>\n <div\n class=\"c-tooltip-inner-content\"\n [style.max-width]=\"'calc(' + _truncateWidth + ' * 1.5)'\"\n style=\"max-height: 200px; overflow-y: auto; scrollbar-width: thin\">\n <div [innerHTML]=\"view.data | sdSafeHtml\"></div>\n </div>\n </ng-template>\n\n @if (isExpandByMore) {\n <button (click)=\"toggle()\" class=\"c-collapse\">{{ _isCollapsed ? ('core.component.form-builder.show-more' | translate) : ('core.component.form-builder.show-less' | translate) }}</button>\n }\n } @else {\n <div\n [attr.data-autoId]=\"_autoId\"\n class=\"text-break\"\n [sdTooltip]=\"view.tooltip || ''\"\n [style]=\"view.cellStyle\"\n [innerHTML]=\"view.data | sdSafeHtml\"></div>\n }\n <!-- End -->\n }\n } @else {\n <!-- N\u1EBFu l\u00E0 gi\u00E1 tr\u1ECB l\u1EA5y hi\u1EC3n th\u1ECB l\u1EA5y tr\u1EF1c ti\u1EBFp t\u1EEB field -->\n @if (view.badge) {\n @if (view.data) {\n <sd-badge\n [attr.data-autoId]=\"_autoId\"\n [type]=\"view.badge.type\"\n [title]=\"view.data\"\n [color]=\"view.badge.color\"\n [icon]=\"view.badge.icon\"\n [sdTooltip]=\"view.tooltip || ''\"\n (click)=\"!!view.click && view.click()\">\n </sd-badge>\n }\n } @else {\n @if (view.click) {\n <div\n #contentContainer\n class=\"text-break\"\n [style]=\"view.cellStyle\"\n [style.max-width]=\"_truncateWidth\"\n [class.c-ellipsis]=\"_truncateEnable\"\n [sdTooltip]=\"view.tooltip || (isExpandByTooltip ? view.data : '') | asString\">\n <a [attr.data-autoId]=\"_autoId\" href=\"javascript:;\" (click)=\"view.click()\">\n {{ view.data }}\n </a>\n </div>\n } @else {\n <!-- N\u1EBFu column c\u00F3 b\u1EADt \"gi\u1EDBi h\u1EA1n k\u00FD t\u1EF1\" -->\n @if (_truncateEnable) {\n <div\n #contentContainer\n [attr.data-autoId]=\"_autoId\"\n class=\"text-break\"\n [style]=\"view.cellStyle\"\n [style.max-width]=\"_truncateWidth\"\n [class.c-ellipsis]=\"_isCollapsed\"\n [sdTooltip]=\"view.tooltip || (isExpandByTooltip ? view.data : '') | asString\">\n {{ view.data }}\n </div>\n\n <!-- N\u1EBFu option xem full th\u00F4ng tin l\u00E0 \"btn xem th\u00EAm\" -->\n @if (isExpandByMore) {\n <button (click)=\"toggle()\" class=\"c-collapse\">{{ _isCollapsed ? ('core.component.form-builder.show-more' | translate) : ('core.component.form-builder.show-less' | translate) }}</button>\n }\n } @else {\n <div [attr.data-autoId]=\"_autoId\" class=\"text-break\" [style]=\"view.cellStyle\" [sdTooltip]=\"view.tooltip || ''\">\n {{ view.data }}\n </div>\n }\n <!-- End -->\n }\n }\n }\n}\n", styles: [".c-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.c-ellipsis-html{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;line-clamp:1;text-overflow:ellipsis;overflow:hidden}.c-collapse{background:none;color:#00e;border:none;padding:0;font-size:12px;cursor:pointer;outline:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: SdBadge, selector: "sd-badge", inputs: ["type", "color", "primary", "secondary", "success", "info", "warning", "error", "fontSet", "title", "description", "tooltip", "icon", "size"], outputs: ["click"] }, { kind: "pipe", type: SdSafeHtmlPipe, name: "sdSafeHtml" }, { kind: "directive", type: SdTooltipDirective, selector: "[sdTooltip]", inputs: ["sdTooltip", "sdTooltipPosition", "sdTooltipDelay", "sdTooltipColor"] }, { kind: "pipe", type: ToStringPipe, name: "asString" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1179
1236
|
}
|
|
1180
1237
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ViewComponent, decorators: [{
|
|
1181
1238
|
type: Component,
|
|
1182
|
-
args: [{ selector: 'view', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, SdBadge, SdSafeHtmlPipe, SdTooltipDirective, ToStringPipe, TranslatePipe], template: "@let _column = column();\n@let _item = item();\n@let _autoId = autoId();\n\n@let view = _item.meta.display[_column.field];\n@let _truncateEnable = truncateEnable();\n@let _truncateWidth = truncateWidth();\n@let isExpandByMore = isOverflowing() && _column?.cell?.truncate?.type === 'more';\n@let isExpandByTooltip = isOverflowing() && _column?.cell?.truncate?.type === 'tooltip';\n\n@if (view) {\n @if (view.isHtml) {\n @if (view.click) {\n <div\n #contentContainer\n [attr.data-autoId]=\"
|
|
1239
|
+
args: [{ selector: 'view', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, SdBadge, SdSafeHtmlPipe, SdTooltipDirective, ToStringPipe, TranslatePipe], template: "@let _column = column();\n@let _item = item();\n@let _autoId = autoId();\n\n@let view = _item.meta.display[_column.field];\n@let _truncateEnable = truncateEnable();\n@let _truncateWidth = truncateWidth();\n@let _isCollapsed = isCollapsed();\n@let isExpandByMore = isOverflowing() && _column?.cell?.truncate?.type === 'more';\n@let isExpandByTooltip = isOverflowing() && _column?.cell?.truncate?.type === 'tooltip';\n\n@if (view) {\n @if (view.isHtml) {\n @if (view.click) {\n <div\n #contentContainer\n [attr.data-autoId]=\"_autoId\"\n (click)=\"view.click()\"\n class=\"text-break cursor-pointer\"\n [sdTooltip]=\"view.tooltip || (isExpandByTooltip ? htmlClickTooltip : '')\"\n [style]=\"view.cellStyle\"\n [style.max-width]=\"_truncateWidth\"\n [class.c-ellipsis-html]=\"_truncateEnable\"\n [innerHTML]=\"view.data | sdSafeHtml\"\n aria-hidden=\"true\"></div>\n\n <ng-template #htmlClickTooltip>\n <div\n class=\"c-tooltip-inner-content\"\n [style.max-width]=\"'calc(' + _truncateWidth + ' * 1.5)'\"\n style=\"max-height: 200px; overflow-y: auto; scrollbar-width: thin\">\n <div [innerHTML]=\"view.data | sdSafeHtml\"></div>\n </div>\n </ng-template>\n } @else {\n <!-- N\u1EBFu column c\u00F3 b\u1EADt \"gi\u1EDBi h\u1EA1n k\u00FD t\u1EF1\" -->\n @if (_truncateEnable) {\n <div\n #contentContainer\n [style.max-width]=\"_truncateWidth\"\n [class.c-ellipsis-html]=\"_isCollapsed\"\n [attr.data-autoId]=\"_autoId\"\n class=\"text-break\"\n [sdTooltip]=\"view.tooltip || (isExpandByTooltip ? htmlTooltip : '')\"\n containerClass=\"c-tooltip-html-d374bd93-f136-425c-bcc2-2d88cb163e44\"\n container=\"body\"\n placement=\"bottom\"\n [sdTooltipDelay]=\"100\"\n [style]=\"view.cellStyle\"\n [innerHTML]=\"view.data | sdSafeHtml\"></div>\n\n <ng-template #htmlTooltip>\n <div\n class=\"c-tooltip-inner-content\"\n [style.max-width]=\"'calc(' + _truncateWidth + ' * 1.5)'\"\n style=\"max-height: 200px; overflow-y: auto; scrollbar-width: thin\">\n <div [innerHTML]=\"view.data | sdSafeHtml\"></div>\n </div>\n </ng-template>\n\n @if (isExpandByMore) {\n <button (click)=\"toggle()\" class=\"c-collapse\">{{ _isCollapsed ? ('core.component.form-builder.show-more' | translate) : ('core.component.form-builder.show-less' | translate) }}</button>\n }\n } @else {\n <div\n [attr.data-autoId]=\"_autoId\"\n class=\"text-break\"\n [sdTooltip]=\"view.tooltip || ''\"\n [style]=\"view.cellStyle\"\n [innerHTML]=\"view.data | sdSafeHtml\"></div>\n }\n <!-- End -->\n }\n } @else {\n <!-- N\u1EBFu l\u00E0 gi\u00E1 tr\u1ECB l\u1EA5y hi\u1EC3n th\u1ECB l\u1EA5y tr\u1EF1c ti\u1EBFp t\u1EEB field -->\n @if (view.badge) {\n @if (view.data) {\n <sd-badge\n [attr.data-autoId]=\"_autoId\"\n [type]=\"view.badge.type\"\n [title]=\"view.data\"\n [color]=\"view.badge.color\"\n [icon]=\"view.badge.icon\"\n [sdTooltip]=\"view.tooltip || ''\"\n (click)=\"!!view.click && view.click()\">\n </sd-badge>\n }\n } @else {\n @if (view.click) {\n <div\n #contentContainer\n class=\"text-break\"\n [style]=\"view.cellStyle\"\n [style.max-width]=\"_truncateWidth\"\n [class.c-ellipsis]=\"_truncateEnable\"\n [sdTooltip]=\"view.tooltip || (isExpandByTooltip ? view.data : '') | asString\">\n <a [attr.data-autoId]=\"_autoId\" href=\"javascript:;\" (click)=\"view.click()\">\n {{ view.data }}\n </a>\n </div>\n } @else {\n <!-- N\u1EBFu column c\u00F3 b\u1EADt \"gi\u1EDBi h\u1EA1n k\u00FD t\u1EF1\" -->\n @if (_truncateEnable) {\n <div\n #contentContainer\n [attr.data-autoId]=\"_autoId\"\n class=\"text-break\"\n [style]=\"view.cellStyle\"\n [style.max-width]=\"_truncateWidth\"\n [class.c-ellipsis]=\"_isCollapsed\"\n [sdTooltip]=\"view.tooltip || (isExpandByTooltip ? view.data : '') | asString\">\n {{ view.data }}\n </div>\n\n <!-- N\u1EBFu option xem full th\u00F4ng tin l\u00E0 \"btn xem th\u00EAm\" -->\n @if (isExpandByMore) {\n <button (click)=\"toggle()\" class=\"c-collapse\">{{ _isCollapsed ? ('core.component.form-builder.show-more' | translate) : ('core.component.form-builder.show-less' | translate) }}</button>\n }\n } @else {\n <div [attr.data-autoId]=\"_autoId\" class=\"text-break\" [style]=\"view.cellStyle\" [sdTooltip]=\"view.tooltip || ''\">\n {{ view.data }}\n </div>\n }\n <!-- End -->\n }\n }\n }\n}\n", styles: [".c-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.c-ellipsis-html{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;line-clamp:1;text-overflow:ellipsis;overflow:hidden}.c-collapse{background:none;color:#00e;border:none;padding:0;font-size:12px;cursor:pointer;outline:none}\n"] }]
|
|
1183
1240
|
}], ctorParameters: () => [] });
|
|
1184
1241
|
|
|
1185
1242
|
class DesktopCellComponent {
|
|
@@ -3041,6 +3098,13 @@ class SdTable extends SdBaseSecureComponent {
|
|
|
3041
3098
|
#itemIndexMap = new WeakMap();
|
|
3042
3099
|
#treeExpandState = new Map();
|
|
3043
3100
|
treeRevision = signal(0);
|
|
3101
|
+
// Search ở cấp con (static tree + type 'local'): predicate khớp 1 dòng theo
|
|
3102
|
+
// column filter hiện hành. Set ở #filterLocal khi có filter active, đọc lại ở
|
|
3103
|
+
// #render/#initTreeMeta/#ensureChildItemsFormatted để prune + auto-expand các
|
|
3104
|
+
// nhánh có node con khớp. undefined = không search → cây render bình thường.
|
|
3105
|
+
#treeSearchPredicate;
|
|
3106
|
+
// Lần render trước có đang ở chế độ search hay không (để xử lý chuyển trạng thái).
|
|
3107
|
+
#treeSearchActive = false;
|
|
3044
3108
|
// 1. Private Services
|
|
3045
3109
|
#ref = inject(ChangeDetectorRef);
|
|
3046
3110
|
#configService = inject(ConfigService);
|
|
@@ -3220,99 +3284,119 @@ class SdTable extends SdBaseSecureComponent {
|
|
|
3220
3284
|
}
|
|
3221
3285
|
Object.assign(cur, next);
|
|
3222
3286
|
};
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
const
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3287
|
+
// True nếu column filter hiện có ít nhất một giá trị đang lọc (để bật search-con).
|
|
3288
|
+
#hasActiveColumnFilter = (rawColumnFilter, columns) => columns.some(col => {
|
|
3289
|
+
const v = rawColumnFilter[col.field];
|
|
3290
|
+
if (Array.isArray(v))
|
|
3291
|
+
return v.length > 0;
|
|
3292
|
+
if (v && typeof v === 'object')
|
|
3293
|
+
return !!v.from || !!v.to;
|
|
3294
|
+
return v !== undefined && v !== null && v !== '';
|
|
3295
|
+
});
|
|
3296
|
+
// Predicate khớp MỘT dòng (raw data) theo column filter. Tách riêng để dùng lại
|
|
3297
|
+
// cho cả lọc root lẫn search-con (subtreeMatches/filterMatchingChildren).
|
|
3298
|
+
#matchesColumnFilter = (data, columns, rawColumnFilter) => {
|
|
3299
|
+
for (const column of columns) {
|
|
3300
|
+
const { field, type } = column;
|
|
3301
|
+
const filterValue = (rawColumnFilter[field] || '').toString().trim().toLowerCase();
|
|
3302
|
+
// SỬA: Dùng getNestedValue để hỗ trợ nested field trong filterLocal
|
|
3303
|
+
const rawColVal = Utilities.getNestedValue(data, field);
|
|
3304
|
+
const columnValue = (rawColVal || '').toString().trim().toLowerCase();
|
|
3305
|
+
if (filterValue) {
|
|
3306
|
+
if (!columnValue && type !== 'datetime' && type !== 'date' && type !== 'time') {
|
|
3307
|
+
return false;
|
|
3308
|
+
}
|
|
3309
|
+
if (type === 'string') {
|
|
3310
|
+
if (columnValue.indexOf(filterValue) === -1) {
|
|
3237
3311
|
return false;
|
|
3238
3312
|
}
|
|
3239
|
-
|
|
3240
|
-
|
|
3313
|
+
}
|
|
3314
|
+
else if (type === 'values' || type === 'lazy-values') {
|
|
3315
|
+
const columnType = column;
|
|
3316
|
+
const isMultiple = columnType.option.selection === 'MULTIPLE';
|
|
3317
|
+
if (isMultiple && Array.isArray(rawColVal)) {
|
|
3318
|
+
const columnValues = rawColVal.map((i) => (Utilities.getNestedValue(i, columnType.option.valueField) ?? '').toString().trim().toLowerCase()) ?? [];
|
|
3319
|
+
const filterValues = rawColumnFilter[field]?.map((v) => (v ?? '').toString().trim().toLowerCase());
|
|
3320
|
+
if (filterValues?.length && filterValues.every(fv => !columnValues.includes(fv))) {
|
|
3241
3321
|
return false;
|
|
3242
3322
|
}
|
|
3243
3323
|
}
|
|
3244
|
-
else
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
if (isMultiple && Array.isArray(rawColVal)) {
|
|
3248
|
-
const columnValues = rawColVal.map((i) => (Utilities.getNestedValue(i, columnType.option.valueField) ?? '').toString().trim().toLowerCase()) ?? [];
|
|
3249
|
-
const filterValues = rawColumnFilter[field]?.map((v) => (v ?? '').toString().trim().toLowerCase());
|
|
3250
|
-
if (filterValues?.length && filterValues.every(fv => !columnValues.includes(fv))) {
|
|
3251
|
-
return false;
|
|
3252
|
-
}
|
|
3253
|
-
}
|
|
3254
|
-
else {
|
|
3255
|
-
if (columnValue !== filterValue) {
|
|
3256
|
-
return false;
|
|
3257
|
-
}
|
|
3324
|
+
else {
|
|
3325
|
+
if (columnValue !== filterValue) {
|
|
3326
|
+
return false;
|
|
3258
3327
|
}
|
|
3259
3328
|
}
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
if (filterValue.indexOf('>=') > -1 && cValue < fValue) {
|
|
3268
|
-
return false;
|
|
3269
|
-
}
|
|
3270
|
-
else if (filterValue.indexOf('<=') > -1 && cValue > fValue) {
|
|
3271
|
-
return false;
|
|
3272
|
-
}
|
|
3273
|
-
else if (filterValue.indexOf('<') > -1 && cValue >= fValue) {
|
|
3274
|
-
return false;
|
|
3275
|
-
}
|
|
3276
|
-
else if (filterValue.indexOf('>') > -1 && cValue <= fValue) {
|
|
3277
|
-
return false;
|
|
3278
|
-
}
|
|
3279
|
-
else if (cValue !== fValue) {
|
|
3280
|
-
return false;
|
|
3281
|
-
}
|
|
3329
|
+
}
|
|
3330
|
+
else if (type === 'number') {
|
|
3331
|
+
const fValue = +filterValue.replace('>=', '').replace('<=', '').replace('>', '').replace('<', '');
|
|
3332
|
+
const cValue = +columnValue;
|
|
3333
|
+
if (fValue || fValue === 0) {
|
|
3334
|
+
if (!cValue && cValue !== 0) {
|
|
3335
|
+
return false;
|
|
3282
3336
|
}
|
|
3283
|
-
|
|
3284
|
-
else if (type === 'boolean') {
|
|
3285
|
-
if ((filterValue === '1' || filterValue === 'true') && columnValue !== '1' && columnValue !== 'true') {
|
|
3337
|
+
if (filterValue.indexOf('>=') > -1 && cValue < fValue) {
|
|
3286
3338
|
return false;
|
|
3287
3339
|
}
|
|
3288
|
-
else if ((
|
|
3340
|
+
else if (filterValue.indexOf('<=') > -1 && cValue > fValue) {
|
|
3341
|
+
return false;
|
|
3342
|
+
}
|
|
3343
|
+
else if (filterValue.indexOf('<') > -1 && cValue >= fValue) {
|
|
3344
|
+
return false;
|
|
3345
|
+
}
|
|
3346
|
+
else if (filterValue.indexOf('>') > -1 && cValue <= fValue) {
|
|
3347
|
+
return false;
|
|
3348
|
+
}
|
|
3349
|
+
else if (cValue !== fValue) {
|
|
3289
3350
|
return false;
|
|
3290
3351
|
}
|
|
3291
3352
|
}
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3353
|
+
}
|
|
3354
|
+
else if (type === 'boolean') {
|
|
3355
|
+
if ((filterValue === '1' || filterValue === 'true') && columnValue !== '1' && columnValue !== 'true') {
|
|
3356
|
+
return false;
|
|
3357
|
+
}
|
|
3358
|
+
else if ((filterValue === '0' || filterValue === 'false') && columnValue !== '0' && columnValue !== 'false') {
|
|
3359
|
+
return false;
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
else if (type === 'datetime' || type === 'date' || type === 'time') {
|
|
3363
|
+
const from = rawColumnFilter[field]?.from ?? rawColumnFilter[field];
|
|
3364
|
+
const to = rawColumnFilter[field]?.to ?? rawColumnFilter[field];
|
|
3365
|
+
const fromDate = DateUtilities.begin(from);
|
|
3366
|
+
const toDate = DateUtilities.end(to);
|
|
3367
|
+
if (fromDate || toDate) {
|
|
3368
|
+
if (!columnValue) {
|
|
3369
|
+
return false;
|
|
3370
|
+
}
|
|
3371
|
+
const columnTime = new Date(columnValue).getTime();
|
|
3372
|
+
const fromDateTime = fromDate?.getTime() || null;
|
|
3373
|
+
const toDateTime = toDate?.getTime() || null;
|
|
3374
|
+
if (fromDateTime && fromDateTime > columnTime) {
|
|
3375
|
+
return false;
|
|
3376
|
+
}
|
|
3377
|
+
if (toDateTime && columnTime > toDateTime) {
|
|
3378
|
+
return false;
|
|
3310
3379
|
}
|
|
3311
3380
|
}
|
|
3312
3381
|
}
|
|
3313
3382
|
}
|
|
3314
|
-
|
|
3315
|
-
|
|
3383
|
+
}
|
|
3384
|
+
return true;
|
|
3385
|
+
};
|
|
3386
|
+
#filterLocal = (localItems, filterInfo) => {
|
|
3387
|
+
const opt = this.tableOption();
|
|
3388
|
+
const { columns } = opt;
|
|
3389
|
+
const { rawColumnFilter, orderBy, orderDirection, pageSize, pageNumber } = filterInfo;
|
|
3390
|
+
const matchesData = (data) => this.#matchesColumnFilter(data, columns, rawColumnFilter);
|
|
3391
|
+
// Search ở cấp con: chỉ bật cho table local + static tree + đang có filter.
|
|
3392
|
+
// Khi bật, một root được giữ nếu CHÍNH NÓ hoặc bất kỳ hậu duệ nào khớp; và
|
|
3393
|
+
// lưu predicate để #render prune + auto-expand nhánh khớp.
|
|
3394
|
+
const treeOpt = opt.tree;
|
|
3395
|
+
const treeSearch = opt.type === 'local' && treeOpt?.loadType === 'static' && this.#hasActiveColumnFilter(rawColumnFilter, columns);
|
|
3396
|
+
this.#treeSearchPredicate = treeSearch ? matchesData : undefined;
|
|
3397
|
+
const items = treeSearch
|
|
3398
|
+
? localItems.filter(tableItem => subtreeMatches(tableItem.data, matchesData, treeOpt))
|
|
3399
|
+
: localItems.filter(tableItem => matchesData(tableItem.data));
|
|
3316
3400
|
// Sort
|
|
3317
3401
|
if (orderBy && orderDirection) {
|
|
3318
3402
|
const column = columns.find(e => e.field === orderBy);
|
|
@@ -3480,8 +3564,18 @@ class SdTable extends SdBaseSecureComponent {
|
|
|
3480
3564
|
this.scroll()?.scrollTop();
|
|
3481
3565
|
}
|
|
3482
3566
|
const treeOpt = this.tableOption()?.tree;
|
|
3567
|
+
const searchNow = !!this.#treeSearchPredicate;
|
|
3483
3568
|
if (treeOpt) {
|
|
3484
|
-
|
|
3569
|
+
// why: KHÔNG persist trạng thái bung bị ÉP trong lúc search (tránh nhánh
|
|
3570
|
+
// tự bung do search bị ghi nhầm thành lựa chọn của user sau khi clear).
|
|
3571
|
+
if (!this.#treeSearchActive) {
|
|
3572
|
+
this.#saveTreeExpandState(this.items());
|
|
3573
|
+
}
|
|
3574
|
+
// why: search vừa tắt → childItems đang là tập đã prune theo từ khoá cũ;
|
|
3575
|
+
// xoá cache để dựng lại đầy đủ children ở chế độ thường.
|
|
3576
|
+
if (this.#treeSearchActive && !searchNow) {
|
|
3577
|
+
this.#clearTreeChildCache(this.#localItems);
|
|
3578
|
+
}
|
|
3485
3579
|
}
|
|
3486
3580
|
this.items.set(args?.items || []);
|
|
3487
3581
|
this.total.set(args?.total || 0);
|
|
@@ -3490,6 +3584,7 @@ class SdTable extends SdBaseSecureComponent {
|
|
|
3490
3584
|
await this.#expandDefaultBranches(this.items(), treeOpt);
|
|
3491
3585
|
this.treeRevision.update(n => n + 1);
|
|
3492
3586
|
}
|
|
3587
|
+
this.#treeSearchActive = searchNow;
|
|
3493
3588
|
// Restore selection từ preservedSelectedMap (chỉ khi preserveSelection bật).
|
|
3494
3589
|
// Phải chạy TRƯỚC applyDefaultSelected để user-selection ưu tiên hơn default.
|
|
3495
3590
|
this.#restorePreservedSelection();
|
|
@@ -3587,37 +3682,68 @@ class SdTable extends SdBaseSecureComponent {
|
|
|
3587
3682
|
}
|
|
3588
3683
|
}
|
|
3589
3684
|
};
|
|
3685
|
+
// Children "nhìn thấy được" của một row: bình thường = toàn bộ embedded;
|
|
3686
|
+
// khi search-con = chỉ giữ child có subtree khớp từ khoá (prune).
|
|
3687
|
+
#visibleChildrenData = (data, treeOpt) => {
|
|
3688
|
+
const all = getChildrenFromData(data, treeOpt);
|
|
3689
|
+
const predicate = this.#treeSearchPredicate;
|
|
3690
|
+
return predicate ? filterMatchingChildren(all, predicate, treeOpt) : all;
|
|
3691
|
+
};
|
|
3692
|
+
// Xoá cache childItems trên toàn cây — dùng khi tắt search để dựng lại đầy đủ.
|
|
3693
|
+
#clearTreeChildCache = (rows) => {
|
|
3694
|
+
for (const row of rows) {
|
|
3695
|
+
const children = row.meta.tree?.childItems;
|
|
3696
|
+
if (children?.length) {
|
|
3697
|
+
this.#clearTreeChildCache(children);
|
|
3698
|
+
row.meta.tree.childItems = undefined;
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
};
|
|
3590
3702
|
#initTreeMeta = (rows, option, level = 0, parentId) => {
|
|
3703
|
+
const searchActive = !!this.#treeSearchPredicate;
|
|
3591
3704
|
for (const row of rows) {
|
|
3592
3705
|
const saved = this.#treeExpandState.get(row.meta.id);
|
|
3706
|
+
// Khi search: hasChildren tính theo tập con đã prune; auto-expand nhánh còn con.
|
|
3707
|
+
const hasChildren = searchActive
|
|
3708
|
+
? this.#visibleChildrenData(row.data, option).length > 0
|
|
3709
|
+
: resolveHasChildren(row, option);
|
|
3593
3710
|
row.meta.tree = {
|
|
3594
3711
|
...row.meta.tree,
|
|
3595
3712
|
level,
|
|
3596
3713
|
parentId,
|
|
3597
|
-
hasChildren
|
|
3598
|
-
isExpanded: saved ?? resolveDefaultExpanded(level, option),
|
|
3714
|
+
hasChildren,
|
|
3715
|
+
isExpanded: searchActive ? hasChildren : (saved ?? resolveDefaultExpanded(level, option)),
|
|
3599
3716
|
isExpanding: false,
|
|
3600
3717
|
};
|
|
3601
3718
|
}
|
|
3602
3719
|
};
|
|
3603
3720
|
#ensureChildItemsFormatted = async (row) => {
|
|
3604
|
-
if (row.meta.tree?.childItems?.length)
|
|
3605
|
-
return;
|
|
3606
3721
|
const opt = this.tableOption();
|
|
3607
3722
|
const treeOpt = opt.tree;
|
|
3608
|
-
const
|
|
3609
|
-
|
|
3723
|
+
const searchActive = !!this.#treeSearchPredicate;
|
|
3724
|
+
// Bình thường cache childItems. Khi search, LUÔN dựng lại theo tập đã prune
|
|
3725
|
+
// vì tập này phụ thuộc từ khoá (đổi mỗi lần gõ) nên không thể cache.
|
|
3726
|
+
if (!searchActive && row.meta.tree?.childItems?.length)
|
|
3610
3727
|
return;
|
|
3728
|
+
const raw = this.#visibleChildrenData(row.data, treeOpt);
|
|
3729
|
+
if (!raw.length) {
|
|
3730
|
+
if (searchActive && row.meta.tree)
|
|
3731
|
+
row.meta.tree.childItems = [];
|
|
3732
|
+
return;
|
|
3733
|
+
}
|
|
3611
3734
|
const formatted = await this.#tableFormatService.format(raw, opt.columns, this.cacheValues, this.#cacheObjValues);
|
|
3612
3735
|
row.meta.tree.childItems = formatted;
|
|
3613
3736
|
const childLevel = (row.meta.tree.level ?? 0) + 1;
|
|
3614
3737
|
for (const child of formatted) {
|
|
3615
3738
|
const saved = this.#treeExpandState.get(child.meta.id);
|
|
3739
|
+
const childHasChildren = searchActive
|
|
3740
|
+
? this.#visibleChildrenData(child.data, treeOpt).length > 0
|
|
3741
|
+
: resolveHasChildren(child, treeOpt);
|
|
3616
3742
|
child.meta.tree = {
|
|
3617
3743
|
level: childLevel,
|
|
3618
3744
|
parentId: row.meta.id,
|
|
3619
|
-
hasChildren:
|
|
3620
|
-
isExpanded: saved ?? resolveDefaultExpanded(childLevel, treeOpt),
|
|
3745
|
+
hasChildren: childHasChildren,
|
|
3746
|
+
isExpanded: searchActive ? childHasChildren : (saved ?? resolveDefaultExpanded(childLevel, treeOpt)),
|
|
3621
3747
|
isExpanding: false,
|
|
3622
3748
|
};
|
|
3623
3749
|
}
|
|
@@ -3650,7 +3776,8 @@ class SdTable extends SdBaseSecureComponent {
|
|
|
3650
3776
|
return;
|
|
3651
3777
|
}
|
|
3652
3778
|
try {
|
|
3653
|
-
|
|
3779
|
+
// isLazyTree() narrow treeOpt về TableOptionTreeLazy → truy cập onExpandChildren an toàn.
|
|
3780
|
+
if (isLazyTree(treeOpt) && hasLazyChildren(row, treeOpt)) {
|
|
3654
3781
|
row.meta.tree.isExpanding = true;
|
|
3655
3782
|
this.#ref.markForCheck();
|
|
3656
3783
|
const key = getChildrenKey(treeOpt);
|
|
@@ -4271,7 +4398,7 @@ class SdTable extends SdBaseSecureComponent {
|
|
|
4271
4398
|
provide: MatPaginatorIntl,
|
|
4272
4399
|
useClass: MatPaginatorIntlCro,
|
|
4273
4400
|
},
|
|
4274
|
-
], queries: [{ propertyName: "sdSubInformation", first: true, predicate: SdTableExpandDefDirective, descendants: true, isSignal: true }, { propertyName: "sdGroupDef", first: true, predicate: SdTableGroupDefDirective, descendants: true, isSignal: true }, { propertyName: "sdCellDefs", predicate: SdTableCellDefDirective, isSignal: true }, { propertyName: "sdFooterDefs", predicate: SdMaterialFooterDefDirective, isSignal: true }, { propertyName: "sdFilterDefs", predicate: SdTableFilterDefDirective, isSignal: true }, { propertyName: "sdTitleDefs", predicate: SdTableTitleDefDirective, isSignal: true }], viewQueries: [{ propertyName: "table", first: true, predicate: MatTable, descendants: true, isSignal: true }, { propertyName: "configComponent", first: true, predicate: ConfigComponent, descendants: true, isSignal: true }, { propertyName: "sdPopupExport", first: true, predicate: SdPopupExport, descendants: true, isSignal: true }, { propertyName: "scroll", first: true, predicate: SdScrollDirective, descendants: true, isSignal: true }, { propertyName: "quickAction", first: true, predicate: SdQuickAction, descendants: true, isSignal: true }, { propertyName: "externalFilter", first: true, predicate: ExternalFilterComponent, descendants: true, isSignal: true }, { propertyName: "mobileFilter", first: true, predicate: MobileFilterComponent, descendants: true, isSignal: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true, isSignal: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "@let _configuration = configuration();\n@let _tableOption = tableOption()!;\n@let _loading = loading();\n@let _items = items();\n@let _total = total();\n@let _isFiltered = isFiltered();\n@let _requireFiltered = requireFiltered();\n@let _export = _tableOption?.export;\n@let _exporting = exporting();\n@let _titleDef = titleDef();\n\n@if (_configuration) {\n @if (!_tableOption.filter?.disabled && !!_tableOption.filter?.externalFilters?.length && filterRegister) {\n <external-filter\n class=\"mb-16\"\n [autoId]=\"autoId()\"\n [filterRegister]=\"filterRegister\"\n [filter]=\"_tableOption.filter!\"\n [externalFilters]=\"_tableOption.filter?.externalFilters!\">\n </external-filter>\n }\n @let groupedItems = _items | sdTree: _tableOption.tree : treeRevision() | sdGroup: _tableOption : groupExpandState;\n <ng-content select=\"[sdTableTop]\"></ng-content>\n <div class=\"c-container\">\n @if (_loading) {\n <div class=\"c-loading\">\n <mat-spinner></mat-spinner>\n </div>\n }\n <div\n class=\"c-table\"\n sdScroll\n stickyShadow\n [style.max-height]=\"_tableOption.style?.maxHeight\"\n [style.min-height]=\"_tableOption.style?.minHeight\">\n <table\n mat-table\n [dataSource]=\"groupedItems\"\n [trackBy]=\"trackBy\"\n matSort\n [matSortDisabled]=\"!_tableOption.sort?.enable\"\n multiTemplateDataRows\n cdkDropList\n [cdkDropListData]=\"groupedItems\"\n [cdkDropListDisabled]=\"!_tableOption.rowReorder?.enabled\"\n [cdkDropListSortPredicate]=\"reorderSortPredicate\"\n (cdkDropListDropped)=\"onReorderDrop($event)\">\n @if (_tableOption.rowReorder?.enabled) {\n <ng-container matColumnDef=\"sdReorder\">\n <th mat-header-cell *matHeaderCellDef class=\"sd-reorder-header\" [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td mat-cell *matCellDef=\"let row\" class=\"sd-reorder-cell\">\n @if (!row.meta?.group?.items?.length) {\n <mat-icon cdkDragHandle [class.sd-reorder-disabled]=\"isReorderDisabled(row)\">\n {{ _tableOption.rowReorder?.icon || 'drag_indicator' }}\n </mat-icon>\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n }\n\n @if (_tableOption.tree) {\n <ng-container matColumnDef=\"sdTreeToggle\">\n <th\n mat-header-cell\n *matHeaderCellDef\n class=\"sd-tree-toggle-cell p-0\"\n [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td mat-cell *matCellDef=\"let row\" class=\"sd-tree-toggle-cell p-0\">\n @if (row.meta?.tree) {\n <div\n class=\"sd-tree-toggle-inner\"\n [style.padding-left.px]=\"(row.meta.tree.level ?? 0) * (_tableOption.tree?.indentSize ?? 20)\">\n @if (row.meta.tree.hasChildren) {\n @if (row.meta.tree.isExpanding) {\n <div class=\"lds-ring sd-tree-spinner\">\n <div></div>\n <div></div>\n <div></div>\n <div></div>\n </div>\n } @else {\n <sd-button\n type=\"link\"\n [prefixIcon]=\"row.meta.tree.isExpanded ? 'expand_less' : 'expand_more'\"\n (click)=\"onTreeToggle(row); $event.stopPropagation()\"\n width=\"35px\">\n </sd-button>\n }\n }\n </div>\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n }\n\n <ng-container matColumnDef=\"sdSubInformation\" sticky>\n <td class=\"p-0\" mat-cell *matCellDef=\"let item\" [attr.colspan]=\"_configuration.displayedColumns.length\">\n @if (sdSubInformation()?.templateRef) {\n @if (_tableOption.expand?.always) {\n <ng-container *ngTemplateOutlet=\"sdSubInformation()?.templateRef!; context: { item: item }\"> </ng-container>\n } @else {\n <div [@detailExpand]=\"item.isExpanded ? 'expanded' : 'collapsed'\">\n @if (item.isExpanded) {\n <ng-container *ngTemplateOutlet=\"sdSubInformation()?.templateRef!; context: { item: item }\"> </ng-container>\n }\n </div>\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n\n <ng-container matColumnDef=\"sdSubInformationAction\" stickyEnd>\n <th class=\"p-0\" mat-header-cell *matHeaderCellDef style=\"width: 1px\" [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td mat-cell *matCellDef=\"let element\">\n @if (!element.isExpanding && !_tableOption.expand?.always) {\n <sd-button\n type=\"link\"\n [prefixIcon]=\"element.isExpanded ? 'expand_less' : 'expand_more'\"\n (click)=\"onExpand(element)\"\n width=\"35px\">\n </sd-button>\n }\n @if (element.isExpanding) {\n <div class=\"lds-ring\">\n <div></div>\n <div></div>\n <div></div>\n <div></div>\n </div>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"sdSelection\" sticky>\n <th class=\"sd-selection-cell p-0\" mat-header-cell *matHeaderCellDef [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\">\n @let visibleSelectAll = _items | selectionVisibleSelectAll: _tableOption.selector | async;\n @if (visibleSelectAll) {\n <mat-checkbox\n class=\"c-selection px-8\"\n color=\"primary\"\n [ngModel]=\"isSelectAll()\"\n (ngModelChange)=\"isSelectAll.set($event)\"\n (change)=\"onSelectAll()\">\n </mat-checkbox>\n }\n </th>\n\n <td class=\"sd-selection-cell p-0\" mat-cell *matCellDef=\"let item\">\n @let visible = item | selectionVisible: _tableOption.selector;\n @if (visible) {\n @if (_tableOption.selector?.single) {\n <mat-radio-button\n class=\"c-selection px-8\"\n color=\"primary\"\n [checked]=\"item.meta.selector.isSelected\"\n (change)=\"item.meta.selector.isSelected = true; onSelect(item)\"\n [disabled]=\"selectedTableItems() | selectionDisabled: item : _tableOption.selector!\">\n </mat-radio-button>\n } @else {\n <mat-checkbox\n class=\"c-selection px-8\"\n color=\"primary\"\n [(ngModel)]=\"item.meta.selector.isSelected\"\n (change)=\"onSelect(item)\"\n [disabled]=\"selectedTableItems() | selectionDisabled: item : _tableOption.selector!\">\n </mat-checkbox>\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n @if (_tableOption.index?.enabled) {\n @let _pageOffset = pageOffset();\n @let _indexWidth = _tableOption.index?.width || '50px';\n <ng-container matColumnDef=\"sdIndex\" sticky>\n <th\n class=\"text-center p-0\"\n mat-header-cell\n *matHeaderCellDef\n [style.width]=\"_indexWidth\"\n [style.min-width]=\"_indexWidth\"\n [style.max-width]=\"_indexWidth\"\n [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\">\n {{ _tableOption.index?.title || '#' }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item; let i = dataIndex\" [style.min-width]=\"_indexWidth\" [style.max-width]=\"_indexWidth\">\n @if (!item?.meta?.group?.isGroupHeader) {\n @if (_tableOption.tree && item?.meta?.tree?.indexPath?.length) {\n <!-- why: tree \u2192 STT ph\u00E2n c\u1EA5p 1, 1.2, 1.2.1 (indexPath join '.'). T\u00F4 \u0111\u1EADm root (level 0). -->\n <span [class.sd-stt-root]=\"(item.meta.tree.level ?? 0) === 0\">\n {{ item.meta.tree.indexPath.join('.') }}\n </span>\n } @else {\n {{ _pageOffset + i + 1 }}\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef [style.min-width]=\"_indexWidth\" [style.max-width]=\"_indexWidth\"></td>\n </ng-container>\n }\n <ng-container\n matColumnDef=\"sdCommand\"\n [sticky]=\"_tableOption.command?.align !== 'right'\"\n [stickyEnd]=\"_tableOption.command?.align === 'right'\">\n <th class=\"p-0\" mat-header-cell *matHeaderCellDef style=\"width: 50px\" [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td class=\"px-8\" mat-cell *matCellDef=\"let item\">\n <desktop-command\n [autoId]=\"autoId()\"\n [commands]=\"_tableOption.command?.commands || _tableOption.commands || []\"\n [item]=\"item\"\n [itemIndex]=\"groupedItems.indexOf(item)\"></desktop-command>\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n <!-- why: group header row d\u00F9ng matRowDef RI\u00CANG v\u1EDBi column list ['sdGroupHeader']\n qua predicate when:isGroupHeaderRow. Cell colspan = displayedColumns.length \u0111\u1EC3\n span TO\u00C0N B\u1ED8 width data row. Tr\u00E1nh colspan trick overflow row khi c\u00E1c TD kh\u00E1c render. -->\n <ng-container matColumnDef=\"sdGroupHeader\">\n <td\n class=\"p-0 sd-group-header-cell\"\n mat-cell\n *matCellDef=\"let item\"\n [attr.colspan]=\"sdGroupColspan()\">\n <div class=\"sd-group-row d-flex align-items-center py-8\">\n <!-- why: slot 42px kh\u1EDBp width v\u1EDBi sd-selection-cell data row \u2192 checkbox group c\u0103n TR\u1EE4C d\u1ECDc \u0111\u00FAng v\u1EDBi checkbox data. -->\n @if (_tableOption.selector?.visible && !_tableOption.selector?.single) {\n <div class=\"sd-group-selection-slot d-flex align-items-center justify-content-center\">\n <mat-checkbox\n class=\"c-selection\"\n color=\"primary\"\n [checked]=\"isGroupAllSelected(item)\"\n [indeterminate]=\"isGroupIndeterminate(item)\"\n (change)=\"onSelectGroup(item, $event.checked)\">\n </mat-checkbox>\n </div>\n } @else {\n <div class=\"sd-group-selection-slot\"></div>\n }\n @if (_tableOption.group?.collapsible) {\n <sd-button\n type=\"link\"\n [prefixIcon]=\"item.meta.group.isExpanded ? 'expand_more' : 'chevron_right'\"\n width=\"32px\"\n (click)=\"toggleGroupExpand(item)\">\n </sd-button>\n }\n @if (sdGroupDef()?.templateRef; as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: groupContext(item)\"></ng-container>\n } @else {\n <span class=\"T14R\">{{ item.meta.group.values | json }}</span>\n }\n </div>\n </td>\n </ng-container>\n <!-- why: filler column \u1EDF cu\u1ED1i h\u1EA5p th\u1EE5 leftover space khi t\u1ED5ng width c\u00E1c c\u1ED9t data < container \u2014 gi\u1EEF cho selection/STT/command kh\u00F4ng b\u1ECB d\u00E3n. -->\n <ng-container matColumnDef=\"sdFiller\">\n <th class=\"sd-filler-cell p-0\" mat-header-cell *matHeaderCellDef [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td class=\"sd-filler-cell p-0\" mat-cell *matCellDef=\"let item\"></td>\n <td class=\"sd-filler-cell p-0\" mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n @for (column of _configuration.firstColumns; track column.field) {\n <ng-container [matColumnDef]=\"column.field\" [sticky]=\"_configuration.fixedColumn[column.field]\">\n <th\n mat-header-cell\n *matHeaderCellDef\n class=\"px-8 py-8 c-th\"\n [sdColumnResize]=\"!!_tableOption.config?.resizable && column.type !== 'children'\"\n [minWidth]=\"column.minWidth\"\n [maxWidth]=\"column.maxWidth\"\n (resizeEnd)=\"onColumnResize(column.field, $event)\"\n [style.width]=\"column.width\"\n [style.min-width]=\"column.minWidth || column.width\"\n [style.max-width]=\"column.maxWidth\"\n [attr.rowspan]=\"_configuration.multipleHeader && column.type !== 'children' ? 2 : 1\"\n [attr.colspan]=\"column.type === 'children' ? column.children.length : 1\">\n <div>\n @if (column.type === 'children') {\n <div\n aria-hidden=\"true\"\n class=\"c-header-title\"\n [class.justify-content-end]=\"column.align === 'right'\"\n [class.text-right]=\"column.align === 'right'\">\n <column-title [column]=\"column\" [titleDef]=\"_titleDef[column.field]\"></column-title>\n </div>\n } @else {\n <div\n aria-hidden=\"true\"\n mat-sort-header\n class=\"c-header-title\"\n [class.justify-content-end]=\"column.align === 'right'\"\n [class.text-right]=\"column.align === 'right'\"\n [disabled]=\"!column.sortable\">\n <column-title [column]=\"column\" [titleDef]=\"_titleDef[column.field]\"></column-title>\n </div>\n @let hideInlineFilter =\n _tableOption.filter?.disabled ||\n _tableOption.filter?.hideInlineFilter === true ||\n (_tableOption.filter?.hideInlineFilter === 'auto' && !_isFiltered && _total! <= 10);\n @if (!hideInlineFilter) {\n <ng-container *sdDesktop>\n <column-filter\n [autoId]=\"autoId()\"\n [value]=\"columnFilter?.[column.field!]!\"\n [operator]=\"columnOperator[column.field]\"\n (operatorChange)=\"columnOperator[column.field] = $event!; onOperatorChange(column, $event)\"\n [columnFilter]=\"columnFilter!\"\n [cacheValues]=\"cacheValues\"\n [column]=\"column\"\n (filterChange)=\"onFilterChange()\"\n (filterCommit)=\"onFilterCommit()\">\n </column-filter>\n </ng-container>\n }\n }\n </div>\n </th>\n <td\n class=\"c-td px-0\"\n [class.d-none]=\"column.type === 'children'\"\n mat-cell\n *matCellDef=\"let item\"\n [sdHoverCopy]=\"(item.data && item.data[column.field]) || ''\"\n [sdHoverCopyDisabled]=\"!column.cell?.copiable\">\n @if (column.type !== 'children' && !item?.meta?.group?.isGroupHeader) {\n <desktop-cell class=\"d-block px-8\" [column]=\"column\" [item]=\"item\" [cellDef]=\"cellDef()\"> </desktop-cell>\n }\n </td>\n <td mat-footer-cell *matFooterCellDef>\n <ng-container *ngIf=\"footerDef()[column.field]\">\n <ng-container *ngTemplateOutlet=\"footerDef()[column.field].templateRef; context: { items: _items, column: column }\">\n </ng-container>\n </ng-container>\n </td>\n </ng-container>\n }\n @for (column of _configuration.secondColumns; track column.field) {\n <ng-container [matColumnDef]=\"column.field\">\n <th\n mat-header-cell\n *matHeaderCellDef\n class=\"c-th px-8\"\n [style.width]=\"column.width\"\n [style.min-width]=\"column.minWidth || column.width\"\n [style.max-width]=\"column.maxWidth\">\n <div>\n <div\n aria-hidden=\"true\"\n mat-sort-header\n class=\"c-header-title\"\n [class.justify-content-end]=\"column.align === 'right'\"\n [class.text-right]=\"column.align === 'right'\"\n [disabled]=\"!column.sortable\">\n <column-title [column]=\"column\" [titleDef]=\"_titleDef[column.field]\"></column-title>\n </div>\n @if (!_tableOption.filter?.disabled && !_tableOption.filter?.hideInlineFilter && columnOperator) {\n <ng-container *sdDesktop>\n <column-filter\n [autoId]=\"autoId()\"\n [value]=\"columnFilter?.[column.field!]!\"\n [operator]=\"columnOperator[column.field]\"\n [columnFilter]=\"columnFilter!\"\n [cacheValues]=\"cacheValues\"\n [column]=\"column\"\n (operatorChange)=\"columnOperator[column.field] = $event!; onOperatorChange(column, $event)\"\n (filterChange)=\"onFilterChange()\"\n (filterCommit)=\"onFilterCommit()\">\n </column-filter>\n </ng-container>\n }\n </div>\n </th>\n <td\n class=\"c-td px-0\"\n mat-cell\n *matCellDef=\"let item\"\n [sdHoverCopy]=\"item[column.field]\"\n [sdHoverCopyDisabled]=\"!column.cell?.copiable\">\n @if (column.type !== 'children') {\n <desktop-cell class=\"d-block px-8\" [column]=\"column\" [item]=\"item\" [cellDef]=\"cellDef()\"> </desktop-cell>\n }\n </td>\n <td mat-footer-cell *matFooterCellDef>\n <ng-container *ngIf=\"footerDef()[column.field]\">\n <ng-container *ngTemplateOutlet=\"footerDef()[column.field].templateRef; context: { items: _items, column: column }\">\n </ng-container>\n </ng-container>\n </td>\n </ng-container>\n }\n <tr class=\"c-first-header\" mat-header-row *matHeaderRowDef=\"_configuration.firstHeaders; sticky: true\"></tr>\n @if (!!_configuration.secondHeaders.length) {\n <tr class=\"c-second-header\" mat-header-row *matHeaderRowDef=\"_configuration.secondHeaders; sticky: true\"></tr>\n }\n <!-- Group header row \u2014 ch\u1EC9 1 cell sdGroupHeader colspan to\u00E0n width. -->\n <tr\n mat-row\n *matRowDef=\"let row; columns: ['sdGroupHeader']; when: isGroupHeaderRow\"\n class=\"c-group-row-tr\"></tr>\n\n <!-- Data row \u2014 d\u00F9ng displayedColumns \u0111\u1EA7y \u0111\u1EE7; when isDataRow \u0111\u1EC3 lo\u1EA1i tr\u1EEB group header. -->\n <tr\n mat-row\n cdkDrag\n [cdkDragData]=\"row\"\n [cdkDragDisabled]=\"\n !_tableOption.rowReorder?.enabled ||\n !!row.meta?.group?.items?.length ||\n (row.meta?.tree?.level ?? 0) > 0 ||\n isReorderDisabled(row)\n \"\n *matRowDef=\"let row; columns: _configuration.displayedColumns; when: isDataRow\"\n matRipple\n class=\"c-row\"\n [class.sd-tree-row]=\"!!_tableOption.tree\"\n [ngClass]=\"_tableOption.tree ? 'sd-tree-level-' + (row.meta?.tree?.level ?? 0) : null\"\n [ngStyle]=\"rowStyle(row)\"\n [class.selected]=\"row.meta.selector.isSelected\"></tr>\n\n <!-- Sub-information row \u2014 also gated b\u1EDFi isDataRow \u0111\u1EC3 KH\u00D4NG render empty expand row d\u01B0\u1EDBi group header. -->\n <tr mat-row *matRowDef=\"let row; columns: ['sdSubInformation']; when: isDataRow\" class=\"c-detail-row\"></tr>\n @if (hasFooter() && !!_configuration.displayedFooters.length) {\n <tr mat-footer-row *matFooterRowDef=\"_configuration.displayedFooters; sticky: true\"></tr>\n }\n </table>\n @if (!_loading && !_total) {\n <div class=\"c-no-data-row\">\n @if (_isFiltered) {\n @if (tableConfiguration?.images?.filterEmpty) {\n <img class=\"c-image\" [src]=\"tableConfiguration!.images!.filterEmpty\" alt=\"filter-empty\" />\n } @else {\n <img class=\"c-image sd-image-filter-empty\" alt=\"filter-empty\" />\n }\n <div class=\"T16M\">{{ 'core.component.table.no-results' | translate }}</div>\n <div class=\"T16R text-secondary\">{{ 'core.component.table.no-results-hint' | translate }}</div>\n } @else {\n @if (_requireFiltered) {\n @if (tableConfiguration?.images?.filterRequired) {\n <img class=\"c-image\" [src]=\"tableConfiguration!.images!.filterRequired\" alt=\"filter-required\" />\n } @else {\n <img class=\"c-image sd-image-filter-required\" alt=\"filter-required\" />\n }\n <div class=\"T16R text-secondary\">{{ 'core.component.table.choose-filter-hint' | translate }}</div>\n } @else {\n @if (tableConfiguration?.images?.dataEmpty) {\n <img class=\"c-image\" [src]=\"tableConfiguration!.images!.dataEmpty\" alt=\"data-empty\" />\n } @else {\n <img class=\"c-image sd-image-data-empty\" alt=\"data-empty\" />\n }\n <div class=\"T16R text-secondary\">{{ 'core.component.table.no-data' | translate }}</div>\n }\n }\n </div>\n }\n </div>\n <div class=\"c-paginator\">\n <div class=\"c-action\">\n <ng-container *sdDesktop>\n @if (_tableOption.reload?.visible) {\n <sd-button\n class=\"mr-8\"\n [title]=\"'core.component.table.reload' | translate\"\n prefixIcon=\"refresh\"\n (click)=\"reload()\"\n [disabled]=\"!_items.length\"\n type=\"link\">\n </sd-button>\n }\n </ng-container>\n @if (_export && _items.length) {\n @if (_export.type === 'custom') {\n <sd-button class=\"mr-8\" [title]=\"exportTitle()\" prefixIcon=\"get_app\" (click)=\"exportCustom()\" type=\"link\"> </sd-button>\n } @else {\n @if (_export.visible) {\n @if (_exporting) {\n <sd-button class=\"mr-8\" [loading]=\"_exporting\" [title]=\"exportTitle()\" prefixIcon=\"get_app\" type=\"link\"> </sd-button>\n } @else {\n @if (_export.visible === 'ALL' || !_export.visible) {\n <sd-button class=\"mr-8\" [title]=\"exportTitle()\" prefixIcon=\"get_app\" [matMenuTriggerFor]=\"menu\" type=\"link\"> </sd-button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"exportExcel()\" type=\"button\">\n <mat-icon fontSet=\"material-icons-outlined\">file_download</mat-icon>\n <span> {{ 'core.component.table.export-excel' | translate }}</span>\n </button>\n <button mat-menu-item (click)=\"exportCSV()\" type=\"button\">\n <mat-icon fontSet=\"material-icons-outlined\">file_download</mat-icon>\n <span> {{ 'core.component.table.export-csv' | translate }}</span>\n </button>\n </mat-menu>\n } @else if (_export.visible === 'EXCEL') {\n <sd-button class=\"mr-8\" [title]=\"exportTitle()\" prefixIcon=\"get_app\" (click)=\"exportExcel()\" type=\"link\"> </sd-button>\n } @else if (_export.visible === 'CSV') {\n <sd-button class=\"mr-8\" [title]=\"exportTitle()\" prefixIcon=\"get_app\" (click)=\"exportCSV()\" type=\"link\"> </sd-button>\n }\n }\n }\n }\n }\n\n <ng-container *sdDesktop>\n @if (configComponent()) {\n <sd-button\n class=\"mr-8\"\n [title]=\"'core.component.table.setup-short' | translate\"\n prefixIcon=\"settings\"\n (click)=\"configComponent()!.open()\"\n type=\"link\">\n </sd-button>\n }\n </ng-container>\n <!-- Mobile: n\u00FAt Filter m\u1EDF mobile-filter drawer (inline filter \u1EA9n tr\u00EAn mobile) -->\n <ng-container *sdMobile>\n @if (!_tableOption.filter?.disabled && mobileFilter()) {\n <sd-button\n [autoId]=\"autoId() + 'mobile-filter-open'\"\n class=\"mr-8\"\n prefixIcon=\"filter_alt\"\n (click)=\"mobileFilter()!.open()\"\n type=\"link\">\n </sd-button>\n }\n </ng-container>\n </div>\n @let hidePaginator = !_tableOption.paginate?.pageSize || _total === undefined || _total <= _tableOption.paginate?.pageSize!;\n <!-- Desktop paginator: full config (pageSize dropdown, first/last buttons) -->\n <ng-container *sdDesktop>\n <mat-paginator\n [class.d-none]=\"_tableOption.paginate?.hidden || hidePaginator\"\n [length]=\"_total\"\n [pageSize]=\"_tableOption.paginate?.pageSize\"\n [pageSizeOptions]=\"_tableOption.paginate?.pages!\"\n [showFirstLastButtons]=\"_tableOption.paginate?.showFirstLastButtons\"\n [hidePageSize]=\"_tableOption.paginate?.hidePageSize\"></mat-paginator>\n </ng-container>\n <!-- Mobile paginator: \u1EA9n pageSize dropdown + first/last buttons \u0111\u1EC3 ti\u1EBFt ki\u1EC7m kh\u00F4ng gian -->\n <ng-container *sdMobile>\n <mat-paginator\n [class.d-none]=\"_tableOption.paginate?.hidden || hidePaginator\"\n [length]=\"_total\"\n [pageSize]=\"_tableOption.paginate?.pageSize\"\n [showFirstLastButtons]=\"false\"\n hidePageSize></mat-paginator>\n </ng-container>\n <!-- \"\u0110ang hi\u1EC3n th\u1ECB...\" ch\u1EC9 desktop -->\n <ng-container *sdDesktop>\n @if (!_tableOption.paginate?.hidden && hidePaginator && _total !== undefined && _total > 0) {\n <div class=\"T14R pr-16\">\n {{ 'core.component.table.showing' | translate }} <span class=\"T14M ml-2\">1-{{ _total }}/{{ _total }}</span>\n </div>\n }\n </ng-container>\n </div>\n </div>\n <selector-action [tableOption]=\"_tableOption\" [selectedTableItems]=\"selectedTableItems()\" (clear)=\"onClearSelection(groupedItems)\" />\n @if (_tableOption.config?.visible) {\n <config [tableOption]=\"_tableOption\" />\n }\n <!-- Mobile filter drawer \u2014 ch\u1EC9 instantiate khi c\u1EA7n render tr\u00EAn mobile -->\n @if (!_tableOption.filter?.disabled && filterRegister) {\n <mobile-filter\n [autoId]=\"autoId()\"\n [filter]=\"_tableOption.filter!\"\n [externalFilters]=\"_tableOption.filter?.externalFilters!\"\n [columns]=\"_configuration.firstColumns\"\n [filterDefs]=\"sdFilterDefs()\"\n [filterRegister]=\"filterRegister\"\n [cacheValues]=\"cacheValues\">\n </mobile-filter>\n }\n}\n", styles: [":host{display:flex;flex-direction:column;overflow:auto;width:100%;height:100%}:host .c-header-title{height:40px;display:flex;align-items:center}:host .c-container{position:relative;min-height:50px;display:flex;flex-direction:column;flex:1}:host .c-container .c-table{position:relative;flex:1;display:flex;flex-direction:column}:host .c-container .c-table table{border-collapse:separate;width:100%}:host .c-container .c-table table tr.c-first-header.mat-mdc-header-row{height:40px}:host .c-container .c-table table tr.c-second-header.mat-mdc-header-row{height:40px}:host .c-container .c-table table tr.c-detail-row{height:0}:host .c-container .c-table table tr.c-row.activated{background-color:#e5ecff}:host .c-container .c-table table tr.c-row.selected{background-color:#eef2ff}:host .c-container .c-table table tr.c-row:not(.selected):not(.activated):hover{background-color:#f5f5f5}:host .c-container .c-table table tr.c-row td{border-bottom-width:0}:host .c-container .c-table table tr.c-row.c-expandable{cursor:pointer}:host .c-container .c-table table tr.c-row.c-expandable:hover{background:#f5f5f5}:host .c-container .c-table table th.mat-mdc-header-cell{background-color:#f2f3f4;border-bottom:0!important}:host .c-container .c-table table td.mat-mdc-cell,:host .c-container .c-table table td.mat-mdc-footer-cell,:host .c-container .c-table table th.mat-mdc-header-cell{border-bottom-color:#f2f2f2!important}:host .c-container .c-table .c-th{vertical-align:middle;font-weight:500;font-size:14px;line-height:20px;color:#212121}:host .c-container .c-table .c-th.sd-col-resize-host{position:relative}:host .c-container .c-table .c-th.sd-resizing{-webkit-user-select:none;user-select:none}:host .c-container .c-table .c-th .sd-col-resize-handle{position:absolute;top:0;right:0;width:6px;height:100%;cursor:col-resize;-webkit-user-select:none;user-select:none;z-index:2}:host .c-container .c-table .c-th .sd-col-resize-handle:hover{background:#00000014}:host .c-container .c-table .c-td:first{padding-left:10px}:host .c-container .c-table .c-no-data-row{flex:1;width:100%;position:sticky;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center}:host .c-container .c-table .c-no-data-row .c-image{margin-bottom:16px;width:96px}:host .c-container .c-loading{position:absolute;inset:0 0 56px;background:#00000026;z-index:2;display:flex;align-items:center;justify-content:center}:host .c-container .c-paginator{display:flex;flex-direction:row;justify-content:space-between;align-items:center;background-color:#fff}:host .c-container .c-paginator .c-action{padding:5px}:host .c-container .c-empty{text-align:center;background-color:#fff;border:none!important}:host .c-container .c-empty mat-icon{font-size:150px;margin-top:30px;margin-bottom:30px;opacity:.2;width:auto;height:auto}:host button.c-btn-add{background-color:#fff;box-shadow:0 2px 4px #2f313629}:host .lds-ring{display:inline-block;position:relative;width:40px;height:40px}:host .lds-ring div{box-sizing:border-box;display:block;position:absolute;width:32px;height:32px;margin:4px;border:4px solid #cef;border-radius:50%;animation:lds-ring 1.2s cubic-bezier(.5,0,.5,1) infinite;border-color:#cef transparent transparent transparent}:host .lds-ring div:nth-child(1){animation-delay:-.45s}:host .lds-ring div:nth-child(2){animation-delay:-.3s}:host .lds-ring div:nth-child(3){animation-delay:-.15s}@keyframes lds-ring{0%{transform:rotate(0)}to{transform:rotate(360deg)}}:host .sd-reorder-header,:host .sd-reorder-cell{width:40px;min-width:40px;padding:0 4px;box-sizing:border-box}:host .sd-reorder-cell mat-icon{cursor:grab;color:#00000061;display:flex;align-items:center}:host .sd-reorder-cell mat-icon:active{cursor:grabbing}:host .sd-reorder-cell mat-icon.sd-reorder-disabled{opacity:.3;cursor:not-allowed;pointer-events:none}:host ::ng-deep .mat-sort-header-content{display:block;text-align:left;width:100%}:host ::ng-deep .mat-select-arrow{color:#a6a6a6}:host ::ng-deep .mat-sort-header-disabled{background-image:none!important;cursor:default!important;padding-right:0!important}:host ::ng-deep .mat-sort-header-container{align-items:start!important}:host ::ng-deep .mat-sort-header-arrow{margin-top:4px!important}:host ::ng-deep .mat-sort-header-arrow{display:none!important}:host ::ng-deep .mat-sort-header{cursor:pointer}:host ::ng-deep .mat-sort-header[aria-sort]{background-repeat:no-repeat;background-position:center right 0;background-size:16px 16px;cursor:pointer;padding-right:24px}:host ::ng-deep .mat-sort-header[aria-sort=none]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath fill='%237A7A7A' d='M12 5.83L15.17 9l1.41-1.41L12 3 7.41 7.59 8.83 9 12 5.83zm0 12.34L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15 12 18.17z'/%3E%3C/svg%3E\")}:host ::ng-deep .mat-sort-header[aria-sort=ascending]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath fill='%237A7A7A' d='M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z'/%3E%3C/svg%3E\")}:host ::ng-deep .mat-sort-header[aria-sort=descending]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath fill='%237A7A7A' d='M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z'/%3E%3C/svg%3E\")}:host ::ng-deep .c-paginator .mat-mdc-paginator .mdc-text-field--outlined .mat-mdc-form-field-infix,:host ::ng-deep .c-paginator .mat-mdc-paginator .mdc-text-field--no-label .mat-mdc-form-field-infix{padding-top:2px;padding-bottom:2px;min-height:32px}:host ::ng-deep .c-paginator .mat-mdc-paginator-range-label{margin:0 4px}:host ::ng-deep .c-paginator .mat-mdc-paginator-page-size-select mat-select{margin-top:2px!important}:host ::ng-deep .cdk-drag-preview{box-shadow:0 4px 12px #00000026;background:#fff;display:table;width:100%}:host ::ng-deep .cdk-drag-preview td{padding:0 8px;border-bottom:1px solid rgba(0,0,0,.12)}:host ::ng-deep .cdk-drag-placeholder{opacity:0}:host ::ng-deep .cdk-drop-list-dragging tr.c-row:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}:host ::ng-deep .sd-selection-cell{width:42px;min-width:42px;max-width:42px}:host ::ng-deep .sd-filler-cell{width:auto;min-width:0;padding:0!important;border:0}:host ::ng-deep .sd-group-header-cell{background:#f5f7fc;border-top:1px solid #e6e9f2;border-bottom:1px solid #e6e9f2}:host ::ng-deep .sd-group-row{min-height:36px}:host ::ng-deep .sd-group-selection-slot{width:42px;min-width:42px;max-width:42px}:host ::ng-deep .c-selection{transform:scale(.85);transform-origin:center}:host ::ng-deep .sd-tree-toggle-cell{width:40px;min-width:40px;max-width:100px}:host ::ng-deep .sd-tree-toggle-inner{display:flex;align-items:center;min-height:40px}:host ::ng-deep .sd-tree-spinner{transform:scale(.5);margin:0 auto}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-0{background:#fbfcfe}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-0>td{font-weight:500}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-1{background:#fff}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-2{background:#fafbfd}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-3{background:#f6f8fb}:host ::ng-deep tr.c-row.sd-tree-row:not(.sd-tree-level-0)>td.sd-tree-toggle-cell .sd-tree-toggle-inner{border-left:1px dashed #d8dde6}:host ::ng-deep .sd-stt-root{font-weight:700;color:#1f2330}\n", ":host ::ng-deep .sticky-shadow-right{overflow:visible!important}:host ::ng-deep .sticky-shadow-right:after{content:\"\";position:absolute;top:0;right:0;bottom:0;width:8px;transform:translate(100%);background:linear-gradient(to right,rgba(0,0,0,.12),transparent);pointer-events:none}:host ::ng-deep .sticky-shadow-left{overflow:visible!important}:host ::ng-deep .sticky-shadow-left:after{content:\"\";position:absolute;top:0;left:0;bottom:0;width:8px;transform:translate(-100%);background:linear-gradient(to left,rgba(0,0,0,.12),transparent);pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.JsonPipe, name: "json" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i4$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatFooterCellDef, selector: "[matFooterCellDef]" }, { kind: "directive", type: i6.MatFooterRowDef, selector: "[matFooterRowDef]", inputs: ["matFooterRowDef", "matFooterRowDefSticky"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "directive", type: i6.MatFooterCell, selector: "mat-footer-cell, td[mat-footer-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i6.MatFooterRow, selector: "mat-footer-row, tr[mat-footer-row]", exportAs: ["matFooterRow"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i7.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i7.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i8.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i9.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "component", type: i10.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i11.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i11.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i11.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: DesktopCommand, selector: "desktop-command", inputs: ["autoId", "item", "itemIndex", "commands"] }, { kind: "component", type: DesktopCellComponent, selector: "desktop-cell", inputs: ["column", "item", "cellDef"] }, { kind: "component", type: ColumnTitleComponent, selector: "column-title", inputs: ["column", "titleDef"] }, { kind: "component", type: ExternalFilterComponent, selector: "external-filter", inputs: ["autoId", "filter", "externalFilters", "filterRegister"] }, { kind: "component", type: ConfigComponent, selector: "config", inputs: ["tableOption"], outputs: ["changes"] }, { kind: "component", type: ColumnFilterComponent, selector: "column-filter", inputs: ["autoId", "column", "columnFilter", "cacheValues", "value", "operator", "isMobile"], outputs: ["operatorChange", "filterChange", "filterCommit"] }, { kind: "component", type: MobileFilterComponent, selector: "mobile-filter", inputs: ["autoId", "filter", "externalFilters", "columns", "filterDefs", "filterRegister", "cacheValues"] }, { kind: "pipe", type: SdGroupPipe, name: "sdGroup" }, { kind: "pipe", type: SdTreePipe, name: "sdTree" }, { kind: "pipe", type: SdSelectionVisiblePipe, name: "selectionVisible" }, { kind: "pipe", type: SdSelectionVisibleSelectAllPipe, name: "selectionVisibleSelectAll" }, { kind: "pipe", type: SdSelectionDisabledPipe, name: "selectionDisabled" }, { kind: "directive", type: SdScrollDirective, selector: "[sdScroll]" }, { kind: "directive", type: SdDesktopDirective, selector: "[sdDesktop]" }, { kind: "directive", type: SdMobileDirective, selector: "[sdMobile]" }, { kind: "directive", type: SdHoverCopyDirective, selector: "[sdHoverCopy]", inputs: ["sdHoverCopy", "sdHoverCopyDisabled"] }, { kind: "component", type: SelectorActionComponent, selector: "selector-action", inputs: ["tableOption", "selectedTableItems"], outputs: ["clear"] }, { kind: "directive", type: StickyShadowDirective, selector: "[stickyShadow]" }, { kind: "directive", type: SdColumnResizeDirective, selector: "[sdColumnResize]", inputs: ["sdColumnResize", "minWidth", "maxWidth"], outputs: ["resizeEnd"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], animations: [
|
|
4401
|
+
], queries: [{ propertyName: "sdSubInformation", first: true, predicate: SdTableExpandDefDirective, descendants: true, isSignal: true }, { propertyName: "sdGroupDef", first: true, predicate: SdTableGroupDefDirective, descendants: true, isSignal: true }, { propertyName: "sdCellDefs", predicate: SdTableCellDefDirective, isSignal: true }, { propertyName: "sdFooterDefs", predicate: SdMaterialFooterDefDirective, isSignal: true }, { propertyName: "sdFilterDefs", predicate: SdTableFilterDefDirective, isSignal: true }, { propertyName: "sdTitleDefs", predicate: SdTableTitleDefDirective, isSignal: true }], viewQueries: [{ propertyName: "table", first: true, predicate: MatTable, descendants: true, isSignal: true }, { propertyName: "configComponent", first: true, predicate: ConfigComponent, descendants: true, isSignal: true }, { propertyName: "sdPopupExport", first: true, predicate: SdPopupExport, descendants: true, isSignal: true }, { propertyName: "scroll", first: true, predicate: SdScrollDirective, descendants: true, isSignal: true }, { propertyName: "quickAction", first: true, predicate: SdQuickAction, descendants: true, isSignal: true }, { propertyName: "externalFilter", first: true, predicate: ExternalFilterComponent, descendants: true, isSignal: true }, { propertyName: "mobileFilter", first: true, predicate: MobileFilterComponent, descendants: true, isSignal: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true, isSignal: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "@let _configuration = configuration();\n@let _tableOption = tableOption()!;\n@let _loading = loading();\n@let _items = items();\n@let _total = total();\n@let _isFiltered = isFiltered();\n@let _requireFiltered = requireFiltered();\n@let _export = _tableOption?.export;\n@let _exporting = exporting();\n@let _titleDef = titleDef();\n@let _autoId = autoId();\n@let _exportTitle = exportTitle();\n@let _cellDef = cellDef();\n@let _footerDef = footerDef();\n@let _sdSubInformation = sdSubInformation();\n@let _configComponent = configComponent();\n@let _mobileFilter = mobileFilter();\n@let _selectedTableItems = selectedTableItems();\n<!-- why: tree kh\u00F4ng c\u00F2n c\u1ED9t ri\u00EAng \u2014 khi KH\u00D4NG c\u00F3 c\u1ED9t Index, icon expand nh\u00FAng v\u00E0o\n c\u1ED9t data \u0111\u1EA7u ti\u00EAn. \u0110\u00E2y l\u00E0 field c\u1EE7a c\u1ED9t \u0111\u00F3 (null n\u1EBFu c\u00F3 Index ho\u1EB7c kh\u00F4ng tree). -->\n@let _treeFirstField = _tableOption?.tree && !_tableOption.index?.enabled ? _configuration?.firstColumns?.[0]?.field : null;\n\n@if (_configuration) {\n @if (!_tableOption.filter?.disabled && !!_tableOption.filter?.externalFilters?.length && filterRegister) {\n <external-filter\n class=\"mb-16\"\n [autoId]=\"_autoId\"\n [filterRegister]=\"filterRegister\"\n [filter]=\"_tableOption.filter!\"\n [externalFilters]=\"_tableOption.filter?.externalFilters!\">\n </external-filter>\n }\n @let groupedItems = _items | sdTree: _tableOption.tree : treeRevision() | sdGroup: _tableOption : groupExpandState;\n\n <!-- Tree toggle d\u00F9ng chung: indent (theo c\u1EA5p) + slot chevron/spinner. Nh\u00FAng v\u00E0o\n c\u1ED9t Index ho\u1EB7c c\u1ED9t data \u0111\u1EA7u qua *ngTemplateOutlet (context: { row }). -->\n <ng-template #sdTreeToggle let-row=\"row\">\n @let _tree = row?.meta?.tree;\n @if (_tree) {\n <span class=\"sd-tree-indent\" [style.width.px]=\"(_tree.level ?? 0) * (_tableOption.tree?.indentSize ?? 20)\"></span>\n <span class=\"sd-tree-toggle-slot\">\n @if (_tree.hasChildren) {\n @if (_tree.isExpanding) {\n <span class=\"lds-ring sd-tree-spinner\">\n <div></div>\n <div></div>\n <div></div>\n <div></div>\n </span>\n } @else {\n @let _aid = _autoId;\n <button\n type=\"button\"\n class=\"sd-tree-toggle-btn\"\n [attr.data-autoid]=\"_aid ? _aid + '-tree-toggle-' + row.meta.id : null\"\n (click)=\"onTreeToggle(row); $event.stopPropagation()\">\n <mat-icon>{{ _tree.isExpanded ? 'expand_more' : 'chevron_right' }}</mat-icon>\n </button>\n }\n }\n </span>\n }\n </ng-template>\n\n <ng-content select=\"[sdTableTop]\"></ng-content>\n <div class=\"c-container\">\n @if (_loading) {\n <div class=\"c-loading\">\n <mat-spinner></mat-spinner>\n </div>\n }\n <div\n class=\"c-table\"\n sdScroll\n stickyShadow\n [style.max-height]=\"_tableOption.style?.maxHeight\"\n [style.min-height]=\"_tableOption.style?.minHeight\">\n <table\n mat-table\n [dataSource]=\"groupedItems\"\n [trackBy]=\"trackBy\"\n matSort\n [matSortDisabled]=\"!_tableOption.sort?.enable\"\n multiTemplateDataRows\n cdkDropList\n [cdkDropListData]=\"groupedItems\"\n [cdkDropListDisabled]=\"!_tableOption.rowReorder?.enabled\"\n [cdkDropListSortPredicate]=\"reorderSortPredicate\"\n (cdkDropListDropped)=\"onReorderDrop($event)\">\n @if (_tableOption.rowReorder?.enabled) {\n <ng-container matColumnDef=\"sdReorder\">\n <th mat-header-cell *matHeaderCellDef class=\"sd-reorder-header\" [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td mat-cell *matCellDef=\"let row\" class=\"sd-reorder-cell\">\n @if (!row.meta?.group?.items?.length) {\n <mat-icon cdkDragHandle [class.sd-reorder-disabled]=\"isReorderDisabled(row)\">\n {{ _tableOption.rowReorder?.icon || 'drag_indicator' }}\n </mat-icon>\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n }\n\n <ng-container matColumnDef=\"sdSubInformation\" sticky>\n <td class=\"p-0\" mat-cell *matCellDef=\"let item\" [attr.colspan]=\"_configuration.displayedColumns.length\">\n @if (_sdSubInformation?.templateRef) {\n @if (_tableOption.expand?.always) {\n <ng-container *ngTemplateOutlet=\"_sdSubInformation?.templateRef!; context: { item: item }\"> </ng-container>\n } @else {\n <div [@detailExpand]=\"item.isExpanded ? 'expanded' : 'collapsed'\">\n @if (item.isExpanded) {\n <ng-container *ngTemplateOutlet=\"_sdSubInformation?.templateRef!; context: { item: item }\"> </ng-container>\n }\n </div>\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n\n <ng-container matColumnDef=\"sdSubInformationAction\" stickyEnd>\n <th class=\"p-0\" mat-header-cell *matHeaderCellDef style=\"width: 1px\" [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td mat-cell *matCellDef=\"let element\">\n @if (!element.isExpanding && !_tableOption.expand?.always) {\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-expand-' + element.meta.id : null\"\n type=\"link\"\n [prefixIcon]=\"element.isExpanded ? 'expand_less' : 'expand_more'\"\n (click)=\"onExpand(element)\"\n width=\"35px\">\n </sd-button>\n }\n @if (element.isExpanding) {\n <div class=\"lds-ring\">\n <div></div>\n <div></div>\n <div></div>\n <div></div>\n </div>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"sdSelection\" sticky>\n <th class=\"sd-selection-cell p-0\" mat-header-cell *matHeaderCellDef [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\">\n @let visibleSelectAll = _items | selectionVisibleSelectAll: _tableOption.selector | async;\n @if (visibleSelectAll) {\n <mat-checkbox\n class=\"c-selection px-8\"\n color=\"primary\"\n [ngModel]=\"isSelectAll()\"\n (ngModelChange)=\"isSelectAll.set($event)\"\n (change)=\"onSelectAll()\">\n </mat-checkbox>\n }\n </th>\n\n <td class=\"sd-selection-cell p-0\" mat-cell *matCellDef=\"let item\">\n @let visible = item | selectionVisible: _tableOption.selector;\n @if (visible) {\n @if (_tableOption.selector?.single) {\n <mat-radio-button\n class=\"c-selection px-8\"\n color=\"primary\"\n [checked]=\"item.meta.selector.isSelected\"\n (change)=\"item.meta.selector.isSelected = true; onSelect(item)\"\n [disabled]=\"_selectedTableItems | selectionDisabled: item : _tableOption.selector!\">\n </mat-radio-button>\n } @else {\n <mat-checkbox\n class=\"c-selection px-8\"\n color=\"primary\"\n [(ngModel)]=\"item.meta.selector.isSelected\"\n (change)=\"onSelect(item)\"\n [disabled]=\"_selectedTableItems | selectionDisabled: item : _tableOption.selector!\">\n </mat-checkbox>\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n @if (_tableOption.index?.enabled) {\n @let _pageOffset = pageOffset();\n @let _indexWidth = _tableOption.index?.width || '50px';\n <!-- why: khi tree, c\u1ED9t Index \"\u00F4m\" th\u00EAm chevron + indent. D\u00F9ng width:1px +\n nowrap (.sd-tree-index-cell) \u0111\u1EC3 c\u1ED9t co S\u00C1T n\u1ED9i dung, kh\u00F4ng chi\u1EBFm ch\u1ED7\n th\u1EEBa; header \"#\" canh tr\u00E1i l\u00F9i v\u00E0o (.sd-tree-index-header) cho th\u1EB3ng s\u1ED1. -->\n @let _treeIndex = !!_tableOption.tree;\n <ng-container matColumnDef=\"sdIndex\" sticky>\n <th\n [class.p-0]=\"!_treeIndex\"\n [class.text-center]=\"!_treeIndex\"\n [class.sd-tree-index-cell]=\"_treeIndex\"\n [class.sd-tree-index-header]=\"_treeIndex\"\n mat-header-cell\n *matHeaderCellDef\n [style.width]=\"_treeIndex ? '1px' : _indexWidth\"\n [style.min-width]=\"_treeIndex ? null : _indexWidth\"\n [style.max-width]=\"_treeIndex ? null : _indexWidth\"\n [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\">\n {{ _tableOption.index?.title || '#' }}\n </th>\n <td\n [class.px-4]=\"!_treeIndex\"\n [class.text-center]=\"!_treeIndex\"\n [class.sd-tree-index-cell]=\"_treeIndex\"\n mat-cell\n *matCellDef=\"let item; let i = dataIndex\"\n [style.width]=\"_treeIndex ? '1px' : null\"\n [style.min-width]=\"_treeIndex ? null : _indexWidth\"\n [style.max-width]=\"_treeIndex ? null : _indexWidth\">\n @if (!item?.meta?.group?.isGroupHeader) {\n @if (_treeIndex) {\n <!-- why: tree \u2192 chevron + indent + STT ph\u00E2n c\u1EA5p (1, 1.2, 1.2.1). T\u00F4 \u0111\u1EADm root (level 0). -->\n <span class=\"sd-tree-line\">\n <ng-container *ngTemplateOutlet=\"sdTreeToggle; context: { row: item }\"></ng-container>\n @if (item?.meta?.tree?.indexPath?.length) {\n <span class=\"sd-tree-stt\" [class.sd-stt-root]=\"(item.meta.tree.level ?? 0) === 0\">\n {{ item.meta.tree.indexPath.join('.') }}\n </span>\n }\n </span>\n } @else {\n {{ _pageOffset + i + 1 }}\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef [style.width]=\"_treeIndex ? '1px' : null\" [style.min-width]=\"_treeIndex ? null : _indexWidth\" [style.max-width]=\"_treeIndex ? null : _indexWidth\"></td>\n </ng-container>\n }\n <ng-container\n matColumnDef=\"sdCommand\"\n [sticky]=\"_tableOption.command?.align !== 'right'\"\n [stickyEnd]=\"_tableOption.command?.align === 'right'\">\n <th class=\"p-0\" mat-header-cell *matHeaderCellDef style=\"width: 50px\" [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td class=\"px-8\" mat-cell *matCellDef=\"let item\">\n <desktop-command\n [autoId]=\"_autoId\"\n [commands]=\"_tableOption.command?.commands || _tableOption.commands || []\"\n [item]=\"item\"\n [itemIndex]=\"groupedItems.indexOf(item)\"></desktop-command>\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n <!-- why: group header row d\u00F9ng matRowDef RI\u00CANG v\u1EDBi column list ['sdGroupHeader']\n qua predicate when:isGroupHeaderRow. Cell colspan = displayedColumns.length \u0111\u1EC3\n span TO\u00C0N B\u1ED8 width data row. Tr\u00E1nh colspan trick overflow row khi c\u00E1c TD kh\u00E1c render. -->\n <ng-container matColumnDef=\"sdGroupHeader\">\n <td\n class=\"p-0 sd-group-header-cell\"\n mat-cell\n *matCellDef=\"let item\"\n [attr.colspan]=\"sdGroupColspan()\">\n <div class=\"sd-group-row d-flex align-items-center py-8\">\n <!-- why: slot 42px kh\u1EDBp width v\u1EDBi sd-selection-cell data row \u2192 checkbox group c\u0103n TR\u1EE4C d\u1ECDc \u0111\u00FAng v\u1EDBi checkbox data. -->\n @if (_tableOption.selector?.visible && !_tableOption.selector?.single) {\n <div class=\"sd-group-selection-slot d-flex align-items-center justify-content-center\">\n <mat-checkbox\n class=\"c-selection\"\n color=\"primary\"\n [checked]=\"isGroupAllSelected(item)\"\n [indeterminate]=\"isGroupIndeterminate(item)\"\n (change)=\"onSelectGroup(item, $event.checked)\">\n </mat-checkbox>\n </div>\n } @else {\n <div class=\"sd-group-selection-slot\"></div>\n }\n @if (_tableOption.group?.collapsible) {\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-group-toggle-' + (item.meta.group?.key || item.meta.id) : null\"\n type=\"link\"\n [prefixIcon]=\"item.meta.group.isExpanded ? 'expand_more' : 'chevron_right'\"\n width=\"32px\"\n (click)=\"toggleGroupExpand(item)\">\n </sd-button>\n }\n @if (sdGroupDef()?.templateRef; as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: groupContext(item)\"></ng-container>\n } @else {\n <span class=\"T14R\">{{ item.meta.group.values | json }}</span>\n }\n </div>\n </td>\n </ng-container>\n <!-- why: filler column \u1EDF cu\u1ED1i h\u1EA5p th\u1EE5 leftover space khi t\u1ED5ng width c\u00E1c c\u1ED9t data < container \u2014 gi\u1EEF cho selection/STT/command kh\u00F4ng b\u1ECB d\u00E3n. -->\n <ng-container matColumnDef=\"sdFiller\">\n <th class=\"sd-filler-cell p-0\" mat-header-cell *matHeaderCellDef [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td class=\"sd-filler-cell p-0\" mat-cell *matCellDef=\"let item\"></td>\n <td class=\"sd-filler-cell p-0\" mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n @for (column of _configuration.firstColumns; track column.field) {\n <ng-container [matColumnDef]=\"column.field\" [sticky]=\"_configuration.fixedColumn[column.field]\">\n <th\n mat-header-cell\n *matHeaderCellDef\n class=\"px-8 py-8 c-th\"\n [sdColumnResize]=\"!!_tableOption.config?.resizable && column.type !== 'children'\"\n [minWidth]=\"column.minWidth\"\n [maxWidth]=\"column.maxWidth\"\n (resizeEnd)=\"onColumnResize(column.field, $event)\"\n [style.width]=\"column.width\"\n [style.min-width]=\"column.minWidth || column.width\"\n [style.max-width]=\"column.maxWidth\"\n [attr.rowspan]=\"_configuration.multipleHeader && column.type !== 'children' ? 2 : 1\"\n [attr.colspan]=\"column.type === 'children' ? column.children.length : 1\">\n <div>\n @if (column.type === 'children') {\n <div\n aria-hidden=\"true\"\n class=\"c-header-title\"\n [class.justify-content-end]=\"column.align === 'right'\"\n [class.text-right]=\"column.align === 'right'\">\n <column-title [column]=\"column\" [titleDef]=\"_titleDef[column.field]\"></column-title>\n </div>\n } @else {\n <div\n aria-hidden=\"true\"\n mat-sort-header\n class=\"c-header-title\"\n [class.justify-content-end]=\"column.align === 'right'\"\n [class.text-right]=\"column.align === 'right'\"\n [disabled]=\"!column.sortable\">\n <column-title [column]=\"column\" [titleDef]=\"_titleDef[column.field]\"></column-title>\n </div>\n @let hideInlineFilter =\n _tableOption.filter?.disabled ||\n _tableOption.filter?.hideInlineFilter === true ||\n (_tableOption.filter?.hideInlineFilter === 'auto' && !_isFiltered && _total! <= 10);\n @if (!hideInlineFilter) {\n <ng-container *sdDesktop>\n <column-filter\n [autoId]=\"_autoId\"\n [value]=\"columnFilter?.[column.field!]!\"\n [operator]=\"columnOperator[column.field]\"\n (operatorChange)=\"columnOperator[column.field] = $event!; onOperatorChange(column, $event)\"\n [columnFilter]=\"columnFilter!\"\n [cacheValues]=\"cacheValues\"\n [column]=\"column\"\n (filterChange)=\"onFilterChange()\"\n (filterCommit)=\"onFilterCommit()\">\n </column-filter>\n </ng-container>\n }\n }\n </div>\n </th>\n <td\n class=\"c-td px-0\"\n [class.d-none]=\"column.type === 'children'\"\n mat-cell\n *matCellDef=\"let item\"\n [sdHoverCopy]=\"(item.data && item.data[column.field]) || ''\"\n [sdHoverCopyDisabled]=\"!column.cell?.copiable\">\n @if (column.type !== 'children' && !item?.meta?.group?.isGroupHeader) {\n @if (_treeFirstField && column.field === _treeFirstField) {\n <!-- why: kh\u00F4ng c\u00F3 c\u1ED9t Index \u2192 chevron + indent nh\u00FAng v\u00E0o c\u1ED9t data \u0111\u1EA7u (nh\u01B0 file explorer). -->\n <span class=\"sd-tree-line px-8\">\n <ng-container *ngTemplateOutlet=\"sdTreeToggle; context: { row: item }\"></ng-container>\n <desktop-cell class=\"d-block\" [column]=\"column\" [item]=\"item\" [cellDef]=\"_cellDef\"></desktop-cell>\n </span>\n } @else {\n <desktop-cell class=\"d-block px-8\" [column]=\"column\" [item]=\"item\" [cellDef]=\"_cellDef\"> </desktop-cell>\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef>\n <ng-container *ngIf=\"_footerDef[column.field]\">\n <ng-container *ngTemplateOutlet=\"_footerDef[column.field].templateRef; context: { items: _items, column: column }\">\n </ng-container>\n </ng-container>\n </td>\n </ng-container>\n }\n @for (column of _configuration.secondColumns; track column.field) {\n <ng-container [matColumnDef]=\"column.field\">\n <th\n mat-header-cell\n *matHeaderCellDef\n class=\"c-th px-8\"\n [style.width]=\"column.width\"\n [style.min-width]=\"column.minWidth || column.width\"\n [style.max-width]=\"column.maxWidth\">\n <div>\n <div\n aria-hidden=\"true\"\n mat-sort-header\n class=\"c-header-title\"\n [class.justify-content-end]=\"column.align === 'right'\"\n [class.text-right]=\"column.align === 'right'\"\n [disabled]=\"!column.sortable\">\n <column-title [column]=\"column\" [titleDef]=\"_titleDef[column.field]\"></column-title>\n </div>\n @if (!_tableOption.filter?.disabled && !_tableOption.filter?.hideInlineFilter && columnOperator) {\n <ng-container *sdDesktop>\n <column-filter\n [autoId]=\"_autoId\"\n [value]=\"columnFilter?.[column.field!]!\"\n [operator]=\"columnOperator[column.field]\"\n [columnFilter]=\"columnFilter!\"\n [cacheValues]=\"cacheValues\"\n [column]=\"column\"\n (operatorChange)=\"columnOperator[column.field] = $event!; onOperatorChange(column, $event)\"\n (filterChange)=\"onFilterChange()\"\n (filterCommit)=\"onFilterCommit()\">\n </column-filter>\n </ng-container>\n }\n </div>\n </th>\n <td\n class=\"c-td px-0\"\n mat-cell\n *matCellDef=\"let item\"\n [sdHoverCopy]=\"item[column.field]\"\n [sdHoverCopyDisabled]=\"!column.cell?.copiable\">\n @if (column.type !== 'children') {\n <desktop-cell class=\"d-block px-8\" [column]=\"column\" [item]=\"item\" [cellDef]=\"_cellDef\"> </desktop-cell>\n }\n </td>\n <td mat-footer-cell *matFooterCellDef>\n <ng-container *ngIf=\"_footerDef[column.field]\">\n <ng-container *ngTemplateOutlet=\"_footerDef[column.field].templateRef; context: { items: _items, column: column }\">\n </ng-container>\n </ng-container>\n </td>\n </ng-container>\n }\n <tr class=\"c-first-header\" mat-header-row *matHeaderRowDef=\"_configuration.firstHeaders; sticky: true\"></tr>\n @if (!!_configuration.secondHeaders.length) {\n <tr class=\"c-second-header\" mat-header-row *matHeaderRowDef=\"_configuration.secondHeaders; sticky: true\"></tr>\n }\n <!-- Group header row \u2014 ch\u1EC9 1 cell sdGroupHeader colspan to\u00E0n width. -->\n <tr\n mat-row\n *matRowDef=\"let row; columns: ['sdGroupHeader']; when: isGroupHeaderRow\"\n class=\"c-group-row-tr\"></tr>\n\n <!-- Data row \u2014 d\u00F9ng displayedColumns \u0111\u1EA7y \u0111\u1EE7; when isDataRow \u0111\u1EC3 lo\u1EA1i tr\u1EEB group header. -->\n <tr\n mat-row\n cdkDrag\n [cdkDragData]=\"row\"\n [cdkDragDisabled]=\"\n !_tableOption.rowReorder?.enabled ||\n !!row.meta?.group?.items?.length ||\n (row.meta?.tree?.level ?? 0) > 0 ||\n isReorderDisabled(row)\n \"\n *matRowDef=\"let row; columns: _configuration.displayedColumns; when: isDataRow\"\n matRipple\n class=\"c-row\"\n [class.sd-tree-row]=\"!!_tableOption.tree\"\n [ngClass]=\"_tableOption.tree ? 'sd-tree-level-' + (row.meta?.tree?.level ?? 0) : null\"\n [ngStyle]=\"rowStyle(row)\"\n [class.selected]=\"row.meta.selector.isSelected\"></tr>\n\n <!-- Sub-information row \u2014 also gated b\u1EDFi isDataRow \u0111\u1EC3 KH\u00D4NG render empty expand row d\u01B0\u1EDBi group header. -->\n <tr mat-row *matRowDef=\"let row; columns: ['sdSubInformation']; when: isDataRow\" class=\"c-detail-row\"></tr>\n @if (hasFooter() && !!_configuration.displayedFooters.length) {\n <tr mat-footer-row *matFooterRowDef=\"_configuration.displayedFooters; sticky: true\"></tr>\n }\n </table>\n @if (!_loading && !_total) {\n <div class=\"c-no-data-row\">\n @if (_isFiltered) {\n @if (tableConfiguration?.images?.filterEmpty) {\n <img class=\"c-image\" [src]=\"tableConfiguration!.images!.filterEmpty\" alt=\"filter-empty\" />\n } @else {\n <img class=\"c-image sd-image-filter-empty\" alt=\"filter-empty\" />\n }\n <div class=\"T16M\">{{ 'core.component.table.no-results' | translate }}</div>\n <div class=\"T16R text-secondary\">{{ 'core.component.table.no-results-hint' | translate }}</div>\n } @else {\n @if (_requireFiltered) {\n @if (tableConfiguration?.images?.filterRequired) {\n <img class=\"c-image\" [src]=\"tableConfiguration!.images!.filterRequired\" alt=\"filter-required\" />\n } @else {\n <img class=\"c-image sd-image-filter-required\" alt=\"filter-required\" />\n }\n <div class=\"T16R text-secondary\">{{ 'core.component.table.choose-filter-hint' | translate }}</div>\n } @else {\n @if (tableConfiguration?.images?.dataEmpty) {\n <img class=\"c-image\" [src]=\"tableConfiguration!.images!.dataEmpty\" alt=\"data-empty\" />\n } @else {\n <img class=\"c-image sd-image-data-empty\" alt=\"data-empty\" />\n }\n <div class=\"T16R text-secondary\">{{ 'core.component.table.no-data' | translate }}</div>\n }\n }\n </div>\n }\n </div>\n <div class=\"c-paginator\">\n <div class=\"c-action\">\n <ng-container *sdDesktop>\n @if (_tableOption.reload?.visible) {\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-reload' : null\"\n class=\"mr-8\"\n [title]=\"'core.component.table.reload' | translate\"\n prefixIcon=\"refresh\"\n (click)=\"reload()\"\n [disabled]=\"!_items.length\"\n type=\"link\">\n </sd-button>\n }\n </ng-container>\n @if (_export && _items.length) {\n @if (_export.type === 'custom') {\n <sd-button [autoId]=\"_autoId ? _autoId + '-export' : null\" class=\"mr-8\" [title]=\"_exportTitle\" prefixIcon=\"get_app\" (click)=\"exportCustom()\" type=\"link\"> </sd-button>\n } @else {\n @if (_export.visible) {\n @if (_exporting) {\n <sd-button [autoId]=\"_autoId ? _autoId + '-export' : null\" class=\"mr-8\" [loading]=\"_exporting\" [title]=\"_exportTitle\" prefixIcon=\"get_app\" type=\"link\"> </sd-button>\n } @else {\n @if (_export.visible === 'ALL' || !_export.visible) {\n <sd-button [autoId]=\"_autoId ? _autoId + '-export' : null\" class=\"mr-8\" [title]=\"_exportTitle\" prefixIcon=\"get_app\" [matMenuTriggerFor]=\"menu\" type=\"link\"> </sd-button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item [attr.data-autoid]=\"_autoId ? _autoId + '-export-excel' : null\" (click)=\"exportExcel()\" type=\"button\">\n <mat-icon fontSet=\"material-icons-outlined\">file_download</mat-icon>\n <span> {{ 'core.component.table.export-excel' | translate }}</span>\n </button>\n <button mat-menu-item [attr.data-autoid]=\"_autoId ? _autoId + '-export-csv' : null\" (click)=\"exportCSV()\" type=\"button\">\n <mat-icon fontSet=\"material-icons-outlined\">file_download</mat-icon>\n <span> {{ 'core.component.table.export-csv' | translate }}</span>\n </button>\n </mat-menu>\n } @else if (_export.visible === 'EXCEL') {\n <sd-button [autoId]=\"_autoId ? _autoId + '-export-excel' : null\" class=\"mr-8\" [title]=\"_exportTitle\" prefixIcon=\"get_app\" (click)=\"exportExcel()\" type=\"link\"> </sd-button>\n } @else if (_export.visible === 'CSV') {\n <sd-button [autoId]=\"_autoId ? _autoId + '-export-csv' : null\" class=\"mr-8\" [title]=\"_exportTitle\" prefixIcon=\"get_app\" (click)=\"exportCSV()\" type=\"link\"> </sd-button>\n }\n }\n }\n }\n }\n\n <ng-container *sdDesktop>\n @if (_configComponent) {\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-config' : null\"\n class=\"mr-8\"\n [title]=\"'core.component.table.setup-short' | translate\"\n prefixIcon=\"settings\"\n (click)=\"_configComponent.open()\"\n type=\"link\">\n </sd-button>\n }\n </ng-container>\n <!-- Mobile: n\u00FAt Filter m\u1EDF mobile-filter drawer (inline filter \u1EA9n tr\u00EAn mobile) -->\n <ng-container *sdMobile>\n @if (!_tableOption.filter?.disabled && _mobileFilter) {\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-mobile-filter-open' : null\"\n class=\"mr-8\"\n prefixIcon=\"filter_alt\"\n (click)=\"_mobileFilter.open()\"\n type=\"link\">\n </sd-button>\n }\n </ng-container>\n </div>\n @let hidePaginator = !_tableOption.paginate?.pageSize || _total === undefined || _total <= _tableOption.paginate?.pageSize!;\n <!-- Desktop paginator: full config (pageSize dropdown, first/last buttons) -->\n <ng-container *sdDesktop>\n <mat-paginator\n [class.d-none]=\"_tableOption.paginate?.hidden || hidePaginator\"\n [length]=\"_total\"\n [pageSize]=\"_tableOption.paginate?.pageSize\"\n [pageSizeOptions]=\"_tableOption.paginate?.pages!\"\n [showFirstLastButtons]=\"_tableOption.paginate?.showFirstLastButtons\"\n [hidePageSize]=\"_tableOption.paginate?.hidePageSize\"></mat-paginator>\n </ng-container>\n <!-- Mobile paginator: \u1EA9n pageSize dropdown + first/last buttons \u0111\u1EC3 ti\u1EBFt ki\u1EC7m kh\u00F4ng gian -->\n <ng-container *sdMobile>\n <mat-paginator\n [class.d-none]=\"_tableOption.paginate?.hidden || hidePaginator\"\n [length]=\"_total\"\n [pageSize]=\"_tableOption.paginate?.pageSize\"\n [showFirstLastButtons]=\"false\"\n hidePageSize></mat-paginator>\n </ng-container>\n <!-- \"\u0110ang hi\u1EC3n th\u1ECB...\" ch\u1EC9 desktop -->\n <ng-container *sdDesktop>\n @if (!_tableOption.paginate?.hidden && hidePaginator && _total !== undefined && _total > 0) {\n <div class=\"T14R pr-16\">\n {{ 'core.component.table.showing' | translate }} <span class=\"T14M ml-2\">1-{{ _total }}/{{ _total }}</span>\n </div>\n }\n </ng-container>\n </div>\n </div>\n <selector-action [autoId]=\"_autoId\" [tableOption]=\"_tableOption\" [selectedTableItems]=\"_selectedTableItems\" (clear)=\"onClearSelection(groupedItems)\" />\n @if (_tableOption.config?.visible) {\n <config [autoId]=\"_autoId\" [tableOption]=\"_tableOption\" />\n }\n <!-- Mobile filter drawer \u2014 ch\u1EC9 instantiate khi c\u1EA7n render tr\u00EAn mobile -->\n @if (!_tableOption.filter?.disabled && filterRegister) {\n <mobile-filter\n [autoId]=\"_autoId\"\n [filter]=\"_tableOption.filter!\"\n [externalFilters]=\"_tableOption.filter?.externalFilters!\"\n [columns]=\"_configuration.firstColumns\"\n [filterDefs]=\"sdFilterDefs()\"\n [filterRegister]=\"filterRegister\"\n [cacheValues]=\"cacheValues\">\n </mobile-filter>\n }\n}\n", styles: [":host{display:flex;flex-direction:column;overflow:auto;width:100%;height:100%}:host .c-header-title{height:40px;display:flex;align-items:center}:host .c-container{position:relative;min-height:50px;display:flex;flex-direction:column;flex:1}:host .c-container .c-table{position:relative;flex:1;display:flex;flex-direction:column}:host .c-container .c-table table{border-collapse:separate;width:100%}:host .c-container .c-table table tr.c-first-header.mat-mdc-header-row{height:40px}:host .c-container .c-table table tr.c-second-header.mat-mdc-header-row{height:40px}:host .c-container .c-table table tr.c-detail-row{height:0}:host .c-container .c-table table tr.c-row.activated{background-color:#e5ecff}:host .c-container .c-table table tr.c-row.selected{background-color:#eef2ff}:host .c-container .c-table table tr.c-row:not(.selected):not(.activated):hover{background-color:#f5f5f5}:host .c-container .c-table table tr.c-row td{border-bottom-width:0}:host .c-container .c-table table tr.c-row.c-expandable{cursor:pointer}:host .c-container .c-table table tr.c-row.c-expandable:hover{background:#f5f5f5}:host .c-container .c-table table th.mat-mdc-header-cell{background-color:#f2f3f4;border-bottom:0!important}:host .c-container .c-table table td.mat-mdc-cell,:host .c-container .c-table table td.mat-mdc-footer-cell,:host .c-container .c-table table th.mat-mdc-header-cell{border-bottom-color:#f2f2f2!important}:host .c-container .c-table .c-th{vertical-align:middle;font-weight:500;font-size:14px;line-height:20px;color:#212121}:host .c-container .c-table .c-th.sd-col-resize-host{position:relative}:host .c-container .c-table .c-th.sd-resizing{-webkit-user-select:none;user-select:none}:host .c-container .c-table .c-th .sd-col-resize-handle{position:absolute;top:0;right:0;width:6px;height:100%;cursor:col-resize;-webkit-user-select:none;user-select:none;z-index:2}:host .c-container .c-table .c-th .sd-col-resize-handle:hover{background:#00000014}:host .c-container .c-table .c-td:first{padding-left:10px}:host .c-container .c-table .c-no-data-row{flex:1;width:100%;position:sticky;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center}:host .c-container .c-table .c-no-data-row .c-image{margin-bottom:16px;width:96px}:host .c-container .c-loading{position:absolute;inset:0 0 56px;background:#00000026;z-index:2;display:flex;align-items:center;justify-content:center}:host .c-container .c-paginator{display:flex;flex-direction:row;justify-content:space-between;align-items:center;background-color:#fff}:host .c-container .c-paginator .c-action{padding:5px}:host .c-container .c-empty{text-align:center;background-color:#fff;border:none!important}:host .c-container .c-empty mat-icon{font-size:150px;margin-top:30px;margin-bottom:30px;opacity:.2;width:auto;height:auto}:host button.c-btn-add{background-color:#fff;box-shadow:0 2px 4px #2f313629}:host .lds-ring{display:inline-block;position:relative;width:40px;height:40px}:host .lds-ring div{box-sizing:border-box;display:block;position:absolute;width:32px;height:32px;margin:4px;border:4px solid #cef;border-radius:50%;animation:lds-ring 1.2s cubic-bezier(.5,0,.5,1) infinite;border-color:#cef transparent transparent transparent}:host .lds-ring div:nth-child(1){animation-delay:-.45s}:host .lds-ring div:nth-child(2){animation-delay:-.3s}:host .lds-ring div:nth-child(3){animation-delay:-.15s}@keyframes lds-ring{0%{transform:rotate(0)}to{transform:rotate(360deg)}}:host .sd-reorder-header,:host .sd-reorder-cell{width:40px;min-width:40px;padding:0 4px;box-sizing:border-box}:host .sd-reorder-cell mat-icon{cursor:grab;color:#00000061;display:flex;align-items:center}:host .sd-reorder-cell mat-icon:active{cursor:grabbing}:host .sd-reorder-cell mat-icon.sd-reorder-disabled{opacity:.3;cursor:not-allowed;pointer-events:none}:host ::ng-deep .mat-sort-header-content{display:block;text-align:left;width:100%}:host ::ng-deep .mat-select-arrow{color:#a6a6a6}:host ::ng-deep .mat-sort-header-disabled{background-image:none!important;cursor:default!important;padding-right:0!important}:host ::ng-deep .mat-sort-header-container{align-items:start!important}:host ::ng-deep .mat-sort-header-arrow{margin-top:4px!important}:host ::ng-deep .mat-sort-header-arrow{display:none!important}:host ::ng-deep .mat-sort-header{cursor:pointer}:host ::ng-deep .mat-sort-header[aria-sort]{background-repeat:no-repeat;background-position:center right 0;background-size:16px 16px;cursor:pointer;padding-right:24px}:host ::ng-deep .mat-sort-header[aria-sort=none]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath fill='%237A7A7A' d='M12 5.83L15.17 9l1.41-1.41L12 3 7.41 7.59 8.83 9 12 5.83zm0 12.34L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15 12 18.17z'/%3E%3C/svg%3E\")}:host ::ng-deep .mat-sort-header[aria-sort=ascending]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath fill='%237A7A7A' d='M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z'/%3E%3C/svg%3E\")}:host ::ng-deep .mat-sort-header[aria-sort=descending]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath fill='%237A7A7A' d='M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z'/%3E%3C/svg%3E\")}:host ::ng-deep .c-paginator .mat-mdc-paginator .mdc-text-field--outlined .mat-mdc-form-field-infix,:host ::ng-deep .c-paginator .mat-mdc-paginator .mdc-text-field--no-label .mat-mdc-form-field-infix{padding-top:2px;padding-bottom:2px;min-height:32px}:host ::ng-deep .c-paginator .mat-mdc-paginator-range-label{margin:0 4px}:host ::ng-deep .c-paginator .mat-mdc-paginator-page-size-select mat-select{margin-top:2px!important}:host ::ng-deep .cdk-drag-preview{box-shadow:0 4px 12px #00000026;background:#fff;display:table;width:100%}:host ::ng-deep .cdk-drag-preview td{padding:0 8px;border-bottom:1px solid rgba(0,0,0,.12)}:host ::ng-deep .cdk-drag-placeholder{opacity:0}:host ::ng-deep .cdk-drop-list-dragging tr.c-row:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}:host ::ng-deep .sd-selection-cell{width:42px;min-width:42px;max-width:42px}:host ::ng-deep .sd-filler-cell{width:auto;min-width:0;padding:0!important;border:0}:host ::ng-deep .sd-group-header-cell{background:#f5f7fc;border-top:1px solid #e6e9f2;border-bottom:1px solid #e6e9f2}:host ::ng-deep .sd-group-row{min-height:36px}:host ::ng-deep .sd-group-selection-slot{width:42px;min-width:42px;max-width:42px}:host ::ng-deep .c-selection{transform:scale(.85);transform-origin:center}:host ::ng-deep .sd-tree-line{display:inline-flex;align-items:center;min-height:32px}:host ::ng-deep .sd-tree-indent{flex:0 0 auto}:host ::ng-deep .sd-tree-toggle-slot{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px}:host ::ng-deep .sd-tree-toggle-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:0;background:transparent;border-radius:50%;cursor:pointer;color:var(--sd-text-secondary, #5b6472);transition:background-color .15s ease}:host ::ng-deep .sd-tree-toggle-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}:host ::ng-deep .sd-tree-toggle-btn:hover{background-color:#0f172a14}:host ::ng-deep .sd-tree-spinner{transform:scale(.45)}:host ::ng-deep .sd-tree-stt{margin-left:2px;white-space:nowrap}:host ::ng-deep .sd-tree-index-cell{text-align:left!important;white-space:nowrap;padding-left:8px;padding-right:8px}:host ::ng-deep .sd-tree-index-header{padding-left:32px}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-0{background:#fbfcfe}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-0>td{font-weight:500}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-1{background:#fff}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-2{background:#fafbfd}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-3{background:#f6f8fb}:host ::ng-deep .sd-stt-root{font-weight:700;color:#1f2330}\n", ":host ::ng-deep .sticky-shadow-right{overflow:visible!important}:host ::ng-deep .sticky-shadow-right:after{content:\"\";position:absolute;top:0;right:0;bottom:0;width:8px;transform:translate(100%);background:linear-gradient(to right,rgba(0,0,0,.12),transparent);pointer-events:none}:host ::ng-deep .sticky-shadow-left{overflow:visible!important}:host ::ng-deep .sticky-shadow-left:after{content:\"\";position:absolute;top:0;left:0;bottom:0;width:8px;transform:translate(-100%);background:linear-gradient(to left,rgba(0,0,0,.12),transparent);pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.JsonPipe, name: "json" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i4$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatFooterCellDef, selector: "[matFooterCellDef]" }, { kind: "directive", type: i6.MatFooterRowDef, selector: "[matFooterRowDef]", inputs: ["matFooterRowDef", "matFooterRowDefSticky"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "directive", type: i6.MatFooterCell, selector: "mat-footer-cell, td[mat-footer-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i6.MatFooterRow, selector: "mat-footer-row, tr[mat-footer-row]", exportAs: ["matFooterRow"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i7.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i7.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i8.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i9.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "component", type: i10.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i11.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i11.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i11.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: DesktopCommand, selector: "desktop-command", inputs: ["autoId", "item", "itemIndex", "commands"] }, { kind: "component", type: DesktopCellComponent, selector: "desktop-cell", inputs: ["column", "item", "cellDef"] }, { kind: "component", type: ColumnTitleComponent, selector: "column-title", inputs: ["column", "titleDef"] }, { kind: "component", type: ExternalFilterComponent, selector: "external-filter", inputs: ["autoId", "filter", "externalFilters", "filterRegister"] }, { kind: "component", type: ConfigComponent, selector: "config", inputs: ["autoId", "tableOption"], outputs: ["changes"] }, { kind: "component", type: ColumnFilterComponent, selector: "column-filter", inputs: ["autoId", "column", "columnFilter", "cacheValues", "value", "operator", "isMobile"], outputs: ["operatorChange", "filterChange", "filterCommit"] }, { kind: "component", type: MobileFilterComponent, selector: "mobile-filter", inputs: ["autoId", "filter", "externalFilters", "columns", "filterDefs", "filterRegister", "cacheValues"] }, { kind: "pipe", type: SdGroupPipe, name: "sdGroup" }, { kind: "pipe", type: SdTreePipe, name: "sdTree" }, { kind: "pipe", type: SdSelectionVisiblePipe, name: "selectionVisible" }, { kind: "pipe", type: SdSelectionVisibleSelectAllPipe, name: "selectionVisibleSelectAll" }, { kind: "pipe", type: SdSelectionDisabledPipe, name: "selectionDisabled" }, { kind: "directive", type: SdScrollDirective, selector: "[sdScroll]" }, { kind: "directive", type: SdDesktopDirective, selector: "[sdDesktop]" }, { kind: "directive", type: SdMobileDirective, selector: "[sdMobile]" }, { kind: "directive", type: SdHoverCopyDirective, selector: "[sdHoverCopy]", inputs: ["sdHoverCopy", "sdHoverCopyDisabled"] }, { kind: "component", type: SelectorActionComponent, selector: "selector-action", inputs: ["autoId", "tableOption", "selectedTableItems"], outputs: ["clear"] }, { kind: "directive", type: StickyShadowDirective, selector: "[stickyShadow]" }, { kind: "directive", type: SdColumnResizeDirective, selector: "[sdColumnResize]", inputs: ["sdColumnResize", "minWidth", "maxWidth"], outputs: ["resizeEnd"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], animations: [
|
|
4275
4402
|
trigger('detailExpand', [
|
|
4276
4403
|
state('collapsed', style({ height: '0', minHeight: '0', visibility: 'hidden' })),
|
|
4277
4404
|
state('expanded', style({ height: '*', visibility: 'visible' })),
|
|
@@ -4336,7 +4463,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
4336
4463
|
StickyShadowDirective,
|
|
4337
4464
|
SdColumnResizeDirective,
|
|
4338
4465
|
TranslatePipe,
|
|
4339
|
-
], template: "@let _configuration = configuration();\n@let _tableOption = tableOption()!;\n@let _loading = loading();\n@let _items = items();\n@let _total = total();\n@let _isFiltered = isFiltered();\n@let _requireFiltered = requireFiltered();\n@let _export = _tableOption?.export;\n@let _exporting = exporting();\n@let _titleDef = titleDef();\n\n@if (_configuration) {\n @if (!_tableOption.filter?.disabled && !!_tableOption.filter?.externalFilters?.length && filterRegister) {\n <external-filter\n class=\"mb-16\"\n [autoId]=\"autoId()\"\n [filterRegister]=\"filterRegister\"\n [filter]=\"_tableOption.filter!\"\n [externalFilters]=\"_tableOption.filter?.externalFilters!\">\n </external-filter>\n }\n @let groupedItems = _items | sdTree: _tableOption.tree : treeRevision() | sdGroup: _tableOption : groupExpandState;\n <ng-content select=\"[sdTableTop]\"></ng-content>\n <div class=\"c-container\">\n @if (_loading) {\n <div class=\"c-loading\">\n <mat-spinner></mat-spinner>\n </div>\n }\n <div\n class=\"c-table\"\n sdScroll\n stickyShadow\n [style.max-height]=\"_tableOption.style?.maxHeight\"\n [style.min-height]=\"_tableOption.style?.minHeight\">\n <table\n mat-table\n [dataSource]=\"groupedItems\"\n [trackBy]=\"trackBy\"\n matSort\n [matSortDisabled]=\"!_tableOption.sort?.enable\"\n multiTemplateDataRows\n cdkDropList\n [cdkDropListData]=\"groupedItems\"\n [cdkDropListDisabled]=\"!_tableOption.rowReorder?.enabled\"\n [cdkDropListSortPredicate]=\"reorderSortPredicate\"\n (cdkDropListDropped)=\"onReorderDrop($event)\">\n @if (_tableOption.rowReorder?.enabled) {\n <ng-container matColumnDef=\"sdReorder\">\n <th mat-header-cell *matHeaderCellDef class=\"sd-reorder-header\" [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td mat-cell *matCellDef=\"let row\" class=\"sd-reorder-cell\">\n @if (!row.meta?.group?.items?.length) {\n <mat-icon cdkDragHandle [class.sd-reorder-disabled]=\"isReorderDisabled(row)\">\n {{ _tableOption.rowReorder?.icon || 'drag_indicator' }}\n </mat-icon>\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n }\n\n @if (_tableOption.tree) {\n <ng-container matColumnDef=\"sdTreeToggle\">\n <th\n mat-header-cell\n *matHeaderCellDef\n class=\"sd-tree-toggle-cell p-0\"\n [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td mat-cell *matCellDef=\"let row\" class=\"sd-tree-toggle-cell p-0\">\n @if (row.meta?.tree) {\n <div\n class=\"sd-tree-toggle-inner\"\n [style.padding-left.px]=\"(row.meta.tree.level ?? 0) * (_tableOption.tree?.indentSize ?? 20)\">\n @if (row.meta.tree.hasChildren) {\n @if (row.meta.tree.isExpanding) {\n <div class=\"lds-ring sd-tree-spinner\">\n <div></div>\n <div></div>\n <div></div>\n <div></div>\n </div>\n } @else {\n <sd-button\n type=\"link\"\n [prefixIcon]=\"row.meta.tree.isExpanded ? 'expand_less' : 'expand_more'\"\n (click)=\"onTreeToggle(row); $event.stopPropagation()\"\n width=\"35px\">\n </sd-button>\n }\n }\n </div>\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n }\n\n <ng-container matColumnDef=\"sdSubInformation\" sticky>\n <td class=\"p-0\" mat-cell *matCellDef=\"let item\" [attr.colspan]=\"_configuration.displayedColumns.length\">\n @if (sdSubInformation()?.templateRef) {\n @if (_tableOption.expand?.always) {\n <ng-container *ngTemplateOutlet=\"sdSubInformation()?.templateRef!; context: { item: item }\"> </ng-container>\n } @else {\n <div [@detailExpand]=\"item.isExpanded ? 'expanded' : 'collapsed'\">\n @if (item.isExpanded) {\n <ng-container *ngTemplateOutlet=\"sdSubInformation()?.templateRef!; context: { item: item }\"> </ng-container>\n }\n </div>\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n\n <ng-container matColumnDef=\"sdSubInformationAction\" stickyEnd>\n <th class=\"p-0\" mat-header-cell *matHeaderCellDef style=\"width: 1px\" [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td mat-cell *matCellDef=\"let element\">\n @if (!element.isExpanding && !_tableOption.expand?.always) {\n <sd-button\n type=\"link\"\n [prefixIcon]=\"element.isExpanded ? 'expand_less' : 'expand_more'\"\n (click)=\"onExpand(element)\"\n width=\"35px\">\n </sd-button>\n }\n @if (element.isExpanding) {\n <div class=\"lds-ring\">\n <div></div>\n <div></div>\n <div></div>\n <div></div>\n </div>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"sdSelection\" sticky>\n <th class=\"sd-selection-cell p-0\" mat-header-cell *matHeaderCellDef [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\">\n @let visibleSelectAll = _items | selectionVisibleSelectAll: _tableOption.selector | async;\n @if (visibleSelectAll) {\n <mat-checkbox\n class=\"c-selection px-8\"\n color=\"primary\"\n [ngModel]=\"isSelectAll()\"\n (ngModelChange)=\"isSelectAll.set($event)\"\n (change)=\"onSelectAll()\">\n </mat-checkbox>\n }\n </th>\n\n <td class=\"sd-selection-cell p-0\" mat-cell *matCellDef=\"let item\">\n @let visible = item | selectionVisible: _tableOption.selector;\n @if (visible) {\n @if (_tableOption.selector?.single) {\n <mat-radio-button\n class=\"c-selection px-8\"\n color=\"primary\"\n [checked]=\"item.meta.selector.isSelected\"\n (change)=\"item.meta.selector.isSelected = true; onSelect(item)\"\n [disabled]=\"selectedTableItems() | selectionDisabled: item : _tableOption.selector!\">\n </mat-radio-button>\n } @else {\n <mat-checkbox\n class=\"c-selection px-8\"\n color=\"primary\"\n [(ngModel)]=\"item.meta.selector.isSelected\"\n (change)=\"onSelect(item)\"\n [disabled]=\"selectedTableItems() | selectionDisabled: item : _tableOption.selector!\">\n </mat-checkbox>\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n @if (_tableOption.index?.enabled) {\n @let _pageOffset = pageOffset();\n @let _indexWidth = _tableOption.index?.width || '50px';\n <ng-container matColumnDef=\"sdIndex\" sticky>\n <th\n class=\"text-center p-0\"\n mat-header-cell\n *matHeaderCellDef\n [style.width]=\"_indexWidth\"\n [style.min-width]=\"_indexWidth\"\n [style.max-width]=\"_indexWidth\"\n [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\">\n {{ _tableOption.index?.title || '#' }}\n </th>\n <td class=\"text-center px-4\" mat-cell *matCellDef=\"let item; let i = dataIndex\" [style.min-width]=\"_indexWidth\" [style.max-width]=\"_indexWidth\">\n @if (!item?.meta?.group?.isGroupHeader) {\n @if (_tableOption.tree && item?.meta?.tree?.indexPath?.length) {\n <!-- why: tree \u2192 STT ph\u00E2n c\u1EA5p 1, 1.2, 1.2.1 (indexPath join '.'). T\u00F4 \u0111\u1EADm root (level 0). -->\n <span [class.sd-stt-root]=\"(item.meta.tree.level ?? 0) === 0\">\n {{ item.meta.tree.indexPath.join('.') }}\n </span>\n } @else {\n {{ _pageOffset + i + 1 }}\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef [style.min-width]=\"_indexWidth\" [style.max-width]=\"_indexWidth\"></td>\n </ng-container>\n }\n <ng-container\n matColumnDef=\"sdCommand\"\n [sticky]=\"_tableOption.command?.align !== 'right'\"\n [stickyEnd]=\"_tableOption.command?.align === 'right'\">\n <th class=\"p-0\" mat-header-cell *matHeaderCellDef style=\"width: 50px\" [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td class=\"px-8\" mat-cell *matCellDef=\"let item\">\n <desktop-command\n [autoId]=\"autoId()\"\n [commands]=\"_tableOption.command?.commands || _tableOption.commands || []\"\n [item]=\"item\"\n [itemIndex]=\"groupedItems.indexOf(item)\"></desktop-command>\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n <!-- why: group header row d\u00F9ng matRowDef RI\u00CANG v\u1EDBi column list ['sdGroupHeader']\n qua predicate when:isGroupHeaderRow. Cell colspan = displayedColumns.length \u0111\u1EC3\n span TO\u00C0N B\u1ED8 width data row. Tr\u00E1nh colspan trick overflow row khi c\u00E1c TD kh\u00E1c render. -->\n <ng-container matColumnDef=\"sdGroupHeader\">\n <td\n class=\"p-0 sd-group-header-cell\"\n mat-cell\n *matCellDef=\"let item\"\n [attr.colspan]=\"sdGroupColspan()\">\n <div class=\"sd-group-row d-flex align-items-center py-8\">\n <!-- why: slot 42px kh\u1EDBp width v\u1EDBi sd-selection-cell data row \u2192 checkbox group c\u0103n TR\u1EE4C d\u1ECDc \u0111\u00FAng v\u1EDBi checkbox data. -->\n @if (_tableOption.selector?.visible && !_tableOption.selector?.single) {\n <div class=\"sd-group-selection-slot d-flex align-items-center justify-content-center\">\n <mat-checkbox\n class=\"c-selection\"\n color=\"primary\"\n [checked]=\"isGroupAllSelected(item)\"\n [indeterminate]=\"isGroupIndeterminate(item)\"\n (change)=\"onSelectGroup(item, $event.checked)\">\n </mat-checkbox>\n </div>\n } @else {\n <div class=\"sd-group-selection-slot\"></div>\n }\n @if (_tableOption.group?.collapsible) {\n <sd-button\n type=\"link\"\n [prefixIcon]=\"item.meta.group.isExpanded ? 'expand_more' : 'chevron_right'\"\n width=\"32px\"\n (click)=\"toggleGroupExpand(item)\">\n </sd-button>\n }\n @if (sdGroupDef()?.templateRef; as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: groupContext(item)\"></ng-container>\n } @else {\n <span class=\"T14R\">{{ item.meta.group.values | json }}</span>\n }\n </div>\n </td>\n </ng-container>\n <!-- why: filler column \u1EDF cu\u1ED1i h\u1EA5p th\u1EE5 leftover space khi t\u1ED5ng width c\u00E1c c\u1ED9t data < container \u2014 gi\u1EEF cho selection/STT/command kh\u00F4ng b\u1ECB d\u00E3n. -->\n <ng-container matColumnDef=\"sdFiller\">\n <th class=\"sd-filler-cell p-0\" mat-header-cell *matHeaderCellDef [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td class=\"sd-filler-cell p-0\" mat-cell *matCellDef=\"let item\"></td>\n <td class=\"sd-filler-cell p-0\" mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n @for (column of _configuration.firstColumns; track column.field) {\n <ng-container [matColumnDef]=\"column.field\" [sticky]=\"_configuration.fixedColumn[column.field]\">\n <th\n mat-header-cell\n *matHeaderCellDef\n class=\"px-8 py-8 c-th\"\n [sdColumnResize]=\"!!_tableOption.config?.resizable && column.type !== 'children'\"\n [minWidth]=\"column.minWidth\"\n [maxWidth]=\"column.maxWidth\"\n (resizeEnd)=\"onColumnResize(column.field, $event)\"\n [style.width]=\"column.width\"\n [style.min-width]=\"column.minWidth || column.width\"\n [style.max-width]=\"column.maxWidth\"\n [attr.rowspan]=\"_configuration.multipleHeader && column.type !== 'children' ? 2 : 1\"\n [attr.colspan]=\"column.type === 'children' ? column.children.length : 1\">\n <div>\n @if (column.type === 'children') {\n <div\n aria-hidden=\"true\"\n class=\"c-header-title\"\n [class.justify-content-end]=\"column.align === 'right'\"\n [class.text-right]=\"column.align === 'right'\">\n <column-title [column]=\"column\" [titleDef]=\"_titleDef[column.field]\"></column-title>\n </div>\n } @else {\n <div\n aria-hidden=\"true\"\n mat-sort-header\n class=\"c-header-title\"\n [class.justify-content-end]=\"column.align === 'right'\"\n [class.text-right]=\"column.align === 'right'\"\n [disabled]=\"!column.sortable\">\n <column-title [column]=\"column\" [titleDef]=\"_titleDef[column.field]\"></column-title>\n </div>\n @let hideInlineFilter =\n _tableOption.filter?.disabled ||\n _tableOption.filter?.hideInlineFilter === true ||\n (_tableOption.filter?.hideInlineFilter === 'auto' && !_isFiltered && _total! <= 10);\n @if (!hideInlineFilter) {\n <ng-container *sdDesktop>\n <column-filter\n [autoId]=\"autoId()\"\n [value]=\"columnFilter?.[column.field!]!\"\n [operator]=\"columnOperator[column.field]\"\n (operatorChange)=\"columnOperator[column.field] = $event!; onOperatorChange(column, $event)\"\n [columnFilter]=\"columnFilter!\"\n [cacheValues]=\"cacheValues\"\n [column]=\"column\"\n (filterChange)=\"onFilterChange()\"\n (filterCommit)=\"onFilterCommit()\">\n </column-filter>\n </ng-container>\n }\n }\n </div>\n </th>\n <td\n class=\"c-td px-0\"\n [class.d-none]=\"column.type === 'children'\"\n mat-cell\n *matCellDef=\"let item\"\n [sdHoverCopy]=\"(item.data && item.data[column.field]) || ''\"\n [sdHoverCopyDisabled]=\"!column.cell?.copiable\">\n @if (column.type !== 'children' && !item?.meta?.group?.isGroupHeader) {\n <desktop-cell class=\"d-block px-8\" [column]=\"column\" [item]=\"item\" [cellDef]=\"cellDef()\"> </desktop-cell>\n }\n </td>\n <td mat-footer-cell *matFooterCellDef>\n <ng-container *ngIf=\"footerDef()[column.field]\">\n <ng-container *ngTemplateOutlet=\"footerDef()[column.field].templateRef; context: { items: _items, column: column }\">\n </ng-container>\n </ng-container>\n </td>\n </ng-container>\n }\n @for (column of _configuration.secondColumns; track column.field) {\n <ng-container [matColumnDef]=\"column.field\">\n <th\n mat-header-cell\n *matHeaderCellDef\n class=\"c-th px-8\"\n [style.width]=\"column.width\"\n [style.min-width]=\"column.minWidth || column.width\"\n [style.max-width]=\"column.maxWidth\">\n <div>\n <div\n aria-hidden=\"true\"\n mat-sort-header\n class=\"c-header-title\"\n [class.justify-content-end]=\"column.align === 'right'\"\n [class.text-right]=\"column.align === 'right'\"\n [disabled]=\"!column.sortable\">\n <column-title [column]=\"column\" [titleDef]=\"_titleDef[column.field]\"></column-title>\n </div>\n @if (!_tableOption.filter?.disabled && !_tableOption.filter?.hideInlineFilter && columnOperator) {\n <ng-container *sdDesktop>\n <column-filter\n [autoId]=\"autoId()\"\n [value]=\"columnFilter?.[column.field!]!\"\n [operator]=\"columnOperator[column.field]\"\n [columnFilter]=\"columnFilter!\"\n [cacheValues]=\"cacheValues\"\n [column]=\"column\"\n (operatorChange)=\"columnOperator[column.field] = $event!; onOperatorChange(column, $event)\"\n (filterChange)=\"onFilterChange()\"\n (filterCommit)=\"onFilterCommit()\">\n </column-filter>\n </ng-container>\n }\n </div>\n </th>\n <td\n class=\"c-td px-0\"\n mat-cell\n *matCellDef=\"let item\"\n [sdHoverCopy]=\"item[column.field]\"\n [sdHoverCopyDisabled]=\"!column.cell?.copiable\">\n @if (column.type !== 'children') {\n <desktop-cell class=\"d-block px-8\" [column]=\"column\" [item]=\"item\" [cellDef]=\"cellDef()\"> </desktop-cell>\n }\n </td>\n <td mat-footer-cell *matFooterCellDef>\n <ng-container *ngIf=\"footerDef()[column.field]\">\n <ng-container *ngTemplateOutlet=\"footerDef()[column.field].templateRef; context: { items: _items, column: column }\">\n </ng-container>\n </ng-container>\n </td>\n </ng-container>\n }\n <tr class=\"c-first-header\" mat-header-row *matHeaderRowDef=\"_configuration.firstHeaders; sticky: true\"></tr>\n @if (!!_configuration.secondHeaders.length) {\n <tr class=\"c-second-header\" mat-header-row *matHeaderRowDef=\"_configuration.secondHeaders; sticky: true\"></tr>\n }\n <!-- Group header row \u2014 ch\u1EC9 1 cell sdGroupHeader colspan to\u00E0n width. -->\n <tr\n mat-row\n *matRowDef=\"let row; columns: ['sdGroupHeader']; when: isGroupHeaderRow\"\n class=\"c-group-row-tr\"></tr>\n\n <!-- Data row \u2014 d\u00F9ng displayedColumns \u0111\u1EA7y \u0111\u1EE7; when isDataRow \u0111\u1EC3 lo\u1EA1i tr\u1EEB group header. -->\n <tr\n mat-row\n cdkDrag\n [cdkDragData]=\"row\"\n [cdkDragDisabled]=\"\n !_tableOption.rowReorder?.enabled ||\n !!row.meta?.group?.items?.length ||\n (row.meta?.tree?.level ?? 0) > 0 ||\n isReorderDisabled(row)\n \"\n *matRowDef=\"let row; columns: _configuration.displayedColumns; when: isDataRow\"\n matRipple\n class=\"c-row\"\n [class.sd-tree-row]=\"!!_tableOption.tree\"\n [ngClass]=\"_tableOption.tree ? 'sd-tree-level-' + (row.meta?.tree?.level ?? 0) : null\"\n [ngStyle]=\"rowStyle(row)\"\n [class.selected]=\"row.meta.selector.isSelected\"></tr>\n\n <!-- Sub-information row \u2014 also gated b\u1EDFi isDataRow \u0111\u1EC3 KH\u00D4NG render empty expand row d\u01B0\u1EDBi group header. -->\n <tr mat-row *matRowDef=\"let row; columns: ['sdSubInformation']; when: isDataRow\" class=\"c-detail-row\"></tr>\n @if (hasFooter() && !!_configuration.displayedFooters.length) {\n <tr mat-footer-row *matFooterRowDef=\"_configuration.displayedFooters; sticky: true\"></tr>\n }\n </table>\n @if (!_loading && !_total) {\n <div class=\"c-no-data-row\">\n @if (_isFiltered) {\n @if (tableConfiguration?.images?.filterEmpty) {\n <img class=\"c-image\" [src]=\"tableConfiguration!.images!.filterEmpty\" alt=\"filter-empty\" />\n } @else {\n <img class=\"c-image sd-image-filter-empty\" alt=\"filter-empty\" />\n }\n <div class=\"T16M\">{{ 'core.component.table.no-results' | translate }}</div>\n <div class=\"T16R text-secondary\">{{ 'core.component.table.no-results-hint' | translate }}</div>\n } @else {\n @if (_requireFiltered) {\n @if (tableConfiguration?.images?.filterRequired) {\n <img class=\"c-image\" [src]=\"tableConfiguration!.images!.filterRequired\" alt=\"filter-required\" />\n } @else {\n <img class=\"c-image sd-image-filter-required\" alt=\"filter-required\" />\n }\n <div class=\"T16R text-secondary\">{{ 'core.component.table.choose-filter-hint' | translate }}</div>\n } @else {\n @if (tableConfiguration?.images?.dataEmpty) {\n <img class=\"c-image\" [src]=\"tableConfiguration!.images!.dataEmpty\" alt=\"data-empty\" />\n } @else {\n <img class=\"c-image sd-image-data-empty\" alt=\"data-empty\" />\n }\n <div class=\"T16R text-secondary\">{{ 'core.component.table.no-data' | translate }}</div>\n }\n }\n </div>\n }\n </div>\n <div class=\"c-paginator\">\n <div class=\"c-action\">\n <ng-container *sdDesktop>\n @if (_tableOption.reload?.visible) {\n <sd-button\n class=\"mr-8\"\n [title]=\"'core.component.table.reload' | translate\"\n prefixIcon=\"refresh\"\n (click)=\"reload()\"\n [disabled]=\"!_items.length\"\n type=\"link\">\n </sd-button>\n }\n </ng-container>\n @if (_export && _items.length) {\n @if (_export.type === 'custom') {\n <sd-button class=\"mr-8\" [title]=\"exportTitle()\" prefixIcon=\"get_app\" (click)=\"exportCustom()\" type=\"link\"> </sd-button>\n } @else {\n @if (_export.visible) {\n @if (_exporting) {\n <sd-button class=\"mr-8\" [loading]=\"_exporting\" [title]=\"exportTitle()\" prefixIcon=\"get_app\" type=\"link\"> </sd-button>\n } @else {\n @if (_export.visible === 'ALL' || !_export.visible) {\n <sd-button class=\"mr-8\" [title]=\"exportTitle()\" prefixIcon=\"get_app\" [matMenuTriggerFor]=\"menu\" type=\"link\"> </sd-button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"exportExcel()\" type=\"button\">\n <mat-icon fontSet=\"material-icons-outlined\">file_download</mat-icon>\n <span> {{ 'core.component.table.export-excel' | translate }}</span>\n </button>\n <button mat-menu-item (click)=\"exportCSV()\" type=\"button\">\n <mat-icon fontSet=\"material-icons-outlined\">file_download</mat-icon>\n <span> {{ 'core.component.table.export-csv' | translate }}</span>\n </button>\n </mat-menu>\n } @else if (_export.visible === 'EXCEL') {\n <sd-button class=\"mr-8\" [title]=\"exportTitle()\" prefixIcon=\"get_app\" (click)=\"exportExcel()\" type=\"link\"> </sd-button>\n } @else if (_export.visible === 'CSV') {\n <sd-button class=\"mr-8\" [title]=\"exportTitle()\" prefixIcon=\"get_app\" (click)=\"exportCSV()\" type=\"link\"> </sd-button>\n }\n }\n }\n }\n }\n\n <ng-container *sdDesktop>\n @if (configComponent()) {\n <sd-button\n class=\"mr-8\"\n [title]=\"'core.component.table.setup-short' | translate\"\n prefixIcon=\"settings\"\n (click)=\"configComponent()!.open()\"\n type=\"link\">\n </sd-button>\n }\n </ng-container>\n <!-- Mobile: n\u00FAt Filter m\u1EDF mobile-filter drawer (inline filter \u1EA9n tr\u00EAn mobile) -->\n <ng-container *sdMobile>\n @if (!_tableOption.filter?.disabled && mobileFilter()) {\n <sd-button\n [autoId]=\"autoId() + 'mobile-filter-open'\"\n class=\"mr-8\"\n prefixIcon=\"filter_alt\"\n (click)=\"mobileFilter()!.open()\"\n type=\"link\">\n </sd-button>\n }\n </ng-container>\n </div>\n @let hidePaginator = !_tableOption.paginate?.pageSize || _total === undefined || _total <= _tableOption.paginate?.pageSize!;\n <!-- Desktop paginator: full config (pageSize dropdown, first/last buttons) -->\n <ng-container *sdDesktop>\n <mat-paginator\n [class.d-none]=\"_tableOption.paginate?.hidden || hidePaginator\"\n [length]=\"_total\"\n [pageSize]=\"_tableOption.paginate?.pageSize\"\n [pageSizeOptions]=\"_tableOption.paginate?.pages!\"\n [showFirstLastButtons]=\"_tableOption.paginate?.showFirstLastButtons\"\n [hidePageSize]=\"_tableOption.paginate?.hidePageSize\"></mat-paginator>\n </ng-container>\n <!-- Mobile paginator: \u1EA9n pageSize dropdown + first/last buttons \u0111\u1EC3 ti\u1EBFt ki\u1EC7m kh\u00F4ng gian -->\n <ng-container *sdMobile>\n <mat-paginator\n [class.d-none]=\"_tableOption.paginate?.hidden || hidePaginator\"\n [length]=\"_total\"\n [pageSize]=\"_tableOption.paginate?.pageSize\"\n [showFirstLastButtons]=\"false\"\n hidePageSize></mat-paginator>\n </ng-container>\n <!-- \"\u0110ang hi\u1EC3n th\u1ECB...\" ch\u1EC9 desktop -->\n <ng-container *sdDesktop>\n @if (!_tableOption.paginate?.hidden && hidePaginator && _total !== undefined && _total > 0) {\n <div class=\"T14R pr-16\">\n {{ 'core.component.table.showing' | translate }} <span class=\"T14M ml-2\">1-{{ _total }}/{{ _total }}</span>\n </div>\n }\n </ng-container>\n </div>\n </div>\n <selector-action [tableOption]=\"_tableOption\" [selectedTableItems]=\"selectedTableItems()\" (clear)=\"onClearSelection(groupedItems)\" />\n @if (_tableOption.config?.visible) {\n <config [tableOption]=\"_tableOption\" />\n }\n <!-- Mobile filter drawer \u2014 ch\u1EC9 instantiate khi c\u1EA7n render tr\u00EAn mobile -->\n @if (!_tableOption.filter?.disabled && filterRegister) {\n <mobile-filter\n [autoId]=\"autoId()\"\n [filter]=\"_tableOption.filter!\"\n [externalFilters]=\"_tableOption.filter?.externalFilters!\"\n [columns]=\"_configuration.firstColumns\"\n [filterDefs]=\"sdFilterDefs()\"\n [filterRegister]=\"filterRegister\"\n [cacheValues]=\"cacheValues\">\n </mobile-filter>\n }\n}\n", styles: [":host{display:flex;flex-direction:column;overflow:auto;width:100%;height:100%}:host .c-header-title{height:40px;display:flex;align-items:center}:host .c-container{position:relative;min-height:50px;display:flex;flex-direction:column;flex:1}:host .c-container .c-table{position:relative;flex:1;display:flex;flex-direction:column}:host .c-container .c-table table{border-collapse:separate;width:100%}:host .c-container .c-table table tr.c-first-header.mat-mdc-header-row{height:40px}:host .c-container .c-table table tr.c-second-header.mat-mdc-header-row{height:40px}:host .c-container .c-table table tr.c-detail-row{height:0}:host .c-container .c-table table tr.c-row.activated{background-color:#e5ecff}:host .c-container .c-table table tr.c-row.selected{background-color:#eef2ff}:host .c-container .c-table table tr.c-row:not(.selected):not(.activated):hover{background-color:#f5f5f5}:host .c-container .c-table table tr.c-row td{border-bottom-width:0}:host .c-container .c-table table tr.c-row.c-expandable{cursor:pointer}:host .c-container .c-table table tr.c-row.c-expandable:hover{background:#f5f5f5}:host .c-container .c-table table th.mat-mdc-header-cell{background-color:#f2f3f4;border-bottom:0!important}:host .c-container .c-table table td.mat-mdc-cell,:host .c-container .c-table table td.mat-mdc-footer-cell,:host .c-container .c-table table th.mat-mdc-header-cell{border-bottom-color:#f2f2f2!important}:host .c-container .c-table .c-th{vertical-align:middle;font-weight:500;font-size:14px;line-height:20px;color:#212121}:host .c-container .c-table .c-th.sd-col-resize-host{position:relative}:host .c-container .c-table .c-th.sd-resizing{-webkit-user-select:none;user-select:none}:host .c-container .c-table .c-th .sd-col-resize-handle{position:absolute;top:0;right:0;width:6px;height:100%;cursor:col-resize;-webkit-user-select:none;user-select:none;z-index:2}:host .c-container .c-table .c-th .sd-col-resize-handle:hover{background:#00000014}:host .c-container .c-table .c-td:first{padding-left:10px}:host .c-container .c-table .c-no-data-row{flex:1;width:100%;position:sticky;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center}:host .c-container .c-table .c-no-data-row .c-image{margin-bottom:16px;width:96px}:host .c-container .c-loading{position:absolute;inset:0 0 56px;background:#00000026;z-index:2;display:flex;align-items:center;justify-content:center}:host .c-container .c-paginator{display:flex;flex-direction:row;justify-content:space-between;align-items:center;background-color:#fff}:host .c-container .c-paginator .c-action{padding:5px}:host .c-container .c-empty{text-align:center;background-color:#fff;border:none!important}:host .c-container .c-empty mat-icon{font-size:150px;margin-top:30px;margin-bottom:30px;opacity:.2;width:auto;height:auto}:host button.c-btn-add{background-color:#fff;box-shadow:0 2px 4px #2f313629}:host .lds-ring{display:inline-block;position:relative;width:40px;height:40px}:host .lds-ring div{box-sizing:border-box;display:block;position:absolute;width:32px;height:32px;margin:4px;border:4px solid #cef;border-radius:50%;animation:lds-ring 1.2s cubic-bezier(.5,0,.5,1) infinite;border-color:#cef transparent transparent transparent}:host .lds-ring div:nth-child(1){animation-delay:-.45s}:host .lds-ring div:nth-child(2){animation-delay:-.3s}:host .lds-ring div:nth-child(3){animation-delay:-.15s}@keyframes lds-ring{0%{transform:rotate(0)}to{transform:rotate(360deg)}}:host .sd-reorder-header,:host .sd-reorder-cell{width:40px;min-width:40px;padding:0 4px;box-sizing:border-box}:host .sd-reorder-cell mat-icon{cursor:grab;color:#00000061;display:flex;align-items:center}:host .sd-reorder-cell mat-icon:active{cursor:grabbing}:host .sd-reorder-cell mat-icon.sd-reorder-disabled{opacity:.3;cursor:not-allowed;pointer-events:none}:host ::ng-deep .mat-sort-header-content{display:block;text-align:left;width:100%}:host ::ng-deep .mat-select-arrow{color:#a6a6a6}:host ::ng-deep .mat-sort-header-disabled{background-image:none!important;cursor:default!important;padding-right:0!important}:host ::ng-deep .mat-sort-header-container{align-items:start!important}:host ::ng-deep .mat-sort-header-arrow{margin-top:4px!important}:host ::ng-deep .mat-sort-header-arrow{display:none!important}:host ::ng-deep .mat-sort-header{cursor:pointer}:host ::ng-deep .mat-sort-header[aria-sort]{background-repeat:no-repeat;background-position:center right 0;background-size:16px 16px;cursor:pointer;padding-right:24px}:host ::ng-deep .mat-sort-header[aria-sort=none]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath fill='%237A7A7A' d='M12 5.83L15.17 9l1.41-1.41L12 3 7.41 7.59 8.83 9 12 5.83zm0 12.34L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15 12 18.17z'/%3E%3C/svg%3E\")}:host ::ng-deep .mat-sort-header[aria-sort=ascending]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath fill='%237A7A7A' d='M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z'/%3E%3C/svg%3E\")}:host ::ng-deep .mat-sort-header[aria-sort=descending]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath fill='%237A7A7A' d='M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z'/%3E%3C/svg%3E\")}:host ::ng-deep .c-paginator .mat-mdc-paginator .mdc-text-field--outlined .mat-mdc-form-field-infix,:host ::ng-deep .c-paginator .mat-mdc-paginator .mdc-text-field--no-label .mat-mdc-form-field-infix{padding-top:2px;padding-bottom:2px;min-height:32px}:host ::ng-deep .c-paginator .mat-mdc-paginator-range-label{margin:0 4px}:host ::ng-deep .c-paginator .mat-mdc-paginator-page-size-select mat-select{margin-top:2px!important}:host ::ng-deep .cdk-drag-preview{box-shadow:0 4px 12px #00000026;background:#fff;display:table;width:100%}:host ::ng-deep .cdk-drag-preview td{padding:0 8px;border-bottom:1px solid rgba(0,0,0,.12)}:host ::ng-deep .cdk-drag-placeholder{opacity:0}:host ::ng-deep .cdk-drop-list-dragging tr.c-row:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}:host ::ng-deep .sd-selection-cell{width:42px;min-width:42px;max-width:42px}:host ::ng-deep .sd-filler-cell{width:auto;min-width:0;padding:0!important;border:0}:host ::ng-deep .sd-group-header-cell{background:#f5f7fc;border-top:1px solid #e6e9f2;border-bottom:1px solid #e6e9f2}:host ::ng-deep .sd-group-row{min-height:36px}:host ::ng-deep .sd-group-selection-slot{width:42px;min-width:42px;max-width:42px}:host ::ng-deep .c-selection{transform:scale(.85);transform-origin:center}:host ::ng-deep .sd-tree-toggle-cell{width:40px;min-width:40px;max-width:100px}:host ::ng-deep .sd-tree-toggle-inner{display:flex;align-items:center;min-height:40px}:host ::ng-deep .sd-tree-spinner{transform:scale(.5);margin:0 auto}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-0{background:#fbfcfe}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-0>td{font-weight:500}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-1{background:#fff}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-2{background:#fafbfd}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-3{background:#f6f8fb}:host ::ng-deep tr.c-row.sd-tree-row:not(.sd-tree-level-0)>td.sd-tree-toggle-cell .sd-tree-toggle-inner{border-left:1px dashed #d8dde6}:host ::ng-deep .sd-stt-root{font-weight:700;color:#1f2330}\n", ":host ::ng-deep .sticky-shadow-right{overflow:visible!important}:host ::ng-deep .sticky-shadow-right:after{content:\"\";position:absolute;top:0;right:0;bottom:0;width:8px;transform:translate(100%);background:linear-gradient(to right,rgba(0,0,0,.12),transparent);pointer-events:none}:host ::ng-deep .sticky-shadow-left{overflow:visible!important}:host ::ng-deep .sticky-shadow-left:after{content:\"\";position:absolute;top:0;left:0;bottom:0;width:8px;transform:translate(-100%);background:linear-gradient(to left,rgba(0,0,0,.12),transparent);pointer-events:none}\n"] }]
|
|
4466
|
+
], template: "@let _configuration = configuration();\n@let _tableOption = tableOption()!;\n@let _loading = loading();\n@let _items = items();\n@let _total = total();\n@let _isFiltered = isFiltered();\n@let _requireFiltered = requireFiltered();\n@let _export = _tableOption?.export;\n@let _exporting = exporting();\n@let _titleDef = titleDef();\n@let _autoId = autoId();\n@let _exportTitle = exportTitle();\n@let _cellDef = cellDef();\n@let _footerDef = footerDef();\n@let _sdSubInformation = sdSubInformation();\n@let _configComponent = configComponent();\n@let _mobileFilter = mobileFilter();\n@let _selectedTableItems = selectedTableItems();\n<!-- why: tree kh\u00F4ng c\u00F2n c\u1ED9t ri\u00EAng \u2014 khi KH\u00D4NG c\u00F3 c\u1ED9t Index, icon expand nh\u00FAng v\u00E0o\n c\u1ED9t data \u0111\u1EA7u ti\u00EAn. \u0110\u00E2y l\u00E0 field c\u1EE7a c\u1ED9t \u0111\u00F3 (null n\u1EBFu c\u00F3 Index ho\u1EB7c kh\u00F4ng tree). -->\n@let _treeFirstField = _tableOption?.tree && !_tableOption.index?.enabled ? _configuration?.firstColumns?.[0]?.field : null;\n\n@if (_configuration) {\n @if (!_tableOption.filter?.disabled && !!_tableOption.filter?.externalFilters?.length && filterRegister) {\n <external-filter\n class=\"mb-16\"\n [autoId]=\"_autoId\"\n [filterRegister]=\"filterRegister\"\n [filter]=\"_tableOption.filter!\"\n [externalFilters]=\"_tableOption.filter?.externalFilters!\">\n </external-filter>\n }\n @let groupedItems = _items | sdTree: _tableOption.tree : treeRevision() | sdGroup: _tableOption : groupExpandState;\n\n <!-- Tree toggle d\u00F9ng chung: indent (theo c\u1EA5p) + slot chevron/spinner. Nh\u00FAng v\u00E0o\n c\u1ED9t Index ho\u1EB7c c\u1ED9t data \u0111\u1EA7u qua *ngTemplateOutlet (context: { row }). -->\n <ng-template #sdTreeToggle let-row=\"row\">\n @let _tree = row?.meta?.tree;\n @if (_tree) {\n <span class=\"sd-tree-indent\" [style.width.px]=\"(_tree.level ?? 0) * (_tableOption.tree?.indentSize ?? 20)\"></span>\n <span class=\"sd-tree-toggle-slot\">\n @if (_tree.hasChildren) {\n @if (_tree.isExpanding) {\n <span class=\"lds-ring sd-tree-spinner\">\n <div></div>\n <div></div>\n <div></div>\n <div></div>\n </span>\n } @else {\n @let _aid = _autoId;\n <button\n type=\"button\"\n class=\"sd-tree-toggle-btn\"\n [attr.data-autoid]=\"_aid ? _aid + '-tree-toggle-' + row.meta.id : null\"\n (click)=\"onTreeToggle(row); $event.stopPropagation()\">\n <mat-icon>{{ _tree.isExpanded ? 'expand_more' : 'chevron_right' }}</mat-icon>\n </button>\n }\n }\n </span>\n }\n </ng-template>\n\n <ng-content select=\"[sdTableTop]\"></ng-content>\n <div class=\"c-container\">\n @if (_loading) {\n <div class=\"c-loading\">\n <mat-spinner></mat-spinner>\n </div>\n }\n <div\n class=\"c-table\"\n sdScroll\n stickyShadow\n [style.max-height]=\"_tableOption.style?.maxHeight\"\n [style.min-height]=\"_tableOption.style?.minHeight\">\n <table\n mat-table\n [dataSource]=\"groupedItems\"\n [trackBy]=\"trackBy\"\n matSort\n [matSortDisabled]=\"!_tableOption.sort?.enable\"\n multiTemplateDataRows\n cdkDropList\n [cdkDropListData]=\"groupedItems\"\n [cdkDropListDisabled]=\"!_tableOption.rowReorder?.enabled\"\n [cdkDropListSortPredicate]=\"reorderSortPredicate\"\n (cdkDropListDropped)=\"onReorderDrop($event)\">\n @if (_tableOption.rowReorder?.enabled) {\n <ng-container matColumnDef=\"sdReorder\">\n <th mat-header-cell *matHeaderCellDef class=\"sd-reorder-header\" [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td mat-cell *matCellDef=\"let row\" class=\"sd-reorder-cell\">\n @if (!row.meta?.group?.items?.length) {\n <mat-icon cdkDragHandle [class.sd-reorder-disabled]=\"isReorderDisabled(row)\">\n {{ _tableOption.rowReorder?.icon || 'drag_indicator' }}\n </mat-icon>\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n }\n\n <ng-container matColumnDef=\"sdSubInformation\" sticky>\n <td class=\"p-0\" mat-cell *matCellDef=\"let item\" [attr.colspan]=\"_configuration.displayedColumns.length\">\n @if (_sdSubInformation?.templateRef) {\n @if (_tableOption.expand?.always) {\n <ng-container *ngTemplateOutlet=\"_sdSubInformation?.templateRef!; context: { item: item }\"> </ng-container>\n } @else {\n <div [@detailExpand]=\"item.isExpanded ? 'expanded' : 'collapsed'\">\n @if (item.isExpanded) {\n <ng-container *ngTemplateOutlet=\"_sdSubInformation?.templateRef!; context: { item: item }\"> </ng-container>\n }\n </div>\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n\n <ng-container matColumnDef=\"sdSubInformationAction\" stickyEnd>\n <th class=\"p-0\" mat-header-cell *matHeaderCellDef style=\"width: 1px\" [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td mat-cell *matCellDef=\"let element\">\n @if (!element.isExpanding && !_tableOption.expand?.always) {\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-expand-' + element.meta.id : null\"\n type=\"link\"\n [prefixIcon]=\"element.isExpanded ? 'expand_less' : 'expand_more'\"\n (click)=\"onExpand(element)\"\n width=\"35px\">\n </sd-button>\n }\n @if (element.isExpanding) {\n <div class=\"lds-ring\">\n <div></div>\n <div></div>\n <div></div>\n <div></div>\n </div>\n }\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"sdSelection\" sticky>\n <th class=\"sd-selection-cell p-0\" mat-header-cell *matHeaderCellDef [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\">\n @let visibleSelectAll = _items | selectionVisibleSelectAll: _tableOption.selector | async;\n @if (visibleSelectAll) {\n <mat-checkbox\n class=\"c-selection px-8\"\n color=\"primary\"\n [ngModel]=\"isSelectAll()\"\n (ngModelChange)=\"isSelectAll.set($event)\"\n (change)=\"onSelectAll()\">\n </mat-checkbox>\n }\n </th>\n\n <td class=\"sd-selection-cell p-0\" mat-cell *matCellDef=\"let item\">\n @let visible = item | selectionVisible: _tableOption.selector;\n @if (visible) {\n @if (_tableOption.selector?.single) {\n <mat-radio-button\n class=\"c-selection px-8\"\n color=\"primary\"\n [checked]=\"item.meta.selector.isSelected\"\n (change)=\"item.meta.selector.isSelected = true; onSelect(item)\"\n [disabled]=\"_selectedTableItems | selectionDisabled: item : _tableOption.selector!\">\n </mat-radio-button>\n } @else {\n <mat-checkbox\n class=\"c-selection px-8\"\n color=\"primary\"\n [(ngModel)]=\"item.meta.selector.isSelected\"\n (change)=\"onSelect(item)\"\n [disabled]=\"_selectedTableItems | selectionDisabled: item : _tableOption.selector!\">\n </mat-checkbox>\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n @if (_tableOption.index?.enabled) {\n @let _pageOffset = pageOffset();\n @let _indexWidth = _tableOption.index?.width || '50px';\n <!-- why: khi tree, c\u1ED9t Index \"\u00F4m\" th\u00EAm chevron + indent. D\u00F9ng width:1px +\n nowrap (.sd-tree-index-cell) \u0111\u1EC3 c\u1ED9t co S\u00C1T n\u1ED9i dung, kh\u00F4ng chi\u1EBFm ch\u1ED7\n th\u1EEBa; header \"#\" canh tr\u00E1i l\u00F9i v\u00E0o (.sd-tree-index-header) cho th\u1EB3ng s\u1ED1. -->\n @let _treeIndex = !!_tableOption.tree;\n <ng-container matColumnDef=\"sdIndex\" sticky>\n <th\n [class.p-0]=\"!_treeIndex\"\n [class.text-center]=\"!_treeIndex\"\n [class.sd-tree-index-cell]=\"_treeIndex\"\n [class.sd-tree-index-header]=\"_treeIndex\"\n mat-header-cell\n *matHeaderCellDef\n [style.width]=\"_treeIndex ? '1px' : _indexWidth\"\n [style.min-width]=\"_treeIndex ? null : _indexWidth\"\n [style.max-width]=\"_treeIndex ? null : _indexWidth\"\n [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\">\n {{ _tableOption.index?.title || '#' }}\n </th>\n <td\n [class.px-4]=\"!_treeIndex\"\n [class.text-center]=\"!_treeIndex\"\n [class.sd-tree-index-cell]=\"_treeIndex\"\n mat-cell\n *matCellDef=\"let item; let i = dataIndex\"\n [style.width]=\"_treeIndex ? '1px' : null\"\n [style.min-width]=\"_treeIndex ? null : _indexWidth\"\n [style.max-width]=\"_treeIndex ? null : _indexWidth\">\n @if (!item?.meta?.group?.isGroupHeader) {\n @if (_treeIndex) {\n <!-- why: tree \u2192 chevron + indent + STT ph\u00E2n c\u1EA5p (1, 1.2, 1.2.1). T\u00F4 \u0111\u1EADm root (level 0). -->\n <span class=\"sd-tree-line\">\n <ng-container *ngTemplateOutlet=\"sdTreeToggle; context: { row: item }\"></ng-container>\n @if (item?.meta?.tree?.indexPath?.length) {\n <span class=\"sd-tree-stt\" [class.sd-stt-root]=\"(item.meta.tree.level ?? 0) === 0\">\n {{ item.meta.tree.indexPath.join('.') }}\n </span>\n }\n </span>\n } @else {\n {{ _pageOffset + i + 1 }}\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef [style.width]=\"_treeIndex ? '1px' : null\" [style.min-width]=\"_treeIndex ? null : _indexWidth\" [style.max-width]=\"_treeIndex ? null : _indexWidth\"></td>\n </ng-container>\n }\n <ng-container\n matColumnDef=\"sdCommand\"\n [sticky]=\"_tableOption.command?.align !== 'right'\"\n [stickyEnd]=\"_tableOption.command?.align === 'right'\">\n <th class=\"p-0\" mat-header-cell *matHeaderCellDef style=\"width: 50px\" [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td class=\"px-8\" mat-cell *matCellDef=\"let item\">\n <desktop-command\n [autoId]=\"_autoId\"\n [commands]=\"_tableOption.command?.commands || _tableOption.commands || []\"\n [item]=\"item\"\n [itemIndex]=\"groupedItems.indexOf(item)\"></desktop-command>\n </td>\n <td mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n <!-- why: group header row d\u00F9ng matRowDef RI\u00CANG v\u1EDBi column list ['sdGroupHeader']\n qua predicate when:isGroupHeaderRow. Cell colspan = displayedColumns.length \u0111\u1EC3\n span TO\u00C0N B\u1ED8 width data row. Tr\u00E1nh colspan trick overflow row khi c\u00E1c TD kh\u00E1c render. -->\n <ng-container matColumnDef=\"sdGroupHeader\">\n <td\n class=\"p-0 sd-group-header-cell\"\n mat-cell\n *matCellDef=\"let item\"\n [attr.colspan]=\"sdGroupColspan()\">\n <div class=\"sd-group-row d-flex align-items-center py-8\">\n <!-- why: slot 42px kh\u1EDBp width v\u1EDBi sd-selection-cell data row \u2192 checkbox group c\u0103n TR\u1EE4C d\u1ECDc \u0111\u00FAng v\u1EDBi checkbox data. -->\n @if (_tableOption.selector?.visible && !_tableOption.selector?.single) {\n <div class=\"sd-group-selection-slot d-flex align-items-center justify-content-center\">\n <mat-checkbox\n class=\"c-selection\"\n color=\"primary\"\n [checked]=\"isGroupAllSelected(item)\"\n [indeterminate]=\"isGroupIndeterminate(item)\"\n (change)=\"onSelectGroup(item, $event.checked)\">\n </mat-checkbox>\n </div>\n } @else {\n <div class=\"sd-group-selection-slot\"></div>\n }\n @if (_tableOption.group?.collapsible) {\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-group-toggle-' + (item.meta.group?.key || item.meta.id) : null\"\n type=\"link\"\n [prefixIcon]=\"item.meta.group.isExpanded ? 'expand_more' : 'chevron_right'\"\n width=\"32px\"\n (click)=\"toggleGroupExpand(item)\">\n </sd-button>\n }\n @if (sdGroupDef()?.templateRef; as tpl) {\n <ng-container *ngTemplateOutlet=\"tpl; context: groupContext(item)\"></ng-container>\n } @else {\n <span class=\"T14R\">{{ item.meta.group.values | json }}</span>\n }\n </div>\n </td>\n </ng-container>\n <!-- why: filler column \u1EDF cu\u1ED1i h\u1EA5p th\u1EE5 leftover space khi t\u1ED5ng width c\u00E1c c\u1ED9t data < container \u2014 gi\u1EEF cho selection/STT/command kh\u00F4ng b\u1ECB d\u00E3n. -->\n <ng-container matColumnDef=\"sdFiller\">\n <th class=\"sd-filler-cell p-0\" mat-header-cell *matHeaderCellDef [attr.rowspan]=\"_configuration.multipleHeader ? 2 : 1\"></th>\n <td class=\"sd-filler-cell p-0\" mat-cell *matCellDef=\"let item\"></td>\n <td class=\"sd-filler-cell p-0\" mat-footer-cell *matFooterCellDef></td>\n </ng-container>\n @for (column of _configuration.firstColumns; track column.field) {\n <ng-container [matColumnDef]=\"column.field\" [sticky]=\"_configuration.fixedColumn[column.field]\">\n <th\n mat-header-cell\n *matHeaderCellDef\n class=\"px-8 py-8 c-th\"\n [sdColumnResize]=\"!!_tableOption.config?.resizable && column.type !== 'children'\"\n [minWidth]=\"column.minWidth\"\n [maxWidth]=\"column.maxWidth\"\n (resizeEnd)=\"onColumnResize(column.field, $event)\"\n [style.width]=\"column.width\"\n [style.min-width]=\"column.minWidth || column.width\"\n [style.max-width]=\"column.maxWidth\"\n [attr.rowspan]=\"_configuration.multipleHeader && column.type !== 'children' ? 2 : 1\"\n [attr.colspan]=\"column.type === 'children' ? column.children.length : 1\">\n <div>\n @if (column.type === 'children') {\n <div\n aria-hidden=\"true\"\n class=\"c-header-title\"\n [class.justify-content-end]=\"column.align === 'right'\"\n [class.text-right]=\"column.align === 'right'\">\n <column-title [column]=\"column\" [titleDef]=\"_titleDef[column.field]\"></column-title>\n </div>\n } @else {\n <div\n aria-hidden=\"true\"\n mat-sort-header\n class=\"c-header-title\"\n [class.justify-content-end]=\"column.align === 'right'\"\n [class.text-right]=\"column.align === 'right'\"\n [disabled]=\"!column.sortable\">\n <column-title [column]=\"column\" [titleDef]=\"_titleDef[column.field]\"></column-title>\n </div>\n @let hideInlineFilter =\n _tableOption.filter?.disabled ||\n _tableOption.filter?.hideInlineFilter === true ||\n (_tableOption.filter?.hideInlineFilter === 'auto' && !_isFiltered && _total! <= 10);\n @if (!hideInlineFilter) {\n <ng-container *sdDesktop>\n <column-filter\n [autoId]=\"_autoId\"\n [value]=\"columnFilter?.[column.field!]!\"\n [operator]=\"columnOperator[column.field]\"\n (operatorChange)=\"columnOperator[column.field] = $event!; onOperatorChange(column, $event)\"\n [columnFilter]=\"columnFilter!\"\n [cacheValues]=\"cacheValues\"\n [column]=\"column\"\n (filterChange)=\"onFilterChange()\"\n (filterCommit)=\"onFilterCommit()\">\n </column-filter>\n </ng-container>\n }\n }\n </div>\n </th>\n <td\n class=\"c-td px-0\"\n [class.d-none]=\"column.type === 'children'\"\n mat-cell\n *matCellDef=\"let item\"\n [sdHoverCopy]=\"(item.data && item.data[column.field]) || ''\"\n [sdHoverCopyDisabled]=\"!column.cell?.copiable\">\n @if (column.type !== 'children' && !item?.meta?.group?.isGroupHeader) {\n @if (_treeFirstField && column.field === _treeFirstField) {\n <!-- why: kh\u00F4ng c\u00F3 c\u1ED9t Index \u2192 chevron + indent nh\u00FAng v\u00E0o c\u1ED9t data \u0111\u1EA7u (nh\u01B0 file explorer). -->\n <span class=\"sd-tree-line px-8\">\n <ng-container *ngTemplateOutlet=\"sdTreeToggle; context: { row: item }\"></ng-container>\n <desktop-cell class=\"d-block\" [column]=\"column\" [item]=\"item\" [cellDef]=\"_cellDef\"></desktop-cell>\n </span>\n } @else {\n <desktop-cell class=\"d-block px-8\" [column]=\"column\" [item]=\"item\" [cellDef]=\"_cellDef\"> </desktop-cell>\n }\n }\n </td>\n <td mat-footer-cell *matFooterCellDef>\n <ng-container *ngIf=\"_footerDef[column.field]\">\n <ng-container *ngTemplateOutlet=\"_footerDef[column.field].templateRef; context: { items: _items, column: column }\">\n </ng-container>\n </ng-container>\n </td>\n </ng-container>\n }\n @for (column of _configuration.secondColumns; track column.field) {\n <ng-container [matColumnDef]=\"column.field\">\n <th\n mat-header-cell\n *matHeaderCellDef\n class=\"c-th px-8\"\n [style.width]=\"column.width\"\n [style.min-width]=\"column.minWidth || column.width\"\n [style.max-width]=\"column.maxWidth\">\n <div>\n <div\n aria-hidden=\"true\"\n mat-sort-header\n class=\"c-header-title\"\n [class.justify-content-end]=\"column.align === 'right'\"\n [class.text-right]=\"column.align === 'right'\"\n [disabled]=\"!column.sortable\">\n <column-title [column]=\"column\" [titleDef]=\"_titleDef[column.field]\"></column-title>\n </div>\n @if (!_tableOption.filter?.disabled && !_tableOption.filter?.hideInlineFilter && columnOperator) {\n <ng-container *sdDesktop>\n <column-filter\n [autoId]=\"_autoId\"\n [value]=\"columnFilter?.[column.field!]!\"\n [operator]=\"columnOperator[column.field]\"\n [columnFilter]=\"columnFilter!\"\n [cacheValues]=\"cacheValues\"\n [column]=\"column\"\n (operatorChange)=\"columnOperator[column.field] = $event!; onOperatorChange(column, $event)\"\n (filterChange)=\"onFilterChange()\"\n (filterCommit)=\"onFilterCommit()\">\n </column-filter>\n </ng-container>\n }\n </div>\n </th>\n <td\n class=\"c-td px-0\"\n mat-cell\n *matCellDef=\"let item\"\n [sdHoverCopy]=\"item[column.field]\"\n [sdHoverCopyDisabled]=\"!column.cell?.copiable\">\n @if (column.type !== 'children') {\n <desktop-cell class=\"d-block px-8\" [column]=\"column\" [item]=\"item\" [cellDef]=\"_cellDef\"> </desktop-cell>\n }\n </td>\n <td mat-footer-cell *matFooterCellDef>\n <ng-container *ngIf=\"_footerDef[column.field]\">\n <ng-container *ngTemplateOutlet=\"_footerDef[column.field].templateRef; context: { items: _items, column: column }\">\n </ng-container>\n </ng-container>\n </td>\n </ng-container>\n }\n <tr class=\"c-first-header\" mat-header-row *matHeaderRowDef=\"_configuration.firstHeaders; sticky: true\"></tr>\n @if (!!_configuration.secondHeaders.length) {\n <tr class=\"c-second-header\" mat-header-row *matHeaderRowDef=\"_configuration.secondHeaders; sticky: true\"></tr>\n }\n <!-- Group header row \u2014 ch\u1EC9 1 cell sdGroupHeader colspan to\u00E0n width. -->\n <tr\n mat-row\n *matRowDef=\"let row; columns: ['sdGroupHeader']; when: isGroupHeaderRow\"\n class=\"c-group-row-tr\"></tr>\n\n <!-- Data row \u2014 d\u00F9ng displayedColumns \u0111\u1EA7y \u0111\u1EE7; when isDataRow \u0111\u1EC3 lo\u1EA1i tr\u1EEB group header. -->\n <tr\n mat-row\n cdkDrag\n [cdkDragData]=\"row\"\n [cdkDragDisabled]=\"\n !_tableOption.rowReorder?.enabled ||\n !!row.meta?.group?.items?.length ||\n (row.meta?.tree?.level ?? 0) > 0 ||\n isReorderDisabled(row)\n \"\n *matRowDef=\"let row; columns: _configuration.displayedColumns; when: isDataRow\"\n matRipple\n class=\"c-row\"\n [class.sd-tree-row]=\"!!_tableOption.tree\"\n [ngClass]=\"_tableOption.tree ? 'sd-tree-level-' + (row.meta?.tree?.level ?? 0) : null\"\n [ngStyle]=\"rowStyle(row)\"\n [class.selected]=\"row.meta.selector.isSelected\"></tr>\n\n <!-- Sub-information row \u2014 also gated b\u1EDFi isDataRow \u0111\u1EC3 KH\u00D4NG render empty expand row d\u01B0\u1EDBi group header. -->\n <tr mat-row *matRowDef=\"let row; columns: ['sdSubInformation']; when: isDataRow\" class=\"c-detail-row\"></tr>\n @if (hasFooter() && !!_configuration.displayedFooters.length) {\n <tr mat-footer-row *matFooterRowDef=\"_configuration.displayedFooters; sticky: true\"></tr>\n }\n </table>\n @if (!_loading && !_total) {\n <div class=\"c-no-data-row\">\n @if (_isFiltered) {\n @if (tableConfiguration?.images?.filterEmpty) {\n <img class=\"c-image\" [src]=\"tableConfiguration!.images!.filterEmpty\" alt=\"filter-empty\" />\n } @else {\n <img class=\"c-image sd-image-filter-empty\" alt=\"filter-empty\" />\n }\n <div class=\"T16M\">{{ 'core.component.table.no-results' | translate }}</div>\n <div class=\"T16R text-secondary\">{{ 'core.component.table.no-results-hint' | translate }}</div>\n } @else {\n @if (_requireFiltered) {\n @if (tableConfiguration?.images?.filterRequired) {\n <img class=\"c-image\" [src]=\"tableConfiguration!.images!.filterRequired\" alt=\"filter-required\" />\n } @else {\n <img class=\"c-image sd-image-filter-required\" alt=\"filter-required\" />\n }\n <div class=\"T16R text-secondary\">{{ 'core.component.table.choose-filter-hint' | translate }}</div>\n } @else {\n @if (tableConfiguration?.images?.dataEmpty) {\n <img class=\"c-image\" [src]=\"tableConfiguration!.images!.dataEmpty\" alt=\"data-empty\" />\n } @else {\n <img class=\"c-image sd-image-data-empty\" alt=\"data-empty\" />\n }\n <div class=\"T16R text-secondary\">{{ 'core.component.table.no-data' | translate }}</div>\n }\n }\n </div>\n }\n </div>\n <div class=\"c-paginator\">\n <div class=\"c-action\">\n <ng-container *sdDesktop>\n @if (_tableOption.reload?.visible) {\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-reload' : null\"\n class=\"mr-8\"\n [title]=\"'core.component.table.reload' | translate\"\n prefixIcon=\"refresh\"\n (click)=\"reload()\"\n [disabled]=\"!_items.length\"\n type=\"link\">\n </sd-button>\n }\n </ng-container>\n @if (_export && _items.length) {\n @if (_export.type === 'custom') {\n <sd-button [autoId]=\"_autoId ? _autoId + '-export' : null\" class=\"mr-8\" [title]=\"_exportTitle\" prefixIcon=\"get_app\" (click)=\"exportCustom()\" type=\"link\"> </sd-button>\n } @else {\n @if (_export.visible) {\n @if (_exporting) {\n <sd-button [autoId]=\"_autoId ? _autoId + '-export' : null\" class=\"mr-8\" [loading]=\"_exporting\" [title]=\"_exportTitle\" prefixIcon=\"get_app\" type=\"link\"> </sd-button>\n } @else {\n @if (_export.visible === 'ALL' || !_export.visible) {\n <sd-button [autoId]=\"_autoId ? _autoId + '-export' : null\" class=\"mr-8\" [title]=\"_exportTitle\" prefixIcon=\"get_app\" [matMenuTriggerFor]=\"menu\" type=\"link\"> </sd-button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item [attr.data-autoid]=\"_autoId ? _autoId + '-export-excel' : null\" (click)=\"exportExcel()\" type=\"button\">\n <mat-icon fontSet=\"material-icons-outlined\">file_download</mat-icon>\n <span> {{ 'core.component.table.export-excel' | translate }}</span>\n </button>\n <button mat-menu-item [attr.data-autoid]=\"_autoId ? _autoId + '-export-csv' : null\" (click)=\"exportCSV()\" type=\"button\">\n <mat-icon fontSet=\"material-icons-outlined\">file_download</mat-icon>\n <span> {{ 'core.component.table.export-csv' | translate }}</span>\n </button>\n </mat-menu>\n } @else if (_export.visible === 'EXCEL') {\n <sd-button [autoId]=\"_autoId ? _autoId + '-export-excel' : null\" class=\"mr-8\" [title]=\"_exportTitle\" prefixIcon=\"get_app\" (click)=\"exportExcel()\" type=\"link\"> </sd-button>\n } @else if (_export.visible === 'CSV') {\n <sd-button [autoId]=\"_autoId ? _autoId + '-export-csv' : null\" class=\"mr-8\" [title]=\"_exportTitle\" prefixIcon=\"get_app\" (click)=\"exportCSV()\" type=\"link\"> </sd-button>\n }\n }\n }\n }\n }\n\n <ng-container *sdDesktop>\n @if (_configComponent) {\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-config' : null\"\n class=\"mr-8\"\n [title]=\"'core.component.table.setup-short' | translate\"\n prefixIcon=\"settings\"\n (click)=\"_configComponent.open()\"\n type=\"link\">\n </sd-button>\n }\n </ng-container>\n <!-- Mobile: n\u00FAt Filter m\u1EDF mobile-filter drawer (inline filter \u1EA9n tr\u00EAn mobile) -->\n <ng-container *sdMobile>\n @if (!_tableOption.filter?.disabled && _mobileFilter) {\n <sd-button\n [autoId]=\"_autoId ? _autoId + '-mobile-filter-open' : null\"\n class=\"mr-8\"\n prefixIcon=\"filter_alt\"\n (click)=\"_mobileFilter.open()\"\n type=\"link\">\n </sd-button>\n }\n </ng-container>\n </div>\n @let hidePaginator = !_tableOption.paginate?.pageSize || _total === undefined || _total <= _tableOption.paginate?.pageSize!;\n <!-- Desktop paginator: full config (pageSize dropdown, first/last buttons) -->\n <ng-container *sdDesktop>\n <mat-paginator\n [class.d-none]=\"_tableOption.paginate?.hidden || hidePaginator\"\n [length]=\"_total\"\n [pageSize]=\"_tableOption.paginate?.pageSize\"\n [pageSizeOptions]=\"_tableOption.paginate?.pages!\"\n [showFirstLastButtons]=\"_tableOption.paginate?.showFirstLastButtons\"\n [hidePageSize]=\"_tableOption.paginate?.hidePageSize\"></mat-paginator>\n </ng-container>\n <!-- Mobile paginator: \u1EA9n pageSize dropdown + first/last buttons \u0111\u1EC3 ti\u1EBFt ki\u1EC7m kh\u00F4ng gian -->\n <ng-container *sdMobile>\n <mat-paginator\n [class.d-none]=\"_tableOption.paginate?.hidden || hidePaginator\"\n [length]=\"_total\"\n [pageSize]=\"_tableOption.paginate?.pageSize\"\n [showFirstLastButtons]=\"false\"\n hidePageSize></mat-paginator>\n </ng-container>\n <!-- \"\u0110ang hi\u1EC3n th\u1ECB...\" ch\u1EC9 desktop -->\n <ng-container *sdDesktop>\n @if (!_tableOption.paginate?.hidden && hidePaginator && _total !== undefined && _total > 0) {\n <div class=\"T14R pr-16\">\n {{ 'core.component.table.showing' | translate }} <span class=\"T14M ml-2\">1-{{ _total }}/{{ _total }}</span>\n </div>\n }\n </ng-container>\n </div>\n </div>\n <selector-action [autoId]=\"_autoId\" [tableOption]=\"_tableOption\" [selectedTableItems]=\"_selectedTableItems\" (clear)=\"onClearSelection(groupedItems)\" />\n @if (_tableOption.config?.visible) {\n <config [autoId]=\"_autoId\" [tableOption]=\"_tableOption\" />\n }\n <!-- Mobile filter drawer \u2014 ch\u1EC9 instantiate khi c\u1EA7n render tr\u00EAn mobile -->\n @if (!_tableOption.filter?.disabled && filterRegister) {\n <mobile-filter\n [autoId]=\"_autoId\"\n [filter]=\"_tableOption.filter!\"\n [externalFilters]=\"_tableOption.filter?.externalFilters!\"\n [columns]=\"_configuration.firstColumns\"\n [filterDefs]=\"sdFilterDefs()\"\n [filterRegister]=\"filterRegister\"\n [cacheValues]=\"cacheValues\">\n </mobile-filter>\n }\n}\n", styles: [":host{display:flex;flex-direction:column;overflow:auto;width:100%;height:100%}:host .c-header-title{height:40px;display:flex;align-items:center}:host .c-container{position:relative;min-height:50px;display:flex;flex-direction:column;flex:1}:host .c-container .c-table{position:relative;flex:1;display:flex;flex-direction:column}:host .c-container .c-table table{border-collapse:separate;width:100%}:host .c-container .c-table table tr.c-first-header.mat-mdc-header-row{height:40px}:host .c-container .c-table table tr.c-second-header.mat-mdc-header-row{height:40px}:host .c-container .c-table table tr.c-detail-row{height:0}:host .c-container .c-table table tr.c-row.activated{background-color:#e5ecff}:host .c-container .c-table table tr.c-row.selected{background-color:#eef2ff}:host .c-container .c-table table tr.c-row:not(.selected):not(.activated):hover{background-color:#f5f5f5}:host .c-container .c-table table tr.c-row td{border-bottom-width:0}:host .c-container .c-table table tr.c-row.c-expandable{cursor:pointer}:host .c-container .c-table table tr.c-row.c-expandable:hover{background:#f5f5f5}:host .c-container .c-table table th.mat-mdc-header-cell{background-color:#f2f3f4;border-bottom:0!important}:host .c-container .c-table table td.mat-mdc-cell,:host .c-container .c-table table td.mat-mdc-footer-cell,:host .c-container .c-table table th.mat-mdc-header-cell{border-bottom-color:#f2f2f2!important}:host .c-container .c-table .c-th{vertical-align:middle;font-weight:500;font-size:14px;line-height:20px;color:#212121}:host .c-container .c-table .c-th.sd-col-resize-host{position:relative}:host .c-container .c-table .c-th.sd-resizing{-webkit-user-select:none;user-select:none}:host .c-container .c-table .c-th .sd-col-resize-handle{position:absolute;top:0;right:0;width:6px;height:100%;cursor:col-resize;-webkit-user-select:none;user-select:none;z-index:2}:host .c-container .c-table .c-th .sd-col-resize-handle:hover{background:#00000014}:host .c-container .c-table .c-td:first{padding-left:10px}:host .c-container .c-table .c-no-data-row{flex:1;width:100%;position:sticky;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center}:host .c-container .c-table .c-no-data-row .c-image{margin-bottom:16px;width:96px}:host .c-container .c-loading{position:absolute;inset:0 0 56px;background:#00000026;z-index:2;display:flex;align-items:center;justify-content:center}:host .c-container .c-paginator{display:flex;flex-direction:row;justify-content:space-between;align-items:center;background-color:#fff}:host .c-container .c-paginator .c-action{padding:5px}:host .c-container .c-empty{text-align:center;background-color:#fff;border:none!important}:host .c-container .c-empty mat-icon{font-size:150px;margin-top:30px;margin-bottom:30px;opacity:.2;width:auto;height:auto}:host button.c-btn-add{background-color:#fff;box-shadow:0 2px 4px #2f313629}:host .lds-ring{display:inline-block;position:relative;width:40px;height:40px}:host .lds-ring div{box-sizing:border-box;display:block;position:absolute;width:32px;height:32px;margin:4px;border:4px solid #cef;border-radius:50%;animation:lds-ring 1.2s cubic-bezier(.5,0,.5,1) infinite;border-color:#cef transparent transparent transparent}:host .lds-ring div:nth-child(1){animation-delay:-.45s}:host .lds-ring div:nth-child(2){animation-delay:-.3s}:host .lds-ring div:nth-child(3){animation-delay:-.15s}@keyframes lds-ring{0%{transform:rotate(0)}to{transform:rotate(360deg)}}:host .sd-reorder-header,:host .sd-reorder-cell{width:40px;min-width:40px;padding:0 4px;box-sizing:border-box}:host .sd-reorder-cell mat-icon{cursor:grab;color:#00000061;display:flex;align-items:center}:host .sd-reorder-cell mat-icon:active{cursor:grabbing}:host .sd-reorder-cell mat-icon.sd-reorder-disabled{opacity:.3;cursor:not-allowed;pointer-events:none}:host ::ng-deep .mat-sort-header-content{display:block;text-align:left;width:100%}:host ::ng-deep .mat-select-arrow{color:#a6a6a6}:host ::ng-deep .mat-sort-header-disabled{background-image:none!important;cursor:default!important;padding-right:0!important}:host ::ng-deep .mat-sort-header-container{align-items:start!important}:host ::ng-deep .mat-sort-header-arrow{margin-top:4px!important}:host ::ng-deep .mat-sort-header-arrow{display:none!important}:host ::ng-deep .mat-sort-header{cursor:pointer}:host ::ng-deep .mat-sort-header[aria-sort]{background-repeat:no-repeat;background-position:center right 0;background-size:16px 16px;cursor:pointer;padding-right:24px}:host ::ng-deep .mat-sort-header[aria-sort=none]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath fill='%237A7A7A' d='M12 5.83L15.17 9l1.41-1.41L12 3 7.41 7.59 8.83 9 12 5.83zm0 12.34L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15 12 18.17z'/%3E%3C/svg%3E\")}:host ::ng-deep .mat-sort-header[aria-sort=ascending]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath fill='%237A7A7A' d='M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z'/%3E%3C/svg%3E\")}:host ::ng-deep .mat-sort-header[aria-sort=descending]{background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath fill='%237A7A7A' d='M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z'/%3E%3C/svg%3E\")}:host ::ng-deep .c-paginator .mat-mdc-paginator .mdc-text-field--outlined .mat-mdc-form-field-infix,:host ::ng-deep .c-paginator .mat-mdc-paginator .mdc-text-field--no-label .mat-mdc-form-field-infix{padding-top:2px;padding-bottom:2px;min-height:32px}:host ::ng-deep .c-paginator .mat-mdc-paginator-range-label{margin:0 4px}:host ::ng-deep .c-paginator .mat-mdc-paginator-page-size-select mat-select{margin-top:2px!important}:host ::ng-deep .cdk-drag-preview{box-shadow:0 4px 12px #00000026;background:#fff;display:table;width:100%}:host ::ng-deep .cdk-drag-preview td{padding:0 8px;border-bottom:1px solid rgba(0,0,0,.12)}:host ::ng-deep .cdk-drag-placeholder{opacity:0}:host ::ng-deep .cdk-drop-list-dragging tr.c-row:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}:host ::ng-deep .sd-selection-cell{width:42px;min-width:42px;max-width:42px}:host ::ng-deep .sd-filler-cell{width:auto;min-width:0;padding:0!important;border:0}:host ::ng-deep .sd-group-header-cell{background:#f5f7fc;border-top:1px solid #e6e9f2;border-bottom:1px solid #e6e9f2}:host ::ng-deep .sd-group-row{min-height:36px}:host ::ng-deep .sd-group-selection-slot{width:42px;min-width:42px;max-width:42px}:host ::ng-deep .c-selection{transform:scale(.85);transform-origin:center}:host ::ng-deep .sd-tree-line{display:inline-flex;align-items:center;min-height:32px}:host ::ng-deep .sd-tree-indent{flex:0 0 auto}:host ::ng-deep .sd-tree-toggle-slot{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px}:host ::ng-deep .sd-tree-toggle-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:0;background:transparent;border-radius:50%;cursor:pointer;color:var(--sd-text-secondary, #5b6472);transition:background-color .15s ease}:host ::ng-deep .sd-tree-toggle-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}:host ::ng-deep .sd-tree-toggle-btn:hover{background-color:#0f172a14}:host ::ng-deep .sd-tree-spinner{transform:scale(.45)}:host ::ng-deep .sd-tree-stt{margin-left:2px;white-space:nowrap}:host ::ng-deep .sd-tree-index-cell{text-align:left!important;white-space:nowrap;padding-left:8px;padding-right:8px}:host ::ng-deep .sd-tree-index-header{padding-left:32px}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-0{background:#fbfcfe}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-0>td{font-weight:500}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-1{background:#fff}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-2{background:#fafbfd}:host ::ng-deep tr.c-row.sd-tree-row.sd-tree-level-3{background:#f6f8fb}:host ::ng-deep .sd-stt-root{font-weight:700;color:#1f2330}\n", ":host ::ng-deep .sticky-shadow-right{overflow:visible!important}:host ::ng-deep .sticky-shadow-right:after{content:\"\";position:absolute;top:0;right:0;bottom:0;width:8px;transform:translate(100%);background:linear-gradient(to right,rgba(0,0,0,.12),transparent);pointer-events:none}:host ::ng-deep .sticky-shadow-left{overflow:visible!important}:host ::ng-deep .sticky-shadow-left:after{content:\"\";position:absolute;top:0;left:0;bottom:0;width:8px;transform:translate(-100%);background:linear-gradient(to left,rgba(0,0,0,.12),transparent);pointer-events:none}\n"] }]
|
|
4340
4467
|
}], ctorParameters: () => [] });
|
|
4341
4468
|
|
|
4342
4469
|
const SdConvertToPagingReq = (filterRequest, args) => {
|