@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-elements-element-focus.mjs","sources":["../../../projects/core/elements/element-focus/index.ts","../../../projects/core/elements/element-focus/signality-core-elements-element-focus.ts"],"sourcesContent":["import { type CreateSignalOptions, signal, type Signal } from '@angular/core';\nimport { constSignal, setupContext } 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 interface ElementFocusOptions extends CreateSignalOptions<boolean>, WithInjector {\n /**\n * Track focus using the `:focus-visible` pseudo-class.\n * The browser uses heuristics to determine when focus should be visually indicated\n * (e.g., keyboard navigation, programmatic focus, or when the element requires user attention).\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:focus-visible MDN: :focus-visible}\n * @default false\n */\n readonly focusVisible?: boolean;\n}\n\n/**\n * Reactive tracking of focus state on an element.\n * Detects when an element gains or loses focus.\n *\n * @param target - The element to track focus state on\n * @param options - Optional configuration including focusVisible mode and injector\n * @returns A signal that is `true` when the element has focus\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <input #input [class.focused]=\"isFocused()\" />\n * @if (isFocused()) {\n * <p>Input is focused</p>\n * }\n * `\n * })\n * class FocusComponent {\n * readonly input = viewChild<ElementRef>('input');\n * readonly isFocused = elementFocus(this.input);\n * }\n * ```\n */\nexport function elementFocus(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementFocusOptions\n): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, elementFocus);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(false);\n }\n\n const focusVisible = options?.focusVisible ?? false;\n const focused = signal<boolean>(false, options);\n\n listener(target, 'focus', e => {\n focused.set(focusVisible ? (e.target as HTMLElement).matches(':focus-visible') : true);\n });\n\n listener(target, 'blur', () => {\n focused.set(false);\n });\n\n onDisconnect(target, () => focused.set(false));\n\n return focused.asReadonly();\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAkBA;;;;;;;;;;;;;;;;;;;;;;;AAuBG;AACG,SAAU,YAAY,CAC1B,MAAuC,EACvC,OAA6B,EAAA;AAE7B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,CAAC;AAEtE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,KAAK,CAAC;QAC3B;AAEA,QAAA,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,KAAK;QACnD,MAAM,OAAO,GAAG,MAAM,CAAU,KAAK,EAAE,OAAO,CAAC;AAE/C,QAAA,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,IAAG;YAC5B,OAAO,CAAC,GAAG,CAAC,YAAY,GAAI,CAAC,CAAC,MAAsB,CAAC,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;AACxF,QAAA,CAAC,CAAC;AAEF,QAAA,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAK;AAC5B,YAAA,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACpB,QAAA,CAAC,CAAC;AAEF,QAAA,YAAY,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAE9C,QAAA,OAAO,OAAO,CAAC,UAAU,EAAE;AAC7B,IAAA,CAAC,CAAC;AACJ;;ACpEA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-elements-element-focus.mjs","sources":["../../../projects/core/elements/element-focus/index.ts","../../../projects/core/elements/element-focus/signality-core-elements-element-focus.ts"],"sourcesContent":["import { type CreateSignalOptions, signal, type Signal } from '@angular/core';\nimport { constSignal, setupContext } 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 interface ElementFocusOptions extends CreateSignalOptions<boolean>, WithInjector {\n /**\n * Track focus using the `:focus-visible` pseudo-class.\n * The browser uses heuristics to determine when focus should be visually indicated\n * (e.g., keyboard navigation, programmatic focus, or when the element requires user attention).\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:focus-visible MDN: :focus-visible}\n * @default false\n */\n readonly focusVisible?: boolean;\n}\n\n/**\n * Reactive tracking of focus state on an element.\n * Detects when an element gains or loses focus.\n *\n * @param target - The element to track focus state on\n * @param options - Optional configuration including focusVisible mode and injector\n * @returns A signal that is `true` when the element has focus\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <input #input [class.focused]=\"isFocused()\" />\n * @if (isFocused()) {\n * <p>Input is focused</p>\n * }\n * `\n * })\n * export class FocusDemo {\n * readonly input = viewChild<ElementRef>('input');\n * readonly isFocused = elementFocus(this.input);\n * }\n * ```\n */\nexport function elementFocus(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementFocusOptions\n): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, elementFocus);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(false);\n }\n\n const focusVisible = options?.focusVisible ?? false;\n const focused = signal<boolean>(false, options);\n\n listener(target, 'focus', e => {\n focused.set(focusVisible ? (e.target as HTMLElement).matches(':focus-visible') : true);\n });\n\n listener(target, 'blur', () => {\n focused.set(false);\n });\n\n onDisconnect(target, () => focused.set(false));\n\n return focused.asReadonly();\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAkBA;;;;;;;;;;;;;;;;;;;;;;;AAuBG;AACG,SAAU,YAAY,CAC1B,MAAuC,EACvC,OAA6B,EAAA;AAE7B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,CAAC;AAEtE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,KAAK,CAAC;QAC3B;AAEA,QAAA,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,KAAK;QACnD,MAAM,OAAO,GAAG,MAAM,CAAU,KAAK,EAAE,OAAO,CAAC;AAE/C,QAAA,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,IAAG;YAC5B,OAAO,CAAC,GAAG,CAAC,YAAY,GAAI,CAAC,CAAC,MAAsB,CAAC,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;AACxF,QAAA,CAAC,CAAC;AAEF,QAAA,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAK;AAC5B,YAAA,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACpB,QAAA,CAAC,CAAC;AAEF,QAAA,YAAY,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAE9C,QAAA,OAAO,OAAO,CAAC,UAAU,EAAE;AAC7B,IAAA,CAAC,CAAC;AACJ;;ACpEA;;AAEG;;;;"}
@@ -20,7 +20,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
20
20
  * </div>
21
21
  * `
22
22
  * })
23
- * class HoverComponent {
23
+ * export class HoverDemo {
24
24
  * readonly box = viewChild<ElementRef>('box');
25
25
  * readonly isHovered = elementHover(this.box);
26
26
  * }
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-element-hover.mjs","sources":["../../../projects/core/elements/element-hover/index.ts","../../../projects/core/elements/element-hover/signality-core-elements-element-hover.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } 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 ElementHoverOptions = CreateSignalOptions<boolean> & WithInjector;\n\n/**\n * Reactive tracking of the hover state on an element.\n * Detects when the mouse enters or leaves an element.\n *\n * @param target - The element to track hover state on\n * @param options - Optional configuration including signal options and injector\n * @returns A signal that is `true` when the element is being hovered\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #box [class.hovered]=\"isHovered()\">\n * Hover over me!\n * </div>\n * `\n * })\n * class HoverComponent {\n * readonly box = viewChild<ElementRef>('box');\n * readonly isHovered = elementHover(this.box);\n * }\n * ```\n */\nexport function elementHover(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementHoverOptions\n): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, elementHover);\n\n return runInContext(({ isServer, isMobile }) => {\n if (isServer || isMobile) {\n return constSignal(false);\n }\n\n const hovered = signal<boolean>(false, options);\n\n listener(target, 'mouseenter', () => hovered.set(true));\n listener(target, 'mouseleave', () => hovered.set(false));\n\n onDisconnect(target, () => hovered.set(false));\n\n return hovered.asReadonly();\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAQA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG,SAAU,YAAY,CAC1B,MAAuC,EACvC,OAA6B,EAAA;AAE7B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,CAAC;IAEtE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAI;AAC7C,QAAA,IAAI,QAAQ,IAAI,QAAQ,EAAE;AACxB,YAAA,OAAO,WAAW,CAAC,KAAK,CAAC;QAC3B;QAEA,MAAM,OAAO,GAAG,MAAM,CAAU,KAAK,EAAE,OAAO,CAAC;AAE/C,QAAA,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACvD,QAAA,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAExD,QAAA,YAAY,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAE9C,QAAA,OAAO,OAAO,CAAC,UAAU,EAAE;AAC7B,IAAA,CAAC,CAAC;AACJ;;ACnDA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-elements-element-hover.mjs","sources":["../../../projects/core/elements/element-hover/index.ts","../../../projects/core/elements/element-hover/signality-core-elements-element-hover.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } 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 ElementHoverOptions = CreateSignalOptions<boolean> & WithInjector;\n\n/**\n * Reactive tracking of the hover state on an element.\n * Detects when the mouse enters or leaves an element.\n *\n * @param target - The element to track hover state on\n * @param options - Optional configuration including signal options and injector\n * @returns A signal that is `true` when the element is being hovered\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #box [class.hovered]=\"isHovered()\">\n * Hover over me!\n * </div>\n * `\n * })\n * export class HoverDemo {\n * readonly box = viewChild<ElementRef>('box');\n * readonly isHovered = elementHover(this.box);\n * }\n * ```\n */\nexport function elementHover(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementHoverOptions\n): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, elementHover);\n\n return runInContext(({ isServer, isMobile }) => {\n if (isServer || isMobile) {\n return constSignal(false);\n }\n\n const hovered = signal<boolean>(false, options);\n\n listener(target, 'mouseenter', () => hovered.set(true));\n listener(target, 'mouseleave', () => hovered.set(false));\n\n onDisconnect(target, () => hovered.set(false));\n\n return hovered.asReadonly();\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAQA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG,SAAU,YAAY,CAC1B,MAAuC,EACvC,OAA6B,EAAA;AAE7B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,CAAC;IAEtE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAI;AAC7C,QAAA,IAAI,QAAQ,IAAI,QAAQ,EAAE;AACxB,YAAA,OAAO,WAAW,CAAC,KAAK,CAAC;QAC3B;QAEA,MAAM,OAAO,GAAG,MAAM,CAAU,KAAK,EAAE,OAAO,CAAC;AAE/C,QAAA,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACvD,QAAA,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAExD,QAAA,YAAY,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAE9C,QAAA,OAAO,OAAO,CAAC,UAAU,EAAE;AAC7B,IAAA,CAAC,CAAC;AACJ;;ACnDA;;AAEG;;;;"}
@@ -19,7 +19,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
19
19
  * </div>
20
20
  * `
21
21
  * })
