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
@@ -0,0 +1,591 @@
1
+ import * as i0 from "@angular/core";
2
+ import { Component, DestroyRef, ElementRef, NgZone, PLATFORM_ID, ViewEncapsulation, afterNextRender, afterRenderEffect, computed, effect, inject, input, signal, untracked, viewChild } from "@angular/core";
3
+ import { coerceBooleanProperty } from "@angular/cdk/coercion";
4
+ import { isPlatformBrowser } from "@angular/common";
5
+ import { clamp, numAttr, resolveCssColor, resolveCssColorChannels, watchColorScheme } from "ngwr/utils";
6
+ import { WrPlatform } from "ngwr/platform";
7
+ /**
8
+ * @license
9
+ *
10
+ * Use of this source code is governed by an MIT-style license that can be
11
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
12
+ *
13
+ * After the Iridescence background idea on reactbits.dev
14
+ * (https://www.reactbits.dev/backgrounds/iridescence). The shader is written for
15
+ * ngwr from scratch: folded silk under a raking light, with the motion, the
16
+ * inputs and the names this library's own.
17
+ *
18
+ * WebGL2 rather than a 2D canvas, deliberately, unlike `wr-threads` and
19
+ * `wr-ripple-grid`. Those draw a few dozen strokes; this works out every pixel,
20
+ * every frame, which is a job for a fragment shader and no job for a 2D context.
21
+ */
22
+ const VERT = `#version 300 es
23
+ in vec2 position;
24
+ void main() {
25
+ gl_Position = vec4(position, 0.0, 1.0);
26
+ }
27
+ `;
28
+ const FRAG = `#version 300 es
29
+ precision highp float;
30
+ uniform vec2 uResolution;
31
+ uniform vec3 uPhase;
32
+ uniform vec2 uPointer;
33
+ uniform vec3 uColor;
34
+ out vec4 fragColor;
35
+
36
+ // Folded silk. A height field of draped folds is lit by a raking key light; hue is a small, luma-free offset
37
+ // driven by the fold slope. No finite differences: every stage carries its gradient by the chain rule, so the
38
+ // normal costs one evaluation of the cloth instead of three.
39
+
40
+ // One stage of the warp: moves v by a sine of u, and carries the gradient of v along with it.
41
+ void shear(inout float v, inout vec2 gv, float u, vec2 gu, float amp, float k, float phi) {
42
+ v += amp * sin(k * u + phi);
43
+ gv += amp * k * cos(k * u + phi) * gu;
44
+ }
45
+
46
+ // One family of folds added to the height. Negative skew bends the sine so the flank facing the light is short
47
+ // and bright and the other long and soft, which is the drape's crest and valley. t tapers the family, gt is its
48
+ // gradient.
49
+ void pleat(inout float h, inout vec2 g, float u, vec2 gu, float skew, float amp, float t, vec2 gt) {
50
+ float b = u + skew * sin(u);
51
+ float s = sin(b);
52
+ h += amp * t * s;
53
+ g += amp * (t * cos(b) * (1.0 + skew * cos(u)) * gu + s * gt);
54
+ }
55
+
56
+ void main() {
57
+ vec2 p = (2.0 * gl_FragCoord.xy - uResolution) / min(uResolution.x, uResolution.y);
58
+ vec3 ph = uPhase;
59
+ vec2 st = uPointer;
60
+
61
+ // Drape axis: q.x runs along the folds, q.y across them. gx and gy are the gradients of q.x and q.y.
62
+ vec2 gx = vec2(0.86, 0.51);
63
+ vec2 gy = vec2(-0.51, 0.86);
64
+ vec2 q = vec2(dot(p, gx), dot(p, gy)) + st * vec2(0.5, 0.8);
65
+
66
+ // The warp. The pointer enters each stage differently, so stirring bends the folds instead of sliding the
67
+ // picture. Sums like ph.y - ph.x are still integer multiples, so seam free, and give slow beats: the large
68
+ // displacements run on those so the cloth does not stream.
69
+
70
+ // A long sweep: the whole drape bends once across the frame.
71
+ shear(q.y, gy, q.x, gx, 0.42, 0.60, ph.x + 1.6 * st.x);
72
+
73
+ // Gather: spacing across the drape swells and tightens along its length, so folds fan out from a pinch.
74
+ float ga = 0.85 * q.x + ph.y - ph.x + 1.2 * st.y;
75
+ float gs = 0.20 * sin(ga);
76
+ gy = (1.0 + gs) * gy + q.y * 0.20 * 0.85 * cos(ga) * gx;
77
+ q.y *= 1.0 + gs;
78
+
79
+ // A slip along the folds, so tapers and crossings do not line up across the width.
80
+ shear(q.x, gx, q.y, gy, 0.22, 1.10, -ph.y - 0.8 * st.y);
81
+
82
+ // Differential bend: this one reads q.y too, so neighbouring folds bend apart and bunch instead of running
83
+ // as parallel stripes.
84
+ shear(q.y, gy, q.x + 0.8 * q.y, gx + 0.8 * gy, 0.24, 1.15, ph.z - ph.y - 2.0 * st.x);
85
+
86
+ // Height, three scales of cloth: a big billow slightly off axis that decides where the light pools, the main
87
+ // folds, and a fine echo. Each main fold is gated across the drape as well as along it, so some stand proud
88
+ // and others die out; that hierarchy is what stops it reading as stripes. The tapers run on the fast phase:
89
+ // folds swell and relax in place more than they travel.
90
+ float h = 0.0;
91
+ vec2 g = vec2(0.0);
92
+ pleat(h, g, 2.3 * q.y - 0.8 * q.x - ph.x + 2.5 * st.y - 1.5 * st.x, 2.3 * gy - 0.8 * gx, -0.2, 0.62, 1.0, vec2(0.0));
93
+ float a = 0.9 * q.x + 1.3 * q.y - ph.z;
94
+ pleat(h, g, 5.0 * q.y + ph.x, 5.0 * gy, -0.36, 0.50, 0.68 + 0.32 * sin(a), 0.32 * cos(a) * (0.9 * gx + 1.3 * gy));
95
+ a = 1.1 * q.y + 0.6 * q.x + 1.9 + ph.z;
96
+ pleat(h, g, 9.5 * q.y + 0.5 * q.x - ph.y, 9.5 * gy + 0.5 * gx, -0.34, 0.20, 0.55 + 0.45 * sin(a),
97
+ 0.45 * cos(a) * (1.1 * gy + 0.6 * gx));
98
+
99
+ // Raking key light: a flat patch sits mid-tone, the two flanks of a crest go opposite ways. The sheen is a broad
100
+ // lobe that rides the lit flank. A weak bounce from the far side keeps the shaded flank from going to slate.
101
+ const vec3 L = normalize(vec3(-0.50, 0.72, 0.48));
102
+ const vec3 H = normalize(L + vec3(0.0, 0.0, 1.0));
103
+ const vec3 B = normalize(vec3(0.42, -0.60, 0.68));
104
+ vec3 n = normalize(vec3(-0.27 * g, 1.0));
105
+ float body = pow(0.5 + 0.5 * dot(n, L), 1.35);
106
+ float sheen = pow(max(dot(n, H), 0.0), 26.0);
107
+ float bounce = pow(max(dot(n, B), 0.0), 8.0);
108
+
109
+ // Valleys sit in their own shade whatever the light does; this keeps depth in the dark tint.
110
+ float lum = (0.21 + 1.12 * body) * (0.90 + 0.15 * h) + 0.30 * sheen + 0.08 * bounce;
111
+
112
+ // Shot silk: the hue turns with the slope seen from the light, pale rose on the lit flank, lilac on the flat,
113
+ // periwinkle in the shade. The drift along the warped cloth wanders further where it is lit, so pale sky and rose
114
+ // zones follow the folds while the shade holds its colour: a warm highlight or a green-heavy shade would grey
115
+ // or flatten under the dark tint.
116
+ float slope = dot(g, vec2(-0.57, 0.82));
117
+ float turn = 0.42 * slope / sqrt(1.0 + 0.18 * slope * slope);
118
+ float drift = sin(1.25 * q.y + 0.55 * q.x + ph.z - ph.y);
119
+ float theta = 1.68 + 0.85 * turn - 0.2 * turn * turn + (0.15 + 0.85 * body) * drift;
120
+ vec3 tone = cos(theta + vec3(0.0, 2.1, 4.2));
121
+
122
+ // The offset has its luma removed, so colour draws no shapes of its own and the folds stay pure luminance.
123
+ tone -= dot(tone, vec3(0.2126, 0.7152, 0.0722));
124
+
125
+ // Chroma lives in the shade and the mid-tones; the highlight goes nearly white, which is what makes it pearl.
126
+ vec3 col = lum * (1.0 + (0.46 - 0.28 * body - 0.08 * sheen) * tone);
127
+
128
+ // Soft shoulder, so a highlight rolls off instead of clipping to a flat white shape.
129
+ col = col / (1.0 + 0.26 * col * col);
130
+
131
+ // Interleaved gradient noise, plus or minus half an 8-bit step: a large dark hero spreads a few levels of tint over
132
+ // hundreds of pixels and bands. Static, off the pixel and never a phase, so it cannot shimmer or cost a held frame a redraw.
133
+ float dither = fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715)))) - 0.5;
134
+
135
+ fragColor = vec4(col * uColor + dither / 255.0, 1.0);
136
+ }
137
+ `;
138
+ const THEME_COLOR = "var(--wr-iridescence-color)";
139
+ const FALLBACK_COLOR = "rgb(255, 255, 255)";
140
+ const TAU = Math.PI * 2;
141
+ const RATE = [
142
+ .187,
143
+ .283,
144
+ .441
145
+ ];
146
+ const FRAME_MS = 1e3 / 60;
147
+ const MAX_FRAME_MS = 50;
148
+ const REACH = .1;
149
+ const POINTER_TAU_MS = 250;
150
+ const POINTER_EPSILON = 1e-4;
151
+ const MAX_PIXELS = 2e6;
152
+ function wrap(angle) {
153
+ const turn = angle % TAU;
154
+ const wrapped = turn < 0 ? turn + TAU : turn;
155
+ return wrapped < TAU ? wrapped : 0;
156
+ }
157
+ var WrIridescence = class WrIridescence {
158
+ color = input(null, ...ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []);
159
+ speed = input(1, {
160
+ ...ngDevMode ? { debugName: "speed" } : /* istanbul ignore next */ {},
161
+ transform: numAttr(1)
162
+ });
163
+ amplitude = input(1, {
164
+ ...ngDevMode ? { debugName: "amplitude" } : /* istanbul ignore next */ {},
165
+ transform: numAttr(1)
166
+ });
167
+ followPointer = input(true, {
168
+ ...ngDevMode ? { debugName: "followPointer" } : /* istanbul ignore next */ {},
169
+ transform: coerceBooleanProperty
170
+ });
171
+ standInColor = computed(() => (this.color() ?? "").trim() || null, ...ngDevMode ? [{ debugName: "standInColor" }] : /* istanbul ignore next */ []);
172
+ expression = computed(() => (this.color() ?? "").trim() || THEME_COLOR, ...ngDevMode ? [{ debugName: "expression" }] : /* istanbul ignore next */ []);
173
+ listening = computed(() => !this.platform.prefersReducedMotion() && this.followPointer() && this.amplitude() !== 0, ...ngDevMode ? [{ debugName: "listening" }] : /* istanbul ignore next */ []);
174
+ moving = computed(() => !this.platform.prefersReducedMotion() && (this.speed() !== 0 || this.listening()), ...ngDevMode ? [{ debugName: "moving" }] : /* istanbul ignore next */ []);
175
+ painted = signal(false, ...ngDevMode ? [{ debugName: "painted" }] : /* istanbul ignore next */ []);
176
+ canvasRef = viewChild.required("canvas", ...ngDevMode ? [{ debugName: "canvasRef" }] : /* istanbul ignore next */ []);
177
+ host = inject(ElementRef);
178
+ zone = inject(NgZone);
179
+ destroyRef = inject(DestroyRef);
180
+ platform = inject(WrPlatform);
181
+ isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
182
+ gl = null;
183
+ gpu = null;
184
+ bitmap = {
185
+ width: 0,
186
+ height: 0
187
+ };
188
+ rafId = 0;
189
+ phase = [
190
+ 0,
191
+ 0,
192
+ 0
193
+ ];
194
+ pointer = {
195
+ x: 0,
196
+ y: 0,
197
+ aimX: 0,
198
+ aimY: 0
199
+ };
200
+ lastMove = null;
201
+ resolved = "";
202
+ channels = [
203
+ 1,
204
+ 1,
205
+ 1
206
+ ];
207
+ dirty = true;
208
+ booted = signal(false, ...ngDevMode ? [{ debugName: "booted" }] : /* istanbul ignore next */ []);
209
+ onScreen = signal(true, ...ngDevMode ? [{ debugName: "onScreen" }] : /* istanbul ignore next */ []);
210
+ hasBox = signal(false, ...ngDevMode ? [{ debugName: "hasBox" }] : /* istanbul ignore next */ []);
211
+ constructor() {
212
+ if (!this.isBrowser) return;
213
+ afterNextRender(() => this.zone.runOutsideAngular(() => this.boot()));
214
+ afterRenderEffect(() => {
215
+ if (!this.booted()) return;
216
+ const moving = this.moving();
217
+ this.expression();
218
+ untracked(() => this.repaint(moving));
219
+ });
220
+ watchColorScheme(() => {
221
+ if (this.booted()) this.repaint(this.moving());
222
+ });
223
+ effect((onCleanup) => {
224
+ if (!this.booted() || !this.onScreen() || !this.hasBox() || !this.moving()) return;
225
+ const follow = this.listening();
226
+ onCleanup(untracked(() => this.zone.runOutsideAngular(() => this.animate(follow))));
227
+ });
228
+ }
229
+ boot() {
230
+ const canvas = this.canvasRef().nativeElement;
231
+ const gl = canvas.getContext("webgl2", {
232
+ alpha: true,
233
+ antialias: false,
234
+ depth: false,
235
+ stencil: false
236
+ });
237
+ if (!gl) return;
238
+ this.gl = gl;
239
+ this.gpu = this.build(gl);
240
+ if (!this.gpu && !gl.isContextLost()) {
241
+ this.release(gl);
242
+ return;
243
+ }
244
+ const onLost = (event) => {
245
+ event.preventDefault();
246
+ this.gpu = null;
247
+ this.booted.set(false);
248
+ this.painted.set(false);
249
+ };
250
+ const onRestored = () => {
251
+ this.gpu = this.build(gl);
252
+ if (this.gpu) this.booted.set(true);
253
+ };
254
+ canvas.addEventListener("webglcontextlost", onLost);
255
+ canvas.addEventListener("webglcontextrestored", onRestored);
256
+ const hostEl = this.host.nativeElement;
257
+ this.fit(hostEl.clientWidth, hostEl.clientHeight);
258
+ const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver((entries) => {
259
+ const rect = entries[entries.length - 1]?.contentRect;
260
+ if (rect && this.fit(rect.width, rect.height)) this.draw();
261
+ });
262
+ ro?.observe(hostEl);
263
+ const io = typeof IntersectionObserver === "undefined" ? null : new IntersectionObserver((entries) => {
264
+ const { isIntersecting, intersectionRatio } = entries[entries.length - 1];
265
+ this.onScreen.set(isIntersecting && intersectionRatio > 0);
266
+ }, { threshold: [0, 1e-9] });
267
+ io?.observe(canvas);
268
+ this.destroyRef.onDestroy(() => {
269
+ ro?.disconnect();
270
+ io?.disconnect();
271
+ canvas.removeEventListener("webglcontextlost", onLost);
272
+ canvas.removeEventListener("webglcontextrestored", onRestored);
273
+ this.release(gl);
274
+ });
275
+ if (this.gpu) this.booted.set(true);
276
+ }
277
+ build(gl) {
278
+ const vert = this.compile(gl, gl.VERTEX_SHADER, VERT);
279
+ const frag = this.compile(gl, gl.FRAGMENT_SHADER, FRAG);
280
+ const program = vert && frag ? gl.createProgram() : null;
281
+ if (vert && frag && program) {
282
+ gl.attachShader(program, vert);
283
+ gl.attachShader(program, frag);
284
+ gl.linkProgram(program);
285
+ }
286
+ gl.deleteShader(vert);
287
+ gl.deleteShader(frag);
288
+ if (!program) return null;
289
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
290
+ if (!gl.isContextLost()) console.error("wr-iridescence program error:", gl.getProgramInfoLog(program));
291
+ gl.deleteProgram(program);
292
+ return null;
293
+ }
294
+ gl.useProgram(program);
295
+ const buffer = gl.createBuffer();
296
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
297
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
298
+ -1,
299
+ -1,
300
+ 3,
301
+ -1,
302
+ -1,
303
+ 3
304
+ ]), gl.STATIC_DRAW);
305
+ const position = gl.getAttribLocation(program, "position");
306
+ gl.enableVertexAttribArray(position);
307
+ gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
308
+ return {
309
+ program,
310
+ buffer,
311
+ resolution: gl.getUniformLocation(program, "uResolution"),
312
+ phase: gl.getUniformLocation(program, "uPhase"),
313
+ pointer: gl.getUniformLocation(program, "uPointer"),
314
+ color: gl.getUniformLocation(program, "uColor")
315
+ };
316
+ }
317
+ compile(gl, type, source) {
318
+ const shader = gl.createShader(type);
319
+ if (!shader) return null;
320
+ gl.shaderSource(shader, source);
321
+ gl.compileShader(shader);
322
+ if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader;
323
+ if (!gl.isContextLost()) console.error("wr-iridescence shader error:", gl.getShaderInfoLog(shader));
324
+ gl.deleteShader(shader);
325
+ return null;
326
+ }
327
+ release(gl) {
328
+ if (this.gpu) {
329
+ gl.deleteProgram(this.gpu.program);
330
+ gl.deleteBuffer(this.gpu.buffer);
331
+ }
332
+ gl.getExtension("WEBGL_lose_context")?.loseContext();
333
+ this.gpu = null;
334
+ this.gl = null;
335
+ }
336
+ fit(width, height) {
337
+ const area = width * height;
338
+ const scale = area > MAX_PIXELS ? Math.sqrt(MAX_PIXELS / area) : 1;
339
+ const bitmapWidth = Math.floor(width * scale);
340
+ const bitmapHeight = Math.floor(height * scale);
341
+ if (bitmapWidth === this.bitmap.width && bitmapHeight === this.bitmap.height) return false;
342
+ this.bitmap = {
343
+ width: bitmapWidth,
344
+ height: bitmapHeight
345
+ };
346
+ this.hasBox.set(bitmapWidth > 0 && bitmapHeight > 0);
347
+ const canvas = this.canvasRef().nativeElement;
348
+ canvas.width = bitmapWidth;
349
+ canvas.height = bitmapHeight;
350
+ return true;
351
+ }
352
+ animate(follow) {
353
+ const stopPointer = follow ? this.listenToPointer() : null;
354
+ let last = -1;
355
+ let stopped = false;
356
+ this.dirty = true;
357
+ const tick = (now) => {
358
+ if (stopped) return;
359
+ const elapsed = now - last;
360
+ const step = last < 0 || elapsed <= 0 ? FRAME_MS : Math.min(MAX_FRAME_MS, elapsed);
361
+ last = now;
362
+ const turned = this.turn(step);
363
+ const stirred = this.easePointer(step);
364
+ if (this.dirty || turned || stirred) this.draw();
365
+ this.rafId = requestAnimationFrame(tick);
366
+ };
367
+ this.rafId = requestAnimationFrame(tick);
368
+ return () => {
369
+ stopped = true;
370
+ cancelAnimationFrame(this.rafId);
371
+ this.rafId = 0;
372
+ stopPointer?.();
373
+ };
374
+ }
375
+ turn(step) {
376
+ const speed = this.speed();
377
+ if (speed === 0) return false;
378
+ const seconds = step / 1e3;
379
+ for (let i = 0; i < RATE.length; i++) this.phase[i] = wrap(this.phase[i] + seconds * RATE[i] * speed);
380
+ return true;
381
+ }
382
+ listenToPointer() {
383
+ const hostEl = this.host.nativeElement;
384
+ const root = hostEl.getRootNode();
385
+ const target = hostEl.parentElement ?? (root instanceof ShadowRoot && root.host instanceof HTMLElement ? root.host : null);
386
+ const onMove = (event) => {
387
+ this.lastMove = {
388
+ x: event.clientX,
389
+ y: event.clientY
390
+ };
391
+ };
392
+ const onLeave = () => this.aimAtRest();
393
+ target?.addEventListener("pointermove", onMove, { passive: true });
394
+ target?.addEventListener("pointerleave", onLeave, { passive: true });
395
+ target?.addEventListener("pointercancel", onLeave, { passive: true });
396
+ return () => {
397
+ target?.removeEventListener("pointermove", onMove);
398
+ target?.removeEventListener("pointerleave", onLeave);
399
+ target?.removeEventListener("pointercancel", onLeave);
400
+ this.aimAtRest();
401
+ };
402
+ }
403
+ aimAtRest() {
404
+ this.lastMove = null;
405
+ this.pointer.aimX = 0;
406
+ this.pointer.aimY = 0;
407
+ }
408
+ easePointer(step) {
409
+ const pointer = this.pointer;
410
+ if (this.lastMove) {
411
+ const rect = this.host.nativeElement.getBoundingClientRect();
412
+ const u = rect.width > 0 ? clamp((this.lastMove.x - rect.left) / rect.width, 0, 1) : .5;
413
+ const v = rect.height > 0 ? clamp((this.lastMove.y - rect.top) / rect.height, 0, 1) : .5;
414
+ pointer.aimX = (u - .5) * 2 * REACH;
415
+ pointer.aimY = (.5 - v) * 2 * REACH;
416
+ this.lastMove = null;
417
+ }
418
+ const amplitude = this.amplitude();
419
+ const targetX = pointer.aimX * amplitude;
420
+ const targetY = pointer.aimY * amplitude;
421
+ if (!Number.isFinite(targetX) || !Number.isFinite(targetY)) return false;
422
+ if (pointer.x === targetX && pointer.y === targetY) return false;
423
+ const share = 1 - Math.exp(-step / POINTER_TAU_MS);
424
+ pointer.x += (targetX - pointer.x) * share;
425
+ pointer.y += (targetY - pointer.y) * share;
426
+ if (Math.abs(targetX - pointer.x) < POINTER_EPSILON && Math.abs(targetY - pointer.y) < POINTER_EPSILON) {
427
+ pointer.x = targetX;
428
+ pointer.y = targetY;
429
+ }
430
+ return true;
431
+ }
432
+ repaint(moving) {
433
+ if (moving) {
434
+ this.dirty = true;
435
+ return;
436
+ }
437
+ this.aimAtRest();
438
+ this.pointer.x = 0;
439
+ this.pointer.y = 0;
440
+ this.draw();
441
+ }
442
+ draw() {
443
+ const { gl, gpu } = this;
444
+ const { width, height } = this.bitmap;
445
+ if (!gl || !gpu || width === 0 || height === 0) return;
446
+ const [r, g, b] = this.tint();
447
+ gl.viewport(0, 0, width, height);
448
+ gl.uniform2f(gpu.resolution, width, height);
449
+ gl.uniform3f(gpu.phase, this.phase[0], this.phase[1], this.phase[2]);
450
+ gl.uniform2f(gpu.pointer, this.pointer.x, this.pointer.y);
451
+ gl.uniform3f(gpu.color, r, g, b);
452
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
453
+ this.dirty = false;
454
+ if (!this.painted()) this.painted.set(true);
455
+ }
456
+ tint() {
457
+ const canvas = this.canvasRef().nativeElement;
458
+ const resolved = resolveCssColor(canvas, this.expression(), FALLBACK_COLOR);
459
+ if (resolved !== this.resolved) {
460
+ this.resolved = resolved;
461
+ this.channels = resolveCssColorChannels(canvas, resolved, FALLBACK_COLOR);
462
+ }
463
+ return this.channels;
464
+ }
465
+ static ɵfac = i0.ɵɵngDeclareFactory({
466
+ minVersion: "12.0.0",
467
+ version: "22.2.4",
468
+ ngImport: i0,
469
+ type: WrIridescence,
470
+ deps: [],
471
+ target: i0.ɵɵFactoryTarget.Component
472
+ });
473
+ static ɵcmp = i0.ɵɵngDeclareComponent({
474
+ minVersion: "17.2.0",
475
+ version: "22.2.4",
476
+ type: WrIridescence,
477
+ isStandalone: true,
478
+ selector: "wr-iridescence",
479
+ inputs: {
480
+ color: {
481
+ classPropertyName: "color",
482
+ publicName: "color",
483
+ isSignal: true,
484
+ isRequired: false,
485
+ transformFunction: null
486
+ },
487
+ speed: {
488
+ classPropertyName: "speed",
489
+ publicName: "speed",
490
+ isSignal: true,
491
+ isRequired: false,
492
+ transformFunction: null
493
+ },
494
+ amplitude: {
495
+ classPropertyName: "amplitude",
496
+ publicName: "amplitude",
497
+ isSignal: true,
498
+ isRequired: false,
499
+ transformFunction: null
500
+ },
501
+ followPointer: {
502
+ classPropertyName: "followPointer",
503
+ publicName: "followPointer",
504
+ isSignal: true,
505
+ isRequired: false,
506
+ transformFunction: null
507
+ }
508
+ },
509
+ host: {
510
+ properties: {
511
+ "style.--wr-iridescence-input-color": "standInColor()",
512
+ "class.wr-iridescence--painted": "painted()"
513
+ },
514
+ classAttribute: "wr-iridescence"
515
+ },
516
+ viewQueries: [{
517
+ propertyName: "canvasRef",
518
+ first: true,
519
+ predicate: ["canvas"],
520
+ descendants: true,
521
+ isSignal: true
522
+ }],
523
+ ngImport: i0,
524
+ template: "<canvas #canvas class=\"wr-iridescence__canvas\" aria-hidden=\"true\"></canvas>",
525
+ isInline: true,
526
+ styles: [".wr-iridescence{--wr-iridescence-color: light-dark(#ffffff, #5c669e);position:absolute;inset:0;z-index:0;display:block;overflow:hidden;pointer-events:none;background-color:var(--wr-iridescence-input-color, var(--wr-iridescence-color));background-image:linear-gradient(59deg,#e6dcf0 0%,transparent 35%,transparent 65%,#d6d0ea 100%),linear-gradient(329deg,#e9dbef,#c49cd1,#f2e6f3,#b2bcde 48%,#7387b2 60%,#d5c7e7 74%,#c6bce4 86%,#ebddf0);background-blend-mode:multiply}.wr-iridescence__canvas{display:block;width:100%;height:100%;opacity:0;transition:opacity var(--wr-duration-slower) var(--wr-ease-in-out)}.wr-iridescence--painted .wr-iridescence__canvas{opacity:1}@media(prefers-reduced-motion:reduce){.wr-iridescence__canvas{transition:none}}@media(forced-colors:active){.wr-iridescence__canvas{display:none;forced-color-adjust:none}}\n"],
527
+ encapsulation: i0.ViewEncapsulation.None
528
+ });
529
+ };
530
+ i0.ɵɵngDeclareClassMetadata({
531
+ minVersion: "12.0.0",
532
+ version: "22.2.4",
533
+ ngImport: i0,
534
+ type: WrIridescence,
535
+ decorators: [{
536
+ type: Component,
537
+ args: [{
538
+ selector: "wr-iridescence",
539
+ template: "<canvas #canvas class=\"wr-iridescence__canvas\" aria-hidden=\"true\"></canvas>",
540
+ encapsulation: ViewEncapsulation.None,
541
+ host: {
542
+ class: "wr-iridescence",
543
+ "[style.--wr-iridescence-input-color]": "standInColor()",
544
+ "[class.wr-iridescence--painted]": "painted()"
545
+ },
546
+ styles: [".wr-iridescence{--wr-iridescence-color: light-dark(#ffffff, #5c669e);position:absolute;inset:0;z-index:0;display:block;overflow:hidden;pointer-events:none;background-color:var(--wr-iridescence-input-color, var(--wr-iridescence-color));background-image:linear-gradient(59deg,#e6dcf0 0%,transparent 35%,transparent 65%,#d6d0ea 100%),linear-gradient(329deg,#e9dbef,#c49cd1,#f2e6f3,#b2bcde 48%,#7387b2 60%,#d5c7e7 74%,#c6bce4 86%,#ebddf0);background-blend-mode:multiply}.wr-iridescence__canvas{display:block;width:100%;height:100%;opacity:0;transition:opacity var(--wr-duration-slower) var(--wr-ease-in-out)}.wr-iridescence--painted .wr-iridescence__canvas{opacity:1}@media(prefers-reduced-motion:reduce){.wr-iridescence__canvas{transition:none}}@media(forced-colors:active){.wr-iridescence__canvas{display:none;forced-color-adjust:none}}\n"]
547
+ }]
548
+ }],
549
+ ctorParameters: () => [],
550
+ propDecorators: {
551
+ color: [{
552
+ type: i0.Input,
553
+ args: [{
554
+ isSignal: true,
555
+ alias: "color",
556
+ required: false
557
+ }]
558
+ }],
559
+ speed: [{
560
+ type: i0.Input,
561
+ args: [{
562
+ isSignal: true,
563
+ alias: "speed",
564
+ required: false
565
+ }]
566
+ }],
567
+ amplitude: [{
568
+ type: i0.Input,
569
+ args: [{
570
+ isSignal: true,
571
+ alias: "amplitude",
572
+ required: false
573
+ }]
574
+ }],
575
+ followPointer: [{
576
+ type: i0.Input,
577
+ args: [{
578
+ isSignal: true,
579
+ alias: "followPointer",
580
+ required: false
581
+ }]
582
+ }],
583
+ canvasRef: [{
584
+ type: i0.ViewChild,
585
+ args: ["canvas", { isSignal: true }]
586
+ }]
587
+ }
588
+ });
589
+ export { WrIridescence };
590
+
591
+ //# sourceMappingURL=ngwr-bits-iridescence.mjs.map