@signality/core 0.0.1-alpha.2

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 (261) hide show
  1. package/README.md +60 -0
  2. package/browser/battery/index.d.ts +34 -0
  3. package/browser/bluetooth/index.d.ts +56 -0
  4. package/browser/breakpoints/index.d.ts +32 -0
  5. package/browser/broadcast-channel/index.d.ts +42 -0
  6. package/browser/browser-language/index.d.ts +34 -0
  7. package/browser/clipboard/index.d.ts +48 -0
  8. package/browser/device-posture/index.d.ts +18 -0
  9. package/browser/display-media/index.d.ts +80 -0
  10. package/browser/eye-dropper/index.d.ts +47 -0
  11. package/browser/favicon/index.d.ts +39 -0
  12. package/browser/fps/index.d.ts +46 -0
  13. package/browser/gamepad/index.d.ts +28 -0
  14. package/browser/geolocation/index.d.ts +64 -0
  15. package/browser/index.d.ts +29 -0
  16. package/browser/input-modality/index.d.ts +26 -0
  17. package/browser/listener/index.d.ts +61 -0
  18. package/browser/media-query/index.d.ts +36 -0
  19. package/browser/network/index.d.ts +44 -0
  20. package/browser/online/index.d.ts +27 -0
  21. package/browser/page-visibility/index.d.ts +27 -0
  22. package/browser/picture-in-picture/index.d.ts +42 -0
  23. package/browser/pointer-lock-element/index.d.ts +22 -0
  24. package/browser/screen-orientation/index.d.ts +29 -0
  25. package/browser/speech-recognition/index.d.ts +77 -0
  26. package/browser/speech-synthesis/index.d.ts +76 -0
  27. package/browser/storage/index.d.ts +142 -0
  28. package/browser/text-direction/index.d.ts +43 -0
  29. package/browser/vibration/index.d.ts +37 -0
  30. package/browser/wake-lock/index.d.ts +37 -0
  31. package/browser/web-notification/index.d.ts +58 -0
  32. package/browser/web-share/index.d.ts +42 -0
  33. package/browser/web-worker/index.d.ts +52 -0
  34. package/elements/active-element/index.d.ts +27 -0
  35. package/elements/dropzone/index.d.ts +61 -0
  36. package/elements/element-focus/index.d.ts +38 -0
  37. package/elements/element-focus-within/index.d.ts +29 -0
  38. package/elements/element-hover/index.d.ts +27 -0
  39. package/elements/element-size/index.d.ts +40 -0
  40. package/elements/element-visibility/index.d.ts +53 -0
  41. package/elements/index.d.ts +16 -0
  42. package/elements/mouse-position/index.d.ts +64 -0
  43. package/elements/on-click-outside/index.d.ts +42 -0
  44. package/elements/on-disconnect/index.d.ts +45 -0
  45. package/elements/on-long-press/index.d.ts +44 -0
  46. package/elements/pointer-swipe/index.d.ts +58 -0
  47. package/elements/scroll-position/index.d.ts +96 -0
  48. package/elements/swipe/index.d.ts +49 -0
  49. package/elements/text-selection/index.d.ts +39 -0
  50. package/elements/window-size/index.d.ts +46 -0
  51. package/fesm2022/signality-core-browser-battery.mjs +80 -0
  52. package/fesm2022/signality-core-browser-battery.mjs.map +1 -0
  53. package/fesm2022/signality-core-browser-bluetooth.mjs +112 -0
  54. package/fesm2022/signality-core-browser-bluetooth.mjs.map +1 -0
  55. package/fesm2022/signality-core-browser-breakpoints.mjs +51 -0
  56. package/fesm2022/signality-core-browser-breakpoints.mjs.map +1 -0
  57. package/fesm2022/signality-core-browser-broadcast-channel.mjs +74 -0
  58. package/fesm2022/signality-core-browser-broadcast-channel.mjs.map +1 -0
  59. package/fesm2022/signality-core-browser-browser-language.mjs +48 -0
  60. package/fesm2022/signality-core-browser-browser-language.mjs.map +1 -0
  61. package/fesm2022/signality-core-browser-clipboard.mjs +102 -0
  62. package/fesm2022/signality-core-browser-clipboard.mjs.map +1 -0
  63. package/fesm2022/signality-core-browser-device-posture.mjs +40 -0
  64. package/fesm2022/signality-core-browser-device-posture.mjs.map +1 -0
  65. package/fesm2022/signality-core-browser-display-media.mjs +121 -0
  66. package/fesm2022/signality-core-browser-display-media.mjs.map +1 -0
  67. package/fesm2022/signality-core-browser-eye-dropper.mjs +82 -0
  68. package/fesm2022/signality-core-browser-eye-dropper.mjs.map +1 -0
  69. package/fesm2022/signality-core-browser-favicon.mjs +100 -0
  70. package/fesm2022/signality-core-browser-favicon.mjs.map +1 -0
  71. package/fesm2022/signality-core-browser-fps.mjs +103 -0
  72. package/fesm2022/signality-core-browser-fps.mjs.map +1 -0
  73. package/fesm2022/signality-core-browser-gamepad.mjs +93 -0
  74. package/fesm2022/signality-core-browser-gamepad.mjs.map +1 -0
  75. package/fesm2022/signality-core-browser-geolocation.mjs +120 -0
  76. package/fesm2022/signality-core-browser-geolocation.mjs.map +1 -0
  77. package/fesm2022/signality-core-browser-input-modality.mjs +64 -0
  78. package/fesm2022/signality-core-browser-input-modality.mjs.map +1 -0
  79. package/fesm2022/signality-core-browser-listener.mjs +132 -0
  80. package/fesm2022/signality-core-browser-listener.mjs.map +1 -0
  81. package/fesm2022/signality-core-browser-media-query.mjs +55 -0
  82. package/fesm2022/signality-core-browser-media-query.mjs.map +1 -0
  83. package/fesm2022/signality-core-browser-network.mjs +76 -0
  84. package/fesm2022/signality-core-browser-network.mjs.map +1 -0
  85. package/fesm2022/signality-core-browser-online.mjs +49 -0
  86. package/fesm2022/signality-core-browser-online.mjs.map +1 -0
  87. package/fesm2022/signality-core-browser-page-visibility.mjs +47 -0
  88. package/fesm2022/signality-core-browser-page-visibility.mjs.map +1 -0
  89. package/fesm2022/signality-core-browser-picture-in-picture.mjs +93 -0
  90. package/fesm2022/signality-core-browser-picture-in-picture.mjs.map +1 -0
  91. package/fesm2022/signality-core-browser-pointer-lock-element.mjs +43 -0
  92. package/fesm2022/signality-core-browser-pointer-lock-element.mjs.map +1 -0
  93. package/fesm2022/signality-core-browser-screen-orientation.mjs +43 -0
  94. package/fesm2022/signality-core-browser-screen-orientation.mjs.map +1 -0
  95. package/fesm2022/signality-core-browser-speech-recognition.mjs +171 -0
  96. package/fesm2022/signality-core-browser-speech-recognition.mjs.map +1 -0
  97. package/fesm2022/signality-core-browser-speech-synthesis.mjs +146 -0
  98. package/fesm2022/signality-core-browser-speech-synthesis.mjs.map +1 -0
  99. package/fesm2022/signality-core-browser-storage.mjs +261 -0
  100. package/fesm2022/signality-core-browser-storage.mjs.map +1 -0
  101. package/fesm2022/signality-core-browser-text-direction.mjs +62 -0
  102. package/fesm2022/signality-core-browser-text-direction.mjs.map +1 -0
  103. package/fesm2022/signality-core-browser-vibration.mjs +94 -0
  104. package/fesm2022/signality-core-browser-vibration.mjs.map +1 -0
  105. package/fesm2022/signality-core-browser-wake-lock.mjs +149 -0
  106. package/fesm2022/signality-core-browser-wake-lock.mjs.map +1 -0
  107. package/fesm2022/signality-core-browser-web-notification.mjs +137 -0
  108. package/fesm2022/signality-core-browser-web-notification.mjs.map +1 -0
  109. package/fesm2022/signality-core-browser-web-share.mjs +92 -0
  110. package/fesm2022/signality-core-browser-web-share.mjs.map +1 -0
  111. package/fesm2022/signality-core-browser-web-worker.mjs +105 -0
  112. package/fesm2022/signality-core-browser-web-worker.mjs.map +1 -0
  113. package/fesm2022/signality-core-browser.mjs +34 -0
  114. package/fesm2022/signality-core-browser.mjs.map +1 -0
  115. package/fesm2022/signality-core-elements-active-element.mjs +88 -0
  116. package/fesm2022/signality-core-elements-active-element.mjs.map +1 -0
  117. package/fesm2022/signality-core-elements-dropzone.mjs +158 -0
  118. package/fesm2022/signality-core-elements-dropzone.mjs.map +1 -0
  119. package/fesm2022/signality-core-elements-element-focus-within.mjs +56 -0
  120. package/fesm2022/signality-core-elements-element-focus-within.mjs.map +1 -0
  121. package/fesm2022/signality-core-elements-element-focus.mjs +54 -0
  122. package/fesm2022/signality-core-elements-element-focus.mjs.map +1 -0
  123. package/fesm2022/signality-core-elements-element-hover.mjs +48 -0
  124. package/fesm2022/signality-core-elements-element-hover.mjs.map +1 -0
  125. package/fesm2022/signality-core-elements-element-size.mjs +73 -0
  126. package/fesm2022/signality-core-elements-element-size.mjs.map +1 -0
  127. package/fesm2022/signality-core-elements-element-visibility.mjs +76 -0
  128. package/fesm2022/signality-core-elements-element-visibility.mjs.map +1 -0
  129. package/fesm2022/signality-core-elements-mouse-position.mjs +109 -0
  130. package/fesm2022/signality-core-elements-mouse-position.mjs.map +1 -0
  131. package/fesm2022/signality-core-elements-on-click-outside.mjs +97 -0
  132. package/fesm2022/signality-core-elements-on-click-outside.mjs.map +1 -0
  133. package/fesm2022/signality-core-elements-on-disconnect.mjs +99 -0
  134. package/fesm2022/signality-core-elements-on-disconnect.mjs.map +1 -0
  135. package/fesm2022/signality-core-elements-on-long-press.mjs +84 -0
  136. package/fesm2022/signality-core-elements-on-long-press.mjs.map +1 -0
  137. package/fesm2022/signality-core-elements-pointer-swipe.mjs +116 -0
  138. package/fesm2022/signality-core-elements-pointer-swipe.mjs.map +1 -0
  139. package/fesm2022/signality-core-elements-scroll-position.mjs +175 -0
  140. package/fesm2022/signality-core-elements-scroll-position.mjs.map +1 -0
  141. package/fesm2022/signality-core-elements-swipe.mjs +107 -0
  142. package/fesm2022/signality-core-elements-swipe.mjs.map +1 -0
  143. package/fesm2022/signality-core-elements-text-selection.mjs +70 -0
  144. package/fesm2022/signality-core-elements-text-selection.mjs.map +1 -0
  145. package/fesm2022/signality-core-elements-window-size.mjs +81 -0
  146. package/fesm2022/signality-core-elements-window-size.mjs.map +1 -0
  147. package/fesm2022/signality-core-elements.mjs +21 -0
  148. package/fesm2022/signality-core-elements.mjs.map +1 -0
  149. package/fesm2022/signality-core-forms-cva.mjs +140 -0
  150. package/fesm2022/signality-core-forms-cva.mjs.map +1 -0
  151. package/fesm2022/signality-core-forms.mjs +6 -0
  152. package/fesm2022/signality-core-forms.mjs.map +1 -0
  153. package/fesm2022/signality-core-internal.mjs +268 -0
  154. package/fesm2022/signality-core-internal.mjs.map +1 -0
  155. package/fesm2022/signality-core-observers-intersection-observer.mjs +70 -0
  156. package/fesm2022/signality-core-observers-intersection-observer.mjs.map +1 -0
  157. package/fesm2022/signality-core-observers-mutation-observer.mjs +77 -0
  158. package/fesm2022/signality-core-observers-mutation-observer.mjs.map +1 -0
  159. package/fesm2022/signality-core-observers-performance-observer.mjs +84 -0
  160. package/fesm2022/signality-core-observers-performance-observer.mjs.map +1 -0
  161. package/fesm2022/signality-core-observers-resize-observer.mjs +69 -0
  162. package/fesm2022/signality-core-observers-resize-observer.mjs.map +1 -0
  163. package/fesm2022/signality-core-observers.mjs +9 -0
  164. package/fesm2022/signality-core-observers.mjs.map +1 -0
  165. package/fesm2022/signality-core-reactivity-debounced.mjs +27 -0
  166. package/fesm2022/signality-core-reactivity-debounced.mjs.map +1 -0
  167. package/fesm2022/signality-core-reactivity-throttled.mjs +27 -0
  168. package/fesm2022/signality-core-reactivity-throttled.mjs.map +1 -0
  169. package/fesm2022/signality-core-reactivity-watcher.mjs +36 -0
  170. package/fesm2022/signality-core-reactivity-watcher.mjs.map +1 -0
  171. package/fesm2022/signality-core-reactivity.mjs +8 -0
  172. package/fesm2022/signality-core-reactivity.mjs.map +1 -0
  173. package/fesm2022/signality-core-router-fragment.mjs +41 -0
  174. package/fesm2022/signality-core-router-fragment.mjs.map +1 -0
  175. package/fesm2022/signality-core-router-params.mjs +45 -0
  176. package/fesm2022/signality-core-router-params.mjs.map +1 -0
  177. package/fesm2022/signality-core-router-query-params.mjs +67 -0
  178. package/fesm2022/signality-core-router-query-params.mjs.map +1 -0
  179. package/fesm2022/signality-core-router-route-data.mjs +46 -0
  180. package/fesm2022/signality-core-router-route-data.mjs.map +1 -0
  181. package/fesm2022/signality-core-router-router-listener.mjs +50 -0
  182. package/fesm2022/signality-core-router-router-listener.mjs.map +1 -0
  183. package/fesm2022/signality-core-router-title.mjs +54 -0
  184. package/fesm2022/signality-core-router-title.mjs.map +1 -0
  185. package/fesm2022/signality-core-router-url.mjs +53 -0
  186. package/fesm2022/signality-core-router-url.mjs.map +1 -0
  187. package/fesm2022/signality-core-router.mjs +12 -0
  188. package/fesm2022/signality-core-router.mjs.map +1 -0
  189. package/fesm2022/signality-core-scheduling-debounce-callback.mjs +59 -0
  190. package/fesm2022/signality-core-scheduling-debounce-callback.mjs.map +1 -0
  191. package/fesm2022/signality-core-scheduling-interval.mjs +110 -0
  192. package/fesm2022/signality-core-scheduling-interval.mjs.map +1 -0
  193. package/fesm2022/signality-core-scheduling-throttle-callback.mjs +66 -0
  194. package/fesm2022/signality-core-scheduling-throttle-callback.mjs.map +1 -0
  195. package/fesm2022/signality-core-scheduling.mjs +8 -0
  196. package/fesm2022/signality-core-scheduling.mjs.map +1 -0
  197. package/fesm2022/signality-core-types.mjs +4 -0
  198. package/fesm2022/signality-core-types.mjs.map +1 -0
  199. package/fesm2022/signality-core.mjs +13 -0
  200. package/fesm2022/signality-core.mjs.map +1 -0
  201. package/forms/cva/index.d.ts +60 -0
  202. package/forms/index.d.ts +1 -0
  203. package/index.d.ts +8 -0
  204. package/internal/constants/index.d.ts +2 -0
  205. package/internal/constants/mobile-regex.d.ts +1 -0
  206. package/internal/constants/stubs.d.ts +32 -0
  207. package/internal/index.d.ts +4 -0
  208. package/internal/providers/index.d.ts +3 -0
  209. package/internal/providers/is-browser.d.ts +2 -0
  210. package/internal/providers/is-mobile.d.ts +2 -0
  211. package/internal/providers/is-server.d.ts +2 -0
  212. package/internal/types/index.d.ts +2 -0
  213. package/internal/types/timer.d.ts +1 -0
  214. package/internal/types/union.d.ts +1 -0
  215. package/internal/utils/bom/index.d.ts +1 -0
  216. package/internal/utils/bom/is-window.d.ts +1 -0
  217. package/internal/utils/const-signal.d.ts +10 -0
  218. package/internal/utils/context.d.ts +18 -0
  219. package/internal/utils/create-token.d.ts +8 -0
  220. package/internal/utils/dom/get-active-element.d.ts +1 -0
  221. package/internal/utils/dom/get-event-target.d.ts +1 -0
  222. package/internal/utils/dom/get-pip-element.d.ts +1 -0
  223. package/internal/utils/dom/get-shadow-root.d.ts +1 -0
  224. package/internal/utils/dom/index.d.ts +6 -0
  225. package/internal/utils/dom/is-element.d.ts +1 -0
  226. package/internal/utils/dom/is-node-within.d.ts +1 -0
  227. package/internal/utils/index.d.ts +10 -0
  228. package/internal/utils/is-plain-object.d.ts +1 -0
  229. package/internal/utils/is-query-signal.d.ts +10 -0
  230. package/internal/utils/proxy-signal.d.ts +18 -0
  231. package/internal/utils/to-element.d.ts +12 -0
  232. package/internal/utils/to-value.d.ts +6 -0
  233. package/observers/index.d.ts +4 -0
  234. package/observers/intersection-observer/index.d.ts +42 -0
  235. package/observers/mutation-observer/index.d.ts +45 -0
  236. package/observers/performance-observer/index.d.ts +58 -0
  237. package/observers/resize-observer/index.d.ts +40 -0
  238. package/package.json +343 -0
  239. package/reactivity/debounced/index.d.ts +50 -0
  240. package/reactivity/index.d.ts +3 -0
  241. package/reactivity/throttled/index.d.ts +53 -0
  242. package/reactivity/watcher/index.d.ts +68 -0
  243. package/router/fragment/index.d.ts +26 -0
  244. package/router/index.d.ts +7 -0
  245. package/router/params/index.d.ts +28 -0
  246. package/router/query-params/index.d.ts +80 -0
  247. package/router/route-data/index.d.ts +28 -0
  248. package/router/router-listener/index.d.ts +83 -0
  249. package/router/title/index.d.ts +29 -0
  250. package/router/url/index.d.ts +32 -0
  251. package/scheduling/debounce-callback/index.d.ts +28 -0
  252. package/scheduling/index.d.ts +3 -0
  253. package/scheduling/interval/index.d.ts +51 -0
  254. package/scheduling/throttle-callback/index.d.ts +30 -0
  255. package/types/index.d.ts +6 -0
  256. package/types/maybe-element-signal.d.ts +2 -0
  257. package/types/maybe-signal.d.ts +2 -0
  258. package/types/signal-value.d.ts +2 -0
  259. package/types/signal-values.d.ts +5 -0
  260. package/types/unref-element.d.ts +2 -0
  261. package/types/with-injector.d.ts +8 -0
