ngwr 15.1.0 → 15.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/bits/aurora/styles/_rules.scss +11 -0
  2. package/bits/dot-grid/styles/_rules.scss +9 -1
  3. package/bits/iridescence/iridescence.scss +1 -0
  4. package/bits/iridescence/styles/_index.scss +8 -0
  5. package/bits/iridescence/styles/_rules.scss +72 -0
  6. package/bits/marquee/styles/_rules.scss +7 -3
  7. package/bits/ripple-grid/styles/_rules.scss +10 -2
  8. package/bits/spotlight-card/styles/_rules.scss +7 -4
  9. package/bits/star-border/styles/_rules.scss +5 -2
  10. package/bits/threads/styles/_rules.scss +9 -1
  11. package/bits/waves/styles/_rules.scss +8 -1
  12. package/charts/candlestick-chart/styles/_index.scss +2 -2
  13. package/fesm2022/ngwr-affix.mjs +3 -3
  14. package/fesm2022/ngwr-alert.mjs +3 -3
  15. package/fesm2022/ngwr-anchor.mjs +3 -3
  16. package/fesm2022/ngwr-avatar.mjs +3 -3
  17. package/fesm2022/ngwr-back-top.mjs +3 -3
  18. package/fesm2022/ngwr-badge.mjs +6 -6
  19. package/fesm2022/ngwr-bits-aurora.mjs +30 -38
  20. package/fesm2022/ngwr-bits-aurora.mjs.map +1 -1
  21. package/fesm2022/ngwr-bits-blur-text.mjs +3 -3
  22. package/fesm2022/ngwr-bits-border-glow-testing.mjs.map +1 -1
  23. package/fesm2022/ngwr-bits-border-glow.mjs +11 -5
  24. package/fesm2022/ngwr-bits-border-glow.mjs.map +1 -1
  25. package/fesm2022/ngwr-bits-circular-text.mjs +3 -3
  26. package/fesm2022/ngwr-bits-click-spark.mjs +3 -3
  27. package/fesm2022/ngwr-bits-confetti.mjs +3 -3
  28. package/fesm2022/ngwr-bits-decrypt-text.mjs +3 -3
  29. package/fesm2022/ngwr-bits-dot-grid.mjs +12 -9
  30. package/fesm2022/ngwr-bits-dot-grid.mjs.map +1 -1
  31. package/fesm2022/ngwr-bits-falling-text.mjs +3 -3
  32. package/fesm2022/ngwr-bits-fuzzy-text.mjs +3 -3
  33. package/fesm2022/ngwr-bits-glitch-text.mjs +3 -3
  34. package/fesm2022/ngwr-bits-gradient-text.mjs +3 -3
  35. package/fesm2022/ngwr-bits-iridescence-testing.mjs +31 -0
  36. package/fesm2022/ngwr-bits-iridescence-testing.mjs.map +1 -0
  37. package/fesm2022/ngwr-bits-iridescence.mjs +591 -0
  38. package/fesm2022/ngwr-bits-iridescence.mjs.map +1 -0
  39. package/fesm2022/ngwr-bits-marquee-testing.mjs +1 -1
  40. package/fesm2022/ngwr-bits-marquee-testing.mjs.map +1 -1
  41. package/fesm2022/ngwr-bits-marquee.mjs +7 -7
  42. package/fesm2022/ngwr-bits-marquee.mjs.map +1 -1
  43. package/fesm2022/ngwr-bits-ripple-grid.mjs +8 -11
  44. package/fesm2022/ngwr-bits-ripple-grid.mjs.map +1 -1
  45. package/fesm2022/ngwr-bits-rotating-text.mjs +3 -3
  46. package/fesm2022/ngwr-bits-shiny-text.mjs +6 -6
  47. package/fesm2022/ngwr-bits-splash-cursor.mjs +3 -3
  48. package/fesm2022/ngwr-bits-split-text.mjs +3 -3
  49. package/fesm2022/ngwr-bits-spotlight-card-testing.mjs +1 -1
  50. package/fesm2022/ngwr-bits-spotlight-card-testing.mjs.map +1 -1
  51. package/fesm2022/ngwr-bits-spotlight-card.mjs +10 -10
  52. package/fesm2022/ngwr-bits-spotlight-card.mjs.map +1 -1
  53. package/fesm2022/ngwr-bits-star-border-testing.mjs +1 -1
  54. package/fesm2022/ngwr-bits-star-border-testing.mjs.map +1 -1
  55. package/fesm2022/ngwr-bits-star-border.mjs +7 -7
  56. package/fesm2022/ngwr-bits-star-border.mjs.map +1 -1
  57. package/fesm2022/ngwr-bits-threads.mjs +17 -12
  58. package/fesm2022/ngwr-bits-threads.mjs.map +1 -1
  59. package/fesm2022/ngwr-bits-tilt-card.mjs +6 -6
  60. package/fesm2022/ngwr-bits-typewriter.mjs +3 -3
  61. package/fesm2022/ngwr-bits-waves.mjs +15 -11
  62. package/fesm2022/ngwr-bits-waves.mjs.map +1 -1
  63. package/fesm2022/ngwr-breadcrumbs.mjs +6 -6
  64. package/fesm2022/ngwr-burger.mjs +3 -3
  65. package/fesm2022/ngwr-button.mjs +6 -6
  66. package/fesm2022/ngwr-calendar.mjs +3 -3
  67. package/fesm2022/ngwr-card.mjs +9 -9
  68. package/fesm2022/ngwr-carousel.mjs +6 -6
  69. package/fesm2022/ngwr-cascader.mjs +3 -3
  70. package/fesm2022/ngwr-charts-bar-chart.mjs +3 -3
  71. package/fesm2022/ngwr-charts-boxplot-chart.mjs +3 -3
  72. package/fesm2022/ngwr-charts-bullet-chart.mjs +3 -3
  73. package/fesm2022/ngwr-charts-calendar-heatmap.mjs +3 -3
  74. package/fesm2022/ngwr-charts-candlestick-chart.mjs +7 -7
  75. package/fesm2022/ngwr-charts-candlestick-chart.mjs.map +1 -1
  76. package/fesm2022/ngwr-charts-chart-legend.mjs +6 -6
  77. package/fesm2022/ngwr-charts-core.mjs +3 -3
  78. package/fesm2022/ngwr-charts-donut-chart.mjs +3 -3
  79. package/fesm2022/ngwr-charts-funnel-chart.mjs +3 -3
  80. package/fesm2022/ngwr-charts-gauge.mjs +3 -3
  81. package/fesm2022/ngwr-charts-heatmap-chart.mjs +3 -3
  82. package/fesm2022/ngwr-charts-line-chart.mjs +3 -3
  83. package/fesm2022/ngwr-charts-meter-group.mjs +3 -3
  84. package/fesm2022/ngwr-charts-radar-chart.mjs +3 -3
  85. package/fesm2022/ngwr-charts-scatter-chart.mjs +3 -3
  86. package/fesm2022/ngwr-charts-sparkline.mjs +3 -3
  87. package/fesm2022/ngwr-charts-treemap-chart.mjs +3 -3
  88. package/fesm2022/ngwr-charts-waterfall-chart.mjs +3 -3
  89. package/fesm2022/ngwr-checkbox.mjs +6 -6
  90. package/fesm2022/ngwr-clipboard.mjs +3 -3
  91. package/fesm2022/ngwr-collapse.mjs +9 -9
  92. package/fesm2022/ngwr-color-picker.mjs +6 -6
  93. package/fesm2022/ngwr-command-palette.mjs +3 -3
  94. package/fesm2022/ngwr-compare.mjs +3 -3
  95. package/fesm2022/ngwr-context-menu.mjs +12 -12
  96. package/fesm2022/ngwr-cookie.mjs +3 -3
  97. package/fesm2022/ngwr-counter.mjs +6 -6
  98. package/fesm2022/ngwr-date-picker.mjs +21 -21
  99. package/fesm2022/ngwr-date.mjs +6 -6
  100. package/fesm2022/ngwr-density.mjs +6 -6
  101. package/fesm2022/ngwr-descriptions.mjs +6 -6
  102. package/fesm2022/ngwr-dialog.mjs +15 -15
  103. package/fesm2022/ngwr-directives.mjs +12 -12
  104. package/fesm2022/ngwr-divider.mjs +3 -3
  105. package/fesm2022/ngwr-drawer.mjs +18 -18
  106. package/fesm2022/ngwr-dropdown.mjs +9 -9
  107. package/fesm2022/ngwr-editor.mjs +3 -3
  108. package/fesm2022/ngwr-empty.mjs +3 -3
  109. package/fesm2022/ngwr-event-calendar.mjs +6 -6
  110. package/fesm2022/ngwr-file-upload.mjs +3 -3
  111. package/fesm2022/ngwr-form.mjs +9 -9
  112. package/fesm2022/ngwr-graph.mjs +6 -6
  113. package/fesm2022/ngwr-hotkey.mjs +6 -6
  114. package/fesm2022/ngwr-i18n.mjs +9 -9
  115. package/fesm2022/ngwr-icon.mjs +6 -6
  116. package/fesm2022/ngwr-image-cropper.mjs +3 -3
  117. package/fesm2022/ngwr-input-number.mjs +3 -3
  118. package/fesm2022/ngwr-input-otp.mjs +3 -3
  119. package/fesm2022/ngwr-input.mjs +15 -15
  120. package/fesm2022/ngwr-keyboard.mjs +3 -3
  121. package/fesm2022/ngwr-knob.mjs +3 -3
  122. package/fesm2022/ngwr-layout.mjs +15 -15
  123. package/fesm2022/ngwr-lightbox.mjs +3 -3
  124. package/fesm2022/ngwr-list.mjs +6 -6
  125. package/fesm2022/ngwr-loading-bar.mjs +6 -6
  126. package/fesm2022/ngwr-mention.mjs +6 -6
  127. package/fesm2022/ngwr-meta.mjs +6 -6
  128. package/fesm2022/ngwr-overlay.mjs +6 -6
  129. package/fesm2022/ngwr-page-header.mjs +3 -3
  130. package/fesm2022/ngwr-pagination.mjs +3 -3
  131. package/fesm2022/ngwr-pipes.mjs +21 -21
  132. package/fesm2022/ngwr-platform.mjs +6 -6
  133. package/fesm2022/ngwr-popconfirm.mjs +6 -6
  134. package/fesm2022/ngwr-popover.mjs +12 -12
  135. package/fesm2022/ngwr-progress.mjs +3 -3
  136. package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
  137. package/fesm2022/ngwr-qr.mjs +4 -3
  138. package/fesm2022/ngwr-qr.mjs.map +1 -1
  139. package/fesm2022/ngwr-radio.mjs +6 -6
  140. package/fesm2022/ngwr-rating.mjs +3 -3
  141. package/fesm2022/ngwr-result.mjs +12 -12
  142. package/fesm2022/ngwr-schema-form.mjs +3 -3
  143. package/fesm2022/ngwr-scroll.mjs +3 -3
  144. package/fesm2022/ngwr-segmented.mjs +3 -3
  145. package/fesm2022/ngwr-select.mjs +12 -12
  146. package/fesm2022/ngwr-sidebar.mjs +3 -3
  147. package/fesm2022/ngwr-skeleton.mjs +3 -3
  148. package/fesm2022/ngwr-slider.mjs +3 -3
  149. package/fesm2022/ngwr-sortable-list.mjs +9 -9
  150. package/fesm2022/ngwr-speed-dial.mjs +3 -3
  151. package/fesm2022/ngwr-spinner.mjs +3 -3
  152. package/fesm2022/ngwr-splitter.mjs +3 -3
  153. package/fesm2022/ngwr-statistic.mjs +9 -9
  154. package/fesm2022/ngwr-stepper.mjs +6 -6
  155. package/fesm2022/ngwr-storage.mjs +3 -3
  156. package/fesm2022/ngwr-switch.mjs +3 -3
  157. package/fesm2022/ngwr-table.mjs +18 -18
  158. package/fesm2022/ngwr-tabs-router.mjs +3 -3
  159. package/fesm2022/ngwr-tabs.mjs +6 -6
  160. package/fesm2022/ngwr-textarea.mjs +3 -3
  161. package/fesm2022/ngwr-theme.mjs +3 -3
  162. package/fesm2022/ngwr-timeline.mjs +6 -6
  163. package/fesm2022/ngwr-toast.mjs +9 -9
  164. package/fesm2022/ngwr-toolbar.mjs +3 -3
  165. package/fesm2022/ngwr-tour.mjs +6 -6
  166. package/fesm2022/ngwr-transfer.mjs +3 -3
  167. package/fesm2022/ngwr-tree.mjs +3 -3
  168. package/fesm2022/ngwr-typography.mjs +3 -3
  169. package/fesm2022/ngwr-utils.mjs +31 -1
  170. package/fesm2022/ngwr-utils.mjs.map +1 -1
  171. package/fesm2022/ngwr-version.mjs +1 -1
  172. package/fesm2022/ngwr-version.mjs.map +1 -1
  173. package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
  174. package/fesm2022/ngwr-window.mjs +12 -12
  175. package/llms-full.txt +12 -1
  176. package/mcp/server.js +15 -1
  177. package/mcp/tools.js +3 -3
  178. package/package.json +10 -1
  179. package/schematics/ng-add/index.js +2 -2
  180. package/schematics/use/symbol-map.json +1 -0
  181. package/skills/ngwr/SKILL.md +1 -1
  182. package/skills/ngwr/references/catalog.md +2 -0
  183. package/types/ngwr-bits-aurora.d.ts +11 -0
  184. package/types/ngwr-bits-border-glow-testing.d.ts +5 -6
  185. package/types/ngwr-bits-border-glow.d.ts +5 -1
  186. package/types/ngwr-bits-dot-grid.d.ts +7 -2
  187. package/types/ngwr-bits-iridescence-testing.d.ts +85 -0
  188. package/types/ngwr-bits-iridescence.d.ts +244 -0
  189. package/types/ngwr-bits-ripple-grid.d.ts +4 -7
  190. package/types/ngwr-bits-splash-cursor.d.ts +0 -1
  191. package/types/ngwr-bits-spotlight-card-testing.d.ts +3 -1
  192. package/types/ngwr-bits-star-border-testing.d.ts +7 -7
  193. package/types/ngwr-bits-threads.d.ts +11 -8
  194. package/types/ngwr-bits-waves.d.ts +6 -3
  195. package/types/ngwr-charts-core.d.ts +0 -10
  196. package/types/ngwr-color-picker.d.ts +0 -21
  197. package/types/ngwr-dropdown.d.ts +0 -52
  198. package/types/ngwr-editor.d.ts +2 -84
  199. package/types/ngwr-i18n.d.ts +0 -15
  200. package/types/ngwr-mention.d.ts +0 -13
  201. package/types/ngwr-popconfirm.d.ts +0 -33
  202. package/types/ngwr-popover.d.ts +0 -51
  203. package/types/ngwr-tour.d.ts +0 -2
  204. package/types/ngwr-utils.d.ts +49 -7
  205. package/types/ngwr-version.d.ts +1 -1
  206. package/types/ngwr-window.d.ts +0 -56
