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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/browser/battery/index.d.ts +27 -2
  2. package/browser/bluetooth/index.d.ts +40 -13
  3. package/browser/breakpoints/index.d.ts +22 -9
  4. package/browser/broadcast-channel/index.d.ts +1 -1
  5. package/browser/browser-language/index.d.ts +1 -1
  6. package/browser/clipboard/index.d.ts +22 -6
  7. package/browser/device-posture/index.d.ts +23 -2
  8. package/browser/display-media/index.d.ts +34 -22
  9. package/browser/eye-dropper/index.d.ts +22 -6
  10. package/browser/favicon/index.d.ts +29 -2
  11. package/browser/file-dialog/index.d.ts +97 -0
  12. package/browser/fps/index.d.ts +1 -1
  13. package/browser/fullscreen/index.d.ts +78 -0
  14. package/browser/gamepad/index.d.ts +39 -9
  15. package/browser/geolocation/index.d.ts +44 -13
  16. package/browser/index.d.ts +8 -1
  17. package/browser/input-modality/index.d.ts +1 -1
  18. package/browser/listener/index.d.ts +1 -1
  19. package/browser/media-query/index.d.ts +1 -1
  20. package/browser/network/index.d.ts +37 -9
  21. package/browser/online/index.d.ts +1 -1
  22. package/browser/page-visibility/index.d.ts +1 -1
  23. package/browser/permission-state/index.d.ts +23 -0
  24. package/browser/picture-in-picture/index.d.ts +24 -6
  25. package/browser/screen-orientation/index.d.ts +1 -1
  26. package/browser/speech-recognition/index.d.ts +51 -13
  27. package/browser/speech-synthesis/index.d.ts +82 -42
  28. package/browser/storage/index.d.ts +1 -1
  29. package/browser/text-direction/index.d.ts +2 -5
  30. package/{elements → browser}/text-selection/index.d.ts +1 -1
  31. package/browser/vibration/index.d.ts +38 -9
  32. package/browser/wake-lock/index.d.ts +51 -12
  33. package/browser/web-notification/index.d.ts +35 -9
  34. package/browser/web-share/index.d.ts +19 -5
  35. package/browser/web-worker/index.d.ts +35 -11
  36. package/browser/window-focus/index.d.ts +27 -0
  37. package/{elements → browser}/window-size/index.d.ts +6 -7
  38. package/elements/active-element/index.d.ts +1 -1
  39. package/elements/dropzone/index.d.ts +60 -10
  40. package/elements/element-focus/index.d.ts +1 -1
  41. package/elements/element-focus-within/index.d.ts +1 -1
  42. package/elements/element-hover/index.d.ts +1 -1
  43. package/elements/element-size/index.d.ts +8 -5
  44. package/elements/element-visibility/index.d.ts +25 -7
  45. package/elements/index.d.ts +0 -2
  46. package/elements/mouse-position/index.d.ts +27 -7
  47. package/elements/on-click-outside/index.d.ts +1 -1
  48. package/elements/on-disconnect/index.d.ts +1 -1
  49. package/elements/on-long-press/index.d.ts +1 -1
  50. package/elements/pointer-swipe/index.d.ts +1 -1
  51. package/elements/scroll-position/index.d.ts +2 -2
  52. package/elements/swipe/index.d.ts +1 -1
  53. package/fesm2022/signality-core-browser-battery.mjs +1 -1
  54. package/fesm2022/signality-core-browser-battery.mjs.map +1 -1
  55. package/fesm2022/signality-core-browser-bluetooth.mjs +28 -27
  56. package/fesm2022/signality-core-browser-bluetooth.mjs.map +1 -1
  57. package/fesm2022/signality-core-browser-breakpoints.mjs +19 -10
  58. package/fesm2022/signality-core-browser-breakpoints.mjs.map +1 -1
  59. package/fesm2022/signality-core-browser-broadcast-channel.mjs +1 -1
  60. package/fesm2022/signality-core-browser-broadcast-channel.mjs.map +1 -1
  61. package/fesm2022/signality-core-browser-browser-language.mjs +1 -1
  62. package/fesm2022/signality-core-browser-browser-language.mjs.map +1 -1
  63. package/fesm2022/signality-core-browser-clipboard.mjs +1 -1
  64. package/fesm2022/signality-core-browser-clipboard.mjs.map +1 -1
  65. package/fesm2022/signality-core-browser-device-posture.mjs +13 -0
  66. package/fesm2022/signality-core-browser-device-posture.mjs.map +1 -1
  67. package/fesm2022/signality-core-browser-display-media.mjs +4 -17
  68. package/fesm2022/signality-core-browser-display-media.mjs.map +1 -1
  69. package/fesm2022/signality-core-browser-eye-dropper.mjs +1 -1
  70. package/fesm2022/signality-core-browser-eye-dropper.mjs.map +1 -1
  71. package/fesm2022/signality-core-browser-favicon.mjs +2 -2
  72. package/fesm2022/signality-core-browser-favicon.mjs.map +1 -1
  73. package/fesm2022/signality-core-browser-file-dialog.mjs +109 -0
  74. package/fesm2022/signality-core-browser-file-dialog.mjs.map +1 -0
  75. package/fesm2022/signality-core-browser-fps.mjs +1 -1
  76. package/fesm2022/signality-core-browser-fps.mjs.map +1 -1
  77. package/fesm2022/signality-core-browser-fullscreen.mjs +113 -0
  78. package/fesm2022/signality-core-browser-fullscreen.mjs.map +1 -0
  79. package/fesm2022/signality-core-browser-gamepad.mjs +14 -4
  80. package/fesm2022/signality-core-browser-gamepad.mjs.map +1 -1
  81. package/fesm2022/signality-core-browser-geolocation.mjs +8 -19
  82. package/fesm2022/signality-core-browser-geolocation.mjs.map +1 -1
  83. package/fesm2022/signality-core-browser-input-modality.mjs +1 -1
  84. package/fesm2022/signality-core-browser-input-modality.mjs.map +1 -1
  85. package/fesm2022/signality-core-browser-listener.mjs +18 -6
  86. package/fesm2022/signality-core-browser-listener.mjs.map +1 -1
  87. package/fesm2022/signality-core-browser-media-query.mjs +1 -1
  88. package/fesm2022/signality-core-browser-media-query.mjs.map +1 -1
  89. package/fesm2022/signality-core-browser-network.mjs +2 -2
  90. package/fesm2022/signality-core-browser-network.mjs.map +1 -1
  91. package/fesm2022/signality-core-browser-online.mjs +1 -1
  92. package/fesm2022/signality-core-browser-online.mjs.map +1 -1
  93. package/fesm2022/signality-core-browser-page-visibility.mjs +1 -1
  94. package/fesm2022/signality-core-browser-page-visibility.mjs.map +1 -1
  95. package/fesm2022/signality-core-browser-permission-state.mjs +57 -0
  96. package/fesm2022/signality-core-browser-permission-state.mjs.map +1 -0
  97. package/fesm2022/signality-core-browser-picture-in-picture.mjs +30 -13
  98. package/fesm2022/signality-core-browser-picture-in-picture.mjs.map +1 -1
  99. package/fesm2022/signality-core-browser-screen-orientation.mjs +1 -1
  100. package/fesm2022/signality-core-browser-screen-orientation.mjs.map +1 -1
  101. package/fesm2022/signality-core-browser-speech-recognition.mjs +7 -19
  102. package/fesm2022/signality-core-browser-speech-recognition.mjs.map +1 -1
  103. package/fesm2022/signality-core-browser-speech-synthesis.mjs +14 -16
  104. package/fesm2022/signality-core-browser-speech-synthesis.mjs.map +1 -1
  105. package/fesm2022/signality-core-browser-storage.mjs +1 -1
  106. package/fesm2022/signality-core-browser-storage.mjs.map +1 -1
  107. package/fesm2022/signality-core-browser-text-direction.mjs +1 -4
  108. package/fesm2022/signality-core-browser-text-direction.mjs.map +1 -1
  109. package/fesm2022/{signality-core-elements-text-selection.mjs → signality-core-browser-text-selection.mjs} +2 -2
  110. package/fesm2022/signality-core-browser-text-selection.mjs.map +1 -0
  111. package/fesm2022/signality-core-browser-vibration.mjs +14 -5
  112. package/fesm2022/signality-core-browser-vibration.mjs.map +1 -1
  113. package/fesm2022/signality-core-browser-wake-lock.mjs +33 -16
  114. package/fesm2022/signality-core-browser-wake-lock.mjs.map +1 -1
  115. package/fesm2022/signality-core-browser-web-notification.mjs +5 -7
  116. package/fesm2022/signality-core-browser-web-notification.mjs.map +1 -1
  117. package/fesm2022/signality-core-browser-web-share.mjs +3 -5
  118. package/fesm2022/signality-core-browser-web-share.mjs.map +1 -1
  119. package/fesm2022/signality-core-browser-web-worker.mjs +6 -3
  120. package/fesm2022/signality-core-browser-web-worker.mjs.map +1 -1
  121. package/fesm2022/signality-core-browser-window-focus.mjs +48 -0
  122. package/fesm2022/signality-core-browser-window-focus.mjs.map +1 -0
  123. package/fesm2022/{signality-core-elements-window-size.mjs → signality-core-browser-window-size.mjs} +4 -24
  124. package/fesm2022/signality-core-browser-window-size.mjs.map +1 -0
  125. package/fesm2022/signality-core-browser.mjs +8 -1
  126. package/fesm2022/signality-core-browser.mjs.map +1 -1
  127. package/fesm2022/signality-core-elements-active-element.mjs +1 -1
  128. package/fesm2022/signality-core-elements-active-element.mjs.map +1 -1
  129. package/fesm2022/signality-core-elements-dropzone.mjs +28 -29
  130. package/fesm2022/signality-core-elements-dropzone.mjs.map +1 -1
  131. package/fesm2022/signality-core-elements-element-focus-within.mjs +1 -1
  132. package/fesm2022/signality-core-elements-element-focus-within.mjs.map +1 -1
  133. package/fesm2022/signality-core-elements-element-focus.mjs +1 -1
  134. package/fesm2022/signality-core-elements-element-focus.mjs.map +1 -1
  135. package/fesm2022/signality-core-elements-element-hover.mjs +1 -1
  136. package/fesm2022/signality-core-elements-element-hover.mjs.map +1 -1
  137. package/fesm2022/signality-core-elements-element-size.mjs +19 -24
  138. package/fesm2022/signality-core-elements-element-size.mjs.map +1 -1
  139. package/fesm2022/signality-core-elements-element-visibility.mjs +2 -2
  140. package/fesm2022/signality-core-elements-element-visibility.mjs.map +1 -1
  141. package/fesm2022/signality-core-elements-mouse-position.mjs +3 -3
  142. package/fesm2022/signality-core-elements-mouse-position.mjs.map +1 -1
  143. package/fesm2022/signality-core-elements-on-click-outside.mjs +1 -1
  144. package/fesm2022/signality-core-elements-on-click-outside.mjs.map +1 -1
  145. package/fesm2022/signality-core-elements-on-disconnect.mjs +1 -1
  146. package/fesm2022/signality-core-elements-on-disconnect.mjs.map +1 -1
  147. package/fesm2022/signality-core-elements-on-long-press.mjs +1 -1
  148. package/fesm2022/signality-core-elements-on-long-press.mjs.map +1 -1
  149. package/fesm2022/signality-core-elements-pointer-swipe.mjs +1 -1
  150. package/fesm2022/signality-core-elements-pointer-swipe.mjs.map +1 -1
  151. package/fesm2022/signality-core-elements-scroll-position.mjs +2 -2
  152. package/fesm2022/signality-core-elements-scroll-position.mjs.map +1 -1
  153. package/fesm2022/signality-core-elements-swipe.mjs +1 -1
  154. package/fesm2022/signality-core-elements-swipe.mjs.map +1 -1
  155. package/fesm2022/signality-core-elements.mjs +0 -2
  156. package/fesm2022/signality-core-elements.mjs.map +1 -1
  157. package/fesm2022/signality-core-internal.mjs +54 -7
  158. package/fesm2022/signality-core-internal.mjs.map +1 -1
  159. package/fesm2022/signality-core-observers-intersection-observer.mjs +3 -2
  160. package/fesm2022/signality-core-observers-intersection-observer.mjs.map +1 -1
  161. package/fesm2022/signality-core-observers-mutation-observer.mjs +3 -2
  162. package/fesm2022/signality-core-observers-mutation-observer.mjs.map +1 -1
  163. package/fesm2022/signality-core-observers-resize-observer.mjs +3 -2
  164. package/fesm2022/signality-core-observers-resize-observer.mjs.map +1 -1
  165. package/fesm2022/signality-core-observers.mjs +0 -1
  166. package/fesm2022/signality-core-observers.mjs.map +1 -1
  167. package/fesm2022/signality-core-reactivity-debounced.mjs.map +1 -1
  168. package/fesm2022/signality-core-reactivity-throttled.mjs.map +1 -1
  169. package/fesm2022/signality-core-reactivity-watcher.mjs.map +1 -1
  170. package/fesm2022/signality-core-router-fragment.mjs +1 -1
  171. package/fesm2022/signality-core-router-fragment.mjs.map +1 -1
  172. package/fesm2022/signality-core-router-params.mjs +1 -1
  173. package/fesm2022/signality-core-router-params.mjs.map +1 -1
  174. package/fesm2022/signality-core-router-query-params.mjs.map +1 -1
  175. package/fesm2022/signality-core-router-route-data.mjs +1 -1
  176. package/fesm2022/signality-core-router-route-data.mjs.map +1 -1
  177. package/fesm2022/signality-core-router-router-listener.mjs.map +1 -1
  178. package/fesm2022/signality-core-router-title.mjs +1 -1
  179. package/fesm2022/signality-core-router-title.mjs.map +1 -1
  180. package/fesm2022/signality-core-router-url.mjs +1 -1
  181. package/fesm2022/signality-core-router-url.mjs.map +1 -1
  182. package/fesm2022/signality-core-scheduling-debounce-callback.mjs +1 -1
  183. package/fesm2022/signality-core-scheduling-debounce-callback.mjs.map +1 -1
  184. package/fesm2022/signality-core-scheduling-interval.mjs +27 -72
  185. package/fesm2022/signality-core-scheduling-interval.mjs.map +1 -1
  186. package/internal/utils/assert.d.ts +2 -0
  187. package/internal/utils/dom/index.d.ts +1 -0
  188. package/internal/utils/dom/is-element.d.ts +1 -1
  189. package/internal/utils/dom/is-event-target.d.ts +1 -0
  190. package/internal/utils/files/index.d.ts +1 -0
  191. package/internal/utils/files/is-accepted-file.d.ts +11 -0
  192. package/internal/utils/index.d.ts +3 -0
  193. package/internal/utils/to-element.d.ts +1 -1
  194. package/internal/utils/unref-element.d.ts +2 -0
  195. package/observers/index.d.ts +0 -1
  196. package/observers/intersection-observer/index.d.ts +22 -1
  197. package/observers/mutation-observer/index.d.ts +43 -1
  198. package/observers/resize-observer/index.d.ts +13 -1
  199. package/package.json +25 -17
  200. package/reactivity/debounced/index.d.ts +2 -2
  201. package/reactivity/throttled/index.d.ts +2 -2
  202. package/reactivity/watcher/index.d.ts +2 -2
  203. package/router/fragment/index.d.ts +1 -1
  204. package/router/params/index.d.ts +1 -1
  205. package/router/query-params/index.d.ts +5 -3
  206. package/router/route-data/index.d.ts +1 -1
  207. package/router/router-listener/index.d.ts +1 -1
  208. package/router/title/index.d.ts +1 -1
  209. package/router/url/index.d.ts +1 -1
  210. package/scheduling/debounce-callback/index.d.ts +1 -1
  211. package/scheduling/interval/index.d.ts +19 -27
  212. package/browser/pointer-lock-element/index.d.ts +0 -22
  213. package/fesm2022/signality-core-browser-pointer-lock-element.mjs +0 -43
  214. package/fesm2022/signality-core-browser-pointer-lock-element.mjs.map +0 -1
  215. package/fesm2022/signality-core-elements-text-selection.mjs.map +0 -1
  216. package/fesm2022/signality-core-elements-window-size.mjs.map +0 -1
  217. package/fesm2022/signality-core-observers-performance-observer.mjs +0 -84
  218. package/fesm2022/signality-core-observers-performance-observer.mjs.map +0 -1
  219. package/observers/performance-observer/index.d.ts +0 -58
