ngwr 13.0.0 → 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.
Files changed (200) hide show
  1. package/README.md +13 -6
  2. package/color-picker/styles/_index.scss +43 -5
  3. package/fesm2022/ngwr-alert.mjs +5 -5
  4. package/fesm2022/ngwr-alert.mjs.map +1 -1
  5. package/fesm2022/ngwr-back-top.mjs +11 -1
  6. package/fesm2022/ngwr-back-top.mjs.map +1 -1
  7. package/fesm2022/ngwr-blur-text.mjs +13 -2
  8. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  9. package/fesm2022/ngwr-border-glow.mjs +12 -3
  10. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  11. package/fesm2022/ngwr-breadcrumbs.mjs +14 -3
  12. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  13. package/fesm2022/ngwr-button.mjs +4 -4
  14. package/fesm2022/ngwr-button.mjs.map +1 -1
  15. package/fesm2022/ngwr-calendar-testing.mjs +15 -0
  16. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
  17. package/fesm2022/ngwr-calendar.mjs +51 -5
  18. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  19. package/fesm2022/ngwr-circular-text.mjs +20 -1
  20. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  21. package/fesm2022/ngwr-collapse.mjs.map +1 -1
  22. package/fesm2022/ngwr-color-picker-testing.mjs +23 -7
  23. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
  24. package/fesm2022/ngwr-color-picker.mjs +89 -10
  25. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  26. package/fesm2022/ngwr-command-palette.mjs +27 -3
  27. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  28. package/fesm2022/ngwr-context-menu.mjs +13 -3
  29. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  30. package/fesm2022/ngwr-date-adapters-fns.mjs +101 -33
  31. package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
  32. package/fesm2022/ngwr-date-picker-testing.mjs +10 -2
  33. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  34. package/fesm2022/ngwr-date-picker.mjs +103 -13
  35. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  36. package/fesm2022/ngwr-date.mjs +480 -79
  37. package/fesm2022/ngwr-date.mjs.map +1 -1
  38. package/fesm2022/ngwr-dialog.mjs +22 -16
  39. package/fesm2022/ngwr-dialog.mjs.map +1 -1
  40. package/fesm2022/ngwr-directives.mjs +5 -2
  41. package/fesm2022/ngwr-directives.mjs.map +1 -1
  42. package/fesm2022/ngwr-drag-drop.mjs +30 -2
  43. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  44. package/fesm2022/ngwr-drawer.mjs +24 -18
  45. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  46. package/fesm2022/ngwr-dropdown.mjs +8 -3
  47. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  48. package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
  49. package/fesm2022/ngwr-event-calendar.mjs +1 -1
  50. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  51. package/fesm2022/ngwr-file-upload.mjs +52 -12
  52. package/fesm2022/ngwr-file-upload.mjs.map +1 -1
  53. package/fesm2022/ngwr-fuzzy-text.mjs +21 -4
  54. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  55. package/fesm2022/ngwr-gradient-text.mjs +9 -2
  56. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  57. package/fesm2022/ngwr-i18n-en.mjs +63 -1
  58. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  59. package/fesm2022/ngwr-i18n-ru.mjs +28 -1
  60. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  61. package/fesm2022/ngwr-i18n.mjs +150 -22
  62. package/fesm2022/ngwr-i18n.mjs.map +1 -1
  63. package/fesm2022/ngwr-icon-adapters-feather.mjs +11 -0
  64. package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -1
  65. package/fesm2022/ngwr-icon-adapters-lucide.mjs +22 -2
  66. package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -1
  67. package/fesm2022/ngwr-icon.mjs +474 -37
  68. package/fesm2022/ngwr-icon.mjs.map +1 -1
  69. package/fesm2022/ngwr-input-number.mjs +3 -3
  70. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  71. package/fesm2022/ngwr-input-otp.mjs +21 -3
  72. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  73. package/fesm2022/ngwr-input.mjs +14 -7
  74. package/fesm2022/ngwr-input.mjs.map +1 -1
  75. package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
  76. package/fesm2022/ngwr-knob.mjs +20 -2
  77. package/fesm2022/ngwr-knob.mjs.map +1 -1
  78. package/fesm2022/ngwr-loading-bar-router.mjs +73 -0
  79. package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -0
  80. package/fesm2022/ngwr-loading-bar.mjs +21 -32
  81. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  82. package/fesm2022/ngwr-markdown.mjs +63 -11
  83. package/fesm2022/ngwr-markdown.mjs.map +1 -1
  84. package/fesm2022/ngwr-mention.mjs +104 -14
  85. package/fesm2022/ngwr-mention.mjs.map +1 -1
  86. package/fesm2022/ngwr-overlay.mjs +36 -7
  87. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  88. package/fesm2022/ngwr-pagination-testing.mjs +11 -10
  89. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
  90. package/fesm2022/ngwr-pagination.mjs +65 -30
  91. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  92. package/fesm2022/ngwr-popconfirm.mjs +3 -2
  93. package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
  94. package/fesm2022/ngwr-popover.mjs +2 -1
  95. package/fesm2022/ngwr-popover.mjs.map +1 -1
  96. package/fesm2022/ngwr-qr.mjs +38 -1
  97. package/fesm2022/ngwr-qr.mjs.map +1 -1
  98. package/fesm2022/ngwr-select.mjs +93 -34
  99. package/fesm2022/ngwr-select.mjs.map +1 -1
  100. package/fesm2022/ngwr-shiny-text.mjs +10 -3
  101. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  102. package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
  103. package/fesm2022/ngwr-squircle.mjs +2 -1
  104. package/fesm2022/ngwr-squircle.mjs.map +1 -1
  105. package/fesm2022/ngwr-statistic.mjs +35 -4
  106. package/fesm2022/ngwr-statistic.mjs.map +1 -1
  107. package/fesm2022/ngwr-table.mjs +35 -12
  108. package/fesm2022/ngwr-table.mjs.map +1 -1
  109. package/fesm2022/ngwr-tabs-router.mjs +158 -0
  110. package/fesm2022/ngwr-tabs-router.mjs.map +1 -0
  111. package/fesm2022/ngwr-tabs-testing.mjs +13 -11
  112. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
  113. package/fesm2022/ngwr-tabs.mjs +104 -12
  114. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  115. package/fesm2022/ngwr-textarea.mjs +20 -1
  116. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  117. package/fesm2022/ngwr-tour.mjs +1 -1
  118. package/fesm2022/ngwr-tour.mjs.map +1 -1
  119. package/fesm2022/ngwr-transfer.mjs +2 -2
  120. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  121. package/fesm2022/ngwr-tree.mjs +18 -2
  122. package/fesm2022/ngwr-tree.mjs.map +1 -1
  123. package/fesm2022/ngwr-utils.mjs +143 -1
  124. package/fesm2022/ngwr-utils.mjs.map +1 -1
  125. package/fesm2022/ngwr-version.mjs +1 -1
  126. package/fesm2022/ngwr-version.mjs.map +1 -1
  127. package/fesm2022/ngwr-window-testing.mjs +10 -2
  128. package/fesm2022/ngwr-window-testing.mjs.map +1 -1
  129. package/fesm2022/ngwr-window.mjs +4 -4
  130. package/fesm2022/ngwr-window.mjs.map +1 -1
  131. package/i18n/en.json +33 -6
  132. package/i18n/ru.json +33 -6
  133. package/llms-full.txt +20 -11
  134. package/llms.txt +47 -16
  135. package/mcp/catalog.js +3 -3
  136. package/mcp/providers.js +11 -0
  137. package/package.json +11 -3
  138. package/schematics/migrations/v13/index.js +101 -0
  139. package/schematics/migrations/v14/index.js +422 -0
  140. package/schematics/migrations.json +10 -0
  141. package/schematics/page/templates.js +1 -1
  142. package/schematics/use/symbol-map.json +1 -0
  143. package/skills/ngwr/SKILL.md +4 -1
  144. package/skills/ngwr/references/catalog.md +2 -0
  145. package/skills/ngwr/references/setup.md +4 -0
  146. package/stepper/styles/_index.scss +19 -1
  147. package/textarea/styles/_index.scss +14 -6
  148. package/toast/styles/_index.scss +12 -4
  149. package/types/ngwr-alert.d.ts +3 -3
  150. package/types/ngwr-blur-text.d.ts +8 -0
  151. package/types/ngwr-breadcrumbs.d.ts +1 -0
  152. package/types/ngwr-button.d.ts +2 -2
  153. package/types/ngwr-calendar-testing.d.ts +21 -4
  154. package/types/ngwr-calendar.d.ts +37 -0
  155. package/types/ngwr-circular-text.d.ts +9 -0
  156. package/types/ngwr-collapse.d.ts +19 -15
  157. package/types/ngwr-color-picker-testing.d.ts +10 -4
  158. package/types/ngwr-color-picker.d.ts +70 -2
  159. package/types/ngwr-command-palette.d.ts +14 -0
  160. package/types/ngwr-context-menu.d.ts +1 -1
  161. package/types/ngwr-date-adapters-fns.d.ts +71 -29
  162. package/types/ngwr-date-picker-testing.d.ts +206 -185
  163. package/types/ngwr-date-picker.d.ts +7 -0
  164. package/types/ngwr-date.d.ts +143 -13
  165. package/types/ngwr-drag-drop.d.ts +19 -0
  166. package/types/ngwr-dropdown.d.ts +1 -1
  167. package/types/ngwr-event-calendar-testing.d.ts +12 -9
  168. package/types/ngwr-file-upload.d.ts +31 -3
  169. package/types/ngwr-fuzzy-text.d.ts +5 -0
  170. package/types/ngwr-i18n.d.ts +89 -7
  171. package/types/ngwr-icon-adapters-feather.d.ts +11 -0
  172. package/types/ngwr-icon.d.ts +42 -1
  173. package/types/ngwr-input-number.d.ts +1 -1
  174. package/types/ngwr-input-otp.d.ts +3 -0
  175. package/types/ngwr-input.d.ts +11 -4
  176. package/types/ngwr-knob-testing.d.ts +8 -4
  177. package/types/ngwr-knob.d.ts +18 -0
  178. package/types/ngwr-loading-bar-router.d.ts +35 -0
  179. package/types/ngwr-loading-bar.d.ts +19 -9
  180. package/types/ngwr-mention.d.ts +13 -0
  181. package/types/ngwr-pagination-testing.d.ts +11 -10
  182. package/types/ngwr-pagination.d.ts +39 -10
  183. package/types/ngwr-popconfirm.d.ts +1 -1
  184. package/types/ngwr-popover.d.ts +1 -1
  185. package/types/ngwr-select.d.ts +65 -10
  186. package/types/ngwr-shiny-text.d.ts +1 -0
  187. package/types/ngwr-splitter-testing.d.ts +9 -4
  188. package/types/ngwr-squircle.d.ts +1 -1
  189. package/types/ngwr-statistic.d.ts +22 -2
  190. package/types/ngwr-table.d.ts +24 -5
  191. package/types/ngwr-tabs-router.d.ts +67 -0
  192. package/types/ngwr-tabs-testing.d.ts +12 -10
  193. package/types/ngwr-tabs.d.ts +103 -6
  194. package/types/ngwr-textarea.d.ts +9 -0
  195. package/types/ngwr-tree.d.ts +7 -0
  196. package/types/ngwr-utils.d.ts +79 -1
  197. package/types/ngwr-version.d.ts +1 -1
  198. package/types/ngwr-window-testing.d.ts +11 -3
  199. package/types/ngwr-window.d.ts +11 -4
  200. package/window/styles/_index.scss +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-utils.mjs","sources":["../../../projects/lib/utils/guards/is-defined.ts","../../../projects/lib/utils/guards/is-non-empty-array.ts","../../../projects/lib/utils/guards/is-observable.ts","../../../projects/lib/utils/math/clamp.ts","../../../projects/lib/utils/math/round.ts","../../../projects/lib/utils/coercion/num-attr.ts","../../../projects/lib/utils/dom/get-root-font-size.ts","../../../projects/lib/utils/dom/focus.ts","../../../projects/lib/utils/css-size/resolve-css-size.ts","../../../projects/lib/utils/id/random-id.ts","../../../projects/lib/utils/fn/noop.ts","../../../projects/lib/utils/fn/debounce.ts","../../../projects/lib/utils/fn/throttle.ts","../../../projects/lib/utils/keyboard/keys.ts","../../../projects/lib/utils/keyboard/predicates.ts","../../../projects/lib/utils/log/badge-log.ts","../../../projects/lib/utils/public-api.ts","../../../projects/lib/utils/ngwr-utils.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { Maybe } from '../interfaces';\n\n/**\n * Type guard that checks if a value is neither `null` nor `undefined`.\n *\n * @example\n * ```ts\n * const maybeValue: Maybe<number> = getValue();\n *\n * if (isDefined(maybeValue)) {\n * // here maybeValue is number\n * console.log(maybeValue.toFixed(2));\n * }\n * ```\n *\n * @see {@link Maybe}\n */\nexport function isDefined<T>(value: Maybe<T>): value is T {\n return value !== null && value !== undefined;\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { Maybe } from '../interfaces';\n\n/**\n * Type guard that checks if a value is an array with at least one element.\n *\n * @example\n * ```ts\n * const items: Maybe<string[]> = getItems();\n *\n * if (isNonEmptyArray(items)) {\n * // items is string[]\n * console.log(items[0]);\n * }\n * ```\n */\nexport function isNonEmptyArray<T>(value: Maybe<T[]>): value is [T, ...T[]] {\n return Array.isArray(value) && value.length > 0;\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { Observable } from 'rxjs';\n\n/**\n * Type guard that checks whether a value looks like an RxJS `Observable`.\n *\n * Uses duck-typing: verifies that the object has a `subscribe` function.\n * Does not require an actual `Observable` instance.\n *\n * @example\n * ```ts\n * if (isObservable(stream)) {\n * stream.subscribe(value => console.log(value));\n * }\n * ```\n */\nexport function isObservable<T = unknown>(value: unknown): value is Observable<T> {\n return !!value && typeof (value as { subscribe?: unknown }).subscribe === 'function';\n}\n","/**\n * Clamps `value` into the inclusive `[min, max]` range.\n *\n * @example\n * ```ts\n * clamp(140, 0, 100); // 100\n * clamp(-3, 0, 100); // 0\n * clamp(42, 0, 100); // 42\n * ```\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n","/**\n * Rounds `value` to `decimals` fraction digits, compensating the usual\n * floating point drift (`0.1 + 0.2` rounds to `0.3`, not `0.30000000000000004`).\n *\n * @example\n * ```ts\n * round(0.1 + 0.2, 2); // 0.3\n * round(1.005, 2); // 1.01\n * round(7.4); // 7\n * ```\n */\nexport function round(value: number, decimals = 0): number {\n const factor = 10 ** decimals;\n return Math.round((value + Number.EPSILON) * factor) / factor;\n}\n","import { coerceNumberProperty } from '@angular/cdk/coercion';\n\n/**\n * Input-transform factory: coerces any bound value to a number, falling\n * back to `fallback` for `null` / `undefined` / unparsable input. The\n * standard transform for numeric `input()`s across ngwr.\n *\n * @example\n * ```ts\n * readonly speed = input(4, { transform: numAttr(4) });\n * ```\n */\nexport const numAttr =\n (fallback: number) =>\n (value: unknown): number =>\n coerceNumberProperty(value, fallback);\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\n/**\n * Reads the root font size (in pixels) from\n * `getComputedStyle(document.documentElement).fontSize`.\n *\n * Typical use case: converting `rem` values to pixels for layout calculations.\n *\n * Falls back to the provided value when:\n * - DOM is not available (SSR);\n * - `getComputedStyle` fails or returns an unparseable value.\n *\n * @example\n * ```ts\n * const rootFontSize = getRootFontSize(); // e.g. 16\n * const customFallback = getRootFontSize(14); // uses 14 if DOM is unavailable\n * ```\n *\n * @param fallback - Value in pixels when DOM is unavailable. Defaults to `16`.\n * @returns Root font size in pixels.\n */\nexport function getRootFontSize(fallback = 16): number {\n try {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return fallback;\n }\n\n const raw = window.getComputedStyle(document.documentElement).fontSize;\n const parsed = Number.parseFloat(raw);\n\n return Number.isFinite(parsed) ? parsed : fallback;\n } catch {\n return fallback;\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\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'iframe',\n 'object',\n 'embed',\n 'audio[controls]',\n 'video[controls]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(',');\n\n/** Return every focusable descendant of `root` in DOM order. */\nexport function getFocusableElements(root: HTMLElement): readonly HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter(\n el => el.offsetParent !== null || el === document.activeElement\n );\n}\n\n/**\n * Trap Tab navigation inside `root` for a keyboard event — call from a\n * `keydown` handler. Cycles focus from first ↔ last focusable element.\n * Returns `true` when focus was redirected (event was handled).\n */\nexport function trapFocus(root: HTMLElement, event: KeyboardEvent): boolean {\n if (event.key !== 'Tab') return false;\n const focusable = getFocusableElements(root);\n if (focusable.length === 0) return false;\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && active === first) {\n event.preventDefault();\n last.focus();\n return true;\n }\n if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n return true;\n }\n return false;\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 { getRootFontSize } from '../dom';\n\nimport type { ResolveCssSizeOptions } from './resolve-css-size-options';\nimport type { ResolvedCssSize } from './resolved-css-size';\n\n/**\n * Resolves a raw size value into a CSS string and an optional pixel equivalent.\n *\n * Supported formats:\n * - `number` → pixels (`48` → `\"48px\"`)\n * - `\"12px\"` → pixels (`\"12px\"` → `\"12px\"`)\n * - `\"3rem\"` → rems via root (`\"3rem\"` → `\"3rem\"`, pxValue = 3 × root font size)\n * - `\"80%\"` → percentage (`\"80%\"` → `\"80%\"`, pxValue = null)\n * - `\"15\"` → pixels (`\"15\"` → `\"15px\"`)\n * - anything else → passed through as-is, pxValue = null\n *\n * @example\n * ```ts\n * const size = resolveCssSize('3rem', { defaultValue: '6rem' });\n * // size.cssValue === '3rem'\n * // size.pxValue === 3 * getRootFontSize()\n * ```\n *\n * @param raw - Value to resolve. Accepts numbers, pixel/rem/percent strings, or `null`/`undefined`.\n * @param options - Optional configuration.\n * @returns Resolved CSS value and its pixel equivalent when computable.\n *\n * @see {@link ResolvedCssSize}\n * @see {@link ResolveCssSizeOptions}\n */\nexport function resolveCssSize(raw: unknown, options: ResolveCssSizeOptions = {}): ResolvedCssSize {\n const rootFontSize = getRootFontSize();\n const value: unknown = raw ?? options.defaultValue;\n\n if (value == null) {\n return { cssValue: '0px', pxValue: 0 };\n }\n\n if (typeof value === 'number' && Number.isFinite(value)) {\n return { cssValue: `${value}px`, pxValue: value };\n }\n\n if (typeof value !== 'string') {\n return { cssValue: '0px', pxValue: 0 };\n }\n\n const trimmed = value.trim();\n\n const remMatch = /^([\\d.]+)\\s*rem$/i.exec(trimmed);\n if (remMatch) {\n const rem = Number.parseFloat(remMatch[1]);\n const px = rem * rootFontSize;\n return { cssValue: `${rem}rem`, pxValue: Number.isFinite(px) ? px : null };\n }\n\n const pxMatch = /^([\\d.]+)\\s*px$/i.exec(trimmed);\n if (pxMatch) {\n const px = Number.parseFloat(pxMatch[1]);\n return { cssValue: `${px}px`, pxValue: Number.isFinite(px) ? px : null };\n }\n\n const percentMatch = /^([\\d.]+)\\s*%$/.exec(trimmed);\n if (percentMatch) {\n return { cssValue: `${percentMatch[1]}%`, pxValue: null };\n }\n\n const numberMatch = /^([\\d.]+)$/.exec(trimmed);\n if (numberMatch) {\n const px = Number.parseFloat(numberMatch[1]);\n return { cssValue: `${px}px`, pxValue: Number.isFinite(px) ? px : null };\n }\n\n return { cssValue: trimmed, pxValue: null };\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\nconst ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789';\nconst MIN_LENGTH = 4;\nconst MAX_LENGTH = 64;\nconst DEFAULT_LENGTH = 12;\n\n/**\n * Returns random bytes using the best available source.\n *\n * @internal\n */\nfunction defaultRandomBytes(count: number): Uint8Array {\n // Covers: all browsers, Node ≥ 18, Deno, Bun, Cloudflare Workers\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n return crypto.getRandomValues(new Uint8Array(count));\n }\n\n // Fallback: Node < 18 or exotic runtimes\n const buf = new Uint8Array(count);\n for (let i = 0; i < count; i++) {\n buf[i] = Math.floor(Math.random() * 256);\n }\n return buf;\n}\n\n/**\n * Generates a pseudo-random, DOM-safe identifier.\n *\n * Produced ids:\n * - are prefixed to avoid collisions with host-application ids;\n * - contain only `[a-z0-9-]` characters;\n * - use `crypto.getRandomValues` when available, falling back to `Math.random`.\n *\n * **Not suitable for cryptographic or security-sensitive purposes** —\n * intended only for UI element identifiers (`id`, `for`, `aria-*`, etc.).\n *\n * @example\n * ```ts\n * randomId();\n * // → \"wr-a8f2bx1k9q4z\"\n *\n * randomId('ng', 8);\n * // → \"ng-k2f9mbeo\"\n *\n * randomId('wr', 24);\n * // → \"wr-a8f2bx1k9q4zmneo73wplr\"\n * ```\n *\n * @param prefix - Namespace prefix prepended before the random segment. Defaults to `'wr'`.\n * @param length - Length of the random segment. Clamped to `[4, 64]`. Defaults to `12`.\n * @param random - Custom random byte source `(byteLength) => Uint8Array`.\n * When omitted, uses `crypto.getRandomValues` with a `Math.random` fallback.\n * @returns A pseudo-random id string matching `/^[a-z]+-[a-z0-9]+$/`.\n */\nexport function randomId(prefix = 'wr', length = DEFAULT_LENGTH, random?: (byteLength: number) => Uint8Array): string {\n const safeLength = Math.min(Math.max(Math.trunc(length), MIN_LENGTH), MAX_LENGTH);\n\n const bytes = random ? random(safeLength) : defaultRandomBytes(safeLength);\n\n let result = '';\n for (let i = 0; i < safeLength; i++) {\n result += ALPHABET[bytes[i] % ALPHABET.length];\n }\n\n return `${prefix}-${result}`;\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\n/**\n * A function that does nothing.\n *\n * Useful as a default callback, placeholder event handler,\n * or to explicitly signal intentional no-ops.\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class MyComponent {\n * onTouched: () => void = noop;\n * }\n * ```\n */\nexport const noop = (): void => undefined;\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\n/** A debounced function — same signature as the original plus `cancel`. */\nexport type WrDebouncedFn<TArgs extends readonly unknown[]> = ((...args: TArgs) => void) & {\n readonly cancel: () => void;\n};\n\n/**\n * Wrap `fn` so it only fires `waitMs` after the last invocation.\n * Useful for resize / scroll / input handlers.\n *\n * @example\n * ```ts\n * const onResize = debounce(() => recalcLayout(), 150);\n * window.addEventListener('resize', onResize);\n * ```\n */\nexport function debounce<TArgs extends readonly unknown[]>(\n fn: (...args: TArgs) => void,\n waitMs: number\n): WrDebouncedFn<TArgs> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const debounced = (...args: TArgs): void => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n fn(...args);\n }, waitMs);\n };\n\n debounced.cancel = (): void => {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n return debounced;\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\n/** A throttled function — same signature as the original plus `cancel`. */\nexport type WrThrottledFn<TArgs extends readonly unknown[]> = ((...args: TArgs) => void) & {\n readonly cancel: () => void;\n};\n\n/**\n * Wrap `fn` so it fires at most once every `waitMs`. Leading-edge call\n * fires immediately; trailing-edge call fires once at the end of the window\n * if there were further invocations.\n */\nexport function throttle<TArgs extends readonly unknown[]>(\n fn: (...args: TArgs) => void,\n waitMs: number\n): WrThrottledFn<TArgs> {\n let lastInvoke = 0;\n let trailingTimer: ReturnType<typeof setTimeout> | null = null;\n let pendingArgs: TArgs | null = null;\n\n const throttled = (...args: TArgs): void => {\n const now = Date.now();\n const remaining = waitMs - (now - lastInvoke);\n\n if (remaining <= 0) {\n if (trailingTimer !== null) {\n clearTimeout(trailingTimer);\n trailingTimer = null;\n }\n lastInvoke = now;\n fn(...args);\n } else {\n pendingArgs = args;\n trailingTimer ??= setTimeout(() => {\n lastInvoke = Date.now();\n trailingTimer = null;\n if (pendingArgs) fn(...pendingArgs);\n pendingArgs = null;\n }, remaining);\n }\n };\n\n throttled.cancel = (): void => {\n if (trailingTimer !== null) {\n clearTimeout(trailingTimer);\n trailingTimer = null;\n }\n pendingArgs = null;\n lastInvoke = 0;\n };\n\n return throttled;\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\n/**\n * Canonical `KeyboardEvent.key` values for your own `keydown` handlers.\n *\n * The spec spellings are easy to get wrong — arrows are `ArrowUp`, space is a\n * literal `' '` — and a typo like `'Esacpe'` never matches and never errors.\n * These give you autocomplete and the {@link WrKey} union to match against.\n */\nexport const KEYS = {\n ENTER: 'Enter',\n ESCAPE: 'Escape',\n TAB: 'Tab',\n SPACE: ' ',\n BACKSPACE: 'Backspace',\n DELETE: 'Delete',\n ARROW_UP: 'ArrowUp',\n ARROW_DOWN: 'ArrowDown',\n ARROW_LEFT: 'ArrowLeft',\n ARROW_RIGHT: 'ArrowRight',\n HOME: 'Home',\n END: 'End',\n PAGE_UP: 'PageUp',\n PAGE_DOWN: 'PageDown',\n} as const;\n\nexport type WrKey = (typeof KEYS)[keyof typeof KEYS];\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\n/** True when any modifier key (Ctrl / Cmd / Alt / Shift / Meta) is held. */\nexport function hasModifier(event: KeyboardEvent): boolean {\n return event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;\n}\n\n/**\n * True when the key is a single printable character (letters, digits,\n * punctuation, etc.). Skips function keys, arrows, modifiers, and named\n * keys like `Backspace` or `Tab`.\n */\nexport function isPrintableKey(event: KeyboardEvent): boolean {\n return event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;\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\n/**\n * Logs a coloured pill to the browser console followed by a payload.\n *\n * Useful when you need to scan a noisy console for a specific subsystem's\n * messages. The pill is rendered with the CSS `%c` console formatting trick.\n *\n * @example\n * ```ts\n * badgeLog('icons', '#3969e2', { name: 'plus', registered: true });\n * // → [icons] { name: 'plus', registered: true }\n * ```\n *\n * @param badgeText Short label shown inside the pill.\n * @param color CSS colour applied to the pill border and text.\n * @param message Anything you'd pass to `console.log` as the second argument.\n *\n * @internal\n */\nexport function badgeLog(badgeText: string, color: string, message: unknown): void {\n const style = `\n display: inline-block;\n border: 1px solid ${color};\n color: ${color};\n padding: 1px 3px;\n border-radius: 4px;\n margin-right: 0\n `;\n // eslint-disable-next-line no-console -- this helper exists to log\n console.log(`%c${badgeText}`, style, message);\n}\n","// Types\nexport type { Maybe, SafeAny } from './interfaces';\n\n// Guards\nexport { isDefined, isNonEmptyArray, isObservable } from './guards';\n\n// Math\nexport { clamp, round } from './math';\n\n// Coercion\nexport { numAttr } from './coercion';\n\n// CSS size\nexport { resolveCssSize } from './css-size';\nexport type { ResolveCssSizeOptions, ResolvedCssSize } from './css-size';\n\n// DOM\nexport { getRootFontSize, getFocusableElements, trapFocus } from './dom';\n\n// ID\nexport { randomId } from './id';\n\n// Fn\nexport { noop, debounce, throttle } from './fn';\nexport type { WrDebouncedFn, WrThrottledFn } from './fn';\n\n// Keyboard\nexport { KEYS, hasModifier, isPrintableKey } from './keyboard';\nexport type { WrKey } from './keyboard';\n\n// Log\nexport { badgeLog } from './log';\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-utils.mjs","sources":["../../../projects/lib/utils/guards/is-defined.ts","../../../projects/lib/utils/guards/is-non-empty-array.ts","../../../projects/lib/utils/guards/is-observable.ts","../../../projects/lib/utils/math/clamp.ts","../../../projects/lib/utils/math/round.ts","../../../projects/lib/utils/coercion/num-attr.ts","../../../projects/lib/utils/css-value/is-safe-css-value.ts","../../../projects/lib/utils/dom/get-root-font-size.ts","../../../projects/lib/utils/dom/focus.ts","../../../projects/lib/utils/css-size/resolve-css-size.ts","../../../projects/lib/utils/id/random-id.ts","../../../projects/lib/utils/fn/noop.ts","../../../projects/lib/utils/fn/debounce.ts","../../../projects/lib/utils/fn/throttle.ts","../../../projects/lib/utils/keyboard/keys.ts","../../../projects/lib/utils/keyboard/predicates.ts","../../../projects/lib/utils/log/badge-log.ts","../../../projects/lib/utils/public-api.ts","../../../projects/lib/utils/ngwr-utils.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { Maybe } from '../interfaces';\n\n/**\n * Type guard that checks if a value is neither `null` nor `undefined`.\n *\n * @example\n * ```ts\n * const maybeValue: Maybe<number> = getValue();\n *\n * if (isDefined(maybeValue)) {\n * // here maybeValue is number\n * console.log(maybeValue.toFixed(2));\n * }\n * ```\n *\n * @see {@link Maybe}\n */\nexport function isDefined<T>(value: Maybe<T>): value is T {\n return value !== null && value !== undefined;\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { Maybe } from '../interfaces';\n\n/**\n * Type guard that checks if a value is an array with at least one element.\n *\n * @example\n * ```ts\n * const items: Maybe<string[]> = getItems();\n *\n * if (isNonEmptyArray(items)) {\n * // items is string[]\n * console.log(items[0]);\n * }\n * ```\n */\nexport function isNonEmptyArray<T>(value: Maybe<T[]>): value is [T, ...T[]] {\n return Array.isArray(value) && value.length > 0;\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { Observable } from 'rxjs';\n\n/**\n * Type guard that checks whether a value looks like an RxJS `Observable`.\n *\n * Uses duck-typing: verifies that the object has a `subscribe` function.\n * Does not require an actual `Observable` instance.\n *\n * @example\n * ```ts\n * if (isObservable(stream)) {\n * stream.subscribe(value => console.log(value));\n * }\n * ```\n */\nexport function isObservable<T = unknown>(value: unknown): value is Observable<T> {\n return !!value && typeof (value as { subscribe?: unknown }).subscribe === 'function';\n}\n","/**\n * Clamps `value` into the inclusive `[min, max]` range.\n *\n * @example\n * ```ts\n * clamp(140, 0, 100); // 100\n * clamp(-3, 0, 100); // 0\n * clamp(42, 0, 100); // 42\n * ```\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n","/**\n * Rounds `value` to `decimals` fraction digits, compensating the usual\n * floating point drift (`0.1 + 0.2` rounds to `0.3`, not `0.30000000000000004`).\n *\n * @example\n * ```ts\n * round(0.1 + 0.2, 2); // 0.3\n * round(1.005, 2); // 1.01\n * round(7.4); // 7\n * ```\n */\nexport function round(value: number, decimals = 0): number {\n const factor = 10 ** decimals;\n return Math.round((value + Number.EPSILON) * factor) / factor;\n}\n","import { coerceNumberProperty } from '@angular/cdk/coercion';\n\n/**\n * Input-transform factory: coerces any bound value to a number, falling\n * back to `fallback` for `null` / `undefined` / unparsable input. The\n * standard transform for numeric `input()`s across ngwr.\n *\n * @example\n * ```ts\n * readonly speed = input(4, { transform: numAttr(4) });\n * ```\n */\nexport const numAttr =\n (fallback: number) =>\n (value: unknown): number =>\n coerceNumberProperty(value, fallback);\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\n/** Longest value worth composing. A colour is never near this. */\nconst MAX_LENGTH = 256;\n\n/**\n * Characters that end a declaration, open a string, or start a comment — none\n * of which belong in a value the library is about to wrap its own syntax around.\n */\nconst FORBIDDEN = /[;{}\"'\\\\@<>!]|\\/\\*|\\*\\//;\n\n/** Function name immediately preceding an open paren. */\nconst FUNCTION_NAME = /([\\w-]*)\\($/;\n\n/**\n * Functions a composed value may call. Every image-producing function is absent\n * on purpose — `url()`, `image-set()`, `cross-fade()`, `element()`, `paint()` —\n * because fetching is the whole impact of this class.\n */\nconst ALLOWED_FUNCTIONS = new Set([\n 'rgb',\n 'rgba',\n 'hsl',\n 'hsla',\n 'hwb',\n 'lab',\n 'lch',\n 'oklab',\n 'oklch',\n 'color',\n 'color-mix',\n 'light-dark',\n 'var',\n 'calc',\n 'min',\n 'max',\n 'clamp',\n]);\n\n/**\n * Answers whether a value can be composed **into** a larger CSS expression\n * without escaping the slot it was given — a colour inside a `linear-gradient()`\n * the component builds, an entry the component joins into a custom property a\n * stylesheet then substitutes.\n *\n * It is a containment check, not a colour parser. It says the value cannot\n * terminate the construct the library wrapped around it; it says nothing about\n * whether the value is a colour a browser will accept, and a rejected value\n * should fall back to the component's own default rather than be repaired.\n *\n * **A value bound straight to `[style.background]` needs none of this.** There\n * is nothing to break out of there, and Angular applies style values through\n * CSSOM by design — validating those would amount to refusing style values in a\n * style binding.\n *\n * Rejected, and each one is a way out of the slot:\n *\n * - a `)` the value never opened — `red), url(…), linear-gradient(red` is the\n * shape that turns a colour into an extra background layer the browser fetches;\n * - a top-level `,` — a `<color>` is one value, so a comma at depth 0 means the\n * payload has reached the slot next to the one it was handed;\n * - `;` `{` `}` a quote, a backslash, `!`, or a CSS comment delimiter;\n * - any function outside {@link ALLOWED_FUNCTIONS} — `url()` above all.\n *\n * Top-level whitespace is allowed: `red 20%` is a legitimate gradient stop, and\n * without a comma or a bare `)` it cannot reach anything.\n *\n * @example\n * ```ts\n * isSafeCssValue('var(--wr-color-primary)'); // true\n * isSafeCssValue('color-mix(in srgb, red 50%, blue)'); // true\n * isSafeCssValue('red), url(\"https://x.test/beacon\"), linear-gradient(red'); // false\n * ```\n *\n * @param value Raw value handed to the component.\n * @returns `true` when the value stays inside its slot.\n */\nexport function isSafeCssValue(value: string): boolean {\n if (typeof value !== 'string') return false;\n\n const trimmed = value.trim();\n if (trimmed === '' || trimmed.length > MAX_LENGTH) return false;\n if (FORBIDDEN.test(trimmed)) return false;\n\n let depth = 0;\n for (let i = 0; i < trimmed.length; i++) {\n const char = trimmed[i];\n\n if (char === '(') {\n const name = FUNCTION_NAME.exec(trimmed.slice(0, i + 1));\n if (!name || !ALLOWED_FUNCTIONS.has(name[1].toLowerCase())) return false;\n depth++;\n } else if (char === ')') {\n depth--;\n if (depth < 0) return false;\n } else if (depth === 0 && char === ',') {\n return false;\n }\n }\n\n return depth === 0;\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\n/**\n * Reads the root font size (in pixels) from\n * `getComputedStyle(document.documentElement).fontSize`.\n *\n * Typical use case: converting `rem` values to pixels for layout calculations.\n *\n * Falls back to the provided value when:\n * - DOM is not available (SSR);\n * - `getComputedStyle` fails or returns an unparseable value.\n *\n * @example\n * ```ts\n * const rootFontSize = getRootFontSize(); // e.g. 16\n * const customFallback = getRootFontSize(14); // uses 14 if DOM is unavailable\n * ```\n *\n * @param fallback - Value in pixels when DOM is unavailable. Defaults to `16`.\n * @returns Root font size in pixels.\n */\nexport function getRootFontSize(fallback = 16): number {\n try {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return fallback;\n }\n\n const raw = window.getComputedStyle(document.documentElement).fontSize;\n const parsed = Number.parseFloat(raw);\n\n return Number.isFinite(parsed) ? parsed : fallback;\n } catch {\n return fallback;\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\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'iframe',\n 'object',\n 'embed',\n 'audio[controls]',\n 'video[controls]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(',');\n\n/** Return every focusable descendant of `root` in DOM order. */\nexport function getFocusableElements(root: HTMLElement): readonly HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter(\n el => el.offsetParent !== null || el === document.activeElement\n );\n}\n\n/**\n * Trap Tab navigation inside `root` for a keyboard event — call from a\n * `keydown` handler. Cycles focus from first ↔ last focusable element.\n * Returns `true` when focus was redirected (event was handled).\n */\nexport function trapFocus(root: HTMLElement, event: KeyboardEvent): boolean {\n if (event.key !== 'Tab') return false;\n const focusable = getFocusableElements(root);\n if (focusable.length === 0) return false;\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && active === first) {\n event.preventDefault();\n last.focus();\n return true;\n }\n if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n return true;\n }\n return false;\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 { getRootFontSize } from '../dom';\n\nimport type { ResolveCssSizeOptions } from './resolve-css-size-options';\nimport type { ResolvedCssSize } from './resolved-css-size';\n\n/**\n * Resolves a raw size value into a CSS string and an optional pixel equivalent.\n *\n * Supported formats:\n * - `number` → pixels (`48` → `\"48px\"`)\n * - `\"12px\"` → pixels (`\"12px\"` → `\"12px\"`)\n * - `\"3rem\"` → rems via root (`\"3rem\"` → `\"3rem\"`, pxValue = 3 × root font size)\n * - `\"80%\"` → percentage (`\"80%\"` → `\"80%\"`, pxValue = null)\n * - `\"15\"` → pixels (`\"15\"` → `\"15px\"`)\n * - anything else → passed through as-is, pxValue = null\n *\n * @example\n * ```ts\n * const size = resolveCssSize('3rem', { defaultValue: '6rem' });\n * // size.cssValue === '3rem'\n * // size.pxValue === 3 * getRootFontSize()\n * ```\n *\n * @param raw - Value to resolve. Accepts numbers, pixel/rem/percent strings, or `null`/`undefined`.\n * @param options - Optional configuration.\n * @returns Resolved CSS value and its pixel equivalent when computable.\n *\n * @see {@link ResolvedCssSize}\n * @see {@link ResolveCssSizeOptions}\n */\nexport function resolveCssSize(raw: unknown, options: ResolveCssSizeOptions = {}): ResolvedCssSize {\n const rootFontSize = getRootFontSize();\n const value: unknown = raw ?? options.defaultValue;\n\n if (value == null) {\n return { cssValue: '0px', pxValue: 0 };\n }\n\n if (typeof value === 'number' && Number.isFinite(value)) {\n return { cssValue: `${value}px`, pxValue: value };\n }\n\n if (typeof value !== 'string') {\n return { cssValue: '0px', pxValue: 0 };\n }\n\n const trimmed = value.trim();\n\n const remMatch = /^([\\d.]+)\\s*rem$/i.exec(trimmed);\n if (remMatch) {\n const rem = Number.parseFloat(remMatch[1]);\n const px = rem * rootFontSize;\n return { cssValue: `${rem}rem`, pxValue: Number.isFinite(px) ? px : null };\n }\n\n const pxMatch = /^([\\d.]+)\\s*px$/i.exec(trimmed);\n if (pxMatch) {\n const px = Number.parseFloat(pxMatch[1]);\n return { cssValue: `${px}px`, pxValue: Number.isFinite(px) ? px : null };\n }\n\n const percentMatch = /^([\\d.]+)\\s*%$/.exec(trimmed);\n if (percentMatch) {\n return { cssValue: `${percentMatch[1]}%`, pxValue: null };\n }\n\n const numberMatch = /^([\\d.]+)$/.exec(trimmed);\n if (numberMatch) {\n const px = Number.parseFloat(numberMatch[1]);\n return { cssValue: `${px}px`, pxValue: Number.isFinite(px) ? px : null };\n }\n\n return { cssValue: trimmed, pxValue: null };\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\nconst ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789';\nconst MIN_LENGTH = 4;\nconst MAX_LENGTH = 64;\nconst DEFAULT_LENGTH = 12;\n\n/**\n * Returns random bytes using the best available source.\n *\n * @internal\n */\nfunction defaultRandomBytes(count: number): Uint8Array {\n // Covers: all browsers, Node ≥ 18, Deno, Bun, Cloudflare Workers\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n return crypto.getRandomValues(new Uint8Array(count));\n }\n\n // Fallback: Node < 18 or exotic runtimes\n const buf = new Uint8Array(count);\n for (let i = 0; i < count; i++) {\n buf[i] = Math.floor(Math.random() * 256);\n }\n return buf;\n}\n\n/**\n * Generates a pseudo-random, DOM-safe identifier.\n *\n * Produced ids:\n * - are prefixed to avoid collisions with host-application ids;\n * - contain only `[a-z0-9-]` characters;\n * - use `crypto.getRandomValues` when available, falling back to `Math.random`.\n *\n * **Not suitable for cryptographic or security-sensitive purposes** —\n * intended only for UI element identifiers (`id`, `for`, `aria-*`, etc.).\n *\n * @example\n * ```ts\n * randomId();\n * // → \"wr-a8f2bx1k9q4z\"\n *\n * randomId('ng', 8);\n * // → \"ng-k2f9mbeo\"\n *\n * randomId('wr', 24);\n * // → \"wr-a8f2bx1k9q4zmneo73wplr\"\n * ```\n *\n * @param prefix - Namespace prefix prepended before the random segment. Defaults to `'wr'`.\n * @param length - Length of the random segment. Clamped to `[4, 64]`. Defaults to `12`.\n * @param random - Custom random byte source `(byteLength) => Uint8Array`.\n * When omitted, uses `crypto.getRandomValues` with a `Math.random` fallback.\n * @returns A pseudo-random id string matching `/^[a-z]+-[a-z0-9]+$/`.\n */\nexport function randomId(prefix = 'wr', length = DEFAULT_LENGTH, random?: (byteLength: number) => Uint8Array): string {\n const safeLength = Math.min(Math.max(Math.trunc(length), MIN_LENGTH), MAX_LENGTH);\n\n const bytes = random ? random(safeLength) : defaultRandomBytes(safeLength);\n\n let result = '';\n for (let i = 0; i < safeLength; i++) {\n result += ALPHABET[bytes[i] % ALPHABET.length];\n }\n\n return `${prefix}-${result}`;\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\n/**\n * A function that does nothing.\n *\n * Useful as a default callback, placeholder event handler,\n * or to explicitly signal intentional no-ops.\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class MyComponent {\n * onTouched: () => void = noop;\n * }\n * ```\n */\nexport const noop = (): void => undefined;\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\n/** A debounced function — same signature as the original plus `cancel`. */\nexport type WrDebouncedFn<TArgs extends readonly unknown[]> = ((...args: TArgs) => void) & {\n readonly cancel: () => void;\n};\n\n/**\n * Wrap `fn` so it only fires `waitMs` after the last invocation.\n * Useful for resize / scroll / input handlers.\n *\n * @example\n * ```ts\n * const onResize = debounce(() => recalcLayout(), 150);\n * window.addEventListener('resize', onResize);\n * ```\n */\nexport function debounce<TArgs extends readonly unknown[]>(\n fn: (...args: TArgs) => void,\n waitMs: number\n): WrDebouncedFn<TArgs> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const debounced = (...args: TArgs): void => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n fn(...args);\n }, waitMs);\n };\n\n debounced.cancel = (): void => {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n return debounced;\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\n/** A throttled function — same signature as the original plus `cancel`. */\nexport type WrThrottledFn<TArgs extends readonly unknown[]> = ((...args: TArgs) => void) & {\n readonly cancel: () => void;\n};\n\n/**\n * Wrap `fn` so it fires at most once every `waitMs`. Leading-edge call\n * fires immediately; trailing-edge call fires once at the end of the window\n * if there were further invocations.\n */\nexport function throttle<TArgs extends readonly unknown[]>(\n fn: (...args: TArgs) => void,\n waitMs: number\n): WrThrottledFn<TArgs> {\n let lastInvoke = 0;\n let trailingTimer: ReturnType<typeof setTimeout> | null = null;\n let pendingArgs: TArgs | null = null;\n\n const throttled = (...args: TArgs): void => {\n const now = Date.now();\n const remaining = waitMs - (now - lastInvoke);\n\n if (remaining <= 0) {\n if (trailingTimer !== null) {\n clearTimeout(trailingTimer);\n trailingTimer = null;\n }\n lastInvoke = now;\n fn(...args);\n } else {\n pendingArgs = args;\n trailingTimer ??= setTimeout(() => {\n lastInvoke = Date.now();\n trailingTimer = null;\n if (pendingArgs) fn(...pendingArgs);\n pendingArgs = null;\n }, remaining);\n }\n };\n\n throttled.cancel = (): void => {\n if (trailingTimer !== null) {\n clearTimeout(trailingTimer);\n trailingTimer = null;\n }\n pendingArgs = null;\n lastInvoke = 0;\n };\n\n return throttled;\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\n/**\n * Canonical `KeyboardEvent.key` values for your own `keydown` handlers.\n *\n * The spec spellings are easy to get wrong — arrows are `ArrowUp`, space is a\n * literal `' '` — and a typo like `'Esacpe'` never matches and never errors.\n * These give you autocomplete and the {@link WrKey} union to match against.\n */\nexport const KEYS = {\n ENTER: 'Enter',\n ESCAPE: 'Escape',\n TAB: 'Tab',\n SPACE: ' ',\n BACKSPACE: 'Backspace',\n DELETE: 'Delete',\n ARROW_UP: 'ArrowUp',\n ARROW_DOWN: 'ArrowDown',\n ARROW_LEFT: 'ArrowLeft',\n ARROW_RIGHT: 'ArrowRight',\n HOME: 'Home',\n END: 'End',\n PAGE_UP: 'PageUp',\n PAGE_DOWN: 'PageDown',\n} as const;\n\nexport type WrKey = (typeof KEYS)[keyof typeof KEYS];\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\n/** True when any modifier key (Ctrl / Cmd / Alt / Shift / Meta) is held. */\nexport function hasModifier(event: KeyboardEvent): boolean {\n return event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;\n}\n\n/**\n * True when the key is a single printable character (letters, digits,\n * punctuation, etc.). Skips function keys, arrows, modifiers, and named\n * keys like `Backspace` or `Tab`.\n */\nexport function isPrintableKey(event: KeyboardEvent): boolean {\n return event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;\n}\n\n/**\n * True while an input method is composing — the key belongs to the IME, not to\n * your handler. **Return early on it in every `keydown` handler that reads\n * Enter, Escape or an arrow on, or above, a field that accepts typed text.**\n *\n * A Japanese or Chinese user reaches every character through a conversion. From\n * `compositionstart` until `compositionend` a candidate window is open, and\n * those keys are how it is driven: Enter accepts the candidate, Escape cancels\n * the conversion back to kana, the arrows walk the candidate list. A component\n * that acts on them anyway takes the key away from the IME mid-word — the\n * overlay closes instead of the candidate window, and the half-composed reading\n * the user was still choosing between is gone with it.\n *\n * Both halves of the test are needed. `isComposing` is the standard flag, but\n * Safari fires `compositionend` *before* the `keydown` of the Enter that commits\n * a candidate, so that one arrives with `isComposing === false` and only the\n * legacy `keyCode` of `229` — the sentinel every engine sets on a key it handed\n * to the input method — to say where it came from.\n *\n * `keydown` is the whole scope. It does NOT answer \"is a composition in\n * progress\" for an `input` or `blur` handler: on the committing keystroke the\n * two disagree by design. A component that needs the state *between* events\n * tracks it with the `compositionstart` / `compositionend` pair instead.\n *\n * ```ts\n * protected onKeydown(event: KeyboardEvent): void {\n * if (isComposing(event)) return;\n * // …\n * }\n * ```\n */\nexport function isComposing(event: KeyboardEvent): boolean {\n // `keyCode` is deprecated and stays anyway: it is the only signal Safari\n // leaves on the committing keystroke. See the doc comment above.\n return event.isComposing || event.keyCode === 229;\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\n/**\n * Logs a coloured pill to the browser console followed by a payload.\n *\n * Useful when you need to scan a noisy console for a specific subsystem's\n * messages. The pill is rendered with the CSS `%c` console formatting trick.\n *\n * @example\n * ```ts\n * badgeLog('icons', '#3969e2', { name: 'plus', registered: true });\n * // → [icons] { name: 'plus', registered: true }\n * ```\n *\n * @param badgeText Short label shown inside the pill.\n * @param color CSS colour applied to the pill border and text.\n * @param message Anything you'd pass to `console.log` as the second argument.\n *\n * @internal\n */\nexport function badgeLog(badgeText: string, color: string, message: unknown): void {\n const style = `\n display: inline-block;\n border: 1px solid ${color};\n color: ${color};\n padding: 1px 3px;\n border-radius: 4px;\n margin-right: 0\n `;\n // eslint-disable-next-line no-console -- this helper exists to log\n console.log(`%c${badgeText}`, style, message);\n}\n","// Types\nexport type { Maybe, SafeAny } from './interfaces';\n\n// Guards\nexport { isDefined, isNonEmptyArray, isObservable } from './guards';\n\n// Math\nexport { clamp, round } from './math';\n\n// Coercion\nexport { numAttr } from './coercion';\n\n// CSS value\nexport { isSafeCssValue } from './css-value';\n\n// CSS size\nexport { resolveCssSize } from './css-size';\nexport type { ResolveCssSizeOptions, ResolvedCssSize } from './css-size';\n\n// DOM\nexport { getRootFontSize, getFocusableElements, trapFocus } from './dom';\n\n// ID\nexport { randomId } from './id';\n\n// Fn\nexport { noop, debounce, throttle } from './fn';\nexport type { WrDebouncedFn, WrThrottledFn } from './fn';\n\n// Keyboard\nexport { KEYS, hasModifier, isComposing, isPrintableKey } from './keyboard';\nexport type { WrKey } from './keyboard';\n\n// Log\nexport { badgeLog } from './log';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["MAX_LENGTH"],"mappings":";;AAAA;;;;;AAKG;AAIH;;;;;;;;;;;;;;AAcG;AACG,SAAU,SAAS,CAAI,KAAe,EAAA;AAC1C,IAAA,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;AAC9C;;AC1BA;;;;;AAKG;AAIH;;;;;;;;;;;;AAYG;AACG,SAAU,eAAe,CAAI,KAAiB,EAAA;AAClD,IAAA,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;AACjD;;ACxBA;;;;;AAKG;AAIH;;;;;;;;;;;;AAYG;AACG,SAAU,YAAY,CAAc,KAAc,EAAA;IACtD,OAAO,CAAC,CAAC,KAAK,IAAI,OAAQ,KAAiC,CAAC,SAAS,KAAK,UAAU;AACtF;;ACxBA;;;;;;;;;AASG;SACa,KAAK,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW,EAAA;AAC3D,IAAA,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAC5C;;ACZA;;;;;;;;;;AAUG;SACa,KAAK,CAAC,KAAa,EAAE,QAAQ,GAAG,CAAC,EAAA;AAC/C,IAAA,MAAM,MAAM,GAAG,EAAE,IAAI,QAAQ;AAC7B,IAAA,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,MAAM;AAC/D;;ACZA;;;;;;;;;AASG;MACU,OAAO,GAClB,CAAC,QAAgB,KACjB,CAAC,KAAc,KACb,oBAAoB,CAAC,KAAK,EAAE,QAAQ;;ACfxC;;;;;AAKG;AAEH;AACA,MAAMA,YAAU,GAAG,GAAG;AAEtB;;;AAGG;AACH,MAAM,SAAS,GAAG,yBAAyB;AAE3C;AACA,MAAM,aAAa,GAAG,aAAa;AAEnC;;;;AAIG;AACH,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IAChC,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,KAAK;IACL,OAAO;IACP,OAAO;IACP,OAAO;IACP,WAAW;IACX,YAAY;IACZ,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,OAAO;AACR,CAAA,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCG;AACG,SAAU,cAAc,CAAC,KAAa,EAAA;IAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ;AAAE,QAAA,OAAO,KAAK;AAE3C,IAAA,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE;IAC5B,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,CAAC,MAAM,GAAGA,YAAU;AAAE,QAAA,OAAO,KAAK;AAC/D,IAAA,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;AAAE,QAAA,OAAO,KAAK;IAEzC,IAAI,KAAK,GAAG,CAAC;AACb,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACvC,QAAA,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC;AAEvB,QAAA,IAAI,IAAI,KAAK,GAAG,EAAE;AAChB,YAAA,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;AACxD,YAAA,IAAI,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AAAE,gBAAA,OAAO,KAAK;AACxE,YAAA,KAAK,EAAE;QACT;AAAO,aAAA,IAAI,IAAI,KAAK,GAAG,EAAE;AACvB,YAAA,KAAK,EAAE;YACP,IAAI,KAAK,GAAG,CAAC;AAAE,gBAAA,OAAO,KAAK;QAC7B;aAAO,IAAI,KAAK,KAAK,CAAC,IAAI,IAAI,KAAK,GAAG,EAAE;AACtC,YAAA,OAAO,KAAK;QACd;IACF;IAEA,OAAO,KAAK,KAAK,CAAC;AACpB;;AC1GA;;;;;AAKG;AAEH;;;;;;;;;;;;;;;;;;AAkBG;AACG,SAAU,eAAe,CAAC,QAAQ,GAAG,EAAE,EAAA;AAC3C,IAAA,IAAI;QACF,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE;AACpE,YAAA,OAAO,QAAQ;QACjB;AAEA,QAAA,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,QAAQ;QACtE,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;AAErC,QAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,QAAQ;IACpD;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,QAAQ;IACjB;AACF;;ACvCA;;;;;AAKG;AAEH,MAAM,kBAAkB,GAAG;IACzB,SAAS;IACT,YAAY;IACZ,wBAAwB;IACxB,4CAA4C;IAC5C,wBAAwB;IACxB,0BAA0B;IAC1B,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,iBAAiB;IACjB,iBAAiB;IACjB,kDAAkD;IAClD,iCAAiC;AAClC,CAAA,CAAC,IAAI,CAAC,GAAG,CAAC;AAEX;AACM,SAAU,oBAAoB,CAAC,IAAiB,EAAA;AACpD,IAAA,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAc,kBAAkB,CAAC,CAAC,CAAC,MAAM,CAC9E,EAAE,IAAI,EAAE,CAAC,YAAY,KAAK,IAAI,IAAI,EAAE,KAAK,QAAQ,CAAC,aAAa,CAChE;AACH;AAEA;;;;AAIG;AACG,SAAU,SAAS,CAAC,IAAiB,EAAE,KAAoB,EAAA;AAC/D,IAAA,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK;AAAE,QAAA,OAAO,KAAK;AACrC,IAAA,MAAM,SAAS,GAAG,oBAAoB,CAAC,IAAI,CAAC;AAC5C,IAAA,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,KAAK;AAExC,IAAA,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC;IAC1B,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;AAC5C,IAAA,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAmC;IAE3D,IAAI,KAAK,CAAC,QAAQ,IAAI,MAAM,KAAK,KAAK,EAAE;QACtC,KAAK,CAAC,cAAc,EAAE;QACtB,IAAI,CAAC,KAAK,EAAE;AACZ,QAAA,OAAO,IAAI;IACb;IACA,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE;QACtC,KAAK,CAAC,cAAc,EAAE;QACtB,KAAK,CAAC,KAAK,EAAE;AACb,QAAA,OAAO,IAAI;IACb;AACA,IAAA,OAAO,KAAK;AACd;;ACvDA;;;;;AAKG;AAOH;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;SACa,cAAc,CAAC,GAAY,EAAE,UAAiC,EAAE,EAAA;AAC9E,IAAA,MAAM,YAAY,GAAG,eAAe,EAAE;AACtC,IAAA,MAAM,KAAK,GAAY,GAAG,IAAI,OAAO,CAAC,YAAY;AAElD,IAAA,IAAI,KAAK,IAAI,IAAI,EAAE;QACjB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE;IACxC;AAEA,IAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;QACvD,OAAO,EAAE,QAAQ,EAAE,CAAA,EAAG,KAAK,CAAA,EAAA,CAAI,EAAE,OAAO,EAAE,KAAK,EAAE;IACnD;AAEA,IAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;QAC7B,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE;IACxC;AAEA,IAAA,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE;IAE5B,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC;IAClD,IAAI,QAAQ,EAAE;QACZ,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC1C,QAAA,MAAM,EAAE,GAAG,GAAG,GAAG,YAAY;QAC7B,OAAO,EAAE,QAAQ,EAAE,CAAA,EAAG,GAAG,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE;IAC5E;IAEA,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC;IAChD,IAAI,OAAO,EAAE;QACX,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACxC,OAAO,EAAE,QAAQ,EAAE,CAAA,EAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE;IAC1E;IAEA,MAAM,YAAY,GAAG,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC;IACnD,IAAI,YAAY,EAAE;AAChB,QAAA,OAAO,EAAE,QAAQ,EAAE,CAAA,EAAG,YAAY,CAAC,CAAC,CAAC,CAAA,CAAA,CAAG,EAAE,OAAO,EAAE,IAAI,EAAE;IAC3D;IAEA,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC;IAC9C,IAAI,WAAW,EAAE;QACf,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QAC5C,OAAO,EAAE,QAAQ,EAAE,CAAA,EAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE;IAC1E;IAEA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;AAC7C;;AChFA;;;;;AAKG;AAEH,MAAM,QAAQ,GAAG,sCAAsC;AACvD,MAAM,UAAU,GAAG,CAAC;AACpB,MAAM,UAAU,GAAG,EAAE;AACrB,MAAM,cAAc,GAAG,EAAE;AAEzB;;;;AAIG;AACH,SAAS,kBAAkB,CAAC,KAAa,EAAA;;AAEvC,IAAA,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,eAAe,KAAK,UAAU,EAAE;QACjF,OAAO,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IACtD;;AAGA,IAAA,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC;AACjC,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE;AAC9B,QAAA,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC;IAC1C;AACA,IAAA,OAAO,GAAG;AACZ;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BG;AACG,SAAU,QAAQ,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,MAA2C,EAAA;IAC1G,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,UAAU,CAAC;AAEjF,IAAA,MAAM,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,kBAAkB,CAAC,UAAU,CAAC;IAE1E,IAAI,MAAM,GAAG,EAAE;AACf,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE;AACnC,QAAA,MAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;IAChD;AAEA,IAAA,OAAO,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,MAAM,EAAE;AAC9B;;ACvEA;;;;;AAKG;AAEH;;;;;;;;;;;;;AAaG;MACU,IAAI,GAAG,MAAY;;ACrBhC;;;;;AAKG;AAOH;;;;;;;;;AASG;AACG,SAAU,QAAQ,CACtB,EAA4B,EAC5B,MAAc,EAAA;IAEd,IAAI,KAAK,GAAyC,IAAI;AAEtD,IAAA,MAAM,SAAS,GAAG,CAAC,GAAG,IAAW,KAAU;QACzC,IAAI,KAAK,KAAK,IAAI;YAAE,YAAY,CAAC,KAAK,CAAC;AACvC,QAAA,KAAK,GAAG,UAAU,CAAC,MAAK;YACtB,KAAK,GAAG,IAAI;AACZ,YAAA,EAAE,CAAC,GAAG,IAAI,CAAC;QACb,CAAC,EAAE,MAAM,CAAC;AACZ,IAAA,CAAC;AAED,IAAA,SAAS,CAAC,MAAM,GAAG,MAAW;AAC5B,QAAA,IAAI,KAAK,KAAK,IAAI,EAAE;YAClB,YAAY,CAAC,KAAK,CAAC;YACnB,KAAK,GAAG,IAAI;QACd;AACF,IAAA,CAAC;AAED,IAAA,OAAO,SAAS;AAClB;;AC5CA;;;;;AAKG;AAOH;;;;AAIG;AACG,SAAU,QAAQ,CACtB,EAA4B,EAC5B,MAAc,EAAA;IAEd,IAAI,UAAU,GAAG,CAAC;IAClB,IAAI,aAAa,GAAyC,IAAI;IAC9D,IAAI,WAAW,GAAiB,IAAI;AAEpC,IAAA,MAAM,SAAS,GAAG,CAAC,GAAG,IAAW,KAAU;AACzC,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACtB,MAAM,SAAS,GAAG,MAAM,IAAI,GAAG,GAAG,UAAU,CAAC;AAE7C,QAAA,IAAI,SAAS,IAAI,CAAC,EAAE;AAClB,YAAA,IAAI,aAAa,KAAK,IAAI,EAAE;gBAC1B,YAAY,CAAC,aAAa,CAAC;gBAC3B,aAAa,GAAG,IAAI;YACtB;YACA,UAAU,GAAG,GAAG;AAChB,YAAA,EAAE,CAAC,GAAG,IAAI,CAAC;QACb;aAAO;YACL,WAAW,GAAG,IAAI;AAClB,YAAA,aAAa,KAAK,UAAU,CAAC,MAAK;AAChC,gBAAA,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE;gBACvB,aAAa,GAAG,IAAI;AACpB,gBAAA,IAAI,WAAW;AAAE,oBAAA,EAAE,CAAC,GAAG,WAAW,CAAC;gBACnC,WAAW,GAAG,IAAI;YACpB,CAAC,EAAE,SAAS,CAAC;QACf;AACF,IAAA,CAAC;AAED,IAAA,SAAS,CAAC,MAAM,GAAG,MAAW;AAC5B,QAAA,IAAI,aAAa,KAAK,IAAI,EAAE;YAC1B,YAAY,CAAC,aAAa,CAAC;YAC3B,aAAa,GAAG,IAAI;QACtB;QACA,WAAW,GAAG,IAAI;QAClB,UAAU,GAAG,CAAC;AAChB,IAAA,CAAC;AAED,IAAA,OAAO,SAAS;AAClB;;ACzDA;;;;;AAKG;AAEH;;;;;;AAMG;AACI,MAAM,IAAI,GAAG;AAClB,IAAA,KAAK,EAAE,OAAO;AACd,IAAA,MAAM,EAAE,QAAQ;AAChB,IAAA,GAAG,EAAE,KAAK;AACV,IAAA,KAAK,EAAE,GAAG;AACV,IAAA,SAAS,EAAE,WAAW;AACtB,IAAA,MAAM,EAAE,QAAQ;AAChB,IAAA,QAAQ,EAAE,SAAS;AACnB,IAAA,UAAU,EAAE,WAAW;AACvB,IAAA,UAAU,EAAE,WAAW;AACvB,IAAA,WAAW,EAAE,YAAY;AACzB,IAAA,IAAI,EAAE,MAAM;AACZ,IAAA,GAAG,EAAE,KAAK;AACV,IAAA,OAAO,EAAE,QAAQ;AACjB,IAAA,SAAS,EAAE,UAAU;;;AC5BvB;;;;;AAKG;AAEH;AACM,SAAU,WAAW,CAAC,KAAoB,EAAA;AAC9C,IAAA,OAAO,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ;AACzE;AAEA;;;;AAIG;AACG,SAAU,cAAc,CAAC,KAAoB,EAAA;IACjD,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM;AACpF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BG;AACG,SAAU,WAAW,CAAC,KAAoB,EAAA;;;IAG9C,OAAO,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,OAAO,KAAK,GAAG;AACnD;;ACxDA;;;;;AAKG;AAEH;;;;;;;;;;;;;;;;;AAiBG;SACa,QAAQ,CAAC,SAAiB,EAAE,KAAa,EAAE,OAAgB,EAAA;AACzE,IAAA,MAAM,KAAK,GAAG;;wBAEQ,KAAK,CAAA;aAChB,KAAK,CAAA;;;;GAIf;;IAED,OAAO,CAAC,GAAG,CAAC,CAAA,EAAA,EAAK,SAAS,CAAA,CAAE,EAAE,KAAK,EAAE,OAAO,CAAC;AAC/C;;ACjCA;;ACHA;;AAEG;;;;"}
@@ -16,7 +16,7 @@ import { InjectionToken } from '@angular/core';
16
16
  * Distinct from `Version` exported by `@angular/core`, which reports the
17
17
  * loaded Angular runtime version (e.g. `'22.0.0'`), not ngwr's.
18
18
  */
19
- const NGWR_VERSION = '13.0.0';
19
+ const NGWR_VERSION = '14.0.0';
20
20
 
21
21
  /**
22
22
  * @license
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-version.mjs","sources":["../../../projects/lib/version/version.ts","../../../projects/lib/version/version.token.ts","../../../projects/lib/version/ngwr-version.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\n/**\n * The ngwr library version string, kept in sync with\n * `projects/lib/package.json` by `scripts/release-prepare.ts`.\n *\n * Prefer this over reading `package.json` at runtime — that requires a JSON\n * import or `require`, neither of which tree-shakes well in Angular builds.\n *\n * Distinct from `Version` exported by `@angular/core`, which reports the\n * loaded Angular runtime version (e.g. `'22.0.0'`), not ngwr's.\n */\nexport const NGWR_VERSION = '13.0.0';\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\nimport { NGWR_VERSION } from './version';\n\n/**\n * Injection token holding the ngwr library version string.\n *\n * Default factory returns {@link NGWR_VERSION}. Override it in tests or in\n * apps that want to surface a custom build identifier.\n *\n * @example\n * ```ts\n * @Component({...})\n * export class FooterComponent {\n * readonly version = inject(NGWR_VERSION_TOKEN);\n * }\n * ```\n */\nexport const NGWR_VERSION_TOKEN = new InjectionToken<string>('NGWR_VERSION_TOKEN', {\n providedIn: 'root',\n factory: () => NGWR_VERSION,\n});\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;AAAA;;;;;AAKG;AAEH;;;;;;;;;AASG;AACI,MAAM,YAAY,GAAG;;ACjB5B;;;;;AAKG;AAMH;;;;;;;;;;;;;AAaG;MACU,kBAAkB,GAAG,IAAI,cAAc,CAAS,oBAAoB,EAAE;AACjF,IAAA,UAAU,EAAE,MAAM;AAClB,IAAA,OAAO,EAAE,MAAM,YAAY;AAC5B,CAAA;;AC5BD;;AAEG;;;;"}
1
+ {"version":3,"file":"ngwr-version.mjs","sources":["../../../projects/lib/version/version.ts","../../../projects/lib/version/version.token.ts","../../../projects/lib/version/ngwr-version.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\n/**\n * The ngwr library version string, kept in sync with\n * `projects/lib/package.json` by `scripts/release-prepare.ts`.\n *\n * Prefer this over reading `package.json` at runtime — that requires a JSON\n * import or `require`, neither of which tree-shakes well in Angular builds.\n *\n * Distinct from `Version` exported by `@angular/core`, which reports the\n * loaded Angular runtime version (e.g. `'22.0.0'`), not ngwr's.\n */\nexport const NGWR_VERSION = '14.0.0';\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\nimport { NGWR_VERSION } from './version';\n\n/**\n * Injection token holding the ngwr library version string.\n *\n * Default factory returns {@link NGWR_VERSION}. Override it in tests or in\n * apps that want to surface a custom build identifier.\n *\n * @example\n * ```ts\n * @Component({...})\n * export class FooterComponent {\n * readonly version = inject(NGWR_VERSION_TOKEN);\n * }\n * ```\n */\nexport const NGWR_VERSION_TOKEN = new InjectionToken<string>('NGWR_VERSION_TOKEN', {\n providedIn: 'root',\n factory: () => NGWR_VERSION,\n});\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;AAAA;;;;;AAKG;AAEH;;;;;;;;;AASG;AACI,MAAM,YAAY,GAAG;;ACjB5B;;;;;AAKG;AAMH;;;;;;;;;;;;;AAaG;MACU,kBAAkB,GAAG,IAAI,cAAc,CAAS,oBAAoB,EAAE;AACjF,IAAA,UAAU,EAAE,MAAM;AAClB,IAAA,OAAO,EAAE,MAAM,YAAY;AAC5B,CAAA;;AC5BD;;AAEG;;;;"}
@@ -95,9 +95,17 @@ class WrWindowHarness extends ContentContainerComponentHarness {
95
95
  }
96
96
  throw new Error('WrWindowHarness.getOs(): this window carries no `wr-window--os-<name>` class.');
97
97
  }
98
- /** The title-bar density — `normal` or `compact`. */
98
+ /**
99
+ * The title-bar density — `sm` or `md`.
100
+ *
101
+ * Typed as the union rather than `string`, so a value added to
102
+ * `WrWindowChromeSize` fails this method's compile instead of quietly
103
+ * answering `md` for it. Only `sm` carries a modifier class: `md` is the
104
+ * default and paints from the base block, so its answer is the absence of
105
+ * the other one.
106
+ */
99
107
  async getChromeSize() {
100
- return (await (await this.host()).hasClass('wr-window--chrome-compact')) ? 'compact' : 'normal';
108
+ return (await (await this.host()).hasClass('wr-window--chrome-sm')) ? 'sm' : 'md';
101
109
  }
102
110
  /**
103
111
  * Where the window is and how big, in CSS pixels.
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-window-testing.mjs","sources":["../../../projects/lib/window/testing/wr-window-harness.ts","../../../projects/lib/window/testing/wr-window-taskbar-harness.ts","../../../projects/lib/window/testing/ngwr-window-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 { ContentContainerComponentHarness, HarnessPredicate, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrWindowState } from 'ngwr/window';\n\nimport type { WrWindowBox, WrWindowHarnessFilters } from './interfaces';\n\nconst STATES: readonly WrWindowState[] = ['normal', 'minimized', 'maximized'];\n\n/**\n * Test harness for a window opened through `WrWindowManager.open()`.\n *\n * `WrWindow` is deliberately not exported — a window is always a service call — so\n * there is nothing in your fixture to load: each window is a portal in its own\n * overlay pane, and this comes from\n * `TestbedHarnessEnvironment.documentRootLoader()`.\n *\n * It is a CONTENT CONTAINER, like `WrDialogHarness`: the component you opened is\n * projected into the window's body, so `window.getHarness(WrButtonHarness…)` reads\n * ITS buttons and cannot reach another window's. That matters more here than\n * anywhere else, because windows are explicitly non-modal and several are normally\n * open at once.\n *\n * **Geometry is read from the inline styles the component writes**, never measured:\n * a window is positioned with `left` / `top` / `width` / `height` in pixels, and a\n * unit test has no layout to measure. That also makes {@link getBox} the way to\n * assert a move or a resize — including the ones a drag would produce, which no\n * synthetic pointer event can, since the drag divides by a rect that is 0×0 here.\n *\n * @example\n * ```ts\n * const rootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture);\n * windows.open(EditorComponent, { title: 'Untitled.md' });\n * await fixture.whenStable();\n *\n * const win = await rootLoader.getHarness(WrWindowHarness.with({ title: 'Untitled.md' }));\n * await win.maximize();\n * expect(await win.getState()).toBe('maximized');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrWindowHarness extends ContentContainerComponentHarness {\n static hostSelector = 'wr-window';\n\n /** Build a predicate that narrows the query — windows come in stacks. */\n static with(options: WrWindowHarnessFilters = {}): HarnessPredicate<WrWindowHarness> {\n return new HarnessPredicate(WrWindowHarness, options)\n .addOption('title', options.title, (harness, title) => HarnessPredicate.stringMatches(harness.getTitle(), title))\n .addOption('state', options.state, async (harness, state) => (await harness.getState()) === state);\n }\n\n private readonly titleEl = this.locatorFor('.wr-window__title');\n\n /**\n * Whether this window is still on the page.\n *\n * For a harness you are already HOLDING: closing disposes the overlay, so this\n * flips to `false` where a fresh `getHarnessOrNull()` comes back `null`. A\n * MINIMIZED window is still open — it is a state, not a dismissal — and this\n * stays `true` for one.\n */\n async isOpen(): Promise<boolean> {\n return (await this.host()).matchesSelector('body wr-window');\n }\n\n /** The title-bar text. */\n async getTitle(): Promise<string> {\n return (await this.titleEl()).text();\n }\n\n /** The projected `[wrWindowTitleExtra]` content, trimmed — empty when there is none. */\n async getTitleExtraText(): Promise<string> {\n return (await this.locatorFor('.wr-window__title-extra')()).text();\n }\n\n /** The window's visual state, from the host modifier. */\n async getState(): Promise<WrWindowState> {\n const host = await this.host();\n for (const state of STATES) {\n if (await host.hasClass(`wr-window--${state}`)) return state;\n }\n throw new Error(\n 'WrWindowHarness.getState(): this host carries no `wr-window--<state>` class, which every window sets — ' +\n 'the element matched is probably not an ngwr window.'\n );\n }\n\n /**\n * The OS the chrome is dressed as — `macos`, `windows` or `linux`.\n *\n * Worth asserting because it is not cosmetic: on macOS the buttons are a\n * traffic-light cluster in close → minimize → maximize order, and the Linux\n * chrome renders neither minimize nor maximize UNLESS `showMinimize` /\n * `showMaximize` asks for them. \"No minimize on Linux\" is a default, not an\n * invariant — see {@link hasMinimizeButton}.\n */\n async getOs(): Promise<string> {\n const host = await this.host();\n for (const os of ['macos', 'windows', 'linux']) {\n if (await host.hasClass(`wr-window--os-${os}`)) return os;\n }\n throw new Error('WrWindowHarness.getOs(): this window carries no `wr-window--os-<name>` class.');\n }\n\n /** The title-bar density — `normal` or `compact`. */\n async getChromeSize(): Promise<string> {\n return (await (await this.host()).hasClass('wr-window--chrome-compact')) ? 'compact' : 'normal';\n }\n\n /**\n * Where the window is and how big, in CSS pixels.\n *\n * From the inline styles the component writes, which is the only readable answer\n * without layout — and the same numbers a drag or a snap would change.\n */\n async getBox(): Promise<WrWindowBox> {\n const host = await this.host();\n return {\n x: Number.parseFloat(await host.getCssValue('left')),\n y: Number.parseFloat(await host.getCssValue('top')),\n width: Number.parseFloat(await host.getCssValue('width')),\n height: Number.parseFloat(await host.getCssValue('height')),\n };\n }\n\n /**\n * The window's stacking order.\n *\n * The manager hands out a z-index per focus, so this is how a spec asserts that\n * clicking a window brought it to the front — the one thing a window manager\n * exists to do, and the reason these panes are deliberately kept OUT of the\n * browser's top layer, which orders strictly by entry.\n */\n async getZIndex(): Promise<number> {\n return Number.parseFloat(await (await this.host()).getCssValue('z-index'));\n }\n\n /** Whether the resize handles are rendered — `resizable`, and only in the normal state. */\n async isResizable(): Promise<boolean> {\n return (await this.host()).hasClass('wr-window--resizable');\n }\n\n /** The resize handles currently rendered, by their edge / corner suffix. */\n async getResizeHandles(): Promise<string[]> {\n const handles = await this.locatorForAll('.wr-window__handle')();\n return Promise.all(\n handles.map(async handle => {\n const classes = (await handle.getAttribute('class')) ?? '';\n return (\n classes\n .split(/\\s+/)\n .find(name => name.startsWith('wr-window__handle--'))\n ?.slice(19) ?? ''\n );\n })\n );\n }\n\n /** The body text of the window — the component you opened, as rendered. */\n async getBodyText(): Promise<string> {\n return (await this.locatorFor('.wr-window__body')()).text();\n }\n\n /** The projected `[wrWindowStatusBar]` content, trimmed. */\n async getStatusBarText(): Promise<string> {\n return (await this.locatorFor('.wr-window__status-bar')()).text();\n }\n\n /** Whether the chrome offers a close button (`showClose`). */\n async hasCloseButton(): Promise<boolean> {\n return (await this.chromeAction('close')) !== null;\n }\n\n /**\n * Whether the chrome offers a minimize button.\n *\n * `false` on Linux BY DEFAULT — that chrome is close-only by convention, which\n * is a real behavioural difference and not a style. `showMinimize: true`\n * overrides it, and a spec that asserts \"no minimize on Linux\" without saying\n * which of the two it means will pass for the wrong reason.\n */\n async hasMinimizeButton(): Promise<boolean> {\n return (await this.chromeAction('minimize')) !== null;\n }\n\n /** Whether the chrome offers a maximize button. Also `false` on Linux by default. */\n async hasMaximizeButton(): Promise<boolean> {\n return (await this.chromeAction('maximize')) !== null;\n }\n\n /** The close button's accessible name — the buttons are icon-only, so it is their only name. */\n async getCloseLabel(): Promise<string | null> {\n return (await this.requireAction('close', 'getCloseLabel')).getAttribute('aria-label');\n }\n\n /**\n * The minimize button's accessible name, which flips to the restore wording while\n * the window is minimized — the same button does both.\n */\n async getMinimizeLabel(): Promise<string | null> {\n return (await this.requireAction('minimize', 'getMinimizeLabel')).getAttribute('aria-label');\n }\n\n /** The maximize button's accessible name, which flips while the window is maximized. */\n async getMaximizeLabel(): Promise<string | null> {\n return (await this.requireAction('maximize', 'getMaximizeLabel')).getAttribute('aria-label');\n }\n\n /** Click the close button. Throws on a window opened `showClose: false`. */\n async close(): Promise<void> {\n await (await this.requireAction('close', 'close')).click();\n }\n\n /**\n * Click the minimize button — which RESTORES a minimized window, since it is the\n * same control.\n */\n async minimize(): Promise<void> {\n await (await this.requireAction('minimize', 'minimize')).click();\n }\n\n /** Click the maximize button — which restores a maximized window down. */\n async maximize(): Promise<void> {\n await (await this.requireAction('maximize', 'maximize')).click();\n }\n\n /**\n * Double-click the title bar, the other way to maximize.\n *\n * A separate gesture worth its own assertion, and it does NOT follow the\n * button: only an explicit `showMaximize: false` turns it off. The Linux\n * chrome hides the button by convention and keeps the gesture — which is also\n * the only way out of a window that reached `maximized` without one.\n */\n async doubleClickChrome(): Promise<void> {\n await (await this.locatorFor('.wr-window__chrome')()).dispatchEvent('dblclick');\n }\n\n /** Click the window body — which pulls it to the front of the stack. */\n async focusWindow(): Promise<void> {\n await (await this.host()).click();\n }\n\n /** Press Escape. A window opened `closeOnEscape: false` ignores it — assert, do not assume. */\n async sendEscape(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.ESCAPE);\n }\n\n /** The role the window announces — `dialog`, on a non-modal window. */\n async getRole(): Promise<string | null> {\n return (await this.host()).getAttribute('role');\n }\n\n /**\n * Whether the window takes its accessible name from its own title element.\n *\n * Resolved rather than trusted: `aria-labelledby` naming an element that is not\n * this window's title is not a name, and with several windows open the ids are\n * the only thing keeping them apart.\n */\n async isLabelledByTitle(): Promise<boolean> {\n const labelledBy = await (await this.host()).getAttribute('aria-labelledby');\n if (!labelledBy) return false;\n return (await (await this.titleEl()).getAttribute('id')) === labelledBy;\n }\n\n /** Whether the window is hidden from assistive tech (`aria-hidden`, set while closed). */\n async isHiddenFromAssistiveTech(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-hidden')) === 'true';\n }\n\n private async chromeAction(name: 'close' | 'minimize' | 'maximize'): Promise<TestElement | null> {\n return this.locatorForOptional(`.wr-window__chrome-action--${name}`)();\n }\n\n private async requireAction(name: 'close' | 'minimize' | 'maximize', method: string): Promise<TestElement> {\n const button = await this.chromeAction(name);\n if (!button) {\n throw new Error(\n `WrWindowHarness.${method}(): this window has no ${name} button. It was opened with \\`show` +\n `${name[0].toUpperCase()}${name.slice(1)}: false\\`, or its chrome is the Linux one, which is ` +\n `close-only unless \\`show${name[0].toUpperCase()}${name.slice(1)}: true\\` asks for it.`\n );\n }\n return button;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrWindowTaskbarHarnessFilters } from './interfaces';\n\n/**\n * Test harness for `<wr-window-taskbar>` — the rail of minimized windows.\n *\n * The taskbar IS an element in your template, so it comes from the normal fixture\n * loader, unlike the windows it lists. It renders nothing but its own host until a\n * window is minimized; {@link isEmpty} is the question, and {@link getTabTitles}\n * answers `[]` rather than throwing, because \"no minimized windows\" is the ordinary\n * state rather than a mistake.\n *\n * @example\n * ```ts\n * const taskbar = await loader.getHarness(WrWindowTaskbarHarness);\n *\n * await (await rootLoader.getHarness(WrWindowHarness)).minimize();\n * expect(await taskbar.getTabTitles()).toEqual(['Untitled.md']);\n * await taskbar.restore('Untitled.md');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrWindowTaskbarHarness extends ComponentHarness {\n static hostSelector = 'wr-window-taskbar';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrWindowTaskbarHarnessFilters = {}): HarnessPredicate<WrWindowTaskbarHarness> {\n return new HarnessPredicate(WrWindowTaskbarHarness, options).addOption(\n 'position',\n options.position,\n async (harness, position) => (await harness.getPosition()) === position\n );\n }\n\n /** The edge the rail is pinned to. */\n async getPosition(): Promise<'top' | 'bottom'> {\n return (await (await this.host()).hasClass('wr-window-taskbar--top')) ? 'top' : 'bottom';\n }\n\n /**\n * The rail's accessible name — it is a `role=\"toolbar\"`, and an unnamed one is\n * announced as nothing.\n */\n async getRailLabel(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /** Whether nothing is minimized — the rail draws no markup at all then. */\n async isEmpty(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-window-taskbar__rail')()) === null;\n }\n\n /**\n * The titles in the rail, in order.\n *\n * A window opened without a title still gets a tab, named by the catalog's\n * fallback rather than left blank — a nameless tab is unreachable by voice and\n * indistinguishable from its neighbours.\n */\n async getTabTitles(): Promise<string[]> {\n const titles = await this.locatorForAll('.wr-window-taskbar__tab-title')();\n return Promise.all(titles.map(title => title.text()));\n }\n\n /** A tab's accessible name — the restore wording with the window's title in it. */\n async getRestoreLabel(title: string): Promise<string | null> {\n return (await this.requireTab(title, 'getRestoreLabel')).getAttribute('aria-label');\n }\n\n /** Click a tab — restoring that window and focusing it. */\n async restore(title: string): Promise<void> {\n await (await this.requireTab(title, 'restore')).click();\n }\n\n /**\n * Click a tab's ✕ — closing the window WITHOUT restoring it.\n *\n * Still worth asserting separately now that the two are siblings rather than\n * nested: they sit inside one pill and look like one control, so a close that\n * also restored would flash a closing window's content and look plausible.\n */\n async closeTab(title: string): Promise<void> {\n const index = await this.indexOf(title, 'closeTab');\n const closers = await this.locatorForAll('.wr-window-taskbar__tab-close')();\n await closers[index].click();\n }\n\n /**\n * The tab's BUTTON, not the pill around it.\n *\n * `.wr-window-taskbar__tab` is a plain container — it carries the pill's look\n * and nothing else, because the close beside it cannot be a control nested in\n * a control. The name and the click both belong to `__tab-restore`.\n */\n private async requireTab(title: string, method: string): Promise<TestElement> {\n const tabs = await this.locatorForAll('.wr-window-taskbar__tab-restore')();\n return tabs[await this.indexOf(title, method)];\n }\n\n /** Which tab carries this title — one lookup for both the tab and its close button. */\n private async indexOf(title: string, method: string): Promise<number> {\n const tabs = await this.locatorForAll('.wr-window-taskbar__tab-restore')();\n for (const [index, tab] of tabs.entries()) {\n if ((await tab.text()) === title) return index;\n }\n\n const offered = await this.getTabTitles();\n throw new Error(\n `WrWindowTaskbarHarness.${method}(\"${title}\"): no such tab. The rail holds: ` +\n `${offered.length ? offered.join(', ') : '(nothing — no window is minimized)'}.`\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;AAAA;;;;;AAKG;AAQH,MAAM,MAAM,GAA6B,CAAC,QAAQ,EAAE,WAAW,EAAE,WAAW,CAAC;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCG;AACG,MAAO,eAAgB,SAAQ,gCAAgC,CAAA;AACnE,IAAA,OAAO,YAAY,GAAG,WAAW;;AAGjC,IAAA,OAAO,IAAI,CAAC,OAAA,GAAkC,EAAE,EAAA;AAC9C,QAAA,OAAO,IAAI,gBAAgB,CAAC,eAAe,EAAE,OAAO;aACjD,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,KAAK,gBAAgB,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC;aAC/G,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,OAAO,EAAE,KAAK,KAAK,CAAC,MAAM,OAAO,CAAC,QAAQ,EAAE,MAAM,KAAK,CAAC;IACtG;AAEiB,IAAA,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC;AAE/D;;;;;;;AAOG;AACH,IAAA,MAAM,MAAM,GAAA;AACV,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,eAAe,CAAC,gBAAgB,CAAC;IAC9D;;AAGA,IAAA,MAAM,QAAQ,GAAA;QACZ,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE;IACtC;;AAGA,IAAA,MAAM,iBAAiB,GAAA;AACrB,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,yBAAyB,CAAC,EAAE,EAAE,IAAI,EAAE;IACpE;;AAGA,IAAA,MAAM,QAAQ,GAAA;AACZ,QAAA,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE;AAC9B,QAAA,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;YAC1B,IAAI,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAA,WAAA,EAAc,KAAK,EAAE,CAAC;AAAE,gBAAA,OAAO,KAAK;QAC9D;QACA,MAAM,IAAI,KAAK,CACb,yGAAyG;AACvG,YAAA,qDAAqD,CACxD;IACH;AAEA;;;;;;;;AAQG;AACH,IAAA,MAAM,KAAK,GAAA;AACT,QAAA,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE;QAC9B,KAAK,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE;YAC9C,IAAI,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAA,cAAA,EAAiB,EAAE,EAAE,CAAC;AAAE,gBAAA,OAAO,EAAE;QAC3D;AACA,QAAA,MAAM,IAAI,KAAK,CAAC,+EAA+E,CAAC;IAClG;;AAGA,IAAA,MAAM,aAAa,GAAA;QACjB,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,2BAA2B,CAAC,IAAI,SAAS,GAAG,QAAQ;IACjG;AAEA;;;;;AAKG;AACH,IAAA,MAAM,MAAM,GAAA;AACV,QAAA,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE;QAC9B,OAAO;AACL,YAAA,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;AACpD,YAAA,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AACnD,YAAA,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;AACzD,YAAA,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;SAC5D;IACH;AAEA;;;;;;;AAOG;AACH,IAAA,MAAM,SAAS,GAAA;AACb,QAAA,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IAC5E;;AAGA,IAAA,MAAM,WAAW,GAAA;AACf,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,sBAAsB,CAAC;IAC7D;;AAGA,IAAA,MAAM,gBAAgB,GAAA;QACpB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,oBAAoB,CAAC,EAAE;AAChE,QAAA,OAAO,OAAO,CAAC,GAAG,CAChB,OAAO,CAAC,GAAG,CAAC,OAAM,MAAM,KAAG;AACzB,YAAA,MAAM,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE;AAC1D,YAAA,QACE;iBACG,KAAK,CAAC,KAAK;iBACX,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,qBAAqB,CAAC;AACpD,kBAAE,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE;QAEvB,CAAC,CAAC,CACH;IACH;;AAGA,IAAA,MAAM,WAAW,GAAA;AACf,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,EAAE,IAAI,EAAE;IAC7D;;AAGA,IAAA,MAAM,gBAAgB,GAAA;AACpB,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,wBAAwB,CAAC,EAAE,EAAE,IAAI,EAAE;IACnE;;AAGA,IAAA,MAAM,cAAc,GAAA;QAClB,OAAO,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,IAAI;IACpD;AAEA;;;;;;;AAOG;AACH,IAAA,MAAM,iBAAiB,GAAA;QACrB,OAAO,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,IAAI;IACvD;;AAGA,IAAA,MAAM,iBAAiB,GAAA;QACrB,OAAO,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,IAAI;IACvD;;AAGA,IAAA,MAAM,aAAa,GAAA;AACjB,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IACxF;AAEA;;;AAGG;AACH,IAAA,MAAM,gBAAgB,GAAA;AACpB,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,kBAAkB,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IAC9F;;AAGA,IAAA,MAAM,gBAAgB,GAAA;AACpB,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,kBAAkB,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IAC9F;;AAGA,IAAA,MAAM,KAAK,GAAA;AACT,QAAA,MAAM,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE;IAC5D;AAEA;;;AAGG;AACH,IAAA,MAAM,QAAQ,GAAA;AACZ,QAAA,MAAM,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,KAAK,EAAE;IAClE;;AAGA,IAAA,MAAM,QAAQ,GAAA;AACZ,QAAA,MAAM,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,KAAK,EAAE;IAClE;AAEA;;;;;;;AAOG;AACH,IAAA,MAAM,iBAAiB,GAAA;AACrB,QAAA,MAAM,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,oBAAoB,CAAC,EAAE,EAAE,aAAa,CAAC,UAAU,CAAC;IACjF;;AAGA,IAAA,MAAM,WAAW,GAAA;QACf,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE;IACnC;;AAGA,IAAA,MAAM,UAAU,GAAA;AACd,QAAA,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC;IACpD;;AAGA,IAAA,MAAM,OAAO,GAAA;AACX,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,YAAY,CAAC,MAAM,CAAC;IACjD;AAEA;;;;;;AAMG;AACH,IAAA,MAAM,iBAAiB,GAAA;AACrB,QAAA,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,YAAY,CAAC,iBAAiB,CAAC;AAC5E,QAAA,IAAI,CAAC,UAAU;AAAE,YAAA,OAAO,KAAK;AAC7B,QAAA,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,MAAM,UAAU;IACzE;;AAGA,IAAA,MAAM,yBAAyB,GAAA;AAC7B,QAAA,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,YAAY,CAAC,aAAa,CAAC,MAAM,MAAM;IAC3E;IAEQ,MAAM,YAAY,CAAC,IAAuC,EAAA;QAChE,OAAO,IAAI,CAAC,kBAAkB,CAAC,8BAA8B,IAAI,CAAA,CAAE,CAAC,EAAE;IACxE;AAEQ,IAAA,MAAM,aAAa,CAAC,IAAuC,EAAE,MAAc,EAAA;QACjF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;QAC5C,IAAI,CAAC,MAAM,EAAE;AACX,YAAA,MAAM,IAAI,KAAK,CACb,mBAAmB,MAAM,CAAA,uBAAA,EAA0B,IAAI,CAAA,kCAAA,CAAoC;AACzF,gBAAA,CAAA,EAAG,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAA,EAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA,oDAAA,CAAsD;AAC9F,gBAAA,CAAA,wBAAA,EAA2B,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAA,EAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA,qBAAA,CAAuB,CAC1F;QACH;AACA,QAAA,OAAO,MAAM;IACf;;;ACpSF;;;;;AAKG;AAMH;;;;;;;;;;;;;;;;;;;AAmBG;AACG,MAAO,sBAAuB,SAAQ,gBAAgB,CAAA;AAC1D,IAAA,OAAO,YAAY,GAAG,mBAAmB;;AAGzC,IAAA,OAAO,IAAI,CAAC,OAAA,GAAyC,EAAE,EAAA;AACrD,QAAA,OAAO,IAAI,gBAAgB,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC,SAAS,CACpE,UAAU,EACV,OAAO,CAAC,QAAQ,EAChB,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,MAAM,OAAO,CAAC,WAAW,EAAE,MAAM,QAAQ,CACxE;IACH;;AAGA,IAAA,MAAM,WAAW,GAAA;QACf,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,wBAAwB,CAAC,IAAI,KAAK,GAAG,QAAQ;IAC1F;AAEA;;;AAGG;AACH,IAAA,MAAM,YAAY,GAAA;AAChB,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,YAAY,CAAC,YAAY,CAAC;IACvD;;AAGA,IAAA,MAAM,OAAO,GAAA;AACX,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,kBAAkB,CAAC,0BAA0B,CAAC,EAAE,MAAM,IAAI;IAC/E;AAEA;;;;;;AAMG;AACH,IAAA,MAAM,YAAY,GAAA;QAChB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,+BAA+B,CAAC,EAAE;AAC1E,QAAA,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACvD;;IAGA,MAAM,eAAe,CAAC,KAAa,EAAA;AACjC,QAAA,OAAO,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,iBAAiB,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IACrF;;IAGA,MAAM,OAAO,CAAC,KAAa,EAAA;AACzB,QAAA,MAAM,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,KAAK,EAAE;IACzD;AAEA;;;;;;AAMG;IACH,MAAM,QAAQ,CAAC,KAAa,EAAA;QAC1B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC;QACnD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,+BAA+B,CAAC,EAAE;AAC3E,QAAA,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE;IAC9B;AAEA;;;;;;AAMG;AACK,IAAA,MAAM,UAAU,CAAC,KAAa,EAAE,MAAc,EAAA;QACpD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,iCAAiC,CAAC,EAAE;AAC1E,QAAA,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAChD;;AAGQ,IAAA,MAAM,OAAO,CAAC,KAAa,EAAE,MAAc,EAAA;QACjD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,iCAAiC,CAAC,EAAE;AAC1E,QAAA,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;YACzC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK;AAAE,gBAAA,OAAO,KAAK;QAChD;AAEA,QAAA,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE;AACzC,QAAA,MAAM,IAAI,KAAK,CACb,0BAA0B,MAAM,CAAA,EAAA,EAAK,KAAK,CAAA,iCAAA,CAAmC;AAC3E,YAAA,CAAA,EAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,oCAAoC,CAAA,CAAA,CAAG,CACnF;IACH;;;ACxHF;;AAEG;;;;"}
1
+ {"version":3,"file":"ngwr-window-testing.mjs","sources":["../../../projects/lib/window/testing/wr-window-harness.ts","../../../projects/lib/window/testing/wr-window-taskbar-harness.ts","../../../projects/lib/window/testing/ngwr-window-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 { ContentContainerComponentHarness, HarnessPredicate, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrWindowChromeSize, WrWindowState } from 'ngwr/window';\n\nimport type { WrWindowBox, WrWindowHarnessFilters } from './interfaces';\n\nconst STATES: readonly WrWindowState[] = ['normal', 'minimized', 'maximized'];\n\n/**\n * Test harness for a window opened through `WrWindowManager.open()`.\n *\n * `WrWindow` is deliberately not exported — a window is always a service call — so\n * there is nothing in your fixture to load: each window is a portal in its own\n * overlay pane, and this comes from\n * `TestbedHarnessEnvironment.documentRootLoader()`.\n *\n * It is a CONTENT CONTAINER, like `WrDialogHarness`: the component you opened is\n * projected into the window's body, so `window.getHarness(WrButtonHarness…)` reads\n * ITS buttons and cannot reach another window's. That matters more here than\n * anywhere else, because windows are explicitly non-modal and several are normally\n * open at once.\n *\n * **Geometry is read from the inline styles the component writes**, never measured:\n * a window is positioned with `left` / `top` / `width` / `height` in pixels, and a\n * unit test has no layout to measure. That also makes {@link getBox} the way to\n * assert a move or a resize — including the ones a drag would produce, which no\n * synthetic pointer event can, since the drag divides by a rect that is 0×0 here.\n *\n * @example\n * ```ts\n * const rootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture);\n * windows.open(EditorComponent, { title: 'Untitled.md' });\n * await fixture.whenStable();\n *\n * const win = await rootLoader.getHarness(WrWindowHarness.with({ title: 'Untitled.md' }));\n * await win.maximize();\n * expect(await win.getState()).toBe('maximized');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrWindowHarness extends ContentContainerComponentHarness {\n static hostSelector = 'wr-window';\n\n /** Build a predicate that narrows the query — windows come in stacks. */\n static with(options: WrWindowHarnessFilters = {}): HarnessPredicate<WrWindowHarness> {\n return new HarnessPredicate(WrWindowHarness, options)\n .addOption('title', options.title, (harness, title) => HarnessPredicate.stringMatches(harness.getTitle(), title))\n .addOption('state', options.state, async (harness, state) => (await harness.getState()) === state);\n }\n\n private readonly titleEl = this.locatorFor('.wr-window__title');\n\n /**\n * Whether this window is still on the page.\n *\n * For a harness you are already HOLDING: closing disposes the overlay, so this\n * flips to `false` where a fresh `getHarnessOrNull()` comes back `null`. A\n * MINIMIZED window is still open — it is a state, not a dismissal — and this\n * stays `true` for one.\n */\n async isOpen(): Promise<boolean> {\n return (await this.host()).matchesSelector('body wr-window');\n }\n\n /** The title-bar text. */\n async getTitle(): Promise<string> {\n return (await this.titleEl()).text();\n }\n\n /** The projected `[wrWindowTitleExtra]` content, trimmed — empty when there is none. */\n async getTitleExtraText(): Promise<string> {\n return (await this.locatorFor('.wr-window__title-extra')()).text();\n }\n\n /** The window's visual state, from the host modifier. */\n async getState(): Promise<WrWindowState> {\n const host = await this.host();\n for (const state of STATES) {\n if (await host.hasClass(`wr-window--${state}`)) return state;\n }\n throw new Error(\n 'WrWindowHarness.getState(): this host carries no `wr-window--<state>` class, which every window sets — ' +\n 'the element matched is probably not an ngwr window.'\n );\n }\n\n /**\n * The OS the chrome is dressed as — `macos`, `windows` or `linux`.\n *\n * Worth asserting because it is not cosmetic: on macOS the buttons are a\n * traffic-light cluster in close → minimize → maximize order, and the Linux\n * chrome renders neither minimize nor maximize UNLESS `showMinimize` /\n * `showMaximize` asks for them. \"No minimize on Linux\" is a default, not an\n * invariant — see {@link hasMinimizeButton}.\n */\n async getOs(): Promise<string> {\n const host = await this.host();\n for (const os of ['macos', 'windows', 'linux']) {\n if (await host.hasClass(`wr-window--os-${os}`)) return os;\n }\n throw new Error('WrWindowHarness.getOs(): this window carries no `wr-window--os-<name>` class.');\n }\n\n /**\n * The title-bar density — `sm` or `md`.\n *\n * Typed as the union rather than `string`, so a value added to\n * `WrWindowChromeSize` fails this method's compile instead of quietly\n * answering `md` for it. Only `sm` carries a modifier class: `md` is the\n * default and paints from the base block, so its answer is the absence of\n * the other one.\n */\n async getChromeSize(): Promise<WrWindowChromeSize> {\n return (await (await this.host()).hasClass('wr-window--chrome-sm')) ? 'sm' : 'md';\n }\n\n /**\n * Where the window is and how big, in CSS pixels.\n *\n * From the inline styles the component writes, which is the only readable answer\n * without layout — and the same numbers a drag or a snap would change.\n */\n async getBox(): Promise<WrWindowBox> {\n const host = await this.host();\n return {\n x: Number.parseFloat(await host.getCssValue('left')),\n y: Number.parseFloat(await host.getCssValue('top')),\n width: Number.parseFloat(await host.getCssValue('width')),\n height: Number.parseFloat(await host.getCssValue('height')),\n };\n }\n\n /**\n * The window's stacking order.\n *\n * The manager hands out a z-index per focus, so this is how a spec asserts that\n * clicking a window brought it to the front — the one thing a window manager\n * exists to do, and the reason these panes are deliberately kept OUT of the\n * browser's top layer, which orders strictly by entry.\n */\n async getZIndex(): Promise<number> {\n return Number.parseFloat(await (await this.host()).getCssValue('z-index'));\n }\n\n /** Whether the resize handles are rendered — `resizable`, and only in the normal state. */\n async isResizable(): Promise<boolean> {\n return (await this.host()).hasClass('wr-window--resizable');\n }\n\n /** The resize handles currently rendered, by their edge / corner suffix. */\n async getResizeHandles(): Promise<string[]> {\n const handles = await this.locatorForAll('.wr-window__handle')();\n return Promise.all(\n handles.map(async handle => {\n const classes = (await handle.getAttribute('class')) ?? '';\n return (\n classes\n .split(/\\s+/)\n .find(name => name.startsWith('wr-window__handle--'))\n ?.slice(19) ?? ''\n );\n })\n );\n }\n\n /** The body text of the window — the component you opened, as rendered. */\n async getBodyText(): Promise<string> {\n return (await this.locatorFor('.wr-window__body')()).text();\n }\n\n /** The projected `[wrWindowStatusBar]` content, trimmed. */\n async getStatusBarText(): Promise<string> {\n return (await this.locatorFor('.wr-window__status-bar')()).text();\n }\n\n /** Whether the chrome offers a close button (`showClose`). */\n async hasCloseButton(): Promise<boolean> {\n return (await this.chromeAction('close')) !== null;\n }\n\n /**\n * Whether the chrome offers a minimize button.\n *\n * `false` on Linux BY DEFAULT — that chrome is close-only by convention, which\n * is a real behavioural difference and not a style. `showMinimize: true`\n * overrides it, and a spec that asserts \"no minimize on Linux\" without saying\n * which of the two it means will pass for the wrong reason.\n */\n async hasMinimizeButton(): Promise<boolean> {\n return (await this.chromeAction('minimize')) !== null;\n }\n\n /** Whether the chrome offers a maximize button. Also `false` on Linux by default. */\n async hasMaximizeButton(): Promise<boolean> {\n return (await this.chromeAction('maximize')) !== null;\n }\n\n /** The close button's accessible name — the buttons are icon-only, so it is their only name. */\n async getCloseLabel(): Promise<string | null> {\n return (await this.requireAction('close', 'getCloseLabel')).getAttribute('aria-label');\n }\n\n /**\n * The minimize button's accessible name, which flips to the restore wording while\n * the window is minimized — the same button does both.\n */\n async getMinimizeLabel(): Promise<string | null> {\n return (await this.requireAction('minimize', 'getMinimizeLabel')).getAttribute('aria-label');\n }\n\n /** The maximize button's accessible name, which flips while the window is maximized. */\n async getMaximizeLabel(): Promise<string | null> {\n return (await this.requireAction('maximize', 'getMaximizeLabel')).getAttribute('aria-label');\n }\n\n /** Click the close button. Throws on a window opened `showClose: false`. */\n async close(): Promise<void> {\n await (await this.requireAction('close', 'close')).click();\n }\n\n /**\n * Click the minimize button — which RESTORES a minimized window, since it is the\n * same control.\n */\n async minimize(): Promise<void> {\n await (await this.requireAction('minimize', 'minimize')).click();\n }\n\n /** Click the maximize button — which restores a maximized window down. */\n async maximize(): Promise<void> {\n await (await this.requireAction('maximize', 'maximize')).click();\n }\n\n /**\n * Double-click the title bar, the other way to maximize.\n *\n * A separate gesture worth its own assertion, and it does NOT follow the\n * button: only an explicit `showMaximize: false` turns it off. The Linux\n * chrome hides the button by convention and keeps the gesture — which is also\n * the only way out of a window that reached `maximized` without one.\n */\n async doubleClickChrome(): Promise<void> {\n await (await this.locatorFor('.wr-window__chrome')()).dispatchEvent('dblclick');\n }\n\n /** Click the window body — which pulls it to the front of the stack. */\n async focusWindow(): Promise<void> {\n await (await this.host()).click();\n }\n\n /** Press Escape. A window opened `closeOnEscape: false` ignores it — assert, do not assume. */\n async sendEscape(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.ESCAPE);\n }\n\n /** The role the window announces — `dialog`, on a non-modal window. */\n async getRole(): Promise<string | null> {\n return (await this.host()).getAttribute('role');\n }\n\n /**\n * Whether the window takes its accessible name from its own title element.\n *\n * Resolved rather than trusted: `aria-labelledby` naming an element that is not\n * this window's title is not a name, and with several windows open the ids are\n * the only thing keeping them apart.\n */\n async isLabelledByTitle(): Promise<boolean> {\n const labelledBy = await (await this.host()).getAttribute('aria-labelledby');\n if (!labelledBy) return false;\n return (await (await this.titleEl()).getAttribute('id')) === labelledBy;\n }\n\n /** Whether the window is hidden from assistive tech (`aria-hidden`, set while closed). */\n async isHiddenFromAssistiveTech(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-hidden')) === 'true';\n }\n\n private async chromeAction(name: 'close' | 'minimize' | 'maximize'): Promise<TestElement | null> {\n return this.locatorForOptional(`.wr-window__chrome-action--${name}`)();\n }\n\n private async requireAction(name: 'close' | 'minimize' | 'maximize', method: string): Promise<TestElement> {\n const button = await this.chromeAction(name);\n if (!button) {\n throw new Error(\n `WrWindowHarness.${method}(): this window has no ${name} button. It was opened with \\`show` +\n `${name[0].toUpperCase()}${name.slice(1)}: false\\`, or its chrome is the Linux one, which is ` +\n `close-only unless \\`show${name[0].toUpperCase()}${name.slice(1)}: true\\` asks for it.`\n );\n }\n return button;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrWindowTaskbarHarnessFilters } from './interfaces';\n\n/**\n * Test harness for `<wr-window-taskbar>` — the rail of minimized windows.\n *\n * The taskbar IS an element in your template, so it comes from the normal fixture\n * loader, unlike the windows it lists. It renders nothing but its own host until a\n * window is minimized; {@link isEmpty} is the question, and {@link getTabTitles}\n * answers `[]` rather than throwing, because \"no minimized windows\" is the ordinary\n * state rather than a mistake.\n *\n * @example\n * ```ts\n * const taskbar = await loader.getHarness(WrWindowTaskbarHarness);\n *\n * await (await rootLoader.getHarness(WrWindowHarness)).minimize();\n * expect(await taskbar.getTabTitles()).toEqual(['Untitled.md']);\n * await taskbar.restore('Untitled.md');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrWindowTaskbarHarness extends ComponentHarness {\n static hostSelector = 'wr-window-taskbar';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrWindowTaskbarHarnessFilters = {}): HarnessPredicate<WrWindowTaskbarHarness> {\n return new HarnessPredicate(WrWindowTaskbarHarness, options).addOption(\n 'position',\n options.position,\n async (harness, position) => (await harness.getPosition()) === position\n );\n }\n\n /** The edge the rail is pinned to. */\n async getPosition(): Promise<'top' | 'bottom'> {\n return (await (await this.host()).hasClass('wr-window-taskbar--top')) ? 'top' : 'bottom';\n }\n\n /**\n * The rail's accessible name — it is a `role=\"toolbar\"`, and an unnamed one is\n * announced as nothing.\n */\n async getRailLabel(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /** Whether nothing is minimized — the rail draws no markup at all then. */\n async isEmpty(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-window-taskbar__rail')()) === null;\n }\n\n /**\n * The titles in the rail, in order.\n *\n * A window opened without a title still gets a tab, named by the catalog's\n * fallback rather than left blank — a nameless tab is unreachable by voice and\n * indistinguishable from its neighbours.\n */\n async getTabTitles(): Promise<string[]> {\n const titles = await this.locatorForAll('.wr-window-taskbar__tab-title')();\n return Promise.all(titles.map(title => title.text()));\n }\n\n /** A tab's accessible name — the restore wording with the window's title in it. */\n async getRestoreLabel(title: string): Promise<string | null> {\n return (await this.requireTab(title, 'getRestoreLabel')).getAttribute('aria-label');\n }\n\n /** Click a tab — restoring that window and focusing it. */\n async restore(title: string): Promise<void> {\n await (await this.requireTab(title, 'restore')).click();\n }\n\n /**\n * Click a tab's ✕ — closing the window WITHOUT restoring it.\n *\n * Still worth asserting separately now that the two are siblings rather than\n * nested: they sit inside one pill and look like one control, so a close that\n * also restored would flash a closing window's content and look plausible.\n */\n async closeTab(title: string): Promise<void> {\n const index = await this.indexOf(title, 'closeTab');\n const closers = await this.locatorForAll('.wr-window-taskbar__tab-close')();\n await closers[index].click();\n }\n\n /**\n * The tab's BUTTON, not the pill around it.\n *\n * `.wr-window-taskbar__tab` is a plain container — it carries the pill's look\n * and nothing else, because the close beside it cannot be a control nested in\n * a control. The name and the click both belong to `__tab-restore`.\n */\n private async requireTab(title: string, method: string): Promise<TestElement> {\n const tabs = await this.locatorForAll('.wr-window-taskbar__tab-restore')();\n return tabs[await this.indexOf(title, method)];\n }\n\n /** Which tab carries this title — one lookup for both the tab and its close button. */\n private async indexOf(title: string, method: string): Promise<number> {\n const tabs = await this.locatorForAll('.wr-window-taskbar__tab-restore')();\n for (const [index, tab] of tabs.entries()) {\n if ((await tab.text()) === title) return index;\n }\n\n const offered = await this.getTabTitles();\n throw new Error(\n `WrWindowTaskbarHarness.${method}(\"${title}\"): no such tab. The rail holds: ` +\n `${offered.length ? offered.join(', ') : '(nothing — no window is minimized)'}.`\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -160,8 +160,8 @@ class WrWindow {
160
160
  ...(ngDevMode ? [{ debugName: "minimizeVisible" }] : /* istanbul ignore next */ []));
161
161
  maximizeVisible = computed(() => this.showMaximize() ?? this.resolvedOs() !== 'linux', /* @ts-ignore */
162
162
  ...(ngDevMode ? [{ debugName: "maximizeVisible" }] : /* istanbul ignore next */ []));
163
- /** Title-bar density. @default 'normal' */
164
- chromeSize = input('normal', /* @ts-ignore */
163
+ /** Title-bar density. @default 'md' */
164
+ chromeSize = input('md', /* @ts-ignore */
165
165
  ...(ngDevMode ? [{ debugName: "chromeSize" }] : /* istanbul ignore next */ []));
166
166
  /**
167
167
  * Enter / leave animations. Set to `false` to disable the open-fade
@@ -747,11 +747,11 @@ class WrWindowContainer {
747
747
  });
748
748
  }
749
749
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrWindowContainer, deps: [], target: i0.ɵɵFactoryTarget.Component });
750
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrWindowContainer, isStandalone: true, selector: "wr-window-container", viewQueries: [{ propertyName: "contentVcr", first: true, predicate: ["content"], descendants: true, read: ViewContainerRef, isSignal: true }, { propertyName: "wrWindow", first: true, predicate: WrWindow, descendants: true, isSignal: true }], ngImport: i0, template: "<wr-window\n [open]=\"true\"\n [title]=\"ref.title()\"\n [os]=\"config.os ?? 'auto'\"\n [size]=\"config.size ?? 'md'\"\n [chromeSize]=\"config.chromeSize ?? 'normal'\"\n [animations]=\"config.animations ?? true\"\n [initialX]=\"config.x ?? null\"\n [initialY]=\"config.y ?? null\"\n [initialWidth]=\"config.width ?? null\"\n [initialHeight]=\"config.height ?? null\"\n [minWidth]=\"config.minWidth ?? 220\"\n [minHeight]=\"config.minHeight ?? 140\"\n [maxWidth]=\"config.maxWidth ?? 1000000\"\n [maxHeight]=\"config.maxHeight ?? 1000000\"\n [movable]=\"config.movable ?? true\"\n [resizable]=\"config.resizable ?? true\"\n [keepInViewport]=\"config.keepInViewport ?? true\"\n [snap]=\"config.snap ?? 'none'\"\n [dragHandle]=\"config.dragHandle ?? null\"\n [showMinimize]=\"config.showMinimize ?? null\"\n [showMaximize]=\"config.showMaximize ?? null\"\n [showClose]=\"config.showClose ?? true\"\n (closed)=\"ref.close()\"\n #window\n>\n <ng-container #content />\n</wr-window>\n\n@if (window.snapTarget(); as target) {\n <div class=\"wr-window__snap-hint\" [attr.data-target]=\"target\" aria-hidden=\"true\"></div>\n}\n", dependencies: [{ kind: "component", type: WrWindow, selector: "wr-window", inputs: ["open", "state", "title", "initialX", "initialY", "size", "initialWidth", "initialHeight", "os", "chromeSize", "animations", "minWidth", "minHeight", "maxWidth", "maxHeight", "movable", "resizable", "keepInViewport", "snap", "dragHandle", "showMinimize", "showMaximize", "showClose"], outputs: ["openChange", "stateChange", "closed", "moved", "resized"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
750
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrWindowContainer, isStandalone: true, selector: "wr-window-container", viewQueries: [{ propertyName: "contentVcr", first: true, predicate: ["content"], descendants: true, read: ViewContainerRef, isSignal: true }, { propertyName: "wrWindow", first: true, predicate: WrWindow, descendants: true, isSignal: true }], ngImport: i0, template: "<wr-window\n [open]=\"true\"\n [title]=\"ref.title()\"\n [os]=\"config.os ?? 'auto'\"\n [size]=\"config.size ?? 'md'\"\n [chromeSize]=\"config.chromeSize ?? 'md'\"\n [animations]=\"config.animations ?? true\"\n [initialX]=\"config.x ?? null\"\n [initialY]=\"config.y ?? null\"\n [initialWidth]=\"config.width ?? null\"\n [initialHeight]=\"config.height ?? null\"\n [minWidth]=\"config.minWidth ?? 220\"\n [minHeight]=\"config.minHeight ?? 140\"\n [maxWidth]=\"config.maxWidth ?? 1000000\"\n [maxHeight]=\"config.maxHeight ?? 1000000\"\n [movable]=\"config.movable ?? true\"\n [resizable]=\"config.resizable ?? true\"\n [keepInViewport]=\"config.keepInViewport ?? true\"\n [snap]=\"config.snap ?? 'none'\"\n [dragHandle]=\"config.dragHandle ?? null\"\n [showMinimize]=\"config.showMinimize ?? null\"\n [showMaximize]=\"config.showMaximize ?? null\"\n [showClose]=\"config.showClose ?? true\"\n (closed)=\"ref.close()\"\n #window\n>\n <ng-container #content />\n</wr-window>\n\n@if (window.snapTarget(); as target) {\n <div class=\"wr-window__snap-hint\" [attr.data-target]=\"target\" aria-hidden=\"true\"></div>\n}\n", dependencies: [{ kind: "component", type: WrWindow, selector: "wr-window", inputs: ["open", "state", "title", "initialX", "initialY", "size", "initialWidth", "initialHeight", "os", "chromeSize", "animations", "minWidth", "minHeight", "maxWidth", "maxHeight", "movable", "resizable", "keepInViewport", "snap", "dragHandle", "showMinimize", "showMaximize", "showClose"], outputs: ["openChange", "stateChange", "closed", "moved", "resized"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
751
751
  }
752
752
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrWindowContainer, decorators: [{
753
753
  type: Component,
754
- args: [{ selector: 'wr-window-container', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [WrWindow], template: "<wr-window\n [open]=\"true\"\n [title]=\"ref.title()\"\n [os]=\"config.os ?? 'auto'\"\n [size]=\"config.size ?? 'md'\"\n [chromeSize]=\"config.chromeSize ?? 'normal'\"\n [animations]=\"config.animations ?? true\"\n [initialX]=\"config.x ?? null\"\n [initialY]=\"config.y ?? null\"\n [initialWidth]=\"config.width ?? null\"\n [initialHeight]=\"config.height ?? null\"\n [minWidth]=\"config.minWidth ?? 220\"\n [minHeight]=\"config.minHeight ?? 140\"\n [maxWidth]=\"config.maxWidth ?? 1000000\"\n [maxHeight]=\"config.maxHeight ?? 1000000\"\n [movable]=\"config.movable ?? true\"\n [resizable]=\"config.resizable ?? true\"\n [keepInViewport]=\"config.keepInViewport ?? true\"\n [snap]=\"config.snap ?? 'none'\"\n [dragHandle]=\"config.dragHandle ?? null\"\n [showMinimize]=\"config.showMinimize ?? null\"\n [showMaximize]=\"config.showMaximize ?? null\"\n [showClose]=\"config.showClose ?? true\"\n (closed)=\"ref.close()\"\n #window\n>\n <ng-container #content />\n</wr-window>\n\n@if (window.snapTarget(); as target) {\n <div class=\"wr-window__snap-hint\" [attr.data-target]=\"target\" aria-hidden=\"true\"></div>\n}\n" }]
754
+ args: [{ selector: 'wr-window-container', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [WrWindow], template: "<wr-window\n [open]=\"true\"\n [title]=\"ref.title()\"\n [os]=\"config.os ?? 'auto'\"\n [size]=\"config.size ?? 'md'\"\n [chromeSize]=\"config.chromeSize ?? 'md'\"\n [animations]=\"config.animations ?? true\"\n [initialX]=\"config.x ?? null\"\n [initialY]=\"config.y ?? null\"\n [initialWidth]=\"config.width ?? null\"\n [initialHeight]=\"config.height ?? null\"\n [minWidth]=\"config.minWidth ?? 220\"\n [minHeight]=\"config.minHeight ?? 140\"\n [maxWidth]=\"config.maxWidth ?? 1000000\"\n [maxHeight]=\"config.maxHeight ?? 1000000\"\n [movable]=\"config.movable ?? true\"\n [resizable]=\"config.resizable ?? true\"\n [keepInViewport]=\"config.keepInViewport ?? true\"\n [snap]=\"config.snap ?? 'none'\"\n [dragHandle]=\"config.dragHandle ?? null\"\n [showMinimize]=\"config.showMinimize ?? null\"\n [showMaximize]=\"config.showMaximize ?? null\"\n [showClose]=\"config.showClose ?? true\"\n (closed)=\"ref.close()\"\n #window\n>\n <ng-container #content />\n</wr-window>\n\n@if (window.snapTarget(); as target) {\n <div class=\"wr-window__snap-hint\" [attr.data-target]=\"target\" aria-hidden=\"true\"></div>\n}\n" }]
755
755
  }], ctorParameters: () => [], propDecorators: { contentVcr: [{ type: i0.ViewChild, args: ['content', { ...{ read: ViewContainerRef }, isSignal: true }] }], wrWindow: [{ type: i0.ViewChild, args: [i0.forwardRef(() => WrWindow), { isSignal: true }] }] } });
756
756
 
757
757
  /**