@@ -47,6 +47,9 @@ var WrDotGrid = class WrDotGrid {
47
47
  });
48
48
  pitch = computed(() => Math.max(MIN_GAP, this.gap()), ...ngDevMode ? [{ debugName: "pitch" }] : /* istanbul ignore next */ []);
49
49
  size = computed(() => Math.max(0, this.dotSize()), ...ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []);
50
+ standInColor = computed(() => (this.dotColor() ?? "").trim() || null, ...ngDevMode ? [{ debugName: "standInColor" }] : /* istanbul ignore next */ []);
51
+ dotExpression = computed(() => (this.dotColor() ?? "").trim() || "var(--wr-dot-grid-dot-color)", ...ngDevMode ? [{ debugName: "dotExpression" }] : /* istanbul ignore next */ []);
52
+ activeExpression = computed(() => (this.activeColor() ?? "").trim() || "var(--wr-dot-grid-active-color)", ...ngDevMode ? [{ debugName: "activeExpression" }] : /* istanbul ignore next */ []);
50
53
  painted = signal(false, ...ngDevMode ? [{ debugName: "painted" }] : /* istanbul ignore next */ []);
51
54
  canvasRef = viewChild.required("canvas", ...ngDevMode ? [{ debugName: "canvasRef" }] : /* istanbul ignore next */ []);
52
55
  host = inject(ElementRef);
@@ -286,7 +289,7 @@ var WrDotGrid = class WrDotGrid {
286
289
  const r = this.size() / 2;
287
290
  ctx.clearRect(0, 0, this.box.width, this.box.height);
288
291
  ctx.globalAlpha = 1;
289
- ctx.fillStyle = resolveCssColor(canvas, this.dotColor() ?? "var(--wr-dot-grid-dot-color)", DOT_FALLBACK);
292
+ ctx.fillStyle = resolveCssColor(canvas, this.dotExpression(), DOT_FALLBACK);
290
293
  this.fillDots(ctx, this.dots, r);
291
294
  for (const level of this.lit) level.length = 0;
292
295
  let anyLit = false;
@@ -298,7 +301,7 @@ var WrDotGrid = class WrDotGrid {
298
301
  }
299
302
  }
300
303
  if (!anyLit) return;
