@bquery/bquery 1.8.1 → 1.9.0
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 +97 -25
- package/dist/{a11y-DVBCy09c.js → a11y-_9X-kt-_.js} +2 -2
- package/dist/{a11y-DVBCy09c.js.map → a11y-_9X-kt-_.js.map} +1 -1
- package/dist/a11y.es.mjs +1 -1
- package/dist/{forms-UcRHsYxC.js → forms-UhAeJEoO.js} +13 -13
- package/dist/{forms-UcRHsYxC.js.map → forms-UhAeJEoO.js.map} +1 -1
- package/dist/forms.es.mjs +1 -1
- package/dist/full.d.ts +4 -4
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +201 -196
- package/dist/full.iife.js +20 -20
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +20 -20
- package/dist/full.umd.js.map +1 -1
- package/dist/index.es.mjs +214 -209
- package/dist/media/index.d.ts +10 -3
- package/dist/media/index.d.ts.map +1 -1
- package/dist/media/observers.d.ts +99 -0
- package/dist/media/observers.d.ts.map +1 -0
- package/dist/media/types.d.ts +125 -0
- package/dist/media/types.d.ts.map +1 -1
- package/dist/media-D4zLj9t-.js +514 -0
- package/dist/media-D4zLj9t-.js.map +1 -0
- package/dist/media.es.mjs +12 -9
- package/dist/mount-B-JvH6Y0.js +449 -0
- package/dist/mount-B-JvH6Y0.js.map +1 -0
- package/dist/reactive/index.d.ts +2 -2
- package/dist/reactive/index.d.ts.map +1 -1
- package/dist/reactive/signal.d.ts +2 -1
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive/watch.d.ts +49 -0
- package/dist/reactive/watch.d.ts.map +1 -1
- package/dist/reactive-BjpLkclt.js +1184 -0
- package/dist/{reactive-DwkhUJfP.js.map → reactive-BjpLkclt.js.map} +1 -1
- package/dist/reactive.es.mjs +35 -33
- package/dist/{router-CQikC9Ed.js → router-BieVwgci.js} +2 -2
- package/dist/{router-CQikC9Ed.js.map → router-BieVwgci.js.map} +1 -1
- package/dist/router.es.mjs +1 -1
- package/dist/{ssr-_dAcGdzu.js → ssr-CrGSJySz.js} +3 -3
- package/dist/{ssr-_dAcGdzu.js.map → ssr-CrGSJySz.js.map} +1 -1
- package/dist/ssr.es.mjs +1 -1
- package/dist/{store-Cb3gPRve.js → store-CY6sjTW3.js} +2 -2
- package/dist/{store-Cb3gPRve.js.map → store-CY6sjTW3.js.map} +1 -1
- package/dist/store.es.mjs +1 -1
- package/dist/{testing-C5Sjfsna.js → testing-UjAtu9aQ.js} +9 -9
- package/dist/{testing-C5Sjfsna.js.map → testing-UjAtu9aQ.js.map} +1 -1
- package/dist/testing.es.mjs +1 -1
- package/dist/view/directives/aria.d.ts +7 -0
- package/dist/view/directives/aria.d.ts.map +1 -0
- package/dist/view/directives/error.d.ts +7 -0
- package/dist/view/directives/error.d.ts.map +1 -0
- package/dist/view/directives/index.d.ts +2 -0
- package/dist/view/directives/index.d.ts.map +1 -1
- package/dist/view/mount.d.ts.map +1 -1
- package/dist/view/process.d.ts +2 -0
- package/dist/view/process.d.ts.map +1 -1
- package/dist/view.es.mjs +2 -2
- package/package.json +7 -6
- package/src/full.ts +12 -0
- package/src/media/index.ts +20 -2
- package/src/media/observers.ts +421 -0
- package/src/media/types.ts +136 -0
- package/src/reactive/index.ts +3 -0
- package/src/reactive/signal.ts +2 -1
- package/src/reactive/watch.ts +137 -0
- package/src/view/directives/aria.ts +72 -0
- package/src/view/directives/error.ts +56 -0
- package/src/view/directives/index.ts +2 -0
- package/src/view/mount.ts +4 -0
- package/src/view/process.ts +6 -0
- package/dist/media-i-fB5WxI.js +0 -340
- package/dist/media-i-fB5WxI.js.map +0 -1
- package/dist/mount-B4Y8bk8Z.js +0 -403
- package/dist/mount-B4Y8bk8Z.js.map +0 -1
- package/dist/reactive-DwkhUJfP.js +0 -1148
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive wrappers for browser Observer APIs.
|
|
3
|
+
*
|
|
4
|
+
* Provides composables for IntersectionObserver, ResizeObserver, and
|
|
5
|
+
* MutationObserver that expose reactive signals updated on every callback.
|
|
6
|
+
*
|
|
7
|
+
* @module bquery/media
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readonly, signal } from '../reactive/index';
|
|
11
|
+
import type {
|
|
12
|
+
IntersectionObserverOptions,
|
|
13
|
+
IntersectionObserverSignal,
|
|
14
|
+
IntersectionObserverState,
|
|
15
|
+
MutationObserverOptions,
|
|
16
|
+
MutationObserverSignal,
|
|
17
|
+
MutationObserverState,
|
|
18
|
+
ResizeObserverOptions,
|
|
19
|
+
ResizeObserverSignal,
|
|
20
|
+
ResizeObserverState,
|
|
21
|
+
} from './types';
|
|
22
|
+
|
|
23
|
+
type ResizeObserverBoxSizeLike = {
|
|
24
|
+
inlineSize: number;
|
|
25
|
+
blockSize: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getResizeDimensions = (
|
|
29
|
+
entry: ResizeObserverEntry,
|
|
30
|
+
box: ResizeObserverBoxOptions
|
|
31
|
+
): Pick<ResizeObserverState, 'width' | 'height'> => {
|
|
32
|
+
let boxSize:
|
|
33
|
+
| ResizeObserverBoxSizeLike
|
|
34
|
+
| readonly ResizeObserverBoxSizeLike[]
|
|
35
|
+
| undefined;
|
|
36
|
+
|
|
37
|
+
if (box === 'border-box') {
|
|
38
|
+
boxSize = entry.borderBoxSize;
|
|
39
|
+
} else if (box === 'device-pixel-content-box') {
|
|
40
|
+
boxSize = (
|
|
41
|
+
entry as ResizeObserverEntry & {
|
|
42
|
+
devicePixelContentBoxSize?:
|
|
43
|
+
| ResizeObserverBoxSizeLike
|
|
44
|
+
| readonly ResizeObserverBoxSizeLike[];
|
|
45
|
+
}
|
|
46
|
+
).devicePixelContentBoxSize;
|
|
47
|
+
} else {
|
|
48
|
+
boxSize = entry.contentBoxSize;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const resolvedBoxSize = Array.isArray(boxSize) ? boxSize[0] : boxSize;
|
|
52
|
+
if (
|
|
53
|
+
resolvedBoxSize &&
|
|
54
|
+
typeof resolvedBoxSize.inlineSize === 'number' &&
|
|
55
|
+
typeof resolvedBoxSize.blockSize === 'number'
|
|
56
|
+
) {
|
|
57
|
+
return {
|
|
58
|
+
width: resolvedBoxSize.inlineSize,
|
|
59
|
+
height: resolvedBoxSize.blockSize,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
width: entry.contentRect.width,
|
|
65
|
+
height: entry.contentRect.height,
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// ─── useIntersectionObserver ────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Returns a reactive signal tracking element intersection with a root viewport.
|
|
73
|
+
*
|
|
74
|
+
* The returned handle exposes `observe()` / `unobserve()` methods so you can
|
|
75
|
+
* control which elements are watched. If an initial `target` is provided it is
|
|
76
|
+
* observed immediately.
|
|
77
|
+
*
|
|
78
|
+
* @param target - Optional element or array of elements to observe immediately.
|
|
79
|
+
* @param options - Standard `IntersectionObserver` init options.
|
|
80
|
+
* @returns A readonly reactive signal with intersection state, plus `observe`,
|
|
81
|
+
* `unobserve`, and `destroy` methods.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* import { useIntersectionObserver } from '@bquery/bquery/media';
|
|
86
|
+
* import { effect } from '@bquery/bquery/reactive';
|
|
87
|
+
*
|
|
88
|
+
* const el = document.querySelector('#lazy-image')!;
|
|
89
|
+
* const io = useIntersectionObserver(el, { threshold: 0.5 });
|
|
90
|
+
*
|
|
91
|
+
* effect(() => {
|
|
92
|
+
* if (io.value.isIntersecting) {
|
|
93
|
+
* console.log('Element is 50% visible');
|
|
94
|
+
* }
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* // Cleanup when done
|
|
98
|
+
* io.destroy();
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export const useIntersectionObserver = (
|
|
102
|
+
target?: Element | Element[] | null,
|
|
103
|
+
options?: IntersectionObserverOptions,
|
|
104
|
+
): IntersectionObserverSignal => {
|
|
105
|
+
const initial: IntersectionObserverState = {
|
|
106
|
+
isIntersecting: false,
|
|
107
|
+
intersectionRatio: 0,
|
|
108
|
+
entry: null,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const s = signal<IntersectionObserverState>(initial);
|
|
112
|
+
let observer: IntersectionObserver | undefined;
|
|
113
|
+
let destroyed = false;
|
|
114
|
+
|
|
115
|
+
if (typeof window !== 'undefined' && typeof IntersectionObserver !== 'undefined') {
|
|
116
|
+
try {
|
|
117
|
+
observer = new IntersectionObserver(
|
|
118
|
+
(entries: IntersectionObserverEntry[]) => {
|
|
119
|
+
if (destroyed) return;
|
|
120
|
+
const last = entries[entries.length - 1];
|
|
121
|
+
if (last) {
|
|
122
|
+
s.value = {
|
|
123
|
+
isIntersecting: last.isIntersecting,
|
|
124
|
+
intersectionRatio: last.intersectionRatio,
|
|
125
|
+
entry: last,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
root: options?.root ?? null,
|
|
131
|
+
rootMargin: options?.rootMargin ?? '0px',
|
|
132
|
+
threshold: options?.threshold ?? 0,
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// Observe initial targets
|
|
137
|
+
if (target) {
|
|
138
|
+
const targets = Array.isArray(target) ? target : [target];
|
|
139
|
+
for (const el of targets) {
|
|
140
|
+
observer.observe(el);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} catch {
|
|
144
|
+
if (observer) observer.disconnect();
|
|
145
|
+
observer = undefined;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const ro = readonly(s) as IntersectionObserverSignal;
|
|
150
|
+
|
|
151
|
+
Object.defineProperties(ro, {
|
|
152
|
+
observe: {
|
|
153
|
+
enumerable: false,
|
|
154
|
+
configurable: true,
|
|
155
|
+
value(el: Element): void {
|
|
156
|
+
if (!destroyed) {
|
|
157
|
+
try {
|
|
158
|
+
observer?.observe(el);
|
|
159
|
+
} catch {}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
unobserve: {
|
|
164
|
+
enumerable: false,
|
|
165
|
+
configurable: true,
|
|
166
|
+
value(el: Element): void {
|
|
167
|
+
if (!destroyed) observer?.unobserve(el);
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
destroy: {
|
|
171
|
+
enumerable: false,
|
|
172
|
+
configurable: true,
|
|
173
|
+
value(): void {
|
|
174
|
+
if (destroyed) return;
|
|
175
|
+
destroyed = true;
|
|
176
|
+
observer?.disconnect();
|
|
177
|
+
observer = undefined;
|
|
178
|
+
s.dispose();
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return ro;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// ─── useResizeObserver ──────────────────────────────────────────────────────
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Returns a reactive signal tracking the size of observed elements.
|
|
190
|
+
*
|
|
191
|
+
* By default it uses the `content-box`, but you can configure the observed box
|
|
192
|
+
* via the underlying `ResizeObserver` options (e.g. `{ box: 'border-box' }`).
|
|
193
|
+
*
|
|
194
|
+
* The returned handle exposes `observe()` / `unobserve()` methods. If an
|
|
195
|
+
* initial `target` is provided it is observed immediately.
|
|
196
|
+
*
|
|
197
|
+
* @param target - Optional element or array of elements to observe immediately.
|
|
198
|
+
* @param options - ResizeObserver options (e.g. `{ box: 'border-box' }`).
|
|
199
|
+
* @returns A readonly reactive signal with `{ width, height, entry }` derived
|
|
200
|
+
* from the configured box, plus `observe`, `unobserve`, and `destroy` methods.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```ts
|
|
204
|
+
* import { useResizeObserver } from '@bquery/bquery/media';
|
|
205
|
+
* import { effect } from '@bquery/bquery/reactive';
|
|
206
|
+
*
|
|
207
|
+
* const el = document.querySelector('#panel')!;
|
|
208
|
+
* const size = useResizeObserver(el);
|
|
209
|
+
*
|
|
210
|
+
* effect(() => {
|
|
211
|
+
* console.log(`Panel size: ${size.value.width}x${size.value.height}`);
|
|
212
|
+
* });
|
|
213
|
+
*
|
|
214
|
+
* size.destroy();
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
export const useResizeObserver = (
|
|
218
|
+
target?: Element | Element[] | null,
|
|
219
|
+
options?: ResizeObserverOptions,
|
|
220
|
+
): ResizeObserverSignal => {
|
|
221
|
+
const initial: ResizeObserverState = {
|
|
222
|
+
width: 0,
|
|
223
|
+
height: 0,
|
|
224
|
+
entry: null,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const s = signal<ResizeObserverState>(initial);
|
|
228
|
+
let observer: ResizeObserver | undefined;
|
|
229
|
+
let destroyed = false;
|
|
230
|
+
const box = options?.box ?? 'content-box';
|
|
231
|
+
const observeOptions = options?.box ? { box: options.box } : undefined;
|
|
232
|
+
|
|
233
|
+
if (typeof window !== 'undefined' && typeof ResizeObserver !== 'undefined') {
|
|
234
|
+
try {
|
|
235
|
+
observer = new ResizeObserver((entries: ResizeObserverEntry[]) => {
|
|
236
|
+
if (destroyed) return;
|
|
237
|
+
const last = entries[entries.length - 1];
|
|
238
|
+
if (last) {
|
|
239
|
+
const { width, height } = getResizeDimensions(last, box);
|
|
240
|
+
s.value = {
|
|
241
|
+
width,
|
|
242
|
+
height,
|
|
243
|
+
entry: last,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Observe initial targets
|
|
249
|
+
if (target) {
|
|
250
|
+
const targets = Array.isArray(target) ? target : [target];
|
|
251
|
+
for (const el of targets) {
|
|
252
|
+
observer.observe(el, observeOptions);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
} catch {
|
|
256
|
+
if (observer) observer.disconnect();
|
|
257
|
+
observer = undefined;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const ro = readonly(s) as ResizeObserverSignal;
|
|
262
|
+
|
|
263
|
+
Object.defineProperties(ro, {
|
|
264
|
+
observe: {
|
|
265
|
+
enumerable: false,
|
|
266
|
+
configurable: true,
|
|
267
|
+
value(el: Element): void {
|
|
268
|
+
if (!destroyed) {
|
|
269
|
+
try {
|
|
270
|
+
observer?.observe(el, observeOptions);
|
|
271
|
+
} catch {}
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
unobserve: {
|
|
276
|
+
enumerable: false,
|
|
277
|
+
configurable: true,
|
|
278
|
+
value(el: Element): void {
|
|
279
|
+
if (!destroyed) observer?.unobserve(el);
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
destroy: {
|
|
283
|
+
enumerable: false,
|
|
284
|
+
configurable: true,
|
|
285
|
+
value(): void {
|
|
286
|
+
if (destroyed) return;
|
|
287
|
+
destroyed = true;
|
|
288
|
+
observer?.disconnect();
|
|
289
|
+
observer = undefined;
|
|
290
|
+
s.dispose();
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
return ro;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// ─── useMutationObserver ────────────────────────────────────────────────────
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Returns a reactive signal tracking DOM mutations on observed nodes.
|
|
302
|
+
*
|
|
303
|
+
* The returned handle exposes `observe()` and `takeRecords()` for manual
|
|
304
|
+
* lifecycle control. If an initial `target` is provided it is observed
|
|
305
|
+
* immediately.
|
|
306
|
+
*
|
|
307
|
+
* @param target - Optional node to observe immediately.
|
|
308
|
+
* @param options - MutationObserver init options. Defaults to `{ attributes: true }`.
|
|
309
|
+
* @returns A readonly reactive signal with `{ mutations, count }`, plus
|
|
310
|
+
* `observe`, `takeRecords`, and `destroy` methods.
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* ```ts
|
|
314
|
+
* import { useMutationObserver } from '@bquery/bquery/media';
|
|
315
|
+
* import { effect } from '@bquery/bquery/reactive';
|
|
316
|
+
*
|
|
317
|
+
* const el = document.querySelector('#dynamic-content')!;
|
|
318
|
+
* const mo = useMutationObserver(el, { childList: true, subtree: true });
|
|
319
|
+
*
|
|
320
|
+
* effect(() => {
|
|
321
|
+
* console.log(`${mo.value.count} mutation batches observed`);
|
|
322
|
+
* });
|
|
323
|
+
*
|
|
324
|
+
* mo.destroy();
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
export const useMutationObserver = (
|
|
328
|
+
target?: Node | null,
|
|
329
|
+
options?: MutationObserverOptions,
|
|
330
|
+
): MutationObserverSignal => {
|
|
331
|
+
const initial: MutationObserverState = {
|
|
332
|
+
mutations: [],
|
|
333
|
+
count: 0,
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const s = signal<MutationObserverState>(initial);
|
|
337
|
+
let observer: MutationObserver | undefined;
|
|
338
|
+
let destroyed = false;
|
|
339
|
+
let totalCount = 0;
|
|
340
|
+
|
|
341
|
+
const resolvedOptions: MutationObserverInit = {
|
|
342
|
+
attributes: options?.attributes ?? true,
|
|
343
|
+
childList: options?.childList ?? false,
|
|
344
|
+
characterData: options?.characterData ?? false,
|
|
345
|
+
subtree: options?.subtree ?? false,
|
|
346
|
+
attributeOldValue: options?.attributeOldValue ?? false,
|
|
347
|
+
characterDataOldValue: options?.characterDataOldValue ?? false,
|
|
348
|
+
...(options && options.attributeFilter !== undefined
|
|
349
|
+
? { attributeFilter: options.attributeFilter }
|
|
350
|
+
: {}),
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
if (!resolvedOptions.attributes && !resolvedOptions.childList && !resolvedOptions.characterData) {
|
|
354
|
+
resolvedOptions.attributes = true;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (!resolvedOptions.attributes) {
|
|
358
|
+
resolvedOptions.attributeOldValue = false;
|
|
359
|
+
delete resolvedOptions.attributeFilter;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (!resolvedOptions.characterData) {
|
|
363
|
+
resolvedOptions.characterDataOldValue = false;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (typeof window !== 'undefined' && typeof MutationObserver !== 'undefined') {
|
|
367
|
+
try {
|
|
368
|
+
observer = new MutationObserver((mutations: MutationRecord[]) => {
|
|
369
|
+
if (destroyed) return;
|
|
370
|
+
totalCount += 1;
|
|
371
|
+
s.value = {
|
|
372
|
+
mutations,
|
|
373
|
+
count: totalCount,
|
|
374
|
+
};
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
if (target) {
|
|
378
|
+
observer.observe(target, resolvedOptions);
|
|
379
|
+
}
|
|
380
|
+
} catch {
|
|
381
|
+
if (observer) observer.disconnect();
|
|
382
|
+
observer = undefined;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const ro = readonly(s) as MutationObserverSignal;
|
|
387
|
+
|
|
388
|
+
Object.defineProperties(ro, {
|
|
389
|
+
observe: {
|
|
390
|
+
enumerable: false,
|
|
391
|
+
configurable: true,
|
|
392
|
+
value(node: Node): void {
|
|
393
|
+
if (!destroyed) {
|
|
394
|
+
try {
|
|
395
|
+
observer?.observe(node, resolvedOptions);
|
|
396
|
+
} catch {}
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
takeRecords: {
|
|
401
|
+
enumerable: false,
|
|
402
|
+
configurable: true,
|
|
403
|
+
value(): MutationRecord[] {
|
|
404
|
+
return observer?.takeRecords() ?? [];
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
destroy: {
|
|
408
|
+
enumerable: false,
|
|
409
|
+
configurable: true,
|
|
410
|
+
value(): void {
|
|
411
|
+
if (destroyed) return;
|
|
412
|
+
destroyed = true;
|
|
413
|
+
observer?.disconnect();
|
|
414
|
+
observer = undefined;
|
|
415
|
+
s.dispose();
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
return ro;
|
|
421
|
+
};
|
package/src/media/types.ts
CHANGED
|
@@ -175,3 +175,139 @@ export interface ClipboardAPI {
|
|
|
175
175
|
/** Write text to the clipboard. */
|
|
176
176
|
write: (text: string) => Promise<void>;
|
|
177
177
|
}
|
|
178
|
+
|
|
179
|
+
// ─── Observer Composable Types ───────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Options for {@link useIntersectionObserver}.
|
|
183
|
+
*/
|
|
184
|
+
export interface IntersectionObserverOptions {
|
|
185
|
+
/**
|
|
186
|
+
* Root element or document to use as the viewport.
|
|
187
|
+
* Defaults to the browser viewport when `null` or omitted.
|
|
188
|
+
*/
|
|
189
|
+
root?: Element | Document | null;
|
|
190
|
+
/** Margin around the root, using CSS margin syntax (e.g. `'10px 20px'`). */
|
|
191
|
+
rootMargin?: string;
|
|
192
|
+
/**
|
|
193
|
+
* Thresholds at which the callback fires.
|
|
194
|
+
* A single number or an array of numbers between 0 and 1.
|
|
195
|
+
*/
|
|
196
|
+
threshold?: number | number[];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* State tracked by {@link useIntersectionObserver}.
|
|
201
|
+
*
|
|
202
|
+
* Reflects the most recent {@link IntersectionObserverEntry} reported by the
|
|
203
|
+
* underlying `IntersectionObserver` callback, not an aggregate across all
|
|
204
|
+
* observed targets.
|
|
205
|
+
*/
|
|
206
|
+
export interface IntersectionObserverState {
|
|
207
|
+
/**
|
|
208
|
+
* Whether the most recently reported entry is intersecting.
|
|
209
|
+
*
|
|
210
|
+
* This is equivalent to `entry?.isIntersecting ?? false` and does not
|
|
211
|
+
* represent an aggregate across all observed targets.
|
|
212
|
+
*/
|
|
213
|
+
isIntersecting: boolean;
|
|
214
|
+
/**
|
|
215
|
+
* The intersection ratio (0–1) from the most recent entry.
|
|
216
|
+
*
|
|
217
|
+
* This is equivalent to `entry?.intersectionRatio ?? 0`.
|
|
218
|
+
*/
|
|
219
|
+
intersectionRatio: number;
|
|
220
|
+
/** The most recent `IntersectionObserverEntry`, or `null` before the first callback. */
|
|
221
|
+
entry: IntersectionObserverEntry | null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Extended handle returned by {@link useIntersectionObserver}.
|
|
226
|
+
*
|
|
227
|
+
* In addition to the reactive signal, exposes `observe()` and `unobserve()`
|
|
228
|
+
* so elements can be added or removed after creation.
|
|
229
|
+
*/
|
|
230
|
+
export interface IntersectionObserverSignal extends MediaSignalHandle<IntersectionObserverState> {
|
|
231
|
+
/** Start observing the given element. */
|
|
232
|
+
observe(target: Element): void;
|
|
233
|
+
/** Stop observing the given element. */
|
|
234
|
+
unobserve(target: Element): void;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Options for {@link useResizeObserver}.
|
|
239
|
+
*/
|
|
240
|
+
export interface ResizeObserverOptions {
|
|
241
|
+
/**
|
|
242
|
+
* Which CSS box model to observe.
|
|
243
|
+
* @default 'content-box'
|
|
244
|
+
*/
|
|
245
|
+
box?: ResizeObserverBoxOptions;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* State tracked by {@link useResizeObserver}.
|
|
250
|
+
*/
|
|
251
|
+
export interface ResizeObserverState {
|
|
252
|
+
/** Observed box width of the element in pixels (based on the configured `box`). */
|
|
253
|
+
width: number;
|
|
254
|
+
/** Observed box height of the element in pixels (based on the configured `box`). */
|
|
255
|
+
height: number;
|
|
256
|
+
/** The most recent `ResizeObserverEntry`, or `null` before the first callback. */
|
|
257
|
+
entry: ResizeObserverEntry | null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Extended handle returned by {@link useResizeObserver}.
|
|
262
|
+
*
|
|
263
|
+
* Exposes `observe()` and `unobserve()` so elements can be added or removed
|
|
264
|
+
* after creation.
|
|
265
|
+
*/
|
|
266
|
+
export interface ResizeObserverSignal extends MediaSignalHandle<ResizeObserverState> {
|
|
267
|
+
/** Start observing the given element. */
|
|
268
|
+
observe(target: Element): void;
|
|
269
|
+
/** Stop observing the given element. */
|
|
270
|
+
unobserve(target: Element): void;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Options for {@link useMutationObserver}.
|
|
275
|
+
*/
|
|
276
|
+
export interface MutationObserverOptions {
|
|
277
|
+
/** Observe attribute changes. @default true */
|
|
278
|
+
attributes?: boolean;
|
|
279
|
+
/** Observe child list changes. @default false */
|
|
280
|
+
childList?: boolean;
|
|
281
|
+
/** Observe character data changes. @default false */
|
|
282
|
+
characterData?: boolean;
|
|
283
|
+
/** Observe the entire subtree. @default false */
|
|
284
|
+
subtree?: boolean;
|
|
285
|
+
/** Record previous attribute values. @default false */
|
|
286
|
+
attributeOldValue?: boolean;
|
|
287
|
+
/** Record previous character data values. @default false */
|
|
288
|
+
characterDataOldValue?: boolean;
|
|
289
|
+
/** Filter observed attributes to this list. */
|
|
290
|
+
attributeFilter?: string[];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* State tracked by {@link useMutationObserver}.
|
|
295
|
+
*/
|
|
296
|
+
export interface MutationObserverState {
|
|
297
|
+
/** The list of mutations from the most recent callback. */
|
|
298
|
+
mutations: MutationRecord[];
|
|
299
|
+
/** Total number of mutation callbacks received. */
|
|
300
|
+
count: number;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Extended handle returned by {@link useMutationObserver}.
|
|
305
|
+
*
|
|
306
|
+
* Exposes `observe()`, `takeRecords()`, and `destroy()` for manual lifecycle control.
|
|
307
|
+
*/
|
|
308
|
+
export interface MutationObserverSignal extends MediaSignalHandle<MutationObserverState> {
|
|
309
|
+
/** Start observing mutations on the given target. Uses the options from construction time. */
|
|
310
|
+
observe(target: Node): void;
|
|
311
|
+
/** Take and clear any pending mutation records. */
|
|
312
|
+
takeRecords(): MutationRecord[];
|
|
313
|
+
}
|
package/src/reactive/index.ts
CHANGED
|
@@ -40,6 +40,8 @@ export {
|
|
|
40
40
|
useWebSocketChannel,
|
|
41
41
|
untrack,
|
|
42
42
|
watch,
|
|
43
|
+
watchDebounce,
|
|
44
|
+
watchThrottle,
|
|
43
45
|
} from './signal';
|
|
44
46
|
|
|
45
47
|
export type {
|
|
@@ -90,6 +92,7 @@ export type {
|
|
|
90
92
|
UseWebSocketChannelReturn,
|
|
91
93
|
UseWebSocketOptions,
|
|
92
94
|
UseWebSocketReturn,
|
|
95
|
+
WatchOptions,
|
|
93
96
|
WebSocketHeartbeatConfig,
|
|
94
97
|
WebSocketReconnectConfig,
|
|
95
98
|
WebSocketSerializer,
|
package/src/reactive/signal.ts
CHANGED
|
@@ -26,7 +26,7 @@ export { effectScope, getCurrentScope, onScopeDispose } from './scope';
|
|
|
26
26
|
export { isComputed, isSignal } from './type-guards';
|
|
27
27
|
export { toValue } from './to-value';
|
|
28
28
|
export { untrack } from './untrack';
|
|
29
|
-
export { watch } from './watch';
|
|
29
|
+
export { watch, watchDebounce, watchThrottle } from './watch';
|
|
30
30
|
export { useEventSource, useWebSocket, useWebSocketChannel } from './websocket';
|
|
31
31
|
|
|
32
32
|
export type { CleanupFn, Observer } from './internals';
|
|
@@ -57,6 +57,7 @@ export type {
|
|
|
57
57
|
UsePaginatedFetchOptions,
|
|
58
58
|
} from './pagination';
|
|
59
59
|
export type { PollingState, UsePollingOptions } from './polling';
|
|
60
|
+
export type { WatchOptions } from './watch';
|
|
60
61
|
export type {
|
|
61
62
|
IdExtractor,
|
|
62
63
|
ResourceListActions,
|