@sdcorejs/angular 19.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +723 -0
- package/assets/fonts/fonts.scss +116 -0
- package/assets/fonts/material-icons/material-icons-outlined-v110-latin-regular.woff2 +0 -0
- package/assets/fonts/material-icons/material-icons-v145-latin-regular.woff2 +0 -0
- package/assets/fonts/material-symbols/material-symbols-v29-latin-regular.woff2 +0 -0
- package/assets/fonts/roboto/roboto-v50-latin_vietnamese-500.woff2 +0 -0
- package/assets/fonts/roboto/roboto-v50-latin_vietnamese-600.woff2 +0 -0
- package/assets/fonts/roboto/roboto-v50-latin_vietnamese-italic.woff2 +0 -0
- package/assets/fonts/roboto/roboto-v50-latin_vietnamese-regular.woff2 +0 -0
- package/assets/images/coming-soon.svg +23 -0
- package/assets/images/data-empty.svg +21 -0
- package/assets/images/expired.svg +22 -0
- package/assets/images/file-error.svg +28 -0
- package/assets/images/filter-empty.svg +20 -0
- package/assets/images/filter-required.svg +21 -0
- package/assets/images/forbidden.svg +15 -0
- package/assets/images/image-error.svg +27 -0
- package/assets/images/maintenance.svg +23 -0
- package/assets/images/not-found.svg +20 -0
- package/assets/images/offline.svg +20 -0
- package/assets/images/submitted.svg +18 -0
- package/assets/images/success.svg +18 -0
- package/assets/images/unauthorized.svg +22 -0
- package/assets/images/unknown-error.svg +18 -0
- package/assets/scss/README.md +604 -0
- package/assets/scss/ckeditor5.scss +88 -0
- package/assets/scss/core/color.scss +74 -0
- package/assets/scss/core/form.scss +302 -0
- package/assets/scss/core/image.scss +35 -0
- package/assets/scss/core/scrollbar.scss +30 -0
- package/assets/scss/core/utilities/_base.scss +333 -0
- package/assets/scss/core/utilities/_border.scss +22 -0
- package/assets/scss/core/utilities/_display.scss +10 -0
- package/assets/scss/core/utilities/_elevation.scss +80 -0
- package/assets/scss/core/utilities/_flexbox.scss +54 -0
- package/assets/scss/core/utilities/_gap.scss +9 -0
- package/assets/scss/core/utilities/_grid.scss +143 -0
- package/assets/scss/core/utilities/_index.scss +17 -0
- package/assets/scss/core/utilities/_misc.scss +20 -0
- package/assets/scss/core/utilities/_overflow.scss +9 -0
- package/assets/scss/core/utilities/_position.scss +8 -0
- package/assets/scss/core/utilities/_sizing.scss +28 -0
- package/assets/scss/core/utilities/_spacing.scss +31 -0
- package/assets/scss/core/utilities/_typography.scss +99 -0
- package/assets/scss/sd-core.scss +17 -0
- package/assets/scss/themes/default.scss +45 -0
- package/assets/scss/themes/material-theme.scss +101 -0
- package/components/anchor/index.d.ts +1 -0
- package/components/anchor/src/components/anchor/anchor.component.d.ts +22 -0
- package/components/anchor/src/components/anchor-item/anchor-item.component.d.ts +13 -0
- package/components/anchor/src/components/anchor-nav/anchor-nav.component.d.ts +21 -0
- package/components/anchor/src/components/index.d.ts +2 -0
- package/components/anchor/src/index.d.ts +2 -0
- package/components/anchor/src/models/index.d.ts +1 -0
- package/components/anchor/src/models/sd-anchor.model.d.ts +7 -0
- package/components/autoid-inspector/index.d.ts +8 -0
- package/components/autoid-inspector/src/autoid-inspector.component.d.ts +55 -0
- package/components/autoid-inspector/src/models/autoid-element.model.d.ts +55 -0
- package/components/autoid-inspector/src/models/autoid-export-format.model.d.ts +6 -0
- package/components/autoid-inspector/src/models/autoid-inspector-config.model.d.ts +8 -0
- package/components/autoid-inspector/src/services/autoid-audit.service.d.ts +19 -0
- package/components/autoid-inspector/src/services/autoid-export.service.d.ts +21 -0
- package/components/autoid-inspector/src/services/autoid-highlight.service.d.ts +12 -0
- package/components/autoid-inspector/src/services/autoid-scanner.service.d.ts +23 -0
- package/components/avatar/index.d.ts +1 -0
- package/components/avatar/src/avatar.component.d.ts +19 -0
- package/components/badge/index.d.ts +1 -0
- package/components/badge/src/badge.component.d.ts +98 -0
- package/components/base/index.d.ts +1 -0
- package/components/base/src/base-secure.component.d.ts +8 -0
- package/components/button/index.d.ts +1 -0
- package/components/button/src/button.component.d.ts +42 -0
- package/components/chart/index.d.ts +4 -0
- package/components/chart/src/bar-chart.component.d.ts +18 -0
- package/components/chart/src/doughnut-chart.component.d.ts +16 -0
- package/components/chart/src/line-chart.component.d.ts +18 -0
- package/components/chart/src/pie-chart.component.d.ts +16 -0
- package/components/ckeditor-styles/index.d.ts +1 -0
- package/components/ckeditor-styles/src/ckeditor-styles.component.d.ts +25 -0
- package/components/code-editor/index.d.ts +1 -0
- package/components/code-editor/src/code-editor.component.d.ts +25 -0
- package/components/document-builder/index.d.ts +4 -0
- package/components/document-builder/src/components/header-footer-builder/header-footer-builder.component.d.ts +16 -0
- package/components/document-builder/src/components/header-footer-builder/plugins/page-number.plugin.d.ts +4 -0
- package/components/document-builder/src/components/index.d.ts +1 -0
- package/components/document-builder/src/document-builder.component.d.ts +53 -0
- package/components/document-builder/src/document-builder.config.d.ts +21 -0
- package/components/document-builder/src/document-builder.model.d.ts +41 -0
- package/components/document-builder/src/document-builder.utils.d.ts +10 -0
- package/components/document-builder/src/plugins/block-space/block-space.plugin.d.ts +9 -0
- package/components/document-builder/src/plugins/ck-comment/ck-comment.plugin.d.ts +44 -0
- package/components/document-builder/src/plugins/ck-comment/ck-comment.plugin.model.d.ts +57 -0
- package/components/document-builder/src/plugins/heading/heading.plugin.d.ts +5 -0
- package/components/document-builder/src/plugins/highlight-range/highlight-range.plugin.d.ts +4 -0
- package/components/document-builder/src/plugins/image-custom/image-custom.plugin.d.ts +31 -0
- package/components/document-builder/src/plugins/image-upload/image-upload.plugin.d.ts +5 -0
- package/components/document-builder/src/plugins/index.d.ts +11 -0
- package/components/document-builder/src/plugins/page-orientation/page-orientation.plugin.d.ts +24 -0
- package/components/document-builder/src/plugins/paste-handler/filters/bookmark.d.ts +14 -0
- package/components/document-builder/src/plugins/paste-handler/filters/br.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/image.d.ts +25 -0
- package/components/document-builder/src/plugins/paste-handler/filters/list.d.ts +29 -0
- package/components/document-builder/src/plugins/paste-handler/filters/parse.d.ts +35 -0
- package/components/document-builder/src/plugins/paste-handler/filters/removeboldwrapper.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/removegooglesheetstag.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/removeinvalidtablewidth.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/removemsattributes.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/removestyleblock.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/removexmlns.d.ts +15 -0
- package/components/document-builder/src/plugins/paste-handler/filters/replacemsfootnotes.d.ts +54 -0
- package/components/document-builder/src/plugins/paste-handler/filters/replacetabswithinprewithspaces.d.ts +24 -0
- package/components/document-builder/src/plugins/paste-handler/filters/space.d.ts +27 -0
- package/components/document-builder/src/plugins/paste-handler/filters/table.d.ts +16 -0
- package/components/document-builder/src/plugins/paste-handler/filters/utils.d.ts +25 -0
- package/components/document-builder/src/plugins/paste-handler/index.d.ts +35 -0
- package/components/document-builder/src/plugins/paste-handler/normalizers/googledocsnormalizer.d.ts +31 -0
- package/components/document-builder/src/plugins/paste-handler/normalizers/googlesheetsnormalizer.d.ts +31 -0
- package/components/document-builder/src/plugins/paste-handler/normalizers/mswordnormalizer.d.ts +29 -0
- package/components/document-builder/src/plugins/paste-handler/types.d.ts +30 -0
- package/components/document-builder/src/plugins/table-custom/index.d.ts +34 -0
- package/components/document-builder/src/plugins/variable/variable.plugin.d.ts +51 -0
- package/components/editor/index.d.ts +3 -0
- package/components/editor/src/configurations/editor.configuration.d.ts +12 -0
- package/components/editor/src/configurations/index.d.ts +1 -0
- package/components/editor/src/editor.component.d.ts +52 -0
- package/components/editor/src/models/editor.model.d.ts +10 -0
- package/components/editor/src/models/image-upload.plugin.model.d.ts +51 -0
- package/components/editor/src/models/index.d.ts +2 -0
- package/components/editor/src/plugins/image-upload/image-upload.plugin.d.ts +22 -0
- package/components/editor/src/plugins/image-upload/utils/batch.utils.d.ts +14 -0
- package/components/editor/src/plugins/image-upload/utils/index.d.ts +3 -0
- package/components/editor/src/plugins/image-upload/utils/style.utils.d.ts +3 -0
- package/components/editor/src/plugins/image-upload/utils/validate.utils.d.ts +4 -0
- package/components/form-generic/index.d.ts +4 -0
- package/components/form-generic/src/components/form-builder/components/attribute-expression/attribute-expression.component.d.ts +23 -0
- package/components/form-generic/src/components/form-builder/components/attribute-input/attribute-input.component.d.ts +17 -0
- package/components/form-generic/src/components/form-builder/components/attribute-input-number/attribute-input-number.component.d.ts +14 -0
- package/components/form-generic/src/components/form-builder/components/attribute-parameter/attribute-parameter.component.d.ts +33 -0
- package/components/form-generic/src/components/form-builder/components/attribute-select/attribute-select.component.d.ts +26 -0
- package/components/form-generic/src/components/form-builder/components/attribute-selection/attribute-selection.component.d.ts +27 -0
- package/components/form-generic/src/components/form-builder/components/attribute-selection/components/build-queries/build-queries.component.d.ts +35 -0
- package/components/form-generic/src/components/form-builder/components/attribute-selection/components/build-variables/build-variables.component.d.ts +41 -0
- package/components/form-generic/src/components/form-builder/components/attribute-switch/attribute-switch.component.d.ts +14 -0
- package/components/form-generic/src/components/form-builder/components/attribute-table/attribute-table.component.d.ts +36 -0
- package/components/form-generic/src/components/form-builder/components/attribute-template/attribute-template.component.d.ts +15 -0
- package/components/form-generic/src/components/form-builder/components/attribute-textarea/attribute-textarea.component.d.ts +17 -0
- package/components/form-generic/src/components/form-builder/components/checkbox/attribute/checkbox-attribute.component.d.ts +21 -0
- package/components/form-generic/src/components/form-builder/components/checkbox/control/checkbox-control.component.d.ts +8 -0
- package/components/form-generic/src/components/form-builder/components/checkbox/index.d.ts +2 -0
- package/components/form-generic/src/components/form-builder/components/chip-calendar/attribute/chip-calendar-attribute.component.d.ts +22 -0
- package/components/form-generic/src/components/form-builder/components/chip-calendar/control/chip-calendar-control.component.d.ts +16 -0
- package/components/form-generic/src/components/form-builder/components/chip-calendar/index.d.ts +2 -0
- package/components/form-generic/src/components/form-builder/components/chip-string/attribute/chip-string-attribute.component.d.ts +22 -0
- package/components/form-generic/src/components/form-builder/components/chip-string/control/chip-string-control.component.d.ts +16 -0
- package/components/form-generic/src/components/form-builder/components/chip-string/index.d.ts +2 -0
- package/components/form-generic/src/components/form-builder/components/configure-validation/configure-validation.component.d.ts +31 -0
- package/components/form-generic/src/components/form-builder/components/datetime/attribute/datetime-attribute.component.d.ts +21 -0
- package/components/form-generic/src/components/form-builder/components/datetime/control/datetime-control.component.d.ts +18 -0
- package/components/form-generic/src/components/form-builder/components/datetime/index.d.ts +2 -0
- package/components/form-generic/src/components/form-builder/components/expression-builder/expression-builder.component.d.ts +47 -0
- package/components/form-generic/src/components/form-builder/components/group/attribute/group-attribute.component.d.ts +23 -0
- package/components/form-generic/src/components/form-builder/components/group/index.d.ts +1 -0
- package/components/form-generic/src/components/form-builder/components/html/attribute/components/build-queries/build-queries.component.d.ts +35 -0
- package/components/form-generic/src/components/form-builder/components/html/attribute/html-attribute.component.d.ts +30 -0
- package/components/form-generic/src/components/form-builder/components/html/control/html-control.component.d.ts +20 -0
- package/components/form-generic/src/components/form-builder/components/html/index.d.ts +2 -0
- package/components/form-generic/src/components/form-builder/components/index.d.ts +13 -0
- package/components/form-generic/src/components/form-builder/components/number/attribute/number-attribute.component.d.ts +22 -0
- package/components/form-generic/src/components/form-builder/components/number/control/number-control.component.d.ts +17 -0
- package/components/form-generic/src/components/form-builder/components/number/index.d.ts +2 -0
- package/components/form-generic/src/components/form-builder/components/radio/attribute/radio-attribute.component.d.ts +22 -0
- package/components/form-generic/src/components/form-builder/components/radio/control/radio-control.component.d.ts +16 -0
- package/components/form-generic/src/components/form-builder/components/radio/index.d.ts +2 -0
- package/components/form-generic/src/components/form-builder/components/select/attribute/select-attribute.component.d.ts +22 -0
- package/components/form-generic/src/components/form-builder/components/select/control/select-control.component.d.ts +16 -0
- package/components/form-generic/src/components/form-builder/components/select/index.d.ts +2 -0
- package/components/form-generic/src/components/form-builder/components/table/attribute/table-attribute.component.d.ts +22 -0
- package/components/form-generic/src/components/form-builder/components/table/control/table-control.component.d.ts +19 -0
- package/components/form-generic/src/components/form-builder/components/table/index.d.ts +2 -0
- package/components/form-generic/src/components/form-builder/components/textarea/attribute/textarea-attribute.component.d.ts +21 -0
- package/components/form-generic/src/components/form-builder/components/textarea/control/textarea-control.component.d.ts +16 -0
- package/components/form-generic/src/components/form-builder/components/textarea/index.d.ts +2 -0
- package/components/form-generic/src/components/form-builder/components/textfield/attribute/textfield-attribute.component.d.ts +21 -0
- package/components/form-generic/src/components/form-builder/components/textfield/control/textfield-control.component.d.ts +16 -0
- package/components/form-generic/src/components/form-builder/components/textfield/index.d.ts +2 -0
- package/components/form-generic/src/components/form-builder/components/upload/attribute/upload-attribute.component.d.ts +27 -0
- package/components/form-generic/src/components/form-builder/components/upload/control/upload-control.component.d.ts +16 -0
- package/components/form-generic/src/components/form-builder/components/upload/index.d.ts +2 -0
- package/components/form-generic/src/components/form-builder/form-builder.component.d.ts +120 -0
- package/components/form-generic/src/components/form-builder/services/builder.service.d.ts +11 -0
- package/components/form-generic/src/components/form-builder/services/index.d.ts +1 -0
- package/components/form-generic/src/components/form-render/components/index.d.ts +2 -0
- package/components/form-generic/src/components/form-render/components/item/components/chip-calendar/chip-calendar.component.d.ts +30 -0
- package/components/form-generic/src/components/form-render/components/item/components/chip-string/chip-string.component.d.ts +30 -0
- package/components/form-generic/src/components/form-render/components/item/components/datetime/datetime.component.d.ts +34 -0
- package/components/form-generic/src/components/form-render/components/item/components/html/html.component.d.ts +32 -0
- package/components/form-generic/src/components/form-render/components/item/components/index.d.ts +11 -0
- package/components/form-generic/src/components/form-render/components/item/components/number/number.component.d.ts +30 -0
- package/components/form-generic/src/components/form-render/components/item/components/radio/radio.component.d.ts +38 -0
- package/components/form-generic/src/components/form-render/components/item/components/select/select.component.d.ts +43 -0
- package/components/form-generic/src/components/form-render/components/item/components/table/table.component.d.ts +51 -0
- package/components/form-generic/src/components/form-render/components/item/components/textarea/textarea.component.d.ts +30 -0
- package/components/form-generic/src/components/form-render/components/item/components/textfield/textfield.component.d.ts +33 -0
- package/components/form-generic/src/components/form-render/components/item/components/upload/upload.component.d.ts +36 -0
- package/components/form-generic/src/components/form-render/components/item/item.component.d.ts +32 -0
- package/components/form-generic/src/components/form-render/components/variable/variable.component.d.ts +22 -0
- package/components/form-generic/src/components/form-render/form-render.component.d.ts +39 -0
- package/components/form-generic/src/components/index.d.ts +3 -0
- package/components/form-generic/src/components/sd-feel-expression/sd-feel-expression.component.d.ts +43 -0
- package/components/form-generic/src/configurations/form-generic.configuration.d.ts +6 -0
- package/components/form-generic/src/configurations/form.configuration.d.ts +12 -0
- package/components/form-generic/src/configurations/index.d.ts +2 -0
- package/components/form-generic/src/models/form-generic-component.model.d.ts +318 -0
- package/components/form-generic/src/models/form-generic-definition-html.model.d.ts +28 -0
- package/components/form-generic/src/models/form-generic-definition-selection.model.d.ts +49 -0
- package/components/form-generic/src/models/form-generic-definition-table.model.d.ts +6 -0
- package/components/form-generic/src/models/form-generic-expression.model.d.ts +74 -0
- package/components/form-generic/src/models/form-generic-template.model.d.ts +6 -0
- package/components/form-generic/src/models/form-generic-validation.model.d.ts +30 -0
- package/components/form-generic/src/models/form-generic.model.d.ts +7 -0
- package/components/form-generic/src/models/form-render/form-render-args.model.d.ts +7 -0
- package/components/form-generic/src/models/form-render/form-render-entity.model.d.ts +3 -0
- package/components/form-generic/src/models/form-render/index.d.ts +2 -0
- package/components/form-generic/src/models/index.d.ts +8 -0
- package/components/form-generic/src/pipes/component-viewed.pipe.d.ts +14 -0
- package/components/form-generic/src/pipes/expression-feel.pipe.d.ts +10 -0
- package/components/form-generic/src/pipes/expression-query.pipe.d.ts +10 -0
- package/components/form-generic/src/pipes/expression-view.pipe.d.ts +8 -0
- package/components/form-generic/src/pipes/html.pipe.d.ts +11 -0
- package/components/form-generic/src/pipes/hyperlink.pipe.d.ts +8 -0
- package/components/form-generic/src/pipes/index.d.ts +7 -0
- package/components/form-generic/src/pipes/when-expression.pipe.d.ts +13 -0
- package/components/form-generic/src/services/form-generic.service.d.ts +26 -0
- package/components/form-generic/src/services/form-render.service.d.ts +10 -0
- package/components/form-generic/src/services/index.d.ts +2 -0
- package/components/history/index.d.ts +1 -0
- package/components/history/models/history.model.d.ts +13 -0
- package/components/history/pipes/view-date.pipe.d.ts +8 -0
- package/components/history/src/history.component.d.ts +7 -0
- package/components/import-excel/index.d.ts +2 -0
- package/components/import-excel/src/import-excel.component.d.ts +47 -0
- package/components/import-excel/src/import-excel.model.d.ts +102 -0
- package/components/import-excel/src/pipes/columm-hidden.pipe.d.ts +10 -0
- package/components/import-excel/src/pipes/column-transform.pipe.d.ts +11 -0
- package/components/index.d.ts +23 -0
- package/components/inform/index.d.ts +2 -0
- package/components/inform/src/inform-action.directive.d.ts +9 -0
- package/components/inform/src/inform.component.d.ts +59 -0
- package/components/mini-editor/index.d.ts +2 -0
- package/components/mini-editor/src/mini-editor.component.d.ts +91 -0
- package/components/mini-editor/src/mini-editor.model.d.ts +44 -0
- package/components/modal/index.d.ts +1 -0
- package/components/modal/src/modal.component.d.ts +30 -0
- package/components/operator/index.d.ts +1 -0
- package/components/operator/src/operator.component.d.ts +34 -0
- package/components/preview/index.d.ts +4 -0
- package/components/preview/src/preview-image/preview-image.component.d.ts +88 -0
- package/components/preview/src/preview-image/preview-image.types.d.ts +53 -0
- package/components/preview/src/preview-pdf/preview-pdf.component.d.ts +186 -0
- package/components/preview/src/preview-pdf/preview-pdf.types.d.ts +84 -0
- package/components/query-bar/index.d.ts +2 -0
- package/components/query-bar/src/components/actions-bar/actions-bar.component.d.ts +28 -0
- package/components/query-bar/src/components/build-chip/build-chip.component.d.ts +67 -0
- package/components/query-bar/src/components/chip-popover/chip-popover.component.d.ts +73 -0
- package/components/query-bar/src/components/field-picker/field-picker.component.d.ts +37 -0
- package/components/query-bar/src/components/inline-chip/inline-chip.component.d.ts +87 -0
- package/components/query-bar/src/components/inline-value-chip/inline-value-chip.component.d.ts +71 -0
- package/components/query-bar/src/components/popover-chip/popover-chip.component.d.ts +35 -0
- package/components/query-bar/src/components/saved-filters-menu/saved-filters-menu.component.d.ts +39 -0
- package/components/query-bar/src/query-bar.component.d.ts +137 -0
- package/components/query-bar/src/query-bar.model.d.ts +136 -0
- package/components/query-builder/index.d.ts +1 -0
- package/components/query-builder/src/query-builder.component.d.ts +26 -0
- package/components/quick-action/index.d.ts +1 -0
- package/components/quick-action/src/quick-action.component.d.ts +9 -0
- package/components/section/index.d.ts +2 -0
- package/components/section/src/section-item/section-item.component.d.ts +7 -0
- package/components/section/src/section.component.d.ts +18 -0
- package/components/side-drawer/index.d.ts +1 -0
- package/components/side-drawer/src/side-drawer.component.d.ts +30 -0
- package/components/splitter/index.d.ts +3 -0
- package/components/splitter/src/splitter-handle/splitter-handle.component.d.ts +24 -0
- package/components/splitter/src/splitter-panel/splitter-panel.component.d.ts +16 -0
- package/components/splitter/src/splitter-state.service.d.ts +26 -0
- package/components/splitter/src/splitter.component.d.ts +28 -0
- package/components/splitter/src/splitter.models.d.ts +23 -0
- package/components/stepper/index.d.ts +2 -0
- package/components/stepper/src/step.component.d.ts +17 -0
- package/components/stepper/src/stepper.component.d.ts +33 -0
- package/components/tab/index.d.ts +2 -0
- package/components/tab/src/tab-group.component.d.ts +37 -0
- package/components/tab/src/tab.component.d.ts +13 -0
- package/components/tab-router/index.d.ts +4 -0
- package/components/tab-router/src/actions/tab-router.action.d.ts +5 -0
- package/components/tab-router/src/components/tab-router-item/tab-router-item.component.d.ts +22 -0
- package/components/tab-router/src/components/tab-router-nav/tab-router-nav.component.d.ts +16 -0
- package/components/tab-router/src/components/tab-router-outlet/tab-router-outlet.component.d.ts +15 -0
- package/components/tab-router/src/decorators/tab.decorator.d.ts +16 -0
- package/components/tab-router/src/events/tab-router.event.d.ts +11 -0
- package/components/tab-router/src/models/index.d.ts +1 -0
- package/components/tab-router/src/models/tab-router.model.d.ts +21 -0
- package/components/tab-router/src/pipes/tab-info.pipe.d.ts +11 -0
- package/components/tab-router/src/services/tab-decorator.service.d.ts +9 -0
- package/components/tab-router/src/services/tab-router.service.d.ts +27 -0
- package/components/table/index.d.ts +11 -0
- package/components/table/src/components/column-title/column-title.component.d.ts +10 -0
- package/components/table/src/components/command/desktop-command.component.d.ts +14 -0
- package/components/table/src/components/command/pipes/command.pipe.d.ts +30 -0
- package/components/table/src/components/command/pipes/filter.pipe.d.ts +35 -0
- package/components/table/src/components/config/config.component.d.ts +25 -0
- package/components/table/src/components/desktop-cell/desktop-cell.component.d.ts +24 -0
- package/components/table/src/components/desktop-cell/view/view.component.d.ts +24 -0
- package/components/table/src/components/filter/column-filter/column-filter.component.d.ts +32 -0
- package/components/table/src/components/filter/external-filter/external-filter.component.d.ts +27 -0
- package/components/table/src/components/filter/index.d.ts +3 -0
- package/components/table/src/components/filter/mobile-filter/mobile-filter.component.d.ts +42 -0
- package/components/table/src/components/index.d.ts +3 -0
- package/components/table/src/components/popup-export/popup-export.component.d.ts +51 -0
- package/components/table/src/components/selector-action/action-filter.pipe.d.ts +34 -0
- package/components/table/src/components/selector-action/selector-action.component.d.ts +16 -0
- package/components/table/src/configurations/index.d.ts +1 -0
- package/components/table/src/configurations/table.configuration.d.ts +35 -0
- package/components/table/src/directives/index.d.ts +6 -0
- package/components/table/src/directives/sd-column-resize.directive.d.ts +13 -0
- package/components/table/src/directives/sd-table-cell-def.directive.d.ts +16 -0
- package/components/table/src/directives/sd-table-column-filter-def.directive.d.ts +16 -0
- package/components/table/src/directives/sd-table-expand-def.directive.d.ts +13 -0
- package/components/table/src/directives/sd-table-filter-def.directive.d.ts +14 -0
- package/components/table/src/directives/sd-table-footer-def.directive.d.ts +16 -0
- package/components/table/src/directives/sd-table-group-def.directive.d.ts +44 -0
- package/components/table/src/directives/sd-table-title-def.directive.d.ts +14 -0
- package/components/table/src/directives/sticky-shadow.directive.d.ts +17 -0
- package/components/table/src/models/index.d.ts +5 -0
- package/components/table/src/models/table-column.model.d.ts +119 -0
- package/components/table/src/models/table-command.model.d.ts +25 -0
- package/components/table/src/models/table-item.model.d.ts +65 -0
- package/components/table/src/models/table-option-config.model.d.ts +42 -0
- package/components/table/src/models/table-option-expand.model.d.ts +6 -0
- package/components/table/src/models/table-option-export.model.d.ts +42 -0
- package/components/table/src/models/table-option-group.model.d.ts +21 -0
- package/components/table/src/models/table-option-paginate.model.d.ts +7 -0
- package/components/table/src/models/table-option-reload.model.d.ts +7 -0
- package/components/table/src/models/table-option-selector.model.d.ts +105 -0
- package/components/table/src/models/table-option-sort.model.d.ts +3 -0
- package/components/table/src/models/table-option-style.model.d.ts +11 -0
- package/components/table/src/models/table-option-tree.model.d.ts +12 -0
- package/components/table/src/models/table-option.model.d.ts +181 -0
- package/components/table/src/pipes/filter-values.pipe.d.ts +8 -0
- package/components/table/src/pipes/index.d.ts +2 -0
- package/components/table/src/pipes/sd-group.pipe.d.ts +23 -0
- package/components/table/src/pipes/sd-tree.pipe.d.ts +9 -0
- package/components/table/src/pipes/selection-disabled.pipe.d.ts +9 -0
- package/components/table/src/pipes/selection-visible-select-all.pipe.d.ts +9 -0
- package/components/table/src/pipes/selection-visible.pipe.d.ts +9 -0
- package/components/table/src/services/column-width.util.d.ts +7 -0
- package/components/table/src/services/config.service.d.ts +21 -0
- package/components/table/src/services/index.d.ts +3 -0
- package/components/table/src/services/table-export/table-export.service.d.ts +26 -0
- package/components/table/src/services/table-filter/table-filter.model.d.ts +150 -0
- package/components/table/src/services/table-filter/table-filter.service.d.ts +20 -0
- package/components/table/src/services/table-format/table-format.service.d.ts +16 -0
- package/components/table/src/services/tree/tree.util.d.ts +14 -0
- package/components/table/src/table.component.d.ts +137 -0
- package/components/upload-file/index.d.ts +2 -0
- package/components/upload-file/src/components/preview/preview.component.d.ts +23 -0
- package/components/upload-file/src/configurations/index.d.ts +1 -0
- package/components/upload-file/src/configurations/upload-file.configuration.d.ts +50 -0
- package/components/upload-file/src/pipes/document.pipe.d.ts +8 -0
- package/components/upload-file/src/pipes/image.pipe.d.ts +8 -0
- package/components/upload-file/src/pipes/index.d.ts +2 -0
- package/components/upload-file/src/services/index.d.ts +2 -0
- package/components/upload-file/src/services/upload-file.model.d.ts +12 -0
- package/components/upload-file/src/services/upload-file.service.d.ts +10 -0
- package/components/upload-file/src/upload-file.component.d.ts +85 -0
- package/components/view/index.d.ts +1 -0
- package/components/view/src/view.component.d.ts +17 -0
- package/configurations/index.d.ts +1 -0
- package/configurations/src/sd-core.configuration.d.ts +10 -0
- package/directives/index.d.ts +6 -0
- package/directives/src/sd-desktop.directive.d.ts +7 -0
- package/directives/src/sd-hover-copy.directive.d.ts +16 -0
- package/directives/src/sd-href.directive.d.ts +9 -0
- package/directives/src/sd-mobile.directive.d.ts +7 -0
- package/directives/src/sd-scroll.directive.d.ts +14 -0
- package/directives/src/sd-tooltip.directive.d.ts +26 -0
- package/fesm2022/sdcorejs-angular-components-anchor.mjs +197 -0
- package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +723 -0
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-avatar.mjs +103 -0
- package/fesm2022/sdcorejs-angular-components-avatar.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-badge.mjs +138 -0
- package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-base.mjs +23 -0
- package/fesm2022/sdcorejs-angular-components-base.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-button.mjs +118 -0
- package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-chart.mjs +290 -0
- package/fesm2022/sdcorejs-angular-components-chart.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs +38 -0
- package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-code-editor.mjs +128 -0
- package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs +5053 -0
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-editor.mjs +1079 -0
- package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs +6715 -0
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-history.mjs +51 -0
- package/fesm2022/sdcorejs-angular-components-history.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs +583 -0
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-inform.mjs +190 -0
- package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-mini-editor.mjs +333 -0
- package/fesm2022/sdcorejs-angular-components-mini-editor.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-modal.mjs +108 -0
- package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-operator.mjs +75 -0
- package/fesm2022/sdcorejs-angular-components-operator.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-preview.mjs +1987 -0
- package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs +1430 -0
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs +83 -0
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-quick-action.mjs +22 -0
- package/fesm2022/sdcorejs-angular-components-quick-action.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-section.mjs +64 -0
- package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +129 -0
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-splitter.mjs +649 -0
- package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-stepper.mjs +140 -0
- package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs +605 -0
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-tab.mjs +145 -0
- package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-table.mjs +4524 -0
- package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs +868 -0
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components-view.mjs +48 -0
- package/fesm2022/sdcorejs-angular-components-view.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-components.mjs +28 -0
- package/fesm2022/sdcorejs-angular-components.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-configurations.mjs +10 -0
- package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-directives.mjs +497 -0
- package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +454 -0
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +139 -0
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +364 -0
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-chip.mjs +363 -0
- package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs +354 -0
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-date.mjs +356 -0
- package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs +748 -0
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-directives.mjs +67 -0
- package/fesm2022/sdcorejs-angular-forms-directives.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs +117 -0
- package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs +438 -0
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-input.mjs +354 -0
- package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-label.mjs +51 -0
- package/fesm2022/sdcorejs-angular-forms-label.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-models.mjs +137 -0
- package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-radio.mjs +190 -0
- package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-select.mjs +593 -0
- package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-switch.mjs +144 -0
- package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs +300 -0
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-forms.mjs +48 -0
- package/fesm2022/sdcorejs-angular-forms.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-handlers.mjs +67 -0
- package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-i18n.mjs +2592 -0
- package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-interceptors.mjs +166 -0
- package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-models.mjs +6 -0
- package/fesm2022/sdcorejs-angular-models.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-modules-auth.mjs +137 -0
- package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-modules-authom.mjs +360 -0
- package/fesm2022/sdcorejs-angular-modules-authom.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +127 -0
- package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-modules-layout.mjs +1633 -0
- package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-modules-permission.mjs +249 -0
- package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-modules.mjs +10 -0
- package/fesm2022/sdcorejs-angular-modules.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-pipes.mjs +149 -0
- package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-api.mjs +235 -0
- package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-cache.mjs +161 -0
- package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-confirm.mjs +208 -0
- package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-docx.mjs +300 -0
- package/fesm2022/sdcorejs-angular-services-docx.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-excel.mjs +422 -0
- package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-license.mjs +104 -0
- package/fesm2022/sdcorejs-angular-services-license.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-loading.mjs +92 -0
- package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-notify.mjs +268 -0
- package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services-storage.mjs +174 -0
- package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-services.mjs +13 -0
- package/fesm2022/sdcorejs-angular-services.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-utilities-data-state.mjs +38 -0
- package/fesm2022/sdcorejs-angular-utilities-data-state.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +391 -0
- package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-utilities-models.mjs +55 -0
- package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-utilities.mjs +8 -0
- package/fesm2022/sdcorejs-angular-utilities.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular.mjs +19 -0
- package/fesm2022/sdcorejs-angular.mjs.map +1 -0
- package/forms/autocomplete/index.d.ts +1 -0
- package/forms/autocomplete/src/autocomplete.component.d.ts +93 -0
- package/forms/checkbox/index.d.ts +1 -0
- package/forms/checkbox/src/checkbox.component.d.ts +28 -0
- package/forms/chip/index.d.ts +1 -0
- package/forms/chip/src/chip.component.d.ts +71 -0
- package/forms/chip/src/index.d.ts +2 -0
- package/forms/chip/src/pipes/index.d.ts +1 -0
- package/forms/chip/src/pipes/removable-chip.pipe.d.ts +8 -0
- package/forms/chip-calendar/index.d.ts +1 -0
- package/forms/chip-calendar/src/chip-calendar.component.d.ts +71 -0
- package/forms/chip-calendar/src/pipes/index.d.ts +1 -0
- package/forms/chip-calendar/src/pipes/removable-chip.pipe.d.ts +8 -0
- package/forms/date/index.d.ts +1 -0
- package/forms/date/src/date.component.d.ts +79 -0
- package/forms/date-range/index.d.ts +1 -0
- package/forms/date-range/src/date-range.component.d.ts +81 -0
- package/forms/datetime/index.d.ts +1 -0
- package/forms/datetime/src/datetime.component.d.ts +89 -0
- package/forms/datetime/src/popup/sd-datetime-picker.component.d.ts +37 -0
- package/forms/datetime/src/popup/sd-time-spinner.component.d.ts +41 -0
- package/forms/directives/index.d.ts +4 -0
- package/forms/directives/src/item-def.directive.d.ts +13 -0
- package/forms/directives/src/label-def.directive.d.ts +9 -0
- package/forms/directives/src/suffix-def.directive.d.ts +9 -0
- package/forms/directives/src/view-def.directive.d.ts +14 -0
- package/forms/index.d.ts +17 -0
- package/forms/input/index.d.ts +1 -0
- package/forms/input/src/input.component.d.ts +84 -0
- package/forms/input-color/index.d.ts +1 -0
- package/forms/input-color/src/input-color.component.d.ts +32 -0
- package/forms/input-number/index.d.ts +1 -0
- package/forms/input-number/src/input-number.component.d.ts +95 -0
- package/forms/label/index.d.ts +1 -0
- package/forms/label/src/label.component.d.ts +13 -0
- package/forms/models/index.d.ts +6 -0
- package/forms/models/src/form-control-state.d.ts +23 -0
- package/forms/models/src/sd-custom-validator.model.d.ts +15 -0
- package/forms/models/src/sd-form-control.model.d.ts +21 -0
- package/forms/models/src/sd-form.configuration.d.ts +6 -0
- package/forms/models/src/sd-search.model.d.ts +7 -0
- package/forms/models/src/sd-selection-data.model.d.ts +7 -0
- package/forms/radio/index.d.ts +1 -0
- package/forms/radio/src/radio.component.d.ts +49 -0
- package/forms/select/index.d.ts +1 -0
- package/forms/select/src/select.component.d.ts +93 -0
- package/forms/src/forms.module.d.ts +14 -0
- package/forms/switch/index.d.ts +1 -0
- package/forms/switch/src/switch.component.d.ts +32 -0
- package/forms/textarea/index.d.ts +1 -0
- package/forms/textarea/src/textarea.component.d.ts +65 -0
- package/handlers/global-error.handler.d.ts +9 -0
- package/handlers/index.d.ts +1 -0
- package/i18n/index.d.ts +5 -0
- package/i18n/src/en.d.ts +449 -0
- package/i18n/src/i18n.messages.d.ts +2 -0
- package/i18n/src/i18n.pipe.d.ts +9 -0
- package/i18n/src/i18n.service.d.ts +14 -0
- package/i18n/src/i18n.token.d.ts +1 -0
- package/i18n/src/i18n.types.d.ts +31 -0
- package/i18n/src/ja.d.ts +2 -0
- package/i18n/src/ko.d.ts +2 -0
- package/i18n/src/vi.d.ts +2 -0
- package/i18n/src/zh.d.ts +2 -0
- package/index.d.ts +5 -0
- package/interceptors/index.d.ts +2 -0
- package/interceptors/no-internet/no-internet.interceptor.d.ts +14 -0
- package/interceptors/unauthorized/unauthorized.interceptor.d.ts +12 -0
- package/models/index.d.ts +1 -0
- package/models/src/language.model.d.ts +2 -0
- package/modules/auth/configurations/auth.configuration.d.ts +19 -0
- package/modules/auth/configurations/index.d.ts +1 -0
- package/modules/auth/guards/auth.guard.d.ts +11 -0
- package/modules/auth/guards/index.d.ts +2 -0
- package/modules/auth/guards/portal.guard.d.ts +11 -0
- package/modules/auth/index.d.ts +3 -0
- package/modules/auth/services/auth.model.d.ts +8 -0
- package/modules/auth/services/auth.service.d.ts +17 -0
- package/modules/auth/services/index.d.ts +2 -0
- package/modules/authom/authom.configuration.d.ts +17 -0
- package/modules/authom/authom.interceptor.d.ts +3 -0
- package/modules/authom/authom.module.d.ts +16 -0
- package/modules/authom/authom.service.d.ts +32 -0
- package/modules/authom/index.d.ts +35 -0
- package/modules/index.d.ts +5 -0
- package/modules/keycloak/index.d.ts +4 -0
- package/modules/keycloak/keycloak.configuration.d.ts +11 -0
- package/modules/keycloak/keycloak.interceptor.d.ts +2 -0
- package/modules/keycloak/keycloak.module.d.ts +18 -0
- package/modules/keycloak/keycloak.service.d.ts +14 -0
- package/modules/layout/components/index.d.ts +4 -0
- package/modules/layout/components/layout-main/layout-main.component.d.ts +12 -0
- package/modules/layout/components/page/page.component.d.ts +10 -0
- package/modules/layout/components/sidebar-mobile-v1/components/sidebar/sidebar.component.d.ts +35 -0
- package/modules/layout/components/sidebar-mobile-v1/components/user/user.component.d.ts +24 -0
- package/modules/layout/components/sidebar-mobile-v1/main.component.d.ts +21 -0
- package/modules/layout/components/sidebar-v1/components/sidebar/sidebar.component.d.ts +55 -0
- package/modules/layout/components/sidebar-v1/components/user/user.component.d.ts +22 -0
- package/modules/layout/components/sidebar-v1/main.component.d.ts +24 -0
- package/modules/layout/configurations/index.d.ts +1 -0
- package/modules/layout/configurations/layout.configuration.d.ts +67 -0
- package/modules/layout/index.d.ts +5 -0
- package/modules/layout/layout.module.d.ts +7 -0
- package/modules/layout/layout.routing.d.ts +2 -0
- package/modules/layout/modules/forbidden/forbidden.module.d.ts +8 -0
- package/modules/layout/modules/forbidden/forbidden.routing.d.ts +2 -0
- package/modules/layout/modules/forbidden/index.d.ts +1 -0
- package/modules/layout/modules/forbidden/pages/root/root.component.d.ts +10 -0
- package/modules/layout/modules/home/components/home-page/home-page.component.d.ts +33 -0
- package/modules/layout/modules/home/guards/home-redirect.guard.d.ts +11 -0
- package/modules/layout/modules/home/home.module.d.ts +8 -0
- package/modules/layout/modules/home/home.routing.d.ts +7 -0
- package/modules/layout/modules/home/index.d.ts +1 -0
- package/modules/layout/modules/index.d.ts +3 -0
- package/modules/layout/modules/not-found/index.d.ts +1 -0
- package/modules/layout/modules/not-found/not-found.module.d.ts +8 -0
- package/modules/layout/modules/not-found/not-found.routing.d.ts +2 -0
- package/modules/layout/modules/not-found/pages/root/root.component.d.ts +10 -0
- package/modules/layout/pipes/high-light-search.pipe.d.ts +10 -0
- package/modules/layout/pipes/index.d.ts +3 -0
- package/modules/layout/pipes/menu-focus.pipe.d.ts +10 -0
- package/modules/layout/pipes/menu.pipe.d.ts +13 -0
- package/modules/layout/services/index.d.ts +3 -0
- package/modules/layout/services/layout.service.d.ts +10 -0
- package/modules/layout/services/menu/menu.model.d.ts +24 -0
- package/modules/layout/services/storage/storage.service.d.ts +13 -0
- package/modules/permission/index.d.ts +4 -0
- package/modules/permission/src/configurations/index.d.ts +1 -0
- package/modules/permission/src/configurations/permission.configuration.d.ts +63 -0
- package/modules/permission/src/directives/index.d.ts +1 -0
- package/modules/permission/src/directives/permission.directive.d.ts +9 -0
- package/modules/permission/src/guards/index.d.ts +1 -0
- package/modules/permission/src/guards/permission.guard.d.ts +14 -0
- package/modules/permission/src/services/index.d.ts +1 -0
- package/modules/permission/src/services/permission.service.d.ts +12 -0
- package/package.json +344 -0
- package/pipes/index.d.ts +4 -0
- package/pipes/src/empty.pipe.d.ts +7 -0
- package/pipes/src/format-number.pipe.d.ts +11 -0
- package/pipes/src/safe-html.pipe.d.ts +11 -0
- package/pipes/src/time-different.pipe.d.ts +13 -0
- package/public-api.d.ts +10 -0
- package/services/api/index.d.ts +4 -0
- package/services/api/src/api.model.d.ts +36 -0
- package/services/api/src/api.module.d.ts +6 -0
- package/services/api/src/api.service.d.ts +19 -0
- package/services/api/src/interceptors/api.interceptor.d.ts +9 -0
- package/services/cache/index.d.ts +2 -0
- package/services/cache/src/cache.model.d.ts +30 -0
- package/services/cache/src/cache.service.d.ts +9 -0
- package/services/confirm/index.d.ts +1 -0
- package/services/confirm/src/lib/components/dialog-confirm/dialog-confirm.component.d.ts +46 -0
- package/services/confirm/src/lib/confirm.service.d.ts +55 -0
- package/services/confirm/src/public-api.d.ts +2 -0
- package/services/docx/index.d.ts +1 -0
- package/services/docx/src/lib/docx.model.d.ts +9 -0
- package/services/docx/src/lib/docx.service.d.ts +15 -0
- package/services/docx/src/lib/pandoc-core.d.ts +11 -0
- package/services/docx/src/public-api.d.ts +2 -0
- package/services/excel/index.d.ts +1 -0
- package/services/excel/src/lib/excel.model.d.ts +31 -0
- package/services/excel/src/lib/excel.service.d.ts +19 -0
- package/services/excel/src/public-api.d.ts +2 -0
- package/services/index.d.ts +8 -0
- package/services/license/index.d.ts +1 -0
- package/services/license/src/license.service.d.ts +10 -0
- package/services/loading/index.d.ts +1 -0
- package/services/loading/src/loading.service.d.ts +14 -0
- package/services/notify/index.d.ts +2 -0
- package/services/notify/src/components/toast/toast.component.d.ts +28 -0
- package/services/notify/src/components/toast-container.component.d.ts +8 -0
- package/services/notify/src/notify.model.d.ts +16 -0
- package/services/notify/src/notify.service.d.ts +20 -0
- package/services/storage/index.d.ts +2 -0
- package/services/storage/src/storage.model.d.ts +20 -0
- package/services/storage/src/storage.service.d.ts +10 -0
- package/utilities/data-state/index.d.ts +1 -0
- package/utilities/data-state/src/data-state.d.ts +11 -0
- package/utilities/extensions/index.d.ts +6 -0
- package/utilities/extensions/src/array.extension.d.ts +1 -0
- package/utilities/extensions/src/color.extension.d.ts +5 -0
- package/utilities/extensions/src/date.extension.d.ts +1 -0
- package/utilities/extensions/src/number.extension.d.ts +1 -0
- package/utilities/extensions/src/string.extension.d.ts +50 -0
- package/utilities/extensions/src/utility.extension.d.ts +31 -0
- package/utilities/index.d.ts +3 -0
- package/utilities/models/index.d.ts +12 -0
- package/utilities/models/src/color.model.d.ts +4 -0
- package/utilities/models/src/empty.model.d.ts +4 -0
- package/utilities/models/src/filter.model.d.ts +12 -0
- package/utilities/models/src/icon.model.d.ts +2 -0
- package/utilities/models/src/maybe-async.model.d.ts +10 -0
- package/utilities/models/src/nested-key-of.model.d.ts +4 -0
- package/utilities/models/src/operator.model.d.ts +13 -0
- package/utilities/models/src/order.model.d.ts +4 -0
- package/utilities/models/src/paging.model.d.ts +8 -0
- package/utilities/models/src/pattern.model.d.ts +21 -0
- package/utilities/models/src/size.model.d.ts +4 -0
- package/utilities/models/src/unwrap-signal.model.d.ts +6 -0
|
@@ -0,0 +1,1430 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { input, output, signal, computed, effect, ChangeDetectionStrategy, Component, booleanAttribute, model, viewChild, afterNextRender, isSignal, inject, Injector, ChangeDetectorRef, viewChildren } from '@angular/core';
|
|
4
|
+
import * as i1$1 from '@angular/forms';
|
|
5
|
+
import { FormsModule } from '@angular/forms';
|
|
6
|
+
import * as i2 from '@angular/material/icon';
|
|
7
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
8
|
+
import * as i1 from '@angular/material/menu';
|
|
9
|
+
import { MatMenuModule, MatMenu, MatMenuTrigger } from '@angular/material/menu';
|
|
10
|
+
import * as i3 from '@angular/material/tooltip';
|
|
11
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
12
|
+
import { I18nService } from '@sdcorejs/angular/i18n';
|
|
13
|
+
import { SdOperator } from '@sdcorejs/angular/components/operator';
|
|
14
|
+
import { SdDate } from '@sdcorejs/angular/forms/date';
|
|
15
|
+
import { SdDateRange } from '@sdcorejs/angular/forms/date-range';
|
|
16
|
+
import { SdDatetime } from '@sdcorejs/angular/forms/datetime';
|
|
17
|
+
import { SdInput } from '@sdcorejs/angular/forms/input';
|
|
18
|
+
import { SdInputNumber } from '@sdcorejs/angular/forms/input-number';
|
|
19
|
+
import { SdSelect } from '@sdcorejs/angular/forms/select';
|
|
20
|
+
import { isObservable } from 'rxjs';
|
|
21
|
+
import { SdButton } from '@sdcorejs/angular/components/button';
|
|
22
|
+
|
|
23
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
24
|
+
/**
|
|
25
|
+
* Saved-filters dropdown for `sd-query-bar`.
|
|
26
|
+
*
|
|
27
|
+
* Self-contained: persists the list to `localStorage` under
|
|
28
|
+
* `sd-query-bar:savedFilters:<key>` whenever the host passes a `[key]`. Without a
|
|
29
|
+
* key the dropdown still renders (host can still preview the menu) but the
|
|
30
|
+
* trigger stays disabled.
|
|
31
|
+
*
|
|
32
|
+
* The host owns the live `[query]`: each save snapshots it into a new entry,
|
|
33
|
+
* each apply emits the chosen filter back via `(apply)` for the host to install
|
|
34
|
+
* into its own `filters` / `logic` / `search` model.
|
|
35
|
+
*
|
|
36
|
+
* The save action (`promptSave`) is public so the parent toolbar can place its
|
|
37
|
+
* own "Lưu bộ lọc" button anywhere — typically next to the Search trigger.
|
|
38
|
+
*/
|
|
39
|
+
class SdQuerySavedFiltersMenu {
|
|
40
|
+
/** Namespace key used to scope localStorage. `undefined` → menu disabled. */
|
|
41
|
+
key = input(undefined);
|
|
42
|
+
/** Current query the host wants to snapshot when the user clicks "Save current". */
|
|
43
|
+
query = input({ filters: [], logic: 'AND' });
|
|
44
|
+
/** Fired when the user picks a saved filter from the list. */
|
|
45
|
+
apply = output();
|
|
46
|
+
savedFilters = signal([]);
|
|
47
|
+
#storageKey = computed(() => {
|
|
48
|
+
const k = this.key();
|
|
49
|
+
return k ? `sd-query-bar:savedFilters:${k}` : undefined;
|
|
50
|
+
});
|
|
51
|
+
// why: reload whenever the namespace key changes (e.g. host switches user/context).
|
|
52
|
+
#loadFilters = effect(() => {
|
|
53
|
+
const storage = this.#storageKey();
|
|
54
|
+
if (!storage) {
|
|
55
|
+
this.savedFilters.set([]);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const raw = localStorage.getItem(storage);
|
|
60
|
+
this.savedFilters.set(raw ? JSON.parse(raw) : []);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
this.savedFilters.set([]);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
#persist(filters) {
|
|
67
|
+
const storage = this.#storageKey();
|
|
68
|
+
if (!storage)
|
|
69
|
+
return;
|
|
70
|
+
try {
|
|
71
|
+
localStorage.setItem(storage, JSON.stringify(filters));
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
/* quota / disabled storage — silent */
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Prompt for a name and append a snapshot of the current query as a new
|
|
79
|
+
* saved filter. Public so the parent toolbar can wire its external "save"
|
|
80
|
+
* button to this method without re-implementing persistence.
|
|
81
|
+
*/
|
|
82
|
+
promptSave() {
|
|
83
|
+
if (!this.#storageKey())
|
|
84
|
+
return;
|
|
85
|
+
const name = window.prompt('Tên bộ lọc:');
|
|
86
|
+
if (!name?.trim())
|
|
87
|
+
return;
|
|
88
|
+
const filter = {
|
|
89
|
+
id: (typeof crypto !== 'undefined' && crypto.randomUUID) ? crypto.randomUUID() : String(Date.now()),
|
|
90
|
+
name: name.trim(),
|
|
91
|
+
query: this.query(),
|
|
92
|
+
};
|
|
93
|
+
const next = [...this.savedFilters(), filter];
|
|
94
|
+
this.savedFilters.set(next);
|
|
95
|
+
this.#persist(next);
|
|
96
|
+
}
|
|
97
|
+
/** Apply a saved filter — host re-installs filters/logic/search via `(apply)`. */
|
|
98
|
+
pick(filter) {
|
|
99
|
+
this.apply.emit(filter);
|
|
100
|
+
}
|
|
101
|
+
/** Delete a saved filter by id. Stops propagation so the parent button doesn't fire `pick`. */
|
|
102
|
+
remove(id, ev) {
|
|
103
|
+
ev?.stopPropagation();
|
|
104
|
+
const next = this.savedFilters().filter(f => f.id !== id);
|
|
105
|
+
this.savedFilters.set(next);
|
|
106
|
+
this.#persist(next);
|
|
107
|
+
}
|
|
108
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQuerySavedFiltersMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
109
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdQuerySavedFiltersMenu, isStandalone: true, selector: "sd-query-saved-filters-menu", inputs: { key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { apply: "apply" }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"c-saved-filters\"\n [disabled]=\"!key()\"\n [matMenuTriggerFor]=\"menu\"\n [matTooltip]=\"key() ? 'B\u1ED9 l\u1ECDc \u0111\u00E3 l\u01B0u' : 'C\u1EA7n [savedFiltersKey] \u0111\u1EC3 b\u1EADt'\">\n <mat-icon fontSet=\"material-icons-outlined\">bookmark_border</mat-icon>\n</button>\n\n<mat-menu #menu=\"matMenu\" class=\"c-saved-filters-menu\" xPosition=\"before\" yPosition=\"below\">\n @for (f of savedFilters(); track f.id) {\n <button type=\"button\" mat-menu-item class=\"c-sf-row\" (click)=\"pick(f)\">\n <mat-icon class=\"c-sf-lead\" fontSet=\"material-icons-outlined\">filter_alt</mat-icon>\n <span class=\"c-sf-name\">{{ f.name }}</span>\n <span\n role=\"button\"\n tabindex=\"0\"\n class=\"c-sf-del\"\n aria-label=\"Xo\u00E1 b\u1ED9 l\u1ECDc\"\n (click)=\"remove(f.id, $event)\"\n (keydown.enter)=\"remove(f.id, $event)\">\n <mat-icon fontSet=\"material-icons-outlined\">close</mat-icon>\n </span>\n </button>\n } @empty {\n <div class=\"c-sf-empty\">Ch\u01B0a c\u00F3 b\u1ED9 l\u1ECDc n\u00E0o.</div>\n }\n <!-- why: footer save action s\u1ED1ng trong mat-menu \u0111\u1EC3 dropdown gom 1 v\u00F9ng (list +\n save) \u2014 user m\u1EDF dropdown th\u1EA5y ngay c\u1EA3 2 h\u00E0nh \u0111\u1ED9ng, kh\u00F4ng ph\u1EA3i t\u00ECm n\u00FAt l\u01B0u\n \u1EDF ch\u1ED7 kh\u00E1c tr\u00EAn toolbar. -->\n <hr class=\"c-sf-divider\" aria-hidden=\"true\" />\n <button type=\"button\" mat-menu-item class=\"c-sf-save\" [disabled]=\"!key()\" (click)=\"promptSave()\">\n <mat-icon fontSet=\"material-icons-outlined\">bookmark_add</mat-icon>\n <span>L\u01B0u b\u1ED9 l\u1ECDc hi\u1EC7n t\u1EA1i</span>\n </button>\n</mat-menu>\n", styles: [":host{display:inline-flex}.c-saved-filters{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid #d6d8db;border-radius:6px;background:#fff;color:#5f6368;cursor:pointer;padding:0}.c-saved-filters mat-icon{font-size:18px;width:18px;height:18px}.c-saved-filters:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-saved-filters[disabled]{opacity:.5;cursor:not-allowed}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu{min-width:220px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .mat-mdc-menu-item.c-sf-row{min-height:40px;padding-left:16px;padding-right:4px}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .mat-mdc-menu-item.c-sf-row .mat-mdc-menu-item-text{display:flex;align-items:center;gap:8px;min-width:0;line-height:1.4}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-lead{display:inline-flex;align-items:center;justify-content:center;font-size:18px!important;width:18px!important;height:18px!important;line-height:1}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del{flex:0 0 auto;margin-left:auto;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;color:#7a7a7a;cursor:pointer;padding:0}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del mat-icon{margin:0!important;padding:0;font-size:16px!important;width:16px!important;height:16px!important;line-height:16px!important;text-align:center;overflow:hidden}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del:hover{background:#0000000f;color:#d23535}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-empty{padding:12px 16px;font-size:13px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-divider{border:0;border-top:1px solid #e2e4e7;margin:4px 0}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-save mat-icon{margin-right:8px}\n"], dependencies: [{ kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1.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: i1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i1.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: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
110
|
+
}
|
|
111
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQuerySavedFiltersMenu, decorators: [{
|
|
112
|
+
type: Component,
|
|
113
|
+
args: [{ selector: 'sd-query-saved-filters-menu', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatMenuModule, MatIconModule, MatTooltipModule], template: "<button\n type=\"button\"\n class=\"c-saved-filters\"\n [disabled]=\"!key()\"\n [matMenuTriggerFor]=\"menu\"\n [matTooltip]=\"key() ? 'B\u1ED9 l\u1ECDc \u0111\u00E3 l\u01B0u' : 'C\u1EA7n [savedFiltersKey] \u0111\u1EC3 b\u1EADt'\">\n <mat-icon fontSet=\"material-icons-outlined\">bookmark_border</mat-icon>\n</button>\n\n<mat-menu #menu=\"matMenu\" class=\"c-saved-filters-menu\" xPosition=\"before\" yPosition=\"below\">\n @for (f of savedFilters(); track f.id) {\n <button type=\"button\" mat-menu-item class=\"c-sf-row\" (click)=\"pick(f)\">\n <mat-icon class=\"c-sf-lead\" fontSet=\"material-icons-outlined\">filter_alt</mat-icon>\n <span class=\"c-sf-name\">{{ f.name }}</span>\n <span\n role=\"button\"\n tabindex=\"0\"\n class=\"c-sf-del\"\n aria-label=\"Xo\u00E1 b\u1ED9 l\u1ECDc\"\n (click)=\"remove(f.id, $event)\"\n (keydown.enter)=\"remove(f.id, $event)\">\n <mat-icon fontSet=\"material-icons-outlined\">close</mat-icon>\n </span>\n </button>\n } @empty {\n <div class=\"c-sf-empty\">Ch\u01B0a c\u00F3 b\u1ED9 l\u1ECDc n\u00E0o.</div>\n }\n <!-- why: footer save action s\u1ED1ng trong mat-menu \u0111\u1EC3 dropdown gom 1 v\u00F9ng (list +\n save) \u2014 user m\u1EDF dropdown th\u1EA5y ngay c\u1EA3 2 h\u00E0nh \u0111\u1ED9ng, kh\u00F4ng ph\u1EA3i t\u00ECm n\u00FAt l\u01B0u\n \u1EDF ch\u1ED7 kh\u00E1c tr\u00EAn toolbar. -->\n <hr class=\"c-sf-divider\" aria-hidden=\"true\" />\n <button type=\"button\" mat-menu-item class=\"c-sf-save\" [disabled]=\"!key()\" (click)=\"promptSave()\">\n <mat-icon fontSet=\"material-icons-outlined\">bookmark_add</mat-icon>\n <span>L\u01B0u b\u1ED9 l\u1ECDc hi\u1EC7n t\u1EA1i</span>\n </button>\n</mat-menu>\n", styles: [":host{display:inline-flex}.c-saved-filters{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid #d6d8db;border-radius:6px;background:#fff;color:#5f6368;cursor:pointer;padding:0}.c-saved-filters mat-icon{font-size:18px;width:18px;height:18px}.c-saved-filters:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-saved-filters[disabled]{opacity:.5;cursor:not-allowed}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu{min-width:220px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .mat-mdc-menu-item.c-sf-row{min-height:40px;padding-left:16px;padding-right:4px}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .mat-mdc-menu-item.c-sf-row .mat-mdc-menu-item-text{display:flex;align-items:center;gap:8px;min-width:0;line-height:1.4}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-lead{display:inline-flex;align-items:center;justify-content:center;font-size:18px!important;width:18px!important;height:18px!important;line-height:1}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del{flex:0 0 auto;margin-left:auto;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;color:#7a7a7a;cursor:pointer;padding:0}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del mat-icon{margin:0!important;padding:0;font-size:16px!important;width:16px!important;height:16px!important;line-height:16px!important;text-align:center;overflow:hidden}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del:hover{background:#0000000f;color:#d23535}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-empty{padding:12px 16px;font-size:13px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-divider{border:0;border-top:1px solid #e2e4e7;margin:4px 0}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-save mat-icon{margin-right:8px}\n"] }]
|
|
114
|
+
}] });
|
|
115
|
+
|
|
116
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
117
|
+
/**
|
|
118
|
+
* Right-pinned toolbar for `sd-query-bar`: AND/OR connector toggle, clear-all,
|
|
119
|
+
* saved-filters dropdown (with footer "Lưu bộ lọc hiện tại"), and the deferred
|
|
120
|
+
* Search trigger. Owns no business logic — just routes user intent back via
|
|
121
|
+
* outputs (`(logic)` model, `(clear)`, `(search)`, `(applyFilter)`).
|
|
122
|
+
*
|
|
123
|
+
* The "save current query" action lives INSIDE the saved-filters mat-menu
|
|
124
|
+
* footer (one zone for both apply + save). The dropdown trigger sits
|
|
125
|
+
* immediately before Search so the user sees one filter-cluster on the right.
|
|
126
|
+
*/
|
|
127
|
+
class SdQueryActionsBar {
|
|
128
|
+
// AND/OR toggle —
|
|
129
|
+
showLogicToggle = input(false, { transform: booleanAttribute });
|
|
130
|
+
logic = model('AND');
|
|
131
|
+
filtersCount = input(0);
|
|
132
|
+
// Saved filters —
|
|
133
|
+
showSavedFilters = input(false, { transform: booleanAttribute });
|
|
134
|
+
savedFiltersKey = input(undefined);
|
|
135
|
+
query = input({ filters: [], logic: 'AND' });
|
|
136
|
+
applyFilter = output();
|
|
137
|
+
// Clear-all —
|
|
138
|
+
showClearAll = input(true, { transform: booleanAttribute });
|
|
139
|
+
clear = output();
|
|
140
|
+
// Search trigger —
|
|
141
|
+
canSearch = input(false);
|
|
142
|
+
search = output();
|
|
143
|
+
setLogic(value) {
|
|
144
|
+
if (this.logic() === value)
|
|
145
|
+
return;
|
|
146
|
+
this.logic.set(value);
|
|
147
|
+
}
|
|
148
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryActionsBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
149
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdQueryActionsBar, isStandalone: true, selector: "sd-query-actions-bar", inputs: { showLogicToggle: { classPropertyName: "showLogicToggle", publicName: "showLogicToggle", isSignal: true, isRequired: false, transformFunction: null }, logic: { classPropertyName: "logic", publicName: "logic", isSignal: true, isRequired: false, transformFunction: null }, filtersCount: { classPropertyName: "filtersCount", publicName: "filtersCount", isSignal: true, isRequired: false, transformFunction: null }, showSavedFilters: { classPropertyName: "showSavedFilters", publicName: "showSavedFilters", isSignal: true, isRequired: false, transformFunction: null }, savedFiltersKey: { classPropertyName: "savedFiltersKey", publicName: "savedFiltersKey", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, showClearAll: { classPropertyName: "showClearAll", publicName: "showClearAll", isSignal: true, isRequired: false, transformFunction: null }, canSearch: { classPropertyName: "canSearch", publicName: "canSearch", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { logic: "logicChange", applyFilter: "applyFilter", clear: "clear", search: "search" }, ngImport: i0, template: "<!-- Vertical separator between the wrapping filter area (left) and the pinned toolbar. -->\n<span class=\"c-bar-divider\" aria-hidden=\"true\"></span>\n\n<div class=\"c-query-bar__actions\">\n <!-- AND / OR toggle -->\n @if (showLogicToggle() && filtersCount() >= 2) {\n <div class=\"c-logic-toggle\" role=\"group\" aria-label=\"Logic operator\">\n <button\n type=\"button\"\n class=\"c-logic-segment\"\n [class.c-logic-active]=\"logic() === 'AND'\"\n (click)=\"setLogic('AND')\">\n AND\n </button>\n <button\n type=\"button\"\n class=\"c-logic-segment\"\n [class.c-logic-active]=\"logic() === 'OR'\"\n (click)=\"setLogic('OR')\">\n OR\n </button>\n </div>\n }\n\n @if (showClearAll() && filtersCount() > 0) {\n <button\n type=\"button\"\n class=\"c-clear-all\"\n [matTooltip]=\"'X\u00F3a t\u1EA5t c\u1EA3 (' + filtersCount() + ')'\"\n (click)=\"clear.emit()\">\n <mat-icon fontSet=\"material-icons-outlined\">delete_sweep</mat-icon>\n </button>\n }\n\n <!-- why: saved-filters dropdown \u0111\u1EB7t s\u00E1t ngay tr\u01B0\u1EDBc Search \u2014 gom to\u00E0n b\u1ED9\n h\u00E0nh \u0111\u1ED9ng v\u1EC1 b\u1ED9 l\u1ECDc (apply view c\u0169 / l\u01B0u view hi\u1EC7n t\u1EA1i) c\u1EA1nh n\u00FAt submit,\n user th\u1EA5y 1 c\u1EE5m. Footer \"L\u01B0u b\u1ED9 l\u1ECDc hi\u1EC7n t\u1EA1i\" s\u1ED1ng trong mat-menu c\u1EE7a\n saved-filters-menu, kh\u00F4ng t\u00E1ch ra n\u00FAt ri\u00EAng n\u1EEFa. -->\n @if (showSavedFilters()) {\n <sd-query-saved-filters-menu\n [key]=\"savedFiltersKey()\"\n [query]=\"query()\"\n (apply)=\"applyFilter.emit($event)\" />\n }\n\n <!-- why: far-right \"submit at the end of the toolbar\" position; sole query trigger. -->\n <button\n type=\"button\"\n class=\"c-search-trigger\"\n [disabled]=\"!canSearch()\"\n matTooltip=\"T\u00ECm ki\u1EBFm\"\n (click)=\"search.emit()\">\n <mat-icon fontSet=\"material-icons-outlined\">search</mat-icon>\n </button>\n</div>\n", styles: [":host{display:contents}.c-bar-divider{flex:0 0 auto;align-self:stretch;width:1px;min-height:28px;background:#e2e4e7}.c-query-bar__actions{flex:0 0 auto;display:flex;align-items:center;gap:6px}.c-logic-toggle{display:inline-flex;align-items:center;padding:2px;background:#f5f6f7;border:1px solid #e2e4e7;border-radius:6px}:host-context(.c-density-compact) .c-logic-toggle{height:28px}:host-context(.c-density-comfortable) .c-logic-toggle{height:32px}.c-logic-toggle .c-logic-segment{height:100%;padding:0 10px;border:none;background:transparent;font-family:Roboto Mono,monospace;font-size:11px;font-weight:600;letter-spacing:.4px;color:#5f6368;cursor:pointer;border-radius:4px}.c-logic-toggle .c-logic-segment.c-logic-active{background:#fff;color:var(--sd-primary);box-shadow:0 1px 2px #00000014}.c-clear-all,.c-search-trigger{display:inline-flex;align-items:center;justify-content:center;border:1px solid #d6d8db;border-radius:6px;background:#fff;color:#5f6368;cursor:pointer;padding:0}:host-context(.c-density-compact) .c-clear-all,:host-context(.c-density-compact) .c-search-trigger{width:28px;height:28px}:host-context(.c-density-comfortable) .c-clear-all,:host-context(.c-density-comfortable) .c-search-trigger{width:32px;height:32px}.c-clear-all mat-icon,.c-search-trigger mat-icon{font-size:18px;width:18px;height:18px}.c-clear-all:hover:not([disabled]),.c-search-trigger:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-clear-all[disabled],.c-search-trigger[disabled]{opacity:.5;cursor:not-allowed}.c-clear-all:hover{border-color:#dc2626;color:#dc2626}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SdQuerySavedFiltersMenu, selector: "sd-query-saved-filters-menu", inputs: ["key", "query"], outputs: ["apply"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
150
|
+
}
|
|
151
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryActionsBar, decorators: [{
|
|
152
|
+
type: Component,
|
|
153
|
+
args: [{ selector: 'sd-query-actions-bar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatIconModule, MatTooltipModule, SdQuerySavedFiltersMenu], template: "<!-- Vertical separator between the wrapping filter area (left) and the pinned toolbar. -->\n<span class=\"c-bar-divider\" aria-hidden=\"true\"></span>\n\n<div class=\"c-query-bar__actions\">\n <!-- AND / OR toggle -->\n @if (showLogicToggle() && filtersCount() >= 2) {\n <div class=\"c-logic-toggle\" role=\"group\" aria-label=\"Logic operator\">\n <button\n type=\"button\"\n class=\"c-logic-segment\"\n [class.c-logic-active]=\"logic() === 'AND'\"\n (click)=\"setLogic('AND')\">\n AND\n </button>\n <button\n type=\"button\"\n class=\"c-logic-segment\"\n [class.c-logic-active]=\"logic() === 'OR'\"\n (click)=\"setLogic('OR')\">\n OR\n </button>\n </div>\n }\n\n @if (showClearAll() && filtersCount() > 0) {\n <button\n type=\"button\"\n class=\"c-clear-all\"\n [matTooltip]=\"'X\u00F3a t\u1EA5t c\u1EA3 (' + filtersCount() + ')'\"\n (click)=\"clear.emit()\">\n <mat-icon fontSet=\"material-icons-outlined\">delete_sweep</mat-icon>\n </button>\n }\n\n <!-- why: saved-filters dropdown \u0111\u1EB7t s\u00E1t ngay tr\u01B0\u1EDBc Search \u2014 gom to\u00E0n b\u1ED9\n h\u00E0nh \u0111\u1ED9ng v\u1EC1 b\u1ED9 l\u1ECDc (apply view c\u0169 / l\u01B0u view hi\u1EC7n t\u1EA1i) c\u1EA1nh n\u00FAt submit,\n user th\u1EA5y 1 c\u1EE5m. Footer \"L\u01B0u b\u1ED9 l\u1ECDc hi\u1EC7n t\u1EA1i\" s\u1ED1ng trong mat-menu c\u1EE7a\n saved-filters-menu, kh\u00F4ng t\u00E1ch ra n\u00FAt ri\u00EAng n\u1EEFa. -->\n @if (showSavedFilters()) {\n <sd-query-saved-filters-menu\n [key]=\"savedFiltersKey()\"\n [query]=\"query()\"\n (apply)=\"applyFilter.emit($event)\" />\n }\n\n <!-- why: far-right \"submit at the end of the toolbar\" position; sole query trigger. -->\n <button\n type=\"button\"\n class=\"c-search-trigger\"\n [disabled]=\"!canSearch()\"\n matTooltip=\"T\u00ECm ki\u1EBFm\"\n (click)=\"search.emit()\">\n <mat-icon fontSet=\"material-icons-outlined\">search</mat-icon>\n </button>\n</div>\n", styles: [":host{display:contents}.c-bar-divider{flex:0 0 auto;align-self:stretch;width:1px;min-height:28px;background:#e2e4e7}.c-query-bar__actions{flex:0 0 auto;display:flex;align-items:center;gap:6px}.c-logic-toggle{display:inline-flex;align-items:center;padding:2px;background:#f5f6f7;border:1px solid #e2e4e7;border-radius:6px}:host-context(.c-density-compact) .c-logic-toggle{height:28px}:host-context(.c-density-comfortable) .c-logic-toggle{height:32px}.c-logic-toggle .c-logic-segment{height:100%;padding:0 10px;border:none;background:transparent;font-family:Roboto Mono,monospace;font-size:11px;font-weight:600;letter-spacing:.4px;color:#5f6368;cursor:pointer;border-radius:4px}.c-logic-toggle .c-logic-segment.c-logic-active{background:#fff;color:var(--sd-primary);box-shadow:0 1px 2px #00000014}.c-clear-all,.c-search-trigger{display:inline-flex;align-items:center;justify-content:center;border:1px solid #d6d8db;border-radius:6px;background:#fff;color:#5f6368;cursor:pointer;padding:0}:host-context(.c-density-compact) .c-clear-all,:host-context(.c-density-compact) .c-search-trigger{width:28px;height:28px}:host-context(.c-density-comfortable) .c-clear-all,:host-context(.c-density-comfortable) .c-search-trigger{width:32px;height:32px}.c-clear-all mat-icon,.c-search-trigger mat-icon{font-size:18px;width:18px;height:18px}.c-clear-all:hover:not([disabled]),.c-search-trigger:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-clear-all[disabled],.c-search-trigger[disabled]{opacity:.5;cursor:not-allowed}.c-clear-all:hover{border-color:#dc2626;color:#dc2626}\n"] }]
|
|
154
|
+
}] });
|
|
155
|
+
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
// Constants
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
/** Full operator set per field type — used when `operators: true`. */
|
|
160
|
+
const SD_QUERY_OPERATORS_BY_TYPE = {
|
|
161
|
+
string: ['CONTAIN', 'EQUAL', 'NOT_EQUAL', 'START_WITH', 'END_WITH', 'NULL', 'NOT_NULL'],
|
|
162
|
+
number: ['EQUAL', 'NOT_EQUAL', 'GREATER_THAN', 'GREATER_OR_EQUAL', 'LESS_THAN', 'LESS_OR_EQUAL', 'BETWEEN'],
|
|
163
|
+
boolean: ['EQUAL'],
|
|
164
|
+
date: ['EQUAL', 'BETWEEN', 'GREATER_THAN', 'LESS_THAN'],
|
|
165
|
+
datetime: ['EQUAL', 'BETWEEN', 'GREATER_THAN', 'LESS_THAN'],
|
|
166
|
+
values: ['IN', 'NOT_IN', 'NULL', 'NOT_NULL'],
|
|
167
|
+
'lazy-values': ['IN', 'NOT_IN', 'NULL', 'NOT_NULL'],
|
|
168
|
+
};
|
|
169
|
+
/** Default operator when a chip is created and `field.defaultOperator` is not set. */
|
|
170
|
+
const SD_QUERY_DEFAULT_OPERATOR_BY_TYPE = {
|
|
171
|
+
string: 'CONTAIN',
|
|
172
|
+
number: 'EQUAL',
|
|
173
|
+
boolean: 'EQUAL',
|
|
174
|
+
date: 'BETWEEN',
|
|
175
|
+
datetime: 'BETWEEN',
|
|
176
|
+
values: 'IN',
|
|
177
|
+
'lazy-values': 'IN',
|
|
178
|
+
};
|
|
179
|
+
/** Icon fallback per field type when `SdQueryField.icon` is not set. */
|
|
180
|
+
const SD_QUERY_TYPE_ICON = {
|
|
181
|
+
string: 'text_fields',
|
|
182
|
+
number: 'tag',
|
|
183
|
+
boolean: 'toggle_on',
|
|
184
|
+
date: 'event',
|
|
185
|
+
datetime: 'schedule',
|
|
186
|
+
values: 'list',
|
|
187
|
+
'lazy-values': 'list',
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* Resolves the icon shown on chips and in the field picker.
|
|
191
|
+
* Priority: `field.icon` → `SD_QUERY_TYPE_ICON[field.type]` → `'tune'`.
|
|
192
|
+
*/
|
|
193
|
+
function sdQueryFieldIcon(field) {
|
|
194
|
+
return field.icon ?? SD_QUERY_TYPE_ICON[field.type] ?? 'tune';
|
|
195
|
+
}
|
|
196
|
+
/** Operators that carry no data — value section is hidden in chip popover. */
|
|
197
|
+
const SD_QUERY_NO_DATA_OPERATORS = ['NULL', 'NOT_NULL'];
|
|
198
|
+
/** Operators that carry an array payload — UI offers multi-select. */
|
|
199
|
+
const SD_QUERY_MULTI_OPERATORS = ['IN', 'NOT_IN'];
|
|
200
|
+
/** Picks the initial operator when a new chip is created for `field`. */
|
|
201
|
+
function sdQueryDefaultOperator(field) {
|
|
202
|
+
if (field.defaultOperator)
|
|
203
|
+
return field.defaultOperator;
|
|
204
|
+
return SD_QUERY_DEFAULT_OPERATOR_BY_TYPE[field.type];
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Operators offered in the chip popover for `field`:
|
|
208
|
+
* - `operators === true` → full set for the type
|
|
209
|
+
* - `operators` is an array → that array (deduped against the type set is the caller's job)
|
|
210
|
+
* - otherwise (simple mode) → just the single default operator (no real choice)
|
|
211
|
+
*/
|
|
212
|
+
function sdQueryAllowedOperators(field) {
|
|
213
|
+
if (field.operators === true)
|
|
214
|
+
return SD_QUERY_OPERATORS_BY_TYPE[field.type];
|
|
215
|
+
if (Array.isArray(field.operators) && field.operators.length > 0)
|
|
216
|
+
return field.operators;
|
|
217
|
+
return [sdQueryDefaultOperator(field)];
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Whether the operator selector should be visible. Simple mode (operators omitted/false,
|
|
221
|
+
* or an array with a single entry) hides the dropdown — the chip uses one fixed operator.
|
|
222
|
+
*/
|
|
223
|
+
function sdQueryShowOperatorSelector(field) {
|
|
224
|
+
return sdQueryAllowedOperators(field).length > 1;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
228
|
+
/**
|
|
229
|
+
* Seamless inline value chip for `string` / `number` fields (inline mode only).
|
|
230
|
+
*
|
|
231
|
+
* why: the pill itself IS the input — one border, no nested rectangle control. The
|
|
232
|
+
* label + value + remove live in three flush segments inside a single pill envelope.
|
|
233
|
+
* Mirrors refs/design_handoff_sd_query_bar "Seamless string/number chip" handoff.
|
|
234
|
+
*
|
|
235
|
+
* Commit model matches inline mode: edits write back via `(valueChange)` immediately
|
|
236
|
+
* (host folds into `filters` without emitting); the bar's Search button stays the only
|
|
237
|
+
* query trigger. Enter commits + blurs, Esc reverts + blurs, blur commits.
|
|
238
|
+
*/
|
|
239
|
+
class SdQueryInlineValueChip {
|
|
240
|
+
/** Field this chip filters by — must be kind `string` or `number`. */
|
|
241
|
+
field = input.required();
|
|
242
|
+
/** Active operator (drives BETWEEN dual-input + no-data hiding). */
|
|
243
|
+
operator = input('CONTAIN');
|
|
244
|
+
/** Current value: scalar for most operators, `{ from, to }` for BETWEEN. */
|
|
245
|
+
value = input(null);
|
|
246
|
+
density = input('compact');
|
|
247
|
+
/** Show the operator label as a small pill inside the label segment. */
|
|
248
|
+
showOperator = input(false);
|
|
249
|
+
/** Prefix for the inner input's `data-autoid`. */
|
|
250
|
+
autoId = input(undefined);
|
|
251
|
+
/** Focus the first input on creation — used for the in-progress build chip. */
|
|
252
|
+
autofocus = input(false);
|
|
253
|
+
/** Emitted on commit (Enter / blur) with the parsed next value. */
|
|
254
|
+
valueChange = output();
|
|
255
|
+
/** Emitted when the user clicks the × segment. */
|
|
256
|
+
remove = output();
|
|
257
|
+
firstInput = viewChild('firstInput');
|
|
258
|
+
focused = signal(false);
|
|
259
|
+
hasError = signal(false);
|
|
260
|
+
/** Editable drafts — single value, plus from/to for BETWEEN. Seeded from `value`. */
|
|
261
|
+
draft = signal('');
|
|
262
|
+
draftFrom = signal('');
|
|
263
|
+
draftTo = signal('');
|
|
264
|
+
isNumber = computed(() => this.field().type === 'number');
|
|
265
|
+
isBetween = computed(() => this.operator() === 'BETWEEN');
|
|
266
|
+
isNoData = computed(() => SD_QUERY_NO_DATA_OPERATORS.includes(this.operator()));
|
|
267
|
+
icon = computed(() => sdQueryFieldIcon(this.field()));
|
|
268
|
+
ph = computed(() => (this.isNumber() ? 'giá trị' : 'nhập…'));
|
|
269
|
+
phFrom = computed(() => (this.isNumber() ? 'Từ' : 'từ…'));
|
|
270
|
+
phTo = computed(() => (this.isNumber() ? 'Đến' : 'đến…'));
|
|
271
|
+
hasValue = computed(() => {
|
|
272
|
+
if (this.isNoData())
|
|
273
|
+
return true;
|
|
274
|
+
const v = this.value();
|
|
275
|
+
if (this.isBetween()) {
|
|
276
|
+
const r = (v ?? {});
|
|
277
|
+
return (r.from != null && r.from !== '') || (r.to != null && r.to !== '');
|
|
278
|
+
}
|
|
279
|
+
return v != null && v !== '';
|
|
280
|
+
});
|
|
281
|
+
state = computed(() => {
|
|
282
|
+
if (this.hasError())
|
|
283
|
+
return 'error';
|
|
284
|
+
if (this.focused())
|
|
285
|
+
return 'focus';
|
|
286
|
+
return this.hasValue() ? 'active' : 'pending';
|
|
287
|
+
});
|
|
288
|
+
constructor() {
|
|
289
|
+
// Reseed drafts whenever the bound value / operator / kind changes. While the user
|
|
290
|
+
// types, none of those change (commit is on blur), so this never clobbers an edit.
|
|
291
|
+
effect(() => {
|
|
292
|
+
const v = this.value();
|
|
293
|
+
if (this.isBetween()) {
|
|
294
|
+
const r = (v ?? {});
|
|
295
|
+
this.draftFrom.set(this.#format(r.from));
|
|
296
|
+
this.draftTo.set(this.#format(r.to));
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
this.draft.set(this.#format(v));
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
// Build chip lands focused so the user types straight away (no rectangle, no extra click).
|
|
303
|
+
afterNextRender(() => {
|
|
304
|
+
if (this.autofocus())
|
|
305
|
+
this.firstInput()?.nativeElement.focus();
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Width in chars — hugs the current value; falls back to the placeholder width only when
|
|
310
|
+
* empty. why: using placeholder.length unconditionally left a long trailing gap before the
|
|
311
|
+
* × for short values (e.g. a 1-digit number under the 'giá trị' placeholder).
|
|
312
|
+
*/
|
|
313
|
+
sizeFor(text, placeholder) {
|
|
314
|
+
const base = text && text.length > 0 ? text.length : placeholder.length;
|
|
315
|
+
return Math.max(base, 2);
|
|
316
|
+
}
|
|
317
|
+
/** Click anywhere in the pill (except × or an input) focuses the first input. */
|
|
318
|
+
focusFromShell(ev) {
|
|
319
|
+
const target = ev.target;
|
|
320
|
+
if (target.closest('.c-seamless__x') || target.closest('input'))
|
|
321
|
+
return;
|
|
322
|
+
this.firstInput()?.nativeElement.focus();
|
|
323
|
+
}
|
|
324
|
+
onFocus() {
|
|
325
|
+
this.focused.set(true);
|
|
326
|
+
}
|
|
327
|
+
/** Commit a single-value field (string / number, non-BETWEEN). */
|
|
328
|
+
commitSingle() {
|
|
329
|
+
this.focused.set(false);
|
|
330
|
+
const parsed = this.#parse(this.draft());
|
|
331
|
+
if (!parsed.ok) {
|
|
332
|
+
this.hasError.set(true);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
this.hasError.set(false);
|
|
336
|
+
this.valueChange.emit(parsed.value);
|
|
337
|
+
}
|
|
338
|
+
/** Commit a BETWEEN range — both ends parsed, emitted as `{ from, to }`. */
|
|
339
|
+
commitRange() {
|
|
340
|
+
this.focused.set(false);
|
|
341
|
+
const from = this.#parse(this.draftFrom());
|
|
342
|
+
const to = this.#parse(this.draftTo());
|
|
343
|
+
if (!from.ok || !to.ok) {
|
|
344
|
+
this.hasError.set(true);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
this.hasError.set(false);
|
|
348
|
+
this.valueChange.emit({ from: from.value, to: to.value });
|
|
349
|
+
}
|
|
350
|
+
/** Esc — revert drafts to the committed value and blur without emitting. */
|
|
351
|
+
revertAndBlur(input) {
|
|
352
|
+
const v = this.value();
|
|
353
|
+
if (this.isBetween()) {
|
|
354
|
+
const r = (v ?? {});
|
|
355
|
+
this.draftFrom.set(this.#format(r.from));
|
|
356
|
+
this.draftTo.set(this.#format(r.to));
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
this.draft.set(this.#format(v));
|
|
360
|
+
}
|
|
361
|
+
this.hasError.set(false);
|
|
362
|
+
this.focused.set(false);
|
|
363
|
+
input.blur();
|
|
364
|
+
}
|
|
365
|
+
onRemove(ev) {
|
|
366
|
+
ev.stopPropagation();
|
|
367
|
+
this.remove.emit();
|
|
368
|
+
}
|
|
369
|
+
/** Format a stored value for display — number → vi-VN grouped digits. */
|
|
370
|
+
#format(v) {
|
|
371
|
+
if (v == null || v === '')
|
|
372
|
+
return '';
|
|
373
|
+
if (this.isNumber()) {
|
|
374
|
+
const n = typeof v === 'number' ? v : Number(String(v).replace(/\./g, ''));
|
|
375
|
+
return Number.isFinite(n) ? n.toLocaleString('vi-VN') : String(v);
|
|
376
|
+
}
|
|
377
|
+
return String(v);
|
|
378
|
+
}
|
|
379
|
+
/** Parse a draft back to a stored value. Number kind rejects non-numeric input. */
|
|
380
|
+
#parse(s) {
|
|
381
|
+
const t = (s ?? '').trim();
|
|
382
|
+
if (t === '')
|
|
383
|
+
return { ok: true, value: this.isNumber() ? null : '' };
|
|
384
|
+
if (this.isNumber()) {
|
|
385
|
+
// vi-VN groups thousands with '.', so strip dots + spaces, then require a plain
|
|
386
|
+
// (optionally negative) integer — anything else (letters, stray symbols) is an error.
|
|
387
|
+
const cleaned = t.replace(/[.\s]/g, '');
|
|
388
|
+
if (!/^-?\d+$/.test(cleaned))
|
|
389
|
+
return { ok: false, value: t };
|
|
390
|
+
const n = Number(cleaned);
|
|
391
|
+
return Number.isFinite(n) ? { ok: true, value: n } : { ok: false, value: t };
|
|
392
|
+
}
|
|
393
|
+
return { ok: true, value: t };
|
|
394
|
+
}
|
|
395
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryInlineValueChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
396
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdQueryInlineValueChip, isStandalone: true, selector: "sd-query-inline-value-chip", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, operator: { classPropertyName: "operator", publicName: "operator", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, showOperator: { classPropertyName: "showOperator", publicName: "showOperator", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", remove: "remove" }, viewQueries: [{ propertyName: "firstInput", first: true, predicate: ["firstInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n Seamless string/number chip \u2014 the pill IS the input. One border, three flush\n segments: label \u00B7 input(s) \u00B7 \u00D7. State styling driven by [data-state].\n-->\n<span\n class=\"c-seamless\"\n [attr.data-state]=\"state()\"\n [class.c-seamless--compact]=\"density() === 'compact'\"\n [class.c-seamless--comfortable]=\"density() === 'comfortable'\"\n (click)=\"focusFromShell($event)\">\n\n <!-- Label segment: icon + field name (+ optional operator pill) -->\n <span class=\"c-seamless__label\">\n <mat-icon class=\"c-seamless__icon\" fontSet=\"material-icons-outlined\">{{ icon() }}</mat-icon>\n <span class=\"c-seamless__field\">{{ field().label }}</span>\n @if (showOperator() && !isNoData()) {\n <sd-operator class=\"c-seamless__op\" [operators]=\"[operator()]\" [model]=\"operator()\" [disabled]=\"true\" />\n } @else if (!isNoData()) {\n <span class=\"c-seamless__sep\">:</span>\n }\n </span>\n\n <!-- Input segment \u2014 transparent, borderless; hidden for NULL / NOT_NULL -->\n @if (!isNoData()) {\n <span class=\"c-seamless__input\">\n @if (isBetween()) {\n <input\n #firstInput\n type=\"text\"\n class=\"c-seamless__field-input\"\n [attr.data-autoid]=\"autoId() ? autoId() + '-from' : null\"\n [ngModel]=\"draftFrom()\"\n (ngModelChange)=\"draftFrom.set($event)\"\n [size]=\"sizeFor(draftFrom(), phFrom())\"\n [placeholder]=\"phFrom()\"\n (focus)=\"onFocus()\"\n (blur)=\"commitRange()\"\n (keydown.enter)=\"$any($event.target).blur()\"\n (keydown.escape)=\"revertAndBlur($any($event.target))\" />\n <span class=\"c-seamless__dash\" aria-hidden=\"true\">\u2014</span>\n <input\n type=\"text\"\n class=\"c-seamless__field-input\"\n [attr.data-autoid]=\"autoId() ? autoId() + '-to' : null\"\n [ngModel]=\"draftTo()\"\n (ngModelChange)=\"draftTo.set($event)\"\n [size]=\"sizeFor(draftTo(), phTo())\"\n [placeholder]=\"phTo()\"\n (focus)=\"onFocus()\"\n (blur)=\"commitRange()\"\n (keydown.enter)=\"$any($event.target).blur()\"\n (keydown.escape)=\"revertAndBlur($any($event.target))\" />\n } @else {\n <input\n #firstInput\n type=\"text\"\n class=\"c-seamless__field-input\"\n [attr.data-autoid]=\"autoId() ?? null\"\n [ngModel]=\"draft()\"\n (ngModelChange)=\"draft.set($event)\"\n [size]=\"sizeFor(draft(), ph())\"\n [placeholder]=\"ph()\"\n (focus)=\"onFocus()\"\n (blur)=\"commitSingle()\"\n (keydown.enter)=\"$any($event.target).blur()\"\n (keydown.escape)=\"revertAndBlur($any($event.target))\" />\n }\n </span>\n }\n\n <!-- \u00D7 segment -->\n <button type=\"button\" class=\"c-seamless__x\" aria-label=\"Remove filter\" (click)=\"onRemove($event)\">\n <mat-icon fontSet=\"material-icons-outlined\">close</mat-icon>\n </button>\n</span>\n", styles: [":host{display:inline-flex}.c-seamless{display:inline-flex;align-items:stretch;border-radius:999px;overflow:hidden;font-family:inherit;font-size:13px;line-height:1;cursor:text;border:1px solid #d6d8db;background:#fff;color:#5f6368;transition:border-color .12s ease,background-color .12s ease,box-shadow .12s ease,color .12s ease}.c-seamless--compact{height:28px}.c-seamless--comfortable{height:32px}.c-seamless:hover:not([data-state=focus]):not([data-state=error]){border-color:var(--sd-primary)}.c-seamless[data-state=pending]{border-style:dashed;border-color:#d6d8db;background:#fff;color:#5f6368}.c-seamless[data-state=pending] .c-seamless__field-input{color:#212121}.c-seamless[data-state=pending] .c-seamless__field-input::placeholder{color:#7a7a7a;font-style:italic}.c-seamless[data-state=active]{border-color:var(--sd-primary-light);background:var(--sd-primary-light)}.c-seamless[data-state=active] .c-seamless__field-input{color:var(--sd-primary)}.c-seamless[data-state=focus]{border-color:var(--sd-primary);background:#fff;box-shadow:0 0 0 3px var(--sd-primary-light)}.c-seamless[data-state=focus] .c-seamless__field-input{color:#212121}.c-seamless[data-state=error]{border-color:#d23535;background:#fff5f5;color:#d23535;box-shadow:0 0 0 3px #d235351f}.c-seamless[data-state=error] .c-seamless__field-input{color:#d23535}.c-seamless__label{display:inline-flex;align-items:center;gap:4px;font-weight:500;white-space:nowrap;cursor:text;color:inherit}.c-seamless--compact .c-seamless__label{padding:0 0 0 12px}.c-seamless--comfortable .c-seamless__label{padding:0 0 0 14px}.c-seamless__icon{font-size:14px;width:14px;height:14px;color:inherit}.c-seamless__field{color:inherit}.c-seamless__op{display:inline-flex;align-items:center;color:inherit}.c-seamless__sep{color:#7a7a7a}.c-seamless__input{display:inline-flex;align-items:center;gap:4px}.c-seamless--compact .c-seamless__input{padding:0 8px 0 4px}.c-seamless--comfortable .c-seamless__input{padding:0 10px 0 6px}.c-seamless__dash{color:#7a7a7a}.c-seamless__field-input{appearance:none;border:0;outline:0;background:transparent;font:inherit;font-weight:500;padding:0;min-width:24px;max-width:200px}.c-seamless__x{display:inline-flex;align-items:center;justify-content:center;border:0;border-left:1px solid rgba(0,0,0,.04);background:transparent;cursor:pointer;color:inherit}.c-seamless--compact .c-seamless__x{width:26px}.c-seamless--comfortable .c-seamless__x{width:28px}.c-seamless__x mat-icon{font-size:14px;width:14px;height:14px}.c-seamless__x:hover{background:#0000000d}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
397
|
+
}
|
|
398
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryInlineValueChip, decorators: [{
|
|
399
|
+
type: Component,
|
|
400
|
+
args: [{ selector: 'sd-query-inline-value-chip', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, MatIconModule, SdOperator], template: "<!--\n Seamless string/number chip \u2014 the pill IS the input. One border, three flush\n segments: label \u00B7 input(s) \u00B7 \u00D7. State styling driven by [data-state].\n-->\n<span\n class=\"c-seamless\"\n [attr.data-state]=\"state()\"\n [class.c-seamless--compact]=\"density() === 'compact'\"\n [class.c-seamless--comfortable]=\"density() === 'comfortable'\"\n (click)=\"focusFromShell($event)\">\n\n <!-- Label segment: icon + field name (+ optional operator pill) -->\n <span class=\"c-seamless__label\">\n <mat-icon class=\"c-seamless__icon\" fontSet=\"material-icons-outlined\">{{ icon() }}</mat-icon>\n <span class=\"c-seamless__field\">{{ field().label }}</span>\n @if (showOperator() && !isNoData()) {\n <sd-operator class=\"c-seamless__op\" [operators]=\"[operator()]\" [model]=\"operator()\" [disabled]=\"true\" />\n } @else if (!isNoData()) {\n <span class=\"c-seamless__sep\">:</span>\n }\n </span>\n\n <!-- Input segment \u2014 transparent, borderless; hidden for NULL / NOT_NULL -->\n @if (!isNoData()) {\n <span class=\"c-seamless__input\">\n @if (isBetween()) {\n <input\n #firstInput\n type=\"text\"\n class=\"c-seamless__field-input\"\n [attr.data-autoid]=\"autoId() ? autoId() + '-from' : null\"\n [ngModel]=\"draftFrom()\"\n (ngModelChange)=\"draftFrom.set($event)\"\n [size]=\"sizeFor(draftFrom(), phFrom())\"\n [placeholder]=\"phFrom()\"\n (focus)=\"onFocus()\"\n (blur)=\"commitRange()\"\n (keydown.enter)=\"$any($event.target).blur()\"\n (keydown.escape)=\"revertAndBlur($any($event.target))\" />\n <span class=\"c-seamless__dash\" aria-hidden=\"true\">\u2014</span>\n <input\n type=\"text\"\n class=\"c-seamless__field-input\"\n [attr.data-autoid]=\"autoId() ? autoId() + '-to' : null\"\n [ngModel]=\"draftTo()\"\n (ngModelChange)=\"draftTo.set($event)\"\n [size]=\"sizeFor(draftTo(), phTo())\"\n [placeholder]=\"phTo()\"\n (focus)=\"onFocus()\"\n (blur)=\"commitRange()\"\n (keydown.enter)=\"$any($event.target).blur()\"\n (keydown.escape)=\"revertAndBlur($any($event.target))\" />\n } @else {\n <input\n #firstInput\n type=\"text\"\n class=\"c-seamless__field-input\"\n [attr.data-autoid]=\"autoId() ?? null\"\n [ngModel]=\"draft()\"\n (ngModelChange)=\"draft.set($event)\"\n [size]=\"sizeFor(draft(), ph())\"\n [placeholder]=\"ph()\"\n (focus)=\"onFocus()\"\n (blur)=\"commitSingle()\"\n (keydown.enter)=\"$any($event.target).blur()\"\n (keydown.escape)=\"revertAndBlur($any($event.target))\" />\n }\n </span>\n }\n\n <!-- \u00D7 segment -->\n <button type=\"button\" class=\"c-seamless__x\" aria-label=\"Remove filter\" (click)=\"onRemove($event)\">\n <mat-icon fontSet=\"material-icons-outlined\">close</mat-icon>\n </button>\n</span>\n", styles: [":host{display:inline-flex}.c-seamless{display:inline-flex;align-items:stretch;border-radius:999px;overflow:hidden;font-family:inherit;font-size:13px;line-height:1;cursor:text;border:1px solid #d6d8db;background:#fff;color:#5f6368;transition:border-color .12s ease,background-color .12s ease,box-shadow .12s ease,color .12s ease}.c-seamless--compact{height:28px}.c-seamless--comfortable{height:32px}.c-seamless:hover:not([data-state=focus]):not([data-state=error]){border-color:var(--sd-primary)}.c-seamless[data-state=pending]{border-style:dashed;border-color:#d6d8db;background:#fff;color:#5f6368}.c-seamless[data-state=pending] .c-seamless__field-input{color:#212121}.c-seamless[data-state=pending] .c-seamless__field-input::placeholder{color:#7a7a7a;font-style:italic}.c-seamless[data-state=active]{border-color:var(--sd-primary-light);background:var(--sd-primary-light)}.c-seamless[data-state=active] .c-seamless__field-input{color:var(--sd-primary)}.c-seamless[data-state=focus]{border-color:var(--sd-primary);background:#fff;box-shadow:0 0 0 3px var(--sd-primary-light)}.c-seamless[data-state=focus] .c-seamless__field-input{color:#212121}.c-seamless[data-state=error]{border-color:#d23535;background:#fff5f5;color:#d23535;box-shadow:0 0 0 3px #d235351f}.c-seamless[data-state=error] .c-seamless__field-input{color:#d23535}.c-seamless__label{display:inline-flex;align-items:center;gap:4px;font-weight:500;white-space:nowrap;cursor:text;color:inherit}.c-seamless--compact .c-seamless__label{padding:0 0 0 12px}.c-seamless--comfortable .c-seamless__label{padding:0 0 0 14px}.c-seamless__icon{font-size:14px;width:14px;height:14px;color:inherit}.c-seamless__field{color:inherit}.c-seamless__op{display:inline-flex;align-items:center;color:inherit}.c-seamless__sep{color:#7a7a7a}.c-seamless__input{display:inline-flex;align-items:center;gap:4px}.c-seamless--compact .c-seamless__input{padding:0 8px 0 4px}.c-seamless--comfortable .c-seamless__input{padding:0 10px 0 6px}.c-seamless__dash{color:#7a7a7a}.c-seamless__field-input{appearance:none;border:0;outline:0;background:transparent;font:inherit;font-weight:500;padding:0;min-width:24px;max-width:200px}.c-seamless__x{display:inline-flex;align-items:center;justify-content:center;border:0;border-left:1px solid rgba(0,0,0,.04);background:transparent;cursor:pointer;color:inherit}.c-seamless--compact .c-seamless__x{width:26px}.c-seamless--comfortable .c-seamless__x{width:28px}.c-seamless__x mat-icon{font-size:14px;width:14px;height:14px}.c-seamless__x:hover{background:#0000000d}\n"] }]
|
|
401
|
+
}], ctorParameters: () => [] });
|
|
402
|
+
|
|
403
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
404
|
+
/**
|
|
405
|
+
* Visual block for the in-progress build chip (inline mode token builder).
|
|
406
|
+
*
|
|
407
|
+
* why: tách khỏi `<sd-query-bar>` để parent chỉ giữ state (`building()` signal +
|
|
408
|
+
* step transitions) — tất cả render logic của 2 step (operator picker / value
|
|
409
|
+
* picker) cộng với 7 nhánh field type sống tại đây.
|
|
410
|
+
*
|
|
411
|
+
* The chip has two visually distinct branches:
|
|
412
|
+
* - **Seamless** (`string` / `number`) — the entire pill IS an `<sd-query-inline-value-chip>`
|
|
413
|
+
* (its own border, autofocused input). The host's `.c-token-building` shell is skipped.
|
|
414
|
+
* - **Token** (`values` / `lazy-values` / `date` / `datetime` / `boolean` / other) —
|
|
415
|
+
* `.c-token.c-token-building` dashed pill with field label + operator (menu in
|
|
416
|
+
* operator step, disabled badge in value step) + the per-type picker + × cancel.
|
|
417
|
+
*/
|
|
418
|
+
class SdQueryBuildChip {
|
|
419
|
+
/** Current build state — drives every visual branch. */
|
|
420
|
+
building = input.required();
|
|
421
|
+
/** Density preset — forwarded to the seamless chip + sizes the .c-token row. */
|
|
422
|
+
density = input('compact');
|
|
423
|
+
/** Operator set offered in the operator-step menu (parent's `allowedOperatorsFor(field)`). */
|
|
424
|
+
allowedOperators = input([]);
|
|
425
|
+
/** Whether the active operator is a multi-select (`IN`/`NOT_IN`) — drives sd-select [multiple]. */
|
|
426
|
+
multiple = input(false);
|
|
427
|
+
/** Show the operator label on the seamless chip's pill (forwarded to inline-value-chip). */
|
|
428
|
+
showOperator = input(false);
|
|
429
|
+
/**
|
|
430
|
+
* Prefix for `data-autoid` on the inner picker / seamless input.
|
|
431
|
+
* why: parent originally hard-coded `'qb-build-value'` everywhere; default preserves that
|
|
432
|
+
* for backward compatibility with existing e2e selectors.
|
|
433
|
+
*/
|
|
434
|
+
autoId = input('qb-build-value');
|
|
435
|
+
// ---------------------------------------------------------------------------
|
|
436
|
+
// Outputs
|
|
437
|
+
// ---------------------------------------------------------------------------
|
|
438
|
+
/** Operator chosen from the operator-step menu — parent advances to value step. */
|
|
439
|
+
pickOperator = output();
|
|
440
|
+
/**
|
|
441
|
+
* Value committed from a non-seamless picker (sd-select / sd-date / sd-date-range /
|
|
442
|
+
* sd-datetime) or a boolean toggle. Parent pushes the completed chip and clears building.
|
|
443
|
+
*/
|
|
444
|
+
commitValue = output();
|
|
445
|
+
/** × button — abandon the build (parent clears `building` signal). */
|
|
446
|
+
cancel = output();
|
|
447
|
+
/**
|
|
448
|
+
* Commit from the seamless (string / number) branch. Parent decides empty → cancel
|
|
449
|
+
* vs push complete (see `onBuildSeamlessCommit`); this just forwards the raw value.
|
|
450
|
+
*/
|
|
451
|
+
seamlessCommit = output();
|
|
452
|
+
/** Fallback editor (boolean ng-template / other) — staged draft on every change. */
|
|
453
|
+
draftChange = output();
|
|
454
|
+
/** Fallback editor — Enter / click commits the staged draft. */
|
|
455
|
+
draftCommit = output();
|
|
456
|
+
// ---------------------------------------------------------------------------
|
|
457
|
+
// Internal refs — used by parent via public open*() methods
|
|
458
|
+
// ---------------------------------------------------------------------------
|
|
459
|
+
/** Operator-step menu — auto-opened by parent right after `building` enters operator step. */
|
|
460
|
+
opMenu = viewChild('buildOperator');
|
|
461
|
+
/** Value-step picker (the active bare control) — auto-opened by parent at value step. */
|
|
462
|
+
picker = viewChild('bPicker');
|
|
463
|
+
/** Open the operator menu — parent calls after rendering the operator step. */
|
|
464
|
+
openOperator() {
|
|
465
|
+
this.opMenu()?.open();
|
|
466
|
+
}
|
|
467
|
+
/** Open the value-step picker's native panel — parent calls after rendering the value step. */
|
|
468
|
+
openPicker() {
|
|
469
|
+
this.picker()?.open?.();
|
|
470
|
+
}
|
|
471
|
+
// ---------------------------------------------------------------------------
|
|
472
|
+
// Template helpers
|
|
473
|
+
// ---------------------------------------------------------------------------
|
|
474
|
+
/** True when the seamless branch (string/number value step) should be rendered. */
|
|
475
|
+
isSeamless = computed(() => {
|
|
476
|
+
const b = this.building();
|
|
477
|
+
if (b.step !== 'value')
|
|
478
|
+
return false;
|
|
479
|
+
const t = b.field.type;
|
|
480
|
+
return t === 'string' || t === 'number';
|
|
481
|
+
});
|
|
482
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryBuildChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
483
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdQueryBuildChip, isStandalone: true, selector: "sd-query-build-chip", inputs: { building: { classPropertyName: "building", publicName: "building", isSignal: true, isRequired: true, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, allowedOperators: { classPropertyName: "allowedOperators", publicName: "allowedOperators", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, showOperator: { classPropertyName: "showOperator", publicName: "showOperator", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pickOperator: "pickOperator", commitValue: "commitValue", cancel: "cancel", seamlessCommit: "seamlessCommit", draftChange: "draftChange", draftCommit: "draftCommit" }, viewQueries: [{ propertyName: "opMenu", first: true, predicate: ["buildOperator"], descendants: true, isSignal: true }, { propertyName: "picker", first: true, predicate: ["bPicker"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n In-progress chip (inline-mode token build flow).\n Seamless string/number branch keeps its own pill; everything else renders inside\n the dashed .c-token.c-token-building shell.\n-->\n@let _b = building();\n\n@if (isSeamless()) {\n <sd-query-inline-value-chip\n [field]=\"_b.field\"\n [operator]=\"$any(_b.operator)\"\n [value]=\"_b.value ?? null\"\n [density]=\"density()\"\n [showOperator]=\"showOperator()\"\n [autoId]=\"autoId()\"\n [autofocus]=\"true\"\n (valueChange)=\"seamlessCommit.emit($event)\"\n (remove)=\"cancel.emit()\" />\n} @else {\n <div class=\"c-token c-token-building\">\n <span class=\"c-token-field\">{{ _b.field.label }}</span>\n\n @if (_b.step === 'operator') {\n <sd-operator\n #buildOperator\n class=\"c-token-op\"\n [operators]=\"allowedOperators()\"\n [model]=\"_b.operator\"\n (modelChange)=\"pickOperator.emit($any($event))\" />\n } @else {\n <!-- why: badge readonly cho operator \u0111\u00E3 ch\u1ECDn \u2014 v\u1EABn render <sd-operator>\n \u0111\u1EC3 gi\u1EEF th\u1ECB gi\u00E1c gi\u1ED1ng chip ho\u00E0n th\u00E0nh (icon + tooltip). -->\n <sd-operator\n class=\"c-token-op\"\n [operators]=\"[$any(_b.operator)]\"\n [model]=\"$any(_b.operator)\"\n [disabled]=\"true\" />\n\n @if (_b.field.type === 'values' || _b.field.type === 'lazy-values') {\n @let _bopt = $any(_b.field).option;\n <span class=\"c-token-value c-token-value-edit\">\n <sd-select #bPicker bare size=\"sm\" minWidthPanel=\"300px\"\n [autoId]=\"autoId()\"\n [items]=\"_b.field.type === 'lazy-values' ? $any(_bopt).search : $any(_bopt.items)\"\n [valueField]=\"_bopt.valueField\"\n [displayField]=\"_bopt.displayField\"\n [multiple]=\"multiple()\"\n [model]=\"$any(_b.value)\"\n (sdChange)=\"commitValue.emit($event)\">\n <!-- why: build chip multi-select c\u0169ng c\u1EA7n \"head +N\" \u0111\u1EC3 g\u1ECDn (gi\u1ED1ng completed chip). -->\n <ng-template #sdValue let-selectedItems=\"selectedItems\">\n @if ((selectedItems?.length ?? 0) > 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_bopt.displayField] }}</span> +{{ selectedItems.length - 1 }}\n } @else if ((selectedItems?.length ?? 0) === 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_bopt.displayField] }}</span>\n }\n </ng-template>\n </sd-select>\n </span>\n } @else if (_b.field.type === 'date') {\n <span class=\"c-token-value c-token-value-edit\">\n @if (_b.operator === 'BETWEEN') {\n <!-- why: 1 sd-date-range thay 2 sd-date \u2014 g\u1ECDn h\u01A1n, h\u00E0nh vi th\u1ED1ng nh\u1EA5t v\u1EDBi completed chip. -->\n <sd-date-range #bPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [model]=\"$any(_b.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date-range>\n } @else {\n <sd-date #bPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [model]=\"$any(_b.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date>\n }\n </span>\n } @else if (_b.field.type === 'datetime') {\n <span class=\"c-token-value c-token-value-edit\">\n @if (_b.operator === 'BETWEEN') {\n <!-- why: datetime BETWEEN downgrade v\u1EC1 date-range (m\u1EA5t time precision; \u0111\u00E3 th\u1ED1ng nh\u1EA5t). -->\n <sd-date-range #bPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [model]=\"$any(_b.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date-range>\n } @else {\n <sd-datetime #bPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [model]=\"$any(_b.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-datetime>\n }\n </span>\n } @else if (_b.field.type === 'boolean') {\n <!-- why: native button thay sd-button (tr\u00E1nh mat-button baseline 36px g\u00E2y tr\u00E0n).\n Pill nh\u1ECF v\u1EBD b\u1EB1ng CSS scoped trong chip \u2014 gi\u1EEF semantic colour qua class\n .c-bool-true / .c-bool-false. -->\n <span class=\"c-token-value c-token-value-edit c-bool-toggle\">\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-true\"\n [class.c-bool-active]=\"_b.value === true\"\n [attr.data-autoId]=\"autoId() + '-true'\"\n (click)=\"commitValue.emit(true)\">\n {{ $any(_b.field).trueLabel || 'C\u00F3' }}\n </button>\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-false\"\n [class.c-bool-active]=\"_b.value === false\"\n [attr.data-autoId]=\"autoId() + '-false'\"\n (click)=\"commitValue.emit(false)\">\n {{ $any(_b.field).falseLabel || 'Kh\u00F4ng' }}\n </button>\n </span>\n } @else {\n <!-- Fallback (e.g. unforeseen type) \u2014 generic sd-input wired via draft outputs. -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-input size=\"sm\"\n [autoId]=\"autoId()\"\n [model]=\"$any(_b.value)\"\n (sdChange)=\"draftChange.emit($event)\"\n (keyupEnter)=\"draftCommit.emit()\"></sd-input>\n </span>\n }\n }\n\n <button type=\"button\" class=\"c-token-remove\" aria-label=\"Cancel\" (click)=\"cancel.emit()\">\n <mat-icon fontSet=\"material-icons-outlined\">close</mat-icon>\n </button>\n </div>\n}\n", styles: [":host{display:contents}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}:host-context(.c-density-compact) .c-token{height:28px}:host-context(.c-density-comfortable) .c-token{height:32px}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary)}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-input,.c-token-value-edit sd-input-number{display:inline-flex;max-width:140px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-token-building{border-style:dashed}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-btn{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:0;padding:0 8px;border:1px solid currentColor;border-radius:999px;background:transparent;font:inherit;font-size:11px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background .12s ease,color .12s ease}:host-context(.c-density-comfortable) .c-bool-btn{height:20px;padding:0 10px;font-size:12px}.c-bool-true{color:var(--sd-success)}.c-bool-false{color:var(--sd-warning)}.c-bool-active.c-bool-true{background:var(--sd-success);color:#fff;border-color:var(--sd-success)}.c-bool-active.c-bool-false{background:var(--sd-warning);color:#fff;border-color:var(--sd-warning)}.c-bool-btn:hover:not(.c-bool-active).c-bool-true{background:var(--sd-success-light)}.c-bool-btn:hover:not(.c-bool-active).c-bool-false{background:var(--sd-warning-light)}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}:host-context(.c-density-compact) .c-token-remove{width:26px}:host-context(.c-density-comfortable) .c-token-remove{width:28px}.c-token-remove mat-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "bare", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "bare", "viewed", "appearance", "floatLabel", "min", "max", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "bare", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: 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: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "bare", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: SdQueryInlineValueChip, selector: "sd-query-inline-value-chip", inputs: ["field", "operator", "value", "density", "showOperator", "autoId", "autofocus"], outputs: ["valueChange", "remove"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
484
|
+
}
|
|
485
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryBuildChip, decorators: [{
|
|
486
|
+
type: Component,
|
|
487
|
+
args: [{ selector: 'sd-query-build-chip', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
488
|
+
CommonModule,
|
|
489
|
+
MatIconModule,
|
|
490
|
+
SdOperator,
|
|
491
|
+
SdDate,
|
|
492
|
+
SdDateRange,
|
|
493
|
+
SdDatetime,
|
|
494
|
+
SdInput,
|
|
495
|
+
SdInputNumber,
|
|
496
|
+
SdSelect,
|
|
497
|
+
SdQueryInlineValueChip,
|
|
498
|
+
], template: "<!--\n In-progress chip (inline-mode token build flow).\n Seamless string/number branch keeps its own pill; everything else renders inside\n the dashed .c-token.c-token-building shell.\n-->\n@let _b = building();\n\n@if (isSeamless()) {\n <sd-query-inline-value-chip\n [field]=\"_b.field\"\n [operator]=\"$any(_b.operator)\"\n [value]=\"_b.value ?? null\"\n [density]=\"density()\"\n [showOperator]=\"showOperator()\"\n [autoId]=\"autoId()\"\n [autofocus]=\"true\"\n (valueChange)=\"seamlessCommit.emit($event)\"\n (remove)=\"cancel.emit()\" />\n} @else {\n <div class=\"c-token c-token-building\">\n <span class=\"c-token-field\">{{ _b.field.label }}</span>\n\n @if (_b.step === 'operator') {\n <sd-operator\n #buildOperator\n class=\"c-token-op\"\n [operators]=\"allowedOperators()\"\n [model]=\"_b.operator\"\n (modelChange)=\"pickOperator.emit($any($event))\" />\n } @else {\n <!-- why: badge readonly cho operator \u0111\u00E3 ch\u1ECDn \u2014 v\u1EABn render <sd-operator>\n \u0111\u1EC3 gi\u1EEF th\u1ECB gi\u00E1c gi\u1ED1ng chip ho\u00E0n th\u00E0nh (icon + tooltip). -->\n <sd-operator\n class=\"c-token-op\"\n [operators]=\"[$any(_b.operator)]\"\n [model]=\"$any(_b.operator)\"\n [disabled]=\"true\" />\n\n @if (_b.field.type === 'values' || _b.field.type === 'lazy-values') {\n @let _bopt = $any(_b.field).option;\n <span class=\"c-token-value c-token-value-edit\">\n <sd-select #bPicker bare size=\"sm\" minWidthPanel=\"300px\"\n [autoId]=\"autoId()\"\n [items]=\"_b.field.type === 'lazy-values' ? $any(_bopt).search : $any(_bopt.items)\"\n [valueField]=\"_bopt.valueField\"\n [displayField]=\"_bopt.displayField\"\n [multiple]=\"multiple()\"\n [model]=\"$any(_b.value)\"\n (sdChange)=\"commitValue.emit($event)\">\n <!-- why: build chip multi-select c\u0169ng c\u1EA7n \"head +N\" \u0111\u1EC3 g\u1ECDn (gi\u1ED1ng completed chip). -->\n <ng-template #sdValue let-selectedItems=\"selectedItems\">\n @if ((selectedItems?.length ?? 0) > 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_bopt.displayField] }}</span> +{{ selectedItems.length - 1 }}\n } @else if ((selectedItems?.length ?? 0) === 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_bopt.displayField] }}</span>\n }\n </ng-template>\n </sd-select>\n </span>\n } @else if (_b.field.type === 'date') {\n <span class=\"c-token-value c-token-value-edit\">\n @if (_b.operator === 'BETWEEN') {\n <!-- why: 1 sd-date-range thay 2 sd-date \u2014 g\u1ECDn h\u01A1n, h\u00E0nh vi th\u1ED1ng nh\u1EA5t v\u1EDBi completed chip. -->\n <sd-date-range #bPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [model]=\"$any(_b.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date-range>\n } @else {\n <sd-date #bPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [model]=\"$any(_b.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date>\n }\n </span>\n } @else if (_b.field.type === 'datetime') {\n <span class=\"c-token-value c-token-value-edit\">\n @if (_b.operator === 'BETWEEN') {\n <!-- why: datetime BETWEEN downgrade v\u1EC1 date-range (m\u1EA5t time precision; \u0111\u00E3 th\u1ED1ng nh\u1EA5t). -->\n <sd-date-range #bPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [model]=\"$any(_b.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date-range>\n } @else {\n <sd-datetime #bPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [model]=\"$any(_b.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-datetime>\n }\n </span>\n } @else if (_b.field.type === 'boolean') {\n <!-- why: native button thay sd-button (tr\u00E1nh mat-button baseline 36px g\u00E2y tr\u00E0n).\n Pill nh\u1ECF v\u1EBD b\u1EB1ng CSS scoped trong chip \u2014 gi\u1EEF semantic colour qua class\n .c-bool-true / .c-bool-false. -->\n <span class=\"c-token-value c-token-value-edit c-bool-toggle\">\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-true\"\n [class.c-bool-active]=\"_b.value === true\"\n [attr.data-autoId]=\"autoId() + '-true'\"\n (click)=\"commitValue.emit(true)\">\n {{ $any(_b.field).trueLabel || 'C\u00F3' }}\n </button>\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-false\"\n [class.c-bool-active]=\"_b.value === false\"\n [attr.data-autoId]=\"autoId() + '-false'\"\n (click)=\"commitValue.emit(false)\">\n {{ $any(_b.field).falseLabel || 'Kh\u00F4ng' }}\n </button>\n </span>\n } @else {\n <!-- Fallback (e.g. unforeseen type) \u2014 generic sd-input wired via draft outputs. -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-input size=\"sm\"\n [autoId]=\"autoId()\"\n [model]=\"$any(_b.value)\"\n (sdChange)=\"draftChange.emit($event)\"\n (keyupEnter)=\"draftCommit.emit()\"></sd-input>\n </span>\n }\n }\n\n <button type=\"button\" class=\"c-token-remove\" aria-label=\"Cancel\" (click)=\"cancel.emit()\">\n <mat-icon fontSet=\"material-icons-outlined\">close</mat-icon>\n </button>\n </div>\n}\n", styles: [":host{display:contents}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}:host-context(.c-density-compact) .c-token{height:28px}:host-context(.c-density-comfortable) .c-token{height:32px}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary)}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-input,.c-token-value-edit sd-input-number{display:inline-flex;max-width:140px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-token-building{border-style:dashed}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-btn{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:0;padding:0 8px;border:1px solid currentColor;border-radius:999px;background:transparent;font:inherit;font-size:11px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background .12s ease,color .12s ease}:host-context(.c-density-comfortable) .c-bool-btn{height:20px;padding:0 10px;font-size:12px}.c-bool-true{color:var(--sd-success)}.c-bool-false{color:var(--sd-warning)}.c-bool-active.c-bool-true{background:var(--sd-success);color:#fff;border-color:var(--sd-success)}.c-bool-active.c-bool-false{background:var(--sd-warning);color:#fff;border-color:var(--sd-warning)}.c-bool-btn:hover:not(.c-bool-active).c-bool-true{background:var(--sd-success-light)}.c-bool-btn:hover:not(.c-bool-active).c-bool-false{background:var(--sd-warning-light)}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}:host-context(.c-density-compact) .c-token-remove{width:26px}:host-context(.c-density-comfortable) .c-token-remove{width:28px}.c-token-remove mat-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}\n"] }]
|
|
499
|
+
}] });
|
|
500
|
+
|
|
501
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
502
|
+
/**
|
|
503
|
+
* Popover-mode chip editor — the entire `<mat-menu>` that opens when a popover-mode
|
|
504
|
+
* chip is clicked. Owns ALL operator+value staging signals and async option loading.
|
|
505
|
+
*
|
|
506
|
+
* why: tách khỏi `<sd-query-bar>` để parent chỉ giữ `editingIndex` (chip nào đang
|
|
507
|
+
* mở) + nhận `(commit)` khi popover đóng. Mỗi lần parent gọi `seed(filter, field)`
|
|
508
|
+
* trước khi mở chip popover, child reset staging signals + kích option loading.
|
|
509
|
+
*
|
|
510
|
+
* Public surface (template ref `#cp`):
|
|
511
|
+
* - `cp.menu()` — the `<mat-menu>` to feed `[menu]` of `<sd-query-popover-chip>`
|
|
512
|
+
* - `cp.seed(filter, field)` — parent reseeds staging before opening
|
|
513
|
+
* - `(commit)` output — emitted on mat-menu close with staged Filter
|
|
514
|
+
* - `(swapField)` output — emitted when the nested field switcher picks a new field
|
|
515
|
+
*/
|
|
516
|
+
class SdQueryChipPopover {
|
|
517
|
+
// ---------------------------------------------------------------------------
|
|
518
|
+
// Inputs
|
|
519
|
+
// ---------------------------------------------------------------------------
|
|
520
|
+
/** Resolved field of the currently editing chip — parent passes from `fieldByKey()`. */
|
|
521
|
+
field = input(undefined);
|
|
522
|
+
/** The chip's current Filter — used by `seed()` for initial staging values. */
|
|
523
|
+
filter = input(undefined);
|
|
524
|
+
/** Optional chip index — embedded in the composed autoIds. */
|
|
525
|
+
chipIndex = input(null);
|
|
526
|
+
/** Prefix for `data-autoId` on inner controls. */
|
|
527
|
+
autoIdBase = input('qb');
|
|
528
|
+
/** MatMenu for the field switcher — parent passes its `<sd-query-field-picker>` menu. */
|
|
529
|
+
switchPickerMenu = input(undefined);
|
|
530
|
+
// ---------------------------------------------------------------------------
|
|
531
|
+
// Outputs
|
|
532
|
+
// ---------------------------------------------------------------------------
|
|
533
|
+
/** Emitted when mat-menu closes — parent splices `next` into filters[idx]. */
|
|
534
|
+
commit = output();
|
|
535
|
+
/** Emitted when nested field-switcher picks a new field — parent calls changeFilterField. */
|
|
536
|
+
swapField = output();
|
|
537
|
+
// ---------------------------------------------------------------------------
|
|
538
|
+
// Staging signals — uncommitted edits, committed on menu close.
|
|
539
|
+
// ---------------------------------------------------------------------------
|
|
540
|
+
/** Staged operator. */
|
|
541
|
+
editingOperator = signal('EQUAL');
|
|
542
|
+
/** Staged value (shape depends on field type + operator). */
|
|
543
|
+
editingValue = signal(null);
|
|
544
|
+
/** Resolved option list for values / lazy-values. */
|
|
545
|
+
editingOptions = signal([]);
|
|
546
|
+
/** Loading flag for async option fetch. */
|
|
547
|
+
editingOptionsLoading = signal(false);
|
|
548
|
+
// ---------------------------------------------------------------------------
|
|
549
|
+
// Derived state
|
|
550
|
+
// ---------------------------------------------------------------------------
|
|
551
|
+
allowedOperators = computed(() => {
|
|
552
|
+
const f = this.field();
|
|
553
|
+
return f ? sdQueryAllowedOperators(f) : [];
|
|
554
|
+
});
|
|
555
|
+
showOperatorSelector = computed(() => {
|
|
556
|
+
const f = this.field();
|
|
557
|
+
return f ? sdQueryShowOperatorSelector(f) : false;
|
|
558
|
+
});
|
|
559
|
+
/** Whether the staged operator is multi-select (IN/NOT_IN). */
|
|
560
|
+
multiple(op) {
|
|
561
|
+
return SD_QUERY_MULTI_OPERATORS.includes(op ?? this.editingOperator());
|
|
562
|
+
}
|
|
563
|
+
/** Whether the staged operator carries no data (NULL/NOT_NULL). */
|
|
564
|
+
isNoDataOperator(op) {
|
|
565
|
+
return SD_QUERY_NO_DATA_OPERATORS.includes(op);
|
|
566
|
+
}
|
|
567
|
+
/** Compose an autoId for an inner control (`<base>-chip<idx>-<role>`). */
|
|
568
|
+
chipAutoId(role) {
|
|
569
|
+
const base = this.autoIdBase() ?? 'qb';
|
|
570
|
+
const idx = this.chipIndex();
|
|
571
|
+
return `${base}-chip${idx ?? 'x'}-${role}`;
|
|
572
|
+
}
|
|
573
|
+
iconFor = sdQueryFieldIcon;
|
|
574
|
+
// ---------------------------------------------------------------------------
|
|
575
|
+
// mat-menu instance — parent reads via `cp.menu()`
|
|
576
|
+
// ---------------------------------------------------------------------------
|
|
577
|
+
menu = viewChild('menu');
|
|
578
|
+
// ---------------------------------------------------------------------------
|
|
579
|
+
// Seed — parent calls before opening the popover so staging signals are
|
|
580
|
+
// primed from the current filter + the option list is loaded.
|
|
581
|
+
// ---------------------------------------------------------------------------
|
|
582
|
+
/** Reseed staging from a new filter/field pair — call before opening the menu. */
|
|
583
|
+
seed(filter, field) {
|
|
584
|
+
if (!filter || !field) {
|
|
585
|
+
this.editingOperator.set('EQUAL');
|
|
586
|
+
this.editingValue.set(null);
|
|
587
|
+
this.editingOptions.set([]);
|
|
588
|
+
this.editingOptionsLoading.set(false);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
this.editingOperator.set(filter.operator);
|
|
592
|
+
this.editingValue.set(filter.data ?? null);
|
|
593
|
+
if (field.type === 'values') {
|
|
594
|
+
this.#loadValuesOptions(field);
|
|
595
|
+
}
|
|
596
|
+
else if (field.type === 'lazy-values') {
|
|
597
|
+
this.#loadLazyOptions(field);
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
this.editingOptions.set([]);
|
|
601
|
+
this.editingOptionsLoading.set(false);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
// ---------------------------------------------------------------------------
|
|
605
|
+
// Operator + value mutators
|
|
606
|
+
// ---------------------------------------------------------------------------
|
|
607
|
+
/** Called on operator change in the popover header — reshapes value when family changes. */
|
|
608
|
+
onEditingOperatorChange(next) {
|
|
609
|
+
const prev = this.editingOperator();
|
|
610
|
+
this.editingOperator.set(next);
|
|
611
|
+
if (SD_QUERY_NO_DATA_OPERATORS.includes(next)) {
|
|
612
|
+
this.editingValue.set(null);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if (next === 'BETWEEN') {
|
|
616
|
+
const v = this.editingValue();
|
|
617
|
+
if (v && typeof v === 'object' && 'from' in v)
|
|
618
|
+
return;
|
|
619
|
+
this.editingValue.set({ from: null, to: null });
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
const goingToMulti = SD_QUERY_MULTI_OPERATORS.includes(next);
|
|
623
|
+
const wasMulti = SD_QUERY_MULTI_OPERATORS.includes(prev);
|
|
624
|
+
if (goingToMulti && !wasMulti) {
|
|
625
|
+
const v = this.editingValue();
|
|
626
|
+
this.editingValue.set(v == null ? [] : [v]);
|
|
627
|
+
}
|
|
628
|
+
else if (!goingToMulti && wasMulti) {
|
|
629
|
+
const v = this.editingValue();
|
|
630
|
+
this.editingValue.set(Array.isArray(v) && v.length > 0 ? v[0] : null);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
/** Single-value input change. */
|
|
634
|
+
onEditingValueInput(value) {
|
|
635
|
+
this.editingValue.set(value);
|
|
636
|
+
}
|
|
637
|
+
/** BETWEEN range — mutate only `.from`. */
|
|
638
|
+
onEditingRangeFrom(value) {
|
|
639
|
+
const v = this.editingValue() ?? {};
|
|
640
|
+
this.editingValue.set({ ...v, from: value });
|
|
641
|
+
}
|
|
642
|
+
/** BETWEEN range — mutate only `.to`. */
|
|
643
|
+
onEditingRangeTo(value) {
|
|
644
|
+
const v = this.editingValue() ?? {};
|
|
645
|
+
this.editingValue.set({ ...v, to: value });
|
|
646
|
+
}
|
|
647
|
+
/** Toggle a value in the staged multi-select array. */
|
|
648
|
+
toggleEditingMultiValue(value) {
|
|
649
|
+
const arr = Array.isArray(this.editingValue()) ? [...this.editingValue()] : [];
|
|
650
|
+
const idx = arr.indexOf(value);
|
|
651
|
+
if (idx >= 0)
|
|
652
|
+
arr.splice(idx, 1);
|
|
653
|
+
else
|
|
654
|
+
arr.push(value);
|
|
655
|
+
this.editingValue.set(arr);
|
|
656
|
+
}
|
|
657
|
+
isEditingMultiSelected(value) {
|
|
658
|
+
const v = this.editingValue();
|
|
659
|
+
return Array.isArray(v) && v.includes(value);
|
|
660
|
+
}
|
|
661
|
+
// ---------------------------------------------------------------------------
|
|
662
|
+
// Menu lifecycle — close = commit
|
|
663
|
+
// ---------------------------------------------------------------------------
|
|
664
|
+
/**
|
|
665
|
+
* mat-menu's (closed) handler — compose the next Filter from staging signals and
|
|
666
|
+
* emit (commit). Parent splices into its filters[] and nulls editingIndex.
|
|
667
|
+
*/
|
|
668
|
+
onMenuClosed() {
|
|
669
|
+
const f = this.filter();
|
|
670
|
+
if (!f)
|
|
671
|
+
return;
|
|
672
|
+
const next = {
|
|
673
|
+
field: f.field,
|
|
674
|
+
operator: this.editingOperator(),
|
|
675
|
+
data: this.editingValue(),
|
|
676
|
+
};
|
|
677
|
+
this.commit.emit(next);
|
|
678
|
+
}
|
|
679
|
+
// ---------------------------------------------------------------------------
|
|
680
|
+
// Async option loaders — same logic as the parent's private methods.
|
|
681
|
+
// ---------------------------------------------------------------------------
|
|
682
|
+
#loadValuesOptions(field) {
|
|
683
|
+
const opt = field.option;
|
|
684
|
+
const items = opt?.items;
|
|
685
|
+
if (Array.isArray(items)) {
|
|
686
|
+
this.editingOptions.set(items);
|
|
687
|
+
this.editingOptionsLoading.set(false);
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if (isSignal(items)) {
|
|
691
|
+
this.editingOptions.set(items());
|
|
692
|
+
this.editingOptionsLoading.set(false);
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
if (typeof items === 'function') {
|
|
696
|
+
this.editingOptionsLoading.set(true);
|
|
697
|
+
Promise.resolve(items())
|
|
698
|
+
.then((arr) => this.editingOptions.set(arr ?? []))
|
|
699
|
+
.finally(() => this.editingOptionsLoading.set(false));
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
#loadLazyOptions(field, search = '') {
|
|
703
|
+
const opt = field.option;
|
|
704
|
+
if (!opt?.search)
|
|
705
|
+
return;
|
|
706
|
+
this.editingOptionsLoading.set(true);
|
|
707
|
+
const result = opt.search({ type: 'SEARCH', searchText: search });
|
|
708
|
+
if (isObservable(result)) {
|
|
709
|
+
result.subscribe({
|
|
710
|
+
next: (arr) => this.editingOptions.set(arr ?? []),
|
|
711
|
+
complete: () => this.editingOptionsLoading.set(false),
|
|
712
|
+
error: () => this.editingOptionsLoading.set(false),
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
716
|
+
Promise.resolve(result)
|
|
717
|
+
.then((arr) => this.editingOptions.set(arr ?? []))
|
|
718
|
+
.finally(() => this.editingOptionsLoading.set(false));
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryChipPopover, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
722
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdQueryChipPopover, isStandalone: true, selector: "sd-query-chip-popover", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, chipIndex: { classPropertyName: "chipIndex", publicName: "chipIndex", isSignal: true, isRequired: false, transformFunction: null }, autoIdBase: { classPropertyName: "autoIdBase", publicName: "autoIdBase", isSignal: true, isRequired: false, transformFunction: null }, switchPickerMenu: { classPropertyName: "switchPickerMenu", publicName: "switchPickerMenu", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { commit: "commit", swapField: "swapField" }, viewQueries: [{ propertyName: "menu", first: true, predicate: ["menu"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n Chip popover \u2014 operator + value editor for the popover-mode chip.\n Visual layout mirrors the previous inline `<mat-menu #chipPopover>` in query-bar.\n Body branches on operator + field.type \u2014 8 paths preserved exactly.\n-->\n<mat-menu #menu=\"matMenu\" class=\"c-chip-popover\" xPosition=\"after\" yPosition=\"below\" (closed)=\"onMenuClosed()\">\n <ng-template matMenuContent>\n @let _field = field();\n @let _op = editingOperator();\n @let _val = editingValue();\n @let _options = editingOptions();\n @let _loading = editingOptionsLoading();\n @let _switchMenu = switchPickerMenu();\n\n @if (_field) {\n <div class=\"c-pop-header\" (click)=\"$event.stopPropagation()\">\n <mat-icon fontSet=\"material-icons-outlined\">{{ iconFor(_field) }}</mat-icon>\n <!-- why: nested mat-menu trigger to swap the chip's field \u2014 parent provides\n the field-picker MatMenu via [switchPickerMenu]. -->\n @if (_switchMenu) {\n <button\n type=\"button\"\n class=\"c-pop-header-field\"\n [matMenuTriggerFor]=\"_switchMenu\"\n matTooltip=\"\u0110\u1ED5i field\">\n <span>{{ _field.label }}</span>\n <mat-icon fontSet=\"material-icons-outlined\">unfold_more</mat-icon>\n </button>\n } @else {\n <span class=\"c-pop-header-field\">{{ _field.label }}</span>\n }\n <!-- Operator selector (right-aligned). Disabled when field has \u22641 operator. -->\n <sd-operator\n class=\"c-pop-header-operator\"\n [autoId]=\"chipAutoId('operator')\"\n [operators]=\"allowedOperators()\"\n [model]=\"_op\"\n [disabled]=\"!showOperatorSelector()\"\n (modelChange)=\"onEditingOperatorChange($any($event))\" />\n </div>\n\n <div class=\"c-pop-body\" (click)=\"$event.stopPropagation()\">\n @if (!isNoDataOperator(_op)) {\n @if (_op === 'BETWEEN') {\n @if (_field.type === 'date') {\n <div class=\"c-pop-between\">\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n placeholder=\"T\u1EEB\"\n [model]=\"$any(_val)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-date>\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n placeholder=\"\u0110\u1EBFn\"\n [model]=\"$any(_val)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-date>\n </div>\n } @else if (_field.type === 'datetime') {\n <div class=\"c-pop-between\">\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n placeholder=\"T\u1EEB\"\n [model]=\"$any(_val)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-datetime>\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n placeholder=\"\u0110\u1EBFn\"\n [model]=\"$any(_val)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-datetime>\n </div>\n } @else {\n <div class=\"c-pop-between\">\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n placeholder=\"T\u1EEB\"\n [model]=\"$any(_val)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-input-number>\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n placeholder=\"\u0110\u1EBFn\"\n [model]=\"$any(_val)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-input-number>\n </div>\n }\n } @else if (_field.type === 'boolean') {\n <div class=\"c-pop-boolean c-bool-toggle\">\n <sd-button\n [autoId]=\"chipAutoId('value-true')\"\n [type]=\"_val === true ? 'fill' : 'outline'\"\n color=\"success\"\n [title]=\"$any(_field).trueLabel || 'C\u00F3'\"\n (click)=\"onEditingValueInput(true)\">\n </sd-button>\n <sd-button\n [autoId]=\"chipAutoId('value-false')\"\n [type]=\"_val === false ? 'fill' : 'outline'\"\n color=\"warning\"\n [title]=\"$any(_field).falseLabel || 'Kh\u00F4ng'\"\n (click)=\"onEditingValueInput(false)\">\n </sd-button>\n </div>\n } @else if (_field.type === 'values' || _field.type === 'lazy-values') {\n @let _opt = $any(_field).option;\n <sd-select size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Ch\u1ECDn gi\u00E1 tr\u1ECB\"\n [items]=\"_options\"\n [valueField]=\"_opt.valueField\"\n [displayField]=\"_opt.displayField\"\n [multiple]=\"multiple()\"\n [model]=\"$any(_val)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-select>\n @if (_loading) {\n <div class=\"c-pop-values-loading\">\u0110ang t\u1EA3i...</div>\n }\n } @else if (_field.type === 'date') {\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Ch\u1ECDn gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_val)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-date>\n } @else if (_field.type === 'datetime') {\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Ch\u1ECDn gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_val)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-datetime>\n } @else if (_field.type === 'number') {\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Nh\u1EADp gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_val)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-input-number>\n } @else {\n <sd-input size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Nh\u1EADp gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_val)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-input>\n }\n }\n </div>\n }\n </ng-template>\n</mat-menu>\n", styles: [":host{display:contents}::ng-deep .mat-mdc-menu-panel.c-chip-popover{min-width:280px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e2e4e7;font-size:13px;font-weight:500;color:#212121}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header mat-icon{font-size:16px;width:16px;height:16px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header .c-pop-header-operator{margin-left:auto}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 6px;margin-left:-4px;background:transparent;border:1px solid transparent;border-radius:4px;font:inherit;font-weight:500;color:#212121;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field mat-icon{font-size:14px!important;width:14px!important;height:14px!important;color:#7a7a7a!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field:hover{background:#f5f6f7;border-color:#e2e4e7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-body{padding:10px 12px;display:flex;flex-direction:column;gap:12px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-between{display:grid;grid-template-columns:1fr 1fr;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean{display:flex;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button{flex:1;height:32px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font:inherit;font-size:13px;cursor:pointer}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-loading{padding:10px 8px;font-size:12px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle{display:inline-flex;align-items:center;gap:4px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle .mdc-button,::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-button],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-stroked-button],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-flat-button]{border-radius:999px;min-width:0;padding:0 12px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "directive", type: i1.MatMenuContent, selector: "ng-template[matMenuContent]" }, { kind: "directive", type: i1.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: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "bare", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: 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: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "bare", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "bare", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
723
|
+
}
|
|
724
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryChipPopover, decorators: [{
|
|
725
|
+
type: Component,
|
|
726
|
+
args: [{ selector: 'sd-query-chip-popover', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
727
|
+
CommonModule,
|
|
728
|
+
FormsModule,
|
|
729
|
+
MatIconModule,
|
|
730
|
+
MatMenuModule,
|
|
731
|
+
MatTooltipModule,
|
|
732
|
+
SdButton,
|
|
733
|
+
SdOperator,
|
|
734
|
+
SdSelect,
|
|
735
|
+
SdInput,
|
|
736
|
+
SdInputNumber,
|
|
737
|
+
SdDate,
|
|
738
|
+
SdDatetime,
|
|
739
|
+
], template: "<!--\n Chip popover \u2014 operator + value editor for the popover-mode chip.\n Visual layout mirrors the previous inline `<mat-menu #chipPopover>` in query-bar.\n Body branches on operator + field.type \u2014 8 paths preserved exactly.\n-->\n<mat-menu #menu=\"matMenu\" class=\"c-chip-popover\" xPosition=\"after\" yPosition=\"below\" (closed)=\"onMenuClosed()\">\n <ng-template matMenuContent>\n @let _field = field();\n @let _op = editingOperator();\n @let _val = editingValue();\n @let _options = editingOptions();\n @let _loading = editingOptionsLoading();\n @let _switchMenu = switchPickerMenu();\n\n @if (_field) {\n <div class=\"c-pop-header\" (click)=\"$event.stopPropagation()\">\n <mat-icon fontSet=\"material-icons-outlined\">{{ iconFor(_field) }}</mat-icon>\n <!-- why: nested mat-menu trigger to swap the chip's field \u2014 parent provides\n the field-picker MatMenu via [switchPickerMenu]. -->\n @if (_switchMenu) {\n <button\n type=\"button\"\n class=\"c-pop-header-field\"\n [matMenuTriggerFor]=\"_switchMenu\"\n matTooltip=\"\u0110\u1ED5i field\">\n <span>{{ _field.label }}</span>\n <mat-icon fontSet=\"material-icons-outlined\">unfold_more</mat-icon>\n </button>\n } @else {\n <span class=\"c-pop-header-field\">{{ _field.label }}</span>\n }\n <!-- Operator selector (right-aligned). Disabled when field has \u22641 operator. -->\n <sd-operator\n class=\"c-pop-header-operator\"\n [autoId]=\"chipAutoId('operator')\"\n [operators]=\"allowedOperators()\"\n [model]=\"_op\"\n [disabled]=\"!showOperatorSelector()\"\n (modelChange)=\"onEditingOperatorChange($any($event))\" />\n </div>\n\n <div class=\"c-pop-body\" (click)=\"$event.stopPropagation()\">\n @if (!isNoDataOperator(_op)) {\n @if (_op === 'BETWEEN') {\n @if (_field.type === 'date') {\n <div class=\"c-pop-between\">\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n placeholder=\"T\u1EEB\"\n [model]=\"$any(_val)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-date>\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n placeholder=\"\u0110\u1EBFn\"\n [model]=\"$any(_val)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-date>\n </div>\n } @else if (_field.type === 'datetime') {\n <div class=\"c-pop-between\">\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n placeholder=\"T\u1EEB\"\n [model]=\"$any(_val)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-datetime>\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n placeholder=\"\u0110\u1EBFn\"\n [model]=\"$any(_val)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-datetime>\n </div>\n } @else {\n <div class=\"c-pop-between\">\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n placeholder=\"T\u1EEB\"\n [model]=\"$any(_val)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-input-number>\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n placeholder=\"\u0110\u1EBFn\"\n [model]=\"$any(_val)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-input-number>\n </div>\n }\n } @else if (_field.type === 'boolean') {\n <div class=\"c-pop-boolean c-bool-toggle\">\n <sd-button\n [autoId]=\"chipAutoId('value-true')\"\n [type]=\"_val === true ? 'fill' : 'outline'\"\n color=\"success\"\n [title]=\"$any(_field).trueLabel || 'C\u00F3'\"\n (click)=\"onEditingValueInput(true)\">\n </sd-button>\n <sd-button\n [autoId]=\"chipAutoId('value-false')\"\n [type]=\"_val === false ? 'fill' : 'outline'\"\n color=\"warning\"\n [title]=\"$any(_field).falseLabel || 'Kh\u00F4ng'\"\n (click)=\"onEditingValueInput(false)\">\n </sd-button>\n </div>\n } @else if (_field.type === 'values' || _field.type === 'lazy-values') {\n @let _opt = $any(_field).option;\n <sd-select size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Ch\u1ECDn gi\u00E1 tr\u1ECB\"\n [items]=\"_options\"\n [valueField]=\"_opt.valueField\"\n [displayField]=\"_opt.displayField\"\n [multiple]=\"multiple()\"\n [model]=\"$any(_val)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-select>\n @if (_loading) {\n <div class=\"c-pop-values-loading\">\u0110ang t\u1EA3i...</div>\n }\n } @else if (_field.type === 'date') {\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Ch\u1ECDn gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_val)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-date>\n } @else if (_field.type === 'datetime') {\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Ch\u1ECDn gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_val)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-datetime>\n } @else if (_field.type === 'number') {\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Nh\u1EADp gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_val)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-input-number>\n } @else {\n <sd-input size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Nh\u1EADp gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_val)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-input>\n }\n }\n </div>\n }\n </ng-template>\n</mat-menu>\n", styles: [":host{display:contents}::ng-deep .mat-mdc-menu-panel.c-chip-popover{min-width:280px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e2e4e7;font-size:13px;font-weight:500;color:#212121}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header mat-icon{font-size:16px;width:16px;height:16px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header .c-pop-header-operator{margin-left:auto}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 6px;margin-left:-4px;background:transparent;border:1px solid transparent;border-radius:4px;font:inherit;font-weight:500;color:#212121;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field mat-icon{font-size:14px!important;width:14px!important;height:14px!important;color:#7a7a7a!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field:hover{background:#f5f6f7;border-color:#e2e4e7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-body{padding:10px 12px;display:flex;flex-direction:column;gap:12px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-between{display:grid;grid-template-columns:1fr 1fr;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean{display:flex;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button{flex:1;height:32px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font:inherit;font-size:13px;cursor:pointer}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-loading{padding:10px 8px;font-size:12px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle{display:inline-flex;align-items:center;gap:4px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle .mdc-button,::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-button],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-stroked-button],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-flat-button]{border-radius:999px;min-width:0;padding:0 12px}\n"] }]
|
|
740
|
+
}] });
|
|
741
|
+
|
|
742
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
743
|
+
/**
|
|
744
|
+
* Field picker mat-menu for `sd-query-bar`.
|
|
745
|
+
*
|
|
746
|
+
* Shared between two flows in the bar:
|
|
747
|
+
* - "Add filter" — pass `[usedKeys]` (a Set of field keys already on the bar) so
|
|
748
|
+
* those entries render disabled with a check.
|
|
749
|
+
* - "Đổi field" inside the chip popover — pass `[currentKey]` to disable the
|
|
750
|
+
* currently-bound field.
|
|
751
|
+
*
|
|
752
|
+
* Parent triggers via `[matMenuTriggerFor]="picker.menu()"` and receives the
|
|
753
|
+
* chosen field through `(pick)`.
|
|
754
|
+
*/
|
|
755
|
+
class SdQueryFieldPicker {
|
|
756
|
+
/** Field list shown in the menu (caller-supplied; rendered in input order). */
|
|
757
|
+
fields = input([]);
|
|
758
|
+
/** Single key disabled with a trailing check — used in the chip-popover "Đổi field" flow. */
|
|
759
|
+
currentKey = input(undefined);
|
|
760
|
+
/** Set of keys already in use — used in the "Add filter" flow to grey out duplicates. */
|
|
761
|
+
usedKeys = input(new Set());
|
|
762
|
+
/** Emits when the user picks an enabled field. */
|
|
763
|
+
pick = output();
|
|
764
|
+
/** MatMenu instance — parent binds `[matMenuTriggerFor]="picker.menu()"`. */
|
|
765
|
+
menu = viewChild.required(MatMenu);
|
|
766
|
+
iconFor = sdQueryFieldIcon;
|
|
767
|
+
/** Per-field disabled state — current key OR used key OR field's own disabled flag. */
|
|
768
|
+
disabledMap = computed(() => {
|
|
769
|
+
const set = new Set(this.usedKeys());
|
|
770
|
+
const cur = this.currentKey();
|
|
771
|
+
if (cur)
|
|
772
|
+
set.add(cur);
|
|
773
|
+
return set;
|
|
774
|
+
});
|
|
775
|
+
isDisabled(field) {
|
|
776
|
+
return field.disabled === true || this.disabledMap().has(field.key);
|
|
777
|
+
}
|
|
778
|
+
/** "Current" check icon — only when the field matches `currentKey`. */
|
|
779
|
+
isCurrent(field) {
|
|
780
|
+
return field.key === this.currentKey();
|
|
781
|
+
}
|
|
782
|
+
/** "Used" check icon — when the field is in `usedKeys` (not the same as current). */
|
|
783
|
+
isUsed(field) {
|
|
784
|
+
return this.usedKeys().has(field.key);
|
|
785
|
+
}
|
|
786
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryFieldPicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
787
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdQueryFieldPicker, isStandalone: true, selector: "sd-query-field-picker", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null }, currentKey: { classPropertyName: "currentKey", publicName: "currentKey", isSignal: true, isRequired: false, transformFunction: null }, usedKeys: { classPropertyName: "usedKeys", publicName: "usedKeys", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pick: "pick" }, viewQueries: [{ propertyName: "menu", first: true, predicate: MatMenu, descendants: true, isSignal: true }], ngImport: i0, template: "<mat-menu #m=\"matMenu\" xPosition=\"after\" yPosition=\"below\" class=\"c-field-picker-menu\">\n @for (field of fields(); track $index) {\n <button\n type=\"button\"\n mat-menu-item\n [disabled]=\"isDisabled(field)\"\n (click)=\"pick.emit(field)\">\n <mat-icon fontSet=\"material-icons-outlined\" class=\"c-field-item-icon\">\n {{ iconFor(field) }}\n </mat-icon>\n <span class=\"c-field-item-label\">{{ field.label }}</span>\n <span class=\"c-field-item-type\">{{ field.type }}</span>\n @if (isCurrent(field) || isUsed(field)) {\n <mat-icon class=\"c-field-item-check\" fontSet=\"material-icons-outlined\">check</mat-icon>\n }\n </button>\n } @empty {\n <div class=\"c-field-empty\">Ch\u01B0a c\u00F3 field n\u00E0o \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh.</div>\n }\n</mat-menu>\n", styles: ["::ng-deep .mat-mdc-menu-panel.c-field-picker-menu{min-width:200px;max-width:260px}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-menu-item{min-height:36px}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-menu-item .mat-mdc-menu-item-text{display:flex;align-items:center;flex:1;min-width:0}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-icon{font-size:16px;width:16px;height:16px;margin-right:8px;color:#5f6368;flex-shrink:0}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-label{flex:1 1 auto;min-width:0;font-size:13px;color:#212121;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-type{flex-shrink:0;margin-left:auto;padding-left:12px;font-family:Roboto Mono,monospace;font-size:11px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-check{flex-shrink:0;font-size:14px;width:14px;height:14px;margin-left:6px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-empty{padding:12px 16px;font-size:13px;color:#7a7a7a}\n"], dependencies: [{ kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1.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: i1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
788
|
+
}
|
|
789
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryFieldPicker, decorators: [{
|
|
790
|
+
type: Component,
|
|
791
|
+
args: [{ selector: 'sd-query-field-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatMenuModule, MatIconModule], template: "<mat-menu #m=\"matMenu\" xPosition=\"after\" yPosition=\"below\" class=\"c-field-picker-menu\">\n @for (field of fields(); track $index) {\n <button\n type=\"button\"\n mat-menu-item\n [disabled]=\"isDisabled(field)\"\n (click)=\"pick.emit(field)\">\n <mat-icon fontSet=\"material-icons-outlined\" class=\"c-field-item-icon\">\n {{ iconFor(field) }}\n </mat-icon>\n <span class=\"c-field-item-label\">{{ field.label }}</span>\n <span class=\"c-field-item-type\">{{ field.type }}</span>\n @if (isCurrent(field) || isUsed(field)) {\n <mat-icon class=\"c-field-item-check\" fontSet=\"material-icons-outlined\">check</mat-icon>\n }\n </button>\n } @empty {\n <div class=\"c-field-empty\">Ch\u01B0a c\u00F3 field n\u00E0o \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh.</div>\n }\n</mat-menu>\n", styles: ["::ng-deep .mat-mdc-menu-panel.c-field-picker-menu{min-width:200px;max-width:260px}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-menu-item{min-height:36px}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-menu-item .mat-mdc-menu-item-text{display:flex;align-items:center;flex:1;min-width:0}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-icon{font-size:16px;width:16px;height:16px;margin-right:8px;color:#5f6368;flex-shrink:0}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-label{flex:1 1 auto;min-width:0;font-size:13px;color:#212121;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-type{flex-shrink:0;margin-left:auto;padding-left:12px;font-family:Roboto Mono,monospace;font-size:11px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-check{flex-shrink:0;font-size:14px;width:14px;height:14px;margin-left:6px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-empty{padding:12px 16px;font-size:13px;color:#7a7a7a}\n"] }]
|
|
792
|
+
}] });
|
|
793
|
+
|
|
794
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
795
|
+
/**
|
|
796
|
+
* Completed inline chip (non-string/number — those use `<sd-query-inline-value-chip>`).
|
|
797
|
+
*
|
|
798
|
+
* why: tách edit lifecycle ra khỏi parent `<sd-query-bar>` — child tự quản lý
|
|
799
|
+
* `isEditing` signal, auto-open picker, focusout exit. Parent chỉ pass field/filter
|
|
800
|
+
* và nhận commit/remove outputs.
|
|
801
|
+
*
|
|
802
|
+
* Covers types: boolean, date, datetime, values, lazy-values, plus a fallback else
|
|
803
|
+
* branch. BETWEEN (date / datetime) uses a single `<sd-date-range>` (datetime
|
|
804
|
+
* downgrades to date precision).
|
|
805
|
+
*/
|
|
806
|
+
class SdQueryInlineChip {
|
|
807
|
+
#injector = inject(Injector);
|
|
808
|
+
#cdr = inject(ChangeDetectorRef);
|
|
809
|
+
// ---------------------------------------------------------------------------
|
|
810
|
+
// Inputs
|
|
811
|
+
// ---------------------------------------------------------------------------
|
|
812
|
+
/** Resolved field — parent passes it (no lookup needed inside). */
|
|
813
|
+
field = input.required();
|
|
814
|
+
/** The chip's Filter — used for `data` + `operator`. */
|
|
815
|
+
filter = input.required();
|
|
816
|
+
/** Density preset — sizes the `.c-token` row. */
|
|
817
|
+
density = input('compact');
|
|
818
|
+
/** Whether the active operator is a multi-select (IN/NOT_IN). */
|
|
819
|
+
multiple = input(false);
|
|
820
|
+
/** Show `<sd-operator>` (disabled badge) on the chip; otherwise show `':'` separator. */
|
|
821
|
+
showOperator = input(false);
|
|
822
|
+
/** autoId prefix for inner controls (parent passes `inlineAutoId(i, 'value')`). */
|
|
823
|
+
autoId = input('');
|
|
824
|
+
/** Pre-computed display text — parent owns `chipValueText(filter)` so we don't recompute. */
|
|
825
|
+
valueText = input('');
|
|
826
|
+
/** No-data operator (NULL/NOT_NULL) — hide entire value slot when true. */
|
|
827
|
+
isNoData = input(false);
|
|
828
|
+
// ---------------------------------------------------------------------------
|
|
829
|
+
// Outputs
|
|
830
|
+
// ---------------------------------------------------------------------------
|
|
831
|
+
/** Single-value commit — parent runs `updateFilter(i, { data })`. */
|
|
832
|
+
commit = output();
|
|
833
|
+
/** BETWEEN commit (date/datetime) — parent runs `setFilterRange(i, ev)`. */
|
|
834
|
+
commitRange = output();
|
|
835
|
+
/** Live edit during multi-select — parent runs `editValueFn(i)(v)`. */
|
|
836
|
+
liveChange = output();
|
|
837
|
+
/** × removal. */
|
|
838
|
+
remove = output();
|
|
839
|
+
// ---------------------------------------------------------------------------
|
|
840
|
+
// Internal state
|
|
841
|
+
// ---------------------------------------------------------------------------
|
|
842
|
+
/** Edit toggle — viewed (false) ↔ editable (true). */
|
|
843
|
+
#editing = signal(false);
|
|
844
|
+
editing = this.#editing.asReadonly();
|
|
845
|
+
/** Active picker reference — auto-opened right after entering edit. */
|
|
846
|
+
chipPicker = viewChild('chipPicker');
|
|
847
|
+
// ---------------------------------------------------------------------------
|
|
848
|
+
// Template helpers
|
|
849
|
+
// ---------------------------------------------------------------------------
|
|
850
|
+
iconName = computed(() => sdQueryFieldIcon(this.field()));
|
|
851
|
+
/** Operator code — convenience accessor for template. */
|
|
852
|
+
operator = computed(() => this.filter().operator);
|
|
853
|
+
/** Data payload — convenience accessor for template. */
|
|
854
|
+
data = computed(() => this.filter().data);
|
|
855
|
+
// ---------------------------------------------------------------------------
|
|
856
|
+
// Edit lifecycle
|
|
857
|
+
// ---------------------------------------------------------------------------
|
|
858
|
+
/** Enter edit mode + auto-open the picker on next render. */
|
|
859
|
+
enterEdit() {
|
|
860
|
+
if (this.#editing())
|
|
861
|
+
return;
|
|
862
|
+
this.#editing.set(true);
|
|
863
|
+
afterNextRender(() => this.chipPicker()?.open?.(), { injector: this.#injector });
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Exit edit only when focus actually leaves the wrapper subtree.
|
|
867
|
+
* why: focusout fires for every internal blur — only exit when relatedTarget
|
|
868
|
+
* is OUTSIDE the wrapper AND không thuộc cdk-overlay (mat-select panel, mat-calendar,
|
|
869
|
+
* datetime overlay đều render trong document.body). Tick option trong panel multi-
|
|
870
|
+
* select sẽ chuyển focus sang `<mat-option>` (nằm trong cdk-overlay-container) →
|
|
871
|
+
* nếu exit ngay sẽ flip viewed → kill panel sớm. Chỉ exit khi panel đã close
|
|
872
|
+
* (relatedTarget không nằm trong overlay). markForCheck vì overlay focusout
|
|
873
|
+
* không kéo theo CD cho OnPush parent.
|
|
874
|
+
*/
|
|
875
|
+
onFocusOut(ev) {
|
|
876
|
+
const wrapper = ev.currentTarget;
|
|
877
|
+
const next = ev.relatedTarget;
|
|
878
|
+
if (!wrapper)
|
|
879
|
+
return;
|
|
880
|
+
if (next && wrapper.contains(next))
|
|
881
|
+
return;
|
|
882
|
+
if (next instanceof Element && next.closest('.cdk-overlay-container'))
|
|
883
|
+
return;
|
|
884
|
+
if (this.#editing()) {
|
|
885
|
+
this.#editing.set(false);
|
|
886
|
+
this.#cdr.markForCheck();
|
|
887
|
+
// why: sd-date-range / sd-datetime đôi khi emit sdChange NGAY SAU focusout —
|
|
888
|
+
// viewed flip xảy ra trước khi model mới về tới [model] input → viewed text
|
|
889
|
+
// format dùng giá trị cũ. Schedule thêm 1 CD pass để re-render với model mới
|
|
890
|
+
// (user-visible bug: "click ra lần đầu chưa update, click vào rồi ra lần 2 mới đúng").
|
|
891
|
+
queueMicrotask(() => this.#cdr.markForCheck());
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
/** Test-only shim — direct call avoids dispatching real FocusEvent in JSDOM. */
|
|
895
|
+
onFocusOutForTest(ev) {
|
|
896
|
+
this.onFocusOut(ev);
|
|
897
|
+
}
|
|
898
|
+
// ---------------------------------------------------------------------------
|
|
899
|
+
// Commit handlers — single emit per category
|
|
900
|
+
// ---------------------------------------------------------------------------
|
|
901
|
+
/** Single-value commit (sd-date, sd-datetime, single sd-select). */
|
|
902
|
+
emitSingleCommit(v) {
|
|
903
|
+
this.commit.emit(v);
|
|
904
|
+
this.#editing.set(false);
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* BETWEEN commit — sd-date-range emits {from, to}.
|
|
908
|
+
* why: nếu cả from + to đều có giá trị → coi như range đã chọn xong → exit edit
|
|
909
|
+
* ngay tại commit (đồng bộ với model.set của parent), không chờ focusout. Tránh
|
|
910
|
+
* race "focusout firing trước sdChange" làm viewed text render với model cũ.
|
|
911
|
+
*/
|
|
912
|
+
emitRangeCommit(ev) {
|
|
913
|
+
this.commitRange.emit(ev ?? null);
|
|
914
|
+
if (ev && ev.from != null && ev.to != null) {
|
|
915
|
+
this.#editing.set(false);
|
|
916
|
+
this.#cdr.markForCheck();
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
/** Multi-select live emit — does NOT exit edit (focusout handles exit). */
|
|
920
|
+
emitLive(v) {
|
|
921
|
+
this.liveChange.emit(v);
|
|
922
|
+
}
|
|
923
|
+
/** Boolean toggle commit — exits edit. */
|
|
924
|
+
emitBoolean(v) {
|
|
925
|
+
this.commit.emit(v);
|
|
926
|
+
this.#editing.set(false);
|
|
927
|
+
}
|
|
928
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryInlineChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
929
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdQueryInlineChip, isStandalone: true, selector: "sd-query-inline-chip", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, showOperator: { classPropertyName: "showOperator", publicName: "showOperator", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, valueText: { classPropertyName: "valueText", publicName: "valueText", isSignal: true, isRequired: false, transformFunction: null }, isNoData: { classPropertyName: "isNoData", publicName: "isNoData", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { commit: "commit", commitRange: "commitRange", liveChange: "liveChange", remove: "remove" }, viewQueries: [{ propertyName: "chipPicker", first: true, predicate: ["chipPicker"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n Completed inline chip \u2014 non-string/number/seamless branches.\n Edit lifecycle is self-managed: click enters edit + auto-opens picker,\n focusout outside the wrapper exits edit, picker emits commit single OR\n liveChange (multi) OR commitRange (BETWEEN).\n-->\n@let _f = field();\n@let _op = operator();\n@let _data = data();\n@let _multi = multiple();\n@let _editing = editing();\n@let _isNoData = isNoData();\n\n<div class=\"c-token\" [class.c-token-editing]=\"_editing\">\n <mat-icon class=\"c-token-icon\" fontSet=\"material-icons-outlined\">{{ iconName() }}</mat-icon>\n <span class=\"c-token-field\">{{ _f.label }}</span>\n\n @if (!_isNoData && showOperator()) {\n <sd-operator class=\"c-token-op\" [operators]=\"[_op]\" [model]=\"_op\" [disabled]=\"true\" />\n } @else if (!_isNoData) {\n <span class=\"c-token-sep\">:</span>\n }\n\n @if (!_isNoData) {\n @if (_f.type === 'boolean') {\n @if (_editing) {\n <!-- why: native button thay sd-button \u2014 mat-button MDC baseline 36px bu\u1ED9c\n ph\u1EA3i hack height/padding overrides; chip 28/32px nh\u1ECF h\u01A1n nhi\u1EC1u. T\u1EF1 v\u1EBD\n pill nh\u1ECF v\u1EDBi CSS scoped trong chip, kh\u00F4ng qua MDC, g\u1ECDn + chu\u1EA9n baseline. -->\n <span class=\"c-token-value c-token-value-edit c-bool-toggle\">\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-true\"\n [class.c-bool-active]=\"_data === true\"\n [attr.data-autoId]=\"autoId() + '-true'\"\n (click)=\"emitBoolean(true)\">\n {{ $any(_f).trueLabel || 'C\u00F3' }}\n </button>\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-false\"\n [class.c-bool-active]=\"_data === false\"\n [attr.data-autoId]=\"autoId() + '-false'\"\n (click)=\"emitBoolean(false)\">\n {{ $any(_f).falseLabel || 'Kh\u00F4ng' }}\n </button>\n </span>\n } @else {\n <button type=\"button\" class=\"c-token-value\" (click)=\"enterEdit()\">{{ valueText() }}</button>\n }\n } @else if ((_f.type === 'date' || _f.type === 'datetime') && _op === 'BETWEEN') {\n <!-- why: d\u00F9ng sd-date-range th\u1ED1ng nh\u1EA5t cho c\u1EA3 date + datetime BETWEEN (datetime downgrade v\u1EC1 date). -->\n <span\n class=\"c-token-value c-token-value-edit\"\n (click)=\"enterEdit()\"\n (focusout)=\"onFocusOut($event)\">\n <sd-date-range #chipPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [viewed]=\"!_editing\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitRangeCommit($any($event))\"></sd-date-range>\n </span>\n } @else if (_f.type === 'values' || _f.type === 'lazy-values') {\n @let _opt = $any(_f).option;\n <span\n class=\"c-token-value c-token-value-edit\"\n (click)=\"enterEdit()\"\n (focusout)=\"onFocusOut($event)\">\n <sd-select #chipPicker bare size=\"sm\" minWidthPanel=\"300px\"\n [autoId]=\"autoId()\"\n [items]=\"_f.type === 'lazy-values' ? $any(_opt).search : $any(_opt.items)\"\n [valueField]=\"_opt.valueField\"\n [displayField]=\"_opt.displayField\"\n [multiple]=\"_multi\"\n [viewed]=\"!_editing\"\n [model]=\"$any(_data)\"\n (sdChange)=\"_multi ? emitLive($event) : emitSingleCommit($event)\">\n <!-- why: viewed mode m\u1EB7c \u0111\u1ECBnh d\u00F9ng comma-joined cho multi \u2014 qu\u00E1 d\u00E0i cho chip.\n Override b\u1EB1ng template \"head +N\" gi\u1ED1ng popover chipValueText. -->\n <ng-template #sdValue let-selectedItems=\"selectedItems\">\n @if ((selectedItems?.length ?? 0) > 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_opt.displayField] }}</span> +{{ selectedItems.length - 1 }}\n } @else if ((selectedItems?.length ?? 0) === 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_opt.displayField] }}</span>\n }\n </ng-template>\n </sd-select>\n </span>\n } @else if (_f.type === 'date') {\n <span\n class=\"c-token-value c-token-value-edit\"\n (click)=\"enterEdit()\"\n (focusout)=\"onFocusOut($event)\">\n <sd-date #chipPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [viewed]=\"!_editing\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitSingleCommit($any($event))\"></sd-date>\n </span>\n } @else if (_f.type === 'datetime') {\n <span\n class=\"c-token-value c-token-value-edit\"\n (click)=\"enterEdit()\"\n (focusout)=\"onFocusOut($event)\">\n <sd-datetime #chipPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [viewed]=\"!_editing\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitSingleCommit($any($event))\"></sd-datetime>\n </span>\n } @else {\n <!-- Fallback (unforeseen type) \u2014 read-only display via valueText. -->\n <span class=\"c-token-value\">{{ valueText() }}</span>\n }\n }\n\n <button type=\"button\" class=\"c-token-remove\" aria-label=\"Remove filter\" (click)=\"remove.emit()\">\n <mat-icon fontSet=\"material-icons-outlined\">close</mat-icon>\n </button>\n</div>\n", styles: [":host{display:contents}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}:host-context(.c-density-compact) .c-token{height:28px}:host-context(.c-density-comfortable) .c-token{height:32px}.c-token-icon{font-size:14px;width:14px;height:14px;line-height:14px;color:inherit}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-sep{color:#7a7a7a}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary);cursor:pointer}.c-token-value:hover{text-decoration:underline}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-btn{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:0;padding:0 8px;border:1px solid currentColor;border-radius:999px;background:transparent;font:inherit;font-size:11px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background .12s ease,color .12s ease}:host-context(.c-density-comfortable) .c-bool-btn{height:20px;padding:0 10px;font-size:12px}.c-bool-true{color:var(--sd-success)}.c-bool-false{color:var(--sd-warning)}.c-bool-active.c-bool-true{background:var(--sd-success);color:#fff;border-color:var(--sd-success)}.c-bool-active.c-bool-false{background:var(--sd-warning);color:#fff;border-color:var(--sd-warning)}.c-bool-btn:hover:not(.c-bool-active).c-bool-true{background:var(--sd-success-light)}.c-bool-btn:hover:not(.c-bool-active).c-bool-false{background:var(--sd-warning-light)}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}:host-context(.c-density-compact) .c-token-remove{width:26px}:host-context(.c-density-comfortable) .c-token-remove{width:28px}.c-token-remove mat-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "bare", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "bare", "viewed", "appearance", "floatLabel", "min", "max", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "bare", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "bare", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
930
|
+
}
|
|
931
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryInlineChip, decorators: [{
|
|
932
|
+
type: Component,
|
|
933
|
+
args: [{ selector: 'sd-query-inline-chip', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
934
|
+
CommonModule,
|
|
935
|
+
MatIconModule,
|
|
936
|
+
SdOperator,
|
|
937
|
+
SdDate,
|
|
938
|
+
SdDateRange,
|
|
939
|
+
SdDatetime,
|
|
940
|
+
SdSelect,
|
|
941
|
+
], template: "<!--\n Completed inline chip \u2014 non-string/number/seamless branches.\n Edit lifecycle is self-managed: click enters edit + auto-opens picker,\n focusout outside the wrapper exits edit, picker emits commit single OR\n liveChange (multi) OR commitRange (BETWEEN).\n-->\n@let _f = field();\n@let _op = operator();\n@let _data = data();\n@let _multi = multiple();\n@let _editing = editing();\n@let _isNoData = isNoData();\n\n<div class=\"c-token\" [class.c-token-editing]=\"_editing\">\n <mat-icon class=\"c-token-icon\" fontSet=\"material-icons-outlined\">{{ iconName() }}</mat-icon>\n <span class=\"c-token-field\">{{ _f.label }}</span>\n\n @if (!_isNoData && showOperator()) {\n <sd-operator class=\"c-token-op\" [operators]=\"[_op]\" [model]=\"_op\" [disabled]=\"true\" />\n } @else if (!_isNoData) {\n <span class=\"c-token-sep\">:</span>\n }\n\n @if (!_isNoData) {\n @if (_f.type === 'boolean') {\n @if (_editing) {\n <!-- why: native button thay sd-button \u2014 mat-button MDC baseline 36px bu\u1ED9c\n ph\u1EA3i hack height/padding overrides; chip 28/32px nh\u1ECF h\u01A1n nhi\u1EC1u. T\u1EF1 v\u1EBD\n pill nh\u1ECF v\u1EDBi CSS scoped trong chip, kh\u00F4ng qua MDC, g\u1ECDn + chu\u1EA9n baseline. -->\n <span class=\"c-token-value c-token-value-edit c-bool-toggle\">\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-true\"\n [class.c-bool-active]=\"_data === true\"\n [attr.data-autoId]=\"autoId() + '-true'\"\n (click)=\"emitBoolean(true)\">\n {{ $any(_f).trueLabel || 'C\u00F3' }}\n </button>\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-false\"\n [class.c-bool-active]=\"_data === false\"\n [attr.data-autoId]=\"autoId() + '-false'\"\n (click)=\"emitBoolean(false)\">\n {{ $any(_f).falseLabel || 'Kh\u00F4ng' }}\n </button>\n </span>\n } @else {\n <button type=\"button\" class=\"c-token-value\" (click)=\"enterEdit()\">{{ valueText() }}</button>\n }\n } @else if ((_f.type === 'date' || _f.type === 'datetime') && _op === 'BETWEEN') {\n <!-- why: d\u00F9ng sd-date-range th\u1ED1ng nh\u1EA5t cho c\u1EA3 date + datetime BETWEEN (datetime downgrade v\u1EC1 date). -->\n <span\n class=\"c-token-value c-token-value-edit\"\n (click)=\"enterEdit()\"\n (focusout)=\"onFocusOut($event)\">\n <sd-date-range #chipPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [viewed]=\"!_editing\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitRangeCommit($any($event))\"></sd-date-range>\n </span>\n } @else if (_f.type === 'values' || _f.type === 'lazy-values') {\n @let _opt = $any(_f).option;\n <span\n class=\"c-token-value c-token-value-edit\"\n (click)=\"enterEdit()\"\n (focusout)=\"onFocusOut($event)\">\n <sd-select #chipPicker bare size=\"sm\" minWidthPanel=\"300px\"\n [autoId]=\"autoId()\"\n [items]=\"_f.type === 'lazy-values' ? $any(_opt).search : $any(_opt.items)\"\n [valueField]=\"_opt.valueField\"\n [displayField]=\"_opt.displayField\"\n [multiple]=\"_multi\"\n [viewed]=\"!_editing\"\n [model]=\"$any(_data)\"\n (sdChange)=\"_multi ? emitLive($event) : emitSingleCommit($event)\">\n <!-- why: viewed mode m\u1EB7c \u0111\u1ECBnh d\u00F9ng comma-joined cho multi \u2014 qu\u00E1 d\u00E0i cho chip.\n Override b\u1EB1ng template \"head +N\" gi\u1ED1ng popover chipValueText. -->\n <ng-template #sdValue let-selectedItems=\"selectedItems\">\n @if ((selectedItems?.length ?? 0) > 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_opt.displayField] }}</span> +{{ selectedItems.length - 1 }}\n } @else if ((selectedItems?.length ?? 0) === 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_opt.displayField] }}</span>\n }\n </ng-template>\n </sd-select>\n </span>\n } @else if (_f.type === 'date') {\n <span\n class=\"c-token-value c-token-value-edit\"\n (click)=\"enterEdit()\"\n (focusout)=\"onFocusOut($event)\">\n <sd-date #chipPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [viewed]=\"!_editing\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitSingleCommit($any($event))\"></sd-date>\n </span>\n } @else if (_f.type === 'datetime') {\n <span\n class=\"c-token-value c-token-value-edit\"\n (click)=\"enterEdit()\"\n (focusout)=\"onFocusOut($event)\">\n <sd-datetime #chipPicker bare size=\"sm\"\n [autoId]=\"autoId()\"\n [viewed]=\"!_editing\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitSingleCommit($any($event))\"></sd-datetime>\n </span>\n } @else {\n <!-- Fallback (unforeseen type) \u2014 read-only display via valueText. -->\n <span class=\"c-token-value\">{{ valueText() }}</span>\n }\n }\n\n <button type=\"button\" class=\"c-token-remove\" aria-label=\"Remove filter\" (click)=\"remove.emit()\">\n <mat-icon fontSet=\"material-icons-outlined\">close</mat-icon>\n </button>\n</div>\n", styles: [":host{display:contents}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}:host-context(.c-density-compact) .c-token{height:28px}:host-context(.c-density-comfortable) .c-token{height:32px}.c-token-icon{font-size:14px;width:14px;height:14px;line-height:14px;color:inherit}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-sep{color:#7a7a7a}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary);cursor:pointer}.c-token-value:hover{text-decoration:underline}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-btn{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:0;padding:0 8px;border:1px solid currentColor;border-radius:999px;background:transparent;font:inherit;font-size:11px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background .12s ease,color .12s ease}:host-context(.c-density-comfortable) .c-bool-btn{height:20px;padding:0 10px;font-size:12px}.c-bool-true{color:var(--sd-success)}.c-bool-false{color:var(--sd-warning)}.c-bool-active.c-bool-true{background:var(--sd-success);color:#fff;border-color:var(--sd-success)}.c-bool-active.c-bool-false{background:var(--sd-warning);color:#fff;border-color:var(--sd-warning)}.c-bool-btn:hover:not(.c-bool-active).c-bool-true{background:var(--sd-success-light)}.c-bool-btn:hover:not(.c-bool-active).c-bool-false{background:var(--sd-warning-light)}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}:host-context(.c-density-compact) .c-token-remove{width:26px}:host-context(.c-density-comfortable) .c-token-remove{width:28px}.c-token-remove mat-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}\n"] }]
|
|
942
|
+
}] });
|
|
943
|
+
|
|
944
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
945
|
+
/**
|
|
946
|
+
* Compact popover-mode chip face. Click → opens the parent-supplied [menu] (mat-menu
|
|
947
|
+
* containing the chip editor). Inert div[role=button] so the nested <sd-operator>
|
|
948
|
+
* doesn't end up as a button-in-button (invalid HTML).
|
|
949
|
+
*/
|
|
950
|
+
class SdQueryPopoverChip {
|
|
951
|
+
/** Resolved field (from the bar's `fieldByKey()` map). `undefined` → render raw key. */
|
|
952
|
+
field = input(undefined);
|
|
953
|
+
/** The chip's filter — used to read `field` / `operator` / `data` for display text. */
|
|
954
|
+
filter = input.required();
|
|
955
|
+
/** "Active" = chip has a real value or is no-data op. Drives layout + colour. */
|
|
956
|
+
active = input.required();
|
|
957
|
+
/** Operator visible on the chip face (else hidden, ":" separator). */
|
|
958
|
+
showOperator = input(false);
|
|
959
|
+
/** Rendered value text for the value slot. */
|
|
960
|
+
valueText = input('');
|
|
961
|
+
/** Chip popover mat-menu instance supplied by the parent. */
|
|
962
|
+
menu = input.required();
|
|
963
|
+
/** Fired when the chip popover opens (parent seeds its editing state). */
|
|
964
|
+
open = output();
|
|
965
|
+
/** Fired when the user clicks the × removal icon. */
|
|
966
|
+
remove = output();
|
|
967
|
+
iconFor = sdQueryFieldIcon;
|
|
968
|
+
filterField() { return this.filter().field; }
|
|
969
|
+
filterOperator() { return this.filter().operator; }
|
|
970
|
+
// why: parent's add/swap flow auto-opens the chip popover after render, and removeFilter
|
|
971
|
+
// closes the open one — expose open/close so the parent doesn't need a ViewChild on the
|
|
972
|
+
// internal MatMenuTrigger directive.
|
|
973
|
+
trigger = viewChild('chipTrigger', { read: MatMenuTrigger });
|
|
974
|
+
openMenu() { this.trigger()?.openMenu(); }
|
|
975
|
+
closeMenu() { this.trigger()?.closeMenu(); }
|
|
976
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryPopoverChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
977
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdQueryPopoverChip, isStandalone: true, selector: "sd-query-popover-chip", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: true, transformFunction: null }, showOperator: { classPropertyName: "showOperator", publicName: "showOperator", isSignal: true, isRequired: false, transformFunction: null }, valueText: { classPropertyName: "valueText", publicName: "valueText", isSignal: true, isRequired: false, transformFunction: null }, menu: { classPropertyName: "menu", publicName: "menu", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { open: "open", remove: "remove" }, viewQueries: [{ propertyName: "trigger", first: true, predicate: ["chipTrigger"], descendants: true, read: MatMenuTrigger, isSignal: true }], ngImport: i0, template: "<!-- why: div[role=button] (not <button>) so the nested <sd-operator> isn't button-in-button (invalid HTML). -->\n<div\n #chipTrigger=\"matMenuTrigger\"\n role=\"button\"\n tabindex=\"0\"\n class=\"c-chip\"\n [class.c-chip-active]=\"active()\"\n [class.c-chip-inactive]=\"!active()\"\n [attr.data-field]=\"filterField()\"\n [matTooltip]=\"field()?.label || filterField()\"\n [matMenuTriggerFor]=\"menu()\"\n (menuOpened)=\"open.emit()\"\n (keydown.enter)=\"chipTrigger.openMenu()\"\n (keydown.space)=\"$event.preventDefault(); chipTrigger.openMenu()\">\n <mat-icon class=\"c-chip-icon\" fontSet=\"material-icons-outlined\">\n {{ field() ? iconFor(field()!) : 'tune' }}\n </mat-icon>\n <span class=\"c-chip-label\">{{ field()?.label || filterField() }}</span>\n\n @if (showOperator() && active()) {\n <sd-operator class=\"c-chip-operator\" [operators]=\"[filterOperator()]\" [model]=\"filterOperator()\" [disabled]=\"true\" />\n } @else if (active()) {\n <span class=\"c-chip-sep\">:</span>\n }\n\n @if (active()) {\n <span class=\"c-chip-value\">{{ valueText() }}</span>\n }\n\n <mat-icon class=\"c-chip-chevron\" fontSet=\"material-icons-outlined\">arrow_drop_down</mat-icon>\n\n @if (active()) {\n <span\n role=\"button\"\n tabindex=\"0\"\n class=\"c-chip-remove\"\n aria-label=\"Remove filter\"\n (click)=\"$event.stopPropagation(); remove.emit()\"\n (keydown.enter)=\"$event.stopPropagation(); remove.emit()\">\n <mat-icon fontSet=\"material-icons-outlined\">close</mat-icon>\n </span>\n }\n</div>\n", styles: [":host{display:contents}.c-chip{display:inline-flex;align-items:center;gap:4px;padding:0 4px 0 10px;border-radius:999px;border:1px solid #d6d8db;background:#fff;cursor:pointer;font-family:inherit;font-size:13px;font-weight:500;color:#5f6368;transition:background-color .12s ease,border-color .12s ease,color .12s ease}:host-context(.c-density-compact) .c-chip{height:28px}:host-context(.c-density-comfortable) .c-chip{height:32px}.c-chip:hover{border-color:var(--sd-primary)}.c-chip.c-chip-active{border-color:var(--sd-primary-light);background:var(--sd-primary-light)}.c-chip.c-chip-active .c-chip-value{color:var(--sd-primary)}.c-chip.c-chip-inactive{padding-right:10px}.c-chip .c-chip-icon{font-size:16px;width:16px;height:16px}.c-chip .c-chip-label{font-weight:500;white-space:nowrap}.c-chip .c-chip-operator{display:inline-flex;align-items:center;color:inherit}.c-chip .c-chip-sep{color:#7a7a7a}.c-chip .c-chip-value{font-weight:500;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.c-chip .c-chip-chevron{font-size:18px;width:18px;height:18px;color:currentColor}.c-chip .c-chip-remove{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;cursor:pointer;color:#7a7a7a}.c-chip .c-chip-remove mat-icon{font-size:14px;width:14px;height:14px}.c-chip .c-chip-remove:hover{background:#0000000f}.c-chip-operator ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-chip-operator ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}\n"], dependencies: [{ kind: "ngmodule", type: MatMenuModule }, { kind: "directive", type: i1.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: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
978
|
+
}
|
|
979
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryPopoverChip, decorators: [{
|
|
980
|
+
type: Component,
|
|
981
|
+
args: [{ selector: 'sd-query-popover-chip', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatMenuModule, MatIconModule, MatTooltipModule, SdOperator], template: "<!-- why: div[role=button] (not <button>) so the nested <sd-operator> isn't button-in-button (invalid HTML). -->\n<div\n #chipTrigger=\"matMenuTrigger\"\n role=\"button\"\n tabindex=\"0\"\n class=\"c-chip\"\n [class.c-chip-active]=\"active()\"\n [class.c-chip-inactive]=\"!active()\"\n [attr.data-field]=\"filterField()\"\n [matTooltip]=\"field()?.label || filterField()\"\n [matMenuTriggerFor]=\"menu()\"\n (menuOpened)=\"open.emit()\"\n (keydown.enter)=\"chipTrigger.openMenu()\"\n (keydown.space)=\"$event.preventDefault(); chipTrigger.openMenu()\">\n <mat-icon class=\"c-chip-icon\" fontSet=\"material-icons-outlined\">\n {{ field() ? iconFor(field()!) : 'tune' }}\n </mat-icon>\n <span class=\"c-chip-label\">{{ field()?.label || filterField() }}</span>\n\n @if (showOperator() && active()) {\n <sd-operator class=\"c-chip-operator\" [operators]=\"[filterOperator()]\" [model]=\"filterOperator()\" [disabled]=\"true\" />\n } @else if (active()) {\n <span class=\"c-chip-sep\">:</span>\n }\n\n @if (active()) {\n <span class=\"c-chip-value\">{{ valueText() }}</span>\n }\n\n <mat-icon class=\"c-chip-chevron\" fontSet=\"material-icons-outlined\">arrow_drop_down</mat-icon>\n\n @if (active()) {\n <span\n role=\"button\"\n tabindex=\"0\"\n class=\"c-chip-remove\"\n aria-label=\"Remove filter\"\n (click)=\"$event.stopPropagation(); remove.emit()\"\n (keydown.enter)=\"$event.stopPropagation(); remove.emit()\">\n <mat-icon fontSet=\"material-icons-outlined\">close</mat-icon>\n </span>\n }\n</div>\n", styles: [":host{display:contents}.c-chip{display:inline-flex;align-items:center;gap:4px;padding:0 4px 0 10px;border-radius:999px;border:1px solid #d6d8db;background:#fff;cursor:pointer;font-family:inherit;font-size:13px;font-weight:500;color:#5f6368;transition:background-color .12s ease,border-color .12s ease,color .12s ease}:host-context(.c-density-compact) .c-chip{height:28px}:host-context(.c-density-comfortable) .c-chip{height:32px}.c-chip:hover{border-color:var(--sd-primary)}.c-chip.c-chip-active{border-color:var(--sd-primary-light);background:var(--sd-primary-light)}.c-chip.c-chip-active .c-chip-value{color:var(--sd-primary)}.c-chip.c-chip-inactive{padding-right:10px}.c-chip .c-chip-icon{font-size:16px;width:16px;height:16px}.c-chip .c-chip-label{font-weight:500;white-space:nowrap}.c-chip .c-chip-operator{display:inline-flex;align-items:center;color:inherit}.c-chip .c-chip-sep{color:#7a7a7a}.c-chip .c-chip-value{font-weight:500;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.c-chip .c-chip-chevron{font-size:18px;width:18px;height:18px;color:currentColor}.c-chip .c-chip-remove{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;cursor:pointer;color:#7a7a7a}.c-chip .c-chip-remove mat-icon{font-size:14px;width:14px;height:14px}.c-chip .c-chip-remove:hover{background:#0000000f}.c-chip-operator ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-chip-operator ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}\n"] }]
|
|
982
|
+
}] });
|
|
983
|
+
|
|
984
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
985
|
+
/** Shared empty array — stable reference so template bindings don't churn change detection. */
|
|
986
|
+
const EMPTY_ARRAY = [];
|
|
987
|
+
class SdQueryBar {
|
|
988
|
+
#i18n = inject(I18nService);
|
|
989
|
+
#injector = inject(Injector);
|
|
990
|
+
/** All popover-mode chips in order — used to auto-open after addFilter / close before removeFilter. */
|
|
991
|
+
popoverChips = viewChildren(SdQueryPopoverChip);
|
|
992
|
+
/** The extracted in-progress chip — owns operator menu + value picker view refs. */
|
|
993
|
+
buildChip = viewChild(SdQueryBuildChip);
|
|
994
|
+
/** The extracted chip popover — owns operator+value staging + commit-on-close. */
|
|
995
|
+
chipPopover = viewChild(SdQueryChipPopover);
|
|
996
|
+
/**
|
|
997
|
+
* Land the user in the value editor after the build chip reaches the value step:
|
|
998
|
+
* popover-kind controls auto-open their native picker via the child; string/number
|
|
999
|
+
* branches use the seamless chip whose `[autofocus]` handles focus internally.
|
|
1000
|
+
*/
|
|
1001
|
+
#enterValueStep(field) {
|
|
1002
|
+
const k = field.type;
|
|
1003
|
+
if (k === 'values' || k === 'lazy-values' || k === 'date' || k === 'datetime') {
|
|
1004
|
+
afterNextRender(() => this.buildChip()?.openPicker(), { injector: this.#injector });
|
|
1005
|
+
}
|
|
1006
|
+
// why: string/number branch — <sd-query-inline-value-chip [autofocus]> tự focus,
|
|
1007
|
+
// không cần parent gọi thêm.
|
|
1008
|
+
}
|
|
1009
|
+
// ---------------------------------------------------------------------------
|
|
1010
|
+
// Inputs — all accept null|undefined at boundary, transform to canonical shape
|
|
1011
|
+
// ---------------------------------------------------------------------------
|
|
1012
|
+
/** Prefix for auto-generated `data-autoid` on inner controls (chips, operator/value editors, buttons). */
|
|
1013
|
+
autoIdInput = input(undefined, {
|
|
1014
|
+
alias: 'autoId',
|
|
1015
|
+
transform: (v) => v ?? undefined,
|
|
1016
|
+
});
|
|
1017
|
+
/** Available fields the user can filter by. Required for the field picker. */
|
|
1018
|
+
fields = input([], {
|
|
1019
|
+
transform: (v) => (Array.isArray(v) ? v : []),
|
|
1020
|
+
});
|
|
1021
|
+
/**
|
|
1022
|
+
* Active filter chips. Two-way bindable via `[(filters)]`.
|
|
1023
|
+
* Use this when you only need a flat `Filter[]` (no global logic / search box).
|
|
1024
|
+
*/
|
|
1025
|
+
filters = model([]);
|
|
1026
|
+
/**
|
|
1027
|
+
* Global connector between filters. Two-way bindable via `[(logic)]`.
|
|
1028
|
+
* Only meaningful when `showLogicToggle=true` and there are ≥2 filters.
|
|
1029
|
+
*/
|
|
1030
|
+
logic = model('AND');
|
|
1031
|
+
/** Free-text search string. Two-way bindable via `[(search)]`. Only shown when `showSearch=true`. */
|
|
1032
|
+
search = model('');
|
|
1033
|
+
/**
|
|
1034
|
+
* Editing mode:
|
|
1035
|
+
* - `'popover'` (default) → compact chips; click a chip to edit operator + value in a
|
|
1036
|
+
* mat-menu popover, commit with "Áp dụng".
|
|
1037
|
+
* - `'inline'` → GitLab-style. Each filter's operator + value controls render directly
|
|
1038
|
+
* on the bar (no popup, no per-filter apply). Edits update `filters` live; the user
|
|
1039
|
+
* presses the search button (or Enter) once to fire `(apply)`.
|
|
1040
|
+
*/
|
|
1041
|
+
mode = input('popover', {
|
|
1042
|
+
transform: (v) => v || 'popover',
|
|
1043
|
+
});
|
|
1044
|
+
/** Density preset — chip / control height. */
|
|
1045
|
+
density = input('compact', {
|
|
1046
|
+
transform: (v) => v || 'compact',
|
|
1047
|
+
});
|
|
1048
|
+
/** Show free-text search input on the left. */
|
|
1049
|
+
showSearch = input(false, { transform: booleanAttribute });
|
|
1050
|
+
/** Show saved-filters dropdown on the right. Requires `[savedFiltersKey]` to actually persist. */
|
|
1051
|
+
showSavedFilters = input(false, { transform: booleanAttribute });
|
|
1052
|
+
/**
|
|
1053
|
+
* Namespace key for persisting saved filters to `localStorage`. When set, the saved-filters
|
|
1054
|
+
* dropdown loads/saves under `sd-query-bar:savedFilters:<key>`. Leave undefined to disable.
|
|
1055
|
+
*/
|
|
1056
|
+
savedFiltersKey = input(undefined);
|
|
1057
|
+
/**
|
|
1058
|
+
* Snapshot of the current query — fed into `<sd-query-saved-filters-menu [query]>` so
|
|
1059
|
+
* saving a filter captures live filters/logic/search.
|
|
1060
|
+
*/
|
|
1061
|
+
currentQuery = computed(() => this.#buildQuery());
|
|
1062
|
+
/** Receive an applied filter from the saved-filters menu — re-install bar state + trigger apply. */
|
|
1063
|
+
onApplyFilter(saved) {
|
|
1064
|
+
this.filters.set(saved.query.filters ?? []);
|
|
1065
|
+
this.logic.set(saved.query.logic ?? 'AND');
|
|
1066
|
+
if (saved.query.search !== undefined)
|
|
1067
|
+
this.search.set(saved.query.search);
|
|
1068
|
+
this.triggerApply();
|
|
1069
|
+
}
|
|
1070
|
+
/** Show AND/OR segmented toggle (renders only when there are ≥2 filters). */
|
|
1071
|
+
showLogicToggle = input(false, { transform: booleanAttribute });
|
|
1072
|
+
/** Show "Clear all" button when at least one filter is active. */
|
|
1073
|
+
showClearAll = input(true, { transform: booleanAttribute });
|
|
1074
|
+
/** Render the operator label on the chip face (default: hidden — operator only visible in popover). */
|
|
1075
|
+
showOperatorOnChip = input(false, { transform: booleanAttribute });
|
|
1076
|
+
// ---------------------------------------------------------------------------
|
|
1077
|
+
// Outputs
|
|
1078
|
+
// ---------------------------------------------------------------------------
|
|
1079
|
+
/** Composite payload — emitted whenever filters / logic / search change. */
|
|
1080
|
+
queryChange = output();
|
|
1081
|
+
/** Fires when the user presses "Áp dụng" inside a chip popover — host should reload data. */
|
|
1082
|
+
apply = output();
|
|
1083
|
+
// ---------------------------------------------------------------------------
|
|
1084
|
+
// Derived state
|
|
1085
|
+
// ---------------------------------------------------------------------------
|
|
1086
|
+
/** Map of `field.key` → `SdQueryField` for fast lookup from filters[]. */
|
|
1087
|
+
fieldByKey = computed(() => {
|
|
1088
|
+
const map = {};
|
|
1089
|
+
for (const f of this.fields())
|
|
1090
|
+
map[f.key] = f;
|
|
1091
|
+
return map;
|
|
1092
|
+
});
|
|
1093
|
+
/** Field keys already chained on a chip — the picker greys these out. */
|
|
1094
|
+
usedFieldKeys = computed(() => {
|
|
1095
|
+
const set = new Set();
|
|
1096
|
+
for (const f of this.filters()) {
|
|
1097
|
+
if ('field' in f && typeof f.field === 'string')
|
|
1098
|
+
set.add(f.field);
|
|
1099
|
+
}
|
|
1100
|
+
return set;
|
|
1101
|
+
});
|
|
1102
|
+
/** Render the global AND/OR connector text between chips when `logic === 'OR'`. */
|
|
1103
|
+
showOrConnector = computed(() => this.showLogicToggle() && this.logic() === 'OR' && this.filters().length >= 2);
|
|
1104
|
+
/** Search button is actionable only when there is something to apply. */
|
|
1105
|
+
canSearch = computed(() => this.filters().length > 0 || this.search().trim().length > 0);
|
|
1106
|
+
// ---------------------------------------------------------------------------
|
|
1107
|
+
// Chip popover state — only `editingIndex` lives on the parent; the
|
|
1108
|
+
// `<sd-query-chip-popover>` child owns ALL staging (operator + value + options).
|
|
1109
|
+
// ---------------------------------------------------------------------------
|
|
1110
|
+
/** Index of the chip whose popover is open. Reset to null after commit / close. */
|
|
1111
|
+
editingIndex = signal(null);
|
|
1112
|
+
/** Resolved field of the chip whose popover is currently open (or null). */
|
|
1113
|
+
editingField = computed(() => {
|
|
1114
|
+
const idx = this.editingIndex();
|
|
1115
|
+
if (idx === null)
|
|
1116
|
+
return null;
|
|
1117
|
+
const f = this.filters()[idx];
|
|
1118
|
+
if (!f || !('field' in f))
|
|
1119
|
+
return null;
|
|
1120
|
+
return this.fieldByKey()[f.field] ?? null;
|
|
1121
|
+
});
|
|
1122
|
+
/**
|
|
1123
|
+
* Multi-select is derived from the operator (IN / NOT_IN) — not a field flag.
|
|
1124
|
+
* Only called by inline-mode + build-mode templates with a known operator;
|
|
1125
|
+
* popover-mode chips read `multiple()` from the child popover directly.
|
|
1126
|
+
*/
|
|
1127
|
+
multiple(op) {
|
|
1128
|
+
return SD_QUERY_MULTI_OPERATORS.includes(op);
|
|
1129
|
+
}
|
|
1130
|
+
// ---------------------------------------------------------------------------
|
|
1131
|
+
// Template helpers
|
|
1132
|
+
// ---------------------------------------------------------------------------
|
|
1133
|
+
iconFor = sdQueryFieldIcon;
|
|
1134
|
+
/** Chip is "active" when it has a non-empty value OR a no-data operator (NULL/NOT_NULL). */
|
|
1135
|
+
isFilterActive(filter) {
|
|
1136
|
+
if (SD_QUERY_NO_DATA_OPERATORS.includes(filter.operator))
|
|
1137
|
+
return true;
|
|
1138
|
+
const data = filter.data;
|
|
1139
|
+
if (data === null || data === undefined || data === '')
|
|
1140
|
+
return false;
|
|
1141
|
+
if (Array.isArray(data) && data.length === 0)
|
|
1142
|
+
return false;
|
|
1143
|
+
return true;
|
|
1144
|
+
}
|
|
1145
|
+
/** Rendered value text on a chip. */
|
|
1146
|
+
chipValueText(filter) {
|
|
1147
|
+
if (SD_QUERY_NO_DATA_OPERATORS.includes(filter.operator))
|
|
1148
|
+
return '';
|
|
1149
|
+
const data = filter.data;
|
|
1150
|
+
if (data === null || data === undefined)
|
|
1151
|
+
return '';
|
|
1152
|
+
if (filter.operator === 'BETWEEN' && data && typeof data === 'object') {
|
|
1153
|
+
return `${data.from ?? ''} — ${data.to ?? ''}`;
|
|
1154
|
+
}
|
|
1155
|
+
if (Array.isArray(data)) {
|
|
1156
|
+
if (data.length === 0)
|
|
1157
|
+
return '';
|
|
1158
|
+
const head = this.#displayOne(filter, data[0]);
|
|
1159
|
+
return data.length > 1 ? `${head} +${data.length - 1}` : head;
|
|
1160
|
+
}
|
|
1161
|
+
return this.#displayOne(filter, data);
|
|
1162
|
+
}
|
|
1163
|
+
isNoDataOperator(op) {
|
|
1164
|
+
return SD_QUERY_NO_DATA_OPERATORS.includes(op);
|
|
1165
|
+
}
|
|
1166
|
+
// ---------------------------------------------------------------------------
|
|
1167
|
+
// Inline mode — per-field helpers + live mutators (no popover / no Apply gate)
|
|
1168
|
+
// ---------------------------------------------------------------------------
|
|
1169
|
+
/** The single in-progress inline chip (not yet in `filters`). */
|
|
1170
|
+
#building = signal(null);
|
|
1171
|
+
building = this.#building.asReadonly();
|
|
1172
|
+
allowedOperatorsFor(field) {
|
|
1173
|
+
return sdQueryAllowedOperators(field);
|
|
1174
|
+
}
|
|
1175
|
+
inlineAutoId(index, role) {
|
|
1176
|
+
const base = this.autoIdInput() ?? 'qb';
|
|
1177
|
+
return `${base}-inline${index}-${role}`;
|
|
1178
|
+
}
|
|
1179
|
+
// ---------------------------------------------------------------------------
|
|
1180
|
+
// Inline mode — progressive token build flow (field → operator → value)
|
|
1181
|
+
// ---------------------------------------------------------------------------
|
|
1182
|
+
/** Append a completed chip to `filters` WITHOUT emitting (inline mode commits on Search). */
|
|
1183
|
+
#pushComplete(field, operator, value) {
|
|
1184
|
+
let data = value;
|
|
1185
|
+
if (SD_QUERY_NO_DATA_OPERATORS.includes(operator))
|
|
1186
|
+
data = null;
|
|
1187
|
+
else if (operator === 'BETWEEN') {
|
|
1188
|
+
if (!data || typeof data !== 'object')
|
|
1189
|
+
data = { from: null, to: null };
|
|
1190
|
+
}
|
|
1191
|
+
else if (SD_QUERY_MULTI_OPERATORS.includes(operator)) {
|
|
1192
|
+
data = Array.isArray(data) ? data : value == null ? [] : [value];
|
|
1193
|
+
}
|
|
1194
|
+
const next = { field: field.key, operator, data };
|
|
1195
|
+
this.filters.set([...this.filters(), next]);
|
|
1196
|
+
}
|
|
1197
|
+
/** Entry point from the field picker — start building a chip for `field`. */
|
|
1198
|
+
beginBuild(field) {
|
|
1199
|
+
const allowed = sdQueryAllowedOperators(field);
|
|
1200
|
+
const def = sdQueryDefaultOperator(field);
|
|
1201
|
+
// why: date/datetime mặc định là BETWEEN — skip operator step để user vào value
|
|
1202
|
+
// step ngay (95% case dùng BETWEEN cho date/datetime; cắt 1 click cho luồng phổ
|
|
1203
|
+
// biến nhất). Field type khác giữ flow chọn operator để user thấy đầy đủ lựa chọn.
|
|
1204
|
+
const skipOperatorStep = (field.type === 'date' || field.type === 'datetime') && allowed.includes(def);
|
|
1205
|
+
if (allowed.length > 1 && !skipOperatorStep) {
|
|
1206
|
+
this.#building.set({ field, step: 'operator' });
|
|
1207
|
+
// why: auto-open the operator menu so the user can pick the condition without a
|
|
1208
|
+
// second click — the viewChild only resolves after the building chip renders.
|
|
1209
|
+
afterNextRender(() => this.buildChip()?.openOperator(), { injector: this.#injector });
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
const operator = skipOperatorStep ? def : (allowed[0] ?? def);
|
|
1213
|
+
if (SD_QUERY_NO_DATA_OPERATORS.includes(operator)) {
|
|
1214
|
+
this.#pushComplete(field, operator, null);
|
|
1215
|
+
this.#building.set(null);
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
this.#building.set({ field, operator, step: 'value' });
|
|
1219
|
+
this.#enterValueStep(field);
|
|
1220
|
+
}
|
|
1221
|
+
/** Operator chosen during build — finish (no-data) or advance to the value step. */
|
|
1222
|
+
pickBuildOperator(op) {
|
|
1223
|
+
const b = this.#building();
|
|
1224
|
+
if (!b)
|
|
1225
|
+
return;
|
|
1226
|
+
if (SD_QUERY_NO_DATA_OPERATORS.includes(op)) {
|
|
1227
|
+
this.#pushComplete(b.field, op, null);
|
|
1228
|
+
this.#building.set(null);
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
this.#building.set({ ...b, operator: op, step: 'value' });
|
|
1232
|
+
this.#enterValueStep(b.field);
|
|
1233
|
+
}
|
|
1234
|
+
/** Value committed during build — push the completed chip, clear building. No emit. */
|
|
1235
|
+
commitBuildValue(value) {
|
|
1236
|
+
const b = this.#building();
|
|
1237
|
+
if (!b || !b.operator)
|
|
1238
|
+
return;
|
|
1239
|
+
this.#pushComplete(b.field, b.operator, value);
|
|
1240
|
+
this.#building.set(null);
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Commit from the seamless build chip (string / number). An empty value (blur with no
|
|
1244
|
+
* input) cancels the build instead of pushing a blank chip.
|
|
1245
|
+
*/
|
|
1246
|
+
onBuildSeamlessCommit(value) {
|
|
1247
|
+
const empty = value == null ||
|
|
1248
|
+
value === '' ||
|
|
1249
|
+
(typeof value === 'object' &&
|
|
1250
|
+
value.from == null &&
|
|
1251
|
+
value.to == null);
|
|
1252
|
+
if (empty) {
|
|
1253
|
+
this.cancelBuild();
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
this.commitBuildValue(value);
|
|
1257
|
+
}
|
|
1258
|
+
/** Abandon the in-progress chip. */
|
|
1259
|
+
cancelBuild() {
|
|
1260
|
+
this.#building.set(null);
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Commit both ends of a BETWEEN range at once — called from `<sd-query-inline-chip>`'s
|
|
1264
|
+
* `(commitRange)` output when the user picks a date range.
|
|
1265
|
+
* why: sd-date-range emits {from,to} via (sdChange) — single call thay cho cặp
|
|
1266
|
+
* setFilterRangeFrom / setFilterRangeTo cũ (đã bỏ).
|
|
1267
|
+
*/
|
|
1268
|
+
setFilterRange(i, ev) {
|
|
1269
|
+
this.updateFilter(i, { data: ev ?? { from: null, to: null } });
|
|
1270
|
+
}
|
|
1271
|
+
// ---------------------------------------------------------------------------
|
|
1272
|
+
// Mutations
|
|
1273
|
+
// ---------------------------------------------------------------------------
|
|
1274
|
+
addFilter(field) {
|
|
1275
|
+
const operator = sdQueryDefaultOperator(field);
|
|
1276
|
+
const data = SD_QUERY_MULTI_OPERATORS.includes(operator) ? [] : null;
|
|
1277
|
+
const next = { field: field.key, operator, data };
|
|
1278
|
+
const newIndex = this.filters().length;
|
|
1279
|
+
this.filters.set([...this.filters(), next]);
|
|
1280
|
+
// why: user added a field — open its edit popover immediately so they can pick
|
|
1281
|
+
// operator + value in one continuous flow (no second click required).
|
|
1282
|
+
afterNextRender(() => this.popoverChips()[newIndex]?.openMenu(), { injector: this.#injector });
|
|
1283
|
+
}
|
|
1284
|
+
/** Swap the field of an existing chip — reset operator + value to the new field's defaults. */
|
|
1285
|
+
changeFilterField(index, field) {
|
|
1286
|
+
const list = [...this.filters()];
|
|
1287
|
+
if (index < 0 || index >= list.length)
|
|
1288
|
+
return;
|
|
1289
|
+
const operator = sdQueryDefaultOperator(field);
|
|
1290
|
+
const data = SD_QUERY_MULTI_OPERATORS.includes(operator) ? [] : null;
|
|
1291
|
+
list[index] = { field: field.key, operator, data };
|
|
1292
|
+
this.filters.set(list);
|
|
1293
|
+
// why: clicking an item in the nested fieldSwitchPicker closes both the picker AND
|
|
1294
|
+
// the parent chipPopover. Re-open the chip's popover on the next render so user can
|
|
1295
|
+
// continue picking operator + value without a second manual click.
|
|
1296
|
+
afterNextRender(() => this.popoverChips()[index]?.openMenu(), { injector: this.#injector });
|
|
1297
|
+
}
|
|
1298
|
+
updateFilter(index, patch) {
|
|
1299
|
+
const list = [...this.filters()];
|
|
1300
|
+
if (index < 0 || index >= list.length)
|
|
1301
|
+
return;
|
|
1302
|
+
list[index] = { ...list[index], ...patch };
|
|
1303
|
+
this.filters.set(list);
|
|
1304
|
+
}
|
|
1305
|
+
removeFilter(index) {
|
|
1306
|
+
const list = [...this.filters()];
|
|
1307
|
+
if (index < 0 || index >= list.length)
|
|
1308
|
+
return;
|
|
1309
|
+
// close the open popover (if it's this chip's) before the chip + its trigger vanish
|
|
1310
|
+
if (this.editingIndex() === index) {
|
|
1311
|
+
this.popoverChips()[index]?.closeMenu();
|
|
1312
|
+
this.editingIndex.set(null);
|
|
1313
|
+
}
|
|
1314
|
+
list.splice(index, 1);
|
|
1315
|
+
this.filters.set(list);
|
|
1316
|
+
}
|
|
1317
|
+
clearAll() {
|
|
1318
|
+
if (this.filters().length === 0)
|
|
1319
|
+
return;
|
|
1320
|
+
this.filters.set([]);
|
|
1321
|
+
this.editingIndex.set(null);
|
|
1322
|
+
}
|
|
1323
|
+
setLogic(value) {
|
|
1324
|
+
if (this.logic() === value)
|
|
1325
|
+
return;
|
|
1326
|
+
this.logic.set(value);
|
|
1327
|
+
}
|
|
1328
|
+
setSearch(value) {
|
|
1329
|
+
if (this.search() === value)
|
|
1330
|
+
return;
|
|
1331
|
+
this.search.set(value);
|
|
1332
|
+
}
|
|
1333
|
+
triggerApply() {
|
|
1334
|
+
// why: single deferred trigger — fire both the change notification and the reload
|
|
1335
|
+
// signal once, from here only (mutations no longer emit).
|
|
1336
|
+
const q = this.#buildQuery();
|
|
1337
|
+
this.queryChange.emit(q);
|
|
1338
|
+
this.apply.emit(q);
|
|
1339
|
+
}
|
|
1340
|
+
// ---------------------------------------------------------------------------
|
|
1341
|
+
// Chip popover — delegate staging to `<sd-query-chip-popover>` child.
|
|
1342
|
+
// ---------------------------------------------------------------------------
|
|
1343
|
+
/**
|
|
1344
|
+
* Called when a popover-mode chip is clicked — set editingIndex and seed the
|
|
1345
|
+
* child popover's staging signals from the chip's Filter.
|
|
1346
|
+
*/
|
|
1347
|
+
openChipPopover(index) {
|
|
1348
|
+
const filter = this.filters()[index];
|
|
1349
|
+
if (!filter || !('field' in filter))
|
|
1350
|
+
return;
|
|
1351
|
+
this.editingIndex.set(index);
|
|
1352
|
+
const field = this.fieldByKey()[filter.field];
|
|
1353
|
+
this.chipPopover()?.seed(filter, field);
|
|
1354
|
+
}
|
|
1355
|
+
/** Receive `(commit)` from the child popover — splice into filters, null the index. */
|
|
1356
|
+
onChipPopoverCommit(next) {
|
|
1357
|
+
const idx = this.editingIndex();
|
|
1358
|
+
if (idx === null)
|
|
1359
|
+
return;
|
|
1360
|
+
const list = [...this.filters()];
|
|
1361
|
+
list[idx] = next;
|
|
1362
|
+
this.filters.set(list);
|
|
1363
|
+
this.editingIndex.set(null);
|
|
1364
|
+
}
|
|
1365
|
+
// ---------------------------------------------------------------------------
|
|
1366
|
+
// Internals
|
|
1367
|
+
// ---------------------------------------------------------------------------
|
|
1368
|
+
#buildQuery() {
|
|
1369
|
+
return {
|
|
1370
|
+
filters: this.filters(),
|
|
1371
|
+
logic: this.logic(),
|
|
1372
|
+
...(this.showSearch() ? { search: this.search() } : {}),
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
#displayOne(filter, raw) {
|
|
1376
|
+
const fieldKey = 'field' in filter ? filter.field : '';
|
|
1377
|
+
const field = this.fieldByKey()[fieldKey];
|
|
1378
|
+
if (!field)
|
|
1379
|
+
return String(raw);
|
|
1380
|
+
if (field.type === 'boolean') {
|
|
1381
|
+
const trueLabel = field.trueLabel ?? 'Có';
|
|
1382
|
+
const falseLabel = field.falseLabel ?? 'Không';
|
|
1383
|
+
return raw ? trueLabel : falseLabel;
|
|
1384
|
+
}
|
|
1385
|
+
if (field.type === 'values' || field.type === 'lazy-values') {
|
|
1386
|
+
const opt = field.option;
|
|
1387
|
+
const cachedOptions = this.chipPopover()?.editingOptions() ?? [];
|
|
1388
|
+
const items = this.#resolveSyncItems(opt?.items) ?? cachedOptions;
|
|
1389
|
+
const match = items.find((i) => i?.[opt.valueField] === raw);
|
|
1390
|
+
if (match)
|
|
1391
|
+
return String(match[opt.displayField]);
|
|
1392
|
+
const cached = cachedOptions.find((i) => i?.[opt.valueField] === raw);
|
|
1393
|
+
return cached ? String(cached[opt.displayField]) : String(raw);
|
|
1394
|
+
}
|
|
1395
|
+
return String(raw);
|
|
1396
|
+
}
|
|
1397
|
+
#resolveSyncItems(items) {
|
|
1398
|
+
if (Array.isArray(items))
|
|
1399
|
+
return items;
|
|
1400
|
+
if (isSignal(items))
|
|
1401
|
+
return items();
|
|
1402
|
+
return null;
|
|
1403
|
+
}
|
|
1404
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1405
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdQueryBar, isStandalone: true, selector: "sd-query-bar", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, logic: { classPropertyName: "logic", publicName: "logic", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, showSavedFilters: { classPropertyName: "showSavedFilters", publicName: "showSavedFilters", isSignal: true, isRequired: false, transformFunction: null }, savedFiltersKey: { classPropertyName: "savedFiltersKey", publicName: "savedFiltersKey", isSignal: true, isRequired: false, transformFunction: null }, showLogicToggle: { classPropertyName: "showLogicToggle", publicName: "showLogicToggle", isSignal: true, isRequired: false, transformFunction: null }, showClearAll: { classPropertyName: "showClearAll", publicName: "showClearAll", isSignal: true, isRequired: false, transformFunction: null }, showOperatorOnChip: { classPropertyName: "showOperatorOnChip", publicName: "showOperatorOnChip", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filters: "filtersChange", logic: "logicChange", search: "searchChange", queryChange: "queryChange", apply: "apply" }, viewQueries: [{ propertyName: "popoverChips", predicate: SdQueryPopoverChip, descendants: true, isSignal: true }, { propertyName: "buildChip", first: true, predicate: SdQueryBuildChip, descendants: true, isSignal: true }, { propertyName: "chipPopover", first: true, predicate: SdQueryChipPopover, descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n sd-query-bar \u2014 unified filter chip row (Jira/Linear/Notion style).\n Visual spec: refs/design_handoff_sd_query_bar/README.md\n TODO(next-iter): replace inline placeholders for field-picker + chip-popover\n with extracted sub-components driven by CDK overlay.\n-->\n@let _filters = filters();\n@let _fields = fields();\n@let _density = density();\n@let _showOr = showOrConnector();\n@let _used = usedFieldKeys();\n\n<div\n class=\"c-query-bar\"\n [class.c-density-compact]=\"_density === 'compact'\"\n [class.c-density-comfortable]=\"_density === 'comfortable'\">\n\n <!-- Main region \u2014 search + filters + add-filter. Wraps internally; the right\n action toolbar stays anchored and does NOT flow with this. -->\n <div class=\"c-query-bar__main\">\n\n <!-- Free-text search (left, optional) -->\n @if (showSearch()) {\n <div class=\"c-search\">\n <mat-icon class=\"c-search-icon\" fontSet=\"material-icons-outlined\">search</mat-icon>\n <input\n class=\"c-search-input\"\n type=\"text\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"setSearch($event)\"\n (keydown.enter)=\"triggerApply()\"\n placeholder=\"T\u00ECm ki\u1EBFm...\" />\n </div>\n <span class=\"c-divider\" aria-hidden=\"true\"></span>\n }\n\n <!-- Field switcher (used by chipPopover header) \u2014 declared first so chipPopover's\n nested ng-template can resolve the template ref. -->\n <!-- Field switcher used by chipPopover header \u2014 current field key comes from editingIndex. -->\n <sd-query-field-picker\n #switchPicker\n [fields]=\"_fields\"\n [currentKey]=\"editingIndex() !== null ? $any(filters()[editingIndex()!])?.field : undefined\"\n (pick)=\"editingIndex() !== null && changeFilterField(editingIndex()!, $event)\" />\n\n <!-- Chip popover \u2014 extracted into <sd-query-chip-popover>. Declared BEFORE chips\n so the popover-mode @for chips can bind their [menu] to `chipPopover.menu()`. -->\n <sd-query-chip-popover\n #chipPopover\n [field]=\"editingField() ?? undefined\"\n [filter]=\"editingIndex() !== null ? $any(filters()[editingIndex()!]) : undefined\"\n [chipIndex]=\"editingIndex()\"\n [autoIdBase]=\"autoIdInput() ?? 'qb'\"\n [switchPickerMenu]=\"switchPicker.menu()!\"\n (commit)=\"onChipPopoverCommit($event)\"\n (swapField)=\"editingIndex() !== null && changeFilterField(editingIndex()!, $event)\" />\n\n @if (mode() === 'inline') {\n <!-- ===== INLINE MODE (GitLab-style token builder) ===== -->\n\n <!-- Completed chips -->\n @for (filter of _filters; track $index; let i = $index) {\n @if (_showOr && i > 0) {\n <span class=\"c-or-connector\" aria-hidden=\"true\">OR</span>\n }\n @let _field = fieldByKey()[$any(filter).field];\n @if (_field) {\n @let _op = $any(filter.operator);\n @let _data = $any(filter).data;\n @if (_field.type === 'string' || _field.type === 'number') {\n <!-- Seamless chip \u2014 the pill is the input (string / number). -->\n <sd-query-inline-value-chip\n [field]=\"_field\"\n [operator]=\"_op\"\n [value]=\"_data\"\n [density]=\"_density\"\n [showOperator]=\"showOperatorOnChip()\"\n [autoId]=\"inlineAutoId(i, 'value')\"\n (valueChange)=\"updateFilter(i, { data: $event })\"\n (remove)=\"removeFilter(i)\" />\n } @else {\n <sd-query-inline-chip\n [field]=\"_field\"\n [filter]=\"filter\"\n [density]=\"_density\"\n [multiple]=\"multiple(_op)\"\n [showOperator]=\"showOperatorOnChip()\"\n [autoId]=\"inlineAutoId(i, 'value')\"\n [valueText]=\"chipValueText(filter)\"\n [isNoData]=\"isNoDataOperator(_op)\"\n (commit)=\"updateFilter(i, { data: $event })\"\n (commitRange)=\"setFilterRange(i, $event)\"\n (liveChange)=\"updateFilter(i, { data: $event })\"\n (remove)=\"removeFilter(i)\" />\n }\n }\n }\n\n <!-- Building chip (in-progress) \u2014 extracted into <sd-query-build-chip>. -->\n @if (building(); as _b) {\n <sd-query-build-chip\n [building]=\"_b\"\n [density]=\"_density\"\n [allowedOperators]=\"allowedOperatorsFor(_b.field)\"\n [multiple]=\"multiple($any(_b.operator))\"\n [showOperator]=\"showOperatorOnChip()\"\n (pickOperator)=\"pickBuildOperator($any($event))\"\n (commitValue)=\"commitBuildValue($event)\"\n (cancel)=\"cancelBuild()\"\n (seamlessCommit)=\"onBuildSeamlessCommit($event)\" />\n }\n\n <!-- Add filter -->\n <button\n type=\"button\"\n class=\"c-add-filter\"\n [matMenuTriggerFor]=\"addPicker.menu()\"\n [disabled]=\"_fields.length === 0 || _used.size >= _fields.length\"\n [matTooltip]=\"_fields.length === 0 ? 'Ch\u01B0a c\u1EA5u h\u00ECnh fields' : 'Th\u00EAm filter'\">\n <mat-icon fontSet=\"material-icons-outlined\">add</mat-icon>\n </button>\n } @else {\n <!-- ===== POPOVER MODE (default) \u2014 compact chips edited via mat-menu ===== -->\n @for (filter of _filters; track $index; let i = $index) {\n @if (_showOr && i > 0) {\n <span class=\"c-or-connector\" aria-hidden=\"true\">OR</span>\n }\n @let _field = fieldByKey()[$any(filter).field];\n @let _active = isFilterActive(filter);\n <!-- why: menu close commit is handled by chipPopover's (commit) \u2192\n onChipPopoverCommit; popover-chip only signals open + remove here. -->\n <sd-query-popover-chip\n [field]=\"_field\"\n [filter]=\"filter\"\n [active]=\"_active\"\n [showOperator]=\"showOperatorOnChip()\"\n [valueText]=\"chipValueText(filter)\"\n [menu]=\"chipPopover.menu()!\"\n (open)=\"openChipPopover(i)\"\n (remove)=\"removeFilter(i)\" />\n }\n\n <!-- Add filter trigger -->\n <button\n type=\"button\"\n class=\"c-add-filter\"\n [matMenuTriggerFor]=\"addPicker.menu()\"\n [disabled]=\"_fields.length === 0 || _used.size >= _fields.length\"\n [matTooltip]=\"_fields.length === 0 ? 'Ch\u01B0a c\u1EA5u h\u00ECnh fields' : 'Th\u00EAm filter'\">\n <mat-icon fontSet=\"material-icons-outlined\">add</mat-icon>\n </button>\n }\n\n </div><!-- /.c-query-bar__main -->\n\n <!-- Field picker \u2014 list available fields, fade ones already in use. -->\n <sd-query-field-picker\n #addPicker\n [fields]=\"_fields\"\n [usedKeys]=\"_used\"\n (pick)=\"mode() === 'inline' ? beginBuild($event) : addFilter($event)\" />\n\n <sd-query-actions-bar\n [showLogicToggle]=\"showLogicToggle()\"\n [(logic)]=\"logic\"\n [filtersCount]=\"_filters.length\"\n [showSavedFilters]=\"showSavedFilters()\"\n [savedFiltersKey]=\"savedFiltersKey()\"\n [query]=\"currentQuery()\"\n (applyFilter)=\"onApplyFilter($event)\"\n [showClearAll]=\"showClearAll()\"\n [canSearch]=\"canSearch()\"\n (clear)=\"clearAll()\"\n (search)=\"triggerApply()\" />\n</div>\n", styles: [":host{display:block;font-family:Roboto,system-ui,sans-serif}.c-query-bar{display:flex;align-items:flex-start;flex-wrap:nowrap;gap:8px;background:#fff;border:1px solid #e2e4e7;border-radius:8px;box-shadow:0 1px 2px #0f172a0a}.c-query-bar.c-density-compact{padding:8px}.c-query-bar.c-density-comfortable{padding:10px}.c-query-bar__main{flex:1 1 auto;min-width:0;display:flex;align-items:center;flex-wrap:wrap;gap:6px}.c-search{display:flex;align-items:center;gap:6px;min-width:220px;padding:0 10px;border:1px solid #d6d8db;border-radius:6px;background:#fff}.c-density-compact .c-search{height:28px}.c-density-comfortable .c-search{height:32px}.c-search .c-search-icon{font-size:16px;width:16px;height:16px;color:#7a7a7a}.c-search .c-search-input{flex:1;border:none;outline:none;background:transparent;font-size:13px;color:#212121}.c-search .c-search-input::placeholder{color:#7a7a7a}.c-search .c-search-kbd{font-family:Roboto Mono,monospace;font-size:10px;padding:1px 4px;border:1px solid #e2e4e7;border-radius:3px;color:#7a7a7a}.c-divider{width:1px;height:16px;background:#e2e4e7;margin:0 2px}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}.c-density-compact .c-token{height:28px}.c-density-comfortable .c-token{height:32px}.c-token-icon{font-size:14px;width:14px;height:14px;line-height:14px;color:inherit}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-sep{color:#7a7a7a}.c-token-dash{color:#7a7a7a;padding:0 4px;line-height:1}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary);cursor:pointer}.c-token-value:hover{text-decoration:underline}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-input,.c-token-value-edit sd-input-number{display:inline-flex;max-width:140px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}.c-density-compact .c-token-remove{width:26px}.c-density-comfortable .c-token-remove{width:28px}.c-token-remove mat-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}.c-add-filter{display:inline-flex;align-items:center;justify-content:center;padding:0;border-radius:999px;border:1px dashed #d6d8db;background:transparent;cursor:pointer;color:#5f6368;transition:border-color .12s ease,color .12s ease,background-color .12s ease}.c-density-compact .c-add-filter{width:28px;height:28px}.c-density-comfortable .c-add-filter{width:32px;height:32px}.c-add-filter mat-icon{font-size:18px;width:18px;height:18px}.c-add-filter:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-add-filter[disabled]{opacity:.5;cursor:not-allowed}.c-or-connector{font-family:Roboto Mono,monospace;font-size:10px;font-weight:700;letter-spacing:.6px;color:#7a7a7a;padding:0 2px}::ng-deep .mat-mdc-menu-panel.c-chip-popover{min-width:280px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e2e4e7;font-size:13px;font-weight:500;color:#212121}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header mat-icon{font-size:16px;width:16px;height:16px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header .c-pop-header-operator{margin-left:auto}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 6px;margin-left:-4px;background:transparent;border:1px solid transparent;border-radius:4px;font:inherit;font-weight:500;color:#212121;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field mat-icon{font-size:14px!important;width:14px!important;height:14px!important;color:#7a7a7a!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field:hover{background:#f5f6f7;border-color:#e2e4e7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-body{padding:10px 12px;display:flex;flex-direction:column;gap:12px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input{width:100%;height:32px;padding:0 10px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font-family:inherit;font-size:13px;color:#212121;outline:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input:focus{border-color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-between{display:grid;grid-template-columns:1fr 1fr;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean{display:flex;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button{flex:1;height:32px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font:inherit;font-size:13px;cursor:pointer}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button.c-pop-boolean-active{background:var(--sd-primary-light);border-color:var(--sd-primary);color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values{max-height:220px;overflow-y:auto;margin:0;padding:0;list-style:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:4px;font-size:13px;cursor:pointer;-webkit-user-select:none;user-select:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li:hover{background:#f5f6f7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li.c-pop-value-selected{background:var(--sd-primary-light);color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li input[type=checkbox],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li input[type=radio]{accent-color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-loading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-empty{padding:10px 8px;font-size:12px;color:#7a7a7a}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-toggle ::ng-deep .mdc-button,.c-bool-toggle ::ng-deep button[mat-button],.c-bool-toggle ::ng-deep button[mat-stroked-button],.c-bool-toggle ::ng-deep button[mat-flat-button]{border-radius:999px;min-width:0;padding:0 12px}\n", ".c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__trailing{border-color:#d6d8db!important;transition:border-color .12s ease}.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__leading{border-radius:6px 0 0 6px!important}.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__trailing{border-radius:0 6px 6px 0!important}.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing{border-color:#b9bdc3!important}.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing{border-color:var(--sd-primary)!important;border-width:1px!important}.c-query-bar ::ng-deep .mat-mdc-form-field .mat-mdc-select-arrow,.c-query-bar ::ng-deep .mat-mdc-form-field .mat-mdc-form-field-icon-suffix .mat-icon{color:#5f6368}.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__trailing{border-color:#d6d8db!important;transition:border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__leading{border-radius:6px 0 0 6px!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__trailing{border-radius:0 6px 6px 0!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing{border-color:#b9bdc3!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing{border-color:var(--sd-primary)!important;border-width:1px!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mat-mdc-form-field-icon-suffix .mat-icon,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mat-mdc-form-field-icon-suffix .mat-icon{color:#5f6368}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--sd-primary)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "directive", type: i1.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: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SdQueryActionsBar, selector: "sd-query-actions-bar", inputs: ["showLogicToggle", "logic", "filtersCount", "showSavedFilters", "savedFiltersKey", "query", "showClearAll", "canSearch"], outputs: ["logicChange", "applyFilter", "clear", "search"] }, { kind: "component", type: SdQueryBuildChip, selector: "sd-query-build-chip", inputs: ["building", "density", "allowedOperators", "multiple", "showOperator", "autoId"], outputs: ["pickOperator", "commitValue", "cancel", "seamlessCommit", "draftChange", "draftCommit"] }, { kind: "component", type: SdQueryChipPopover, selector: "sd-query-chip-popover", inputs: ["field", "filter", "chipIndex", "autoIdBase", "switchPickerMenu"], outputs: ["commit", "swapField"] }, { kind: "component", type: SdQueryFieldPicker, selector: "sd-query-field-picker", inputs: ["fields", "currentKey", "usedKeys"], outputs: ["pick"] }, { kind: "component", type: SdQueryInlineChip, selector: "sd-query-inline-chip", inputs: ["field", "filter", "density", "multiple", "showOperator", "autoId", "valueText", "isNoData"], outputs: ["commit", "commitRange", "liveChange", "remove"] }, { kind: "component", type: SdQueryInlineValueChip, selector: "sd-query-inline-value-chip", inputs: ["field", "operator", "value", "density", "showOperator", "autoId", "autofocus"], outputs: ["valueChange", "remove"] }, { kind: "component", type: SdQueryPopoverChip, selector: "sd-query-popover-chip", inputs: ["field", "filter", "active", "showOperator", "valueText", "menu"], outputs: ["open", "remove"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1406
|
+
}
|
|
1407
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryBar, decorators: [{
|
|
1408
|
+
type: Component,
|
|
1409
|
+
args: [{ selector: 'sd-query-bar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
1410
|
+
CommonModule,
|
|
1411
|
+
FormsModule,
|
|
1412
|
+
MatIconModule,
|
|
1413
|
+
MatMenuModule,
|
|
1414
|
+
MatTooltipModule,
|
|
1415
|
+
SdQueryActionsBar,
|
|
1416
|
+
SdQueryBuildChip,
|
|
1417
|
+
SdQueryChipPopover,
|
|
1418
|
+
SdQueryFieldPicker,
|
|
1419
|
+
SdQueryInlineChip,
|
|
1420
|
+
SdQueryInlineValueChip,
|
|
1421
|
+
SdQueryPopoverChip,
|
|
1422
|
+
], template: "<!--\n sd-query-bar \u2014 unified filter chip row (Jira/Linear/Notion style).\n Visual spec: refs/design_handoff_sd_query_bar/README.md\n TODO(next-iter): replace inline placeholders for field-picker + chip-popover\n with extracted sub-components driven by CDK overlay.\n-->\n@let _filters = filters();\n@let _fields = fields();\n@let _density = density();\n@let _showOr = showOrConnector();\n@let _used = usedFieldKeys();\n\n<div\n class=\"c-query-bar\"\n [class.c-density-compact]=\"_density === 'compact'\"\n [class.c-density-comfortable]=\"_density === 'comfortable'\">\n\n <!-- Main region \u2014 search + filters + add-filter. Wraps internally; the right\n action toolbar stays anchored and does NOT flow with this. -->\n <div class=\"c-query-bar__main\">\n\n <!-- Free-text search (left, optional) -->\n @if (showSearch()) {\n <div class=\"c-search\">\n <mat-icon class=\"c-search-icon\" fontSet=\"material-icons-outlined\">search</mat-icon>\n <input\n class=\"c-search-input\"\n type=\"text\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"setSearch($event)\"\n (keydown.enter)=\"triggerApply()\"\n placeholder=\"T\u00ECm ki\u1EBFm...\" />\n </div>\n <span class=\"c-divider\" aria-hidden=\"true\"></span>\n }\n\n <!-- Field switcher (used by chipPopover header) \u2014 declared first so chipPopover's\n nested ng-template can resolve the template ref. -->\n <!-- Field switcher used by chipPopover header \u2014 current field key comes from editingIndex. -->\n <sd-query-field-picker\n #switchPicker\n [fields]=\"_fields\"\n [currentKey]=\"editingIndex() !== null ? $any(filters()[editingIndex()!])?.field : undefined\"\n (pick)=\"editingIndex() !== null && changeFilterField(editingIndex()!, $event)\" />\n\n <!-- Chip popover \u2014 extracted into <sd-query-chip-popover>. Declared BEFORE chips\n so the popover-mode @for chips can bind their [menu] to `chipPopover.menu()`. -->\n <sd-query-chip-popover\n #chipPopover\n [field]=\"editingField() ?? undefined\"\n [filter]=\"editingIndex() !== null ? $any(filters()[editingIndex()!]) : undefined\"\n [chipIndex]=\"editingIndex()\"\n [autoIdBase]=\"autoIdInput() ?? 'qb'\"\n [switchPickerMenu]=\"switchPicker.menu()!\"\n (commit)=\"onChipPopoverCommit($event)\"\n (swapField)=\"editingIndex() !== null && changeFilterField(editingIndex()!, $event)\" />\n\n @if (mode() === 'inline') {\n <!-- ===== INLINE MODE (GitLab-style token builder) ===== -->\n\n <!-- Completed chips -->\n @for (filter of _filters; track $index; let i = $index) {\n @if (_showOr && i > 0) {\n <span class=\"c-or-connector\" aria-hidden=\"true\">OR</span>\n }\n @let _field = fieldByKey()[$any(filter).field];\n @if (_field) {\n @let _op = $any(filter.operator);\n @let _data = $any(filter).data;\n @if (_field.type === 'string' || _field.type === 'number') {\n <!-- Seamless chip \u2014 the pill is the input (string / number). -->\n <sd-query-inline-value-chip\n [field]=\"_field\"\n [operator]=\"_op\"\n [value]=\"_data\"\n [density]=\"_density\"\n [showOperator]=\"showOperatorOnChip()\"\n [autoId]=\"inlineAutoId(i, 'value')\"\n (valueChange)=\"updateFilter(i, { data: $event })\"\n (remove)=\"removeFilter(i)\" />\n } @else {\n <sd-query-inline-chip\n [field]=\"_field\"\n [filter]=\"filter\"\n [density]=\"_density\"\n [multiple]=\"multiple(_op)\"\n [showOperator]=\"showOperatorOnChip()\"\n [autoId]=\"inlineAutoId(i, 'value')\"\n [valueText]=\"chipValueText(filter)\"\n [isNoData]=\"isNoDataOperator(_op)\"\n (commit)=\"updateFilter(i, { data: $event })\"\n (commitRange)=\"setFilterRange(i, $event)\"\n (liveChange)=\"updateFilter(i, { data: $event })\"\n (remove)=\"removeFilter(i)\" />\n }\n }\n }\n\n <!-- Building chip (in-progress) \u2014 extracted into <sd-query-build-chip>. -->\n @if (building(); as _b) {\n <sd-query-build-chip\n [building]=\"_b\"\n [density]=\"_density\"\n [allowedOperators]=\"allowedOperatorsFor(_b.field)\"\n [multiple]=\"multiple($any(_b.operator))\"\n [showOperator]=\"showOperatorOnChip()\"\n (pickOperator)=\"pickBuildOperator($any($event))\"\n (commitValue)=\"commitBuildValue($event)\"\n (cancel)=\"cancelBuild()\"\n (seamlessCommit)=\"onBuildSeamlessCommit($event)\" />\n }\n\n <!-- Add filter -->\n <button\n type=\"button\"\n class=\"c-add-filter\"\n [matMenuTriggerFor]=\"addPicker.menu()\"\n [disabled]=\"_fields.length === 0 || _used.size >= _fields.length\"\n [matTooltip]=\"_fields.length === 0 ? 'Ch\u01B0a c\u1EA5u h\u00ECnh fields' : 'Th\u00EAm filter'\">\n <mat-icon fontSet=\"material-icons-outlined\">add</mat-icon>\n </button>\n } @else {\n <!-- ===== POPOVER MODE (default) \u2014 compact chips edited via mat-menu ===== -->\n @for (filter of _filters; track $index; let i = $index) {\n @if (_showOr && i > 0) {\n <span class=\"c-or-connector\" aria-hidden=\"true\">OR</span>\n }\n @let _field = fieldByKey()[$any(filter).field];\n @let _active = isFilterActive(filter);\n <!-- why: menu close commit is handled by chipPopover's (commit) \u2192\n onChipPopoverCommit; popover-chip only signals open + remove here. -->\n <sd-query-popover-chip\n [field]=\"_field\"\n [filter]=\"filter\"\n [active]=\"_active\"\n [showOperator]=\"showOperatorOnChip()\"\n [valueText]=\"chipValueText(filter)\"\n [menu]=\"chipPopover.menu()!\"\n (open)=\"openChipPopover(i)\"\n (remove)=\"removeFilter(i)\" />\n }\n\n <!-- Add filter trigger -->\n <button\n type=\"button\"\n class=\"c-add-filter\"\n [matMenuTriggerFor]=\"addPicker.menu()\"\n [disabled]=\"_fields.length === 0 || _used.size >= _fields.length\"\n [matTooltip]=\"_fields.length === 0 ? 'Ch\u01B0a c\u1EA5u h\u00ECnh fields' : 'Th\u00EAm filter'\">\n <mat-icon fontSet=\"material-icons-outlined\">add</mat-icon>\n </button>\n }\n\n </div><!-- /.c-query-bar__main -->\n\n <!-- Field picker \u2014 list available fields, fade ones already in use. -->\n <sd-query-field-picker\n #addPicker\n [fields]=\"_fields\"\n [usedKeys]=\"_used\"\n (pick)=\"mode() === 'inline' ? beginBuild($event) : addFilter($event)\" />\n\n <sd-query-actions-bar\n [showLogicToggle]=\"showLogicToggle()\"\n [(logic)]=\"logic\"\n [filtersCount]=\"_filters.length\"\n [showSavedFilters]=\"showSavedFilters()\"\n [savedFiltersKey]=\"savedFiltersKey()\"\n [query]=\"currentQuery()\"\n (applyFilter)=\"onApplyFilter($event)\"\n [showClearAll]=\"showClearAll()\"\n [canSearch]=\"canSearch()\"\n (clear)=\"clearAll()\"\n (search)=\"triggerApply()\" />\n</div>\n", styles: [":host{display:block;font-family:Roboto,system-ui,sans-serif}.c-query-bar{display:flex;align-items:flex-start;flex-wrap:nowrap;gap:8px;background:#fff;border:1px solid #e2e4e7;border-radius:8px;box-shadow:0 1px 2px #0f172a0a}.c-query-bar.c-density-compact{padding:8px}.c-query-bar.c-density-comfortable{padding:10px}.c-query-bar__main{flex:1 1 auto;min-width:0;display:flex;align-items:center;flex-wrap:wrap;gap:6px}.c-search{display:flex;align-items:center;gap:6px;min-width:220px;padding:0 10px;border:1px solid #d6d8db;border-radius:6px;background:#fff}.c-density-compact .c-search{height:28px}.c-density-comfortable .c-search{height:32px}.c-search .c-search-icon{font-size:16px;width:16px;height:16px;color:#7a7a7a}.c-search .c-search-input{flex:1;border:none;outline:none;background:transparent;font-size:13px;color:#212121}.c-search .c-search-input::placeholder{color:#7a7a7a}.c-search .c-search-kbd{font-family:Roboto Mono,monospace;font-size:10px;padding:1px 4px;border:1px solid #e2e4e7;border-radius:3px;color:#7a7a7a}.c-divider{width:1px;height:16px;background:#e2e4e7;margin:0 2px}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}.c-density-compact .c-token{height:28px}.c-density-comfortable .c-token{height:32px}.c-token-icon{font-size:14px;width:14px;height:14px;line-height:14px;color:inherit}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-sep{color:#7a7a7a}.c-token-dash{color:#7a7a7a;padding:0 4px;line-height:1}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary);cursor:pointer}.c-token-value:hover{text-decoration:underline}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-input,.c-token-value-edit sd-input-number{display:inline-flex;max-width:140px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}.c-density-compact .c-token-remove{width:26px}.c-density-comfortable .c-token-remove{width:28px}.c-token-remove mat-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}.c-add-filter{display:inline-flex;align-items:center;justify-content:center;padding:0;border-radius:999px;border:1px dashed #d6d8db;background:transparent;cursor:pointer;color:#5f6368;transition:border-color .12s ease,color .12s ease,background-color .12s ease}.c-density-compact .c-add-filter{width:28px;height:28px}.c-density-comfortable .c-add-filter{width:32px;height:32px}.c-add-filter mat-icon{font-size:18px;width:18px;height:18px}.c-add-filter:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-add-filter[disabled]{opacity:.5;cursor:not-allowed}.c-or-connector{font-family:Roboto Mono,monospace;font-size:10px;font-weight:700;letter-spacing:.6px;color:#7a7a7a;padding:0 2px}::ng-deep .mat-mdc-menu-panel.c-chip-popover{min-width:280px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e2e4e7;font-size:13px;font-weight:500;color:#212121}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header mat-icon{font-size:16px;width:16px;height:16px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header .c-pop-header-operator{margin-left:auto}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 6px;margin-left:-4px;background:transparent;border:1px solid transparent;border-radius:4px;font:inherit;font-weight:500;color:#212121;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field mat-icon{font-size:14px!important;width:14px!important;height:14px!important;color:#7a7a7a!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field:hover{background:#f5f6f7;border-color:#e2e4e7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-body{padding:10px 12px;display:flex;flex-direction:column;gap:12px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input{width:100%;height:32px;padding:0 10px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font-family:inherit;font-size:13px;color:#212121;outline:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input:focus{border-color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-between{display:grid;grid-template-columns:1fr 1fr;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean{display:flex;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button{flex:1;height:32px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font:inherit;font-size:13px;cursor:pointer}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button.c-pop-boolean-active{background:var(--sd-primary-light);border-color:var(--sd-primary);color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values{max-height:220px;overflow-y:auto;margin:0;padding:0;list-style:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:4px;font-size:13px;cursor:pointer;-webkit-user-select:none;user-select:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li:hover{background:#f5f6f7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li.c-pop-value-selected{background:var(--sd-primary-light);color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li input[type=checkbox],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li input[type=radio]{accent-color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-loading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-empty{padding:10px 8px;font-size:12px;color:#7a7a7a}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-toggle ::ng-deep .mdc-button,.c-bool-toggle ::ng-deep button[mat-button],.c-bool-toggle ::ng-deep button[mat-stroked-button],.c-bool-toggle ::ng-deep button[mat-flat-button]{border-radius:999px;min-width:0;padding:0 12px}\n", ".c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__trailing{border-color:#d6d8db!important;transition:border-color .12s ease}.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__leading{border-radius:6px 0 0 6px!important}.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__trailing{border-radius:0 6px 6px 0!important}.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing{border-color:#b9bdc3!important}.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing{border-color:var(--sd-primary)!important;border-width:1px!important}.c-query-bar ::ng-deep .mat-mdc-form-field .mat-mdc-select-arrow,.c-query-bar ::ng-deep .mat-mdc-form-field .mat-mdc-form-field-icon-suffix .mat-icon{color:#5f6368}.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__trailing{border-color:#d6d8db!important;transition:border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__leading{border-radius:6px 0 0 6px!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__trailing{border-radius:0 6px 6px 0!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing{border-color:#b9bdc3!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing{border-color:var(--sd-primary)!important;border-width:1px!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mat-mdc-form-field-icon-suffix .mat-icon,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mat-mdc-form-field-icon-suffix .mat-icon{color:#5f6368}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--sd-primary)}\n"] }]
|
|
1423
|
+
}] });
|
|
1424
|
+
|
|
1425
|
+
/**
|
|
1426
|
+
* Generated bundle index. Do not edit.
|
|
1427
|
+
*/
|
|
1428
|
+
|
|
1429
|
+
export { SD_QUERY_DEFAULT_OPERATOR_BY_TYPE, SD_QUERY_MULTI_OPERATORS, SD_QUERY_NO_DATA_OPERATORS, SD_QUERY_OPERATORS_BY_TYPE, SD_QUERY_TYPE_ICON, SdQueryBar, sdQueryAllowedOperators, sdQueryDefaultOperator, sdQueryFieldIcon, sdQueryShowOperatorSelector };
|
|
1430
|
+
//# sourceMappingURL=sdcorejs-angular-components-query-bar.mjs.map
|