ngwr 11.1.0 → 11.2.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/circular-text/styles/_index.scss +11 -0
- package/command-palette/styles/_index.scss +12 -0
- package/fesm2022/ngwr-action-sheet-testing.mjs +313 -0
- package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -0
- package/fesm2022/ngwr-bar-chart-testing.mjs +89 -0
- package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -0
- package/fesm2022/ngwr-blur-text-testing.mjs +203 -0
- package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-blur-text.mjs +15 -1
- package/fesm2022/ngwr-blur-text.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow-testing.mjs +325 -0
- package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -0
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs +95 -0
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -0
- package/fesm2022/ngwr-calendar-testing.mjs +368 -0
- package/fesm2022/ngwr-calendar-testing.mjs.map +1 -0
- package/fesm2022/ngwr-calendar.mjs +43 -3
- package/fesm2022/ngwr-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +12 -0
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox.mjs +8 -0
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text-testing.mjs +230 -0
- package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-circular-text.mjs +2 -2
- package/fesm2022/ngwr-circular-text.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker-testing.mjs +432 -0
- package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -0
- package/fesm2022/ngwr-color-picker.mjs +29 -3
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu.mjs +25 -5
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker-testing.mjs +10 -79
- package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-decrypt-text-testing.mjs +261 -0
- package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-decrypt-text.mjs +6 -1
- package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart-testing.mjs +93 -0
- package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -0
- package/fesm2022/ngwr-drawer.mjs +36 -7
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +32 -2
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar-testing.mjs +446 -0
- package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -0
- package/fesm2022/ngwr-event-calendar.mjs +43 -18
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text-testing.mjs +218 -0
- package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-fuzzy-text-testing.mjs +134 -0
- package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-gauge-testing.mjs +86 -0
- package/fesm2022/ngwr-gauge-testing.mjs.map +1 -0
- package/fesm2022/ngwr-glitch-text-testing.mjs +181 -0
- package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-gradient-text-testing.mjs +223 -0
- package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-i18n-en.mjs +14 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +14 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper-testing.mjs +156 -0
- package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -0
- package/fesm2022/ngwr-image-cropper.mjs +10 -3
- package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
- package/fesm2022/ngwr-input-number.mjs +73 -6
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-knob-testing.mjs +220 -0
- package/fesm2022/ngwr-knob-testing.mjs.map +1 -0
- package/fesm2022/ngwr-lightbox-testing.mjs +223 -0
- package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -0
- package/fesm2022/ngwr-lightbox.mjs +9 -2
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart-testing.mjs +119 -0
- package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -0
- package/fesm2022/ngwr-marquee-testing.mjs +336 -0
- package/fesm2022/ngwr-marquee-testing.mjs.map +1 -0
- package/fesm2022/ngwr-meter-group-testing.mjs +80 -0
- package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -0
- package/fesm2022/ngwr-overlay.mjs +28 -1
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +14 -0
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-rotating-text-testing.mjs +220 -0
- package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-rotating-text.mjs +2 -2
- package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +70 -11
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text-testing.mjs +237 -0
- package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-slider.mjs +44 -6
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline-testing.mjs +76 -0
- package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -0
- package/fesm2022/ngwr-speed-dial-testing.mjs +279 -0
- package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -0
- package/fesm2022/ngwr-splash-cursor-testing.mjs +89 -0
- package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -0
- package/fesm2022/ngwr-split-text-testing.mjs +224 -0
- package/fesm2022/ngwr-split-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-split-text.mjs +15 -1
- package/fesm2022/ngwr-split-text.mjs.map +1 -1
- package/fesm2022/ngwr-splitter-testing.mjs +245 -0
- package/fesm2022/ngwr-splitter-testing.mjs.map +1 -0
- package/fesm2022/ngwr-spotlight-card-testing.mjs +368 -0
- package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -0
- package/fesm2022/ngwr-star-border-testing.mjs +172 -0
- package/fesm2022/ngwr-star-border-testing.mjs.map +1 -0
- package/fesm2022/ngwr-table.mjs +13 -3
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-tabs.mjs +15 -1
- package/fesm2022/ngwr-tabs.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card-testing.mjs +190 -0
- package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -0
- package/fesm2022/ngwr-tilt-card.mjs +19 -1
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
- package/fesm2022/ngwr-toast.mjs +36 -7
- package/fesm2022/ngwr-toast.mjs.map +1 -1
- package/fesm2022/ngwr-tour-testing.mjs +173 -0
- package/fesm2022/ngwr-tour-testing.mjs.map +1 -0
- package/fesm2022/ngwr-tour.mjs +2 -2
- package/fesm2022/ngwr-tour.mjs.map +1 -1
- package/fesm2022/ngwr-tree.mjs +10 -3
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter-testing.mjs +228 -0
- package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -0
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-waves-testing.mjs +128 -0
- package/fesm2022/ngwr-waves-testing.mjs.map +1 -0
- package/fesm2022/ngwr-window-testing.mjs +361 -0
- package/fesm2022/ngwr-window-testing.mjs.map +1 -0
- package/fesm2022/ngwr-window.mjs +84 -10
- package/fesm2022/ngwr-window.mjs.map +1 -1
- package/i18n/en.json +16 -2
- package/i18n/ru.json +16 -2
- package/llms-full.txt +147 -0
- package/llms.txt +27 -8
- package/mcp/providers.js +29 -0
- package/mcp/tools.js +1 -29
- package/package.json +145 -1
- package/skills/ngwr/SKILL.md +115 -0
- package/skills/ngwr/references/catalog.md +212 -0
- package/skills/ngwr/references/setup.md +73 -0
- package/types/ngwr-action-sheet-testing.d.ts +260 -0
- package/types/ngwr-bar-chart-testing.d.ts +77 -0
- package/types/ngwr-blur-text-testing.d.ts +180 -0
- package/types/ngwr-blur-text.d.ts +12 -0
- package/types/ngwr-border-glow-testing.d.ts +219 -0
- package/types/ngwr-calendar-heatmap-testing.d.ts +79 -0
- package/types/ngwr-calendar-testing.d.ts +277 -0
- package/types/ngwr-calendar.d.ts +10 -0
- package/types/ngwr-checkbox.d.ts +13 -0
- package/types/ngwr-circular-text-testing.d.ts +196 -0
- package/types/ngwr-color-picker-testing.d.ts +291 -0
- package/types/ngwr-color-picker.d.ts +13 -1
- package/types/ngwr-context-menu.d.ts +15 -1
- package/types/ngwr-date-picker-testing.d.ts +6 -66
- package/types/ngwr-decrypt-text-testing.d.ts +222 -0
- package/types/ngwr-donut-chart-testing.d.ts +79 -0
- package/types/ngwr-drawer.d.ts +12 -0
- package/types/ngwr-dropdown.d.ts +9 -0
- package/types/ngwr-event-calendar-testing.d.ts +301 -0
- package/types/ngwr-event-calendar.d.ts +9 -6
- package/types/ngwr-falling-text-testing.d.ts +186 -0
- package/types/ngwr-fuzzy-text-testing.d.ts +130 -0
- package/types/ngwr-gauge-testing.d.ts +71 -0
- package/types/ngwr-glitch-text-testing.d.ts +172 -0
- package/types/ngwr-gradient-text-testing.d.ts +173 -0
- package/types/ngwr-image-cropper-testing.d.ts +134 -0
- package/types/ngwr-image-cropper.d.ts +1 -0
- package/types/ngwr-input-number.d.ts +33 -1
- package/types/ngwr-knob-testing.d.ts +150 -0
- package/types/ngwr-lightbox-testing.d.ts +154 -0
- package/types/ngwr-lightbox.d.ts +6 -0
- package/types/ngwr-line-chart-testing.d.ts +93 -0
- package/types/ngwr-marquee-testing.d.ts +280 -0
- package/types/ngwr-meter-group-testing.d.ts +67 -0
- package/types/ngwr-overlay.d.ts +19 -1
- package/types/ngwr-rotating-text-testing.d.ts +191 -0
- package/types/ngwr-select.d.ts +32 -0
- package/types/ngwr-shiny-text-testing.d.ts +165 -0
- package/types/ngwr-slider.d.ts +15 -0
- package/types/ngwr-sparkline-testing.d.ts +69 -0
- package/types/ngwr-speed-dial-testing.d.ts +195 -0
- package/types/ngwr-splash-cursor-testing.d.ts +95 -0
- package/types/ngwr-split-text-testing.d.ts +209 -0
- package/types/ngwr-split-text.d.ts +12 -0
- package/types/ngwr-splitter-testing.d.ts +174 -0
- package/types/ngwr-spotlight-card-testing.d.ts +303 -0
- package/types/ngwr-star-border-testing.d.ts +135 -0
- package/types/ngwr-table.d.ts +10 -1
- package/types/ngwr-tilt-card-testing.d.ts +158 -0
- package/types/ngwr-tilt-card.d.ts +2 -0
- package/types/ngwr-toast.d.ts +14 -5
- package/types/ngwr-tour-testing.d.ts +143 -0
- package/types/ngwr-tree.d.ts +7 -0
- package/types/ngwr-typewriter-testing.d.ts +201 -0
- package/types/ngwr-version.d.ts +1 -1
- package/types/ngwr-waves-testing.d.ts +121 -0
- package/types/ngwr-window-testing.d.ts +263 -0
- package/types/ngwr-window.d.ts +27 -10
- package/window/styles/_index.scss +44 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-overlay.mjs","sources":["../../../projects/lib/overlay/wr-overlay-close.ts","../../../projects/lib/overlay/wr-mirror-offsets.ts","../../../projects/lib/overlay/wr-outside-click.ts","../../../projects/lib/overlay/wr-overlay-container.ts","../../../projects/lib/overlay/tokens/wr-overlay.token.ts","../../../projects/lib/overlay/tokens/wr-overlay-container.token.ts","../../../projects/lib/overlay/tokens/wr-responsive-overlays.token.ts","../../../projects/lib/overlay/provide-wr-overlay.ts","../../../projects/lib/overlay/provide-wr-responsive-overlays.ts","../../../projects/lib/overlay/ngwr-overlay.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\n/**\n * The same 24×24 stroked ✕ the rest of the library's chrome draws (alert, toast,\n * lightbox). Static markup — the caller's label goes on via `setAttribute`, so\n * nothing from a catalog or a consumer is ever parsed as HTML.\n */\nconst CLOSE_ICON =\n '<svg class=\"wr-icon__svg\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\"' +\n ' stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">' +\n '<path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" /></svg>';\n\n/**\n * Appends a dismiss (✕) button to a service-opened overlay panel.\n *\n * `WrDialog` and `WrDrawerManager` build their panel around a component the\n * consumer supplies, so there is no template of their own to put a corner button\n * in — they call this instead. Component-driven overlays (`<wr-drawer>`) render\n * the same markup in their template.\n *\n * Uses `host.ownerDocument` rather than the global `document`, so it carries no\n * hidden browser-only assumption; callers still gate on the platform because\n * they have nothing to append to on the server.\n *\n * @param host Panel element the button is appended to (and positioned against).\n * @param className BEM class for the button, e.g. `wr-dialog__close`.\n * @param label Accessible name — already localized by the caller.\n * @param onClose Invoked on click.\n */\nexport function wrAppendOverlayClose(\n host: HTMLElement,\n className: string,\n label: string,\n onClose: () => void\n): HTMLButtonElement {\n const button = host.ownerDocument.createElement('button');\n button.type = 'button';\n button.className = className;\n button.setAttribute('aria-label', label);\n button.innerHTML = CLOSE_ICON;\n button.addEventListener('click', onClose);\n host.appendChild(button);\n return button;\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { ConnectedPosition } from '@angular/cdk/overlay';\n\n/**\n * Mirror the horizontal offsets of a connected-position list for RTL.\n *\n * The CDK mirrors `originX` / `overlayX` under `dir=\"rtl\"` — a `start` anchor\n * resolves to the origin's right edge — but it does NOT mirror `offsetX`, which\n * it adds to the final PHYSICAL x (`_getOffset` returns `position.offsetX`\n * verbatim). So a position that meant \"sit to the inline-start of the trigger,\n * 8px clear of it\" keeps its `-8` under RTL, where the panel is now on the\n * right: the gap becomes an 8px OVERLAP, and the panel sits on top of the thing\n * it is describing.\n *\n * Nothing in the CDK will do this for us, and the amount is per position, so\n * every ngwr overlay that offsets along the inline axis passes its list through\n * here. `offsetY` is untouched: the block axis does not flip.\n *\n * @example\n * ```ts\n * const dir = inject(Directionality, { optional: true });\n *\n * strategy.withPositions(wrMirrorOffsets(WR_POPOVER_POSITIONS[position], dir?.value === 'rtl'));\n * ```\n */\nexport function wrMirrorOffsets(positions: readonly ConnectedPosition[], isRtl: boolean): ConnectedPosition[] {\n // Copied rather than mutated even when there is nothing to do: the position\n // tables are module-level constants shared by every instance, so handing back\n // the same objects invites a caller to write through them.\n if (!isRtl) return positions.map(position => ({ ...position }));\n\n return positions.map(position => ({\n ...position,\n ...(position.offsetX == null ? {} : { offsetX: -position.offsetX }),\n }));\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { OverlayRef } from '@angular/cdk/overlay';\nimport { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { PLATFORM_ID, Service, inject } from '@angular/core';\n\nimport { Observable, type Subscriber } from 'rxjs';\n\n/** One live subscription to `WrOutsideClick.outsidePointerEvents()`. */\ninterface Watcher {\n readonly overlayRef: OverlayRef;\n readonly subscriber: Subscriber<MouseEvent>;\n}\n\n/**\n * Shadow-piercing `event.target` — `composedPath()[0]` is the real originator\n * when the click started inside a shadow root, where `target` is retargeted to\n * the host.\n */\nfunction eventTarget(event: Event): EventTarget | null {\n if (event.composedPath) {\n try {\n return event.composedPath()[0] ?? event.target;\n } catch {\n /* Detached / synthetic events can throw — fall through to `target`. */\n }\n }\n return event.target;\n}\n\n/** `parent.contains(child)`, but stepping out of shadow roots through their hosts. */\nfunction containsAcrossShadow(parent: Node, child: EventTarget | null): boolean {\n const hasShadowRoot = typeof ShadowRoot !== 'undefined';\n let current = child as Node | null;\n while (current) {\n if (current === parent) return true;\n current = hasShadowRoot && current instanceof ShadowRoot ? current.host : current.parentNode;\n }\n return false;\n}\n\n/**\n * Outside-click source for overlays — a drop-in replacement for\n * `OverlayRef.outsidePointerEvents()`.\n *\n * **Why this exists.** CDK's `OverlayOutsideClickDispatcher` binds its\n * listeners to `document.body`. When page content is shorter than the viewport,\n * a click in the empty space below it targets `<html>` — and `<html>` is\n * `body`'s *parent*, so the event bubbles straight to the document and `body`\n * never sees it. The overlay silently stays open. Listening on the document\n * itself catches both cases, and costs nothing else.\n *\n * Semantics are otherwise identical to the CDK's, deliberately:\n *\n * - Emits on `click` / `auxclick` / `contextmenu`, in the capture phase, and\n * tracks `pointerdown` so a `click` is judged by where the press *started*\n * (a drag that ends outside the pane doesn't count as an outside click).\n * - Walks watchers top-most first and stops at the first one that contains the\n * event, so clicking inside a stacked overlay can't close the ones beneath\n * it (a `wr-select` opened inside a `wr-popover`, say).\n * - Fires for the very click that opened the overlay, which is why every caller\n * also ignores events landing on its own trigger.\n *\n * Registration is per subscription: the document listeners go up with the first\n * watcher and come down with the last, so an app with no open overlay has none.\n */\n@Service()\nexport class WrOutsideClick {\n private readonly doc = inject(DOCUMENT);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n /** In attach order — last is top-most. */\n private readonly watchers: Watcher[] = [];\n private cleanup: (() => void) | null = null;\n private pressOrigin: EventTarget | null = null;\n\n /**\n * Emits every `click` / `auxclick` / `contextmenu` that lands outside the\n * given overlay's pane, for as long as the returned observable is subscribed.\n */\n outsidePointerEvents(overlayRef: OverlayRef): Observable<MouseEvent> {\n return new Observable<MouseEvent>(subscriber => {\n if (!this.isBrowser) return;\n\n const watcher: Watcher = { overlayRef, subscriber };\n this.watchers.push(watcher);\n if (this.watchers.length === 1) this.listen();\n\n return () => {\n const index = this.watchers.indexOf(watcher);\n if (index > -1) this.watchers.splice(index, 1);\n if (!this.watchers.length) this.unlisten();\n };\n });\n }\n\n private readonly onPointerDown = (event: Event): void => {\n this.pressOrigin = eventTarget(event);\n };\n\n private readonly onClick = (event: Event): void => {\n const target = eventTarget(event);\n // `detail > 0` means the click really came from a pointer press. A click\n // activated by Enter or Space carries `detail: 0` and has no `pointerdown`\n // of its own — so trusting a leftover origin there judged it by a press\n // that belonged to something else entirely. A press inside the pane that\n // never produced a click (a drag released off-window, a node removed under\n // the finger, a cancelled touch) survives in `pressOrigin`, and the next\n // keyboard activation anywhere on the page then read as \"inside\": the panel\n // would not close, for keyboard users only.\n const fromPointer = event.type === 'click' && (event as MouseEvent).detail > 0;\n const origin = fromPointer && this.pressOrigin ? this.pressOrigin : target;\n this.pressOrigin = null;\n\n // Snapshot: a watcher that closes on this event unsubscribes synchronously,\n // mutating `watchers` while we're walking it.\n const watchers = this.watchers.slice();\n for (let i = watchers.length - 1; i > -1; i--) {\n const { overlayRef, subscriber } = watchers[i];\n if (!overlayRef.hasAttached()) continue;\n\n const pane = overlayRef.overlayElement;\n if (containsAcrossShadow(pane, target) || containsAcrossShadow(pane, origin)) break;\n\n subscriber.next(event as MouseEvent);\n }\n };\n\n private listen(): void {\n const options: AddEventListenerOptions = { capture: true };\n const doc = this.doc;\n\n doc.addEventListener('pointerdown', this.onPointerDown, options);\n doc.addEventListener('click', this.onClick, options);\n doc.addEventListener('auxclick', this.onClick, options);\n doc.addEventListener('contextmenu', this.onClick, options);\n\n this.cleanup = () => {\n doc.removeEventListener('pointerdown', this.onPointerDown, options);\n doc.removeEventListener('click', this.onClick, options);\n doc.removeEventListener('auxclick', this.onClick, options);\n doc.removeEventListener('contextmenu', this.onClick, options);\n };\n }\n\n private unlisten(): void {\n this.cleanup?.();\n this.cleanup = null;\n this.pressOrigin = null;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { OverlayContainer } from '@angular/cdk/overlay';\nimport { Service } from '@angular/core';\n\n/**\n * Custom CDK `OverlayContainer` subclass that tags its DOM element with\n * `.wr-overlay-container` in addition to CDK's own `.cdk-overlay-container`.\n *\n * Why: CDK uses a single overlay container per app — every CDK consumer\n * (this lib, Angular Material, NG-ZORRO, etc.) puts its overlays in the\n * same root element. With this class applied you can scope NGWR-specific\n * overlay styles via the tag, e.g.:\n *\n * ```scss\n * .wr-overlay-container .cdk-overlay-pane { z-index: 1100; }\n * ```\n *\n * It does **not** isolate two libraries that both subclass OverlayContainer\n * — the last provider wins. It does give NGWR overlays a stable CSS hook.\n */\n@Service()\nexport class WrOverlayContainer extends OverlayContainer {\n protected override _createContainer(): void {\n super._createContainer();\n this._containerElement?.classList.add('wr-overlay-container');\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Overlay } from '@angular/cdk/overlay';\nimport { InjectionToken, inject } from '@angular/core';\n\n/**\n * Injection token for the CDK `Overlay` service NGWR components should\n * use to open panels.\n *\n * Defaults to CDK's root `Overlay` (shared with every other CDK consumer\n * in the app). Call `provideWrOverlay()` to bind it to an isolated\n * `Overlay` instance backed by a separate `OverlayContainer` — that way\n * NGWR overlays don't share the same DOM root with other libraries.\n *\n * @internal — directives/services inside the lib inject this instead of\n * CDK's `Overlay` directly.\n */\nexport const WR_OVERLAY = new InjectionToken<Overlay>('WR_OVERLAY', {\n providedIn: 'root',\n factory: () => inject(Overlay),\n});\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { OverlayContainer } from '@angular/cdk/overlay';\nimport { InjectionToken, inject } from '@angular/core';\n\n/**\n * Injection token for the overlay container NGWR components should use.\n *\n * Defaults to CDK's `OverlayContainer` — i.e. the same root element every\n * CDK consumer shares. Call `provideWrOverlay()` to replace it with an\n * isolated instance so NGWR overlays don't collide with other libraries.\n *\n * @internal\n */\nexport const WR_OVERLAY_CONTAINER = new InjectionToken<OverlayContainer>('WR_OVERLAY_CONTAINER', {\n providedIn: 'root',\n factory: () => inject(OverlayContainer),\n});\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\n/** Configuration for responsive (bottom-sheet) overlay presentation. */\ninterface WrResponsiveOverlaysConfig {\n /**\n * Viewport width (CSS px) at or below which overlays present as a\n * bottom-sheet instead of a floating panel. @default 640\n */\n readonly breakpoint: number;\n}\n\n/**\n * When set, overlay services (dialog, select, dropdown, …) present as a\n * slide-up bottom-sheet on viewports at or below `breakpoint`. `null`\n * (the default) keeps every overlay floating. Configure via\n * {@link provideWrResponsiveOverlays}; override per call with a\n * component's `responsive` option.\n *\n * @internal\n */\nconst WR_RESPONSIVE_OVERLAYS = new InjectionToken<WrResponsiveOverlaysConfig | null>('WR_RESPONSIVE_OVERLAYS', {\n factory: () => null,\n});\n\n/**\n * Decide whether an overlay should present as a bottom-sheet for the\n * current viewport.\n *\n * - `responsive === true` — sheet on small viewports regardless of config.\n * - `responsive === false` — never a sheet.\n * - `responsive === undefined` — follow the global config (sheet when\n * {@link provideWrResponsiveOverlays} is configured).\n *\n * SSR-safe: returns `false` when there's no `window`.\n *\n * @internal\n */\nfunction wrPresentAsSheet(responsive: boolean | undefined, config: WrResponsiveOverlaysConfig | null): boolean {\n const enabled = responsive ?? config !== null;\n if (!enabled || typeof window === 'undefined') return false;\n return window.innerWidth <= (config?.breakpoint ?? 640);\n}\n\nexport { WR_RESPONSIVE_OVERLAYS, wrPresentAsSheet };\nexport type { WrResponsiveOverlaysConfig };\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Overlay, OverlayContainer } from '@angular/cdk/overlay';\nimport {\n EnvironmentInjector,\n Injector,\n type EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideEnvironmentInitializer,\n} from '@angular/core';\n\nimport { WrVisualViewport } from 'ngwr/platform';\n\nimport { WR_OVERLAY, WR_OVERLAY_CONTAINER } from './tokens';\nimport { WrOverlayContainer } from './wr-overlay-container';\n\n/**\n * Isolates NGWR overlays from other CDK consumers in the app.\n *\n * Without this call, NGWR overlays use CDK's root `Overlay` — they\n * render into the same `.cdk-overlay-container` element as Angular\n * Material, NG-ZORRO, etc. With it, NGWR gets its own `OverlayContainer`\n * (marked `.wr-overlay-container`) **and** its own `Overlay` service\n * instance — so styles, z-index, and lifecycle never collide.\n *\n * It does this by replacing two injection tokens:\n * - `WR_OVERLAY_CONTAINER` → custom `WrOverlayContainer` subclass\n * - `WR_OVERLAY` → a fresh `Overlay` instance built in a child injector\n * that resolves `OverlayContainer` to the custom subclass\n *\n * CDK's own root `Overlay` / `OverlayContainer` are left untouched, so\n * other libraries keep working with their original container.\n *\n * @example\n * ```ts\n * import { provideWrOverlay } from 'ngwr/overlay';\n *\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideWrOverlay(),\n * ],\n * });\n * ```\n */\nexport function provideWrOverlay(): EnvironmentProviders {\n return makeEnvironmentProviders([\n // Track the visual viewport app-wide so overlay sheets (and the\n // command-palette) can lift above the on-screen keyboard. No-op on the\n // server / in browsers without the API.\n provideEnvironmentInitializer(() => void inject(WrVisualViewport)),\n { provide: WR_OVERLAY_CONTAINER, useClass: WrOverlayContainer },\n {\n provide: WR_OVERLAY,\n useFactory: (): Overlay => {\n const parent = inject(EnvironmentInjector);\n const container = inject(WR_OVERLAY_CONTAINER);\n\n // Child injector resolves `OverlayContainer` to our subclass and\n // creates a fresh `Overlay` instance bound to it. CDK's own root\n // `Overlay` and `OverlayContainer` are untouched in the parent.\n const child = Injector.create({\n parent,\n providers: [\n { provide: OverlayContainer, useValue: container },\n { provide: Overlay, useClass: Overlay },\n ],\n });\n\n return child.get(Overlay);\n },\n },\n ]);\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\nimport { WR_RESPONSIVE_OVERLAYS, type WrResponsiveOverlaysConfig } from './tokens/wr-responsive-overlays.token';\n\n/**\n * Opt every NGWR overlay into responsive presentation — on viewports at or\n * below `breakpoint`, dialogs / selects / dropdowns and friends slide up as\n * a full-width bottom-sheet instead of a floating panel. Built on the same\n * overlay plumbing, so focus-trap, backdrop and scroll-blocking carry over.\n *\n * Individual components can still opt out (or in) per instance via their\n * `responsive` option.\n *\n * @example\n * ```ts\n * bootstrapApplication(AppComponent, {\n * providers: [provideWrOverlay(), provideWrResponsiveOverlays()],\n * });\n *\n * // Custom breakpoint:\n * provideWrResponsiveOverlays({ breakpoint: 768 });\n * ```\n */\nexport function provideWrResponsiveOverlays(config?: Partial<WrResponsiveOverlaysConfig>): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: WR_RESPONSIVE_OVERLAYS, useValue: { breakpoint: config?.breakpoint ?? 640 } },\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-overlay.mjs","sources":["../../../projects/lib/overlay/wr-overlay-close.ts","../../../projects/lib/overlay/wr-mirror-offsets.ts","../../../projects/lib/overlay/wr-outside-click.ts","../../../projects/lib/overlay/wr-overlay-container.ts","../../../projects/lib/overlay/tokens/wr-overlay.token.ts","../../../projects/lib/overlay/tokens/wr-overlay-container.token.ts","../../../projects/lib/overlay/tokens/wr-responsive-overlays.token.ts","../../../projects/lib/overlay/provide-wr-overlay.ts","../../../projects/lib/overlay/provide-wr-responsive-overlays.ts","../../../projects/lib/overlay/ngwr-overlay.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\n/**\n * The same 24×24 stroked ✕ the rest of the library's chrome draws (alert, toast,\n * lightbox). Static markup — the caller's label goes on via `setAttribute`, so\n * nothing from a catalog or a consumer is ever parsed as HTML.\n */\nconst CLOSE_ICON =\n '<svg class=\"wr-icon__svg\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\"' +\n ' stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">' +\n '<path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" /></svg>';\n\n/**\n * Appends a dismiss (✕) button to a service-opened overlay panel.\n *\n * `WrDialog` and `WrDrawerManager` build their panel around a component the\n * consumer supplies, so there is no template of their own to put a corner button\n * in — they call this instead. Component-driven overlays (`<wr-drawer>`) render\n * the same markup in their template.\n *\n * Uses `host.ownerDocument` rather than the global `document`, so it carries no\n * hidden browser-only assumption; callers still gate on the platform because\n * they have nothing to append to on the server.\n *\n * @param host Panel element the button is appended to (and positioned against).\n * @param className BEM class for the button, e.g. `wr-dialog__close`.\n * @param label Accessible name — already localized by the caller.\n * @param onClose Invoked on click.\n */\nexport function wrAppendOverlayClose(\n host: HTMLElement,\n className: string,\n label: string,\n onClose: () => void\n): HTMLButtonElement {\n const button = host.ownerDocument.createElement('button');\n button.type = 'button';\n button.className = className;\n button.setAttribute('aria-label', label);\n button.innerHTML = CLOSE_ICON;\n button.addEventListener('click', onClose);\n host.appendChild(button);\n return button;\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { ConnectedPosition } from '@angular/cdk/overlay';\n\n/**\n * Mirror the horizontal offsets of a connected-position list for RTL.\n *\n * The CDK mirrors `originX` / `overlayX` under `dir=\"rtl\"` — a `start` anchor\n * resolves to the origin's right edge — but it does NOT mirror `offsetX`, which\n * it adds to the final PHYSICAL x (`_getOffset` returns `position.offsetX`\n * verbatim). So a position that meant \"sit to the inline-start of the trigger,\n * 8px clear of it\" keeps its `-8` under RTL, where the panel is now on the\n * right: the gap becomes an 8px OVERLAP, and the panel sits on top of the thing\n * it is describing.\n *\n * Nothing in the CDK will do this for us, and the amount is per position, so\n * every ngwr overlay that offsets along the inline axis passes its list through\n * here. `offsetY` is untouched: the block axis does not flip.\n *\n * @example\n * ```ts\n * const dir = inject(Directionality, { optional: true });\n *\n * strategy.withPositions(wrMirrorOffsets(WR_POPOVER_POSITIONS[position], dir?.value === 'rtl'));\n * ```\n */\nexport function wrMirrorOffsets(positions: readonly ConnectedPosition[], isRtl: boolean): ConnectedPosition[] {\n // Copied rather than mutated even when there is nothing to do: the position\n // tables are module-level constants shared by every instance, so handing back\n // the same objects invites a caller to write through them.\n if (!isRtl) return positions.map(position => ({ ...position }));\n\n return positions.map(position => ({\n ...position,\n ...(position.offsetX == null ? {} : { offsetX: -position.offsetX }),\n }));\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { OverlayRef } from '@angular/cdk/overlay';\nimport { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { PLATFORM_ID, Service, inject } from '@angular/core';\n\nimport { Observable, type Subscriber } from 'rxjs';\n\n/** One live subscription to `WrOutsideClick.outsidePointerEvents()`. */\ninterface Watcher {\n readonly overlayRef: OverlayRef;\n readonly subscriber: Subscriber<MouseEvent>;\n}\n\n/**\n * Shadow-piercing `event.target` — `composedPath()[0]` is the real originator\n * when the click started inside a shadow root, where `target` is retargeted to\n * the host.\n */\nfunction eventTarget(event: Event): EventTarget | null {\n if (event.composedPath) {\n try {\n return event.composedPath()[0] ?? event.target;\n } catch {\n /* Detached / synthetic events can throw — fall through to `target`. */\n }\n }\n return event.target;\n}\n\n/** `parent.contains(child)`, but stepping out of shadow roots through their hosts. */\nfunction containsAcrossShadow(parent: Node, child: EventTarget | null): boolean {\n const hasShadowRoot = typeof ShadowRoot !== 'undefined';\n let current = child as Node | null;\n while (current) {\n if (current === parent) return true;\n current = hasShadowRoot && current instanceof ShadowRoot ? current.host : current.parentNode;\n }\n return false;\n}\n\n/**\n * Outside-click source for overlays — a drop-in replacement for\n * `OverlayRef.outsidePointerEvents()`.\n *\n * **Why this exists.** CDK's `OverlayOutsideClickDispatcher` binds its\n * listeners to `document.body`. When page content is shorter than the viewport,\n * a click in the empty space below it targets `<html>` — and `<html>` is\n * `body`'s *parent*, so the event bubbles straight to the document and `body`\n * never sees it. The overlay silently stays open. Listening on the document\n * itself catches both cases, and costs nothing else.\n *\n * Semantics are otherwise identical to the CDK's, deliberately:\n *\n * - Emits on `click` / `auxclick` / `contextmenu`, in the capture phase, and\n * tracks `pointerdown` so a `click` is judged by where the press *started*\n * (a drag that ends outside the pane doesn't count as an outside click).\n * - Walks watchers top-most first and stops at the first one that contains the\n * event, so clicking inside a stacked overlay can't close the ones beneath\n * it (a `wr-select` opened inside a `wr-popover`, say).\n * - Fires for the very click that opened the overlay, which is why every caller\n * also ignores events landing on its own trigger.\n *\n * Registration is per subscription: the document listeners go up with the first\n * watcher and come down with the last, so an app with no open overlay has none.\n * That sentence was aspirational until v11 — see {@link outsidePointerEvents}.\n */\n@Service()\nexport class WrOutsideClick {\n private readonly doc = inject(DOCUMENT);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n /** In attach order — last is top-most. */\n private readonly watchers: Watcher[] = [];\n private cleanup: (() => void) | null = null;\n private pressOrigin: EventTarget | null = null;\n\n /**\n * Emits every `click` / `auxclick` / `contextmenu` that lands outside the\n * given overlay's pane, for as long as the overlay is attached.\n *\n * **It completes when the overlay detaches, and that is load-bearing.** Every\n * one of the ten call sites subscribes inside its own `openOverlay()` and\n * pipes `takeUntilDestroyed(destroyRef)` — a DIRECTIVE lifetime, not an\n * overlay one — and none of them unsubscribes on close, because the natural\n * reading of that idiom is \"this ends with the component\". Without the\n * completion below the teardown never ran: one watcher accumulated per OPEN\n * for the life of the page, each pinning a disposed `OverlayRef` (and through\n * it a detached pane, its portal outlet and their injectors), the four\n * capture-phase document listeners could never come down because\n * `watchers.length` never reached zero, and every click on the page walked a\n * list that only grew. A `mode=\"tooltip\"` popover made it unbounded — one\n * permanent watcher per hover.\n *\n * Completing here rather than fixing ten call sites is deliberate: the next\n * component to open an overlay would have made the same reasonable mistake.\n * It is also what CDK's own `OverlayRef.outsidePointerEvents()` does.\n */\n outsidePointerEvents(overlayRef: OverlayRef): Observable<MouseEvent> {\n return new Observable<MouseEvent>(subscriber => {\n if (!this.isBrowser) return;\n\n const watcher: Watcher = { overlayRef, subscriber };\n this.watchers.push(watcher);\n if (this.watchers.length === 1) this.listen();\n\n // BOTH handlers, and the second one is not defensive padding. `dispose()`\n // emits a detachment and then completes the subject, so a live overlay\n // arrives through `next` — but a ref that was already disposed before\n // anyone subscribed has nothing left to emit and arrives through\n // `complete` alone. Listening for `next` only left exactly the watcher\n // this whole change exists to remove.\n const ended = (): void => subscriber.complete();\n const detached = overlayRef.detachments().subscribe({ next: ended, complete: ended });\n\n return () => {\n detached.unsubscribe();\n const index = this.watchers.indexOf(watcher);\n if (index > -1) this.watchers.splice(index, 1);\n if (!this.watchers.length) this.unlisten();\n };\n });\n }\n\n private readonly onPointerDown = (event: Event): void => {\n this.pressOrigin = eventTarget(event);\n };\n\n private readonly onClick = (event: Event): void => {\n const target = eventTarget(event);\n // `detail > 0` means the click really came from a pointer press. A click\n // activated by Enter or Space carries `detail: 0` and has no `pointerdown`\n // of its own — so trusting a leftover origin there judged it by a press\n // that belonged to something else entirely. A press inside the pane that\n // never produced a click (a drag released off-window, a node removed under\n // the finger, a cancelled touch) survives in `pressOrigin`, and the next\n // keyboard activation anywhere on the page then read as \"inside\": the panel\n // would not close, for keyboard users only.\n const fromPointer = event.type === 'click' && (event as MouseEvent).detail > 0;\n const origin = fromPointer && this.pressOrigin ? this.pressOrigin : target;\n this.pressOrigin = null;\n\n // Snapshot: a watcher that closes on this event unsubscribes synchronously,\n // mutating `watchers` while we're walking it.\n const watchers = this.watchers.slice();\n for (let i = watchers.length - 1; i > -1; i--) {\n const { overlayRef, subscriber } = watchers[i];\n if (!overlayRef.hasAttached()) continue;\n\n const pane = overlayRef.overlayElement;\n if (containsAcrossShadow(pane, target) || containsAcrossShadow(pane, origin)) break;\n\n subscriber.next(event as MouseEvent);\n }\n };\n\n private listen(): void {\n const options: AddEventListenerOptions = { capture: true };\n const doc = this.doc;\n\n doc.addEventListener('pointerdown', this.onPointerDown, options);\n doc.addEventListener('click', this.onClick, options);\n doc.addEventListener('auxclick', this.onClick, options);\n doc.addEventListener('contextmenu', this.onClick, options);\n\n this.cleanup = () => {\n doc.removeEventListener('pointerdown', this.onPointerDown, options);\n doc.removeEventListener('click', this.onClick, options);\n doc.removeEventListener('auxclick', this.onClick, options);\n doc.removeEventListener('contextmenu', this.onClick, options);\n };\n }\n\n private unlisten(): void {\n this.cleanup?.();\n this.cleanup = null;\n this.pressOrigin = null;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { OverlayContainer } from '@angular/cdk/overlay';\nimport { Service } from '@angular/core';\n\n/**\n * Custom CDK `OverlayContainer` subclass that tags its DOM element with\n * `.wr-overlay-container` in addition to CDK's own `.cdk-overlay-container`.\n *\n * Why: CDK uses a single overlay container per app — every CDK consumer\n * (this lib, Angular Material, NG-ZORRO, etc.) puts its overlays in the\n * same root element. With this class applied you can scope NGWR-specific\n * overlay styles via the tag, e.g.:\n *\n * ```scss\n * .wr-overlay-container .cdk-overlay-pane { z-index: 1100; }\n * ```\n *\n * It does **not** isolate two libraries that both subclass OverlayContainer\n * — the last provider wins. It does give NGWR overlays a stable CSS hook.\n */\n@Service()\nexport class WrOverlayContainer extends OverlayContainer {\n protected override _createContainer(): void {\n super._createContainer();\n this._containerElement?.classList.add('wr-overlay-container');\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Overlay } from '@angular/cdk/overlay';\nimport { InjectionToken, inject } from '@angular/core';\n\n/**\n * Injection token for the CDK `Overlay` service NGWR components should\n * use to open panels.\n *\n * Defaults to CDK's root `Overlay` (shared with every other CDK consumer\n * in the app). Call `provideWrOverlay()` to bind it to an isolated\n * `Overlay` instance backed by a separate `OverlayContainer` — that way\n * NGWR overlays don't share the same DOM root with other libraries.\n *\n * @internal — directives/services inside the lib inject this instead of\n * CDK's `Overlay` directly.\n */\nexport const WR_OVERLAY = new InjectionToken<Overlay>('WR_OVERLAY', {\n providedIn: 'root',\n factory: () => inject(Overlay),\n});\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { OverlayContainer } from '@angular/cdk/overlay';\nimport { InjectionToken, inject } from '@angular/core';\n\n/**\n * Injection token for the overlay container NGWR components should use.\n *\n * Defaults to CDK's `OverlayContainer` — i.e. the same root element every\n * CDK consumer shares. Call `provideWrOverlay()` to replace it with an\n * isolated instance so NGWR overlays don't collide with other libraries.\n *\n * @internal\n */\nexport const WR_OVERLAY_CONTAINER = new InjectionToken<OverlayContainer>('WR_OVERLAY_CONTAINER', {\n providedIn: 'root',\n factory: () => inject(OverlayContainer),\n});\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\n/** Configuration for responsive (bottom-sheet) overlay presentation. */\ninterface WrResponsiveOverlaysConfig {\n /**\n * Viewport width (CSS px) at or below which overlays present as a\n * bottom-sheet instead of a floating panel. @default 640\n */\n readonly breakpoint: number;\n}\n\n/**\n * When set, overlay services (dialog, select, dropdown, …) present as a\n * slide-up bottom-sheet on viewports at or below `breakpoint`. `null`\n * (the default) keeps every overlay floating. Configure via\n * {@link provideWrResponsiveOverlays}; override per call with a\n * component's `responsive` option.\n *\n * @internal\n */\nconst WR_RESPONSIVE_OVERLAYS = new InjectionToken<WrResponsiveOverlaysConfig | null>('WR_RESPONSIVE_OVERLAYS', {\n factory: () => null,\n});\n\n/**\n * Decide whether an overlay should present as a bottom-sheet for the\n * current viewport.\n *\n * - `responsive === true` — sheet on small viewports regardless of config.\n * - `responsive === false` — never a sheet.\n * - `responsive === undefined` — follow the global config (sheet when\n * {@link provideWrResponsiveOverlays} is configured).\n *\n * SSR-safe: returns `false` when there's no `window`.\n *\n * @internal\n */\nfunction wrPresentAsSheet(responsive: boolean | undefined, config: WrResponsiveOverlaysConfig | null): boolean {\n const enabled = responsive ?? config !== null;\n if (!enabled || typeof window === 'undefined') return false;\n return window.innerWidth <= (config?.breakpoint ?? 640);\n}\n\nexport { WR_RESPONSIVE_OVERLAYS, wrPresentAsSheet };\nexport type { WrResponsiveOverlaysConfig };\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Overlay, OverlayContainer } from '@angular/cdk/overlay';\nimport {\n EnvironmentInjector,\n Injector,\n type EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideEnvironmentInitializer,\n} from '@angular/core';\n\nimport { WrVisualViewport } from 'ngwr/platform';\n\nimport { WR_OVERLAY, WR_OVERLAY_CONTAINER } from './tokens';\nimport { WrOverlayContainer } from './wr-overlay-container';\n\n/**\n * Isolates NGWR overlays from other CDK consumers in the app.\n *\n * Without this call, NGWR overlays use CDK's root `Overlay` — they\n * render into the same `.cdk-overlay-container` element as Angular\n * Material, NG-ZORRO, etc. With it, NGWR gets its own `OverlayContainer`\n * (marked `.wr-overlay-container`) **and** its own `Overlay` service\n * instance — so styles, z-index, and lifecycle never collide.\n *\n * It does this by replacing two injection tokens:\n * - `WR_OVERLAY_CONTAINER` → custom `WrOverlayContainer` subclass\n * - `WR_OVERLAY` → a fresh `Overlay` instance built in a child injector\n * that resolves `OverlayContainer` to the custom subclass\n *\n * CDK's own root `Overlay` / `OverlayContainer` are left untouched, so\n * other libraries keep working with their original container.\n *\n * @example\n * ```ts\n * import { provideWrOverlay } from 'ngwr/overlay';\n *\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideWrOverlay(),\n * ],\n * });\n * ```\n */\nexport function provideWrOverlay(): EnvironmentProviders {\n return makeEnvironmentProviders([\n // Track the visual viewport app-wide so overlay sheets (and the\n // command-palette) can lift above the on-screen keyboard. No-op on the\n // server / in browsers without the API.\n provideEnvironmentInitializer(() => void inject(WrVisualViewport)),\n { provide: WR_OVERLAY_CONTAINER, useClass: WrOverlayContainer },\n {\n provide: WR_OVERLAY,\n useFactory: (): Overlay => {\n const parent = inject(EnvironmentInjector);\n const container = inject(WR_OVERLAY_CONTAINER);\n\n // Child injector resolves `OverlayContainer` to our subclass and\n // creates a fresh `Overlay` instance bound to it. CDK's own root\n // `Overlay` and `OverlayContainer` are untouched in the parent.\n const child = Injector.create({\n parent,\n providers: [\n { provide: OverlayContainer, useValue: container },\n { provide: Overlay, useClass: Overlay },\n ],\n });\n\n return child.get(Overlay);\n },\n },\n ]);\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\nimport { WR_RESPONSIVE_OVERLAYS, type WrResponsiveOverlaysConfig } from './tokens/wr-responsive-overlays.token';\n\n/**\n * Opt every NGWR overlay into responsive presentation — on viewports at or\n * below `breakpoint`, dialogs / selects / dropdowns and friends slide up as\n * a full-width bottom-sheet instead of a floating panel. Built on the same\n * overlay plumbing, so focus-trap, backdrop and scroll-blocking carry over.\n *\n * Individual components can still opt out (or in) per instance via their\n * `responsive` option.\n *\n * @example\n * ```ts\n * bootstrapApplication(AppComponent, {\n * providers: [provideWrOverlay(), provideWrResponsiveOverlays()],\n * });\n *\n * // Custom breakpoint:\n * provideWrResponsiveOverlays({ breakpoint: 768 });\n * ```\n */\nexport function provideWrResponsiveOverlays(config?: Partial<WrResponsiveOverlaysConfig>): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: WR_RESPONSIVE_OVERLAYS, useValue: { breakpoint: config?.breakpoint ?? 640 } },\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;AAAA;;;;;AAKG;AAEH;;;;AAIG;AACH,MAAM,UAAU,GACd,8FAA8F;IAC9F,4GAA4G;AAC5G,IAAA,sDAAsD;AAExD;;;;;;;;;;;;;;;;AAgBG;AACG,SAAU,oBAAoB,CAClC,IAAiB,EACjB,SAAiB,EACjB,KAAa,EACb,OAAmB,EAAA;IAEnB,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,QAAQ,CAAC;AACzD,IAAA,MAAM,CAAC,IAAI,GAAG,QAAQ;AACtB,IAAA,MAAM,CAAC,SAAS,GAAG,SAAS;AAC5B,IAAA,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,KAAK,CAAC;AACxC,IAAA,MAAM,CAAC,SAAS,GAAG,UAAU;AAC7B,IAAA,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC;AACzC,IAAA,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;AACxB,IAAA,OAAO,MAAM;AACf;;AChDA;;;;;AAKG;AAIH;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,eAAe,CAAC,SAAuC,EAAE,KAAc,EAAA;;;;AAIrF,IAAA,IAAI,CAAC,KAAK;AAAE,QAAA,OAAO,SAAS,CAAC,GAAG,CAAC,QAAQ,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;IAE/D,OAAO,SAAS,CAAC,GAAG,CAAC,QAAQ,KAAK;AAChC,QAAA,GAAG,QAAQ;QACX,IAAI,QAAQ,CAAC,OAAO,IAAI,IAAI,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;AACpE,KAAA,CAAC,CAAC;AACL;;ACtBA;;;;AAIG;AACH,SAAS,WAAW,CAAC,KAAY,EAAA;AAC/B,IAAA,IAAI,KAAK,CAAC,YAAY,EAAE;AACtB,QAAA,IAAI;YACF,OAAO,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM;QAChD;AAAE,QAAA,MAAM;;QAER;IACF;IACA,OAAO,KAAK,CAAC,MAAM;AACrB;AAEA;AACA,SAAS,oBAAoB,CAAC,MAAY,EAAE,KAAyB,EAAA;AACnE,IAAA,MAAM,aAAa,GAAG,OAAO,UAAU,KAAK,WAAW;IACvD,IAAI,OAAO,GAAG,KAAoB;IAClC,OAAO,OAAO,EAAE;QACd,IAAI,OAAO,KAAK,MAAM;AAAE,YAAA,OAAO,IAAI;AACnC,QAAA,OAAO,GAAG,aAAa,IAAI,OAAO,YAAY,UAAU,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,UAAU;IAC9F;AACA,IAAA,OAAO,KAAK;AACd;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;MAEU,cAAc,CAAA;AACR,IAAA,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC;IACtB,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;;IAGlD,QAAQ,GAAc,EAAE;IACjC,OAAO,GAAwB,IAAI;IACnC,WAAW,GAAuB,IAAI;AAE9C;;;;;;;;;;;;;;;;;;;;AAoBG;AACH,IAAA,oBAAoB,CAAC,UAAsB,EAAA;AACzC,QAAA,OAAO,IAAI,UAAU,CAAa,UAAU,IAAG;YAC7C,IAAI,CAAC,IAAI,CAAC,SAAS;gBAAE;AAErB,YAAA,MAAM,OAAO,GAAY,EAAE,UAAU,EAAE,UAAU,EAAE;AACnD,YAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC;AAC3B,YAAA,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,IAAI,CAAC,MAAM,EAAE;;;;;;;YAQ7C,MAAM,KAAK,GAAG,MAAY,UAAU,CAAC,QAAQ,EAAE;AAC/C,YAAA,MAAM,QAAQ,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAErF,YAAA,OAAO,MAAK;gBACV,QAAQ,CAAC,WAAW,EAAE;gBACtB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC;gBAC5C,IAAI,KAAK,GAAG,CAAC,CAAC;oBAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;AAC9C,gBAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM;oBAAE,IAAI,CAAC,QAAQ,EAAE;AAC5C,YAAA,CAAC;AACH,QAAA,CAAC,CAAC;IACJ;AAEiB,IAAA,aAAa,GAAG,CAAC,KAAY,KAAU;AACtD,QAAA,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC;AACvC,IAAA,CAAC;AAEgB,IAAA,OAAO,GAAG,CAAC,KAAY,KAAU;AAChD,QAAA,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC;;;;;;;;;AASjC,QAAA,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,KAAK,OAAO,IAAK,KAAoB,CAAC,MAAM,GAAG,CAAC;AAC9E,QAAA,MAAM,MAAM,GAAG,WAAW,IAAI,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,GAAG,MAAM;AAC1E,QAAA,IAAI,CAAC,WAAW,GAAG,IAAI;;;QAIvB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE;AACtC,QAAA,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC7C,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC;AAC9C,YAAA,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE;gBAAE;AAE/B,YAAA,MAAM,IAAI,GAAG,UAAU,CAAC,cAAc;AACtC,YAAA,IAAI,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC;gBAAE;AAE9E,YAAA,UAAU,CAAC,IAAI,CAAC,KAAmB,CAAC;QACtC;AACF,IAAA,CAAC;IAEO,MAAM,GAAA;AACZ,QAAA,MAAM,OAAO,GAA4B,EAAE,OAAO,EAAE,IAAI,EAAE;AAC1D,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG;QAEpB,GAAG,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC;QAChE,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC;QACpD,GAAG,CAAC,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC;QACvD,GAAG,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC;AAE1D,QAAA,IAAI,CAAC,OAAO,GAAG,MAAK;YAClB,GAAG,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC;YACnE,GAAG,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC;YACvD,GAAG,CAAC,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC;YAC1D,GAAG,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC;AAC/D,QAAA,CAAC;IACH;IAEQ,QAAQ,GAAA;AACd,QAAA,IAAI,CAAC,OAAO,IAAI;AAChB,QAAA,IAAI,CAAC,OAAO,GAAG,IAAI;AACnB,QAAA,IAAI,CAAC,WAAW,GAAG,IAAI;IACzB;uGA7GW,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,OAAA,EAAA,CAAA;wGAAd,cAAc,EAAA,CAAA;;2FAAd,cAAc,EAAA,UAAA,EAAA,CAAA;kBAD1B;;;ACxED;;;;;AAKG;AAKH;;;;;;;;;;;;;;;AAeG;AAEG,MAAO,kBAAmB,SAAQ,gBAAgB,CAAA;IACnC,gBAAgB,GAAA;QACjC,KAAK,CAAC,gBAAgB,EAAE;QACxB,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,GAAG,CAAC,sBAAsB,CAAC;IAC/D;uGAJW,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,OAAA,EAAA,CAAA;wGAAlB,kBAAkB,EAAA,CAAA;;2FAAlB,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAD9B;;;AC1BD;;;;;AAKG;AAKH;;;;;;;;;;;AAWG;MACU,UAAU,GAAG,IAAI,cAAc,CAAU,YAAY,EAAE;AAClE,IAAA,UAAU,EAAE,MAAM;AAClB,IAAA,OAAO,EAAE,MAAM,MAAM,CAAC,OAAO,CAAC;AAC/B,CAAA;;ACzBD;;;;;AAKG;AAKH;;;;;;;;AAQG;MACU,oBAAoB,GAAG,IAAI,cAAc,CAAmB,sBAAsB,EAAE;AAC/F,IAAA,UAAU,EAAE,MAAM;AAClB,IAAA,OAAO,EAAE,MAAM,MAAM,CAAC,gBAAgB,CAAC;AACxC,CAAA;;ACtBD;;;;;AAKG;AAaH;;;;;;;;AAQG;AACH,MAAM,sBAAsB,GAAG,IAAI,cAAc,CAAoC,wBAAwB,EAAE;AAC7G,IAAA,OAAO,EAAE,MAAM,IAAI;AACpB,CAAA;AAED;;;;;;;;;;;;AAYG;AACH,SAAS,gBAAgB,CAAC,UAA+B,EAAE,MAAyC,EAAA;AAClG,IAAA,MAAM,OAAO,GAAG,UAAU,IAAI,MAAM,KAAK,IAAI;AAC7C,IAAA,IAAI,CAAC,OAAO,IAAI,OAAO,MAAM,KAAK,WAAW;AAAE,QAAA,OAAO,KAAK;IAC3D,OAAO,MAAM,CAAC,UAAU,KAAK,MAAM,EAAE,UAAU,IAAI,GAAG,CAAC;AACzD;;AChDA;;;;;AAKG;AAiBH;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BG;SACa,gBAAgB,GAAA;AAC9B,IAAA,OAAO,wBAAwB,CAAC;;;;QAI9B,6BAA6B,CAAC,MAAM,KAAK,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAClE,QAAA,EAAE,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,kBAAkB,EAAE;AAC/D,QAAA;AACE,YAAA,OAAO,EAAE,UAAU;YACnB,UAAU,EAAE,MAAc;AACxB,gBAAA,MAAM,MAAM,GAAG,MAAM,CAAC,mBAAmB,CAAC;AAC1C,gBAAA,MAAM,SAAS,GAAG,MAAM,CAAC,oBAAoB,CAAC;;;;AAK9C,gBAAA,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;oBAC5B,MAAM;AACN,oBAAA,SAAS,EAAE;AACT,wBAAA,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE;AAClD,wBAAA,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE;AACxC,qBAAA;AACF,iBAAA,CAAC;AAEF,gBAAA,OAAO,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;YAC3B,CAAC;AACF,SAAA;AACF,KAAA,CAAC;AACJ;;AC9EA;;;;;AAKG;AAMH;;;;;;;;;;;;;;;;;;AAkBG;AACG,SAAU,2BAA2B,CAAC,MAA4C,EAAA;AACtF,IAAA,OAAO,wBAAwB,CAAC;AAC9B,QAAA,EAAE,OAAO,EAAE,sBAAsB,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,IAAI,GAAG,EAAE,EAAE;AACzF,KAAA,CAAC;AACJ;;AClCA;;AAEG;;;;"}
|
|
@@ -367,8 +367,22 @@ class WrPopover {
|
|
|
367
367
|
this.overlayRef.overlayElement.removeEventListener('mouseleave', this.onOverlayLeave);
|
|
368
368
|
}
|
|
369
369
|
this.clearHoverClose();
|
|
370
|
+
// Take the focus back before the pane goes, and only if it was in there.
|
|
371
|
+
//
|
|
372
|
+
// A popover is not a tooltip: it attaches a `TemplatePortal` under
|
|
373
|
+
// `role="dialog"`, so a form or a menu inside it is the point, and Escape
|
|
374
|
+
// arrives through the overlay's `keydownEvents()` no matter where the caret
|
|
375
|
+
// is. Disposing with focus inside dropped it on `<body>`, and the next Tab
|
|
376
|
+
// restarted from the top of the document. `wr-date-picker` and
|
|
377
|
+
// `wr-popconfirm` already guard it exactly this way; leaving focus alone
|
|
378
|
+
// when it had already moved is what stops the close from fighting the user
|
|
379
|
+
// for the caret.
|
|
380
|
+
const pane = this.overlayRef.overlayElement;
|
|
381
|
+
const focusWasInside = pane.contains(this.host.nativeElement.ownerDocument.activeElement);
|
|
370
382
|
this.overlayRef.dispose();
|
|
371
383
|
this.overlayRef = null;
|
|
384
|
+
if (focusWasInside)
|
|
385
|
+
this.host.nativeElement.focus();
|
|
372
386
|
this.closed.emit();
|
|
373
387
|
}
|
|
374
388
|
onOverlayLeave = (event) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-popover.mjs","sources":["../../../projects/lib/popover/interfaces/popover-position.ts","../../../projects/lib/popover/internal/text-panel.ts","../../../projects/lib/popover/popover.ts","../../../projects/lib/popover/ngwr-popover.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 type { ConnectedPosition } from '@angular/cdk/overlay';\n\nexport type WrPopoverPosition =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'left'\n | 'left-start'\n | 'left-end'\n | 'right'\n | 'right-start'\n | 'right-end';\n\n/** @internal */\nexport const WR_POPOVER_POSITIONS: Record<WrPopoverPosition, ConnectedPosition[]> = {\n top: [{ originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -8 }],\n 'top-start': [{ originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -8 }],\n 'top-end': [{ originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -8 }],\n bottom: [{ originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 8 }],\n 'bottom-start': [{ originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 8 }],\n 'bottom-end': [{ originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 8 }],\n left: [{ originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -8 }],\n 'left-start': [{ originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'top', offsetX: -8 }],\n 'left-end': [{ originX: 'start', originY: 'bottom', overlayX: 'end', overlayY: 'bottom', offsetX: -8 }],\n right: [{ originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: 8 }],\n 'right-start': [{ originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top', offsetX: 8 }],\n 'right-end': [{ originX: 'end', originY: 'bottom', overlayX: 'start', overlayY: 'bottom', offsetX: 8 }],\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 { Component, ViewEncapsulation, input } from '@angular/core';\n\n/**\n * Internal floating label rendered when `<… [wrPopover]=\"'…'\" mode=\"tooltip\">`\n * receives a string instead of a template. Not part of the public template\n * API — use the `WrPopover` directive in tooltip mode instead.\n *\n * @internal\n */\n@Component({\n selector: 'wr-popover-text',\n template: '{{ text() }}',\n encapsulation: ViewEncapsulation.None,\n // No `role=\"tooltip\"` here on purpose: the overlay pane this is attached to\n // is the element `aria-describedby` resolves to, and it carries the role.\n // Setting it here too nested one tooltip inside another.\n host: { class: 'wr-tooltip' },\n})\nexport class WrPopoverTextPanel {\n readonly text = input.required<string>();\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 { Directionality } from '@angular/cdk/bidi';\nimport { type BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { type OverlayRef, ScrollStrategyOptions } from '@angular/cdk/overlay';\nimport { ComponentPortal, TemplatePortal } from '@angular/cdk/portal';\nimport type { TemplateRef } from '@angular/core';\nimport {\n DestroyRef,\n Directive,\n ElementRef,\n ViewContainerRef,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { WR_OVERLAY, WR_RESPONSIVE_OVERLAYS, WrOutsideClick, wrMirrorOffsets, wrPresentAsSheet } from 'ngwr/overlay';\nimport { numAttr } from 'ngwr/utils';\n\nimport { WR_POPOVER_POSITIONS, type WrPopoverPosition } from './interfaces';\nimport { WrPopoverTextPanel } from './internal/text-panel';\n\n/**\n * Anchored content panel. The same directive covers two shapes:\n *\n * - **Popover** (default, `mode=\"popover\"`) — `[wrPopover]` takes a\n * `TemplateRef`. Opens on click or hover. Use for rich anchored content:\n * forms, summaries, menus.\n *\n * - **Tooltip** (`mode=\"tooltip\"`) — `[wrPopover]` takes a plain string.\n * Shown on hover and focus, dismissed on blur / pointer-leave / Escape.\n * Uses `aria-describedby` instead of `aria-haspopup` and is rendered into\n * a small dark text panel.\n *\n * Built on CDK Overlay so it auto-flips, closes on outside-click and Escape.\n *\n * @example\n * ```html\n * <!-- Popover with template content -->\n * <button wr-btn [wrPopover]=\"info\">Details</button>\n * <ng-template #info>\n * <div style=\"padding: 1rem; max-width: 18rem\">Anything you can render.</div>\n * </ng-template>\n *\n * <!-- Tooltip with plain text -->\n * <button wr-btn [wrPopover]=\"'Save changes'\" mode=\"tooltip\" position=\"top\">\n * Save\n * </button>\n * ```\n *\n * @see https://ngwr.dev/reference/components/popover\n */\nlet popoverUid = 0;\n\n@Directive({\n selector: '[wrPopover]',\n host: {\n // Marks the trigger the way `[wrDropdown]` marks its own. Popover-mode\n // content is a `TemplateRef`, so it is always bound as `[wrPopover]=\"…\"`,\n // which leaves no attribute in the DOM at all, and a closed tooltip\n // publishes no ARIA either — so without this there is nothing to find a\n // trigger by. (Only the static string form, `wrPopover=\"text\"`, would be.)\n class: 'wr-popover-trigger',\n '[attr.aria-haspopup]': 'isTooltip() ? null : \"dialog\"',\n '[attr.aria-expanded]': 'isTooltip() ? null : isOpen()',\n '[attr.aria-controls]': '!isTooltip() && isOpen() ? panelId : null',\n '[attr.aria-describedby]': 'isTooltip() && isOpen() ? panelId : null',\n '(click)': 'onClick($event)',\n '(mouseenter)': 'onMouseEnter()',\n '(mouseleave)': 'onMouseLeave($event)',\n '(focus)': 'onFocus()',\n '(blur)': 'onBlur()',\n '(keydown.escape)': 'onEscape()',\n },\n})\nexport class WrPopover {\n /**\n * Content to render inside the panel.\n * - In **popover** mode (default): pass a `TemplateRef`.\n * - In **tooltip** mode: pass a plain string.\n */\n readonly content = input.required<TemplateRef<unknown> | string>({ alias: 'wrPopover' });\n\n /**\n * Shape preset.\n *\n * - `'popover'` (default) — template content, click trigger, dialog\n * semantics.\n * - `'tooltip'` — text content, hover+focus trigger, `role=\"tooltip\"`,\n * `aria-describedby` on the host.\n */\n readonly mode = input<'popover' | 'tooltip'>('popover');\n\n /**\n * How the popover opens. Ignored in tooltip mode — tooltips are always\n * hover+focus. @default 'click'\n */\n readonly trigger = input<'click' | 'hover'>('click');\n\n /** Anchor side. @default 'bottom' for popover, 'top' for tooltip */\n readonly position = input<WrPopoverPosition | null>(null);\n\n /** Tooltip only — delay before showing, in ms. @default 120 */\n readonly showDelay = input(120, { transform: numAttr(120) });\n\n /** Tooltip only — delay before hiding, in ms. @default 60 */\n readonly hideDelay = input(60, { transform: numAttr(60) });\n\n /**\n * Popover mode only — present the panel as a full-width bottom-sheet on\n * small viewports instead of an anchored panel. `undefined` follows the\n * app-wide `provideWrResponsiveOverlays()` setting; `true`/`false`\n * overrides it. Tooltips never become sheets. @default undefined\n */\n readonly responsive = input<boolean | undefined, BooleanInput>(undefined, {\n transform: (v: BooleanInput): boolean | undefined => (v == null ? undefined : coerceBooleanProperty(v)),\n });\n\n /**\n * Popover mode only — accessible name of the panel. `role=\"dialog\"` with no\n * name announces as a bare \"dialog\", so the catalog's `popover.label` is used\n * when nothing is given. A popover has no universal name; whenever the panel\n * has a heading or a purpose, pass it.\n */\n readonly ariaLabel = input<string | null>(null);\n\n /** Fires after the panel opens. */\n readonly opened = output<void>();\n\n /** Fires after the panel closes. */\n readonly closed = output<void>();\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly overlay = inject(WR_OVERLAY);\n private readonly dir = inject(Directionality, { optional: true });\n private readonly outsideClick = inject(WrOutsideClick);\n private readonly responsiveConfig = inject(WR_RESPONSIVE_OVERLAYS);\n private readonly vcr = inject(ViewContainerRef);\n private readonly scrollStrategies = inject(ScrollStrategyOptions);\n private readonly destroyRef = inject(DestroyRef);\n\n /** @internal */\n readonly isOpen = signal(false);\n\n /** @internal */\n protected readonly isTooltip = computed(() => this.mode() === 'tooltip');\n\n /** Auto-generated id for `aria-controls` / `aria-describedby`. */\n protected readonly panelId = `wr-popover-${++popoverUid}`;\n\n private readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'popover.label', 'Popover');\n\n private readonly resolvedPosition = computed<WrPopoverPosition>(\n () => this.position() ?? (this.isTooltip() ? 'top' : 'bottom')\n );\n\n private overlayRef: OverlayRef | null = null;\n private hoverCloseTimer: ReturnType<typeof setTimeout> | null = null;\n private showTimer: ReturnType<typeof setTimeout> | null = null;\n private hideTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor() {\n effect(() => {\n if (this.isOpen()) {\n this.openOverlay();\n } else {\n this.closeOverlay();\n }\n });\n this.destroyRef.onDestroy(() => {\n this.clearTimers();\n this.closeOverlay();\n });\n }\n\n open(): void {\n this.isOpen.set(true);\n }\n\n close(): void {\n this.isOpen.set(false);\n }\n\n toggle(): void {\n this.isOpen.update(v => !v);\n }\n\n // Host listeners\n\n /** @internal */\n protected onClick(event: MouseEvent): void {\n if (this.isTooltip() || this.trigger() !== 'click') return;\n event.stopPropagation();\n this.toggle();\n }\n\n /** @internal */\n protected onMouseEnter(): void {\n if (this.isTooltip()) {\n this.scheduleShow();\n return;\n }\n if (this.trigger() !== 'hover') return;\n this.clearHoverClose();\n this.isOpen.set(true);\n }\n\n /** @internal */\n protected onMouseLeave(event: MouseEvent): void {\n if (this.isTooltip()) {\n this.scheduleHide();\n return;\n }\n if (this.trigger() !== 'hover') return;\n const related = event.relatedTarget as Node | null;\n if (related && this.overlayRef?.overlayElement.contains(related)) return;\n // Delay so the pointer can cross the gap between trigger and panel —\n // entering the panel cancels the close (see attach listeners).\n this.scheduleHoverClose();\n }\n\n /** @internal */\n protected onFocus(): void {\n if (!this.isTooltip()) return;\n this.scheduleShow();\n }\n\n /** @internal */\n protected onBlur(): void {\n if (!this.isTooltip()) return;\n this.scheduleHide();\n }\n\n /** @internal */\n protected onEscape(): void {\n if (!this.isTooltip()) return;\n this.clearTimers();\n this.isOpen.set(false);\n }\n\n // Tooltip timers\n\n private scheduleShow(): void {\n if (this.isOpen()) {\n this.clearTimers();\n return;\n }\n const text = this.content();\n if (typeof text === 'string' && !text) return;\n this.clearTimers();\n this.showTimer = setTimeout(() => this.isOpen.set(true), this.showDelay());\n }\n\n private scheduleHide(): void {\n this.clearTimers();\n this.hideTimer = setTimeout(() => this.isOpen.set(false), this.hideDelay());\n }\n\n private clearTimers(): void {\n if (this.showTimer) {\n clearTimeout(this.showTimer);\n this.showTimer = null;\n }\n if (this.hideTimer) {\n clearTimeout(this.hideTimer);\n this.hideTimer = null;\n }\n }\n\n // Overlay\n\n private openOverlay(): void {\n if (this.overlayRef) return;\n\n const tooltip = this.isTooltip();\n const position = this.resolvedPosition();\n\n // Sheet presentation is popover-only — tooltips are transient labels and\n // never dock to the bottom. On small viewports (when opted in) detach\n // from the trigger and slide the panel up from the bottom edge.\n const asSheet = !tooltip && wrPresentAsSheet(this.responsive(), this.responsiveConfig);\n\n const positionStrategy = asSheet\n ? this.overlay.position().global().centerHorizontally().bottom('0')\n : this.overlay\n .position()\n .flexibleConnectedTo(this.host)\n .withPositions(wrMirrorOffsets(WR_POPOVER_POSITIONS[position], this.isRtl()))\n .withPush(true);\n\n const overlayClass = tooltip\n ? ['wr-tooltip-overlay', `wr-tooltip-overlay--${position}`]\n : asSheet\n ? ['wr-popover-overlay', 'wr-overlay-sheet']\n : ['wr-popover-overlay', `wr-popover-overlay--${position}`];\n\n this.overlayRef = this.overlay.create({\n positionStrategy,\n scrollStrategy: asSheet ? this.scrollStrategies.block() : this.scrollStrategies.reposition(),\n width: asSheet ? '100%' : undefined,\n hasBackdrop: asSheet,\n backdropClass: asSheet ? 'wr-overlay-backdrop' : undefined,\n panelClass: overlayClass,\n });\n\n if (asSheet) {\n this.overlayRef\n .backdropClick()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(() => this.isOpen.set(false));\n }\n\n const content = this.content();\n\n if (tooltip) {\n // Text content rendered via the internal text panel — accepts a\n // string and wraps it in the `.wr-tooltip` shell.\n const text = typeof content === 'string' ? content : '';\n const ref = this.overlayRef.attach(new ComponentPortal(WrPopoverTextPanel, this.vcr));\n ref.setInput('text', text);\n // The pane is the element `aria-describedby` points at (it carries\n // `panelId`), so this is the role that has to be `tooltip` — the inner\n // `wr-popover-text` host deliberately carries none, or the description\n // would be a tooltip nested inside a tooltip.\n this.overlayRef.overlayElement.setAttribute('role', 'tooltip');\n } else if (typeof content !== 'string') {\n this.overlayRef.attach(new TemplatePortal(content, this.vcr));\n // Popover mode is a non-modal dialog: the docstring promised dialog\n // semantics but nothing ever set a role, so a screen reader met an\n // unnamed generic container. `aria-modal=\"false\"` is explicit — focus is\n // deliberately NOT trapped, the panel closes on outside click / Escape.\n this.overlayRef.overlayElement.setAttribute('role', 'dialog');\n this.overlayRef.overlayElement.setAttribute('aria-modal', 'false');\n // A named dialog: `role=\"dialog\"` with no name announces as a bare\n // \"dialog\" and trips axe's `aria-dialog-name`.\n this.overlayRef.overlayElement.setAttribute('aria-label', this.resolvedAriaLabel());\n }\n this.overlayRef.overlayElement.id = this.panelId;\n\n this.outsideClick\n .outsidePointerEvents(this.overlayRef)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(event => {\n if (this.host.nativeElement.contains(event.target as Node)) return;\n this.isOpen.set(false);\n });\n\n this.overlayRef\n .keydownEvents()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(event => {\n if (event.key === 'Escape') {\n event.preventDefault();\n this.isOpen.set(false);\n }\n });\n\n if (!tooltip && this.trigger() === 'hover') {\n this.overlayRef.overlayElement.addEventListener('mouseenter', this.onOverlayEnter);\n this.overlayRef.overlayElement.addEventListener('mouseleave', this.onOverlayLeave);\n }\n\n this.opened.emit();\n }\n\n private closeOverlay(): void {\n if (!this.overlayRef) return;\n if (!this.isTooltip() && this.trigger() === 'hover') {\n this.overlayRef.overlayElement.removeEventListener('mouseenter', this.onOverlayEnter);\n this.overlayRef.overlayElement.removeEventListener('mouseleave', this.onOverlayLeave);\n }\n this.clearHoverClose();\n this.overlayRef.dispose();\n this.overlayRef = null;\n this.closed.emit();\n }\n\n private readonly onOverlayLeave = (event: MouseEvent): void => {\n const related = event.relatedTarget as Node | null;\n if (related && this.host.nativeElement.contains(related)) return;\n this.scheduleHoverClose();\n };\n\n private readonly onOverlayEnter = (): void => {\n this.clearHoverClose();\n };\n\n private scheduleHoverClose(): void {\n this.clearHoverClose();\n this.hoverCloseTimer = setTimeout(() => this.isOpen.set(false), 120);\n }\n\n private clearHoverClose(): void {\n if (this.hoverCloseTimer) {\n clearTimeout(this.hoverCloseTimer);\n this.hoverCloseTimer = null;\n }\n }\n\n /**\n * Whether the app reads right-to-left.\n *\n * Optional inject: `Directionality` is root-provided, so this is never null in\n * an app — but a bare `TestBed` that provides nothing should still get a\n * component that works, and defaulting to LTR is the honest fallback.\n */\n private isRtl(): boolean {\n return this.dir?.value === 'rtl';\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-popover.mjs","sources":["../../../projects/lib/popover/interfaces/popover-position.ts","../../../projects/lib/popover/internal/text-panel.ts","../../../projects/lib/popover/popover.ts","../../../projects/lib/popover/ngwr-popover.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 type { ConnectedPosition } from '@angular/cdk/overlay';\n\nexport type WrPopoverPosition =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'left'\n | 'left-start'\n | 'left-end'\n | 'right'\n | 'right-start'\n | 'right-end';\n\n/** @internal */\nexport const WR_POPOVER_POSITIONS: Record<WrPopoverPosition, ConnectedPosition[]> = {\n top: [{ originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -8 }],\n 'top-start': [{ originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -8 }],\n 'top-end': [{ originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -8 }],\n bottom: [{ originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 8 }],\n 'bottom-start': [{ originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 8 }],\n 'bottom-end': [{ originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 8 }],\n left: [{ originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -8 }],\n 'left-start': [{ originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'top', offsetX: -8 }],\n 'left-end': [{ originX: 'start', originY: 'bottom', overlayX: 'end', overlayY: 'bottom', offsetX: -8 }],\n right: [{ originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: 8 }],\n 'right-start': [{ originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top', offsetX: 8 }],\n 'right-end': [{ originX: 'end', originY: 'bottom', overlayX: 'start', overlayY: 'bottom', offsetX: 8 }],\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 { Component, ViewEncapsulation, input } from '@angular/core';\n\n/**\n * Internal floating label rendered when `<… [wrPopover]=\"'…'\" mode=\"tooltip\">`\n * receives a string instead of a template. Not part of the public template\n * API — use the `WrPopover` directive in tooltip mode instead.\n *\n * @internal\n */\n@Component({\n selector: 'wr-popover-text',\n template: '{{ text() }}',\n encapsulation: ViewEncapsulation.None,\n // No `role=\"tooltip\"` here on purpose: the overlay pane this is attached to\n // is the element `aria-describedby` resolves to, and it carries the role.\n // Setting it here too nested one tooltip inside another.\n host: { class: 'wr-tooltip' },\n})\nexport class WrPopoverTextPanel {\n readonly text = input.required<string>();\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 { Directionality } from '@angular/cdk/bidi';\nimport { type BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { type OverlayRef, ScrollStrategyOptions } from '@angular/cdk/overlay';\nimport { ComponentPortal, TemplatePortal } from '@angular/cdk/portal';\nimport type { TemplateRef } from '@angular/core';\nimport {\n DestroyRef,\n Directive,\n ElementRef,\n ViewContainerRef,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { useI18nText } from 'ngwr/i18n';\nimport { WR_OVERLAY, WR_RESPONSIVE_OVERLAYS, WrOutsideClick, wrMirrorOffsets, wrPresentAsSheet } from 'ngwr/overlay';\nimport { numAttr } from 'ngwr/utils';\n\nimport { WR_POPOVER_POSITIONS, type WrPopoverPosition } from './interfaces';\nimport { WrPopoverTextPanel } from './internal/text-panel';\n\n/**\n * Anchored content panel. The same directive covers two shapes:\n *\n * - **Popover** (default, `mode=\"popover\"`) — `[wrPopover]` takes a\n * `TemplateRef`. Opens on click or hover. Use for rich anchored content:\n * forms, summaries, menus.\n *\n * - **Tooltip** (`mode=\"tooltip\"`) — `[wrPopover]` takes a plain string.\n * Shown on hover and focus, dismissed on blur / pointer-leave / Escape.\n * Uses `aria-describedby` instead of `aria-haspopup` and is rendered into\n * a small dark text panel.\n *\n * Built on CDK Overlay so it auto-flips, closes on outside-click and Escape.\n *\n * @example\n * ```html\n * <!-- Popover with template content -->\n * <button wr-btn [wrPopover]=\"info\">Details</button>\n * <ng-template #info>\n * <div style=\"padding: 1rem; max-width: 18rem\">Anything you can render.</div>\n * </ng-template>\n *\n * <!-- Tooltip with plain text -->\n * <button wr-btn [wrPopover]=\"'Save changes'\" mode=\"tooltip\" position=\"top\">\n * Save\n * </button>\n * ```\n *\n * @see https://ngwr.dev/reference/components/popover\n */\nlet popoverUid = 0;\n\n@Directive({\n selector: '[wrPopover]',\n host: {\n // Marks the trigger the way `[wrDropdown]` marks its own. Popover-mode\n // content is a `TemplateRef`, so it is always bound as `[wrPopover]=\"…\"`,\n // which leaves no attribute in the DOM at all, and a closed tooltip\n // publishes no ARIA either — so without this there is nothing to find a\n // trigger by. (Only the static string form, `wrPopover=\"text\"`, would be.)\n class: 'wr-popover-trigger',\n '[attr.aria-haspopup]': 'isTooltip() ? null : \"dialog\"',\n '[attr.aria-expanded]': 'isTooltip() ? null : isOpen()',\n '[attr.aria-controls]': '!isTooltip() && isOpen() ? panelId : null',\n '[attr.aria-describedby]': 'isTooltip() && isOpen() ? panelId : null',\n '(click)': 'onClick($event)',\n '(mouseenter)': 'onMouseEnter()',\n '(mouseleave)': 'onMouseLeave($event)',\n '(focus)': 'onFocus()',\n '(blur)': 'onBlur()',\n '(keydown.escape)': 'onEscape()',\n },\n})\nexport class WrPopover {\n /**\n * Content to render inside the panel.\n * - In **popover** mode (default): pass a `TemplateRef`.\n * - In **tooltip** mode: pass a plain string.\n */\n readonly content = input.required<TemplateRef<unknown> | string>({ alias: 'wrPopover' });\n\n /**\n * Shape preset.\n *\n * - `'popover'` (default) — template content, click trigger, dialog\n * semantics.\n * - `'tooltip'` — text content, hover+focus trigger, `role=\"tooltip\"`,\n * `aria-describedby` on the host.\n */\n readonly mode = input<'popover' | 'tooltip'>('popover');\n\n /**\n * How the popover opens. Ignored in tooltip mode — tooltips are always\n * hover+focus. @default 'click'\n */\n readonly trigger = input<'click' | 'hover'>('click');\n\n /** Anchor side. @default 'bottom' for popover, 'top' for tooltip */\n readonly position = input<WrPopoverPosition | null>(null);\n\n /** Tooltip only — delay before showing, in ms. @default 120 */\n readonly showDelay = input(120, { transform: numAttr(120) });\n\n /** Tooltip only — delay before hiding, in ms. @default 60 */\n readonly hideDelay = input(60, { transform: numAttr(60) });\n\n /**\n * Popover mode only — present the panel as a full-width bottom-sheet on\n * small viewports instead of an anchored panel. `undefined` follows the\n * app-wide `provideWrResponsiveOverlays()` setting; `true`/`false`\n * overrides it. Tooltips never become sheets. @default undefined\n */\n readonly responsive = input<boolean | undefined, BooleanInput>(undefined, {\n transform: (v: BooleanInput): boolean | undefined => (v == null ? undefined : coerceBooleanProperty(v)),\n });\n\n /**\n * Popover mode only — accessible name of the panel. `role=\"dialog\"` with no\n * name announces as a bare \"dialog\", so the catalog's `popover.label` is used\n * when nothing is given. A popover has no universal name; whenever the panel\n * has a heading or a purpose, pass it.\n */\n readonly ariaLabel = input<string | null>(null);\n\n /** Fires after the panel opens. */\n readonly opened = output<void>();\n\n /** Fires after the panel closes. */\n readonly closed = output<void>();\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly overlay = inject(WR_OVERLAY);\n private readonly dir = inject(Directionality, { optional: true });\n private readonly outsideClick = inject(WrOutsideClick);\n private readonly responsiveConfig = inject(WR_RESPONSIVE_OVERLAYS);\n private readonly vcr = inject(ViewContainerRef);\n private readonly scrollStrategies = inject(ScrollStrategyOptions);\n private readonly destroyRef = inject(DestroyRef);\n\n /** @internal */\n readonly isOpen = signal(false);\n\n /** @internal */\n protected readonly isTooltip = computed(() => this.mode() === 'tooltip');\n\n /** Auto-generated id for `aria-controls` / `aria-describedby`. */\n protected readonly panelId = `wr-popover-${++popoverUid}`;\n\n private readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'popover.label', 'Popover');\n\n private readonly resolvedPosition = computed<WrPopoverPosition>(\n () => this.position() ?? (this.isTooltip() ? 'top' : 'bottom')\n );\n\n private overlayRef: OverlayRef | null = null;\n private hoverCloseTimer: ReturnType<typeof setTimeout> | null = null;\n private showTimer: ReturnType<typeof setTimeout> | null = null;\n private hideTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor() {\n effect(() => {\n if (this.isOpen()) {\n this.openOverlay();\n } else {\n this.closeOverlay();\n }\n });\n this.destroyRef.onDestroy(() => {\n this.clearTimers();\n this.closeOverlay();\n });\n }\n\n open(): void {\n this.isOpen.set(true);\n }\n\n close(): void {\n this.isOpen.set(false);\n }\n\n toggle(): void {\n this.isOpen.update(v => !v);\n }\n\n // Host listeners\n\n /** @internal */\n protected onClick(event: MouseEvent): void {\n if (this.isTooltip() || this.trigger() !== 'click') return;\n event.stopPropagation();\n this.toggle();\n }\n\n /** @internal */\n protected onMouseEnter(): void {\n if (this.isTooltip()) {\n this.scheduleShow();\n return;\n }\n if (this.trigger() !== 'hover') return;\n this.clearHoverClose();\n this.isOpen.set(true);\n }\n\n /** @internal */\n protected onMouseLeave(event: MouseEvent): void {\n if (this.isTooltip()) {\n this.scheduleHide();\n return;\n }\n if (this.trigger() !== 'hover') return;\n const related = event.relatedTarget as Node | null;\n if (related && this.overlayRef?.overlayElement.contains(related)) return;\n // Delay so the pointer can cross the gap between trigger and panel —\n // entering the panel cancels the close (see attach listeners).\n this.scheduleHoverClose();\n }\n\n /** @internal */\n protected onFocus(): void {\n if (!this.isTooltip()) return;\n this.scheduleShow();\n }\n\n /** @internal */\n protected onBlur(): void {\n if (!this.isTooltip()) return;\n this.scheduleHide();\n }\n\n /** @internal */\n protected onEscape(): void {\n if (!this.isTooltip()) return;\n this.clearTimers();\n this.isOpen.set(false);\n }\n\n // Tooltip timers\n\n private scheduleShow(): void {\n if (this.isOpen()) {\n this.clearTimers();\n return;\n }\n const text = this.content();\n if (typeof text === 'string' && !text) return;\n this.clearTimers();\n this.showTimer = setTimeout(() => this.isOpen.set(true), this.showDelay());\n }\n\n private scheduleHide(): void {\n this.clearTimers();\n this.hideTimer = setTimeout(() => this.isOpen.set(false), this.hideDelay());\n }\n\n private clearTimers(): void {\n if (this.showTimer) {\n clearTimeout(this.showTimer);\n this.showTimer = null;\n }\n if (this.hideTimer) {\n clearTimeout(this.hideTimer);\n this.hideTimer = null;\n }\n }\n\n // Overlay\n\n private openOverlay(): void {\n if (this.overlayRef) return;\n\n const tooltip = this.isTooltip();\n const position = this.resolvedPosition();\n\n // Sheet presentation is popover-only — tooltips are transient labels and\n // never dock to the bottom. On small viewports (when opted in) detach\n // from the trigger and slide the panel up from the bottom edge.\n const asSheet = !tooltip && wrPresentAsSheet(this.responsive(), this.responsiveConfig);\n\n const positionStrategy = asSheet\n ? this.overlay.position().global().centerHorizontally().bottom('0')\n : this.overlay\n .position()\n .flexibleConnectedTo(this.host)\n .withPositions(wrMirrorOffsets(WR_POPOVER_POSITIONS[position], this.isRtl()))\n .withPush(true);\n\n const overlayClass = tooltip\n ? ['wr-tooltip-overlay', `wr-tooltip-overlay--${position}`]\n : asSheet\n ? ['wr-popover-overlay', 'wr-overlay-sheet']\n : ['wr-popover-overlay', `wr-popover-overlay--${position}`];\n\n this.overlayRef = this.overlay.create({\n positionStrategy,\n scrollStrategy: asSheet ? this.scrollStrategies.block() : this.scrollStrategies.reposition(),\n width: asSheet ? '100%' : undefined,\n hasBackdrop: asSheet,\n backdropClass: asSheet ? 'wr-overlay-backdrop' : undefined,\n panelClass: overlayClass,\n });\n\n if (asSheet) {\n this.overlayRef\n .backdropClick()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(() => this.isOpen.set(false));\n }\n\n const content = this.content();\n\n if (tooltip) {\n // Text content rendered via the internal text panel — accepts a\n // string and wraps it in the `.wr-tooltip` shell.\n const text = typeof content === 'string' ? content : '';\n const ref = this.overlayRef.attach(new ComponentPortal(WrPopoverTextPanel, this.vcr));\n ref.setInput('text', text);\n // The pane is the element `aria-describedby` points at (it carries\n // `panelId`), so this is the role that has to be `tooltip` — the inner\n // `wr-popover-text` host deliberately carries none, or the description\n // would be a tooltip nested inside a tooltip.\n this.overlayRef.overlayElement.setAttribute('role', 'tooltip');\n } else if (typeof content !== 'string') {\n this.overlayRef.attach(new TemplatePortal(content, this.vcr));\n // Popover mode is a non-modal dialog: the docstring promised dialog\n // semantics but nothing ever set a role, so a screen reader met an\n // unnamed generic container. `aria-modal=\"false\"` is explicit — focus is\n // deliberately NOT trapped, the panel closes on outside click / Escape.\n this.overlayRef.overlayElement.setAttribute('role', 'dialog');\n this.overlayRef.overlayElement.setAttribute('aria-modal', 'false');\n // A named dialog: `role=\"dialog\"` with no name announces as a bare\n // \"dialog\" and trips axe's `aria-dialog-name`.\n this.overlayRef.overlayElement.setAttribute('aria-label', this.resolvedAriaLabel());\n }\n this.overlayRef.overlayElement.id = this.panelId;\n\n this.outsideClick\n .outsidePointerEvents(this.overlayRef)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(event => {\n if (this.host.nativeElement.contains(event.target as Node)) return;\n this.isOpen.set(false);\n });\n\n this.overlayRef\n .keydownEvents()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(event => {\n if (event.key === 'Escape') {\n event.preventDefault();\n this.isOpen.set(false);\n }\n });\n\n if (!tooltip && this.trigger() === 'hover') {\n this.overlayRef.overlayElement.addEventListener('mouseenter', this.onOverlayEnter);\n this.overlayRef.overlayElement.addEventListener('mouseleave', this.onOverlayLeave);\n }\n\n this.opened.emit();\n }\n\n private closeOverlay(): void {\n if (!this.overlayRef) return;\n if (!this.isTooltip() && this.trigger() === 'hover') {\n this.overlayRef.overlayElement.removeEventListener('mouseenter', this.onOverlayEnter);\n this.overlayRef.overlayElement.removeEventListener('mouseleave', this.onOverlayLeave);\n }\n this.clearHoverClose();\n // Take the focus back before the pane goes, and only if it was in there.\n //\n // A popover is not a tooltip: it attaches a `TemplatePortal` under\n // `role=\"dialog\"`, so a form or a menu inside it is the point, and Escape\n // arrives through the overlay's `keydownEvents()` no matter where the caret\n // is. Disposing with focus inside dropped it on `<body>`, and the next Tab\n // restarted from the top of the document. `wr-date-picker` and\n // `wr-popconfirm` already guard it exactly this way; leaving focus alone\n // when it had already moved is what stops the close from fighting the user\n // for the caret.\n const pane = this.overlayRef.overlayElement;\n const focusWasInside = pane.contains(this.host.nativeElement.ownerDocument.activeElement);\n this.overlayRef.dispose();\n this.overlayRef = null;\n if (focusWasInside) this.host.nativeElement.focus();\n this.closed.emit();\n }\n\n private readonly onOverlayLeave = (event: MouseEvent): void => {\n const related = event.relatedTarget as Node | null;\n if (related && this.host.nativeElement.contains(related)) return;\n this.scheduleHoverClose();\n };\n\n private readonly onOverlayEnter = (): void => {\n this.clearHoverClose();\n };\n\n private scheduleHoverClose(): void {\n this.clearHoverClose();\n this.hoverCloseTimer = setTimeout(() => this.isOpen.set(false), 120);\n }\n\n private clearHoverClose(): void {\n if (this.hoverCloseTimer) {\n clearTimeout(this.hoverCloseTimer);\n this.hoverCloseTimer = null;\n }\n }\n\n /**\n * Whether the app reads right-to-left.\n *\n * Optional inject: `Directionality` is root-provided, so this is never null in\n * an app — but a bare `TestBed` that provides nothing should still get a\n * component that works, and defaulting to LTR is the honest fallback.\n */\n private isRtl(): boolean {\n return this.dir?.value === 'rtl';\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
8
|
+
*/
|
|
9
|
+
/** One animated piece — a grapheme, a word or a line, depending on `splitBy`. */
|
|
10
|
+
const CHAR = '.wr-rotating-text__char';
|
|
11
|
+
/** The whole animated layer, and the one element taken out of the a11y tree. */
|
|
12
|
+
const INNER = '.wr-rotating-text__inner';
|
|
13
|
+
/** The readable copy of the string showing now, carried once outside the animated layer. */
|
|
14
|
+
const SR_ONLY = '.wr-rotating-text__sr-only';
|
|
15
|
+
/** A word group — kept whether the word is drawn whole or exploded into characters. */
|
|
16
|
+
const WORD = '.wr-rotating-text__word';
|
|
17
|
+
/**
|
|
18
|
+
* An inline `opacity: 1` — the end state the enter step commits on every piece.
|
|
19
|
+
*
|
|
20
|
+
* Matched off the `style` ATTRIBUTE, never through `getCssValue`: a computed style would
|
|
21
|
+
* answer `1` for a piece nobody ever wrote to, which is the exact case worth telling
|
|
22
|
+
* apart from a piece the component finished with.
|
|
23
|
+
*/
|
|
24
|
+
const SETTLED_OPACITY = /(?:^|;)\s*opacity\s*:\s*1\s*(?:;|$)/;
|
|
25
|
+
/** Exactly what the element holds — untrimmed, unlike `TestElement.text()`. */
|
|
26
|
+
async function rawText(element) {
|
|
27
|
+
return (await element.getProperty('textContent')) ?? '';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Test harness for `<wr-rotating-text>`.
|
|
31
|
+
*
|
|
32
|
+
* **What is announced and what is drawn are two questions, and this component is worth a
|
|
33
|
+
* harness mostly because it answers them separately.** The string showing now is carried
|
|
34
|
+
* verbatim in a visually-hidden span; the animated layer beside it is one span per
|
|
35
|
+
* grapheme, word or line and is `aria-hidden` as a whole, so a screen reader hears `one`
|
|
36
|
+
* rather than `o. n. e.` {@link getAccessibleText} is the first, {@link getRenderedText}
|
|
37
|
+
* and {@link getPieces} the second, and {@link isAnimatedLayerHidden} together with
|
|
38
|
+
* {@link hasAccessibleCopy} is the pairing that regresses silently when someone
|
|
39
|
+
* restructures the template — pieces hidden AND no readable copy is an element with no
|
|
40
|
+
* accessible text at all, which reads as a pass on either check alone.
|
|
41
|
+
*
|
|
42
|
+
* **The sr-only span is deliberately not a live region, and no method here implies
|
|
43
|
+
* otherwise.** `auto` advances every two seconds by default, and the APG's answer for
|
|
44
|
+
* self-advancing content is silence, not an announcement on a timer. What the harness
|
|
45
|
+
* pins is that the announced copy TRACKS the current word — nothing about it being
|
|
46
|
+
* spoken.
|
|
47
|
+
*
|
|
48
|
+
* **There is no `getIndex()`, and no `next()` / `previous()` / `jumpTo()` / `reset()`.**
|
|
49
|
+
* The index appears nowhere in the DOM — no attribute, no data hook — so deriving it
|
|
50
|
+
* would mean matching the shown string against a list the harness was never given, and
|
|
51
|
+
* guessing between duplicates. And the component renders no controls at all: rotation is
|
|
52
|
+
* a timer or a method call on the instance, so a harness "click" would have nothing to
|
|
53
|
+
* click and reaching into the component would make this a wrapper around the class rather
|
|
54
|
+
* than a reader of the DOM. Drive it from the spec — through the instance, or by
|
|
55
|
+
* advancing the interval — and assert the `nextChange` output for the index.
|
|
56
|
+
*
|
|
57
|
+
* **Nothing here reports the tween.** `duration`, `easing`, `staggerDuration` and
|
|
58
|
+
* `staggerFrom` are passed straight into Web Animation options and never written to the
|
|
59
|
+
* document; `delayFor()` is arithmetic with no rendered trace. So no `getDuration()`, no
|
|
60
|
+
* `getStagger()`, no `isAnimating()`. The pieces do carry inline `opacity` and
|
|
61
|
+
* `transform`, but on the animated path those are the FIRST keyframe — `opacity: 0` on
|
|
62
|
+
* text the user can see perfectly well — which is why the only reading offered is
|
|
63
|
+
* {@link isSettled}, and why it is spelled as a commitment rather than as a state of
|
|
64
|
+
* motion. The host's `overflow: hidden` clipping is layout, and every rect in a unit test
|
|
65
|
+
* is 0×0, so there is no `isClipped()` either.
|
|
66
|
+
*
|
|
67
|
+
* jsdom implements no Web Animations API, so the animated path throws on the first
|
|
68
|
+
* `el.animate` — before any assertion runs. Every spec supplies one half or the other:
|
|
69
|
+
*
|
|
70
|
+
* ```ts
|
|
71
|
+
* // Either: mount under reduced motion, where each swap is instant and nothing tweens.
|
|
72
|
+
* // (jsdom has no matchMedia, so WrPlatform must be provided as a value.)
|
|
73
|
+
* TestBed.configureTestingModule({ providers: [{ provide: WrPlatform, useValue: reducedMotion }] });
|
|
74
|
+
*
|
|
75
|
+
* // Or: stub the API — and remember a stub that never fires `onfinish` never resolves
|
|
76
|
+
* // the exit step, so the rotator stops after one swap.
|
|
77
|
+
* Reflect.set(Element.prototype, 'animate', () => ({ cancel: () => undefined, onfinish: null }));
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* const rotator = await loader.getHarness(WrRotatingTextHarness);
|
|
83
|
+
*
|
|
84
|
+
* expect(await rotator.getAccessibleText()).toBe('design');
|
|
85
|
+
* expect(await rotator.getPieces()).toEqual(['d', 'e', 's', 'i', 'g', 'n']);
|
|
86
|
+
* expect(await rotator.isAnimatedLayerHidden()).toBe(true);
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @see https://ngwr.dev/guides/testing
|
|
90
|
+
*/
|
|
91
|
+
class WrRotatingTextHarness extends ComponentHarness {
|
|
92
|
+
static hostSelector = 'wr-rotating-text';
|
|
93
|
+
/** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
|
|
94
|
+
static with(options = {}) {
|
|
95
|
+
return new HarnessPredicate(WrRotatingTextHarness, options)
|
|
96
|
+
.addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getAccessibleText(), text))
|
|
97
|
+
.addOption('wordCount', options.wordCount, async (harness, count) => (await harness.getWordCount()) === count);
|
|
98
|
+
}
|
|
99
|
+
chars = this.locatorForAll(CHAR);
|
|
100
|
+
/**
|
|
101
|
+
* The string showing right now, as the one readable copy holds it.
|
|
102
|
+
*
|
|
103
|
+
* Literally what a screen reader gets, the animated layer being out of the tree — and
|
|
104
|
+
* the thing to assert a rotation against, since it is where `next()`, `previous()`, a
|
|
105
|
+
* clamped `jumpTo()`, `reset()`, the wrap at either end, the stop when `loop` is off and
|
|
106
|
+
* the shrinking-list clamp all become visible.
|
|
107
|
+
*
|
|
108
|
+
* Throws when the copy is missing rather than answering with the pieces' text: an empty
|
|
109
|
+
* string is what a rotator holding an empty list correctly reports, so a plausible
|
|
110
|
+
* nothing here is indistinguishable from the component having lost its accessible name
|
|
111
|
+
* entirely. Ask {@link hasAccessibleCopy} when presence is the question.
|
|
112
|
+
*/
|
|
113
|
+
async getAccessibleText() {
|
|
114
|
+
const copy = await this.locatorForOptional(SR_ONLY)();
|
|
115
|
+
if (!copy) {
|
|
116
|
+
throw new Error('WrRotatingTextHarness.getAccessibleText(): this <wr-rotating-text> renders no ' +
|
|
117
|
+
`${SR_ONLY} span, so it has no accessible text at all — every piece it draws is aria-hidden. ` +
|
|
118
|
+
'Reading the pieces instead would report a healthy-looking string for a component a screen ' +
|
|
119
|
+
'reader cannot see.');
|
|
120
|
+
}
|
|
121
|
+
return rawText(copy);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Everything the animated layer draws, in document order, as one string.
|
|
125
|
+
*
|
|
126
|
+
* A different fact from {@link getAccessibleText}, and the comparison between them is
|
|
127
|
+
* the point: a split that drops its spacer renders `hellothere` while the readable copy
|
|
128
|
+
* stays perfect, and nothing else in the DOM says so.
|
|
129
|
+
*
|
|
130
|
+
* The spacer between words is a non-breaking space (` `), which is a rendering
|
|
131
|
+
* choice rather than a contract, so it is normalized to a plain space here — otherwise
|
|
132
|
+
* every round-trip assertion would have to spell U+00A0 out to pass against a working
|
|
133
|
+
* component. Note that `splitBy="lines"` genuinely does not round-trip: the newline the
|
|
134
|
+
* readable copy carries is drawn as that same spacer.
|
|
135
|
+
*/
|
|
136
|
+
async getRenderedText() {
|
|
137
|
+
return (await rawText(await this.locatorFor(INNER)())).replace(/\u00a0/g, ' ');
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The animated pieces, in document order.
|
|
141
|
+
*
|
|
142
|
+
* The granularity actually applied: one piece per grapheme for `splitBy="characters"`,
|
|
143
|
+
* one per word for `"words"`, one per line for `"lines"`. It is also where the grapheme
|
|
144
|
+
* contract shows — `a🚀b` must come back as three pieces, not four with two broken
|
|
145
|
+
* halves of a surrogate pair.
|
|
146
|
+
*/
|
|
147
|
+
async getPieces() {
|
|
148
|
+
return Promise.all((await this.chars()).map(rawText));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* How many word groups the split produced.
|
|
152
|
+
*
|
|
153
|
+
* Word boundaries survive every granularity: three words stay three groups even when
|
|
154
|
+
* each is exploded into characters, which is what keeps the line wrapping sane. A
|
|
155
|
+
* splitter that flattened the grouping would leave {@link getPieces} correct and break
|
|
156
|
+
* only this.
|
|
157
|
+
*/
|
|
158
|
+
async getWordCount() {
|
|
159
|
+
return (await this.locatorForAll(WORD)()).length;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Whether the animated layer is out of the accessibility tree.
|
|
163
|
+
*
|
|
164
|
+
* ONE attribute answers it — `aria-hidden` sits on the `__inner` wrapper, so the pieces
|
|
165
|
+
* inherit it and none carries it itself, which keeps the attribute off the nodes the
|
|
166
|
+
* tween writes to. Asserting it per piece would fail on a component that is behaving.
|
|
167
|
+
*
|
|
168
|
+
* If it regresses, every rotation is spelled out letter by letter beside the copy that
|
|
169
|
+
* was already read, and nothing changes on screen — so no other check catches it.
|
|
170
|
+
*/
|
|
171
|
+
async isAnimatedLayerHidden() {
|
|
172
|
+
return (await (await this.locatorFor(INNER)()).getAttribute('aria-hidden')) === 'true';
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Whether the readable copy is rendered at all.
|
|
176
|
+
*
|
|
177
|
+
* The other half of {@link isAnimatedLayerHidden}, and it exists as a separate question
|
|
178
|
+
* because the failure the two catch together is invisible to each alone: a layer
|
|
179
|
+
* correctly hidden with no readable copy beside it is a control with no accessible text
|
|
180
|
+
* whatsoever, and `isAnimatedLayerHidden() === true` is exactly what it reports.
|
|
181
|
+
*/
|
|
182
|
+
async hasAccessibleCopy() {
|
|
183
|
+
return (await this.locatorForOptional(SR_ONLY)()) !== null;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Whether the enter step committed its end state on every piece — NOT whether an
|
|
187
|
+
* animation finished.
|
|
188
|
+
*
|
|
189
|
+
* The component writes `opacity: 1` inline on each piece when the swap settles: instantly
|
|
190
|
+
* under reduced motion, and from the tween's `onfinish` otherwise. So this answers "the
|
|
191
|
+
* new word was actually taken over", which is the bug the deferred enter exists to
|
|
192
|
+
* prevent — a swap that animates the OLD spans leaves the new ones staged at `opacity: 0`
|
|
193
|
+
* and the text invisible, while the readable copy reports the new word correctly.
|
|
194
|
+
*
|
|
195
|
+
* It is all-or-nothing: one piece left staged is one invisible glyph in the middle of a
|
|
196
|
+
* word. On the animated path it stays `false` until each tween finishes, which in a unit
|
|
197
|
+
* test means forever, since a stubbed `Element.animate` never fires anything — that is
|
|
198
|
+
* an honest answer about a swap that never landed, not a limitation to work around.
|
|
199
|
+
*
|
|
200
|
+
* Throws when there are no pieces: "every piece is settled" is true of none of them,
|
|
201
|
+
* and that is also what a component that stopped rendering its text would report.
|
|
202
|
+
*/
|
|
203
|
+
async isSettled() {
|
|
204
|
+
const pieces = await this.chars();
|
|
205
|
+
if (pieces.length === 0) {
|
|
206
|
+
throw new Error('WrRotatingTextHarness.isSettled(): this <wr-rotating-text> drew no pieces, so "settled" would be ' +
|
|
207
|
+
'vacuously true — the same answer a rotator that stopped rendering its text would give. Check ' +
|
|
208
|
+
'`texts` is not empty, or ask getPieces().');
|
|
209
|
+
}
|
|
210
|
+
const styles = await Promise.all(pieces.map(piece => piece.getAttribute('style')));
|
|
211
|
+
return styles.every(style => SETTLED_OPACITY.test(style ?? ''));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Generated bundle index. Do not edit.
|
|
217
|
+
*/
|
|
218
|
+
|
|
219
|
+
export { WrRotatingTextHarness };
|
|
220
|
+
//# sourceMappingURL=ngwr-rotating-text-testing.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngwr-rotating-text-testing.mjs","sources":["../../../projects/lib/rotating-text/testing/wr-rotating-text-harness.ts","../../../projects/lib/rotating-text/testing/ngwr-rotating-text-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrRotatingTextHarnessFilters } from './interfaces';\n\n/** One animated piece — a grapheme, a word or a line, depending on `splitBy`. */\nconst CHAR = '.wr-rotating-text__char';\n\n/** The whole animated layer, and the one element taken out of the a11y tree. */\nconst INNER = '.wr-rotating-text__inner';\n\n/** The readable copy of the string showing now, carried once outside the animated layer. */\nconst SR_ONLY = '.wr-rotating-text__sr-only';\n\n/** A word group — kept whether the word is drawn whole or exploded into characters. */\nconst WORD = '.wr-rotating-text__word';\n\n/**\n * An inline `opacity: 1` — the end state the enter step commits on every piece.\n *\n * Matched off the `style` ATTRIBUTE, never through `getCssValue`: a computed style would\n * answer `1` for a piece nobody ever wrote to, which is the exact case worth telling\n * apart from a piece the component finished with.\n */\nconst SETTLED_OPACITY = /(?:^|;)\\s*opacity\\s*:\\s*1\\s*(?:;|$)/;\n\n/** Exactly what the element holds — untrimmed, unlike `TestElement.text()`. */\nasync function rawText(element: TestElement): Promise<string> {\n return (await element.getProperty<string | null>('textContent')) ?? '';\n}\n\n/**\n * Test harness for `<wr-rotating-text>`.\n *\n * **What is announced and what is drawn are two questions, and this component is worth a\n * harness mostly because it answers them separately.** The string showing now is carried\n * verbatim in a visually-hidden span; the animated layer beside it is one span per\n * grapheme, word or line and is `aria-hidden` as a whole, so a screen reader hears `one`\n * rather than `o. n. e.` {@link getAccessibleText} is the first, {@link getRenderedText}\n * and {@link getPieces} the second, and {@link isAnimatedLayerHidden} together with\n * {@link hasAccessibleCopy} is the pairing that regresses silently when someone\n * restructures the template — pieces hidden AND no readable copy is an element with no\n * accessible text at all, which reads as a pass on either check alone.\n *\n * **The sr-only span is deliberately not a live region, and no method here implies\n * otherwise.** `auto` advances every two seconds by default, and the APG's answer for\n * self-advancing content is silence, not an announcement on a timer. What the harness\n * pins is that the announced copy TRACKS the current word — nothing about it being\n * spoken.\n *\n * **There is no `getIndex()`, and no `next()` / `previous()` / `jumpTo()` / `reset()`.**\n * The index appears nowhere in the DOM — no attribute, no data hook — so deriving it\n * would mean matching the shown string against a list the harness was never given, and\n * guessing between duplicates. And the component renders no controls at all: rotation is\n * a timer or a method call on the instance, so a harness \"click\" would have nothing to\n * click and reaching into the component would make this a wrapper around the class rather\n * than a reader of the DOM. Drive it from the spec — through the instance, or by\n * advancing the interval — and assert the `nextChange` output for the index.\n *\n * **Nothing here reports the tween.** `duration`, `easing`, `staggerDuration` and\n * `staggerFrom` are passed straight into Web Animation options and never written to the\n * document; `delayFor()` is arithmetic with no rendered trace. So no `getDuration()`, no\n * `getStagger()`, no `isAnimating()`. The pieces do carry inline `opacity` and\n * `transform`, but on the animated path those are the FIRST keyframe — `opacity: 0` on\n * text the user can see perfectly well — which is why the only reading offered is\n * {@link isSettled}, and why it is spelled as a commitment rather than as a state of\n * motion. The host's `overflow: hidden` clipping is layout, and every rect in a unit test\n * is 0×0, so there is no `isClipped()` either.\n *\n * jsdom implements no Web Animations API, so the animated path throws on the first\n * `el.animate` — before any assertion runs. Every spec supplies one half or the other:\n *\n * ```ts\n * // Either: mount under reduced motion, where each swap is instant and nothing tweens.\n * // (jsdom has no matchMedia, so WrPlatform must be provided as a value.)\n * TestBed.configureTestingModule({ providers: [{ provide: WrPlatform, useValue: reducedMotion }] });\n *\n * // Or: stub the API — and remember a stub that never fires `onfinish` never resolves\n * // the exit step, so the rotator stops after one swap.\n * Reflect.set(Element.prototype, 'animate', () => ({ cancel: () => undefined, onfinish: null }));\n * ```\n *\n * @example\n * ```ts\n * const rotator = await loader.getHarness(WrRotatingTextHarness);\n *\n * expect(await rotator.getAccessibleText()).toBe('design');\n * expect(await rotator.getPieces()).toEqual(['d', 'e', 's', 'i', 'g', 'n']);\n * expect(await rotator.isAnimatedLayerHidden()).toBe(true);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrRotatingTextHarness extends ComponentHarness {\n static hostSelector = 'wr-rotating-text';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrRotatingTextHarnessFilters = {}): HarnessPredicate<WrRotatingTextHarness> {\n return new HarnessPredicate(WrRotatingTextHarness, options)\n .addOption('text', options.text, (harness, text) =>\n HarnessPredicate.stringMatches(harness.getAccessibleText(), text)\n )\n .addOption('wordCount', options.wordCount, async (harness, count) => (await harness.getWordCount()) === count);\n }\n\n private readonly chars = this.locatorForAll(CHAR);\n\n /**\n * The string showing right now, as the one readable copy holds it.\n *\n * Literally what a screen reader gets, the animated layer being out of the tree — and\n * the thing to assert a rotation against, since it is where `next()`, `previous()`, a\n * clamped `jumpTo()`, `reset()`, the wrap at either end, the stop when `loop` is off and\n * the shrinking-list clamp all become visible.\n *\n * Throws when the copy is missing rather than answering with the pieces' text: an empty\n * string is what a rotator holding an empty list correctly reports, so a plausible\n * nothing here is indistinguishable from the component having lost its accessible name\n * entirely. Ask {@link hasAccessibleCopy} when presence is the question.\n */\n async getAccessibleText(): Promise<string> {\n const copy = await this.locatorForOptional(SR_ONLY)();\n\n if (!copy) {\n throw new Error(\n 'WrRotatingTextHarness.getAccessibleText(): this <wr-rotating-text> renders no ' +\n `${SR_ONLY} span, so it has no accessible text at all — every piece it draws is aria-hidden. ` +\n 'Reading the pieces instead would report a healthy-looking string for a component a screen ' +\n 'reader cannot see.'\n );\n }\n\n return rawText(copy);\n }\n\n /**\n * Everything the animated layer draws, in document order, as one string.\n *\n * A different fact from {@link getAccessibleText}, and the comparison between them is\n * the point: a split that drops its spacer renders `hellothere` while the readable copy\n * stays perfect, and nothing else in the DOM says so.\n *\n * The spacer between words is a non-breaking space (` `), which is a rendering\n * choice rather than a contract, so it is normalized to a plain space here — otherwise\n * every round-trip assertion would have to spell U+00A0 out to pass against a working\n * component. Note that `splitBy=\"lines\"` genuinely does not round-trip: the newline the\n * readable copy carries is drawn as that same spacer.\n */\n async getRenderedText(): Promise<string> {\n return (await rawText(await this.locatorFor(INNER)())).replace(/\\u00a0/g, ' ');\n }\n\n /**\n * The animated pieces, in document order.\n *\n * The granularity actually applied: one piece per grapheme for `splitBy=\"characters\"`,\n * one per word for `\"words\"`, one per line for `\"lines\"`. It is also where the grapheme\n * contract shows — `a🚀b` must come back as three pieces, not four with two broken\n * halves of a surrogate pair.\n */\n async getPieces(): Promise<string[]> {\n return Promise.all((await this.chars()).map(rawText));\n }\n\n /**\n * How many word groups the split produced.\n *\n * Word boundaries survive every granularity: three words stay three groups even when\n * each is exploded into characters, which is what keeps the line wrapping sane. A\n * splitter that flattened the grouping would leave {@link getPieces} correct and break\n * only this.\n */\n async getWordCount(): Promise<number> {\n return (await this.locatorForAll(WORD)()).length;\n }\n\n /**\n * Whether the animated layer is out of the accessibility tree.\n *\n * ONE attribute answers it — `aria-hidden` sits on the `__inner` wrapper, so the pieces\n * inherit it and none carries it itself, which keeps the attribute off the nodes the\n * tween writes to. Asserting it per piece would fail on a component that is behaving.\n *\n * If it regresses, every rotation is spelled out letter by letter beside the copy that\n * was already read, and nothing changes on screen — so no other check catches it.\n */\n async isAnimatedLayerHidden(): Promise<boolean> {\n return (await (await this.locatorFor(INNER)()).getAttribute('aria-hidden')) === 'true';\n }\n\n /**\n * Whether the readable copy is rendered at all.\n *\n * The other half of {@link isAnimatedLayerHidden}, and it exists as a separate question\n * because the failure the two catch together is invisible to each alone: a layer\n * correctly hidden with no readable copy beside it is a control with no accessible text\n * whatsoever, and `isAnimatedLayerHidden() === true` is exactly what it reports.\n */\n async hasAccessibleCopy(): Promise<boolean> {\n return (await this.locatorForOptional(SR_ONLY)()) !== null;\n }\n\n /**\n * Whether the enter step committed its end state on every piece — NOT whether an\n * animation finished.\n *\n * The component writes `opacity: 1` inline on each piece when the swap settles: instantly\n * under reduced motion, and from the tween's `onfinish` otherwise. So this answers \"the\n * new word was actually taken over\", which is the bug the deferred enter exists to\n * prevent — a swap that animates the OLD spans leaves the new ones staged at `opacity: 0`\n * and the text invisible, while the readable copy reports the new word correctly.\n *\n * It is all-or-nothing: one piece left staged is one invisible glyph in the middle of a\n * word. On the animated path it stays `false` until each tween finishes, which in a unit\n * test means forever, since a stubbed `Element.animate` never fires anything — that is\n * an honest answer about a swap that never landed, not a limitation to work around.\n *\n * Throws when there are no pieces: \"every piece is settled\" is true of none of them,\n * and that is also what a component that stopped rendering its text would report.\n */\n async isSettled(): Promise<boolean> {\n const pieces = await this.chars();\n\n if (pieces.length === 0) {\n throw new Error(\n 'WrRotatingTextHarness.isSettled(): this <wr-rotating-text> drew no pieces, so \"settled\" would be ' +\n 'vacuously true — the same answer a rotator that stopped rendering its text would give. Check ' +\n '`texts` is not empty, or ask getPieces().'\n );\n }\n\n const styles = await Promise.all(pieces.map(piece => piece.getAttribute('style')));\n return styles.every(style => SETTLED_OPACITY.test(style ?? ''));\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -263,11 +263,11 @@ class WrRotatingText {
|
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
265
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrRotatingText, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
266
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrRotatingText, isStandalone: true, selector: "wr-rotating-text", inputs: { texts: { classPropertyName: "texts", publicName: "texts", isSignal: true, isRequired: true, transformFunction: null }, rotationInterval: { classPropertyName: "rotationInterval", publicName: "rotationInterval", isSignal: true, isRequired: false, transformFunction: null }, splitBy: { classPropertyName: "splitBy", publicName: "splitBy", isSignal: true, isRequired: false, transformFunction: null }, auto: { classPropertyName: "auto", publicName: "auto", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", 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 }, staggerDuration: { classPropertyName: "staggerDuration", publicName: "staggerDuration", isSignal: true, isRequired: false, transformFunction: null }, staggerFrom: { classPropertyName: "staggerFrom", publicName: "staggerFrom", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { nextChange: "nextChange" }, host: { classAttribute: "wr-rotating-text" }, ngImport: i0, template: "
|
|
266
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: WrRotatingText, isStandalone: true, selector: "wr-rotating-text", inputs: { texts: { classPropertyName: "texts", publicName: "texts", isSignal: true, isRequired: true, transformFunction: null }, rotationInterval: { classPropertyName: "rotationInterval", publicName: "rotationInterval", isSignal: true, isRequired: false, transformFunction: null }, splitBy: { classPropertyName: "splitBy", publicName: "splitBy", isSignal: true, isRequired: false, transformFunction: null }, auto: { classPropertyName: "auto", publicName: "auto", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", 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 }, staggerDuration: { classPropertyName: "staggerDuration", publicName: "staggerDuration", isSignal: true, isRequired: false, transformFunction: null }, staggerFrom: { classPropertyName: "staggerFrom", publicName: "staggerFrom", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { nextChange: "nextChange" }, host: { classAttribute: "wr-rotating-text" }, ngImport: i0, template: "<!--\n The readable text, once: the spans below are per-character. Deliberately NOT a\n live region \u2014 `auto` rotates every two seconds by default, and the APG's answer\n for self-advancing content is silence, not an announcement every two seconds.\n-->\n\n<span class=\"wr-rotating-text__sr-only\">{{ current() }}</span>\n\n<!-- Animated visual content. -->\n<span class=\"wr-rotating-text__inner\" aria-hidden=\"true\">\n @for (word of words(); track $index) {\n <span class=\"wr-rotating-text__word\">\n @for (char of word.characters; track $index) {\n <span class=\"wr-rotating-text__char\">{{ char }}</span>\n }\n @if (word.needsSpace) {\n <span class=\"wr-rotating-text__space\"> </span>\n }\n </span>\n }\n</span>\n", styles: [".wr-rotating-text{display:inline-flex;align-items:center;overflow:hidden;vertical-align:bottom}.wr-rotating-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-rotating-text__inner{display:inline-flex;flex-wrap:wrap}.wr-rotating-text__word{display:inline-flex;align-items:center}.wr-rotating-text__char{display:inline-block;will-change:transform,opacity}.wr-rotating-text__space{display:inline;white-space:pre}\n"], encapsulation: i0.ViewEncapsulation.None });
|
|
267
267
|
}
|
|
268
268
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: WrRotatingText, decorators: [{
|
|
269
269
|
type: Component,
|
|
270
|
-
args: [{ selector: 'wr-rotating-text', encapsulation: ViewEncapsulation.None, host: { class: 'wr-rotating-text' }, template: "
|
|
270
|
+
args: [{ selector: 'wr-rotating-text', encapsulation: ViewEncapsulation.None, host: { class: 'wr-rotating-text' }, template: "<!--\n The readable text, once: the spans below are per-character. Deliberately NOT a\n live region \u2014 `auto` rotates every two seconds by default, and the APG's answer\n for self-advancing content is silence, not an announcement every two seconds.\n-->\n\n<span class=\"wr-rotating-text__sr-only\">{{ current() }}</span>\n\n<!-- Animated visual content. -->\n<span class=\"wr-rotating-text__inner\" aria-hidden=\"true\">\n @for (word of words(); track $index) {\n <span class=\"wr-rotating-text__word\">\n @for (char of word.characters; track $index) {\n <span class=\"wr-rotating-text__char\">{{ char }}</span>\n }\n @if (word.needsSpace) {\n <span class=\"wr-rotating-text__space\"> </span>\n }\n </span>\n }\n</span>\n", styles: [".wr-rotating-text{display:inline-flex;align-items:center;overflow:hidden;vertical-align:bottom}.wr-rotating-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-rotating-text__inner{display:inline-flex;flex-wrap:wrap}.wr-rotating-text__word{display:inline-flex;align-items:center}.wr-rotating-text__char{display:inline-block;will-change:transform,opacity}.wr-rotating-text__space{display:inline;white-space:pre}\n"] }]
|
|
271
271
|
}], ctorParameters: () => [], propDecorators: { texts: [{ type: i0.Input, args: [{ isSignal: true, alias: "texts", required: true }] }], rotationInterval: [{ type: i0.Input, args: [{ isSignal: true, alias: "rotationInterval", required: false }] }], splitBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "splitBy", required: false }] }], auto: [{ type: i0.Input, args: [{ isSignal: true, alias: "auto", required: false }] }], loop: [{ type: i0.Input, args: [{ isSignal: true, alias: "loop", required: false }] }], duration: [{ type: i0.Input, args: [{ isSignal: true, alias: "duration", required: false }] }], easing: [{ type: i0.Input, args: [{ isSignal: true, alias: "easing", required: false }] }], staggerDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "staggerDuration", required: false }] }], staggerFrom: [{ type: i0.Input, args: [{ isSignal: true, alias: "staggerFrom", required: false }] }], nextChange: [{ type: i0.Output, args: ["nextChange"] }] } });
|
|
272
272
|
|
|
273
273
|
/**
|