@signality/core 0.0.1-alpha.2 → 0.0.1-alpha.3

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 (219) hide show
  1. package/browser/battery/index.d.ts +27 -2
  2. package/browser/bluetooth/index.d.ts +40 -13
  3. package/browser/breakpoints/index.d.ts +22 -9
  4. package/browser/broadcast-channel/index.d.ts +1 -1
  5. package/browser/browser-language/index.d.ts +1 -1
  6. package/browser/clipboard/index.d.ts +22 -6
  7. package/browser/device-posture/index.d.ts +23 -2
  8. package/browser/display-media/index.d.ts +34 -22
  9. package/browser/eye-dropper/index.d.ts +22 -6
  10. package/browser/favicon/index.d.ts +29 -2
  11. package/browser/file-dialog/index.d.ts +97 -0
  12. package/browser/fps/index.d.ts +1 -1
  13. package/browser/fullscreen/index.d.ts +78 -0
  14. package/browser/gamepad/index.d.ts +39 -9
  15. package/browser/geolocation/index.d.ts +44 -13
  16. package/browser/index.d.ts +8 -1
  17. package/browser/input-modality/index.d.ts +1 -1
  18. package/browser/listener/index.d.ts +1 -1
  19. package/browser/media-query/index.d.ts +1 -1
  20. package/browser/network/index.d.ts +37 -9
  21. package/browser/online/index.d.ts +1 -1
  22. package/browser/page-visibility/index.d.ts +1 -1
  23. package/browser/permission-state/index.d.ts +23 -0
  24. package/browser/picture-in-picture/index.d.ts +24 -6
  25. package/browser/screen-orientation/index.d.ts +1 -1
  26. package/browser/speech-recognition/index.d.ts +51 -13
  27. package/browser/speech-synthesis/index.d.ts +82 -42
  28. package/browser/storage/index.d.ts +1 -1
  29. package/browser/text-direction/index.d.ts +2 -5
  30. package/{elements → browser}/text-selection/index.d.ts +1 -1
  31. package/browser/vibration/index.d.ts +38 -9
  32. package/browser/wake-lock/index.d.ts +51 -12
  33. package/browser/web-notification/index.d.ts +35 -9
  34. package/browser/web-share/index.d.ts +19 -5
  35. package/browser/web-worker/index.d.ts +35 -11
  36. package/browser/window-focus/index.d.ts +27 -0
  37. package/{elements → browser}/window-size/index.d.ts +6 -7
  38. package/elements/active-element/index.d.ts +1 -1
  39. package/elements/dropzone/index.d.ts +60 -10
  40. package/elements/element-focus/index.d.ts +1 -1
  41. package/elements/element-focus-within/index.d.ts +1 -1
  42. package/elements/element-hover/index.d.ts +1 -1
  43. package/elements/element-size/index.d.ts +8 -5
  44. package/elements/element-visibility/index.d.ts +25 -7
  45. package/elements/index.d.ts +0 -2
  46. package/elements/mouse-position/index.d.ts +27 -7
  47. package/elements/on-click-outside/index.d.ts +1 -1
  48. package/elements/on-disconnect/index.d.ts +1 -1
  49. package/elements/on-long-press/index.d.ts +1 -1
  50. package/elements/pointer-swipe/index.d.ts +1 -1
  51. package/elements/scroll-position/index.d.ts +2 -2
  52. package/elements/swipe/index.d.ts +1 -1
  53. package/fesm2022/signality-core-browser-battery.mjs +1 -1
  54. package/fesm2022/signality-core-browser-battery.mjs.map +1 -1
  55. package/fesm2022/signality-core-browser-bluetooth.mjs +28 -27
  56. package/fesm2022/signality-core-browser-bluetooth.mjs.map +1 -1
  57. package/fesm2022/signality-core-browser-breakpoints.mjs +19 -10
  58. package/fesm2022/signality-core-browser-breakpoints.mjs.map +1 -1
  59. package/fesm2022/signality-core-browser-broadcast-channel.mjs +1 -1
  60. package/fesm2022/signality-core-browser-broadcast-channel.mjs.map +1 -1
  61. package/fesm2022/signality-core-browser-browser-language.mjs +1 -1
  62. package/fesm2022/signality-core-browser-browser-language.mjs.map +1 -1
  63. package/fesm2022/signality-core-browser-clipboard.mjs +1 -1
  64. package/fesm2022/signality-core-browser-clipboard.mjs.map +1 -1
  65. package/fesm2022/signality-core-browser-device-posture.mjs +13 -0
  66. package/fesm2022/signality-core-browser-device-posture.mjs.map +1 -1
  67. package/fesm2022/signality-core-browser-display-media.mjs +4 -17
  68. package/fesm2022/signality-core-browser-display-media.mjs.map +1 -1
  69. package/fesm2022/signality-core-browser-eye-dropper.mjs +1 -1
  70. package/fesm2022/signality-core-browser-eye-dropper.mjs.map +1 -1
  71. package/fesm2022/signality-core-browser-favicon.mjs +2 -2
  72. package/fesm2022/signality-core-browser-favicon.mjs.map +1 -1
  73. package/fesm2022/signality-core-browser-file-dialog.mjs +109 -0
  74. package/fesm2022/signality-core-browser-file-dialog.mjs.map +1 -0
  75. package/fesm2022/signality-core-browser-fps.mjs +1 -1
  76. package/fesm2022/signality-core-browser-fps.mjs.map +1 -1
  77. package/fesm2022/signality-core-browser-fullscreen.mjs +113 -0
  78. package/fesm2022/signality-core-browser-fullscreen.mjs.map +1 -0
  79. package/fesm2022/signality-core-browser-gamepad.mjs +14 -4
  80. package/fesm2022/signality-core-browser-gamepad.mjs.map +1 -1
  81. package/fesm2022/signality-core-browser-geolocation.mjs +8 -19
  82. package/fesm2022/signality-core-browser-geolocation.mjs.map +1 -1
  83. package/fesm2022/signality-core-browser-input-modality.mjs +1 -1
  84. package/fesm2022/signality-core-browser-input-modality.mjs.map +1 -1
  85. package/fesm2022/signality-core-browser-listener.mjs +18 -6
  86. package/fesm2022/signality-core-browser-listener.mjs.map +1 -1
  87. package/fesm2022/signality-core-browser-media-query.mjs +1 -1
  88. package/fesm2022/signality-core-browser-media-query.mjs.map +1 -1
  89. package/fesm2022/signality-core-browser-network.mjs +2 -2
  90. package/fesm2022/signality-core-browser-network.mjs.map +1 -1
  91. package/fesm2022/signality-core-browser-online.mjs +1 -1
  92. package/fesm2022/signality-core-browser-online.mjs.map +1 -1
  93. package/fesm2022/signality-core-browser-page-visibility.mjs +1 -1
  94. package/fesm2022/signality-core-browser-page-visibility.mjs.map +1 -1
  95. package/fesm2022/signality-core-browser-permission-state.mjs +57 -0
  96. package/fesm2022/signality-core-browser-permission-state.mjs.map +1 -0
  97. package/fesm2022/signality-core-browser-picture-in-picture.mjs +30 -13
  98. package/fesm2022/signality-core-browser-picture-in-picture.mjs.map +1 -1
  99. package/fesm2022/signality-core-browser-screen-orientation.mjs +1 -1
  100. package/fesm2022/signality-core-browser-screen-orientation.mjs.map +1 -1
  101. package/fesm2022/signality-core-browser-speech-recognition.mjs +7 -19
  102. package/fesm2022/signality-core-browser-speech-recognition.mjs.map +1 -1
  103. package/fesm2022/signality-core-browser-speech-synthesis.mjs +14 -16
  104. package/fesm2022/signality-core-browser-speech-synthesis.mjs.map +1 -1
  105. package/fesm2022/signality-core-browser-storage.mjs +1 -1
  106. package/fesm2022/signality-core-browser-storage.mjs.map +1 -1
  107. package/fesm2022/signality-core-browser-text-direction.mjs +1 -4
  108. package/fesm2022/signality-core-browser-text-direction.mjs.map +1 -1
  109. package/fesm2022/{signality-core-elements-text-selection.mjs → signality-core-browser-text-selection.mjs} +2 -2
  110. package/fesm2022/signality-core-browser-text-selection.mjs.map +1 -0
  111. package/fesm2022/signality-core-browser-vibration.mjs +14 -5
  112. package/fesm2022/signality-core-browser-vibration.mjs.map +1 -1
  113. package/fesm2022/signality-core-browser-wake-lock.mjs +33 -16
  114. package/fesm2022/signality-core-browser-wake-lock.mjs.map +1 -1
  115. package/fesm2022/signality-core-browser-web-notification.mjs +5 -7
  116. package/fesm2022/signality-core-browser-web-notification.mjs.map +1 -1
  117. package/fesm2022/signality-core-browser-web-share.mjs +3 -5
  118. package/fesm2022/signality-core-browser-web-share.mjs.map +1 -1
  119. package/fesm2022/signality-core-browser-web-worker.mjs +6 -3
  120. package/fesm2022/signality-core-browser-web-worker.mjs.map +1 -1
  121. package/fesm2022/signality-core-browser-window-focus.mjs +48 -0
  122. package/fesm2022/signality-core-browser-window-focus.mjs.map +1 -0
  123. package/fesm2022/{signality-core-elements-window-size.mjs → signality-core-browser-window-size.mjs} +4 -24
  124. package/fesm2022/signality-core-browser-window-size.mjs.map +1 -0
  125. package/fesm2022/signality-core-browser.mjs +8 -1
  126. package/fesm2022/signality-core-browser.mjs.map +1 -1
  127. package/fesm2022/signality-core-elements-active-element.mjs +1 -1
  128. package/fesm2022/signality-core-elements-active-element.mjs.map +1 -1
  129. package/fesm2022/signality-core-elements-dropzone.mjs +28 -29
  130. package/fesm2022/signality-core-elements-dropzone.mjs.map +1 -1
  131. package/fesm2022/signality-core-elements-element-focus-within.mjs +1 -1
  132. package/fesm2022/signality-core-elements-element-focus-within.mjs.map +1 -1
  133. package/fesm2022/signality-core-elements-element-focus.mjs +1 -1
  134. package/fesm2022/signality-core-elements-element-focus.mjs.map +1 -1
  135. package/fesm2022/signality-core-elements-element-hover.mjs +1 -1
  136. package/fesm2022/signality-core-elements-element-hover.mjs.map +1 -1
  137. package/fesm2022/signality-core-elements-element-size.mjs +19 -24
  138. package/fesm2022/signality-core-elements-element-size.mjs.map +1 -1
  139. package/fesm2022/signality-core-elements-element-visibility.mjs +2 -2
  140. package/fesm2022/signality-core-elements-element-visibility.mjs.map +1 -1
  141. package/fesm2022/signality-core-elements-mouse-position.mjs +3 -3
  142. package/fesm2022/signality-core-elements-mouse-position.mjs.map +1 -1
  143. package/fesm2022/signality-core-elements-on-click-outside.mjs +1 -1
  144. package/fesm2022/signality-core-elements-on-click-outside.mjs.map +1 -1
  145. package/fesm2022/signality-core-elements-on-disconnect.mjs +1 -1
  146. package/fesm2022/signality-core-elements-on-disconnect.mjs.map +1 -1
  147. package/fesm2022/signality-core-elements-on-long-press.mjs +1 -1
  148. package/fesm2022/signality-core-elements-on-long-press.mjs.map +1 -1
  149. package/fesm2022/signality-core-elements-pointer-swipe.mjs +1 -1
  150. package/fesm2022/signality-core-elements-pointer-swipe.mjs.map +1 -1
  151. package/fesm2022/signality-core-elements-scroll-position.mjs +2 -2
  152. package/fesm2022/signality-core-elements-scroll-position.mjs.map +1 -1
  153. package/fesm2022/signality-core-elements-swipe.mjs +1 -1
  154. package/fesm2022/signality-core-elements-swipe.mjs.map +1 -1
  155. package/fesm2022/signality-core-elements.mjs +0 -2
  156. package/fesm2022/signality-core-elements.mjs.map +1 -1
  157. package/fesm2022/signality-core-internal.mjs +54 -7
  158. package/fesm2022/signality-core-internal.mjs.map +1 -1
  159. package/fesm2022/signality-core-observers-intersection-observer.mjs +3 -2
  160. package/fesm2022/signality-core-observers-intersection-observer.mjs.map +1 -1
  161. package/fesm2022/signality-core-observers-mutation-observer.mjs +3 -2
  162. package/fesm2022/signality-core-observers-mutation-observer.mjs.map +1 -1
  163. package/fesm2022/signality-core-observers-resize-observer.mjs +3 -2
  164. package/fesm2022/signality-core-observers-resize-observer.mjs.map +1 -1
  165. package/fesm2022/signality-core-observers.mjs +0 -1
  166. package/fesm2022/signality-core-observers.mjs.map +1 -1
  167. package/fesm2022/signality-core-reactivity-debounced.mjs.map +1 -1
  168. package/fesm2022/signality-core-reactivity-throttled.mjs.map +1 -1
  169. package/fesm2022/signality-core-reactivity-watcher.mjs.map +1 -1
  170. package/fesm2022/signality-core-router-fragment.mjs +1 -1
  171. package/fesm2022/signality-core-router-fragment.mjs.map +1 -1
  172. package/fesm2022/signality-core-router-params.mjs +1 -1
  173. package/fesm2022/signality-core-router-params.mjs.map +1 -1
  174. package/fesm2022/signality-core-router-query-params.mjs.map +1 -1
  175. package/fesm2022/signality-core-router-route-data.mjs +1 -1
  176. package/fesm2022/signality-core-router-route-data.mjs.map +1 -1
  177. package/fesm2022/signality-core-router-router-listener.mjs.map +1 -1
  178. package/fesm2022/signality-core-router-title.mjs +1 -1
  179. package/fesm2022/signality-core-router-title.mjs.map +1 -1
  180. package/fesm2022/signality-core-router-url.mjs +1 -1
  181. package/fesm2022/signality-core-router-url.mjs.map +1 -1
  182. package/fesm2022/signality-core-scheduling-debounce-callback.mjs +1 -1
  183. package/fesm2022/signality-core-scheduling-debounce-callback.mjs.map +1 -1
  184. package/fesm2022/signality-core-scheduling-interval.mjs +27 -72
  185. package/fesm2022/signality-core-scheduling-interval.mjs.map +1 -1
  186. package/internal/utils/assert.d.ts +2 -0
  187. package/internal/utils/dom/index.d.ts +1 -0
  188. package/internal/utils/dom/is-element.d.ts +1 -1
  189. package/internal/utils/dom/is-event-target.d.ts +1 -0
  190. package/internal/utils/files/index.d.ts +1 -0
  191. package/internal/utils/files/is-accepted-file.d.ts +11 -0
  192. package/internal/utils/index.d.ts +3 -0
  193. package/internal/utils/to-element.d.ts +1 -1
  194. package/internal/utils/unref-element.d.ts +2 -0
  195. package/observers/index.d.ts +0 -1
  196. package/observers/intersection-observer/index.d.ts +22 -1
  197. package/observers/mutation-observer/index.d.ts +43 -1
  198. package/observers/resize-observer/index.d.ts +13 -1
  199. package/package.json +25 -17
  200. package/reactivity/debounced/index.d.ts +2 -2
  201. package/reactivity/throttled/index.d.ts +2 -2
  202. package/reactivity/watcher/index.d.ts +2 -2
  203. package/router/fragment/index.d.ts +1 -1
  204. package/router/params/index.d.ts +1 -1
  205. package/router/query-params/index.d.ts +5 -3
  206. package/router/route-data/index.d.ts +1 -1
  207. package/router/router-listener/index.d.ts +1 -1
  208. package/router/title/index.d.ts +1 -1
  209. package/router/url/index.d.ts +1 -1
  210. package/scheduling/debounce-callback/index.d.ts +1 -1
  211. package/scheduling/interval/index.d.ts +19 -27
  212. package/browser/pointer-lock-element/index.d.ts +0 -22
  213. package/fesm2022/signality-core-browser-pointer-lock-element.mjs +0 -43
  214. package/fesm2022/signality-core-browser-pointer-lock-element.mjs.map +0 -1
  215. package/fesm2022/signality-core-elements-text-selection.mjs.map +0 -1
  216. package/fesm2022/signality-core-elements-window-size.mjs.map +0 -1
  217. package/fesm2022/signality-core-observers-performance-observer.mjs +0 -84
  218. package/fesm2022/signality-core-observers-performance-observer.mjs.map +0 -1
  219. package/observers/performance-observer/index.d.ts +0 -58
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-geolocation.mjs","sources":["../../../projects/core/browser/geolocation/index.ts","../../../projects/core/browser/geolocation/signality-core-browser-geolocation.ts"],"sourcesContent":["import { computed, type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\n\nexport interface GeolocationOptions extends WithInjector {\n /**\n * Start tracking immediately.\n * @default true\n */\n readonly immediate?: boolean;\n\n /**\n * Use GPS for better accuracy.\n * @default true\n */\n readonly enableHighAccuracy?: boolean;\n\n /**\n * Max age of cached position (ms).\n * @default 0\n */\n readonly maximumAge?: number;\n\n /**\n * Request timeout (ms).\n * @default Infinity\n */\n readonly timeout?: number;\n}\n\nexport interface GeolocationRef {\n /** Whether Geolocation is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Full position object with timestamp */\n readonly position: Signal<GeolocationPosition | null>;\n\n /** Last error */\n readonly error: Signal<GeolocationPositionError | null>;\n\n /** Whether location tracking is currently active */\n readonly isActive: Signal<boolean>;\n\n /** Whether currently fetching location */\n readonly isLoading: Signal<boolean>;\n\n /** Start/resume watching position */\n readonly start: () => void;\n\n /** Stop watching position */\n readonly stop: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API).\n *\n * @param options - Configuration options\n * @returns A GeolocationRef with location signals and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (geo.isLoading()) {\n * <p>Getting your location...</p>\n * } @else if (geo.position()?.coords; as coords) {\n * <p>{{ coords.latitude }}, {{ coords.longitude }}</p>\n * }\n * <button (click)=\"geo.stop()\">Stop</button>\n * <button (click)=\"geo.start()\">Start</button>\n * })\n * class LocationComponent {\n * readonly geo = geolocation();\n * }\n * ```\n */\nexport function geolocation(options?: GeolocationOptions): GeolocationRef {\n const { runInContext } = setupContext(options?.injector, geolocation);\n\n return runInContext(({ isBrowser, onCleanup }) => {\n const isSupported = constSignal(isBrowser && 'geolocation' in navigator);\n\n if (!isSupported()) {\n return {\n isSupported,\n position: constSignal(null),\n error: constSignal(null),\n isLoading: constSignal(false),\n isActive: constSignal(false),\n start: NOOP_FN,\n stop: NOOP_FN,\n };\n }\n\n const immediate = options?.immediate ?? true;\n\n const positionOptions: PositionOptions = {\n enableHighAccuracy: options?.enableHighAccuracy ?? true,\n maximumAge: options?.maximumAge ?? 0,\n timeout: options?.timeout ?? Infinity,\n };\n\n const position = signal<GeolocationPosition | null>(null);\n const error = signal<GeolocationPositionError | null>(null);\n const isLoading = signal(false);\n const watchId = signal<number | undefined>(undefined);\n\n const handleSuccess = (pos: GeolocationPosition) => {\n position.set(pos);\n error.set(null);\n isLoading.set(false);\n };\n\n const handleError = (err: GeolocationPositionError) => {\n error.set(err);\n isLoading.set(false);\n };\n\n const start = () => {\n untracked(() => {\n if (watchId() !== undefined) {\n return;\n }\n\n isLoading.set(true);\n const id = navigator.geolocation.watchPosition(handleSuccess, handleError, positionOptions);\n watchId.set(id);\n });\n };\n\n const stop = () => {\n untracked(() => {\n const currentId = watchId();\n\n if (currentId !== undefined) {\n navigator.geolocation.clearWatch(currentId);\n watchId.set(undefined);\n }\n\n isLoading.set(false);\n });\n };\n\n const abortController = new AbortController();\n\n onCleanup(() => {\n abortController.abort();\n stop();\n });\n\n navigator.permissions.query({ name: 'geolocation' }).then(status => {\n if (abortController.signal.aborted) {\n return;\n }\n\n const check = () => {\n if (status.state === 'denied') {\n stop();\n }\n };\n\n check();\n\n status.addEventListener('change', check, {\n signal: abortController.signal,\n });\n });\n\n if (immediate) {\n start();\n }\n\n return {\n isSupported,\n position: position.asReadonly(),\n error: error.asReadonly(),\n isLoading: isLoading.asReadonly(),\n isActive: computed(() => watchId() !== undefined),\n start,\n stop,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"signality-core-browser-geolocation.mjs","sources":["../../../projects/core/browser/geolocation/index.ts","../../../projects/core/browser/geolocation/signality-core-browser-geolocation.ts"],"sourcesContent":["import { computed, type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { watcher } from '@signality/core/reactivity/watcher';\nimport { permissionState } from '@signality/core/browser/permission-state';\n\nexport interface GeolocationOptions extends WithInjector {\n /**\n * Start tracking immediately on initialization.\n *\n * @default false\n */\n readonly immediate?: boolean;\n\n /**\n * Use GPS for higher accuracy. May be slower and consume more power.\n *\n * @default true\n * @see [PositionOptions: enableHighAccuracy on MDN](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions/enableHighAccuracy)\n */\n readonly enableHighAccuracy?: boolean;\n\n /**\n * Maximum age of a cached position in milliseconds. `0` forces a fresh lookup.\n *\n * @default 0\n * @see [PositionOptions: maximumAge on MDN](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions/maximumAge)\n */\n readonly maximumAge?: number;\n\n /**\n * Maximum time in milliseconds allowed to retrieve a position before erroring.\n *\n * @default Infinity\n * @see [PositionOptions: timeout on MDN](https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions/timeout)\n */\n readonly timeout?: number;\n}\n\nexport interface GeolocationRef {\n /**\n * Whether the Geolocation API is supported in the current browser.\n *\n * @see [Geolocation API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * The most recent position, including coordinates and timestamp. `null` until first fix.\n *\n * @see [GeolocationPosition on MDN](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPosition)\n */\n readonly position: Signal<GeolocationPosition | null>;\n\n /**\n * The last error returned by the Geolocation API, or `null` if no error.\n *\n * @see [GeolocationPositionError on MDN](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError)\n */\n readonly error: Signal<GeolocationPositionError | null>;\n\n /**\n * Whether position watching is currently active.\n */\n readonly isActive: Signal<boolean>;\n\n /**\n * Whether a position fix is currently being fetched.\n */\n readonly isLoading: Signal<boolean>;\n\n /**\n * Start watching the device position.\n *\n * @see [Geolocation: watchPosition() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition)\n */\n readonly start: () => void;\n\n /**\n * Stop watching the device position.\n *\n * @see [Geolocation: clearWatch() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/clearWatch)\n */\n readonly stop: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API).\n *\n * @param options - Configuration options\n * @returns A GeolocationRef with location signals and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (geo.isLoading()) {\n * <p>Getting your location...</p>\n * } @else if (geo.position()?.coords; as coords) {\n * <p>{{ coords.latitude }}, {{ coords.longitude }}</p>\n * }\n * <button (click)=\"geo.stop()\">Stop</button>\n * <button (click)=\"geo.start()\">Start</button>\n * })\n * export class LocationDemo {\n * readonly geo = geolocation();\n * }\n * ```\n */\nexport function geolocation(options?: GeolocationOptions): GeolocationRef {\n const { runInContext } = setupContext(options?.injector, geolocation);\n\n return runInContext(({ isBrowser, onCleanup }) => {\n const isSupported = constSignal(isBrowser && 'geolocation' in navigator);\n\n if (!isSupported()) {\n return {\n isSupported,\n position: constSignal(null),\n error: constSignal(null),\n isLoading: constSignal(false),\n isActive: constSignal(false),\n start: NOOP_FN,\n stop: NOOP_FN,\n };\n }\n\n const immediate = options?.immediate ?? false;\n\n const positionOptions: PositionOptions = {\n enableHighAccuracy: options?.enableHighAccuracy ?? true,\n maximumAge: options?.maximumAge ?? 0,\n timeout: options?.timeout ?? Infinity,\n };\n\n const position = signal<GeolocationPosition | null>(null);\n const error = signal<GeolocationPositionError | null>(null);\n const isLoading = signal(false);\n const watchId = signal<number | undefined>(undefined);\n\n const handleSuccess = (pos: GeolocationPosition) => {\n position.set(pos);\n error.set(null);\n isLoading.set(false);\n };\n\n const handleError = (err: GeolocationPositionError) => {\n error.set(err);\n isLoading.set(false);\n };\n\n const start = () => {\n untracked(() => {\n if (watchId() !== undefined) {\n return;\n }\n\n isLoading.set(true);\n const id = navigator.geolocation.watchPosition(handleSuccess, handleError, positionOptions);\n watchId.set(id);\n });\n };\n\n const stop = () => {\n untracked(() => {\n const currentId = watchId();\n\n if (currentId !== undefined) {\n navigator.geolocation.clearWatch(currentId);\n watchId.set(undefined);\n }\n\n isLoading.set(false);\n });\n };\n\n onCleanup(stop);\n\n watcher(permissionState('geolocation'), state => {\n if (state === 'denied') {\n stop();\n }\n });\n\n if (immediate) {\n start();\n }\n\n return {\n isSupported,\n position: position.asReadonly(),\n error: error.asReadonly(),\n isLoading: isLoading.asReadonly(),\n isActive: computed(() => watchId() !== undefined),\n start,\n stop,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -20,7 +20,7 @@ const MODIFIER_KEYS = ['Shift', 'Control', 'Alt', 'Meta', 'ContextMenu'];
20
20
  * </div>
21
21
  * `
22
22
  * })
23
- * export class ModalityComponent {
23
+ * export class ModalityDemo {
24
24
  * readonly modality = inputModality();
25
25
  * }
26
26
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-input-modality.mjs","sources":["../../../projects/core/browser/input-modality/index.ts","../../../projects/core/browser/input-modality/signality-core-browser-input-modality.ts"],"sourcesContent":["import { signal, type Signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type InputModality = 'keyboard' | 'mouse' | 'touch' | null;\n\nconst TOUCH_BUFFER_MS = 650;\n\nconst MODIFIER_KEYS = ['Shift', 'Control', 'Alt', 'Meta', 'ContextMenu'];\n\n/**\n * Reactively track the user's current input method (keyboard, mouse, or touch).\n *\n * @param options - Optional configuration including injector\n * @returns A signal containing the current input modality: `'keyboard'`, `'mouse'`, `'touch'`, or `null`\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div [class.keyboard]=\"modality() === 'keyboard'\">\n * <p>Current input: {{ modality() ?? 'none' }}</p>\n * <button>Button with conditional focus ring</button>\n * </div>\n * `\n * })\n * export class ModalityComponent {\n * readonly modality = inputModality();\n * }\n * ```\n */\nexport function inputModality(options?: WithInjector): Signal<InputModality> {\n const { runInContext } = setupContext(options?.injector, inputModality);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(null);\n }\n\n const modality = signal<InputModality>(null);\n\n let lastTouchMs = 0;\n\n setupSync(() => {\n listener(document, 'keydown', (event: KeyboardEvent) => {\n if (MODIFIER_KEYS.includes(event.key)) {\n return;\n }\n modality.set('keyboard');\n });\n\n listener(document, 'mousedown', () => {\n if (Date.now() - lastTouchMs < TOUCH_BUFFER_MS) {\n return;\n }\n modality.set('mouse');\n });\n\n listener.passive(document, 'touchstart', () => {\n lastTouchMs = Date.now();\n modality.set('touch');\n });\n });\n\n return modality.asReadonly();\n });\n}\n\nexport const INPUT_MODALITY = /* @__PURE__ */ createToken(inputModality);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAOA,MAAM,eAAe,GAAG,GAAG;AAE3B,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;AAoBG;AACG,SAAU,aAAa,CAAC,OAAsB,EAAA;AAClD,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC;AAEvE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,IAAI,CAAC;QAC1B;AAEA,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAgB,IAAI,oDAAC;QAE5C,IAAI,WAAW,GAAG,CAAC;QAEnB,SAAS,CAAC,MAAK;YACb,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC,KAAoB,KAAI;gBACrD,IAAI,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;oBACrC;gBACF;AACA,gBAAA,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC;AAC1B,YAAA,CAAC,CAAC;AAEF,YAAA,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAK;gBACnC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,GAAG,eAAe,EAAE;oBAC9C;gBACF;AACA,gBAAA,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;AACvB,YAAA,CAAC,CAAC;YAEF,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAK;AAC5C,gBAAA,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE;AACxB,gBAAA,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;AACvB,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,QAAQ,CAAC,UAAU,EAAE;AAC9B,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,cAAc,mBAAmB,WAAW,CAAC,aAAa;;ACrEvE;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-input-modality.mjs","sources":["../../../projects/core/browser/input-modality/index.ts","../../../projects/core/browser/input-modality/signality-core-browser-input-modality.ts"],"sourcesContent":["import { signal, type Signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type InputModality = 'keyboard' | 'mouse' | 'touch' | null;\n\nconst TOUCH_BUFFER_MS = 650;\n\nconst MODIFIER_KEYS = ['Shift', 'Control', 'Alt', 'Meta', 'ContextMenu'];\n\n/**\n * Reactively track the user's current input method (keyboard, mouse, or touch).\n *\n * @param options - Optional configuration including injector\n * @returns A signal containing the current input modality: `'keyboard'`, `'mouse'`, `'touch'`, or `null`\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div [class.keyboard]=\"modality() === 'keyboard'\">\n * <p>Current input: {{ modality() ?? 'none' }}</p>\n * <button>Button with conditional focus ring</button>\n * </div>\n * `\n * })\n * export class ModalityDemo {\n * readonly modality = inputModality();\n * }\n * ```\n */\nexport function inputModality(options?: WithInjector): Signal<InputModality> {\n const { runInContext } = setupContext(options?.injector, inputModality);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(null);\n }\n\n const modality = signal<InputModality>(null);\n\n let lastTouchMs = 0;\n\n setupSync(() => {\n listener(document, 'keydown', (event: KeyboardEvent) => {\n if (MODIFIER_KEYS.includes(event.key)) {\n return;\n }\n modality.set('keyboard');\n });\n\n listener(document, 'mousedown', () => {\n if (Date.now() - lastTouchMs < TOUCH_BUFFER_MS) {\n return;\n }\n modality.set('mouse');\n });\n\n listener.passive(document, 'touchstart', () => {\n lastTouchMs = Date.now();\n modality.set('touch');\n });\n });\n\n return modality.asReadonly();\n });\n}\n\nexport const INPUT_MODALITY = /* @__PURE__ */ createToken(inputModality);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAOA,MAAM,eAAe,GAAG,GAAG;AAE3B,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;AAoBG;AACG,SAAU,aAAa,CAAC,OAAsB,EAAA;AAClD,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC;AAEvE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,IAAI,CAAC;QAC1B;AAEA,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAgB,IAAI,oDAAC;QAE5C,IAAI,WAAW,GAAG,CAAC;QAEnB,SAAS,CAAC,MAAK;YACb,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC,KAAoB,KAAI;gBACrD,IAAI,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;oBACrC;gBACF;AACA,gBAAA,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC;AAC1B,YAAA,CAAC,CAAC;AAEF,YAAA,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAK;gBACnC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,GAAG,eAAe,EAAE;oBAC9C;gBACF;AACA,gBAAA,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;AACvB,YAAA,CAAC,CAAC;YAEF,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAK;AAC5C,gBAAA,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE;AACxB,gBAAA,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;AACvB,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,QAAQ,CAAC,UAAU,EAAE;AAC9B,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,cAAc,mBAAmB,WAAW,CAAC,aAAa;;ACrEvE;;AAEG;;;;"}
@@ -1,6 +1,6 @@
1
- import { afterRenderEffect, effect } from '@angular/core';
1
+ import { untracked, afterRenderEffect, effect } from '@angular/core';
2
2
  import { SIGNAL } from '@angular/core/primitives/signals';
3
- import { setupContext, NOOP_EFFECT_REF, toElement, toValue } from '@signality/core/internal';
3
+ import { setupContext, NOOP_EFFECT_REF, toValue, unrefElement, assertEventTarget } from '@signality/core/internal';
4
4
 
5
5
  /**
6
6
  * Signal-based wrapper around the [addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) method.
@@ -16,7 +16,7 @@ import { setupContext, NOOP_EFFECT_REF, toElement, toValue } from '@signality/co
16
16
  * @Component({
17
17
  * template: `<button #btn>Click me</button>`,
