ngwr 11.2.0 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/burger/styles/_index.scss +1 -1
- package/button/styles/_index.scss +2 -2
- package/calendar/styles/_index.scss +32 -12
- package/calendar-heatmap/styles/_index.scss +20 -0
- package/carousel/styles/_index.scss +23 -1
- package/cascader/styles/_index.scss +15 -3
- package/checkbox/styles/_index.scss +2 -2
- package/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 +17 -13
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-pipes.mjs +39 -26
- package/fesm2022/ngwr-pipes.mjs.map +1 -1
- package/fesm2022/ngwr-platform.mjs +9 -9
- package/fesm2022/ngwr-popconfirm.mjs +6 -6
- package/fesm2022/ngwr-popover-testing.mjs +7 -2
- package/fesm2022/ngwr-popover-testing.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +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 +14 -1
- package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
- package/fesm2022/ngwr-segmented.mjs +29 -6
- package/fesm2022/ngwr-segmented.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +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 +18 -23
- package/llms.txt +22 -6
- 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-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 +9 -0
- package/types/ngwr-segmented.d.ts +21 -1
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-i18n.mjs","sources":["../../../projects/lib/i18n/i18n-base-catalogs.ts","../../../projects/lib/i18n/i18n-config.ts","../../../projects/lib/i18n/i18n-loader.ts","../../../projects/lib/i18n/i18n.ts","../../../projects/lib/i18n/t.pipe.ts","../../../projects/lib/i18n/t.directive.ts","../../../projects/lib/i18n/loaders/static-loader.ts","../../../projects/lib/i18n/loaders/http-loader.ts","../../../projects/lib/i18n/provide-wr-i18n.ts","../../../projects/lib/i18n/util.ts","../../../projects/lib/i18n/ngwr-i18n.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\nimport { type WrI18nCatalog } from './i18n-config';\n\n/** Base catalogs keyed by locale, contributed by {@link provideWrI18nBaseCatalogs}. */\nexport type WrI18nBaseCatalogs = Readonly<Record<string, WrI18nCatalog>>;\n\n/**\n * Catalogs consulted *underneath* whatever the loader returned — the place for\n * ngwr's own built-in strings.\n *\n * A loader **replaces** the catalog for a locale rather than extending it, so an\n * app whose JSON holds only its own keys used to lose every built-in label to\n * its hardcoded English fallback, silently. Registering `wrRu` / `wrEn` here\n * puts them back without the app having to merge anything itself.\n *\n * `multi`, so several features can each contribute; later contributions win on\n * a key collision, and anything the loader returned still wins over all of them.\n *\n * It has to be handed in by the consumer rather than wired up inside\n * `provideWrI18n()`: the catalogs are their own entry points (`ngwr/i18n/ru`)\n * and they already import from `ngwr/i18n`, so importing them back here is a\n * circular entry-point dependency that ng-packagr rejects outright. Passing\n * them in also keeps the locales you don't ship out of your bundle.\n */\nexport const WR_I18N_BASE_CATALOGS = new InjectionToken<readonly WrI18nBaseCatalogs[]>('WR_I18N_BASE_CATALOGS');\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\n/** Translation catalog — flat or nested record of strings. */\nexport interface WrI18nCatalog {\n readonly [key: string]: string | WrI18nCatalog;\n}\n\n/** Parameters passed to interpolation — coerced to strings via `String(v)`. */\nexport type WrI18nParams = Readonly<Record<string, unknown>>;\n\n/** Hook called once on every key that resolves to nothing. */\nexport type WrI18nMissingHandler = (key: string, locale: string) => string;\n\n/** User-facing config for {@link provideWrI18n}. */\nexport interface WrI18nConfig {\n /** BCP-47 tag used until the user calls `setLocale`. @default 'en' */\n readonly defaultLocale?: string;\n\n /** Whitelisted locales — `setLocale` ignores anything outside this list. */\n readonly availableLocales?: readonly string[];\n\n /**\n * Storage key for persisting the active locale (via `WrStorage`).\n * Set to `null` to disable persistence. @default 'wr:i18n:locale'\n */\n readonly storageKey?: string | null;\n\n /** Called when a key is missing. Default returns the key itself. */\n readonly missingHandler?: WrI18nMissingHandler;\n}\n\n/** Resolved config — every field non-optional. @internal */\nexport interface WrI18nConfigResolved {\n readonly defaultLocale: string;\n readonly availableLocales: readonly string[];\n readonly storageKey: string | null;\n readonly missingHandler: WrI18nMissingHandler;\n}\n\nexport const DEFAULT_WR_I18N_CONFIG: WrI18nConfigResolved = {\n defaultLocale: 'en',\n availableLocales: ['en'],\n storageKey: 'wr:i18n:locale',\n missingHandler: key => key,\n};\n\nexport const WR_I18N_CONFIG = new InjectionToken<WrI18nConfigResolved>('WR_I18N_CONFIG', {\n factory: () => DEFAULT_WR_I18N_CONFIG,\n});\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\nimport { of, type Observable } from 'rxjs';\n\nimport { type WrI18nCatalog } from './i18n-config';\n\n/**\n * Loads a catalog for a `(locale, scope)` pair. `scope` is `null` for the\n * root catalog. Implementations may return the catalog synchronously\n * (via `of(...)`) for static catalogs or asynchronously (via HTTP) for\n * lazy fetches.\n */\nexport interface WrI18nLoader {\n load(locale: string, scope: string | null): Observable<WrI18nCatalog>;\n}\n\n/**\n * The active loader.\n *\n * Defaults to one that serves an empty catalog, which is what makes\n * `provideWrI18n()` genuinely optional: every ngwr component routes its\n * built-in strings through `useI18nText` / `readI18nText`, those inject\n * `WrI18n` optionally — and because `WrI18n` is `providedIn: 'root'`, an\n * optional inject still CONSTRUCTS it. Without a default here that construction\n * threw `NG0201: No provider found for WR_I18N_LOADER`, so an app that had\n * simply not configured i18n could not render `<wr-alert>` at all.\n *\n * With the empty catalog, every lookup misses, `t()` hands back the key, and\n * the helpers fall through to the component's own English default — the\n * documented no-i18n behaviour.\n */\nexport const WR_I18N_LOADER = new InjectionToken<WrI18nLoader>('WR_I18N_LOADER', {\n providedIn: 'root',\n factory: () => ({ load: () => of({}) }),\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 { Service, type Signal, computed, effect, inject, signal } from '@angular/core';\n\nimport { type Observable, firstValueFrom } from 'rxjs';\n\nimport { WrStorage } from 'ngwr/storage';\n\nimport { WR_I18N_BASE_CATALOGS } from './i18n-base-catalogs';\nimport { WR_I18N_CONFIG, type WrI18nCatalog, type WrI18nParams } from './i18n-config';\nimport { WR_I18N_LOADER } from './i18n-loader';\n\n/** Cache key shape: `<locale>::<scope|''>`. @internal */\nfunction cacheKey(locale: string, scope: string | null): string {\n return `${locale}::${scope ?? ''}`;\n}\n\n/** Walk a dotted path through a catalog tree. Returns string | null. @internal */\nfunction walk(cat: WrI18nCatalog | null, key: string): string | null {\n if (!cat) return null;\n let node: string | WrI18nCatalog | undefined = cat;\n for (const part of key.split('.')) {\n if (node === undefined || typeof node === 'string') return null;\n node = node[part];\n }\n return typeof node === 'string' ? node : null;\n}\n\n/**\n * `{{name}}` interpolation. Missing params resolve to `''`. Exported so\n * helpers in `util.ts` can interpolate the fallback template when no\n * `WrI18n` provider is wired up.\n *\n * @internal\n */\nexport function wrInterpolate(template: string, params: WrI18nParams | undefined): string {\n if (!params) return template;\n return template.replace(/\\{\\{\\s*([\\w.-]+)\\s*\\}\\}/g, (_match, name: string) => {\n const v = params[name];\n if (v === undefined || v === null) return '';\n if (typeof v === 'string') return v;\n if (typeof v === 'number' || typeof v === 'boolean') return String(v);\n try {\n return JSON.stringify(v);\n } catch {\n return '';\n }\n });\n}\n\n/**\n * Reactive i18n service. Holds:\n *\n * - the active locale as a `Signal<string>`\n * - per-`(locale, scope)` catalogs in a `Map` (loaded once, then cached)\n * - a per-locale signal counter so every `translate()` reading the signal\n * re-evaluates the moment a new catalog finishes loading\n *\n * Translations are looked up scope-first, then root. `t('forms.save', { ... })`\n * with `scope: 'forms'` will try the scoped catalog before falling back to\n * `forms.save` in the root catalog — useful for per-feature overrides.\n *\n * @example\n * ```ts\n * const i18n = inject(WrI18n);\n * i18n.use('ru'); // switch locale\n * i18n.t('cart.checkout'); // → 'Оформить заказ'\n * i18n.t('greeting', { name: 'Ada' }); // → 'Hello, Ada!'\n *\n * effect(() => console.log(i18n.translate('hi')())); // re-runs on locale change\n * ```\n *\n * @see https://ngwr.dev/reference/services/i18n\n */\n@Service()\nexport class WrI18n {\n private readonly config = inject(WR_I18N_CONFIG);\n private readonly loader = inject(WR_I18N_LOADER);\n private readonly storage = inject(WrStorage);\n\n /**\n * Catalogs registered via `provideWrI18nBaseCatalogs()` — the floor under the\n * loader's own catalogs, so ngwr's built-in strings survive an app that only\n * ships its own keys. @internal\n */\n private readonly baseCatalogs = inject(WR_I18N_BASE_CATALOGS, { optional: true }) ?? [];\n\n /** Per-(locale,scope) catalogs, populated by `loadCatalog`. @internal */\n private readonly catalogs = new Map<string, WrI18nCatalog>();\n\n /** Bumped after each catalog write so reactive translates recompute. @internal */\n private readonly revision = signal(0);\n\n /** In-flight load promises — dedupes parallel `t()` callers. @internal */\n private readonly inflight = new Map<string, Promise<WrI18nCatalog>>();\n\n /** Registered scopes — auto-loaded whenever the locale changes. @internal */\n private readonly scopes = new Set<string>();\n\n /** Active locale. Writes go through `use()` so storage stays in sync. */\n private readonly _locale = signal<string>(this.initialLocale());\n readonly locale: Signal<string> = this._locale.asReadonly();\n\n constructor() {\n // Eagerly load the root catalog + every registered scope for the\n // current locale. Subsequent locale changes refire the same effect.\n effect(() => {\n const lc = this._locale();\n void this.loadCatalog(lc, null);\n for (const sc of this.scopes) void this.loadCatalog(lc, sc);\n });\n }\n\n // Locale\n\n /** Switch active locale. Ignored when not whitelisted. */\n use(locale: string): void {\n if (!this.config.availableLocales.includes(locale)) return;\n this._locale.set(locale);\n if (this.config.storageKey) this.storage.set(this.config.storageKey, locale);\n }\n\n /** Available locales — pass-through from config. */\n available(): readonly string[] {\n return this.config.availableLocales;\n }\n\n // Scopes\n\n /**\n * Register a scope so its catalog loads alongside the root one. Idempotent.\n * Returns a promise resolving once the current locale's scope catalog is in\n * the cache — handy for feature `canActivate` guards.\n */\n registerScope(scope: string): Promise<WrI18nCatalog> {\n this.scopes.add(scope);\n return this.loadCatalog(this._locale(), scope);\n }\n\n // Translate\n\n /**\n * Eager translate — returns the current value or the missing-handler\n * fallback. Does NOT auto-load missing catalogs; combine with the\n * reactive `translate(...)` if you want load-on-demand.\n */\n t(key: string, params?: WrI18nParams, scope?: string): string {\n // Read revision so callers inside computed/effect re-run on updates.\n this.revision();\n const lc = this._locale();\n\n let hit = scope ? walk(this.catalogs.get(cacheKey(lc, scope)) ?? null, key) : null;\n hit ??= walk(this.catalogs.get(cacheKey(lc, null)) ?? null, key);\n // Base catalogs are the floor: consulted only once the loader's own catalogs\n // have missed, so an app's keys always win, but ngwr's built-in strings are\n // still there when the app's catalog doesn't carry them.\n hit ??= this.walkBase(lc, key);\n\n if (hit === null) {\n // Fire a background load so a future read can resolve it.\n void this.loadCatalog(lc, scope ?? null);\n return this.config.missingHandler(key, lc);\n }\n return wrInterpolate(hit, params);\n }\n\n /** Reactive translate — re-evaluates on locale or catalog updates. */\n translate(key: string, params?: WrI18nParams, scope?: string): Signal<string> {\n return computed(() => this.t(key, params, scope));\n }\n\n // Internals\n\n /**\n * Look a key up in the registered base catalogs, later contributions first so\n * a feature can shadow an earlier one.\n */\n private walkBase(locale: string, key: string): string | null {\n for (let i = this.baseCatalogs.length - 1; i >= 0; i--) {\n const hit = walk(this.baseCatalogs[i][locale] ?? null, key);\n if (hit !== null) return hit;\n }\n return null;\n }\n\n private initialLocale(): string {\n if (this.config.storageKey) {\n const saved = this.storage.get<string>(this.config.storageKey);\n if (saved && this.config.availableLocales.includes(saved)) return saved;\n }\n return this.config.defaultLocale;\n }\n\n private loadCatalog(locale: string, scope: string | null): Promise<WrI18nCatalog> {\n const ck = cacheKey(locale, scope);\n const cached = this.catalogs.get(ck);\n if (cached) return Promise.resolve(cached);\n\n const pending = this.inflight.get(ck);\n if (pending) return pending;\n\n const stream: Observable<WrI18nCatalog> = this.loader.load(locale, scope);\n const promise = firstValueFrom(stream)\n .then(catalog => {\n this.catalogs.set(ck, catalog);\n this.revision.update(v => v + 1);\n return catalog;\n })\n .catch((err: unknown) => {\n // eslint-disable-next-line no-console -- intentional diagnostic for missing catalog\n console.warn(`[ngwr/i18n] failed to load (${locale}, ${scope ?? 'root'})`, err);\n const empty: WrI18nCatalog = {};\n this.catalogs.set(ck, empty);\n return empty;\n })\n .finally(() => {\n this.inflight.delete(ck);\n });\n\n this.inflight.set(ck, promise);\n return promise;\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 { Pipe, type PipeTransform, inject } from '@angular/core';\n\nimport { WrI18n } from './i18n';\nimport { type WrI18nParams } from './i18n-config';\n\n/**\n * Translate a key. Impure — re-evaluates on every CD so locale switches\n * and catalog loads propagate without a manual refresh.\n *\n * @example\n * ```html\n * <h1>{{ 'home.title' | wrT }}</h1>\n * <p>{{ 'greeting' | wrT: { name: user().name } }}</p>\n * <button>{{ 'forms.save' | wrT: null : 'checkout' }}</button>\n * ```\n */\n// eslint-disable-next-line @angular-eslint/no-pipe-impure -- intentional: locale + catalog updates must propagate even when args are stable\n@Pipe({ name: 'wrT', pure: false })\nexport class WrTPipe implements PipeTransform {\n private readonly i18n = inject(WrI18n);\n\n transform(key: string, params?: WrI18nParams | null, scope?: string): string {\n return this.i18n.t(key, params ?? undefined, scope);\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 { Directive, ElementRef, computed, effect, inject, input } from '@angular/core';\n\nimport { WrI18n } from './i18n';\nimport { type WrI18nParams } from './i18n-config';\n\n/**\n * Attribute directive that sets `textContent` of its host to the translation\n * of the bound key. Reactive — re-renders on locale + catalog changes.\n *\n * @example\n * ```html\n * <span [wrT]=\"'home.title'\"></span>\n * <span [wrT]=\"'greeting'\" [wrTParams]=\"{ name: user().name }\"></span>\n * <span [wrT]=\"'forms.save'\" wrTScope=\"checkout\"></span>\n * ```\n */\n@Directive({\n selector: '[wrT]',\n})\nexport class WrTDirective {\n /** Translation key. Required. */\n readonly wrT = input.required<string>();\n\n /** Interpolation params for `{{name}}` tokens. */\n readonly wrTParams = input<WrI18nParams | null>(null);\n\n /** Optional scope — scoped lookup first, then root fallback. */\n readonly wrTScope = input<string | null>(null);\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly i18n = inject(WrI18n);\n\n private readonly text = computed(() =>\n this.i18n.t(this.wrT(), this.wrTParams() ?? undefined, this.wrTScope() ?? undefined)\n );\n\n constructor() {\n effect(() => {\n this.host.nativeElement.textContent = this.text();\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 { type Observable, of } from 'rxjs';\n\nimport { type WrI18nCatalog } from '../i18n-config';\nimport { type WrI18nLoader } from '../i18n-loader';\n\n/** Root catalogs keyed by locale: `{ en: catalog, ru: catalog, … }`. */\nexport type WrI18nStaticCatalogs = Readonly<Record<string, WrI18nCatalog>>;\n\n/** Per-scope catalogs: `{ checkout: { en: catalog, ru: catalog }, … }`. */\nexport type WrI18nStaticScopedCatalogs = Readonly<Record<string, WrI18nStaticCatalogs>>;\n\n/**\n * Loader for catalogs already in memory (imported from JSON or generated).\n *\n * - Pass `catalogs` for the root catalog of every locale.\n * - Pass `scopes` if you ship per-feature catalogs the loader should serve\n * to `WrI18n.registerScope(name)` calls.\n *\n * Missing locale or scope resolves to `{}` — the missing-key handler then\n * decides what to render.\n */\nexport class WrI18nStaticLoader implements WrI18nLoader {\n constructor(\n private readonly catalogs: WrI18nStaticCatalogs,\n private readonly scopes: WrI18nStaticScopedCatalogs = {}\n ) {}\n\n load(locale: string, scope: string | null): Observable<WrI18nCatalog> {\n if (scope === null) return of(this.catalogs[locale] ?? {});\n return of(this.scopes[scope]?.[locale] ?? {});\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 { HttpClient } from '@angular/common/http';\nimport { inject } from '@angular/core';\n\nimport { type Observable, catchError, of } from 'rxjs';\n\nimport { type WrI18nCatalog } from '../i18n-config';\nimport { type WrI18nLoader } from '../i18n-loader';\n\n/** Configuration for {@link WrI18nHttpLoader}. */\nexport interface WrI18nHttpLoaderConfig {\n /**\n * URL template. Tokens:\n * - `{locale}` — required, BCP-47 tag (e.g. `en`).\n * - `{scope}` — optional, scope name. Use the `rootPath` if you need a\n * different path entirely for the root catalog.\n *\n * @example\n * ```ts\n * { path: '/assets/i18n/{locale}.json' } // root only\n * { path: '/assets/i18n/{scope}/{locale}.json',\n * rootPath: '/assets/i18n/{locale}.json' } // root + scopes\n * ```\n */\n readonly path: string;\n /** Path template for the root catalog. Defaults to `path`. */\n readonly rootPath?: string;\n}\n\n/**\n * Loader that fetches catalogs via `HttpClient`. Failures resolve to `{}`\n * (logged once at the service layer) so a missing file never throws into\n * user code.\n */\nexport class WrI18nHttpLoader implements WrI18nLoader {\n private readonly http = inject(HttpClient);\n\n constructor(private readonly config: WrI18nHttpLoaderConfig) {}\n\n load(locale: string, scope: string | null): Observable<WrI18nCatalog> {\n const template = scope === null ? (this.config.rootPath ?? this.config.path) : this.config.path;\n const url = template.replace('{locale}', locale).replace('{scope}', scope ?? '');\n return this.http.get<WrI18nCatalog>(url).pipe(catchError(() => of<WrI18nCatalog>({})));\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type EnvironmentProviders, type Provider, makeEnvironmentProviders } from '@angular/core';\n\nimport { WR_I18N_BASE_CATALOGS, type WrI18nBaseCatalogs } from './i18n-base-catalogs';\nimport { DEFAULT_WR_I18N_CONFIG, WR_I18N_CONFIG, type WrI18nConfig, type WrI18nConfigResolved } from './i18n-config';\nimport { WR_I18N_LOADER, type WrI18nLoader } from './i18n-loader';\nimport { WrI18nHttpLoader, type WrI18nHttpLoaderConfig } from './loaders/http-loader';\nimport {\n WrI18nStaticLoader,\n type WrI18nStaticCatalogs,\n type WrI18nStaticScopedCatalogs,\n} from './loaders/static-loader';\n\n/**\n * Configure {@link WrI18n}. Either pass a `loader` directly, or use the\n * `provideWrI18nHttpLoader` / `provideWrI18nStaticLoader` helpers in the\n * same providers array.\n */\nexport interface ProvideWrI18nOptions extends WrI18nConfig {\n readonly loader?: WrI18nLoader;\n}\n\nexport function provideWrI18n(options: ProvideWrI18nOptions = {}): EnvironmentProviders {\n const { loader, ...config } = options;\n const merged: WrI18nConfigResolved = { ...DEFAULT_WR_I18N_CONFIG, ...config };\n\n const providers: Provider[] = [{ provide: WR_I18N_CONFIG, useValue: merged }];\n if (loader) providers.push({ provide: WR_I18N_LOADER, useValue: loader });\n\n return makeEnvironmentProviders(providers);\n}\n\n/**\n * Provide a static loader — catalogs already in memory.\n *\n * @param catalogs Root catalogs keyed by locale.\n * @param scopes Optional per-scope catalogs served to `registerScope(name)`.\n */\nexport function provideWrI18nStaticLoader(\n catalogs: WrI18nStaticCatalogs,\n scopes?: WrI18nStaticScopedCatalogs\n): Provider {\n return { provide: WR_I18N_LOADER, useValue: new WrI18nStaticLoader(catalogs, scopes) };\n}\n\n/**\n * Register catalogs consulted *underneath* the loader — this is how ngwr's own\n * built-in strings survive when you supply your own catalogs.\n *\n * A loader replaces the catalog for a locale instead of extending it, so a JSON\n * file holding only your keys leaves every built-in label on its hardcoded\n * English fallback, with nothing logged. One line fixes it, on either loader:\n *\n * @example\n * ```ts\n * import { wrRu } from 'ngwr/i18n/ru';\n *\n * providers: [\n * provideWrI18n({ defaultLocale: 'ru', availableLocales: ['ru'] }),\n * provideWrI18nBaseCatalogs({ ru: wrRu }),\n * provideWrI18nHttpLoader({ path: '/assets/i18n/{locale}.json' }),\n * ]\n * ```\n *\n * Pass only the locales you ship — the ones you leave out stay out of the\n * bundle. Your own keys always win; this is a floor, not an override.\n */\nexport function provideWrI18nBaseCatalogs(catalogs: WrI18nBaseCatalogs): Provider {\n return { provide: WR_I18N_BASE_CATALOGS, useValue: catalogs, multi: true };\n}\n\n/** Provide an HTTP loader — catalogs fetched from URLs. Requires `provideHttpClient`. */\nexport function provideWrI18nHttpLoader(config: WrI18nHttpLoaderConfig): Provider {\n return { provide: WR_I18N_LOADER, useFactory: () => new WrI18nHttpLoader(config) };\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type Signal, computed, inject, signal } from '@angular/core';\n\nimport { WrI18n, wrInterpolate } from './i18n';\nimport type { WrI18nParams } from './i18n-config';\n\n/**\n * Helper for component default text. Returns a reactive string that:\n *\n * 1. Prefers the consumer's override (an `input<string | null>()` value).\n * 2. Falls back to the i18n catalog under `key` — re-renders on locale\n * change so a switcher updates the DOM live.\n * 3. Falls back to `fallback` when no `WrI18n` is provided OR the catalog\n * is missing the key.\n *\n * Components don't need a hard dependency on `provideWrI18n` — without it,\n * the fallback is returned and everything still works.\n *\n * @example\n * ```ts\n * readonly emptyText = input<string | null>(null);\n * protected readonly empty = useI18nText(this.emptyText, 'table.empty', 'No data');\n *\n * // In template: <div>{{ empty() }}</div>\n * ```\n */\nexport function useI18nText(binding: Signal<string | null | undefined>, key: string, fallback: string): Signal<string> {\n const i18n = inject(WrI18n, { optional: true });\n if (!i18n) {\n return computed(() => {\n const v = binding();\n return v ?? fallback;\n });\n }\n\n return computed(() => {\n const v = binding();\n if (v !== null && v !== undefined && v !== '') return v;\n const translated = i18n.t(key);\n // Default missing handler returns the key itself — treat that as \"missing\"\n // and use our explicit fallback.\n return translated === key ? fallback : translated;\n });\n}\n\n/**\n * Like {@link useI18nText} but takes a literal value (not a Signal) — for\n * computed `aria-label`s that already mix a static piece with a dynamic\n * value. Returns the resolved string once, at injection time.\n */\nexport function readI18nText(key: string, fallback: string): string {\n const i18n = inject(WrI18n, { optional: true });\n if (!i18n) return fallback;\n const v = i18n.t(key);\n return v === key ? fallback : v;\n}\n\n/** Tiny shim — useful when the caller doesn't have an `input` to forward. */\nexport function nullSignal(): Signal<null> {\n return signal(null).asReadonly();\n}\n\n/**\n * Build a per-call formatter for parametrized labels (e.g. ARIA labels\n * that interpolate a per-row value: `\"Remove {{label}}\"`). Returns a\n * function the template calls with the params for the current item.\n *\n * The returned function:\n * - Re-resolves the i18n catalog on every call (live locale switches just\n * work — re-rendered templates pick up new strings).\n * - Falls back to the literal `fallback` template with the same\n * `{{name}}` syntax when no `WrI18n` provider is present, or when the\n * key is missing.\n *\n * Use this for labels that vary per row. For static labels, prefer\n * {@link useI18nText} or {@link readI18nText}.\n *\n * @example\n * ```ts\n * protected readonly removeChipLabel = useI18nFormatter('select.removeItem', 'Remove {{label}}');\n *\n * // In template:\n * // [attr.aria-label]=\"removeChipLabel({ label: chip.label })\"\n * ```\n */\nexport function useI18nFormatter(key: string, fallback: string): (params?: WrI18nParams) => string {\n const i18n = inject(WrI18n, { optional: true });\n if (!i18n) {\n return (params?: WrI18nParams) => wrInterpolate(fallback, params);\n }\n return (params?: WrI18nParams) => {\n const v = i18n.t(key, params);\n return v === key ? wrInterpolate(fallback, params) : v;\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-i18n.mjs","sources":["../../../projects/lib/i18n/i18n-base-catalogs.ts","../../../projects/lib/i18n/i18n-config.ts","../../../projects/lib/i18n/i18n-loader.ts","../../../projects/lib/i18n/i18n.ts","../../../projects/lib/i18n/t.pipe.ts","../../../projects/lib/i18n/t.directive.ts","../../../projects/lib/i18n/loaders/static-loader.ts","../../../projects/lib/i18n/loaders/http-loader.ts","../../../projects/lib/i18n/provide-wr-i18n.ts","../../../projects/lib/i18n/util.ts","../../../projects/lib/i18n/ngwr-i18n.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\nimport { type WrI18nCatalog } from './i18n-config';\n\n/** Base catalogs keyed by locale, contributed by {@link provideWrI18nBaseCatalogs}. */\nexport type WrI18nBaseCatalogs = Readonly<Record<string, WrI18nCatalog>>;\n\n/**\n * Catalogs consulted *underneath* whatever the loader returned — the place for\n * ngwr's own built-in strings.\n *\n * A loader **replaces** the catalog for a locale rather than extending it, so an\n * app whose JSON holds only its own keys used to lose every built-in label to\n * its hardcoded English fallback, silently. Registering `wrRu` / `wrEn` here\n * puts them back without the app having to merge anything itself.\n *\n * `multi`, so several features can each contribute; later contributions win on\n * a key collision, and anything the loader returned still wins over all of them.\n *\n * It has to be handed in by the consumer rather than wired up inside\n * `provideWrI18n()`: the catalogs are their own entry points (`ngwr/i18n/ru`)\n * and they already import from `ngwr/i18n`, so importing them back here is a\n * circular entry-point dependency that ng-packagr rejects outright. Passing\n * them in also keeps the locales you don't ship out of your bundle.\n */\nexport const WR_I18N_BASE_CATALOGS = new InjectionToken<readonly WrI18nBaseCatalogs[]>('WR_I18N_BASE_CATALOGS');\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\n/** Translation catalog — flat or nested record of strings. */\nexport interface WrI18nCatalog {\n readonly [key: string]: string | WrI18nCatalog;\n}\n\n/** Parameters passed to interpolation — coerced to strings via `String(v)`. */\nexport type WrI18nParams = Readonly<Record<string, unknown>>;\n\n/** Hook called once on every key that resolves to nothing. */\nexport type WrI18nMissingHandler = (key: string, locale: string) => string;\n\n/** User-facing config for {@link provideWrI18n}. */\nexport interface WrI18nConfig {\n /** BCP-47 tag used until the user calls `setLocale`. @default 'en' */\n readonly defaultLocale?: string;\n\n /** Whitelisted locales — `setLocale` ignores anything outside this list. */\n readonly availableLocales?: readonly string[];\n\n /**\n * Storage key for persisting the active locale (via `WrStorage`).\n * Set to `null` to disable persistence. @default 'wr:i18n:locale'\n */\n readonly storageKey?: string | null;\n\n /** Called when a key is missing. Default returns the key itself. */\n readonly missingHandler?: WrI18nMissingHandler;\n}\n\n/** Resolved config — every field non-optional. @internal */\nexport interface WrI18nConfigResolved {\n readonly defaultLocale: string;\n readonly availableLocales: readonly string[];\n readonly storageKey: string | null;\n readonly missingHandler: WrI18nMissingHandler;\n}\n\nexport const DEFAULT_WR_I18N_CONFIG: WrI18nConfigResolved = {\n defaultLocale: 'en',\n availableLocales: ['en'],\n storageKey: 'wr:i18n:locale',\n missingHandler: key => key,\n};\n\nexport const WR_I18N_CONFIG = new InjectionToken<WrI18nConfigResolved>('WR_I18N_CONFIG', {\n factory: () => DEFAULT_WR_I18N_CONFIG,\n});\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\nimport { of, type Observable } from 'rxjs';\n\nimport { type WrI18nCatalog } from './i18n-config';\n\n/**\n * Loads a catalog for a `(locale, scope)` pair. `scope` is `null` for the\n * root catalog. Implementations may return the catalog synchronously\n * (via `of(...)`) for static catalogs or asynchronously (via HTTP) for\n * lazy fetches.\n */\nexport interface WrI18nLoader {\n load(locale: string, scope: string | null): Observable<WrI18nCatalog>;\n}\n\n/**\n * The active loader.\n *\n * Defaults to one that serves an empty catalog, which is what makes\n * `provideWrI18n()` genuinely optional: every ngwr component routes its\n * built-in strings through `useI18nText` / `readI18nText`, those inject\n * `WrI18n` optionally — and because `WrI18n` is `providedIn: 'root'`, an\n * optional inject still CONSTRUCTS it. Without a default here that construction\n * threw `NG0201: No provider found for WR_I18N_LOADER`, so an app that had\n * simply not configured i18n could not render `<wr-alert>` at all.\n *\n * With the empty catalog, every lookup misses, `t()` hands back the key, and\n * the helpers fall through to the component's own English default — the\n * documented no-i18n behaviour.\n */\nexport const WR_I18N_LOADER = new InjectionToken<WrI18nLoader>('WR_I18N_LOADER', {\n providedIn: 'root',\n factory: () => ({ load: () => of({}) }),\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 { Service, type Signal, computed, effect, inject, signal } from '@angular/core';\n\nimport { type Observable, firstValueFrom } from 'rxjs';\n\nimport { WrStorage } from 'ngwr/storage';\n\nimport { WR_I18N_BASE_CATALOGS } from './i18n-base-catalogs';\nimport { WR_I18N_CONFIG, type WrI18nCatalog, type WrI18nParams } from './i18n-config';\nimport { WR_I18N_LOADER } from './i18n-loader';\n\n/** Cache key shape: `<locale>::<scope|''>`. @internal */\nfunction cacheKey(locale: string, scope: string | null): string {\n return `${locale}::${scope ?? ''}`;\n}\n\n/** Walk a dotted path through a catalog tree. Returns string | null. @internal */\nfunction walk(cat: WrI18nCatalog | null, key: string): string | null {\n if (!cat) return null;\n let node: string | WrI18nCatalog | undefined = cat;\n for (const part of key.split('.')) {\n if (node === undefined || typeof node === 'string') return null;\n node = node[part];\n }\n return typeof node === 'string' ? node : null;\n}\n\n/**\n * `{{name}}` interpolation. Missing params resolve to `''`. Exported so\n * helpers in `util.ts` can interpolate the fallback template when no\n * `WrI18n` provider is wired up.\n *\n * @internal\n */\nexport function wrInterpolate(template: string, params: WrI18nParams | undefined): string {\n if (!params) return template;\n return template.replace(/\\{\\{\\s*([\\w.-]+)\\s*\\}\\}/g, (_match, name: string) => {\n const v = params[name];\n if (v === undefined || v === null) return '';\n if (typeof v === 'string') return v;\n if (typeof v === 'number' || typeof v === 'boolean') return String(v);\n try {\n return JSON.stringify(v);\n } catch {\n return '';\n }\n });\n}\n\n/**\n * Reactive i18n service. Holds:\n *\n * - the active locale as a `Signal<string>`\n * - per-`(locale, scope)` catalogs in a `Map` (loaded once, then cached)\n * - a per-locale signal counter so every `translate()` reading the signal\n * re-evaluates the moment a new catalog finishes loading\n *\n * Translations are looked up scope-first, then root. `t('forms.save', { ... })`\n * with `scope: 'forms'` will try the scoped catalog before falling back to\n * `forms.save` in the root catalog — useful for per-feature overrides.\n *\n * @example\n * ```ts\n * const i18n = inject(WrI18n);\n * i18n.use('ru'); // switch locale\n * i18n.t('cart.checkout'); // → 'Оформить заказ'\n * i18n.t('greeting', { name: 'Ada' }); // → 'Hello, Ada!'\n *\n * effect(() => console.log(i18n.translate('hi')())); // re-runs on locale change\n * ```\n *\n * @see https://ngwr.dev/reference/services/i18n\n */\n@Service()\nexport class WrI18n {\n private readonly config = inject(WR_I18N_CONFIG);\n private readonly loader = inject(WR_I18N_LOADER);\n private readonly storage = inject(WrStorage);\n\n /**\n * Catalogs registered via `provideWrI18nBaseCatalogs()` — the floor under the\n * loader's own catalogs, so ngwr's built-in strings survive an app that only\n * ships its own keys. @internal\n */\n private readonly baseCatalogs = inject(WR_I18N_BASE_CATALOGS, { optional: true }) ?? [];\n\n /** Per-(locale,scope) catalogs, populated by `loadCatalog`. @internal */\n private readonly catalogs = new Map<string, WrI18nCatalog>();\n\n /** Bumped after each catalog write so reactive translates recompute. @internal */\n private readonly revision = signal(0);\n\n /** In-flight load promises — dedupes parallel `t()` callers. @internal */\n private readonly inflight = new Map<string, Promise<WrI18nCatalog>>();\n\n /** Registered scopes — auto-loaded whenever the locale changes. @internal */\n private readonly scopes = new Set<string>();\n\n /** Active locale. Writes go through `use()` so storage stays in sync. */\n private readonly _locale = signal<string>(this.initialLocale());\n readonly locale: Signal<string> = this._locale.asReadonly();\n\n constructor() {\n // Eagerly load the root catalog + every registered scope for the\n // current locale. Subsequent locale changes refire the same effect.\n effect(() => {\n const lc = this._locale();\n void this.loadCatalog(lc, null);\n for (const sc of this.scopes) void this.loadCatalog(lc, sc);\n });\n }\n\n // Locale\n\n /** Switch active locale. Ignored when not whitelisted. */\n use(locale: string): void {\n if (!this.config.availableLocales.includes(locale)) return;\n this._locale.set(locale);\n if (this.config.storageKey) this.storage.set(this.config.storageKey, locale);\n }\n\n /** Available locales — pass-through from config. */\n available(): readonly string[] {\n return this.config.availableLocales;\n }\n\n // Scopes\n\n /**\n * Register a scope so its catalog loads alongside the root one. Idempotent.\n * Returns a promise resolving once the current locale's scope catalog is in\n * the cache — handy for feature `canActivate` guards.\n */\n registerScope(scope: string): Promise<WrI18nCatalog> {\n this.scopes.add(scope);\n return this.loadCatalog(this._locale(), scope);\n }\n\n // Translate\n\n /**\n * Eager translate — returns the current value or the missing-handler\n * fallback. Does NOT auto-load missing catalogs; combine with the\n * reactive `translate(...)` if you want load-on-demand.\n */\n t(key: string, params?: WrI18nParams, scope?: string): string {\n // Read revision so callers inside computed/effect re-run on updates.\n this.revision();\n const lc = this._locale();\n\n let hit = scope ? walk(this.catalogs.get(cacheKey(lc, scope)) ?? null, key) : null;\n hit ??= walk(this.catalogs.get(cacheKey(lc, null)) ?? null, key);\n // Base catalogs are the floor: consulted only once the loader's own catalogs\n // have missed, so an app's keys always win, but ngwr's built-in strings are\n // still there when the app's catalog doesn't carry them.\n hit ??= this.walkBase(lc, key);\n\n if (hit === null) {\n // Fire a background load so a future read can resolve it.\n void this.loadCatalog(lc, scope ?? null);\n return this.config.missingHandler(key, lc);\n }\n return wrInterpolate(hit, params);\n }\n\n /** Reactive translate — re-evaluates on locale or catalog updates. */\n translate(key: string, params?: WrI18nParams, scope?: string): Signal<string> {\n return computed(() => this.t(key, params, scope));\n }\n\n // Internals\n\n /**\n * Look a key up in the registered base catalogs, later contributions first so\n * a feature can shadow an earlier one.\n */\n private walkBase(locale: string, key: string): string | null {\n for (let i = this.baseCatalogs.length - 1; i >= 0; i--) {\n const hit = walk(this.baseCatalogs[i][locale] ?? null, key);\n if (hit !== null) return hit;\n }\n return null;\n }\n\n private initialLocale(): string {\n if (this.config.storageKey) {\n const saved = this.storage.get<string>(this.config.storageKey);\n if (saved && this.config.availableLocales.includes(saved)) return saved;\n }\n return this.config.defaultLocale;\n }\n\n private loadCatalog(locale: string, scope: string | null): Promise<WrI18nCatalog> {\n const ck = cacheKey(locale, scope);\n const cached = this.catalogs.get(ck);\n if (cached) return Promise.resolve(cached);\n\n const pending = this.inflight.get(ck);\n if (pending) return pending;\n\n const stream: Observable<WrI18nCatalog> = this.loader.load(locale, scope);\n const promise = firstValueFrom(stream)\n .then(catalog => {\n this.catalogs.set(ck, catalog);\n this.revision.update(v => v + 1);\n return catalog;\n })\n .catch((err: unknown) => {\n // eslint-disable-next-line no-console -- intentional diagnostic for missing catalog\n console.warn(`[ngwr/i18n] failed to load (${locale}, ${scope ?? 'root'})`, err);\n const empty: WrI18nCatalog = {};\n this.catalogs.set(ck, empty);\n return empty;\n })\n .finally(() => {\n this.inflight.delete(ck);\n });\n\n this.inflight.set(ck, promise);\n return promise;\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 { Pipe, type PipeTransform, inject } from '@angular/core';\n\nimport { WrI18n } from './i18n';\nimport { type WrI18nParams } from './i18n-config';\n\n/**\n * Translate a key. Impure — re-evaluates on every CD so locale switches\n * and catalog loads propagate without a manual refresh.\n *\n * @example\n * ```html\n * <h1>{{ 'home.title' | wrT }}</h1>\n * <p>{{ 'greeting' | wrT: { name: user().name } }}</p>\n * <button>{{ 'forms.save' | wrT: null : 'checkout' }}</button>\n * ```\n */\n// eslint-disable-next-line @angular-eslint/no-pipe-impure -- intentional: locale + catalog updates must propagate even when args are stable\n@Pipe({ name: 'wrT', pure: false })\nexport class WrTPipe implements PipeTransform {\n private readonly i18n = inject(WrI18n);\n\n transform(key: string, params?: WrI18nParams | null, scope?: string): string {\n return this.i18n.t(key, params ?? undefined, scope);\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 { Directive, ElementRef, computed, effect, inject, input } from '@angular/core';\n\nimport { WrI18n } from './i18n';\nimport { type WrI18nParams } from './i18n-config';\n\n/**\n * Attribute directive that sets `textContent` of its host to the translation\n * of the bound key. Reactive — re-renders on locale + catalog changes.\n *\n * @example\n * ```html\n * <span [wrT]=\"'home.title'\"></span>\n * <span [wrT]=\"'greeting'\" [wrTParams]=\"{ name: user().name }\"></span>\n * <span [wrT]=\"'forms.save'\" wrTScope=\"checkout\"></span>\n * ```\n */\n@Directive({\n selector: '[wrT]',\n})\nexport class WrTDirective {\n /** Translation key. Required. */\n readonly wrT = input.required<string>();\n\n /** Interpolation params for `{{name}}` tokens. */\n readonly wrTParams = input<WrI18nParams | null>(null);\n\n /** Optional scope — scoped lookup first, then root fallback. */\n readonly wrTScope = input<string | null>(null);\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly i18n = inject(WrI18n);\n\n private readonly text = computed(() =>\n this.i18n.t(this.wrT(), this.wrTParams() ?? undefined, this.wrTScope() ?? undefined)\n );\n\n constructor() {\n effect(() => {\n this.host.nativeElement.textContent = this.text();\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 { type Observable, of } from 'rxjs';\n\nimport { type WrI18nCatalog } from '../i18n-config';\nimport { type WrI18nLoader } from '../i18n-loader';\n\n/** Root catalogs keyed by locale: `{ en: catalog, ru: catalog, … }`. */\nexport type WrI18nStaticCatalogs = Readonly<Record<string, WrI18nCatalog>>;\n\n/** Per-scope catalogs: `{ checkout: { en: catalog, ru: catalog }, … }`. */\nexport type WrI18nStaticScopedCatalogs = Readonly<Record<string, WrI18nStaticCatalogs>>;\n\n/**\n * Loader for catalogs already in memory (imported from JSON or generated).\n *\n * - Pass `catalogs` for the root catalog of every locale.\n * - Pass `scopes` if you ship per-feature catalogs the loader should serve\n * to `WrI18n.registerScope(name)` calls.\n *\n * Missing locale or scope resolves to `{}` — the missing-key handler then\n * decides what to render.\n */\nexport class WrI18nStaticLoader implements WrI18nLoader {\n constructor(\n private readonly catalogs: WrI18nStaticCatalogs,\n private readonly scopes: WrI18nStaticScopedCatalogs = {}\n ) {}\n\n load(locale: string, scope: string | null): Observable<WrI18nCatalog> {\n if (scope === null) return of(this.catalogs[locale] ?? {});\n return of(this.scopes[scope]?.[locale] ?? {});\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 { HttpClient } from '@angular/common/http';\nimport { inject } from '@angular/core';\n\nimport { type Observable, catchError, of } from 'rxjs';\n\nimport { type WrI18nCatalog } from '../i18n-config';\nimport { type WrI18nLoader } from '../i18n-loader';\n\n/** Configuration for {@link WrI18nHttpLoader}. */\nexport interface WrI18nHttpLoaderConfig {\n /**\n * URL template. Tokens:\n * - `{locale}` — required, BCP-47 tag (e.g. `en`).\n * - `{scope}` — optional, scope name. Use the `rootPath` if you need a\n * different path entirely for the root catalog.\n *\n * @example\n * ```ts\n * { path: '/assets/i18n/{locale}.json' } // root only\n * { path: '/assets/i18n/{scope}/{locale}.json',\n * rootPath: '/assets/i18n/{locale}.json' } // root + scopes\n * ```\n */\n readonly path: string;\n /** Path template for the root catalog. Defaults to `path`. */\n readonly rootPath?: string;\n}\n\n/**\n * Loader that fetches catalogs via `HttpClient`. Failures resolve to `{}`\n * (logged once at the service layer) so a missing file never throws into\n * user code.\n */\nexport class WrI18nHttpLoader implements WrI18nLoader {\n private readonly http = inject(HttpClient);\n\n constructor(private readonly config: WrI18nHttpLoaderConfig) {}\n\n load(locale: string, scope: string | null): Observable<WrI18nCatalog> {\n const template = scope === null ? (this.config.rootPath ?? this.config.path) : this.config.path;\n const url = template.replace('{locale}', locale).replace('{scope}', scope ?? '');\n return this.http.get<WrI18nCatalog>(url).pipe(catchError(() => of<WrI18nCatalog>({})));\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type EnvironmentProviders, type Provider, makeEnvironmentProviders } from '@angular/core';\n\nimport { WR_I18N_BASE_CATALOGS, type WrI18nBaseCatalogs } from './i18n-base-catalogs';\nimport { DEFAULT_WR_I18N_CONFIG, WR_I18N_CONFIG, type WrI18nConfig, type WrI18nConfigResolved } from './i18n-config';\nimport { WR_I18N_LOADER, type WrI18nLoader } from './i18n-loader';\nimport { WrI18nHttpLoader, type WrI18nHttpLoaderConfig } from './loaders/http-loader';\nimport {\n WrI18nStaticLoader,\n type WrI18nStaticCatalogs,\n type WrI18nStaticScopedCatalogs,\n} from './loaders/static-loader';\n\n/**\n * Configure {@link WrI18n}. Either pass a `loader` directly, or use the\n * `provideWrI18nHttpLoader` / `provideWrI18nStaticLoader` helpers in the\n * same providers array.\n */\nexport interface ProvideWrI18nOptions extends WrI18nConfig {\n readonly loader?: WrI18nLoader;\n}\n\nexport function provideWrI18n(options: ProvideWrI18nOptions = {}): EnvironmentProviders {\n const { loader, ...config } = options;\n const merged: WrI18nConfigResolved = { ...DEFAULT_WR_I18N_CONFIG, ...config };\n\n const providers: Provider[] = [{ provide: WR_I18N_CONFIG, useValue: merged }];\n if (loader) providers.push({ provide: WR_I18N_LOADER, useValue: loader });\n\n return makeEnvironmentProviders(providers);\n}\n\n/**\n * Provide a static loader — catalogs already in memory.\n *\n * @param catalogs Root catalogs keyed by locale.\n * @param scopes Optional per-scope catalogs served to `registerScope(name)`.\n */\nexport function provideWrI18nStaticLoader(\n catalogs: WrI18nStaticCatalogs,\n scopes?: WrI18nStaticScopedCatalogs\n): Provider {\n return { provide: WR_I18N_LOADER, useValue: new WrI18nStaticLoader(catalogs, scopes) };\n}\n\n/**\n * Register catalogs consulted *underneath* the loader — this is how ngwr's own\n * built-in strings survive when you supply your own catalogs.\n *\n * A loader replaces the catalog for a locale instead of extending it, so a JSON\n * file holding only your keys leaves every built-in label on its hardcoded\n * English fallback, with nothing logged. One line fixes it, on either loader:\n *\n * @example\n * ```ts\n * import { wrRu } from 'ngwr/i18n/ru';\n *\n * providers: [\n * provideWrI18n({ defaultLocale: 'ru', availableLocales: ['ru'] }),\n * provideWrI18nBaseCatalogs({ ru: wrRu }),\n * provideWrI18nHttpLoader({ path: '/assets/i18n/{locale}.json' }),\n * ]\n * ```\n *\n * Pass only the locales you ship — the ones you leave out stay out of the\n * bundle. Your own keys always win; this is a floor, not an override.\n */\nexport function provideWrI18nBaseCatalogs(catalogs: WrI18nBaseCatalogs): Provider {\n return { provide: WR_I18N_BASE_CATALOGS, useValue: catalogs, multi: true };\n}\n\n/** Provide an HTTP loader — catalogs fetched from URLs. Requires `provideHttpClient`. */\nexport function provideWrI18nHttpLoader(config: WrI18nHttpLoaderConfig): Provider {\n return { provide: WR_I18N_LOADER, useFactory: () => new WrI18nHttpLoader(config) };\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type Signal, computed, inject, signal } from '@angular/core';\n\nimport { WrI18n, wrInterpolate } from './i18n';\nimport type { WrI18nParams } from './i18n-config';\n\n/**\n * Helper for component default text. Returns a reactive string that:\n *\n * 1. Prefers the consumer's override (an `input<string | null>()` value).\n * 2. Falls back to the i18n catalog under `key` — re-renders on locale\n * change so a switcher updates the DOM live.\n * 3. Falls back to `fallback` when no `WrI18n` is provided OR the catalog\n * is missing the key.\n *\n * Components don't need a hard dependency on `provideWrI18n` — without it,\n * the fallback is returned and everything still works.\n *\n * @example\n * ```ts\n * readonly emptyText = input<string | null>(null);\n * protected readonly empty = useI18nText(this.emptyText, 'table.empty', 'No data');\n *\n * // In template: <div>{{ empty() }}</div>\n * ```\n */\nexport function useI18nText(binding: Signal<string | null | undefined>, key: string, fallback: string): Signal<string> {\n const i18n = inject(WrI18n, { optional: true });\n if (!i18n) {\n return computed(() => {\n const v = binding();\n return v ?? fallback;\n });\n }\n\n return computed(() => {\n const v = binding();\n if (v !== null && v !== undefined && v !== '') return v;\n const translated = i18n.t(key);\n // Default missing handler returns the key itself — treat that as \"missing\"\n // and use our explicit fallback.\n return translated === key ? fallback : translated;\n });\n}\n\n/**\n * {@link useI18nText} without a binding to forward — for a built-in string the\n * component exposes no override input for. Same reactivity, same fallback\n * rules; the only difference is the missing first argument.\n *\n * It used to return a plain `string`, resolved once at injection time, and\n * that was wrong in the ordinary case rather than an edge one: `WrI18n` writes\n * every loader-backed catalog from `firstValueFrom(...).then(...)`, so even a\n * static loader serving an object literal lands a microtask AFTER the first\n * change-detection pass. Every component built in that pass read an empty\n * catalog and froze its English fallback for the life of the app — in one\n * `<wr-date-picker>` the field said \"Открыть календарь\" and the calendar\n * button beside it \"Open calendar\", off the same key.\n */\nexport function readI18nText(key: string, fallback: string): Signal<string> {\n const i18n = inject(WrI18n, { optional: true });\n if (!i18n) return computed(() => fallback);\n\n return computed(() => {\n const v = i18n.t(key);\n return v === key ? fallback : v;\n });\n}\n\n/** Tiny shim — useful when the caller doesn't have an `input` to forward. */\nexport function nullSignal(): Signal<null> {\n return signal(null).asReadonly();\n}\n\n/**\n * Build a per-call formatter for parametrized labels (e.g. ARIA labels\n * that interpolate a per-row value: `\"Remove {{label}}\"`). Returns a\n * function the template calls with the params for the current item.\n *\n * The returned function:\n * - Re-resolves the i18n catalog on every call (live locale switches just\n * work — re-rendered templates pick up new strings).\n * - Falls back to the literal `fallback` template with the same\n * `{{name}}` syntax when no `WrI18n` provider is present, or when the\n * key is missing.\n *\n * Use this for labels that vary per row. For static labels, prefer\n * {@link useI18nText} or {@link readI18nText}.\n *\n * @example\n * ```ts\n * protected readonly removeChipLabel = useI18nFormatter('select.removeItem', 'Remove {{label}}');\n *\n * // In template:\n * // [attr.aria-label]=\"removeChipLabel({ label: chip.label })\"\n * ```\n */\nexport function useI18nFormatter(key: string, fallback: string): (params?: WrI18nParams) => string {\n const i18n = inject(WrI18n, { optional: true });\n if (!i18n) {\n return (params?: WrI18nParams) => wrInterpolate(fallback, params);\n }\n return (params?: WrI18nParams) => {\n const v = i18n.t(key, params);\n return v === key ? wrInterpolate(fallback, params) : v;\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/fesm2022/ngwr-icon.mjs
CHANGED
|
@@ -53,10 +53,10 @@ class WrIconRegistry {
|
|
|
53
53
|
}
|
|
54
54
|
return map;
|
|
55
55
|
}
|
|
56
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
57
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.
|
|
56
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrIconRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
57
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrIconRegistry });
|
|
58
58
|
}
|
|
59
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
59
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrIconRegistry, decorators: [{
|
|
60
60
|
type: Injectable
|
|
61
61
|
}] });
|
|
62
62
|
|
|
@@ -129,10 +129,10 @@ class WrIcon {
|
|
|
129
129
|
this.host.nativeElement.querySelector('svg')?.setAttribute('aria-hidden', 'true');
|
|
130
130
|
});
|
|
131
131
|
}
|
|
132
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
133
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
132
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrIcon, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
133
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.2", type: WrIcon, isStandalone: true, selector: "wr-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-icon": "name()" }, classAttribute: "wr-icon" }, ngImport: i0, template: '', isInline: true, encapsulation: i0.ViewEncapsulation.None });
|
|
134
134
|
}
|
|
135
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
135
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrIcon, decorators: [{
|
|
136
136
|
type: Component,
|
|
137
137
|
args: [{
|
|
138
138
|
selector: 'wr-icon',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
|
|
1
|
+
import { TestKey, ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @license
|
|
@@ -6,6 +6,12 @@ import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
|
|
|
6
6
|
* Use of this source code is governed by an MIT-style license that can be
|
|
7
7
|
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
8
8
|
*/
|
|
9
|
+
const ARROWS = {
|
|
10
|
+
left: TestKey.LEFT_ARROW,
|
|
11
|
+
right: TestKey.RIGHT_ARROW,
|
|
12
|
+
up: TestKey.UP_ARROW,
|
|
13
|
+
down: TestKey.DOWN_ARROW,
|
|
14
|
+
};
|
|
9
15
|
/**
|
|
10
16
|
* Test harness for `<wr-image-cropper>`.
|
|
11
17
|
*
|
|
@@ -15,13 +21,16 @@ import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
|
|
|
15
21
|
* `naturalWidth`, converts between display and source pixels, and moves the crop
|
|
16
22
|
* window with pointer deltas. jsdom measures nothing — every rect is 0×0 and
|
|
17
23
|
* `naturalWidth` is 0 — so an image never "loads", the crop UI never renders, and a
|
|
18
|
-
* synthetic drag divides by zero. There is deliberately no
|
|
19
|
-
*
|
|
24
|
+
* synthetic drag divides by zero. There is deliberately no drag method: it would
|
|
25
|
+
* write `NaN` and report success.
|
|
20
26
|
*
|
|
21
27
|
* What a spec CAN do is give the image a box and a natural size itself, then call
|
|
22
28
|
* {@link dispatchImageLoad} — the two numbers `onImageLoad` reads are the whole
|
|
23
29
|
* stub, and from there {@link getCropBox} and {@link getHandles} are real answers,
|
|
24
|
-
* because the component writes the crop window's geometry as inline pixels.
|
|
30
|
+
* because the component writes the crop window's geometry as inline pixels. The
|
|
31
|
+
* KEYBOARD path needs the same stub to get the crop UI up, but nothing beyond it:
|
|
32
|
+
* its step is a constant rather than a pointer delta, so {@link pressArrow} lands
|
|
33
|
+
* on a number a spec can name.
|
|
25
34
|
*
|
|
26
35
|
* ```ts
|
|
27
36
|
* // In your spec, before dispatching the load:
|
|
@@ -31,11 +40,13 @@ import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
|
|
|
31
40
|
* Object.defineProperty(img, 'naturalHeight', { value: 800 });
|
|
32
41
|
* ```
|
|
33
42
|
*
|
|
34
|
-
* **There is
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
43
|
+
* **There is nothing to click.** The cropper renders no buttons: the result
|
|
44
|
+
* arrives on the `(cropped)` output, which emits a `Blob` after each drag end or
|
|
45
|
+
* each run of arrow keys, and a consumer who wants a one-off read calls `toBlob()` /
|
|
46
|
+
* `toDataUrl()` — or `cropRect()` for the geometry — on the component through a
|
|
47
|
+
* `viewChild`. An output and three instance members: component API rather than DOM,
|
|
48
|
+
* so all of it is outside what a harness can reach at all, which is worth knowing
|
|
49
|
+
* rather than discovering.
|
|
39
50
|
*
|
|
40
51
|
* @example
|
|
41
52
|
* ```ts
|
|
@@ -138,6 +149,39 @@ class WrImageCropperHarness extends ComponentHarness {
|
|
|
138
149
|
async hasBackdrop() {
|
|
139
150
|
return (await this.locatorForOptional('.wr-image-cropper__backdrop')()) !== null;
|
|
140
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Press an arrow key on the crop window — one display pixel, or ten with
|
|
154
|
+
* `shift`; `alt` resizes from the east / south edge instead of moving.
|
|
155
|
+
*
|
|
156
|
+
* Sent to the crop window, where the component listens, so it does not depend on
|
|
157
|
+
* jsdom having managed to focus anything. `sendKeys` fires keyup as well as
|
|
158
|
+
* keydown, which is what makes `(cropped)` emit — the component treats a run of
|
|
159
|
+
* arrows as one gesture, the way a drag is one.
|
|
160
|
+
*/
|
|
161
|
+
async pressArrow(arrow, modifiers = {}) {
|
|
162
|
+
const window_ = await this.requireReady('pressArrow');
|
|
163
|
+
const keys = ARROWS[arrow];
|
|
164
|
+
await (modifiers.alt || modifiers.shift ? window_.sendKeys(modifiers, keys) : window_.sendKeys(keys));
|
|
165
|
+
}
|
|
166
|
+
/** The crop window's accessible name. Comes from the catalog. */
|
|
167
|
+
async getCropWindowLabel() {
|
|
168
|
+
return (await this.requireReady('getCropWindowLabel')).getAttribute('aria-label');
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The last thing the live region announced — the crop rect in SOURCE pixels,
|
|
172
|
+
* as `x, y, width × height`.
|
|
173
|
+
*
|
|
174
|
+
* Empty until a key has moved the crop: a pointer drag deliberately says nothing
|
|
175
|
+
* (it would flood the region and the sighted user can see it), and the initial
|
|
176
|
+
* crop is not news. Two questions, not one — {@link getCropBox} reads the window's
|
|
177
|
+
* DISPLAY geometry, and a component that moved the box without announcing it
|
|
178
|
+
* would still pass that one.
|
|
179
|
+
*/
|
|
180
|
+
async getAnnouncement() {
|
|
181
|
+
await this.requireReady('getAnnouncement');
|
|
182
|
+
const status = await this.locatorForOptional('[role="status"]')();
|
|
183
|
+
return status ? status.text() : '';
|
|
184
|
+
}
|
|
141
185
|
async requireReady(method) {
|
|
142
186
|
const window_ = await this.cropWindow();
|
|
143
187
|
if (window_)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-image-cropper-testing.mjs","sources":["../../../projects/lib/image-cropper/testing/wr-image-cropper-harness.ts","../../../projects/lib/image-cropper/testing/ngwr-image-cropper-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, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrCropHandle } from 'ngwr/image-cropper';\n\nimport type { WrImageCropperHarnessFilters } from './interfaces';\n\n/** Where the crop window sits over the DISPLAYED image, in CSS pixels. */\nexport interface WrCropWindowBox {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Test harness for `<wr-image-cropper>`.\n *\n * **This is the smallest honest surface of any harness here, and the reason is worth\n * reading before you reach for a method that is not on it.** Almost everything the\n * cropper does is measured: it reads the `<img>`'s rendered box and its\n * `naturalWidth`, converts between display and source pixels, and moves the crop\n * window with pointer deltas. jsdom measures nothing — every rect is 0×0 and\n * `naturalWidth` is 0 — so an image never \"loads\", the crop UI never renders, and a\n * synthetic drag divides by zero. There is deliberately no `moveCrop()` or\n * `resizeCrop()`: either would write `NaN` and report success.\n *\n * What a spec CAN do is give the image a box and a natural size itself, then call\n * {@link dispatchImageLoad} — the two numbers `onImageLoad` reads are the whole\n * stub, and from there {@link getCropBox} and {@link getHandles} are real answers,\n * because the component writes the crop window's geometry as inline pixels.\n *\n * ```ts\n * // In your spec, before dispatching the load:\n * const img = fixture.nativeElement.querySelector('.wr-image-cropper__image');\n * img.getBoundingClientRect = () => ({ width: 400, height: 400, x: 0, y: 0 }) as DOMRect;\n * Object.defineProperty(img, 'naturalWidth', { value: 800 });\n * Object.defineProperty(img, 'naturalHeight', { value: 800 });\n * ```\n *\n * **There is also nothing to click.** The cropper renders no buttons: a consumer\n * takes the result by calling `crop()` on the component through a `viewChild`, and\n * reads `cropRect()` the same way. Those are component API rather than DOM, so they\n * are outside what a harness can reach at all — which is worth knowing rather than\n * discovering.\n *\n * @example\n * ```ts\n * const cropper = await loader.getHarness(WrImageCropperHarness);\n *\n * expect(await cropper.isEmpty()).toBe(false);\n * expect(await cropper.isReady()).toBe(false); // nothing measured yet\n *\n * await cropper.dispatchImageLoad();\n * expect(await cropper.getCropBox()).toEqual({ x: 80, y: 80, width: 240, height: 240 });\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrImageCropperHarness extends ComponentHarness {\n static hostSelector = 'wr-image-cropper';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrImageCropperHarnessFilters = {}): HarnessPredicate<WrImageCropperHarness> {\n return new HarnessPredicate(WrImageCropperHarness, options).addOption(\n 'empty',\n options.empty,\n async (harness, empty) => (await harness.isEmpty()) === empty\n );\n }\n\n private readonly image = this.locatorForOptional('.wr-image-cropper__image');\n private readonly cropWindow = this.locatorForOptional('.wr-image-cropper__window');\n\n /** Whether the cropper has no source at all, and is showing its placeholder. */\n async isEmpty(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-image-cropper__empty')()) !== null;\n }\n\n /** The placeholder's text, or `null` when there is an image. Comes from the catalog. */\n async getEmptyText(): Promise<string | null> {\n const empty = await this.locatorForOptional('.wr-image-cropper__empty')();\n return empty ? empty.text() : null;\n }\n\n /**\n * Whether the crop UI is up.\n *\n * Not the same question as {@link isEmpty}: a cropper with a perfectly good `src`\n * shows nothing but the image until it has been MEASURED, because the window's size\n * is a fraction of the rendered box. In a browser that happens on load; in a unit\n * test it happens when the spec stubs the box and calls\n * {@link dispatchImageLoad}.\n */\n async isReady(): Promise<boolean> {\n return (await this.cropWindow()) !== null;\n }\n\n /**\n * What the `<img>` is pointing at.\n *\n * A `File` or `Blob` source becomes an object URL, so this is `blob:…` for those\n * and the plain string for a string `src`. Comparing the two is not useful; asking\n * whether it CHANGED is.\n */\n async getImageSrc(): Promise<string | null> {\n const image = await this.image();\n return image ? image.getProperty<string>('src') : null;\n }\n\n /**\n * Fire the image's `load` event.\n *\n * The event, not a real load: jsdom fetches nothing, and the handler reads\n * `getBoundingClientRect()` and `naturalWidth` off the element. Stub those first —\n * see this class's docs — or the crop window will still not render and\n * {@link isReady} will stay `false`, which is exactly what a real cropper does with\n * a zero-sized image.\n */\n async dispatchImageLoad(): Promise<void> {\n const image = await this.image();\n if (!image) {\n throw new Error(\n 'WrImageCropperHarness.dispatchImageLoad(): there is no image — the cropper has no `src`, so it is ' +\n 'showing its empty state.'\n );\n }\n await image.dispatchEvent('load');\n }\n\n /**\n * Where the crop window sits over the displayed image, in CSS pixels.\n *\n * From the inline styles the component writes — a measured box would be 0×0 — and\n * in DISPLAY pixels, which is not what a consumer receives: `cropRect()` converts\n * to the image's natural pixels, and that conversion is the component's own contract\n * rather than something the DOM shows.\n */\n async getCropBox(): Promise<WrCropWindowBox> {\n const window_ = await this.requireReady('getCropBox');\n return {\n x: Number.parseFloat(await window_.getCssValue('left')),\n y: Number.parseFloat(await window_.getCssValue('top')),\n width: Number.parseFloat(await window_.getCssValue('width')),\n height: Number.parseFloat(await window_.getCssValue('height')),\n };\n }\n\n /** The eight resize handles, by their compass suffix. Throws before the crop UI is up. */\n async getHandles(): Promise<WrCropHandle[]> {\n await this.requireReady('getHandles');\n const handles = await this.locatorForAll('.wr-image-cropper__handle')();\n\n return Promise.all(\n handles.map(async handle => {\n const classes = (await handle.getAttribute('class')) ?? '';\n const found = classes.split(/\\s+/).find(name => name.startsWith('wr-image-cropper__handle--'));\n return (found?.slice('wr-image-cropper__handle--'.length) ?? '') as WrCropHandle;\n })\n );\n }\n\n /** Whether the dimming backdrop with the cut-out is drawn. */\n async hasBackdrop(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-image-cropper__backdrop')()) !== null;\n }\n\n private async requireReady(method: string): Promise<TestElement> {\n const window_ = await this.cropWindow();\n if (window_) return window_;\n\n throw new Error(\n `WrImageCropperHarness.${method}(): the crop window is not rendered. The cropper sizes it from the ` +\n \"image's MEASURED box, and a unit test has none — give the <img> a `getBoundingClientRect` and a \" +\n '`naturalWidth`, then call dispatchImageLoad().'\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-image-cropper-testing.mjs","sources":["../../../projects/lib/image-cropper/testing/wr-image-cropper-harness.ts","../../../projects/lib/image-cropper/testing/ngwr-image-cropper-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, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrCropHandle } from 'ngwr/image-cropper';\n\nimport type { WrImageCropperHarnessFilters } from './interfaces';\n\nconst ARROWS = {\n left: TestKey.LEFT_ARROW,\n right: TestKey.RIGHT_ARROW,\n up: TestKey.UP_ARROW,\n down: TestKey.DOWN_ARROW,\n} as const;\n\n/** The four directions the crop window's keyboard model understands. */\nexport type WrCropArrow = keyof typeof ARROWS;\n\n/** Where the crop window sits over the DISPLAYED image, in CSS pixels. */\nexport interface WrCropWindowBox {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Test harness for `<wr-image-cropper>`.\n *\n * **This is the smallest honest surface of any harness here, and the reason is worth\n * reading before you reach for a method that is not on it.** Almost everything the\n * cropper does is measured: it reads the `<img>`'s rendered box and its\n * `naturalWidth`, converts between display and source pixels, and moves the crop\n * window with pointer deltas. jsdom measures nothing — every rect is 0×0 and\n * `naturalWidth` is 0 — so an image never \"loads\", the crop UI never renders, and a\n * synthetic drag divides by zero. There is deliberately no drag method: it would\n * write `NaN` and report success.\n *\n * What a spec CAN do is give the image a box and a natural size itself, then call\n * {@link dispatchImageLoad} — the two numbers `onImageLoad` reads are the whole\n * stub, and from there {@link getCropBox} and {@link getHandles} are real answers,\n * because the component writes the crop window's geometry as inline pixels. The\n * KEYBOARD path needs the same stub to get the crop UI up, but nothing beyond it:\n * its step is a constant rather than a pointer delta, so {@link pressArrow} lands\n * on a number a spec can name.\n *\n * ```ts\n * // In your spec, before dispatching the load:\n * const img = fixture.nativeElement.querySelector('.wr-image-cropper__image');\n * img.getBoundingClientRect = () => ({ width: 400, height: 400, x: 0, y: 0 }) as DOMRect;\n * Object.defineProperty(img, 'naturalWidth', { value: 800 });\n * Object.defineProperty(img, 'naturalHeight', { value: 800 });\n * ```\n *\n * **There is nothing to click.** The cropper renders no buttons: the result\n * arrives on the `(cropped)` output, which emits a `Blob` after each drag end or\n * each run of arrow keys, and a consumer who wants a one-off read calls `toBlob()` /\n * `toDataUrl()` — or `cropRect()` for the geometry — on the component through a\n * `viewChild`. An output and three instance members: component API rather than DOM,\n * so all of it is outside what a harness can reach at all, which is worth knowing\n * rather than discovering.\n *\n * @example\n * ```ts\n * const cropper = await loader.getHarness(WrImageCropperHarness);\n *\n * expect(await cropper.isEmpty()).toBe(false);\n * expect(await cropper.isReady()).toBe(false); // nothing measured yet\n *\n * await cropper.dispatchImageLoad();\n * expect(await cropper.getCropBox()).toEqual({ x: 80, y: 80, width: 240, height: 240 });\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrImageCropperHarness extends ComponentHarness {\n static hostSelector = 'wr-image-cropper';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrImageCropperHarnessFilters = {}): HarnessPredicate<WrImageCropperHarness> {\n return new HarnessPredicate(WrImageCropperHarness, options).addOption(\n 'empty',\n options.empty,\n async (harness, empty) => (await harness.isEmpty()) === empty\n );\n }\n\n private readonly image = this.locatorForOptional('.wr-image-cropper__image');\n private readonly cropWindow = this.locatorForOptional('.wr-image-cropper__window');\n\n /** Whether the cropper has no source at all, and is showing its placeholder. */\n async isEmpty(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-image-cropper__empty')()) !== null;\n }\n\n /** The placeholder's text, or `null` when there is an image. Comes from the catalog. */\n async getEmptyText(): Promise<string | null> {\n const empty = await this.locatorForOptional('.wr-image-cropper__empty')();\n return empty ? empty.text() : null;\n }\n\n /**\n * Whether the crop UI is up.\n *\n * Not the same question as {@link isEmpty}: a cropper with a perfectly good `src`\n * shows nothing but the image until it has been MEASURED, because the window's size\n * is a fraction of the rendered box. In a browser that happens on load; in a unit\n * test it happens when the spec stubs the box and calls\n * {@link dispatchImageLoad}.\n */\n async isReady(): Promise<boolean> {\n return (await this.cropWindow()) !== null;\n }\n\n /**\n * What the `<img>` is pointing at.\n *\n * A `File` or `Blob` source becomes an object URL, so this is `blob:…` for those\n * and the plain string for a string `src`. Comparing the two is not useful; asking\n * whether it CHANGED is.\n */\n async getImageSrc(): Promise<string | null> {\n const image = await this.image();\n return image ? image.getProperty<string>('src') : null;\n }\n\n /**\n * Fire the image's `load` event.\n *\n * The event, not a real load: jsdom fetches nothing, and the handler reads\n * `getBoundingClientRect()` and `naturalWidth` off the element. Stub those first —\n * see this class's docs — or the crop window will still not render and\n * {@link isReady} will stay `false`, which is exactly what a real cropper does with\n * a zero-sized image.\n */\n async dispatchImageLoad(): Promise<void> {\n const image = await this.image();\n if (!image) {\n throw new Error(\n 'WrImageCropperHarness.dispatchImageLoad(): there is no image — the cropper has no `src`, so it is ' +\n 'showing its empty state.'\n );\n }\n await image.dispatchEvent('load');\n }\n\n /**\n * Where the crop window sits over the displayed image, in CSS pixels.\n *\n * From the inline styles the component writes — a measured box would be 0×0 — and\n * in DISPLAY pixels, which is not what a consumer receives: `cropRect()` converts\n * to the image's natural pixels, and that conversion is the component's own contract\n * rather than something the DOM shows.\n */\n async getCropBox(): Promise<WrCropWindowBox> {\n const window_ = await this.requireReady('getCropBox');\n return {\n x: Number.parseFloat(await window_.getCssValue('left')),\n y: Number.parseFloat(await window_.getCssValue('top')),\n width: Number.parseFloat(await window_.getCssValue('width')),\n height: Number.parseFloat(await window_.getCssValue('height')),\n };\n }\n\n /** The eight resize handles, by their compass suffix. Throws before the crop UI is up. */\n async getHandles(): Promise<WrCropHandle[]> {\n await this.requireReady('getHandles');\n const handles = await this.locatorForAll('.wr-image-cropper__handle')();\n\n return Promise.all(\n handles.map(async handle => {\n const classes = (await handle.getAttribute('class')) ?? '';\n const found = classes.split(/\\s+/).find(name => name.startsWith('wr-image-cropper__handle--'));\n return (found?.slice('wr-image-cropper__handle--'.length) ?? '') as WrCropHandle;\n })\n );\n }\n\n /** Whether the dimming backdrop with the cut-out is drawn. */\n async hasBackdrop(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-image-cropper__backdrop')()) !== null;\n }\n\n /**\n * Press an arrow key on the crop window — one display pixel, or ten with\n * `shift`; `alt` resizes from the east / south edge instead of moving.\n *\n * Sent to the crop window, where the component listens, so it does not depend on\n * jsdom having managed to focus anything. `sendKeys` fires keyup as well as\n * keydown, which is what makes `(cropped)` emit — the component treats a run of\n * arrows as one gesture, the way a drag is one.\n */\n async pressArrow(arrow: WrCropArrow, modifiers: { alt?: boolean; shift?: boolean } = {}): Promise<void> {\n const window_ = await this.requireReady('pressArrow');\n const keys = ARROWS[arrow];\n await (modifiers.alt || modifiers.shift ? window_.sendKeys(modifiers, keys) : window_.sendKeys(keys));\n }\n\n /** The crop window's accessible name. Comes from the catalog. */\n async getCropWindowLabel(): Promise<string | null> {\n return (await this.requireReady('getCropWindowLabel')).getAttribute('aria-label');\n }\n\n /**\n * The last thing the live region announced — the crop rect in SOURCE pixels,\n * as `x, y, width × height`.\n *\n * Empty until a key has moved the crop: a pointer drag deliberately says nothing\n * (it would flood the region and the sighted user can see it), and the initial\n * crop is not news. Two questions, not one — {@link getCropBox} reads the window's\n * DISPLAY geometry, and a component that moved the box without announcing it\n * would still pass that one.\n */\n async getAnnouncement(): Promise<string> {\n await this.requireReady('getAnnouncement');\n const status = await this.locatorForOptional('[role=\"status\"]')();\n return status ? status.text() : '';\n }\n\n private async requireReady(method: string): Promise<TestElement> {\n const window_ = await this.cropWindow();\n if (window_) return window_;\n\n throw new Error(\n `WrImageCropperHarness.${method}(): the crop window is not rendered. The cropper sizes it from the ` +\n \"image's MEASURED box, and a unit test has none — give the <img> a `getBoundingClientRect` and a \" +\n '`naturalWidth`, then call dispatchImageLoad().'\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|