@@ -0,0 +1,140 @@
1
+ import { inject, signal, afterNextRender } from '@angular/core';
2
+ import { NgControl, RequiredValidator, Validators, NgModel } from '@angular/forms';
3
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
+ import { timer, switchMap, EMPTY, startWith, map } from 'rxjs';
5
+ import { setupContext, ALWAYS_FALSE_FN } from '@signality/core/internal';
6
+ import { watcher } from '@signality/core/reactivity/watcher';
7
+
8
+ /**
9
+ * Reactive wrapper around the [Angular Forms](https://angular.dev/guide/forms) Control Value Accessor (CVA) pattern.
10
+ * Integrates seamlessly with both template-driven and reactive forms.
11
+ *
12
+ * @param options - Configuration options including the value signal
13
+ * @returns A CvaRef with reactive form control state signals
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * @Component({
18
+ * selector: 'app-currency-input',
19
+ * template: `
20
+ * <input
21
+ * type="text"
22
+ * [value]="displayValue()"
23
+ * [required]="cva.required()"
24
+ * (input)="handleInput($any($event.target).value)"
25
+ * (blur)="cva.touched.set(true)"
26
+ * />
27
+ * `,
28
+ * })
29
+ * export class CurrencyInput {
30
+ * readonly value = model(0);
31
+ * readonly cva = cva({ value: this.value });
32
+ *
33
+ * readonly displayValue = computed(() => {
34
+ * return this.value()
35
+ * .toFixed(2)
36
+ * .replace(/\B(?=(\d{3})+(?!\d))/g, ','); // Shows "1,234.56"
37
+ * });
38
+ *
39
+ * handleInput(input: string) {
40
+ * const num = parseFloat(input.replace(/[^0-9.]/g, ''));
41
+ * if (!isNaN(num)) {
42
+ * this.value.set(num);
43
+ * }
44
+ * }
45
+ * }
46
+ * ```
47
+ */
48
+ function cva(options) {
49
+ const { runInContext } = setupContext(options?.injector, cva);
50
+ return runInContext(({ injector }) => {
51
+ const ngControl = inject(NgControl, { self: true, optional: true });
52
+ const ngModelRequired = inject(RequiredValidator, { self: true, optional: true });
53
+ const initialValue = options.value();
54
+ const { value,
55
+ // Use ALWAYS_FALSE_FN to ensure touched signal always triggers watcher updates.
56
+ // This is critical for controls with `updateOn: 'blur'` - when blur occurs, the control
57
+ // may update its value, but control.touched remains true. Without forcing signal updates,
58
+ // the watcher won't fire and the control won't properly synchronize its value after blur.
59
+ touched = signal(false, { equal: ALWAYS_FALSE_FN }), disabled = signal(false), required = signal(false), invalid = signal(false), pending = signal(false), dirty = signal(false), errors = signal(null), } = options;
60
+ const cvaRef = {
61
+ value,
62
+ touched,
63
+ disabled: disabled.asReadonly(),
64
+ required: required.asReadonly(),
65
+ invalid: invalid.asReadonly(),
66
+ pending: pending.asReadonly(),
67
+ dirty: dirty.asReadonly(),
68
+ errors: errors.asReadonly(),
69
+ reset: () => value.set(initialValue),
70
+ };
71
+ if (!ngControl) {
72
+ return cvaRef;
73
+ }
74
+ let touchedFn;
75
+ let updateFn;
76
+ let scheduledModelUpdate;
77
+ const runModelUpdate = (fn) => {
78
+ scheduledModelUpdate?.destroy();
79
+ fn();
80
+ scheduledModelUpdate = afterNextRender(() => {
81
+ scheduledModelUpdate = null;
82
+ }, { injector });
83
+ };
84
+ watcher(touched, isTouched => {
85
+ if (isTouched) {
86
+ touchedFn?.();
87
+ }
88
+ });
89
+ watcher(value, v => {
90
+ if (scheduledModelUpdate) {
91
+ scheduledModelUpdate.destroy();
92
+ scheduledModelUpdate = null;
93
+ }
94
+ else {
95
+ updateFn?.(v);
96
+ }
97
+ });
98
+ // the control instance isn't available immediately inside FormControl or FormControlName,
99
+ // because they depend on [inputs]. That's why we schedule the subscription asynchronously.
100
+ timer(0)
101
+ .pipe(switchMap(() => {
102
+ const { control } = ngControl;
103
+ if (!control) {
104
+ return EMPTY;
105
+ }
106
+ return control.events.pipe(startWith(null), map(() => control));
107
+ }), takeUntilDestroyed())
108
+ .subscribe(control => {
109
+ required.set(control.hasValidator(Validators.required) ||
110
+ // cannot compare references because `RequiredValidator` wraps `requiredValidator` fn
111
+ // (https://github.com/angular/angular/blob/19.1.0/packages/forms/src/directives/validators.ts#L398)
112
+ !!ngModelRequired?.required);
113
+ touched.set(control.touched);
114
+ invalid.set(control.invalid);
115
+ pending.set(control.pending);
116
+ errors.set(control.errors);
117
+ dirty.set(control.dirty);
118
+ });
119
+ ngControl.valueAccessor = {
120
+ writeValue: (rawValue) => {
121
+ runModelUpdate(() => {
122
+ // fix (https://github.com/angular/angular/issues/14988)
123
+ const modelValue = ngControl instanceof NgModel ? ngControl.model : rawValue;
124
+ value.set(modelValue);
125
+ });
126
+ },
127
+ registerOnChange: fn => (updateFn = fn),
128
+ registerOnTouched: fn => (touchedFn = fn),
129
+ setDisabledState: isDisabled => disabled?.set(isDisabled),
130
+ };
131
+ return cvaRef;
132
+ });
133
+ }
134
+
135
+ /**
136
+ * Generated bundle index. Do not edit.
137
+ */
138
+
139
+ export { cva };
140
+ //# sourceMappingURL=signality-core-forms-cva.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-forms-cva.mjs","sources":["../../../projects/core/forms/cva/index.ts","../../../projects/core/forms/cva/signality-core-forms-cva.ts"],"sourcesContent":["import {\n afterNextRender,\n type AfterRenderRef,\n inject,\n type Signal,\n signal,\n type WritableSignal,\n} from '@angular/core';\nimport {\n NgControl,\n NgModel,\n RequiredValidator,\n type ValidationErrors,\n Validators,\n} from '@angular/forms';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { EMPTY, map, startWith, switchMap, timer } from 'rxjs';\nimport { ALWAYS_FALSE_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { watcher } from '@signality/core/reactivity/watcher';\n\nexport type CvaOptions<T> = Omit<Partial<MakeWritable<CvaRef<T>>>, 'value'> &\n Pick<CvaRef<T>, 'value'> &\n WithInjector;\n\nexport interface CvaRef<T> {\n readonly value: WritableSignal<T>;\n readonly touched: WritableSignal<boolean>;\n readonly disabled: Signal<boolean>;\n readonly required: Signal<boolean>;\n readonly invalid: Signal<boolean>;\n readonly pending: Signal<boolean>;\n readonly dirty: Signal<boolean>;\n readonly errors: Signal<ValidationErrors | null>;\n readonly reset: () => void;\n}\n\n/**\n * Reactive wrapper around the [Angular Forms](https://angular.dev/guide/forms) Control Value Accessor (CVA) pattern.\n * Integrates seamlessly with both template-driven and reactive forms.\n *\n * @param options - Configuration options including the value signal\n * @returns A CvaRef with reactive form control state signals\n *\n * @example\n * ```typescript\n * @Component({\n * selector: 'app-currency-input',\n * template: `\n * <input\n * type=\"text\"\n * [value]=\"displayValue()\"\n * [required]=\"cva.required()\"\n * (input)=\"handleInput($any($event.target).value)\"\n * (blur)=\"cva.touched.set(true)\"\n * />\n * `,\n * })\n * export class CurrencyInput {\n * readonly value = model(0);\n * readonly cva = cva({ value: this.value });\n *\n * readonly displayValue = computed(() => {\n * return this.value()\n * .toFixed(2)\n * .replace(/\\B(?=(\\d{3})+(?!\\d))/g, ','); // Shows \"1,234.56\"\n * });\n *\n * handleInput(input: string) {\n * const num = parseFloat(input.replace(/[^0-9.]/g, ''));\n * if (!isNaN(num)) {\n * this.value.set(num);\n * }\n * }\n * }\n * ```\n */\nexport function cva<T>(options: CvaOptions<T>): CvaRef<T> {\n const { runInContext } = setupContext(options?.injector, cva);\n\n return runInContext(({ injector }) => {\n const ngControl = inject(NgControl, { self: true, optional: true });\n const ngModelRequired = inject(RequiredValidator, { self: true, optional: true });\n\n const initialValue = options.value();\n\n const {\n value,\n // Use ALWAYS_FALSE_FN to ensure touched signal always triggers watcher updates.\n // This is critical for controls with `updateOn: 'blur'` - when blur occurs, the control\n // may update its value, but control.touched remains true. Without forcing signal updates,\n // the watcher won't fire and the control won't properly synchronize its value after blur.\n touched = signal(false, { equal: ALWAYS_FALSE_FN }),\n disabled = signal(false),\n required = signal(false),\n invalid = signal(false),\n pending = signal(false),\n dirty = signal(false),\n errors = signal(null),\n } = options;\n\n const cvaRef: CvaRef<T> = {\n value,\n touched,\n disabled: disabled.asReadonly(),\n required: required.asReadonly(),\n invalid: invalid.asReadonly(),\n pending: pending.asReadonly(),\n dirty: dirty.asReadonly(),\n errors: errors.asReadonly(),\n reset: () => value.set(initialValue),\n };\n\n if (!ngControl) {\n return cvaRef;\n }\n\n let touchedFn: () => void;\n let updateFn: (v: T) => void;\n let scheduledModelUpdate: AfterRenderRef | null;\n\n const runModelUpdate = (fn: () => void) => {\n scheduledModelUpdate?.destroy();\n\n fn();\n\n scheduledModelUpdate = afterNextRender(\n () => {\n scheduledModelUpdate = null;\n },\n { injector }\n );\n };\n\n watcher(touched, isTouched => {\n if (isTouched) {\n touchedFn?.();\n }\n });\n\n watcher(value, v => {\n if (scheduledModelUpdate) {\n scheduledModelUpdate.destroy();\n scheduledModelUpdate = null;\n } else {\n updateFn?.(v);\n }\n });\n\n // the control instance isn't available immediately inside FormControl or FormControlName,\n // because they depend on [inputs]. That's why we schedule the subscription asynchronously.\n timer(0)\n .pipe(\n switchMap(() => {\n const { control } = ngControl;\n\n if (!control) {\n return EMPTY;\n }\n\n return control.events.pipe(\n startWith(null),\n map(() => control)\n );\n }),\n takeUntilDestroyed()\n )\n .subscribe(control => {\n required.set(\n control.hasValidator(Validators.required) ||\n // cannot compare references because `RequiredValidator` wraps `requiredValidator` fn\n // (https://github.com/angular/angular/blob/19.1.0/packages/forms/src/directives/validators.ts#L398)\n !!ngModelRequired?.required\n );\n touched.set(control.touched);\n invalid.set(control.invalid);\n pending.set(control.pending);\n errors.set(control.errors);\n dirty.set(control.dirty);\n });\n\n ngControl.valueAccessor = {\n writeValue: (rawValue: T | null) => {\n runModelUpdate(() => {\n // fix (https://github.com/angular/angular/issues/14988)\n const modelValue = ngControl instanceof NgModel ? ngControl.model : rawValue;\n value.set(modelValue);\n });\n },\n registerOnChange: fn => (updateFn = fn),\n registerOnTouched: fn => (touchedFn = fn),\n setDisabledState: isDisabled => disabled?.set(isDisabled),\n };\n\n return cvaRef;\n });\n}\n\ntype MakeWritable<T extends object> = {\n [K in keyof T]: T[K] extends Signal<infer U> ? WritableSignal<U> : never;\n};\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export * from '@signality/core/forms/cva';
2
+
3
+ /**
4
+ * Generated bundle index. Do not edit.
5
+ */
6
+ //# sourceMappingURL=signality-core-forms.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-forms.mjs","sources":["../../../projects/core/forms/signality-core-forms.ts"],"sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;AAAA;;AAEG"}
@@ -0,0 +1,268 @@
1
+ import { InjectionToken, inject, PLATFORM_ID, assertInInjectionContext, INJECTOR, runInInjectionContext, DestroyRef, untracked, isSignal, ElementRef } from '@angular/core';
2
+ import { isPlatformBrowser, isPlatformServer } from '@angular/common';
3
+ import { SIGNAL, SIGNAL_NODE } from '@angular/core/primitives/signals';
4
+
5
+ const MOBILE_REGEX = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
6
+
7
+ /**
8
+ * Empty synchronous function stub.
9
+ * Used for SSR compatibility when returning method functions in Ref objects that should do nothing on the server.
10
+ *
11
+ * Example: `close: NOOP_FN` in {@link WebNotificationRef}
12
+ */
13
+ const NOOP_FN = () => {
14
+ /* empty */
15
+ };
16
+ /**
17
+ * Empty asynchronous function stub that returns a resolved Promise.
18
+ * Used for SSR compatibility when returning async method functions in Ref objects that should do nothing on the server.
19
+ *
20
+ * Example: `share: NOOP_ASYNC_FN` in {@link WebShareRef}
21
+ */
22
+ const NOOP_ASYNC_FN = () => Promise.resolve();
23
+ /**
24
+ * Frozen EffectRef stub with a no-op destroy method.
25
+ * Used for SSR compatibility when returning EffectRef from observer utilities (ResizeObserver, MutationObserver, etc.)
26
+ * that cannot run on the server. Prevents errors when calling destroy() on server-rendered refs.
27
+ *
28
+ * Example: `return NOOP_EFFECT_REF` in {@link resizeObserver}
29
+ */
30
+ const NOOP_EFFECT_REF = { destroy: NOOP_FN };
31
+ /**
32
+ * Equality function that always returns false, forcing signal updates on every change.
33
+ * Used for signals that hold mutable objects (like Selection, Range) where reference equality is not sufficient
34
+ * and we need to detect changes even when the object structure appears the same.
35
+ *
36
+ * Example: `signal<Selection | null>(null, { equal: ALWAYS_FALSE_FN })` in {@link textSelection}
37
+ */
38
+ const ALWAYS_FALSE_FN = () => false;
39
+
40
+ const IS_BROWSER = new InjectionToken(ngDevMode ? 'IS_BROWSER' : '', {
41
+ providedIn: 'platform',
42
+ factory: () => isPlatformBrowser(inject(PLATFORM_ID)),
43
+ });
44
+
45
+ const IS_MOBILE = new InjectionToken(ngDevMode ? 'IS_MOBILE' : '', {
46
+ providedIn: 'platform',
47
+ factory: () => {
48
+ return inject(IS_BROWSER) ? MOBILE_REGEX.test(navigator.userAgent) : false;
49
+ },
50
+ });
51
+
52
+ const IS_SERVER = new InjectionToken(ngDevMode ? 'IS_SERVER' : '', {
53
+ providedIn: 'platform',
54
+ factory: () => isPlatformServer(inject(PLATFORM_ID)),
55
+ });
56
+
57
+ function isWindow(obj) {
58
+ return !!obj && obj.window === obj;
59
+ }
60
+
61
+ function getActiveElement(document) {
62
+ let activeElement = document.activeElement;
63
+ while (activeElement && activeElement.shadowRoot) {
64
+ const newActiveElement = activeElement.shadowRoot.activeElement;
65
+ if (newActiveElement === activeElement) {
66
+ break;
67
+ }
68
+ else {
69
+ activeElement = newActiveElement;
70
+ }
71
+ }
72
+ return activeElement;
73
+ }
74
+
75
+ function getEventTarget(event) {
76
+ return (event.composedPath ? event.composedPath()[0] : event.target);
77
+ }
78
+
79
+ function getPipElement(document) {
80
+ let pipElement = document.pictureInPictureElement;
81
+ while (pipElement && pipElement.shadowRoot) {
82
+ const newPipElement = pipElement.shadowRoot.pictureInPictureElement;
83
+ if (newPipElement === pipElement) {
84
+ break;
85
+ }
86
+ else {
87
+ pipElement = newPipElement;
88
+ }
89
+ }
90
+ return pipElement;
91
+ }
92
+
93
+ function getShadowRoot(element) {
94
+ const rootNode = element?.getRootNode ? element.getRootNode() : null;
95
+ if (rootNode instanceof ShadowRoot) {
96
+ return rootNode;
97
+ }
98
+ return null;
99
+ }
100
+
101
+ function isElement(obj) {
102
+ return !!obj && obj.nodeType === Node.ELEMENT_NODE;
103
+ }
104
+
105
+ function isNodeWithin(node, root) {
106
+ return root === node || root.contains(node) || (root.shadowRoot?.contains(node) ?? false);
107
+ }
108
+
109
+ /**
110
+ * @internal
111
+ *
112
+ * @param injector - injector to use for context
113
+ * @param debugFn - context owner function
114
+ */
115
+ function setupContext(injector, debugFn) {
116
+ if (typeof ngDevMode !== 'undefined' && ngDevMode && !injector) {
117
+ assertInInjectionContext(debugFn || setupContext);
118
+ }
119
+ const ctxInjector = injector || inject(INJECTOR);
120
+ return {
121
+ runInContext(fn) {
122
+ return runInContextImpl(fn, ctxInjector, debugFn || setupContext);
123
+ },
124
+ };
125
+ }
126
+ function runInContextImpl(fn, injector, debugFn) {
127
+ const result = runInInjectionContext(injector, () => {
128
+ const isBrowser = inject(IS_BROWSER);
129
+ const isServer = inject(IS_SERVER);
130
+ const isMobile = inject(IS_MOBILE);
131
+ const destroyRef = inject(DestroyRef);
132
+ const onCleanup = (cleanupFn) => {
133
+ destroyRef.onDestroy(cleanupFn);
134
+ };
135
+ return untracked(() => fn({ injector, isBrowser, isServer, isMobile, onCleanup }));
136
+ });
137
+ if (typeof ngDevMode !== 'undefined' && ngDevMode && result != null) {
138
+ setupDebugInfo(result, debugFn);
139
+ }
140
+ return result;
141
+ }
142
+ function setupDebugInfo(value, debugFn) {
143
+ if (isSignal(value)) {
144
+ setDebugName(value, debugFn);
145
+ }
146
+ else if (value && typeof value === 'object') {
147
+ for (const [postfix, maybeSignal] of Object.entries(value)) {
148
+ if (isSignal(maybeSignal)) {
149
+ setDebugName(maybeSignal, debugFn, postfix);
150
+ }
151
+ }
152
+ }
153
+ return value;
154
+ }
155
+ function setDebugName(signal, debugFn, postfix) {
156
+ const node = signal[SIGNAL];
157
+ if (node.debugName === undefined) {
158
+ node.debugName = debugFn.name + (postfix ? '.' + postfix : '');
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Creates an Angular InjectionToken with a factory function.
164
+ * @internal
165
+ */
166
+ function createToken(factory, providedIn = 'root') {
167
+ return new InjectionToken(ngDevMode ? factory.name : '', { factory, providedIn });
168
+ }
169
+
170
+ /**
171
+ * Determines if a signal is a query signal (viewChild, contentChild).
172
+ * Query signals have a special internal structure with a `_dirtyCounter` property that tracks
173
+ * when query results change.
174
+
175
+ * See: https://github.com/angular/angular/blob/main/packages/core/src/render3/queries/query_reactive.ts#L43
176
+ * @internal
177
+ */
178
+ function isQuerySignal(val) {
179
+ if (!isSignal(val)) {
180
+ return false;
181
+ }
182
+ const node = val[SIGNAL];
183
+ return '_dirtyCounter' in node;
184
+ }
185
+
186
+ function isPlainObject(value) {
187
+ if (typeof value !== 'object' || value === null) {
188
+ return false;
189
+ }
190
+ const prototype = Object.getPrototypeOf(value);
191
+ return prototype === null || prototype === Object.prototype;
192
+ }
193
+
194
+ /***
195
+ * Creates a lightweight, readonly signal.
196
+ * This is primarily used to provide fallback values for states that cannot be
197
+ * computed in the current environment. For example:
198
+ * - During SSR where browser-only APIs are unavailable
199
+ * - In environments that lack support for specific APIs
200
+ * @internal
201
+ */
202
+ function constSignal(value) {
203
+ const node = Object.create(SIGNAL_NODE);
204
+ node.value = value;
205
+ const getter = (() => node.value);
206
+ getter[SIGNAL] = node;
207
+ if (typeof ngDevMode !== 'undefined' && ngDevMode) {
208
+ const debugName = node.debugName ? ' (' + node.debugName + ')' : '';
209
+ getter.toString = () => `[Signal${debugName}: ${String(node.value)}]`;
210
+ }
211
+ return getter;
212
+ }
213
+
214
+ function proxySignal(source, handler) {
215
+ const node = source[SIGNAL];
216
+ const isWritable = 'set' in source && typeof source.set === 'function';
217
+ const proxy = (handler.get ? () => handler.get(source) : () => source());
218
+ proxy[SIGNAL] = node;
219
+ // @TODO: consider (original toString internally reads from the original getter, bypassing the proxy)
220
+ proxy.toString = source.toString;
221
+ if (isWritable) {
222
+ const set = handler.set
223
+ ? (value) => untracked(() => handler.set(value, source))
224
+ : (value) => source.set(value);
225
+ const update = (updater) => set(updater(node.value));
226
+ proxy.set = set;
227
+ proxy.update = update;
228
+ proxy.asReadonly = () => {
229
+ const getter = source.asReadonly();
230
+ return proxySignal(getter, { get: handler.get?.bind(handler) });
231
+ };
232
+ }
233
+ return proxy;
234
+ }
235
+
236
+ // @TODO: Consider moving it out of internal
237
+ const toValue = (() => {
238
+ const fn = toValueFn;
239
+ fn.untracked = v => toValueFn(v, true);
240
+ return fn;
241
+ })();
242
+ function toValueFn(maybeSignal, untracked$1 = false) {
243
+ if (isSignal(maybeSignal)) {
244
+ return untracked$1 ? untracked(maybeSignal) : maybeSignal();
245
+ }
246
+ return maybeSignal;
247
+ }
248
+
249
+ // @TODO: Consider moving it out of internal
250
+ const toElement = (() => {
251
+ const fn = toElementFn;
252
+ fn.untracked = v => toElementFn(v, true);
253
+ return fn;
254
+ })();
255
+ function toElementFn(maybeSignal, untracked = false) {
256
+ const value = untracked ? toValue.untracked(maybeSignal) : toValue(maybeSignal);
257
+ if (value instanceof ElementRef) {
258
+ return value.nativeElement;
259
+ }
260
+ return value;
261
+ }
262
+
263
+ /**
264
+ * Generated bundle index. Do not edit.
265
+ */
266
+
267
+ export { ALWAYS_FALSE_FN, IS_BROWSER, IS_MOBILE, IS_SERVER, MOBILE_REGEX, NOOP_ASYNC_FN, NOOP_EFFECT_REF, NOOP_FN, constSignal, createToken, getActiveElement, getEventTarget, getPipElement, getShadowRoot, isElement, isNodeWithin, isPlainObject, isQuerySignal, isWindow, proxySignal, setupContext, toElement, toValue };
268
+ //# sourceMappingURL=signality-core-internal.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-internal.mjs","sources":["../../../projects/core/internal/constants/mobile-regex.ts","../../../projects/core/internal/constants/stubs.ts","../../../projects/core/internal/providers/is-browser.ts","../../../projects/core/internal/providers/is-mobile.ts","../../../projects/core/internal/providers/is-server.ts","../../../projects/core/internal/utils/bom/is-window.ts","../../../projects/core/internal/utils/dom/get-active-element.ts","../../../projects/core/internal/utils/dom/get-event-target.ts","../../../projects/core/internal/utils/dom/get-pip-element.ts","../../../projects/core/internal/utils/dom/get-shadow-root.ts","../../../projects/core/internal/utils/dom/is-element.ts","../../../projects/core/internal/utils/dom/is-node-within.ts","../../../projects/core/internal/utils/context.ts","../../../projects/core/internal/utils/create-token.ts","../../../projects/core/internal/utils/is-query-signal.ts","../../../projects/core/internal/utils/is-plain-object.ts","../../../projects/core/internal/utils/const-signal.ts","../../../projects/core/internal/utils/proxy-signal.ts","../../../projects/core/internal/utils/to-value.ts","../../../projects/core/internal/utils/to-element.ts","../../../projects/core/internal/signality-core-internal.ts"],"sourcesContent":["export const MOBILE_REGEX = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;\n","/**\n * Empty synchronous function stub.\n * Used for SSR compatibility when returning method functions in Ref objects that should do nothing on the server.\n *\n * Example: `close: NOOP_FN` in {@link WebNotificationRef}\n */\nexport const NOOP_FN: (...args: any[]) => void = () => {\n /* empty */\n};\n\n/**\n * Empty asynchronous function stub that returns a resolved Promise.\n * Used for SSR compatibility when returning async method functions in Ref objects that should do nothing on the server.\n *\n * Example: `share: NOOP_ASYNC_FN` in {@link WebShareRef}\n */\nexport const NOOP_ASYNC_FN = () => Promise.resolve();\n\n/**\n * Frozen EffectRef stub with a no-op destroy method.\n * Used for SSR compatibility when returning EffectRef from observer utilities (ResizeObserver, MutationObserver, etc.)\n * that cannot run on the server. Prevents errors when calling destroy() on server-rendered refs.\n *\n * Example: `return NOOP_EFFECT_REF` in {@link resizeObserver}\n */\nexport const NOOP_EFFECT_REF = { destroy: NOOP_FN };\n\n/**\n * Equality function that always returns false, forcing signal updates on every change.\n * Used for signals that hold mutable objects (like Selection, Range) where reference equality is not sufficient\n * and we need to detect changes even when the object structure appears the same.\n *\n * Example: `signal<Selection | null>(null, { equal: ALWAYS_FALSE_FN })` in {@link textSelection}\n */\nexport const ALWAYS_FALSE_FN = () => false;\n","import { inject, InjectionToken, PLATFORM_ID } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\n\nexport const IS_BROWSER = new InjectionToken<boolean>(ngDevMode ? 'IS_BROWSER' : '', {\n providedIn: 'platform',\n factory: () => isPlatformBrowser(inject(PLATFORM_ID)),\n});\n","import { inject, InjectionToken } from '@angular/core';\nimport { MOBILE_REGEX } from '../constants';\nimport { IS_BROWSER } from './is-browser';\n\nexport const IS_MOBILE = new InjectionToken<boolean>(ngDevMode ? 'IS_MOBILE' : '', {\n providedIn: 'platform',\n factory: () => {\n return inject(IS_BROWSER) ? MOBILE_REGEX.test(navigator.userAgent) : false;\n },\n});\n","import { inject, InjectionToken, PLATFORM_ID } from '@angular/core';\nimport { isPlatformServer } from '@angular/common';\n\nexport const IS_SERVER = new InjectionToken<boolean>(ngDevMode ? 'IS_SERVER' : '', {\n providedIn: 'platform',\n factory: () => isPlatformServer(inject(PLATFORM_ID)),\n});\n","export function isWindow(obj: object | null): obj is Window {\n return !!obj && (obj as Window).window === obj;\n}\n","export function getActiveElement(document: Document): Element | null {\n let activeElement = document.activeElement;\n\n while (activeElement && activeElement.shadowRoot) {\n const newActiveElement = activeElement.shadowRoot.activeElement;\n if (newActiveElement === activeElement) {\n break;\n } else {\n activeElement = newActiveElement;\n }\n }\n\n return activeElement;\n}\n","export function getEventTarget<T extends EventTarget>(event: Event): T | null {\n return (event.composedPath ? event.composedPath()[0] : event.target) as T | null;\n}\n","export function getPipElement(document: Document): Element | null {\n let pipElement = document.pictureInPictureElement;\n\n while (pipElement && pipElement.shadowRoot) {\n const newPipElement = pipElement.shadowRoot.pictureInPictureElement;\n if (newPipElement === pipElement) {\n break;\n } else {\n pipElement = newPipElement;\n }\n }\n\n return pipElement;\n}\n","export function getShadowRoot(element: Element | null): ShadowRoot | null {\n const rootNode = element?.getRootNode ? element.getRootNode() : null;\n\n if (rootNode instanceof ShadowRoot) {\n return rootNode;\n }\n\n return null;\n}\n","export function isElement(obj: Element | EventTarget | Node | object | null): obj is Element {\n return !!obj && (obj as Element).nodeType === Node.ELEMENT_NODE;\n}\n","export function isNodeWithin(node: Node, root: Element): boolean {\n return root === node || root.contains(node) || (root.shadowRoot?.contains(node) ?? false);\n}\n","import {\n assertInInjectionContext,\n DestroyRef,\n inject,\n type Injector,\n INJECTOR,\n isSignal,\n runInInjectionContext,\n type Signal,\n untracked,\n} from '@angular/core';\nimport { SIGNAL, type SignalNode } from '@angular/core/primitives/signals';\nimport { IS_BROWSER, IS_MOBILE, IS_SERVER } from '../providers';\n\nexport interface ContextRef {\n readonly injector: Injector;\n readonly isServer: boolean;\n readonly isBrowser: boolean;\n readonly isMobile: boolean;\n readonly onCleanup: (cleanupFn: () => void) => void;\n}\n\nexport interface SetupContextRef {\n runInContext<T>(fn: (context: ContextRef) => T): T;\n}\n\n/**\n * @internal\n *\n * @param injector - injector to use for context\n * @param debugFn - context owner function\n */\nexport function setupContext(\n injector?: Injector,\n debugFn?: (...args: any[]) => any\n): SetupContextRef {\n if (typeof ngDevMode !== 'undefined' && ngDevMode && !injector) {\n assertInInjectionContext(debugFn || setupContext);\n }\n\n const ctxInjector = injector || inject(INJECTOR);\n\n return {\n runInContext<T>(fn: (context: ContextRef) => T): T {\n return runInContextImpl(fn, ctxInjector, debugFn || setupContext);\n },\n };\n}\n\nfunction runInContextImpl<T>(\n fn: (context: ContextRef) => T,\n injector: Injector,\n debugFn: (...args: any[]) => any\n): T {\n const result = runInInjectionContext(injector, () => {\n const isBrowser = inject(IS_BROWSER);\n const isServer = inject(IS_SERVER);\n const isMobile = inject(IS_MOBILE);\n const destroyRef = inject(DestroyRef);\n\n const onCleanup = (cleanupFn: () => void) => {\n destroyRef.onDestroy(cleanupFn);\n };\n\n return untracked(() => fn({ injector, isBrowser, isServer, isMobile, onCleanup }));\n });\n\n if (typeof ngDevMode !== 'undefined' && ngDevMode && result != null) {\n setupDebugInfo(result, debugFn);\n }\n\n return result;\n}\n\nfunction setupDebugInfo<T>(value: T, debugFn: (...args: any[]) => any): T {\n if (isSignal(value)) {\n setDebugName(value, debugFn);\n } else if (value && typeof value === 'object') {\n for (const [postfix, maybeSignal] of Object.entries(value)) {\n if (isSignal(maybeSignal)) {\n setDebugName(maybeSignal, debugFn, postfix);\n }\n }\n }\n\n return value;\n}\n\nfunction setDebugName(\n signal: Signal<unknown>,\n debugFn: (...args: any[]) => any,\n postfix?: string\n): void {\n const node = signal[SIGNAL] as SignalNode<unknown>;\n\n if (node.debugName === undefined) {\n node.debugName = debugFn.name + (postfix ? '.' + postfix : '');\n }\n}\n","import { InjectionToken, type ProviderToken } from '@angular/core';\n\n/**\n * Creates an Angular InjectionToken with a factory function.\n * @internal\n */\nexport function createToken<T>(\n factory: () => T,\n providedIn: ProvidedIn = 'root'\n): ProviderToken<T> {\n return new InjectionToken(ngDevMode ? factory.name : '', { factory, providedIn });\n}\n\ntype ProvidedIn = NonNullable<ConstructorParameters<typeof InjectionToken>[1]>['providedIn'];\n","import { isSignal, type Signal } from '@angular/core';\nimport { SIGNAL } from '@angular/core/primitives/signals';\n\n/**\n * Determines if a signal is a query signal (viewChild, contentChild).\n * Query signals have a special internal structure with a `_dirtyCounter` property that tracks\n * when query results change.\n\n * See: https://github.com/angular/angular/blob/main/packages/core/src/render3/queries/query_reactive.ts#L43\n * @internal\n */\nexport function isQuerySignal(val: unknown): val is Signal<unknown> {\n if (!isSignal(val)) {\n return false;\n }\n\n const node = val[SIGNAL] as object;\n return '_dirtyCounter' in node;\n}\n","export function isPlainObject(value: unknown): value is Record<PropertyKey, unknown> {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const prototype = Object.getPrototypeOf(value);\n return prototype === null || prototype === Object.prototype;\n}\n","import type { Signal } from '@angular/core';\nimport {\n SIGNAL,\n SIGNAL_NODE,\n type SignalGetter,\n type SignalNode,\n} from '@angular/core/primitives/signals';\n\n/***\n * Creates a lightweight, readonly signal.\n * This is primarily used to provide fallback values for states that cannot be\n * computed in the current environment. For example:\n * - During SSR where browser-only APIs are unavailable\n * - In environments that lack support for specific APIs\n * @internal\n */\nexport function constSignal<T>(value: T): Signal<T> {\n const node: SignalNode<T> = Object.create(SIGNAL_NODE);\n node.value = value;\n\n const getter = (() => node.value) as SignalGetter<T>;\n (getter as any)[SIGNAL] = node;\n\n if (typeof ngDevMode !== 'undefined' && ngDevMode) {\n const debugName = node.debugName ? ' (' + node.debugName + ')' : '';\n getter.toString = () => `[Signal${debugName}: ${String(node.value)}]`;\n }\n\n return getter;\n}\n","import { type Signal, untracked, type WritableSignal } from '@angular/core';\nimport { SIGNAL, type SignalNode } from '@angular/core/primitives/signals';\n\n/**\n * @internal\n */\nexport interface SignalProxyHandler<T> {\n get?(source: Signal<T>): T;\n set?(value: T, source: WritableSignal<T>): void;\n}\n\n/**\n * Creates a proxy wrapper around a {@link Signal}\n * @internal\n */\nexport function proxySignal<T>(\n source: Signal<T>,\n handler: Omit<SignalProxyHandler<T>, 'set'>\n): Signal<T>;\n\n/**\n * Creates a proxy wrapper around a {@link WritableSignal}\n * @internal\n */\nexport function proxySignal<T>(\n source: WritableSignal<T>,\n handler: SignalProxyHandler<T>\n): WritableSignal<T>;\n\nexport function proxySignal<T>(\n source: Signal<T> | WritableSignal<T>,\n handler: SignalProxyHandler<T>\n): Signal<T> | WritableSignal<T> {\n const node = source[SIGNAL] as SignalNode<T>;\n const isWritable = 'set' in source && typeof source.set === 'function';\n\n const proxy = (handler.get ? () => handler.get!(source) : () => source()) as Signal<T>;\n\n proxy[SIGNAL] = node;\n\n // @TODO: consider (original toString internally reads from the original getter, bypassing the proxy)\n proxy.toString = source.toString;\n\n if (isWritable) {\n const set = handler.set\n ? (value: T) => untracked(() => handler.set!(value, source))\n : (value: T) => source.set(value);\n\n const update = (updater: (current: T) => T) => set(updater(node.value));\n\n (proxy as WritableSignal<T>).set = set;\n (proxy as WritableSignal<T>).update = update;\n (proxy as WritableSignal<T>).asReadonly = () => {\n const getter = source.asReadonly();\n return proxySignal(getter, { get: handler.get?.bind(handler) });\n };\n }\n\n return proxy;\n}\n","import { isSignal, untracked as _untracked } from '@angular/core';\nimport type { MaybeSignal } from '@signality/core/types';\n\nexport interface ToValueFn {\n <T>(maybeSignal: MaybeSignal<T>): T;\n untracked: <T>(maybeSignal: MaybeSignal<T>) => T;\n}\n\n// @TODO: Consider moving it out of internal\nexport const toValue: ToValueFn = (() => {\n const fn = toValueFn as ToValueFn;\n fn.untracked = v => toValueFn(v, true);\n return fn;\n})();\n\nfunction toValueFn<T>(maybeSignal: MaybeSignal<T>, untracked = false): T {\n if (isSignal(maybeSignal)) {\n return untracked ? _untracked(maybeSignal) : maybeSignal();\n }\n return maybeSignal;\n}\n","import { ElementRef, type Signal } from '@angular/core';\nimport type { MaybeElementSignal } from '@signality/core/types';\nimport { toValue } from './to-value';\n\nexport interface ToElementFn extends ToElementBase {\n untracked: ToElementBase;\n}\n\nexport interface ToElementBase {\n <T extends Element>(element: T | ElementRef<T>): T;\n <T extends Element>(element: Signal<T | ElementRef<T> | null>): T | null;\n <T extends Element>(element: Signal<T | ElementRef<T> | undefined>): T | undefined;\n <T extends Element>(element: Signal<T | ElementRef<T> | null | undefined>): T | null | undefined;\n <T extends Element>(element: T | ElementRef<T> | Signal<T | ElementRef<T> | null | undefined>):\n | T\n | null\n | undefined;\n}\n\n// @TODO: Consider moving it out of internal\nexport const toElement: ToElementFn = (() => {\n const fn = toElementFn as ToElementFn;\n fn.untracked = v => toElementFn(v, true);\n return fn;\n})();\n\nfunction toElementFn<T extends Element>(\n maybeSignal: MaybeElementSignal<T>,\n untracked = false\n): T | null | undefined {\n const value = untracked ? toValue.untracked(maybeSignal) : toValue(maybeSignal);\n\n if (value instanceof ElementRef) {\n return value.nativeElement;\n }\n\n return value;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["untracked","_untracked"],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { afterRenderEffect } from '@angular/core';
2
+ import { setupContext, NOOP_EFFECT_REF, toElement, toValue } from '@signality/core/internal';
3
+
4
+ /**
5
+ * Low-level utility for observing element intersection with viewport using the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
6
+ * Provides fine-grained control over the observation lifecycle.
7
+ *
8
+ * @param target - Element(s) to observe
9
+ * @param callback - Callback function called when intersection changes
10
+ * @param options - Optional configuration (see {@link IntersectionObserverInitOptions})
11
+ * @returns An IntersectionObserverRef with a `destroy()` method to stop observing the element(s)
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * @Component({
16
+ * template: `
17
+ * <div #section>Section content</div>
18
+ * @if (isVisible()) {
19
+ * <p>Section is visible!</p>
20
+ * }
21
+ * `
22
+ * })
23
+ * class IntersectionComponent {
24
+ * readonly section = viewChild<ElementRef>('section');
25
+ * readonly isVisible = signal(false);
26
+ *
27
+ * constructor() {
28
+ * intersectionObserver(this.section, entries => {
29
+ * this.isVisible.set(entries[0].isIntersecting);
30
+ * }, { threshold: 0.5 });
31
+ * }
32
+ * }
33
+ * ```
34
+ */
35
+ function intersectionObserver(target, callback, options) {
36
+ const { runInContext } = setupContext(options?.injector, intersectionObserver);
37
+ return runInContext(({ isServer }) => {
38
+ if (isServer) {
39
+ return NOOP_EFFECT_REF;
40
+ }
41
+ const targets = Array.isArray(target) ? target : [target];
42
+ const setupObserver = (onCleanup) => {
43
+ const els = targets.map(toElement).filter(Boolean);
44
+ if (!els.length) {
45
+ return;
46
+ }
47
+ const root = options?.root
48
+ ? options.root instanceof Document
49
+ ? options.root
50
+ : toElement(options.root)
51
+ : null;
52
+ const rootMargin = toValue(options?.rootMargin);
53
+ const threshold = toValue(options?.threshold);
54
+ const observer = new IntersectionObserver(callback, { root, rootMargin, threshold });
55
+ for (const el of els) {
56
+ observer.observe(el);
57
+ }
58
+ onCleanup(observer.disconnect.bind(observer));
59
+ };
60
+ const effectRef = afterRenderEffect({ read: setupObserver }, options);
61
+ return { destroy: () => effectRef.destroy() };
62
+ });
63
+ }
64
+
65
+ /**
66
+ * Generated bundle index. Do not edit.
67
+ */
68
+
69
+ export { intersectionObserver };
70
+ //# sourceMappingURL=signality-core-observers-intersection-observer.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-observers-intersection-observer.mjs","sources":["../../../projects/core/observers/intersection-observer/index.ts","../../../projects/core/observers/intersection-observer/signality-core-observers-intersection-observer.ts"],"sourcesContent":["import {\n afterRenderEffect,\n type CreateEffectOptions,\n type EffectCleanupRegisterFn,\n} from '@angular/core';\nimport { NOOP_EFFECT_REF, setupContext, toElement, toValue } from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal } from '@signality/core/types';\n\nexport interface IntersectionObserverInitOptions\n extends Omit<CreateEffectOptions, 'allowSignalWrites'> {\n readonly root?: MaybeElementSignal<Element> | Document | null;\n readonly rootMargin?: MaybeSignal<string>;\n readonly threshold?: MaybeSignal<number | number[]>;\n}\n\nexport interface IntersectionObserverRef {\n readonly destroy: () => void;\n}\n\n/**\n * Low-level utility for observing element intersection with viewport using the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).\n * Provides fine-grained control over the observation lifecycle.\n *\n * @param target - Element(s) to observe\n * @param callback - Callback function called when intersection changes\n * @param options - Optional configuration (see {@link IntersectionObserverInitOptions})\n * @returns An IntersectionObserverRef with a `destroy()` method to stop observing the element(s)\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #section>Section content</div>\n * @if (isVisible()) {\n * <p>Section is visible!</p>\n * }\n * `\n * })\n * class IntersectionComponent {\n * readonly section = viewChild<ElementRef>('section');\n * readonly isVisible = signal(false);\n *\n * constructor() {\n * intersectionObserver(this.section, entries => {\n * this.isVisible.set(entries[0].isIntersecting);\n * }, { threshold: 0.5 });\n * }\n * }\n * ```\n */\nexport function intersectionObserver(\n target: MaybeElementSignal<Element> | MaybeElementSignal<Element>[],\n callback: (entries: readonly IntersectionObserverEntry[], observer: IntersectionObserver) => void,\n options?: IntersectionObserverInitOptions\n): IntersectionObserverRef {\n const { runInContext } = setupContext(options?.injector, intersectionObserver);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n const targets = Array.isArray(target) ? target : [target];\n\n const setupObserver = (onCleanup: EffectCleanupRegisterFn) => {\n const els = targets.map(toElement).filter(Boolean);\n\n if (!els.length) {\n return;\n }\n\n const root = options?.root\n ? options.root instanceof Document\n ? options.root\n : toElement(options.root)\n : null;\n const rootMargin = toValue(options?.rootMargin);\n const threshold = toValue(options?.threshold);\n\n const observer = new IntersectionObserver(callback, { root, rootMargin, threshold });\n\n for (const el of els) {\n observer.observe(el!);\n }\n\n onCleanup(observer.disconnect.bind(observer));\n };\n\n const effectRef = afterRenderEffect({ read: setupObserver }, options);\n\n return { destroy: () => effectRef.destroy() };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAmBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BG;SACa,oBAAoB,CAClC,MAAmE,EACnE,QAAiG,EACjG,OAAyC,EAAA;AAEzC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,oBAAoB,CAAC;AAE9E,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,eAAe;QACxB;AAEA,QAAA,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC;AAEzD,QAAA,MAAM,aAAa,GAAG,CAAC,SAAkC,KAAI;AAC3D,YAAA,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;AAElD,YAAA,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE;gBACf;YACF;AAEA,YAAA,MAAM,IAAI,GAAG,OAAO,EAAE;AACpB,kBAAE,OAAO,CAAC,IAAI,YAAY;sBACtB,OAAO,CAAC;AACV,sBAAE,SAAS,CAAC,OAAO,CAAC,IAAI;kBACxB,IAAI;YACR,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC;YAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC;AAE7C,YAAA,MAAM,QAAQ,GAAG,IAAI,oBAAoB,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AAEpF,YAAA,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE;AACpB,gBAAA,QAAQ,CAAC,OAAO,CAAC,EAAG,CAAC;YACvB;YAEA,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC/C,QAAA,CAAC;AAED,QAAA,MAAM,SAAS,GAAG,iBAAiB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,OAAO,CAAC;QAErE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE;AAC/C,IAAA,CAAC,CAAC;AACJ;;AC5FA;;AAEG;;;;"}
@@ -0,0 +1,77 @@
1
+ import { afterRenderEffect } from '@angular/core';
2
+ import { setupContext, NOOP_EFFECT_REF, toElement, toValue } from '@signality/core/internal';
3
+
4
+ /**
5
+ * Low-level utility for observing DOM tree changes using the [MutationObserver API](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver).
6
+ * Provides fine-grained control over the observation lifecycle.
7
+ *
8
+ * @param target - Element(s) to observe
9
+ * @param callback - Callback function called when DOM mutations occur
10
+ * @param options - Optional configuration (see {@link MutationObserverInitOptions})
11
+ * @returns MutationObserverRef with a `destroy()` method to stop observing the element(s)
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * @Component({
16
+ * template: `
17
+ * <div #container>
18
+ * <p>Children: {{ childCount() }}</p>
19
+ * </div>
20
+ * `
21
+ * })
22
+ * class MutationComponent {
23
+ * readonly container = viewChild<ElementRef>('container');
24
+ * readonly childCount = signal(0);
25
+ *
26
+ * constructor() {
27
+ * mutationObserver(this.container, mutations => {
28
+ * this.childCount.set(mutations[0].target.childNodes.length);
29
+ * }, { childList: true });
30
+ * }
31
+ * }
32
+ * ```
33
+ */
34
+ function mutationObserver(target, callback, options) {
35
+ const { runInContext } = setupContext(options.injector, mutationObserver);
36
+ return runInContext(({ isServer }) => {
37
+ if (isServer) {
38
+ return NOOP_EFFECT_REF;
39
+ }
40
+ const targets = Array.isArray(target) ? target : [target];
41
+ const setupObserver = (onCleanup) => {
42
+ const els = targets.map(toElement).filter(Boolean);
43
+ if (!els.length) {
44
+ return;
45
+ }
46
+ const childList = toValue(options.childList);
47
+ const attributes = toValue(options.attributes);
48
+ const characterData = toValue(options.characterData);
49
+ const subtree = toValue(options.subtree);
50
+ const attributeOldValue = toValue(options.attributeOldValue);
51
+ const characterDataOldValue = toValue(options.characterDataOldValue);
52
+ const attributeFilter = toValue(options.attributeFilter);
53
+ const observer = new MutationObserver(callback);
54
+ for (const el of els) {
55
+ observer.observe(el, {
56
+ childList,
57
+ attributes,
58
+ characterData,
59
+ subtree,
60
+ attributeOldValue,
61
+ characterDataOldValue,
62
+ attributeFilter,
63
+ });
64
+ }
65
+ onCleanup(observer.disconnect.bind(observer));
66
+ };
67
+ const effectRef = afterRenderEffect({ read: setupObserver }, options);
68
+ return { destroy: () => effectRef.destroy() };
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Generated bundle index. Do not edit.
74
+ */
75
+
76
+ export { mutationObserver };
77
+ //# sourceMappingURL=signality-core-observers-mutation-observer.mjs.map