ngwr 14.7.0 → 14.9.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 +66 -332
- package/alert/styles/_index.scss +10 -0
- package/avatar/styles/_index.scss +18 -7
- package/badge/styles/_index.scss +7 -0
- package/badge/styles/_tag.scss +7 -1
- package/bar-chart/styles/_index.scss +1 -0
- package/button/styles/_index.scss +6 -0
- package/calendar-heatmap/styles/_index.scss +1 -0
- package/cascader/styles/_rules.scss +87 -15
- package/checkbox/styles/_index.scss +4 -0
- package/command-palette/styles/_index.scss +1 -1
- package/date-picker/styles/_index.scss +30 -0
- package/density/styles/_index.scss +4 -2
- package/divider/styles/_index.scss +2 -0
- package/donut-chart/styles/_index.scss +10 -0
- package/dropdown/styles/_index.scss +67 -5
- package/editor/styles/_index.scss +441 -0
- package/event-calendar/styles/_index.scss +209 -35
- package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -1
- package/fesm2022/ngwr-action-sheet.mjs +3 -3
- package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
- package/fesm2022/ngwr-affix.mjs +3 -3
- package/fesm2022/ngwr-affix.mjs.map +1 -1
- package/fesm2022/ngwr-alert.mjs +3 -3
- package/fesm2022/ngwr-alert.mjs.map +1 -1
- package/fesm2022/ngwr-anchor.mjs +3 -3
- package/fesm2022/ngwr-anchor.mjs.map +1 -1
- package/fesm2022/ngwr-aurora.mjs +3 -3
- package/fesm2022/ngwr-aurora.mjs.map +1 -1
- package/fesm2022/ngwr-avatar.mjs +3 -3
- package/fesm2022/ngwr-avatar.mjs.map +1 -1
- package/fesm2022/ngwr-back-top.mjs +3 -3
- package/fesm2022/ngwr-back-top.mjs.map +1 -1
- package/fesm2022/ngwr-badge.mjs +28 -13
- package/fesm2022/ngwr-badge.mjs.map +1 -1
- package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-bar-chart.mjs +31 -6
- package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
- package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-blur-text.mjs +3 -3
- package/fesm2022/ngwr-blur-text.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow.mjs +3 -3
- package/fesm2022/ngwr-border-glow.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +6 -6
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-burger.mjs +3 -3
- package/fesm2022/ngwr-burger.mjs.map +1 -1
- package/fesm2022/ngwr-button-testing.mjs.map +1 -1
- package/fesm2022/ngwr-button.mjs +6 -6
- package/fesm2022/ngwr-button.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-heatmap.mjs +61 -6
- package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
- package/fesm2022/ngwr-calendar.mjs +3 -3
- package/fesm2022/ngwr-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +9 -9
- package/fesm2022/ngwr-card.mjs.map +1 -1
- package/fesm2022/ngwr-carousel-testing.mjs.map +1 -1
- package/fesm2022/ngwr-carousel.mjs +6 -6
- package/fesm2022/ngwr-carousel.mjs.map +1 -1
- package/fesm2022/ngwr-cascader-testing.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +4 -4
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox-testing.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox.mjs +18 -8
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text.mjs +3 -3
- package/fesm2022/ngwr-circular-text.mjs.map +1 -1
- package/fesm2022/ngwr-click-spark.mjs +3 -3
- package/fesm2022/ngwr-click-spark.mjs.map +1 -1
- package/fesm2022/ngwr-clipboard.mjs +3 -3
- package/fesm2022/ngwr-clipboard.mjs.map +1 -1
- package/fesm2022/ngwr-collapse-testing.mjs.map +1 -1
- package/fesm2022/ngwr-collapse.mjs +9 -9
- package/fesm2022/ngwr-collapse.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker.mjs +6 -6
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-command-palette-testing.mjs.map +1 -1
- package/fesm2022/ngwr-command-palette.mjs +3 -3
- package/fesm2022/ngwr-command-palette.mjs.map +1 -1
- package/fesm2022/ngwr-compare.mjs +3 -3
- package/fesm2022/ngwr-compare.mjs.map +1 -1
- package/fesm2022/ngwr-confetti.mjs +3 -3
- package/fesm2022/ngwr-confetti.mjs.map +1 -1
- package/fesm2022/ngwr-config.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu.mjs +12 -12
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-cookie.mjs +3 -3
- package/fesm2022/ngwr-cookie.mjs.map +1 -1
- package/fesm2022/ngwr-counter.mjs +6 -6
- package/fesm2022/ngwr-counter.mjs.map +1 -1
- package/fesm2022/ngwr-date-adapters-fns.mjs +3 -3
- package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
- package/fesm2022/ngwr-date-adapters-luxon.mjs +3 -3
- package/fesm2022/ngwr-date-adapters-luxon.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker.mjs +721 -62
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-date.mjs +3 -3
- package/fesm2022/ngwr-date.mjs.map +1 -1
- package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-decrypt-text.mjs +3 -3
- package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
- package/fesm2022/ngwr-density.mjs +6 -6
- package/fesm2022/ngwr-density.mjs.map +1 -1
- package/fesm2022/ngwr-descriptions.mjs +6 -6
- package/fesm2022/ngwr-descriptions.mjs.map +1 -1
- package/fesm2022/ngwr-dialog-testing.mjs.map +1 -1
- package/fesm2022/ngwr-dialog.mjs +15 -15
- package/fesm2022/ngwr-dialog.mjs.map +1 -1
- package/fesm2022/ngwr-directives.mjs +12 -12
- package/fesm2022/ngwr-directives.mjs.map +1 -1
- package/fesm2022/ngwr-divider.mjs +3 -3
- package/fesm2022/ngwr-divider.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart.mjs +48 -6
- package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +6 -6
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
- package/fesm2022/ngwr-drawer-testing.mjs.map +1 -1
- package/fesm2022/ngwr-drawer.mjs +18 -18
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown-testing.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +174 -25
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-editor-testing.mjs +568 -0
- package/fesm2022/ngwr-editor-testing.mjs.map +1 -0
- package/fesm2022/ngwr-editor.mjs +2601 -0
- package/fesm2022/ngwr-editor.mjs.map +1 -0
- package/fesm2022/ngwr-empty.mjs +3 -3
- package/fesm2022/ngwr-empty.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar.mjs +14 -7
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text.mjs +3 -3
- package/fesm2022/ngwr-falling-text.mjs.map +1 -1
- package/fesm2022/ngwr-file-upload-testing.mjs.map +1 -1
- package/fesm2022/ngwr-file-upload.mjs +3 -3
- package/fesm2022/ngwr-file-upload.mjs.map +1 -1
- package/fesm2022/ngwr-form-testing.mjs.map +1 -1
- package/fesm2022/ngwr-form.mjs +61 -10
- package/fesm2022/ngwr-form.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text.mjs +3 -3
- package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
- package/fesm2022/ngwr-gauge-testing.mjs.map +1 -1
- package/fesm2022/ngwr-gauge.mjs +35 -6
- package/fesm2022/ngwr-gauge.mjs.map +1 -1
- package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-glitch-text.mjs +3 -3
- package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
- package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-gradient-text.mjs +3 -3
- package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
- package/fesm2022/ngwr-graph-testing.mjs.map +1 -1
- package/fesm2022/ngwr-graph.mjs +6 -6
- package/fesm2022/ngwr-graph.mjs.map +1 -1
- package/fesm2022/ngwr-hotkey.mjs +6 -6
- package/fesm2022/ngwr-hotkey.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ar.mjs +31 -0
- package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-cs.mjs +31 -0
- package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-de.mjs +31 -0
- package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +39 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-es.mjs +31 -0
- package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-fr.mjs +31 -0
- package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-he.mjs +31 -0
- package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-hi.mjs +31 -0
- package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-id.mjs +31 -0
- package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-it.mjs +31 -0
- package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ja.mjs +31 -0
- package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ko.mjs +31 -0
- package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-nl.mjs +31 -0
- package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pl.mjs +31 -0
- package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pt.mjs +31 -0
- package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +31 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-sv.mjs +31 -0
- package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-tr.mjs +31 -0
- package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-uk.mjs +31 -0
- package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-vi.mjs +31 -0
- package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh-TW.mjs +31 -0
- package/fesm2022/ngwr-i18n-zh-TW.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh.mjs +31 -0
- package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +9 -9
- package/fesm2022/ngwr-i18n.mjs.map +1 -1
- package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -1
- package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -1
- package/fesm2022/ngwr-icon.mjs +6 -6
- package/fesm2022/ngwr-icon.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper.mjs +3 -3
- package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
- package/fesm2022/ngwr-input-number-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input-number.mjs +15 -3
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp.mjs +3 -3
- package/fesm2022/ngwr-input-otp.mjs.map +1 -1
- package/fesm2022/ngwr-input-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input.mjs +15 -15
- package/fesm2022/ngwr-input.mjs.map +1 -1
- package/fesm2022/ngwr-keyboard.mjs +3 -3
- package/fesm2022/ngwr-keyboard.mjs.map +1 -1
- package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
- package/fesm2022/ngwr-knob.mjs +3 -3
- package/fesm2022/ngwr-knob.mjs.map +1 -1
- package/fesm2022/ngwr-layout.mjs +15 -15
- package/fesm2022/ngwr-layout.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox.mjs +3 -3
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart.mjs +18 -7
- package/fesm2022/ngwr-line-chart.mjs.map +1 -1
- package/fesm2022/ngwr-list.mjs +6 -6
- package/fesm2022/ngwr-list.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar.mjs +6 -6
- package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
- package/fesm2022/ngwr-markdown-testing.mjs.map +1 -1
- package/fesm2022/ngwr-markdown.mjs +798 -17
- package/fesm2022/ngwr-markdown.mjs.map +1 -1
- package/fesm2022/ngwr-marquee-testing.mjs.map +1 -1
- package/fesm2022/ngwr-marquee.mjs +3 -3
- package/fesm2022/ngwr-marquee.mjs.map +1 -1
- package/fesm2022/ngwr-media.mjs +3 -3
- package/fesm2022/ngwr-media.mjs.map +1 -1
- package/fesm2022/ngwr-mention-testing.mjs.map +1 -1
- package/fesm2022/ngwr-mention.mjs +6 -6
- package/fesm2022/ngwr-mention.mjs.map +1 -1
- package/fesm2022/ngwr-meta.mjs +6 -6
- package/fesm2022/ngwr-meta.mjs.map +1 -1
- package/fesm2022/ngwr-meter-group-testing.mjs +7 -3
- package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -1
- package/fesm2022/ngwr-meter-group.mjs +26 -6
- package/fesm2022/ngwr-meter-group.mjs.map +1 -1
- package/fesm2022/ngwr-overlay.mjs +6 -6
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-page-header.mjs +13 -5
- package/fesm2022/ngwr-page-header.mjs.map +1 -1
- package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +3 -3
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-pipes.mjs +21 -21
- package/fesm2022/ngwr-pipes.mjs.map +1 -1
- package/fesm2022/ngwr-platform.mjs +9 -9
- package/fesm2022/ngwr-platform.mjs.map +1 -1
- package/fesm2022/ngwr-popconfirm-testing.mjs.map +1 -1
- package/fesm2022/ngwr-popconfirm.mjs +6 -6
- package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
- package/fesm2022/ngwr-popover-testing.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +578 -16
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-progress.mjs +3 -3
- package/fesm2022/ngwr-progress.mjs.map +1 -1
- package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
- package/fesm2022/ngwr-pull-to-refresh.mjs.map +1 -1
- package/fesm2022/ngwr-qr.mjs +3 -3
- package/fesm2022/ngwr-qr.mjs.map +1 -1
- package/fesm2022/ngwr-radio-testing.mjs.map +1 -1
- package/fesm2022/ngwr-radio.mjs +6 -6
- package/fesm2022/ngwr-radio.mjs.map +1 -1
- package/fesm2022/ngwr-rating-testing.mjs.map +1 -1
- package/fesm2022/ngwr-rating.mjs +3 -3
- package/fesm2022/ngwr-rating.mjs.map +1 -1
- package/fesm2022/ngwr-result.mjs +12 -12
- package/fesm2022/ngwr-result.mjs.map +1 -1
- package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-rotating-text.mjs +3 -3
- package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
- package/fesm2022/ngwr-schema-form.mjs +3 -3
- package/fesm2022/ngwr-schema-form.mjs.map +1 -1
- package/fesm2022/ngwr-scroll.mjs +3 -3
- package/fesm2022/ngwr-scroll.mjs.map +1 -1
- package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
- package/fesm2022/ngwr-segmented.mjs +3 -3
- package/fesm2022/ngwr-segmented.mjs.map +1 -1
- package/fesm2022/ngwr-select-testing.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +12 -12
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text.mjs +6 -6
- package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
- package/fesm2022/ngwr-sidebar.mjs +10 -3
- package/fesm2022/ngwr-sidebar.mjs.map +1 -1
- package/fesm2022/ngwr-skeleton.mjs +3 -3
- package/fesm2022/ngwr-skeleton.mjs.map +1 -1
- package/fesm2022/ngwr-slider-testing.mjs.map +1 -1
- package/fesm2022/ngwr-slider.mjs +3 -3
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline.mjs +84 -10
- package/fesm2022/ngwr-sparkline.mjs.map +1 -1
- package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -1
- package/fesm2022/ngwr-speed-dial.mjs +3 -3
- package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
- package/fesm2022/ngwr-spinner.mjs +3 -3
- package/fesm2022/ngwr-spinner.mjs.map +1 -1
- package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -1
- package/fesm2022/ngwr-splash-cursor.mjs +3 -3
- package/fesm2022/ngwr-splash-cursor.mjs.map +1 -1
- package/fesm2022/ngwr-split-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-split-text.mjs +3 -3
- package/fesm2022/ngwr-split-text.mjs.map +1 -1
- package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
- package/fesm2022/ngwr-splitter.mjs +3 -3
- package/fesm2022/ngwr-splitter.mjs.map +1 -1
- package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -1
- package/fesm2022/ngwr-spotlight-card.mjs +6 -6
- package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
- package/fesm2022/ngwr-squircle.mjs +6 -6
- package/fesm2022/ngwr-squircle.mjs.map +1 -1
- package/fesm2022/ngwr-star-border-testing.mjs.map +1 -1
- package/fesm2022/ngwr-star-border.mjs +3 -3
- package/fesm2022/ngwr-star-border.mjs.map +1 -1
- package/fesm2022/ngwr-statistic.mjs +9 -9
- package/fesm2022/ngwr-statistic.mjs.map +1 -1
- package/fesm2022/ngwr-stepper-testing.mjs.map +1 -1
- package/fesm2022/ngwr-stepper.mjs +6 -6
- package/fesm2022/ngwr-stepper.mjs.map +1 -1
- package/fesm2022/ngwr-storage.mjs +3 -3
- package/fesm2022/ngwr-storage.mjs.map +1 -1
- package/fesm2022/ngwr-switch-testing.mjs.map +1 -1
- package/fesm2022/ngwr-switch.mjs +3 -3
- package/fesm2022/ngwr-switch.mjs.map +1 -1
- package/fesm2022/ngwr-table-testing.mjs.map +1 -1
- package/fesm2022/ngwr-table.mjs +18 -18
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-tabs-router.mjs +3 -3
- package/fesm2022/ngwr-tabs-router.mjs.map +1 -1
- package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tabs.mjs +6 -6
- package/fesm2022/ngwr-tabs.mjs.map +1 -1
- package/fesm2022/ngwr-textarea-testing.mjs.map +1 -1
- package/fesm2022/ngwr-textarea.mjs +3 -3
- package/fesm2022/ngwr-textarea.mjs.map +1 -1
- package/fesm2022/ngwr-theme.mjs +3 -3
- package/fesm2022/ngwr-theme.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card.mjs +6 -6
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
- package/fesm2022/ngwr-timeline.mjs +6 -6
- package/fesm2022/ngwr-timeline.mjs.map +1 -1
- package/fesm2022/ngwr-toast-testing.mjs.map +1 -1
- package/fesm2022/ngwr-toast.mjs +9 -9
- package/fesm2022/ngwr-toast.mjs.map +1 -1
- package/fesm2022/ngwr-toolbar.mjs +10 -3
- package/fesm2022/ngwr-toolbar.mjs.map +1 -1
- package/fesm2022/ngwr-tour-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tour.mjs +6 -6
- package/fesm2022/ngwr-tour.mjs.map +1 -1
- package/fesm2022/ngwr-transfer-testing.mjs.map +1 -1
- package/fesm2022/ngwr-transfer.mjs +3 -3
- package/fesm2022/ngwr-transfer.mjs.map +1 -1
- package/fesm2022/ngwr-tree-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tree.mjs +3 -3
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter.mjs +3 -3
- package/fesm2022/ngwr-typewriter.mjs.map +1 -1
- package/fesm2022/ngwr-typography.mjs +7 -3
- package/fesm2022/ngwr-typography.mjs.map +1 -1
- package/fesm2022/ngwr-utils.mjs.map +1 -1
- package/fesm2022/ngwr-validators.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 +3 -3
- package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
- package/fesm2022/ngwr-waves-testing.mjs.map +1 -1
- package/fesm2022/ngwr-waves.mjs +3 -3
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/fesm2022/ngwr-window-testing.mjs.map +1 -1
- package/fesm2022/ngwr-window.mjs +12 -12
- package/fesm2022/ngwr-window.mjs.map +1 -1
- package/form/styles/_index.scss +16 -1
- package/gauge/styles/_index.scss +1 -0
- package/i18n/ar.json +32 -1
- package/i18n/cs.json +32 -1
- package/i18n/de.json +32 -1
- package/i18n/en.json +32 -1
- package/i18n/es.json +32 -1
- package/i18n/fr.json +32 -1
- package/i18n/he.json +32 -1
- package/i18n/hi.json +32 -1
- package/i18n/id.json +32 -1
- package/i18n/it.json +32 -1
- package/i18n/ja.json +32 -1
- package/i18n/ko.json +32 -1
- package/i18n/nl.json +32 -1
- package/i18n/pl.json +32 -1
- package/i18n/pt.json +32 -1
- package/i18n/ru.json +32 -1
- package/i18n/sv.json +32 -1
- package/i18n/tr.json +32 -1
- package/i18n/uk.json +32 -1
- package/i18n/vi.json +32 -1
- package/i18n/zh-TW.json +32 -1
- package/i18n/zh.json +32 -1
- package/input/styles/_index.scss +24 -1
- package/input-number/styles/_index.scss +69 -0
- package/input-otp/styles/_index.scss +5 -5
- package/line-chart/styles/_index.scss +30 -2
- package/llms-full.txt +14 -4
- package/llms.txt +188 -132
- package/mcp/providers.js +2 -2
- package/mcp/server.js +1 -1
- package/meter-group/styles/_index.scss +1 -0
- package/overlay/styles/_arrow.scss +30 -2
- package/package.json +43 -2
- package/page-header/styles/_index.scss +37 -1
- package/pagination/styles/_index.scss +9 -5
- package/popover/styles/_index.scss +73 -5
- package/progress/styles/_index.scss +4 -0
- package/radio/styles/_index.scss +4 -1
- package/schematics/use/symbol-map.json +1 -0
- package/select/styles/_index.scss +51 -12
- package/sidebar/styles/_index.scss +44 -2
- package/skeleton/styles/_index.scss +3 -0
- package/skills/ngwr/SKILL.md +5 -5
- package/skills/ngwr/references/catalog.md +2 -0
- package/skills/ngwr/references/setup.md +3 -3
- package/sparkline/styles/_index.scss +19 -0
- package/styles.scss +1 -0
- package/table/styles/_index.scss +7 -0
- package/textarea/styles/_index.scss +12 -3
- package/theme/styles/_colors.scss +39 -13
- package/theme/styles/_contrast.scss +9 -7
- package/toast/styles/_index.scss +4 -0
- package/toolbar/styles/_index.scss +35 -1
- package/tree/styles/_index.scss +46 -7
- package/types/ngwr-badge.d.ts +22 -7
- package/types/ngwr-bar-chart.d.ts +19 -1
- package/types/ngwr-calendar-heatmap.d.ts +29 -1
- package/types/ngwr-checkbox.d.ts +2 -0
- package/types/ngwr-config.d.ts +12 -0
- package/types/ngwr-date-picker.d.ts +308 -43
- package/types/ngwr-donut-chart.d.ts +24 -1
- package/types/ngwr-dropdown.d.ts +22 -2
- package/types/ngwr-editor-testing.d.ts +378 -0
- package/types/ngwr-editor.d.ts +449 -0
- package/types/ngwr-event-calendar.d.ts +8 -0
- package/types/ngwr-form.d.ts +35 -5
- package/types/ngwr-gauge.d.ts +20 -1
- package/types/ngwr-input-number.d.ts +7 -0
- package/types/ngwr-line-chart.d.ts +9 -1
- package/types/ngwr-markdown.d.ts +61 -2
- package/types/ngwr-meter-group-testing.d.ts +3 -2
- package/types/ngwr-meter-group.d.ts +14 -1
- package/types/ngwr-page-header.d.ts +10 -2
- package/types/ngwr-popover.d.ts +154 -3
- package/types/ngwr-sidebar.d.ts +7 -0
- package/types/ngwr-sparkline.d.ts +43 -1
- package/types/ngwr-toolbar.d.ts +7 -0
- package/types/ngwr-typography.d.ts +4 -0
- package/types/ngwr-version.d.ts +1 -1
- package/typography/styles/_index.scss +16 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-marquee-testing.mjs","sources":["../../../projects/lib/marquee/testing/wr-marquee-item-harness.ts","../../../projects/lib/marquee/testing/wr-marquee-harness.ts","../../../projects/lib/marquee/testing/ngwr-marquee-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrMarqueeItemHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one entry of a `<wr-marquee>`.\n *\n * **Reach these through `WrMarqueeHarness.getItems()`, which scopes them to the first\n * copy.** The strip duplicates its whole sequence to make the loop seamless, so asking\n * the document loader for all of these returns one per entry per copy — at least twice\n * everything, and a count assertion that quietly multiplies. The parent harness is the\n * only place that knows which copy is the original.\n *\n * An entry is built from an image or from a projected template, and it may or may not be\n * a link. That is the reason to ask a harness rather than the DOM: the same question —\n * what does this announce, is it decoration — is answered from a different attribute in\n * each of those shapes, and {@link getAccessibleName} and {@link isDecorative} pick the\n * right one.\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrMarqueeItemHarness extends ComponentHarness {\n static hostSelector = '.wr-marquee__item';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrMarqueeItemHarnessFilters = {}): HarnessPredicate<WrMarqueeItemHarness> {\n return new HarnessPredicate(WrMarqueeItemHarness, options)\n .addOption('name', options.name, (harness, name) =>\n HarnessPredicate.stringMatches(harness.getAccessibleName(), name)\n )\n .addOption('link', options.link, async (harness, link) => (await harness.isLink()) === link);\n }\n\n private readonly link = this.locatorForOptional('a.wr-marquee__link');\n private readonly image = this.locatorForOptional('img');\n private readonly node = this.locatorForOptional('.wr-marquee__node');\n\n /**\n * What this entry announces, without the caller needing to know how it was built.\n *\n * A linked entry answers with the link's own name, which is the end of a four-step\n * ladder — the item's `ariaLabel`, then its `title`, then an image's `alt`, then the\n * catalog's `marquee.link`. An unlinked image answers with its `alt`, where `''` means\n * the logo is decoration and announces nothing. Anything else answers with its text.\n */\n async getAccessibleName(): Promise<string | null> {\n const link = await this.link();\n if (link) return link.getAttribute('aria-label');\n\n const image = await this.image();\n if (image) return image.getAttribute('alt');\n\n return (await this.host()).text();\n }\n\n /** Whether this entry became a link — `[items]` carries `href` per entry, optionally. */\n async isLink(): Promise<boolean> {\n return (await this.link()) !== null;\n }\n\n /** The `href` exactly as the entry declared it, unresolved, or `null` when it is not a link. */\n async getHref(): Promise<string | null> {\n const link = await this.link();\n return link ? link.getAttribute('href') : null;\n }\n\n /**\n * Whether the link opens in a new tab AND says so safely: `target=\"_blank\"` with both\n * `noreferrer` and `noopener` in `rel`.\n *\n * One question rather than two, because half of it is a reverse-tabnabbing hole that\n * nothing visual would ever show. The library hard-codes the safe pair, so this is a\n * regression check on the template rather than on the caller's data.\n *\n * Throws for an entry that is not a link, where a `false` would read as \"it opens in\n * this tab\" — an answer about safety that is not true of anything.\n */\n async opensInNewTab(): Promise<boolean> {\n const link = await this.link();\n if (!link) {\n throw new Error(\n `WrMarqueeItemHarness.opensInNewTab(): \"${await this.getAccessibleName()}\" is not a link — its entry in ` +\n '[items] carries no `href`, so there is no target or rel to check. Ask isLink() first.'\n );\n }\n\n const rel = (await link.getAttribute('rel')) ?? '';\n return (await link.getAttribute('target')) === '_blank' && rel.includes('noreferrer') && rel.includes('noopener');\n }\n\n /**\n * Whether this entry's content is kept out of the accessible tree, leaving whatever\n * wraps it to do the announcing.\n *\n * The marker depends on how the entry was built, which is the whole reason to ask here:\n * a projected template inside a named link carries `aria-hidden`, so the link's own name\n * is not read twice, while an image says the same thing with `alt=\"\"`. It describes the\n * CONTENT — a decorative logo inside a link is still announced, by the link, and that\n * name is {@link getAccessibleName}.\n *\n * Throws for an entry that rendered neither, which should not happen — every entry of\n * `[items]` is one or the other.\n */\n async isDecorative(): Promise<boolean> {\n const node = await this.node();\n if (node) return (await node.getAttribute('aria-hidden')) === 'true';\n\n const image = await this.image();\n if (image) return (await image.getAttribute('alt')) === '';\n\n throw new Error(\n 'WrMarqueeItemHarness.isDecorative(): this entry rendered neither a projected node nor an image, so there ' +\n 'is nothing carrying a decorative marker. Every entry of [items] is one or the other.'\n );\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrMarqueeHarnessFilters, WrMarqueeLink } from './interfaces';\nimport { WrMarqueeItemHarness } from './wr-marquee-item-harness';\n\n/** Copy 0 is the original sequence; every other `.wr-marquee__list` is a clone of it. */\nconst FIRST_COPY = '.wr-marquee__list[data-copy=\"0\"]';\n\n/** Read one declaration out of an inline `style` attribute, or `null` when it is not there. */\nfunction inlineStyle(style: string | null, property: string): string | null {\n for (const declaration of (style ?? '').split(';')) {\n const colon = declaration.indexOf(':');\n if (colon === -1) continue;\n if (declaration.slice(0, colon).trim() !== property) continue;\n return declaration.slice(colon + 1).trim();\n }\n return null;\n}\n\n/**\n * Test harness for `<wr-marquee>`.\n *\n * **Almost everything worth asserting about a marquee is structural, not animated.** The\n * strip duplicates its sequence so the loop has no seam, and only the first copy is left\n * in the accessible tree — {@link getAnnouncedCopyCount} is the assertion that catches\n * the regression that matters, because a duplicate leaking in makes a screen reader read\n * the whole logo strip twice and changes nothing on screen. The other half is the naming\n * ladder on linked entries, which is real logic with a real bug in its history: the\n * fallback used to be the hard-coded English word \"link\".\n *\n * **Every list query is scoped to the first copy**, and that is the reason to use\n * {@link getItems} rather than a raw `.wr-marquee__item` query, which returns at least\n * twice as many elements as there are entries.\n *\n * Nothing about the motion is offered. The track's `translate3d` is written only when the\n * measured sequence is wider than zero, so it never appears in a test at all, and in a\n * browser it is one frame of a loop that is already stale by the time an assertion reads\n * it. `[speed]` and `[direction]` reach the DOM in no form. Neither does the hover state:\n * pausing flips a private signal with no class, attribute or property behind it, so a\n * `hover()` here would fire events that provably change nothing readable. And the\n * reduced-motion freeze is a CSS `!important` rule with no counterpart in TypeScript,\n * which means the DOM is identical either way — see `getGap` for what a harness can\n * honestly read off a stylesheet-driven component, and why it reads it where it does.\n *\n * @example\n * ```ts\n * const loader = TestbedHarnessEnvironment.loader(fixture);\n * const strip = await loader.getHarness(WrMarqueeHarness);\n *\n * expect(await strip.getAnnouncedCopyCount()).toBe(1);\n * expect(await strip.getItemCount()).toBe(logos.length);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrMarqueeHarness extends ComponentHarness {\n static hostSelector = 'wr-marquee';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrMarqueeHarnessFilters = {}): HarnessPredicate<WrMarqueeHarness> {\n return new HarnessPredicate(WrMarqueeHarness, options).addOption('ariaLabel', options.ariaLabel, (harness, label) =>\n HarnessPredicate.stringMatches(harness.getAccessibleName(), label)\n );\n }\n\n private readonly copies = this.locatorForAll('.wr-marquee__list');\n\n /**\n * The name the region announces.\n *\n * Pins the i18n ladder end to end: `'Marquee'` with no catalog loaded, the consumer's\n * `[ariaLabel]` when one is given, the localized string under a catalog. A landmark\n * with no name is an axe failure, so this is what keeps the `region` legitimate rather\n * than a decoration a screen-reader user is invited to jump to and finds unnamed.\n */\n async getAccessibleName(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /**\n * The landmark role the strip claims.\n *\n * Static in the template and one attribute away from being dropped in a refactor, which\n * is exactly why a consumer asserting their page's landmark structure should be able to\n * see it from here.\n */\n async getRole(): Promise<string | null> {\n return (await this.host()).getAttribute('role');\n }\n\n /**\n * How many copies of the sequence are rendered.\n *\n * The seamless loop is built by repeating the whole list until it covers the container,\n * so the count is decided by measurement — which means it stays at the floor of 2 in a\n * test with no layout, whatever the container would really be. Assert `>= 2`; the\n * growth needs a browser.\n */\n async getCopyCount(): Promise<number> {\n return (await this.copies()).length;\n }\n\n /**\n * How many copies are left in the accessible tree. Must be exactly 1.\n *\n * The one assertion here that catches a silent regression: every copy after the first\n * carries `aria-hidden=\"true\"`, and losing that makes a screen reader read the whole\n * strip once per copy while the page looks unchanged.\n *\n * Note the first copy carries the literal `aria-hidden=\"false\"` rather than no\n * attribute at all, so this compares against the string `'true'` — testing the\n * attribute for presence would count nothing at all as announced.\n */\n async getAnnouncedCopyCount(): Promise<number> {\n const copies = await this.copies();\n const hidden = await Promise.all(copies.map(copy => copy.getAttribute('aria-hidden')));\n return hidden.filter(value => value !== 'true').length;\n }\n\n /** The entries of the first copy, in order — one per entry of `[items]`. */\n async getItems(): Promise<WrMarqueeItemHarness[]> {\n return this.locatorForAll(WrMarqueeItemHarness.with({ ancestor: FIRST_COPY }))();\n }\n\n /**\n * How many entries the strip drew, counting the first copy only.\n *\n * One per entry of `[items]` — the mapping a consumer's own data pipeline is really\n * being tested for.\n */\n async getItemCount(): Promise<number> {\n return (await this.locatorForAll(`${FIRST_COPY} .wr-marquee__item`)()).length;\n }\n\n /**\n * The `alt` of each image in the first copy, in order.\n *\n * `null` in this list is the thing to look for: it means the attribute is MISSING, which\n * is a serious axe violation and announces the file name. An empty string is the\n * opposite — a deliberately decorative logo — and the template's `alt || ''` is what\n * keeps the two apart. A method typed `string[]` would have had to flatten them.\n */\n async getImageAlts(): Promise<(string | null)[]> {\n const images = await this.locatorForAll(`${FIRST_COPY} img`)();\n return Promise.all(images.map(image => image.getAttribute('alt')));\n }\n\n /**\n * Every linked entry of the first copy: where it goes and what it announces.\n *\n * The name is the interesting half. It walks a ladder ending in the catalog's\n * `marquee.link`, so an unlabelled logo announces a localized word rather than nothing\n * — real component logic rather than an echo of the input.\n */\n async getLinks(): Promise<WrMarqueeLink[]> {\n const links = await this.locatorForAll(`${FIRST_COPY} a.wr-marquee__link`)();\n return Promise.all(\n links.map(async link => ({ href: await link.getAttribute('href'), name: await link.getAttribute('aria-label') }))\n );\n }\n\n /**\n * Whether the edge fades are on.\n *\n * The fades themselves are `::before` / `::after` gradients, which no unit environment\n * can see — so the modifier class is the observable, and it is public API a consumer\n * may be styling against in any case.\n */\n async hasFade(): Promise<boolean> {\n return (await this.host()).hasClass('wr-marquee--fade');\n }\n\n /**\n * The fade colour as the component passed it through — a CSS value such as\n * `'var(--wr-color-success)'`, not a resolved colour — or `null` when none was given.\n *\n * Worth asserting next to {@link hasFade}: a colour with no fade modifier renders\n * nothing at all, which is a consumer mistake this pairing makes visible.\n */\n async getFadeColor(): Promise<string | null> {\n return this.hostStyle('--wr-marquee-fade-color');\n }\n\n /**\n * Whether entries scale up under the pointer.\n *\n * A `:hover` transform, so again the modifier is the only honest read — and it earns\n * its place by doing more than the name suggests: it also changes the strip's vertical\n * padding, so a lost class shifts the layout as well as the hover.\n */\n async hasScaleOnHover(): Promise<boolean> {\n return (await this.host()).hasClass('wr-marquee--scale-hover');\n }\n\n /** The gap between entries in pixels — the seam width the stylesheet reads. */\n async getGap(): Promise<number> {\n return this.pixels('--wr-marquee-gap', 'getGap');\n }\n\n /**\n * The entry height in pixels.\n *\n * It drives the image height AND the entry font size, so a binding that stops arriving\n * collapses the whole strip rather than merely resizing it.\n */\n async getItemHeight(): Promise<number> {\n return this.pixels('--wr-marquee-height', 'getItemHeight');\n }\n\n /**\n * Read a custom property off the host's inline `style`, rather than through\n * `getCssValue()`.\n *\n * Not a jsdom workaround — the opposite. The stylesheet declares its own\n * `--wr-marquee-gap: 32px` and `--wr-marquee-height: 28px` on `.wr-marquee`, so a\n * computed read in a real browser answers with the stylesheet's default when the host\n * binding is the very thing that broke. The inline value is what the component wrote,\n * and nothing else can forge it.\n */\n private async hostStyle(property: string): Promise<string | null> {\n return inlineStyle(await (await this.host()).getAttribute('style'), property);\n }\n\n /**\n * Parse a `<n>px` custom property, refusing anything that lost its unit.\n *\n * The refusal is the point. `[gap]` and `[itemHeight]` are numbers that the host binding\n * turns into lengths, and the stylesheet uses them raw — a bare `32` is an invalid\n * declaration, so the property falls back to the stylesheet default and the strip looks\n * merely a little different. A `parseFloat` would report 32 either way and hide it.\n */\n private async pixels(property: string, method: string): Promise<number> {\n const raw = await this.hostStyle(property);\n const match = /^(-?\\d*\\.?\\d+)px$/.exec(raw ?? '');\n\n if (!match) {\n throw new Error(\n `WrMarqueeHarness.${method}(): \\`${property}\\` reads \"${raw}\" rather than a px length. The stylesheet ` +\n 'uses this value as a raw CSS length, so anything without the unit is an invalid declaration and the ' +\n 'strip silently falls back to the default in the stylesheet.'\n );\n }\n return Number(match[1]);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-marquee-testing.mjs","sources":["../../../projects/lib/marquee/testing/wr-marquee-item-harness.ts","../../../projects/lib/marquee/testing/wr-marquee-harness.ts","../../../projects/lib/marquee/testing/ngwr-marquee-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrMarqueeItemHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one entry of a `<wr-marquee>`.\n *\n * **Reach these through `WrMarqueeHarness.getItems()`, which scopes them to the first\n * copy.** The strip duplicates its whole sequence to make the loop seamless, so asking\n * the document loader for all of these returns one per entry per copy — at least twice\n * everything, and a count assertion that quietly multiplies. The parent harness is the\n * only place that knows which copy is the original.\n *\n * An entry is built from an image or from a projected template, and it may or may not be\n * a link. That is the reason to ask a harness rather than the DOM: the same question —\n * what does this announce, is it decoration — is answered from a different attribute in\n * each of those shapes, and {@link getAccessibleName} and {@link isDecorative} pick the\n * right one.\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrMarqueeItemHarness extends ComponentHarness {\n static hostSelector = '.wr-marquee__item';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrMarqueeItemHarnessFilters = {}): HarnessPredicate<WrMarqueeItemHarness> {\n return new HarnessPredicate(WrMarqueeItemHarness, options)\n .addOption('name', options.name, (harness, name) =>\n HarnessPredicate.stringMatches(harness.getAccessibleName(), name)\n )\n .addOption('link', options.link, async (harness, link) => (await harness.isLink()) === link);\n }\n\n private readonly link = this.locatorForOptional('a.wr-marquee__link');\n private readonly image = this.locatorForOptional('img');\n private readonly node = this.locatorForOptional('.wr-marquee__node');\n\n /**\n * What this entry announces, without the caller needing to know how it was built.\n *\n * A linked entry answers with the link's own name, which is the end of a four-step\n * ladder — the item's `ariaLabel`, then its `title`, then an image's `alt`, then the\n * catalog's `marquee.link`. An unlinked image answers with its `alt`, where `''` means\n * the logo is decoration and announces nothing. Anything else answers with its text.\n */\n async getAccessibleName(): Promise<string | null> {\n const link = await this.link();\n if (link) return link.getAttribute('aria-label');\n\n const image = await this.image();\n if (image) return image.getAttribute('alt');\n\n return (await this.host()).text();\n }\n\n /** Whether this entry became a link — `[items]` carries `href` per entry, optionally. */\n async isLink(): Promise<boolean> {\n return (await this.link()) !== null;\n }\n\n /** The `href` exactly as the entry declared it, unresolved, or `null` when it is not a link. */\n async getHref(): Promise<string | null> {\n const link = await this.link();\n return link ? link.getAttribute('href') : null;\n }\n\n /**\n * Whether the link opens in a new tab AND says so safely: `target=\"_blank\"` with both\n * `noreferrer` and `noopener` in `rel`.\n *\n * One question rather than two, because half of it is a reverse-tabnabbing hole that\n * nothing visual would ever show. The library hard-codes the safe pair, so this is a\n * regression check on the template rather than on the caller's data.\n *\n * Throws for an entry that is not a link, where a `false` would read as \"it opens in\n * this tab\" — an answer about safety that is not true of anything.\n */\n async opensInNewTab(): Promise<boolean> {\n const link = await this.link();\n if (!link) {\n throw new Error(\n `WrMarqueeItemHarness.opensInNewTab(): \"${await this.getAccessibleName()}\" is not a link — its entry in ` +\n '[items] carries no `href`, so there is no target or rel to check. Ask isLink() first.'\n );\n }\n\n const rel = (await link.getAttribute('rel')) ?? '';\n return (await link.getAttribute('target')) === '_blank' && rel.includes('noreferrer') && rel.includes('noopener');\n }\n\n /**\n * Whether this entry's content is kept out of the accessible tree, leaving whatever\n * wraps it to do the announcing.\n *\n * The marker depends on how the entry was built, which is the whole reason to ask here:\n * a projected template inside a named link carries `aria-hidden`, so the link's own name\n * is not read twice, while an image says the same thing with `alt=\"\"`. It describes the\n * CONTENT — a decorative logo inside a link is still announced, by the link, and that\n * name is {@link getAccessibleName}.\n *\n * Throws for an entry that rendered neither, which should not happen — every entry of\n * `[items]` is one or the other.\n */\n async isDecorative(): Promise<boolean> {\n const node = await this.node();\n if (node) return (await node.getAttribute('aria-hidden')) === 'true';\n\n const image = await this.image();\n if (image) return (await image.getAttribute('alt')) === '';\n\n throw new Error(\n 'WrMarqueeItemHarness.isDecorative(): this entry rendered neither a projected node nor an image, so there ' +\n 'is nothing carrying a decorative marker. Every entry of [items] is one or the other.'\n );\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrMarqueeHarnessFilters, WrMarqueeLink } from './interfaces';\nimport { WrMarqueeItemHarness } from './wr-marquee-item-harness';\n\n/** Copy 0 is the original sequence; every other `.wr-marquee__list` is a clone of it. */\nconst FIRST_COPY = '.wr-marquee__list[data-copy=\"0\"]';\n\n/** Read one declaration out of an inline `style` attribute, or `null` when it is not there. */\nfunction inlineStyle(style: string | null, property: string): string | null {\n for (const declaration of (style ?? '').split(';')) {\n const colon = declaration.indexOf(':');\n if (colon === -1) continue;\n if (declaration.slice(0, colon).trim() !== property) continue;\n return declaration.slice(colon + 1).trim();\n }\n return null;\n}\n\n/**\n * Test harness for `<wr-marquee>`.\n *\n * **Almost everything worth asserting about a marquee is structural, not animated.** The\n * strip duplicates its sequence so the loop has no seam, and only the first copy is left\n * in the accessible tree — {@link getAnnouncedCopyCount} is the assertion that catches\n * the regression that matters, because a duplicate leaking in makes a screen reader read\n * the whole logo strip twice and changes nothing on screen. The other half is the naming\n * ladder on linked entries, which is real logic with a real bug in its history: the\n * fallback used to be the hard-coded English word \"link\".\n *\n * **Every list query is scoped to the first copy**, and that is the reason to use\n * {@link getItems} rather than a raw `.wr-marquee__item` query, which returns at least\n * twice as many elements as there are entries.\n *\n * Nothing about the motion is offered. The track's `translate3d` is written only when the\n * measured sequence is wider than zero, so it never appears in a test at all, and in a\n * browser it is one frame of a loop that is already stale by the time an assertion reads\n * it. `[speed]` and `[direction]` reach the DOM in no form. Neither does the hover state:\n * pausing flips a private signal with no class, attribute or property behind it, so a\n * `hover()` here would fire events that provably change nothing readable. And the\n * reduced-motion freeze is a CSS `!important` rule with no counterpart in TypeScript,\n * which means the DOM is identical either way — see `getGap` for what a harness can\n * honestly read off a stylesheet-driven component, and why it reads it where it does.\n *\n * @example\n * ```ts\n * const loader = TestbedHarnessEnvironment.loader(fixture);\n * const strip = await loader.getHarness(WrMarqueeHarness);\n *\n * expect(await strip.getAnnouncedCopyCount()).toBe(1);\n * expect(await strip.getItemCount()).toBe(logos.length);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrMarqueeHarness extends ComponentHarness {\n static hostSelector = 'wr-marquee';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrMarqueeHarnessFilters = {}): HarnessPredicate<WrMarqueeHarness> {\n return new HarnessPredicate(WrMarqueeHarness, options).addOption('ariaLabel', options.ariaLabel, (harness, label) =>\n HarnessPredicate.stringMatches(harness.getAccessibleName(), label)\n );\n }\n\n private readonly copies = this.locatorForAll('.wr-marquee__list');\n\n /**\n * The name the region announces.\n *\n * Pins the i18n ladder end to end: `'Marquee'` with no catalog loaded, the consumer's\n * `[ariaLabel]` when one is given, the localized string under a catalog. A landmark\n * with no name is an axe failure, so this is what keeps the `region` legitimate rather\n * than a decoration a screen-reader user is invited to jump to and finds unnamed.\n */\n async getAccessibleName(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /**\n * The landmark role the strip claims.\n *\n * Static in the template and one attribute away from being dropped in a refactor, which\n * is exactly why a consumer asserting their page's landmark structure should be able to\n * see it from here.\n */\n async getRole(): Promise<string | null> {\n return (await this.host()).getAttribute('role');\n }\n\n /**\n * How many copies of the sequence are rendered.\n *\n * The seamless loop is built by repeating the whole list until it covers the container,\n * so the count is decided by measurement — which means it stays at the floor of 2 in a\n * test with no layout, whatever the container would really be. Assert `>= 2`; the\n * growth needs a browser.\n */\n async getCopyCount(): Promise<number> {\n return (await this.copies()).length;\n }\n\n /**\n * How many copies are left in the accessible tree. Must be exactly 1.\n *\n * The one assertion here that catches a silent regression: every copy after the first\n * carries `aria-hidden=\"true\"`, and losing that makes a screen reader read the whole\n * strip once per copy while the page looks unchanged.\n *\n * Note the first copy carries the literal `aria-hidden=\"false\"` rather than no\n * attribute at all, so this compares against the string `'true'` — testing the\n * attribute for presence would count nothing at all as announced.\n */\n async getAnnouncedCopyCount(): Promise<number> {\n const copies = await this.copies();\n const hidden = await Promise.all(copies.map(copy => copy.getAttribute('aria-hidden')));\n return hidden.filter(value => value !== 'true').length;\n }\n\n /** The entries of the first copy, in order — one per entry of `[items]`. */\n async getItems(): Promise<WrMarqueeItemHarness[]> {\n return this.locatorForAll(WrMarqueeItemHarness.with({ ancestor: FIRST_COPY }))();\n }\n\n /**\n * How many entries the strip drew, counting the first copy only.\n *\n * One per entry of `[items]` — the mapping a consumer's own data pipeline is really\n * being tested for.\n */\n async getItemCount(): Promise<number> {\n return (await this.locatorForAll(`${FIRST_COPY} .wr-marquee__item`)()).length;\n }\n\n /**\n * The `alt` of each image in the first copy, in order.\n *\n * `null` in this list is the thing to look for: it means the attribute is MISSING, which\n * is a serious axe violation and announces the file name. An empty string is the\n * opposite — a deliberately decorative logo — and the template's `alt || ''` is what\n * keeps the two apart. A method typed `string[]` would have had to flatten them.\n */\n async getImageAlts(): Promise<(string | null)[]> {\n const images = await this.locatorForAll(`${FIRST_COPY} img`)();\n return Promise.all(images.map(image => image.getAttribute('alt')));\n }\n\n /**\n * Every linked entry of the first copy: where it goes and what it announces.\n *\n * The name is the interesting half. It walks a ladder ending in the catalog's\n * `marquee.link`, so an unlabelled logo announces a localized word rather than nothing\n * — real component logic rather than an echo of the input.\n */\n async getLinks(): Promise<WrMarqueeLink[]> {\n const links = await this.locatorForAll(`${FIRST_COPY} a.wr-marquee__link`)();\n return Promise.all(\n links.map(async link => ({ href: await link.getAttribute('href'), name: await link.getAttribute('aria-label') }))\n );\n }\n\n /**\n * Whether the edge fades are on.\n *\n * The fades themselves are `::before` / `::after` gradients, which no unit environment\n * can see — so the modifier class is the observable, and it is public API a consumer\n * may be styling against in any case.\n */\n async hasFade(): Promise<boolean> {\n return (await this.host()).hasClass('wr-marquee--fade');\n }\n\n /**\n * The fade colour as the component passed it through — a CSS value such as\n * `'var(--wr-color-success)'`, not a resolved colour — or `null` when none was given.\n *\n * Worth asserting next to {@link hasFade}: a colour with no fade modifier renders\n * nothing at all, which is a consumer mistake this pairing makes visible.\n */\n async getFadeColor(): Promise<string | null> {\n return this.hostStyle('--wr-marquee-fade-color');\n }\n\n /**\n * Whether entries scale up under the pointer.\n *\n * A `:hover` transform, so again the modifier is the only honest read — and it earns\n * its place by doing more than the name suggests: it also changes the strip's vertical\n * padding, so a lost class shifts the layout as well as the hover.\n */\n async hasScaleOnHover(): Promise<boolean> {\n return (await this.host()).hasClass('wr-marquee--scale-hover');\n }\n\n /** The gap between entries in pixels — the seam width the stylesheet reads. */\n async getGap(): Promise<number> {\n return this.pixels('--wr-marquee-gap', 'getGap');\n }\n\n /**\n * The entry height in pixels.\n *\n * It drives the image height AND the entry font size, so a binding that stops arriving\n * collapses the whole strip rather than merely resizing it.\n */\n async getItemHeight(): Promise<number> {\n return this.pixels('--wr-marquee-height', 'getItemHeight');\n }\n\n /**\n * Read a custom property off the host's inline `style`, rather than through\n * `getCssValue()`.\n *\n * Not a jsdom workaround — the opposite. The stylesheet declares its own\n * `--wr-marquee-gap: 32px` and `--wr-marquee-height: 28px` on `.wr-marquee`, so a\n * computed read in a real browser answers with the stylesheet's default when the host\n * binding is the very thing that broke. The inline value is what the component wrote,\n * and nothing else can forge it.\n */\n private async hostStyle(property: string): Promise<string | null> {\n return inlineStyle(await (await this.host()).getAttribute('style'), property);\n }\n\n /**\n * Parse a `<n>px` custom property, refusing anything that lost its unit.\n *\n * The refusal is the point. `[gap]` and `[itemHeight]` are numbers that the host binding\n * turns into lengths, and the stylesheet uses them raw — a bare `32` is an invalid\n * declaration, so the property falls back to the stylesheet default and the strip looks\n * merely a little different. A `parseFloat` would report 32 either way and hide it.\n */\n private async pixels(property: string, method: string): Promise<number> {\n const raw = await this.hostStyle(property);\n const match = /^(-?\\d*\\.?\\d+)px$/.exec(raw ?? '');\n\n if (!match) {\n throw new Error(\n `WrMarqueeHarness.${method}(): \\`${property}\\` reads \"${raw}\" rather than a px length. The stylesheet ` +\n 'uses this value as a raw CSS length, so anything without the unit is an invalid declaration and the ' +\n 'strip silently falls back to the default in the stylesheet.'\n );\n }\n return Number(match[1]);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -211,10 +211,10 @@ class WrMarquee {
|
|
|
211
211
|
raf = requestAnimationFrame(tick);
|
|
212
212
|
this.destroyRef.onDestroy(() => cancelAnimationFrame(raf));
|
|
213
213
|
}
|
|
214
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
215
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
214
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMarquee, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
215
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrMarquee, isStandalone: true, selector: "wr-marquee", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, speed: { classPropertyName: "speed", publicName: "speed", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, itemHeight: { classPropertyName: "itemHeight", publicName: "itemHeight", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, pauseOnHover: { classPropertyName: "pauseOnHover", publicName: "pauseOnHover", isSignal: true, isRequired: false, transformFunction: null }, hoverSpeed: { classPropertyName: "hoverSpeed", publicName: "hoverSpeed", isSignal: true, isRequired: false, transformFunction: null }, fadeOut: { classPropertyName: "fadeOut", publicName: "fadeOut", isSignal: true, isRequired: false, transformFunction: null }, fadeOutColor: { classPropertyName: "fadeOutColor", publicName: "fadeOutColor", isSignal: true, isRequired: false, transformFunction: null }, scaleOnHover: { classPropertyName: "scaleOnHover", publicName: "scaleOnHover", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.wr-marquee--fade": "fadeOut()", "class.wr-marquee--scale-hover": "scaleOnHover()", "style.--wr-marquee-gap.px": "gap()", "style.--wr-marquee-height.px": "itemHeight()", "style.--wr-marquee-fade-color": "fadeOutColor() || null", "attr.role": "\"region\"", "attr.aria-label": "resolvedAriaLabel()" }, classAttribute: "wr-marquee" }, viewQueries: [{ propertyName: "trackEl", first: true, predicate: ["track"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"wr-marquee__track\" #track (mouseenter)=\"onEnter()\" (mouseleave)=\"onLeave()\">\n @for (copyIndex of copyIndices(); track copyIndex) {\n <!--\n `inert` alongside `aria-hidden`, and never one without the other: the copies\n exist only to fill the track, and an item with an `href` renders a real anchor\n in every one of them. Hidden from the screen reader but still in the tab order,\n each duplicate is a tab stop that announces nothing \u2014 axe's `aria-hidden-focus`,\n and N stops on one destination for anyone using the keyboard.\n -->\n <ul\n class=\"wr-marquee__list\"\n role=\"list\"\n [attr.aria-hidden]=\"copyIndex > 0\"\n [attr.inert]=\"copyIndex > 0 ? '' : null\"\n [attr.data-copy]=\"copyIndex\"\n >\n @for (item of items(); track $index) {\n <li class=\"wr-marquee__item\" role=\"listitem\">\n @if (item.href) {\n <a\n class=\"wr-marquee__link\"\n [href]=\"item.href\"\n [attr.aria-label]=\"\n (isNodeItem(item) ? item.ariaLabel || item.title : item.alt || item.title) || resolvedLinkLabel()\n \"\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n >\n <ng-container *ngTemplateOutlet=\"content; context: { $implicit: item }\" />\n </a>\n } @else {\n <ng-container *ngTemplateOutlet=\"content; context: { $implicit: item }\" />\n }\n </li>\n }\n </ul>\n }\n</div>\n\n<ng-template #content let-item>\n @if (isNodeItem(item)) {\n <span class=\"wr-marquee__node\" [attr.aria-hidden]=\"!!item.href && !item.ariaLabel\">\n <ng-container *ngTemplateOutlet=\"item.node\" />\n </span>\n } @else {\n <img\n [src]=\"item.src\"\n [srcset]=\"item.srcSet || null\"\n [sizes]=\"item.sizes || null\"\n [width]=\"item.width || null\"\n [height]=\"item.height || null\"\n [alt]=\"item.alt || ''\"\n [title]=\"item.title || null\"\n loading=\"lazy\"\n decoding=\"async\"\n draggable=\"false\"\n />\n }\n</ng-template>\n", styles: [".wr-marquee{--wr-marquee-gap: 32px;--wr-marquee-height: 28px;--wr-marquee-fade-color-auto: var(--wr-color-surface, #ffffff);position:relative;display:block;width:100%;overflow:hidden}.wr-marquee--scale-hover{padding-top:calc(var(--wr-marquee-height) * .1);padding-bottom:calc(var(--wr-marquee-height) * .1)}.wr-marquee__track{display:flex;width:max-content;will-change:transform;-webkit-user-select:none;user-select:none;position:relative;z-index:0}.wr-marquee__list{display:flex;align-items:center;margin:0;padding:0;list-style:none}.wr-marquee__item{flex:0 0 auto;margin-right:var(--wr-marquee-gap);font-size:var(--wr-marquee-height);line-height:1}.wr-marquee__item:last-child{margin-right:var(--wr-marquee-gap)}.wr-marquee__node{display:inline-flex;align-items:center}.wr-marquee__item img{height:var(--wr-marquee-height);width:auto;display:block;object-fit:contain;image-rendering:-webkit-optimize-contrast;-webkit-user-drag:none;pointer-events:none;transition:transform var(--wr-duration-slow) var(--wr-ease-out)}.wr-marquee--scale-hover .wr-marquee__item{overflow:visible}.wr-marquee--scale-hover .wr-marquee__item:hover img,.wr-marquee--scale-hover .wr-marquee__item:hover .wr-marquee__node{transform:scale(1.2);transform-origin:center center}.wr-marquee--scale-hover .wr-marquee__node{transition:transform var(--wr-duration-slow) var(--wr-ease-out)}.wr-marquee__link{display:inline-flex;align-items:center;text-decoration:none;border-radius:4px;transition:opacity .2s ease}.wr-marquee__link:hover{opacity:.8}.wr-marquee__link:focus-visible{outline:2px solid currentColor;outline-offset:2px}.wr-marquee--fade:before,.wr-marquee--fade:after{content:\"\";position:absolute;top:0;bottom:0;width:clamp(24px,8%,120px);pointer-events:none;z-index:10}.wr-marquee--fade:before{left:0;background:linear-gradient(to right,var(--wr-marquee-fade-color, var(--wr-marquee-fade-color-auto)) 0%,rgba(0,0,0,0) 100%)}.wr-marquee--fade:after{right:0;background:linear-gradient(to left,var(--wr-marquee-fade-color, var(--wr-marquee-fade-color-auto)) 0%,rgba(0,0,0,0) 100%)}@media(prefers-reduced-motion:reduce){.wr-marquee .wr-marquee__track{transform:translateZ(0)!important}.wr-marquee .wr-marquee__item img,.wr-marquee .wr-marquee__node{transition:none!important}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
216
216
|
}
|
|
217
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
217
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMarquee, decorators: [{
|
|
218
218
|
type: Component,
|
|
219
219
|
args: [{ selector: 'wr-marquee', encapsulation: ViewEncapsulation.None, imports: [NgTemplateOutlet], host: {
|
|
220
220
|
class: 'wr-marquee',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-marquee.mjs","sources":["../../../projects/lib/marquee/marquee.ts","../../../projects/lib/marquee/marquee.html","../../../projects/lib/marquee/ngwr-marquee.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n *\n * Angular adaptation of the LogoLoop effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/animations/logo-loop\n *\n * Renamed `wr-marquee` in ngwr — same rAF-driven infinite carousel,\n * supports image + template items, optional fade/scale/pause-on-hover.\n */\n\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { NgTemplateOutlet, isPlatformBrowser } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n ElementRef,\n PLATFORM_ID,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n signal,\n viewChild,\n} from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { numAttr } from 'ngwr/utils';\n\nimport type { WrMarqueeNode, WrMarqueeItem } from './interfaces';\n\nconst SMOOTH_TAU = 0.25;\nconst MIN_COPIES = 2;\nconst COPY_HEADROOM = 2;\n\nfunction isNode(item: WrMarqueeItem): item is WrMarqueeNode {\n return 'node' in item;\n}\n\n/**\n * Endless horizontal marquee. Auto-duplicates the items list until it\n * covers the container width, then animates the track with a smooth\n * rAF-driven scroll. Optional edge fades, scale-on-hover, and pause /\n * variable-speed hover behaviour.\n *\n * @example\n * ```html\n * <wr-marquee\n * [items]=\"logos\"\n * [speed]=\"80\"\n * [gap]=\"48\"\n * [itemHeight]=\"36\"\n * [fadeOut]=\"true\"\n * [scaleOnHover]=\"true\"\n * [pauseOnHover]=\"true\"\n * />\n * ```\n *\n * @see https://www.reactbits.dev/animations/logo-loop\n */\n@Component({\n selector: 'wr-marquee',\n templateUrl: './marquee.html',\n styleUrl: './marquee.scss',\n encapsulation: ViewEncapsulation.None,\n imports: [NgTemplateOutlet],\n host: {\n class: 'wr-marquee',\n '[class.wr-marquee--fade]': 'fadeOut()',\n '[class.wr-marquee--scale-hover]': 'scaleOnHover()',\n '[style.--wr-marquee-gap.px]': 'gap()',\n '[style.--wr-marquee-height.px]': 'itemHeight()',\n '[style.--wr-marquee-fade-color]': 'fadeOutColor() || null',\n '[attr.role]': '\"region\"',\n '[attr.aria-label]': 'resolvedAriaLabel()',\n },\n})\nexport class WrMarquee {\n /** Items to display. Each entry is either an image source or a TemplateRef node. */\n readonly items = input.required<readonly WrMarqueeItem[]>();\n\n /** Scroll speed in pixels per second. Negative reverses direction. @default 120 */\n readonly speed = input(120, { transform: numAttr(120) });\n\n /** Track direction. @default 'left' */\n readonly direction = input<'left' | 'right'>('left');\n\n /** Item height in pixels. @default 28 */\n readonly itemHeight = input(28, { transform: numAttr(28) });\n\n /** Gap between items in pixels. @default 32 */\n readonly gap = input(32, { transform: numAttr(32) });\n\n /** Pause the loop on hover. @default false */\n readonly pauseOnHover = input(false, { transform: coerceBooleanProperty });\n\n /** Custom speed (px/s) when hovered. Overrides `pauseOnHover`. */\n readonly hoverSpeed = input<number | undefined>(undefined, {\n transform: (v: unknown): number | undefined => (v == null || v === '' ? undefined : coerceNumberProperty(v, 0)),\n });\n\n /** Apply edge fade-out gradients. @default false */\n readonly fadeOut = input(false, { transform: coerceBooleanProperty });\n\n /** CSS colour for the fade-out gradient. Defaults to the page background. */\n readonly fadeOutColor = input<string>('');\n\n /** Scale individual items up on hover. @default false */\n readonly scaleOnHover = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Accessible name for the region. Falls back to `marquee.label`, then\n * `'Marquee'`.\n */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'marquee.label', 'Marquee');\n\n /**\n * Name used for an item that links somewhere but carries no text of its own.\n * Falls back to `marquee.link`, then `'link'` — a bare `'link'` in English was\n * what a screen reader announced for every unlabelled logo in the strip.\n */\n protected readonly resolvedLinkLabel = useI18nText(signal(null).asReadonly(), 'marquee.link', 'link');\n\n private readonly trackEl = viewChild.required<ElementRef<HTMLElement>>('track');\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n protected readonly seqWidth = signal(0);\n protected readonly copyCount = signal(MIN_COPIES);\n protected readonly isHovered = signal(false);\n\n protected readonly copyIndices = computed(() => Array.from({ length: this.copyCount() }, (_, i) => i));\n\n /** Resolved hover speed: `hoverSpeed` overrides, else 0 when `pauseOnHover`, else live. */\n private readonly effectiveHoverSpeed = computed<number | undefined>(() => {\n const explicit = this.hoverSpeed();\n if (explicit !== undefined) return explicit;\n return this.pauseOnHover() ? 0 : undefined;\n });\n\n /** Target velocity in px/s, sign baked in. */\n private readonly targetVelocity = computed(() => {\n const s = this.speed();\n const sign = this.direction() === 'left' ? 1 : -1;\n return Math.abs(s) * sign * (s < 0 ? -1 : 1);\n });\n\n constructor() {\n if (!this.isBrowser) return;\n\n afterNextRender(() => {\n this.measure();\n this.watchResize();\n this.watchImageLoads();\n this.startLoop();\n });\n\n // Re-measure on input changes that affect layout (items, gap, height).\n effect(() => {\n this.items();\n this.gap();\n this.itemHeight();\n queueMicrotask(() => this.measure());\n });\n }\n\n protected onEnter(): void {\n if (this.effectiveHoverSpeed() !== undefined) this.isHovered.set(true);\n }\n protected onLeave(): void {\n if (this.effectiveHoverSpeed() !== undefined) this.isHovered.set(false);\n }\n protected isNodeItem = isNode;\n\n // Internals\n\n /** Read first sequence width + decide how many copies cover the viewport. */\n private measure(): void {\n const hostEl = this.host.nativeElement;\n const seqEl = hostEl.querySelector<HTMLElement>('.wr-marquee__list');\n if (!seqEl) return;\n const seqWidth = Math.ceil(seqEl.getBoundingClientRect().width);\n if (seqWidth <= 0) return;\n this.seqWidth.set(seqWidth);\n const containerWidth = hostEl.clientWidth || seqWidth;\n const copies = Math.max(MIN_COPIES, Math.ceil(containerWidth / seqWidth) + COPY_HEADROOM);\n this.copyCount.set(copies);\n }\n\n private watchResize(): void {\n const hostEl = this.host.nativeElement;\n if (typeof ResizeObserver === 'undefined') {\n const onResize = (): void => this.measure();\n window.addEventListener('resize', onResize);\n this.destroyRef.onDestroy(() => window.removeEventListener('resize', onResize));\n return;\n }\n const ro = new ResizeObserver(() => this.measure());\n ro.observe(hostEl);\n const seqEl = hostEl.querySelector<HTMLElement>('.wr-marquee__list');\n if (seqEl) ro.observe(seqEl);\n this.destroyRef.onDestroy(() => ro.disconnect());\n }\n\n private watchImageLoads(): void {\n const hostEl = this.host.nativeElement;\n const seqEl = hostEl.querySelector<HTMLElement>('.wr-marquee__list');\n if (!seqEl) return;\n const imgs = seqEl.querySelectorAll('img');\n if (imgs.length === 0) return;\n let remaining = imgs.length;\n const onDone = (): void => {\n remaining -= 1;\n if (remaining === 0) this.measure();\n };\n imgs.forEach(img => {\n if (img.complete) onDone();\n else {\n img.addEventListener('load', onDone, { once: true });\n img.addEventListener('error', onDone, { once: true });\n }\n });\n }\n\n private startLoop(): void {\n const track = this.trackEl().nativeElement;\n let raf = 0;\n let lastTs: number | null = null;\n let offset = 0;\n let velocity = 0;\n\n const tick = (ts: number): void => {\n lastTs ??= ts;\n const dt = Math.max(0, ts - lastTs) / 1000;\n lastTs = ts;\n\n const target = this.isHovered() ? (this.effectiveHoverSpeed() ?? this.targetVelocity()) : this.targetVelocity();\n const ease = 1 - Math.exp(-dt / SMOOTH_TAU);\n velocity += (target - velocity) * ease;\n\n const w = this.seqWidth();\n if (w > 0) {\n offset = (((offset + velocity * dt) % w) + w) % w;\n track.style.transform = `translate3d(${-offset}px, 0, 0)`;\n }\n raf = requestAnimationFrame(tick);\n };\n\n raf = requestAnimationFrame(tick);\n this.destroyRef.onDestroy(() => cancelAnimationFrame(raf));\n }\n}\n\nexport type { WrMarqueeImage, WrMarqueeNode, WrMarqueeItem } from './interfaces';\n","<div class=\"wr-marquee__track\" #track (mouseenter)=\"onEnter()\" (mouseleave)=\"onLeave()\">\n @for (copyIndex of copyIndices(); track copyIndex) {\n <!--\n `inert` alongside `aria-hidden`, and never one without the other: the copies\n exist only to fill the track, and an item with an `href` renders a real anchor\n in every one of them. Hidden from the screen reader but still in the tab order,\n each duplicate is a tab stop that announces nothing — axe's `aria-hidden-focus`,\n and N stops on one destination for anyone using the keyboard.\n -->\n <ul\n class=\"wr-marquee__list\"\n role=\"list\"\n [attr.aria-hidden]=\"copyIndex > 0\"\n [attr.inert]=\"copyIndex > 0 ? '' : null\"\n [attr.data-copy]=\"copyIndex\"\n >\n @for (item of items(); track $index) {\n <li class=\"wr-marquee__item\" role=\"listitem\">\n @if (item.href) {\n <a\n class=\"wr-marquee__link\"\n [href]=\"item.href\"\n [attr.aria-label]=\"\n (isNodeItem(item) ? item.ariaLabel || item.title : item.alt || item.title) || resolvedLinkLabel()\n \"\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n >\n <ng-container *ngTemplateOutlet=\"content; context: { $implicit: item }\" />\n </a>\n } @else {\n <ng-container *ngTemplateOutlet=\"content; context: { $implicit: item }\" />\n }\n </li>\n }\n </ul>\n }\n</div>\n\n<ng-template #content let-item>\n @if (isNodeItem(item)) {\n <span class=\"wr-marquee__node\" [attr.aria-hidden]=\"!!item.href && !item.ariaLabel\">\n <ng-container *ngTemplateOutlet=\"item.node\" />\n </span>\n } @else {\n <img\n [src]=\"item.src\"\n [srcset]=\"item.srcSet || null\"\n [sizes]=\"item.sizes || null\"\n [width]=\"item.width || null\"\n [height]=\"item.height || null\"\n [alt]=\"item.alt || ''\"\n [title]=\"item.title || null\"\n loading=\"lazy\"\n decoding=\"async\"\n draggable=\"false\"\n />\n }\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-marquee.mjs","sources":["../../../projects/lib/marquee/marquee.ts","../../../projects/lib/marquee/marquee.html","../../../projects/lib/marquee/ngwr-marquee.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n *\n * Angular adaptation of the LogoLoop effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/animations/logo-loop\n *\n * Renamed `wr-marquee` in ngwr — same rAF-driven infinite carousel,\n * supports image + template items, optional fade/scale/pause-on-hover.\n */\n\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { NgTemplateOutlet, isPlatformBrowser } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n ElementRef,\n PLATFORM_ID,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n signal,\n viewChild,\n} from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { numAttr } from 'ngwr/utils';\n\nimport type { WrMarqueeNode, WrMarqueeItem } from './interfaces';\n\nconst SMOOTH_TAU = 0.25;\nconst MIN_COPIES = 2;\nconst COPY_HEADROOM = 2;\n\nfunction isNode(item: WrMarqueeItem): item is WrMarqueeNode {\n return 'node' in item;\n}\n\n/**\n * Endless horizontal marquee. Auto-duplicates the items list until it\n * covers the container width, then animates the track with a smooth\n * rAF-driven scroll. Optional edge fades, scale-on-hover, and pause /\n * variable-speed hover behaviour.\n *\n * @example\n * ```html\n * <wr-marquee\n * [items]=\"logos\"\n * [speed]=\"80\"\n * [gap]=\"48\"\n * [itemHeight]=\"36\"\n * [fadeOut]=\"true\"\n * [scaleOnHover]=\"true\"\n * [pauseOnHover]=\"true\"\n * />\n * ```\n *\n * @see https://www.reactbits.dev/animations/logo-loop\n */\n@Component({\n selector: 'wr-marquee',\n templateUrl: './marquee.html',\n styleUrl: './marquee.scss',\n encapsulation: ViewEncapsulation.None,\n imports: [NgTemplateOutlet],\n host: {\n class: 'wr-marquee',\n '[class.wr-marquee--fade]': 'fadeOut()',\n '[class.wr-marquee--scale-hover]': 'scaleOnHover()',\n '[style.--wr-marquee-gap.px]': 'gap()',\n '[style.--wr-marquee-height.px]': 'itemHeight()',\n '[style.--wr-marquee-fade-color]': 'fadeOutColor() || null',\n '[attr.role]': '\"region\"',\n '[attr.aria-label]': 'resolvedAriaLabel()',\n },\n})\nexport class WrMarquee {\n /** Items to display. Each entry is either an image source or a TemplateRef node. */\n readonly items = input.required<readonly WrMarqueeItem[]>();\n\n /** Scroll speed in pixels per second. Negative reverses direction. @default 120 */\n readonly speed = input(120, { transform: numAttr(120) });\n\n /** Track direction. @default 'left' */\n readonly direction = input<'left' | 'right'>('left');\n\n /** Item height in pixels. @default 28 */\n readonly itemHeight = input(28, { transform: numAttr(28) });\n\n /** Gap between items in pixels. @default 32 */\n readonly gap = input(32, { transform: numAttr(32) });\n\n /** Pause the loop on hover. @default false */\n readonly pauseOnHover = input(false, { transform: coerceBooleanProperty });\n\n /** Custom speed (px/s) when hovered. Overrides `pauseOnHover`. */\n readonly hoverSpeed = input<number | undefined>(undefined, {\n transform: (v: unknown): number | undefined => (v == null || v === '' ? undefined : coerceNumberProperty(v, 0)),\n });\n\n /** Apply edge fade-out gradients. @default false */\n readonly fadeOut = input(false, { transform: coerceBooleanProperty });\n\n /** CSS colour for the fade-out gradient. Defaults to the page background. */\n readonly fadeOutColor = input<string>('');\n\n /** Scale individual items up on hover. @default false */\n readonly scaleOnHover = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Accessible name for the region. Falls back to `marquee.label`, then\n * `'Marquee'`.\n */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'marquee.label', 'Marquee');\n\n /**\n * Name used for an item that links somewhere but carries no text of its own.\n * Falls back to `marquee.link`, then `'link'` — a bare `'link'` in English was\n * what a screen reader announced for every unlabelled logo in the strip.\n */\n protected readonly resolvedLinkLabel = useI18nText(signal(null).asReadonly(), 'marquee.link', 'link');\n\n private readonly trackEl = viewChild.required<ElementRef<HTMLElement>>('track');\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n protected readonly seqWidth = signal(0);\n protected readonly copyCount = signal(MIN_COPIES);\n protected readonly isHovered = signal(false);\n\n protected readonly copyIndices = computed(() => Array.from({ length: this.copyCount() }, (_, i) => i));\n\n /** Resolved hover speed: `hoverSpeed` overrides, else 0 when `pauseOnHover`, else live. */\n private readonly effectiveHoverSpeed = computed<number | undefined>(() => {\n const explicit = this.hoverSpeed();\n if (explicit !== undefined) return explicit;\n return this.pauseOnHover() ? 0 : undefined;\n });\n\n /** Target velocity in px/s, sign baked in. */\n private readonly targetVelocity = computed(() => {\n const s = this.speed();\n const sign = this.direction() === 'left' ? 1 : -1;\n return Math.abs(s) * sign * (s < 0 ? -1 : 1);\n });\n\n constructor() {\n if (!this.isBrowser) return;\n\n afterNextRender(() => {\n this.measure();\n this.watchResize();\n this.watchImageLoads();\n this.startLoop();\n });\n\n // Re-measure on input changes that affect layout (items, gap, height).\n effect(() => {\n this.items();\n this.gap();\n this.itemHeight();\n queueMicrotask(() => this.measure());\n });\n }\n\n protected onEnter(): void {\n if (this.effectiveHoverSpeed() !== undefined) this.isHovered.set(true);\n }\n protected onLeave(): void {\n if (this.effectiveHoverSpeed() !== undefined) this.isHovered.set(false);\n }\n protected isNodeItem = isNode;\n\n // Internals\n\n /** Read first sequence width + decide how many copies cover the viewport. */\n private measure(): void {\n const hostEl = this.host.nativeElement;\n const seqEl = hostEl.querySelector<HTMLElement>('.wr-marquee__list');\n if (!seqEl) return;\n const seqWidth = Math.ceil(seqEl.getBoundingClientRect().width);\n if (seqWidth <= 0) return;\n this.seqWidth.set(seqWidth);\n const containerWidth = hostEl.clientWidth || seqWidth;\n const copies = Math.max(MIN_COPIES, Math.ceil(containerWidth / seqWidth) + COPY_HEADROOM);\n this.copyCount.set(copies);\n }\n\n private watchResize(): void {\n const hostEl = this.host.nativeElement;\n if (typeof ResizeObserver === 'undefined') {\n const onResize = (): void => this.measure();\n window.addEventListener('resize', onResize);\n this.destroyRef.onDestroy(() => window.removeEventListener('resize', onResize));\n return;\n }\n const ro = new ResizeObserver(() => this.measure());\n ro.observe(hostEl);\n const seqEl = hostEl.querySelector<HTMLElement>('.wr-marquee__list');\n if (seqEl) ro.observe(seqEl);\n this.destroyRef.onDestroy(() => ro.disconnect());\n }\n\n private watchImageLoads(): void {\n const hostEl = this.host.nativeElement;\n const seqEl = hostEl.querySelector<HTMLElement>('.wr-marquee__list');\n if (!seqEl) return;\n const imgs = seqEl.querySelectorAll('img');\n if (imgs.length === 0) return;\n let remaining = imgs.length;\n const onDone = (): void => {\n remaining -= 1;\n if (remaining === 0) this.measure();\n };\n imgs.forEach(img => {\n if (img.complete) onDone();\n else {\n img.addEventListener('load', onDone, { once: true });\n img.addEventListener('error', onDone, { once: true });\n }\n });\n }\n\n private startLoop(): void {\n const track = this.trackEl().nativeElement;\n let raf = 0;\n let lastTs: number | null = null;\n let offset = 0;\n let velocity = 0;\n\n const tick = (ts: number): void => {\n lastTs ??= ts;\n const dt = Math.max(0, ts - lastTs) / 1000;\n lastTs = ts;\n\n const target = this.isHovered() ? (this.effectiveHoverSpeed() ?? this.targetVelocity()) : this.targetVelocity();\n const ease = 1 - Math.exp(-dt / SMOOTH_TAU);\n velocity += (target - velocity) * ease;\n\n const w = this.seqWidth();\n if (w > 0) {\n offset = (((offset + velocity * dt) % w) + w) % w;\n track.style.transform = `translate3d(${-offset}px, 0, 0)`;\n }\n raf = requestAnimationFrame(tick);\n };\n\n raf = requestAnimationFrame(tick);\n this.destroyRef.onDestroy(() => cancelAnimationFrame(raf));\n }\n}\n\nexport type { WrMarqueeImage, WrMarqueeNode, WrMarqueeItem } from './interfaces';\n","<div class=\"wr-marquee__track\" #track (mouseenter)=\"onEnter()\" (mouseleave)=\"onLeave()\">\n @for (copyIndex of copyIndices(); track copyIndex) {\n <!--\n `inert` alongside `aria-hidden`, and never one without the other: the copies\n exist only to fill the track, and an item with an `href` renders a real anchor\n in every one of them. Hidden from the screen reader but still in the tab order,\n each duplicate is a tab stop that announces nothing — axe's `aria-hidden-focus`,\n and N stops on one destination for anyone using the keyboard.\n -->\n <ul\n class=\"wr-marquee__list\"\n role=\"list\"\n [attr.aria-hidden]=\"copyIndex > 0\"\n [attr.inert]=\"copyIndex > 0 ? '' : null\"\n [attr.data-copy]=\"copyIndex\"\n >\n @for (item of items(); track $index) {\n <li class=\"wr-marquee__item\" role=\"listitem\">\n @if (item.href) {\n <a\n class=\"wr-marquee__link\"\n [href]=\"item.href\"\n [attr.aria-label]=\"\n (isNodeItem(item) ? item.ariaLabel || item.title : item.alt || item.title) || resolvedLinkLabel()\n \"\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n >\n <ng-container *ngTemplateOutlet=\"content; context: { $implicit: item }\" />\n </a>\n } @else {\n <ng-container *ngTemplateOutlet=\"content; context: { $implicit: item }\" />\n }\n </li>\n }\n </ul>\n }\n</div>\n\n<ng-template #content let-item>\n @if (isNodeItem(item)) {\n <span class=\"wr-marquee__node\" [attr.aria-hidden]=\"!!item.href && !item.ariaLabel\">\n <ng-container *ngTemplateOutlet=\"item.node\" />\n </span>\n } @else {\n <img\n [src]=\"item.src\"\n [srcset]=\"item.srcSet || null\"\n [sizes]=\"item.sizes || null\"\n [width]=\"item.width || null\"\n [height]=\"item.height || null\"\n [alt]=\"item.alt || ''\"\n [title]=\"item.title || null\"\n loading=\"lazy\"\n decoding=\"async\"\n draggable=\"false\"\n />\n }\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/fesm2022/ngwr-media.mjs
CHANGED
|
@@ -97,10 +97,10 @@ class WrMedia {
|
|
|
97
97
|
this.destroyRef.onDestroy(() => mql.removeEventListener('change', handler));
|
|
98
98
|
return sig.asReadonly();
|
|
99
99
|
}
|
|
100
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
101
|
-
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.
|
|
100
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMedia, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
101
|
+
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.7", ngImport: i0, type: WrMedia });
|
|
102
102
|
}
|
|
103
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
103
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMedia, decorators: [{
|
|
104
104
|
type: Service
|
|
105
105
|
}] });
|
|
106
106
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-media.mjs","sources":["../../../projects/lib/media/wr-breakpoints.ts","../../../projects/lib/media/wr-media.ts","../../../projects/lib/media/provide-wr-media.ts","../../../projects/lib/media/ngwr-media.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\nimport type { WrBreakpointMap } from './interfaces';\n\n/** Defaults mirror `projects/lib/styles/_breakpoints.scss`. */\nexport const DEFAULT_WR_BREAKPOINTS: WrBreakpointMap = Object.freeze({\n xs: 0,\n sm: 576,\n md: 768,\n lg: 992,\n xl: 1200,\n xxl: 1400,\n xga: 1600,\n fhd: 1920,\n rt: 2560,\n});\n\n/** DI token for the breakpoint map — override via {@link provideWrMedia}. */\nexport const WR_BREAKPOINTS = new InjectionToken<WrBreakpointMap>('WR_BREAKPOINTS', {\n factory: () => DEFAULT_WR_BREAKPOINTS,\n});\n\nexport type { WrBreakpoint, WrBreakpointMap } from './interfaces';\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { isPlatformBrowser } from '@angular/common';\nimport { DestroyRef, Service, PLATFORM_ID, type Signal, computed, inject, signal } from '@angular/core';\n\nimport { WR_BREAKPOINTS, type WrBreakpoint } from './wr-breakpoints';\n\n/**\n * Reactive viewport queries backed by signals.\n *\n * Each query subscribes lazily — `matches(...)` caches its signal so\n * repeated calls with the same query share a single `matchMedia` listener.\n * SSR-safe: on the server every signal stays `false`.\n *\n * @example\n * ```ts\n * private readonly media = inject(WrMedia);\n * protected readonly isMd = this.media.matches('md');\n * protected readonly isWide = this.media.matches('(min-width: 1200px)');\n * ```\n *\n * @see https://ngwr.dev/reference/services/media\n */\n@Service()\nexport class WrMedia {\n private readonly breakpoints = inject(WR_BREAKPOINTS);\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n /** Cache of query → signal so we share `matchMedia` listeners. */\n private readonly cache = new Map<string, Signal<boolean>>();\n\n /** Active breakpoint key — `xs` / `sm` / `md` / `lg` / `xl` / `xxl`. */\n readonly current = computed<WrBreakpoint>(() => {\n // Walk from largest to smallest, return the first one that matches.\n const ordered: readonly WrBreakpoint[] = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];\n for (const key of ordered) {\n if (this.matches(key)()) return key;\n }\n return 'xs';\n });\n\n /**\n * Returns a signal that tracks the given query.\n *\n * - Named breakpoint (`'md'`) → `(min-width: <px>)`.\n * - Raw query (`'(prefers-color-scheme: dark)'`) → passed through unchanged.\n */\n matches(query: WrBreakpoint | (string & {})): Signal<boolean> {\n const resolved = this.resolveQuery(query);\n const cached = this.cache.get(resolved);\n if (cached) return cached;\n const sig = this.createSignal(resolved);\n this.cache.set(resolved, sig);\n return sig;\n }\n\n private resolveQuery(query: WrBreakpoint | (string & {})): string {\n if (query in this.breakpoints) {\n const px = this.breakpoints[query as WrBreakpoint];\n return px === 0 ? '(min-width: 0px)' : `(min-width: ${px}px)`;\n }\n return query;\n }\n\n private createSignal(query: string): Signal<boolean> {\n if (!this.isBrowser) return signal(false).asReadonly();\n const mql = window.matchMedia(query);\n const sig = signal(mql.matches);\n const handler = (event: MediaQueryListEvent): void => sig.set(event.matches);\n mql.addEventListener('change', handler);\n this.destroyRef.onDestroy(() => mql.removeEventListener('change', handler));\n return sig.asReadonly();\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\nimport { DEFAULT_WR_BREAKPOINTS, WR_BREAKPOINTS, type WrBreakpointMap } from './wr-breakpoints';\n\n/**\n * Override the breakpoint map used by {@link WrMedia}. Partial maps\n * are merged with {@link DEFAULT_WR_BREAKPOINTS}, so you only need to list\n * the breakpoints you want to change.\n *\n * @example\n * ```ts\n * bootstrapApplication(AppComponent, {\n * providers: [provideWrMedia({ md: 720, lg: 1024 })],\n * });\n * ```\n */\nexport function provideWrMedia(breakpoints: Partial<WrBreakpointMap> = {}): EnvironmentProviders {\n const merged: WrBreakpointMap = { ...DEFAULT_WR_BREAKPOINTS, ...breakpoints };\n return makeEnvironmentProviders([{ provide: WR_BREAKPOINTS, useValue: merged }]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ngwr-media.mjs","sources":["../../../projects/lib/media/wr-breakpoints.ts","../../../projects/lib/media/wr-media.ts","../../../projects/lib/media/provide-wr-media.ts","../../../projects/lib/media/ngwr-media.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\nimport type { WrBreakpointMap } from './interfaces';\n\n/** Defaults mirror `projects/lib/styles/_breakpoints.scss`. */\nexport const DEFAULT_WR_BREAKPOINTS: WrBreakpointMap = Object.freeze({\n xs: 0,\n sm: 576,\n md: 768,\n lg: 992,\n xl: 1200,\n xxl: 1400,\n xga: 1600,\n fhd: 1920,\n rt: 2560,\n});\n\n/** DI token for the breakpoint map — override via {@link provideWrMedia}. */\nexport const WR_BREAKPOINTS = new InjectionToken<WrBreakpointMap>('WR_BREAKPOINTS', {\n factory: () => DEFAULT_WR_BREAKPOINTS,\n});\n\nexport type { WrBreakpoint, WrBreakpointMap } from './interfaces';\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { isPlatformBrowser } from '@angular/common';\nimport { DestroyRef, Service, PLATFORM_ID, type Signal, computed, inject, signal } from '@angular/core';\n\nimport { WR_BREAKPOINTS, type WrBreakpoint } from './wr-breakpoints';\n\n/**\n * Reactive viewport queries backed by signals.\n *\n * Each query subscribes lazily — `matches(...)` caches its signal so\n * repeated calls with the same query share a single `matchMedia` listener.\n * SSR-safe: on the server every signal stays `false`.\n *\n * @example\n * ```ts\n * private readonly media = inject(WrMedia);\n * protected readonly isMd = this.media.matches('md');\n * protected readonly isWide = this.media.matches('(min-width: 1200px)');\n * ```\n *\n * @see https://ngwr.dev/reference/services/media\n */\n@Service()\nexport class WrMedia {\n private readonly breakpoints = inject(WR_BREAKPOINTS);\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n /** Cache of query → signal so we share `matchMedia` listeners. */\n private readonly cache = new Map<string, Signal<boolean>>();\n\n /** Active breakpoint key — `xs` / `sm` / `md` / `lg` / `xl` / `xxl`. */\n readonly current = computed<WrBreakpoint>(() => {\n // Walk from largest to smallest, return the first one that matches.\n const ordered: readonly WrBreakpoint[] = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];\n for (const key of ordered) {\n if (this.matches(key)()) return key;\n }\n return 'xs';\n });\n\n /**\n * Returns a signal that tracks the given query.\n *\n * - Named breakpoint (`'md'`) → `(min-width: <px>)`.\n * - Raw query (`'(prefers-color-scheme: dark)'`) → passed through unchanged.\n */\n matches(query: WrBreakpoint | (string & {})): Signal<boolean> {\n const resolved = this.resolveQuery(query);\n const cached = this.cache.get(resolved);\n if (cached) return cached;\n const sig = this.createSignal(resolved);\n this.cache.set(resolved, sig);\n return sig;\n }\n\n private resolveQuery(query: WrBreakpoint | (string & {})): string {\n if (query in this.breakpoints) {\n const px = this.breakpoints[query as WrBreakpoint];\n return px === 0 ? '(min-width: 0px)' : `(min-width: ${px}px)`;\n }\n return query;\n }\n\n private createSignal(query: string): Signal<boolean> {\n if (!this.isBrowser) return signal(false).asReadonly();\n const mql = window.matchMedia(query);\n const sig = signal(mql.matches);\n const handler = (event: MediaQueryListEvent): void => sig.set(event.matches);\n mql.addEventListener('change', handler);\n this.destroyRef.onDestroy(() => mql.removeEventListener('change', handler));\n return sig.asReadonly();\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\nimport { DEFAULT_WR_BREAKPOINTS, WR_BREAKPOINTS, type WrBreakpointMap } from './wr-breakpoints';\n\n/**\n * Override the breakpoint map used by {@link WrMedia}. Partial maps\n * are merged with {@link DEFAULT_WR_BREAKPOINTS}, so you only need to list\n * the breakpoints you want to change.\n *\n * @example\n * ```ts\n * bootstrapApplication(AppComponent, {\n * providers: [provideWrMedia({ md: 720, lg: 1024 })],\n * });\n * ```\n */\nexport function provideWrMedia(breakpoints: Partial<WrBreakpointMap> = {}): EnvironmentProviders {\n const merged: WrBreakpointMap = { ...DEFAULT_WR_BREAKPOINTS, ...breakpoints };\n return makeEnvironmentProviders([{ provide: WR_BREAKPOINTS, useValue: merged }]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;AAAA;;;;;AAKG;AAMH;AACO,MAAM,sBAAsB,GAAoB,MAAM,CAAC,MAAM,CAAC;AACnE,IAAA,EAAE,EAAE,CAAC;AACL,IAAA,EAAE,EAAE,GAAG;AACP,IAAA,EAAE,EAAE,GAAG;AACP,IAAA,EAAE,EAAE,GAAG;AACP,IAAA,EAAE,EAAE,IAAI;AACR,IAAA,GAAG,EAAE,IAAI;AACT,IAAA,GAAG,EAAE,IAAI;AACT,IAAA,GAAG,EAAE,IAAI;AACT,IAAA,EAAE,EAAE,IAAI;AACT,CAAA;AAED;MACa,cAAc,GAAG,IAAI,cAAc,CAAkB,gBAAgB,EAAE;AAClF,IAAA,OAAO,EAAE,MAAM,sBAAsB;AACtC,CAAA;;AC3BD;;;;;AAKG;AAOH;;;;;;;;;;;;;;;AAeG;MAEU,OAAO,CAAA;AACD,IAAA,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC;AACpC,IAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IAC/B,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;;AAGlD,IAAA,KAAK,GAAG,IAAI,GAAG,EAA2B;;AAGlD,IAAA,OAAO,GAAG,QAAQ,CAAe,MAAK;;AAE7C,QAAA,MAAM,OAAO,GAA4B,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;AAC9E,QAAA,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE;AACzB,YAAA,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;AAAE,gBAAA,OAAO,GAAG;QACrC;AACA,QAAA,OAAO,IAAI;IACb,CAAC;gFAAC;AAEF;;;;;AAKG;AACH,IAAA,OAAO,CAAC,KAAmC,EAAA;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;AACvC,QAAA,IAAI,MAAM;AAAE,YAAA,OAAO,MAAM;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;QACvC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC;AAC7B,QAAA,OAAO,GAAG;IACZ;AAEQ,IAAA,YAAY,CAAC,KAAmC,EAAA;AACtD,QAAA,IAAI,KAAK,IAAI,IAAI,CAAC,WAAW,EAAE;YAC7B,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,KAAqB,CAAC;AAClD,YAAA,OAAO,EAAE,KAAK,CAAC,GAAG,kBAAkB,GAAG,CAAA,YAAA,EAAe,EAAE,KAAK;QAC/D;AACA,QAAA,OAAO,KAAK;IACd;AAEQ,IAAA,YAAY,CAAC,KAAa,EAAA;QAChC,IAAI,CAAC,IAAI,CAAC,SAAS;AAAE,YAAA,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE;QACtD,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC;AACpC,QAAA,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO;gFAAC;AAC/B,QAAA,MAAM,OAAO,GAAG,CAAC,KAA0B,KAAW,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;AAC5E,QAAA,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC;AACvC,QAAA,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC3E,QAAA,OAAO,GAAG,CAAC,UAAU,EAAE;IACzB;uGAjDW,OAAO,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,OAAA,EAAA,CAAA;wGAAP,OAAO,EAAA,CAAA;;2FAAP,OAAO,EAAA,UAAA,EAAA,CAAA;kBADnB;;;AC5BD;;;;;AAKG;AAMH;;;;;;;;;;;AAWG;AACG,SAAU,cAAc,CAAC,WAAA,GAAwC,EAAE,EAAA;IACvE,MAAM,MAAM,GAAoB,EAAE,GAAG,sBAAsB,EAAE,GAAG,WAAW,EAAE;AAC7E,IAAA,OAAO,wBAAwB,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;AAClF;;AC1BA;;AAEG;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-mention-testing.mjs","sources":["../../../projects/lib/mention/testing/wr-mention-option-harness.ts","../../../projects/lib/mention/testing/wr-mention-harness.ts","../../../projects/lib/mention/testing/ngwr-mention-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrMentionOptionHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one suggestion inside a `[wrMention]` panel.\n *\n * Reach these through {@link WrMentionHarness.getOptions}, never off the document:\n * the panel is a portal in the shared overlay container, so a bare query answers\n * with whichever mention field opened first.\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrMentionOptionHarness extends ComponentHarness {\n /** One `<li role=\"option\">` in the panel's listbox. */\n static hostSelector = '.wr-mention-panel__option';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrMentionOptionHarnessFilters = {}): HarnessPredicate<WrMentionOptionHarness> {\n return new HarnessPredicate(WrMentionOptionHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption('active', options.active, async (harness, active) => (await harness.isActive()) === active);\n }\n\n /** The suggestion's label — whatever `displayWith` returned for the item. */\n async getText(): Promise<string> {\n return (await this.host()).text();\n }\n\n /**\n * Whether the cursor is on this suggestion.\n *\n * Read from `aria-selected` rather than from the `--active` modifier class. The\n * two are set from the same index, so they agree — but the ARIA state is what a\n * screen reader is told, and the class is only what the highlight is painted\n * with.\n */\n async isActive(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-selected')) === 'true';\n }\n\n /**\n * The suggestion's id — the value the field names in `aria-activedescendant`.\n *\n * The only link between the two DOM trees: the panel is in the overlay\n * container, not inside the field, so this id is how the reference reaches it\n * at all.\n */\n async getId(): Promise<string | null> {\n return (await this.host()).getAttribute('id');\n }\n\n /**\n * Pick this suggestion with the mouse.\n *\n * Needs no layout, so it works here: the panel commits on `mousedown` —\n * deliberately, so the field never loses focus and the caret stays put — and\n * the CDK's no-arg click dispatches `mousedown` at the head of its sequence\n * without any hit test.\n */\n async click(): Promise<void> {\n return (await this.host()).click();\n }\n\n /**\n * Move the pointer onto this suggestion, which makes it the active one.\n *\n * Hover and the arrow keys drive the same single cursor, so a spec can assert\n * the mouse path through {@link WrMentionHarness.getActiveOptionLabel} exactly\n * as it does the keyboard one.\n */\n async hover(): Promise<void> {\n return (await this.host()).hover();\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport {\n ComponentHarness,\n type HarnessLoader,\n HarnessPredicate,\n TestKey,\n type TestElement,\n} from '@angular/cdk/testing';\n\nimport type { WrMentionHarnessFilters, WrMentionOptionHarnessFilters } from './interfaces';\nimport { WrMentionOptionHarness } from './wr-mention-option-harness';\n\n/** How long {@link WrMentionHarness.blur} waits for the deferred close, in ms. Generous next to the 120ms grace period. */\nconst CLOSE_TIMEOUT = 1000;\n\n/** How often it re-checks, in ms. */\nconst POLL_STEP = 10;\n\n/**\n * The real `setTimeout`, captured at module load — before a spec can install fake\n * timers.\n *\n * {@link WrMentionHarness.blur} has to let REAL time pass: the close on blur is a\n * plain `setTimeout` inside the directive, and under zoneless change detection\n * `whenStable()` resolves without waiting for a macrotask. Reading a faked global\n * instead would mean the sleep never fires and the harness hangs where it should\n * fail with the message it prepared.\n */\nconst realSetTimeout = globalThis.setTimeout;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => {\n realSetTimeout(resolve, ms);\n });\n}\n\n/**\n * Test harness for `[wrMention]` — the field, and the suggestion panel it opens\n * at the caret.\n *\n * The harness matches the FIELD, because that is the only part of a mention in the\n * fixture: the panel is a `ComponentPortal` in the shared overlay container, a\n * sibling of the whole app. Suggestions are read through the document root, scoped\n * by the id the field publishes as `aria-controls` — which is what keeps two\n * mention fields on one page from answering with each other's suggestions.\n *\n * **The field is not a combobox, and it has no `aria-expanded`.** Both are\n * deliberate (the source spells out why: it holds prose, `role=\"combobox\"` is\n * disallowed on `<textarea>` and would drop `aria-multiline`, and `aria-expanded`\n * is not a supported state of `role=\"textbox\"`), and both shape this harness.\n * `aria-controls` is STATIC — it names the listbox even while there is no listbox —\n * so {@link isOpen} asks whether the listbox EXISTS instead of reading a state\n * flag, and \"a list appeared\" reaches a screen reader through the live region that\n * {@link getStatusMessage} reads.\n *\n * **The panel's POSITION is not testable in jsdom, so nothing here reports it.**\n * `caret.ts` places the panel by measuring a mirror `<div>` with\n * `getBoundingClientRect`, and jsdom has no layout: every rect is zeros, so the\n * measurement collapses to the origin. The overlay still lands on finite numbers —\n * `line-height` computes to `normal` there, so `caret.ts` falls back to\n * `font-size * 1.2` and the pane is offset by that alone (a measured\n * `margin-top: 23.2px`, `margin-left: 0px` on a default fixture) — which is\n * precisely the problem: every one of them is an artefact of the environment\n * rather than a fact about the component. Assert placement in a real browser.\n *\n * @example\n * ```ts\n * const mention = await loader.getHarness(WrMentionHarness);\n *\n * await mention.type('hey @al');\n * expect(await mention.getOptionLabels()).toEqual(['Alan Turing', 'Linus Torvalds']);\n *\n * await mention.commit();\n * expect(await mention.getValue()).toBe('hey @Alan Turing ');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrMentionHarness extends ComponentHarness {\n /** The directive's host. Both tags, since the directive drives either one. */\n static hostSelector = 'input[wrMention], textarea[wrMention]';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrMentionHarnessFilters = {}): HarnessPredicate<WrMentionHarness> {\n return new HarnessPredicate(WrMentionHarness, options)\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('placeholder', options.placeholder, (harness, placeholder) =>\n HarnessPredicate.stringMatches(harness.getPlaceholder(), placeholder)\n )\n .addOption('open', options.open, async (harness, open) => (await harness.isOpen()) === open);\n }\n\n /** The field's whole text, mention fragments and prose alike. */\n async getValue(): Promise<string> {\n return (await this.host()).getProperty<string>('value');\n }\n\n /** The field's placeholder, or `''` when it has none. */\n async getPlaceholder(): Promise<string> {\n return (await this.host()).getProperty<string>('placeholder');\n }\n\n /**\n * Type into the field one keystroke at a time, APPENDING to whatever is there.\n *\n * The realistic path, and the one that exercises detection the way a person\n * does: the directive re-reads the text before the caret on every `input`, so\n * `type('hey @al')` opens the panel on the `@` and then narrows it twice.\n *\n * Focuses the field, which BLURS whatever was focused before — and a blurred\n * mention field closes its own panel. Use {@link setValue} when a spec needs two\n * fields suggesting at the same time.\n */\n async type(text: string): Promise<void> {\n return (await this.host()).sendKeys(text);\n }\n\n /**\n * Replace the field's whole text and let the directive detect once.\n *\n * The `input` event is dispatched by hand rather than through keystrokes so the\n * field is never focused — which is the only way to get two mention panels open\n * at once, since focusing one field blurs the other and blur closes its panel.\n * The caret lands at the end of the new text, where detection looks.\n */\n async setValue(text: string): Promise<void> {\n const host = await this.host();\n await host.setInputValue(text);\n await host.dispatchEvent('input');\n }\n\n /** Empty the field. The panel goes with it — there is no trigger char left to detect. */\n async clear(): Promise<void> {\n return (await this.host()).clear();\n }\n\n /**\n * Click the field.\n *\n * Re-opens the suggestions when the caret is still sitting inside a mention —\n * the directive re-detects on click, which is how a user who pressed Escape (or\n * clicked away and back) gets the panel again without retyping. Needs no layout:\n * the CDK dispatches the event on the element rather than hit-testing a point.\n */\n async click(): Promise<void> {\n return (await this.host()).click();\n }\n\n /**\n * Whether the panel is offering suggestions.\n *\n * Asks whether this field's listbox exists, because the field publishes no\n * open/closed state to read: `aria-expanded` is not a supported state of\n * `role=\"textbox\"` (axe rates it a critical violation), and `aria-controls` is\n * static here, so its presence says nothing.\n */\n async isOpen(): Promise<boolean> {\n return (await this.listboxOrNull()) !== null;\n }\n\n /**\n * The field's `aria-autocomplete` — `'list'`, permanently.\n *\n * A capability, not a state: ARIA says authors should not toggle it to signal\n * that suggestions are showing, and being static is what makes the feature\n * discoverable on focus, before a trigger char is ever typed. {@link isOpen}\n * answers the state.\n */\n async getAutocomplete(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-autocomplete');\n }\n\n /**\n * The role the field PROMISES its popup will announce, from `aria-haspopup` —\n * `'listbox'`. Required alongside `aria-autocomplete=\"list\"`, and it has to name\n * the popup's real role, which {@link getListboxRole} reads back off the panel.\n */\n async getPopupRole(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-haspopup');\n }\n\n /** The role the open panel announces — `'listbox'`. Throws while the panel is closed. */\n async getListboxRole(): Promise<string | null> {\n return (await this.listbox()).getAttribute('role');\n }\n\n /**\n * The panel's accessible name.\n *\n * ARIA requires a `role=\"listbox\"` to have one, and a consumer never renders the\n * panel themselves — so the directive supplies it from the i18n catalog\n * (`mention.listbox`).\n */\n async getListboxLabel(): Promise<string | null> {\n return (await this.listbox()).getAttribute('aria-label');\n }\n\n /**\n * The suggestions on offer, in DOM order.\n *\n * The whole list a user can reach, not a window onto a longer one: the directive\n * slices the matches at `maxResults` (8 by default) before rendering, which is\n * why this panel needs no virtualisation.\n *\n * Throws while the panel is closed — the options do not exist anywhere until the\n * portal is attached, and an empty array reads like a panel that rendered\n * nothing.\n */\n async getOptions(filters: WrMentionOptionHarnessFilters = {}): Promise<WrMentionOptionHarness[]> {\n const loader = await this.listboxLoader();\n return loader.getAllHarnesses(WrMentionOptionHarness.with(filters));\n }\n\n /** The labels of the suggestions on offer, in DOM order. */\n async getOptionLabels(): Promise<string[]> {\n const options = await this.getOptions();\n return Promise.all(options.map(option => option.getText()));\n }\n\n /**\n * The id the field names in `aria-activedescendant`, or `null` when it names\n * nothing.\n *\n * The strict half of the field's two references. `aria-controls` is allowed to\n * dangle (see {@link isOpen}); this one is not — naming an element that is not\n * in the document is an author error no browser reports, it simply stops\n * announcing options. So `null` is the CORRECT answer while the panel is closed,\n * and worth asserting there.\n */\n async getActiveOptionId(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-activedescendant');\n }\n\n /**\n * The label a screen reader announces as the active suggestion.\n *\n * Resolved through `aria-activedescendant` the way an assistive technology\n * would, rather than by hunting for the active class — which is why it throws on\n * a reference that resolves to nothing instead of quietly reporting what the\n * class says.\n */\n async getActiveOptionLabel(): Promise<string> {\n const active = await this.activeOption('getActiveOptionLabel');\n return active.text();\n }\n\n /**\n * Where the active suggestion sits in {@link getOptions}, 0-based.\n *\n * Matched by id rather than counted by class, so the answer is the one\n * `aria-activedescendant` actually points at.\n */\n async getActiveOptionIndex(): Promise<number> {\n const id = await this.requireActiveOptionId('getActiveOptionIndex');\n const options = await this.getOptions();\n\n for (let index = 0; index < options.length; index++) {\n if ((await options[index].getId()) === id) return index;\n }\n\n throw new Error(\n `WrMentionHarness.getActiveOptionIndex(): aria-activedescendant names \"${id}\", which is not one of ` +\n `the ${options.length} suggestions on offer.`\n );\n }\n\n /**\n * Move the cursor down one suggestion, wrapping past the last back to the first.\n *\n * ArrowDown — and `aria-activedescendant` is the whole navigation model here,\n * because the options are never focused: focus stays in the field so the user can\n * keep typing the query.\n *\n * Throws while the panel is closed, where ArrowDown belongs to the field.\n */\n async nextOption(): Promise<void> {\n await this.requireOpen('nextOption');\n return (await this.host()).sendKeys(TestKey.DOWN_ARROW);\n }\n\n /** Move the cursor up one suggestion, wrapping past the first round to the last. */\n async previousOption(): Promise<void> {\n await this.requireOpen('previousOption');\n return (await this.host()).sendKeys(TestKey.UP_ARROW);\n }\n\n /**\n * Insert the active suggestion with Enter.\n *\n * The trigger char and the query are replaced by `valueWith(item, trigger)`\n * (`@Label` by default) plus a trailing space, the caret lands after that space,\n * the panel closes and `(wrMentionSelected)` fires with the item, the trigger\n * char and the query.\n *\n * Throws while the panel is closed — Enter is the field's own key there (a\n * newline in a `<textarea>`), and swallowing it would be the bug.\n */\n async commit(): Promise<void> {\n await this.requireOpen('commit');\n return (await this.host()).sendKeys(TestKey.ENTER);\n }\n\n /**\n * Insert the active suggestion with Tab instead.\n *\n * The same commit: the directive takes Tab as a second confirm key and\n * `preventDefault`s it, so focus stays in the field rather than leaving for the\n * next control.\n */\n async commitWithTab(): Promise<void> {\n await this.requireOpen('commitWithTab');\n return (await this.host()).sendKeys(TestKey.TAB);\n }\n\n /**\n * Pick the first suggestion matching the filters, with the mouse.\n *\n * Opens nothing by itself — type a trigger char first. Names what the panel does\n * offer when nothing matches, since a silent no-op here surfaces as an\n * unexplained unchanged field further down the spec.\n */\n async pick(filters: WrMentionOptionHarnessFilters): Promise<void> {\n const [option] = await this.getOptions(filters);\n if (!option) {\n const offered = await this.getOptionLabels();\n throw new Error(\n `WrMentionHarness.pick(): no suggestion matched ${JSON.stringify(filters)}. ` +\n `The panel offers: ${offered.join(', ')}.`\n );\n }\n\n return option.click();\n }\n\n /**\n * Dismiss the panel with Escape, leaving the typed text — trigger char and\n * query included — exactly as it is. A closed panel is left alone.\n *\n * Sends the WHOLE keystroke, keydown and keyup, as a real Escape is. That\n * matters here: the caret has not moved, so a keyup the directive re-detected on\n * would reopen the panel this just closed, and a harness that dispatched only\n * the keydown would report a dismissal the user never got.\n */\n async dismiss(): Promise<void> {\n if (!(await this.isOpen())) return;\n return (await this.host()).sendKeys(TestKey.ESCAPE);\n }\n\n /**\n * Blur the field and wait for the panel to go.\n *\n * The close is deferred by 120ms so a `mousedown` on a suggestion lands first,\n * and that is a real `setTimeout`: under zoneless change detection nothing\n * flushes it for us, so this waits out REAL time. A spec on fake timers should\n * drive its own clock and read {@link isOpen} instead of calling this.\n */\n async blur(timeout = CLOSE_TIMEOUT): Promise<void> {\n await (await this.host()).blur();\n\n for (let waited = 0; waited <= timeout; waited += POLL_STEP) {\n if (!(await this.isOpen())) return;\n\n await sleep(POLL_STEP);\n await this.forceStabilize();\n }\n\n throw new Error(\n `WrMentionHarness.blur(): the panel is still showing ${timeout}ms after the blur. The close is ` +\n 'deferred so that a click on a suggestion lands first, and this wait uses real time — a spec on ' +\n 'fake timers has to advance the clock itself and read isOpen().'\n );\n }\n\n /**\n * What the live region currently says.\n *\n * This is the announcement channel the field has INSTEAD of ARIA state, and\n * three things reach a screen reader only through it: how many suggestions\n * appeared, that NOTHING matched — the panel closes in that case, so there is no\n * DOM left to inspect and no state on the field to read — and what was inserted\n * on commit.\n *\n * Throws when more than one mention field is mounted. The region is parked on\n * `<body>` rather than in the panel (which is disposed on every keystroke that\n * leaves a mention, and a live region has to pre-exist its own text to be\n * announced at all), and it carries no id tying it to a field — so with two\n * fields around, the harness genuinely cannot tell which region is this one's.\n * Answering with whichever came first would be worse than refusing: assert\n * announcements in a fixture holding a single mention field.\n */\n async getStatusMessage(): Promise<string> {\n const regions = await this.documentRootLocatorFactory().locatorForAll('.wr-mention__status')();\n\n if (regions.length === 0) {\n throw new Error(\n 'WrMentionHarness.getStatusMessage(): no mention live region is in the document. The directive ' +\n 'appends it to <body> in the browser only, so there is none under SSR.'\n );\n }\n if (regions.length > 1) {\n throw new Error(\n `WrMentionHarness.getStatusMessage(): ${regions.length} mention live regions are in the document ` +\n 'and none of them names the field it belongs to, so this cannot say which one is yours. Assert ' +\n 'the announcement in a fixture with a single mention field.'\n );\n }\n\n return regions[0].text();\n }\n\n /** Move keyboard focus to the field. Focusing alone opens nothing — the panel needs a trigger char. */\n async focus(): Promise<void> {\n return (await this.host()).focus();\n }\n\n /** Whether the field holds keyboard focus. It keeps it while the panel is open, by design. */\n async isFocused(): Promise<boolean> {\n return (await this.host()).isFocused();\n }\n\n /**\n * A loader scoped to THIS field's listbox.\n *\n * Scoped by the id the field publishes as `aria-controls`, never by\n * `.wr-mention-panel`: the overlay container is shared by every mention on the\n * page, so a class query would answer with whichever field opened first.\n */\n private async listboxLoader(): Promise<HarnessLoader> {\n // Resolve the element first, so a closed panel fails with the message below\n // rather than with the CDK's generic \"expected to find element\" error.\n await this.listbox();\n return this.documentRootLocatorFactory().harnessLoaderFor(`#${await this.listboxId()}`);\n }\n\n /** This field's listbox in the overlay. Throws when the panel is closed. */\n private async listbox(): Promise<TestElement> {\n const listbox = await this.listboxOrNull();\n if (!listbox) {\n throw new Error(\n 'WrMentionHarness: no suggestions are showing, so there is nothing inside the panel to read. It ' +\n 'exists only while the caret sits after a trigger char that matched something — type() one in ' +\n 'first. (The field publishes aria-controls whether or not the panel is up, so that attribute is ' +\n 'not the check.)'\n );\n }\n return listbox;\n }\n\n /** This field's listbox, or `null` when the panel is closed. */\n private async listboxOrNull(): Promise<TestElement | null> {\n return this.documentRootLocatorFactory().locatorForOptional(`#${await this.listboxId()}`)();\n }\n\n /** The id the field names in `aria-controls`, which its listbox carries. */\n private async listboxId(): Promise<string> {\n const id = await (await this.host()).getAttribute('aria-controls');\n if (!id) {\n throw new Error(\n 'WrMentionHarness: this field publishes no aria-controls. That is a static host binding on ' +\n '[wrMention] — rendered even under SSR — so the element matched here is probably not a mention ' +\n 'field at all.'\n );\n }\n return id;\n }\n\n /** The element `aria-activedescendant` points at, or a targeted failure. */\n private async activeOption(method: string): Promise<TestElement> {\n const id = await this.requireActiveOptionId(method);\n const active = await this.documentRootLocatorFactory().locatorForOptional(`#${id}`)();\n\n if (!active) {\n throw new Error(\n `WrMentionHarness.${method}(): aria-activedescendant names \"${id}\", which is not in the document. ` +\n 'A dangling reference stops a screen reader announcing options and no browser reports it, so it ' +\n 'is a real failure rather than a harness one.'\n );\n }\n return active;\n }\n\n /** The active option's id, or a targeted failure. */\n private async requireActiveOptionId(method: string): Promise<string> {\n const id = await this.getActiveOptionId();\n if (!id) {\n throw new Error(\n `WrMentionHarness.${method}(): the field names no active suggestion. That is the correct state ` +\n 'while the panel is closed — check isOpen() first.'\n );\n }\n return id;\n }\n\n /** Refuse a keystroke that would mean something else entirely with the panel closed. */\n private async requireOpen(method: string): Promise<void> {\n if (await this.isOpen()) return;\n\n throw new Error(\n `WrMentionHarness.${method}(): no suggestions are showing, so this key belongs to the field itself ` +\n '— Enter is a newline, Tab leaves for the next control, and the arrows move the caret. Type a ' +\n 'trigger char first.'\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-mention-testing.mjs","sources":["../../../projects/lib/mention/testing/wr-mention-option-harness.ts","../../../projects/lib/mention/testing/wr-mention-harness.ts","../../../projects/lib/mention/testing/ngwr-mention-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrMentionOptionHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one suggestion inside a `[wrMention]` panel.\n *\n * Reach these through {@link WrMentionHarness.getOptions}, never off the document:\n * the panel is a portal in the shared overlay container, so a bare query answers\n * with whichever mention field opened first.\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrMentionOptionHarness extends ComponentHarness {\n /** One `<li role=\"option\">` in the panel's listbox. */\n static hostSelector = '.wr-mention-panel__option';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrMentionOptionHarnessFilters = {}): HarnessPredicate<WrMentionOptionHarness> {\n return new HarnessPredicate(WrMentionOptionHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption('active', options.active, async (harness, active) => (await harness.isActive()) === active);\n }\n\n /** The suggestion's label — whatever `displayWith` returned for the item. */\n async getText(): Promise<string> {\n return (await this.host()).text();\n }\n\n /**\n * Whether the cursor is on this suggestion.\n *\n * Read from `aria-selected` rather than from the `--active` modifier class. The\n * two are set from the same index, so they agree — but the ARIA state is what a\n * screen reader is told, and the class is only what the highlight is painted\n * with.\n */\n async isActive(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-selected')) === 'true';\n }\n\n /**\n * The suggestion's id — the value the field names in `aria-activedescendant`.\n *\n * The only link between the two DOM trees: the panel is in the overlay\n * container, not inside the field, so this id is how the reference reaches it\n * at all.\n */\n async getId(): Promise<string | null> {\n return (await this.host()).getAttribute('id');\n }\n\n /**\n * Pick this suggestion with the mouse.\n *\n * Needs no layout, so it works here: the panel commits on `mousedown` —\n * deliberately, so the field never loses focus and the caret stays put — and\n * the CDK's no-arg click dispatches `mousedown` at the head of its sequence\n * without any hit test.\n */\n async click(): Promise<void> {\n return (await this.host()).click();\n }\n\n /**\n * Move the pointer onto this suggestion, which makes it the active one.\n *\n * Hover and the arrow keys drive the same single cursor, so a spec can assert\n * the mouse path through {@link WrMentionHarness.getActiveOptionLabel} exactly\n * as it does the keyboard one.\n */\n async hover(): Promise<void> {\n return (await this.host()).hover();\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport {\n ComponentHarness,\n type HarnessLoader,\n HarnessPredicate,\n TestKey,\n type TestElement,\n} from '@angular/cdk/testing';\n\nimport type { WrMentionHarnessFilters, WrMentionOptionHarnessFilters } from './interfaces';\nimport { WrMentionOptionHarness } from './wr-mention-option-harness';\n\n/** How long {@link WrMentionHarness.blur} waits for the deferred close, in ms. Generous next to the 120ms grace period. */\nconst CLOSE_TIMEOUT = 1000;\n\n/** How often it re-checks, in ms. */\nconst POLL_STEP = 10;\n\n/**\n * The real `setTimeout`, captured at module load — before a spec can install fake\n * timers.\n *\n * {@link WrMentionHarness.blur} has to let REAL time pass: the close on blur is a\n * plain `setTimeout` inside the directive, and under zoneless change detection\n * `whenStable()` resolves without waiting for a macrotask. Reading a faked global\n * instead would mean the sleep never fires and the harness hangs where it should\n * fail with the message it prepared.\n */\nconst realSetTimeout = globalThis.setTimeout;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => {\n realSetTimeout(resolve, ms);\n });\n}\n\n/**\n * Test harness for `[wrMention]` — the field, and the suggestion panel it opens\n * at the caret.\n *\n * The harness matches the FIELD, because that is the only part of a mention in the\n * fixture: the panel is a `ComponentPortal` in the shared overlay container, a\n * sibling of the whole app. Suggestions are read through the document root, scoped\n * by the id the field publishes as `aria-controls` — which is what keeps two\n * mention fields on one page from answering with each other's suggestions.\n *\n * **The field is not a combobox, and it has no `aria-expanded`.** Both are\n * deliberate (the source spells out why: it holds prose, `role=\"combobox\"` is\n * disallowed on `<textarea>` and would drop `aria-multiline`, and `aria-expanded`\n * is not a supported state of `role=\"textbox\"`), and both shape this harness.\n * `aria-controls` is STATIC — it names the listbox even while there is no listbox —\n * so {@link isOpen} asks whether the listbox EXISTS instead of reading a state\n * flag, and \"a list appeared\" reaches a screen reader through the live region that\n * {@link getStatusMessage} reads.\n *\n * **The panel's POSITION is not testable in jsdom, so nothing here reports it.**\n * `caret.ts` places the panel by measuring a mirror `<div>` with\n * `getBoundingClientRect`, and jsdom has no layout: every rect is zeros, so the\n * measurement collapses to the origin. The overlay still lands on finite numbers —\n * `line-height` computes to `normal` there, so `caret.ts` falls back to\n * `font-size * 1.2` and the pane is offset by that alone (a measured\n * `margin-top: 23.2px`, `margin-left: 0px` on a default fixture) — which is\n * precisely the problem: every one of them is an artefact of the environment\n * rather than a fact about the component. Assert placement in a real browser.\n *\n * @example\n * ```ts\n * const mention = await loader.getHarness(WrMentionHarness);\n *\n * await mention.type('hey @al');\n * expect(await mention.getOptionLabels()).toEqual(['Alan Turing', 'Linus Torvalds']);\n *\n * await mention.commit();\n * expect(await mention.getValue()).toBe('hey @Alan Turing ');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrMentionHarness extends ComponentHarness {\n /** The directive's host. Both tags, since the directive drives either one. */\n static hostSelector = 'input[wrMention], textarea[wrMention]';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrMentionHarnessFilters = {}): HarnessPredicate<WrMentionHarness> {\n return new HarnessPredicate(WrMentionHarness, options)\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('placeholder', options.placeholder, (harness, placeholder) =>\n HarnessPredicate.stringMatches(harness.getPlaceholder(), placeholder)\n )\n .addOption('open', options.open, async (harness, open) => (await harness.isOpen()) === open);\n }\n\n /** The field's whole text, mention fragments and prose alike. */\n async getValue(): Promise<string> {\n return (await this.host()).getProperty<string>('value');\n }\n\n /** The field's placeholder, or `''` when it has none. */\n async getPlaceholder(): Promise<string> {\n return (await this.host()).getProperty<string>('placeholder');\n }\n\n /**\n * Type into the field one keystroke at a time, APPENDING to whatever is there.\n *\n * The realistic path, and the one that exercises detection the way a person\n * does: the directive re-reads the text before the caret on every `input`, so\n * `type('hey @al')` opens the panel on the `@` and then narrows it twice.\n *\n * Focuses the field, which BLURS whatever was focused before — and a blurred\n * mention field closes its own panel. Use {@link setValue} when a spec needs two\n * fields suggesting at the same time.\n */\n async type(text: string): Promise<void> {\n return (await this.host()).sendKeys(text);\n }\n\n /**\n * Replace the field's whole text and let the directive detect once.\n *\n * The `input` event is dispatched by hand rather than through keystrokes so the\n * field is never focused — which is the only way to get two mention panels open\n * at once, since focusing one field blurs the other and blur closes its panel.\n * The caret lands at the end of the new text, where detection looks.\n */\n async setValue(text: string): Promise<void> {\n const host = await this.host();\n await host.setInputValue(text);\n await host.dispatchEvent('input');\n }\n\n /** Empty the field. The panel goes with it — there is no trigger char left to detect. */\n async clear(): Promise<void> {\n return (await this.host()).clear();\n }\n\n /**\n * Click the field.\n *\n * Re-opens the suggestions when the caret is still sitting inside a mention —\n * the directive re-detects on click, which is how a user who pressed Escape (or\n * clicked away and back) gets the panel again without retyping. Needs no layout:\n * the CDK dispatches the event on the element rather than hit-testing a point.\n */\n async click(): Promise<void> {\n return (await this.host()).click();\n }\n\n /**\n * Whether the panel is offering suggestions.\n *\n * Asks whether this field's listbox exists, because the field publishes no\n * open/closed state to read: `aria-expanded` is not a supported state of\n * `role=\"textbox\"` (axe rates it a critical violation), and `aria-controls` is\n * static here, so its presence says nothing.\n */\n async isOpen(): Promise<boolean> {\n return (await this.listboxOrNull()) !== null;\n }\n\n /**\n * The field's `aria-autocomplete` — `'list'`, permanently.\n *\n * A capability, not a state: ARIA says authors should not toggle it to signal\n * that suggestions are showing, and being static is what makes the feature\n * discoverable on focus, before a trigger char is ever typed. {@link isOpen}\n * answers the state.\n */\n async getAutocomplete(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-autocomplete');\n }\n\n /**\n * The role the field PROMISES its popup will announce, from `aria-haspopup` —\n * `'listbox'`. Required alongside `aria-autocomplete=\"list\"`, and it has to name\n * the popup's real role, which {@link getListboxRole} reads back off the panel.\n */\n async getPopupRole(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-haspopup');\n }\n\n /** The role the open panel announces — `'listbox'`. Throws while the panel is closed. */\n async getListboxRole(): Promise<string | null> {\n return (await this.listbox()).getAttribute('role');\n }\n\n /**\n * The panel's accessible name.\n *\n * ARIA requires a `role=\"listbox\"` to have one, and a consumer never renders the\n * panel themselves — so the directive supplies it from the i18n catalog\n * (`mention.listbox`).\n */\n async getListboxLabel(): Promise<string | null> {\n return (await this.listbox()).getAttribute('aria-label');\n }\n\n /**\n * The suggestions on offer, in DOM order.\n *\n * The whole list a user can reach, not a window onto a longer one: the directive\n * slices the matches at `maxResults` (8 by default) before rendering, which is\n * why this panel needs no virtualisation.\n *\n * Throws while the panel is closed — the options do not exist anywhere until the\n * portal is attached, and an empty array reads like a panel that rendered\n * nothing.\n */\n async getOptions(filters: WrMentionOptionHarnessFilters = {}): Promise<WrMentionOptionHarness[]> {\n const loader = await this.listboxLoader();\n return loader.getAllHarnesses(WrMentionOptionHarness.with(filters));\n }\n\n /** The labels of the suggestions on offer, in DOM order. */\n async getOptionLabels(): Promise<string[]> {\n const options = await this.getOptions();\n return Promise.all(options.map(option => option.getText()));\n }\n\n /**\n * The id the field names in `aria-activedescendant`, or `null` when it names\n * nothing.\n *\n * The strict half of the field's two references. `aria-controls` is allowed to\n * dangle (see {@link isOpen}); this one is not — naming an element that is not\n * in the document is an author error no browser reports, it simply stops\n * announcing options. So `null` is the CORRECT answer while the panel is closed,\n * and worth asserting there.\n */\n async getActiveOptionId(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-activedescendant');\n }\n\n /**\n * The label a screen reader announces as the active suggestion.\n *\n * Resolved through `aria-activedescendant` the way an assistive technology\n * would, rather than by hunting for the active class — which is why it throws on\n * a reference that resolves to nothing instead of quietly reporting what the\n * class says.\n */\n async getActiveOptionLabel(): Promise<string> {\n const active = await this.activeOption('getActiveOptionLabel');\n return active.text();\n }\n\n /**\n * Where the active suggestion sits in {@link getOptions}, 0-based.\n *\n * Matched by id rather than counted by class, so the answer is the one\n * `aria-activedescendant` actually points at.\n */\n async getActiveOptionIndex(): Promise<number> {\n const id = await this.requireActiveOptionId('getActiveOptionIndex');\n const options = await this.getOptions();\n\n for (let index = 0; index < options.length; index++) {\n if ((await options[index].getId()) === id) return index;\n }\n\n throw new Error(\n `WrMentionHarness.getActiveOptionIndex(): aria-activedescendant names \"${id}\", which is not one of ` +\n `the ${options.length} suggestions on offer.`\n );\n }\n\n /**\n * Move the cursor down one suggestion, wrapping past the last back to the first.\n *\n * ArrowDown — and `aria-activedescendant` is the whole navigation model here,\n * because the options are never focused: focus stays in the field so the user can\n * keep typing the query.\n *\n * Throws while the panel is closed, where ArrowDown belongs to the field.\n */\n async nextOption(): Promise<void> {\n await this.requireOpen('nextOption');\n return (await this.host()).sendKeys(TestKey.DOWN_ARROW);\n }\n\n /** Move the cursor up one suggestion, wrapping past the first round to the last. */\n async previousOption(): Promise<void> {\n await this.requireOpen('previousOption');\n return (await this.host()).sendKeys(TestKey.UP_ARROW);\n }\n\n /**\n * Insert the active suggestion with Enter.\n *\n * The trigger char and the query are replaced by `valueWith(item, trigger)`\n * (`@Label` by default) plus a trailing space, the caret lands after that space,\n * the panel closes and `(wrMentionSelected)` fires with the item, the trigger\n * char and the query.\n *\n * Throws while the panel is closed — Enter is the field's own key there (a\n * newline in a `<textarea>`), and swallowing it would be the bug.\n */\n async commit(): Promise<void> {\n await this.requireOpen('commit');\n return (await this.host()).sendKeys(TestKey.ENTER);\n }\n\n /**\n * Insert the active suggestion with Tab instead.\n *\n * The same commit: the directive takes Tab as a second confirm key and\n * `preventDefault`s it, so focus stays in the field rather than leaving for the\n * next control.\n */\n async commitWithTab(): Promise<void> {\n await this.requireOpen('commitWithTab');\n return (await this.host()).sendKeys(TestKey.TAB);\n }\n\n /**\n * Pick the first suggestion matching the filters, with the mouse.\n *\n * Opens nothing by itself — type a trigger char first. Names what the panel does\n * offer when nothing matches, since a silent no-op here surfaces as an\n * unexplained unchanged field further down the spec.\n */\n async pick(filters: WrMentionOptionHarnessFilters): Promise<void> {\n const [option] = await this.getOptions(filters);\n if (!option) {\n const offered = await this.getOptionLabels();\n throw new Error(\n `WrMentionHarness.pick(): no suggestion matched ${JSON.stringify(filters)}. ` +\n `The panel offers: ${offered.join(', ')}.`\n );\n }\n\n return option.click();\n }\n\n /**\n * Dismiss the panel with Escape, leaving the typed text — trigger char and\n * query included — exactly as it is. A closed panel is left alone.\n *\n * Sends the WHOLE keystroke, keydown and keyup, as a real Escape is. That\n * matters here: the caret has not moved, so a keyup the directive re-detected on\n * would reopen the panel this just closed, and a harness that dispatched only\n * the keydown would report a dismissal the user never got.\n */\n async dismiss(): Promise<void> {\n if (!(await this.isOpen())) return;\n return (await this.host()).sendKeys(TestKey.ESCAPE);\n }\n\n /**\n * Blur the field and wait for the panel to go.\n *\n * The close is deferred by 120ms so a `mousedown` on a suggestion lands first,\n * and that is a real `setTimeout`: under zoneless change detection nothing\n * flushes it for us, so this waits out REAL time. A spec on fake timers should\n * drive its own clock and read {@link isOpen} instead of calling this.\n */\n async blur(timeout = CLOSE_TIMEOUT): Promise<void> {\n await (await this.host()).blur();\n\n for (let waited = 0; waited <= timeout; waited += POLL_STEP) {\n if (!(await this.isOpen())) return;\n\n await sleep(POLL_STEP);\n await this.forceStabilize();\n }\n\n throw new Error(\n `WrMentionHarness.blur(): the panel is still showing ${timeout}ms after the blur. The close is ` +\n 'deferred so that a click on a suggestion lands first, and this wait uses real time — a spec on ' +\n 'fake timers has to advance the clock itself and read isOpen().'\n );\n }\n\n /**\n * What the live region currently says.\n *\n * This is the announcement channel the field has INSTEAD of ARIA state, and\n * three things reach a screen reader only through it: how many suggestions\n * appeared, that NOTHING matched — the panel closes in that case, so there is no\n * DOM left to inspect and no state on the field to read — and what was inserted\n * on commit.\n *\n * Throws when more than one mention field is mounted. The region is parked on\n * `<body>` rather than in the panel (which is disposed on every keystroke that\n * leaves a mention, and a live region has to pre-exist its own text to be\n * announced at all), and it carries no id tying it to a field — so with two\n * fields around, the harness genuinely cannot tell which region is this one's.\n * Answering with whichever came first would be worse than refusing: assert\n * announcements in a fixture holding a single mention field.\n */\n async getStatusMessage(): Promise<string> {\n const regions = await this.documentRootLocatorFactory().locatorForAll('.wr-mention__status')();\n\n if (regions.length === 0) {\n throw new Error(\n 'WrMentionHarness.getStatusMessage(): no mention live region is in the document. The directive ' +\n 'appends it to <body> in the browser only, so there is none under SSR.'\n );\n }\n if (regions.length > 1) {\n throw new Error(\n `WrMentionHarness.getStatusMessage(): ${regions.length} mention live regions are in the document ` +\n 'and none of them names the field it belongs to, so this cannot say which one is yours. Assert ' +\n 'the announcement in a fixture with a single mention field.'\n );\n }\n\n return regions[0].text();\n }\n\n /** Move keyboard focus to the field. Focusing alone opens nothing — the panel needs a trigger char. */\n async focus(): Promise<void> {\n return (await this.host()).focus();\n }\n\n /** Whether the field holds keyboard focus. It keeps it while the panel is open, by design. */\n async isFocused(): Promise<boolean> {\n return (await this.host()).isFocused();\n }\n\n /**\n * A loader scoped to THIS field's listbox.\n *\n * Scoped by the id the field publishes as `aria-controls`, never by\n * `.wr-mention-panel`: the overlay container is shared by every mention on the\n * page, so a class query would answer with whichever field opened first.\n */\n private async listboxLoader(): Promise<HarnessLoader> {\n // Resolve the element first, so a closed panel fails with the message below\n // rather than with the CDK's generic \"expected to find element\" error.\n await this.listbox();\n return this.documentRootLocatorFactory().harnessLoaderFor(`#${await this.listboxId()}`);\n }\n\n /** This field's listbox in the overlay. Throws when the panel is closed. */\n private async listbox(): Promise<TestElement> {\n const listbox = await this.listboxOrNull();\n if (!listbox) {\n throw new Error(\n 'WrMentionHarness: no suggestions are showing, so there is nothing inside the panel to read. It ' +\n 'exists only while the caret sits after a trigger char that matched something — type() one in ' +\n 'first. (The field publishes aria-controls whether or not the panel is up, so that attribute is ' +\n 'not the check.)'\n );\n }\n return listbox;\n }\n\n /** This field's listbox, or `null` when the panel is closed. */\n private async listboxOrNull(): Promise<TestElement | null> {\n return this.documentRootLocatorFactory().locatorForOptional(`#${await this.listboxId()}`)();\n }\n\n /** The id the field names in `aria-controls`, which its listbox carries. */\n private async listboxId(): Promise<string> {\n const id = await (await this.host()).getAttribute('aria-controls');\n if (!id) {\n throw new Error(\n 'WrMentionHarness: this field publishes no aria-controls. That is a static host binding on ' +\n '[wrMention] — rendered even under SSR — so the element matched here is probably not a mention ' +\n 'field at all.'\n );\n }\n return id;\n }\n\n /** The element `aria-activedescendant` points at, or a targeted failure. */\n private async activeOption(method: string): Promise<TestElement> {\n const id = await this.requireActiveOptionId(method);\n const active = await this.documentRootLocatorFactory().locatorForOptional(`#${id}`)();\n\n if (!active) {\n throw new Error(\n `WrMentionHarness.${method}(): aria-activedescendant names \"${id}\", which is not in the document. ` +\n 'A dangling reference stops a screen reader announcing options and no browser reports it, so it ' +\n 'is a real failure rather than a harness one.'\n );\n }\n return active;\n }\n\n /** The active option's id, or a targeted failure. */\n private async requireActiveOptionId(method: string): Promise<string> {\n const id = await this.getActiveOptionId();\n if (!id) {\n throw new Error(\n `WrMentionHarness.${method}(): the field names no active suggestion. That is the correct state ` +\n 'while the panel is closed — check isOpen() first.'\n );\n }\n return id;\n }\n\n /** Refuse a keystroke that would mean something else entirely with the panel closed. */\n private async requireOpen(method: string): Promise<void> {\n if (await this.isOpen()) return;\n\n throw new Error(\n `WrMentionHarness.${method}(): no suggestions are showing, so this key belongs to the field itself ` +\n '— Enter is a newline, Tab leaves for the next control, and the arrows move the caret. Type a ' +\n 'trigger char first.'\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -214,10 +214,10 @@ class WrMentionPanel {
|
|
|
214
214
|
event.preventDefault();
|
|
215
215
|
this.picked.emit(item);
|
|
216
216
|
}
|
|
217
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
218
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
217
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMentionPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
218
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrMentionPanel, isStandalone: true, selector: "wr-mention-panel", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, activeIndex: { classPropertyName: "activeIndex", publicName: "activeIndex", isSignal: true, isRequired: false, transformFunction: null }, displayWith: { classPropertyName: "displayWith", publicName: "displayWith", isSignal: true, isRequired: false, transformFunction: null }, listboxId: { classPropertyName: "listboxId", publicName: "listboxId", isSignal: true, isRequired: false, transformFunction: null }, listLabel: { classPropertyName: "listLabel", publicName: "listLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { picked: "picked", hovered: "hovered" }, host: { classAttribute: "wr-mention-panel" }, ngImport: i0, template: "<!--\n No empty branch: `activate()` calls `close()` and returns before `open()` when\n nothing matches, so the panel is never rendered with zero items. The \"No\n matches\" `<div>` that used to live here was unreachable \u2014 and a hard-coded\n English string besides. The zero-match case is announced through the\n directive's live region instead.\n-->\n<ul class=\"wr-mention-panel__list\" role=\"listbox\" [attr.id]=\"listboxId()\" [attr.aria-label]=\"listLabel()\">\n @for (item of items(); track $index) {\n <li\n class=\"wr-mention-panel__option\"\n role=\"option\"\n [attr.id]=\"optionId($index)\"\n [class.wr-mention-panel__option--active]=\"$index === activeIndex()\"\n [attr.aria-selected]=\"$index === activeIndex()\"\n (mouseenter)=\"hovered.emit($index)\"\n (mousedown)=\"onPick(item, $event)\"\n >\n {{ displayWith()(item) }}\n </li>\n }\n</ul>\n", encapsulation: i0.ViewEncapsulation.None });
|
|
219
219
|
}
|
|
220
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
220
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMentionPanel, decorators: [{
|
|
221
221
|
type: Component,
|
|
222
222
|
args: [{ selector: 'wr-mention-panel', encapsulation: ViewEncapsulation.None, host: { class: 'wr-mention-panel' }, template: "<!--\n No empty branch: `activate()` calls `close()` and returns before `open()` when\n nothing matches, so the panel is never rendered with zero items. The \"No\n matches\" `<div>` that used to live here was unreachable \u2014 and a hard-coded\n English string besides. The zero-match case is announced through the\n directive's live region instead.\n-->\n<ul class=\"wr-mention-panel__list\" role=\"listbox\" [attr.id]=\"listboxId()\" [attr.aria-label]=\"listLabel()\">\n @for (item of items(); track $index) {\n <li\n class=\"wr-mention-panel__option\"\n role=\"option\"\n [attr.id]=\"optionId($index)\"\n [class.wr-mention-panel__option--active]=\"$index === activeIndex()\"\n [attr.aria-selected]=\"$index === activeIndex()\"\n (mouseenter)=\"hovered.emit($index)\"\n (mousedown)=\"onPick(item, $event)\"\n >\n {{ displayWith()(item) }}\n </li>\n }\n</ul>\n" }]
|
|
223
223
|
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: false }] }], displayWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayWith", required: false }] }], listboxId: [{ type: i0.Input, args: [{ isSignal: true, alias: "listboxId", required: false }] }], listLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "listLabel", required: false }] }], picked: [{ type: i0.Output, args: ["picked"] }], hovered: [{ type: i0.Output, args: ["hovered"] }] } });
|
|
@@ -682,10 +682,10 @@ class WrMention {
|
|
|
682
682
|
this.close();
|
|
683
683
|
this.announce(this.insertedText({ label }));
|
|
684
684
|
}
|
|
685
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
686
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.
|
|
685
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMention, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
686
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: WrMention, isStandalone: true, selector: "[wrMention]", inputs: { wrMentionItems: { classPropertyName: "wrMentionItems", publicName: "wrMentionItems", isSignal: true, isRequired: false, transformFunction: null }, triggers: { classPropertyName: "triggers", publicName: "triggers", isSignal: true, isRequired: false, transformFunction: null }, displayWith: { classPropertyName: "displayWith", publicName: "displayWith", isSignal: true, isRequired: false, transformFunction: null }, valueWith: { classPropertyName: "valueWith", publicName: "valueWith", isSignal: true, isRequired: false, transformFunction: null }, filterWith: { classPropertyName: "filterWith", publicName: "filterWith", isSignal: true, isRequired: false, transformFunction: null }, maxResults: { classPropertyName: "maxResults", publicName: "maxResults", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { wrMentionSelected: "wrMentionSelected" }, host: { attributes: { "aria-autocomplete": "list", "aria-haspopup": "listbox" }, listeners: { "input": "onInput()", "click": "onClick()", "keyup": "onCaretMove($event)", "keydown": "onKeydown($event)", "blur": "onBlur()", "compositionstart": "onCompositionStart()", "compositionend": "onCompositionEnd()" }, properties: { "attr.aria-controls": "listboxId", "attr.aria-activedescendant": "activeOptionId()" } }, ngImport: i0 });
|
|
687
687
|
}
|
|
688
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
688
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrMention, decorators: [{
|
|
689
689
|
type: Directive,
|
|
690
690
|
args: [{
|
|
691
691
|
selector: '[wrMention]',
|