@hoci/components 0.8.0 → 0.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/dist/index.mjs DELETED
@@ -1,2224 +0,0 @@
1
- import { defineHookProps, defineHookEmits, defineHookComponent, elementRef, toReactive, useElement, throttleByRaf, isWindow, valuePropType, labelPropType, classPropType, useSharedConfig, getFirstChilld } from '@hoci/shared';
2
- import { onMounted, nextTick, watch, getCurrentScope, onScopeDispose, getCurrentInstance, unref, readonly, toRef as toRef$1, ref, customRef, shallowRef, toValue, computed, inject, watchPostEffect, provide, defineComponent, h, renderSlot, Teleport, reactive, KeepAlive, triggerRef } from 'vue';
3
- import { px, capitalize, cls, each } from 'tslx';
4
- import { affixProps as affixProps$1, useAffix, configProviderProps, provideSharedConfig, fileUploadEmits, fileUploadProps, useFileUpload, iconProps, useIcon, itemEmits as itemEmits$1, itemProps as itemProps$1, useSelectionItem as useSelectionItem$1, popoverEmits, popoverProps, usePopover, selectionEmits as selectionEmits$1, selectionProps as selectionProps$1, useSelectionList as useSelectionList$1, switchEmits, switchProps, useSwitch } from '@hoci/core';
5
-
6
- function tryOnScopeDispose(fn) {
7
- if (getCurrentScope()) {
8
- onScopeDispose(fn);
9
- return true;
10
- }
11
- return false;
12
- }
13
-
14
- function isDefined(v) {
15
- return unref(v) != null;
16
- }
17
-
18
- const isClient = typeof window !== "undefined" && typeof document !== "undefined";
19
- typeof WorkerGlobalScope !== "undefined" && globalThis instanceof WorkerGlobalScope;
20
- const notNullish = (val) => val != null;
21
- const toString = Object.prototype.toString;
22
- const isObject = (val) => toString.call(val) === "[object Object]";
23
- const noop = () => {
24
- };
25
-
26
- function createFilterWrapper(filter, fn) {
27
- function wrapper(...args) {
28
- return new Promise((resolve, reject) => {
29
- Promise.resolve(filter(() => fn.apply(this, args), { fn, thisArg: this, args })).then(resolve).catch(reject);
30
- });
31
- }
32
- return wrapper;
33
- }
34
- const bypassFilter = (invoke) => {
35
- return invoke();
36
- };
37
- function pausableFilter(extendFilter = bypassFilter, options = {}) {
38
- const {
39
- initialState = "active"
40
- } = options;
41
- const isActive = toRef(initialState === "active");
42
- function pause() {
43
- isActive.value = false;
44
- }
45
- function resume() {
46
- isActive.value = true;
47
- }
48
- const eventFilter = (...args) => {
49
- if (isActive.value)
50
- extendFilter(...args);
51
- };
52
- return { isActive: readonly(isActive), pause, resume, eventFilter };
53
- }
54
- function getLifeCycleTarget(target) {
55
- return getCurrentInstance();
56
- }
57
- function toArray(value) {
58
- return Array.isArray(value) ? value : [value];
59
- }
60
-
61
- function toRef(...args) {
62
- if (args.length !== 1)
63
- return toRef$1(...args);
64
- const r = args[0];
65
- return typeof r === "function" ? readonly(customRef(() => ({ get: r, set: noop }))) : ref(r);
66
- }
67
-
68
- function watchWithFilter(source, cb, options = {}) {
69
- const {
70
- eventFilter = bypassFilter,
71
- ...watchOptions
72
- } = options;
73
- return watch(
74
- source,
75
- createFilterWrapper(
76
- eventFilter,
77
- cb
78
- ),
79
- watchOptions
80
- );
81
- }
82
-
83
- function watchPausable(source, cb, options = {}) {
84
- const {
85
- eventFilter: filter,
86
- initialState = "active",
87
- ...watchOptions
88
- } = options;
89
- const { eventFilter, pause, resume, isActive } = pausableFilter(filter, { initialState });
90
- const stop = watchWithFilter(
91
- source,
92
- cb,
93
- {
94
- ...watchOptions,
95
- eventFilter
96
- }
97
- );
98
- return { stop, pause, resume, isActive };
99
- }
100
-
101
- function syncRef(left, right, ...[options]) {
102
- const {
103
- flush = "sync",
104
- deep = false,
105
- immediate = true,
106
- direction = "both",
107
- transform = {}
108
- } = options || {};
109
- const watchers = [];
110
- const transformLTR = "ltr" in transform && transform.ltr || ((v) => v);
111
- const transformRTL = "rtl" in transform && transform.rtl || ((v) => v);
112
- if (direction === "both" || direction === "ltr") {
113
- watchers.push(watchPausable(
114
- left,
115
- (newValue) => {
116
- watchers.forEach((w) => w.pause());
117
- right.value = transformLTR(newValue);
118
- watchers.forEach((w) => w.resume());
119
- },
120
- { flush, deep, immediate }
121
- ));
122
- }
123
- if (direction === "both" || direction === "rtl") {
124
- watchers.push(watchPausable(
125
- right,
126
- (newValue) => {
127
- watchers.forEach((w) => w.pause());
128
- left.value = transformRTL(newValue);
129
- watchers.forEach((w) => w.resume());
130
- },
131
- { flush, deep, immediate }
132
- ));
133
- }
134
- const stop = () => {
135
- watchers.forEach((w) => w.stop());
136
- };
137
- return stop;
138
- }
139
-
140
- function tryOnMounted(fn, sync = true, target) {
141
- const instance = getLifeCycleTarget();
142
- if (instance)
143
- onMounted(fn, target);
144
- else if (sync)
145
- fn();
146
- else
147
- nextTick(fn);
148
- }
149
-
150
- function watchImmediate(source, cb, options) {
151
- return watch(
152
- source,
153
- cb,
154
- {
155
- ...options,
156
- immediate: true
157
- }
158
- );
159
- }
160
-
161
- function watchOnce(source, cb, options) {
162
- const stop = watch(source, (...args) => {
163
- nextTick(() => stop());
164
- return cb(...args);
165
- }, options);
166
- return stop;
167
- }
168
-
169
- const defaultWindow = isClient ? window : void 0;
170
-
171
- function unrefElement(elRef) {
172
- var _a;
173
- const plain = toValue(elRef);
174
- return (_a = plain == null ? void 0 : plain.$el) != null ? _a : plain;
175
- }
176
-
177
- function useEventListener(...args) {
178
- const cleanups = [];
179
- const cleanup = () => {
180
- cleanups.forEach((fn) => fn());
181
- cleanups.length = 0;
182
- };
183
- const register = (el, event, listener, options) => {
184
- el.addEventListener(event, listener, options);
185
- return () => el.removeEventListener(event, listener, options);
186
- };
187
- const firstParamTargets = computed(() => {
188
- const test = toArray(toValue(args[0])).filter((e) => e != null);
189
- return test.every((e) => typeof e !== "string") ? test : void 0;
190
- });
191
- const stopWatch = watchImmediate(
192
- () => {
193
- var _a, _b;
194
- return [
195
- (_b = (_a = firstParamTargets.value) == null ? void 0 : _a.map((e) => unrefElement(e))) != null ? _b : [defaultWindow].filter((e) => e != null),
196
- toArray(toValue(firstParamTargets.value ? args[1] : args[0])),
197
- toArray(unref(firstParamTargets.value ? args[2] : args[1])),
198
- // @ts-expect-error - TypeScript gets the correct types, but somehow still complains
199
- toValue(firstParamTargets.value ? args[3] : args[2])
200
- ];
201
- },
202
- ([raw_targets, raw_events, raw_listeners, raw_options]) => {
203
- cleanup();
204
- 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))
205
- return;
206
- const optionsClone = isObject(raw_options) ? { ...raw_options } : raw_options;
207
- cleanups.push(
208
- ...raw_targets.flatMap(
209
- (el) => raw_events.flatMap(
210
- (event) => raw_listeners.map((listener) => register(el, event, listener, optionsClone))
211
- )
212
- )
213
- );
214
- },
215
- { flush: "post" }
216
- );
217
- const stop = () => {
218
- stopWatch();
219
- cleanup();
220
- };
221
- tryOnScopeDispose(cleanup);
222
- return stop;
223
- }
224
-
225
- function useMounted() {
226
- const isMounted = shallowRef(false);
227
- const instance = getCurrentInstance();
228
- if (instance) {
229
- onMounted(() => {
230
- isMounted.value = true;
231
- }, instance);
232
- }
233
- return isMounted;
234
- }
235
-
236
- function useSupported(callback) {
237
- const isMounted = useMounted();
238
- return computed(() => {
239
- isMounted.value;
240
- return Boolean(callback());
241
- });
242
- }
243
-
244
- function useMutationObserver(target, callback, options = {}) {
245
- const { window = defaultWindow, ...mutationOptions } = options;
246
- let observer;
247
- const isSupported = useSupported(() => window && "MutationObserver" in window);
248
- const cleanup = () => {
249
- if (observer) {
250
- observer.disconnect();
251
- observer = void 0;
252
- }
253
- };
254
- const targets = computed(() => {
255
- const value = toValue(target);
256
- const items = toArray(value).map(unrefElement).filter(notNullish);
257
- return new Set(items);
258
- });
259
- const stopWatch = watch(
260
- () => targets.value,
261
- (targets2) => {
262
- cleanup();
263
- if (isSupported.value && targets2.size) {
264
- observer = new MutationObserver(callback);
265
- targets2.forEach((el) => observer.observe(el, mutationOptions));
266
- }
267
- },
268
- { immediate: true, flush: "post" }
269
- );
270
- const takeRecords = () => {
271
- return observer == null ? void 0 : observer.takeRecords();
272
- };
273
- const stop = () => {
274
- stopWatch();
275
- cleanup();
276
- };
277
- tryOnScopeDispose(stop);
278
- return {
279
- isSupported,
280
- stop,
281
- takeRecords
282
- };
283
- }
284
-
285
- function useResizeObserver(target, callback, options = {}) {
286
- const { window = defaultWindow, ...observerOptions } = options;
287
- let observer;
288
- const isSupported = useSupported(() => window && "ResizeObserver" in window);
289
- const cleanup = () => {
290
- if (observer) {
291
- observer.disconnect();
292
- observer = void 0;
293
- }
294
- };
295
- const targets = computed(() => {
296
- const _targets = toValue(target);
297
- return Array.isArray(_targets) ? _targets.map((el) => unrefElement(el)) : [unrefElement(_targets)];
298
- });
299
- const stopWatch = watch(
300
- targets,
301
- (els) => {
302
- cleanup();
303
- if (isSupported.value && window) {
304
- observer = new ResizeObserver(callback);
305
- for (const _el of els) {
306
- if (_el)
307
- observer.observe(_el, observerOptions);
308
- }
309
- }
310
- },
311
- { immediate: true, flush: "post" }
312
- );
313
- const stop = () => {
314
- cleanup();
315
- stopWatch();
316
- };
317
- tryOnScopeDispose(stop);
318
- return {
319
- isSupported,
320
- stop
321
- };
322
- }
323
-
324
- function useElementBounding(target, options = {}) {
325
- const {
326
- reset = true,
327
- windowResize = true,
328
- windowScroll = true,
329
- immediate = true,
330
- updateTiming = "sync"
331
- } = options;
332
- const height = shallowRef(0);
333
- const bottom = shallowRef(0);
334
- const left = shallowRef(0);
335
- const right = shallowRef(0);
336
- const top = shallowRef(0);
337
- const width = shallowRef(0);
338
- const x = shallowRef(0);
339
- const y = shallowRef(0);
340
- function recalculate() {
341
- const el = unrefElement(target);
342
- if (!el) {
343
- if (reset) {
344
- height.value = 0;
345
- bottom.value = 0;
346
- left.value = 0;
347
- right.value = 0;
348
- top.value = 0;
349
- width.value = 0;
350
- x.value = 0;
351
- y.value = 0;
352
- }
353
- return;
354
- }
355
- const rect = el.getBoundingClientRect();
356
- height.value = rect.height;
357
- bottom.value = rect.bottom;
358
- left.value = rect.left;
359
- right.value = rect.right;
360
- top.value = rect.top;
361
- width.value = rect.width;
362
- x.value = rect.x;
363
- y.value = rect.y;
364
- }
365
- function update() {
366
- if (updateTiming === "sync")
367
- recalculate();
368
- else if (updateTiming === "next-frame")
369
- requestAnimationFrame(() => recalculate());
370
- }
371
- useResizeObserver(target, update);
372
- watch(() => unrefElement(target), (ele) => !ele && update());
373
- useMutationObserver(target, update, {
374
- attributeFilter: ["style", "class"]
375
- });
376
- if (windowScroll)
377
- useEventListener("scroll", update, { capture: true, passive: true });
378
- if (windowResize)
379
- useEventListener("resize", update, { passive: true });
380
- tryOnMounted(() => {
381
- if (immediate)
382
- update();
383
- });
384
- return {
385
- height,
386
- bottom,
387
- left,
388
- right,
389
- top,
390
- width,
391
- x,
392
- y,
393
- update
394
- };
395
- }
396
-
397
- function useIntersectionObserver(target, callback, options = {}) {
398
- const {
399
- root,
400
- rootMargin = "0px",
401
- threshold = 0,
402
- window = defaultWindow,
403
- immediate = true
404
- } = options;
405
- const isSupported = useSupported(() => window && "IntersectionObserver" in window);
406
- const targets = computed(() => {
407
- const _target = toValue(target);
408
- return toArray(_target).map(unrefElement).filter(notNullish);
409
- });
410
- let cleanup = noop;
411
- const isActive = shallowRef(immediate);
412
- const stopWatch = isSupported.value ? watch(
413
- () => [targets.value, unrefElement(root), isActive.value],
414
- ([targets2, root2]) => {
415
- cleanup();
416
- if (!isActive.value)
417
- return;
418
- if (!targets2.length)
419
- return;
420
- const observer = new IntersectionObserver(
421
- callback,
422
- {
423
- root: unrefElement(root2),
424
- rootMargin,
425
- threshold
426
- }
427
- );
428
- targets2.forEach((el) => el && observer.observe(el));
429
- cleanup = () => {
430
- observer.disconnect();
431
- cleanup = noop;
432
- };
433
- },
434
- { immediate, flush: "post" }
435
- ) : noop;
436
- const stop = () => {
437
- cleanup();
438
- stopWatch();
439
- isActive.value = false;
440
- };
441
- tryOnScopeDispose(stop);
442
- return {
443
- isSupported,
444
- isActive,
445
- pause() {
446
- cleanup();
447
- isActive.value = false;
448
- },
449
- resume() {
450
- isActive.value = true;
451
- },
452
- stop
453
- };
454
- }
455
-
456
- function useElementVisibility(element, options = {}) {
457
- const {
458
- window = defaultWindow,
459
- scrollTarget,
460
- threshold = 0,
461
- rootMargin,
462
- once = false
463
- } = options;
464
- const elementIsVisible = shallowRef(false);
465
- const { stop } = useIntersectionObserver(
466
- element,
467
- (intersectionObserverEntries) => {
468
- let isIntersecting = elementIsVisible.value;
469
- let latestTime = 0;
470
- for (const entry of intersectionObserverEntries) {
471
- if (entry.time >= latestTime) {
472
- latestTime = entry.time;
473
- isIntersecting = entry.isIntersecting;
474
- }
475
- }
476
- elementIsVisible.value = isIntersecting;
477
- if (once) {
478
- watchOnce(elementIsVisible, () => {
479
- stop();
480
- });
481
- }
482
- },
483
- {
484
- root: scrollTarget,
485
- window,
486
- threshold,
487
- rootMargin: toValue(rootMargin)
488
- }
489
- );
490
- return elementIsVisible;
491
- }
492
-
493
- const affixProps = defineHookProps(
494
- {
495
- fixedClass: {
496
- type: String,
497
- default: ""
498
- },
499
- /**
500
- * @zh 距离窗口顶部达到指定偏移量后触发
501
- * @en Triggered when the specified offset is reached from the top of the window
502
- */
503
- offset: {
504
- type: Number,
505
- default: 0
506
- },
507
- /**
508
- * @zh 固定的相对方向
509
- */
510
- position: {
511
- type: String,
512
- default: "top"
513
- },
514
- /**
515
- * @zh 滚动容器,默认是 `window`
516
- * @en Scroll container, default is `window`
517
- */
518
- target: {
519
- type: [String, Object, Function]
520
- },
521
- /**
522
- * @zh z-index 值
523
- * @en Z index value
524
- */
525
- zIndex: {
526
- type: Number,
527
- default: 998
528
- }
529
- }
530
- );
531
- defineHookEmits(["scroll", "change"]);
532
- const AFFIX_TARGET_KEY = Symbol("AFFIX_TARGET_KEY");
533
- function getTargetRect(target) {
534
- return isWindow(target) ? {
535
- top: 0,
536
- bottom: window.innerHeight
537
- } : target.getBoundingClientRect();
538
- }
539
- defineHookComponent({
540
- props: affixProps,
541
- setup(props, { emit }) {
542
- const wrapperRef = elementRef();
543
- const wrapperRect = toReactive(useElementBounding(wrapperRef));
544
- const parentRef = inject(AFFIX_TARGET_KEY, void 0);
545
- const targetRef = useElement(props.target, parentRef);
546
- const isFixed = ref(false);
547
- const placeholderStyle = ref({});
548
- const fixedStyle = ref({});
549
- const className = computed(() => {
550
- return isFixed.value ? props.fixedClass : "";
551
- });
552
- const wrapperVisible = useElementVisibility(wrapperRef);
553
- const containerRef = computed(() => {
554
- if (!wrapperVisible.value) {
555
- return null;
556
- }
557
- return targetRef.value ?? window;
558
- });
559
- const updatePosition = throttleByRaf(async () => {
560
- if (!wrapperRef.value || !containerRef.value) {
561
- return;
562
- }
563
- const area = wrapperRect.width * wrapperRect.height;
564
- if (area === 0) {
565
- return;
566
- }
567
- const newPlaceholderStyles = {
568
- width: px(wrapperRect.width),
569
- height: px(wrapperRect.height)
570
- };
571
- const targetRect = getTargetRect(containerRef.value);
572
- let newIsFixed = false;
573
- let newFixedStyles = {};
574
- const offset = props.offset;
575
- if (props.position === "top") {
576
- newIsFixed = wrapperRect.top - targetRect.top < offset && offset >= 0;
577
- newFixedStyles = newIsFixed ? {
578
- position: "fixed",
579
- zIndex: props.zIndex,
580
- top: px(targetRect.top + offset)
581
- } : {};
582
- } else {
583
- newIsFixed = targetRect.bottom - wrapperRect.bottom < offset;
584
- newFixedStyles = newIsFixed ? {
585
- position: "fixed",
586
- bottom: px(window.innerHeight - targetRect.bottom + offset)
587
- } : {};
588
- }
589
- if (newIsFixed !== isFixed.value) {
590
- isFixed.value = newIsFixed;
591
- emit("change", newIsFixed);
592
- }
593
- placeholderStyle.value = newPlaceholderStyles;
594
- fixedStyle.value = {
595
- ...newFixedStyles,
596
- ...newIsFixed ? newPlaceholderStyles : {}
597
- };
598
- });
599
- useEventListener(containerRef, "scroll", () => {
600
- emit("scroll");
601
- updatePosition();
602
- });
603
- useEventListener(containerRef, "resize", updatePosition);
604
- watchPostEffect(updatePosition);
605
- return {
606
- className,
607
- wrapperRef,
608
- containerRef,
609
- isFixed,
610
- placeholderStyle,
611
- fixedStyle,
612
- updatePosition
613
- };
614
- }
615
- });
616
- function provideAffixTarget(target) {
617
- provide(AFFIX_TARGET_KEY, target);
618
- }
619
-
620
- const HiAffixTarget = defineComponent({
621
- name: "HiAffixTarget",
622
- setup(_, context) {
623
- const targetRef = elementRef();
624
- provideAffixTarget(targetRef);
625
- return () => h("div", {
626
- ref: targetRef,
627
- ...context.attrs
628
- }, renderSlot(context.slots, "default"));
629
- }
630
- });
631
-
632
- const HiAffix = defineComponent({
633
- name: "HiAffix",
634
- props: {
635
- ...affixProps$1,
636
- as: {
637
- type: String,
638
- default: "div"
639
- }
640
- },
641
- setup(props, context) {
642
- const { className, wrapperRef, isFixed, placeholderStyle, fixedStyle } = useAffix(props, context);
643
- return () => h(props.as, { ref: wrapperRef }, [
644
- isFixed.value && h("div", { style: placeholderStyle.value }),
645
- h("div", { class: className.value, style: fixedStyle.value }, renderSlot(context.slots, "default"))
646
- ]);
647
- }
648
- });
649
-
650
- const HiConfigProvider = defineComponent({
651
- props: {
652
- ...configProviderProps,
653
- as: {
654
- type: String
655
- }
656
- },
657
- setup(props, context) {
658
- provideSharedConfig(props);
659
- return () => {
660
- const content = renderSlot(context.slots, "default", void 0);
661
- if (props.as) {
662
- return h(props.as, content);
663
- }
664
- return content;
665
- };
666
- }
667
- });
668
-
669
- const HiFileUpload = defineComponent({
670
- name: "HiFileUpload",
671
- props: {
672
- ...fileUploadProps,
673
- accept: {
674
- type: String,
675
- default: "*/*"
676
- },
677
- as: {
678
- type: String,
679
- default: "div"
680
- }
681
- },
682
- emits: fileUploadEmits,
683
- setup(props, context) {
684
- const { slots } = context;
685
- const { fileInputRef, files, openFileInput } = useFileUpload(props, context);
686
- return () => {
687
- return h(props.as, {
688
- onClick: openFileInput
689
- }, [
690
- h("input", {
691
- ref: fileInputRef,
692
- type: "file",
693
- accept: props.accept,
694
- multiple: props.multiple,
695
- style: { display: "none" }
696
- }),
697
- renderSlot(slots, "default", {
698
- files: files.value
699
- })
700
- ]);
701
- };
702
- }
703
- });
704
-
705
- const HiIcon = defineComponent({
706
- props: {
707
- ...iconProps,
708
- as: {
709
- type: String,
710
- default: "div"
711
- }
712
- },
713
- setup(props, context) {
714
- const { style } = useIcon(props, context);
715
- return () => {
716
- return h(props.as, {
717
- style: style.value
718
- });
719
- };
720
- }
721
- });
722
-
723
- const HiItem = defineComponent({
724
- name: "HiItem",
725
- props: {
726
- ...itemProps$1,
727
- as: {
728
- type: String,
729
- default: "div"
730
- }
731
- },
732
- emits: itemEmits$1,
733
- setup(props, context) {
734
- const { render, activate, className, isDisabled, activateEvent } = useSelectionItem$1(
735
- props,
736
- context
737
- );
738
- return () => h(
739
- props.as,
740
- {
741
- class: className.value,
742
- [`on${capitalize(activateEvent.value)}`]: activate,
743
- disabled: isDisabled.value
744
- },
745
- render()
746
- );
747
- }
748
- });
749
-
750
- const HiPopover = defineComponent({
751
- name: "HiPopover",
752
- props: {
753
- ...popoverProps,
754
- as: {
755
- type: String,
756
- default: "div"
757
- }
758
- },
759
- emits: popoverEmits,
760
- setup(props, context) {
761
- const { triggerRef, popupClass, events, popupRef, popupStyle } = usePopover(props, context);
762
- return () => {
763
- let content = h(
764
- "div",
765
- {
766
- class: popupClass.value,
767
- style: popupStyle.value,
768
- ref: popupRef
769
- },
770
- renderSlot(context.slots, "popup")
771
- );
772
- if (props.teleport) {
773
- content = h(
774
- Teleport,
775
- {
776
- to: props.teleport === true ? "body" : props.teleport
777
- },
778
- content
779
- );
780
- }
781
- return h(props.as, {
782
- ref: triggerRef,
783
- ...events
784
- }, [
785
- renderSlot(context.slots, "default"),
786
- content
787
- ]);
788
- };
789
- }
790
- });
791
-
792
- const HiSelection = defineComponent({
793
- name: "HiSelection",
794
- props: {
795
- ...selectionProps$1,
796
- as: {
797
- type: String,
798
- default: "div"
799
- }
800
- },
801
- emits: selectionEmits$1,
802
- setup(props, context) {
803
- const { render } = useSelectionList$1(props, context);
804
- return () => h(props.as, {}, render());
805
- }
806
- });
807
-
808
- const HiSwitch = defineComponent({
809
- name: "HiSwitch",
810
- props: {
811
- ...switchProps,
812
- as: {
813
- type: String,
814
- default: "div"
815
- }
816
- },
817
- emits: switchEmits,
818
- setup(props, context) {
819
- const { slots } = context;
820
- const { className, toggle, modelValue, isDisabled, activateEvent } = useSwitch(props, context);
821
- return () => {
822
- return h(
823
- props.as,
824
- {
825
- class: className.value,
826
- [`on${capitalize(activateEvent.value)}`]: toggle
827
- },
828
- renderSlot(slots, "default", {
829
- active: modelValue.value,
830
- isDisabled: isDisabled.value
831
- })
832
- );
833
- };
834
- }
835
- });
836
-
837
- const selectionProps = defineHookProps({
838
- modelValue: {
839
- type: valuePropType,
840
- default: () => null
841
- },
842
- /**
843
- * 选中时的 class
844
- */
845
- activeClass: {
846
- type: classPropType,
847
- default: "active"
848
- },
849
- /**
850
- * 每个选项的 class
851
- */
852
- itemClass: {
853
- type: classPropType,
854
- default: ""
855
- },
856
- disabledClass: {
857
- type: classPropType,
858
- default: "disabled"
859
- },
860
- unactiveClass: {
861
- type: classPropType,
862
- default: ""
863
- },
864
- label: {
865
- type: labelPropType
866
- },
867
- /**
868
- * 多选模式
869
- */
870
- multiple: {
871
- type: [Boolean, Number, Array],
872
- default: () => false
873
- },
874
- /**
875
- * 可清除
876
- */
877
- clearable: {
878
- type: Boolean
879
- },
880
- defaultValue: {
881
- type: valuePropType,
882
- default: () => null
883
- },
884
- activateEvent: {
885
- type: String
886
- }
887
- });
888
- const selectionEmits = defineHookEmits([
889
- "update:modelValue",
890
- "change",
891
- "load",
892
- "unload",
893
- "reject"
894
- ]);
895
- const HiSelectionContextSymbol = Symbol("[hi-selection]context");
896
- function useSelectionContext() {
897
- const sharedConfig = useSharedConfig();
898
- return inject(HiSelectionContextSymbol, {
899
- isActive: () => false,
900
- activate: () => {
901
- },
902
- changeActive: () => {
903
- },
904
- reject: () => {
905
- },
906
- activeClass: "active",
907
- unactiveClass: "unactive",
908
- disabledClass: "disabled",
909
- itemClass: "",
910
- activateEvent: sharedConfig.activateEvent,
911
- label: null,
912
- multiple: false,
913
- limit: [0, Number.POSITIVE_INFINITY]
914
- });
915
- }
916
- const useSelectionList = defineHookComponent({
917
- props: selectionProps,
918
- emits: selectionEmits,
919
- setup(props, { emit, slots }) {
920
- const options = reactive([]);
921
- function toArray(value) {
922
- if (!isDefined(value)) {
923
- return [];
924
- }
925
- if (props.multiple && Array.isArray(value)) {
926
- return value.filter((v) => v != null || v !== void 0);
927
- }
928
- return [value];
929
- }
930
- const actives = reactive([
931
- ...toArray(props.modelValue ?? props.defaultValue)
932
- ]);
933
- const currentValue = computed({
934
- get() {
935
- return props.multiple ? actives : actives[0];
936
- },
937
- set(val) {
938
- actives.splice(0, actives.length, ...toArray(val));
939
- }
940
- });
941
- const modelValue = computed({
942
- get() {
943
- return props.modelValue ?? props.defaultValue;
944
- },
945
- set(val) {
946
- emit("update:modelValue", val);
947
- }
948
- });
949
- syncRef(currentValue, modelValue, { immediate: true, deep: true });
950
- const emitChange = () => emit("change", currentValue.value);
951
- function isActive(value) {
952
- return actives.includes(value);
953
- }
954
- const multipleActive = computed(() => !!props.multiple);
955
- const multipleLimit = computed(() => {
956
- if (Array.isArray(props.multiple)) {
957
- if (props.multiple[1] === void 0) {
958
- return [props.multiple[0], Number.POSITIVE_INFINITY];
959
- }
960
- return props.multiple;
961
- }
962
- return [0, Number.POSITIVE_INFINITY];
963
- });
964
- function activate(value) {
965
- const [min, max] = multipleLimit.value;
966
- if (isActive(value)) {
967
- if (multipleActive.value && actives.length > min || props.clearable) {
968
- actives.splice(actives.indexOf(value), 1);
969
- emitChange();
970
- }
971
- } else {
972
- if (props.multiple) {
973
- if (actives.length < max) {
974
- actives.push(value);
975
- emitChange();
976
- }
977
- } else {
978
- actives.splice(0, actives.length, value);
979
- emitChange();
980
- }
981
- }
982
- }
983
- function reject(value) {
984
- emit("reject", value);
985
- }
986
- const init = (option) => {
987
- function remove() {
988
- const index = options.findIndex((e) => e.id === option.id);
989
- if (index > -1) {
990
- options.splice(index, 1);
991
- emit("unload", option);
992
- }
993
- }
994
- for (let i = 0; i < options.length; i++) {
995
- if (options[i].value === option.value) {
996
- options.splice(i, 1);
997
- i--;
998
- }
999
- }
1000
- options.push(option);
1001
- emit("load", option);
1002
- return remove;
1003
- };
1004
- const sharedConfig = useSharedConfig();
1005
- provide(HiSelectionContextSymbol, toReactive({
1006
- activeClass: computed(() => cls(props.activeClass)),
1007
- unactiveClass: computed(() => cls(props.unactiveClass)),
1008
- disabledClass: computed(() => cls(props.disabledClass)),
1009
- itemClass: computed(() => cls(props.itemClass)),
1010
- label: computed(() => props.label),
1011
- multiple: multipleActive,
1012
- limit: multipleLimit,
1013
- clearable: computed(() => props.clearable),
1014
- defaultValue: computed(() => props.defaultValue),
1015
- activateEvent: computed(() => props.activateEvent ?? sharedConfig.activateEvent),
1016
- active: currentValue,
1017
- activate,
1018
- changeActive: activate,
1019
- isActive,
1020
- reject,
1021
- init
1022
- }));
1023
- const renderItem = () => {
1024
- const children = options.filter((e) => actives.includes(e.value)).map((e) => e.render());
1025
- return props.multiple ? children : getFirstChilld(children);
1026
- };
1027
- const slotData = {
1028
- isActive,
1029
- changeActive: activate,
1030
- renderItem
1031
- };
1032
- const render = () => {
1033
- return renderSlot(slots, "default", slotData);
1034
- };
1035
- return {
1036
- options,
1037
- actives,
1038
- isActive,
1039
- changeActive: activate,
1040
- renderItem,
1041
- render
1042
- };
1043
- }
1044
- });
1045
-
1046
- const itemProps = defineHookProps({
1047
- value: {
1048
- type: valuePropType,
1049
- default() {
1050
- return Math.random().toString(16).slice(2);
1051
- }
1052
- },
1053
- label: {
1054
- type: [Function, String]
1055
- },
1056
- keepAlive: {
1057
- type: Boolean,
1058
- default: () => true
1059
- },
1060
- activateEvent: {
1061
- type: String
1062
- },
1063
- disabled: {
1064
- type: Boolean,
1065
- default: false
1066
- }
1067
- });
1068
- const itemEmits = defineHookEmits(["reject"]);
1069
- const useSelectionItem = defineHookComponent({
1070
- props: itemProps,
1071
- emits: itemEmits,
1072
- setup(props, { slots, emit }) {
1073
- const context = useSelectionContext();
1074
- const activate = () => {
1075
- if (props.disabled) {
1076
- emit("reject", props.value);
1077
- context.reject(props.value);
1078
- return;
1079
- }
1080
- context.activate(props.value);
1081
- };
1082
- const label = computed(() => {
1083
- let label2 = props.label ?? context.label;
1084
- if (label2 && typeof label2 === "function") {
1085
- label2 = label2(props.value);
1086
- }
1087
- return Array.isArray(label2) ? label2 : [label2];
1088
- });
1089
- function render() {
1090
- return slots.default?.({
1091
- active: context.isActive(props.value),
1092
- activate
1093
- }) ?? label.value.filter(Boolean);
1094
- }
1095
- let remove = () => {
1096
- };
1097
- const init = context.init;
1098
- if (init) {
1099
- watch(
1100
- () => props.value,
1101
- (value) => {
1102
- remove();
1103
- remove = init({
1104
- id: Math.random().toString(16).slice(2),
1105
- label: typeof props.label === "string" ? props.label : void 0,
1106
- value,
1107
- render
1108
- });
1109
- },
1110
- { immediate: true }
1111
- );
1112
- tryOnScopeDispose(remove);
1113
- }
1114
- const isActive = computed(() => context.isActive(props.value));
1115
- const isDisabled = computed(() => props.disabled);
1116
- const className = computed(() => {
1117
- const array = [context.itemClass];
1118
- if (!isDisabled.value) {
1119
- array.push(context.isActive(props.value) ? context.activeClass : context.unactiveClass);
1120
- } else {
1121
- array.push(context.disabledClass);
1122
- }
1123
- return cls(array);
1124
- });
1125
- const activateEvent = computed(() => props.activateEvent ?? context.activateEvent);
1126
- return {
1127
- activate,
1128
- render,
1129
- isActive,
1130
- isDisabled,
1131
- className,
1132
- activateEvent,
1133
- label
1134
- };
1135
- }
1136
- });
1137
-
1138
- const HiTabPane = defineComponent({
1139
- name: "HiTabPane",
1140
- props: {
1141
- ...itemProps
1142
- },
1143
- emits: itemEmits,
1144
- inheritAttrs: true,
1145
- setup(props, context) {
1146
- const { className, activateEvent, activate, isDisabled, label } = useSelectionItem(props, context);
1147
- return () => {
1148
- return h("div", {
1149
- class: className.value,
1150
- [`on${capitalize(activateEvent.value)}`]: activate,
1151
- disabled: isDisabled.value
1152
- }, label.value);
1153
- };
1154
- }
1155
- });
1156
-
1157
- const HiTabs = defineComponent({
1158
- name: "HiTabs",
1159
- props: {
1160
- ...selectionProps,
1161
- headerClass: {
1162
- type: classPropType
1163
- },
1164
- as: {
1165
- type: String,
1166
- default: "div"
1167
- },
1168
- headerAs: {
1169
- type: String,
1170
- default: "div"
1171
- },
1172
- contentAs: {
1173
- type: String,
1174
- default: "div"
1175
- },
1176
- contentClass: {
1177
- type: classPropType
1178
- },
1179
- keepAlive: {
1180
- type: [Boolean, Object],
1181
- default: false
1182
- }
1183
- },
1184
- inheritAttrs: true,
1185
- setup(props, context) {
1186
- const selection = useSelectionList(props, context);
1187
- return () => {
1188
- let component = selection.renderItem();
1189
- if (props.keepAlive) {
1190
- component = h(KeepAlive, {
1191
- ...typeof props.keepAlive === "object" ? props.keepAlive : {}
1192
- }, component);
1193
- }
1194
- if (context.slots.content) {
1195
- component = context.slots.content({
1196
- component
1197
- });
1198
- } else {
1199
- component = h(props.contentAs, {
1200
- class: props.contentClass
1201
- }, component);
1202
- }
1203
- return h(props.as, [
1204
- h(props.headerAs, {
1205
- class: props.headerClass
1206
- }, renderSlot(context.slots, "default")),
1207
- component
1208
- ]);
1209
- };
1210
- }
1211
- });
1212
-
1213
- function memo(getDeps, fn, opts) {
1214
- let deps = opts.initialDeps ?? [];
1215
- let result;
1216
- function memoizedFunction() {
1217
- var _a, _b, _c, _d;
1218
- let depTime;
1219
- if (opts.key && ((_a = opts.debug) == null ? void 0 : _a.call(opts))) depTime = Date.now();
1220
- const newDeps = getDeps();
1221
- const depsChanged = newDeps.length !== deps.length || newDeps.some((dep, index) => deps[index] !== dep);
1222
- if (!depsChanged) {
1223
- return result;
1224
- }
1225
- deps = newDeps;
1226
- let resultTime;
1227
- if (opts.key && ((_b = opts.debug) == null ? void 0 : _b.call(opts))) resultTime = Date.now();
1228
- result = fn(...newDeps);
1229
- if (opts.key && ((_c = opts.debug) == null ? void 0 : _c.call(opts))) {
1230
- const depEndTime = Math.round((Date.now() - depTime) * 100) / 100;
1231
- const resultEndTime = Math.round((Date.now() - resultTime) * 100) / 100;
1232
- const resultFpsPercentage = resultEndTime / 16;
1233
- const pad = (str, num) => {
1234
- str = String(str);
1235
- while (str.length < num) {
1236
- str = " " + str;
1237
- }
1238
- return str;
1239
- };
1240
- console.info(
1241
- `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,
1242
- `
1243
- font-size: .6rem;
1244
- font-weight: bold;
1245
- color: hsl(${Math.max(
1246
- 0,
1247
- Math.min(120 - 120 * resultFpsPercentage, 120)
1248
- )}deg 100% 31%);`,
1249
- opts == null ? void 0 : opts.key
1250
- );
1251
- }
1252
- (_d = opts == null ? void 0 : opts.onChange) == null ? void 0 : _d.call(opts, result);
1253
- return result;
1254
- }
1255
- memoizedFunction.updateDeps = (newDeps) => {
1256
- deps = newDeps;
1257
- };
1258
- return memoizedFunction;
1259
- }
1260
- function notUndefined(value, msg) {
1261
- if (value === void 0) {
1262
- throw new Error(`Unexpected undefined${""}`);
1263
- } else {
1264
- return value;
1265
- }
1266
- }
1267
- const approxEqual = (a, b) => Math.abs(a - b) <= 1;
1268
- const debounce = (targetWindow, fn, ms) => {
1269
- let timeoutId;
1270
- return function(...args) {
1271
- targetWindow.clearTimeout(timeoutId);
1272
- timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms);
1273
- };
1274
- };
1275
-
1276
- const getRect = (element) => {
1277
- const { offsetWidth, offsetHeight } = element;
1278
- return { width: offsetWidth, height: offsetHeight };
1279
- };
1280
- const defaultKeyExtractor = (index) => index;
1281
- const defaultRangeExtractor = (range) => {
1282
- const start = Math.max(range.startIndex - range.overscan, 0);
1283
- const end = Math.min(range.endIndex + range.overscan, range.count - 1);
1284
- const arr = [];
1285
- for (let i = start; i <= end; i++) {
1286
- arr.push(i);
1287
- }
1288
- return arr;
1289
- };
1290
- const observeElementRect = (instance, cb) => {
1291
- const element = instance.scrollElement;
1292
- if (!element) {
1293
- return;
1294
- }
1295
- const targetWindow = instance.targetWindow;
1296
- if (!targetWindow) {
1297
- return;
1298
- }
1299
- const handler = (rect) => {
1300
- const { width, height } = rect;
1301
- cb({ width: Math.round(width), height: Math.round(height) });
1302
- };
1303
- handler(getRect(element));
1304
- if (!targetWindow.ResizeObserver) {
1305
- return () => {
1306
- };
1307
- }
1308
- const observer = new targetWindow.ResizeObserver((entries) => {
1309
- const run = () => {
1310
- const entry = entries[0];
1311
- if (entry == null ? void 0 : entry.borderBoxSize) {
1312
- const box = entry.borderBoxSize[0];
1313
- if (box) {
1314
- handler({ width: box.inlineSize, height: box.blockSize });
1315
- return;
1316
- }
1317
- }
1318
- handler(getRect(element));
1319
- };
1320
- instance.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
1321
- });
1322
- observer.observe(element, { box: "border-box" });
1323
- return () => {
1324
- observer.unobserve(element);
1325
- };
1326
- };
1327
- const addEventListenerOptions = {
1328
- passive: true
1329
- };
1330
- const supportsScrollend = typeof window == "undefined" ? true : "onscrollend" in window;
1331
- const observeElementOffset = (instance, cb) => {
1332
- const element = instance.scrollElement;
1333
- if (!element) {
1334
- return;
1335
- }
1336
- const targetWindow = instance.targetWindow;
1337
- if (!targetWindow) {
1338
- return;
1339
- }
1340
- let offset = 0;
1341
- const fallback = instance.options.useScrollendEvent && supportsScrollend ? () => void 0 : debounce(
1342
- targetWindow,
1343
- () => {
1344
- cb(offset, false);
1345
- },
1346
- instance.options.isScrollingResetDelay
1347
- );
1348
- const createHandler = (isScrolling) => () => {
1349
- const { horizontal, isRtl } = instance.options;
1350
- offset = horizontal ? element["scrollLeft"] * (isRtl && -1 || 1) : element["scrollTop"];
1351
- fallback();
1352
- cb(offset, isScrolling);
1353
- };
1354
- const handler = createHandler(true);
1355
- const endHandler = createHandler(false);
1356
- endHandler();
1357
- element.addEventListener("scroll", handler, addEventListenerOptions);
1358
- const registerScrollendEvent = instance.options.useScrollendEvent && supportsScrollend;
1359
- if (registerScrollendEvent) {
1360
- element.addEventListener("scrollend", endHandler, addEventListenerOptions);
1361
- }
1362
- return () => {
1363
- element.removeEventListener("scroll", handler);
1364
- if (registerScrollendEvent) {
1365
- element.removeEventListener("scrollend", endHandler);
1366
- }
1367
- };
1368
- };
1369
- const measureElement = (element, entry, instance) => {
1370
- if (entry == null ? void 0 : entry.borderBoxSize) {
1371
- const box = entry.borderBoxSize[0];
1372
- if (box) {
1373
- const size = Math.round(
1374
- box[instance.options.horizontal ? "inlineSize" : "blockSize"]
1375
- );
1376
- return size;
1377
- }
1378
- }
1379
- return element[instance.options.horizontal ? "offsetWidth" : "offsetHeight"];
1380
- };
1381
- const elementScroll = (offset, {
1382
- adjustments = 0,
1383
- behavior
1384
- }, instance) => {
1385
- var _a, _b;
1386
- const toOffset = offset + adjustments;
1387
- (_b = (_a = instance.scrollElement) == null ? void 0 : _a.scrollTo) == null ? void 0 : _b.call(_a, {
1388
- [instance.options.horizontal ? "left" : "top"]: toOffset,
1389
- behavior
1390
- });
1391
- };
1392
- class Virtualizer {
1393
- constructor(opts) {
1394
- this.unsubs = [];
1395
- this.scrollElement = null;
1396
- this.targetWindow = null;
1397
- this.isScrolling = false;
1398
- this.scrollToIndexTimeoutId = null;
1399
- this.measurementsCache = [];
1400
- this.itemSizeCache = /* @__PURE__ */ new Map();
1401
- this.pendingMeasuredCacheIndexes = [];
1402
- this.scrollRect = null;
1403
- this.scrollOffset = null;
1404
- this.scrollDirection = null;
1405
- this.scrollAdjustments = 0;
1406
- this.elementsCache = /* @__PURE__ */ new Map();
1407
- this.observer = /* @__PURE__ */ (() => {
1408
- let _ro = null;
1409
- const get = () => {
1410
- if (_ro) {
1411
- return _ro;
1412
- }
1413
- if (!this.targetWindow || !this.targetWindow.ResizeObserver) {
1414
- return null;
1415
- }
1416
- return _ro = new this.targetWindow.ResizeObserver((entries) => {
1417
- entries.forEach((entry) => {
1418
- const run = () => {
1419
- this._measureElement(entry.target, entry);
1420
- };
1421
- this.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
1422
- });
1423
- });
1424
- };
1425
- return {
1426
- disconnect: () => {
1427
- var _a;
1428
- (_a = get()) == null ? void 0 : _a.disconnect();
1429
- _ro = null;
1430
- },
1431
- observe: (target) => {
1432
- var _a;
1433
- return (_a = get()) == null ? void 0 : _a.observe(target, { box: "border-box" });
1434
- },
1435
- unobserve: (target) => {
1436
- var _a;
1437
- return (_a = get()) == null ? void 0 : _a.unobserve(target);
1438
- }
1439
- };
1440
- })();
1441
- this.range = null;
1442
- this.setOptions = (opts2) => {
1443
- Object.entries(opts2).forEach(([key, value]) => {
1444
- if (typeof value === "undefined") delete opts2[key];
1445
- });
1446
- this.options = {
1447
- debug: false,
1448
- initialOffset: 0,
1449
- overscan: 1,
1450
- paddingStart: 0,
1451
- paddingEnd: 0,
1452
- scrollPaddingStart: 0,
1453
- scrollPaddingEnd: 0,
1454
- horizontal: false,
1455
- getItemKey: defaultKeyExtractor,
1456
- rangeExtractor: defaultRangeExtractor,
1457
- onChange: () => {
1458
- },
1459
- measureElement,
1460
- initialRect: { width: 0, height: 0 },
1461
- scrollMargin: 0,
1462
- gap: 0,
1463
- indexAttribute: "data-index",
1464
- initialMeasurementsCache: [],
1465
- lanes: 1,
1466
- isScrollingResetDelay: 150,
1467
- enabled: true,
1468
- isRtl: false,
1469
- useScrollendEvent: false,
1470
- useAnimationFrameWithResizeObserver: false,
1471
- ...opts2
1472
- };
1473
- };
1474
- this.notify = (sync) => {
1475
- var _a, _b;
1476
- (_b = (_a = this.options).onChange) == null ? void 0 : _b.call(_a, this, sync);
1477
- };
1478
- this.maybeNotify = memo(
1479
- () => {
1480
- this.calculateRange();
1481
- return [
1482
- this.isScrolling,
1483
- this.range ? this.range.startIndex : null,
1484
- this.range ? this.range.endIndex : null
1485
- ];
1486
- },
1487
- (isScrolling) => {
1488
- this.notify(isScrolling);
1489
- },
1490
- {
1491
- key: process.env.NODE_ENV !== "production" && "maybeNotify",
1492
- debug: () => this.options.debug,
1493
- initialDeps: [
1494
- this.isScrolling,
1495
- this.range ? this.range.startIndex : null,
1496
- this.range ? this.range.endIndex : null
1497
- ]
1498
- }
1499
- );
1500
- this.cleanup = () => {
1501
- this.unsubs.filter(Boolean).forEach((d) => d());
1502
- this.unsubs = [];
1503
- this.observer.disconnect();
1504
- this.scrollElement = null;
1505
- this.targetWindow = null;
1506
- };
1507
- this._didMount = () => {
1508
- return () => {
1509
- this.cleanup();
1510
- };
1511
- };
1512
- this._willUpdate = () => {
1513
- var _a;
1514
- const scrollElement = this.options.enabled ? this.options.getScrollElement() : null;
1515
- if (this.scrollElement !== scrollElement) {
1516
- this.cleanup();
1517
- if (!scrollElement) {
1518
- this.maybeNotify();
1519
- return;
1520
- }
1521
- this.scrollElement = scrollElement;
1522
- if (this.scrollElement && "ownerDocument" in this.scrollElement) {
1523
- this.targetWindow = this.scrollElement.ownerDocument.defaultView;
1524
- } else {
1525
- this.targetWindow = ((_a = this.scrollElement) == null ? void 0 : _a.window) ?? null;
1526
- }
1527
- this.elementsCache.forEach((cached) => {
1528
- this.observer.observe(cached);
1529
- });
1530
- this._scrollToOffset(this.getScrollOffset(), {
1531
- adjustments: void 0,
1532
- behavior: void 0
1533
- });
1534
- this.unsubs.push(
1535
- this.options.observeElementRect(this, (rect) => {
1536
- this.scrollRect = rect;
1537
- this.maybeNotify();
1538
- })
1539
- );
1540
- this.unsubs.push(
1541
- this.options.observeElementOffset(this, (offset, isScrolling) => {
1542
- this.scrollAdjustments = 0;
1543
- this.scrollDirection = isScrolling ? this.getScrollOffset() < offset ? "forward" : "backward" : null;
1544
- this.scrollOffset = offset;
1545
- this.isScrolling = isScrolling;
1546
- this.maybeNotify();
1547
- })
1548
- );
1549
- }
1550
- };
1551
- this.getSize = () => {
1552
- if (!this.options.enabled) {
1553
- this.scrollRect = null;
1554
- return 0;
1555
- }
1556
- this.scrollRect = this.scrollRect ?? this.options.initialRect;
1557
- return this.scrollRect[this.options.horizontal ? "width" : "height"];
1558
- };
1559
- this.getScrollOffset = () => {
1560
- if (!this.options.enabled) {
1561
- this.scrollOffset = null;
1562
- return 0;
1563
- }
1564
- this.scrollOffset = this.scrollOffset ?? (typeof this.options.initialOffset === "function" ? this.options.initialOffset() : this.options.initialOffset);
1565
- return this.scrollOffset;
1566
- };
1567
- this.getFurthestMeasurement = (measurements, index) => {
1568
- const furthestMeasurementsFound = /* @__PURE__ */ new Map();
1569
- const furthestMeasurements = /* @__PURE__ */ new Map();
1570
- for (let m = index - 1; m >= 0; m--) {
1571
- const measurement = measurements[m];
1572
- if (furthestMeasurementsFound.has(measurement.lane)) {
1573
- continue;
1574
- }
1575
- const previousFurthestMeasurement = furthestMeasurements.get(
1576
- measurement.lane
1577
- );
1578
- if (previousFurthestMeasurement == null || measurement.end > previousFurthestMeasurement.end) {
1579
- furthestMeasurements.set(measurement.lane, measurement);
1580
- } else if (measurement.end < previousFurthestMeasurement.end) {
1581
- furthestMeasurementsFound.set(measurement.lane, true);
1582
- }
1583
- if (furthestMeasurementsFound.size === this.options.lanes) {
1584
- break;
1585
- }
1586
- }
1587
- return furthestMeasurements.size === this.options.lanes ? Array.from(furthestMeasurements.values()).sort((a, b) => {
1588
- if (a.end === b.end) {
1589
- return a.index - b.index;
1590
- }
1591
- return a.end - b.end;
1592
- })[0] : void 0;
1593
- };
1594
- this.getMeasurementOptions = memo(
1595
- () => [
1596
- this.options.count,
1597
- this.options.paddingStart,
1598
- this.options.scrollMargin,
1599
- this.options.getItemKey,
1600
- this.options.enabled
1601
- ],
1602
- (count, paddingStart, scrollMargin, getItemKey, enabled) => {
1603
- this.pendingMeasuredCacheIndexes = [];
1604
- return {
1605
- count,
1606
- paddingStart,
1607
- scrollMargin,
1608
- getItemKey,
1609
- enabled
1610
- };
1611
- },
1612
- {
1613
- key: false
1614
- }
1615
- );
1616
- this.getMeasurements = memo(
1617
- () => [this.getMeasurementOptions(), this.itemSizeCache],
1618
- ({ count, paddingStart, scrollMargin, getItemKey, enabled }, itemSizeCache) => {
1619
- if (!enabled) {
1620
- this.measurementsCache = [];
1621
- this.itemSizeCache.clear();
1622
- return [];
1623
- }
1624
- if (this.measurementsCache.length === 0) {
1625
- this.measurementsCache = this.options.initialMeasurementsCache;
1626
- this.measurementsCache.forEach((item) => {
1627
- this.itemSizeCache.set(item.key, item.size);
1628
- });
1629
- }
1630
- const min = this.pendingMeasuredCacheIndexes.length > 0 ? Math.min(...this.pendingMeasuredCacheIndexes) : 0;
1631
- this.pendingMeasuredCacheIndexes = [];
1632
- const measurements = this.measurementsCache.slice(0, min);
1633
- for (let i = min; i < count; i++) {
1634
- const key = getItemKey(i);
1635
- const furthestMeasurement = this.options.lanes === 1 ? measurements[i - 1] : this.getFurthestMeasurement(measurements, i);
1636
- const start = furthestMeasurement ? furthestMeasurement.end + this.options.gap : paddingStart + scrollMargin;
1637
- const measuredSize = itemSizeCache.get(key);
1638
- const size = typeof measuredSize === "number" ? measuredSize : this.options.estimateSize(i);
1639
- const end = start + size;
1640
- const lane = furthestMeasurement ? furthestMeasurement.lane : i % this.options.lanes;
1641
- measurements[i] = {
1642
- index: i,
1643
- start,
1644
- size,
1645
- end,
1646
- key,
1647
- lane
1648
- };
1649
- }
1650
- this.measurementsCache = measurements;
1651
- return measurements;
1652
- },
1653
- {
1654
- key: process.env.NODE_ENV !== "production" && "getMeasurements",
1655
- debug: () => this.options.debug
1656
- }
1657
- );
1658
- this.calculateRange = memo(
1659
- () => [
1660
- this.getMeasurements(),
1661
- this.getSize(),
1662
- this.getScrollOffset(),
1663
- this.options.lanes
1664
- ],
1665
- (measurements, outerSize, scrollOffset, lanes) => {
1666
- return this.range = measurements.length > 0 && outerSize > 0 ? calculateRange({
1667
- measurements,
1668
- outerSize,
1669
- scrollOffset,
1670
- lanes
1671
- }) : null;
1672
- },
1673
- {
1674
- key: process.env.NODE_ENV !== "production" && "calculateRange",
1675
- debug: () => this.options.debug
1676
- }
1677
- );
1678
- this.getVirtualIndexes = memo(
1679
- () => {
1680
- let startIndex = null;
1681
- let endIndex = null;
1682
- const range = this.calculateRange();
1683
- if (range) {
1684
- startIndex = range.startIndex;
1685
- endIndex = range.endIndex;
1686
- }
1687
- this.maybeNotify.updateDeps([this.isScrolling, startIndex, endIndex]);
1688
- return [
1689
- this.options.rangeExtractor,
1690
- this.options.overscan,
1691
- this.options.count,
1692
- startIndex,
1693
- endIndex
1694
- ];
1695
- },
1696
- (rangeExtractor, overscan, count, startIndex, endIndex) => {
1697
- return startIndex === null || endIndex === null ? [] : rangeExtractor({
1698
- startIndex,
1699
- endIndex,
1700
- overscan,
1701
- count
1702
- });
1703
- },
1704
- {
1705
- key: process.env.NODE_ENV !== "production" && "getVirtualIndexes",
1706
- debug: () => this.options.debug
1707
- }
1708
- );
1709
- this.indexFromElement = (node) => {
1710
- const attributeName = this.options.indexAttribute;
1711
- const indexStr = node.getAttribute(attributeName);
1712
- if (!indexStr) {
1713
- console.warn(
1714
- `Missing attribute name '${attributeName}={index}' on measured element.`
1715
- );
1716
- return -1;
1717
- }
1718
- return parseInt(indexStr, 10);
1719
- };
1720
- this._measureElement = (node, entry) => {
1721
- const index = this.indexFromElement(node);
1722
- const item = this.measurementsCache[index];
1723
- if (!item) {
1724
- return;
1725
- }
1726
- const key = item.key;
1727
- const prevNode = this.elementsCache.get(key);
1728
- if (prevNode !== node) {
1729
- if (prevNode) {
1730
- this.observer.unobserve(prevNode);
1731
- }
1732
- this.observer.observe(node);
1733
- this.elementsCache.set(key, node);
1734
- }
1735
- if (node.isConnected) {
1736
- this.resizeItem(index, this.options.measureElement(node, entry, this));
1737
- }
1738
- };
1739
- this.resizeItem = (index, size) => {
1740
- const item = this.measurementsCache[index];
1741
- if (!item) {
1742
- return;
1743
- }
1744
- const itemSize = this.itemSizeCache.get(item.key) ?? item.size;
1745
- const delta = size - itemSize;
1746
- if (delta !== 0) {
1747
- if (this.shouldAdjustScrollPositionOnItemSizeChange !== void 0 ? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this) : item.start < this.getScrollOffset() + this.scrollAdjustments) {
1748
- if (process.env.NODE_ENV !== "production" && this.options.debug) {
1749
- console.info("correction", delta);
1750
- }
1751
- this._scrollToOffset(this.getScrollOffset(), {
1752
- adjustments: this.scrollAdjustments += delta,
1753
- behavior: void 0
1754
- });
1755
- }
1756
- this.pendingMeasuredCacheIndexes.push(item.index);
1757
- this.itemSizeCache = new Map(this.itemSizeCache.set(item.key, size));
1758
- this.notify(false);
1759
- }
1760
- };
1761
- this.measureElement = (node) => {
1762
- if (!node) {
1763
- this.elementsCache.forEach((cached, key) => {
1764
- if (!cached.isConnected) {
1765
- this.observer.unobserve(cached);
1766
- this.elementsCache.delete(key);
1767
- }
1768
- });
1769
- return;
1770
- }
1771
- this._measureElement(node, void 0);
1772
- };
1773
- this.getVirtualItems = memo(
1774
- () => [this.getVirtualIndexes(), this.getMeasurements()],
1775
- (indexes, measurements) => {
1776
- const virtualItems = [];
1777
- for (let k = 0, len = indexes.length; k < len; k++) {
1778
- const i = indexes[k];
1779
- const measurement = measurements[i];
1780
- virtualItems.push(measurement);
1781
- }
1782
- return virtualItems;
1783
- },
1784
- {
1785
- key: process.env.NODE_ENV !== "production" && "getVirtualItems",
1786
- debug: () => this.options.debug
1787
- }
1788
- );
1789
- this.getVirtualItemForOffset = (offset) => {
1790
- const measurements = this.getMeasurements();
1791
- if (measurements.length === 0) {
1792
- return void 0;
1793
- }
1794
- return notUndefined(
1795
- measurements[findNearestBinarySearch(
1796
- 0,
1797
- measurements.length - 1,
1798
- (index) => notUndefined(measurements[index]).start,
1799
- offset
1800
- )]
1801
- );
1802
- };
1803
- this.getOffsetForAlignment = (toOffset, align, itemSize = 0) => {
1804
- const size = this.getSize();
1805
- const scrollOffset = this.getScrollOffset();
1806
- if (align === "auto") {
1807
- align = toOffset >= scrollOffset + size ? "end" : "start";
1808
- }
1809
- if (align === "center") {
1810
- toOffset += (itemSize - size) / 2;
1811
- } else if (align === "end") {
1812
- toOffset -= size;
1813
- }
1814
- const maxOffset = this.getTotalSize() - size;
1815
- return Math.max(Math.min(maxOffset, toOffset), 0);
1816
- };
1817
- this.getOffsetForIndex = (index, align = "auto") => {
1818
- index = Math.max(0, Math.min(index, this.options.count - 1));
1819
- const item = this.measurementsCache[index];
1820
- if (!item) {
1821
- return void 0;
1822
- }
1823
- const size = this.getSize();
1824
- const scrollOffset = this.getScrollOffset();
1825
- if (align === "auto") {
1826
- if (item.end >= scrollOffset + size - this.options.scrollPaddingEnd) {
1827
- align = "end";
1828
- } else if (item.start <= scrollOffset + this.options.scrollPaddingStart) {
1829
- align = "start";
1830
- } else {
1831
- return [scrollOffset, align];
1832
- }
1833
- }
1834
- const toOffset = align === "end" ? item.end + this.options.scrollPaddingEnd : item.start - this.options.scrollPaddingStart;
1835
- return [
1836
- this.getOffsetForAlignment(toOffset, align, item.size),
1837
- align
1838
- ];
1839
- };
1840
- this.isDynamicMode = () => this.elementsCache.size > 0;
1841
- this.cancelScrollToIndex = () => {
1842
- if (this.scrollToIndexTimeoutId !== null && this.targetWindow) {
1843
- this.targetWindow.clearTimeout(this.scrollToIndexTimeoutId);
1844
- this.scrollToIndexTimeoutId = null;
1845
- }
1846
- };
1847
- this.scrollToOffset = (toOffset, { align = "start", behavior } = {}) => {
1848
- this.cancelScrollToIndex();
1849
- if (behavior === "smooth" && this.isDynamicMode()) {
1850
- console.warn(
1851
- "The `smooth` scroll behavior is not fully supported with dynamic size."
1852
- );
1853
- }
1854
- this._scrollToOffset(this.getOffsetForAlignment(toOffset, align), {
1855
- adjustments: void 0,
1856
- behavior
1857
- });
1858
- };
1859
- this.scrollToIndex = (index, { align: initialAlign = "auto", behavior } = {}) => {
1860
- index = Math.max(0, Math.min(index, this.options.count - 1));
1861
- this.cancelScrollToIndex();
1862
- if (behavior === "smooth" && this.isDynamicMode()) {
1863
- console.warn(
1864
- "The `smooth` scroll behavior is not fully supported with dynamic size."
1865
- );
1866
- }
1867
- const offsetAndAlign = this.getOffsetForIndex(index, initialAlign);
1868
- if (!offsetAndAlign) return;
1869
- const [offset, align] = offsetAndAlign;
1870
- this._scrollToOffset(offset, { adjustments: void 0, behavior });
1871
- if (behavior !== "smooth" && this.isDynamicMode() && this.targetWindow) {
1872
- this.scrollToIndexTimeoutId = this.targetWindow.setTimeout(() => {
1873
- this.scrollToIndexTimeoutId = null;
1874
- const elementInDOM = this.elementsCache.has(
1875
- this.options.getItemKey(index)
1876
- );
1877
- if (elementInDOM) {
1878
- const [latestOffset] = notUndefined(
1879
- this.getOffsetForIndex(index, align)
1880
- );
1881
- const currentScrollOffset = this.getScrollOffset();
1882
- if (!approxEqual(latestOffset, currentScrollOffset)) {
1883
- this.scrollToIndex(index, { align, behavior });
1884
- }
1885
- } else {
1886
- this.scrollToIndex(index, { align, behavior });
1887
- }
1888
- });
1889
- }
1890
- };
1891
- this.scrollBy = (delta, { behavior } = {}) => {
1892
- this.cancelScrollToIndex();
1893
- if (behavior === "smooth" && this.isDynamicMode()) {
1894
- console.warn(
1895
- "The `smooth` scroll behavior is not fully supported with dynamic size."
1896
- );
1897
- }
1898
- this._scrollToOffset(this.getScrollOffset() + delta, {
1899
- adjustments: void 0,
1900
- behavior
1901
- });
1902
- };
1903
- this.getTotalSize = () => {
1904
- var _a;
1905
- const measurements = this.getMeasurements();
1906
- let end;
1907
- if (measurements.length === 0) {
1908
- end = this.options.paddingStart;
1909
- } else if (this.options.lanes === 1) {
1910
- end = ((_a = measurements[measurements.length - 1]) == null ? void 0 : _a.end) ?? 0;
1911
- } else {
1912
- const endByLane = Array(this.options.lanes).fill(null);
1913
- let endIndex = measurements.length - 1;
1914
- while (endIndex >= 0 && endByLane.some((val) => val === null)) {
1915
- const item = measurements[endIndex];
1916
- if (endByLane[item.lane] === null) {
1917
- endByLane[item.lane] = item.end;
1918
- }
1919
- endIndex--;
1920
- }
1921
- end = Math.max(...endByLane.filter((val) => val !== null));
1922
- }
1923
- return Math.max(
1924
- end - this.options.scrollMargin + this.options.paddingEnd,
1925
- 0
1926
- );
1927
- };
1928
- this._scrollToOffset = (offset, {
1929
- adjustments,
1930
- behavior
1931
- }) => {
1932
- this.options.scrollToFn(offset, { behavior, adjustments }, this);
1933
- };
1934
- this.measure = () => {
1935
- this.itemSizeCache = /* @__PURE__ */ new Map();
1936
- this.notify(false);
1937
- };
1938
- this.setOptions(opts);
1939
- }
1940
- }
1941
- const findNearestBinarySearch = (low, high, getCurrentValue, value) => {
1942
- while (low <= high) {
1943
- const middle = (low + high) / 2 | 0;
1944
- const currentValue = getCurrentValue(middle);
1945
- if (currentValue < value) {
1946
- low = middle + 1;
1947
- } else if (currentValue > value) {
1948
- high = middle - 1;
1949
- } else {
1950
- return middle;
1951
- }
1952
- }
1953
- if (low > 0) {
1954
- return low - 1;
1955
- } else {
1956
- return 0;
1957
- }
1958
- };
1959
- function calculateRange({
1960
- measurements,
1961
- outerSize,
1962
- scrollOffset,
1963
- lanes
1964
- }) {
1965
- const lastIndex = measurements.length - 1;
1966
- const getOffset = (index) => measurements[index].start;
1967
- if (measurements.length <= lanes) {
1968
- return {
1969
- startIndex: 0,
1970
- endIndex: lastIndex
1971
- };
1972
- }
1973
- let startIndex = findNearestBinarySearch(
1974
- 0,
1975
- lastIndex,
1976
- getOffset,
1977
- scrollOffset
1978
- );
1979
- let endIndex = startIndex;
1980
- if (lanes === 1) {
1981
- while (endIndex < lastIndex && measurements[endIndex].end < scrollOffset + outerSize) {
1982
- endIndex++;
1983
- }
1984
- } else if (lanes > 1) {
1985
- const endPerLane = Array(lanes).fill(0);
1986
- while (endIndex < lastIndex && endPerLane.some((pos) => pos < scrollOffset + outerSize)) {
1987
- const item = measurements[endIndex];
1988
- endPerLane[item.lane] = item.end;
1989
- endIndex++;
1990
- }
1991
- const startPerLane = Array(lanes).fill(scrollOffset + outerSize);
1992
- while (startIndex >= 0 && startPerLane.some((pos) => pos >= scrollOffset)) {
1993
- const item = measurements[startIndex];
1994
- startPerLane[item.lane] = item.start;
1995
- startIndex--;
1996
- }
1997
- startIndex = Math.max(0, startIndex - startIndex % lanes);
1998
- endIndex = Math.min(lastIndex, endIndex + (lanes - 1 - endIndex % lanes));
1999
- }
2000
- return { startIndex, endIndex };
2001
- }
2002
-
2003
- const virtualListProps = defineHookProps({
2004
- options: {
2005
- type: Object,
2006
- default: () => ({})
2007
- },
2008
- count: {
2009
- type: Number,
2010
- default: () => 0
2011
- },
2012
- estimateSize: {
2013
- type: [Function, Number],
2014
- default: () => 50
2015
- },
2016
- horizontal: {
2017
- type: Boolean,
2018
- default: () => false
2019
- }
2020
- });
2021
- const virtualListEmits = defineHookEmits({
2022
- scrollEnd: () => true,
2023
- scrollStart: () => true,
2024
- scroll: (_) => true
2025
- });
2026
- const useVirtualList = defineHookComponent({
2027
- props: virtualListProps,
2028
- emits: virtualListEmits,
2029
- setup(props, context) {
2030
- const { emit } = context;
2031
- const scrollElementRef = elementRef();
2032
- const propsEstimateSize = props.estimateSize;
2033
- const estimateSize = typeof propsEstimateSize === "function" ? propsEstimateSize : () => propsEstimateSize;
2034
- const options = computed(() => {
2035
- const opts = { ...props.options || {} };
2036
- return {
2037
- ...opts,
2038
- count: props.count,
2039
- estimateSize,
2040
- horizontal: props.horizontal,
2041
- getScrollElement: () => scrollElementRef.value,
2042
- observeElementRect,
2043
- observeElementOffset,
2044
- scrollToFn: elementScroll
2045
- };
2046
- });
2047
- const virtualizer = new Virtualizer(options.value);
2048
- const state = shallowRef(virtualizer);
2049
- const virtualItems = computed(() => state.value.getVirtualItems());
2050
- const virtualIndexes = computed(() => state.value.getVirtualIndexes());
2051
- const totalSize = computed(() => state.value.getTotalSize());
2052
- watch(
2053
- virtualIndexes,
2054
- (indexes) => {
2055
- if (indexes.length === 0) {
2056
- return;
2057
- }
2058
- if (indexes[indexes.length - 1] === props.count - 1) {
2059
- emit("scrollEnd");
2060
- } else if (indexes[0] === 0) {
2061
- emit("scrollStart");
2062
- }
2063
- emit("scroll", indexes);
2064
- },
2065
- { immediate: true }
2066
- );
2067
- watch(
2068
- options,
2069
- (opts) => {
2070
- virtualizer.setOptions({
2071
- ...opts,
2072
- onChange: (instance, sync) => {
2073
- opts.onChange?.(instance, sync);
2074
- triggerRef(state);
2075
- }
2076
- });
2077
- virtualizer._willUpdate();
2078
- triggerRef(state);
2079
- },
2080
- { immediate: true }
2081
- );
2082
- watch(
2083
- scrollElementRef,
2084
- (el) => {
2085
- if (el) {
2086
- virtualizer._willUpdate();
2087
- triggerRef(state);
2088
- }
2089
- },
2090
- { immediate: true }
2091
- );
2092
- tryOnScopeDispose(virtualizer._didMount());
2093
- const measureElement = (el) => {
2094
- virtualizer.measureElement(el);
2095
- };
2096
- const scrollToIndex = (index, options2 = {
2097
- behavior: "smooth"
2098
- }) => {
2099
- virtualizer.scrollToIndex(index, options2);
2100
- };
2101
- const scrollToStart = (options2 = {
2102
- behavior: "smooth"
2103
- }) => {
2104
- scrollToIndex(0, options2);
2105
- };
2106
- const scrollToEnd = (options2 = {
2107
- behavior: "smooth"
2108
- }) => {
2109
- scrollToIndex(props.count - 1, options2);
2110
- };
2111
- return {
2112
- virtualizer,
2113
- virtualItems,
2114
- virtualIndexes,
2115
- totalSize,
2116
- scrollElementRef,
2117
- measureElement,
2118
- scrollToIndex,
2119
- scrollToStart,
2120
- scrollToEnd
2121
- };
2122
- }
2123
- });
2124
-
2125
- const HiVirtualList = defineComponent({
2126
- name: "HiVirtualList",
2127
- inheritAttrs: true,
2128
- props: {
2129
- ...virtualListProps,
2130
- as: {
2131
- type: String,
2132
- default: () => "div"
2133
- },
2134
- wrapperAs: {
2135
- type: String,
2136
- default: () => "div"
2137
- },
2138
- wrapperStyle: {
2139
- type: Object,
2140
- default: () => ({})
2141
- },
2142
- wrapperClass: {
2143
- type: classPropType,
2144
- default: () => ""
2145
- }
2146
- },
2147
- emits: virtualListEmits,
2148
- setup(props, context) {
2149
- const { slots, expose } = context;
2150
- const {
2151
- totalSize,
2152
- scrollElementRef,
2153
- virtualItems,
2154
- scrollToIndex,
2155
- scrollToStart,
2156
- scrollToEnd
2157
- } = useVirtualList(props, context);
2158
- expose({
2159
- scrollToIndex,
2160
- scrollToStart,
2161
- scrollToEnd
2162
- });
2163
- const wrapperStyle = computed(() => {
2164
- return {
2165
- position: "relative",
2166
- [props.horizontal ? "width" : "height"]: `${totalSize.value}px`,
2167
- ...props.wrapperStyle
2168
- };
2169
- });
2170
- return () => h(
2171
- props.as,
2172
- {
2173
- ref: scrollElementRef,
2174
- style: {
2175
- [props.horizontal ? "overflowX" : "overflowY"]: "auto"
2176
- }
2177
- },
2178
- [
2179
- h(
2180
- props.wrapperAs,
2181
- {
2182
- style: wrapperStyle.value,
2183
- class: props.wrapperClass
2184
- },
2185
- each(virtualItems.value, (item) => {
2186
- const slotData = {
2187
- ...item,
2188
- style: {
2189
- position: "absolute",
2190
- [props.horizontal ? "left" : "top"]: `${item.start}px`,
2191
- [props.horizontal ? "width" : "height"]: `${item.size}px`
2192
- }
2193
- };
2194
- return renderSlot(slots, "item", slotData);
2195
- })
2196
- )
2197
- ]
2198
- );
2199
- }
2200
- });
2201
-
2202
- const components = {
2203
- __proto__: null,
2204
- HiAffix: HiAffix,
2205
- HiAffixTarget: HiAffixTarget,
2206
- HiConfigProvider: HiConfigProvider,
2207
- HiFileUpload: HiFileUpload,
2208
- HiIcon: HiIcon,
2209
- HiItem: HiItem,
2210
- HiPopover: HiPopover,
2211
- HiSelection: HiSelection,
2212
- HiSwitch: HiSwitch,
2213
- HiTabPane: HiTabPane,
2214
- HiTabs: HiTabs,
2215
- HiVirtualList: HiVirtualList
2216
- };
2217
-
2218
- function install(app) {
2219
- for (const key in components) {
2220
- app.component(key, components[key]);
2221
- }
2222
- }
2223
-
2224
- export { HiAffix, HiAffixTarget, HiConfigProvider, HiFileUpload, HiIcon, HiItem, HiPopover, HiSelection, HiSwitch, HiTabPane, HiTabs, HiVirtualList, install };