@@ -2,7 +2,7 @@ import { signal, untracked } from '@angular/core';
2
2
  import { setupContext, constSignal, NOOP_FN, toValue } from '@signality/core/internal';
3
3
 
4
4
  /**
5
- * Signal-based wrapper around the Notifications API.
5
+ * Signal-based wrapper around the [Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API).
6
6
  *
7
7
  * Tracks the current notification as a signal. Calling `show()` auto-closes
8
8
  * the previous notification. Calling `close()` closes the current one.
@@ -23,7 +23,7 @@ import { setupContext, constSignal, NOOP_FN, toValue } from '@signality/core/int
23
23
  * }
24
24
  * `
25
25
  * })
26
- * class NotificationComponent {
26
+ * export class NotificationDemo {
27
27
  * readonly notif = webNotification({ icon: '/icon.png' });
28
28
  *
29
29
  * async requestPermission() {
@@ -54,10 +54,8 @@ function webNotification(options) {
54
54
  const notification = signal(null, ...(ngDevMode ? [{ debugName: "notification" }] : []));
55
55
  let autoCloseTimeout;
56
56
  const clearAutoClose = () => {
57
- if (autoCloseTimeout) {
58
- clearTimeout(autoCloseTimeout);
59
- autoCloseTimeout = undefined;
60
- }
57
+ clearTimeout(autoCloseTimeout);
58
+ autoCloseTimeout = undefined;
61
59
  };
62
60
  const requestPermission = async () => {
63
61
  try {
@@ -74,7 +72,7 @@ function webNotification(options) {
74
72
  };
75
73
  const close = () => {
76
74
  clearAutoClose();
77
- const current = untracked(notification);
75
+ const current = notification();
78
76
  if (current) {
79
77
  try {
80
78
  current.onclose = null;
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-web-notification.mjs","sources":["../../../projects/core/browser/web-notification/index.ts","../../../projects/core/browser/web-notification/signality-core-browser-web-notification.ts"],"sourcesContent":["import { type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_FN, setupContext, type Timer, toValue } from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\n\nexport interface WebNotificationOptions extends NotificationOptions, WithInjector {\n /**\n * Auto-close notification after specified milliseconds.\n */\n readonly autoClose?: MaybeSignal<number>;\n}\n\nexport interface WebNotificationRef {\n /** Whether Notifications API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Current permission state */\n readonly permission: Signal<NotificationPermission>;\n\n /** Current active notification instance */\n readonly notification: Signal<Notification | null>;\n\n /** Request notification permission */\n readonly requestPermission: () => Promise<NotificationPermission>;\n\n /** Show a notification (auto-closes previous). Per-call options override defaults from constructor. */\n readonly show: (title: string, options?: NotificationOptions) => Notification | undefined;\n\n /** Close the current notification */\n readonly close: () => void;\n}\n\n/**\n * Signal-based wrapper around the Notifications API.\n *\n * Tracks the current notification as a signal. Calling `show()` auto-closes\n * the previous notification. Calling `close()` closes the current one.\n *\n * @param options - Optional configuration and default notification options\n * @returns A WebNotificationRef with notification state and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <button (click)=\"requestPermission()\">Request Permission</button>\n * <button (click)=\"showNotification()\" [disabled]=\"notif.permission() !== 'granted'\">\n * Show Notification\n * </button>\n * @if (notif.notification()) {\n * <button (click)=\"notif.close()\">Close</button>\n * }\n * `\n * })\n * class NotificationComponent {\n * readonly notif = webNotification({ icon: '/icon.png' });\n *\n * async requestPermission() {\n * await this.notif.requestPermission();\n * }\n *\n * showNotification() {\n * this.notif.show('Hello!', { body: 'This is a notification' });\n * }\n * }\n * ```\n */\nexport function webNotification(options?: WebNotificationOptions): WebNotificationRef {\n const { runInContext } = setupContext(options?.injector, webNotification);\n\n return runInContext(({ isBrowser, onCleanup }) => {\n const isSupported = constSignal(isBrowser && 'Notification' in window);\n\n if (!isSupported()) {\n return {\n isSupported,\n permission: constSignal('denied'),\n notification: constSignal(null),\n requestPermission: async () => 'denied',\n show: () => undefined,\n close: NOOP_FN,\n };\n }\n\n const permission = signal(Notification.permission);\n const notification = signal<Notification | null>(null);\n\n let autoCloseTimeout: Timer;\n\n const clearAutoClose = () => {\n if (autoCloseTimeout) {\n clearTimeout(autoCloseTimeout);\n autoCloseTimeout = undefined;\n }\n };\n\n const requestPermission = async (): Promise<NotificationPermission> => {\n try {\n const result = await Notification.requestPermission();\n permission.set(result);\n return result;\n } catch (error) {\n if (ngDevMode) {\n console.warn(`[webNotification] Failed to request notification permission.`, error);\n }\n return permission();\n }\n };\n\n const close = (): void => {\n clearAutoClose();\n\n const current = untracked(notification);\n\n if (current) {\n try {\n current.onclose = null;\n current.close();\n } catch (error) {\n if (ngDevMode) {\n console.warn(`[webNotification] Failed to close notification.`, error);\n }\n }\n notification.set(null);\n }\n };\n\n const show = (title: string, overrides?: NotificationOptions): Notification | undefined => {\n return untracked(() => {\n if (permission() !== 'granted') {\n return undefined;\n }\n\n close();\n\n try {\n const { autoClose, ...defaults } = options ?? {};\n const mergedOptions = { ...defaults, ...overrides };\n\n const instance = new Notification(title, mergedOptions);\n\n instance.onclose = () => {\n clearAutoClose();\n notification.set(null);\n };\n\n notification.set(instance);\n\n const autoCloseMs = toValue(autoClose);\n if (autoCloseMs && autoCloseMs > 0) {\n autoCloseTimeout = setTimeout(close, autoCloseMs);\n }\n\n return instance;\n } catch (error) {\n if (ngDevMode) {\n console.warn(`[webNotification] Failed to show notification.`, error);\n }\n return undefined;\n }\n });\n };\n\n onCleanup(close);\n\n return {\n isSupported,\n permission: permission.asReadonly(),\n notification: notification.asReadonly(),\n requestPermission,\n show,\n close,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"signality-core-browser-web-notification.mjs","sources":["../../../projects/core/browser/web-notification/index.ts","../../../projects/core/browser/web-notification/signality-core-browser-web-notification.ts"],"sourcesContent":["import { type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_FN, setupContext, type Timer, toValue } from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\n\nexport interface WebNotificationOptions extends NotificationOptions, WithInjector {\n /**\n * Auto-close the notification after the specified number of milliseconds.\n *\n * @see [Notification: close() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Notification/close)\n */\n readonly autoClose?: MaybeSignal<number>;\n}\n\nexport interface WebNotificationRef {\n /**\n * Whether the Notifications API is supported in the current browser.\n *\n * @see [Notifications API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Current notification permission state: `'granted'`, `'denied'`, or `'default'`.\n *\n * @see [Notification: permission static property on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Notification/permission_static)\n */\n readonly permission: Signal<NotificationPermission>;\n\n /**\n * The currently active notification instance, or `null` if none is shown.\n *\n * @see [Notification on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Notification)\n */\n readonly notification: Signal<Notification | null>;\n\n /**\n * Request permission to show notifications.\n *\n * @see [Notification: requestPermission() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Notification/requestPermission_static)\n */\n readonly requestPermission: () => Promise<NotificationPermission>;\n\n /**\n * Show a notification. Automatically closes the previous one. Per-call options override constructor defaults.\n *\n * @see [Notification() constructor on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification)\n */\n readonly show: (title: string, options?: NotificationOptions) => Notification | undefined;\n\n /**\n * Close the currently active notification.\n *\n * @see [Notification: close() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Notification/close)\n */\n readonly close: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API).\n *\n * Tracks the current notification as a signal. Calling `show()` auto-closes\n * the previous notification. Calling `close()` closes the current one.\n *\n * @param options - Optional configuration and default notification options\n * @returns A WebNotificationRef with notification state and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <button (click)=\"requestPermission()\">Request Permission</button>\n * <button (click)=\"showNotification()\" [disabled]=\"notif.permission() !== 'granted'\">\n * Show Notification\n * </button>\n * @if (notif.notification()) {\n * <button (click)=\"notif.close()\">Close</button>\n * }\n * `\n * })\n * export class NotificationDemo {\n * readonly notif = webNotification({ icon: '/icon.png' });\n *\n * async requestPermission() {\n * await this.notif.requestPermission();\n * }\n *\n * showNotification() {\n * this.notif.show('Hello!', { body: 'This is a notification' });\n * }\n * }\n * ```\n */\nexport function webNotification(options?: WebNotificationOptions): WebNotificationRef {\n const { runInContext } = setupContext(options?.injector, webNotification);\n\n return runInContext(({ isBrowser, onCleanup }) => {\n const isSupported = constSignal(isBrowser && 'Notification' in window);\n\n if (!isSupported()) {\n return {\n isSupported,\n permission: constSignal('denied'),\n notification: constSignal(null),\n requestPermission: async () => 'denied',\n show: () => undefined,\n close: NOOP_FN,\n };\n }\n\n const permission = signal(Notification.permission);\n const notification = signal<Notification | null>(null);\n\n let autoCloseTimeout: Timer;\n\n const clearAutoClose = () => {\n clearTimeout(autoCloseTimeout);\n autoCloseTimeout = undefined;\n };\n\n const requestPermission = async (): Promise<NotificationPermission> => {\n try {\n const result = await Notification.requestPermission();\n permission.set(result);\n return result;\n } catch (error) {\n if (ngDevMode) {\n console.warn(`[webNotification] Failed to request notification permission.`, error);\n }\n return permission();\n }\n };\n\n const close = (): void => {\n clearAutoClose();\n\n const current = notification();\n\n if (current) {\n try {\n current.onclose = null;\n current.close();\n } catch (error) {\n if (ngDevMode) {\n console.warn(`[webNotification] Failed to close notification.`, error);\n }\n }\n notification.set(null);\n }\n };\n\n const show = (title: string, overrides?: NotificationOptions): Notification | undefined => {\n return untracked(() => {\n if (permission() !== 'granted') {\n return undefined;\n }\n\n close();\n\n try {\n const { autoClose, ...defaults } = options ?? {};\n const mergedOptions = { ...defaults, ...overrides };\n\n const instance = new Notification(title, mergedOptions);\n\n instance.onclose = () => {\n clearAutoClose();\n notification.set(null);\n };\n\n notification.set(instance);\n\n const autoCloseMs = toValue(autoClose);\n if (autoCloseMs && autoCloseMs > 0) {\n autoCloseTimeout = setTimeout(close, autoCloseMs);\n }\n\n return instance;\n } catch (error) {\n if (ngDevMode) {\n console.warn(`[webNotification] Failed to show notification.`, error);\n }\n return undefined;\n }\n });\n };\n\n onCleanup(close);\n\n return {\n isSupported,\n permission: permission.asReadonly(),\n notification: notification.asReadonly(),\n requestPermission,\n show,\n close,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -18,7 +18,7 @@ import { setupContext, constSignal, NOOP_ASYNC_FN } from '@signality/core/intern
18
18
  * }
19
19
  * `
20
20
  * })
21
- * class WebShareComponent {
21
+ * export class WebShareDemo {
22
22
  * readonly webShare = webShare();
23
23
  *
24
24
  * async shareContent() {
@@ -54,10 +54,8 @@ function webShare(options) {
54
54
  catch (error) {
55
55
  // user canceled, or share failed
56
56
  // AbortError is expected when a user cancels
57
- if (error.name !== 'AbortError') {
58
- if (ngDevMode) {
59
- console.warn(`[webShare] Failed to share content.`, error);
60
- }
57
+ if (ngDevMode && error.name !== 'AbortError') {
58
+ console.warn(`[webShare] Failed to share content.`, error);
61
59
  }
62
60
  }
63
61
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-web-share.mjs","sources":["../../../projects/core/browser/web-share/index.ts","../../../projects/core/browser/web-share/signality-core-browser-web-share.ts"],"sourcesContent":["import { type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_ASYNC_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\n\nexport interface WebShareRef {\n /** Whether Web Share API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Whether the share dialog is currently open */\n readonly isSharing: Signal<boolean>;\n\n /** Open the native share dialog */\n readonly share: (data: ShareData) => Promise<void>;\n\n /** Check if data can be shared */\n readonly canShare: (data?: ShareData) => boolean;\n}\n\n/**\n * Signal-based wrapper around the [Web Share API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Share_API).\n *\n * @param options - Optional injector for DI context\n * @returns A WebShareRef with share methods and state signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (webShare.isSupported()) {\n * <button (click)=\"shareContent()\" [disabled]=\"webShare.isSharing()\">\n * Share This Page\n * </button>\n * }\n * `\n * })\n * class WebShareComponent {\n * readonly webShare = webShare();\n *\n * async shareContent() {\n * await this.webShare.share({\n * title: 'Check this out!',\n * url: window.location.href,\n * });\n * }\n * }\n * ```\n */\nexport function webShare(options?: WithInjector): WebShareRef {\n const { runInContext } = setupContext(options?.injector, webShare);\n\n return runInContext(({ isBrowser }) => {\n const isSupported = constSignal(isBrowser && 'share' in navigator);\n\n if (!isSupported()) {\n return {\n isSupported,\n isSharing: constSignal(false),\n canShare: () => false,\n share: NOOP_ASYNC_FN,\n };\n }\n\n const isSharing = signal(false);\n\n const share = async (data: ShareData): Promise<void> => {\n if (untracked(isSharing)) {\n return;\n }\n\n try {\n isSharing.set(true);\n await navigator.share(data);\n } catch (error) {\n // user canceled, or share failed\n // AbortError is expected when a user cancels\n if ((error as Error).name !== 'AbortError') {\n if (ngDevMode) {\n console.warn(`[webShare] Failed to share content.`, error);\n }\n }\n } finally {\n isSharing.set(false);\n }\n };\n\n const canShare = (data?: ShareData): boolean => {\n try {\n return navigator.canShare(data);\n } catch (error) {\n if (ngDevMode) {\n console.warn(`[webShare] Failed to check if content can be shared.`, error);\n }\n return false;\n }\n };\n\n return {\n isSupported,\n isSharing: isSharing.asReadonly(),\n canShare,\n share,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAkBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BG;AACG,SAAU,QAAQ,CAAC,OAAsB,EAAA;AAC7C,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAElE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAI;QACpC,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,IAAI,OAAO,IAAI,SAAS,CAAC;AAElE,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,SAAS,EAAE,WAAW,CAAC,KAAK,CAAC;AAC7B,gBAAA,QAAQ,EAAE,MAAM,KAAK;AACrB,gBAAA,KAAK,EAAE,aAAa;aACrB;QACH;AAEA,QAAA,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,qDAAC;AAE/B,QAAA,MAAM,KAAK,GAAG,OAAO,IAAe,KAAmB;AACrD,YAAA,IAAI,SAAS,CAAC,SAAS,CAAC,EAAE;gBACxB;YACF;AAEA,YAAA,IAAI;AACF,gBAAA,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;AACnB,gBAAA,MAAM,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;YAC7B;YAAE,OAAO,KAAK,EAAE;;;AAGd,gBAAA,IAAK,KAAe,CAAC,IAAI,KAAK,YAAY,EAAE;oBAC1C,IAAI,SAAS,EAAE;AACb,wBAAA,OAAO,CAAC,IAAI,CAAC,qCAAqC,EAAE,KAAK,CAAC;oBAC5D;gBACF;YACF;oBAAU;AACR,gBAAA,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;YACtB;AACF,QAAA,CAAC;AAED,QAAA,MAAM,QAAQ,GAAG,CAAC,IAAgB,KAAa;AAC7C,YAAA,IAAI;AACF,gBAAA,OAAO,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjC;YAAE,OAAO,KAAK,EAAE;gBACd,IAAI,SAAS,EAAE;AACb,oBAAA,OAAO,CAAC,IAAI,CAAC,sDAAsD,EAAE,KAAK,CAAC;gBAC7E;AACA,gBAAA,OAAO,KAAK;YACd;AACF,QAAA,CAAC;QAED,OAAO;YACL,WAAW;AACX,YAAA,SAAS,EAAE,SAAS,CAAC,UAAU,EAAE;YACjC,QAAQ;YACR,KAAK;SACN;AACH,IAAA,CAAC,CAAC;AACJ;;ACvGA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-browser-web-share.mjs","sources":["../../../projects/core/browser/web-share/index.ts","../../../projects/core/browser/web-share/signality-core-browser-web-share.ts"],"sourcesContent":["import { type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_ASYNC_FN, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\n\nexport interface WebShareRef {\n /**\n * Whether the Web Share API is supported in the current browser.\n *\n * @see [Web Share API browser compatibility on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Web_Share_API#browser_compatibility)\n */\n readonly isSupported: Signal<boolean>;\n\n /**\n * Whether the native share dialog is currently open.\n */\n readonly isSharing: Signal<boolean>;\n\n /**\n * Open the native share dialog with the provided data.\n *\n * @see [Navigator: share() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share)\n */\n readonly share: (data: ShareData) => Promise<void>;\n\n /**\n * Check whether the given data can be shared via the Web Share API.\n *\n * @see [Navigator: canShare() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/canShare)\n */\n readonly canShare: (data?: ShareData) => boolean;\n}\n\n/**\n * Signal-based wrapper around the [Web Share API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Share_API).\n *\n * @param options - Optional injector for DI context\n * @returns A WebShareRef with share methods and state signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (webShare.isSupported()) {\n * <button (click)=\"shareContent()\" [disabled]=\"webShare.isSharing()\">\n * Share This Page\n * </button>\n * }\n * `\n * })\n * export class WebShareDemo {\n * readonly webShare = webShare();\n *\n * async shareContent() {\n * await this.webShare.share({\n * title: 'Check this out!',\n * url: window.location.href,\n * });\n * }\n * }\n * ```\n */\nexport function webShare(options?: WithInjector): WebShareRef {\n const { runInContext } = setupContext(options?.injector, webShare);\n\n return runInContext(({ isBrowser }) => {\n const isSupported = constSignal(isBrowser && 'share' in navigator);\n\n if (!isSupported()) {\n return {\n isSupported,\n isSharing: constSignal(false),\n canShare: () => false,\n share: NOOP_ASYNC_FN,\n };\n }\n\n const isSharing = signal(false);\n\n const share = async (data: ShareData): Promise<void> => {\n if (untracked(isSharing)) {\n return;\n }\n\n try {\n isSharing.set(true);\n await navigator.share(data);\n } catch (error) {\n // user canceled, or share failed\n // AbortError is expected when a user cancels\n if (ngDevMode && (error as Error).name !== 'AbortError') {\n console.warn(`[webShare] Failed to share content.`, error);\n }\n } finally {\n isSharing.set(false);\n }\n };\n\n const canShare = (data?: ShareData): boolean => {\n try {\n return navigator.canShare(data);\n } catch (error) {\n if (ngDevMode) {\n console.warn(`[webShare] Failed to check if content can be shared.`, error);\n }\n return false;\n }\n };\n\n return {\n isSupported,\n isSharing: isSharing.asReadonly(),\n canShare,\n share,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAgCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BG;AACG,SAAU,QAAQ,CAAC,OAAsB,EAAA;AAC7C,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAElE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAI;QACpC,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,IAAI,OAAO,IAAI,SAAS,CAAC;AAElE,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,SAAS,EAAE,WAAW,CAAC,KAAK,CAAC;AAC7B,gBAAA,QAAQ,EAAE,MAAM,KAAK;AACrB,gBAAA,KAAK,EAAE,aAAa;aACrB;QACH;AAEA,QAAA,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,qDAAC;AAE/B,QAAA,MAAM,KAAK,GAAG,OAAO,IAAe,KAAmB;AACrD,YAAA,IAAI,SAAS,CAAC,SAAS,CAAC,EAAE;gBACxB;YACF;AAEA,YAAA,IAAI;AACF,gBAAA,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;AACnB,gBAAA,MAAM,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;YAC7B;YAAE,OAAO,KAAK,EAAE;;;gBAGd,IAAI,SAAS,IAAK,KAAe,CAAC,IAAI,KAAK,YAAY,EAAE;AACvD,oBAAA,OAAO,CAAC,IAAI,CAAC,qCAAqC,EAAE,KAAK,CAAC;gBAC5D;YACF;oBAAU;AACR,gBAAA,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;YACtB;AACF,QAAA,CAAC;AAED,QAAA,MAAM,QAAQ,GAAG,CAAC,IAAgB,KAAa;AAC7C,YAAA,IAAI;AACF,gBAAA,OAAO,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjC;YAAE,OAAO,KAAK,EAAE;gBACd,IAAI,SAAS,EAAE;AACb,oBAAA,OAAO,CAAC,IAAI,CAAC,sDAAsD,EAAE,KAAK,CAAC;gBAC7E;AACA,gBAAA,OAAO,KAAK;YACd;AACF,QAAA,CAAC;QAED,OAAO;YACL,WAAW;AACX,YAAA,SAAS,EAAE,SAAS,CAAC,UAAU,EAAE;YACjC,QAAQ;YACR,KAAK;SACN;AACH,IAAA,CAAC,CAAC;AACJ;;ACnHA;;AAEG;;;;"}
@@ -3,9 +3,12 @@ import { setupContext, NOOP_FN, constSignal, toValue } from '@signality/core/int
3
3
  import { watcher } from '@signality/core/reactivity/watcher';
4
4
 
5
5
  /**
6
- * Signal-based wrapper around the Web Workers API.
6
+ * Signal-based wrapper around the [Web Workers API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API).
7
+ * Run scripts in background threads without blocking the main thread.
7
8
  *
8
- * @param url - Worker script URL
9
+ * When `url` is a signal, the worker is automatically recreated whenever the URL changes.
10
+ *
11
+ * @param url - Worker script URL (static or reactive signal)
9
12
  * @param options - Optional configuration
10
13
  * @returns A WebWorkerRef with data signal and control methods
11
14
  *
@@ -19,7 +22,7 @@ import { watcher } from '@signality/core/reactivity/watcher';
19
22
  * }
20
23
  * `