22
- * class SizeComponent {
22
+ * export class ElementSizeDemo {
23
23
  * readonly box = viewChild<ElementRef>('box');
24
24
  * readonly size = elementSize(this.box);
25
25
  * }
@@ -27,42 +27,37 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
27
27
  */
28
28
  function elementSize(target, options) {
29
29
  const { runInContext } = setupContext(options?.injector, elementSize);
30
+ const initialValue = options?.initialValue ?? DEFAULT_SIZE;
30
31
  return runInContext(({ isServer }) => {
31
32
  if (isServer) {
32
- return constSignal(DEFAULT_SIZE);
33
+ return constSignal(initialValue);
33
34
  }
34
- const size = signal(DEFAULT_SIZE, options);
35
+ const size = signal(initialValue, options);
35
36
  const updateSize = ([entry]) => {
36
- const contentBoxSize = entry.contentBoxSize?.[0];
37
- const borderBoxSize = entry.borderBoxSize?.[0];
38
- const contentWidth = contentBoxSize?.inlineSize ?? entry.contentRect.width;
39
- const contentHeight = contentBoxSize?.blockSize ?? entry.contentRect.height;
40
- const borderBoxWidth = borderBoxSize?.inlineSize ?? entry.contentRect.width;
41
- const borderBoxHeight = borderBoxSize?.blockSize ?? entry.contentRect.height;
42
37
  const box = toValue(options?.box) ?? 'border-box';
43
- const width = box === 'content-box' ? contentWidth : borderBoxWidth;
44
- const height = box === 'content-box' ? contentHeight : borderBoxHeight;
45
- size.set({
46
- width,
47
- height,
48
- contentWidth,
49
- contentHeight,
50
- borderBoxWidth,
51
- borderBoxHeight,
52
- });
38
+ if (box === 'content-box') {
39
+ const contentBoxSize = entry.contentBoxSize?.[0];
40
+ size.set({
41
+ width: contentBoxSize?.inlineSize ?? entry.contentRect.width,
42
+ height: contentBoxSize?.blockSize ?? entry.contentRect.height,
43
+ });
44
+ }
45
+ else {
46
+ const borderBoxSize = entry.borderBoxSize?.[0];
47
+ size.set({
48
+ width: borderBoxSize?.inlineSize ?? entry.contentRect.width,
49
+ height: borderBoxSize?.blockSize ?? entry.contentRect.height,
50
+ });
51
+ }
53
52
  };
54
53
  resizeObserver(target, updateSize, options);
55
- onDisconnect(target, () => size.set(DEFAULT_SIZE));
54
+ onDisconnect(target, () => size.set(initialValue));
56
55
  return size;
57
56
  });
58
57
  }
59
58
  const DEFAULT_SIZE = {
60
59
  width: 0,
61
60
  height: 0,
62
- contentWidth: 0,
63
- contentHeight: 0,
64
- borderBoxWidth: 0,
65
- borderBoxHeight: 0,
66
61
  };
67
62
 
68
63
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-element-size.mjs","sources":["../../../projects/core/elements/element-size/index.ts","../../../projects/core/elements/element-size/signality-core-elements-element-size.ts"],"sourcesContent":["import { type CreateSignalOptions, signal, type Signal } from '@angular/core';\nimport { constSignal, setupContext, toValue } from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\nimport { resizeObserver } from '@signality/core/observers/resize-observer';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport interface ElementSizeValue {\n readonly width: number;\n readonly height: number;\n readonly contentWidth: number;\n readonly contentHeight: number;\n readonly borderBoxWidth: number;\n readonly borderBoxHeight: number;\n}\n\nexport interface ElementSizeOptions extends CreateSignalOptions<ElementSizeValue>, WithInjector {\n /**\n * Which box model to observe. Can be a reactive signal.\n * @default 'border-box'\n */\n readonly box?: MaybeSignal<ResizeObserverBoxOptions>;\n}\n\n/**\n * Signal-based wrapper around the [ResizeObserver API](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).\n *\n * @param target - The element to observe\n * @param options - Optional configuration including signal options (equal, debugName), box model, and injector\n * @returns A signal containing the current element dimensions\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #box>\n * Size: {{ size().width }} × {{ size().height }}px\n * </div>\n * `\n * })\n * class SizeComponent {\n * readonly box = viewChild<ElementRef>('box');\n * readonly size = elementSize(this.box);\n * }\n * ```\n */\nexport function elementSize(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementSizeOptions\n): Signal<ElementSizeValue> {\n const { runInContext } = setupContext(options?.injector, elementSize);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(DEFAULT_SIZE);\n }\n\n const size = signal<ElementSizeValue>(DEFAULT_SIZE, options);\n\n const updateSize = ([entry]: readonly ResizeObserverEntry[]) => {\n const contentBoxSize = entry.contentBoxSize?.[0];\n const borderBoxSize = entry.borderBoxSize?.[0];\n\n const contentWidth = contentBoxSize?.inlineSize ?? entry.contentRect.width;\n const contentHeight = contentBoxSize?.blockSize ?? entry.contentRect.height;\n const borderBoxWidth = borderBoxSize?.inlineSize ?? entry.contentRect.width;\n const borderBoxHeight = borderBoxSize?.blockSize ?? entry.contentRect.height;\n\n const box = toValue(options?.box) ?? 'border-box';\n const width = box === 'content-box' ? contentWidth : borderBoxWidth;\n const height = box === 'content-box' ? contentHeight : borderBoxHeight;\n\n size.set({\n width,\n height,\n contentWidth,\n contentHeight,\n borderBoxWidth,\n borderBoxHeight,\n });\n };\n\n resizeObserver(target, updateSize, options);\n\n onDisconnect(target, () => size.set(DEFAULT_SIZE));\n\n return size;\n });\n}\n\nconst DEFAULT_SIZE: ElementSizeValue = {\n width: 0,\n height: 0,\n contentWidth: 0,\n contentHeight: 0,\n borderBoxWidth: 0,\n borderBoxHeight: 0,\n};\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAuBA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,WAAW,CACzB,MAAuC,EACvC,OAA4B,EAAA;AAE5B,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,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,YAAY,CAAC;QAClC;QAEA,MAAM,IAAI,GAAG,MAAM,CAAmB,YAAY,EAAE,OAAO,CAAC;AAE5D,QAAA,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAiC,KAAI;YAC7D,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC;YAChD,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC;YAE9C,MAAM,YAAY,GAAG,cAAc,EAAE,UAAU,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK;YAC1E,MAAM,aAAa,GAAG,cAAc,EAAE,SAAS,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM;YAC3E,MAAM,cAAc,GAAG,aAAa,EAAE,UAAU,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK;YAC3E,MAAM,eAAe,GAAG,aAAa,EAAE,SAAS,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM;YAE5E,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,YAAY;AACjD,YAAA,MAAM,KAAK,GAAG,GAAG,KAAK,aAAa,GAAG,YAAY,GAAG,cAAc;AACnE,YAAA,MAAM,MAAM,GAAG,GAAG,KAAK,aAAa,GAAG,aAAa,GAAG,eAAe;YAEtE,IAAI,CAAC,GAAG,CAAC;gBACP,KAAK;gBACL,MAAM;gBACN,YAAY;gBACZ,aAAa;gBACb,cAAc;gBACd,eAAe;AAChB,aAAA,CAAC;AACJ,QAAA,CAAC;AAED,QAAA,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC;AAE3C,QAAA,YAAY,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAElD,QAAA,OAAO,IAAI;AACb,IAAA,CAAC,CAAC;AACJ;AAEA,MAAM,YAAY,GAAqB;AACrC,IAAA,KAAK,EAAE,CAAC;AACR,IAAA,MAAM,EAAE,CAAC;AACT,IAAA,YAAY,EAAE,CAAC;AACf,IAAA,aAAa,EAAE,CAAC;AAChB,IAAA,cAAc,EAAE,CAAC;AACjB,IAAA,eAAe,EAAE,CAAC;CACnB;;AChGD;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-elements-element-size.mjs","sources":["../../../projects/core/elements/element-size/index.ts","../../../projects/core/elements/element-size/signality-core-elements-element-size.ts"],"sourcesContent":["import { type CreateSignalOptions, signal, type Signal } from '@angular/core';\nimport { constSignal, setupContext, toValue } from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\nimport { resizeObserver } from '@signality/core/observers/resize-observer';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport interface ElementSizeValue {\n readonly width: number;\n readonly height: number;\n}\n\nexport interface ElementSizeOptions extends CreateSignalOptions<ElementSizeValue>, WithInjector {\n /**\n * Which box model to observe. Can be a reactive signal.\n *\n * @default 'border-box'\n */\n readonly box?: MaybeSignal<ResizeObserverBoxOptions>;\n\n /**\n * Initial value for SSR and before the first measurement.\n *\n * @default { width: 0, height: 0 }\n */\n readonly initialValue?: ElementSizeValue;\n}\n\n/**\n * Signal-based wrapper around the [ResizeObserver API](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).\n *\n * @param target - The element to observe\n * @param options - Optional configuration including signal options (equal, debugName), box model, and injector\n * @returns A signal containing the current element dimensions\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #box>\n * Size: {{ size().width }} × {{ size().height }}px\n * </div>\n * `\n * })\n * export class ElementSizeDemo {\n * readonly box = viewChild<ElementRef>('box');\n * readonly size = elementSize(this.box);\n * }\n * ```\n */\nexport function elementSize(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementSizeOptions\n): Signal<ElementSizeValue> {\n const { runInContext } = setupContext(options?.injector, elementSize);\n const initialValue = options?.initialValue ?? DEFAULT_SIZE;\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(initialValue);\n }\n\n const size = signal<ElementSizeValue>(initialValue, options);\n\n const updateSize = ([entry]: readonly ResizeObserverEntry[]) => {\n const box = toValue(options?.box) ?? 'border-box';\n\n if (box === 'content-box') {\n const contentBoxSize = entry.contentBoxSize?.[0];\n size.set({\n width: contentBoxSize?.inlineSize ?? entry.contentRect.width,\n height: contentBoxSize?.blockSize ?? entry.contentRect.height,\n });\n } else {\n const borderBoxSize = entry.borderBoxSize?.[0];\n size.set({\n width: borderBoxSize?.inlineSize ?? entry.contentRect.width,\n height: borderBoxSize?.blockSize ?? entry.contentRect.height,\n });\n }\n };\n\n resizeObserver(target, updateSize, options);\n\n onDisconnect(target, () => size.set(initialValue));\n\n return size;\n });\n}\n\nconst DEFAULT_SIZE: ElementSizeValue = {\n width: 0,\n height: 0,\n};\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AA2BA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,WAAW,CACzB,MAAuC,EACvC,OAA4B,EAAA;AAE5B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC;AACrE,IAAA,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,YAAY;AAE1D,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,YAAY,CAAC;QAClC;QAEA,MAAM,IAAI,GAAG,MAAM,CAAmB,YAAY,EAAE,OAAO,CAAC;AAE5D,QAAA,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAiC,KAAI;YAC7D,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,YAAY;AAEjD,YAAA,IAAI,GAAG,KAAK,aAAa,EAAE;gBACzB,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC;gBAChD,IAAI,CAAC,GAAG,CAAC;oBACP,KAAK,EAAE,cAAc,EAAE,UAAU,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK;oBAC5D,MAAM,EAAE,cAAc,EAAE,SAAS,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM;AAC9D,iBAAA,CAAC;YACJ;iBAAO;gBACL,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC;gBAC9C,IAAI,CAAC,GAAG,CAAC;oBACP,KAAK,EAAE,aAAa,EAAE,UAAU,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK;oBAC3D,MAAM,EAAE,aAAa,EAAE,SAAS,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM;AAC7D,iBAAA,CAAC;YACJ;AACF,QAAA,CAAC;AAED,QAAA,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC;AAE3C,QAAA,YAAY,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAElD,QAAA,OAAO,IAAI;AACb,IAAA,CAAC,CAAC;AACJ;AAEA,MAAM,YAAY,GAAqB;AACrC,IAAA,KAAK,EAAE,CAAC;AACR,IAAA,MAAM,EAAE,CAAC;CACV;;AC5FD;;AAEG;;;;"}
@@ -7,7 +7,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
7
7
  * Signal-based wrapper around the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
8
8
  *
9
9
  * @param target - The element to observe
10
- * @param options - Optional configuration including signal options (equal, debugName), observer options (threshold, root, rootMargin, initialValue), and injector
10
+ * @param options - Optional configuration
11
11
  * @returns A signal containing the current visibility state
12
12
  *
13
13
  * @example
@@ -19,7 +19,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
19
19
  * </div>
