@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
@@ -2,37 +2,55 @@ import { type CreateSignalOptions, type Signal } from '@angular/core';
2
2
  import type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';
3
3
  export interface ElementVisibilityOptions extends CreateSignalOptions<ElementVisibilityValue>, WithInjector {
4
4
  /**
5
- * Visibility threshold(s). A number between 0 and 1, or an array of thresholds.
5
+ * Fraction of the element that must be visible to trigger a change.
6
+ * A single number or an array of thresholds, each between `0` and `1`.
7
+ *
6
8
  * @default 0
9
+ * @see [IntersectionObserver: thresholds on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/thresholds)
7
10
  */
8
11
  readonly threshold?: MaybeSignal<number | number[]>;
9
12
  /**
10
- * Scrollable ancestor element (null = viewport).
13
+ * Scrollable ancestor used as the viewport for intersection checks.
14
+ * `null` or `undefined` defaults to the browser viewport.
15
+ *
11
16
  * @default undefined
17
+ * @see [IntersectionObserver: root on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/root)
12
18
  */
13
19
  readonly root?: MaybeElementSignal<Element> | Document;
14
20
  /**
15
- * Margin around the root element.
21
+ * CSS margin applied around the root before computing intersections.
22
+ * Accepts values in the same format as the CSS `margin` property (e.g. `'10px 0px'`).
23
+ *
16
24
  * @default '0px'
25
+ * @see [IntersectionObserver: rootMargin on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin)
17
26
  */
18
27
  readonly rootMargin?: MaybeSignal<string>;
19
28
  /**
20
29
  * Initial value for SSR.
30
+ *
21
31
  * @default { isVisible: true, ratio: 1 }
22
32
  */
23
33
  readonly initialValue?: ElementVisibilityValue;
24
34
  }
25
35
  export interface ElementVisibilityValue {
26
- /** Whether the element is visible in the viewport */
36
+ /**
37
+ * Whether the element is currently intersecting the root (visible in the viewport).
38
+ *
39
+ * @see [IntersectionObserverEntry: isIntersecting on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry/isIntersecting)
40
+ */
27
41
  readonly isVisible: boolean;
28
- /** Intersection ratio (0.0 to 1.0) */
42
+ /**
43
+ * Fraction of the element visible within the root, from `0.0` (not visible) to `1.0` (fully visible).
44
+ *
45
+ * @see [IntersectionObserverEntry: intersectionRatio on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry/intersectionRatio)
46
+ */
29
47
  readonly ratio: number;
30
48
  }
31
49
  /**
32
50
  * Signal-based wrapper around the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
33
51
  *
34
52
  * @param target - The element to observe
35
- * @param options - Optional configuration including signal options (equal, debugName), observer options (threshold, root, rootMargin, initialValue), and injector
53
+ * @param options - Optional configuration
36
54
  * @returns A signal containing the current visibility state
37
55
  *
38
56
  * @example
@@ -44,7 +62,7 @@ export interface ElementVisibilityValue {
44
62
  * </div>
45
63
  * `
46
64
  * })
47
- * class VisibilityComponent {
65
+ * export class VisibilityDemo {
48
66
  * readonly section = viewChild<ElementRef>('section');
49
67
  * readonly visibility = elementVisibility(this.section);
50
68
  * }
@@ -12,5 +12,3 @@ export * from '@signality/core/elements/pointer-swipe';
12
12
  export * from '@signality/core/elements/on-disconnect';
13
13
  export * from '@signality/core/elements/scroll-position';
14
14
  export * from '@signality/core/elements/swipe';
15
- export * from '@signality/core/elements/text-selection';
16
- export * from '@signality/core/elements/window-size';
@@ -1,33 +1,53 @@
1
1
  import { type Signal } from '@angular/core';
2
2
  import type { MaybeElementSignal, WithInjector } from '@signality/core/types';
3
3
  export interface MousePosition {
4
+ /** Horizontal coordinate in pixels. */
4
5
  readonly x: number;
6
+ /** Vertical coordinate in pixels. */
5
7
  readonly y: number;
6
8
  }
9
+ /**
10
+ * Coordinate space used to report mouse position.
11
+ *
12
+ * - `'page'` — relative to the document origin, includes scroll offset.
13
+ * - `'client'` — relative to the viewport, unaffected by scroll.
14
+ * - `'screen'` — relative to the physical screen.
15
+ *
16
+ * @see [MouseEvent.pageX on MDN](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX)
17
+ * @see [MouseEvent.clientX on MDN](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX)
18
+ * @see [MouseEvent.screenX on MDN](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenX)
19
+ */
7
20
  export type MouseCoordinateType = 'page' | 'client' | 'screen';
8
21
  export interface MousePositionOptions extends WithInjector {
9
22
  /**
10
- * Element or window to track mouse position on.
23
+ * Element or `window` to track mouse position on.
24
+ * When an element signal is provided, tracking stops on element disconnect.
25
+ *
11
26
  * @default window
12
27
  */
13
28
  readonly target?: MaybeElementSignal<Element> | Window;
14
29
  /**
15
- * Coordinate type to use.
30
+ * Coordinate space for the reported position. See {@link MouseCoordinateType}.
31
+ *
16
32
  * @default 'page'
17
33
  */
18
34
  readonly type?: MouseCoordinateType;
19
35
  /**
20
- * Whether to track touch events as well.
36
+ * Whether to also track touch events (`touchmove`).
37
+ *
21
38
  * @default true
39
+ * @see [TouchEvent on MDN](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent)
22
40
  */
23
41
  readonly touch?: boolean;
24
42
  /**
25
- * Initial mouse position.
43
+ * Initial value for SSR.
44
+ *
45
+ * @default { x: 0, y: 0 }
26
46
  */
27
47
  readonly initialValue?: MousePosition;
28
48
  }