18
18
  * })
19
- * class ListenerComponent {
19
+ * export class ListenerDemo {
20
20
  * readonly btn = viewChild<ElementRef>('btn');
21
21
  *
22
22
  * constructor() {
@@ -65,15 +65,27 @@ function listenerImpl(applied, ...args) {
65
65
  rawHandler.call(this, event);
66
66
  }
67
67
  : rawHandler;
68
+ // Isolate event handler from signal dependency tracking.
69
+ // Angular's template compiler does this via setActiveConsumer(null) in executeListenerWithErrorHandling.
70
+ // Without this, events that fire during change detection (e.g. window 'blur' when the tab loses focus)
71
+ // can trigger NG0600 if the handler writes to a signal while a reactive consumer is active.
72
+ // See: https://github.com/angular/angular/issues/60143
73
+ const untrackedHandler = function (event) {
74
+ untracked(() => handler.call(this, event));
75
+ };
68
76
  const setupListener = (onCleanup) => {
69
- const target = toElement(maybeReactiveTarget);
77
+ const raw = toValue(maybeReactiveTarget);
78
+ const target = unrefElement(raw);
70
79
  const event = toValue(maybeReactiveEvent);
71
80
  if (!target) {
72
81
  return;
73
82
  }
74
- target.addEventListener(event, handler, nativeOptions);
83
+ if (ngDevMode) {
84
+ assertEventTarget(target, 'listener');
85
+ }
86
+ target.addEventListener(event, untrackedHandler, nativeOptions);
75
87
  onCleanup(() => {
76
- target.removeEventListener(event, handler, nativeOptions);
88
+ target.removeEventListener(event, untrackedHandler, nativeOptions);
77
89
  });
78
90
  };
79
91
  let effectRef;
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-listener.mjs","sources":["../../../projects/core/browser/listener/index.ts","../../../projects/core/browser/listener/signality-core-browser-listener.ts"],"sourcesContent":["import {\n afterRenderEffect,\n type CreateEffectOptions,\n effect,\n type EffectCleanupRegisterFn,\n type EffectRef,\n} from '@angular/core';\nimport { type BaseEffectNode, SIGNAL } from '@angular/core/primitives/signals';\nimport { NOOP_EFFECT_REF, setupContext, toElement, toValue } from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\n\nexport type ListenerOptions = WithInjector;\n\nexport interface ListenerRef {\n readonly destroy: () => void;\n}\n\nexport interface ListenerFunction {\n <E extends keyof WindowEventMap>(\n target: Window,\n event: MaybeSignal<E>,\n handler: (this: Window, e: WindowEventMap[E]) => any,\n options?: ListenerOptions\n ): ListenerRef;\n\n <E extends keyof DocumentEventMap>(\n target: Document,\n event: MaybeSignal<E>,\n handler: (this: Document, e: DocumentEventMap[E]) => any,\n options?: ListenerOptions\n ): ListenerRef;\n\n <E extends keyof ShadowRootEventMap>(\n target: MaybeSignal<ShadowRoot>,\n event: MaybeSignal<E>,\n handler: (this: ShadowRoot, e: ShadowRootEventMap[E]) => any,\n options?: ListenerOptions\n ): ListenerRef;\n\n <T extends HTMLElement, E extends keyof HTMLElementEventMap>(\n target: MaybeElementSignal<T>,\n event: MaybeSignal<E>,\n handler: (this: T, e: HTMLElementEventMap[E]) => any,\n options?: ListenerOptions\n ): ListenerRef;\n\n <T extends SVGElement, E extends keyof SVGElementEventMap>(\n target: MaybeElementSignal<T>,\n event: MaybeSignal<E>,\n handler: (this: T, e: SVGElementEventMap[E]) => any,\n options?: ListenerOptions\n ): ListenerRef;\n\n <Names extends string>(\n target: MaybeSignal<InferEventTarget<Names>>,\n event: MaybeSignal<Names>,\n handler: (e: Event) => void,\n options?: ListenerOptions\n ): ListenerRef;\n\n <EventType = Event>(\n target: MaybeSignal<EventTarget> | MaybeElementSignal<Element>,\n event: MaybeSignal<string>,\n handler: GeneralEventListener<EventType>,\n options?: ListenerOptions\n ): ListenerRef;\n\n readonly capture: ListenerFunction;\n readonly passive: ListenerFunction;\n readonly once: ListenerFunction;\n readonly stop: ListenerFunction;\n readonly prevent: ListenerFunction;\n readonly self: ListenerFunction;\n}\n\n/**\n * Signal-based wrapper around the [addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) method.\n *\n * @param target - Event target\n * @param event - Event type name\n * @param handler - Event handler function\n * @param options - Optional listener configuration\n * @returns A ListenerRef that can be used to destroy the listener\n *\n * @example\n * ```typescript\n * @Component({\n * template: `<button #btn>Click me</button>`,\n * })\n * class ListenerComponent {\n * readonly btn = viewChild<ElementRef>('btn');\n *\n * constructor() {\n * listener.capture.prevent(this.btn, 'click', event => {\n * console.log('Button clicked!', event);\n * });\n * }\n * }\n * ```\n */\nexport const listener: ListenerFunction = createModifier({});\n\nlet isSyncSetupRequired = false;\n\n/**\n * By default, `listener()` registers event listeners after the render cycle completes\n * to ensure DOM elements exist. However, global targets (window, document, navigator.*, etc.)\n * are not tied to the render cycle. Use `setupSync()` to wrap listener calls when you need to prevent\n * race conditions where a global event is dispatched before Angular completes its scheduled rendering tasks.\n */\nexport function setupSync<T>(listenerFactoryExecFn: () => T): T {\n isSyncSetupRequired = true;\n\n try {\n return listenerFactoryExecFn();\n } finally {\n isSyncSetupRequired = false;\n }\n}\n\nfunction listenerImpl(applied: InternalListenerOptions, ...args: any[]): ListenerRef {\n const options = args[3] as ListenerOptions | undefined;\n\n const { runInContext } = setupContext(options?.injector, listenerImpl);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n const [maybeReactiveTarget, maybeReactiveEvent, rawHandler] = args;\n const { stop, prevent, self, ...nativeOptions } = applied;\n const hasModifiers = stop || prevent || self;\n\n const handler = hasModifiers\n ? function (this: any, event: Event) {\n if (self && event.target !== event.currentTarget) return;\n if (prevent) event.preventDefault();\n if (stop) event.stopPropagation();\n rawHandler.call(this, event);\n }\n : rawHandler;\n\n const setupListener = (onCleanup: EffectCleanupRegisterFn) => {\n const target = toElement(maybeReactiveTarget);\n const event = toValue(maybeReactiveEvent);\n\n if (!target) {\n return;\n }\n\n target.addEventListener(event, handler, nativeOptions);\n\n onCleanup(() => {\n target.removeEventListener(event, handler, nativeOptions);\n });\n };\n\n let effectRef: EffectRef;\n\n if (isSyncSetupRequired) {\n effectRef = syncEffect(setupListener);\n } else {\n effectRef = afterRenderEffect({ read: setupListener });\n }\n\n return { destroy: () => effectRef.destroy() };\n });\n}\n\nconst MODIFIERS = new Set<keyof InternalListenerOptions>([\n 'capture',\n 'passive',\n 'once',\n 'stop',\n 'prevent',\n 'self',\n]);\n\nfunction createModifier(applied: InternalListenerOptions): ListenerFunction {\n const modifierFn = ((...args: any[]) => {\n return listenerImpl(applied, ...args);\n }) as ListenerFunction;\n\n return new Proxy(modifierFn, {\n get(target, prop) {\n if (typeof prop !== 'string' || !MODIFIERS.has(prop as any)) {\n return target[prop as keyof typeof target];\n }\n\n if (applied[prop as keyof InternalListenerOptions]) {\n return target;\n }\n\n return createModifier({ ...applied, [prop]: true });\n },\n });\n}\n\nfunction syncEffect(\n effectFn: (onCleanup: EffectCleanupRegisterFn) => void,\n options?: CreateEffectOptions\n): EffectRef {\n const effectRef = effect(effectFn, options);\n const effectNode: BaseEffectNode = (effectRef as any)[SIGNAL];\n try {\n effectNode.run();\n } catch (error) {\n if (ngDevMode) {\n console.warn('[syncEffect] Failed to run effectFn synchronously', error);\n }\n }\n return effectRef;\n}\n\ninterface InternalListenerOptions {\n readonly capture?: boolean;\n readonly passive?: boolean;\n readonly once?: boolean;\n readonly stop?: boolean;\n readonly prevent?: boolean;\n readonly self?: boolean;\n}\n\ninterface InferEventTarget<Events> {\n readonly addEventListener: (event: Events, fn?: any, options?: any) => any;\n readonly removeEventListener: (event: Events, fn?: any, options?: any) => any;\n}\n\ninterface GeneralEventListener<E = Event> {\n (e: E): void;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"signality-core-browser-listener.mjs","sources":["../../../projects/core/browser/listener/index.ts","../../../projects/core/browser/listener/signality-core-browser-listener.ts"],"sourcesContent":["import {\n afterRenderEffect,\n type CreateEffectOptions,\n effect,\n type EffectCleanupRegisterFn,\n type EffectRef,\n untracked,\n} from '@angular/core';\nimport { type BaseEffectNode, SIGNAL } from '@angular/core/primitives/signals';\nimport {\n assertEventTarget,\n NOOP_EFFECT_REF,\n setupContext,\n toValue,\n unrefElement,\n} from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\n\nexport type ListenerOptions = WithInjector;\n\nexport interface ListenerRef {\n readonly destroy: () => void;\n}\n\nexport interface ListenerFunction {\n <E extends keyof WindowEventMap>(\n target: Window,\n event: MaybeSignal<E>,\n handler: (this: Window, e: WindowEventMap[E]) => any,\n options?: ListenerOptions\n ): ListenerRef;\n\n <E extends keyof DocumentEventMap>(\n target: Document,\n event: MaybeSignal<E>,\n handler: (this: Document, e: DocumentEventMap[E]) => any,\n options?: ListenerOptions\n ): ListenerRef;\n\n <E extends keyof ShadowRootEventMap>(\n target: MaybeSignal<ShadowRoot>,\n event: MaybeSignal<E>,\n handler: (this: ShadowRoot, e: ShadowRootEventMap[E]) => any,\n options?: ListenerOptions\n ): ListenerRef;\n\n <T extends HTMLElement, E extends keyof HTMLElementEventMap>(\n target: MaybeElementSignal<T>,\n event: MaybeSignal<E>,\n handler: (this: T, e: HTMLElementEventMap[E]) => any,\n options?: ListenerOptions\n ): ListenerRef;\n\n <T extends SVGElement, E extends keyof SVGElementEventMap>(\n target: MaybeElementSignal<T>,\n event: MaybeSignal<E>,\n handler: (this: T, e: SVGElementEventMap[E]) => any,\n options?: ListenerOptions\n ): ListenerRef;\n\n <Names extends string>(\n target: MaybeSignal<InferEventTarget<Names>>,\n event: MaybeSignal<Names>,\n handler: (e: Event) => void,\n options?: ListenerOptions\n ): ListenerRef;\n\n <EventType = Event>(\n target: MaybeSignal<EventTarget> | MaybeElementSignal<Element>,\n event: MaybeSignal<string>,\n handler: GeneralEventListener<EventType>,\n options?: ListenerOptions\n ): ListenerRef;\n\n readonly capture: ListenerFunction;\n readonly passive: ListenerFunction;\n readonly once: ListenerFunction;\n readonly stop: ListenerFunction;\n readonly prevent: ListenerFunction;\n readonly self: ListenerFunction;\n}\n\n/**\n * Signal-based wrapper around the [addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) method.\n *\n * @param target - Event target\n * @param event - Event type name\n * @param handler - Event handler function\n * @param options - Optional listener configuration\n * @returns A ListenerRef that can be used to destroy the listener\n *\n * @example\n * ```typescript\n * @Component({\n * template: `<button #btn>Click me</button>`,\n * })\n * export class ListenerDemo {\n * readonly btn = viewChild<ElementRef>('btn');\n *\n * constructor() {\n * listener.capture.prevent(this.btn, 'click', event => {\n * console.log('Button clicked!', event);\n * });\n * }\n * }\n * ```\n */\nexport const listener: ListenerFunction = createModifier({});\n\nlet isSyncSetupRequired = false;\n\n/**\n * By default, `listener()` registers event listeners after the render cycle completes\n * to ensure DOM elements exist. However, global targets (window, document, navigator.*, etc.)\n * are not tied to the render cycle. Use `setupSync()` to wrap listener calls when you need to prevent\n * race conditions where a global event is dispatched before Angular completes its scheduled rendering tasks.\n */\nexport function setupSync<T>(listenerFactoryExecFn: () => T): T {\n isSyncSetupRequired = true;\n\n try {\n return listenerFactoryExecFn();\n } finally {\n isSyncSetupRequired = false;\n }\n}\n\nfunction listenerImpl(applied: InternalListenerOptions, ...args: any[]): ListenerRef {\n const options = args[3] as ListenerOptions | undefined;\n\n const { runInContext } = setupContext(options?.injector, listenerImpl);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n const [maybeReactiveTarget, maybeReactiveEvent, rawHandler] = args;\n const { stop, prevent, self, ...nativeOptions } = applied;\n const hasModifiers = stop || prevent || self;\n\n const handler = hasModifiers\n ? function (this: any, event: Event) {\n if (self && event.target !== event.currentTarget) return;\n if (prevent) event.preventDefault();\n if (stop) event.stopPropagation();\n rawHandler.call(this, event);\n }\n : rawHandler;\n\n // Isolate event handler from signal dependency tracking.\n // Angular's template compiler does this via setActiveConsumer(null) in executeListenerWithErrorHandling.\n // Without this, events that fire during change detection (e.g. window 'blur' when the tab loses focus)\n // can trigger NG0600 if the handler writes to a signal while a reactive consumer is active.\n // See: https://github.com/angular/angular/issues/60143\n const untrackedHandler = function (this: any, event: Event) {\n untracked(() => handler.call(this, event));\n };\n\n const setupListener = (onCleanup: EffectCleanupRegisterFn) => {\n const raw = toValue(maybeReactiveTarget);\n const target = unrefElement(raw);\n const event = toValue(maybeReactiveEvent);\n\n if (!target) {\n return;\n }\n\n if (ngDevMode) {\n assertEventTarget(target, 'listener');\n }\n\n target.addEventListener(event, untrackedHandler, nativeOptions);\n\n onCleanup(() => {\n target.removeEventListener(event, untrackedHandler, nativeOptions);\n });\n };\n\n let effectRef: EffectRef;\n\n if (isSyncSetupRequired) {\n effectRef = syncEffect(setupListener);\n } else {\n effectRef = afterRenderEffect({ read: setupListener });\n }\n\n return { destroy: () => effectRef.destroy() };\n });\n}\n\nconst MODIFIERS = new Set<keyof InternalListenerOptions>([\n 'capture',\n 'passive',\n 'once',\n 'stop',\n 'prevent',\n 'self',\n]);\n\nfunction createModifier(applied: InternalListenerOptions): ListenerFunction {\n const modifierFn = ((...args: any[]) => {\n return listenerImpl(applied, ...args);\n }) as ListenerFunction;\n\n return new Proxy(modifierFn, {\n get(target, prop) {\n if (typeof prop !== 'string' || !MODIFIERS.has(prop as any)) {\n return target[prop as keyof typeof target];\n }\n\n if (applied[prop as keyof InternalListenerOptions]) {\n return target;\n }\n\n return createModifier({ ...applied, [prop]: true });\n },\n });\n}\n\nfunction syncEffect(\n effectFn: (onCleanup: EffectCleanupRegisterFn) => void,\n options?: CreateEffectOptions\n): EffectRef {\n const effectRef = effect(effectFn, options);\n const effectNode: BaseEffectNode = (effectRef as any)[SIGNAL];\n try {\n effectNode.run();\n } catch (error) {\n if (ngDevMode) {\n console.warn('[syncEffect] Failed to run effectFn synchronously', error);\n }\n }\n return effectRef;\n}\n\ninterface InternalListenerOptions {\n readonly capture?: boolean;\n readonly passive?: boolean;\n readonly once?: boolean;\n readonly stop?: boolean;\n readonly prevent?: boolean;\n readonly self?: boolean;\n}\n\ninterface InferEventTarget<Events> {\n readonly addEventListener: (event: Events, fn?: any, options?: any) => any;\n readonly removeEventListener: (event: Events, fn?: any, options?: any) => any;\n}\n\ninterface GeneralEventListener<E = Event> {\n (e: E): void;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -20,7 +20,7 @@ import { listener } from '@signality/core/browser/listener';
20
20
  * }
21
21
  * `
22
22
  * })
23
- * class ThemeComponent {
23
+ * export class ThemeDemo {
24
24
  * readonly prefersDark = mediaQuery('(prefers-color-scheme: dark)');
25
25
  * }
26
26
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-media-query.mjs","sources":["../../../projects/core/browser/media-query/index.ts","../../../projects/core/browser/media-query/signality-core-browser-media-query.ts"],"sourcesContent":["import { type CreateSignalOptions, effect, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext, toValue, type Union } from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\n\nexport interface MediaQueryOptions extends CreateSignalOptions<boolean>, WithInjector {\n /**\n * Initial value for SSR.\n * @default false\n */\n readonly initialValue?: boolean;\n}\n\n/**\n * Reactive wrapper around the [Window.matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) method.\n *\n * @param query - CSS media query string (can be a signal)\n * @param options - Optional configuration\n * @returns A signal that is true when the media query matches\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (prefersDark()) {\n * <p>Dark mode is preferred</p>\n * } @else {\n * <p>Light mode is preferred</p>\n * }\n * `\n * })\n * class ThemeComponent {\n * readonly prefersDark = mediaQuery('(prefers-color-scheme: dark)');\n * }\n * ```\n */\nexport function mediaQuery(\n query: MaybeSignal<Union<MediaQueryFeature, string>>,\n options?: MediaQueryOptions\n): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, mediaQuery);\n\n return runInContext(({ isServer, injector }) => {\n if (isServer) {\n return constSignal(!!options?.initialValue);\n }\n\n const matches = signal(!!options?.initialValue, options);\n\n effect(onCleanup => {\n const queryString = toValue(query);\n\n if (!queryString) {\n matches.set(false);\n return;\n }\n\n const mediaQueryList = window.matchMedia(queryString);\n\n matches.set(mediaQueryList.matches);\n\n const changeListener = listener(\n mediaQueryList,\n 'change',\n (e: MediaQueryListEvent) => matches.set(e.matches),\n { injector }\n );\n\n onCleanup(changeListener.destroy);\n });\n\n return matches.asReadonly();\n });\n}\n\ntype MediaQueryFeature =\n | `(any-hover: ${'none' | 'hover'})`\n | `(any-pointer: ${'none' | 'coarse' | 'fine'})`\n | `(aspect-ratio: ${string})`\n | `(color: ${string})`\n | `(color-gamut: ${'srgb' | 'p3' | 'rec2020'})`\n | `(color-index: ${string})`\n | `(device-aspect-ratio: ${string})`\n | `(device-height: ${string})`\n | `(device-posture: ${'flat' | 'folded' | 'continuous'})`\n | `(device-width: ${string})`\n | `(display-mode: ${\n | 'fullscreen'\n | 'standalone'\n | 'minimal-ui'\n | 'browser'\n | 'picture-in-picture'\n | 'window-controls-overlay'})`\n | `(dynamic-range: ${'standard' | 'high'})`\n | `(forced-colors: ${'none' | 'active'})`\n | `(grid: ${'0' | '1'})`\n | `(height: ${string})`\n | `(hover: ${'none' | 'hover'})`\n | `(inverted-colors: ${'none' | 'inverted'})`\n | `(monochrome: ${string})`\n | `(orientation: ${'portrait' | 'landscape'})`\n | `(overflow-block: ${'none' | 'scroll' | 'optional-paged' | 'paged'})`\n | `(overflow-inline: ${'none' | 'scroll'})`\n | `(pointer: ${'none' | 'coarse' | 'fine'})`\n | `(prefers-color-scheme: ${'light' | 'dark' | 'no-preference'})`\n | `(prefers-contrast: ${'no-preference' | 'more' | 'less' | 'custom'})`\n | `(prefers-reduced-data: ${'no-preference' | 'reduce'})`\n | `(prefers-reduced-motion: ${'no-preference' | 'reduce'})`\n | `(prefers-reduced-transparency: ${'no-preference' | 'reduce'})`\n | `(resolution: ${string})`\n | `(scan: ${'progressive' | 'interlace'})`\n | `(scripting: ${'none' | 'initial-only' | 'enabled'})`\n | `(shape: ${'rect' | 'round'})`\n | `(update: ${'none' | 'slow' | 'fast'})`\n | `(video-dynamic-range: ${'standard' | 'high'})`\n | `(width: ${string})`\n | `(min-width: ${string})`\n | `(max-width: ${string})`\n | `(min-height: ${string})`\n | `(max-height: ${string})`\n | `(min-aspect-ratio: ${string})`\n | `(max-aspect-ratio: ${string})`\n | `(min-resolution: ${string})`\n | `(max-resolution: ${string})`\n | `(min-color: ${string})`\n | `(max-color: ${string})`\n | `(min-color-index: ${string})`\n | `(max-color-index: ${string})`\n | `(min-monochrome: ${string})`\n | `(max-monochrome: ${string})`;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAaA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG,SAAU,UAAU,CACxB,KAAoD,EACpD,OAA2B,EAAA;AAE3B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC;IAEpE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAI;QAC7C,IAAI,QAAQ,EAAE;YACZ,OAAO,WAAW,CAAC,CAAC,CAAC,OAAO,EAAE,YAAY,CAAC;QAC7C;AAEA,QAAA,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC;QAExD,MAAM,CAAC,SAAS,IAAG;AACjB,YAAA,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC;YAElC,IAAI,CAAC,WAAW,EAAE;AAChB,gBAAA,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;gBAClB;YACF;YAEA,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC;AAErD,YAAA,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC;YAEnC,MAAM,cAAc,GAAG,QAAQ,CAC7B,cAAc,EACd,QAAQ,EACR,CAAC,CAAsB,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAClD,EAAE,QAAQ,EAAE,CACb;AAED,YAAA,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC;AACnC,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,OAAO,CAAC,UAAU,EAAE;AAC7B,IAAA,CAAC,CAAC;AACJ;;ACzEA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-media-query.mjs","sources":["../../../projects/core/browser/media-query/index.ts","../../../projects/core/browser/media-query/signality-core-browser-media-query.ts"],"sourcesContent":["import { type CreateSignalOptions, effect, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext, toValue, type Union } from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\n\nexport interface MediaQueryOptions extends CreateSignalOptions<boolean>, WithInjector {\n /**\n * Initial value for SSR.\n * @default false\n */\n readonly initialValue?: boolean;\n}\n\n/**\n * Reactive wrapper around the [Window.matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) method.\n *\n * @param query - CSS media query string (can be a signal)\n * @param options - Optional configuration\n * @returns A signal that is true when the media query matches\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (prefersDark()) {\n * <p>Dark mode is preferred</p>\n * } @else {\n * <p>Light mode is preferred</p>\n * }\n * `\n * })\n * export class ThemeDemo {\n * readonly prefersDark = mediaQuery('(prefers-color-scheme: dark)');\n * }\n * ```\n */\nexport function mediaQuery(\n query: MaybeSignal<Union<MediaQueryFeature, string>>,\n options?: MediaQueryOptions\n): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, mediaQuery);\n\n return runInContext(({ isServer, injector }) => {\n if (isServer) {\n return constSignal(!!options?.initialValue);\n }\n\n const matches = signal(!!options?.initialValue, options);\n\n effect(onCleanup => {\n const queryString = toValue(query);\n\n if (!queryString) {\n matches.set(false);\n return;\n }\n\n const mediaQueryList = window.matchMedia(queryString);\n\n matches.set(mediaQueryList.matches);\n\n const changeListener = listener(\n mediaQueryList,\n 'change',\n (e: MediaQueryListEvent) => matches.set(e.matches),\n { injector }\n );\n\n onCleanup(changeListener.destroy);\n });\n\n return matches.asReadonly();\n });\n}\n\ntype MediaQueryFeature =\n | `(any-hover: ${'none' | 'hover'})`\n | `(any-pointer: ${'none' | 'coarse' | 'fine'})`\n | `(aspect-ratio: ${string})`\n | `(color: ${string})`\n | `(color-gamut: ${'srgb' | 'p3' | 'rec2020'})`\n | `(color-index: ${string})`\n | `(device-aspect-ratio: ${string})`\n | `(device-height: ${string})`\n | `(device-posture: ${'flat' | 'folded' | 'continuous'})`\n | `(device-width: ${string})`\n | `(display-mode: ${\n | 'fullscreen'\n | 'standalone'\n | 'minimal-ui'\n | 'browser'\n | 'picture-in-picture'\n | 'window-controls-overlay'})`\n | `(dynamic-range: ${'standard' | 'high'})`\n | `(forced-colors: ${'none' | 'active'})`\n | `(grid: ${'0' | '1'})`\n | `(height: ${string})`\n | `(hover: ${'none' | 'hover'})`\n | `(inverted-colors: ${'none' | 'inverted'})`\n | `(monochrome: ${string})`\n | `(orientation: ${'portrait' | 'landscape'})`\n | `(overflow-block: ${'none' | 'scroll' | 'optional-paged' | 'paged'})`\n | `(overflow-inline: ${'none' | 'scroll'})`\n | `(pointer: ${'none' | 'coarse' | 'fine'})`\n | `(prefers-color-scheme: ${'light' | 'dark' | 'no-preference'})`\n | `(prefers-contrast: ${'no-preference' | 'more' | 'less' | 'custom'})`\n | `(prefers-reduced-data: ${'no-preference' | 'reduce'})`\n | `(prefers-reduced-motion: ${'no-preference' | 'reduce'})`\n | `(prefers-reduced-transparency: ${'no-preference' | 'reduce'})`\n | `(resolution: ${string})`\n | `(scan: ${'progressive' | 'interlace'})`\n | `(scripting: ${'none' | 'initial-only' | 'enabled'})`\n | `(shape: ${'rect' | 'round'})`\n | `(update: ${'none' | 'slow' | 'fast'})`\n | `(video-dynamic-range: ${'standard' | 'high'})`\n | `(width: ${string})`\n | `(min-width: ${string})`\n | `(max-width: ${string})`\n | `(min-height: ${string})`\n | `(max-height: ${string})`\n | `(min-aspect-ratio: ${string})`\n | `(max-aspect-ratio: ${string})`\n | `(min-resolution: ${string})`\n | `(max-resolution: ${string})`\n | `(min-color: ${string})`\n | `(max-color: ${string})`\n | `(min-color-index: ${string})`\n | `(max-color-index: ${string})`\n | `(min-monochrome: ${string})`\n | `(max-monochrome: ${string})`;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAaA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG,SAAU,UAAU,CACxB,KAAoD,EACpD,OAA2B,EAAA;AAE3B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC;IAEpE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAI;QAC7C,IAAI,QAAQ,EAAE;YACZ,OAAO,WAAW,CAAC,CAAC,CAAC,OAAO,EAAE,YAAY,CAAC;QAC7C;AAEA,QAAA,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC;QAExD,MAAM,CAAC,SAAS,IAAG;AACjB,YAAA,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC;YAElC,IAAI,CAAC,WAAW,EAAE;AAChB,gBAAA,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;gBAClB;YACF;YAEA,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC;AAErD,YAAA,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC;YAEnC,MAAM,cAAc,GAAG,QAAQ,CAC7B,cAAc,EACd,QAAQ,EACR,CAAC,CAAsB,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAClD,EAAE,QAAQ,EAAE,CACb;AAED,YAAA,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC;AACnC,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,OAAO,CAAC,UAAU,EAAE;AAC7B,IAAA,CAAC,CAAC;AACJ;;ACzEA;;AAEG;;;;"}
@@ -4,7 +4,7 @@ import { setupSync, listener } from '@signality/core/browser/listener';
4
4
  import { ONLINE } from '@signality/core/browser/online';
5
5
 
6
6
  /**
7
- * Signal-based wrapper around the Network Information API and online/offline events.
7
+ * Signal-based wrapper around the [Network Information API](https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API) and online/offline events.
8
8
  *
9
9
  * @param options - Optional configuration including injector
10
10
  * @returns A NetworkRef with network status signals
@@ -19,7 +19,7 @@ import { ONLINE } from '@signality/core/browser/online';
19
19
  * }
20
20
  * `