20
20
  * `
21
21
  * })
22
- * class VisibilityComponent {
22
+ * export class VisibilityDemo {
23
23
  * readonly section = viewChild<ElementRef>('section');
24
24
  * readonly visibility = elementVisibility(this.section);
25
25
  * }
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-element-visibility.mjs","sources":["../../../projects/core/elements/element-visibility/index.ts","../../../projects/core/elements/element-visibility/signality-core-elements-element-visibility.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\nimport { intersectionObserver } from '@signality/core/observers/intersection-observer';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport interface ElementVisibilityOptions\n extends CreateSignalOptions<ElementVisibilityValue>,\n WithInjector {\n /**\n * Visibility threshold(s). A number between 0 and 1, or an array of thresholds.\n * @default 0\n */\n readonly threshold?: MaybeSignal<number | number[]>;\n\n /**\n * Scrollable ancestor element (null = viewport).\n * @default undefined\n */\n readonly root?: MaybeElementSignal<Element> | Document;\n\n /**\n * Margin around the root element.\n * @default '0px'\n */\n readonly rootMargin?: MaybeSignal<string>;\n\n /**\n * Initial value for SSR.\n * @default { isVisible: true, ratio: 1 }\n */\n readonly initialValue?: ElementVisibilityValue;\n}\n\nexport interface ElementVisibilityValue {\n /** Whether the element is visible in the viewport */\n readonly isVisible: boolean;\n\n /** Intersection ratio (0.0 to 1.0) */\n readonly ratio: number;\n}\n\n/**\n * Signal-based wrapper around the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).\n *\n * @param target - The element to observe\n * @param options - Optional configuration including signal options (equal, debugName), observer options (threshold, root, rootMargin, initialValue), and injector\n * @returns A signal containing the current visibility state\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #section [class.visible]=\"visibility().isVisible\">\n * Visibility: {{ visibility().ratio * 100 }}%\n * </div>\n * `\n * })\n * class VisibilityComponent {\n * readonly section = viewChild<ElementRef>('section');\n * readonly visibility = elementVisibility(this.section);\n * }\n * ```\n */\nexport function elementVisibility(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementVisibilityOptions\n): Signal<ElementVisibilityValue> {\n const { runInContext } = setupContext(options?.injector, elementVisibility);\n const initialValue = options?.initialValue ?? DEFAULT_VISIBILITY;\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(initialValue);\n }\n\n const visibility = signal(initialValue, options);\n\n const threshold = options?.threshold ?? 0;\n const root = options?.root ?? undefined;\n const rootMargin = options?.rootMargin ?? '0px';\n\n const update = (entries: readonly IntersectionObserverEntry[]) => {\n if (entries.length === 0) {\n return;\n }\n\n // Find the entry with the latest time to ensure we use the most up-to-date state\n // IntersectionObserver may batch multiple changes and call the callback once\n // with multiple entries, and the order in the array doesn't guarantee\n // that the last entry is the most recent one\n let latestEntry = entries[0];\n let latestTime = entries[0].time;\n\n for (let i = 1; i < entries.length; i++) {\n const entry = entries[i];\n if (entry.time >= latestTime) {\n latestTime = entry.time;\n latestEntry = entry;\n }\n }\n\n visibility.set({\n isVisible: latestEntry.isIntersecting,\n ratio: latestEntry.intersectionRatio,\n });\n };\n\n intersectionObserver(target, update, { threshold, root, rootMargin });\n\n onDisconnect(target, () => visibility.set(initialValue));\n\n return visibility;\n });\n}\n\nconst DEFAULT_VISIBILITY: ElementVisibilityValue = {\n isVisible: true,\n ratio: 1,\n};\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AA0CA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,iBAAiB,CAC/B,MAAuC,EACvC,OAAkC,EAAA;AAElC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,CAAC;AAC3E,IAAA,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,kBAAkB;AAEhE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,YAAY,CAAC;QAClC;QAEA,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC;AAEhD,QAAA,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,CAAC;AACzC,QAAA,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,SAAS;AACvC,QAAA,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,KAAK;AAE/C,QAAA,MAAM,MAAM,GAAG,CAAC,OAA6C,KAAI;AAC/D,YAAA,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;gBACxB;YACF;;;;;AAMA,YAAA,IAAI,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;YAC5B,IAAI,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI;AAEhC,YAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACvC,gBAAA,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC;AACxB,gBAAA,IAAI,KAAK,CAAC,IAAI,IAAI,UAAU,EAAE;AAC5B,oBAAA,UAAU,GAAG,KAAK,CAAC,IAAI;oBACvB,WAAW,GAAG,KAAK;gBACrB;YACF;YAEA,UAAU,CAAC,GAAG,CAAC;gBACb,SAAS,EAAE,WAAW,CAAC,cAAc;gBACrC,KAAK,EAAE,WAAW,CAAC,iBAAiB;AACrC,aAAA,CAAC;AACJ,QAAA,CAAC;AAED,QAAA,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAErE,QAAA,YAAY,CAAC,MAAM,EAAE,MAAM,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAExD,QAAA,OAAO,UAAU;AACnB,IAAA,CAAC,CAAC;AACJ;AAEA,MAAM,kBAAkB,GAA2B;AACjD,IAAA,SAAS,EAAE,IAAI;AACf,IAAA,KAAK,EAAE,CAAC;CACT;;ACvHD;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-elements-element-visibility.mjs","sources":["../../../projects/core/elements/element-visibility/index.ts","../../../projects/core/elements/element-visibility/signality-core-elements-element-visibility.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\nimport { intersectionObserver } from '@signality/core/observers/intersection-observer';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport interface ElementVisibilityOptions\n extends CreateSignalOptions<ElementVisibilityValue>,\n WithInjector {\n /**\n * Fraction of the element that must be visible to trigger a change.\n * A single number or an array of thresholds, each between `0` and `1`.\n *\n * @default 0\n * @see [IntersectionObserver: thresholds on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/thresholds)\n */\n readonly threshold?: MaybeSignal<number | number[]>;\n\n /**\n * Scrollable ancestor used as the viewport for intersection checks.\n * `null` or `undefined` defaults to the browser viewport.\n *\n * @default undefined\n * @see [IntersectionObserver: root on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/root)\n */\n readonly root?: MaybeElementSignal<Element> | Document;\n\n /**\n * CSS margin applied around the root before computing intersections.\n * Accepts values in the same format as the CSS `margin` property (e.g. `'10px 0px'`).\n *\n * @default '0px'\n * @see [IntersectionObserver: rootMargin on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin)\n */\n readonly rootMargin?: MaybeSignal<string>;\n\n /**\n * Initial value for SSR.\n *\n * @default { isVisible: true, ratio: 1 }\n */\n readonly initialValue?: ElementVisibilityValue;\n}\n\nexport interface ElementVisibilityValue {\n /**\n * Whether the element is currently intersecting the root (visible in the viewport).\n *\n * @see [IntersectionObserverEntry: isIntersecting on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry/isIntersecting)\n */\n readonly isVisible: boolean;\n\n /**\n * Fraction of the element visible within the root, from `0.0` (not visible) to `1.0` (fully visible).\n *\n * @see [IntersectionObserverEntry: intersectionRatio on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry/intersectionRatio)\n */\n readonly ratio: number;\n}\n\n/**\n * Signal-based wrapper around the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).\n *\n * @param target - The element to observe\n * @param options - Optional configuration\n * @returns A signal containing the current visibility state\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #section [class.visible]=\"visibility().isVisible\">\n * Visibility: {{ visibility().ratio * 100 }}%\n * </div>\n * `\n * })\n * export class VisibilityDemo {\n * readonly section = viewChild<ElementRef>('section');\n * readonly visibility = elementVisibility(this.section);\n * }\n * ```\n */\nexport function elementVisibility(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementVisibilityOptions\n): Signal<ElementVisibilityValue> {\n const { runInContext } = setupContext(options?.injector, elementVisibility);\n const initialValue = options?.initialValue ?? DEFAULT_VISIBILITY;\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(initialValue);\n }\n\n const visibility = signal(initialValue, options);\n\n const threshold = options?.threshold ?? 0;\n const root = options?.root ?? undefined;\n const rootMargin = options?.rootMargin ?? '0px';\n\n const update = (entries: readonly IntersectionObserverEntry[]) => {\n if (entries.length === 0) {\n return;\n }\n\n // Find the entry with the latest time to ensure we use the most up-to-date state\n // IntersectionObserver may batch multiple changes and call the callback once\n // with multiple entries, and the order in the array doesn't guarantee\n // that the last entry is the most recent one\n let latestEntry = entries[0];\n let latestTime = entries[0].time;\n\n for (let i = 1; i < entries.length; i++) {\n const entry = entries[i];\n if (entry.time >= latestTime) {\n latestTime = entry.time;\n latestEntry = entry;\n }\n }\n\n visibility.set({\n isVisible: latestEntry.isIntersecting,\n ratio: latestEntry.intersectionRatio,\n });\n };\n\n intersectionObserver(target, update, { threshold, root, rootMargin });\n\n onDisconnect(target, () => visibility.set(initialValue));\n\n return visibility;\n });\n}\n\nconst DEFAULT_VISIBILITY: ElementVisibilityValue = {\n isVisible: true,\n ratio: 1,\n};\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AA4DA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,iBAAiB,CAC/B,MAAuC,EACvC,OAAkC,EAAA;AAElC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,CAAC;AAC3E,IAAA,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,kBAAkB;AAEhE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,YAAY,CAAC;QAClC;QAEA,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC;AAEhD,QAAA,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,CAAC;AACzC,QAAA,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,SAAS;AACvC,QAAA,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,KAAK;AAE/C,QAAA,MAAM,MAAM,GAAG,CAAC,OAA6C,KAAI;AAC/D,YAAA,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;gBACxB;YACF;;;;;AAMA,YAAA,IAAI,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;YAC5B,IAAI,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI;AAEhC,YAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACvC,gBAAA,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC;AACxB,gBAAA,IAAI,KAAK,CAAC,IAAI,IAAI,UAAU,EAAE;AAC5B,oBAAA,UAAU,GAAG,KAAK,CAAC,IAAI;oBACvB,WAAW,GAAG,KAAK;gBACrB;YACF;YAEA,UAAU,CAAC,GAAG,CAAC;gBACb,SAAS,EAAE,WAAW,CAAC,cAAc;gBACrC,KAAK,EAAE,WAAW,CAAC,iBAAiB;AACrC,aAAA,CAAC;AACJ,QAAA,CAAC;AAED,QAAA,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAErE,QAAA,YAAY,CAAC,MAAM,EAAE,MAAM,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAExD,QAAA,OAAO,UAAU;AACnB,IAAA,CAAC,CAAC;AACJ;AAEA,MAAM,kBAAkB,GAA2B;AACjD,IAAA,SAAS,EAAE,IAAI;AACf,IAAA,KAAK,EAAE,CAAC;CACT;;ACzID;;AAEG;;;;"}
@@ -4,7 +4,7 @@ import { listener } from '@signality/core/browser/listener';
4
4
  import { onDisconnect } from '@signality/core/elements/on-disconnect';
5
5
 
6
6
  /**
7
- * Reactive tracking of mouse position.
7
+ * Reactive tracking of mouse position using the [mousemove](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event) event.
8
8
  * Track cursor coordinates globally or relative to an element.
9
9
  *
10
10
  * @param options - Optional configuration
@@ -17,7 +17,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
17
17
  * <p>Mouse position: X={{ position().x }}, Y={{ position().y }}</p>
18
18
  * `
19
19
  * })
20
- * class MouseTracker {
20
+ * export class MouseTracker {
21
21
  * readonly position = mousePosition();
22
22
  * }
23
23
  * ```
@@ -32,7 +32,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
32
32
  * </div>
33
33
  * `
34
34
  * })
