@caribeng/ui 22.1.1 → 22.4.0
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 +134 -12
- package/fesm2022/caribeng-ui-accordion.mjs +4 -4
- package/fesm2022/caribeng-ui-anchor.mjs +169 -0
- package/fesm2022/caribeng-ui-autocomplete.mjs +10 -8
- package/fesm2022/caribeng-ui-avatar.mjs +2 -2
- package/fesm2022/caribeng-ui-avatargroup.mjs +9 -3
- package/fesm2022/caribeng-ui-badge.mjs +8 -2
- package/fesm2022/caribeng-ui-blockui.mjs +12 -4
- package/fesm2022/caribeng-ui-breadcrumb.mjs +17 -7
- package/fesm2022/caribeng-ui-button.mjs +22 -2
- package/fesm2022/caribeng-ui-caribe-alert.mjs +5 -2
- package/fesm2022/caribeng-ui-caribe-animation-lottie.mjs +6 -3
- package/fesm2022/caribeng-ui-caribe-button-group.mjs +6 -3
- package/fesm2022/caribeng-ui-caribe-calendar.mjs +11 -6
- package/fesm2022/caribeng-ui-caribe-card-content.mjs +6 -3
- package/fesm2022/caribeng-ui-caribe-card-move.mjs +29 -7
- package/fesm2022/caribeng-ui-caribe-card-select.mjs +56 -14
- package/fesm2022/caribeng-ui-caribe-circle-loading.mjs +10 -7
- package/fesm2022/caribeng-ui-caribe-coachmark.mjs +42 -19
- package/fesm2022/caribeng-ui-caribe-data-table.mjs +15 -9
- package/fesm2022/caribeng-ui-caribe-dots-indicator.mjs +38 -5
- package/fesm2022/caribeng-ui-caribe-expansion-panel.mjs +16 -5
- package/fesm2022/caribeng-ui-caribe-fab-menu.mjs +56 -7
- package/fesm2022/caribeng-ui-caribe-file-download.mjs +68 -27
- package/fesm2022/caribeng-ui-caribe-file-select.mjs +22 -17
- package/fesm2022/caribeng-ui-caribe-float-menu.mjs +6 -3
- package/fesm2022/caribeng-ui-caribe-footer.mjs +8 -4
- package/fesm2022/caribeng-ui-caribe-header.mjs +6 -3
- package/fesm2022/caribeng-ui-caribe-icon-button.mjs +6 -3
- package/fesm2022/caribeng-ui-caribe-illustration.mjs +6 -3
- package/fesm2022/caribeng-ui-caribe-inline-alert.mjs +5 -2
- package/fesm2022/caribeng-ui-caribe-input-search.mjs +14 -10
- package/fesm2022/caribeng-ui-caribe-input-select.mjs +12 -10
- package/fesm2022/caribeng-ui-caribe-input-token.mjs +61 -11
- package/fesm2022/caribeng-ui-caribe-link.mjs +7 -2
- package/fesm2022/caribeng-ui-caribe-loader.mjs +8 -5
- package/fesm2022/caribeng-ui-caribe-modal.mjs +32 -16
- package/fesm2022/caribeng-ui-caribe-off-canvas.mjs +26 -12
- package/fesm2022/caribeng-ui-caribe-onboarding.mjs +44 -19
- package/fesm2022/caribeng-ui-caribe-pictogram.mjs +6 -3
- package/fesm2022/caribeng-ui-caribe-pop-over.mjs +34 -10
- package/fesm2022/caribeng-ui-caribe-preloader.mjs +9 -6
- package/fesm2022/caribeng-ui-caribe-progress-bar.mjs +5 -2
- package/fesm2022/caribeng-ui-caribe-shortcut.mjs +8 -3
- package/fesm2022/caribeng-ui-caribe-step-indicator.mjs +12 -5
- package/fesm2022/caribeng-ui-caribe-stroke.mjs +6 -3
- package/fesm2022/caribeng-ui-caribe-switch.mjs +5 -3
- package/fesm2022/caribeng-ui-caribe-tag-button.mjs +1 -1
- package/fesm2022/caribeng-ui-caribe-tracker.mjs +5 -2
- package/fesm2022/caribeng-ui-caribe-transaction-status.mjs +1 -1
- package/fesm2022/caribeng-ui-caribe-voucher.mjs +11 -8
- package/fesm2022/caribeng-ui-carousel.mjs +49 -14
- package/fesm2022/caribeng-ui-cascadeselect.mjs +22 -8
- package/fesm2022/caribeng-ui-chart.mjs +2601 -303
- package/fesm2022/caribeng-ui-chat.mjs +1303 -0
- package/fesm2022/caribeng-ui-chip.mjs +9 -4
- package/fesm2022/caribeng-ui-chips.mjs +9 -5
- package/fesm2022/caribeng-ui-code-block.mjs +332 -0
- package/fesm2022/caribeng-ui-colorpicker.mjs +107 -11
- package/fesm2022/caribeng-ui-command-palette.mjs +476 -0
- package/fesm2022/caribeng-ui-confirmdialog.mjs +140 -10
- package/fesm2022/caribeng-ui-confirmpopup.mjs +209 -9
- package/fesm2022/caribeng-ui-contextmenu.mjs +13 -6
- package/fesm2022/caribeng-ui-copy-button.mjs +230 -0
- package/fesm2022/caribeng-ui-dataview.mjs +214 -6
- package/fesm2022/caribeng-ui-datepicker.mjs +1630 -393
- package/fesm2022/caribeng-ui-descriptions.mjs +124 -0
- package/fesm2022/caribeng-ui-dialog.mjs +10 -7
- package/fesm2022/caribeng-ui-dock.mjs +58 -15
- package/fesm2022/caribeng-ui-drawer.mjs +17 -9
- package/fesm2022/caribeng-ui-dynamicdialog.mjs +210 -6
- package/fesm2022/caribeng-ui-editor.mjs +4413 -243
- package/fesm2022/caribeng-ui-empty.mjs +80 -0
- package/fesm2022/caribeng-ui-fieldset.mjs +2 -2
- package/fesm2022/caribeng-ui-file-card.mjs +136 -19
- package/fesm2022/caribeng-ui-fileupload.mjs +96 -19
- package/fesm2022/caribeng-ui-floatlabel.mjs +4 -2
- package/fesm2022/caribeng-ui-folder-card.mjs +132 -15
- package/fesm2022/caribeng-ui-galleria.mjs +76 -45
- package/fesm2022/caribeng-ui-image.mjs +52 -29
- package/fesm2022/caribeng-ui-inlinemessage.mjs +2 -2
- package/fesm2022/caribeng-ui-inplace.mjs +22 -6
- package/fesm2022/caribeng-ui-input-number.mjs +9 -7
- package/fesm2022/caribeng-ui-inputgroup.mjs +2 -2
- package/fesm2022/caribeng-ui-inputmask.mjs +22 -13
- package/fesm2022/caribeng-ui-inputotp.mjs +12 -6
- package/fesm2022/caribeng-ui-kanban.mjs +4612 -0
- package/fesm2022/caribeng-ui-kbd.mjs +122 -0
- package/fesm2022/caribeng-ui-keyfilter.mjs +72 -0
- package/fesm2022/caribeng-ui-listbox.mjs +13 -11
- package/fesm2022/caribeng-ui-megamenu.mjs +228 -32
- package/fesm2022/caribeng-ui-mentions.mjs +495 -0
- package/fesm2022/caribeng-ui-menu.mjs +18 -8
- package/fesm2022/caribeng-ui-menubar.mjs +17 -8
- package/fesm2022/caribeng-ui-message.mjs +13 -7
- package/fesm2022/caribeng-ui-metergroup.mjs +29 -11
- package/fesm2022/caribeng-ui-multiselect.mjs +399 -97
- package/fesm2022/caribeng-ui-orderlist.mjs +230 -11
- package/fesm2022/caribeng-ui-organizationchart.mjs +16 -15
- package/fesm2022/caribeng-ui-paginator.mjs +18 -15
- package/fesm2022/caribeng-ui-panel.mjs +11 -8
- package/fesm2022/caribeng-ui-panelmenu.mjs +14 -8
- package/fesm2022/caribeng-ui-password.mjs +8 -6
- package/fesm2022/caribeng-ui-pdfviewer.mjs +1553 -0
- package/fesm2022/caribeng-ui-phone-input.mjs +688 -0
- package/fesm2022/caribeng-ui-picklist.mjs +251 -14
- package/fesm2022/caribeng-ui-pricing.mjs +272 -75
- package/fesm2022/caribeng-ui-qrcode.mjs +564 -0
- package/fesm2022/caribeng-ui-rating.mjs +7 -3
- package/fesm2022/caribeng-ui-repeater.mjs +223 -0
- package/fesm2022/caribeng-ui-result.mjs +91 -0
- package/fesm2022/caribeng-ui-ripple.mjs +2 -0
- package/fesm2022/caribeng-ui-scrollpanel.mjs +6 -3
- package/fesm2022/caribeng-ui-scrolltop.mjs +9 -4
- package/fesm2022/caribeng-ui-select.mjs +379 -87
- package/fesm2022/caribeng-ui-selectbutton.mjs +2 -2
- package/fesm2022/caribeng-ui-sidebar.mjs +573 -0
- package/fesm2022/caribeng-ui-slider.mjs +171 -75
- package/fesm2022/caribeng-ui-speeddial.mjs +7 -4
- package/fesm2022/caribeng-ui-spinner.mjs +5 -2
- package/fesm2022/caribeng-ui-splitbutton.mjs +163 -0
- package/fesm2022/caribeng-ui-splitter.mjs +5 -2
- package/fesm2022/caribeng-ui-statistic.mjs +183 -0
- package/fesm2022/caribeng-ui-stepper.mjs +17 -7
- package/fesm2022/caribeng-ui-steps.mjs +6 -3
- package/fesm2022/caribeng-ui-table.mjs +716 -84
- package/fesm2022/caribeng-ui-tabmenu.mjs +13 -6
- package/fesm2022/caribeng-ui-tag.mjs +2 -2
- package/fesm2022/caribeng-ui-terminal.mjs +45 -9
- package/fesm2022/caribeng-ui-themetoggle.mjs +82 -0
- package/fesm2022/caribeng-ui-tieredmenu.mjs +113 -20
- package/fesm2022/caribeng-ui-timeline.mjs +175 -6
- package/fesm2022/caribeng-ui-toast.mjs +7 -4
- package/fesm2022/caribeng-ui-togglebutton.mjs +8 -6
- package/fesm2022/caribeng-ui-toolbar.mjs +6 -3
- package/fesm2022/caribeng-ui-tree.mjs +302 -6
- package/fesm2022/caribeng-ui-treeselect.mjs +26 -10
- package/fesm2022/caribeng-ui-treetable.mjs +269 -6
- package/fesm2022/caribeng-ui-virtualscroller.mjs +123 -10
- package/fesm2022/caribeng-ui.mjs +1216 -6
- package/package.json +93 -3
- package/schematics/collection.json +15 -0
- package/schematics/migrate-from-primeng/core-icons.json +1 -0
- package/schematics/migrate-from-primeng/icons.js +269 -0
- package/schematics/migrate-from-primeng/index.js +290 -0
- package/schematics/migrate-from-primeng/mappings/button.js +93 -0
- package/schematics/migrate-from-primeng/mappings/card.js +33 -0
- package/schematics/migrate-from-primeng/mappings/checkbox.js +44 -0
- package/schematics/migrate-from-primeng/mappings/confirm.js +221 -0
- package/schematics/migrate-from-primeng/mappings/datepicker.js +121 -0
- package/schematics/migrate-from-primeng/mappings/dialog.js +84 -0
- package/schematics/migrate-from-primeng/mappings/index.js +88 -0
- package/schematics/migrate-from-primeng/mappings/inputnumber.js +62 -0
- package/schematics/migrate-from-primeng/mappings/inputtext.js +60 -0
- package/schematics/migrate-from-primeng/mappings/menu.js +468 -0
- package/schematics/migrate-from-primeng/mappings/multiselect.js +49 -0
- package/schematics/migrate-from-primeng/mappings/radiobutton.js +22 -0
- package/schematics/migrate-from-primeng/mappings/rules.js +206 -0
- package/schematics/migrate-from-primeng/mappings/select.js +97 -0
- package/schematics/migrate-from-primeng/mappings/service-calls.js +100 -0
- package/schematics/migrate-from-primeng/mappings/table.js +613 -0
- package/schematics/migrate-from-primeng/mappings/tabs.js +225 -0
- package/schematics/migrate-from-primeng/mappings/tag.js +29 -0
- package/schematics/migrate-from-primeng/mappings/textarea.js +44 -0
- package/schematics/migrate-from-primeng/mappings/toast.js +173 -0
- package/schematics/migrate-from-primeng/mappings/toggleswitch.js +37 -0
- package/schematics/migrate-from-primeng/mappings/tooltip.js +44 -0
- package/schematics/migrate-from-primeng/mappings/types.js +10 -0
- package/schematics/migrate-from-primeng/report.js +79 -0
- package/schematics/migrate-from-primeng/schema.js +2 -0
- package/schematics/migrate-from-primeng/schema.json +34 -0
- package/schematics/migrate-from-primeng/template.js +387 -0
- package/schematics/migrate-from-primeng/todo.js +5 -0
- package/schematics/migrate-from-primeng/typescript.js +442 -0
- package/schematics/migrate-from-primeng/workspace.js +117 -0
- package/schematics/ng-add/index.js +140 -0
- package/schematics/ng-add/schema.js +2 -0
- package/schematics/ng-add/schema.json +66 -0
- package/types/caribeng-ui-anchor.d.ts +50 -0
- package/types/caribeng-ui-autocomplete.d.ts +4 -2
- package/types/caribeng-ui-avatargroup.d.ts +3 -1
- package/types/caribeng-ui-badge.d.ts +3 -1
- package/types/caribeng-ui-blockui.d.ts +9 -4
- package/types/caribeng-ui-breadcrumb.d.ts +11 -0
- package/types/caribeng-ui-button.d.ts +9 -1
- package/types/caribeng-ui-caribe-alert.d.ts +3 -0
- package/types/caribeng-ui-caribe-animation-lottie.d.ts +4 -1
- package/types/caribeng-ui-caribe-button-group.d.ts +4 -1
- package/types/caribeng-ui-caribe-calendar.d.ts +5 -1
- package/types/caribeng-ui-caribe-card-content.d.ts +4 -1
- package/types/caribeng-ui-caribe-card-move.d.ts +13 -13
- package/types/caribeng-ui-caribe-card-select.d.ts +10 -1
- package/types/caribeng-ui-caribe-circle-loading.d.ts +4 -1
- package/types/caribeng-ui-caribe-coachmark.d.ts +6 -0
- package/types/caribeng-ui-caribe-data-table.d.ts +7 -2
- package/types/caribeng-ui-caribe-dots-indicator.d.ts +9 -0
- package/types/caribeng-ui-caribe-expansion-panel.d.ts +7 -10
- package/types/caribeng-ui-caribe-fab-menu.d.ts +10 -1
- package/types/caribeng-ui-caribe-file-download.d.ts +8 -10
- package/types/caribeng-ui-caribe-file-select.d.ts +7 -1
- package/types/caribeng-ui-caribe-float-menu.d.ts +4 -1
- package/types/caribeng-ui-caribe-header.d.ts +3 -0
- package/types/caribeng-ui-caribe-icon-button.d.ts +4 -1
- package/types/caribeng-ui-caribe-illustration.d.ts +4 -1
- package/types/caribeng-ui-caribe-inline-alert.d.ts +3 -0
- package/types/caribeng-ui-caribe-input-search.d.ts +4 -2
- package/types/caribeng-ui-caribe-input-select.d.ts +4 -2
- package/types/caribeng-ui-caribe-input-token.d.ts +16 -1
- package/types/caribeng-ui-caribe-link.d.ts +3 -0
- package/types/caribeng-ui-caribe-loader.d.ts +4 -1
- package/types/caribeng-ui-caribe-modal.d.ts +7 -10
- package/types/caribeng-ui-caribe-off-canvas.d.ts +6 -1
- package/types/caribeng-ui-caribe-onboarding.d.ts +6 -0
- package/types/caribeng-ui-caribe-pictogram.d.ts +4 -1
- package/types/caribeng-ui-caribe-pop-over.d.ts +7 -10
- package/types/caribeng-ui-caribe-preloader.d.ts +4 -1
- package/types/caribeng-ui-caribe-progress-bar.d.ts +3 -0
- package/types/caribeng-ui-caribe-shortcut.d.ts +3 -1
- package/types/caribeng-ui-caribe-step-indicator.d.ts +6 -0
- package/types/caribeng-ui-caribe-stroke.d.ts +4 -1
- package/types/caribeng-ui-caribe-switch.d.ts +3 -1
- package/types/caribeng-ui-caribe-tracker.d.ts +3 -0
- package/types/caribeng-ui-caribe-voucher.d.ts +5 -2
- package/types/caribeng-ui-carousel.d.ts +10 -1
- package/types/caribeng-ui-cascadeselect.d.ts +7 -2
- package/types/caribeng-ui-chart.d.ts +471 -44
- package/types/caribeng-ui-chat.d.ts +299 -0
- package/types/caribeng-ui-chip.d.ts +6 -0
- package/types/caribeng-ui-chips.d.ts +6 -2
- package/types/caribeng-ui-code-block.d.ts +65 -0
- package/types/caribeng-ui-colorpicker.d.ts +8 -2
- package/types/caribeng-ui-command-palette.d.ts +133 -0
- package/types/caribeng-ui-confirmdialog.d.ts +57 -6
- package/types/caribeng-ui-confirmpopup.d.ts +74 -5
- package/types/caribeng-ui-contextmenu.d.ts +11 -1
- package/types/caribeng-ui-copy-button.d.ts +90 -0
- package/types/caribeng-ui-dataview.d.ts +65 -5
- package/types/caribeng-ui-datepicker.d.ts +186 -21
- package/types/caribeng-ui-descriptions.d.ts +63 -0
- package/types/caribeng-ui-dialog.d.ts +4 -1
- package/types/caribeng-ui-dock.d.ts +17 -2
- package/types/caribeng-ui-drawer.d.ts +7 -2
- package/types/caribeng-ui-dynamicdialog.d.ts +93 -3
- package/types/caribeng-ui-editor.d.ts +486 -13
- package/types/caribeng-ui-empty.d.ts +34 -0
- package/types/caribeng-ui-file-card.d.ts +28 -3
- package/types/caribeng-ui-fileupload.d.ts +27 -4
- package/types/caribeng-ui-folder-card.d.ts +31 -6
- package/types/caribeng-ui-galleria.d.ts +9 -0
- package/types/caribeng-ui-image.d.ts +10 -0
- package/types/caribeng-ui-inplace.d.ts +8 -0
- package/types/caribeng-ui-input-number.d.ts +3 -1
- package/types/caribeng-ui-inputmask.d.ts +8 -1
- package/types/caribeng-ui-inputotp.d.ts +7 -2
- package/types/caribeng-ui-kanban.d.ts +783 -0
- package/types/caribeng-ui-kbd.d.ts +36 -0
- package/types/caribeng-ui-keyfilter.d.ts +30 -0
- package/types/caribeng-ui-listbox.d.ts +5 -3
- package/types/caribeng-ui-megamenu.d.ts +37 -2
- package/types/caribeng-ui-mentions.d.ts +127 -0
- package/types/caribeng-ui-menu.d.ts +14 -2
- package/types/caribeng-ui-menubar.d.ts +12 -1
- package/types/caribeng-ui-message.d.ts +5 -0
- package/types/caribeng-ui-metergroup.d.ts +9 -1
- package/types/caribeng-ui-multiselect.d.ts +71 -10
- package/types/caribeng-ui-orderlist.d.ts +58 -6
- package/types/caribeng-ui-organizationchart.d.ts +7 -1
- package/types/caribeng-ui-paginator.d.ts +3 -0
- package/types/caribeng-ui-panel.d.ts +3 -0
- package/types/caribeng-ui-panelmenu.d.ts +11 -1
- package/types/caribeng-ui-password.d.ts +4 -2
- package/types/caribeng-ui-pdfviewer.d.ts +258 -0
- package/types/caribeng-ui-phone-input.d.ts +168 -0
- package/types/caribeng-ui-picklist.d.ts +67 -7
- package/types/caribeng-ui-pricing.d.ts +49 -9
- package/types/caribeng-ui-qrcode.d.ts +88 -0
- package/types/caribeng-ui-rating.d.ts +6 -2
- package/types/caribeng-ui-repeater.d.ts +79 -0
- package/types/caribeng-ui-result.d.ts +35 -0
- package/types/caribeng-ui-scrollpanel.d.ts +4 -1
- package/types/caribeng-ui-scrolltop.d.ts +4 -1
- package/types/caribeng-ui-select.d.ts +73 -9
- package/types/caribeng-ui-sidebar.d.ts +167 -0
- package/types/caribeng-ui-slider.d.ts +63 -10
- package/types/caribeng-ui-speeddial.d.ts +5 -2
- package/types/caribeng-ui-spinner.d.ts +3 -0
- package/types/caribeng-ui-splitbutton.d.ts +49 -0
- package/types/caribeng-ui-splitter.d.ts +3 -0
- package/types/caribeng-ui-statistic.d.ts +65 -0
- package/types/caribeng-ui-stepper.d.ts +5 -0
- package/types/caribeng-ui-steps.d.ts +4 -1
- package/types/caribeng-ui-table.d.ts +185 -17
- package/types/caribeng-ui-tabmenu.d.ts +11 -1
- package/types/caribeng-ui-terminal.d.ts +10 -0
- package/types/caribeng-ui-themetoggle.d.ts +32 -0
- package/types/caribeng-ui-tieredmenu.d.ts +22 -3
- package/types/caribeng-ui-timeline.d.ts +63 -4
- package/types/caribeng-ui-toast.d.ts +3 -0
- package/types/caribeng-ui-togglebutton.d.ts +5 -3
- package/types/caribeng-ui-toolbar.d.ts +7 -4
- package/types/caribeng-ui-tree.d.ts +76 -4
- package/types/caribeng-ui-treeselect.d.ts +10 -2
- package/types/caribeng-ui-treetable.d.ts +68 -4
- package/types/caribeng-ui-virtualscroller.d.ts +45 -5
- package/types/caribeng-ui.d.ts +775 -12
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, model, signal, computed, viewChild, effect, forwardRef, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
2
|
+
import { inject, Injector, input, model, signal, computed, viewChild, effect, DestroyRef, afterNextRender, forwardRef, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
3
4
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
4
5
|
import * as i1 from '@angular/cdk/overlay';
|
|
5
6
|
import { OverlayModule } from '@angular/cdk/overlay';
|
|
6
7
|
import { CbIcon } from '@caribeng/ui/icon';
|
|
7
|
-
import { cbFieldState } from '@caribeng/ui';
|
|
8
|
+
import { injectCbTranslation, cbFormat, cbFieldState } from '@caribeng/ui';
|
|
8
9
|
|
|
9
10
|
let nextUid = 0;
|
|
11
|
+
/** Filas extra que se pintan por encima y por debajo de las visibles en modo virtual. */
|
|
12
|
+
const VIRTUAL_BUFFER = 4;
|
|
10
13
|
/**
|
|
11
14
|
* Selección múltiple (multiselect) de CaribeNG. Listbox accesible sobre CDK
|
|
12
|
-
* Overlay con casillas, filtro
|
|
13
|
-
*
|
|
15
|
+
* Overlay con casillas, filtro, seleccionar-todo, grupos, búsqueda al teclear
|
|
16
|
+
* y scroll virtual. Integrado con Angular Forms vía `ControlValueAccessor`;
|
|
17
|
+
* el modelo es un array de valores.
|
|
14
18
|
*
|
|
15
19
|
* @example
|
|
16
20
|
* ```html
|
|
17
21
|
* <cb-multiselect [options]="ciudades" optionLabel="nombre" optionValue="id"
|
|
18
22
|
* formControlName="ciudades" placeholder="Elige ciudades" />
|
|
23
|
+
*
|
|
24
|
+
* <!-- Agrupado: [{ label: 'Colombia', items: [...] }, …] -->
|
|
25
|
+
* <cb-multiselect [options]="paises" group filter />
|
|
19
26
|
* ```
|
|
20
27
|
*/
|
|
21
28
|
class CbMultiSelect {
|
|
22
29
|
constructor() {
|
|
30
|
+
/** Textos de interfaz en el idioma activo (`provideCaribeNG({ locale })`). */
|
|
31
|
+
this.t = injectCbTranslation();
|
|
32
|
+
/** Rellena `{0}`, `{1}`… de un texto traducido. */
|
|
33
|
+
this.fmt = cbFormat;
|
|
34
|
+
this.injector = inject(Injector);
|
|
23
35
|
this.options = input([], /* @ts-ignore */
|
|
24
36
|
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
25
37
|
this.optionLabel = input('label', /* @ts-ignore */
|
|
@@ -28,19 +40,35 @@ class CbMultiSelect {
|
|
|
28
40
|
...(ngDevMode ? [{ debugName: "optionValue" }] : /* istanbul ignore next */ []));
|
|
29
41
|
this.optionDisabled = input('disabled', /* @ts-ignore */
|
|
30
42
|
...(ngDevMode ? [{ debugName: "optionDisabled" }] : /* istanbul ignore next */ []));
|
|
31
|
-
|
|
43
|
+
/** Las opciones son grupos (`{ label, items }`): se muestran con su título. */
|
|
44
|
+
this.group = input(false, { ...(ngDevMode ? { debugName: "group" } : /* istanbul ignore next */ {}), transform: toBoolean });
|
|
45
|
+
/** Propiedad con el título de cada grupo. */
|
|
46
|
+
this.optionGroupLabel = input('label', /* @ts-ignore */
|
|
47
|
+
...(ngDevMode ? [{ debugName: "optionGroupLabel" }] : /* istanbul ignore next */ []));
|
|
48
|
+
/** Propiedad con las opciones de cada grupo. */
|
|
49
|
+
this.optionGroupChildren = input('items', /* @ts-ignore */
|
|
50
|
+
...(ngDevMode ? [{ debugName: "optionGroupChildren" }] : /* istanbul ignore next */ []));
|
|
51
|
+
this.placeholder = input(undefined, /* @ts-ignore */
|
|
32
52
|
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
33
53
|
this.size = input('md', /* @ts-ignore */
|
|
34
54
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
35
55
|
this.filter = input(false, { ...(ngDevMode ? { debugName: "filter" } : /* istanbul ignore next */ {}), transform: toBoolean });
|
|
36
|
-
this.filterPlaceholder = input(
|
|
56
|
+
this.filterPlaceholder = input(undefined, /* @ts-ignore */
|
|
37
57
|
...(ngDevMode ? [{ debugName: "filterPlaceholder" }] : /* istanbul ignore next */ []));
|
|
38
|
-
this.emptyMessage = input(
|
|
58
|
+
this.emptyMessage = input(undefined, /* @ts-ignore */
|
|
39
59
|
...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
|
|
40
60
|
this.showToggleAll = input(true, { ...(ngDevMode ? { debugName: "showToggleAll" } : /* istanbul ignore next */ {}), transform: toBoolean });
|
|
41
61
|
/** Nº máx. de etiquetas antes de mostrar "N seleccionados". */
|
|
42
62
|
this.maxSelectedLabels = input(3, /* @ts-ignore */
|
|
43
63
|
...(ngDevMode ? [{ debugName: "maxSelectedLabels" }] : /* istanbul ignore next */ []));
|
|
64
|
+
/** Alto máximo de la lista (CSS). En modo virtual es su alto fijo. */
|
|
65
|
+
this.scrollHeight = input('15rem', /* @ts-ignore */
|
|
66
|
+
...(ngDevMode ? [{ debugName: "scrollHeight" }] : /* istanbul ignore next */ []));
|
|
67
|
+
/** Pinta solo las opciones visibles: para listas de miles de elementos. */
|
|
68
|
+
this.virtualScroll = input(false, { ...(ngDevMode ? { debugName: "virtualScroll" } : /* istanbul ignore next */ {}), transform: toBoolean });
|
|
69
|
+
/** Alto fijo de cada fila en modo virtual (px). */
|
|
70
|
+
this.virtualScrollItemSize = input(36, /* @ts-ignore */
|
|
71
|
+
...(ngDevMode ? [{ debugName: "virtualScrollItemSize" }] : /* istanbul ignore next */ []));
|
|
44
72
|
this.invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: toBoolean });
|
|
45
73
|
/** Estado de validación: inputs manuales + estado de Angular Forms. */
|
|
46
74
|
this.fieldState = cbFieldState({ invalid: () => this.invalid() });
|
|
@@ -63,6 +91,8 @@ class CbMultiSelect {
|
|
|
63
91
|
...(ngDevMode ? [{ debugName: "filterText" }] : /* istanbul ignore next */ []));
|
|
64
92
|
this.triggerWidth = signal(0, /* @ts-ignore */
|
|
65
93
|
...(ngDevMode ? [{ debugName: "triggerWidth" }] : /* istanbul ignore next */ []));
|
|
94
|
+
this.scrollTop = signal(0, /* @ts-ignore */
|
|
95
|
+
...(ngDevMode ? [{ debugName: "scrollTop" }] : /* istanbul ignore next */ []));
|
|
66
96
|
this.cvaDisabled = signal(false, /* @ts-ignore */
|
|
67
97
|
...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
|
|
68
98
|
this.isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
|
|
@@ -71,34 +101,67 @@ class CbMultiSelect {
|
|
|
71
101
|
this.triggerRef = viewChild.required('trigger');
|
|
72
102
|
this.filterInputRef = viewChild('filterInput', /* @ts-ignore */
|
|
73
103
|
...(ngDevMode ? [{ debugName: "filterInputRef" }] : /* istanbul ignore next */ []));
|
|
104
|
+
this.viewportRef = viewChild('viewport', /* @ts-ignore */
|
|
105
|
+
...(ngDevMode ? [{ debugName: "viewportRef" }] : /* istanbul ignore next */ []));
|
|
74
106
|
this.positions = [
|
|
75
107
|
{ originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
|
|
76
108
|
{ originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
|
|
77
109
|
];
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
110
|
+
// --- Búsqueda al teclear ------------------------------------------------------
|
|
111
|
+
this.typeahead = '';
|
|
112
|
+
/** Todas las opciones (aplanando los grupos). */
|
|
113
|
+
this.flatOptions = computed(() => this.group() ? this.options().flatMap((g) => this.childrenOf(g)) : this.options(), /* @ts-ignore */
|
|
114
|
+
...(ngDevMode ? [{ debugName: "flatOptions" }] : /* istanbul ignore next */ []));
|
|
115
|
+
this.filteredGroups = computed(() => {
|
|
116
|
+
const q = normalize(this.filterText());
|
|
117
|
+
const keep = (o) => !this.filter() || !q || normalize(this.labelOf(o)).includes(q);
|
|
118
|
+
let index = 0;
|
|
119
|
+
const indexed = (opts) => opts.filter(keep).map((opt) => ({ opt, index: index++ }));
|
|
120
|
+
if (!this.group())
|
|
121
|
+
return [{ label: null, options: indexed(this.options()) }];
|
|
122
|
+
return this.options()
|
|
123
|
+
.map((g) => ({ label: this.groupLabelOf(g), options: indexed(this.childrenOf(g)) }))
|
|
124
|
+
.filter((g) => g.options.length > 0);
|
|
84
125
|
}, /* @ts-ignore */
|
|
126
|
+
...(ngDevMode ? [{ debugName: "filteredGroups" }] : /* istanbul ignore next */ []));
|
|
127
|
+
this.filteredOptions = computed(() => this.filteredGroups().flatMap((g) => g.options.map((o) => o.opt)), /* @ts-ignore */
|
|
85
128
|
...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
|
|
86
|
-
|
|
129
|
+
/** Filas del modo virtual (cabeceras de grupo + opciones). */
|
|
130
|
+
this.virtualRows = computed(() => this.filteredGroups().flatMap((g, gi) => [
|
|
131
|
+
...(g.label !== null ? [{ kind: 'group', label: g.label, key: `g${gi}` }] : []),
|
|
132
|
+
...g.options.map((o) => ({ kind: 'option', ...o })),
|
|
133
|
+
]), /* @ts-ignore */
|
|
134
|
+
...(ngDevMode ? [{ debugName: "virtualRows" }] : /* istanbul ignore next */ []));
|
|
135
|
+
this.viewportHeight = computed(() => cssToPx(this.scrollHeight()), /* @ts-ignore */
|
|
136
|
+
...(ngDevMode ? [{ debugName: "viewportHeight" }] : /* istanbul ignore next */ []));
|
|
137
|
+
this.windowStart = computed(() => Math.max(0, Math.floor(this.scrollTop() / this.virtualScrollItemSize()) - VIRTUAL_BUFFER), /* @ts-ignore */
|
|
138
|
+
...(ngDevMode ? [{ debugName: "windowStart" }] : /* istanbul ignore next */ []));
|
|
139
|
+
this.windowOffset = computed(() => this.windowStart() * this.virtualScrollItemSize(), /* @ts-ignore */
|
|
140
|
+
...(ngDevMode ? [{ debugName: "windowOffset" }] : /* istanbul ignore next */ []));
|
|
141
|
+
this.visibleRows = computed(() => {
|
|
142
|
+
const size = this.virtualScrollItemSize();
|
|
143
|
+
const end = Math.ceil((this.scrollTop() + this.viewportHeight()) / size) + VIRTUAL_BUFFER;
|
|
144
|
+
return this.virtualRows().slice(this.windowStart(), end);
|
|
145
|
+
}, /* @ts-ignore */
|
|
146
|
+
...(ngDevMode ? [{ debugName: "visibleRows" }] : /* istanbul ignore next */ []));
|
|
147
|
+
this.selected = computed(() => this.flatOptions().filter((o) => this.value().includes(this.valueOf(o))), /* @ts-ignore */
|
|
87
148
|
...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
|
|
88
149
|
this.displayLabel = computed(() => {
|
|
89
150
|
const sel = this.selected();
|
|
90
151
|
if (!sel.length)
|
|
91
|
-
return this.placeholder();
|
|
152
|
+
return (this.placeholder() ?? this.t().select);
|
|
92
153
|
if (sel.length > this.maxSelectedLabels())
|
|
93
|
-
return
|
|
154
|
+
return cbFormat(this.t().selectedCount, sel.length);
|
|
94
155
|
return sel.map((o) => this.labelOf(o)).join(', ');
|
|
95
156
|
}, /* @ts-ignore */
|
|
96
157
|
...(ngDevMode ? [{ debugName: "displayLabel" }] : /* istanbul ignore next */ []));
|
|
97
158
|
this.allSelected = computed(() => {
|
|
98
|
-
const enabled = this.
|
|
159
|
+
const enabled = this.flatOptions().filter((o) => !this.isOptionDisabled(o));
|
|
99
160
|
return enabled.length > 0 && enabled.every((o) => this.value().includes(this.valueOf(o)));
|
|
100
161
|
}, /* @ts-ignore */
|
|
101
162
|
...(ngDevMode ? [{ debugName: "allSelected" }] : /* istanbul ignore next */ []));
|
|
163
|
+
this.activeDescendant = computed(() => this.open() && this.activeIndex() >= 0 ? this.optionId(this.activeIndex()) : null, /* @ts-ignore */
|
|
164
|
+
...(ngDevMode ? [{ debugName: "activeDescendant" }] : /* istanbul ignore next */ []));
|
|
102
165
|
this.onChange = () => { };
|
|
103
166
|
this.onTouched = () => { };
|
|
104
167
|
effect(() => {
|
|
@@ -106,6 +169,13 @@ class CbMultiSelect {
|
|
|
106
169
|
queueMicrotask(() => this.filterInputRef()?.nativeElement.focus());
|
|
107
170
|
}
|
|
108
171
|
});
|
|
172
|
+
// Mantiene el índice activo dentro de la lista filtrada.
|
|
173
|
+
effect(() => {
|
|
174
|
+
const length = this.filteredOptions().length;
|
|
175
|
+
if (this.open() && this.activeIndex() >= length)
|
|
176
|
+
this.setActive(this.firstEnabledIndex());
|
|
177
|
+
});
|
|
178
|
+
inject(DestroyRef).onDestroy(() => clearTimeout(this.typeaheadTimer));
|
|
109
179
|
}
|
|
110
180
|
// --- ControlValueAccessor --------------------------------------------------
|
|
111
181
|
writeValue(value) {
|
|
@@ -130,8 +200,9 @@ class CbMultiSelect {
|
|
|
130
200
|
if (this.isDisabled())
|
|
131
201
|
return;
|
|
132
202
|
this.triggerWidth.set(this.triggerRef().nativeElement.offsetWidth);
|
|
203
|
+
this.scrollTop.set(0);
|
|
133
204
|
this.open.set(true);
|
|
134
|
-
this.
|
|
205
|
+
this.setActive(this.firstEnabledIndex());
|
|
135
206
|
}
|
|
136
207
|
close() {
|
|
137
208
|
if (!this.open())
|
|
@@ -150,7 +221,7 @@ class CbMultiSelect {
|
|
|
150
221
|
this.commit(next);
|
|
151
222
|
}
|
|
152
223
|
toggleAll() {
|
|
153
|
-
const enabled = this.
|
|
224
|
+
const enabled = this.flatOptions().filter((o) => !this.isOptionDisabled(o));
|
|
154
225
|
this.commit(this.allSelected() ? [] : enabled.map((o) => this.valueOf(o)));
|
|
155
226
|
}
|
|
156
227
|
onBlur() {
|
|
@@ -168,6 +239,20 @@ class CbMultiSelect {
|
|
|
168
239
|
}
|
|
169
240
|
onTriggerKeydown(event) {
|
|
170
241
|
const key = event.key;
|
|
242
|
+
// En el filtro, Espacio, Inicio y Fin escriben o mueven el cursor del texto.
|
|
243
|
+
const inFilter = event.target === this.filterInputRef()?.nativeElement;
|
|
244
|
+
if (!inFilter && isPrintable(event)) {
|
|
245
|
+
event.preventDefault();
|
|
246
|
+
if (!this.open()) {
|
|
247
|
+
this.openPanel();
|
|
248
|
+
this.activeIndex.set(-1);
|
|
249
|
+
}
|
|
250
|
+
if (this.filter())
|
|
251
|
+
this.filterText.set(this.filterText() + key);
|
|
252
|
+
else
|
|
253
|
+
this.searchByTyping(key);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
171
256
|
if (!this.open()) {
|
|
172
257
|
if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(key)) {
|
|
173
258
|
event.preventDefault();
|
|
@@ -185,22 +270,25 @@ class CbMultiSelect {
|
|
|
185
270
|
this.move(-1);
|
|
186
271
|
break;
|
|
187
272
|
case 'Home':
|
|
273
|
+
if (inFilter)
|
|
274
|
+
break;
|
|
188
275
|
event.preventDefault();
|
|
189
|
-
this.
|
|
276
|
+
this.setActive(this.firstEnabledIndex());
|
|
190
277
|
break;
|
|
191
278
|
case 'End':
|
|
279
|
+
if (inFilter)
|
|
280
|
+
break;
|
|
192
281
|
event.preventDefault();
|
|
193
|
-
this.
|
|
282
|
+
this.setActive(this.lastEnabledIndex());
|
|
283
|
+
break;
|
|
284
|
+
case ' ':
|
|
285
|
+
if (inFilter)
|
|
286
|
+
break;
|
|
287
|
+
this.toggleActive(event);
|
|
194
288
|
break;
|
|
195
289
|
case 'Enter':
|
|
196
|
-
|
|
197
|
-
const opt = this.filteredOptions()[this.activeIndex()];
|
|
198
|
-
if (opt !== undefined) {
|
|
199
|
-
event.preventDefault();
|
|
200
|
-
this.toggleOption(opt);
|
|
201
|
-
}
|
|
290
|
+
this.toggleActive(event);
|
|
202
291
|
break;
|
|
203
|
-
}
|
|
204
292
|
case 'Escape':
|
|
205
293
|
event.preventDefault();
|
|
206
294
|
this.close();
|
|
@@ -235,12 +323,57 @@ class CbMultiSelect {
|
|
|
235
323
|
optionId(index) {
|
|
236
324
|
return `${this.inputId()}-opt-${index}`;
|
|
237
325
|
}
|
|
238
|
-
|
|
239
|
-
return this.
|
|
326
|
+
groupId(index) {
|
|
327
|
+
return `${this.inputId()}-group-${index}`;
|
|
328
|
+
}
|
|
329
|
+
groupLabelOf(group) {
|
|
330
|
+
return String(group?.[this.optionGroupLabel()] ?? '');
|
|
331
|
+
}
|
|
332
|
+
childrenOf(group) {
|
|
333
|
+
const items = group?.[this.optionGroupChildren()];
|
|
334
|
+
return Array.isArray(items) ? items : [];
|
|
335
|
+
}
|
|
336
|
+
toggleActive(event) {
|
|
337
|
+
const opt = this.filteredOptions()[this.activeIndex()];
|
|
338
|
+
if (opt === undefined)
|
|
339
|
+
return;
|
|
340
|
+
event.preventDefault();
|
|
341
|
+
this.toggleOption(opt);
|
|
342
|
+
}
|
|
343
|
+
/** Cambia la opción activa y la desplaza a la vista. */
|
|
344
|
+
setActive(index) {
|
|
345
|
+
this.activeIndex.set(index);
|
|
346
|
+
if (index < 0 || !this.open())
|
|
347
|
+
return;
|
|
348
|
+
if (this.virtualScroll()) {
|
|
349
|
+
const size = this.virtualScrollItemSize();
|
|
350
|
+
const row = this.virtualRows().findIndex((r) => r.kind === 'option' && r.index === index);
|
|
351
|
+
const top = row * size;
|
|
352
|
+
const view = this.viewportHeight();
|
|
353
|
+
const current = this.scrollTop();
|
|
354
|
+
const next = top < current ? top : top + size > current + view ? top + size - view : current;
|
|
355
|
+
if (next !== current) {
|
|
356
|
+
this.scrollTop.set(next);
|
|
357
|
+
const viewport = this.viewportRef()?.nativeElement;
|
|
358
|
+
if (viewport)
|
|
359
|
+
viewport.scrollTop = next;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
afterNextRender(() => this.viewportRef()?.nativeElement.querySelector(`#${this.optionId(index)}`)?.scrollIntoView?.({ block: 'nearest' }), { injector: this.injector });
|
|
364
|
+
}
|
|
240
365
|
}
|
|
241
366
|
firstEnabledIndex() {
|
|
242
367
|
return this.filteredOptions().findIndex((o) => !this.isOptionDisabled(o));
|
|
243
368
|
}
|
|
369
|
+
lastEnabledIndex() {
|
|
370
|
+
const opts = this.filteredOptions();
|
|
371
|
+
for (let i = opts.length - 1; i >= 0; i--) {
|
|
372
|
+
if (!this.isOptionDisabled(opts[i]))
|
|
373
|
+
return i;
|
|
374
|
+
}
|
|
375
|
+
return -1;
|
|
376
|
+
}
|
|
244
377
|
move(delta) {
|
|
245
378
|
const opts = this.filteredOptions();
|
|
246
379
|
if (!opts.length)
|
|
@@ -249,7 +382,25 @@ class CbMultiSelect {
|
|
|
249
382
|
for (let s = 0; s < opts.length; s++) {
|
|
250
383
|
i = (i + delta + opts.length) % opts.length;
|
|
251
384
|
if (!this.isOptionDisabled(opts[i])) {
|
|
252
|
-
this.
|
|
385
|
+
this.setActive(i);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/** Teclear salta a la siguiente opción que empieza por lo escrito (se reinicia tras 500 ms). */
|
|
391
|
+
searchByTyping(char) {
|
|
392
|
+
clearTimeout(this.typeaheadTimer);
|
|
393
|
+
this.typeahead += normalize(char);
|
|
394
|
+
this.typeaheadTimer = setTimeout(() => (this.typeahead = ''), 500);
|
|
395
|
+
const opts = this.filteredOptions();
|
|
396
|
+
// Repetir la misma letra recorre las opciones que empiezan por ella ("m", "m"…).
|
|
397
|
+
const query = [...this.typeahead].every((c) => c === this.typeahead[0]) ? this.typeahead[0] : this.typeahead;
|
|
398
|
+
// Con una sola letra se avanza a la siguiente coincidencia; con más, se refina desde la actual.
|
|
399
|
+
const from = query.length === 1 ? this.activeIndex() + 1 : Math.max(0, this.activeIndex());
|
|
400
|
+
for (let step = 0; step < opts.length; step++) {
|
|
401
|
+
const i = (from + step) % opts.length;
|
|
402
|
+
if (!this.isOptionDisabled(opts[i]) && normalize(this.labelOf(opts[i])).startsWith(query)) {
|
|
403
|
+
this.setActive(i);
|
|
253
404
|
return;
|
|
254
405
|
}
|
|
255
406
|
}
|
|
@@ -259,13 +410,13 @@ class CbMultiSelect {
|
|
|
259
410
|
this.onChange(next);
|
|
260
411
|
}
|
|
261
412
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbMultiSelect, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
262
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbMultiSelect, isStandalone: true, selector: "cb-multiselect", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: false, transformFunction: null }, optionDisabled: { classPropertyName: "optionDisabled", publicName: "optionDisabled", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, filterPlaceholder: { classPropertyName: "filterPlaceholder", publicName: "filterPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, showToggleAll: { classPropertyName: "showToggleAll", publicName: "showToggleAll", isSignal: true, isRequired: false, transformFunction: null }, maxSelectedLabels: { classPropertyName: "maxSelectedLabels", publicName: "maxSelectedLabels", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "cb-multiselect-host" }, providers: [
|
|
413
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbMultiSelect, isStandalone: true, selector: "cb-multiselect", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: false, transformFunction: null }, optionDisabled: { classPropertyName: "optionDisabled", publicName: "optionDisabled", isSignal: true, isRequired: false, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: false, transformFunction: null }, optionGroupLabel: { classPropertyName: "optionGroupLabel", publicName: "optionGroupLabel", isSignal: true, isRequired: false, transformFunction: null }, optionGroupChildren: { classPropertyName: "optionGroupChildren", publicName: "optionGroupChildren", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, filterPlaceholder: { classPropertyName: "filterPlaceholder", publicName: "filterPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, showToggleAll: { classPropertyName: "showToggleAll", publicName: "showToggleAll", isSignal: true, isRequired: false, transformFunction: null }, maxSelectedLabels: { classPropertyName: "maxSelectedLabels", publicName: "maxSelectedLabels", isSignal: true, isRequired: false, transformFunction: null }, scrollHeight: { classPropertyName: "scrollHeight", publicName: "scrollHeight", isSignal: true, isRequired: false, transformFunction: null }, virtualScroll: { classPropertyName: "virtualScroll", publicName: "virtualScroll", isSignal: true, isRequired: false, transformFunction: null }, virtualScrollItemSize: { classPropertyName: "virtualScrollItemSize", publicName: "virtualScrollItemSize", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "cb-multiselect-host" }, providers: [
|
|
263
414
|
{
|
|
264
415
|
provide: NG_VALUE_ACCESSOR,
|
|
265
416
|
useExisting: forwardRef(() => CbMultiSelect),
|
|
266
417
|
multi: true,
|
|
267
418
|
},
|
|
268
|
-
], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "filterInputRef", first: true, predicate: ["filterInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
419
|
+
], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "filterInputRef", first: true, predicate: ["filterInput"], descendants: true, isSignal: true }, { propertyName: "viewportRef", first: true, predicate: ["viewport"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
269
420
|
<div
|
|
270
421
|
#trigger
|
|
271
422
|
class="cb-multiselect__trigger"
|
|
@@ -281,6 +432,7 @@ class CbMultiSelect {
|
|
|
281
432
|
aria-haspopup="listbox"
|
|
282
433
|
[attr.aria-expanded]="open()"
|
|
283
434
|
[attr.aria-controls]="listboxId"
|
|
435
|
+
[attr.aria-activedescendant]="filter() ? null : activeDescendant()"
|
|
284
436
|
[attr.aria-disabled]="isDisabled() ? 'true' : null"
|
|
285
437
|
[attr.aria-invalid]="fieldState.invalid() ? 'true' : null"
|
|
286
438
|
[attr.aria-required]="fieldState.required() || null"
|
|
@@ -309,59 +461,126 @@ class CbMultiSelect {
|
|
|
309
461
|
@if (filter() || showToggleAll()) {
|
|
310
462
|
<div class="cb-multiselect__header">
|
|
311
463
|
@if (showToggleAll()) {
|
|
312
|
-
<button type="button" class="cb-multiselect__all" (click)="toggleAll()">
|
|
313
|
-
<span class="cb-multiselect__check" [class.cb-is-checked]="allSelected()">
|
|
314
|
-
@if (allSelected()) {
|
|
464
|
+
<button type="button" class="cb-multiselect__all" [attr.aria-pressed]="allSelected()" (click)="toggleAll()">
|
|
465
|
+
<span class="cb-multiselect__check" [class.cb-is-checked]="allSelected()" aria-hidden="true">
|
|
466
|
+
@if (allSelected()) {
|
|
467
|
+
<cb-icon name="check" [size]="14" />
|
|
468
|
+
}
|
|
315
469
|
</span>
|
|
316
|
-
|
|
470
|
+
{{ t().selectAll }}
|
|
317
471
|
</button>
|
|
318
472
|
}
|
|
319
473
|
@if (filter()) {
|
|
474
|
+
<!-- Con filtro, el foco vive aquí: es el combobox que anuncia la opción activa. -->
|
|
320
475
|
<input
|
|
321
476
|
#filterInput
|
|
322
477
|
type="text"
|
|
323
478
|
class="cb-multiselect__filter-input"
|
|
324
|
-
|
|
479
|
+
role="combobox"
|
|
480
|
+
aria-autocomplete="list"
|
|
481
|
+
aria-expanded="true"
|
|
482
|
+
[attr.aria-controls]="listboxId"
|
|
483
|
+
[attr.aria-activedescendant]="activeDescendant()"
|
|
484
|
+
[placeholder]="(filterPlaceholder() ?? t().search)"
|
|
325
485
|
[value]="filterText()"
|
|
326
486
|
(input)="onFilter($event)"
|
|
327
487
|
(keydown)="onTriggerKeydown($event)"
|
|
328
|
-
aria-label="
|
|
488
|
+
[attr.aria-label]="t().filterOptions"
|
|
329
489
|
/>
|
|
330
490
|
}
|
|
331
491
|
</div>
|
|
332
492
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
493
|
+
@if (virtualScroll()) {
|
|
494
|
+
<ul
|
|
495
|
+
#viewport
|
|
496
|
+
class="cb-multiselect__list cb-multiselect__list--virtual"
|
|
497
|
+
role="listbox"
|
|
498
|
+
aria-multiselectable="true"
|
|
499
|
+
[id]="listboxId"
|
|
500
|
+
[style.height.px]="viewportHeight()"
|
|
501
|
+
(scroll)="scrollTop.set($any($event.target).scrollTop)"
|
|
502
|
+
>
|
|
503
|
+
<li role="presentation" class="cb-multiselect__spacer" [style.height.px]="virtualRows().length * virtualScrollItemSize()">
|
|
504
|
+
<ul role="presentation" class="cb-multiselect__window" [style.transform]="'translateY(' + windowOffset() + 'px)'">
|
|
505
|
+
@for (row of visibleRows(); track row.kind === 'group' ? row.key : row.index) {
|
|
506
|
+
@if (row.kind === 'group') {
|
|
507
|
+
<li role="presentation" class="cb-multiselect__group-label" [style.height.px]="virtualScrollItemSize()">
|
|
508
|
+
{{ row.label }}
|
|
509
|
+
</li>
|
|
510
|
+
} @else {
|
|
511
|
+
<ng-container
|
|
512
|
+
*ngTemplateOutlet="optionTpl; context: { $implicit: row, height: virtualScrollItemSize(), virtual: true }"
|
|
513
|
+
/>
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
</ul>
|
|
355
517
|
</li>
|
|
356
|
-
|
|
357
|
-
|
|
518
|
+
@if (!filteredOptions().length) {
|
|
519
|
+
<li class="cb-multiselect__empty" role="option" aria-disabled="true">{{ (emptyMessage() ?? t().noResults) }}</li>
|
|
520
|
+
}
|
|
521
|
+
</ul>
|
|
522
|
+
} @else {
|
|
523
|
+
<ul
|
|
524
|
+
#viewport
|
|
525
|
+
class="cb-multiselect__list"
|
|
526
|
+
role="listbox"
|
|
527
|
+
aria-multiselectable="true"
|
|
528
|
+
[id]="listboxId"
|
|
529
|
+
[style.max-height]="scrollHeight()"
|
|
530
|
+
>
|
|
531
|
+
@for (g of filteredGroups(); track $index; let gi = $index) {
|
|
532
|
+
@if (g.label !== null) {
|
|
533
|
+
<li role="presentation">
|
|
534
|
+
<ul role="group" class="cb-multiselect__group" [attr.aria-labelledby]="groupId(gi)">
|
|
535
|
+
<li role="presentation" class="cb-multiselect__group-label" [id]="groupId(gi)">{{ g.label }}</li>
|
|
536
|
+
@for (item of g.options; track item.index) {
|
|
537
|
+
<ng-container *ngTemplateOutlet="optionTpl; context: { $implicit: item, height: null, virtual: false }" />
|
|
538
|
+
}
|
|
539
|
+
</ul>
|
|
540
|
+
</li>
|
|
541
|
+
} @else {
|
|
542
|
+
@for (item of g.options; track item.index) {
|
|
543
|
+
<ng-container *ngTemplateOutlet="optionTpl; context: { $implicit: item, height: null, virtual: false }" />
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
@if (!filteredOptions().length) {
|
|
548
|
+
<li class="cb-multiselect__empty" role="option" aria-disabled="true">{{ (emptyMessage() ?? t().noResults) }}</li>
|
|
549
|
+
}
|
|
550
|
+
</ul>
|
|
551
|
+
}
|
|
358
552
|
</div>
|
|
359
553
|
</ng-template>
|
|
360
|
-
|
|
554
|
+
|
|
555
|
+
<ng-template #optionTpl let-item let-height="height" let-virtual="virtual">
|
|
556
|
+
<li
|
|
557
|
+
class="cb-multiselect__option"
|
|
558
|
+
[class.cb-is-active]="item.index === activeIndex()"
|
|
559
|
+
[class.cb-is-selected]="isSelected(item.opt)"
|
|
560
|
+
[class.cb-is-disabled]="isOptionDisabled(item.opt)"
|
|
561
|
+
role="option"
|
|
562
|
+
[id]="optionId(item.index)"
|
|
563
|
+
[style.height.px]="height"
|
|
564
|
+
[attr.aria-selected]="isSelected(item.opt)"
|
|
565
|
+
[attr.aria-disabled]="isOptionDisabled(item.opt) ? 'true' : null"
|
|
566
|
+
[attr.aria-setsize]="virtual ? filteredOptions().length : null"
|
|
567
|
+
[attr.aria-posinset]="virtual ? item.index + 1 : null"
|
|
568
|
+
(click)="toggleOption(item.opt)"
|
|
569
|
+
(mouseenter)="activeIndex.set(item.index)"
|
|
570
|
+
>
|
|
571
|
+
<span class="cb-multiselect__check" [class.cb-is-checked]="isSelected(item.opt)" aria-hidden="true">
|
|
572
|
+
@if (isSelected(item.opt)) {
|
|
573
|
+
<cb-icon name="check" [size]="14" />
|
|
574
|
+
}
|
|
575
|
+
</span>
|
|
576
|
+
{{ labelOf(item.opt) }}
|
|
577
|
+
</li>
|
|
578
|
+
</ng-template>
|
|
579
|
+
`, isInline: true, styles: [":host{display:block}.cb-multiselect__trigger{display:flex;align-items:center;gap:var(--cb-space-2);width:100%;min-height:2.5rem;padding:0 var(--cb-space-3);background-color:var(--cb-color-bg);border:var(--cb-border-width) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-md);color:var(--cb-color-text);font-family:var(--cb-font-sans);cursor:pointer;transition:border-color var(--cb-duration-fast) var(--cb-ease),box-shadow var(--cb-duration-fast) var(--cb-ease)}.cb-multiselect__trigger--sm{min-height:2.125rem;font-size:var(--cb-text-sm)}.cb-multiselect__trigger--lg{min-height:2.875rem;font-size:var(--cb-text-lg)}.cb-multiselect__trigger:focus-visible,.cb-multiselect__trigger.cb-is-open{outline:none;border-color:var(--cb-color-primary-500);box-shadow:0 0 0 3px var(--cb-color-focus-ring)}.cb-multiselect__trigger.cb-is-invalid{border-color:var(--cb-color-danger)}.cb-multiselect__trigger.cb-is-disabled{opacity:var(--cb-opacity-disabled, .55);cursor:default;background-color:var(--cb-color-surface)}.cb-multiselect__label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left}.cb-multiselect__label--placeholder{color:var(--cb-color-text-muted)}.cb-multiselect__chevron{flex:none;color:var(--cb-color-text-muted);transition:transform var(--cb-duration-fast) var(--cb-ease)}.cb-multiselect__trigger.cb-is-open .cb-multiselect__chevron{transform:rotate(180deg)}.cb-multiselect__panel{box-sizing:border-box;width:100%;background-color:var(--cb-color-surface-raised);border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-md);box-shadow:var(--cb-shadow-md);overflow:hidden;font-family:var(--cb-font-sans)}.cb-multiselect__header{display:flex;align-items:center;gap:var(--cb-space-2);padding:var(--cb-space-2);border-bottom:1px solid var(--cb-color-border)}.cb-multiselect__all{display:inline-flex;align-items:center;gap:var(--cb-space-2);padding:var(--cb-space-1) var(--cb-space-2);border:none;background:transparent;color:var(--cb-color-text);border-radius:var(--cb-radius-sm);cursor:pointer;font:inherit;white-space:nowrap}.cb-multiselect__all:hover{background-color:var(--cb-color-surface-hover)}.cb-multiselect__filter-input{flex:1 1 auto;min-width:0;padding:var(--cb-space-1) var(--cb-space-2);border:var(--cb-border-width) solid var(--cb-color-border);border-radius:var(--cb-radius-sm);background-color:var(--cb-color-bg);color:var(--cb-color-text);font:inherit;outline:none}.cb-multiselect__filter-input:focus{border-color:var(--cb-color-primary-500)}.cb-multiselect__list{list-style:none;margin:0;padding:var(--cb-space-1);max-height:15rem;overflow-y:auto}.cb-multiselect__option{display:flex;align-items:center;gap:var(--cb-space-2);padding:var(--cb-space-2) var(--cb-space-3);border-radius:var(--cb-radius-sm);cursor:pointer;color:var(--cb-color-text)}.cb-multiselect__option.cb-is-active{background-color:var(--cb-color-surface-hover)}.cb-multiselect__option.cb-is-selected{color:var(--cb-color-primary-700)}.cb-multiselect__option.cb-is-disabled{opacity:var(--cb-opacity-disabled, .55);cursor:default}.cb-multiselect__check{display:inline-flex;align-items:center;justify-content:center;flex:none;width:1.1rem;height:1.1rem;border:var(--cb-border-width) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-sm);color:var(--cb-color-primary-contrast)}.cb-multiselect__check.cb-is-checked{background-color:var(--cb-color-primary-500);border-color:var(--cb-color-primary-500)}.cb-multiselect__empty{padding:var(--cb-space-3);color:var(--cb-color-text-muted);text-align:center}:host-context([data-theme=\"dark\"]) .cb-multiselect__option.cb-is-selected{color:var(--cb-color-primary-200)}.cb-multiselect__group{list-style:none;margin:0;padding:0}.cb-multiselect__group-label{box-sizing:border-box;display:flex;align-items:center;padding:var(--cb-space-2) var(--cb-space-3) var(--cb-space-1);font-size:var(--cb-text-xs);font-weight:var(--cb-font-weight-semibold);letter-spacing:.02em;text-transform:uppercase;color:var(--cb-color-text-muted);cursor:default}.cb-multiselect__list--virtual{max-height:none}.cb-multiselect__spacer{position:relative;list-style:none}.cb-multiselect__window{list-style:none;margin:0;padding:0}.cb-multiselect__list--virtual .cb-multiselect__option{box-sizing:border-box;padding-top:0;padding-bottom:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"], dependencies: [{ kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
361
580
|
}
|
|
362
581
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbMultiSelect, decorators: [{
|
|
363
582
|
type: Component,
|
|
364
|
-
args: [{ selector: 'cb-multiselect', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [OverlayModule, CbIcon], host: { class: 'cb-multiselect-host' }, providers: [
|
|
583
|
+
args: [{ selector: 'cb-multiselect', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [OverlayModule, NgTemplateOutlet, CbIcon], host: { class: 'cb-multiselect-host' }, providers: [
|
|
365
584
|
{
|
|
366
585
|
provide: NG_VALUE_ACCESSOR,
|
|
367
586
|
useExisting: forwardRef(() => CbMultiSelect),
|
|
@@ -383,6 +602,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
383
602
|
aria-haspopup="listbox"
|
|
384
603
|
[attr.aria-expanded]="open()"
|
|
385
604
|
[attr.aria-controls]="listboxId"
|
|
605
|
+
[attr.aria-activedescendant]="filter() ? null : activeDescendant()"
|
|
386
606
|
[attr.aria-disabled]="isDisabled() ? 'true' : null"
|
|
387
607
|
[attr.aria-invalid]="fieldState.invalid() ? 'true' : null"
|
|
388
608
|
[attr.aria-required]="fieldState.required() || null"
|
|
@@ -411,59 +631,141 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
411
631
|
@if (filter() || showToggleAll()) {
|
|
412
632
|
<div class="cb-multiselect__header">
|
|
413
633
|
@if (showToggleAll()) {
|
|
414
|
-
<button type="button" class="cb-multiselect__all" (click)="toggleAll()">
|
|
415
|
-
<span class="cb-multiselect__check" [class.cb-is-checked]="allSelected()">
|
|
416
|
-
@if (allSelected()) {
|
|
634
|
+
<button type="button" class="cb-multiselect__all" [attr.aria-pressed]="allSelected()" (click)="toggleAll()">
|
|
635
|
+
<span class="cb-multiselect__check" [class.cb-is-checked]="allSelected()" aria-hidden="true">
|
|
636
|
+
@if (allSelected()) {
|
|
637
|
+
<cb-icon name="check" [size]="14" />
|
|
638
|
+
}
|
|
417
639
|
</span>
|
|
418
|
-
|
|
640
|
+
{{ t().selectAll }}
|
|
419
641
|
</button>
|
|
420
642
|
}
|
|
421
643
|
@if (filter()) {
|
|
644
|
+
<!-- Con filtro, el foco vive aquí: es el combobox que anuncia la opción activa. -->
|
|
422
645
|
<input
|
|
423
646
|
#filterInput
|
|
424
647
|
type="text"
|
|
425
648
|
class="cb-multiselect__filter-input"
|
|
426
|
-
|
|
649
|
+
role="combobox"
|
|
650
|
+
aria-autocomplete="list"
|
|
651
|
+
aria-expanded="true"
|
|
652
|
+
[attr.aria-controls]="listboxId"
|
|
653
|
+
[attr.aria-activedescendant]="activeDescendant()"
|
|
654
|
+
[placeholder]="(filterPlaceholder() ?? t().search)"
|
|
427
655
|
[value]="filterText()"
|
|
428
656
|
(input)="onFilter($event)"
|
|
429
657
|
(keydown)="onTriggerKeydown($event)"
|
|
430
|
-
aria-label="
|
|
658
|
+
[attr.aria-label]="t().filterOptions"
|
|
431
659
|
/>
|
|
432
660
|
}
|
|
433
661
|
</div>
|
|
434
662
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
663
|
+
@if (virtualScroll()) {
|
|
664
|
+
<ul
|
|
665
|
+
#viewport
|
|
666
|
+
class="cb-multiselect__list cb-multiselect__list--virtual"
|
|
667
|
+
role="listbox"
|
|
668
|
+
aria-multiselectable="true"
|
|
669
|
+
[id]="listboxId"
|
|
670
|
+
[style.height.px]="viewportHeight()"
|
|
671
|
+
(scroll)="scrollTop.set($any($event.target).scrollTop)"
|
|
672
|
+
>
|
|
673
|
+
<li role="presentation" class="cb-multiselect__spacer" [style.height.px]="virtualRows().length * virtualScrollItemSize()">
|
|
674
|
+
<ul role="presentation" class="cb-multiselect__window" [style.transform]="'translateY(' + windowOffset() + 'px)'">
|
|
675
|
+
@for (row of visibleRows(); track row.kind === 'group' ? row.key : row.index) {
|
|
676
|
+
@if (row.kind === 'group') {
|
|
677
|
+
<li role="presentation" class="cb-multiselect__group-label" [style.height.px]="virtualScrollItemSize()">
|
|
678
|
+
{{ row.label }}
|
|
679
|
+
</li>
|
|
680
|
+
} @else {
|
|
681
|
+
<ng-container
|
|
682
|
+
*ngTemplateOutlet="optionTpl; context: { $implicit: row, height: virtualScrollItemSize(), virtual: true }"
|
|
683
|
+
/>
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
</ul>
|
|
457
687
|
</li>
|
|
458
|
-
|
|
459
|
-
|
|
688
|
+
@if (!filteredOptions().length) {
|
|
689
|
+
<li class="cb-multiselect__empty" role="option" aria-disabled="true">{{ (emptyMessage() ?? t().noResults) }}</li>
|
|
690
|
+
}
|
|
691
|
+
</ul>
|
|
692
|
+
} @else {
|
|
693
|
+
<ul
|
|
694
|
+
#viewport
|
|
695
|
+
class="cb-multiselect__list"
|
|
696
|
+
role="listbox"
|
|
697
|
+
aria-multiselectable="true"
|
|
698
|
+
[id]="listboxId"
|
|
699
|
+
[style.max-height]="scrollHeight()"
|
|
700
|
+
>
|
|
701
|
+
@for (g of filteredGroups(); track $index; let gi = $index) {
|
|
702
|
+
@if (g.label !== null) {
|
|
703
|
+
<li role="presentation">
|
|
704
|
+
<ul role="group" class="cb-multiselect__group" [attr.aria-labelledby]="groupId(gi)">
|
|
705
|
+
<li role="presentation" class="cb-multiselect__group-label" [id]="groupId(gi)">{{ g.label }}</li>
|
|
706
|
+
@for (item of g.options; track item.index) {
|
|
707
|
+
<ng-container *ngTemplateOutlet="optionTpl; context: { $implicit: item, height: null, virtual: false }" />
|
|
708
|
+
}
|
|
709
|
+
</ul>
|
|
710
|
+
</li>
|
|
711
|
+
} @else {
|
|
712
|
+
@for (item of g.options; track item.index) {
|
|
713
|
+
<ng-container *ngTemplateOutlet="optionTpl; context: { $implicit: item, height: null, virtual: false }" />
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
@if (!filteredOptions().length) {
|
|
718
|
+
<li class="cb-multiselect__empty" role="option" aria-disabled="true">{{ (emptyMessage() ?? t().noResults) }}</li>
|
|
719
|
+
}
|
|
720
|
+
</ul>
|
|
721
|
+
}
|
|
460
722
|
</div>
|
|
461
723
|
</ng-template>
|
|
462
|
-
|
|
463
|
-
|
|
724
|
+
|
|
725
|
+
<ng-template #optionTpl let-item let-height="height" let-virtual="virtual">
|
|
726
|
+
<li
|
|
727
|
+
class="cb-multiselect__option"
|
|
728
|
+
[class.cb-is-active]="item.index === activeIndex()"
|
|
729
|
+
[class.cb-is-selected]="isSelected(item.opt)"
|
|
730
|
+
[class.cb-is-disabled]="isOptionDisabled(item.opt)"
|
|
731
|
+
role="option"
|
|
732
|
+
[id]="optionId(item.index)"
|
|
733
|
+
[style.height.px]="height"
|
|
734
|
+
[attr.aria-selected]="isSelected(item.opt)"
|
|
735
|
+
[attr.aria-disabled]="isOptionDisabled(item.opt) ? 'true' : null"
|
|
736
|
+
[attr.aria-setsize]="virtual ? filteredOptions().length : null"
|
|
737
|
+
[attr.aria-posinset]="virtual ? item.index + 1 : null"
|
|
738
|
+
(click)="toggleOption(item.opt)"
|
|
739
|
+
(mouseenter)="activeIndex.set(item.index)"
|
|
740
|
+
>
|
|
741
|
+
<span class="cb-multiselect__check" [class.cb-is-checked]="isSelected(item.opt)" aria-hidden="true">
|
|
742
|
+
@if (isSelected(item.opt)) {
|
|
743
|
+
<cb-icon name="check" [size]="14" />
|
|
744
|
+
}
|
|
745
|
+
</span>
|
|
746
|
+
{{ labelOf(item.opt) }}
|
|
747
|
+
</li>
|
|
748
|
+
</ng-template>
|
|
749
|
+
`, styles: [":host{display:block}.cb-multiselect__trigger{display:flex;align-items:center;gap:var(--cb-space-2);width:100%;min-height:2.5rem;padding:0 var(--cb-space-3);background-color:var(--cb-color-bg);border:var(--cb-border-width) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-md);color:var(--cb-color-text);font-family:var(--cb-font-sans);cursor:pointer;transition:border-color var(--cb-duration-fast) var(--cb-ease),box-shadow var(--cb-duration-fast) var(--cb-ease)}.cb-multiselect__trigger--sm{min-height:2.125rem;font-size:var(--cb-text-sm)}.cb-multiselect__trigger--lg{min-height:2.875rem;font-size:var(--cb-text-lg)}.cb-multiselect__trigger:focus-visible,.cb-multiselect__trigger.cb-is-open{outline:none;border-color:var(--cb-color-primary-500);box-shadow:0 0 0 3px var(--cb-color-focus-ring)}.cb-multiselect__trigger.cb-is-invalid{border-color:var(--cb-color-danger)}.cb-multiselect__trigger.cb-is-disabled{opacity:var(--cb-opacity-disabled, .55);cursor:default;background-color:var(--cb-color-surface)}.cb-multiselect__label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left}.cb-multiselect__label--placeholder{color:var(--cb-color-text-muted)}.cb-multiselect__chevron{flex:none;color:var(--cb-color-text-muted);transition:transform var(--cb-duration-fast) var(--cb-ease)}.cb-multiselect__trigger.cb-is-open .cb-multiselect__chevron{transform:rotate(180deg)}.cb-multiselect__panel{box-sizing:border-box;width:100%;background-color:var(--cb-color-surface-raised);border:1px solid var(--cb-color-border);border-radius:var(--cb-radius-md);box-shadow:var(--cb-shadow-md);overflow:hidden;font-family:var(--cb-font-sans)}.cb-multiselect__header{display:flex;align-items:center;gap:var(--cb-space-2);padding:var(--cb-space-2);border-bottom:1px solid var(--cb-color-border)}.cb-multiselect__all{display:inline-flex;align-items:center;gap:var(--cb-space-2);padding:var(--cb-space-1) var(--cb-space-2);border:none;background:transparent;color:var(--cb-color-text);border-radius:var(--cb-radius-sm);cursor:pointer;font:inherit;white-space:nowrap}.cb-multiselect__all:hover{background-color:var(--cb-color-surface-hover)}.cb-multiselect__filter-input{flex:1 1 auto;min-width:0;padding:var(--cb-space-1) var(--cb-space-2);border:var(--cb-border-width) solid var(--cb-color-border);border-radius:var(--cb-radius-sm);background-color:var(--cb-color-bg);color:var(--cb-color-text);font:inherit;outline:none}.cb-multiselect__filter-input:focus{border-color:var(--cb-color-primary-500)}.cb-multiselect__list{list-style:none;margin:0;padding:var(--cb-space-1);max-height:15rem;overflow-y:auto}.cb-multiselect__option{display:flex;align-items:center;gap:var(--cb-space-2);padding:var(--cb-space-2) var(--cb-space-3);border-radius:var(--cb-radius-sm);cursor:pointer;color:var(--cb-color-text)}.cb-multiselect__option.cb-is-active{background-color:var(--cb-color-surface-hover)}.cb-multiselect__option.cb-is-selected{color:var(--cb-color-primary-700)}.cb-multiselect__option.cb-is-disabled{opacity:var(--cb-opacity-disabled, .55);cursor:default}.cb-multiselect__check{display:inline-flex;align-items:center;justify-content:center;flex:none;width:1.1rem;height:1.1rem;border:var(--cb-border-width) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-sm);color:var(--cb-color-primary-contrast)}.cb-multiselect__check.cb-is-checked{background-color:var(--cb-color-primary-500);border-color:var(--cb-color-primary-500)}.cb-multiselect__empty{padding:var(--cb-space-3);color:var(--cb-color-text-muted);text-align:center}:host-context([data-theme=\"dark\"]) .cb-multiselect__option.cb-is-selected{color:var(--cb-color-primary-200)}.cb-multiselect__group{list-style:none;margin:0;padding:0}.cb-multiselect__group-label{box-sizing:border-box;display:flex;align-items:center;padding:var(--cb-space-2) var(--cb-space-3) var(--cb-space-1);font-size:var(--cb-text-xs);font-weight:var(--cb-font-weight-semibold);letter-spacing:.02em;text-transform:uppercase;color:var(--cb-color-text-muted);cursor:default}.cb-multiselect__list--virtual{max-height:none}.cb-multiselect__spacer{position:relative;list-style:none}.cb-multiselect__window{list-style:none;margin:0;padding:0}.cb-multiselect__list--virtual .cb-multiselect__option{box-sizing:border-box;padding-top:0;padding-bottom:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"] }]
|
|
750
|
+
}], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], optionValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValue", required: false }] }], optionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionDisabled", required: false }] }], group: [{ type: i0.Input, args: [{ isSignal: true, alias: "group", required: false }] }], optionGroupLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionGroupLabel", required: false }] }], optionGroupChildren: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionGroupChildren", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], filterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPlaceholder", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], showToggleAll: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToggleAll", required: false }] }], maxSelectedLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelectedLabels", required: false }] }], scrollHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollHeight", required: false }] }], virtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScroll", required: false }] }], virtualScrollItemSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScrollItemSize", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledBy", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], filterInputRef: [{ type: i0.ViewChild, args: ['filterInput', { isSignal: true }] }], viewportRef: [{ type: i0.ViewChild, args: ['viewport', { isSignal: true }] }] } });
|
|
464
751
|
function toBoolean(value) {
|
|
465
752
|
return value !== false && value != null && `${value}` !== 'false';
|
|
466
753
|
}
|
|
754
|
+
/** Minúsculas y sin tildes, para que "medellin" encuentre "Medellín". */
|
|
755
|
+
function normalize(text) {
|
|
756
|
+
return text.normalize('NFD').replace(/[̀-ͯ]/g, '').toLocaleLowerCase().trim();
|
|
757
|
+
}
|
|
758
|
+
/** Tecla imprimible sin modificadores (para la búsqueda al teclear). */
|
|
759
|
+
function isPrintable(event) {
|
|
760
|
+
return event.key.length === 1 && event.key !== ' ' && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
761
|
+
}
|
|
762
|
+
/** Convierte un alto CSS (`px` o `rem`) a píxeles para el modo virtual. */
|
|
763
|
+
function cssToPx(value) {
|
|
764
|
+
const n = parseFloat(value);
|
|
765
|
+
if (Number.isNaN(n))
|
|
766
|
+
return 256;
|
|
767
|
+
return value.trim().endsWith('rem') ? n * 16 : n;
|
|
768
|
+
}
|
|
467
769
|
|
|
468
770
|
/*
|
|
469
771
|
* Public API Surface de @caribeng/ui/multiselect
|