29
49
  /**
30
- * Reactive tracking of mouse position.
50
+ * Reactive tracking of mouse position using the [mousemove](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event) event.
31
51
  * Track cursor coordinates globally or relative to an element.
32
52
  *
33
53
  * @param options - Optional configuration
@@ -40,7 +60,7 @@ export interface MousePositionOptions extends WithInjector {
40
60
  * <p>Mouse position: X={{ position().x }}, Y={{ position().y }}</p>
41
61
  * `
42
62
  * })
43
- * class MouseTracker {
63
+ * export class MouseTracker {
44
64
  * readonly position = mousePosition();
45
65
  * }
46
66
  * ```
@@ -55,7 +75,7 @@ export interface MousePositionOptions extends WithInjector {
55
75
  * </div>
56
76
  * `
57
77
  * })
58
- * class MouseElementTracker {
78
+ * export class MouseElementTracker {
59
79
  * readonly box = viewChild<ElementRef>('box');
60
80
  * readonly position = mousePosition({ target: this.box });
61
81
  * }
@@ -27,7 +27,7 @@ export interface OnClickOutsideRef {
27
27
  * </div>
28
28
  * `
29
29
  * })
30
- * class DropdownComponent {
30
+ * export class Dropdown {
31
31
  * readonly dropdown = viewChild<ElementRef>('dropdown');
32
32
  * readonly isOpen = signal(true);
33
33
  *
@@ -19,7 +19,7 @@ export interface OnDisconnectRef {
19
19
  * <button (click)="remove()">Remove</button>
20
20
  * `
21
21
  * })
22
- * class DisconnectComponent {
22
+ * export class OnDisconnectDemo {
23
23
  * readonly box = viewChild<ElementRef>('box');
24
24
  *
25
25
  * constructor() {
@@ -30,7 +30,7 @@ export interface OnLongPressRef {
30
30
  * @Component({
31
31
  * template: `<button #btn>Hold me</button>`
32
32
  * })
33
- * class LongPressComponent {
33
+ * export class OnLongPressDemo {
34
34
  * readonly btn = viewChild<ElementRef>('btn');
35
35
  *
36
36
  * constructor() {
@@ -49,7 +49,7 @@ export interface PointerSwipeRef {
49
49
  * </div>
50
50
  * `
51
51
  * })
52
- * class PointerSwipeComponent {
52
+ * export class PointerSwipeComponent {
53
53
  * readonly area = viewChild<ElementRef>('area');
54
54
  * readonly sw = pointerSwipe(this.area);
55
55
  * }
@@ -71,7 +71,7 @@ export interface ScrollPositionRef {
71
71
  * }
72
72
  * `
73
73
  * })
74
- * class ScrollTracker {
74
+ * export class ScrollTracker {
75
75
  * readonly scrollPos = scrollPosition();
76
76
  * }
77
77
  * ```
@@ -87,7 +87,7 @@ export interface ScrollPositionRef {
87
87
  * <p>Scroll position: {{ pos.y() }}</p>
88
88
  * `
89
89
  * })
90
- * class ScrollableComponent {
90
+ * export class ScrollableComponent {
91
91
  * readonly scrollableEl = viewChild<ElementRef>('scrollable');
92
92
  * readonly pos = scrollPosition({ target: this.scrollableEl });
93
93
  * }
@@ -40,7 +40,7 @@ export interface SwipeRef {
40
40
  * </div>
41
41
  * `
42
42
  * })
43
- * class SwipeComponent {
43
+ * export class SwipeComponent {
44
44
  * readonly area = viewChild<ElementRef>('area');
45
45
  * readonly sw = swipe(this.area);
46
46
  * }
@@ -20,7 +20,7 @@ import { setupSync, listener } from '@signality/core/browser/listener';
20
20
  * }
21
21
  * `
22
22
  * })
23
- * class BatteryComponent {
23
+ * export class BatteryDemo {
24
24
  * readonly batteryStatus = battery();
25
25
  * }
26
26
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-battery.mjs","sources":["../../../projects/core/browser/battery/index.ts","../../../projects/core/browser/battery/signality-core-browser-battery.ts"],"sourcesContent":["import { 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 type BatteryOptions = WithInjector;\n\nexport interface BatteryRef {\n readonly charging: Signal<boolean>;\n readonly chargingTime: Signal<number>;\n readonly dischargingTime: Signal<number>;\n readonly level: Signal<number>;\n readonly isSupported: Signal<boolean>;\n}\n\n/**\n * Signal-based wrapper around the [Battery Status API](https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API).\n *\n * @param options - Optional configuration with injector\n * @returns A BatteryRef with battery status signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (batteryStatus.isSupported()) {\n * <div>\n * <p>Charging: {{ batteryStatus.charging() }}</p>\n * <p>Level: {{ batteryStatus.level() * 100 }}%</p>\n * </div>\n * }\n * `\n * })\n * class BatteryComponent {\n * readonly batteryStatus = battery();\n * }\n * ```\n */\nexport function battery(options?: BatteryOptions): BatteryRef {\n const { runInContext } = setupContext(options?.injector, battery);\n\n return runInContext(({ isBrowser, injector }) => {\n const isSupported = constSignal(\n isBrowser && 'getBattery' in navigator && typeof navigator.getBattery === 'function'\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n charging: constSignal(false),\n chargingTime: constSignal(0),\n dischargingTime: constSignal(0),\n level: constSignal(1),\n };\n }\n\n const charging = signal(false);\n const chargingTime = signal(0);\n const dischargingTime = signal(0);\n const level = signal(1);\n\n function update(this: BatteryManager) {\n charging.set(this.charging);\n chargingTime.set(this.chargingTime);\n dischargingTime.set(this.dischargingTime);\n level.set(this.level);\n }\n\n (navigator as NavigatorWithBattery).getBattery().then(battery => {\n update.call(battery);\n\n setupSync(() => {\n for (const event of BATTERY_EVENTS) {\n listener.passive(battery, event, update, { injector });\n }\n });\n });\n\n return {\n isSupported,\n charging: charging.asReadonly(),\n chargingTime: chargingTime.asReadonly(),\n dischargingTime: dischargingTime.asReadonly(),\n level: level.asReadonly(),\n };\n });\n}\n\ninterface NavigatorWithBattery extends Navigator {\n readonly getBattery: () => Promise<BatteryManager>;\n}\n\ninterface BatteryManager extends EventTarget {\n readonly charging: boolean;\n readonly chargingTime: number;\n readonly dischargingTime: number;\n readonly level: number;\n}\n\nconst BATTERY_EVENTS = [\n 'chargingchange',\n 'chargingtimechange',\n 'dischargingtimechange',\n 'levelchange',\n] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAeA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG,SAAU,OAAO,CAAC,OAAwB,EAAA;AAC9C,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC;IAEjE,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAI;AAC9C,QAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,SAAS,IAAI,YAAY,IAAI,SAAS,IAAI,OAAO,SAAS,CAAC,UAAU,KAAK,UAAU,CACrF;AAED,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;AAC5B,gBAAA,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;AAC5B,gBAAA,eAAe,EAAE,WAAW,CAAC,CAAC,CAAC;AAC/B,gBAAA,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;aACtB;QACH;AAEA,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,oDAAC;AAC9B,QAAA,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,wDAAC;AAC9B,QAAA,MAAM,eAAe,GAAG,MAAM,CAAC,CAAC,2DAAC;AACjC,QAAA,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,iDAAC;AAEvB,QAAA,SAAS,MAAM,GAAA;AACb,YAAA,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC;AAC3B,YAAA,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC;AACnC,YAAA,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC;AACzC,YAAA,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC;QACvB;QAEC,SAAkC,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,OAAO,IAAG;AAC9D,YAAA,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;YAEpB,SAAS,CAAC,MAAK;AACb,gBAAA,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE;AAClC,oBAAA,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC;gBACxD;AACF,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;AACX,YAAA,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE;AAC/B,YAAA,YAAY,EAAE,YAAY,CAAC,UAAU,EAAE;AACvC,YAAA,eAAe,EAAE,eAAe,CAAC,UAAU,EAAE;AAC7C,YAAA,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE;SAC1B;AACH,IAAA,CAAC,CAAC;AACJ;AAaA,MAAM,cAAc,GAAG;IACrB,gBAAgB;IAChB,oBAAoB;IACpB,uBAAuB;IACvB,aAAa;CACL;;ACxGV;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-battery.mjs","sources":["../../../projects/core/browser/battery/index.ts","../../../projects/core/browser/battery/signality-core-browser-battery.ts"],"sourcesContent":["import { 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 type BatteryOptions = WithInjector;\n\nexport interface BatteryRef {\n /**\n * Whether the Battery Status API is supported in the current browser.\n *\n * @see [Battery Status API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Whether the battery is currently charging.\n *\n * @see [BatteryManager: charging on MDN](https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager/charging)\n */\n readonly charging: Signal<boolean>;\n\n /**\n * Time in seconds until the battery is fully charged. `Infinity` if not charging.\n *\n * @see [BatteryManager: chargingTime on MDN](https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager/chargingTime)\n */\n readonly chargingTime: Signal<number>;\n\n /**\n * Time in seconds until the battery is fully discharged. `Infinity` if charging.\n *\n * @see [BatteryManager: dischargingTime on MDN](https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager/dischargingTime)\n */\n readonly dischargingTime: Signal<number>;\n\n /**\n * Battery level as a value between `0.0` and `1.0`.\n *\n * @see [BatteryManager: level on MDN](https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager/level)\n */\n readonly level: Signal<number>;\n}\n\n/**\n * Signal-based wrapper around the [Battery Status API](https://developer.mozilla.org/en-US/docs/Web/API/Battery_Status_API).\n *\n * @param options - Optional configuration with injector\n * @returns A BatteryRef with battery status signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (batteryStatus.isSupported()) {\n * <div>\n * <p>Charging: {{ batteryStatus.charging() }}</p>\n * <p>Level: {{ batteryStatus.level() * 100 }}%</p>\n * </div>\n * }\n * `\n * })\n * export class BatteryDemo {\n * readonly batteryStatus = battery();\n * }\n * ```\n */\nexport function battery(options?: BatteryOptions): BatteryRef {\n const { runInContext } = setupContext(options?.injector, battery);\n\n return runInContext(({ isBrowser, injector }) => {\n const isSupported = constSignal(\n isBrowser && 'getBattery' in navigator && typeof navigator.getBattery === 'function'\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n charging: constSignal(false),\n chargingTime: constSignal(0),\n dischargingTime: constSignal(0),\n level: constSignal(1),\n };\n }\n\n const charging = signal(false);\n const chargingTime = signal(0);\n const dischargingTime = signal(0);\n const level = signal(1);\n\n function update(this: BatteryManager) {\n charging.set(this.charging);\n chargingTime.set(this.chargingTime);\n dischargingTime.set(this.dischargingTime);\n level.set(this.level);\n }\n\n (navigator as NavigatorWithBattery).getBattery().then(battery => {\n update.call(battery);\n\n setupSync(() => {\n for (const event of BATTERY_EVENTS) {\n listener.passive(battery, event, update, { injector });\n }\n });\n });\n\n return {\n isSupported,\n charging: charging.asReadonly(),\n chargingTime: chargingTime.asReadonly(),\n dischargingTime: dischargingTime.asReadonly(),\n level: level.asReadonly(),\n };\n });\n}\n\ninterface NavigatorWithBattery extends Navigator {\n readonly getBattery: () => Promise<BatteryManager>;\n}\n\ninterface BatteryManager extends EventTarget {\n readonly charging: boolean;\n readonly chargingTime: number;\n readonly dischargingTime: number;\n readonly level: number;\n}\n\nconst BATTERY_EVENTS = [\n 'chargingchange',\n 'chargingtimechange',\n 'dischargingtimechange',\n 'levelchange',\n] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AA4CA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG,SAAU,OAAO,CAAC,OAAwB,EAAA;AAC9C,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC;IAEjE,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAI;AAC9C,QAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,SAAS,IAAI,YAAY,IAAI,SAAS,IAAI,OAAO,SAAS,CAAC,UAAU,KAAK,UAAU,CACrF;AAED,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;AAC5B,gBAAA,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;AAC5B,gBAAA,eAAe,EAAE,WAAW,CAAC,CAAC,CAAC;AAC/B,gBAAA,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;aACtB;QACH;AAEA,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,oDAAC;AAC9B,QAAA,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,wDAAC;AAC9B,QAAA,MAAM,eAAe,GAAG,MAAM,CAAC,CAAC,2DAAC;AACjC,QAAA,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,iDAAC;AAEvB,QAAA,SAAS,MAAM,GAAA;AACb,YAAA,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC;AAC3B,YAAA,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC;AACnC,YAAA,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC;AACzC,YAAA,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC;QACvB;QAEC,SAAkC,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,OAAO,IAAG;AAC9D,YAAA,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;YAEpB,SAAS,CAAC,MAAK;AACb,gBAAA,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE;AAClC,oBAAA,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC;gBACxD;AACF,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;AACX,YAAA,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE;AAC/B,YAAA,YAAY,EAAE,YAAY,CAAC,UAAU,EAAE;AACvC,YAAA,eAAe,EAAE,eAAe,CAAC,UAAU,EAAE;AAC7C,YAAA,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE;SAC1B;AACH,IAAA,CAAC,CAAC;AACJ;AAaA,MAAM,cAAc,GAAG;IACrB,gBAAgB;IAChB,oBAAoB;IACpB,uBAAuB;IACvB,aAAa;CACL;;ACrIV;;AAEG;;;;"}
@@ -18,7 +18,7 @@ import { setupSync, listener } from '@signality/core/browser/listener';
18
18
  * }
