@hoci/core 0.7.1 → 0.8.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/dist/index.cjs CHANGED
@@ -3,22 +3,7 @@
3
3
  const shared = require('@hoci/shared');
4
4
  const vue = require('vue');
5
5
  const tslx = require('tslx');
6
-
7
- var _a;
8
- const isClient = typeof window !== "undefined";
9
- const isDef = (val) => typeof val !== "undefined";
10
- const isFunction = (val) => typeof val === "function";
11
- const isString = (val) => typeof val === "string";
12
- const noop = () => {
13
- };
14
- isClient && ((_a = window == null ? void 0 : window.navigator) == null ? void 0 : _a.userAgent) && /iP(ad|hone|od)/.test(window.navigator.userAgent);
15
-
16
- function resolveUnref(r) {
17
- return typeof r === "function" ? r() : vue.unref(r);
18
- }
19
- function identity(arg) {
20
- return arg;
21
- }
6
+ const virtualCore = require('@tanstack/virtual-core');
22
7
 
23
8
  function tryOnScopeDispose(fn) {
24
9
  if (vue.getCurrentScope()) {
@@ -32,98 +17,234 @@ function isDefined(v) {
32
17
  return vue.unref(v) != null;
33
18
  }
34
19
 
35
- function syncRef(left, right, options = {}) {
20
+ const isClient = typeof window !== "undefined" && typeof document !== "undefined";
21
+ typeof WorkerGlobalScope !== "undefined" && globalThis instanceof WorkerGlobalScope;
22
+ const isDef = (val) => typeof val !== "undefined";
23
+ const notNullish = (val) => val != null;
24
+ const toString = Object.prototype.toString;
25
+ const isObject = (val) => toString.call(val) === "[object Object]";
26
+ const noop = () => {
27
+ };
28
+ const isIOS = /* @__PURE__ */ getIsIOS();
29
+ function getIsIOS() {
36
30
  var _a, _b;
31
+ return isClient && ((_a = window == null ? void 0 : window.navigator) == null ? void 0 : _a.userAgent) && (/iP(?:ad|hone|od)/.test(window.navigator.userAgent) || ((_b = window == null ? void 0 : window.navigator) == null ? void 0 : _b.maxTouchPoints) > 2 && /iPad|Macintosh/.test(window == null ? void 0 : window.navigator.userAgent));
32
+ }
33
+
34
+ function createFilterWrapper(filter, fn) {
35
+ function wrapper(...args) {
36
+ return new Promise((resolve, reject) => {
37
+ Promise.resolve(filter(() => fn.apply(this, args), { fn, thisArg: this, args })).then(resolve).catch(reject);
38
+ });
39
+ }
40
+ return wrapper;
41
+ }
42
+ const bypassFilter = (invoke) => {
43
+ return invoke();
44
+ };
45
+ function pausableFilter(extendFilter = bypassFilter, options = {}) {
46
+ const {
47
+ initialState = "active"
48
+ } = options;
49
+ const isActive = toRef(initialState === "active");
50
+ function pause() {
51
+ isActive.value = false;
52
+ }
53
+ function resume() {
54
+ isActive.value = true;
55
+ }
56
+ const eventFilter = (...args) => {
57
+ if (isActive.value)
58
+ extendFilter(...args);
59
+ };
60
+ return { isActive: vue.readonly(isActive), pause, resume, eventFilter };
61
+ }
62
+ function getLifeCycleTarget(target) {
63
+ return vue.getCurrentInstance();
64
+ }
65
+ function toArray(value) {
66
+ return Array.isArray(value) ? value : [value];
67
+ }
68
+
69
+ function toRef(...args) {
70
+ if (args.length !== 1)
71
+ return vue.toRef(...args);
72
+ const r = args[0];
73
+ return typeof r === "function" ? vue.readonly(vue.customRef(() => ({ get: r, set: noop }))) : vue.ref(r);
74
+ }
75
+
76
+ function watchWithFilter(source, cb, options = {}) {
77
+ const {
78
+ eventFilter = bypassFilter,
79
+ ...watchOptions
80
+ } = options;
81
+ return vue.watch(
82
+ source,
83
+ createFilterWrapper(
84
+ eventFilter,
85
+ cb
86
+ ),
87
+ watchOptions
88
+ );
89
+ }
90
+
91
+ function watchPausable(source, cb, options = {}) {
92
+ const {
93
+ eventFilter: filter,
94
+ initialState = "active",
95
+ ...watchOptions
96
+ } = options;
97
+ const { eventFilter, pause, resume, isActive } = pausableFilter(filter, { initialState });
98
+ const stop = watchWithFilter(
99
+ source,
100
+ cb,
101
+ {
102
+ ...watchOptions,
103
+ eventFilter
104
+ }
105
+ );
106
+ return { stop, pause, resume, isActive };
107
+ }
108
+
109
+ function syncRef(left, right, ...[options]) {
37
110
  const {
38
111
  flush = "sync",
39
112
  deep = false,
40
113
  immediate = true,
41
114
  direction = "both",
42
115
  transform = {}
43
- } = options;
44
- let watchLeft;
45
- let watchRight;
46
- const transformLTR = (_a = transform.ltr) != null ? _a : (v) => v;
47
- const transformRTL = (_b = transform.rtl) != null ? _b : (v) => v;
116
+ } = options || {};
117
+ const watchers = [];
118
+ const transformLTR = "ltr" in transform && transform.ltr || ((v) => v);
119
+ const transformRTL = "rtl" in transform && transform.rtl || ((v) => v);
48
120
  if (direction === "both" || direction === "ltr") {
49
- watchLeft = vue.watch(left, (newValue) => right.value = transformLTR(newValue), { flush, deep, immediate });
121
+ watchers.push(watchPausable(
122
+ left,
123
+ (newValue) => {
124
+ watchers.forEach((w) => w.pause());
125
+ right.value = transformLTR(newValue);
126
+ watchers.forEach((w) => w.resume());
127
+ },
128
+ { flush, deep, immediate }
129
+ ));
50
130
  }
51
131
  if (direction === "both" || direction === "rtl") {
52
- watchRight = vue.watch(right, (newValue) => left.value = transformRTL(newValue), { flush, deep, immediate });
132
+ watchers.push(watchPausable(
133
+ right,
134
+ (newValue) => {
135
+ watchers.forEach((w) => w.pause());
136
+ left.value = transformRTL(newValue);
137
+ watchers.forEach((w) => w.resume());
138
+ },
139
+ { flush, deep, immediate }
140
+ ));
53
141
  }
54
- return () => {
55
- watchLeft == null ? void 0 : watchLeft();
56
- watchRight == null ? void 0 : watchRight();
142
+ const stop = () => {
143
+ watchers.forEach((w) => w.stop());
57
144
  };
145
+ return stop;
58
146
  }
59
147
 
60
- function tryOnMounted(fn, sync = true) {
61
- if (vue.getCurrentInstance())
62
- vue.onMounted(fn);
148
+ function tryOnMounted(fn, sync = true, target) {
149
+ const instance = getLifeCycleTarget();
150
+ if (instance)
151
+ vue.onMounted(fn, target);
63
152
  else if (sync)
64
153
  fn();
65
154
  else
66
155
  vue.nextTick(fn);
67
156
  }
68
157
 
158
+ function watchImmediate(source, cb, options) {
159
+ return vue.watch(
160
+ source,
161
+ cb,
162
+ {
163
+ ...options,
164
+ immediate: true
165
+ }
166
+ );
167
+ }
168
+
169
+ function watchOnce(source, cb, options) {
170
+ const stop = vue.watch(source, (...args) => {
171
+ vue.nextTick(() => stop());
172
+ return cb(...args);
173
+ }, options);
174
+ return stop;
175
+ }
176
+
177
+ const defaultWindow = isClient ? window : void 0;
178
+
69
179
  function unrefElement(elRef) {
70
180
  var _a;
71
- const plain = resolveUnref(elRef);
181
+ const plain = vue.toValue(elRef);
72
182
  return (_a = plain == null ? void 0 : plain.$el) != null ? _a : plain;
73
183
  }
74
184
 
75
- const defaultWindow = isClient ? window : void 0;
76
-
77
185
  function useEventListener(...args) {
78
- let target;
79
- let events;
80
- let listeners;
81
- let options;
82
- if (isString(args[0]) || Array.isArray(args[0])) {
83
- [events, listeners, options] = args;
84
- target = defaultWindow;
85
- } else {
86
- [target, events, listeners, options] = args;
87
- }
88
- if (!target)
89
- return noop;
90
- if (!Array.isArray(events))
91
- events = [events];
92
- if (!Array.isArray(listeners))
93
- listeners = [listeners];
94
186
  const cleanups = [];
95
187
  const cleanup = () => {
96
188
  cleanups.forEach((fn) => fn());
97
189
  cleanups.length = 0;
98
190
  };
99
- const register = (el, event, listener) => {
191
+ const register = (el, event, listener, options) => {
100
192
  el.addEventListener(event, listener, options);
101
193
  return () => el.removeEventListener(event, listener, options);
102
194
  };
103
- const stopWatch = vue.watch(() => unrefElement(target), (el) => {
104
- cleanup();
105
- if (!el)
106
- return;
107
- cleanups.push(...events.flatMap((event) => {
108
- return listeners.map((listener) => register(el, event, listener));
109
- }));
110
- }, { immediate: true, flush: "post" });
195
+ const firstParamTargets = vue.computed(() => {
196
+ const test = toArray(vue.toValue(args[0])).filter((e) => e != null);
197
+ return test.every((e) => typeof e !== "string") ? test : void 0;
198
+ });
199
+ const stopWatch = watchImmediate(
200
+ () => {
201
+ var _a, _b;
202
+ return [
203
+ (_b = (_a = firstParamTargets.value) == null ? void 0 : _a.map((e) => unrefElement(e))) != null ? _b : [defaultWindow].filter((e) => e != null),
204
+ toArray(vue.toValue(firstParamTargets.value ? args[1] : args[0])),
205
+ toArray(vue.unref(firstParamTargets.value ? args[2] : args[1])),
206
+ // @ts-expect-error - TypeScript gets the correct types, but somehow still complains
207
+ vue.toValue(firstParamTargets.value ? args[3] : args[2])
208
+ ];
209
+ },
210
+ ([raw_targets, raw_events, raw_listeners, raw_options]) => {
211
+ cleanup();
212
+ if (!(raw_targets == null ? void 0 : raw_targets.length) || !(raw_events == null ? void 0 : raw_events.length) || !(raw_listeners == null ? void 0 : raw_listeners.length))
213
+ return;
214
+ const optionsClone = isObject(raw_options) ? { ...raw_options } : raw_options;
215
+ cleanups.push(
216
+ ...raw_targets.flatMap(
217
+ (el) => raw_events.flatMap(
218
+ (event) => raw_listeners.map((listener) => register(el, event, listener, optionsClone))
219
+ )
220
+ )
221
+ );
222
+ },
223
+ { flush: "post" }
224
+ );
111
225
  const stop = () => {
112
226
  stopWatch();
113
227
  cleanup();
114
228
  };
115
- tryOnScopeDispose(stop);
229
+ tryOnScopeDispose(cleanup);
116
230
  return stop;
117
231
  }
118
232
 
233
+ let _iOSWorkaround = false;
119
234
  function onClickOutside(target, handler, options = {}) {
120
- const { window = defaultWindow, ignore = [], capture = true, detectIframe = false } = options;
121
- if (!window)
122
- return;
235
+ const { window = defaultWindow, ignore = [], capture = true, detectIframe = false, controls = false } = options;
236
+ if (!window) {
237
+ return controls ? { stop: noop, cancel: noop, trigger: noop } : noop;
238
+ }
239
+ if (isIOS && !_iOSWorkaround) {
240
+ _iOSWorkaround = true;
241
+ const listenerOptions = { passive: true };
242
+ Array.from(window.document.body.children).forEach((el) => useEventListener(el, "click", noop, listenerOptions));
243
+ useEventListener(window.document.documentElement, "click", noop, listenerOptions);
244
+ }
123
245
  let shouldListen = true;
124
- let fallback;
125
246
  const shouldIgnore = (event) => {
126
- return ignore.some((target2) => {
247
+ return vue.toValue(ignore).some((target2) => {
127
248
  if (typeof target2 === "string") {
128
249
  return Array.from(window.document.querySelectorAll(target2)).some((el) => el === event.target || event.composedPath().includes(el));
129
250
  } else {
@@ -132,12 +253,26 @@ function onClickOutside(target, handler, options = {}) {
132
253
  }
133
254
  });
134
255
  };
256
+ function hasMultipleRoots(target2) {
257
+ const vm = vue.toValue(target2);
258
+ return vm && vm.$.subTree.shapeFlag === 16;
259
+ }
260
+ function checkMultipleRoots(target2, event) {
261
+ const vm = vue.toValue(target2);
262
+ const children = vm.$.subTree && vm.$.subTree.children;
263
+ if (children == null || !Array.isArray(children))
264
+ return false;
265
+ return children.some((child) => child.el === event.target || event.composedPath().includes(child.el));
266
+ }
135
267
  const listener = (event) => {
136
- window.clearTimeout(fallback);
137
268
  const el = unrefElement(target);
269
+ if (event.target == null)
270
+ return;
271
+ if (!(el instanceof Element) && hasMultipleRoots(target) && checkMultipleRoots(target, event))
272
+ return;
138
273
  if (!el || el === event.target || event.composedPath().includes(el))
139
274
  return;
140
- if (event.detail === 0)
275
+ if ("detail" in event && event.detail === 0)
141
276
  shouldListen = !shouldIgnore(event);
142
277
  if (!shouldListen) {
143
278
  shouldListen = true;
@@ -145,63 +280,114 @@ function onClickOutside(target, handler, options = {}) {
145
280
  }
146
281
  handler(event);
147
282
  };
283
+ let isProcessingClick = false;
148
284
  const cleanup = [
149
- useEventListener(window, "click", listener, { passive: true, capture }),
285
+ useEventListener(window, "click", (event) => {
286
+ if (!isProcessingClick) {
287
+ isProcessingClick = true;
288
+ setTimeout(() => {
289
+ isProcessingClick = false;
290
+ }, 0);
291
+ listener(event);
292
+ }
293
+ }, { passive: true, capture }),
150
294
  useEventListener(window, "pointerdown", (e) => {
151
295
  const el = unrefElement(target);
152
- if (el)
153
- shouldListen = !e.composedPath().includes(el) && !shouldIgnore(e);
154
- }, { passive: true }),
155
- useEventListener(window, "pointerup", (e) => {
156
- if (e.button === 0) {
157
- const path = e.composedPath();
158
- e.composedPath = () => path;
159
- fallback = window.setTimeout(() => listener(e), 50);
160
- }
296
+ shouldListen = !shouldIgnore(e) && !!(el && !e.composedPath().includes(el));
161
297
  }, { passive: true }),
162
298
  detectIframe && useEventListener(window, "blur", (event) => {
163
- var _a;
164
- const el = unrefElement(target);
165
- if (((_a = window.document.activeElement) == null ? void 0 : _a.tagName) === "IFRAME" && !(el == null ? void 0 : el.contains(window.document.activeElement)))
166
- handler(event);
167
- })
299
+ setTimeout(() => {
300
+ var _a;
301
+ const el = unrefElement(target);
302
+ if (((_a = window.document.activeElement) == null ? void 0 : _a.tagName) === "IFRAME" && !(el == null ? void 0 : el.contains(window.document.activeElement))) {
303
+ handler(event);
304
+ }
305
+ }, 0);
306
+ }, { passive: true })
168
307
  ].filter(Boolean);
169
308
  const stop = () => cleanup.forEach((fn) => fn());
309
+ if (controls) {
310
+ return {
311
+ stop,
312
+ cancel: () => {
313
+ shouldListen = false;
314
+ },
315
+ trigger: (event) => {
316
+ shouldListen = true;
317
+ listener(event);
318
+ shouldListen = false;
319
+ }
320
+ };
321
+ }
170
322
  return stop;
171
323
  }
172
324
 
173
- function useSupported(callback, sync = false) {
174
- const isSupported = vue.ref();
175
- const update = () => isSupported.value = Boolean(callback());
176
- update();
177
- tryOnMounted(update, sync);
178
- return isSupported;
325
+ function useMounted() {
326
+ const isMounted = vue.shallowRef(false);
327
+ const instance = vue.getCurrentInstance();
328
+ if (instance) {
329
+ vue.onMounted(() => {
330
+ isMounted.value = true;
331
+ }, instance);
332
+ }
333
+ return isMounted;
179
334
  }
335
+
336
+ function useSupported(callback) {
337
+ const isMounted = useMounted();
338
+ return vue.computed(() => {
339
+ isMounted.value;
340
+ return Boolean(callback());
341
+ });
342
+ }
343
+
344
+ function useMutationObserver(target, callback, options = {}) {
345
+ const { window = defaultWindow, ...mutationOptions } = options;
346
+ let observer;
347
+ const isSupported = useSupported(() => window && "MutationObserver" in window);
348
+ const cleanup = () => {
349
+ if (observer) {
350
+ observer.disconnect();
351
+ observer = void 0;
352
+ }
353
+ };
354
+ const targets = vue.computed(() => {
355
+ const value = vue.toValue(target);
356
+ const items = toArray(value).map(unrefElement).filter(notNullish);
357
+ return new Set(items);
358
+ });
359
+ const stopWatch = vue.watch(
360
+ () => targets.value,
361
+ (targets2) => {
362
+ cleanup();
363
+ if (isSupported.value && targets2.size) {
364
+ observer = new MutationObserver(callback);
365
+ targets2.forEach((el) => observer.observe(el, mutationOptions));
366
+ }
367
+ },
368
+ { immediate: true, flush: "post" }
369
+ );
370
+ const takeRecords = () => {
371
+ return observer == null ? void 0 : observer.takeRecords();
372
+ };
373
+ const stop = () => {
374
+ stopWatch();
375
+ cleanup();
376
+ };
377
+ tryOnScopeDispose(stop);
378
+ return {
379
+ isSupported,
380
+ stop,
381
+ takeRecords
382
+ };
383
+ }
384
+
180
385
  function cloneFnJSON(source) {
181
386
  return JSON.parse(JSON.stringify(source));
182
387
  }
183
388
 
184
- const _global = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
185
- const globalKey = "__vueuse_ssr_handlers__";
186
- _global[globalKey] = _global[globalKey] || {};
187
-
188
- var __getOwnPropSymbols$f = Object.getOwnPropertySymbols;
189
- var __hasOwnProp$f = Object.prototype.hasOwnProperty;
190
- var __propIsEnum$f = Object.prototype.propertyIsEnumerable;
191
- var __objRest$2 = (source, exclude) => {
192
- var target = {};
193
- for (var prop in source)
194
- if (__hasOwnProp$f.call(source, prop) && exclude.indexOf(prop) < 0)
195
- target[prop] = source[prop];
196
- if (source != null && __getOwnPropSymbols$f)
197
- for (var prop of __getOwnPropSymbols$f(source)) {
198
- if (exclude.indexOf(prop) < 0 && __propIsEnum$f.call(source, prop))
199
- target[prop] = source[prop];
200
- }
201
- return target;
202
- };
203
389
  function useResizeObserver(target, callback, options = {}) {
204
- const _a = options, { window = defaultWindow } = _a, observerOptions = __objRest$2(_a, ["window"]);
390
+ const { window = defaultWindow, ...observerOptions } = options;
205
391
  let observer;
206
392
  const isSupported = useSupported(() => window && "ResizeObserver" in window);
207
393
  const cleanup = () => {
@@ -210,13 +396,24 @@ function useResizeObserver(target, callback, options = {}) {
210
396
  observer = void 0;
211
397
  }
212
398
  };
213
- const stopWatch = vue.watch(() => unrefElement(target), (el) => {
214
- cleanup();
215
- if (isSupported.value && window && el) {
216
- observer = new ResizeObserver(callback);
217
- observer.observe(el, observerOptions);
218
- }
219
- }, { immediate: true, flush: "post" });
399
+ const targets = vue.computed(() => {
400
+ const _targets = vue.toValue(target);
401
+ return Array.isArray(_targets) ? _targets.map((el) => unrefElement(el)) : [unrefElement(_targets)];
402
+ });
403
+ const stopWatch = vue.watch(
404
+ targets,
405
+ (els) => {
406
+ cleanup();
407
+ if (isSupported.value && window) {
408
+ observer = new ResizeObserver(callback);
409
+ for (const _el of els) {
410
+ if (_el)
411
+ observer.observe(_el, observerOptions);
412
+ }
413
+ }
414
+ },
415
+ { immediate: true, flush: "post" }
416
+ );
220
417
  const stop = () => {
221
418
  cleanup();
222
419
  stopWatch();
@@ -233,17 +430,18 @@ function useElementBounding(target, options = {}) {
233
430
  reset = true,
234
431
  windowResize = true,
235
432
  windowScroll = true,
236
- immediate = true
433
+ immediate = true,
434
+ updateTiming = "sync"
237
435
  } = options;
238
- const height = vue.ref(0);
239
- const bottom = vue.ref(0);
240
- const left = vue.ref(0);
241
- const right = vue.ref(0);
242
- const top = vue.ref(0);
243
- const width = vue.ref(0);
244
- const x = vue.ref(0);
245
- const y = vue.ref(0);
246
- function update() {
436
+ const height = vue.shallowRef(0);
437
+ const bottom = vue.shallowRef(0);
438
+ const left = vue.shallowRef(0);
439
+ const right = vue.shallowRef(0);
440
+ const top = vue.shallowRef(0);
441
+ const width = vue.shallowRef(0);
442
+ const x = vue.shallowRef(0);
443
+ const y = vue.shallowRef(0);
444
+ function recalculate() {
247
445
  const el = unrefElement(target);
248
446
  if (!el) {
249
447
  if (reset) {
@@ -268,8 +466,17 @@ function useElementBounding(target, options = {}) {
268
466
  x.value = rect.x;
269
467
  y.value = rect.y;
270
468
  }
469
+ function update() {
470
+ if (updateTiming === "sync")
471
+ recalculate();
472
+ else if (updateTiming === "next-frame")
473
+ requestAnimationFrame(() => recalculate());
474
+ }
271
475
  useResizeObserver(target, update);
272
476
  vue.watch(() => unrefElement(target), (ele) => !ele && update());
477
+ useMutationObserver(target, update, {
478
+ attributeFilter: ["style", "class"]
479
+ });
273
480
  if (windowScroll)
274
481
  useEventListener("scroll", update, { capture: true, passive: true });
275
482
  if (windowResize)
@@ -291,84 +498,101 @@ function useElementBounding(target, options = {}) {
291
498
  };
292
499
  }
293
500
 
294
- function useElementVisibility(element, { window = defaultWindow, scrollTarget } = {}) {
295
- const elementIsVisible = vue.ref(false);
296
- const testBounding = () => {
297
- if (!window)
298
- return;
299
- const document = window.document;
300
- const el = unrefElement(element);
301
- if (!el) {
302
- elementIsVisible.value = false;
303
- } else {
304
- const rect = el.getBoundingClientRect();
305
- elementIsVisible.value = rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth) && rect.bottom >= 0 && rect.right >= 0;
306
- }
501
+ function useIntersectionObserver(target, callback, options = {}) {
502
+ const {
503
+ root,
504
+ rootMargin = "0px",
505
+ threshold = 0,
506
+ window = defaultWindow,
507
+ immediate = true
508
+ } = options;
509
+ const isSupported = useSupported(() => window && "IntersectionObserver" in window);
510
+ const targets = vue.computed(() => {
511
+ const _target = vue.toValue(target);
512
+ return toArray(_target).map(unrefElement).filter(notNullish);
513
+ });
514
+ let cleanup = noop;
515
+ const isActive = vue.shallowRef(immediate);
516
+ const stopWatch = isSupported.value ? vue.watch(
517
+ () => [targets.value, unrefElement(root), isActive.value],
518
+ ([targets2, root2]) => {
519
+ cleanup();
520
+ if (!isActive.value)
521
+ return;
522
+ if (!targets2.length)
523
+ return;
524
+ const observer = new IntersectionObserver(
525
+ callback,
526
+ {
527
+ root: unrefElement(root2),
528
+ rootMargin,
529
+ threshold
530
+ }
531
+ );
532
+ targets2.forEach((el) => el && observer.observe(el));
533
+ cleanup = () => {
534
+ observer.disconnect();
535
+ cleanup = noop;
536
+ };
537
+ },
538
+ { immediate, flush: "post" }
539
+ ) : noop;
540
+ const stop = () => {
541
+ cleanup();
542
+ stopWatch();
543
+ isActive.value = false;
544
+ };
545
+ tryOnScopeDispose(stop);
546
+ return {
547
+ isSupported,
548
+ isActive,
549
+ pause() {
550
+ cleanup();
551
+ isActive.value = false;
552
+ },
553
+ resume() {
554
+ isActive.value = true;
555
+ },
556
+ stop
307
557
  };
308
- vue.watch(() => unrefElement(element), () => testBounding(), { immediate: true, flush: "post" });
309
- if (window) {
310
- useEventListener(scrollTarget || window, "scroll", testBounding, {
311
- capture: false,
312
- passive: true
313
- });
314
- }
315
- return elementIsVisible;
316
558
  }
317
559
 
318
- var SwipeDirection;
319
- (function(SwipeDirection2) {
320
- SwipeDirection2["UP"] = "UP";
321
- SwipeDirection2["RIGHT"] = "RIGHT";
322
- SwipeDirection2["DOWN"] = "DOWN";
323
- SwipeDirection2["LEFT"] = "LEFT";
324
- SwipeDirection2["NONE"] = "NONE";
325
- })(SwipeDirection || (SwipeDirection = {}));
326
-
327
- var __defProp = Object.defineProperty;
328
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
329
- var __hasOwnProp = Object.prototype.hasOwnProperty;
330
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
331
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
332
- var __spreadValues = (a, b) => {
333
- for (var prop in b || (b = {}))
334
- if (__hasOwnProp.call(b, prop))
335
- __defNormalProp(a, prop, b[prop]);
336
- if (__getOwnPropSymbols)
337
- for (var prop of __getOwnPropSymbols(b)) {
338
- if (__propIsEnum.call(b, prop))
339
- __defNormalProp(a, prop, b[prop]);
560
+ function useElementVisibility(element, options = {}) {
561
+ const {
562
+ window = defaultWindow,
563
+ scrollTarget,
564
+ threshold = 0,
565
+ rootMargin,
566
+ once = false
567
+ } = options;
568
+ const elementIsVisible = vue.shallowRef(false);
569
+ const { stop } = useIntersectionObserver(
570
+ element,
571
+ (intersectionObserverEntries) => {
572
+ let isIntersecting = elementIsVisible.value;
573
+ let latestTime = 0;
574
+ for (const entry of intersectionObserverEntries) {
575
+ if (entry.time >= latestTime) {
576
+ latestTime = entry.time;
577
+ isIntersecting = entry.isIntersecting;
578
+ }
579
+ }
580
+ elementIsVisible.value = isIntersecting;
581
+ if (once) {
582
+ watchOnce(elementIsVisible, () => {
583
+ stop();
584
+ });
585
+ }
586
+ },
587
+ {
588
+ root: scrollTarget,
589
+ window,
590
+ threshold,
591
+ rootMargin: vue.toValue(rootMargin)
340
592
  }
341
- return a;
342
- };
343
- const _TransitionPresets = {
344
- easeInSine: [0.12, 0, 0.39, 0],
345
- easeOutSine: [0.61, 1, 0.88, 1],
346
- easeInOutSine: [0.37, 0, 0.63, 1],
347
- easeInQuad: [0.11, 0, 0.5, 0],
348
- easeOutQuad: [0.5, 1, 0.89, 1],
349
- easeInOutQuad: [0.45, 0, 0.55, 1],
350
- easeInCubic: [0.32, 0, 0.67, 0],
351
- easeOutCubic: [0.33, 1, 0.68, 1],
352
- easeInOutCubic: [0.65, 0, 0.35, 1],
353
- easeInQuart: [0.5, 0, 0.75, 0],
354
- easeOutQuart: [0.25, 1, 0.5, 1],
355
- easeInOutQuart: [0.76, 0, 0.24, 1],
356
- easeInQuint: [0.64, 0, 0.78, 0],
357
- easeOutQuint: [0.22, 1, 0.36, 1],
358
- easeInOutQuint: [0.83, 0, 0.17, 1],
359
- easeInExpo: [0.7, 0, 0.84, 0],
360
- easeOutExpo: [0.16, 1, 0.3, 1],
361
- easeInOutExpo: [0.87, 0, 0.13, 1],
362
- easeInCirc: [0.55, 0, 1, 0.45],
363
- easeOutCirc: [0, 0.55, 0.45, 1],
364
- easeInOutCirc: [0.85, 0, 0.15, 1],
365
- easeInBack: [0.36, 0, 0.66, -0.56],
366
- easeOutBack: [0.34, 1.56, 0.64, 1],
367
- easeInOutBack: [0.68, -0.6, 0.32, 1.6]
368
- };
369
- __spreadValues({
370
- linear: identity
371
- }, _TransitionPresets);
593
+ );
594
+ return elementIsVisible;
595
+ }
372
596
 
373
597
  function useVModel(props, key, emit, options = {}) {
374
598
  var _a, _b, _c;
@@ -377,27 +601,48 @@ function useVModel(props, key, emit, options = {}) {
377
601
  passive = false,
378
602
  eventName,
379
603
  deep = false,
380
- defaultValue
604
+ defaultValue,
605
+ shouldEmit
381
606
  } = options;
382
607
  const vm = vue.getCurrentInstance();
383
608
  const _emit = emit || (vm == null ? void 0 : vm.emit) || ((_a = vm == null ? void 0 : vm.$emit) == null ? void 0 : _a.bind(vm)) || ((_c = (_b = vm == null ? void 0 : vm.proxy) == null ? void 0 : _b.$emit) == null ? void 0 : _c.bind(vm == null ? void 0 : vm.proxy));
384
609
  let event = eventName;
385
610
  if (!key) {
386
- {
387
- key = "modelValue";
388
- }
611
+ key = "modelValue";
389
612
  }
390
- event = eventName || event || `update:${key.toString()}`;
391
- const cloneFn = (val) => !clone ? val : isFunction(clone) ? clone(val) : cloneFnJSON(val);
613
+ event = event || `update:${key.toString()}`;
614
+ const cloneFn = (val) => !clone ? val : typeof clone === "function" ? clone(val) : cloneFnJSON(val);
392
615
  const getValue = () => isDef(props[key]) ? cloneFn(props[key]) : defaultValue;
616
+ const triggerEmit = (value) => {
617
+ if (shouldEmit) {
618
+ if (shouldEmit(value))
619
+ _emit(event, value);
620
+ } else {
621
+ _emit(event, value);
622
+ }
623
+ };
393
624
  if (passive) {
394
625
  const initialValue = getValue();
395
626
  const proxy = vue.ref(initialValue);
396
- vue.watch(() => props[key], (v) => proxy.value = cloneFn(v));
397
- vue.watch(proxy, (v) => {
398
- if (v !== props[key] || deep)
399
- _emit(event, v);
400
- }, { deep });
627
+ let isUpdating = false;
628
+ vue.watch(
629
+ () => props[key],
630
+ (v) => {
631
+ if (!isUpdating) {
632
+ isUpdating = true;
633
+ proxy.value = cloneFn(v);
634
+ vue.nextTick(() => isUpdating = false);
635
+ }
636
+ }
637
+ );
638
+ vue.watch(
639
+ proxy,
640
+ (v) => {
641
+ if (!isUpdating && (v !== props[key] || deep))
642
+ triggerEmit(v);
643
+ },
644
+ { deep }
645
+ );
401
646
  return proxy;
402
647
  } else {
403
648
  return vue.computed({
@@ -405,7 +650,7 @@ function useVModel(props, key, emit, options = {}) {
405
650
  return getValue();
406
651
  },
407
652
  set(value) {
408
- _emit(event, value);
653
+ triggerEmit(value);
409
654
  }
410
655
  });
411
656
  }
@@ -1282,6 +1527,128 @@ const useSwitch = shared.defineHookComponent({
1282
1527
  }
1283
1528
  });
1284
1529
 
1530
+ const virtualListProps = shared.defineHookProps({
1531
+ options: {
1532
+ type: Object,
1533
+ default: () => ({})
1534
+ },
1535
+ count: {
1536
+ type: Number,
1537
+ default: () => 0
1538
+ },
1539
+ estimateSize: {
1540
+ type: [Function, Number],
1541
+ default: () => 50
1542
+ },
1543
+ horizontal: {
1544
+ type: Boolean,
1545
+ default: () => false
1546
+ }
1547
+ });
1548
+ const virtualListEmits = shared.defineHookEmits({
1549
+ scrollEnd: () => true,
1550
+ scrollStart: () => true,
1551
+ scroll: (_) => true
1552
+ });
1553
+ const useVirtualList = shared.defineHookComponent({
1554
+ props: virtualListProps,
1555
+ emits: virtualListEmits,
1556
+ setup(props, context) {
1557
+ const { emit } = context;
1558
+ const scrollElementRef = shared.elementRef();
1559
+ const propsEstimateSize = props.estimateSize;
1560
+ const estimateSize = typeof propsEstimateSize === "function" ? propsEstimateSize : () => propsEstimateSize;
1561
+ const options = vue.computed(() => {
1562
+ const opts = { ...props.options || {} };
1563
+ return {
1564
+ ...opts,
1565
+ count: props.count,
1566
+ estimateSize,
1567
+ horizontal: props.horizontal,
1568
+ getScrollElement: () => scrollElementRef.value,
1569
+ observeElementRect: virtualCore.observeElementRect,
1570
+ observeElementOffset: virtualCore.observeElementOffset,
1571
+ scrollToFn: virtualCore.elementScroll
1572
+ };
1573
+ });
1574
+ const virtualizer = new virtualCore.Virtualizer(options.value);
1575
+ const state = vue.shallowRef(virtualizer);
1576
+ const virtualItems = vue.computed(() => state.value.getVirtualItems());
1577
+ const virtualIndexes = vue.computed(() => state.value.getVirtualIndexes());
1578
+ const totalSize = vue.computed(() => state.value.getTotalSize());
1579
+ vue.watch(
1580
+ virtualIndexes,
1581
+ (indexes) => {
1582
+ if (indexes.length === 0) {
1583
+ return;
1584
+ }
1585
+ if (indexes[indexes.length - 1] === props.count - 1) {
1586
+ emit("scrollEnd");
1587
+ } else if (indexes[0] === 0) {
1588
+ emit("scrollStart");
1589
+ }
1590
+ emit("scroll", indexes);
1591
+ },
1592
+ { immediate: true }
1593
+ );
1594
+ vue.watch(
1595
+ options,
1596
+ (opts) => {
1597
+ virtualizer.setOptions({
1598
+ ...opts,
1599
+ onChange: (instance, sync) => {
1600
+ opts.onChange?.(instance, sync);
1601
+ vue.triggerRef(state);
1602
+ }
1603
+ });
1604
+ virtualizer._willUpdate();
1605
+ vue.triggerRef(state);
1606
+ },
1607
+ { immediate: true }
1608
+ );
1609
+ vue.watch(
1610
+ scrollElementRef,
1611
+ (el) => {
1612
+ if (el) {
1613
+ virtualizer._willUpdate();
1614
+ vue.triggerRef(state);
1615
+ }
1616
+ },
1617
+ { immediate: true }
1618
+ );
1619
+ tryOnScopeDispose(virtualizer._didMount());
1620
+ const measureElement = (el) => {
1621
+ virtualizer.measureElement(el);
1622
+ };
1623
+ const scrollToIndex = (index, options2 = {
1624
+ behavior: "smooth"
1625
+ }) => {
1626
+ virtualizer.scrollToIndex(index, options2);
1627
+ };
1628
+ const scrollToStart = (options2 = {
1629
+ behavior: "smooth"
1630
+ }) => {
1631
+ scrollToIndex(0, options2);
1632
+ };
1633
+ const scrollToEnd = (options2 = {
1634
+ behavior: "smooth"
1635
+ }) => {
1636
+ scrollToIndex(props.count - 1, options2);
1637
+ };
1638
+ return {
1639
+ virtualizer,
1640
+ virtualItems,
1641
+ virtualIndexes,
1642
+ totalSize,
1643
+ scrollElementRef,
1644
+ measureElement,
1645
+ scrollToIndex,
1646
+ scrollToStart,
1647
+ scrollToEnd
1648
+ };
1649
+ }
1650
+ });
1651
+
1285
1652
  exports.AFFIX_TARGET_KEY = AFFIX_TARGET_KEY;
1286
1653
  exports.affixEmits = affixEmits;
1287
1654
  exports.affixProps = affixProps;
@@ -1306,6 +1673,9 @@ exports.useSelectionContext = useSelectionContext;
1306
1673
  exports.useSelectionItem = useSelectionItem;
1307
1674
  exports.useSelectionList = useSelectionList;
1308
1675
  exports.useSwitch = useSwitch;
1676
+ exports.useVirtualList = useVirtualList;
1677
+ exports.virtualListEmits = virtualListEmits;
1678
+ exports.virtualListProps = virtualListProps;
1309
1679
  Object.prototype.hasOwnProperty.call(shared, '__proto__') &&
1310
1680
  !Object.prototype.hasOwnProperty.call(exports, '__proto__') &&
1311
1681
  Object.defineProperty(exports, '__proto__', {