ngwr 11.1.0 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/burger/styles/_index.scss +1 -1
- package/button/styles/_index.scss +2 -2
- package/calendar/styles/_index.scss +32 -12
- package/calendar-heatmap/styles/_index.scss +20 -0
- package/carousel/styles/_index.scss +23 -1
- package/cascader/styles/_index.scss +15 -3
- package/checkbox/styles/_index.scss +2 -2
- package/circular-text/styles/_index.scss +11 -0
- package/collapse/styles/_index.scss +2 -2
- package/color-picker/styles/_index.scss +3 -3
- package/command-palette/styles/_index.scss +12 -0
- package/compare/styles/_index.scss +18 -2
- package/context-menu/styles/_index.scss +21 -2
- package/date-picker/styles/_index.scss +5 -5
- package/descriptions/styles/_index.scss +1 -1
- package/drag-drop/styles/_index.scss +28 -1
- package/drawer/styles/_index.scss +37 -0
- package/dropdown/styles/_index.scss +2 -2
- package/event-calendar/styles/_index.scss +67 -10
- package/fesm2022/ngwr-action-sheet-testing.mjs +313 -0
- package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -0
- package/fesm2022/ngwr-action-sheet.mjs +3 -3
- package/fesm2022/ngwr-affix.mjs +3 -3
- package/fesm2022/ngwr-alert.mjs +3 -3
- package/fesm2022/ngwr-anchor.mjs +3 -3
- package/fesm2022/ngwr-aurora.mjs +3 -3
- package/fesm2022/ngwr-avatar.mjs +3 -3
- package/fesm2022/ngwr-back-top.mjs +3 -3
- package/fesm2022/ngwr-badge.mjs +6 -6
- package/fesm2022/ngwr-bar-chart-testing.mjs +107 -0
- package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -0
- package/fesm2022/ngwr-bar-chart.mjs +3 -3
- package/fesm2022/ngwr-blur-text-testing.mjs +203 -0
- package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-blur-text.mjs +18 -4
- package/fesm2022/ngwr-blur-text.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow-testing.mjs +325 -0
- package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -0
- package/fesm2022/ngwr-border-glow.mjs +4 -4
- package/fesm2022/ngwr-border-glow.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +29 -17
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-burger.mjs +3 -3
- package/fesm2022/ngwr-button.mjs +6 -6
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs +95 -0
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -0
- package/fesm2022/ngwr-calendar-heatmap.mjs +17 -5
- package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-testing.mjs +368 -0
- package/fesm2022/ngwr-calendar-testing.mjs.map +1 -0
- package/fesm2022/ngwr-calendar.mjs +228 -10
- package/fesm2022/ngwr-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +10 -10
- package/fesm2022/ngwr-card.mjs.map +1 -1
- package/fesm2022/ngwr-carousel.mjs +124 -24
- package/fesm2022/ngwr-carousel.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +60 -5
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox.mjs +14 -6
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text-testing.mjs +230 -0
- package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-circular-text.mjs +4 -4
- package/fesm2022/ngwr-circular-text.mjs.map +1 -1
- package/fesm2022/ngwr-click-spark.mjs +27 -7
- package/fesm2022/ngwr-click-spark.mjs.map +1 -1
- package/fesm2022/ngwr-clipboard.mjs +3 -3
- package/fesm2022/ngwr-collapse.mjs +9 -9
- package/fesm2022/ngwr-color-picker-testing.mjs +452 -0
- package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -0
- package/fesm2022/ngwr-color-picker.mjs +126 -10
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-command-palette.mjs +14 -5
- package/fesm2022/ngwr-command-palette.mjs.map +1 -1
- package/fesm2022/ngwr-compare.mjs +3 -3
- package/fesm2022/ngwr-confetti.mjs +31 -6
- package/fesm2022/ngwr-confetti.mjs.map +1 -1
- package/fesm2022/ngwr-config.mjs +9 -0
- package/fesm2022/ngwr-config.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu-testing.mjs +30 -11
- package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu.mjs +358 -48
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-cookie.mjs +3 -3
- package/fesm2022/ngwr-counter.mjs +25 -9
- package/fesm2022/ngwr-counter.mjs.map +1 -1
- package/fesm2022/{ngwr-date-adapter-fns.mjs → ngwr-date-adapters-fns.mjs} +37 -10
- package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -0
- package/fesm2022/{ngwr-date-adapter-luxon.mjs → ngwr-date-adapters-luxon.mjs} +5 -5
- package/fesm2022/ngwr-date-adapters-luxon.mjs.map +1 -0
- package/fesm2022/ngwr-date-picker-testing.mjs +21 -85
- package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker.mjs +119 -37
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/{ngwr-date-adapter.mjs → ngwr-date.mjs} +36 -10
- package/fesm2022/ngwr-date.mjs.map +1 -0
- package/fesm2022/ngwr-decrypt-text-testing.mjs +261 -0
- package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-decrypt-text.mjs +9 -4
- package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
- package/fesm2022/ngwr-density.mjs +6 -6
- package/fesm2022/ngwr-descriptions.mjs +6 -6
- package/fesm2022/ngwr-dialog.mjs +73 -18
- package/fesm2022/ngwr-dialog.mjs.map +1 -1
- package/fesm2022/ngwr-directives.mjs +12 -12
- package/fesm2022/ngwr-divider.mjs +52 -5
- package/fesm2022/ngwr-divider.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart-testing.mjs +97 -0
- package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -0
- package/fesm2022/ngwr-donut-chart.mjs +7 -7
- package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +160 -11
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
- package/fesm2022/ngwr-drawer.mjs +156 -64
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +58 -12
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-empty.mjs +3 -3
- package/fesm2022/ngwr-event-calendar-testing.mjs +446 -0
- package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -0
- package/fesm2022/ngwr-event-calendar.mjs +66 -28
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text-testing.mjs +220 -0
- package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-falling-text.mjs +75 -46
- package/fesm2022/ngwr-falling-text.mjs.map +1 -1
- package/fesm2022/ngwr-file-upload.mjs +3 -3
- package/fesm2022/ngwr-form.mjs +32 -15
- package/fesm2022/ngwr-form.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text-testing.mjs +134 -0
- package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-fuzzy-text.mjs +21 -11
- package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
- package/fesm2022/ngwr-gauge-testing.mjs +86 -0
- package/fesm2022/ngwr-gauge-testing.mjs.map +1 -0
- package/fesm2022/ngwr-gauge.mjs +3 -3
- package/fesm2022/ngwr-glitch-text-testing.mjs +215 -0
- package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-glitch-text.mjs +7 -7
- package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
- package/fesm2022/ngwr-gradient-text-testing.mjs +258 -0
- package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-gradient-text.mjs +4 -4
- package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
- package/fesm2022/ngwr-hotkey.mjs +24 -9
- package/fesm2022/ngwr-hotkey.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +87 -1
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +69 -1
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +26 -15
- package/fesm2022/ngwr-i18n.mjs.map +1 -1
- package/fesm2022/ngwr-icon.mjs +6 -6
- package/fesm2022/ngwr-image-cropper-testing.mjs +200 -0
- package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -0
- package/fesm2022/ngwr-image-cropper.mjs +156 -11
- package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
- package/fesm2022/ngwr-input-number-testing.mjs +15 -10
- package/fesm2022/ngwr-input-number-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input-number.mjs +89 -15
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp-testing.mjs +12 -6
- package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp.mjs +24 -5
- package/fesm2022/ngwr-input-otp.mjs.map +1 -1
- package/fesm2022/ngwr-input.mjs +15 -15
- package/fesm2022/ngwr-keyboard.mjs +3 -3
- package/fesm2022/ngwr-knob-testing.mjs +220 -0
- package/fesm2022/ngwr-knob-testing.mjs.map +1 -0
- package/fesm2022/ngwr-knob.mjs +3 -3
- package/fesm2022/ngwr-layout.mjs +15 -15
- package/fesm2022/ngwr-lightbox-testing.mjs +223 -0
- package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -0
- package/fesm2022/ngwr-lightbox.mjs +11 -4
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart-testing.mjs +119 -0
- package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -0
- package/fesm2022/ngwr-line-chart.mjs +73 -26
- package/fesm2022/ngwr-line-chart.mjs.map +1 -1
- package/fesm2022/ngwr-list.mjs +7 -7
- package/fesm2022/ngwr-list.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar.mjs +7 -7
- package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
- package/fesm2022/ngwr-markdown.mjs +66 -8
- package/fesm2022/ngwr-markdown.mjs.map +1 -1
- package/fesm2022/ngwr-marquee-testing.mjs +336 -0
- package/fesm2022/ngwr-marquee-testing.mjs.map +1 -0
- package/fesm2022/ngwr-marquee.mjs +4 -4
- package/fesm2022/ngwr-marquee.mjs.map +1 -1
- package/fesm2022/ngwr-media.mjs +3 -3
- package/fesm2022/ngwr-mention.mjs +108 -13
- package/fesm2022/ngwr-mention.mjs.map +1 -1
- package/fesm2022/ngwr-meta.mjs +6 -6
- package/fesm2022/ngwr-meter-group-testing.mjs +100 -0
- package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -0
- package/fesm2022/ngwr-meter-group.mjs +3 -3
- package/fesm2022/ngwr-overlay.mjs +34 -7
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-page-header.mjs +3 -3
- package/fesm2022/ngwr-pagination-testing.mjs +6 -6
- package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +17 -13
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-pipes.mjs +39 -26
- package/fesm2022/ngwr-pipes.mjs.map +1 -1
- package/fesm2022/ngwr-platform.mjs +9 -9
- package/fesm2022/ngwr-popconfirm.mjs +6 -6
- package/fesm2022/ngwr-popover-testing.mjs +7 -2
- package/fesm2022/ngwr-popover-testing.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +169 -33
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-progress.mjs +3 -3
- package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
- package/fesm2022/ngwr-qr.mjs +42 -5
- package/fesm2022/ngwr-qr.mjs.map +1 -1
- package/fesm2022/ngwr-radio.mjs +6 -6
- package/fesm2022/ngwr-rating.mjs +3 -3
- package/fesm2022/ngwr-result.mjs +21 -30
- package/fesm2022/ngwr-result.mjs.map +1 -1
- package/fesm2022/ngwr-rotating-text-testing.mjs +220 -0
- package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-rotating-text.mjs +4 -4
- package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
- package/fesm2022/ngwr-scroll.mjs +3 -3
- package/fesm2022/ngwr-segmented-testing.mjs +14 -1
- package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
- package/fesm2022/ngwr-segmented.mjs +29 -6
- package/fesm2022/ngwr-segmented.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +227 -43
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text-testing.mjs +237 -0
- package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-shiny-text.mjs +11 -9
- package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
- package/fesm2022/ngwr-sidebar.mjs +31 -16
- package/fesm2022/ngwr-sidebar.mjs.map +1 -1
- package/fesm2022/ngwr-skeleton.mjs +3 -3
- package/fesm2022/ngwr-slider.mjs +143 -14
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline-testing.mjs +76 -0
- package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -0
- package/fesm2022/ngwr-sparkline.mjs +3 -3
- package/fesm2022/ngwr-speed-dial-testing.mjs +281 -0
- package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -0
- package/fesm2022/ngwr-speed-dial.mjs +27 -12
- package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
- package/fesm2022/ngwr-spinner.mjs +3 -3
- package/fesm2022/ngwr-splash-cursor-testing.mjs +89 -0
- package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -0
- package/fesm2022/ngwr-splash-cursor.mjs +3 -3
- package/fesm2022/ngwr-split-text-testing.mjs +224 -0
- package/fesm2022/ngwr-split-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-split-text.mjs +31 -5
- package/fesm2022/ngwr-split-text.mjs.map +1 -1
- package/fesm2022/ngwr-splitter-testing.mjs +245 -0
- package/fesm2022/ngwr-splitter-testing.mjs.map +1 -0
- package/fesm2022/ngwr-splitter.mjs +3 -3
- package/fesm2022/ngwr-spotlight-card-testing.mjs +368 -0
- package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -0
- package/fesm2022/ngwr-spotlight-card.mjs +7 -7
- package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
- package/fesm2022/ngwr-squircle.mjs +6 -6
- package/fesm2022/ngwr-star-border-testing.mjs +172 -0
- package/fesm2022/ngwr-star-border-testing.mjs.map +1 -0
- package/fesm2022/ngwr-star-border.mjs +4 -4
- package/fesm2022/ngwr-star-border.mjs.map +1 -1
- package/fesm2022/ngwr-statistic.mjs +9 -9
- package/fesm2022/ngwr-stepper-testing.mjs +8 -8
- package/fesm2022/ngwr-stepper-testing.mjs.map +1 -1
- package/fesm2022/ngwr-stepper.mjs +33 -8
- package/fesm2022/ngwr-stepper.mjs.map +1 -1
- package/fesm2022/ngwr-storage.mjs +17 -5
- package/fesm2022/ngwr-storage.mjs.map +1 -1
- package/fesm2022/ngwr-switch.mjs +3 -3
- package/fesm2022/ngwr-table.mjs +110 -40
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-tabs-testing.mjs +11 -10
- package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tabs.mjs +51 -11
- package/fesm2022/ngwr-tabs.mjs.map +1 -1
- package/fesm2022/ngwr-textarea.mjs +3 -3
- package/fesm2022/ngwr-theme.mjs +181 -4
- package/fesm2022/ngwr-theme.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card-testing.mjs +190 -0
- package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -0
- package/fesm2022/ngwr-tilt-card.mjs +26 -8
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
- package/fesm2022/ngwr-timeline.mjs +6 -6
- package/fesm2022/ngwr-toast.mjs +44 -15
- package/fesm2022/ngwr-toast.mjs.map +1 -1
- package/fesm2022/ngwr-toolbar.mjs +3 -3
- package/fesm2022/ngwr-tour-testing.mjs +173 -0
- package/fesm2022/ngwr-tour-testing.mjs.map +1 -0
- package/fesm2022/ngwr-tour.mjs +34 -10
- package/fesm2022/ngwr-tour.mjs.map +1 -1
- package/fesm2022/ngwr-transfer.mjs +3 -3
- package/fesm2022/ngwr-tree.mjs +36 -11
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter-testing.mjs +242 -0
- package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -0
- package/fesm2022/ngwr-typewriter.mjs +13 -5
- package/fesm2022/ngwr-typewriter.mjs.map +1 -1
- package/fesm2022/ngwr-typography.mjs +3 -3
- package/fesm2022/ngwr-utils.mjs +5 -2
- package/fesm2022/ngwr-utils.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-virtual-scroll.mjs +4 -4
- package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
- package/fesm2022/ngwr-waves-testing.mjs +128 -0
- package/fesm2022/ngwr-waves-testing.mjs.map +1 -0
- package/fesm2022/ngwr-waves.mjs +4 -4
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/fesm2022/ngwr-window-testing.mjs +361 -0
- package/fesm2022/ngwr-window-testing.mjs.map +1 -0
- package/fesm2022/ngwr-window.mjs +175 -31
- package/fesm2022/ngwr-window.mjs.map +1 -1
- package/file-upload/styles/_index.scss +2 -2
- package/glitch-text/styles/_index.scss +27 -12
- package/i18n/en.json +71 -7
- package/i18n/ru.json +71 -7
- package/image-cropper/styles/_index.scss +19 -0
- package/input/styles/_index.scss +2 -2
- package/input-number/styles/_index.scss +2 -2
- package/input-otp/styles/_index.scss +9 -2
- package/knob/styles/_index.scss +1 -1
- package/lightbox/styles/_index.scss +16 -2
- package/line-chart/styles/_index.scss +28 -14
- package/list/styles/_index.scss +1 -1
- package/llms-full.txt +161 -19
- package/llms.txt +45 -10
- package/markdown/styles/_index.scss +3 -3
- package/mcp/api.js +6 -4
- package/mcp/catalog.js +40 -3
- package/mcp/providers.js +42 -0
- package/mcp/tools.js +22 -30
- package/meter-group/styles/_index.scss +1 -1
- package/overlay/styles/_index.scss +33 -1
- package/package.json +161 -12
- package/pagination/styles/_index.scss +23 -1
- package/popover/styles/_index.scss +16 -7
- package/progress/styles/_index.scss +1 -1
- package/radio/styles/_index.scss +2 -2
- package/rating/styles/_index.scss +1 -1
- package/schematics/collection.json +1 -1
- package/schematics/migrations/v12/index.js +65 -0
- package/schematics/migrations/v7/index.js +12 -1
- package/schematics/migrations/v8/index.js +10 -1
- package/schematics/migrations.json +5 -0
- package/schematics/ng-add/index.js +8 -3
- package/schematics/provider/index.js +80 -13
- package/schematics/use/index.js +9 -129
- package/schematics/use/rule.js +206 -0
- package/schematics/use/symbol-map.json +4 -4
- package/segmented/styles/_index.scss +14 -3
- package/select/styles/_index.scss +14 -2
- package/sidebar/styles/_index.scss +12 -10
- package/skills/ngwr/SKILL.md +117 -0
- package/skills/ngwr/references/catalog.md +212 -0
- package/skills/ngwr/references/setup.md +78 -0
- package/slider/styles/_index.scss +1 -1
- package/speed-dial/styles/_index.scss +1 -1
- package/stepper/styles/_index.scss +12 -3
- package/switch/styles/_index.scss +2 -2
- package/table/styles/_index.scss +61 -12
- package/tabs/styles/_index.scss +1 -1
- package/textarea/styles/_index.scss +1 -1
- package/theme/styles/_colors.scss +142 -56
- package/theme/styles/_dark.scss +46 -3
- package/theme/styles/_focus.scss +28 -5
- package/theme/styles/_mixins.scss +1 -1
- package/theme/styles/_variables.scss +67 -0
- package/transfer/styles/_index.scss +26 -3
- package/tree/styles/_index.scss +29 -6
- package/types/ngwr-action-sheet-testing.d.ts +260 -0
- package/types/ngwr-bar-chart-testing.d.ts +85 -0
- package/types/ngwr-blur-text-testing.d.ts +180 -0
- package/types/ngwr-blur-text.d.ts +20 -4
- package/types/ngwr-border-glow-testing.d.ts +219 -0
- package/types/ngwr-breadcrumbs.d.ts +17 -10
- package/types/ngwr-calendar-heatmap-testing.d.ts +79 -0
- package/types/ngwr-calendar-testing.d.ts +277 -0
- package/types/ngwr-calendar.d.ts +76 -0
- package/types/ngwr-carousel.d.ts +85 -6
- package/types/ngwr-cascader.d.ts +17 -0
- package/types/ngwr-checkbox.d.ts +13 -0
- package/types/ngwr-circular-text-testing.d.ts +196 -0
- package/types/ngwr-click-spark.d.ts +4 -0
- package/types/ngwr-color-picker-testing.d.ts +299 -0
- package/types/ngwr-color-picker.d.ts +34 -2
- package/types/ngwr-confetti.d.ts +8 -0
- package/types/ngwr-config.d.ts +10 -1
- package/types/ngwr-context-menu-testing.d.ts +24 -11
- package/types/ngwr-context-menu.d.ts +71 -1
- package/types/ngwr-counter.d.ts +1 -0
- package/types/{ngwr-date-adapter-fns.d.ts → ngwr-date-adapters-fns.d.ts} +33 -6
- package/types/{ngwr-date-adapter-luxon.d.ts → ngwr-date-adapters-luxon.d.ts} +1 -1
- package/types/ngwr-date-picker-testing.d.ts +17 -72
- package/types/ngwr-date-picker.d.ts +8 -4
- package/types/{ngwr-date-adapter.d.ts → ngwr-date.d.ts} +24 -5
- package/types/ngwr-decrypt-text-testing.d.ts +222 -0
- package/types/ngwr-dialog.d.ts +38 -1
- package/types/ngwr-divider.d.ts +25 -0
- package/types/ngwr-donut-chart-testing.d.ts +83 -0
- package/types/ngwr-donut-chart.d.ts +3 -3
- package/types/ngwr-drag-drop.d.ts +56 -2
- package/types/ngwr-drawer.d.ts +68 -5
- package/types/ngwr-dropdown.d.ts +9 -0
- package/types/ngwr-event-calendar-testing.d.ts +301 -0
- package/types/ngwr-event-calendar.d.ts +17 -8
- package/types/ngwr-falling-text-testing.d.ts +188 -0
- package/types/ngwr-falling-text.d.ts +1 -1
- package/types/ngwr-form.d.ts +12 -2
- package/types/ngwr-fuzzy-text-testing.d.ts +130 -0
- package/types/ngwr-gauge-testing.d.ts +71 -0
- package/types/ngwr-glitch-text-testing.d.ts +201 -0
- package/types/ngwr-gradient-text-testing.d.ts +186 -0
- package/types/ngwr-i18n.d.ts +13 -4
- package/types/ngwr-image-cropper-testing.d.ts +173 -0
- package/types/ngwr-image-cropper.d.ts +71 -5
- package/types/ngwr-input-number-testing.d.ts +15 -10
- package/types/ngwr-input-number.d.ts +44 -5
- package/types/ngwr-input-otp-testing.d.ts +12 -6
- package/types/ngwr-input-otp.d.ts +15 -1
- package/types/ngwr-knob-testing.d.ts +150 -0
- package/types/ngwr-lightbox-testing.d.ts +154 -0
- package/types/ngwr-lightbox.d.ts +6 -0
- package/types/ngwr-line-chart-testing.d.ts +93 -0
- package/types/ngwr-line-chart.d.ts +20 -3
- package/types/ngwr-markdown.d.ts +11 -0
- package/types/ngwr-marquee-testing.d.ts +280 -0
- package/types/ngwr-mention.d.ts +32 -0
- package/types/ngwr-meter-group-testing.d.ts +77 -0
- package/types/ngwr-overlay.d.ts +20 -1
- package/types/ngwr-pagination-testing.d.ts +4 -4
- package/types/ngwr-pipes.d.ts +1 -1
- package/types/ngwr-popover-testing.d.ts +7 -2
- package/types/ngwr-popover.d.ts +1 -0
- package/types/ngwr-result.d.ts +6 -0
- package/types/ngwr-rotating-text-testing.d.ts +191 -0
- package/types/ngwr-segmented-testing.d.ts +9 -0
- package/types/ngwr-segmented.d.ts +21 -1
- package/types/ngwr-select.d.ts +100 -6
- package/types/ngwr-shiny-text-testing.d.ts +165 -0
- package/types/ngwr-shiny-text.d.ts +4 -2
- package/types/ngwr-sidebar.d.ts +14 -5
- package/types/ngwr-slider.d.ts +84 -1
- package/types/ngwr-sparkline-testing.d.ts +69 -0
- package/types/ngwr-speed-dial-testing.d.ts +199 -0
- package/types/ngwr-speed-dial.d.ts +22 -3
- package/types/ngwr-splash-cursor-testing.d.ts +95 -0
- package/types/ngwr-split-text-testing.d.ts +209 -0
- package/types/ngwr-split-text.d.ts +21 -0
- package/types/ngwr-splitter-testing.d.ts +174 -0
- package/types/ngwr-spotlight-card-testing.d.ts +303 -0
- package/types/ngwr-star-border-testing.d.ts +135 -0
- package/types/ngwr-stepper-testing.d.ts +5 -5
- package/types/ngwr-stepper.d.ts +8 -0
- package/types/ngwr-storage.d.ts +1 -0
- package/types/ngwr-table.d.ts +114 -12
- package/types/ngwr-tabs-testing.d.ts +11 -10
- package/types/ngwr-tabs.d.ts +2 -0
- package/types/ngwr-theme.d.ts +82 -1
- package/types/ngwr-tilt-card-testing.d.ts +158 -0
- package/types/ngwr-tilt-card.d.ts +2 -0
- package/types/ngwr-toast.d.ts +14 -5
- package/types/ngwr-tour-testing.d.ts +143 -0
- package/types/ngwr-tour.d.ts +22 -4
- package/types/ngwr-tree.d.ts +12 -4
- package/types/ngwr-typewriter-testing.d.ts +211 -0
- package/types/ngwr-utils.d.ts +5 -2
- package/types/ngwr-version.d.ts +1 -1
- package/types/ngwr-waves-testing.d.ts +121 -0
- package/types/ngwr-window-testing.d.ts +263 -0
- package/types/ngwr-window.d.ts +66 -21
- package/typography/styles/_index.scss +1 -1
- package/window/styles/_index.scss +116 -42
- package/fesm2022/ngwr-date-adapter-fns.mjs.map +0 -1
- package/fesm2022/ngwr-date-adapter-luxon.mjs.map +0 -1
- package/fesm2022/ngwr-date-adapter.mjs.map +0 -1
package/fesm2022/ngwr-form.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import * as i0 from '@angular/core';
|
|
|
3
3
|
import { input, computed, ViewEncapsulation, Component, InjectionToken, makeEnvironmentProviders, inject, signal, contentChild, effect, DestroyRef, contentChildren, forwardRef } from '@angular/core';
|
|
4
4
|
import { NgControl } from '@angular/forms';
|
|
5
5
|
import { EMPTY } from 'rxjs';
|
|
6
|
-
import { WrI18n } from 'ngwr/i18n';
|
|
6
|
+
import { useI18nText, WrI18n } from 'ngwr/i18n';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @license
|
|
@@ -42,10 +42,10 @@ class WrFormItem {
|
|
|
42
42
|
return parts.join(' ');
|
|
43
43
|
}, /* @ts-ignore */
|
|
44
44
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
45
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
46
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
45
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrFormItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
46
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: WrFormItem, isStandalone: true, selector: "wr-form-item", inputs: { hasError: { classPropertyName: "hasError", publicName: "hasError", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: '<ng-content />', isInline: true, encapsulation: i0.ViewEncapsulation.None });
|
|
47
47
|
}
|
|
48
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
48
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrFormItem, decorators: [{
|
|
49
49
|
type: Component,
|
|
50
50
|
args: [{
|
|
51
51
|
selector: 'wr-form-item',
|
|
@@ -121,7 +121,10 @@ const WR_FORM_ERROR_FALLBACKS = {
|
|
|
121
121
|
max: ({ error }) => `Enter ${num(error, 'max')} or less.`,
|
|
122
122
|
pattern: 'This value is not in the expected format.',
|
|
123
123
|
// WrValidators.
|
|
124
|
-
|
|
124
|
+
// Not "cannot be blank": the validator PASSES on an empty value and fails on
|
|
125
|
+
// any value that holds whitespace, so the blank wording described the one case
|
|
126
|
+
// that never reaches this message — over a field reading `Ada Lovelace`.
|
|
127
|
+
noWhitespace: 'This value cannot contain spaces.',
|
|
125
128
|
hexColor: 'Enter a hex colour, e.g. #1a2b3c.',
|
|
126
129
|
url: 'Enter a valid URL.',
|
|
127
130
|
cardNumber: 'Enter a valid card number.',
|
|
@@ -197,10 +200,10 @@ class WrFormError {
|
|
|
197
200
|
return this.field.errorKeys().includes(key);
|
|
198
201
|
}, /* @ts-ignore */
|
|
199
202
|
...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
200
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
201
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
203
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrFormError, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
204
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: WrFormError, isStandalone: true, selector: "wr-form-error", inputs: { key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "alert" }, properties: { "attr.data-key": "key() ?? null", "class.wr-form-field__error--hidden": "!visible()" }, classAttribute: "wr-form-error wr-form-field__error" }, ngImport: i0, template: `<ng-content />`, isInline: true, encapsulation: i0.ViewEncapsulation.None });
|
|
202
205
|
}
|
|
203
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
206
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrFormError, decorators: [{
|
|
204
207
|
type: Component,
|
|
205
208
|
args: [{
|
|
206
209
|
selector: 'wr-form-error',
|
|
@@ -244,8 +247,22 @@ class WrFormField {
|
|
|
244
247
|
...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
245
248
|
/** Show a `*` next to the label. @default false */
|
|
246
249
|
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
247
|
-
/**
|
|
250
|
+
/**
|
|
251
|
+
* Show an "optional" marker next to the label — the word comes from
|
|
252
|
+
* `optionalLabel`. Mutually exclusive with `required`. @default false
|
|
253
|
+
*/
|
|
248
254
|
optional = input(false, { ...(ngDevMode ? { debugName: "optional" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
255
|
+
/**
|
|
256
|
+
* Word inside the optional marker's parentheses. Falls back to
|
|
257
|
+
* `form.optional`, then `'optional'`. The parentheses are the template's, so
|
|
258
|
+
* no locale has to repeat punctuation.
|
|
259
|
+
*/
|
|
260
|
+
optionalLabel = input(null, /* @ts-ignore */
|
|
261
|
+
...(ngDevMode ? [{ debugName: "optionalLabel" }] : /* istanbul ignore next */ []));
|
|
262
|
+
// Unlike the `*` marker beside it, this one is NOT `aria-hidden` — it sits
|
|
263
|
+
// inside the `<label>` and is read as part of the control's accessible name,
|
|
264
|
+
// so an English word here reached a Russian screen reader.
|
|
265
|
+
resolvedOptional = useI18nText(this.optionalLabel, 'form.optional', 'optional');
|
|
249
266
|
/**
|
|
250
267
|
* Force a specific id on the label's `for` attribute. Auto-generated
|
|
251
268
|
* otherwise, and adopted by the projected `wrInput` unless that element
|
|
@@ -375,16 +392,16 @@ class WrFormField {
|
|
|
375
392
|
return parts.join(' ');
|
|
376
393
|
}, /* @ts-ignore */
|
|
377
394
|
...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
378
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
379
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
395
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrFormField, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
396
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: WrFormField, isStandalone: true, selector: "wr-form-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, optional: { classPropertyName: "optional", publicName: "optional", isSignal: true, isRequired: false, transformFunction: null }, optionalLabel: { classPropertyName: "optionalLabel", publicName: "optionalLabel", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, autoErrors: { classPropertyName: "autoErrors", publicName: "autoErrors", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, providers: [
|
|
380
397
|
{
|
|
381
398
|
provide: WR_FORM_FIELD,
|
|
382
399
|
// eslint-disable-next-line @angular-eslint/no-forward-ref
|
|
383
400
|
useExisting: forwardRef(() => WrFormField),
|
|
384
401
|
},
|
|
385
|
-
], queries: [{ propertyName: "ngControl", first: true, predicate: NgControl, descendants: true, isSignal: true }, { propertyName: "projectedErrors", predicate: WrFormError, isSignal: true }], ngImport: i0, template: "@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"labelFor()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">(
|
|
402
|
+
], queries: [{ propertyName: "ngControl", first: true, predicate: NgControl, descendants: true, isSignal: true }, { propertyName: "projectedErrors", predicate: WrFormError, isSignal: true }], ngImport: i0, template: "@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"labelFor()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">({{ resolvedOptional() }})</span>\n }\n </label>\n}\n\n<div class=\"wr-form-field__control\"><ng-content /></div>\n\n@if (hasError()) {\n <div class=\"wr-form-field__errors\" [attr.id]=\"describedBy()\">\n <ng-content select=\"wr-form-error\" />\n @for (message of autoMessages(); track message.key) {\n <div class=\"wr-form-error wr-form-field__error\" role=\"alert\" [attr.data-key]=\"message.key\">\n {{ message.text }}\n </div>\n }\n </div>\n} @else if (hint()) {\n <small class=\"wr-form-field__hint\">{{ hint() }}</small>\n}\n", encapsulation: i0.ViewEncapsulation.None });
|
|
386
403
|
}
|
|
387
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
404
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrFormField, decorators: [{
|
|
388
405
|
type: Component,
|
|
389
406
|
args: [{ selector: 'wr-form-field', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, providers: [
|
|
390
407
|
{
|
|
@@ -392,8 +409,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImpor
|
|
|
392
409
|
// eslint-disable-next-line @angular-eslint/no-forward-ref
|
|
393
410
|
useExisting: forwardRef(() => WrFormField),
|
|
394
411
|
},
|
|
395
|
-
], template: "@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"labelFor()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">(
|
|
396
|
-
}], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], optional: [{ type: i0.Input, args: [{ isSignal: true, alias: "optional", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], ngControl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { isSignal: true }] }], autoErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoErrors", required: false }] }], projectedErrors: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => WrFormError), { isSignal: true }] }] } });
|
|
412
|
+
], template: "@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"labelFor()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">({{ resolvedOptional() }})</span>\n }\n </label>\n}\n\n<div class=\"wr-form-field__control\"><ng-content /></div>\n\n@if (hasError()) {\n <div class=\"wr-form-field__errors\" [attr.id]=\"describedBy()\">\n <ng-content select=\"wr-form-error\" />\n @for (message of autoMessages(); track message.key) {\n <div class=\"wr-form-error wr-form-field__error\" role=\"alert\" [attr.data-key]=\"message.key\">\n {{ message.text }}\n </div>\n }\n </div>\n} @else if (hint()) {\n <small class=\"wr-form-field__hint\">{{ hint() }}</small>\n}\n" }]
|
|
413
|
+
}], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], optional: [{ type: i0.Input, args: [{ isSignal: true, alias: "optional", required: false }] }], optionalLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionalLabel", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], ngControl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { isSignal: true }] }], autoErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoErrors", required: false }] }], projectedErrors: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => WrFormError), { isSignal: true }] }] } });
|
|
397
414
|
|
|
398
415
|
/**
|
|
399
416
|
* Generated bundle index. Do not edit.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-form.mjs","sources":["../../../projects/lib/form/form-item.ts","../../../projects/lib/form/form-errors.ts","../../../projects/lib/form/tokens.ts","../../../projects/lib/form/form-field.ts","../../../projects/lib/form/form-field.html","../../../projects/lib/form/ngwr-form.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 { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\n/**\n * Wraps a label + control + error message into a single field row.\n *\n * Toggle `hasError` to apply error styling to the projected label and\n * any descendant input / textarea / checkbox.\n *\n * @example\n * ```html\n * <wr-form-item [hasError]=\"form.controls.email.invalid\">\n * <label>Email</label>\n * <input wrInput formControlName=\"email\" />\n * <wr-form-error>Invalid email</wr-form-error>\n * </wr-form-item>\n * ```\n *\n * @see https://ngwr.dev/reference/components/form\n */\n@Component({\n selector: 'wr-form-item',\n template: '<ng-content />',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrFormItem {\n /**\n * When `true`, applies error coloring to the projected label and inputs.\n *\n * @default false\n */\n readonly hasError = input(false, { transform: coerceBooleanProperty });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-form-item'];\n if (this.hasError()) parts.push('wr-form-item--error');\n return parts.join(' ');\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 { InjectionToken, type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\n/** Pull a number out of a validator payload without trusting its shape. */\nfunction num(error: unknown, field: string): string {\n if (error && typeof error === 'object' && field in error) {\n const value = (error as Record<string, unknown>)[field];\n if (typeof value === 'number' || typeof value === 'string') return String(value);\n }\n return '';\n}\n\n/** What a message function is handed to build its sentence. */\nexport interface WrFormErrorContext {\n /** Validator key (classic forms) or Signal Forms `kind` that produced the error. */\n readonly key: string;\n /**\n * The validator's own payload — `{ requiredLength, actualLength }` for\n * `minlength`, `{ min }` for `wrMinDate`, and so on. `true` for the\n * flag-shaped validators, which carry nothing.\n */\n readonly error: unknown;\n /** The surrounding field's `label`, so a message can name the field. */\n readonly label: string;\n}\n\n/** A message, or a function that builds one from the validator's payload. */\nexport type WrFormErrorMessage = string | ((ctx: WrFormErrorContext) => string);\n\n/** Validator key → message. Keys are matched exactly against `control.errors`. */\nexport type WrFormErrorMessages = Readonly<Record<string, WrFormErrorMessage>>;\n\n/**\n * App-wide validation copy. Empty by default; `<wr-form-field>` falls through\n * to the `ngwr/i18n` catalog and then to a built-in English sentence, so a\n * form shows a usable message with no configuration at all.\n */\nexport const WR_FORM_ERRORS = new InjectionToken<WrFormErrorMessages>('WR_FORM_ERRORS', {\n providedIn: 'root',\n factory: () => ({}),\n});\n\n/**\n * Register validation messages for the whole app.\n *\n * Only the keys you name are overridden — everything else keeps resolving\n * through the i18n catalog, so this composes with `provideWrI18n()` rather than\n * replacing it. A projected `<wr-form-error key=\"…\">` still wins over both:\n * per-field copy beats app-wide copy.\n *\n * @example\n * ```ts\n * bootstrapApplication(App, {\n * providers: [\n * provideWrFormErrors({\n * required: 'Please fill this in.',\n * minlength: ({ error }) => `At least ${(error as { requiredLength: number }).requiredLength} characters.`,\n * }),\n * ],\n * });\n * ```\n */\nexport function provideWrFormErrors(messages: WrFormErrorMessages = {}): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: WR_FORM_ERRORS, useValue: messages }]);\n}\n\n/**\n * Last-resort English, used when no app catalog and no i18n catalog answer.\n *\n * These exist so that a form with zero configuration still says something true\n * rather than rendering an empty error block. Every key here also ships as a\n * `validation.*` entry in `ngwr/i18n/{en,ru}`, which is what a localized app\n * actually reads.\n */\nexport const WR_FORM_ERROR_FALLBACKS: WrFormErrorMessages = {\n // Angular built-ins.\n required: 'This field is required.',\n requiredTrue: 'This field must be checked.',\n email: 'Enter a valid email address.',\n minlength: ({ error }) => `Enter at least ${num(error, 'requiredLength')} characters.`,\n maxlength: ({ error }) => `Enter at most ${num(error, 'requiredLength')} characters.`,\n min: ({ error }) => `Enter ${num(error, 'min')} or more.`,\n max: ({ error }) => `Enter ${num(error, 'max')} or less.`,\n pattern: 'This value is not in the expected format.',\n\n // WrValidators.\n noWhitespace: 'This field cannot be blank.',\n hexColor: 'Enter a hex colour, e.g. #1a2b3c.',\n url: 'Enter a valid URL.',\n cardNumber: 'Enter a valid card number.',\n cvc: ({ error }) => `Enter the ${num(error, 'length')}-digit security code.`,\n iban: 'Enter a valid IBAN.',\n match: 'The two values do not match.',\n matchFields: 'These fields do not match.',\n oneOf: 'Choose one of the allowed values.',\n minDate: 'Choose a later date.',\n maxDate: 'Choose an earlier date.',\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 { InjectionToken, type Signal } from '@angular/core';\n\n/**\n * What a projected control can read from its surrounding `<wr-form-field>`.\n *\n * The field renders `<label [for]>` before it can see what was projected into\n * it, so the id has to travel the other way: the control adopts the id the\n * label already points at. Doing it through DI keeps it a render-time binding,\n * which is what SSR needs — stamping the id from `afterNextRender` would leave\n * every prerendered page with a label pointing at nothing.\n */\nexport interface WrFormFieldContext {\n /** Id the field's `<label for>` points at. */\n readonly controlId: Signal<string>;\n\n /**\n * The reverse direction, for the one case the paragraph above does not cover: a\n * projected control that already carries its own `id` keeps it, and the label has\n * to follow, or `for` names an element that does not exist and the field ends up\n * with no label at all. Called during the control's construction, so the label's\n * binding still resolves in the same render pass.\n */\n adoptControlId(id: string): void;\n\n /**\n * Validator keys currently in error, once the control is touched or dirty.\n * Empty when the field is valid or has not been interacted with.\n */\n readonly errorKeys: Signal<readonly string[]>;\n\n /** Id of the element listing the visible messages, or `null` when there are none. */\n readonly describedBy: Signal<string | null>;\n}\n\n/** Present only inside a `<wr-form-field>`; inject with `{ optional: true }`. */\nexport const WR_FORM_FIELD = new InjectionToken<WrFormFieldContext>('WR_FORM_FIELD');\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 {\n Component,\n DestroyRef,\n ViewEncapsulation,\n computed,\n contentChild,\n contentChildren,\n effect,\n forwardRef,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport { NgControl } from '@angular/forms';\n\nimport { EMPTY, type Subscription } from 'rxjs';\n\nimport { WrI18n } from 'ngwr/i18n';\n\nimport { WR_FORM_ERRORS, WR_FORM_ERROR_FALLBACKS, type WrFormErrorMessage } from './form-errors';\nimport { WR_FORM_FIELD, type WrFormFieldContext } from './tokens';\n\nlet uid = 0;\n\n/** A message is either literal text or a function over the validator's payload. */\nfunction render(message: WrFormErrorMessage, key: string, error: unknown, label: string): string {\n return typeof message === 'function' ? message({ key, error, label }) : message;\n}\n\n/** Validator payloads are plain objects; anything else interpolates nothing. */\nfunction toParams(error: unknown): Record<string, unknown> | undefined {\n return error && typeof error === 'object' ? (error as Record<string, unknown>) : undefined;\n}\n\n/**\n * One error message tied to a validator key. Renders only when the parent\n * form-field has a matching error in `control.errors`, and only once the\n * control is touched or dirty.\n *\n * @example\n * ```html\n * <wr-form-error key=\"required\">This field is required.</wr-form-error>\n * <wr-form-error key=\"email\">That doesn't look right.</wr-form-error>\n * ```\n */\n@Component({\n selector: 'wr-form-error',\n template: `<ng-content />`,\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-form-error wr-form-field__error',\n role: 'alert',\n '[attr.data-key]': 'key() ?? null',\n '[class.wr-form-field__error--hidden]': '!visible()',\n },\n})\nexport class WrFormError {\n /**\n * Validator key this message corresponds to (e.g. `'required'`).\n * Optional — without a key the message always renders, which is the\n * plain inline-error usage inside `<wr-form-item>`.\n */\n readonly key = input<string>();\n\n private readonly field = inject(WR_FORM_FIELD, { optional: true });\n\n /**\n * Whether this message is the one the current error calls for.\n *\n * The parent projects every `<wr-form-error>` through a single\n * `<ng-content select>`, which cannot filter — so each message decides for\n * itself. Without this, one invalid control rendered \"Email is required.\"\n * and \"That isn't a valid email.\" side by side, which is what the docs have\n * always described as gated behaviour.\n *\n * A keyless message always shows: that is the plain inline-error usage inside\n * `<wr-form-item>`, which has no error discovery at all.\n */\n protected readonly visible = computed(() => {\n const key = this.key();\n if (!key || !this.field) return true;\n return this.field.errorKeys().includes(key);\n });\n}\n\n/**\n * Wraps a label + control + hint + error messages into a single block —\n * the layout primitive every form ends up reinventing.\n *\n * - Auto-binds `label[for]` to the projected control via a generated id\n * (or use the form-control's `[id]` if you set one).\n * - Reads the projected control's `NgControl` to surface validation\n * errors only when the control has been touched / submitted.\n * - Optional `[required]` / `[optional]` markers next to the label.\n *\n * @example\n * ```html\n * <wr-form-field label=\"Email\" hint=\"We'll never share it.\">\n * <input wrInput [formControl]=\"email\" type=\"email\" />\n * <wr-form-error key=\"required\">Email is required.</wr-form-error>\n * <wr-form-error key=\"email\">Not a valid email.</wr-form-error>\n * </wr-form-field>\n * ```\n *\n * @see https://ngwr.dev/reference/components/form-field\n */\n@Component({\n selector: 'wr-form-field',\n templateUrl: './form-field.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n providers: [\n {\n provide: WR_FORM_FIELD,\n // eslint-disable-next-line @angular-eslint/no-forward-ref\n useExisting: forwardRef(() => WrFormField),\n },\n ],\n})\nexport class WrFormField implements WrFormFieldContext {\n /** Label text shown above the control. */\n readonly label = input<string>('');\n\n /** Hint text shown below the control. Hidden when an error is visible. */\n readonly hint = input<string>('');\n\n /** Show a `*` next to the label. @default false */\n readonly required = input(false, { transform: coerceBooleanProperty });\n\n /** Show `(optional)` next to the label. Mutually exclusive with `required`. @default false */\n readonly optional = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Force a specific id on the label's `for` attribute. Auto-generated\n * otherwise, and adopted by the projected `wrInput` unless that element\n * already carries an `id` of its own.\n */\n readonly controlId = input<string>(`wr-form-field-${++uid}`);\n\n // What a projected control reported it actually kept. `null` until one does — most\n // controls simply adopt `controlId`, and only a control with its own `id` overrides\n // it from the other direction.\n private readonly adoptedId = signal<string | null>(null);\n\n /**\n * Id the `<label for>` renders. A control that came with its own `id` keeps it, so\n * the label follows that instead of the generated one — otherwise `for` points at\n * an element that is not in the document and the field is unlabelled.\n */\n protected readonly labelFor = computed(() => this.adoptedId() ?? this.controlId());\n\n /** @internal `WrFormFieldContext` — see the token's docs. */\n adoptControlId(id: string): void {\n this.adoptedId.set(id);\n }\n\n /**\n * Projected `NgControl` — used to read touched / dirty / errors.\n *\n * This catches `[formField]` too: Angular's Signal Forms directive provides\n * `NgControl` via its interop shim, so both forms flavours land here.\n */\n protected readonly ngControl = contentChild(NgControl);\n\n /**\n * Bumped on every event the projected control emits.\n *\n * `AbstractControl`'s `touched` / `dirty` / `errors` accessors are NOT\n * reactive under classic forms — Angular reads them inside `untracked()`, and\n * `errors` is a plain assigned field. A `computed` over them therefore caches\n * its first value and never recomputes, which in a zoneless app means the\n * error block never appears at all. `control.events` is the reactive source\n * Angular does expose, so the computed depends on this counter instead.\n * Signal Forms needs none of this — its interop getters read live signals —\n * and harmlessly contributes no events.\n */\n private readonly revision = signal(0);\n\n constructor() {\n let sub: Subscription | null = null;\n effect(() => {\n const control = this.ngControl()?.control;\n sub?.unsubscribe();\n sub = (control?.events ?? EMPTY).subscribe(() => this.revision.update(n => n + 1));\n });\n inject(DestroyRef).onDestroy(() => sub?.unsubscribe());\n }\n\n protected readonly errors = computed(() => {\n this.revision();\n const c = this.ngControl();\n if (!c) return null;\n if (!c.touched && !c.dirty) return null;\n return c.errors;\n });\n\n protected readonly hasError = computed(() => {\n const errs = this.errors();\n return !!errs && Object.keys(errs).length > 0;\n });\n\n /** @internal Read by the projected `<wr-form-error>` children to decide which of them renders. */\n readonly errorKeys = computed<readonly string[]>(() => Object.keys(this.errors() ?? {}));\n\n /** @internal Read by the projected control so a screen reader is pointed at the message. */\n readonly describedBy = computed(() => (this.hasError() ? `${this.controlId()}-errors` : null));\n\n /**\n * Render a catalog message for any error the markup does not already answer.\n *\n * On by default: a field with no `<wr-form-error>` at all is the common case,\n * and an empty error block helps nobody. Turn it off for a field whose copy is\n * entirely hand-written. @default true\n */\n readonly autoErrors = input(true, { transform: coerceBooleanProperty });\n\n private readonly projectedErrors = contentChildren(WrFormError);\n private readonly appMessages = inject(WR_FORM_ERRORS);\n private readonly i18n = inject(WrI18n, { optional: true });\n\n /**\n * Errors with no projected `<wr-form-error key>` to answer them, resolved to\n * text: the app catalog first, then the `validation.*` i18n keys, then a\n * built-in English sentence. Per-field markup always wins — it is the most\n * specific thing the author wrote.\n */\n protected readonly autoMessages = computed<readonly { key: string; text: string }[]>(() => {\n if (!this.autoErrors()) return [];\n const errors = this.errors();\n if (!errors) return [];\n\n const claimed = new Set(\n this.projectedErrors()\n .map(e => e.key())\n .filter(Boolean)\n );\n\n return Object.entries(errors)\n .filter(([key]) => !claimed.has(key))\n .map(([key, error]) => ({ key, text: this.resolve(key, error) }))\n .filter(m => m.text.length > 0);\n });\n\n private resolve(key: string, error: unknown): string {\n const app = this.appMessages[key];\n if (app !== undefined) return render(app, key, error, this.label());\n\n // `t()` reads the service's revision signal, so this recomputes on a locale\n // switch; a miss returns the key itself, which is the documented signal.\n const translated = this.i18n?.t(`validation.${key}`, toParams(error));\n if (translated && translated !== `validation.${key}`) return translated;\n\n const fallback = WR_FORM_ERROR_FALLBACKS[key];\n return fallback === undefined ? '' : render(fallback, key, error, this.label());\n }\n\n protected readonly classes = computed(() => {\n const parts = ['wr-form-field'];\n if (this.hasError()) parts.push('wr-form-field--invalid');\n if (this.required()) parts.push('wr-form-field--required');\n return parts.join(' ');\n });\n}\n","@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"labelFor()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">(optional)</span>\n }\n </label>\n}\n\n<div class=\"wr-form-field__control\"><ng-content /></div>\n\n@if (hasError()) {\n <div class=\"wr-form-field__errors\" [attr.id]=\"describedBy()\">\n <ng-content select=\"wr-form-error\" />\n @for (message of autoMessages(); track message.key) {\n <div class=\"wr-form-error wr-form-field__error\" role=\"alert\" [attr.data-key]=\"message.key\">\n {{ message.text }}\n </div>\n }\n </div>\n} @else if (hint()) {\n <small class=\"wr-form-field__hint\">{{ hint() }}</small>\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-form.mjs","sources":["../../../projects/lib/form/form-item.ts","../../../projects/lib/form/form-errors.ts","../../../projects/lib/form/tokens.ts","../../../projects/lib/form/form-field.ts","../../../projects/lib/form/form-field.html","../../../projects/lib/form/ngwr-form.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 { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\n/**\n * Wraps a label + control + error message into a single field row.\n *\n * Toggle `hasError` to apply error styling to the projected label and\n * any descendant input / textarea / checkbox.\n *\n * @example\n * ```html\n * <wr-form-item [hasError]=\"form.controls.email.invalid\">\n * <label>Email</label>\n * <input wrInput formControlName=\"email\" />\n * <wr-form-error>Invalid email</wr-form-error>\n * </wr-form-item>\n * ```\n *\n * @see https://ngwr.dev/reference/components/form\n */\n@Component({\n selector: 'wr-form-item',\n template: '<ng-content />',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrFormItem {\n /**\n * When `true`, applies error coloring to the projected label and inputs.\n *\n * @default false\n */\n readonly hasError = input(false, { transform: coerceBooleanProperty });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-form-item'];\n if (this.hasError()) parts.push('wr-form-item--error');\n return parts.join(' ');\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 { InjectionToken, type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\n/** Pull a number out of a validator payload without trusting its shape. */\nfunction num(error: unknown, field: string): string {\n if (error && typeof error === 'object' && field in error) {\n const value = (error as Record<string, unknown>)[field];\n if (typeof value === 'number' || typeof value === 'string') return String(value);\n }\n return '';\n}\n\n/** What a message function is handed to build its sentence. */\nexport interface WrFormErrorContext {\n /** Validator key (classic forms) or Signal Forms `kind` that produced the error. */\n readonly key: string;\n /**\n * The validator's own payload — `{ requiredLength, actualLength }` for\n * `minlength`, `{ min }` for `wrMinDate`, and so on. `true` for the\n * flag-shaped validators, which carry nothing.\n */\n readonly error: unknown;\n /** The surrounding field's `label`, so a message can name the field. */\n readonly label: string;\n}\n\n/** A message, or a function that builds one from the validator's payload. */\nexport type WrFormErrorMessage = string | ((ctx: WrFormErrorContext) => string);\n\n/** Validator key → message. Keys are matched exactly against `control.errors`. */\nexport type WrFormErrorMessages = Readonly<Record<string, WrFormErrorMessage>>;\n\n/**\n * App-wide validation copy. Empty by default; `<wr-form-field>` falls through\n * to the `ngwr/i18n` catalog and then to a built-in English sentence, so a\n * form shows a usable message with no configuration at all.\n */\nexport const WR_FORM_ERRORS = new InjectionToken<WrFormErrorMessages>('WR_FORM_ERRORS', {\n providedIn: 'root',\n factory: () => ({}),\n});\n\n/**\n * Register validation messages for the whole app.\n *\n * Only the keys you name are overridden — everything else keeps resolving\n * through the i18n catalog, so this composes with `provideWrI18n()` rather than\n * replacing it. A projected `<wr-form-error key=\"…\">` still wins over both:\n * per-field copy beats app-wide copy.\n *\n * @example\n * ```ts\n * bootstrapApplication(App, {\n * providers: [\n * provideWrFormErrors({\n * required: 'Please fill this in.',\n * minlength: ({ error }) => `At least ${(error as { requiredLength: number }).requiredLength} characters.`,\n * }),\n * ],\n * });\n * ```\n */\nexport function provideWrFormErrors(messages: WrFormErrorMessages = {}): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: WR_FORM_ERRORS, useValue: messages }]);\n}\n\n/**\n * Last-resort English, used when no app catalog and no i18n catalog answer.\n *\n * These exist so that a form with zero configuration still says something true\n * rather than rendering an empty error block. Every key here also ships as a\n * `validation.*` entry in `ngwr/i18n/{en,ru}`, which is what a localized app\n * actually reads.\n */\nexport const WR_FORM_ERROR_FALLBACKS: WrFormErrorMessages = {\n // Angular built-ins.\n required: 'This field is required.',\n requiredTrue: 'This field must be checked.',\n email: 'Enter a valid email address.',\n minlength: ({ error }) => `Enter at least ${num(error, 'requiredLength')} characters.`,\n maxlength: ({ error }) => `Enter at most ${num(error, 'requiredLength')} characters.`,\n min: ({ error }) => `Enter ${num(error, 'min')} or more.`,\n max: ({ error }) => `Enter ${num(error, 'max')} or less.`,\n pattern: 'This value is not in the expected format.',\n\n // WrValidators.\n // Not \"cannot be blank\": the validator PASSES on an empty value and fails on\n // any value that holds whitespace, so the blank wording described the one case\n // that never reaches this message — over a field reading `Ada Lovelace`.\n noWhitespace: 'This value cannot contain spaces.',\n hexColor: 'Enter a hex colour, e.g. #1a2b3c.',\n url: 'Enter a valid URL.',\n cardNumber: 'Enter a valid card number.',\n cvc: ({ error }) => `Enter the ${num(error, 'length')}-digit security code.`,\n iban: 'Enter a valid IBAN.',\n match: 'The two values do not match.',\n matchFields: 'These fields do not match.',\n oneOf: 'Choose one of the allowed values.',\n minDate: 'Choose a later date.',\n maxDate: 'Choose an earlier date.',\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 { InjectionToken, type Signal } from '@angular/core';\n\n/**\n * What a projected control can read from its surrounding `<wr-form-field>`.\n *\n * The field renders `<label [for]>` before it can see what was projected into\n * it, so the id has to travel the other way: the control adopts the id the\n * label already points at. Doing it through DI keeps it a render-time binding,\n * which is what SSR needs — stamping the id from `afterNextRender` would leave\n * every prerendered page with a label pointing at nothing.\n */\nexport interface WrFormFieldContext {\n /** Id the field's `<label for>` points at. */\n readonly controlId: Signal<string>;\n\n /**\n * The reverse direction, for the one case the paragraph above does not cover: a\n * projected control that already carries its own `id` keeps it, and the label has\n * to follow, or `for` names an element that does not exist and the field ends up\n * with no label at all. Called during the control's construction, so the label's\n * binding still resolves in the same render pass.\n */\n adoptControlId(id: string): void;\n\n /**\n * Validator keys currently in error, once the control is touched or dirty.\n * Empty when the field is valid or has not been interacted with.\n */\n readonly errorKeys: Signal<readonly string[]>;\n\n /** Id of the element listing the visible messages, or `null` when there are none. */\n readonly describedBy: Signal<string | null>;\n}\n\n/** Present only inside a `<wr-form-field>`; inject with `{ optional: true }`. */\nexport const WR_FORM_FIELD = new InjectionToken<WrFormFieldContext>('WR_FORM_FIELD');\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 {\n Component,\n DestroyRef,\n ViewEncapsulation,\n computed,\n contentChild,\n contentChildren,\n effect,\n forwardRef,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport { NgControl } from '@angular/forms';\n\nimport { EMPTY, type Subscription } from 'rxjs';\n\nimport { WrI18n, useI18nText } from 'ngwr/i18n';\n\nimport { WR_FORM_ERRORS, WR_FORM_ERROR_FALLBACKS, type WrFormErrorMessage } from './form-errors';\nimport { WR_FORM_FIELD, type WrFormFieldContext } from './tokens';\n\nlet uid = 0;\n\n/** A message is either literal text or a function over the validator's payload. */\nfunction render(message: WrFormErrorMessage, key: string, error: unknown, label: string): string {\n return typeof message === 'function' ? message({ key, error, label }) : message;\n}\n\n/** Validator payloads are plain objects; anything else interpolates nothing. */\nfunction toParams(error: unknown): Record<string, unknown> | undefined {\n return error && typeof error === 'object' ? (error as Record<string, unknown>) : undefined;\n}\n\n/**\n * One error message tied to a validator key. Renders only when the parent\n * form-field has a matching error in `control.errors`, and only once the\n * control is touched or dirty.\n *\n * @example\n * ```html\n * <wr-form-error key=\"required\">This field is required.</wr-form-error>\n * <wr-form-error key=\"email\">That doesn't look right.</wr-form-error>\n * ```\n */\n@Component({\n selector: 'wr-form-error',\n template: `<ng-content />`,\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-form-error wr-form-field__error',\n role: 'alert',\n '[attr.data-key]': 'key() ?? null',\n '[class.wr-form-field__error--hidden]': '!visible()',\n },\n})\nexport class WrFormError {\n /**\n * Validator key this message corresponds to (e.g. `'required'`).\n * Optional — without a key the message always renders, which is the\n * plain inline-error usage inside `<wr-form-item>`.\n */\n readonly key = input<string>();\n\n private readonly field = inject(WR_FORM_FIELD, { optional: true });\n\n /**\n * Whether this message is the one the current error calls for.\n *\n * The parent projects every `<wr-form-error>` through a single\n * `<ng-content select>`, which cannot filter — so each message decides for\n * itself. Without this, one invalid control rendered \"Email is required.\"\n * and \"That isn't a valid email.\" side by side, which is what the docs have\n * always described as gated behaviour.\n *\n * A keyless message always shows: that is the plain inline-error usage inside\n * `<wr-form-item>`, which has no error discovery at all.\n */\n protected readonly visible = computed(() => {\n const key = this.key();\n if (!key || !this.field) return true;\n return this.field.errorKeys().includes(key);\n });\n}\n\n/**\n * Wraps a label + control + hint + error messages into a single block —\n * the layout primitive every form ends up reinventing.\n *\n * - Auto-binds `label[for]` to the projected control via a generated id\n * (or use the form-control's `[id]` if you set one).\n * - Reads the projected control's `NgControl` to surface validation\n * errors only when the control has been touched / submitted.\n * - Optional `[required]` / `[optional]` markers next to the label.\n *\n * @example\n * ```html\n * <wr-form-field label=\"Email\" hint=\"We'll never share it.\">\n * <input wrInput [formControl]=\"email\" type=\"email\" />\n * <wr-form-error key=\"required\">Email is required.</wr-form-error>\n * <wr-form-error key=\"email\">Not a valid email.</wr-form-error>\n * </wr-form-field>\n * ```\n *\n * @see https://ngwr.dev/reference/components/form-field\n */\n@Component({\n selector: 'wr-form-field',\n templateUrl: './form-field.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n providers: [\n {\n provide: WR_FORM_FIELD,\n // eslint-disable-next-line @angular-eslint/no-forward-ref\n useExisting: forwardRef(() => WrFormField),\n },\n ],\n})\nexport class WrFormField implements WrFormFieldContext {\n /** Label text shown above the control. */\n readonly label = input<string>('');\n\n /** Hint text shown below the control. Hidden when an error is visible. */\n readonly hint = input<string>('');\n\n /** Show a `*` next to the label. @default false */\n readonly required = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Show an \"optional\" marker next to the label — the word comes from\n * `optionalLabel`. Mutually exclusive with `required`. @default false\n */\n readonly optional = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Word inside the optional marker's parentheses. Falls back to\n * `form.optional`, then `'optional'`. The parentheses are the template's, so\n * no locale has to repeat punctuation.\n */\n readonly optionalLabel = input<string | null>(null);\n\n // Unlike the `*` marker beside it, this one is NOT `aria-hidden` — it sits\n // inside the `<label>` and is read as part of the control's accessible name,\n // so an English word here reached a Russian screen reader.\n protected readonly resolvedOptional = useI18nText(this.optionalLabel, 'form.optional', 'optional');\n\n /**\n * Force a specific id on the label's `for` attribute. Auto-generated\n * otherwise, and adopted by the projected `wrInput` unless that element\n * already carries an `id` of its own.\n */\n readonly controlId = input<string>(`wr-form-field-${++uid}`);\n\n // What a projected control reported it actually kept. `null` until one does — most\n // controls simply adopt `controlId`, and only a control with its own `id` overrides\n // it from the other direction.\n private readonly adoptedId = signal<string | null>(null);\n\n /**\n * Id the `<label for>` renders. A control that came with its own `id` keeps it, so\n * the label follows that instead of the generated one — otherwise `for` points at\n * an element that is not in the document and the field is unlabelled.\n */\n protected readonly labelFor = computed(() => this.adoptedId() ?? this.controlId());\n\n /** @internal `WrFormFieldContext` — see the token's docs. */\n adoptControlId(id: string): void {\n this.adoptedId.set(id);\n }\n\n /**\n * Projected `NgControl` — used to read touched / dirty / errors.\n *\n * This catches `[formField]` too: Angular's Signal Forms directive provides\n * `NgControl` via its interop shim, so both forms flavours land here.\n */\n protected readonly ngControl = contentChild(NgControl);\n\n /**\n * Bumped on every event the projected control emits.\n *\n * `AbstractControl`'s `touched` / `dirty` / `errors` accessors are NOT\n * reactive under classic forms — Angular reads them inside `untracked()`, and\n * `errors` is a plain assigned field. A `computed` over them therefore caches\n * its first value and never recomputes, which in a zoneless app means the\n * error block never appears at all. `control.events` is the reactive source\n * Angular does expose, so the computed depends on this counter instead.\n * Signal Forms needs none of this — its interop getters read live signals —\n * and harmlessly contributes no events.\n */\n private readonly revision = signal(0);\n\n constructor() {\n let sub: Subscription | null = null;\n effect(() => {\n const control = this.ngControl()?.control;\n sub?.unsubscribe();\n sub = (control?.events ?? EMPTY).subscribe(() => this.revision.update(n => n + 1));\n });\n inject(DestroyRef).onDestroy(() => sub?.unsubscribe());\n }\n\n protected readonly errors = computed(() => {\n this.revision();\n const c = this.ngControl();\n if (!c) return null;\n if (!c.touched && !c.dirty) return null;\n return c.errors;\n });\n\n protected readonly hasError = computed(() => {\n const errs = this.errors();\n return !!errs && Object.keys(errs).length > 0;\n });\n\n /** @internal Read by the projected `<wr-form-error>` children to decide which of them renders. */\n readonly errorKeys = computed<readonly string[]>(() => Object.keys(this.errors() ?? {}));\n\n /** @internal Read by the projected control so a screen reader is pointed at the message. */\n readonly describedBy = computed(() => (this.hasError() ? `${this.controlId()}-errors` : null));\n\n /**\n * Render a catalog message for any error the markup does not already answer.\n *\n * On by default: a field with no `<wr-form-error>` at all is the common case,\n * and an empty error block helps nobody. Turn it off for a field whose copy is\n * entirely hand-written. @default true\n */\n readonly autoErrors = input(true, { transform: coerceBooleanProperty });\n\n private readonly projectedErrors = contentChildren(WrFormError);\n private readonly appMessages = inject(WR_FORM_ERRORS);\n private readonly i18n = inject(WrI18n, { optional: true });\n\n /**\n * Errors with no projected `<wr-form-error key>` to answer them, resolved to\n * text: the app catalog first, then the `validation.*` i18n keys, then a\n * built-in English sentence. Per-field markup always wins — it is the most\n * specific thing the author wrote.\n */\n protected readonly autoMessages = computed<readonly { key: string; text: string }[]>(() => {\n if (!this.autoErrors()) return [];\n const errors = this.errors();\n if (!errors) return [];\n\n const claimed = new Set(\n this.projectedErrors()\n .map(e => e.key())\n .filter(Boolean)\n );\n\n return Object.entries(errors)\n .filter(([key]) => !claimed.has(key))\n .map(([key, error]) => ({ key, text: this.resolve(key, error) }))\n .filter(m => m.text.length > 0);\n });\n\n private resolve(key: string, error: unknown): string {\n const app = this.appMessages[key];\n if (app !== undefined) return render(app, key, error, this.label());\n\n // `t()` reads the service's revision signal, so this recomputes on a locale\n // switch; a miss returns the key itself, which is the documented signal.\n const translated = this.i18n?.t(`validation.${key}`, toParams(error));\n if (translated && translated !== `validation.${key}`) return translated;\n\n const fallback = WR_FORM_ERROR_FALLBACKS[key];\n return fallback === undefined ? '' : render(fallback, key, error, this.label());\n }\n\n protected readonly classes = computed(() => {\n const parts = ['wr-form-field'];\n if (this.hasError()) parts.push('wr-form-field--invalid');\n if (this.required()) parts.push('wr-form-field--required');\n return parts.join(' ');\n });\n}\n","@if (label()) {\n <label class=\"wr-form-field__label\" [attr.for]=\"labelFor()\">\n {{ label() }}\n @if (required()) {\n <span class=\"wr-form-field__required\" aria-hidden=\"true\">*</span>\n } @else if (optional()) {\n <span class=\"wr-form-field__optional\">({{ resolvedOptional() }})</span>\n }\n </label>\n}\n\n<div class=\"wr-form-field__control\"><ng-content /></div>\n\n@if (hasError()) {\n <div class=\"wr-form-field__errors\" [attr.id]=\"describedBy()\">\n <ng-content select=\"wr-form-error\" />\n @for (message of autoMessages(); track message.key) {\n <div class=\"wr-form-error wr-form-field__error\" role=\"alert\" [attr.data-key]=\"message.key\">\n {{ message.text }}\n </div>\n }\n </div>\n} @else if (hint()) {\n <small class=\"wr-form-field__hint\">{{ hint() }}</small>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Test harness for `<wr-fuzzy-text>` — a headline drawn into a canvas, one row of pixels
|
|
11
|
+
* at a time, with a random offset per row.
|
|
12
|
+
*
|
|
13
|
+
* **The drawing is out of reach and the accessible half is not, which is the whole shape
|
|
14
|
+
* of this harness.** There is no 2D context in a unit test — `getContext('2d')` returns
|
|
15
|
+
* `null`, and the component's `init()` gives up on the line after — so nothing downstream
|
|
16
|
+
* of that ever happens: no font load, no animation frame, no canvas listeners, no sizing.
|
|
17
|
+
* What survives is the markup, and the markup is the part that regressed: everything this
|
|
18
|
+
* component rendered used to be a canvas, so a screen reader got an empty element where
|
|
19
|
+
* the page showed a headline. {@link getText} and {@link isDecorative} are that fix,
|
|
20
|
+
* asserted as the PAIR it has to be — a readable copy plus a hidden pixel copy. Reading
|
|
21
|
+
* one without the other passes just as happily on a component that announces the headline
|
|
22
|
+
* twice.
|
|
23
|
+
*
|
|
24
|
+
* **The canvas is selected by tag, not by a class.** It carries no BEM name, unlike the
|
|
25
|
+
* two sibling animation canvases — this entry point ships no stylesheet at all, which is
|
|
26
|
+
* also why {@link isTextVisuallyHidden} is a real question rather than a formality.
|
|
27
|
+
*
|
|
28
|
+
* Nothing about the animation is offered, and none of it is a gap that could be closed by
|
|
29
|
+
* trying harder. There is no `getIntensity()` / `isFuzzing()` / `isGlitching()`: those are
|
|
30
|
+
* closure variables inside `init()` that only ever manifest as pixels, and in a test the
|
|
31
|
+
* function returns before they exist. No `hover()` / `click()` / `getHoverIntensity()`:
|
|
32
|
+
* the pointer listeners are attached only after a real context AND a resolved
|
|
33
|
+
* `document.fonts` — neither of which a unit test has — so a dispatched event would land
|
|
34
|
+
* on nothing and the assertion would pass vacuously; the hit test also measures the canvas
|
|
35
|
+
* box, which is all zeros. No `getCanvasSize()`, which would be asserting jsdom's untouched
|
|
36
|
+
* 300×150 default rather than the component's layout maths. No `getColor()` /
|
|
37
|
+
* `getFontSize()` / `getFontFamily()` / `getGradient()`: every one of those becomes a
|
|
38
|
+
* canvas font string or a fill style and never reaches the DOM. No `getRenderedText()` or
|
|
39
|
+
* `readPixel()` — there are no pixels, and faking a context would change what every spec
|
|
40
|
+
* here is testing. And no `isReducedMotion()`: the preference is honoured by taking a
|
|
41
|
+
* different DRAWING path, with identical markup, classes and attributes either way, so the
|
|
42
|
+
* method would answer the same for a component that honoured it and one that ignored it.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const headline = await loader.getHarness(WrFuzzyTextHarness.with({ text: '404' }));
|
|
47
|
+
*
|
|
48
|
+
* expect(await headline.getText()).toBe('404');
|
|
49
|
+
* expect(await headline.isDecorative()).toBe(true);
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @see https://ngwr.dev/guides/testing
|
|
53
|
+
*/
|
|
54
|
+
class WrFuzzyTextHarness extends ComponentHarness {
|
|
55
|
+
static hostSelector = 'wr-fuzzy-text';
|
|
56
|
+
/** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
|
|
57
|
+
static with(options = {}) {
|
|
58
|
+
return new HarnessPredicate(WrFuzzyTextHarness, options).addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text));
|
|
59
|
+
}
|
|
60
|
+
srOnly = this.locatorFor('.wr-fuzzy-text__sr-only');
|
|
61
|
+
canvas = this.locatorForOptional('canvas');
|
|
62
|
+
/**
|
|
63
|
+
* The headline as TEXT, trimmed.
|
|
64
|
+
*
|
|
65
|
+
* From the screen-reader span, which is the only place the string exists as characters —
|
|
66
|
+
* the visible copy is glyphs painted into a canvas. This is the single assertion that
|
|
67
|
+
* separates the component from an empty box for anyone not looking at the screen, and it
|
|
68
|
+
* is the one that has already regressed once.
|
|
69
|
+
*
|
|
70
|
+
* There is no letter-by-letter split to worry about here, unlike the other text
|
|
71
|
+
* animations in the library: the span holds one text node, so what a reader announces is
|
|
72
|
+
* what {@link WrFuzzyTextHarness.getText} returns.
|
|
73
|
+
*/
|
|
74
|
+
async getText() {
|
|
75
|
+
return (await this.srOnly()).text();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Whether the pixel copy stays out of the accessibility tree.
|
|
79
|
+
*
|
|
80
|
+
* The other half of {@link getText}. The canvas has no text and no name, so without its
|
|
81
|
+
* `aria-hidden` a reader meets an anonymous element next to the headline it already
|
|
82
|
+
* read — or, if the span ever goes away again, only the anonymous element. Asserting the
|
|
83
|
+
* two together is what pins the arrangement; asserting either alone does not.
|
|
84
|
+
*/
|
|
85
|
+
async isDecorative() {
|
|
86
|
+
const canvas = await this.canvas();
|
|
87
|
+
if (!canvas) {
|
|
88
|
+
throw new Error('WrFuzzyTextHarness.isDecorative(): there is no <canvas> in this headline at all, so there is nothing to ' +
|
|
89
|
+
'keep out of the accessibility tree. That is a different failure from an announced canvas — ask ' +
|
|
90
|
+
'hasCanvas() to tell the two apart.');
|
|
91
|
+
}
|
|
92
|
+
return (await canvas.getAttribute('aria-hidden')) === 'true';
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Whether the drawing surface is in the markup.
|
|
96
|
+
*
|
|
97
|
+
* True even where it can never be painted — under SSR, in a prerender, in a browser that
|
|
98
|
+
* refuses a 2D context, and in every unit test — because the element is rendered
|
|
99
|
+
* unconditionally and only the drawing depends on a context. That is precisely the state
|
|
100
|
+
* the readable copy exists to cover, so this is the method that says "the fallback is
|
|
101
|
+
* carrying the page right now".
|
|
102
|
+
*/
|
|
103
|
+
async hasCanvas() {
|
|
104
|
+
return (await this.canvas()) !== null;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Whether the readable copy hides itself without a stylesheet.
|
|
108
|
+
*
|
|
109
|
+
* This entry point ships no CSS. The span is therefore hidden with inline declarations
|
|
110
|
+
* rather than an `.sr-only` class, and that is load-bearing rather than a style choice:
|
|
111
|
+
* move the block into a class and every consumer who imported no CSS sees the headline
|
|
112
|
+
* twice — once as pixels, once as a stray line of markup underneath.
|
|
113
|
+
*
|
|
114
|
+
* Three of the declarations are checked, not one, because they fail apart: `position`
|
|
115
|
+
* takes the span out of flow, the 1px box collapses it, and `clip-path` is what keeps
|
|
116
|
+
* the character still reachable by a reader instead of `display: none`-ing it away.
|
|
117
|
+
*/
|
|
118
|
+
async isTextVisuallyHidden() {
|
|
119
|
+
const span = await this.srOnly();
|
|
120
|
+
const [position, width, clipPath] = await Promise.all([
|
|
121
|
+
span.getCssValue('position'),
|
|
122
|
+
span.getCssValue('width'),
|
|
123
|
+
span.getCssValue('clip-path'),
|
|
124
|
+
]);
|
|
125
|
+
return position === 'absolute' && width === '1px' && clipPath === 'inset(50%)';
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Generated bundle index. Do not edit.
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
export { WrFuzzyTextHarness };
|
|
134
|
+
//# sourceMappingURL=ngwr-fuzzy-text-testing.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngwr-fuzzy-text-testing.mjs","sources":["../../../projects/lib/fuzzy-text/testing/wr-fuzzy-text-harness.ts","../../../projects/lib/fuzzy-text/testing/ngwr-fuzzy-text-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 { WrFuzzyTextHarnessFilters } from './interfaces';\n\n/**\n * Test harness for `<wr-fuzzy-text>` — a headline drawn into a canvas, one row of pixels\n * at a time, with a random offset per row.\n *\n * **The drawing is out of reach and the accessible half is not, which is the whole shape\n * of this harness.** There is no 2D context in a unit test — `getContext('2d')` returns\n * `null`, and the component's `init()` gives up on the line after — so nothing downstream\n * of that ever happens: no font load, no animation frame, no canvas listeners, no sizing.\n * What survives is the markup, and the markup is the part that regressed: everything this\n * component rendered used to be a canvas, so a screen reader got an empty element where\n * the page showed a headline. {@link getText} and {@link isDecorative} are that fix,\n * asserted as the PAIR it has to be — a readable copy plus a hidden pixel copy. Reading\n * one without the other passes just as happily on a component that announces the headline\n * twice.\n *\n * **The canvas is selected by tag, not by a class.** It carries no BEM name, unlike the\n * two sibling animation canvases — this entry point ships no stylesheet at all, which is\n * also why {@link isTextVisuallyHidden} is a real question rather than a formality.\n *\n * Nothing about the animation is offered, and none of it is a gap that could be closed by\n * trying harder. There is no `getIntensity()` / `isFuzzing()` / `isGlitching()`: those are\n * closure variables inside `init()` that only ever manifest as pixels, and in a test the\n * function returns before they exist. No `hover()` / `click()` / `getHoverIntensity()`:\n * the pointer listeners are attached only after a real context AND a resolved\n * `document.fonts` — neither of which a unit test has — so a dispatched event would land\n * on nothing and the assertion would pass vacuously; the hit test also measures the canvas\n * box, which is all zeros. No `getCanvasSize()`, which would be asserting jsdom's untouched\n * 300×150 default rather than the component's layout maths. No `getColor()` /\n * `getFontSize()` / `getFontFamily()` / `getGradient()`: every one of those becomes a\n * canvas font string or a fill style and never reaches the DOM. No `getRenderedText()` or\n * `readPixel()` — there are no pixels, and faking a context would change what every spec\n * here is testing. And no `isReducedMotion()`: the preference is honoured by taking a\n * different DRAWING path, with identical markup, classes and attributes either way, so the\n * method would answer the same for a component that honoured it and one that ignored it.\n *\n * @example\n * ```ts\n * const headline = await loader.getHarness(WrFuzzyTextHarness.with({ text: '404' }));\n *\n * expect(await headline.getText()).toBe('404');\n * expect(await headline.isDecorative()).toBe(true);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrFuzzyTextHarness extends ComponentHarness {\n static hostSelector = 'wr-fuzzy-text';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrFuzzyTextHarnessFilters = {}): HarnessPredicate<WrFuzzyTextHarness> {\n return new HarnessPredicate(WrFuzzyTextHarness, options).addOption('text', options.text, (harness, text) =>\n HarnessPredicate.stringMatches(harness.getText(), text)\n );\n }\n\n private readonly srOnly = this.locatorFor('.wr-fuzzy-text__sr-only');\n private readonly canvas = this.locatorForOptional('canvas');\n\n /**\n * The headline as TEXT, trimmed.\n *\n * From the screen-reader span, which is the only place the string exists as characters —\n * the visible copy is glyphs painted into a canvas. This is the single assertion that\n * separates the component from an empty box for anyone not looking at the screen, and it\n * is the one that has already regressed once.\n *\n * There is no letter-by-letter split to worry about here, unlike the other text\n * animations in the library: the span holds one text node, so what a reader announces is\n * what {@link WrFuzzyTextHarness.getText} returns.\n */\n async getText(): Promise<string> {\n return (await this.srOnly()).text();\n }\n\n /**\n * Whether the pixel copy stays out of the accessibility tree.\n *\n * The other half of {@link getText}. The canvas has no text and no name, so without its\n * `aria-hidden` a reader meets an anonymous element next to the headline it already\n * read — or, if the span ever goes away again, only the anonymous element. Asserting the\n * two together is what pins the arrangement; asserting either alone does not.\n */\n async isDecorative(): Promise<boolean> {\n const canvas = await this.canvas();\n if (!canvas) {\n throw new Error(\n 'WrFuzzyTextHarness.isDecorative(): there is no <canvas> in this headline at all, so there is nothing to ' +\n 'keep out of the accessibility tree. That is a different failure from an announced canvas — ask ' +\n 'hasCanvas() to tell the two apart.'\n );\n }\n return (await canvas.getAttribute('aria-hidden')) === 'true';\n }\n\n /**\n * Whether the drawing surface is in the markup.\n *\n * True even where it can never be painted — under SSR, in a prerender, in a browser that\n * refuses a 2D context, and in every unit test — because the element is rendered\n * unconditionally and only the drawing depends on a context. That is precisely the state\n * the readable copy exists to cover, so this is the method that says \"the fallback is\n * carrying the page right now\".\n */\n async hasCanvas(): Promise<boolean> {\n return (await this.canvas()) !== null;\n }\n\n /**\n * Whether the readable copy hides itself without a stylesheet.\n *\n * This entry point ships no CSS. The span is therefore hidden with inline declarations\n * rather than an `.sr-only` class, and that is load-bearing rather than a style choice:\n * move the block into a class and every consumer who imported no CSS sees the headline\n * twice — once as pixels, once as a stray line of markup underneath.\n *\n * Three of the declarations are checked, not one, because they fail apart: `position`\n * takes the span out of flow, the 1px box collapses it, and `clip-path` is what keeps\n * the character still reachable by a reader instead of `display: none`-ing it away.\n */\n async isTextVisuallyHidden(): Promise<boolean> {\n const span = await this.srOnly();\n const [position, width, clipPath] = await Promise.all([\n span.getCssValue('position'),\n span.getCssValue('width'),\n span.getCssValue('clip-path'),\n ]);\n return position === 'absolute' && width === '1px' && clipPath === 'inset(50%)';\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;AAAA;;;;;AAKG;AAMH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CG;AACG,MAAO,kBAAmB,SAAQ,gBAAgB,CAAA;AACtD,IAAA,OAAO,YAAY,GAAG,eAAe;;AAGrC,IAAA,OAAO,IAAI,CAAC,OAAA,GAAqC,EAAE,EAAA;AACjD,QAAA,OAAO,IAAI,gBAAgB,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,IAAI,KACrG,gBAAgB,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,CACxD;IACH;AAEiB,IAAA,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,yBAAyB,CAAC;AACnD,IAAA,MAAM,GAAG,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC;AAE3D;;;;;;;;;;;AAWG;AACH,IAAA,MAAM,OAAO,GAAA;QACX,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE;IACrC;AAEA;;;;;;;AAOG;AACH,IAAA,MAAM,YAAY,GAAA;AAChB,QAAA,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE;QAClC,IAAI,CAAC,MAAM,EAAE;YACX,MAAM,IAAI,KAAK,CACb,0GAA0G;gBACxG,iGAAiG;AACjG,gBAAA,oCAAoC,CACvC;QACH;QACA,OAAO,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,aAAa,CAAC,MAAM,MAAM;IAC9D;AAEA;;;;;;;;AAQG;AACH,IAAA,MAAM,SAAS,GAAA;QACb,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI;IACvC;AAEA;;;;;;;;;;;AAWG;AACH,IAAA,MAAM,oBAAoB,GAAA;AACxB,QAAA,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE;AAChC,QAAA,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;AACpD,YAAA,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC;AAC5B,YAAA,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;AACzB,YAAA,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC;AAC9B,SAAA,CAAC;QACF,OAAO,QAAQ,KAAK,UAAU,IAAI,KAAK,KAAK,KAAK,IAAI,QAAQ,KAAK,YAAY;IAChF;;;AC1IF;;AAEG;;;;"}
|
|
@@ -348,14 +348,24 @@ class WrFuzzyText {
|
|
|
348
348
|
isClicking = false;
|
|
349
349
|
}, 150);
|
|
350
350
|
};
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
351
|
+
// Attached unconditionally, and gated inside the three handlers that can SET a
|
|
352
|
+
// reactive state instead. `enableHover` and `clickEffect` are not in the re-init
|
|
353
|
+
// effect's dependency list, so an attach conditional on them read the value at
|
|
354
|
+
// boot and never again: a `[clickEffect]` bound to a signal that starts `false` —
|
|
355
|
+
// the default, so the ordinary case — stayed dead after it flipped true. Adding
|
|
356
|
+
// them to that list would restart the rAF loop and the glitch timers just to
|
|
357
|
+
// toggle a listener, which is visible; the guards are not. `onTouchMove` calls
|
|
358
|
+
// `preventDefault()` only past its own guard, so a hover-disabled instance still
|
|
359
|
+
// swallows nothing.
|
|
360
|
+
//
|
|
361
|
+
// `onMouseLeave` / `onTouchEnd` stay ungated deliberately: they only ever CLEAR
|
|
362
|
+
// toward the idle state, and gating them would strand `isHovering` on for good
|
|
363
|
+
// when hover is switched off mid-hover.
|
|
364
|
+
canvas.addEventListener('mousemove', onMouseMove);
|
|
365
|
+
canvas.addEventListener('mouseleave', onMouseLeave);
|
|
366
|
+
canvas.addEventListener('touchmove', onTouchMove, { passive: false });
|
|
367
|
+
canvas.addEventListener('touchend', onTouchEnd);
|
|
368
|
+
canvas.addEventListener('click', onClick);
|
|
359
369
|
this.teardown = () => {
|
|
360
370
|
cancelAnimationFrame(rafId);
|
|
361
371
|
clearTimeout(glitchOnTimer);
|
|
@@ -368,10 +378,10 @@ class WrFuzzyText {
|
|
|
368
378
|
canvas.removeEventListener('click', onClick);
|
|
369
379
|
};
|
|
370
380
|
}
|
|
371
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
372
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.
|
|
381
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrFuzzyText, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
382
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.2", type: WrFuzzyText, isStandalone: true, selector: "wr-fuzzy-text", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, fontSize: { classPropertyName: "fontSize", publicName: "fontSize", isSignal: true, isRequired: false, transformFunction: null }, fontWeight: { classPropertyName: "fontWeight", publicName: "fontWeight", isSignal: true, isRequired: false, transformFunction: null }, fontFamily: { classPropertyName: "fontFamily", publicName: "fontFamily", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, gradient: { classPropertyName: "gradient", publicName: "gradient", isSignal: true, isRequired: false, transformFunction: null }, enableHover: { classPropertyName: "enableHover", publicName: "enableHover", isSignal: true, isRequired: false, transformFunction: null }, baseIntensity: { classPropertyName: "baseIntensity", publicName: "baseIntensity", isSignal: true, isRequired: false, transformFunction: null }, hoverIntensity: { classPropertyName: "hoverIntensity", publicName: "hoverIntensity", isSignal: true, isRequired: false, transformFunction: null }, fuzzRange: { classPropertyName: "fuzzRange", publicName: "fuzzRange", isSignal: true, isRequired: false, transformFunction: null }, fps: { classPropertyName: "fps", publicName: "fps", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, transitionDuration: { classPropertyName: "transitionDuration", publicName: "transitionDuration", isSignal: true, isRequired: false, transformFunction: null }, clickEffect: { classPropertyName: "clickEffect", publicName: "clickEffect", isSignal: true, isRequired: false, transformFunction: null }, glitchMode: { classPropertyName: "glitchMode", publicName: "glitchMode", isSignal: true, isRequired: false, transformFunction: null }, glitchInterval: { classPropertyName: "glitchInterval", publicName: "glitchInterval", isSignal: true, isRequired: false, transformFunction: null }, glitchDuration: { classPropertyName: "glitchDuration", publicName: "glitchDuration", isSignal: true, isRequired: false, transformFunction: null }, letterSpacing: { classPropertyName: "letterSpacing", publicName: "letterSpacing", isSignal: true, isRequired: false, transformFunction: null } }, host: { styleAttribute: "color: var(--wr-color-dark)", classAttribute: "wr-fuzzy-text" }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvas"], descendants: true, isSignal: true }], ngImport: i0, template: "<span class=\"wr-fuzzy-text__sr-only\" style=\"position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap\">{{ text() }}</span><canvas #canvas aria-hidden=\"true\"></canvas>", isInline: true, encapsulation: i0.ViewEncapsulation.None });
|
|
373
383
|
}
|
|
374
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
384
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrFuzzyText, decorators: [{
|
|
375
385
|
type: Component,
|
|
376
386
|
args: [{
|
|
377
387
|
selector: 'wr-fuzzy-text',
|