21
21
  * })
22
- * class NetworkComponent {
22
+ * export class NetworkComponent {
23
23
  * readonly net = network();
24
24
  * }
25
25
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-network.mjs","sources":["../../../projects/core/browser/network/index.ts","../../../projects/core/browser/network/signality-core-browser-network.ts"],"sourcesContent":["import { inject, type Signal, signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\nimport { ONLINE } from '@signality/core/browser/online';\n\nexport type EffectiveConnectionType = 'slow-2g' | '2g' | '3g' | '4g';\n\nexport type ConnectionType =\n | 'bluetooth'\n | 'cellular'\n | 'ethernet'\n | 'wifi'\n | 'wimax'\n | 'none'\n | 'other'\n | 'unknown';\n\nexport type NetworkOptions = WithInjector;\n\nexport interface NetworkRef {\n /** Whether Network Information API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Whether the browser is online */\n readonly isOnline: Signal<boolean>;\n\n /** Effective connection type */\n readonly effectiveType: Signal<EffectiveConnectionType | undefined>;\n\n /** Downlink speed in Mbps */\n readonly downlink: Signal<number | undefined>;\n\n /** Round-trip time in ms */\n readonly rtt: Signal<number | undefined>;\n\n /** Whether user has data saver enabled */\n readonly saveData: Signal<boolean>;\n\n /** Connection type (wifi, cellular, etc.) */\n readonly type: Signal<ConnectionType | undefined>;\n}\n\n/**\n * Signal-based wrapper around the Network Information API and online/offline events.\n *\n * @param options - Optional configuration including injector\n * @returns A NetworkRef with network status signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>{{ net.isOnline() ? 'Online' : 'Offline' }}</p>\n * @if (net.effectiveType()) {\n * <p>Connection: {{ net.effectiveType() }}</p>\n * }\n * `\n * })\n * class NetworkComponent {\n * readonly net = network();\n * }\n * ```\n */\nexport function network(options?: NetworkOptions): NetworkRef {\n const { runInContext } = setupContext(options?.injector, network);\n\n return runInContext(({ isBrowser }) => {\n const isSupported = constSignal(isBrowser && 'connection' in navigator);\n\n if (!isSupported()) {\n return {\n isSupported,\n isOnline: constSignal(true),\n effectiveType: constSignal(undefined),\n downlink: constSignal(undefined),\n rtt: constSignal(undefined),\n saveData: constSignal(false),\n type: constSignal(undefined),\n };\n }\n\n const { connection } = navigator as NavigatorWithConnection;\n\n const isOnline = inject(ONLINE);\n const effectiveType = signal(connection.effectiveType);\n const downlink = signal(connection.downlink);\n const rtt = signal(connection.rtt);\n const saveData = signal(!!connection.saveData);\n const type = signal(connection.type);\n\n setupSync(() => {\n listener(connection, 'change', () => {\n effectiveType.set(connection.effectiveType);\n downlink.set(connection.downlink);\n rtt.set(connection.rtt);\n saveData.set(!!connection.saveData);\n type.set(connection.type);\n });\n });\n\n return {\n isSupported,\n isOnline,\n effectiveType: effectiveType.asReadonly(),\n downlink: downlink.asReadonly(),\n rtt: rtt.asReadonly(),\n saveData: saveData.asReadonly(),\n type: type.asReadonly(),\n };\n });\n}\n\nexport const NETWORK = /* @__PURE__ */ createToken(network);\n\ninterface NetworkInformation extends EventTarget {\n readonly effectiveType?: EffectiveConnectionType;\n readonly downlink?: number;\n readonly rtt?: number;\n readonly saveData?: boolean;\n readonly type?: ConnectionType;\n}\n\ninterface NavigatorWithConnection extends Navigator {\n readonly connection: NetworkInformation;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AA2CA;;;;;;;;;;;;;;;;;;;;AAoBG;AACG,SAAU,OAAO,CAAC,OAAwB,EAAA;AAC9C,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC;AAEjE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAI;QACpC,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,IAAI,YAAY,IAAI,SAAS,CAAC;AAEvE,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC;AAC3B,gBAAA,aAAa,EAAE,WAAW,CAAC,SAAS,CAAC;AACrC,gBAAA,QAAQ,EAAE,WAAW,CAAC,SAAS,CAAC;AAChC,gBAAA,GAAG,EAAE,WAAW,CAAC,SAAS,CAAC;AAC3B,gBAAA,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;AAC5B,gBAAA,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC;aAC7B;QACH;AAEA,QAAA,MAAM,EAAE,UAAU,EAAE,GAAG,SAAoC;AAE3D,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;QAC/B,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,aAAa,yDAAC;QACtD,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,oDAAC;QAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,+CAAC;QAClC,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,UAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;QAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,gDAAC;QAEpC,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAK;AAClC,gBAAA,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC;AAC3C,gBAAA,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC;AACjC,gBAAA,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;gBACvB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC;AACnC,gBAAA,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;AAC3B,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;YACX,QAAQ;AACR,YAAA,aAAa,EAAE,aAAa,CAAC,UAAU,EAAE;AACzC,YAAA,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE;AAC/B,YAAA,GAAG,EAAE,GAAG,CAAC,UAAU,EAAE;AACrB,YAAA,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE;AAC/B,YAAA,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;SACxB;AACH,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,OAAO,mBAAmB,WAAW,CAAC,OAAO;;ACjH1D;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-network.mjs","sources":["../../../projects/core/browser/network/index.ts","../../../projects/core/browser/network/signality-core-browser-network.ts"],"sourcesContent":["import { inject, type Signal, signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\nimport { ONLINE } from '@signality/core/browser/online';\n\nexport type EffectiveConnectionType = 'slow-2g' | '2g' | '3g' | '4g';\n\nexport type ConnectionType =\n | 'bluetooth'\n | 'cellular'\n | 'ethernet'\n | 'wifi'\n | 'wimax'\n | 'none'\n | 'other'\n | 'unknown';\n\nexport type NetworkOptions = WithInjector;\n\nexport interface NetworkRef {\n /**\n * Whether the Network Information API is supported in the current browser.\n *\n * @see [Network Information API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Whether the browser currently has network connectivity.\n *\n * @see [Navigator: onLine on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine)\n */\n readonly isOnline: Signal<boolean>;\n\n /**\n * Estimated effective connection type based on recently observed network conditions.\n *\n * @see [NetworkInformation: effectiveType on MDN](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType)\n */\n readonly effectiveType: Signal<EffectiveConnectionType | undefined>;\n\n /**\n * Estimated downlink bandwidth in Mbps.\n *\n * @see [NetworkInformation: downlink on MDN](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/downlink)\n */\n readonly downlink: Signal<number | undefined>;\n\n /**\n * Estimated round-trip latency in milliseconds.\n *\n * @see [NetworkInformation: rtt on MDN](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/rtt)\n */\n readonly rtt: Signal<number | undefined>;\n\n /**\n * Whether the user has enabled a data-saving mode in their browser or OS.\n *\n * @see [NetworkInformation: saveData on MDN](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/saveData)\n */\n readonly saveData: Signal<boolean>;\n\n /**\n * The physical connection type (e.g. `'wifi'`, `'cellular'`).\n *\n * @see [NetworkInformation: type on MDN](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/type)\n */\n readonly type: Signal<ConnectionType | undefined>;\n}\n\n/**\n * Signal-based wrapper around the [Network Information API](https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API) and online/offline events.\n *\n * @param options - Optional configuration including injector\n * @returns A NetworkRef with network status signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>{{ net.isOnline() ? 'Online' : 'Offline' }}</p>\n * @if (net.effectiveType()) {\n * <p>Connection: {{ net.effectiveType() }}</p>\n * }\n * `\n * })\n * export class NetworkComponent {\n * readonly net = network();\n * }\n * ```\n */\nexport function network(options?: NetworkOptions): NetworkRef {\n const { runInContext } = setupContext(options?.injector, network);\n\n return runInContext(({ isBrowser }) => {\n const isSupported = constSignal(isBrowser && 'connection' in navigator);\n\n if (!isSupported()) {\n return {\n isSupported,\n isOnline: constSignal(true),\n effectiveType: constSignal(undefined),\n downlink: constSignal(undefined),\n rtt: constSignal(undefined),\n saveData: constSignal(false),\n type: constSignal(undefined),\n };\n }\n\n const { connection } = navigator as NavigatorWithConnection;\n\n const isOnline = inject(ONLINE);\n const effectiveType = signal(connection.effectiveType);\n const downlink = signal(connection.downlink);\n const rtt = signal(connection.rtt);\n const saveData = signal(!!connection.saveData);\n const type = signal(connection.type);\n\n setupSync(() => {\n listener(connection, 'change', () => {\n effectiveType.set(connection.effectiveType);\n downlink.set(connection.downlink);\n rtt.set(connection.rtt);\n saveData.set(!!connection.saveData);\n type.set(connection.type);\n });\n });\n\n return {\n isSupported,\n isOnline,\n effectiveType: effectiveType.asReadonly(),\n downlink: downlink.asReadonly(),\n rtt: rtt.asReadonly(),\n saveData: saveData.asReadonly(),\n type: type.asReadonly(),\n };\n });\n}\n\nexport const NETWORK = /* @__PURE__ */ createToken(network);\n\ninterface NetworkInformation extends EventTarget {\n readonly effectiveType?: EffectiveConnectionType;\n readonly downlink?: number;\n readonly rtt?: number;\n readonly saveData?: boolean;\n readonly type?: ConnectionType;\n}\n\ninterface NavigatorWithConnection extends Navigator {\n readonly connection: NetworkInformation;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAuEA;;;;;;;;;;;;;;;;;;;;AAoBG;AACG,SAAU,OAAO,CAAC,OAAwB,EAAA;AAC9C,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC;AAEjE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAI;QACpC,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,IAAI,YAAY,IAAI,SAAS,CAAC;AAEvE,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC;AAC3B,gBAAA,aAAa,EAAE,WAAW,CAAC,SAAS,CAAC;AACrC,gBAAA,QAAQ,EAAE,WAAW,CAAC,SAAS,CAAC;AAChC,gBAAA,GAAG,EAAE,WAAW,CAAC,SAAS,CAAC;AAC3B,gBAAA,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;AAC5B,gBAAA,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC;aAC7B;QACH;AAEA,QAAA,MAAM,EAAE,UAAU,EAAE,GAAG,SAAoC;AAE3D,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;QAC/B,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,aAAa,yDAAC;QACtD,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,oDAAC;QAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,+CAAC;QAClC,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,UAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;QAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,gDAAC;QAEpC,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAK;AAClC,gBAAA,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC;AAC3C,gBAAA,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC;AACjC,gBAAA,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;gBACvB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC;AACnC,gBAAA,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;AAC3B,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;YACX,QAAQ;AACR,YAAA,aAAa,EAAE,aAAa,CAAC,UAAU,EAAE;AACzC,YAAA,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE;AAC/B,YAAA,GAAG,EAAE,GAAG,CAAC,UAAU,EAAE;AACrB,YAAA,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE;AAC/B,YAAA,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;SACxB;AACH,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,OAAO,mBAAmB,WAAW,CAAC,OAAO;;AC7I1D;;AAEG;;;;"}
@@ -19,7 +19,7 @@ import { setupSync, listener } from '@signality/core/browser/listener';
19
19
  * }
20
20
  * `
21
21
  * })
22
- * class NetworkStatusComponent {
22
+ * export class NetworkStatus {
23
23
  * readonly isOnline = online();
24
24
  * }
25
25
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-online.mjs","sources":["../../../projects/core/browser/online/index.ts","../../../projects/core/browser/online/signality-core-browser-online.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type OnlineOptions = CreateSignalOptions<boolean> & WithInjector;\n\n/**\n * Reactive wrapper around the [Navigator.onLine](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine) property.\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current online status (`true` for online, `false` for offline)\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (isOnline()) {\n * <p>You're online</p>\n * } @else {\n * <p>You're offline</p>\n * }\n * `\n * })\n * class NetworkStatusComponent {\n * readonly isOnline = online();\n * }\n * ```\n */\nexport function online(options?: OnlineOptions): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, online);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(true);\n }\n\n const isOnline = signal(navigator.onLine, options);\n\n const update = () => isOnline.set(navigator.onLine);\n\n setupSync(() => {\n listener(window, 'online', update);\n listener(window, 'offline', update);\n });\n\n return isOnline.asReadonly();\n });\n}\n\nexport const ONLINE = /* @__PURE__ */ createToken(online);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAOA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,MAAM,CAAC,OAAuB,EAAA;AAC5C,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC;AAEhE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,IAAI,CAAC;QAC1B;QAEA,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;AAElD,QAAA,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC;QAEnD,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC;AAClC,YAAA,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC;AACrC,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,QAAQ,CAAC,UAAU,EAAE;AAC9B,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,MAAM,mBAAmB,WAAW,CAAC,MAAM;;AClDxD;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-online.mjs","sources":["../../../projects/core/browser/online/index.ts","../../../projects/core/browser/online/signality-core-browser-online.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type OnlineOptions = CreateSignalOptions<boolean> & WithInjector;\n\n/**\n * Reactive wrapper around the [Navigator.onLine](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine) property.\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current online status (`true` for online, `false` for offline)\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (isOnline()) {\n * <p>You're online</p>\n * } @else {\n * <p>You're offline</p>\n * }\n * `\n * })\n * export class NetworkStatus {\n * readonly isOnline = online();\n * }\n * ```\n */\nexport function online(options?: OnlineOptions): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, online);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(true);\n }\n\n const isOnline = signal(navigator.onLine, options);\n\n const update = () => isOnline.set(navigator.onLine);\n\n setupSync(() => {\n listener(window, 'online', update);\n listener(window, 'offline', update);\n });\n\n return isOnline.asReadonly();\n });\n}\n\nexport const ONLINE = /* @__PURE__ */ createToken(online);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAOA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,MAAM,CAAC,OAAuB,EAAA;AAC5C,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC;AAEhE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,IAAI,CAAC;QAC1B;QAEA,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;AAElD,QAAA,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC;QAEnD,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC;AAClC,YAAA,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC;AACrC,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,QAAQ,CAAC,UAAU,EAAE;AAC9B,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,MAAM,mBAAmB,WAAW,CAAC,MAAM;;AClDxD;;AAEG;;;;"}
@@ -19,7 +19,7 @@ import { setupSync, listener } from '@signality/core/browser/listener';
19
19
  * }
20
20
  * `
