ngwr 11.1.0 → 12.0.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 +7 -7
- package/burger/styles/_index.scss +1 -1
- package/button/styles/_index.scss +2 -2
- package/calendar/styles/_index.scss +32 -12
- package/calendar-heatmap/styles/_index.scss +20 -0
- package/carousel/styles/_index.scss +23 -1
- package/cascader/styles/_index.scss +15 -3
- package/checkbox/styles/_index.scss +2 -2
- package/circular-text/styles/_index.scss +11 -0
- package/collapse/styles/_index.scss +2 -2
- package/color-picker/styles/_index.scss +3 -3
- package/command-palette/styles/_index.scss +12 -0
- package/compare/styles/_index.scss +18 -2
- package/context-menu/styles/_index.scss +21 -2
- package/date-picker/styles/_index.scss +5 -5
- package/descriptions/styles/_index.scss +1 -1
- package/drag-drop/styles/_index.scss +28 -1
- package/drawer/styles/_index.scss +37 -0
- package/dropdown/styles/_index.scss +2 -2
- package/event-calendar/styles/_index.scss +67 -10
- package/fesm2022/ngwr-action-sheet-testing.mjs +313 -0
- package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -0
- package/fesm2022/ngwr-action-sheet.mjs +3 -3
- package/fesm2022/ngwr-affix.mjs +3 -3
- package/fesm2022/ngwr-alert.mjs +3 -3
- package/fesm2022/ngwr-anchor.mjs +3 -3
- package/fesm2022/ngwr-aurora.mjs +3 -3
- package/fesm2022/ngwr-avatar.mjs +3 -3
- package/fesm2022/ngwr-back-top.mjs +3 -3
- package/fesm2022/ngwr-badge.mjs +6 -6
- package/fesm2022/ngwr-bar-chart-testing.mjs +107 -0
- package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -0
- package/fesm2022/ngwr-bar-chart.mjs +3 -3
- package/fesm2022/ngwr-blur-text-testing.mjs +203 -0
- package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-blur-text.mjs +18 -4
- package/fesm2022/ngwr-blur-text.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow-testing.mjs +325 -0
- package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -0
- package/fesm2022/ngwr-border-glow.mjs +4 -4
- package/fesm2022/ngwr-border-glow.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +29 -17
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-burger.mjs +3 -3
- package/fesm2022/ngwr-button.mjs +6 -6
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs +95 -0
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -0
- package/fesm2022/ngwr-calendar-heatmap.mjs +17 -5
- package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-testing.mjs +368 -0
- package/fesm2022/ngwr-calendar-testing.mjs.map +1 -0
- package/fesm2022/ngwr-calendar.mjs +228 -10
- package/fesm2022/ngwr-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +10 -10
- package/fesm2022/ngwr-card.mjs.map +1 -1
- package/fesm2022/ngwr-carousel.mjs +124 -24
- package/fesm2022/ngwr-carousel.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +60 -5
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox.mjs +14 -6
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text-testing.mjs +230 -0
- package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-circular-text.mjs +4 -4
- package/fesm2022/ngwr-circular-text.mjs.map +1 -1
- package/fesm2022/ngwr-click-spark.mjs +27 -7
- package/fesm2022/ngwr-click-spark.mjs.map +1 -1
- package/fesm2022/ngwr-clipboard.mjs +3 -3
- package/fesm2022/ngwr-collapse.mjs +9 -9
- package/fesm2022/ngwr-color-picker-testing.mjs +452 -0
- package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -0
- package/fesm2022/ngwr-color-picker.mjs +126 -10
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-command-palette.mjs +14 -5
- package/fesm2022/ngwr-command-palette.mjs.map +1 -1
- package/fesm2022/ngwr-compare.mjs +3 -3
- package/fesm2022/ngwr-confetti.mjs +31 -6
- package/fesm2022/ngwr-confetti.mjs.map +1 -1
- package/fesm2022/ngwr-config.mjs +9 -0
- package/fesm2022/ngwr-config.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu-testing.mjs +30 -11
- package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu.mjs +358 -48
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-cookie.mjs +3 -3
- package/fesm2022/ngwr-counter.mjs +25 -9
- package/fesm2022/ngwr-counter.mjs.map +1 -1
- package/fesm2022/{ngwr-date-adapter-fns.mjs → ngwr-date-adapters-fns.mjs} +37 -10
- package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -0
- package/fesm2022/{ngwr-date-adapter-luxon.mjs → ngwr-date-adapters-luxon.mjs} +5 -5
- package/fesm2022/ngwr-date-adapters-luxon.mjs.map +1 -0
- package/fesm2022/ngwr-date-picker-testing.mjs +21 -85
- package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker.mjs +119 -37
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/{ngwr-date-adapter.mjs → ngwr-date.mjs} +36 -10
- package/fesm2022/ngwr-date.mjs.map +1 -0
- package/fesm2022/ngwr-decrypt-text-testing.mjs +261 -0
- package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-decrypt-text.mjs +9 -4
- package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
- package/fesm2022/ngwr-density.mjs +6 -6
- package/fesm2022/ngwr-descriptions.mjs +6 -6
- package/fesm2022/ngwr-dialog.mjs +73 -18
- package/fesm2022/ngwr-dialog.mjs.map +1 -1
- package/fesm2022/ngwr-directives.mjs +12 -12
- package/fesm2022/ngwr-divider.mjs +52 -5
- package/fesm2022/ngwr-divider.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart-testing.mjs +97 -0
- package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -0
- package/fesm2022/ngwr-donut-chart.mjs +7 -7
- package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +160 -11
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
- package/fesm2022/ngwr-drawer.mjs +156 -64
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +58 -12
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-empty.mjs +3 -3
- package/fesm2022/ngwr-event-calendar-testing.mjs +446 -0
- package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -0
- package/fesm2022/ngwr-event-calendar.mjs +66 -28
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text-testing.mjs +220 -0
- package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-falling-text.mjs +75 -46
- package/fesm2022/ngwr-falling-text.mjs.map +1 -1
- package/fesm2022/ngwr-file-upload.mjs +3 -3
- package/fesm2022/ngwr-form.mjs +32 -15
- package/fesm2022/ngwr-form.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text-testing.mjs +134 -0
- package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-fuzzy-text.mjs +21 -11
- package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
- package/fesm2022/ngwr-gauge-testing.mjs +86 -0
- package/fesm2022/ngwr-gauge-testing.mjs.map +1 -0
- package/fesm2022/ngwr-gauge.mjs +3 -3
- package/fesm2022/ngwr-glitch-text-testing.mjs +215 -0
- package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-glitch-text.mjs +7 -7
- package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
- package/fesm2022/ngwr-gradient-text-testing.mjs +258 -0
- package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-gradient-text.mjs +4 -4
- package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
- package/fesm2022/ngwr-hotkey.mjs +24 -9
- package/fesm2022/ngwr-hotkey.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +87 -1
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +69 -1
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +26 -15
- package/fesm2022/ngwr-i18n.mjs.map +1 -1
- package/fesm2022/ngwr-icon.mjs +6 -6
- package/fesm2022/ngwr-image-cropper-testing.mjs +200 -0
- package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -0
- package/fesm2022/ngwr-image-cropper.mjs +156 -11
- package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
- package/fesm2022/ngwr-input-number-testing.mjs +15 -10
- package/fesm2022/ngwr-input-number-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input-number.mjs +89 -15
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp-testing.mjs +12 -6
- package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp.mjs +24 -5
- package/fesm2022/ngwr-input-otp.mjs.map +1 -1
- package/fesm2022/ngwr-input.mjs +15 -15
- package/fesm2022/ngwr-keyboard.mjs +3 -3
- package/fesm2022/ngwr-knob-testing.mjs +220 -0
- package/fesm2022/ngwr-knob-testing.mjs.map +1 -0
- package/fesm2022/ngwr-knob.mjs +3 -3
- package/fesm2022/ngwr-layout.mjs +15 -15
- package/fesm2022/ngwr-lightbox-testing.mjs +223 -0
- package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -0
- package/fesm2022/ngwr-lightbox.mjs +11 -4
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart-testing.mjs +119 -0
- package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -0
- package/fesm2022/ngwr-line-chart.mjs +73 -26
- package/fesm2022/ngwr-line-chart.mjs.map +1 -1
- package/fesm2022/ngwr-list.mjs +7 -7
- package/fesm2022/ngwr-list.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar.mjs +7 -7
- package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
- package/fesm2022/ngwr-markdown.mjs +66 -8
- package/fesm2022/ngwr-markdown.mjs.map +1 -1
- package/fesm2022/ngwr-marquee-testing.mjs +336 -0
- package/fesm2022/ngwr-marquee-testing.mjs.map +1 -0
- package/fesm2022/ngwr-marquee.mjs +4 -4
- package/fesm2022/ngwr-marquee.mjs.map +1 -1
- package/fesm2022/ngwr-media.mjs +3 -3
- package/fesm2022/ngwr-mention.mjs +108 -13
- package/fesm2022/ngwr-mention.mjs.map +1 -1
- package/fesm2022/ngwr-meta.mjs +6 -6
- package/fesm2022/ngwr-meter-group-testing.mjs +100 -0
- package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -0
- package/fesm2022/ngwr-meter-group.mjs +3 -3
- package/fesm2022/ngwr-overlay.mjs +34 -7
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-page-header.mjs +3 -3
- package/fesm2022/ngwr-pagination-testing.mjs +6 -6
- package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +17 -13
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-pipes.mjs +39 -26
- package/fesm2022/ngwr-pipes.mjs.map +1 -1
- package/fesm2022/ngwr-platform.mjs +9 -9
- package/fesm2022/ngwr-popconfirm.mjs +6 -6
- package/fesm2022/ngwr-popover-testing.mjs +7 -2
- package/fesm2022/ngwr-popover-testing.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +169 -33
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-progress.mjs +3 -3
- package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
- package/fesm2022/ngwr-qr.mjs +42 -5
- package/fesm2022/ngwr-qr.mjs.map +1 -1
- package/fesm2022/ngwr-radio.mjs +6 -6
- package/fesm2022/ngwr-rating.mjs +3 -3
- package/fesm2022/ngwr-result.mjs +21 -30
- package/fesm2022/ngwr-result.mjs.map +1 -1
- package/fesm2022/ngwr-rotating-text-testing.mjs +220 -0
- package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-rotating-text.mjs +4 -4
- package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
- package/fesm2022/ngwr-scroll.mjs +3 -3
- package/fesm2022/ngwr-segmented-testing.mjs +14 -1
- package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
- package/fesm2022/ngwr-segmented.mjs +29 -6
- package/fesm2022/ngwr-segmented.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +227 -43
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text-testing.mjs +237 -0
- package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-shiny-text.mjs +11 -9
- package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
- package/fesm2022/ngwr-sidebar.mjs +31 -16
- package/fesm2022/ngwr-sidebar.mjs.map +1 -1
- package/fesm2022/ngwr-skeleton.mjs +3 -3
- package/fesm2022/ngwr-slider.mjs +143 -14
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline-testing.mjs +76 -0
- package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -0
- package/fesm2022/ngwr-sparkline.mjs +3 -3
- package/fesm2022/ngwr-speed-dial-testing.mjs +281 -0
- package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -0
- package/fesm2022/ngwr-speed-dial.mjs +27 -12
- package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
- package/fesm2022/ngwr-spinner.mjs +3 -3
- package/fesm2022/ngwr-splash-cursor-testing.mjs +89 -0
- package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -0
- package/fesm2022/ngwr-splash-cursor.mjs +3 -3
- package/fesm2022/ngwr-split-text-testing.mjs +224 -0
- package/fesm2022/ngwr-split-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-split-text.mjs +31 -5
- package/fesm2022/ngwr-split-text.mjs.map +1 -1
- package/fesm2022/ngwr-splitter-testing.mjs +245 -0
- package/fesm2022/ngwr-splitter-testing.mjs.map +1 -0
- package/fesm2022/ngwr-splitter.mjs +3 -3
- package/fesm2022/ngwr-spotlight-card-testing.mjs +368 -0
- package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -0
- package/fesm2022/ngwr-spotlight-card.mjs +7 -7
- package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
- package/fesm2022/ngwr-squircle.mjs +6 -6
- package/fesm2022/ngwr-star-border-testing.mjs +172 -0
- package/fesm2022/ngwr-star-border-testing.mjs.map +1 -0
- package/fesm2022/ngwr-star-border.mjs +4 -4
- package/fesm2022/ngwr-star-border.mjs.map +1 -1
- package/fesm2022/ngwr-statistic.mjs +9 -9
- package/fesm2022/ngwr-stepper-testing.mjs +8 -8
- package/fesm2022/ngwr-stepper-testing.mjs.map +1 -1
- package/fesm2022/ngwr-stepper.mjs +33 -8
- package/fesm2022/ngwr-stepper.mjs.map +1 -1
- package/fesm2022/ngwr-storage.mjs +17 -5
- package/fesm2022/ngwr-storage.mjs.map +1 -1
- package/fesm2022/ngwr-switch.mjs +3 -3
- package/fesm2022/ngwr-table.mjs +110 -40
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-tabs-testing.mjs +11 -10
- package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tabs.mjs +51 -11
- package/fesm2022/ngwr-tabs.mjs.map +1 -1
- package/fesm2022/ngwr-textarea.mjs +3 -3
- package/fesm2022/ngwr-theme.mjs +181 -4
- package/fesm2022/ngwr-theme.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card-testing.mjs +190 -0
- package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -0
- package/fesm2022/ngwr-tilt-card.mjs +26 -8
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
- package/fesm2022/ngwr-timeline.mjs +6 -6
- package/fesm2022/ngwr-toast.mjs +44 -15
- package/fesm2022/ngwr-toast.mjs.map +1 -1
- package/fesm2022/ngwr-toolbar.mjs +3 -3
- package/fesm2022/ngwr-tour-testing.mjs +173 -0
- package/fesm2022/ngwr-tour-testing.mjs.map +1 -0
- package/fesm2022/ngwr-tour.mjs +34 -10
- package/fesm2022/ngwr-tour.mjs.map +1 -1
- package/fesm2022/ngwr-transfer.mjs +3 -3
- package/fesm2022/ngwr-tree.mjs +36 -11
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter-testing.mjs +242 -0
- package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -0
- package/fesm2022/ngwr-typewriter.mjs +13 -5
- package/fesm2022/ngwr-typewriter.mjs.map +1 -1
- package/fesm2022/ngwr-typography.mjs +3 -3
- package/fesm2022/ngwr-utils.mjs +5 -2
- package/fesm2022/ngwr-utils.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-virtual-scroll.mjs +4 -4
- package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
- package/fesm2022/ngwr-waves-testing.mjs +128 -0
- package/fesm2022/ngwr-waves-testing.mjs.map +1 -0
- package/fesm2022/ngwr-waves.mjs +4 -4
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/fesm2022/ngwr-window-testing.mjs +361 -0
- package/fesm2022/ngwr-window-testing.mjs.map +1 -0
- package/fesm2022/ngwr-window.mjs +175 -31
- package/fesm2022/ngwr-window.mjs.map +1 -1
- package/file-upload/styles/_index.scss +2 -2
- package/glitch-text/styles/_index.scss +27 -12
- package/i18n/en.json +71 -7
- package/i18n/ru.json +71 -7
- package/image-cropper/styles/_index.scss +19 -0
- package/input/styles/_index.scss +2 -2
- package/input-number/styles/_index.scss +2 -2
- package/input-otp/styles/_index.scss +9 -2
- package/knob/styles/_index.scss +1 -1
- package/lightbox/styles/_index.scss +16 -2
- package/line-chart/styles/_index.scss +28 -14
- package/list/styles/_index.scss +1 -1
- package/llms-full.txt +161 -19
- package/llms.txt +45 -10
- package/markdown/styles/_index.scss +3 -3
- package/mcp/api.js +6 -4
- package/mcp/catalog.js +40 -3
- package/mcp/providers.js +42 -0
- package/mcp/tools.js +22 -30
- package/meter-group/styles/_index.scss +1 -1
- package/overlay/styles/_index.scss +33 -1
- package/package.json +161 -12
- package/pagination/styles/_index.scss +23 -1
- package/popover/styles/_index.scss +16 -7
- package/progress/styles/_index.scss +1 -1
- package/radio/styles/_index.scss +2 -2
- package/rating/styles/_index.scss +1 -1
- package/schematics/collection.json +1 -1
- package/schematics/migrations/v12/index.js +65 -0
- package/schematics/migrations/v7/index.js +12 -1
- package/schematics/migrations/v8/index.js +10 -1
- package/schematics/migrations.json +5 -0
- package/schematics/ng-add/index.js +8 -3
- package/schematics/provider/index.js +80 -13
- package/schematics/use/index.js +9 -129
- package/schematics/use/rule.js +206 -0
- package/schematics/use/symbol-map.json +4 -4
- package/segmented/styles/_index.scss +14 -3
- package/select/styles/_index.scss +14 -2
- package/sidebar/styles/_index.scss +12 -10
- package/skills/ngwr/SKILL.md +117 -0
- package/skills/ngwr/references/catalog.md +212 -0
- package/skills/ngwr/references/setup.md +78 -0
- package/slider/styles/_index.scss +1 -1
- package/speed-dial/styles/_index.scss +1 -1
- package/stepper/styles/_index.scss +12 -3
- package/switch/styles/_index.scss +2 -2
- package/table/styles/_index.scss +61 -12
- package/tabs/styles/_index.scss +1 -1
- package/textarea/styles/_index.scss +1 -1
- package/theme/styles/_colors.scss +142 -56
- package/theme/styles/_dark.scss +46 -3
- package/theme/styles/_focus.scss +28 -5
- package/theme/styles/_mixins.scss +1 -1
- package/theme/styles/_variables.scss +67 -0
- package/transfer/styles/_index.scss +26 -3
- package/tree/styles/_index.scss +29 -6
- package/types/ngwr-action-sheet-testing.d.ts +260 -0
- package/types/ngwr-bar-chart-testing.d.ts +85 -0
- package/types/ngwr-blur-text-testing.d.ts +180 -0
- package/types/ngwr-blur-text.d.ts +20 -4
- package/types/ngwr-border-glow-testing.d.ts +219 -0
- package/types/ngwr-breadcrumbs.d.ts +17 -10
- package/types/ngwr-calendar-heatmap-testing.d.ts +79 -0
- package/types/ngwr-calendar-testing.d.ts +277 -0
- package/types/ngwr-calendar.d.ts +76 -0
- package/types/ngwr-carousel.d.ts +85 -6
- package/types/ngwr-cascader.d.ts +17 -0
- package/types/ngwr-checkbox.d.ts +13 -0
- package/types/ngwr-circular-text-testing.d.ts +196 -0
- package/types/ngwr-click-spark.d.ts +4 -0
- package/types/ngwr-color-picker-testing.d.ts +299 -0
- package/types/ngwr-color-picker.d.ts +34 -2
- package/types/ngwr-confetti.d.ts +8 -0
- package/types/ngwr-config.d.ts +10 -1
- package/types/ngwr-context-menu-testing.d.ts +24 -11
- package/types/ngwr-context-menu.d.ts +71 -1
- package/types/ngwr-counter.d.ts +1 -0
- package/types/{ngwr-date-adapter-fns.d.ts → ngwr-date-adapters-fns.d.ts} +33 -6
- package/types/{ngwr-date-adapter-luxon.d.ts → ngwr-date-adapters-luxon.d.ts} +1 -1
- package/types/ngwr-date-picker-testing.d.ts +17 -72
- package/types/ngwr-date-picker.d.ts +8 -4
- package/types/{ngwr-date-adapter.d.ts → ngwr-date.d.ts} +24 -5
- package/types/ngwr-decrypt-text-testing.d.ts +222 -0
- package/types/ngwr-dialog.d.ts +38 -1
- package/types/ngwr-divider.d.ts +25 -0
- package/types/ngwr-donut-chart-testing.d.ts +83 -0
- package/types/ngwr-donut-chart.d.ts +3 -3
- package/types/ngwr-drag-drop.d.ts +56 -2
- package/types/ngwr-drawer.d.ts +68 -5
- package/types/ngwr-dropdown.d.ts +9 -0
- package/types/ngwr-event-calendar-testing.d.ts +301 -0
- package/types/ngwr-event-calendar.d.ts +17 -8
- package/types/ngwr-falling-text-testing.d.ts +188 -0
- package/types/ngwr-falling-text.d.ts +1 -1
- package/types/ngwr-form.d.ts +12 -2
- package/types/ngwr-fuzzy-text-testing.d.ts +130 -0
- package/types/ngwr-gauge-testing.d.ts +71 -0
- package/types/ngwr-glitch-text-testing.d.ts +201 -0
- package/types/ngwr-gradient-text-testing.d.ts +186 -0
- package/types/ngwr-i18n.d.ts +13 -4
- package/types/ngwr-image-cropper-testing.d.ts +173 -0
- package/types/ngwr-image-cropper.d.ts +71 -5
- package/types/ngwr-input-number-testing.d.ts +15 -10
- package/types/ngwr-input-number.d.ts +44 -5
- package/types/ngwr-input-otp-testing.d.ts +12 -6
- package/types/ngwr-input-otp.d.ts +15 -1
- package/types/ngwr-knob-testing.d.ts +150 -0
- package/types/ngwr-lightbox-testing.d.ts +154 -0
- package/types/ngwr-lightbox.d.ts +6 -0
- package/types/ngwr-line-chart-testing.d.ts +93 -0
- package/types/ngwr-line-chart.d.ts +20 -3
- package/types/ngwr-markdown.d.ts +11 -0
- package/types/ngwr-marquee-testing.d.ts +280 -0
- package/types/ngwr-mention.d.ts +32 -0
- package/types/ngwr-meter-group-testing.d.ts +77 -0
- package/types/ngwr-overlay.d.ts +20 -1
- package/types/ngwr-pagination-testing.d.ts +4 -4
- package/types/ngwr-pipes.d.ts +1 -1
- package/types/ngwr-popover-testing.d.ts +7 -2
- package/types/ngwr-popover.d.ts +1 -0
- package/types/ngwr-result.d.ts +6 -0
- package/types/ngwr-rotating-text-testing.d.ts +191 -0
- package/types/ngwr-segmented-testing.d.ts +9 -0
- package/types/ngwr-segmented.d.ts +21 -1
- package/types/ngwr-select.d.ts +100 -6
- package/types/ngwr-shiny-text-testing.d.ts +165 -0
- package/types/ngwr-shiny-text.d.ts +4 -2
- package/types/ngwr-sidebar.d.ts +14 -5
- package/types/ngwr-slider.d.ts +84 -1
- package/types/ngwr-sparkline-testing.d.ts +69 -0
- package/types/ngwr-speed-dial-testing.d.ts +199 -0
- package/types/ngwr-speed-dial.d.ts +22 -3
- package/types/ngwr-splash-cursor-testing.d.ts +95 -0
- package/types/ngwr-split-text-testing.d.ts +209 -0
- package/types/ngwr-split-text.d.ts +21 -0
- package/types/ngwr-splitter-testing.d.ts +174 -0
- package/types/ngwr-spotlight-card-testing.d.ts +303 -0
- package/types/ngwr-star-border-testing.d.ts +135 -0
- package/types/ngwr-stepper-testing.d.ts +5 -5
- package/types/ngwr-stepper.d.ts +8 -0
- package/types/ngwr-storage.d.ts +1 -0
- package/types/ngwr-table.d.ts +114 -12
- package/types/ngwr-tabs-testing.d.ts +11 -10
- package/types/ngwr-tabs.d.ts +2 -0
- package/types/ngwr-theme.d.ts +82 -1
- package/types/ngwr-tilt-card-testing.d.ts +158 -0
- package/types/ngwr-tilt-card.d.ts +2 -0
- package/types/ngwr-toast.d.ts +14 -5
- package/types/ngwr-tour-testing.d.ts +143 -0
- package/types/ngwr-tour.d.ts +22 -4
- package/types/ngwr-tree.d.ts +12 -4
- package/types/ngwr-typewriter-testing.d.ts +211 -0
- package/types/ngwr-utils.d.ts +5 -2
- package/types/ngwr-version.d.ts +1 -1
- package/types/ngwr-waves-testing.d.ts +121 -0
- package/types/ngwr-window-testing.d.ts +263 -0
- package/types/ngwr-window.d.ts +66 -21
- package/typography/styles/_index.scss +1 -1
- package/window/styles/_index.scss +116 -42
- package/fesm2022/ngwr-date-adapter-fns.mjs.map +0 -1
- package/fesm2022/ngwr-date-adapter-luxon.mjs.map +0 -1
- package/fesm2022/ngwr-date-adapter.mjs.map +0 -1
package/fesm2022/ngwr-select.mjs
CHANGED
|
@@ -5,8 +5,9 @@ import { isPlatformBrowser } from '@angular/common';
|
|
|
5
5
|
import * as i0 from '@angular/core';
|
|
6
6
|
import { InjectionToken, input, inject, ElementRef, computed, effect, DestroyRef, ViewEncapsulation, Component, model, output, signal, contentChildren, viewChild, TemplateRef, ViewContainerRef, PLATFORM_ID, forwardRef } from '@angular/core';
|
|
7
7
|
import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
8
|
-
import { skip, debounce, timer, switchMap, of, isObservable, from, tap, finalize } from 'rxjs';
|
|
8
|
+
import { skip, debounce, timer, switchMap, of, defer, isObservable, from, tap, catchError, finalize } from 'rxjs';
|
|
9
9
|
import { useConfigValue } from 'ngwr/config';
|
|
10
|
+
import { WR_FORM_FIELD } from 'ngwr/form';
|
|
10
11
|
import { useI18nText, useI18nFormatter } from 'ngwr/i18n';
|
|
11
12
|
import { WR_OVERLAY, WrOutsideClick, WR_RESPONSIVE_OVERLAYS, wrPresentAsSheet } from 'ngwr/overlay';
|
|
12
13
|
import { randomId } from 'ngwr/utils';
|
|
@@ -122,10 +123,10 @@ class WrOption {
|
|
|
122
123
|
return;
|
|
123
124
|
this.parent.selectOption(this.value());
|
|
124
125
|
}
|
|
125
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
126
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
126
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrOption, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
127
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: WrOption, isStandalone: true, selector: "wr-option", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "click": "onClick()" }, properties: { "attr.id": "id", "class": "classes()", "attr.aria-selected": "selected()", "attr.aria-disabled": "disabled() ? true : null" } }, ngImport: i0, template: '<ng-content />', isInline: true, encapsulation: i0.ViewEncapsulation.None });
|
|
127
128
|
}
|
|
128
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
129
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrOption, decorators: [{
|
|
129
130
|
type: Component,
|
|
130
131
|
args: [{
|
|
131
132
|
selector: 'wr-option',
|
|
@@ -230,6 +231,51 @@ class WrSelect {
|
|
|
230
231
|
...(ngDevMode ? [{ debugName: "resolvedAriaLabel" }] : /* istanbul ignore next */ []));
|
|
231
232
|
/** Per-chip ARIA label — interpolates `{{label}}`. @internal */
|
|
232
233
|
chipRemoveLabel = useI18nFormatter('select.removeItem', 'Remove {{label}}');
|
|
234
|
+
/**
|
|
235
|
+
* The overflow chip past `maxTagCount` — interpolates `{{count}}`. Through the
|
|
236
|
+
* catalog, like every other string here: printed as a bare `+{{ extra }} more`
|
|
237
|
+
* it stayed English in a fully localized app, beside an event-calendar cell
|
|
238
|
+
* reading "ещё 3" from the same string. @internal
|
|
239
|
+
*/
|
|
240
|
+
moreLabel = useI18nFormatter('select.more', '+{{count}} more');
|
|
241
|
+
field = inject(WR_FORM_FIELD, { optional: true });
|
|
242
|
+
/**
|
|
243
|
+
* Id the surrounding `<wr-form-field>`'s `<label for>` points at.
|
|
244
|
+
*
|
|
245
|
+
* The field renders its label before it can see what was projected into it, so
|
|
246
|
+
* the id travels the other way and the control adopts it, exactly as `[wrInput]`
|
|
247
|
+
* and `wr-slider` do. Without this the `for` named an element that was nowhere in
|
|
248
|
+
* the document: clicking the label did nothing, and the field's own text reached
|
|
249
|
+
* the control through no path at all.
|
|
250
|
+
*
|
|
251
|
+
* It lands on the ONE focusable element each trigger shape has — the button, the
|
|
252
|
+
* search input, or the tag input — never on the host, because only a labelable
|
|
253
|
+
* element can be a label's target and `<wr-select>` is not one.
|
|
254
|
+
*
|
|
255
|
+
* What this does NOT change is that element's NAME: `aria-label` outranks a
|
|
256
|
+
* `<label>` in the accname order, and the trigger keeps `resolvedAriaLabel()`
|
|
257
|
+
* because the field cannot promise it has a name to give — `<wr-form-field>`
|
|
258
|
+
* renders a label only when its `label` input is set, and a combobox that traded
|
|
259
|
+
* a generic name for no name would be the worse bug. `WrFormFieldContext`
|
|
260
|
+
* publishes no "do I have a visible label" signal to key that on. Until it does,
|
|
261
|
+
* set `[ariaLabel]` to the field's label where the two should read alike.
|
|
262
|
+
*/
|
|
263
|
+
controlId = computed(() => this.field?.controlId() ?? null, /* @ts-ignore */
|
|
264
|
+
...(ngDevMode ? [{ debugName: "controlId" }] : /* istanbul ignore next */ []));
|
|
265
|
+
/**
|
|
266
|
+
* The field's error message, wired to the same element.
|
|
267
|
+
*
|
|
268
|
+
* Without it the messages `<wr-form-field>` renders are visible and nothing else:
|
|
269
|
+
* a screen reader on the trigger never learns the field is invalid, nor what the
|
|
270
|
+
* message says. `aria-invalid` is keyed on the message EXISTING rather than on
|
|
271
|
+
* `errorKeys()`, because the field only publishes an id once it is showing
|
|
272
|
+
* something — announcing "invalid" while pointing at nothing is worse than
|
|
273
|
+
* staying quiet.
|
|
274
|
+
*/
|
|
275
|
+
describedBy = computed(() => this.field?.describedBy() ?? null, /* @ts-ignore */
|
|
276
|
+
...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
|
|
277
|
+
ariaInvalid = computed(() => (this.field?.describedBy() ? 'true' : null), /* @ts-ignore */
|
|
278
|
+
...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
|
|
233
279
|
/**
|
|
234
280
|
* Disable the select. Bound automatically from the field's disabled state
|
|
235
281
|
* when used with `[formField]`.
|
|
@@ -628,9 +674,18 @@ class WrSelect {
|
|
|
628
674
|
// --- Virtual scroll (search mode, data-array path) ------------------------
|
|
629
675
|
static FALLBACK_ROW_PX = 34;
|
|
630
676
|
static FALLBACK_VIEWPORT_PX = 256;
|
|
631
|
-
/**
|
|
632
|
-
|
|
633
|
-
|
|
677
|
+
/**
|
|
678
|
+
* Consumer-projected `<wr-option>` children only (NOT the `@for`-rendered ones
|
|
679
|
+
* — those live in the panel's own view, which a content query never sees).
|
|
680
|
+
*
|
|
681
|
+
* `descendants: true` because a projected option is as often wrapped in a
|
|
682
|
+
* `<wr-option-group>` as it is a direct child, and the signal default is
|
|
683
|
+
* `false`. A grouped list therefore read as NO projected options: `virtualActive`
|
|
684
|
+
* engaged over the (empty) data array, so the panel rendered the group's rows
|
|
685
|
+
* with a virtual window beside them that owned the keyboard — no
|
|
686
|
+
* `aria-activedescendant`, no active row, and Enter committing nothing.
|
|
687
|
+
*/
|
|
688
|
+
projectedOptions = contentChildren(WrOption, { ...(ngDevMode ? { debugName: "projectedOptions" } : /* istanbul ignore next */ {}), descendants: true });
|
|
634
689
|
/**
|
|
635
690
|
* `dynamicOptions()` narrowed to the current search query. `dynamicOptions()`
|
|
636
691
|
* is intentionally UNFILTERED (projected `<wr-option>` self-hide via CSS), so
|
|
@@ -649,14 +704,20 @@ class WrSelect {
|
|
|
649
704
|
}, /* @ts-ignore */
|
|
650
705
|
...(ngDevMode ? [{ debugName: "filteredDynamicOptions" }] : /* istanbul ignore next */ []));
|
|
651
706
|
/**
|
|
652
|
-
* Virtualization engaged: opt-in, search mode,
|
|
653
|
-
*
|
|
654
|
-
*
|
|
707
|
+
* Virtualization engaged: opt-in, search mode, and NO projected `<wr-option>`
|
|
708
|
+
* children — grouped ones included (those keep the full-render registry path so
|
|
709
|
+
* their DOM-derived labels / selection still work).
|
|
710
|
+
*
|
|
711
|
+
* NOT gated on there being matches. It used to also require
|
|
712
|
+
* `filteredDynamicOptions().length > 0`, which meant a query matching nothing
|
|
713
|
+
* fell back to the full-render path and mounted the ENTIRE unfiltered array —
|
|
714
|
+
* 2000 `<wr-option>` hosts, every one of them `--hidden`, behind a "No
|
|
715
|
+
* results" line — and then tore all of them down again on the next keystroke
|
|
716
|
+
* that matched. The empty window is handled where it belongs: `virtualWindow`
|
|
717
|
+
* already short-circuits on `total === 0`, and `firstEnabled` / `lastEnabled`
|
|
718
|
+
* already answer -1.
|
|
655
719
|
*/
|
|
656
|
-
virtualActive = computed(() => this.virtualScroll() &&
|
|
657
|
-
this.isSearchable() &&
|
|
658
|
-
this.projectedOptions().length === 0 &&
|
|
659
|
-
this.filteredDynamicOptions().length > 0, /* @ts-ignore */
|
|
720
|
+
virtualActive = computed(() => this.virtualScroll() && this.isSearchable() && this.projectedOptions().length === 0, /* @ts-ignore */
|
|
660
721
|
...(ngDevMode ? [{ debugName: "virtualActive" }] : /* istanbul ignore next */ []));
|
|
661
722
|
scrollTop = signal(0, /* @ts-ignore */
|
|
662
723
|
...(ngDevMode ? [{ debugName: "scrollTop" }] : /* istanbul ignore next */ []));
|
|
@@ -848,6 +909,7 @@ class WrSelect {
|
|
|
848
909
|
onSearchFocus() {
|
|
849
910
|
if (this.isDisabled())
|
|
850
911
|
return;
|
|
912
|
+
this.searchHasFocus.set(true);
|
|
851
913
|
if (!this.meetsMinChars())
|
|
852
914
|
return;
|
|
853
915
|
if (!this.open()) {
|
|
@@ -855,6 +917,14 @@ class WrSelect {
|
|
|
855
917
|
this.open.set(true);
|
|
856
918
|
}
|
|
857
919
|
}
|
|
920
|
+
onSearchBlur() {
|
|
921
|
+
this.searchHasFocus.set(false);
|
|
922
|
+
// Leaving the field is what makes a below-threshold query stale, so this is
|
|
923
|
+
// where the panel-close reset would have run if it could have waited.
|
|
924
|
+
if (!this.open() && this.isSearchable())
|
|
925
|
+
this.searchQuery.set('');
|
|
926
|
+
this.touch.emit();
|
|
927
|
+
}
|
|
858
928
|
onSearchKey(event) {
|
|
859
929
|
// freeText: Enter on an unmatched query commits the typed string
|
|
860
930
|
// as the value. Skips when an active option is highlighted (the
|
|
@@ -884,13 +954,34 @@ class WrSelect {
|
|
|
884
954
|
// keeps the panel open, so it never reaches that branch.
|
|
885
955
|
if (event.key === 'Backspace' && this.hasChipSearch() && this.searchQuery() === '' && this.hasSelection()) {
|
|
886
956
|
const chips = this.selectedChips();
|
|
887
|
-
|
|
888
|
-
if (last) {
|
|
957
|
+
if (chips.length > 0) {
|
|
889
958
|
event.preventDefault();
|
|
890
|
-
this.removeChip(
|
|
959
|
+
this.removeChip(chips.length - 1, event);
|
|
891
960
|
return;
|
|
892
961
|
}
|
|
893
962
|
}
|
|
963
|
+
// The same key, for the searchable SINGLE: Backspace on an empty field is
|
|
964
|
+
// its clear control. Every other mode could already undo a selection from
|
|
965
|
+
// the keyboard — the chip modes above, the button trigger one chip per
|
|
966
|
+
// Backspace — while here the × was the only way out, and it is a
|
|
967
|
+
// `tabindex="-1"` span no key can reach. A function offered by pointer
|
|
968
|
+
// alone is a WCAG 2.1.1 failure, not a missing convenience.
|
|
969
|
+
//
|
|
970
|
+
// Gated on `clearable()` because that input IS the clear control: a consumer
|
|
971
|
+
// who turned the × off did not ask for a keyboard twin of it. Gated on the
|
|
972
|
+
// FIELD being empty rather than on `searchQuery()`, which is not the same
|
|
973
|
+
// question — under `[minChars]` a collapsed field shows the selected label
|
|
974
|
+
// while the query is still '', and backspacing over that text has always
|
|
975
|
+
// started a query rather than thrown the selection away.
|
|
976
|
+
if (event.key === 'Backspace' &&
|
|
977
|
+
!this.hasChipSearch() &&
|
|
978
|
+
this.clearable() &&
|
|
979
|
+
this.hasSelection() &&
|
|
980
|
+
event.target?.value === '') {
|
|
981
|
+
event.preventDefault();
|
|
982
|
+
this.clearAll(event);
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
894
985
|
// Otherwise route through the same keyboard plumbing as the button trigger.
|
|
895
986
|
this.onTriggerKey(event);
|
|
896
987
|
}
|
|
@@ -900,11 +991,31 @@ class WrSelect {
|
|
|
900
991
|
* @internal
|
|
901
992
|
*/
|
|
902
993
|
searchDisplay = computed(() => {
|
|
994
|
+
// FOCUS as well as open, and that second term is the whole `[minChars]`
|
|
995
|
+
// story. Backspacing under the threshold closes the panel, and falling back
|
|
996
|
+
// to the selected label the moment `open()` goes false rewrote the field
|
|
997
|
+
// out from under someone who was still typing — "sma" -> "sm" showed an
|
|
998
|
+
// empty box.
|
|
999
|
+
//
|
|
1000
|
+
// A NON-EMPTY query, though: picking an option clears it and leaves focus
|
|
1001
|
+
// in the field, and there the label is what belongs on screen.
|
|
903
1002
|
if (this.open())
|
|
904
1003
|
return this.searchQuery();
|
|
1004
|
+
if (this.searchHasFocus() && this.searchQuery() !== '')
|
|
1005
|
+
return this.searchQuery();
|
|
905
1006
|
return this.selectedLabel() ?? '';
|
|
906
1007
|
}, /* @ts-ignore */
|
|
907
1008
|
...(ngDevMode ? [{ debugName: "searchDisplay" }] : /* istanbul ignore next */ []));
|
|
1009
|
+
/**
|
|
1010
|
+
* Whether the search field holds focus.
|
|
1011
|
+
*
|
|
1012
|
+
* Read by `searchDisplay`, so it has to be a signal — and it is also what
|
|
1013
|
+
* lets the panel-close effect tell "the user walked away" (reset the query)
|
|
1014
|
+
* from "the query dropped under `minChars` and they are still typing" (keep
|
|
1015
|
+
* it).
|
|
1016
|
+
*/
|
|
1017
|
+
searchHasFocus = signal(false, /* @ts-ignore */
|
|
1018
|
+
...(ngDevMode ? [{ debugName: "searchHasFocus" }] : /* istanbul ignore next */ []));
|
|
908
1019
|
host = inject(ElementRef);
|
|
909
1020
|
overlay = inject(WR_OVERLAY);
|
|
910
1021
|
outsideClick = inject(WrOutsideClick);
|
|
@@ -926,9 +1037,15 @@ class WrSelect {
|
|
|
926
1037
|
}
|
|
927
1038
|
else {
|
|
928
1039
|
this.closeOverlay();
|
|
929
|
-
// Reset the search query when the panel closes so a re-open
|
|
930
|
-
//
|
|
931
|
-
|
|
1040
|
+
// Reset the search query when the panel closes so a re-open doesn't
|
|
1041
|
+
// carry a stale filter — but NOT while the field still has focus.
|
|
1042
|
+
//
|
|
1043
|
+
// With `[minChars]`, backspacing under the threshold closes the panel
|
|
1044
|
+
// (`onSearchInput`), and `searchDisplay` falls back to the selected
|
|
1045
|
+
// label the moment `open()` is false — so an unconditional reset here
|
|
1046
|
+
// erased what the user was still typing, mid-word. A query the user is
|
|
1047
|
+
// actively editing is not stale.
|
|
1048
|
+
if (this.isSearchable() && !this.searchHasFocus())
|
|
932
1049
|
this.searchQuery.set('');
|
|
933
1050
|
}
|
|
934
1051
|
});
|
|
@@ -958,13 +1075,30 @@ class WrSelect {
|
|
|
958
1075
|
return of([]);
|
|
959
1076
|
}
|
|
960
1077
|
this.loaderLoading.set(true);
|
|
961
|
-
|
|
962
|
-
|
|
1078
|
+
// `defer` so the loader CALL is part of the inner observable too. Called
|
|
1079
|
+
// out here, a loader that throws SYNCHRONOUSLY throws from switchMap's
|
|
1080
|
+
// project function, which rxjs routes straight to the destination — past
|
|
1081
|
+
// the `catchError` below and past `finalize`, so the flag raised on the
|
|
1082
|
+
// line above would stay up for good.
|
|
1083
|
+
const source = defer(() => {
|
|
1084
|
+
const result = loader(query);
|
|
1085
|
+
return isObservable(result) ? result : from(Promise.resolve(result));
|
|
1086
|
+
});
|
|
1087
|
+
return source.pipe(
|
|
963
1088
|
// A loader can hand back a long-lived observable (a store selector,
|
|
964
1089
|
// say) that emits and never completes — so the first value lowers the
|
|
965
1090
|
// flag. `finalize` still covers completion, errors and cancellation,
|
|
966
1091
|
// including a loader that completes without emitting.
|
|
967
|
-
|
|
1092
|
+
tap(() => this.loaderLoading.set(false)),
|
|
1093
|
+
// INSIDE the projection, which is the whole point: an error allowed to
|
|
1094
|
+
// reach the outer subscriber closes the subscription for good, and the
|
|
1095
|
+
// select then never calls the loader again for the life of the
|
|
1096
|
+
// component — silently, since nothing here has an error callback. Kept
|
|
1097
|
+
// inside, one failed request costs that query and nothing else.
|
|
1098
|
+
// Reported as "no results" rather than as an error row: the panel has
|
|
1099
|
+
// no error state, and stale results for a query two keystrokes old
|
|
1100
|
+
// would read as an answer to the current one.
|
|
1101
|
+
catchError(() => of([])), finalize(() => this.loaderLoading.set(false)));
|
|
968
1102
|
}), takeUntilDestroyed(this.destroyRef))
|
|
969
1103
|
.subscribe(items => this.loadedOptions.set(items));
|
|
970
1104
|
// Resolve the single-mode trigger label whenever the value or
|
|
@@ -1071,6 +1205,11 @@ class WrSelect {
|
|
|
1071
1205
|
}
|
|
1072
1206
|
this.value.set(value);
|
|
1073
1207
|
this.touch.emit();
|
|
1208
|
+
// A committed choice replaces the query, focus or no focus: the field goes
|
|
1209
|
+
// back to showing the selected label. The close-effect below deliberately
|
|
1210
|
+
// will not do it while the field is focused, which is right for a panel
|
|
1211
|
+
// that closed under `minChars` and wrong for one that closed on a pick.
|
|
1212
|
+
this.searchQuery.set('');
|
|
1074
1213
|
this.open.set(false);
|
|
1075
1214
|
}
|
|
1076
1215
|
registerOption(reg) {
|
|
@@ -1078,13 +1217,25 @@ class WrSelect {
|
|
|
1078
1217
|
return () => this.registry.update(list => list.filter(o => o.id !== reg.id));
|
|
1079
1218
|
}
|
|
1080
1219
|
// Chip handlers (multi + tag)
|
|
1081
|
-
/**
|
|
1082
|
-
|
|
1220
|
+
/**
|
|
1221
|
+
* Remove one selection (chip × button) BY POSITION. Works for multi and tag
|
|
1222
|
+
* modes.
|
|
1223
|
+
*
|
|
1224
|
+
* Positional, not by value: under `allowDuplicates` two chips carry the same
|
|
1225
|
+
* string, and a filter on equality deleted every copy at once — one click on
|
|
1226
|
+
* the second "red" took both reds with it. `visibleChips()` slices the HEAD
|
|
1227
|
+
* of `selectedChips()`, which maps the value array one for one, so the
|
|
1228
|
+
* `$index` a chip was rendered with indexes the value array directly. The
|
|
1229
|
+
* multi-mode toggle in `selectOption` was already positional.
|
|
1230
|
+
*/
|
|
1231
|
+
removeChip(index, event) {
|
|
1083
1232
|
event.stopPropagation();
|
|
1084
1233
|
if (this.isDisabled() || !this.hasChips())
|
|
1085
1234
|
return;
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1235
|
+
const current = this.asArray(this.value());
|
|
1236
|
+
if (index < 0 || index >= current.length)
|
|
1237
|
+
return;
|
|
1238
|
+
this.value.set([...current.slice(0, index), ...current.slice(index + 1)]);
|
|
1088
1239
|
this.touch.emit();
|
|
1089
1240
|
}
|
|
1090
1241
|
/** Clear every selection (× button). Multi/tag → empty array; search → null. */
|
|
@@ -1131,7 +1282,7 @@ class WrSelect {
|
|
|
1131
1282
|
const chips = this.selectedChips();
|
|
1132
1283
|
if (chips.length > 0) {
|
|
1133
1284
|
event.preventDefault();
|
|
1134
|
-
this.removeChip(chips
|
|
1285
|
+
this.removeChip(chips.length - 1, event);
|
|
1135
1286
|
}
|
|
1136
1287
|
}
|
|
1137
1288
|
}
|
|
@@ -1145,7 +1296,14 @@ class WrSelect {
|
|
|
1145
1296
|
if (seps.length === 0)
|
|
1146
1297
|
return;
|
|
1147
1298
|
event.preventDefault();
|
|
1148
|
-
|
|
1299
|
+
// `-` has to be escaped too, and it is the one that bites: unescaped it
|
|
1300
|
+
// OPENS A RANGE inside the class. `[',', '-']` compiled to `/[,-\n]+/` —
|
|
1301
|
+
// "Range out of order", a SyntaxError thrown after `preventDefault()`, so
|
|
1302
|
+
// the paste was dropped whole; `[',', '-', ';']` compiled without
|
|
1303
|
+
// complaint to a `,`–`;` range that swallowed every digit, shredding
|
|
1304
|
+
// `2024-01-02` into nothing. A lone `-` only worked because it landed
|
|
1305
|
+
// first in the class, where it is literal — position-dependent luck.
|
|
1306
|
+
const escaped = seps.map(s => s.replace(/[-.*+?^${}()|[\]\\]/g, '\\$&')).join('');
|
|
1149
1307
|
const splitRegex = new RegExp(`[${escaped}\\n]+`);
|
|
1150
1308
|
for (const part of text.split(splitRegex))
|
|
1151
1309
|
this.tryAddTag(part);
|
|
@@ -1207,14 +1365,27 @@ class WrSelect {
|
|
|
1207
1365
|
(!this.hasChipSearch() || this.searchQuery() === '')) {
|
|
1208
1366
|
event.preventDefault();
|
|
1209
1367
|
const chips = this.selectedChips();
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
this.removeChip(last.value, event);
|
|
1368
|
+
if (chips.length > 0)
|
|
1369
|
+
this.removeChip(chips.length - 1, event);
|
|
1213
1370
|
}
|
|
1214
1371
|
return;
|
|
1215
1372
|
}
|
|
1216
1373
|
// Open — navigation/selection.
|
|
1217
1374
|
switch (event.key) {
|
|
1375
|
+
case 'Tab':
|
|
1376
|
+
// Let focus leave naturally — no `preventDefault` — but do not let the panel
|
|
1377
|
+
// outlive it. Nothing closed on Tab, so the listbox stayed on screen with a
|
|
1378
|
+
// live `aria-activedescendant` while the combobox no longer had focus: it can
|
|
1379
|
+
// cover the control that just took it (WCAG 2.4.11), and it keeps tracking a
|
|
1380
|
+
// trigger nobody is on, so a click aimed at what is underneath lands on an
|
|
1381
|
+
// option and commits into a select the user had already left. `wr-dropdown`
|
|
1382
|
+
// and `wr-context-menu` close on Tab for the same reason.
|
|
1383
|
+
//
|
|
1384
|
+
// Closing does NOT commit the active option. The cursor is SEEDED on open, so
|
|
1385
|
+
// tabbing straight through would select a row the user never looked at — and
|
|
1386
|
+
// in multi / search / freeText modes there is no single "the option" to take.
|
|
1387
|
+
this.open.set(false);
|
|
1388
|
+
break;
|
|
1218
1389
|
case 'ArrowDown':
|
|
1219
1390
|
event.preventDefault();
|
|
1220
1391
|
this.moveActive(1);
|
|
@@ -1305,15 +1476,28 @@ class WrSelect {
|
|
|
1305
1476
|
}
|
|
1306
1477
|
}
|
|
1307
1478
|
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Both scan `orderedRegistry`, and that is not interchangeable with
|
|
1481
|
+
* `registry`.
|
|
1482
|
+
*
|
|
1483
|
+
* They RETURN an index, and every reader of `activeIndex` — `activeOptionId`,
|
|
1484
|
+
* `moveActive`, `seedActiveIndex`, the Enter branch of `onTriggerKey` —
|
|
1485
|
+
* resolves it against DOM order. Scanning creation order here produced an
|
|
1486
|
+
* index into the other list: with projected `<wr-option>` children next to
|
|
1487
|
+
* `[options]` rows the two differ, so opening or searching left the cursor on
|
|
1488
|
+
* a row the user was not looking at, and Enter committed whatever happened to
|
|
1489
|
+
* sit at that position in DOM order — a filter-hidden option, which commits
|
|
1490
|
+
* nothing at all.
|
|
1491
|
+
*/
|
|
1308
1492
|
firstEnabled() {
|
|
1309
1493
|
if (this.virtualActive())
|
|
1310
1494
|
return this.filteredDynamicOptions().length > 0 ? 0 : -1;
|
|
1311
|
-
return this.
|
|
1495
|
+
return this.orderedRegistry().findIndex(o => !o.disabled && !this.isOptionHidden(o.getLabel()));
|
|
1312
1496
|
}
|
|
1313
1497
|
lastEnabled() {
|
|
1314
1498
|
if (this.virtualActive())
|
|
1315
1499
|
return this.filteredDynamicOptions().length - 1;
|
|
1316
|
-
const list = this.
|
|
1500
|
+
const list = this.orderedRegistry();
|
|
1317
1501
|
let found = -1;
|
|
1318
1502
|
list.forEach((o, i) => {
|
|
1319
1503
|
if (!o.disabled && !this.isOptionHidden(o.getLabel()))
|
|
@@ -1397,16 +1581,16 @@ class WrSelect {
|
|
|
1397
1581
|
this.overlayRef.dispose();
|
|
1398
1582
|
this.overlayRef = null;
|
|
1399
1583
|
}
|
|
1400
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
1401
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
1584
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1585
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: WrSelect, isStandalone: true, selector: "wr-select", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, clearLabel: { classPropertyName: "clearLabel", publicName: "clearLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, searchQuery: { classPropertyName: "searchQuery", publicName: "searchQuery", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, displayWith: { classPropertyName: "displayWith", publicName: "displayWith", isSignal: true, isRequired: false, transformFunction: null }, loader: { classPropertyName: "loader", publicName: "loader", isSignal: true, isRequired: false, transformFunction: null }, serverSearch: { classPropertyName: "serverSearch", publicName: "serverSearch", isSignal: true, isRequired: false, transformFunction: null }, debounceMs: { classPropertyName: "debounceMs", publicName: "debounceMs", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null }, freeText: { classPropertyName: "freeText", publicName: "freeText", isSignal: true, isRequired: false, transformFunction: null }, virtualScroll: { classPropertyName: "virtualScroll", publicName: "virtualScroll", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, viewportHeight: { classPropertyName: "viewportHeight", publicName: "viewportHeight", isSignal: true, isRequired: false, transformFunction: null }, overscan: { classPropertyName: "overscan", publicName: "overscan", isSignal: true, isRequired: false, transformFunction: null }, noResultsText: { classPropertyName: "noResultsText", publicName: "noResultsText", isSignal: true, isRequired: false, transformFunction: null }, loadingText: { classPropertyName: "loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, separators: { classPropertyName: "separators", publicName: "separators", isSignal: true, isRequired: false, transformFunction: null }, allowDuplicates: { classPropertyName: "allowDuplicates", publicName: "allowDuplicates", isSignal: true, isRequired: false, transformFunction: null }, validate: { classPropertyName: "validate", publicName: "validate", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, maxItems: { classPropertyName: "maxItems", publicName: "maxItems", isSignal: true, isRequired: false, transformFunction: null }, maxTagCount: { classPropertyName: "maxTagCount", publicName: "maxTagCount", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { searchQuery: "searchQueryChange", searchChange: "searchChange", value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, providers: [
|
|
1402
1586
|
{
|
|
1403
1587
|
provide: WR_SELECT,
|
|
1404
1588
|
// eslint-disable-next-line @angular-eslint/no-forward-ref
|
|
1405
1589
|
useExisting: forwardRef(() => WrSelect),
|
|
1406
1590
|
},
|
|
1407
|
-
], queries: [{ propertyName: "projectedOptions", predicate: WrOption, isSignal: true }], viewQueries: [{ propertyName: "panelTpl", first: true, predicate: ["panelTpl"], descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "tagInputEl", first: true, predicate: ["tagInput"], descendants: true, isSignal: true }, { propertyName: "searchInputEl", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (isSearchTrigger()) {\n <!-- Search mode (or a searchable single): a real text input drives a filtered\n overlay panel. The wrapper isn't itself focusable \u2014 the input is the\n tabstop. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div class=\"wr-select__trigger wr-select__trigger--search\" (click)=\"focusSearchInput()\">\n <input\n #searchInput\n type=\"text\"\n role=\"combobox\"\n class=\"wr-select__search-input\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"open() ? activeOptionId() || null : null\"\n [placeholder]=\"resolvedPlaceholder()\"\n [value]=\"searchDisplay()\"\n (input)=\"onSearchInput($event)\"\n (focus)=\"onSearchFocus()\"\n (keydown)=\"onSearchKey($event)\"\n (blur)=\"touch.emit()\"\n />\n @if (clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n (keydown.enter)=\"clearAll($event)\"\n (keydown.space)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n <svg\n class=\"wr-icon__svg wr-select__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </div>\n} @else if (isTag() || hasChipSearch()) {\n <!-- Chip trigger \u2014 tag mode, or a searchable multi. A focusable input lives\n inside, so the wrapper itself only exists to host chips and forward\n whitespace clicks to the input. Tag mode needs no role (users tab\n straight into the input); the searchable multi is a real combobox, so it\n carries the listbox wiring. The click handler is a mouse-only\n convenience. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"wr-select__trigger wr-select__trigger--tag\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n (click)=\"focusTagInput()\"\n >\n <span class=\"wr-select__chips\">\n @for (chip of visibleChips(); track $index) {\n <span class=\"wr-select__chip\">\n <span class=\"wr-select__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-select__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip(chip.value, $event)\"\n (keydown.enter)=\"removeChip(chip.value, $event)\"\n (keydown.space)=\"removeChip(chip.value, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-select__chip wr-select__chip--more\">+{{ extra }} more</span>\n }\n <input\n #tagInput\n type=\"text\"\n class=\"wr-select__tag-input\"\n autocomplete=\"off\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n spellcheck=\"false\"\n [value]=\"hasChipSearch() ? searchQuery() : draft()\"\n [disabled]=\"isDisabled() || (isTag() && atCapacity())\"\n [placeholder]=\"hasSelection() ? '' : resolvedPlaceholder()\"\n [attr.role]=\"hasChipSearch() ? 'combobox' : null\"\n [attr.aria-haspopup]=\"hasChipSearch() ? 'listbox' : null\"\n [attr.aria-expanded]=\"hasChipSearch() ? open() : null\"\n [attr.aria-controls]=\"hasChipSearch() ? listboxId : null\"\n [attr.aria-activedescendant]=\"hasChipSearch() && open() ? activeOptionId() || null : null\"\n (input)=\"onChipsInput($event)\"\n (keydown)=\"onChipsKeydown($event)\"\n (paste)=\"onChipsPaste($event)\"\n (focus)=\"onChipsFocus()\"\n (blur)=\"onChipsBlur()\"\n />\n </span>\n\n @if (clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n (keydown.enter)=\"clearAll($event)\"\n (keydown.space)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n </div>\n} @else {\n <button\n type=\"button\"\n class=\"wr-select__trigger\"\n role=\"combobox\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"open() ? activeOptionId() || null : null\"\n (click)=\"onTriggerClick()\"\n (keydown)=\"onTriggerKey($event)\"\n (blur)=\"touch.emit()\"\n >\n @if (isMulti()) {\n @if (hasSelection()) {\n <span class=\"wr-select__chips\">\n @for (chip of visibleChips(); track $index) {\n <span class=\"wr-select__chip\">\n <span class=\"wr-select__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-select__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip(chip.value, $event)\"\n (keydown.enter)=\"removeChip(chip.value, $event)\"\n (keydown.space)=\"removeChip(chip.value, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-select__chip wr-select__chip--more\">+{{ extra }} more</span>\n }\n </span>\n } @else {\n <span class=\"wr-select__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n } @else {\n @if (selectedLabel(); as label) {\n <span class=\"wr-select__value\">{{ label }}</span>\n } @else {\n <span class=\"wr-select__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n }\n\n @if (isMulti() && clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n (keydown.enter)=\"clearAll($event)\"\n (keydown.space)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n\n <svg\n class=\"wr-icon__svg wr-select__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n}\n\n<ng-template #panelTpl>\n <div\n class=\"wr-select-panel\"\n [class.wr-select-panel--sm]=\"resolvedSize() === 'sm'\"\n [class.wr-select-panel--lg]=\"resolvedSize() === 'lg'\"\n [class.wr-select-panel--virtual]=\"virtualActive()\"\n role=\"listbox\"\n [attr.id]=\"listboxId\"\n [attr.aria-multiselectable]=\"isMulti() ? 'true' : null\"\n >\n @if (isLoading()) {\n <div class=\"wr-select-panel__loading\" aria-busy=\"true\">{{ resolvedLoading() }}</div>\n }\n\n @if (virtualActive()) {\n <!-- Windowed data-array rows: plain role=\"option\" elements (not <wr-option>),\n navigated via the input's aria-activedescendant, selected by value. -->\n <div class=\"wr-select-panel__vlist\" [style.max-height]=\"resolvedViewportHeight()\">\n @if (virtualWindow().padTop > 0) {\n <div class=\"wr-select-panel__vspacer\" aria-hidden=\"true\" [style.height.px]=\"virtualWindow().padTop\"></div>\n }\n @for (entry of windowOptions(); track entry.index) {\n <!-- Keyboard handled by the search input via aria-activedescendant; the\n row is a pointer target only. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n role=\"option\"\n class=\"wr-option\"\n [id]=\"virtualRowId(entry.index)\"\n [class.wr-option--active]=\"entry.index === activeIndex()\"\n [class.wr-option--selected]=\"isSelected(entry.item)\"\n [attr.aria-selected]=\"isSelected(entry.item)\"\n [attr.aria-setsize]=\"filteredDynamicOptions().length\"\n [attr.aria-posinset]=\"entry.index + 1\"\n (click)=\"selectOption(entry.item)\"\n >\n {{ displayWith()(entry.item) }}\n </div>\n }\n @if (virtualWindow().padBottom > 0) {\n <div class=\"wr-select-panel__vspacer\" aria-hidden=\"true\" [style.height.px]=\"virtualWindow().padBottom\"></div>\n }\n </div>\n } @else {\n <!-- Dynamic options from [options] or async [loader] (search mode).\n Rendered as wr-option so the standard registration / filter\n pipeline applies uniformly. -->\n @for (item of dynamicOptions(); track $index) {\n <wr-option [value]=\"item\">{{ displayWith()(item) }}</wr-option>\n }\n }\n\n <!-- Projected static options \u2014 always present (a conditional `<ng-content>`\n would drop projected content). Empty whenever `virtualActive`. -->\n <ng-content />\n\n @if (hasNoResults()) {\n <div class=\"wr-select-panel__empty\">{{ resolvedNoResults() }}</div>\n }\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: WrOption, selector: "wr-option", inputs: ["value", "disabled"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
1591
|
+
], queries: [{ propertyName: "projectedOptions", predicate: WrOption, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "panelTpl", first: true, predicate: ["panelTpl"], descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "tagInputEl", first: true, predicate: ["tagInput"], descendants: true, isSignal: true }, { propertyName: "searchInputEl", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (isSearchTrigger()) {\n <!-- Search mode (or a searchable single): a real text input drives a filtered\n overlay panel. The wrapper isn't itself focusable \u2014 the input is the\n tabstop. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div class=\"wr-select__trigger wr-select__trigger--search\" (click)=\"focusSearchInput()\">\n <!-- `id` / `aria-describedby` / `aria-invalid` come from a surrounding\n `<wr-form-field>` and are null without one \u2014 see `controlId` in the\n component. They go on the tab stop, the only labelable element here. -->\n <input\n #searchInput\n type=\"text\"\n role=\"combobox\"\n class=\"wr-select__search-input\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [attr.id]=\"controlId()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"ariaInvalid()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"open() ? activeOptionId() || null : null\"\n [placeholder]=\"resolvedPlaceholder()\"\n [value]=\"searchDisplay()\"\n (input)=\"onSearchInput($event)\"\n (focus)=\"onSearchFocus()\"\n (keydown)=\"onSearchKey($event)\"\n (blur)=\"onSearchBlur()\"\n />\n <!-- Pointer-only, and the `keydown.enter` / `keydown.space` pair this used to\n carry was dead code rather than the keyboard path it looked like: the \u00D7\n is `tabindex=\"-1\"` with no roving owner, so no key event ever originates\n here to bubble into a handler. It stays unfocusable because the same\n span sits inside the BUTTON trigger further down, where a focusable\n child is `nested-interactive` \u2014 the defect the window taskbar's close\n button already taught this repo. The keyboard reaches the function\n instead: Backspace on the empty field, which `onSearchKey` routes to\n `clearAll` for a single and to the last chip for a multi. -->\n @if (clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n <svg\n class=\"wr-icon__svg wr-select__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </div>\n} @else if (isTag() || hasChipSearch()) {\n <!-- Chip trigger \u2014 tag mode, or a searchable multi. A focusable input lives\n inside, so the wrapper itself only exists to host chips and forward\n whitespace clicks to the input. Tag mode needs no role (users tab\n straight into the input); the searchable multi is a real combobox, so it\n carries the listbox wiring. The click handler is a mouse-only\n convenience. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"wr-select__trigger wr-select__trigger--tag\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n (click)=\"focusTagInput()\"\n >\n <span class=\"wr-select__chips\">\n @for (chip of visibleChips(); track $index) {\n <span class=\"wr-select__chip\">\n <span class=\"wr-select__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-select__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip($index, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-select__chip wr-select__chip--more\">{{ moreLabel({ count: extra }) }}</span>\n }\n <!-- At `maxItems` this input goes READONLY, never disabled: it is the only\n tab stop in the whole control (the chip \u00D7 and the clear button are\n `tabindex=\"-1\"` spans), so disabling it left a full tag select with\n nothing focusable at all \u2014 and Backspace on an empty draft, the one\n keyboard way back UNDER the cap, is bound to this very element. It was\n never needed as enforcement either: `tryAddTag` already refuses past\n capacity. -->\n <input\n #tagInput\n type=\"text\"\n class=\"wr-select__tag-input\"\n autocomplete=\"off\"\n [attr.id]=\"controlId()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"ariaInvalid()\"\n spellcheck=\"false\"\n [value]=\"hasChipSearch() ? searchQuery() : draft()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"isTag() && atCapacity()\"\n [placeholder]=\"hasSelection() ? '' : resolvedPlaceholder()\"\n [attr.role]=\"hasChipSearch() ? 'combobox' : null\"\n [attr.aria-haspopup]=\"hasChipSearch() ? 'listbox' : null\"\n [attr.aria-expanded]=\"hasChipSearch() ? open() : null\"\n [attr.aria-controls]=\"hasChipSearch() ? listboxId : null\"\n [attr.aria-activedescendant]=\"hasChipSearch() && open() ? activeOptionId() || null : null\"\n (input)=\"onChipsInput($event)\"\n (keydown)=\"onChipsKeydown($event)\"\n (paste)=\"onChipsPaste($event)\"\n (focus)=\"onChipsFocus()\"\n (blur)=\"onChipsBlur()\"\n />\n </span>\n\n @if (clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n </div>\n} @else {\n <button\n type=\"button\"\n class=\"wr-select__trigger\"\n role=\"combobox\"\n [attr.id]=\"controlId()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"ariaInvalid()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"open() ? activeOptionId() || null : null\"\n (click)=\"onTriggerClick()\"\n (keydown)=\"onTriggerKey($event)\"\n (blur)=\"touch.emit()\"\n >\n @if (isMulti()) {\n @if (hasSelection()) {\n <span class=\"wr-select__chips\">\n @for (chip of visibleChips(); track $index) {\n <span class=\"wr-select__chip\">\n <span class=\"wr-select__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-select__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip($index, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-select__chip wr-select__chip--more\">{{ moreLabel({ count: extra }) }}</span>\n }\n </span>\n } @else {\n <span class=\"wr-select__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n } @else {\n @if (selectedLabel(); as label) {\n <span class=\"wr-select__value\">{{ label }}</span>\n } @else {\n <span class=\"wr-select__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n }\n\n @if (isMulti() && clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n\n <svg\n class=\"wr-icon__svg wr-select__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n}\n\n<ng-template #panelTpl>\n <div\n class=\"wr-select-panel\"\n [class.wr-select-panel--sm]=\"resolvedSize() === 'sm'\"\n [class.wr-select-panel--lg]=\"resolvedSize() === 'lg'\"\n [class.wr-select-panel--virtual]=\"virtualActive()\"\n role=\"listbox\"\n [attr.id]=\"listboxId\"\n [attr.aria-multiselectable]=\"isMulti() ? 'true' : null\"\n [attr.aria-busy]=\"isLoading() ? 'true' : null\"\n >\n <!-- `aria-busy` belongs on the listbox, not on this row: it is the container\n whose required `option` children are temporarily missing, and a global\n ARIA attribute on a role-less child makes that child an owned element the\n listbox may not own (axe `aria-required-children`, critical). Plain text\n inside the listbox is fine \u2014 only ELEMENTS are checked. -->\n @if (isLoading()) {\n <div class=\"wr-select-panel__loading\">{{ resolvedLoading() }}</div>\n }\n\n @if (virtualActive()) {\n <!-- Windowed data-array rows: plain role=\"option\" elements (not <wr-option>),\n navigated via the input's aria-activedescendant, selected by value. -->\n <div class=\"wr-select-panel__vlist\" [style.max-height]=\"resolvedViewportHeight()\">\n @if (virtualWindow().padTop > 0) {\n <div class=\"wr-select-panel__vspacer\" aria-hidden=\"true\" [style.height.px]=\"virtualWindow().padTop\"></div>\n }\n @for (entry of windowOptions(); track entry.index) {\n <!-- Keyboard handled by the search input via aria-activedescendant; the\n row is a pointer target only. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n role=\"option\"\n class=\"wr-option\"\n [id]=\"virtualRowId(entry.index)\"\n [class.wr-option--active]=\"entry.index === activeIndex()\"\n [class.wr-option--selected]=\"isSelected(entry.item)\"\n [attr.aria-selected]=\"isSelected(entry.item)\"\n [attr.aria-setsize]=\"filteredDynamicOptions().length\"\n [attr.aria-posinset]=\"entry.index + 1\"\n (click)=\"selectOption(entry.item)\"\n >\n {{ displayWith()(entry.item) }}\n </div>\n }\n @if (virtualWindow().padBottom > 0) {\n <div class=\"wr-select-panel__vspacer\" aria-hidden=\"true\" [style.height.px]=\"virtualWindow().padBottom\"></div>\n }\n </div>\n } @else {\n <!-- Dynamic options from [options] or async [loader] (search mode).\n Rendered as wr-option so the standard registration / filter\n pipeline applies uniformly. -->\n @for (item of dynamicOptions(); track $index) {\n <wr-option [value]=\"item\">{{ displayWith()(item) }}</wr-option>\n }\n }\n\n <!-- Projected static options \u2014 always present (a conditional `<ng-content>`\n would drop projected content). Empty whenever `virtualActive`. -->\n <ng-content />\n\n <!-- A listbox may only own `option` and `group` children, and as a bare `<div>`\n this row was the only thing in the tree when it showed \u2014 an\n `aria-required-children` critical. It cannot move out of the listbox the\n way the command palette's does: this element IS the listbox, and the id\n the trigger's `aria-controls` names is what gives it its accessible name.\n So it announces as a disabled option instead. Keyboard navigation skips\n it for free \u2014 arrow keys walk the registered `wr-option`s, and nothing\n registers this row. -->\n @if (hasNoResults()) {\n <div class=\"wr-select-panel__empty\" role=\"option\" aria-selected=\"false\" aria-disabled=\"true\">\n {{ resolvedNoResults() }}\n </div>\n }\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: WrOption, selector: "wr-option", inputs: ["value", "disabled"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
1408
1592
|
}
|
|
1409
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1593
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrSelect, decorators: [{
|
|
1410
1594
|
type: Component,
|
|
1411
1595
|
args: [{ selector: 'wr-select', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [WrOption], providers: [
|
|
1412
1596
|
{
|
|
@@ -1414,8 +1598,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
|
|
|
1414
1598
|
// eslint-disable-next-line @angular-eslint/no-forward-ref
|
|
1415
1599
|
useExisting: forwardRef(() => WrSelect),
|
|
1416
1600
|
},
|
|
1417
|
-
], template: "@if (isSearchTrigger()) {\n <!-- Search mode (or a searchable single): a real text input drives a filtered\n overlay panel. The wrapper isn't itself focusable \u2014 the input is the\n tabstop. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div class=\"wr-select__trigger wr-select__trigger--search\" (click)=\"focusSearchInput()\">\n <input\n #searchInput\n type=\"text\"\n role=\"combobox\"\n class=\"wr-select__search-input\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"open() ? activeOptionId() || null : null\"\n [placeholder]=\"resolvedPlaceholder()\"\n [value]=\"searchDisplay()\"\n (input)=\"onSearchInput($event)\"\n (focus)=\"onSearchFocus()\"\n (keydown)=\"onSearchKey($event)\"\n (blur)=\"touch.emit()\"\n />\n @if (clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n (keydown.enter)=\"clearAll($event)\"\n (keydown.space)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n <svg\n class=\"wr-icon__svg wr-select__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </div>\n} @else if (isTag() || hasChipSearch()) {\n <!-- Chip trigger \u2014 tag mode, or a searchable multi. A focusable input lives\n inside, so the wrapper itself only exists to host chips and forward\n whitespace clicks to the input. Tag mode needs no role (users tab\n straight into the input); the searchable multi is a real combobox, so it\n carries the listbox wiring. The click handler is a mouse-only\n convenience. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"wr-select__trigger wr-select__trigger--tag\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n (click)=\"focusTagInput()\"\n >\n <span class=\"wr-select__chips\">\n @for (chip of visibleChips(); track $index) {\n <span class=\"wr-select__chip\">\n <span class=\"wr-select__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-select__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip(chip.value, $event)\"\n (keydown.enter)=\"removeChip(chip.value, $event)\"\n (keydown.space)=\"removeChip(chip.value, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-select__chip wr-select__chip--more\">+{{ extra }} more</span>\n }\n <input\n #tagInput\n type=\"text\"\n class=\"wr-select__tag-input\"\n autocomplete=\"off\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n spellcheck=\"false\"\n [value]=\"hasChipSearch() ? searchQuery() : draft()\"\n [disabled]=\"isDisabled() || (isTag() && atCapacity())\"\n [placeholder]=\"hasSelection() ? '' : resolvedPlaceholder()\"\n [attr.role]=\"hasChipSearch() ? 'combobox' : null\"\n [attr.aria-haspopup]=\"hasChipSearch() ? 'listbox' : null\"\n [attr.aria-expanded]=\"hasChipSearch() ? open() : null\"\n [attr.aria-controls]=\"hasChipSearch() ? listboxId : null\"\n [attr.aria-activedescendant]=\"hasChipSearch() && open() ? activeOptionId() || null : null\"\n (input)=\"onChipsInput($event)\"\n (keydown)=\"onChipsKeydown($event)\"\n (paste)=\"onChipsPaste($event)\"\n (focus)=\"onChipsFocus()\"\n (blur)=\"onChipsBlur()\"\n />\n </span>\n\n @if (clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n (keydown.enter)=\"clearAll($event)\"\n (keydown.space)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n </div>\n} @else {\n <button\n type=\"button\"\n class=\"wr-select__trigger\"\n role=\"combobox\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"open() ? activeOptionId() || null : null\"\n (click)=\"onTriggerClick()\"\n (keydown)=\"onTriggerKey($event)\"\n (blur)=\"touch.emit()\"\n >\n @if (isMulti()) {\n @if (hasSelection()) {\n <span class=\"wr-select__chips\">\n @for (chip of visibleChips(); track $index) {\n <span class=\"wr-select__chip\">\n <span class=\"wr-select__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-select__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip(chip.value, $event)\"\n (keydown.enter)=\"removeChip(chip.value, $event)\"\n (keydown.space)=\"removeChip(chip.value, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-select__chip wr-select__chip--more\">+{{ extra }} more</span>\n }\n </span>\n } @else {\n <span class=\"wr-select__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n } @else {\n @if (selectedLabel(); as label) {\n <span class=\"wr-select__value\">{{ label }}</span>\n } @else {\n <span class=\"wr-select__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n }\n\n @if (isMulti() && clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n (keydown.enter)=\"clearAll($event)\"\n (keydown.space)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n\n <svg\n class=\"wr-icon__svg wr-select__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n}\n\n<ng-template #panelTpl>\n <div\n class=\"wr-select-panel\"\n [class.wr-select-panel--sm]=\"resolvedSize() === 'sm'\"\n [class.wr-select-panel--lg]=\"resolvedSize() === 'lg'\"\n [class.wr-select-panel--virtual]=\"virtualActive()\"\n role=\"listbox\"\n [attr.id]=\"listboxId\"\n [attr.aria-multiselectable]=\"isMulti() ? 'true' : null\"\n >\n @if (isLoading()) {\n <div class=\"wr-select-panel__loading\" aria-busy=\"true\">{{ resolvedLoading() }}</div>\n }\n\n @if (virtualActive()) {\n <!-- Windowed data-array rows: plain role=\"option\" elements (not <wr-option>),\n navigated via the input's aria-activedescendant, selected by value. -->\n <div class=\"wr-select-panel__vlist\" [style.max-height]=\"resolvedViewportHeight()\">\n @if (virtualWindow().padTop > 0) {\n <div class=\"wr-select-panel__vspacer\" aria-hidden=\"true\" [style.height.px]=\"virtualWindow().padTop\"></div>\n }\n @for (entry of windowOptions(); track entry.index) {\n <!-- Keyboard handled by the search input via aria-activedescendant; the\n row is a pointer target only. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n role=\"option\"\n class=\"wr-option\"\n [id]=\"virtualRowId(entry.index)\"\n [class.wr-option--active]=\"entry.index === activeIndex()\"\n [class.wr-option--selected]=\"isSelected(entry.item)\"\n [attr.aria-selected]=\"isSelected(entry.item)\"\n [attr.aria-setsize]=\"filteredDynamicOptions().length\"\n [attr.aria-posinset]=\"entry.index + 1\"\n (click)=\"selectOption(entry.item)\"\n >\n {{ displayWith()(entry.item) }}\n </div>\n }\n @if (virtualWindow().padBottom > 0) {\n <div class=\"wr-select-panel__vspacer\" aria-hidden=\"true\" [style.height.px]=\"virtualWindow().padBottom\"></div>\n }\n </div>\n } @else {\n <!-- Dynamic options from [options] or async [loader] (search mode).\n Rendered as wr-option so the standard registration / filter\n pipeline applies uniformly. -->\n @for (item of dynamicOptions(); track $index) {\n <wr-option [value]=\"item\">{{ displayWith()(item) }}</wr-option>\n }\n }\n\n <!-- Projected static options \u2014 always present (a conditional `<ng-content>`\n would drop projected content). Empty whenever `virtualActive`. -->\n <ng-content />\n\n @if (hasNoResults()) {\n <div class=\"wr-select-panel__empty\">{{ resolvedNoResults() }}</div>\n }\n </div>\n</ng-template>\n" }]
|
|
1418
|
-
}], ctorParameters: () => [], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], searchQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchQuery", required: false }] }, { type: i0.Output, args: ["searchQueryChange"] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], displayWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayWith", required: false }] }], loader: [{ type: i0.Input, args: [{ isSignal: true, alias: "loader", required: false }] }], serverSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "serverSearch", required: false }] }], debounceMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounceMs", required: false }] }], minChars: [{ type: i0.Input, args: [{ isSignal: true, alias: "minChars", required: false }] }], freeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "freeText", required: false }] }], virtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScroll", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], viewportHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewportHeight", required: false }] }], overscan: [{ type: i0.Input, args: [{ isSignal: true, alias: "overscan", required: false }] }], noResultsText: [{ type: i0.Input, args: [{ isSignal: true, alias: "noResultsText", required: false }] }], loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], separators: [{ type: i0.Input, args: [{ isSignal: true, alias: "separators", required: false }] }], allowDuplicates: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowDuplicates", required: false }] }], validate: [{ type: i0.Input, args: [{ isSignal: true, alias: "validate", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], maxItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxItems", required: false }] }], maxTagCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxTagCount", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], projectedOptions: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => WrOption), { isSignal: true }] }], panelTpl: [{ type: i0.ViewChild, args: ['panelTpl', { ...{ read: TemplateRef }, isSignal: true }] }], tagInputEl: [{ type: i0.ViewChild, args: ['tagInput', { isSignal: true }] }], searchInputEl: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }] } });
|
|
1601
|
+
], template: "@if (isSearchTrigger()) {\n <!-- Search mode (or a searchable single): a real text input drives a filtered\n overlay panel. The wrapper isn't itself focusable \u2014 the input is the\n tabstop. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div class=\"wr-select__trigger wr-select__trigger--search\" (click)=\"focusSearchInput()\">\n <!-- `id` / `aria-describedby` / `aria-invalid` come from a surrounding\n `<wr-form-field>` and are null without one \u2014 see `controlId` in the\n component. They go on the tab stop, the only labelable element here. -->\n <input\n #searchInput\n type=\"text\"\n role=\"combobox\"\n class=\"wr-select__search-input\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [attr.id]=\"controlId()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"ariaInvalid()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"open() ? activeOptionId() || null : null\"\n [placeholder]=\"resolvedPlaceholder()\"\n [value]=\"searchDisplay()\"\n (input)=\"onSearchInput($event)\"\n (focus)=\"onSearchFocus()\"\n (keydown)=\"onSearchKey($event)\"\n (blur)=\"onSearchBlur()\"\n />\n <!-- Pointer-only, and the `keydown.enter` / `keydown.space` pair this used to\n carry was dead code rather than the keyboard path it looked like: the \u00D7\n is `tabindex=\"-1\"` with no roving owner, so no key event ever originates\n here to bubble into a handler. It stays unfocusable because the same\n span sits inside the BUTTON trigger further down, where a focusable\n child is `nested-interactive` \u2014 the defect the window taskbar's close\n button already taught this repo. The keyboard reaches the function\n instead: Backspace on the empty field, which `onSearchKey` routes to\n `clearAll` for a single and to the last chip for a multi. -->\n @if (clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n <svg\n class=\"wr-icon__svg wr-select__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </div>\n} @else if (isTag() || hasChipSearch()) {\n <!-- Chip trigger \u2014 tag mode, or a searchable multi. A focusable input lives\n inside, so the wrapper itself only exists to host chips and forward\n whitespace clicks to the input. Tag mode needs no role (users tab\n straight into the input); the searchable multi is a real combobox, so it\n carries the listbox wiring. The click handler is a mouse-only\n convenience. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"wr-select__trigger wr-select__trigger--tag\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n (click)=\"focusTagInput()\"\n >\n <span class=\"wr-select__chips\">\n @for (chip of visibleChips(); track $index) {\n <span class=\"wr-select__chip\">\n <span class=\"wr-select__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-select__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip($index, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-select__chip wr-select__chip--more\">{{ moreLabel({ count: extra }) }}</span>\n }\n <!-- At `maxItems` this input goes READONLY, never disabled: it is the only\n tab stop in the whole control (the chip \u00D7 and the clear button are\n `tabindex=\"-1\"` spans), so disabling it left a full tag select with\n nothing focusable at all \u2014 and Backspace on an empty draft, the one\n keyboard way back UNDER the cap, is bound to this very element. It was\n never needed as enforcement either: `tryAddTag` already refuses past\n capacity. -->\n <input\n #tagInput\n type=\"text\"\n class=\"wr-select__tag-input\"\n autocomplete=\"off\"\n [attr.id]=\"controlId()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"ariaInvalid()\"\n spellcheck=\"false\"\n [value]=\"hasChipSearch() ? searchQuery() : draft()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"isTag() && atCapacity()\"\n [placeholder]=\"hasSelection() ? '' : resolvedPlaceholder()\"\n [attr.role]=\"hasChipSearch() ? 'combobox' : null\"\n [attr.aria-haspopup]=\"hasChipSearch() ? 'listbox' : null\"\n [attr.aria-expanded]=\"hasChipSearch() ? open() : null\"\n [attr.aria-controls]=\"hasChipSearch() ? listboxId : null\"\n [attr.aria-activedescendant]=\"hasChipSearch() && open() ? activeOptionId() || null : null\"\n (input)=\"onChipsInput($event)\"\n (keydown)=\"onChipsKeydown($event)\"\n (paste)=\"onChipsPaste($event)\"\n (focus)=\"onChipsFocus()\"\n (blur)=\"onChipsBlur()\"\n />\n </span>\n\n @if (clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n </div>\n} @else {\n <button\n type=\"button\"\n class=\"wr-select__trigger\"\n role=\"combobox\"\n [attr.id]=\"controlId()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"ariaInvalid()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"open() ? activeOptionId() || null : null\"\n (click)=\"onTriggerClick()\"\n (keydown)=\"onTriggerKey($event)\"\n (blur)=\"touch.emit()\"\n >\n @if (isMulti()) {\n @if (hasSelection()) {\n <span class=\"wr-select__chips\">\n @for (chip of visibleChips(); track $index) {\n <span class=\"wr-select__chip\">\n <span class=\"wr-select__chip-label\">{{ chip.label }}</span>\n <span\n class=\"wr-select__chip-remove\"\n role=\"button\"\n [attr.aria-label]=\"chipRemoveLabel({ label: chip.label })\"\n tabindex=\"-1\"\n (click)=\"removeChip($index, $event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n </span>\n }\n @if (hiddenChipCount(); as extra) {\n <span class=\"wr-select__chip wr-select__chip--more\">{{ moreLabel({ count: extra }) }}</span>\n }\n </span>\n } @else {\n <span class=\"wr-select__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n } @else {\n @if (selectedLabel(); as label) {\n <span class=\"wr-select__value\">{{ label }}</span>\n } @else {\n <span class=\"wr-select__placeholder\">{{ resolvedPlaceholder() }}</span>\n }\n }\n\n @if (isMulti() && clearable() && hasSelection() && !isDisabled()) {\n <span\n class=\"wr-select__clear\"\n role=\"button\"\n [attr.aria-label]=\"resolvedClear()\"\n tabindex=\"-1\"\n (click)=\"clearAll($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </span>\n }\n\n <svg\n class=\"wr-icon__svg wr-select__chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n}\n\n<ng-template #panelTpl>\n <div\n class=\"wr-select-panel\"\n [class.wr-select-panel--sm]=\"resolvedSize() === 'sm'\"\n [class.wr-select-panel--lg]=\"resolvedSize() === 'lg'\"\n [class.wr-select-panel--virtual]=\"virtualActive()\"\n role=\"listbox\"\n [attr.id]=\"listboxId\"\n [attr.aria-multiselectable]=\"isMulti() ? 'true' : null\"\n [attr.aria-busy]=\"isLoading() ? 'true' : null\"\n >\n <!-- `aria-busy` belongs on the listbox, not on this row: it is the container\n whose required `option` children are temporarily missing, and a global\n ARIA attribute on a role-less child makes that child an owned element the\n listbox may not own (axe `aria-required-children`, critical). Plain text\n inside the listbox is fine \u2014 only ELEMENTS are checked. -->\n @if (isLoading()) {\n <div class=\"wr-select-panel__loading\">{{ resolvedLoading() }}</div>\n }\n\n @if (virtualActive()) {\n <!-- Windowed data-array rows: plain role=\"option\" elements (not <wr-option>),\n navigated via the input's aria-activedescendant, selected by value. -->\n <div class=\"wr-select-panel__vlist\" [style.max-height]=\"resolvedViewportHeight()\">\n @if (virtualWindow().padTop > 0) {\n <div class=\"wr-select-panel__vspacer\" aria-hidden=\"true\" [style.height.px]=\"virtualWindow().padTop\"></div>\n }\n @for (entry of windowOptions(); track entry.index) {\n <!-- Keyboard handled by the search input via aria-activedescendant; the\n row is a pointer target only. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <div\n role=\"option\"\n class=\"wr-option\"\n [id]=\"virtualRowId(entry.index)\"\n [class.wr-option--active]=\"entry.index === activeIndex()\"\n [class.wr-option--selected]=\"isSelected(entry.item)\"\n [attr.aria-selected]=\"isSelected(entry.item)\"\n [attr.aria-setsize]=\"filteredDynamicOptions().length\"\n [attr.aria-posinset]=\"entry.index + 1\"\n (click)=\"selectOption(entry.item)\"\n >\n {{ displayWith()(entry.item) }}\n </div>\n }\n @if (virtualWindow().padBottom > 0) {\n <div class=\"wr-select-panel__vspacer\" aria-hidden=\"true\" [style.height.px]=\"virtualWindow().padBottom\"></div>\n }\n </div>\n } @else {\n <!-- Dynamic options from [options] or async [loader] (search mode).\n Rendered as wr-option so the standard registration / filter\n pipeline applies uniformly. -->\n @for (item of dynamicOptions(); track $index) {\n <wr-option [value]=\"item\">{{ displayWith()(item) }}</wr-option>\n }\n }\n\n <!-- Projected static options \u2014 always present (a conditional `<ng-content>`\n would drop projected content). Empty whenever `virtualActive`. -->\n <ng-content />\n\n <!-- A listbox may only own `option` and `group` children, and as a bare `<div>`\n this row was the only thing in the tree when it showed \u2014 an\n `aria-required-children` critical. It cannot move out of the listbox the\n way the command palette's does: this element IS the listbox, and the id\n the trigger's `aria-controls` names is what gives it its accessible name.\n So it announces as a disabled option instead. Keyboard navigation skips\n it for free \u2014 arrow keys walk the registered `wr-option`s, and nothing\n registers this row. -->\n @if (hasNoResults()) {\n <div class=\"wr-select-panel__empty\" role=\"option\" aria-selected=\"false\" aria-disabled=\"true\">\n {{ resolvedNoResults() }}\n </div>\n }\n </div>\n</ng-template>\n" }]
|
|
1602
|
+
}], ctorParameters: () => [], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], searchQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchQuery", required: false }] }, { type: i0.Output, args: ["searchQueryChange"] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], displayWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayWith", required: false }] }], loader: [{ type: i0.Input, args: [{ isSignal: true, alias: "loader", required: false }] }], serverSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "serverSearch", required: false }] }], debounceMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounceMs", required: false }] }], minChars: [{ type: i0.Input, args: [{ isSignal: true, alias: "minChars", required: false }] }], freeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "freeText", required: false }] }], virtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScroll", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], viewportHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewportHeight", required: false }] }], overscan: [{ type: i0.Input, args: [{ isSignal: true, alias: "overscan", required: false }] }], noResultsText: [{ type: i0.Input, args: [{ isSignal: true, alias: "noResultsText", required: false }] }], loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], separators: [{ type: i0.Input, args: [{ isSignal: true, alias: "separators", required: false }] }], allowDuplicates: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowDuplicates", required: false }] }], validate: [{ type: i0.Input, args: [{ isSignal: true, alias: "validate", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], maxItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxItems", required: false }] }], maxTagCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxTagCount", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], projectedOptions: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => WrOption), { ...{ descendants: true }, isSignal: true }] }], panelTpl: [{ type: i0.ViewChild, args: ['panelTpl', { ...{ read: TemplateRef }, isSignal: true }] }], tagInputEl: [{ type: i0.ViewChild, args: ['tagInput', { isSignal: true }] }], searchInputEl: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }] } });
|
|
1419
1603
|
|
|
1420
1604
|
/**
|
|
1421
1605
|
* @license
|
|
@@ -1443,10 +1627,10 @@ class WrOptionGroup {
|
|
|
1443
1627
|
* be wired to it for the name to reach a screen reader.
|
|
1444
1628
|
*/
|
|
1445
1629
|
labelId = randomId('wr-option-group-label');
|
|
1446
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
1447
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
1630
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrOptionGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1631
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: WrOptionGroup, isStandalone: true, selector: "wr-option-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "group" }, properties: { "attr.aria-labelledby": "labelId" }, classAttribute: "wr-option-group" }, ngImport: i0, template: "<div class=\"wr-option-group__label\" [id]=\"labelId\">{{ label() }}</div>\n<ng-content />\n", encapsulation: i0.ViewEncapsulation.None });
|
|
1448
1632
|
}
|
|
1449
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1633
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrOptionGroup, decorators: [{
|
|
1450
1634
|
type: Component,
|
|
1451
1635
|
args: [{ selector: 'wr-option-group', encapsulation: ViewEncapsulation.None, host: { class: 'wr-option-group', role: 'group', '[attr.aria-labelledby]': 'labelId' }, template: "<div class=\"wr-option-group__label\" [id]=\"labelId\">{{ label() }}</div>\n<ng-content />\n" }]
|
|
1452
1636
|
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }] } });
|