35
- * class MouseElementTracker {
35
+ * export class MouseElementTracker {
36
36
  * readonly box = viewChild<ElementRef>('box');
37
37
  * readonly position = mousePosition({ target: this.box });
38
38
  * }
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-mouse-position.mjs","sources":["../../../projects/core/elements/mouse-position/index.ts","../../../projects/core/elements/mouse-position/signality-core-elements-mouse-position.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport { constSignal, isWindow, setupContext } 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 interface MousePosition {\n readonly x: number;\n readonly y: number;\n}\n\nexport type MouseCoordinateType = 'page' | 'client' | 'screen';\n\nexport interface MousePositionOptions extends WithInjector {\n /**\n * Element or window to track mouse position on.\n * @default window\n */\n readonly target?: MaybeElementSignal<Element> | Window;\n\n /**\n * Coordinate type to use.\n * @default 'page'\n */\n readonly type?: MouseCoordinateType;\n\n /**\n * Whether to track touch events as well.\n * @default true\n */\n readonly touch?: boolean;\n\n /**\n * Initial mouse position.\n */\n readonly initialValue?: MousePosition;\n}\n\n/**\n * Reactive tracking of mouse position.\n * Track cursor coordinates globally or relative to an element.\n *\n * @param options - Optional configuration\n * @returns A signal containing the current mouse position\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Mouse position: X={{ position().x }}, Y={{ position().y }}</p>\n * `\n * })\n * class MouseTracker {\n * readonly position = mousePosition();\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Track mouse position on a specific element\n * @Component({\n * template: `\n * <div #box>\n * <p>Position: X={{ position().x }}, Y={{ position().y }}</p>\n * </div>\n * `\n * })\n * class MouseElementTracker {\n * readonly box = viewChild<ElementRef>('box');\n * readonly position = mousePosition({ target: this.box });\n * }\n * ```\n */\nexport function mousePosition(options?: MousePositionOptions): Signal<MousePosition> {\n const { runInContext } = setupContext(options?.injector, mousePosition);\n\n return runInContext(({ isServer }) => {\n const initialValue = options?.initialValue ?? DEFAULT_POSITION;\n\n if (isServer) {\n return constSignal(initialValue);\n }\n\n const target = options?.target ?? window;\n const targetIsWindow = isWindow(target);\n const coordinateType = options?.type ?? 'page';\n const trackTouch = options?.touch ?? true;\n const trackScroll = coordinateType === 'page';\n const extractor = EXTRACTORS[coordinateType];\n\n const position = signal<MousePosition>(initialValue);\n\n let prevMouseEvent: MouseEvent | null = null;\n let prevScrollX = 0;\n let prevScrollY = 0;\n\n const handleMouse = (e: MouseEvent): void => {\n prevMouseEvent = e;\n\n position.set(extractor(e));\n\n if (trackScroll) {\n prevScrollX = window.scrollX;\n prevScrollY = window.scrollY;\n }\n };\n\n listener(target, 'mousemove', handleMouse);\n listener(target, 'dragover', handleMouse);\n\n if (trackTouch) {\n listener.passive(target, 'touchmove', (e: TouchEvent) => {\n if (e.touches.length === 0) {\n return;\n }\n\n position.set(extractor(e.touches[0]));\n });\n }\n\n if (trackScroll) {\n listener(window, 'scroll', () => {\n if (!prevMouseEvent) {\n return;\n }\n\n const pos = extractor(prevMouseEvent);\n\n position.set({\n x: pos.x + window.scrollX - prevScrollX,\n y: pos.y + window.scrollY - prevScrollY,\n });\n });\n }\n\n if (!targetIsWindow) {\n onDisconnect(target, () => position.set(DEFAULT_POSITION));\n }\n\n return position.asReadonly();\n });\n}\n\nconst DEFAULT_POSITION: MousePosition = {\n x: 0,\n y: 0,\n};\n\nconst EXTRACTORS: Record<MouseCoordinateType, (e: MouseEvent | Touch) => MousePosition> = {\n page: e => ({ x: e.pageX, y: e.pageY }),\n client: e => ({ x: e.clientX, y: e.clientY }),\n screen: e => ({ x: e.screenX, y: e.screenY }),\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-elements-mouse-position.mjs","sources":["../../../projects/core/elements/mouse-position/index.ts","../../../projects/core/elements/mouse-position/signality-core-elements-mouse-position.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport { constSignal, isWindow, setupContext } 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 interface MousePosition {\n /** Horizontal coordinate in pixels. */\n readonly x: number;\n\n /** Vertical coordinate in pixels. */\n readonly y: number;\n}\n\n/**\n * Coordinate space used to report mouse position.\n *\n * - `'page'` — relative to the document origin, includes scroll offset.\n * - `'client'` — relative to the viewport, unaffected by scroll.\n * - `'screen'` — relative to the physical screen.\n *\n * @see [MouseEvent.pageX on MDN](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/pageX)\n * @see [MouseEvent.clientX on MDN](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX)\n * @see [MouseEvent.screenX on MDN](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenX)\n */\nexport type MouseCoordinateType = 'page' | 'client' | 'screen';\n\nexport interface MousePositionOptions extends WithInjector {\n /**\n * Element or `window` to track mouse position on.\n * When an element signal is provided, tracking stops on element disconnect.\n *\n * @default window\n */\n readonly target?: MaybeElementSignal<Element> | Window;\n\n /**\n * Coordinate space for the reported position. See {@link MouseCoordinateType}.\n *\n * @default 'page'\n */\n readonly type?: MouseCoordinateType;\n\n /**\n * Whether to also track touch events (`touchmove`).\n *\n * @default true\n * @see [TouchEvent on MDN](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent)\n */\n readonly touch?: boolean;\n\n /**\n * Initial value for SSR.\n *\n * @default { x: 0, y: 0 }\n */\n readonly initialValue?: MousePosition;\n}\n\n/**\n * Reactive tracking of mouse position using the [mousemove](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event) event.\n * Track cursor coordinates globally or relative to an element.\n *\n * @param options - Optional configuration\n * @returns A signal containing the current mouse position\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Mouse position: X={{ position().x }}, Y={{ position().y }}</p>\n * `\n * })\n * export class MouseTracker {\n * readonly position = mousePosition();\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Track mouse position on a specific element\n * @Component({\n * template: `\n * <div #box>\n * <p>Position: X={{ position().x }}, Y={{ position().y }}</p>\n * </div>\n * `\n * })\n * export class MouseElementTracker {\n * readonly box = viewChild<ElementRef>('box');\n * readonly position = mousePosition({ target: this.box });\n * }\n * ```\n */\nexport function mousePosition(options?: MousePositionOptions): Signal<MousePosition> {\n const { runInContext } = setupContext(options?.injector, mousePosition);\n\n return runInContext(({ isServer }) => {\n const initialValue = options?.initialValue ?? DEFAULT_POSITION;\n\n if (isServer) {\n return constSignal(initialValue);\n }\n\n const target = options?.target ?? window;\n const targetIsWindow = isWindow(target);\n const coordinateType = options?.type ?? 'page';\n const trackTouch = options?.touch ?? true;\n const trackScroll = coordinateType === 'page';\n const extractor = EXTRACTORS[coordinateType];\n\n const position = signal<MousePosition>(initialValue);\n\n let prevMouseEvent: MouseEvent | null = null;\n let prevScrollX = 0;\n let prevScrollY = 0;\n\n const handleMouse = (e: MouseEvent): void => {\n prevMouseEvent = e;\n\n position.set(extractor(e));\n\n if (trackScroll) {\n prevScrollX = window.scrollX;\n prevScrollY = window.scrollY;\n }\n };\n\n listener(target, 'mousemove', handleMouse);\n listener(target, 'dragover', handleMouse);\n\n if (trackTouch) {\n listener.passive(target, 'touchmove', (e: TouchEvent) => {\n if (e.touches.length === 0) {\n return;\n }\n\n position.set(extractor(e.touches[0]));\n });\n }\n\n if (trackScroll) {\n listener(window, 'scroll', () => {\n if (!prevMouseEvent) {\n return;\n }\n\n const pos = extractor(prevMouseEvent);\n\n position.set({\n x: pos.x + window.scrollX - prevScrollX,\n y: pos.y + window.scrollY - prevScrollY,\n });\n });\n }\n\n if (!targetIsWindow) {\n onDisconnect(target, () => position.set(DEFAULT_POSITION));\n }\n\n return position.asReadonly();\n });\n}\n\nconst DEFAULT_POSITION: MousePosition = {\n x: 0,\n y: 0,\n};\n\nconst EXTRACTORS: Record<MouseCoordinateType, (e: MouseEvent | Touch) => MousePosition> = {\n page: e => ({ x: e.pageX, y: e.pageY }),\n client: e => ({ x: e.clientX, y: e.clientY }),\n screen: e => ({ x: e.screenX, y: e.screenY }),\n};\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -19,7 +19,7 @@ import { setupSync, listener } from '@signality/core/browser/listener';
19
19
  * </div>
20
20
  * `
21
21
  * })
22
- * class DropdownComponent {
22
+ * export class Dropdown {
23
23
  * readonly dropdown = viewChild<ElementRef>('dropdown');
24
24
  * readonly isOpen = signal(true);
25
25
  *
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-on-click-outside.mjs","sources":["../../../projects/core/elements/on-click-outside/index.ts","../../../projects/core/elements/on-click-outside/signality-core-elements-on-click-outside.ts"],"sourcesContent":["import { NOOP_EFFECT_REF, setupContext, toElement } from '@signality/core/internal';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport interface OnClickOutsideOptions extends WithInjector {\n /**\n * Elements that should not trigger the outside click handler.\n */\n readonly ignore?: MaybeElementSignal<Element>[];\n}\n\nexport interface OnClickOutsideRef {\n /** Stops listening for outside clicks. */\n readonly destroy: () => void;\n}\n\n/**\n * Detects clicks outside a target element and invokes a callback.\n * Useful for closing dropdowns, modals, and popovers when the user clicks elsewhere.\n *\n * @param target - Element to detect clicks outside of\n * @param handler - Callback invoked when a click outside the target is detected\n * @param options - Optional configuration including ignore list and injector\n * @returns An OnClickOutsideRef with a destroy method to stop detection\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #dropdown class=\"dropdown\">\n * <p>Dropdown content</p>\n * </div>\n * `\n * })\n * class DropdownComponent {\n * readonly dropdown = viewChild<ElementRef>('dropdown');\n * readonly isOpen = signal(true);\n *\n * constructor() {\n * onClickOutside(this.dropdown, () => {\n * this.isOpen.set(false);\n * });\n * }\n * }\n * ```\n */\nexport function onClickOutside(\n target: MaybeElementSignal<HTMLElement>,\n handler: (event: PointerEvent | FocusEvent) => void,\n options?: OnClickOutsideOptions\n): OnClickOutsideRef {\n const { runInContext } = setupContext(options?.injector, onClickOutside);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n const ignoreList = options?.ignore ?? [];\n\n let shouldFire = false;\n\n function isOutside(event: Event): boolean {\n const el = toElement(target);\n const path = event.composedPath();\n\n if (el && path.includes(el)) {\n return false;\n }\n\n if (ignoreList.length) {\n return !ignoreList.some(ignored => {\n const ignoredEl = toElement(ignored);\n return ignoredEl && path.includes(ignoredEl);\n });\n }\n\n return true;\n }\n\n const pointerDownListener = setupSync(() =>\n listener.capture(window, 'pointerdown', (e: PointerEvent) => {\n shouldFire = isOutside(e);\n })\n );\n\n const clickListener = setupSync(() =>\n listener.capture(window, 'pointerup', (e: PointerEvent) => {\n if (!shouldFire) {\n return;\n }\n\n shouldFire = false;\n\n if (!isOutside(e)) {\n return;\n }\n\n handler(e);\n })\n );\n\n const blurListener = setupSync(() =>\n listener(window, 'blur', (e: FocusEvent) => {\n setTimeout(() => {\n const active = document.activeElement;\n\n if (active?.tagName !== 'IFRAME') {\n return;\n }\n\n const el = toElement(target);\n\n if (el?.contains(active)) {\n return;\n }\n\n handler(e);\n }, 0);\n })\n );\n\n return {\n destroy: () => {\n pointerDownListener.destroy();\n clickListener.destroy();\n blurListener.destroy();\n },\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAgBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BG;SACa,cAAc,CAC5B,MAAuC,EACvC,OAAmD,EACnD,OAA+B,EAAA;AAE/B,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,eAAe;QACxB;AAEA,QAAA,MAAM,UAAU,GAAG,OAAO,EAAE,MAAM,IAAI,EAAE;QAExC,IAAI,UAAU,GAAG,KAAK;QAEtB,SAAS,SAAS,CAAC,KAAY,EAAA;AAC7B,YAAA,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC;AAC5B,YAAA,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,EAAE;YAEjC,IAAI,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE;AAC3B,gBAAA,OAAO,KAAK;YACd;AAEA,YAAA,IAAI,UAAU,CAAC,MAAM,EAAE;AACrB,gBAAA,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,IAAG;AAChC,oBAAA,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC;oBACpC,OAAO,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;AAC9C,gBAAA,CAAC,CAAC;YACJ;AAEA,YAAA,OAAO,IAAI;QACb;AAEA,QAAA,MAAM,mBAAmB,GAAG,SAAS,CAAC,MACpC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAe,KAAI;AAC1D,YAAA,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC;QAC3B,CAAC,CAAC,CACH;AAED,QAAA,MAAM,aAAa,GAAG,SAAS,CAAC,MAC9B,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAe,KAAI;YACxD,IAAI,CAAC,UAAU,EAAE;gBACf;YACF;YAEA,UAAU,GAAG,KAAK;AAElB,YAAA,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;gBACjB;YACF;YAEA,OAAO,CAAC,CAAC,CAAC;QACZ,CAAC,CAAC,CACH;AAED,QAAA,MAAM,YAAY,GAAG,SAAS,CAAC,MAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAa,KAAI;YACzC,UAAU,CAAC,MAAK;AACd,gBAAA,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa;AAErC,gBAAA,IAAI,MAAM,EAAE,OAAO,KAAK,QAAQ,EAAE;oBAChC;gBACF;AAEA,gBAAA,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC;AAE5B,gBAAA,IAAI,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE;oBACxB;gBACF;gBAEA,OAAO,CAAC,CAAC,CAAC;YACZ,CAAC,EAAE,CAAC,CAAC;QACP,CAAC,CAAC,CACH;QAED,OAAO;YACL,OAAO,EAAE,MAAK;gBACZ,mBAAmB,CAAC,OAAO,EAAE;gBAC7B,aAAa,CAAC,OAAO,EAAE;gBACvB,YAAY,CAAC,OAAO,EAAE;YACxB,CAAC;SACF;AACH,IAAA,CAAC,CAAC;AACJ;;AClIA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-elements-on-click-outside.mjs","sources":["../../../projects/core/elements/on-click-outside/index.ts","../../../projects/core/elements/on-click-outside/signality-core-elements-on-click-outside.ts"],"sourcesContent":["import { NOOP_EFFECT_REF, setupContext, toElement } from '@signality/core/internal';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport interface OnClickOutsideOptions extends WithInjector {\n /**\n * Elements that should not trigger the outside click handler.\n */\n readonly ignore?: MaybeElementSignal<Element>[];\n}\n\nexport interface OnClickOutsideRef {\n /** Stops listening for outside clicks. */\n readonly destroy: () => void;\n}\n\n/**\n * Detects clicks outside a target element and invokes a callback.\n * Useful for closing dropdowns, modals, and popovers when the user clicks elsewhere.\n *\n * @param target - Element to detect clicks outside of\n * @param handler - Callback invoked when a click outside the target is detected\n * @param options - Optional configuration including ignore list and injector\n * @returns An OnClickOutsideRef with a destroy method to stop detection\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #dropdown class=\"dropdown\">\n * <p>Dropdown content</p>\n * </div>\n * `\n * })\n * export class Dropdown {\n * readonly dropdown = viewChild<ElementRef>('dropdown');\n * readonly isOpen = signal(true);\n *\n * constructor() {\n * onClickOutside(this.dropdown, () => {\n * this.isOpen.set(false);\n * });\n * }\n * }\n * ```\n */\nexport function onClickOutside(\n target: MaybeElementSignal<HTMLElement>,\n handler: (event: PointerEvent | FocusEvent) => void,\n options?: OnClickOutsideOptions\n): OnClickOutsideRef {\n const { runInContext } = setupContext(options?.injector, onClickOutside);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n const ignoreList = options?.ignore ?? [];\n\n let shouldFire = false;\n\n function isOutside(event: Event): boolean {\n const el = toElement(target);\n const path = event.composedPath();\n\n if (el && path.includes(el)) {\n return false;\n }\n\n if (ignoreList.length) {\n return !ignoreList.some(ignored => {\n const ignoredEl = toElement(ignored);\n return ignoredEl && path.includes(ignoredEl);\n });\n }\n\n return true;\n }\n\n const pointerDownListener = setupSync(() =>\n listener.capture(window, 'pointerdown', (e: PointerEvent) => {\n shouldFire = isOutside(e);\n })\n );\n\n const clickListener = setupSync(() =>\n listener.capture(window, 'pointerup', (e: PointerEvent) => {\n if (!shouldFire) {\n return;\n }\n\n shouldFire = false;\n\n if (!isOutside(e)) {\n return;\n }\n\n handler(e);\n })\n );\n\n const blurListener = setupSync(() =>\n listener(window, 'blur', (e: FocusEvent) => {\n setTimeout(() => {\n const active = document.activeElement;\n\n if (active?.tagName !== 'IFRAME') {\n return;\n }\n\n const el = toElement(target);\n\n if (el?.contains(active)) {\n return;\n }\n\n handler(e);\n }, 0);\n })\n );\n\n return {\n destroy: () => {\n pointerDownListener.destroy();\n clickListener.destroy();\n blurListener.destroy();\n },\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAgBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BG;SACa,cAAc,CAC5B,MAAuC,EACvC,OAAmD,EACnD,OAA+B,EAAA;AAE/B,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,eAAe;QACxB;AAEA,QAAA,MAAM,UAAU,GAAG,OAAO,EAAE,MAAM,IAAI,EAAE;QAExC,IAAI,UAAU,GAAG,KAAK;QAEtB,SAAS,SAAS,CAAC,KAAY,EAAA;AAC7B,YAAA,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC;AAC5B,YAAA,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,EAAE;YAEjC,IAAI,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE;AAC3B,gBAAA,OAAO,KAAK;YACd;AAEA,YAAA,IAAI,UAAU,CAAC,MAAM,EAAE;AACrB,gBAAA,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,IAAG;AAChC,oBAAA,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC;oBACpC,OAAO,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;AAC9C,gBAAA,CAAC,CAAC;YACJ;AAEA,YAAA,OAAO,IAAI;QACb;AAEA,QAAA,MAAM,mBAAmB,GAAG,SAAS,CAAC,MACpC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAe,KAAI;AAC1D,YAAA,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC;QAC3B,CAAC,CAAC,CACH;AAED,QAAA,MAAM,aAAa,GAAG,SAAS,CAAC,MAC9B,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAe,KAAI;YACxD,IAAI,CAAC,UAAU,EAAE;gBACf;YACF;YAEA,UAAU,GAAG,KAAK;AAElB,YAAA,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;gBACjB;YACF;YAEA,OAAO,CAAC,CAAC,CAAC;QACZ,CAAC,CAAC,CACH;AAED,QAAA,MAAM,YAAY,GAAG,SAAS,CAAC,MAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAa,KAAI;YACzC,UAAU,CAAC,MAAK;AACd,gBAAA,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa;AAErC,gBAAA,IAAI,MAAM,EAAE,OAAO,KAAK,QAAQ,EAAE;oBAChC;gBACF;AAEA,gBAAA,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC;AAE5B,gBAAA,IAAI,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE;oBACxB;gBACF;gBAEA,OAAO,CAAC,CAAC,CAAC;YACZ,CAAC,EAAE,CAAC,CAAC;QACP,CAAC,CAAC,CACH;QAED,OAAO;YACL,OAAO,EAAE,MAAK;gBACZ,mBAAmB,CAAC,OAAO,EAAE;gBAC7B,aAAa,CAAC,OAAO,EAAE;gBACvB,YAAY,CAAC,OAAO,EAAE;YACxB,CAAC;SACF;AACH,IAAA,CAAC,CAAC;AACJ;;AClIA;;AAEG;;;;"}
@@ -17,7 +17,7 @@ import { setupContext, NOOP_EFFECT_REF, toElement, isQuerySignal } from '@signal
17
17
  * <button (click)="remove()">Remove</button>
18
18
  * `
19
19
  * })