21
21
  * })
22
- * class VisibilityComponent {
22
+ * export class VisibilityDemo {
23
23
  * readonly visibility = pageVisibility();
24
24
  * }
25
25
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-page-visibility.mjs","sources":["../../../projects/core/browser/page-visibility/index.ts","../../../projects/core/browser/page-visibility/signality-core-browser-page-visibility.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type PageVisibilityOptions = CreateSignalOptions<DocumentVisibilityState> & WithInjector;\n\n/**\n * Signal-based wrapper around the [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API).\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current DocumentVisibilityState ('visible' or 'hidden')\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (visibility() === 'hidden') {\n * <p>Page is hidden</p>\n * } @else {\n * <p>Page is visible</p>\n * }\n * `\n * })\n * class VisibilityComponent {\n * readonly visibility = pageVisibility();\n * }\n * ```\n */\nexport function pageVisibility(options?: PageVisibilityOptions): Signal<DocumentVisibilityState> {\n const { runInContext } = setupContext(options?.injector, pageVisibility);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal('visible');\n }\n\n const visibilityState = signal(document.visibilityState, options);\n\n setupSync(() => {\n listener(document, 'visibilitychange', () => visibilityState.set(document.visibilityState));\n });\n\n return visibilityState.asReadonly();\n });\n}\n\nexport const PAGE_VISIBILITY = /* @__PURE__ */ createToken(pageVisibility);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAOA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,cAAc,CAAC,OAA+B,EAAA;AAC5D,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,cAAc,CAAC;AAExE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,SAAS,CAAC;QAC/B;QAEA,MAAM,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;QAEjE,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;AAC7F,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,eAAe,CAAC,UAAU,EAAE;AACrC,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,eAAe,mBAAmB,WAAW,CAAC,cAAc;;AC/CzE;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-page-visibility.mjs","sources":["../../../projects/core/browser/page-visibility/index.ts","../../../projects/core/browser/page-visibility/signality-core-browser-page-visibility.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type PageVisibilityOptions = CreateSignalOptions<DocumentVisibilityState> & WithInjector;\n\n/**\n * Signal-based wrapper around the [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API).\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current DocumentVisibilityState ('visible' or 'hidden')\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (visibility() === 'hidden') {\n * <p>Page is hidden</p>\n * } @else {\n * <p>Page is visible</p>\n * }\n * `\n * })\n * export class VisibilityDemo {\n * readonly visibility = pageVisibility();\n * }\n * ```\n */\nexport function pageVisibility(options?: PageVisibilityOptions): Signal<DocumentVisibilityState> {\n const { runInContext } = setupContext(options?.injector, pageVisibility);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal('visible');\n }\n\n const visibilityState = signal(document.visibilityState, options);\n\n setupSync(() => {\n listener(document, 'visibilitychange', () => visibilityState.set(document.visibilityState));\n });\n\n return visibilityState.asReadonly();\n });\n}\n\nexport const PAGE_VISIBILITY = /* @__PURE__ */ createToken(pageVisibility);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAOA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,cAAc,CAAC,OAA+B,EAAA;AAC5D,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,cAAc,CAAC;AAExE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,SAAS,CAAC;QAC/B;QAEA,MAAM,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;QAEjE,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;AAC7F,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,eAAe,CAAC,UAAU,EAAE;AACrC,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,eAAe,mBAAmB,WAAW,CAAC,cAAc;;AC/CzE;;AAEG;;;;"}
@@ -0,0 +1,57 @@
1
+ import { signal } from '@angular/core';
2
+ import { setupContext, constSignal } from '@signality/core/internal';
3
+
4
+ /**
5
+ * Signal-based wrapper around the [Permissions API](https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API).
6
+ *
7
+ * @param name - The permission name to query
8
+ * @param options - Optional configuration including signal options and injector
9
+ * @returns A signal containing the current `PermissionState` (`'granted'`, `'denied'`, or `'prompt'`)
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * @Component({
14
+ * template: `
15
+ * <p>Camera permission: {{ cameraPermission() }}</p>
16
+ * `
17
+ * })
18
+ * export class PermissionDemo {
19
+ * readonly cameraPermission = permissionState('camera');
20
+ * }
21
+ * ```
22
+ */
23
+ function permissionState(name, options) {
24
+ const { runInContext } = setupContext(options?.injector, permissionState);
25
+ return runInContext(({ isServer, onCleanup }) => {
26
+ if (isServer) {
27
+ return constSignal('prompt');
28
+ }
29
+ const state = signal('prompt', options);
30
+ const controller = new AbortController();
31
+ navigator.permissions
32
+ .query({ name })
33
+ .then(status => {
34
+ if (controller.signal.aborted) {
35
+ return;
36
+ }
37
+ state.set(status.state);
38
+ status.addEventListener('change', () => state.set(status.state), {
39
+ signal: controller.signal,
40
+ });
41
+ })
42
+ .catch(() => {
43
+ if (ngDevMode) {
44
+ console.warn(`[permissionState] Failed to query permission state for ${name}`);
45
+ }
46
+ });
47
+ onCleanup(() => controller.abort());
48
+ return state.asReadonly();
49
+ });
50
+ }
51
+
52
+ /**
53
+ * Generated bundle index. Do not edit.
54
+ */
55
+
56
+ export { permissionState };
57
+ //# sourceMappingURL=signality-core-browser-permission-state.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-browser-permission-state.mjs","sources":["../../../projects/core/browser/permission-state/index.ts","../../../projects/core/browser/permission-state/signality-core-browser-permission-state.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\n\nexport type PermissionStateOptions = CreateSignalOptions<PermissionState> & WithInjector;\n\n/**\n * Signal-based wrapper around the [Permissions API](https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API).\n *\n * @param name - The permission name to query\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current `PermissionState` (`'granted'`, `'denied'`, or `'prompt'`)\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Camera permission: {{ cameraPermission() }}</p>\n * `\n * })\n * export class PermissionDemo {\n * readonly cameraPermission = permissionState('camera');\n * }\n * ```\n */\nexport function permissionState(\n name: PermissionName,\n options?: PermissionStateOptions\n): Signal<PermissionState> {\n const { runInContext } = setupContext(options?.injector, permissionState);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return constSignal('prompt');\n }\n\n const state = signal<PermissionState>('prompt', options);\n const controller = new AbortController();\n\n navigator.permissions\n .query({ name })\n .then(status => {\n if (controller.signal.aborted) {\n return;\n }\n\n state.set(status.state);\n\n status.addEventListener('change', () => state.set(status.state), {\n signal: controller.signal,\n });\n })\n .catch(() => {\n if (ngDevMode) {\n console.warn(`[permissionState] Failed to query permission state for ${name}`);\n }\n });\n\n onCleanup(() => controller.abort());\n\n return state.asReadonly();\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAMA;;;;;;;;;;;;;;;;;;AAkBG;AACG,SAAU,eAAe,CAC7B,IAAoB,EACpB,OAAgC,EAAA;AAEhC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,eAAe,CAAC;IAEzE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAI;QAC9C,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,QAAQ,CAAC;QAC9B;QAEA,MAAM,KAAK,GAAG,MAAM,CAAkB,QAAQ,EAAE,OAAO,CAAC;AACxD,QAAA,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE;AAExC,QAAA,SAAS,CAAC;AACP,aAAA,KAAK,CAAC,EAAE,IAAI,EAAE;aACd,IAAI,CAAC,MAAM,IAAG;AACb,YAAA,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE;gBAC7B;YACF;AAEA,YAAA,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;AAEvB,YAAA,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;gBAC/D,MAAM,EAAE,UAAU,CAAC,MAAM;AAC1B,aAAA,CAAC;AACJ,QAAA,CAAC;aACA,KAAK,CAAC,MAAK;YACV,IAAI,SAAS,EAAE;AACb,gBAAA,OAAO,CAAC,IAAI,CAAC,0DAA0D,IAAI,CAAA,CAAE,CAAC;YAChF;AACF,QAAA,CAAC,CAAC;QAEJ,SAAS,CAAC,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC;AAEnC,QAAA,OAAO,KAAK,CAAC,UAAU,EAAE;AAC3B,IAAA,CAAC,CAAC;AACJ;;AC9DA;;AAEG;;;;"}
@@ -23,7 +23,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
23
23
  * }
