ngwr 13.0.1 → 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -6
- package/color-picker/styles/_index.scss +43 -5
- package/fesm2022/ngwr-alert.mjs +5 -5
- package/fesm2022/ngwr-alert.mjs.map +1 -1
- package/fesm2022/ngwr-back-top.mjs +11 -1
- package/fesm2022/ngwr-back-top.mjs.map +1 -1
- package/fesm2022/ngwr-blur-text.mjs +13 -2
- package/fesm2022/ngwr-blur-text.mjs.map +1 -1
- package/fesm2022/ngwr-button.mjs +4 -4
- package/fesm2022/ngwr-button.mjs.map +1 -1
- package/fesm2022/ngwr-calendar-testing.mjs +15 -0
- package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
- package/fesm2022/ngwr-calendar.mjs +51 -5
- package/fesm2022/ngwr-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text.mjs +20 -1
- package/fesm2022/ngwr-circular-text.mjs.map +1 -1
- package/fesm2022/ngwr-collapse.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker-testing.mjs +23 -7
- package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker.mjs +89 -10
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-command-palette.mjs +27 -3
- package/fesm2022/ngwr-command-palette.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu.mjs +7 -1
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-date-adapters-fns.mjs +101 -33
- package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker-testing.mjs +10 -2
- package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker.mjs +103 -13
- package/fesm2022/ngwr-date-picker.mjs.map +1 -1
- package/fesm2022/ngwr-date.mjs +480 -79
- package/fesm2022/ngwr-date.mjs.map +1 -1
- package/fesm2022/ngwr-drag-drop.mjs +30 -2
- package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +2 -1
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar.mjs +1 -1
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-file-upload.mjs +52 -12
- package/fesm2022/ngwr-file-upload.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-en.mjs +63 -1
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +28 -1
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-i18n.mjs +150 -22
- package/fesm2022/ngwr-i18n.mjs.map +1 -1
- package/fesm2022/ngwr-input-number.mjs +3 -3
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-input-otp.mjs +21 -3
- package/fesm2022/ngwr-input-otp.mjs.map +1 -1
- package/fesm2022/ngwr-input.mjs +14 -7
- package/fesm2022/ngwr-input.mjs.map +1 -1
- package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
- package/fesm2022/ngwr-knob.mjs +20 -2
- package/fesm2022/ngwr-knob.mjs.map +1 -1
- package/fesm2022/ngwr-loading-bar-router.mjs +73 -0
- package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -0
- package/fesm2022/ngwr-loading-bar.mjs +21 -32
- package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
- package/fesm2022/ngwr-mention.mjs +104 -14
- package/fesm2022/ngwr-mention.mjs.map +1 -1
- package/fesm2022/ngwr-pagination-testing.mjs +11 -10
- package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
- package/fesm2022/ngwr-pagination.mjs +65 -30
- package/fesm2022/ngwr-pagination.mjs.map +1 -1
- package/fesm2022/ngwr-popconfirm.mjs +3 -2
- package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +2 -1
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +93 -34
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
- package/fesm2022/ngwr-squircle.mjs +2 -1
- package/fesm2022/ngwr-squircle.mjs.map +1 -1
- package/fesm2022/ngwr-statistic.mjs +35 -4
- package/fesm2022/ngwr-statistic.mjs.map +1 -1
- package/fesm2022/ngwr-table.mjs +35 -12
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-tabs-router.mjs +158 -0
- package/fesm2022/ngwr-tabs-router.mjs.map +1 -0
- package/fesm2022/ngwr-tabs-testing.mjs +13 -11
- package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
- package/fesm2022/ngwr-tabs.mjs +98 -10
- package/fesm2022/ngwr-tabs.mjs.map +1 -1
- package/fesm2022/ngwr-textarea.mjs +20 -1
- package/fesm2022/ngwr-textarea.mjs.map +1 -1
- package/fesm2022/ngwr-tour.mjs +1 -1
- package/fesm2022/ngwr-tour.mjs.map +1 -1
- package/fesm2022/ngwr-transfer.mjs +2 -2
- package/fesm2022/ngwr-transfer.mjs.map +1 -1
- package/fesm2022/ngwr-tree.mjs +18 -2
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-utils.mjs +37 -1
- package/fesm2022/ngwr-utils.mjs.map +1 -1
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-window-testing.mjs +10 -2
- package/fesm2022/ngwr-window-testing.mjs.map +1 -1
- package/fesm2022/ngwr-window.mjs +4 -4
- package/fesm2022/ngwr-window.mjs.map +1 -1
- package/i18n/en.json +33 -6
- package/i18n/ru.json +33 -6
- package/llms-full.txt +20 -11
- package/llms.txt +47 -16
- package/mcp/catalog.js +3 -3
- package/mcp/providers.js +11 -0
- package/package.json +9 -1
- package/schematics/migrations/v13/index.js +101 -0
- package/schematics/migrations/v14/index.js +422 -0
- package/schematics/migrations.json +10 -0
- package/schematics/page/templates.js +1 -1
- package/schematics/use/symbol-map.json +1 -0
- package/skills/ngwr/SKILL.md +4 -1
- package/skills/ngwr/references/catalog.md +2 -0
- package/skills/ngwr/references/setup.md +4 -0
- package/stepper/styles/_index.scss +19 -1
- package/textarea/styles/_index.scss +14 -6
- package/toast/styles/_index.scss +12 -4
- package/types/ngwr-alert.d.ts +3 -3
- package/types/ngwr-blur-text.d.ts +8 -0
- package/types/ngwr-button.d.ts +2 -2
- package/types/ngwr-calendar-testing.d.ts +21 -4
- package/types/ngwr-calendar.d.ts +37 -0
- package/types/ngwr-circular-text.d.ts +9 -0
- package/types/ngwr-collapse.d.ts +19 -15
- package/types/ngwr-color-picker-testing.d.ts +10 -4
- package/types/ngwr-color-picker.d.ts +70 -2
- package/types/ngwr-command-palette.d.ts +14 -0
- package/types/ngwr-context-menu.d.ts +1 -1
- package/types/ngwr-date-adapters-fns.d.ts +71 -29
- package/types/ngwr-date-picker-testing.d.ts +206 -185
- package/types/ngwr-date-picker.d.ts +7 -0
- package/types/ngwr-date.d.ts +143 -13
- package/types/ngwr-drag-drop.d.ts +19 -0
- package/types/ngwr-dropdown.d.ts +1 -1
- package/types/ngwr-event-calendar-testing.d.ts +12 -9
- package/types/ngwr-file-upload.d.ts +31 -3
- package/types/ngwr-i18n.d.ts +89 -7
- package/types/ngwr-input-number.d.ts +1 -1
- package/types/ngwr-input-otp.d.ts +3 -0
- package/types/ngwr-input.d.ts +11 -4
- package/types/ngwr-knob-testing.d.ts +8 -4
- package/types/ngwr-knob.d.ts +18 -0
- package/types/ngwr-loading-bar-router.d.ts +35 -0
- package/types/ngwr-loading-bar.d.ts +19 -9
- package/types/ngwr-mention.d.ts +13 -0
- package/types/ngwr-pagination-testing.d.ts +11 -10
- package/types/ngwr-pagination.d.ts +39 -10
- package/types/ngwr-popconfirm.d.ts +1 -1
- package/types/ngwr-popover.d.ts +1 -1
- package/types/ngwr-select.d.ts +65 -10
- package/types/ngwr-splitter-testing.d.ts +9 -4
- package/types/ngwr-squircle.d.ts +1 -1
- package/types/ngwr-statistic.d.ts +22 -2
- package/types/ngwr-table.d.ts +24 -5
- package/types/ngwr-tabs-router.d.ts +67 -0
- package/types/ngwr-tabs-testing.d.ts +12 -10
- package/types/ngwr-tabs.d.ts +103 -6
- package/types/ngwr-textarea.d.ts +9 -0
- package/types/ngwr-tree.d.ts +7 -0
- package/types/ngwr-utils.d.ts +33 -1
- package/types/ngwr-version.d.ts +1 -1
- package/types/ngwr-window-testing.d.ts +11 -3
- package/types/ngwr-window.d.ts +11 -4
- package/window/styles/_index.scss +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-mention.mjs","sources":["../../../projects/lib/mention/caret.ts","../../../projects/lib/mention/mention-panel.ts","../../../projects/lib/mention/mention-panel.html","../../../projects/lib/mention/mention.ts","../../../projects/lib/mention/ngwr-mention.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 * CSS properties whose values must be copied onto the mirror to match layout.\n *\n * `as const satisfies` keeps each name checked against `CSSStyleDeclaration`\n * while narrowing the element type to these string-valued literals — the wider\n * `keyof CSSStyleDeclaration` also spans its methods (`setProperty`, `item`, …),\n * which makes the indexed read below look like an unbound method.\n */\nconst MIRROR_PROPS = [\n 'boxSizing',\n 'width',\n 'height',\n 'overflowX',\n 'overflowY',\n 'borderTopWidth',\n 'borderRightWidth',\n 'borderBottomWidth',\n 'borderLeftWidth',\n 'paddingTop',\n 'paddingRight',\n 'paddingBottom',\n 'paddingLeft',\n 'fontStyle',\n 'fontVariant',\n 'fontWeight',\n 'fontStretch',\n 'fontSize',\n 'fontSizeAdjust',\n 'lineHeight',\n 'fontFamily',\n 'textAlign',\n 'textTransform',\n 'textIndent',\n 'textDecoration',\n 'letterSpacing',\n 'wordSpacing',\n 'tabSize',\n 'whiteSpace',\n 'wordWrap',\n] as const satisfies readonly (keyof CSSStyleDeclaration)[];\n\n/**\n * Compute the viewport coordinates of a textarea / input caret at a given\n * `position`. Uses the \"mirror div\" technique — a hidden div with the\n * same styling holds the text up to the caret, and we read the bounding\n * rect of a probe span placed at the boundary.\n *\n * Returns viewport-relative `{ top, left }` — pass straight to a CDK\n * Overlay anchored at that point.\n */\nexport function getCaretCoordinates(\n input: HTMLTextAreaElement | HTMLInputElement,\n position: number\n): { top: number; left: number; lineHeight: number } {\n const doc = input.ownerDocument;\n const style = getComputedStyle(input);\n const mirror = doc.createElement('div');\n\n for (const prop of MIRROR_PROPS) {\n mirror.style[prop] = style[prop];\n }\n mirror.style.position = 'absolute';\n mirror.style.visibility = 'hidden';\n mirror.style.top = '0';\n mirror.style.left = '0';\n mirror.style.whiteSpace = 'pre-wrap';\n mirror.style.wordWrap = 'break-word';\n\n // <input> is single-line — force whitespace behaviour so coords are sane.\n if (input.tagName === 'INPUT') {\n mirror.style.whiteSpace = 'pre';\n mirror.style.height = 'auto';\n mirror.style.width = 'auto';\n mirror.style.overflow = 'hidden';\n }\n\n const before = input.value.substring(0, position);\n mirror.textContent = before;\n\n const probe = doc.createElement('span');\n // Single character so the span has measurable size even at the end.\n probe.textContent = input.value.substring(position) || '.';\n mirror.appendChild(probe);\n\n doc.body.appendChild(mirror);\n const probeRect = probe.getBoundingClientRect();\n const mirrorRect = mirror.getBoundingClientRect();\n const lineHeight = parseFloat(style.lineHeight) || parseFloat(style.fontSize) * 1.2;\n doc.body.removeChild(mirror);\n\n const inputRect = input.getBoundingClientRect();\n return {\n top: inputRect.top + (probeRect.top - mirrorRect.top) - input.scrollTop,\n left: inputRect.left + (probeRect.left - mirrorRect.left) - input.scrollLeft,\n lineHeight,\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 { Component, ViewEncapsulation, input, output } from '@angular/core';\n\nimport type { WrMentionItem } from './interfaces';\n\n/**\n * The id of the option at `index`, given the listbox's own id.\n *\n * Lives here, and is used from both sides, because this format is a contract\n * between two files: the panel stamps it onto each `<li>`, and the directive\n * names one of them in `aria-activedescendant`. Written out twice, the two would\n * drift and the reference would silently dangle — and a dangling\n * `aria-activedescendant` is not an error the browser reports, it just stops\n * announcing options.\n *\n * @internal\n */\nexport function wrMentionOptionId(listboxId: string, index: number): string {\n return `${listboxId}-opt-${index}`;\n}\n\n/**\n * Overlay panel rendered by {@link WrMention}. Not intended for\n * direct use — the directive instantiates it via a ComponentPortal.\n *\n * @internal — the same tag its twin `WrPopconfirmPanel` already carried. It says\n * the intent; it does not by itself keep the class out of the generated API\n * tables, because `WrTableFilter` and `WrTableSort` are tagged AND documented —\n * see `EXPORTED_BUT_INTERNAL` in `scripts/lib/extract-api.ts`.\n */\n@Component({\n selector: 'wr-mention-panel',\n templateUrl: './mention-panel.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-mention-panel' },\n})\nexport class WrMentionPanel {\n readonly items = input<readonly WrMentionItem[]>([]);\n readonly activeIndex = input(0);\n readonly displayWith = input<(item: WrMentionItem) => string>(item => item.label);\n\n /**\n * Id of the `<ul role=\"listbox\">`, minted by the directive and pointed at by\n * its `aria-controls`. Owned there rather than here because the panel is\n * disposed and rebuilt whenever the caret leaves a mention, while the\n * attribute on the host has to stay put.\n *\n * Plain `input('')`, never `input.required` — a ComponentPortal-created\n * component throws on the first change detection if a required input is unset.\n */\n readonly listboxId = input('');\n\n /** Accessible name for the listbox. ARIA requires one; the directive supplies it from the catalog. */\n readonly listLabel = input('');\n\n protected optionId(index: number): string {\n return wrMentionOptionId(this.listboxId(), index);\n }\n\n /** Emitted when the user picks an item (click). */\n readonly picked = output<WrMentionItem>();\n\n /** Emitted on mouseenter — used by the directive to track hover. */\n readonly hovered = output<number>();\n\n protected onPick(item: WrMentionItem, event: MouseEvent): void {\n // mousedown semantics — keep focus on the input.\n event.preventDefault();\n this.picked.emit(item);\n }\n}\n","<!--\n No empty branch: `activate()` calls `close()` and returns before `open()` when\n nothing matches, so the panel is never rendered with zero items. The \"No\n matches\" `<div>` that used to live here was unreachable — and a hard-coded\n English string besides. The zero-match case is announced through the\n directive's live region instead.\n-->\n<ul class=\"wr-mention-panel__list\" role=\"listbox\" [attr.id]=\"listboxId()\" [attr.aria-label]=\"listLabel()\">\n @for (item of items(); track $index) {\n <li\n class=\"wr-mention-panel__option\"\n role=\"option\"\n [attr.id]=\"optionId($index)\"\n [class.wr-mention-panel__option--active]=\"$index === activeIndex()\"\n [attr.aria-selected]=\"$index === activeIndex()\"\n (mouseenter)=\"hovered.emit($index)\"\n (mousedown)=\"onPick(item, $event)\"\n >\n {{ displayWith()(item) }}\n </li>\n }\n</ul>\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 { coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n type ConnectedPosition,\n type FlexibleConnectedPositionStrategyOrigin,\n type OverlayRef,\n ScrollStrategyOptions,\n} from '@angular/cdk/overlay';\nimport { ComponentPortal } from '@angular/cdk/portal';\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n type ComponentRef,\n DestroyRef,\n Directive,\n ElementRef,\n PLATFORM_ID,\n computed,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\n\nimport { readI18nText, useI18nFormatter } from 'ngwr/i18n';\nimport { WR_OVERLAY } from 'ngwr/overlay';\n\nimport { getCaretCoordinates } from './caret';\nimport type { WrMentionCommit, WrMentionItem } from './interfaces';\nimport { WrMentionPanel, wrMentionOptionId } from './mention-panel';\n\ninterface ActiveState {\n trigger: string;\n query: string;\n startIndex: number;\n}\n\n/**\n * Deterministic under prerender, unlike `randomId()` — `aria-controls` is a\n * static host binding, so it renders on the server too, and a random value would\n * differ between that render and the hydrated client.\n */\nlet mentionUid = 0;\n\n/** Gap between the caret's line and the panel, on whichever side it opens. */\nconst CARET_GAP = 4;\n\n/**\n * Below the caret's line first, above it when there is no room — the two sides\n * a caret-anchored list can take. There is no horizontal pair: a panel that\n * jumped to the other side of the caret would read as a different widget, so a\n * caret near the inline end is handled by `withPush` sliding the panel back\n * onto the screen instead. `overlayX: 'start'` is the logical corner, so RTL\n * hangs the panel from the caret's other side without a second table.\n */\nconst CARET_POSITIONS: ConnectedPosition[] = [\n { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' },\n { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' },\n];\n\n/**\n * `@`-style mention picker for `<textarea>` and `<input>`. Listens for\n * trigger characters (default `'@'`); when typed at a word boundary, an\n * overlay panel opens at the caret and shows filtered options. Pick with\n * Enter / Tab / click — the trigger + query is replaced with the inserted\n * value plus a trailing space.\n *\n * @example\n * ```html\n * <textarea\n * wrMention\n * [wrMentionItems]=\"users\"\n * [displayWith]=\"userLabel\"\n * (wrMentionSelected)=\"onMention($event)\"\n * ></textarea>\n * ```\n *\n * @see https://ngwr.dev/reference/components/mention\n */\n@Directive({\n selector: '[wrMention]',\n host: {\n '(input)': 'onInput()',\n '(click)': 'onClick()',\n '(keyup)': 'onCaretMove($event)',\n '(keydown)': 'onKeydown($event)',\n '(blur)': 'onBlur()',\n\n // The host keeps its implicit `role=\"textbox\"`. It is NOT a combobox: this\n // field holds prose and the mention is one fragment inside it, not the\n // field's value. `role=\"combobox\"` is also flatly disallowed on `<textarea>`\n // by ARIA in HTML (axe: `aria-allowed-role`), and it would drop\n // `aria-multiline`, which combobox does not support — so a screen reader\n // would stop reporting the field as multi-line for the whole editing\n // session, not just while the popup is open.\n //\n // Static, because `aria-autocomplete` describes a permanent capability of\n // the field; ARIA says authors SHOULD NOT toggle it to signal that\n // suggestions are currently showing. Being static is also what makes the\n // feature discoverable — it is announced on focus, before the user types a\n // trigger char.\n 'aria-autocomplete': 'list',\n // Required alongside `aria-autocomplete=\"list\"`, and must name the popup's\n // role. Measured clean on a textbox with axe-core 4.13.\n 'aria-haspopup': 'listbox',\n // Also static, and deliberately left pointing at an id that only exists\n // while the panel is open: gating it would mean gating `aria-autocomplete`\n // too. An unresolved `aria-controls` is a manual-review note in axe, never a\n // violation — unlike `aria-activedescendant`, which is an author error.\n //\n // This is the attribute that legalises the whole design: for a textbox\n // controlling a popup, `aria-activedescendant` is allowed to name an element\n // owned by the *controlled* element, which is how the reference reaches into\n // the CDK overlay's separate DOM subtree at all.\n '[attr.aria-controls]': 'listboxId',\n '[attr.aria-activedescendant]': 'activeOptionId()',\n\n // No `aria-expanded`. It is not a supported state of `role=\"textbox\"` —\n // axe rates it a *critical* `aria-allowed-attr` violation — so the\n // \"a list appeared\" signal cannot be an ARIA state here. That is what the\n // live region below is for; it is load-bearing, not decoration.\n },\n})\nexport class WrMention<T extends WrMentionItem = WrMentionItem> {\n /** Items to filter against the typed query. */\n readonly wrMentionItems = input<readonly T[]>([]);\n\n /** Trigger characters that open the panel. @default ['@'] */\n readonly triggers = input<readonly string[]>(['@']);\n\n /** Maps an item to its display label. @default `item.label` */\n readonly displayWith = input<(item: T) => string>((item: T) => item.label);\n\n /**\n * Returns the text to insert into the textarea on commit. Receives the\n * picked item and the trigger char. @default `${trigger}${displayWith(item)}`\n */\n readonly valueWith = input<((item: T, trigger: string) => string) | null>(null);\n\n /** Custom filter. @default case-insensitive `includes` over `displayWith(item)`. */\n readonly filterWith = input<((query: string, item: T) => boolean) | null>(null);\n\n /** Maximum number of items shown in the panel. @default 8 */\n readonly maxResults = input(8, { transform: (v: unknown): number => Math.max(1, coerceNumberProperty(v, 8)) });\n\n /** Emits the selected item, trigger, and query whenever the user commits. */\n readonly wrMentionSelected = output<WrMentionCommit<T>>();\n\n private readonly host = inject<ElementRef<HTMLTextAreaElement | HTMLInputElement>>(ElementRef);\n private readonly overlay = inject(WR_OVERLAY);\n private readonly scrollStrategies = inject(ScrollStrategyOptions);\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n private overlayRef: OverlayRef | null = null;\n private panelRef: ComponentRef<WrMentionPanel> | null = null;\n /**\n * Where the caret sits INSIDE the field — its offset from the field's\n * top-left corner, plus the height of the line it is on. Mutated in place by\n * `open()`, never replaced: `caretAnchor()` closes over this object.\n */\n private readonly caretOffset = { dx: 0, dy: 0, lineHeight: 0 };\n private state: ActiveState | null = null;\n private readonly activeIndex = signal(0);\n private readonly filteredItems = signal<readonly T[]>([]);\n private readonly isOpen = signal(false);\n\n /** @internal — read by the `aria-controls` host binding. */\n protected readonly listboxId = `wr-mention-listbox-${++mentionUid}`;\n\n private readonly listLabel = readI18nText('mention.listbox', 'Mentions');\n private readonly availableText = useI18nFormatter('mention.available', 'Matches available: {{count}}');\n private readonly insertedText = useI18nFormatter('mention.inserted', 'Inserted: {{label}}');\n\n /**\n * `aria-activedescendant`, or `null`.\n *\n * Gated hard on `isOpen()`, which is only set once the panel's options are\n * actually in the DOM. An id that resolves to nothing is an author error, and\n * the failure mode is silent: the browser does not complain, it simply stops\n * firing the event that makes a screen reader speak the option. Same\n * discipline `tree.ts` uses for its virtualized rows.\n */\n protected readonly activeOptionId = computed<string | null>(() => {\n if (!this.isOpen()) return null;\n const index = this.activeIndex();\n return index >= 0 && index < this.filteredItems().length ? wrMentionOptionId(this.listboxId, index) : null;\n });\n\n /**\n * Polite live region, parked on `<body>`.\n *\n * Not inside the panel: `close()` disposes the overlay on every keystroke that\n * leaves a mention, so a region living there would be recreated constantly —\n * and a live region has to be in the DOM *before* its text changes to be\n * announced at all. It also could not announce the two cases that matter most,\n * since in both the panel is already gone: \"nothing matched\" and \"inserted X\".\n *\n * Not a sibling of the host either — that would land inside the consumer's\n * markup and start matching their `+`, `~` and `:nth-child()` selectors.\n */\n private liveRegion: HTMLElement | null = null;\n\n constructor() {\n if (this.isBrowser) {\n const doc = this.host.nativeElement.ownerDocument;\n const region = doc.createElement('div');\n region.className = 'wr-mention__status';\n region.setAttribute('role', 'status');\n doc.body.appendChild(region);\n this.liveRegion = region;\n }\n\n this.destroyRef.onDestroy(() => {\n this.close();\n this.liveRegion?.remove();\n this.liveRegion = null;\n });\n }\n\n private announce(message: string): void {\n if (this.liveRegion) this.liveRegion.textContent = message;\n }\n\n // Host listeners\n\n /** @internal */\n protected onInput(): void {\n this.detect();\n }\n\n /** @internal */\n protected onClick(): void {\n this.detect();\n }\n\n /** @internal */\n protected onCaretMove(event: KeyboardEvent): void {\n // Every key `onKeydown` already consumed, Escape included: its keyup would\n // re-detect at a caret that is still sitting inside the mention and reopen the\n // panel Escape had just dismissed. A synthetic keydown-only Escape hides that\n // — the real gesture is a pair, and the second half undid the first.\n if (\n event.key === 'ArrowUp' ||\n event.key === 'ArrowDown' ||\n event.key === 'Enter' ||\n event.key === 'Tab' ||\n event.key === 'Escape'\n ) {\n return;\n }\n this.detect();\n }\n\n /** @internal */\n protected onKeydown(event: KeyboardEvent): void {\n if (!this.state) return;\n const list = this.filteredItems();\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n if (list.length > 0) this.setActive((this.activeIndex() + 1) % list.length);\n break;\n case 'ArrowUp':\n event.preventDefault();\n if (list.length > 0) this.setActive((this.activeIndex() - 1 + list.length) % list.length);\n break;\n case 'Enter':\n case 'Tab': {\n const item = list[this.activeIndex()];\n if (item) {\n event.preventDefault();\n this.commit(item);\n }\n break;\n }\n case 'Escape':\n event.preventDefault();\n // The panel is the innermost thing Escape can dismiss, so it keeps the\n // key: without this, the same press also closed the dialog / drawer /\n // window the field sits in.\n //\n // `preventDefault()` alone buys nothing — every host overlay matches on\n // `event.key` and none of them looks at `defaultPrevented` — and letting\n // CDK's `OverlayKeyboardDispatcher` sort it out does not work either.\n // The dispatcher walks the stack newest-first and stops at the first\n // overlay with a `keydownEvents()` subscriber; this panel has none (it\n // is driven from the field's own handler, which runs at the target\n // before the body-phase dispatcher), so Escape falls straight through to\n // the overlay underneath.\n event.stopPropagation();\n this.close();\n break;\n default:\n break;\n }\n }\n\n /** @internal */\n protected onBlur(): void {\n // Defer so option mousedown lands first.\n setTimeout(() => this.close(), 120);\n }\n\n // Detection\n\n private detect(): void {\n const el = this.host.nativeElement;\n const caret = el.selectionStart ?? 0;\n const text = el.value.substring(0, caret);\n const triggers = this.triggers();\n\n // Walk back from the caret looking for a trigger char with whitespace\n // (or start-of-string) immediately before it. Stop on whitespace.\n let i = caret;\n while (i > 0) {\n const ch = text[i - 1];\n if (triggers.includes(ch)) {\n const before = i - 2 >= 0 ? text[i - 2] : '';\n if (i - 2 < 0 || /\\s/.test(before)) {\n const query = text.substring(i);\n this.activate(ch, query, i - 1);\n return;\n }\n break;\n }\n if (/\\s/.test(ch)) break;\n i--;\n }\n this.close();\n }\n\n private activate(trigger: string, query: string, startIndex: number): void {\n this.state = { trigger, query, startIndex };\n const previousCount = this.filteredItems().length;\n const filtered = this.filter(query).slice(0, this.maxResults());\n this.filteredItems.set(filtered);\n if (filtered.length === 0) {\n // Announce *after* closing: `close()` clears the region, so announcing\n // first would wipe the message before a screen reader reached it.\n this.close();\n this.announce(this.availableText({ count: 0 }));\n return;\n }\n this.setActive(Math.min(this.activeIndex(), filtered.length - 1));\n this.open();\n // Only when the size actually changes. Re-announcing the same count on every\n // keystroke would talk over the per-option speech from activedescendant.\n if (filtered.length !== previousCount) this.announce(this.availableText({ count: filtered.length }));\n }\n\n private filter(query: string): readonly T[] {\n const items = this.wrMentionItems();\n const custom = this.filterWith();\n const display = this.displayWith();\n if (!query) return items;\n if (custom) return items.filter(item => custom(query, item));\n const q = query.toLowerCase();\n return items.filter(item => display(item).toLowerCase().includes(q));\n }\n\n private setActive(i: number): void {\n this.activeIndex.set(i);\n this.panelRef?.setInput('activeIndex', i);\n }\n\n // Overlay\n\n /**\n * The overlay's anchor: the caret's line box, grown by the gap the panel\n * keeps from the text, as a zero-WIDTH virtual origin in viewport\n * coordinates. Inflating the rect rather than passing `offsetY` is what keeps\n * `withPush` honest — CDK applies a position offset as a `transform` AFTER it\n * has clamped, so a pushed panel would land the offset off the screen edge.\n *\n * Live getters, not a snapshot, and that is the whole of the scroll fix.\n * `getCaretCoordinates` answers in viewport coordinates, which a\n * `GlobalPositionStrategy` froze at the instant of the keystroke: the panel\n * then stayed pinned to the screen while the page scrolled out from under it\n * (150px of scroll, 150px of drift, ending up over unrelated content). CDK\n * re-reads a virtual origin on every `apply()`, so pinning it to the FIELD's\n * live rect is what turns `reposition()` into a real re-anchor.\n *\n * Only the caret's offset inside the field is captured up front. Measuring it\n * runs the mirror-div pass in `caret.ts` — a full layout read, far too\n * expensive to repeat per scroll event — and it cannot change without a\n * keystroke, which re-enters `open()` anyway.\n */\n private caretAnchor(): FlexibleConnectedPositionStrategyOrigin {\n const field = this.host.nativeElement;\n const offset = this.caretOffset;\n return {\n width: 0,\n get x(): number {\n return field.getBoundingClientRect().left + offset.dx;\n },\n get y(): number {\n return field.getBoundingClientRect().top + offset.dy - CARET_GAP;\n },\n get height(): number {\n return offset.lineHeight + CARET_GAP * 2;\n },\n };\n }\n\n private open(): void {\n const el = this.host.nativeElement;\n const caret = el.selectionStart ?? 0;\n const { top, left, lineHeight } = getCaretCoordinates(el, caret);\n const field = el.getBoundingClientRect();\n this.caretOffset.dx = left - field.left;\n this.caretOffset.dy = top - field.top;\n this.caretOffset.lineHeight = lineHeight;\n\n if (!this.overlayRef) {\n this.overlayRef = this.overlay.create({\n // Flexible, not global: a global strategy has no anchor and no fallback\n // positions, so the panel was always drawn downward from the caret no\n // matter how little room was left below it — half the list ended up\n // under the fold, unreachable by pointer AND invisible to a sighted\n // keyboard user walking it with `aria-activedescendant` (WCAG 2.4.11).\n positionStrategy: this.overlay\n .position()\n .flexibleConnectedTo(this.caretAnchor())\n // The panel sizes itself (`max-height` on the list); the flip above\n // and `withPush` are the whole answer to a cramped viewport.\n .withFlexibleDimensions(false)\n .withPush(true)\n .withPositions(CARET_POSITIONS),\n scrollStrategy: this.scrollStrategies.reposition(),\n panelClass: 'wr-mention-overlay',\n });\n\n const portal = new ComponentPortal(WrMentionPanel);\n const ref = this.overlayRef.attach(portal);\n this.panelRef = ref;\n ref.setInput('displayWith', this.displayWith());\n ref.setInput('listboxId', this.listboxId);\n // Read once here rather than bound: the panel is created per open, and by\n // then the catalog has long landed.\n ref.setInput('listLabel', this.listLabel());\n ref.instance.picked.subscribe(item => this.commit(item as T));\n ref.instance.hovered.subscribe(i => this.setActive(i));\n }\n // No `else` re-building the strategy: the anchor reads `caretOffset` and the\n // field's live rect, both of which the lines above have already refreshed.\n\n this.panelRef?.setInput('items', this.filteredItems());\n this.panelRef?.setInput('activeIndex', this.activeIndex());\n\n // Render the panel's own view NOW, before `aria-activedescendant` is allowed\n // to name one of its options. `attach()` inserts the host element but leaves\n // it empty — the panel is a separate root view, so on the first open the\n // `<li>` being referenced does not exist anywhere in the document yet.\n // Waiting for the next tick would leave a window where the reference dangles\n // and the announcement is lost with no error anywhere.\n this.panelRef?.changeDetectorRef.detectChanges();\n // Place it only now, with the options rendered: which side of the caret the\n // panel takes is decided by measuring it, and `attach()` defers CDK's own\n // first placement into `afterNextRender` — a frame in which the panel would\n // be painted, full size and fully opaque, at the overlay container's\n // top-left corner.\n this.overlayRef.updatePosition();\n this.isOpen.set(true);\n }\n\n private close(): void {\n if (this.overlayRef) {\n this.overlayRef.dispose();\n this.overlayRef = null;\n this.panelRef = null;\n }\n this.isOpen.set(false);\n this.state = null;\n this.activeIndex.set(0);\n this.filteredItems.set([]);\n this.announce('');\n }\n\n private commit(item: T): void {\n // Snapshot the state up front. `dispatchEvent` below re-enters this directive\n // synchronously: the listener runs `detect()`, and because assigning\n // `el.value` has already parked the caret at the end of the text — past the\n // space this method appends — `detect()` finds no trigger and calls\n // `close()`, which nulls `this.state`. Reading `this.state` after that line\n // threw, so `wrMentionSelected` never fired for anyone.\n const state = this.state;\n if (!state) return;\n\n const el = this.host.nativeElement;\n const value = el.value;\n const caret = el.selectionStart ?? value.length;\n const valueFn = this.valueWith();\n const label = this.displayWith()(item);\n const inserted = valueFn ? valueFn(item, state.trigger) : `${state.trigger}${label}`;\n const replacement = `${inserted} `;\n const before = value.substring(0, state.startIndex);\n const after = value.substring(caret);\n const next = before + replacement + after;\n\n el.value = next;\n el.dispatchEvent(new Event('input', { bubbles: true }));\n const nextCaret = (before + replacement).length;\n el.setSelectionRange(nextCaret, nextCaret);\n this.wrMentionSelected.emit({ item, trigger: state.trigger, query: state.query });\n\n // Announce after `close()`, never before — it clears the live region.\n // Committing is otherwise silent: the panel simply vanishes.\n this.close();\n this.announce(this.insertedText({ label }));\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-mention.mjs","sources":["../../../projects/lib/mention/caret.ts","../../../projects/lib/mention/mention-panel.ts","../../../projects/lib/mention/mention-panel.html","../../../projects/lib/mention/mention.ts","../../../projects/lib/mention/ngwr-mention.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 * CSS properties whose values must be copied onto the mirror to match layout.\n *\n * `as const satisfies` keeps each name checked against `CSSStyleDeclaration`\n * while narrowing the element type to these string-valued literals — the wider\n * `keyof CSSStyleDeclaration` also spans its methods (`setProperty`, `item`, …),\n * which makes the indexed read below look like an unbound method.\n */\nconst MIRROR_PROPS = [\n 'boxSizing',\n // Without this the mirror inherits `<body>`'s direction, and the two disagree\n // whenever the field carries its own `dir` — measured at 230px of error for a\n // 320px RTL field inside an LTR page, which put the panel outside the field\n // entirely. `textAlign` below is the same story from the other side: an RTL\n // field's computed value is the logical `start`, which only resolves to the\n // right edge once the mirror knows the direction.\n 'direction',\n 'width',\n 'height',\n 'overflowX',\n 'overflowY',\n 'borderTopWidth',\n 'borderRightWidth',\n 'borderBottomWidth',\n 'borderLeftWidth',\n 'paddingTop',\n 'paddingRight',\n 'paddingBottom',\n 'paddingLeft',\n 'fontStyle',\n 'fontVariant',\n 'fontWeight',\n 'fontStretch',\n 'fontSize',\n 'fontSizeAdjust',\n 'lineHeight',\n 'fontFamily',\n 'textAlign',\n 'textTransform',\n 'textIndent',\n 'textDecoration',\n 'letterSpacing',\n 'wordSpacing',\n 'tabSize',\n 'whiteSpace',\n 'wordWrap',\n] as const satisfies readonly (keyof CSSStyleDeclaration)[];\n\n/** A caret has no size, and an environment with no layout has no rects at all. */\nconst NO_RECT = { top: 0, left: 0 } as const;\n\n/**\n * Where the browser would draw the caret this collapsed `range` marks.\n *\n * `getClientRects()` before `getBoundingClientRect()`: a collapsed range at a\n * bidi boundary has TWO candidate positions and reports both, and the bounding\n * box of the pair spans the whole run between them. The first rect is the one\n * the caret is actually painted at.\n *\n * Both are optional-called because jsdom implements NEITHER on `Range` — it lays\n * nothing out, so every element rect in it is already 0×0 and a zero here keeps\n * the function answering what the rest of that environment answers rather than\n * throwing halfway through an unrelated spec.\n */\nfunction caretRectOf(range: Range): { readonly top: number; readonly left: number } {\n const rects = range.getClientRects?.();\n if (rects && rects.length > 0) return rects[0];\n return range.getBoundingClientRect?.() ?? NO_RECT;\n}\n\n/**\n * Compute the viewport coordinates of a textarea / input caret at a given\n * `position`. Uses the \"mirror div\" technique — a hidden div with the same\n * styling holds the field's text, and a COLLAPSED RANGE at `position` reports\n * where the caret would be.\n *\n * Returns viewport-relative `{ top, left }` — pass straight to a CDK\n * Overlay anchored at that point.\n *\n * **A range, not a probe span, and the difference only shows in RTL.** The\n * classic version of this technique puts the text before the caret in the div,\n * the text after it in a `<span>`, and reads the span's LEFT edge. That reads\n * the caret's side of the boundary only while the text runs left to right. Under\n * the Unicode BiDi algorithm the span can be laid out to the LEFT of the run it\n * follows, so the edge that faces the caret is its right one — and which edge\n * that is depends on the bidi level of the text at the caret, not on the field's\n * direction, so there is no side to pick. A collapsed range asks the browser\n * where it would draw the caret and gets the reordering for free. Measured\n * against a `contenteditable` twin of the field, in Chromium: the range agrees\n * to within a pixel for Latin, Arabic and mixed text in both directions, where\n * the span was out by up to 300px in RTL (see the spec next door).\n *\n * The `<input>` branch keeps the field's WIDTH for the same reason. It used to\n * force `width: auto` so a long single line could not wrap; but an RTL field\n * right-aligns its text inside its own box, so a shrink-to-fit mirror throws\n * away the only measurement that decides where the text starts. `overflow:\n * hidden` is what keeps the overflow from painting, and `input.scrollLeft`\n * already accounts for a field scrolled past its box.\n */\nexport function getCaretCoordinates(\n input: HTMLTextAreaElement | HTMLInputElement,\n position: number\n): { top: number; left: number; lineHeight: number } {\n const doc = input.ownerDocument;\n const style = getComputedStyle(input);\n const mirror = doc.createElement('div');\n\n for (const prop of MIRROR_PROPS) {\n mirror.style[prop] = style[prop];\n }\n mirror.style.position = 'absolute';\n mirror.style.visibility = 'hidden';\n mirror.style.top = '0';\n mirror.style.left = '0';\n mirror.style.whiteSpace = 'pre-wrap';\n mirror.style.wordWrap = 'break-word';\n\n // <input> is single-line — force whitespace behaviour so coords are sane.\n if (input.tagName === 'INPUT') {\n mirror.style.whiteSpace = 'pre';\n mirror.style.height = 'auto';\n mirror.style.overflow = 'hidden';\n }\n\n // One text node, so the range can address any offset in it. An empty field\n // still needs a line box to measure, hence the fallback character.\n const text = doc.createTextNode(input.value || '.');\n mirror.appendChild(text);\n\n doc.body.appendChild(mirror);\n const range = doc.createRange();\n range.setStart(text, Math.min(position, text.length));\n range.collapse(true);\n const caretRect = caretRectOf(range);\n const mirrorRect = mirror.getBoundingClientRect();\n const lineHeight = parseFloat(style.lineHeight) || parseFloat(style.fontSize) * 1.2;\n doc.body.removeChild(mirror);\n\n const inputRect = input.getBoundingClientRect();\n return {\n top: inputRect.top + (caretRect.top - mirrorRect.top) - input.scrollTop,\n left: inputRect.left + (caretRect.left - mirrorRect.left) - input.scrollLeft,\n lineHeight,\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 { Component, ViewEncapsulation, input, output } from '@angular/core';\n\nimport type { WrMentionItem } from './interfaces';\n\n/**\n * The id of the option at `index`, given the listbox's own id.\n *\n * Lives here, and is used from both sides, because this format is a contract\n * between two files: the panel stamps it onto each `<li>`, and the directive\n * names one of them in `aria-activedescendant`. Written out twice, the two would\n * drift and the reference would silently dangle — and a dangling\n * `aria-activedescendant` is not an error the browser reports, it just stops\n * announcing options.\n *\n * @internal\n */\nexport function wrMentionOptionId(listboxId: string, index: number): string {\n return `${listboxId}-opt-${index}`;\n}\n\n/**\n * Overlay panel rendered by {@link WrMention}. Not intended for\n * direct use — the directive instantiates it via a ComponentPortal.\n *\n * @internal — the same tag its twin `WrPopconfirmPanel` already carried. It says\n * the intent; it does not by itself keep the class out of the generated API\n * tables, because `WrTableFilter` and `WrTableSort` are tagged AND documented —\n * see `EXPORTED_BUT_INTERNAL` in `scripts/lib/extract-api.ts`.\n */\n@Component({\n selector: 'wr-mention-panel',\n templateUrl: './mention-panel.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-mention-panel' },\n})\nexport class WrMentionPanel {\n readonly items = input<readonly WrMentionItem[]>([]);\n readonly activeIndex = input(0);\n readonly displayWith = input<(item: WrMentionItem) => string>(item => item.label);\n\n /**\n * Id of the `<ul role=\"listbox\">`, minted by the directive and pointed at by\n * its `aria-controls`. Owned there rather than here because the panel is\n * disposed and rebuilt whenever the caret leaves a mention, while the\n * attribute on the host has to stay put.\n *\n * Plain `input('')`, never `input.required` — a ComponentPortal-created\n * component throws on the first change detection if a required input is unset.\n */\n readonly listboxId = input('');\n\n /** Accessible name for the listbox. ARIA requires one; the directive supplies it from the catalog. */\n readonly listLabel = input('');\n\n protected optionId(index: number): string {\n return wrMentionOptionId(this.listboxId(), index);\n }\n\n /** Emitted when the user picks an item (click). */\n readonly picked = output<WrMentionItem>();\n\n /** Emitted on mouseenter — used by the directive to track hover. */\n readonly hovered = output<number>();\n\n protected onPick(item: WrMentionItem, event: MouseEvent): void {\n // mousedown semantics — keep focus on the input.\n event.preventDefault();\n this.picked.emit(item);\n }\n}\n","<!--\n No empty branch: `activate()` calls `close()` and returns before `open()` when\n nothing matches, so the panel is never rendered with zero items. The \"No\n matches\" `<div>` that used to live here was unreachable — and a hard-coded\n English string besides. The zero-match case is announced through the\n directive's live region instead.\n-->\n<ul class=\"wr-mention-panel__list\" role=\"listbox\" [attr.id]=\"listboxId()\" [attr.aria-label]=\"listLabel()\">\n @for (item of items(); track $index) {\n <li\n class=\"wr-mention-panel__option\"\n role=\"option\"\n [attr.id]=\"optionId($index)\"\n [class.wr-mention-panel__option--active]=\"$index === activeIndex()\"\n [attr.aria-selected]=\"$index === activeIndex()\"\n (mouseenter)=\"hovered.emit($index)\"\n (mousedown)=\"onPick(item, $event)\"\n >\n {{ displayWith()(item) }}\n </li>\n }\n</ul>\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 { coerceNumberProperty } from '@angular/cdk/coercion';\nimport {\n type ConnectedPosition,\n type FlexibleConnectedPositionStrategyOrigin,\n type OverlayRef,\n ScrollStrategyOptions,\n} from '@angular/cdk/overlay';\nimport { ComponentPortal } from '@angular/cdk/portal';\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n type ComponentRef,\n DestroyRef,\n Directive,\n ElementRef,\n PLATFORM_ID,\n computed,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\n\nimport { readI18nText, useI18nFormatter } from 'ngwr/i18n';\nimport { WR_OVERLAY } from 'ngwr/overlay';\nimport { isComposing } from 'ngwr/utils';\n\nimport { getCaretCoordinates } from './caret';\nimport type { WrMentionCommit, WrMentionItem } from './interfaces';\nimport { WrMentionPanel, wrMentionOptionId } from './mention-panel';\n\ninterface ActiveState {\n trigger: string;\n query: string;\n startIndex: number;\n}\n\n/**\n * Deterministic under prerender, unlike `randomId()` — `aria-controls` is a\n * static host binding, so it renders on the server too, and a random value would\n * differ between that render and the hydrated client.\n */\nlet mentionUid = 0;\n\n/** Gap between the caret's line and the panel, on whichever side it opens. */\nconst CARET_GAP = 4;\n\n/**\n * Below the caret's line first, above it when there is no room — the two sides\n * a caret-anchored list can take. There is no horizontal pair: a panel that\n * jumped to the other side of the caret would read as a different widget, so a\n * caret near the inline end is handled by `withPush` sliding the panel back\n * onto the screen instead. `overlayX: 'start'` is the logical corner, so RTL\n * hangs the panel from the caret's other side without a second table.\n */\nconst CARET_POSITIONS: ConnectedPosition[] = [\n { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' },\n { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' },\n];\n\n/**\n * `@`-style mention picker for `<textarea>` and `<input>`. Listens for\n * trigger characters (default `'@'`); when typed at a word boundary, an\n * overlay panel opens at the caret and shows filtered options. Pick with\n * Enter / Tab / click — the trigger + query is replaced with the inserted\n * value plus a trailing space.\n *\n * @example\n * ```html\n * <textarea\n * wrMention\n * [wrMentionItems]=\"users\"\n * [displayWith]=\"userLabel\"\n * (wrMentionSelected)=\"onMention($event)\"\n * ></textarea>\n * ```\n *\n * @see https://ngwr.dev/reference/components/mention\n */\n@Directive({\n selector: '[wrMention]',\n host: {\n '(input)': 'onInput()',\n '(click)': 'onClick()',\n '(keyup)': 'onCaretMove($event)',\n '(keydown)': 'onKeydown($event)',\n '(blur)': 'onBlur()',\n\n // The IME pair. `isComposing` on a `keydown` answers \"did the input method\n // take this key\", which is a different question from \"is a conversion open\n // right now\" — on the committing keystroke the two disagree by design — and\n // detection runs from `input` and `keyup`, where there is no such flag to\n // read. So the state is tracked here and the detector consults it.\n '(compositionstart)': 'onCompositionStart()',\n '(compositionend)': 'onCompositionEnd()',\n\n // The host keeps its implicit `role=\"textbox\"`. It is NOT a combobox: this\n // field holds prose and the mention is one fragment inside it, not the\n // field's value. `role=\"combobox\"` is also flatly disallowed on `<textarea>`\n // by ARIA in HTML (axe: `aria-allowed-role`), and it would drop\n // `aria-multiline`, which combobox does not support — so a screen reader\n // would stop reporting the field as multi-line for the whole editing\n // session, not just while the popup is open.\n //\n // Static, because `aria-autocomplete` describes a permanent capability of\n // the field; ARIA says authors SHOULD NOT toggle it to signal that\n // suggestions are currently showing. Being static is also what makes the\n // feature discoverable — it is announced on focus, before the user types a\n // trigger char.\n 'aria-autocomplete': 'list',\n // Required alongside `aria-autocomplete=\"list\"`, and must name the popup's\n // role. Measured clean on a textbox with axe-core 4.13.\n 'aria-haspopup': 'listbox',\n // Also static, and deliberately left pointing at an id that only exists\n // while the panel is open: gating it would mean gating `aria-autocomplete`\n // too. An unresolved `aria-controls` is a manual-review note in axe, never a\n // violation — unlike `aria-activedescendant`, which is an author error.\n //\n // This is the attribute that legalises the whole design: for a textbox\n // controlling a popup, `aria-activedescendant` is allowed to name an element\n // owned by the *controlled* element, which is how the reference reaches into\n // the CDK overlay's separate DOM subtree at all.\n '[attr.aria-controls]': 'listboxId',\n '[attr.aria-activedescendant]': 'activeOptionId()',\n\n // No `aria-expanded`. It is not a supported state of `role=\"textbox\"` —\n // axe rates it a *critical* `aria-allowed-attr` violation — so the\n // \"a list appeared\" signal cannot be an ARIA state here. That is what the\n // live region below is for; it is load-bearing, not decoration.\n },\n})\nexport class WrMention<T extends WrMentionItem = WrMentionItem> {\n /** Items to filter against the typed query. */\n readonly wrMentionItems = input<readonly T[]>([]);\n\n /** Trigger characters that open the panel. @default ['@'] */\n readonly triggers = input<readonly string[]>(['@']);\n\n /** Maps an item to its display label. @default `item.label` */\n readonly displayWith = input<(item: T) => string>((item: T) => item.label);\n\n /**\n * Returns the text to insert into the textarea on commit. Receives the\n * picked item and the trigger char. @default `${trigger}${displayWith(item)}`\n */\n readonly valueWith = input<((item: T, trigger: string) => string) | null>(null);\n\n /** Custom filter. @default case-insensitive `includes` over `displayWith(item)`. */\n readonly filterWith = input<((query: string, item: T) => boolean) | null>(null);\n\n /** Maximum number of items shown in the panel. @default 8 */\n readonly maxResults = input(8, { transform: (v: unknown): number => Math.max(1, coerceNumberProperty(v, 8)) });\n\n /** Emits the selected item, trigger, and query whenever the user commits. */\n readonly wrMentionSelected = output<WrMentionCommit<T>>();\n\n private readonly host = inject<ElementRef<HTMLTextAreaElement | HTMLInputElement>>(ElementRef);\n private readonly overlay = inject(WR_OVERLAY);\n private readonly scrollStrategies = inject(ScrollStrategyOptions);\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n private overlayRef: OverlayRef | null = null;\n private panelRef: ComponentRef<WrMentionPanel> | null = null;\n /**\n * Where the caret sits INSIDE the field — its offset from the field's\n * top-left corner, plus the height of the line it is on. Mutated in place by\n * `open()`, never replaced: `caretAnchor()` closes over this object.\n */\n private readonly caretOffset = { dx: 0, dy: 0, lineHeight: 0 };\n private state: ActiveState | null = null;\n private readonly activeIndex = signal(0);\n private readonly filteredItems = signal<readonly T[]>([]);\n private readonly isOpen = signal(false);\n\n /** @internal — read by the `aria-controls` host binding. */\n protected readonly listboxId = `wr-mention-listbox-${++mentionUid}`;\n\n private readonly listLabel = readI18nText('mention.listbox', 'Mentions');\n private readonly availableText = useI18nFormatter('mention.available', 'Matches available: {{count}}');\n private readonly insertedText = useI18nFormatter('mention.inserted', 'Inserted: {{label}}');\n\n /**\n * `aria-activedescendant`, or `null`.\n *\n * Gated hard on `isOpen()`, which is only set once the panel's options are\n * actually in the DOM. An id that resolves to nothing is an author error, and\n * the failure mode is silent: the browser does not complain, it simply stops\n * firing the event that makes a screen reader speak the option. Same\n * discipline `tree.ts` uses for its virtualized rows.\n */\n protected readonly activeOptionId = computed<string | null>(() => {\n if (!this.isOpen()) return null;\n const index = this.activeIndex();\n return index >= 0 && index < this.filteredItems().length ? wrMentionOptionId(this.listboxId, index) : null;\n });\n\n /**\n * Polite live region, parked on `<body>`.\n *\n * Not inside the panel: `close()` disposes the overlay on every keystroke that\n * leaves a mention, so a region living there would be recreated constantly —\n * and a live region has to be in the DOM *before* its text changes to be\n * announced at all. It also could not announce the two cases that matter most,\n * since in both the panel is already gone: \"nothing matched\" and \"inserted X\".\n *\n * Not a sibling of the host either — that would land inside the consumer's\n * markup and start matching their `+`, `~` and `:nth-child()` selectors.\n */\n private liveRegion: HTMLElement | null = null;\n\n constructor() {\n if (this.isBrowser) {\n const doc = this.host.nativeElement.ownerDocument;\n const region = doc.createElement('div');\n region.className = 'wr-mention__status';\n region.setAttribute('role', 'status');\n doc.body.appendChild(region);\n this.liveRegion = region;\n }\n\n this.destroyRef.onDestroy(() => {\n this.close();\n this.liveRegion?.remove();\n this.liveRegion = null;\n });\n }\n\n private announce(message: string): void {\n if (this.liveRegion) this.liveRegion.textContent = message;\n }\n\n // Host listeners\n\n /**\n * True between `compositionstart` and `compositionend`.\n *\n * Detection must not run on the half-built text an IME leaves in the field:\n * `@やま` is a reading on its way to `@山田`, and matching the reading opens a\n * panel that then eats the Enter meant to accept the conversion. So the field\n * is read once, on `compositionend`, when the text is real.\n */\n private composing = false;\n\n /** @internal */\n protected onCompositionStart(): void {\n this.composing = true;\n // A panel opened before the conversion started would take the arrows and the\n // Enter that now belong to the candidate window.\n this.close();\n }\n\n /** @internal */\n protected onCompositionEnd(): void {\n this.composing = false;\n // The committed text is only in the field now, so this is the first honest\n // read of it.\n this.detect();\n }\n\n /** @internal */\n protected onInput(): void {\n if (this.composing) return;\n this.detect();\n }\n\n /** @internal */\n protected onClick(): void {\n this.detect();\n }\n\n /** @internal */\n protected onCaretMove(event: KeyboardEvent): void {\n if (this.composing || isComposing(event)) return;\n // Every key `onKeydown` already consumed, Escape included: its keyup would\n // re-detect at a caret that is still sitting inside the mention and reopen the\n // panel Escape had just dismissed. A synthetic keydown-only Escape hides that\n // — the real gesture is a pair, and the second half undid the first.\n if (\n event.key === 'ArrowUp' ||\n event.key === 'ArrowDown' ||\n event.key === 'Enter' ||\n event.key === 'Tab' ||\n event.key === 'Escape'\n ) {\n return;\n }\n this.detect();\n }\n\n /** @internal */\n protected onKeydown(event: KeyboardEvent): void {\n // The key is the input method's while a conversion is open — Enter accepts a\n // candidate, Escape cancels the reading, the arrows walk the candidate list.\n // `this.composing` is deliberately not consulted: on Safari's committing\n // keystroke it has already gone false, and `isComposing` covers that case\n // through the 229 sentinel.\n if (isComposing(event)) return;\n if (!this.state) return;\n const list = this.filteredItems();\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n if (list.length > 0) this.setActive((this.activeIndex() + 1) % list.length);\n break;\n case 'ArrowUp':\n event.preventDefault();\n if (list.length > 0) this.setActive((this.activeIndex() - 1 + list.length) % list.length);\n break;\n case 'Enter':\n case 'Tab': {\n const item = list[this.activeIndex()];\n if (item) {\n event.preventDefault();\n this.commit(item);\n }\n break;\n }\n case 'Escape':\n event.preventDefault();\n // The panel is the innermost thing Escape can dismiss, so it keeps the\n // key: without this, the same press also closed the dialog / drawer /\n // window the field sits in.\n //\n // `preventDefault()` alone buys nothing — every host overlay matches on\n // `event.key` and none of them looks at `defaultPrevented` — and letting\n // CDK's `OverlayKeyboardDispatcher` sort it out does not work either.\n // The dispatcher walks the stack newest-first and stops at the first\n // overlay with a `keydownEvents()` subscriber; this panel has none (it\n // is driven from the field's own handler, which runs at the target\n // before the body-phase dispatcher), so Escape falls straight through to\n // the overlay underneath.\n event.stopPropagation();\n this.close();\n break;\n default:\n break;\n }\n }\n\n /** @internal */\n protected onBlur(): void {\n // Defer so option mousedown lands first.\n setTimeout(() => this.close(), 120);\n }\n\n // Detection\n\n private detect(): void {\n const el = this.host.nativeElement;\n const caret = el.selectionStart ?? 0;\n const text = el.value.substring(0, caret);\n const triggers = this.triggers();\n\n // Walk back from the caret looking for a trigger char with whitespace\n // (or start-of-string) immediately before it. Stop on whitespace.\n let i = caret;\n while (i > 0) {\n const ch = text[i - 1];\n if (triggers.includes(ch)) {\n const before = i - 2 >= 0 ? text[i - 2] : '';\n if (i - 2 < 0 || /\\s/.test(before)) {\n const query = text.substring(i);\n this.activate(ch, query, i - 1);\n return;\n }\n break;\n }\n if (/\\s/.test(ch)) break;\n i--;\n }\n this.close();\n }\n\n private activate(trigger: string, query: string, startIndex: number): void {\n this.state = { trigger, query, startIndex };\n const previousCount = this.filteredItems().length;\n const filtered = this.filter(query).slice(0, this.maxResults());\n this.filteredItems.set(filtered);\n if (filtered.length === 0) {\n // Announce *after* closing: `close()` clears the region, so announcing\n // first would wipe the message before a screen reader reached it.\n this.close();\n this.announce(this.availableText({ count: 0 }));\n return;\n }\n this.setActive(Math.min(this.activeIndex(), filtered.length - 1));\n this.open();\n // Only when the size actually changes. Re-announcing the same count on every\n // keystroke would talk over the per-option speech from activedescendant.\n if (filtered.length !== previousCount) this.announce(this.availableText({ count: filtered.length }));\n }\n\n private filter(query: string): readonly T[] {\n const items = this.wrMentionItems();\n const custom = this.filterWith();\n const display = this.displayWith();\n if (!query) return items;\n if (custom) return items.filter(item => custom(query, item));\n const q = query.toLowerCase();\n return items.filter(item => display(item).toLowerCase().includes(q));\n }\n\n private setActive(i: number): void {\n this.activeIndex.set(i);\n this.panelRef?.setInput('activeIndex', i);\n }\n\n // Overlay\n\n /**\n * The overlay's anchor: the caret's line box, grown by the gap the panel\n * keeps from the text, as a zero-WIDTH virtual origin in viewport\n * coordinates. Inflating the rect rather than passing `offsetY` is what keeps\n * `withPush` honest — CDK applies a position offset as a `transform` AFTER it\n * has clamped, so a pushed panel would land the offset off the screen edge.\n *\n * Live getters, not a snapshot, and that is the whole of the scroll fix.\n * `getCaretCoordinates` answers in viewport coordinates, which a\n * `GlobalPositionStrategy` froze at the instant of the keystroke: the panel\n * then stayed pinned to the screen while the page scrolled out from under it\n * (150px of scroll, 150px of drift, ending up over unrelated content). CDK\n * re-reads a virtual origin on every `apply()`, so pinning it to the FIELD's\n * live rect is what turns `reposition()` into a real re-anchor.\n *\n * Only the caret's offset inside the field is captured up front. Measuring it\n * runs the mirror-div pass in `caret.ts` — a full layout read, far too\n * expensive to repeat per scroll event — and it cannot change without a\n * keystroke, which re-enters `open()` anyway.\n */\n private caretAnchor(): FlexibleConnectedPositionStrategyOrigin {\n const field = this.host.nativeElement;\n const offset = this.caretOffset;\n return {\n width: 0,\n get x(): number {\n return field.getBoundingClientRect().left + offset.dx;\n },\n get y(): number {\n return field.getBoundingClientRect().top + offset.dy - CARET_GAP;\n },\n get height(): number {\n return offset.lineHeight + CARET_GAP * 2;\n },\n };\n }\n\n private open(): void {\n const el = this.host.nativeElement;\n const caret = el.selectionStart ?? 0;\n const { top, left, lineHeight } = getCaretCoordinates(el, caret);\n const field = el.getBoundingClientRect();\n this.caretOffset.dx = left - field.left;\n this.caretOffset.dy = top - field.top;\n this.caretOffset.lineHeight = lineHeight;\n\n if (!this.overlayRef) {\n this.overlayRef = this.overlay.create({\n // Flexible, not global: a global strategy has no anchor and no fallback\n // positions, so the panel was always drawn downward from the caret no\n // matter how little room was left below it — half the list ended up\n // under the fold, unreachable by pointer AND invisible to a sighted\n // keyboard user walking it with `aria-activedescendant` (WCAG 2.4.11).\n positionStrategy: this.overlay\n .position()\n .flexibleConnectedTo(this.caretAnchor())\n // The panel sizes itself (`max-height` on the list); the flip above\n // and `withPush` are the whole answer to a cramped viewport.\n .withFlexibleDimensions(false)\n .withPush(true)\n .withPositions(CARET_POSITIONS),\n scrollStrategy: this.scrollStrategies.reposition(),\n panelClass: 'wr-mention-overlay',\n });\n\n const portal = new ComponentPortal(WrMentionPanel);\n const ref = this.overlayRef.attach(portal);\n this.panelRef = ref;\n ref.setInput('displayWith', this.displayWith());\n ref.setInput('listboxId', this.listboxId);\n // Read once here rather than bound: the panel is created per open, and by\n // then the catalog has long landed.\n ref.setInput('listLabel', this.listLabel());\n ref.instance.picked.subscribe(item => this.commit(item as T));\n ref.instance.hovered.subscribe(i => this.setActive(i));\n }\n // No `else` re-building the strategy: the anchor reads `caretOffset` and the\n // field's live rect, both of which the lines above have already refreshed.\n\n this.panelRef?.setInput('items', this.filteredItems());\n this.panelRef?.setInput('activeIndex', this.activeIndex());\n\n // Render the panel's own view NOW, before `aria-activedescendant` is allowed\n // to name one of its options. `attach()` inserts the host element but leaves\n // it empty — the panel is a separate root view, so on the first open the\n // `<li>` being referenced does not exist anywhere in the document yet.\n // Waiting for the next tick would leave a window where the reference dangles\n // and the announcement is lost with no error anywhere.\n this.panelRef?.changeDetectorRef.detectChanges();\n // Place it only now, with the options rendered: which side of the caret the\n // panel takes is decided by measuring it, and `attach()` defers CDK's own\n // first placement into `afterNextRender` — a frame in which the panel would\n // be painted, full size and fully opaque, at the overlay container's\n // top-left corner.\n this.overlayRef.updatePosition();\n this.isOpen.set(true);\n }\n\n private close(): void {\n if (this.overlayRef) {\n this.overlayRef.dispose();\n this.overlayRef = null;\n this.panelRef = null;\n }\n this.isOpen.set(false);\n this.state = null;\n this.activeIndex.set(0);\n this.filteredItems.set([]);\n this.announce('');\n }\n\n private commit(item: T): void {\n // Snapshot the state up front. `dispatchEvent` below re-enters this directive\n // synchronously: the listener runs `detect()`, and because assigning\n // `el.value` has already parked the caret at the end of the text — past the\n // space this method appends — `detect()` finds no trigger and calls\n // `close()`, which nulls `this.state`. Reading `this.state` after that line\n // threw, so `wrMentionSelected` never fired for anyone.\n const state = this.state;\n if (!state) return;\n\n const el = this.host.nativeElement;\n const value = el.value;\n const caret = el.selectionStart ?? value.length;\n const valueFn = this.valueWith();\n const label = this.displayWith()(item);\n const inserted = valueFn ? valueFn(item, state.trigger) : `${state.trigger}${label}`;\n const replacement = `${inserted} `;\n const before = value.substring(0, state.startIndex);\n const after = value.substring(caret);\n const next = before + replacement + after;\n\n el.value = next;\n el.dispatchEvent(new Event('input', { bubbles: true }));\n const nextCaret = (before + replacement).length;\n el.setSelectionRange(nextCaret, nextCaret);\n this.wrMentionSelected.emit({ item, trigger: state.trigger, query: state.query });\n\n // Announce after `close()`, never before — it clears the live region.\n // Committing is otherwise silent: the panel simply vanishes.\n this.close();\n this.announce(this.insertedText({ label }));\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -69,7 +69,7 @@ class WrPaginationHarness extends ComponentHarness {
|
|
|
69
69
|
static with(options = {}) {
|
|
70
70
|
return new HarnessPredicate(WrPaginationHarness, options)
|
|
71
71
|
.addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))
|
|
72
|
-
.addOption('
|
|
72
|
+
.addOption('page', options.page, async (harness, page) => {
|
|
73
73
|
// The nullable read on purpose: the throwing getter would reject the whole
|
|
74
74
|
// query over one pager that announces no current page, taking its
|
|
75
75
|
// well-behaved neighbours down with it.
|
|
@@ -191,7 +191,7 @@ class WrPaginationHarness extends ComponentHarness {
|
|
|
191
191
|
* The page the pager announces as current, from `aria-current="page"`.
|
|
192
192
|
*
|
|
193
193
|
* Throws when no cell claims it. The window always keeps the current page on the
|
|
194
|
-
* strip, so getting here means `
|
|
194
|
+
* strip, so getting here means `page` is not one of the pages at all. The
|
|
195
195
|
* component clamps a host write into `1..totalPages`, which leaves one survivor:
|
|
196
196
|
* a fractional page, which is in range and matches no cell — and which leaves
|
|
197
197
|
* every cell looking equally inactive to an assistive tech user.
|
|
@@ -200,7 +200,7 @@ class WrPaginationHarness extends ComponentHarness {
|
|
|
200
200
|
const page = await this.currentPageOrNull();
|
|
201
201
|
if (page === null) {
|
|
202
202
|
throw new Error('WrPaginationHarness.getCurrentPage(): no cell carries `aria-current="page"`. The visible window ' +
|
|
203
|
-
`always includes the current page, so \`
|
|
203
|
+
`always includes the current page, so \`page\` is not a whole number — the component ` +
|
|
204
204
|
`clamps it into 1..${await this.getTotalPages()} but does not round it.`);
|
|
205
205
|
}
|
|
206
206
|
return page;
|
|
@@ -211,7 +211,7 @@ class WrPaginationHarness extends ComponentHarness {
|
|
|
211
211
|
* Throws when that page is not on the strip — a seven-slot window over twenty
|
|
212
212
|
* pages simply has no cell for page 15, and a silent no-op there reads as
|
|
213
213
|
* "navigation is broken" three assertions later. Step with {@link next} /
|
|
214
|
-
* {@link previous}, or move the host's own `
|
|
214
|
+
* {@link previous}, or move the host's own `page` model.
|
|
215
215
|
*
|
|
216
216
|
* A disabled pager is still clicked; the component's own guard is what refuses
|
|
217
217
|
* the move, so assert the page rather than assume it. Clicking the CURRENT page
|
|
@@ -277,14 +277,15 @@ class WrPaginationHarness extends ComponentHarness {
|
|
|
277
277
|
return (await this.totalLabel()) !== null;
|
|
278
278
|
}
|
|
279
279
|
/**
|
|
280
|
-
* The total label exactly as rendered, e.g. `1
|
|
280
|
+
* The total label exactly as rendered, e.g. `1–10 of 95`.
|
|
281
281
|
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
282
|
+
* The WHOLE string is one catalog entry (`pagination.range`) and the three
|
|
283
|
+
* numbers in it are formatted per `LOCALE_ID`, so a spec asserting the literal
|
|
284
|
+
* owns both the copy and the locale it asserts under. Note the range separator
|
|
285
|
+
* is an en dash, not the ASCII hyphen this used to print.
|
|
285
286
|
*
|
|
286
287
|
* Throws when the label is not rendered, which is a `showTotal` that was never
|
|
287
|
-
* set rather than a total of zero (a zero total still renders `0
|
|
288
|
+
* set rather than a total of zero (a zero total still renders `0–0 of 0`).
|
|
288
289
|
*/
|
|
289
290
|
async getTotalText() {
|
|
290
291
|
const label = await this.totalLabel();
|
|
@@ -344,7 +345,7 @@ class WrPaginationHarness extends ComponentHarness {
|
|
|
344
345
|
* {@link getPageSize} reads them: `20 / page` and `20 / стр.` are the same option.
|
|
345
346
|
* Throws when no option offers that size, naming what is on offer.
|
|
346
347
|
*
|
|
347
|
-
* Changing the size can move the page — the component pulls `
|
|
348
|
+
* Changing the size can move the page — the component pulls `page` back
|
|
348
349
|
* when the new size leaves it past the end — so re-read {@link getCurrentPage}
|
|
349
350
|
* afterwards rather than assuming it held.
|
|
350
351
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngwr-pagination-testing.mjs","sources":["../../../projects/lib/pagination/testing/wr-pagination-harness.ts","../../../projects/lib/pagination/testing/ngwr-pagination-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport { WrButtonHarness } from 'ngwr/button/testing';\nimport type { WrPaginationSize } from 'ngwr/pagination';\nimport { WrSelectHarness } from 'ngwr/select/testing';\n\nimport type { WrPaginationHarnessFilters } from './interfaces';\n\n/**\n * Test harness for `<wr-pagination>` — the numbered strip, its two step controls,\n * the total label and the page-size changer.\n *\n * The strip is not the page list: it is a WINDOW over it, at most seven pages wide,\n * with an `…` standing in for every page it skipped. So \"which pages can I click\"\n * and \"how many pages are there\" are different questions and this harness keeps\n * them apart —\n * {@link getPages} answers the first, {@link getTotalPages} the second, and\n * {@link getStrip} shows the gaps for what they are. A gap is a `<span>`, not a\n * control; counting the strip's children as pages is the mistake this API exists to\n * make impossible.\n *\n * **Which page is current has two answers, and this reads the ARIA one.** The\n * current cell is also PAINTED — the template binds\n * `[color]=\"isCurrent(page) ? 'primary' : null\"`, so `.wr-btn--primary` sits on it\n * too. But colour is a theming decision (`ngwr/config` names this very binding as\n * the reason it ships no app-wide `color` default), while `aria-current=\"page\"` is\n * the navigation contract: it is what a screen-reader user hears, what the\n * component's own spec pins, and the only one of the two that still means\n * \"you are here\" after a restyle. A harness that read the class would be reporting\n * on the paint.\n *\n * **No roving tab stop to disambiguate.** Every cell is a `<wr-btn>` ELEMENT with\n * its own tab stop (`tabindex=\"0\"`, dropped only while the control is off), so the\n * pager has no separate \"focused\" item that could drift from the current page —\n * unlike a tab strip or a radio group, there is one active state here and\n * {@link getCurrentPage} is it.\n *\n * Composed rather than re-queried: the cells and step controls are driven through\n * {@link WrButtonHarness}, which already knows how a `<wr-btn>` says it is off —\n * both the `disabled` attribute (written on every shape, though inert on a custom\n * element) and `aria-disabled`, in that order — and the size changer through\n * {@link WrSelectHarness}, whose panel is a portal in the overlay container and\n * nowhere inside the pager.\n *\n * jsdom notes: every method here is a class / attribute read or a no-arg click, so\n * none of it needs layout. Two things are therefore invisible in a unit test and\n * deliberately absent from this API — the responsive collapse, which is a container\n * query ({@link isResponsive} reports only that it is ALLOWED), and the compact\n * `‹ 3 / 10 ›` pager it swaps in, which is `aria-hidden` decoration duplicating\n * {@link getCurrentPage} and {@link getTotalPages} and which a real browser would\n * read as empty text while CSS keeps it hidden.\n *\n * @example\n * ```ts\n * const pager = await loader.getHarness(WrPaginationHarness);\n *\n * expect(await pager.getPages()).toEqual([1, 2, 3, 4, 5, 10]);\n * await pager.goToPage(3);\n * expect(await pager.getCurrentPage()).toBe(3);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrPaginationHarness extends ComponentHarness {\n static hostSelector = 'wr-pagination';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrPaginationHarnessFilters = {}): HarnessPredicate<WrPaginationHarness> {\n return new HarnessPredicate(WrPaginationHarness, options)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))\n .addOption('currentPage', options.currentPage, async (harness, page) => {\n // The nullable read on purpose: the throwing getter would reject the whole\n // query over one pager that announces no current page, taking its\n // well-behaved neighbours down with it.\n return (await harness.currentPageOrNull()) === page;\n })\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n /** The numbered cells, in DOM order — ascending, and never a gap. */\n private readonly pageCells = this.locatorForAll(WrButtonHarness.with({ selector: '.wr-pagination__page' }));\n\n /**\n * The two controls bracketing the strip: previous, then next.\n *\n * Located by POSITION, not by their labels. `prevLabel` / `nextLabel` are inputs\n * and both fall back to the `pagination.prev` / `pagination.next` catalog entries,\n * so an English `aria-label` match works right up until someone ships a locale —\n * whereas the nav's shape (one step control, the strip, one step control) is\n * structural. `:not(.wr-pagination__page)` is what keeps the cells out of it.\n */\n private readonly stepControls = this.locatorForAll(\n WrButtonHarness.with({ selector: '.wr-pagination__nav > wr-btn:not(.wr-pagination__page)' })\n );\n\n /** Both kinds of strip slot, in DOM order — cells and the gaps between them. */\n private readonly stripSlots = this.locatorForAll('.wr-pagination__page, .wr-pagination__ellipsis');\n\n private readonly totalLabel = this.locatorForOptional('.wr-pagination__total');\n\n private readonly sizeChanger = this.locatorForOptional(WrSelectHarness.with({ selector: '.wr-pagination__size' }));\n\n /**\n * The accessible name of the `role=\"navigation\"` landmark.\n *\n * `''` only if the landmark lost its name, which is a bug rather than a state: a\n * page with two unnamed pagers gives a screen-reader user two identical\n * \"navigation\" landmarks.\n */\n async getLabel(): Promise<string> {\n return (await (await this.host()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The size the pager RENDERS at, from its host modifier.\n *\n * The modifier is always emitted — `sm` included — so the fall-through below is\n * only ever reached for the default, and a size the strip is not wearing cannot\n * come back from here.\n *\n * The STRIP resolves nothing through `provideWrConfig()`, which is worth knowing\n * before an app-wide default is blamed for a cell size: the pager passes its own\n * `size` down to every `<wr-btn>` explicitly, so a configured `button.size` never\n * reaches a cell. The page-size changer is the exception and not covered by this\n * value — it is a bare `<wr-select>`, so `provideWrConfig({ select: … })` does\n * reach it.\n */\n async getSize(): Promise<WrPaginationSize> {\n const host = await this.host();\n if (await host.hasClass('wr-pagination--lg')) return 'lg';\n if (await host.hasClass('wr-pagination--md')) return 'md';\n return 'sm';\n }\n\n /**\n * Whether the pager is ALLOWED to collapse into its compact pager.\n *\n * Whether it HAS collapsed is a container query on the control's own box, and a\n * unit test has no layout for one to answer — assert this input in jsdom and the\n * reflow itself in a real browser.\n */\n async isResponsive(): Promise<boolean> {\n return (await this.host()).hasClass('wr-pagination--responsive');\n }\n\n /**\n * Whether the whole control refuses interaction.\n *\n * Read from the host modifier, because a `role=\"navigation\"` landmark has no ARIA\n * disabled state to prefer — the per-control answer lives on the buttons\n * themselves (`disabled` plus `aria-disabled` on each), which is what\n * {@link isPreviousDisabled} and {@link isNextDisabled} read.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.host()).hasClass('wr-pagination--disabled');\n }\n\n /**\n * The whole strip in DOM order: a `number` for every page cell, and the gap\n * marker's own text (`…`) for every ellipsis.\n *\n * The marker is read from the DOM rather than compared against a literal, so a\n * component that changes what it draws in a gap changes this too instead of\n * quietly reporting a page.\n */\n async getStrip(): Promise<(number | string)[]> {\n const slots = await this.stripSlots();\n return Promise.all(\n slots.map(async slot => {\n const text = (await slot.text()).trim();\n return (await slot.hasClass('wr-pagination__ellipsis')) ? text : Number(text);\n })\n );\n }\n\n /**\n * The pages that can be clicked right now, ascending. Gaps are not pages and are\n * not here — `[1, 8, 9, 10, 11, 12, 20]` for a twenty-page list sitting on 10.\n */\n async getPages(): Promise<number[]> {\n const cells = await this.pageCells();\n return Promise.all(cells.map(async cell => Number(await cell.getText())));\n }\n\n /**\n * How many pages there are — not how many are on the strip.\n *\n * Taken from the LAST cell, which the window always pins to the final page (the\n * first is pinned the same way), so this survives the gap that usually precedes\n * it. Deliberately not read from the compact `3 / 10` pager: that span is\n * `aria-hidden` and CSS-hidden outside responsive mode, so a browser-based\n * harness environment would read it as empty.\n */\n async getTotalPages(): Promise<number> {\n const pages = await this.getPages();\n const last = pages.at(-1);\n if (last === undefined) {\n throw new Error(\n 'WrPaginationHarness.getTotalPages(): the strip has no page cells at all. The pager offers page ' +\n '1 even for an empty list, so this is a rendering failure rather than an empty state.'\n );\n }\n return last;\n }\n\n /**\n * The page the pager announces as current, from `aria-current=\"page\"`.\n *\n * Throws when no cell claims it. The window always keeps the current page on the\n * strip, so getting here means `currentPage` is not one of the pages at all. The\n * component clamps a host write into `1..totalPages`, which leaves one survivor:\n * a fractional page, which is in range and matches no cell — and which leaves\n * every cell looking equally inactive to an assistive tech user.\n */\n async getCurrentPage(): Promise<number> {\n const page = await this.currentPageOrNull();\n if (page === null) {\n throw new Error(\n 'WrPaginationHarness.getCurrentPage(): no cell carries `aria-current=\"page\"`. The visible window ' +\n `always includes the current page, so \\`currentPage\\` is not a whole number — the component ` +\n `clamps it into 1..${await this.getTotalPages()} but does not round it.`\n );\n }\n return page;\n }\n\n /**\n * Click the cell for a page.\n *\n * Throws when that page is not on the strip — a seven-slot window over twenty\n * pages simply has no cell for page 15, and a silent no-op there reads as\n * \"navigation is broken\" three assertions later. Step with {@link next} /\n * {@link previous}, or move the host's own `currentPage` model.\n *\n * A disabled pager is still clicked; the component's own guard is what refuses\n * the move, so assert the page rather than assume it. Clicking the CURRENT page\n * is a no-op by the same guard.\n */\n async goToPage(page: number): Promise<void> {\n for (const cell of await this.pageCells()) {\n if (Number(await cell.getText()) === page) {\n await cell.click();\n return;\n }\n }\n\n throw new Error(\n `WrPaginationHarness.goToPage(): page ${page} is not on the strip, which offers ` +\n `${(await this.getStrip()).join(' ')}. The window shows at most seven pages — step with next() / ` +\n 'previous(), or set the page on the host.'\n );\n }\n\n /**\n * Jump to page 1.\n *\n * There is no dedicated first / last control in this component; the window pins\n * the first and last page to the ends of the strip instead, so these two click\n * cells like any other page.\n */\n async goToFirst(): Promise<void> {\n return this.goToPage(1);\n }\n\n /** Jump to the last page — the trailing cell, not the last one before a gap. */\n async goToLast(): Promise<void> {\n return this.goToPage(await this.getTotalPages());\n }\n\n /**\n * Click the next-page control.\n *\n * Disabled on the last page, and a disabled `<wr-btn>` is still clicked here (see\n * {@link goToPage}) — {@link isNextDisabled} is the question, this is the action.\n */\n async next(): Promise<void> {\n return (await this.stepControl('next')).click();\n }\n\n /** Click the previous-page control. Disabled on the first page. */\n async previous(): Promise<void> {\n return (await this.stepControl('previous')).click();\n }\n\n /**\n * Whether the next-page control refuses the move — true on the last page, and\n * true for every control while the pager is disabled.\n *\n * Answered by {@link WrButtonHarness}, which reads the `disabled` attribute first\n * and `aria-disabled` second. A `<wr-btn>` element carries both: the attribute is\n * inert on anything but a real `<button>`, which is why the component adds the\n * ARIA state as well — so this is the accessible answer whichever branch supplies\n * it.\n */\n async isNextDisabled(): Promise<boolean> {\n return (await this.stepControl('next')).isDisabled();\n }\n\n /** Whether the previous-page control refuses the move — true on the first page. */\n async isPreviousDisabled(): Promise<boolean> {\n return (await this.stepControl('previous')).isDisabled();\n }\n\n /** Whether the pager shows its \"X–Y of Z\" label (the `showTotal` input). */\n async hasTotal(): Promise<boolean> {\n return (await this.totalLabel()) !== null;\n }\n\n /**\n * The total label exactly as rendered, e.g. `1-10 of 95`.\n *\n * Both halves are localizable — the word between range and total comes from\n * `ofLabel` or the `pagination.of` catalog entry — so a spec asserting the whole\n * string should own the copy it asserts.\n *\n * Throws when the label is not rendered, which is a `showTotal` that was never\n * set rather than a total of zero (a zero total still renders `0-0 of 0`).\n */\n async getTotalText(): Promise<string> {\n const label = await this.totalLabel();\n if (!label) {\n throw new Error(\n 'WrPaginationHarness.getTotalText(): this pager renders no total label — set `showTotal`. A ' +\n 'total of 0 still renders one.'\n );\n }\n return label.text();\n }\n\n /** Whether the pager renders its page-size changer (the `showSizeChanger` input). */\n async hasPageSizeChanger(): Promise<boolean> {\n return (await this.sizeChanger()) !== null;\n }\n\n /**\n * The page-size changer as a {@link WrSelectHarness} — for anything this harness\n * does not wrap: the offered labels, the open state, keyboard driving.\n *\n * Composed rather than re-queried on purpose: the changer is a real\n * `<wr-select>`, its options live in the overlay container rather than inside the\n * pager, and the select harness is the thing that already knows how to scope them\n * to their own trigger.\n *\n * Throws when there is no changer — an `undefined` here would turn into a\n * confusing failure inside a select call.\n */\n async getPageSizeSelect(): Promise<WrSelectHarness> {\n const select = await this.sizeChanger();\n if (!select) {\n throw new Error('WrPaginationHarness.getPageSizeSelect(): no page-size changer here — set `showSizeChanger`.');\n }\n return select;\n }\n\n /**\n * The page size currently in effect.\n *\n * The changer's trigger is the only place the size reaches the DOM, so this reads\n * it there and takes the digits: the label is localizable (`pagination.perPage`,\n * `{{size}} / page`) but interpolates the number in every language — unless a\n * catalog entry drops the placeholder, which is the second throw below.\n *\n * Throws when there is no changer at all: a pager without one says nothing on\n * screen about its page size, and the host's own model is where to assert it.\n */\n async getPageSize(): Promise<number> {\n const text = await (await this.getPageSizeSelect()).getValueText();\n const digits = /\\d+/.exec(text);\n if (!digits) {\n throw new Error(\n `WrPaginationHarness.getPageSize(): the changer reads \"${text}\", which names no number — a ` +\n 'catalog entry for `pagination.perPage` that drops `{{size}}` takes the size off the screen.'\n );\n }\n return Number(digits[0]);\n }\n\n /**\n * Pick a page size through the changer, the way a user does.\n *\n * Matched on the digits rather than the whole label, for the same reason\n * {@link getPageSize} reads them: `20 / page` and `20 / стр.` are the same option.\n * Throws when no option offers that size, naming what is on offer.\n *\n * Changing the size can move the page — the component pulls `currentPage` back\n * when the new size leaves it past the end — so re-read {@link getCurrentPage}\n * afterwards rather than assuming it held.\n */\n async setPageSize(size: number): Promise<void> {\n const select = await this.getPageSizeSelect();\n if (await this.isDisabled()) {\n // Without this the call fails inside the select harness, whose message blames\n // a tag-mode panel and a `minChars` search that have nothing to do with it.\n throw new Error(\n 'WrPaginationHarness.setPageSize(): the pager is disabled, so its changer refuses to open — the ' +\n 'pager hands its own `disabled` straight to it.'\n );\n }\n\n await select.open();\n\n const labels = await select.getOptionLabels();\n const digits = new RegExp(`(^|\\\\D)${size}(\\\\D|$)`);\n const match = labels.find(label => digits.test(label));\n if (match === undefined) {\n throw new Error(\n `WrPaginationHarness.setPageSize(): no option offers ${size} — the changer offers ` +\n `${labels.join(', ')}. \\`pageSizeOptions\\` is what decides that list.`\n );\n }\n\n await select.selectOption({ text: match });\n }\n\n /** The current page, or `null` when no cell announces one. @see getCurrentPage */\n private async currentPageOrNull(): Promise<number | null> {\n const cell = await this.locatorForOptional(\n WrButtonHarness.with({ selector: '.wr-pagination__page[aria-current=\"page\"]' })\n )();\n return cell ? Number(await cell.getText()) : null;\n }\n\n private async stepControl(which: 'previous' | 'next'): Promise<WrButtonHarness> {\n const controls = await this.stepControls();\n if (controls.length !== 2) {\n throw new Error(\n `WrPaginationHarness: expected the nav to bracket the strip with a previous and a next control, ` +\n `found ${controls.length}. They are addressed by position, since their labels are localizable.`\n );\n }\n return which === 'previous' ? controls[0] : controls[1];\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngwr-pagination-testing.mjs","sources":["../../../projects/lib/pagination/testing/wr-pagination-harness.ts","../../../projects/lib/pagination/testing/ngwr-pagination-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport { WrButtonHarness } from 'ngwr/button/testing';\nimport type { WrPaginationSize } from 'ngwr/pagination';\nimport { WrSelectHarness } from 'ngwr/select/testing';\n\nimport type { WrPaginationHarnessFilters } from './interfaces';\n\n/**\n * Test harness for `<wr-pagination>` — the numbered strip, its two step controls,\n * the total label and the page-size changer.\n *\n * The strip is not the page list: it is a WINDOW over it, at most seven pages wide,\n * with an `…` standing in for every page it skipped. So \"which pages can I click\"\n * and \"how many pages are there\" are different questions and this harness keeps\n * them apart —\n * {@link getPages} answers the first, {@link getTotalPages} the second, and\n * {@link getStrip} shows the gaps for what they are. A gap is a `<span>`, not a\n * control; counting the strip's children as pages is the mistake this API exists to\n * make impossible.\n *\n * **Which page is current has two answers, and this reads the ARIA one.** The\n * current cell is also PAINTED — the template binds\n * `[color]=\"isCurrent(page) ? 'primary' : null\"`, so `.wr-btn--primary` sits on it\n * too. But colour is a theming decision (`ngwr/config` names this very binding as\n * the reason it ships no app-wide `color` default), while `aria-current=\"page\"` is\n * the navigation contract: it is what a screen-reader user hears, what the\n * component's own spec pins, and the only one of the two that still means\n * \"you are here\" after a restyle. A harness that read the class would be reporting\n * on the paint.\n *\n * **No roving tab stop to disambiguate.** Every cell is a `<wr-btn>` ELEMENT with\n * its own tab stop (`tabindex=\"0\"`, dropped only while the control is off), so the\n * pager has no separate \"focused\" item that could drift from the current page —\n * unlike a tab strip or a radio group, there is one active state here and\n * {@link getCurrentPage} is it.\n *\n * Composed rather than re-queried: the cells and step controls are driven through\n * {@link WrButtonHarness}, which already knows how a `<wr-btn>` says it is off —\n * both the `disabled` attribute (written on every shape, though inert on a custom\n * element) and `aria-disabled`, in that order — and the size changer through\n * {@link WrSelectHarness}, whose panel is a portal in the overlay container and\n * nowhere inside the pager.\n *\n * jsdom notes: every method here is a class / attribute read or a no-arg click, so\n * none of it needs layout. Two things are therefore invisible in a unit test and\n * deliberately absent from this API — the responsive collapse, which is a container\n * query ({@link isResponsive} reports only that it is ALLOWED), and the compact\n * `‹ 3 / 10 ›` pager it swaps in, which is `aria-hidden` decoration duplicating\n * {@link getCurrentPage} and {@link getTotalPages} and which a real browser would\n * read as empty text while CSS keeps it hidden.\n *\n * @example\n * ```ts\n * const pager = await loader.getHarness(WrPaginationHarness);\n *\n * expect(await pager.getPages()).toEqual([1, 2, 3, 4, 5, 10]);\n * await pager.goToPage(3);\n * expect(await pager.getCurrentPage()).toBe(3);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrPaginationHarness extends ComponentHarness {\n static hostSelector = 'wr-pagination';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrPaginationHarnessFilters = {}): HarnessPredicate<WrPaginationHarness> {\n return new HarnessPredicate(WrPaginationHarness, options)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))\n .addOption('page', options.page, async (harness, page) => {\n // The nullable read on purpose: the throwing getter would reject the whole\n // query over one pager that announces no current page, taking its\n // well-behaved neighbours down with it.\n return (await harness.currentPageOrNull()) === page;\n })\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n /** The numbered cells, in DOM order — ascending, and never a gap. */\n private readonly pageCells = this.locatorForAll(WrButtonHarness.with({ selector: '.wr-pagination__page' }));\n\n /**\n * The two controls bracketing the strip: previous, then next.\n *\n * Located by POSITION, not by their labels. `prevLabel` / `nextLabel` are inputs\n * and both fall back to the `pagination.prev` / `pagination.next` catalog entries,\n * so an English `aria-label` match works right up until someone ships a locale —\n * whereas the nav's shape (one step control, the strip, one step control) is\n * structural. `:not(.wr-pagination__page)` is what keeps the cells out of it.\n */\n private readonly stepControls = this.locatorForAll(\n WrButtonHarness.with({ selector: '.wr-pagination__nav > wr-btn:not(.wr-pagination__page)' })\n );\n\n /** Both kinds of strip slot, in DOM order — cells and the gaps between them. */\n private readonly stripSlots = this.locatorForAll('.wr-pagination__page, .wr-pagination__ellipsis');\n\n private readonly totalLabel = this.locatorForOptional('.wr-pagination__total');\n\n private readonly sizeChanger = this.locatorForOptional(WrSelectHarness.with({ selector: '.wr-pagination__size' }));\n\n /**\n * The accessible name of the `role=\"navigation\"` landmark.\n *\n * `''` only if the landmark lost its name, which is a bug rather than a state: a\n * page with two unnamed pagers gives a screen-reader user two identical\n * \"navigation\" landmarks.\n */\n async getLabel(): Promise<string> {\n return (await (await this.host()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The size the pager RENDERS at, from its host modifier.\n *\n * The modifier is always emitted — `sm` included — so the fall-through below is\n * only ever reached for the default, and a size the strip is not wearing cannot\n * come back from here.\n *\n * The STRIP resolves nothing through `provideWrConfig()`, which is worth knowing\n * before an app-wide default is blamed for a cell size: the pager passes its own\n * `size` down to every `<wr-btn>` explicitly, so a configured `button.size` never\n * reaches a cell. The page-size changer is the exception and not covered by this\n * value — it is a bare `<wr-select>`, so `provideWrConfig({ select: … })` does\n * reach it.\n */\n async getSize(): Promise<WrPaginationSize> {\n const host = await this.host();\n if (await host.hasClass('wr-pagination--lg')) return 'lg';\n if (await host.hasClass('wr-pagination--md')) return 'md';\n return 'sm';\n }\n\n /**\n * Whether the pager is ALLOWED to collapse into its compact pager.\n *\n * Whether it HAS collapsed is a container query on the control's own box, and a\n * unit test has no layout for one to answer — assert this input in jsdom and the\n * reflow itself in a real browser.\n */\n async isResponsive(): Promise<boolean> {\n return (await this.host()).hasClass('wr-pagination--responsive');\n }\n\n /**\n * Whether the whole control refuses interaction.\n *\n * Read from the host modifier, because a `role=\"navigation\"` landmark has no ARIA\n * disabled state to prefer — the per-control answer lives on the buttons\n * themselves (`disabled` plus `aria-disabled` on each), which is what\n * {@link isPreviousDisabled} and {@link isNextDisabled} read.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.host()).hasClass('wr-pagination--disabled');\n }\n\n /**\n * The whole strip in DOM order: a `number` for every page cell, and the gap\n * marker's own text (`…`) for every ellipsis.\n *\n * The marker is read from the DOM rather than compared against a literal, so a\n * component that changes what it draws in a gap changes this too instead of\n * quietly reporting a page.\n */\n async getStrip(): Promise<(number | string)[]> {\n const slots = await this.stripSlots();\n return Promise.all(\n slots.map(async slot => {\n const text = (await slot.text()).trim();\n return (await slot.hasClass('wr-pagination__ellipsis')) ? text : Number(text);\n })\n );\n }\n\n /**\n * The pages that can be clicked right now, ascending. Gaps are not pages and are\n * not here — `[1, 8, 9, 10, 11, 12, 20]` for a twenty-page list sitting on 10.\n */\n async getPages(): Promise<number[]> {\n const cells = await this.pageCells();\n return Promise.all(cells.map(async cell => Number(await cell.getText())));\n }\n\n /**\n * How many pages there are — not how many are on the strip.\n *\n * Taken from the LAST cell, which the window always pins to the final page (the\n * first is pinned the same way), so this survives the gap that usually precedes\n * it. Deliberately not read from the compact `3 / 10` pager: that span is\n * `aria-hidden` and CSS-hidden outside responsive mode, so a browser-based\n * harness environment would read it as empty.\n */\n async getTotalPages(): Promise<number> {\n const pages = await this.getPages();\n const last = pages.at(-1);\n if (last === undefined) {\n throw new Error(\n 'WrPaginationHarness.getTotalPages(): the strip has no page cells at all. The pager offers page ' +\n '1 even for an empty list, so this is a rendering failure rather than an empty state.'\n );\n }\n return last;\n }\n\n /**\n * The page the pager announces as current, from `aria-current=\"page\"`.\n *\n * Throws when no cell claims it. The window always keeps the current page on the\n * strip, so getting here means `page` is not one of the pages at all. The\n * component clamps a host write into `1..totalPages`, which leaves one survivor:\n * a fractional page, which is in range and matches no cell — and which leaves\n * every cell looking equally inactive to an assistive tech user.\n */\n async getCurrentPage(): Promise<number> {\n const page = await this.currentPageOrNull();\n if (page === null) {\n throw new Error(\n 'WrPaginationHarness.getCurrentPage(): no cell carries `aria-current=\"page\"`. The visible window ' +\n `always includes the current page, so \\`page\\` is not a whole number — the component ` +\n `clamps it into 1..${await this.getTotalPages()} but does not round it.`\n );\n }\n return page;\n }\n\n /**\n * Click the cell for a page.\n *\n * Throws when that page is not on the strip — a seven-slot window over twenty\n * pages simply has no cell for page 15, and a silent no-op there reads as\n * \"navigation is broken\" three assertions later. Step with {@link next} /\n * {@link previous}, or move the host's own `page` model.\n *\n * A disabled pager is still clicked; the component's own guard is what refuses\n * the move, so assert the page rather than assume it. Clicking the CURRENT page\n * is a no-op by the same guard.\n */\n async goToPage(page: number): Promise<void> {\n for (const cell of await this.pageCells()) {\n if (Number(await cell.getText()) === page) {\n await cell.click();\n return;\n }\n }\n\n throw new Error(\n `WrPaginationHarness.goToPage(): page ${page} is not on the strip, which offers ` +\n `${(await this.getStrip()).join(' ')}. The window shows at most seven pages — step with next() / ` +\n 'previous(), or set the page on the host.'\n );\n }\n\n /**\n * Jump to page 1.\n *\n * There is no dedicated first / last control in this component; the window pins\n * the first and last page to the ends of the strip instead, so these two click\n * cells like any other page.\n */\n async goToFirst(): Promise<void> {\n return this.goToPage(1);\n }\n\n /** Jump to the last page — the trailing cell, not the last one before a gap. */\n async goToLast(): Promise<void> {\n return this.goToPage(await this.getTotalPages());\n }\n\n /**\n * Click the next-page control.\n *\n * Disabled on the last page, and a disabled `<wr-btn>` is still clicked here (see\n * {@link goToPage}) — {@link isNextDisabled} is the question, this is the action.\n */\n async next(): Promise<void> {\n return (await this.stepControl('next')).click();\n }\n\n /** Click the previous-page control. Disabled on the first page. */\n async previous(): Promise<void> {\n return (await this.stepControl('previous')).click();\n }\n\n /**\n * Whether the next-page control refuses the move — true on the last page, and\n * true for every control while the pager is disabled.\n *\n * Answered by {@link WrButtonHarness}, which reads the `disabled` attribute first\n * and `aria-disabled` second. A `<wr-btn>` element carries both: the attribute is\n * inert on anything but a real `<button>`, which is why the component adds the\n * ARIA state as well — so this is the accessible answer whichever branch supplies\n * it.\n */\n async isNextDisabled(): Promise<boolean> {\n return (await this.stepControl('next')).isDisabled();\n }\n\n /** Whether the previous-page control refuses the move — true on the first page. */\n async isPreviousDisabled(): Promise<boolean> {\n return (await this.stepControl('previous')).isDisabled();\n }\n\n /** Whether the pager shows its \"X–Y of Z\" label (the `showTotal` input). */\n async hasTotal(): Promise<boolean> {\n return (await this.totalLabel()) !== null;\n }\n\n /**\n * The total label exactly as rendered, e.g. `1–10 of 95`.\n *\n * The WHOLE string is one catalog entry (`pagination.range`) and the three\n * numbers in it are formatted per `LOCALE_ID`, so a spec asserting the literal\n * owns both the copy and the locale it asserts under. Note the range separator\n * is an en dash, not the ASCII hyphen this used to print.\n *\n * Throws when the label is not rendered, which is a `showTotal` that was never\n * set rather than a total of zero (a zero total still renders `0–0 of 0`).\n */\n async getTotalText(): Promise<string> {\n const label = await this.totalLabel();\n if (!label) {\n throw new Error(\n 'WrPaginationHarness.getTotalText(): this pager renders no total label — set `showTotal`. A ' +\n 'total of 0 still renders one.'\n );\n }\n return label.text();\n }\n\n /** Whether the pager renders its page-size changer (the `showSizeChanger` input). */\n async hasPageSizeChanger(): Promise<boolean> {\n return (await this.sizeChanger()) !== null;\n }\n\n /**\n * The page-size changer as a {@link WrSelectHarness} — for anything this harness\n * does not wrap: the offered labels, the open state, keyboard driving.\n *\n * Composed rather than re-queried on purpose: the changer is a real\n * `<wr-select>`, its options live in the overlay container rather than inside the\n * pager, and the select harness is the thing that already knows how to scope them\n * to their own trigger.\n *\n * Throws when there is no changer — an `undefined` here would turn into a\n * confusing failure inside a select call.\n */\n async getPageSizeSelect(): Promise<WrSelectHarness> {\n const select = await this.sizeChanger();\n if (!select) {\n throw new Error('WrPaginationHarness.getPageSizeSelect(): no page-size changer here — set `showSizeChanger`.');\n }\n return select;\n }\n\n /**\n * The page size currently in effect.\n *\n * The changer's trigger is the only place the size reaches the DOM, so this reads\n * it there and takes the digits: the label is localizable (`pagination.perPage`,\n * `{{size}} / page`) but interpolates the number in every language — unless a\n * catalog entry drops the placeholder, which is the second throw below.\n *\n * Throws when there is no changer at all: a pager without one says nothing on\n * screen about its page size, and the host's own model is where to assert it.\n */\n async getPageSize(): Promise<number> {\n const text = await (await this.getPageSizeSelect()).getValueText();\n const digits = /\\d+/.exec(text);\n if (!digits) {\n throw new Error(\n `WrPaginationHarness.getPageSize(): the changer reads \"${text}\", which names no number — a ` +\n 'catalog entry for `pagination.perPage` that drops `{{size}}` takes the size off the screen.'\n );\n }\n return Number(digits[0]);\n }\n\n /**\n * Pick a page size through the changer, the way a user does.\n *\n * Matched on the digits rather than the whole label, for the same reason\n * {@link getPageSize} reads them: `20 / page` and `20 / стр.` are the same option.\n * Throws when no option offers that size, naming what is on offer.\n *\n * Changing the size can move the page — the component pulls `page` back\n * when the new size leaves it past the end — so re-read {@link getCurrentPage}\n * afterwards rather than assuming it held.\n */\n async setPageSize(size: number): Promise<void> {\n const select = await this.getPageSizeSelect();\n if (await this.isDisabled()) {\n // Without this the call fails inside the select harness, whose message blames\n // a tag-mode panel and a `minChars` search that have nothing to do with it.\n throw new Error(\n 'WrPaginationHarness.setPageSize(): the pager is disabled, so its changer refuses to open — the ' +\n 'pager hands its own `disabled` straight to it.'\n );\n }\n\n await select.open();\n\n const labels = await select.getOptionLabels();\n const digits = new RegExp(`(^|\\\\D)${size}(\\\\D|$)`);\n const match = labels.find(label => digits.test(label));\n if (match === undefined) {\n throw new Error(\n `WrPaginationHarness.setPageSize(): no option offers ${size} — the changer offers ` +\n `${labels.join(', ')}. \\`pageSizeOptions\\` is what decides that list.`\n );\n }\n\n await select.selectOption({ text: match });\n }\n\n /** The current page, or `null` when no cell announces one. @see getCurrentPage */\n private async currentPageOrNull(): Promise<number | null> {\n const cell = await this.locatorForOptional(\n WrButtonHarness.with({ selector: '.wr-pagination__page[aria-current=\"page\"]' })\n )();\n return cell ? Number(await cell.getText()) : null;\n }\n\n private async stepControl(which: 'previous' | 'next'): Promise<WrButtonHarness> {\n const controls = await this.stepControls();\n if (controls.length !== 2) {\n throw new Error(\n `WrPaginationHarness: expected the nav to bracket the strip with a previous and a next control, ` +\n `found ${controls.length}. They are addressed by position, since their labels are localizable.`\n );\n }\n return which === 'previous' ? controls[0] : controls[1];\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|