@signality/core 0.0.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/browser/battery/index.d.ts +34 -0
- package/browser/bluetooth/index.d.ts +56 -0
- package/browser/breakpoints/index.d.ts +32 -0
- package/browser/broadcast-channel/index.d.ts +42 -0
- package/browser/browser-language/index.d.ts +34 -0
- package/browser/clipboard/index.d.ts +48 -0
- package/browser/device-posture/index.d.ts +18 -0
- package/browser/display-media/index.d.ts +80 -0
- package/browser/eye-dropper/index.d.ts +47 -0
- package/browser/favicon/index.d.ts +39 -0
- package/browser/fps/index.d.ts +46 -0
- package/browser/gamepad/index.d.ts +28 -0
- package/browser/geolocation/index.d.ts +64 -0
- package/browser/index.d.ts +29 -0
- package/browser/input-modality/index.d.ts +26 -0
- package/browser/listener/index.d.ts +61 -0
- package/browser/media-query/index.d.ts +36 -0
- package/browser/network/index.d.ts +44 -0
- package/browser/online/index.d.ts +27 -0
- package/browser/page-visibility/index.d.ts +27 -0
- package/browser/picture-in-picture/index.d.ts +42 -0
- package/browser/pointer-lock-element/index.d.ts +22 -0
- package/browser/screen-orientation/index.d.ts +29 -0
- package/browser/speech-recognition/index.d.ts +77 -0
- package/browser/speech-synthesis/index.d.ts +76 -0
- package/browser/storage/index.d.ts +142 -0
- package/browser/text-direction/index.d.ts +43 -0
- package/browser/vibration/index.d.ts +37 -0
- package/browser/wake-lock/index.d.ts +37 -0
- package/browser/web-notification/index.d.ts +58 -0
- package/browser/web-share/index.d.ts +42 -0
- package/browser/web-worker/index.d.ts +52 -0
- package/elements/active-element/index.d.ts +27 -0
- package/elements/dropzone/index.d.ts +61 -0
- package/elements/element-focus/index.d.ts +38 -0
- package/elements/element-focus-within/index.d.ts +29 -0
- package/elements/element-hover/index.d.ts +27 -0
- package/elements/element-size/index.d.ts +40 -0
- package/elements/element-visibility/index.d.ts +53 -0
- package/elements/index.d.ts +16 -0
- package/elements/mouse-position/index.d.ts +64 -0
- package/elements/on-click-outside/index.d.ts +42 -0
- package/elements/on-disconnect/index.d.ts +45 -0
- package/elements/on-long-press/index.d.ts +44 -0
- package/elements/pointer-swipe/index.d.ts +58 -0
- package/elements/scroll-position/index.d.ts +96 -0
- package/elements/swipe/index.d.ts +49 -0
- package/elements/text-selection/index.d.ts +39 -0
- package/elements/window-size/index.d.ts +46 -0
- package/fesm2022/signality-core-browser-battery.mjs +80 -0
- package/fesm2022/signality-core-browser-battery.mjs.map +1 -0
- package/fesm2022/signality-core-browser-bluetooth.mjs +112 -0
- package/fesm2022/signality-core-browser-bluetooth.mjs.map +1 -0
- package/fesm2022/signality-core-browser-breakpoints.mjs +51 -0
- package/fesm2022/signality-core-browser-breakpoints.mjs.map +1 -0
- package/fesm2022/signality-core-browser-broadcast-channel.mjs +74 -0
- package/fesm2022/signality-core-browser-broadcast-channel.mjs.map +1 -0
- package/fesm2022/signality-core-browser-browser-language.mjs +48 -0
- package/fesm2022/signality-core-browser-browser-language.mjs.map +1 -0
- package/fesm2022/signality-core-browser-clipboard.mjs +102 -0
- package/fesm2022/signality-core-browser-clipboard.mjs.map +1 -0
- package/fesm2022/signality-core-browser-device-posture.mjs +40 -0
- package/fesm2022/signality-core-browser-device-posture.mjs.map +1 -0
- package/fesm2022/signality-core-browser-display-media.mjs +121 -0
- package/fesm2022/signality-core-browser-display-media.mjs.map +1 -0
- package/fesm2022/signality-core-browser-eye-dropper.mjs +82 -0
- package/fesm2022/signality-core-browser-eye-dropper.mjs.map +1 -0
- package/fesm2022/signality-core-browser-favicon.mjs +100 -0
- package/fesm2022/signality-core-browser-favicon.mjs.map +1 -0
- package/fesm2022/signality-core-browser-fps.mjs +103 -0
- package/fesm2022/signality-core-browser-fps.mjs.map +1 -0
- package/fesm2022/signality-core-browser-gamepad.mjs +93 -0
- package/fesm2022/signality-core-browser-gamepad.mjs.map +1 -0
- package/fesm2022/signality-core-browser-geolocation.mjs +120 -0
- package/fesm2022/signality-core-browser-geolocation.mjs.map +1 -0
- package/fesm2022/signality-core-browser-input-modality.mjs +64 -0
- package/fesm2022/signality-core-browser-input-modality.mjs.map +1 -0
- package/fesm2022/signality-core-browser-listener.mjs +132 -0
- package/fesm2022/signality-core-browser-listener.mjs.map +1 -0
- package/fesm2022/signality-core-browser-media-query.mjs +55 -0
- package/fesm2022/signality-core-browser-media-query.mjs.map +1 -0
- package/fesm2022/signality-core-browser-network.mjs +76 -0
- package/fesm2022/signality-core-browser-network.mjs.map +1 -0
- package/fesm2022/signality-core-browser-online.mjs +49 -0
- package/fesm2022/signality-core-browser-online.mjs.map +1 -0
- package/fesm2022/signality-core-browser-page-visibility.mjs +47 -0
- package/fesm2022/signality-core-browser-page-visibility.mjs.map +1 -0
- package/fesm2022/signality-core-browser-picture-in-picture.mjs +93 -0
- package/fesm2022/signality-core-browser-picture-in-picture.mjs.map +1 -0
- package/fesm2022/signality-core-browser-pointer-lock-element.mjs +43 -0
- package/fesm2022/signality-core-browser-pointer-lock-element.mjs.map +1 -0
- package/fesm2022/signality-core-browser-screen-orientation.mjs +43 -0
- package/fesm2022/signality-core-browser-screen-orientation.mjs.map +1 -0
- package/fesm2022/signality-core-browser-speech-recognition.mjs +171 -0
- package/fesm2022/signality-core-browser-speech-recognition.mjs.map +1 -0
- package/fesm2022/signality-core-browser-speech-synthesis.mjs +146 -0
- package/fesm2022/signality-core-browser-speech-synthesis.mjs.map +1 -0
- package/fesm2022/signality-core-browser-storage.mjs +261 -0
- package/fesm2022/signality-core-browser-storage.mjs.map +1 -0
- package/fesm2022/signality-core-browser-text-direction.mjs +62 -0
- package/fesm2022/signality-core-browser-text-direction.mjs.map +1 -0
- package/fesm2022/signality-core-browser-vibration.mjs +94 -0
- package/fesm2022/signality-core-browser-vibration.mjs.map +1 -0
- package/fesm2022/signality-core-browser-wake-lock.mjs +149 -0
- package/fesm2022/signality-core-browser-wake-lock.mjs.map +1 -0
- package/fesm2022/signality-core-browser-web-notification.mjs +137 -0
- package/fesm2022/signality-core-browser-web-notification.mjs.map +1 -0
- package/fesm2022/signality-core-browser-web-share.mjs +92 -0
- package/fesm2022/signality-core-browser-web-share.mjs.map +1 -0
- package/fesm2022/signality-core-browser-web-worker.mjs +105 -0
- package/fesm2022/signality-core-browser-web-worker.mjs.map +1 -0
- package/fesm2022/signality-core-browser.mjs +34 -0
- package/fesm2022/signality-core-browser.mjs.map +1 -0
- package/fesm2022/signality-core-elements-active-element.mjs +88 -0
- package/fesm2022/signality-core-elements-active-element.mjs.map +1 -0
- package/fesm2022/signality-core-elements-dropzone.mjs +158 -0
- package/fesm2022/signality-core-elements-dropzone.mjs.map +1 -0
- package/fesm2022/signality-core-elements-element-focus-within.mjs +56 -0
- package/fesm2022/signality-core-elements-element-focus-within.mjs.map +1 -0
- package/fesm2022/signality-core-elements-element-focus.mjs +54 -0
- package/fesm2022/signality-core-elements-element-focus.mjs.map +1 -0
- package/fesm2022/signality-core-elements-element-hover.mjs +48 -0
- package/fesm2022/signality-core-elements-element-hover.mjs.map +1 -0
- package/fesm2022/signality-core-elements-element-size.mjs +73 -0
- package/fesm2022/signality-core-elements-element-size.mjs.map +1 -0
- package/fesm2022/signality-core-elements-element-visibility.mjs +76 -0
- package/fesm2022/signality-core-elements-element-visibility.mjs.map +1 -0
- package/fesm2022/signality-core-elements-mouse-position.mjs +109 -0
- package/fesm2022/signality-core-elements-mouse-position.mjs.map +1 -0
- package/fesm2022/signality-core-elements-on-click-outside.mjs +97 -0
- package/fesm2022/signality-core-elements-on-click-outside.mjs.map +1 -0
- package/fesm2022/signality-core-elements-on-disconnect.mjs +99 -0
- package/fesm2022/signality-core-elements-on-disconnect.mjs.map +1 -0
- package/fesm2022/signality-core-elements-on-long-press.mjs +84 -0
- package/fesm2022/signality-core-elements-on-long-press.mjs.map +1 -0
- package/fesm2022/signality-core-elements-pointer-swipe.mjs +116 -0
- package/fesm2022/signality-core-elements-pointer-swipe.mjs.map +1 -0
- package/fesm2022/signality-core-elements-scroll-position.mjs +175 -0
- package/fesm2022/signality-core-elements-scroll-position.mjs.map +1 -0
- package/fesm2022/signality-core-elements-swipe.mjs +107 -0
- package/fesm2022/signality-core-elements-swipe.mjs.map +1 -0
- package/fesm2022/signality-core-elements-text-selection.mjs +70 -0
- package/fesm2022/signality-core-elements-text-selection.mjs.map +1 -0
- package/fesm2022/signality-core-elements-window-size.mjs +81 -0
- package/fesm2022/signality-core-elements-window-size.mjs.map +1 -0
- package/fesm2022/signality-core-elements.mjs +21 -0
- package/fesm2022/signality-core-elements.mjs.map +1 -0
- package/fesm2022/signality-core-forms-cva.mjs +140 -0
- package/fesm2022/signality-core-forms-cva.mjs.map +1 -0
- package/fesm2022/signality-core-forms.mjs +6 -0
- package/fesm2022/signality-core-forms.mjs.map +1 -0
- package/fesm2022/signality-core-internal.mjs +268 -0
- package/fesm2022/signality-core-internal.mjs.map +1 -0
- package/fesm2022/signality-core-observers-intersection-observer.mjs +70 -0
- package/fesm2022/signality-core-observers-intersection-observer.mjs.map +1 -0
- package/fesm2022/signality-core-observers-mutation-observer.mjs +77 -0
- package/fesm2022/signality-core-observers-mutation-observer.mjs.map +1 -0
- package/fesm2022/signality-core-observers-performance-observer.mjs +84 -0
- package/fesm2022/signality-core-observers-performance-observer.mjs.map +1 -0
- package/fesm2022/signality-core-observers-resize-observer.mjs +69 -0
- package/fesm2022/signality-core-observers-resize-observer.mjs.map +1 -0
- package/fesm2022/signality-core-observers.mjs +9 -0
- package/fesm2022/signality-core-observers.mjs.map +1 -0
- package/fesm2022/signality-core-reactivity-debounced.mjs +27 -0
- package/fesm2022/signality-core-reactivity-debounced.mjs.map +1 -0
- package/fesm2022/signality-core-reactivity-throttled.mjs +27 -0
- package/fesm2022/signality-core-reactivity-throttled.mjs.map +1 -0
- package/fesm2022/signality-core-reactivity-watcher.mjs +36 -0
- package/fesm2022/signality-core-reactivity-watcher.mjs.map +1 -0
- package/fesm2022/signality-core-reactivity.mjs +8 -0
- package/fesm2022/signality-core-reactivity.mjs.map +1 -0
- package/fesm2022/signality-core-router-fragment.mjs +41 -0
- package/fesm2022/signality-core-router-fragment.mjs.map +1 -0
- package/fesm2022/signality-core-router-params.mjs +45 -0
- package/fesm2022/signality-core-router-params.mjs.map +1 -0
- package/fesm2022/signality-core-router-query-params.mjs +67 -0
- package/fesm2022/signality-core-router-query-params.mjs.map +1 -0
- package/fesm2022/signality-core-router-route-data.mjs +46 -0
- package/fesm2022/signality-core-router-route-data.mjs.map +1 -0
- package/fesm2022/signality-core-router-router-listener.mjs +50 -0
- package/fesm2022/signality-core-router-router-listener.mjs.map +1 -0
- package/fesm2022/signality-core-router-title.mjs +54 -0
- package/fesm2022/signality-core-router-title.mjs.map +1 -0
- package/fesm2022/signality-core-router-url.mjs +53 -0
- package/fesm2022/signality-core-router-url.mjs.map +1 -0
- package/fesm2022/signality-core-router.mjs +12 -0
- package/fesm2022/signality-core-router.mjs.map +1 -0
- package/fesm2022/signality-core-scheduling-debounce-callback.mjs +59 -0
- package/fesm2022/signality-core-scheduling-debounce-callback.mjs.map +1 -0
- package/fesm2022/signality-core-scheduling-interval.mjs +110 -0
- package/fesm2022/signality-core-scheduling-interval.mjs.map +1 -0
- package/fesm2022/signality-core-scheduling-throttle-callback.mjs +66 -0
- package/fesm2022/signality-core-scheduling-throttle-callback.mjs.map +1 -0
- package/fesm2022/signality-core-scheduling.mjs +8 -0
- package/fesm2022/signality-core-scheduling.mjs.map +1 -0
- package/fesm2022/signality-core-types.mjs +4 -0
- package/fesm2022/signality-core-types.mjs.map +1 -0
- package/fesm2022/signality-core.mjs +13 -0
- package/fesm2022/signality-core.mjs.map +1 -0
- package/forms/cva/index.d.ts +60 -0
- package/forms/index.d.ts +1 -0
- package/index.d.ts +8 -0
- package/internal/constants/index.d.ts +2 -0
- package/internal/constants/mobile-regex.d.ts +1 -0
- package/internal/constants/stubs.d.ts +32 -0
- package/internal/index.d.ts +4 -0
- package/internal/providers/index.d.ts +3 -0
- package/internal/providers/is-browser.d.ts +2 -0
- package/internal/providers/is-mobile.d.ts +2 -0
- package/internal/providers/is-server.d.ts +2 -0
- package/internal/types/index.d.ts +2 -0
- package/internal/types/timer.d.ts +1 -0
- package/internal/types/union.d.ts +1 -0
- package/internal/utils/bom/index.d.ts +1 -0
- package/internal/utils/bom/is-window.d.ts +1 -0
- package/internal/utils/const-signal.d.ts +10 -0
- package/internal/utils/context.d.ts +18 -0
- package/internal/utils/create-token.d.ts +8 -0
- package/internal/utils/dom/get-active-element.d.ts +1 -0
- package/internal/utils/dom/get-event-target.d.ts +1 -0
- package/internal/utils/dom/get-pip-element.d.ts +1 -0
- package/internal/utils/dom/get-shadow-root.d.ts +1 -0
- package/internal/utils/dom/index.d.ts +6 -0
- package/internal/utils/dom/is-element.d.ts +1 -0
- package/internal/utils/dom/is-node-within.d.ts +1 -0
- package/internal/utils/index.d.ts +10 -0
- package/internal/utils/is-plain-object.d.ts +1 -0
- package/internal/utils/is-query-signal.d.ts +10 -0
- package/internal/utils/proxy-signal.d.ts +18 -0
- package/internal/utils/to-element.d.ts +12 -0
- package/internal/utils/to-value.d.ts +6 -0
- package/observers/index.d.ts +4 -0
- package/observers/intersection-observer/index.d.ts +42 -0
- package/observers/mutation-observer/index.d.ts +45 -0
- package/observers/performance-observer/index.d.ts +58 -0
- package/observers/resize-observer/index.d.ts +40 -0
- package/package.json +343 -0
- package/reactivity/debounced/index.d.ts +50 -0
- package/reactivity/index.d.ts +3 -0
- package/reactivity/throttled/index.d.ts +53 -0
- package/reactivity/watcher/index.d.ts +68 -0
- package/router/fragment/index.d.ts +26 -0
- package/router/index.d.ts +7 -0
- package/router/params/index.d.ts +28 -0
- package/router/query-params/index.d.ts +80 -0
- package/router/route-data/index.d.ts +28 -0
- package/router/router-listener/index.d.ts +83 -0
- package/router/title/index.d.ts +29 -0
- package/router/url/index.d.ts +32 -0
- package/scheduling/debounce-callback/index.d.ts +28 -0
- package/scheduling/index.d.ts +3 -0
- package/scheduling/interval/index.d.ts +51 -0
- package/scheduling/throttle-callback/index.d.ts +30 -0
- package/types/index.d.ts +6 -0
- package/types/maybe-element-signal.d.ts +2 -0
- package/types/maybe-signal.d.ts +2 -0
- package/types/signal-value.d.ts +2 -0
- package/types/signal-values.d.ts +5 -0
- package/types/unref-element.d.ts +2 -0
- package/types/with-injector.d.ts +8 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signality-core-browser-media-query.mjs","sources":["../../../projects/core/browser/media-query/index.ts","../../../projects/core/browser/media-query/signality-core-browser-media-query.ts"],"sourcesContent":["import { type CreateSignalOptions, effect, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext, toValue, type Union } from '@signality/core/internal';\nimport type { MaybeSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\n\nexport interface MediaQueryOptions extends CreateSignalOptions<boolean>, WithInjector {\n /**\n * Initial value for SSR.\n * @default false\n */\n readonly initialValue?: boolean;\n}\n\n/**\n * Reactive wrapper around the [Window.matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) method.\n *\n * @param query - CSS media query string (can be a signal)\n * @param options - Optional configuration\n * @returns A signal that is true when the media query matches\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (prefersDark()) {\n * <p>Dark mode is preferred</p>\n * } @else {\n * <p>Light mode is preferred</p>\n * }\n * `\n * })\n * class ThemeComponent {\n * readonly prefersDark = mediaQuery('(prefers-color-scheme: dark)');\n * }\n * ```\n */\nexport function mediaQuery(\n query: MaybeSignal<Union<MediaQueryFeature, string>>,\n options?: MediaQueryOptions\n): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, mediaQuery);\n\n return runInContext(({ isServer, injector }) => {\n if (isServer) {\n return constSignal(!!options?.initialValue);\n }\n\n const matches = signal(!!options?.initialValue, options);\n\n effect(onCleanup => {\n const queryString = toValue(query);\n\n if (!queryString) {\n matches.set(false);\n return;\n }\n\n const mediaQueryList = window.matchMedia(queryString);\n\n matches.set(mediaQueryList.matches);\n\n const changeListener = listener(\n mediaQueryList,\n 'change',\n (e: MediaQueryListEvent) => matches.set(e.matches),\n { injector }\n );\n\n onCleanup(changeListener.destroy);\n });\n\n return matches.asReadonly();\n });\n}\n\ntype MediaQueryFeature =\n | `(any-hover: ${'none' | 'hover'})`\n | `(any-pointer: ${'none' | 'coarse' | 'fine'})`\n | `(aspect-ratio: ${string})`\n | `(color: ${string})`\n | `(color-gamut: ${'srgb' | 'p3' | 'rec2020'})`\n | `(color-index: ${string})`\n | `(device-aspect-ratio: ${string})`\n | `(device-height: ${string})`\n | `(device-posture: ${'flat' | 'folded' | 'continuous'})`\n | `(device-width: ${string})`\n | `(display-mode: ${\n | 'fullscreen'\n | 'standalone'\n | 'minimal-ui'\n | 'browser'\n | 'picture-in-picture'\n | 'window-controls-overlay'})`\n | `(dynamic-range: ${'standard' | 'high'})`\n | `(forced-colors: ${'none' | 'active'})`\n | `(grid: ${'0' | '1'})`\n | `(height: ${string})`\n | `(hover: ${'none' | 'hover'})`\n | `(inverted-colors: ${'none' | 'inverted'})`\n | `(monochrome: ${string})`\n | `(orientation: ${'portrait' | 'landscape'})`\n | `(overflow-block: ${'none' | 'scroll' | 'optional-paged' | 'paged'})`\n | `(overflow-inline: ${'none' | 'scroll'})`\n | `(pointer: ${'none' | 'coarse' | 'fine'})`\n | `(prefers-color-scheme: ${'light' | 'dark' | 'no-preference'})`\n | `(prefers-contrast: ${'no-preference' | 'more' | 'less' | 'custom'})`\n | `(prefers-reduced-data: ${'no-preference' | 'reduce'})`\n | `(prefers-reduced-motion: ${'no-preference' | 'reduce'})`\n | `(prefers-reduced-transparency: ${'no-preference' | 'reduce'})`\n | `(resolution: ${string})`\n | `(scan: ${'progressive' | 'interlace'})`\n | `(scripting: ${'none' | 'initial-only' | 'enabled'})`\n | `(shape: ${'rect' | 'round'})`\n | `(update: ${'none' | 'slow' | 'fast'})`\n | `(video-dynamic-range: ${'standard' | 'high'})`\n | `(width: ${string})`\n | `(min-width: ${string})`\n | `(max-width: ${string})`\n | `(min-height: ${string})`\n | `(max-height: ${string})`\n | `(min-aspect-ratio: ${string})`\n | `(max-aspect-ratio: ${string})`\n | `(min-resolution: ${string})`\n | `(max-resolution: ${string})`\n | `(min-color: ${string})`\n | `(max-color: ${string})`\n | `(min-color-index: ${string})`\n | `(max-color-index: ${string})`\n | `(min-monochrome: ${string})`\n | `(max-monochrome: ${string})`;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAaA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG,SAAU,UAAU,CACxB,KAAoD,EACpD,OAA2B,EAAA;AAE3B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC;IAEpE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAI;QAC7C,IAAI,QAAQ,EAAE;YACZ,OAAO,WAAW,CAAC,CAAC,CAAC,OAAO,EAAE,YAAY,CAAC;QAC7C;AAEA,QAAA,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC;QAExD,MAAM,CAAC,SAAS,IAAG;AACjB,YAAA,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC;YAElC,IAAI,CAAC,WAAW,EAAE;AAChB,gBAAA,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;gBAClB;YACF;YAEA,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC;AAErD,YAAA,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC;YAEnC,MAAM,cAAc,GAAG,QAAQ,CAC7B,cAAc,EACd,QAAQ,EACR,CAAC,CAAsB,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAClD,EAAE,QAAQ,EAAE,CACb;AAED,YAAA,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC;AACnC,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,OAAO,CAAC,UAAU,EAAE;AAC7B,IAAA,CAAC,CAAC;AACJ;;ACzEA;;AAEG;;;;"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { inject, signal } from '@angular/core';
|
|
2
|
+
import { setupContext, constSignal, createToken } from '@signality/core/internal';
|
|
3
|
+
import { setupSync, listener } from '@signality/core/browser/listener';
|
|
4
|
+
import { ONLINE } from '@signality/core/browser/online';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Signal-based wrapper around the Network Information API and online/offline events.
|
|
8
|
+
*
|
|
9
|
+
* @param options - Optional configuration including injector
|
|
10
|
+
* @returns A NetworkRef with network status signals
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* @Component({
|
|
15
|
+
* template: `
|
|
16
|
+
* <p>{{ net.isOnline() ? 'Online' : 'Offline' }}</p>
|
|
17
|
+
* @if (net.effectiveType()) {
|
|
18
|
+
* <p>Connection: {{ net.effectiveType() }}</p>
|
|
19
|
+
* }
|
|
20
|
+
* `
|
|
21
|
+
* })
|
|
22
|
+
* class NetworkComponent {
|
|
23
|
+
* readonly net = network();
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
function network(options) {
|
|
28
|
+
const { runInContext } = setupContext(options?.injector, network);
|
|
29
|
+
return runInContext(({ isBrowser }) => {
|
|
30
|
+
const isSupported = constSignal(isBrowser && 'connection' in navigator);
|
|
31
|
+
if (!isSupported()) {
|
|
32
|
+
return {
|
|
33
|
+
isSupported,
|
|
34
|
+
isOnline: constSignal(true),
|
|
35
|
+
effectiveType: constSignal(undefined),
|
|
36
|
+
downlink: constSignal(undefined),
|
|
37
|
+
rtt: constSignal(undefined),
|
|
38
|
+
saveData: constSignal(false),
|
|
39
|
+
type: constSignal(undefined),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const { connection } = navigator;
|
|
43
|
+
const isOnline = inject(ONLINE);
|
|
44
|
+
const effectiveType = signal(connection.effectiveType, ...(ngDevMode ? [{ debugName: "effectiveType" }] : []));
|
|
45
|
+
const downlink = signal(connection.downlink, ...(ngDevMode ? [{ debugName: "downlink" }] : []));
|
|
46
|
+
const rtt = signal(connection.rtt, ...(ngDevMode ? [{ debugName: "rtt" }] : []));
|
|
47
|
+
const saveData = signal(!!connection.saveData, ...(ngDevMode ? [{ debugName: "saveData" }] : []));
|
|
48
|
+
const type = signal(connection.type, ...(ngDevMode ? [{ debugName: "type" }] : []));
|
|
49
|
+
setupSync(() => {
|
|
50
|
+
listener(connection, 'change', () => {
|
|
51
|
+
effectiveType.set(connection.effectiveType);
|
|
52
|
+
downlink.set(connection.downlink);
|
|
53
|
+
rtt.set(connection.rtt);
|
|
54
|
+
saveData.set(!!connection.saveData);
|
|
55
|
+
type.set(connection.type);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
isSupported,
|
|
60
|
+
isOnline,
|
|
61
|
+
effectiveType: effectiveType.asReadonly(),
|
|
62
|
+
downlink: downlink.asReadonly(),
|
|
63
|
+
rtt: rtt.asReadonly(),
|
|
64
|
+
saveData: saveData.asReadonly(),
|
|
65
|
+
type: type.asReadonly(),
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const NETWORK = /* @__PURE__ */ createToken(network);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Generated bundle index. Do not edit.
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
export { NETWORK, network };
|
|
76
|
+
//# sourceMappingURL=signality-core-browser-network.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signality-core-browser-network.mjs","sources":["../../../projects/core/browser/network/index.ts","../../../projects/core/browser/network/signality-core-browser-network.ts"],"sourcesContent":["import { inject, type Signal, signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\nimport { ONLINE } from '@signality/core/browser/online';\n\nexport type EffectiveConnectionType = 'slow-2g' | '2g' | '3g' | '4g';\n\nexport type ConnectionType =\n | 'bluetooth'\n | 'cellular'\n | 'ethernet'\n | 'wifi'\n | 'wimax'\n | 'none'\n | 'other'\n | 'unknown';\n\nexport type NetworkOptions = WithInjector;\n\nexport interface NetworkRef {\n /** Whether Network Information API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Whether the browser is online */\n readonly isOnline: Signal<boolean>;\n\n /** Effective connection type */\n readonly effectiveType: Signal<EffectiveConnectionType | undefined>;\n\n /** Downlink speed in Mbps */\n readonly downlink: Signal<number | undefined>;\n\n /** Round-trip time in ms */\n readonly rtt: Signal<number | undefined>;\n\n /** Whether user has data saver enabled */\n readonly saveData: Signal<boolean>;\n\n /** Connection type (wifi, cellular, etc.) */\n readonly type: Signal<ConnectionType | undefined>;\n}\n\n/**\n * Signal-based wrapper around the Network Information API and online/offline events.\n *\n * @param options - Optional configuration including injector\n * @returns A NetworkRef with network status signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>{{ net.isOnline() ? 'Online' : 'Offline' }}</p>\n * @if (net.effectiveType()) {\n * <p>Connection: {{ net.effectiveType() }}</p>\n * }\n * `\n * })\n * class NetworkComponent {\n * readonly net = network();\n * }\n * ```\n */\nexport function network(options?: NetworkOptions): NetworkRef {\n const { runInContext } = setupContext(options?.injector, network);\n\n return runInContext(({ isBrowser }) => {\n const isSupported = constSignal(isBrowser && 'connection' in navigator);\n\n if (!isSupported()) {\n return {\n isSupported,\n isOnline: constSignal(true),\n effectiveType: constSignal(undefined),\n downlink: constSignal(undefined),\n rtt: constSignal(undefined),\n saveData: constSignal(false),\n type: constSignal(undefined),\n };\n }\n\n const { connection } = navigator as NavigatorWithConnection;\n\n const isOnline = inject(ONLINE);\n const effectiveType = signal(connection.effectiveType);\n const downlink = signal(connection.downlink);\n const rtt = signal(connection.rtt);\n const saveData = signal(!!connection.saveData);\n const type = signal(connection.type);\n\n setupSync(() => {\n listener(connection, 'change', () => {\n effectiveType.set(connection.effectiveType);\n downlink.set(connection.downlink);\n rtt.set(connection.rtt);\n saveData.set(!!connection.saveData);\n type.set(connection.type);\n });\n });\n\n return {\n isSupported,\n isOnline,\n effectiveType: effectiveType.asReadonly(),\n downlink: downlink.asReadonly(),\n rtt: rtt.asReadonly(),\n saveData: saveData.asReadonly(),\n type: type.asReadonly(),\n };\n });\n}\n\nexport const NETWORK = /* @__PURE__ */ createToken(network);\n\ninterface NetworkInformation extends EventTarget {\n readonly effectiveType?: EffectiveConnectionType;\n readonly downlink?: number;\n readonly rtt?: number;\n readonly saveData?: boolean;\n readonly type?: ConnectionType;\n}\n\ninterface NavigatorWithConnection extends Navigator {\n readonly connection: NetworkInformation;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AA2CA;;;;;;;;;;;;;;;;;;;;AAoBG;AACG,SAAU,OAAO,CAAC,OAAwB,EAAA;AAC9C,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC;AAEjE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAI;QACpC,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,IAAI,YAAY,IAAI,SAAS,CAAC;AAEvE,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC;AAC3B,gBAAA,aAAa,EAAE,WAAW,CAAC,SAAS,CAAC;AACrC,gBAAA,QAAQ,EAAE,WAAW,CAAC,SAAS,CAAC;AAChC,gBAAA,GAAG,EAAE,WAAW,CAAC,SAAS,CAAC;AAC3B,gBAAA,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;AAC5B,gBAAA,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC;aAC7B;QACH;AAEA,QAAA,MAAM,EAAE,UAAU,EAAE,GAAG,SAAoC;AAE3D,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;QAC/B,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,aAAa,yDAAC;QACtD,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,oDAAC;QAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,+CAAC;QAClC,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,UAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;QAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,gDAAC;QAEpC,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAK;AAClC,gBAAA,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC;AAC3C,gBAAA,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC;AACjC,gBAAA,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;gBACvB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC;AACnC,gBAAA,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;AAC3B,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;YACX,QAAQ;AACR,YAAA,aAAa,EAAE,aAAa,CAAC,UAAU,EAAE;AACzC,YAAA,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE;AAC/B,YAAA,GAAG,EAAE,GAAG,CAAC,UAAU,EAAE;AACrB,YAAA,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE;AAC/B,YAAA,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;SACxB;AACH,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,OAAO,mBAAmB,WAAW,CAAC,OAAO;;ACjH1D;;AAEG;;;;"}
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
* Reactive wrapper around the [Navigator.onLine](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine) property.
|
|
7
|
+
*
|
|
8
|
+
* @param options - Optional configuration including signal options and injector
|
|
9
|
+
* @returns A signal containing the current online status (`true` for online, `false` for offline)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* @Component({
|
|
14
|
+
* template: `
|
|
15
|
+
* @if (isOnline()) {
|
|
16
|
+
* <p>You're online</p>
|
|
17
|
+
* } @else {
|
|
18
|
+
* <p>You're offline</p>
|
|
19
|
+
* }
|
|
20
|
+
* `
|
|
21
|
+
* })
|
|
22
|
+
* class NetworkStatusComponent {
|
|
23
|
+
* readonly isOnline = online();
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
function online(options) {
|
|
28
|
+
const { runInContext } = setupContext(options?.injector, online);
|
|
29
|
+
return runInContext(({ isServer }) => {
|
|
30
|
+
if (isServer) {
|
|
31
|
+
return constSignal(true);
|
|
32
|
+
}
|
|
33
|
+
const isOnline = signal(navigator.onLine, options);
|
|
34
|
+
const update = () => isOnline.set(navigator.onLine);
|
|
35
|
+
setupSync(() => {
|
|
36
|
+
listener(window, 'online', update);
|
|
37
|
+
listener(window, 'offline', update);
|
|
38
|
+
});
|
|
39
|
+
return isOnline.asReadonly();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const ONLINE = /* @__PURE__ */ createToken(online);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generated bundle index. Do not edit.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
export { ONLINE, online };
|
|
49
|
+
//# sourceMappingURL=signality-core-browser-online.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signality-core-browser-online.mjs","sources":["../../../projects/core/browser/online/index.ts","../../../projects/core/browser/online/signality-core-browser-online.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type OnlineOptions = CreateSignalOptions<boolean> & WithInjector;\n\n/**\n * Reactive wrapper around the [Navigator.onLine](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine) property.\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current online status (`true` for online, `false` for offline)\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (isOnline()) {\n * <p>You're online</p>\n * } @else {\n * <p>You're offline</p>\n * }\n * `\n * })\n * class NetworkStatusComponent {\n * readonly isOnline = online();\n * }\n * ```\n */\nexport function online(options?: OnlineOptions): Signal<boolean> {\n const { runInContext } = setupContext(options?.injector, online);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(true);\n }\n\n const isOnline = signal(navigator.onLine, options);\n\n const update = () => isOnline.set(navigator.onLine);\n\n setupSync(() => {\n listener(window, 'online', update);\n listener(window, 'offline', update);\n });\n\n return isOnline.asReadonly();\n });\n}\n\nexport const ONLINE = /* @__PURE__ */ createToken(online);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAOA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,MAAM,CAAC,OAAuB,EAAA;AAC5C,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC;AAEhE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,IAAI,CAAC;QAC1B;QAEA,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;AAElD,QAAA,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC;QAEnD,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC;AAClC,YAAA,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC;AACrC,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,QAAQ,CAAC,UAAU,EAAE;AAC9B,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,MAAM,mBAAmB,WAAW,CAAC,MAAM;;AClDxD;;AAEG;;;;"}
|
|
@@ -0,0 +1,47 @@
|
|
|
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 [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API).
|
|
7
|
+
*
|
|
8
|
+
* @param options - Optional configuration including signal options and injector
|
|
9
|
+
* @returns A signal containing the current DocumentVisibilityState ('visible' or 'hidden')
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* @Component({
|
|
14
|
+
* template: `
|
|
15
|
+
* @if (visibility() === 'hidden') {
|
|
16
|
+
* <p>Page is hidden</p>
|
|
17
|
+
* } @else {
|
|
18
|
+
* <p>Page is visible</p>
|
|
19
|
+
* }
|
|
20
|
+
* `
|
|
21
|
+
* })
|
|
22
|
+
* class VisibilityComponent {
|
|
23
|
+
* readonly visibility = pageVisibility();
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
function pageVisibility(options) {
|
|
28
|
+
const { runInContext } = setupContext(options?.injector, pageVisibility);
|
|
29
|
+
return runInContext(({ isServer }) => {
|
|
30
|
+
if (isServer) {
|
|
31
|
+
return constSignal('visible');
|
|
32
|
+
}
|
|
33
|
+
const visibilityState = signal(document.visibilityState, options);
|
|
34
|
+
setupSync(() => {
|
|
35
|
+
listener(document, 'visibilitychange', () => visibilityState.set(document.visibilityState));
|
|
36
|
+
});
|
|
37
|
+
return visibilityState.asReadonly();
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
const PAGE_VISIBILITY = /* @__PURE__ */ createToken(pageVisibility);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generated bundle index. Do not edit.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
export { PAGE_VISIBILITY, pageVisibility };
|
|
47
|
+
//# sourceMappingURL=signality-core-browser-page-visibility.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signality-core-browser-page-visibility.mjs","sources":["../../../projects/core/browser/page-visibility/index.ts","../../../projects/core/browser/page-visibility/signality-core-browser-page-visibility.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, createToken, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type PageVisibilityOptions = CreateSignalOptions<DocumentVisibilityState> & WithInjector;\n\n/**\n * Signal-based wrapper around the [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API).\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current DocumentVisibilityState ('visible' or 'hidden')\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (visibility() === 'hidden') {\n * <p>Page is hidden</p>\n * } @else {\n * <p>Page is visible</p>\n * }\n * `\n * })\n * class VisibilityComponent {\n * readonly visibility = pageVisibility();\n * }\n * ```\n */\nexport function pageVisibility(options?: PageVisibilityOptions): Signal<DocumentVisibilityState> {\n const { runInContext } = setupContext(options?.injector, pageVisibility);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal('visible');\n }\n\n const visibilityState = signal(document.visibilityState, options);\n\n setupSync(() => {\n listener(document, 'visibilitychange', () => visibilityState.set(document.visibilityState));\n });\n\n return visibilityState.asReadonly();\n });\n}\n\nexport const PAGE_VISIBILITY = /* @__PURE__ */ createToken(pageVisibility);\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAOA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,cAAc,CAAC,OAA+B,EAAA;AAC5D,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,cAAc,CAAC;AAExE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,SAAS,CAAC;QAC/B;QAEA,MAAM,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;QAEjE,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;AAC7F,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,eAAe,CAAC,UAAU,EAAE;AACrC,IAAA,CAAC,CAAC;AACJ;AAEO,MAAM,eAAe,mBAAmB,WAAW,CAAC,cAAc;;AC/CzE;;AAEG;;;;"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { signal, untracked } from '@angular/core';
|
|
2
|
+
import { setupContext, constSignal, NOOP_ASYNC_FN, toElement, getPipElement } from '@signality/core/internal';
|
|
3
|
+
import { listener } from '@signality/core/browser/listener';
|
|
4
|
+
import { onDisconnect } from '@signality/core/elements/on-disconnect';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Signal-based wrapper around the [Picture-in-Picture API](https://developer.mozilla.org/en-US/docs/Web/API/Picture-in-Picture_API).
|
|
8
|
+
*
|
|
9
|
+
* Automatically exits Picture-in-Picture when the target element is disconnected from the DOM.
|
|
10
|
+
*
|
|
11
|
+
* @param target - Video element
|
|
12
|
+
* @param options - Optional configuration
|
|
13
|
+
* @returns A {@link PictureInPictureRef} with `isSupported`, `isActive` signals and `enter`/`exit`/`toggle` methods
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* @Component({
|
|
18
|
+
* template: `
|
|
19
|
+
* @if (pip.isSupported()) {
|
|
20
|
+
* <video #video src="video.mp4"></video>
|
|
21
|
+
* <button (click)="pip.toggle()">Toggle PiP</button>
|
|
22
|
+
* <p>Active: {{ pip.isActive() }}</p>
|
|
23
|
+
* }
|
|
24
|
+
* `
|
|
25
|
+
* })
|
|
26
|
+
* class PiPComponent {
|
|
27
|
+
* readonly video = viewChild<HTMLVideoElement>('video');
|
|
28
|
+
* readonly pip = pictureInPicture(this.video);
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function pictureInPicture(target, options) {
|
|
33
|
+
const { runInContext } = setupContext(options?.injector, pictureInPicture);
|
|
34
|
+
return runInContext(({ isBrowser }) => {
|
|
35
|
+
const isSupported = constSignal(isBrowser &&
|
|
36
|
+
'pictureInPictureEnabled' in document &&
|
|
37
|
+
document.pictureInPictureEnabled !== false);
|
|
38
|
+
if (!isSupported()) {
|
|
39
|
+
return {
|
|
40
|
+
isSupported,
|
|
41
|
+
isActive: constSignal(false),
|
|
42
|
+
enter: NOOP_ASYNC_FN,
|
|
43
|
+
exit: NOOP_ASYNC_FN,
|
|
44
|
+
toggle: NOOP_ASYNC_FN,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const isActive = signal(false, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
|
|
48
|
+
const enter = async () => {
|
|
49
|
+
const el = toElement.untracked(target);
|
|
50
|
+
if (el) {
|
|
51
|
+
await el.requestPictureInPicture();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const exit = async () => {
|
|
55
|
+
const el = toElement.untracked(target);
|
|
56
|
+
const pipEl = getPipElement(document);
|
|
57
|
+
if (el && pipEl && el === pipEl) {
|
|
58
|
+
await document.exitPictureInPicture();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const toggle = async () => {
|
|
62
|
+
if (untracked(isActive)) {
|
|
63
|
+
await exit();
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
await enter();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
listener(target, 'enterpictureinpicture', () => isActive.set(true));
|
|
70
|
+
listener(target, 'leavepictureinpicture', () => isActive.set(false));
|
|
71
|
+
onDisconnect(target, async (el) => {
|
|
72
|
+
const pipEl = getPipElement(document);
|
|
73
|
+
if (pipEl && el === pipEl) {
|
|
74
|
+
await document.exitPictureInPicture();
|
|
75
|
+
isActive.set(false);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
isSupported,
|
|
80
|
+
isActive,
|
|
81
|
+
enter,
|
|
82
|
+
exit,
|
|
83
|
+
toggle,
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Generated bundle index. Do not edit.
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
export { pictureInPicture };
|
|
93
|
+
//# sourceMappingURL=signality-core-browser-picture-in-picture.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signality-core-browser-picture-in-picture.mjs","sources":["../../../projects/core/browser/picture-in-picture/index.ts","../../../projects/core/browser/picture-in-picture/signality-core-browser-picture-in-picture.ts"],"sourcesContent":["import { signal, type Signal, untracked } from '@angular/core';\nimport {\n constSignal,\n getPipElement,\n NOOP_ASYNC_FN,\n setupContext,\n toElement,\n} from '@signality/core/internal';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport type PictureInPictureOptions = WithInjector;\n\nexport interface PictureInPictureRef {\n /** Whether Picture-in-Picture API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Whether Picture-in-Picture is active */\n readonly isActive: Signal<boolean>;\n\n /** Enter Picture-in-Picture mode */\n readonly enter: () => Promise<void>;\n\n /** Exit Picture-in-Picture mode */\n readonly exit: () => Promise<void>;\n\n /** Toggle Picture-in-Picture mode */\n readonly toggle: () => Promise<void>;\n}\n\n/**\n * Signal-based wrapper around the [Picture-in-Picture API](https://developer.mozilla.org/en-US/docs/Web/API/Picture-in-Picture_API).\n *\n * Automatically exits Picture-in-Picture when the target element is disconnected from the DOM.\n *\n * @param target - Video element\n * @param options - Optional configuration\n * @returns A {@link PictureInPictureRef} with `isSupported`, `isActive` signals and `enter`/`exit`/`toggle` methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (pip.isSupported()) {\n * <video #video src=\"video.mp4\"></video>\n * <button (click)=\"pip.toggle()\">Toggle PiP</button>\n * <p>Active: {{ pip.isActive() }}</p>\n * }\n * `\n * })\n * class PiPComponent {\n * readonly video = viewChild<HTMLVideoElement>('video');\n * readonly pip = pictureInPicture(this.video);\n * }\n * ```\n */\nexport function pictureInPicture(\n target: MaybeElementSignal<HTMLVideoElement>,\n options?: PictureInPictureOptions\n): PictureInPictureRef {\n const { runInContext } = setupContext(options?.injector, pictureInPicture);\n\n return runInContext(({ isBrowser }) => {\n const isSupported = constSignal(\n isBrowser &&\n 'pictureInPictureEnabled' in document &&\n document.pictureInPictureEnabled !== false\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n isActive: constSignal(false),\n enter: NOOP_ASYNC_FN,\n exit: NOOP_ASYNC_FN,\n toggle: NOOP_ASYNC_FN,\n };\n }\n\n const isActive = signal(false);\n\n const enter = async (): Promise<void> => {\n const el = toElement.untracked(target);\n if (el) {\n await el.requestPictureInPicture();\n }\n };\n\n const exit = async (): Promise<void> => {\n const el = toElement.untracked(target);\n const pipEl = getPipElement(document);\n\n if (el && pipEl && el === pipEl) {\n await document.exitPictureInPicture();\n }\n };\n\n const toggle = async (): Promise<void> => {\n if (untracked(isActive)) {\n await exit();\n } else {\n await enter();\n }\n };\n\n listener(target, 'enterpictureinpicture', () => isActive.set(true));\n listener(target, 'leavepictureinpicture', () => isActive.set(false));\n\n onDisconnect(target, async el => {\n const pipEl = getPipElement(document);\n\n if (pipEl && el === pipEl) {\n await document.exitPictureInPicture();\n isActive.set(false);\n }\n });\n\n return {\n isSupported,\n isActive,\n enter,\n exit,\n toggle,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AA+BA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;AACG,SAAU,gBAAgB,CAC9B,MAA4C,EAC5C,OAAiC,EAAA;AAEjC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,CAAC;AAE1E,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,KAAI;AACpC,QAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,SAAS;AACP,YAAA,yBAAyB,IAAI,QAAQ;AACrC,YAAA,QAAQ,CAAC,uBAAuB,KAAK,KAAK,CAC7C;AAED,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;AAC5B,gBAAA,KAAK,EAAE,aAAa;AACpB,gBAAA,IAAI,EAAE,aAAa;AACnB,gBAAA,MAAM,EAAE,aAAa;aACtB;QACH;AAEA,QAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,oDAAC;AAE9B,QAAA,MAAM,KAAK,GAAG,YAA0B;YACtC,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC;YACtC,IAAI,EAAE,EAAE;AACN,gBAAA,MAAM,EAAE,CAAC,uBAAuB,EAAE;YACpC;AACF,QAAA,CAAC;AAED,QAAA,MAAM,IAAI,GAAG,YAA0B;YACrC,MAAM,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC;AACtC,YAAA,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC;YAErC,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE;AAC/B,gBAAA,MAAM,QAAQ,CAAC,oBAAoB,EAAE;YACvC;AACF,QAAA,CAAC;AAED,QAAA,MAAM,MAAM,GAAG,YAA0B;AACvC,YAAA,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE;gBACvB,MAAM,IAAI,EAAE;YACd;iBAAO;gBACL,MAAM,KAAK,EAAE;YACf;AACF,QAAA,CAAC;AAED,QAAA,QAAQ,CAAC,MAAM,EAAE,uBAAuB,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACnE,QAAA,QAAQ,CAAC,MAAM,EAAE,uBAAuB,EAAE,MAAM,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAEpE,QAAA,YAAY,CAAC,MAAM,EAAE,OAAM,EAAE,KAAG;AAC9B,YAAA,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC;AAErC,YAAA,IAAI,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE;AACzB,gBAAA,MAAM,QAAQ,CAAC,oBAAoB,EAAE;AACrC,gBAAA,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;YACrB;AACF,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;YACX,QAAQ;YACR,KAAK;YACL,IAAI;YACJ,MAAM;SACP;AACH,IAAA,CAAC,CAAC;AACJ;;AC9HA;;AAEG;;;;"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { signal } from '@angular/core';
|
|
2
|
+
import { setupContext, constSignal } from '@signality/core/internal';
|
|
3
|
+
import { setupSync, listener } from '@signality/core/browser/listener';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Reactive wrapper around the [Pointer Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API).
|
|
7
|
+
* Returns a signal that tracks the element that currently has pointer lock.
|
|
8
|
+
*
|
|
9
|
+
* @param options - Optional configuration including signal options and injector
|
|
10
|
+
* @returns A signal containing the element with pointer lock, or `null` if no element has lock
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const lockedElement = pointerLockElement();
|
|
15
|
+
*
|
|
16
|
+
* effect(() => {
|
|
17
|
+
* if (lockedElement()) {
|
|
18
|
+
* console.log('Pointer locked on:', lockedElement());
|
|
19
|
+
* }
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
function pointerLockElement(options) {
|
|
24
|
+
const { runInContext } = setupContext(options?.injector, pointerLockElement);
|
|
25
|
+
return runInContext(({ isServer }) => {
|
|
26
|
+
if (isServer) {
|
|
27
|
+
return constSignal(null);
|
|
28
|
+
}
|
|
29
|
+
const element = signal(document.pointerLockElement, options);
|
|
30
|
+
setupSync(() => {
|
|
31
|
+
listener(document, 'pointerlockchange', () => element.set(document.pointerLockElement));
|
|
32
|
+
listener(document, 'pointerlockerror', () => element.set(null));
|
|
33
|
+
});
|
|
34
|
+
return element.asReadonly();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generated bundle index. Do not edit.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
export { pointerLockElement };
|
|
43
|
+
//# sourceMappingURL=signality-core-browser-pointer-lock-element.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signality-core-browser-pointer-lock-element.mjs","sources":["../../../projects/core/browser/pointer-lock-element/index.ts","../../../projects/core/browser/pointer-lock-element/signality-core-browser-pointer-lock-element.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport type PointerLockElementOptions = CreateSignalOptions<Element | null> & WithInjector;\n\n/**\n * Reactive wrapper around the [Pointer Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API).\n * Returns a signal that tracks the element that currently has pointer lock.\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the element with pointer lock, or `null` if no element has lock\n *\n * @example\n * ```typescript\n * const lockedElement = pointerLockElement();\n *\n * effect(() => {\n * if (lockedElement()) {\n * console.log('Pointer locked on:', lockedElement());\n * }\n * });\n * ```\n */\nexport function pointerLockElement(options?: PointerLockElementOptions): Signal<Element | null> {\n const { runInContext } = setupContext(options?.injector, pointerLockElement);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(null);\n }\n\n const element = signal(document.pointerLockElement, options);\n\n setupSync(() => {\n listener(document, 'pointerlockchange', () => element.set(document.pointerLockElement));\n listener(document, 'pointerlockerror', () => element.set(null));\n });\n\n return element.asReadonly();\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAOA;;;;;;;;;;;;;;;;;AAiBG;AACG,SAAU,kBAAkB,CAAC,OAAmC,EAAA;AACpE,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,IAAI,CAAC;QAC1B;QAEA,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;QAE5D,SAAS,CAAC,MAAK;AACb,YAAA,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;AACvF,YAAA,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACjE,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,OAAO,CAAC,UAAU,EAAE;AAC7B,IAAA,CAAC,CAAC;AACJ;;AC1CA;;AAEG;;;;"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { signal } from '@angular/core';
|
|
2
|
+
import { setupContext, constSignal } from '@signality/core/internal';
|
|
3
|
+
import { setupSync, listener } from '@signality/core/browser/listener';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Reactive wrapper around the [Screen Orientation API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Orientation_API).
|
|
7
|
+
* Returns a signal that tracks the current screen orientation.
|
|
8
|
+
*
|
|
9
|
+
* @param options - Optional configuration including signal options and injector
|
|
10
|
+
* @returns A signal containing the current orientation type (`'portrait-primary'`, `'portrait-secondary'`, `'landscape-primary'`, or `'landscape-secondary'`)
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* @Component({
|
|
15
|
+
* template: `
|
|
16
|
+
* <p>Orientation: {{ orientation() }}</p>
|
|
17
|
+
* `
|
|
18
|
+
* })
|
|
19
|
+
* class OrientationComponent {
|
|
20
|
+
* readonly orientation = screenOrientation();
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
function screenOrientation(options) {
|
|
25
|
+
const { runInContext } = setupContext(options?.injector, screenOrientation);
|
|
26
|
+
return runInContext(({ isServer }) => {
|
|
27
|
+
if (isServer) {
|
|
28
|
+
return constSignal(options?.initialValue || 'portrait-primary');
|
|
29
|
+
}
|
|
30
|
+
const orientation = signal(screen.orientation.type, options);
|
|
31
|
+
setupSync(() => {
|
|
32
|
+
listener(screen.orientation, 'change', () => orientation.set(screen.orientation.type));
|
|
33
|
+
});
|
|
34
|
+
return orientation.asReadonly();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generated bundle index. Do not edit.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
export { screenOrientation };
|
|
43
|
+
//# sourceMappingURL=signality-core-browser-screen-orientation.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signality-core-browser-screen-orientation.mjs","sources":["../../../projects/core/browser/screen-orientation/index.ts","../../../projects/core/browser/screen-orientation/signality-core-browser-screen-orientation.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport type { WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport interface ScreenOrientationOptions\n extends CreateSignalOptions<OrientationType>,\n WithInjector {\n /**\n * Initial value for SSR.\n * @default 'portrait-primary'\n */\n readonly initialValue?: OrientationType;\n}\n\n/**\n * Reactive wrapper around the [Screen Orientation API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Orientation_API).\n * Returns a signal that tracks the current screen orientation.\n *\n * @param options - Optional configuration including signal options and injector\n * @returns A signal containing the current orientation type (`'portrait-primary'`, `'portrait-secondary'`, `'landscape-primary'`, or `'landscape-secondary'`)\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Orientation: {{ orientation() }}</p>\n * `\n * })\n * class OrientationComponent {\n * readonly orientation = screenOrientation();\n * }\n * ```\n */\nexport function screenOrientation(options?: ScreenOrientationOptions): Signal<OrientationType> {\n const { runInContext } = setupContext(options?.injector, screenOrientation);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(options?.initialValue || 'portrait-primary');\n }\n\n const orientation = signal(screen.orientation.type, options);\n\n setupSync(() => {\n listener(screen.orientation, 'change', () => orientation.set(screen.orientation.type));\n });\n\n return orientation.asReadonly();\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAeA;;;;;;;;;;;;;;;;;;AAkBG;AACG,SAAU,iBAAiB,CAAC,OAAkC,EAAA;AAClE,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,CAAC;AAE3E,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;YACZ,OAAO,WAAW,CAAC,OAAO,EAAE,YAAY,IAAI,kBAAkB,CAAC;QACjE;AAEA,QAAA,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC;QAE5D,SAAS,CAAC,MAAK;YACb,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;AACxF,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,WAAW,CAAC,UAAU,EAAE;AACjC,IAAA,CAAC,CAAC;AACJ;;AClDA;;AAEG;;;;"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { signal, untracked, isSignal } from '@angular/core';
|
|
2
|
+
import { setupContext, constSignal, NOOP_FN, toValue } from '@signality/core/internal';
|
|
3
|
+
import { watcher } from '@signality/core/reactivity/watcher';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Signal-based wrapper around the [Speech Recognition API](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition).
|
|
7
|
+
*
|
|
8
|
+
* @param options - Optional configuration
|
|
9
|
+
* @returns A SpeechRecognitionRef with isSupported, isListening, text, interimText, error signals and control methods
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* @Component({
|
|
14
|
+
* template: `
|
|
15
|
+
* @if (recognition.isSupported()) {
|
|
16
|
+
* <button (click)="toggleRecognition()">
|
|
17
|
+
* {{ recognition.isListening() ? 'Stop' : 'Start' }} Recording
|
|
18
|
+
* </button>
|
|
19
|
+
* <p>{{ recognition.text() }}</p>
|
|
20
|
+
* @if (recognition.interimText()) {
|
|
21
|
+
* <p><em>{{ recognition.interimText() }}</em></p>
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* `
|
|
25
|
+
* })
|
|
26
|
+
* class SpeechComponent {
|
|
27
|
+
* readonly recognition = speechRecognition();
|
|
28
|
+
*
|
|
29
|
+
* toggleRecognition() {
|
|
30
|
+
* if (this.recognition.isListening()) {
|
|
31
|
+
* this.recognition.stop();
|
|
32
|
+
* } else {
|
|
33
|
+
* this.recognition.start();
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
function speechRecognition(options) {
|
|
40
|
+
const { runInContext } = setupContext(options?.injector, speechRecognition);
|
|
41
|
+
return runInContext(({ isBrowser, onCleanup }) => {
|
|
42
|
+
const isSupported = constSignal(isBrowser && ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window));
|
|
43
|
+
if (!isSupported()) {
|
|
44
|
+
return {
|
|
45
|
+
isSupported,
|
|
46
|
+
isListening: constSignal(false),
|
|
47
|
+
text: constSignal(''),
|
|
48
|
+
interimText: constSignal(''),
|
|
49
|
+
error: constSignal(null),
|
|
50
|
+
start: NOOP_FN,
|
|
51
|
+
stop: NOOP_FN,
|
|
52
|
+
abort: NOOP_FN,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const SpeechRecognitionClass = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
56
|
+
const recognition = new SpeechRecognitionClass();
|
|
57
|
+
const abortController = new AbortController();
|
|
58
|
+
const { lang = 'en-US', interimResults = false, continuous = false, maxAlternatives = 1, } = options ?? {};
|
|
59
|
+
recognition.lang = toValue(lang);
|
|
60
|
+
recognition.continuous = continuous;
|
|
61
|
+
recognition.interimResults = interimResults;
|
|
62
|
+
recognition.maxAlternatives = maxAlternatives;
|
|
63
|
+
const isListening = signal(false, ...(ngDevMode ? [{ debugName: "isListening" }] : []));
|
|
64
|
+
const text = signal('', ...(ngDevMode ? [{ debugName: "text" }] : []));
|
|
65
|
+
const interimText = signal('', ...(ngDevMode ? [{ debugName: "interimText" }] : []));
|
|
66
|
+
const error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
67
|
+
const handleResult = (event) => {
|
|
68
|
+
let finalTranscript = '';
|
|
69
|
+
let interimTranscript = '';
|
|
70
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
71
|
+
const result = event.results[i];
|
|
72
|
+
const transcript = result[0]?.transcript || result.item(0)?.transcript || '';
|
|
73
|
+
if (result.isFinal) {
|
|
74
|
+
finalTranscript += transcript;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
interimTranscript += transcript;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (finalTranscript) {
|
|
81
|
+
text.update(t => (t ? t + ' ' : '') + finalTranscript);
|
|
82
|
+
interimText.set('');
|
|
83
|
+
}
|
|
84
|
+
else if (interimTranscript) {
|
|
85
|
+
interimText.set(interimTranscript);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const handleError = (event) => {
|
|
89
|
+
error.set(event);
|
|
90
|
+
isListening.set(false);
|
|
91
|
+
};
|
|
92
|
+
const handleStart = () => {
|
|
93
|
+
isListening.set(true);
|
|
94
|
+
error.set(null);
|
|
95
|
+
if (!continuous) {
|
|
96
|
+
text.set('');
|
|
97
|
+
interimText.set('');
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const handleEnd = () => {
|
|
101
|
+
isListening.set(false);
|
|
102
|
+
recognition.lang = toValue(lang);
|
|
103
|
+
};
|
|
104
|
+
recognition.onstart = handleStart;
|
|
105
|
+
recognition.onend = handleEnd;
|
|
106
|
+
recognition.onerror = handleError;
|
|
107
|
+
recognition.onresult = handleResult;
|
|
108
|
+
const start = () => {
|
|
109
|
+
try {
|
|
110
|
+
if (!untracked(isListening)) {
|
|
111
|
+
recognition.start();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
error.set(err);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const stop = () => {
|
|
119
|
+
if (untracked(isListening)) {
|
|
120
|
+
recognition.stop();
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const abort = () => {
|
|
124
|
+
if (untracked(isListening)) {
|
|
125
|
+
recognition.abort();
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
onCleanup(() => {
|
|
129
|
+
abortController.abort();
|
|
130
|
+
abort();
|
|
131
|
+
});
|
|
132
|
+
if (isSignal(lang)) {
|
|
133
|
+
watcher(lang, newLang => {
|
|
134
|
+
if (!isListening()) {
|
|
135
|
+
recognition.lang = newLang;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
navigator.permissions.query({ name: 'microphone' }).then(result => {
|
|
140
|
+
if (abortController.signal.aborted) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const check = () => {
|
|
144
|
+
if (result.state === 'denied') {
|
|
145
|
+
abort();
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
check();
|
|
149
|
+
result.addEventListener('change', check, {
|
|
150
|
+
signal: abortController.signal,
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
return {
|
|
154
|
+
isSupported,
|
|
155
|
+
isListening: isListening.asReadonly(),
|
|
156
|
+
text: text.asReadonly(),
|
|
157
|
+
interimText: interimText.asReadonly(),
|
|
158
|
+
error: error.asReadonly(),
|
|
159
|
+
start,
|
|
160
|
+
stop,
|
|
161
|
+
abort,
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Generated bundle index. Do not edit.
|
|
168
|
+
*/
|
|
169
|
+
|
|
170
|
+
export { speechRecognition };
|
|
171
|
+
//# sourceMappingURL=signality-core-browser-speech-recognition.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signality-core-browser-speech-recognition.mjs","sources":["../../../projects/core/browser/speech-recognition/index.ts","../../../projects/core/browser/speech-recognition/signality-core-browser-speech-recognition.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 SpeechRecognitionOptions extends WithInjector {\n /**\n * Language for speech recognition.\n * @default 'en-US'\n */\n readonly lang?: MaybeSignal<string>;\n\n /**\n * Whether to return interim results.\n * @default false\n */\n readonly interimResults?: boolean;\n\n /**\n * Whether to continue recognition after speech ends.\n * @default false\n */\n readonly continuous?: boolean;\n\n /**\n * Maximum number of alternative transcripts.\n * @default 1\n */\n readonly maxAlternatives?: number;\n}\n\nexport interface SpeechRecognitionRef {\n /** Whether Speech Recognition API is supported */\n readonly isSupported: Signal<boolean>;\n\n /** Whether recognition is currently active */\n readonly isListening: Signal<boolean>;\n\n /** Final transcript text */\n readonly text: Signal<string>;\n\n /** Interim transcript text */\n readonly interimText: Signal<string>;\n\n /** Error if recognition failed */\n readonly error: Signal<SpeechRecognitionErrorEvent | Error | null>;\n\n /** Start speech recognition */\n readonly start: () => void;\n\n /** Stop speech recognition */\n readonly stop: () => void;\n\n /** Abort speech recognition */\n readonly abort: () => void;\n}\n\n/**\n * Signal-based wrapper around the [Speech Recognition API](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition).\n *\n * @param options - Optional configuration\n * @returns A SpeechRecognitionRef with isSupported, isListening, text, interimText, error signals and control methods\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * @if (recognition.isSupported()) {\n * <button (click)=\"toggleRecognition()\">\n * {{ recognition.isListening() ? 'Stop' : 'Start' }} Recording\n * </button>\n * <p>{{ recognition.text() }}</p>\n * @if (recognition.interimText()) {\n * <p><em>{{ recognition.interimText() }}</em></p>\n * }\n * }\n * `\n * })\n * class SpeechComponent {\n * readonly recognition = speechRecognition();\n *\n * toggleRecognition() {\n * if (this.recognition.isListening()) {\n * this.recognition.stop();\n * } else {\n * this.recognition.start();\n * }\n * }\n * }\n * ```\n */\nexport function speechRecognition(options?: SpeechRecognitionOptions): SpeechRecognitionRef {\n const { runInContext } = setupContext(options?.injector, speechRecognition);\n\n return runInContext(({ isBrowser, onCleanup }) => {\n const isSupported = constSignal(\n isBrowser && ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)\n );\n\n if (!isSupported()) {\n return {\n isSupported,\n isListening: constSignal(false),\n text: constSignal(''),\n interimText: constSignal(''),\n error: constSignal(null),\n start: NOOP_FN,\n stop: NOOP_FN,\n abort: NOOP_FN,\n };\n }\n\n const SpeechRecognitionClass = window.SpeechRecognition || window.webkitSpeechRecognition;\n\n const recognition = new SpeechRecognitionClass();\n const abortController = new AbortController();\n\n const {\n lang = 'en-US',\n interimResults = false,\n continuous = false,\n maxAlternatives = 1,\n } = options ?? {};\n\n recognition.lang = toValue(lang);\n recognition.continuous = continuous;\n recognition.interimResults = interimResults;\n recognition.maxAlternatives = maxAlternatives;\n\n const isListening = signal(false);\n const text = signal('');\n const interimText = signal('');\n const error = signal<SpeechRecognitionErrorEvent | Error | null>(null);\n\n const handleResult = (event: SpeechRecognitionEvent) => {\n let finalTranscript = '';\n let interimTranscript = '';\n\n for (let i = event.resultIndex; i < event.results.length; i++) {\n const result = event.results[i];\n const transcript = result[0]?.transcript || result.item(0)?.transcript || '';\n\n if (result.isFinal) {\n finalTranscript += transcript;\n } else {\n interimTranscript += transcript;\n }\n }\n\n if (finalTranscript) {\n text.update(t => (t ? t + ' ' : '') + finalTranscript);\n interimText.set('');\n } else if (interimTranscript) {\n interimText.set(interimTranscript);\n }\n };\n\n const handleError = (event: SpeechRecognitionErrorEvent) => {\n error.set(event);\n isListening.set(false);\n };\n\n const handleStart = () => {\n isListening.set(true);\n error.set(null);\n\n if (!continuous) {\n text.set('');\n interimText.set('');\n }\n };\n\n const handleEnd = () => {\n isListening.set(false);\n recognition.lang = toValue(lang);\n };\n\n recognition.onstart = handleStart;\n recognition.onend = handleEnd;\n recognition.onerror = handleError;\n recognition.onresult = handleResult;\n\n const start = () => {\n try {\n if (!untracked(isListening)) {\n recognition.start();\n }\n } catch (err) {\n error.set(err as Error);\n }\n };\n\n const stop = () => {\n if (untracked(isListening)) {\n recognition.stop();\n }\n };\n\n const abort = () => {\n if (untracked(isListening)) {\n recognition.abort();\n }\n };\n\n onCleanup(() => {\n abortController.abort();\n abort();\n });\n\n if (isSignal(lang)) {\n watcher(lang, newLang => {\n if (!isListening()) {\n recognition.lang = newLang;\n }\n });\n }\n\n navigator.permissions.query({ name: 'microphone' }).then(result => {\n if (abortController.signal.aborted) {\n return;\n }\n\n const check = () => {\n if (result.state === 'denied') {\n abort();\n }\n };\n\n check();\n\n result.addEventListener('change', check, {\n signal: abortController.signal,\n });\n });\n\n return {\n isSupported,\n isListening: isListening.asReadonly(),\n text: text.asReadonly(),\n interimText: interimText.asReadonly(),\n error: error.asReadonly(),\n start,\n stop,\n abort,\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAyDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCG;AACG,SAAU,iBAAiB,CAAC,OAAkC,EAAA;AAClE,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,CAAC;IAE3E,OAAO,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,KAAI;AAC/C,QAAA,MAAM,WAAW,GAAG,WAAW,CAC7B,SAAS,KAAK,mBAAmB,IAAI,MAAM,IAAI,yBAAyB,IAAI,MAAM,CAAC,CACpF;AAED,QAAA,IAAI,CAAC,WAAW,EAAE,EAAE;YAClB,OAAO;gBACL,WAAW;AACX,gBAAA,WAAW,EAAE,WAAW,CAAC,KAAK,CAAC;AAC/B,gBAAA,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;AACrB,gBAAA,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC;AAC5B,gBAAA,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC;AACxB,gBAAA,KAAK,EAAE,OAAO;AACd,gBAAA,IAAI,EAAE,OAAO;AACb,gBAAA,KAAK,EAAE,OAAO;aACf;QACH;QAEA,MAAM,sBAAsB,GAAG,MAAM,CAAC,iBAAiB,IAAI,MAAM,CAAC,uBAAuB;AAEzF,QAAA,MAAM,WAAW,GAAG,IAAI,sBAAsB,EAAE;AAChD,QAAA,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE;QAE7C,MAAM,EACJ,IAAI,GAAG,OAAO,EACd,cAAc,GAAG,KAAK,EACtB,UAAU,GAAG,KAAK,EAClB,eAAe,GAAG,CAAC,GACpB,GAAG,OAAO,IAAI,EAAE;AAEjB,QAAA,WAAW,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;AAChC,QAAA,WAAW,CAAC,UAAU,GAAG,UAAU;AACnC,QAAA,WAAW,CAAC,cAAc,GAAG,cAAc;AAC3C,QAAA,WAAW,CAAC,eAAe,GAAG,eAAe;AAE7C,QAAA,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,uDAAC;AACjC,QAAA,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,gDAAC;AACvB,QAAA,MAAM,WAAW,GAAG,MAAM,CAAC,EAAE,uDAAC;AAC9B,QAAA,MAAM,KAAK,GAAG,MAAM,CAA6C,IAAI,iDAAC;AAEtE,QAAA,MAAM,YAAY,GAAG,CAAC,KAA6B,KAAI;YACrD,IAAI,eAAe,GAAG,EAAE;YACxB,IAAI,iBAAiB,GAAG,EAAE;AAE1B,YAAA,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBAC7D,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AAC/B,gBAAA,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,IAAI,EAAE;AAE5E,gBAAA,IAAI,MAAM,CAAC,OAAO,EAAE;oBAClB,eAAe,IAAI,UAAU;gBAC/B;qBAAO;oBACL,iBAAiB,IAAI,UAAU;gBACjC;YACF;YAEA,IAAI,eAAe,EAAE;gBACnB,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,EAAE,IAAI,eAAe,CAAC;AACtD,gBAAA,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB;iBAAO,IAAI,iBAAiB,EAAE;AAC5B,gBAAA,WAAW,CAAC,GAAG,CAAC,iBAAiB,CAAC;YACpC;AACF,QAAA,CAAC;AAED,QAAA,MAAM,WAAW,GAAG,CAAC,KAAkC,KAAI;AACzD,YAAA,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;AAChB,YAAA,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;AACxB,QAAA,CAAC;QAED,MAAM,WAAW,GAAG,MAAK;AACvB,YAAA,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;AACrB,YAAA,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YAEf,IAAI,CAAC,UAAU,EAAE;AACf,gBAAA,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AACZ,gBAAA,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB;AACF,QAAA,CAAC;QAED,MAAM,SAAS,GAAG,MAAK;AACrB,YAAA,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;AACtB,YAAA,WAAW,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;AAClC,QAAA,CAAC;AAED,QAAA,WAAW,CAAC,OAAO,GAAG,WAAW;AACjC,QAAA,WAAW,CAAC,KAAK,GAAG,SAAS;AAC7B,QAAA,WAAW,CAAC,OAAO,GAAG,WAAW;AACjC,QAAA,WAAW,CAAC,QAAQ,GAAG,YAAY;QAEnC,MAAM,KAAK,GAAG,MAAK;AACjB,YAAA,IAAI;AACF,gBAAA,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE;oBAC3B,WAAW,CAAC,KAAK,EAAE;gBACrB;YACF;YAAE,OAAO,GAAG,EAAE;AACZ,gBAAA,KAAK,CAAC,GAAG,CAAC,GAAY,CAAC;YACzB;AACF,QAAA,CAAC;QAED,MAAM,IAAI,GAAG,MAAK;AAChB,YAAA,IAAI,SAAS,CAAC,WAAW,CAAC,EAAE;gBAC1B,WAAW,CAAC,IAAI,EAAE;YACpB;AACF,QAAA,CAAC;QAED,MAAM,KAAK,GAAG,MAAK;AACjB,YAAA,IAAI,SAAS,CAAC,WAAW,CAAC,EAAE;gBAC1B,WAAW,CAAC,KAAK,EAAE;YACrB;AACF,QAAA,CAAC;QAED,SAAS,CAAC,MAAK;YACb,eAAe,CAAC,KAAK,EAAE;AACvB,YAAA,KAAK,EAAE;AACT,QAAA,CAAC,CAAC;AAEF,QAAA,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE;AAClB,YAAA,OAAO,CAAC,IAAI,EAAE,OAAO,IAAG;AACtB,gBAAA,IAAI,CAAC,WAAW,EAAE,EAAE;AAClB,oBAAA,WAAW,CAAC,IAAI,GAAG,OAAO;gBAC5B;AACF,YAAA,CAAC,CAAC;QACJ;AAEA,QAAA,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,IAAG;AAChE,YAAA,IAAI,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE;gBAClC;YACF;YAEA,MAAM,KAAK,GAAG,MAAK;AACjB,gBAAA,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE;AAC7B,oBAAA,KAAK,EAAE;gBACT;AACF,YAAA,CAAC;AAED,YAAA,KAAK,EAAE;AAEP,YAAA,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,KAAK,EAAE;gBACvC,MAAM,EAAE,eAAe,CAAC,MAAM;AAC/B,aAAA,CAAC;AACJ,QAAA,CAAC,CAAC;QAEF,OAAO;YACL,WAAW;AACX,YAAA,WAAW,EAAE,WAAW,CAAC,UAAU,EAAE;AACrC,YAAA,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;AACvB,YAAA,WAAW,EAAE,WAAW,CAAC,UAAU,EAAE;AACrC,YAAA,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE;YACzB,KAAK;YACL,IAAI;YACJ,KAAK;SACN;AACH,IAAA,CAAC,CAAC;AACJ;;ACtPA;;AAEG;;;;"}
|