ngwr 14.8.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/button/styles/_index.scss +6 -0
- package/cascader/styles/_rules.scss +1 -1
- package/checkbox/styles/_index.scss +4 -0
- package/command-palette/styles/_index.scss +1 -1
- package/divider/styles/_index.scss +2 -0
- package/editor/styles/_index.scss +441 -0
- 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 +3 -3
- 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 +3 -3
- 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 +6 -6
- 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 +21 -21
- 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 +3 -3
- 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 +9 -9
- 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 +6 -6
- 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 +9 -9
- 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 +3 -3
- 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 +29 -0
- package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-cs.mjs +29 -0
- package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-de.mjs +29 -0
- package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +34 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-es.mjs +29 -0
- package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-fr.mjs +29 -0
- package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-he.mjs +29 -0
- package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-hi.mjs +29 -0
- package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-id.mjs +29 -0
- package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-it.mjs +29 -0
- package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ja.mjs +29 -0
- package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ko.mjs +29 -0
- package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-nl.mjs +29 -0
- package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pl.mjs +29 -0
- package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pt.mjs +29 -0
- package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +29 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-sv.mjs +29 -0
- package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-tr.mjs +29 -0
- package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-uk.mjs +29 -0
- package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-vi.mjs +29 -0
- package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh-TW.mjs +29 -0
- package/fesm2022/ngwr-i18n-zh-TW.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh.mjs +29 -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 +3 -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 +3 -3
- 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.map +1 -1
- package/fesm2022/ngwr-meter-group.mjs +3 -3
- 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 +3 -3
- 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 +12 -12
- 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 +3 -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 +3 -3
- 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 +3 -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 +5 -0
- package/i18n/ar.json +29 -0
- package/i18n/cs.json +29 -0
- package/i18n/de.json +29 -0
- package/i18n/en.json +29 -0
- package/i18n/es.json +29 -0
- package/i18n/fr.json +29 -0
- package/i18n/he.json +29 -0
- package/i18n/hi.json +29 -0
- package/i18n/id.json +29 -0
- package/i18n/it.json +29 -0
- package/i18n/ja.json +29 -0
- package/i18n/ko.json +29 -0
- package/i18n/nl.json +29 -0
- package/i18n/pl.json +29 -0
- package/i18n/pt.json +29 -0
- package/i18n/ru.json +29 -0
- package/i18n/sv.json +29 -0
- package/i18n/tr.json +29 -0
- package/i18n/uk.json +29 -0
- package/i18n/vi.json +29 -0
- package/i18n/zh-TW.json +29 -0
- package/i18n/zh.json +29 -0
- package/input/styles/_index.scss +5 -1
- package/input-otp/styles/_index.scss +5 -5
- package/llms-full.txt +11 -1
- package/llms.txt +188 -132
- package/mcp/providers.js +2 -2
- package/mcp/server.js +1 -1
- package/overlay/styles/_arrow.scss +3 -0
- package/package.json +43 -2
- 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 +3 -3
- package/sidebar/styles/_index.scss +10 -0
- 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/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/tree/styles/_index.scss +1 -1
- package/types/ngwr-badge.d.ts +22 -7
- package/types/ngwr-config.d.ts +12 -0
- package/types/ngwr-editor-testing.d.ts +378 -0
- package/types/ngwr-editor.d.ts +449 -0
- package/types/ngwr-markdown.d.ts +61 -2
- 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-radio-testing.mjs","sources":["../../../projects/lib/radio/testing/wr-radio-harness.ts","../../../projects/lib/radio/testing/wr-radio-group-harness.ts","../../../projects/lib/radio/testing/ngwr-radio-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 { WrRadioSize } from 'ngwr/radio';\n\nimport type { WrRadioHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one `<wr-radio>` — an option, not a control.\n *\n * The visible dot is a `<span>` the stylesheet paints; the state lives on a real\n * `<input type=\"radio\">` inside the label, and that is what this reads and clicks.\n * A radio cannot exist alone — the component throws when it is constructed outside\n * a `<wr-radio-group>` — so `WrRadioGroupHarness` is where the SELECTION lives and\n * this answers questions about one option.\n *\n * There is no `aria-checked` here to prefer over the DOM, and that is right: a\n * native radio carries its state in the `checked` IDL property, so the ARIA\n * attribute would only be a second source of truth for a screen reader to\n * disagree with. {@link isChecked} reads the property, which is the state.\n *\n * @example\n * ```ts\n * const medium = await loader.getHarness(WrRadioHarness.with({ label: 'Medium' }));\n * await medium.check();\n * expect(await medium.isChecked()).toBe(true);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrRadioHarness extends ComponentHarness {\n static hostSelector = 'wr-radio';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrRadioHarnessFilters = {}): HarnessPredicate<WrRadioHarness> {\n return new HarnessPredicate(WrRadioHarness, options)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('checked', options.checked, async (harness, checked) => (await harness.isChecked()) === checked)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly input = this.locatorFor('input.wr-radio__input');\n private readonly label = this.locatorFor('label.wr-radio__label');\n\n /** The projected label text, trimmed. An icon lives in the dot, so it is not part of it. */\n async getLabel(): Promise<string> {\n return (await this.locatorFor('.wr-radio__text')()).text();\n }\n\n /**\n * The option's value as the template WROTE it, or `null` when it was bound.\n *\n * The gap worth knowing about before a spec leans on this: `value` is `unknown`,\n * and the component never puts it on the native input — the group holds the\n * selection and a radio only says which one it is. So the only `value` in the\n * DOM is the static attribute Angular leaves behind for a literal `value=\"sm\"`;\n * `[value]=\"size\"` reaches the component without ever touching the page and\n * reads `null` here whatever it holds, which for an object value it always will.\n *\n * Address options by their LABEL when it matters, and assert the picked value on\n * the host's own model, which is the only place it exists.\n */\n async getValue(): Promise<string | null> {\n return (await this.host()).getAttribute('value');\n }\n\n /**\n * The `name` the group stamps on this radio's native input.\n *\n * It is the whole of the group's keyboard behaviour: one shared name is what\n * makes a browser treat the radios as a single tab stop, walk the arrow keys\n * between them and announce \"1 of 3\". None of that is implemented in the\n * component, so a name that stops being shared takes all of it away silently.\n */\n async getName(): Promise<string> {\n return (await this.input()).getProperty<string>('name');\n }\n\n /**\n * Whether this option is the group's answer.\n *\n * Read from the input's `checked` PROPERTY: `[checked]` is a property binding,\n * so the attribute is never written and reading it would report `null` for a\n * radio with a visible dot.\n */\n async isChecked(): Promise<boolean> {\n return (await this.input()).getProperty<boolean>('checked');\n }\n\n /**\n * Whether the option refuses selection — for either reason.\n *\n * The native input is disabled by this radio's own `disabled` OR by the group's,\n * and from outside they are the same thing: the option cannot be picked.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.input()).getProperty<boolean>('disabled');\n }\n\n /**\n * The control size, from the host modifier.\n *\n * `md` is the default and carries NO modifier class, so its absence is the\n * answer rather than a missing one.\n */\n async getSize(): Promise<WrRadioSize> {\n const host = await this.host();\n if (await host.hasClass('wr-radio--sm')) return 'sm';\n if (await host.hasClass('wr-radio--lg')) return 'lg';\n return 'md';\n }\n\n /** Whether the dot is filled by an icon instead of the default solid circle. */\n async hasIcon(): Promise<boolean> {\n return (await this.host()).hasClass('wr-radio--has-icon');\n }\n\n /**\n * Whether the `<label for>` really points at this radio's input.\n *\n * Not the option's only naming route, and worth being precise about: the input\n * sits INSIDE the `<label>`, so the implicit association already names it and a\n * `for` that misses does not leave the radio unnamed. What a stray `for` does\n * take away is the pairing every consumer assumes — it hands label clicks, and\n * anything that references the label, to whatever else on the page owns that id.\n */\n async isLabelBound(): Promise<boolean> {\n const forAttr = await (await this.label()).getAttribute('for');\n return forAttr !== null && forAttr === (await (await this.input()).getProperty<string>('id'));\n }\n\n /**\n * Pick this option, the way a user does — a click on the native input.\n *\n * Nothing happens when it is already checked (a radio has no untick), and\n * nothing happens when it is disabled, so assert the result rather than assume\n * it — or reach for `WrRadioGroupHarness.select()`, which says which of the two\n * it was.\n *\n * A click rather than a keypress, despite jsdom having no layout: the events are\n * dispatched onto the input itself, so nothing is hit-tested and the zero-sized\n * rect jsdom reports for every element cannot miss its target, and the\n * `change` event it produces is exactly the one the component listens for. The\n * arrow keys that would move a real user between options are the BROWSER's\n * behaviour over a shared `name`, not the component's, and jsdom implements none\n * of it — there is nothing here for a keyboard to drive.\n */\n async check(): Promise<void> {\n if (await this.isChecked()) return;\n await (await this.input()).click();\n }\n\n /** Move keyboard focus onto the real control. */\n async focus(): Promise<void> {\n return (await this.input()).focus();\n }\n\n /**\n * Take focus off the radio — which is what makes the GROUP emit `touch`, since a\n * blur from any child is what marks a bound field touched.\n */\n async blur(): Promise<void> {\n return (await this.input()).blur();\n }\n\n async isFocused(): Promise<boolean> {\n return (await this.input()).isFocused();\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 { WrRadioGroupHarnessFilters, WrRadioHarnessFilters } from './interfaces';\nimport { WrRadioHarness } from './wr-radio-harness';\n\n/**\n * Test harness for `<wr-radio-group>` — the control a `<wr-radio>` only means\n * anything inside of, and the root of a two-part family with {@link WrRadioHarness}.\n *\n * Options are addressed by their LABEL rather than by their value, and there is\n * deliberately no `getValue()` here. The group's value is a signal of `unknown`\n * that the component never writes to the DOM: not to the group element, not to a\n * radio's native input. The one thing on the page is whatever static attribute the\n * template happened to leave behind, so a selection made through `[(value)]`, a\n * `[formField]` or an object is not there at all, and a harness reporting it would\n * be guessing. {@link getSelectedLabel} says what the page says, and a spec that\n * needs the value itself asserts the host's own model — which is the assertion that\n * would have caught the bug anyway.\n *\n * Arrow-key navigation is not this component's either: the radios share one `name`\n * attribute and the browser does the rest, so jsdom — which implements none of it\n * — leaves nothing for a keyboard to drive. {@link getName} is what a spec should\n * pin instead, because losing the shared name is what actually takes arrow\n * navigation away, and {@link getTabStopLabel} answers the other half.\n *\n * @example\n * ```ts\n * const size = await loader.getHarness(WrRadioGroupHarness.with({ name: 'size' }));\n *\n * expect(await size.getRadioLabels()).toEqual(['Small', 'Medium', 'Large']);\n * await size.select({ label: 'Large' });\n * expect(await size.getSelectedLabel()).toBe('Large');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrRadioGroupHarness extends ComponentHarness {\n static hostSelector = 'wr-radio-group';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrRadioGroupHarnessFilters = {}): HarnessPredicate<WrRadioGroupHarness> {\n return new HarnessPredicate(WrRadioGroupHarness, options)\n .addOption('name', options.name, (harness, name) => HarnessPredicate.stringMatches(harness.getName(), name))\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n /**\n * The role the group announces — `radiogroup`, which is what tells a screen\n * reader that the options belong to one question and how many of them there are.\n */\n async getRole(): Promise<string | null> {\n return (await this.host()).getAttribute('role');\n }\n\n /**\n * The group's accessible name, resolved in the order a screen reader resolves it:\n * the text of every element `aria-labelledby` points at, and `aria-label` only\n * when that named nothing. `null` when the consumer wired neither.\n *\n * The order is not interchangeable — `aria-labelledby` WINS over `aria-label` in\n * the accessible-name computation, so a group carrying both is announced by the\n * heading it points at and reporting the `aria-label` would describe a name\n * nobody hears.\n *\n * That `null` is a finding rather than a detail — a `radiogroup` with no name\n * announces three answers and never the question. The component ships no label\n * input on purpose (the question is the consumer's own heading), so this is the\n * only way to check the wiring held.\n */\n async getAccessibleName(): Promise<string | null> {\n const host = await this.host();\n\n const labelledBy = await host.getAttribute('aria-labelledby');\n if (labelledBy !== null) {\n // Resolved from the document root: `aria-labelledby` may point anywhere on the\n // page, and the heading above a group is normally outside it.\n const root = this.documentRootLocatorFactory();\n const texts: string[] = [];\n for (const id of labelledBy.split(/\\s+/).filter(Boolean)) {\n const target = await root.locatorForOptional(`#${id}`)();\n if (target) texts.push(await target.text());\n }\n\n // A reference that resolves to nothing names nothing, and the computation\n // falls through to `aria-label` exactly as it does in a browser.\n if (texts.length) return texts.join(' ');\n }\n\n return host.getAttribute('aria-label');\n }\n\n /**\n * The `name` every radio in the group shares.\n *\n * Read off the radios because they are the only place it is RELIABLY written: the\n * group's `name` input reaches the page as an attribute on each native input, and\n * a bound `[name]=\"…\"` never lands on the group element at all. A literal\n * `name=\"size\"` does survive on the group element — Angular leaves a static\n * attribute alone — which is exactly the trap: reading it there works for the\n * literal form and quietly returns nothing for the bound one.\n */\n async getName(): Promise<string> {\n const [first] = await this.radiosOrThrow();\n return first.getName();\n }\n\n /**\n * Whether the group refuses every option.\n *\n * Answered from the radios, and it could not be otherwise: a bound `[disabled]`\n * on the group reaches the page only by disabling each native input, so a group\n * disabled as a whole and a group whose every option is individually disabled\n * are the same page. They are also the same thing to the person using it, which\n * is the question a harness should be answering.\n */\n async isDisabled(): Promise<boolean> {\n for (const radio of await this.radiosOrThrow()) {\n if (!(await radio.isDisabled())) return false;\n }\n return true;\n }\n\n /** The group's own options, in DOM order. A sibling group's radios are not here. */\n async getRadios(filters: WrRadioHarnessFilters = {}): Promise<WrRadioHarness[]> {\n return this.locatorForAll(WrRadioHarness.with(filters))();\n }\n\n /** The label of every option, in DOM order. */\n async getRadioLabels(): Promise<string[]> {\n const radios = await this.getRadios();\n return Promise.all(radios.map(radio => radio.getLabel()));\n }\n\n /** The checked option, or `null` while the group is unanswered. */\n async getSelectedRadio(): Promise<WrRadioHarness | null> {\n const [checked] = await this.getRadios({ checked: true });\n return checked ?? null;\n }\n\n /**\n * The label of the checked option, or `null` while the group is unanswered.\n *\n * Also `null` for a value that matches no radio — the group holds it, nothing is\n * checked, and the page shows an unanswered question.\n */\n async getSelectedLabel(): Promise<string | null> {\n const selected = await this.getSelectedRadio();\n return selected ? selected.getLabel() : null;\n }\n\n /**\n * Pick the first option matching the filters — this is how the group's value\n * changes.\n *\n * Throws when nothing matches, naming the options that do exist, and throws\n * again when the option is still unchecked afterwards. That second failure has\n * exactly two causes and reports both, because a click that quietly did nothing\n * surfaces as an unrelated assertion three lines later.\n */\n async select(filters: WrRadioHarnessFilters): Promise<void> {\n const [radio] = await this.getRadios(filters);\n if (!radio) {\n throw new Error(\n `WrRadioGroupHarness.select(): no radio matched ${JSON.stringify(filters)}. This group offers: ` +\n `${(await this.getRadioLabels()).join(', ')}.`\n );\n }\n\n await radio.check();\n\n if (!(await radio.isChecked())) {\n throw new Error(\n `WrRadioGroupHarness.select(): \"${await radio.getLabel()}\" is still unchecked after being clicked — ` +\n 'either that option carries `disabled` or the whole group does.'\n );\n }\n }\n\n /**\n * The label of the option a Tab press would land on, or `null` when every option\n * is disabled and the group is skipped over entirely.\n *\n * A radio group is ONE tab stop, and which radio it is is written down nowhere:\n * the component authors no roving `tabindex`, because the browser already\n * applies the native rule to a shared `name` — the checked radio, or the first\n * enabled one while nothing is checked. So this is DERIVED from the rendered\n * state rather than read off an attribute, there being no attribute to read.\n *\n * Worth asking precisely because those two answers are different radios exactly\n * when a form opens unanswered, which is the state most specs start in.\n */\n async getTabStopLabel(): Promise<string | null> {\n const stop = await this.tabStop();\n return stop ? stop.getLabel() : null;\n }\n\n /** Put focus where a Tab press would put it. Throws when the group has no tab stop. */\n async focusTabStop(): Promise<void> {\n const stop = await this.tabStop();\n if (!stop) {\n throw new Error(\n 'WrRadioGroupHarness.focusTabStop(): every option in this group is disabled, so the group is not a ' +\n 'tab stop at all — Tab skips it rather than entering it.'\n );\n }\n await stop.focus();\n }\n\n /**\n * The label of the option that currently holds focus, or `null` when focus is\n * elsewhere. The counterpart to {@link focusTabStop}: in a real browser the\n * arrow keys move this, and moving it is also what picks a new answer.\n */\n async getFocusedLabel(): Promise<string | null> {\n for (const radio of await this.getRadios()) {\n if (await radio.isFocused()) return radio.getLabel();\n }\n return null;\n }\n\n /** The radio Tab would enter on, applying the browser's rule to the rendered state. */\n private async tabStop(): Promise<WrRadioHarness | null> {\n const enabled: WrRadioHarness[] = [];\n for (const radio of await this.getRadios()) {\n if (!(await radio.isDisabled())) enabled.push(radio);\n }\n\n for (const radio of enabled) {\n if (await radio.isChecked()) return radio;\n }\n return enabled[0] ?? null;\n }\n\n /** The options, or a failure saying why an empty group has nothing to report. */\n private async radiosOrThrow(): Promise<WrRadioHarness[]> {\n const radios = await this.getRadios();\n if (!radios.length) {\n throw new Error(\n 'WrRadioGroupHarness: this group has no <wr-radio> children, and every group-level fact here is read ' +\n 'off them — the shared name and the disabled state reach the DOM through the radios, and a bound ' +\n '[name] / [disabled] leaves nothing on the group element to read instead.'\n );\n }\n return radios;\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-radio-testing.mjs","sources":["../../../projects/lib/radio/testing/wr-radio-harness.ts","../../../projects/lib/radio/testing/wr-radio-group-harness.ts","../../../projects/lib/radio/testing/ngwr-radio-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 { WrRadioSize } from 'ngwr/radio';\n\nimport type { WrRadioHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one `<wr-radio>` — an option, not a control.\n *\n * The visible dot is a `<span>` the stylesheet paints; the state lives on a real\n * `<input type=\"radio\">` inside the label, and that is what this reads and clicks.\n * A radio cannot exist alone — the component throws when it is constructed outside\n * a `<wr-radio-group>` — so `WrRadioGroupHarness` is where the SELECTION lives and\n * this answers questions about one option.\n *\n * There is no `aria-checked` here to prefer over the DOM, and that is right: a\n * native radio carries its state in the `checked` IDL property, so the ARIA\n * attribute would only be a second source of truth for a screen reader to\n * disagree with. {@link isChecked} reads the property, which is the state.\n *\n * @example\n * ```ts\n * const medium = await loader.getHarness(WrRadioHarness.with({ label: 'Medium' }));\n * await medium.check();\n * expect(await medium.isChecked()).toBe(true);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrRadioHarness extends ComponentHarness {\n static hostSelector = 'wr-radio';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrRadioHarnessFilters = {}): HarnessPredicate<WrRadioHarness> {\n return new HarnessPredicate(WrRadioHarness, options)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('checked', options.checked, async (harness, checked) => (await harness.isChecked()) === checked)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly input = this.locatorFor('input.wr-radio__input');\n private readonly label = this.locatorFor('label.wr-radio__label');\n\n /** The projected label text, trimmed. An icon lives in the dot, so it is not part of it. */\n async getLabel(): Promise<string> {\n return (await this.locatorFor('.wr-radio__text')()).text();\n }\n\n /**\n * The option's value as the template WROTE it, or `null` when it was bound.\n *\n * The gap worth knowing about before a spec leans on this: `value` is `unknown`,\n * and the component never puts it on the native input — the group holds the\n * selection and a radio only says which one it is. So the only `value` in the\n * DOM is the static attribute Angular leaves behind for a literal `value=\"sm\"`;\n * `[value]=\"size\"` reaches the component without ever touching the page and\n * reads `null` here whatever it holds, which for an object value it always will.\n *\n * Address options by their LABEL when it matters, and assert the picked value on\n * the host's own model, which is the only place it exists.\n */\n async getValue(): Promise<string | null> {\n return (await this.host()).getAttribute('value');\n }\n\n /**\n * The `name` the group stamps on this radio's native input.\n *\n * It is the whole of the group's keyboard behaviour: one shared name is what\n * makes a browser treat the radios as a single tab stop, walk the arrow keys\n * between them and announce \"1 of 3\". None of that is implemented in the\n * component, so a name that stops being shared takes all of it away silently.\n */\n async getName(): Promise<string> {\n return (await this.input()).getProperty<string>('name');\n }\n\n /**\n * Whether this option is the group's answer.\n *\n * Read from the input's `checked` PROPERTY: `[checked]` is a property binding,\n * so the attribute is never written and reading it would report `null` for a\n * radio with a visible dot.\n */\n async isChecked(): Promise<boolean> {\n return (await this.input()).getProperty<boolean>('checked');\n }\n\n /**\n * Whether the option refuses selection — for either reason.\n *\n * The native input is disabled by this radio's own `disabled` OR by the group's,\n * and from outside they are the same thing: the option cannot be picked.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.input()).getProperty<boolean>('disabled');\n }\n\n /**\n * The control size, from the host modifier.\n *\n * `md` is the default and carries NO modifier class, so its absence is the\n * answer rather than a missing one.\n */\n async getSize(): Promise<WrRadioSize> {\n const host = await this.host();\n if (await host.hasClass('wr-radio--sm')) return 'sm';\n if (await host.hasClass('wr-radio--lg')) return 'lg';\n return 'md';\n }\n\n /** Whether the dot is filled by an icon instead of the default solid circle. */\n async hasIcon(): Promise<boolean> {\n return (await this.host()).hasClass('wr-radio--has-icon');\n }\n\n /**\n * Whether the `<label for>` really points at this radio's input.\n *\n * Not the option's only naming route, and worth being precise about: the input\n * sits INSIDE the `<label>`, so the implicit association already names it and a\n * `for` that misses does not leave the radio unnamed. What a stray `for` does\n * take away is the pairing every consumer assumes — it hands label clicks, and\n * anything that references the label, to whatever else on the page owns that id.\n */\n async isLabelBound(): Promise<boolean> {\n const forAttr = await (await this.label()).getAttribute('for');\n return forAttr !== null && forAttr === (await (await this.input()).getProperty<string>('id'));\n }\n\n /**\n * Pick this option, the way a user does — a click on the native input.\n *\n * Nothing happens when it is already checked (a radio has no untick), and\n * nothing happens when it is disabled, so assert the result rather than assume\n * it — or reach for `WrRadioGroupHarness.select()`, which says which of the two\n * it was.\n *\n * A click rather than a keypress, despite jsdom having no layout: the events are\n * dispatched onto the input itself, so nothing is hit-tested and the zero-sized\n * rect jsdom reports for every element cannot miss its target, and the\n * `change` event it produces is exactly the one the component listens for. The\n * arrow keys that would move a real user between options are the BROWSER's\n * behaviour over a shared `name`, not the component's, and jsdom implements none\n * of it — there is nothing here for a keyboard to drive.\n */\n async check(): Promise<void> {\n if (await this.isChecked()) return;\n await (await this.input()).click();\n }\n\n /** Move keyboard focus onto the real control. */\n async focus(): Promise<void> {\n return (await this.input()).focus();\n }\n\n /**\n * Take focus off the radio — which is what makes the GROUP emit `touch`, since a\n * blur from any child is what marks a bound field touched.\n */\n async blur(): Promise<void> {\n return (await this.input()).blur();\n }\n\n async isFocused(): Promise<boolean> {\n return (await this.input()).isFocused();\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 { WrRadioGroupHarnessFilters, WrRadioHarnessFilters } from './interfaces';\nimport { WrRadioHarness } from './wr-radio-harness';\n\n/**\n * Test harness for `<wr-radio-group>` — the control a `<wr-radio>` only means\n * anything inside of, and the root of a two-part family with {@link WrRadioHarness}.\n *\n * Options are addressed by their LABEL rather than by their value, and there is\n * deliberately no `getValue()` here. The group's value is a signal of `unknown`\n * that the component never writes to the DOM: not to the group element, not to a\n * radio's native input. The one thing on the page is whatever static attribute the\n * template happened to leave behind, so a selection made through `[(value)]`, a\n * `[formField]` or an object is not there at all, and a harness reporting it would\n * be guessing. {@link getSelectedLabel} says what the page says, and a spec that\n * needs the value itself asserts the host's own model — which is the assertion that\n * would have caught the bug anyway.\n *\n * Arrow-key navigation is not this component's either: the radios share one `name`\n * attribute and the browser does the rest, so jsdom — which implements none of it\n * — leaves nothing for a keyboard to drive. {@link getName} is what a spec should\n * pin instead, because losing the shared name is what actually takes arrow\n * navigation away, and {@link getTabStopLabel} answers the other half.\n *\n * @example\n * ```ts\n * const size = await loader.getHarness(WrRadioGroupHarness.with({ name: 'size' }));\n *\n * expect(await size.getRadioLabels()).toEqual(['Small', 'Medium', 'Large']);\n * await size.select({ label: 'Large' });\n * expect(await size.getSelectedLabel()).toBe('Large');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrRadioGroupHarness extends ComponentHarness {\n static hostSelector = 'wr-radio-group';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrRadioGroupHarnessFilters = {}): HarnessPredicate<WrRadioGroupHarness> {\n return new HarnessPredicate(WrRadioGroupHarness, options)\n .addOption('name', options.name, (harness, name) => HarnessPredicate.stringMatches(harness.getName(), name))\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n /**\n * The role the group announces — `radiogroup`, which is what tells a screen\n * reader that the options belong to one question and how many of them there are.\n */\n async getRole(): Promise<string | null> {\n return (await this.host()).getAttribute('role');\n }\n\n /**\n * The group's accessible name, resolved in the order a screen reader resolves it:\n * the text of every element `aria-labelledby` points at, and `aria-label` only\n * when that named nothing. `null` when the consumer wired neither.\n *\n * The order is not interchangeable — `aria-labelledby` WINS over `aria-label` in\n * the accessible-name computation, so a group carrying both is announced by the\n * heading it points at and reporting the `aria-label` would describe a name\n * nobody hears.\n *\n * That `null` is a finding rather than a detail — a `radiogroup` with no name\n * announces three answers and never the question. The component ships no label\n * input on purpose (the question is the consumer's own heading), so this is the\n * only way to check the wiring held.\n */\n async getAccessibleName(): Promise<string | null> {\n const host = await this.host();\n\n const labelledBy = await host.getAttribute('aria-labelledby');\n if (labelledBy !== null) {\n // Resolved from the document root: `aria-labelledby` may point anywhere on the\n // page, and the heading above a group is normally outside it.\n const root = this.documentRootLocatorFactory();\n const texts: string[] = [];\n for (const id of labelledBy.split(/\\s+/).filter(Boolean)) {\n const target = await root.locatorForOptional(`#${id}`)();\n if (target) texts.push(await target.text());\n }\n\n // A reference that resolves to nothing names nothing, and the computation\n // falls through to `aria-label` exactly as it does in a browser.\n if (texts.length) return texts.join(' ');\n }\n\n return host.getAttribute('aria-label');\n }\n\n /**\n * The `name` every radio in the group shares.\n *\n * Read off the radios because they are the only place it is RELIABLY written: the\n * group's `name` input reaches the page as an attribute on each native input, and\n * a bound `[name]=\"…\"` never lands on the group element at all. A literal\n * `name=\"size\"` does survive on the group element — Angular leaves a static\n * attribute alone — which is exactly the trap: reading it there works for the\n * literal form and quietly returns nothing for the bound one.\n */\n async getName(): Promise<string> {\n const [first] = await this.radiosOrThrow();\n return first.getName();\n }\n\n /**\n * Whether the group refuses every option.\n *\n * Answered from the radios, and it could not be otherwise: a bound `[disabled]`\n * on the group reaches the page only by disabling each native input, so a group\n * disabled as a whole and a group whose every option is individually disabled\n * are the same page. They are also the same thing to the person using it, which\n * is the question a harness should be answering.\n */\n async isDisabled(): Promise<boolean> {\n for (const radio of await this.radiosOrThrow()) {\n if (!(await radio.isDisabled())) return false;\n }\n return true;\n }\n\n /** The group's own options, in DOM order. A sibling group's radios are not here. */\n async getRadios(filters: WrRadioHarnessFilters = {}): Promise<WrRadioHarness[]> {\n return this.locatorForAll(WrRadioHarness.with(filters))();\n }\n\n /** The label of every option, in DOM order. */\n async getRadioLabels(): Promise<string[]> {\n const radios = await this.getRadios();\n return Promise.all(radios.map(radio => radio.getLabel()));\n }\n\n /** The checked option, or `null` while the group is unanswered. */\n async getSelectedRadio(): Promise<WrRadioHarness | null> {\n const [checked] = await this.getRadios({ checked: true });\n return checked ?? null;\n }\n\n /**\n * The label of the checked option, or `null` while the group is unanswered.\n *\n * Also `null` for a value that matches no radio — the group holds it, nothing is\n * checked, and the page shows an unanswered question.\n */\n async getSelectedLabel(): Promise<string | null> {\n const selected = await this.getSelectedRadio();\n return selected ? selected.getLabel() : null;\n }\n\n /**\n * Pick the first option matching the filters — this is how the group's value\n * changes.\n *\n * Throws when nothing matches, naming the options that do exist, and throws\n * again when the option is still unchecked afterwards. That second failure has\n * exactly two causes and reports both, because a click that quietly did nothing\n * surfaces as an unrelated assertion three lines later.\n */\n async select(filters: WrRadioHarnessFilters): Promise<void> {\n const [radio] = await this.getRadios(filters);\n if (!radio) {\n throw new Error(\n `WrRadioGroupHarness.select(): no radio matched ${JSON.stringify(filters)}. This group offers: ` +\n `${(await this.getRadioLabels()).join(', ')}.`\n );\n }\n\n await radio.check();\n\n if (!(await radio.isChecked())) {\n throw new Error(\n `WrRadioGroupHarness.select(): \"${await radio.getLabel()}\" is still unchecked after being clicked — ` +\n 'either that option carries `disabled` or the whole group does.'\n );\n }\n }\n\n /**\n * The label of the option a Tab press would land on, or `null` when every option\n * is disabled and the group is skipped over entirely.\n *\n * A radio group is ONE tab stop, and which radio it is is written down nowhere:\n * the component authors no roving `tabindex`, because the browser already\n * applies the native rule to a shared `name` — the checked radio, or the first\n * enabled one while nothing is checked. So this is DERIVED from the rendered\n * state rather than read off an attribute, there being no attribute to read.\n *\n * Worth asking precisely because those two answers are different radios exactly\n * when a form opens unanswered, which is the state most specs start in.\n */\n async getTabStopLabel(): Promise<string | null> {\n const stop = await this.tabStop();\n return stop ? stop.getLabel() : null;\n }\n\n /** Put focus where a Tab press would put it. Throws when the group has no tab stop. */\n async focusTabStop(): Promise<void> {\n const stop = await this.tabStop();\n if (!stop) {\n throw new Error(\n 'WrRadioGroupHarness.focusTabStop(): every option in this group is disabled, so the group is not a ' +\n 'tab stop at all — Tab skips it rather than entering it.'\n );\n }\n await stop.focus();\n }\n\n /**\n * The label of the option that currently holds focus, or `null` when focus is\n * elsewhere. The counterpart to {@link focusTabStop}: in a real browser the\n * arrow keys move this, and moving it is also what picks a new answer.\n */\n async getFocusedLabel(): Promise<string | null> {\n for (const radio of await this.getRadios()) {\n if (await radio.isFocused()) return radio.getLabel();\n }\n return null;\n }\n\n /** The radio Tab would enter on, applying the browser's rule to the rendered state. */\n private async tabStop(): Promise<WrRadioHarness | null> {\n const enabled: WrRadioHarness[] = [];\n for (const radio of await this.getRadios()) {\n if (!(await radio.isDisabled())) enabled.push(radio);\n }\n\n for (const radio of enabled) {\n if (await radio.isChecked()) return radio;\n }\n return enabled[0] ?? null;\n }\n\n /** The options, or a failure saying why an empty group has nothing to report. */\n private async radiosOrThrow(): Promise<WrRadioHarness[]> {\n const radios = await this.getRadios();\n if (!radios.length) {\n throw new Error(\n 'WrRadioGroupHarness: this group has no <wr-radio> children, and every group-level fact here is read ' +\n 'off them — the shared name and the disabled state reach the DOM through the radios, and a bound ' +\n '[name] / [disabled] leaves nothing on the group element to read instead.'\n );\n }\n return radios;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/fesm2022/ngwr-radio.mjs
CHANGED
|
@@ -113,10 +113,10 @@ class WrRadio {
|
|
|
113
113
|
onBlur() {
|
|
114
114
|
this.group?.markTouched();
|
|
115
115
|
}
|
|
116
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
117
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
116
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrRadio, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
117
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrRadio, isStandalone: true, selector: "wr-radio", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()", "attr.id": "null" } }, ngImport: i0, template: "<label class=\"wr-radio__label\" [for]=\"id()\">\n <input\n class=\"wr-radio__input\"\n type=\"radio\"\n [id]=\"id()\"\n [name]=\"name()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n (click)=\"onInputClick($event)\"\n (change)=\"onSelect()\"\n (blur)=\"onBlur()\"\n />\n\n <span class=\"wr-radio__dot\" aria-hidden=\"true\">\n @if (icon(); as iconName) {\n <wr-icon class=\"wr-radio__icon\" [name]=\"iconName\" />\n }\n </span>\n\n <span class=\"wr-radio__text\">\n <ng-content />\n </span>\n</label>\n", dependencies: [{ kind: "component", type: WrIcon, selector: "wr-icon", inputs: ["name"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
118
118
|
}
|
|
119
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
119
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrRadio, decorators: [{
|
|
120
120
|
type: Component,
|
|
121
121
|
args: [{ selector: 'wr-radio', encapsulation: ViewEncapsulation.None, host: {
|
|
122
122
|
'[class]': 'classes()',
|
|
@@ -203,8 +203,8 @@ class WrRadioGroup {
|
|
|
203
203
|
markTouched() {
|
|
204
204
|
this.touch.emit();
|
|
205
205
|
}
|
|
206
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
207
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
206
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrRadioGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
207
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: WrRadioGroup, isStandalone: true, selector: "wr-radio-group", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { attributes: { "role": "radiogroup" }, properties: { "attr.aria-readonly": "readonly() || null", "attr.aria-invalid": "fieldAria.ariaInvalid()", "attr.aria-describedby": "fieldAria.describedBy()" }, classAttribute: "wr-radio-group" }, providers: [
|
|
208
208
|
{
|
|
209
209
|
provide: WR_RADIO_GROUP,
|
|
210
210
|
// eslint-disable-next-line @angular-eslint/no-forward-ref
|
|
@@ -212,7 +212,7 @@ class WrRadioGroup {
|
|
|
212
212
|
},
|
|
213
213
|
], ngImport: i0, template: '<ng-content />', isInline: true, encapsulation: i0.ViewEncapsulation.None });
|
|
214
214
|
}
|
|
215
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
215
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrRadioGroup, decorators: [{
|
|
216
216
|
type: Component,
|
|
217
217
|
args: [{
|
|
218
218
|
selector: 'wr-radio-group',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-radio.mjs","sources":["../../../projects/lib/radio/tokens/radio-group.token.ts","../../../projects/lib/radio/radio.ts","../../../projects/lib/radio/radio.html","../../../projects/lib/radio/radio-group.ts","../../../projects/lib/radio/ngwr-radio.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, type Signal } from '@angular/core';\n\n/**\n * Contract a radio uses to talk to its parent `<wr-radio-group>`.\n *\n * @internal\n */\nexport interface WrRadioGroupContext {\n /** Shared `name` attribute applied to every native input in the group. */\n readonly name: Signal<string>;\n /** Currently selected value. */\n readonly value: Signal<unknown>;\n /** Whether the entire group is disabled. */\n readonly isDisabled: Signal<boolean>;\n /** Whether the entire group refuses edits while staying focusable. */\n readonly isReadonly: Signal<boolean>;\n /** Select the given value. */\n select(value: unknown): void;\n /**\n * Mark the group as touched (called on blur from any child). Named\n * `markTouched` rather than `touch` so it doesn't clash with the\n * `FormUiControl.touch` output the group now exposes for signal forms.\n */\n markTouched(): void;\n}\n\n/**\n * Token a `<wr-radio>` injects to participate in its parent `<wr-radio-group>`\n * — reads the selected value, shares the `name` attribute, and signals\n * selection / blur back up.\n *\n * @internal\n */\nexport const WR_RADIO_GROUP = new InjectionToken<WrRadioGroupContext>('WR_RADIO_GROUP');\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 { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, inject, input } from '@angular/core';\n\nimport { useConfigValue } from 'ngwr/config';\nimport { WrIcon, type WrIconName } from 'ngwr/icon';\nimport { randomId } from 'ngwr/utils';\n\nimport { WR_RADIO_GROUP } from './tokens';\n\n/**\n * Single-choice option. Must be a child of `<wr-radio-group>`.\n *\n * @example\n * ```html\n * <wr-radio-group [(ngModel)]=\"size\">\n * <wr-radio value=\"sm\">Small</wr-radio>\n * <wr-radio value=\"md\">Medium</wr-radio>\n * <wr-radio value=\"lg\">Large</wr-radio>\n * </wr-radio-group>\n * ```\n *\n * @see https://ngwr.dev/reference/components/radio\n */\nexport type WrRadioSize = 'sm' | 'md' | 'lg';\n\n@Component({\n selector: 'wr-radio',\n templateUrl: './radio.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n // The `id` input belongs to the native input, not to this element. A static\n // `id=\"x\"` in a consumer's template is written to the host as a plain\n // attribute AS WELL as fed to the input, which put two elements with the\n // same id in the document and made `<label for>` resolve through\n // `getElementById` to the host — not a labelable element, so `input.labels`\n // went from 1 to 0 and `document.getElementById(id)` returned the wrong node.\n '[attr.id]': 'null',\n },\n imports: [WrIcon],\n})\nexport class WrRadio {\n /**\n * Stable id used to associate the native input with its label. Lands on the\n * inner `<input>`; the host never keeps it.\n */\n readonly id = input<string>(randomId('wr-radio'));\n\n /** Value selected when this radio is checked. */\n readonly value = input.required<unknown>();\n\n /**\n * Optional icon name rendered inside the dot when checked, in place of the\n * default solid circle. Use any registered NGWR icon.\n */\n readonly icon = input<WrIconName | null>(null);\n\n /**\n * Control size — shares the `--wr-control-*` contract. Set per option (the\n * group has no `size`); unset falls back to the `radio.size` app default from\n * `provideWrConfig()`. @default 'md'\n */\n readonly size = input<WrRadioSize | null>(null);\n\n /** `size`, then the app config, then `md`. @internal */\n protected readonly resolvedSize = useConfigValue(this.size, c => c.radio?.size, 'md');\n\n /**\n * Disable just this option. The group can also be disabled as a whole via\n * `<wr-radio-group disabled>`; either source disables this radio.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n private readonly group = inject(WR_RADIO_GROUP, { optional: true });\n\n constructor() {\n if (!this.group) {\n throw new Error('[NGWR] <wr-radio> must be used inside <wr-radio-group>.');\n }\n }\n\n protected readonly name = computed(() => this.group?.name() ?? '');\n protected readonly checked = computed(() => this.group?.value() === this.value());\n /** Effective disabled — this option's own `disabled` or the group's. */\n protected readonly isDisabled = computed(() => this.disabled() || (this.group?.isDisabled() ?? false));\n\n /**\n * Readonly is a GROUP-level state — a single option cannot be read-only while\n * its siblings are not, because picking any of them rewrites the same value.\n * `aria-readonly` therefore lives on the `role=\"radiogroup\"` host and NOT here:\n * role `radio` does not support it.\n */\n protected readonly isReadonly = computed(() => this.group?.isReadonly() ?? false);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-radio'];\n const size = this.resolvedSize();\n if (size !== 'md') parts.push(`wr-radio--${size}`);\n if (this.checked()) parts.push('wr-radio--checked');\n if (this.isDisabled()) parts.push('wr-radio--disabled');\n else if (this.isReadonly()) parts.push('wr-radio--readonly');\n if (this.icon()) parts.push('wr-radio--has-icon');\n return parts.join(' ');\n });\n\n /**\n * Cancel the selection while the group is read-only. `change` never fires for\n * a click whose default was prevented, so this is the only guard the pointer\n * and Space paths need; `onSelect` keeps its own for a synthetic `change`.\n */\n protected onInputClick(event: Event): void {\n if (this.isReadonly()) event.preventDefault();\n }\n\n protected onSelect(): void {\n if (this.isDisabled() || this.isReadonly()) return;\n this.group?.select(this.value());\n }\n\n protected onBlur(): void {\n this.group?.markTouched();\n }\n}\n","<label class=\"wr-radio__label\" [for]=\"id()\">\n <input\n class=\"wr-radio__input\"\n type=\"radio\"\n [id]=\"id()\"\n [name]=\"name()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n (click)=\"onInputClick($event)\"\n (change)=\"onSelect()\"\n (blur)=\"onBlur()\"\n />\n\n <span class=\"wr-radio__dot\" aria-hidden=\"true\">\n @if (icon(); as iconName) {\n <wr-icon class=\"wr-radio__icon\" [name]=\"iconName\" />\n }\n </span>\n\n <span class=\"wr-radio__text\">\n <ng-content />\n </span>\n</label>\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 { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, forwardRef, input, model, output } from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useFormFieldAria } from 'ngwr/form';\nimport { randomId } from 'ngwr/utils';\n\nimport { WR_RADIO_GROUP, type WrRadioGroupContext } from './tokens';\n\n/**\n * Hosts a group of `<wr-radio>` children as a single-value selection.\n *\n * A signal-forms native control: it implements `FormValueControl<unknown>`, so\n * `[formField]` binds to its `value` model — the currently selected radio's\n * `value`. `[(value)]` works standalone. Classic `[(ngModel)]` / reactive forms\n * keep working through Angular's bridge.\n *\n * @example\n * ```html\n * <wr-radio-group [formField]=\"form.size\">\n * <wr-radio value=\"sm\">Small</wr-radio>\n * <wr-radio value=\"md\">Medium</wr-radio>\n * <wr-radio value=\"lg\">Large</wr-radio>\n * </wr-radio-group>\n * ```\n *\n * @see https://ngwr.dev/reference/components/radio\n */\n@Component({\n selector: 'wr-radio-group',\n template: '<ng-content />',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-radio-group',\n role: 'radiogroup',\n '[attr.aria-readonly]': 'readonly() || null',\n '[attr.aria-invalid]': 'fieldAria.ariaInvalid()',\n '[attr.aria-describedby]': 'fieldAria.describedBy()',\n },\n providers: [\n {\n provide: WR_RADIO_GROUP,\n // eslint-disable-next-line @angular-eslint/no-forward-ref\n useExisting: forwardRef(() => WrRadioGroup),\n },\n ],\n})\nexport class WrRadioGroup implements FormValueControl<unknown>, WrRadioGroupContext {\n /**\n * Shared `name` attribute. Defaults to a random id so multiple groups\n * on the same page don't collide. (Also the `FormUiControl.name` slot.)\n */\n readonly name = input<string>(randomId('wr-radio-group'));\n\n /**\n * Disable the whole group. Bound automatically from the field's disabled\n * state when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Refuse selection changes while every option stays focusable and the value\n * still submits. Bound automatically from the field's readonly state when used\n * with `[formField]`.\n *\n * Native radios ignore the `readonly` attribute, so the group cancels the\n * activation instead and mirrors the state as `aria-readonly`, which role\n * `radiogroup` supports.\n *\n * @default false\n */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** The surrounding `<wr-form-field>`'s error state. @internal */\n protected readonly fieldAria = useFormFieldAria();\n\n /** The selected radio's value. Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<unknown>(null);\n\n /** Emitted on blur from any child so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n /** Effective disabled state (WrRadioGroupContext). */\n readonly isDisabled = computed(() => this.disabled());\n\n /** Effective readonly state (WrRadioGroupContext). */\n readonly isReadonly = computed(() => this.readonly());\n\n // WrRadioGroupContext\n\n select(value: unknown): void {\n if (this.isDisabled() || this.isReadonly()) return;\n this.value.set(value);\n }\n\n markTouched(): void {\n this.touch.emit();\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-radio.mjs","sources":["../../../projects/lib/radio/tokens/radio-group.token.ts","../../../projects/lib/radio/radio.ts","../../../projects/lib/radio/radio.html","../../../projects/lib/radio/radio-group.ts","../../../projects/lib/radio/ngwr-radio.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, type Signal } from '@angular/core';\n\n/**\n * Contract a radio uses to talk to its parent `<wr-radio-group>`.\n *\n * @internal\n */\nexport interface WrRadioGroupContext {\n /** Shared `name` attribute applied to every native input in the group. */\n readonly name: Signal<string>;\n /** Currently selected value. */\n readonly value: Signal<unknown>;\n /** Whether the entire group is disabled. */\n readonly isDisabled: Signal<boolean>;\n /** Whether the entire group refuses edits while staying focusable. */\n readonly isReadonly: Signal<boolean>;\n /** Select the given value. */\n select(value: unknown): void;\n /**\n * Mark the group as touched (called on blur from any child). Named\n * `markTouched` rather than `touch` so it doesn't clash with the\n * `FormUiControl.touch` output the group now exposes for signal forms.\n */\n markTouched(): void;\n}\n\n/**\n * Token a `<wr-radio>` injects to participate in its parent `<wr-radio-group>`\n * — reads the selected value, shares the `name` attribute, and signals\n * selection / blur back up.\n *\n * @internal\n */\nexport const WR_RADIO_GROUP = new InjectionToken<WrRadioGroupContext>('WR_RADIO_GROUP');\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 { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, inject, input } from '@angular/core';\n\nimport { useConfigValue } from 'ngwr/config';\nimport { WrIcon, type WrIconName } from 'ngwr/icon';\nimport { randomId } from 'ngwr/utils';\n\nimport { WR_RADIO_GROUP } from './tokens';\n\n/**\n * Single-choice option. Must be a child of `<wr-radio-group>`.\n *\n * @example\n * ```html\n * <wr-radio-group [(ngModel)]=\"size\">\n * <wr-radio value=\"sm\">Small</wr-radio>\n * <wr-radio value=\"md\">Medium</wr-radio>\n * <wr-radio value=\"lg\">Large</wr-radio>\n * </wr-radio-group>\n * ```\n *\n * @see https://ngwr.dev/reference/components/radio\n */\nexport type WrRadioSize = 'sm' | 'md' | 'lg';\n\n@Component({\n selector: 'wr-radio',\n templateUrl: './radio.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n // The `id` input belongs to the native input, not to this element. A static\n // `id=\"x\"` in a consumer's template is written to the host as a plain\n // attribute AS WELL as fed to the input, which put two elements with the\n // same id in the document and made `<label for>` resolve through\n // `getElementById` to the host — not a labelable element, so `input.labels`\n // went from 1 to 0 and `document.getElementById(id)` returned the wrong node.\n '[attr.id]': 'null',\n },\n imports: [WrIcon],\n})\nexport class WrRadio {\n /**\n * Stable id used to associate the native input with its label. Lands on the\n * inner `<input>`; the host never keeps it.\n */\n readonly id = input<string>(randomId('wr-radio'));\n\n /** Value selected when this radio is checked. */\n readonly value = input.required<unknown>();\n\n /**\n * Optional icon name rendered inside the dot when checked, in place of the\n * default solid circle. Use any registered NGWR icon.\n */\n readonly icon = input<WrIconName | null>(null);\n\n /**\n * Control size — shares the `--wr-control-*` contract. Set per option (the\n * group has no `size`); unset falls back to the `radio.size` app default from\n * `provideWrConfig()`. @default 'md'\n */\n readonly size = input<WrRadioSize | null>(null);\n\n /** `size`, then the app config, then `md`. @internal */\n protected readonly resolvedSize = useConfigValue(this.size, c => c.radio?.size, 'md');\n\n /**\n * Disable just this option. The group can also be disabled as a whole via\n * `<wr-radio-group disabled>`; either source disables this radio.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n private readonly group = inject(WR_RADIO_GROUP, { optional: true });\n\n constructor() {\n if (!this.group) {\n throw new Error('[NGWR] <wr-radio> must be used inside <wr-radio-group>.');\n }\n }\n\n protected readonly name = computed(() => this.group?.name() ?? '');\n protected readonly checked = computed(() => this.group?.value() === this.value());\n /** Effective disabled — this option's own `disabled` or the group's. */\n protected readonly isDisabled = computed(() => this.disabled() || (this.group?.isDisabled() ?? false));\n\n /**\n * Readonly is a GROUP-level state — a single option cannot be read-only while\n * its siblings are not, because picking any of them rewrites the same value.\n * `aria-readonly` therefore lives on the `role=\"radiogroup\"` host and NOT here:\n * role `radio` does not support it.\n */\n protected readonly isReadonly = computed(() => this.group?.isReadonly() ?? false);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-radio'];\n const size = this.resolvedSize();\n if (size !== 'md') parts.push(`wr-radio--${size}`);\n if (this.checked()) parts.push('wr-radio--checked');\n if (this.isDisabled()) parts.push('wr-radio--disabled');\n else if (this.isReadonly()) parts.push('wr-radio--readonly');\n if (this.icon()) parts.push('wr-radio--has-icon');\n return parts.join(' ');\n });\n\n /**\n * Cancel the selection while the group is read-only. `change` never fires for\n * a click whose default was prevented, so this is the only guard the pointer\n * and Space paths need; `onSelect` keeps its own for a synthetic `change`.\n */\n protected onInputClick(event: Event): void {\n if (this.isReadonly()) event.preventDefault();\n }\n\n protected onSelect(): void {\n if (this.isDisabled() || this.isReadonly()) return;\n this.group?.select(this.value());\n }\n\n protected onBlur(): void {\n this.group?.markTouched();\n }\n}\n","<label class=\"wr-radio__label\" [for]=\"id()\">\n <input\n class=\"wr-radio__input\"\n type=\"radio\"\n [id]=\"id()\"\n [name]=\"name()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n (click)=\"onInputClick($event)\"\n (change)=\"onSelect()\"\n (blur)=\"onBlur()\"\n />\n\n <span class=\"wr-radio__dot\" aria-hidden=\"true\">\n @if (icon(); as iconName) {\n <wr-icon class=\"wr-radio__icon\" [name]=\"iconName\" />\n }\n </span>\n\n <span class=\"wr-radio__text\">\n <ng-content />\n </span>\n</label>\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 { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, forwardRef, input, model, output } from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useFormFieldAria } from 'ngwr/form';\nimport { randomId } from 'ngwr/utils';\n\nimport { WR_RADIO_GROUP, type WrRadioGroupContext } from './tokens';\n\n/**\n * Hosts a group of `<wr-radio>` children as a single-value selection.\n *\n * A signal-forms native control: it implements `FormValueControl<unknown>`, so\n * `[formField]` binds to its `value` model — the currently selected radio's\n * `value`. `[(value)]` works standalone. Classic `[(ngModel)]` / reactive forms\n * keep working through Angular's bridge.\n *\n * @example\n * ```html\n * <wr-radio-group [formField]=\"form.size\">\n * <wr-radio value=\"sm\">Small</wr-radio>\n * <wr-radio value=\"md\">Medium</wr-radio>\n * <wr-radio value=\"lg\">Large</wr-radio>\n * </wr-radio-group>\n * ```\n *\n * @see https://ngwr.dev/reference/components/radio\n */\n@Component({\n selector: 'wr-radio-group',\n template: '<ng-content />',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-radio-group',\n role: 'radiogroup',\n '[attr.aria-readonly]': 'readonly() || null',\n '[attr.aria-invalid]': 'fieldAria.ariaInvalid()',\n '[attr.aria-describedby]': 'fieldAria.describedBy()',\n },\n providers: [\n {\n provide: WR_RADIO_GROUP,\n // eslint-disable-next-line @angular-eslint/no-forward-ref\n useExisting: forwardRef(() => WrRadioGroup),\n },\n ],\n})\nexport class WrRadioGroup implements FormValueControl<unknown>, WrRadioGroupContext {\n /**\n * Shared `name` attribute. Defaults to a random id so multiple groups\n * on the same page don't collide. (Also the `FormUiControl.name` slot.)\n */\n readonly name = input<string>(randomId('wr-radio-group'));\n\n /**\n * Disable the whole group. Bound automatically from the field's disabled\n * state when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Refuse selection changes while every option stays focusable and the value\n * still submits. Bound automatically from the field's readonly state when used\n * with `[formField]`.\n *\n * Native radios ignore the `readonly` attribute, so the group cancels the\n * activation instead and mirrors the state as `aria-readonly`, which role\n * `radiogroup` supports.\n *\n * @default false\n */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** The surrounding `<wr-form-field>`'s error state. @internal */\n protected readonly fieldAria = useFormFieldAria();\n\n /** The selected radio's value. Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<unknown>(null);\n\n /** Emitted on blur from any child so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n /** Effective disabled state (WrRadioGroupContext). */\n readonly isDisabled = computed(() => this.disabled());\n\n /** Effective readonly state (WrRadioGroupContext). */\n readonly isReadonly = computed(() => this.readonly());\n\n // WrRadioGroupContext\n\n select(value: unknown): void {\n if (this.isDisabled() || this.isReadonly()) return;\n this.value.set(value);\n }\n\n markTouched(): void {\n this.touch.emit();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-rating-testing.mjs","sources":["../../../projects/lib/rating/testing/wr-rating-item-harness.ts","../../../projects/lib/rating/testing/wr-rating-harness.ts","../../../projects/lib/rating/testing/ngwr-rating-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 { WrRatingItemHarnessFilters } from './interfaces';\n\n/** The star's own box, once it is known to have one. */\ninterface WrRatingItemBox {\n readonly left: number;\n readonly top: number;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Test harness for one star of a `<wr-rating>`.\n *\n * A star is invisible to a screen reader — the row above it is the `slider` and\n * owns the whole accessible story — but it carries the state the eye reads: how\n * far it is filled, whether it is still a click target, and, while the pointer is\n * over the row, the PREVIEW rather than the committed value. That is the reason\n * this exists as a harness of its own.\n *\n * The fill is read from `--wr-rating-fill`, the per-star custom property the\n * component sets and the stylesheet clips the filled glyph with — a public token,\n * and the only place a half star exists in the DOM.\n *\n * @example\n * ```ts\n * const rating = await loader.getHarness(WrRatingHarness.with({ label: 'Overall' }));\n * const [, , third] = await rating.getItems();\n *\n * expect(await third.isPartiallyFilled()).toBe(true);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrRatingItemHarness extends ComponentHarness {\n static hostSelector = '.wr-rating__slot';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrRatingItemHarnessFilters = {}): HarnessPredicate<WrRatingItemHarness> {\n return new HarnessPredicate(WrRatingItemHarness, options)\n .addOption('fill', options.fill, async (harness, fill) => (await harness.getFill()) === fill)\n .addOption(\n 'interactive',\n options.interactive,\n async (harness, interactive) => (await harness.isInteractive()) === interactive\n );\n }\n\n /**\n * How much of this star is painted, `0` to `1`.\n *\n * While the pointer is over the rating this is the PREVIEW — the component\n * paints `hoverValue ?? value`, so a hovered star reads full before anything is\n * committed. `WrRatingHarness.getValue()` is the committed one.\n */\n async getFill(): Promise<number> {\n const raw = (await (await this.host()).getCssValue('--wr-rating-fill')).trim();\n const fill = Number.parseFloat(raw);\n if (Number.isNaN(fill)) {\n throw new Error(\n `WrRatingItemHarness: could not read this star's fill — \\`--wr-rating-fill\\` computed to \"${raw}\". ` +\n 'The rating sets it per star; an environment that drops custom properties cannot answer this.'\n );\n }\n return fill;\n }\n\n /** Whether the star is painted whole. */\n async isFilled(): Promise<boolean> {\n return (await this.getFill()) >= 1;\n }\n\n /**\n * Whether the star is painted part-way — a half star.\n *\n * Any fractional value paints one, not only `step=\"0.5\"`: the step governs the\n * keyboard and the pointer, but a fraction written straight into `value` is only\n * clamped to `[0, count]`, never snapped — so `[value]=\"3.5\"` half-fills star four\n * on a whole-star rating.\n */\n async isPartiallyFilled(): Promise<boolean> {\n const fill = await this.getFill();\n return fill > 0 && fill < 1;\n }\n\n /** Whether the star still takes the pointer — a readonly or disabled rating drops the modifier. */\n async isInteractive(): Promise<boolean> {\n return (await this.host()).hasClass('wr-rating__slot--interactive');\n }\n\n /**\n * Click this star, picking it whole — star three sets the value to `3`.\n *\n * Clicking the value the rating ALREADY holds clears it instead; that is the\n * component's toggle-off, not an accident of this harness.\n *\n * Needs real layout: the component snaps the value from where the pointer sits\n * inside the star, so this aims at the star's inline-END edge. In jsdom every\n * element is 0×0 and a pointer position over one cannot mean anything — this\n * throws rather than committing the `NaN` such a click computes. Reach for\n * `WrRatingHarness.setValue()` there, which drives the keyboard.\n */\n async click(): Promise<void> {\n const { width, height } = await this.box();\n await (await this.host()).click(await this.inlineEndX(width), height / 2);\n }\n\n /**\n * Click the leading half of this star — `2.5` on star three.\n *\n * On a whole-star rating this is the same as {@link click}: the component snaps\n * UP to its `step`, so half of star three is still three stars. Carries the same\n * layout requirement.\n */\n async clickHalf(): Promise<void> {\n const { width, height } = await this.box();\n await (await this.host()).click(width / 2, height / 2);\n }\n\n /**\n * Move the pointer onto this star, previewing it whole.\n *\n * Sends `mousemove` rather than the `TestElement.hover()` pair: the rating\n * tracks the pointer's x across a star to decide whether it is over the half or\n * the whole, so `mouseenter` alone tells it nothing and leaves the preview\n * unchanged. `WrRatingHarness.unhover()` ends it. Carries the same layout\n * requirement as {@link click}.\n */\n async hover(): Promise<void> {\n const { left, top, width, height } = await this.box();\n const clientX = left + (await this.inlineEndX(width));\n await (await this.host()).dispatchEvent('mousemove', { clientX, clientY: top + height / 2 });\n }\n\n /**\n * The x, relative to the star's own box, that lands on its inline-END edge —\n * the far side of the star, where the rating reads a whole one.\n *\n * The component measures the pointer from the star's inline-START edge, which\n * is the right one once `dir=\"rtl\"` mirrors the row; aiming at the physical\n * right edge there reads ratio 0 and picks one star FEWER. So the side is asked\n * for rather than assumed: `direction` is a computed style, which the harness\n * can read in every environment — jsdom included, where it inherits from an\n * ancestor `dir` attribute like any other browser.\n */\n private async inlineEndX(width: number): Promise<number> {\n const direction = await (await this.host()).getCssValue('direction');\n return direction === 'rtl' ? 0 : width;\n }\n\n /** The star's box, or a failure that names the reason it has none. */\n private async box(): Promise<WrRatingItemBox> {\n const { left, top, width, height } = await (await this.host()).getDimensions();\n if (!width) {\n throw new Error(\n 'WrRatingItemHarness: this star has no layout, so a pointer position over it means nothing — the ' +\n 'rating reads the value off where the pointer sits INSIDE the star. Every element is 0×0 in jsdom: ' +\n 'use `WrRatingHarness.setValue()`, which drives the keyboard, or stub `getBoundingClientRect`.'\n );\n }\n return { left, top, width, height };\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, TestKey } from '@angular/cdk/testing';\n\nimport type { WrRatingSize } from 'ngwr/rating';\n\nimport type { WrRatingHarnessFilters, WrRatingItemHarnessFilters } from './interfaces';\nimport { WrRatingItemHarness } from './wr-rating-item-harness';\n\n/**\n * Test harness for `<wr-rating>` — the root of a two-class family, since a star\n * carries state of its own: {@link WrRatingItemHarness}.\n *\n * The stars are decoration. Everything a screen reader is told lives on the\n * `.wr-rating__row` inside them, which is the `slider`: `aria-valuenow` is the\n * committed value, `aria-valuemax` the top of the range, and the row — not the\n * host — is the tab stop. So this harness reads the row and, for writes, drives\n * its KEYBOARD: arrows step by the rating's own `step`, `Home` / `End` jump to\n * the ends, `Delete` clears. That is the accessible path, and it is the only one a\n * unit test can take unaided, because the pointer path snaps the value from where\n * the cursor sits inside a star — which needs layout jsdom does not have, so a\n * spec that wants it has to stub the star's box (see\n * {@link WrRatingItemHarness.click}).\n *\n * @example\n * ```ts\n * const rating = await loader.getHarness(WrRatingHarness.with({ label: 'Overall' }));\n *\n * await rating.setValue(4);\n * expect(await rating.getValue()).toBe(4);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrRatingHarness extends ComponentHarness {\n static hostSelector = 'wr-rating';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrRatingHarnessFilters = {}): HarnessPredicate<WrRatingHarness> {\n return new HarnessPredicate(WrRatingHarness, options)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))\n .addOption('value', options.value, async (harness, value) => (await harness.getValue()) === value)\n .addOption('readonly', options.readonly, async (harness, readonly) => (await harness.isReadonly()) === readonly)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly row = this.locatorFor('.wr-rating__row');\n\n /**\n * What the control announces itself as — `slider`.\n *\n * Not a `radiogroup`: the value is a number on a continuum with a `step`, the\n * arrows move it and `Home` / `End` bound it, and that is a slider's contract\n * rather than a group of one-of-N choices.\n */\n async getRole(): Promise<string | null> {\n return (await this.row()).getAttribute('role');\n }\n\n /**\n * The rating's accessible name, from `ariaLabel` or the i18n catalogue.\n *\n * A rating projects no text, so this is the only name it has — and it sits on\n * the row, not on the host, because the row is what carries the role.\n */\n async getLabel(): Promise<string | null> {\n return (await this.row()).getAttribute('aria-label');\n }\n\n /**\n * The committed value, from `aria-valuenow` — what a screen reader is told, not\n * what is painted, which during a hover is the preview instead.\n *\n * A CLEARED rating is announced as `0`, exactly like a rating of zero stars:\n * `value` is `number | null` and ARIA has no null. So `0` here means \"zero or\n * nothing\" — assert the bound model when the difference matters, and see\n * {@link clear}.\n */\n async getValue(): Promise<number> {\n const raw = await (await this.row()).getAttribute('aria-valuenow');\n const value = Number.parseFloat(raw ?? '');\n if (Number.isNaN(value)) {\n throw new Error(`WrRatingHarness: the slider announces aria-valuenow=\"${raw}\", which is not a number.`);\n }\n return value;\n }\n\n /** The top of the range, from `aria-valuemax` — the `count` of stars. */\n async getMax(): Promise<number> {\n const raw = await (await this.row()).getAttribute('aria-valuemax');\n return Number.parseFloat(raw ?? '');\n }\n\n /** How many stars are painted. Should always agree with {@link getMax}. */\n async getCount(): Promise<number> {\n return (await this.getItems()).length;\n }\n\n /** The stars, left to right. */\n async getItems(filters: WrRatingItemHarnessFilters = {}): Promise<WrRatingItemHarness[]> {\n return this.locatorForAll(WrRatingItemHarness.with(filters))();\n }\n\n /**\n * Each star's fill ratio, left to right — `[1, 1, 0.5, 0, 0]` for two and a\n * half stars out of five.\n *\n * This is what the eye sees, so it follows the hover preview while there is one;\n * {@link getValue} stays on the committed value throughout.\n */\n async getFills(): Promise<number[]> {\n const items = await this.getItems();\n return Promise.all(items.map(item => item.getFill()));\n }\n\n /** The control size. `'md'` is the default and paints no modifier of its own. */\n async getSize(): Promise<WrRatingSize> {\n const host = await this.host();\n if (await host.hasClass('wr-rating--sm')) return 'sm';\n if (await host.hasClass('wr-rating--lg')) return 'lg';\n return 'md';\n }\n\n /**\n * Whether the rating displays a value it will not let you change.\n *\n * Read from `aria-readonly` rather than from the `wr-rating--readonly` class:\n * both are public, but the attribute is the one a screen reader acts on, and a\n * rating that looks read-only without saying so is the bug worth catching.\n */\n async isReadonly(): Promise<boolean> {\n return (await (await this.row()).getAttribute('aria-readonly')) === 'true';\n }\n\n /** Whether the rating refuses interaction — `aria-disabled`, for the same reason. */\n async isDisabled(): Promise<boolean> {\n return (await (await this.row()).getAttribute('aria-disabled')) === 'true';\n }\n\n /**\n * Whether the rating is a tab stop.\n *\n * `disabled` only, never `readonly` — and the distinction is deliberate, not\n * incidental. A disabled control leaves the tab order; a READ-ONLY one stays\n * in it, keeps announcing its value, and still submits, which is the whole\n * difference between \"you may not\" and \"there is nothing here\". The component\n * records that driving this off an `interactive()` that folded the two\n * together made `readonly` unreachable, with an `aria-describedby` pointing\n * at an element nobody could focus.\n *\n * This docblock used to describe exactly that fixed bug — \"false while\n * readonly or disabled\" — so a reader trusting it would have asserted the\n * behaviour the component was changed to stop having.\n */\n async isFocusable(): Promise<boolean> {\n const tabIndex = await (await this.row()).getAttribute('tabindex');\n return Number.parseInt(tabIndex ?? '-1', 10) >= 0;\n }\n\n /**\n * Set the rating, from the keyboard.\n *\n * `Home` first, then one `ArrowUp` per step — the step is not in the DOM, so\n * the only honest way to reach a value is to walk to it and watch where the\n * rating lands. (The top of the range takes `End` instead of walking there.) A\n * host counting value changes therefore sees the intermediate ones, which is what\n * a keyboard user produces too.\n *\n * The BLOCK-axis arrow, deliberately: the stars lie on the inline axis, so\n * `ArrowRight` raises the value in LTR and lowers it under `dir=\"rtl\"`, where\n * the arrows follow visual order. `ArrowUp` means \"more\" in both, so a spec\n * written against this harness reads the same either way.\n *\n * Throws rather than settling for a near miss: on a value outside `[0, max]`, on\n * one the step cannot land on (`2.5` on a whole-star rating), and on a rating\n * that took no input at all because it is readonly or disabled.\n *\n * `setValue(0)` commits ZERO. It is {@link clear} that returns the model to\n * `null`, and the two are indistinguishable from the DOM.\n */\n async setValue(value: number): Promise<void> {\n const max = await this.getMax();\n if (!Number.isFinite(value) || value < 0 || value > max) {\n throw new Error(`WrRatingHarness: ${value} is outside this rating's range of [0, ${max}].`);\n }\n\n const row = await this.row();\n await row.sendKeys(TestKey.HOME);\n\n if (value === max) {\n await row.sendKeys(TestKey.END);\n } else {\n let current = await this.getValue();\n while (current < value) {\n await row.sendKeys(TestKey.UP_ARROW);\n const next = await this.getValue();\n // Standing still means the rating is not listening; the check below says why.\n if (next === current) break;\n if (next > value) {\n throw new Error(\n `WrRatingHarness: this rating cannot land on ${value} — one step took it from ${current} to ` +\n `${next}, so ${value} is not a multiple of its \\`step\\`.`\n );\n }\n current = next;\n }\n }\n\n const landed = await this.getValue();\n if (landed !== value) {\n throw new Error(\n `WrRatingHarness: asked for ${value}, but the rating reads ${landed} — a readonly or disabled ` +\n 'rating takes no input.'\n );\n }\n }\n\n /**\n * Clear the rating, the way `Delete` does — the bound value becomes `null`, not\n * `0`.\n *\n * The slider then announces `aria-valuenow=\"0\"` and every star empties, so\n * {@link getValue} cannot tell you this happened. Assert the model.\n */\n async clear(): Promise<void> {\n await (await this.row()).sendKeys(TestKey.DELETE);\n }\n\n /**\n * Bump the rating up by one step — one `ArrowUp`, the arrow that means \"more\"\n * in both reading directions (see {@link setValue}).\n *\n * The step is the rating's, not this harness's, so this is also how a spec finds\n * out what it is: clear the rating, step up once, read the value.\n * Already at the top, this does nothing; the value clamps rather than wrapping.\n */\n async stepUp(): Promise<void> {\n await (await this.row()).sendKeys(TestKey.UP_ARROW);\n }\n\n /** Bump the rating down by one step — one `ArrowDown`. Clamps at `0`. */\n async stepDown(): Promise<void> {\n await (await this.row()).sendKeys(TestKey.DOWN_ARROW);\n }\n\n /**\n * End a hover preview, dropping the stars back to the committed value.\n *\n * It lives here rather than on the star because the component listens for\n * `mouseleave` on the ROW: leaving one star for the next is not leaving the\n * rating.\n */\n async unhover(): Promise<void> {\n await (await this.row()).mouseAway();\n }\n\n /** Move keyboard focus to the rating — onto the row, which is the tab stop. */\n async focus(): Promise<void> {\n return (await this.row()).focus();\n }\n\n /** Blur the rating. This is what emits `touch`, so a bound field marks itself touched. */\n async blur(): Promise<void> {\n return (await this.row()).blur();\n }\n\n async isFocused(): Promise<boolean> {\n return (await this.row()).isFocused();\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-rating-testing.mjs","sources":["../../../projects/lib/rating/testing/wr-rating-item-harness.ts","../../../projects/lib/rating/testing/wr-rating-harness.ts","../../../projects/lib/rating/testing/ngwr-rating-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 { WrRatingItemHarnessFilters } from './interfaces';\n\n/** The star's own box, once it is known to have one. */\ninterface WrRatingItemBox {\n readonly left: number;\n readonly top: number;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Test harness for one star of a `<wr-rating>`.\n *\n * A star is invisible to a screen reader — the row above it is the `slider` and\n * owns the whole accessible story — but it carries the state the eye reads: how\n * far it is filled, whether it is still a click target, and, while the pointer is\n * over the row, the PREVIEW rather than the committed value. That is the reason\n * this exists as a harness of its own.\n *\n * The fill is read from `--wr-rating-fill`, the per-star custom property the\n * component sets and the stylesheet clips the filled glyph with — a public token,\n * and the only place a half star exists in the DOM.\n *\n * @example\n * ```ts\n * const rating = await loader.getHarness(WrRatingHarness.with({ label: 'Overall' }));\n * const [, , third] = await rating.getItems();\n *\n * expect(await third.isPartiallyFilled()).toBe(true);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrRatingItemHarness extends ComponentHarness {\n static hostSelector = '.wr-rating__slot';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrRatingItemHarnessFilters = {}): HarnessPredicate<WrRatingItemHarness> {\n return new HarnessPredicate(WrRatingItemHarness, options)\n .addOption('fill', options.fill, async (harness, fill) => (await harness.getFill()) === fill)\n .addOption(\n 'interactive',\n options.interactive,\n async (harness, interactive) => (await harness.isInteractive()) === interactive\n );\n }\n\n /**\n * How much of this star is painted, `0` to `1`.\n *\n * While the pointer is over the rating this is the PREVIEW — the component\n * paints `hoverValue ?? value`, so a hovered star reads full before anything is\n * committed. `WrRatingHarness.getValue()` is the committed one.\n */\n async getFill(): Promise<number> {\n const raw = (await (await this.host()).getCssValue('--wr-rating-fill')).trim();\n const fill = Number.parseFloat(raw);\n if (Number.isNaN(fill)) {\n throw new Error(\n `WrRatingItemHarness: could not read this star's fill — \\`--wr-rating-fill\\` computed to \"${raw}\". ` +\n 'The rating sets it per star; an environment that drops custom properties cannot answer this.'\n );\n }\n return fill;\n }\n\n /** Whether the star is painted whole. */\n async isFilled(): Promise<boolean> {\n return (await this.getFill()) >= 1;\n }\n\n /**\n * Whether the star is painted part-way — a half star.\n *\n * Any fractional value paints one, not only `step=\"0.5\"`: the step governs the\n * keyboard and the pointer, but a fraction written straight into `value` is only\n * clamped to `[0, count]`, never snapped — so `[value]=\"3.5\"` half-fills star four\n * on a whole-star rating.\n */\n async isPartiallyFilled(): Promise<boolean> {\n const fill = await this.getFill();\n return fill > 0 && fill < 1;\n }\n\n /** Whether the star still takes the pointer — a readonly or disabled rating drops the modifier. */\n async isInteractive(): Promise<boolean> {\n return (await this.host()).hasClass('wr-rating__slot--interactive');\n }\n\n /**\n * Click this star, picking it whole — star three sets the value to `3`.\n *\n * Clicking the value the rating ALREADY holds clears it instead; that is the\n * component's toggle-off, not an accident of this harness.\n *\n * Needs real layout: the component snaps the value from where the pointer sits\n * inside the star, so this aims at the star's inline-END edge. In jsdom every\n * element is 0×0 and a pointer position over one cannot mean anything — this\n * throws rather than committing the `NaN` such a click computes. Reach for\n * `WrRatingHarness.setValue()` there, which drives the keyboard.\n */\n async click(): Promise<void> {\n const { width, height } = await this.box();\n await (await this.host()).click(await this.inlineEndX(width), height / 2);\n }\n\n /**\n * Click the leading half of this star — `2.5` on star three.\n *\n * On a whole-star rating this is the same as {@link click}: the component snaps\n * UP to its `step`, so half of star three is still three stars. Carries the same\n * layout requirement.\n */\n async clickHalf(): Promise<void> {\n const { width, height } = await this.box();\n await (await this.host()).click(width / 2, height / 2);\n }\n\n /**\n * Move the pointer onto this star, previewing it whole.\n *\n * Sends `mousemove` rather than the `TestElement.hover()` pair: the rating\n * tracks the pointer's x across a star to decide whether it is over the half or\n * the whole, so `mouseenter` alone tells it nothing and leaves the preview\n * unchanged. `WrRatingHarness.unhover()` ends it. Carries the same layout\n * requirement as {@link click}.\n */\n async hover(): Promise<void> {\n const { left, top, width, height } = await this.box();\n const clientX = left + (await this.inlineEndX(width));\n await (await this.host()).dispatchEvent('mousemove', { clientX, clientY: top + height / 2 });\n }\n\n /**\n * The x, relative to the star's own box, that lands on its inline-END edge —\n * the far side of the star, where the rating reads a whole one.\n *\n * The component measures the pointer from the star's inline-START edge, which\n * is the right one once `dir=\"rtl\"` mirrors the row; aiming at the physical\n * right edge there reads ratio 0 and picks one star FEWER. So the side is asked\n * for rather than assumed: `direction` is a computed style, which the harness\n * can read in every environment — jsdom included, where it inherits from an\n * ancestor `dir` attribute like any other browser.\n */\n private async inlineEndX(width: number): Promise<number> {\n const direction = await (await this.host()).getCssValue('direction');\n return direction === 'rtl' ? 0 : width;\n }\n\n /** The star's box, or a failure that names the reason it has none. */\n private async box(): Promise<WrRatingItemBox> {\n const { left, top, width, height } = await (await this.host()).getDimensions();\n if (!width) {\n throw new Error(\n 'WrRatingItemHarness: this star has no layout, so a pointer position over it means nothing — the ' +\n 'rating reads the value off where the pointer sits INSIDE the star. Every element is 0×0 in jsdom: ' +\n 'use `WrRatingHarness.setValue()`, which drives the keyboard, or stub `getBoundingClientRect`.'\n );\n }\n return { left, top, width, height };\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, TestKey } from '@angular/cdk/testing';\n\nimport type { WrRatingSize } from 'ngwr/rating';\n\nimport type { WrRatingHarnessFilters, WrRatingItemHarnessFilters } from './interfaces';\nimport { WrRatingItemHarness } from './wr-rating-item-harness';\n\n/**\n * Test harness for `<wr-rating>` — the root of a two-class family, since a star\n * carries state of its own: {@link WrRatingItemHarness}.\n *\n * The stars are decoration. Everything a screen reader is told lives on the\n * `.wr-rating__row` inside them, which is the `slider`: `aria-valuenow` is the\n * committed value, `aria-valuemax` the top of the range, and the row — not the\n * host — is the tab stop. So this harness reads the row and, for writes, drives\n * its KEYBOARD: arrows step by the rating's own `step`, `Home` / `End` jump to\n * the ends, `Delete` clears. That is the accessible path, and it is the only one a\n * unit test can take unaided, because the pointer path snaps the value from where\n * the cursor sits inside a star — which needs layout jsdom does not have, so a\n * spec that wants it has to stub the star's box (see\n * {@link WrRatingItemHarness.click}).\n *\n * @example\n * ```ts\n * const rating = await loader.getHarness(WrRatingHarness.with({ label: 'Overall' }));\n *\n * await rating.setValue(4);\n * expect(await rating.getValue()).toBe(4);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrRatingHarness extends ComponentHarness {\n static hostSelector = 'wr-rating';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrRatingHarnessFilters = {}): HarnessPredicate<WrRatingHarness> {\n return new HarnessPredicate(WrRatingHarness, options)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))\n .addOption('value', options.value, async (harness, value) => (await harness.getValue()) === value)\n .addOption('readonly', options.readonly, async (harness, readonly) => (await harness.isReadonly()) === readonly)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly row = this.locatorFor('.wr-rating__row');\n\n /**\n * What the control announces itself as — `slider`.\n *\n * Not a `radiogroup`: the value is a number on a continuum with a `step`, the\n * arrows move it and `Home` / `End` bound it, and that is a slider's contract\n * rather than a group of one-of-N choices.\n */\n async getRole(): Promise<string | null> {\n return (await this.row()).getAttribute('role');\n }\n\n /**\n * The rating's accessible name, from `ariaLabel` or the i18n catalogue.\n *\n * A rating projects no text, so this is the only name it has — and it sits on\n * the row, not on the host, because the row is what carries the role.\n */\n async getLabel(): Promise<string | null> {\n return (await this.row()).getAttribute('aria-label');\n }\n\n /**\n * The committed value, from `aria-valuenow` — what a screen reader is told, not\n * what is painted, which during a hover is the preview instead.\n *\n * A CLEARED rating is announced as `0`, exactly like a rating of zero stars:\n * `value` is `number | null` and ARIA has no null. So `0` here means \"zero or\n * nothing\" — assert the bound model when the difference matters, and see\n * {@link clear}.\n */\n async getValue(): Promise<number> {\n const raw = await (await this.row()).getAttribute('aria-valuenow');\n const value = Number.parseFloat(raw ?? '');\n if (Number.isNaN(value)) {\n throw new Error(`WrRatingHarness: the slider announces aria-valuenow=\"${raw}\", which is not a number.`);\n }\n return value;\n }\n\n /** The top of the range, from `aria-valuemax` — the `count` of stars. */\n async getMax(): Promise<number> {\n const raw = await (await this.row()).getAttribute('aria-valuemax');\n return Number.parseFloat(raw ?? '');\n }\n\n /** How many stars are painted. Should always agree with {@link getMax}. */\n async getCount(): Promise<number> {\n return (await this.getItems()).length;\n }\n\n /** The stars, left to right. */\n async getItems(filters: WrRatingItemHarnessFilters = {}): Promise<WrRatingItemHarness[]> {\n return this.locatorForAll(WrRatingItemHarness.with(filters))();\n }\n\n /**\n * Each star's fill ratio, left to right — `[1, 1, 0.5, 0, 0]` for two and a\n * half stars out of five.\n *\n * This is what the eye sees, so it follows the hover preview while there is one;\n * {@link getValue} stays on the committed value throughout.\n */\n async getFills(): Promise<number[]> {\n const items = await this.getItems();\n return Promise.all(items.map(item => item.getFill()));\n }\n\n /** The control size. `'md'` is the default and paints no modifier of its own. */\n async getSize(): Promise<WrRatingSize> {\n const host = await this.host();\n if (await host.hasClass('wr-rating--sm')) return 'sm';\n if (await host.hasClass('wr-rating--lg')) return 'lg';\n return 'md';\n }\n\n /**\n * Whether the rating displays a value it will not let you change.\n *\n * Read from `aria-readonly` rather than from the `wr-rating--readonly` class:\n * both are public, but the attribute is the one a screen reader acts on, and a\n * rating that looks read-only without saying so is the bug worth catching.\n */\n async isReadonly(): Promise<boolean> {\n return (await (await this.row()).getAttribute('aria-readonly')) === 'true';\n }\n\n /** Whether the rating refuses interaction — `aria-disabled`, for the same reason. */\n async isDisabled(): Promise<boolean> {\n return (await (await this.row()).getAttribute('aria-disabled')) === 'true';\n }\n\n /**\n * Whether the rating is a tab stop.\n *\n * `disabled` only, never `readonly` — and the distinction is deliberate, not\n * incidental. A disabled control leaves the tab order; a READ-ONLY one stays\n * in it, keeps announcing its value, and still submits, which is the whole\n * difference between \"you may not\" and \"there is nothing here\". The component\n * records that driving this off an `interactive()` that folded the two\n * together made `readonly` unreachable, with an `aria-describedby` pointing\n * at an element nobody could focus.\n *\n * This docblock used to describe exactly that fixed bug — \"false while\n * readonly or disabled\" — so a reader trusting it would have asserted the\n * behaviour the component was changed to stop having.\n */\n async isFocusable(): Promise<boolean> {\n const tabIndex = await (await this.row()).getAttribute('tabindex');\n return Number.parseInt(tabIndex ?? '-1', 10) >= 0;\n }\n\n /**\n * Set the rating, from the keyboard.\n *\n * `Home` first, then one `ArrowUp` per step — the step is not in the DOM, so\n * the only honest way to reach a value is to walk to it and watch where the\n * rating lands. (The top of the range takes `End` instead of walking there.) A\n * host counting value changes therefore sees the intermediate ones, which is what\n * a keyboard user produces too.\n *\n * The BLOCK-axis arrow, deliberately: the stars lie on the inline axis, so\n * `ArrowRight` raises the value in LTR and lowers it under `dir=\"rtl\"`, where\n * the arrows follow visual order. `ArrowUp` means \"more\" in both, so a spec\n * written against this harness reads the same either way.\n *\n * Throws rather than settling for a near miss: on a value outside `[0, max]`, on\n * one the step cannot land on (`2.5` on a whole-star rating), and on a rating\n * that took no input at all because it is readonly or disabled.\n *\n * `setValue(0)` commits ZERO. It is {@link clear} that returns the model to\n * `null`, and the two are indistinguishable from the DOM.\n */\n async setValue(value: number): Promise<void> {\n const max = await this.getMax();\n if (!Number.isFinite(value) || value < 0 || value > max) {\n throw new Error(`WrRatingHarness: ${value} is outside this rating's range of [0, ${max}].`);\n }\n\n const row = await this.row();\n await row.sendKeys(TestKey.HOME);\n\n if (value === max) {\n await row.sendKeys(TestKey.END);\n } else {\n let current = await this.getValue();\n while (current < value) {\n await row.sendKeys(TestKey.UP_ARROW);\n const next = await this.getValue();\n // Standing still means the rating is not listening; the check below says why.\n if (next === current) break;\n if (next > value) {\n throw new Error(\n `WrRatingHarness: this rating cannot land on ${value} — one step took it from ${current} to ` +\n `${next}, so ${value} is not a multiple of its \\`step\\`.`\n );\n }\n current = next;\n }\n }\n\n const landed = await this.getValue();\n if (landed !== value) {\n throw new Error(\n `WrRatingHarness: asked for ${value}, but the rating reads ${landed} — a readonly or disabled ` +\n 'rating takes no input.'\n );\n }\n }\n\n /**\n * Clear the rating, the way `Delete` does — the bound value becomes `null`, not\n * `0`.\n *\n * The slider then announces `aria-valuenow=\"0\"` and every star empties, so\n * {@link getValue} cannot tell you this happened. Assert the model.\n */\n async clear(): Promise<void> {\n await (await this.row()).sendKeys(TestKey.DELETE);\n }\n\n /**\n * Bump the rating up by one step — one `ArrowUp`, the arrow that means \"more\"\n * in both reading directions (see {@link setValue}).\n *\n * The step is the rating's, not this harness's, so this is also how a spec finds\n * out what it is: clear the rating, step up once, read the value.\n * Already at the top, this does nothing; the value clamps rather than wrapping.\n */\n async stepUp(): Promise<void> {\n await (await this.row()).sendKeys(TestKey.UP_ARROW);\n }\n\n /** Bump the rating down by one step — one `ArrowDown`. Clamps at `0`. */\n async stepDown(): Promise<void> {\n await (await this.row()).sendKeys(TestKey.DOWN_ARROW);\n }\n\n /**\n * End a hover preview, dropping the stars back to the committed value.\n *\n * It lives here rather than on the star because the component listens for\n * `mouseleave` on the ROW: leaving one star for the next is not leaving the\n * rating.\n */\n async unhover(): Promise<void> {\n await (await this.row()).mouseAway();\n }\n\n /** Move keyboard focus to the rating — onto the row, which is the tab stop. */\n async focus(): Promise<void> {\n return (await this.row()).focus();\n }\n\n /** Blur the rating. This is what emits `touch`, so a bound field marks itself touched. */\n async blur(): Promise<void> {\n return (await this.row()).blur();\n }\n\n async isFocused(): Promise<boolean> {\n return (await this.row()).isFocused();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/fesm2022/ngwr-rating.mjs
CHANGED
|
@@ -233,10 +233,10 @@ class WrRating {
|
|
|
233
233
|
this.value.set(value);
|
|
234
234
|
this.hoverValue.set(null);
|
|
235
235
|
}
|
|
236
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
237
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
236
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrRating, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
237
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrRating, isStandalone: true, selector: "wr-rating", inputs: { count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: "<div\n class=\"wr-rating__row\"\n role=\"slider\"\n [attr.aria-label]=\"nameLabel()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"count()\"\n [attr.aria-valuenow]=\"announcedValue()\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"focusable() ? 0 : -1\"\n (keydown)=\"onKeydown($event)\"\n (mouseleave)=\"onLeave()\"\n (blur)=\"onBlur()\"\n>\n @for (slot of slots(); track $index) {\n <!-- The parent row owns keyboard focus + handlers; slots are pointer-only\n click targets, so the a11y rules below are intentionally suppressed. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <span\n class=\"wr-rating__slot\"\n [class.wr-rating__slot--interactive]=\"interactive()\"\n [style.--wr-rating-fill]=\"fillFor($index)\"\n (mousemove)=\"onSlotMove($event, $index)\"\n (click)=\"onSlotClick($event, $index)\"\n >\n <!-- Outline (always visible) -->\n <svg class=\"wr-rating__icon wr-rating__icon--bg\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path\n d=\"M12 2.5l2.95 5.98 6.6.96-4.78 4.66 1.13 6.58L12 17.6l-5.9 3.08 1.13-6.58L2.45 9.44l6.6-.96L12 2.5z\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linejoin=\"round\"\n />\n </svg>\n <!-- Filled overlay, clipped by --wr-rating-fill (0..1) -->\n <svg class=\"wr-rating__icon wr-rating__icon--fg\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path\n d=\"M12 2.5l2.95 5.98 6.6.96-4.78 4.66 1.13 6.58L12 17.6l-5.9 3.08 1.13-6.58L2.45 9.44l6.6-.96L12 2.5z\"\n fill=\"currentColor\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n }\n</div>\n", encapsulation: i0.ViewEncapsulation.None });
|
|
238
238
|
}
|
|
239
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
239
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrRating, decorators: [{
|
|
240
240
|
type: Component,
|
|
241
241
|
args: [{ selector: 'wr-rating', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, template: "<div\n class=\"wr-rating__row\"\n role=\"slider\"\n [attr.aria-label]=\"nameLabel()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"count()\"\n [attr.aria-valuenow]=\"announcedValue()\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"focusable() ? 0 : -1\"\n (keydown)=\"onKeydown($event)\"\n (mouseleave)=\"onLeave()\"\n (blur)=\"onBlur()\"\n>\n @for (slot of slots(); track $index) {\n <!-- The parent row owns keyboard focus + handlers; slots are pointer-only\n click targets, so the a11y rules below are intentionally suppressed. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <span\n class=\"wr-rating__slot\"\n [class.wr-rating__slot--interactive]=\"interactive()\"\n [style.--wr-rating-fill]=\"fillFor($index)\"\n (mousemove)=\"onSlotMove($event, $index)\"\n (click)=\"onSlotClick($event, $index)\"\n >\n <!-- Outline (always visible) -->\n <svg class=\"wr-rating__icon wr-rating__icon--bg\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path\n d=\"M12 2.5l2.95 5.98 6.6.96-4.78 4.66 1.13 6.58L12 17.6l-5.9 3.08 1.13-6.58L2.45 9.44l6.6-.96L12 2.5z\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linejoin=\"round\"\n />\n </svg>\n <!-- Filled overlay, clipped by --wr-rating-fill (0..1) -->\n <svg class=\"wr-rating__icon wr-rating__icon--fg\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path\n d=\"M12 2.5l2.95 5.98 6.6.96-4.78 4.66 1.13 6.58L12 17.6l-5.9 3.08 1.13-6.58L2.45 9.44l6.6-.96L12 2.5z\"\n fill=\"currentColor\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n }\n</div>\n" }]
|
|
242
242
|
}], propDecorators: { count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }] } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-rating.mjs","sources":["../../../projects/lib/rating/rating.ts","../../../projects/lib/rating/rating.html","../../../projects/lib/rating/ngwr-rating.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 { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, inject, input, model, output, signal } from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useFormFieldAria } from 'ngwr/form';\nimport { useI18nText } from 'ngwr/i18n';\nimport { clamp } from 'ngwr/utils';\n\n/**\n * Star rating input. Click or hover to set a value; supports half-star\n * increments via `step=\"0.5\"`. Value is `number | null`.\n *\n * `[0, count]` bounds what this control's OWN edits produce — a click, an arrow\n * key, `End`. A value written from outside is displayed and announced clamped\n * (a slider cannot report an `aria-valuenow` past its own `aria-valuemax`) and\n * left in the model as it stands, the same rule `wr-slider` and\n * `wr-input-number` follow.\n *\n * A signal-forms native control: it implements `FormValueControl<number | null>`,\n * so `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone.\n *\n * Keyboard: `←` / `→` bump by `step`, `Home` / `End` jump to 0 / max.\n * Clicking the current value clears it (toggle off).\n *\n * The stars sit on the inline axis, so under `dir=\"rtl\"` the row is mirrored and\n * both halves of the input mirror with it: `→` walks toward the low end, and a\n * click is measured from the star's inline-start (right) edge.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-rating [formField]=\"form.score\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-rating [(value)]=\"score\" [count]=\"10\" step=\"0.5\" />\n * <wr-rating [value]=\"4.5\" [readonly]=\"true\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/rating\n */\nexport type WrRatingSize = 'sm' | 'md' | 'lg';\n\n@Component({\n selector: 'wr-rating',\n templateUrl: './rating.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrRating implements FormValueControl<number | null> {\n /** Total number of slots. @default 5 */\n readonly count = input(5, { transform: (v: unknown): number => Math.max(1, coerceNumberProperty(v, 5)) });\n\n /** Step granularity — `1` for whole stars, `0.5` for halves. @default 1 */\n readonly step = input(1 as 0.5 | 1, {\n transform: (v: unknown): 0.5 | 1 => (Number(v) === 0.5 ? 0.5 : 1),\n });\n\n /** Read-only — value is displayed but not interactive. @default false */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Disable interaction. Bound automatically from the field's disabled state\n * when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Control size — scales the icons + gaps. @default 'md' */\n readonly size = input<WrRatingSize>('md');\n\n /** Accessible label. Falls back to `rating.label`, then `'Rating'`. */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'rating.label', 'Rating');\n\n /** The rating. Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<number | null>(null);\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n /** Transient hover preview — overrides `value` for display when set. */\n protected readonly hoverValue = signal<number | null>(null);\n\n protected readonly interactive = computed(() => !this.disabled() && !this.readonly());\n\n /**\n * Whether the control keeps its tab stop — `disabled` only, never `readonly`.\n *\n * The two states differ precisely here: a disabled control leaves the tab\n * order, a read-only one stays in it and keeps announcing `aria-valuenow`,\n * because a keyboard user still has to be able to READ a value they cannot\n * change. Driving `tabindex` off `interactive()` collapsed that distinction\n * and made `readonly()` unreachable — with an `aria-describedby` pointing at\n * an error message on an element nobody could focus.\n */\n protected readonly focusable = computed(() => !this.disabled());\n\n /**\n * What we actually render — hover wins while hovering.\n *\n * Clamped, because there is no such thing as drawing six stars in a row of\n * five, and the same number is what `aria-valuenow` reports: a `role=\"slider\"`\n * whose value sits outside its own min / max is an invalid slider whatever the\n * model holds. The MODEL is left alone — see the note above `fillFor` for why\n * the clamp is not written back.\n */\n protected readonly displayValue = computed(() => clamp(this.hoverValue() ?? this.value() ?? 0, 0, this.count()));\n\n /**\n * What the slider REPORTS, and where the arrows start from.\n *\n * Deliberately not `displayValue`: a hover preview is a pointer affordance and\n * must not be announced as the current value, and an arrow press after a hover\n * has to step from the committed number rather than from wherever the mouse\n * happened to rest.\n */\n protected readonly announcedValue = computed(() => clamp(this.value() ?? 0, 0, this.count()));\n\n protected readonly slots = computed(() => Array.from({ length: this.count() }, (_, i) => i));\n\n /**\n * Reading direction, for the two places it changes the answer: the arrows and\n * the pointer ratio inside a star.\n *\n * `optional` because a consumer who never set a direction should not have to\n * provide anything — `Directionality` is root-provided, so this only resolves\n * to `null` in a bare `TestBed`, where LTR is the right assumption. Read per\n * interaction rather than cached, so a runtime flip needs no subscription.\n */\n private readonly directionality = inject(Directionality, { optional: true });\n\n /** The surrounding `<wr-form-field>`'s error state. @internal */\n protected readonly fieldAria = useFormFieldAria();\n\n /**\n * The field's label wins over the built-in default, and an explicit\n * `ariaLabel` wins over both.\n *\n * `<wr-form-field label=\"…\">` renders a `<label for>` that cannot reach this\n * control — `for` binds only to a LABELABLE element and the focusable part\n * here carries a role instead — so the field's label named nothing and this\n * announced its own generic default. A consumer who wrote\n * `<wr-form-field label=\"Overall satisfaction\">` heard \"Rating\".\n */\n protected readonly labelledBy = computed(() => (this.ariaLabel() ? null : this.fieldAria.labelledBy()));\n\n /** `null` once `aria-labelledby` is carrying the name, or the two are announced together. */\n protected readonly nameLabel = computed(() => (this.labelledBy() ? null : this.resolvedAriaLabel()));\n\n protected readonly classes = computed(() => {\n const parts = ['wr-rating'];\n const size = this.size();\n if (size !== 'md') parts.push(`wr-rating--${size}`);\n if (this.readonly()) parts.push('wr-rating--readonly');\n if (this.disabled()) parts.push('wr-rating--disabled');\n return parts.join(' ');\n });\n\n /*\n * There is deliberately no effect re-clamping `value` into `[0, count]`.\n *\n * There was one — it wrote the clamp back into the model, which the old CVA\n * `writeValue` also did — and under `[formField]` that write is\n * indistinguishable from the user moving the stars: a `FormControl(9)` on a\n * five-star rating became `5`, `dirty`, and emitted through `valueChanges`\n * before anything had been touched. So an unsaved-changes guard fired on a\n * form nobody had edited, and the out-of-range value that a `max()` rule\n * exists to REPORT was deleted by the control it was reported on (binding\n * `[count]` beside `[formField]` cannot help — the schema's rules are the only\n * bounds that reach here).\n *\n * `wr-slider` and `wr-input-number` had both already settled this the other\n * way, for the same two reasons, and this one was the odd control out.\n * `displayValue` clamps what is drawn and announced; `computeValue` and\n * `onKeydown` clamp what this control itself produces; the model is the\n * consumer's until they touch a star.\n */\n /** Fill ratio for slot `i` in `[0, 1]` — drives the CSS clip width. */\n protected fillFor(i: number): number {\n return clamp(this.displayValue() - i, 0, 1);\n }\n\n // Template handlers\n\n protected onSlotMove(event: MouseEvent, index: number): void {\n if (!this.interactive()) return;\n this.hoverValue.set(this.computeValue(event, index));\n }\n\n protected onLeave(): void {\n this.hoverValue.set(null);\n }\n\n protected onSlotClick(event: MouseEvent, index: number): void {\n if (!this.interactive()) return;\n const next = this.computeValue(event, index);\n // Click the current value → clear it.\n const final = next === this.value() ? null : next;\n this.commit(final);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (!this.interactive()) return;\n const max = this.count();\n const step = this.step();\n // The clamped reading, so an arrow after an out-of-range external write\n // steps from the star the user can see rather than from a number off the row.\n const current = this.announcedValue();\n // Arrows follow VISUAL order (WAI-ARIA APG). The row lays its stars along the\n // inline axis, so under `dir=\"rtl\"` the first star is the RIGHTMOST one and\n // `→` walks down the scale. Up / Down are the block axis and never flip;\n // Home / End mean first / last, a semantic position rather than a physical\n // one, so they don't either.\n const inline = this.isRtl() ? -step : step;\n let next: number | null;\n switch (event.key) {\n case 'ArrowRight':\n next = clamp(current + inline, 0, max);\n break;\n case 'ArrowUp':\n next = clamp(current + step, 0, max);\n break;\n case 'ArrowLeft':\n next = clamp(current - inline, 0, max);\n break;\n case 'ArrowDown':\n next = clamp(current - step, 0, max);\n break;\n case 'Home':\n next = 0;\n break;\n case 'End':\n next = max;\n break;\n case 'Delete':\n case 'Backspace':\n next = null;\n break;\n default:\n return;\n }\n event.preventDefault();\n this.commit(next);\n }\n\n protected onBlur(): void {\n this.touch.emit();\n }\n\n // Internals\n\n /** Whether the row reads right-to-left. */\n private isRtl(): boolean {\n return this.directionality?.value === 'rtl';\n }\n\n /**\n * How far into the star the pointer sits, `0` to `1`, measured from its\n * INLINE-start edge — the left one in LTR, the right one in RTL, where the row\n * is mirrored. Taken from `rect.left` alone it reads 1 exactly where an RTL\n * user sees the start of the star, which inverts every half step.\n */\n private inlineRatio(clientX: number, rect: DOMRect): number {\n const offset = this.isRtl() ? rect.right - clientX : clientX - rect.left;\n return clamp(offset / rect.width, 0, 1);\n }\n\n /** Convert a mouse position over slot `index` to a snapped value. */\n private computeValue(event: MouseEvent, index: number): number {\n const target = event.currentTarget as HTMLElement;\n const rect = target.getBoundingClientRect();\n const ratio = this.inlineRatio(event.clientX, rect);\n const raw = index + ratio;\n const step = this.step();\n const snapped = Math.ceil(raw / step) * step;\n return clamp(snapped, step, this.count());\n }\n\n private commit(value: number | null): void {\n this.value.set(value);\n this.hoverValue.set(null);\n }\n}\n","<div\n class=\"wr-rating__row\"\n role=\"slider\"\n [attr.aria-label]=\"nameLabel()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"count()\"\n [attr.aria-valuenow]=\"announcedValue()\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"focusable() ? 0 : -1\"\n (keydown)=\"onKeydown($event)\"\n (mouseleave)=\"onLeave()\"\n (blur)=\"onBlur()\"\n>\n @for (slot of slots(); track $index) {\n <!-- The parent row owns keyboard focus + handlers; slots are pointer-only\n click targets, so the a11y rules below are intentionally suppressed. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <span\n class=\"wr-rating__slot\"\n [class.wr-rating__slot--interactive]=\"interactive()\"\n [style.--wr-rating-fill]=\"fillFor($index)\"\n (mousemove)=\"onSlotMove($event, $index)\"\n (click)=\"onSlotClick($event, $index)\"\n >\n <!-- Outline (always visible) -->\n <svg class=\"wr-rating__icon wr-rating__icon--bg\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path\n d=\"M12 2.5l2.95 5.98 6.6.96-4.78 4.66 1.13 6.58L12 17.6l-5.9 3.08 1.13-6.58L2.45 9.44l6.6-.96L12 2.5z\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linejoin=\"round\"\n />\n </svg>\n <!-- Filled overlay, clipped by --wr-rating-fill (0..1) -->\n <svg class=\"wr-rating__icon wr-rating__icon--fg\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path\n d=\"M12 2.5l2.95 5.98 6.6.96-4.78 4.66 1.13 6.58L12 17.6l-5.9 3.08 1.13-6.58L2.45 9.44l6.6-.96L12 2.5z\"\n fill=\"currentColor\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n }\n</div>\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-rating.mjs","sources":["../../../projects/lib/rating/rating.ts","../../../projects/lib/rating/rating.html","../../../projects/lib/rating/ngwr-rating.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 { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, inject, input, model, output, signal } from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useFormFieldAria } from 'ngwr/form';\nimport { useI18nText } from 'ngwr/i18n';\nimport { clamp } from 'ngwr/utils';\n\n/**\n * Star rating input. Click or hover to set a value; supports half-star\n * increments via `step=\"0.5\"`. Value is `number | null`.\n *\n * `[0, count]` bounds what this control's OWN edits produce — a click, an arrow\n * key, `End`. A value written from outside is displayed and announced clamped\n * (a slider cannot report an `aria-valuenow` past its own `aria-valuemax`) and\n * left in the model as it stands, the same rule `wr-slider` and\n * `wr-input-number` follow.\n *\n * A signal-forms native control: it implements `FormValueControl<number | null>`,\n * so `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone.\n *\n * Keyboard: `←` / `→` bump by `step`, `Home` / `End` jump to 0 / max.\n * Clicking the current value clears it (toggle off).\n *\n * The stars sit on the inline axis, so under `dir=\"rtl\"` the row is mirrored and\n * both halves of the input mirror with it: `→` walks toward the low end, and a\n * click is measured from the star's inline-start (right) edge.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-rating [formField]=\"form.score\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-rating [(value)]=\"score\" [count]=\"10\" step=\"0.5\" />\n * <wr-rating [value]=\"4.5\" [readonly]=\"true\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/rating\n */\nexport type WrRatingSize = 'sm' | 'md' | 'lg';\n\n@Component({\n selector: 'wr-rating',\n templateUrl: './rating.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrRating implements FormValueControl<number | null> {\n /** Total number of slots. @default 5 */\n readonly count = input(5, { transform: (v: unknown): number => Math.max(1, coerceNumberProperty(v, 5)) });\n\n /** Step granularity — `1` for whole stars, `0.5` for halves. @default 1 */\n readonly step = input(1 as 0.5 | 1, {\n transform: (v: unknown): 0.5 | 1 => (Number(v) === 0.5 ? 0.5 : 1),\n });\n\n /** Read-only — value is displayed but not interactive. @default false */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Disable interaction. Bound automatically from the field's disabled state\n * when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Control size — scales the icons + gaps. @default 'md' */\n readonly size = input<WrRatingSize>('md');\n\n /** Accessible label. Falls back to `rating.label`, then `'Rating'`. */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'rating.label', 'Rating');\n\n /** The rating. Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<number | null>(null);\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n /** Transient hover preview — overrides `value` for display when set. */\n protected readonly hoverValue = signal<number | null>(null);\n\n protected readonly interactive = computed(() => !this.disabled() && !this.readonly());\n\n /**\n * Whether the control keeps its tab stop — `disabled` only, never `readonly`.\n *\n * The two states differ precisely here: a disabled control leaves the tab\n * order, a read-only one stays in it and keeps announcing `aria-valuenow`,\n * because a keyboard user still has to be able to READ a value they cannot\n * change. Driving `tabindex` off `interactive()` collapsed that distinction\n * and made `readonly()` unreachable — with an `aria-describedby` pointing at\n * an error message on an element nobody could focus.\n */\n protected readonly focusable = computed(() => !this.disabled());\n\n /**\n * What we actually render — hover wins while hovering.\n *\n * Clamped, because there is no such thing as drawing six stars in a row of\n * five, and the same number is what `aria-valuenow` reports: a `role=\"slider\"`\n * whose value sits outside its own min / max is an invalid slider whatever the\n * model holds. The MODEL is left alone — see the note above `fillFor` for why\n * the clamp is not written back.\n */\n protected readonly displayValue = computed(() => clamp(this.hoverValue() ?? this.value() ?? 0, 0, this.count()));\n\n /**\n * What the slider REPORTS, and where the arrows start from.\n *\n * Deliberately not `displayValue`: a hover preview is a pointer affordance and\n * must not be announced as the current value, and an arrow press after a hover\n * has to step from the committed number rather than from wherever the mouse\n * happened to rest.\n */\n protected readonly announcedValue = computed(() => clamp(this.value() ?? 0, 0, this.count()));\n\n protected readonly slots = computed(() => Array.from({ length: this.count() }, (_, i) => i));\n\n /**\n * Reading direction, for the two places it changes the answer: the arrows and\n * the pointer ratio inside a star.\n *\n * `optional` because a consumer who never set a direction should not have to\n * provide anything — `Directionality` is root-provided, so this only resolves\n * to `null` in a bare `TestBed`, where LTR is the right assumption. Read per\n * interaction rather than cached, so a runtime flip needs no subscription.\n */\n private readonly directionality = inject(Directionality, { optional: true });\n\n /** The surrounding `<wr-form-field>`'s error state. @internal */\n protected readonly fieldAria = useFormFieldAria();\n\n /**\n * The field's label wins over the built-in default, and an explicit\n * `ariaLabel` wins over both.\n *\n * `<wr-form-field label=\"…\">` renders a `<label for>` that cannot reach this\n * control — `for` binds only to a LABELABLE element and the focusable part\n * here carries a role instead — so the field's label named nothing and this\n * announced its own generic default. A consumer who wrote\n * `<wr-form-field label=\"Overall satisfaction\">` heard \"Rating\".\n */\n protected readonly labelledBy = computed(() => (this.ariaLabel() ? null : this.fieldAria.labelledBy()));\n\n /** `null` once `aria-labelledby` is carrying the name, or the two are announced together. */\n protected readonly nameLabel = computed(() => (this.labelledBy() ? null : this.resolvedAriaLabel()));\n\n protected readonly classes = computed(() => {\n const parts = ['wr-rating'];\n const size = this.size();\n if (size !== 'md') parts.push(`wr-rating--${size}`);\n if (this.readonly()) parts.push('wr-rating--readonly');\n if (this.disabled()) parts.push('wr-rating--disabled');\n return parts.join(' ');\n });\n\n /*\n * There is deliberately no effect re-clamping `value` into `[0, count]`.\n *\n * There was one — it wrote the clamp back into the model, which the old CVA\n * `writeValue` also did — and under `[formField]` that write is\n * indistinguishable from the user moving the stars: a `FormControl(9)` on a\n * five-star rating became `5`, `dirty`, and emitted through `valueChanges`\n * before anything had been touched. So an unsaved-changes guard fired on a\n * form nobody had edited, and the out-of-range value that a `max()` rule\n * exists to REPORT was deleted by the control it was reported on (binding\n * `[count]` beside `[formField]` cannot help — the schema's rules are the only\n * bounds that reach here).\n *\n * `wr-slider` and `wr-input-number` had both already settled this the other\n * way, for the same two reasons, and this one was the odd control out.\n * `displayValue` clamps what is drawn and announced; `computeValue` and\n * `onKeydown` clamp what this control itself produces; the model is the\n * consumer's until they touch a star.\n */\n /** Fill ratio for slot `i` in `[0, 1]` — drives the CSS clip width. */\n protected fillFor(i: number): number {\n return clamp(this.displayValue() - i, 0, 1);\n }\n\n // Template handlers\n\n protected onSlotMove(event: MouseEvent, index: number): void {\n if (!this.interactive()) return;\n this.hoverValue.set(this.computeValue(event, index));\n }\n\n protected onLeave(): void {\n this.hoverValue.set(null);\n }\n\n protected onSlotClick(event: MouseEvent, index: number): void {\n if (!this.interactive()) return;\n const next = this.computeValue(event, index);\n // Click the current value → clear it.\n const final = next === this.value() ? null : next;\n this.commit(final);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (!this.interactive()) return;\n const max = this.count();\n const step = this.step();\n // The clamped reading, so an arrow after an out-of-range external write\n // steps from the star the user can see rather than from a number off the row.\n const current = this.announcedValue();\n // Arrows follow VISUAL order (WAI-ARIA APG). The row lays its stars along the\n // inline axis, so under `dir=\"rtl\"` the first star is the RIGHTMOST one and\n // `→` walks down the scale. Up / Down are the block axis and never flip;\n // Home / End mean first / last, a semantic position rather than a physical\n // one, so they don't either.\n const inline = this.isRtl() ? -step : step;\n let next: number | null;\n switch (event.key) {\n case 'ArrowRight':\n next = clamp(current + inline, 0, max);\n break;\n case 'ArrowUp':\n next = clamp(current + step, 0, max);\n break;\n case 'ArrowLeft':\n next = clamp(current - inline, 0, max);\n break;\n case 'ArrowDown':\n next = clamp(current - step, 0, max);\n break;\n case 'Home':\n next = 0;\n break;\n case 'End':\n next = max;\n break;\n case 'Delete':\n case 'Backspace':\n next = null;\n break;\n default:\n return;\n }\n event.preventDefault();\n this.commit(next);\n }\n\n protected onBlur(): void {\n this.touch.emit();\n }\n\n // Internals\n\n /** Whether the row reads right-to-left. */\n private isRtl(): boolean {\n return this.directionality?.value === 'rtl';\n }\n\n /**\n * How far into the star the pointer sits, `0` to `1`, measured from its\n * INLINE-start edge — the left one in LTR, the right one in RTL, where the row\n * is mirrored. Taken from `rect.left` alone it reads 1 exactly where an RTL\n * user sees the start of the star, which inverts every half step.\n */\n private inlineRatio(clientX: number, rect: DOMRect): number {\n const offset = this.isRtl() ? rect.right - clientX : clientX - rect.left;\n return clamp(offset / rect.width, 0, 1);\n }\n\n /** Convert a mouse position over slot `index` to a snapped value. */\n private computeValue(event: MouseEvent, index: number): number {\n const target = event.currentTarget as HTMLElement;\n const rect = target.getBoundingClientRect();\n const ratio = this.inlineRatio(event.clientX, rect);\n const raw = index + ratio;\n const step = this.step();\n const snapped = Math.ceil(raw / step) * step;\n return clamp(snapped, step, this.count());\n }\n\n private commit(value: number | null): void {\n this.value.set(value);\n this.hoverValue.set(null);\n }\n}\n","<div\n class=\"wr-rating__row\"\n role=\"slider\"\n [attr.aria-label]=\"nameLabel()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"count()\"\n [attr.aria-valuenow]=\"announcedValue()\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [attr.tabindex]=\"focusable() ? 0 : -1\"\n (keydown)=\"onKeydown($event)\"\n (mouseleave)=\"onLeave()\"\n (blur)=\"onBlur()\"\n>\n @for (slot of slots(); track $index) {\n <!-- The parent row owns keyboard focus + handlers; slots are pointer-only\n click targets, so the a11y rules below are intentionally suppressed. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events,@angular-eslint/template/interactive-supports-focus -->\n <span\n class=\"wr-rating__slot\"\n [class.wr-rating__slot--interactive]=\"interactive()\"\n [style.--wr-rating-fill]=\"fillFor($index)\"\n (mousemove)=\"onSlotMove($event, $index)\"\n (click)=\"onSlotClick($event, $index)\"\n >\n <!-- Outline (always visible) -->\n <svg class=\"wr-rating__icon wr-rating__icon--bg\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path\n d=\"M12 2.5l2.95 5.98 6.6.96-4.78 4.66 1.13 6.58L12 17.6l-5.9 3.08 1.13-6.58L2.45 9.44l6.6-.96L12 2.5z\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linejoin=\"round\"\n />\n </svg>\n <!-- Filled overlay, clipped by --wr-rating-fill (0..1) -->\n <svg class=\"wr-rating__icon wr-rating__icon--fg\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path\n d=\"M12 2.5l2.95 5.98 6.6.96-4.78 4.66 1.13 6.58L12 17.6l-5.9 3.08 1.13-6.58L2.45 9.44l6.6-.96L12 2.5z\"\n fill=\"currentColor\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|