@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,38 @@
|
|
|
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
|
-
import { cbFieldState } from '@caribeng/ui';
|
|
7
|
+
import { injectCbTranslation, cbFieldState } from '@caribeng/ui';
|
|
7
8
|
import { CbIcon } from '@caribeng/ui/icon';
|
|
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
|
* Select (dropdown) de CaribeNG. Listbox accesible sobre CDK Overlay con
|
|
12
|
-
* navegación por teclado,
|
|
15
|
+
* navegación por teclado, búsqueda al teclear, filtro opcional, grupos,
|
|
16
|
+
* scroll virtual para listas grandes e integración con Angular Forms
|
|
13
17
|
* mediante `ControlValueAccessor`.
|
|
14
18
|
*
|
|
15
19
|
* @example
|
|
16
20
|
* ```html
|
|
17
21
|
* <cb-select [options]="ciudades" optionLabel="nombre" optionValue="id"
|
|
18
|
-
* formControlName="ciudad" placeholder="Elige una ciudad" />
|
|
22
|
+
* formControlName="ciudad" placeholder="Elige una ciudad" showClear />
|
|
23
|
+
*
|
|
24
|
+
* <!-- Agrupado: [{ label: 'Colombia', items: [...] }, …] -->
|
|
25
|
+
* <cb-select [options]="paises" group optionLabel="nombre" />
|
|
26
|
+
*
|
|
27
|
+
* <!-- 10 000 opciones -->
|
|
28
|
+
* <cb-select [options]="clientes" virtualScroll [virtualScrollItemSize]="36" filter />
|
|
19
29
|
* ```
|
|
20
30
|
*/
|
|
21
31
|
class CbSelect {
|
|
22
32
|
constructor() {
|
|
33
|
+
/** Textos de interfaz en el idioma activo (`provideCaribeNG({ locale })`). */
|
|
34
|
+
this.t = injectCbTranslation();
|
|
35
|
+
this.injector = inject(Injector);
|
|
23
36
|
// --- Inputs ----------------------------------------------------------------
|
|
24
37
|
this.options = input([], /* @ts-ignore */
|
|
25
38
|
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
@@ -29,15 +42,33 @@ class CbSelect {
|
|
|
29
42
|
...(ngDevMode ? [{ debugName: "optionValue" }] : /* istanbul ignore next */ []));
|
|
30
43
|
this.optionDisabled = input('disabled', /* @ts-ignore */
|
|
31
44
|
...(ngDevMode ? [{ debugName: "optionDisabled" }] : /* istanbul ignore next */ []));
|
|
32
|
-
|
|
45
|
+
/** Las opciones son grupos (`{ label, items }`): se muestran con su título. */
|
|
46
|
+
this.group = input(false, { ...(ngDevMode ? { debugName: "group" } : /* istanbul ignore next */ {}), transform: toBoolean });
|
|
47
|
+
/** Propiedad con el título de cada grupo. */
|
|
48
|
+
this.optionGroupLabel = input('label', /* @ts-ignore */
|
|
49
|
+
...(ngDevMode ? [{ debugName: "optionGroupLabel" }] : /* istanbul ignore next */ []));
|
|
50
|
+
/** Propiedad con las opciones de cada grupo. */
|
|
51
|
+
this.optionGroupChildren = input('items', /* @ts-ignore */
|
|
52
|
+
...(ngDevMode ? [{ debugName: "optionGroupChildren" }] : /* istanbul ignore next */ []));
|
|
53
|
+
this.placeholder = input(undefined, /* @ts-ignore */
|
|
33
54
|
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
34
55
|
this.size = input('md', /* @ts-ignore */
|
|
35
56
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
36
57
|
this.filter = input(false, { ...(ngDevMode ? { debugName: "filter" } : /* istanbul ignore next */ {}), transform: toBoolean });
|
|
37
|
-
this.filterPlaceholder = input(
|
|
58
|
+
this.filterPlaceholder = input(undefined, /* @ts-ignore */
|
|
38
59
|
...(ngDevMode ? [{ debugName: "filterPlaceholder" }] : /* istanbul ignore next */ []));
|
|
39
|
-
this.emptyMessage = input(
|
|
60
|
+
this.emptyMessage = input(undefined, /* @ts-ignore */
|
|
40
61
|
...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
|
|
62
|
+
/** Muestra un botón para vaciar la selección. */
|
|
63
|
+
this.showClear = input(false, { ...(ngDevMode ? { debugName: "showClear" } : /* istanbul ignore next */ {}), transform: toBoolean });
|
|
64
|
+
/** Alto máximo de la lista (CSS). En modo virtual es su alto fijo. */
|
|
65
|
+
this.scrollHeight = input('16rem', /* @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 */ []));
|
|
41
72
|
this.invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: toBoolean });
|
|
42
73
|
/** Estado de validación: inputs manuales + estado de Angular Forms. */
|
|
43
74
|
this.fieldState = cbFieldState({ invalid: () => this.invalid() });
|
|
@@ -62,6 +93,8 @@ class CbSelect {
|
|
|
62
93
|
...(ngDevMode ? [{ debugName: "filterText" }] : /* istanbul ignore next */ []));
|
|
63
94
|
this.triggerWidth = signal(0, /* @ts-ignore */
|
|
64
95
|
...(ngDevMode ? [{ debugName: "triggerWidth" }] : /* istanbul ignore next */ []));
|
|
96
|
+
this.scrollTop = signal(0, /* @ts-ignore */
|
|
97
|
+
...(ngDevMode ? [{ debugName: "scrollTop" }] : /* istanbul ignore next */ []));
|
|
65
98
|
this.cvaDisabled = signal(false, /* @ts-ignore */
|
|
66
99
|
...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
|
|
67
100
|
this.isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
|
|
@@ -70,32 +103,63 @@ class CbSelect {
|
|
|
70
103
|
this.triggerRef = viewChild.required('trigger');
|
|
71
104
|
this.filterInputRef = viewChild('filterInput', /* @ts-ignore */
|
|
72
105
|
...(ngDevMode ? [{ debugName: "filterInputRef" }] : /* istanbul ignore next */ []));
|
|
106
|
+
this.viewportRef = viewChild('viewport', /* @ts-ignore */
|
|
107
|
+
...(ngDevMode ? [{ debugName: "viewportRef" }] : /* istanbul ignore next */ []));
|
|
73
108
|
this.positions = [
|
|
74
109
|
{ originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
|
|
75
110
|
{ originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
|
|
76
111
|
];
|
|
112
|
+
// --- Búsqueda al teclear ------------------------------------------------------
|
|
113
|
+
this.typeahead = '';
|
|
77
114
|
// --- Derivados -------------------------------------------------------------
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
115
|
+
/** Todas las opciones (aplanando los grupos). */
|
|
116
|
+
this.flatOptions = computed(() => this.group() ? this.options().flatMap((g) => this.childrenOf(g)) : this.options(), /* @ts-ignore */
|
|
117
|
+
...(ngDevMode ? [{ debugName: "flatOptions" }] : /* istanbul ignore next */ []));
|
|
118
|
+
this.filteredGroups = computed(() => {
|
|
119
|
+
const q = normalize(this.filterText());
|
|
120
|
+
const keep = (o) => !this.filter() || !q || normalize(this.labelOf(o)).includes(q);
|
|
121
|
+
let index = 0;
|
|
122
|
+
const indexed = (opts) => opts.filter(keep).map((opt) => ({ opt, index: index++ }));
|
|
123
|
+
if (!this.group())
|
|
124
|
+
return [{ label: null, options: indexed(this.options()) }];
|
|
125
|
+
return this.options()
|
|
126
|
+
.map((g) => ({ label: this.groupLabelOf(g), options: indexed(this.childrenOf(g)) }))
|
|
127
|
+
.filter((g) => g.options.length > 0);
|
|
84
128
|
}, /* @ts-ignore */
|
|
129
|
+
...(ngDevMode ? [{ debugName: "filteredGroups" }] : /* istanbul ignore next */ []));
|
|
130
|
+
this.filteredOptions = computed(() => this.filteredGroups().flatMap((g) => g.options.map((o) => o.opt)), /* @ts-ignore */
|
|
85
131
|
...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
|
|
132
|
+
/** Filas del modo virtual (cabeceras de grupo + opciones). */
|
|
133
|
+
this.virtualRows = computed(() => this.filteredGroups().flatMap((g, gi) => [
|
|
134
|
+
...(g.label !== null ? [{ kind: 'group', label: g.label, key: `g${gi}` }] : []),
|
|
135
|
+
...g.options.map((o) => ({ kind: 'option', ...o })),
|
|
136
|
+
]), /* @ts-ignore */
|
|
137
|
+
...(ngDevMode ? [{ debugName: "virtualRows" }] : /* istanbul ignore next */ []));
|
|
138
|
+
this.viewportHeight = computed(() => cssToPx(this.scrollHeight()), /* @ts-ignore */
|
|
139
|
+
...(ngDevMode ? [{ debugName: "viewportHeight" }] : /* istanbul ignore next */ []));
|
|
140
|
+
this.windowStart = computed(() => Math.max(0, Math.floor(this.scrollTop() / this.virtualScrollItemSize()) - VIRTUAL_BUFFER), /* @ts-ignore */
|
|
141
|
+
...(ngDevMode ? [{ debugName: "windowStart" }] : /* istanbul ignore next */ []));
|
|
142
|
+
this.windowOffset = computed(() => this.windowStart() * this.virtualScrollItemSize(), /* @ts-ignore */
|
|
143
|
+
...(ngDevMode ? [{ debugName: "windowOffset" }] : /* istanbul ignore next */ []));
|
|
144
|
+
this.visibleRows = computed(() => {
|
|
145
|
+
const size = this.virtualScrollItemSize();
|
|
146
|
+
const end = Math.ceil((this.scrollTop() + this.viewportHeight()) / size) + VIRTUAL_BUFFER;
|
|
147
|
+
return this.virtualRows().slice(this.windowStart(), end);
|
|
148
|
+
}, /* @ts-ignore */
|
|
149
|
+
...(ngDevMode ? [{ debugName: "visibleRows" }] : /* istanbul ignore next */ []));
|
|
86
150
|
this.selectedOption = computed(() => {
|
|
87
151
|
const value = this.value();
|
|
88
|
-
return this.
|
|
152
|
+
return this.flatOptions().find((o) => this.valueOf(o) === value) ?? null;
|
|
89
153
|
}, /* @ts-ignore */
|
|
90
154
|
...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
|
|
91
155
|
this.displayLabel = computed(() => {
|
|
92
156
|
const selected = this.selectedOption();
|
|
93
|
-
return selected ? this.labelOf(selected) : this.placeholder();
|
|
157
|
+
return selected ? this.labelOf(selected) : (this.placeholder() ?? this.t().select);
|
|
94
158
|
}, /* @ts-ignore */
|
|
95
159
|
...(ngDevMode ? [{ debugName: "displayLabel" }] : /* istanbul ignore next */ []));
|
|
96
|
-
this.
|
|
97
|
-
|
|
98
|
-
|
|
160
|
+
this.canClear = computed(() => this.showClear() && this.selectedOption() !== null && !this.isDisabled(), /* @ts-ignore */
|
|
161
|
+
...(ngDevMode ? [{ debugName: "canClear" }] : /* istanbul ignore next */ []));
|
|
162
|
+
this.activeDescendant = computed(() => this.open() && this.activeIndex() >= 0 ? this.optionId(this.activeIndex()) : null, /* @ts-ignore */
|
|
99
163
|
...(ngDevMode ? [{ debugName: "activeDescendant" }] : /* istanbul ignore next */ []));
|
|
100
164
|
// --- ControlValueAccessor --------------------------------------------------
|
|
101
165
|
this.onChange = () => { };
|
|
@@ -112,6 +176,7 @@ class CbSelect {
|
|
|
112
176
|
if (this.open())
|
|
113
177
|
this.ensureActiveVisible();
|
|
114
178
|
});
|
|
179
|
+
inject(DestroyRef).onDestroy(() => clearTimeout(this.typeaheadTimer));
|
|
115
180
|
}
|
|
116
181
|
writeValue(value) {
|
|
117
182
|
this.value.set(value ?? null);
|
|
@@ -135,9 +200,10 @@ class CbSelect {
|
|
|
135
200
|
if (this.isDisabled())
|
|
136
201
|
return;
|
|
137
202
|
this.triggerWidth.set(this.triggerRef().nativeElement.offsetWidth);
|
|
203
|
+
this.scrollTop.set(0);
|
|
138
204
|
this.open.set(true);
|
|
139
205
|
const selectedIdx = this.filteredOptions().indexOf(this.selectedOption());
|
|
140
|
-
this.
|
|
206
|
+
this.setActive(selectedIdx >= 0 ? selectedIdx : this.firstEnabledIndex());
|
|
141
207
|
}
|
|
142
208
|
close() {
|
|
143
209
|
if (!this.open())
|
|
@@ -155,6 +221,13 @@ class CbSelect {
|
|
|
155
221
|
this.onChange(value);
|
|
156
222
|
this.close();
|
|
157
223
|
}
|
|
224
|
+
/** Vacía la selección (botón de `showClear`) y devuelve el foco al disparador. */
|
|
225
|
+
clear() {
|
|
226
|
+
this.value.set(null);
|
|
227
|
+
this.onChange(null);
|
|
228
|
+
this.onTouched();
|
|
229
|
+
this.triggerRef().nativeElement.focus();
|
|
230
|
+
}
|
|
158
231
|
onBlur() {
|
|
159
232
|
if (!this.open())
|
|
160
233
|
this.onTouched();
|
|
@@ -170,6 +243,22 @@ class CbSelect {
|
|
|
170
243
|
}
|
|
171
244
|
onTriggerKeydown(event) {
|
|
172
245
|
const key = event.key;
|
|
246
|
+
const inFilter = event.target === this.filterInputRef()?.nativeElement;
|
|
247
|
+
if (!inFilter && isPrintable(event)) {
|
|
248
|
+
event.preventDefault();
|
|
249
|
+
if (!this.open()) {
|
|
250
|
+
this.openPanel();
|
|
251
|
+
// Sin selección previa, la búsqueda empieza por la primera opción.
|
|
252
|
+
if (!this.selectedOption())
|
|
253
|
+
this.activeIndex.set(-1);
|
|
254
|
+
}
|
|
255
|
+
// Con filtro, lo tecleado empieza la búsqueda; sin él, salta a la opción que coincide.
|
|
256
|
+
if (this.filter())
|
|
257
|
+
this.filterText.set(this.filterText() + key);
|
|
258
|
+
else
|
|
259
|
+
this.searchByTyping(key);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
173
262
|
if (!this.open()) {
|
|
174
263
|
if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(key)) {
|
|
175
264
|
event.preventDefault();
|
|
@@ -187,12 +276,16 @@ class CbSelect {
|
|
|
187
276
|
this.move(-1);
|
|
188
277
|
break;
|
|
189
278
|
case 'Home':
|
|
279
|
+
if (inFilter)
|
|
280
|
+
break;
|
|
190
281
|
event.preventDefault();
|
|
191
|
-
this.
|
|
282
|
+
this.setActive(this.firstEnabledIndex());
|
|
192
283
|
break;
|
|
193
284
|
case 'End':
|
|
285
|
+
if (inFilter)
|
|
286
|
+
break;
|
|
194
287
|
event.preventDefault();
|
|
195
|
-
this.
|
|
288
|
+
this.setActive(this.lastEnabledIndex());
|
|
196
289
|
break;
|
|
197
290
|
case 'Enter':
|
|
198
291
|
event.preventDefault();
|
|
@@ -232,8 +325,38 @@ class CbSelect {
|
|
|
232
325
|
optionId(index) {
|
|
233
326
|
return `${this.inputId()}-opt-${index}`;
|
|
234
327
|
}
|
|
235
|
-
|
|
236
|
-
return this.
|
|
328
|
+
groupId(index) {
|
|
329
|
+
return `${this.inputId()}-group-${index}`;
|
|
330
|
+
}
|
|
331
|
+
groupLabelOf(group) {
|
|
332
|
+
return String(group?.[this.optionGroupLabel()] ?? '');
|
|
333
|
+
}
|
|
334
|
+
childrenOf(group) {
|
|
335
|
+
const items = group?.[this.optionGroupChildren()];
|
|
336
|
+
return Array.isArray(items) ? items : [];
|
|
337
|
+
}
|
|
338
|
+
/** Cambia la opción activa y la desplaza a la vista. */
|
|
339
|
+
setActive(index) {
|
|
340
|
+
this.activeIndex.set(index);
|
|
341
|
+
if (index < 0 || !this.open())
|
|
342
|
+
return;
|
|
343
|
+
if (this.virtualScroll()) {
|
|
344
|
+
const size = this.virtualScrollItemSize();
|
|
345
|
+
const row = this.virtualRows().findIndex((r) => r.kind === 'option' && r.index === index);
|
|
346
|
+
const top = row * size;
|
|
347
|
+
const view = this.viewportHeight();
|
|
348
|
+
const current = this.scrollTop();
|
|
349
|
+
const next = top < current ? top : top + size > current + view ? top + size - view : current;
|
|
350
|
+
if (next !== current) {
|
|
351
|
+
this.scrollTop.set(next);
|
|
352
|
+
const viewport = this.viewportRef()?.nativeElement;
|
|
353
|
+
if (viewport)
|
|
354
|
+
viewport.scrollTop = next;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
afterNextRender(() => this.viewportRef()?.nativeElement.querySelector(`#${this.optionId(index)}`)?.scrollIntoView?.({ block: 'nearest' }), { injector: this.injector });
|
|
359
|
+
}
|
|
237
360
|
}
|
|
238
361
|
move(delta) {
|
|
239
362
|
const opts = this.filteredOptions();
|
|
@@ -243,7 +366,25 @@ class CbSelect {
|
|
|
243
366
|
for (let step = 0; step < opts.length; step++) {
|
|
244
367
|
i = (i + delta + opts.length) % opts.length;
|
|
245
368
|
if (!this.isOptionDisabled(opts[i])) {
|
|
246
|
-
this.
|
|
369
|
+
this.setActive(i);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/** Teclear salta a la siguiente opción que empieza por lo escrito (se reinicia tras 500 ms). */
|
|
375
|
+
searchByTyping(char) {
|
|
376
|
+
clearTimeout(this.typeaheadTimer);
|
|
377
|
+
this.typeahead += normalize(char);
|
|
378
|
+
this.typeaheadTimer = setTimeout(() => (this.typeahead = ''), 500);
|
|
379
|
+
const opts = this.filteredOptions();
|
|
380
|
+
// Repetir la misma letra recorre las opciones que empiezan por ella ("m", "m"…).
|
|
381
|
+
const query = [...this.typeahead].every((c) => c === this.typeahead[0]) ? this.typeahead[0] : this.typeahead;
|
|
382
|
+
// Con una sola letra se avanza a la siguiente coincidencia; con más, se refina desde la actual.
|
|
383
|
+
const from = query.length === 1 ? this.activeIndex() + 1 : Math.max(0, this.activeIndex());
|
|
384
|
+
for (let step = 0; step < opts.length; step++) {
|
|
385
|
+
const i = (from + step) % opts.length;
|
|
386
|
+
if (!this.isOptionDisabled(opts[i]) && normalize(this.labelOf(opts[i])).startsWith(query)) {
|
|
387
|
+
this.setActive(i);
|
|
247
388
|
return;
|
|
248
389
|
}
|
|
249
390
|
}
|
|
@@ -266,17 +407,17 @@ class CbSelect {
|
|
|
266
407
|
}
|
|
267
408
|
ensureActiveVisible() {
|
|
268
409
|
if (this.activeIndex() >= this.filteredOptions().length) {
|
|
269
|
-
this.
|
|
410
|
+
this.setActive(this.firstEnabledIndex());
|
|
270
411
|
}
|
|
271
412
|
}
|
|
272
413
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbSelect, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
273
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbSelect, isStandalone: true, selector: "cb-select", 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 }, 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-select-host" }, providers: [
|
|
414
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbSelect, isStandalone: true, selector: "cb-select", 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 }, showClear: { classPropertyName: "showClear", publicName: "showClear", 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: { properties: { "class.cb-has-clear": "canClear()" }, classAttribute: "cb-select-host" }, providers: [
|
|
274
415
|
{
|
|
275
416
|
provide: NG_VALUE_ACCESSOR,
|
|
276
417
|
useExisting: forwardRef(() => CbSelect),
|
|
277
418
|
multi: true,
|
|
278
419
|
},
|
|
279
|
-
], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "filterInputRef", first: true, predicate: ["filterInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
420
|
+
], 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: `
|
|
280
421
|
<div
|
|
281
422
|
#trigger
|
|
282
423
|
class="cb-select__trigger"
|
|
@@ -292,7 +433,7 @@ class CbSelect {
|
|
|
292
433
|
[attr.aria-haspopup]="'listbox'"
|
|
293
434
|
[attr.aria-expanded]="open()"
|
|
294
435
|
[attr.aria-controls]="listboxId"
|
|
295
|
-
[attr.aria-activedescendant]="activeDescendant()"
|
|
436
|
+
[attr.aria-activedescendant]="filter() ? null : activeDescendant()"
|
|
296
437
|
[attr.aria-disabled]="isDisabled() ? 'true' : null"
|
|
297
438
|
[attr.aria-invalid]="fieldState.invalid() ? 'true' : null"
|
|
298
439
|
[attr.aria-required]="fieldState.required() || null"
|
|
@@ -300,14 +441,16 @@ class CbSelect {
|
|
|
300
441
|
(keydown)="onTriggerKeydown($event)"
|
|
301
442
|
(blur)="onBlur()"
|
|
302
443
|
>
|
|
303
|
-
<span
|
|
304
|
-
class="cb-select__label"
|
|
305
|
-
[class.cb-select__label--placeholder]="!selectedOption()"
|
|
306
|
-
>
|
|
444
|
+
<span class="cb-select__label" [class.cb-select__label--placeholder]="!selectedOption()">
|
|
307
445
|
{{ displayLabel() }}
|
|
308
446
|
</span>
|
|
309
447
|
<cb-icon class="cb-select__chevron" name="chevron-down" [size]="16" aria-hidden="true" />
|
|
310
448
|
</div>
|
|
449
|
+
@if (canClear()) {
|
|
450
|
+
<button type="button" class="cb-select__clear" [attr.aria-label]="t().clear" (click)="clear()">
|
|
451
|
+
<cb-icon name="x" [size]="14" aria-hidden="true" />
|
|
452
|
+
</button>
|
|
453
|
+
}
|
|
311
454
|
|
|
312
455
|
<ng-template
|
|
313
456
|
cdkConnectedOverlay
|
|
@@ -323,47 +466,113 @@ class CbSelect {
|
|
|
323
466
|
<div class="cb-select__panel" [attr.data-theme]="null">
|
|
324
467
|
@if (filter()) {
|
|
325
468
|
<div class="cb-select__filter">
|
|
469
|
+
<!-- Con filtro, el foco vive aquí: es el combobox que anuncia la opción activa. -->
|
|
326
470
|
<input
|
|
327
471
|
#filterInput
|
|
328
472
|
type="text"
|
|
329
473
|
class="cb-select__filter-input"
|
|
330
|
-
|
|
474
|
+
role="combobox"
|
|
475
|
+
aria-autocomplete="list"
|
|
476
|
+
aria-expanded="true"
|
|
477
|
+
[attr.aria-controls]="listboxId"
|
|
478
|
+
[attr.aria-activedescendant]="activeDescendant()"
|
|
479
|
+
[placeholder]="(filterPlaceholder() ?? t().search)"
|
|
331
480
|
[value]="filterText()"
|
|
332
481
|
(input)="onFilter($event)"
|
|
333
482
|
(keydown)="onTriggerKeydown($event)"
|
|
334
|
-
aria-label="
|
|
483
|
+
[attr.aria-label]="t().filterOptions"
|
|
335
484
|
/>
|
|
336
485
|
</div>
|
|
337
486
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
[
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
487
|
+
@if (virtualScroll()) {
|
|
488
|
+
<ul
|
|
489
|
+
#viewport
|
|
490
|
+
class="cb-select__list cb-select__list--virtual"
|
|
491
|
+
role="listbox"
|
|
492
|
+
[id]="listboxId"
|
|
493
|
+
[style.height.px]="viewportHeight()"
|
|
494
|
+
(scroll)="scrollTop.set($any($event.target).scrollTop)"
|
|
495
|
+
>
|
|
496
|
+
<li role="presentation" class="cb-select__spacer" [style.height.px]="virtualRows().length * virtualScrollItemSize()">
|
|
497
|
+
<ul role="presentation" class="cb-select__window" [style.transform]="'translateY(' + windowOffset() + 'px)'">
|
|
498
|
+
@for (row of visibleRows(); track row.kind === 'group' ? row.key : row.index) {
|
|
499
|
+
@if (row.kind === 'group') {
|
|
500
|
+
<li role="presentation" class="cb-select__group-label" [style.height.px]="virtualScrollItemSize()">
|
|
501
|
+
{{ row.label }}
|
|
502
|
+
</li>
|
|
503
|
+
} @else {
|
|
504
|
+
<li
|
|
505
|
+
class="cb-select__option"
|
|
506
|
+
[class.cb-is-active]="row.index === activeIndex()"
|
|
507
|
+
[class.cb-is-selected]="isSelected(row.opt)"
|
|
508
|
+
[class.cb-is-disabled]="isOptionDisabled(row.opt)"
|
|
509
|
+
role="option"
|
|
510
|
+
[id]="optionId(row.index)"
|
|
511
|
+
[style.height.px]="virtualScrollItemSize()"
|
|
512
|
+
[attr.aria-selected]="isSelected(row.opt)"
|
|
513
|
+
[attr.aria-disabled]="isOptionDisabled(row.opt) ? 'true' : null"
|
|
514
|
+
[attr.aria-setsize]="filteredOptions().length"
|
|
515
|
+
[attr.aria-posinset]="row.index + 1"
|
|
516
|
+
(click)="selectOption(row.opt)"
|
|
517
|
+
(mouseenter)="activeIndex.set(row.index)"
|
|
518
|
+
>
|
|
519
|
+
{{ labelOf(row.opt) }}
|
|
520
|
+
</li>
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
</ul>
|
|
357
524
|
</li>
|
|
358
|
-
|
|
359
|
-
|
|
525
|
+
@if (!filteredOptions().length) {
|
|
526
|
+
<li class="cb-select__empty" role="option" aria-disabled="true">{{ (emptyMessage() ?? t().noResults) }}</li>
|
|
527
|
+
}
|
|
528
|
+
</ul>
|
|
529
|
+
} @else {
|
|
530
|
+
<ul #viewport class="cb-select__list" role="listbox" [id]="listboxId" [style.max-height]="scrollHeight()">
|
|
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-select__group" [attr.aria-labelledby]="groupId(gi)">
|
|
535
|
+
<li role="presentation" class="cb-select__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 }" />
|
|
538
|
+
}
|
|
539
|
+
</ul>
|
|
540
|
+
</li>
|
|
541
|
+
} @else {
|
|
542
|
+
@for (item of g.options; track item.index) {
|
|
543
|
+
<ng-container *ngTemplateOutlet="optionTpl; context: { $implicit: item }" />
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
@if (!filteredOptions().length) {
|
|
548
|
+
<li class="cb-select__empty" role="option" aria-disabled="true">{{ (emptyMessage() ?? t().noResults) }}</li>
|
|
549
|
+
}
|
|
550
|
+
</ul>
|
|
551
|
+
}
|
|
360
552
|
</div>
|
|
361
553
|
</ng-template>
|
|
362
|
-
|
|
554
|
+
|
|
555
|
+
<ng-template #optionTpl let-item>
|
|
556
|
+
<li
|
|
557
|
+
class="cb-select__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
|
+
[attr.aria-selected]="isSelected(item.opt)"
|
|
564
|
+
[attr.aria-disabled]="isOptionDisabled(item.opt) ? 'true' : null"
|
|
565
|
+
(click)="selectOption(item.opt)"
|
|
566
|
+
(mouseenter)="activeIndex.set(item.index)"
|
|
567
|
+
>
|
|
568
|
+
{{ labelOf(item.opt) }}
|
|
569
|
+
</li>
|
|
570
|
+
</ng-template>
|
|
571
|
+
`, isInline: true, styles: [":host{display:block}.cb-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--cb-space-2);box-sizing:border-box;width:100%;font-family:var(--cb-font-sans);color:var(--cb-color-text);background-color:var(--cb-color-bg);border:var(--cb-border-width) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-md);cursor:pointer;-webkit-user-select:none;user-select:none;transition:border-color var(--cb-duration-fast) var(--cb-ease),box-shadow var(--cb-duration-fast) var(--cb-ease)}.cb-select__trigger:hover:not(.cb-is-disabled){border-color:var(--cb-color-text-muted)}.cb-select__trigger:focus-visible,.cb-select__trigger.cb-is-open{outline:none;border-color:var(--cb-color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--cb-color-primary-500) 25%,transparent)}.cb-select__trigger.cb-is-invalid{border-color:var(--cb-color-danger)}.cb-select__trigger.cb-is-invalid.cb-is-open,.cb-select__trigger.cb-is-invalid:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,var(--cb-color-danger) 25%,transparent)}.cb-select__trigger.cb-is-disabled{cursor:not-allowed;background-color:var(--cb-color-surface);color:var(--cb-color-text-muted);opacity:.8}.cb-select__trigger--sm{min-height:2rem;padding:var(--cb-space-1) var(--cb-space-3);font-size:var(--cb-text-sm)}.cb-select__trigger--md{min-height:2.5rem;padding:var(--cb-space-2) var(--cb-space-3);font-size:var(--cb-text-base)}.cb-select__trigger--lg{min-height:3rem;padding:var(--cb-space-3) var(--cb-space-4);font-size:var(--cb-text-lg)}.cb-select__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cb-select__label--placeholder{color:var(--cb-color-text-muted)}.cb-select__chevron{flex:none;color:var(--cb-color-text-muted);transition:transform var(--cb-duration-fast) var(--cb-ease)}.cb-select__trigger.cb-is-open .cb-select__chevron{transform:rotate(180deg)}.cb-select__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-select__filter{padding:var(--cb-space-2);border-bottom:1px solid var(--cb-color-border)}.cb-select__filter-input{box-sizing:border-box;width:100%;padding:var(--cb-space-2) var(--cb-space-3);font-family:inherit;font-size:var(--cb-text-sm);color:var(--cb-color-text);background-color:var(--cb-color-bg);border:1px solid var(--cb-color-border-strong);border-radius:var(--cb-radius-sm)}.cb-select__filter-input:focus-visible{outline:none;border-color:var(--cb-color-primary-500);box-shadow:0 0 0 2px color-mix(in srgb,var(--cb-color-primary-500) 25%,transparent)}.cb-select__list{list-style:none;margin:0;padding:var(--cb-space-1);max-height:16rem;overflow-y:auto}.cb-select__option{display:flex;align-items:center;padding:var(--cb-space-2) var(--cb-space-3);border-radius:var(--cb-radius-sm);font-size:var(--cb-text-base);color:var(--cb-color-text);cursor:pointer}.cb-select__option.cb-is-active{background-color:var(--cb-color-surface-hover)}.cb-select__option.cb-is-selected{color:var(--cb-color-primary-600);background-color:color-mix(in srgb,var(--cb-color-primary-500) 12%,transparent);font-weight:var(--cb-font-weight-semibold)}.cb-select__option.cb-is-selected.cb-is-active{background-color:color-mix(in srgb,var(--cb-color-primary-500) 20%,transparent)}.cb-select__option.cb-is-disabled{color:var(--cb-color-text-muted);cursor:not-allowed;opacity:.6}.cb-select__empty{padding:var(--cb-space-3);color:var(--cb-color-text-muted);font-size:var(--cb-text-sm);text-align:center}.cb-select__group{list-style:none;margin:0;padding:0}.cb-select__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-select__list--virtual{max-height:none}.cb-select__spacer{position:relative;list-style:none}.cb-select__window{list-style:none;margin:0;padding:0}.cb-select__list--virtual .cb-select__option{box-sizing:border-box;padding-top:0;padding-bottom:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}:host{position:relative}:host(.cb-has-clear) .cb-select__trigger{padding-right:3.25rem}.cb-select__clear{position:absolute;top:50%;right:2.1rem;display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;padding:0;color:var(--cb-color-text-muted);background:none;border:none;border-radius:var(--cb-radius-sm);transform:translateY(-50%);cursor:pointer}.cb-select__clear:hover{color:var(--cb-color-text);background-color:var(--cb-color-surface-hover)}.cb-select__clear:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:1px}\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 }); }
|
|
363
572
|
}
|
|
364
573
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbSelect, decorators: [{
|
|
365
574
|
type: Component,
|
|
366
|
-
args: [{ selector: 'cb-select', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [OverlayModule, CbIcon], host: { class: 'cb-select-host' }, providers: [
|
|
575
|
+
args: [{ selector: 'cb-select', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [OverlayModule, NgTemplateOutlet, CbIcon], host: { class: 'cb-select-host', '[class.cb-has-clear]': 'canClear()' }, providers: [
|
|
367
576
|
{
|
|
368
577
|
provide: NG_VALUE_ACCESSOR,
|
|
369
578
|
useExisting: forwardRef(() => CbSelect),
|
|
@@ -385,7 +594,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
385
594
|
[attr.aria-haspopup]="'listbox'"
|
|
386
595
|
[attr.aria-expanded]="open()"
|
|
387
596
|
[attr.aria-controls]="listboxId"
|
|
388
|
-
[attr.aria-activedescendant]="activeDescendant()"
|
|
597
|
+
[attr.aria-activedescendant]="filter() ? null : activeDescendant()"
|
|
389
598
|
[attr.aria-disabled]="isDisabled() ? 'true' : null"
|
|
390
599
|
[attr.aria-invalid]="fieldState.invalid() ? 'true' : null"
|
|
391
600
|
[attr.aria-required]="fieldState.required() || null"
|
|
@@ -393,14 +602,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
393
602
|
(keydown)="onTriggerKeydown($event)"
|
|
394
603
|
(blur)="onBlur()"
|
|
395
604
|
>
|
|
396
|
-
<span
|
|
397
|
-
class="cb-select__label"
|
|
398
|
-
[class.cb-select__label--placeholder]="!selectedOption()"
|
|
399
|
-
>
|
|
605
|
+
<span class="cb-select__label" [class.cb-select__label--placeholder]="!selectedOption()">
|
|
400
606
|
{{ displayLabel() }}
|
|
401
607
|
</span>
|
|
402
608
|
<cb-icon class="cb-select__chevron" name="chevron-down" [size]="16" aria-hidden="true" />
|
|
403
609
|
</div>
|
|
610
|
+
@if (canClear()) {
|
|
611
|
+
<button type="button" class="cb-select__clear" [attr.aria-label]="t().clear" (click)="clear()">
|
|
612
|
+
<cb-icon name="x" [size]="14" aria-hidden="true" />
|
|
613
|
+
</button>
|
|
614
|
+
}
|
|
404
615
|
|
|
405
616
|
<ng-template
|
|
406
617
|
cdkConnectedOverlay
|
|
@@ -416,47 +627,128 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
416
627
|
<div class="cb-select__panel" [attr.data-theme]="null">
|
|
417
628
|
@if (filter()) {
|
|
418
629
|
<div class="cb-select__filter">
|
|
630
|
+
<!-- Con filtro, el foco vive aquí: es el combobox que anuncia la opción activa. -->
|
|
419
631
|
<input
|
|
420
632
|
#filterInput
|
|
421
633
|
type="text"
|
|
422
634
|
class="cb-select__filter-input"
|
|
423
|
-
|
|
635
|
+
role="combobox"
|
|
636
|
+
aria-autocomplete="list"
|
|
637
|
+
aria-expanded="true"
|
|
638
|
+
[attr.aria-controls]="listboxId"
|
|
639
|
+
[attr.aria-activedescendant]="activeDescendant()"
|
|
640
|
+
[placeholder]="(filterPlaceholder() ?? t().search)"
|
|
424
641
|
[value]="filterText()"
|
|
425
642
|
(input)="onFilter($event)"
|
|
426
643
|
(keydown)="onTriggerKeydown($event)"
|
|
427
|
-
aria-label="
|
|
644
|
+
[attr.aria-label]="t().filterOptions"
|
|
428
645
|
/>
|
|
429
646
|
</div>
|
|
430
647
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
[
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
648
|
+
@if (virtualScroll()) {
|
|
649
|
+
<ul
|
|
650
|
+
#viewport
|
|
651
|
+
class="cb-select__list cb-select__list--virtual"
|
|
652
|
+
role="listbox"
|
|
653
|
+
[id]="listboxId"
|
|
654
|
+
[style.height.px]="viewportHeight()"
|
|
655
|
+
(scroll)="scrollTop.set($any($event.target).scrollTop)"
|
|
656
|
+
>
|
|
657
|
+
<li role="presentation" class="cb-select__spacer" [style.height.px]="virtualRows().length * virtualScrollItemSize()">
|
|
658
|
+
<ul role="presentation" class="cb-select__window" [style.transform]="'translateY(' + windowOffset() + 'px)'">
|
|
659
|
+
@for (row of visibleRows(); track row.kind === 'group' ? row.key : row.index) {
|
|
660
|
+
@if (row.kind === 'group') {
|
|
661
|
+
<li role="presentation" class="cb-select__group-label" [style.height.px]="virtualScrollItemSize()">
|
|
662
|
+
{{ row.label }}
|
|
663
|
+
</li>
|
|
664
|
+
} @else {
|
|
665
|
+
<li
|
|
666
|
+
class="cb-select__option"
|
|
667
|
+
[class.cb-is-active]="row.index === activeIndex()"
|
|
668
|
+
[class.cb-is-selected]="isSelected(row.opt)"
|
|
669
|
+
[class.cb-is-disabled]="isOptionDisabled(row.opt)"
|
|
670
|
+
role="option"
|
|
671
|
+
[id]="optionId(row.index)"
|
|
672
|
+
[style.height.px]="virtualScrollItemSize()"
|
|
673
|
+
[attr.aria-selected]="isSelected(row.opt)"
|
|
674
|
+
[attr.aria-disabled]="isOptionDisabled(row.opt) ? 'true' : null"
|
|
675
|
+
[attr.aria-setsize]="filteredOptions().length"
|
|
676
|
+
[attr.aria-posinset]="row.index + 1"
|
|
677
|
+
(click)="selectOption(row.opt)"
|
|
678
|
+
(mouseenter)="activeIndex.set(row.index)"
|
|
679
|
+
>
|
|
680
|
+
{{ labelOf(row.opt) }}
|
|
681
|
+
</li>
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
</ul>
|
|
446
685
|
</li>
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
686
|
+
@if (!filteredOptions().length) {
|
|
687
|
+
<li class="cb-select__empty" role="option" aria-disabled="true">{{ (emptyMessage() ?? t().noResults) }}</li>
|
|
688
|
+
}
|
|
689
|
+
</ul>
|
|
690
|
+
} @else {
|
|
691
|
+
<ul #viewport class="cb-select__list" role="listbox" [id]="listboxId" [style.max-height]="scrollHeight()">
|
|
692
|
+
@for (g of filteredGroups(); track $index; let gi = $index) {
|
|
693
|
+
@if (g.label !== null) {
|
|
694
|
+
<li role="presentation">
|
|
695
|
+
<ul role="group" class="cb-select__group" [attr.aria-labelledby]="groupId(gi)">
|
|
696
|
+
<li role="presentation" class="cb-select__group-label" [id]="groupId(gi)">{{ g.label }}</li>
|
|
697
|
+
@for (item of g.options; track item.index) {
|
|
698
|
+
<ng-container *ngTemplateOutlet="optionTpl; context: { $implicit: item }" />
|
|
699
|
+
}
|
|
700
|
+
</ul>
|
|
701
|
+
</li>
|
|
702
|
+
} @else {
|
|
703
|
+
@for (item of g.options; track item.index) {
|
|
704
|
+
<ng-container *ngTemplateOutlet="optionTpl; context: { $implicit: item }" />
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
@if (!filteredOptions().length) {
|
|
709
|
+
<li class="cb-select__empty" role="option" aria-disabled="true">{{ (emptyMessage() ?? t().noResults) }}</li>
|
|
710
|
+
}
|
|
711
|
+
</ul>
|
|
712
|
+
}
|
|
453
713
|
</div>
|
|
454
714
|
</ng-template>
|
|
455
|
-
|
|
456
|
-
|
|
715
|
+
|
|
716
|
+
<ng-template #optionTpl let-item>
|
|
717
|
+
<li
|
|
718
|
+
class="cb-select__option"
|
|
719
|
+
[class.cb-is-active]="item.index === activeIndex()"
|
|
720
|
+
[class.cb-is-selected]="isSelected(item.opt)"
|
|
721
|
+
[class.cb-is-disabled]="isOptionDisabled(item.opt)"
|
|
722
|
+
role="option"
|
|
723
|
+
[id]="optionId(item.index)"
|
|
724
|
+
[attr.aria-selected]="isSelected(item.opt)"
|
|
725
|
+
[attr.aria-disabled]="isOptionDisabled(item.opt) ? 'true' : null"
|
|
726
|
+
(click)="selectOption(item.opt)"
|
|
727
|
+
(mouseenter)="activeIndex.set(item.index)"
|
|
728
|
+
>
|
|
729
|
+
{{ labelOf(item.opt) }}
|
|
730
|
+
</li>
|
|
731
|
+
</ng-template>
|
|
732
|
+
`, styles: [":host{display:block}.cb-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--cb-space-2);box-sizing:border-box;width:100%;font-family:var(--cb-font-sans);color:var(--cb-color-text);background-color:var(--cb-color-bg);border:var(--cb-border-width) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-md);cursor:pointer;-webkit-user-select:none;user-select:none;transition:border-color var(--cb-duration-fast) var(--cb-ease),box-shadow var(--cb-duration-fast) var(--cb-ease)}.cb-select__trigger:hover:not(.cb-is-disabled){border-color:var(--cb-color-text-muted)}.cb-select__trigger:focus-visible,.cb-select__trigger.cb-is-open{outline:none;border-color:var(--cb-color-primary-500);box-shadow:0 0 0 3px color-mix(in srgb,var(--cb-color-primary-500) 25%,transparent)}.cb-select__trigger.cb-is-invalid{border-color:var(--cb-color-danger)}.cb-select__trigger.cb-is-invalid.cb-is-open,.cb-select__trigger.cb-is-invalid:focus-visible{box-shadow:0 0 0 3px color-mix(in srgb,var(--cb-color-danger) 25%,transparent)}.cb-select__trigger.cb-is-disabled{cursor:not-allowed;background-color:var(--cb-color-surface);color:var(--cb-color-text-muted);opacity:.8}.cb-select__trigger--sm{min-height:2rem;padding:var(--cb-space-1) var(--cb-space-3);font-size:var(--cb-text-sm)}.cb-select__trigger--md{min-height:2.5rem;padding:var(--cb-space-2) var(--cb-space-3);font-size:var(--cb-text-base)}.cb-select__trigger--lg{min-height:3rem;padding:var(--cb-space-3) var(--cb-space-4);font-size:var(--cb-text-lg)}.cb-select__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cb-select__label--placeholder{color:var(--cb-color-text-muted)}.cb-select__chevron{flex:none;color:var(--cb-color-text-muted);transition:transform var(--cb-duration-fast) var(--cb-ease)}.cb-select__trigger.cb-is-open .cb-select__chevron{transform:rotate(180deg)}.cb-select__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-select__filter{padding:var(--cb-space-2);border-bottom:1px solid var(--cb-color-border)}.cb-select__filter-input{box-sizing:border-box;width:100%;padding:var(--cb-space-2) var(--cb-space-3);font-family:inherit;font-size:var(--cb-text-sm);color:var(--cb-color-text);background-color:var(--cb-color-bg);border:1px solid var(--cb-color-border-strong);border-radius:var(--cb-radius-sm)}.cb-select__filter-input:focus-visible{outline:none;border-color:var(--cb-color-primary-500);box-shadow:0 0 0 2px color-mix(in srgb,var(--cb-color-primary-500) 25%,transparent)}.cb-select__list{list-style:none;margin:0;padding:var(--cb-space-1);max-height:16rem;overflow-y:auto}.cb-select__option{display:flex;align-items:center;padding:var(--cb-space-2) var(--cb-space-3);border-radius:var(--cb-radius-sm);font-size:var(--cb-text-base);color:var(--cb-color-text);cursor:pointer}.cb-select__option.cb-is-active{background-color:var(--cb-color-surface-hover)}.cb-select__option.cb-is-selected{color:var(--cb-color-primary-600);background-color:color-mix(in srgb,var(--cb-color-primary-500) 12%,transparent);font-weight:var(--cb-font-weight-semibold)}.cb-select__option.cb-is-selected.cb-is-active{background-color:color-mix(in srgb,var(--cb-color-primary-500) 20%,transparent)}.cb-select__option.cb-is-disabled{color:var(--cb-color-text-muted);cursor:not-allowed;opacity:.6}.cb-select__empty{padding:var(--cb-space-3);color:var(--cb-color-text-muted);font-size:var(--cb-text-sm);text-align:center}.cb-select__group{list-style:none;margin:0;padding:0}.cb-select__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-select__list--virtual{max-height:none}.cb-select__spacer{position:relative;list-style:none}.cb-select__window{list-style:none;margin:0;padding:0}.cb-select__list--virtual .cb-select__option{box-sizing:border-box;padding-top:0;padding-bottom:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}:host{position:relative}:host(.cb-has-clear) .cb-select__trigger{padding-right:3.25rem}.cb-select__clear{position:absolute;top:50%;right:2.1rem;display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;padding:0;color:var(--cb-color-text-muted);background:none;border:none;border-radius:var(--cb-radius-sm);transform:translateY(-50%);cursor:pointer}.cb-select__clear:hover{color:var(--cb-color-text);background-color:var(--cb-color-surface-hover)}.cb-select__clear:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:1px}\n"] }]
|
|
733
|
+
}], 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 }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", 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 }] }] } });
|
|
457
734
|
function toBoolean(value) {
|
|
458
735
|
return value !== false && value != null && `${value}` !== 'false';
|
|
459
736
|
}
|
|
737
|
+
/** Minúsculas y sin tildes, para que "medellin" encuentre "Medellín". */
|
|
738
|
+
function normalize(text) {
|
|
739
|
+
return text.normalize('NFD').replace(/[̀-ͯ]/g, '').toLocaleLowerCase().trim();
|
|
740
|
+
}
|
|
741
|
+
/** Tecla imprimible sin modificadores (para la búsqueda al teclear). */
|
|
742
|
+
function isPrintable(event) {
|
|
743
|
+
return event.key.length === 1 && event.key !== ' ' && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
744
|
+
}
|
|
745
|
+
/** Convierte un alto CSS (`px` o `rem`) a píxeles para el modo virtual. */
|
|
746
|
+
function cssToPx(value) {
|
|
747
|
+
const n = parseFloat(value);
|
|
748
|
+
if (Number.isNaN(n))
|
|
749
|
+
return 256;
|
|
750
|
+
return value.trim().endsWith('rem') ? n * 16 : n;
|
|
751
|
+
}
|
|
460
752
|
|
|
461
753
|
/*
|
|
462
754
|
* Public API Surface de @caribeng/ui/select
|