19
19
  * `
20
20
  * })
21
- * class BluetoothComponent {
21
+ * export class BluetoothDemo {
22
22
  * readonly bt = bluetooth();
23
23
  * }
24
24
  * ```
@@ -39,32 +39,44 @@ function bluetooth(options) {
39
39
  disconnect: NOOP_FN,
40
40
  };
41
41
  }
42
- const { bluetooth } = navigator;
42
+ const requestOptions = {
43
+ ...(options?.filters?.length
44
+ ? { filters: options.filters }
45
+ : { acceptAllDevices: options?.acceptAllDevices ?? true }),
46
+ optionalServices: options?.optionalServices ?? [],
47
+ };
43
48
  const isConnected = signal(false, ...(ngDevMode ? [{ debugName: "isConnected" }] : []));
44
49
  const isConnecting = signal(false, ...(ngDevMode ? [{ debugName: "isConnecting" }] : []));
45
50
  const device = signal(null, ...(ngDevMode ? [{ debugName: "device" }] : []));
46
51
  const server = signal(null, ...(ngDevMode ? [{ debugName: "server" }] : []));
47
52
  const error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
48
- const request = async (requestOptions) => {
53
+ let disconnectListener = null;
54
+ const disconnect = () => {
55
+ if (disconnectListener) {
56
+ disconnectListener?.destroy();
57
+ disconnectListener = null;
58
+ }
59
+ const activeDevice = untracked(device);
60
+ if (activeDevice?.gatt?.connected) {
61
+ activeDevice.gatt.disconnect();
62
+ }
63
+ device.set(null);
64
+ server.set(null);
65
+ isConnected.set(false);
66
+ };
67
+ const request = async () => {
49
68
  if (untracked(isConnecting)) {
50
69
  return;
51
70
  }
71
+ if (untracked(isConnected)) {
72
+ disconnect();
73
+ }
52
74
  isConnecting.set(true);
53
75
  error.set(null);
54
76
  try {
55
- const requestOpts = requestOptions ?? {
56
- acceptAllDevices: options?.acceptAllDevices ?? true,
57
- optionalServices: options?.optionalServices ?? [],
58
- ...(options?.filters ? { filters: options.filters, acceptAllDevices: false } : {}),
59
- };
60
- const btDevice = await bluetooth.requestDevice(requestOpts);
77
+ const btDevice = await navigator.bluetooth.requestDevice(requestOptions);
61
78
  device.set(btDevice);
62
- setupSync(() => {
63
- listener(btDevice, 'gattserverdisconnected', () => {
64
- isConnected.set(false);
65
- server.set(null);
66
- }, { injector });
67
- });
79
+ disconnectListener = setupSync(() => listener(btDevice, 'gattserverdisconnected', disconnect, { injector }));
68
80
  const gattServer = await btDevice.gatt?.connect();
69
81
  if (gattServer) {
70
82
  server.set(gattServer);
@@ -73,23 +85,12 @@ function bluetooth(options) {
73
85
  }
74
86
  catch (e) {
75
87
  error.set(e);
76
- device.set(null);
77
- server.set(null);
78
- isConnected.set(false);
88
+ disconnect();
79
89
  }
80
90
  finally {
81
91
  isConnecting.set(false);
82
92
  }
83
93
  };
84
- const disconnect = () => {
85
- const currentDevice = untracked(device);
86
- if (currentDevice?.gatt?.connected) {
87
- currentDevice.gatt.disconnect();
88
- }
89
- device.set(null);
90
- server.set(null);
91
- isConnected.set(false);
92
- };
93
94
  onCleanup(disconnect);
94
95
  return {
95
96
  isSupported,
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-bluetooth.mjs","sources":["../../../projects/core/browser/bluetooth/index.ts","../../../projects/core/browser/bluetooth/signality-core-browser-bluetooth.ts"],"sourcesContent":["import { type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_ASYNC_FN, NOOP_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport interface BluetoothOptions extends WithInjector {\n /**\n * Accept any Bluetooth device.\n */\n readonly acceptAllDevices?: boolean;\n\n /**\n * Filters for device selection.\n */\n readonly filters?: BluetoothLEScanFilter[];\n\n /**\n * Optional services to access.\n */\n readonly optionalServices?: BluetoothServiceUUID[];\n}\n\nexport interface BluetoothRef {\n /** Whether Web Bluetooth API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Whether a device is currently connected */\n readonly isConnected: Signal<boolean>;\n\n /** Whether connection is in progress */\n readonly isConnecting: Signal<boolean>;\n\n /** Connected Bluetooth device */\n readonly device: Signal<BluetoothDevice | null>;\n\n /** GATT server of connected device */\n readonly server: Signal<BluetoothRemoteGATTServer | null>;\n\n /** Last error that occurred */\n readonly error: Signal<Error | null>;\n\n /** Request device connection */\n readonly request: (options?: RequestDeviceOptions) => Promise<void>;\n\n /** Disconnect from device */\n readonly disconnect: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Web Bluetooth API](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth).\n *\n * @param options - Optional configuration\n * @returns A BluetoothRef with connection state and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <button (click)=\"bt.request()\">Connect</button>\n * @if (bt.isConnected()) {\n * <p>{{ bt.device()?.name }}</p>\n * }\n * `\n * })\n * class BluetoothComponent {\n * readonly bt = bluetooth();\n * }\n * ```\n */\nexport function bluetooth(options?: BluetoothOptions): BluetoothRef {\n const { runInContext } = setupContext(options?.injector, bluetooth);\n\n return runInContext(({ isBrowser, injector, onCleanup }) => {\n const isSupported = constSignal(isBrowser && 'bluetooth' in navigator);\n\n if (!isSupported()) {\n return {\n isSupported,\n isConnected: constSignal(false),\n isConnecting: constSignal(false),\n device: constSignal(null),\n server: constSignal(null),\n error: constSignal(null),\n request: NOOP_ASYNC_FN,\n disconnect: NOOP_FN,\n };\n }\n\n const { bluetooth } = navigator;\n\n const isConnected = signal(false);\n const isConnecting = signal(false);\n const device = signal<BluetoothDevice | null>(null);\n const server = signal<BluetoothRemoteGATTServer | null>(null);\n const error = signal<Error | null>(null);\n\n const request = async (requestOptions?: RequestDeviceOptions): Promise<void> => {\n if (untracked(isConnecting)) {\n return;\n }\n\n isConnecting.set(true);\n error.set(null);\n\n try {\n const requestOpts: RequestDeviceOptions = requestOptions ?? {\n acceptAllDevices: options?.acceptAllDevices ?? true,\n optionalServices: options?.optionalServices ?? [],\n ...(options?.filters ? { filters: options.filters, acceptAllDevices: false } : {}),\n };\n\n const btDevice = await bluetooth.requestDevice(requestOpts);\n\n device.set(btDevice);\n\n setupSync(() => {\n listener(\n btDevice,\n 'gattserverdisconnected',\n () => {\n isConnected.set(false);\n server.set(null);\n },\n { injector }\n );\n });\n\n const gattServer = await btDevice.gatt?.connect();\n\n if (gattServer) {\n server.set(gattServer);\n isConnected.set(true);\n }\n } catch (e) {\n error.set(e as Error);\n device.set(null);\n server.set(null);\n isConnected.set(false);\n } finally {\n isConnecting.set(false);\n }\n };\n\n const disconnect = () => {\n const currentDevice = untracked(device);\n\n if (currentDevice?.gatt?.connected) {\n currentDevice.gatt.disconnect();\n }\n\n device.set(null);\n server.set(null);\n isConnected.set(false);\n };\n\n onCleanup(disconnect);\n\n return {\n isSupported,\n isConnected: isConnected.asReadonly(),\n isConnecting: isConnecting.asReadonly(),\n device: device.asReadonly(),\n server: server.asReadonly(),\n error: error.asReadonly(),\n request,\n disconnect,\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-bluetooth.mjs","sources":["../../../projects/core/browser/bluetooth/index.ts","../../../projects/core/browser/bluetooth/signality-core-browser-bluetooth.ts"],"sourcesContent":["import { type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_ASYNC_FN, NOOP_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, ListenerRef, setupSync } from '@signality/core/browser/listener';\n\nexport interface BluetoothOptions extends WithInjector {\n /**\n * Accept any Bluetooth device without filtering.\n *\n * @default true\n * @see [requestDevice: acceptAllDevices on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth/requestDevice#acceptalldevices)\n */\n readonly acceptAllDevices?: boolean;\n\n /**\n * Filters for device selection. Mutually exclusive with `acceptAllDevices`.\n *\n * @default undefined\n * @see [requestDevice: filters on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth/requestDevice#filters)\n */\n readonly filters?: BluetoothLEScanFilter[];\n\n /**\n * Optional GATT services to access on the connected device.\n *\n * @default []\n * @see [requestDevice: optionalServices on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth/requestDevice#optionalservices)\n */\n readonly optionalServices?: BluetoothServiceUUID[];\n}\n\nexport interface BluetoothRef {\n /**\n * Whether Web Bluetooth API is supported in the current browser.\n *\n * @see [Web Bluetooth API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Whether a device is currently connected.\n */\n readonly isConnected: Signal<boolean>;\n\n /**\n * Whether a connection is in progress.\n */\n readonly isConnecting: Signal<boolean>;\n\n /**\n * Connected Bluetooth device.\n */\n readonly device: Signal<BluetoothDevice | null>;\n\n /**\n * GATT server of a connected device.\n */\n readonly server: Signal<BluetoothRemoteGATTServer | null>;\n\n /**\n * The last error that occurred.\n */\n readonly error: Signal<Error | null>;\n\n /**\n * Request device connection.\n */\n readonly request: () => Promise<void>;\n\n /**\n * Disconnect from a device.\n */\n readonly disconnect: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Web Bluetooth API](https://developer.mozilla.org/en-US/docs/Web/API/Bluetooth).\n *\n * @param options - Optional configuration\n * @returns A BluetoothRef with connection state and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <button (click)=\"bt.request()\">Connect</button>\n * @if (bt.isConnected()) {\n * <p>{{ bt.device()?.name }}</p>\n * }\n * `\n * })\n * export class BluetoothDemo {\n * readonly bt = bluetooth();\n * }\n * ```\n */\nexport function bluetooth(options?: BluetoothOptions): BluetoothRef {\n const { runInContext } = setupContext(options?.injector, bluetooth);\n\n return runInContext(({ isBrowser, injector, onCleanup }) => {\n const isSupported = constSignal(isBrowser && 'bluetooth' in navigator);\n\n if (!isSupported()) {\n return {\n isSupported,\n isConnected: constSignal(false),\n isConnecting: constSignal(false),\n device: constSignal(null),\n server: constSignal(null),\n error: constSignal(null),\n request: NOOP_ASYNC_FN,\n disconnect: NOOP_FN,\n };\n }\n\n const requestOptions = {\n ...(options?.filters?.length\n ? { filters: options.filters }\n : { acceptAllDevices: options?.acceptAllDevices ?? true }),\n optionalServices: options?.optionalServices ?? [],\n };\n\n const isConnected = signal(false);\n const isConnecting = signal(false);\n const device = signal<BluetoothDevice | null>(null);\n const server = signal<BluetoothRemoteGATTServer | null>(null);\n const error = signal<Error | null>(null);\n\n let disconnectListener: ListenerRef | null = null;\n\n const disconnect = () => {\n if (disconnectListener) {\n disconnectListener?.destroy();\n disconnectListener = null;\n }\n\n const activeDevice = untracked(device);\n\n if (activeDevice?.gatt?.connected) {\n activeDevice.gatt.disconnect();\n }\n\n device.set(null);\n server.set(null);\n isConnected.set(false);\n };\n\n const request = async (): Promise<void> => {\n if (untracked(isConnecting)) {\n return;\n }\n\n if (untracked(isConnected)) {\n disconnect();\n }\n\n isConnecting.set(true);\n error.set(null);\n\n try {\n const btDevice = await navigator.bluetooth.requestDevice(requestOptions);\n\n device.set(btDevice);\n\n disconnectListener = setupSync(() =>\n listener(btDevice, 'gattserverdisconnected', disconnect, { injector })\n );\n\n const gattServer = await btDevice.gatt?.connect();\n\n if (gattServer) {\n server.set(gattServer);\n isConnected.set(true);\n }\n } catch (e) {\n error.set(e as Error);\n disconnect();\n } finally {\n isConnecting.set(false);\n }\n };\n\n onCleanup(disconnect);\n\n return {\n isSupported,\n isConnected: isConnected.asReadonly(),\n isConnecting: isConnecting.asReadonly(),\n device: device.asReadonly(),\n server: server.asReadonly(),\n error: error.asReadonly(),\n request,\n disconnect,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -3,21 +3,30 @@ import { setupContext, constSignal } from '@signality/core/internal';
3
3
  import { mediaQuery } from '@signality/core/browser/media-query';
4
4
 
5
5
  /**
6
- * Reactive breakpoint matching using matchMedia.
7
- * Track responsive breakpoints with Angular signals.
6
+ * Reactive breakpoint matching using [matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia).
8
7
  *
9
8
  * @param map - Object mapping breakpoint names to media queries
10
9
  * @param options - Optional configuration
11
- * @returns An object with signals for each breakpoint
10
+ * @returns An object with signals for each breakpoint and a `current` signal with active breakpoint keys
12
11
  *
13
12
  * @example
14
13
  * ```typescript
15
- * const bp = breakpoints({
16
- * mobile: '(max-width: 767px)',
17
- * desktop: '(min-width: 768px)',
18
- * });
19
- *
20
- * // In template: @if (bp.mobile()) { ... }
14
+ * @Component({
15
+ * template: `
16
+ * @if (bp.mobile()) {
17
+ * <p>Mobile layout</p>
18
+ * } @else {
19
+ * <p>Desktop layout</p>
20
+ * }
21
+ * <p>Active: {{ bp.current() }}</p>
22
+ * `
23
+ * })
24
+ * export class Layout {
25
+ * readonly bp = breakpoints({
26
+ * mobile: '(max-width: 767px)',
27
+ * desktop: '(min-width: 768px)',
28
+ * });
29
+ * }
21
30
  * ```
22
31
  */
23
32
  function breakpoints(map, options) {
@@ -33,7 +42,7 @@ function breakpoints(map, options) {
33
42
  if (isServer) {
34
43
  return {
35
44
  ...queries,
36
- current: constSignal(Object.keys(map).filter(key => !!initialValues[key])),
45
+ current: constSignal(Object.keys(map).filter(key => initialValues[key])),
37
46
  };
38
47
  }
39
48
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-breakpoints.mjs","sources":["../../../projects/core/browser/breakpoints/index.ts","../../../projects/core/browser/breakpoints/signality-core-browser-breakpoints.ts"],"sourcesContent":["import { computed, type Signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { mediaQuery } from '@signality/core/browser/media-query';\n\nexport interface BreakpointsOptions<T extends Record<string, string>> extends WithInjector {\n /**\n * Initial values for SSR.\n */\n readonly initialValue?: Partial<Record<keyof T, boolean>>;\n}\n\nexport type BreakpointsRef<T extends Record<string, string>> = {\n readonly [K in keyof T]: Signal<boolean>;\n} & {\n readonly current: Signal<(keyof T)[]>;\n};\n\n/**\n * Reactive breakpoint matching using matchMedia.\n * Track responsive breakpoints with Angular signals.\n *\n * @param map - Object mapping breakpoint names to media queries\n * @param options - Optional configuration\n * @returns An object with signals for each breakpoint\n *\n * @example\n * ```typescript\n * const bp = breakpoints({\n * mobile: '(max-width: 767px)',\n * desktop: '(min-width: 768px)',\n * });\n *\n * // In template: @if (bp.mobile()) { ... }\n * ```\n */\nexport function breakpoints<T extends Record<string, string>>(\n map: T,\n options?: BreakpointsOptions<T>\n): BreakpointsRef<T> {\n const { runInContext } = setupContext(options?.injector, breakpoints);\n\n return runInContext(({ isServer }) => {\n const initialValues = (options?.initialValue ?? {}) as Record<keyof T, boolean>;\n const queries: Record<string, Signal<boolean>> = {};\n\n for (const key of Object.keys(map)) {\n const query = map[key];\n const initialValue = initialValues[key] ?? false;\n queries[key] = mediaQuery(query, { initialValue });\n }\n\n if (isServer) {\n return {\n ...queries,\n current: constSignal(Object.keys(map).filter(key => !!initialValues[key])),\n } as BreakpointsRef<T>;\n }\n\n return {\n ...queries,\n current: computed(() => Object.keys(map).filter(key => queries[key]())),\n } as BreakpointsRef<T>;\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAkBA;;;;;;;;;;;;;;;;;AAiBG;AACG,SAAU,WAAW,CACzB,GAAM,EACN,OAA+B,EAAA;AAE/B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC;AAErE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,MAAM,aAAa,IAAI,OAAO,EAAE,YAAY,IAAI,EAAE,CAA6B;QAC/E,MAAM,OAAO,GAAoC,EAAE;QAEnD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;AAClC,YAAA,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC;YACtB,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,KAAK;AAChD,YAAA,OAAO,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,EAAE,YAAY,EAAE,CAAC;QACpD;QAEA,IAAI,QAAQ,EAAE;YACZ,OAAO;AACL,gBAAA,GAAG,OAAO;gBACV,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;aACtD;QACxB;QAEA,OAAO;AACL,YAAA,GAAG,OAAO;YACV,OAAO,EAAE,QAAQ,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;SACnD;AACxB,IAAA,CAAC,CAAC;AACJ;;AChEA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-breakpoints.mjs","sources":["../../../projects/core/browser/breakpoints/index.ts","../../../projects/core/browser/breakpoints/signality-core-browser-breakpoints.ts"],"sourcesContent":["import { computed, type Signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { mediaQuery } from '@signality/core/browser/media-query';\n\nexport interface BreakpointsOptions<T extends Record<string, string>> extends WithInjector {\n /**\n * Initial values for SSR.\n */\n readonly initialValue?: Partial<Record<keyof T, boolean>>;\n}\n\nexport type BreakpointsRef<T extends Record<string, string>> = {\n readonly [K in keyof T]: Signal<boolean>;\n} & {\n /**\n * List of currently active breakpoint keys — keys whose media query matches at the moment.\n * Updated reactively whenever any breakpoint changes.\n */\n readonly current: Signal<(keyof T)[]>;\n};\n\n/**\n * Reactive breakpoint matching using [matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia).\n *\n * @param map - Object mapping breakpoint names to media queries\n * @param options - Optional configuration\n * @returns An object with signals for each breakpoint and a `current` signal with active breakpoint keys\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (bp.mobile()) {\n * <p>Mobile layout</p>\n * } @else {\n * <p>Desktop layout</p>\n * }\n * <p>Active: {{ bp.current() }}</p>\n * `\n * })\n * export class Layout {\n * readonly bp = breakpoints({\n * mobile: '(max-width: 767px)',\n * desktop: '(min-width: 768px)',\n * });\n * }\n * ```\n */\nexport function breakpoints<T extends Record<string, string>>(\n map: T,\n options?: BreakpointsOptions<T>\n): BreakpointsRef<T> {\n const { runInContext } = setupContext(options?.injector, breakpoints);\n\n return runInContext(({ isServer }) => {\n const initialValues = (options?.initialValue ?? {}) as Record<keyof T, boolean>;\n const queries: Record<string, Signal<boolean>> = {};\n\n for (const key of Object.keys(map)) {\n const query = map[key];\n const initialValue = initialValues[key] ?? false;\n queries[key] = mediaQuery(query, { initialValue });\n }\n\n if (isServer) {\n return {\n ...queries,\n current: constSignal(Object.keys(map).filter(key => initialValues[key])),\n } as BreakpointsRef<T>;\n }\n\n return {\n ...queries,\n current: computed(() => Object.keys(map).filter(key => queries[key]())),\n } as BreakpointsRef<T>;\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAsBA;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BG;AACG,SAAU,WAAW,CACzB,GAAM,EACN,OAA+B,EAAA;AAE/B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC;AAErE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,MAAM,aAAa,IAAI,OAAO,EAAE,YAAY,IAAI,EAAE,CAA6B;QAC/E,MAAM,OAAO,GAAoC,EAAE;QAEnD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;AAClC,YAAA,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC;YACtB,MAAM,YAAY,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,KAAK;AAChD,YAAA,OAAO,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,EAAE,YAAY,EAAE,CAAC;QACpD;QAEA,IAAI,QAAQ,EAAE;YACZ,OAAO;AACL,gBAAA,GAAG,OAAO;gBACV,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;aACpD;QACxB;QAEA,OAAO;AACL,YAAA,GAAG,OAAO;YACV,OAAO,EAAE,QAAQ,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;SACnD;AACxB,IAAA,CAAC,CAAC;AACJ;;AC7EA;;AAEG;;;;"}
@@ -19,7 +19,7 @@ import { setupSync, listener } from '@signality/core/browser/listener';
19
19
  * <button (click)="sendMessage()">Send Message</button>
20
20
  * `
21
21
  * })
22
- * class ChatComponent {
22
+ * export class ChatDemo {
23
23
  * readonly channel = broadcastChannel<string>('my-channel');
24
24
  *
25
25
  * sendMessage() {
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-broadcast-channel.mjs","sources":["../../../projects/core/browser/broadcast-channel/index.ts","../../../projects/core/browser/broadcast-channel/signality-core-browser-broadcast-channel.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport { constSignal, NOOP_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type BroadcastChannelOptions = WithInjector;\n\nexport interface BroadcastChannelRef<T> {\n /** Last received data */\n readonly data: Signal<T | null>;\n\n /** The last error that occurred */\n readonly error: Signal<MessageEvent | null>;\n\n /** Whether the channel is closed */\n readonly isClosed: Signal<boolean>;\n\n /** Send a message to all tabs */\n readonly post: (data: T) => void;\n\n /** Close the channel */\n readonly close: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Broadcast Channel API](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API).\n *\n * @param name - Channel name (must match across tabs)\n * @param options - Optional configuration\n * @returns A BroadcastChannelRef with data signal and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (channel.data(); as data) {\n * <p>Received: {{ data }}</p>\n * }\n * <button (click)=\"sendMessage()\">Send Message</button>\n * `\n * })\n * class ChatComponent {\n * readonly channel = broadcastChannel<string>('my-channel');\n *\n * sendMessage() {\n * this.channel.post('Hello from this tab!');\n * }\n * }\n * ```\n */\nexport function broadcastChannel<T>(\n name: string,\n options?: BroadcastChannelOptions\n): BroadcastChannelRef<T> {\n const { runInContext } = setupContext(options?.injector, broadcastChannel);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return {\n data: constSignal(null),\n error: constSignal(null),\n isClosed: constSignal(false),\n post: NOOP_FN,\n close: NOOP_FN,\n };\n }\n\n const data = signal<T | null>(null);\n const error = signal<MessageEvent | null>(null);\n const isClosed = signal(false);\n\n const channel = new BroadcastChannel(name);\n\n const post = (message: T) => {\n channel.postMessage(message);\n };\n\n const close = () => {\n channel.close();\n isClosed.set(true);\n };\n\n setupSync(() => {\n listener(channel, 'message', (e: MessageEvent<T>) => data.set(e.data));\n listener(channel, 'messageerror', error.set);\n });\n\n onCleanup(close);\n\n return {\n data: data.asReadonly(),\n error: error.asReadonly(),\n isClosed: isClosed.asReadonly(),\n post,\n close,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAwBA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACG,SAAU,gBAAgB,CAC9B,IAAY,EACZ,OAAiC,EAAA;AAEjC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,CAAC;IAE1E,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAI;QAC9C,IAAI,QAAQ,EAAE;YACZ,OAAO;AACL,gBAAA,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC;AACvB,gBAAA,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC;AACxB,gBAAA,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;AAC5B,gBAAA,IAAI,EAAE,OAAO;AACb,gBAAA,KAAK,EAAE,OAAO;aACf;QACH;AAEA,QAAA,MAAM,IAAI,GAAG,MAAM,CAAW,IAAI,gDAAC;AACnC,QAAA,MAAM,KAAK,GAAG,MAAM,CAAsB,IAAI,iDAAC;AAC/C,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,oDAAC;AAE9B,QAAA,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC;AAE1C,QAAA,MAAM,IAAI,GAAG,CAAC,OAAU,KAAI;AAC1B,YAAA,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC;AAC9B,QAAA,CAAC;QAED,MAAM,KAAK,GAAG,MAAK;YACjB,OAAO,CAAC,KAAK,EAAE;AACf,YAAA,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;AACpB,QAAA,CAAC;QAED,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,CAAkB,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACtE,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC;AAC9C,QAAA,CAAC,CAAC;QAEF,SAAS,CAAC,KAAK,CAAC;QAEhB,OAAO;AACL,YAAA,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;AACvB,YAAA,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE;AACzB,YAAA,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE;YAC/B,IAAI;YACJ,KAAK;SACN;AACH,IAAA,CAAC,CAAC;AACJ;;ACjGA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-broadcast-channel.mjs","sources":["../../../projects/core/browser/broadcast-channel/index.ts","../../../projects/core/browser/broadcast-channel/signality-core-browser-broadcast-channel.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport { constSignal, NOOP_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type BroadcastChannelOptions = WithInjector;\n\nexport interface BroadcastChannelRef<T> {\n /** Last received data */\n readonly data: Signal<T | null>;\n\n /** The last error that occurred */\n readonly error: Signal<MessageEvent | null>;\n\n /** Whether the channel is closed */\n readonly isClosed: Signal<boolean>;\n\n /** Send a message to all tabs */\n readonly post: (data: T) => void;\n\n /** Close the channel */\n readonly close: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Broadcast Channel API](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API).\n *\n * @param name - Channel name (must match across tabs)\n * @param options - Optional configuration\n * @returns A BroadcastChannelRef with data signal and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (channel.data(); as data) {\n * <p>Received: {{ data }}</p>\n * }\n * <button (click)=\"sendMessage()\">Send Message</button>\n * `\n * })\n * export class ChatDemo {\n * readonly channel = broadcastChannel<string>('my-channel');\n *\n * sendMessage() {\n * this.channel.post('Hello from this tab!');\n * }\n * }\n * ```\n */\nexport function broadcastChannel<T>(\n name: string,\n options?: BroadcastChannelOptions\n): BroadcastChannelRef<T> {\n const { runInContext } = setupContext(options?.injector, broadcastChannel);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return {\n data: constSignal(null),\n error: constSignal(null),\n isClosed: constSignal(false),\n post: NOOP_FN,\n close: NOOP_FN,\n };\n }\n\n const data = signal<T | null>(null);\n const error = signal<MessageEvent | null>(null);\n const isClosed = signal(false);\n\n const channel = new BroadcastChannel(name);\n\n const post = (message: T) => {\n channel.postMessage(message);\n };\n\n const close = () => {\n channel.close();\n isClosed.set(true);\n };\n\n setupSync(() => {\n listener(channel, 'message', (e: MessageEvent<T>) => data.set(e.data));\n listener(channel, 'messageerror', error.set);\n });\n\n onCleanup(close);\n\n return {\n data: data.asReadonly(),\n error: error.asReadonly(),\n isClosed: isClosed.asReadonly(),\n post,\n close,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAwBA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACG,SAAU,gBAAgB,CAC9B,IAAY,EACZ,OAAiC,EAAA;AAEjC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,CAAC;IAE1E,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAI;QAC9C,IAAI,QAAQ,EAAE;YACZ,OAAO;AACL,gBAAA,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC;AACvB,gBAAA,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC;AACxB,gBAAA,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;AAC5B,gBAAA,IAAI,EAAE,OAAO;AACb,gBAAA,KAAK,EAAE,OAAO;aACf;QACH;AAEA,QAAA,MAAM,IAAI,GAAG,MAAM,CAAW,IAAI,gDAAC;AACnC,QAAA,MAAM,KAAK,GAAG,MAAM,CAAsB,IAAI,iDAAC;AAC/C,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,oDAAC;AAE9B,QAAA,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC;AAE1C,QAAA,MAAM,IAAI,GAAG,CAAC,OAAU,KAAI;AAC1B,YAAA,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC;AAC9B,QAAA,CAAC;QAED,MAAM,KAAK,GAAG,MAAK;YACjB,OAAO,CAAC,KAAK,EAAE;AACf,YAAA,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;AACpB,QAAA,CAAC;QAED,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC,CAAkB,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACtE,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC;AAC9C,QAAA,CAAC,CAAC;QAEF,SAAS,CAAC,KAAK,CAAC;QAEhB,OAAO;AACL,YAAA,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;AACvB,YAAA,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE;AACzB,YAAA,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE;YAC/B,IAAI;YACJ,KAAK;SACN;AACH,IAAA,CAAC,CAAC;AACJ;;ACjGA;;AAEG;;;;"}
@@ -20,7 +20,7 @@ import { setupSync, listener } from '@signality/core/browser/listener';
20
20
  * }
21
21
  * `
22
22
  * })
23
- * class LanguageComponent {
23
+ * export class LanguageDemo {
24
24
  * readonly language = browserLanguage();
25
25
  * }
26
26
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-browser-language.mjs","sources":["../../../projects/core/browser/browser-language/index.ts","../../../projects/core/browser/browser-language/signality-core-browser-browser-language.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 interface BrowserLanguageOptions extends CreateSignalOptions<string>, WithInjector {\n /**\n * Initial value for SSR.\n * @default ''\n */\n readonly initialValue?: string;\n}\n\n/**\n * Reactive wrapper around the [Navigator.language](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language) property.\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current browser language in BCP 47 format (e.g., 'en-US', 'fr-FR')\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Current language: {{ language() }}</p>\n * @if (language() === 'en-US') {\n * <p>Welcome!</p>\n * } @else if (language() === 'fr-FR') {\n * <p>Bienvenue!</p>\n * }\n * `\n * })\n * class LanguageComponent {\n * readonly language = browserLanguage();\n * }\n * ```\n */\nexport function browserLanguage(options?: BrowserLanguageOptions): Signal<string> {\n const { runInContext } = setupContext(options?.injector, browserLanguage);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(options?.initialValue ?? '');\n }\n\n const language = signal(navigator.language, options);\n\n setupSync(() => {\n listener(window, 'languagechange', () => language.set(navigator.language));\n });\n\n return language.asReadonly();\n });\n}\n\nexport const BROWSER_LANGUAGE = /* @__PURE__ */ createToken(browserLanguage);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAaA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG,SAAU,eAAe,CAAC,OAAgC,EAAA;AAC9D,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,eAAe,CAAC;AAEzE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;YACZ,OAAO,WAAW,CAAC,OAAO,EAAE,YAAY,IAAI,EAAE,CAAC;QACjD;QAEA,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC;QAEpD,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC5E,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,QAAQ,CAAC,UAAU,EAAE;AAC9B,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,gBAAgB,mBAAmB,WAAW,CAAC,eAAe;;ACtD3E;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-browser-language.mjs","sources":["../../../projects/core/browser/browser-language/index.ts","../../../projects/core/browser/browser-language/signality-core-browser-browser-language.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 interface BrowserLanguageOptions extends CreateSignalOptions<string>, WithInjector {\n /**\n * Initial value for SSR.\n * @default ''\n */\n readonly initialValue?: string;\n}\n\n/**\n * Reactive wrapper around the [Navigator.language](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/language) property.\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current browser language in BCP 47 format (e.g., 'en-US', 'fr-FR')\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Current language: {{ language() }}</p>\n * @if (language() === 'en-US') {\n * <p>Welcome!</p>\n * } @else if (language() === 'fr-FR') {\n * <p>Bienvenue!</p>\n * }\n * `\n * })\n * export class LanguageDemo {\n * readonly language = browserLanguage();\n * }\n * ```\n */\nexport function browserLanguage(options?: BrowserLanguageOptions): Signal<string> {\n const { runInContext } = setupContext(options?.injector, browserLanguage);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(options?.initialValue ?? '');\n }\n\n const language = signal(navigator.language, options);\n\n setupSync(() => {\n listener(window, 'languagechange', () => language.set(navigator.language));\n });\n\n return language.asReadonly();\n });\n}\n\nexport const BROWSER_LANGUAGE = /* @__PURE__ */ createToken(browserLanguage);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAaA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG,SAAU,eAAe,CAAC,OAAgC,EAAA;AAC9D,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,eAAe,CAAC;AAEzE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;YACZ,OAAO,WAAW,CAAC,OAAO,EAAE,YAAY,IAAI,EAAE,CAAC;QACjD;QAEA,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC;QAEpD,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC5E,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,QAAQ,CAAC,UAAU,EAAE;AAC9B,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,gBAAgB,mBAAmB,WAAW,CAAC,eAAe;;ACtD3E;;AAEG;;;;"}
@@ -18,7 +18,7 @@ import { setupContext, constSignal, NOOP_ASYNC_FN, toValue } from '@signality/co
18
18
  * }
19
19
  * `
20
20
  * })
21
- * class ClipboardComponent {
21
+ * export class ClipboardDemo {
22
22
  * readonly cb = clipboard();
23
23
  *
24
24
  * async copyText(text: string) {
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-clipboard.mjs","sources":["../../../projects/core/browser/clipboard/index.ts","../../../projects/core/browser/clipboard/signality-core-browser-clipboard.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport {\n constSignal,\n NOOP_ASYNC_FN,\n setupContext,\n type Timer,\n toValue,\n} from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\n\nexport interface ClipboardOptions extends WithInjector {\n /**\n * How long `copied` stays `true` after copy (ms).\n * @default 1500\n */\n readonly copiedDuration?: MaybeSignal<number>;\n}\n\nexport interface ClipboardRef {\n /** Whether Clipboard API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Current clipboard text content */\n readonly text: Signal<string>;\n\n /** Whether content was recently copied (resets after timeout) */\n readonly copied: Signal<boolean>;\n\n /** Copy text to clipboard */\n readonly copy: (text: string) => Promise<void>;\n\n /** Read text from clipboard */\n readonly paste: () => Promise<string>;\n}\n\n/**\n * Signal-based wrapper around the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API).\n *\n * @param options - Optional configuration\n * @returns A ClipboardRef with text, copied, isSupported signals and copy/paste methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <input #input value=\"Hello World!\" />\n * <button (click)=\"copyText(input.value)\">Copy</button>\n * @if (cb.copied()) {\n * <span>Copied!</span>\n * }\n * `\n * })\n * class ClipboardComponent {\n * readonly cb = clipboard();\n *\n * async copyText(text: string) {\n * await this.cb.copy(text);\n * }\n * }\n * ```\n */\nexport function clipboard(options?: ClipboardOptions): ClipboardRef {\n const { runInContext } = setupContext(options?.injector, clipboard);\n\n return runInContext(({ isBrowser, onCleanup }) => {\n const isSupported = constSignal(\n isBrowser && 'clipboard' in navigator && typeof navigator.clipboard?.writeText === 'function'\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n text: constSignal(''),\n copied: constSignal(false),\n copy: NOOP_ASYNC_FN,\n paste: () => Promise.resolve(''),\n };\n }\n\n const copiedDuration = options?.copiedDuration ?? 1500;\n\n const text = signal('');\n const copied = signal(false);\n\n let copiedTimeout: Timer;\n\n const copy = async (value: string): Promise<void> => {\n try {\n await navigator.clipboard.writeText(value);\n\n text.set(value);\n copied.set(true);\n\n if (copiedTimeout) {\n clearTimeout(copiedTimeout);\n }\n\n copiedTimeout = setTimeout(() => {\n copied.set(false);\n copiedTimeout = undefined;\n }, toValue.untracked(copiedDuration));\n } catch (error) {\n copied.set(false);\n if (ngDevMode) {\n console.warn(\n `[clipboard] Failed to copy text to clipboard. ` +\n `This may be due to permission denied or clipboard access failed.`,\n error\n );\n }\n }\n };\n\n const paste = async (): Promise<string> => {\n try {\n const value = await navigator.clipboard.readText();\n text.set(value);\n return value;\n } catch (error) {\n if (ngDevMode) {\n console.warn(\n `[clipboard] Failed to read text from clipboard. ` +\n `This may be due to permission denied or clipboard access failed.`,\n error\n );\n }\n return '';\n }\n };\n\n onCleanup(() => {\n if (copiedTimeout) {\n clearTimeout(copiedTimeout);\n }\n });\n\n return {\n isSupported,\n text: text.asReadonly(),\n copied: copied.asReadonly(),\n copy,\n paste,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAmCA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACG,SAAU,SAAS,CAAC,OAA0B,EAAA;AAClD,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC;IAEnE,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,KAAI;AAC/C,QAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,SAAS,IAAI,WAAW,IAAI,SAAS,IAAI,OAAO,SAAS,CAAC,SAAS,EAAE,SAAS,KAAK,UAAU,CAC9F;AAED,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;AACrB,gBAAA,MAAM,EAAE,WAAW,CAAC,KAAK,CAAC;AAC1B,gBAAA,IAAI,EAAE,aAAa;gBACnB,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;aACjC;QACH;AAEA,QAAA,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,IAAI;AAEtD,QAAA,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,gDAAC;AACvB,QAAA,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,kDAAC;AAE5B,QAAA,IAAI,aAAoB;AAExB,QAAA,MAAM,IAAI,GAAG,OAAO,KAAa,KAAmB;AAClD,YAAA,IAAI;gBACF,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC;AAE1C,gBAAA,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;AACf,gBAAA,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;gBAEhB,IAAI,aAAa,EAAE;oBACjB,YAAY,CAAC,aAAa,CAAC;gBAC7B;AAEA,gBAAA,aAAa,GAAG,UAAU,CAAC,MAAK;AAC9B,oBAAA,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;oBACjB,aAAa,GAAG,SAAS;gBAC3B,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;YACvC;YAAE,OAAO,KAAK,EAAE;AACd,gBAAA,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;gBACjB,IAAI,SAAS,EAAE;oBACb,OAAO,CAAC,IAAI,CACV,CAAA,8CAAA,CAAgD;wBAC9C,CAAA,gEAAA,CAAkE,EACpE,KAAK,CACN;gBACH;YACF;AACF,QAAA,CAAC;AAED,QAAA,MAAM,KAAK,GAAG,YAA4B;AACxC,YAAA,IAAI;gBACF,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,QAAQ,EAAE;AAClD,gBAAA,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;AACf,gBAAA,OAAO,KAAK;YACd;YAAE,OAAO,KAAK,EAAE;gBACd,IAAI,SAAS,EAAE;oBACb,OAAO,CAAC,IAAI,CACV,CAAA,gDAAA,CAAkD;wBAChD,CAAA,gEAAA,CAAkE,EACpE,KAAK,CACN;gBACH;AACA,gBAAA,OAAO,EAAE;YACX;AACF,QAAA,CAAC;QAED,SAAS,CAAC,MAAK;YACb,IAAI,aAAa,EAAE;gBACjB,YAAY,CAAC,aAAa,CAAC;YAC7B;AACF,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;AACX,YAAA,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;AACvB,YAAA,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE;YAC3B,IAAI;YACJ,KAAK;SACN;AACH,IAAA,CAAC,CAAC;AACJ;;AChJA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-clipboard.mjs","sources":["../../../projects/core/browser/clipboard/index.ts","../../../projects/core/browser/clipboard/signality-core-browser-clipboard.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport {\n constSignal,\n NOOP_ASYNC_FN,\n setupContext,\n type Timer,\n toValue,\n} from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\n\nexport interface ClipboardOptions extends WithInjector {\n /**\n * How long `copied` stays `true` after copy (ms).\n * @default 1500\n */\n readonly copiedDuration?: MaybeSignal<number>;\n}\n\nexport interface ClipboardRef {\n /**\n * Whether the Clipboard API is supported in the current browser.\n *\n * @see [Clipboard API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * The most recently copied or pasted text.\n */\n readonly text: Signal<string>;\n\n /**\n * Whether the text was recently copied. Resets to `false` after `copiedDuration` ms.\n */\n readonly copied: Signal<boolean>;\n\n /**\n * Write text to the clipboard.\n *\n * @see [Clipboard: writeText() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText)\n */\n readonly copy: (text: string) => Promise<void>;\n\n /**\n * Read text from the clipboard.\n *\n * @see [Clipboard: readText() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText)\n */\n readonly paste: () => Promise<string>;\n}\n\n/**\n * Signal-based wrapper around the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API).\n *\n * @param options - Optional configuration\n * @returns A ClipboardRef with text, copied, isSupported signals and copy/paste methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <input #input value=\"Hello World!\" />\n * <button (click)=\"copyText(input.value)\">Copy</button>\n * @if (cb.copied()) {\n * <span>Copied!</span>\n * }\n * `\n * })\n * export class ClipboardDemo {\n * readonly cb = clipboard();\n *\n * async copyText(text: string) {\n * await this.cb.copy(text);\n * }\n * }\n * ```\n */\nexport function clipboard(options?: ClipboardOptions): ClipboardRef {\n const { runInContext } = setupContext(options?.injector, clipboard);\n\n return runInContext(({ isBrowser, onCleanup }) => {\n const isSupported = constSignal(\n isBrowser && 'clipboard' in navigator && typeof navigator.clipboard?.writeText === 'function'\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n text: constSignal(''),\n copied: constSignal(false),\n copy: NOOP_ASYNC_FN,\n paste: () => Promise.resolve(''),\n };\n }\n\n const copiedDuration = options?.copiedDuration ?? 1500;\n\n const text = signal('');\n const copied = signal(false);\n\n let copiedTimeout: Timer;\n\n const copy = async (value: string): Promise<void> => {\n try {\n await navigator.clipboard.writeText(value);\n\n text.set(value);\n copied.set(true);\n\n if (copiedTimeout) {\n clearTimeout(copiedTimeout);\n }\n\n copiedTimeout = setTimeout(() => {\n copied.set(false);\n copiedTimeout = undefined;\n }, toValue.untracked(copiedDuration));\n } catch (error) {\n copied.set(false);\n if (ngDevMode) {\n console.warn(\n `[clipboard] Failed to copy text to clipboard. ` +\n `This may be due to permission denied or clipboard access failed.`,\n error\n );\n }\n }\n };\n\n const paste = async (): Promise<string> => {\n try {\n const value = await navigator.clipboard.readText();\n text.set(value);\n return value;\n } catch (error) {\n if (ngDevMode) {\n console.warn(\n `[clipboard] Failed to read text from clipboard. ` +\n `This may be due to permission denied or clipboard access failed.`,\n error\n );\n }\n return '';\n }\n };\n\n onCleanup(() => {\n if (copiedTimeout) {\n clearTimeout(copiedTimeout);\n }\n });\n\n return {\n isSupported,\n text: text.asReadonly(),\n copied: copied.asReadonly(),\n copy,\n paste,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAmDA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACG,SAAU,SAAS,CAAC,OAA0B,EAAA;AAClD,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC;IAEnE,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,KAAI;AAC/C,QAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,SAAS,IAAI,WAAW,IAAI,SAAS,IAAI,OAAO,SAAS,CAAC,SAAS,EAAE,SAAS,KAAK,UAAU,CAC9F;AAED,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;AACrB,gBAAA,MAAM,EAAE,WAAW,CAAC,KAAK,CAAC;AAC1B,gBAAA,IAAI,EAAE,aAAa;gBACnB,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;aACjC;QACH;AAEA,QAAA,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,IAAI;AAEtD,QAAA,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,gDAAC;AACvB,QAAA,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,kDAAC;AAE5B,QAAA,IAAI,aAAoB;AAExB,QAAA,MAAM,IAAI,GAAG,OAAO,KAAa,KAAmB;AAClD,YAAA,IAAI;gBACF,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC;AAE1C,gBAAA,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;AACf,gBAAA,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;gBAEhB,IAAI,aAAa,EAAE;oBACjB,YAAY,CAAC,aAAa,CAAC;gBAC7B;AAEA,gBAAA,aAAa,GAAG,UAAU,CAAC,MAAK;AAC9B,oBAAA,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;oBACjB,aAAa,GAAG,SAAS;gBAC3B,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;YACvC;YAAE,OAAO,KAAK,EAAE;AACd,gBAAA,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;gBACjB,IAAI,SAAS,EAAE;oBACb,OAAO,CAAC,IAAI,CACV,CAAA,8CAAA,CAAgD;wBAC9C,CAAA,gEAAA,CAAkE,EACpE,KAAK,CACN;gBACH;YACF;AACF,QAAA,CAAC;AAED,QAAA,MAAM,KAAK,GAAG,YAA4B;AACxC,YAAA,IAAI;gBACF,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,QAAQ,EAAE;AAClD,gBAAA,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;AACf,gBAAA,OAAO,KAAK;YACd;YAAE,OAAO,KAAK,EAAE;gBACd,IAAI,SAAS,EAAE;oBACb,OAAO,CAAC,IAAI,CACV,CAAA,gDAAA,CAAkD;wBAChD,CAAA,gEAAA,CAAkE,EACpE,KAAK,CACN;gBACH;AACA,gBAAA,OAAO,EAAE;YACX;AACF,QAAA,CAAC;QAED,SAAS,CAAC,MAAK;YACb,IAAI,aAAa,EAAE;gBACjB,YAAY,CAAC,aAAa,CAAC;YAC7B;AACF,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;AACX,YAAA,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;AACvB,YAAA,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE;YAC3B,IAAI;YACJ,KAAK;SACN;AACH,IAAA,CAAC,CAAC;AACJ;;AChKA;;AAEG;;;;"}
@@ -9,6 +9,19 @@ import { setupSync, listener } from '@signality/core/browser/listener';
9
9
  * @param options - Optional configuration including injector
10
10
  * @returns A DevicePostureRef with type signal
11
11
  *
12
+ * @example
13
+ * ```typescript
14
+ * @Component({
15
+ * template: `
16
+ * @if (posture.isSupported()) {
17
+ * <p>Device posture: {{ posture.type() }}</p>
18
+ * }
19
+ * `
20
+ * })
21
+ * export class PostureDemo {
22
+ * readonly posture = devicePosture();
23
+ * }
24
+ * ```
12
25
  */
13
26
  function devicePosture(options) {
14
27
  const { runInContext } = setupContext(options?.injector, devicePosture);