20
- * class DisconnectComponent {
20
+ * export class OnDisconnectDemo {
21
21
  * readonly box = viewChild<ElementRef>('box');
22
22
  *
23
23
  * constructor() {
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-on-disconnect.mjs","sources":["../../../projects/core/elements/on-disconnect/index.ts","../../../projects/core/elements/on-disconnect/signality-core-elements-on-disconnect.ts"],"sourcesContent":["import { afterEveryRender, afterRenderEffect, DestroyRef, ElementRef, inject } from '@angular/core';\nimport { isQuerySignal, NOOP_EFFECT_REF, setupContext, toElement } from '@signality/core/internal';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\n\nexport type OnDisconnectOptions = WithInjector;\n\nexport interface OnDisconnectRef {\n readonly destroy: () => void;\n}\n\n/**\n * Executes a callback when an element is disconnected from the DOM.\n *\n * @param target - The element to watch for disconnection\n * @param callback - Callback to execute when the element is disconnected\n * @param options - Optional configuration including injector\n * @returns OnDisconnectRef with a destroy method to stop watching for disconnection\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #box>Content</div>\n * <button (click)=\"remove()\">Remove</button>\n * `\n * })\n * class DisconnectComponent {\n * readonly box = viewChild<ElementRef>('box');\n *\n * constructor() {\n * onDisconnect(this.box, el => {\n * console.log('Element disconnected: ', el.tagName);\n * // perform cleanup without storing the reference\n * });\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Manual cleanup\n * const disconnectRef = onDisconnect(element, () => {\n * console.log('Disconnected');\n * });\n *\n * // Stop watching before disconnection\n * disconnectRef.destroy();\n * ```\n */\nexport function onDisconnect<T extends Element>(\n target: MaybeElementSignal<T>,\n callback: (element: T) => void,\n options?: OnDisconnectOptions\n): OnDisconnectRef {\n const { runInContext } = setupContext(options?.injector, onDisconnect);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n // (1) Host element check\n // if we are inside a directive and the target element is its host,\n // then we hook into the directive's lifecycle via its DestroyRef\n const hostElRef = inject(ElementRef, { optional: true, self: true });\n if (hostElRef) {\n const targetEl = toElement(target);\n if (targetEl && hostElRef.nativeElement === targetEl) {\n return {\n destroy: inject(DestroyRef).onDestroy(() => callback(targetEl)),\n };\n }\n }\n\n // (2) Query signal check (viewChild/contentChild)\n // if target is a query signal, we rely on its automatic state transition\n // managed by Angular's change detection, calling callback at render completion timing\n if (isQuerySignal(target)) {\n const effectRef = afterRenderEffect({\n read: onCleanup => {\n const targetEl = toElement(target);\n\n if (targetEl) {\n onCleanup(() => {\n if (!targetEl.isConnected) callback(targetEl);\n });\n }\n },\n });\n\n return { destroy: () => effectRef.destroy() };\n }\n\n // (3) Fallback\n // for any DOM target (reactive or non-reactive), we assume the value was\n // manually read from the DOM. Therefore, we perform isConnected check after\n // every render cycle to detect disconnection\n // @TODO: document the behavior of state transitions when the target is reactive\n const afterRenderRef = afterEveryRender({\n read: () => {\n const targetEl = toElement(target);\n if (targetEl && !targetEl.isConnected) callback(targetEl);\n },\n });\n\n return { destroy: () => afterRenderRef.destroy() };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCG;SACa,YAAY,CAC1B,MAA6B,EAC7B,QAA8B,EAC9B,OAA6B,EAAA;AAE7B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,CAAC;AAEtE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,eAAe;QACxB;;;;AAKA,QAAA,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACpE,IAAI,SAAS,EAAE;AACb,YAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC;YAClC,IAAI,QAAQ,IAAI,SAAS,CAAC,aAAa,KAAK,QAAQ,EAAE;gBACpD,OAAO;AACL,oBAAA,OAAO,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;iBAChE;YACH;QACF;;;;AAKA,QAAA,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE;YACzB,MAAM,SAAS,GAAG,iBAAiB,CAAC;gBAClC,IAAI,EAAE,SAAS,IAAG;AAChB,oBAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC;oBAElC,IAAI,QAAQ,EAAE;wBACZ,SAAS,CAAC,MAAK;4BACb,IAAI,CAAC,QAAQ,CAAC,WAAW;gCAAE,QAAQ,CAAC,QAAQ,CAAC;AAC/C,wBAAA,CAAC,CAAC;oBACJ;gBACF,CAAC;AACF,aAAA,CAAC;YAEF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE;QAC/C;;;;;;QAOA,MAAM,cAAc,GAAG,gBAAgB,CAAC;YACtC,IAAI,EAAE,MAAK;AACT,gBAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC;AAClC,gBAAA,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,WAAW;oBAAE,QAAQ,CAAC,QAAQ,CAAC;YAC3D,CAAC;AACF,SAAA,CAAC;QAEF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC,OAAO,EAAE,EAAE;AACpD,IAAA,CAAC,CAAC;AACJ;;AC3GA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-elements-on-disconnect.mjs","sources":["../../../projects/core/elements/on-disconnect/index.ts","../../../projects/core/elements/on-disconnect/signality-core-elements-on-disconnect.ts"],"sourcesContent":["import { afterEveryRender, afterRenderEffect, DestroyRef, ElementRef, inject } from '@angular/core';\nimport { isQuerySignal, NOOP_EFFECT_REF, setupContext, toElement } from '@signality/core/internal';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\n\nexport type OnDisconnectOptions = WithInjector;\n\nexport interface OnDisconnectRef {\n readonly destroy: () => void;\n}\n\n/**\n * Executes a callback when an element is disconnected from the DOM.\n *\n * @param target - The element to watch for disconnection\n * @param callback - Callback to execute when the element is disconnected\n * @param options - Optional configuration including injector\n * @returns OnDisconnectRef with a destroy method to stop watching for disconnection\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #box>Content</div>\n * <button (click)=\"remove()\">Remove</button>\n * `\n * })\n * export class OnDisconnectDemo {\n * readonly box = viewChild<ElementRef>('box');\n *\n * constructor() {\n * onDisconnect(this.box, el => {\n * console.log('Element disconnected: ', el.tagName);\n * // perform cleanup without storing the reference\n * });\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Manual cleanup\n * const disconnectRef = onDisconnect(element, () => {\n * console.log('Disconnected');\n * });\n *\n * // Stop watching before disconnection\n * disconnectRef.destroy();\n * ```\n */\nexport function onDisconnect<T extends Element>(\n target: MaybeElementSignal<T>,\n callback: (element: T) => void,\n options?: OnDisconnectOptions\n): OnDisconnectRef {\n const { runInContext } = setupContext(options?.injector, onDisconnect);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n // (1) Host element check\n // if we are inside a directive and the target element is its host,\n // then we hook into the directive's lifecycle via its DestroyRef\n const hostElRef = inject(ElementRef, { optional: true, self: true });\n if (hostElRef) {\n const targetEl = toElement(target);\n if (targetEl && hostElRef.nativeElement === targetEl) {\n return {\n destroy: inject(DestroyRef).onDestroy(() => callback(targetEl)),\n };\n }\n }\n\n // (2) Query signal check (viewChild/contentChild)\n // if target is a query signal, we rely on its automatic state transition\n // managed by Angular's change detection, calling callback at render completion timing\n if (isQuerySignal(target)) {\n const effectRef = afterRenderEffect({\n read: onCleanup => {\n const targetEl = toElement(target);\n\n if (targetEl) {\n onCleanup(() => {\n if (!targetEl.isConnected) callback(targetEl);\n });\n }\n },\n });\n\n return { destroy: () => effectRef.destroy() };\n }\n\n // (3) Fallback\n // for any DOM target (reactive or non-reactive), we assume the value was\n // manually read from the DOM. Therefore, we perform isConnected check after\n // every render cycle to detect disconnection\n // @TODO: document the behavior of state transitions when the target is reactive\n const afterRenderRef = afterEveryRender({\n read: () => {\n const targetEl = toElement(target);\n if (targetEl && !targetEl.isConnected) callback(targetEl);\n },\n });\n\n return { destroy: () => afterRenderRef.destroy() };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCG;SACa,YAAY,CAC1B,MAA6B,EAC7B,QAA8B,EAC9B,OAA6B,EAAA;AAE7B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,CAAC;AAEtE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,eAAe;QACxB;;;;AAKA,QAAA,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACpE,IAAI,SAAS,EAAE;AACb,YAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC;YAClC,IAAI,QAAQ,IAAI,SAAS,CAAC,aAAa,KAAK,QAAQ,EAAE;gBACpD,OAAO;AACL,oBAAA,OAAO,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;iBAChE;YACH;QACF;;;;AAKA,QAAA,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE;YACzB,MAAM,SAAS,GAAG,iBAAiB,CAAC;gBAClC,IAAI,EAAE,SAAS,IAAG;AAChB,oBAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC;oBAElC,IAAI,QAAQ,EAAE;wBACZ,SAAS,CAAC,MAAK;4BACb,IAAI,CAAC,QAAQ,CAAC,WAAW;gCAAE,QAAQ,CAAC,QAAQ,CAAC;AAC/C,wBAAA,CAAC,CAAC;oBACJ;gBACF,CAAC;AACF,aAAA,CAAC;YAEF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE;QAC/C;;;;;;QAOA,MAAM,cAAc,GAAG,gBAAgB,CAAC;YACtC,IAAI,EAAE,MAAK;AACT,gBAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC;AAClC,gBAAA,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,WAAW;oBAAE,QAAQ,CAAC,QAAQ,CAAC;YAC3D,CAAC;AACF,SAAA,CAAC;QAEF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC,OAAO,EAAE,EAAE;AACpD,IAAA,CAAC,CAAC;AACJ;;AC3GA;;AAEG;;;;"}
@@ -16,7 +16,7 @@ import { listener } from '@signality/core/browser/listener';
16
16
  * @Component({
17
17
  * template: `<button #btn>Hold me</button>`
18
18
  * })
19
- * class LongPressComponent {
19
+ * export class OnLongPressDemo {
20
20
  * readonly btn = viewChild<ElementRef>('btn');
21
21
  *
22
22
  * constructor() {
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-on-long-press.mjs","sources":["../../../projects/core/elements/on-long-press/index.ts","../../../projects/core/elements/on-long-press/signality-core-elements-on-long-press.ts"],"sourcesContent":["import { NOOP_EFFECT_REF, setupContext, type Timer, toValue } from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\n\nexport interface OnLongPressOptions extends WithInjector {\n /**\n * Time in ms before the callback is triggered.\n * @default 500\n */\n readonly delay?: MaybeSignal<number>;\n\n /**\n * Maximum distance (in pixels) the pointer can move before cancelling.\n * Set to `false` to disable distance checking.\n * @default 10\n */\n readonly distanceThreshold?: number | false;\n}\n\nexport interface OnLongPressRef {\n readonly destroy: () => void;\n}\n\n/**\n * Detect long press gestures on an element.\n * Calls a callback after a configurable delay if the pointer stays down\n * without moving beyond the distance threshold.\n *\n * @param target - The element to detect long presses on\n * @param handler - Callback invoked when a long press is detected\n * @param options - Optional configuration including delay, distance threshold, and injector\n * @returns A OnLongPressRef with a destroy method to stop detection\n *\n * @example\n * ```typescript\n * @Component({\n * template: `<button #btn>Hold me</button>`\n * })\n * class LongPressComponent {\n * readonly btn = viewChild<ElementRef>('btn');\n *\n * constructor() {\n * onLongPress(this.btn, () => {\n * console.log('Long press detected!');\n * });\n * }\n * }\n * ```\n */\nexport function onLongPress(\n target: MaybeElementSignal<HTMLElement>,\n handler: (event: PointerEvent) => void,\n options?: OnLongPressOptions\n): OnLongPressRef {\n const { runInContext } = setupContext(options?.injector, onLongPress);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n const distanceThreshold = options?.distanceThreshold;\n\n let startX = 0;\n let startY = 0;\n let longPressTimeout: Timer;\n\n function abortPendingPress(): void {\n clearTimeout(longPressTimeout);\n longPressTimeout = undefined;\n }\n\n const downListener = listener(target, 'pointerdown', (e: PointerEvent) => {\n startX = e.clientX;\n startY = e.clientY;\n\n const delay = toValue(options?.delay) ?? 500;\n\n longPressTimeout = setTimeout(() => {\n handler(e);\n longPressTimeout = undefined;\n }, delay);\n });\n\n const moveListener = listener(target, 'pointermove', (e: PointerEvent) => {\n if (!longPressTimeout || distanceThreshold === false) {\n return;\n }\n\n const threshold = distanceThreshold ?? 10;\n const dx = e.clientX - startX;\n const dy = e.clientY - startY;\n\n if (Math.sqrt(dx * dx + dy * dy) > threshold) {\n abortPendingPress();\n }\n });\n\n const upListener = listener(target, 'pointerup', abortPendingPress);\n const leaveListener = listener(target, 'pointerleave', abortPendingPress);\n\n onCleanup(abortPendingPress);\n\n return {\n destroy: () => {\n abortPendingPress();\n upListener.destroy();\n downListener.destroy();\n moveListener.destroy();\n leaveListener.destroy();\n },\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAuBA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;SACa,WAAW,CACzB,MAAuC,EACvC,OAAsC,EACtC,OAA4B,EAAA;AAE5B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC;IAErE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAI;QAC9C,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,eAAe;QACxB;AAEA,QAAA,MAAM,iBAAiB,GAAG,OAAO,EAAE,iBAAiB;QAEpD,IAAI,MAAM,GAAG,CAAC;QACd,IAAI,MAAM,GAAG,CAAC;AACd,QAAA,IAAI,gBAAuB;AAE3B,QAAA,SAAS,iBAAiB,GAAA;YACxB,YAAY,CAAC,gBAAgB,CAAC;YAC9B,gBAAgB,GAAG,SAAS;QAC9B;QAEA,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAe,KAAI;AACvE,YAAA,MAAM,GAAG,CAAC,CAAC,OAAO;AAClB,YAAA,MAAM,GAAG,CAAC,CAAC,OAAO;YAElB,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,GAAG;AAE5C,YAAA,gBAAgB,GAAG,UAAU,CAAC,MAAK;gBACjC,OAAO,CAAC,CAAC,CAAC;gBACV,gBAAgB,GAAG,SAAS;YAC9B,CAAC,EAAE,KAAK,CAAC;AACX,QAAA,CAAC,CAAC;QAEF,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAe,KAAI;AACvE,YAAA,IAAI,CAAC,gBAAgB,IAAI,iBAAiB,KAAK,KAAK,EAAE;gBACpD;YACF;AAEA,YAAA,MAAM,SAAS,GAAG,iBAAiB,IAAI,EAAE;AACzC,YAAA,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,GAAG,MAAM;AAC7B,YAAA,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,GAAG,MAAM;AAE7B,YAAA,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,SAAS,EAAE;AAC5C,gBAAA,iBAAiB,EAAE;YACrB;AACF,QAAA,CAAC,CAAC;QAEF,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,iBAAiB,CAAC;QACnE,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,iBAAiB,CAAC;QAEzE,SAAS,CAAC,iBAAiB,CAAC;QAE5B,OAAO;YACL,OAAO,EAAE,MAAK;AACZ,gBAAA,iBAAiB,EAAE;gBACnB,UAAU,CAAC,OAAO,EAAE;gBACpB,YAAY,CAAC,OAAO,EAAE;gBACtB,YAAY,CAAC,OAAO,EAAE;gBACtB,aAAa,CAAC,OAAO,EAAE;YACzB,CAAC;SACF;AACH,IAAA,CAAC,CAAC;AACJ;;ACjHA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-elements-on-long-press.mjs","sources":["../../../projects/core/elements/on-long-press/index.ts","../../../projects/core/elements/on-long-press/signality-core-elements-on-long-press.ts"],"sourcesContent":["import { NOOP_EFFECT_REF, setupContext, type Timer, toValue } from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\n\nexport interface OnLongPressOptions extends WithInjector {\n /**\n * Time in ms before the callback is triggered.\n * @default 500\n */\n readonly delay?: MaybeSignal<number>;\n\n /**\n * Maximum distance (in pixels) the pointer can move before cancelling.\n * Set to `false` to disable distance checking.\n * @default 10\n */\n readonly distanceThreshold?: number | false;\n}\n\nexport interface OnLongPressRef {\n readonly destroy: () => void;\n}\n\n/**\n * Detect long press gestures on an element.\n * Calls a callback after a configurable delay if the pointer stays down\n * without moving beyond the distance threshold.\n *\n * @param target - The element to detect long presses on\n * @param handler - Callback invoked when a long press is detected\n * @param options - Optional configuration including delay, distance threshold, and injector\n * @returns A OnLongPressRef with a destroy method to stop detection\n *\n * @example\n * ```typescript\n * @Component({\n * template: `<button #btn>Hold me</button>`\n * })\n * export class OnLongPressDemo {\n * readonly btn = viewChild<ElementRef>('btn');\n *\n * constructor() {\n * onLongPress(this.btn, () => {\n * console.log('Long press detected!');\n * });\n * }\n * }\n * ```\n */\nexport function onLongPress(\n target: MaybeElementSignal<HTMLElement>,\n handler: (event: PointerEvent) => void,\n options?: OnLongPressOptions\n): OnLongPressRef {\n const { runInContext } = setupContext(options?.injector, onLongPress);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n const distanceThreshold = options?.distanceThreshold;\n\n let startX = 0;\n let startY = 0;\n let longPressTimeout: Timer;\n\n function abortPendingPress(): void {\n clearTimeout(longPressTimeout);\n longPressTimeout = undefined;\n }\n\n const downListener = listener(target, 'pointerdown', (e: PointerEvent) => {\n startX = e.clientX;\n startY = e.clientY;\n\n const delay = toValue(options?.delay) ?? 500;\n\n longPressTimeout = setTimeout(() => {\n handler(e);\n longPressTimeout = undefined;\n }, delay);\n });\n\n const moveListener = listener(target, 'pointermove', (e: PointerEvent) => {\n if (!longPressTimeout || distanceThreshold === false) {\n return;\n }\n\n const threshold = distanceThreshold ?? 10;\n const dx = e.clientX - startX;\n const dy = e.clientY - startY;\n\n if (Math.sqrt(dx * dx + dy * dy) > threshold) {\n abortPendingPress();\n }\n });\n\n const upListener = listener(target, 'pointerup', abortPendingPress);\n const leaveListener = listener(target, 'pointerleave', abortPendingPress);\n\n onCleanup(abortPendingPress);\n\n return {\n destroy: () => {\n abortPendingPress();\n upListener.destroy();\n downListener.destroy();\n moveListener.destroy();\n leaveListener.destroy();\n },\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAuBA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;SACa,WAAW,CACzB,MAAuC,EACvC,OAAsC,EACtC,OAA4B,EAAA;AAE5B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC;IAErE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAI;QAC9C,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,eAAe;QACxB;AAEA,QAAA,MAAM,iBAAiB,GAAG,OAAO,EAAE,iBAAiB;QAEpD,IAAI,MAAM,GAAG,CAAC;QACd,IAAI,MAAM,GAAG,CAAC;AACd,QAAA,IAAI,gBAAuB;AAE3B,QAAA,SAAS,iBAAiB,GAAA;YACxB,YAAY,CAAC,gBAAgB,CAAC;YAC9B,gBAAgB,GAAG,SAAS;QAC9B;QAEA,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAe,KAAI;AACvE,YAAA,MAAM,GAAG,CAAC,CAAC,OAAO;AAClB,YAAA,MAAM,GAAG,CAAC,CAAC,OAAO;YAElB,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,GAAG;AAE5C,YAAA,gBAAgB,GAAG,UAAU,CAAC,MAAK;gBACjC,OAAO,CAAC,CAAC,CAAC;gBACV,gBAAgB,GAAG,SAAS;YAC9B,CAAC,EAAE,KAAK,CAAC;AACX,QAAA,CAAC,CAAC;QAEF,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAe,KAAI;AACvE,YAAA,IAAI,CAAC,gBAAgB,IAAI,iBAAiB,KAAK,KAAK,EAAE;gBACpD;YACF;AAEA,YAAA,MAAM,SAAS,GAAG,iBAAiB,IAAI,EAAE;AACzC,YAAA,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,GAAG,MAAM;AAC7B,YAAA,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,GAAG,MAAM;AAE7B,YAAA,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,SAAS,EAAE;AAC5C,gBAAA,iBAAiB,EAAE;YACrB;AACF,QAAA,CAAC,CAAC;QAEF,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,iBAAiB,CAAC;QACnE,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,iBAAiB,CAAC;QAEzE,SAAS,CAAC,iBAAiB,CAAC;QAE5B,OAAO;YACL,OAAO,EAAE,MAAK;AACZ,gBAAA,iBAAiB,EAAE;gBACnB,UAAU,CAAC,OAAO,EAAE;gBACpB,YAAY,CAAC,OAAO,EAAE;gBACtB,YAAY,CAAC,OAAO,EAAE;gBACtB,aAAa,CAAC,OAAO,EAAE;YACzB,CAAC;SACF;AACH,IAAA,CAAC,CAAC;AACJ;;ACjHA;;AAEG;;;;"}
@@ -22,7 +22,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
22
22
  * </div>
23
23
  * `
24
24
  * })
25
- * class PointerSwipeComponent {
25
+ * export class PointerSwipeComponent {
26
26
  * readonly area = viewChild<ElementRef>('area');
27
27
  * readonly sw = pointerSwipe(this.area);
28
28
  * }
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-pointer-swipe.mjs","sources":["../../../projects/core/elements/pointer-swipe/index.ts","../../../projects/core/elements/pointer-swipe/signality-core-elements-pointer-swipe.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } 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\n/**\n * Pointer type that triggered the event.\n */\nexport type PointerType = 'mouse' | 'touch' | 'pen';\n\n/**\n * Possible swipe direction values.\n */\nexport type PointerSwipeDirection = 'up' | 'down' | 'left' | 'right' | 'none';\n\nexport interface PointerSwipeOptions extends WithInjector {\n /**\n * Minimum distance in pixels before a swipe is recognized.\n * @default 50\n */\n readonly threshold?: number;\n\n /**\n * Pointer types to listen for.\n * @default ['mouse', 'touch', 'pen']\n */\n readonly pointerTypes?: PointerType[];\n}\n\nexport interface PointerSwipeRef {\n /** Whether a swipe gesture is currently in progress. */\n readonly isSwiping: Signal<boolean>;\n\n /** Current swipe direction, or `'none'` if a threshold has not been exceeded. */\n readonly direction: Signal<PointerSwipeDirection>;\n\n /** Horizontal distance from start (positive = swiped left). */\n readonly distanceX: Signal<number>;\n\n /** Vertical distance from start (positive = swiped up). */\n readonly distanceY: Signal<number>;\n}\n\n/**\n * Reactive pointer-swipe detection on an element.\n * Tracks swipe gestures using Pointer Events and provides direction and distance signals.\n * Works with mouse, touch, and pen input.\n *\n * @param target - Element to detect swipe gestures on\n * @param options - Optional configuration including threshold, pointer types, and injector\n * @returns A PointerSwipeRef with reactive signals for swipe state\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #area>\n * <p>Swiping: {{ sw.isSwiping() }}</p>\n * <p>Direction: {{ sw.direction() }}</p>\n * </div>\n * `\n * })\n * class PointerSwipeComponent {\n * readonly area = viewChild<ElementRef>('area');\n * readonly sw = pointerSwipe(this.area);\n * }\n * ```\n */\nexport function pointerSwipe(\n target: MaybeElementSignal<HTMLElement>,\n options?: PointerSwipeOptions\n): PointerSwipeRef {\n const { runInContext } = setupContext(options?.injector, pointerSwipe);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return {\n isSwiping: constSignal(false),\n direction: constSignal('none'),\n distanceX: constSignal(0),\n distanceY: constSignal(0),\n };\n }\n\n const threshold = options?.threshold ?? 50;\n const pointerTypes = options?.pointerTypes;\n\n const isSwiping = signal(false);\n const direction = signal<PointerSwipeDirection>('none');\n const distanceX = signal(0);\n const distanceY = signal(0);\n\n let startX = 0;\n let startY = 0;\n let isPointerDown = false;\n\n function isAllowedPointerType(e: PointerEvent): boolean {\n return !pointerTypes || pointerTypes.includes(e.pointerType as PointerType);\n }\n\n listener.passive(target, 'pointerdown', (e: PointerEvent) => {\n if (!isAllowedPointerType(e)) {\n return;\n }\n\n isPointerDown = true;\n\n (e.target as HTMLElement | undefined)?.setPointerCapture(e.pointerId);\n\n startX = e.clientX;\n startY = e.clientY;\n\n isSwiping.set(false);\n direction.set('none');\n distanceX.set(0);\n distanceY.set(0);\n });\n\n listener.passive(target, 'pointermove', (e: PointerEvent) => {\n if (!isPointerDown || !isAllowedPointerType(e)) {\n return;\n }\n\n const dx = startX - e.clientX;\n const dy = startY - e.clientY;\n\n distanceX.set(dx);\n distanceY.set(dy);\n\n const absDx = Math.abs(dx);\n const absDy = Math.abs(dy);\n\n if (Math.max(absDx, absDy) >= threshold) {\n if (!isSwiping()) {\n isSwiping.set(true);\n }\n\n if (absDx > absDy) {\n direction.set(dx > 0 ? 'left' : 'right');\n } else {\n direction.set(dy > 0 ? 'up' : 'down');\n }\n }\n });\n\n const onPointerEnd = () => {\n isPointerDown = false;\n isSwiping.set(false);\n };\n\n listener.passive(target, 'pointerup', onPointerEnd);\n listener.passive(target, 'pointercancel', onPointerEnd);\n\n onDisconnect(target, () => {\n isPointerDown = false;\n isSwiping.set(false);\n direction.set('none');\n distanceX.set(0);\n distanceY.set(0);\n });\n\n return {\n isSwiping: isSwiping.asReadonly(),\n direction: direction.asReadonly(),\n distanceX: distanceX.asReadonly(),\n distanceY: distanceY.asReadonly(),\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-elements-pointer-swipe.mjs","sources":["../../../projects/core/elements/pointer-swipe/index.ts","../../../projects/core/elements/pointer-swipe/signality-core-elements-pointer-swipe.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } 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\n/**\n * Pointer type that triggered the event.\n */\nexport type PointerType = 'mouse' | 'touch' | 'pen';\n\n/**\n * Possible swipe direction values.\n */\nexport type PointerSwipeDirection = 'up' | 'down' | 'left' | 'right' | 'none';\n\nexport interface PointerSwipeOptions extends WithInjector {\n /**\n * Minimum distance in pixels before a swipe is recognized.\n * @default 50\n */\n readonly threshold?: number;\n\n /**\n * Pointer types to listen for.\n * @default ['mouse', 'touch', 'pen']\n */\n readonly pointerTypes?: PointerType[];\n}\n\nexport interface PointerSwipeRef {\n /** Whether a swipe gesture is currently in progress. */\n readonly isSwiping: Signal<boolean>;\n\n /** Current swipe direction, or `'none'` if a threshold has not been exceeded. */\n readonly direction: Signal<PointerSwipeDirection>;\n\n /** Horizontal distance from start (positive = swiped left). */\n readonly distanceX: Signal<number>;\n\n /** Vertical distance from start (positive = swiped up). */\n readonly distanceY: Signal<number>;\n}\n\n/**\n * Reactive pointer-swipe detection on an element.\n * Tracks swipe gestures using Pointer Events and provides direction and distance signals.\n * Works with mouse, touch, and pen input.\n *\n * @param target - Element to detect swipe gestures on\n * @param options - Optional configuration including threshold, pointer types, and injector\n * @returns A PointerSwipeRef with reactive signals for swipe state\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #area>\n * <p>Swiping: {{ sw.isSwiping() }}</p>\n * <p>Direction: {{ sw.direction() }}</p>\n * </div>\n * `\n * })\n * export class PointerSwipeComponent {\n * readonly area = viewChild<ElementRef>('area');\n * readonly sw = pointerSwipe(this.area);\n * }\n * ```\n */\nexport function pointerSwipe(\n target: MaybeElementSignal<HTMLElement>,\n options?: PointerSwipeOptions\n): PointerSwipeRef {\n const { runInContext } = setupContext(options?.injector, pointerSwipe);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return {\n isSwiping: constSignal(false),\n direction: constSignal('none'),\n distanceX: constSignal(0),\n distanceY: constSignal(0),\n };\n }\n\n const threshold = options?.threshold ?? 50;\n const pointerTypes = options?.pointerTypes;\n\n const isSwiping = signal(false);\n const direction = signal<PointerSwipeDirection>('none');\n const distanceX = signal(0);\n const distanceY = signal(0);\n\n let startX = 0;\n let startY = 0;\n let isPointerDown = false;\n\n function isAllowedPointerType(e: PointerEvent): boolean {\n return !pointerTypes || pointerTypes.includes(e.pointerType as PointerType);\n }\n\n listener.passive(target, 'pointerdown', (e: PointerEvent) => {\n if (!isAllowedPointerType(e)) {\n return;\n }\n\n isPointerDown = true;\n\n (e.target as HTMLElement | undefined)?.setPointerCapture(e.pointerId);\n\n startX = e.clientX;\n startY = e.clientY;\n\n isSwiping.set(false);\n direction.set('none');\n distanceX.set(0);\n distanceY.set(0);\n });\n\n listener.passive(target, 'pointermove', (e: PointerEvent) => {\n if (!isPointerDown || !isAllowedPointerType(e)) {\n return;\n }\n\n const dx = startX - e.clientX;\n const dy = startY - e.clientY;\n\n distanceX.set(dx);\n distanceY.set(dy);\n\n const absDx = Math.abs(dx);\n const absDy = Math.abs(dy);\n\n if (Math.max(absDx, absDy) >= threshold) {\n if (!isSwiping()) {\n isSwiping.set(true);\n }\n\n if (absDx > absDy) {\n direction.set(dx > 0 ? 'left' : 'right');\n } else {\n direction.set(dy > 0 ? 'up' : 'down');\n }\n }\n });\n\n const onPointerEnd = () => {\n isPointerDown = false;\n isSwiping.set(false);\n };\n\n listener.passive(target, 'pointerup', onPointerEnd);\n listener.passive(target, 'pointercancel', onPointerEnd);\n\n onDisconnect(target, () => {\n isPointerDown = false;\n isSwiping.set(false);\n direction.set('none');\n distanceX.set(0);\n distanceY.set(0);\n });\n\n return {\n isSwiping: isSwiping.asReadonly(),\n direction: direction.asReadonly(),\n distanceX: distanceX.asReadonly(),\n distanceY: distanceY.asReadonly(),\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -24,7 +24,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
24
24
  * }
25
25
  * `
26
26
  * })
27
- * class ScrollTracker {
27
+ * export class ScrollTracker {
28
28
  * readonly scrollPos = scrollPosition();
29
29
  * }
30
30
  * ```
@@ -40,7 +40,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
40
40
  * <p>Scroll position: {{ pos.y() }}</p>
41
41
  * `
42
42
  * })
43
- * class ScrollableComponent {
43
+ * export class ScrollableComponent {
44
44
  * readonly scrollableEl = viewChild<ElementRef>('scrollable');
45
45
  * readonly pos = scrollPosition({ target: this.scrollableEl });
46
46
  * }
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-scroll-position.mjs","sources":["../../../projects/core/elements/scroll-position/index.ts","../../../projects/core/elements/scroll-position/signality-core-elements-scroll-position.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport {\n constSignal,\n isWindow,\n setupContext,\n type Timer,\n toElement,\n} from '@signality/core/internal';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { throttleCallback } from '@signality/core/scheduling/throttle-callback';\nimport { listener } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\n/**\n * State indicating which edges have been reached.\n */\nexport interface ArrivedState {\n readonly top: boolean;\n readonly bottom: boolean;\n readonly left: boolean;\n readonly right: boolean;\n}\n\n/**\n * Current scroll directions.\n */\nexport interface ScrollDirections {\n readonly top: boolean;\n readonly bottom: boolean;\n readonly left: boolean;\n readonly right: boolean;\n}\n\nexport interface ScrollPositionOptions extends WithInjector {\n /**\n * Element or window to track scroll on.\n * @default window\n */\n readonly target?: MaybeElementSignal<Element> | Window;\n\n /**\n * Throttle scroll events in milliseconds.\n * @default 0\n */\n readonly throttle?: number;\n\n /**\n * Offset for arrived detection.\n */\n readonly offset?: {\n readonly top?: number;\n readonly bottom?: number;\n readonly left?: number;\n readonly right?: number;\n };\n}\n\nexport interface ScrollPositionRef {\n /** Horizontal scroll position */\n readonly x: Signal<number>;\n\n /** Vertical scroll position */\n readonly y: Signal<number>;\n\n /** Whether currently scrolling */\n readonly isScrolling: Signal<boolean>;\n\n /** Which edges have been reached */\n readonly arrivedState: Signal<ArrivedState>;\n\n /** Current scroll direction */\n readonly directions: Signal<ScrollDirections>;\n}\n\n/**\n * Reactive tracking of scroll position.\n * Track scroll offset of window or any scrollable element.\n *\n * @param options - Optional configuration\n * @returns An object with x, y, isScrolling, arrivedState, and directions signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Scroll Y: {{ scrollPos.y() }}px</p>\n * @if (scrollPos.isScrolling()) {\n * <p>Scrolling...</p>\n * }\n * @if (scrollPos.arrivedState().bottom) {\n * <p>Reached bottom!</p>\n * }\n * `\n * })\n * class ScrollTracker {\n * readonly scrollPos = scrollPosition();\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Track scroll on a specific element\n * @Component({\n * template: `\n * <div #scrollable style=\"overflow: auto; height: 200px;\">\n * <div style=\"height: 1000px;\">Long content</div>\n * </div>\n * <p>Scroll position: {{ pos.y() }}</p>\n * `\n * })\n * class ScrollableComponent {\n * readonly scrollableEl = viewChild<ElementRef>('scrollable');\n * readonly pos = scrollPosition({ target: this.scrollableEl });\n * }\n * ```\n */\nexport function scrollPosition(options?: ScrollPositionOptions): ScrollPositionRef {\n const { runInContext } = setupContext(options?.injector, scrollPosition);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return {\n x: constSignal(0),\n y: constSignal(0),\n isScrolling: constSignal(false),\n arrivedState: constSignal(DEFAULT_ARRIVED),\n directions: constSignal(DEFAULT_DIRECTIONS),\n };\n }\n\n const target = options?.target ?? window;\n const targetIsWindow = isWindow(target);\n const offset = options?.offset ?? {};\n const throttleMs = options?.throttle ?? 0;\n\n const x = signal(0);\n const y = signal(0);\n const isScrolling = signal(false);\n const arrivedState = signal(DEFAULT_ARRIVED);\n const directions = signal(DEFAULT_DIRECTIONS);\n\n let lastX = 0;\n let lastY = 0;\n let scrollingTimeout: Timer;\n\n const getScrollPosition = (): { scrollX: number; scrollY: number } => {\n if (targetIsWindow) {\n return { scrollX: target.scrollX, scrollY: target.scrollY };\n }\n\n const el = toElement(target);\n return {\n scrollX: el?.scrollLeft || 0,\n scrollY: el?.scrollTop || 0,\n };\n };\n\n const getScrollSize = (): {\n scrollWidth: number;\n scrollHeight: number;\n clientWidth: number;\n clientHeight: number;\n } => {\n if (targetIsWindow) {\n return {\n scrollWidth: document.documentElement.scrollWidth,\n scrollHeight: document.documentElement.scrollHeight,\n clientWidth: target.innerWidth,\n clientHeight: target.innerHeight,\n };\n }\n\n const el = toElement(target);\n return {\n scrollWidth: el?.scrollWidth || 0,\n scrollHeight: el?.scrollHeight || 0,\n clientWidth: el?.clientWidth || 0,\n clientHeight: el?.clientHeight || 0,\n };\n };\n\n const update = () => {\n const { scrollX, scrollY } = getScrollPosition();\n const { scrollWidth, scrollHeight, clientWidth, clientHeight } = getScrollSize();\n\n directions.set({\n top: scrollY < lastY,\n bottom: scrollY > lastY,\n left: scrollX < lastX,\n right: scrollX > lastX,\n });\n\n lastX = scrollX;\n lastY = scrollY;\n\n x.set(scrollX);\n y.set(scrollY);\n\n const topOffset = offset.top ?? 0;\n const bottomOffset = offset.bottom ?? 0;\n const leftOffset = offset.left ?? 0;\n const rightOffset = offset.right ?? 0;\n\n arrivedState.set({\n top: scrollY <= topOffset,\n bottom: scrollY + clientHeight >= scrollHeight - bottomOffset,\n left: scrollX <= leftOffset,\n right: scrollX + clientWidth >= scrollWidth - rightOffset,\n });\n\n isScrolling.set(true);\n\n if (scrollingTimeout) {\n clearTimeout(scrollingTimeout);\n }\n\n scrollingTimeout = setTimeout(() => {\n isScrolling.set(false);\n }, SCROLL_IDLE_DELAY);\n };\n\n listener(target, 'scroll', throttleMs > 0 ? throttleCallback(update, throttleMs) : update);\n\n if (!targetIsWindow) {\n onDisconnect(target, () => {\n x.set(0);\n y.set(0);\n isScrolling.set(false);\n arrivedState.set(DEFAULT_ARRIVED);\n directions.set(DEFAULT_DIRECTIONS);\n });\n }\n\n onCleanup(() => {\n if (scrollingTimeout) {\n clearTimeout(scrollingTimeout);\n }\n });\n\n update();\n\n return {\n x: x.asReadonly(),\n y: y.asReadonly(),\n isScrolling: isScrolling.asReadonly(),\n arrivedState: arrivedState.asReadonly(),\n directions: directions.asReadonly(),\n };\n });\n}\n\nconst DEFAULT_ARRIVED: ArrivedState = {\n top: true,\n bottom: false,\n left: true,\n right: false,\n};\n\nconst DEFAULT_DIRECTIONS: ScrollDirections = {\n top: false,\n bottom: false,\n left: false,\n right: false,\n};\n\nconst SCROLL_IDLE_DELAY = 150;\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-elements-scroll-position.mjs","sources":["../../../projects/core/elements/scroll-position/index.ts","../../../projects/core/elements/scroll-position/signality-core-elements-scroll-position.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport {\n constSignal,\n isWindow,\n setupContext,\n type Timer,\n toElement,\n} from '@signality/core/internal';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { throttleCallback } from '@signality/core/scheduling/throttle-callback';\nimport { listener } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\n/**\n * State indicating which edges have been reached.\n */\nexport interface ArrivedState {\n readonly top: boolean;\n readonly bottom: boolean;\n readonly left: boolean;\n readonly right: boolean;\n}\n\n/**\n * Current scroll directions.\n */\nexport interface ScrollDirections {\n readonly top: boolean;\n readonly bottom: boolean;\n readonly left: boolean;\n readonly right: boolean;\n}\n\nexport interface ScrollPositionOptions extends WithInjector {\n /**\n * Element or window to track scroll on.\n * @default window\n */\n readonly target?: MaybeElementSignal<Element> | Window;\n\n /**\n * Throttle scroll events in milliseconds.\n * @default 0\n */\n readonly throttle?: number;\n\n /**\n * Offset for arrived detection.\n */\n readonly offset?: {\n readonly top?: number;\n readonly bottom?: number;\n readonly left?: number;\n readonly right?: number;\n };\n}\n\nexport interface ScrollPositionRef {\n /** Horizontal scroll position */\n readonly x: Signal<number>;\n\n /** Vertical scroll position */\n readonly y: Signal<number>;\n\n /** Whether currently scrolling */\n readonly isScrolling: Signal<boolean>;\n\n /** Which edges have been reached */\n readonly arrivedState: Signal<ArrivedState>;\n\n /** Current scroll direction */\n readonly directions: Signal<ScrollDirections>;\n}\n\n/**\n * Reactive tracking of scroll position.\n * Track scroll offset of window or any scrollable element.\n *\n * @param options - Optional configuration\n * @returns An object with x, y, isScrolling, arrivedState, and directions signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Scroll Y: {{ scrollPos.y() }}px</p>\n * @if (scrollPos.isScrolling()) {\n * <p>Scrolling...</p>\n * }\n * @if (scrollPos.arrivedState().bottom) {\n * <p>Reached bottom!</p>\n * }\n * `\n * })\n * export class ScrollTracker {\n * readonly scrollPos = scrollPosition();\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Track scroll on a specific element\n * @Component({\n * template: `\n * <div #scrollable style=\"overflow: auto; height: 200px;\">\n * <div style=\"height: 1000px;\">Long content</div>\n * </div>\n * <p>Scroll position: {{ pos.y() }}</p>\n * `\n * })\n * export class ScrollableComponent {\n * readonly scrollableEl = viewChild<ElementRef>('scrollable');\n * readonly pos = scrollPosition({ target: this.scrollableEl });\n * }\n * ```\n */\nexport function scrollPosition(options?: ScrollPositionOptions): ScrollPositionRef {\n const { runInContext } = setupContext(options?.injector, scrollPosition);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return {\n x: constSignal(0),\n y: constSignal(0),\n isScrolling: constSignal(false),\n arrivedState: constSignal(DEFAULT_ARRIVED),\n directions: constSignal(DEFAULT_DIRECTIONS),\n };\n }\n\n const target = options?.target ?? window;\n const targetIsWindow = isWindow(target);\n const offset = options?.offset ?? {};\n const throttleMs = options?.throttle ?? 0;\n\n const x = signal(0);\n const y = signal(0);\n const isScrolling = signal(false);\n const arrivedState = signal(DEFAULT_ARRIVED);\n const directions = signal(DEFAULT_DIRECTIONS);\n\n let lastX = 0;\n let lastY = 0;\n let scrollingTimeout: Timer;\n\n const getScrollPosition = (): { scrollX: number; scrollY: number } => {\n if (targetIsWindow) {\n return { scrollX: target.scrollX, scrollY: target.scrollY };\n }\n\n const el = toElement(target);\n return {\n scrollX: el?.scrollLeft || 0,\n scrollY: el?.scrollTop || 0,\n };\n };\n\n const getScrollSize = (): {\n scrollWidth: number;\n scrollHeight: number;\n clientWidth: number;\n clientHeight: number;\n } => {\n if (targetIsWindow) {\n return {\n scrollWidth: document.documentElement.scrollWidth,\n scrollHeight: document.documentElement.scrollHeight,\n clientWidth: target.innerWidth,\n clientHeight: target.innerHeight,\n };\n }\n\n const el = toElement(target);\n return {\n scrollWidth: el?.scrollWidth || 0,\n scrollHeight: el?.scrollHeight || 0,\n clientWidth: el?.clientWidth || 0,\n clientHeight: el?.clientHeight || 0,\n };\n };\n\n const update = () => {\n const { scrollX, scrollY } = getScrollPosition();\n const { scrollWidth, scrollHeight, clientWidth, clientHeight } = getScrollSize();\n\n directions.set({\n top: scrollY < lastY,\n bottom: scrollY > lastY,\n left: scrollX < lastX,\n right: scrollX > lastX,\n });\n\n lastX = scrollX;\n lastY = scrollY;\n\n x.set(scrollX);\n y.set(scrollY);\n\n const topOffset = offset.top ?? 0;\n const bottomOffset = offset.bottom ?? 0;\n const leftOffset = offset.left ?? 0;\n const rightOffset = offset.right ?? 0;\n\n arrivedState.set({\n top: scrollY <= topOffset,\n bottom: scrollY + clientHeight >= scrollHeight - bottomOffset,\n left: scrollX <= leftOffset,\n right: scrollX + clientWidth >= scrollWidth - rightOffset,\n });\n\n isScrolling.set(true);\n\n if (scrollingTimeout) {\n clearTimeout(scrollingTimeout);\n }\n\n scrollingTimeout = setTimeout(() => {\n isScrolling.set(false);\n }, SCROLL_IDLE_DELAY);\n };\n\n listener(target, 'scroll', throttleMs > 0 ? throttleCallback(update, throttleMs) : update);\n\n if (!targetIsWindow) {\n onDisconnect(target, () => {\n x.set(0);\n y.set(0);\n isScrolling.set(false);\n arrivedState.set(DEFAULT_ARRIVED);\n directions.set(DEFAULT_DIRECTIONS);\n });\n }\n\n onCleanup(() => {\n if (scrollingTimeout) {\n clearTimeout(scrollingTimeout);\n }\n });\n\n update();\n\n return {\n x: x.asReadonly(),\n y: y.asReadonly(),\n isScrolling: isScrolling.asReadonly(),\n arrivedState: arrivedState.asReadonly(),\n directions: directions.asReadonly(),\n };\n });\n}\n\nconst DEFAULT_ARRIVED: ArrivedState = {\n top: true,\n bottom: false,\n left: true,\n right: false,\n};\n\nconst DEFAULT_DIRECTIONS: ScrollDirections = {\n top: false,\n bottom: false,\n left: false,\n right: false,\n};\n\nconst SCROLL_IDLE_DELAY = 150;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -22,7 +22,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
22
22
  * </div>