301
- ctx.fillStyle = resolveCssColor(canvas, this.activeColor() ?? "var(--wr-dot-grid-active-color)", ACTIVE_FALLBACK);
304
+ ctx.fillStyle = resolveCssColor(canvas, this.activeExpression(), ACTIVE_FALLBACK);
302
305
  this.lit.forEach((dots, i) => {
303
306
  if (dots.length === 0) return;
304
307
  ctx.globalAlpha = (i + 1) / GLOW_LEVELS;
@@ -318,7 +321,7 @@ var WrDotGrid = class WrDotGrid {
318
321
  }
319
322
  static ɵfac = i0.ɵɵngDeclareFactory({
320
323
  minVersion: "12.0.0",
321
- version: "22.2.3",
324
+ version: "22.2.4",
322
325
  ngImport: i0,
323
326
  type: WrDotGrid,
324
327
  deps: [],
@@ -326,7 +329,7 @@ var WrDotGrid = class WrDotGrid {
326
329
  });
327
330
  static ɵcmp = i0.ɵɵngDeclareComponent({
328
331
  minVersion: "17.2.0",
329
- version: "22.2.3",
332
+ version: "22.2.4",
330
333
  type: WrDotGrid,
331
334
  isStandalone: true,
332
335
  selector: "wr-dot-grid",
@@ -378,7 +381,7 @@ var WrDotGrid = class WrDotGrid {
378
381
  properties: {
379
382
  "style.--wr-dot-grid-gap.px": "pitch()",
380
383
  "style.--wr-dot-grid-dot-size.px": "size()",
381
- "style.--wr-dot-grid-dot-color": "dotColor()",
384
+ "style.--wr-dot-grid-input-dot-color": "standInColor()",
382
385
  "class.wr-dot-grid--painted": "painted()"
383
386
  },
384
387
  classAttribute: "wr-dot-grid"
@@ -393,13 +396,13 @@ var WrDotGrid = class WrDotGrid {
393
396
  ngImport: i0,
394
397
  template: "<canvas #canvas class=\"wr-dot-grid__canvas\" aria-hidden=\"true\"></canvas>",
395
398
  isInline: true,
396
- styles: [".wr-dot-grid{--wr-dot-grid-dot-color: var(--wr-border-strong);--wr-dot-grid-active-color: var(--wr-color-primary);--wr-dot-grid-gap: 24px;--wr-dot-grid-dot-size: 3px;position:absolute;inset:0;z-index:0;display:block;overflow:hidden;pointer-events:none;background-image:radial-gradient(circle,var(--wr-dot-grid-dot-color) calc(var(--wr-dot-grid-dot-size) / 2 - .25px),transparent calc(var(--wr-dot-grid-dot-size) / 2 + .25px));background-position:center;background-size:var(--wr-dot-grid-gap) var(--wr-dot-grid-gap)}.wr-dot-grid--painted{background-image:none}.wr-dot-grid__canvas{display:block;width:100%;height:100%}\n"],
399
+ styles: [".wr-dot-grid{--wr-dot-grid-dot-color: var(--wr-border-strong);--wr-dot-grid-active-color: var(--wr-color-primary);--wr-dot-grid-gap: 24px;--wr-dot-grid-dot-size: 3px;position:absolute;inset:0;z-index:0;display:block;overflow:hidden;pointer-events:none;background-image:radial-gradient(circle,var(--wr-dot-grid-input-dot-color, var(--wr-dot-grid-dot-color)) calc(var(--wr-dot-grid-dot-size) / 2 - .25px),transparent calc(var(--wr-dot-grid-dot-size) / 2 + .25px));background-position:center;background-size:var(--wr-dot-grid-gap) var(--wr-dot-grid-gap)}.wr-dot-grid--painted{background-image:none}.wr-dot-grid__canvas{display:block;width:100%;height:100%}@media(forced-colors:active){.wr-dot-grid__canvas{forced-color-adjust:none}}\n"],
397
400
  encapsulation: i0.ViewEncapsulation.None
398
401
  });
399
402
  };
400
403
  i0.ɵɵngDeclareClassMetadata({
401
404
  minVersion: "12.0.0",
402
- version: "22.2.3",
405
+ version: "22.2.4",
403
406
  ngImport: i0,
404
407
  type: WrDotGrid,
405
408
  decorators: [{
@@ -412,10 +415,10 @@ i0.ɵɵngDeclareClassMetadata({
412
415
  class: "wr-dot-grid",
413
416
  "[style.--wr-dot-grid-gap.px]": "pitch()",
414
417
  "[style.--wr-dot-grid-dot-size.px]": "size()",
415
- "[style.--wr-dot-grid-dot-color]": "dotColor()",
418
+ "[style.--wr-dot-grid-input-dot-color]": "standInColor()",
416
419
  "[class.wr-dot-grid--painted]": "painted()"
417
420
  },
418
- styles: [".wr-dot-grid{--wr-dot-grid-dot-color: var(--wr-border-strong);--wr-dot-grid-active-color: var(--wr-color-primary);--wr-dot-grid-gap: 24px;--wr-dot-grid-dot-size: 3px;position:absolute;inset:0;z-index:0;display:block;overflow:hidden;pointer-events:none;background-image:radial-gradient(circle,var(--wr-dot-grid-dot-color) calc(var(--wr-dot-grid-dot-size) / 2 - .25px),transparent calc(var(--wr-dot-grid-dot-size) / 2 + .25px));background-position:center;background-size:var(--wr-dot-grid-gap) var(--wr-dot-grid-gap)}.wr-dot-grid--painted{background-image:none}.wr-dot-grid__canvas{display:block;width:100%;height:100%}\n"]
421
+ styles: [".wr-dot-grid{--wr-dot-grid-dot-color: var(--wr-border-strong);--wr-dot-grid-active-color: var(--wr-color-primary);--wr-dot-grid-gap: 24px;--wr-dot-grid-dot-size: 3px;position:absolute;inset:0;z-index:0;display:block;overflow:hidden;pointer-events:none;background-image:radial-gradient(circle,var(--wr-dot-grid-input-dot-color, var(--wr-dot-grid-dot-color)) calc(var(--wr-dot-grid-dot-size) / 2 - .25px),transparent calc(var(--wr-dot-grid-dot-size) / 2 + .25px));background-position:center;background-size:var(--wr-dot-grid-gap) var(--wr-dot-grid-gap)}.wr-dot-grid--painted{background-image:none}.wr-dot-grid__canvas{display:block;width:100%;height:100%}@media(forced-colors:active){.wr-dot-grid__canvas{forced-color-adjust:none}}\n"]
419
422
  }]
420
423
  }],
421
424
  ctorParameters: () => [],
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-bits-dot-grid.mjs","names":[],"sources":["../../../projects/lib/bits/dot-grid/dot-grid.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 * After the Dot Grid background on reactbits.dev\n * (https://www.reactbits.dev/backgrounds/dot-grid). The effect is the same\n * idea; the code is ngwr's own: a 2D canvas, one spring per dot, and a frame\n * loop that sleeps whenever nothing moves.\n */\n\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n ElementRef,\n NgZone,\n PLATFORM_ID,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { numAttr, resolveCssColor, watchColorScheme } from 'ngwr/utils';\n\n/** One dot: where the grid puts it, how far it has been pushed, and how lit it is. */\ninterface Dot {\n readonly x: number;\n readonly y: number;\n /** Offset from rest, in CSS pixels. */\n ox: number;\n oy: number;\n /** Velocity of that offset, in CSS pixels per 60 Hz frame. */\n vx: number;\n vy: number;\n /** How far toward the active colour, 0..1. */\n glow: number;\n}\n\n/**\n * The most bitmap pixels the canvas is backed with, the bound `wr-waves`\n * uses: a hero keeps the full 2x, and a very wide one steps down toward 1x\n * rather than growing a bitmap every frame has to clear and fill.\n */\nconst MAX_CANVAS_PIXELS = 2_500_000;\n\n/**\n * The smallest pitch the grid is laid out at. A 1px gap on a full-page host\n * is two million dots, which no frame can draw; 4px is already a fine mesh.\n */\nconst MIN_GAP = 4;\n\n/** A 60 Hz frame, in ms — the unit the spring constants below are tuned in. */\nconst FRAME_MS = 1000 / 60;\n\n/** The longest step one frame may take, so a tab that wakes after a pause does not fling the dots. */\nconst MAX_STEP = 3;\n\n/** Spring pull back toward the target offset, per frame. */\nconst SPRING = 0.1;\n\n/** Velocity a dot keeps from one frame to the next; under 1 is what lets it settle. */\nconst DAMPING = 0.8;\n\n/** Share of the gap to its target a dot's glow closes per frame while it brightens. */\nconst GLOW_RISE = 0.3;\n\n/** The same while it fades — slower, so the pointer leaves a short trail behind it. */\nconst GLOW_FALL = 0.06;\n\n/** Below this, in pixels or pixels per frame, a dot counts as at rest. */\nconst REST = 0.01;\n\n/** Below this a glow counts as settled: under one step of an 8-bit alpha. */\nconst GLOW_REST = 1 / 256;\n\n/** Alpha steps the lit dots are batched into, one path and one fill each. */\nconst GLOW_LEVELS = 16;\n\n/** What the dots fall back to when the colour cannot be read: `--wr-border-strong` in light. */\nconst DOT_FALLBACK = 'rgb(156, 163, 175)';\n\n/** What the lit dots fall back to: the shipped primary. */\nconst ACTIVE_FALLBACK = 'rgb(57, 105, 226)';\n\nconst TAU = Math.PI * 2;\n\n/**\n * Dot-grid background — evenly spaced dots on a 2D canvas that light up in the\n * active colour near the pointer and are pushed away from it, both by the same\n * falloff, then spring back. Fills its nearest positioned ancestor; put it\n * behind hero copy or section content.\n *\n * The pointer is followed on the host's PARENT, with passive listeners, so the\n * dots answer a pointer that is over the content in front of them rather than\n * over the canvas — the host itself takes no pointer events and never steals a\n * click. Placed directly in a shadow root, where it has no parent element, it\n * follows the pointer on the shadow host; with neither it listens nowhere and\n * the grid stays unlit. Content placed after it needs `position: relative` (or\n * any z-index) to paint above it, as with any absolutely positioned layer.\n *\n * The grid is laid out in the host's own box, before transforms, so one that\n * boots inside a scaling-in panel (a dialog opening) is not stretched when the\n * animation ends, and the pointer is mapped back through the transform.\n *\n * Decorative: the canvas is `aria-hidden`, the host has no role and nothing in\n * it takes focus. The frame loop runs only while something moves — a still\n * grid asks for no frames at all — and never while the host is off screen.\n *\n * Under `prefers-reduced-motion` one still grid is drawn, in the resting\n * colour, and nothing follows the pointer; it is drawn again when the colours\n * or the page's colour scheme change. The preference is read live.\n *\n * Colours are theme tokens by default — the dots `--wr-border-strong`, the lit\n * ones `--wr-color-primary` — through the `--wr-dot-grid-dot-color` and\n * `--wr-dot-grid-active-color` hooks, and resolved before they reach the\n * canvas, which reads neither a `var()` nor a `light-dark()`.\n *\n * @example\n * ```html\n * <section style=\"position: relative; min-height: 24rem; overflow: hidden\">\n * <wr-dot-grid />\n * <h1 style=\"position: relative\">Hero copy above the dots</h1>\n * </section>\n * ```\n *\n * @see https://ngwr.dev/bits/dot-grid\n */\n@Component({\n selector: 'wr-dot-grid',\n template: '<canvas #canvas class=\"wr-dot-grid__canvas\" aria-hidden=\"true\"></canvas>',\n styleUrl: './dot-grid.scss',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-dot-grid',\n // Feed the CSS stand-in grid (see `styles/_rules.scss`) so it draws at the\n // real pitch, size and colour, and switch it off once the canvas has painted.\n '[style.--wr-dot-grid-gap.px]': 'pitch()',\n '[style.--wr-dot-grid-dot-size.px]': 'size()',\n '[style.--wr-dot-grid-dot-color]': 'dotColor()',\n '[class.wr-dot-grid--painted]': 'painted()',\n },\n})\nexport class WrDotGrid {\n /** Dot diameter in px. @default 3 */\n readonly dotSize = input(3, { transform: numAttr(3) });\n\n /** Distance between dot centres in px, never under 4. @default 24 */\n readonly gap = input(24, { transform: numAttr(24) });\n\n /** Resting dot colour, any CSS colour. When unset, `--wr-dot-grid-dot-color` decides. */\n readonly dotColor = input<string | null>(null);\n\n /** Colour a dot takes near the pointer, any CSS colour. When unset, `--wr-dot-grid-active-color` decides. */\n readonly activeColor = input<string | null>(null);\n\n /** How far from the pointer a dot still reacts, in px. @default 120 */\n readonly radius = input(120, { transform: numAttr(120) });\n\n /**\n * How far a dot right by the pointer is pushed, in px; the push fades out\n * toward `radius`. `0` keeps the dots in place and only lights them, and a\n * negative value pulls them in. @default 12\n */\n readonly strength = input(12, { transform: numAttr(12) });\n\n /** The pitch the grid is laid out at: `gap`, floored. */\n protected readonly pitch = computed(() => Math.max(MIN_GAP, this.gap()));\n\n /** The dot diameter drawn: `dotSize`, never negative. */\n protected readonly size = computed(() => Math.max(0, this.dotSize()));\n\n /**\n * Flips once the canvas has drawn, which retires the CSS stand-in grid.\n *\n * A canvas cannot be prerendered, so on an SSR'd page it is blank until the\n * bundle has loaded and hydrated. The stylesheet paints the same dots at the\n * same pitch from the prerendered HTML in the meantime, and this hands over.\n */\n protected readonly painted = signal(false);\n\n private readonly canvasRef = viewChild.required<ElementRef<HTMLCanvasElement>>('canvas');\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly zone = inject(NgZone);\n private readonly destroyRef = inject(DestroyRef);\n private readonly platform = inject(WrPlatform);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n private ctx: CanvasRenderingContext2D | null = null;\n /** The host's layout box, in CSS pixels before any transform: what the grid is laid out in. */\n private box = { width: 0, height: 0 };\n /** Bitmap pixels per CSS pixel the canvas is backed at; 0 until the first fit. */\n private ratio = 0;\n private dots: Dot[] = [];\n /** Lit dots by alpha step, reused frame to frame. */\n private readonly lit: Dot[][] = Array.from({ length: GLOW_LEVELS }, () => []);\n /** The pointer, in viewport coordinates, and whether it is over the parent at all. */\n private readonly pointer = { clientX: 0, clientY: 0, inside: false };\n private rafId = 0;\n private lastTime = -1;\n /** Schedules a frame while motion is allowed; `null` under reduced motion. */\n private wake: (() => void) | null = null;\n\n /** Flips once `boot()` has a context and a grid — everything after waits for it. */\n private readonly booted = signal(false);\n\n constructor() {\n if (!this.isBrowser) return;\n\n afterNextRender(() => this.zone.runOutsideAngular(() => this.boot()));\n\n // A new pitch is a new grid. It leaves the bitmap alone, so the frame it\n // asks for repaints over the old grid rather than over a blank canvas.\n effect(() => {\n this.pitch();\n untracked(() => {\n if (!this.ctx) return;\n this.placeDots();\n this.requestDraw();\n });\n });\n\n // Everything else only changes what the next frame draws. A sleeping loop\n // or a still grid has no next frame, so ask for one.\n effect(() => {\n this.size();\n this.dotColor();\n this.activeColor();\n this.radius();\n this.strength();\n untracked(() => this.requestDraw());\n });\n\n // A drawn grid keeps the colours it was drawn in, and the loop sleeps when\n // nothing moves, so a scheme switch asks for a frame to re-resolve them.\n watchColorScheme(() => this.requestDraw());\n\n // Motion or a still grid, decided live.\n effect(onCleanup => {\n if (!this.booted()) return;\n if (this.platform.prefersReducedMotion()) {\n untracked(() => {\n this.rest();\n this.draw();\n });\n return;\n }\n onCleanup(untracked(() => this.zone.runOutsideAngular(() => this.interact())));\n });\n }\n\n // Setup\n\n private boot(): void {\n const canvas = this.canvasRef().nativeElement;\n this.ctx = canvas.getContext('2d');\n if (!this.ctx) return;\n\n // `clientWidth` and not `getBoundingClientRect()`: the rect is the box after\n // transforms, so a grid booted inside a dialog's scale-in would be laid out\n // for 96% of its box and stretched by CSS once the animation ended. The\n // observer's first entry follows with the exact, sub-pixel size.\n const hostEl = this.host.nativeElement;\n this.fit(hostEl.clientWidth, hostEl.clientHeight);\n this.placeDots();\n\n // The box is the observer's to follow, transforms excluded. A window resize\n // is for the pixel ratio, which browser zoom and a move to another screen\n // change with no box change at all.\n const onResize = (): void => this.resize(this.box.width, this.box.height);\n window.addEventListener('resize', onResize);\n const ro = new ResizeObserver(entries => {\n const rect = entries[entries.length - 1]?.contentRect;\n if (rect) this.resize(rect.width, rect.height);\n });\n ro.observe(hostEl);\n this.destroyRef.onDestroy(() => {\n window.removeEventListener('resize', onResize);\n ro.disconnect();\n });\n this.booted.set(true);\n }\n\n /** Follow the pointer and draw on demand; returns what stops both. */\n private interact(): () => void {\n const hostEl = this.host.nativeElement;\n // The parent, not the host: content in front of the canvas receives the\n // pointer, and its events bubble to the parent, never to a sibling. Placed\n // directly in a shadow root it has none, and the shadow host is where those\n // composed events land. Never the host itself, which takes no pointer\n // events and so could never hear one.\n const root = hostEl.getRootNode();\n const target =\n hostEl.parentElement ?? (root instanceof ShadowRoot && root.host instanceof HTMLElement ? root.host : null);\n\n // Off screen the grid asks for no frames. Without an observer (jsdom) it\n // counts as visible.\n let visible = typeof IntersectionObserver === 'undefined';\n const wake = (): void => {\n if (visible && this.rafId === 0) this.rafId = requestAnimationFrame(time => this.tick(time));\n };\n const halt = (): void => {\n cancelAnimationFrame(this.rafId);\n this.rafId = 0;\n this.lastTime = -1;\n };\n\n const onMove = (event: PointerEvent): void => {\n this.pointer.clientX = event.clientX;\n this.pointer.clientY = event.clientY;\n this.pointer.inside = true;\n wake();\n };\n const onLeave = (): void => {\n this.pointer.inside = false;\n wake();\n };\n target?.addEventListener('pointermove', onMove, { passive: true });\n target?.addEventListener('pointerdown', onMove, { passive: true });\n target?.addEventListener('pointerleave', onLeave, { passive: true });\n target?.addEventListener('pointercancel', onLeave, { passive: true });\n\n const io =\n typeof IntersectionObserver === 'undefined'\n ? null\n : new IntersectionObserver(entries => {\n visible = entries[entries.length - 1].isIntersecting;\n if (visible) wake();\n else halt();\n });\n io?.observe(hostEl);\n\n this.wake = wake;\n wake();\n\n return () => {\n target?.removeEventListener('pointermove', onMove);\n target?.removeEventListener('pointerdown', onMove);\n target?.removeEventListener('pointerleave', onLeave);\n target?.removeEventListener('pointercancel', onLeave);\n io?.disconnect();\n halt();\n this.wake = null;\n this.pointer.inside = false;\n };\n }\n\n /** Draw the grid as it should look now: a frame while motion is allowed, at once under reduced motion. */\n private requestDraw(): void {\n if (!this.booted()) return;\n if (this.wake) this.wake();\n else if (this.platform.prefersReducedMotion()) this.draw();\n }\n\n /** Fit the canvas to a new box or pixel ratio and, when that cleared it, repaint at once. */\n private resize(width: number, height: number): void {\n const moved = width !== this.box.width || height !== this.box.height;\n if (!this.fit(width, height)) return;\n if (moved) this.placeDots();\n // Observer entries arrive after the frame's own callbacks and before its\n // paint, so a frame asked for here would leave this one blank — with the\n // stand-in already retired, a resize drag would flicker every frame.\n this.draw();\n // Re-placed dots are at rest and unlit, which is right unless the pointer\n // is over them; a running loop simply carries on.\n if (this.pointer.inside) this.wake?.();\n }\n\n /**\n * Back the canvas for a `width` × `height` box, in CSS pixels, at the screen's\n * pixel ratio. Returns whether anything changed: writing `canvas.width` clears\n * the bitmap even to the value it already has, so an unchanged box is left alone.\n */\n private fit(width: number, height: number): boolean {\n // Capped at 2 and by the pixel budget, never below 1 — a bitmap the size of\n // the CSS box blurs every dot on a 2x screen.\n const area = width * height;\n const budget = area > 0 ? Math.sqrt(MAX_CANVAS_PIXELS / area) : 2;\n const ratio = Math.max(1, Math.min(window.devicePixelRatio || 1, 2, budget));\n if (width === this.box.width && height === this.box.height && ratio === this.ratio) return false;\n\n this.box = { width, height };\n this.ratio = ratio;\n const canvas = this.canvasRef().nativeElement;\n canvas.width = Math.round(width * ratio);\n canvas.height = Math.round(height * ratio);\n // Resizing the bitmap resets the transform, so the scale goes on after it.\n // Every coordinate the draw uses is in CSS pixels.\n this.ctx?.setTransform(ratio, 0, 0, ratio, 0, 0);\n return true;\n }\n\n /** Lay the dots out over the box, every one at rest. */\n private placeDots(): void {\n // One dot sits on the centre of the box and the rest step out from it by\n // the pitch — exactly where the stand-in's centred `background-position`\n // puts them, so the handover does not shift a single dot. One ring past\n // each edge, for the dots a push brings into view.\n const gap = this.pitch();\n const xs = this.axis(this.box.width, gap);\n const ys = this.axis(this.box.height, gap);\n const dots: Dot[] = [];\n for (const y of ys) {\n for (const x of xs) dots.push({ x, y, ox: 0, oy: 0, vx: 0, vy: 0, glow: 0 });\n }\n this.dots = dots;\n }\n\n /** Positions along one side of `length`: the centre, then every `gap` out to one step past each edge. */\n private axis(length: number, gap: number): number[] {\n const centre = length / 2;\n const steps = Math.ceil(centre / gap) + 1;\n const positions: number[] = [];\n for (let i = -steps; i <= steps; i++) positions.push(centre + i * gap);\n return positions;\n }\n\n /** Every dot back at rest and unlit, and the pointer gone. */\n private rest(): void {\n this.pointer.inside = false;\n for (const dot of this.dots) {\n dot.ox = dot.oy = dot.vx = dot.vy = dot.glow = 0;\n }\n }\n\n // Frame loop\n\n private tick(time: number): void {\n this.rafId = 0;\n // The springs are tuned per 60 Hz frame and stepped by the time that really\n // passed, so a 120 Hz screen does not run them twice as fast. A timestamp\n // that did not advance counts as one frame.\n const elapsed = time - this.lastTime;\n const step = this.lastTime < 0 || elapsed <= 0 ? 1 : Math.min(MAX_STEP, elapsed / FRAME_MS);\n this.lastTime = time;\n\n const moving = this.move(step);\n this.draw();\n\n // Nothing left to move: sleep until the pointer, a resize or a new input\n // asks for another frame.\n if (moving && this.wake) this.rafId = requestAnimationFrame(next => this.tick(next));\n else this.lastTime = -1;\n }\n\n /** Advance every dot by `step` frames; returns whether any is still moving. */\n private move(step: number): boolean {\n const reach = Math.max(0, this.radius());\n const push = this.strength();\n const active = this.pointer.inside && reach > 0;\n\n let px = 0;\n let py = 0;\n if (active) {\n // Read once a frame rather than once a move: a page that scrolls under a\n // still pointer moves the grid under it too. The rect is the box as\n // drawn, after any transform on it or an ancestor, and the grid is laid\n // out in the box before them, so the pointer is scaled back into it.\n const rect = this.host.nativeElement.getBoundingClientRect();\n px = (this.pointer.clientX - rect.left) * (rect.width > 0 ? this.box.width / rect.width : 1);\n py = (this.pointer.clientY - rect.top) * (rect.height > 0 ? this.box.height / rect.height : 1);\n }\n\n const reach2 = reach * reach;\n const pull = SPRING * step;\n const keep = Math.pow(DAMPING, step);\n const rise = 1 - Math.pow(1 - GLOW_RISE, step);\n const fall = 1 - Math.pow(1 - GLOW_FALL, step);\n\n let moving = false;\n for (const dot of this.dots) {\n let tx = 0;\n let ty = 0;\n let glow = 0;\n if (active) {\n const dx = dot.x - px;\n const dy = dot.y - py;\n const d2 = dx * dx + dy * dy;\n if (d2 < reach2) {\n const d = Math.sqrt(d2);\n const u = 1 - d / reach;\n // Smoothstep: no edge where the radius ends, and a flat top under the pointer.\n glow = u * u * (3 - 2 * u);\n if (d > 0) {\n tx = (dx / d) * push * glow;\n ty = (dy / d) * push * glow;\n }\n }\n }\n\n dot.vx = (dot.vx + (tx - dot.ox) * pull) * keep;\n dot.vy = (dot.vy + (ty - dot.oy) * pull) * keep;\n dot.ox += dot.vx * step;\n dot.oy += dot.vy * step;\n dot.glow += (glow - dot.glow) * (glow > dot.glow ? rise : fall);\n\n if (\n !moving &&\n (Math.abs(tx - dot.ox) > REST ||\n Math.abs(ty - dot.oy) > REST ||\n Math.abs(dot.vx) > REST ||\n Math.abs(dot.vy) > REST ||\n Math.abs(glow - dot.glow) > GLOW_REST)\n ) {\n moving = true;\n }\n }\n return moving;\n }\n\n private draw(): void {\n const ctx = this.ctx;\n if (!ctx) return;\n // Retire the stand-in only once something is really painted. Runs outside\n // Angular, so the `set` schedules the one host-binding update it needs.\n if (!this.painted()) this.painted.set(true);\n\n const canvas = this.canvasRef().nativeElement;\n const r = this.size() / 2;\n ctx.clearRect(0, 0, this.box.width, this.box.height);\n ctx.globalAlpha = 1;\n ctx.fillStyle = resolveCssColor(canvas, this.dotColor() ?? 'var(--wr-dot-grid-dot-color)', DOT_FALLBACK);\n this.fillDots(ctx, this.dots, r);\n\n // Lit dots go over their resting selves in the active colour, at their\n // glow as alpha: the blend of the two colours without parsing either, so\n // an `oklch()` or a `display-p3` colour works as well as an `rgb()`.\n for (const level of this.lit) level.length = 0;\n let anyLit = false;\n for (const dot of this.dots) {\n const level = Math.round(dot.glow * GLOW_LEVELS);\n if (level > 0) {\n this.lit[Math.min(level, GLOW_LEVELS) - 1].push(dot);\n anyLit = true;\n }\n }\n if (!anyLit) return;\n\n ctx.fillStyle = resolveCssColor(canvas, this.activeColor() ?? 'var(--wr-dot-grid-active-color)', ACTIVE_FALLBACK);\n this.lit.forEach((dots, i) => {\n if (dots.length === 0) return;\n ctx.globalAlpha = (i + 1) / GLOW_LEVELS;\n this.fillDots(ctx, dots, r);\n });\n ctx.globalAlpha = 1;\n }\n\n /** One path for every dot in `dots`, filled once. */\n private fillDots(ctx: CanvasRenderingContext2D, dots: readonly Dot[], r: number): void {\n ctx.beginPath();\n for (const dot of dots) {\n const x = dot.x + dot.ox;\n const y = dot.y + dot.oy;\n ctx.moveTo(x + r, y);\n ctx.arc(x, y, r, 0, TAU);\n }\n ctx.fill();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-bits-dot-grid.mjs","names":[],"sources":["../../../projects/lib/bits/dot-grid/dot-grid.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 * After the Dot Grid background on reactbits.dev\n * (https://www.reactbits.dev/backgrounds/dot-grid). The effect is the same\n * idea; the code is ngwr's own: a 2D canvas, one spring per dot, and a frame\n * loop that sleeps whenever nothing moves.\n */\n\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n ElementRef,\n NgZone,\n PLATFORM_ID,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { numAttr, resolveCssColor, watchColorScheme } from 'ngwr/utils';\n\n/** One dot: where the grid puts it, how far it has been pushed, and how lit it is. */\ninterface Dot {\n readonly x: number;\n readonly y: number;\n /** Offset from rest, in CSS pixels. */\n ox: number;\n oy: number;\n /** Velocity of that offset, in CSS pixels per 60 Hz frame. */\n vx: number;\n vy: number;\n /** How far toward the active colour, 0..1. */\n glow: number;\n}\n\n/**\n * The most bitmap pixels the canvas is backed with, the bound `wr-waves`\n * uses: a hero keeps the full 2x, and a very wide one steps down toward 1x\n * rather than growing a bitmap every frame has to clear and fill.\n */\nconst MAX_CANVAS_PIXELS = 2_500_000;\n\n/**\n * The smallest pitch the grid is laid out at. A 1px gap on a full-page host\n * is two million dots, which no frame can draw; 4px is already a fine mesh.\n */\nconst MIN_GAP = 4;\n\n/** A 60 Hz frame, in ms — the unit the spring constants below are tuned in. */\nconst FRAME_MS = 1000 / 60;\n\n/** The longest step one frame may take, so a tab that wakes after a pause does not fling the dots. */\nconst MAX_STEP = 3;\n\n/** Spring pull back toward the target offset, per frame. */\nconst SPRING = 0.1;\n\n/** Velocity a dot keeps from one frame to the next; under 1 is what lets it settle. */\nconst DAMPING = 0.8;\n\n/** Share of the gap to its target a dot's glow closes per frame while it brightens. */\nconst GLOW_RISE = 0.3;\n\n/** The same while it fades — slower, so the pointer leaves a short trail behind it. */\nconst GLOW_FALL = 0.06;\n\n/** Below this, in pixels or pixels per frame, a dot counts as at rest. */\nconst REST = 0.01;\n\n/** Below this a glow counts as settled: under one step of an 8-bit alpha. */\nconst GLOW_REST = 1 / 256;\n\n/** Alpha steps the lit dots are batched into, one path and one fill each. */\nconst GLOW_LEVELS = 16;\n\n/** What the dots fall back to when the colour cannot be read: `--wr-border-strong` in light. */\nconst DOT_FALLBACK = 'rgb(156, 163, 175)';\n\n/** What the lit dots fall back to: the shipped primary. */\nconst ACTIVE_FALLBACK = 'rgb(57, 105, 226)';\n\nconst TAU = Math.PI * 2;\n\n/**\n * Dot-grid background — evenly spaced dots on a 2D canvas that light up in the\n * active colour near the pointer and are pushed away from it, both by the same\n * falloff, then spring back. Fills its nearest positioned ancestor; put it\n * behind hero copy or section content.\n *\n * The pointer is followed on the host's PARENT, with passive listeners, so the\n * dots answer a pointer that is over the content in front of them rather than\n * over the canvas — the host itself takes no pointer events and never steals a\n * click. Placed directly in a shadow root, where it has no parent element, it\n * follows the pointer on the shadow host; with neither it listens nowhere and\n * the grid stays unlit. Content placed after it needs `position: relative` (or\n * any z-index) to paint above it, as with any absolutely positioned layer.\n *\n * The grid is laid out in the host's own box, before transforms, so one that\n * boots inside a scaling-in panel (a dialog opening) is not stretched when the\n * animation ends, and the pointer is mapped back through the transform.\n *\n * Decorative: the canvas is `aria-hidden`, the host has no role and nothing in\n * it takes focus. The frame loop runs only while something moves — a still\n * grid asks for no frames at all — and never while the host is off screen.\n *\n * Under `prefers-reduced-motion` one still grid is drawn, in the resting\n * colour, and nothing follows the pointer; it is drawn again when the colours\n * or the page's colour scheme change. The preference is read live.\n *\n * Colours are theme tokens by default — the dots `--wr-border-strong`, the lit\n * ones `--wr-color-primary` — through the `--wr-dot-grid-dot-color` and\n * `--wr-dot-grid-active-color` hooks, and resolved before they reach the\n * canvas, which reads neither a `var()` nor a `light-dark()`.\n *\n * @example\n * ```html\n * <section style=\"position: relative; min-height: 24rem; overflow: hidden\">\n * <wr-dot-grid />\n * <h1 style=\"position: relative\">Hero copy above the dots</h1>\n * </section>\n * ```\n *\n * @see https://ngwr.dev/bits/dot-grid\n */\n@Component({\n selector: 'wr-dot-grid',\n template: '<canvas #canvas class=\"wr-dot-grid__canvas\" aria-hidden=\"true\"></canvas>',\n styleUrl: './dot-grid.scss',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-dot-grid',\n // Feed the CSS stand-in grid (see `styles/_rules.scss`) so it draws at the\n // real pitch, size and colour, and switch it off once the canvas has painted.\n '[style.--wr-dot-grid-gap.px]': 'pitch()',\n '[style.--wr-dot-grid-dot-size.px]': 'size()',\n '[style.--wr-dot-grid-input-dot-color]': 'standInColor()',\n '[class.wr-dot-grid--painted]': 'painted()',\n },\n})\nexport class WrDotGrid {\n /** Dot diameter in px. @default 3 */\n readonly dotSize = input(3, { transform: numAttr(3) });\n\n /** Distance between dot centres in px, never under 4. @default 24 */\n readonly gap = input(24, { transform: numAttr(24) });\n\n /** Resting dot colour, any CSS colour. When unset, empty or blank, `--wr-dot-grid-dot-color` decides. */\n readonly dotColor = input<string | null>(null);\n\n /** Colour a dot takes near the pointer, any CSS colour. When unset, empty or blank, `--wr-dot-grid-active-color` decides. */\n readonly activeColor = input<string | null>(null);\n\n /** How far from the pointer a dot still reacts, in px. @default 120 */\n readonly radius = input(120, { transform: numAttr(120) });\n\n /**\n * How far a dot right by the pointer is pushed, in px; the push fades out\n * toward `radius`. `0` keeps the dots in place and only lights them, and a\n * negative value pulls them in. @default 12\n */\n readonly strength = input(12, { transform: numAttr(12) });\n\n /** The pitch the grid is laid out at: `gap`, floored. */\n protected readonly pitch = computed(() => Math.max(MIN_GAP, this.gap()));\n\n /** The dot diameter drawn: `dotSize`, never negative. */\n protected readonly size = computed(() => Math.max(0, this.dotSize()));\n\n /** A colour the author chose, published so the stand-in draws in it too; `null` while the hook decides. */\n protected readonly standInColor = computed(() => (this.dotColor() ?? '').trim() || null);\n\n /** The colours the canvas resolves: an input that is unset, empty or blank leaves the hook in charge. */\n private readonly dotExpression = computed(() => (this.dotColor() ?? '').trim() || 'var(--wr-dot-grid-dot-color)');\n private readonly activeExpression = computed(\n () => (this.activeColor() ?? '').trim() || 'var(--wr-dot-grid-active-color)'\n );\n\n /**\n * Flips once the canvas has drawn, which retires the CSS stand-in grid.\n *\n * A canvas cannot be prerendered, so on an SSR'd page it is blank until the\n * bundle has loaded and hydrated. The stylesheet paints the same dots at the\n * same pitch from the prerendered HTML in the meantime, and this hands over.\n */\n protected readonly painted = signal(false);\n\n private readonly canvasRef = viewChild.required<ElementRef<HTMLCanvasElement>>('canvas');\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly zone = inject(NgZone);\n private readonly destroyRef = inject(DestroyRef);\n private readonly platform = inject(WrPlatform);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n private ctx: CanvasRenderingContext2D | null = null;\n /** The host's layout box, in CSS pixels before any transform: what the grid is laid out in. */\n private box = { width: 0, height: 0 };\n /** Bitmap pixels per CSS pixel the canvas is backed at; 0 until the first fit. */\n private ratio = 0;\n private dots: Dot[] = [];\n /** Lit dots by alpha step, reused frame to frame. */\n private readonly lit: Dot[][] = Array.from({ length: GLOW_LEVELS }, () => []);\n /** The pointer, in viewport coordinates, and whether it is over the parent at all. */\n private readonly pointer = { clientX: 0, clientY: 0, inside: false };\n private rafId = 0;\n private lastTime = -1;\n /** Schedules a frame while motion is allowed; `null` under reduced motion. */\n private wake: (() => void) | null = null;\n\n /** Flips once `boot()` has a context and a grid — everything after waits for it. */\n private readonly booted = signal(false);\n\n constructor() {\n if (!this.isBrowser) return;\n\n afterNextRender(() => this.zone.runOutsideAngular(() => this.boot()));\n\n // A new pitch is a new grid. It leaves the bitmap alone, so the frame it\n // asks for repaints over the old grid rather than over a blank canvas.\n effect(() => {\n this.pitch();\n untracked(() => {\n if (!this.ctx) return;\n this.placeDots();\n this.requestDraw();\n });\n });\n\n // Everything else only changes what the next frame draws. A sleeping loop\n // or a still grid has no next frame, so ask for one.\n effect(() => {\n this.size();\n this.dotColor();\n this.activeColor();\n this.radius();\n this.strength();\n untracked(() => this.requestDraw());\n });\n\n // A drawn grid keeps the colours it was drawn in, and the loop sleeps when\n // nothing moves, so a scheme switch asks for a frame to re-resolve them.\n watchColorScheme(() => this.requestDraw());\n\n // Motion or a still grid, decided live.\n effect(onCleanup => {\n if (!this.booted()) return;\n if (this.platform.prefersReducedMotion()) {\n untracked(() => {\n this.rest();\n this.draw();\n });\n return;\n }\n onCleanup(untracked(() => this.zone.runOutsideAngular(() => this.interact())));\n });\n }\n\n // Setup\n\n private boot(): void {\n const canvas = this.canvasRef().nativeElement;\n this.ctx = canvas.getContext('2d');\n if (!this.ctx) return;\n\n // `clientWidth` and not `getBoundingClientRect()`: the rect is the box after\n // transforms, so a grid booted inside a dialog's scale-in would be laid out\n // for 96% of its box and stretched by CSS once the animation ended. The\n // observer's first entry follows with the exact, sub-pixel size.\n const hostEl = this.host.nativeElement;\n this.fit(hostEl.clientWidth, hostEl.clientHeight);\n this.placeDots();\n\n // The box is the observer's to follow, transforms excluded. A window resize\n // is for the pixel ratio, which browser zoom and a move to another screen\n // change with no box change at all.\n const onResize = (): void => this.resize(this.box.width, this.box.height);\n window.addEventListener('resize', onResize);\n const ro = new ResizeObserver(entries => {\n const rect = entries[entries.length - 1]?.contentRect;\n if (rect) this.resize(rect.width, rect.height);\n });\n ro.observe(hostEl);\n this.destroyRef.onDestroy(() => {\n window.removeEventListener('resize', onResize);\n ro.disconnect();\n });\n this.booted.set(true);\n }\n\n /** Follow the pointer and draw on demand; returns what stops both. */\n private interact(): () => void {\n const hostEl = this.host.nativeElement;\n // The parent, not the host: content in front of the canvas receives the\n // pointer, and its events bubble to the parent, never to a sibling. Placed\n // directly in a shadow root it has none, and the shadow host is where those\n // composed events land. Never the host itself, which takes no pointer\n // events and so could never hear one.\n const root = hostEl.getRootNode();\n const target =\n hostEl.parentElement ?? (root instanceof ShadowRoot && root.host instanceof HTMLElement ? root.host : null);\n\n // Off screen the grid asks for no frames. Without an observer (jsdom) it\n // counts as visible.\n let visible = typeof IntersectionObserver === 'undefined';\n const wake = (): void => {\n if (visible && this.rafId === 0) this.rafId = requestAnimationFrame(time => this.tick(time));\n };\n const halt = (): void => {\n cancelAnimationFrame(this.rafId);\n this.rafId = 0;\n this.lastTime = -1;\n };\n\n const onMove = (event: PointerEvent): void => {\n this.pointer.clientX = event.clientX;\n this.pointer.clientY = event.clientY;\n this.pointer.inside = true;\n wake();\n };\n const onLeave = (): void => {\n this.pointer.inside = false;\n wake();\n };\n target?.addEventListener('pointermove', onMove, { passive: true });\n target?.addEventListener('pointerdown', onMove, { passive: true });\n target?.addEventListener('pointerleave', onLeave, { passive: true });\n target?.addEventListener('pointercancel', onLeave, { passive: true });\n\n const io =\n typeof IntersectionObserver === 'undefined'\n ? null\n : new IntersectionObserver(entries => {\n visible = entries[entries.length - 1].isIntersecting;\n if (visible) wake();\n else halt();\n });\n io?.observe(hostEl);\n\n this.wake = wake;\n wake();\n\n return () => {\n target?.removeEventListener('pointermove', onMove);\n target?.removeEventListener('pointerdown', onMove);\n target?.removeEventListener('pointerleave', onLeave);\n target?.removeEventListener('pointercancel', onLeave);\n io?.disconnect();\n halt();\n this.wake = null;\n this.pointer.inside = false;\n };\n }\n\n /** Draw the grid as it should look now: a frame while motion is allowed, at once under reduced motion. */\n private requestDraw(): void {\n if (!this.booted()) return;\n if (this.wake) this.wake();\n else if (this.platform.prefersReducedMotion()) this.draw();\n }\n\n /** Fit the canvas to a new box or pixel ratio and, when that cleared it, repaint at once. */\n private resize(width: number, height: number): void {\n const moved = width !== this.box.width || height !== this.box.height;\n if (!this.fit(width, height)) return;\n if (moved) this.placeDots();\n // Observer entries arrive after the frame's own callbacks and before its\n // paint, so a frame asked for here would leave this one blank — with the\n // stand-in already retired, a resize drag would flicker every frame.\n this.draw();\n // Re-placed dots are at rest and unlit, which is right unless the pointer\n // is over them; a running loop simply carries on.\n if (this.pointer.inside) this.wake?.();\n }\n\n /**\n * Back the canvas for a `width` × `height` box, in CSS pixels, at the screen's\n * pixel ratio. Returns whether anything changed: writing `canvas.width` clears\n * the bitmap even to the value it already has, so an unchanged box is left alone.\n */\n private fit(width: number, height: number): boolean {\n // Capped at 2 and by the pixel budget, never below 1 — a bitmap the size of\n // the CSS box blurs every dot on a 2x screen.\n const area = width * height;\n const budget = area > 0 ? Math.sqrt(MAX_CANVAS_PIXELS / area) : 2;\n const ratio = Math.max(1, Math.min(window.devicePixelRatio || 1, 2, budget));\n if (width === this.box.width && height === this.box.height && ratio === this.ratio) return false;\n\n this.box = { width, height };\n this.ratio = ratio;\n const canvas = this.canvasRef().nativeElement;\n canvas.width = Math.round(width * ratio);\n canvas.height = Math.round(height * ratio);\n // Resizing the bitmap resets the transform, so the scale goes on after it.\n // Every coordinate the draw uses is in CSS pixels.\n this.ctx?.setTransform(ratio, 0, 0, ratio, 0, 0);\n return true;\n }\n\n /** Lay the dots out over the box, every one at rest. */\n private placeDots(): void {\n // One dot sits on the centre of the box and the rest step out from it by\n // the pitch — exactly where the stand-in's centred `background-position`\n // puts them, so the handover does not shift a single dot. One ring past\n // each edge, for the dots a push brings into view.\n const gap = this.pitch();\n const xs = this.axis(this.box.width, gap);\n const ys = this.axis(this.box.height, gap);\n const dots: Dot[] = [];\n for (const y of ys) {\n for (const x of xs) dots.push({ x, y, ox: 0, oy: 0, vx: 0, vy: 0, glow: 0 });\n }\n this.dots = dots;\n }\n\n /** Positions along one side of `length`: the centre, then every `gap` out to one step past each edge. */\n private axis(length: number, gap: number): number[] {\n const centre = length / 2;\n const steps = Math.ceil(centre / gap) + 1;\n const positions: number[] = [];\n for (let i = -steps; i <= steps; i++) positions.push(centre + i * gap);\n return positions;\n }\n\n /** Every dot back at rest and unlit, and the pointer gone. */\n private rest(): void {\n this.pointer.inside = false;\n for (const dot of this.dots) {\n dot.ox = dot.oy = dot.vx = dot.vy = dot.glow = 0;\n }\n }\n\n // Frame loop\n\n private tick(time: number): void {\n this.rafId = 0;\n // The springs are tuned per 60 Hz frame and stepped by the time that really\n // passed, so a 120 Hz screen does not run them twice as fast. A timestamp\n // that did not advance counts as one frame.\n const elapsed = time - this.lastTime;\n const step = this.lastTime < 0 || elapsed <= 0 ? 1 : Math.min(MAX_STEP, elapsed / FRAME_MS);\n this.lastTime = time;\n\n const moving = this.move(step);\n this.draw();\n\n // Nothing left to move: sleep until the pointer, a resize or a new input\n // asks for another frame.\n if (moving && this.wake) this.rafId = requestAnimationFrame(next => this.tick(next));\n else this.lastTime = -1;\n }\n\n /** Advance every dot by `step` frames; returns whether any is still moving. */\n private move(step: number): boolean {\n const reach = Math.max(0, this.radius());\n const push = this.strength();\n const active = this.pointer.inside && reach > 0;\n\n let px = 0;\n let py = 0;\n if (active) {\n // Read once a frame rather than once a move: a page that scrolls under a\n // still pointer moves the grid under it too. The rect is the box as\n // drawn, after any transform on it or an ancestor, and the grid is laid\n // out in the box before them, so the pointer is scaled back into it.\n const rect = this.host.nativeElement.getBoundingClientRect();\n px = (this.pointer.clientX - rect.left) * (rect.width > 0 ? this.box.width / rect.width : 1);\n py = (this.pointer.clientY - rect.top) * (rect.height > 0 ? this.box.height / rect.height : 1);\n }\n\n const reach2 = reach * reach;\n const pull = SPRING * step;\n const keep = Math.pow(DAMPING, step);\n const rise = 1 - Math.pow(1 - GLOW_RISE, step);\n const fall = 1 - Math.pow(1 - GLOW_FALL, step);\n\n let moving = false;\n for (const dot of this.dots) {\n let tx = 0;\n let ty = 0;\n let glow = 0;\n if (active) {\n const dx = dot.x - px;\n const dy = dot.y - py;\n const d2 = dx * dx + dy * dy;\n if (d2 < reach2) {\n const d = Math.sqrt(d2);\n const u = 1 - d / reach;\n // Smoothstep: no edge where the radius ends, and a flat top under the pointer.\n glow = u * u * (3 - 2 * u);\n if (d > 0) {\n tx = (dx / d) * push * glow;\n ty = (dy / d) * push * glow;\n }\n }\n }\n\n dot.vx = (dot.vx + (tx - dot.ox) * pull) * keep;\n dot.vy = (dot.vy + (ty - dot.oy) * pull) * keep;\n dot.ox += dot.vx * step;\n dot.oy += dot.vy * step;\n dot.glow += (glow - dot.glow) * (glow > dot.glow ? rise : fall);\n\n if (\n !moving &&\n (Math.abs(tx - dot.ox) > REST ||\n Math.abs(ty - dot.oy) > REST ||\n Math.abs(dot.vx) > REST ||\n Math.abs(dot.vy) > REST ||\n Math.abs(glow - dot.glow) > GLOW_REST)\n ) {\n moving = true;\n }\n }\n return moving;\n }\n\n private draw(): void {\n const ctx = this.ctx;\n if (!ctx) return;\n // Retire the stand-in only once something is really painted. Runs outside\n // Angular, so the `set` schedules the one host-binding update it needs.\n if (!this.painted()) this.painted.set(true);\n\n const canvas = this.canvasRef().nativeElement;\n const r = this.size() / 2;\n ctx.clearRect(0, 0, this.box.width, this.box.height);\n ctx.globalAlpha = 1;\n ctx.fillStyle = resolveCssColor(canvas, this.dotExpression(), DOT_FALLBACK);\n this.fillDots(ctx, this.dots, r);\n\n // Lit dots go over their resting selves in the active colour, at their\n // glow as alpha: the blend of the two colours without parsing either, so\n // an `oklch()` or a `display-p3` colour works as well as an `rgb()`.\n for (const level of this.lit) level.length = 0;\n let anyLit = false;\n for (const dot of this.dots) {\n const level = Math.round(dot.glow * GLOW_LEVELS);\n if (level > 0) {\n this.lit[Math.min(level, GLOW_LEVELS) - 1].push(dot);\n anyLit = true;\n }\n }\n if (!anyLit) return;\n\n ctx.fillStyle = resolveCssColor(canvas, this.activeExpression(), ACTIVE_FALLBACK);\n this.lit.forEach((dots, i) => {\n if (dots.length === 0) return;\n ctx.globalAlpha = (i + 1) / GLOW_LEVELS;\n this.fillDots(ctx, dots, r);\n });\n ctx.globalAlpha = 1;\n }\n\n /** One path for every dot in `dots`, filled once. */\n private fillDots(ctx: CanvasRenderingContext2D, dots: readonly Dot[], r: number): void {\n ctx.beginPath();\n for (const dot of dots) {\n const x = dot.x + dot.ox;\n const y = dot.y + dot.oy;\n ctx.moveTo(x + r, y);\n ctx.arc(x, y, r, 0, TAU);\n }\n ctx.fill();\n }\n}\n"],"mappings":"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"}
@@ -281,7 +281,7 @@ var WrFallingText = class WrFallingText {
281
281
  }
282
282
  static ɵfac = i0.ɵɵngDeclareFactory({
283
283
  minVersion: "12.0.0",
284
- version: "22.2.3",
284
+ version: "22.2.4",
285
285
  ngImport: i0,
286
286
  type: WrFallingText,
287
287
  deps: [],
@@ -289,7 +289,7 @@ var WrFallingText = class WrFallingText {
289
289
  });
290
290
  static ɵcmp = i0.ɵɵngDeclareComponent({
291
291
  minVersion: "17.0.0",
292
- version: "22.2.3",
292
+ version: "22.2.4",
293
293
  type: WrFallingText,
294
294
  isStandalone: true,
295
295
  selector: "wr-falling-text",
@@ -349,7 +349,7 @@ var WrFallingText = class WrFallingText {
349
349
  };
350
350
  i0.ɵɵngDeclareClassMetadata({
351
351
  minVersion: "12.0.0",
352
- version: "22.2.3",
352
+ version: "22.2.4",
353
353
  ngImport: i0,
354
354
  type: WrFallingText,
355
355
  decorators: [{
@@ -370,7 +370,7 @@ var WrFuzzyText = class WrFuzzyText {
370
370
  }
371
371
  static ɵfac = i0.ɵɵngDeclareFactory({
372
372
  minVersion: "12.0.0",
373
- version: "22.2.3",
373
+ version: "22.2.4",
374
374
  ngImport: i0,
375
375
  type: WrFuzzyText,
376
376
  deps: [],
@@ -378,7 +378,7 @@ var WrFuzzyText = class WrFuzzyText {
378
378
  });
379
379
  static ɵcmp = i0.ɵɵngDeclareComponent({
380
380
  minVersion: "17.2.0",
381
- version: "22.2.3",
381
+ version: "22.2.4",
382
382
  type: WrFuzzyText,
383
383
  isStandalone: true,
384
384
  selector: "wr-fuzzy-text",
@@ -529,7 +529,7 @@ var WrFuzzyText = class WrFuzzyText {
529
529
  };
530
530
  i0.ɵɵngDeclareClassMetadata({
531
531
  minVersion: "12.0.0",
532
- version: "22.2.3",
532
+ version: "22.2.4",
533
533
  ngImport: i0,
534
534
  type: WrFuzzyText,
535
535
  decorators: [{
@@ -35,7 +35,7 @@ var WrGlitchText = class WrGlitchText {
35
35
  afterShadow = computed(() => this.enableShadows() ? "-5px 0 var(--wr-color-danger)" : "none", ...ngDevMode ? [{ debugName: "afterShadow" }] : /* istanbul ignore next */ []);
36
36
  static ɵfac = i0.ɵɵngDeclareFactory({
37
37
  minVersion: "12.0.0",
38
- version: "22.2.3",
38
+ version: "22.2.4",
39
39
  ngImport: i0,
40
40
  type: WrGlitchText,
41
41
  deps: [],
@@ -43,7 +43,7 @@ var WrGlitchText = class WrGlitchText {
43
43
  });
44
44
  static ɵcmp = i0.ɵɵngDeclareComponent({
45
45
  minVersion: "17.1.0",
46
- version: "22.2.3",
46
+ version: "22.2.4",
47
47
  type: WrGlitchText,
48
48
  isStandalone: true,
49
49
  selector: "wr-glitch-text",
@@ -103,7 +103,7 @@ var WrGlitchText = class WrGlitchText {
103
103
  };
104
104
  i0.ɵɵngDeclareClassMetadata({
105
105
  minVersion: "12.0.0",
106
- version: "22.2.3",
106
+ version: "22.2.4",
107
107
  ngImport: i0,
108
108
  type: WrGlitchText,
109
109
  decorators: [{
@@ -61,7 +61,7 @@ var WrGradientText = class WrGradientText {
61
61
  }
62
62
  static ɵfac = i0.ɵɵngDeclareFactory({
63
63
  minVersion: "12.0.0",
64
- version: "22.2.3",
64
+ version: "22.2.4",
65
65
  ngImport: i0,
66
66
  type: WrGradientText,
67
67
  deps: [],
@@ -69,7 +69,7 @@ var WrGradientText = class WrGradientText {
69
69
  });
70
70
  static ɵcmp = i0.ɵɵngDeclareComponent({
71
71
  minVersion: "17.0.0",
72
- version: "22.2.3",
72
+ version: "22.2.4",
73
73
  type: WrGradientText,
74
74
  isStandalone: true,
75
75
  selector: "wr-gradient-text",
@@ -138,7 +138,7 @@ var WrGradientText = class WrGradientText {
138
138
  };
139
139
  i0.ɵɵngDeclareClassMetadata({
140
140
  minVersion: "12.0.0",
141
- version: "22.2.3",
141
+ version: "22.2.4",
142
142
  ngImport: i0,
143
143
  type: WrGradientText,
144
144
  decorators: [{
@@ -0,0 +1,31 @@
1
+ import { ComponentHarness, HarnessPredicate } from "@angular/cdk/testing";
2
+ /**
3
+ * @license
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
7
+ */
8
+ const PUBLISHED_COLOR = /(?:^|;)\s*--wr-iridescence-input-color\s*:\s*([^;]+)/;
9
+ var WrIridescenceHarness = class WrIridescenceHarness extends ComponentHarness {
10
+ static hostSelector = "wr-iridescence";
11
+ static with(options = {}) {
12
+ return new HarnessPredicate(WrIridescenceHarness, options).addOption("painted", options.painted, async (harness, painted) => await harness.isPainted() === painted);
13
+ }
14
+ canvas = this.locatorFor(".wr-iridescence__canvas");
15
+ async isPainted() {
16
+ return (await this.host()).hasClass("wr-iridescence--painted");
17
+ }
18
+ async getColor() {
19
+ const style = await (await this.host()).getAttribute("style") ?? "";
20
+ return PUBLISHED_COLOR.exec(style)?.[1].trim() ?? null;
21
+ }
22
+ async isDecorative() {
23
+ return await (await this.canvas()).getAttribute("aria-hidden") === "true";
24
+ }
25
+ async hasCanvas() {
26
+ return await this.locatorForOptional(".wr-iridescence__canvas")() !== null;
27
+ }
28
+ };
29
+ export { WrIridescenceHarness };
30
+
31
+ //# sourceMappingURL=ngwr-bits-iridescence-testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngwr-bits-iridescence-testing.mjs","names":[],"sources":["../../../projects/lib/bits/iridescence/testing/wr-iridescence-harness.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrIridescenceHarnessFilters } from './types';\n\n/** `--wr-iridescence-input-color: …` on the host's own `style` attribute, a value as published. */\nconst PUBLISHED_COLOR = /(?:^|;)\\s*--wr-iridescence-input-color\\s*:\\s*([^;]+)/;\n\n/**\n * Test harness for `<wr-iridescence>` — folded silk on a WebGL2 canvas.\n *\n * **The picture is unreadable and always will be.** It is a fragment shader's output,\n * and jsdom has no WebGL2 to run one. What the component publishes to the DOM is small\n * and deliberate: the handover flag {@link isPainted}, the colour {@link getColor} it\n * hands the CSS stand-in, and the surface itself.\n *\n * The colour is read from the host's inline declaration, a variable of the component's\n * own. The `--wr-iridescence-color` hook belongs to the author and the stylesheet, so it\n * says nothing about what the component published.\n *\n * There is no `getPhase()`: the phases live in a frame loop, step by the real frame\n * delta and leave the component only as a uniform, so any number would be a stale copy\n * of an implementation detail. No pixel reads: a stub context paints nothing, and with\n * no stub there is no context. No `isAnimating()`: a loop that is running and a loop\n * that was never started look the same from the DOM, and a frame that would repeat the\n * last one draws nothing either way. And no `stir()`: the pointer's easing runs on\n * frames and its result goes to a uniform, so a method that moved it could report\n * nothing it had done.\n *\n * @example\n * ```ts\n * const silk = await loader.getHarness(WrIridescenceHarness);\n *\n * expect(await silk.hasCanvas()).toBe(true);\n * expect(await silk.isDecorative()).toBe(true);\n * expect(await silk.getColor()).toBe('var(--wr-color-primary)');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrIridescenceHarness extends ComponentHarness {\n static hostSelector = 'wr-iridescence';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrIridescenceHarnessFilters = {}): HarnessPredicate<WrIridescenceHarness> {\n return new HarnessPredicate(WrIridescenceHarness, options).addOption(\n 'painted',\n options.painted,\n async (harness, painted) => (await harness.isPainted()) === painted\n );\n }\n\n private readonly canvas = this.locatorFor('.wr-iridescence__canvas');\n\n /**\n * Whether the canvas has drawn a frame and is fading in over the CSS stand-in.\n *\n * **In a plain unit test this is `false`, and asserting exactly that is the point.**\n * jsdom hands back no WebGL2 context, so the component must leave the stand-in up\n * rather than declare itself painted over a blank box: the flag is set inside the draw\n * for that reason, not at the end of the boot. A spec that stubs a context and runs a\n * frame gets `true`, and a lost context takes it back to `false`.\n */\n async isPainted(): Promise<boolean> {\n return (await this.host()).hasClass('wr-iridescence--painted');\n }\n\n /**\n * The colour published on the host's own `--wr-iridescence-input-color` for the stand-in,\n * or `null` when none is: `[color]` unset, empty or blank, which leaves the stylesheet's\n * value in charge.\n *\n * It is the input as authored, a `var()` or a `light-dark()` included, not what it\n * resolves to: what reaches the shader is a uniform, which the DOM does not carry. A\n * stand-in tinted one colour while the canvas arrives in another is what this catches.\n */\n async getColor(): Promise<string | null> {\n const style = (await (await this.host()).getAttribute('style')) ?? '';\n return PUBLISHED_COLOR.exec(style)?.[1].trim() ?? null;\n }\n\n /**\n * Whether the background is hidden from assistive tech.\n *\n * A background canvas has nothing to announce, so losing this leaves a screen reader\n * stopping on a nameless graphic in front of the section's actual content.\n */\n async isDecorative(): Promise<boolean> {\n return (await (await this.canvas()).getAttribute('aria-hidden')) === 'true';\n }\n\n /**\n * Whether the drawing surface is in the markup.\n *\n * It is there in every state, including the ones where nothing is ever drawn on it\n * (prerendered, no WebGL2, a shader the driver refused), so the stand-in has something\n * to sit behind and {@link isPainted} has something to answer about.\n */\n async hasCanvas(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-iridescence__canvas')()) !== null;\n }\n}\n"],"mappings":";;;;;;;AAYA,MAAM,kBAAkB;AAkCxB,IAAa,uBAAb,MAAa,6BAA6B,iBAAgB;CACxD,OAAO,eAAe;CAGtB,OAAO,KAAK,UAAuC,CAAA,GAAE;EACnD,OAAO,IAAI,iBAAiB,sBAAsB,OAAO,CAAC,CAAC,UACzD,WACA,QAAQ,SACR,OAAO,SAAS,YAAa,MAAM,QAAQ,UAAS,MAAQ,OAAO;CAEvE;CAEiB,SAAS,KAAK,WAAW,yBAAyB;CAWnE,MAAM,YAAS;EACb,QAAQ,MAAM,KAAK,KAAI,EAAA,CAAI,SAAS,yBAAyB;CAC/D;CAWA,MAAM,WAAQ;EACZ,MAAM,QAAS,OAAO,MAAM,KAAK,KAAI,EAAA,CAAI,aAAa,OAAO,KAAM;EACnE,OAAO,gBAAgB,KAAK,KAAK,CAAC,GAAG,EAAE,CAAC,KAAI,KAAM;CACpD;CAQA,MAAM,eAAY;EAChB,OAAQ,OAAO,MAAM,KAAK,OAAM,EAAA,CAAI,aAAa,aAAa,MAAO;CACvE;CASA,MAAM,YAAS;EACb,OAAQ,MAAM,KAAK,mBAAmB,yBAAyB,CAAC,CAAA,MAAQ;CAC1E"}