21
24
  * })
22
- * class WorkerComponent {
25
+ * export class WorkerDemo {
23
26
  * readonly worker = webWorker<number, number>('/workers/calc.js');
24
27
  * }
25
28
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser-web-worker.mjs","sources":["../../../projects/core/browser/web-worker/index.ts","../../../projects/core/browser/web-worker/signality-core-browser-web-worker.ts"],"sourcesContent":["import { isSignal, type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_FN, setupContext, toValue } from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\nimport { watcher } from '@signality/core/reactivity/watcher';\n\nexport interface WebWorkerOptions extends WithInjector {\n /**\n * Worker type.\n * @default 'classic'\n */\n readonly type?: 'classic' | 'module';\n\n /**\n * Credentials mode.\n */\n readonly credentials?: RequestCredentials;\n\n /**\n * Worker name for debugging.\n */\n readonly name?: string;\n}\n\nexport interface WebWorkerRef<I, O> {\n /** Last message received from worker */\n readonly data: Signal<O | undefined>;\n\n /** Whether worker is ready */\n readonly isReady: Signal<boolean>;\n\n /** Last error from worker */\n readonly error: Signal<Error | null>;\n\n /** Send message to worker */\n readonly post: (data: I) => void;\n\n /** Terminate the worker */\n readonly terminate: () => void;\n}\n\n/**\n * Signal-based wrapper around the Web Workers API.\n *\n * @param url - Worker script URL\n * @param options - Optional configuration\n * @returns A WebWorkerRef with data signal and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <button (click)=\"worker.post(42)\">Calculate</button>\n * @if (worker.data()) {\n * <p>Result: {{ worker.data() }}</p>\n * }\n * `\n * })\n * class WorkerComponent {\n * readonly worker = webWorker<number, number>('/workers/calc.js');\n * }\n * ```\n */\nexport function webWorker<I, O>(\n url: MaybeSignal<string | URL>,\n options?: WebWorkerOptions\n): WebWorkerRef<I, O> {\n const { runInContext } = setupContext(options?.injector, webWorker);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return {\n data: constSignal(undefined),\n isReady: constSignal(false),\n error: constSignal(null),\n post: NOOP_FN,\n terminate: NOOP_FN,\n };\n }\n\n const type = options?.type ?? 'classic';\n const credentials = options?.credentials;\n const name = options?.name;\n\n const data = signal<O | undefined>(undefined);\n const isReady = signal(false);\n const error = signal<Error | null>(null);\n\n let worker: Worker | null = null;\n\n const createWorker = (workerUrl: string | URL) => {\n if (worker) {\n worker.terminate();\n }\n\n try {\n worker = new Worker(workerUrl, { type, credentials, name });\n\n worker.onmessage = (event: MessageEvent<O>) => {\n data.set(event.data);\n };\n\n worker.onerror = (event: ErrorEvent) => {\n error.set(new Error(event.message));\n };\n\n worker.onmessageerror = () => {\n error.set(new Error('Message deserialization error'));\n };\n\n isReady.set(true);\n } catch (e) {\n error.set(e as Error);\n isReady.set(false);\n }\n };\n\n const post = (message: I) => {\n if (worker && untracked(isReady)) {\n try {\n worker.postMessage(message);\n } catch (e) {\n error.set(e as Error);\n }\n }\n };\n\n const terminate = () => {\n if (worker) {\n worker.terminate();\n worker = null;\n isReady.set(false);\n }\n };\n\n if (isSignal(url)) {\n watcher(url, createWorker);\n }\n\n onCleanup(terminate);\n\n createWorker(toValue(url));\n\n return {\n data: data.asReadonly(),\n isReady: isReady.asReadonly(),\n error: error.asReadonly(),\n post,\n terminate,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"signality-core-browser-web-worker.mjs","sources":["../../../projects/core/browser/web-worker/index.ts","../../../projects/core/browser/web-worker/signality-core-browser-web-worker.ts"],"sourcesContent":["import { isSignal, type Signal, signal, untracked } from '@angular/core';\nimport { constSignal, NOOP_FN, setupContext, toValue } from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\nimport { watcher } from '@signality/core/reactivity/watcher';\n\nexport interface WebWorkerOptions extends WithInjector {\n /**\n * Worker script type.\n *\n * @default 'classic'\n * @see [Worker: Worker() constructor — type on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker#type)\n */\n readonly type?: 'classic' | 'module';\n\n /**\n * Credentials mode used when fetching the worker script.\n *\n * @see [Worker: Worker() constructor — credentials on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker#credentials)\n */\n readonly credentials?: RequestCredentials;\n\n /**\n * Identifying name for the worker, useful for debugging in DevTools.\n *\n * @see [Worker: Worker() constructor — name on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker#name)\n */\n readonly name?: string;\n}\n\nexport interface WebWorkerRef<I, O> {\n /**\n * The last message received from the worker via `postMessage`.\n * `undefined` until the first message is received.\n */\n readonly data: Signal<O | undefined>;\n\n /**\n * Whether the worker has been successfully created and is ready to receive messages.\n */\n readonly isReady: Signal<boolean>;\n\n /**\n * The last error emitted by the worker, or `null` if no error has occurred.\n */\n readonly error: Signal<Error | null>;\n\n /**\n * Send a message to the worker.\n *\n * @see [Worker.postMessage() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage)\n */\n readonly post: (data: I) => void;\n\n /**\n * Terminate the worker immediately, discarding any pending messages.\n *\n * @see [Worker.terminate() on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Worker/terminate)\n */\n readonly terminate: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Web Workers API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API).\n * Run scripts in background threads without blocking the main thread.\n *\n * When `url` is a signal, the worker is automatically recreated whenever the URL changes.\n *\n * @param url - Worker script URL (static or reactive signal)\n * @param options - Optional configuration\n * @returns A WebWorkerRef with data signal and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <button (click)=\"worker.post(42)\">Calculate</button>\n * @if (worker.data()) {\n * <p>Result: {{ worker.data() }}</p>\n * }\n * `\n * })\n * export class WorkerDemo {\n * readonly worker = webWorker<number, number>('/workers/calc.js');\n * }\n * ```\n */\nexport function webWorker<I, O>(\n url: MaybeSignal<string | URL>,\n options?: WebWorkerOptions\n): WebWorkerRef<I, O> {\n const { runInContext } = setupContext(options?.injector, webWorker);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return {\n data: constSignal(undefined),\n isReady: constSignal(false),\n error: constSignal(null),\n post: NOOP_FN,\n terminate: NOOP_FN,\n };\n }\n\n const type = options?.type ?? 'classic';\n const credentials = options?.credentials;\n const name = options?.name;\n\n const data = signal<O | undefined>(undefined);\n const isReady = signal(false);\n const error = signal<Error | null>(null);\n\n let worker: Worker | null = null;\n\n const createWorker = (workerUrl: string | URL) => {\n if (worker) {\n worker.terminate();\n }\n\n try {\n worker = new Worker(workerUrl, { type, credentials, name });\n\n worker.onmessage = (event: MessageEvent<O>) => {\n data.set(event.data);\n };\n\n worker.onerror = (event: ErrorEvent) => {\n error.set(new Error(event.message));\n };\n\n worker.onmessageerror = () => {\n error.set(new Error('Message deserialization error'));\n };\n\n isReady.set(true);\n } catch (e) {\n error.set(e as Error);\n isReady.set(false);\n }\n };\n\n const post = (message: I) => {\n if (worker && untracked(isReady)) {\n try {\n worker.postMessage(message);\n } catch (e) {\n error.set(e as Error);\n }\n }\n };\n\n const terminate = () => {\n if (worker) {\n worker.terminate();\n worker = null;\n isReady.set(false);\n }\n };\n\n if (isSignal(url)) {\n watcher(url, createWorker);\n }\n\n onCleanup(terminate);\n\n createWorker(toValue(url));\n\n return {\n data: data.asReadonly(),\n isReady: isReady.asReadonly(),\n error: error.asReadonly(),\n post,\n terminate,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { signal } from '@angular/core';
2
+ import { setupContext, constSignal, createToken } from '@signality/core/internal';
3
+ import { setupSync, listener } from '@signality/core/browser/listener';
4
+
5
+ /**
6
+ * Signal-based wrapper around the [Window focus/blur events](https://developer.mozilla.org/en-US/docs/Web/API/Window/focus_event).
7
+ *
8
+ * @param options - Optional configuration including signal options and injector
9
+ * @returns A signal containing the current window focus state (`true` if focused, `false` if blurred)
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * @Component({
14
+ * template: `
15
+ * @if (isFocused()) {
16
+ * <p>Window is focused</p>
17
+ * } @else {
18
+ * <p>Window is not focused</p>
19
+ * }
20
+ * `
21
+ * })
22
+ * export class FocusDemo {
23
+ * readonly isFocused = windowFocus();
24
+ * }
25
+ * ```
26
+ */
27
+ function windowFocus(options) {
28
+ const { runInContext } = setupContext(options?.injector, windowFocus);
29
+ return runInContext(({ isServer }) => {
30
+ if (isServer) {
31
+ return constSignal(true);
32
+ }
33
+ const isFocused = signal(document.hasFocus(), options);
34
+ setupSync(() => {
35
+ listener(window, 'focus', () => isFocused.set(true));
36
+ listener(window, 'blur', () => isFocused.set(false));
37
+ });
38
+ return isFocused.asReadonly();
39
+ });
40
+ }
41
+ const WINDOW_FOCUS = /* @__PURE__ */ createToken(windowFocus);
42
+
43
+ /**
44
+ * Generated bundle index. Do not edit.
45
+ */
46
+
47
+ export { WINDOW_FOCUS, windowFocus };
48
+ //# sourceMappingURL=signality-core-browser-window-focus.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-browser-window-focus.mjs","sources":["../../../projects/core/browser/window-focus/index.ts","../../../projects/core/browser/window-focus/signality-core-browser-window-focus.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type WindowFocusOptions = CreateSignalOptions<boolean> & WithInjector;\n\n/**\n * Signal-based wrapper around the [Window focus/blur events](https://developer.mozilla.org/en-US/docs/Web/API/Window/focus_event).\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current window focus state (`true` if focused, `false` if blurred)\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (isFocused()) {\n * <p>Window is focused</p>\n * } @else {\n * <p>Window is not focused</p>\n * }\n * `\n * })\n * export class FocusDemo {\n * readonly isFocused = windowFocus();\n * }\n * ```\n */\nexport function windowFocus(options?: WindowFocusOptions): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, windowFocus);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(true);\n }\n\n const isFocused = signal(document.hasFocus(), options);\n\n setupSync(() => {\n listener(window, 'focus', () => isFocused.set(true));\n listener(window, 'blur', () => isFocused.set(false));\n });\n\n return isFocused.asReadonly();\n });\n}\n\nexport const WINDOW_FOCUS = /* @__PURE__ */ createToken(windowFocus);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAOA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,WAAW,CAAC,OAA4B,EAAA;AACtD,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,IAAI,CAAC;QAC1B;QAEA,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,OAAO,CAAC;QAEtD,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACpD,YAAA,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACtD,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,SAAS,CAAC,UAAU,EAAE;AAC/B,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,YAAY,mBAAmB,WAAW,CAAC,WAAW;;AChDnE;;AAEG;;;;"}
@@ -22,24 +22,15 @@ import { mediaQuery } from '@signality/core/browser/media-query';
22
22
  * </div>
23
23
  * `
24
24
  * })
25
- * class WindowSizeComponent {
25
+ * export class WindowSizeComponent {
26
26
  * readonly size = windowSize();
27
27
  * }
28
28
  * ```
29
29
  */
30
30
  function windowSize(options) {
31
31
  const { runInContext } = setupContext(options?.injector, windowSize);
32
+ const initialValue = options?.initialValue ?? DEFAULT_VALUE;
32
33
  return runInContext(({ isServer }) => {
33
- const initialValue = options?.initialValue
34
- ? {
35
- width: options.initialValue.width,
36
- height: options.initialValue.height,
37
- innerWidth: options.initialValue.width,
38
- innerHeight: options.initialValue.height,
39
- outerWidth: options.initialValue.width,
40
- outerHeight: options.initialValue.height,
41
- }
42
- : DEFAULT_VALUE;
43
34
  if (isServer) {
44
35
  return constSignal(initialValue);
45
36
  }
@@ -48,14 +39,7 @@ function windowSize(options) {
48
39
  const update = () => {
49
40
  const width = includeScrollbar ? window.innerWidth : document.documentElement.clientWidth;
50
41
  const height = includeScrollbar ? window.innerHeight : document.documentElement.clientHeight;
51
- size.set({
52
- width,
53
- height,
54
- innerWidth: window.innerWidth,
55
- innerHeight: window.innerHeight,
56
- outerWidth: window.outerWidth,
57
- outerHeight: window.outerHeight,
58
- });
42
+ size.set({ width, height });
59
43
  };
60
44
  listener(window, 'resize', update);
61
45
  watcher(mediaQuery('(orientation: portrait)'), update);
@@ -67,10 +51,6 @@ const WINDOW_SIZE = /* @__PURE__ */ createToken(windowSize);
67
51
  const DEFAULT_VALUE = {
68
52
  width: 0,
69
53
  height: 0,
70
- innerWidth: 0,
71
- innerHeight: 0,
72
- outerWidth: 0,
73
- outerHeight: 0,
74
54
  };
75
55
 
76
56
  /**
@@ -78,4 +58,4 @@ const DEFAULT_VALUE = {
78
58
  */
79
59
 
80
60
  export { WINDOW_SIZE, windowSize };
81
- //# sourceMappingURL=signality-core-elements-window-size.mjs.map
61
+ //# sourceMappingURL=signality-core-browser-window-size.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signality-core-browser-window-size.mjs","sources":["../../../projects/core/browser/window-size/index.ts","../../../projects/core/browser/window-size/signality-core-browser-window-size.ts"],"sourcesContent":["import { afterNextRender, type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\nimport { watcher } from '@signality/core/reactivity/watcher';\nimport { mediaQuery } from '@signality/core/browser/media-query';\n\nexport interface WindowSizeOptions extends CreateSignalOptions<WindowSizeValue>, WithInjector {\n /**\n * Include scrollbar in dimensions calculation.\n *\n * @default false\n */\n readonly includeScrollbar?: boolean;\n\n /**\n * Initial value for SSR and before the first measurement.\n *\n * @default { width: 0, height: 0 }\n */\n readonly initialValue?: WindowSizeValue;\n}\n\nexport interface WindowSizeValue {\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Signal-based wrapper around the [Window API](https://developer.mozilla.org/en-US/docs/Web/API/Window) dimensions.\n *\n * @param options - Optional configuration including initialValue for SSR\n * @returns A signal containing the current window dimensions\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div>\n * Window: {{ size().width }} × {{ size().height }}px\n * @if (size().width < 768) {\n * <p>Mobile view</p>\n * }\n * </div>\n * `\n * })\n * export class WindowSizeComponent {\n * readonly size = windowSize();\n * }\n * ```\n */\nexport function windowSize(options?: WindowSizeOptions): Signal<WindowSizeValue> {\n const { runInContext } = setupContext(options?.injector, windowSize);\n const initialValue = options?.initialValue ?? DEFAULT_VALUE;\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(initialValue);\n }\n\n const includeScrollbar = options?.includeScrollbar ?? false;\n\n const size = signal<WindowSizeValue>(initialValue, options);\n\n const update = () => {\n const width = includeScrollbar ? window.innerWidth : document.documentElement.clientWidth;\n const height = includeScrollbar ? window.innerHeight : document.documentElement.clientHeight;\n\n size.set({ width, height });\n };\n\n listener(window, 'resize', update);\n\n watcher(mediaQuery('(orientation: portrait)'), update);\n\n afterNextRender({ read: update });\n\n return size.asReadonly();\n });\n}\n\nexport const WINDOW_SIZE = /* @__PURE__ */ createToken(windowSize);\n\nconst DEFAULT_VALUE: WindowSizeValue = {\n width: 0,\n height: 0,\n};\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;AA4BA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG,SAAU,UAAU,CAAC,OAA2B,EAAA;AACpD,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC;AACpE,IAAA,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,aAAa;AAE3D,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,YAAY,CAAC;QAClC;AAEA,QAAA,MAAM,gBAAgB,GAAG,OAAO,EAAE,gBAAgB,IAAI,KAAK;QAE3D,MAAM,IAAI,GAAG,MAAM,CAAkB,YAAY,EAAE,OAAO,CAAC;QAE3D,MAAM,MAAM,GAAG,MAAK;AAClB,YAAA,MAAM,KAAK,GAAG,gBAAgB,GAAG,MAAM,CAAC,UAAU,GAAG,QAAQ,CAAC,eAAe,CAAC,WAAW;AACzF,YAAA,MAAM,MAAM,GAAG,gBAAgB,GAAG,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC,eAAe,CAAC,YAAY;YAE5F,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC7B,QAAA,CAAC;AAED,QAAA,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC;QAElC,OAAO,CAAC,UAAU,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;AAEtD,QAAA,eAAe,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAEjC,QAAA,OAAO,IAAI,CAAC,UAAU,EAAE;AAC1B,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,WAAW,mBAAmB,WAAW,CAAC,UAAU;AAEjE,MAAM,aAAa,GAAoB;AACrC,IAAA,KAAK,EAAE,CAAC;AACR,IAAA,MAAM,EAAE,CAAC;CACV;;ACtFD;;AAEG;;;;"}
@@ -7,8 +7,10 @@ export * from '@signality/core/browser/clipboard';
7
7
  export * from '@signality/core/browser/device-posture';
8
8
  export * from '@signality/core/browser/display-media';
9
9
  export * from '@signality/core/browser/eye-dropper';
10
+ export * from '@signality/core/browser/file-dialog';
10
11
  export * from '@signality/core/browser/favicon';
11
12
  export * from '@signality/core/browser/fps';
13
+ export * from '@signality/core/browser/fullscreen';
12
14
  export * from '@signality/core/browser/gamepad';
13
15
  export * from '@signality/core/browser/geolocation';
14
16
  export * from '@signality/core/browser/input-modality';
@@ -16,17 +18,22 @@ export * from '@signality/core/browser/listener';
16
18
  export * from '@signality/core/browser/media-query';
17
19
  export * from '@signality/core/browser/network';
18
20
  export * from '@signality/core/browser/online';
21
+ export * from '@signality/core/browser/permission-state';
19
22
  export * from '@signality/core/browser/page-visibility';
20
23
  export * from '@signality/core/browser/picture-in-picture';
21
- export * from '@signality/core/browser/web-share';
24
+ export * from '@signality/core/browser/screen-orientation';
22
25
  export * from '@signality/core/browser/speech-recognition';
23
26
  export * from '@signality/core/browser/speech-synthesis';
24
27
  export * from '@signality/core/browser/storage';
25
28
  export * from '@signality/core/browser/text-direction';
29
+ export * from '@signality/core/browser/text-selection';
26
30
  export * from '@signality/core/browser/vibration';
27
31
  export * from '@signality/core/browser/wake-lock';
28
32
  export * from '@signality/core/browser/web-notification';
33
+ export * from '@signality/core/browser/web-share';
29
34
  export * from '@signality/core/browser/web-worker';
35
+ export * from '@signality/core/browser/window-focus';
36
+ export * from '@signality/core/browser/window-size';
30
37
 
31
38
  /**
32
39
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-browser.mjs","sources":["../../../projects/core/browser/signality-core-browser.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-browser.mjs","sources":["../../../projects/core/browser/signality-core-browser.ts"],"sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;AAEG"}
@@ -20,7 +20,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
20
20
  * }
21
21
  * `
22
22
  * })
23
- * class ActiveElementComponent {
23
+ * export class ActiveElementDemo {
24
24
  * readonly activeEl = activeElement();
25
25
  * }
26
26
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-active-element.mjs","sources":["../../../projects/core/elements/active-element/index.ts","../../../projects/core/elements/active-element/signality-core-elements-active-element.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport {\n constSignal,\n createToken,\n getActiveElement,\n getEventTarget,\n getShadowRoot,\n setupContext,\n} from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, type ListenerRef, setupSync } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport type ActiveElementOptions = CreateSignalOptions<Element | null> & WithInjector;\n\n/**\n * Signal-based wrapper around the [document.activeElement](https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement) property.\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the currently active element, or null\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (activeEl(); as el) {\n * <p>Active element: {{ el.tagName }}</p>\n * } @else {\n * <p>No element is active</p>\n * }\n * `\n * })\n * class ActiveElementComponent {\n * readonly activeEl = activeElement();\n * }\n * ```\n */\nexport function activeElement(options?: ActiveElementOptions): Signal<Element | null> {\n const { runInContext } = setupContext(options?.injector, activeElement);\n\n return runInContext(({ isServer, injector }) => {\n if (isServer) {\n return constSignal(null);\n }\n\n const activeEl = signal<Element | null>(getActiveElement(document), options);\n\n let shadowFocusinListener: ListenerRef | null;\n let shadowFocusoutListener: ListenerRef | null;\n\n const updateActiveElement = () => {\n activeEl.set(getActiveElement(document));\n };\n\n const cleanupShadowListeners = () => {\n shadowFocusinListener?.destroy();\n shadowFocusinListener = null;\n shadowFocusoutListener?.destroy();\n shadowFocusoutListener = null;\n };\n\n const setupShadowListeners = (target: Element | null) => {\n cleanupShadowListeners();\n\n if (!target) {\n return;\n }\n\n const shadowRoot = getShadowRoot(target);\n\n if (!shadowRoot) {\n return;\n }\n\n shadowFocusinListener = listener(shadowRoot, 'focusin', updateActiveElement, { injector });\n\n shadowFocusoutListener = listener(\n shadowRoot,\n 'focusout',\n () => {\n setTimeout(() => {\n updateActiveElement();\n setupShadowListeners(activeEl());\n });\n },\n { injector }\n );\n };\n\n setupSync(() => {\n listener.capture(window, 'focusin', (e: FocusEvent) => {\n updateActiveElement();\n setupShadowListeners(getEventTarget(e));\n });\n\n listener.capture(window, 'focusout', (e: FocusEvent) => {\n if (e.relatedTarget === null) {\n updateActiveElement();\n }\n });\n });\n\n onDisconnect(activeEl, () => {\n cleanupShadowListeners();\n updateActiveElement();\n });\n\n return activeEl.asReadonly();\n });\n}\n\nexport const ACTIVE_ELEMENT = /* @__PURE__ */ createToken(activeElement);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAeA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,aAAa,CAAC,OAA8B,EAAA;AAC1D,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC;IAEvE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAI;QAC7C,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,IAAI,CAAC;QAC1B;QAEA,MAAM,QAAQ,GAAG,MAAM,CAAiB,gBAAgB,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;AAE5E,QAAA,IAAI,qBAAyC;AAC7C,QAAA,IAAI,sBAA0C;QAE9C,MAAM,mBAAmB,GAAG,MAAK;YAC/B,QAAQ,CAAC,GAAG,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;AAC1C,QAAA,CAAC;QAED,MAAM,sBAAsB,GAAG,MAAK;YAClC,qBAAqB,EAAE,OAAO,EAAE;YAChC,qBAAqB,GAAG,IAAI;YAC5B,sBAAsB,EAAE,OAAO,EAAE;YACjC,sBAAsB,GAAG,IAAI;AAC/B,QAAA,CAAC;AAED,QAAA,MAAM,oBAAoB,GAAG,CAAC,MAAsB,KAAI;AACtD,YAAA,sBAAsB,EAAE;YAExB,IAAI,CAAC,MAAM,EAAE;gBACX;YACF;AAEA,YAAA,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC;YAExC,IAAI,CAAC,UAAU,EAAE;gBACf;YACF;AAEA,YAAA,qBAAqB,GAAG,QAAQ,CAAC,UAAU,EAAE,SAAS,EAAE,mBAAmB,EAAE,EAAE,QAAQ,EAAE,CAAC;YAE1F,sBAAsB,GAAG,QAAQ,CAC/B,UAAU,EACV,UAAU,EACV,MAAK;gBACH,UAAU,CAAC,MAAK;AACd,oBAAA,mBAAmB,EAAE;AACrB,oBAAA,oBAAoB,CAAC,QAAQ,EAAE,CAAC;AAClC,gBAAA,CAAC,CAAC;AACJ,YAAA,CAAC,EACD,EAAE,QAAQ,EAAE,CACb;AACH,QAAA,CAAC;QAED,SAAS,CAAC,MAAK;YACb,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAa,KAAI;AACpD,gBAAA,mBAAmB,EAAE;AACrB,gBAAA,oBAAoB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;AACzC,YAAA,CAAC,CAAC;YAEF,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAa,KAAI;AACrD,gBAAA,IAAI,CAAC,CAAC,aAAa,KAAK,IAAI,EAAE;AAC5B,oBAAA,mBAAmB,EAAE;gBACvB;AACF,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;AAEF,QAAA,YAAY,CAAC,QAAQ,EAAE,MAAK;AAC1B,YAAA,sBAAsB,EAAE;AACxB,YAAA,mBAAmB,EAAE;AACvB,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,QAAQ,CAAC,UAAU,EAAE;AAC9B,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,cAAc,mBAAmB,WAAW,CAAC,aAAa;;AC/GvE;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-elements-active-element.mjs","sources":["../../../projects/core/elements/active-element/index.ts","../../../projects/core/elements/active-element/signality-core-elements-active-element.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport {\n constSignal,\n createToken,\n getActiveElement,\n getEventTarget,\n getShadowRoot,\n setupContext,\n} from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, type ListenerRef, setupSync } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport type ActiveElementOptions = CreateSignalOptions<Element | null> & WithInjector;\n\n/**\n * Signal-based wrapper around the [document.activeElement](https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement) property.\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the currently active element, or null\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (activeEl(); as el) {\n * <p>Active element: {{ el.tagName }}</p>\n * } @else {\n * <p>No element is active</p>\n * }\n * `\n * })\n * export class ActiveElementDemo {\n * readonly activeEl = activeElement();\n * }\n * ```\n */\nexport function activeElement(options?: ActiveElementOptions): Signal<Element | null> {\n const { runInContext } = setupContext(options?.injector, activeElement);\n\n return runInContext(({ isServer, injector }) => {\n if (isServer) {\n return constSignal(null);\n }\n\n const activeEl = signal<Element | null>(getActiveElement(document), options);\n\n let shadowFocusinListener: ListenerRef | null;\n let shadowFocusoutListener: ListenerRef | null;\n\n const updateActiveElement = () => {\n activeEl.set(getActiveElement(document));\n };\n\n const cleanupShadowListeners = () => {\n shadowFocusinListener?.destroy();\n shadowFocusinListener = null;\n shadowFocusoutListener?.destroy();\n shadowFocusoutListener = null;\n };\n\n const setupShadowListeners = (target: Element | null) => {\n cleanupShadowListeners();\n\n if (!target) {\n return;\n }\n\n const shadowRoot = getShadowRoot(target);\n\n if (!shadowRoot) {\n return;\n }\n\n shadowFocusinListener = listener(shadowRoot, 'focusin', updateActiveElement, { injector });\n\n shadowFocusoutListener = listener(\n shadowRoot,\n 'focusout',\n () => {\n setTimeout(() => {\n updateActiveElement();\n setupShadowListeners(activeEl());\n });\n },\n { injector }\n );\n };\n\n setupSync(() => {\n listener.capture(window, 'focusin', (e: FocusEvent) => {\n updateActiveElement();\n setupShadowListeners(getEventTarget(e));\n });\n\n listener.capture(window, 'focusout', (e: FocusEvent) => {\n if (e.relatedTarget === null) {\n updateActiveElement();\n }\n });\n });\n\n onDisconnect(activeEl, () => {\n cleanupShadowListeners();\n updateActiveElement();\n });\n\n return activeEl.asReadonly();\n });\n}\n\nexport const ACTIVE_ELEMENT = /* @__PURE__ */ createToken(activeElement);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAeA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,aAAa,CAAC,OAA8B,EAAA;AAC1D,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC;IAEvE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAI;QAC7C,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,IAAI,CAAC;QAC1B;QAEA,MAAM,QAAQ,GAAG,MAAM,CAAiB,gBAAgB,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;AAE5E,QAAA,IAAI,qBAAyC;AAC7C,QAAA,IAAI,sBAA0C;QAE9C,MAAM,mBAAmB,GAAG,MAAK;YAC/B,QAAQ,CAAC,GAAG,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;AAC1C,QAAA,CAAC;QAED,MAAM,sBAAsB,GAAG,MAAK;YAClC,qBAAqB,EAAE,OAAO,EAAE;YAChC,qBAAqB,GAAG,IAAI;YAC5B,sBAAsB,EAAE,OAAO,EAAE;YACjC,sBAAsB,GAAG,IAAI;AAC/B,QAAA,CAAC;AAED,QAAA,MAAM,oBAAoB,GAAG,CAAC,MAAsB,KAAI;AACtD,YAAA,sBAAsB,EAAE;YAExB,IAAI,CAAC,MAAM,EAAE;gBACX;YACF;AAEA,YAAA,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC;YAExC,IAAI,CAAC,UAAU,EAAE;gBACf;YACF;AAEA,YAAA,qBAAqB,GAAG,QAAQ,CAAC,UAAU,EAAE,SAAS,EAAE,mBAAmB,EAAE,EAAE,QAAQ,EAAE,CAAC;YAE1F,sBAAsB,GAAG,QAAQ,CAC/B,UAAU,EACV,UAAU,EACV,MAAK;gBACH,UAAU,CAAC,MAAK;AACd,oBAAA,mBAAmB,EAAE;AACrB,oBAAA,oBAAoB,CAAC,QAAQ,EAAE,CAAC;AAClC,gBAAA,CAAC,CAAC;AACJ,YAAA,CAAC,EACD,EAAE,QAAQ,EAAE,CACb;AACH,QAAA,CAAC;QAED,SAAS,CAAC,MAAK;YACb,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAa,KAAI;AACpD,gBAAA,mBAAmB,EAAE;AACrB,gBAAA,oBAAoB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;AACzC,YAAA,CAAC,CAAC;YAEF,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAa,KAAI;AACrD,gBAAA,IAAI,CAAC,CAAC,aAAa,KAAK,IAAI,EAAE;AAC5B,oBAAA,mBAAmB,EAAE;gBACvB;AACF,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;AAEF,QAAA,YAAY,CAAC,QAAQ,EAAE,MAAK;AAC1B,YAAA,sBAAsB,EAAE;AACxB,YAAA,mBAAmB,EAAE;AACvB,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,QAAQ,CAAC,UAAU,EAAE;AAC9B,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,cAAc,mBAAmB,WAAW,CAAC,aAAa;;AC/GvE;;AAEG;;;;"}
@@ -1,5 +1,5 @@
1
1
  import { signal, isSignal } from '@angular/core';
2
- import { setupContext, constSignal, toValue, toElement, isNodeWithin } from '@signality/core/internal';
2
+ import { setupContext, constSignal, toValue, isAcceptedFile, toElement, isNodeWithin } from '@signality/core/internal';
3
3
  import { listener } from '@signality/core/browser/listener';
4
4
  import { onDisconnect } from '@signality/core/elements/on-disconnect';
5
5
  import { watcher } from '@signality/core/reactivity/watcher';
@@ -31,9 +31,9 @@ import { watcher } from '@signality/core/reactivity/watcher';
31
31
  * </div>
32
32
  * `
33
33
  * })
34
- * class DropzoneComponent {
34
+ * export class DropzoneDemo {
35
35
  * readonly zone = viewChild<ElementRef>('zone');
36
- * readonly drop = dropzone(this.zone, { accept: ['image/*'], multiple: true });
36
+ * readonly drop = dropzone(this.zone, { accept: 'image/*', multiple: true });
37
37
  * }
38
38
  * ```
39
39
  */
@@ -47,39 +47,38 @@ function dropzone(target, options) {
47
47
  isDragging: constSignal(false),
48
48
  };
49
49
  }
50
- const accept = options?.accept ?? ['*'];
50
+ const accept = options?.accept ?? '*';
51
51
  const multiple = options?.multiple ?? true;
52
52
  const preventDocumentDrop = options?.preventDocumentDrop ?? true;
53
+ const validatorFn = options?.validator;
54
+ const onReject = options?.onReject;
53
55
  const files = signal([], ...(ngDevMode ? [{ debugName: "files" }] : []));
54
56
  const isOver = signal(false, ...(ngDevMode ? [{ debugName: "isOver" }] : []));
55
57
  const isDragging = signal(false, ...(ngDevMode ? [{ debugName: "isDragging" }] : []));
56
58
  let dragCounter = 0;
57
- const isAcceptedType = (file) => {
58
- const acceptTypes = toValue(accept);
59
- if (acceptTypes.includes('*')) {
60
- return true;
61
- }
62
- return acceptTypes.some(type => {
63
- if (type.endsWith('/*')) {
64
- const prefix = type.slice(0, -1);
65
- return file.type.startsWith(prefix);
66
- }
67
- return file.type === type;
68
- });
69
- };
70
- const filterFiles = (files) => {
71
- const isMultiple = toValue(multiple);
72
- const result = [];
73
- for (let i = 0; i < files.length; i++) {
74
- const file = files[i];
75
- if (isAcceptedType(file)) {
76
- result.push(file);
77
- if (!isMultiple) {
59
+ const processFiles = (files) => {
60
+ const accepted = [];
61
+ const rejected = [];
62
+ const acceptValue = toValue(accept);
63
+ const multipleValue = toValue(multiple);
64
+ const isAccepted = validatorFn
65
+ ? validatorFn
66
+ : (file) => isAcceptedFile(file, acceptValue);
67
+ for (const file of files) {
68
+ if (isAccepted(file)) {
69
+ accepted.push(file);
70
+ if (!multipleValue) {
78
71
  break;
79
72
  }
80
73
  }
74
+ else {
75
+ rejected.push(file);
76
+ }
77
+ }
78
+ if (onReject && rejected.length > 0) {
79
+ onReject(rejected);
81
80
  }
82
- return result;
81
+ return accepted;
83
82
  };
84
83
  listener.prevent.capture(target, 'dragenter', () => {
85
84
  dragCounter++;
@@ -101,8 +100,8 @@ function dropzone(target, options) {
101
100
  isOver.set(false);
102
101
  isDragging.set(false);
103
102
  if (e.dataTransfer && e.dataTransfer.files.length > 0) {
104
- const filtered = filterFiles(Array.from(e.dataTransfer.files));
105
- files.set(filtered);
103
+ const arr = Array.from(e.dataTransfer.files);
104
+ files.set(processFiles(arr));
106
105
  }
107
106
  });
108
107
  listener.capture(document, 'dragenter', () => {
@@ -135,7 +134,7 @@ function dropzone(target, options) {
135
134
  }
136
135
  const filters = [accept, multiple].filter(isSignal);
137
136
  if (filters.length) {
138
- watcher(filters, () => files.update(filterFiles));
137
+ watcher(filters, () => files.update(processFiles));
139
138
  }
140
139
  onDisconnect(target, () => {
141
140
  files.set([]);
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-dropzone.mjs","sources":["../../../projects/core/elements/dropzone/index.ts","../../../projects/core/elements/dropzone/signality-core-elements-dropzone.ts"],"sourcesContent":["import { isSignal, type Signal, signal, type WritableSignal } from '@angular/core';\nimport {\n constSignal,\n isNodeWithin,\n setupContext,\n toElement,\n toValue,\n} from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\nimport { watcher } from '@signality/core/reactivity/watcher';\n\nexport interface DropzoneOptions extends WithInjector {\n /**\n * Accepted MIME types.\n * @default ['*']\n */\n readonly accept?: MaybeSignal<string[]>;\n\n /**\n * Allow multiple files.\n * @default true\n */\n readonly multiple?: MaybeSignal<boolean>;\n\n /**\n * Prevent drops outside the zone.\n * @default true\n */\n readonly preventDocumentDrop?: boolean;\n}\n\nexport interface DropzoneRef {\n /** Dropped files */\n readonly files: WritableSignal<File[]>;\n\n /** Whether dragging over the zone */\n readonly isOver: Signal<boolean>;\n\n /** Whether any drag is in progress (document-wide) */\n readonly isDragging: Signal<boolean>;\n}\n\n/**\n * Signal-based wrapper around the [HTML Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API).\n *\n * @param target - Drop zone element\n * @param options - Optional configuration\n * @returns An object with isOver, files, data, and isDragging signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div\n * #zone\n * [class.over]=\"drop.isOver()\"\n * [class.dragging]=\"drop.isDragging()\"\n * >\n * Drop files here\n * @if (drop.files().length > 0) {\n * <ul>\n * @for (file of drop.files(); track file.name) {\n * <li>{{ file.name }} ({{ file.size }} bytes)</li>\n * }\n * </ul>\n * }\n * </div>\n * `\n * })\n * class DropzoneComponent {\n * readonly zone = viewChild<ElementRef>('zone');\n * readonly drop = dropzone(this.zone, { accept: ['image/*'], multiple: true });\n * }\n * ```\n */\nexport function dropzone(\n target: MaybeElementSignal<HTMLElement>,\n options?: DropzoneOptions\n): DropzoneRef {\n const { runInContext } = setupContext(options?.injector, dropzone);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return {\n files: signal([]),\n isOver: constSignal(false),\n isDragging: constSignal(false),\n };\n }\n\n const accept = options?.accept ?? ['*'];\n const multiple = options?.multiple ?? true;\n const preventDocumentDrop = options?.preventDocumentDrop ?? true;\n\n const files = signal<File[]>([]);\n const isOver = signal(false);\n const isDragging = signal(false);\n\n let dragCounter = 0;\n\n const isAcceptedType = (file: File): boolean => {\n const acceptTypes = toValue(accept);\n\n if (acceptTypes.includes('*')) {\n return true;\n }\n\n return acceptTypes.some(type => {\n if (type.endsWith('/*')) {\n const prefix = type.slice(0, -1);\n return file.type.startsWith(prefix);\n }\n return file.type === type;\n });\n };\n\n const filterFiles = (files: File[]): File[] => {\n const isMultiple = toValue(multiple);\n const result: File[] = [];\n\n for (let i = 0; i < files.length; i++) {\n const file = files[i];\n if (isAcceptedType(file)) {\n result.push(file);\n if (!isMultiple) {\n break;\n }\n }\n }\n\n return result;\n };\n\n listener.prevent.capture(target, 'dragenter', () => {\n dragCounter++;\n isOver.set(true);\n });\n\n listener.prevent.capture(target, 'dragleave', () => {\n dragCounter--;\n if (dragCounter === 0) {\n isOver.set(false);\n }\n });\n\n listener.prevent.capture(target, 'dragover', (e: DragEvent) => {\n if (e.dataTransfer) {\n e.dataTransfer.dropEffect = 'copy';\n }\n });\n\n listener.prevent.stop.capture(target, 'drop', (e: DragEvent) => {\n dragCounter = 0;\n isOver.set(false);\n isDragging.set(false);\n\n if (e.dataTransfer && e.dataTransfer.files.length > 0) {\n const filtered = filterFiles(Array.from(e.dataTransfer.files));\n files.set(filtered);\n }\n });\n\n listener.capture(document, 'dragenter', () => {\n isDragging.set(true);\n });\n\n listener.capture(document, 'dragleave', (e: DragEvent) => {\n if (e.relatedTarget === null) {\n isDragging.set(false);\n }\n });\n\n listener.capture(document, 'drop', () => {\n isDragging.set(false);\n });\n\n if (preventDocumentDrop) {\n listener.capture(document, 'dragover', (e: DragEvent) => {\n const el = toElement(target);\n if (el && !isNodeWithin(e.target as Node, el)) {\n e.preventDefault();\n if (e.dataTransfer) {\n e.dataTransfer.dropEffect = 'none';\n }\n }\n });\n\n listener.capture(document, 'drop', (e: DragEvent) => {\n const el = toElement(target);\n if (el && !isNodeWithin(e.target as Node, el)) {\n e.preventDefault();\n }\n });\n }\n\n const filters = [accept, multiple].filter(isSignal) as Signal<any>[];\n\n if (filters.length) {\n watcher(filters, () => files.update(filterFiles));\n }\n\n onDisconnect(target, () => {\n files.set([]);\n isOver.set(false);\n isDragging.set(false);\n });\n\n return {\n files,\n isOver: isOver.asReadonly(),\n isDragging: isDragging.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-dropzone.mjs","sources":["../../../projects/core/elements/dropzone/index.ts","../../../projects/core/elements/dropzone/signality-core-elements-dropzone.ts"],"sourcesContent":["import { isSignal, type Signal, signal, type WritableSignal } from '@angular/core';\nimport {\n constSignal,\n isAcceptedFile,\n isNodeWithin,\n setupContext,\n toElement,\n toValue,\n} from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\nimport { watcher } from '@signality/core/reactivity/watcher';\n\nexport interface DropzoneOptions extends WithInjector {\n /**\n * Comma-separated list of accepted file types, matching the native HTML\n * [`accept`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/accept) attribute format.\n * Supports MIME types (`'image/png'`), wildcards (`'image/*'`), and file extensions (`'.pdf'`).\n * Use `'*'` to accept all file types.\n *\n * @default '*'\n * @see [accept attribute on MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/accept)\n */\n readonly accept?: MaybeSignal<string>;\n\n /**\n * Whether to allow dropping multiple files at once.\n * When `false`, only the first accepted file is kept.\n *\n * @default true\n */\n readonly multiple?: MaybeSignal<boolean>;\n\n /**\n * When `true`, prevents files from being dropped anywhere outside the drop zone\n * by intercepting `dragover` and `drop` events on the document.\n *\n * @default true\n */\n readonly preventDocumentDrop?: boolean;\n\n /**\n * Custom validation predicate called for each dropped file.\n * Return `true` to keep the file, `false` to reject it.\n *\n * When provided, the `accept` option is ignored — the validator\n * takes full responsibility for deciding which files are valid.\n *\n * @example\n * ```typescript\n * dropzone(target, {\n * validator: (file) => file.size <= 5 * 1024 * 1024, // max 5 MB\n * });\n * ```\n */\n readonly validator?: (file: File) => boolean;\n\n /**\n * Callback invoked with files that were rejected during a drop.\n * Called once per drop with the full array of rejected files.\n * Useful for showing toast notifications or validation errors.\n *\n * @example\n * ```typescript\n * dropzone(target, {\n * accept: 'image/*',\n * onReject: (rejected) => {\n * rejected.forEach(f => toast.error(`${f.name} is not an image`));\n * },\n * });\n * ```\n */\n readonly onReject?: (files: File[]) => void;\n}\n\nexport interface DropzoneRef {\n /**\n * List of files dropped onto the zone, filtered by `accept` and `multiple`.\n * A `WritableSignal` — can be reset externally (e.g. after upload).\n *\n * @see [DataTransfer.files on MDN](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/files)\n */\n readonly files: WritableSignal<File[]>;\n\n /**\n * Whether the user is currently dragging over the drop zone.\n */\n readonly isOver: Signal<boolean>;\n\n /**\n * Whether any drag operation is in progress anywhere on the document.\n * Useful for showing a global drop overlay.\n */\n readonly isDragging: Signal<boolean>;\n}\n\n/**\n * Signal-based wrapper around the [HTML Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API).\n *\n * @param target - Drop zone element\n * @param options - Optional configuration\n * @returns An object with isOver, files, data, and isDragging signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div\n * #zone\n * [class.over]=\"drop.isOver()\"\n * [class.dragging]=\"drop.isDragging()\"\n * >\n * Drop files here\n * @if (drop.files().length > 0) {\n * <ul>\n * @for (file of drop.files(); track file.name) {\n * <li>{{ file.name }} ({{ file.size }} bytes)</li>\n * }\n * </ul>\n * }\n * </div>\n * `\n * })\n * export class DropzoneDemo {\n * readonly zone = viewChild<ElementRef>('zone');\n * readonly drop = dropzone(this.zone, { accept: 'image/*', multiple: true });\n * }\n * ```\n */\nexport function dropzone(\n target: MaybeElementSignal<HTMLElement>,\n options?: DropzoneOptions\n): DropzoneRef {\n const { runInContext } = setupContext(options?.injector, dropzone);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return {\n files: signal([]),\n isOver: constSignal(false),\n isDragging: constSignal(false),\n };\n }\n\n const accept = options?.accept ?? '*';\n const multiple = options?.multiple ?? true;\n const preventDocumentDrop = options?.preventDocumentDrop ?? true;\n const validatorFn = options?.validator;\n const onReject = options?.onReject;\n\n const files = signal<File[]>([]);\n const isOver = signal(false);\n const isDragging = signal(false);\n\n let dragCounter = 0;\n\n const processFiles = (files: File[]): File[] => {\n const accepted: File[] = [];\n const rejected: File[] = [];\n const acceptValue = toValue(accept);\n const multipleValue = toValue(multiple);\n\n const isAccepted = validatorFn\n ? validatorFn\n : (file: File) => isAcceptedFile(file, acceptValue);\n\n for (const file of files) {\n if (isAccepted(file)) {\n accepted.push(file);\n if (!multipleValue) {\n break;\n }\n } else {\n rejected.push(file);\n }\n }\n\n if (onReject && rejected.length > 0) {\n onReject(rejected);\n }\n\n return accepted;\n };\n\n listener.prevent.capture(target, 'dragenter', () => {\n dragCounter++;\n isOver.set(true);\n });\n\n listener.prevent.capture(target, 'dragleave', () => {\n dragCounter--;\n if (dragCounter === 0) {\n isOver.set(false);\n }\n });\n\n listener.prevent.capture(target, 'dragover', (e: DragEvent) => {\n if (e.dataTransfer) {\n e.dataTransfer.dropEffect = 'copy';\n }\n });\n\n listener.prevent.stop.capture(target, 'drop', (e: DragEvent) => {\n dragCounter = 0;\n isOver.set(false);\n isDragging.set(false);\n\n if (e.dataTransfer && e.dataTransfer.files.length > 0) {\n const arr = Array.from(e.dataTransfer.files);\n files.set(processFiles(arr));\n }\n });\n\n listener.capture(document, 'dragenter', () => {\n isDragging.set(true);\n });\n\n listener.capture(document, 'dragleave', (e: DragEvent) => {\n if (e.relatedTarget === null) {\n isDragging.set(false);\n }\n });\n\n listener.capture(document, 'drop', () => {\n isDragging.set(false);\n });\n\n if (preventDocumentDrop) {\n listener.capture(document, 'dragover', (e: DragEvent) => {\n const el = toElement(target);\n\n if (el && !isNodeWithin(e.target as Node, el)) {\n e.preventDefault();\n if (e.dataTransfer) {\n e.dataTransfer.dropEffect = 'none';\n }\n }\n });\n\n listener.capture(document, 'drop', (e: DragEvent) => {\n const el = toElement(target);\n\n if (el && !isNodeWithin(e.target as Node, el)) {\n e.preventDefault();\n }\n });\n }\n\n const filters = [accept, multiple].filter(isSignal) as Signal<any>[];\n\n if (filters.length) {\n watcher(filters, () => files.update(processFiles));\n }\n\n onDisconnect(target, () => {\n files.set([]);\n isOver.set(false);\n isDragging.set(false);\n });\n\n return {\n files,\n isOver: isOver.asReadonly(),\n isDragging: isDragging.asReadonly(),\n };\n });\n}\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 FocusWithinComponent {
25
+ * export class FocusWithinDemo {
26
26
  * readonly container = viewChild<ElementRef>('container');
27
27
  * readonly isFocusedWithin = elementFocusWithin(this.container);
28
28
  * }
@@ -1 +1 @@
1
- {"version":3,"file":"signality-core-elements-element-focus-within.mjs","sources":["../../../projects/core/elements/element-focus-within/index.ts","../../../projects/core/elements/element-focus-within/signality-core-elements-element-focus-within.ts"],"sourcesContent":["import { type CreateSignalOptions, signal, type Signal } from '@angular/core';\nimport { constSignal, setupContext, toElement } 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 ElementFocusWithinOptions = CreateSignalOptions<boolean> & WithInjector;\n\n/**\n * Reactive tracking of focus-within state on an element.\n * Detects when focus is inside an element or any of its descendants,\n * analogous to the CSS `:focus-within` pseudo-class.\n *\n * @param target - The element to track focus-within state on\n * @param options - Optional configuration including signal options and injector\n * @returns A signal that is `true` when focus is within the element\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #container [class.focused]=\"isFocusedWithin()\">\n * <input placeholder=\"First\" />\n * <input placeholder=\"Second\" />\n * </div>\n * `\n * })\n * class FocusWithinComponent {\n * readonly container = viewChild<ElementRef>('container');\n * readonly isFocusedWithin = elementFocusWithin(this.container);\n * }\n * ```\n */\nexport function elementFocusWithin(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementFocusWithinOptions\n): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, elementFocusWithin);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(false);\n }\n\n const focused = signal<boolean>(false, options);\n\n listener(target, 'focusin', () => focused.set(true));\n\n listener(target, 'focusout', e => {\n const el = toElement(target);\n\n if (el && e.relatedTarget instanceof Node && el.contains(e.relatedTarget)) {\n return;\n }\n\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":";;;;;AAQA;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;AACG,SAAU,kBAAkB,CAChC,MAAuC,EACvC,OAAmC,EAAA;AAEnC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,kBAAkB,CAAC;AAE5E,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,KAAK,CAAC;QAC3B;QAEA,MAAM,OAAO,GAAG,MAAM,CAAU,KAAK,EAAE,OAAO,CAAC;AAE/C,QAAA,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAEpD,QAAA,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,IAAG;AAC/B,YAAA,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC;AAE5B,YAAA,IAAI,EAAE,IAAI,CAAC,CAAC,aAAa,YAAY,IAAI,IAAI,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE;gBACzE;YACF;AAEA,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;;AC9DA;;AAEG;;;;"}
1
+ {"version":3,"file":"signality-core-elements-element-focus-within.mjs","sources":["../../../projects/core/elements/element-focus-within/index.ts","../../../projects/core/elements/element-focus-within/signality-core-elements-element-focus-within.ts"],"sourcesContent":["import { type CreateSignalOptions, signal, type Signal } from '@angular/core';\nimport { constSignal, setupContext, toElement } 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 ElementFocusWithinOptions = CreateSignalOptions<boolean> & WithInjector;\n\n/**\n * Reactive tracking of focus-within state on an element.\n * Detects when focus is inside an element or any of its descendants,\n * analogous to the CSS `:focus-within` pseudo-class.\n *\n * @param target - The element to track focus-within state on\n * @param options - Optional configuration including signal options and injector\n * @returns A signal that is `true` when focus is within the element\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #container [class.focused]=\"isFocusedWithin()\">\n * <input placeholder=\"First\" />\n * <input placeholder=\"Second\" />\n * </div>\n * `\n * })\n * export class FocusWithinDemo {\n * readonly container = viewChild<ElementRef>('container');\n * readonly isFocusedWithin = elementFocusWithin(this.container);\n * }\n * ```\n */\nexport function elementFocusWithin(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementFocusWithinOptions\n): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, elementFocusWithin);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(false);\n }\n\n const focused = signal<boolean>(false, options);\n\n listener(target, 'focusin', () => focused.set(true));\n\n listener(target, 'focusout', e => {\n const el = toElement(target);\n\n if (el && e.relatedTarget instanceof Node && el.contains(e.relatedTarget)) {\n return;\n }\n\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":";;;;;AAQA;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;AACG,SAAU,kBAAkB,CAChC,MAAuC,EACvC,OAAmC,EAAA;AAEnC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,kBAAkB,CAAC;AAE5E,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,KAAK,CAAC;QAC3B;QAEA,MAAM,OAAO,GAAG,MAAM,CAAU,KAAK,EAAE,OAAO,CAAC;AAE/C,QAAA,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAEpD,QAAA,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,IAAG;AAC/B,YAAA,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC;AAE5B,YAAA,IAAI,EAAE,IAAI,CAAC,CAAC,aAAa,YAAY,IAAI,IAAI,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE;gBACzE;YACF;AAEA,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;;AC9DA;;AAEG;;;;"}
@@ -21,7 +21,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
21
21
  * }
22
22
  * `
23
23
  * })
24
- * class FocusComponent {
24
+ * export class FocusDemo {
25
25
  * readonly input = viewChild<ElementRef>('input');
26
26
  * readonly isFocused = elementFocus(this.input);
27
27
  * }