ngwr 10.2.0 → 11.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -19
- package/anchor/styles/_index.scss +4 -4
- package/back-top/styles/_index.scss +6 -1
- package/blur-text/styles/_index.scss +13 -0
- package/button/styles/_index.scss +11 -6
- package/carousel/styles/_index.scss +13 -2
- package/cascader/styles/_index.scss +2 -2
- package/circular-text/styles/_index.scss +2 -0
- package/collapse/styles/_index.scss +1 -1
- package/color-picker/styles/_index.scss +6 -1
- package/compare/styles/_index.scss +1 -2
- package/context-menu/styles/_index.scss +6 -1
- package/descriptions/styles/_index.scss +2 -2
- package/dialog/styles/_index.scss +38 -0
- package/drawer/styles/_index.scss +20 -1
- package/dropdown/styles/_index.scss +19 -5
- package/empty/styles/_index.scss +10 -0
- package/fesm2022/ngwr-action-sheet.mjs +15 -5
- package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
- package/fesm2022/ngwr-affix.mjs +43 -16
- package/fesm2022/ngwr-affix.mjs.map +1 -1
- package/fesm2022/ngwr-alert.mjs +3 -3
- package/fesm2022/ngwr-anchor.mjs +20 -8
- package/fesm2022/ngwr-anchor.mjs.map +1 -1
- package/fesm2022/ngwr-aurora.mjs +4 -4
- package/fesm2022/ngwr-aurora.mjs.map +1 -1
- package/fesm2022/ngwr-avatar.mjs +33 -9
- package/fesm2022/ngwr-avatar.mjs.map +1 -1
- package/fesm2022/ngwr-back-top.mjs +8 -4
- package/fesm2022/ngwr-back-top.mjs.map +1 -1
- package/fesm2022/ngwr-badge.mjs +6 -6
- package/fesm2022/ngwr-badge.mjs.map +1 -1
- package/fesm2022/ngwr-bar-chart.mjs +32 -11
- package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
- package/fesm2022/ngwr-blur-text.mjs +4 -4
- package/fesm2022/ngwr-blur-text.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow.mjs +4 -4
- package/fesm2022/ngwr-border-glow.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +9 -8
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-burger.mjs +11 -6
- 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 +89 -24
- package/fesm2022/ngwr-button.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-heatmap.mjs +42 -10
- package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
- package/fesm2022/ngwr-calendar.mjs +108 -9
- package/fesm2022/ngwr-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +10 -10
- package/fesm2022/ngwr-card.mjs.map +1 -1
- package/fesm2022/ngwr-carousel-testing.mjs +377 -0
- package/fesm2022/ngwr-carousel-testing.mjs.map +1 -0
- package/fesm2022/ngwr-carousel.mjs +72 -18
- 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 +4 -4
- 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 +15 -9
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text.mjs +4 -4
- package/fesm2022/ngwr-circular-text.mjs.map +1 -1
- package/fesm2022/ngwr-click-spark.mjs +4 -4
- package/fesm2022/ngwr-click-spark.mjs.map +1 -1
- package/fesm2022/ngwr-clipboard.mjs +18 -4
- package/fesm2022/ngwr-clipboard.mjs.map +1 -1
- package/fesm2022/ngwr-collapse-testing.mjs +368 -0
- package/fesm2022/ngwr-collapse-testing.mjs.map +1 -0
- package/fesm2022/ngwr-collapse.mjs +9 -9
- package/fesm2022/ngwr-color-picker.mjs +139 -19
- 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 +60 -10
- package/fesm2022/ngwr-command-palette.mjs.map +1 -1
- package/fesm2022/ngwr-compare.mjs +105 -15
- package/fesm2022/ngwr-compare.mjs.map +1 -1
- package/fesm2022/ngwr-confetti.mjs +3 -3
- 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 +104 -18
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-cookie.mjs +8 -4
- package/fesm2022/ngwr-cookie.mjs.map +1 -1
- package/fesm2022/ngwr-counter.mjs +17 -9
- package/fesm2022/ngwr-counter.mjs.map +1 -1
- package/fesm2022/ngwr-date-adapter-fns.mjs +3 -3
- package/fesm2022/ngwr-date-adapter-luxon.mjs +55 -7
- package/fesm2022/ngwr-date-adapter-luxon.mjs.map +1 -1
- package/fesm2022/ngwr-date-adapter.mjs +57 -11
- 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 +409 -57
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-decrypt-text.mjs +30 -12
- package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
- package/fesm2022/ngwr-density.mjs +6 -6
- package/fesm2022/ngwr-descriptions.mjs +6 -6
- package/fesm2022/ngwr-dialog-testing.mjs +89 -0
- package/fesm2022/ngwr-dialog-testing.mjs.map +1 -0
- package/fesm2022/ngwr-dialog.mjs +33 -17
- package/fesm2022/ngwr-dialog.mjs.map +1 -1
- package/fesm2022/ngwr-directives.mjs +48 -24
- package/fesm2022/ngwr-directives.mjs.map +1 -1
- package/fesm2022/ngwr-divider.mjs +3 -3
- package/fesm2022/ngwr-donut-chart.mjs +24 -7
- package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +7 -7
- 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-drawer.mjs +37 -19
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown-testing.mjs +291 -0
- package/fesm2022/ngwr-dropdown-testing.mjs.map +1 -0
- package/fesm2022/ngwr-dropdown.mjs +19 -11
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-empty.mjs +26 -9
- package/fesm2022/ngwr-empty.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar.mjs +62 -15
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text.mjs +4 -4
- 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-file-upload.mjs +3 -3
- package/fesm2022/ngwr-form-testing.mjs +451 -0
- package/fesm2022/ngwr-form-testing.mjs.map +1 -0
- package/fesm2022/ngwr-form.mjs +28 -12
- package/fesm2022/ngwr-form.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text.mjs +11 -4
- package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
- package/fesm2022/ngwr-gauge.mjs +21 -7
- package/fesm2022/ngwr-gauge.mjs.map +1 -1
- package/fesm2022/ngwr-glitch-text.mjs +4 -4
- package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
- package/fesm2022/ngwr-gradient-text.mjs +4 -4
- package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
- package/fesm2022/ngwr-hotkey.mjs +31 -10
- package/fesm2022/ngwr-hotkey.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +61 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +59 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +9 -9
- package/fesm2022/ngwr-icon.mjs +6 -6
- package/fesm2022/ngwr-image-cropper.mjs +26 -3
- 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 +37 -5
- 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 +39 -8
- 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 +62 -25
- package/fesm2022/ngwr-input.mjs.map +1 -1
- package/fesm2022/ngwr-keyboard.mjs +3 -3
- package/fesm2022/ngwr-knob.mjs +46 -10
- package/fesm2022/ngwr-knob.mjs.map +1 -1
- package/fesm2022/ngwr-layout.mjs +36 -20
- package/fesm2022/ngwr-layout.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox.mjs +35 -8
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart.mjs +19 -6
- package/fesm2022/ngwr-line-chart.mjs.map +1 -1
- package/fesm2022/ngwr-list.mjs +14 -9
- package/fesm2022/ngwr-list.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar.mjs +71 -31
- 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 +19 -8
- package/fesm2022/ngwr-marquee.mjs.map +1 -1
- package/fesm2022/ngwr-media.mjs +3 -3
- package/fesm2022/ngwr-mention-testing.mjs +513 -0
- package/fesm2022/ngwr-mention-testing.mjs.map +1 -0
- package/fesm2022/ngwr-mention.mjs +16 -7
- package/fesm2022/ngwr-mention.mjs.map +1 -1
- package/fesm2022/ngwr-meta.mjs +28 -13
- package/fesm2022/ngwr-meta.mjs.map +1 -1
- package/fesm2022/ngwr-meter-group.mjs +22 -8
- package/fesm2022/ngwr-meter-group.mjs.map +1 -1
- package/fesm2022/ngwr-overlay.mjs +57 -8
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-page-header.mjs +3 -3
- package/fesm2022/ngwr-pagination-testing.mjs +389 -0
- package/fesm2022/ngwr-pagination-testing.mjs.map +1 -0
- package/fesm2022/ngwr-pagination.mjs +27 -11
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-pipes.mjs +21 -21
- package/fesm2022/ngwr-platform.mjs +9 -9
- package/fesm2022/ngwr-popconfirm-testing.mjs +309 -0
- package/fesm2022/ngwr-popconfirm-testing.mjs.map +1 -0
- package/fesm2022/ngwr-popconfirm.mjs +99 -21
- 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 +48 -10
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-progress.mjs +3 -3
- package/fesm2022/ngwr-pull-to-refresh.mjs +19 -4
- package/fesm2022/ngwr-pull-to-refresh.mjs.map +1 -1
- package/fesm2022/ngwr-qr.mjs +15 -5
- 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 +16 -9
- 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 +40 -5
- package/fesm2022/ngwr-rating.mjs.map +1 -1
- package/fesm2022/ngwr-result.mjs +12 -12
- package/fesm2022/ngwr-rotating-text.mjs +3 -3
- package/fesm2022/ngwr-scroll.mjs +3 -3
- package/fesm2022/ngwr-segmented-testing.mjs +512 -0
- package/fesm2022/ngwr-segmented-testing.mjs.map +1 -0
- package/fesm2022/ngwr-segmented.mjs +3 -3
- package/fesm2022/ngwr-select-testing.mjs +268 -0
- package/fesm2022/ngwr-select-testing.mjs.map +1 -0
- package/fesm2022/ngwr-select.mjs +96 -25
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text.mjs +7 -7
- package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
- package/fesm2022/ngwr-sidebar.mjs +67 -14
- package/fesm2022/ngwr-sidebar.mjs.map +1 -1
- package/fesm2022/ngwr-skeleton.mjs +3 -3
- package/fesm2022/ngwr-slider-testing.mjs +478 -0
- package/fesm2022/ngwr-slider-testing.mjs.map +1 -0
- package/fesm2022/ngwr-slider.mjs +59 -15
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline.mjs +28 -8
- package/fesm2022/ngwr-sparkline.mjs.map +1 -1
- package/fesm2022/ngwr-speed-dial.mjs +39 -6
- package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
- package/fesm2022/ngwr-spinner.mjs +14 -5
- package/fesm2022/ngwr-spinner.mjs.map +1 -1
- package/fesm2022/ngwr-splash-cursor.mjs +4 -4
- package/fesm2022/ngwr-splash-cursor.mjs.map +1 -1
- package/fesm2022/ngwr-split-text.mjs +4 -4
- package/fesm2022/ngwr-split-text.mjs.map +1 -1
- package/fesm2022/ngwr-splitter.mjs +72 -11
- package/fesm2022/ngwr-splitter.mjs.map +1 -1
- package/fesm2022/ngwr-spotlight-card.mjs +7 -7
- package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
- package/fesm2022/ngwr-squircle.mjs +6 -6
- package/fesm2022/ngwr-star-border.mjs +4 -4
- package/fesm2022/ngwr-star-border.mjs.map +1 -1
- package/fesm2022/ngwr-statistic.mjs +33 -16
- 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-stepper.mjs +6 -6
- package/fesm2022/ngwr-storage.mjs +27 -17
- package/fesm2022/ngwr-storage.mjs.map +1 -1
- package/fesm2022/ngwr-switch-testing.mjs +77 -0
- package/fesm2022/ngwr-switch-testing.mjs.map +1 -0
- package/fesm2022/ngwr-switch.mjs +12 -6
- 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 +105 -44
- 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 +44 -12
- 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 +35 -7
- package/fesm2022/ngwr-textarea.mjs.map +1 -1
- package/fesm2022/ngwr-theme.mjs +3 -3
- package/fesm2022/ngwr-tilt-card.mjs +7 -7
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
- package/fesm2022/ngwr-timeline.mjs +6 -6
- package/fesm2022/ngwr-toast-testing.mjs +101 -0
- package/fesm2022/ngwr-toast-testing.mjs.map +1 -0
- package/fesm2022/ngwr-toast.mjs +55 -21
- package/fesm2022/ngwr-toast.mjs.map +1 -1
- package/fesm2022/ngwr-toolbar.mjs +3 -3
- package/fesm2022/ngwr-tour.mjs +67 -15
- 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 +47 -16
- 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 +75 -9
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter.mjs +7 -5
- package/fesm2022/ngwr-typewriter.mjs.map +1 -1
- package/fesm2022/ngwr-typography.mjs +3 -3
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-virtual-scroll.mjs +33 -7
- package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
- package/fesm2022/ngwr-waves.mjs +5 -5
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/fesm2022/ngwr-window.mjs +88 -18
- package/fesm2022/ngwr-window.mjs.map +1 -1
- package/gauge/styles/_index.scss +1 -1
- package/glitch-text/styles/_index.scss +4 -0
- package/i18n/en.json +56 -0
- package/i18n/ru.json +56 -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/schematics/use/symbol-map.json +2 -0
- package/segmented/styles/_index.scss +7 -0
- package/select/styles/_index.scss +29 -9
- 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 +1 -1
- package/styles/_grid.scss +4 -4
- package/styles.scss +1 -0
- package/switch/styles/_index.scss +5 -3
- package/table/styles/_index.scss +22 -11
- package/tabs/styles/_index.scss +11 -1
- package/textarea/styles/_index.scss +5 -0
- package/theme/styles/_colors.scss +29 -13
- package/theme/styles/_dark.scss +11 -3
- 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 +5 -3
- 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 +58 -10
- package/types/ngwr-calendar-heatmap.d.ts +16 -2
- package/types/ngwr-calendar.d.ts +37 -1
- 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-clipboard.d.ts +8 -1
- 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 +128 -8
- package/types/ngwr-dialog-testing.d.ts +79 -0
- package/types/ngwr-dialog.d.ts +13 -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-drawer.d.ts +15 -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-hotkey.d.ts +2 -0
- 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-list.d.ts +7 -2
- package/types/ngwr-loading-bar.d.ts +4 -7
- 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 +7 -1
- 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 +19 -2
- 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-storage.d.ts +10 -1
- 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-toast.d.ts +19 -4
- 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/types/ngwr-window.d.ts +23 -2
- package/typewriter/styles/_index.scss +1 -1
- package/typography/styles/_index.scss +1 -1
- package/window/styles/_index.scss +32 -9
|
@@ -2,15 +2,13 @@ import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coerci
|
|
|
2
2
|
import { ScrollStrategyOptions } from '@angular/cdk/overlay';
|
|
3
3
|
import { ComponentPortal } from '@angular/cdk/portal';
|
|
4
4
|
import * as i0 from '@angular/core';
|
|
5
|
-
import { input, inject, model, output, signal, computed, effect, untracked, ViewEncapsulation, Component,
|
|
5
|
+
import { input, inject, ElementRef, model, output, signal, computed, afterNextRender, effect, untracked, ViewEncapsulation, Component, DestroyRef, viewChild } from '@angular/core';
|
|
6
6
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
7
7
|
import { WrCalendar } from 'ngwr/calendar';
|
|
8
8
|
import { WrDateAdapter, WR_DATE_LOCALE } from 'ngwr/date-adapter';
|
|
9
9
|
import { readI18nText, useI18nText } from 'ngwr/i18n';
|
|
10
10
|
import { WrInput, WrInputGroup, WrInputSuffix } from 'ngwr/input';
|
|
11
11
|
import { WR_OVERLAY, WrOutsideClick } from 'ngwr/overlay';
|
|
12
|
-
import * as i1 from '@angular/forms';
|
|
13
|
-
import { FormsModule } from '@angular/forms';
|
|
14
12
|
import { clamp } from 'ngwr/utils';
|
|
15
13
|
|
|
16
14
|
/**
|
|
@@ -54,7 +52,13 @@ class WrTimePanel {
|
|
|
54
52
|
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
55
53
|
/** Read-only — values cannot be changed. @default false */
|
|
56
54
|
readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
55
|
+
/**
|
|
56
|
+
* Move focus onto the hours field once the panel is on screen. Off by
|
|
57
|
+
* default — see `WrCalendar.autoFocus` for why. @default false
|
|
58
|
+
*/
|
|
59
|
+
autoFocus = input(false, { ...(ngDevMode ? { debugName: "autoFocus" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
57
60
|
adapter = inject(WrDateAdapter);
|
|
61
|
+
host = inject(ElementRef);
|
|
58
62
|
locale = inject(WR_DATE_LOCALE);
|
|
59
63
|
/**
|
|
60
64
|
* Form value — the selected time as a `Date` (its date portion is preserved).
|
|
@@ -78,6 +82,14 @@ class WrTimePanel {
|
|
|
78
82
|
effectiveDisabled = computed(() => this.disabled(), /* @ts-ignore */
|
|
79
83
|
...(ngDevMode ? [{ debugName: "effectiveDisabled" }] : /* istanbul ignore next */ []));
|
|
80
84
|
constructor() {
|
|
85
|
+
// The hours field, not the increment button: a stepper is a shortcut, the
|
|
86
|
+
// number is the control. Deferred because the inputs are not in the DOM
|
|
87
|
+
// until the panel's first change-detection pass.
|
|
88
|
+
afterNextRender(() => {
|
|
89
|
+
if (!this.autoFocus())
|
|
90
|
+
return;
|
|
91
|
+
this.host.nativeElement.querySelector('.wr-time-picker__input')?.focus();
|
|
92
|
+
});
|
|
81
93
|
// Mirror an external `value` write into the internal h/m/s state (the old
|
|
82
94
|
// `writeValue` body). Guarded against the echo of our own `emit()` so a live
|
|
83
95
|
// edit is never clobbered by re-deriving it.
|
|
@@ -146,7 +158,12 @@ class WrTimePanel {
|
|
|
146
158
|
lastEmitted = null;
|
|
147
159
|
// Typed-input handlers
|
|
148
160
|
onHoursInput(event) {
|
|
149
|
-
const
|
|
161
|
+
const raw = event.target.value;
|
|
162
|
+
// An emptied box is someone mid-retype, not a request for zero: `Number('')`
|
|
163
|
+
// is 0, so committing here rewrote the field they had just cleared.
|
|
164
|
+
if (!raw.trim())
|
|
165
|
+
return;
|
|
166
|
+
const n = Number(raw);
|
|
150
167
|
if (Number.isNaN(n))
|
|
151
168
|
return;
|
|
152
169
|
if (this.is12h()) {
|
|
@@ -159,14 +176,24 @@ class WrTimePanel {
|
|
|
159
176
|
this.emit();
|
|
160
177
|
}
|
|
161
178
|
onMinutesInput(event) {
|
|
162
|
-
const
|
|
179
|
+
const raw = event.target.value;
|
|
180
|
+
// An emptied box is someone mid-retype, not a request for zero: `Number('')`
|
|
181
|
+
// is 0, so committing here rewrote the field they had just cleared.
|
|
182
|
+
if (!raw.trim())
|
|
183
|
+
return;
|
|
184
|
+
const n = Number(raw);
|
|
163
185
|
if (Number.isNaN(n))
|
|
164
186
|
return;
|
|
165
187
|
this.minutes.set(clamp(Math.trunc(n), 0, 59));
|
|
166
188
|
this.emit();
|
|
167
189
|
}
|
|
168
190
|
onSecondsInput(event) {
|
|
169
|
-
const
|
|
191
|
+
const raw = event.target.value;
|
|
192
|
+
// An emptied box is someone mid-retype, not a request for zero: `Number('')`
|
|
193
|
+
// is 0, so committing here rewrote the field they had just cleared.
|
|
194
|
+
if (!raw.trim())
|
|
195
|
+
return;
|
|
196
|
+
const n = Number(raw);
|
|
170
197
|
if (Number.isNaN(n))
|
|
171
198
|
return;
|
|
172
199
|
this.seconds.set(clamp(Math.trunc(n), 0, 59));
|
|
@@ -207,13 +234,13 @@ class WrTimePanel {
|
|
|
207
234
|
this.lastEmitted = next;
|
|
208
235
|
this.value.set(next);
|
|
209
236
|
}
|
|
210
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
211
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
237
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrTimePanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
238
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrTimePanel, isStandalone: true, selector: "wr-time-picker", inputs: { format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, autoFocus: { classPropertyName: "autoFocus", publicName: "autoFocus", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: "<div class=\"wr-time-picker__col\">\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--up\"\n aria-label=\"Increment hours\"\n [disabled]=\"!interactive()\"\n (click)=\"stepHours(1)\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m18 15-6-6-6 6\" />\n </svg>\n </button>\n <input\n class=\"wr-time-picker__input\"\n type=\"text\"\n inputmode=\"numeric\"\n aria-label=\"Hours\"\n [value]=\"hoursDisplay()\"\n [disabled]=\"effectiveDisabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHoursInput($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--down\"\n aria-label=\"Decrement hours\"\n [disabled]=\"!interactive()\"\n (click)=\"stepHours(-1)\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n</div>\n\n<span class=\"wr-time-picker__sep\" aria-hidden=\"true\">:</span>\n\n<div class=\"wr-time-picker__col\">\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--up\"\n aria-label=\"Increment minutes\"\n [disabled]=\"!interactive()\"\n (click)=\"stepMinutes(1)\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m18 15-6-6-6 6\" />\n </svg>\n </button>\n <input\n class=\"wr-time-picker__input\"\n type=\"text\"\n inputmode=\"numeric\"\n aria-label=\"Minutes\"\n [value]=\"minutesDisplay()\"\n [disabled]=\"effectiveDisabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onMinutesInput($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--down\"\n aria-label=\"Decrement minutes\"\n [disabled]=\"!interactive()\"\n (click)=\"stepMinutes(-1)\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n</div>\n\n@if (showSeconds()) {\n <span class=\"wr-time-picker__sep\" aria-hidden=\"true\">:</span>\n\n <div class=\"wr-time-picker__col\">\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--up\"\n aria-label=\"Increment seconds\"\n [disabled]=\"!interactive()\"\n (click)=\"stepSeconds(1)\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m18 15-6-6-6 6\" />\n </svg>\n </button>\n <input\n class=\"wr-time-picker__input\"\n type=\"text\"\n inputmode=\"numeric\"\n aria-label=\"Seconds\"\n [value]=\"secondsDisplay()\"\n [disabled]=\"effectiveDisabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onSecondsInput($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--down\"\n aria-label=\"Decrement seconds\"\n [disabled]=\"!interactive()\"\n (click)=\"stepSeconds(-1)\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n </div>\n}\n\n@if (is12h()) {\n <div class=\"wr-time-picker__col wr-time-picker__col--ampm\">\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--up\"\n aria-label=\"Toggle AM / PM\"\n [disabled]=\"!interactive()\"\n (click)=\"toggleAmPm()\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m18 15-6-6-6 6\" />\n </svg>\n </button>\n <span class=\"wr-time-picker__label\" aria-live=\"polite\">{{ amPmLabel() }}</span>\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--down\"\n aria-label=\"Toggle AM / PM\"\n [disabled]=\"!interactive()\"\n (click)=\"toggleAmPm()\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n </div>\n}\n", encapsulation: i0.ViewEncapsulation.None });
|
|
212
239
|
}
|
|
213
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
240
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrTimePanel, decorators: [{
|
|
214
241
|
type: Component,
|
|
215
242
|
args: [{ selector: 'wr-time-picker', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, template: "<div class=\"wr-time-picker__col\">\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--up\"\n aria-label=\"Increment hours\"\n [disabled]=\"!interactive()\"\n (click)=\"stepHours(1)\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m18 15-6-6-6 6\" />\n </svg>\n </button>\n <input\n class=\"wr-time-picker__input\"\n type=\"text\"\n inputmode=\"numeric\"\n aria-label=\"Hours\"\n [value]=\"hoursDisplay()\"\n [disabled]=\"effectiveDisabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onHoursInput($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--down\"\n aria-label=\"Decrement hours\"\n [disabled]=\"!interactive()\"\n (click)=\"stepHours(-1)\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n</div>\n\n<span class=\"wr-time-picker__sep\" aria-hidden=\"true\">:</span>\n\n<div class=\"wr-time-picker__col\">\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--up\"\n aria-label=\"Increment minutes\"\n [disabled]=\"!interactive()\"\n (click)=\"stepMinutes(1)\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m18 15-6-6-6 6\" />\n </svg>\n </button>\n <input\n class=\"wr-time-picker__input\"\n type=\"text\"\n inputmode=\"numeric\"\n aria-label=\"Minutes\"\n [value]=\"minutesDisplay()\"\n [disabled]=\"effectiveDisabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onMinutesInput($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--down\"\n aria-label=\"Decrement minutes\"\n [disabled]=\"!interactive()\"\n (click)=\"stepMinutes(-1)\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n</div>\n\n@if (showSeconds()) {\n <span class=\"wr-time-picker__sep\" aria-hidden=\"true\">:</span>\n\n <div class=\"wr-time-picker__col\">\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--up\"\n aria-label=\"Increment seconds\"\n [disabled]=\"!interactive()\"\n (click)=\"stepSeconds(1)\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m18 15-6-6-6 6\" />\n </svg>\n </button>\n <input\n class=\"wr-time-picker__input\"\n type=\"text\"\n inputmode=\"numeric\"\n aria-label=\"Seconds\"\n [value]=\"secondsDisplay()\"\n [disabled]=\"effectiveDisabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onSecondsInput($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--down\"\n aria-label=\"Decrement seconds\"\n [disabled]=\"!interactive()\"\n (click)=\"stepSeconds(-1)\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n </div>\n}\n\n@if (is12h()) {\n <div class=\"wr-time-picker__col wr-time-picker__col--ampm\">\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--up\"\n aria-label=\"Toggle AM / PM\"\n [disabled]=\"!interactive()\"\n (click)=\"toggleAmPm()\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m18 15-6-6-6 6\" />\n </svg>\n </button>\n <span class=\"wr-time-picker__label\" aria-live=\"polite\">{{ amPmLabel() }}</span>\n <button\n type=\"button\"\n class=\"wr-time-picker__step wr-time-picker__step--down\"\n aria-label=\"Toggle AM / PM\"\n [disabled]=\"!interactive()\"\n (click)=\"toggleAmPm()\"\n >\n <svg\n class=\"wr-time-picker__chevron\"\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=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n </div>\n}\n" }]
|
|
216
|
-
}], ctorParameters: () => [], propDecorators: { format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }] } });
|
|
243
|
+
}], ctorParameters: () => [], propDecorators: { format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], autoFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFocus", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }] } });
|
|
217
244
|
|
|
218
245
|
/**
|
|
219
246
|
* @license
|
|
@@ -243,6 +270,13 @@ class WrDateTimePanel {
|
|
|
243
270
|
...(ngDevMode ? [{ debugName: "showSeconds" }] : /* istanbul ignore next */ []));
|
|
244
271
|
step = input(1, /* @ts-ignore */
|
|
245
272
|
...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
|
|
273
|
+
/**
|
|
274
|
+
* Forwarded to the calendar only: a datetime panel opened from the keyboard
|
|
275
|
+
* lands on the day grid, never on the hours field. Picking the date first is
|
|
276
|
+
* the order the panel is laid out in, and the time fields are one Tab away.
|
|
277
|
+
*/
|
|
278
|
+
autoFocus = input(false, /* @ts-ignore */
|
|
279
|
+
...(ngDevMode ? [{ debugName: "autoFocus" }] : /* istanbul ignore next */ []));
|
|
246
280
|
changed = output();
|
|
247
281
|
adapter = inject(WrDateAdapter);
|
|
248
282
|
/** Date portion used by the calendar — falls back to today when value is null. */
|
|
@@ -265,13 +299,13 @@ class WrDateTimePanel {
|
|
|
265
299
|
const combined = this.adapter.setTime(this.adapter.createDate(this.adapter.getYear(base), this.adapter.getMonth(base), this.adapter.getDate(base)), this.adapter.getHours(time), this.adapter.getMinutes(time), this.adapter.getSeconds(time));
|
|
266
300
|
this.changed.emit(combined);
|
|
267
301
|
}
|
|
268
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
269
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
302
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrDateTimePanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
303
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.1", type: WrDateTimePanel, isStandalone: true, selector: "wr-date-time-panel", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, timeFormat: { classPropertyName: "timeFormat", publicName: "timeFormat", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, autoFocus: { classPropertyName: "autoFocus", publicName: "autoFocus", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, host: { classAttribute: "wr-date-time-panel" }, ngImport: i0, template: "<wr-calendar\n [date]=\"calendarValue()\"\n [min]=\"min()\"\n [max]=\"max()\"\n [dateFilter]=\"dateFilter()\"\n [autoFocus]=\"autoFocus()\"\n (dateChange)=\"onCalendarSelect($event)\"\n/>\n\n<div class=\"wr-date-time-panel__time\">\n <wr-time-picker\n [value]=\"timeValue()\"\n [format]=\"timeFormat()\"\n [showSeconds]=\"showSeconds()\"\n [step]=\"step()\"\n (valueChange)=\"onTimeChange($event)\"\n />\n</div>\n", dependencies: [{ kind: "component", type: WrCalendar, selector: "wr-calendar", inputs: ["mode", "date", "range", "min", "max", "dateFilter", "autoFocus", "disabled"], outputs: ["dateChange", "rangeChange"] }, { kind: "component", type: WrTimePanel, selector: "wr-time-picker", inputs: ["format", "showSeconds", "step", "disabled", "readonly", "autoFocus", "value"], outputs: ["valueChange", "touch"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
270
304
|
}
|
|
271
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
305
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrDateTimePanel, decorators: [{
|
|
272
306
|
type: Component,
|
|
273
|
-
args: [{ selector: 'wr-date-time-panel', encapsulation: ViewEncapsulation.None, host: { class: 'wr-date-time-panel' }, imports: [
|
|
274
|
-
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
|
|
307
|
+
args: [{ selector: 'wr-date-time-panel', encapsulation: ViewEncapsulation.None, host: { class: 'wr-date-time-panel' }, imports: [WrCalendar, WrTimePanel], template: "<wr-calendar\n [date]=\"calendarValue()\"\n [min]=\"min()\"\n [max]=\"max()\"\n [dateFilter]=\"dateFilter()\"\n [autoFocus]=\"autoFocus()\"\n (dateChange)=\"onCalendarSelect($event)\"\n/>\n\n<div class=\"wr-date-time-panel__time\">\n <wr-time-picker\n [value]=\"timeValue()\"\n [format]=\"timeFormat()\"\n [showSeconds]=\"showSeconds()\"\n [step]=\"step()\"\n (valueChange)=\"onTimeChange($event)\"\n />\n</div>\n" }]
|
|
308
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], autoFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFocus", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
|
|
275
309
|
|
|
276
310
|
/**
|
|
277
311
|
* @license
|
|
@@ -279,6 +313,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
279
313
|
* Use of this source code is governed by an MIT-style license that can be
|
|
280
314
|
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
281
315
|
*/
|
|
316
|
+
let panelUid = 0;
|
|
282
317
|
/**
|
|
283
318
|
* Unified date / time / date-time picker. Same `<input>` + popover skeleton
|
|
284
319
|
* for every mode — the overlay content swaps based on `[mode]`:
|
|
@@ -372,6 +407,8 @@ class WrDatePicker {
|
|
|
372
407
|
destroyRef = inject(DestroyRef);
|
|
373
408
|
inputEl = viewChild.required('input', /* @ts-ignore */
|
|
374
409
|
...(ngDevMode ? [{ debugName: "inputEl" }] : /* istanbul ignore next */ []));
|
|
410
|
+
triggerEl = viewChild('trigger', /* @ts-ignore */
|
|
411
|
+
...(ngDevMode ? [{ debugName: "triggerEl" }] : /* istanbul ignore next */ []));
|
|
375
412
|
/** The picked Date. Bound by `[formField]`, or two-way via `[(value)]`. */
|
|
376
413
|
value = model(null, /* @ts-ignore */
|
|
377
414
|
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
@@ -383,6 +420,14 @@ class WrDatePicker {
|
|
|
383
420
|
/** Whether the popover is currently open. */
|
|
384
421
|
overlayOpen = signal(false, /* @ts-ignore */
|
|
385
422
|
...(ngDevMode ? [{ debugName: "overlayOpen" }] : /* istanbul ignore next */ []));
|
|
423
|
+
/**
|
|
424
|
+
* Where focus came from, captured as an explicit argument at open time —
|
|
425
|
+
* never read off `document.activeElement`, which by then may already be the
|
|
426
|
+
* pane. Every close path that took focus INTO the panel hands it back here.
|
|
427
|
+
*/
|
|
428
|
+
openedFrom = null;
|
|
429
|
+
/** Whether the panel this open cycle should take focus. */
|
|
430
|
+
autoFocusPanel = false;
|
|
386
431
|
/** Resolved format — falls back to a mode-appropriate default. */
|
|
387
432
|
resolvedFormat = computed(() => {
|
|
388
433
|
const explicit = this.format();
|
|
@@ -408,6 +453,9 @@ class WrDatePicker {
|
|
|
408
453
|
labelDate = readI18nText('datePicker.open', 'Open calendar');
|
|
409
454
|
labelTime = readI18nText('datePicker.openTime', 'Open time picker');
|
|
410
455
|
labelDateTime = readI18nText('datePicker.openDateTime', 'Open date and time picker');
|
|
456
|
+
panelLabelDate = readI18nText('datePicker.panel', 'Choose date');
|
|
457
|
+
panelLabelTime = readI18nText('datePicker.panelTime', 'Choose time');
|
|
458
|
+
panelLabelDateTime = readI18nText('datePicker.panelDateTime', 'Choose date and time');
|
|
411
459
|
/**
|
|
412
460
|
* Accessible name of the text field. Falls back to the placeholder, then to
|
|
413
461
|
* the same catalog string the calendar button uses — the field is a
|
|
@@ -434,6 +482,28 @@ class WrDatePicker {
|
|
|
434
482
|
return this.labelDate;
|
|
435
483
|
}, /* @ts-ignore */
|
|
436
484
|
...(ngDevMode ? [{ debugName: "triggerLabel" }] : /* istanbul ignore next */ []));
|
|
485
|
+
/**
|
|
486
|
+
* Accessible name of the popup. The trigger advertises
|
|
487
|
+
* `aria-haspopup="dialog"`, so the panel is a `role="dialog"` — and an unnamed
|
|
488
|
+
* dialog announces as a bare "dialog". Defaults to the catalog's
|
|
489
|
+
* `datePicker.panel*` string for the current `mode`.
|
|
490
|
+
*/
|
|
491
|
+
panelAriaLabel = input(null, /* @ts-ignore */
|
|
492
|
+
...(ngDevMode ? [{ debugName: "panelAriaLabel" }] : /* istanbul ignore next */ []));
|
|
493
|
+
/** Popup id — what the trigger's `aria-controls` points at while open. */
|
|
494
|
+
panelId = `wr-date-picker-panel-${++panelUid}`;
|
|
495
|
+
resolvedPanelLabel = computed(() => {
|
|
496
|
+
const explicit = this.panelAriaLabel();
|
|
497
|
+
if (explicit)
|
|
498
|
+
return explicit;
|
|
499
|
+
const m = this.mode();
|
|
500
|
+
if (m === 'time')
|
|
501
|
+
return this.panelLabelTime;
|
|
502
|
+
if (m === 'datetime')
|
|
503
|
+
return this.panelLabelDateTime;
|
|
504
|
+
return this.panelLabelDate;
|
|
505
|
+
}, /* @ts-ignore */
|
|
506
|
+
...(ngDevMode ? [{ debugName: "resolvedPanelLabel" }] : /* istanbul ignore next */ []));
|
|
437
507
|
classes = computed(() => {
|
|
438
508
|
const parts = ['wr-date-picker', `wr-date-picker--${this.mode()}`];
|
|
439
509
|
if (this.disabled())
|
|
@@ -446,6 +516,12 @@ class WrDatePicker {
|
|
|
446
516
|
* the typed value in real time (not just on reopen). */
|
|
447
517
|
dateRef = signal(null, /* @ts-ignore */
|
|
448
518
|
...(ngDevMode ? [{ debugName: "dateRef" }] : /* istanbul ignore next */ []));
|
|
519
|
+
timeRef = signal(null, /* @ts-ignore */
|
|
520
|
+
...(ngDevMode ? [{ debugName: "timeRef" }] : /* istanbul ignore next */ []));
|
|
521
|
+
dateTimeRef = signal(null, /* @ts-ignore */
|
|
522
|
+
...(ngDevMode ? [{ debugName: "dateTimeRef" }] : /* istanbul ignore next */ []));
|
|
523
|
+
/** The last value an open panel handed us, so the live push never echoes it back. */
|
|
524
|
+
lastFromPanel = null;
|
|
449
525
|
/** Last value we pushed into the model ourselves — lets the sync effect
|
|
450
526
|
* skip the echo of our own edits (so a live keystroke's raw text is never
|
|
451
527
|
* reformatted mid-type). */
|
|
@@ -474,6 +550,43 @@ class WrDatePicker {
|
|
|
474
550
|
if (ref)
|
|
475
551
|
ref.setInput('date', this.value());
|
|
476
552
|
});
|
|
553
|
+
// …and the constraints with it. Pushing these only at attach time meant a
|
|
554
|
+
// `[min]` (or `[max]`, or the filter) that changed while the popover was open
|
|
555
|
+
// kept the stale constraint until the next reopen — `wr-date-range-picker`
|
|
556
|
+
// carries the same effect under a comment saying exactly that.
|
|
557
|
+
effect(() => {
|
|
558
|
+
const ref = this.dateRef();
|
|
559
|
+
if (!ref)
|
|
560
|
+
return;
|
|
561
|
+
ref.setInput('min', this.min());
|
|
562
|
+
ref.setInput('max', this.max());
|
|
563
|
+
ref.setInput('dateFilter', this.dateFilter());
|
|
564
|
+
});
|
|
565
|
+
effect(() => {
|
|
566
|
+
const ref = this.timeRef();
|
|
567
|
+
if (!ref)
|
|
568
|
+
return;
|
|
569
|
+
ref.setInput('format', this.timeFormat());
|
|
570
|
+
ref.setInput('showSeconds', this.showSeconds());
|
|
571
|
+
ref.setInput('step', this.step());
|
|
572
|
+
});
|
|
573
|
+
// The datetime panel had no live sync at all: a date typed into the field
|
|
574
|
+
// while it was open never reached it, so the next stepper click emitted the
|
|
575
|
+
// panel's own stale value and silently undid the typing.
|
|
576
|
+
effect(() => {
|
|
577
|
+
const ref = this.dateTimeRef();
|
|
578
|
+
if (!ref)
|
|
579
|
+
return;
|
|
580
|
+
const value = this.value();
|
|
581
|
+
if (value !== this.lastFromPanel)
|
|
582
|
+
ref.setInput('value', value);
|
|
583
|
+
ref.setInput('min', this.min());
|
|
584
|
+
ref.setInput('max', this.max());
|
|
585
|
+
ref.setInput('dateFilter', this.dateFilter());
|
|
586
|
+
ref.setInput('timeFormat', this.timeFormat());
|
|
587
|
+
ref.setInput('showSeconds', this.showSeconds());
|
|
588
|
+
ref.setInput('step', this.step());
|
|
589
|
+
});
|
|
477
590
|
}
|
|
478
591
|
// Template handlers
|
|
479
592
|
onInput(event) {
|
|
@@ -503,22 +616,69 @@ class WrDatePicker {
|
|
|
503
616
|
openOnInput() {
|
|
504
617
|
if (this.disabled() || this.overlayRef)
|
|
505
618
|
return;
|
|
506
|
-
this
|
|
619
|
+
// Deliberately WITHOUT focus: this click placed a caret in the text field,
|
|
620
|
+
// and yanking focus into the grid would throw away what the user just did.
|
|
621
|
+
// `Alt+ArrowDown` / `ArrowDown` are the keyboard way in — see onFieldKey.
|
|
622
|
+
this.openOverlay(this.inputEl().nativeElement, false);
|
|
507
623
|
}
|
|
508
624
|
toggleOverlay() {
|
|
509
625
|
if (this.disabled())
|
|
510
626
|
return;
|
|
511
627
|
if (this.overlayRef) {
|
|
628
|
+
// Focus is already on the trigger — nothing to restore.
|
|
512
629
|
this.closeOverlay();
|
|
513
630
|
}
|
|
514
631
|
else {
|
|
515
|
-
this.openOverlay();
|
|
632
|
+
this.openOverlay(this.triggerEl()?.nativeElement ?? null, true);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* The keyboard route into the panel, per the APG date-picker pattern.
|
|
637
|
+
*
|
|
638
|
+
* `Alt+ArrowDown` opens and takes focus; a bare arrow moves focus in when the
|
|
639
|
+
* panel is ALREADY open, which is how someone who opened it by clicking the
|
|
640
|
+
* field gets to the grid. Every other key — characters, Backspace, the
|
|
641
|
+
* horizontal arrows, Home / End — is left to the field untouched, so typing a
|
|
642
|
+
* date keeps working exactly as before.
|
|
643
|
+
*/
|
|
644
|
+
onFieldKey(event) {
|
|
645
|
+
if (this.disabled())
|
|
646
|
+
return;
|
|
647
|
+
const vertical = event.key === 'ArrowDown' || event.key === 'ArrowUp';
|
|
648
|
+
if (!vertical)
|
|
649
|
+
return;
|
|
650
|
+
if (event.altKey && event.key === 'ArrowDown' && !this.overlayRef) {
|
|
651
|
+
event.preventDefault();
|
|
652
|
+
this.openOverlay(this.inputEl().nativeElement, true);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
if (this.overlayRef) {
|
|
656
|
+
event.preventDefault();
|
|
657
|
+
this.focusPanel();
|
|
516
658
|
}
|
|
517
659
|
}
|
|
660
|
+
/**
|
|
661
|
+
* Move focus into an already-mounted panel. A plain query is right HERE and
|
|
662
|
+
* wrong at mount time: the panel has settled, so there is no deferral to get
|
|
663
|
+
* wrong — and it keeps `autoFocus` as the single piece of panel API.
|
|
664
|
+
*/
|
|
665
|
+
focusPanel() {
|
|
666
|
+
const pane = this.overlayRef?.overlayElement;
|
|
667
|
+
const target = pane?.querySelector('.wr-calendar__day--focused:not([disabled])') ??
|
|
668
|
+
pane?.querySelector('.wr-time-picker__input');
|
|
669
|
+
target?.focus();
|
|
670
|
+
}
|
|
671
|
+
/** Hand focus back to whatever opened the panel, if it is still on the page. */
|
|
672
|
+
restoreFocus() {
|
|
673
|
+
const target = this.openedFrom?.isConnected ? this.openedFrom : this.inputEl().nativeElement;
|
|
674
|
+
target?.focus();
|
|
675
|
+
}
|
|
518
676
|
// Overlay
|
|
519
|
-
openOverlay() {
|
|
677
|
+
openOverlay(openedFrom = null, autoFocus = false) {
|
|
520
678
|
if (this.overlayRef)
|
|
521
679
|
return;
|
|
680
|
+
this.openedFrom = openedFrom;
|
|
681
|
+
this.autoFocusPanel = autoFocus;
|
|
522
682
|
const positionStrategy = this.overlay
|
|
523
683
|
.position()
|
|
524
684
|
.flexibleConnectedTo(this.host)
|
|
@@ -534,6 +694,15 @@ class WrDatePicker {
|
|
|
534
694
|
scrollStrategy: this.scrollStrategies.reposition(),
|
|
535
695
|
panelClass: 'wr-date-picker-overlay',
|
|
536
696
|
});
|
|
697
|
+
// The trigger promises `aria-haspopup="dialog"`; the pane is the element it
|
|
698
|
+
// points at, so the role, the name and the id all belong here. Non-modal on
|
|
699
|
+
// purpose — focus is not trapped, and the panel closes on outside click or
|
|
700
|
+
// Escape.
|
|
701
|
+
const pane = this.overlayRef.overlayElement;
|
|
702
|
+
pane.id = this.panelId;
|
|
703
|
+
pane.setAttribute('role', 'dialog');
|
|
704
|
+
pane.setAttribute('aria-modal', 'false');
|
|
705
|
+
pane.setAttribute('aria-label', this.resolvedPanelLabel());
|
|
537
706
|
this.overlayOpen.set(true);
|
|
538
707
|
// Dispatch by mode — pick the panel + wire its emissions.
|
|
539
708
|
const m = this.mode();
|
|
@@ -571,6 +740,7 @@ class WrDatePicker {
|
|
|
571
740
|
ref.setInput('min', this.min());
|
|
572
741
|
ref.setInput('max', this.max());
|
|
573
742
|
ref.setInput('dateFilter', this.dateFilter());
|
|
743
|
+
ref.setInput('autoFocus', this.autoFocusPanel);
|
|
574
744
|
ref.instance.date.subscribe(next => {
|
|
575
745
|
if (!next)
|
|
576
746
|
return;
|
|
@@ -585,10 +755,13 @@ class WrDatePicker {
|
|
|
585
755
|
ref.setInput('format', this.timeFormat());
|
|
586
756
|
ref.setInput('showSeconds', this.showSeconds());
|
|
587
757
|
ref.setInput('step', this.step());
|
|
758
|
+
ref.setInput('autoFocus', this.autoFocusPanel);
|
|
588
759
|
ref.setInput('value', this.value() ?? this.adapter.today());
|
|
760
|
+
this.timeRef.set(ref);
|
|
589
761
|
ref.instance.value.subscribe((next) => {
|
|
590
762
|
if (!next)
|
|
591
763
|
return;
|
|
764
|
+
this.lastFromPanel = next;
|
|
592
765
|
this.commit(next);
|
|
593
766
|
});
|
|
594
767
|
}
|
|
@@ -603,7 +776,10 @@ class WrDatePicker {
|
|
|
603
776
|
ref.setInput('timeFormat', this.timeFormat());
|
|
604
777
|
ref.setInput('showSeconds', this.showSeconds());
|
|
605
778
|
ref.setInput('step', this.step());
|
|
779
|
+
ref.setInput('autoFocus', this.autoFocusPanel);
|
|
780
|
+
this.dateTimeRef.set(ref);
|
|
606
781
|
ref.instance.changed.subscribe(next => {
|
|
782
|
+
this.lastFromPanel = next;
|
|
607
783
|
this.commit(next);
|
|
608
784
|
ref.setInput('value', next);
|
|
609
785
|
});
|
|
@@ -618,8 +794,19 @@ class WrDatePicker {
|
|
|
618
794
|
this.lastValue = next;
|
|
619
795
|
this.value.set(next);
|
|
620
796
|
}
|
|
797
|
+
/**
|
|
798
|
+
* Focus goes back to the opener only when it was still INSIDE the panel —
|
|
799
|
+
* dropped there, it would land on `<body>` and the next Tab would restart
|
|
800
|
+
* from the top of the page. When focus had already moved elsewhere (a click
|
|
801
|
+
* on another control, which is itself what closed us) it is left alone:
|
|
802
|
+
* stealing it back would fight the user for the caret.
|
|
803
|
+
*/
|
|
621
804
|
closeOverlay() {
|
|
805
|
+
const pane = this.overlayRef?.overlayElement;
|
|
806
|
+
const inside = !!pane && pane.contains(document.activeElement);
|
|
622
807
|
this.dispose();
|
|
808
|
+
if (inside)
|
|
809
|
+
this.restoreFocus();
|
|
623
810
|
}
|
|
624
811
|
dispose() {
|
|
625
812
|
if (this.overlayRef) {
|
|
@@ -627,7 +814,10 @@ class WrDatePicker {
|
|
|
627
814
|
this.overlayRef = null;
|
|
628
815
|
}
|
|
629
816
|
this.dateRef.set(null);
|
|
817
|
+
this.timeRef.set(null);
|
|
818
|
+
this.dateTimeRef.set(null);
|
|
630
819
|
this.overlayOpen.set(false);
|
|
820
|
+
this.autoFocusPanel = false;
|
|
631
821
|
}
|
|
632
822
|
// Helpers
|
|
633
823
|
isOutOfBounds(date) {
|
|
@@ -639,6 +829,12 @@ class WrDatePicker {
|
|
|
639
829
|
const max = this.max();
|
|
640
830
|
if (max && this.adapter.compareDate(date, max) > 0)
|
|
641
831
|
return true;
|
|
832
|
+
// Covers `dateFilter` as well as the bounds — the calendar disables filtered
|
|
833
|
+
// days, so accepting them from the keyboard made the two entry paths
|
|
834
|
+
// disagree. Same check, same reason, as `wr-date-range-picker`.
|
|
835
|
+
const filter = this.dateFilter();
|
|
836
|
+
if (filter && !filter(date))
|
|
837
|
+
return true;
|
|
642
838
|
return false;
|
|
643
839
|
}
|
|
644
840
|
sameDate(a, b) {
|
|
@@ -648,13 +844,13 @@ class WrDatePicker {
|
|
|
648
844
|
return false;
|
|
649
845
|
return a.getTime() === b.getTime();
|
|
650
846
|
}
|
|
651
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
652
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
847
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrDatePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
848
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrDatePicker, isStandalone: true, selector: "wr-date-picker", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, timeFormat: { classPropertyName: "timeFormat", publicName: "timeFormat", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, panelAriaLabel: { classPropertyName: "panelAriaLabel", publicName: "panelAriaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["input"], descendants: true, isSignal: true }, { propertyName: "triggerEl", first: true, predicate: ["trigger"], descendants: true, isSignal: true }], ngImport: i0, template: "<wr-input-group>\n <!--\n A plain textbox, per the APG date-picker-dialog pattern: `aria-haspopup` and\n `aria-expanded` are not permitted on `textbox` and belong to the trigger\n button below, which is the thing that opens the dialog.\n -->\n <input\n #input\n wrInput\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (click)=\"openOnInput()\"\n (keydown)=\"onFieldKey($event)\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n #trigger\n type=\"button\"\n wrInputSuffix\n class=\"wr-date-picker__trigger\"\n [attr.aria-label]=\"triggerLabel()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"overlayOpen()\"\n [attr.aria-controls]=\"overlayOpen() ? panelId : null\"\n [disabled]=\"disabled()\"\n (click)=\"toggleOverlay()\"\n >\n @if (isTime()) {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"5.75\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"7\" y1=\"7\" x2=\"7\" y2=\"3.75\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n <line x1=\"7\" y1=\"7\" x2=\"9.5\" y2=\"7\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n </svg>\n } @else {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <rect x=\"1.5\" y=\"2.5\" width=\"11\" height=\"10\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"1.5\" y1=\"5.5\" x2=\"12.5\" y2=\"5.5\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"4\" y1=\"1\" x2=\"4\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n <line x1=\"10\" y1=\"1\" x2=\"10\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n @if (isDateTime()) {\n <circle cx=\"7\" cy=\"9\" r=\"1.5\" fill=\"currentColor\" />\n }\n </svg>\n }\n </button>\n</wr-input-group>\n", dependencies: [{ kind: "directive", type: WrInput, selector: "input[wrInput], textarea[wrInput]", inputs: ["wrSize", "rounded"] }, { kind: "component", type: WrInputGroup, selector: "wr-input-group", inputs: ["rounded"] }, { kind: "directive", type: WrInputSuffix, selector: "[wrInputSuffix]" }], encapsulation: i0.ViewEncapsulation.None });
|
|
653
849
|
}
|
|
654
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
850
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrDatePicker, decorators: [{
|
|
655
851
|
type: Component,
|
|
656
|
-
args: [{ selector: 'wr-date-picker', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [WrInput, WrInputGroup, WrInputSuffix], template: "<wr-input-group>\n <!--\n A plain textbox, per the APG date-picker-dialog pattern: `aria-haspopup` and\n `aria-expanded` are not permitted on `textbox` and belong to the trigger\n button below, which is the thing that opens the dialog.\n -->\n <input\n #input\n wrInput\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (click)=\"openOnInput()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n type=\"button\"\n wrInputSuffix\n class=\"wr-date-picker__trigger\"\n [attr.aria-label]=\"triggerLabel()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"overlayOpen()\"\n [disabled]=\"disabled()\"\n (click)=\"toggleOverlay()\"\n >\n @if (isTime()) {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"5.75\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"7\" y1=\"7\" x2=\"7\" y2=\"3.75\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n <line x1=\"7\" y1=\"7\" x2=\"9.5\" y2=\"7\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n </svg>\n } @else {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <rect x=\"1.5\" y=\"2.5\" width=\"11\" height=\"10\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"1.5\" y1=\"5.5\" x2=\"12.5\" y2=\"5.5\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"4\" y1=\"1\" x2=\"4\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n <line x1=\"10\" y1=\"1\" x2=\"10\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n @if (isDateTime()) {\n <circle cx=\"7\" cy=\"9\" r=\"1.5\" fill=\"currentColor\" />\n }\n </svg>\n }\n </button>\n</wr-input-group>\n" }]
|
|
657
|
-
}], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], inputEl: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
|
|
852
|
+
args: [{ selector: 'wr-date-picker', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [WrInput, WrInputGroup, WrInputSuffix], template: "<wr-input-group>\n <!--\n A plain textbox, per the APG date-picker-dialog pattern: `aria-haspopup` and\n `aria-expanded` are not permitted on `textbox` and belong to the trigger\n button below, which is the thing that opens the dialog.\n -->\n <input\n #input\n wrInput\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [value]=\"text()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (click)=\"openOnInput()\"\n (keydown)=\"onFieldKey($event)\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n />\n <button\n #trigger\n type=\"button\"\n wrInputSuffix\n class=\"wr-date-picker__trigger\"\n [attr.aria-label]=\"triggerLabel()\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"overlayOpen()\"\n [attr.aria-controls]=\"overlayOpen() ? panelId : null\"\n [disabled]=\"disabled()\"\n (click)=\"toggleOverlay()\"\n >\n @if (isTime()) {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <circle cx=\"7\" cy=\"7\" r=\"5.75\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"7\" y1=\"7\" x2=\"7\" y2=\"3.75\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n <line x1=\"7\" y1=\"7\" x2=\"9.5\" y2=\"7\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n </svg>\n } @else {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <rect x=\"1.5\" y=\"2.5\" width=\"11\" height=\"10\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"1.5\" y1=\"5.5\" x2=\"12.5\" y2=\"5.5\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"4\" y1=\"1\" x2=\"4\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n <line x1=\"10\" y1=\"1\" x2=\"10\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n @if (isDateTime()) {\n <circle cx=\"7\" cy=\"9\" r=\"1.5\" fill=\"currentColor\" />\n }\n </svg>\n }\n </button>\n</wr-input-group>\n" }]
|
|
853
|
+
}], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], inputEl: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], triggerEl: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], panelAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelAriaLabel", required: false }] }] } });
|
|
658
854
|
|
|
659
855
|
/**
|
|
660
856
|
* @license
|
|
@@ -671,6 +867,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
671
867
|
* preview); this panel only re-attaches the times so a datetime range keeps the
|
|
672
868
|
* hours the user set while the dates move around.
|
|
673
869
|
*
|
|
870
|
+
* The steppers bind `WrTimePanel`'s own `value` model, NOT `[ngModel]`. The
|
|
871
|
+
* classic-forms bridge defers its first write, and under zoneless change
|
|
872
|
+
* detection that write lost the race with the panel's own echo guard: a range
|
|
873
|
+
* bound to 16:00–17:00 opened showing 00:00 on both ends, and the first click
|
|
874
|
+
* on a stepper committed midnight-plus-one over the real value.
|
|
875
|
+
*
|
|
674
876
|
* @internal
|
|
675
877
|
*/
|
|
676
878
|
class WrDateRangePanel {
|
|
@@ -690,7 +892,24 @@ class WrDateRangePanel {
|
|
|
690
892
|
...(ngDevMode ? [{ debugName: "showSeconds" }] : /* istanbul ignore next */ []));
|
|
691
893
|
step = input(1, /* @ts-ignore */
|
|
692
894
|
...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
|
|
895
|
+
/**
|
|
896
|
+
* Forwarded to the calendar only: a range panel opened from the keyboard
|
|
897
|
+
* lands on the day grid, never on a time field. Dates come first here — the
|
|
898
|
+
* steppers are for the range you have already drawn.
|
|
899
|
+
*/
|
|
900
|
+
autoFocus = input(false, /* @ts-ignore */
|
|
901
|
+
...(ngDevMode ? [{ debugName: "autoFocus" }] : /* istanbul ignore next */ []));
|
|
902
|
+
/** A range change from the CALENDAR — the picker is free to sort it. */
|
|
693
903
|
changed = output();
|
|
904
|
+
/**
|
|
905
|
+
* A range change from one of the TIME steppers, which must NOT be sorted while
|
|
906
|
+
* it is happening. The steppers are bound to positions, so sorting mid-edit
|
|
907
|
+
* moves the user's change to the other end: raising a same-day start past the
|
|
908
|
+
* end left the start stuck and pushed the END up instead, once per click, on
|
|
909
|
+
* the button the user was not pressing. Ordering is settled when the popup
|
|
910
|
+
* closes — the same rule the text inputs already follow for typing.
|
|
911
|
+
*/
|
|
912
|
+
timeChanged = output();
|
|
694
913
|
adapter = inject(WrDateAdapter);
|
|
695
914
|
startTimeLabel = readI18nText('datePicker.startTime', 'Start time');
|
|
696
915
|
endTimeLabel = readI18nText('datePicker.endTime', 'End time');
|
|
@@ -745,7 +964,7 @@ class WrDateRangePanel {
|
|
|
745
964
|
this.pendingStartTime.set(time);
|
|
746
965
|
return;
|
|
747
966
|
}
|
|
748
|
-
this.
|
|
967
|
+
this.timeChanged.emit([this.withTimeOf(start, time), end]);
|
|
749
968
|
}
|
|
750
969
|
onEndTimeChange(time) {
|
|
751
970
|
if (!time)
|
|
@@ -755,7 +974,7 @@ class WrDateRangePanel {
|
|
|
755
974
|
this.pendingEndTime.set(time);
|
|
756
975
|
return;
|
|
757
976
|
}
|
|
758
|
-
this.
|
|
977
|
+
this.timeChanged.emit([start, this.withTimeOf(end, time)]);
|
|
759
978
|
}
|
|
760
979
|
/** `date`'s calendar day carrying `time`'s clock. Null date stays null. */
|
|
761
980
|
withTimeOf(date, time) {
|
|
@@ -765,13 +984,13 @@ class WrDateRangePanel {
|
|
|
765
984
|
return date;
|
|
766
985
|
return this.adapter.setTime(this.adapter.createDate(this.adapter.getYear(date), this.adapter.getMonth(date), this.adapter.getDate(date)), this.adapter.getHours(time), this.adapter.getMinutes(time), this.adapter.getSeconds(time));
|
|
767
986
|
}
|
|
768
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
769
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
987
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrDateRangePanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
988
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrDateRangePanel, isStandalone: true, selector: "wr-date-range-panel", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, withTime: { classPropertyName: "withTime", publicName: "withTime", isSignal: true, isRequired: false, transformFunction: null }, timeFormat: { classPropertyName: "timeFormat", publicName: "timeFormat", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, autoFocus: { classPropertyName: "autoFocus", publicName: "autoFocus", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed", timeChanged: "timeChanged" }, host: { classAttribute: "wr-date-range-panel" }, ngImport: i0, template: "<wr-calendar\n mode=\"range\"\n [range]=\"calendarRange()\"\n [min]=\"min()\"\n [max]=\"max()\"\n [dateFilter]=\"dateFilter()\"\n [autoFocus]=\"autoFocus()\"\n (rangeChange)=\"onRangeChange($event)\"\n/>\n\n@if (withTime()) {\n <div class=\"wr-date-range-panel__times\">\n <!-- A time picker is several inputs, not one control, so each end is a\n labelled group rather than a <label>. -->\n <div class=\"wr-date-range-panel__time\" role=\"group\" [attr.aria-label]=\"startTimeLabel\">\n <span class=\"wr-date-range-panel__time-label\" aria-hidden=\"true\">{{ startTimeLabel }}</span>\n <wr-time-picker\n [value]=\"startTime()\"\n [format]=\"timeFormat()\"\n [showSeconds]=\"showSeconds()\"\n [step]=\"step()\"\n (valueChange)=\"onStartTimeChange($event)\"\n />\n </div>\n <div class=\"wr-date-range-panel__time\" role=\"group\" [attr.aria-label]=\"endTimeLabel\">\n <span class=\"wr-date-range-panel__time-label\" aria-hidden=\"true\">{{ endTimeLabel }}</span>\n <wr-time-picker\n [value]=\"endTime()\"\n [format]=\"timeFormat()\"\n [showSeconds]=\"showSeconds()\"\n [step]=\"step()\"\n (valueChange)=\"onEndTimeChange($event)\"\n />\n </div>\n </div>\n}\n", dependencies: [{ kind: "component", type: WrCalendar, selector: "wr-calendar", inputs: ["mode", "date", "range", "min", "max", "dateFilter", "autoFocus", "disabled"], outputs: ["dateChange", "rangeChange"] }, { kind: "component", type: WrTimePanel, selector: "wr-time-picker", inputs: ["format", "showSeconds", "step", "disabled", "readonly", "autoFocus", "value"], outputs: ["valueChange", "touch"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
770
989
|
}
|
|
771
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
990
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrDateRangePanel, decorators: [{
|
|
772
991
|
type: Component,
|
|
773
|
-
args: [{ selector: 'wr-date-range-panel', encapsulation: ViewEncapsulation.None, host: { class: 'wr-date-range-panel' }, imports: [
|
|
774
|
-
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], withTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "withTime", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
|
|
992
|
+
args: [{ selector: 'wr-date-range-panel', encapsulation: ViewEncapsulation.None, host: { class: 'wr-date-range-panel' }, imports: [WrCalendar, WrTimePanel], template: "<wr-calendar\n mode=\"range\"\n [range]=\"calendarRange()\"\n [min]=\"min()\"\n [max]=\"max()\"\n [dateFilter]=\"dateFilter()\"\n [autoFocus]=\"autoFocus()\"\n (rangeChange)=\"onRangeChange($event)\"\n/>\n\n@if (withTime()) {\n <div class=\"wr-date-range-panel__times\">\n <!-- A time picker is several inputs, not one control, so each end is a\n labelled group rather than a <label>. -->\n <div class=\"wr-date-range-panel__time\" role=\"group\" [attr.aria-label]=\"startTimeLabel\">\n <span class=\"wr-date-range-panel__time-label\" aria-hidden=\"true\">{{ startTimeLabel }}</span>\n <wr-time-picker\n [value]=\"startTime()\"\n [format]=\"timeFormat()\"\n [showSeconds]=\"showSeconds()\"\n [step]=\"step()\"\n (valueChange)=\"onStartTimeChange($event)\"\n />\n </div>\n <div class=\"wr-date-range-panel__time\" role=\"group\" [attr.aria-label]=\"endTimeLabel\">\n <span class=\"wr-date-range-panel__time-label\" aria-hidden=\"true\">{{ endTimeLabel }}</span>\n <wr-time-picker\n [value]=\"endTime()\"\n [format]=\"timeFormat()\"\n [showSeconds]=\"showSeconds()\"\n [step]=\"step()\"\n (valueChange)=\"onEndTimeChange($event)\"\n />\n </div>\n </div>\n}\n" }]
|
|
993
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], withTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "withTime", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], autoFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFocus", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }], timeChanged: [{ type: i0.Output, args: ["timeChanged"] }] } });
|
|
775
994
|
|
|
776
995
|
/**
|
|
777
996
|
* @license
|
|
@@ -779,6 +998,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
779
998
|
* Use of this source code is governed by an MIT-style license that can be
|
|
780
999
|
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
781
1000
|
*/
|
|
1001
|
+
/** Per-instance popup ids, so `aria-controls` can point at one panel. */
|
|
1002
|
+
let rangePanelUid = 0;
|
|
782
1003
|
/**
|
|
783
1004
|
* Date-range picker — two text inputs sharing one range calendar.
|
|
784
1005
|
*
|
|
@@ -861,7 +1082,17 @@ class WrDateRangePicker {
|
|
|
861
1082
|
* @default false
|
|
862
1083
|
*/
|
|
863
1084
|
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
864
|
-
/**
|
|
1085
|
+
/**
|
|
1086
|
+
* Read-only — neither the inputs nor the calendar can change the value.
|
|
1087
|
+
*
|
|
1088
|
+
* Deliberately stricter than `wr-date-picker`, which still opens its popup
|
|
1089
|
+
* while read-only: with two fields feeding one calendar there is no reading
|
|
1090
|
+
* of "untypeable" that leaves the grid free to rewrite both ends. The doc
|
|
1091
|
+
* used to promise the trigger still opened; the code has always refused, and
|
|
1092
|
+
* refusing is the behaviour worth keeping.
|
|
1093
|
+
*
|
|
1094
|
+
* @default false
|
|
1095
|
+
*/
|
|
865
1096
|
readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
|
|
866
1097
|
adapter = inject(WrDateAdapter);
|
|
867
1098
|
overlay = inject(WR_OVERLAY);
|
|
@@ -871,6 +1102,10 @@ class WrDateRangePicker {
|
|
|
871
1102
|
destroyRef = inject(DestroyRef);
|
|
872
1103
|
startEl = viewChild.required('startInput', /* @ts-ignore */
|
|
873
1104
|
...(ngDevMode ? [{ debugName: "startEl" }] : /* istanbul ignore next */ []));
|
|
1105
|
+
endEl = viewChild.required('endInput', /* @ts-ignore */
|
|
1106
|
+
...(ngDevMode ? [{ debugName: "endEl" }] : /* istanbul ignore next */ []));
|
|
1107
|
+
triggerEl = viewChild('trigger', /* @ts-ignore */
|
|
1108
|
+
...(ngDevMode ? [{ debugName: "triggerEl" }] : /* istanbul ignore next */ []));
|
|
874
1109
|
/** The picked range. Bound by `[formField]`, or two-way via `[(value)]`. */
|
|
875
1110
|
value = model(null, /* @ts-ignore */
|
|
876
1111
|
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
@@ -884,6 +1119,16 @@ class WrDateRangePicker {
|
|
|
884
1119
|
/** Whether the popover is currently open. */
|
|
885
1120
|
overlayOpen = signal(false, /* @ts-ignore */
|
|
886
1121
|
...(ngDevMode ? [{ debugName: "overlayOpen" }] : /* istanbul ignore next */ []));
|
|
1122
|
+
/**
|
|
1123
|
+
* Which element opened the popup — captured as an argument, never read off
|
|
1124
|
+
* `document.activeElement`, which by then may already be the pane. With TWO
|
|
1125
|
+
* text inputs this is the whole point: "restore focus to the field" is
|
|
1126
|
+
* ambiguous here, and the old code resolved it by always picking the START
|
|
1127
|
+
* input, so closing from the end input threw the caret across the control.
|
|
1128
|
+
*/
|
|
1129
|
+
openedFrom = null;
|
|
1130
|
+
/** Whether the panel this open cycle should take focus. */
|
|
1131
|
+
autoFocusPanel = false;
|
|
887
1132
|
/** Resolved format — falls back to a mode-appropriate default. */
|
|
888
1133
|
resolvedFormat = computed(() => this.format() ? String(this.format()) : this.mode() === 'datetime' ? 'shortDateTime' : 'shortDate', /* @ts-ignore */
|
|
889
1134
|
...(ngDevMode ? [{ debugName: "resolvedFormat" }] : /* istanbul ignore next */ []));
|
|
@@ -892,6 +1137,25 @@ class WrDateRangePicker {
|
|
|
892
1137
|
triggerLabel = readI18nText('datePicker.openRange', 'Open range calendar');
|
|
893
1138
|
startLabel = readI18nText('datePicker.rangeStart', 'Range start');
|
|
894
1139
|
endLabel = readI18nText('datePicker.rangeEnd', 'Range end');
|
|
1140
|
+
panelLabelRange = readI18nText('datePicker.panelRange', 'Choose date range');
|
|
1141
|
+
panelLabelRangeDateTime = readI18nText('datePicker.panelRangeDateTime', 'Choose date and time range');
|
|
1142
|
+
/**
|
|
1143
|
+
* Accessible name of the popup. The trigger advertises
|
|
1144
|
+
* `aria-haspopup="dialog"`, so the pane is a `role="dialog"` — and an unnamed
|
|
1145
|
+
* dialog announces as a bare "dialog". Defaults to the catalog's
|
|
1146
|
+
* `datePicker.panelRange*` string for the current `mode`.
|
|
1147
|
+
*/
|
|
1148
|
+
panelAriaLabel = input(null, /* @ts-ignore */
|
|
1149
|
+
...(ngDevMode ? [{ debugName: "panelAriaLabel" }] : /* istanbul ignore next */ []));
|
|
1150
|
+
/** Popup id — what the trigger's `aria-controls` points at while open. */
|
|
1151
|
+
panelId = `wr-date-range-picker-panel-${++rangePanelUid}`;
|
|
1152
|
+
resolvedPanelLabel = computed(() => {
|
|
1153
|
+
const explicit = this.panelAriaLabel();
|
|
1154
|
+
if (explicit)
|
|
1155
|
+
return explicit;
|
|
1156
|
+
return this.isDateTime() ? this.panelLabelRangeDateTime : this.panelLabelRange;
|
|
1157
|
+
}, /* @ts-ignore */
|
|
1158
|
+
...(ngDevMode ? [{ debugName: "resolvedPanelLabel" }] : /* istanbul ignore next */ []));
|
|
895
1159
|
classes = computed(() => {
|
|
896
1160
|
const parts = ['wr-date-range-picker', `wr-date-range-picker--${this.mode()}`];
|
|
897
1161
|
if (this.disabled())
|
|
@@ -954,30 +1218,83 @@ class WrDateRangePicker {
|
|
|
954
1218
|
// control as touched once focus actually leaves it, or tabbing from start to
|
|
955
1219
|
// end would mark a bound field touched mid-entry.
|
|
956
1220
|
const next = event.relatedTarget;
|
|
957
|
-
|
|
1221
|
+
const leaving = !next || !this.host.nativeElement.contains(next);
|
|
1222
|
+
if (leaving)
|
|
958
1223
|
this.touch.emit();
|
|
959
1224
|
// Typing leaves the ends in whatever order they were entered; settle it
|
|
960
|
-
// here, once the user has stopped
|
|
961
|
-
|
|
1225
|
+
// here, once the user has stopped — and moving from one end to the other is
|
|
1226
|
+
// NOT stopping. Sorting on that hop took the start date the user had just
|
|
1227
|
+
// typed and moved it into the field they were tabbing into, leaving the old
|
|
1228
|
+
// end date under their cursor. Same rule the time steppers follow.
|
|
1229
|
+
const [start, end] = this.commitRange(this.current(), { normalise: leaving });
|
|
962
1230
|
// Reformat to canonical on blur (cleans up `1/5/25` → `1/5/2025`).
|
|
963
1231
|
this.startText.set(this.display(start));
|
|
964
1232
|
this.endText.set(this.display(end));
|
|
965
1233
|
}
|
|
966
1234
|
/** Called by an input's click — opens the overlay if it isn't open already. */
|
|
967
|
-
openOnInput() {
|
|
1235
|
+
openOnInput(end) {
|
|
968
1236
|
if (this.disabled() || this.readonly() || this.overlayRef)
|
|
969
1237
|
return;
|
|
970
|
-
this
|
|
1238
|
+
// Deliberately WITHOUT focus: this click placed a caret in one of the text
|
|
1239
|
+
// fields, and pulling focus into the grid would throw that away.
|
|
1240
|
+
// `Alt+ArrowDown` / `ArrowDown` are the keyboard way in — see onFieldKey.
|
|
1241
|
+
this.openOverlay(this.fieldEl(end), false);
|
|
971
1242
|
}
|
|
972
1243
|
toggleOverlay() {
|
|
973
1244
|
// `readonly` blocks the calendar too — otherwise the inputs refuse typing
|
|
974
1245
|
// while the popover happily edits the same value.
|
|
975
1246
|
if (this.disabled() || this.readonly())
|
|
976
1247
|
return;
|
|
977
|
-
if (this.overlayRef)
|
|
1248
|
+
if (this.overlayRef) {
|
|
1249
|
+
// Focus is already on the trigger — nothing to restore.
|
|
978
1250
|
this.closeOverlay();
|
|
979
|
-
|
|
980
|
-
|
|
1251
|
+
}
|
|
1252
|
+
else {
|
|
1253
|
+
this.openOverlay(this.triggerEl()?.nativeElement ?? null, true);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* The keyboard route into the popup, per the APG date-picker pattern, and the
|
|
1258
|
+
* only one either field has. `Alt+ArrowDown` opens and takes focus; a bare
|
|
1259
|
+
* vertical arrow walks focus in when the popup is ALREADY open, which is how
|
|
1260
|
+
* someone who opened it by clicking a field reaches the grid. Every other key
|
|
1261
|
+
* belongs to the field, so typing a date keeps working with the popup open.
|
|
1262
|
+
*/
|
|
1263
|
+
onFieldKey(event, end) {
|
|
1264
|
+
if (this.disabled() || this.readonly())
|
|
1265
|
+
return;
|
|
1266
|
+
const vertical = event.key === 'ArrowDown' || event.key === 'ArrowUp';
|
|
1267
|
+
if (!vertical)
|
|
1268
|
+
return;
|
|
1269
|
+
if (event.altKey && event.key === 'ArrowDown' && !this.overlayRef) {
|
|
1270
|
+
event.preventDefault();
|
|
1271
|
+
this.openOverlay(this.fieldEl(end), true);
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1274
|
+
if (this.overlayRef) {
|
|
1275
|
+
event.preventDefault();
|
|
1276
|
+
this.focusPanel();
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
/** The input element for an end — the two are otherwise addressed by index. */
|
|
1280
|
+
fieldEl(end) {
|
|
1281
|
+
return end === 0 ? this.startEl().nativeElement : this.endEl().nativeElement;
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Move focus into an already-mounted panel. A plain query is right HERE and
|
|
1285
|
+
* wrong at mount time: the panel has settled, so there is no deferral to get
|
|
1286
|
+
* wrong — and it keeps `autoFocus` as the single piece of panel API.
|
|
1287
|
+
*/
|
|
1288
|
+
focusPanel() {
|
|
1289
|
+
const pane = this.overlayRef?.overlayElement;
|
|
1290
|
+
const target = pane?.querySelector('.wr-calendar__day--focused:not([disabled])') ??
|
|
1291
|
+
pane?.querySelector('.wr-time-picker__input');
|
|
1292
|
+
target?.focus();
|
|
1293
|
+
}
|
|
1294
|
+
/** Hand focus back to whatever opened the popup, if it is still on the page. */
|
|
1295
|
+
restoreFocus() {
|
|
1296
|
+
const target = this.openedFrom?.isConnected ? this.openedFrom : this.startEl().nativeElement;
|
|
1297
|
+
target.focus();
|
|
981
1298
|
}
|
|
982
1299
|
// Input parsing
|
|
983
1300
|
onRangeInput(event, end) {
|
|
@@ -997,9 +1314,11 @@ class WrDateRangePicker {
|
|
|
997
1314
|
this.commitRange(end === 0 ? [parsed, finish] : [start, parsed], { normalise: false });
|
|
998
1315
|
}
|
|
999
1316
|
// Overlay
|
|
1000
|
-
openOverlay() {
|
|
1317
|
+
openOverlay(openedFrom = null, autoFocus = false) {
|
|
1001
1318
|
if (this.overlayRef)
|
|
1002
1319
|
return;
|
|
1320
|
+
this.openedFrom = openedFrom;
|
|
1321
|
+
this.autoFocusPanel = autoFocus;
|
|
1003
1322
|
const positionStrategy = this.overlay
|
|
1004
1323
|
.position()
|
|
1005
1324
|
.flexibleConnectedTo(this.host)
|
|
@@ -1016,9 +1335,22 @@ class WrDateRangePicker {
|
|
|
1016
1335
|
panelClass: 'wr-date-picker-overlay',
|
|
1017
1336
|
});
|
|
1018
1337
|
this.overlayOpen.set(true);
|
|
1338
|
+
// The trigger promises `aria-haspopup="dialog"`; the pane is the element it
|
|
1339
|
+
// points at, so the role, the name and the id all belong here. Non-modal on
|
|
1340
|
+
// purpose — the two text fields stay reachable while the calendar is up,
|
|
1341
|
+
// which is the whole interaction model.
|
|
1342
|
+
const pane = this.overlayRef.overlayElement;
|
|
1343
|
+
pane.id = this.panelId;
|
|
1344
|
+
pane.setAttribute('role', 'dialog');
|
|
1345
|
+
pane.setAttribute('aria-modal', 'false');
|
|
1346
|
+
pane.setAttribute('aria-label', this.resolvedPanelLabel());
|
|
1019
1347
|
const ref = this.overlayRef.attach(new ComponentPortal(WrDateRangePanel));
|
|
1020
1348
|
ref.setInput('value', this.value() ?? [null, null]);
|
|
1349
|
+
ref.setInput('autoFocus', this.autoFocusPanel);
|
|
1021
1350
|
this.panelRef.set(ref);
|
|
1351
|
+
// A stepper edit belongs to the end it was made on. Sorting it here is what
|
|
1352
|
+
// made the start stepper stop responding once it passed the end.
|
|
1353
|
+
ref.instance.timeChanged.subscribe(next => this.commitRange(next, { normalise: false }));
|
|
1022
1354
|
ref.instance.changed.subscribe(next => {
|
|
1023
1355
|
const committed = this.commitRange(next, { normalise: true });
|
|
1024
1356
|
// A complete date-only range is the end of the interaction; a datetime
|
|
@@ -1041,21 +1373,31 @@ class WrDateRangePicker {
|
|
|
1041
1373
|
if (event.key === 'Escape') {
|
|
1042
1374
|
event.preventDefault();
|
|
1043
1375
|
this.closeOverlay();
|
|
1044
|
-
this.startEl().nativeElement.focus();
|
|
1045
1376
|
}
|
|
1046
1377
|
});
|
|
1047
1378
|
}
|
|
1048
1379
|
/**
|
|
1049
|
-
* Close the popover
|
|
1050
|
-
*
|
|
1051
|
-
*
|
|
1052
|
-
* the
|
|
1380
|
+
* Close the popover, handing focus back to whichever field or button opened
|
|
1381
|
+
* it — but ONLY when focus was still inside the pane. Dropped there it would
|
|
1382
|
+
* land on `<body>` and the next Tab would restart from the top of the page;
|
|
1383
|
+
* moved on by the user, it must be left where they put it.
|
|
1384
|
+
*
|
|
1385
|
+
* There is deliberately no "always restore" escape hatch. Escape reaches this
|
|
1386
|
+
* overlay from anywhere in the document (the CDK dispatches it to the topmost
|
|
1387
|
+
* one), so forcing a restore would drag the caret backwards out of the field
|
|
1388
|
+
* the user was actually typing in — into the one that happened to open the
|
|
1389
|
+
* popup, possibly several Tab stops away.
|
|
1053
1390
|
*/
|
|
1054
|
-
closeOverlay(
|
|
1055
|
-
const
|
|
1391
|
+
closeOverlay() {
|
|
1392
|
+
const pane = this.overlayRef?.overlayElement;
|
|
1393
|
+
const inside = !!pane && pane.contains(this.host.nativeElement.ownerDocument.activeElement);
|
|
1056
1394
|
this.dispose();
|
|
1057
|
-
|
|
1058
|
-
|
|
1395
|
+
// The interaction is over, so settle the ordering an in-progress time edit
|
|
1396
|
+
// was allowed to leave inverted. Blur does the same for the text inputs;
|
|
1397
|
+
// without this, closing by clicking outside never passes through one.
|
|
1398
|
+
this.commitRange(this.current(), { normalise: true });
|
|
1399
|
+
if (inside)
|
|
1400
|
+
this.restoreFocus();
|
|
1059
1401
|
}
|
|
1060
1402
|
dispose() {
|
|
1061
1403
|
if (this.overlayRef) {
|
|
@@ -1064,6 +1406,7 @@ class WrDateRangePicker {
|
|
|
1064
1406
|
}
|
|
1065
1407
|
this.panelRef.set(null);
|
|
1066
1408
|
this.overlayOpen.set(false);
|
|
1409
|
+
this.autoFocusPanel = false;
|
|
1067
1410
|
}
|
|
1068
1411
|
// Commit
|
|
1069
1412
|
/**
|
|
@@ -1150,20 +1493,29 @@ class WrDateRangePicker {
|
|
|
1150
1493
|
return false;
|
|
1151
1494
|
return a.getTime() === b.getTime();
|
|
1152
1495
|
}
|
|
1496
|
+
/**
|
|
1497
|
+
* `null` and `[null, null]` are the same VALUE — an empty range — and treating
|
|
1498
|
+
* them as different is what let the FIRST blur on an untouched picker write
|
|
1499
|
+
* `[null, null]` over the `null` it was bound to. No date had moved, but
|
|
1500
|
+
* `model()` emitted: a bound `[formField]` went dirty from nothing but tabbing
|
|
1501
|
+
* through, and a consumer's `@if (period())` flipped from empty to truthy with
|
|
1502
|
+
* neither end picked. The write guard in `commitRange` is only as good as this
|
|
1503
|
+
* comparison.
|
|
1504
|
+
*/
|
|
1153
1505
|
sameRange(a, b) {
|
|
1154
1506
|
if (a === b)
|
|
1155
1507
|
return true;
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
return this.sameDate(
|
|
1508
|
+
const [aStart, aEnd] = a ?? [null, null];
|
|
1509
|
+
const [bStart, bEnd] = b ?? [null, null];
|
|
1510
|
+
return this.sameDate(aStart, bStart) && this.sameDate(aEnd, bEnd);
|
|
1159
1511
|
}
|
|
1160
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
1161
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
1512
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrDateRangePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1513
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrDateRangePicker, isStandalone: true, selector: "wr-date-range-picker", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, startPlaceholder: { classPropertyName: "startPlaceholder", publicName: "startPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, endPlaceholder: { classPropertyName: "endPlaceholder", publicName: "endPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, timeFormat: { classPropertyName: "timeFormat", publicName: "timeFormat", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, panelAriaLabel: { classPropertyName: "panelAriaLabel", publicName: "panelAriaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, viewQueries: [{ propertyName: "startEl", first: true, predicate: ["startInput"], descendants: true, isSignal: true }, { propertyName: "endEl", first: true, predicate: ["endInput"], descendants: true, isSignal: true }, { propertyName: "triggerEl", first: true, predicate: ["trigger"], descendants: true, isSignal: true }], ngImport: i0, template: "<wr-input-group>\n <input\n #startInput\n wrInput\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n class=\"wr-date-range-picker__input\"\n [attr.aria-label]=\"startLabel\"\n [value]=\"startText()\"\n [placeholder]=\"startPlaceholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (click)=\"openOnInput(0)\"\n (keydown)=\"onFieldKey($event, 0)\"\n (input)=\"onStartInput($event)\"\n (blur)=\"onBlur($event)\"\n />\n <span class=\"wr-date-range-picker__separator\" aria-hidden=\"true\">{{ separator() }}</span>\n <input\n #endInput\n wrInput\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n class=\"wr-date-range-picker__input\"\n [attr.aria-label]=\"endLabel\"\n [value]=\"endText()\"\n [placeholder]=\"endPlaceholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (click)=\"openOnInput(1)\"\n (keydown)=\"onFieldKey($event, 1)\"\n (input)=\"onEndInput($event)\"\n (blur)=\"onBlur($event)\"\n />\n <!--\n `readonly` refuses to open the popup, so the trigger is disabled with it \u2014\n otherwise it stays a focusable button advertising `aria-haspopup=\"dialog\"`\n for a dialog it will never show.\n -->\n <button\n #trigger\n type=\"button\"\n wrInputSuffix\n class=\"wr-date-picker__trigger\"\n [attr.aria-label]=\"triggerLabel\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"overlayOpen()\"\n [attr.aria-controls]=\"overlayOpen() ? panelId : null\"\n [disabled]=\"disabled() || readonly()\"\n (click)=\"toggleOverlay()\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <rect x=\"1.5\" y=\"2.5\" width=\"11\" height=\"10\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"1.5\" y1=\"5.5\" x2=\"12.5\" y2=\"5.5\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"4\" y1=\"1\" x2=\"4\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n <line x1=\"10\" y1=\"1\" x2=\"10\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n @if (isDateTime()) {\n <circle cx=\"7\" cy=\"9\" r=\"1.5\" fill=\"currentColor\" />\n }\n </svg>\n </button>\n</wr-input-group>\n", dependencies: [{ kind: "directive", type: WrInput, selector: "input[wrInput], textarea[wrInput]", inputs: ["wrSize", "rounded"] }, { kind: "component", type: WrInputGroup, selector: "wr-input-group", inputs: ["rounded"] }, { kind: "directive", type: WrInputSuffix, selector: "[wrInputSuffix]" }], encapsulation: i0.ViewEncapsulation.None });
|
|
1162
1514
|
}
|
|
1163
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
1515
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrDateRangePicker, decorators: [{
|
|
1164
1516
|
type: Component,
|
|
1165
|
-
args: [{ selector: 'wr-date-range-picker', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [WrInput, WrInputGroup, WrInputSuffix], template: "<wr-input-group>\n <input\n #startInput\n wrInput\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n class=\"wr-date-range-picker__input\"\n [attr.aria-label]=\"startLabel\"\n [value]=\"startText()\"\n [placeholder]=\"startPlaceholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (click)=\"openOnInput()\"\n (input)=\"onStartInput($event)\"\n (blur)=\"onBlur($event)\"\n />\n <span class=\"wr-date-range-picker__separator\" aria-hidden=\"true\">{{ separator() }}</span>\n <input\n wrInput\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n class=\"wr-date-range-picker__input\"\n [attr.aria-label]=\"endLabel\"\n [value]=\"endText()\"\n [placeholder]=\"endPlaceholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (click)=\"openOnInput()\"\n (input)=\"onEndInput($event)\"\n (blur)=\"onBlur($event)\"\n />\n <button\n type=\"button\"\n wrInputSuffix\n class=\"wr-date-picker__trigger\"\n [attr.aria-label]=\"triggerLabel\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"overlayOpen()\"\n [disabled]=\"disabled()\"\n (click)=\"toggleOverlay()\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <rect x=\"1.5\" y=\"2.5\" width=\"11\" height=\"10\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"1.5\" y1=\"5.5\" x2=\"12.5\" y2=\"5.5\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"4\" y1=\"1\" x2=\"4\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n <line x1=\"10\" y1=\"1\" x2=\"10\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n @if (isDateTime()) {\n <circle cx=\"7\" cy=\"9\" r=\"1.5\" fill=\"currentColor\" />\n }\n </svg>\n </button>\n</wr-input-group>\n" }]
|
|
1166
|
-
}], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], startPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "startPlaceholder", required: false }] }], endPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "endPlaceholder", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], startEl: [{ type: i0.ViewChild, args: ['startInput', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }] } });
|
|
1517
|
+
args: [{ selector: 'wr-date-range-picker', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, imports: [WrInput, WrInputGroup, WrInputSuffix], template: "<wr-input-group>\n <input\n #startInput\n wrInput\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n class=\"wr-date-range-picker__input\"\n [attr.aria-label]=\"startLabel\"\n [value]=\"startText()\"\n [placeholder]=\"startPlaceholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (click)=\"openOnInput(0)\"\n (keydown)=\"onFieldKey($event, 0)\"\n (input)=\"onStartInput($event)\"\n (blur)=\"onBlur($event)\"\n />\n <span class=\"wr-date-range-picker__separator\" aria-hidden=\"true\">{{ separator() }}</span>\n <input\n #endInput\n wrInput\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n class=\"wr-date-range-picker__input\"\n [attr.aria-label]=\"endLabel\"\n [value]=\"endText()\"\n [placeholder]=\"endPlaceholder()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (click)=\"openOnInput(1)\"\n (keydown)=\"onFieldKey($event, 1)\"\n (input)=\"onEndInput($event)\"\n (blur)=\"onBlur($event)\"\n />\n <!--\n `readonly` refuses to open the popup, so the trigger is disabled with it \u2014\n otherwise it stays a focusable button advertising `aria-haspopup=\"dialog\"`\n for a dialog it will never show.\n -->\n <button\n #trigger\n type=\"button\"\n wrInputSuffix\n class=\"wr-date-picker__trigger\"\n [attr.aria-label]=\"triggerLabel\"\n [attr.aria-haspopup]=\"'dialog'\"\n [attr.aria-expanded]=\"overlayOpen()\"\n [attr.aria-controls]=\"overlayOpen() ? panelId : null\"\n [disabled]=\"disabled() || readonly()\"\n (click)=\"toggleOverlay()\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\">\n <rect x=\"1.5\" y=\"2.5\" width=\"11\" height=\"10\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"1.5\" y1=\"5.5\" x2=\"12.5\" y2=\"5.5\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <line x1=\"4\" y1=\"1\" x2=\"4\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n <line x1=\"10\" y1=\"1\" x2=\"10\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n @if (isDateTime()) {\n <circle cx=\"7\" cy=\"9\" r=\"1.5\" fill=\"currentColor\" />\n }\n </svg>\n </button>\n</wr-input-group>\n" }]
|
|
1518
|
+
}], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], startPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "startPlaceholder", required: false }] }], endPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "endPlaceholder", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], startEl: [{ type: i0.ViewChild, args: ['startInput', { isSignal: true }] }], endEl: [{ type: i0.ViewChild, args: ['endInput', { isSignal: true }] }], triggerEl: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], panelAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelAriaLabel", required: false }] }] } });
|
|
1167
1519
|
|
|
1168
1520
|
/**
|
|
1169
1521
|
* Generated bundle index. Do not edit.
|