23
23
  * `
24
24
  * })
25
- * class SwipeComponent {
25
+ * export class SwipeComponent {
26
26
  * readonly area = viewChild<ElementRef>('area');
27
27
  * readonly sw = swipe(this.area);
28
28
  * }
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-swipe.mjs","sources":["../../../projects/core/elements/swipe/index.ts","../../../projects/core/elements/swipe/signality-core-elements-swipe.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } 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\n/**\n * Possible swipe direction values.\n */\nexport type SwipeDirection = 'up' | 'down' | 'left' | 'right' | 'none';\n\nexport interface SwipeOptions extends WithInjector {\n /**\n * Minimum distance in pixels before a swipe is recognized.\n * @default 50\n */\n readonly threshold?: number;\n}\n\nexport interface SwipeRef {\n /** Whether a swipe gesture is currently in progress. */\n readonly isSwiping: Signal<boolean>;\n\n /** Current swipe direction, or `'none'` if a threshold has not been exceeded. */\n readonly direction: Signal<SwipeDirection>;\n\n /** Horizontal distance from start (positive = swiped left). */\n readonly distanceX: Signal<number>;\n\n /** Vertical distance from start (positive = swiped up). */\n readonly distanceY: Signal<number>;\n}\n\n/**\n * Reactive touch-swipe detection on an element.\n * Uses Touch Events API — for mouse/pen input use PointerSwipe instead.\n * Tracks single-finger swipe gestures and provides direction and distance signals.\n *\n * @param target - Element to detect swipe gestures on\n * @param options - Optional configuration including threshold and injector\n * @returns A SwipeRef with reactive signals for swipe state\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #area>\n * <p>Swiping: {{ sw.isSwiping() }}</p>\n * <p>Direction: {{ sw.direction() }}</p>\n * </div>\n * `\n * })\n * class SwipeComponent {\n * readonly area = viewChild<ElementRef>('area');\n * readonly sw = swipe(this.area);\n * }\n * ```\n */\nexport function swipe(target: MaybeElementSignal<HTMLElement>, options?: SwipeOptions): SwipeRef {\n const { runInContext } = setupContext(options?.injector, swipe);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return {\n isSwiping: constSignal(false),\n direction: constSignal('none'),\n distanceX: constSignal(0),\n distanceY: constSignal(0),\n };\n }\n\n const threshold = options?.threshold ?? 50;\n\n const isSwiping = signal(false);\n const direction = signal<SwipeDirection>('none');\n const distanceX = signal(0);\n const distanceY = signal(0);\n\n let startX = 0;\n let startY = 0;\n\n listener.passive(target, 'touchstart', (e: TouchEvent) => {\n if (e.touches.length !== 1) {\n return;\n }\n\n startX = e.touches[0].clientX;\n startY = e.touches[0].clientY;\n\n isSwiping.set(false);\n direction.set('none');\n distanceX.set(0);\n distanceY.set(0);\n });\n\n listener.passive(target, 'touchmove', (e: TouchEvent) => {\n if (e.touches.length !== 1) {\n return;\n }\n\n const dx = startX - e.touches[0].clientX;\n const dy = startY - e.touches[0].clientY;\n\n distanceX.set(dx);\n distanceY.set(dy);\n\n const absDx = Math.abs(dx);\n const absDy = Math.abs(dy);\n\n if (Math.max(absDx, absDy) >= threshold) {\n if (!isSwiping()) {\n isSwiping.set(true);\n }\n\n if (absDx > absDy) {\n direction.set(dx > 0 ? 'left' : 'right');\n } else {\n direction.set(dy > 0 ? 'up' : 'down');\n }\n }\n });\n\n const onTouchEnd = () => {\n isSwiping.set(false);\n };\n\n listener.passive(target, 'touchend', onTouchEnd);\n listener.passive(target, 'touchcancel', onTouchEnd);\n\n onDisconnect(target, () => {\n isSwiping.set(false);\n direction.set('none');\n distanceX.set(0);\n distanceY.set(0);\n });\n\n return {\n isSwiping: isSwiping.asReadonly(),\n direction: direction.asReadonly(),\n distanceX: distanceX.asReadonly(),\n distanceY: distanceY.asReadonly(),\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-elements-swipe.mjs","sources":["../../../projects/core/elements/swipe/index.ts","../../../projects/core/elements/swipe/signality-core-elements-swipe.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } 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\n/**\n * Possible swipe direction values.\n */\nexport type SwipeDirection = 'up' | 'down' | 'left' | 'right' | 'none';\n\nexport interface SwipeOptions extends WithInjector {\n /**\n * Minimum distance in pixels before a swipe is recognized.\n * @default 50\n */\n readonly threshold?: number;\n}\n\nexport interface SwipeRef {\n /** Whether a swipe gesture is currently in progress. */\n readonly isSwiping: Signal<boolean>;\n\n /** Current swipe direction, or `'none'` if a threshold has not been exceeded. */\n readonly direction: Signal<SwipeDirection>;\n\n /** Horizontal distance from start (positive = swiped left). */\n readonly distanceX: Signal<number>;\n\n /** Vertical distance from start (positive = swiped up). */\n readonly distanceY: Signal<number>;\n}\n\n/**\n * Reactive touch-swipe detection on an element.\n * Uses Touch Events API — for mouse/pen input use PointerSwipe instead.\n * Tracks single-finger swipe gestures and provides direction and distance signals.\n *\n * @param target - Element to detect swipe gestures on\n * @param options - Optional configuration including threshold and injector\n * @returns A SwipeRef with reactive signals for swipe state\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #area>\n * <p>Swiping: {{ sw.isSwiping() }}</p>\n * <p>Direction: {{ sw.direction() }}</p>\n * </div>\n * `\n * })\n * export class SwipeComponent {\n * readonly area = viewChild<ElementRef>('area');\n * readonly sw = swipe(this.area);\n * }\n * ```\n */\nexport function swipe(target: MaybeElementSignal<HTMLElement>, options?: SwipeOptions): SwipeRef {\n const { runInContext } = setupContext(options?.injector, swipe);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return {\n isSwiping: constSignal(false),\n direction: constSignal('none'),\n distanceX: constSignal(0),\n distanceY: constSignal(0),\n };\n }\n\n const threshold = options?.threshold ?? 50;\n\n const isSwiping = signal(false);\n const direction = signal<SwipeDirection>('none');\n const distanceX = signal(0);\n const distanceY = signal(0);\n\n let startX = 0;\n let startY = 0;\n\n listener.passive(target, 'touchstart', (e: TouchEvent) => {\n if (e.touches.length !== 1) {\n return;\n }\n\n startX = e.touches[0].clientX;\n startY = e.touches[0].clientY;\n\n isSwiping.set(false);\n direction.set('none');\n distanceX.set(0);\n distanceY.set(0);\n });\n\n listener.passive(target, 'touchmove', (e: TouchEvent) => {\n if (e.touches.length !== 1) {\n return;\n }\n\n const dx = startX - e.touches[0].clientX;\n const dy = startY - e.touches[0].clientY;\n\n distanceX.set(dx);\n distanceY.set(dy);\n\n const absDx = Math.abs(dx);\n const absDy = Math.abs(dy);\n\n if (Math.max(absDx, absDy) >= threshold) {\n if (!isSwiping()) {\n isSwiping.set(true);\n }\n\n if (absDx > absDy) {\n direction.set(dx > 0 ? 'left' : 'right');\n } else {\n direction.set(dy > 0 ? 'up' : 'down');\n }\n }\n });\n\n const onTouchEnd = () => {\n isSwiping.set(false);\n };\n\n listener.passive(target, 'touchend', onTouchEnd);\n listener.passive(target, 'touchcancel', onTouchEnd);\n\n onDisconnect(target, () => {\n isSwiping.set(false);\n direction.set('none');\n distanceX.set(0);\n distanceY.set(0);\n });\n\n return {\n isSwiping: isSwiping.asReadonly(),\n direction: direction.asReadonly(),\n distanceX: distanceX.asReadonly(),\n distanceY: distanceY.asReadonly(),\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAiCA;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;AACG,SAAU,KAAK,CAAC,MAAuC,EAAE,OAAsB,EAAA;AACnF,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC;AAE/D,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;YACZ,OAAO;AACL,gBAAA,SAAS,EAAE,WAAW,CAAC,KAAK,CAAC;AAC7B,gBAAA,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC;AAC9B,gBAAA,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;AACzB,gBAAA,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;aAC1B;QACH;AAEA,QAAA,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,EAAE;AAE1C,QAAA,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,qDAAC;AAC/B,QAAA,MAAM,SAAS,GAAG,MAAM,CAAiB,MAAM,qDAAC;AAChD,QAAA,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,qDAAC;AAC3B,QAAA,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,qDAAC;QAE3B,IAAI,MAAM,GAAG,CAAC;QACd,IAAI,MAAM,GAAG,CAAC;QAEd,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,CAAa,KAAI;YACvD,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;gBAC1B;YACF;YAEA,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO;YAC7B,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO;AAE7B,YAAA,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;AACpB,YAAA,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC;AACrB,YAAA,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AAChB,YAAA,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AAClB,QAAA,CAAC,CAAC;QAEF,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAa,KAAI;YACtD,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;gBAC1B;YACF;AAEA,YAAA,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO;AACxC,YAAA,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO;AAExC,YAAA,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;AACjB,YAAA,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;YAEjB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAE1B,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,SAAS,EAAE;AACvC,gBAAA,IAAI,CAAC,SAAS,EAAE,EAAE;AAChB,oBAAA,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;gBACrB;AAEA,gBAAA,IAAI,KAAK,GAAG,KAAK,EAAE;AACjB,oBAAA,SAAS,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,MAAM,GAAG,OAAO,CAAC;gBAC1C;qBAAO;AACL,oBAAA,SAAS,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC;gBACvC;YACF;AACF,QAAA,CAAC,CAAC;QAEF,MAAM,UAAU,GAAG,MAAK;AACtB,YAAA,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;AACtB,QAAA,CAAC;QAED,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC;QAChD,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC;AAEnD,QAAA,YAAY,CAAC,MAAM,EAAE,MAAK;AACxB,YAAA,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;AACpB,YAAA,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC;AACrB,YAAA,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AAChB,YAAA,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AAClB,QAAA,CAAC,CAAC;QAEF,OAAO;AACL,YAAA,SAAS,EAAE,SAAS,CAAC,UAAU,EAAE;AACjC,YAAA,SAAS,EAAE,SAAS,CAAC,UAAU,EAAE;AACjC,YAAA,SAAS,EAAE,SAAS,CAAC,UAAU,EAAE;AACjC,YAAA,SAAS,EAAE,SAAS,CAAC,UAAU,EAAE;SAClC;AACH,IAAA,CAAC,CAAC;AACJ;;AC/IA;;AAEG;;;;"}
@@ -12,8 +12,6 @@ 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';
17
15
 
18
16
  /**
19
17
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements.mjs","sources":["../../../projects/core/elements/signality-core-elements.ts"],"sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA;;AAEG"}
1
+ {"version":3,"file":"signality-core-elements.mjs","sources":["../../../projects/core/elements/signality-core-elements.ts"],"sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA;;AAEG"}