@hoci/components 0.7.1 → 0.8.1

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
@@ -5,20 +5,6 @@ const vue = require('vue');
5
5
  const tslx = require('tslx');
6
6
  const core = require('@hoci/core');
7
7
 
8
- var _a;
9
- const isClient = typeof window !== "undefined";
10
- const isString = (val) => typeof val === "string";
11
- const noop = () => {
12
- };
13
- isClient && ((_a = window == null ? void 0 : window.navigator) == null ? void 0 : _a.userAgent) && /iP(ad|hone|od)/.test(window.navigator.userAgent);
14
-
15
- function resolveUnref(r) {
16
- return typeof r === "function" ? r() : vue.unref(r);
17
- }
18
- function identity(arg) {
19
- return arg;
20
- }
21
-
22
8
  function tryOnScopeDispose(fn) {
23
9
  if (vue.getCurrentScope()) {
24
10
  vue.onScopeDispose(fn);
@@ -31,119 +17,275 @@ function isDefined(v) {
31
17
  return vue.unref(v) != null;
32
18
  }
33
19
 
34
- function syncRef(left, right, options = {}) {
35
- var _a, _b;
20
+ const isClient = typeof window !== "undefined" && typeof document !== "undefined";
21
+ typeof WorkerGlobalScope !== "undefined" && globalThis instanceof WorkerGlobalScope;
22
+ const notNullish = (val) => val != null;
23
+ const toString = Object.prototype.toString;
24
+ const isObject = (val) => toString.call(val) === "[object Object]";
25
+ const noop = () => {
26
+ };
27
+
28
+ function createFilterWrapper(filter, fn) {
29
+ function wrapper(...args) {
30
+ return new Promise((resolve, reject) => {
31
+ Promise.resolve(filter(() => fn.apply(this, args), { fn, thisArg: this, args })).then(resolve).catch(reject);
32
+ });
33
+ }
34
+ return wrapper;
35
+ }
36
+ const bypassFilter = (invoke) => {
37
+ return invoke();
38
+ };
39
+ function pausableFilter(extendFilter = bypassFilter, options = {}) {
40
+ const {
41
+ initialState = "active"
42
+ } = options;
43
+ const isActive = toRef(initialState === "active");
44
+ function pause() {
45
+ isActive.value = false;
46
+ }
47
+ function resume() {
48
+ isActive.value = true;
49
+ }
50
+ const eventFilter = (...args) => {
51
+ if (isActive.value)
52
+ extendFilter(...args);
53
+ };
54
+ return { isActive: vue.readonly(isActive), pause, resume, eventFilter };
55
+ }
56
+ function getLifeCycleTarget(target) {
57
+ return vue.getCurrentInstance();
58
+ }
59
+ function toArray(value) {
60
+ return Array.isArray(value) ? value : [value];
61
+ }
62
+
63
+ function toRef(...args) {
64
+ if (args.length !== 1)
65
+ return vue.toRef(...args);
66
+ const r = args[0];
67
+ return typeof r === "function" ? vue.readonly(vue.customRef(() => ({ get: r, set: noop }))) : vue.ref(r);
68
+ }
69
+
70
+ function watchWithFilter(source, cb, options = {}) {
71
+ const {
72
+ eventFilter = bypassFilter,
73
+ ...watchOptions
74
+ } = options;
75
+ return vue.watch(
76
+ source,
77
+ createFilterWrapper(
78
+ eventFilter,
79
+ cb
80
+ ),
81
+ watchOptions
82
+ );
83
+ }
84
+
85
+ function watchPausable(source, cb, options = {}) {
86
+ const {
87
+ eventFilter: filter,
88
+ initialState = "active",
89
+ ...watchOptions
90
+ } = options;
91
+ const { eventFilter, pause, resume, isActive } = pausableFilter(filter, { initialState });
92
+ const stop = watchWithFilter(
93
+ source,
94
+ cb,
95
+ {
96
+ ...watchOptions,
97
+ eventFilter
98
+ }
99
+ );
100
+ return { stop, pause, resume, isActive };
101
+ }
102
+
103
+ function syncRef(left, right, ...[options]) {
36
104
  const {
37
105
  flush = "sync",
38
106
  deep = false,
39
107
  immediate = true,
40
108
  direction = "both",
41
109
  transform = {}
42
- } = options;
43
- let watchLeft;
44
- let watchRight;
45
- const transformLTR = (_a = transform.ltr) != null ? _a : (v) => v;
46
- const transformRTL = (_b = transform.rtl) != null ? _b : (v) => v;
110
+ } = options || {};
111
+ const watchers = [];
112
+ const transformLTR = "ltr" in transform && transform.ltr || ((v) => v);
113
+ const transformRTL = "rtl" in transform && transform.rtl || ((v) => v);
47
114
  if (direction === "both" || direction === "ltr") {
48
- watchLeft = vue.watch(left, (newValue) => right.value = transformLTR(newValue), { flush, deep, immediate });
115
+ watchers.push(watchPausable(
116
+ left,
117
+ (newValue) => {
118
+ watchers.forEach((w) => w.pause());
119
+ right.value = transformLTR(newValue);
120
+ watchers.forEach((w) => w.resume());
121
+ },
122
+ { flush, deep, immediate }
123
+ ));
49
124
  }
50
125
  if (direction === "both" || direction === "rtl") {
51
- watchRight = vue.watch(right, (newValue) => left.value = transformRTL(newValue), { flush, deep, immediate });
126
+ watchers.push(watchPausable(
127
+ right,
128
+ (newValue) => {
129
+ watchers.forEach((w) => w.pause());
130
+ left.value = transformRTL(newValue);
131
+ watchers.forEach((w) => w.resume());
132
+ },
133
+ { flush, deep, immediate }
134
+ ));
52
135
  }
53
- return () => {
54
- watchLeft == null ? void 0 : watchLeft();
55
- watchRight == null ? void 0 : watchRight();
136
+ const stop = () => {
137
+ watchers.forEach((w) => w.stop());
56
138
  };
139
+ return stop;
57
140
  }
58
141
 
59
- function tryOnMounted(fn, sync = true) {
60
- if (vue.getCurrentInstance())
61
- vue.onMounted(fn);
142
+ function tryOnMounted(fn, sync = true, target) {
143
+ const instance = getLifeCycleTarget();
144
+ if (instance)
145
+ vue.onMounted(fn, target);
62
146
  else if (sync)
63
147
  fn();
64
148
  else
65
149
  vue.nextTick(fn);
66
150
  }
67
151
 
152
+ function watchImmediate(source, cb, options) {
153
+ return vue.watch(
154
+ source,
155
+ cb,
156
+ {
157
+ ...options,
158
+ immediate: true
159
+ }
160
+ );
161
+ }
162
+
163
+ function watchOnce(source, cb, options) {
164
+ const stop = vue.watch(source, (...args) => {
165
+ vue.nextTick(() => stop());
166
+ return cb(...args);
167
+ }, options);
168
+ return stop;
169
+ }
170
+
171
+ const defaultWindow = isClient ? window : void 0;
172
+
68
173
  function unrefElement(elRef) {
69
174
  var _a;
70
- const plain = resolveUnref(elRef);
175
+ const plain = vue.toValue(elRef);
71
176
  return (_a = plain == null ? void 0 : plain.$el) != null ? _a : plain;
72
177
  }
73
178
 
74
- const defaultWindow = isClient ? window : void 0;
75
-
76
179
  function useEventListener(...args) {
77
- let target;
78
- let events;
79
- let listeners;
80
- let options;
81
- if (isString(args[0]) || Array.isArray(args[0])) {
82
- [events, listeners, options] = args;
83
- target = defaultWindow;
84
- } else {
85
- [target, events, listeners, options] = args;
86
- }
87
- if (!target)
88
- return noop;
89
- if (!Array.isArray(events))
90
- events = [events];
91
- if (!Array.isArray(listeners))
92
- listeners = [listeners];
93
180
  const cleanups = [];
94
181
  const cleanup = () => {
95
182
  cleanups.forEach((fn) => fn());
96
183
  cleanups.length = 0;
97
184
  };
98
- const register = (el, event, listener) => {
185
+ const register = (el, event, listener, options) => {
99
186
  el.addEventListener(event, listener, options);
100
187
  return () => el.removeEventListener(event, listener, options);
101
188
  };
102
- const stopWatch = vue.watch(() => unrefElement(target), (el) => {
103
- cleanup();
104
- if (!el)
105
- return;
106
- cleanups.push(...events.flatMap((event) => {
107
- return listeners.map((listener) => register(el, event, listener));
108
- }));
109
- }, { immediate: true, flush: "post" });
189
+ const firstParamTargets = vue.computed(() => {
190
+ const test = toArray(vue.toValue(args[0])).filter((e) => e != null);
191
+ return test.every((e) => typeof e !== "string") ? test : void 0;
192
+ });
193
+ const stopWatch = watchImmediate(
194
+ () => {
195
+ var _a, _b;
196
+ return [
197
+ (_b = (_a = firstParamTargets.value) == null ? void 0 : _a.map((e) => unrefElement(e))) != null ? _b : [defaultWindow].filter((e) => e != null),
198
+ toArray(vue.toValue(firstParamTargets.value ? args[1] : args[0])),
199
+ toArray(vue.unref(firstParamTargets.value ? args[2] : args[1])),
200
+ // @ts-expect-error - TypeScript gets the correct types, but somehow still complains
201
+ vue.toValue(firstParamTargets.value ? args[3] : args[2])
202
+ ];
203
+ },
204
+ ([raw_targets, raw_events, raw_listeners, raw_options]) => {
205
+ cleanup();
206
+ 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))
207
+ return;
208
+ const optionsClone = isObject(raw_options) ? { ...raw_options } : raw_options;
209
+ cleanups.push(
210
+ ...raw_targets.flatMap(
211
+ (el) => raw_events.flatMap(
212
+ (event) => raw_listeners.map((listener) => register(el, event, listener, optionsClone))
213
+ )
214
+ )
215
+ );
216
+ },
217
+ { flush: "post" }
218
+ );
110
219
  const stop = () => {
111
220
  stopWatch();
112
221
  cleanup();
113
222
  };
114
- tryOnScopeDispose(stop);
223
+ tryOnScopeDispose(cleanup);
115
224
  return stop;
116
225
  }
117
226
 
118
- function useSupported(callback, sync = false) {
119
- const isSupported = vue.ref();
120
- const update = () => isSupported.value = Boolean(callback());
121
- update();
122
- tryOnMounted(update, sync);
123
- return isSupported;
227
+ function useMounted() {
228
+ const isMounted = vue.shallowRef(false);
229
+ const instance = vue.getCurrentInstance();
230
+ if (instance) {
231
+ vue.onMounted(() => {
232
+ isMounted.value = true;
233
+ }, instance);
234
+ }
235
+ return isMounted;
124
236
  }
125
237
 
126
- const _global = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
127
- const globalKey = "__vueuse_ssr_handlers__";
128
- _global[globalKey] = _global[globalKey] || {};
238
+ function useSupported(callback) {
239
+ const isMounted = useMounted();
240
+ return vue.computed(() => {
241
+ isMounted.value;
242
+ return Boolean(callback());
243
+ });
244
+ }
129
245
 
130
- var __getOwnPropSymbols$f = Object.getOwnPropertySymbols;
131
- var __hasOwnProp$f = Object.prototype.hasOwnProperty;
132
- var __propIsEnum$f = Object.prototype.propertyIsEnumerable;
133
- var __objRest$2 = (source, exclude) => {
134
- var target = {};
135
- for (var prop in source)
136
- if (__hasOwnProp$f.call(source, prop) && exclude.indexOf(prop) < 0)
137
- target[prop] = source[prop];
138
- if (source != null && __getOwnPropSymbols$f)
139
- for (var prop of __getOwnPropSymbols$f(source)) {
140
- if (exclude.indexOf(prop) < 0 && __propIsEnum$f.call(source, prop))
141
- target[prop] = source[prop];
246
+ function useMutationObserver(target, callback, options = {}) {
247
+ const { window = defaultWindow, ...mutationOptions } = options;
248
+ let observer;
249
+ const isSupported = useSupported(() => window && "MutationObserver" in window);
250
+ const cleanup = () => {
251
+ if (observer) {
252
+ observer.disconnect();
253
+ observer = void 0;
142
254
  }
143
- return target;
144
- };
255
+ };
256
+ const targets = vue.computed(() => {
257
+ const value = vue.toValue(target);
258
+ const items = toArray(value).map(unrefElement).filter(notNullish);
259
+ return new Set(items);
260
+ });
261
+ const stopWatch = vue.watch(
262
+ () => targets.value,
263
+ (targets2) => {
264
+ cleanup();
265
+ if (isSupported.value && targets2.size) {
266
+ observer = new MutationObserver(callback);
267
+ targets2.forEach((el) => observer.observe(el, mutationOptions));
268
+ }
269
+ },
270
+ { immediate: true, flush: "post" }
271
+ );
272
+ const takeRecords = () => {
273
+ return observer == null ? void 0 : observer.takeRecords();
274
+ };
275
+ const stop = () => {
276
+ stopWatch();
277
+ cleanup();
278
+ };
279
+ tryOnScopeDispose(stop);
280
+ return {
281
+ isSupported,
282
+ stop,
283
+ takeRecords
284
+ };
285
+ }
286
+
145
287
  function useResizeObserver(target, callback, options = {}) {
146
- const _a = options, { window = defaultWindow } = _a, observerOptions = __objRest$2(_a, ["window"]);
288
+ const { window = defaultWindow, ...observerOptions } = options;
147
289
  let observer;
148
290
  const isSupported = useSupported(() => window && "ResizeObserver" in window);
149
291
  const cleanup = () => {
@@ -152,13 +294,24 @@ function useResizeObserver(target, callback, options = {}) {
152
294
  observer = void 0;
153
295
  }
154
296
  };
155
- const stopWatch = vue.watch(() => unrefElement(target), (el) => {
156
- cleanup();
157
- if (isSupported.value && window && el) {
158
- observer = new ResizeObserver(callback);
159
- observer.observe(el, observerOptions);
160
- }
161
- }, { immediate: true, flush: "post" });
297
+ const targets = vue.computed(() => {
298
+ const _targets = vue.toValue(target);
299
+ return Array.isArray(_targets) ? _targets.map((el) => unrefElement(el)) : [unrefElement(_targets)];
300
+ });
301
+ const stopWatch = vue.watch(
302
+ targets,
303
+ (els) => {
304
+ cleanup();
305
+ if (isSupported.value && window) {
306
+ observer = new ResizeObserver(callback);
307
+ for (const _el of els) {
308
+ if (_el)
309
+ observer.observe(_el, observerOptions);
310
+ }
311
+ }
312
+ },
313
+ { immediate: true, flush: "post" }
314
+ );
162
315
  const stop = () => {
163
316
  cleanup();
164
317
  stopWatch();
@@ -175,17 +328,18 @@ function useElementBounding(target, options = {}) {
175
328
  reset = true,
176
329
  windowResize = true,
177
330
  windowScroll = true,
178
- immediate = true
331
+ immediate = true,
332
+ updateTiming = "sync"
179
333
  } = options;
180
- const height = vue.ref(0);
181
- const bottom = vue.ref(0);
182
- const left = vue.ref(0);
183
- const right = vue.ref(0);
184
- const top = vue.ref(0);
185
- const width = vue.ref(0);
186
- const x = vue.ref(0);
187
- const y = vue.ref(0);
188
- function update() {
334
+ const height = vue.shallowRef(0);
335
+ const bottom = vue.shallowRef(0);
336
+ const left = vue.shallowRef(0);
337
+ const right = vue.shallowRef(0);
338
+ const top = vue.shallowRef(0);
339
+ const width = vue.shallowRef(0);
340
+ const x = vue.shallowRef(0);
341
+ const y = vue.shallowRef(0);
342
+ function recalculate() {
189
343
  const el = unrefElement(target);
190
344
  if (!el) {
191
345
  if (reset) {
@@ -210,8 +364,17 @@ function useElementBounding(target, options = {}) {
210
364
  x.value = rect.x;
211
365
  y.value = rect.y;
212
366
  }
367
+ function update() {
368
+ if (updateTiming === "sync")
369
+ recalculate();
370
+ else if (updateTiming === "next-frame")
371
+ requestAnimationFrame(() => recalculate());
372
+ }
213
373
  useResizeObserver(target, update);
214
374
  vue.watch(() => unrefElement(target), (ele) => !ele && update());
375
+ useMutationObserver(target, update, {
376
+ attributeFilter: ["style", "class"]
377
+ });
215
378
  if (windowScroll)
216
379
  useEventListener("scroll", update, { capture: true, passive: true });
217
380
  if (windowResize)
@@ -233,84 +396,101 @@ function useElementBounding(target, options = {}) {
233
396
  };
234
397
  }
235
398
 
236
- function useElementVisibility(element, { window = defaultWindow, scrollTarget } = {}) {
237
- const elementIsVisible = vue.ref(false);
238
- const testBounding = () => {
239
- if (!window)
240
- return;
241
- const document = window.document;
242
- const el = unrefElement(element);
243
- if (!el) {
244
- elementIsVisible.value = false;
245
- } else {
246
- const rect = el.getBoundingClientRect();
247
- elementIsVisible.value = rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth) && rect.bottom >= 0 && rect.right >= 0;
248
- }
399
+ function useIntersectionObserver(target, callback, options = {}) {
400
+ const {
401
+ root,
402
+ rootMargin = "0px",
403
+ threshold = 0,
404
+ window = defaultWindow,
405
+ immediate = true
406
+ } = options;
407
+ const isSupported = useSupported(() => window && "IntersectionObserver" in window);
408
+ const targets = vue.computed(() => {
409
+ const _target = vue.toValue(target);
410
+ return toArray(_target).map(unrefElement).filter(notNullish);
411
+ });
412
+ let cleanup = noop;
413
+ const isActive = vue.shallowRef(immediate);
414
+ const stopWatch = isSupported.value ? vue.watch(
415
+ () => [targets.value, unrefElement(root), isActive.value],
416
+ ([targets2, root2]) => {
417
+ cleanup();
418
+ if (!isActive.value)
419
+ return;
420
+ if (!targets2.length)
421
+ return;
422
+ const observer = new IntersectionObserver(
423
+ callback,
424
+ {
425
+ root: unrefElement(root2),
426
+ rootMargin,
427
+ threshold
428
+ }
429
+ );
430
+ targets2.forEach((el) => el && observer.observe(el));
431
+ cleanup = () => {
432
+ observer.disconnect();
433
+ cleanup = noop;
434
+ };
435
+ },
436
+ { immediate, flush: "post" }
437
+ ) : noop;
438
+ const stop = () => {
439
+ cleanup();
440
+ stopWatch();
441
+ isActive.value = false;
442
+ };
443
+ tryOnScopeDispose(stop);
444
+ return {
445
+ isSupported,
446
+ isActive,
447
+ pause() {
448
+ cleanup();
449
+ isActive.value = false;
450
+ },
451
+ resume() {
452
+ isActive.value = true;
453
+ },
454
+ stop
249
455
  };
250
- vue.watch(() => unrefElement(element), () => testBounding(), { immediate: true, flush: "post" });
251
- if (window) {
252
- useEventListener(scrollTarget || window, "scroll", testBounding, {
253
- capture: false,
254
- passive: true
255
- });
256
- }
257
- return elementIsVisible;
258
456
  }
259
457
 
260
- var SwipeDirection;
261
- (function(SwipeDirection2) {
262
- SwipeDirection2["UP"] = "UP";
263
- SwipeDirection2["RIGHT"] = "RIGHT";
264
- SwipeDirection2["DOWN"] = "DOWN";
265
- SwipeDirection2["LEFT"] = "LEFT";
266
- SwipeDirection2["NONE"] = "NONE";
267
- })(SwipeDirection || (SwipeDirection = {}));
268
-
269
- var __defProp = Object.defineProperty;
270
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
271
- var __hasOwnProp = Object.prototype.hasOwnProperty;
272
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
273
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
274
- var __spreadValues = (a, b) => {
275
- for (var prop in b || (b = {}))
276
- if (__hasOwnProp.call(b, prop))
277
- __defNormalProp(a, prop, b[prop]);
278
- if (__getOwnPropSymbols)
279
- for (var prop of __getOwnPropSymbols(b)) {
280
- if (__propIsEnum.call(b, prop))
281
- __defNormalProp(a, prop, b[prop]);
458
+ function useElementVisibility(element, options = {}) {
459
+ const {
460
+ window = defaultWindow,
461
+ scrollTarget,
462
+ threshold = 0,
463
+ rootMargin,
464
+ once = false
465
+ } = options;
466
+ const elementIsVisible = vue.shallowRef(false);
467
+ const { stop } = useIntersectionObserver(
468
+ element,
469
+ (intersectionObserverEntries) => {
470
+ let isIntersecting = elementIsVisible.value;
471
+ let latestTime = 0;
472
+ for (const entry of intersectionObserverEntries) {
473
+ if (entry.time >= latestTime) {
474
+ latestTime = entry.time;
475
+ isIntersecting = entry.isIntersecting;
476
+ }
477
+ }
478
+ elementIsVisible.value = isIntersecting;
479
+ if (once) {
480
+ watchOnce(elementIsVisible, () => {
481
+ stop();
482
+ });
483
+ }
484
+ },
485
+ {
486
+ root: scrollTarget,
487
+ window,
488
+ threshold,
489
+ rootMargin: vue.toValue(rootMargin)
282
490
  }
283
- return a;
284
- };
285
- const _TransitionPresets = {
286
- easeInSine: [0.12, 0, 0.39, 0],
287
- easeOutSine: [0.61, 1, 0.88, 1],
288
- easeInOutSine: [0.37, 0, 0.63, 1],
289
- easeInQuad: [0.11, 0, 0.5, 0],
290
- easeOutQuad: [0.5, 1, 0.89, 1],
291
- easeInOutQuad: [0.45, 0, 0.55, 1],
292
- easeInCubic: [0.32, 0, 0.67, 0],
293
- easeOutCubic: [0.33, 1, 0.68, 1],
294
- easeInOutCubic: [0.65, 0, 0.35, 1],
295
- easeInQuart: [0.5, 0, 0.75, 0],
296
- easeOutQuart: [0.25, 1, 0.5, 1],
297
- easeInOutQuart: [0.76, 0, 0.24, 1],
298
- easeInQuint: [0.64, 0, 0.78, 0],
299
- easeOutQuint: [0.22, 1, 0.36, 1],
300
- easeInOutQuint: [0.83, 0, 0.17, 1],
301
- easeInExpo: [0.7, 0, 0.84, 0],
302
- easeOutExpo: [0.16, 1, 0.3, 1],
303
- easeInOutExpo: [0.87, 0, 0.13, 1],
304
- easeInCirc: [0.55, 0, 1, 0.45],
305
- easeOutCirc: [0, 0.55, 0.45, 1],
306
- easeInOutCirc: [0.85, 0, 0.15, 1],
307
- easeInBack: [0.36, 0, 0.66, -0.56],
308
- easeOutBack: [0.34, 1.56, 0.64, 1],
309
- easeInOutBack: [0.68, -0.6, 0.32, 1.6]
310
- };
311
- __spreadValues({
312
- linear: identity
313
- }, _TransitionPresets);
491
+ );
492
+ return elementIsVisible;
493
+ }
314
494
 
315
495
  const affixProps = shared.defineHookProps(
316
496
  {
@@ -1032,6 +1212,1000 @@ const HiTabs = vue.defineComponent({
1032
1212
  }
1033
1213
  });
1034
1214
 
1215
+ function memo(getDeps, fn, opts) {
1216
+ let deps = opts.initialDeps ?? [];
1217
+ let result;
1218
+ function memoizedFunction() {
1219
+ var _a, _b, _c, _d;
1220
+ let depTime;
1221
+ if (opts.key && ((_a = opts.debug) == null ? void 0 : _a.call(opts))) depTime = Date.now();
1222
+ const newDeps = getDeps();
1223
+ const depsChanged = newDeps.length !== deps.length || newDeps.some((dep, index) => deps[index] !== dep);
1224
+ if (!depsChanged) {
1225
+ return result;
1226
+ }
1227
+ deps = newDeps;
1228
+ let resultTime;
1229
+ if (opts.key && ((_b = opts.debug) == null ? void 0 : _b.call(opts))) resultTime = Date.now();
1230
+ result = fn(...newDeps);
1231
+ if (opts.key && ((_c = opts.debug) == null ? void 0 : _c.call(opts))) {
1232
+ const depEndTime = Math.round((Date.now() - depTime) * 100) / 100;
1233
+ const resultEndTime = Math.round((Date.now() - resultTime) * 100) / 100;
1234
+ const resultFpsPercentage = resultEndTime / 16;
1235
+ const pad = (str, num) => {
1236
+ str = String(str);
1237
+ while (str.length < num) {
1238
+ str = " " + str;
1239
+ }
1240
+ return str;
1241
+ };
1242
+ console.info(
1243
+ `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,
1244
+ `
1245
+ font-size: .6rem;
1246
+ font-weight: bold;
1247
+ color: hsl(${Math.max(
1248
+ 0,
1249
+ Math.min(120 - 120 * resultFpsPercentage, 120)
1250
+ )}deg 100% 31%);`,
1251
+ opts == null ? void 0 : opts.key
1252
+ );
1253
+ }
1254
+ (_d = opts == null ? void 0 : opts.onChange) == null ? void 0 : _d.call(opts, result);
1255
+ return result;
1256
+ }
1257
+ memoizedFunction.updateDeps = (newDeps) => {
1258
+ deps = newDeps;
1259
+ };
1260
+ return memoizedFunction;
1261
+ }
1262
+ function notUndefined(value, msg) {
1263
+ if (value === void 0) {
1264
+ throw new Error(`Unexpected undefined${""}`);
1265
+ } else {
1266
+ return value;
1267
+ }
1268
+ }
1269
+ const approxEqual = (a, b) => Math.abs(a - b) < 1.01;
1270
+ const debounce = (targetWindow, fn, ms) => {
1271
+ let timeoutId;
1272
+ return function(...args) {
1273
+ targetWindow.clearTimeout(timeoutId);
1274
+ timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms);
1275
+ };
1276
+ };
1277
+
1278
+ const getRect = (element) => {
1279
+ const { offsetWidth, offsetHeight } = element;
1280
+ return { width: offsetWidth, height: offsetHeight };
1281
+ };
1282
+ const defaultKeyExtractor = (index) => index;
1283
+ const defaultRangeExtractor = (range) => {
1284
+ const start = Math.max(range.startIndex - range.overscan, 0);
1285
+ const end = Math.min(range.endIndex + range.overscan, range.count - 1);
1286
+ const arr = [];
1287
+ for (let i = start; i <= end; i++) {
1288
+ arr.push(i);
1289
+ }
1290
+ return arr;
1291
+ };
1292
+ const observeElementRect = (instance, cb) => {
1293
+ const element = instance.scrollElement;
1294
+ if (!element) {
1295
+ return;
1296
+ }
1297
+ const targetWindow = instance.targetWindow;
1298
+ if (!targetWindow) {
1299
+ return;
1300
+ }
1301
+ const handler = (rect) => {
1302
+ const { width, height } = rect;
1303
+ cb({ width: Math.round(width), height: Math.round(height) });
1304
+ };
1305
+ handler(getRect(element));
1306
+ if (!targetWindow.ResizeObserver) {
1307
+ return () => {
1308
+ };
1309
+ }
1310
+ const observer = new targetWindow.ResizeObserver((entries) => {
1311
+ const run = () => {
1312
+ const entry = entries[0];
1313
+ if (entry == null ? void 0 : entry.borderBoxSize) {
1314
+ const box = entry.borderBoxSize[0];
1315
+ if (box) {
1316
+ handler({ width: box.inlineSize, height: box.blockSize });
1317
+ return;
1318
+ }
1319
+ }
1320
+ handler(getRect(element));
1321
+ };
1322
+ instance.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
1323
+ });
1324
+ observer.observe(element, { box: "border-box" });
1325
+ return () => {
1326
+ observer.unobserve(element);
1327
+ };
1328
+ };
1329
+ const addEventListenerOptions = {
1330
+ passive: true
1331
+ };
1332
+ const supportsScrollend = typeof window == "undefined" ? true : "onscrollend" in window;
1333
+ const observeElementOffset = (instance, cb) => {
1334
+ const element = instance.scrollElement;
1335
+ if (!element) {
1336
+ return;
1337
+ }
1338
+ const targetWindow = instance.targetWindow;
1339
+ if (!targetWindow) {
1340
+ return;
1341
+ }
1342
+ let offset = 0;
1343
+ const fallback = instance.options.useScrollendEvent && supportsScrollend ? () => void 0 : debounce(
1344
+ targetWindow,
1345
+ () => {
1346
+ cb(offset, false);
1347
+ },
1348
+ instance.options.isScrollingResetDelay
1349
+ );
1350
+ const createHandler = (isScrolling) => () => {
1351
+ const { horizontal, isRtl } = instance.options;
1352
+ offset = horizontal ? element["scrollLeft"] * (isRtl && -1 || 1) : element["scrollTop"];
1353
+ fallback();
1354
+ cb(offset, isScrolling);
1355
+ };
1356
+ const handler = createHandler(true);
1357
+ const endHandler = createHandler(false);
1358
+ endHandler();
1359
+ element.addEventListener("scroll", handler, addEventListenerOptions);
1360
+ const registerScrollendEvent = instance.options.useScrollendEvent && supportsScrollend;
1361
+ if (registerScrollendEvent) {
1362
+ element.addEventListener("scrollend", endHandler, addEventListenerOptions);
1363
+ }
1364
+ return () => {
1365
+ element.removeEventListener("scroll", handler);
1366
+ if (registerScrollendEvent) {
1367
+ element.removeEventListener("scrollend", endHandler);
1368
+ }
1369
+ };
1370
+ };
1371
+ const measureElement = (element, entry, instance) => {
1372
+ if (entry == null ? void 0 : entry.borderBoxSize) {
1373
+ const box = entry.borderBoxSize[0];
1374
+ if (box) {
1375
+ const size = Math.round(
1376
+ box[instance.options.horizontal ? "inlineSize" : "blockSize"]
1377
+ );
1378
+ return size;
1379
+ }
1380
+ }
1381
+ return element[instance.options.horizontal ? "offsetWidth" : "offsetHeight"];
1382
+ };
1383
+ const elementScroll = (offset, {
1384
+ adjustments = 0,
1385
+ behavior
1386
+ }, instance) => {
1387
+ var _a, _b;
1388
+ const toOffset = offset + adjustments;
1389
+ (_b = (_a = instance.scrollElement) == null ? void 0 : _a.scrollTo) == null ? void 0 : _b.call(_a, {
1390
+ [instance.options.horizontal ? "left" : "top"]: toOffset,
1391
+ behavior
1392
+ });
1393
+ };
1394
+ class Virtualizer {
1395
+ constructor(opts) {
1396
+ this.unsubs = [];
1397
+ this.scrollElement = null;
1398
+ this.targetWindow = null;
1399
+ this.isScrolling = false;
1400
+ this.measurementsCache = [];
1401
+ this.itemSizeCache = /* @__PURE__ */ new Map();
1402
+ this.pendingMeasuredCacheIndexes = [];
1403
+ this.scrollRect = null;
1404
+ this.scrollOffset = null;
1405
+ this.scrollDirection = null;
1406
+ this.scrollAdjustments = 0;
1407
+ this.elementsCache = /* @__PURE__ */ new Map();
1408
+ this.observer = /* @__PURE__ */ (() => {
1409
+ let _ro = null;
1410
+ const get = () => {
1411
+ if (_ro) {
1412
+ return _ro;
1413
+ }
1414
+ if (!this.targetWindow || !this.targetWindow.ResizeObserver) {
1415
+ return null;
1416
+ }
1417
+ return _ro = new this.targetWindow.ResizeObserver((entries) => {
1418
+ entries.forEach((entry) => {
1419
+ const run = () => {
1420
+ this._measureElement(entry.target, entry);
1421
+ };
1422
+ this.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
1423
+ });
1424
+ });
1425
+ };
1426
+ return {
1427
+ disconnect: () => {
1428
+ var _a;
1429
+ (_a = get()) == null ? void 0 : _a.disconnect();
1430
+ _ro = null;
1431
+ },
1432
+ observe: (target) => {
1433
+ var _a;
1434
+ return (_a = get()) == null ? void 0 : _a.observe(target, { box: "border-box" });
1435
+ },
1436
+ unobserve: (target) => {
1437
+ var _a;
1438
+ return (_a = get()) == null ? void 0 : _a.unobserve(target);
1439
+ }
1440
+ };
1441
+ })();
1442
+ this.range = null;
1443
+ this.setOptions = (opts2) => {
1444
+ Object.entries(opts2).forEach(([key, value]) => {
1445
+ if (typeof value === "undefined") delete opts2[key];
1446
+ });
1447
+ this.options = {
1448
+ debug: false,
1449
+ initialOffset: 0,
1450
+ overscan: 1,
1451
+ paddingStart: 0,
1452
+ paddingEnd: 0,
1453
+ scrollPaddingStart: 0,
1454
+ scrollPaddingEnd: 0,
1455
+ horizontal: false,
1456
+ getItemKey: defaultKeyExtractor,
1457
+ rangeExtractor: defaultRangeExtractor,
1458
+ onChange: () => {
1459
+ },
1460
+ measureElement,
1461
+ initialRect: { width: 0, height: 0 },
1462
+ scrollMargin: 0,
1463
+ gap: 0,
1464
+ indexAttribute: "data-index",
1465
+ initialMeasurementsCache: [],
1466
+ lanes: 1,
1467
+ isScrollingResetDelay: 150,
1468
+ enabled: true,
1469
+ isRtl: false,
1470
+ useScrollendEvent: false,
1471
+ useAnimationFrameWithResizeObserver: false,
1472
+ ...opts2
1473
+ };
1474
+ };
1475
+ this.notify = (sync) => {
1476
+ var _a, _b;
1477
+ (_b = (_a = this.options).onChange) == null ? void 0 : _b.call(_a, this, sync);
1478
+ };
1479
+ this.maybeNotify = memo(
1480
+ () => {
1481
+ this.calculateRange();
1482
+ return [
1483
+ this.isScrolling,
1484
+ this.range ? this.range.startIndex : null,
1485
+ this.range ? this.range.endIndex : null
1486
+ ];
1487
+ },
1488
+ (isScrolling) => {
1489
+ this.notify(isScrolling);
1490
+ },
1491
+ {
1492
+ key: process.env.NODE_ENV !== "production" && "maybeNotify",
1493
+ debug: () => this.options.debug,
1494
+ initialDeps: [
1495
+ this.isScrolling,
1496
+ this.range ? this.range.startIndex : null,
1497
+ this.range ? this.range.endIndex : null
1498
+ ]
1499
+ }
1500
+ );
1501
+ this.cleanup = () => {
1502
+ this.unsubs.filter(Boolean).forEach((d) => d());
1503
+ this.unsubs = [];
1504
+ this.observer.disconnect();
1505
+ this.scrollElement = null;
1506
+ this.targetWindow = null;
1507
+ };
1508
+ this._didMount = () => {
1509
+ return () => {
1510
+ this.cleanup();
1511
+ };
1512
+ };
1513
+ this._willUpdate = () => {
1514
+ var _a;
1515
+ const scrollElement = this.options.enabled ? this.options.getScrollElement() : null;
1516
+ if (this.scrollElement !== scrollElement) {
1517
+ this.cleanup();
1518
+ if (!scrollElement) {
1519
+ this.maybeNotify();
1520
+ return;
1521
+ }
1522
+ this.scrollElement = scrollElement;
1523
+ if (this.scrollElement && "ownerDocument" in this.scrollElement) {
1524
+ this.targetWindow = this.scrollElement.ownerDocument.defaultView;
1525
+ } else {
1526
+ this.targetWindow = ((_a = this.scrollElement) == null ? void 0 : _a.window) ?? null;
1527
+ }
1528
+ this.elementsCache.forEach((cached) => {
1529
+ this.observer.observe(cached);
1530
+ });
1531
+ this._scrollToOffset(this.getScrollOffset(), {
1532
+ adjustments: void 0,
1533
+ behavior: void 0
1534
+ });
1535
+ this.unsubs.push(
1536
+ this.options.observeElementRect(this, (rect) => {
1537
+ this.scrollRect = rect;
1538
+ this.maybeNotify();
1539
+ })
1540
+ );
1541
+ this.unsubs.push(
1542
+ this.options.observeElementOffset(this, (offset, isScrolling) => {
1543
+ this.scrollAdjustments = 0;
1544
+ this.scrollDirection = isScrolling ? this.getScrollOffset() < offset ? "forward" : "backward" : null;
1545
+ this.scrollOffset = offset;
1546
+ this.isScrolling = isScrolling;
1547
+ this.maybeNotify();
1548
+ })
1549
+ );
1550
+ }
1551
+ };
1552
+ this.getSize = () => {
1553
+ if (!this.options.enabled) {
1554
+ this.scrollRect = null;
1555
+ return 0;
1556
+ }
1557
+ this.scrollRect = this.scrollRect ?? this.options.initialRect;
1558
+ return this.scrollRect[this.options.horizontal ? "width" : "height"];
1559
+ };
1560
+ this.getScrollOffset = () => {
1561
+ if (!this.options.enabled) {
1562
+ this.scrollOffset = null;
1563
+ return 0;
1564
+ }
1565
+ this.scrollOffset = this.scrollOffset ?? (typeof this.options.initialOffset === "function" ? this.options.initialOffset() : this.options.initialOffset);
1566
+ return this.scrollOffset;
1567
+ };
1568
+ this.getFurthestMeasurement = (measurements, index) => {
1569
+ const furthestMeasurementsFound = /* @__PURE__ */ new Map();
1570
+ const furthestMeasurements = /* @__PURE__ */ new Map();
1571
+ for (let m = index - 1; m >= 0; m--) {
1572
+ const measurement = measurements[m];
1573
+ if (furthestMeasurementsFound.has(measurement.lane)) {
1574
+ continue;
1575
+ }
1576
+ const previousFurthestMeasurement = furthestMeasurements.get(
1577
+ measurement.lane
1578
+ );
1579
+ if (previousFurthestMeasurement == null || measurement.end > previousFurthestMeasurement.end) {
1580
+ furthestMeasurements.set(measurement.lane, measurement);
1581
+ } else if (measurement.end < previousFurthestMeasurement.end) {
1582
+ furthestMeasurementsFound.set(measurement.lane, true);
1583
+ }
1584
+ if (furthestMeasurementsFound.size === this.options.lanes) {
1585
+ break;
1586
+ }
1587
+ }
1588
+ return furthestMeasurements.size === this.options.lanes ? Array.from(furthestMeasurements.values()).sort((a, b) => {
1589
+ if (a.end === b.end) {
1590
+ return a.index - b.index;
1591
+ }
1592
+ return a.end - b.end;
1593
+ })[0] : void 0;
1594
+ };
1595
+ this.getMeasurementOptions = memo(
1596
+ () => [
1597
+ this.options.count,
1598
+ this.options.paddingStart,
1599
+ this.options.scrollMargin,
1600
+ this.options.getItemKey,
1601
+ this.options.enabled
1602
+ ],
1603
+ (count, paddingStart, scrollMargin, getItemKey, enabled) => {
1604
+ this.pendingMeasuredCacheIndexes = [];
1605
+ return {
1606
+ count,
1607
+ paddingStart,
1608
+ scrollMargin,
1609
+ getItemKey,
1610
+ enabled
1611
+ };
1612
+ },
1613
+ {
1614
+ key: false
1615
+ }
1616
+ );
1617
+ this.getMeasurements = memo(
1618
+ () => [this.getMeasurementOptions(), this.itemSizeCache],
1619
+ ({ count, paddingStart, scrollMargin, getItemKey, enabled }, itemSizeCache) => {
1620
+ if (!enabled) {
1621
+ this.measurementsCache = [];
1622
+ this.itemSizeCache.clear();
1623
+ return [];
1624
+ }
1625
+ if (this.measurementsCache.length === 0) {
1626
+ this.measurementsCache = this.options.initialMeasurementsCache;
1627
+ this.measurementsCache.forEach((item) => {
1628
+ this.itemSizeCache.set(item.key, item.size);
1629
+ });
1630
+ }
1631
+ const min = this.pendingMeasuredCacheIndexes.length > 0 ? Math.min(...this.pendingMeasuredCacheIndexes) : 0;
1632
+ this.pendingMeasuredCacheIndexes = [];
1633
+ const measurements = this.measurementsCache.slice(0, min);
1634
+ for (let i = min; i < count; i++) {
1635
+ const key = getItemKey(i);
1636
+ const furthestMeasurement = this.options.lanes === 1 ? measurements[i - 1] : this.getFurthestMeasurement(measurements, i);
1637
+ const start = furthestMeasurement ? furthestMeasurement.end + this.options.gap : paddingStart + scrollMargin;
1638
+ const measuredSize = itemSizeCache.get(key);
1639
+ const size = typeof measuredSize === "number" ? measuredSize : this.options.estimateSize(i);
1640
+ const end = start + size;
1641
+ const lane = furthestMeasurement ? furthestMeasurement.lane : i % this.options.lanes;
1642
+ measurements[i] = {
1643
+ index: i,
1644
+ start,
1645
+ size,
1646
+ end,
1647
+ key,
1648
+ lane
1649
+ };
1650
+ }
1651
+ this.measurementsCache = measurements;
1652
+ return measurements;
1653
+ },
1654
+ {
1655
+ key: process.env.NODE_ENV !== "production" && "getMeasurements",
1656
+ debug: () => this.options.debug
1657
+ }
1658
+ );
1659
+ this.calculateRange = memo(
1660
+ () => [
1661
+ this.getMeasurements(),
1662
+ this.getSize(),
1663
+ this.getScrollOffset(),
1664
+ this.options.lanes
1665
+ ],
1666
+ (measurements, outerSize, scrollOffset, lanes) => {
1667
+ return this.range = measurements.length > 0 && outerSize > 0 ? calculateRange({
1668
+ measurements,
1669
+ outerSize,
1670
+ scrollOffset,
1671
+ lanes
1672
+ }) : null;
1673
+ },
1674
+ {
1675
+ key: process.env.NODE_ENV !== "production" && "calculateRange",
1676
+ debug: () => this.options.debug
1677
+ }
1678
+ );
1679
+ this.getVirtualIndexes = memo(
1680
+ () => {
1681
+ let startIndex = null;
1682
+ let endIndex = null;
1683
+ const range = this.calculateRange();
1684
+ if (range) {
1685
+ startIndex = range.startIndex;
1686
+ endIndex = range.endIndex;
1687
+ }
1688
+ this.maybeNotify.updateDeps([this.isScrolling, startIndex, endIndex]);
1689
+ return [
1690
+ this.options.rangeExtractor,
1691
+ this.options.overscan,
1692
+ this.options.count,
1693
+ startIndex,
1694
+ endIndex
1695
+ ];
1696
+ },
1697
+ (rangeExtractor, overscan, count, startIndex, endIndex) => {
1698
+ return startIndex === null || endIndex === null ? [] : rangeExtractor({
1699
+ startIndex,
1700
+ endIndex,
1701
+ overscan,
1702
+ count
1703
+ });
1704
+ },
1705
+ {
1706
+ key: process.env.NODE_ENV !== "production" && "getVirtualIndexes",
1707
+ debug: () => this.options.debug
1708
+ }
1709
+ );
1710
+ this.indexFromElement = (node) => {
1711
+ const attributeName = this.options.indexAttribute;
1712
+ const indexStr = node.getAttribute(attributeName);
1713
+ if (!indexStr) {
1714
+ console.warn(
1715
+ `Missing attribute name '${attributeName}={index}' on measured element.`
1716
+ );
1717
+ return -1;
1718
+ }
1719
+ return parseInt(indexStr, 10);
1720
+ };
1721
+ this._measureElement = (node, entry) => {
1722
+ const index = this.indexFromElement(node);
1723
+ const item = this.measurementsCache[index];
1724
+ if (!item) {
1725
+ return;
1726
+ }
1727
+ const key = item.key;
1728
+ const prevNode = this.elementsCache.get(key);
1729
+ if (prevNode !== node) {
1730
+ if (prevNode) {
1731
+ this.observer.unobserve(prevNode);
1732
+ }
1733
+ this.observer.observe(node);
1734
+ this.elementsCache.set(key, node);
1735
+ }
1736
+ if (node.isConnected) {
1737
+ this.resizeItem(index, this.options.measureElement(node, entry, this));
1738
+ }
1739
+ };
1740
+ this.resizeItem = (index, size) => {
1741
+ const item = this.measurementsCache[index];
1742
+ if (!item) {
1743
+ return;
1744
+ }
1745
+ const itemSize = this.itemSizeCache.get(item.key) ?? item.size;
1746
+ const delta = size - itemSize;
1747
+ if (delta !== 0) {
1748
+ if (this.shouldAdjustScrollPositionOnItemSizeChange !== void 0 ? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this) : item.start < this.getScrollOffset() + this.scrollAdjustments) {
1749
+ if (process.env.NODE_ENV !== "production" && this.options.debug) {
1750
+ console.info("correction", delta);
1751
+ }
1752
+ this._scrollToOffset(this.getScrollOffset(), {
1753
+ adjustments: this.scrollAdjustments += delta,
1754
+ behavior: void 0
1755
+ });
1756
+ }
1757
+ this.pendingMeasuredCacheIndexes.push(item.index);
1758
+ this.itemSizeCache = new Map(this.itemSizeCache.set(item.key, size));
1759
+ this.notify(false);
1760
+ }
1761
+ };
1762
+ this.measureElement = (node) => {
1763
+ if (!node) {
1764
+ this.elementsCache.forEach((cached, key) => {
1765
+ if (!cached.isConnected) {
1766
+ this.observer.unobserve(cached);
1767
+ this.elementsCache.delete(key);
1768
+ }
1769
+ });
1770
+ return;
1771
+ }
1772
+ this._measureElement(node, void 0);
1773
+ };
1774
+ this.getVirtualItems = memo(
1775
+ () => [this.getVirtualIndexes(), this.getMeasurements()],
1776
+ (indexes, measurements) => {
1777
+ const virtualItems = [];
1778
+ for (let k = 0, len = indexes.length; k < len; k++) {
1779
+ const i = indexes[k];
1780
+ const measurement = measurements[i];
1781
+ virtualItems.push(measurement);
1782
+ }
1783
+ return virtualItems;
1784
+ },
1785
+ {
1786
+ key: process.env.NODE_ENV !== "production" && "getVirtualItems",
1787
+ debug: () => this.options.debug
1788
+ }
1789
+ );
1790
+ this.getVirtualItemForOffset = (offset) => {
1791
+ const measurements = this.getMeasurements();
1792
+ if (measurements.length === 0) {
1793
+ return void 0;
1794
+ }
1795
+ return notUndefined(
1796
+ measurements[findNearestBinarySearch(
1797
+ 0,
1798
+ measurements.length - 1,
1799
+ (index) => notUndefined(measurements[index]).start,
1800
+ offset
1801
+ )]
1802
+ );
1803
+ };
1804
+ this.getOffsetForAlignment = (toOffset, align, itemSize = 0) => {
1805
+ const size = this.getSize();
1806
+ const scrollOffset = this.getScrollOffset();
1807
+ if (align === "auto") {
1808
+ align = toOffset >= scrollOffset + size ? "end" : "start";
1809
+ }
1810
+ if (align === "center") {
1811
+ toOffset += (itemSize - size) / 2;
1812
+ } else if (align === "end") {
1813
+ toOffset -= size;
1814
+ }
1815
+ const maxOffset = this.getTotalSize() + this.options.scrollMargin - size;
1816
+ return Math.max(Math.min(maxOffset, toOffset), 0);
1817
+ };
1818
+ this.getOffsetForIndex = (index, align = "auto") => {
1819
+ index = Math.max(0, Math.min(index, this.options.count - 1));
1820
+ const item = this.measurementsCache[index];
1821
+ if (!item) {
1822
+ return void 0;
1823
+ }
1824
+ const size = this.getSize();
1825
+ const scrollOffset = this.getScrollOffset();
1826
+ if (align === "auto") {
1827
+ if (item.end >= scrollOffset + size - this.options.scrollPaddingEnd) {
1828
+ align = "end";
1829
+ } else if (item.start <= scrollOffset + this.options.scrollPaddingStart) {
1830
+ align = "start";
1831
+ } else {
1832
+ return [scrollOffset, align];
1833
+ }
1834
+ }
1835
+ const toOffset = align === "end" ? item.end + this.options.scrollPaddingEnd : item.start - this.options.scrollPaddingStart;
1836
+ return [
1837
+ this.getOffsetForAlignment(toOffset, align, item.size),
1838
+ align
1839
+ ];
1840
+ };
1841
+ this.isDynamicMode = () => this.elementsCache.size > 0;
1842
+ this.scrollToOffset = (toOffset, { align = "start", behavior } = {}) => {
1843
+ if (behavior === "smooth" && this.isDynamicMode()) {
1844
+ console.warn(
1845
+ "The `smooth` scroll behavior is not fully supported with dynamic size."
1846
+ );
1847
+ }
1848
+ this._scrollToOffset(this.getOffsetForAlignment(toOffset, align), {
1849
+ adjustments: void 0,
1850
+ behavior
1851
+ });
1852
+ };
1853
+ this.scrollToIndex = (index, { align: initialAlign = "auto", behavior } = {}) => {
1854
+ if (behavior === "smooth" && this.isDynamicMode()) {
1855
+ console.warn(
1856
+ "The `smooth` scroll behavior is not fully supported with dynamic size."
1857
+ );
1858
+ }
1859
+ index = Math.max(0, Math.min(index, this.options.count - 1));
1860
+ let attempts = 0;
1861
+ const maxAttempts = 10;
1862
+ const tryScroll = (currentAlign) => {
1863
+ if (!this.targetWindow) return;
1864
+ const offsetInfo = this.getOffsetForIndex(index, currentAlign);
1865
+ if (!offsetInfo) {
1866
+ console.warn("Failed to get offset for index:", index);
1867
+ return;
1868
+ }
1869
+ const [offset, align] = offsetInfo;
1870
+ this._scrollToOffset(offset, { adjustments: void 0, behavior });
1871
+ this.targetWindow.requestAnimationFrame(() => {
1872
+ const currentOffset = this.getScrollOffset();
1873
+ const afterInfo = this.getOffsetForIndex(index, align);
1874
+ if (!afterInfo) {
1875
+ console.warn("Failed to get offset for index:", index);
1876
+ return;
1877
+ }
1878
+ if (!approxEqual(afterInfo[0], currentOffset)) {
1879
+ scheduleRetry(align);
1880
+ }
1881
+ });
1882
+ };
1883
+ const scheduleRetry = (align) => {
1884
+ if (!this.targetWindow) return;
1885
+ attempts++;
1886
+ if (attempts < maxAttempts) {
1887
+ if (process.env.NODE_ENV !== "production" && this.options.debug) {
1888
+ console.info("Schedule retry", attempts, maxAttempts);
1889
+ }
1890
+ this.targetWindow.requestAnimationFrame(() => tryScroll(align));
1891
+ } else {
1892
+ console.warn(
1893
+ `Failed to scroll to index ${index} after ${maxAttempts} attempts.`
1894
+ );
1895
+ }
1896
+ };
1897
+ tryScroll(initialAlign);
1898
+ };
1899
+ this.scrollBy = (delta, { behavior } = {}) => {
1900
+ if (behavior === "smooth" && this.isDynamicMode()) {
1901
+ console.warn(
1902
+ "The `smooth` scroll behavior is not fully supported with dynamic size."
1903
+ );
1904
+ }
1905
+ this._scrollToOffset(this.getScrollOffset() + delta, {
1906
+ adjustments: void 0,
1907
+ behavior
1908
+ });
1909
+ };
1910
+ this.getTotalSize = () => {
1911
+ var _a;
1912
+ const measurements = this.getMeasurements();
1913
+ let end;
1914
+ if (measurements.length === 0) {
1915
+ end = this.options.paddingStart;
1916
+ } else if (this.options.lanes === 1) {
1917
+ end = ((_a = measurements[measurements.length - 1]) == null ? void 0 : _a.end) ?? 0;
1918
+ } else {
1919
+ const endByLane = Array(this.options.lanes).fill(null);
1920
+ let endIndex = measurements.length - 1;
1921
+ while (endIndex >= 0 && endByLane.some((val) => val === null)) {
1922
+ const item = measurements[endIndex];
1923
+ if (endByLane[item.lane] === null) {
1924
+ endByLane[item.lane] = item.end;
1925
+ }
1926
+ endIndex--;
1927
+ }
1928
+ end = Math.max(...endByLane.filter((val) => val !== null));
1929
+ }
1930
+ return Math.max(
1931
+ end - this.options.scrollMargin + this.options.paddingEnd,
1932
+ 0
1933
+ );
1934
+ };
1935
+ this._scrollToOffset = (offset, {
1936
+ adjustments,
1937
+ behavior
1938
+ }) => {
1939
+ this.options.scrollToFn(offset, { behavior, adjustments }, this);
1940
+ };
1941
+ this.measure = () => {
1942
+ this.itemSizeCache = /* @__PURE__ */ new Map();
1943
+ this.notify(false);
1944
+ };
1945
+ this.setOptions(opts);
1946
+ }
1947
+ }
1948
+ const findNearestBinarySearch = (low, high, getCurrentValue, value) => {
1949
+ while (low <= high) {
1950
+ const middle = (low + high) / 2 | 0;
1951
+ const currentValue = getCurrentValue(middle);
1952
+ if (currentValue < value) {
1953
+ low = middle + 1;
1954
+ } else if (currentValue > value) {
1955
+ high = middle - 1;
1956
+ } else {
1957
+ return middle;
1958
+ }
1959
+ }
1960
+ if (low > 0) {
1961
+ return low - 1;
1962
+ } else {
1963
+ return 0;
1964
+ }
1965
+ };
1966
+ function calculateRange({
1967
+ measurements,
1968
+ outerSize,
1969
+ scrollOffset,
1970
+ lanes
1971
+ }) {
1972
+ const lastIndex = measurements.length - 1;
1973
+ const getOffset = (index) => measurements[index].start;
1974
+ if (measurements.length <= lanes) {
1975
+ return {
1976
+ startIndex: 0,
1977
+ endIndex: lastIndex
1978
+ };
1979
+ }
1980
+ let startIndex = findNearestBinarySearch(
1981
+ 0,
1982
+ lastIndex,
1983
+ getOffset,
1984
+ scrollOffset
1985
+ );
1986
+ let endIndex = startIndex;
1987
+ if (lanes === 1) {
1988
+ while (endIndex < lastIndex && measurements[endIndex].end < scrollOffset + outerSize) {
1989
+ endIndex++;
1990
+ }
1991
+ } else if (lanes > 1) {
1992
+ const endPerLane = Array(lanes).fill(0);
1993
+ while (endIndex < lastIndex && endPerLane.some((pos) => pos < scrollOffset + outerSize)) {
1994
+ const item = measurements[endIndex];
1995
+ endPerLane[item.lane] = item.end;
1996
+ endIndex++;
1997
+ }
1998
+ const startPerLane = Array(lanes).fill(scrollOffset + outerSize);
1999
+ while (startIndex >= 0 && startPerLane.some((pos) => pos >= scrollOffset)) {
2000
+ const item = measurements[startIndex];
2001
+ startPerLane[item.lane] = item.start;
2002
+ startIndex--;
2003
+ }
2004
+ startIndex = Math.max(0, startIndex - startIndex % lanes);
2005
+ endIndex = Math.min(lastIndex, endIndex + (lanes - 1 - endIndex % lanes));
2006
+ }
2007
+ return { startIndex, endIndex };
2008
+ }
2009
+
2010
+ const virtualListProps = shared.defineHookProps({
2011
+ options: {
2012
+ type: Object,
2013
+ default: () => ({})
2014
+ },
2015
+ count: {
2016
+ type: Number,
2017
+ default: () => 0
2018
+ },
2019
+ estimateSize: {
2020
+ type: [Function, Number],
2021
+ default: () => 50
2022
+ },
2023
+ horizontal: {
2024
+ type: Boolean,
2025
+ default: () => false
2026
+ }
2027
+ });
2028
+ const virtualListEmits = shared.defineHookEmits({
2029
+ scrollEnd: () => true,
2030
+ scrollStart: () => true,
2031
+ scroll: (_) => true
2032
+ });
2033
+ const useVirtualList = shared.defineHookComponent({
2034
+ props: virtualListProps,
2035
+ emits: virtualListEmits,
2036
+ setup(props, context) {
2037
+ const { emit } = context;
2038
+ const scrollElementRef = shared.elementRef();
2039
+ const propsEstimateSize = props.estimateSize;
2040
+ const estimateSize = typeof propsEstimateSize === "function" ? propsEstimateSize : () => propsEstimateSize;
2041
+ const options = vue.computed(() => {
2042
+ const opts = { ...props.options || {} };
2043
+ return {
2044
+ ...opts,
2045
+ count: props.count,
2046
+ estimateSize,
2047
+ horizontal: props.horizontal,
2048
+ getScrollElement: () => scrollElementRef.value,
2049
+ observeElementRect,
2050
+ observeElementOffset,
2051
+ scrollToFn: elementScroll
2052
+ };
2053
+ });
2054
+ const virtualizer = new Virtualizer(options.value);
2055
+ const state = vue.shallowRef(virtualizer);
2056
+ const virtualItems = vue.computed(() => state.value.getVirtualItems());
2057
+ const virtualIndexes = vue.computed(() => state.value.getVirtualIndexes());
2058
+ const totalSize = vue.computed(() => state.value.getTotalSize());
2059
+ vue.watch(
2060
+ virtualIndexes,
2061
+ (indexes) => {
2062
+ if (indexes.length === 0) {
2063
+ return;
2064
+ }
2065
+ if (indexes[indexes.length - 1] === props.count - 1) {
2066
+ emit("scrollEnd");
2067
+ } else if (indexes[0] === 0) {
2068
+ emit("scrollStart");
2069
+ }
2070
+ emit("scroll", indexes);
2071
+ },
2072
+ { immediate: true }
2073
+ );
2074
+ vue.watch(
2075
+ options,
2076
+ (opts) => {
2077
+ virtualizer.setOptions({
2078
+ ...opts,
2079
+ onChange: (instance, sync) => {
2080
+ opts.onChange?.(instance, sync);
2081
+ vue.triggerRef(state);
2082
+ }
2083
+ });
2084
+ virtualizer._willUpdate();
2085
+ vue.triggerRef(state);
2086
+ },
2087
+ { immediate: true }
2088
+ );
2089
+ vue.watch(
2090
+ scrollElementRef,
2091
+ (el) => {
2092
+ if (el) {
2093
+ virtualizer._willUpdate();
2094
+ vue.triggerRef(state);
2095
+ }
2096
+ },
2097
+ { immediate: true }
2098
+ );
2099
+ tryOnScopeDispose(virtualizer._didMount());
2100
+ const measureElement = (el) => {
2101
+ virtualizer.measureElement(el);
2102
+ };
2103
+ const scrollToIndex = (index, options2 = {
2104
+ behavior: "smooth"
2105
+ }) => {
2106
+ virtualizer.scrollToIndex(index, options2);
2107
+ };
2108
+ const scrollToStart = (options2 = {
2109
+ behavior: "smooth"
2110
+ }) => {
2111
+ scrollToIndex(0, options2);
2112
+ };
2113
+ const scrollToEnd = (options2 = {
2114
+ behavior: "smooth"
2115
+ }) => {
2116
+ scrollToIndex(props.count - 1, options2);
2117
+ };
2118
+ return {
2119
+ virtualizer,
2120
+ virtualItems,
2121
+ virtualIndexes,
2122
+ totalSize,
2123
+ scrollElementRef,
2124
+ measureElement,
2125
+ scrollToIndex,
2126
+ scrollToStart,
2127
+ scrollToEnd
2128
+ };
2129
+ }
2130
+ });
2131
+
2132
+ const HiVirtualList = vue.defineComponent({
2133
+ name: "HiVirtualList",
2134
+ inheritAttrs: true,
2135
+ props: {
2136
+ ...virtualListProps,
2137
+ as: {
2138
+ type: String,
2139
+ default: () => "div"
2140
+ },
2141
+ wrapperAs: {
2142
+ type: String,
2143
+ default: () => "div"
2144
+ },
2145
+ wrapperStyle: {
2146
+ type: Object,
2147
+ default: () => ({})
2148
+ },
2149
+ wrapperClass: {
2150
+ type: shared.classPropType,
2151
+ default: () => ""
2152
+ }
2153
+ },
2154
+ emits: virtualListEmits,
2155
+ setup(props, context) {
2156
+ const { slots, expose } = context;
2157
+ const {
2158
+ totalSize,
2159
+ scrollElementRef,
2160
+ virtualItems,
2161
+ scrollToIndex,
2162
+ scrollToStart,
2163
+ scrollToEnd
2164
+ } = useVirtualList(props, context);
2165
+ expose({
2166
+ scrollToIndex,
2167
+ scrollToStart,
2168
+ scrollToEnd
2169
+ });
2170
+ const wrapperStyle = vue.computed(() => {
2171
+ return {
2172
+ position: "relative",
2173
+ [props.horizontal ? "width" : "height"]: `${totalSize.value}px`,
2174
+ ...props.wrapperStyle
2175
+ };
2176
+ });
2177
+ return () => vue.h(
2178
+ props.as,
2179
+ {
2180
+ ref: scrollElementRef,
2181
+ style: {
2182
+ [props.horizontal ? "overflowX" : "overflowY"]: "auto"
2183
+ }
2184
+ },
2185
+ [
2186
+ vue.h(
2187
+ props.wrapperAs,
2188
+ {
2189
+ style: wrapperStyle.value,
2190
+ class: props.wrapperClass
2191
+ },
2192
+ tslx.each(virtualItems.value, (item) => {
2193
+ const slotData = {
2194
+ ...item,
2195
+ style: {
2196
+ position: "absolute",
2197
+ [props.horizontal ? "left" : "top"]: `${item.start}px`,
2198
+ [props.horizontal ? "width" : "height"]: `${item.size}px`
2199
+ }
2200
+ };
2201
+ return vue.renderSlot(slots, "item", slotData);
2202
+ })
2203
+ )
2204
+ ]
2205
+ );
2206
+ }
2207
+ });
2208
+
1035
2209
  const components = {
1036
2210
  __proto__: null,
1037
2211
  HiAffix: HiAffix,
@@ -1044,7 +2218,8 @@ const components = {
1044
2218
  HiSelection: HiSelection,
1045
2219
  HiSwitch: HiSwitch,
1046
2220
  HiTabPane: HiTabPane,
1047
- HiTabs: HiTabs
2221
+ HiTabs: HiTabs,
2222
+ HiVirtualList: HiVirtualList
1048
2223
  };
1049
2224
 
1050
2225
  function install(app) {
@@ -1064,4 +2239,5 @@ exports.HiSelection = HiSelection;
1064
2239
  exports.HiSwitch = HiSwitch;
1065
2240
  exports.HiTabPane = HiTabPane;
1066
2241
  exports.HiTabs = HiTabs;
2242
+ exports.HiVirtualList = HiVirtualList;
1067
2243
  exports.install = install;