ngwr 10.2.1 → 11.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -19
- package/action-sheet/styles/_index.scss +4 -1
- package/anchor/styles/_index.scss +6 -5
- package/back-top/styles/_index.scss +6 -1
- package/blur-text/styles/_index.scss +13 -0
- package/button/styles/_index.scss +11 -6
- package/calendar/styles/_index.scss +11 -2
- package/carousel/styles/_index.scss +13 -2
- package/cascader/styles/_index.scss +5 -3
- package/circular-text/styles/_index.scss +2 -0
- package/collapse/styles/_index.scss +1 -1
- package/color-picker/styles/_index.scss +6 -1
- package/command-palette/styles/_index.scss +2 -1
- package/compare/styles/_index.scss +1 -2
- package/context-menu/styles/_index.scss +8 -2
- package/descriptions/styles/_index.scss +2 -2
- package/dialog/styles/_index.scss +7 -0
- package/drawer/styles/_index.scss +20 -1
- package/dropdown/styles/_index.scss +19 -5
- package/empty/styles/_index.scss +10 -0
- package/falling-text/styles/_index.scss +2 -1
- package/fesm2022/ngwr-action-sheet.mjs +13 -3
- package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
- package/fesm2022/ngwr-affix.mjs +40 -13
- package/fesm2022/ngwr-affix.mjs.map +1 -1
- package/fesm2022/ngwr-anchor.mjs +18 -6
- package/fesm2022/ngwr-anchor.mjs.map +1 -1
- package/fesm2022/ngwr-aurora.mjs +2 -2
- package/fesm2022/ngwr-aurora.mjs.map +1 -1
- package/fesm2022/ngwr-avatar.mjs +31 -7
- package/fesm2022/ngwr-avatar.mjs.map +1 -1
- package/fesm2022/ngwr-back-top.mjs +6 -2
- package/fesm2022/ngwr-back-top.mjs.map +1 -1
- package/fesm2022/ngwr-badge.mjs +1 -1
- package/fesm2022/ngwr-badge.mjs.map +1 -1
- package/fesm2022/ngwr-bar-chart.mjs +30 -9
- package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
- package/fesm2022/ngwr-blur-text.mjs +2 -2
- package/fesm2022/ngwr-blur-text.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow.mjs +2 -2
- package/fesm2022/ngwr-border-glow.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +2 -2
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-burger.mjs +9 -4
- package/fesm2022/ngwr-burger.mjs.map +1 -1
- package/fesm2022/ngwr-button-testing.mjs +96 -0
- package/fesm2022/ngwr-button-testing.mjs.map +1 -0
- package/fesm2022/ngwr-button.mjs +31 -14
- package/fesm2022/ngwr-button.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-heatmap.mjs +40 -8
- package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
- package/fesm2022/ngwr-calendar.mjs +20 -1
- package/fesm2022/ngwr-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +2 -2
- package/fesm2022/ngwr-card.mjs.map +1 -1
- package/fesm2022/ngwr-carousel-testing.mjs +377 -0
- package/fesm2022/ngwr-carousel-testing.mjs.map +1 -0
- package/fesm2022/ngwr-carousel.mjs +67 -13
- package/fesm2022/ngwr-carousel.mjs.map +1 -1
- package/fesm2022/ngwr-cascader-testing.mjs +534 -0
- package/fesm2022/ngwr-cascader-testing.mjs.map +1 -0
- package/fesm2022/ngwr-cascader.mjs +2 -2
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox-testing.mjs +90 -0
- package/fesm2022/ngwr-checkbox-testing.mjs.map +1 -0
- package/fesm2022/ngwr-checkbox.mjs +9 -3
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text.mjs +2 -2
- package/fesm2022/ngwr-circular-text.mjs.map +1 -1
- package/fesm2022/ngwr-click-spark.mjs +2 -2
- package/fesm2022/ngwr-click-spark.mjs.map +1 -1
- package/fesm2022/ngwr-collapse-testing.mjs +368 -0
- package/fesm2022/ngwr-collapse-testing.mjs.map +1 -0
- package/fesm2022/ngwr-color-picker.mjs +134 -14
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-command-palette-testing.mjs +589 -0
- package/fesm2022/ngwr-command-palette-testing.mjs.map +1 -0
- package/fesm2022/ngwr-command-palette.mjs +58 -8
- package/fesm2022/ngwr-command-palette.mjs.map +1 -1
- package/fesm2022/ngwr-compare.mjs +103 -13
- package/fesm2022/ngwr-compare.mjs.map +1 -1
- package/fesm2022/ngwr-config.mjs +106 -0
- package/fesm2022/ngwr-config.mjs.map +1 -0
- package/fesm2022/ngwr-context-menu-testing.mjs +631 -0
- package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -0
- package/fesm2022/ngwr-context-menu.mjs +94 -8
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-cookie.mjs +5 -1
- package/fesm2022/ngwr-cookie.mjs.map +1 -1
- package/fesm2022/ngwr-counter.mjs +11 -3
- package/fesm2022/ngwr-counter.mjs.map +1 -1
- package/fesm2022/ngwr-date-adapter-luxon.mjs +52 -4
- package/fesm2022/ngwr-date-adapter-luxon.mjs.map +1 -1
- package/fesm2022/ngwr-date-adapter.mjs +54 -8
- package/fesm2022/ngwr-date-adapter.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker-testing.mjs +766 -0
- package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -0
- package/fesm2022/ngwr-date-picker.mjs +73 -3
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-decrypt-text.mjs +27 -9
- package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
- package/fesm2022/ngwr-dialog-testing.mjs +89 -0
- package/fesm2022/ngwr-dialog-testing.mjs.map +1 -0
- package/fesm2022/ngwr-directives.mjs +36 -12
- package/fesm2022/ngwr-directives.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart.mjs +22 -5
- package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +2 -2
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
- package/fesm2022/ngwr-drawer-testing.mjs +250 -0
- package/fesm2022/ngwr-drawer-testing.mjs.map +1 -0
- package/fesm2022/ngwr-dropdown-testing.mjs +291 -0
- package/fesm2022/ngwr-dropdown-testing.mjs.map +1 -0
- package/fesm2022/ngwr-dropdown.mjs +10 -2
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-empty.mjs +24 -7
- package/fesm2022/ngwr-empty.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar.mjs +57 -10
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text.mjs +2 -2
- package/fesm2022/ngwr-falling-text.mjs.map +1 -1
- package/fesm2022/ngwr-file-upload-testing.mjs +332 -0
- package/fesm2022/ngwr-file-upload-testing.mjs.map +1 -0
- package/fesm2022/ngwr-form-testing.mjs +451 -0
- package/fesm2022/ngwr-form-testing.mjs.map +1 -0
- package/fesm2022/ngwr-form.mjs +19 -3
- package/fesm2022/ngwr-form.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text.mjs +9 -2
- package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
- package/fesm2022/ngwr-gauge.mjs +19 -5
- package/fesm2022/ngwr-gauge.mjs.map +1 -1
- package/fesm2022/ngwr-glitch-text.mjs +2 -2
- package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
- package/fesm2022/ngwr-gradient-text.mjs +2 -2
- package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +50 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +48 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper.mjs +23 -0
- package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
- package/fesm2022/ngwr-input-number-testing.mjs +306 -0
- package/fesm2022/ngwr-input-number-testing.mjs.map +1 -0
- package/fesm2022/ngwr-input-number.mjs +35 -3
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp-testing.mjs +419 -0
- package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -0
- package/fesm2022/ngwr-input-otp.mjs +36 -5
- package/fesm2022/ngwr-input-otp.mjs.map +1 -1
- package/fesm2022/ngwr-input-testing.mjs +102 -0
- package/fesm2022/ngwr-input-testing.mjs.map +1 -0
- package/fesm2022/ngwr-input.mjs +47 -10
- package/fesm2022/ngwr-input.mjs.map +1 -1
- package/fesm2022/ngwr-knob.mjs +44 -8
- package/fesm2022/ngwr-knob.mjs.map +1 -1
- package/fesm2022/ngwr-layout.mjs +21 -5
- package/fesm2022/ngwr-layout.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox.mjs +33 -6
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart.mjs +17 -4
- package/fesm2022/ngwr-line-chart.mjs.map +1 -1
- package/fesm2022/ngwr-list.mjs +2 -2
- package/fesm2022/ngwr-list.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar.mjs +2 -2
- package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
- package/fesm2022/ngwr-markdown-testing.mjs +516 -0
- package/fesm2022/ngwr-markdown-testing.mjs.map +1 -0
- package/fesm2022/ngwr-markdown.mjs +1492 -0
- package/fesm2022/ngwr-markdown.mjs.map +1 -0
- package/fesm2022/ngwr-marquee.mjs +17 -6
- package/fesm2022/ngwr-marquee.mjs.map +1 -1
- package/fesm2022/ngwr-mention-testing.mjs +513 -0
- package/fesm2022/ngwr-mention-testing.mjs.map +1 -0
- package/fesm2022/ngwr-mention.mjs +10 -1
- package/fesm2022/ngwr-mention.mjs.map +1 -1
- package/fesm2022/ngwr-meta.mjs +22 -7
- package/fesm2022/ngwr-meta.mjs.map +1 -1
- package/fesm2022/ngwr-meter-group.mjs +19 -5
- package/fesm2022/ngwr-meter-group.mjs.map +1 -1
- package/fesm2022/ngwr-overlay.mjs +41 -1
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-pagination-testing.mjs +389 -0
- package/fesm2022/ngwr-pagination-testing.mjs.map +1 -0
- package/fesm2022/ngwr-pagination.mjs +14 -2
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-popconfirm-testing.mjs +309 -0
- package/fesm2022/ngwr-popconfirm-testing.mjs.map +1 -0
- package/fesm2022/ngwr-popconfirm.mjs +95 -17
- package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
- package/fesm2022/ngwr-popover-testing.mjs +316 -0
- package/fesm2022/ngwr-popover-testing.mjs.map +1 -0
- package/fesm2022/ngwr-popover.mjs +21 -3
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-pull-to-refresh.mjs +17 -2
- package/fesm2022/ngwr-pull-to-refresh.mjs.map +1 -1
- package/fesm2022/ngwr-qr.mjs +13 -3
- package/fesm2022/ngwr-qr.mjs.map +1 -1
- package/fesm2022/ngwr-radio-testing.mjs +392 -0
- package/fesm2022/ngwr-radio-testing.mjs.map +1 -0
- package/fesm2022/ngwr-radio.mjs +10 -3
- package/fesm2022/ngwr-radio.mjs.map +1 -1
- package/fesm2022/ngwr-rating-testing.mjs +384 -0
- package/fesm2022/ngwr-rating-testing.mjs.map +1 -0
- package/fesm2022/ngwr-rating.mjs +37 -2
- package/fesm2022/ngwr-rating.mjs.map +1 -1
- package/fesm2022/ngwr-segmented-testing.mjs +512 -0
- package/fesm2022/ngwr-segmented-testing.mjs.map +1 -0
- package/fesm2022/ngwr-select-testing.mjs +268 -0
- package/fesm2022/ngwr-select-testing.mjs.map +1 -0
- package/fesm2022/ngwr-select.mjs +87 -16
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text.mjs +2 -2
- package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
- package/fesm2022/ngwr-sidebar.mjs +65 -12
- package/fesm2022/ngwr-sidebar.mjs.map +1 -1
- package/fesm2022/ngwr-slider-testing.mjs +478 -0
- package/fesm2022/ngwr-slider-testing.mjs.map +1 -0
- package/fesm2022/ngwr-slider.mjs +57 -13
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline.mjs +26 -6
- package/fesm2022/ngwr-sparkline.mjs.map +1 -1
- package/fesm2022/ngwr-speed-dial.mjs +37 -4
- package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
- package/fesm2022/ngwr-spinner.mjs +12 -3
- package/fesm2022/ngwr-spinner.mjs.map +1 -1
- package/fesm2022/ngwr-splash-cursor.mjs +2 -2
- package/fesm2022/ngwr-splash-cursor.mjs.map +1 -1
- package/fesm2022/ngwr-split-text.mjs +2 -2
- package/fesm2022/ngwr-split-text.mjs.map +1 -1
- package/fesm2022/ngwr-splitter.mjs +70 -9
- package/fesm2022/ngwr-splitter.mjs.map +1 -1
- package/fesm2022/ngwr-spotlight-card.mjs +2 -2
- package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
- package/fesm2022/ngwr-star-border.mjs +2 -2
- package/fesm2022/ngwr-star-border.mjs.map +1 -1
- package/fesm2022/ngwr-statistic.mjs +24 -7
- package/fesm2022/ngwr-statistic.mjs.map +1 -1
- package/fesm2022/ngwr-stepper-testing.mjs +477 -0
- package/fesm2022/ngwr-stepper-testing.mjs.map +1 -0
- package/fesm2022/ngwr-switch-testing.mjs +77 -0
- package/fesm2022/ngwr-switch-testing.mjs.map +1 -0
- package/fesm2022/ngwr-switch.mjs +9 -3
- package/fesm2022/ngwr-switch.mjs.map +1 -1
- package/fesm2022/ngwr-table-testing.mjs +580 -0
- package/fesm2022/ngwr-table-testing.mjs.map +1 -0
- package/fesm2022/ngwr-table.mjs +87 -14
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-tabs-testing.mjs +617 -0
- package/fesm2022/ngwr-tabs-testing.mjs.map +1 -0
- package/fesm2022/ngwr-tabs.mjs +38 -6
- package/fesm2022/ngwr-tabs.mjs.map +1 -1
- package/fesm2022/ngwr-textarea-testing.mjs +238 -0
- package/fesm2022/ngwr-textarea-testing.mjs.map +1 -0
- package/fesm2022/ngwr-textarea.mjs +32 -4
- package/fesm2022/ngwr-textarea.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card.mjs +2 -2
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
- package/fesm2022/ngwr-toast-testing.mjs +101 -0
- package/fesm2022/ngwr-toast-testing.mjs.map +1 -0
- package/fesm2022/ngwr-toast.mjs +2 -2
- package/fesm2022/ngwr-toast.mjs.map +1 -1
- package/fesm2022/ngwr-tour.mjs +61 -9
- package/fesm2022/ngwr-tour.mjs.map +1 -1
- package/fesm2022/ngwr-transfer-testing.mjs +538 -0
- package/fesm2022/ngwr-transfer-testing.mjs.map +1 -0
- package/fesm2022/ngwr-transfer.mjs +45 -14
- package/fesm2022/ngwr-transfer.mjs.map +1 -1
- package/fesm2022/ngwr-tree-testing.mjs +734 -0
- package/fesm2022/ngwr-tree-testing.mjs.map +1 -0
- package/fesm2022/ngwr-tree.mjs +73 -7
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter.mjs +5 -3
- package/fesm2022/ngwr-typewriter.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 +31 -5
- package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
- package/fesm2022/ngwr-waves.mjs +3 -3
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/fesm2022/ngwr-window.mjs +8 -0
- package/fesm2022/ngwr-window.mjs.map +1 -1
- package/file-upload/styles/_index.scss +7 -2
- package/gauge/styles/_index.scss +1 -1
- package/glitch-text/styles/_index.scss +4 -0
- package/i18n/en.json +48 -0
- package/i18n/ru.json +48 -0
- package/image-cropper/styles/_index.scss +13 -1
- package/input/styles/_index.scss +2 -2
- package/input-number/styles/_index.scss +7 -2
- package/knob/styles/_index.scss +8 -1
- package/layout/styles/_index.scss +3 -3
- package/lightbox/styles/_index.scss +10 -2
- package/line-chart/styles/_index.scss +6 -0
- package/llms-full.txt +151 -4
- package/llms.txt +57 -8
- package/loading-bar/styles/_index.scss +1 -2
- package/markdown/styles/_index.scss +296 -0
- package/marquee/styles/_index.scss +12 -0
- package/mcp/api.js +512 -0
- package/mcp/catalog.js +157 -0
- package/mcp/server.js +273 -0
- package/mcp/tools.js +366 -0
- package/meter-group/styles/_index.scss +1 -1
- package/package.json +186 -35
- package/pagination/styles/_index.scss +6 -6
- package/popover/styles/_index.scss +37 -5
- package/rating/styles/_index.scss +10 -0
- package/result/styles/_index.scss +3 -1
- package/schematics/use/symbol-map.json +2 -0
- package/segmented/styles/_index.scss +12 -1
- package/select/styles/_index.scss +32 -10
- package/sidebar/styles/_index.scss +30 -5
- package/slider/styles/_index.scss +15 -0
- package/speed-dial/styles/_index.scss +20 -0
- package/split-text/styles/_index.scss +13 -0
- package/star-border/styles/_index.scss +2 -0
- package/stepper/styles/_index.scss +19 -3
- package/styles/_grid.scss +4 -4
- package/styles.scss +1 -0
- package/switch/styles/_index.scss +5 -3
- package/table/styles/_index.scss +25 -12
- package/tabs/styles/_index.scss +11 -1
- package/textarea/styles/_index.scss +5 -0
- package/theme/styles/_colors.scss +44 -15
- package/theme/styles/_dark.scss +35 -15
- package/theme/styles/_mixins.scss +3 -0
- package/timeline/styles/_index.scss +2 -2
- package/toast/styles/_index.scss +17 -8
- package/toolbar/styles/_index.scss +3 -3
- package/tour/styles/_index.scss +7 -1
- package/tree/styles/_index.scss +9 -5
- package/types/ngwr-action-sheet.d.ts +9 -1
- package/types/ngwr-affix.d.ts +7 -1
- package/types/ngwr-anchor.d.ts +8 -1
- package/types/ngwr-avatar.d.ts +13 -3
- package/types/ngwr-bar-chart.d.ts +16 -0
- package/types/ngwr-burger.d.ts +6 -2
- package/types/ngwr-button-testing.d.ts +85 -0
- package/types/ngwr-button.d.ts +26 -10
- package/types/ngwr-calendar-heatmap.d.ts +16 -2
- package/types/ngwr-calendar.d.ts +10 -0
- package/types/ngwr-carousel-testing.d.ts +257 -0
- package/types/ngwr-carousel.d.ts +20 -4
- package/types/ngwr-cascader-testing.d.ts +392 -0
- package/types/ngwr-checkbox-testing.d.ts +77 -0
- package/types/ngwr-checkbox.d.ts +7 -2
- package/types/ngwr-collapse-testing.d.ts +290 -0
- package/types/ngwr-color-picker.d.ts +20 -11
- package/types/ngwr-command-palette-testing.d.ts +437 -0
- package/types/ngwr-command-palette.d.ts +25 -2
- package/types/ngwr-compare.d.ts +50 -2
- package/types/ngwr-config.d.ts +176 -0
- package/types/ngwr-context-menu-testing.d.ts +401 -0
- package/types/ngwr-context-menu.d.ts +65 -17
- package/types/ngwr-counter.d.ts +6 -1
- package/types/ngwr-date-adapter-luxon.d.ts +26 -0
- package/types/ngwr-date-picker-testing.d.ts +518 -0
- package/types/ngwr-date-picker.d.ts +4 -0
- package/types/ngwr-dialog-testing.d.ts +79 -0
- package/types/ngwr-directives.d.ts +10 -1
- package/types/ngwr-donut-chart.d.ts +8 -1
- package/types/ngwr-drawer-testing.d.ts +196 -0
- package/types/ngwr-dropdown-testing.d.ts +202 -0
- package/types/ngwr-dropdown.d.ts +7 -1
- package/types/ngwr-empty.d.ts +19 -4
- package/types/ngwr-event-calendar.d.ts +26 -1
- package/types/ngwr-file-upload-testing.d.ts +194 -0
- package/types/ngwr-form-testing.d.ts +379 -0
- package/types/ngwr-form.d.ts +17 -0
- package/types/ngwr-gauge.d.ts +13 -1
- package/types/ngwr-image-cropper.d.ts +10 -0
- package/types/ngwr-input-number-testing.d.ts +226 -0
- package/types/ngwr-input-number.d.ts +29 -1
- package/types/ngwr-input-otp-testing.d.ts +299 -0
- package/types/ngwr-input-otp.d.ts +23 -2
- package/types/ngwr-input-testing.d.ts +84 -0
- package/types/ngwr-input.d.ts +20 -6
- package/types/ngwr-knob.d.ts +18 -0
- package/types/ngwr-layout.d.ts +1 -0
- package/types/ngwr-lightbox.d.ts +17 -3
- package/types/ngwr-line-chart.d.ts +9 -2
- package/types/ngwr-markdown-testing.d.ts +462 -0
- package/types/ngwr-markdown.d.ts +534 -0
- package/types/ngwr-marquee.d.ts +12 -2
- package/types/ngwr-mention-testing.d.ts +359 -0
- package/types/ngwr-meta.d.ts +4 -2
- package/types/ngwr-meter-group.d.ts +11 -1
- package/types/ngwr-overlay.d.ts +33 -2
- package/types/ngwr-pagination-testing.d.ts +291 -0
- package/types/ngwr-pagination.d.ts +1 -0
- package/types/ngwr-popconfirm-testing.d.ts +235 -0
- package/types/ngwr-popconfirm.d.ts +31 -21
- package/types/ngwr-popover-testing.d.ts +221 -0
- package/types/ngwr-popover.d.ts +9 -0
- package/types/ngwr-pull-to-refresh.d.ts +7 -0
- package/types/ngwr-qr.d.ts +9 -1
- package/types/ngwr-radio-testing.d.ts +296 -0
- package/types/ngwr-radio.d.ts +8 -2
- package/types/ngwr-rating-testing.d.ts +291 -0
- package/types/ngwr-rating.d.ts +23 -0
- package/types/ngwr-segmented-testing.d.ts +401 -0
- package/types/ngwr-select-testing.d.ts +175 -0
- package/types/ngwr-select.d.ts +45 -4
- package/types/ngwr-sidebar.d.ts +17 -2
- package/types/ngwr-slider-testing.d.ts +329 -0
- package/types/ngwr-slider.d.ts +35 -1
- package/types/ngwr-sparkline.d.ts +12 -2
- package/types/ngwr-speed-dial.d.ts +17 -18
- package/types/ngwr-spinner.d.ts +8 -1
- package/types/ngwr-splitter.d.ts +24 -1
- package/types/ngwr-statistic.d.ts +12 -2
- package/types/ngwr-stepper-testing.d.ts +346 -0
- package/types/ngwr-switch-testing.d.ts +66 -0
- package/types/ngwr-switch.d.ts +7 -2
- package/types/ngwr-table-testing.d.ts +423 -0
- package/types/ngwr-table.d.ts +42 -0
- package/types/ngwr-tabs-testing.d.ts +470 -0
- package/types/ngwr-tabs.d.ts +9 -0
- package/types/ngwr-textarea-testing.d.ts +184 -0
- package/types/ngwr-textarea.d.ts +7 -2
- package/types/ngwr-toast-testing.d.ts +80 -0
- package/types/ngwr-tour.d.ts +27 -0
- package/types/ngwr-transfer-testing.d.ts +444 -0
- package/types/ngwr-transfer.d.ts +16 -1
- package/types/ngwr-tree-testing.d.ts +501 -0
- package/types/ngwr-tree.d.ts +33 -0
- package/types/ngwr-version.d.ts +1 -1
- package/types/ngwr-virtual-scroll.d.ts +13 -1
- package/typewriter/styles/_index.scss +1 -1
- package/typography/styles/_index.scss +1 -1
- package/window/styles/_index.scss +32 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-overlay.mjs","sources":["../../../projects/lib/overlay/wr-overlay-close.ts","../../../projects/lib/overlay/wr-outside-click.ts","../../../projects/lib/overlay/wr-overlay-container.ts","../../../projects/lib/overlay/tokens/wr-overlay.token.ts","../../../projects/lib/overlay/tokens/wr-overlay-container.token.ts","../../../projects/lib/overlay/tokens/wr-responsive-overlays.token.ts","../../../projects/lib/overlay/provide-wr-overlay.ts","../../../projects/lib/overlay/provide-wr-responsive-overlays.ts","../../../projects/lib/overlay/ngwr-overlay.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/**\n * The same 24×24 stroked ✕ the rest of the library's chrome draws (alert, toast,\n * lightbox). Static markup — the caller's label goes on via `setAttribute`, so\n * nothing from a catalog or a consumer is ever parsed as HTML.\n */\nconst CLOSE_ICON =\n '<svg class=\"wr-icon__svg\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\"' +\n ' stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">' +\n '<path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" /></svg>';\n\n/**\n * Appends a dismiss (✕) button to a service-opened overlay panel.\n *\n * `WrDialog` and `WrDrawerManager` build their panel around a component the\n * consumer supplies, so there is no template of their own to put a corner button\n * in — they call this instead. Component-driven overlays (`<wr-drawer>`) render\n * the same markup in their template.\n *\n * Uses `host.ownerDocument` rather than the global `document`, so it carries no\n * hidden browser-only assumption; callers still gate on the platform because\n * they have nothing to append to on the server.\n *\n * @param host Panel element the button is appended to (and positioned against).\n * @param className BEM class for the button, e.g. `wr-dialog__close`.\n * @param label Accessible name — already localized by the caller.\n * @param onClose Invoked on click.\n */\nexport function wrAppendOverlayClose(\n host: HTMLElement,\n className: string,\n label: string,\n onClose: () => void\n): HTMLButtonElement {\n const button = host.ownerDocument.createElement('button');\n button.type = 'button';\n button.className = className;\n button.setAttribute('aria-label', label);\n button.innerHTML = CLOSE_ICON;\n button.addEventListener('click', onClose);\n host.appendChild(button);\n return button;\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 { OverlayRef } from '@angular/cdk/overlay';\nimport { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { PLATFORM_ID, Service, inject } from '@angular/core';\n\nimport { Observable, type Subscriber } from 'rxjs';\n\n/** One live subscription to `WrOutsideClick.outsidePointerEvents()`. */\ninterface Watcher {\n readonly overlayRef: OverlayRef;\n readonly subscriber: Subscriber<MouseEvent>;\n}\n\n/**\n * Shadow-piercing `event.target` — `composedPath()[0]` is the real originator\n * when the click started inside a shadow root, where `target` is retargeted to\n * the host.\n */\nfunction eventTarget(event: Event): EventTarget | null {\n if (event.composedPath) {\n try {\n return event.composedPath()[0] ?? event.target;\n } catch {\n /* Detached / synthetic events can throw — fall through to `target`. */\n }\n }\n return event.target;\n}\n\n/** `parent.contains(child)`, but stepping out of shadow roots through their hosts. */\nfunction containsAcrossShadow(parent: Node, child: EventTarget | null): boolean {\n const hasShadowRoot = typeof ShadowRoot !== 'undefined';\n let current = child as Node | null;\n while (current) {\n if (current === parent) return true;\n current = hasShadowRoot && current instanceof ShadowRoot ? current.host : current.parentNode;\n }\n return false;\n}\n\n/**\n * Outside-click source for overlays — a drop-in replacement for\n * `OverlayRef.outsidePointerEvents()`.\n *\n * **Why this exists.** CDK's `OverlayOutsideClickDispatcher` binds its\n * listeners to `document.body`. When page content is shorter than the viewport,\n * a click in the empty space below it targets `<html>` — and `<html>` is\n * `body`'s *parent*, so the event bubbles straight to the document and `body`\n * never sees it. The overlay silently stays open. Listening on the document\n * itself catches both cases, and costs nothing else.\n *\n * Semantics are otherwise identical to the CDK's, deliberately:\n *\n * - Emits on `click` / `auxclick` / `contextmenu`, in the capture phase, and\n * tracks `pointerdown` so a `click` is judged by where the press *started*\n * (a drag that ends outside the pane doesn't count as an outside click).\n * - Walks watchers top-most first and stops at the first one that contains the\n * event, so clicking inside a stacked overlay can't close the ones beneath\n * it (a `wr-select` opened inside a `wr-popover`, say).\n * - Fires for the very click that opened the overlay, which is why every caller\n * also ignores events landing on its own trigger.\n *\n * Registration is per subscription: the document listeners go up with the first\n * watcher and come down with the last, so an app with no open overlay has none.\n */\n@Service()\nexport class WrOutsideClick {\n private readonly doc = inject(DOCUMENT);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n /** In attach order — last is top-most. */\n private readonly watchers: Watcher[] = [];\n private cleanup: (() => void) | null = null;\n private pressOrigin: EventTarget | null = null;\n\n /**\n * Emits every `click` / `auxclick` / `contextmenu` that lands outside the\n * given overlay's pane, for as long as the returned observable is subscribed.\n */\n outsidePointerEvents(overlayRef: OverlayRef): Observable<MouseEvent> {\n return new Observable<MouseEvent>(subscriber => {\n if (!this.isBrowser) return;\n\n const watcher: Watcher = { overlayRef, subscriber };\n this.watchers.push(watcher);\n if (this.watchers.length === 1) this.listen();\n\n return () => {\n const index = this.watchers.indexOf(watcher);\n if (index > -1) this.watchers.splice(index, 1);\n if (!this.watchers.length) this.unlisten();\n };\n });\n }\n\n private readonly onPointerDown = (event: Event): void => {\n this.pressOrigin = eventTarget(event);\n };\n\n private readonly onClick = (event: Event): void => {\n const target = eventTarget(event);\n // `detail > 0` means the click really came from a pointer press. A click\n // activated by Enter or Space carries `detail: 0` and has no `pointerdown`\n // of its own — so trusting a leftover origin there judged it by a press\n // that belonged to something else entirely. A press inside the pane that\n // never produced a click (a drag released off-window, a node removed under\n // the finger, a cancelled touch) survives in `pressOrigin`, and the next\n // keyboard activation anywhere on the page then read as \"inside\": the panel\n // would not close, for keyboard users only.\n const fromPointer = event.type === 'click' && (event as MouseEvent).detail > 0;\n const origin = fromPointer && this.pressOrigin ? this.pressOrigin : target;\n this.pressOrigin = null;\n\n // Snapshot: a watcher that closes on this event unsubscribes synchronously,\n // mutating `watchers` while we're walking it.\n const watchers = this.watchers.slice();\n for (let i = watchers.length - 1; i > -1; i--) {\n const { overlayRef, subscriber } = watchers[i];\n if (!overlayRef.hasAttached()) continue;\n\n const pane = overlayRef.overlayElement;\n if (containsAcrossShadow(pane, target) || containsAcrossShadow(pane, origin)) break;\n\n subscriber.next(event as MouseEvent);\n }\n };\n\n private listen(): void {\n const options: AddEventListenerOptions = { capture: true };\n const doc = this.doc;\n\n doc.addEventListener('pointerdown', this.onPointerDown, options);\n doc.addEventListener('click', this.onClick, options);\n doc.addEventListener('auxclick', this.onClick, options);\n doc.addEventListener('contextmenu', this.onClick, options);\n\n this.cleanup = () => {\n doc.removeEventListener('pointerdown', this.onPointerDown, options);\n doc.removeEventListener('click', this.onClick, options);\n doc.removeEventListener('auxclick', this.onClick, options);\n doc.removeEventListener('contextmenu', this.onClick, options);\n };\n }\n\n private unlisten(): void {\n this.cleanup?.();\n this.cleanup = null;\n this.pressOrigin = null;\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 { OverlayContainer } from '@angular/cdk/overlay';\nimport { Service } from '@angular/core';\n\n/**\n * Custom CDK `OverlayContainer` subclass that tags its DOM element with\n * `.wr-overlay-container` in addition to CDK's own `.cdk-overlay-container`.\n *\n * Why: CDK uses a single overlay container per app — every CDK consumer\n * (this lib, Angular Material, NG-ZORRO, etc.) puts its overlays in the\n * same root element. With this class applied you can scope NGWR-specific\n * overlay styles via the tag, e.g.:\n *\n * ```scss\n * .wr-overlay-container .cdk-overlay-pane { z-index: 1100; }\n * ```\n *\n * It does **not** isolate two libraries that both subclass OverlayContainer\n * — the last provider wins. It does give NGWR overlays a stable CSS hook.\n */\n@Service()\nexport class WrOverlayContainer extends OverlayContainer {\n protected override _createContainer(): void {\n super._createContainer();\n this._containerElement?.classList.add('wr-overlay-container');\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 { Overlay } from '@angular/cdk/overlay';\nimport { InjectionToken, inject } from '@angular/core';\n\n/**\n * Injection token for the CDK `Overlay` service NGWR components should\n * use to open panels.\n *\n * Defaults to CDK's root `Overlay` (shared with every other CDK consumer\n * in the app). Call `provideWrOverlay()` to bind it to an isolated\n * `Overlay` instance backed by a separate `OverlayContainer` — that way\n * NGWR overlays don't share the same DOM root with other libraries.\n *\n * @internal — directives/services inside the lib inject this instead of\n * CDK's `Overlay` directly.\n */\nexport const WR_OVERLAY = new InjectionToken<Overlay>('WR_OVERLAY', {\n providedIn: 'root',\n factory: () => inject(Overlay),\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 { OverlayContainer } from '@angular/cdk/overlay';\nimport { InjectionToken, inject } from '@angular/core';\n\n/**\n * Injection token for the overlay container NGWR components should use.\n *\n * Defaults to CDK's `OverlayContainer` — i.e. the same root element every\n * CDK consumer shares. Call `provideWrOverlay()` to replace it with an\n * isolated instance so NGWR overlays don't collide with other libraries.\n *\n * @internal\n */\nexport const WR_OVERLAY_CONTAINER = new InjectionToken<OverlayContainer>('WR_OVERLAY_CONTAINER', {\n providedIn: 'root',\n factory: () => inject(OverlayContainer),\n});\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\n/** Configuration for responsive (bottom-sheet) overlay presentation. */\ninterface WrResponsiveOverlaysConfig {\n /**\n * Viewport width (CSS px) at or below which overlays present as a\n * bottom-sheet instead of a floating panel. @default 640\n */\n readonly breakpoint: number;\n}\n\n/**\n * When set, overlay services (dialog, select, dropdown, …) present as a\n * slide-up bottom-sheet on viewports at or below `breakpoint`. `null`\n * (the default) keeps every overlay floating. Configure via\n * {@link provideWrResponsiveOverlays}; override per call with a\n * component's `responsive` option.\n *\n * @internal\n */\nconst WR_RESPONSIVE_OVERLAYS = new InjectionToken<WrResponsiveOverlaysConfig | null>('WR_RESPONSIVE_OVERLAYS', {\n factory: () => null,\n});\n\n/**\n * Decide whether an overlay should present as a bottom-sheet for the\n * current viewport.\n *\n * - `responsive === true` — sheet on small viewports regardless of config.\n * - `responsive === false` — never a sheet.\n * - `responsive === undefined` — follow the global config (sheet when\n * {@link provideWrResponsiveOverlays} is configured).\n *\n * SSR-safe: returns `false` when there's no `window`.\n *\n * @internal\n */\nfunction wrPresentAsSheet(responsive: boolean | undefined, config: WrResponsiveOverlaysConfig | null): boolean {\n const enabled = responsive ?? config !== null;\n if (!enabled || typeof window === 'undefined') return false;\n return window.innerWidth <= (config?.breakpoint ?? 640);\n}\n\nexport { WR_RESPONSIVE_OVERLAYS, wrPresentAsSheet };\nexport type { WrResponsiveOverlaysConfig };\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 { Overlay, OverlayContainer } from '@angular/cdk/overlay';\nimport {\n EnvironmentInjector,\n Injector,\n type EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideEnvironmentInitializer,\n} from '@angular/core';\n\nimport { WrVisualViewport } from 'ngwr/platform';\n\nimport { WR_OVERLAY, WR_OVERLAY_CONTAINER } from './tokens';\nimport { WrOverlayContainer } from './wr-overlay-container';\n\n/**\n * Isolates NGWR overlays from other CDK consumers in the app.\n *\n * Without this call, NGWR overlays use CDK's root `Overlay` — they\n * render into the same `.cdk-overlay-container` element as Angular\n * Material, NG-ZORRO, etc. With it, NGWR gets its own `OverlayContainer`\n * (marked `.wr-overlay-container`) **and** its own `Overlay` service\n * instance — so styles, z-index, and lifecycle never collide.\n *\n * It does this by replacing two injection tokens:\n * - `WR_OVERLAY_CONTAINER` → custom `WrOverlayContainer` subclass\n * - `WR_OVERLAY` → a fresh `Overlay` instance built in a child injector\n * that resolves `OverlayContainer` to the custom subclass\n *\n * CDK's own root `Overlay` / `OverlayContainer` are left untouched, so\n * other libraries keep working with their original container.\n *\n * @example\n * ```ts\n * import { provideWrOverlay } from 'ngwr/overlay';\n *\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideWrOverlay(),\n * ],\n * });\n * ```\n */\nexport function provideWrOverlay(): EnvironmentProviders {\n return makeEnvironmentProviders([\n // Track the visual viewport app-wide so overlay sheets (and the\n // command-palette) can lift above the on-screen keyboard. No-op on the\n // server / in browsers without the API.\n provideEnvironmentInitializer(() => void inject(WrVisualViewport)),\n { provide: WR_OVERLAY_CONTAINER, useClass: WrOverlayContainer },\n {\n provide: WR_OVERLAY,\n useFactory: (): Overlay => {\n const parent = inject(EnvironmentInjector);\n const container = inject(WR_OVERLAY_CONTAINER);\n\n // Child injector resolves `OverlayContainer` to our subclass and\n // creates a fresh `Overlay` instance bound to it. CDK's own root\n // `Overlay` and `OverlayContainer` are untouched in the parent.\n const child = Injector.create({\n parent,\n providers: [\n { provide: OverlayContainer, useValue: container },\n { provide: Overlay, useClass: Overlay },\n ],\n });\n\n return child.get(Overlay);\n },\n },\n ]);\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\nimport { WR_RESPONSIVE_OVERLAYS, type WrResponsiveOverlaysConfig } from './tokens/wr-responsive-overlays.token';\n\n/**\n * Opt every NGWR overlay into responsive presentation — on viewports at or\n * below `breakpoint`, dialogs / selects / dropdowns and friends slide up as\n * a full-width bottom-sheet instead of a floating panel. Built on the same\n * overlay plumbing, so focus-trap, backdrop and scroll-blocking carry over.\n *\n * Individual components can still opt out (or in) per instance via their\n * `responsive` option.\n *\n * @example\n * ```ts\n * bootstrapApplication(AppComponent, {\n * providers: [provideWrOverlay(), provideWrResponsiveOverlays()],\n * });\n *\n * // Custom breakpoint:\n * provideWrResponsiveOverlays({ breakpoint: 768 });\n * ```\n */\nexport function provideWrResponsiveOverlays(config?: Partial<WrResponsiveOverlaysConfig>): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: WR_RESPONSIVE_OVERLAYS, useValue: { breakpoint: config?.breakpoint ?? 640 } },\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-overlay.mjs","sources":["../../../projects/lib/overlay/wr-overlay-close.ts","../../../projects/lib/overlay/wr-mirror-offsets.ts","../../../projects/lib/overlay/wr-outside-click.ts","../../../projects/lib/overlay/wr-overlay-container.ts","../../../projects/lib/overlay/tokens/wr-overlay.token.ts","../../../projects/lib/overlay/tokens/wr-overlay-container.token.ts","../../../projects/lib/overlay/tokens/wr-responsive-overlays.token.ts","../../../projects/lib/overlay/provide-wr-overlay.ts","../../../projects/lib/overlay/provide-wr-responsive-overlays.ts","../../../projects/lib/overlay/ngwr-overlay.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/**\n * The same 24×24 stroked ✕ the rest of the library's chrome draws (alert, toast,\n * lightbox). Static markup — the caller's label goes on via `setAttribute`, so\n * nothing from a catalog or a consumer is ever parsed as HTML.\n */\nconst CLOSE_ICON =\n '<svg class=\"wr-icon__svg\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\"' +\n ' stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">' +\n '<path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" /></svg>';\n\n/**\n * Appends a dismiss (✕) button to a service-opened overlay panel.\n *\n * `WrDialog` and `WrDrawerManager` build their panel around a component the\n * consumer supplies, so there is no template of their own to put a corner button\n * in — they call this instead. Component-driven overlays (`<wr-drawer>`) render\n * the same markup in their template.\n *\n * Uses `host.ownerDocument` rather than the global `document`, so it carries no\n * hidden browser-only assumption; callers still gate on the platform because\n * they have nothing to append to on the server.\n *\n * @param host Panel element the button is appended to (and positioned against).\n * @param className BEM class for the button, e.g. `wr-dialog__close`.\n * @param label Accessible name — already localized by the caller.\n * @param onClose Invoked on click.\n */\nexport function wrAppendOverlayClose(\n host: HTMLElement,\n className: string,\n label: string,\n onClose: () => void\n): HTMLButtonElement {\n const button = host.ownerDocument.createElement('button');\n button.type = 'button';\n button.className = className;\n button.setAttribute('aria-label', label);\n button.innerHTML = CLOSE_ICON;\n button.addEventListener('click', onClose);\n host.appendChild(button);\n return button;\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 { ConnectedPosition } from '@angular/cdk/overlay';\n\n/**\n * Mirror the horizontal offsets of a connected-position list for RTL.\n *\n * The CDK mirrors `originX` / `overlayX` under `dir=\"rtl\"` — a `start` anchor\n * resolves to the origin's right edge — but it does NOT mirror `offsetX`, which\n * it adds to the final PHYSICAL x (`_getOffset` returns `position.offsetX`\n * verbatim). So a position that meant \"sit to the inline-start of the trigger,\n * 8px clear of it\" keeps its `-8` under RTL, where the panel is now on the\n * right: the gap becomes an 8px OVERLAP, and the panel sits on top of the thing\n * it is describing.\n *\n * Nothing in the CDK will do this for us, and the amount is per position, so\n * every ngwr overlay that offsets along the inline axis passes its list through\n * here. `offsetY` is untouched: the block axis does not flip.\n *\n * @example\n * ```ts\n * const dir = inject(Directionality, { optional: true });\n *\n * strategy.withPositions(wrMirrorOffsets(WR_POPOVER_POSITIONS[position], dir?.value === 'rtl'));\n * ```\n */\nexport function wrMirrorOffsets(positions: readonly ConnectedPosition[], isRtl: boolean): ConnectedPosition[] {\n // Copied rather than mutated even when there is nothing to do: the position\n // tables are module-level constants shared by every instance, so handing back\n // the same objects invites a caller to write through them.\n if (!isRtl) return positions.map(position => ({ ...position }));\n\n return positions.map(position => ({\n ...position,\n ...(position.offsetX == null ? {} : { offsetX: -position.offsetX }),\n }));\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { OverlayRef } from '@angular/cdk/overlay';\nimport { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { PLATFORM_ID, Service, inject } from '@angular/core';\n\nimport { Observable, type Subscriber } from 'rxjs';\n\n/** One live subscription to `WrOutsideClick.outsidePointerEvents()`. */\ninterface Watcher {\n readonly overlayRef: OverlayRef;\n readonly subscriber: Subscriber<MouseEvent>;\n}\n\n/**\n * Shadow-piercing `event.target` — `composedPath()[0]` is the real originator\n * when the click started inside a shadow root, where `target` is retargeted to\n * the host.\n */\nfunction eventTarget(event: Event): EventTarget | null {\n if (event.composedPath) {\n try {\n return event.composedPath()[0] ?? event.target;\n } catch {\n /* Detached / synthetic events can throw — fall through to `target`. */\n }\n }\n return event.target;\n}\n\n/** `parent.contains(child)`, but stepping out of shadow roots through their hosts. */\nfunction containsAcrossShadow(parent: Node, child: EventTarget | null): boolean {\n const hasShadowRoot = typeof ShadowRoot !== 'undefined';\n let current = child as Node | null;\n while (current) {\n if (current === parent) return true;\n current = hasShadowRoot && current instanceof ShadowRoot ? current.host : current.parentNode;\n }\n return false;\n}\n\n/**\n * Outside-click source for overlays — a drop-in replacement for\n * `OverlayRef.outsidePointerEvents()`.\n *\n * **Why this exists.** CDK's `OverlayOutsideClickDispatcher` binds its\n * listeners to `document.body`. When page content is shorter than the viewport,\n * a click in the empty space below it targets `<html>` — and `<html>` is\n * `body`'s *parent*, so the event bubbles straight to the document and `body`\n * never sees it. The overlay silently stays open. Listening on the document\n * itself catches both cases, and costs nothing else.\n *\n * Semantics are otherwise identical to the CDK's, deliberately:\n *\n * - Emits on `click` / `auxclick` / `contextmenu`, in the capture phase, and\n * tracks `pointerdown` so a `click` is judged by where the press *started*\n * (a drag that ends outside the pane doesn't count as an outside click).\n * - Walks watchers top-most first and stops at the first one that contains the\n * event, so clicking inside a stacked overlay can't close the ones beneath\n * it (a `wr-select` opened inside a `wr-popover`, say).\n * - Fires for the very click that opened the overlay, which is why every caller\n * also ignores events landing on its own trigger.\n *\n * Registration is per subscription: the document listeners go up with the first\n * watcher and come down with the last, so an app with no open overlay has none.\n */\n@Service()\nexport class WrOutsideClick {\n private readonly doc = inject(DOCUMENT);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n /** In attach order — last is top-most. */\n private readonly watchers: Watcher[] = [];\n private cleanup: (() => void) | null = null;\n private pressOrigin: EventTarget | null = null;\n\n /**\n * Emits every `click` / `auxclick` / `contextmenu` that lands outside the\n * given overlay's pane, for as long as the returned observable is subscribed.\n */\n outsidePointerEvents(overlayRef: OverlayRef): Observable<MouseEvent> {\n return new Observable<MouseEvent>(subscriber => {\n if (!this.isBrowser) return;\n\n const watcher: Watcher = { overlayRef, subscriber };\n this.watchers.push(watcher);\n if (this.watchers.length === 1) this.listen();\n\n return () => {\n const index = this.watchers.indexOf(watcher);\n if (index > -1) this.watchers.splice(index, 1);\n if (!this.watchers.length) this.unlisten();\n };\n });\n }\n\n private readonly onPointerDown = (event: Event): void => {\n this.pressOrigin = eventTarget(event);\n };\n\n private readonly onClick = (event: Event): void => {\n const target = eventTarget(event);\n // `detail > 0` means the click really came from a pointer press. A click\n // activated by Enter or Space carries `detail: 0` and has no `pointerdown`\n // of its own — so trusting a leftover origin there judged it by a press\n // that belonged to something else entirely. A press inside the pane that\n // never produced a click (a drag released off-window, a node removed under\n // the finger, a cancelled touch) survives in `pressOrigin`, and the next\n // keyboard activation anywhere on the page then read as \"inside\": the panel\n // would not close, for keyboard users only.\n const fromPointer = event.type === 'click' && (event as MouseEvent).detail > 0;\n const origin = fromPointer && this.pressOrigin ? this.pressOrigin : target;\n this.pressOrigin = null;\n\n // Snapshot: a watcher that closes on this event unsubscribes synchronously,\n // mutating `watchers` while we're walking it.\n const watchers = this.watchers.slice();\n for (let i = watchers.length - 1; i > -1; i--) {\n const { overlayRef, subscriber } = watchers[i];\n if (!overlayRef.hasAttached()) continue;\n\n const pane = overlayRef.overlayElement;\n if (containsAcrossShadow(pane, target) || containsAcrossShadow(pane, origin)) break;\n\n subscriber.next(event as MouseEvent);\n }\n };\n\n private listen(): void {\n const options: AddEventListenerOptions = { capture: true };\n const doc = this.doc;\n\n doc.addEventListener('pointerdown', this.onPointerDown, options);\n doc.addEventListener('click', this.onClick, options);\n doc.addEventListener('auxclick', this.onClick, options);\n doc.addEventListener('contextmenu', this.onClick, options);\n\n this.cleanup = () => {\n doc.removeEventListener('pointerdown', this.onPointerDown, options);\n doc.removeEventListener('click', this.onClick, options);\n doc.removeEventListener('auxclick', this.onClick, options);\n doc.removeEventListener('contextmenu', this.onClick, options);\n };\n }\n\n private unlisten(): void {\n this.cleanup?.();\n this.cleanup = null;\n this.pressOrigin = null;\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 { OverlayContainer } from '@angular/cdk/overlay';\nimport { Service } from '@angular/core';\n\n/**\n * Custom CDK `OverlayContainer` subclass that tags its DOM element with\n * `.wr-overlay-container` in addition to CDK's own `.cdk-overlay-container`.\n *\n * Why: CDK uses a single overlay container per app — every CDK consumer\n * (this lib, Angular Material, NG-ZORRO, etc.) puts its overlays in the\n * same root element. With this class applied you can scope NGWR-specific\n * overlay styles via the tag, e.g.:\n *\n * ```scss\n * .wr-overlay-container .cdk-overlay-pane { z-index: 1100; }\n * ```\n *\n * It does **not** isolate two libraries that both subclass OverlayContainer\n * — the last provider wins. It does give NGWR overlays a stable CSS hook.\n */\n@Service()\nexport class WrOverlayContainer extends OverlayContainer {\n protected override _createContainer(): void {\n super._createContainer();\n this._containerElement?.classList.add('wr-overlay-container');\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 { Overlay } from '@angular/cdk/overlay';\nimport { InjectionToken, inject } from '@angular/core';\n\n/**\n * Injection token for the CDK `Overlay` service NGWR components should\n * use to open panels.\n *\n * Defaults to CDK's root `Overlay` (shared with every other CDK consumer\n * in the app). Call `provideWrOverlay()` to bind it to an isolated\n * `Overlay` instance backed by a separate `OverlayContainer` — that way\n * NGWR overlays don't share the same DOM root with other libraries.\n *\n * @internal — directives/services inside the lib inject this instead of\n * CDK's `Overlay` directly.\n */\nexport const WR_OVERLAY = new InjectionToken<Overlay>('WR_OVERLAY', {\n providedIn: 'root',\n factory: () => inject(Overlay),\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 { OverlayContainer } from '@angular/cdk/overlay';\nimport { InjectionToken, inject } from '@angular/core';\n\n/**\n * Injection token for the overlay container NGWR components should use.\n *\n * Defaults to CDK's `OverlayContainer` — i.e. the same root element every\n * CDK consumer shares. Call `provideWrOverlay()` to replace it with an\n * isolated instance so NGWR overlays don't collide with other libraries.\n *\n * @internal\n */\nexport const WR_OVERLAY_CONTAINER = new InjectionToken<OverlayContainer>('WR_OVERLAY_CONTAINER', {\n providedIn: 'root',\n factory: () => inject(OverlayContainer),\n});\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\n/** Configuration for responsive (bottom-sheet) overlay presentation. */\ninterface WrResponsiveOverlaysConfig {\n /**\n * Viewport width (CSS px) at or below which overlays present as a\n * bottom-sheet instead of a floating panel. @default 640\n */\n readonly breakpoint: number;\n}\n\n/**\n * When set, overlay services (dialog, select, dropdown, …) present as a\n * slide-up bottom-sheet on viewports at or below `breakpoint`. `null`\n * (the default) keeps every overlay floating. Configure via\n * {@link provideWrResponsiveOverlays}; override per call with a\n * component's `responsive` option.\n *\n * @internal\n */\nconst WR_RESPONSIVE_OVERLAYS = new InjectionToken<WrResponsiveOverlaysConfig | null>('WR_RESPONSIVE_OVERLAYS', {\n factory: () => null,\n});\n\n/**\n * Decide whether an overlay should present as a bottom-sheet for the\n * current viewport.\n *\n * - `responsive === true` — sheet on small viewports regardless of config.\n * - `responsive === false` — never a sheet.\n * - `responsive === undefined` — follow the global config (sheet when\n * {@link provideWrResponsiveOverlays} is configured).\n *\n * SSR-safe: returns `false` when there's no `window`.\n *\n * @internal\n */\nfunction wrPresentAsSheet(responsive: boolean | undefined, config: WrResponsiveOverlaysConfig | null): boolean {\n const enabled = responsive ?? config !== null;\n if (!enabled || typeof window === 'undefined') return false;\n return window.innerWidth <= (config?.breakpoint ?? 640);\n}\n\nexport { WR_RESPONSIVE_OVERLAYS, wrPresentAsSheet };\nexport type { WrResponsiveOverlaysConfig };\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 { Overlay, OverlayContainer } from '@angular/cdk/overlay';\nimport {\n EnvironmentInjector,\n Injector,\n type EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideEnvironmentInitializer,\n} from '@angular/core';\n\nimport { WrVisualViewport } from 'ngwr/platform';\n\nimport { WR_OVERLAY, WR_OVERLAY_CONTAINER } from './tokens';\nimport { WrOverlayContainer } from './wr-overlay-container';\n\n/**\n * Isolates NGWR overlays from other CDK consumers in the app.\n *\n * Without this call, NGWR overlays use CDK's root `Overlay` — they\n * render into the same `.cdk-overlay-container` element as Angular\n * Material, NG-ZORRO, etc. With it, NGWR gets its own `OverlayContainer`\n * (marked `.wr-overlay-container`) **and** its own `Overlay` service\n * instance — so styles, z-index, and lifecycle never collide.\n *\n * It does this by replacing two injection tokens:\n * - `WR_OVERLAY_CONTAINER` → custom `WrOverlayContainer` subclass\n * - `WR_OVERLAY` → a fresh `Overlay` instance built in a child injector\n * that resolves `OverlayContainer` to the custom subclass\n *\n * CDK's own root `Overlay` / `OverlayContainer` are left untouched, so\n * other libraries keep working with their original container.\n *\n * @example\n * ```ts\n * import { provideWrOverlay } from 'ngwr/overlay';\n *\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideWrOverlay(),\n * ],\n * });\n * ```\n */\nexport function provideWrOverlay(): EnvironmentProviders {\n return makeEnvironmentProviders([\n // Track the visual viewport app-wide so overlay sheets (and the\n // command-palette) can lift above the on-screen keyboard. No-op on the\n // server / in browsers without the API.\n provideEnvironmentInitializer(() => void inject(WrVisualViewport)),\n { provide: WR_OVERLAY_CONTAINER, useClass: WrOverlayContainer },\n {\n provide: WR_OVERLAY,\n useFactory: (): Overlay => {\n const parent = inject(EnvironmentInjector);\n const container = inject(WR_OVERLAY_CONTAINER);\n\n // Child injector resolves `OverlayContainer` to our subclass and\n // creates a fresh `Overlay` instance bound to it. CDK's own root\n // `Overlay` and `OverlayContainer` are untouched in the parent.\n const child = Injector.create({\n parent,\n providers: [\n { provide: OverlayContainer, useValue: container },\n { provide: Overlay, useClass: Overlay },\n ],\n });\n\n return child.get(Overlay);\n },\n },\n ]);\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\nimport { WR_RESPONSIVE_OVERLAYS, type WrResponsiveOverlaysConfig } from './tokens/wr-responsive-overlays.token';\n\n/**\n * Opt every NGWR overlay into responsive presentation — on viewports at or\n * below `breakpoint`, dialogs / selects / dropdowns and friends slide up as\n * a full-width bottom-sheet instead of a floating panel. Built on the same\n * overlay plumbing, so focus-trap, backdrop and scroll-blocking carry over.\n *\n * Individual components can still opt out (or in) per instance via their\n * `responsive` option.\n *\n * @example\n * ```ts\n * bootstrapApplication(AppComponent, {\n * providers: [provideWrOverlay(), provideWrResponsiveOverlays()],\n * });\n *\n * // Custom breakpoint:\n * provideWrResponsiveOverlays({ breakpoint: 768 });\n * ```\n */\nexport function provideWrResponsiveOverlays(config?: Partial<WrResponsiveOverlaysConfig>): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: WR_RESPONSIVE_OVERLAYS, useValue: { breakpoint: config?.breakpoint ?? 640 } },\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
|
|
2
|
+
import { WrButtonHarness } from 'ngwr/button/testing';
|
|
3
|
+
import { WrSelectHarness } from 'ngwr/select/testing';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @license
|
|
7
|
+
*
|
|
8
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
9
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Test harness for `<wr-pagination>` — the numbered strip, its two step controls,
|
|
13
|
+
* the total label and the page-size changer.
|
|
14
|
+
*
|
|
15
|
+
* The strip is not the page list: it is a WINDOW over it, at most seven pages wide,
|
|
16
|
+
* with an `…` standing in for every page it skipped. So "which pages can I click"
|
|
17
|
+
* and "how many pages are there" are different questions and this harness keeps
|
|
18
|
+
* them apart —
|
|
19
|
+
* {@link getPages} answers the first, {@link getTotalPages} the second, and
|
|
20
|
+
* {@link getStrip} shows the gaps for what they are. A gap is a `<span>`, not a
|
|
21
|
+
* control; counting the strip's children as pages is the mistake this API exists to
|
|
22
|
+
* make impossible.
|
|
23
|
+
*
|
|
24
|
+
* **Which page is current has two answers, and this reads the ARIA one.** The
|
|
25
|
+
* current cell is also PAINTED — the template binds
|
|
26
|
+
* `[color]="isCurrent(page) ? 'primary' : null"`, so `.wr-btn--primary` sits on it
|
|
27
|
+
* too. But colour is a theming decision (`ngwr/config` names this very binding as
|
|
28
|
+
* the reason it ships no app-wide `color` default), while `aria-current="page"` is
|
|
29
|
+
* the navigation contract: it is what a screen-reader user hears, what the
|
|
30
|
+
* component's own spec pins, and the only one of the two that still means
|
|
31
|
+
* "you are here" after a restyle. A harness that read the class would be reporting
|
|
32
|
+
* on the paint.
|
|
33
|
+
*
|
|
34
|
+
* **No roving tab stop to disambiguate.** Every cell is a `<wr-btn>` ELEMENT with
|
|
35
|
+
* its own tab stop (`tabindex="0"`, dropped only while the control is off), so the
|
|
36
|
+
* pager has no separate "focused" item that could drift from the current page —
|
|
37
|
+
* unlike a tab strip or a radio group, there is one active state here and
|
|
38
|
+
* {@link getCurrentPage} is it.
|
|
39
|
+
*
|
|
40
|
+
* Composed rather than re-queried: the cells and step controls are driven through
|
|
41
|
+
* {@link WrButtonHarness}, which already knows how a `<wr-btn>` says it is off —
|
|
42
|
+
* both the `disabled` attribute (written on every shape, though inert on a custom
|
|
43
|
+
* element) and `aria-disabled`, in that order — and the size changer through
|
|
44
|
+
* {@link WrSelectHarness}, whose panel is a portal in the overlay container and
|
|
45
|
+
* nowhere inside the pager.
|
|
46
|
+
*
|
|
47
|
+
* jsdom notes: every method here is a class / attribute read or a no-arg click, so
|
|
48
|
+
* none of it needs layout. Two things are therefore invisible in a unit test and
|
|
49
|
+
* deliberately absent from this API — the responsive collapse, which is a container
|
|
50
|
+
* query ({@link isResponsive} reports only that it is ALLOWED), and the compact
|
|
51
|
+
* `‹ 3 / 10 ›` pager it swaps in, which is `aria-hidden` decoration duplicating
|
|
52
|
+
* {@link getCurrentPage} and {@link getTotalPages} and which a real browser would
|
|
53
|
+
* read as empty text while CSS keeps it hidden.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* const pager = await loader.getHarness(WrPaginationHarness);
|
|
58
|
+
*
|
|
59
|
+
* expect(await pager.getPages()).toEqual([1, 2, 3, 4, 5, 10]);
|
|
60
|
+
* await pager.goToPage(3);
|
|
61
|
+
* expect(await pager.getCurrentPage()).toBe(3);
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @see https://ngwr.dev/guides/testing
|
|
65
|
+
*/
|
|
66
|
+
class WrPaginationHarness extends ComponentHarness {
|
|
67
|
+
static hostSelector = 'wr-pagination';
|
|
68
|
+
/** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
|
|
69
|
+
static with(options = {}) {
|
|
70
|
+
return new HarnessPredicate(WrPaginationHarness, options)
|
|
71
|
+
.addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))
|
|
72
|
+
.addOption('currentPage', options.currentPage, async (harness, page) => {
|
|
73
|
+
// The nullable read on purpose: the throwing getter would reject the whole
|
|
74
|
+
// query over one pager that announces no current page, taking its
|
|
75
|
+
// well-behaved neighbours down with it.
|
|
76
|
+
return (await harness.currentPageOrNull()) === page;
|
|
77
|
+
})
|
|
78
|
+
.addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);
|
|
79
|
+
}
|
|
80
|
+
/** The numbered cells, in DOM order — ascending, and never a gap. */
|
|
81
|
+
pageCells = this.locatorForAll(WrButtonHarness.with({ selector: '.wr-pagination__page' }));
|
|
82
|
+
/**
|
|
83
|
+
* The two controls bracketing the strip: previous, then next.
|
|
84
|
+
*
|
|
85
|
+
* Located by POSITION, not by their labels. `prevLabel` / `nextLabel` are inputs
|
|
86
|
+
* and both fall back to the `pagination.prev` / `pagination.next` catalog entries,
|
|
87
|
+
* so an English `aria-label` match works right up until someone ships a locale —
|
|
88
|
+
* whereas the nav's shape (one step control, the strip, one step control) is
|
|
89
|
+
* structural. `:not(.wr-pagination__page)` is what keeps the cells out of it.
|
|
90
|
+
*/
|
|
91
|
+
stepControls = this.locatorForAll(WrButtonHarness.with({ selector: '.wr-pagination__nav > wr-btn:not(.wr-pagination__page)' }));
|
|
92
|
+
/** Both kinds of strip slot, in DOM order — cells and the gaps between them. */
|
|
93
|
+
stripSlots = this.locatorForAll('.wr-pagination__page, .wr-pagination__ellipsis');
|
|
94
|
+
totalLabel = this.locatorForOptional('.wr-pagination__total');
|
|
95
|
+
sizeChanger = this.locatorForOptional(WrSelectHarness.with({ selector: '.wr-pagination__size' }));
|
|
96
|
+
/**
|
|
97
|
+
* The accessible name of the `role="navigation"` landmark.
|
|
98
|
+
*
|
|
99
|
+
* `''` only if the landmark lost its name, which is a bug rather than a state: a
|
|
100
|
+
* page with two unnamed pagers gives a screen-reader user two identical
|
|
101
|
+
* "navigation" landmarks.
|
|
102
|
+
*/
|
|
103
|
+
async getLabel() {
|
|
104
|
+
return (await (await this.host()).getAttribute('aria-label')) ?? '';
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The size the pager RENDERS at, from its host modifier.
|
|
108
|
+
*
|
|
109
|
+
* The modifier is always emitted — `sm` included — so the fall-through below is
|
|
110
|
+
* only ever reached for the default, and a size the strip is not wearing cannot
|
|
111
|
+
* come back from here.
|
|
112
|
+
*
|
|
113
|
+
* The STRIP resolves nothing through `provideWrConfig()`, which is worth knowing
|
|
114
|
+
* before an app-wide default is blamed for a cell size: the pager passes its own
|
|
115
|
+
* `size` down to every `<wr-btn>` explicitly, so a configured `button.size` never
|
|
116
|
+
* reaches a cell. The page-size changer is the exception and not covered by this
|
|
117
|
+
* value — it is a bare `<wr-select>`, so `provideWrConfig({ select: … })` does
|
|
118
|
+
* reach it.
|
|
119
|
+
*/
|
|
120
|
+
async getSize() {
|
|
121
|
+
const host = await this.host();
|
|
122
|
+
if (await host.hasClass('wr-pagination--lg'))
|
|
123
|
+
return 'lg';
|
|
124
|
+
if (await host.hasClass('wr-pagination--md'))
|
|
125
|
+
return 'md';
|
|
126
|
+
return 'sm';
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Whether the pager is ALLOWED to collapse into its compact pager.
|
|
130
|
+
*
|
|
131
|
+
* Whether it HAS collapsed is a container query on the control's own box, and a
|
|
132
|
+
* unit test has no layout for one to answer — assert this input in jsdom and the
|
|
133
|
+
* reflow itself in a real browser.
|
|
134
|
+
*/
|
|
135
|
+
async isResponsive() {
|
|
136
|
+
return (await this.host()).hasClass('wr-pagination--responsive');
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Whether the whole control refuses interaction.
|
|
140
|
+
*
|
|
141
|
+
* Read from the host modifier, because a `role="navigation"` landmark has no ARIA
|
|
142
|
+
* disabled state to prefer — the per-control answer lives on the buttons
|
|
143
|
+
* themselves (`disabled` plus `aria-disabled` on each), which is what
|
|
144
|
+
* {@link isPreviousDisabled} and {@link isNextDisabled} read.
|
|
145
|
+
*/
|
|
146
|
+
async isDisabled() {
|
|
147
|
+
return (await this.host()).hasClass('wr-pagination--disabled');
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The whole strip in DOM order: a `number` for every page cell, and the gap
|
|
151
|
+
* marker's own text (`…`) for every ellipsis.
|
|
152
|
+
*
|
|
153
|
+
* The marker is read from the DOM rather than compared against a literal, so a
|
|
154
|
+
* component that changes what it draws in a gap changes this too instead of
|
|
155
|
+
* quietly reporting a page.
|
|
156
|
+
*/
|
|
157
|
+
async getStrip() {
|
|
158
|
+
const slots = await this.stripSlots();
|
|
159
|
+
return Promise.all(slots.map(async (slot) => {
|
|
160
|
+
const text = (await slot.text()).trim();
|
|
161
|
+
return (await slot.hasClass('wr-pagination__ellipsis')) ? text : Number(text);
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The pages that can be clicked right now, ascending. Gaps are not pages and are
|
|
166
|
+
* not here — `[1, 8, 9, 10, 11, 12, 20]` for a twenty-page list sitting on 10.
|
|
167
|
+
*/
|
|
168
|
+
async getPages() {
|
|
169
|
+
const cells = await this.pageCells();
|
|
170
|
+
return Promise.all(cells.map(async (cell) => Number(await cell.getText())));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* How many pages there are — not how many are on the strip.
|
|
174
|
+
*
|
|
175
|
+
* Taken from the LAST cell, which the window always pins to the final page (the
|
|
176
|
+
* first is pinned the same way), so this survives the gap that usually precedes
|
|
177
|
+
* it. Deliberately not read from the compact `3 / 10` pager: that span is
|
|
178
|
+
* `aria-hidden` and CSS-hidden outside responsive mode, so a browser-based
|
|
179
|
+
* harness environment would read it as empty.
|
|
180
|
+
*/
|
|
181
|
+
async getTotalPages() {
|
|
182
|
+
const pages = await this.getPages();
|
|
183
|
+
const last = pages.at(-1);
|
|
184
|
+
if (last === undefined) {
|
|
185
|
+
throw new Error('WrPaginationHarness.getTotalPages(): the strip has no page cells at all. The pager offers page ' +
|
|
186
|
+
'1 even for an empty list, so this is a rendering failure rather than an empty state.');
|
|
187
|
+
}
|
|
188
|
+
return last;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* The page the pager announces as current, from `aria-current="page"`.
|
|
192
|
+
*
|
|
193
|
+
* Throws when no cell claims it. The window always keeps the current page on the
|
|
194
|
+
* strip, so getting here means `currentPage` is not one of the pages at all — it
|
|
195
|
+
* is below 1, or not a whole number. The component clamps a page ABOVE the last
|
|
196
|
+
* one and nothing else, and either survivor leaves every cell looking equally
|
|
197
|
+
* inactive to an assistive tech user.
|
|
198
|
+
*/
|
|
199
|
+
async getCurrentPage() {
|
|
200
|
+
const page = await this.currentPageOrNull();
|
|
201
|
+
if (page === null) {
|
|
202
|
+
throw new Error('WrPaginationHarness.getCurrentPage(): no cell carries `aria-current="page"`. The visible window ' +
|
|
203
|
+
`always includes the current page, so \`currentPage\` is not a page: below 1, or not a whole ` +
|
|
204
|
+
`number (the pager clamps above ${await this.getTotalPages()}, and nothing else).`);
|
|
205
|
+
}
|
|
206
|
+
return page;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Click the cell for a page.
|
|
210
|
+
*
|
|
211
|
+
* Throws when that page is not on the strip — a seven-slot window over twenty
|
|
212
|
+
* pages simply has no cell for page 15, and a silent no-op there reads as
|
|
213
|
+
* "navigation is broken" three assertions later. Step with {@link next} /
|
|
214
|
+
* {@link previous}, or move the host's own `currentPage` model.
|
|
215
|
+
*
|
|
216
|
+
* A disabled pager is still clicked; the component's own guard is what refuses
|
|
217
|
+
* the move, so assert the page rather than assume it. Clicking the CURRENT page
|
|
218
|
+
* is a no-op by the same guard.
|
|
219
|
+
*/
|
|
220
|
+
async goToPage(page) {
|
|
221
|
+
for (const cell of await this.pageCells()) {
|
|
222
|
+
if (Number(await cell.getText()) === page) {
|
|
223
|
+
await cell.click();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
throw new Error(`WrPaginationHarness.goToPage(): page ${page} is not on the strip, which offers ` +
|
|
228
|
+
`${(await this.getStrip()).join(' ')}. The window shows at most seven pages — step with next() / ` +
|
|
229
|
+
'previous(), or set the page on the host.');
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Jump to page 1.
|
|
233
|
+
*
|
|
234
|
+
* There is no dedicated first / last control in this component; the window pins
|
|
235
|
+
* the first and last page to the ends of the strip instead, so these two click
|
|
236
|
+
* cells like any other page.
|
|
237
|
+
*/
|
|
238
|
+
async goToFirst() {
|
|
239
|
+
return this.goToPage(1);
|
|
240
|
+
}
|
|
241
|
+
/** Jump to the last page — the trailing cell, not the last one before a gap. */
|
|
242
|
+
async goToLast() {
|
|
243
|
+
return this.goToPage(await this.getTotalPages());
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Click the next-page control.
|
|
247
|
+
*
|
|
248
|
+
* Disabled on the last page, and a disabled `<wr-btn>` is still clicked here (see
|
|
249
|
+
* {@link goToPage}) — {@link isNextDisabled} is the question, this is the action.
|
|
250
|
+
*/
|
|
251
|
+
async next() {
|
|
252
|
+
return (await this.stepControl('next')).click();
|
|
253
|
+
}
|
|
254
|
+
/** Click the previous-page control. Disabled on the first page. */
|
|
255
|
+
async previous() {
|
|
256
|
+
return (await this.stepControl('previous')).click();
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Whether the next-page control refuses the move — true on the last page, and
|
|
260
|
+
* true for every control while the pager is disabled.
|
|
261
|
+
*
|
|
262
|
+
* Answered by {@link WrButtonHarness}, which reads the `disabled` attribute first
|
|
263
|
+
* and `aria-disabled` second. A `<wr-btn>` element carries both: the attribute is
|
|
264
|
+
* inert on anything but a real `<button>`, which is why the component adds the
|
|
265
|
+
* ARIA state as well — so this is the accessible answer whichever branch supplies
|
|
266
|
+
* it.
|
|
267
|
+
*/
|
|
268
|
+
async isNextDisabled() {
|
|
269
|
+
return (await this.stepControl('next')).isDisabled();
|
|
270
|
+
}
|
|
271
|
+
/** Whether the previous-page control refuses the move — true on the first page. */
|
|
272
|
+
async isPreviousDisabled() {
|
|
273
|
+
return (await this.stepControl('previous')).isDisabled();
|
|
274
|
+
}
|
|
275
|
+
/** Whether the pager shows its "X–Y of Z" label (the `showTotal` input). */
|
|
276
|
+
async hasTotal() {
|
|
277
|
+
return (await this.totalLabel()) !== null;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* The total label exactly as rendered, e.g. `1-10 of 95`.
|
|
281
|
+
*
|
|
282
|
+
* Both halves are localizable — the word between range and total comes from
|
|
283
|
+
* `ofLabel` or the `pagination.of` catalog entry — so a spec asserting the whole
|
|
284
|
+
* string should own the copy it asserts.
|
|
285
|
+
*
|
|
286
|
+
* Throws when the label is not rendered, which is a `showTotal` that was never
|
|
287
|
+
* set rather than a total of zero (a zero total still renders `0-0 of 0`).
|
|
288
|
+
*/
|
|
289
|
+
async getTotalText() {
|
|
290
|
+
const label = await this.totalLabel();
|
|
291
|
+
if (!label) {
|
|
292
|
+
throw new Error('WrPaginationHarness.getTotalText(): this pager renders no total label — set `showTotal`. A ' +
|
|
293
|
+
'total of 0 still renders one.');
|
|
294
|
+
}
|
|
295
|
+
return label.text();
|
|
296
|
+
}
|
|
297
|
+
/** Whether the pager renders its page-size changer (the `showSizeChanger` input). */
|
|
298
|
+
async hasPageSizeChanger() {
|
|
299
|
+
return (await this.sizeChanger()) !== null;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* The page-size changer as a {@link WrSelectHarness} — for anything this harness
|
|
303
|
+
* does not wrap: the offered labels, the open state, keyboard driving.
|
|
304
|
+
*
|
|
305
|
+
* Composed rather than re-queried on purpose: the changer is a real
|
|
306
|
+
* `<wr-select>`, its options live in the overlay container rather than inside the
|
|
307
|
+
* pager, and the select harness is the thing that already knows how to scope them
|
|
308
|
+
* to their own trigger.
|
|
309
|
+
*
|
|
310
|
+
* Throws when there is no changer — an `undefined` here would turn into a
|
|
311
|
+
* confusing failure inside a select call.
|
|
312
|
+
*/
|
|
313
|
+
async getPageSizeSelect() {
|
|
314
|
+
const select = await this.sizeChanger();
|
|
315
|
+
if (!select) {
|
|
316
|
+
throw new Error('WrPaginationHarness.getPageSizeSelect(): no page-size changer here — set `showSizeChanger`.');
|
|
317
|
+
}
|
|
318
|
+
return select;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* The page size currently in effect.
|
|
322
|
+
*
|
|
323
|
+
* The changer's trigger is the only place the size reaches the DOM, so this reads
|
|
324
|
+
* it there and takes the digits: the label is localizable (`pagination.perPage`,
|
|
325
|
+
* `{{size}} / page`) but interpolates the number in every language — unless a
|
|
326
|
+
* catalog entry drops the placeholder, which is the second throw below.
|
|
327
|
+
*
|
|
328
|
+
* Throws when there is no changer at all: a pager without one says nothing on
|
|
329
|
+
* screen about its page size, and the host's own model is where to assert it.
|
|
330
|
+
*/
|
|
331
|
+
async getPageSize() {
|
|
332
|
+
const text = await (await this.getPageSizeSelect()).getValueText();
|
|
333
|
+
const digits = /\d+/.exec(text);
|
|
334
|
+
if (!digits) {
|
|
335
|
+
throw new Error(`WrPaginationHarness.getPageSize(): the changer reads "${text}", which names no number — a ` +
|
|
336
|
+
'catalog entry for `pagination.perPage` that drops `{{size}}` takes the size off the screen.');
|
|
337
|
+
}
|
|
338
|
+
return Number(digits[0]);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Pick a page size through the changer, the way a user does.
|
|
342
|
+
*
|
|
343
|
+
* Matched on the digits rather than the whole label, for the same reason
|
|
344
|
+
* {@link getPageSize} reads them: `20 / page` and `20 / стр.` are the same option.
|
|
345
|
+
* Throws when no option offers that size, naming what is on offer.
|
|
346
|
+
*
|
|
347
|
+
* Changing the size can move the page — the component pulls `currentPage` back
|
|
348
|
+
* when the new size leaves it past the end — so re-read {@link getCurrentPage}
|
|
349
|
+
* afterwards rather than assuming it held.
|
|
350
|
+
*/
|
|
351
|
+
async setPageSize(size) {
|
|
352
|
+
const select = await this.getPageSizeSelect();
|
|
353
|
+
if (await this.isDisabled()) {
|
|
354
|
+
// Without this the call fails inside the select harness, whose message blames
|
|
355
|
+
// a tag-mode panel and a `minChars` search that have nothing to do with it.
|
|
356
|
+
throw new Error('WrPaginationHarness.setPageSize(): the pager is disabled, so its changer refuses to open — the ' +
|
|
357
|
+
'pager hands its own `disabled` straight to it.');
|
|
358
|
+
}
|
|
359
|
+
await select.open();
|
|
360
|
+
const labels = await select.getOptionLabels();
|
|
361
|
+
const digits = new RegExp(`(^|\\D)${size}(\\D|$)`);
|
|
362
|
+
const match = labels.find(label => digits.test(label));
|
|
363
|
+
if (match === undefined) {
|
|
364
|
+
throw new Error(`WrPaginationHarness.setPageSize(): no option offers ${size} — the changer offers ` +
|
|
365
|
+
`${labels.join(', ')}. \`pageSizeOptions\` is what decides that list.`);
|
|
366
|
+
}
|
|
367
|
+
await select.selectOption({ text: match });
|
|
368
|
+
}
|
|
369
|
+
/** The current page, or `null` when no cell announces one. @see getCurrentPage */
|
|
370
|
+
async currentPageOrNull() {
|
|
371
|
+
const cell = await this.locatorForOptional(WrButtonHarness.with({ selector: '.wr-pagination__page[aria-current="page"]' }))();
|
|
372
|
+
return cell ? Number(await cell.getText()) : null;
|
|
373
|
+
}
|
|
374
|
+
async stepControl(which) {
|
|
375
|
+
const controls = await this.stepControls();
|
|
376
|
+
if (controls.length !== 2) {
|
|
377
|
+
throw new Error(`WrPaginationHarness: expected the nav to bracket the strip with a previous and a next control, ` +
|
|
378
|
+
`found ${controls.length}. They are addressed by position, since their labels are localizable.`);
|
|
379
|
+
}
|
|
380
|
+
return which === 'previous' ? controls[0] : controls[1];
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Generated bundle index. Do not edit.
|
|
386
|
+
*/
|
|
387
|
+
|
|
388
|
+
export { WrPaginationHarness };
|
|
389
|
+
//# sourceMappingURL=ngwr-pagination-testing.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngwr-pagination-testing.mjs","sources":["../../../projects/lib/pagination/testing/wr-pagination-harness.ts","../../../projects/lib/pagination/testing/ngwr-pagination-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport { WrButtonHarness } from 'ngwr/button/testing';\nimport type { WrPaginationSize } from 'ngwr/pagination';\nimport { WrSelectHarness } from 'ngwr/select/testing';\n\nimport type { WrPaginationHarnessFilters } from './interfaces';\n\n/**\n * Test harness for `<wr-pagination>` — the numbered strip, its two step controls,\n * the total label and the page-size changer.\n *\n * The strip is not the page list: it is a WINDOW over it, at most seven pages wide,\n * with an `…` standing in for every page it skipped. So \"which pages can I click\"\n * and \"how many pages are there\" are different questions and this harness keeps\n * them apart —\n * {@link getPages} answers the first, {@link getTotalPages} the second, and\n * {@link getStrip} shows the gaps for what they are. A gap is a `<span>`, not a\n * control; counting the strip's children as pages is the mistake this API exists to\n * make impossible.\n *\n * **Which page is current has two answers, and this reads the ARIA one.** The\n * current cell is also PAINTED — the template binds\n * `[color]=\"isCurrent(page) ? 'primary' : null\"`, so `.wr-btn--primary` sits on it\n * too. But colour is a theming decision (`ngwr/config` names this very binding as\n * the reason it ships no app-wide `color` default), while `aria-current=\"page\"` is\n * the navigation contract: it is what a screen-reader user hears, what the\n * component's own spec pins, and the only one of the two that still means\n * \"you are here\" after a restyle. A harness that read the class would be reporting\n * on the paint.\n *\n * **No roving tab stop to disambiguate.** Every cell is a `<wr-btn>` ELEMENT with\n * its own tab stop (`tabindex=\"0\"`, dropped only while the control is off), so the\n * pager has no separate \"focused\" item that could drift from the current page —\n * unlike a tab strip or a radio group, there is one active state here and\n * {@link getCurrentPage} is it.\n *\n * Composed rather than re-queried: the cells and step controls are driven through\n * {@link WrButtonHarness}, which already knows how a `<wr-btn>` says it is off —\n * both the `disabled` attribute (written on every shape, though inert on a custom\n * element) and `aria-disabled`, in that order — and the size changer through\n * {@link WrSelectHarness}, whose panel is a portal in the overlay container and\n * nowhere inside the pager.\n *\n * jsdom notes: every method here is a class / attribute read or a no-arg click, so\n * none of it needs layout. Two things are therefore invisible in a unit test and\n * deliberately absent from this API — the responsive collapse, which is a container\n * query ({@link isResponsive} reports only that it is ALLOWED), and the compact\n * `‹ 3 / 10 ›` pager it swaps in, which is `aria-hidden` decoration duplicating\n * {@link getCurrentPage} and {@link getTotalPages} and which a real browser would\n * read as empty text while CSS keeps it hidden.\n *\n * @example\n * ```ts\n * const pager = await loader.getHarness(WrPaginationHarness);\n *\n * expect(await pager.getPages()).toEqual([1, 2, 3, 4, 5, 10]);\n * await pager.goToPage(3);\n * expect(await pager.getCurrentPage()).toBe(3);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrPaginationHarness extends ComponentHarness {\n static hostSelector = 'wr-pagination';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrPaginationHarnessFilters = {}): HarnessPredicate<WrPaginationHarness> {\n return new HarnessPredicate(WrPaginationHarness, options)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))\n .addOption('currentPage', options.currentPage, async (harness, page) => {\n // The nullable read on purpose: the throwing getter would reject the whole\n // query over one pager that announces no current page, taking its\n // well-behaved neighbours down with it.\n return (await harness.currentPageOrNull()) === page;\n })\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n /** The numbered cells, in DOM order — ascending, and never a gap. */\n private readonly pageCells = this.locatorForAll(WrButtonHarness.with({ selector: '.wr-pagination__page' }));\n\n /**\n * The two controls bracketing the strip: previous, then next.\n *\n * Located by POSITION, not by their labels. `prevLabel` / `nextLabel` are inputs\n * and both fall back to the `pagination.prev` / `pagination.next` catalog entries,\n * so an English `aria-label` match works right up until someone ships a locale —\n * whereas the nav's shape (one step control, the strip, one step control) is\n * structural. `:not(.wr-pagination__page)` is what keeps the cells out of it.\n */\n private readonly stepControls = this.locatorForAll(\n WrButtonHarness.with({ selector: '.wr-pagination__nav > wr-btn:not(.wr-pagination__page)' })\n );\n\n /** Both kinds of strip slot, in DOM order — cells and the gaps between them. */\n private readonly stripSlots = this.locatorForAll('.wr-pagination__page, .wr-pagination__ellipsis');\n\n private readonly totalLabel = this.locatorForOptional('.wr-pagination__total');\n\n private readonly sizeChanger = this.locatorForOptional(WrSelectHarness.with({ selector: '.wr-pagination__size' }));\n\n /**\n * The accessible name of the `role=\"navigation\"` landmark.\n *\n * `''` only if the landmark lost its name, which is a bug rather than a state: a\n * page with two unnamed pagers gives a screen-reader user two identical\n * \"navigation\" landmarks.\n */\n async getLabel(): Promise<string> {\n return (await (await this.host()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The size the pager RENDERS at, from its host modifier.\n *\n * The modifier is always emitted — `sm` included — so the fall-through below is\n * only ever reached for the default, and a size the strip is not wearing cannot\n * come back from here.\n *\n * The STRIP resolves nothing through `provideWrConfig()`, which is worth knowing\n * before an app-wide default is blamed for a cell size: the pager passes its own\n * `size` down to every `<wr-btn>` explicitly, so a configured `button.size` never\n * reaches a cell. The page-size changer is the exception and not covered by this\n * value — it is a bare `<wr-select>`, so `provideWrConfig({ select: … })` does\n * reach it.\n */\n async getSize(): Promise<WrPaginationSize> {\n const host = await this.host();\n if (await host.hasClass('wr-pagination--lg')) return 'lg';\n if (await host.hasClass('wr-pagination--md')) return 'md';\n return 'sm';\n }\n\n /**\n * Whether the pager is ALLOWED to collapse into its compact pager.\n *\n * Whether it HAS collapsed is a container query on the control's own box, and a\n * unit test has no layout for one to answer — assert this input in jsdom and the\n * reflow itself in a real browser.\n */\n async isResponsive(): Promise<boolean> {\n return (await this.host()).hasClass('wr-pagination--responsive');\n }\n\n /**\n * Whether the whole control refuses interaction.\n *\n * Read from the host modifier, because a `role=\"navigation\"` landmark has no ARIA\n * disabled state to prefer — the per-control answer lives on the buttons\n * themselves (`disabled` plus `aria-disabled` on each), which is what\n * {@link isPreviousDisabled} and {@link isNextDisabled} read.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.host()).hasClass('wr-pagination--disabled');\n }\n\n /**\n * The whole strip in DOM order: a `number` for every page cell, and the gap\n * marker's own text (`…`) for every ellipsis.\n *\n * The marker is read from the DOM rather than compared against a literal, so a\n * component that changes what it draws in a gap changes this too instead of\n * quietly reporting a page.\n */\n async getStrip(): Promise<(number | string)[]> {\n const slots = await this.stripSlots();\n return Promise.all(\n slots.map(async slot => {\n const text = (await slot.text()).trim();\n return (await slot.hasClass('wr-pagination__ellipsis')) ? text : Number(text);\n })\n );\n }\n\n /**\n * The pages that can be clicked right now, ascending. Gaps are not pages and are\n * not here — `[1, 8, 9, 10, 11, 12, 20]` for a twenty-page list sitting on 10.\n */\n async getPages(): Promise<number[]> {\n const cells = await this.pageCells();\n return Promise.all(cells.map(async cell => Number(await cell.getText())));\n }\n\n /**\n * How many pages there are — not how many are on the strip.\n *\n * Taken from the LAST cell, which the window always pins to the final page (the\n * first is pinned the same way), so this survives the gap that usually precedes\n * it. Deliberately not read from the compact `3 / 10` pager: that span is\n * `aria-hidden` and CSS-hidden outside responsive mode, so a browser-based\n * harness environment would read it as empty.\n */\n async getTotalPages(): Promise<number> {\n const pages = await this.getPages();\n const last = pages.at(-1);\n if (last === undefined) {\n throw new Error(\n 'WrPaginationHarness.getTotalPages(): the strip has no page cells at all. The pager offers page ' +\n '1 even for an empty list, so this is a rendering failure rather than an empty state.'\n );\n }\n return last;\n }\n\n /**\n * The page the pager announces as current, from `aria-current=\"page\"`.\n *\n * Throws when no cell claims it. The window always keeps the current page on the\n * strip, so getting here means `currentPage` is not one of the pages at all — it\n * is below 1, or not a whole number. The component clamps a page ABOVE the last\n * one and nothing else, and either survivor leaves every cell looking equally\n * inactive to an assistive tech user.\n */\n async getCurrentPage(): Promise<number> {\n const page = await this.currentPageOrNull();\n if (page === null) {\n throw new Error(\n 'WrPaginationHarness.getCurrentPage(): no cell carries `aria-current=\"page\"`. The visible window ' +\n `always includes the current page, so \\`currentPage\\` is not a page: below 1, or not a whole ` +\n `number (the pager clamps above ${await this.getTotalPages()}, and nothing else).`\n );\n }\n return page;\n }\n\n /**\n * Click the cell for a page.\n *\n * Throws when that page is not on the strip — a seven-slot window over twenty\n * pages simply has no cell for page 15, and a silent no-op there reads as\n * \"navigation is broken\" three assertions later. Step with {@link next} /\n * {@link previous}, or move the host's own `currentPage` model.\n *\n * A disabled pager is still clicked; the component's own guard is what refuses\n * the move, so assert the page rather than assume it. Clicking the CURRENT page\n * is a no-op by the same guard.\n */\n async goToPage(page: number): Promise<void> {\n for (const cell of await this.pageCells()) {\n if (Number(await cell.getText()) === page) {\n await cell.click();\n return;\n }\n }\n\n throw new Error(\n `WrPaginationHarness.goToPage(): page ${page} is not on the strip, which offers ` +\n `${(await this.getStrip()).join(' ')}. The window shows at most seven pages — step with next() / ` +\n 'previous(), or set the page on the host.'\n );\n }\n\n /**\n * Jump to page 1.\n *\n * There is no dedicated first / last control in this component; the window pins\n * the first and last page to the ends of the strip instead, so these two click\n * cells like any other page.\n */\n async goToFirst(): Promise<void> {\n return this.goToPage(1);\n }\n\n /** Jump to the last page — the trailing cell, not the last one before a gap. */\n async goToLast(): Promise<void> {\n return this.goToPage(await this.getTotalPages());\n }\n\n /**\n * Click the next-page control.\n *\n * Disabled on the last page, and a disabled `<wr-btn>` is still clicked here (see\n * {@link goToPage}) — {@link isNextDisabled} is the question, this is the action.\n */\n async next(): Promise<void> {\n return (await this.stepControl('next')).click();\n }\n\n /** Click the previous-page control. Disabled on the first page. */\n async previous(): Promise<void> {\n return (await this.stepControl('previous')).click();\n }\n\n /**\n * Whether the next-page control refuses the move — true on the last page, and\n * true for every control while the pager is disabled.\n *\n * Answered by {@link WrButtonHarness}, which reads the `disabled` attribute first\n * and `aria-disabled` second. A `<wr-btn>` element carries both: the attribute is\n * inert on anything but a real `<button>`, which is why the component adds the\n * ARIA state as well — so this is the accessible answer whichever branch supplies\n * it.\n */\n async isNextDisabled(): Promise<boolean> {\n return (await this.stepControl('next')).isDisabled();\n }\n\n /** Whether the previous-page control refuses the move — true on the first page. */\n async isPreviousDisabled(): Promise<boolean> {\n return (await this.stepControl('previous')).isDisabled();\n }\n\n /** Whether the pager shows its \"X–Y of Z\" label (the `showTotal` input). */\n async hasTotal(): Promise<boolean> {\n return (await this.totalLabel()) !== null;\n }\n\n /**\n * The total label exactly as rendered, e.g. `1-10 of 95`.\n *\n * Both halves are localizable — the word between range and total comes from\n * `ofLabel` or the `pagination.of` catalog entry — so a spec asserting the whole\n * string should own the copy it asserts.\n *\n * Throws when the label is not rendered, which is a `showTotal` that was never\n * set rather than a total of zero (a zero total still renders `0-0 of 0`).\n */\n async getTotalText(): Promise<string> {\n const label = await this.totalLabel();\n if (!label) {\n throw new Error(\n 'WrPaginationHarness.getTotalText(): this pager renders no total label — set `showTotal`. A ' +\n 'total of 0 still renders one.'\n );\n }\n return label.text();\n }\n\n /** Whether the pager renders its page-size changer (the `showSizeChanger` input). */\n async hasPageSizeChanger(): Promise<boolean> {\n return (await this.sizeChanger()) !== null;\n }\n\n /**\n * The page-size changer as a {@link WrSelectHarness} — for anything this harness\n * does not wrap: the offered labels, the open state, keyboard driving.\n *\n * Composed rather than re-queried on purpose: the changer is a real\n * `<wr-select>`, its options live in the overlay container rather than inside the\n * pager, and the select harness is the thing that already knows how to scope them\n * to their own trigger.\n *\n * Throws when there is no changer — an `undefined` here would turn into a\n * confusing failure inside a select call.\n */\n async getPageSizeSelect(): Promise<WrSelectHarness> {\n const select = await this.sizeChanger();\n if (!select) {\n throw new Error('WrPaginationHarness.getPageSizeSelect(): no page-size changer here — set `showSizeChanger`.');\n }\n return select;\n }\n\n /**\n * The page size currently in effect.\n *\n * The changer's trigger is the only place the size reaches the DOM, so this reads\n * it there and takes the digits: the label is localizable (`pagination.perPage`,\n * `{{size}} / page`) but interpolates the number in every language — unless a\n * catalog entry drops the placeholder, which is the second throw below.\n *\n * Throws when there is no changer at all: a pager without one says nothing on\n * screen about its page size, and the host's own model is where to assert it.\n */\n async getPageSize(): Promise<number> {\n const text = await (await this.getPageSizeSelect()).getValueText();\n const digits = /\\d+/.exec(text);\n if (!digits) {\n throw new Error(\n `WrPaginationHarness.getPageSize(): the changer reads \"${text}\", which names no number — a ` +\n 'catalog entry for `pagination.perPage` that drops `{{size}}` takes the size off the screen.'\n );\n }\n return Number(digits[0]);\n }\n\n /**\n * Pick a page size through the changer, the way a user does.\n *\n * Matched on the digits rather than the whole label, for the same reason\n * {@link getPageSize} reads them: `20 / page` and `20 / стр.` are the same option.\n * Throws when no option offers that size, naming what is on offer.\n *\n * Changing the size can move the page — the component pulls `currentPage` back\n * when the new size leaves it past the end — so re-read {@link getCurrentPage}\n * afterwards rather than assuming it held.\n */\n async setPageSize(size: number): Promise<void> {\n const select = await this.getPageSizeSelect();\n if (await this.isDisabled()) {\n // Without this the call fails inside the select harness, whose message blames\n // a tag-mode panel and a `minChars` search that have nothing to do with it.\n throw new Error(\n 'WrPaginationHarness.setPageSize(): the pager is disabled, so its changer refuses to open — the ' +\n 'pager hands its own `disabled` straight to it.'\n );\n }\n\n await select.open();\n\n const labels = await select.getOptionLabels();\n const digits = new RegExp(`(^|\\\\D)${size}(\\\\D|$)`);\n const match = labels.find(label => digits.test(label));\n if (match === undefined) {\n throw new Error(\n `WrPaginationHarness.setPageSize(): no option offers ${size} — the changer offers ` +\n `${labels.join(', ')}. \\`pageSizeOptions\\` is what decides that list.`\n );\n }\n\n await select.selectOption({ text: match });\n }\n\n /** The current page, or `null` when no cell announces one. @see getCurrentPage */\n private async currentPageOrNull(): Promise<number | null> {\n const cell = await this.locatorForOptional(\n WrButtonHarness.with({ selector: '.wr-pagination__page[aria-current=\"page\"]' })\n )();\n return cell ? Number(await cell.getText()) : null;\n }\n\n private async stepControl(which: 'previous' | 'next'): Promise<WrButtonHarness> {\n const controls = await this.stepControls();\n if (controls.length !== 2) {\n throw new Error(\n `WrPaginationHarness: expected the nav to bracket the strip with a previous and a next control, ` +\n `found ${controls.length}. They are addressed by position, since their labels are localizable.`\n );\n }\n return which === 'previous' ? controls[0] : controls[1];\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;AAAA;;;;;AAKG;AAUH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDG;AACG,MAAO,mBAAoB,SAAQ,gBAAgB,CAAA;AACvD,IAAA,OAAO,YAAY,GAAG,eAAe;;AAGrC,IAAA,OAAO,IAAI,CAAC,OAAA,GAAsC,EAAE,EAAA;AAClD,QAAA,OAAO,IAAI,gBAAgB,CAAC,mBAAmB,EAAE,OAAO;aACrD,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,KAAK,gBAAgB,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC;AAC/G,aAAA,SAAS,CAAC,aAAa,EAAE,OAAO,CAAC,WAAW,EAAE,OAAO,OAAO,EAAE,IAAI,KAAI;;;;YAIrE,OAAO,CAAC,MAAM,OAAO,CAAC,iBAAiB,EAAE,MAAM,IAAI;AACrD,QAAA,CAAC;aACA,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,MAAM,OAAO,CAAC,UAAU,EAAE,MAAM,QAAQ,CAAC;IACpH;;AAGiB,IAAA,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,sBAAsB,EAAE,CAAC,CAAC;AAE3G;;;;;;;;AAQG;AACc,IAAA,YAAY,GAAG,IAAI,CAAC,aAAa,CAChD,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,wDAAwD,EAAE,CAAC,CAC7F;;AAGgB,IAAA,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,gDAAgD,CAAC;AAEjF,IAAA,UAAU,GAAG,IAAI,CAAC,kBAAkB,CAAC,uBAAuB,CAAC;AAE7D,IAAA,WAAW,GAAG,IAAI,CAAC,kBAAkB,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,sBAAsB,EAAE,CAAC,CAAC;AAElH;;;;;;AAMG;AACH,IAAA,MAAM,QAAQ,GAAA;AACZ,QAAA,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,YAAY,CAAC,YAAY,CAAC,KAAK,EAAE;IACrE;AAEA;;;;;;;;;;;;;AAaG;AACH,IAAA,MAAM,OAAO,GAAA;AACX,QAAA,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE;AAC9B,QAAA,IAAI,MAAM,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC;AAAE,YAAA,OAAO,IAAI;AACzD,QAAA,IAAI,MAAM,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC;AAAE,YAAA,OAAO,IAAI;AACzD,QAAA,OAAO,IAAI;IACb;AAEA;;;;;;AAMG;AACH,IAAA,MAAM,YAAY,GAAA;AAChB,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,2BAA2B,CAAC;IAClE;AAEA;;;;;;;AAOG;AACH,IAAA,MAAM,UAAU,GAAA;AACd,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,yBAAyB,CAAC;IAChE;AAEA;;;;;;;AAOG;AACH,IAAA,MAAM,QAAQ,GAAA;AACZ,QAAA,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE;AACrC,QAAA,OAAO,OAAO,CAAC,GAAG,CAChB,KAAK,CAAC,GAAG,CAAC,OAAM,IAAI,KAAG;AACrB,YAAA,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE;YACvC,OAAO,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC,IAAI,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QAC/E,CAAC,CAAC,CACH;IACH;AAEA;;;AAGG;AACH,IAAA,MAAM,QAAQ,GAAA;AACZ,QAAA,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE;QACpC,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAM,IAAI,KAAI,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC3E;AAEA;;;;;;;;AAQG;AACH,IAAA,MAAM,aAAa,GAAA;AACjB,QAAA,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE;QACnC,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACzB,QAAA,IAAI,IAAI,KAAK,SAAS,EAAE;YACtB,MAAM,IAAI,KAAK,CACb,iGAAiG;AAC/F,gBAAA,sFAAsF,CACzF;QACH;AACA,QAAA,OAAO,IAAI;IACb;AAEA;;;;;;;;AAQG;AACH,IAAA,MAAM,cAAc,GAAA;AAClB,QAAA,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE;AAC3C,QAAA,IAAI,IAAI,KAAK,IAAI,EAAE;YACjB,MAAM,IAAI,KAAK,CACb,kGAAkG;gBAChG,CAAA,4FAAA,CAA8F;AAC9F,gBAAA,CAAA,+BAAA,EAAkC,MAAM,IAAI,CAAC,aAAa,EAAE,CAAA,oBAAA,CAAsB,CACrF;QACH;AACA,QAAA,OAAO,IAAI;IACb;AAEA;;;;;;;;;;;AAWG;IACH,MAAM,QAAQ,CAAC,IAAY,EAAA;QACzB,KAAK,MAAM,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,EAAE,EAAE;YACzC,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,EAAE;AACzC,gBAAA,MAAM,IAAI,CAAC,KAAK,EAAE;gBAClB;YACF;QACF;AAEA,QAAA,MAAM,IAAI,KAAK,CACb,CAAA,qCAAA,EAAwC,IAAI,CAAA,mCAAA,CAAqC;AAC/E,YAAA,CAAA,EAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA,4DAAA,CAA8D;AAClG,YAAA,0CAA0C,CAC7C;IACH;AAEA;;;;;;AAMG;AACH,IAAA,MAAM,SAAS,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzB;;AAGA,IAAA,MAAM,QAAQ,GAAA;QACZ,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;IAClD;AAEA;;;;;AAKG;AACH,IAAA,MAAM,IAAI,GAAA;AACR,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE;IACjD;;AAGA,IAAA,MAAM,QAAQ,GAAA;AACZ,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE;IACrD;AAEA;;;;;;;;;AASG;AACH,IAAA,MAAM,cAAc,GAAA;AAClB,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE;IACtD;;AAGA,IAAA,MAAM,kBAAkB,GAAA;AACtB,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,UAAU,EAAE;IAC1D;;AAGA,IAAA,MAAM,QAAQ,GAAA;QACZ,OAAO,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE,MAAM,IAAI;IAC3C;AAEA;;;;;;;;;AASG;AACH,IAAA,MAAM,YAAY,GAAA;AAChB,QAAA,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE;QACrC,IAAI,CAAC,KAAK,EAAE;YACV,MAAM,IAAI,KAAK,CACb,6FAA6F;AAC3F,gBAAA,+BAA+B,CAClC;QACH;AACA,QAAA,OAAO,KAAK,CAAC,IAAI,EAAE;IACrB;;AAGA,IAAA,MAAM,kBAAkB,GAAA;QACtB,OAAO,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,MAAM,IAAI;IAC5C;AAEA;;;;;;;;;;;AAWG;AACH,IAAA,MAAM,iBAAiB,GAAA;AACrB,QAAA,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE;QACvC,IAAI,CAAC,MAAM,EAAE;AACX,YAAA,MAAM,IAAI,KAAK,CAAC,6FAA6F,CAAC;QAChH;AACA,QAAA,OAAO,MAAM;IACf;AAEA;;;;;;;;;;AAUG;AACH,IAAA,MAAM,WAAW,GAAA;AACf,QAAA,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,iBAAiB,EAAE,EAAE,YAAY,EAAE;QAClE,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAC/B,IAAI,CAAC,MAAM,EAAE;AACX,YAAA,MAAM,IAAI,KAAK,CACb,CAAA,sDAAA,EAAyD,IAAI,CAAA,6BAAA,CAA+B;AAC1F,gBAAA,6FAA6F,CAChG;QACH;AACA,QAAA,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1B;AAEA;;;;;;;;;;AAUG;IACH,MAAM,WAAW,CAAC,IAAY,EAAA;AAC5B,QAAA,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE;AAC7C,QAAA,IAAI,MAAM,IAAI,CAAC,UAAU,EAAE,EAAE;;;YAG3B,MAAM,IAAI,KAAK,CACb,iGAAiG;AAC/F,gBAAA,gDAAgD,CACnD;QACH;AAEA,QAAA,MAAM,MAAM,CAAC,IAAI,EAAE;AAEnB,QAAA,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,eAAe,EAAE;QAC7C,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,CAAA,OAAA,EAAU,IAAI,CAAA,OAAA,CAAS,CAAC;AAClD,QAAA,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACtD,QAAA,IAAI,KAAK,KAAK,SAAS,EAAE;AACvB,YAAA,MAAM,IAAI,KAAK,CACb,CAAA,oDAAA,EAAuD,IAAI,CAAA,sBAAA,CAAwB;gBACjF,CAAA,EAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,gDAAA,CAAkD,CACzE;QACH;QAEA,MAAM,MAAM,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAC5C;;AAGQ,IAAA,MAAM,iBAAiB,GAAA;AAC7B,QAAA,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,kBAAkB,CACxC,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,2CAA2C,EAAE,CAAC,CAChF,EAAE;AACH,QAAA,OAAO,IAAI,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI;IACnD;IAEQ,MAAM,WAAW,CAAC,KAA0B,EAAA;AAClD,QAAA,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE;AAC1C,QAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;YACzB,MAAM,IAAI,KAAK,CACb,CAAA,+FAAA,CAAiG;AAC/F,gBAAA,CAAA,MAAA,EAAS,QAAQ,CAAC,MAAM,CAAA,qEAAA,CAAuE,CAClG;QACH;AACA,QAAA,OAAO,KAAK,KAAK,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;IACzD;;;ACtbF;;AAEG;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { model, input, computed, ViewEncapsulation, Component } from '@angular/core';
|
|
3
|
+
import { model, input, computed, effect, untracked, ViewEncapsulation, Component } from '@angular/core';
|
|
4
4
|
import { WrButton } from 'ngwr/button';
|
|
5
5
|
import { useI18nText, useI18nFormatter } from 'ngwr/i18n';
|
|
6
6
|
import { WrSelect, WrOption } from 'ngwr/select';
|
|
@@ -171,6 +171,18 @@ class WrPagination {
|
|
|
171
171
|
* untyped and is narrowed here. It used to arrive as `any` through
|
|
172
172
|
* `[ngModel]`, which hid the coercion rather than removing the need for it.
|
|
173
173
|
*/
|
|
174
|
+
constructor() {
|
|
175
|
+
// A shrinking `total` must not leave the pager on a page that no longer
|
|
176
|
+
// exists: it only ever pulled back when the SIZE changed through its own
|
|
177
|
+
// control, so a filtered list left "page 7 of 2" pointing at nothing.
|
|
178
|
+
effect(() => {
|
|
179
|
+
const cap = this.totalPages();
|
|
180
|
+
untracked(() => {
|
|
181
|
+
if (this.currentPage() > cap)
|
|
182
|
+
this.currentPage.set(cap);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
}
|
|
174
186
|
onSizeChange(size) {
|
|
175
187
|
const next = Number(size);
|
|
176
188
|
if (this.disabled() || !Number.isFinite(next) || next <= 0 || next === this.pageSize())
|
|
@@ -186,7 +198,7 @@ class WrPagination {
|
|
|
186
198
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrPagination, decorators: [{
|
|
187
199
|
type: Component,
|
|
188
200
|
args: [{ selector: 'wr-pagination', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()', role: 'navigation', '[attr.aria-label]': 'navLabel()' }, imports: [WrButton, WrSelect, WrOption], template: "<div class=\"wr-pagination__inner\">\n @if (showTotal()) {\n <span class=\"wr-pagination__total\">{{ rangeLabel() }}</span>\n }\n\n <div class=\"wr-pagination__nav\">\n <wr-btn\n [size]=\"size()\"\n outlined\n [attr.aria-label]=\"resolvedPrev()\"\n [disabled]=\"disabled() || currentPage() === 1\"\n (click)=\"goTo(currentPage() - 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m12 19-7-7 7-7\" />\n <path d=\"M19 12H5\" />\n </svg>\n </wr-btn>\n\n @for (page of pages(); track $index) {\n @if (page === ellipsis) {\n <span class=\"wr-pagination__ellipsis\">{{ ellipsis }}</span>\n } @else {\n <wr-btn\n class=\"wr-pagination__page\"\n [size]=\"size()\"\n [outlined]=\"!isCurrent(page)\"\n [color]=\"isCurrent(page) ? 'primary' : null\"\n [attr.aria-current]=\"isCurrent(page) ? 'page' : null\"\n [attr.aria-label]=\"goToPage({ page })\"\n [disabled]=\"disabled()\"\n (click)=\"goTo(page)\"\n >\n {{ page }}\n </wr-btn>\n }\n }\n\n <!-- Compact pager \u2014 shown only in responsive mode on a narrow box (CSS). -->\n <span class=\"wr-pagination__current\" aria-hidden=\"true\">{{ currentPage() }} / {{ totalPages() }}</span>\n\n <wr-btn\n [size]=\"size()\"\n outlined\n [attr.aria-label]=\"resolvedNext()\"\n [disabled]=\"disabled() || currentPage() === totalPages()\"\n (click)=\"goTo(currentPage() + 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M5 12h14\" />\n <path d=\"m12 5 7 7-7 7\" />\n </svg>\n </wr-btn>\n </div>\n\n @if (showSizeChanger()) {\n <wr-select\n class=\"wr-pagination__size\"\n [attr.aria-label]=\"resolvedItemsPerPage()\"\n [disabled]=\"disabled()\"\n [value]=\"pageSize()\"\n (valueChange)=\"onSizeChange($event)\"\n >\n @for (option of pageSizeOptions(); track option) {\n <wr-option [value]=\"option\">{{ perPage({ size: option }) }}</wr-option>\n }\n </wr-select>\n }\n</div>\n" }]
|
|
189
|
-
}], propDecorators: { currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }, { type: i0.Output, args: ["currentPageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], showSizeChanger: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSizeChanger", required: false }] }], showTotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTotal", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], ofLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ofLabel", required: false }] }], prevLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], itemsPerPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemsPerPageLabel", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
|
|
201
|
+
}], ctorParameters: () => [], propDecorators: { currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }, { type: i0.Output, args: ["currentPageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], showSizeChanger: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSizeChanger", required: false }] }], showTotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTotal", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], ofLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ofLabel", required: false }] }], prevLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], itemsPerPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemsPerPageLabel", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
|
|
190
202
|
|
|
191
203
|
/**
|
|
192
204
|
* Generated bundle index. Do not edit.
|