24
24
  * `
25
25
  * })
26
- * class PiPComponent {
26
+ * export class PiPDemo {
27
27
  * readonly video = viewChild<HTMLVideoElement>('video');
28
28
  * readonly pip = pictureInPicture(this.video);
29
29
  * }
@@ -32,9 +32,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
32
32
  function pictureInPicture(target, options) {
33
33
  const { runInContext } = setupContext(options?.injector, pictureInPicture);
34
34
  return runInContext(({ isBrowser }) => {
35
- const isSupported = constSignal(isBrowser &&
36
- 'pictureInPictureEnabled' in document &&
37
- document.pictureInPictureEnabled !== false);
35
+ const isSupported = constSignal(isBrowser && 'pictureInPictureEnabled' in document && document.pictureInPictureEnabled);
38
36
  if (!isSupported()) {
39
37
  return {
40
38
  isSupported,
@@ -46,16 +44,28 @@ function pictureInPicture(target, options) {
46
44
  }
47
45
  const isActive = signal(false, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
48
46
  const enter = async () => {
49
- const el = toElement.untracked(target);
50
- if (el) {
51
- await el.requestPictureInPicture();
47
+ const targetEl = toElement.untracked(target);
48
+ try {
49
+ await targetEl?.requestPictureInPicture();
50
+ }
51
+ catch (error) {
52
+ if (ngDevMode) {
53
+ console.warn(`[pictureInPicture] Failed to enter Picture-in-Picture mode.`, error);
54
+ }
52
55
  }
53
56
  };
54
57
  const exit = async () => {
55
- const el = toElement.untracked(target);
58
+ const targetEl = toElement.untracked(target);
56
59
  const pipEl = getPipElement(document);
57
- if (el && pipEl && el === pipEl) {
58
- await document.exitPictureInPicture();
60
+ if (targetEl && pipEl && targetEl === pipEl) {
61
+ try {
62
+ await document.exitPictureInPicture();
63
+ }
64
+ catch (error) {
65
+ if (ngDevMode) {
66
+ console.warn(`[pictureInPicture] Failed to exit Picture-in-Picture mode.`, error);
67
+ }
68
+ }
59
69
  }
60
70
  };
61
71
  const toggle = async () => {
@@ -68,10 +78,17 @@ function pictureInPicture(target, options) {
68
78
  };
69
79
  listener(target, 'enterpictureinpicture', () => isActive.set(true));
70
80
  listener(target, 'leavepictureinpicture', () => isActive.set(false));
71
- onDisconnect(target, async (el) => {
81
+ onDisconnect(target, async (targetEl) => {
72
82
  const pipEl = getPipElement(document);
73
- if (pipEl && el === pipEl) {
74
- await document.exitPictureInPicture();
83
+ if (pipEl && targetEl === pipEl) {
84
+ try {
85
+ await document.exitPictureInPicture();
86
+ }
87
+ catch (error) {
88
+ if (ngDevMode) {
89
+ console.warn(`[pictureInPicture] Failed to exit Picture-in-Picture mode on disconnect.`, error);
90
+ }
91
+ }
75
92
  isActive.set(false);
76
93
  }
77
94
  });
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-picture-in-picture.mjs","sources":["../../../projects/core/browser/picture-in-picture/index.ts","../../../projects/core/browser/picture-in-picture/signality-core-browser-picture-in-picture.ts"],"sourcesContent":["import { signal, type Signal, untracked } from '@angular/core';\nimport {\n constSignal,\n getPipElement,\n NOOP_ASYNC_FN,\n setupContext,\n toElement,\n} from '@signality/core/internal';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport type PictureInPictureOptions = WithInjector;\n\nexport interface PictureInPictureRef {\n /** Whether Picture-in-Picture API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Whether Picture-in-Picture is active */\n readonly isActive: Signal<boolean>;\n\n /** Enter Picture-in-Picture mode */\n readonly enter: () => Promise<void>;\n\n /** Exit Picture-in-Picture mode */\n readonly exit: () => Promise<void>;\n\n /** Toggle Picture-in-Picture mode */\n readonly toggle: () => Promise<void>;\n}\n\n/**\n * Signal-based wrapper around the [Picture-in-Picture API](https://developer.mozilla.org/en-US/docs/Web/API/Picture-in-Picture_API).\n *\n * Automatically exits Picture-in-Picture when the target element is disconnected from the DOM.\n *\n * @param target - Video element\n * @param options - Optional configuration\n * @returns A {@link PictureInPictureRef} with `isSupported`, `isActive` signals and `enter`/`exit`/`toggle` methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (pip.isSupported()) {\n * <video #video src=\"video.mp4\"></video>\n * <button (click)=\"pip.toggle()\">Toggle PiP</button>\n * <p>Active: {{ pip.isActive() }}</p>\n * }\n * `\n * })\n * class PiPComponent {\n * readonly video = viewChild<HTMLVideoElement>('video');\n * readonly pip = pictureInPicture(this.video);\n * }\n * ```\n */\nexport function pictureInPicture(\n target: MaybeElementSignal<HTMLVideoElement>,\n options?: PictureInPictureOptions\n): PictureInPictureRef {\n const { runInContext } = setupContext(options?.injector, pictureInPicture);\n\n return runInContext(({ isBrowser }) => {\n const isSupported = constSignal(\n isBrowser &&\n 'pictureInPictureEnabled' in document &&\n document.pictureInPictureEnabled !== false\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n isActive: constSignal(false),\n enter: NOOP_ASYNC_FN,\n exit: NOOP_ASYNC_FN,\n toggle: NOOP_ASYNC_FN,\n };\n }\n\n const isActive = signal(false);\n\n const enter = async (): Promise<void> => {\n const el = toElement.untracked(target);\n if (el) {\n await el.requestPictureInPicture();\n }\n };\n\n const exit = async (): Promise<void> => {\n const el = toElement.untracked(target);\n const pipEl = getPipElement(document);\n\n if (el && pipEl && el === pipEl) {\n await document.exitPictureInPicture();\n }\n };\n\n const toggle = async (): Promise<void> => {\n if (untracked(isActive)) {\n await exit();\n } else {\n await enter();\n }\n };\n\n listener(target, 'enterpictureinpicture', () => isActive.set(true));\n listener(target, 'leavepictureinpicture', () => isActive.set(false));\n\n onDisconnect(target, async el => {\n const pipEl = getPipElement(document);\n\n if (pipEl && el === pipEl) {\n await document.exitPictureInPicture();\n isActive.set(false);\n }\n });\n\n return {\n isSupported,\n isActive,\n enter,\n exit,\n toggle,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AA+BA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACG,SAAU,gBAAgB,CAC9B,MAA4C,EAC5C,OAAiC,EAAA;AAEjC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,CAAC;AAE1E,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAI;AACpC,QAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,SAAS;AACP,YAAA,yBAAyB,IAAI,QAAQ;AACrC,YAAA,QAAQ,CAAC,uBAAuB,KAAK,KAAK,CAC7C;AAED,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;AAC5B,gBAAA,KAAK,EAAE,aAAa;AACpB,gBAAA,IAAI,EAAE,aAAa;AACnB,gBAAA,MAAM,EAAE,aAAa;aACtB;QACH;AAEA,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,oDAAC;AAE9B,QAAA,MAAM,KAAK,GAAG,YAA0B;YACtC,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC;YACtC,IAAI,EAAE,EAAE;AACN,gBAAA,MAAM,EAAE,CAAC,uBAAuB,EAAE;YACpC;AACF,QAAA,CAAC;AAED,QAAA,MAAM,IAAI,GAAG,YAA0B;YACrC,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC;AACtC,YAAA,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC;YAErC,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE;AAC/B,gBAAA,MAAM,QAAQ,CAAC,oBAAoB,EAAE;YACvC;AACF,QAAA,CAAC;AAED,QAAA,MAAM,MAAM,GAAG,YAA0B;AACvC,YAAA,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE;gBACvB,MAAM,IAAI,EAAE;YACd;iBAAO;gBACL,MAAM,KAAK,EAAE;YACf;AACF,QAAA,CAAC;AAED,QAAA,QAAQ,CAAC,MAAM,EAAE,uBAAuB,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACnE,QAAA,QAAQ,CAAC,MAAM,EAAE,uBAAuB,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAEpE,QAAA,YAAY,CAAC,MAAM,EAAE,OAAM,EAAE,KAAG;AAC9B,YAAA,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC;AAErC,YAAA,IAAI,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE;AACzB,gBAAA,MAAM,QAAQ,CAAC,oBAAoB,EAAE;AACrC,gBAAA,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;YACrB;AACF,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;YACX,QAAQ;YACR,KAAK;YACL,IAAI;YACJ,MAAM;SACP;AACH,IAAA,CAAC,CAAC;AACJ;;AC9HA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-picture-in-picture.mjs","sources":["../../../projects/core/browser/picture-in-picture/index.ts","../../../projects/core/browser/picture-in-picture/signality-core-browser-picture-in-picture.ts"],"sourcesContent":["import { signal, type Signal, untracked } from '@angular/core';\nimport {\n constSignal,\n getPipElement,\n NOOP_ASYNC_FN,\n setupContext,\n toElement,\n} from '@signality/core/internal';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport type PictureInPictureOptions = WithInjector;\n\nexport interface PictureInPictureRef {\n /**\n * Whether the Picture-in-Picture API is supported in the current browser.\n *\n * @see [Picture-in-Picture API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Picture-in-Picture_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Whether the target video element is currently displayed in Picture-in-Picture mode.\n *\n * @see [Document: pictureInPictureElement on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/pictureInPictureElement)\n */\n readonly isActive: Signal<boolean>;\n\n /**\n * Enter Picture-in-Picture mode for the target video element.\n *\n * @see [HTMLVideoElement: requestPictureInPicture() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/requestPictureInPicture)\n */\n readonly enter: () => Promise<void>;\n\n /**\n * Exit Picture-in-Picture mode.\n *\n * @see [Document: exitPictureInPicture() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/exitPictureInPicture)\n */\n readonly exit: () => Promise<void>;\n\n /**\n * Toggle Picture-in-Picture mode — enters if inactive, exits if active.\n */\n readonly toggle: () => Promise<void>;\n}\n\n/**\n * Signal-based wrapper around the [Picture-in-Picture API](https://developer.mozilla.org/en-US/docs/Web/API/Picture-in-Picture_API).\n *\n * Automatically exits Picture-in-Picture when the target element is disconnected from the DOM.\n *\n * @param target - Video element\n * @param options - Optional configuration\n * @returns A {@link PictureInPictureRef} with `isSupported`, `isActive` signals and `enter`/`exit`/`toggle` methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (pip.isSupported()) {\n * <video #video src=\"video.mp4\"></video>\n * <button (click)=\"pip.toggle()\">Toggle PiP</button>\n * <p>Active: {{ pip.isActive() }}</p>\n * }\n * `\n * })\n * export class PiPDemo {\n * readonly video = viewChild<HTMLVideoElement>('video');\n * readonly pip = pictureInPicture(this.video);\n * }\n * ```\n */\nexport function pictureInPicture(\n target: MaybeElementSignal<HTMLVideoElement>,\n options?: PictureInPictureOptions\n): PictureInPictureRef {\n const { runInContext } = setupContext(options?.injector, pictureInPicture);\n\n return runInContext(({ isBrowser }) => {\n const isSupported = constSignal(\n isBrowser && 'pictureInPictureEnabled' in document && document.pictureInPictureEnabled\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n isActive: constSignal(false),\n enter: NOOP_ASYNC_FN,\n exit: NOOP_ASYNC_FN,\n toggle: NOOP_ASYNC_FN,\n };\n }\n\n const isActive = signal(false);\n\n const enter = async (): Promise<void> => {\n const targetEl = toElement.untracked(target);\n\n try {\n await targetEl?.requestPictureInPicture();\n } catch (error) {\n if (ngDevMode) {\n console.warn(`[pictureInPicture] Failed to enter Picture-in-Picture mode.`, error);\n }\n }\n };\n\n const exit = async (): Promise<void> => {\n const targetEl = toElement.untracked(target);\n const pipEl = getPipElement(document);\n\n if (targetEl && pipEl && targetEl === pipEl) {\n try {\n await document.exitPictureInPicture();\n } catch (error) {\n if (ngDevMode) {\n console.warn(`[pictureInPicture] Failed to exit Picture-in-Picture mode.`, error);\n }\n }\n }\n };\n\n const toggle = async (): Promise<void> => {\n if (untracked(isActive)) {\n await exit();\n } else {\n await enter();\n }\n };\n\n listener(target, 'enterpictureinpicture', () => isActive.set(true));\n listener(target, 'leavepictureinpicture', () => isActive.set(false));\n\n onDisconnect(target, async targetEl => {\n const pipEl = getPipElement(document);\n\n if (pipEl && targetEl === pipEl) {\n try {\n await document.exitPictureInPicture();\n } catch (error) {\n if (ngDevMode) {\n console.warn(\n `[pictureInPicture] Failed to exit Picture-in-Picture mode on disconnect.`,\n error\n );\n }\n }\n isActive.set(false);\n }\n });\n\n return {\n isSupported,\n isActive,\n enter,\n exit,\n toggle,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAiDA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACG,SAAU,gBAAgB,CAC9B,MAA4C,EAC5C,OAAiC,EAAA;AAEjC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,CAAC;AAE1E,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAI;AACpC,QAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,SAAS,IAAI,yBAAyB,IAAI,QAAQ,IAAI,QAAQ,CAAC,uBAAuB,CACvF;AAED,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;AAC5B,gBAAA,KAAK,EAAE,aAAa;AACpB,gBAAA,IAAI,EAAE,aAAa;AACnB,gBAAA,MAAM,EAAE,aAAa;aACtB;QACH;AAEA,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,oDAAC;AAE9B,QAAA,MAAM,KAAK,GAAG,YAA0B;YACtC,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC;AAE5C,YAAA,IAAI;AACF,gBAAA,MAAM,QAAQ,EAAE,uBAAuB,EAAE;YAC3C;YAAE,OAAO,KAAK,EAAE;gBACd,IAAI,SAAS,EAAE;AACb,oBAAA,OAAO,CAAC,IAAI,CAAC,6DAA6D,EAAE,KAAK,CAAC;gBACpF;YACF;AACF,QAAA,CAAC;AAED,QAAA,MAAM,IAAI,GAAG,YAA0B;YACrC,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC;AAC5C,YAAA,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC;YAErC,IAAI,QAAQ,IAAI,KAAK,IAAI,QAAQ,KAAK,KAAK,EAAE;AAC3C,gBAAA,IAAI;AACF,oBAAA,MAAM,QAAQ,CAAC,oBAAoB,EAAE;gBACvC;gBAAE,OAAO,KAAK,EAAE;oBACd,IAAI,SAAS,EAAE;AACb,wBAAA,OAAO,CAAC,IAAI,CAAC,4DAA4D,EAAE,KAAK,CAAC;oBACnF;gBACF;YACF;AACF,QAAA,CAAC;AAED,QAAA,MAAM,MAAM,GAAG,YAA0B;AACvC,YAAA,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE;gBACvB,MAAM,IAAI,EAAE;YACd;iBAAO;gBACL,MAAM,KAAK,EAAE;YACf;AACF,QAAA,CAAC;AAED,QAAA,QAAQ,CAAC,MAAM,EAAE,uBAAuB,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACnE,QAAA,QAAQ,CAAC,MAAM,EAAE,uBAAuB,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAEpE,QAAA,YAAY,CAAC,MAAM,EAAE,OAAM,QAAQ,KAAG;AACpC,YAAA,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC;AAErC,YAAA,IAAI,KAAK,IAAI,QAAQ,KAAK,KAAK,EAAE;AAC/B,gBAAA,IAAI;AACF,oBAAA,MAAM,QAAQ,CAAC,oBAAoB,EAAE;gBACvC;gBAAE,OAAO,KAAK,EAAE;oBACd,IAAI,SAAS,EAAE;AACb,wBAAA,OAAO,CAAC,IAAI,CACV,0EAA0E,EAC1E,KAAK,CACN;oBACH;gBACF;AACA,gBAAA,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;YACrB;AACF,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;YACX,QAAQ;YACR,KAAK;YACL,IAAI;YACJ,MAAM;SACP;AACH,IAAA,CAAC,CAAC;AACJ;;AClKA;;AAEG;;;;"}
@@ -16,7 +16,7 @@ import { setupSync, listener } from '@signality/core/browser/listener';
16
16
  * <p>Orientation: {{ orientation() }}</p>
17
17
  * `
