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
|
@@ -211,10 +211,10 @@ class WrCompare {
|
|
|
211
211
|
inlineFraction(clientX, rect) {
|
|
212
212
|
return this.rtl() ? (rect.right - clientX) / rect.width : (clientX - rect.left) / rect.width;
|
|
213
213
|
}
|
|
214
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
215
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
214
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrCompare, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
215
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: WrCompare, isStandalone: true, selector: "wr-compare", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, showHandle: { classPropertyName: "showHandle", publicName: "showHandle", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, minPosition: { classPropertyName: "minPosition", publicName: "minPosition", isSignal: true, isRequired: false, transformFunction: null }, maxPosition: { classPropertyName: "maxPosition", publicName: "maxPosition", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { position: "positionChange" }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: "<div\n class=\"wr-compare__surface\"\n role=\"slider\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [attr.aria-orientation]=\"orientation()\"\n [attr.aria-valuemin]=\"minPosition()\"\n [attr.aria-valuemax]=\"maxPosition()\"\n [attr.aria-valuenow]=\"position()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (keydown)=\"onKeydown($event)\"\n>\n <div class=\"wr-compare__layers\">\n <div class=\"wr-compare__layer wr-compare__layer--before\">\n <ng-content select=\"[wrCompareBefore]\" />\n </div>\n <div class=\"wr-compare__layer wr-compare__layer--after\" [style.clip-path]=\"clipPath()\">\n <ng-content select=\"[wrCompareAfter]\" />\n </div>\n </div>\n\n <div\n class=\"wr-compare__divider\"\n [style.left.%]=\"orientation() === 'horizontal' ? dividerLeft() : null\"\n [style.top.%]=\"orientation() === 'vertical' ? position() : null\"\n aria-hidden=\"true\"\n >\n @if (showHandle()) {\n <span class=\"wr-compare__handle\">\n @if (orientation() === 'horizontal') {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path\n d=\"M5 4 L1 8 L5 12 M11 4 L15 8 L11 12\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n } @else {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path\n d=\"M4 5 L8 1 L12 5 M4 11 L8 15 L12 11\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n }\n </span>\n }\n </div>\n</div>\n", encapsulation: i0.ViewEncapsulation.None });
|
|
216
216
|
}
|
|
217
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
217
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrCompare, decorators: [{
|
|
218
218
|
type: Component,
|
|
219
219
|
args: [{ selector: 'wr-compare', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, template: "<div\n class=\"wr-compare__surface\"\n role=\"slider\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [attr.aria-orientation]=\"orientation()\"\n [attr.aria-valuemin]=\"minPosition()\"\n [attr.aria-valuemax]=\"maxPosition()\"\n [attr.aria-valuenow]=\"position()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (keydown)=\"onKeydown($event)\"\n>\n <div class=\"wr-compare__layers\">\n <div class=\"wr-compare__layer wr-compare__layer--before\">\n <ng-content select=\"[wrCompareBefore]\" />\n </div>\n <div class=\"wr-compare__layer wr-compare__layer--after\" [style.clip-path]=\"clipPath()\">\n <ng-content select=\"[wrCompareAfter]\" />\n </div>\n </div>\n\n <div\n class=\"wr-compare__divider\"\n [style.left.%]=\"orientation() === 'horizontal' ? dividerLeft() : null\"\n [style.top.%]=\"orientation() === 'vertical' ? position() : null\"\n aria-hidden=\"true\"\n >\n @if (showHandle()) {\n <span class=\"wr-compare__handle\">\n @if (orientation() === 'horizontal') {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path\n d=\"M5 4 L1 8 L5 12 M11 4 L15 8 L11 12\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n } @else {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path\n d=\"M4 5 L8 1 L12 5 M4 11 L8 15 L12 11\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n }\n </span>\n }\n </div>\n</div>\n" }]
|
|
220
220
|
}], ctorParameters: () => [], propDecorators: { position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }, { type: i0.Output, args: ["positionChange"] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], showHandle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHandle", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], minPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "minPosition", required: false }] }], maxPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxPosition", required: false }] }] } });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DOCUMENT } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { inject, Service } from '@angular/core';
|
|
3
|
+
import { inject, DestroyRef, Service } from '@angular/core';
|
|
4
4
|
import { WrPlatform } from 'ngwr/platform';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -41,6 +41,16 @@ class WrConfetti {
|
|
|
41
41
|
ctx = null;
|
|
42
42
|
particles = [];
|
|
43
43
|
rafId = null;
|
|
44
|
+
// Named, so it can be removed again. The listener used to be an inline arrow
|
|
45
|
+
// and the canvas was never taken back out of `document.body`, which is only
|
|
46
|
+
// harmless while this is the root singleton: a route or a component that
|
|
47
|
+
// lists `WrConfetti` in its own `providers` left one orphaned full-viewport
|
|
48
|
+
// canvas — and one listener still reallocating its backing store on every
|
|
49
|
+
// window resize — behind per destroyed injector.
|
|
50
|
+
onResize = () => this.resize();
|
|
51
|
+
constructor() {
|
|
52
|
+
inject(DestroyRef).onDestroy(() => this.teardown());
|
|
53
|
+
}
|
|
44
54
|
/** Spawn a confetti burst with the given (or default) options. */
|
|
45
55
|
fire(options = {}) {
|
|
46
56
|
if (!this.platform.isBrowser)
|
|
@@ -71,7 +81,22 @@ class WrConfetti {
|
|
|
71
81
|
this.canvas = canvas;
|
|
72
82
|
this.ctx = canvas.getContext('2d');
|
|
73
83
|
this.resize();
|
|
74
|
-
win.addEventListener('resize',
|
|
84
|
+
win.addEventListener('resize', this.onResize);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Give back the canvas and the listener `ensureCanvas()` took from the page,
|
|
88
|
+
* stop the frame loop, and reset the state both of them guard on so a `fire()`
|
|
89
|
+
* from a fresh injector rebuilds cleanly.
|
|
90
|
+
*/
|
|
91
|
+
teardown() {
|
|
92
|
+
if (this.rafId !== null)
|
|
93
|
+
cancelAnimationFrame(this.rafId);
|
|
94
|
+
this.rafId = null;
|
|
95
|
+
this.doc.defaultView?.removeEventListener('resize', this.onResize);
|
|
96
|
+
this.canvas?.remove();
|
|
97
|
+
this.canvas = null;
|
|
98
|
+
this.ctx = null;
|
|
99
|
+
this.particles = [];
|
|
75
100
|
}
|
|
76
101
|
resize() {
|
|
77
102
|
if (!this.canvas)
|
|
@@ -145,12 +170,12 @@ class WrConfetti {
|
|
|
145
170
|
this.rafId = null;
|
|
146
171
|
}
|
|
147
172
|
}
|
|
148
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
149
|
-
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.
|
|
173
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrConfetti, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
174
|
+
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.2", ngImport: i0, type: WrConfetti });
|
|
150
175
|
}
|
|
151
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
176
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: WrConfetti, decorators: [{
|
|
152
177
|
type: Service
|
|
153
|
-
}] });
|
|
178
|
+
}], ctorParameters: () => [] });
|
|
154
179
|
|
|
155
180
|
/**
|
|
156
181
|
* Generated bundle index. Do not edit.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-confetti.mjs","sources":["../../../projects/lib/confetti/wr-confetti.ts","../../../projects/lib/confetti/ngwr-confetti.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 { DOCUMENT } from '@angular/common';\nimport { Service, inject } from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\n\nimport type { WrConfettiOptions } from './interfaces';\n\nconst DEFAULTS: Required<WrConfettiOptions> = {\n count: 80,\n spread: 60,\n angle: 90,\n velocity: 12,\n gravity: 0.35,\n drag: 0.92,\n origin: { x: 0.5, y: 1 },\n colors: ['#3969e2', '#f51c6a', '#00a400', '#ffba00', '#fa383e'],\n size: 8,\n ttl: 180,\n};\n\ninterface Particle {\n x: number;\n y: number;\n vx: number;\n vy: number;\n rotation: number;\n rotationSpeed: number;\n color: string;\n size: number;\n ttl: number;\n}\n\n/**\n * Burst of celebratory confetti particles painted onto a single shared\n * full-viewport canvas. The canvas is created on first call and reused.\n *\n * @example\n * ```ts\n * const confetti = inject(WrConfetti);\n * confetti.fire();\n * confetti.fire({ count: 200, origin: { x: 0.25, y: 0.5 } });\n * ```\n *\n * @see https://ngwr.dev/animations/confetti\n */\n@Service()\nexport class WrConfetti {\n private readonly doc = inject(DOCUMENT);\n private readonly platform = inject(WrPlatform);\n\n private canvas: HTMLCanvasElement | null = null;\n private ctx: CanvasRenderingContext2D | null = null;\n private particles: Particle[] = [];\n private rafId: number | null = null;\n\n /** Spawn a confetti burst with the given (or default) options. */\n fire(options: WrConfettiOptions = {}): void {\n if (!this.platform.isBrowser) return;\n if (this.platform.prefersReducedMotion()) return;\n const opts: Required<WrConfettiOptions> = { ...DEFAULTS, ...options };\n this.ensureCanvas();\n this.spawn(opts);\n this.rafId ??= requestAnimationFrame(() => this.tick());\n }\n\n // Internals\n\n private ensureCanvas(): void {\n if (this.canvas) return;\n const win = this.doc.defaultView;\n if (!win) return;\n const canvas = this.doc.createElement('canvas');\n canvas.style.position = 'fixed';\n canvas.style.inset = '0';\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n canvas.style.pointerEvents = 'none';\n canvas.style.zIndex = '9999';\n canvas.setAttribute('aria-hidden', 'true');\n this.doc.body.appendChild(canvas);\n this.canvas = canvas;\n this.ctx = canvas.getContext('2d');\n this.resize();\n win.addEventListener('resize', () => this.resize());\n }\n\n private resize(): void {\n if (!this.canvas) return;\n const win = this.doc.defaultView;\n if (!win) return;\n const dpr = win.devicePixelRatio || 1;\n this.canvas.width = win.innerWidth * dpr;\n this.canvas.height = win.innerHeight * dpr;\n this.ctx?.setTransform(dpr, 0, 0, dpr, 0, 0);\n }\n\n private spawn(opts: Required<WrConfettiOptions>): void {\n const win = this.doc.defaultView;\n if (!win) return;\n const cx = opts.origin.x * win.innerWidth;\n const cy = opts.origin.y * win.innerHeight;\n const halfSpread = (opts.spread / 2) * (Math.PI / 180);\n for (let i = 0; i < opts.count; i++) {\n // Launch direction: 90° = up (default), 0° = right — same convention\n // as canvas-confetti. Jitter spreads the cone around it.\n const base = (-opts.angle * Math.PI) / 180;\n const angle = base + (Math.random() - 0.5) * 2 * halfSpread;\n const speed = opts.velocity * (0.6 + Math.random() * 0.8);\n this.particles.push({\n x: cx,\n y: cy,\n vx: Math.cos(angle) * speed,\n vy: Math.sin(angle) * speed,\n rotation: Math.random() * Math.PI * 2,\n rotationSpeed: (Math.random() - 0.5) * 0.3,\n color: opts.colors[Math.floor(Math.random() * opts.colors.length)],\n size: opts.size * (0.8 + Math.random() * 0.6),\n ttl: opts.ttl,\n });\n }\n }\n\n private tick(): void {\n const ctx = this.ctx;\n const win = this.doc.defaultView;\n if (!ctx || !win) {\n this.rafId = null;\n return;\n }\n ctx.clearRect(0, 0, win.innerWidth, win.innerHeight);\n const next: Particle[] = [];\n for (const p of this.particles) {\n p.vx *= DEFAULTS.drag;\n p.vy = p.vy * DEFAULTS.drag + DEFAULTS.gravity;\n p.x += p.vx;\n p.y += p.vy;\n p.rotation += p.rotationSpeed;\n p.ttl -= 1;\n if (p.ttl <= 0 || p.y > win.innerHeight + 50) continue;\n next.push(p);\n ctx.save();\n ctx.translate(p.x, p.y);\n ctx.rotate(p.rotation);\n ctx.globalAlpha = Math.min(1, p.ttl / 60);\n ctx.fillStyle = p.color;\n ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size * 0.6);\n ctx.restore();\n }\n this.particles = next;\n if (this.particles.length > 0) {\n this.rafId = requestAnimationFrame(() => this.tick());\n } else {\n this.rafId = null;\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-confetti.mjs","sources":["../../../projects/lib/confetti/wr-confetti.ts","../../../projects/lib/confetti/ngwr-confetti.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 { DOCUMENT } from '@angular/common';\nimport { DestroyRef, Service, inject } from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\n\nimport type { WrConfettiOptions } from './interfaces';\n\nconst DEFAULTS: Required<WrConfettiOptions> = {\n count: 80,\n spread: 60,\n angle: 90,\n velocity: 12,\n gravity: 0.35,\n drag: 0.92,\n origin: { x: 0.5, y: 1 },\n colors: ['#3969e2', '#f51c6a', '#00a400', '#ffba00', '#fa383e'],\n size: 8,\n ttl: 180,\n};\n\ninterface Particle {\n x: number;\n y: number;\n vx: number;\n vy: number;\n rotation: number;\n rotationSpeed: number;\n color: string;\n size: number;\n ttl: number;\n}\n\n/**\n * Burst of celebratory confetti particles painted onto a single shared\n * full-viewport canvas. The canvas is created on first call and reused.\n *\n * @example\n * ```ts\n * const confetti = inject(WrConfetti);\n * confetti.fire();\n * confetti.fire({ count: 200, origin: { x: 0.25, y: 0.5 } });\n * ```\n *\n * @see https://ngwr.dev/animations/confetti\n */\n@Service()\nexport class WrConfetti {\n private readonly doc = inject(DOCUMENT);\n private readonly platform = inject(WrPlatform);\n\n private canvas: HTMLCanvasElement | null = null;\n private ctx: CanvasRenderingContext2D | null = null;\n private particles: Particle[] = [];\n private rafId: number | null = null;\n\n // Named, so it can be removed again. The listener used to be an inline arrow\n // and the canvas was never taken back out of `document.body`, which is only\n // harmless while this is the root singleton: a route or a component that\n // lists `WrConfetti` in its own `providers` left one orphaned full-viewport\n // canvas — and one listener still reallocating its backing store on every\n // window resize — behind per destroyed injector.\n private readonly onResize = (): void => this.resize();\n\n constructor() {\n inject(DestroyRef).onDestroy(() => this.teardown());\n }\n\n /** Spawn a confetti burst with the given (or default) options. */\n fire(options: WrConfettiOptions = {}): void {\n if (!this.platform.isBrowser) return;\n if (this.platform.prefersReducedMotion()) return;\n const opts: Required<WrConfettiOptions> = { ...DEFAULTS, ...options };\n this.ensureCanvas();\n this.spawn(opts);\n this.rafId ??= requestAnimationFrame(() => this.tick());\n }\n\n // Internals\n\n private ensureCanvas(): void {\n if (this.canvas) return;\n const win = this.doc.defaultView;\n if (!win) return;\n const canvas = this.doc.createElement('canvas');\n canvas.style.position = 'fixed';\n canvas.style.inset = '0';\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n canvas.style.pointerEvents = 'none';\n canvas.style.zIndex = '9999';\n canvas.setAttribute('aria-hidden', 'true');\n this.doc.body.appendChild(canvas);\n this.canvas = canvas;\n this.ctx = canvas.getContext('2d');\n this.resize();\n win.addEventListener('resize', this.onResize);\n }\n\n /**\n * Give back the canvas and the listener `ensureCanvas()` took from the page,\n * stop the frame loop, and reset the state both of them guard on so a `fire()`\n * from a fresh injector rebuilds cleanly.\n */\n private teardown(): void {\n if (this.rafId !== null) cancelAnimationFrame(this.rafId);\n this.rafId = null;\n this.doc.defaultView?.removeEventListener('resize', this.onResize);\n this.canvas?.remove();\n this.canvas = null;\n this.ctx = null;\n this.particles = [];\n }\n\n private resize(): void {\n if (!this.canvas) return;\n const win = this.doc.defaultView;\n if (!win) return;\n const dpr = win.devicePixelRatio || 1;\n this.canvas.width = win.innerWidth * dpr;\n this.canvas.height = win.innerHeight * dpr;\n this.ctx?.setTransform(dpr, 0, 0, dpr, 0, 0);\n }\n\n private spawn(opts: Required<WrConfettiOptions>): void {\n const win = this.doc.defaultView;\n if (!win) return;\n const cx = opts.origin.x * win.innerWidth;\n const cy = opts.origin.y * win.innerHeight;\n const halfSpread = (opts.spread / 2) * (Math.PI / 180);\n for (let i = 0; i < opts.count; i++) {\n // Launch direction: 90° = up (default), 0° = right — same convention\n // as canvas-confetti. Jitter spreads the cone around it.\n const base = (-opts.angle * Math.PI) / 180;\n const angle = base + (Math.random() - 0.5) * 2 * halfSpread;\n const speed = opts.velocity * (0.6 + Math.random() * 0.8);\n this.particles.push({\n x: cx,\n y: cy,\n vx: Math.cos(angle) * speed,\n vy: Math.sin(angle) * speed,\n rotation: Math.random() * Math.PI * 2,\n rotationSpeed: (Math.random() - 0.5) * 0.3,\n color: opts.colors[Math.floor(Math.random() * opts.colors.length)],\n size: opts.size * (0.8 + Math.random() * 0.6),\n ttl: opts.ttl,\n });\n }\n }\n\n private tick(): void {\n const ctx = this.ctx;\n const win = this.doc.defaultView;\n if (!ctx || !win) {\n this.rafId = null;\n return;\n }\n ctx.clearRect(0, 0, win.innerWidth, win.innerHeight);\n const next: Particle[] = [];\n for (const p of this.particles) {\n p.vx *= DEFAULTS.drag;\n p.vy = p.vy * DEFAULTS.drag + DEFAULTS.gravity;\n p.x += p.vx;\n p.y += p.vy;\n p.rotation += p.rotationSpeed;\n p.ttl -= 1;\n if (p.ttl <= 0 || p.y > win.innerHeight + 50) continue;\n next.push(p);\n ctx.save();\n ctx.translate(p.x, p.y);\n ctx.rotate(p.rotation);\n ctx.globalAlpha = Math.min(1, p.ttl / 60);\n ctx.fillStyle = p.color;\n ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size * 0.6);\n ctx.restore();\n }\n this.particles = next;\n if (this.particles.length > 0) {\n this.rafId = requestAnimationFrame(() => this.tick());\n } else {\n this.rafId = null;\n }\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-config.mjs
CHANGED
|
@@ -84,6 +84,15 @@ function provideWrConfig(config) {
|
|
|
84
84
|
* size scale is narrower than {@link WrControlSize} stops compiling here rather
|
|
85
85
|
* than accepting a value it cannot render — the type checker doing the work a
|
|
86
86
|
* parity script would otherwise have to.
|
|
87
|
+
*
|
|
88
|
+
* `NoInfer` is what makes that true. Drop it and the guard is a comment again on
|
|
89
|
+
* every call site that does not pin the generic by hand, which was all but two —
|
|
90
|
+
* `wr-btn` the only size one among them. Without it `T` has three inference sites;
|
|
91
|
+
* TypeScript collects all three candidates and keeps the SUPERTYPE, so `pick`'s
|
|
92
|
+
* `WrControlSize` won and `T` widened to the config's type. `Signal<T>` is
|
|
93
|
+
* covariant, so the narrower binding stayed assignable and the call compiled clean.
|
|
94
|
+
* Widening `WrControlSize` by one member used to raise exactly one error, in
|
|
95
|
+
* `wr-btn`; it now raises nine.
|
|
87
96
|
*/
|
|
88
97
|
function useConfigValue(binding, pick, fallback) {
|
|
89
98
|
const config = inject(WR_CONFIG);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-config.mjs","sources":["../../../projects/lib/config/tokens.ts","../../../projects/lib/config/provide-wr-config.ts","../../../projects/lib/config/util.ts","../../../projects/lib/config/ngwr-config.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 { WrConfig } from './interfaces';\n\n/**\n * App-wide component defaults, or `{}` when the app never called\n * `provideWrConfig()`.\n *\n * A factory default rather than an optional inject at every call site: a\n * component asking for its own default should not have to care whether anyone\n * configured anything, and `{}` reads the same as absent through\n * {@link useConfigValue}.\n */\nexport const WR_CONFIG = new InjectionToken<WrConfig>('WR_CONFIG', {\n providedIn: 'root',\n factory: (): WrConfig => ({}),\n});\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\nimport type { WrConfig } from './interfaces';\nimport { WR_CONFIG } from './tokens';\n\n/**\n * Set app-wide component defaults.\n *\n * Nothing is activated by this and no service is constructed — unlike\n * `provideWrDensity()`, which has to instantiate its service to write an\n * attribute onto `<html>`. A config is read on demand by whichever component\n * wants a default, so providing it costs one token.\n *\n * @example\n * ```ts\n * bootstrapApplication(App, {\n * providers: [provideWrConfig({ button: { size: 'sm' }, select: { rounded: true } })],\n * });\n * ```\n *\n * Scoped use works the same way: put it in a route's `providers` and only that\n * branch of the tree sees it, because the components resolve the token through\n * their own injector.\n *\n * @see https://ngwr.dev/start/configuration\n */\nexport function provideWrConfig(config: WrConfig): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: WR_CONFIG, useValue: 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 } from '@angular/core';\n\nimport type { WrConfig } from './interfaces';\nimport { WR_CONFIG } from './tokens';\n\n/**\n * Resolve an input against the app's {@link WrConfig}, then a built-in fallback.\n *\n * The same shape as `useI18nText` from `ngwr/i18n`, and for the same reason: a\n * component's default has three possible sources and exactly one precedence\n * order, so it is worth writing once.\n *\n * 1. What the template bound, if it bound anything.\n * 2. What `provideWrConfig()` set for this component.\n * 3. The component's own fallback.\n *\n * A bound value always wins. That is what keeps a global config from becoming\n * something templates have to escape — the mistake NG-ZORRO's `NzConfigService`\n * is remembered for.\n *\n * Call it from a field initializer (it injects), and give the input `null` as its\n * own default so \"not set\" is expressible:\n *\n * ```ts\n * readonly size = input<WrButtonSize | null>(null);\n * protected readonly resolvedSize = useConfigValue(this.size, c => c.button?.size, 'md');\n * ```\n *\n * The generic is the COMPONENT's type, not the config's, so a component whose\n * size scale is narrower than {@link WrControlSize} stops compiling here rather\n * than accepting a value it cannot render — the type checker doing the work a\n * parity script would otherwise have to.\n */\nexport function useConfigValue<T>(\n binding: Signal<T | null | undefined>,\n pick: (config: WrConfig) => T | undefined,\n fallback: T
|
|
1
|
+
{"version":3,"file":"ngwr-config.mjs","sources":["../../../projects/lib/config/tokens.ts","../../../projects/lib/config/provide-wr-config.ts","../../../projects/lib/config/util.ts","../../../projects/lib/config/ngwr-config.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 { WrConfig } from './interfaces';\n\n/**\n * App-wide component defaults, or `{}` when the app never called\n * `provideWrConfig()`.\n *\n * A factory default rather than an optional inject at every call site: a\n * component asking for its own default should not have to care whether anyone\n * configured anything, and `{}` reads the same as absent through\n * {@link useConfigValue}.\n */\nexport const WR_CONFIG = new InjectionToken<WrConfig>('WR_CONFIG', {\n providedIn: 'root',\n factory: (): WrConfig => ({}),\n});\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\nimport type { WrConfig } from './interfaces';\nimport { WR_CONFIG } from './tokens';\n\n/**\n * Set app-wide component defaults.\n *\n * Nothing is activated by this and no service is constructed — unlike\n * `provideWrDensity()`, which has to instantiate its service to write an\n * attribute onto `<html>`. A config is read on demand by whichever component\n * wants a default, so providing it costs one token.\n *\n * @example\n * ```ts\n * bootstrapApplication(App, {\n * providers: [provideWrConfig({ button: { size: 'sm' }, select: { rounded: true } })],\n * });\n * ```\n *\n * Scoped use works the same way: put it in a route's `providers` and only that\n * branch of the tree sees it, because the components resolve the token through\n * their own injector.\n *\n * @see https://ngwr.dev/start/configuration\n */\nexport function provideWrConfig(config: WrConfig): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: WR_CONFIG, useValue: 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 } from '@angular/core';\n\nimport type { WrConfig } from './interfaces';\nimport { WR_CONFIG } from './tokens';\n\n/**\n * Resolve an input against the app's {@link WrConfig}, then a built-in fallback.\n *\n * The same shape as `useI18nText` from `ngwr/i18n`, and for the same reason: a\n * component's default has three possible sources and exactly one precedence\n * order, so it is worth writing once.\n *\n * 1. What the template bound, if it bound anything.\n * 2. What `provideWrConfig()` set for this component.\n * 3. The component's own fallback.\n *\n * A bound value always wins. That is what keeps a global config from becoming\n * something templates have to escape — the mistake NG-ZORRO's `NzConfigService`\n * is remembered for.\n *\n * Call it from a field initializer (it injects), and give the input `null` as its\n * own default so \"not set\" is expressible:\n *\n * ```ts\n * readonly size = input<WrButtonSize | null>(null);\n * protected readonly resolvedSize = useConfigValue(this.size, c => c.button?.size, 'md');\n * ```\n *\n * The generic is the COMPONENT's type, not the config's, so a component whose\n * size scale is narrower than {@link WrControlSize} stops compiling here rather\n * than accepting a value it cannot render — the type checker doing the work a\n * parity script would otherwise have to.\n *\n * `NoInfer` is what makes that true. Drop it and the guard is a comment again on\n * every call site that does not pin the generic by hand, which was all but two —\n * `wr-btn` the only size one among them. Without it `T` has three inference sites;\n * TypeScript collects all three candidates and keeps the SUPERTYPE, so `pick`'s\n * `WrControlSize` won and `T` widened to the config's type. `Signal<T>` is\n * covariant, so the narrower binding stayed assignable and the call compiled clean.\n * Widening `WrControlSize` by one member used to raise exactly one error, in\n * `wr-btn`; it now raises nine.\n */\nexport function useConfigValue<T>(\n binding: Signal<T | null | undefined>,\n pick: (config: WrConfig) => NoInfer<T> | undefined,\n fallback: NoInfer<T>\n): Signal<T> {\n const config = inject(WR_CONFIG);\n\n return computed(() => {\n const bound = binding();\n // `null` and `undefined` both mean \"the template did not say\". `false` and\n // `0` do not, which is why this is not `??` on a falsy check — a\n // `[rounded]=\"false\"` has to be able to turn a configured `true` back off.\n if (bound !== null && bound !== undefined) return bound;\n\n return pick(config) ?? fallback;\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;AAAA;;;;;AAKG;AAMH;;;;;;;;AAQG;MACU,SAAS,GAAG,IAAI,cAAc,CAAW,WAAW,EAAE;AACjE,IAAA,UAAU,EAAE,MAAM;AAClB,IAAA,OAAO,EAAE,OAAiB,EAAE,CAAC;AAC9B,CAAA;;ACvBD;;;;;AAKG;AAOH;;;;;;;;;;;;;;;;;;;;AAoBG;AACG,SAAU,eAAe,CAAC,MAAgB,EAAA;AAC9C,IAAA,OAAO,wBAAwB,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;AAC7E;;ACnCA;;;;;AAKG;AAOH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCG;SACa,cAAc,CAC5B,OAAqC,EACrC,IAAkD,EAClD,QAAoB,EAAA;AAEpB,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC;IAEhC,OAAO,QAAQ,CAAC,MAAK;AACnB,QAAA,MAAM,KAAK,GAAG,OAAO,EAAE;;;;AAIvB,QAAA,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;AAAE,YAAA,OAAO,KAAK;AAEvD,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,QAAQ;AACjC,IAAA,CAAC,CAAC;AACJ;;ACjEA;;AAEG;;;;"}
|
|
@@ -124,13 +124,12 @@ class WrContextMenuItemHarness extends ComponentHarness {
|
|
|
124
124
|
return icon ? icon.getAttribute('data-icon') : null;
|
|
125
125
|
}
|
|
126
126
|
/**
|
|
127
|
-
* Whether the item holds focus.
|
|
127
|
+
* Whether the item holds the menu's roving focus.
|
|
128
128
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* CDK focuses an element before typing into it.
|
|
129
|
+
* The menu moves focus onto its first enabled row as the pane renders and the
|
|
130
|
+
* arrows walk it from there, so this is where the keyboard is. Opening a
|
|
131
|
+
* submenu hands the cursor to the submenu's own first row, which leaves the
|
|
132
|
+
* item that owns it unfocused while its pane is up.
|
|
134
133
|
*/
|
|
135
134
|
async isFocused() {
|
|
136
135
|
return (await this.host()).isFocused();
|
|
@@ -177,8 +176,10 @@ class WrContextMenuItemHarness extends ComponentHarness {
|
|
|
177
176
|
*
|
|
178
177
|
* The keyboard, not the pointer: it opens with no delay (hover waits out
|
|
179
178
|
* 120ms — `openSubmenuByHover()` covers that path) and needs no coordinates,
|
|
180
|
-
* which jsdom could not supply anyway.
|
|
181
|
-
*
|
|
179
|
+
* which jsdom could not supply anyway. A keyboard open walks INTO the submenu,
|
|
180
|
+
* so afterwards the cursor is on the submenu's first row and `isFocused()` on
|
|
181
|
+
* this item is `false` — `openSubmenuByHover()` only shows the pane and leaves
|
|
182
|
+
* the cursor where it was.
|
|
182
183
|
*/
|
|
183
184
|
async openSubmenu(timeout = DEFAULT_TIMEOUT) {
|
|
184
185
|
await this.assertHasSubmenu('openSubmenu');
|
|
@@ -332,9 +333,10 @@ const OUTSIDE_GUARD_MS = 250;
|
|
|
332
333
|
* `clickItem()` leaves it closed.
|
|
333
334
|
* - Submenus are a first-class input here rather than a nested trigger, so they
|
|
334
335
|
* are driven from {@link WrContextMenuItemHarness}.
|
|
335
|
-
* -
|
|
336
|
-
*
|
|
337
|
-
* —
|
|
336
|
+
* - Focus moves in on EVERY open, where a dropdown's does so only when a
|
|
337
|
+
* keyboard opened it. There is no hover trigger here — a right-click, Shift+F10
|
|
338
|
+
* or a long-press is always a deliberate request for the menu — so
|
|
339
|
+
* `getFocusedItemText()` answers straight after `open()`.
|
|
338
340
|
*
|
|
339
341
|
* **Timing.** The waits are real: a 500ms `setTimeout` for the long-press hold and
|
|
340
342
|
* another 220ms one for the exit animation the pane is held alive through, plus the
|
|
@@ -541,6 +543,23 @@ class WrContextMenuHarness extends ContentContainerComponentHarness {
|
|
|
541
543
|
const items = await this.getItems();
|
|
542
544
|
return Promise.all(items.map(item => item.getText()));
|
|
543
545
|
}
|
|
546
|
+
/**
|
|
547
|
+
* The label of the item that currently holds focus, or `null` when the
|
|
548
|
+
* keyboard is elsewhere.
|
|
549
|
+
*
|
|
550
|
+
* The menu roves focus rather than drawing a cursor — it lands on the first
|
|
551
|
+
* ENABLED item as the pane renders, and the arrows walk it over the enabled
|
|
552
|
+
* items only — so this is the only way to ask where the keyboard is. It answers
|
|
553
|
+
* `null` once a submenu has taken the cursor: that pane is a separate overlay,
|
|
554
|
+
* and its own rows are reached through `WrContextMenuItemHarness`.
|
|
555
|
+
*/
|
|
556
|
+
async getFocusedItemText() {
|
|
557
|
+
for (const item of await this.getItems()) {
|
|
558
|
+
if (await item.isFocused())
|
|
559
|
+
return item.getText();
|
|
560
|
+
}
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
544
563
|
/**
|
|
545
564
|
* How many separators this menu shows.
|
|
546
565
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-context-menu-testing.mjs","sources":["../../../projects/lib/context-menu/testing/wait.ts","../../../projects/lib/context-menu/testing/wr-context-menu-item-harness.ts","../../../projects/lib/context-menu/testing/wr-context-menu-harness.ts","../../../projects/lib/context-menu/testing/ngwr-context-menu-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\n/** How long the waiters poll before giving up, in ms. Generous next to the 500ms hold. */\nconst DEFAULT_TIMEOUT = 1000;\n\n/** How often they re-check, in ms. */\nconst POLL_STEP = 10;\n\n/**\n * The real `setTimeout`, captured at module load — before a spec can install\n * fake timers.\n *\n * Every delay in this component family is wall-clock: a plain `setTimeout` inside\n * the directive for the 500ms long-press hold, the 120ms submenu hover and the\n * 220ms exit animation the pane is held alive through, and a `performance.now()`\n * comparison for the 200ms window in which outside presses are ignored — no timer\n * behind that one, just elapsed time. Under zoneless change detection `whenStable()` resolves\n * without waiting for a macrotask, so the waiters have to let REAL time pass —\n * reading a faked global instead would mean the sleep never fires at all, and\n * the harness would hang where it should fail with the message it prepared.\n */\nconst realSetTimeout = globalThis.setTimeout;\n\n/** Let real time pass, whatever a spec has done to the global clock. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => {\n realSetTimeout(resolve, ms);\n });\n}\n\nexport { DEFAULT_TIMEOUT, POLL_STEP, sleep };\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, type HarnessLoader, HarnessPredicate, TestKey } from '@angular/cdk/testing';\n\nimport type { WrContextMenuItemHarnessFilters } from './interfaces';\nimport { DEFAULT_TIMEOUT, POLL_STEP, sleep } from './wait';\n\n/**\n * Test harness for one `<wr-context-menu-item>` inside a context menu.\n *\n * The selector is the CLASS rather than the element, matching\n * `.wr-dropdown-item`: it is public API, and it is on the host whichever way the\n * item was written. A `<wr-context-menu-divider>` is deliberately not one of\n * these — it announces `role=\"separator\"` rather than `menuitem`, so it is not\n * something a user can land on and not a member of the item list\n * (`WrContextMenuHarness.getDividerCount()` counts those).\n *\n * An item that owns a `[submenu]` is also the way INTO it: the submenu is a\n * second overlay pane, a sibling of the root menu rather than a child of the\n * item, and it is reached through the id the item publishes as `aria-controls`\n * while it is open. `getSubmenuItems()` answers with these same harnesses, so a\n * chain of any depth is walked by repeating the call.\n *\n * @example\n * ```ts\n * const [cut, copy, more] = await menu.getItems();\n *\n * expect(await copy.isDisabled()).toBe(true);\n * await more.openSubmenu();\n * await more.clickSubmenuItem({ text: 'Duplicate' });\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrContextMenuItemHarness extends ComponentHarness {\n /** The class the item paints on its host. */\n static hostSelector = '.wr-context-menu-item';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrContextMenuItemHarnessFilters = {}): HarnessPredicate<WrContextMenuItemHarness> {\n return new HarnessPredicate(WrContextMenuItemHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption(\n 'hasSubmenu',\n options.hasSubmenu,\n async (harness, hasSubmenu) => (await harness.hasSubmenu()) === hasSubmenu\n );\n }\n\n /**\n * The item's label, trimmed.\n *\n * Read from the label span, not from the host: a registered icon is written in\n * with `innerHTML`, so an icon carrying a `<title>` would otherwise become\n * part of the answer.\n */\n async getText(): Promise<string> {\n return (await this.locatorFor('.wr-context-menu-item__label')()).text();\n }\n\n /** The role the item announces — `menuitem`. */\n async getRole(): Promise<string | null> {\n return (await this.host()).getAttribute('role');\n }\n\n /**\n * Whether the item refuses activation.\n *\n * `aria-disabled`, not the `--disabled` modifier class: both are there, and\n * this is the one a screen reader is told.\n */\n async isDisabled(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-disabled')) === 'true';\n }\n\n /**\n * Whether the item owns a nested submenu.\n *\n * Read from `aria-haspopup`, not from the `--has-submenu` class or the\n * `[submenu]` input: the input is always BOUND (it takes a\n * `<wr-context-menu>` reference), so it never reaches the DOM, and the ARIA\n * property is what announces the item as a drill-down.\n */\n async hasSubmenu(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-haspopup')) === 'menu';\n }\n\n /** Whether this item's submenu is showing, per `aria-expanded`. */\n async isSubmenuOpen(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-expanded')) === 'true';\n }\n\n /** Whether the item shows a leading icon. */\n async hasIcon(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-context-menu-item__icon')()) !== null;\n }\n\n /** The leading icon's registered name, or `null` when the item has no icon. */\n async getIconName(): Promise<string | null> {\n const icon = await this.locatorForOptional('.wr-context-menu-item__icon')();\n // `<wr-icon>` reflects the name it was asked for as `data-icon`, whether or not\n // that name is registered — so this answers what the item MEANT to draw.\n return icon ? icon.getAttribute('data-icon') : null;\n }\n\n /**\n * Whether the item holds focus.\n *\n * Worth asking, because nothing in this component ever puts it there: unlike\n * `<wr-dropdown-menu>`, a context menu does not focus its first item on open\n * and has no arrow-key cursor between items. Focus lands on an item when\n * something sends it a key — which every keyboard method here does, since the\n * CDK focuses an element before typing into it.\n */\n async isFocused(): Promise<boolean> {\n return (await this.host()).isFocused();\n }\n\n /**\n * Click the item. A disabled item throws instead.\n *\n * Refusing is the only honest answer. A browser never delivers the click at all:\n * `pointer-events: none` on the `--disabled` modifier takes the row out of the\n * hit test. jsdom loads no stylesheets, so a click dispatched here DOES land on\n * the host, and while the component's own handler refuses it (a disabled item\n * dismisses nothing), the consumer's `(click)` on that same host is a separate\n * listener and runs — so the spec would record an activation that cannot happen\n * in a browser. Assert `isDisabled()` instead.\n *\n * Activating a LEAF item dismisses the whole chain, root menu included. An item\n * that owns a submenu is not a leaf: clicking it does nothing at all (hover or\n * `openSubmenu()` is how it opens).\n */\n async click(): Promise<void> {\n if (await this.isDisabled()) {\n throw new Error(\n `WrContextMenuItemHarness.click(): \"${await this.getText()}\" is disabled, so clicking it would ` +\n 'report behaviour a browser cannot produce — the pointer is stopped by CSS, which jsdom does ' +\n 'not load. Assert isDisabled() instead.'\n );\n }\n\n return (await this.host()).click();\n }\n\n /**\n * Press Enter on the item — the keyboard equivalent of a click, and the route\n * a spec should prefer, since it needs no hit test.\n *\n * Deliberately does NOT refuse a disabled item the way `click()` does: the\n * component guards the keyboard path itself, so the key can be sent honestly\n * and a spec can assert that nothing happened. On an item that owns a submenu\n * this OPENS the submenu rather than activating the row, which is the APG\n * behaviour — activating it would close the menu and leave the submenu\n * unreachable from the keyboard.\n */\n async activateByKeyboard(): Promise<void> {\n return (await this.host()).sendKeys(TestKey.ENTER);\n }\n\n /**\n * Open this item's submenu with the right arrow and wait until it says so.\n *\n * The keyboard, not the pointer: it opens with no delay (hover waits out\n * 120ms — `openSubmenuByHover()` covers that path) and needs no coordinates,\n * which jsdom could not supply anyway. Sending the key focuses the item first,\n * exactly as a roving cursor would have.\n */\n async openSubmenu(timeout = DEFAULT_TIMEOUT): Promise<void> {\n await this.assertHasSubmenu('openSubmenu');\n if (await this.isSubmenuOpen()) return;\n\n await (await this.host()).sendKeys(TestKey.RIGHT_ARROW);\n if (await this.isSubmenuOpen()) return;\n if (await this.settled(true, timeout)) return;\n\n throw new Error(\n `WrContextMenuItemHarness.openSubmenu(): \"${await this.getText()}\" did not open its submenu within ` +\n `${timeout}ms. A disabled item refuses the key outright — check isDisabled().`\n );\n }\n\n /**\n * Open this item's submenu by resting the pointer on it, the gesture a mouse\n * user actually makes, and wait out the hover delay.\n */\n async openSubmenuByHover(timeout = DEFAULT_TIMEOUT): Promise<void> {\n await this.assertHasSubmenu('openSubmenuByHover');\n if (await this.isSubmenuOpen()) return;\n\n await (await this.host()).hover();\n if (await this.settled(true, timeout)) return;\n\n throw new Error(\n `WrContextMenuItemHarness.openSubmenuByHover(): \"${await this.getText()}\" did not open its submenu ` +\n `within ${timeout}ms. The hover path is a real 120ms timer, so a spec on fake timers has to ` +\n 'advance the clock itself; a disabled item ignores the pointer entirely.'\n );\n }\n\n /** Close this item's submenu with the left arrow. An item without one open is left alone. */\n async closeSubmenu(timeout = DEFAULT_TIMEOUT): Promise<void> {\n if (!(await this.isSubmenuOpen())) return;\n\n await (await this.host()).sendKeys(TestKey.LEFT_ARROW);\n if (await this.settled(false, timeout)) return;\n\n throw new Error(\n `WrContextMenuItemHarness.closeSubmenu(): \"${await this.getText()}\" still reports its submenu open ` +\n `after ${timeout}ms.`\n );\n }\n\n /**\n * The items in THIS item's submenu, in DOM order.\n *\n * Throws while the submenu is shut, and while the item owns none: both would\n * otherwise answer with an empty array, which reads like a submenu that\n * rendered nothing and fails somewhere else entirely.\n */\n async getSubmenuItems(filters: WrContextMenuItemHarnessFilters = {}): Promise<WrContextMenuItemHarness[]> {\n const loader = await this.submenuLoader();\n return loader.getAllHarnesses(WrContextMenuItemHarness.with(filters));\n }\n\n /** The labels of the items in this item's submenu, in DOM order. */\n async getSubmenuItemTexts(): Promise<string[]> {\n const items = await this.getSubmenuItems();\n return Promise.all(items.map(item => item.getText()));\n }\n\n /**\n * Open the submenu if it is shut, then click the first item in it matching the\n * filters.\n *\n * Picking a leaf dismisses the whole chain — this item's submenu and the root\n * menu with it.\n */\n async clickSubmenuItem(filters: WrContextMenuItemHarnessFilters): Promise<void> {\n await this.openSubmenu();\n\n const [item] = await this.getSubmenuItems(filters);\n if (!item) {\n const offered = await this.getSubmenuItemTexts();\n throw new Error(\n `WrContextMenuItemHarness.clickSubmenuItem(): no item matched ${JSON.stringify(filters)}. ` +\n `The submenu of \"${await this.getText()}\" offers: ${offered.join(', ')}.`\n );\n }\n\n await item.click();\n }\n\n /**\n * A loader scoped to THIS item's submenu pane.\n *\n * Scoped by the published id rather than by `.wr-context-menu`: every pane in\n * the chain — the root menu and each open submenu — carries that class in one\n * shared overlay container, so a class query would answer with whichever\n * opened first.\n */\n private async submenuLoader(): Promise<HarnessLoader> {\n return this.documentRootLocatorFactory().harnessLoaderFor(`#${await this.submenuId()}`);\n }\n\n /** The id this item publishes as `aria-controls`, which its open submenu carries. */\n private async submenuId(): Promise<string> {\n const id = await (await this.host()).getAttribute('aria-controls');\n if (!id) {\n const label = await this.getText();\n throw new Error(\n `WrContextMenuItemHarness: \"${label}\" has no submenu showing. An item publishes aria-controls only ` +\n 'while its submenu is open — call openSubmenu() first, and check that it owns one at all with ' +\n 'hasSubmenu().'\n );\n }\n return id;\n }\n\n /** Fail early, and by name, when the item is not a drill-down at all. */\n private async assertHasSubmenu(method: string): Promise<void> {\n if (await this.hasSubmenu()) return;\n\n throw new Error(\n `WrContextMenuItemHarness.${method}(): \"${await this.getText()}\" owns no submenu, so there is ` +\n 'nothing to open. An item that does advertises it with aria-haspopup=\"menu\" — hasSubmenu().'\n );\n }\n\n /** Poll until `isSubmenuOpen()` reports `open`, and say whether it did. */\n private async settled(open: boolean, timeout: number): Promise<boolean> {\n for (let waited = 0; waited <= timeout; waited += POLL_STEP) {\n if ((await this.isSubmenuOpen()) === open) return true;\n\n await sleep(POLL_STEP);\n await this.forceStabilize();\n }\n return false;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport {\n ContentContainerComponentHarness,\n type HarnessLoader,\n HarnessPredicate,\n TestKey,\n type TestElement,\n} from '@angular/cdk/testing';\n\nimport type { WrContextMenuHarnessFilters, WrContextMenuItemHarnessFilters } from './interfaces';\nimport { DEFAULT_TIMEOUT, POLL_STEP, sleep } from './wait';\nimport { WrContextMenuItemHarness } from './wr-context-menu-item-harness';\n\n/**\n * How long to sit still before an outside press is allowed to dismiss the menu.\n *\n * The directive ignores outside events for 200ms after opening, because the very\n * right-click that opened the menu still has its own `mouseup` / `auxclick`\n * pending — without the window, lifting the finger would shut the menu again.\n */\nconst OUTSIDE_GUARD_MS = 250;\n\n/**\n * Test harness for `[wrContextMenu]` — the right-click target, and the menu it\n * opens.\n *\n * The harness matches the TARGET, because that is the only part of a context\n * menu in the fixture: `<wr-context-menu>` renders nothing of its own (it hands\n * over an `<ng-template>` and hides its host), and the menu is portalled into the\n * overlay container, a sibling of the whole app. So every item is read through\n * the document root, scoped by the `aria-controls` id the target publishes while\n * its menu is up — which is what keeps two menus on one page from answering with\n * each other's items, and what keeps a re-opened menu from answering with the one\n * still playing its exit animation.\n *\n * That scoped loader is also this harness's CONTENT loader, so\n * `menu.getHarness(…)` resolves inside THIS menu: a component the consumer\n * projected into the menu is reachable without the spec ever touching the\n * overlay.\n *\n * **How it differs from `WrDropdownHarness`, its closest sibling.**\n *\n * - The gesture is a real `contextmenu` event, not a click — `open()` sends one,\n * and `openByLongPress()` covers the touch path. There is no toggle: a second\n * right-click re-opens the menu at the new pointer position.\n * - Activating an item DISMISSES the menu (a dropdown's stays up), so\n * `clickItem()` leaves it closed.\n * - Submenus are a first-class input here rather than a nested trigger, so they\n * are driven from {@link WrContextMenuItemHarness}.\n * - The menu never takes focus: nothing focuses the first item and there is no\n * arrow-key cursor between items, so there is no `getFocusedItemText()` to ask\n * — see `WrContextMenuItemHarness.isFocused()`.\n *\n * **Timing.** The waits are real: a 500ms `setTimeout` for the long-press hold and\n * another 220ms one for the exit animation the pane is held alive through, plus the\n * outside-press window above — that one is a `performance.now()` comparison against\n * the moment the menu opened rather than a timer, so only wall-clock time gets past\n * it. The methods that must sit out real time say so; a spec on fake timers should\n * drive the clock itself and read `isOpen()`.\n *\n * @example\n * ```ts\n * const menu = await loader.getHarness(WrContextMenuHarness.with({ text: 'Right-click me' }));\n *\n * await menu.open();\n * expect(await menu.getItemTexts()).toEqual(['Cut', 'Copy', 'More']);\n *\n * await menu.clickItem({ text: 'Cut' });\n * expect(await menu.isOpen()).toBe(false);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrContextMenuHarness extends ContentContainerComponentHarness {\n /** The directive's host — whatever element the consumer put `[wrContextMenu]` on. */\n static hostSelector = '.wr-context-menu-host';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrContextMenuHarnessFilters = {}): HarnessPredicate<WrContextMenuHarness> {\n return new HarnessPredicate(WrContextMenuHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getTargetText(), text))\n .addOption('open', options.open, async (harness, open) => (await harness.isOpen()) === open);\n }\n\n /** The target's own visible text — the content that gets right-clicked. */\n async getTargetText(): Promise<string> {\n return (await this.host()).text();\n }\n\n /**\n * Whether this target's menu is showing.\n *\n * Read from the target rather than from the overlay, and that is the whole\n * point: a closed menu's pane stays in the DOM for its exit animation, while\n * the reference is dropped the instant the close begins. Asking the DOM\n * \"is a menu present\" would answer `true` for a quarter of a second after every\n * dismissal.\n *\n * So the answer is only as good as the directive's claim on `aria-controls`:\n * put `[wrContextMenu]` on an element that publishes one of its own and the two\n * fight over a single attribute.\n */\n async isOpen(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-controls')) !== null;\n }\n\n /** The role the menu announces — `menu`. Throws while no menu is open. */\n async getMenuRole(): Promise<string | null> {\n return (await this.menu()).getAttribute('role');\n }\n\n /**\n * Everything the open menu renders as text, runs of whitespace collapsed.\n *\n * Wider than `getItemTexts()` on purpose: `<wr-context-menu>` projects whatever\n * it is given, so a section heading or any other markup a consumer put between\n * the items shows up here and in no item harness. Dividers contribute nothing —\n * they render no text.\n *\n * Expect to match a FRAGMENT of this rather than the whole of it: Angular\n * strips the whitespace between elements at compile time, so the labels run\n * together (`'Pick oneAppleBanana'`). The collapsing here only tidies newlines\n * inside a single block of projected text.\n */\n async getMenuText(): Promise<string> {\n return (await (await this.menu()).text()).replace(/\\s+/g, ' ').trim();\n }\n\n /**\n * Open the menu by right-clicking the target, the gesture the directive\n * actually listens for (`contextmenu`, which is also what Shift+F10 sends).\n *\n * An already-open menu is left alone — `rightClick()` is the raw gesture if\n * what you want is the re-open.\n *\n * Note for a spec with two targets: a `contextmenu` event anywhere is an\n * outside pointer event for every OTHER open menu, so this dismisses them on\n * the way in. `openByLongPress()` is the route that does not.\n */\n async open(): Promise<void> {\n if (await this.isOpen()) return;\n\n await this.rightClick();\n if (await this.isOpen()) return;\n\n throw new Error(\n 'WrContextMenuHarness.open(): the right-click did not open a menu. The directive answers ' +\n '`contextmenu` on its own host, so check this is the element you put [wrContextMenu] on. One ' +\n 'deliberate exception: a right-click within 700ms of a long-press open is swallowed, because touch ' +\n 'browsers fire a synthetic contextmenu after the hold.'\n );\n }\n\n /**\n * Send a `contextmenu` to the target whatever state the menu is in.\n *\n * On an open menu this re-opens it at the new pointer position — the previous\n * pane is dismissed and a fresh one takes its place, which is why the harness\n * scopes by a per-open id rather than by class: for the length of the exit\n * animation both are in the container, the dying one first.\n *\n * The coordinates the CDK requires are all zeros, and meaningless: jsdom has no\n * layout. Nothing here needs them — the event is dispatched on the element\n * itself, no hit test involved.\n */\n async rightClick(): Promise<void> {\n return (await this.host()).rightClick(0, 0);\n }\n\n /**\n * Open the menu with a touch long-press — hold a non-mouse pointer on the\n * target for 500ms — and wait until it is up.\n *\n * Worth its own method rather than an argument to `open()`, for the reason the\n * dropdown has `openByKeyboard()`: it is the only way to get TWO context menus\n * open at once. The press emits no `click` / `auxclick` / `contextmenu`, so it\n * is not an outside press for anyone else's menu, while a right-click is.\n *\n * The pointer fields the directive reads are assigned onto a synthetic event:\n * what matters is a primary pointer that is not a mouse. Movement beyond 10px,\n * a scroll, or an early lift cancels the hold in the component — none of which\n * this does.\n */\n async openByLongPress(timeout = DEFAULT_TIMEOUT): Promise<void> {\n if (await this.isOpen()) return;\n\n const host = await this.host();\n await host.dispatchEvent('pointerdown', {\n pointerType: 'touch',\n isPrimary: true,\n clientX: 0,\n clientY: 0,\n pageX: 0,\n pageY: 0,\n });\n\n const opened = await this.settled(true, timeout);\n // The finger comes off once the menu is up — after the hold, so it reads as a\n // completed long-press rather than the early lift that cancels one.\n await host.dispatchEvent('pointerup');\n\n if (opened) return;\n\n throw new Error(\n `WrContextMenuHarness.openByLongPress(): nothing opened within ${timeout}ms. The hold is a real 500ms ` +\n 'timer, so a spec on fake timers has to advance the clock itself and read isOpen().'\n );\n }\n\n /**\n * Dismiss the menu with Escape and wait until its pane has left the DOM.\n *\n * Escape is delivered by the CDK's keyboard dispatcher to the TOP-MOST overlay,\n * so with a later menu (or any other overlay) open on top, this one keeps\n * waiting its turn — that is what the throw below is about. The wait covers the\n * 220ms exit animation, so a spec can assert afterwards that nothing lingers.\n */\n async close(timeout = DEFAULT_TIMEOUT): Promise<void> {\n if (!(await this.isOpen())) return;\n\n // Captured first: the target drops the reference the instant the close starts,\n // and the wait below still has to know which pane to watch.\n const id = await this.menuId();\n await (await this.host()).sendKeys(TestKey.ESCAPE);\n\n if (await this.isOpen()) {\n throw new Error(\n 'WrContextMenuHarness.close(): Escape did not dismiss the menu. The CDK keyboard dispatcher ' +\n 'delivers it to the TOP-MOST overlay only, so anything opened after this menu takes it first.'\n );\n }\n\n return this.waitUntilGone(id, 'close', timeout);\n }\n\n /**\n * Dismiss the menu by pressing outside it, the way a user gets rid of one, and\n * wait until its pane has left the DOM.\n *\n * Sits out real time first: the directive ignores outside events for 200ms after\n * opening, so the right-click that opened the menu cannot immediately close it\n * again with its own trailing `auxclick`. The press then lands on the TARGET,\n * which is outside the pane and therefore counts — the page background is not\n * something a harness has a handle on. A target that carries a `(click)` of its\n * own will see it fire, so assert on the dismissal rather than on that handler.\n */\n async closeByOutsidePress(timeout = DEFAULT_TIMEOUT): Promise<void> {\n if (!(await this.isOpen())) return;\n\n const id = await this.menuId();\n await sleep(OUTSIDE_GUARD_MS);\n await this.forceStabilize();\n await (await this.host()).click();\n\n if (await this.isOpen()) {\n throw new Error(\n 'WrContextMenuHarness.closeByOutsidePress(): the menu is still open. A press inside the pane is ' +\n 'not an outside press, and neither is one that started inside it — this one lands on the target.'\n );\n }\n\n return this.waitUntilGone(id, 'closeByOutsidePress', timeout);\n }\n\n /**\n * The items in this menu, in DOM order.\n *\n * Only this menu's own: an open submenu is a separate pane, so its items are\n * `WrContextMenuItemHarness.getSubmenuItems()`, not part of this list. Dividers\n * are not items either.\n *\n * Throws while the menu is shut — the items do not exist anywhere until the\n * portal is attached, and an empty array reads like a menu that rendered\n * nothing.\n */\n async getItems(filters: WrContextMenuItemHarnessFilters = {}): Promise<WrContextMenuItemHarness[]> {\n return this.getAllHarnesses(WrContextMenuItemHarness.with(filters));\n }\n\n /** The labels of this menu's items, in DOM order. */\n async getItemTexts(): Promise<string[]> {\n const items = await this.getItems();\n return Promise.all(items.map(item => item.getText()));\n }\n\n /**\n * How many separators this menu shows.\n *\n * Counted by `role=\"separator\"` rather than by `.wr-context-menu-divider`, so a\n * separator the consumer wrote themselves counts too — the role is what tells a\n * screen reader where one group of items ends. A separator is never a member of\n * `getItems()`.\n */\n async getDividerCount(): Promise<number> {\n const separators = await this.inMenuAll('[role=\"separator\"]');\n return separators.length;\n }\n\n /**\n * Open the menu if it is shut, then click the first item matching the filters.\n *\n * Picking a leaf DISMISSES the menu — unlike a dropdown, which stays up. The\n * dismissal is reported at once (the target drops its reference synchronously),\n * while the pane plays its exit animation for another 220ms.\n */\n async clickItem(filters: WrContextMenuItemHarnessFilters): Promise<void> {\n await this.open();\n\n const [item] = await this.getItems(filters);\n if (!item) {\n const offered = await this.getItemTexts();\n throw new Error(\n `WrContextMenuHarness.clickItem(): no item matched ${JSON.stringify(filters)}. ` +\n `The menu offers: ${offered.join(', ')}.`\n );\n }\n\n await item.click();\n }\n\n /**\n * A loader scoped to THIS target's menu — the content loader every\n * `HarnessLoader` method on this class runs through, `getItems()` included.\n *\n * Scoped by the published id rather than by `.wr-context-menu`: the overlay\n * container is shared by every pane on the page, so a bare class query would\n * answer with whichever menu opened first.\n */\n protected override async getRootHarnessLoader(): Promise<HarnessLoader> {\n return this.documentRootLocatorFactory().harnessLoaderFor(`#${await this.menuId()}`);\n }\n\n /** This target's menu element in the overlay. */\n private async menu(): Promise<TestElement> {\n return this.documentRootLocatorFactory().locatorFor(`#${await this.menuId()}`)();\n }\n\n /** Every element inside THIS menu matching the selector. */\n private async inMenuAll(selector: string): Promise<TestElement[]> {\n return this.documentRootLocatorFactory().locatorForAll(`#${await this.menuId()} ${selector}`)();\n }\n\n /** The id the target publishes as `aria-controls`, which its open menu carries. */\n private async menuId(): Promise<string> {\n const id = await (await this.host()).getAttribute('aria-controls');\n if (!id) {\n throw new Error(\n 'WrContextMenuHarness: no menu is open, so there is nothing inside it to read — call open() ' +\n 'first. The target publishes `aria-controls` only while its menu is up.'\n );\n }\n return id;\n }\n\n /** Poll until the pane with this id has been disposed. */\n private async waitUntilGone(id: string, method: string, timeout: number): Promise<void> {\n const locate = this.documentRootLocatorFactory().locatorForOptional(`#${id}`);\n\n for (let waited = 0; waited <= timeout; waited += POLL_STEP) {\n if ((await locate()) === null) return;\n\n await sleep(POLL_STEP);\n await this.forceStabilize();\n }\n\n throw new Error(\n `WrContextMenuHarness.${method}(): the menu was still in the DOM ${timeout}ms after it closed. The ` +\n 'directive keeps the pane alive for its 220ms exit animation and disposes it on a real timer, so a ' +\n 'spec on fake timers has to advance the clock itself.'\n );\n }\n\n /** Poll until `isOpen()` reports `open`, and say whether it did. */\n private async settled(open: boolean, timeout: number): Promise<boolean> {\n for (let waited = 0; waited <= timeout; waited += POLL_STEP) {\n if ((await this.isOpen()) === open) return true;\n\n await sleep(POLL_STEP);\n await this.forceStabilize();\n }\n return false;\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-context-menu-testing.mjs","sources":["../../../projects/lib/context-menu/testing/wait.ts","../../../projects/lib/context-menu/testing/wr-context-menu-item-harness.ts","../../../projects/lib/context-menu/testing/wr-context-menu-harness.ts","../../../projects/lib/context-menu/testing/ngwr-context-menu-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\n/** How long the waiters poll before giving up, in ms. Generous next to the 500ms hold. */\nconst DEFAULT_TIMEOUT = 1000;\n\n/** How often they re-check, in ms. */\nconst POLL_STEP = 10;\n\n/**\n * The real `setTimeout`, captured at module load — before a spec can install\n * fake timers.\n *\n * Every delay in this component family is wall-clock: a plain `setTimeout` inside\n * the directive for the 500ms long-press hold, the 120ms submenu hover and the\n * 220ms exit animation the pane is held alive through, and a `performance.now()`\n * comparison for the 200ms window in which outside presses are ignored — no timer\n * behind that one, just elapsed time. Under zoneless change detection `whenStable()` resolves\n * without waiting for a macrotask, so the waiters have to let REAL time pass —\n * reading a faked global instead would mean the sleep never fires at all, and\n * the harness would hang where it should fail with the message it prepared.\n */\nconst realSetTimeout = globalThis.setTimeout;\n\n/** Let real time pass, whatever a spec has done to the global clock. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => {\n realSetTimeout(resolve, ms);\n });\n}\n\nexport { DEFAULT_TIMEOUT, POLL_STEP, sleep };\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, type HarnessLoader, HarnessPredicate, TestKey } from '@angular/cdk/testing';\n\nimport type { WrContextMenuItemHarnessFilters } from './interfaces';\nimport { DEFAULT_TIMEOUT, POLL_STEP, sleep } from './wait';\n\n/**\n * Test harness for one `<wr-context-menu-item>` inside a context menu.\n *\n * The selector is the CLASS rather than the element, matching\n * `.wr-dropdown-item`: it is public API, and it is on the host whichever way the\n * item was written. A `<wr-context-menu-divider>` is deliberately not one of\n * these — it announces `role=\"separator\"` rather than `menuitem`, so it is not\n * something a user can land on and not a member of the item list\n * (`WrContextMenuHarness.getDividerCount()` counts those).\n *\n * An item that owns a `[submenu]` is also the way INTO it: the submenu is a\n * second overlay pane, a sibling of the root menu rather than a child of the\n * item, and it is reached through the id the item publishes as `aria-controls`\n * while it is open. `getSubmenuItems()` answers with these same harnesses, so a\n * chain of any depth is walked by repeating the call.\n *\n * @example\n * ```ts\n * const [cut, copy, more] = await menu.getItems();\n *\n * expect(await copy.isDisabled()).toBe(true);\n * await more.openSubmenu();\n * await more.clickSubmenuItem({ text: 'Duplicate' });\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrContextMenuItemHarness extends ComponentHarness {\n /** The class the item paints on its host. */\n static hostSelector = '.wr-context-menu-item';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrContextMenuItemHarnessFilters = {}): HarnessPredicate<WrContextMenuItemHarness> {\n return new HarnessPredicate(WrContextMenuItemHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption(\n 'hasSubmenu',\n options.hasSubmenu,\n async (harness, hasSubmenu) => (await harness.hasSubmenu()) === hasSubmenu\n );\n }\n\n /**\n * The item's label, trimmed.\n *\n * Read from the label span, not from the host: a registered icon is written in\n * with `innerHTML`, so an icon carrying a `<title>` would otherwise become\n * part of the answer.\n */\n async getText(): Promise<string> {\n return (await this.locatorFor('.wr-context-menu-item__label')()).text();\n }\n\n /** The role the item announces — `menuitem`. */\n async getRole(): Promise<string | null> {\n return (await this.host()).getAttribute('role');\n }\n\n /**\n * Whether the item refuses activation.\n *\n * `aria-disabled`, not the `--disabled` modifier class: both are there, and\n * this is the one a screen reader is told.\n */\n async isDisabled(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-disabled')) === 'true';\n }\n\n /**\n * Whether the item owns a nested submenu.\n *\n * Read from `aria-haspopup`, not from the `--has-submenu` class or the\n * `[submenu]` input: the input is always BOUND (it takes a\n * `<wr-context-menu>` reference), so it never reaches the DOM, and the ARIA\n * property is what announces the item as a drill-down.\n */\n async hasSubmenu(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-haspopup')) === 'menu';\n }\n\n /** Whether this item's submenu is showing, per `aria-expanded`. */\n async isSubmenuOpen(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-expanded')) === 'true';\n }\n\n /** Whether the item shows a leading icon. */\n async hasIcon(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-context-menu-item__icon')()) !== null;\n }\n\n /** The leading icon's registered name, or `null` when the item has no icon. */\n async getIconName(): Promise<string | null> {\n const icon = await this.locatorForOptional('.wr-context-menu-item__icon')();\n // `<wr-icon>` reflects the name it was asked for as `data-icon`, whether or not\n // that name is registered — so this answers what the item MEANT to draw.\n return icon ? icon.getAttribute('data-icon') : null;\n }\n\n /**\n * Whether the item holds the menu's roving focus.\n *\n * The menu moves focus onto its first enabled row as the pane renders and the\n * arrows walk it from there, so this is where the keyboard is. Opening a\n * submenu hands the cursor to the submenu's own first row, which leaves the\n * item that owns it unfocused while its pane is up.\n */\n async isFocused(): Promise<boolean> {\n return (await this.host()).isFocused();\n }\n\n /**\n * Click the item. A disabled item throws instead.\n *\n * Refusing is the only honest answer. A browser never delivers the click at all:\n * `pointer-events: none` on the `--disabled` modifier takes the row out of the\n * hit test. jsdom loads no stylesheets, so a click dispatched here DOES land on\n * the host, and while the component's own handler refuses it (a disabled item\n * dismisses nothing), the consumer's `(click)` on that same host is a separate\n * listener and runs — so the spec would record an activation that cannot happen\n * in a browser. Assert `isDisabled()` instead.\n *\n * Activating a LEAF item dismisses the whole chain, root menu included. An item\n * that owns a submenu is not a leaf: clicking it does nothing at all (hover or\n * `openSubmenu()` is how it opens).\n */\n async click(): Promise<void> {\n if (await this.isDisabled()) {\n throw new Error(\n `WrContextMenuItemHarness.click(): \"${await this.getText()}\" is disabled, so clicking it would ` +\n 'report behaviour a browser cannot produce — the pointer is stopped by CSS, which jsdom does ' +\n 'not load. Assert isDisabled() instead.'\n );\n }\n\n return (await this.host()).click();\n }\n\n /**\n * Press Enter on the item — the keyboard equivalent of a click, and the route\n * a spec should prefer, since it needs no hit test.\n *\n * Deliberately does NOT refuse a disabled item the way `click()` does: the\n * component guards the keyboard path itself, so the key can be sent honestly\n * and a spec can assert that nothing happened. On an item that owns a submenu\n * this OPENS the submenu rather than activating the row, which is the APG\n * behaviour — activating it would close the menu and leave the submenu\n * unreachable from the keyboard.\n */\n async activateByKeyboard(): Promise<void> {\n return (await this.host()).sendKeys(TestKey.ENTER);\n }\n\n /**\n * Open this item's submenu with the right arrow and wait until it says so.\n *\n * The keyboard, not the pointer: it opens with no delay (hover waits out\n * 120ms — `openSubmenuByHover()` covers that path) and needs no coordinates,\n * which jsdom could not supply anyway. A keyboard open walks INTO the submenu,\n * so afterwards the cursor is on the submenu's first row and `isFocused()` on\n * this item is `false` — `openSubmenuByHover()` only shows the pane and leaves\n * the cursor where it was.\n */\n async openSubmenu(timeout = DEFAULT_TIMEOUT): Promise<void> {\n await this.assertHasSubmenu('openSubmenu');\n if (await this.isSubmenuOpen()) return;\n\n await (await this.host()).sendKeys(TestKey.RIGHT_ARROW);\n if (await this.isSubmenuOpen()) return;\n if (await this.settled(true, timeout)) return;\n\n throw new Error(\n `WrContextMenuItemHarness.openSubmenu(): \"${await this.getText()}\" did not open its submenu within ` +\n `${timeout}ms. A disabled item refuses the key outright — check isDisabled().`\n );\n }\n\n /**\n * Open this item's submenu by resting the pointer on it, the gesture a mouse\n * user actually makes, and wait out the hover delay.\n */\n async openSubmenuByHover(timeout = DEFAULT_TIMEOUT): Promise<void> {\n await this.assertHasSubmenu('openSubmenuByHover');\n if (await this.isSubmenuOpen()) return;\n\n await (await this.host()).hover();\n if (await this.settled(true, timeout)) return;\n\n throw new Error(\n `WrContextMenuItemHarness.openSubmenuByHover(): \"${await this.getText()}\" did not open its submenu ` +\n `within ${timeout}ms. The hover path is a real 120ms timer, so a spec on fake timers has to ` +\n 'advance the clock itself; a disabled item ignores the pointer entirely.'\n );\n }\n\n /** Close this item's submenu with the left arrow. An item without one open is left alone. */\n async closeSubmenu(timeout = DEFAULT_TIMEOUT): Promise<void> {\n if (!(await this.isSubmenuOpen())) return;\n\n await (await this.host()).sendKeys(TestKey.LEFT_ARROW);\n if (await this.settled(false, timeout)) return;\n\n throw new Error(\n `WrContextMenuItemHarness.closeSubmenu(): \"${await this.getText()}\" still reports its submenu open ` +\n `after ${timeout}ms.`\n );\n }\n\n /**\n * The items in THIS item's submenu, in DOM order.\n *\n * Throws while the submenu is shut, and while the item owns none: both would\n * otherwise answer with an empty array, which reads like a submenu that\n * rendered nothing and fails somewhere else entirely.\n */\n async getSubmenuItems(filters: WrContextMenuItemHarnessFilters = {}): Promise<WrContextMenuItemHarness[]> {\n const loader = await this.submenuLoader();\n return loader.getAllHarnesses(WrContextMenuItemHarness.with(filters));\n }\n\n /** The labels of the items in this item's submenu, in DOM order. */\n async getSubmenuItemTexts(): Promise<string[]> {\n const items = await this.getSubmenuItems();\n return Promise.all(items.map(item => item.getText()));\n }\n\n /**\n * Open the submenu if it is shut, then click the first item in it matching the\n * filters.\n *\n * Picking a leaf dismisses the whole chain — this item's submenu and the root\n * menu with it.\n */\n async clickSubmenuItem(filters: WrContextMenuItemHarnessFilters): Promise<void> {\n await this.openSubmenu();\n\n const [item] = await this.getSubmenuItems(filters);\n if (!item) {\n const offered = await this.getSubmenuItemTexts();\n throw new Error(\n `WrContextMenuItemHarness.clickSubmenuItem(): no item matched ${JSON.stringify(filters)}. ` +\n `The submenu of \"${await this.getText()}\" offers: ${offered.join(', ')}.`\n );\n }\n\n await item.click();\n }\n\n /**\n * A loader scoped to THIS item's submenu pane.\n *\n * Scoped by the published id rather than by `.wr-context-menu`: every pane in\n * the chain — the root menu and each open submenu — carries that class in one\n * shared overlay container, so a class query would answer with whichever\n * opened first.\n */\n private async submenuLoader(): Promise<HarnessLoader> {\n return this.documentRootLocatorFactory().harnessLoaderFor(`#${await this.submenuId()}`);\n }\n\n /** The id this item publishes as `aria-controls`, which its open submenu carries. */\n private async submenuId(): Promise<string> {\n const id = await (await this.host()).getAttribute('aria-controls');\n if (!id) {\n const label = await this.getText();\n throw new Error(\n `WrContextMenuItemHarness: \"${label}\" has no submenu showing. An item publishes aria-controls only ` +\n 'while its submenu is open — call openSubmenu() first, and check that it owns one at all with ' +\n 'hasSubmenu().'\n );\n }\n return id;\n }\n\n /** Fail early, and by name, when the item is not a drill-down at all. */\n private async assertHasSubmenu(method: string): Promise<void> {\n if (await this.hasSubmenu()) return;\n\n throw new Error(\n `WrContextMenuItemHarness.${method}(): \"${await this.getText()}\" owns no submenu, so there is ` +\n 'nothing to open. An item that does advertises it with aria-haspopup=\"menu\" — hasSubmenu().'\n );\n }\n\n /** Poll until `isSubmenuOpen()` reports `open`, and say whether it did. */\n private async settled(open: boolean, timeout: number): Promise<boolean> {\n for (let waited = 0; waited <= timeout; waited += POLL_STEP) {\n if ((await this.isSubmenuOpen()) === open) return true;\n\n await sleep(POLL_STEP);\n await this.forceStabilize();\n }\n return false;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport {\n ContentContainerComponentHarness,\n type HarnessLoader,\n HarnessPredicate,\n TestKey,\n type TestElement,\n} from '@angular/cdk/testing';\n\nimport type { WrContextMenuHarnessFilters, WrContextMenuItemHarnessFilters } from './interfaces';\nimport { DEFAULT_TIMEOUT, POLL_STEP, sleep } from './wait';\nimport { WrContextMenuItemHarness } from './wr-context-menu-item-harness';\n\n/**\n * How long to sit still before an outside press is allowed to dismiss the menu.\n *\n * The directive ignores outside events for 200ms after opening, because the very\n * right-click that opened the menu still has its own `mouseup` / `auxclick`\n * pending — without the window, lifting the finger would shut the menu again.\n */\nconst OUTSIDE_GUARD_MS = 250;\n\n/**\n * Test harness for `[wrContextMenu]` — the right-click target, and the menu it\n * opens.\n *\n * The harness matches the TARGET, because that is the only part of a context\n * menu in the fixture: `<wr-context-menu>` renders nothing of its own (it hands\n * over an `<ng-template>` and hides its host), and the menu is portalled into the\n * overlay container, a sibling of the whole app. So every item is read through\n * the document root, scoped by the `aria-controls` id the target publishes while\n * its menu is up — which is what keeps two menus on one page from answering with\n * each other's items, and what keeps a re-opened menu from answering with the one\n * still playing its exit animation.\n *\n * That scoped loader is also this harness's CONTENT loader, so\n * `menu.getHarness(…)` resolves inside THIS menu: a component the consumer\n * projected into the menu is reachable without the spec ever touching the\n * overlay.\n *\n * **How it differs from `WrDropdownHarness`, its closest sibling.**\n *\n * - The gesture is a real `contextmenu` event, not a click — `open()` sends one,\n * and `openByLongPress()` covers the touch path. There is no toggle: a second\n * right-click re-opens the menu at the new pointer position.\n * - Activating an item DISMISSES the menu (a dropdown's stays up), so\n * `clickItem()` leaves it closed.\n * - Submenus are a first-class input here rather than a nested trigger, so they\n * are driven from {@link WrContextMenuItemHarness}.\n * - Focus moves in on EVERY open, where a dropdown's does so only when a\n * keyboard opened it. There is no hover trigger here — a right-click, Shift+F10\n * or a long-press is always a deliberate request for the menu — so\n * `getFocusedItemText()` answers straight after `open()`.\n *\n * **Timing.** The waits are real: a 500ms `setTimeout` for the long-press hold and\n * another 220ms one for the exit animation the pane is held alive through, plus the\n * outside-press window above — that one is a `performance.now()` comparison against\n * the moment the menu opened rather than a timer, so only wall-clock time gets past\n * it. The methods that must sit out real time say so; a spec on fake timers should\n * drive the clock itself and read `isOpen()`.\n *\n * @example\n * ```ts\n * const menu = await loader.getHarness(WrContextMenuHarness.with({ text: 'Right-click me' }));\n *\n * await menu.open();\n * expect(await menu.getItemTexts()).toEqual(['Cut', 'Copy', 'More']);\n *\n * await menu.clickItem({ text: 'Cut' });\n * expect(await menu.isOpen()).toBe(false);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrContextMenuHarness extends ContentContainerComponentHarness {\n /** The directive's host — whatever element the consumer put `[wrContextMenu]` on. */\n static hostSelector = '.wr-context-menu-host';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrContextMenuHarnessFilters = {}): HarnessPredicate<WrContextMenuHarness> {\n return new HarnessPredicate(WrContextMenuHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getTargetText(), text))\n .addOption('open', options.open, async (harness, open) => (await harness.isOpen()) === open);\n }\n\n /** The target's own visible text — the content that gets right-clicked. */\n async getTargetText(): Promise<string> {\n return (await this.host()).text();\n }\n\n /**\n * Whether this target's menu is showing.\n *\n * Read from the target rather than from the overlay, and that is the whole\n * point: a closed menu's pane stays in the DOM for its exit animation, while\n * the reference is dropped the instant the close begins. Asking the DOM\n * \"is a menu present\" would answer `true` for a quarter of a second after every\n * dismissal.\n *\n * So the answer is only as good as the directive's claim on `aria-controls`:\n * put `[wrContextMenu]` on an element that publishes one of its own and the two\n * fight over a single attribute.\n */\n async isOpen(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-controls')) !== null;\n }\n\n /** The role the menu announces — `menu`. Throws while no menu is open. */\n async getMenuRole(): Promise<string | null> {\n return (await this.menu()).getAttribute('role');\n }\n\n /**\n * Everything the open menu renders as text, runs of whitespace collapsed.\n *\n * Wider than `getItemTexts()` on purpose: `<wr-context-menu>` projects whatever\n * it is given, so a section heading or any other markup a consumer put between\n * the items shows up here and in no item harness. Dividers contribute nothing —\n * they render no text.\n *\n * Expect to match a FRAGMENT of this rather than the whole of it: Angular\n * strips the whitespace between elements at compile time, so the labels run\n * together (`'Pick oneAppleBanana'`). The collapsing here only tidies newlines\n * inside a single block of projected text.\n */\n async getMenuText(): Promise<string> {\n return (await (await this.menu()).text()).replace(/\\s+/g, ' ').trim();\n }\n\n /**\n * Open the menu by right-clicking the target, the gesture the directive\n * actually listens for (`contextmenu`, which is also what Shift+F10 sends).\n *\n * An already-open menu is left alone — `rightClick()` is the raw gesture if\n * what you want is the re-open.\n *\n * Note for a spec with two targets: a `contextmenu` event anywhere is an\n * outside pointer event for every OTHER open menu, so this dismisses them on\n * the way in. `openByLongPress()` is the route that does not.\n */\n async open(): Promise<void> {\n if (await this.isOpen()) return;\n\n await this.rightClick();\n if (await this.isOpen()) return;\n\n throw new Error(\n 'WrContextMenuHarness.open(): the right-click did not open a menu. The directive answers ' +\n '`contextmenu` on its own host, so check this is the element you put [wrContextMenu] on. One ' +\n 'deliberate exception: a right-click within 700ms of a long-press open is swallowed, because touch ' +\n 'browsers fire a synthetic contextmenu after the hold.'\n );\n }\n\n /**\n * Send a `contextmenu` to the target whatever state the menu is in.\n *\n * On an open menu this re-opens it at the new pointer position — the previous\n * pane is dismissed and a fresh one takes its place, which is why the harness\n * scopes by a per-open id rather than by class: for the length of the exit\n * animation both are in the container, the dying one first.\n *\n * The coordinates the CDK requires are all zeros, and meaningless: jsdom has no\n * layout. Nothing here needs them — the event is dispatched on the element\n * itself, no hit test involved.\n */\n async rightClick(): Promise<void> {\n return (await this.host()).rightClick(0, 0);\n }\n\n /**\n * Open the menu with a touch long-press — hold a non-mouse pointer on the\n * target for 500ms — and wait until it is up.\n *\n * Worth its own method rather than an argument to `open()`, for the reason the\n * dropdown has `openByKeyboard()`: it is the only way to get TWO context menus\n * open at once. The press emits no `click` / `auxclick` / `contextmenu`, so it\n * is not an outside press for anyone else's menu, while a right-click is.\n *\n * The pointer fields the directive reads are assigned onto a synthetic event:\n * what matters is a primary pointer that is not a mouse. Movement beyond 10px,\n * a scroll, or an early lift cancels the hold in the component — none of which\n * this does.\n */\n async openByLongPress(timeout = DEFAULT_TIMEOUT): Promise<void> {\n if (await this.isOpen()) return;\n\n const host = await this.host();\n await host.dispatchEvent('pointerdown', {\n pointerType: 'touch',\n isPrimary: true,\n clientX: 0,\n clientY: 0,\n pageX: 0,\n pageY: 0,\n });\n\n const opened = await this.settled(true, timeout);\n // The finger comes off once the menu is up — after the hold, so it reads as a\n // completed long-press rather than the early lift that cancels one.\n await host.dispatchEvent('pointerup');\n\n if (opened) return;\n\n throw new Error(\n `WrContextMenuHarness.openByLongPress(): nothing opened within ${timeout}ms. The hold is a real 500ms ` +\n 'timer, so a spec on fake timers has to advance the clock itself and read isOpen().'\n );\n }\n\n /**\n * Dismiss the menu with Escape and wait until its pane has left the DOM.\n *\n * Escape is delivered by the CDK's keyboard dispatcher to the TOP-MOST overlay,\n * so with a later menu (or any other overlay) open on top, this one keeps\n * waiting its turn — that is what the throw below is about. The wait covers the\n * 220ms exit animation, so a spec can assert afterwards that nothing lingers.\n */\n async close(timeout = DEFAULT_TIMEOUT): Promise<void> {\n if (!(await this.isOpen())) return;\n\n // Captured first: the target drops the reference the instant the close starts,\n // and the wait below still has to know which pane to watch.\n const id = await this.menuId();\n await (await this.host()).sendKeys(TestKey.ESCAPE);\n\n if (await this.isOpen()) {\n throw new Error(\n 'WrContextMenuHarness.close(): Escape did not dismiss the menu. The CDK keyboard dispatcher ' +\n 'delivers it to the TOP-MOST overlay only, so anything opened after this menu takes it first.'\n );\n }\n\n return this.waitUntilGone(id, 'close', timeout);\n }\n\n /**\n * Dismiss the menu by pressing outside it, the way a user gets rid of one, and\n * wait until its pane has left the DOM.\n *\n * Sits out real time first: the directive ignores outside events for 200ms after\n * opening, so the right-click that opened the menu cannot immediately close it\n * again with its own trailing `auxclick`. The press then lands on the TARGET,\n * which is outside the pane and therefore counts — the page background is not\n * something a harness has a handle on. A target that carries a `(click)` of its\n * own will see it fire, so assert on the dismissal rather than on that handler.\n */\n async closeByOutsidePress(timeout = DEFAULT_TIMEOUT): Promise<void> {\n if (!(await this.isOpen())) return;\n\n const id = await this.menuId();\n await sleep(OUTSIDE_GUARD_MS);\n await this.forceStabilize();\n await (await this.host()).click();\n\n if (await this.isOpen()) {\n throw new Error(\n 'WrContextMenuHarness.closeByOutsidePress(): the menu is still open. A press inside the pane is ' +\n 'not an outside press, and neither is one that started inside it — this one lands on the target.'\n );\n }\n\n return this.waitUntilGone(id, 'closeByOutsidePress', timeout);\n }\n\n /**\n * The items in this menu, in DOM order.\n *\n * Only this menu's own: an open submenu is a separate pane, so its items are\n * `WrContextMenuItemHarness.getSubmenuItems()`, not part of this list. Dividers\n * are not items either.\n *\n * Throws while the menu is shut — the items do not exist anywhere until the\n * portal is attached, and an empty array reads like a menu that rendered\n * nothing.\n */\n async getItems(filters: WrContextMenuItemHarnessFilters = {}): Promise<WrContextMenuItemHarness[]> {\n return this.getAllHarnesses(WrContextMenuItemHarness.with(filters));\n }\n\n /** The labels of this menu's items, in DOM order. */\n async getItemTexts(): Promise<string[]> {\n const items = await this.getItems();\n return Promise.all(items.map(item => item.getText()));\n }\n\n /**\n * The label of the item that currently holds focus, or `null` when the\n * keyboard is elsewhere.\n *\n * The menu roves focus rather than drawing a cursor — it lands on the first\n * ENABLED item as the pane renders, and the arrows walk it over the enabled\n * items only — so this is the only way to ask where the keyboard is. It answers\n * `null` once a submenu has taken the cursor: that pane is a separate overlay,\n * and its own rows are reached through `WrContextMenuItemHarness`.\n */\n async getFocusedItemText(): Promise<string | null> {\n for (const item of await this.getItems()) {\n if (await item.isFocused()) return item.getText();\n }\n return null;\n }\n\n /**\n * How many separators this menu shows.\n *\n * Counted by `role=\"separator\"` rather than by `.wr-context-menu-divider`, so a\n * separator the consumer wrote themselves counts too — the role is what tells a\n * screen reader where one group of items ends. A separator is never a member of\n * `getItems()`.\n */\n async getDividerCount(): Promise<number> {\n const separators = await this.inMenuAll('[role=\"separator\"]');\n return separators.length;\n }\n\n /**\n * Open the menu if it is shut, then click the first item matching the filters.\n *\n * Picking a leaf DISMISSES the menu — unlike a dropdown, which stays up. The\n * dismissal is reported at once (the target drops its reference synchronously),\n * while the pane plays its exit animation for another 220ms.\n */\n async clickItem(filters: WrContextMenuItemHarnessFilters): Promise<void> {\n await this.open();\n\n const [item] = await this.getItems(filters);\n if (!item) {\n const offered = await this.getItemTexts();\n throw new Error(\n `WrContextMenuHarness.clickItem(): no item matched ${JSON.stringify(filters)}. ` +\n `The menu offers: ${offered.join(', ')}.`\n );\n }\n\n await item.click();\n }\n\n /**\n * A loader scoped to THIS target's menu — the content loader every\n * `HarnessLoader` method on this class runs through, `getItems()` included.\n *\n * Scoped by the published id rather than by `.wr-context-menu`: the overlay\n * container is shared by every pane on the page, so a bare class query would\n * answer with whichever menu opened first.\n */\n protected override async getRootHarnessLoader(): Promise<HarnessLoader> {\n return this.documentRootLocatorFactory().harnessLoaderFor(`#${await this.menuId()}`);\n }\n\n /** This target's menu element in the overlay. */\n private async menu(): Promise<TestElement> {\n return this.documentRootLocatorFactory().locatorFor(`#${await this.menuId()}`)();\n }\n\n /** Every element inside THIS menu matching the selector. */\n private async inMenuAll(selector: string): Promise<TestElement[]> {\n return this.documentRootLocatorFactory().locatorForAll(`#${await this.menuId()} ${selector}`)();\n }\n\n /** The id the target publishes as `aria-controls`, which its open menu carries. */\n private async menuId(): Promise<string> {\n const id = await (await this.host()).getAttribute('aria-controls');\n if (!id) {\n throw new Error(\n 'WrContextMenuHarness: no menu is open, so there is nothing inside it to read — call open() ' +\n 'first. The target publishes `aria-controls` only while its menu is up.'\n );\n }\n return id;\n }\n\n /** Poll until the pane with this id has been disposed. */\n private async waitUntilGone(id: string, method: string, timeout: number): Promise<void> {\n const locate = this.documentRootLocatorFactory().locatorForOptional(`#${id}`);\n\n for (let waited = 0; waited <= timeout; waited += POLL_STEP) {\n if ((await locate()) === null) return;\n\n await sleep(POLL_STEP);\n await this.forceStabilize();\n }\n\n throw new Error(\n `WrContextMenuHarness.${method}(): the menu was still in the DOM ${timeout}ms after it closed. The ` +\n 'directive keeps the pane alive for its 220ms exit animation and disposes it on a real timer, so a ' +\n 'spec on fake timers has to advance the clock itself.'\n );\n }\n\n /** Poll until `isOpen()` reports `open`, and say whether it did. */\n private async settled(open: boolean, timeout: number): Promise<boolean> {\n for (let waited = 0; waited <= timeout; waited += POLL_STEP) {\n if ((await this.isOpen()) === open) return true;\n\n await sleep(POLL_STEP);\n await this.forceStabilize();\n }\n return false;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|