ngwr 11.2.0 → 12.1.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 +85 -26
- 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/collapse/styles/_index.scss +2 -2
- package/color-picker/styles/_index.scss +3 -3
- 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.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 +20 -2
- package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-bar-chart.mjs +3 -3
- package/fesm2022/ngwr-blur-text.mjs +3 -3
- package/fesm2022/ngwr-blur-text.mjs.map +1 -1
- 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.mjs +17 -5
- package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-testing.mjs +1 -1
- package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
- package/fesm2022/ngwr-calendar.mjs +187 -9
- 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 +48 -5
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox.mjs +6 -6
- 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 +26 -6
- package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker.mjs +98 -8
- 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 +333 -43
- 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 +11 -6
- 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.mjs +3 -3
- 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 +8 -4
- package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -1
- 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 +121 -58
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +26 -10
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-empty.mjs +3 -3
- package/fesm2022/ngwr-event-calendar.mjs +28 -15
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text-testing.mjs +5 -3
- package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -1
- 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.mjs +21 -11
- package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
- package/fesm2022/ngwr-gauge.mjs +3 -3
- package/fesm2022/ngwr-glitch-text-testing.mjs +51 -17
- package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-glitch-text.mjs +7 -7
- package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
- package/fesm2022/ngwr-gradient-text-testing.mjs +46 -11
- package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -1
- 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 +73 -1
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +55 -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 +53 -9
- package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper.mjs +150 -12
- 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 +17 -10
- 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.mjs +3 -3
- package/fesm2022/ngwr-layout.mjs +15 -15
- package/fesm2022/ngwr-lightbox.mjs +4 -4
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- 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.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 +26 -6
- package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -1
- package/fesm2022/ngwr-meter-group.mjs +3 -3
- package/fesm2022/ngwr-overlay.mjs +6 -6
- 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 +83 -16
- 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 +155 -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.mjs +3 -3
- package/fesm2022/ngwr-scroll.mjs +3 -3
- package/fesm2022/ngwr-segmented-testing.mjs +85 -1
- package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
- package/fesm2022/ngwr-segmented.mjs +123 -10
- package/fesm2022/ngwr-segmented.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +159 -34
- package/fesm2022/ngwr-select.mjs.map +1 -1
- 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 +100 -9
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline.mjs +3 -3
- package/fesm2022/ngwr-speed-dial-testing.mjs +15 -13
- package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -1
- 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.mjs +3 -3
- package/fesm2022/ngwr-split-text.mjs +17 -5
- package/fesm2022/ngwr-split-text.mjs.map +1 -1
- package/fesm2022/ngwr-splitter.mjs +3 -3
- 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.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 +99 -39
- 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 +36 -10
- 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.mjs +7 -7
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
- package/fesm2022/ngwr-timeline.mjs +6 -6
- package/fesm2022/ngwr-toast.mjs +9 -9
- package/fesm2022/ngwr-toolbar.mjs +3 -3
- 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 +28 -10
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter-testing.mjs +15 -1
- package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -1
- 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.mjs +4 -4
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/fesm2022/ngwr-window.mjs +94 -24
- 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 +56 -6
- package/i18n/ru.json +56 -6
- 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 +19 -24
- package/llms.txt +46 -8
- package/markdown/styles/_index.scss +3 -3
- package/mcp/api.js +6 -4
- package/mcp/catalog.js +40 -3
- package/mcp/providers.js +16 -3
- package/mcp/tools.js +21 -1
- package/meter-group/styles/_index.scss +1 -1
- package/overlay/styles/_index.scss +33 -1
- package/package.json +17 -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 +7 -5
- package/skills/ngwr/references/catalog.md +9 -9
- package/skills/ngwr/references/setup.md +10 -5
- 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-bar-chart-testing.d.ts +9 -1
- package/types/ngwr-blur-text.d.ts +8 -4
- package/types/ngwr-breadcrumbs.d.ts +17 -10
- package/types/ngwr-calendar-testing.d.ts +1 -1
- package/types/ngwr-calendar.d.ts +66 -0
- package/types/ngwr-carousel.d.ts +85 -6
- package/types/ngwr-cascader.d.ts +17 -0
- package/types/ngwr-click-spark.d.ts +4 -0
- package/types/ngwr-color-picker-testing.d.ts +8 -0
- package/types/ngwr-color-picker.d.ts +21 -1
- 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 +56 -0
- 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 +11 -6
- 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-dialog.d.ts +38 -1
- package/types/ngwr-divider.d.ts +25 -0
- package/types/ngwr-donut-chart-testing.d.ts +7 -3
- package/types/ngwr-donut-chart.d.ts +3 -3
- package/types/ngwr-drag-drop.d.ts +56 -2
- package/types/ngwr-drawer.d.ts +56 -5
- package/types/ngwr-event-calendar.d.ts +8 -2
- package/types/ngwr-falling-text-testing.d.ts +5 -3
- package/types/ngwr-falling-text.d.ts +1 -1
- package/types/ngwr-form.d.ts +12 -2
- package/types/ngwr-glitch-text-testing.d.ts +37 -8
- package/types/ngwr-gradient-text-testing.d.ts +16 -3
- package/types/ngwr-i18n.d.ts +13 -4
- package/types/ngwr-image-cropper-testing.d.ts +49 -10
- package/types/ngwr-image-cropper.d.ts +70 -5
- package/types/ngwr-input-number-testing.d.ts +15 -10
- package/types/ngwr-input-number.d.ts +11 -4
- package/types/ngwr-input-otp-testing.d.ts +12 -6
- package/types/ngwr-input-otp.d.ts +15 -1
- package/types/ngwr-line-chart.d.ts +20 -3
- package/types/ngwr-markdown.d.ts +11 -0
- package/types/ngwr-mention.d.ts +32 -0
- package/types/ngwr-meter-group-testing.d.ts +12 -2
- package/types/ngwr-overlay.d.ts +1 -0
- package/types/ngwr-pagination-testing.d.ts +4 -4
- package/types/ngwr-pagination.d.ts +11 -0
- 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-segmented-testing.d.ts +53 -0
- package/types/ngwr-segmented.d.ts +107 -5
- package/types/ngwr-select.d.ts +68 -6
- package/types/ngwr-shiny-text.d.ts +4 -2
- package/types/ngwr-sidebar.d.ts +14 -5
- package/types/ngwr-slider.d.ts +69 -1
- package/types/ngwr-speed-dial-testing.d.ts +15 -11
- package/types/ngwr-speed-dial.d.ts +22 -3
- package/types/ngwr-split-text.d.ts +9 -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 +104 -11
- 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-tour.d.ts +22 -4
- package/types/ngwr-tree.d.ts +5 -4
- package/types/ngwr-typewriter-testing.d.ts +10 -0
- package/types/ngwr-utils.d.ts +5 -2
- package/types/ngwr-version.d.ts +1 -1
- package/types/ngwr-window.d.ts +39 -11
- package/typography/styles/_index.scss +1 -1
- package/window/styles/_index.scss +72 -26
- 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
|
@@ -31,7 +31,7 @@ function resolveSeparators(locale) {
|
|
|
31
31
|
* re-formats with grouping + fixed decimals on blur.
|
|
32
32
|
*
|
|
33
33
|
* Locale comes from Angular's `LOCALE_ID` — input-number intentionally does
|
|
34
|
-
* NOT depend on `ngwr/date
|
|
34
|
+
* NOT depend on `ngwr/date` so it can be used in date-free apps.
|
|
35
35
|
*
|
|
36
36
|
* @example
|
|
37
37
|
* ```html
|
|
@@ -101,19 +101,26 @@ class WrInputNumber {
|
|
|
101
101
|
placeholder = input('', /* @ts-ignore */
|
|
102
102
|
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
103
103
|
/**
|
|
104
|
-
* Accessible name for the field.
|
|
105
|
-
*
|
|
106
|
-
*
|
|
104
|
+
* Accessible name for the field. Unset, the field is named by whatever labels
|
|
105
|
+
* it — there is deliberately no fallback here.
|
|
106
|
+
*
|
|
107
|
+
* It used to mirror the `placeholder`, and an `aria-label` OUTRANKS a
|
|
108
|
+
* `<label>` in the accname order: inside a `<wr-form-field label="Quantity">`
|
|
109
|
+
* the very input that adopts the field's id announced the placeholder ("0")
|
|
110
|
+
* instead, and a placeholder disappears as soon as the user types. Standalone
|
|
111
|
+
* nothing was lost by dropping it — an input with no other name already falls
|
|
112
|
+
* back to its own `placeholder` attribute, which is the name axe's `label`
|
|
113
|
+
* rule accepts too.
|
|
107
114
|
*/
|
|
108
115
|
ariaLabel = input(null, /* @ts-ignore */
|
|
109
116
|
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
110
117
|
resolvedAriaLabel = computed(() => {
|
|
111
|
-
// Not `??`: an empty
|
|
118
|
+
// Not `??`: an empty binding is no name at all, and an empty `aria-label` is
|
|
119
|
+
// worse than none — it still wins the accname order, and announces nothing.
|
|
112
120
|
const explicit = this.ariaLabel();
|
|
113
121
|
if (explicit)
|
|
114
122
|
return explicit;
|
|
115
|
-
|
|
116
|
-
return placeholder ? placeholder : null;
|
|
123
|
+
return null;
|
|
117
124
|
}, /* @ts-ignore */
|
|
118
125
|
...(ngDevMode ? [{ debugName: "resolvedAriaLabel" }] : /* istanbul ignore next */ []));
|
|
119
126
|
/** Increment button aria-label. Falls back to `inputNumber.increment`. */
|
|
@@ -321,10 +328,10 @@ class WrInputNumber {
|
|
|
321
328
|
});
|
|
322
329
|
return formatter.format(v);
|
|
323
330
|
}
|
|
324
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
325
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
331
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrInputNumber, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
332
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrInputNumber, isStandalone: true, selector: "wr-input-number", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, decimals: { classPropertyName: "decimals", publicName: "decimals", isSignal: true, isRequired: false, transformFunction: null }, showSteppers: { classPropertyName: "showSteppers", publicName: "showSteppers", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, prefix: { classPropertyName: "prefix", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, incrementLabel: { classPropertyName: "incrementLabel", publicName: "incrementLabel", isSignal: true, isRequired: false, transformFunction: null }, decrementLabel: { classPropertyName: "decrementLabel", publicName: "decrementLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: "<wr-input-group [rounded]=\"resolvedRounded()\">\n @if (prefix(); as p) {\n <span wrInputPrefix>{{ p }}</span>\n }\n <input\n #input\n wrInput\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [wrSize]=\"resolvedSize()\"\n [rounded]=\"resolvedRounded()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n spellcheck=\"false\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (suffix(); as s) {\n <span wrInputSuffix>{{ s }}</span>\n }\n @if (showSteppers()) {\n <div class=\"wr-input-number__steppers\" wrInputSuffix>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedIncrement()\"\n [disabled]=\"!interactive() || atMax()\"\n (click)=\"stepBy(1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 5 L4 0 L8 5 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedDecrement()\"\n [disabled]=\"!interactive() || atMin()\"\n (click)=\"stepBy(-1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 0 L4 5 L8 0 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n </div>\n }\n</wr-input-group>\n", dependencies: [{ kind: "directive", type: WrInput, selector: "input[wrInput], textarea[wrInput]", inputs: ["wrSize", "rounded"] }, { kind: "component", type: WrInputGroup, selector: "wr-input-group", inputs: ["rounded"] }, { kind: "directive", type: WrInputPrefix, selector: "[wrInputPrefix]" }, { kind: "directive", type: WrInputSuffix, selector: "[wrInputSuffix]" }], encapsulation: i0.ViewEncapsulation.None });
|
|
326
333
|
}
|
|
327
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
334
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrInputNumber, decorators: [{
|
|
328
335
|
type: Component,
|
|
329
336
|
args: [{ selector: 'wr-input-number', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [WrInput, WrInputGroup, WrInputPrefix, WrInputSuffix], template: "<wr-input-group [rounded]=\"resolvedRounded()\">\n @if (prefix(); as p) {\n <span wrInputPrefix>{{ p }}</span>\n }\n <input\n #input\n wrInput\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [wrSize]=\"resolvedSize()\"\n [rounded]=\"resolvedRounded()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n spellcheck=\"false\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (suffix(); as s) {\n <span wrInputSuffix>{{ s }}</span>\n }\n @if (showSteppers()) {\n <div class=\"wr-input-number__steppers\" wrInputSuffix>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedIncrement()\"\n [disabled]=\"!interactive() || atMax()\"\n (click)=\"stepBy(1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 5 L4 0 L8 5 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedDecrement()\"\n [disabled]=\"!interactive() || atMin()\"\n (click)=\"stepBy(-1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 0 L4 5 L8 0 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n </div>\n }\n</wr-input-group>\n" }]
|
|
330
337
|
}], ctorParameters: () => [], propDecorators: { min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], decimals: [{ type: i0.Input, args: [{ isSignal: true, alias: "decimals", required: false }] }], showSteppers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSteppers", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], prefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefix", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], incrementLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "incrementLabel", required: false }] }], decrementLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "decrementLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }] } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-input-number.mjs","sources":["../../../projects/lib/input-number/input-number.ts","../../../projects/lib/input-number/input-number.html","../../../projects/lib/input-number/ngwr-input-number.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 { type BooleanInput, coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n LOCALE_ID,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useConfigValue } from 'ngwr/config';\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrInput, WrInputGroup, WrInputPrefix, WrInputSuffix, type WrInputSize } from 'ngwr/input';\nimport { clamp, round } from 'ngwr/utils';\n\n/** Round to `decimals` places, avoiding common float artefacts (`1.005 → 1.01`). */\n\n/** Resolve the locale's decimal + grouping separators from `Intl.NumberFormat`. */\nfunction resolveSeparators(locale: string): { decimal: string; group: string } {\n const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);\n const decimal = parts.find(p => p.type === 'decimal')?.value ?? '.';\n const group = parts.find(p => p.type === 'group')?.value ?? '';\n return { decimal, group };\n}\n\n/**\n * Numeric input with locale-aware formatting + ▲▼ stepper buttons.\n *\n * A signal-forms native control: it implements `FormValueControl<number | null>`,\n * so `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone.\n *\n * Parses leniently while typing (allows partial input like `\"1.\"` or `\"-\"`),\n * re-formats with grouping + fixed decimals on blur.\n *\n * Locale comes from Angular's `LOCALE_ID` — input-number intentionally does\n * NOT depend on `ngwr/date-adapter` so it can be used in date-free apps.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-input-number [formField]=\"form.qty\" [min]=\"0\" [max]=\"100\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-input-number [(value)]=\"price\" [decimals]=\"2\" suffix=\"USD\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/input-number\n */\n@Component({\n selector: 'wr-input-number',\n templateUrl: './input-number.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n imports: [WrInput, WrInputGroup, WrInputPrefix, WrInputSuffix],\n})\nexport class WrInputNumber implements FormValueControl<number | null> {\n /** Minimum allowed value. @default -Infinity */\n readonly min = input<number | undefined>(Number.NEGATIVE_INFINITY, {\n transform: (v: unknown): number => coerceNumberProperty(v, Number.NEGATIVE_INFINITY),\n });\n\n /** Maximum allowed value. @default Infinity */\n readonly max = input<number | undefined>(Number.POSITIVE_INFINITY, {\n transform: (v: unknown): number => coerceNumberProperty(v, Number.POSITIVE_INFINITY),\n });\n\n private readonly minValue = computed(() => this.min() ?? Number.NEGATIVE_INFINITY);\n private readonly maxValue = computed(() => this.max() ?? Number.POSITIVE_INFINITY);\n\n /** Step used by stepper buttons + arrow keys. @default 1 */\n readonly step = input(1, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 1)) });\n\n /** Fixed number of decimals shown on blur. `null` keeps the entered precision. @default null */\n readonly decimals = input<number | null>(null);\n\n /** Render the ▲▼ stepper column. @default true */\n readonly showSteppers = input(true, { transform: coerceBooleanProperty });\n\n /**\n * Control size — forwarded to the field, and shares the `--wr-control-*`\n * contract. Unset falls back to the `inputNumber.size` app default from\n * `provideWrConfig()`, then to the `input.size` one, then to `md`. @default 'md'\n */\n readonly size = input<WrInputSize | null>(null);\n\n /**\n * Pill-shaped corners. Unset falls back to the `inputNumber.rounded` app default\n * from `provideWrConfig()`, then to the `input.rounded` one;\n * `[rounded]=\"false\"` turns a configured `true` back off. @default false\n */\n readonly rounded = input<boolean | null, BooleanInput>(null, {\n // Null-preserving: the plain `coerceBooleanProperty` folds `null` into\n // `false`, which would make \"not set\" and \"set to false\" the same value —\n // and a config default nothing could ever supply.\n transform: (v: BooleanInput): boolean | null => (v == null ? null : coerceBooleanProperty(v)),\n });\n\n /**\n * `size`, then the app config, then `md` — bound to the field's `[wrSize]`.\n *\n * The chrome this control is built from resolves the same pair for itself, so\n * the config lookup falls through to `input.*` rather than stopping at\n * `inputNumber.*`: binding a value down always wins there, and an unchained\n * lookup would therefore SUPPRESS an `input.size` an app had set — the field\n * inside a number input would be the one `[wrInput]` in the app that ignored it.\n * Chained, the specific key wins where it is set and the general one still\n * applies where it is not.\n */\n protected readonly resolvedSize = useConfigValue(this.size, c => c.inputNumber?.size ?? c.input?.size, 'md');\n\n /** `rounded`, then the app config, then `false` — bound to the group AND the field. */\n protected readonly resolvedRounded = useConfigValue(\n this.rounded,\n c => c.inputNumber?.rounded ?? c.input?.rounded,\n false\n );\n\n /** Optional prefix label (e.g. `\"$\"`). */\n readonly prefix = input<string>('');\n\n /** Optional suffix label (e.g. `\"kg\"`). */\n readonly suffix = input<string>('');\n\n /** Placeholder shown when the input is empty. */\n readonly placeholder = input<string>('');\n\n /**\n * Accessible name for the field. Falls back to the placeholder — inside a\n * `<wr-form-field>` the projected `wrInput` picks up the label's id and needs\n * neither, but standalone the text field has no name at all.\n */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = computed(() => {\n // Not `??`: an empty placeholder is no name at all, so it must fall through.\n const explicit = this.ariaLabel();\n if (explicit) return explicit;\n const placeholder = this.placeholder();\n return placeholder ? placeholder : null;\n });\n\n /** Increment button aria-label. Falls back to `inputNumber.increment`. */\n readonly incrementLabel = input<string | null>(null);\n\n /** Decrement button aria-label. Falls back to `inputNumber.decrement`. */\n readonly decrementLabel = input<string | null>(null);\n\n /**\n * The steppers are icon-only buttons, so these ARE their accessible names.\n * They were hard-coded English while the catalog carried the keys for them\n * unread — a Russian app announced \"Increment\".\n */\n protected readonly resolvedIncrement = useI18nText(this.incrementLabel, 'inputNumber.increment', 'Increment');\n protected readonly resolvedDecrement = useI18nText(this.decrementLabel, 'inputNumber.decrement', 'Decrement');\n\n /** Disable interaction. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Read-only — values cannot be changed (steppers + typing disabled). @default false */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** Numeric value (`null` when empty). Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<number | null>(null);\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n private readonly locale = inject(LOCALE_ID);\n private readonly separators = resolveSeparators(this.locale);\n\n /** Text currently in the input (may be partial / invalid mid-type). */\n protected readonly text = signal<string>('');\n\n /** Whether the input is focused. While true, the input handlers own `text`. */\n protected readonly focused = signal(false);\n\n protected readonly interactive = computed(() => !this.disabled() && !this.readonly());\n\n protected readonly atMin = computed(() => {\n const v = this.value();\n return v !== null && v <= this.minValue();\n });\n\n protected readonly atMax = computed(() => {\n const v = this.value();\n return v !== null && v >= this.maxValue();\n });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-input-number'];\n if (this.disabled()) parts.push('wr-input-number--disabled');\n return parts.join(' ');\n });\n\n /**\n * Fraction digits the stepper rounds its arithmetic to when `decimals` says\n * nothing. High enough to leave a real value alone, low enough to erase the\n * binary-float tail that repeated stepping accumulates.\n */\n private static readonly STEP_PRECISION = 10;\n\n /**\n * Above this, the anti-drift round is skipped — it would MANUFACTURE drift.\n *\n * `round(v, 10)` is `Math.round((v + EPSILON) * 1e10) / 1e10`, and the scale\n * factor is only exact while `v * 1e10` stays inside 2^53, i.e. |v| under\n * about 900,720. In practice the round-trip still recovers up to ~9.2e8 and\n * then breaks: `round(1000000001, 10)` is `1000000000.9999999`, and since\n * `format` prints up to 20 fraction digits that tail reaches the screen —\n * and compounds, because the next press starts from it. Integer stepping at\n * that magnitude is exact in IEEE754 and needed no help; the only thing this\n * rounding exists to defend is `0.1 + 0.1 + 0.1`.\n */\n private static readonly SAFE_ROUND_MAGNITUDE = 1e6;\n\n /** Steps finer than this would be rounded away by {@link STEP_PRECISION}. */\n private static readonly SAFE_ROUND_STEP = 1e-9;\n\n constructor() {\n // Keep the display `text` in sync with external writes to `value` (via\n // `[formField]` / `[(value)]`). While the user is editing, the input\n // handlers own `text`, so skip re-formatting to avoid clobbering partial\n // input (e.g. mid-typed grouping or a value that is still being clamped).\n effect(() => {\n const v = this.value();\n if (this.focused()) return;\n if (v === null || Number.isNaN(v)) {\n this.text.set('');\n return;\n }\n // SHOW WHAT THE MODEL HOLDS. It used to format `constrain(v)`, so a\n // `[max]=\"10\"` field handed 500 drew \"10\" over a model that still said\n // 500 — the display was the thing lying, not the model.\n //\n // The tempting repair is to write the clamp back so the two agree, and\n // that is worse: under `[formField]` the bounds arrive ONLY through the\n // schema's `min()` / `max()` rules (binding `[min]` next to `[formField]`\n // is an NG8022 compile error), so overwriting the value ERASES the very\n // error those rules exist to raise — and the write marks a pristine form\n // dirty, tripping unsaved-changes guards and opening every other\n // validator's message on first paint.\n //\n // So: `min` / `max` bound what this component's own edits may produce,\n // and a value written from outside is displayed as it is and left for\n // validation to judge.\n this.text.set(this.format(v));\n });\n }\n\n // Template handlers\n\n protected onInput(event: Event): void {\n const raw = (event.target as HTMLInputElement).value;\n this.text.set(raw);\n if (!raw.trim()) {\n this.value.set(null);\n return;\n }\n const parsed = this.parse(raw);\n if (parsed === null) return; // partial input — wait for more\n const next = this.constrain(parsed);\n this.value.set(next);\n }\n\n protected onFocus(): void {\n this.focused.set(true);\n }\n\n protected onBlur(): void {\n // Flipping `focused` false lets the sync effect re-format `text` from the\n // committed `value` (e.g. `\"1.\"` → `\"1\"`, or grouping the final number).\n this.focused.set(false);\n this.touch.emit();\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (!this.interactive()) return;\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return;\n event.preventDefault();\n const direction = event.key === 'ArrowUp' ? 1 : -1;\n const multiplier = event.shiftKey ? 10 : 1;\n this.bump(direction * multiplier);\n }\n\n protected stepBy(direction: 1 | -1): void {\n if (!this.interactive()) return;\n this.bump(direction);\n }\n\n // Internals\n\n private bump(units: number): void {\n const stepSize = this.step() || 1;\n const base = this.value() ?? this.fallbackBase();\n // `round` before `constrain`, and only on the stepped ARITHMETIC. Binary\n // floats do not add: three presses of a `step=\"0.1\"` stepper produced\n // 0.30000000000000004, `constrain` only rounds when `decimals` is set, and\n // `format` prints up to 20 fraction digits — so the drift went into the\n // model AND onto the screen. Ten digits erases IEEE noise without\n // quantising a value the user typed (0.15000000000000002 stays 0.15, it\n // does not snap to the step grid).\n //\n // Guarded on BOTH ends, because outside them the correction is the thing\n // that corrupts: see SAFE_ROUND_MAGNITUDE, and a step finer than 1e-9 would\n // simply be rounded away to nothing.\n const raw = base + units * stepSize;\n const correctable =\n Math.abs(raw) < WrInputNumber.SAFE_ROUND_MAGNITUDE && Math.abs(stepSize) >= WrInputNumber.SAFE_ROUND_STEP;\n const next = this.constrain(correctable ? round(raw, WrInputNumber.STEP_PRECISION) : raw);\n this.value.set(next);\n this.text.set(this.format(next));\n }\n\n /** Choose a sensible starting point when bumping from `null`. */\n private fallbackBase(): number {\n if (Number.isFinite(this.minValue()) && this.minValue() > 0) return this.minValue();\n if (Number.isFinite(this.maxValue()) && this.maxValue() < 0) return this.maxValue();\n return 0;\n }\n\n private constrain(v: number): number {\n let next = clamp(v, this.minValue(), this.maxValue());\n const d = this.decimals();\n if (d !== null) next = round(next, d);\n return next;\n }\n\n /** Parse a locale-formatted string. Returns `null` for partial / not-yet-numeric input. */\n private parse(raw: string): number | null {\n const trimmed = raw.trim();\n if (!trimmed) return null;\n // Strip group separators, normalise the decimal separator to `.`.\n let normalised = trimmed;\n if (this.separators.group) {\n normalised = normalised.split(this.separators.group).join('');\n }\n if (this.separators.decimal !== '.') {\n normalised = normalised.split(this.separators.decimal).join('.');\n }\n // Partial inputs we don't want to clobber while the user is still typing:\n // `-`, `-.`, `.`, `1.`, `-1.` etc.\n if (/^-?\\.?$/.test(normalised) || normalised.endsWith('.')) return null;\n const n = Number(normalised);\n return Number.isFinite(n) ? n : null;\n }\n\n /** Format a value using `Intl.NumberFormat` for the active locale. */\n private format(v: number): string {\n const d = this.decimals();\n const formatter = new Intl.NumberFormat(this.locale, {\n minimumFractionDigits: d ?? 0,\n maximumFractionDigits: d ?? 20,\n useGrouping: true,\n });\n return formatter.format(v);\n }\n}\n","<wr-input-group [rounded]=\"resolvedRounded()\">\n @if (prefix(); as p) {\n <span wrInputPrefix>{{ p }}</span>\n }\n <input\n #input\n wrInput\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [wrSize]=\"resolvedSize()\"\n [rounded]=\"resolvedRounded()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n spellcheck=\"false\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (suffix(); as s) {\n <span wrInputSuffix>{{ s }}</span>\n }\n @if (showSteppers()) {\n <div class=\"wr-input-number__steppers\" wrInputSuffix>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedIncrement()\"\n [disabled]=\"!interactive() || atMax()\"\n (click)=\"stepBy(1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 5 L4 0 L8 5 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedDecrement()\"\n [disabled]=\"!interactive() || atMin()\"\n (click)=\"stepBy(-1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 0 L4 5 L8 0 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n </div>\n }\n</wr-input-group>\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-input-number.mjs","sources":["../../../projects/lib/input-number/input-number.ts","../../../projects/lib/input-number/input-number.html","../../../projects/lib/input-number/ngwr-input-number.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 { type BooleanInput, coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n LOCALE_ID,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useConfigValue } from 'ngwr/config';\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrInput, WrInputGroup, WrInputPrefix, WrInputSuffix, type WrInputSize } from 'ngwr/input';\nimport { clamp, round } from 'ngwr/utils';\n\n/** Round to `decimals` places, avoiding common float artefacts (`1.005 → 1.01`). */\n\n/** Resolve the locale's decimal + grouping separators from `Intl.NumberFormat`. */\nfunction resolveSeparators(locale: string): { decimal: string; group: string } {\n const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);\n const decimal = parts.find(p => p.type === 'decimal')?.value ?? '.';\n const group = parts.find(p => p.type === 'group')?.value ?? '';\n return { decimal, group };\n}\n\n/**\n * Numeric input with locale-aware formatting + ▲▼ stepper buttons.\n *\n * A signal-forms native control: it implements `FormValueControl<number | null>`,\n * so `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone.\n *\n * Parses leniently while typing (allows partial input like `\"1.\"` or `\"-\"`),\n * re-formats with grouping + fixed decimals on blur.\n *\n * Locale comes from Angular's `LOCALE_ID` — input-number intentionally does\n * NOT depend on `ngwr/date` so it can be used in date-free apps.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-input-number [formField]=\"form.qty\" [min]=\"0\" [max]=\"100\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-input-number [(value)]=\"price\" [decimals]=\"2\" suffix=\"USD\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/input-number\n */\n@Component({\n selector: 'wr-input-number',\n templateUrl: './input-number.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n imports: [WrInput, WrInputGroup, WrInputPrefix, WrInputSuffix],\n})\nexport class WrInputNumber implements FormValueControl<number | null> {\n /** Minimum allowed value. @default -Infinity */\n readonly min = input<number | undefined>(Number.NEGATIVE_INFINITY, {\n transform: (v: unknown): number => coerceNumberProperty(v, Number.NEGATIVE_INFINITY),\n });\n\n /** Maximum allowed value. @default Infinity */\n readonly max = input<number | undefined>(Number.POSITIVE_INFINITY, {\n transform: (v: unknown): number => coerceNumberProperty(v, Number.POSITIVE_INFINITY),\n });\n\n private readonly minValue = computed(() => this.min() ?? Number.NEGATIVE_INFINITY);\n private readonly maxValue = computed(() => this.max() ?? Number.POSITIVE_INFINITY);\n\n /** Step used by stepper buttons + arrow keys. @default 1 */\n readonly step = input(1, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 1)) });\n\n /** Fixed number of decimals shown on blur. `null` keeps the entered precision. @default null */\n readonly decimals = input<number | null>(null);\n\n /** Render the ▲▼ stepper column. @default true */\n readonly showSteppers = input(true, { transform: coerceBooleanProperty });\n\n /**\n * Control size — forwarded to the field, and shares the `--wr-control-*`\n * contract. Unset falls back to the `inputNumber.size` app default from\n * `provideWrConfig()`, then to the `input.size` one, then to `md`. @default 'md'\n */\n readonly size = input<WrInputSize | null>(null);\n\n /**\n * Pill-shaped corners. Unset falls back to the `inputNumber.rounded` app default\n * from `provideWrConfig()`, then to the `input.rounded` one;\n * `[rounded]=\"false\"` turns a configured `true` back off. @default false\n */\n readonly rounded = input<boolean | null, BooleanInput>(null, {\n // Null-preserving: the plain `coerceBooleanProperty` folds `null` into\n // `false`, which would make \"not set\" and \"set to false\" the same value —\n // and a config default nothing could ever supply.\n transform: (v: BooleanInput): boolean | null => (v == null ? null : coerceBooleanProperty(v)),\n });\n\n /**\n * `size`, then the app config, then `md` — bound to the field's `[wrSize]`.\n *\n * The chrome this control is built from resolves the same pair for itself, so\n * the config lookup falls through to `input.*` rather than stopping at\n * `inputNumber.*`: binding a value down always wins there, and an unchained\n * lookup would therefore SUPPRESS an `input.size` an app had set — the field\n * inside a number input would be the one `[wrInput]` in the app that ignored it.\n * Chained, the specific key wins where it is set and the general one still\n * applies where it is not.\n */\n protected readonly resolvedSize = useConfigValue(this.size, c => c.inputNumber?.size ?? c.input?.size, 'md');\n\n /** `rounded`, then the app config, then `false` — bound to the group AND the field. */\n protected readonly resolvedRounded = useConfigValue(\n this.rounded,\n c => c.inputNumber?.rounded ?? c.input?.rounded,\n false\n );\n\n /** Optional prefix label (e.g. `\"$\"`). */\n readonly prefix = input<string>('');\n\n /** Optional suffix label (e.g. `\"kg\"`). */\n readonly suffix = input<string>('');\n\n /** Placeholder shown when the input is empty. */\n readonly placeholder = input<string>('');\n\n /**\n * Accessible name for the field. Unset, the field is named by whatever labels\n * it — there is deliberately no fallback here.\n *\n * It used to mirror the `placeholder`, and an `aria-label` OUTRANKS a\n * `<label>` in the accname order: inside a `<wr-form-field label=\"Quantity\">`\n * the very input that adopts the field's id announced the placeholder (\"0\")\n * instead, and a placeholder disappears as soon as the user types. Standalone\n * nothing was lost by dropping it — an input with no other name already falls\n * back to its own `placeholder` attribute, which is the name axe's `label`\n * rule accepts too.\n */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = computed(() => {\n // Not `??`: an empty binding is no name at all, and an empty `aria-label` is\n // worse than none — it still wins the accname order, and announces nothing.\n const explicit = this.ariaLabel();\n if (explicit) return explicit;\n return null;\n });\n\n /** Increment button aria-label. Falls back to `inputNumber.increment`. */\n readonly incrementLabel = input<string | null>(null);\n\n /** Decrement button aria-label. Falls back to `inputNumber.decrement`. */\n readonly decrementLabel = input<string | null>(null);\n\n /**\n * The steppers are icon-only buttons, so these ARE their accessible names.\n * They were hard-coded English while the catalog carried the keys for them\n * unread — a Russian app announced \"Increment\".\n */\n protected readonly resolvedIncrement = useI18nText(this.incrementLabel, 'inputNumber.increment', 'Increment');\n protected readonly resolvedDecrement = useI18nText(this.decrementLabel, 'inputNumber.decrement', 'Decrement');\n\n /** Disable interaction. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Read-only — values cannot be changed (steppers + typing disabled). @default false */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** Numeric value (`null` when empty). Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<number | null>(null);\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n private readonly locale = inject(LOCALE_ID);\n private readonly separators = resolveSeparators(this.locale);\n\n /** Text currently in the input (may be partial / invalid mid-type). */\n protected readonly text = signal<string>('');\n\n /** Whether the input is focused. While true, the input handlers own `text`. */\n protected readonly focused = signal(false);\n\n protected readonly interactive = computed(() => !this.disabled() && !this.readonly());\n\n protected readonly atMin = computed(() => {\n const v = this.value();\n return v !== null && v <= this.minValue();\n });\n\n protected readonly atMax = computed(() => {\n const v = this.value();\n return v !== null && v >= this.maxValue();\n });\n\n protected readonly classes = computed(() => {\n const parts = ['wr-input-number'];\n if (this.disabled()) parts.push('wr-input-number--disabled');\n return parts.join(' ');\n });\n\n /**\n * Fraction digits the stepper rounds its arithmetic to when `decimals` says\n * nothing. High enough to leave a real value alone, low enough to erase the\n * binary-float tail that repeated stepping accumulates.\n */\n private static readonly STEP_PRECISION = 10;\n\n /**\n * Above this, the anti-drift round is skipped — it would MANUFACTURE drift.\n *\n * `round(v, 10)` is `Math.round((v + EPSILON) * 1e10) / 1e10`, and the scale\n * factor is only exact while `v * 1e10` stays inside 2^53, i.e. |v| under\n * about 900,720. In practice the round-trip still recovers up to ~9.2e8 and\n * then breaks: `round(1000000001, 10)` is `1000000000.9999999`, and since\n * `format` prints up to 20 fraction digits that tail reaches the screen —\n * and compounds, because the next press starts from it. Integer stepping at\n * that magnitude is exact in IEEE754 and needed no help; the only thing this\n * rounding exists to defend is `0.1 + 0.1 + 0.1`.\n */\n private static readonly SAFE_ROUND_MAGNITUDE = 1e6;\n\n /** Steps finer than this would be rounded away by {@link STEP_PRECISION}. */\n private static readonly SAFE_ROUND_STEP = 1e-9;\n\n constructor() {\n // Keep the display `text` in sync with external writes to `value` (via\n // `[formField]` / `[(value)]`). While the user is editing, the input\n // handlers own `text`, so skip re-formatting to avoid clobbering partial\n // input (e.g. mid-typed grouping or a value that is still being clamped).\n effect(() => {\n const v = this.value();\n if (this.focused()) return;\n if (v === null || Number.isNaN(v)) {\n this.text.set('');\n return;\n }\n // SHOW WHAT THE MODEL HOLDS. It used to format `constrain(v)`, so a\n // `[max]=\"10\"` field handed 500 drew \"10\" over a model that still said\n // 500 — the display was the thing lying, not the model.\n //\n // The tempting repair is to write the clamp back so the two agree, and\n // that is worse: under `[formField]` the bounds arrive ONLY through the\n // schema's `min()` / `max()` rules (binding `[min]` next to `[formField]`\n // is an NG8022 compile error), so overwriting the value ERASES the very\n // error those rules exist to raise — and the write marks a pristine form\n // dirty, tripping unsaved-changes guards and opening every other\n // validator's message on first paint.\n //\n // So: `min` / `max` bound what this component's own edits may produce,\n // and a value written from outside is displayed as it is and left for\n // validation to judge.\n this.text.set(this.format(v));\n });\n }\n\n // Template handlers\n\n protected onInput(event: Event): void {\n const raw = (event.target as HTMLInputElement).value;\n this.text.set(raw);\n if (!raw.trim()) {\n this.value.set(null);\n return;\n }\n const parsed = this.parse(raw);\n if (parsed === null) return; // partial input — wait for more\n const next = this.constrain(parsed);\n this.value.set(next);\n }\n\n protected onFocus(): void {\n this.focused.set(true);\n }\n\n protected onBlur(): void {\n // Flipping `focused` false lets the sync effect re-format `text` from the\n // committed `value` (e.g. `\"1.\"` → `\"1\"`, or grouping the final number).\n this.focused.set(false);\n this.touch.emit();\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (!this.interactive()) return;\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return;\n event.preventDefault();\n const direction = event.key === 'ArrowUp' ? 1 : -1;\n const multiplier = event.shiftKey ? 10 : 1;\n this.bump(direction * multiplier);\n }\n\n protected stepBy(direction: 1 | -1): void {\n if (!this.interactive()) return;\n this.bump(direction);\n }\n\n // Internals\n\n private bump(units: number): void {\n const stepSize = this.step() || 1;\n const base = this.value() ?? this.fallbackBase();\n // `round` before `constrain`, and only on the stepped ARITHMETIC. Binary\n // floats do not add: three presses of a `step=\"0.1\"` stepper produced\n // 0.30000000000000004, `constrain` only rounds when `decimals` is set, and\n // `format` prints up to 20 fraction digits — so the drift went into the\n // model AND onto the screen. Ten digits erases IEEE noise without\n // quantising a value the user typed (0.15000000000000002 stays 0.15, it\n // does not snap to the step grid).\n //\n // Guarded on BOTH ends, because outside them the correction is the thing\n // that corrupts: see SAFE_ROUND_MAGNITUDE, and a step finer than 1e-9 would\n // simply be rounded away to nothing.\n const raw = base + units * stepSize;\n const correctable =\n Math.abs(raw) < WrInputNumber.SAFE_ROUND_MAGNITUDE && Math.abs(stepSize) >= WrInputNumber.SAFE_ROUND_STEP;\n const next = this.constrain(correctable ? round(raw, WrInputNumber.STEP_PRECISION) : raw);\n this.value.set(next);\n this.text.set(this.format(next));\n }\n\n /** Choose a sensible starting point when bumping from `null`. */\n private fallbackBase(): number {\n if (Number.isFinite(this.minValue()) && this.minValue() > 0) return this.minValue();\n if (Number.isFinite(this.maxValue()) && this.maxValue() < 0) return this.maxValue();\n return 0;\n }\n\n private constrain(v: number): number {\n let next = clamp(v, this.minValue(), this.maxValue());\n const d = this.decimals();\n if (d !== null) next = round(next, d);\n return next;\n }\n\n /** Parse a locale-formatted string. Returns `null` for partial / not-yet-numeric input. */\n private parse(raw: string): number | null {\n const trimmed = raw.trim();\n if (!trimmed) return null;\n // Strip group separators, normalise the decimal separator to `.`.\n let normalised = trimmed;\n if (this.separators.group) {\n normalised = normalised.split(this.separators.group).join('');\n }\n if (this.separators.decimal !== '.') {\n normalised = normalised.split(this.separators.decimal).join('.');\n }\n // Partial inputs we don't want to clobber while the user is still typing:\n // `-`, `-.`, `.`, `1.`, `-1.` etc.\n if (/^-?\\.?$/.test(normalised) || normalised.endsWith('.')) return null;\n const n = Number(normalised);\n return Number.isFinite(n) ? n : null;\n }\n\n /** Format a value using `Intl.NumberFormat` for the active locale. */\n private format(v: number): string {\n const d = this.decimals();\n const formatter = new Intl.NumberFormat(this.locale, {\n minimumFractionDigits: d ?? 0,\n maximumFractionDigits: d ?? 20,\n useGrouping: true,\n });\n return formatter.format(v);\n }\n}\n","<wr-input-group [rounded]=\"resolvedRounded()\">\n @if (prefix(); as p) {\n <span wrInputPrefix>{{ p }}</span>\n }\n <input\n #input\n wrInput\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [wrSize]=\"resolvedSize()\"\n [rounded]=\"resolvedRounded()\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n spellcheck=\"false\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (suffix(); as s) {\n <span wrInputSuffix>{{ s }}</span>\n }\n @if (showSteppers()) {\n <div class=\"wr-input-number__steppers\" wrInputSuffix>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedIncrement()\"\n [disabled]=\"!interactive() || atMax()\"\n (click)=\"stepBy(1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 5 L4 0 L8 5 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-input-number__step\"\n tabindex=\"-1\"\n [attr.aria-label]=\"resolvedDecrement()\"\n [disabled]=\"!interactive() || atMin()\"\n (click)=\"stepBy(-1)\"\n >\n <svg width=\"10\" height=\"6\" viewBox=\"0 0 8 5\" aria-hidden=\"true\">\n <path d=\"M0 0 L4 5 L8 0 Z\" fill=\"currentColor\" />\n </svg>\n </button>\n </div>\n }\n</wr-input-group>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -11,11 +11,11 @@ import { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testin
|
|
|
11
11
|
* single character.
|
|
12
12
|
*
|
|
13
13
|
* A box is more than the string it shows: it carries its own accessible name
|
|
14
|
-
* (`Digit 3`
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* box's, so a spec can hold on to
|
|
18
|
-
* four parallel arrays by index.
|
|
14
|
+
* (`Digit 3` on a numeric strip, `Character 3` on the others), its own
|
|
15
|
+
* emptiness, its own masking, and it is the thing that either has focus or does
|
|
16
|
+
* not. The root harness answers the control's questions (the assembled code,
|
|
17
|
+
* which index is focused); this one answers the box's, so a spec can hold on to
|
|
18
|
+
* "the box that has focus" instead of zipping four parallel arrays by index.
|
|
19
19
|
*
|
|
20
20
|
* @example
|
|
21
21
|
* ```ts
|
|
@@ -59,11 +59,17 @@ class WrInputOtpBoxHarness extends ComponentHarness {
|
|
|
59
59
|
return (await this.getValue()) === '';
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
|
-
* The box's accessible name — `Digit 1`, `Digit 2`, …
|
|
62
|
+
* The box's accessible name — `Digit 1`, `Digit 2`, … on a numeric strip.
|
|
63
63
|
*
|
|
64
64
|
* This is the whole of a box's identity for a screen-reader user: the boxes
|
|
65
65
|
* carry no visible label and no position in an `aria-*` set, so the name is
|
|
66
66
|
* how someone knows which one they are in.
|
|
67
|
+
*
|
|
68
|
+
* The word depends on `mode`, so an assertion has to follow it: the
|
|
69
|
+
* `alphanumeric` and `text` modes accept letters, and the component names
|
|
70
|
+
* their boxes `Character 1`, `Character 2`, … rather than call an `A` a digit.
|
|
71
|
+
* Both come from the `inputOtp.digit` / `inputOtp.character` catalog entries,
|
|
72
|
+
* so a localized app answers here in its own language.
|
|
67
73
|
*/
|
|
68
74
|
async getLabel() {
|
|
69
75
|
return (await this.host()).getAttribute('aria-label');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-input-otp-testing.mjs","sources":["../../../projects/lib/input-otp/testing/wr-input-otp-box-harness.ts","../../../projects/lib/input-otp/testing/wr-input-otp-harness.ts","../../../projects/lib/input-otp/testing/ngwr-input-otp-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 { WrInputOtpBoxHarnessFilters } from './interfaces';\n\n/**\n * Test harness for ONE box of a `<wr-input-otp>` — the `<input>` that holds a\n * single character.\n *\n * A box is more than the string it shows: it carries its own accessible name\n * (`Digit 3`), its own emptiness, its own masking, and it is the thing that\n * either has focus or does not. The root harness answers the control's\n * questions (the assembled code, which index is focused); this one answers the\n * box's, so a spec can hold on to \"the box that has focus\" instead of zipping\n * four parallel arrays by index.\n *\n * @example\n * ```ts\n * await otp.setValue('12');\n * // Two digits in, the first box still waiting is the third.\n * const [box] = await otp.getBoxes({ empty: true });\n * expect(await box.getLabel()).toBe('Digit 3');\n * await box.type('7');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrInputOtpBoxHarness extends ComponentHarness {\n /**\n * The cell class, not `wr-input-otp input`: the class is public API, so it is\n * the stable handle on a cell, and matching on it keeps the harness usable on\n * its own — `getAllHarnesses(WrInputOtpBoxHarness)` finds cells without\n * collecting every other `<input>` on the page.\n */\n static hostSelector = 'input.wr-input-otp__cell';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrInputOtpBoxHarnessFilters = {}): HarnessPredicate<WrInputOtpBoxHarness> {\n return new HarnessPredicate(WrInputOtpBoxHarness, options)\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('empty', options.empty, async (harness, empty) => (await harness.isEmpty()) === empty)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label));\n }\n\n /**\n * The character the box shows, or `''`.\n *\n * Read from the `value` PROPERTY. The `value` attribute is never written at all\n * — Angular binds the property, and the component rewrites that property again\n * in its own `input` handler to keep one character per box — so\n * `getAttribute('value')` answers `null` for every box, full or empty.\n */\n async getValue(): Promise<string> {\n return (await this.host()).getProperty<string>('value');\n }\n\n /** Whether the box is still waiting for a character. */\n async isEmpty(): Promise<boolean> {\n return (await this.getValue()) === '';\n }\n\n /**\n * The box's accessible name — `Digit 1`, `Digit 2`, …\n *\n * This is the whole of a box's identity for a screen-reader user: the boxes\n * carry no visible label and no position in an `aria-*` set, so the name is\n * how someone knows which one they are in.\n */\n async getLabel(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /** Whether the box hides what was typed, like a password field. */\n async isMasked(): Promise<boolean> {\n return (await (await this.host()).getProperty<string>('type')) === 'password';\n }\n\n async isDisabled(): Promise<boolean> {\n return (await this.host()).getProperty<boolean>('disabled');\n }\n\n async isFocused(): Promise<boolean> {\n return (await this.host()).isFocused();\n }\n\n /** Put the caret in this box. Throws when the control is disabled. */\n async focus(): Promise<void> {\n await this.assertEnabled('focus');\n await (await this.host()).focus();\n }\n\n /**\n * Type one character into this box.\n *\n * Focus advances to the next box when the character is accepted — the control\n * moves it in the `input` handler, so it has already happened when this\n * resolves. A character the box's `mode` rejects leaves the box EMPTY and\n * focus where it was, which is what a browser shows too.\n */\n async type(char: string): Promise<void> {\n await this.assertEnabled('type');\n if ([...char].length !== 1) {\n throw new Error(\n `WrInputOtpBoxHarness.type('${char}'): a box holds exactly one character (maxlength=\"1\") — ` +\n 'use WrInputOtpHarness.setValue() to enter a whole code.'\n );\n }\n await (await this.host()).sendKeys(char);\n }\n\n private async assertEnabled(method: string): Promise<void> {\n if (!(await this.isDisabled())) return;\n throw new Error(\n `WrInputOtpBoxHarness.${method}(): this box is disabled. A disabled input fires no events and ` +\n 'takes no focus, so driving it here would report interaction a browser never delivers.'\n );\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrInputOtpSize } from 'ngwr/input-otp';\n\nimport type { WrInputOtpBoxHarnessFilters, WrInputOtpHarnessFilters } from './interfaces';\nimport { WrInputOtpBoxHarness } from './wr-input-otp-box-harness';\n\n/** Which box a keyboard move aims at, and the key that gets it there. */\nconst MOVE_KEYS = {\n previous: TestKey.LEFT_ARROW,\n next: TestKey.RIGHT_ARROW,\n first: TestKey.HOME,\n last: TestKey.END,\n} as const;\n\n/**\n * Test harness for `<wr-input-otp>` — one code spread over N single-character\n * boxes, with {@link WrInputOtpBoxHarness} for a box.\n *\n * Everything here is driven from the KEYBOARD and from `input` / `paste` events,\n * never by clicking a box. The control binds no `(click)` handler at all — what a\n * click gives you in a browser is FOCUS, and a unit-test DOM moves no focus on\n * click, so a click-driven method would look like it worked and change nothing.\n * The keyboard is the path this control is designed around anyway.\n *\n * Two things about this control regularly surprise people, and the harness\n * reports them rather than smoothing them over:\n * - {@link getValue} is the boxes JOINED, which is what the component publishes\n * for every edit made inside it. Emptying a middle box therefore SHORTENS the\n * code instead of leaving a gap in it — `1 2 _ 4 5 6` publishes `'12456'`.\n * {@link getBoxValues} is the only place that hole is visible, and\n * {@link isComplete} is the only safe answer to \"can this be submitted\".\n * - {@link paste} always fills from the FIRST box, whichever box the paste\n * landed on.\n *\n * @example\n * ```ts\n * const otp = await loader.getHarness(WrInputOtpHarness);\n *\n * await otp.setValue('482913');\n * expect(await otp.isComplete()).toBe(true);\n * expect(await otp.getValue()).toBe('482913');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrInputOtpHarness extends ComponentHarness {\n static hostSelector = 'wr-input-otp';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrInputOtpHarnessFilters = {}): HarnessPredicate<WrInputOtpHarness> {\n return new HarnessPredicate(WrInputOtpHarness, options)\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('length', options.length, async (harness, length) => (await harness.getLength()) === length)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption('complete', options.complete, async (harness, complete) => (await harness.isComplete()) === complete);\n }\n\n /** The boxes, in order. A box is a whole harness — see {@link WrInputOtpBoxHarness}. */\n async getBoxes(filters: WrInputOtpBoxHarnessFilters = {}): Promise<WrInputOtpBoxHarness[]> {\n return this.locatorForAll(WrInputOtpBoxHarness.with(filters))();\n }\n\n /** One box by index. Throws rather than returning `undefined` for an index that is not there. */\n async getBox(index: number): Promise<WrInputOtpBoxHarness> {\n const boxes = await this.getBoxes();\n const box = boxes[index];\n if (!box) {\n throw new Error(\n `WrInputOtpHarness.getBox(${index}): out of range — this control renders ${boxes.length} ` +\n `boxes, so the valid indexes are 0 to ${boxes.length - 1}.`\n );\n }\n return box;\n }\n\n /** How many boxes are rendered. `length` is clamped to `[1, 20]`, so this is the real count. */\n async getLength(): Promise<number> {\n return (await this.getBoxes()).length;\n }\n\n /** The character in each box, in order, `''` for an empty one. */\n async getBoxValues(): Promise<string[]> {\n const boxes = await this.getBoxes();\n return Promise.all(boxes.map(box => box.getValue()));\n }\n\n /**\n * The assembled code — the boxes joined. For anything typed, pasted or deleted\n * inside the control this is exactly the string it publishes through its\n * `value` model.\n *\n * An emptied middle box closes up here; {@link getBoxValues} keeps the hole.\n *\n * A write from OUTSIDE can leave the two disagreeing, and the model is the one\n * that keeps the surplus: the control splits the incoming value into boxes,\n * dropping what its `mode` rejects and anything past `length`, without writing\n * the shortened code back. Writing `'12a456'` in `numeric` mode shows `12_456`\n * and reads `'12456'` here while the bound model still holds `'12a456'`.\n */\n async getValue(): Promise<string> {\n return (await this.getBoxValues()).join('');\n }\n\n /** Whether every box holds a character — the condition the `completed` output fires on. */\n async isComplete(): Promise<boolean> {\n const values = await this.getBoxValues();\n return values.length > 0 && values.every(value => value !== '');\n }\n\n /**\n * Whether the control refuses interaction.\n *\n * Read from the boxes' `disabled` property rather than the host's\n * `wr-input-otp--disabled` class: the property is what actually stops the\n * events and the focus, the class only paints.\n */\n async isDisabled(): Promise<boolean> {\n const boxes = await this.getBoxes();\n const flags = await Promise.all(boxes.map(box => box.isDisabled()));\n return flags.every(Boolean);\n }\n\n /** Whether the typed characters are hidden, like a password field. */\n async isMasked(): Promise<boolean> {\n return (await this.getBox(0)).isMasked();\n }\n\n /**\n * The keyboard a mobile browser is asked for.\n *\n * `numeric` mode publishes `inputmode=\"numeric\"`; `alphanumeric` and `text`\n * BOTH publish `text`, so the two cannot be told apart from the DOM. What\n * separates them is only observable by typing: `alphanumeric` drops anything\n * that is not a letter or a digit.\n */\n async getInputMode(): Promise<'numeric' | 'text'> {\n const mode = await (await this.boxElement(0)).getAttribute('inputmode');\n return mode === 'numeric' ? 'numeric' : 'text';\n }\n\n /** The control size, from the host modifier. `md` is the default and emits no modifier. */\n async getSize(): Promise<WrInputOtpSize> {\n const host = await this.host();\n if (await host.hasClass('wr-input-otp--sm')) return 'sm';\n if (await host.hasClass('wr-input-otp--lg')) return 'lg';\n return 'md';\n }\n\n /** The character shown in an empty box. */\n async getPlaceholder(): Promise<string> {\n return (await this.boxElement(0)).getProperty<string>('placeholder');\n }\n\n /**\n * The name the group announces.\n *\n * The boxes are separate inputs; the host's `role=\"group\"` plus this label are\n * what make them one control to a screen reader.\n */\n async getLabel(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /** The index of the box with focus, or `null` when focus is outside the control. */\n async getFocusedIndex(): Promise<number | null> {\n const boxes = await this.getBoxes();\n const flags = await Promise.all(boxes.map(box => box.isFocused()));\n const index = flags.indexOf(true);\n return index === -1 ? null : index;\n }\n\n /** Put the caret in a box — the first one unless asked otherwise. */\n async focus(index = 0): Promise<void> {\n await this.assertEnabled('focus');\n await (await this.getBox(index)).focus();\n }\n\n /**\n * Take focus off the control, which is what makes it emit `touch` so a bound\n * field can mark itself touched. The box that HAS focus is the one blurred:\n * blurring any other one still emits `touch` (a unit-test DOM dispatches a blur\n * event even on an element that never had focus), which would report a field as\n * touched while the caret sits in it. Throws when no box has focus at all, for\n * the same reason.\n */\n async blur(): Promise<void> {\n await this.assertEnabled('blur');\n const index = await this.focusedIndexOrThrow('blur');\n await (await this.boxElement(index)).blur();\n }\n\n /**\n * Enter a whole code, the way a person does: one character per box, into\n * whichever box the control has moved focus to.\n *\n * Every box is emptied first, so a shorter code cannot leave the tail of a\n * previous one behind.\n *\n * The control is left to decide what it accepts: a character its `mode`\n * rejects lands nowhere and focus stays put, so the next character overwrites\n * the same box — which is exactly what a browser produces. Read {@link getValue}\n * back rather than assuming the argument arrived.\n */\n async setValue(code: string): Promise<void> {\n await this.assertEnabled('setValue');\n\n const chars = [...code];\n const length = await this.getLength();\n if (chars.length > length) {\n throw new Error(\n `WrInputOtpHarness.setValue('${code}'): ${chars.length} characters for ${length} boxes — ` +\n 'the extra ones would pile into the last box. Trim the code, or raise `length`.'\n );\n }\n\n await this.clear();\n await this.typeFrom(0, chars);\n }\n\n /**\n * Type into the control from where it already is — the focused box, or the\n * first box when focus is elsewhere. Unlike {@link setValue} nothing is cleared,\n * so this appends the way a half-finished code is finished.\n */\n async type(chars: string): Promise<void> {\n await this.assertEnabled('type');\n await this.typeFrom((await this.getFocusedIndex()) ?? 0, [...chars]);\n }\n\n /**\n * Paste a code in.\n *\n * The event goes to the focused box (the first one when focus is elsewhere),\n * because that is where a browser delivers it — but the control fills from the\n * FIRST box regardless, replacing everything, dropping characters its `mode`\n * rejects and trimming what is left to `length`. A paste with nothing usable in\n * it is ignored outright.\n *\n * jsdom implements neither `ClipboardEvent` nor `DataTransfer`, so the payload\n * is attached to a plain event; `getData('text')` is what the control asks for,\n * aliased to `text/plain` the way a browser does.\n */\n async paste(text: string): Promise<void> {\n await this.assertEnabled('paste');\n const target = await this.boxElement((await this.getFocusedIndex()) ?? 0);\n await target.dispatchEvent('paste', {\n clipboardData: { getData: (type: string) => (type === 'text' || type === 'text/plain' ? text : '') },\n });\n }\n\n /**\n * Press Backspace in the focused box.\n *\n * The control only handles the key itself when the box is ALREADY empty — then\n * it clears the box behind and steps focus back, which is what makes fixing a\n * typo one keystroke. On a filled box it defers to the browser's own editing,\n * and jsdom performs none, so the harness supplies that half: the character is\n * removed and an `input` event is dispatched, exactly as a real delete would.\n * Focus stays where it is, and the emptied box becomes a hole in\n * {@link getBoxValues} while {@link getValue} closes up.\n */\n async backspace(): Promise<void> {\n await this.assertEnabled('backspace');\n const index = await this.focusedIndexOrThrow('backspace');\n const box = await this.boxElement(index);\n const filled = (await box.getProperty<string>('value')) !== '';\n\n await box.sendKeys(TestKey.BACKSPACE);\n if (filled) await box.clear();\n }\n\n /**\n * Move focus with the arrow keys, `Home` or `End` — the control's own\n * navigation, and the only one available in a unit test, where nothing has a\n * position to click.\n *\n * Throws when no box has focus: the keys are handled by the box that receives\n * them, so there is no move to make.\n */\n async moveFocus(to: 'previous' | 'next' | 'first' | 'last'): Promise<void> {\n await this.assertEnabled('moveFocus');\n const index = await this.focusedIndexOrThrow('moveFocus');\n await (await this.boxElement(index)).sendKeys(MOVE_KEYS[to]);\n }\n\n /**\n * Empty every box and leave focus on the first, ready to be typed into.\n *\n * Each box is cleared through an `input` event rather than by writing the\n * model, so the control publishes the change the same way it would for a user.\n */\n async clear(): Promise<void> {\n await this.assertEnabled('clear');\n for (const box of await this.getBoxes()) {\n // An already-empty box is skipped: clearing it would re-publish the same\n // code, and drag focus through the control on the way.\n if (!(await box.isEmpty())) await (await box.host()).clear();\n }\n await (await this.boxElement(0)).focus();\n }\n\n /** Type `chars` starting at `index`, following the control's focus as it advances. */\n private async typeFrom(index: number, chars: readonly string[]): Promise<void> {\n let cursor = index;\n for (const char of chars) {\n await (await this.getBox(cursor)).type(char);\n // The control advances focus itself, and only for a character it accepted.\n // Following it — rather than incrementing — is what keeps a rejected\n // character from silently shifting the rest of the code one box along.\n cursor = (await this.getFocusedIndex()) ?? cursor;\n }\n }\n\n private async boxElement(index: number): Promise<TestElement> {\n return (await this.getBox(index)).host();\n }\n\n private async focusedIndexOrThrow(method: string): Promise<number> {\n const index = await this.getFocusedIndex();\n if (index === null) {\n throw new Error(\n `WrInputOtpHarness.${method}(): no box has focus, and this acts on the focused box. ` +\n 'Call focus() (or type into the control) first.'\n );\n }\n return index;\n }\n\n private async assertEnabled(method: string): Promise<void> {\n if (!(await this.isDisabled())) return;\n throw new Error(\n `WrInputOtpHarness.${method}(): the control is disabled. A disabled input fires no events and ` +\n 'takes no focus, so driving it here would report interaction a browser never delivers.'\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-input-otp-testing.mjs","sources":["../../../projects/lib/input-otp/testing/wr-input-otp-box-harness.ts","../../../projects/lib/input-otp/testing/wr-input-otp-harness.ts","../../../projects/lib/input-otp/testing/ngwr-input-otp-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 { WrInputOtpBoxHarnessFilters } from './interfaces';\n\n/**\n * Test harness for ONE box of a `<wr-input-otp>` — the `<input>` that holds a\n * single character.\n *\n * A box is more than the string it shows: it carries its own accessible name\n * (`Digit 3` on a numeric strip, `Character 3` on the others), its own\n * emptiness, its own masking, and it is the thing that either has focus or does\n * not. The root harness answers the control's questions (the assembled code,\n * which index is focused); this one answers the box's, so a spec can hold on to\n * \"the box that has focus\" instead of zipping four parallel arrays by index.\n *\n * @example\n * ```ts\n * await otp.setValue('12');\n * // Two digits in, the first box still waiting is the third.\n * const [box] = await otp.getBoxes({ empty: true });\n * expect(await box.getLabel()).toBe('Digit 3');\n * await box.type('7');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrInputOtpBoxHarness extends ComponentHarness {\n /**\n * The cell class, not `wr-input-otp input`: the class is public API, so it is\n * the stable handle on a cell, and matching on it keeps the harness usable on\n * its own — `getAllHarnesses(WrInputOtpBoxHarness)` finds cells without\n * collecting every other `<input>` on the page.\n */\n static hostSelector = 'input.wr-input-otp__cell';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrInputOtpBoxHarnessFilters = {}): HarnessPredicate<WrInputOtpBoxHarness> {\n return new HarnessPredicate(WrInputOtpBoxHarness, options)\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('empty', options.empty, async (harness, empty) => (await harness.isEmpty()) === empty)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label));\n }\n\n /**\n * The character the box shows, or `''`.\n *\n * Read from the `value` PROPERTY. The `value` attribute is never written at all\n * — Angular binds the property, and the component rewrites that property again\n * in its own `input` handler to keep one character per box — so\n * `getAttribute('value')` answers `null` for every box, full or empty.\n */\n async getValue(): Promise<string> {\n return (await this.host()).getProperty<string>('value');\n }\n\n /** Whether the box is still waiting for a character. */\n async isEmpty(): Promise<boolean> {\n return (await this.getValue()) === '';\n }\n\n /**\n * The box's accessible name — `Digit 1`, `Digit 2`, … on a numeric strip.\n *\n * This is the whole of a box's identity for a screen-reader user: the boxes\n * carry no visible label and no position in an `aria-*` set, so the name is\n * how someone knows which one they are in.\n *\n * The word depends on `mode`, so an assertion has to follow it: the\n * `alphanumeric` and `text` modes accept letters, and the component names\n * their boxes `Character 1`, `Character 2`, … rather than call an `A` a digit.\n * Both come from the `inputOtp.digit` / `inputOtp.character` catalog entries,\n * so a localized app answers here in its own language.\n */\n async getLabel(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /** Whether the box hides what was typed, like a password field. */\n async isMasked(): Promise<boolean> {\n return (await (await this.host()).getProperty<string>('type')) === 'password';\n }\n\n async isDisabled(): Promise<boolean> {\n return (await this.host()).getProperty<boolean>('disabled');\n }\n\n async isFocused(): Promise<boolean> {\n return (await this.host()).isFocused();\n }\n\n /** Put the caret in this box. Throws when the control is disabled. */\n async focus(): Promise<void> {\n await this.assertEnabled('focus');\n await (await this.host()).focus();\n }\n\n /**\n * Type one character into this box.\n *\n * Focus advances to the next box when the character is accepted — the control\n * moves it in the `input` handler, so it has already happened when this\n * resolves. A character the box's `mode` rejects leaves the box EMPTY and\n * focus where it was, which is what a browser shows too.\n */\n async type(char: string): Promise<void> {\n await this.assertEnabled('type');\n if ([...char].length !== 1) {\n throw new Error(\n `WrInputOtpBoxHarness.type('${char}'): a box holds exactly one character (maxlength=\"1\") — ` +\n 'use WrInputOtpHarness.setValue() to enter a whole code.'\n );\n }\n await (await this.host()).sendKeys(char);\n }\n\n private async assertEnabled(method: string): Promise<void> {\n if (!(await this.isDisabled())) return;\n throw new Error(\n `WrInputOtpBoxHarness.${method}(): this box is disabled. A disabled input fires no events and ` +\n 'takes no focus, so driving it here would report interaction a browser never delivers.'\n );\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrInputOtpSize } from 'ngwr/input-otp';\n\nimport type { WrInputOtpBoxHarnessFilters, WrInputOtpHarnessFilters } from './interfaces';\nimport { WrInputOtpBoxHarness } from './wr-input-otp-box-harness';\n\n/** Which box a keyboard move aims at, and the key that gets it there. */\nconst MOVE_KEYS = {\n previous: TestKey.LEFT_ARROW,\n next: TestKey.RIGHT_ARROW,\n first: TestKey.HOME,\n last: TestKey.END,\n} as const;\n\n/**\n * Test harness for `<wr-input-otp>` — one code spread over N single-character\n * boxes, with {@link WrInputOtpBoxHarness} for a box.\n *\n * Everything here is driven from the KEYBOARD and from `input` / `paste` events,\n * never by clicking a box. The control binds no `(click)` handler at all — what a\n * click gives you in a browser is FOCUS, and a unit-test DOM moves no focus on\n * click, so a click-driven method would look like it worked and change nothing.\n * The keyboard is the path this control is designed around anyway.\n *\n * Two things about this control regularly surprise people, and the harness\n * reports them rather than smoothing them over:\n * - {@link getValue} is the boxes JOINED, which is what the component publishes\n * for every edit made inside it. Emptying a middle box therefore SHORTENS the\n * code instead of leaving a gap in it — `1 2 _ 4 5 6` publishes `'12456'`.\n * {@link getBoxValues} is the only place that hole is visible, and\n * {@link isComplete} is the only safe answer to \"can this be submitted\".\n * - {@link paste} always fills from the FIRST box, whichever box the paste\n * landed on.\n *\n * @example\n * ```ts\n * const otp = await loader.getHarness(WrInputOtpHarness);\n *\n * await otp.setValue('482913');\n * expect(await otp.isComplete()).toBe(true);\n * expect(await otp.getValue()).toBe('482913');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrInputOtpHarness extends ComponentHarness {\n static hostSelector = 'wr-input-otp';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrInputOtpHarnessFilters = {}): HarnessPredicate<WrInputOtpHarness> {\n return new HarnessPredicate(WrInputOtpHarness, options)\n .addOption('value', options.value, (harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value))\n .addOption('length', options.length, async (harness, length) => (await harness.getLength()) === length)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption('complete', options.complete, async (harness, complete) => (await harness.isComplete()) === complete);\n }\n\n /** The boxes, in order. A box is a whole harness — see {@link WrInputOtpBoxHarness}. */\n async getBoxes(filters: WrInputOtpBoxHarnessFilters = {}): Promise<WrInputOtpBoxHarness[]> {\n return this.locatorForAll(WrInputOtpBoxHarness.with(filters))();\n }\n\n /** One box by index. Throws rather than returning `undefined` for an index that is not there. */\n async getBox(index: number): Promise<WrInputOtpBoxHarness> {\n const boxes = await this.getBoxes();\n const box = boxes[index];\n if (!box) {\n throw new Error(\n `WrInputOtpHarness.getBox(${index}): out of range — this control renders ${boxes.length} ` +\n `boxes, so the valid indexes are 0 to ${boxes.length - 1}.`\n );\n }\n return box;\n }\n\n /** How many boxes are rendered. `length` is clamped to `[1, 20]`, so this is the real count. */\n async getLength(): Promise<number> {\n return (await this.getBoxes()).length;\n }\n\n /** The character in each box, in order, `''` for an empty one. */\n async getBoxValues(): Promise<string[]> {\n const boxes = await this.getBoxes();\n return Promise.all(boxes.map(box => box.getValue()));\n }\n\n /**\n * The assembled code — the boxes joined. For anything typed, pasted or deleted\n * inside the control this is exactly the string it publishes through its\n * `value` model.\n *\n * An emptied middle box closes up here; {@link getBoxValues} keeps the hole.\n *\n * A write from OUTSIDE can leave the two disagreeing, and the model is the one\n * that keeps the surplus: the control splits the incoming value into boxes,\n * dropping what its `mode` rejects and anything past `length`, without writing\n * the shortened code back. Writing `'12a456'` in `numeric` mode shows `12_456`\n * and reads `'12456'` here while the bound model still holds `'12a456'`.\n */\n async getValue(): Promise<string> {\n return (await this.getBoxValues()).join('');\n }\n\n /** Whether every box holds a character — the condition the `completed` output fires on. */\n async isComplete(): Promise<boolean> {\n const values = await this.getBoxValues();\n return values.length > 0 && values.every(value => value !== '');\n }\n\n /**\n * Whether the control refuses interaction.\n *\n * Read from the boxes' `disabled` property rather than the host's\n * `wr-input-otp--disabled` class: the property is what actually stops the\n * events and the focus, the class only paints.\n */\n async isDisabled(): Promise<boolean> {\n const boxes = await this.getBoxes();\n const flags = await Promise.all(boxes.map(box => box.isDisabled()));\n return flags.every(Boolean);\n }\n\n /** Whether the typed characters are hidden, like a password field. */\n async isMasked(): Promise<boolean> {\n return (await this.getBox(0)).isMasked();\n }\n\n /**\n * The keyboard a mobile browser is asked for.\n *\n * `numeric` mode publishes `inputmode=\"numeric\"`; `alphanumeric` and `text`\n * BOTH publish `text`, so the two cannot be told apart from the DOM. What\n * separates them is only observable by typing: `alphanumeric` drops anything\n * that is not a letter or a digit.\n */\n async getInputMode(): Promise<'numeric' | 'text'> {\n const mode = await (await this.boxElement(0)).getAttribute('inputmode');\n return mode === 'numeric' ? 'numeric' : 'text';\n }\n\n /** The control size, from the host modifier. `md` is the default and emits no modifier. */\n async getSize(): Promise<WrInputOtpSize> {\n const host = await this.host();\n if (await host.hasClass('wr-input-otp--sm')) return 'sm';\n if (await host.hasClass('wr-input-otp--lg')) return 'lg';\n return 'md';\n }\n\n /** The character shown in an empty box. */\n async getPlaceholder(): Promise<string> {\n return (await this.boxElement(0)).getProperty<string>('placeholder');\n }\n\n /**\n * The name the group announces.\n *\n * The boxes are separate inputs; the host's `role=\"group\"` plus this label are\n * what make them one control to a screen reader.\n */\n async getLabel(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /** The index of the box with focus, or `null` when focus is outside the control. */\n async getFocusedIndex(): Promise<number | null> {\n const boxes = await this.getBoxes();\n const flags = await Promise.all(boxes.map(box => box.isFocused()));\n const index = flags.indexOf(true);\n return index === -1 ? null : index;\n }\n\n /** Put the caret in a box — the first one unless asked otherwise. */\n async focus(index = 0): Promise<void> {\n await this.assertEnabled('focus');\n await (await this.getBox(index)).focus();\n }\n\n /**\n * Take focus off the control, which is what makes it emit `touch` so a bound\n * field can mark itself touched. The box that HAS focus is the one blurred:\n * blurring any other one still emits `touch` (a unit-test DOM dispatches a blur\n * event even on an element that never had focus), which would report a field as\n * touched while the caret sits in it. Throws when no box has focus at all, for\n * the same reason.\n */\n async blur(): Promise<void> {\n await this.assertEnabled('blur');\n const index = await this.focusedIndexOrThrow('blur');\n await (await this.boxElement(index)).blur();\n }\n\n /**\n * Enter a whole code, the way a person does: one character per box, into\n * whichever box the control has moved focus to.\n *\n * Every box is emptied first, so a shorter code cannot leave the tail of a\n * previous one behind.\n *\n * The control is left to decide what it accepts: a character its `mode`\n * rejects lands nowhere and focus stays put, so the next character overwrites\n * the same box — which is exactly what a browser produces. Read {@link getValue}\n * back rather than assuming the argument arrived.\n */\n async setValue(code: string): Promise<void> {\n await this.assertEnabled('setValue');\n\n const chars = [...code];\n const length = await this.getLength();\n if (chars.length > length) {\n throw new Error(\n `WrInputOtpHarness.setValue('${code}'): ${chars.length} characters for ${length} boxes — ` +\n 'the extra ones would pile into the last box. Trim the code, or raise `length`.'\n );\n }\n\n await this.clear();\n await this.typeFrom(0, chars);\n }\n\n /**\n * Type into the control from where it already is — the focused box, or the\n * first box when focus is elsewhere. Unlike {@link setValue} nothing is cleared,\n * so this appends the way a half-finished code is finished.\n */\n async type(chars: string): Promise<void> {\n await this.assertEnabled('type');\n await this.typeFrom((await this.getFocusedIndex()) ?? 0, [...chars]);\n }\n\n /**\n * Paste a code in.\n *\n * The event goes to the focused box (the first one when focus is elsewhere),\n * because that is where a browser delivers it — but the control fills from the\n * FIRST box regardless, replacing everything, dropping characters its `mode`\n * rejects and trimming what is left to `length`. A paste with nothing usable in\n * it is ignored outright.\n *\n * jsdom implements neither `ClipboardEvent` nor `DataTransfer`, so the payload\n * is attached to a plain event; `getData('text')` is what the control asks for,\n * aliased to `text/plain` the way a browser does.\n */\n async paste(text: string): Promise<void> {\n await this.assertEnabled('paste');\n const target = await this.boxElement((await this.getFocusedIndex()) ?? 0);\n await target.dispatchEvent('paste', {\n clipboardData: { getData: (type: string) => (type === 'text' || type === 'text/plain' ? text : '') },\n });\n }\n\n /**\n * Press Backspace in the focused box.\n *\n * The control only handles the key itself when the box is ALREADY empty — then\n * it clears the box behind and steps focus back, which is what makes fixing a\n * typo one keystroke. On a filled box it defers to the browser's own editing,\n * and jsdom performs none, so the harness supplies that half: the character is\n * removed and an `input` event is dispatched, exactly as a real delete would.\n * Focus stays where it is, and the emptied box becomes a hole in\n * {@link getBoxValues} while {@link getValue} closes up.\n */\n async backspace(): Promise<void> {\n await this.assertEnabled('backspace');\n const index = await this.focusedIndexOrThrow('backspace');\n const box = await this.boxElement(index);\n const filled = (await box.getProperty<string>('value')) !== '';\n\n await box.sendKeys(TestKey.BACKSPACE);\n if (filled) await box.clear();\n }\n\n /**\n * Move focus with the arrow keys, `Home` or `End` — the control's own\n * navigation, and the only one available in a unit test, where nothing has a\n * position to click.\n *\n * Throws when no box has focus: the keys are handled by the box that receives\n * them, so there is no move to make.\n */\n async moveFocus(to: 'previous' | 'next' | 'first' | 'last'): Promise<void> {\n await this.assertEnabled('moveFocus');\n const index = await this.focusedIndexOrThrow('moveFocus');\n await (await this.boxElement(index)).sendKeys(MOVE_KEYS[to]);\n }\n\n /**\n * Empty every box and leave focus on the first, ready to be typed into.\n *\n * Each box is cleared through an `input` event rather than by writing the\n * model, so the control publishes the change the same way it would for a user.\n */\n async clear(): Promise<void> {\n await this.assertEnabled('clear');\n for (const box of await this.getBoxes()) {\n // An already-empty box is skipped: clearing it would re-publish the same\n // code, and drag focus through the control on the way.\n if (!(await box.isEmpty())) await (await box.host()).clear();\n }\n await (await this.boxElement(0)).focus();\n }\n\n /** Type `chars` starting at `index`, following the control's focus as it advances. */\n private async typeFrom(index: number, chars: readonly string[]): Promise<void> {\n let cursor = index;\n for (const char of chars) {\n await (await this.getBox(cursor)).type(char);\n // The control advances focus itself, and only for a character it accepted.\n // Following it — rather than incrementing — is what keeps a rejected\n // character from silently shifting the rest of the code one box along.\n cursor = (await this.getFocusedIndex()) ?? cursor;\n }\n }\n\n private async boxElement(index: number): Promise<TestElement> {\n return (await this.getBox(index)).host();\n }\n\n private async focusedIndexOrThrow(method: string): Promise<number> {\n const index = await this.getFocusedIndex();\n if (index === null) {\n throw new Error(\n `WrInputOtpHarness.${method}(): no box has focus, and this acts on the focused box. ` +\n 'Call focus() (or type into the control) first.'\n );\n }\n return index;\n }\n\n private async assertEnabled(method: string): Promise<void> {\n if (!(await this.isDisabled())) return;\n throw new Error(\n `WrInputOtpHarness.${method}(): the control is disabled. A disabled input fires no events and ` +\n 'takes no focus, so driving it here would report interaction a browser never delivers.'\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import { coerceNumberProperty, coerceBooleanProperty } from '@angular/cdk/coerci
|
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { inject, input, model, output, signal, computed, viewChildren, effect, untracked, ViewEncapsulation, Component } from '@angular/core';
|
|
5
5
|
import { useConfigValue } from 'ngwr/config';
|
|
6
|
+
import { useI18nText, useI18nFormatter } from 'ngwr/i18n';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @license
|
|
@@ -33,6 +34,10 @@ import { useConfigValue } from 'ngwr/config';
|
|
|
33
34
|
*/
|
|
34
35
|
class WrInputOtp {
|
|
35
36
|
dir = inject(Directionality, { optional: true });
|
|
37
|
+
/** Accessible name of the whole strip. Falls back to `inputOtp.label`. */
|
|
38
|
+
ariaLabel = input(null, /* @ts-ignore */
|
|
39
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
40
|
+
resolvedAriaLabel = useI18nText(this.ariaLabel, 'inputOtp.label', 'Verification code');
|
|
36
41
|
/** Number of cells to render. Clamped to `[1, 20]`. @default 6 */
|
|
37
42
|
length = input(6, { ...(ngDevMode ? { debugName: "length" } : /* istanbul ignore next */ {}), transform: (v) => Math.max(1, Math.min(20, coerceNumberProperty(v, 6))) });
|
|
38
43
|
/** Character set per cell. @default 'numeric' */
|
|
@@ -88,6 +93,8 @@ class WrInputOtp {
|
|
|
88
93
|
...(ngDevMode ? [{ debugName: "cellType" }] : /* istanbul ignore next */ []));
|
|
89
94
|
cellInputMode = computed(() => (this.mode() === 'numeric' ? 'numeric' : 'text'), /* @ts-ignore */
|
|
90
95
|
...(ngDevMode ? [{ debugName: "cellInputMode" }] : /* istanbul ignore next */ []));
|
|
96
|
+
digitLabel = useI18nFormatter('inputOtp.digit', 'Digit {{index}}');
|
|
97
|
+
characterLabel = useI18nFormatter('inputOtp.character', 'Character {{index}}');
|
|
91
98
|
cellRefs = viewChildren('cell', /* @ts-ignore */
|
|
92
99
|
...(ngDevMode ? [{ debugName: "cellRefs" }] : /* istanbul ignore next */ []));
|
|
93
100
|
constructor() {
|
|
@@ -175,6 +182,18 @@ class WrInputOtp {
|
|
|
175
182
|
trackIndex(index) {
|
|
176
183
|
return index;
|
|
177
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* A box's accessible name. It projects no text and carries no label of its own,
|
|
187
|
+
* so this string IS the name a screen reader reads for it.
|
|
188
|
+
*
|
|
189
|
+
* Keyed off `mode`, not fixed: `sanitiseChar` passes any letter through in
|
|
190
|
+
* `alphanumeric` and `text`, so the old literal announced "Digit 3" over a box
|
|
191
|
+
* holding `A` — wrong in a way translating it would not have fixed.
|
|
192
|
+
*/
|
|
193
|
+
cellLabel(index) {
|
|
194
|
+
const params = { index: index + 1 };
|
|
195
|
+
return this.mode() === 'numeric' ? this.digitLabel(params) : this.characterLabel(params);
|
|
196
|
+
}
|
|
178
197
|
// Internals
|
|
179
198
|
update(index, char) {
|
|
180
199
|
if (index < 0 || index >= this.length())
|
|
@@ -231,13 +250,13 @@ class WrInputOtp {
|
|
|
231
250
|
return 'ArrowRight';
|
|
232
251
|
return key;
|
|
233
252
|
}
|
|
234
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
235
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
253
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrInputOtp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
254
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrInputOtp, isStandalone: true, selector: "wr-input-otp", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, length: { classPropertyName: "length", publicName: "length", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, mask: { classPropertyName: "mask", publicName: "mask", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", completed: "completed", touch: "touch" }, host: { attributes: { "role": "group" }, properties: { "class": "classes()", "attr.aria-label": "resolvedAriaLabel()" } }, viewQueries: [{ propertyName: "cellRefs", predicate: ["cell"], descendants: true, isSignal: true }], ngImport: i0, template: "@for (cell of cells(); track trackIndex($index)) {\n <input\n #cell\n class=\"wr-input-otp__cell\"\n [type]=\"cellType()\"\n [attr.inputmode]=\"cellInputMode()\"\n maxlength=\"1\"\n autocomplete=\"one-time-code\"\n autocapitalize=\"off\"\n spellcheck=\"false\"\n [value]=\"cell\"\n [disabled]=\"disabled()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"cellLabel($index)\"\n (input)=\"onInput($event, $index)\"\n (keydown)=\"onKeyDown($event, $index)\"\n (paste)=\"onPaste($event)\"\n (blur)=\"onBlur()\"\n />\n}\n", encapsulation: i0.ViewEncapsulation.None });
|
|
236
255
|
}
|
|
237
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
256
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrInputOtp, decorators: [{
|
|
238
257
|
type: Component,
|
|
239
|
-
args: [{ selector: 'wr-input-otp', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()', role: 'group', 'aria-label': '
|
|
240
|
-
}], ctorParameters: () => [], propDecorators: { length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], mask: [{ type: i0.Input, args: [{ isSignal: true, alias: "mask", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], completed: [{ type: i0.Output, args: ["completed"] }], touch: [{ type: i0.Output, args: ["touch"] }], cellRefs: [{ type: i0.ViewChildren, args: ['cell', { isSignal: true }] }] } });
|
|
258
|
+
args: [{ selector: 'wr-input-otp', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()', role: 'group', '[attr.aria-label]': 'resolvedAriaLabel()' }, template: "@for (cell of cells(); track trackIndex($index)) {\n <input\n #cell\n class=\"wr-input-otp__cell\"\n [type]=\"cellType()\"\n [attr.inputmode]=\"cellInputMode()\"\n maxlength=\"1\"\n autocomplete=\"one-time-code\"\n autocapitalize=\"off\"\n spellcheck=\"false\"\n [value]=\"cell\"\n [disabled]=\"disabled()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"cellLabel($index)\"\n (input)=\"onInput($event, $index)\"\n (keydown)=\"onKeyDown($event, $index)\"\n (paste)=\"onPaste($event)\"\n (blur)=\"onBlur()\"\n />\n}\n" }]
|
|
259
|
+
}], ctorParameters: () => [], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], mask: [{ type: i0.Input, args: [{ isSignal: true, alias: "mask", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], completed: [{ type: i0.Output, args: ["completed"] }], touch: [{ type: i0.Output, args: ["touch"] }], cellRefs: [{ type: i0.ViewChildren, args: ['cell', { isSignal: true }] }] } });
|
|
241
260
|
|
|
242
261
|
/**
|
|
243
262
|
* Generated bundle index. Do not edit.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-input-otp.mjs","sources":["../../../projects/lib/input-otp/input-otp.ts","../../../projects/lib/input-otp/input-otp.html","../../../projects/lib/input-otp/ngwr-input-otp.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n type ElementRef,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n untracked,\n viewChildren,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useConfigValue } from 'ngwr/config';\n\nimport type { WrInputOtpMode, WrInputOtpSize } from './interfaces';\n\n/**\n * Fixed-length one-time-code input. Renders one `<input>` per character,\n * auto-advances focus on typing, handles paste of a full code, and supports\n * masking like a password field.\n *\n * A signal-forms native control: it implements `FormValueControl<string>`, so\n * `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone. Emits\n * `completed` once all cells are filled (useful for auto-submission).\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-input-otp [formField]=\"form.code\" length=\"6\" (completed)=\"verify($event)\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-input-otp [(value)]=\"code\" length=\"6\" (completed)=\"verify($event)\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/input-otp\n */\n@Component({\n selector: 'wr-input-otp',\n templateUrl: './input-otp.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()', role: 'group', 'aria-label': 'Verification code' },\n})\nexport class WrInputOtp implements FormValueControl<string> {\n private readonly dir = inject(Directionality, { optional: true });\n\n /** Number of cells to render. Clamped to `[1, 20]`. @default 6 */\n readonly length = input(6, {\n transform: (v: unknown): number => Math.max(1, Math.min(20, coerceNumberProperty(v, 6))),\n });\n\n /** Character set per cell. @default 'numeric' */\n readonly mode = input<WrInputOtpMode>('numeric');\n\n /**\n * Control size — shares the `--wr-control-*` contract. Unset falls back to the\n * `inputOtp.size` app default from `provideWrConfig()`. @default 'md'\n */\n readonly size = input<WrInputOtpSize | null>(null);\n\n /**\n * `size`, then the app config, then `md`.\n *\n * Read ONCE, here: a box carries no size of its own — the host modifier sets\n * `--wr-input-otp-size` / `-font` / `-radius` / `-gap` and every box reads them\n * from the cascade — so one resolution sizes the whole strip however many boxes\n * `length` renders.\n */\n protected readonly resolvedSize = useConfigValue(this.size, c => c.inputOtp?.size, 'md');\n\n /** Mask the typed characters like a password. @default false */\n readonly mask = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Disable interaction. Bound automatically from the field's disabled state\n * when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Character shown in empty cells. @default '•' */\n readonly placeholder = input<string>('•');\n\n /** The entered code. Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<string>('');\n\n /** Fires once when every cell holds a character. */\n readonly completed = output<string>();\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n protected readonly cells = signal<readonly string[]>(Array.from({ length: 6 }, () => ''));\n\n protected readonly classes = computed(() => {\n const parts = ['wr-input-otp'];\n const size = this.resolvedSize();\n if (size !== 'md') parts.push(`wr-input-otp--${size}`);\n if (this.disabled()) parts.push('wr-input-otp--disabled');\n return parts.join(' ');\n });\n\n protected readonly cellType = computed<'password' | 'text'>(() => (this.mask() ? 'password' : 'text'));\n protected readonly cellInputMode = computed(() => (this.mode() === 'numeric' ? 'numeric' : 'text'));\n\n protected readonly cellRefs = viewChildren<ElementRef<HTMLInputElement>>('cell');\n\n constructor() {\n // Resize the cell array whenever `length` changes — preserves existing values.\n effect(() => {\n const len = this.length();\n const current = this.cells();\n if (current.length === len) return;\n const next = Array.from({ length: len }, (_, i) => current[i] ?? '');\n this.cells.set(next);\n });\n\n // Split an external `value` write back into cells (mirrors the old\n // writeValue). Skipped when the value is merely the echo of an in-cell\n // edit, so re-splitting can never shift or clobber what the user typed.\n effect(() => {\n // Coerce null/undefined to '' — a classic-forms binding can write null,\n // which the old `writeValue(v: string | null)` accepted too.\n const v = this.value() ?? '';\n untracked(() => {\n if (this.cells().join('') === v) return;\n const len = this.length();\n const raw = v.slice(0, len);\n const next = Array.from({ length: len }, (_, i) => this.sanitiseChar(raw[i] ?? ''));\n this.cells.set(next);\n });\n });\n }\n\n // Template handlers\n\n protected onInput(event: Event, index: number): void {\n const target = event.target as HTMLInputElement;\n const char = this.sanitiseChar(target.value.slice(-1));\n target.value = char;\n this.update(index, char);\n if (char) this.focusCell(index + 1);\n }\n\n protected onKeyDown(event: KeyboardEvent, index: number): void {\n const target = event.target as HTMLInputElement;\n switch (this.inlineKey(event.key)) {\n case 'Backspace':\n if (target.value === '') {\n event.preventDefault();\n this.update(index - 1, '');\n this.focusCell(index - 1);\n }\n break;\n case 'ArrowLeft':\n event.preventDefault();\n this.focusCell(index - 1);\n break;\n case 'ArrowRight':\n event.preventDefault();\n this.focusCell(index + 1);\n break;\n case 'Home':\n event.preventDefault();\n this.focusCell(0);\n break;\n case 'End':\n event.preventDefault();\n this.focusCell(this.length() - 1);\n break;\n }\n }\n\n protected onPaste(event: ClipboardEvent): void {\n event.preventDefault();\n const text = event.clipboardData?.getData('text') ?? '';\n const chars = text\n .split('')\n .map(c => this.sanitiseChar(c))\n .filter(c => c.length > 0);\n if (chars.length === 0) return;\n const trimmed = chars.slice(0, this.length());\n const next = Array.from({ length: this.length() }, (_, i) => trimmed[i] ?? '');\n this.cells.set(next);\n this.emitChange();\n this.focusCell(Math.min(trimmed.length, this.length() - 1));\n }\n\n protected onBlur(): void {\n this.touch.emit();\n }\n\n protected trackIndex(index: number): number {\n return index;\n }\n\n // Internals\n\n private update(index: number, char: string): void {\n if (index < 0 || index >= this.length()) return;\n const next = [...this.cells()];\n next[index] = char;\n this.cells.set(next);\n this.emitChange();\n }\n\n private emitChange(): void {\n const code = this.cells().join('');\n this.value.set(code);\n if (code.length === this.length() && code.split('').every(c => c.length === 1)) {\n this.completed.emit(code);\n }\n }\n\n private focusCell(i: number): void {\n if (i < 0 || i >= this.length()) return;\n const el = this.cellRefs()[i]?.nativeElement;\n if (!el) return;\n el.focus();\n el.select();\n }\n\n /** Drop a character that doesn't match the configured mode. */\n private sanitiseChar(c: string): string {\n if (!c) return '';\n const one = c.slice(0, 1);\n switch (this.mode()) {\n case 'numeric':\n return /\\d/.test(one) ? one : '';\n case 'alphanumeric':\n return /[a-zA-Z0-9]/.test(one) ? one : '';\n case 'text':\n default:\n return one;\n }\n }\n\n /**\n * The key as the strip SEES it.\n *\n * The boxes run along the inline axis, so under `dir=\"rtl\"` the box to the\n * visual right of the caret is the PREVIOUS one. Home/End stay semantic (first\n * and last box), and Backspace is not a direction at all.\n */\n private inlineKey(key: string): string {\n if (this.dir?.value !== 'rtl') return key;\n if (key === 'ArrowRight') return 'ArrowLeft';\n if (key === 'ArrowLeft') return 'ArrowRight';\n return key;\n }\n}\n","@for (cell of cells(); track trackIndex($index)) {\n <input\n #cell\n class=\"wr-input-otp__cell\"\n [type]=\"cellType()\"\n [attr.inputmode]=\"cellInputMode()\"\n maxlength=\"1\"\n autocomplete=\"one-time-code\"\n autocapitalize=\"off\"\n spellcheck=\"false\"\n [value]=\"cell\"\n [disabled]=\"disabled()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"`Digit ${$index + 1}`\"\n (input)=\"onInput($event, $index)\"\n (keydown)=\"onKeyDown($event, $index)\"\n (paste)=\"onPaste($event)\"\n (blur)=\"onBlur()\"\n />\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-input-otp.mjs","sources":["../../../projects/lib/input-otp/input-otp.ts","../../../projects/lib/input-otp/input-otp.html","../../../projects/lib/input-otp/ngwr-input-otp.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n Component,\n type ElementRef,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n untracked,\n viewChildren,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useConfigValue } from 'ngwr/config';\nimport { useI18nFormatter, useI18nText } from 'ngwr/i18n';\n\nimport type { WrInputOtpMode, WrInputOtpSize } from './interfaces';\n\n/**\n * Fixed-length one-time-code input. Renders one `<input>` per character,\n * auto-advances focus on typing, handles paste of a full code, and supports\n * masking like a password field.\n *\n * A signal-forms native control: it implements `FormValueControl<string>`, so\n * `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone. Emits\n * `completed` once all cells are filled (useful for auto-submission).\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-input-otp [formField]=\"form.code\" length=\"6\" (completed)=\"verify($event)\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-input-otp [(value)]=\"code\" length=\"6\" (completed)=\"verify($event)\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/input-otp\n */\n@Component({\n selector: 'wr-input-otp',\n templateUrl: './input-otp.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()', role: 'group', '[attr.aria-label]': 'resolvedAriaLabel()' },\n})\nexport class WrInputOtp implements FormValueControl<string> {\n private readonly dir = inject(Directionality, { optional: true });\n\n /** Accessible name of the whole strip. Falls back to `inputOtp.label`. */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'inputOtp.label', 'Verification code');\n\n /** Number of cells to render. Clamped to `[1, 20]`. @default 6 */\n readonly length = input(6, {\n transform: (v: unknown): number => Math.max(1, Math.min(20, coerceNumberProperty(v, 6))),\n });\n\n /** Character set per cell. @default 'numeric' */\n readonly mode = input<WrInputOtpMode>('numeric');\n\n /**\n * Control size — shares the `--wr-control-*` contract. Unset falls back to the\n * `inputOtp.size` app default from `provideWrConfig()`. @default 'md'\n */\n readonly size = input<WrInputOtpSize | null>(null);\n\n /**\n * `size`, then the app config, then `md`.\n *\n * Read ONCE, here: a box carries no size of its own — the host modifier sets\n * `--wr-input-otp-size` / `-font` / `-radius` / `-gap` and every box reads them\n * from the cascade — so one resolution sizes the whole strip however many boxes\n * `length` renders.\n */\n protected readonly resolvedSize = useConfigValue(this.size, c => c.inputOtp?.size, 'md');\n\n /** Mask the typed characters like a password. @default false */\n readonly mask = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Disable interaction. Bound automatically from the field's disabled state\n * when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Character shown in empty cells. @default '•' */\n readonly placeholder = input<string>('•');\n\n /** The entered code. Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<string>('');\n\n /** Fires once when every cell holds a character. */\n readonly completed = output<string>();\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n protected readonly cells = signal<readonly string[]>(Array.from({ length: 6 }, () => ''));\n\n protected readonly classes = computed(() => {\n const parts = ['wr-input-otp'];\n const size = this.resolvedSize();\n if (size !== 'md') parts.push(`wr-input-otp--${size}`);\n if (this.disabled()) parts.push('wr-input-otp--disabled');\n return parts.join(' ');\n });\n\n protected readonly cellType = computed<'password' | 'text'>(() => (this.mask() ? 'password' : 'text'));\n protected readonly cellInputMode = computed(() => (this.mode() === 'numeric' ? 'numeric' : 'text'));\n\n private readonly digitLabel = useI18nFormatter('inputOtp.digit', 'Digit {{index}}');\n private readonly characterLabel = useI18nFormatter('inputOtp.character', 'Character {{index}}');\n\n protected readonly cellRefs = viewChildren<ElementRef<HTMLInputElement>>('cell');\n\n constructor() {\n // Resize the cell array whenever `length` changes — preserves existing values.\n effect(() => {\n const len = this.length();\n const current = this.cells();\n if (current.length === len) return;\n const next = Array.from({ length: len }, (_, i) => current[i] ?? '');\n this.cells.set(next);\n });\n\n // Split an external `value` write back into cells (mirrors the old\n // writeValue). Skipped when the value is merely the echo of an in-cell\n // edit, so re-splitting can never shift or clobber what the user typed.\n effect(() => {\n // Coerce null/undefined to '' — a classic-forms binding can write null,\n // which the old `writeValue(v: string | null)` accepted too.\n const v = this.value() ?? '';\n untracked(() => {\n if (this.cells().join('') === v) return;\n const len = this.length();\n const raw = v.slice(0, len);\n const next = Array.from({ length: len }, (_, i) => this.sanitiseChar(raw[i] ?? ''));\n this.cells.set(next);\n });\n });\n }\n\n // Template handlers\n\n protected onInput(event: Event, index: number): void {\n const target = event.target as HTMLInputElement;\n const char = this.sanitiseChar(target.value.slice(-1));\n target.value = char;\n this.update(index, char);\n if (char) this.focusCell(index + 1);\n }\n\n protected onKeyDown(event: KeyboardEvent, index: number): void {\n const target = event.target as HTMLInputElement;\n switch (this.inlineKey(event.key)) {\n case 'Backspace':\n if (target.value === '') {\n event.preventDefault();\n this.update(index - 1, '');\n this.focusCell(index - 1);\n }\n break;\n case 'ArrowLeft':\n event.preventDefault();\n this.focusCell(index - 1);\n break;\n case 'ArrowRight':\n event.preventDefault();\n this.focusCell(index + 1);\n break;\n case 'Home':\n event.preventDefault();\n this.focusCell(0);\n break;\n case 'End':\n event.preventDefault();\n this.focusCell(this.length() - 1);\n break;\n }\n }\n\n protected onPaste(event: ClipboardEvent): void {\n event.preventDefault();\n const text = event.clipboardData?.getData('text') ?? '';\n const chars = text\n .split('')\n .map(c => this.sanitiseChar(c))\n .filter(c => c.length > 0);\n if (chars.length === 0) return;\n const trimmed = chars.slice(0, this.length());\n const next = Array.from({ length: this.length() }, (_, i) => trimmed[i] ?? '');\n this.cells.set(next);\n this.emitChange();\n this.focusCell(Math.min(trimmed.length, this.length() - 1));\n }\n\n protected onBlur(): void {\n this.touch.emit();\n }\n\n protected trackIndex(index: number): number {\n return index;\n }\n\n /**\n * A box's accessible name. It projects no text and carries no label of its own,\n * so this string IS the name a screen reader reads for it.\n *\n * Keyed off `mode`, not fixed: `sanitiseChar` passes any letter through in\n * `alphanumeric` and `text`, so the old literal announced \"Digit 3\" over a box\n * holding `A` — wrong in a way translating it would not have fixed.\n */\n protected cellLabel(index: number): string {\n const params = { index: index + 1 };\n return this.mode() === 'numeric' ? this.digitLabel(params) : this.characterLabel(params);\n }\n\n // Internals\n\n private update(index: number, char: string): void {\n if (index < 0 || index >= this.length()) return;\n const next = [...this.cells()];\n next[index] = char;\n this.cells.set(next);\n this.emitChange();\n }\n\n private emitChange(): void {\n const code = this.cells().join('');\n this.value.set(code);\n if (code.length === this.length() && code.split('').every(c => c.length === 1)) {\n this.completed.emit(code);\n }\n }\n\n private focusCell(i: number): void {\n if (i < 0 || i >= this.length()) return;\n const el = this.cellRefs()[i]?.nativeElement;\n if (!el) return;\n el.focus();\n el.select();\n }\n\n /** Drop a character that doesn't match the configured mode. */\n private sanitiseChar(c: string): string {\n if (!c) return '';\n const one = c.slice(0, 1);\n switch (this.mode()) {\n case 'numeric':\n return /\\d/.test(one) ? one : '';\n case 'alphanumeric':\n return /[a-zA-Z0-9]/.test(one) ? one : '';\n case 'text':\n default:\n return one;\n }\n }\n\n /**\n * The key as the strip SEES it.\n *\n * The boxes run along the inline axis, so under `dir=\"rtl\"` the box to the\n * visual right of the caret is the PREVIOUS one. Home/End stay semantic (first\n * and last box), and Backspace is not a direction at all.\n */\n private inlineKey(key: string): string {\n if (this.dir?.value !== 'rtl') return key;\n if (key === 'ArrowRight') return 'ArrowLeft';\n if (key === 'ArrowLeft') return 'ArrowRight';\n return key;\n }\n}\n","@for (cell of cells(); track trackIndex($index)) {\n <input\n #cell\n class=\"wr-input-otp__cell\"\n [type]=\"cellType()\"\n [attr.inputmode]=\"cellInputMode()\"\n maxlength=\"1\"\n autocomplete=\"one-time-code\"\n autocapitalize=\"off\"\n spellcheck=\"false\"\n [value]=\"cell\"\n [disabled]=\"disabled()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"cellLabel($index)\"\n (input)=\"onInput($event, $index)\"\n (keydown)=\"onKeyDown($event, $index)\"\n (paste)=\"onPaste($event)\"\n (blur)=\"onBlur()\"\n />\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;AAAA;;;;;AAKG;AAyBH;;;;;;;;;;;;;;;;;;;;AAoBG;MAOU,UAAU,CAAA;IACJ,GAAG,GAAG,MAAM,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;IAGxD,SAAS,GAAG,KAAK,CAAgB,IAAI;kFAAC;IAE5B,iBAAiB,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,EAAE,mBAAmB,CAAC;;AAGhG,IAAA,MAAM,GAAG,KAAK,CAAC,CAAC,8EACvB,SAAS,EAAE,CAAC,CAAU,KAAa,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GACxF;;IAGO,IAAI,GAAG,KAAK,CAAiB,SAAS;6EAAC;AAEhD;;;AAGG;IACM,IAAI,GAAG,KAAK,CAAwB,IAAI;6EAAC;AAElD;;;;;;;AAOG;AACgB,IAAA,YAAY,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC;;IAG/E,IAAI,GAAG,KAAK,CAAC,KAAK,4EAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;AAElE;;;;;AAKG;IACM,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;IAG7D,WAAW,GAAG,KAAK,CAAS,GAAG;oFAAC;;IAGhC,KAAK,GAAG,KAAK,CAAS,EAAE;8EAAC;;IAGzB,SAAS,GAAG,MAAM,EAAU;;IAG5B,KAAK,GAAG,MAAM,EAAQ;AAEZ,IAAA,KAAK,GAAG,MAAM,CAAoB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC;8EAAC;AAEtE,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAK;AACzC,QAAA,MAAM,KAAK,GAAG,CAAC,cAAc,CAAC;AAC9B,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE;QAChC,IAAI,IAAI,KAAK,IAAI;AAAE,YAAA,KAAK,CAAC,IAAI,CAAC,iBAAiB,IAAI,CAAA,CAAE,CAAC;QACtD,IAAI,IAAI,CAAC,QAAQ,EAAE;AAAE,YAAA,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC;AACzD,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;IACxB,CAAC;gFAAC;AAEiB,IAAA,QAAQ,GAAG,QAAQ,CAAsB,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,UAAU,GAAG,MAAM,CAAC;iFAAC;IACnF,aAAa,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,IAAI,EAAE,KAAK,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;sFAAC;AAElF,IAAA,UAAU,GAAG,gBAAgB,CAAC,gBAAgB,EAAE,iBAAiB,CAAC;AAClE,IAAA,cAAc,GAAG,gBAAgB,CAAC,oBAAoB,EAAE,qBAAqB,CAAC;IAE5E,QAAQ,GAAG,YAAY,CAA+B,MAAM;iFAAC;AAEhF,IAAA,WAAA,GAAA;;QAEE,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE;AACzB,YAAA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE;AAC5B,YAAA,IAAI,OAAO,CAAC,MAAM,KAAK,GAAG;gBAAE;YAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACpE,YAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,QAAA,CAAC,CAAC;;;;QAKF,MAAM,CAAC,MAAK;;;YAGV,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE;YAC5B,SAAS,CAAC,MAAK;gBACb,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;oBAAE;AACjC,gBAAA,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE;gBACzB,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;AAC3B,gBAAA,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACnF,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACtB,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;IACJ;;IAIU,OAAO,CAAC,KAAY,EAAE,KAAa,EAAA;AAC3C,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAA0B;AAC/C,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACtD,QAAA,MAAM,CAAC,KAAK,GAAG,IAAI;AACnB,QAAA,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC;AACxB,QAAA,IAAI,IAAI;AAAE,YAAA,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC;IACrC;IAEU,SAAS,CAAC,KAAoB,EAAE,KAAa,EAAA;AACrD,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAA0B;QAC/C,QAAQ,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC;AAC/B,YAAA,KAAK,WAAW;AACd,gBAAA,IAAI,MAAM,CAAC,KAAK,KAAK,EAAE,EAAE;oBACvB,KAAK,CAAC,cAAc,EAAE;oBACtB,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,EAAE,EAAE,CAAC;AAC1B,oBAAA,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC;gBAC3B;gBACA;AACF,YAAA,KAAK,WAAW;gBACd,KAAK,CAAC,cAAc,EAAE;AACtB,gBAAA,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC;gBACzB;AACF,YAAA,KAAK,YAAY;gBACf,KAAK,CAAC,cAAc,EAAE;AACtB,gBAAA,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC;gBACzB;AACF,YAAA,KAAK,MAAM;gBACT,KAAK,CAAC,cAAc,EAAE;AACtB,gBAAA,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;gBACjB;AACF,YAAA,KAAK,KAAK;gBACR,KAAK,CAAC,cAAc,EAAE;gBACtB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;gBACjC;;IAEN;AAEU,IAAA,OAAO,CAAC,KAAqB,EAAA;QACrC,KAAK,CAAC,cAAc,EAAE;AACtB,QAAA,MAAM,IAAI,GAAG,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE;QACvD,MAAM,KAAK,GAAG;aACX,KAAK,CAAC,EAAE;aACR,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;aAC7B,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAC5B,QAAA,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE;AACxB,QAAA,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;AAC7C,QAAA,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC9E,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QACpB,IAAI,CAAC,UAAU,EAAE;AACjB,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAC7D;IAEU,MAAM,GAAA;AACd,QAAA,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;IACnB;AAEU,IAAA,UAAU,CAAC,KAAa,EAAA;AAChC,QAAA,OAAO,KAAK;IACd;AAEA;;;;;;;AAOG;AACO,IAAA,SAAS,CAAC,KAAa,EAAA;QAC/B,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE;QACnC,OAAO,IAAI,CAAC,IAAI,EAAE,KAAK,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;IAC1F;;IAIQ,MAAM,CAAC,KAAa,EAAE,IAAY,EAAA;QACxC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE;YAAE;QACzC,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;AAC9B,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI;AAClB,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QACpB,IAAI,CAAC,UAAU,EAAE;IACnB;IAEQ,UAAU,GAAA;QAChB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;AAClC,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACpB,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE;AAC9E,YAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;QAC3B;IACF;AAEQ,IAAA,SAAS,CAAC,CAAS,EAAA;QACzB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE;YAAE;QACjC,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa;AAC5C,QAAA,IAAI,CAAC,EAAE;YAAE;QACT,EAAE,CAAC,KAAK,EAAE;QACV,EAAE,CAAC,MAAM,EAAE;IACb;;AAGQ,IAAA,YAAY,CAAC,CAAS,EAAA;AAC5B,QAAA,IAAI,CAAC,CAAC;AAAE,YAAA,OAAO,EAAE;QACjB,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;AACzB,QAAA,QAAQ,IAAI,CAAC,IAAI,EAAE;AACjB,YAAA,KAAK,SAAS;AACZ,gBAAA,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,EAAE;AAClC,YAAA,KAAK,cAAc;AACjB,gBAAA,OAAO,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,EAAE;AAC3C,YAAA,KAAK,MAAM;AACX,YAAA;AACE,gBAAA,OAAO,GAAG;;IAEhB;AAEA;;;;;;AAMG;AACK,IAAA,SAAS,CAAC,GAAW,EAAA;AAC3B,QAAA,IAAI,IAAI,CAAC,GAAG,EAAE,KAAK,KAAK,KAAK;AAAE,YAAA,OAAO,GAAG;QACzC,IAAI,GAAG,KAAK,YAAY;AAAE,YAAA,OAAO,WAAW;QAC5C,IAAI,GAAG,KAAK,WAAW;AAAE,YAAA,OAAO,YAAY;AAC5C,QAAA,OAAO,GAAG;IACZ;uGApOW,UAAU,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAV,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAU,g2CCzDvB,ikBAoBA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FDqCa,UAAU,EAAA,UAAA,EAAA,CAAA;kBANtB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,cAAc,iBAET,iBAAiB,CAAC,IAAI,EAAA,IAAA,EAC/B,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,EAAA,QAAA,EAAA,ikBAAA,EAAA;29BAyElB,MAAM,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AEhIjF;;AAEG;;;;"}
|