18
18
  * })
19
- * class OrientationComponent {
19
+ * export class OrientationDemo {
20
20
  * readonly orientation = screenOrientation();
21
21
  * }
22
22
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-screen-orientation.mjs","sources":["../../../projects/core/browser/screen-orientation/index.ts","../../../projects/core/browser/screen-orientation/signality-core-browser-screen-orientation.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport interface ScreenOrientationOptions\n extends CreateSignalOptions<OrientationType>,\n WithInjector {\n /**\n * Initial value for SSR.\n * @default 'portrait-primary'\n */\n readonly initialValue?: OrientationType;\n}\n\n/**\n * Reactive wrapper around the [Screen Orientation API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Orientation_API).\n * Returns a signal that tracks the current screen orientation.\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current orientation type (`'portrait-primary'`, `'portrait-secondary'`, `'landscape-primary'`, or `'landscape-secondary'`)\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Orientation: {{ orientation() }}</p>\n * `\n * })\n * class OrientationComponent {\n * readonly orientation = screenOrientation();\n * }\n * ```\n */\nexport function screenOrientation(options?: ScreenOrientationOptions): Signal<OrientationType> {\n const { runInContext } = setupContext(options?.injector, screenOrientation);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(options?.initialValue || 'portrait-primary');\n }\n\n const orientation = signal(screen.orientation.type, options);\n\n setupSync(() => {\n listener(screen.orientation, 'change', () => orientation.set(screen.orientation.type));\n });\n\n return orientation.asReadonly();\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAeA;;;;;;;;;;;;;;;;;;AAkBG;AACG,SAAU,iBAAiB,CAAC,OAAkC,EAAA;AAClE,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,CAAC;AAE3E,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;YACZ,OAAO,WAAW,CAAC,OAAO,EAAE,YAAY,IAAI,kBAAkB,CAAC;QACjE;AAEA,QAAA,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC;QAE5D,SAAS,CAAC,MAAK;YACb,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;AACxF,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,WAAW,CAAC,UAAU,EAAE;AACjC,IAAA,CAAC,CAAC;AACJ;;AClDA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-screen-orientation.mjs","sources":["../../../projects/core/browser/screen-orientation/index.ts","../../../projects/core/browser/screen-orientation/signality-core-browser-screen-orientation.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport interface ScreenOrientationOptions\n extends CreateSignalOptions<OrientationType>,\n WithInjector {\n /**\n * Initial value for SSR.\n * @default 'portrait-primary'\n */\n readonly initialValue?: OrientationType;\n}\n\n/**\n * Reactive wrapper around the [Screen Orientation API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Orientation_API).\n * Returns a signal that tracks the current screen orientation.\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current orientation type (`'portrait-primary'`, `'portrait-secondary'`, `'landscape-primary'`, or `'landscape-secondary'`)\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Orientation: {{ orientation() }}</p>\n * `\n * })\n * export class OrientationDemo {\n * readonly orientation = screenOrientation();\n * }\n * ```\n */\nexport function screenOrientation(options?: ScreenOrientationOptions): Signal<OrientationType> {\n const { runInContext } = setupContext(options?.injector, screenOrientation);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(options?.initialValue || 'portrait-primary');\n }\n\n const orientation = signal(screen.orientation.type, options);\n\n setupSync(() => {\n listener(screen.orientation, 'change', () => orientation.set(screen.orientation.type));\n });\n\n return orientation.asReadonly();\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAeA;;;;;;;;;;;;;;;;;;AAkBG;AACG,SAAU,iBAAiB,CAAC,OAAkC,EAAA;AAClE,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,CAAC;AAE3E,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;YACZ,OAAO,WAAW,CAAC,OAAO,EAAE,YAAY,IAAI,kBAAkB,CAAC;QACjE;AAEA,QAAA,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC;QAE5D,SAAS,CAAC,MAAK;YACb,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;AACxF,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,WAAW,CAAC,UAAU,EAAE;AACjC,IAAA,CAAC,CAAC;AACJ;;AClDA;;AAEG;;;;"}
@@ -1,6 +1,7 @@
1
1
  import { signal, untracked, isSignal } from '@angular/core';
2
2
  import { setupContext, constSignal, NOOP_FN, toValue } from '@signality/core/internal';
3
3
  import { watcher } from '@signality/core/reactivity/watcher';
4
+ import { permissionState } from '@signality/core/browser/permission-state';
4
5
 
5
6
  /**
6
7
  * Signal-based wrapper around the [Speech Recognition API](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition).
@@ -23,7 +24,7 @@ import { watcher } from '@signality/core/reactivity/watcher';
23
24
  * }
24
25
  * `
25
26
  * })
26
- * class SpeechComponent {
27
+ * export class SpeechComponent {
27
28
  * readonly recognition = speechRecognition();
28
29
  *
29
30
  * toggleRecognition() {
@@ -38,7 +39,7 @@ import { watcher } from '@signality/core/reactivity/watcher';
38
39
  */
39
40
  function speechRecognition(options) {
40
41
  const { runInContext } = setupContext(options?.injector, speechRecognition);
41
- return runInContext(({ isBrowser, onCleanup }) => {
42
+ return runInContext(({ isBrowser, injector, onCleanup }) => {
42
43
  const isSupported = constSignal(isBrowser && ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window));
43
44
  if (!isSupported()) {
44
45
  return {
@@ -54,7 +55,6 @@ function speechRecognition(options) {
54
55
  }
55
56
  const SpeechRecognitionClass = window.SpeechRecognition || window.webkitSpeechRecognition;
56
57
  const recognition = new SpeechRecognitionClass();
57
- const abortController = new AbortController();
58
58
  const { lang = 'en-US', interimResults = false, continuous = false, maxAlternatives = 1, } = options ?? {};
59
59
  recognition.lang = toValue(lang);
60
60
  recognition.continuous = continuous;
@@ -125,10 +125,7 @@ function speechRecognition(options) {
125
125
  recognition.abort();
126
126
  }
127
127
  };
128
- onCleanup(() => {
129
- abortController.abort();
130
- abort();
131
- });
128
+ onCleanup(abort);
132
129
  if (isSignal(lang)) {
133
130
  watcher(lang, newLang => {
134
131
  if (!isListening()) {
@@ -136,19 +133,10 @@ function speechRecognition(options) {
136
133
  }
137
134
  });
138
135
  }
139
- navigator.permissions.query({ name: 'microphone' }).then(result => {
140
- if (abortController.signal.aborted) {
141
- return;
136
+ watcher(permissionState('microphone'), state => {
137
+ if (state === 'denied') {
138
+ abort();
142
139
  }
143
- const check = () => {
144
- if (result.state === 'denied') {
145
- abort();
146
- }
147
- };
148
- check();
149
- result.addEventListener('change', check, {
150
- signal: abortController.signal,
151
- });
152
140
  });
153
141
  return {
154
142
  isSupported,