@sdcorejs/angular 19.0.8 → 19.0.10
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 +270 -140
- package/assets/scss/core/utilities/_grid.scss +25 -19
- package/components/anchor/src/models/sd-anchor.model.d.ts +1 -1
- package/components/document-builder/src/document-builder.component.d.ts +2 -2
- package/components/document-builder/src/plugins/paste-handler/filters/parse.d.ts +1 -1
- package/components/editor/src/plugins/image-upload/utils/batch.utils.d.ts +3 -3
- package/components/form-generic/src/components/form-builder/components/attribute-expression/attribute-expression.component.d.ts +10 -6
- package/components/form-generic/src/components/form-builder/components/attribute-expression/form-expression-query-adapter.d.ts +6 -0
- package/components/form-generic/src/components/form-builder/components/attribute-parameter/attribute-parameter.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/attribute-selection/attribute-selection.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/attribute-selection/components/build-queries/build-queries.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/attribute-selection/components/build-variables/build-variables.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/attribute-table/attribute-table.component.d.ts +2 -4
- package/components/form-generic/src/components/form-builder/components/attribute-template/attribute-template.component.d.ts +2 -4
- package/components/form-generic/src/components/form-builder/components/checkbox/attribute/checkbox-attribute.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/chip-calendar/attribute/chip-calendar-attribute.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/chip-calendar/control/chip-calendar-control.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/chip-string/attribute/chip-string-attribute.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/chip-string/control/chip-string-control.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/configure-validation/configure-validation.component.d.ts +6 -7
- package/components/form-generic/src/components/form-builder/components/datetime/attribute/datetime-attribute.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/datetime/control/datetime-control.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/expression-builder/expression-builder.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/html/attribute/components/build-queries/build-queries.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/html/control/html-control.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/number/attribute/number-attribute.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/number/control/number-control.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/radio/attribute/radio-attribute.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/radio/control/radio-control.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/select/attribute/select-attribute.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/select/control/select-control.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/table/attribute/table-attribute.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/table/control/table-control.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/textarea/attribute/textarea-attribute.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/textarea/control/textarea-control.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/textfield/attribute/textfield-attribute.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/textfield/control/textfield-control.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/upload/attribute/upload-attribute.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/components/upload/control/upload-control.component.d.ts +2 -2
- package/components/form-generic/src/components/form-builder/form-builder-layout.d.ts +22 -0
- package/components/form-generic/src/components/form-builder/form-builder.component.d.ts +24 -5
- package/components/form-generic/src/components/form-render/components/item/components/chip-calendar/chip-calendar.component.d.ts +2 -2
- package/components/form-generic/src/components/form-render/components/item/components/chip-string/chip-string.component.d.ts +2 -2
- package/components/form-generic/src/components/form-render/components/item/components/datetime/datetime.component.d.ts +2 -2
- package/components/form-generic/src/components/form-render/components/item/components/html/html.component.d.ts +2 -2
- package/components/form-generic/src/components/form-render/components/item/components/number/number.component.d.ts +2 -2
- package/components/form-generic/src/components/form-render/components/item/components/radio/radio.component.d.ts +2 -2
- package/components/form-generic/src/components/form-render/components/item/components/select/select.component.d.ts +2 -2
- package/components/form-generic/src/components/form-render/components/item/components/table/table.component.d.ts +2 -2
- package/components/form-generic/src/components/form-render/components/item/components/textarea/textarea.component.d.ts +2 -2
- package/components/form-generic/src/components/form-render/components/item/components/textfield/textfield.component.d.ts +2 -2
- package/components/form-generic/src/components/form-render/components/item/components/upload/upload.component.d.ts +2 -2
- package/components/form-generic/src/components/form-render/components/variable/variable.component.d.ts +2 -2
- package/components/form-generic/src/components/form-render/form-render.component.d.ts +4 -6
- package/components/form-generic/src/components/sd-feel-expression/sd-feel-expression.component.d.ts +2 -2
- package/components/form-generic/src/configurations/form.configuration.d.ts +1 -1
- package/components/form-generic/src/models/form-generic-component.model.d.ts +2 -2
- package/components/form-generic/src/services/form-generic.service.d.ts +1 -3
- package/components/import-excel/src/import-excel.model.d.ts +2 -2
- package/components/mini-editor/src/mini-editor.component.d.ts +4 -4
- package/components/modal/src/modal.component.d.ts +1 -1
- package/components/query-builder/src/query-builder.model.d.ts +4 -4
- package/components/splitter/src/splitter-state.service.d.ts +1 -1
- package/components/tab-router/src/models/tab-router.model.d.ts +2 -1
- package/components/tab-router/src/services/tab-router.service.d.ts +0 -1
- package/components/table/src/components/command/desktop-command.component.d.ts +8 -7
- package/components/table/src/components/command/pipes/command.pipe.d.ts +1 -12
- package/components/table/src/components/command/pipes/filter.pipe.d.ts +1 -8
- package/components/table/src/components/filter/column-filter/column-filter.component.d.ts +1 -1
- package/components/table/src/models/table-column.model.d.ts +3 -2
- package/components/table/src/pipes/selection-disabled.pipe.d.ts +1 -1
- package/components/table/src/services/config.service.d.ts +4 -5
- package/components/table/src/services/table-filter/table-filter.service.d.ts +1 -0
- package/components/table/src/services/table-local/table-local.util.d.ts +19 -0
- package/components/table/src/services/table-reorder/table-reorder.util.d.ts +29 -0
- package/components/table/src/services/table-selection/table-selection.util.d.ts +8 -0
- package/components/table/src/services/tree/tree.util.d.ts +10 -1
- package/components/table/src/table.component.d.ts +6 -10
- package/components/view/src/view.component.d.ts +1 -1
- package/configurations/src/sd-core.configuration.d.ts +2 -2
- package/fesm2022/sdcorejs-angular-components-anchor.mjs +15 -18
- package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-avatar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-badge.mjs +0 -1
- package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-button.mjs +6 -7
- package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-chart.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-code-editor.mjs +1 -1
- package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs +17 -26
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-editor.mjs +7 -2
- package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs +711 -426
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs +35 -18
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-inform.mjs +0 -1
- package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-mini-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal.mjs +25 -14
- package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-operator.mjs +1 -1
- package/fesm2022/sdcorejs-angular-components-operator.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-preview.mjs +33 -29
- package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs +15 -28
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs +2 -3
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-quick-action.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-section.mjs +0 -1
- package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-splitter.mjs +4 -4
- package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs +31 -31
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-table.mjs +794 -849
- package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs +6 -6
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-view.mjs +3 -9
- package/fesm2022/sdcorejs-angular-components-view.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-directives.mjs +2 -7
- package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +0 -3
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +2 -2
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip.mjs +2 -2
- package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs +11 -5
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date.mjs +2 -4
- package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs +2 -6
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs +3 -3
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-label.mjs +0 -1
- package/fesm2022/sdcorejs-angular-forms-label.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-radio.mjs +12 -2
- package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-select.mjs +45 -14
- package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs +2 -2
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-handlers.mjs +3 -6
- package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-i18n.mjs +11 -7
- package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-interceptors.mjs +0 -2
- package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-auth.mjs +14 -38
- package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-authom.mjs +14 -14
- package/fesm2022/sdcorejs-angular-modules-authom.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +22 -11
- package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-layout.mjs +30 -27
- package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-permission.mjs +5 -12
- package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-pipes.mjs +71 -17
- package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-api.mjs +0 -2
- package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-cache.mjs +5 -11
- package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-confirm.mjs +16 -21
- package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-docx.mjs +2 -8
- package/fesm2022/sdcorejs-angular-services-docx.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-excel.mjs +2 -2
- package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-license.mjs +5 -12
- package/fesm2022/sdcorejs-angular-services-license.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-notify.mjs +70 -87
- package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-storage.mjs +6 -15
- package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-utilities-data-state.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +47 -5
- package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-utilities-models.mjs +18 -3
- package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular.mjs.map +1 -1
- package/forms/chip/src/chip.component.d.ts +2 -2
- package/forms/chip-calendar/src/chip-calendar.component.d.ts +2 -2
- package/forms/date-range/src/date-range.component.d.ts +2 -0
- package/forms/input-color/src/input-color.component.d.ts +2 -1
- package/forms/select/src/select-footer-action.directive.d.ts +5 -1
- package/forms/select/src/select.component.d.ts +4 -0
- package/modules/auth/guards/auth.guard.d.ts +2 -4
- package/modules/auth/guards/portal.guard.d.ts +2 -4
- package/modules/auth/services/auth.service.d.ts +3 -4
- package/modules/layout/modules/home/guards/home-redirect.guard.d.ts +3 -5
- package/modules/permission/src/configurations/permission.configuration.d.ts +3 -3
- package/modules/permission/src/guards/permission.guard.d.ts +2 -5
- package/package.json +2 -2
- package/pipes/index.d.ts +3 -0
- package/pipes/src/format-date.pipe.d.ts +8 -0
- package/pipes/src/format-datetime.pipe.d.ts +8 -0
- package/pipes/src/format-number.pipe.d.ts +1 -3
- package/pipes/src/view.pipe.d.ts +7 -0
- package/services/confirm/src/lib/components/dialog-confirm/dialog-confirm.component.d.ts +3 -3
- package/services/license/src/license.service.d.ts +2 -3
- package/services/notify/src/components/toast/toast.component.d.ts +9 -13
- package/services/notify/src/notify.service.d.ts +4 -5
- package/services/storage/src/storage.service.d.ts +3 -4
- package/utilities/extensions/index.d.ts +1 -0
- package/utilities/extensions/src/object.extension.d.ts +8 -0
- package/components/document-builder/src/plugins/paste-handler/filters/replacetabswithinprewithspaces.d.ts +0 -24
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, inject, TemplateRef, Directive, booleanAttribute, computed, ChangeDetectionStrategy, Component, model, output, isSignal, effect, Pipe, ChangeDetectorRef, signal, untracked, viewChild, InjectionToken,
|
|
2
|
+
import { input, inject, TemplateRef, Directive, booleanAttribute, computed, ChangeDetectionStrategy, Component, model, output, isSignal, effect, Pipe, ChangeDetectorRef, signal, untracked, viewChild, InjectionToken, Injectable, EventEmitter, Input, Output, ViewChild, ElementRef, DestroyRef, afterNextRender, Renderer2, NgZone, contentChild, contentChildren } from '@angular/core';
|
|
3
3
|
import { trigger, state, style, transition, animate } from '@angular/animations';
|
|
4
4
|
import * as i4$1 from '@angular/material/paginator';
|
|
5
5
|
import { MatPaginatorIntl, MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
|
|
@@ -8,9 +8,10 @@ import { MatSort, MatSortModule } from '@angular/material/sort';
|
|
|
8
8
|
import * as i6 from '@angular/material/table';
|
|
9
9
|
import { MatTable, MatTableModule } from '@angular/material/table';
|
|
10
10
|
import * as i1$1 from '@sdcorejs/angular/services';
|
|
11
|
-
import { SdExcelService, SdNotifyService } from '@sdcorejs/angular/services';
|
|
11
|
+
import { SdStorageService, SdExcelService, SdNotifyService } from '@sdcorejs/angular/services';
|
|
12
12
|
import { Subject, fromEvent, Subscription, isObservable, firstValueFrom } from 'rxjs';
|
|
13
13
|
import { debounceTime, startWith, map, switchMap } from 'rxjs/operators';
|
|
14
|
+
import { DateUtilities, StringUtilities } from '@sdcorejs/angular/utilities';
|
|
14
15
|
import { CdkTableModule, CdkColumnDef } from '@angular/cdk/table';
|
|
15
16
|
import * as i11 from '@angular/cdk/drag-drop';
|
|
16
17
|
import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
|
|
@@ -31,9 +32,9 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
|
31
32
|
import { SdButton } from '@sdcorejs/angular/components/button';
|
|
32
33
|
import { SdQuickAction } from '@sdcorejs/angular/components/quick-action';
|
|
33
34
|
import { SdTooltipDirective, SdScrollDirective, SdDesktopDirective, SdMobileDirective, SdHoverCopyDirective } from '@sdcorejs/angular/directives';
|
|
34
|
-
import { SdSafeHtmlPipe, SdFormatNumberPipe } from '@sdcorejs/angular/pipes';
|
|
35
|
-
import {
|
|
36
|
-
import { DateUtilities, ArrayUtilities, NumberUtilities } from '@sdcorejs/angular/utilities/extensions';
|
|
35
|
+
import { SdSafeHtmlPipe, SdFormatDatePipe, SdFormatDatetimePipe, SdFormatNumberPipe } from '@sdcorejs/angular/pipes';
|
|
36
|
+
import { Utilities, BrowserUtilities } from '@sdcorejs/utils/fns';
|
|
37
|
+
import { DateUtilities as DateUtilities$1, ArrayUtilities, NumberUtilities } from '@sdcorejs/angular/utilities/extensions';
|
|
37
38
|
import { SdBadge } from '@sdcorejs/angular/components/badge';
|
|
38
39
|
import { SdOperator } from '@sdcorejs/angular/components/operator';
|
|
39
40
|
import { SdInput, SdInputNumber, SdSelect, SdDate, SdDatetime, SdDateRange as SdDateRange$1, SdCheckbox, SdSwitch } from '@sdcorejs/angular/forms';
|
|
@@ -50,7 +51,7 @@ import * as i4 from '@angular/material/button';
|
|
|
50
51
|
import { MatButtonModule } from '@angular/material/button';
|
|
51
52
|
import * as i2$1 from '@angular/material/tooltip';
|
|
52
53
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
53
|
-
import {
|
|
54
|
+
import { DefaultMaterialIconFontSet } from '@sdcorejs/angular/utilities/models';
|
|
54
55
|
import * as i5$1 from '@angular/material/chips';
|
|
55
56
|
import { MatChipsModule } from '@angular/material/chips';
|
|
56
57
|
|
|
@@ -162,7 +163,242 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
162
163
|
}]
|
|
163
164
|
}] });
|
|
164
165
|
|
|
165
|
-
|
|
166
|
+
const SdConvertToPagingReq = (filterRequest, args) => {
|
|
167
|
+
const { externalFilters, columns, fieldMapping } = args;
|
|
168
|
+
const req = {
|
|
169
|
+
filters: [],
|
|
170
|
+
orders: args?.orders || [],
|
|
171
|
+
pageNumber: filterRequest.pageNumber,
|
|
172
|
+
pageSize: filterRequest.pageSize,
|
|
173
|
+
};
|
|
174
|
+
const { filters } = req;
|
|
175
|
+
const { rawExternalFilter, rawColumnFilter, columnOperator, orderBy, orderDirection } = filterRequest;
|
|
176
|
+
// Xử lý external filter
|
|
177
|
+
for (const externalFilter of externalFilters || []) {
|
|
178
|
+
const value = rawExternalFilter?.[externalFilter.field];
|
|
179
|
+
const field = fieldMapping?.[externalFilter.field] || externalFilter.field;
|
|
180
|
+
// Nếu có giá trị thì mới xử lý filter
|
|
181
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
182
|
+
if (externalFilter.type === 'string') {
|
|
183
|
+
if (externalFilter.defaultOperator === 'EQUAL' && typeof value === 'string' && value.includes(',')) {
|
|
184
|
+
filters.push({
|
|
185
|
+
field,
|
|
186
|
+
operator: 'IN',
|
|
187
|
+
data: value.split(',').map(val => val.trim()),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
filters.push({
|
|
192
|
+
field,
|
|
193
|
+
operator: externalFilter.defaultOperator || 'CONTAIN',
|
|
194
|
+
data: value,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else if (externalFilter.type === 'boolean') {
|
|
199
|
+
filters.push({
|
|
200
|
+
field,
|
|
201
|
+
operator: 'EQUAL',
|
|
202
|
+
data: value === true || value === 1 || value === 'true' || value === '1',
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
else if (externalFilter.type === 'daterange') {
|
|
206
|
+
if (typeof value === 'object' && 'from' in value && 'to' in value) {
|
|
207
|
+
if (value?.from) {
|
|
208
|
+
filters.push({
|
|
209
|
+
field,
|
|
210
|
+
operator: 'GREATER_OR_EQUAL',
|
|
211
|
+
data: DateUtilities.begin(value?.from).toISOString(),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
if (value?.to) {
|
|
215
|
+
filters.push({
|
|
216
|
+
field,
|
|
217
|
+
operator: 'LESS_THAN',
|
|
218
|
+
data: DateUtilities.begin(DateUtilities.addDays(value?.to, 1)).toISOString(),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else if (externalFilter.type === 'date' || externalFilter.type === 'datetime') {
|
|
224
|
+
if (typeof value === 'object' && 'from' in value && 'to' in value) {
|
|
225
|
+
if (value?.from) {
|
|
226
|
+
filters.push({
|
|
227
|
+
field,
|
|
228
|
+
operator: 'GREATER_OR_EQUAL',
|
|
229
|
+
data: DateUtilities.begin(value?.from).toISOString(),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
if (value?.to) {
|
|
233
|
+
filters.push({
|
|
234
|
+
field,
|
|
235
|
+
operator: 'LESS_THAN',
|
|
236
|
+
data: DateUtilities.begin(DateUtilities.addDays(value?.to, 1)).toISOString(),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else if (DateUtilities.isDate(value)) {
|
|
241
|
+
if (externalFilter.type === 'date') {
|
|
242
|
+
if (externalFilter.defaultOperator === 'GREATER_OR_EQUAL') {
|
|
243
|
+
filters.push({
|
|
244
|
+
field,
|
|
245
|
+
operator: 'GREATER_OR_EQUAL',
|
|
246
|
+
data: DateUtilities.begin(value).toISOString(),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (externalFilter.defaultOperator === 'LESS_OR_EQUAL') {
|
|
250
|
+
filters.push({
|
|
251
|
+
field,
|
|
252
|
+
operator: 'LESS_THAN',
|
|
253
|
+
data: DateUtilities.begin(DateUtilities.addDays(value, 1)).toISOString(),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
if (externalFilter.defaultOperator === 'GREATER_OR_EQUAL') {
|
|
259
|
+
filters.push({
|
|
260
|
+
field,
|
|
261
|
+
operator: 'GREATER_OR_EQUAL',
|
|
262
|
+
data: new Date(value).toISOString(),
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
if (externalFilter.defaultOperator === 'LESS_OR_EQUAL') {
|
|
266
|
+
filters.push({
|
|
267
|
+
field,
|
|
268
|
+
operator: 'LESS_OR_EQUAL',
|
|
269
|
+
data: new Date(value).toISOString(),
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
if (Array.isArray(value)) {
|
|
277
|
+
if (value.length) {
|
|
278
|
+
filters.push({
|
|
279
|
+
field,
|
|
280
|
+
operator: 'IN',
|
|
281
|
+
data: value,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
else if (typeof value === 'object' && 'from' in value && 'to' in value) {
|
|
286
|
+
if (value?.from) {
|
|
287
|
+
filters.push({
|
|
288
|
+
field,
|
|
289
|
+
operator: 'GREATER_OR_EQUAL',
|
|
290
|
+
data: DateUtilities.begin(value?.from).toISOString(),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
if (value?.to) {
|
|
294
|
+
filters.push({
|
|
295
|
+
field,
|
|
296
|
+
operator: 'LESS_THAN',
|
|
297
|
+
data: DateUtilities.begin(DateUtilities.addDays(value?.to, 1)).toISOString(),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
filters.push({
|
|
303
|
+
field,
|
|
304
|
+
operator: externalFilter.defaultOperator || 'EQUAL',
|
|
305
|
+
data: value,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Xử lý column filter
|
|
312
|
+
for (const column of columns || []) {
|
|
313
|
+
const value = rawColumnFilter?.[column.field];
|
|
314
|
+
const field = fieldMapping?.[column.field] || column.field;
|
|
315
|
+
const operator = columnOperator?.[column.field] || column.filter?.operator?.default;
|
|
316
|
+
// Nếu có giá trị thì mới xử lý filter
|
|
317
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
318
|
+
if (column.type === 'string') {
|
|
319
|
+
filters.push({
|
|
320
|
+
field,
|
|
321
|
+
operator: operator || 'CONTAIN',
|
|
322
|
+
data: value,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
else if (column.type === 'boolean') {
|
|
326
|
+
filters.push({
|
|
327
|
+
field,
|
|
328
|
+
operator: 'EQUAL',
|
|
329
|
+
data: value === true || value === 1 || value === 'true' || value === '1',
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
else if (column.type === 'date' || column.type === 'datetime') {
|
|
333
|
+
if (typeof value === 'object' && 'from' in value && 'to' in value) {
|
|
334
|
+
if (value?.from && value?.to) {
|
|
335
|
+
filters.push({
|
|
336
|
+
field,
|
|
337
|
+
operator: 'BETWEEN',
|
|
338
|
+
data: {
|
|
339
|
+
from: DateUtilities.begin(value?.from).toISOString(),
|
|
340
|
+
to: DateUtilities.end(value?.to).toISOString(),
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
else if (value?.from) {
|
|
345
|
+
filters.push({
|
|
346
|
+
field,
|
|
347
|
+
operator: 'GREATER_OR_EQUAL',
|
|
348
|
+
data: DateUtilities.begin(value?.from).toISOString(),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
else if (value?.to) {
|
|
352
|
+
filters.push({
|
|
353
|
+
field,
|
|
354
|
+
operator: 'LESS_THAN',
|
|
355
|
+
data: DateUtilities.begin(DateUtilities.addDays(value?.to, 1)).toISOString(),
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
if (DateUtilities.isDate(value)) {
|
|
361
|
+
filters.push({
|
|
362
|
+
field,
|
|
363
|
+
operator: 'BETWEEN',
|
|
364
|
+
data: {
|
|
365
|
+
from: DateUtilities.begin(value).toISOString(),
|
|
366
|
+
to: DateUtilities.end(value).toISOString(),
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
if (Array.isArray(value)) {
|
|
374
|
+
if (value.length) {
|
|
375
|
+
filters.push({
|
|
376
|
+
field,
|
|
377
|
+
operator: 'IN',
|
|
378
|
+
data: value,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
filters.push({
|
|
384
|
+
field,
|
|
385
|
+
operator: operator || 'EQUAL',
|
|
386
|
+
data: value,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// Xử lý orders
|
|
393
|
+
if (orderBy && orderDirection) {
|
|
394
|
+
req.orders.push({
|
|
395
|
+
field: orderBy,
|
|
396
|
+
direction: orderDirection,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
return req;
|
|
400
|
+
};
|
|
401
|
+
|
|
166
402
|
class ColumnTitleComponent {
|
|
167
403
|
column = input.required();
|
|
168
404
|
titleDef = input(undefined);
|
|
@@ -181,8 +417,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
181
417
|
args: [{ selector: 'column-title', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [NgTemplateOutlet], template: "@let _column = column();\n@let _templateRef = templateRef();\n\n@if (_templateRef) {\n <ng-container *ngTemplateOutlet=\"_templateRef\"></ng-container>\n} @else {\n {{ _column.title }}\n}\n" }]
|
|
182
418
|
}] });
|
|
183
419
|
|
|
184
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
185
420
|
class ColumnFilterComponent {
|
|
421
|
+
#lastOnChangeValueHash;
|
|
186
422
|
// ==========================================
|
|
187
423
|
// 1. SIGNAL INPUTS
|
|
188
424
|
// ==========================================
|
|
@@ -234,8 +470,6 @@ class ColumnFilterComponent {
|
|
|
234
470
|
});
|
|
235
471
|
// Chỉ các Operator value cho phép — truyền vào <sd-operator [operators]>.
|
|
236
472
|
operatorValues = computed(() => this.operators().map(o => o.value));
|
|
237
|
-
// Margin wrapper operator: số + đang chọn operator → thêm mb-4 (canh baseline với sd-input-number).
|
|
238
|
-
operatorWrapperClass = computed(() => (this.column()?.type === 'number' && this.operator() ? 'mb-4 mr-2' : 'mr-2'));
|
|
239
473
|
// Items cho sd-select khi column type = values / lazy-values
|
|
240
474
|
items = computed(() => {
|
|
241
475
|
const col = this.column();
|
|
@@ -269,20 +503,40 @@ class ColumnFilterComponent {
|
|
|
269
503
|
if (col.type === 'number' && col.filter?.type === 'split-number') {
|
|
270
504
|
filter[col.field] = filter[col.field] || { from: null, to: null };
|
|
271
505
|
}
|
|
506
|
+
this.#lastOnChangeValueHash = this.#filterValueHash(filter[col.field]);
|
|
272
507
|
});
|
|
273
508
|
}
|
|
274
509
|
// ==========================================
|
|
275
510
|
// 4. HANDLERS
|
|
276
511
|
// ==========================================
|
|
277
|
-
onFilterChange = () =>
|
|
512
|
+
onFilterChange = () => {
|
|
513
|
+
this.#emitFilterOnChange();
|
|
514
|
+
this.filterChange.emit();
|
|
515
|
+
};
|
|
278
516
|
// Blur input: commit giá trị KHÔNG trigger reload.
|
|
279
|
-
onFilterCommit = () =>
|
|
517
|
+
onFilterCommit = () => {
|
|
518
|
+
this.#emitFilterOnChange();
|
|
519
|
+
this.filterCommit.emit();
|
|
520
|
+
};
|
|
521
|
+
#emitFilterOnChange = () => {
|
|
522
|
+
const col = this.column();
|
|
523
|
+
const filter = this.columnFilter();
|
|
524
|
+
if (!col?.filter?.onChange || !filter)
|
|
525
|
+
return;
|
|
526
|
+
const value = filter[col.field];
|
|
527
|
+
const hash = this.#filterValueHash(value);
|
|
528
|
+
if (hash === this.#lastOnChangeValueHash)
|
|
529
|
+
return;
|
|
530
|
+
this.#lastOnChangeValueHash = hash;
|
|
531
|
+
col.filter.onChange(value, col, filter);
|
|
532
|
+
};
|
|
533
|
+
#filterValueHash = (value) => Utilities.hash({ value });
|
|
280
534
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ColumnFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
281
|
-
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 });
|
|
535
|
+
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-center\" 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 <sd-operator class=\"mr-4\" [(model)]=\"operator\" [operators]=\"operatorValues()\" />\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 *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 *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.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 });
|
|
282
536
|
}
|
|
283
537
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ColumnFilterComponent, decorators: [{
|
|
284
538
|
type: Component,
|
|
285
|
-
args: [{ selector: 'column-filter', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, SdOperator, SdInput, SdInputNumber, SdSelect, SdDate, SdDateRange, SdBadge], 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-
|
|
539
|
+
args: [{ selector: 'column-filter', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, SdOperator, SdInput, SdInputNumber, SdSelect, SdDate, SdDateRange, SdBadge], 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-center\" 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 <sd-operator class=\"mr-4\" [(model)]=\"operator\" [operators]=\"operatorValues()\" />\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 *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 *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"] }]
|
|
286
540
|
}], ctorParameters: () => [] });
|
|
287
541
|
|
|
288
542
|
// Đối với filter loại values, items có thể là 1 mảng, có thể là 1 hàm, do đó cần xử lý nếu là hàm có thể xử lý gọi API
|
|
@@ -335,7 +589,7 @@ const getChildrenFromData = (data, option) => {
|
|
|
335
589
|
* mặc định nhánh chưa nạp).
|
|
336
590
|
*/
|
|
337
591
|
const resolveDefaultExpanded = (level, option) => {
|
|
338
|
-
const def = option?.loadType === 'static' ? option.defaultExpanded ?? false : false;
|
|
592
|
+
const def = option?.loadType === 'static' ? (option.defaultExpanded ?? false) : false;
|
|
339
593
|
if (def === true)
|
|
340
594
|
return true;
|
|
341
595
|
if (def === false)
|
|
@@ -391,6 +645,48 @@ const subtreeMatches = (data, predicate, option, visited = new Set()) => {
|
|
|
391
645
|
* bị mất khi từ khoá search đổi. Đây là bước "prune" của tính năng search-con.
|
|
392
646
|
*/
|
|
393
647
|
const filterMatchingChildren = (children, predicate, option) => children.filter(child => subtreeMatches(child, predicate, option));
|
|
648
|
+
const getVisibleChildrenData = (data, option, predicate) => {
|
|
649
|
+
const children = getChildrenFromData(data, option);
|
|
650
|
+
return predicate ? filterMatchingChildren(children, predicate, option) : children;
|
|
651
|
+
};
|
|
652
|
+
const saveTreeExpandState = (rows, expandState) => {
|
|
653
|
+
for (const row of rows) {
|
|
654
|
+
if (row.meta.tree?.isExpanded) {
|
|
655
|
+
expandState.set(row.meta.id, true);
|
|
656
|
+
}
|
|
657
|
+
const children = row.meta.tree?.childItems;
|
|
658
|
+
if (children?.length) {
|
|
659
|
+
saveTreeExpandState(children, expandState);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
const clearTreeChildCache = (rows) => {
|
|
664
|
+
for (const row of rows) {
|
|
665
|
+
const children = row.meta.tree?.childItems;
|
|
666
|
+
if (children?.length) {
|
|
667
|
+
clearTreeChildCache(children);
|
|
668
|
+
row.meta.tree.childItems = undefined;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
const initTreeMeta = (rows, option, args = {}) => {
|
|
673
|
+
const { expandState, treeSearchPredicate, level = 0, parentId } = args;
|
|
674
|
+
const searchActive = !!treeSearchPredicate;
|
|
675
|
+
for (const row of rows) {
|
|
676
|
+
const saved = expandState?.get(row.meta.id);
|
|
677
|
+
const hasChildren = searchActive
|
|
678
|
+
? getVisibleChildrenData(row.data, option, treeSearchPredicate).length > 0
|
|
679
|
+
: resolveHasChildren(row, option);
|
|
680
|
+
row.meta.tree = {
|
|
681
|
+
...row.meta.tree,
|
|
682
|
+
level,
|
|
683
|
+
parentId,
|
|
684
|
+
hasChildren,
|
|
685
|
+
isExpanded: searchActive ? hasChildren : (saved ?? resolveDefaultExpanded(level, option)),
|
|
686
|
+
isExpanding: false,
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
};
|
|
394
690
|
const flattenTree = (roots, option, visited = new Set()) => {
|
|
395
691
|
if (!option)
|
|
396
692
|
return roots;
|
|
@@ -490,8 +786,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
490
786
|
args: [{ name: 'sdTree' }]
|
|
491
787
|
}] });
|
|
492
788
|
|
|
493
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
494
|
-
// eslint-disable-next-line @angular-eslint/component-class-suffix
|
|
495
789
|
class ExternalFilterComponent {
|
|
496
790
|
// ==========================================
|
|
497
791
|
// 1. SIGNAL INPUTS
|
|
@@ -639,7 +933,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
639
933
|
], template: "@let _filter = filter();\n@let _autoId = autoId();\n@let _filterItems = 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 && _filterItems.length) {\n <sd-section\n [hideHeader]=\"_filter?.hideExternalFilterToolbar\"\n icon=\"filter_alt\"\n iconColor=\"secondary\"\n [title]=\"'core.component.table.filter' | translate\"\n [collapsible]=\"_filter?.collapsible ?? _filter?.collapsable ?? true\">\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 _filterItems; 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 _filterItems; 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"] }]
|
|
640
934
|
}], ctorParameters: () => [] });
|
|
641
935
|
|
|
642
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
643
936
|
/**
|
|
644
937
|
* Mobile-filter — side-drawer chiếm ~90% width thiết bị. Dùng cho table khi
|
|
645
938
|
* render trên màn hình điện thoại (inline filter bị ẩn).
|
|
@@ -857,8 +1150,6 @@ const DEFAULT_TABLE_CONFIG = {
|
|
|
857
1150
|
const SD_TABLE_CONFIGURATION = new InjectionToken('sd-table.configuration');
|
|
858
1151
|
|
|
859
1152
|
class ConfigService {
|
|
860
|
-
storageService;
|
|
861
|
-
tableConfiguration;
|
|
862
1153
|
#COLUMNS = {
|
|
863
1154
|
SUBINFORMATION: 'sdSubInformationAction',
|
|
864
1155
|
COMMAND: 'sdCommand',
|
|
@@ -872,10 +1163,8 @@ class ConfigService {
|
|
|
872
1163
|
#storage;
|
|
873
1164
|
#widthChange = new Subject();
|
|
874
1165
|
widthChange$ = this.#widthChange.asObservable();
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
this.tableConfiguration = tableConfiguration;
|
|
878
|
-
}
|
|
1166
|
+
storageService = inject(SdStorageService);
|
|
1167
|
+
tableConfiguration = inject(SD_TABLE_CONFIGURATION, { optional: true });
|
|
879
1168
|
#loadConfiguredTable = (option) => {
|
|
880
1169
|
// Nếu không có key thì không lấy được setting
|
|
881
1170
|
if (!option?.key) {
|
|
@@ -1064,20 +1353,13 @@ class ConfigService {
|
|
|
1064
1353
|
columns,
|
|
1065
1354
|
};
|
|
1066
1355
|
};
|
|
1067
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ConfigService, deps: [
|
|
1356
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1068
1357
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ConfigService });
|
|
1069
1358
|
}
|
|
1070
1359
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: ConfigService, decorators: [{
|
|
1071
1360
|
type: Injectable
|
|
1072
|
-
}]
|
|
1073
|
-
type: Inject,
|
|
1074
|
-
args: [SD_TABLE_CONFIGURATION]
|
|
1075
|
-
}, {
|
|
1076
|
-
type: Optional
|
|
1077
|
-
}] }] });
|
|
1361
|
+
}] });
|
|
1078
1362
|
|
|
1079
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1080
|
-
// eslint-disable-next-line @angular-eslint/component-class-suffix
|
|
1081
1363
|
class ConfigComponent {
|
|
1082
1364
|
// ==========================================
|
|
1083
1365
|
// 1. SIGNAL INPUTS / OUTPUTS
|
|
@@ -1304,18 +1586,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
1304
1586
|
}], ctorParameters: () => [] });
|
|
1305
1587
|
|
|
1306
1588
|
/**
|
|
1307
|
-
*
|
|
1308
|
-
* chứa `disabled`, `title`, `icon`, và `htmlTemplate` (khi command là child có
|
|
1309
|
-
* htmlTemplate). Một lần gọi cho cả 4 truy vấn, template dùng `@let` để alias.
|
|
1310
|
-
*
|
|
1311
|
-
* Trước đây tách thành 3 pipe `commandDisable` / `commandTitle` / `commandIcon`.
|
|
1312
|
-
*
|
|
1313
|
-
* @example
|
|
1314
|
-
* @let meta = item | command:cmd;
|
|
1315
|
-
* <button [disabled]="meta.disabled" [matTooltip]="meta.title">
|
|
1316
|
-
* <mat-icon>{{ meta.icon }}</mat-icon>
|
|
1317
|
-
* <div [innerHTML]="meta.htmlTemplate"></div>
|
|
1318
|
-
* </button>
|
|
1589
|
+
* Resolves command metadata for the current row once, then lets the template reuse it with `@let`.
|
|
1319
1590
|
*/
|
|
1320
1591
|
class CommandPipe {
|
|
1321
1592
|
transform(item, command) {
|
|
@@ -1342,8 +1613,7 @@ class CommandPipe {
|
|
|
1342
1613
|
return '';
|
|
1343
1614
|
return typeof command.icon === 'string' ? command.icon : command.icon(item.data);
|
|
1344
1615
|
};
|
|
1345
|
-
//
|
|
1346
|
-
// Parent (SdTableCommandChildren) không khai báo — trả về undefined.
|
|
1616
|
+
// Only child commands can provide htmlTemplate.
|
|
1347
1617
|
#htmlTemplate = (item, command) => {
|
|
1348
1618
|
const tpl = command?.htmlTemplate;
|
|
1349
1619
|
return typeof tpl === 'function' ? tpl(item.data) : undefined;
|
|
@@ -1353,18 +1623,11 @@ class CommandPipe {
|
|
|
1353
1623
|
}
|
|
1354
1624
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: CommandPipe, decorators: [{
|
|
1355
1625
|
type: Pipe,
|
|
1356
|
-
args: [{ name: 'command' }]
|
|
1626
|
+
args: [{ name: 'command', standalone: true }]
|
|
1357
1627
|
}] });
|
|
1358
1628
|
|
|
1359
1629
|
/**
|
|
1360
|
-
*
|
|
1361
|
-
* cho từng row. Output dùng trực tiếp trong template không cần thêm pipe phụ.
|
|
1362
|
-
*
|
|
1363
|
-
* Đã rename từ `commandFilter` → `filter` sau khi gom toàn bộ pipe command vào
|
|
1364
|
-
* `components/command/pipes/`.
|
|
1365
|
-
*
|
|
1366
|
-
* @example
|
|
1367
|
-
* @let filteredCommands = item | filter:commands | async;
|
|
1630
|
+
* Filters visible commands and pre-resolves icon, title, and stable key for the current row.
|
|
1368
1631
|
*/
|
|
1369
1632
|
class CommandFilterPipe {
|
|
1370
1633
|
async transform(item, commands) {
|
|
@@ -1431,7 +1694,7 @@ class CommandFilterPipe {
|
|
|
1431
1694
|
return '';
|
|
1432
1695
|
return typeof command.title === 'string' ? command.title : command.title(item.data);
|
|
1433
1696
|
};
|
|
1434
|
-
//
|
|
1697
|
+
// Stable autoId key generated from resolved icon and title.
|
|
1435
1698
|
#key = (item, command) => {
|
|
1436
1699
|
const icon = this.#icon(item, command);
|
|
1437
1700
|
const title = this.#title(item, command);
|
|
@@ -1442,46 +1705,36 @@ class CommandFilterPipe {
|
|
|
1442
1705
|
}
|
|
1443
1706
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: CommandFilterPipe, decorators: [{
|
|
1444
1707
|
type: Pipe,
|
|
1445
|
-
args: [{ name: 'filter' }]
|
|
1708
|
+
args: [{ name: 'filter', standalone: true }]
|
|
1446
1709
|
}] });
|
|
1447
1710
|
|
|
1448
|
-
// eslint-disable-next-line @angular-eslint/component-class-suffix
|
|
1449
1711
|
class DesktopCommand {
|
|
1450
|
-
|
|
1451
|
-
// 1. SIGNAL INPUTS
|
|
1452
|
-
// ==========================================
|
|
1712
|
+
defaultIconFontSet = DefaultMaterialIconFontSet;
|
|
1453
1713
|
autoIdInput = input(undefined, { alias: 'autoId' });
|
|
1454
1714
|
item = input.required();
|
|
1455
1715
|
itemIndex = input.required();
|
|
1456
1716
|
commands = input([]);
|
|
1457
|
-
//
|
|
1458
|
-
// 2. COMPUTED
|
|
1459
|
-
// ==========================================
|
|
1460
|
-
// Key định danh row dùng để build autoId: ưu tiên id → code → value → fallback itemIndex.
|
|
1717
|
+
// Row key for autoId: prefer id, then code, then value, then itemIndex.
|
|
1461
1718
|
itemKey = computed(() => {
|
|
1462
1719
|
const data = this.item()?.data;
|
|
1463
1720
|
return data?.['id']?.toString() || data?.['code']?.toString() || data?.['value']?.toString() || this.itemIndex().toString();
|
|
1464
1721
|
});
|
|
1465
|
-
// autoId
|
|
1466
|
-
// Template ghép tiếp `-<commandKey>` (và `-<childKey>` nếu child) cho mỗi button.
|
|
1722
|
+
// Host autoId: `<base>-command-<itemKey>`. Template appends command and child keys.
|
|
1467
1723
|
autoId = computed(() => {
|
|
1468
1724
|
const base = this.autoIdInput();
|
|
1469
1725
|
return base ? `${base}-command-${this.itemKey()}` : '';
|
|
1470
1726
|
});
|
|
1471
|
-
// Commands signal (mặc định mảng rỗng).
|
|
1472
1727
|
_commands = computed(() => this.commands() || []);
|
|
1473
1728
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: DesktopCommand, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1474
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: DesktopCommand, isStandalone: true, selector: "desktop-command", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, itemIndex: { classPropertyName: "itemIndex", publicName: "itemIndex", isSignal: true, isRequired: true, transformFunction: null }, commands: { classPropertyName: "commands", publicName: "commands", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-autoid": "autoId()" } }, ngImport: i0, template: "@let _autoId = autoId();\n@let _item = item();\n@let filteredCommands = _item | filter: _commands() | async;\n\n@if (filteredCommands?.length) {\n @for (command of filteredCommands; track command.key) {\n @let _meta = _item | command: command;\n @if (command.type === 'normal') {\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key\"\n (click)=\"command.click(_item.data)\"\n [matTooltip]=\"_meta.title\"\n aria-hidden=\"true\"\n [disabled]=\"_meta.disabled\"\n mat-icon-button>\n <mat-icon class=\"c-icon\" [color]=\"command.color || 'secondary'\" [fontSet]=\"command.fontSet
|
|
1729
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: DesktopCommand, isStandalone: true, selector: "desktop-command", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, itemIndex: { classPropertyName: "itemIndex", publicName: "itemIndex", isSignal: true, isRequired: true, transformFunction: null }, commands: { classPropertyName: "commands", publicName: "commands", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-autoid": "autoId()" } }, ngImport: i0, template: "@let _autoId = autoId();\n@let _item = item();\n@let filteredCommands = _item | filter: _commands() | async;\n\n@if (filteredCommands?.length) {\n @for (command of filteredCommands; track command.key) {\n @let _meta = _item | command: command;\n @if (command.type === 'normal') {\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key\"\n (click)=\"command.click(_item.data)\"\n [matTooltip]=\"_meta.title\"\n aria-hidden=\"true\"\n [disabled]=\"_meta.disabled\"\n mat-icon-button>\n <mat-icon class=\"c-icon\" [color]=\"command.color || 'secondary'\" [fontSet]=\"command.fontSet || defaultIconFontSet\">\n {{ _meta.icon }}\n </mat-icon>\n </button>\n } @else {\n <button [attr.data-autoid]=\"_autoId + '-' + command.key\" [matMenuTriggerFor]=\"menu\" aria-hidden=\"true\" mat-icon-button>\n @if (_meta.icon) {\n <mat-icon class=\"c-icon\" [fontSet]=\"command.fontSet || defaultIconFontSet\">{{ _meta.icon }}</mat-icon>\n } @else {\n <mat-icon class=\"c-icon\" [fontSet]=\"command.fontSet || defaultIconFontSet\">more_vert</mat-icon>\n }\n </button>\n <mat-menu #menu=\"matMenu\" [attr.data-autoid]=\"_autoId + '-' + command.key + '-menu'\">\n <span>\n @for (childCommand of command.children; track childCommand.key) {\n @let _childMeta = _item | command: childCommand;\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key + '-' + childCommand.key\"\n mat-menu-item\n (click)=\"childCommand.click(_item.data)\"\n [disabled]=\"_childMeta.disabled\">\n @if (_childMeta.htmlTemplate) {\n <div [innerHTML]=\"_childMeta.htmlTemplate\"></div>\n } @else {\n <span class=\"sd-command-menu-item__content\">\n <mat-icon\n class=\"sd-command-menu-item__icon\"\n [color]=\"childCommand.color || 'secondary'\"\n [fontSet]=\"childCommand.fontSet || defaultIconFontSet\">\n {{ _childMeta.icon }}\n </mat-icon>\n <span class=\"sd-command-menu-item__title\">{{ _childMeta.title }}</span>\n </span>\n }\n </button>\n }\n </span>\n </mat-menu>\n }\n }\n}\n", styles: [".text-primary{color:var(--sd-primary)!important}.bg-primary{background:var(--sd-primary)!important}.border-primary{border-color:var(--sd-primary)!important}.text-primary-light{color:var(--sd-primary-light)!important}.bg-primary-light{background:var(--sd-primary-light)!important}.border-primary-light{border-color:var(--sd-primary-light)!important}.text-primary-dark{color:var(--sd-primary-dark)!important}.bg-primary-dark{background:var(--sd-primary-dark)!important}.border-primary-dark{border-color:var(--sd-primary-dark)!important}.text-info{color:var(--sd-info)!important}.bg-info{background:var(--sd-info)!important}.border-info{border-color:var(--sd-info)!important}.text-info-light{color:var(--sd-info-light)!important}.bg-info-light{background:var(--sd-info-light)!important}.border-info-light{border-color:var(--sd-info-light)!important}.text-info-dark{color:var(--sd-info-dark)!important}.bg-info-dark{background:var(--sd-info-dark)!important}.border-info-dark{border-color:var(--sd-info-dark)!important}.text-success{color:var(--sd-success)!important}.bg-success{background:var(--sd-success)!important}.border-success{border-color:var(--sd-success)!important}.text-success-light{color:var(--sd-success-light)!important}.bg-success-light{background:var(--sd-success-light)!important}.border-success-light{border-color:var(--sd-success-light)!important}.text-success-dark{color:var(--sd-success-dark)!important}.bg-success-dark{background:var(--sd-success-dark)!important}.border-success-dark{border-color:var(--sd-success-dark)!important}.text-warning{color:var(--sd-warning)!important}.bg-warning{background:var(--sd-warning)!important}.border-warning{border-color:var(--sd-warning)!important}.text-warning-light{color:var(--sd-warning-light)!important}.bg-warning-light{background:var(--sd-warning-light)!important}.border-warning-light{border-color:var(--sd-warning-light)!important}.text-warning-dark{color:var(--sd-warning-dark)!important}.bg-warning-dark{background:var(--sd-warning-dark)!important}.border-warning-dark{border-color:var(--sd-warning-dark)!important}.text-error{color:var(--sd-error)!important}.bg-error{background:var(--sd-error)!important}.border-error{border-color:var(--sd-error)!important}.text-error-light{color:var(--sd-error-light)!important}.bg-error-light{background:var(--sd-error-light)!important}.border-error-light{border-color:var(--sd-error-light)!important}.text-error-dark{color:var(--sd-error-dark)!important}.bg-error-dark{background:var(--sd-error-dark)!important}.border-error-dark{border-color:var(--sd-error-dark)!important}.text-secondary{color:var(--sd-secondary)!important}.bg-secondary{background:var(--sd-secondary)!important}.border-secondary{border-color:var(--sd-secondary)!important}.text-secondary-light{color:var(--sd-secondary-light)!important}.bg-secondary-light{background:var(--sd-secondary-light)!important}.border-secondary-light{border-color:var(--sd-secondary-light)!important}.text-secondary-dark{color:var(--sd-secondary-dark)!important}.bg-secondary-dark{background:var(--sd-secondary-dark)!important}.border-secondary-dark{border-color:var(--sd-secondary-dark)!important}.text-light{color:var(--sd-light)!important}.bg-light{background:var(--sd-light)!important}.border-light{border-color:var(--sd-light)!important}.text-dark{color:var(--sd-dark)!important}.bg-dark{background:var(--sd-dark)!important}.border-dark{border-color:var(--sd-dark)!important}.text-black500{color:var(--sd-black500)!important}.bg-black500{background:var(--sd-black500)!important}.border-black500{border-color:var(--sd-black500)!important}.text-black400{color:var(--sd-black400)!important}.bg-black400{background:var(--sd-black400)!important}.border-black400{border-color:var(--sd-black400)!important}.text-black300{color:var(--sd-black300)!important}.bg-black300{background:var(--sd-black300)!important}.border-black300{border-color:var(--sd-black300)!important}.text-black200{color:var(--sd-black200)!important}.bg-black200{background:var(--sd-black200)!important}.border-black200{border-color:var(--sd-black200)!important}.text-black100{color:var(--sd-black100)!important}.bg-black100{background:var(--sd-black100)!important}.border-black100{border-color:var(--sd-black100)!important}.text-white{color:#fff!important}.bg-white{background:#fff!important}.border-white{border-color:#fff!important}.text-black{color:#000!important}.bg-black{background:#000!important}.border-black{border-color:#000!important}:host{display:flex;align-items:center}button[mat-icon-button]{width:24px;height:24px;line-height:24px;padding:0}mat-icon.c-icon{color:var(--sd-black400)!important;width:16px;height:16px;font-size:16px;line-height:16px}.sd-command-menu-item__content{display:inline-flex;align-items:center;gap:8px;min-width:0;width:100%}.sd-command-menu-item__icon{color:var(--sd-black400)!important;width:24px;height:24px;font-size:18px;line-height:24px!important;margin-right:0!important;flex:0 0 24px}.sd-command-menu-item__title{min-width:0;line-height:20px;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { 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: MatButtonModule }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: CommandPipe, name: "command" }, { kind: "pipe", type: CommandFilterPipe, name: "filter" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1475
1730
|
}
|
|
1476
1731
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: DesktopCommand, decorators: [{
|
|
1477
1732
|
type: Component,
|
|
1478
1733
|
args: [{ selector: 'desktop-command', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, host: {
|
|
1479
1734
|
'[attr.data-autoid]': 'autoId()',
|
|
1480
|
-
}, imports: [CommonModule, MatTooltipModule, MatMenuModule, MatButtonModule, MatIconModule, CommandPipe, CommandFilterPipe], template: "@let _autoId = autoId();\n@let _item = item();\n@let filteredCommands = _item | filter: _commands() | async;\n\n@if (filteredCommands?.length) {\n @for (command of filteredCommands; track command.key) {\n @let _meta = _item | command: command;\n @if (command.type === 'normal') {\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key\"\n (click)=\"command.click(_item.data)\"\n [matTooltip]=\"_meta.title\"\n aria-hidden=\"true\"\n [disabled]=\"_meta.disabled\"\n mat-icon-button>\n <mat-icon class=\"c-icon\" [color]=\"command.color || 'secondary'\" [fontSet]=\"command.fontSet
|
|
1735
|
+
}, imports: [CommonModule, MatTooltipModule, MatMenuModule, MatButtonModule, MatIconModule, CommandPipe, CommandFilterPipe], template: "@let _autoId = autoId();\n@let _item = item();\n@let filteredCommands = _item | filter: _commands() | async;\n\n@if (filteredCommands?.length) {\n @for (command of filteredCommands; track command.key) {\n @let _meta = _item | command: command;\n @if (command.type === 'normal') {\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key\"\n (click)=\"command.click(_item.data)\"\n [matTooltip]=\"_meta.title\"\n aria-hidden=\"true\"\n [disabled]=\"_meta.disabled\"\n mat-icon-button>\n <mat-icon class=\"c-icon\" [color]=\"command.color || 'secondary'\" [fontSet]=\"command.fontSet || defaultIconFontSet\">\n {{ _meta.icon }}\n </mat-icon>\n </button>\n } @else {\n <button [attr.data-autoid]=\"_autoId + '-' + command.key\" [matMenuTriggerFor]=\"menu\" aria-hidden=\"true\" mat-icon-button>\n @if (_meta.icon) {\n <mat-icon class=\"c-icon\" [fontSet]=\"command.fontSet || defaultIconFontSet\">{{ _meta.icon }}</mat-icon>\n } @else {\n <mat-icon class=\"c-icon\" [fontSet]=\"command.fontSet || defaultIconFontSet\">more_vert</mat-icon>\n }\n </button>\n <mat-menu #menu=\"matMenu\" [attr.data-autoid]=\"_autoId + '-' + command.key + '-menu'\">\n <span>\n @for (childCommand of command.children; track childCommand.key) {\n @let _childMeta = _item | command: childCommand;\n <button\n [attr.data-autoid]=\"_autoId + '-' + command.key + '-' + childCommand.key\"\n mat-menu-item\n (click)=\"childCommand.click(_item.data)\"\n [disabled]=\"_childMeta.disabled\">\n @if (_childMeta.htmlTemplate) {\n <div [innerHTML]=\"_childMeta.htmlTemplate\"></div>\n } @else {\n <span class=\"sd-command-menu-item__content\">\n <mat-icon\n class=\"sd-command-menu-item__icon\"\n [color]=\"childCommand.color || 'secondary'\"\n [fontSet]=\"childCommand.fontSet || defaultIconFontSet\">\n {{ _childMeta.icon }}\n </mat-icon>\n <span class=\"sd-command-menu-item__title\">{{ _childMeta.title }}</span>\n </span>\n }\n </button>\n }\n </span>\n </mat-menu>\n }\n }\n}\n", styles: [".text-primary{color:var(--sd-primary)!important}.bg-primary{background:var(--sd-primary)!important}.border-primary{border-color:var(--sd-primary)!important}.text-primary-light{color:var(--sd-primary-light)!important}.bg-primary-light{background:var(--sd-primary-light)!important}.border-primary-light{border-color:var(--sd-primary-light)!important}.text-primary-dark{color:var(--sd-primary-dark)!important}.bg-primary-dark{background:var(--sd-primary-dark)!important}.border-primary-dark{border-color:var(--sd-primary-dark)!important}.text-info{color:var(--sd-info)!important}.bg-info{background:var(--sd-info)!important}.border-info{border-color:var(--sd-info)!important}.text-info-light{color:var(--sd-info-light)!important}.bg-info-light{background:var(--sd-info-light)!important}.border-info-light{border-color:var(--sd-info-light)!important}.text-info-dark{color:var(--sd-info-dark)!important}.bg-info-dark{background:var(--sd-info-dark)!important}.border-info-dark{border-color:var(--sd-info-dark)!important}.text-success{color:var(--sd-success)!important}.bg-success{background:var(--sd-success)!important}.border-success{border-color:var(--sd-success)!important}.text-success-light{color:var(--sd-success-light)!important}.bg-success-light{background:var(--sd-success-light)!important}.border-success-light{border-color:var(--sd-success-light)!important}.text-success-dark{color:var(--sd-success-dark)!important}.bg-success-dark{background:var(--sd-success-dark)!important}.border-success-dark{border-color:var(--sd-success-dark)!important}.text-warning{color:var(--sd-warning)!important}.bg-warning{background:var(--sd-warning)!important}.border-warning{border-color:var(--sd-warning)!important}.text-warning-light{color:var(--sd-warning-light)!important}.bg-warning-light{background:var(--sd-warning-light)!important}.border-warning-light{border-color:var(--sd-warning-light)!important}.text-warning-dark{color:var(--sd-warning-dark)!important}.bg-warning-dark{background:var(--sd-warning-dark)!important}.border-warning-dark{border-color:var(--sd-warning-dark)!important}.text-error{color:var(--sd-error)!important}.bg-error{background:var(--sd-error)!important}.border-error{border-color:var(--sd-error)!important}.text-error-light{color:var(--sd-error-light)!important}.bg-error-light{background:var(--sd-error-light)!important}.border-error-light{border-color:var(--sd-error-light)!important}.text-error-dark{color:var(--sd-error-dark)!important}.bg-error-dark{background:var(--sd-error-dark)!important}.border-error-dark{border-color:var(--sd-error-dark)!important}.text-secondary{color:var(--sd-secondary)!important}.bg-secondary{background:var(--sd-secondary)!important}.border-secondary{border-color:var(--sd-secondary)!important}.text-secondary-light{color:var(--sd-secondary-light)!important}.bg-secondary-light{background:var(--sd-secondary-light)!important}.border-secondary-light{border-color:var(--sd-secondary-light)!important}.text-secondary-dark{color:var(--sd-secondary-dark)!important}.bg-secondary-dark{background:var(--sd-secondary-dark)!important}.border-secondary-dark{border-color:var(--sd-secondary-dark)!important}.text-light{color:var(--sd-light)!important}.bg-light{background:var(--sd-light)!important}.border-light{border-color:var(--sd-light)!important}.text-dark{color:var(--sd-dark)!important}.bg-dark{background:var(--sd-dark)!important}.border-dark{border-color:var(--sd-dark)!important}.text-black500{color:var(--sd-black500)!important}.bg-black500{background:var(--sd-black500)!important}.border-black500{border-color:var(--sd-black500)!important}.text-black400{color:var(--sd-black400)!important}.bg-black400{background:var(--sd-black400)!important}.border-black400{border-color:var(--sd-black400)!important}.text-black300{color:var(--sd-black300)!important}.bg-black300{background:var(--sd-black300)!important}.border-black300{border-color:var(--sd-black300)!important}.text-black200{color:var(--sd-black200)!important}.bg-black200{background:var(--sd-black200)!important}.border-black200{border-color:var(--sd-black200)!important}.text-black100{color:var(--sd-black100)!important}.bg-black100{background:var(--sd-black100)!important}.border-black100{border-color:var(--sd-black100)!important}.text-white{color:#fff!important}.bg-white{background:#fff!important}.border-white{border-color:#fff!important}.text-black{color:#000!important}.bg-black{background:#000!important}.border-black{border-color:#000!important}:host{display:flex;align-items:center}button[mat-icon-button]{width:24px;height:24px;line-height:24px;padding:0}mat-icon.c-icon{color:var(--sd-black400)!important;width:16px;height:16px;font-size:16px;line-height:16px}.sd-command-menu-item__content{display:inline-flex;align-items:center;gap:8px;min-width:0;width:100%}.sd-command-menu-item__icon{color:var(--sd-black400)!important;width:24px;height:24px;font-size:18px;line-height:24px!important;margin-right:0!important;flex:0 0 24px}.sd-command-menu-item__title{min-width:0;line-height:20px;white-space:nowrap}\n"] }]
|
|
1481
1736
|
}] });
|
|
1482
1737
|
|
|
1483
|
-
/* eslint-disable @angular-eslint/no-input-rename */
|
|
1484
|
-
// eslint-disable-next-line @angular-eslint/component-class-suffix
|
|
1485
1738
|
class SdPopupExport {
|
|
1486
1739
|
ref;
|
|
1487
1740
|
loadingService;
|
|
@@ -1660,7 +1913,7 @@ class SdPopupExport {
|
|
|
1660
1913
|
generateAndUploadTemplate = async (fileName) => {
|
|
1661
1914
|
const sheets = [];
|
|
1662
1915
|
if (Array.isArray(this.exportOption?.sheets)) {
|
|
1663
|
-
for (const sheet of this.exportOption
|
|
1916
|
+
for (const sheet of this.exportOption.sheets) {
|
|
1664
1917
|
if (sheet.name && sheet.items && sheet.headers) {
|
|
1665
1918
|
if (Array.isArray(sheet.items)) {
|
|
1666
1919
|
sheets.push({
|
|
@@ -2118,19 +2371,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
2118
2371
|
class SdSelectionDisabledPipe {
|
|
2119
2372
|
transform = (selectedItems, rowData, selection) => {
|
|
2120
2373
|
const { disabled, actions } = selection;
|
|
2374
|
+
if (rowData.meta.selector.isSelected) {
|
|
2375
|
+
rowData.meta.selector.selectable = true;
|
|
2376
|
+
return false;
|
|
2377
|
+
}
|
|
2121
2378
|
if (!actions?.length) {
|
|
2122
2379
|
if (!disabled) {
|
|
2123
2380
|
rowData.meta.selector.selectable = true;
|
|
2124
2381
|
return false;
|
|
2125
2382
|
}
|
|
2126
|
-
rowData.meta.selector.selectable = !disabled(rowData, selectedItems);
|
|
2383
|
+
rowData.meta.selector.selectable = !disabled(rowData.data, selectedItems.map(e => e.data));
|
|
2127
2384
|
return !rowData.meta.selector.selectable;
|
|
2128
2385
|
}
|
|
2129
2386
|
// Kiểm tra có bị disabled theo function không
|
|
2130
2387
|
// Dữ liệu chưa được check thì kiểm tra hàm disable nếu có
|
|
2131
|
-
if (disabled
|
|
2388
|
+
if (disabled) {
|
|
2132
2389
|
// Nếu disabled và dữ liệu chưa được check
|
|
2133
|
-
if (disabled(rowData, selectedItems)) {
|
|
2390
|
+
if (disabled(rowData.data, selectedItems.map(e => e.data))) {
|
|
2134
2391
|
return true;
|
|
2135
2392
|
}
|
|
2136
2393
|
}
|
|
@@ -2195,7 +2452,7 @@ class SdSelectionVisibleSelectAllPipe {
|
|
|
2195
2452
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
2196
2453
|
const first = items.find(t => t.meta.selector?.actions?.length);
|
|
2197
2454
|
if (first) {
|
|
2198
|
-
for (const action of first.meta.selector?.actions) {
|
|
2455
|
+
for (const action of first.meta.selector?.actions ?? []) {
|
|
2199
2456
|
if (items.filter(t => t.meta.selector?.actions?.length).every(e => e.meta.selector?.actions?.includes(action))) {
|
|
2200
2457
|
return true;
|
|
2201
2458
|
}
|
|
@@ -2221,7 +2478,7 @@ class SdSelectionVisiblePipe {
|
|
|
2221
2478
|
rowData.meta.selector.selectable = true;
|
|
2222
2479
|
return rowData.meta.selector.selectable;
|
|
2223
2480
|
}
|
|
2224
|
-
for (const action of selection
|
|
2481
|
+
for (const action of selection.actions) {
|
|
2225
2482
|
if ('children' in action) {
|
|
2226
2483
|
let flag = false;
|
|
2227
2484
|
let hasGroup = false;
|
|
@@ -2410,13 +2667,13 @@ class TableExportService {
|
|
|
2410
2667
|
}
|
|
2411
2668
|
}
|
|
2412
2669
|
else if (column.type === 'date') {
|
|
2413
|
-
obj[fieldStr] = DateUtilities.toFormat(itemValue, 'yyyy/MM/dd');
|
|
2670
|
+
obj[fieldStr] = DateUtilities$1.toFormat(itemValue, 'yyyy/MM/dd');
|
|
2414
2671
|
}
|
|
2415
2672
|
else if (column.type === 'datetime') {
|
|
2416
|
-
obj[fieldStr] = DateUtilities.toFormat(itemValue, 'yyyy/MM/dd HH:mm:ss');
|
|
2673
|
+
obj[fieldStr] = DateUtilities$1.toFormat(itemValue, 'yyyy/MM/dd HH:mm:ss');
|
|
2417
2674
|
}
|
|
2418
2675
|
else if (column.type === 'time') {
|
|
2419
|
-
obj[fieldStr] = DateUtilities.toFormat(itemValue, 'HH:mm:ss');
|
|
2676
|
+
obj[fieldStr] = DateUtilities$1.toFormat(itemValue, 'HH:mm:ss');
|
|
2420
2677
|
}
|
|
2421
2678
|
else if (column.type === 'values' || column.type === 'lazy-values') {
|
|
2422
2679
|
const vals = (Array.isArray(itemValue) ? itemValue : [itemValue]).filter(e => !!e?.toString());
|
|
@@ -2550,7 +2807,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
2550
2807
|
}] });
|
|
2551
2808
|
|
|
2552
2809
|
class TableFormatService {
|
|
2553
|
-
//
|
|
2810
|
+
// Keep table display formatting aligned with the public template pipes.
|
|
2811
|
+
#formatDatePipe = inject(SdFormatDatePipe);
|
|
2812
|
+
#formatDatetimePipe = inject(SdFormatDatetimePipe);
|
|
2554
2813
|
#formatNumberPipe = inject(SdFormatNumberPipe);
|
|
2555
2814
|
// ==========================================
|
|
2556
2815
|
// PUBLIC METHODS
|
|
@@ -2711,15 +2970,15 @@ class TableFormatService {
|
|
|
2711
2970
|
// PRIVATE HELPERS
|
|
2712
2971
|
// ==========================================
|
|
2713
2972
|
#formatDateDisplay(value, type) {
|
|
2714
|
-
const date =
|
|
2715
|
-
const time =
|
|
2973
|
+
const date = this.#formatDatePipe.transform(value);
|
|
2974
|
+
const time = this.#formatDatetimePipe.transform(value, 'HH:mm:ss');
|
|
2716
2975
|
if (type === 'datetime') {
|
|
2717
2976
|
return time && date ? `<div class="T14R">${date}<span class="T14R text-black400 ml-4">${time}</span></div>` : '';
|
|
2718
2977
|
}
|
|
2719
2978
|
if (type === 'date')
|
|
2720
|
-
return date
|
|
2979
|
+
return date ?? '';
|
|
2721
2980
|
if (type === 'time')
|
|
2722
|
-
return time
|
|
2981
|
+
return time ?? '';
|
|
2723
2982
|
return '';
|
|
2724
2983
|
}
|
|
2725
2984
|
#processValuesDisplay(value, column, field, cacheObjValues) {
|
|
@@ -2773,25 +3032,285 @@ const buildColumnWidthMap = (column) => {
|
|
|
2773
3032
|
return result;
|
|
2774
3033
|
};
|
|
2775
3034
|
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
3035
|
+
const isTableItem = (item) => !!item && typeof item === 'object' && 'data' in item && 'meta' in item;
|
|
3036
|
+
const resolveItemData = (item) => (isTableItem(item) ? item.data : item);
|
|
3037
|
+
const hasActiveColumnFilter = (rawColumnFilter = {}, columns = []) => columns.some(col => {
|
|
3038
|
+
const v = rawColumnFilter[col.field];
|
|
3039
|
+
if (Array.isArray(v))
|
|
3040
|
+
return v.length > 0;
|
|
3041
|
+
if (v && typeof v === 'object')
|
|
3042
|
+
return !!v.from || !!v.to;
|
|
3043
|
+
return v !== undefined && v !== null && v !== '';
|
|
3044
|
+
});
|
|
3045
|
+
const matchesColumnFilter = (data, columns = [], rawColumnFilter = {}) => {
|
|
3046
|
+
for (const column of columns) {
|
|
3047
|
+
const { field, type } = column;
|
|
3048
|
+
const filterValue = (rawColumnFilter[field] || '').toString().trim().toLowerCase();
|
|
3049
|
+
const rawColVal = Utilities.getNestedValue(data, field);
|
|
3050
|
+
const columnValue = (rawColVal || '').toString().trim().toLowerCase();
|
|
3051
|
+
if (filterValue) {
|
|
3052
|
+
if (!columnValue && type !== 'datetime' && type !== 'date' && type !== 'time') {
|
|
3053
|
+
return false;
|
|
3054
|
+
}
|
|
3055
|
+
if (type === 'string') {
|
|
3056
|
+
if (columnValue.indexOf(filterValue) === -1) {
|
|
3057
|
+
return false;
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
else if (type === 'values' || type === 'lazy-values') {
|
|
3061
|
+
const columnType = column;
|
|
3062
|
+
const isMultiple = columnType.option.selection === 'MULTIPLE';
|
|
3063
|
+
if (isMultiple && Array.isArray(rawColVal)) {
|
|
3064
|
+
const columnValues = rawColVal.map((i) => (Utilities.getNestedValue(i, columnType.option.valueField) ?? '').toString().trim().toLowerCase()) ??
|
|
3065
|
+
[];
|
|
3066
|
+
const filterValues = rawColumnFilter[field]?.map((v) => (v ?? '').toString().trim().toLowerCase());
|
|
3067
|
+
if (filterValues?.length && filterValues.every(fv => !columnValues.includes(fv))) {
|
|
3068
|
+
return false;
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
else {
|
|
3072
|
+
if (columnValue !== filterValue) {
|
|
3073
|
+
return false;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
else if (type === 'number') {
|
|
3078
|
+
const fValue = +filterValue.replace('>=', '').replace('<=', '').replace('>', '').replace('<', '');
|
|
3079
|
+
const cValue = +columnValue;
|
|
3080
|
+
if (fValue || fValue === 0) {
|
|
3081
|
+
if (!cValue && cValue !== 0) {
|
|
3082
|
+
return false;
|
|
3083
|
+
}
|
|
3084
|
+
if (filterValue.indexOf('>=') > -1 && cValue < fValue) {
|
|
3085
|
+
return false;
|
|
3086
|
+
}
|
|
3087
|
+
else if (filterValue.indexOf('<=') > -1 && cValue > fValue) {
|
|
3088
|
+
return false;
|
|
3089
|
+
}
|
|
3090
|
+
else if (filterValue.indexOf('<') > -1 && cValue >= fValue) {
|
|
3091
|
+
return false;
|
|
3092
|
+
}
|
|
3093
|
+
else if (filterValue.indexOf('>') > -1 && cValue <= fValue) {
|
|
3094
|
+
return false;
|
|
3095
|
+
}
|
|
3096
|
+
else if (cValue !== fValue) {
|
|
3097
|
+
return false;
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
else if (type === 'boolean') {
|
|
3102
|
+
if ((filterValue === '1' || filterValue === 'true') && columnValue !== '1' && columnValue !== 'true') {
|
|
3103
|
+
return false;
|
|
3104
|
+
}
|
|
3105
|
+
else if ((filterValue === '0' || filterValue === 'false') && columnValue !== '0' && columnValue !== 'false') {
|
|
3106
|
+
return false;
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
else if (type === 'datetime' || type === 'date' || type === 'time') {
|
|
3110
|
+
const from = rawColumnFilter[field]?.from ?? rawColumnFilter[field];
|
|
3111
|
+
const to = rawColumnFilter[field]?.to ?? rawColumnFilter[field];
|
|
3112
|
+
const fromDate = DateUtilities$1.begin(from);
|
|
3113
|
+
const toDate = DateUtilities$1.end(to);
|
|
3114
|
+
if (fromDate || toDate) {
|
|
3115
|
+
if (!columnValue) {
|
|
3116
|
+
return false;
|
|
3117
|
+
}
|
|
3118
|
+
const columnTime = new Date(columnValue).getTime();
|
|
3119
|
+
const fromDateTime = fromDate?.getTime() || null;
|
|
3120
|
+
const toDateTime = toDate?.getTime() || null;
|
|
3121
|
+
if (fromDateTime && fromDateTime > columnTime) {
|
|
3122
|
+
return false;
|
|
3123
|
+
}
|
|
3124
|
+
if (toDateTime && columnTime > toDateTime) {
|
|
3125
|
+
return false;
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
return true;
|
|
3132
|
+
};
|
|
3133
|
+
const filterLocalItems = (localItems, option, filterInfo) => {
|
|
3134
|
+
const { columns } = option;
|
|
3135
|
+
const { rawColumnFilter, orderBy, orderDirection, pageSize, pageNumber } = filterInfo;
|
|
3136
|
+
const matchesData = (data) => matchesColumnFilter(data, columns, rawColumnFilter);
|
|
3137
|
+
const treeOpt = option.tree;
|
|
3138
|
+
const treeSearch = option.type === 'local' && treeOpt?.loadType === 'static' && hasActiveColumnFilter(rawColumnFilter, columns);
|
|
3139
|
+
const treeSearchPredicate = treeSearch ? matchesData : undefined;
|
|
3140
|
+
const items = treeSearch
|
|
3141
|
+
? localItems.filter(item => subtreeMatches(resolveItemData(item), matchesData, treeOpt))
|
|
3142
|
+
: localItems.filter(item => matchesData(resolveItemData(item)));
|
|
3143
|
+
if (orderBy && orderDirection) {
|
|
3144
|
+
const column = columns.find(e => e.field === orderBy);
|
|
3145
|
+
if (column) {
|
|
3146
|
+
const { type, field } = column;
|
|
3147
|
+
items.sort((tableItemCurrent, tableItemNext) => {
|
|
3148
|
+
const dataVal = Utilities.getNestedValue(resolveItemData(tableItemCurrent), field);
|
|
3149
|
+
const nextVal = Utilities.getNestedValue(resolveItemData(tableItemNext), field);
|
|
3150
|
+
if (type === 'number') {
|
|
3151
|
+
return (dataVal || 0) - (nextVal || 0);
|
|
3152
|
+
}
|
|
3153
|
+
if (type === 'date' || type === 'datetime' || type === 'time') {
|
|
3154
|
+
const d1 = new Date(dataVal || '').getTime();
|
|
3155
|
+
const d2 = new Date(nextVal || '').getTime();
|
|
3156
|
+
return d1 - d2;
|
|
3157
|
+
}
|
|
3158
|
+
const s1 = (dataVal || '').toString();
|
|
3159
|
+
const s2 = (nextVal || '').toString();
|
|
3160
|
+
if (s1 > s2) {
|
|
3161
|
+
return 1;
|
|
3162
|
+
}
|
|
3163
|
+
if (s1 < s2) {
|
|
3164
|
+
return -1;
|
|
3165
|
+
}
|
|
3166
|
+
return 0;
|
|
3167
|
+
});
|
|
3168
|
+
if (orderDirection === 'DESC') {
|
|
3169
|
+
items.reverse();
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
return {
|
|
3174
|
+
items: items.filter((item, index) => index >= pageNumber * pageSize && index < (pageNumber + 1) * pageSize),
|
|
3175
|
+
total: items.length,
|
|
3176
|
+
treeSearchPredicate,
|
|
3177
|
+
};
|
|
3178
|
+
};
|
|
3179
|
+
|
|
3180
|
+
const isGroupHeader = (item) => !!item?.meta?.group?.items?.length;
|
|
3181
|
+
const isChildRow = (item) => (item?.meta.tree?.level ?? 0) > 0;
|
|
3182
|
+
const isRootReorderRow = (item) => !!item && !isGroupHeader(item) && !isChildRow(item);
|
|
3183
|
+
const moveItem = (items, fromIndex, toIndex) => {
|
|
3184
|
+
const next = [...items];
|
|
3185
|
+
const [moved] = next.splice(fromIndex, 1);
|
|
3186
|
+
if (!moved)
|
|
3187
|
+
return next;
|
|
3188
|
+
next.splice(toIndex, 0, moved);
|
|
3189
|
+
return next;
|
|
3190
|
+
};
|
|
3191
|
+
const isReorderDisabled = (item, option, index = -1) => {
|
|
3192
|
+
if (isChildRow(item))
|
|
3193
|
+
return true;
|
|
3194
|
+
if (!option?.disabled || isGroupHeader(item))
|
|
3195
|
+
return false;
|
|
3196
|
+
return option.disabled(item.data, index);
|
|
3197
|
+
};
|
|
3198
|
+
const sameReorderGroup = (a, b, allItems) => {
|
|
3199
|
+
const groupOf = (item) => {
|
|
3200
|
+
let lastGroupIdx = -1;
|
|
3201
|
+
for (let i = 0; i < allItems.length; i++) {
|
|
3202
|
+
if (isGroupHeader(allItems[i]))
|
|
3203
|
+
lastGroupIdx = i;
|
|
3204
|
+
if (allItems[i] === item)
|
|
3205
|
+
return lastGroupIdx;
|
|
3206
|
+
}
|
|
3207
|
+
return -1;
|
|
3208
|
+
};
|
|
3209
|
+
return groupOf(a) === groupOf(b);
|
|
3210
|
+
};
|
|
3211
|
+
const canSortReorder = ({ enabled, index, dragItem, allItems = [], hasGroup }) => {
|
|
3212
|
+
if (!enabled)
|
|
3213
|
+
return false;
|
|
3214
|
+
const targetItem = allItems[index];
|
|
3215
|
+
if (!isRootReorderRow(targetItem))
|
|
3216
|
+
return false;
|
|
3217
|
+
if (isChildRow(dragItem))
|
|
3218
|
+
return false;
|
|
3219
|
+
if (hasGroup) {
|
|
3220
|
+
return sameReorderGroup(dragItem, targetItem, allItems);
|
|
3221
|
+
}
|
|
3222
|
+
return true;
|
|
3223
|
+
};
|
|
3224
|
+
const toReorderItemsIndex = (renderedItems, renderedIndex) => {
|
|
3225
|
+
let count = 0;
|
|
3226
|
+
for (let i = 0; i < renderedIndex; i++) {
|
|
3227
|
+
if (isRootReorderRow(renderedItems[i]))
|
|
3228
|
+
count++;
|
|
3229
|
+
}
|
|
3230
|
+
return count;
|
|
3231
|
+
};
|
|
3232
|
+
const reorderTableItems = ({ items, renderedItems, previousRenderedIndex, currentRenderedIndex, localItems, }) => {
|
|
3233
|
+
const fromIndex = toReorderItemsIndex(renderedItems, previousRenderedIndex);
|
|
3234
|
+
const toIndex = toReorderItemsIndex(renderedItems, currentRenderedIndex);
|
|
3235
|
+
const nextItems = moveItem(items, fromIndex, toIndex);
|
|
3236
|
+
let nextLocalItems = localItems;
|
|
3237
|
+
if (localItems) {
|
|
3238
|
+
const localPositions = items.map(item => localItems.indexOf(item));
|
|
3239
|
+
if (localPositions.every(p => p >= 0)) {
|
|
3240
|
+
nextLocalItems = [...localItems];
|
|
3241
|
+
nextItems.forEach((item, i) => {
|
|
3242
|
+
nextLocalItems[localPositions[i]] = item;
|
|
3243
|
+
});
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
return {
|
|
3247
|
+
items: nextItems,
|
|
3248
|
+
localItems: nextLocalItems,
|
|
3249
|
+
fromIndex,
|
|
3250
|
+
toIndex,
|
|
3251
|
+
};
|
|
3252
|
+
};
|
|
3253
|
+
|
|
3254
|
+
const getSelectionRows = (roots, treeOpt) => treeOpt ? flattenTree(roots, treeOpt) : roots;
|
|
3255
|
+
const getSelectedRowData = (rows) => rows.filter(row => row.meta.selector.isSelected).map(row => row.data);
|
|
3256
|
+
const applyDefaultSelected = (roots, treeOpt, defaultSelected) => {
|
|
3257
|
+
if (!defaultSelected)
|
|
3258
|
+
return;
|
|
3259
|
+
const rows = treeOpt ? collectFormattedTreeRows(roots) : roots;
|
|
3260
|
+
rows.forEach(item => {
|
|
3261
|
+
item.meta.selector.isSelected = defaultSelected(item.data);
|
|
3262
|
+
});
|
|
3263
|
+
};
|
|
3264
|
+
const resolveSelectAllState = (visibleRows) => visibleRows.length > 0 && visibleRows.every(row => row.meta.selector?.isSelected);
|
|
3265
|
+
const restorePreservedSelection = (visibleRows, preservedSelectedMap) => {
|
|
3266
|
+
visibleRows.forEach(item => {
|
|
3267
|
+
if (preservedSelectedMap.has(item.meta.id)) {
|
|
3268
|
+
item.meta.selector.isSelected = true;
|
|
3269
|
+
preservedSelectedMap.set(item.meta.id, item);
|
|
3270
|
+
}
|
|
3271
|
+
});
|
|
3272
|
+
};
|
|
3273
|
+
const syncPreservedSelection = (visibleRows, preservedSelectedMap) => {
|
|
3274
|
+
visibleRows.forEach(item => {
|
|
3275
|
+
const id = item.meta.id;
|
|
3276
|
+
if (item.meta.selector.isSelected) {
|
|
3277
|
+
preservedSelectedMap.set(id, item);
|
|
3278
|
+
}
|
|
3279
|
+
else {
|
|
3280
|
+
preservedSelectedMap.delete(id);
|
|
3281
|
+
}
|
|
3282
|
+
});
|
|
3283
|
+
return Array.from(preservedSelectedMap.values());
|
|
3284
|
+
};
|
|
3285
|
+
|
|
3286
|
+
class SdTableFilterService {
|
|
3287
|
+
storageService;
|
|
3288
|
+
#filterConfiguration = 'GRID-FILTER-CONFIGURATION';
|
|
3289
|
+
#filterValue = 'GRID-FILTER-VALUE';
|
|
3290
|
+
#cache = {};
|
|
3291
|
+
constructor(storageService) {
|
|
2782
3292
|
this.storageService = storageService;
|
|
2783
3293
|
}
|
|
2784
|
-
|
|
2785
|
-
|
|
3294
|
+
#hasOwnValue = (values, field) => {
|
|
3295
|
+
return !!values && Object.prototype.hasOwnProperty.call(values, field);
|
|
3296
|
+
};
|
|
3297
|
+
#resolveKey = (filter, args) => {
|
|
2786
3298
|
const { id, columns, externalFilters } = args;
|
|
2787
3299
|
const tempKey = Utilities.hash({
|
|
2788
3300
|
id,
|
|
2789
3301
|
columns: columns?.map(e => e.field).filter(field => !!field) || [],
|
|
2790
3302
|
externalFilters: externalFilters?.map(e => e.field).filter(field => !!field) || [],
|
|
2791
3303
|
});
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
3304
|
+
return {
|
|
3305
|
+
tempKey,
|
|
3306
|
+
key: filter?.key || tempKey,
|
|
3307
|
+
cacheSession: !filter?.key,
|
|
3308
|
+
};
|
|
3309
|
+
};
|
|
3310
|
+
register = (filter, args) => {
|
|
3311
|
+
const { key, tempKey, cacheSession } = this.#resolveKey(filter, args);
|
|
3312
|
+
if (args.force) {
|
|
3313
|
+
delete this.#cache[key];
|
|
2795
3314
|
}
|
|
2796
3315
|
if (!this.#cache[key]) {
|
|
2797
3316
|
// Setting của filter configuration
|
|
@@ -2958,21 +3477,25 @@ class SdTableFilterService {
|
|
|
2958
3477
|
const { columns, externalFilters } = args;
|
|
2959
3478
|
// Filter column
|
|
2960
3479
|
for (const item of columns || []) {
|
|
2961
|
-
|
|
3480
|
+
// why: null is a deliberate cached clear from SD controls; only a missing key should fall back to default.
|
|
3481
|
+
columnFilter[item.field] = this.#hasOwnValue(value?.columnFilter, item.field)
|
|
3482
|
+
? value?.columnFilter?.[item.field]
|
|
3483
|
+
: item?.filter?.default;
|
|
2962
3484
|
if (item?.filter?.operator?.enable && item?.filter?.operator?.default) {
|
|
2963
3485
|
columnOperator[item.field] = item.filter.operator.default;
|
|
2964
3486
|
}
|
|
2965
3487
|
}
|
|
2966
3488
|
// Filter external
|
|
2967
3489
|
for (const item of externalFilters || []) {
|
|
3490
|
+
const hasExternalValue = this.#hasOwnValue(value?.externalFilter, item.field);
|
|
2968
3491
|
if (item.type === 'daterange') {
|
|
2969
3492
|
externalFilter[item.field] = {
|
|
2970
|
-
from: value?.externalFilter?.[item.field]?.from
|
|
2971
|
-
to: value?.externalFilter?.[item.field]?.to
|
|
3493
|
+
from: hasExternalValue ? value?.externalFilter?.[item.field]?.from : item.default?.from,
|
|
3494
|
+
to: hasExternalValue ? value?.externalFilter?.[item.field]?.to : item.default?.to,
|
|
2972
3495
|
};
|
|
2973
3496
|
}
|
|
2974
3497
|
else {
|
|
2975
|
-
externalFilter[item.field] = value?.externalFilter?.[item.field]
|
|
3498
|
+
externalFilter[item.field] = hasExternalValue ? value?.externalFilter?.[item.field] : item?.default;
|
|
2976
3499
|
}
|
|
2977
3500
|
}
|
|
2978
3501
|
return {
|
|
@@ -3089,6 +3612,10 @@ class SdTable {
|
|
|
3089
3612
|
columnFilter = {};
|
|
3090
3613
|
#localItems = [];
|
|
3091
3614
|
#subscription = new Subscription();
|
|
3615
|
+
#configurationSubscription;
|
|
3616
|
+
#filterRegisterSubscription;
|
|
3617
|
+
#optionInstance;
|
|
3618
|
+
#optionRevision = 0;
|
|
3092
3619
|
#reload = new Subject();
|
|
3093
3620
|
#loadCompleted = false;
|
|
3094
3621
|
cacheValues = {};
|
|
@@ -3098,7 +3625,7 @@ class SdTable {
|
|
|
3098
3625
|
treeRevision = signal(0);
|
|
3099
3626
|
// Search ở cấp con (static tree + type 'local'): predicate khớp 1 dòng theo
|
|
3100
3627
|
// column filter hiện hành. Set ở #filterLocal khi có filter active, đọc lại ở
|
|
3101
|
-
// #render/#
|
|
3628
|
+
// #render/#ensureChildItemsFormatted dùng predicate này để prune + auto-expand các
|
|
3102
3629
|
// nhánh có node con khớp. undefined = không search → cây render bình thường.
|
|
3103
3630
|
#treeSearchPredicate;
|
|
3104
3631
|
// Lần render trước có đang ở chế độ search hay không (để xử lý chuyển trạng thái).
|
|
@@ -3121,27 +3648,40 @@ class SdTable {
|
|
|
3121
3648
|
const option = this.option();
|
|
3122
3649
|
if (option) {
|
|
3123
3650
|
untracked(() => {
|
|
3651
|
+
if (this.#optionInstance !== option) {
|
|
3652
|
+
this.#optionInstance = option;
|
|
3653
|
+
this.#resetStateForNewOption();
|
|
3654
|
+
}
|
|
3655
|
+
const optionRevision = this.#optionRevision;
|
|
3124
3656
|
const initOpt = this.#initConfiguration({ ...option });
|
|
3125
3657
|
this.tableOption.set(initOpt);
|
|
3126
3658
|
this.#loadCompleted = false;
|
|
3127
3659
|
const storage = this.#configService.init(initOpt);
|
|
3128
|
-
this.#
|
|
3660
|
+
this.#configurationSubscription?.unsubscribe();
|
|
3661
|
+
this.#configurationSubscription = storage.observer.pipe(startWith(storage.subject.getValue())).subscribe(() => {
|
|
3662
|
+
if (optionRevision !== this.#optionRevision)
|
|
3663
|
+
return;
|
|
3129
3664
|
const configurationResult = this.#configService.loadConfigurationResult(initOpt, storage.get());
|
|
3130
3665
|
const displayColumns = configurationResult.displayedColumns || [];
|
|
3131
3666
|
this.#ref.detectChanges();
|
|
3132
3667
|
this.#tableFormatService
|
|
3133
3668
|
.loadValues(initOpt.columns.filter(column => displayColumns.includes(column.field)), this.cacheValues, this.#cacheObjValues)
|
|
3134
3669
|
.then(() => {
|
|
3670
|
+
if (optionRevision !== this.#optionRevision)
|
|
3671
|
+
return;
|
|
3135
3672
|
this.configuration.set(configurationResult);
|
|
3136
3673
|
this.#loadFilterRegister();
|
|
3137
3674
|
if (this.filterRegister) {
|
|
3138
|
-
this.#
|
|
3675
|
+
this.#requestReload(true);
|
|
3139
3676
|
}
|
|
3140
3677
|
})
|
|
3141
3678
|
.finally(() => {
|
|
3679
|
+
if (optionRevision !== this.#optionRevision)
|
|
3680
|
+
return;
|
|
3142
3681
|
this.#ref.detectChanges();
|
|
3143
3682
|
});
|
|
3144
|
-
})
|
|
3683
|
+
});
|
|
3684
|
+
this.#subscription.add(this.#configurationSubscription);
|
|
3145
3685
|
});
|
|
3146
3686
|
}
|
|
3147
3687
|
});
|
|
@@ -3149,7 +3689,7 @@ class SdTable {
|
|
|
3149
3689
|
const paginator = this.paginator();
|
|
3150
3690
|
if (paginator) {
|
|
3151
3691
|
untracked(() => {
|
|
3152
|
-
this.#subscription.add(paginator.page.subscribe(() => this.#
|
|
3692
|
+
this.#subscription.add(paginator.page.subscribe(() => this.#requestReload(false)));
|
|
3153
3693
|
});
|
|
3154
3694
|
}
|
|
3155
3695
|
});
|
|
@@ -3157,7 +3697,7 @@ class SdTable {
|
|
|
3157
3697
|
const sort = this.sort();
|
|
3158
3698
|
if (sort) {
|
|
3159
3699
|
untracked(() => {
|
|
3160
|
-
this.#subscription.add(sort.sortChange.subscribe(() => this.#
|
|
3700
|
+
this.#subscription.add(sort.sortChange.subscribe(() => this.#requestReload(false)));
|
|
3161
3701
|
});
|
|
3162
3702
|
}
|
|
3163
3703
|
});
|
|
@@ -3171,7 +3711,7 @@ class SdTable {
|
|
|
3171
3711
|
const conf = this.configuration();
|
|
3172
3712
|
if (!conf)
|
|
3173
3713
|
return;
|
|
3174
|
-
const firstColumns = conf.firstColumns.map(c => c.field === field ? { ...c, width } : c);
|
|
3714
|
+
const firstColumns = conf.firstColumns.map(c => (c.field === field ? { ...c, width } : c));
|
|
3175
3715
|
const column = { ...conf.column };
|
|
3176
3716
|
if (column[field]) {
|
|
3177
3717
|
column[field] = { ...column[field], width };
|
|
@@ -3183,16 +3723,26 @@ class SdTable {
|
|
|
3183
3723
|
this.configuration.set({ ...conf, firstColumns, column, fixedColumn });
|
|
3184
3724
|
}));
|
|
3185
3725
|
}
|
|
3186
|
-
ngOnInit() { }
|
|
3187
3726
|
ngAfterViewInit() {
|
|
3188
3727
|
this.#subscription.add(this.#reload
|
|
3189
3728
|
.pipe(debounceTime(200), switchMap(async (data) => {
|
|
3729
|
+
if (data.revision !== this.#optionRevision || !this.filterRegister)
|
|
3730
|
+
return undefined;
|
|
3190
3731
|
const filterInfo = this.getFilterRequest();
|
|
3191
3732
|
const result = await this.#load(filterInfo, !this.#loadCompleted || data.force);
|
|
3733
|
+
if (data.revision !== this.#optionRevision)
|
|
3734
|
+
return undefined;
|
|
3192
3735
|
this.#loadCompleted = true;
|
|
3193
|
-
return
|
|
3736
|
+
return {
|
|
3737
|
+
result,
|
|
3738
|
+
revision: data.revision,
|
|
3739
|
+
};
|
|
3194
3740
|
}))
|
|
3195
|
-
.subscribe(
|
|
3741
|
+
.subscribe(loadResult => {
|
|
3742
|
+
if (!loadResult || loadResult.revision !== this.#optionRevision)
|
|
3743
|
+
return;
|
|
3744
|
+
this.#render(loadResult.result);
|
|
3745
|
+
}));
|
|
3196
3746
|
}
|
|
3197
3747
|
ngOnDestroy() {
|
|
3198
3748
|
this.#subscription.unsubscribe();
|
|
@@ -3211,6 +3761,49 @@ class SdTable {
|
|
|
3211
3761
|
isExported: true,
|
|
3212
3762
|
};
|
|
3213
3763
|
};
|
|
3764
|
+
#requestReload = (force) => {
|
|
3765
|
+
this.#reload.next({ force, revision: this.#optionRevision });
|
|
3766
|
+
};
|
|
3767
|
+
#resetStateForNewOption = () => {
|
|
3768
|
+
this.#optionRevision += 1;
|
|
3769
|
+
this.#configurationSubscription?.unsubscribe();
|
|
3770
|
+
this.#configurationSubscription = undefined;
|
|
3771
|
+
this.#filterRegisterSubscription?.unsubscribe();
|
|
3772
|
+
this.#filterRegisterSubscription = undefined;
|
|
3773
|
+
this.filterRegister = undefined;
|
|
3774
|
+
this.#tableId = Utilities.generateUuid();
|
|
3775
|
+
this.key = Utilities.generateUuid();
|
|
3776
|
+
this.#loadCompleted = false;
|
|
3777
|
+
this.tableOption.set(undefined);
|
|
3778
|
+
this.configuration.set(undefined);
|
|
3779
|
+
this.items.set([]);
|
|
3780
|
+
this.selectedTableItems.set([]);
|
|
3781
|
+
this.total.set(undefined);
|
|
3782
|
+
this.loading.set(false);
|
|
3783
|
+
this.isSelectAll.set(false);
|
|
3784
|
+
this.isFiltered.set(false);
|
|
3785
|
+
this.requireFiltered.set(false);
|
|
3786
|
+
this.columnOperator = {};
|
|
3787
|
+
this.#syncColumnFilterInPlace({});
|
|
3788
|
+
this.#localItems = [];
|
|
3789
|
+
this.cacheValues = {};
|
|
3790
|
+
this.#cacheObjValues = {};
|
|
3791
|
+
this.#itemIndexMap = new WeakMap();
|
|
3792
|
+
this.#treeExpandState.clear();
|
|
3793
|
+
this.#treeSearchPredicate = undefined;
|
|
3794
|
+
this.#treeSearchActive = false;
|
|
3795
|
+
this.groupExpandState.clear();
|
|
3796
|
+
this.#preservedSelectedMap.clear();
|
|
3797
|
+
this.treeRevision.update(n => n + 1);
|
|
3798
|
+
if (this.paginator()) {
|
|
3799
|
+
this.paginator().pageIndex = 0;
|
|
3800
|
+
}
|
|
3801
|
+
const sort = this.sort();
|
|
3802
|
+
if (sort) {
|
|
3803
|
+
sort.active = '';
|
|
3804
|
+
sort.direction = '';
|
|
3805
|
+
}
|
|
3806
|
+
};
|
|
3214
3807
|
#initConfiguration = (option) => {
|
|
3215
3808
|
option.paginate = {
|
|
3216
3809
|
hidden: option?.paginate?.hidden,
|
|
@@ -3230,7 +3823,10 @@ class SdTable {
|
|
|
3230
3823
|
if (!column.filter?.operator?.list?.length) {
|
|
3231
3824
|
column.filter.operator.list = this.tableConfiguration?.filter?.operator?.list?.[column.type] || [];
|
|
3232
3825
|
}
|
|
3233
|
-
|
|
3826
|
+
const defaultOperator = this.tableConfiguration?.filter?.operator?.default?.[column.type];
|
|
3827
|
+
if (defaultOperator) {
|
|
3828
|
+
this.columnOperator[column.field] = defaultOperator;
|
|
3829
|
+
}
|
|
3234
3830
|
if (column.filter.operator.default && column.filter.operator.list?.some(el => el === column.filter?.operator?.default)) {
|
|
3235
3831
|
this.columnOperator[column.field] = column.filter.operator.default;
|
|
3236
3832
|
}
|
|
@@ -3240,33 +3836,39 @@ class SdTable {
|
|
|
3240
3836
|
};
|
|
3241
3837
|
#loadFilterRegister = () => {
|
|
3242
3838
|
const opt = this.tableOption();
|
|
3243
|
-
if (opt
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3839
|
+
if (!opt || this.filterRegister)
|
|
3840
|
+
return;
|
|
3841
|
+
this.filterRegister = this.#gridFilterService.register(opt?.filter, {
|
|
3842
|
+
id: this.#tableId,
|
|
3843
|
+
columns: opt?.columns,
|
|
3844
|
+
externalFilters: opt?.filter?.externalFilters,
|
|
3845
|
+
filterDefs: [...this.sdFilterDefs()],
|
|
3846
|
+
columnOperator: this.columnOperator,
|
|
3847
|
+
force: true,
|
|
3848
|
+
});
|
|
3849
|
+
const { columnOperator, columnFilter } = this.filterRegister.value.get();
|
|
3850
|
+
this.columnOperator = columnOperator || {};
|
|
3851
|
+
this.#syncColumnFilterInPlace(columnFilter || {});
|
|
3852
|
+
this.#filterRegisterSubscription?.unsubscribe();
|
|
3853
|
+
this.#filterRegisterSubscription = this.filterRegister.value.observer
|
|
3854
|
+
.pipe(debounceTime(500), map(filterValue => {
|
|
3855
|
+
const { columnOperator, columnFilter, notReload } = filterValue;
|
|
3856
|
+
this.columnOperator = columnOperator || {};
|
|
3857
|
+
// Sync IN PLACE — không gán object clone mới. column-filter chia sẻ
|
|
3858
|
+
// reference this.columnFilter qua [columnFilter] input; nếu gán clone
|
|
3859
|
+
// mới, cf giữ ref cũ (do OnPush + reload async lag) → ghi clear vào
|
|
3860
|
+
// object orphan → giá trị cũ persist. Giữ ref ổn định để cf + table
|
|
3861
|
+
// luôn trỏ cùng 1 object.
|
|
3862
|
+
this.#syncColumnFilterInPlace(columnFilter || {});
|
|
3863
|
+
if (!notReload) {
|
|
3864
|
+
if (this.paginator()) {
|
|
3865
|
+
this.paginator().pageIndex = 0;
|
|
3266
3866
|
}
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
}
|
|
3867
|
+
this.#requestReload(false);
|
|
3868
|
+
}
|
|
3869
|
+
}))
|
|
3870
|
+
.subscribe();
|
|
3871
|
+
this.#subscription.add(this.#filterRegisterSubscription);
|
|
3270
3872
|
};
|
|
3271
3873
|
// Đồng bộ this.columnFilter với `next` mà GIỮ NGUYÊN reference object.
|
|
3272
3874
|
// column-filter chia sẻ ref này qua [columnFilter] — reassign clone mới sẽ
|
|
@@ -3281,158 +3883,12 @@ class SdTable {
|
|
|
3281
3883
|
}
|
|
3282
3884
|
Object.assign(cur, next);
|
|
3283
3885
|
};
|
|
3284
|
-
// True nếu column filter hiện có ít nhất một giá trị đang lọc (để bật search-con).
|
|
3285
|
-
#hasActiveColumnFilter = (rawColumnFilter, columns) => columns.some(col => {
|
|
3286
|
-
const v = rawColumnFilter[col.field];
|
|
3287
|
-
if (Array.isArray(v))
|
|
3288
|
-
return v.length > 0;
|
|
3289
|
-
if (v && typeof v === 'object')
|
|
3290
|
-
return !!v.from || !!v.to;
|
|
3291
|
-
return v !== undefined && v !== null && v !== '';
|
|
3292
|
-
});
|
|
3293
|
-
// Predicate khớp MỘT dòng (raw data) theo column filter. Tách riêng để dùng lại
|
|
3294
|
-
// cho cả lọc root lẫn search-con (subtreeMatches/filterMatchingChildren).
|
|
3295
|
-
#matchesColumnFilter = (data, columns, rawColumnFilter) => {
|
|
3296
|
-
for (const column of columns) {
|
|
3297
|
-
const { field, type } = column;
|
|
3298
|
-
const filterValue = (rawColumnFilter[field] || '').toString().trim().toLowerCase();
|
|
3299
|
-
// SỬA: Dùng getNestedValue để hỗ trợ nested field trong filterLocal
|
|
3300
|
-
const rawColVal = Utilities.getNestedValue(data, field);
|
|
3301
|
-
const columnValue = (rawColVal || '').toString().trim().toLowerCase();
|
|
3302
|
-
if (filterValue) {
|
|
3303
|
-
if (!columnValue && type !== 'datetime' && type !== 'date' && type !== 'time') {
|
|
3304
|
-
return false;
|
|
3305
|
-
}
|
|
3306
|
-
if (type === 'string') {
|
|
3307
|
-
if (columnValue.indexOf(filterValue) === -1) {
|
|
3308
|
-
return false;
|
|
3309
|
-
}
|
|
3310
|
-
}
|
|
3311
|
-
else if (type === 'values' || type === 'lazy-values') {
|
|
3312
|
-
const columnType = column;
|
|
3313
|
-
const isMultiple = columnType.option.selection === 'MULTIPLE';
|
|
3314
|
-
if (isMultiple && Array.isArray(rawColVal)) {
|
|
3315
|
-
const columnValues = rawColVal.map((i) => (Utilities.getNestedValue(i, columnType.option.valueField) ?? '').toString().trim().toLowerCase()) ?? [];
|
|
3316
|
-
const filterValues = rawColumnFilter[field]?.map((v) => (v ?? '').toString().trim().toLowerCase());
|
|
3317
|
-
if (filterValues?.length && filterValues.every(fv => !columnValues.includes(fv))) {
|
|
3318
|
-
return false;
|
|
3319
|
-
}
|
|
3320
|
-
}
|
|
3321
|
-
else {
|
|
3322
|
-
if (columnValue !== filterValue) {
|
|
3323
|
-
return false;
|
|
3324
|
-
}
|
|
3325
|
-
}
|
|
3326
|
-
}
|
|
3327
|
-
else if (type === 'number') {
|
|
3328
|
-
const fValue = +filterValue.replace('>=', '').replace('<=', '').replace('>', '').replace('<', '');
|
|
3329
|
-
const cValue = +columnValue;
|
|
3330
|
-
if (fValue || fValue === 0) {
|
|
3331
|
-
if (!cValue && cValue !== 0) {
|
|
3332
|
-
return false;
|
|
3333
|
-
}
|
|
3334
|
-
if (filterValue.indexOf('>=') > -1 && cValue < fValue) {
|
|
3335
|
-
return false;
|
|
3336
|
-
}
|
|
3337
|
-
else if (filterValue.indexOf('<=') > -1 && cValue > fValue) {
|
|
3338
|
-
return false;
|
|
3339
|
-
}
|
|
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 (cValue !== fValue) {
|
|
3347
|
-
return false;
|
|
3348
|
-
}
|
|
3349
|
-
}
|
|
3350
|
-
}
|
|
3351
|
-
else if (type === 'boolean') {
|
|
3352
|
-
if ((filterValue === '1' || filterValue === 'true') && columnValue !== '1' && columnValue !== 'true') {
|
|
3353
|
-
return false;
|
|
3354
|
-
}
|
|
3355
|
-
else if ((filterValue === '0' || filterValue === 'false') && columnValue !== '0' && columnValue !== 'false') {
|
|
3356
|
-
return false;
|
|
3357
|
-
}
|
|
3358
|
-
}
|
|
3359
|
-
else if (type === 'datetime' || type === 'date' || type === 'time') {
|
|
3360
|
-
const from = rawColumnFilter[field]?.from ?? rawColumnFilter[field];
|
|
3361
|
-
const to = rawColumnFilter[field]?.to ?? rawColumnFilter[field];
|
|
3362
|
-
const fromDate = DateUtilities.begin(from);
|
|
3363
|
-
const toDate = DateUtilities.end(to);
|
|
3364
|
-
if (fromDate || toDate) {
|
|
3365
|
-
if (!columnValue) {
|
|
3366
|
-
return false;
|
|
3367
|
-
}
|
|
3368
|
-
const columnTime = new Date(columnValue).getTime();
|
|
3369
|
-
const fromDateTime = fromDate?.getTime() || null;
|
|
3370
|
-
const toDateTime = toDate?.getTime() || null;
|
|
3371
|
-
if (fromDateTime && fromDateTime > columnTime) {
|
|
3372
|
-
return false;
|
|
3373
|
-
}
|
|
3374
|
-
if (toDateTime && columnTime > toDateTime) {
|
|
3375
|
-
return false;
|
|
3376
|
-
}
|
|
3377
|
-
}
|
|
3378
|
-
}
|
|
3379
|
-
}
|
|
3380
|
-
}
|
|
3381
|
-
return true;
|
|
3382
|
-
};
|
|
3383
3886
|
#filterLocal = (localItems, filterInfo) => {
|
|
3384
|
-
const
|
|
3385
|
-
|
|
3386
|
-
const { rawColumnFilter, orderBy, orderDirection, pageSize, pageNumber } = filterInfo;
|
|
3387
|
-
const matchesData = (data) => this.#matchesColumnFilter(data, columns, rawColumnFilter);
|
|
3388
|
-
// Search ở cấp con: chỉ bật cho table local + static tree + đang có filter.
|
|
3389
|
-
// 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à
|
|
3390
|
-
// lưu predicate để #render prune + auto-expand nhánh khớp.
|
|
3391
|
-
const treeOpt = opt.tree;
|
|
3392
|
-
const treeSearch = opt.type === 'local' && treeOpt?.loadType === 'static' && this.#hasActiveColumnFilter(rawColumnFilter, columns);
|
|
3393
|
-
this.#treeSearchPredicate = treeSearch ? matchesData : undefined;
|
|
3394
|
-
const items = treeSearch
|
|
3395
|
-
? localItems.filter(tableItem => subtreeMatches(tableItem.data, matchesData, treeOpt))
|
|
3396
|
-
: localItems.filter(tableItem => matchesData(tableItem.data));
|
|
3397
|
-
// Sort
|
|
3398
|
-
if (orderBy && orderDirection) {
|
|
3399
|
-
const column = columns.find(e => e.field === orderBy);
|
|
3400
|
-
if (column) {
|
|
3401
|
-
const { type, field } = column;
|
|
3402
|
-
items.sort((tableItemCurrent, tableItemNext) => {
|
|
3403
|
-
const data = tableItemCurrent.data;
|
|
3404
|
-
const next = tableItemNext.data;
|
|
3405
|
-
// SỬA: Dùng getNestedValue cho sorting
|
|
3406
|
-
const dataVal = Utilities.getNestedValue(data, field);
|
|
3407
|
-
const nextVal = Utilities.getNestedValue(next, field);
|
|
3408
|
-
if (type === 'number') {
|
|
3409
|
-
return (dataVal || 0) - (nextVal || 0);
|
|
3410
|
-
}
|
|
3411
|
-
if (type === 'date' || type === 'datetime' || type === 'time') {
|
|
3412
|
-
const d1 = new Date(dataVal || '').getTime();
|
|
3413
|
-
const d2 = new Date(nextVal || '').getTime();
|
|
3414
|
-
return d1 - d2;
|
|
3415
|
-
}
|
|
3416
|
-
const s1 = (dataVal || '').toString();
|
|
3417
|
-
const s2 = (nextVal || '').toString();
|
|
3418
|
-
if (s1 > s2) {
|
|
3419
|
-
return 1;
|
|
3420
|
-
}
|
|
3421
|
-
if (s1 < s2) {
|
|
3422
|
-
return -1;
|
|
3423
|
-
}
|
|
3424
|
-
return 0;
|
|
3425
|
-
});
|
|
3426
|
-
if (orderDirection === 'DESC') {
|
|
3427
|
-
items.reverse();
|
|
3428
|
-
}
|
|
3429
|
-
}
|
|
3430
|
-
}
|
|
3887
|
+
const result = filterLocalItems(localItems, this.tableOption(), filterInfo);
|
|
3888
|
+
this.#treeSearchPredicate = result.treeSearchPredicate;
|
|
3431
3889
|
return {
|
|
3432
|
-
items: items
|
|
3433
|
-
|
|
3434
|
-
}),
|
|
3435
|
-
total: items.length,
|
|
3890
|
+
items: result.items,
|
|
3891
|
+
total: result.total,
|
|
3436
3892
|
};
|
|
3437
3893
|
};
|
|
3438
3894
|
getFilterRequest = () => {
|
|
@@ -3516,7 +3972,10 @@ class SdTable {
|
|
|
3516
3972
|
total: 0,
|
|
3517
3973
|
};
|
|
3518
3974
|
}
|
|
3519
|
-
const pagingReq =
|
|
3975
|
+
const pagingReq = SdConvertToPagingReq(filterReq, {
|
|
3976
|
+
columns: opt.columns,
|
|
3977
|
+
externalFilters: opt.filter?.externalFilters,
|
|
3978
|
+
});
|
|
3520
3979
|
const data = await items(filterReq, pagingReq).catch(err => {
|
|
3521
3980
|
console.error(err);
|
|
3522
3981
|
return {
|
|
@@ -3566,18 +4025,21 @@ class SdTable {
|
|
|
3566
4025
|
// why: KHÔNG persist trạng thái bung bị ÉP trong lúc search (tránh nhánh
|
|
3567
4026
|
// tự bung do search bị ghi nhầm thành lựa chọn của user sau khi clear).
|
|
3568
4027
|
if (!this.#treeSearchActive) {
|
|
3569
|
-
|
|
4028
|
+
saveTreeExpandState(this.items(), this.#treeExpandState);
|
|
3570
4029
|
}
|
|
3571
4030
|
// why: search vừa tắt → childItems đang là tập đã prune theo từ khoá cũ;
|
|
3572
4031
|
// xoá cache để dựng lại đầy đủ children ở chế độ thường.
|
|
3573
4032
|
if (this.#treeSearchActive && !searchNow) {
|
|
3574
|
-
|
|
4033
|
+
clearTreeChildCache(this.#localItems);
|
|
3575
4034
|
}
|
|
3576
4035
|
}
|
|
3577
4036
|
this.items.set(args?.items || []);
|
|
3578
4037
|
this.total.set(args?.total || 0);
|
|
3579
4038
|
if (treeOpt) {
|
|
3580
|
-
|
|
4039
|
+
initTreeMeta(this.items(), treeOpt, {
|
|
4040
|
+
expandState: this.#treeExpandState,
|
|
4041
|
+
treeSearchPredicate: this.#treeSearchPredicate,
|
|
4042
|
+
});
|
|
3581
4043
|
await this.#expandDefaultBranches(this.items(), treeOpt);
|
|
3582
4044
|
this.treeRevision.update(n => n + 1);
|
|
3583
4045
|
}
|
|
@@ -3624,7 +4086,10 @@ class SdTable {
|
|
|
3624
4086
|
else {
|
|
3625
4087
|
const filterReq = this.#filterExportInfo(pageNumber, pageSize);
|
|
3626
4088
|
if (opt.type === 'server') {
|
|
3627
|
-
const pagingReq =
|
|
4089
|
+
const pagingReq = SdConvertToPagingReq(filterReq, {
|
|
4090
|
+
columns: opt.columns,
|
|
4091
|
+
externalFilters: opt.filter?.externalFilters,
|
|
4092
|
+
});
|
|
3628
4093
|
const result = opt.items(filterReq, pagingReq);
|
|
3629
4094
|
return await result;
|
|
3630
4095
|
}
|
|
@@ -3669,51 +4134,6 @@ class SdTable {
|
|
|
3669
4134
|
exportCustom = () => {
|
|
3670
4135
|
this.#tableExportService.exportCustom(this.#createExportContext());
|
|
3671
4136
|
};
|
|
3672
|
-
#saveTreeExpandState = (rows) => {
|
|
3673
|
-
for (const row of rows) {
|
|
3674
|
-
if (row.meta.tree?.isExpanded) {
|
|
3675
|
-
this.#treeExpandState.set(row.meta.id, true);
|
|
3676
|
-
}
|
|
3677
|
-
for (const child of row.meta.tree?.childItems ?? []) {
|
|
3678
|
-
this.#saveTreeExpandState([child]);
|
|
3679
|
-
}
|
|
3680
|
-
}
|
|
3681
|
-
};
|
|
3682
|
-
// Children "nhìn thấy được" của một row: bình thường = toàn bộ embedded;
|
|
3683
|
-
// khi search-con = chỉ giữ child có subtree khớp từ khoá (prune).
|
|
3684
|
-
#visibleChildrenData = (data, treeOpt) => {
|
|
3685
|
-
const all = getChildrenFromData(data, treeOpt);
|
|
3686
|
-
const predicate = this.#treeSearchPredicate;
|
|
3687
|
-
return predicate ? filterMatchingChildren(all, predicate, treeOpt) : all;
|
|
3688
|
-
};
|
|
3689
|
-
// Xoá cache childItems trên toàn cây — dùng khi tắt search để dựng lại đầy đủ.
|
|
3690
|
-
#clearTreeChildCache = (rows) => {
|
|
3691
|
-
for (const row of rows) {
|
|
3692
|
-
const children = row.meta.tree?.childItems;
|
|
3693
|
-
if (children?.length) {
|
|
3694
|
-
this.#clearTreeChildCache(children);
|
|
3695
|
-
row.meta.tree.childItems = undefined;
|
|
3696
|
-
}
|
|
3697
|
-
}
|
|
3698
|
-
};
|
|
3699
|
-
#initTreeMeta = (rows, option, level = 0, parentId) => {
|
|
3700
|
-
const searchActive = !!this.#treeSearchPredicate;
|
|
3701
|
-
for (const row of rows) {
|
|
3702
|
-
const saved = this.#treeExpandState.get(row.meta.id);
|
|
3703
|
-
// Khi search: hasChildren tính theo tập con đã prune; auto-expand nhánh còn con.
|
|
3704
|
-
const hasChildren = searchActive
|
|
3705
|
-
? this.#visibleChildrenData(row.data, option).length > 0
|
|
3706
|
-
: resolveHasChildren(row, option);
|
|
3707
|
-
row.meta.tree = {
|
|
3708
|
-
...row.meta.tree,
|
|
3709
|
-
level,
|
|
3710
|
-
parentId,
|
|
3711
|
-
hasChildren,
|
|
3712
|
-
isExpanded: searchActive ? hasChildren : (saved ?? resolveDefaultExpanded(level, option)),
|
|
3713
|
-
isExpanding: false,
|
|
3714
|
-
};
|
|
3715
|
-
}
|
|
3716
|
-
};
|
|
3717
4137
|
#ensureChildItemsFormatted = async (row) => {
|
|
3718
4138
|
const opt = this.tableOption();
|
|
3719
4139
|
const treeOpt = opt.tree;
|
|
@@ -3722,7 +4142,7 @@ class SdTable {
|
|
|
3722
4142
|
// vì tập này phụ thuộc từ khoá (đổi mỗi lần gõ) nên không thể cache.
|
|
3723
4143
|
if (!searchActive && row.meta.tree?.childItems?.length)
|
|
3724
4144
|
return;
|
|
3725
|
-
const raw =
|
|
4145
|
+
const raw = getVisibleChildrenData(row.data, treeOpt, this.#treeSearchPredicate);
|
|
3726
4146
|
if (!raw.length) {
|
|
3727
4147
|
if (searchActive && row.meta.tree)
|
|
3728
4148
|
row.meta.tree.childItems = [];
|
|
@@ -3730,20 +4150,12 @@ class SdTable {
|
|
|
3730
4150
|
}
|
|
3731
4151
|
const formatted = await this.#tableFormatService.format(raw, opt.columns, this.cacheValues, this.#cacheObjValues);
|
|
3732
4152
|
row.meta.tree.childItems = formatted;
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
child.meta.tree = {
|
|
3740
|
-
level: childLevel,
|
|
3741
|
-
parentId: row.meta.id,
|
|
3742
|
-
hasChildren: childHasChildren,
|
|
3743
|
-
isExpanded: searchActive ? childHasChildren : (saved ?? resolveDefaultExpanded(childLevel, treeOpt)),
|
|
3744
|
-
isExpanding: false,
|
|
3745
|
-
};
|
|
3746
|
-
}
|
|
4153
|
+
initTreeMeta(formatted, treeOpt, {
|
|
4154
|
+
level: (row.meta.tree.level ?? 0) + 1,
|
|
4155
|
+
parentId: row.meta.id,
|
|
4156
|
+
expandState: this.#treeExpandState,
|
|
4157
|
+
treeSearchPredicate: this.#treeSearchPredicate,
|
|
4158
|
+
});
|
|
3747
4159
|
};
|
|
3748
4160
|
#expandDefaultBranches = async (rows, option) => {
|
|
3749
4161
|
const maxDepth = option.maxDepth;
|
|
@@ -3902,28 +4314,14 @@ class SdTable {
|
|
|
3902
4314
|
this.#updateSelectedItems();
|
|
3903
4315
|
};
|
|
3904
4316
|
#getSelectionRows = () => {
|
|
3905
|
-
|
|
3906
|
-
const treeOpt = this.tableOption()?.tree;
|
|
3907
|
-
if (!treeOpt)
|
|
3908
|
-
return roots;
|
|
3909
|
-
return flattenTree(roots, treeOpt);
|
|
4317
|
+
return getSelectionRows(this.items(), this.tableOption()?.tree);
|
|
3910
4318
|
};
|
|
3911
|
-
#getSelectedRowData = () => this.#getSelectionRows()
|
|
3912
|
-
.filter(e => e.meta.selector.isSelected)
|
|
3913
|
-
.map(e => e.data);
|
|
4319
|
+
#getSelectedRowData = () => getSelectedRowData(this.#getSelectionRows());
|
|
3914
4320
|
#applyDefaultSelected = () => {
|
|
3915
|
-
|
|
3916
|
-
if (!defaultSelected)
|
|
3917
|
-
return;
|
|
3918
|
-
const treeOpt = this.tableOption()?.tree;
|
|
3919
|
-
const rows = treeOpt ? collectFormattedTreeRows(this.items()) : this.items();
|
|
3920
|
-
rows.forEach(item => {
|
|
3921
|
-
item.meta.selector.isSelected = defaultSelected(item.data);
|
|
3922
|
-
});
|
|
4321
|
+
applyDefaultSelected(this.items(), this.tableOption()?.tree, this.tableOption()?.selector?.defaultSelected);
|
|
3923
4322
|
};
|
|
3924
4323
|
#syncSelectAllState = () => {
|
|
3925
|
-
|
|
3926
|
-
this.isSelectAll.set(visible.length > 0 && visible.every(e => e.meta.selector?.isSelected));
|
|
4324
|
+
this.isSelectAll.set(resolveSelectAllState(this.#getSelectionRows()));
|
|
3927
4325
|
};
|
|
3928
4326
|
// ==========================================
|
|
3929
4327
|
// GROUP HELPERS — sync selection + expand state cho group header rows
|
|
@@ -3966,9 +4364,7 @@ class SdTable {
|
|
|
3966
4364
|
return;
|
|
3967
4365
|
const key = header.meta.group.key;
|
|
3968
4366
|
const defaultExpanded = !this.tableOption()?.group?.defaultCollapsed;
|
|
3969
|
-
const current = this.groupExpandState.has(key)
|
|
3970
|
-
? !!this.groupExpandState.get(key)
|
|
3971
|
-
: defaultExpanded;
|
|
4367
|
+
const current = this.groupExpandState.has(key) ? !!this.groupExpandState.get(key) : defaultExpanded;
|
|
3972
4368
|
this.groupExpandState.set(key, !current);
|
|
3973
4369
|
header.meta.group.isExpanded = !current;
|
|
3974
4370
|
// why: trigger pipe re-eval — Map mutation không thay đổi reference items() nên cần update.
|
|
@@ -3985,7 +4381,7 @@ class SdTable {
|
|
|
3985
4381
|
* Colspan cho cell sdGroupHeader trên group row = displayedColumns.length.
|
|
3986
4382
|
* Span TOÀN BỘ width data row vì group row chỉ có 1 cell.
|
|
3987
4383
|
*/
|
|
3988
|
-
sdGroupColspan = computed(() =>
|
|
4384
|
+
sdGroupColspan = computed(() => this.configuration()?.displayedColumns?.length || 1);
|
|
3989
4385
|
/** Build context object truyền vào SdTableGroupDefDirective template. */
|
|
3990
4386
|
groupContext = (header) => {
|
|
3991
4387
|
const g = header.meta.group;
|
|
@@ -4007,9 +4403,7 @@ class SdTable {
|
|
|
4007
4403
|
if (!fn)
|
|
4008
4404
|
return null;
|
|
4009
4405
|
const tree = row.meta?.tree;
|
|
4010
|
-
const ctx = this.tableOption()?.tree && tree
|
|
4011
|
-
? { level: tree.level, hasChildren: tree.hasChildren, isExpanded: tree.isExpanded }
|
|
4012
|
-
: undefined;
|
|
4406
|
+
const ctx = this.tableOption()?.tree && tree ? { level: tree.level, hasChildren: tree.hasChildren, isExpanded: tree.isExpanded } : undefined;
|
|
4013
4407
|
return fn(row.data, index, ctx);
|
|
4014
4408
|
};
|
|
4015
4409
|
// why: khi preserveSelection bật, giữ map nội bộ id → SdTableItem để selection
|
|
@@ -4023,29 +4417,14 @@ class SdTable {
|
|
|
4023
4417
|
#restorePreservedSelection = () => {
|
|
4024
4418
|
if (!this.#preserveEnabled())
|
|
4025
4419
|
return;
|
|
4026
|
-
|
|
4027
|
-
rows.forEach(item => {
|
|
4028
|
-
if (this.#preservedSelectedMap.has(item.meta.id)) {
|
|
4029
|
-
item.meta.selector.isSelected = true;
|
|
4030
|
-
this.#preservedSelectedMap.set(item.meta.id, item);
|
|
4031
|
-
}
|
|
4032
|
-
});
|
|
4420
|
+
restorePreservedSelection(this.#getSelectionRows(), this.#preservedSelectedMap);
|
|
4033
4421
|
};
|
|
4034
4422
|
#updateSelectedItems = () => {
|
|
4035
4423
|
const rows = this.#getSelectionRows();
|
|
4036
4424
|
if (this.#preserveEnabled()) {
|
|
4037
4425
|
// Sync map theo state visible: add nếu selected, remove nếu deselected.
|
|
4038
4426
|
// Off-page items giữ nguyên trong map (không bị visit ở đây).
|
|
4039
|
-
|
|
4040
|
-
const id = item.meta.id;
|
|
4041
|
-
if (item.meta.selector.isSelected) {
|
|
4042
|
-
this.#preservedSelectedMap.set(id, item);
|
|
4043
|
-
}
|
|
4044
|
-
else {
|
|
4045
|
-
this.#preservedSelectedMap.delete(id);
|
|
4046
|
-
}
|
|
4047
|
-
});
|
|
4048
|
-
this.selectedTableItems.set(Array.from(this.#preservedSelectedMap.values()));
|
|
4427
|
+
this.selectedTableItems.set(syncPreservedSelection(rows, this.#preservedSelectedMap));
|
|
4049
4428
|
}
|
|
4050
4429
|
else {
|
|
4051
4430
|
this.selectedTableItems.set(rows.filter(item => item.meta.selector.isSelected));
|
|
@@ -4097,295 +4476,38 @@ class SdTable {
|
|
|
4097
4476
|
return (p?.pageIndex ?? 0) * (p?.pageSize ?? 0);
|
|
4098
4477
|
});
|
|
4099
4478
|
isReorderDisabled(item) {
|
|
4100
|
-
if ((item.meta.tree?.level ?? 0) > 0)
|
|
4101
|
-
return true;
|
|
4102
4479
|
const opt = this.tableOption()?.rowReorder;
|
|
4103
|
-
if (!opt?.disabled || item.meta?.group?.items?.length)
|
|
4104
|
-
return false;
|
|
4105
4480
|
const idx = this.#itemIndexMap.get(item) ?? -1;
|
|
4106
|
-
return
|
|
4107
|
-
}
|
|
4108
|
-
#sameGroup(a, b, allItems) {
|
|
4109
|
-
const groupOf = (item) => {
|
|
4110
|
-
let lastGroupIdx = -1;
|
|
4111
|
-
for (let i = 0; i < allItems.length; i++) {
|
|
4112
|
-
if (allItems[i].meta?.group?.items?.length)
|
|
4113
|
-
lastGroupIdx = i;
|
|
4114
|
-
if (allItems[i] === item)
|
|
4115
|
-
return lastGroupIdx;
|
|
4116
|
-
}
|
|
4117
|
-
return -1;
|
|
4118
|
-
};
|
|
4119
|
-
return groupOf(a) === groupOf(b);
|
|
4481
|
+
return isReorderDisabled(item, opt, idx);
|
|
4120
4482
|
}
|
|
4121
4483
|
reorderSortPredicate = (index, drag, drop) => {
|
|
4122
4484
|
const opt = this.tableOption()?.rowReorder;
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
return false;
|
|
4131
|
-
if ((drag.data.meta.tree?.level ?? 0) > 0)
|
|
4132
|
-
return false;
|
|
4133
|
-
if (targetItem.meta?.group?.items?.length)
|
|
4134
|
-
return false;
|
|
4135
|
-
if (this.tableOption()?.group) {
|
|
4136
|
-
return this.#sameGroup(drag.data, targetItem, allItems);
|
|
4137
|
-
}
|
|
4138
|
-
return true;
|
|
4485
|
+
return canSortReorder({
|
|
4486
|
+
enabled: opt?.enabled,
|
|
4487
|
+
index,
|
|
4488
|
+
dragItem: drag.data,
|
|
4489
|
+
allItems: drop.data,
|
|
4490
|
+
hasGroup: !!this.tableOption()?.group,
|
|
4491
|
+
});
|
|
4139
4492
|
};
|
|
4140
4493
|
onReorderDrop(event) {
|
|
4141
4494
|
const { previousIndex, currentIndex } = event;
|
|
4142
4495
|
if (previousIndex === currentIndex)
|
|
4143
4496
|
return;
|
|
4144
|
-
const
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
const toIdx = toItemsIndex(currentIndex);
|
|
4155
|
-
const current = [...this.items()];
|
|
4156
|
-
const localPositions = current.map(item => this.#localItems.indexOf(item));
|
|
4157
|
-
moveItemInArray(current, fromIdx, toIdx);
|
|
4158
|
-
this.items.set(current);
|
|
4159
|
-
if (this.tableOption()?.type === 'local' && localPositions.every(p => p >= 0)) {
|
|
4160
|
-
const newLocal = [...this.#localItems];
|
|
4161
|
-
current.forEach((item, i) => {
|
|
4162
|
-
newLocal[localPositions[i]] = item;
|
|
4163
|
-
});
|
|
4164
|
-
this.#localItems = newLocal;
|
|
4497
|
+
const result = reorderTableItems({
|
|
4498
|
+
items: this.items(),
|
|
4499
|
+
renderedItems: event.container.data,
|
|
4500
|
+
previousRenderedIndex: previousIndex,
|
|
4501
|
+
currentRenderedIndex: currentIndex,
|
|
4502
|
+
localItems: this.tableOption()?.type === 'local' ? this.#localItems : undefined,
|
|
4503
|
+
});
|
|
4504
|
+
this.items.set(result.items);
|
|
4505
|
+
if (this.tableOption()?.type === 'local' && result.localItems) {
|
|
4506
|
+
this.#localItems = result.localItems;
|
|
4165
4507
|
}
|
|
4166
4508
|
this.table()?.renderRows();
|
|
4167
|
-
this.tableOption()?.rowReorder?.onChange?.(
|
|
4509
|
+
this.tableOption()?.rowReorder?.onChange?.(result.items.map(i => i.data), event.item.data.data, result.fromIndex, result.toIndex);
|
|
4168
4510
|
}
|
|
4169
|
-
#convertPagingReq = (filterReq) => {
|
|
4170
|
-
const opt = this.tableOption();
|
|
4171
|
-
const { columns, filter } = opt;
|
|
4172
|
-
const externalFilters = filter?.externalFilters || [];
|
|
4173
|
-
const req = {
|
|
4174
|
-
filters: [],
|
|
4175
|
-
orders: [],
|
|
4176
|
-
pageNumber: filterReq.pageNumber,
|
|
4177
|
-
pageSize: filterReq.pageSize,
|
|
4178
|
-
};
|
|
4179
|
-
const { filters, orders } = req;
|
|
4180
|
-
const { rawColumnFilter, columnOperator, rawExternalFilter, orderBy, orderDirection } = filterReq;
|
|
4181
|
-
for (const externalFilter of externalFilters || []) {
|
|
4182
|
-
const { field } = externalFilter;
|
|
4183
|
-
const value = rawExternalFilter?.[field];
|
|
4184
|
-
if (value !== undefined && value !== null && value !== '') {
|
|
4185
|
-
if (externalFilter.type === 'string') {
|
|
4186
|
-
if (externalFilter.defaultOperator === 'EQUAL' && value?.includes(',')) {
|
|
4187
|
-
filters.push({
|
|
4188
|
-
field,
|
|
4189
|
-
operator: 'IN',
|
|
4190
|
-
data: value.split(',').map(val => val.trim()),
|
|
4191
|
-
});
|
|
4192
|
-
}
|
|
4193
|
-
else {
|
|
4194
|
-
filters.push({
|
|
4195
|
-
field,
|
|
4196
|
-
operator: externalFilter.defaultOperator || 'CONTAIN',
|
|
4197
|
-
data: value,
|
|
4198
|
-
});
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
|
-
else if (externalFilter.type === 'boolean') {
|
|
4202
|
-
filters.push({
|
|
4203
|
-
field,
|
|
4204
|
-
operator: 'EQUAL',
|
|
4205
|
-
data: value === true || value === 1 || value === 'true' || value === '1',
|
|
4206
|
-
});
|
|
4207
|
-
}
|
|
4208
|
-
else if (externalFilter.type === 'daterange') {
|
|
4209
|
-
if (typeof value === 'object' && 'from' in value && 'to' in value) {
|
|
4210
|
-
if (value?.from) {
|
|
4211
|
-
filters.push({
|
|
4212
|
-
field,
|
|
4213
|
-
operator: 'GREATER_OR_EQUAL',
|
|
4214
|
-
data: DateUtilities.begin(value?.from).toISOString(),
|
|
4215
|
-
});
|
|
4216
|
-
}
|
|
4217
|
-
if (value?.to) {
|
|
4218
|
-
filters.push({
|
|
4219
|
-
field,
|
|
4220
|
-
operator: 'LESS_THAN',
|
|
4221
|
-
data: DateUtilities.begin(DateUtilities.addDays(value?.to, 1)).toISOString(),
|
|
4222
|
-
});
|
|
4223
|
-
}
|
|
4224
|
-
}
|
|
4225
|
-
}
|
|
4226
|
-
else if (externalFilter.type === 'date' || externalFilter.type === 'datetime') {
|
|
4227
|
-
if (DateUtilities.isDate(value)) {
|
|
4228
|
-
if (externalFilter.type === 'date') {
|
|
4229
|
-
if (externalFilter.defaultOperator === 'GREATER_OR_EQUAL') {
|
|
4230
|
-
filters.push({
|
|
4231
|
-
field,
|
|
4232
|
-
operator: 'GREATER_OR_EQUAL',
|
|
4233
|
-
data: DateUtilities.begin(value).toISOString(),
|
|
4234
|
-
});
|
|
4235
|
-
}
|
|
4236
|
-
if (externalFilter.defaultOperator === 'LESS_OR_EQUAL') {
|
|
4237
|
-
filters.push({
|
|
4238
|
-
field,
|
|
4239
|
-
operator: 'LESS_THAN',
|
|
4240
|
-
data: DateUtilities.begin(DateUtilities.addDays(value, 1)).toISOString(),
|
|
4241
|
-
});
|
|
4242
|
-
}
|
|
4243
|
-
}
|
|
4244
|
-
else {
|
|
4245
|
-
if (externalFilter.defaultOperator === 'GREATER_OR_EQUAL') {
|
|
4246
|
-
filters.push({
|
|
4247
|
-
field,
|
|
4248
|
-
operator: 'GREATER_OR_EQUAL',
|
|
4249
|
-
data: new Date(value).toISOString(),
|
|
4250
|
-
});
|
|
4251
|
-
}
|
|
4252
|
-
if (externalFilter.defaultOperator === 'LESS_OR_EQUAL') {
|
|
4253
|
-
filters.push({
|
|
4254
|
-
field,
|
|
4255
|
-
operator: 'LESS_OR_EQUAL',
|
|
4256
|
-
data: new Date(value).toISOString(),
|
|
4257
|
-
});
|
|
4258
|
-
}
|
|
4259
|
-
}
|
|
4260
|
-
}
|
|
4261
|
-
}
|
|
4262
|
-
else {
|
|
4263
|
-
if (Array.isArray(value)) {
|
|
4264
|
-
if (value.length) {
|
|
4265
|
-
filters.push({
|
|
4266
|
-
field,
|
|
4267
|
-
operator: 'IN',
|
|
4268
|
-
data: value,
|
|
4269
|
-
});
|
|
4270
|
-
}
|
|
4271
|
-
}
|
|
4272
|
-
else if (typeof value === 'object' && 'from' in value && 'to' in value) {
|
|
4273
|
-
if (value?.from) {
|
|
4274
|
-
filters.push({
|
|
4275
|
-
field,
|
|
4276
|
-
operator: 'GREATER_OR_EQUAL',
|
|
4277
|
-
data: DateUtilities.begin(value?.from).toISOString(),
|
|
4278
|
-
});
|
|
4279
|
-
}
|
|
4280
|
-
if (value?.to) {
|
|
4281
|
-
filters.push({
|
|
4282
|
-
field,
|
|
4283
|
-
operator: 'LESS_THAN',
|
|
4284
|
-
data: DateUtilities.begin(DateUtilities.addDays(value?.to, 1)).toISOString(),
|
|
4285
|
-
});
|
|
4286
|
-
}
|
|
4287
|
-
}
|
|
4288
|
-
else {
|
|
4289
|
-
filters.push({
|
|
4290
|
-
field,
|
|
4291
|
-
operator: externalFilter.defaultOperator || 'EQUAL',
|
|
4292
|
-
data: value,
|
|
4293
|
-
});
|
|
4294
|
-
}
|
|
4295
|
-
}
|
|
4296
|
-
}
|
|
4297
|
-
}
|
|
4298
|
-
for (const column of columns || []) {
|
|
4299
|
-
const { field } = column;
|
|
4300
|
-
const value = rawColumnFilter?.[field];
|
|
4301
|
-
const operator = columnOperator?.[field] || column.filter?.operator?.default;
|
|
4302
|
-
if (value !== undefined && value !== null && value !== '') {
|
|
4303
|
-
if (column.type === 'string') {
|
|
4304
|
-
// `operator` is the wide `Operator` union; in this data-bearing path it is a
|
|
4305
|
-
// single-value comparison operator, hence the cast to the `Filter` contract.
|
|
4306
|
-
filters.push({
|
|
4307
|
-
field: field,
|
|
4308
|
-
operator: operator || 'CONTAIN',
|
|
4309
|
-
data: value,
|
|
4310
|
-
});
|
|
4311
|
-
}
|
|
4312
|
-
else if (column.type === 'boolean') {
|
|
4313
|
-
filters.push({
|
|
4314
|
-
field: field,
|
|
4315
|
-
operator: 'EQUAL',
|
|
4316
|
-
data: value === true || value === 1 || value === 'true' || value === '1',
|
|
4317
|
-
});
|
|
4318
|
-
}
|
|
4319
|
-
else if (column.type === 'date' || column.type === 'datetime') {
|
|
4320
|
-
if (value && typeof value === 'object' && 'from' in value && 'to' in value) {
|
|
4321
|
-
if (value?.from && value?.to) {
|
|
4322
|
-
filters.push({
|
|
4323
|
-
field: field,
|
|
4324
|
-
operator: 'BETWEEN',
|
|
4325
|
-
data: {
|
|
4326
|
-
from: DateUtilities.begin(value?.from).toISOString(),
|
|
4327
|
-
to: DateUtilities.end(value?.to).toISOString(),
|
|
4328
|
-
},
|
|
4329
|
-
});
|
|
4330
|
-
}
|
|
4331
|
-
else if (value?.from) {
|
|
4332
|
-
filters.push({
|
|
4333
|
-
field: field,
|
|
4334
|
-
operator: 'GREATER_OR_EQUAL',
|
|
4335
|
-
data: DateUtilities.begin(value?.from).toISOString(),
|
|
4336
|
-
});
|
|
4337
|
-
}
|
|
4338
|
-
else if (value?.to) {
|
|
4339
|
-
filters.push({
|
|
4340
|
-
field: field,
|
|
4341
|
-
operator: 'LESS_THAN',
|
|
4342
|
-
data: DateUtilities.begin(DateUtilities.addDays(value?.to, 1)).toISOString(),
|
|
4343
|
-
});
|
|
4344
|
-
}
|
|
4345
|
-
}
|
|
4346
|
-
else {
|
|
4347
|
-
if (DateUtilities.isDate(value)) {
|
|
4348
|
-
filters.push({
|
|
4349
|
-
field: field,
|
|
4350
|
-
operator: 'BETWEEN',
|
|
4351
|
-
data: {
|
|
4352
|
-
from: DateUtilities.begin(value).toISOString(),
|
|
4353
|
-
to: DateUtilities.end(value).toISOString(),
|
|
4354
|
-
},
|
|
4355
|
-
});
|
|
4356
|
-
}
|
|
4357
|
-
}
|
|
4358
|
-
}
|
|
4359
|
-
else {
|
|
4360
|
-
if (Array.isArray(value)) {
|
|
4361
|
-
if (value.length) {
|
|
4362
|
-
filters.push({
|
|
4363
|
-
field: field,
|
|
4364
|
-
operator: 'IN',
|
|
4365
|
-
data: value,
|
|
4366
|
-
});
|
|
4367
|
-
}
|
|
4368
|
-
}
|
|
4369
|
-
else {
|
|
4370
|
-
// `operator` is the wide `Operator` union; this data-bearing path uses a
|
|
4371
|
-
// single-value comparison operator, hence the cast to the `Filter` contract.
|
|
4372
|
-
filters.push({
|
|
4373
|
-
field: field,
|
|
4374
|
-
operator: operator || 'EQUAL',
|
|
4375
|
-
data: value,
|
|
4376
|
-
});
|
|
4377
|
-
}
|
|
4378
|
-
}
|
|
4379
|
-
}
|
|
4380
|
-
}
|
|
4381
|
-
if (orderBy && orderDirection) {
|
|
4382
|
-
orders.push({
|
|
4383
|
-
field: orderBy,
|
|
4384
|
-
direction: orderDirection,
|
|
4385
|
-
});
|
|
4386
|
-
}
|
|
4387
|
-
return req;
|
|
4388
|
-
};
|
|
4389
4511
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4390
4512
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdTable, isStandalone: true, selector: "sd-table", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, option: { classPropertyName: "option", publicName: "option", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-autoid": "autoId()", "attr.data-loading": "loading() ? \"true\" : \"false\"" } }, providers: [
|
|
4391
4513
|
DatePipe,
|
|
@@ -4467,183 +4589,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
4467
4589
|
], 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"] }]
|
|
4468
4590
|
}], ctorParameters: () => [] });
|
|
4469
4591
|
|
|
4470
|
-
const SdConvertToPagingReq = (filterRequest, args) => {
|
|
4471
|
-
const { externalFilters, columns, fieldMapping } = args;
|
|
4472
|
-
const req = {
|
|
4473
|
-
filters: [],
|
|
4474
|
-
orders: args?.orders || [],
|
|
4475
|
-
pageNumber: filterRequest.pageNumber,
|
|
4476
|
-
pageSize: filterRequest.pageSize,
|
|
4477
|
-
};
|
|
4478
|
-
const { filters } = req;
|
|
4479
|
-
const { rawExternalFilter, rawColumnFilter, columnOperator, orderBy, orderDirection } = filterRequest;
|
|
4480
|
-
// Xử lý external filter
|
|
4481
|
-
for (const externalFilter of externalFilters || []) {
|
|
4482
|
-
const value = rawExternalFilter?.[externalFilter.field];
|
|
4483
|
-
const field = fieldMapping?.[externalFilter.field] || externalFilter.field;
|
|
4484
|
-
// Nếu có giá trị thì mới xử lý filter
|
|
4485
|
-
if (value !== undefined && value !== null && value !== '') {
|
|
4486
|
-
if (externalFilter.type === 'string') {
|
|
4487
|
-
filters.push({
|
|
4488
|
-
field,
|
|
4489
|
-
operator: externalFilter.defaultOperator || 'CONTAIN',
|
|
4490
|
-
data: value,
|
|
4491
|
-
});
|
|
4492
|
-
}
|
|
4493
|
-
else if (externalFilter.type === 'boolean') {
|
|
4494
|
-
filters.push({
|
|
4495
|
-
field,
|
|
4496
|
-
operator: 'EQUAL',
|
|
4497
|
-
data: value === true || value === 1 || value === 'true' || value === '1',
|
|
4498
|
-
});
|
|
4499
|
-
}
|
|
4500
|
-
else if (externalFilter.type === 'date') {
|
|
4501
|
-
if (typeof value === 'object' && 'from' in value && 'to' in value) {
|
|
4502
|
-
if (value?.from) {
|
|
4503
|
-
filters.push({
|
|
4504
|
-
field,
|
|
4505
|
-
operator: 'GREATER_OR_EQUAL',
|
|
4506
|
-
data: DateUtilities$1.begin(value?.from).toISOString(),
|
|
4507
|
-
});
|
|
4508
|
-
}
|
|
4509
|
-
if (value?.to) {
|
|
4510
|
-
filters.push({
|
|
4511
|
-
field,
|
|
4512
|
-
operator: 'LESS_THAN',
|
|
4513
|
-
data: DateUtilities$1.begin(DateUtilities$1.addDays(value?.to, 1)).toISOString(),
|
|
4514
|
-
});
|
|
4515
|
-
}
|
|
4516
|
-
}
|
|
4517
|
-
else {
|
|
4518
|
-
if (DateUtilities$1.isDate(value)) {
|
|
4519
|
-
if (externalFilter.defaultOperator === 'GREATER_OR_EQUAL') {
|
|
4520
|
-
filters.push({
|
|
4521
|
-
field,
|
|
4522
|
-
operator: 'GREATER_OR_EQUAL',
|
|
4523
|
-
data: DateUtilities$1.begin(value).toISOString(),
|
|
4524
|
-
});
|
|
4525
|
-
}
|
|
4526
|
-
if (externalFilter.defaultOperator === 'LESS_OR_EQUAL') {
|
|
4527
|
-
filters.push({
|
|
4528
|
-
field,
|
|
4529
|
-
operator: 'LESS_THAN',
|
|
4530
|
-
data: DateUtilities$1.begin(DateUtilities$1.addDays(value, 1)).toISOString(),
|
|
4531
|
-
});
|
|
4532
|
-
}
|
|
4533
|
-
}
|
|
4534
|
-
}
|
|
4535
|
-
}
|
|
4536
|
-
else {
|
|
4537
|
-
if (Array.isArray(value)) {
|
|
4538
|
-
if (value.length) {
|
|
4539
|
-
filters.push({
|
|
4540
|
-
field,
|
|
4541
|
-
operator: 'IN',
|
|
4542
|
-
data: value,
|
|
4543
|
-
});
|
|
4544
|
-
}
|
|
4545
|
-
}
|
|
4546
|
-
else {
|
|
4547
|
-
filters.push({
|
|
4548
|
-
field,
|
|
4549
|
-
operator: externalFilter.defaultOperator || 'EQUAL',
|
|
4550
|
-
data: value,
|
|
4551
|
-
});
|
|
4552
|
-
}
|
|
4553
|
-
}
|
|
4554
|
-
}
|
|
4555
|
-
}
|
|
4556
|
-
// Xử lý column filter
|
|
4557
|
-
for (const column of columns || []) {
|
|
4558
|
-
const value = rawColumnFilter?.[column.field];
|
|
4559
|
-
const field = fieldMapping?.[column.field] || column.field;
|
|
4560
|
-
const operator = columnOperator?.[column.field] || column.filter?.operator?.default;
|
|
4561
|
-
// Nếu có giá trị thì mới xử lý filter
|
|
4562
|
-
if (value !== undefined && value !== null && value !== '') {
|
|
4563
|
-
if (column.type === 'string') {
|
|
4564
|
-
filters.push({
|
|
4565
|
-
field,
|
|
4566
|
-
operator: operator || 'CONTAIN',
|
|
4567
|
-
data: value,
|
|
4568
|
-
});
|
|
4569
|
-
}
|
|
4570
|
-
else if (column.type === 'boolean') {
|
|
4571
|
-
filters.push({
|
|
4572
|
-
field,
|
|
4573
|
-
operator: 'EQUAL',
|
|
4574
|
-
data: value === true || value === 1 || value === 'true' || value === '1',
|
|
4575
|
-
});
|
|
4576
|
-
}
|
|
4577
|
-
else if (column.type === 'date' || column.type === 'datetime') {
|
|
4578
|
-
if (typeof value === 'object' && 'from' in value && 'to' in value) {
|
|
4579
|
-
if (value?.from && value?.to) {
|
|
4580
|
-
filters.push({
|
|
4581
|
-
field,
|
|
4582
|
-
operator: 'BETWEEN',
|
|
4583
|
-
data: {
|
|
4584
|
-
from: DateUtilities$1.begin(value?.from).toISOString(),
|
|
4585
|
-
to: DateUtilities$1.end(value?.to).toISOString(),
|
|
4586
|
-
},
|
|
4587
|
-
});
|
|
4588
|
-
}
|
|
4589
|
-
else if (value?.from) {
|
|
4590
|
-
filters.push({
|
|
4591
|
-
field,
|
|
4592
|
-
operator: 'GREATER_OR_EQUAL',
|
|
4593
|
-
data: DateUtilities$1.begin(value?.from).toISOString(),
|
|
4594
|
-
});
|
|
4595
|
-
}
|
|
4596
|
-
else if (value?.to) {
|
|
4597
|
-
filters.push({
|
|
4598
|
-
field,
|
|
4599
|
-
operator: 'LESS_THAN',
|
|
4600
|
-
data: DateUtilities$1.begin(DateUtilities$1.addDays(value?.to, 1)).toISOString(),
|
|
4601
|
-
});
|
|
4602
|
-
}
|
|
4603
|
-
}
|
|
4604
|
-
else {
|
|
4605
|
-
if (DateUtilities$1.isDate(value)) {
|
|
4606
|
-
filters.push({
|
|
4607
|
-
field,
|
|
4608
|
-
operator: 'BETWEEN',
|
|
4609
|
-
data: {
|
|
4610
|
-
from: DateUtilities$1.begin(value).toISOString(),
|
|
4611
|
-
to: DateUtilities$1.end(value).toISOString(),
|
|
4612
|
-
},
|
|
4613
|
-
});
|
|
4614
|
-
}
|
|
4615
|
-
}
|
|
4616
|
-
}
|
|
4617
|
-
else {
|
|
4618
|
-
if (Array.isArray(value)) {
|
|
4619
|
-
if (value.length) {
|
|
4620
|
-
filters.push({
|
|
4621
|
-
field,
|
|
4622
|
-
operator: 'IN',
|
|
4623
|
-
data: value,
|
|
4624
|
-
});
|
|
4625
|
-
}
|
|
4626
|
-
}
|
|
4627
|
-
else {
|
|
4628
|
-
filters.push({
|
|
4629
|
-
field,
|
|
4630
|
-
operator: operator || 'EQUAL',
|
|
4631
|
-
data: value,
|
|
4632
|
-
});
|
|
4633
|
-
}
|
|
4634
|
-
}
|
|
4635
|
-
}
|
|
4636
|
-
}
|
|
4637
|
-
// Xử lý orders
|
|
4638
|
-
if (orderBy && orderDirection) {
|
|
4639
|
-
req.orders.push({
|
|
4640
|
-
field: orderBy,
|
|
4641
|
-
direction: orderDirection,
|
|
4642
|
-
});
|
|
4643
|
-
}
|
|
4644
|
-
return req;
|
|
4645
|
-
};
|
|
4646
|
-
|
|
4647
4592
|
/**
|
|
4648
4593
|
* Generated bundle index. Do not edit.
|
|
4649
4594
|
*/
|