ngwr 14.7.0 → 14.9.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 +66 -332
- package/alert/styles/_index.scss +10 -0
- package/avatar/styles/_index.scss +18 -7
- package/badge/styles/_index.scss +7 -0
- package/badge/styles/_tag.scss +7 -1
- package/bar-chart/styles/_index.scss +1 -0
- package/button/styles/_index.scss +6 -0
- package/calendar-heatmap/styles/_index.scss +1 -0
- package/cascader/styles/_rules.scss +87 -15
- package/checkbox/styles/_index.scss +4 -0
- package/command-palette/styles/_index.scss +1 -1
- package/date-picker/styles/_index.scss +30 -0
- package/density/styles/_index.scss +4 -2
- package/divider/styles/_index.scss +2 -0
- package/donut-chart/styles/_index.scss +10 -0
- package/dropdown/styles/_index.scss +67 -5
- package/editor/styles/_index.scss +441 -0
- package/event-calendar/styles/_index.scss +209 -35
- package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -1
- package/fesm2022/ngwr-action-sheet.mjs +3 -3
- package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
- package/fesm2022/ngwr-affix.mjs +3 -3
- package/fesm2022/ngwr-affix.mjs.map +1 -1
- package/fesm2022/ngwr-alert.mjs +3 -3
- package/fesm2022/ngwr-alert.mjs.map +1 -1
- package/fesm2022/ngwr-anchor.mjs +3 -3
- package/fesm2022/ngwr-anchor.mjs.map +1 -1
- package/fesm2022/ngwr-aurora.mjs +3 -3
- package/fesm2022/ngwr-aurora.mjs.map +1 -1
- package/fesm2022/ngwr-avatar.mjs +3 -3
- package/fesm2022/ngwr-avatar.mjs.map +1 -1
- package/fesm2022/ngwr-back-top.mjs +3 -3
- package/fesm2022/ngwr-back-top.mjs.map +1 -1
- package/fesm2022/ngwr-badge.mjs +28 -13
- package/fesm2022/ngwr-badge.mjs.map +1 -1
- package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-bar-chart.mjs +31 -6
- package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
- package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-blur-text.mjs +3 -3
- package/fesm2022/ngwr-blur-text.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow.mjs +3 -3
- package/fesm2022/ngwr-border-glow.mjs.map +1 -1
- package/fesm2022/ngwr-breadcrumbs.mjs +6 -6
- package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
- package/fesm2022/ngwr-burger.mjs +3 -3
- package/fesm2022/ngwr-burger.mjs.map +1 -1
- package/fesm2022/ngwr-button-testing.mjs.map +1 -1
- package/fesm2022/ngwr-button.mjs +6 -6
- package/fesm2022/ngwr-button.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-heatmap.mjs +61 -6
- package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
- package/fesm2022/ngwr-calendar.mjs +3 -3
- package/fesm2022/ngwr-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-card.mjs +9 -9
- package/fesm2022/ngwr-card.mjs.map +1 -1
- package/fesm2022/ngwr-carousel-testing.mjs.map +1 -1
- package/fesm2022/ngwr-carousel.mjs +6 -6
- package/fesm2022/ngwr-carousel.mjs.map +1 -1
- package/fesm2022/ngwr-cascader-testing.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +4 -4
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox-testing.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox.mjs +18 -8
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text.mjs +3 -3
- package/fesm2022/ngwr-circular-text.mjs.map +1 -1
- package/fesm2022/ngwr-click-spark.mjs +3 -3
- package/fesm2022/ngwr-click-spark.mjs.map +1 -1
- package/fesm2022/ngwr-clipboard.mjs +3 -3
- package/fesm2022/ngwr-clipboard.mjs.map +1 -1
- package/fesm2022/ngwr-collapse-testing.mjs.map +1 -1
- package/fesm2022/ngwr-collapse.mjs +9 -9
- package/fesm2022/ngwr-collapse.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker.mjs +6 -6
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-command-palette-testing.mjs.map +1 -1
- package/fesm2022/ngwr-command-palette.mjs +3 -3
- package/fesm2022/ngwr-command-palette.mjs.map +1 -1
- package/fesm2022/ngwr-compare.mjs +3 -3
- package/fesm2022/ngwr-compare.mjs.map +1 -1
- package/fesm2022/ngwr-confetti.mjs +3 -3
- package/fesm2022/ngwr-confetti.mjs.map +1 -1
- package/fesm2022/ngwr-config.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu.mjs +12 -12
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-cookie.mjs +3 -3
- package/fesm2022/ngwr-cookie.mjs.map +1 -1
- package/fesm2022/ngwr-counter.mjs +6 -6
- package/fesm2022/ngwr-counter.mjs.map +1 -1
- package/fesm2022/ngwr-date-adapters-fns.mjs +3 -3
- package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
- package/fesm2022/ngwr-date-adapters-luxon.mjs +3 -3
- package/fesm2022/ngwr-date-adapters-luxon.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker.mjs +721 -62
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-date.mjs +3 -3
- package/fesm2022/ngwr-date.mjs.map +1 -1
- package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-decrypt-text.mjs +3 -3
- package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
- package/fesm2022/ngwr-density.mjs +6 -6
- package/fesm2022/ngwr-density.mjs.map +1 -1
- package/fesm2022/ngwr-descriptions.mjs +6 -6
- package/fesm2022/ngwr-descriptions.mjs.map +1 -1
- package/fesm2022/ngwr-dialog-testing.mjs.map +1 -1
- package/fesm2022/ngwr-dialog.mjs +15 -15
- package/fesm2022/ngwr-dialog.mjs.map +1 -1
- package/fesm2022/ngwr-directives.mjs +12 -12
- package/fesm2022/ngwr-directives.mjs.map +1 -1
- package/fesm2022/ngwr-divider.mjs +3 -3
- package/fesm2022/ngwr-divider.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart.mjs +48 -6
- package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +6 -6
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
- package/fesm2022/ngwr-drawer-testing.mjs.map +1 -1
- package/fesm2022/ngwr-drawer.mjs +18 -18
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown-testing.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +174 -25
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-editor-testing.mjs +568 -0
- package/fesm2022/ngwr-editor-testing.mjs.map +1 -0
- package/fesm2022/ngwr-editor.mjs +2601 -0
- package/fesm2022/ngwr-editor.mjs.map +1 -0
- package/fesm2022/ngwr-empty.mjs +3 -3
- package/fesm2022/ngwr-empty.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar.mjs +14 -7
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text.mjs +3 -3
- package/fesm2022/ngwr-falling-text.mjs.map +1 -1
- package/fesm2022/ngwr-file-upload-testing.mjs.map +1 -1
- package/fesm2022/ngwr-file-upload.mjs +3 -3
- package/fesm2022/ngwr-file-upload.mjs.map +1 -1
- package/fesm2022/ngwr-form-testing.mjs.map +1 -1
- package/fesm2022/ngwr-form.mjs +61 -10
- package/fesm2022/ngwr-form.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-fuzzy-text.mjs +3 -3
- package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
- package/fesm2022/ngwr-gauge-testing.mjs.map +1 -1
- package/fesm2022/ngwr-gauge.mjs +35 -6
- package/fesm2022/ngwr-gauge.mjs.map +1 -1
- package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-glitch-text.mjs +3 -3
- package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
- package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-gradient-text.mjs +3 -3
- package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
- package/fesm2022/ngwr-graph-testing.mjs.map +1 -1
- package/fesm2022/ngwr-graph.mjs +6 -6
- package/fesm2022/ngwr-graph.mjs.map +1 -1
- package/fesm2022/ngwr-hotkey.mjs +6 -6
- package/fesm2022/ngwr-hotkey.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ar.mjs +31 -0
- package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-cs.mjs +31 -0
- package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-de.mjs +31 -0
- package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +39 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-es.mjs +31 -0
- package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-fr.mjs +31 -0
- package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-he.mjs +31 -0
- package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-hi.mjs +31 -0
- package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-id.mjs +31 -0
- package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-it.mjs +31 -0
- package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ja.mjs +31 -0
- package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ko.mjs +31 -0
- package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-nl.mjs +31 -0
- package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pl.mjs +31 -0
- package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-pt.mjs +31 -0
- package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +31 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-sv.mjs +31 -0
- package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-tr.mjs +31 -0
- package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-uk.mjs +31 -0
- package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-vi.mjs +31 -0
- package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh-TW.mjs +31 -0
- package/fesm2022/ngwr-i18n-zh-TW.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-zh.mjs +31 -0
- package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +9 -9
- package/fesm2022/ngwr-i18n.mjs.map +1 -1
- package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -1
- package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -1
- package/fesm2022/ngwr-icon.mjs +6 -6
- package/fesm2022/ngwr-icon.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper.mjs +3 -3
- package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
- package/fesm2022/ngwr-input-number-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input-number.mjs +15 -3
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp.mjs +3 -3
- package/fesm2022/ngwr-input-otp.mjs.map +1 -1
- package/fesm2022/ngwr-input-testing.mjs.map +1 -1
- package/fesm2022/ngwr-input.mjs +15 -15
- package/fesm2022/ngwr-input.mjs.map +1 -1
- package/fesm2022/ngwr-keyboard.mjs +3 -3
- package/fesm2022/ngwr-keyboard.mjs.map +1 -1
- package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
- package/fesm2022/ngwr-knob.mjs +3 -3
- package/fesm2022/ngwr-knob.mjs.map +1 -1
- package/fesm2022/ngwr-layout.mjs +15 -15
- package/fesm2022/ngwr-layout.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -1
- package/fesm2022/ngwr-lightbox.mjs +3 -3
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart.mjs +18 -7
- package/fesm2022/ngwr-line-chart.mjs.map +1 -1
- package/fesm2022/ngwr-list.mjs +6 -6
- package/fesm2022/ngwr-list.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar.mjs +6 -6
- package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
- package/fesm2022/ngwr-markdown-testing.mjs.map +1 -1
- package/fesm2022/ngwr-markdown.mjs +798 -17
- package/fesm2022/ngwr-markdown.mjs.map +1 -1
- package/fesm2022/ngwr-marquee-testing.mjs.map +1 -1
- package/fesm2022/ngwr-marquee.mjs +3 -3
- package/fesm2022/ngwr-marquee.mjs.map +1 -1
- package/fesm2022/ngwr-media.mjs +3 -3
- package/fesm2022/ngwr-media.mjs.map +1 -1
- package/fesm2022/ngwr-mention-testing.mjs.map +1 -1
- package/fesm2022/ngwr-mention.mjs +6 -6
- package/fesm2022/ngwr-mention.mjs.map +1 -1
- package/fesm2022/ngwr-meta.mjs +6 -6
- package/fesm2022/ngwr-meta.mjs.map +1 -1
- package/fesm2022/ngwr-meter-group-testing.mjs +7 -3
- package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -1
- package/fesm2022/ngwr-meter-group.mjs +26 -6
- package/fesm2022/ngwr-meter-group.mjs.map +1 -1
- package/fesm2022/ngwr-overlay.mjs +6 -6
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-page-header.mjs +13 -5
- package/fesm2022/ngwr-page-header.mjs.map +1 -1
- package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +3 -3
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-pipes.mjs +21 -21
- package/fesm2022/ngwr-pipes.mjs.map +1 -1
- package/fesm2022/ngwr-platform.mjs +9 -9
- package/fesm2022/ngwr-platform.mjs.map +1 -1
- package/fesm2022/ngwr-popconfirm-testing.mjs.map +1 -1
- package/fesm2022/ngwr-popconfirm.mjs +6 -6
- package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
- package/fesm2022/ngwr-popover-testing.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +578 -16
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-progress.mjs +3 -3
- package/fesm2022/ngwr-progress.mjs.map +1 -1
- package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
- package/fesm2022/ngwr-pull-to-refresh.mjs.map +1 -1
- package/fesm2022/ngwr-qr.mjs +3 -3
- package/fesm2022/ngwr-qr.mjs.map +1 -1
- package/fesm2022/ngwr-radio-testing.mjs.map +1 -1
- package/fesm2022/ngwr-radio.mjs +6 -6
- package/fesm2022/ngwr-radio.mjs.map +1 -1
- package/fesm2022/ngwr-rating-testing.mjs.map +1 -1
- package/fesm2022/ngwr-rating.mjs +3 -3
- package/fesm2022/ngwr-rating.mjs.map +1 -1
- package/fesm2022/ngwr-result.mjs +12 -12
- package/fesm2022/ngwr-result.mjs.map +1 -1
- package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-rotating-text.mjs +3 -3
- package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
- package/fesm2022/ngwr-schema-form.mjs +3 -3
- package/fesm2022/ngwr-schema-form.mjs.map +1 -1
- package/fesm2022/ngwr-scroll.mjs +3 -3
- package/fesm2022/ngwr-scroll.mjs.map +1 -1
- package/fesm2022/ngwr-segmented-testing.mjs.map +1 -1
- package/fesm2022/ngwr-segmented.mjs +3 -3
- package/fesm2022/ngwr-segmented.mjs.map +1 -1
- package/fesm2022/ngwr-select-testing.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +12 -12
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text.mjs +6 -6
- package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
- package/fesm2022/ngwr-sidebar.mjs +10 -3
- package/fesm2022/ngwr-sidebar.mjs.map +1 -1
- package/fesm2022/ngwr-skeleton.mjs +3 -3
- package/fesm2022/ngwr-skeleton.mjs.map +1 -1
- package/fesm2022/ngwr-slider-testing.mjs.map +1 -1
- package/fesm2022/ngwr-slider.mjs +3 -3
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline.mjs +84 -10
- package/fesm2022/ngwr-sparkline.mjs.map +1 -1
- package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -1
- package/fesm2022/ngwr-speed-dial.mjs +3 -3
- package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
- package/fesm2022/ngwr-spinner.mjs +3 -3
- package/fesm2022/ngwr-spinner.mjs.map +1 -1
- package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -1
- package/fesm2022/ngwr-splash-cursor.mjs +3 -3
- package/fesm2022/ngwr-splash-cursor.mjs.map +1 -1
- package/fesm2022/ngwr-split-text-testing.mjs.map +1 -1
- package/fesm2022/ngwr-split-text.mjs +3 -3
- package/fesm2022/ngwr-split-text.mjs.map +1 -1
- package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
- package/fesm2022/ngwr-splitter.mjs +3 -3
- package/fesm2022/ngwr-splitter.mjs.map +1 -1
- package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -1
- package/fesm2022/ngwr-spotlight-card.mjs +6 -6
- package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
- package/fesm2022/ngwr-squircle.mjs +6 -6
- package/fesm2022/ngwr-squircle.mjs.map +1 -1
- package/fesm2022/ngwr-star-border-testing.mjs.map +1 -1
- package/fesm2022/ngwr-star-border.mjs +3 -3
- package/fesm2022/ngwr-star-border.mjs.map +1 -1
- package/fesm2022/ngwr-statistic.mjs +9 -9
- package/fesm2022/ngwr-statistic.mjs.map +1 -1
- package/fesm2022/ngwr-stepper-testing.mjs.map +1 -1
- package/fesm2022/ngwr-stepper.mjs +6 -6
- package/fesm2022/ngwr-stepper.mjs.map +1 -1
- package/fesm2022/ngwr-storage.mjs +3 -3
- package/fesm2022/ngwr-storage.mjs.map +1 -1
- package/fesm2022/ngwr-switch-testing.mjs.map +1 -1
- package/fesm2022/ngwr-switch.mjs +3 -3
- package/fesm2022/ngwr-switch.mjs.map +1 -1
- package/fesm2022/ngwr-table-testing.mjs.map +1 -1
- package/fesm2022/ngwr-table.mjs +18 -18
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-tabs-router.mjs +3 -3
- package/fesm2022/ngwr-tabs-router.mjs.map +1 -1
- package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tabs.mjs +6 -6
- package/fesm2022/ngwr-tabs.mjs.map +1 -1
- package/fesm2022/ngwr-textarea-testing.mjs.map +1 -1
- package/fesm2022/ngwr-textarea.mjs +3 -3
- package/fesm2022/ngwr-textarea.mjs.map +1 -1
- package/fesm2022/ngwr-theme.mjs +3 -3
- package/fesm2022/ngwr-theme.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card.mjs +6 -6
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
- package/fesm2022/ngwr-timeline.mjs +6 -6
- package/fesm2022/ngwr-timeline.mjs.map +1 -1
- package/fesm2022/ngwr-toast-testing.mjs.map +1 -1
- package/fesm2022/ngwr-toast.mjs +9 -9
- package/fesm2022/ngwr-toast.mjs.map +1 -1
- package/fesm2022/ngwr-toolbar.mjs +10 -3
- package/fesm2022/ngwr-toolbar.mjs.map +1 -1
- package/fesm2022/ngwr-tour-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tour.mjs +6 -6
- package/fesm2022/ngwr-tour.mjs.map +1 -1
- package/fesm2022/ngwr-transfer-testing.mjs.map +1 -1
- package/fesm2022/ngwr-transfer.mjs +3 -3
- package/fesm2022/ngwr-transfer.mjs.map +1 -1
- package/fesm2022/ngwr-tree-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tree.mjs +3 -3
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter.mjs +3 -3
- package/fesm2022/ngwr-typewriter.mjs.map +1 -1
- package/fesm2022/ngwr-typography.mjs +7 -3
- package/fesm2022/ngwr-typography.mjs.map +1 -1
- package/fesm2022/ngwr-utils.mjs.map +1 -1
- package/fesm2022/ngwr-validators.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
- package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
- package/fesm2022/ngwr-waves-testing.mjs.map +1 -1
- package/fesm2022/ngwr-waves.mjs +3 -3
- package/fesm2022/ngwr-waves.mjs.map +1 -1
- package/fesm2022/ngwr-window-testing.mjs.map +1 -1
- package/fesm2022/ngwr-window.mjs +12 -12
- package/fesm2022/ngwr-window.mjs.map +1 -1
- package/form/styles/_index.scss +16 -1
- package/gauge/styles/_index.scss +1 -0
- package/i18n/ar.json +32 -1
- package/i18n/cs.json +32 -1
- package/i18n/de.json +32 -1
- package/i18n/en.json +32 -1
- package/i18n/es.json +32 -1
- package/i18n/fr.json +32 -1
- package/i18n/he.json +32 -1
- package/i18n/hi.json +32 -1
- package/i18n/id.json +32 -1
- package/i18n/it.json +32 -1
- package/i18n/ja.json +32 -1
- package/i18n/ko.json +32 -1
- package/i18n/nl.json +32 -1
- package/i18n/pl.json +32 -1
- package/i18n/pt.json +32 -1
- package/i18n/ru.json +32 -1
- package/i18n/sv.json +32 -1
- package/i18n/tr.json +32 -1
- package/i18n/uk.json +32 -1
- package/i18n/vi.json +32 -1
- package/i18n/zh-TW.json +32 -1
- package/i18n/zh.json +32 -1
- package/input/styles/_index.scss +24 -1
- package/input-number/styles/_index.scss +69 -0
- package/input-otp/styles/_index.scss +5 -5
- package/line-chart/styles/_index.scss +30 -2
- package/llms-full.txt +14 -4
- package/llms.txt +188 -132
- package/mcp/providers.js +2 -2
- package/mcp/server.js +1 -1
- package/meter-group/styles/_index.scss +1 -0
- package/overlay/styles/_arrow.scss +30 -2
- package/package.json +43 -2
- package/page-header/styles/_index.scss +37 -1
- package/pagination/styles/_index.scss +9 -5
- package/popover/styles/_index.scss +73 -5
- package/progress/styles/_index.scss +4 -0
- package/radio/styles/_index.scss +4 -1
- package/schematics/use/symbol-map.json +1 -0
- package/select/styles/_index.scss +51 -12
- package/sidebar/styles/_index.scss +44 -2
- package/skeleton/styles/_index.scss +3 -0
- package/skills/ngwr/SKILL.md +5 -5
- package/skills/ngwr/references/catalog.md +2 -0
- package/skills/ngwr/references/setup.md +3 -3
- package/sparkline/styles/_index.scss +19 -0
- package/styles.scss +1 -0
- package/table/styles/_index.scss +7 -0
- package/textarea/styles/_index.scss +12 -3
- package/theme/styles/_colors.scss +39 -13
- package/theme/styles/_contrast.scss +9 -7
- package/toast/styles/_index.scss +4 -0
- package/toolbar/styles/_index.scss +35 -1
- package/tree/styles/_index.scss +46 -7
- package/types/ngwr-badge.d.ts +22 -7
- package/types/ngwr-bar-chart.d.ts +19 -1
- package/types/ngwr-calendar-heatmap.d.ts +29 -1
- package/types/ngwr-checkbox.d.ts +2 -0
- package/types/ngwr-config.d.ts +12 -0
- package/types/ngwr-date-picker.d.ts +308 -43
- package/types/ngwr-donut-chart.d.ts +24 -1
- package/types/ngwr-dropdown.d.ts +22 -2
- package/types/ngwr-editor-testing.d.ts +378 -0
- package/types/ngwr-editor.d.ts +449 -0
- package/types/ngwr-event-calendar.d.ts +8 -0
- package/types/ngwr-form.d.ts +35 -5
- package/types/ngwr-gauge.d.ts +20 -1
- package/types/ngwr-input-number.d.ts +7 -0
- package/types/ngwr-line-chart.d.ts +9 -1
- package/types/ngwr-markdown.d.ts +61 -2
- package/types/ngwr-meter-group-testing.d.ts +3 -2
- package/types/ngwr-meter-group.d.ts +14 -1
- package/types/ngwr-page-header.d.ts +10 -2
- package/types/ngwr-popover.d.ts +154 -3
- package/types/ngwr-sidebar.d.ts +7 -0
- package/types/ngwr-sparkline.d.ts +43 -1
- package/types/ngwr-toolbar.d.ts +7 -0
- package/types/ngwr-typography.d.ts +4 -0
- package/types/ngwr-version.d.ts +1 -1
- package/typography/styles/_index.scss +16 -0
|
@@ -210,10 +210,10 @@ class WrSplitText {
|
|
|
210
210
|
};
|
|
211
211
|
});
|
|
212
212
|
}
|
|
213
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
214
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.
|
|
213
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrSplitText, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
214
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: WrSplitText, isStandalone: true, selector: "wr-split-text", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, splitType: { classPropertyName: "splitType", publicName: "splitType", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "delay", isSignal: true, isRequired: false, transformFunction: null }, duration: { classPropertyName: "duration", publicName: "duration", isSignal: true, isRequired: false, transformFunction: null }, easing: { classPropertyName: "easing", publicName: "easing", isSignal: true, isRequired: false, transformFunction: null }, from: { classPropertyName: "from", publicName: "from", isSignal: true, isRequired: false, transformFunction: null }, to: { classPropertyName: "to", publicName: "to", isSignal: true, isRequired: false, transformFunction: null }, threshold: { classPropertyName: "threshold", publicName: "threshold", isSignal: true, isRequired: false, transformFunction: null }, rootMargin: { classPropertyName: "rootMargin", publicName: "rootMargin", isSignal: true, isRequired: false, transformFunction: null }, textAlign: { classPropertyName: "textAlign", publicName: "textAlign", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { animationComplete: "animationComplete" }, host: { properties: { "style.text-align": "textAlign()" }, classAttribute: "wr-split-text" }, ngImport: i0, template: "<!-- The readable text, once: the pieces below are per-character spans. -->\n<span class=\"wr-split-text__sr-only\">{{ text() }}</span>\n@for (p of pieces(); track $index) {\n @if (p.kind === 'piece') {\n <span class=\"wr-split-text__piece\" aria-hidden=\"true\">{{ p.text }}</span>\n } @else {\n <span class=\"wr-split-text__space\" aria-hidden=\"true\">{{ p.text }}</span>\n }\n}\n", styles: [".wr-split-text{display:inline-block;overflow:hidden;padding-bottom:.15em;margin-bottom:-.15em;white-space:normal;overflow-wrap:break-word;will-change:transform,opacity}.wr-split-text__sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.wr-split-text__piece{display:inline-block;will-change:transform,opacity}.wr-split-text__space{display:inline;white-space:pre}\n"], encapsulation: i0.ViewEncapsulation.None });
|
|
215
215
|
}
|
|
216
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
216
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrSplitText, decorators: [{
|
|
217
217
|
type: Component,
|
|
218
218
|
args: [{ selector: 'wr-split-text', encapsulation: ViewEncapsulation.None, host: { class: 'wr-split-text', '[style.text-align]': 'textAlign()' }, template: "<!-- The readable text, once: the pieces below are per-character spans. -->\n<span class=\"wr-split-text__sr-only\">{{ text() }}</span>\n@for (p of pieces(); track $index) {\n @if (p.kind === 'piece') {\n <span class=\"wr-split-text__piece\" aria-hidden=\"true\">{{ p.text }}</span>\n } @else {\n <span class=\"wr-split-text__space\" aria-hidden=\"true\">{{ p.text }}</span>\n }\n}\n", styles: [".wr-split-text{display:inline-block;overflow:hidden;padding-bottom:.15em;margin-bottom:-.15em;white-space:normal;overflow-wrap:break-word;will-change:transform,opacity}.wr-split-text__sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.wr-split-text__piece{display:inline-block;will-change:transform,opacity}.wr-split-text__space{display:inline;white-space:pre}\n"] }]
|
|
219
219
|
}], ctorParameters: () => [], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: true }] }], splitType: [{ type: i0.Input, args: [{ isSignal: true, alias: "splitType", required: false }] }], delay: [{ type: i0.Input, args: [{ isSignal: true, alias: "delay", required: false }] }], duration: [{ type: i0.Input, args: [{ isSignal: true, alias: "duration", required: false }] }], easing: [{ type: i0.Input, args: [{ isSignal: true, alias: "easing", required: false }] }], from: [{ type: i0.Input, args: [{ isSignal: true, alias: "from", required: false }] }], to: [{ type: i0.Input, args: [{ isSignal: true, alias: "to", required: false }] }], threshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "threshold", required: false }] }], rootMargin: [{ type: i0.Input, args: [{ isSignal: true, alias: "rootMargin", required: false }] }], textAlign: [{ type: i0.Input, args: [{ isSignal: true, alias: "textAlign", required: false }] }], animationComplete: [{ type: i0.Output, args: ["animationComplete"] }] } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-split-text.mjs","sources":["../../../projects/lib/split-text/split-text.ts","../../../projects/lib/split-text/split-text.html","../../../projects/lib/split-text/ngwr-split-text.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n *\n * Angular adaptation of the SplitText effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/text-animations/split-text\n *\n * The reactbits version uses GSAP's paid `SplitText` plugin. This port is\n * GSAP-free — splits via plain DOM spans, animates via the Web Animations\n * API, triggers on viewport entry via `IntersectionObserver`.\n */\n\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n ElementRef,\n PLATFORM_ID,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n output,\n} from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { numAttr } from 'ngwr/utils';\n\nimport type { WrSplitTextUnit, WrSplitTextMotion } from './interfaces';\n\ntype Piece = { readonly kind: 'piece'; readonly text: string } | { readonly kind: 'space'; readonly text: string };\n\n/** Split `text` into render pieces, preserving whitespace as `kind: 'space'`. */\nfunction splitPieces(text: string, unit: WrSplitTextUnit): readonly Piece[] {\n if (!text) return [];\n if (unit === 'words') {\n // Split on whitespace, keeping spaces as separate pieces.\n return text.split(/(\\s+)/).flatMap<Piece>(seg => {\n if (seg.length === 0) return [];\n if (/^\\s+$/.test(seg)) return [{ kind: 'space', text: seg } as const];\n return [{ kind: 'piece', text: seg } as const];\n });\n }\n // chars: each character is its own piece; whitespace becomes 'space'.\n return [...text].map<Piece>(ch => (/\\s/.test(ch) ? { kind: 'space', text: ch } : { kind: 'piece', text: ch }));\n}\n\nfunction motionToStyle(m: WrSplitTextMotion): { opacity: number; transform: string } {\n const opacity = m.opacity ?? 1;\n const parts: string[] = [];\n if (m.x !== undefined || m.y !== undefined) {\n parts.push(`translate3d(${m.x ?? 0}px, ${m.y ?? 0}px, 0)`);\n }\n if (m.scale !== undefined) parts.push(`scale(${m.scale})`);\n if (m.rotate !== undefined) parts.push(`rotate(${m.rotate}deg)`);\n return { opacity, transform: parts.length ? parts.join(' ') : 'none' };\n}\n\n/**\n * Animated text reveal — splits the input string into chars or words and\n * staggers each piece in from a `from` motion state to a `to` motion\n * state when the host scrolls into view. Whitespace is preserved.\n *\n * @example\n * ```html\n * <wr-split-text\n * text=\"Hello, ngwr!\"\n * splitType=\"chars\"\n * [delay]=\"40\"\n * [duration]=\"1.2\"\n * [from]=\"{ opacity: 0, y: 40 }\"\n * [to]=\"{ opacity: 1, y: 0 }\"\n * (animationComplete)=\"onDone()\"\n * />\n * ```\n *\n * @see https://www.reactbits.dev/text-animations/split-text\n */\n@Component({\n selector: 'wr-split-text',\n templateUrl: './split-text.html',\n styleUrl: './split-text.scss',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-split-text', '[style.text-align]': 'textAlign()' },\n})\nexport class WrSplitText {\n /** Text to animate. Required. */\n readonly text = input.required<string>();\n\n /** Split granularity. @default 'chars' */\n readonly splitType = input<WrSplitTextUnit>('chars');\n\n /** Stagger delay between pieces in ms. @default 50 */\n readonly delay = input(50, { transform: numAttr(50) });\n\n /** Animation duration in seconds (matches reactbits' API). @default 1.25 */\n readonly duration = input(1.25, { transform: numAttr(1.25) });\n\n /** CSS easing function. @default 'cubic-bezier(0.16, 1, 0.3, 1)' (~power3.out) */\n readonly easing = input('cubic-bezier(0.16, 1, 0.3, 1)');\n\n /** Start state for each piece. @default { opacity: 0, y: 40 } */\n readonly from = input<WrSplitTextMotion>({ opacity: 0, y: 40 });\n\n /** End state for each piece. @default { opacity: 1, y: 0 } */\n readonly to = input<WrSplitTextMotion>({ opacity: 1, y: 0 });\n\n /** `IntersectionObserver.threshold` — 0..1. @default 0.1 */\n readonly threshold = input(0.1, { transform: numAttr(0.1) });\n\n /** `IntersectionObserver.rootMargin`. @default '-100px' */\n readonly rootMargin = input('-100px');\n\n /** Text alignment of the host. @default 'center' */\n readonly textAlign = input<'left' | 'center' | 'right' | 'justify'>('center');\n\n /** Emitted once all pieces finish animating. */\n readonly animationComplete = output<void>();\n\n /** Pieces to render. Each piece is `{ kind: 'piece', text }` or `{ kind: 'space' }`. */\n protected readonly pieces = computed(() => splitPieces(this.text(), this.splitType()));\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly platform = inject(WrPlatform);\n\n private hasAnimated = false;\n\n /**\n * The ONE observer, replaced rather than stacked.\n *\n * `startObserver()` used to `new IntersectionObserver(…)` and register its own\n * `destroyRef.onDestroy` every time, and the effect below calls it on every\n * `text` change after setting `hasAnimated = false` — so the guard at the top\n * of `startObserver` never blocked that path. An off-screen element (the only\n * case where the observer does not immediately disconnect itself) therefore\n * leaked one live observer AND one destroy callback per change, all of them\n * watching the same host and all of them able to fire `animate()`.\n */\n private observer: IntersectionObserver | null = null;\n\n /**\n * `startObserver()` builds its observer inside a `document.fonts.ready`\n * continuation, so the component can be destroyed while it is awaiting. At\n * that moment `observer` is still null, so the destroy hook below disconnects\n * nothing — and the continuation then starts an observer on a host Angular\n * has already detached, which nothing is left to disconnect. Same hazard, and\n * same flag, as `wr-fuzzy-text`.\n */\n private destroyed = false;\n\n constructor() {\n if (!this.isBrowser) return;\n this.destroyRef.onDestroy(() => {\n this.destroyed = true;\n this.observer?.disconnect();\n });\n\n afterNextRender(() => this.startObserver());\n\n // Re-run animation if `text` (and therefore the split pieces) changes.\n effect(() => {\n this.text();\n this.splitType();\n this.hasAnimated = false;\n queueMicrotask(() => this.startObserver());\n });\n }\n\n private startObserver(): void {\n const host = this.host.nativeElement;\n const fonts = (document as Document & { fonts?: FontFaceSet }).fonts;\n const ready = fonts?.status === 'loaded' ? Promise.resolve() : (fonts?.ready ?? Promise.resolve());\n\n const observe = (): void => {\n if (this.destroyed || this.hasAnimated) return;\n this.observer?.disconnect();\n const io = new IntersectionObserver(\n (entries, obs) => {\n for (const e of entries) {\n if (e.isIntersecting) {\n obs.disconnect();\n this.animate();\n break;\n }\n }\n },\n { threshold: this.threshold(), rootMargin: this.rootMargin() }\n );\n this.observer = io;\n io.observe(host);\n };\n\n void ready.then(observe);\n }\n\n private animate(): void {\n if (this.hasAnimated) return;\n this.hasAnimated = true;\n\n // Reduced motion: pieces already render at their natural (final)\n // state — skip the tween and report completion right away.\n if (this.platform.prefersReducedMotion()) {\n this.animationComplete.emit();\n return;\n }\n\n const host = this.host.nativeElement;\n const targets = host.querySelectorAll<HTMLElement>('.wr-split-text__piece');\n if (targets.length === 0) {\n this.animationComplete.emit();\n return;\n }\n\n const fromStyle = motionToStyle(this.from());\n const toStyle = motionToStyle(this.to());\n const durationMs = this.duration() * 1000;\n const stagger = this.delay();\n const easing = this.easing();\n\n let remaining = targets.length;\n targets.forEach((el, i) => {\n // Set initial state immediately so there's no flash of final state\n // before the animation kicks in.\n el.style.opacity = String(fromStyle.opacity);\n el.style.transform = fromStyle.transform;\n const animation = el.animate(\n [\n { opacity: fromStyle.opacity, transform: fromStyle.transform },\n { opacity: toStyle.opacity, transform: toStyle.transform },\n ],\n { duration: durationMs, delay: i * stagger, easing, fill: 'forwards' }\n );\n animation.onfinish = (): void => {\n // Commit final styles + clean up the WAAPI tween.\n el.style.opacity = String(toStyle.opacity);\n el.style.transform = toStyle.transform;\n try {\n animation.cancel();\n } catch {\n /* noop */\n }\n remaining -= 1;\n if (remaining === 0) this.animationComplete.emit();\n };\n });\n }\n}\n\nexport type { WrSplitTextUnit, WrSplitTextMotion } from './interfaces';\n","<!-- The readable text, once: the pieces below are per-character spans. -->\n<span class=\"wr-split-text__sr-only\">{{ text() }}</span>\n@for (p of pieces(); track $index) {\n @if (p.kind === 'piece') {\n <span class=\"wr-split-text__piece\" aria-hidden=\"true\">{{ p.text }}</span>\n } @else {\n <span class=\"wr-split-text__space\" aria-hidden=\"true\">{{ p.text }}</span>\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-split-text.mjs","sources":["../../../projects/lib/split-text/split-text.ts","../../../projects/lib/split-text/split-text.html","../../../projects/lib/split-text/ngwr-split-text.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n *\n * Angular adaptation of the SplitText effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/text-animations/split-text\n *\n * The reactbits version uses GSAP's paid `SplitText` plugin. This port is\n * GSAP-free — splits via plain DOM spans, animates via the Web Animations\n * API, triggers on viewport entry via `IntersectionObserver`.\n */\n\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n ElementRef,\n PLATFORM_ID,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n output,\n} from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { numAttr } from 'ngwr/utils';\n\nimport type { WrSplitTextUnit, WrSplitTextMotion } from './interfaces';\n\ntype Piece = { readonly kind: 'piece'; readonly text: string } | { readonly kind: 'space'; readonly text: string };\n\n/** Split `text` into render pieces, preserving whitespace as `kind: 'space'`. */\nfunction splitPieces(text: string, unit: WrSplitTextUnit): readonly Piece[] {\n if (!text) return [];\n if (unit === 'words') {\n // Split on whitespace, keeping spaces as separate pieces.\n return text.split(/(\\s+)/).flatMap<Piece>(seg => {\n if (seg.length === 0) return [];\n if (/^\\s+$/.test(seg)) return [{ kind: 'space', text: seg } as const];\n return [{ kind: 'piece', text: seg } as const];\n });\n }\n // chars: each character is its own piece; whitespace becomes 'space'.\n return [...text].map<Piece>(ch => (/\\s/.test(ch) ? { kind: 'space', text: ch } : { kind: 'piece', text: ch }));\n}\n\nfunction motionToStyle(m: WrSplitTextMotion): { opacity: number; transform: string } {\n const opacity = m.opacity ?? 1;\n const parts: string[] = [];\n if (m.x !== undefined || m.y !== undefined) {\n parts.push(`translate3d(${m.x ?? 0}px, ${m.y ?? 0}px, 0)`);\n }\n if (m.scale !== undefined) parts.push(`scale(${m.scale})`);\n if (m.rotate !== undefined) parts.push(`rotate(${m.rotate}deg)`);\n return { opacity, transform: parts.length ? parts.join(' ') : 'none' };\n}\n\n/**\n * Animated text reveal — splits the input string into chars or words and\n * staggers each piece in from a `from` motion state to a `to` motion\n * state when the host scrolls into view. Whitespace is preserved.\n *\n * @example\n * ```html\n * <wr-split-text\n * text=\"Hello, ngwr!\"\n * splitType=\"chars\"\n * [delay]=\"40\"\n * [duration]=\"1.2\"\n * [from]=\"{ opacity: 0, y: 40 }\"\n * [to]=\"{ opacity: 1, y: 0 }\"\n * (animationComplete)=\"onDone()\"\n * />\n * ```\n *\n * @see https://www.reactbits.dev/text-animations/split-text\n */\n@Component({\n selector: 'wr-split-text',\n templateUrl: './split-text.html',\n styleUrl: './split-text.scss',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-split-text', '[style.text-align]': 'textAlign()' },\n})\nexport class WrSplitText {\n /** Text to animate. Required. */\n readonly text = input.required<string>();\n\n /** Split granularity. @default 'chars' */\n readonly splitType = input<WrSplitTextUnit>('chars');\n\n /** Stagger delay between pieces in ms. @default 50 */\n readonly delay = input(50, { transform: numAttr(50) });\n\n /** Animation duration in seconds (matches reactbits' API). @default 1.25 */\n readonly duration = input(1.25, { transform: numAttr(1.25) });\n\n /** CSS easing function. @default 'cubic-bezier(0.16, 1, 0.3, 1)' (~power3.out) */\n readonly easing = input('cubic-bezier(0.16, 1, 0.3, 1)');\n\n /** Start state for each piece. @default { opacity: 0, y: 40 } */\n readonly from = input<WrSplitTextMotion>({ opacity: 0, y: 40 });\n\n /** End state for each piece. @default { opacity: 1, y: 0 } */\n readonly to = input<WrSplitTextMotion>({ opacity: 1, y: 0 });\n\n /** `IntersectionObserver.threshold` — 0..1. @default 0.1 */\n readonly threshold = input(0.1, { transform: numAttr(0.1) });\n\n /** `IntersectionObserver.rootMargin`. @default '-100px' */\n readonly rootMargin = input('-100px');\n\n /** Text alignment of the host. @default 'center' */\n readonly textAlign = input<'left' | 'center' | 'right' | 'justify'>('center');\n\n /** Emitted once all pieces finish animating. */\n readonly animationComplete = output<void>();\n\n /** Pieces to render. Each piece is `{ kind: 'piece', text }` or `{ kind: 'space' }`. */\n protected readonly pieces = computed(() => splitPieces(this.text(), this.splitType()));\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly platform = inject(WrPlatform);\n\n private hasAnimated = false;\n\n /**\n * The ONE observer, replaced rather than stacked.\n *\n * `startObserver()` used to `new IntersectionObserver(…)` and register its own\n * `destroyRef.onDestroy` every time, and the effect below calls it on every\n * `text` change after setting `hasAnimated = false` — so the guard at the top\n * of `startObserver` never blocked that path. An off-screen element (the only\n * case where the observer does not immediately disconnect itself) therefore\n * leaked one live observer AND one destroy callback per change, all of them\n * watching the same host and all of them able to fire `animate()`.\n */\n private observer: IntersectionObserver | null = null;\n\n /**\n * `startObserver()` builds its observer inside a `document.fonts.ready`\n * continuation, so the component can be destroyed while it is awaiting. At\n * that moment `observer` is still null, so the destroy hook below disconnects\n * nothing — and the continuation then starts an observer on a host Angular\n * has already detached, which nothing is left to disconnect. Same hazard, and\n * same flag, as `wr-fuzzy-text`.\n */\n private destroyed = false;\n\n constructor() {\n if (!this.isBrowser) return;\n this.destroyRef.onDestroy(() => {\n this.destroyed = true;\n this.observer?.disconnect();\n });\n\n afterNextRender(() => this.startObserver());\n\n // Re-run animation if `text` (and therefore the split pieces) changes.\n effect(() => {\n this.text();\n this.splitType();\n this.hasAnimated = false;\n queueMicrotask(() => this.startObserver());\n });\n }\n\n private startObserver(): void {\n const host = this.host.nativeElement;\n const fonts = (document as Document & { fonts?: FontFaceSet }).fonts;\n const ready = fonts?.status === 'loaded' ? Promise.resolve() : (fonts?.ready ?? Promise.resolve());\n\n const observe = (): void => {\n if (this.destroyed || this.hasAnimated) return;\n this.observer?.disconnect();\n const io = new IntersectionObserver(\n (entries, obs) => {\n for (const e of entries) {\n if (e.isIntersecting) {\n obs.disconnect();\n this.animate();\n break;\n }\n }\n },\n { threshold: this.threshold(), rootMargin: this.rootMargin() }\n );\n this.observer = io;\n io.observe(host);\n };\n\n void ready.then(observe);\n }\n\n private animate(): void {\n if (this.hasAnimated) return;\n this.hasAnimated = true;\n\n // Reduced motion: pieces already render at their natural (final)\n // state — skip the tween and report completion right away.\n if (this.platform.prefersReducedMotion()) {\n this.animationComplete.emit();\n return;\n }\n\n const host = this.host.nativeElement;\n const targets = host.querySelectorAll<HTMLElement>('.wr-split-text__piece');\n if (targets.length === 0) {\n this.animationComplete.emit();\n return;\n }\n\n const fromStyle = motionToStyle(this.from());\n const toStyle = motionToStyle(this.to());\n const durationMs = this.duration() * 1000;\n const stagger = this.delay();\n const easing = this.easing();\n\n let remaining = targets.length;\n targets.forEach((el, i) => {\n // Set initial state immediately so there's no flash of final state\n // before the animation kicks in.\n el.style.opacity = String(fromStyle.opacity);\n el.style.transform = fromStyle.transform;\n const animation = el.animate(\n [\n { opacity: fromStyle.opacity, transform: fromStyle.transform },\n { opacity: toStyle.opacity, transform: toStyle.transform },\n ],\n { duration: durationMs, delay: i * stagger, easing, fill: 'forwards' }\n );\n animation.onfinish = (): void => {\n // Commit final styles + clean up the WAAPI tween.\n el.style.opacity = String(toStyle.opacity);\n el.style.transform = toStyle.transform;\n try {\n animation.cancel();\n } catch {\n /* noop */\n }\n remaining -= 1;\n if (remaining === 0) this.animationComplete.emit();\n };\n });\n }\n}\n\nexport type { WrSplitTextUnit, WrSplitTextMotion } from './interfaces';\n","<!-- The readable text, once: the pieces below are per-character spans. -->\n<span class=\"wr-split-text__sr-only\">{{ text() }}</span>\n@for (p of pieces(); track $index) {\n @if (p.kind === 'piece') {\n <span class=\"wr-split-text__piece\" aria-hidden=\"true\">{{ p.text }}</span>\n } @else {\n <span class=\"wr-split-text__space\" aria-hidden=\"true\">{{ p.text }}</span>\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;AAAA;;;;;;;;;;;;AAYG;AAwBH;AACA,SAAS,WAAW,CAAC,IAAY,EAAE,IAAqB,EAAA;AACtD,IAAA,IAAI,CAAC,IAAI;AAAE,QAAA,OAAO,EAAE;AACpB,IAAA,IAAI,IAAI,KAAK,OAAO,EAAE;;QAEpB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAQ,GAAG,IAAG;AAC9C,YAAA,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;AAAE,gBAAA,OAAO,EAAE;AAC/B,YAAA,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAW,CAAC;YACrE,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAW,CAAC;AAChD,QAAA,CAAC,CAAC;IACJ;;IAEA,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAQ,EAAE,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;AAChH;AAEA,SAAS,aAAa,CAAC,CAAoB,EAAA;AACzC,IAAA,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC;IAC9B,MAAM,KAAK,GAAa,EAAE;AAC1B,IAAA,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE;AAC1C,QAAA,KAAK,CAAC,IAAI,CAAC,CAAA,YAAA,EAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA,IAAA,EAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA,MAAA,CAAQ,CAAC;IAC5D;AACA,IAAA,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,CAAA,MAAA,EAAS,CAAC,CAAC,KAAK,CAAA,CAAA,CAAG,CAAC;AAC1D,IAAA,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,CAAA,OAAA,EAAU,CAAC,CAAC,MAAM,CAAA,IAAA,CAAM,CAAC;IAChE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE;AACxE;AAEA;;;;;;;;;;;;;;;;;;;AAmBG;MAQU,WAAW,CAAA;;IAEb,IAAI,GAAG,KAAK,CAAC,QAAQ;6EAAU;;IAG/B,SAAS,GAAG,KAAK,CAAkB,OAAO;kFAAC;;AAG3C,IAAA,KAAK,GAAG,KAAK,CAAC,EAAE,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,OAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC,EAAA,CAAG;;AAG7C,IAAA,QAAQ,GAAG,KAAK,CAAC,IAAI,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,UAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,EAAA,CAAG;;IAGpD,MAAM,GAAG,KAAK,CAAC,+BAA+B;+EAAC;;IAG/C,IAAI,GAAG,KAAK,CAAoB,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE;6EAAC;;IAGtD,EAAE,GAAG,KAAK,CAAoB,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;2EAAC;;AAGnD,IAAA,SAAS,GAAG,KAAK,CAAC,GAAG,EAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,WAAA,EAAA,8BAAA,EAAA,CAAA,EAAI,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,EAAA,CAAG;;IAGnD,UAAU,GAAG,KAAK,CAAC,QAAQ;mFAAC;;IAG5B,SAAS,GAAG,KAAK,CAA0C,QAAQ;kFAAC;;IAGpE,iBAAiB,GAAG,MAAM,EAAQ;;AAGxB,IAAA,MAAM,GAAG,QAAQ,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;+EAAC;AAErE,IAAA,IAAI,GAAG,MAAM,CAA0B,UAAU,CAAC;AAClD,IAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IAC/B,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAClD,IAAA,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC;IAEtC,WAAW,GAAG,KAAK;AAE3B;;;;;;;;;;AAUG;IACK,QAAQ,GAAgC,IAAI;AAEpD;;;;;;;AAOG;IACK,SAAS,GAAG,KAAK;AAEzB,IAAA,WAAA,GAAA;QACE,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE;AACrB,QAAA,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAK;AAC7B,YAAA,IAAI,CAAC,SAAS,GAAG,IAAI;AACrB,YAAA,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE;AAC7B,QAAA,CAAC,CAAC;QAEF,eAAe,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;;QAG3C,MAAM,CAAC,MAAK;YACV,IAAI,CAAC,IAAI,EAAE;YACX,IAAI,CAAC,SAAS,EAAE;AAChB,YAAA,IAAI,CAAC,WAAW,GAAG,KAAK;YACxB,cAAc,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;AAC5C,QAAA,CAAC,CAAC;IACJ;IAEQ,aAAa,GAAA;AACnB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa;AACpC,QAAA,MAAM,KAAK,GAAI,QAA+C,CAAC,KAAK;QACpE,MAAM,KAAK,GAAG,KAAK,EAAE,MAAM,KAAK,QAAQ,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,KAAK,EAAE,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAElG,MAAM,OAAO,GAAG,MAAW;AACzB,YAAA,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,WAAW;gBAAE;AACxC,YAAA,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE;YAC3B,MAAM,EAAE,GAAG,IAAI,oBAAoB,CACjC,CAAC,OAAO,EAAE,GAAG,KAAI;AACf,gBAAA,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE;AACvB,oBAAA,IAAI,CAAC,CAAC,cAAc,EAAE;wBACpB,GAAG,CAAC,UAAU,EAAE;wBAChB,IAAI,CAAC,OAAO,EAAE;wBACd;oBACF;gBACF;AACF,YAAA,CAAC,EACD,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,CAC/D;AACD,YAAA,IAAI,CAAC,QAAQ,GAAG,EAAE;AAClB,YAAA,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;AAClB,QAAA,CAAC;AAED,QAAA,KAAK,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;IAC1B;IAEQ,OAAO,GAAA;QACb,IAAI,IAAI,CAAC,WAAW;YAAE;AACtB,QAAA,IAAI,CAAC,WAAW,GAAG,IAAI;;;AAIvB,QAAA,IAAI,IAAI,CAAC,QAAQ,CAAC,oBAAoB,EAAE,EAAE;AACxC,YAAA,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE;YAC7B;QACF;AAEA,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAc,uBAAuB,CAAC;AAC3E,QAAA,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;AACxB,YAAA,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE;YAC7B;QACF;QAEA,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI;AACzC,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE;AAC5B,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE;AAE5B,QAAA,IAAI,SAAS,GAAG,OAAO,CAAC,MAAM;QAC9B,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,KAAI;;;YAGxB,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC;YAC5C,EAAE,CAAC,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC,SAAS;AACxC,YAAA,MAAM,SAAS,GAAG,EAAE,CAAC,OAAO,CAC1B;gBACE,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,SAAS,EAAE;gBAC9D,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE;AAC3D,aAAA,EACD,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,CACvE;AACD,YAAA,SAAS,CAAC,QAAQ,GAAG,MAAW;;gBAE9B,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;gBAC1C,EAAE,CAAC,KAAK,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS;AACtC,gBAAA,IAAI;oBACF,SAAS,CAAC,MAAM,EAAE;gBACpB;AAAE,gBAAA,MAAM;;gBAER;gBACA,SAAS,IAAI,CAAC;gBACd,IAAI,SAAS,KAAK,CAAC;AAAE,oBAAA,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE;AACpD,YAAA,CAAC;AACH,QAAA,CAAC,CAAC;IACJ;uGAlKW,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAX,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,WAAW,u8CCzFxB,0YASA,EAAA,MAAA,EAAA,CAAA,obAAA,CAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FDgFa,WAAW,EAAA,UAAA,EAAA,CAAA;kBAPvB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,eAAe,EAAA,aAAA,EAGV,iBAAiB,CAAC,IAAI,EAAA,IAAA,EAC/B,EAAE,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,aAAa,EAAE,EAAA,QAAA,EAAA,0YAAA,EAAA,MAAA,EAAA,CAAA,obAAA,CAAA,EAAA;;;AEvFvE;;AAEG;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-splitter-testing.mjs","sources":["../../../projects/lib/splitter/testing/wr-splitter-harness.ts","../../../projects/lib/splitter/testing/ngwr-splitter-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';\n\nimport type { WrSplitterArrowKey, WrSplitterHarnessFilters, WrSplitterPaneSizes } from './interfaces';\n\nconst ARROWS: Record<WrSplitterArrowKey, TestKey> = {\n left: TestKey.LEFT_ARROW,\n right: TestKey.RIGHT_ARROW,\n up: TestKey.UP_ARROW,\n down: TestKey.DOWN_ARROW,\n};\n\n/**\n * Test harness for `<wr-splitter>` — two panes and the divider between them.\n *\n * **Everything here is the keyboard, and that is not a workaround.** The divider is\n * dragged with a pointer in a browser, but the drag reads\n * `getBoundingClientRect()` and turns a client coordinate into a percentage; jsdom\n * lays nothing out, so every rect is 0×0 and a synthetic drag writes `NaN` into the\n * position. The arrow keys reach the same state, are the accessible path anyway, and\n * are what the WAI-ARIA separator pattern is judged on.\n *\n * **Two words for the axis, and they are opposites.** The component's\n * `orientation=\"horizontal\"` means the panes sit side by side, which needs a\n * VERTICAL divider — so that is what `aria-orientation` says. {@link getOrientation}\n * answers the component's word and {@link getDividerOrientation} the divider's; both\n * are here because a spec that conflated them would look right and assert nothing.\n *\n * **Arrows follow the VISUAL axis.** Under `dir=\"rtl\"` the start pane is the one on\n * the right, so ArrowRight SHRINKS the position. {@link pressArrow} takes the key a\n * user presses rather than a semantic direction, which is the only way a spec can\n * pin that mirroring; {@link setPosition} walks with whichever key currently grows\n * the number, and reports where it landed.\n *\n * @example\n * ```ts\n * const splitter = await loader.getHarness(WrSplitterHarness);\n *\n * expect(await splitter.getPosition()).toBe(50);\n * await splitter.setPosition(70);\n * expect(await splitter.getPaneSizes()).toEqual({ start: 70, end: 30 });\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrSplitterHarness extends ComponentHarness {\n static hostSelector = 'wr-splitter';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrSplitterHarnessFilters = {}): HarnessPredicate<WrSplitterHarness> {\n return new HarnessPredicate(WrSplitterHarness, options)\n .addOption(\n 'orientation',\n options.orientation,\n async (harness, orientation) => (await harness.getOrientation()) === orientation\n )\n .addOption('dividerLabel', options.dividerLabel, (harness, label) =>\n HarnessPredicate.stringMatches(harness.getDividerLabel(), label)\n )\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly divider = this.locatorFor('.wr-splitter__divider');\n\n /**\n * The splitter's own axis: `horizontal` when the panes sit side by side.\n *\n * Read from the host modifier rather than from `aria-orientation`, which says the\n * opposite thing about the same layout — see {@link getDividerOrientation}.\n */\n async getOrientation(): Promise<'horizontal' | 'vertical'> {\n return (await (await this.host()).hasClass('wr-splitter--vertical')) ? 'vertical' : 'horizontal';\n }\n\n /**\n * What the divider announces — the INVERSE of {@link getOrientation}, and correct:\n * a separator's orientation is the line it draws, not the axis it travels along.\n */\n async getDividerOrientation(): Promise<string | null> {\n return (await this.divider()).getAttribute('aria-orientation');\n }\n\n /** The divider's position as a percentage, from `aria-valuenow`. */\n async getPosition(): Promise<number> {\n return this.numeric('aria-valuenow');\n }\n\n /** The lowest position the divider will accept, from `aria-valuemin`. */\n async getMinPosition(): Promise<number> {\n return this.numeric('aria-valuemin');\n }\n\n /** The highest position the divider will accept, from `aria-valuemax`. */\n async getMaxPosition(): Promise<number> {\n return this.numeric('aria-valuemax');\n }\n\n /**\n * What each pane is asking for, in percent, from the `flex-basis` the component\n * writes inline.\n *\n * This is the only evidence a spec without layout has that the panes followed the\n * divider — a measured width is zero for both. They should always sum to 100:\n * the end pane is `100 - position`, so a pair that does not is a component bug\n * rather than a rounding artefact.\n */\n async getPaneSizes(): Promise<WrSplitterPaneSizes> {\n const start = await this.locatorFor('.wr-splitter__pane--start')();\n const end = await this.locatorFor('.wr-splitter__pane--end')();\n\n return {\n start: Number.parseFloat(await start.getCssValue('flex-basis')),\n end: Number.parseFloat(await end.getCssValue('flex-basis')),\n };\n }\n\n /** The start pane's projected text, trimmed. */\n async getStartText(): Promise<string> {\n return (await this.locatorFor('.wr-splitter__pane--start')()).text();\n }\n\n /** The end pane's projected text, trimmed. */\n async getEndText(): Promise<string> {\n return (await this.locatorFor('.wr-splitter__pane--end')()).text();\n }\n\n /** The divider's accessible name — a focusable separator is a widget and needs one. */\n async getDividerLabel(): Promise<string | null> {\n return (await this.divider()).getAttribute('aria-label');\n }\n\n /**\n * Whether the splitter refuses to resize.\n *\n * From the divider's `aria-disabled`, which is the half a screen reader acts on.\n * The component also drops the divider out of the tab order and paints a modifier\n * on the host, and {@link isDividerFocusable} answers the first of those — worth\n * asking separately, because a divider that is announced as disabled and still a\n * tab stop is a control the keyboard can reach and not use.\n */\n async isDisabled(): Promise<boolean> {\n return (await (await this.divider()).getAttribute('aria-disabled')) === 'true';\n }\n\n /** Whether the divider is a tab stop (`tabindex=\"0\"`). */\n async isDividerFocusable(): Promise<boolean> {\n return (await (await this.divider()).getAttribute('tabindex')) === '0';\n }\n\n /** Move keyboard focus to the divider. */\n async focusDivider(): Promise<void> {\n return (await this.divider()).focus();\n }\n\n /** Whether the divider currently holds focus. */\n async isDividerFocused(): Promise<boolean> {\n return (await this.divider()).isFocused();\n }\n\n /**\n * Press one arrow key on the divider.\n *\n * The key rather than a direction, deliberately: which way the position moves is a\n * function of the key AND the reading direction, and a `grow()` / `shrink()` pair\n * would hide exactly the behaviour worth asserting. An arrow off the splitter's own\n * axis does nothing at all — the horizontal splitter ignores Up and Down.\n *\n * `shift` is the coarse step: 10 instead of 1.\n */\n async pressArrow(arrow: WrSplitterArrowKey, options: { shift?: boolean } = {}): Promise<void> {\n const divider = await this.divider();\n await (options.shift ? divider.sendKeys({ shift: true }, ARROWS[arrow]) : divider.sendKeys(ARROWS[arrow]));\n }\n\n /** Press Home — jumping to `minPosition`, whatever the reading direction. */\n async pressHome(): Promise<void> {\n return (await this.divider()).sendKeys(TestKey.HOME);\n }\n\n /** Press End — jumping to `maxPosition`. Semantic, so it never mirrors under RTL. */\n async pressEnd(): Promise<void> {\n return (await this.divider()).sendKeys(TestKey.END);\n }\n\n /**\n * Walk the divider to a position with the keyboard, and assert it landed there.\n *\n * Ten-unit hops first, then single steps — the same two strides a user has. It\n * walks rather than assigning, because assigning would prove the harness can write\n * a signal and nothing about the component: the clamp, the min / max and the\n * mirroring all live on this path.\n *\n * Throws rather than half-arriving. A target outside `[min, max]` is unreachable by\n * construction, and so is any target the walk cannot hit exactly — the position is a\n * float that a previous drag may have left mid-step, and whole-unit keys cannot get\n * off a fractional offset. Both say so, with where it actually stopped.\n */\n async setPosition(target: number): Promise<void> {\n const [min, max] = [await this.getMinPosition(), await this.getMaxPosition()];\n if (target < min || target > max) {\n throw new Error(\n `WrSplitterHarness.setPosition(${target}): out of range — this splitter accepts ${min}–${max}. ` +\n 'The component clamps, so the walk would stop at the bound and never reach the number asked for.'\n );\n }\n\n if (target === min) return this.pressHome();\n if (target === max) return this.pressEnd();\n\n await this.focusDivider();\n // The block axis never mirrors, so only the horizontal splitter needs the probe.\n const vertical = (await this.getOrientation()) === 'vertical';\n const [grow, shrink]: [WrSplitterArrowKey, WrSplitterArrowKey] = vertical\n ? ['down', 'up']\n : (await this.growsWithRight())\n ? ['right', 'left']\n : ['left', 'right'];\n\n for (const shift of [true, false]) {\n const step = shift ? 10 : 1;\n // Recomputed each pass: the coarse hops clamp at the bounds, so the remaining\n // distance is not what simple arithmetic on the starting point would predict.\n let delta = target - (await this.getPosition());\n\n while (Math.abs(delta) >= step) {\n const before = await this.getPosition();\n await this.pressArrow(delta > 0 ? grow : shrink, { shift });\n const now = await this.getPosition();\n\n if (now === before) {\n throw new Error(\n `WrSplitterHarness.setPosition(${target}): the divider stopped moving at ${now}. A disabled ` +\n 'splitter ignores its arrows, which is the usual reason this happens.'\n );\n }\n delta = target - now;\n }\n }\n\n const landed = await this.getPosition();\n if (landed !== target) {\n throw new Error(\n `WrSplitterHarness.setPosition(${target}): the walk stopped at ${landed}. The arrows move in whole ` +\n 'units, so a position left on a fraction by a drag cannot be walked onto a whole number.'\n );\n }\n }\n\n /**\n * Whether ArrowRight is currently the key that GROWS the position — true in LTR,\n * false in RTL, where the start pane is the one on the right.\n *\n * Probed rather than read off `Directionality`, which a harness has no access to:\n * press, look, and put it back. At the top of the range a growing press is clamped\n * and would prove nothing, so the probe presses the other way there instead. The\n * undo is conditional — a press that moved nothing has nothing to undo, and undoing\n * it anyway would move the divider by one in the direction the probe was testing.\n */\n private async growsWithRight(): Promise<boolean> {\n const before = await this.getPosition();\n const atCeiling = before >= (await this.getMaxPosition());\n const [probe, undo]: [WrSplitterArrowKey, WrSplitterArrowKey] = atCeiling ? ['left', 'right'] : ['right', 'left'];\n\n await this.pressArrow(probe);\n const after = await this.getPosition();\n if (after !== before) await this.pressArrow(undo);\n\n return atCeiling ? after < before : after > before;\n }\n\n private async numeric(attribute: string): Promise<number> {\n const raw = await (await this.divider()).getAttribute(attribute);\n return raw === null ? Number.NaN : Number(raw);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-splitter-testing.mjs","sources":["../../../projects/lib/splitter/testing/wr-splitter-harness.ts","../../../projects/lib/splitter/testing/ngwr-splitter-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';\n\nimport type { WrSplitterArrowKey, WrSplitterHarnessFilters, WrSplitterPaneSizes } from './interfaces';\n\nconst ARROWS: Record<WrSplitterArrowKey, TestKey> = {\n left: TestKey.LEFT_ARROW,\n right: TestKey.RIGHT_ARROW,\n up: TestKey.UP_ARROW,\n down: TestKey.DOWN_ARROW,\n};\n\n/**\n * Test harness for `<wr-splitter>` — two panes and the divider between them.\n *\n * **Everything here is the keyboard, and that is not a workaround.** The divider is\n * dragged with a pointer in a browser, but the drag reads\n * `getBoundingClientRect()` and turns a client coordinate into a percentage; jsdom\n * lays nothing out, so every rect is 0×0 and a synthetic drag writes `NaN` into the\n * position. The arrow keys reach the same state, are the accessible path anyway, and\n * are what the WAI-ARIA separator pattern is judged on.\n *\n * **Two words for the axis, and they are opposites.** The component's\n * `orientation=\"horizontal\"` means the panes sit side by side, which needs a\n * VERTICAL divider — so that is what `aria-orientation` says. {@link getOrientation}\n * answers the component's word and {@link getDividerOrientation} the divider's; both\n * are here because a spec that conflated them would look right and assert nothing.\n *\n * **Arrows follow the VISUAL axis.** Under `dir=\"rtl\"` the start pane is the one on\n * the right, so ArrowRight SHRINKS the position. {@link pressArrow} takes the key a\n * user presses rather than a semantic direction, which is the only way a spec can\n * pin that mirroring; {@link setPosition} walks with whichever key currently grows\n * the number, and reports where it landed.\n *\n * @example\n * ```ts\n * const splitter = await loader.getHarness(WrSplitterHarness);\n *\n * expect(await splitter.getPosition()).toBe(50);\n * await splitter.setPosition(70);\n * expect(await splitter.getPaneSizes()).toEqual({ start: 70, end: 30 });\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrSplitterHarness extends ComponentHarness {\n static hostSelector = 'wr-splitter';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrSplitterHarnessFilters = {}): HarnessPredicate<WrSplitterHarness> {\n return new HarnessPredicate(WrSplitterHarness, options)\n .addOption(\n 'orientation',\n options.orientation,\n async (harness, orientation) => (await harness.getOrientation()) === orientation\n )\n .addOption('dividerLabel', options.dividerLabel, (harness, label) =>\n HarnessPredicate.stringMatches(harness.getDividerLabel(), label)\n )\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly divider = this.locatorFor('.wr-splitter__divider');\n\n /**\n * The splitter's own axis: `horizontal` when the panes sit side by side.\n *\n * Read from the host modifier rather than from `aria-orientation`, which says the\n * opposite thing about the same layout — see {@link getDividerOrientation}.\n */\n async getOrientation(): Promise<'horizontal' | 'vertical'> {\n return (await (await this.host()).hasClass('wr-splitter--vertical')) ? 'vertical' : 'horizontal';\n }\n\n /**\n * What the divider announces — the INVERSE of {@link getOrientation}, and correct:\n * a separator's orientation is the line it draws, not the axis it travels along.\n */\n async getDividerOrientation(): Promise<string | null> {\n return (await this.divider()).getAttribute('aria-orientation');\n }\n\n /** The divider's position as a percentage, from `aria-valuenow`. */\n async getPosition(): Promise<number> {\n return this.numeric('aria-valuenow');\n }\n\n /** The lowest position the divider will accept, from `aria-valuemin`. */\n async getMinPosition(): Promise<number> {\n return this.numeric('aria-valuemin');\n }\n\n /** The highest position the divider will accept, from `aria-valuemax`. */\n async getMaxPosition(): Promise<number> {\n return this.numeric('aria-valuemax');\n }\n\n /**\n * What each pane is asking for, in percent, from the `flex-basis` the component\n * writes inline.\n *\n * This is the only evidence a spec without layout has that the panes followed the\n * divider — a measured width is zero for both. They should always sum to 100:\n * the end pane is `100 - position`, so a pair that does not is a component bug\n * rather than a rounding artefact.\n */\n async getPaneSizes(): Promise<WrSplitterPaneSizes> {\n const start = await this.locatorFor('.wr-splitter__pane--start')();\n const end = await this.locatorFor('.wr-splitter__pane--end')();\n\n return {\n start: Number.parseFloat(await start.getCssValue('flex-basis')),\n end: Number.parseFloat(await end.getCssValue('flex-basis')),\n };\n }\n\n /** The start pane's projected text, trimmed. */\n async getStartText(): Promise<string> {\n return (await this.locatorFor('.wr-splitter__pane--start')()).text();\n }\n\n /** The end pane's projected text, trimmed. */\n async getEndText(): Promise<string> {\n return (await this.locatorFor('.wr-splitter__pane--end')()).text();\n }\n\n /** The divider's accessible name — a focusable separator is a widget and needs one. */\n async getDividerLabel(): Promise<string | null> {\n return (await this.divider()).getAttribute('aria-label');\n }\n\n /**\n * Whether the splitter refuses to resize.\n *\n * From the divider's `aria-disabled`, which is the half a screen reader acts on.\n * The component also drops the divider out of the tab order and paints a modifier\n * on the host, and {@link isDividerFocusable} answers the first of those — worth\n * asking separately, because a divider that is announced as disabled and still a\n * tab stop is a control the keyboard can reach and not use.\n */\n async isDisabled(): Promise<boolean> {\n return (await (await this.divider()).getAttribute('aria-disabled')) === 'true';\n }\n\n /** Whether the divider is a tab stop (`tabindex=\"0\"`). */\n async isDividerFocusable(): Promise<boolean> {\n return (await (await this.divider()).getAttribute('tabindex')) === '0';\n }\n\n /** Move keyboard focus to the divider. */\n async focusDivider(): Promise<void> {\n return (await this.divider()).focus();\n }\n\n /** Whether the divider currently holds focus. */\n async isDividerFocused(): Promise<boolean> {\n return (await this.divider()).isFocused();\n }\n\n /**\n * Press one arrow key on the divider.\n *\n * The key rather than a direction, deliberately: which way the position moves is a\n * function of the key AND the reading direction, and a `grow()` / `shrink()` pair\n * would hide exactly the behaviour worth asserting. An arrow off the splitter's own\n * axis does nothing at all — the horizontal splitter ignores Up and Down.\n *\n * `shift` is the coarse step: 10 instead of 1.\n */\n async pressArrow(arrow: WrSplitterArrowKey, options: { shift?: boolean } = {}): Promise<void> {\n const divider = await this.divider();\n await (options.shift ? divider.sendKeys({ shift: true }, ARROWS[arrow]) : divider.sendKeys(ARROWS[arrow]));\n }\n\n /** Press Home — jumping to `minPosition`, whatever the reading direction. */\n async pressHome(): Promise<void> {\n return (await this.divider()).sendKeys(TestKey.HOME);\n }\n\n /** Press End — jumping to `maxPosition`. Semantic, so it never mirrors under RTL. */\n async pressEnd(): Promise<void> {\n return (await this.divider()).sendKeys(TestKey.END);\n }\n\n /**\n * Walk the divider to a position with the keyboard, and assert it landed there.\n *\n * Ten-unit hops first, then single steps — the same two strides a user has. It\n * walks rather than assigning, because assigning would prove the harness can write\n * a signal and nothing about the component: the clamp, the min / max and the\n * mirroring all live on this path.\n *\n * Throws rather than half-arriving. A target outside `[min, max]` is unreachable by\n * construction, and so is any target the walk cannot hit exactly — the position is a\n * float that a previous drag may have left mid-step, and whole-unit keys cannot get\n * off a fractional offset. Both say so, with where it actually stopped.\n */\n async setPosition(target: number): Promise<void> {\n const [min, max] = [await this.getMinPosition(), await this.getMaxPosition()];\n if (target < min || target > max) {\n throw new Error(\n `WrSplitterHarness.setPosition(${target}): out of range — this splitter accepts ${min}–${max}. ` +\n 'The component clamps, so the walk would stop at the bound and never reach the number asked for.'\n );\n }\n\n if (target === min) return this.pressHome();\n if (target === max) return this.pressEnd();\n\n await this.focusDivider();\n // The block axis never mirrors, so only the horizontal splitter needs the probe.\n const vertical = (await this.getOrientation()) === 'vertical';\n const [grow, shrink]: [WrSplitterArrowKey, WrSplitterArrowKey] = vertical\n ? ['down', 'up']\n : (await this.growsWithRight())\n ? ['right', 'left']\n : ['left', 'right'];\n\n for (const shift of [true, false]) {\n const step = shift ? 10 : 1;\n // Recomputed each pass: the coarse hops clamp at the bounds, so the remaining\n // distance is not what simple arithmetic on the starting point would predict.\n let delta = target - (await this.getPosition());\n\n while (Math.abs(delta) >= step) {\n const before = await this.getPosition();\n await this.pressArrow(delta > 0 ? grow : shrink, { shift });\n const now = await this.getPosition();\n\n if (now === before) {\n throw new Error(\n `WrSplitterHarness.setPosition(${target}): the divider stopped moving at ${now}. A disabled ` +\n 'splitter ignores its arrows, which is the usual reason this happens.'\n );\n }\n delta = target - now;\n }\n }\n\n const landed = await this.getPosition();\n if (landed !== target) {\n throw new Error(\n `WrSplitterHarness.setPosition(${target}): the walk stopped at ${landed}. The arrows move in whole ` +\n 'units, so a position left on a fraction by a drag cannot be walked onto a whole number.'\n );\n }\n }\n\n /**\n * Whether ArrowRight is currently the key that GROWS the position — true in LTR,\n * false in RTL, where the start pane is the one on the right.\n *\n * Probed rather than read off `Directionality`, which a harness has no access to:\n * press, look, and put it back. At the top of the range a growing press is clamped\n * and would prove nothing, so the probe presses the other way there instead. The\n * undo is conditional — a press that moved nothing has nothing to undo, and undoing\n * it anyway would move the divider by one in the direction the probe was testing.\n */\n private async growsWithRight(): Promise<boolean> {\n const before = await this.getPosition();\n const atCeiling = before >= (await this.getMaxPosition());\n const [probe, undo]: [WrSplitterArrowKey, WrSplitterArrowKey] = atCeiling ? ['left', 'right'] : ['right', 'left'];\n\n await this.pressArrow(probe);\n const after = await this.getPosition();\n if (after !== before) await this.pressArrow(undo);\n\n return atCeiling ? after < before : after > before;\n }\n\n private async numeric(attribute: string): Promise<number> {\n const raw = await (await this.divider()).getAttribute(attribute);\n return raw === null ? Number.NaN : Number(raw);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -170,10 +170,10 @@ class WrSplitter {
|
|
|
170
170
|
isRtl() {
|
|
171
171
|
return this.dir?.value === 'rtl';
|
|
172
172
|
}
|
|
173
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
174
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
173
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrSplitter, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
174
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: WrSplitter, isStandalone: true, selector: "wr-splitter", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, minPosition: { classPropertyName: "minPosition", publicName: "minPosition", isSignal: true, isRequired: false, transformFunction: null }, maxPosition: { classPropertyName: "maxPosition", publicName: "maxPosition", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, dividerLabel: { classPropertyName: "dividerLabel", publicName: "dividerLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { position: "positionChange" }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: "<div class=\"wr-splitter__pane wr-splitter__pane--start\" [style.flex-basis]=\"startStyle()\">\n <ng-content select=\"[wrSplitterStart]\" />\n</div>\n\n<!-- Roving focus + arrow keys for keyboard resizing. -->\n<div\n class=\"wr-splitter__divider\"\n role=\"separator\"\n [attr.aria-label]=\"resolvedDividerLabel()\"\n [attr.aria-orientation]=\"orientation() === 'horizontal' ? 'vertical' : 'horizontal'\"\n [attr.aria-valuemin]=\"minPosition()\"\n [attr.aria-valuemax]=\"maxPosition()\"\n [attr.aria-valuenow]=\"position()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (keydown)=\"onKeydown($event)\"\n>\n <span class=\"wr-splitter__handle\" aria-hidden=\"true\"></span>\n</div>\n\n<div class=\"wr-splitter__pane wr-splitter__pane--end\" [style.flex-basis]=\"endStyle()\">\n <ng-content select=\"[wrSplitterEnd]\" />\n</div>\n", encapsulation: i0.ViewEncapsulation.None });
|
|
175
175
|
}
|
|
176
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
176
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrSplitter, decorators: [{
|
|
177
177
|
type: Component,
|
|
178
178
|
args: [{ selector: 'wr-splitter', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, template: "<div class=\"wr-splitter__pane wr-splitter__pane--start\" [style.flex-basis]=\"startStyle()\">\n <ng-content select=\"[wrSplitterStart]\" />\n</div>\n\n<!-- Roving focus + arrow keys for keyboard resizing. -->\n<div\n class=\"wr-splitter__divider\"\n role=\"separator\"\n [attr.aria-label]=\"resolvedDividerLabel()\"\n [attr.aria-orientation]=\"orientation() === 'horizontal' ? 'vertical' : 'horizontal'\"\n [attr.aria-valuemin]=\"minPosition()\"\n [attr.aria-valuemax]=\"maxPosition()\"\n [attr.aria-valuenow]=\"position()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (keydown)=\"onKeydown($event)\"\n>\n <span class=\"wr-splitter__handle\" aria-hidden=\"true\"></span>\n</div>\n\n<div class=\"wr-splitter__pane wr-splitter__pane--end\" [style.flex-basis]=\"endStyle()\">\n <ng-content select=\"[wrSplitterEnd]\" />\n</div>\n" }]
|
|
179
179
|
}], ctorParameters: () => [], propDecorators: { position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }, { type: i0.Output, args: ["positionChange"] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], minPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "minPosition", required: false }] }], maxPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxPosition", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], dividerLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dividerLabel", required: false }] }] } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-splitter.mjs","sources":["../../../projects/lib/splitter/splitter.ts","../../../projects/lib/splitter/splitter.html","../../../projects/lib/splitter/ngwr-splitter.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, ElementRef, ViewEncapsulation, computed, effect, inject, input, model } from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { clamp } from 'ngwr/utils';\n\n/**\n * Resizable two-pane splitter. Project two pieces of content via\n * `[wrSplitterStart]` and `[wrSplitterEnd]`; the user drags the divider\n * to reallocate space between them.\n *\n * @example\n * ```html\n * <wr-splitter [(position)]=\"pos\">\n * <div wrSplitterStart>Files</div>\n * <div wrSplitterEnd>Editor</div>\n * </wr-splitter>\n *\n * <wr-splitter orientation=\"vertical\" [minPosition]=\"20\" [maxPosition]=\"80\">\n * <div wrSplitterStart>Preview</div>\n * <div wrSplitterEnd>Logs</div>\n * </wr-splitter>\n * ```\n *\n * @see https://ngwr.dev/reference/components/splitter\n */\n@Component({\n selector: 'wr-splitter',\n templateUrl: './splitter.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrSplitter {\n /** Position of the divider as a percentage (0–100). Two-way bindable. @default 50 */\n readonly position = model(50);\n\n /**\n * Divider direction:\n * - `'horizontal'` — vertical divider, drags L/R (panes side by side).\n * - `'vertical'` — horizontal divider, drags U/D (panes stacked).\n * @default 'horizontal'\n */\n readonly orientation = input<'horizontal' | 'vertical'>('horizontal');\n\n /** Minimum allowed position. @default 0 */\n readonly minPosition = input(0, { transform: (v: unknown): number => coerceNumberProperty(v, 0) });\n\n /** Maximum allowed position. @default 100 */\n readonly maxPosition = input(100, { transform: (v: unknown): number => coerceNumberProperty(v, 100) });\n\n /** Disable dragging. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Accessible name of the divider. A focusable `role=\"separator\"` is an\n * interactive widget, so it needs one — without it a screen reader reaching the\n * divider announces nothing but \"separator\" and its number. Falls back to\n * `splitter.divider`.\n */\n readonly dividerLabel = input<string | null>(null);\n\n protected readonly resolvedDividerLabel = useI18nText(this.dividerLabel, 'splitter.divider', 'Resize panes');\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n // Optional on purpose: `Directionality` is root-provided, so a consumer never has to\n // supply it — and a bare `TestBed` does not either.\n private readonly dir = inject(Directionality, { optional: true });\n private dragging = false;\n\n constructor() {\n // `position` is a `model`, so `[(position)]` and a restored layout write into it\n // directly; only the drag and the arrows clamped. Left alone, an out-of-range\n // write reached the DOM as `aria-valuenow=\"150\"` against a `valuemax` of 100 and\n // sized a pane at `150%`.\n effect(() => {\n const clamped = clamp(this.position(), this.minPosition(), this.maxPosition());\n if (clamped !== this.position()) this.position.set(clamped);\n });\n }\n\n protected readonly classes = computed(() => {\n const parts = ['wr-splitter', `wr-splitter--${this.orientation()}`];\n if (this.disabled()) parts.push('wr-splitter--disabled');\n return parts.join(' ');\n });\n\n protected readonly startStyle = computed(() => `${this.position()}%`);\n protected readonly endStyle = computed(() => `${100 - this.position()}%`);\n\n // Drag\n\n protected onPointerDown(event: PointerEvent): void {\n if (this.disabled()) return;\n // Any pointerdown used to start a drag, so holding the RIGHT button over the\n // divider and moving resized the panes; the same guard keeps a second finger\n // from taking over a drag already in progress.\n if (event.button !== 0 || !event.isPrimary) return;\n event.preventDefault();\n this.dragging = true;\n const handle = event.currentTarget as HTMLElement;\n handle.setPointerCapture(event.pointerId);\n // `preventDefault` above also suppresses the click's default focus, so without\n // this the arrows did nothing after a mouse drag until the user found the\n // divider again with Tab. `wr-slider` focuses its thumb for the same reason.\n handle.focus();\n }\n\n protected onPointerMove(event: PointerEvent): void {\n if (!this.dragging) return;\n const rect = this.host.nativeElement.getBoundingClientRect();\n const raw =\n this.orientation() === 'horizontal'\n ? this.inlineFraction(event.clientX, rect) * 100\n : // The vertical split is laid out on the BLOCK axis, which `dir` does not\n // touch — measured from the top in both directions.\n ((event.clientY - rect.top) / rect.height) * 100;\n this.position.set(clamp(raw, this.minPosition(), this.maxPosition()));\n }\n\n protected onPointerUp(event: PointerEvent): void {\n if (!this.dragging) return;\n this.dragging = false;\n (event.currentTarget as HTMLElement).releasePointerCapture(event.pointerId);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (this.disabled()) return;\n const step = event.shiftKey ? 10 : 1;\n let next: number | null = null;\n if (this.orientation() === 'horizontal') {\n // Arrows follow the VISUAL axis (WAI-ARIA APG). Under `dir=\"rtl\"` the start pane\n // is the one on the RIGHT, so the visual right is the low end of the range and\n // ArrowRight has to shrink the position rather than grow it — otherwise the\n // divider walks away from the key the user is pressing.\n const inline = this.isRtl() ? -step : step;\n if (event.key === 'ArrowLeft') next = this.position() - inline;\n else if (event.key === 'ArrowRight') next = this.position() + inline;\n } else {\n // Block axis: unaffected by `dir`.\n if (event.key === 'ArrowUp') next = this.position() - step;\n else if (event.key === 'ArrowDown') next = this.position() + step;\n }\n // Home/End are semantic — first/last, not left/right — so they never mirror.\n if (event.key === 'Home') next = this.minPosition();\n else if (event.key === 'End') next = this.maxPosition();\n if (next === null) return;\n event.preventDefault();\n this.position.set(clamp(next, this.minPosition(), this.maxPosition()));\n }\n\n /**\n * Fraction of the host's width measured from the INLINE-START edge — the left edge in\n * LTR, the right edge in RTL, which is where `flex-direction: row` puts the start pane\n * whose size `position` is. Counted from `rect.left` in both, a drag under `dir=\"rtl\"`\n * read 0 where the start pane was widest and inverted the whole gesture.\n */\n private inlineFraction(clientX: number, rect: DOMRect): number {\n return this.isRtl() ? (rect.right - clientX) / rect.width : (clientX - rect.left) / rect.width;\n }\n\n /**\n * Read per event rather than cached: nothing this component renders is derived from the\n * direction — `flex-direction: row` mirrors the panes on its own — so there is no stale\n * value for `Directionality.change` to invalidate.\n */\n private isRtl(): boolean {\n return this.dir?.value === 'rtl';\n }\n}\n","<div class=\"wr-splitter__pane wr-splitter__pane--start\" [style.flex-basis]=\"startStyle()\">\n <ng-content select=\"[wrSplitterStart]\" />\n</div>\n\n<!-- Roving focus + arrow keys for keyboard resizing. -->\n<div\n class=\"wr-splitter__divider\"\n role=\"separator\"\n [attr.aria-label]=\"resolvedDividerLabel()\"\n [attr.aria-orientation]=\"orientation() === 'horizontal' ? 'vertical' : 'horizontal'\"\n [attr.aria-valuemin]=\"minPosition()\"\n [attr.aria-valuemax]=\"maxPosition()\"\n [attr.aria-valuenow]=\"position()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (keydown)=\"onKeydown($event)\"\n>\n <span class=\"wr-splitter__handle\" aria-hidden=\"true\"></span>\n</div>\n\n<div class=\"wr-splitter__pane wr-splitter__pane--end\" [style.flex-basis]=\"endStyle()\">\n <ng-content select=\"[wrSplitterEnd]\" />\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-splitter.mjs","sources":["../../../projects/lib/splitter/splitter.ts","../../../projects/lib/splitter/splitter.html","../../../projects/lib/splitter/ngwr-splitter.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, ElementRef, ViewEncapsulation, computed, effect, inject, input, model } from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { clamp } from 'ngwr/utils';\n\n/**\n * Resizable two-pane splitter. Project two pieces of content via\n * `[wrSplitterStart]` and `[wrSplitterEnd]`; the user drags the divider\n * to reallocate space between them.\n *\n * @example\n * ```html\n * <wr-splitter [(position)]=\"pos\">\n * <div wrSplitterStart>Files</div>\n * <div wrSplitterEnd>Editor</div>\n * </wr-splitter>\n *\n * <wr-splitter orientation=\"vertical\" [minPosition]=\"20\" [maxPosition]=\"80\">\n * <div wrSplitterStart>Preview</div>\n * <div wrSplitterEnd>Logs</div>\n * </wr-splitter>\n * ```\n *\n * @see https://ngwr.dev/reference/components/splitter\n */\n@Component({\n selector: 'wr-splitter',\n templateUrl: './splitter.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrSplitter {\n /** Position of the divider as a percentage (0–100). Two-way bindable. @default 50 */\n readonly position = model(50);\n\n /**\n * Divider direction:\n * - `'horizontal'` — vertical divider, drags L/R (panes side by side).\n * - `'vertical'` — horizontal divider, drags U/D (panes stacked).\n * @default 'horizontal'\n */\n readonly orientation = input<'horizontal' | 'vertical'>('horizontal');\n\n /** Minimum allowed position. @default 0 */\n readonly minPosition = input(0, { transform: (v: unknown): number => coerceNumberProperty(v, 0) });\n\n /** Maximum allowed position. @default 100 */\n readonly maxPosition = input(100, { transform: (v: unknown): number => coerceNumberProperty(v, 100) });\n\n /** Disable dragging. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Accessible name of the divider. A focusable `role=\"separator\"` is an\n * interactive widget, so it needs one — without it a screen reader reaching the\n * divider announces nothing but \"separator\" and its number. Falls back to\n * `splitter.divider`.\n */\n readonly dividerLabel = input<string | null>(null);\n\n protected readonly resolvedDividerLabel = useI18nText(this.dividerLabel, 'splitter.divider', 'Resize panes');\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n // Optional on purpose: `Directionality` is root-provided, so a consumer never has to\n // supply it — and a bare `TestBed` does not either.\n private readonly dir = inject(Directionality, { optional: true });\n private dragging = false;\n\n constructor() {\n // `position` is a `model`, so `[(position)]` and a restored layout write into it\n // directly; only the drag and the arrows clamped. Left alone, an out-of-range\n // write reached the DOM as `aria-valuenow=\"150\"` against a `valuemax` of 100 and\n // sized a pane at `150%`.\n effect(() => {\n const clamped = clamp(this.position(), this.minPosition(), this.maxPosition());\n if (clamped !== this.position()) this.position.set(clamped);\n });\n }\n\n protected readonly classes = computed(() => {\n const parts = ['wr-splitter', `wr-splitter--${this.orientation()}`];\n if (this.disabled()) parts.push('wr-splitter--disabled');\n return parts.join(' ');\n });\n\n protected readonly startStyle = computed(() => `${this.position()}%`);\n protected readonly endStyle = computed(() => `${100 - this.position()}%`);\n\n // Drag\n\n protected onPointerDown(event: PointerEvent): void {\n if (this.disabled()) return;\n // Any pointerdown used to start a drag, so holding the RIGHT button over the\n // divider and moving resized the panes; the same guard keeps a second finger\n // from taking over a drag already in progress.\n if (event.button !== 0 || !event.isPrimary) return;\n event.preventDefault();\n this.dragging = true;\n const handle = event.currentTarget as HTMLElement;\n handle.setPointerCapture(event.pointerId);\n // `preventDefault` above also suppresses the click's default focus, so without\n // this the arrows did nothing after a mouse drag until the user found the\n // divider again with Tab. `wr-slider` focuses its thumb for the same reason.\n handle.focus();\n }\n\n protected onPointerMove(event: PointerEvent): void {\n if (!this.dragging) return;\n const rect = this.host.nativeElement.getBoundingClientRect();\n const raw =\n this.orientation() === 'horizontal'\n ? this.inlineFraction(event.clientX, rect) * 100\n : // The vertical split is laid out on the BLOCK axis, which `dir` does not\n // touch — measured from the top in both directions.\n ((event.clientY - rect.top) / rect.height) * 100;\n this.position.set(clamp(raw, this.minPosition(), this.maxPosition()));\n }\n\n protected onPointerUp(event: PointerEvent): void {\n if (!this.dragging) return;\n this.dragging = false;\n (event.currentTarget as HTMLElement).releasePointerCapture(event.pointerId);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (this.disabled()) return;\n const step = event.shiftKey ? 10 : 1;\n let next: number | null = null;\n if (this.orientation() === 'horizontal') {\n // Arrows follow the VISUAL axis (WAI-ARIA APG). Under `dir=\"rtl\"` the start pane\n // is the one on the RIGHT, so the visual right is the low end of the range and\n // ArrowRight has to shrink the position rather than grow it — otherwise the\n // divider walks away from the key the user is pressing.\n const inline = this.isRtl() ? -step : step;\n if (event.key === 'ArrowLeft') next = this.position() - inline;\n else if (event.key === 'ArrowRight') next = this.position() + inline;\n } else {\n // Block axis: unaffected by `dir`.\n if (event.key === 'ArrowUp') next = this.position() - step;\n else if (event.key === 'ArrowDown') next = this.position() + step;\n }\n // Home/End are semantic — first/last, not left/right — so they never mirror.\n if (event.key === 'Home') next = this.minPosition();\n else if (event.key === 'End') next = this.maxPosition();\n if (next === null) return;\n event.preventDefault();\n this.position.set(clamp(next, this.minPosition(), this.maxPosition()));\n }\n\n /**\n * Fraction of the host's width measured from the INLINE-START edge — the left edge in\n * LTR, the right edge in RTL, which is where `flex-direction: row` puts the start pane\n * whose size `position` is. Counted from `rect.left` in both, a drag under `dir=\"rtl\"`\n * read 0 where the start pane was widest and inverted the whole gesture.\n */\n private inlineFraction(clientX: number, rect: DOMRect): number {\n return this.isRtl() ? (rect.right - clientX) / rect.width : (clientX - rect.left) / rect.width;\n }\n\n /**\n * Read per event rather than cached: nothing this component renders is derived from the\n * direction — `flex-direction: row` mirrors the panes on its own — so there is no stale\n * value for `Directionality.change` to invalidate.\n */\n private isRtl(): boolean {\n return this.dir?.value === 'rtl';\n }\n}\n","<div class=\"wr-splitter__pane wr-splitter__pane--start\" [style.flex-basis]=\"startStyle()\">\n <ng-content select=\"[wrSplitterStart]\" />\n</div>\n\n<!-- Roving focus + arrow keys for keyboard resizing. -->\n<div\n class=\"wr-splitter__divider\"\n role=\"separator\"\n [attr.aria-label]=\"resolvedDividerLabel()\"\n [attr.aria-orientation]=\"orientation() === 'horizontal' ? 'vertical' : 'horizontal'\"\n [attr.aria-valuemin]=\"minPosition()\"\n [attr.aria-valuemax]=\"maxPosition()\"\n [attr.aria-valuenow]=\"position()\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (keydown)=\"onKeydown($event)\"\n>\n <span class=\"wr-splitter__handle\" aria-hidden=\"true\"></span>\n</div>\n\n<div class=\"wr-splitter__pane wr-splitter__pane--end\" [style.flex-basis]=\"endStyle()\">\n <ng-content select=\"[wrSplitterEnd]\" />\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-spotlight-card-testing.mjs","sources":["../../../projects/lib/spotlight-card/testing/wr-spotlight-harness-vars.ts","../../../projects/lib/spotlight-card/testing/wr-spotlight-card-harness.ts","../../../projects/lib/spotlight-card/testing/wr-spotlight-harness.ts","../../../projects/lib/spotlight-card/testing/ngwr-spotlight-card-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\n/**\n * Split an inline `style` attribute into its declarations.\n *\n * Read from the attribute rather than through `getCssValue()`, and the difference is not\n * cosmetic. `getCssValue()` is `getComputedStyle()`, which in a browser resolves the\n * stylesheet's own `--wr-spotlight-x: 50%` / `--wr-spotlight-color: rgba(…)` defaults —\n * so \"the component has written nothing here yet, and the theme decides\" would come back\n * as a concrete value and the assertion would invert. jsdom loads no stylesheet, so the\n * computed read happens to agree today and would start lying the moment these harnesses\n * ran anywhere else. The attribute holds what the component or the directive itself\n * wrote, and nothing else.\n *\n * Shared by both harnesses in this entry point because both read the same three\n * variables off their host — in different units. See each class for which.\n */\nexport function wrSpotlightInlineVars(style: string): Map<string, string> {\n const declarations = new Map<string, string>();\n for (const declaration of style.split(';')) {\n const colon = declaration.indexOf(':');\n if (colon === -1) continue;\n const name = declaration.slice(0, colon).trim();\n if (name !== '') declarations.set(name, declaration.slice(colon + 1).trim());\n }\n return declarations;\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrSpotlightCardHarnessFilters } from './interfaces';\nimport { wrSpotlightInlineVars } from './wr-spotlight-harness-vars';\n\n/** `120px` — the card writes its pointer coordinates as a pixel length, always. */\nconst PIXEL_LENGTH = /^(-?[\\d.]+)px$/;\n\n/** `80%` — the radius, whose `%` suffix is the half that makes the gradient legal. */\nconst PERCENTAGE = /^(-?[\\d.]+)%$/;\n\n/**\n * Test harness for `<wr-spotlight-card>` — a card with a soft radial highlight that\n * follows the cursor.\n *\n * **The component's entire output is three custom properties on its own host, and this\n * harness reads them off the inline declaration.** There is no DOM for the highlight at\n * all: it is a `::before` pseudo-element, painted by the stylesheet from those three\n * values. So the paint is out of reach here and always will be — what is in reach is the\n * arithmetic and the exact spelling that feeds it, which is where this component breaks.\n *\n * **This harness reads PIXELS. {@link WrSpotlightHarness}, the `[wrSpotlight]` directive\n * shipped from the same entry point, reads PERCENTAGES.** Both write\n * `--wr-spotlight-x` / `--wr-spotlight-y`, in different units, off different events —\n * the card listens for `mousemove`, the directive for `pointermove`. That is deliberate\n * and documented, and it is also the mistake waiting to happen: putting `wrSpotlight` on\n * a `<wr-spotlight-card>` gives you two handlers overwriting one variable in units that\n * do not agree. Reach for the harness that matches the thing under test.\n *\n * **{@link movePointerTo} needs the card to have a box, and in a unit test it has none.**\n * The component subtracts its own `getBoundingClientRect()` from the client coordinate,\n * and jsdom answers that with a 0×0 rect at the origin — so the subtraction is a no-op\n * and the coordinate comes back unchanged. No throw here, unlike the directive's version:\n * the answer is degenerate rather than wrong, and it is legitimately what a real card\n * with no box would report. But a spec that asserts `x === clientX` against an unstubbed\n * rect has proved nothing about the box-relative conversion, which is the only thing\n * {@link getSpotlightPosition} is for. Give the host a box first:\n *\n * ```ts\n * const card = fixture.nativeElement.querySelector('wr-spotlight-card');\n * card.getBoundingClientRect = () => ({ left: 40, top: 20, width: 200, height: 100 }) as DOMRect;\n * ```\n *\n * Several plausible methods are deliberately absent. There is no `isSpotlightVisible()`\n * or `getSpotlightOpacity()`: the highlight sits at `opacity: 0` and is lifted to `0.6`\n * only under `:hover` / `:focus-within`, both of which are stylesheet selectors on a\n * pseudo-element — jsdom loads no stylesheet, matches no `:hover`, and cannot read a\n * pseudo-element's style at any time, so any answer would be invented. No `getGradient()`\n * for the same reason. No `hover()` / `mouseAway()` wrappers, which would be worse than\n * missing: the CDK's versions dispatch `mouseenter` / `mouseover` / `mouseleave`, none of\n * which this component listens to and none of which changes a `:hover` match in a test —\n * a spec calling them would look like it exercised the reveal and would have exercised\n * nothing. No `getSpotlightRect()` or `getCardSize()`, which are layout. And no\n * `respectsReducedMotion()`: this is the one animation component in the library that\n * deliberately does not implement the preference — a gradient tracks the cursor and no\n * content moves — so the method would answer identically for a component that honoured it\n * and one that ignored it.\n *\n * @example\n * ```ts\n * const card = await loader.getHarness(WrSpotlightCardHarness.with({ text: 'Hover me' }));\n *\n * expect(await card.getRadiusPercent()).toBe(80);\n * expect(await card.getSpotlightColor()).toBeNull(); // the theme decides\n * expect(await card.getSpotlightPosition()).toBeNull(); // nothing has pointed at it\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrSpotlightCardHarness extends ComponentHarness {\n static hostSelector = 'wr-spotlight-card';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrSpotlightCardHarnessFilters = {}): HarnessPredicate<WrSpotlightCardHarness> {\n return new HarnessPredicate(WrSpotlightCardHarness, options).addOption('text', options.text, (harness, text) =>\n HarnessPredicate.stringMatches(harness.getContentText(), text)\n );\n }\n\n /**\n * Where the gradient fades out, as a percentage of its own radius.\n *\n * Returns the number, but the assertion it really makes is about the `%` that was\n * stripped to get it — this method refuses a bare `80`. The stylesheet interpolates the\n * variable straight into `transparent var(--wr-spotlight-radius, 80%)`, where a unitless\n * value is not a length, so the whole `radial-gradient()` declaration is dropped and the\n * highlight disappears with nothing in the console to say why. Reading this as a plain\n * number would sail past exactly that.\n *\n * Always present, whatever the input says: a `[radius]` that is not a number coerces\n * back to the default 80 rather than writing nonsense into the sheet.\n */\n async getRadiusPercent(): Promise<number> {\n const raw = (await this.vars()).get('--wr-spotlight-radius') ?? null;\n const match = raw === null ? null : PERCENTAGE.exec(raw);\n if (!match) {\n throw new Error(\n `WrSpotlightCardHarness.getRadiusPercent(): \\`--wr-spotlight-radius\\` reads \"${raw ?? '(absent)'}\" on the ` +\n 'host, and the stylesheet needs a percentage there — it drops the whole gradient for a bare number. The ' +\n 'card writes this one on every render whatever the input says, so a missing or unsuffixed value means ' +\n 'the binding itself stopped reaching the element.'\n );\n }\n return Number.parseFloat(match[1]);\n }\n\n /**\n * The highlight colour the author asked for, or `null` when they left it to the theme.\n *\n * `null` IS the contract for an unset `[spotlightColor]`, not a gap in it. The\n * stylesheet's own default is `rgba(var(--wr-color-on-surface-rgb), .15)`, and that\n * token flips per theme — which is what makes one component read as a dark glow on a\n * light surface and a light beam on a dark one. A card that started writing a concrete\n * colour here would look right in whichever theme it was authored against and wrong in\n * the other, and this is the only method that can tell.\n */\n async getSpotlightColor(): Promise<string | null> {\n return (await this.vars()).get('--wr-spotlight-color') ?? null;\n }\n\n /**\n * Where the highlight last sat, in pixels from the card's own top-left corner. `null`\n * until something has pointed at the card.\n *\n * Pixels, not percentages — see this class's docs for why the distinction matters\n * inside this one entry point.\n *\n * `null` is a state of its own rather than a stand-in for `{ x: 0, y: 0 }`: before the\n * first move the stylesheet's `50% / 50%` default applies, and a pointer at the card's\n * own origin legitimately reports zero, so folding the two together would make an\n * untouched card indistinguishable from one being pointed at in its corner.\n *\n * Note there is no reset when the cursor leaves, and that is not an oversight the\n * harness papers over: the stylesheet fades the whole overlay to `opacity: 0` on leave,\n * so nothing is parked visibly — but the coordinate stays written for the life of the\n * element. `null` therefore means \"no pointer has EVER arrived\", not \"none is here now\".\n */\n async getSpotlightPosition(): Promise<{ x: number; y: number } | null> {\n const vars = await this.vars();\n const x = vars.get('--wr-spotlight-x');\n const y = vars.get('--wr-spotlight-y');\n if (x === undefined || y === undefined) return null;\n\n return { x: this.parsePixels(x, '--wr-spotlight-x'), y: this.parsePixels(y, '--wr-spotlight-y') };\n }\n\n /**\n * Point at the card, in client coordinates.\n *\n * A `mousemove`, not a `pointermove` — the component listens for the former, and the\n * directive in the same entry point for the latter, so a pointer event dispatched here\n * is silently ignored. It goes to the host itself, so it needs no hit test and cannot\n * miss in a test that has no layout; the coordinates only ever reach the component's\n * own subtraction.\n *\n * Stub the host's `getBoundingClientRect` before calling this, or the subtraction is\n * invisible — see this class's docs.\n */\n async movePointerTo(clientX: number, clientY: number): Promise<void> {\n await (await this.host()).dispatchEvent('mousemove', { clientX, clientY });\n }\n\n /**\n * The projected content's text, trimmed.\n *\n * Read from the host, which is the whole assertion: the template is a bare\n * `<ng-content />` with no wrapper element, so content lands as a direct child and an\n * interactive one keeps its own name and role. The component adds no ARIA and no node\n * of its own — there is nothing else here for a harness to ask about.\n */\n async getContentText(): Promise<string> {\n return (await this.host()).text();\n }\n\n /** The host's inline declarations — the component's whole written output. */\n private async vars(): Promise<Map<string, string>> {\n return wrSpotlightInlineVars((await (await this.host()).getAttribute('style')) ?? '');\n }\n\n /** One written coordinate, as a number, insisting on the `px` the card promises. */\n private parsePixels(raw: string, name: string): number {\n const match = PIXEL_LENGTH.exec(raw);\n if (!match) {\n throw new Error(\n `WrSpotlightCardHarness.getSpotlightPosition(): \\`${name}\\` reads \"${raw}\", which is not the pixel length ` +\n 'the card writes. A percentage here is the tell that `[wrSpotlight]` is on the same element — the two ' +\n 'exports of this entry point share these variables and disagree about the unit.'\n );\n }\n return Number.parseFloat(match[1]);\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrSpotlightHarnessFilters } from './interfaces';\nimport { wrSpotlightInlineVars } from './wr-spotlight-harness-vars';\n\n/**\n * Test harness for `[wrSpotlight]` — the directive that makes a cursor-following\n * gradient possible on an element the consumer owns.\n *\n * **This harness reads PERCENTAGES. {@link WrSpotlightCardHarness}, shipped from the same\n * entry point, reads PIXELS.** Both write `--wr-spotlight-x` / `--wr-spotlight-y`; the\n * directive writes them as a share of its own box off `pointermove`, the card as a pixel\n * offset off `mousemove`. That is why {@link getSpotlightPosition} hands back raw strings\n * and not numbers — collapsing `'37.5%'` to `37.5` would hide the one thing that decides\n * whether the gradient lands where the cursor is, and would make the two halves of this\n * entry point look interchangeable when they are not.\n *\n * **The directive ships no styles and adds no node.** There is no class to assert, no\n * wrapper, no ARIA: whatever the element announced before the directive applies is\n * exactly what it announces after, and the gradient is entirely the consumer's own CSS.\n * So there is no `isSpotlightVisible()` — there is nothing here to be visible — and no\n * `getResetX()` / `getResetY()`, which would echo the inputs back rather than report the\n * coordinate the directive actually WROTE. The echo would pass while the written value\n * was wrong, which is not hypothetical (see {@link getSpotlightPosition}). And no\n * `respectsReducedMotion()`: neither export in this entry point implements the\n * preference — it is the library's one documented exemption, since a gradient tracks the\n * cursor and no content moves — so the method would answer the same for a directive that\n * honoured it and one that ignored it.\n *\n * @example\n * ```ts\n * const spotlight = await loader.getHarness(WrSpotlightHarness.with({ hostClass: 'card' }));\n *\n * expect(await spotlight.getSpotlightPosition()).toEqual({ x: '50%', y: '50%' });\n * await spotlight.movePointerTo(150, 25); // stub the host's rect first\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrSpotlightHarness extends ComponentHarness {\n static hostSelector = '[wrSpotlight]';\n\n /**\n * Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`.\n *\n * Both options describe the HOST rather than the directive, because the directive\n * leaves no mark of its own to match on.\n */\n static with(options: WrSpotlightHarnessFilters = {}): HarnessPredicate<WrSpotlightHarness> {\n return new HarnessPredicate(WrSpotlightHarness, options)\n .addOption('text', options.text, async (harness, text) =>\n HarnessPredicate.stringMatches((await harness.host()).text(), text)\n )\n .addOption('hostClass', options.hostClass, async (harness, hostClass) =>\n (await harness.host()).hasClass(hostClass)\n );\n }\n\n /**\n * The coordinates the directive has written, as the raw strings it wrote them.\n *\n * Strings on purpose — see this class's docs. The values are percentages of the host's\n * own box while a pointer is over it, and whatever `[resetX]` / `[resetY]` say\n * otherwise, so a `px` appearing here means something else is writing these variables.\n *\n * **Present from construction, before any pointer arrives.** The directive seeds the\n * reset coordinates in its constructor, so the first read is `50%` / `50%` on a fresh\n * host — do not mistake that for a pointer having been tracked. Worth knowing that the\n * constructor runs before signal inputs are bound, so a bound (or even a static)\n * `resetX` is NOT what appears here initially: it is honoured from the first\n * {@link leave} onward, and the highlight jumps at that point. Reading the written\n * coordinate rather than the input is what makes that visible at all.\n *\n * Throws once the directive has been destroyed, rather than answering with the last\n * value it happened to leave behind — {@link hasTrackedPointer} is the question to ask\n * when the host may be gone.\n */\n async getSpotlightPosition(): Promise<{ x: string; y: string }> {\n const vars = await this.vars();\n const x = vars.get('--wr-spotlight-x');\n const y = vars.get('--wr-spotlight-y');\n if (x === undefined || y === undefined) {\n throw new Error(\n 'WrSpotlightHarness.getSpotlightPosition(): the host carries no `--wr-spotlight-x` / `--wr-spotlight-y`. ' +\n 'The directive writes both in its constructor and removes both on destroy, so this is a torn-down ' +\n 'directive rather than one that has not started — ask hasTrackedPointer() when the host may be gone.'\n );\n }\n return { x, y };\n }\n\n /**\n * Point at the host, in client coordinates.\n *\n * **Refuses on a host with no box, which in a unit test is every host.** The directive\n * divides the cursor offset by the element's own width and height, so a 0×0 rect writes\n * literally `Infinity%` or — at the origin — `NaN%` into the style. jsdom accepts either\n * string without complaint, so an unguarded harness would hand back something that looks\n * like a coordinate and describes nothing. Give the host a box first:\n *\n * ```ts\n * const el = fixture.nativeElement.querySelector('[wrSpotlight]');\n * el.getBoundingClientRect = () => ({ left: 0, top: 0, width: 200, height: 100 }) as DOMRect;\n * ```\n *\n * A `pointermove`, not a `mousemove` — the directive listens for the former, and the\n * component in the same entry point for the latter. It is dispatched on the host itself,\n * so it needs no hit test and cannot miss.\n */\n async movePointerTo(clientX: number, clientY: number): Promise<void> {\n const host = await this.host();\n const { width, height } = await host.getDimensions();\n if (!width || !height) {\n throw new Error(\n `WrSpotlightHarness.movePointerTo(${clientX}, ${clientY}): this host measures ${width}×${height}, so a ` +\n 'pointer position over it means nothing — the directive divides the offset by the box, and the result ' +\n 'goes into the style as `Infinity%` or `NaN%`. Stub `getBoundingClientRect` on the host first.'\n );\n }\n await host.dispatchEvent('pointermove', { clientX, clientY, pointerId: 1, isPrimary: true });\n }\n\n /**\n * Take the pointer away.\n *\n * Dispatches `pointerleave` — the directive's reset path, and the only place `[resetX]`\n * / `[resetY]` are read after construction. Needs no box: the handler writes the reset\n * strings straight through without measuring anything, which is why this one does not\n * refuse the way {@link movePointerTo} does.\n *\n * Not the CDK's `mouseAway()`, though that dispatches `pointerleave` too. This says what\n * it does, and it does not also fire the `mouseleave` / `mouseout` that the CDK helper\n * sends alongside it — events this directive ignores, and that would make a spec look\n * like it had covered more than it did.\n */\n async leave(): Promise<void> {\n await (await this.host()).dispatchEvent('pointerleave');\n }\n\n /**\n * Whether the directive still owns the host's spotlight variables.\n *\n * **This is \"the directive is alive\", not \"a pointer has moved over it\".** It is `true`\n * from the moment the directive constructs, because the constructor seeds the reset\n * coordinates — there is no earlier state to catch. What it distinguishes is a live\n * directive from a torn-down one: destroy removes both declarations from the element,\n * and nothing else in the repo asserts that cleanup. A directive that stopped removing\n * them would leave a stale gradient position on an element the consumer went on using,\n * and the only trace would be these two declarations outliving it.\n */\n async hasTrackedPointer(): Promise<boolean> {\n const vars = await this.vars();\n return vars.has('--wr-spotlight-x') && vars.has('--wr-spotlight-y');\n }\n\n /** The host's inline declarations — the directive's whole written output. */\n private async vars(): Promise<Map<string, string>> {\n return wrSpotlightInlineVars((await (await this.host()).getAttribute('style')) ?? '');\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-spotlight-card-testing.mjs","sources":["../../../projects/lib/spotlight-card/testing/wr-spotlight-harness-vars.ts","../../../projects/lib/spotlight-card/testing/wr-spotlight-card-harness.ts","../../../projects/lib/spotlight-card/testing/wr-spotlight-harness.ts","../../../projects/lib/spotlight-card/testing/ngwr-spotlight-card-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\n/**\n * Split an inline `style` attribute into its declarations.\n *\n * Read from the attribute rather than through `getCssValue()`, and the difference is not\n * cosmetic. `getCssValue()` is `getComputedStyle()`, which in a browser resolves the\n * stylesheet's own `--wr-spotlight-x: 50%` / `--wr-spotlight-color: rgba(…)` defaults —\n * so \"the component has written nothing here yet, and the theme decides\" would come back\n * as a concrete value and the assertion would invert. jsdom loads no stylesheet, so the\n * computed read happens to agree today and would start lying the moment these harnesses\n * ran anywhere else. The attribute holds what the component or the directive itself\n * wrote, and nothing else.\n *\n * Shared by both harnesses in this entry point because both read the same three\n * variables off their host — in different units. See each class for which.\n */\nexport function wrSpotlightInlineVars(style: string): Map<string, string> {\n const declarations = new Map<string, string>();\n for (const declaration of style.split(';')) {\n const colon = declaration.indexOf(':');\n if (colon === -1) continue;\n const name = declaration.slice(0, colon).trim();\n if (name !== '') declarations.set(name, declaration.slice(colon + 1).trim());\n }\n return declarations;\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrSpotlightCardHarnessFilters } from './interfaces';\nimport { wrSpotlightInlineVars } from './wr-spotlight-harness-vars';\n\n/** `120px` — the card writes its pointer coordinates as a pixel length, always. */\nconst PIXEL_LENGTH = /^(-?[\\d.]+)px$/;\n\n/** `80%` — the radius, whose `%` suffix is the half that makes the gradient legal. */\nconst PERCENTAGE = /^(-?[\\d.]+)%$/;\n\n/**\n * Test harness for `<wr-spotlight-card>` — a card with a soft radial highlight that\n * follows the cursor.\n *\n * **The component's entire output is three custom properties on its own host, and this\n * harness reads them off the inline declaration.** There is no DOM for the highlight at\n * all: it is a `::before` pseudo-element, painted by the stylesheet from those three\n * values. So the paint is out of reach here and always will be — what is in reach is the\n * arithmetic and the exact spelling that feeds it, which is where this component breaks.\n *\n * **This harness reads PIXELS. {@link WrSpotlightHarness}, the `[wrSpotlight]` directive\n * shipped from the same entry point, reads PERCENTAGES.** Both write\n * `--wr-spotlight-x` / `--wr-spotlight-y`, in different units, off different events —\n * the card listens for `mousemove`, the directive for `pointermove`. That is deliberate\n * and documented, and it is also the mistake waiting to happen: putting `wrSpotlight` on\n * a `<wr-spotlight-card>` gives you two handlers overwriting one variable in units that\n * do not agree. Reach for the harness that matches the thing under test.\n *\n * **{@link movePointerTo} needs the card to have a box, and in a unit test it has none.**\n * The component subtracts its own `getBoundingClientRect()` from the client coordinate,\n * and jsdom answers that with a 0×0 rect at the origin — so the subtraction is a no-op\n * and the coordinate comes back unchanged. No throw here, unlike the directive's version:\n * the answer is degenerate rather than wrong, and it is legitimately what a real card\n * with no box would report. But a spec that asserts `x === clientX` against an unstubbed\n * rect has proved nothing about the box-relative conversion, which is the only thing\n * {@link getSpotlightPosition} is for. Give the host a box first:\n *\n * ```ts\n * const card = fixture.nativeElement.querySelector('wr-spotlight-card');\n * card.getBoundingClientRect = () => ({ left: 40, top: 20, width: 200, height: 100 }) as DOMRect;\n * ```\n *\n * Several plausible methods are deliberately absent. There is no `isSpotlightVisible()`\n * or `getSpotlightOpacity()`: the highlight sits at `opacity: 0` and is lifted to `0.6`\n * only under `:hover` / `:focus-within`, both of which are stylesheet selectors on a\n * pseudo-element — jsdom loads no stylesheet, matches no `:hover`, and cannot read a\n * pseudo-element's style at any time, so any answer would be invented. No `getGradient()`\n * for the same reason. No `hover()` / `mouseAway()` wrappers, which would be worse than\n * missing: the CDK's versions dispatch `mouseenter` / `mouseover` / `mouseleave`, none of\n * which this component listens to and none of which changes a `:hover` match in a test —\n * a spec calling them would look like it exercised the reveal and would have exercised\n * nothing. No `getSpotlightRect()` or `getCardSize()`, which are layout. And no\n * `respectsReducedMotion()`: this is the one animation component in the library that\n * deliberately does not implement the preference — a gradient tracks the cursor and no\n * content moves — so the method would answer identically for a component that honoured it\n * and one that ignored it.\n *\n * @example\n * ```ts\n * const card = await loader.getHarness(WrSpotlightCardHarness.with({ text: 'Hover me' }));\n *\n * expect(await card.getRadiusPercent()).toBe(80);\n * expect(await card.getSpotlightColor()).toBeNull(); // the theme decides\n * expect(await card.getSpotlightPosition()).toBeNull(); // nothing has pointed at it\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrSpotlightCardHarness extends ComponentHarness {\n static hostSelector = 'wr-spotlight-card';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrSpotlightCardHarnessFilters = {}): HarnessPredicate<WrSpotlightCardHarness> {\n return new HarnessPredicate(WrSpotlightCardHarness, options).addOption('text', options.text, (harness, text) =>\n HarnessPredicate.stringMatches(harness.getContentText(), text)\n );\n }\n\n /**\n * Where the gradient fades out, as a percentage of its own radius.\n *\n * Returns the number, but the assertion it really makes is about the `%` that was\n * stripped to get it — this method refuses a bare `80`. The stylesheet interpolates the\n * variable straight into `transparent var(--wr-spotlight-radius, 80%)`, where a unitless\n * value is not a length, so the whole `radial-gradient()` declaration is dropped and the\n * highlight disappears with nothing in the console to say why. Reading this as a plain\n * number would sail past exactly that.\n *\n * Always present, whatever the input says: a `[radius]` that is not a number coerces\n * back to the default 80 rather than writing nonsense into the sheet.\n */\n async getRadiusPercent(): Promise<number> {\n const raw = (await this.vars()).get('--wr-spotlight-radius') ?? null;\n const match = raw === null ? null : PERCENTAGE.exec(raw);\n if (!match) {\n throw new Error(\n `WrSpotlightCardHarness.getRadiusPercent(): \\`--wr-spotlight-radius\\` reads \"${raw ?? '(absent)'}\" on the ` +\n 'host, and the stylesheet needs a percentage there — it drops the whole gradient for a bare number. The ' +\n 'card writes this one on every render whatever the input says, so a missing or unsuffixed value means ' +\n 'the binding itself stopped reaching the element.'\n );\n }\n return Number.parseFloat(match[1]);\n }\n\n /**\n * The highlight colour the author asked for, or `null` when they left it to the theme.\n *\n * `null` IS the contract for an unset `[spotlightColor]`, not a gap in it. The\n * stylesheet's own default is `rgba(var(--wr-color-on-surface-rgb), .15)`, and that\n * token flips per theme — which is what makes one component read as a dark glow on a\n * light surface and a light beam on a dark one. A card that started writing a concrete\n * colour here would look right in whichever theme it was authored against and wrong in\n * the other, and this is the only method that can tell.\n */\n async getSpotlightColor(): Promise<string | null> {\n return (await this.vars()).get('--wr-spotlight-color') ?? null;\n }\n\n /**\n * Where the highlight last sat, in pixels from the card's own top-left corner. `null`\n * until something has pointed at the card.\n *\n * Pixels, not percentages — see this class's docs for why the distinction matters\n * inside this one entry point.\n *\n * `null` is a state of its own rather than a stand-in for `{ x: 0, y: 0 }`: before the\n * first move the stylesheet's `50% / 50%` default applies, and a pointer at the card's\n * own origin legitimately reports zero, so folding the two together would make an\n * untouched card indistinguishable from one being pointed at in its corner.\n *\n * Note there is no reset when the cursor leaves, and that is not an oversight the\n * harness papers over: the stylesheet fades the whole overlay to `opacity: 0` on leave,\n * so nothing is parked visibly — but the coordinate stays written for the life of the\n * element. `null` therefore means \"no pointer has EVER arrived\", not \"none is here now\".\n */\n async getSpotlightPosition(): Promise<{ x: number; y: number } | null> {\n const vars = await this.vars();\n const x = vars.get('--wr-spotlight-x');\n const y = vars.get('--wr-spotlight-y');\n if (x === undefined || y === undefined) return null;\n\n return { x: this.parsePixels(x, '--wr-spotlight-x'), y: this.parsePixels(y, '--wr-spotlight-y') };\n }\n\n /**\n * Point at the card, in client coordinates.\n *\n * A `mousemove`, not a `pointermove` — the component listens for the former, and the\n * directive in the same entry point for the latter, so a pointer event dispatched here\n * is silently ignored. It goes to the host itself, so it needs no hit test and cannot\n * miss in a test that has no layout; the coordinates only ever reach the component's\n * own subtraction.\n *\n * Stub the host's `getBoundingClientRect` before calling this, or the subtraction is\n * invisible — see this class's docs.\n */\n async movePointerTo(clientX: number, clientY: number): Promise<void> {\n await (await this.host()).dispatchEvent('mousemove', { clientX, clientY });\n }\n\n /**\n * The projected content's text, trimmed.\n *\n * Read from the host, which is the whole assertion: the template is a bare\n * `<ng-content />` with no wrapper element, so content lands as a direct child and an\n * interactive one keeps its own name and role. The component adds no ARIA and no node\n * of its own — there is nothing else here for a harness to ask about.\n */\n async getContentText(): Promise<string> {\n return (await this.host()).text();\n }\n\n /** The host's inline declarations — the component's whole written output. */\n private async vars(): Promise<Map<string, string>> {\n return wrSpotlightInlineVars((await (await this.host()).getAttribute('style')) ?? '');\n }\n\n /** One written coordinate, as a number, insisting on the `px` the card promises. */\n private parsePixels(raw: string, name: string): number {\n const match = PIXEL_LENGTH.exec(raw);\n if (!match) {\n throw new Error(\n `WrSpotlightCardHarness.getSpotlightPosition(): \\`${name}\\` reads \"${raw}\", which is not the pixel length ` +\n 'the card writes. A percentage here is the tell that `[wrSpotlight]` is on the same element — the two ' +\n 'exports of this entry point share these variables and disagree about the unit.'\n );\n }\n return Number.parseFloat(match[1]);\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrSpotlightHarnessFilters } from './interfaces';\nimport { wrSpotlightInlineVars } from './wr-spotlight-harness-vars';\n\n/**\n * Test harness for `[wrSpotlight]` — the directive that makes a cursor-following\n * gradient possible on an element the consumer owns.\n *\n * **This harness reads PERCENTAGES. {@link WrSpotlightCardHarness}, shipped from the same\n * entry point, reads PIXELS.** Both write `--wr-spotlight-x` / `--wr-spotlight-y`; the\n * directive writes them as a share of its own box off `pointermove`, the card as a pixel\n * offset off `mousemove`. That is why {@link getSpotlightPosition} hands back raw strings\n * and not numbers — collapsing `'37.5%'` to `37.5` would hide the one thing that decides\n * whether the gradient lands where the cursor is, and would make the two halves of this\n * entry point look interchangeable when they are not.\n *\n * **The directive ships no styles and adds no node.** There is no class to assert, no\n * wrapper, no ARIA: whatever the element announced before the directive applies is\n * exactly what it announces after, and the gradient is entirely the consumer's own CSS.\n * So there is no `isSpotlightVisible()` — there is nothing here to be visible — and no\n * `getResetX()` / `getResetY()`, which would echo the inputs back rather than report the\n * coordinate the directive actually WROTE. The echo would pass while the written value\n * was wrong, which is not hypothetical (see {@link getSpotlightPosition}). And no\n * `respectsReducedMotion()`: neither export in this entry point implements the\n * preference — it is the library's one documented exemption, since a gradient tracks the\n * cursor and no content moves — so the method would answer the same for a directive that\n * honoured it and one that ignored it.\n *\n * @example\n * ```ts\n * const spotlight = await loader.getHarness(WrSpotlightHarness.with({ hostClass: 'card' }));\n *\n * expect(await spotlight.getSpotlightPosition()).toEqual({ x: '50%', y: '50%' });\n * await spotlight.movePointerTo(150, 25); // stub the host's rect first\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrSpotlightHarness extends ComponentHarness {\n static hostSelector = '[wrSpotlight]';\n\n /**\n * Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`.\n *\n * Both options describe the HOST rather than the directive, because the directive\n * leaves no mark of its own to match on.\n */\n static with(options: WrSpotlightHarnessFilters = {}): HarnessPredicate<WrSpotlightHarness> {\n return new HarnessPredicate(WrSpotlightHarness, options)\n .addOption('text', options.text, async (harness, text) =>\n HarnessPredicate.stringMatches((await harness.host()).text(), text)\n )\n .addOption('hostClass', options.hostClass, async (harness, hostClass) =>\n (await harness.host()).hasClass(hostClass)\n );\n }\n\n /**\n * The coordinates the directive has written, as the raw strings it wrote them.\n *\n * Strings on purpose — see this class's docs. The values are percentages of the host's\n * own box while a pointer is over it, and whatever `[resetX]` / `[resetY]` say\n * otherwise, so a `px` appearing here means something else is writing these variables.\n *\n * **Present from construction, before any pointer arrives.** The directive seeds the\n * reset coordinates in its constructor, so the first read is `50%` / `50%` on a fresh\n * host — do not mistake that for a pointer having been tracked. Worth knowing that the\n * constructor runs before signal inputs are bound, so a bound (or even a static)\n * `resetX` is NOT what appears here initially: it is honoured from the first\n * {@link leave} onward, and the highlight jumps at that point. Reading the written\n * coordinate rather than the input is what makes that visible at all.\n *\n * Throws once the directive has been destroyed, rather than answering with the last\n * value it happened to leave behind — {@link hasTrackedPointer} is the question to ask\n * when the host may be gone.\n */\n async getSpotlightPosition(): Promise<{ x: string; y: string }> {\n const vars = await this.vars();\n const x = vars.get('--wr-spotlight-x');\n const y = vars.get('--wr-spotlight-y');\n if (x === undefined || y === undefined) {\n throw new Error(\n 'WrSpotlightHarness.getSpotlightPosition(): the host carries no `--wr-spotlight-x` / `--wr-spotlight-y`. ' +\n 'The directive writes both in its constructor and removes both on destroy, so this is a torn-down ' +\n 'directive rather than one that has not started — ask hasTrackedPointer() when the host may be gone.'\n );\n }\n return { x, y };\n }\n\n /**\n * Point at the host, in client coordinates.\n *\n * **Refuses on a host with no box, which in a unit test is every host.** The directive\n * divides the cursor offset by the element's own width and height, so a 0×0 rect writes\n * literally `Infinity%` or — at the origin — `NaN%` into the style. jsdom accepts either\n * string without complaint, so an unguarded harness would hand back something that looks\n * like a coordinate and describes nothing. Give the host a box first:\n *\n * ```ts\n * const el = fixture.nativeElement.querySelector('[wrSpotlight]');\n * el.getBoundingClientRect = () => ({ left: 0, top: 0, width: 200, height: 100 }) as DOMRect;\n * ```\n *\n * A `pointermove`, not a `mousemove` — the directive listens for the former, and the\n * component in the same entry point for the latter. It is dispatched on the host itself,\n * so it needs no hit test and cannot miss.\n */\n async movePointerTo(clientX: number, clientY: number): Promise<void> {\n const host = await this.host();\n const { width, height } = await host.getDimensions();\n if (!width || !height) {\n throw new Error(\n `WrSpotlightHarness.movePointerTo(${clientX}, ${clientY}): this host measures ${width}×${height}, so a ` +\n 'pointer position over it means nothing — the directive divides the offset by the box, and the result ' +\n 'goes into the style as `Infinity%` or `NaN%`. Stub `getBoundingClientRect` on the host first.'\n );\n }\n await host.dispatchEvent('pointermove', { clientX, clientY, pointerId: 1, isPrimary: true });\n }\n\n /**\n * Take the pointer away.\n *\n * Dispatches `pointerleave` — the directive's reset path, and the only place `[resetX]`\n * / `[resetY]` are read after construction. Needs no box: the handler writes the reset\n * strings straight through without measuring anything, which is why this one does not\n * refuse the way {@link movePointerTo} does.\n *\n * Not the CDK's `mouseAway()`, though that dispatches `pointerleave` too. This says what\n * it does, and it does not also fire the `mouseleave` / `mouseout` that the CDK helper\n * sends alongside it — events this directive ignores, and that would make a spec look\n * like it had covered more than it did.\n */\n async leave(): Promise<void> {\n await (await this.host()).dispatchEvent('pointerleave');\n }\n\n /**\n * Whether the directive still owns the host's spotlight variables.\n *\n * **This is \"the directive is alive\", not \"a pointer has moved over it\".** It is `true`\n * from the moment the directive constructs, because the constructor seeds the reset\n * coordinates — there is no earlier state to catch. What it distinguishes is a live\n * directive from a torn-down one: destroy removes both declarations from the element,\n * and nothing else in the repo asserts that cleanup. A directive that stopped removing\n * them would leave a stale gradient position on an element the consumer went on using,\n * and the only trace would be these two declarations outliving it.\n */\n async hasTrackedPointer(): Promise<boolean> {\n const vars = await this.vars();\n return vars.has('--wr-spotlight-x') && vars.has('--wr-spotlight-y');\n }\n\n /** The host's inline declarations — the directive's whole written output. */\n private async vars(): Promise<Map<string, string>> {\n return wrSpotlightInlineVars((await (await this.host()).getAttribute('style')) ?? '');\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -44,10 +44,10 @@ class WrSpotlightCard {
|
|
|
44
44
|
host.style.setProperty('--wr-spotlight-x', `${event.clientX - rect.left}px`);
|
|
45
45
|
host.style.setProperty('--wr-spotlight-y', `${event.clientY - rect.top}px`);
|
|
46
46
|
}
|
|
47
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
48
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.
|
|
47
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrSpotlightCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
48
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: WrSpotlightCard, isStandalone: true, selector: "wr-spotlight-card", inputs: { spotlightColor: { classPropertyName: "spotlightColor", publicName: "spotlightColor", isSignal: true, isRequired: false, transformFunction: null }, radius: { classPropertyName: "radius", publicName: "radius", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mousemove": "onMouseMove($event)" }, properties: { "style.--wr-spotlight-color": "spotlightColor()", "style.--wr-spotlight-radius": "radius() + \"%\"" }, classAttribute: "wr-spotlight-card" }, ngImport: i0, template: '<ng-content />', isInline: true, styles: [".wr-spotlight-card{--wr-spotlight-x: 50%;--wr-spotlight-y: 50%;--wr-spotlight-color: rgba(var(--wr-color-on-surface-rgb), .15);position:relative;display:block;border-radius:1.5rem;border:1px solid var(--wr-color-outline);background-color:var(--wr-color-surface);padding:2rem;overflow:hidden;color:var(--wr-color-on-surface)}.wr-spotlight-card:before{content:\"\";position:absolute;inset:0;background:radial-gradient(circle at var(--wr-spotlight-x) var(--wr-spotlight-y),var(--wr-spotlight-color),transparent var(--wr-spotlight-radius, 80%));opacity:0;transition:opacity .5s ease;pointer-events:none}.wr-spotlight-card:hover:before,.wr-spotlight-card:focus-within:before{opacity:.6}\n"], encapsulation: i0.ViewEncapsulation.None });
|
|
49
49
|
}
|
|
50
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
50
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrSpotlightCard, decorators: [{
|
|
51
51
|
type: Component,
|
|
52
52
|
args: [{ selector: 'wr-spotlight-card', template: '<ng-content />', encapsulation: ViewEncapsulation.None, host: {
|
|
53
53
|
class: 'wr-spotlight-card',
|
|
@@ -119,10 +119,10 @@ class WrSpotlight {
|
|
|
119
119
|
host.style.setProperty('--wr-spotlight-x', this.resetX());
|
|
120
120
|
host.style.setProperty('--wr-spotlight-y', this.resetY());
|
|
121
121
|
}
|
|
122
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
123
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.
|
|
122
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrSpotlight, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
123
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: WrSpotlight, isStandalone: true, selector: "[wrSpotlight]", inputs: { resetX: { classPropertyName: "resetX", publicName: "resetX", isSignal: true, isRequired: false, transformFunction: null }, resetY: { classPropertyName: "resetY", publicName: "resetY", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "pointermove": "onMove($event)", "pointerleave": "onLeave()" } }, ngImport: i0 });
|
|
124
124
|
}
|
|
125
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
125
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrSpotlight, decorators: [{
|
|
126
126
|
type: Directive,
|
|
127
127
|
args: [{
|
|
128
128
|
selector: '[wrSpotlight]',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-spotlight-card.mjs","sources":["../../../projects/lib/spotlight-card/spotlight-card.ts","../../../projects/lib/spotlight-card/spotlight.ts","../../../projects/lib/spotlight-card/ngwr-spotlight-card.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n *\n * Angular adaptation of the SpotlightCard effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/components/spotlight-card\n */\n\nimport { coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, ElementRef, inject, input } from '@angular/core';\n\n/**\n * Cursor-tracked spotlight card. A soft radial highlight follows the\n * pointer inside the card; fades in on hover, out on leave.\n *\n * Project any content as children — the spotlight overlay sits behind\n * the projected DOM (via a `::before` pseudo with `pointer-events: none`)\n * so clicks pass through naturally.\n *\n * @example\n * ```html\n * <wr-spotlight-card spotlightColor=\"rgba(120, 180, 255, 0.25)\">\n * <h3>Hover me</h3>\n * </wr-spotlight-card>\n * ```\n *\n * @see https://www.reactbits.dev/components/spotlight-card\n */\n@Component({\n selector: 'wr-spotlight-card',\n template: '<ng-content />',\n styleUrl: './spotlight-card.scss',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-spotlight-card',\n '[style.--wr-spotlight-color]': 'spotlightColor()',\n '[style.--wr-spotlight-radius]': 'radius() + \"%\"',\n '(mousemove)': 'onMouseMove($event)',\n },\n})\nexport class WrSpotlightCard {\n /**\n * Highlight colour (any CSS colour). When unset, the theme decides:\n * a dark-ish glow on light surfaces, a light glow on dark.\n */\n readonly spotlightColor = input<string | null>(null);\n\n /** Where the spotlight fades out, as a percentage of the gradient. @default 80 */\n readonly radius = input(80, { transform: (v: unknown): number => coerceNumberProperty(v, 80) });\n\n private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);\n\n protected onMouseMove(event: MouseEvent): void {\n const host = this.el.nativeElement;\n const rect = host.getBoundingClientRect();\n host.style.setProperty('--wr-spotlight-x', `${event.clientX - rect.left}px`);\n host.style.setProperty('--wr-spotlight-y', `${event.clientY - rect.top}px`);\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { DestroyRef, Directive, ElementRef, NgZone, inject, input } from '@angular/core';\n\n/**\n * Cursor-follow radial spotlight. Sets the `--wr-spotlight-x` and\n * `--wr-spotlight-y` CSS custom properties on the host based on the\n * pointer position so a `radial-gradient` (or `mask`) in your CSS can\n * follow the cursor.\n *\n * @example\n * ```html\n * <div class=\"card\" wrSpotlight>…</div>\n * ```\n *\n * ```scss\n * .card {\n * background: radial-gradient(\n * 400px circle at var(--wr-spotlight-x, 50%) var(--wr-spotlight-y, 50%),\n * rgba(var(--wr-color-primary-rgb), 0.15),\n * transparent 60%\n * );\n * }\n * ```\n */\n@Directive({\n selector: '[wrSpotlight]',\n host: { '(pointermove)': 'onMove($event)', '(pointerleave)': 'onLeave()' },\n})\nexport class WrSpotlight {\n /** Optional default coordinates when no pointer is over the host. */\n readonly resetX = input<string>('50%');\n readonly resetY = input<string>('50%');\n\n private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly zone = inject(NgZone);\n private readonly destroyRef = inject(DestroyRef);\n\n constructor() {\n const host = this.el.nativeElement;\n host.style.setProperty('--wr-spotlight-x', this.resetX());\n host.style.setProperty('--wr-spotlight-y', this.resetY());\n this.destroyRef.onDestroy(() => {\n host.style.removeProperty('--wr-spotlight-x');\n host.style.removeProperty('--wr-spotlight-y');\n });\n }\n\n /** @internal */\n protected onMove(event: PointerEvent): void {\n this.zone.runOutsideAngular(() => {\n const host = this.el.nativeElement;\n const rect = host.getBoundingClientRect();\n const x = ((event.clientX - rect.left) / rect.width) * 100;\n const y = ((event.clientY - rect.top) / rect.height) * 100;\n host.style.setProperty('--wr-spotlight-x', `${x}%`);\n host.style.setProperty('--wr-spotlight-y', `${y}%`);\n });\n }\n\n /** @internal */\n protected onLeave(): void {\n const host = this.el.nativeElement;\n host.style.setProperty('--wr-spotlight-x', this.resetX());\n host.style.setProperty('--wr-spotlight-y', this.resetY());\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ngwr-spotlight-card.mjs","sources":["../../../projects/lib/spotlight-card/spotlight-card.ts","../../../projects/lib/spotlight-card/spotlight.ts","../../../projects/lib/spotlight-card/ngwr-spotlight-card.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n *\n * Angular adaptation of the SpotlightCard effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/components/spotlight-card\n */\n\nimport { coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, ElementRef, inject, input } from '@angular/core';\n\n/**\n * Cursor-tracked spotlight card. A soft radial highlight follows the\n * pointer inside the card; fades in on hover, out on leave.\n *\n * Project any content as children — the spotlight overlay sits behind\n * the projected DOM (via a `::before` pseudo with `pointer-events: none`)\n * so clicks pass through naturally.\n *\n * @example\n * ```html\n * <wr-spotlight-card spotlightColor=\"rgba(120, 180, 255, 0.25)\">\n * <h3>Hover me</h3>\n * </wr-spotlight-card>\n * ```\n *\n * @see https://www.reactbits.dev/components/spotlight-card\n */\n@Component({\n selector: 'wr-spotlight-card',\n template: '<ng-content />',\n styleUrl: './spotlight-card.scss',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-spotlight-card',\n '[style.--wr-spotlight-color]': 'spotlightColor()',\n '[style.--wr-spotlight-radius]': 'radius() + \"%\"',\n '(mousemove)': 'onMouseMove($event)',\n },\n})\nexport class WrSpotlightCard {\n /**\n * Highlight colour (any CSS colour). When unset, the theme decides:\n * a dark-ish glow on light surfaces, a light glow on dark.\n */\n readonly spotlightColor = input<string | null>(null);\n\n /** Where the spotlight fades out, as a percentage of the gradient. @default 80 */\n readonly radius = input(80, { transform: (v: unknown): number => coerceNumberProperty(v, 80) });\n\n private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);\n\n protected onMouseMove(event: MouseEvent): void {\n const host = this.el.nativeElement;\n const rect = host.getBoundingClientRect();\n host.style.setProperty('--wr-spotlight-x', `${event.clientX - rect.left}px`);\n host.style.setProperty('--wr-spotlight-y', `${event.clientY - rect.top}px`);\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { DestroyRef, Directive, ElementRef, NgZone, inject, input } from '@angular/core';\n\n/**\n * Cursor-follow radial spotlight. Sets the `--wr-spotlight-x` and\n * `--wr-spotlight-y` CSS custom properties on the host based on the\n * pointer position so a `radial-gradient` (or `mask`) in your CSS can\n * follow the cursor.\n *\n * @example\n * ```html\n * <div class=\"card\" wrSpotlight>…</div>\n * ```\n *\n * ```scss\n * .card {\n * background: radial-gradient(\n * 400px circle at var(--wr-spotlight-x, 50%) var(--wr-spotlight-y, 50%),\n * rgba(var(--wr-color-primary-rgb), 0.15),\n * transparent 60%\n * );\n * }\n * ```\n */\n@Directive({\n selector: '[wrSpotlight]',\n host: { '(pointermove)': 'onMove($event)', '(pointerleave)': 'onLeave()' },\n})\nexport class WrSpotlight {\n /** Optional default coordinates when no pointer is over the host. */\n readonly resetX = input<string>('50%');\n readonly resetY = input<string>('50%');\n\n private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly zone = inject(NgZone);\n private readonly destroyRef = inject(DestroyRef);\n\n constructor() {\n const host = this.el.nativeElement;\n host.style.setProperty('--wr-spotlight-x', this.resetX());\n host.style.setProperty('--wr-spotlight-y', this.resetY());\n this.destroyRef.onDestroy(() => {\n host.style.removeProperty('--wr-spotlight-x');\n host.style.removeProperty('--wr-spotlight-y');\n });\n }\n\n /** @internal */\n protected onMove(event: PointerEvent): void {\n this.zone.runOutsideAngular(() => {\n const host = this.el.nativeElement;\n const rect = host.getBoundingClientRect();\n const x = ((event.clientX - rect.left) / rect.width) * 100;\n const y = ((event.clientY - rect.top) / rect.height) * 100;\n host.style.setProperty('--wr-spotlight-x', `${x}%`);\n host.style.setProperty('--wr-spotlight-y', `${y}%`);\n });\n }\n\n /** @internal */\n protected onLeave(): void {\n const host = this.el.nativeElement;\n host.style.setProperty('--wr-spotlight-x', this.resetX());\n host.style.setProperty('--wr-spotlight-y', this.resetY());\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -283,10 +283,10 @@ class WrSquircle {
|
|
|
283
283
|
host.style.removeProperty('--wr-squircle-border-width');
|
|
284
284
|
host.style.removeProperty('--wr-squircle-border-color');
|
|
285
285
|
}
|
|
286
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
287
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.
|
|
286
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrSquircle, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
287
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: WrSquircle, isStandalone: true, selector: "[wrSquircle]", inputs: { radius: { classPropertyName: "radius", publicName: "radius", isSignal: true, isRequired: false, transformFunction: null }, smoothing: { classPropertyName: "smoothing", publicName: "smoothing", isSignal: true, isRequired: false, transformFunction: null }, enabled: { classPropertyName: "enabled", publicName: "enabled", isSignal: true, isRequired: false, transformFunction: null }, borderWidth: { classPropertyName: "borderWidth", publicName: "borderWidth", isSignal: true, isRequired: false, transformFunction: null }, borderColor: { classPropertyName: "borderColor", publicName: "borderColor", isSignal: true, isRequired: false, transformFunction: null }, corners: { classPropertyName: "corners", publicName: "corners", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { enabled: "enabledChange", borderWidth: "borderWidthChange", borderColor: "borderColorChange", corners: "cornersChange" }, host: { properties: { "class.wr-squircle--bordered": "borderWidth() > 0 && enabled()" } }, exportAs: ["wrSquircle"], ngImport: i0 });
|
|
288
288
|
}
|
|
289
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
289
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrSquircle, decorators: [{
|
|
290
290
|
type: Directive,
|
|
291
291
|
args: [{
|
|
292
292
|
selector: '[wrSquircle]',
|
|
@@ -318,10 +318,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
318
318
|
* for why, and for the native route to use instead.
|
|
319
319
|
*/
|
|
320
320
|
class WrSquircleHost {
|
|
321
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.
|
|
322
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.
|
|
321
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrSquircleHost, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
322
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: WrSquircleHost, isStandalone: true, selector: "wr-squircle", host: { classAttribute: "wr-squircle" }, hostDirectives: [{ directive: WrSquircle, inputs: ["radius", "radius", "smoothing", "smoothing", "borderWidth", "borderWidth", "borderColor", "borderColor"] }], ngImport: i0, template: '<ng-content />', isInline: true, encapsulation: i0.ViewEncapsulation.None });
|
|
323
323
|
}
|
|
324
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.
|
|
324
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: WrSquircleHost, decorators: [{
|
|
325
325
|
type: Component,
|
|
326
326
|
args: [{
|
|
327
327
|
selector: 'wr-squircle',
|