@signality/core 0.1.1 → 0.1.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.
- package/browser/file-dialog/index.d.ts +5 -3
- package/browser/storage/index.d.ts +1 -1
- package/browser/text-selection/index.d.ts +9 -2
- package/elements/element-focus/index.d.ts +12 -8
- package/fesm2022/signality-core-browser-clipboard.mjs +2 -1
- package/fesm2022/signality-core-browser-clipboard.mjs.map +1 -1
- package/fesm2022/signality-core-browser-favicon.mjs +1 -1
- package/fesm2022/signality-core-browser-favicon.mjs.map +1 -1
- package/fesm2022/signality-core-browser-file-dialog.mjs +3 -2
- package/fesm2022/signality-core-browser-file-dialog.mjs.map +1 -1
- package/fesm2022/signality-core-browser-fullscreen.mjs +2 -1
- package/fesm2022/signality-core-browser-fullscreen.mjs.map +1 -1
- package/fesm2022/signality-core-browser-listener.mjs +2 -1
- package/fesm2022/signality-core-browser-listener.mjs.map +1 -1
- package/fesm2022/signality-core-browser-media-query.mjs +2 -1
- package/fesm2022/signality-core-browser-media-query.mjs.map +1 -1
- package/fesm2022/signality-core-browser-picture-in-picture.mjs +2 -1
- package/fesm2022/signality-core-browser-picture-in-picture.mjs.map +1 -1
- package/fesm2022/signality-core-browser-speech-recognition.mjs +2 -1
- package/fesm2022/signality-core-browser-speech-recognition.mjs.map +1 -1
- package/fesm2022/signality-core-browser-speech-synthesis.mjs +2 -1
- package/fesm2022/signality-core-browser-speech-synthesis.mjs.map +1 -1
- package/fesm2022/signality-core-browser-storage.mjs +40 -45
- package/fesm2022/signality-core-browser-storage.mjs.map +1 -1
- package/fesm2022/signality-core-browser-text-direction.mjs +2 -1
- package/fesm2022/signality-core-browser-text-direction.mjs.map +1 -1
- package/fesm2022/signality-core-browser-text-selection.mjs +36 -4
- package/fesm2022/signality-core-browser-text-selection.mjs.map +1 -1
- package/fesm2022/signality-core-browser-vibration.mjs +2 -1
- package/fesm2022/signality-core-browser-vibration.mjs.map +1 -1
- package/fesm2022/signality-core-browser-web-notification.mjs +2 -1
- package/fesm2022/signality-core-browser-web-notification.mjs.map +1 -1
- package/fesm2022/signality-core-browser-web-worker.mjs +2 -1
- package/fesm2022/signality-core-browser-web-worker.mjs.map +1 -1
- package/fesm2022/signality-core-elements-dropzone.mjs +2 -1
- package/fesm2022/signality-core-elements-dropzone.mjs.map +1 -1
- package/fesm2022/signality-core-elements-element-focus-within.mjs +2 -1
- package/fesm2022/signality-core-elements-element-focus-within.mjs.map +1 -1
- package/fesm2022/signality-core-elements-element-focus.mjs +24 -10
- package/fesm2022/signality-core-elements-element-focus.mjs.map +1 -1
- package/fesm2022/signality-core-elements-element-size.mjs +3 -2
- package/fesm2022/signality-core-elements-element-size.mjs.map +1 -1
- package/fesm2022/signality-core-elements-element-visibility.mjs +2 -6
- package/fesm2022/signality-core-elements-element-visibility.mjs.map +1 -1
- package/fesm2022/signality-core-elements-on-click-outside.mjs +2 -1
- package/fesm2022/signality-core-elements-on-click-outside.mjs.map +1 -1
- package/fesm2022/signality-core-elements-on-disconnect.mjs +2 -1
- package/fesm2022/signality-core-elements-on-disconnect.mjs.map +1 -1
- package/fesm2022/signality-core-elements-on-long-press.mjs +2 -1
- package/fesm2022/signality-core-elements-on-long-press.mjs.map +1 -1
- package/fesm2022/signality-core-elements-scroll-position.mjs +2 -1
- package/fesm2022/signality-core-elements-scroll-position.mjs.map +1 -1
- package/fesm2022/signality-core-internal.mjs +2 -26
- package/fesm2022/signality-core-internal.mjs.map +1 -1
- package/fesm2022/signality-core-observers-intersection-observer.mjs +2 -1
- package/fesm2022/signality-core-observers-intersection-observer.mjs.map +1 -1
- package/fesm2022/signality-core-observers-mutation-observer.mjs +2 -1
- package/fesm2022/signality-core-observers-mutation-observer.mjs.map +1 -1
- package/fesm2022/signality-core-observers-resize-observer.mjs +2 -1
- package/fesm2022/signality-core-observers-resize-observer.mjs.map +1 -1
- package/fesm2022/signality-core-reactivity-debounced.mjs +2 -1
- package/fesm2022/signality-core-reactivity-debounced.mjs.map +1 -1
- package/fesm2022/signality-core-reactivity-throttled.mjs +2 -1
- package/fesm2022/signality-core-reactivity-throttled.mjs.map +1 -1
- package/fesm2022/signality-core-scheduling-debounce-callback.mjs +2 -1
- package/fesm2022/signality-core-scheduling-debounce-callback.mjs.map +1 -1
- package/fesm2022/signality-core-scheduling-interval.mjs +2 -1
- package/fesm2022/signality-core-scheduling-interval.mjs.map +1 -1
- package/fesm2022/signality-core-scheduling-throttle-callback.mjs +2 -1
- package/fesm2022/signality-core-scheduling-throttle-callback.mjs.map +1 -1
- package/fesm2022/signality-core-utilities.mjs +64 -0
- package/fesm2022/signality-core-utilities.mjs.map +1 -0
- package/fesm2022/signality-core.mjs +1 -0
- package/fesm2022/signality-core.mjs.map +1 -1
- package/index.d.ts +1 -0
- package/internal/utils/index.d.ts +0 -2
- package/package.json +9 -5
- package/utilities/generate-id.d.ts +29 -0
- package/utilities/index.d.ts +3 -0
- /package/{internal/utils → utilities}/to-element.d.ts +0 -0
- /package/{internal/utils → utilities}/to-value.d.ts +0 -0
|
@@ -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 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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"}
|
|
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 { constSignal, isAcceptedFile, isNodeWithin, setupContext } from '@signality/core/internal';\nimport { toElement, toValue } from '@signality/core/utilities';\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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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { signal } from '@angular/core';
|
|
2
|
-
import { setupContext, constSignal
|
|
2
|
+
import { setupContext, constSignal } from '@signality/core/internal';
|
|
3
|
+
import { toElement } from '@signality/core/utilities';
|
|
3
4
|
import { listener } from '@signality/core/browser/listener';
|
|
4
5
|
import { onDisconnect } from '@signality/core/elements/on-disconnect';
|
|
5
6
|
|
|
@@ -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
|
|
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 } from '@signality/core/internal';\nimport { toElement } from '@signality/core/utilities';\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":";;;;;;AASA;;;;;;;;;;;;;;;;;;;;;;;;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;;AC/DA;;AAEG;;;;"}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { signal } from '@angular/core';
|
|
2
|
-
import { setupContext,
|
|
2
|
+
import { setupContext, proxySignal } from '@signality/core/internal';
|
|
3
|
+
import { toElement } from '@signality/core/utilities';
|
|
3
4
|
import { listener } from '@signality/core/browser/listener';
|
|
4
5
|
import { onDisconnect } from '@signality/core/elements/on-disconnect';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Reactive tracking of focus state on an element.
|
|
8
|
-
* Detects when an element gains or loses focus.
|
|
9
|
+
* Detects when an element gains or loses focus, and allows programmatically setting focus.
|
|
9
10
|
*
|
|
10
11
|
* @param target - The element to track focus state on
|
|
11
|
-
* @param options - Optional configuration including focusVisible
|
|
12
|
-
* @returns A signal that is `true` when the element has focus
|
|
12
|
+
* @param options - Optional configuration including focusVisible, preventScroll and injector
|
|
13
|
+
* @returns A writable signal that is `true` when the element has focus
|
|
13
14
|
*
|
|
14
15
|
* @example
|
|
15
16
|
* ```typescript
|
|
16
17
|
* @Component({
|
|
17
18
|
* template: `
|
|
18
19
|
* <input #input [class.focused]="isFocused()" />
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* }
|
|
20
|
+
* <button (click)="isFocused.set(true)">Focus Input</button>
|
|
21
|
+
* <button (click)="isFocused.set(false)">Blur Input</button>
|
|
22
22
|
* `
|
|
23
23
|
* })
|
|
24
24
|
* export class FocusDemo {
|
|
@@ -31,9 +31,10 @@ function elementFocus(target, options) {
|
|
|
31
31
|
const { runInContext } = setupContext(options?.injector, elementFocus);
|
|
32
32
|
return runInContext(({ isServer }) => {
|
|
33
33
|
if (isServer) {
|
|
34
|
-
return
|
|
34
|
+
return signal(false, options);
|
|
35
35
|
}
|
|
36
36
|
const focusVisible = options?.focusVisible ?? false;
|
|
37
|
+
const preventScroll = options?.preventScroll ?? false;
|
|
37
38
|
const focused = signal(false, options);
|
|
38
39
|
listener(target, 'focus', e => {
|
|
39
40
|
focused.set(focusVisible ? e.target.matches(':focus-visible') : true);
|
|
@@ -41,8 +42,21 @@ function elementFocus(target, options) {
|
|
|
41
42
|
listener(target, 'blur', () => {
|
|
42
43
|
focused.set(false);
|
|
43
44
|
});
|
|
44
|
-
onDisconnect(target, () =>
|
|
45
|
-
|
|
45
|
+
onDisconnect(target, () => {
|
|
46
|
+
focused.set(false);
|
|
47
|
+
});
|
|
48
|
+
return proxySignal(focused, {
|
|
49
|
+
set: (value) => {
|
|
50
|
+
const el = toElement(target);
|
|
51
|
+
const hasFocus = el?.matches(':focus') ?? false;
|
|
52
|
+
if (value && !hasFocus) {
|
|
53
|
+
el?.focus({ preventScroll });
|
|
54
|
+
}
|
|
55
|
+
else if (!value && hasFocus) {
|
|
56
|
+
el?.blur();
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
});
|
|
46
60
|
});
|
|
47
61
|
}
|
|
48
62
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signality-core-elements-element-focus.mjs","sources":["../../../projects/core/elements/element-focus/index.ts","../../../projects/core/elements/element-focus/signality-core-elements-element-focus.ts"],"sourcesContent":["import { type CreateSignalOptions, signal, type
|
|
1
|
+
{"version":3,"file":"signality-core-elements-element-focus.mjs","sources":["../../../projects/core/elements/element-focus/index.ts","../../../projects/core/elements/element-focus/signality-core-elements-element-focus.ts"],"sourcesContent":["import { type CreateSignalOptions, signal, type WritableSignal } from '@angular/core';\nimport { proxySignal, setupContext } from '@signality/core/internal';\nimport { toElement } from '@signality/core/utilities';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport interface ElementFocusOptions extends CreateSignalOptions<boolean>, WithInjector {\n /**\n * Track focus using the `:focus-visible` pseudo-class.\n * The browser uses heuristics to determine when focus should be visually indicated\n * (e.g., keyboard navigation, programmatic focus, or when the element requires user attention).\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:focus-visible MDN: :focus-visible}\n * @default false\n */\n readonly focusVisible?: boolean;\n\n /**\n * Prevent scrolling to the element when it is focused.\n * @default false\n */\n readonly preventScroll?: boolean;\n}\n\n/**\n * Reactive tracking of focus state on an element.\n * Detects when an element gains or loses focus, and allows programmatically setting focus.\n *\n * @param target - The element to track focus state on\n * @param options - Optional configuration including focusVisible, preventScroll and injector\n * @returns A writable signal that is `true` when the element has focus\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <input #input [class.focused]=\"isFocused()\" />\n * <button (click)=\"isFocused.set(true)\">Focus Input</button>\n * <button (click)=\"isFocused.set(false)\">Blur Input</button>\n * `\n * })\n * export class FocusDemo {\n * readonly input = viewChild<ElementRef>('input');\n * readonly isFocused = elementFocus(this.input);\n * }\n * ```\n */\nexport function elementFocus(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementFocusOptions\n): WritableSignal<boolean> {\n const { runInContext } = setupContext(options?.injector, elementFocus);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return signal(false, options);\n }\n\n const focusVisible = options?.focusVisible ?? false;\n const preventScroll = options?.preventScroll ?? false;\n\n const focused = signal<boolean>(false, options);\n\n listener(target, 'focus', e => {\n focused.set(focusVisible ? (e.target as HTMLElement).matches(':focus-visible') : true);\n });\n\n listener(target, 'blur', () => {\n focused.set(false);\n });\n\n onDisconnect(target, () => {\n focused.set(false);\n });\n\n return proxySignal(focused, {\n set: (value: boolean) => {\n const el = toElement(target);\n const hasFocus = el?.matches(':focus') ?? false;\n\n if (value && !hasFocus) {\n el?.focus({ preventScroll });\n } else if (!value && hasFocus) {\n el?.blur();\n }\n },\n });\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;AAyBA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACG,SAAU,YAAY,CAC1B,MAAuC,EACvC,OAA6B,EAAA;AAE7B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,CAAC;AAEtE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC;QAC/B;AAEA,QAAA,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,KAAK;AACnD,QAAA,MAAM,aAAa,GAAG,OAAO,EAAE,aAAa,IAAI,KAAK;QAErD,MAAM,OAAO,GAAG,MAAM,CAAU,KAAK,EAAE,OAAO,CAAC;AAE/C,QAAA,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,IAAG;YAC5B,OAAO,CAAC,GAAG,CAAC,YAAY,GAAI,CAAC,CAAC,MAAsB,CAAC,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;AACxF,QAAA,CAAC,CAAC;AAEF,QAAA,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAK;AAC5B,YAAA,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACpB,QAAA,CAAC,CAAC;AAEF,QAAA,YAAY,CAAC,MAAM,EAAE,MAAK;AACxB,YAAA,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;AACpB,QAAA,CAAC,CAAC;QAEF,OAAO,WAAW,CAAC,OAAO,EAAE;AAC1B,YAAA,GAAG,EAAE,CAAC,KAAc,KAAI;AACtB,gBAAA,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC;gBAC5B,MAAM,QAAQ,GAAG,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK;AAE/C,gBAAA,IAAI,KAAK,IAAI,CAAC,QAAQ,EAAE;AACtB,oBAAA,EAAE,EAAE,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;gBAC9B;AAAO,qBAAA,IAAI,CAAC,KAAK,IAAI,QAAQ,EAAE;oBAC7B,EAAE,EAAE,IAAI,EAAE;gBACZ;YACF,CAAC;AACF,SAAA,CAAC;AACJ,IAAA,CAAC,CAAC;AACJ;;ACzFA;;AAEG;;;;"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { signal } from '@angular/core';
|
|
2
|
-
import { setupContext, constSignal
|
|
2
|
+
import { setupContext, constSignal } from '@signality/core/internal';
|
|
3
|
+
import { toValue } from '@signality/core/utilities';
|
|
3
4
|
import { resizeObserver } from '@signality/core/observers/resize-observer';
|
|
4
5
|
import { onDisconnect } from '@signality/core/elements/on-disconnect';
|
|
5
6
|
|
|
@@ -51,7 +52,7 @@ function elementSize(target, options) {
|
|
|
51
52
|
}
|
|
52
53
|
};
|
|
53
54
|
resizeObserver(target, updateSize, options);
|
|
54
|
-
onDisconnect(target, () => size.set(
|
|
55
|
+
onDisconnect(target, () => size.set(DEFAULT_SIZE));
|
|
55
56
|
return size;
|
|
56
57
|
});
|
|
57
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signality-core-elements-element-size.mjs","sources":["../../../projects/core/elements/element-size/index.ts","../../../projects/core/elements/element-size/signality-core-elements-element-size.ts"],"sourcesContent":["import { type CreateSignalOptions, signal, type Signal } from '@angular/core';\nimport { constSignal, setupContext
|
|
1
|
+
{"version":3,"file":"signality-core-elements-element-size.mjs","sources":["../../../projects/core/elements/element-size/index.ts","../../../projects/core/elements/element-size/signality-core-elements-element-size.ts"],"sourcesContent":["import { type CreateSignalOptions, signal, type Signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport { toValue } from '@signality/core/utilities';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\nimport { resizeObserver } from '@signality/core/observers/resize-observer';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport interface ElementSizeValue {\n readonly width: number;\n readonly height: number;\n}\n\nexport interface ElementSizeOptions extends CreateSignalOptions<ElementSizeValue>, WithInjector {\n /**\n * Which box model to observe. Can be a reactive signal.\n *\n * @default 'border-box'\n */\n readonly box?: MaybeSignal<ResizeObserverBoxOptions>;\n\n /**\n * Initial value for SSR and before the first measurement.\n *\n * @default { width: 0, height: 0 }\n */\n readonly initialValue?: ElementSizeValue;\n}\n\n/**\n * Signal-based wrapper around the [ResizeObserver API](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).\n *\n * @param target - The element to observe\n * @param options - Optional configuration including signal options (equal, debugName), box model, and injector\n * @returns A signal containing the current element dimensions\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #box>\n * Size: {{ size().width }} × {{ size().height }}px\n * </div>\n * `\n * })\n * export class ElementSizeDemo {\n * readonly box = viewChild<ElementRef>('box');\n * readonly size = elementSize(this.box);\n * }\n * ```\n */\nexport function elementSize(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementSizeOptions\n): Signal<ElementSizeValue> {\n const { runInContext } = setupContext(options?.injector, elementSize);\n const initialValue = options?.initialValue ?? DEFAULT_SIZE;\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(initialValue);\n }\n\n const size = signal<ElementSizeValue>(initialValue, options);\n\n const updateSize = ([entry]: readonly ResizeObserverEntry[]) => {\n const box = toValue(options?.box) ?? 'border-box';\n\n if (box === 'content-box') {\n const contentBoxSize = entry.contentBoxSize?.[0];\n size.set({\n width: contentBoxSize?.inlineSize ?? entry.contentRect.width,\n height: contentBoxSize?.blockSize ?? entry.contentRect.height,\n });\n } else {\n const borderBoxSize = entry.borderBoxSize?.[0];\n size.set({\n width: borderBoxSize?.inlineSize ?? entry.contentRect.width,\n height: borderBoxSize?.blockSize ?? entry.contentRect.height,\n });\n }\n };\n\n resizeObserver(target, updateSize, options);\n\n onDisconnect(target, () => size.set(DEFAULT_SIZE));\n\n return size;\n });\n}\n\nconst DEFAULT_SIZE: ElementSizeValue = {\n width: 0,\n height: 0,\n};\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;AA4BA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,WAAW,CACzB,MAAuC,EACvC,OAA4B,EAAA;AAE5B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC;AACrE,IAAA,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,YAAY;AAE1D,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,YAAY,CAAC;QAClC;QAEA,MAAM,IAAI,GAAG,MAAM,CAAmB,YAAY,EAAE,OAAO,CAAC;AAE5D,QAAA,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAiC,KAAI;YAC7D,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,YAAY;AAEjD,YAAA,IAAI,GAAG,KAAK,aAAa,EAAE;gBACzB,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC;gBAChD,IAAI,CAAC,GAAG,CAAC;oBACP,KAAK,EAAE,cAAc,EAAE,UAAU,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK;oBAC5D,MAAM,EAAE,cAAc,EAAE,SAAS,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM;AAC9D,iBAAA,CAAC;YACJ;iBAAO;gBACL,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC;gBAC9C,IAAI,CAAC,GAAG,CAAC;oBACP,KAAK,EAAE,aAAa,EAAE,UAAU,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK;oBAC3D,MAAM,EAAE,aAAa,EAAE,SAAS,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM;AAC7D,iBAAA,CAAC;YACJ;AACF,QAAA,CAAC;AAED,QAAA,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC;AAE3C,QAAA,YAAY,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAElD,QAAA,OAAO,IAAI;AACb,IAAA,CAAC,CAAC;AACJ;AAEA,MAAM,YAAY,GAAqB;AACrC,IAAA,KAAK,EAAE,CAAC;AACR,IAAA,MAAM,EAAE,CAAC;CACV;;AC7FD;;AAEG;;;;"}
|
|
@@ -27,7 +27,7 @@ import { onDisconnect } from '@signality/core/elements/on-disconnect';
|
|
|
27
27
|
*/
|
|
28
28
|
function elementVisibility(target, options) {
|
|
29
29
|
const { runInContext } = setupContext(options?.injector, elementVisibility);
|
|
30
|
-
const initialValue = options?.initialValue ??
|
|
30
|
+
const initialValue = options?.initialValue ?? { isVisible: true, ratio: 1 };
|
|
31
31
|
return runInContext(({ isServer }) => {
|
|
32
32
|
if (isServer) {
|
|
33
33
|
return constSignal(initialValue);
|
|
@@ -59,14 +59,10 @@ function elementVisibility(target, options) {
|
|
|
59
59
|
});
|
|
60
60
|
};
|
|
61
61
|
intersectionObserver(target, update, { threshold, root, rootMargin });
|
|
62
|
-
onDisconnect(target, () => visibility.set(
|
|
62
|
+
onDisconnect(target, () => visibility.set({ isVisible: false, ratio: 0 }));
|
|
63
63
|
return visibility;
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
|
-
const DEFAULT_VISIBILITY = {
|
|
67
|
-
isVisible: true,
|
|
68
|
-
ratio: 1,
|
|
69
|
-
};
|
|
70
66
|
|
|
71
67
|
/**
|
|
72
68
|
* Generated bundle index. Do not edit.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signality-core-elements-element-visibility.mjs","sources":["../../../projects/core/elements/element-visibility/index.ts","../../../projects/core/elements/element-visibility/signality-core-elements-element-visibility.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\nimport { intersectionObserver } from '@signality/core/observers/intersection-observer';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport interface ElementVisibilityOptions\n extends CreateSignalOptions<ElementVisibilityValue>,\n WithInjector {\n /**\n * Fraction of the element that must be visible to trigger a change.\n * A single number or an array of thresholds, each between `0` and `1`.\n *\n * @default 0\n * @see [IntersectionObserver: thresholds on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/thresholds)\n */\n readonly threshold?: MaybeSignal<number | number[]>;\n\n /**\n * Scrollable ancestor used as the viewport for intersection checks.\n * `null` or `undefined` defaults to the browser viewport.\n *\n * @default undefined\n * @see [IntersectionObserver: root on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/root)\n */\n readonly root?: MaybeElementSignal<Element> | Document;\n\n /**\n * CSS margin applied around the root before computing intersections.\n * Accepts values in the same format as the CSS `margin` property (e.g. `'10px 0px'`).\n *\n * @default '0px'\n * @see [IntersectionObserver: rootMargin on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin)\n */\n readonly rootMargin?: MaybeSignal<string>;\n\n /**\n * Initial value for SSR.\n *\n * @default { isVisible: true, ratio: 1 }\n */\n readonly initialValue?: ElementVisibilityValue;\n}\n\nexport interface ElementVisibilityValue {\n /**\n * Whether the element is currently intersecting the root (visible in the viewport).\n *\n * @see [IntersectionObserverEntry: isIntersecting on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry/isIntersecting)\n */\n readonly isVisible: boolean;\n\n /**\n * Fraction of the element visible within the root, from `0.0` (not visible) to `1.0` (fully visible).\n *\n * @see [IntersectionObserverEntry: intersectionRatio on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry/intersectionRatio)\n */\n readonly ratio: number;\n}\n\n/**\n * Signal-based wrapper around the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).\n *\n * @param target - The element to observe\n * @param options - Optional configuration\n * @returns A signal containing the current visibility state\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #section [class.visible]=\"visibility().isVisible\">\n * Visibility: {{ visibility().ratio * 100 }}%\n * </div>\n * `\n * })\n * export class VisibilityDemo {\n * readonly section = viewChild<ElementRef>('section');\n * readonly visibility = elementVisibility(this.section);\n * }\n * ```\n */\nexport function elementVisibility(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementVisibilityOptions\n): Signal<ElementVisibilityValue> {\n const { runInContext } = setupContext(options?.injector, elementVisibility);\n const initialValue = options?.initialValue ??
|
|
1
|
+
{"version":3,"file":"signality-core-elements-element-visibility.mjs","sources":["../../../projects/core/elements/element-visibility/index.ts","../../../projects/core/elements/element-visibility/signality-core-elements-element-visibility.ts"],"sourcesContent":["import { type CreateSignalOptions, type Signal, signal } from '@angular/core';\nimport { constSignal, setupContext } from '@signality/core/internal';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\nimport { intersectionObserver } from '@signality/core/observers/intersection-observer';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\nexport interface ElementVisibilityOptions\n extends CreateSignalOptions<ElementVisibilityValue>,\n WithInjector {\n /**\n * Fraction of the element that must be visible to trigger a change.\n * A single number or an array of thresholds, each between `0` and `1`.\n *\n * @default 0\n * @see [IntersectionObserver: thresholds on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/thresholds)\n */\n readonly threshold?: MaybeSignal<number | number[]>;\n\n /**\n * Scrollable ancestor used as the viewport for intersection checks.\n * `null` or `undefined` defaults to the browser viewport.\n *\n * @default undefined\n * @see [IntersectionObserver: root on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/root)\n */\n readonly root?: MaybeElementSignal<Element> | Document;\n\n /**\n * CSS margin applied around the root before computing intersections.\n * Accepts values in the same format as the CSS `margin` property (e.g. `'10px 0px'`).\n *\n * @default '0px'\n * @see [IntersectionObserver: rootMargin on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin)\n */\n readonly rootMargin?: MaybeSignal<string>;\n\n /**\n * Initial value for SSR.\n *\n * @default { isVisible: true, ratio: 1 }\n */\n readonly initialValue?: ElementVisibilityValue;\n}\n\nexport interface ElementVisibilityValue {\n /**\n * Whether the element is currently intersecting the root (visible in the viewport).\n *\n * @see [IntersectionObserverEntry: isIntersecting on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry/isIntersecting)\n */\n readonly isVisible: boolean;\n\n /**\n * Fraction of the element visible within the root, from `0.0` (not visible) to `1.0` (fully visible).\n *\n * @see [IntersectionObserverEntry: intersectionRatio on MDN](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry/intersectionRatio)\n */\n readonly ratio: number;\n}\n\n/**\n * Signal-based wrapper around the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).\n *\n * @param target - The element to observe\n * @param options - Optional configuration\n * @returns A signal containing the current visibility state\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #section [class.visible]=\"visibility().isVisible\">\n * Visibility: {{ visibility().ratio * 100 }}%\n * </div>\n * `\n * })\n * export class VisibilityDemo {\n * readonly section = viewChild<ElementRef>('section');\n * readonly visibility = elementVisibility(this.section);\n * }\n * ```\n */\nexport function elementVisibility(\n target: MaybeElementSignal<HTMLElement>,\n options?: ElementVisibilityOptions\n): Signal<ElementVisibilityValue> {\n const { runInContext } = setupContext(options?.injector, elementVisibility);\n const initialValue = options?.initialValue ?? { isVisible: true, ratio: 1 };\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return constSignal(initialValue);\n }\n\n const visibility = signal(initialValue, options);\n\n const threshold = options?.threshold ?? 0;\n const root = options?.root ?? undefined;\n const rootMargin = options?.rootMargin ?? '0px';\n\n const update = (entries: readonly IntersectionObserverEntry[]) => {\n if (entries.length === 0) {\n return;\n }\n\n // Find the entry with the latest time to ensure we use the most up-to-date state\n // IntersectionObserver may batch multiple changes and call the callback once\n // with multiple entries, and the order in the array doesn't guarantee\n // that the last entry is the most recent one\n let latestEntry = entries[0];\n let latestTime = entries[0].time;\n\n for (let i = 1; i < entries.length; i++) {\n const entry = entries[i];\n if (entry.time >= latestTime) {\n latestTime = entry.time;\n latestEntry = entry;\n }\n }\n\n visibility.set({\n isVisible: latestEntry.isIntersecting,\n ratio: latestEntry.intersectionRatio,\n });\n };\n\n intersectionObserver(target, update, { threshold, root, rootMargin });\n\n onDisconnect(target, () => visibility.set({ isVisible: false, ratio: 0 }));\n\n return visibility;\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AA4DA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,iBAAiB,CAC/B,MAAuC,EACvC,OAAkC,EAAA;AAElC,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,CAAC;AAC3E,IAAA,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE;AAE3E,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,WAAW,CAAC,YAAY,CAAC;QAClC;QAEA,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC;AAEhD,QAAA,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,CAAC;AACzC,QAAA,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,SAAS;AACvC,QAAA,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,KAAK;AAE/C,QAAA,MAAM,MAAM,GAAG,CAAC,OAA6C,KAAI;AAC/D,YAAA,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;gBACxB;YACF;;;;;AAMA,YAAA,IAAI,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;YAC5B,IAAI,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI;AAEhC,YAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACvC,gBAAA,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC;AACxB,gBAAA,IAAI,KAAK,CAAC,IAAI,IAAI,UAAU,EAAE;AAC5B,oBAAA,UAAU,GAAG,KAAK,CAAC,IAAI;oBACvB,WAAW,GAAG,KAAK;gBACrB;YACF;YAEA,UAAU,CAAC,GAAG,CAAC;gBACb,SAAS,EAAE,WAAW,CAAC,cAAc;gBACrC,KAAK,EAAE,WAAW,CAAC,iBAAiB;AACrC,aAAA,CAAC;AACJ,QAAA,CAAC;AAED,QAAA,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAErE,YAAY,CAAC,MAAM,EAAE,MAAM,UAAU,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;AAE1E,QAAA,OAAO,UAAU;AACnB,IAAA,CAAC,CAAC;AACJ;;ACpIA;;AAEG;;;;"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { setupContext, NOOP_EFFECT_REF
|
|
1
|
+
import { setupContext, NOOP_EFFECT_REF } from '@signality/core/internal';
|
|
2
|
+
import { toElement } from '@signality/core/utilities';
|
|
2
3
|
import { setupSync, listener } from '@signality/core/browser/listener';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signality-core-elements-on-click-outside.mjs","sources":["../../../projects/core/elements/on-click-outside/index.ts","../../../projects/core/elements/on-click-outside/signality-core-elements-on-click-outside.ts"],"sourcesContent":["import { NOOP_EFFECT_REF, setupContext
|
|
1
|
+
{"version":3,"file":"signality-core-elements-on-click-outside.mjs","sources":["../../../projects/core/elements/on-click-outside/index.ts","../../../projects/core/elements/on-click-outside/signality-core-elements-on-click-outside.ts"],"sourcesContent":["import { NOOP_EFFECT_REF, setupContext } from '@signality/core/internal';\nimport { toElement } from '@signality/core/utilities';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { listener, setupSync } from '@signality/core/browser/listener';\n\nexport interface OnClickOutsideOptions extends WithInjector {\n /**\n * Elements that should not trigger the outside click handler.\n */\n readonly ignore?: MaybeElementSignal<Element>[];\n}\n\nexport interface OnClickOutsideRef {\n /** Stops listening for outside clicks. */\n readonly destroy: () => void;\n}\n\n/**\n * Detects clicks outside a target element and invokes a callback.\n * Useful for closing dropdowns, modals, and popovers when the user clicks elsewhere.\n *\n * @param target - Element to detect clicks outside of\n * @param handler - Callback invoked when a click outside the target is detected\n * @param options - Optional configuration including ignore list and injector\n * @returns An OnClickOutsideRef with a destroy method to stop detection\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #dropdown class=\"dropdown\">\n * <p>Dropdown content</p>\n * </div>\n * `\n * })\n * export class Dropdown {\n * readonly dropdown = viewChild<ElementRef>('dropdown');\n * readonly isOpen = signal(true);\n *\n * constructor() {\n * onClickOutside(this.dropdown, () => {\n * this.isOpen.set(false);\n * });\n * }\n * }\n * ```\n */\nexport function onClickOutside(\n target: MaybeElementSignal<HTMLElement>,\n handler: (event: PointerEvent | FocusEvent) => void,\n options?: OnClickOutsideOptions\n): OnClickOutsideRef {\n const { runInContext } = setupContext(options?.injector, onClickOutside);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n const ignoreList = options?.ignore ?? [];\n\n let shouldFire = false;\n\n function isOutside(event: Event): boolean {\n const el = toElement(target);\n const path = event.composedPath();\n\n if (el && path.includes(el)) {\n return false;\n }\n\n if (ignoreList.length) {\n return !ignoreList.some(ignored => {\n const ignoredEl = toElement(ignored);\n return ignoredEl && path.includes(ignoredEl);\n });\n }\n\n return true;\n }\n\n const pointerDownListener = setupSync(() =>\n listener.capture(window, 'pointerdown', (e: PointerEvent) => {\n shouldFire = isOutside(e);\n })\n );\n\n const clickListener = setupSync(() =>\n listener.capture(window, 'pointerup', (e: PointerEvent) => {\n if (!shouldFire) {\n return;\n }\n\n shouldFire = false;\n\n if (!isOutside(e)) {\n return;\n }\n\n handler(e);\n })\n );\n\n const blurListener = setupSync(() =>\n listener(window, 'blur', (e: FocusEvent) => {\n setTimeout(() => {\n const active = document.activeElement;\n\n if (active?.tagName !== 'IFRAME') {\n return;\n }\n\n const el = toElement(target);\n\n if (el?.contains(active)) {\n return;\n }\n\n handler(e);\n }, 0);\n })\n );\n\n return {\n destroy: () => {\n pointerDownListener.destroy();\n clickListener.destroy();\n blurListener.destroy();\n },\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BG;SACa,cAAc,CAC5B,MAAuC,EACvC,OAAmD,EACnD,OAA+B,EAAA;AAE/B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,cAAc,CAAC;AAExE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,eAAe;QACxB;AAEA,QAAA,MAAM,UAAU,GAAG,OAAO,EAAE,MAAM,IAAI,EAAE;QAExC,IAAI,UAAU,GAAG,KAAK;QAEtB,SAAS,SAAS,CAAC,KAAY,EAAA;AAC7B,YAAA,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC;AAC5B,YAAA,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,EAAE;YAEjC,IAAI,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE;AAC3B,gBAAA,OAAO,KAAK;YACd;AAEA,YAAA,IAAI,UAAU,CAAC,MAAM,EAAE;AACrB,gBAAA,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,IAAG;AAChC,oBAAA,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC;oBACpC,OAAO,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;AAC9C,gBAAA,CAAC,CAAC;YACJ;AAEA,YAAA,OAAO,IAAI;QACb;AAEA,QAAA,MAAM,mBAAmB,GAAG,SAAS,CAAC,MACpC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAe,KAAI;AAC1D,YAAA,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC;QAC3B,CAAC,CAAC,CACH;AAED,QAAA,MAAM,aAAa,GAAG,SAAS,CAAC,MAC9B,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAe,KAAI;YACxD,IAAI,CAAC,UAAU,EAAE;gBACf;YACF;YAEA,UAAU,GAAG,KAAK;AAElB,YAAA,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;gBACjB;YACF;YAEA,OAAO,CAAC,CAAC,CAAC;QACZ,CAAC,CAAC,CACH;AAED,QAAA,MAAM,YAAY,GAAG,SAAS,CAAC,MAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAa,KAAI;YACzC,UAAU,CAAC,MAAK;AACd,gBAAA,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa;AAErC,gBAAA,IAAI,MAAM,EAAE,OAAO,KAAK,QAAQ,EAAE;oBAChC;gBACF;AAEA,gBAAA,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC;AAE5B,gBAAA,IAAI,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE;oBACxB;gBACF;gBAEA,OAAO,CAAC,CAAC,CAAC;YACZ,CAAC,EAAE,CAAC,CAAC;QACP,CAAC,CAAC,CACH;QAED,OAAO;YACL,OAAO,EAAE,MAAK;gBACZ,mBAAmB,CAAC,OAAO,EAAE;gBAC7B,aAAa,CAAC,OAAO,EAAE;gBACvB,YAAY,CAAC,OAAO,EAAE;YACxB,CAAC;SACF;AACH,IAAA,CAAC,CAAC;AACJ;;ACnIA;;AAEG;;;;"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { inject, ElementRef, DestroyRef, afterRenderEffect, afterEveryRender } from '@angular/core';
|
|
2
|
-
import { setupContext, NOOP_EFFECT_REF,
|
|
2
|
+
import { setupContext, NOOP_EFFECT_REF, isQuerySignal } from '@signality/core/internal';
|
|
3
|
+
import { toElement } from '@signality/core/utilities';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Executes a callback when an element is disconnected from the DOM.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signality-core-elements-on-disconnect.mjs","sources":["../../../projects/core/elements/on-disconnect/index.ts","../../../projects/core/elements/on-disconnect/signality-core-elements-on-disconnect.ts"],"sourcesContent":["import { afterEveryRender, afterRenderEffect, DestroyRef, ElementRef, inject } from '@angular/core';\nimport { isQuerySignal, NOOP_EFFECT_REF, setupContext
|
|
1
|
+
{"version":3,"file":"signality-core-elements-on-disconnect.mjs","sources":["../../../projects/core/elements/on-disconnect/index.ts","../../../projects/core/elements/on-disconnect/signality-core-elements-on-disconnect.ts"],"sourcesContent":["import { afterEveryRender, afterRenderEffect, DestroyRef, ElementRef, inject } from '@angular/core';\nimport { isQuerySignal, NOOP_EFFECT_REF, setupContext } from '@signality/core/internal';\nimport { toElement } from '@signality/core/utilities';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\n\nexport type OnDisconnectOptions = WithInjector;\n\nexport interface OnDisconnectRef {\n readonly destroy: () => void;\n}\n\n/**\n * Executes a callback when an element is disconnected from the DOM.\n *\n * @param target - The element to watch for disconnection\n * @param callback - Callback to execute when the element is disconnected\n * @param options - Optional configuration including injector\n * @returns OnDisconnectRef with a destroy method to stop watching for disconnection\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <div #box>Content</div>\n * <button (click)=\"remove()\">Remove</button>\n * `\n * })\n * export class OnDisconnectDemo {\n * readonly box = viewChild<ElementRef>('box');\n *\n * constructor() {\n * onDisconnect(this.box, el => {\n * console.log('Element disconnected: ', el.tagName);\n * // perform cleanup without storing the reference\n * });\n * }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Manual cleanup\n * const disconnectRef = onDisconnect(element, () => {\n * console.log('Disconnected');\n * });\n *\n * // Stop watching before disconnection\n * disconnectRef.destroy();\n * ```\n */\nexport function onDisconnect<T extends Element>(\n target: MaybeElementSignal<T>,\n callback: (element: T) => void,\n options?: OnDisconnectOptions\n): OnDisconnectRef {\n const { runInContext } = setupContext(options?.injector, onDisconnect);\n\n return runInContext(({ isServer }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n // (1) Host element check\n // if we are inside a directive and the target element is its host,\n // then we hook into the directive's lifecycle via its DestroyRef\n const hostElRef = inject(ElementRef, { optional: true, self: true });\n if (hostElRef) {\n const targetEl = toElement(target);\n if (targetEl && hostElRef.nativeElement === targetEl) {\n return {\n destroy: inject(DestroyRef).onDestroy(() => callback(targetEl)),\n };\n }\n }\n\n // (2) Query signal check (viewChild/contentChild)\n // if target is a query signal, we rely on its automatic state transition\n // managed by Angular's change detection, calling callback at render completion timing\n if (isQuerySignal(target)) {\n const effectRef = afterRenderEffect({\n read: onCleanup => {\n const targetEl = toElement(target);\n\n if (targetEl) {\n onCleanup(() => {\n if (!targetEl.isConnected) callback(targetEl);\n });\n }\n },\n });\n\n return { destroy: () => effectRef.destroy() };\n }\n\n // (3) Fallback\n // for any DOM target (reactive or non-reactive), we assume the value was\n // manually read from the DOM. Therefore, we perform isConnected check after\n // every render cycle to detect disconnection\n // @TODO: document the behavior of state transitions when the target is reactive\n const afterRenderRef = afterEveryRender({\n read: () => {\n const targetEl = toElement(target);\n if (targetEl && !targetEl.isConnected) callback(targetEl);\n },\n });\n\n return { destroy: () => afterRenderRef.destroy() };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCG;SACa,YAAY,CAC1B,MAA6B,EAC7B,QAA8B,EAC9B,OAA6B,EAAA;AAE7B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,CAAC;AAEtE,IAAA,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAI;QACnC,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,eAAe;QACxB;;;;AAKA,QAAA,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACpE,IAAI,SAAS,EAAE;AACb,YAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC;YAClC,IAAI,QAAQ,IAAI,SAAS,CAAC,aAAa,KAAK,QAAQ,EAAE;gBACpD,OAAO;AACL,oBAAA,OAAO,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;iBAChE;YACH;QACF;;;;AAKA,QAAA,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE;YACzB,MAAM,SAAS,GAAG,iBAAiB,CAAC;gBAClC,IAAI,EAAE,SAAS,IAAG;AAChB,oBAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC;oBAElC,IAAI,QAAQ,EAAE;wBACZ,SAAS,CAAC,MAAK;4BACb,IAAI,CAAC,QAAQ,CAAC,WAAW;gCAAE,QAAQ,CAAC,QAAQ,CAAC;AAC/C,wBAAA,CAAC,CAAC;oBACJ;gBACF,CAAC;AACF,aAAA,CAAC;YAEF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE;QAC/C;;;;;;QAOA,MAAM,cAAc,GAAG,gBAAgB,CAAC;YACtC,IAAI,EAAE,MAAK;AACT,gBAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC;AAClC,gBAAA,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,WAAW;oBAAE,QAAQ,CAAC,QAAQ,CAAC;YAC3D,CAAC;AACF,SAAA,CAAC;QAEF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC,OAAO,EAAE,EAAE;AACpD,IAAA,CAAC,CAAC;AACJ;;AC5GA;;AAEG;;;;"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { setupContext, NOOP_EFFECT_REF
|
|
1
|
+
import { setupContext, NOOP_EFFECT_REF } from '@signality/core/internal';
|
|
2
|
+
import { toValue } from '@signality/core/utilities';
|
|
2
3
|
import { listener } from '@signality/core/browser/listener';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signality-core-elements-on-long-press.mjs","sources":["../../../projects/core/elements/on-long-press/index.ts","../../../projects/core/elements/on-long-press/signality-core-elements-on-long-press.ts"],"sourcesContent":["import { NOOP_EFFECT_REF, setupContext, type Timer
|
|
1
|
+
{"version":3,"file":"signality-core-elements-on-long-press.mjs","sources":["../../../projects/core/elements/on-long-press/index.ts","../../../projects/core/elements/on-long-press/signality-core-elements-on-long-press.ts"],"sourcesContent":["import { NOOP_EFFECT_REF, setupContext, type Timer } from '@signality/core/internal';\nimport { toValue } from '@signality/core/utilities';\nimport type { MaybeElementSignal, MaybeSignal, WithInjector } from '@signality/core/types';\nimport { listener } from '@signality/core/browser/listener';\n\nexport interface OnLongPressOptions extends WithInjector {\n /**\n * Time in ms before the callback is triggered.\n * @default 500\n */\n readonly delay?: MaybeSignal<number>;\n\n /**\n * Maximum distance (in pixels) the pointer can move before cancelling.\n * Set to `false` to disable distance checking.\n * @default 10\n */\n readonly distanceThreshold?: number | false;\n}\n\nexport interface OnLongPressRef {\n readonly destroy: () => void;\n}\n\n/**\n * Detect long press gestures on an element.\n * Calls a callback after a configurable delay if the pointer stays down\n * without moving beyond the distance threshold.\n *\n * @param target - The element to detect long presses on\n * @param handler - Callback invoked when a long press is detected\n * @param options - Optional configuration including delay, distance threshold, and injector\n * @returns A OnLongPressRef with a destroy method to stop detection\n *\n * @example\n * ```typescript\n * @Component({\n * template: `<button #btn>Hold me</button>`\n * })\n * export class OnLongPressDemo {\n * readonly btn = viewChild<ElementRef>('btn');\n *\n * constructor() {\n * onLongPress(this.btn, () => {\n * console.log('Long press detected!');\n * });\n * }\n * }\n * ```\n */\nexport function onLongPress(\n target: MaybeElementSignal<HTMLElement>,\n handler: (event: PointerEvent) => void,\n options?: OnLongPressOptions\n): OnLongPressRef {\n const { runInContext } = setupContext(options?.injector, onLongPress);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return NOOP_EFFECT_REF;\n }\n\n const distanceThreshold = options?.distanceThreshold;\n\n let startX = 0;\n let startY = 0;\n let longPressTimeout: Timer;\n\n function abortPendingPress(): void {\n clearTimeout(longPressTimeout);\n longPressTimeout = undefined;\n }\n\n const downListener = listener(target, 'pointerdown', (e: PointerEvent) => {\n startX = e.clientX;\n startY = e.clientY;\n\n const delay = toValue(options?.delay) ?? 500;\n\n longPressTimeout = setTimeout(() => {\n handler(e);\n longPressTimeout = undefined;\n }, delay);\n });\n\n const moveListener = listener(target, 'pointermove', (e: PointerEvent) => {\n if (!longPressTimeout || distanceThreshold === false) {\n return;\n }\n\n const threshold = distanceThreshold ?? 10;\n const dx = e.clientX - startX;\n const dy = e.clientY - startY;\n\n if (Math.sqrt(dx * dx + dy * dy) > threshold) {\n abortPendingPress();\n }\n });\n\n const upListener = listener(target, 'pointerup', abortPendingPress);\n const leaveListener = listener(target, 'pointerleave', abortPendingPress);\n\n onCleanup(abortPendingPress);\n\n return {\n destroy: () => {\n abortPendingPress();\n upListener.destroy();\n downListener.destroy();\n moveListener.destroy();\n leaveListener.destroy();\n },\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAwBA;;;;;;;;;;;;;;;;;;;;;;;;;AAyBG;SACa,WAAW,CACzB,MAAuC,EACvC,OAAsC,EACtC,OAA4B,EAAA;AAE5B,IAAA,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC;IAErE,OAAO,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAI;QAC9C,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,eAAe;QACxB;AAEA,QAAA,MAAM,iBAAiB,GAAG,OAAO,EAAE,iBAAiB;QAEpD,IAAI,MAAM,GAAG,CAAC;QACd,IAAI,MAAM,GAAG,CAAC;AACd,QAAA,IAAI,gBAAuB;AAE3B,QAAA,SAAS,iBAAiB,GAAA;YACxB,YAAY,CAAC,gBAAgB,CAAC;YAC9B,gBAAgB,GAAG,SAAS;QAC9B;QAEA,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAe,KAAI;AACvE,YAAA,MAAM,GAAG,CAAC,CAAC,OAAO;AAClB,YAAA,MAAM,GAAG,CAAC,CAAC,OAAO;YAElB,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,GAAG;AAE5C,YAAA,gBAAgB,GAAG,UAAU,CAAC,MAAK;gBACjC,OAAO,CAAC,CAAC,CAAC;gBACV,gBAAgB,GAAG,SAAS;YAC9B,CAAC,EAAE,KAAK,CAAC;AACX,QAAA,CAAC,CAAC;QAEF,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAe,KAAI;AACvE,YAAA,IAAI,CAAC,gBAAgB,IAAI,iBAAiB,KAAK,KAAK,EAAE;gBACpD;YACF;AAEA,YAAA,MAAM,SAAS,GAAG,iBAAiB,IAAI,EAAE;AACzC,YAAA,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,GAAG,MAAM;AAC7B,YAAA,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,GAAG,MAAM;AAE7B,YAAA,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,SAAS,EAAE;AAC5C,gBAAA,iBAAiB,EAAE;YACrB;AACF,QAAA,CAAC,CAAC;QAEF,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,iBAAiB,CAAC;QACnE,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,iBAAiB,CAAC;QAEzE,SAAS,CAAC,iBAAiB,CAAC;QAE5B,OAAO;YACL,OAAO,EAAE,MAAK;AACZ,gBAAA,iBAAiB,EAAE;gBACnB,UAAU,CAAC,OAAO,EAAE;gBACpB,YAAY,CAAC,OAAO,EAAE;gBACtB,YAAY,CAAC,OAAO,EAAE;gBACtB,aAAa,CAAC,OAAO,EAAE;YACzB,CAAC;SACF;AACH,IAAA,CAAC,CAAC;AACJ;;AClHA;;AAEG;;;;"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { signal } from '@angular/core';
|
|
2
|
-
import { setupContext, constSignal, isWindow
|
|
2
|
+
import { setupContext, constSignal, isWindow } from '@signality/core/internal';
|
|
3
|
+
import { toElement } from '@signality/core/utilities';
|
|
3
4
|
import { throttleCallback } from '@signality/core/scheduling/throttle-callback';
|
|
4
5
|
import { listener } from '@signality/core/browser/listener';
|
|
5
6
|
import { onDisconnect } from '@signality/core/elements/on-disconnect';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signality-core-elements-scroll-position.mjs","sources":["../../../projects/core/elements/scroll-position/index.ts","../../../projects/core/elements/scroll-position/signality-core-elements-scroll-position.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport {\n constSignal,\n isWindow,\n setupContext,\n type Timer,\n toElement,\n} from '@signality/core/internal';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { throttleCallback } from '@signality/core/scheduling/throttle-callback';\nimport { listener } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\n/**\n * State indicating which edges have been reached.\n */\nexport interface ArrivedState {\n readonly top: boolean;\n readonly bottom: boolean;\n readonly left: boolean;\n readonly right: boolean;\n}\n\n/**\n * Current scroll directions.\n */\nexport interface ScrollDirections {\n readonly top: boolean;\n readonly bottom: boolean;\n readonly left: boolean;\n readonly right: boolean;\n}\n\nexport interface ScrollPositionOptions extends WithInjector {\n /**\n * Element or window to track scroll on.\n * @default window\n */\n readonly target?: MaybeElementSignal<Element> | Window;\n\n /**\n * Throttle scroll events in milliseconds.\n * @default 0\n */\n readonly throttle?: number;\n\n /**\n * Offset for arrived detection.\n */\n readonly offset?: {\n readonly top?: number;\n readonly bottom?: number;\n readonly left?: number;\n readonly right?: number;\n };\n}\n\nexport interface ScrollPositionRef {\n /** Horizontal scroll position */\n readonly x: Signal<number>;\n\n /** Vertical scroll position */\n readonly y: Signal<number>;\n\n /** Whether currently scrolling */\n readonly isScrolling: Signal<boolean>;\n\n /** Which edges have been reached */\n readonly arrivedState: Signal<ArrivedState>;\n\n /** Current scroll direction */\n readonly directions: Signal<ScrollDirections>;\n}\n\n/**\n * Reactive tracking of scroll position.\n * Track scroll offset of window or any scrollable element.\n *\n * @param options - Optional configuration\n * @returns An object with x, y, isScrolling, arrivedState, and directions signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Scroll Y: {{ scrollPos.y() }}px</p>\n * @if (scrollPos.isScrolling()) {\n * <p>Scrolling...</p>\n * }\n * @if (scrollPos.arrivedState().bottom) {\n * <p>Reached bottom!</p>\n * }\n * `\n * })\n * export class ScrollTracker {\n * readonly scrollPos = scrollPosition();\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Track scroll on a specific element\n * @Component({\n * template: `\n * <div #scrollable style=\"overflow: auto; height: 200px;\">\n * <div style=\"height: 1000px;\">Long content</div>\n * </div>\n * <p>Scroll position: {{ pos.y() }}</p>\n * `\n * })\n * export class ScrollableComponent {\n * readonly scrollableEl = viewChild<ElementRef>('scrollable');\n * readonly pos = scrollPosition({ target: this.scrollableEl });\n * }\n * ```\n */\nexport function scrollPosition(options?: ScrollPositionOptions): ScrollPositionRef {\n const { runInContext } = setupContext(options?.injector, scrollPosition);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return {\n x: constSignal(0),\n y: constSignal(0),\n isScrolling: constSignal(false),\n arrivedState: constSignal(DEFAULT_ARRIVED),\n directions: constSignal(DEFAULT_DIRECTIONS),\n };\n }\n\n const target = options?.target ?? window;\n const targetIsWindow = isWindow(target);\n const offset = options?.offset ?? {};\n const throttleMs = options?.throttle ?? 0;\n\n const x = signal(0);\n const y = signal(0);\n const isScrolling = signal(false);\n const arrivedState = signal(DEFAULT_ARRIVED);\n const directions = signal(DEFAULT_DIRECTIONS);\n\n let lastX = 0;\n let lastY = 0;\n let scrollingTimeout: Timer;\n\n const getScrollPosition = (): { scrollX: number; scrollY: number } => {\n if (targetIsWindow) {\n return { scrollX: target.scrollX, scrollY: target.scrollY };\n }\n\n const el = toElement(target);\n return {\n scrollX: el?.scrollLeft || 0,\n scrollY: el?.scrollTop || 0,\n };\n };\n\n const getScrollSize = (): {\n scrollWidth: number;\n scrollHeight: number;\n clientWidth: number;\n clientHeight: number;\n } => {\n if (targetIsWindow) {\n return {\n scrollWidth: document.documentElement.scrollWidth,\n scrollHeight: document.documentElement.scrollHeight,\n clientWidth: target.innerWidth,\n clientHeight: target.innerHeight,\n };\n }\n\n const el = toElement(target);\n return {\n scrollWidth: el?.scrollWidth || 0,\n scrollHeight: el?.scrollHeight || 0,\n clientWidth: el?.clientWidth || 0,\n clientHeight: el?.clientHeight || 0,\n };\n };\n\n const update = () => {\n const { scrollX, scrollY } = getScrollPosition();\n const { scrollWidth, scrollHeight, clientWidth, clientHeight } = getScrollSize();\n\n directions.set({\n top: scrollY < lastY,\n bottom: scrollY > lastY,\n left: scrollX < lastX,\n right: scrollX > lastX,\n });\n\n lastX = scrollX;\n lastY = scrollY;\n\n x.set(scrollX);\n y.set(scrollY);\n\n const topOffset = offset.top ?? 0;\n const bottomOffset = offset.bottom ?? 0;\n const leftOffset = offset.left ?? 0;\n const rightOffset = offset.right ?? 0;\n\n arrivedState.set({\n top: scrollY <= topOffset,\n bottom: scrollY + clientHeight >= scrollHeight - bottomOffset,\n left: scrollX <= leftOffset,\n right: scrollX + clientWidth >= scrollWidth - rightOffset,\n });\n\n isScrolling.set(true);\n\n if (scrollingTimeout) {\n clearTimeout(scrollingTimeout);\n }\n\n scrollingTimeout = setTimeout(() => {\n isScrolling.set(false);\n }, SCROLL_IDLE_DELAY);\n };\n\n listener(target, 'scroll', throttleMs > 0 ? throttleCallback(update, throttleMs) : update);\n\n if (!targetIsWindow) {\n onDisconnect(target, () => {\n x.set(0);\n y.set(0);\n isScrolling.set(false);\n arrivedState.set(DEFAULT_ARRIVED);\n directions.set(DEFAULT_DIRECTIONS);\n });\n }\n\n onCleanup(() => {\n if (scrollingTimeout) {\n clearTimeout(scrollingTimeout);\n }\n });\n\n update();\n\n return {\n x: x.asReadonly(),\n y: y.asReadonly(),\n isScrolling: isScrolling.asReadonly(),\n arrivedState: arrivedState.asReadonly(),\n directions: directions.asReadonly(),\n };\n });\n}\n\nconst DEFAULT_ARRIVED: ArrivedState = {\n top: true,\n bottom: false,\n left: true,\n right: false,\n};\n\nconst DEFAULT_DIRECTIONS: ScrollDirections = {\n top: false,\n bottom: false,\n left: false,\n right: false,\n};\n\nconst SCROLL_IDLE_DELAY = 150;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"signality-core-elements-scroll-position.mjs","sources":["../../../projects/core/elements/scroll-position/index.ts","../../../projects/core/elements/scroll-position/signality-core-elements-scroll-position.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport { constSignal, isWindow, setupContext, type Timer } from '@signality/core/internal';\nimport { toElement } from '@signality/core/utilities';\nimport type { MaybeElementSignal, WithInjector } from '@signality/core/types';\nimport { throttleCallback } from '@signality/core/scheduling/throttle-callback';\nimport { listener } from '@signality/core/browser/listener';\nimport { onDisconnect } from '@signality/core/elements/on-disconnect';\n\n/**\n * State indicating which edges have been reached.\n */\nexport interface ArrivedState {\n readonly top: boolean;\n readonly bottom: boolean;\n readonly left: boolean;\n readonly right: boolean;\n}\n\n/**\n * Current scroll directions.\n */\nexport interface ScrollDirections {\n readonly top: boolean;\n readonly bottom: boolean;\n readonly left: boolean;\n readonly right: boolean;\n}\n\nexport interface ScrollPositionOptions extends WithInjector {\n /**\n * Element or window to track scroll on.\n * @default window\n */\n readonly target?: MaybeElementSignal<Element> | Window;\n\n /**\n * Throttle scroll events in milliseconds.\n * @default 0\n */\n readonly throttle?: number;\n\n /**\n * Offset for arrived detection.\n */\n readonly offset?: {\n readonly top?: number;\n readonly bottom?: number;\n readonly left?: number;\n readonly right?: number;\n };\n}\n\nexport interface ScrollPositionRef {\n /** Horizontal scroll position */\n readonly x: Signal<number>;\n\n /** Vertical scroll position */\n readonly y: Signal<number>;\n\n /** Whether currently scrolling */\n readonly isScrolling: Signal<boolean>;\n\n /** Which edges have been reached */\n readonly arrivedState: Signal<ArrivedState>;\n\n /** Current scroll direction */\n readonly directions: Signal<ScrollDirections>;\n}\n\n/**\n * Reactive tracking of scroll position.\n * Track scroll offset of window or any scrollable element.\n *\n * @param options - Optional configuration\n * @returns An object with x, y, isScrolling, arrivedState, and directions signals\n *\n * @example\n * ```typescript\n * @Component({\n * template: `\n * <p>Scroll Y: {{ scrollPos.y() }}px</p>\n * @if (scrollPos.isScrolling()) {\n * <p>Scrolling...</p>\n * }\n * @if (scrollPos.arrivedState().bottom) {\n * <p>Reached bottom!</p>\n * }\n * `\n * })\n * export class ScrollTracker {\n * readonly scrollPos = scrollPosition();\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Track scroll on a specific element\n * @Component({\n * template: `\n * <div #scrollable style=\"overflow: auto; height: 200px;\">\n * <div style=\"height: 1000px;\">Long content</div>\n * </div>\n * <p>Scroll position: {{ pos.y() }}</p>\n * `\n * })\n * export class ScrollableComponent {\n * readonly scrollableEl = viewChild<ElementRef>('scrollable');\n * readonly pos = scrollPosition({ target: this.scrollableEl });\n * }\n * ```\n */\nexport function scrollPosition(options?: ScrollPositionOptions): ScrollPositionRef {\n const { runInContext } = setupContext(options?.injector, scrollPosition);\n\n return runInContext(({ isServer, onCleanup }) => {\n if (isServer) {\n return {\n x: constSignal(0),\n y: constSignal(0),\n isScrolling: constSignal(false),\n arrivedState: constSignal(DEFAULT_ARRIVED),\n directions: constSignal(DEFAULT_DIRECTIONS),\n };\n }\n\n const target = options?.target ?? window;\n const targetIsWindow = isWindow(target);\n const offset = options?.offset ?? {};\n const throttleMs = options?.throttle ?? 0;\n\n const x = signal(0);\n const y = signal(0);\n const isScrolling = signal(false);\n const arrivedState = signal(DEFAULT_ARRIVED);\n const directions = signal(DEFAULT_DIRECTIONS);\n\n let lastX = 0;\n let lastY = 0;\n let scrollingTimeout: Timer;\n\n const getScrollPosition = (): { scrollX: number; scrollY: number } => {\n if (targetIsWindow) {\n return { scrollX: target.scrollX, scrollY: target.scrollY };\n }\n\n const el = toElement(target);\n return {\n scrollX: el?.scrollLeft || 0,\n scrollY: el?.scrollTop || 0,\n };\n };\n\n const getScrollSize = (): {\n scrollWidth: number;\n scrollHeight: number;\n clientWidth: number;\n clientHeight: number;\n } => {\n if (targetIsWindow) {\n return {\n scrollWidth: document.documentElement.scrollWidth,\n scrollHeight: document.documentElement.scrollHeight,\n clientWidth: target.innerWidth,\n clientHeight: target.innerHeight,\n };\n }\n\n const el = toElement(target);\n return {\n scrollWidth: el?.scrollWidth || 0,\n scrollHeight: el?.scrollHeight || 0,\n clientWidth: el?.clientWidth || 0,\n clientHeight: el?.clientHeight || 0,\n };\n };\n\n const update = () => {\n const { scrollX, scrollY } = getScrollPosition();\n const { scrollWidth, scrollHeight, clientWidth, clientHeight } = getScrollSize();\n\n directions.set({\n top: scrollY < lastY,\n bottom: scrollY > lastY,\n left: scrollX < lastX,\n right: scrollX > lastX,\n });\n\n lastX = scrollX;\n lastY = scrollY;\n\n x.set(scrollX);\n y.set(scrollY);\n\n const topOffset = offset.top ?? 0;\n const bottomOffset = offset.bottom ?? 0;\n const leftOffset = offset.left ?? 0;\n const rightOffset = offset.right ?? 0;\n\n arrivedState.set({\n top: scrollY <= topOffset,\n bottom: scrollY + clientHeight >= scrollHeight - bottomOffset,\n left: scrollX <= leftOffset,\n right: scrollX + clientWidth >= scrollWidth - rightOffset,\n });\n\n isScrolling.set(true);\n\n if (scrollingTimeout) {\n clearTimeout(scrollingTimeout);\n }\n\n scrollingTimeout = setTimeout(() => {\n isScrolling.set(false);\n }, SCROLL_IDLE_DELAY);\n };\n\n listener(target, 'scroll', throttleMs > 0 ? throttleCallback(update, throttleMs) : update);\n\n if (!targetIsWindow) {\n onDisconnect(target, () => {\n x.set(0);\n y.set(0);\n isScrolling.set(false);\n arrivedState.set(DEFAULT_ARRIVED);\n directions.set(DEFAULT_DIRECTIONS);\n });\n }\n\n onCleanup(() => {\n if (scrollingTimeout) {\n clearTimeout(scrollingTimeout);\n }\n });\n\n update();\n\n return {\n x: x.asReadonly(),\n y: y.asReadonly(),\n isScrolling: isScrolling.asReadonly(),\n arrivedState: arrivedState.asReadonly(),\n directions: directions.asReadonly(),\n };\n });\n}\n\nconst DEFAULT_ARRIVED: ArrivedState = {\n top: true,\n bottom: false,\n left: true,\n right: false,\n};\n\nconst DEFAULT_DIRECTIONS: ScrollDirections = {\n top: false,\n bottom: false,\n left: false,\n right: false,\n};\n\nconst SCROLL_IDLE_DELAY = 150;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -118,7 +118,7 @@ function assertElement(value, source) {
|
|
|
118
118
|
}
|
|
119
119
|
function assertEventTarget(value, source) {
|
|
120
120
|
if (!isEventTarget(value)) {
|
|
121
|
-
throw new Error(`[${source}] Expected an EventTarget
|
|
121
|
+
throw new Error(`[${source}] Expected an EventTarget, ElementRef but received: ${value.constructor?.name ?? typeof value}. ` +
|
|
122
122
|
`If you are using viewChild/contentChild, specify "{ read: ElementRef }" to avoid implicit directive references.`);
|
|
123
123
|
}
|
|
124
124
|
}
|
|
@@ -250,34 +250,10 @@ function proxySignal(source, handler) {
|
|
|
250
250
|
return proxy;
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
// @TODO: Consider moving it out of internal
|
|
254
|
-
const toValue = (() => {
|
|
255
|
-
const fn = toValueFn;
|
|
256
|
-
fn.untracked = v => toValueFn(v, true);
|
|
257
|
-
return fn;
|
|
258
|
-
})();
|
|
259
|
-
function toValueFn(maybeSignal, untracked$1 = false) {
|
|
260
|
-
if (isSignal(maybeSignal)) {
|
|
261
|
-
return untracked$1 ? untracked(maybeSignal) : maybeSignal();
|
|
262
|
-
}
|
|
263
|
-
return maybeSignal;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
253
|
function unrefElement(value) {
|
|
267
254
|
return value instanceof ElementRef ? value.nativeElement : value;
|
|
268
255
|
}
|
|
269
256
|
|
|
270
|
-
// @TODO: Consider moving it out of internal
|
|
271
|
-
const toElement = (() => {
|
|
272
|
-
const fn = toElementFn;
|
|
273
|
-
fn.untracked = v => toElementFn(v, true);
|
|
274
|
-
return fn;
|
|
275
|
-
})();
|
|
276
|
-
function toElementFn(maybeSignal, untracked = false) {
|
|
277
|
-
const raw = untracked ? toValue.untracked(maybeSignal) : toValue(maybeSignal);
|
|
278
|
-
return unrefElement(raw);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
257
|
/**
|
|
282
258
|
* Checks whether a file matches the given accept patterns.
|
|
283
259
|
* Follows the native HTML
|
|
@@ -311,5 +287,5 @@ function isAcceptedFile(file, accept) {
|
|
|
311
287
|
* Generated bundle index. Do not edit.
|
|
312
288
|
*/
|
|
313
289
|
|
|
314
|
-
export { ALWAYS_FALSE_FN, IS_BROWSER, IS_MOBILE, IS_SERVER, MOBILE_REGEX, NOOP_ASYNC_FN, NOOP_EFFECT_REF, NOOP_FN, assertElement, assertEventTarget, constSignal, createToken, getActiveElement, getEventTarget, getPipElement, getShadowRoot, isAcceptedFile, isElement, isEventTarget, isNodeWithin, isPlainObject, isQuerySignal, isWindow, proxySignal, setupContext,
|
|
290
|
+
export { ALWAYS_FALSE_FN, IS_BROWSER, IS_MOBILE, IS_SERVER, MOBILE_REGEX, NOOP_ASYNC_FN, NOOP_EFFECT_REF, NOOP_FN, assertElement, assertEventTarget, constSignal, createToken, getActiveElement, getEventTarget, getPipElement, getShadowRoot, isAcceptedFile, isElement, isEventTarget, isNodeWithin, isPlainObject, isQuerySignal, isWindow, proxySignal, setupContext, unrefElement };
|
|
315
291
|
//# sourceMappingURL=signality-core-internal.mjs.map
|