vanilla-jui 1.4.7 → 1.4.8
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.d.ts +24 -1
- package/dist/index.js +1 -1
- package/dist/index.umd.js +1 -1
- package/package.json +1 -1
- package/src/components/accordion.ts +25 -23
- package/src/components/form.ts +1 -1
- package/src/components/menu.ts +14 -17
- package/src/components/offcanvas.ts +6 -20
- package/src/components/pagination.ts +9 -16
- package/src/components/swiper.ts +84 -22
- package/src/components/tabs.ts +48 -39
- package/src/index.ts +1 -0
- package/src/utilities/scheduler.ts +156 -0
package/package.json
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bindAttr,
|
|
3
3
|
createDeepStore,
|
|
4
|
-
createEffect,
|
|
5
4
|
createRoot,
|
|
6
5
|
flushSync,
|
|
7
6
|
jsx,
|
|
8
|
-
untrack,
|
|
9
7
|
} from 'vanilla-signal';
|
|
10
8
|
|
|
11
9
|
import Component, {
|
|
@@ -18,6 +16,11 @@ import {
|
|
|
18
16
|
normalizeContentNodes,
|
|
19
17
|
} from '../utilities/dom.ts';
|
|
20
18
|
import { randomId } from '../utilities/id.ts';
|
|
19
|
+
import {
|
|
20
|
+
createStateSync,
|
|
21
|
+
stateSnapshot,
|
|
22
|
+
trackStoreVersion,
|
|
23
|
+
} from '../utilities/scheduler.ts';
|
|
21
24
|
import {
|
|
22
25
|
type ResolveSchema,
|
|
23
26
|
resolveProps,
|
|
@@ -329,28 +332,27 @@ class Accordion extends Component<
|
|
|
329
332
|
|
|
330
333
|
private bindItems(): void {
|
|
331
334
|
this.itemsDispose?.();
|
|
332
|
-
this.
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (needsItemsSync(sourceItems, items)) {
|
|
339
|
-
this.state.items = items;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
const activeNames = reconcileActiveNames(
|
|
343
|
-
this.state.activeNames,
|
|
344
|
-
items,
|
|
345
|
-
this.props.multiple
|
|
346
|
-
);
|
|
347
|
-
this.buildItems(items, activeNames);
|
|
348
|
-
this.syncActiveNames(activeNames, items);
|
|
349
|
-
});
|
|
350
|
-
});
|
|
335
|
+
this.syncItems(this.state.items);
|
|
336
|
+
this.itemsDispose = createStateSync(
|
|
337
|
+
() => trackStoreVersion(this.state.items),
|
|
338
|
+
(items) => this.syncItems(items)
|
|
339
|
+
);
|
|
340
|
+
}
|
|
351
341
|
|
|
352
|
-
|
|
353
|
-
|
|
342
|
+
private syncItems(sourceItems: AccordionItem[]): void {
|
|
343
|
+
const items = normalizeItems(stateSnapshot(sourceItems));
|
|
344
|
+
|
|
345
|
+
if (needsItemsSync(sourceItems, items)) {
|
|
346
|
+
this.state.items = items;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const activeNames = reconcileActiveNames(
|
|
350
|
+
this.state.activeNames,
|
|
351
|
+
items,
|
|
352
|
+
this.props.multiple
|
|
353
|
+
);
|
|
354
|
+
this.buildItems(items, activeNames);
|
|
355
|
+
this.syncActiveNames(activeNames, items);
|
|
354
356
|
}
|
|
355
357
|
|
|
356
358
|
private buildItems(items: AccordionItem[], activeNames: string[]): void {
|
package/src/components/form.ts
CHANGED
|
@@ -625,7 +625,7 @@ class Form extends Component<ResolvedFormProps, FormState, FormDOM> {
|
|
|
625
625
|
type: 'checkbox',
|
|
626
626
|
id,
|
|
627
627
|
name: field.name,
|
|
628
|
-
value: field.value ?? '
|
|
628
|
+
value: field.value ?? '1',
|
|
629
629
|
checked: !!field.checked,
|
|
630
630
|
disabled: !!field.disabled,
|
|
631
631
|
required: !!field.required,
|
package/src/components/menu.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createDeepStore,
|
|
3
|
-
createEffect,
|
|
4
|
-
createRoot,
|
|
5
|
-
jsx,
|
|
6
|
-
untrack,
|
|
7
|
-
} from 'vanilla-signal';
|
|
1
|
+
import { createDeepStore, jsx } from 'vanilla-signal';
|
|
8
2
|
import { t } from 'vanilla-signal-i18n';
|
|
9
3
|
|
|
10
4
|
import Component, {
|
|
@@ -16,6 +10,11 @@ import { icon } from '../primitives/icons.ts';
|
|
|
16
10
|
import { joinClasses } from '../utilities/class-name.ts';
|
|
17
11
|
import { all } from '../utilities/dom.ts';
|
|
18
12
|
import { randomId } from '../utilities/id.ts';
|
|
13
|
+
import {
|
|
14
|
+
createStateSync,
|
|
15
|
+
stateSnapshot,
|
|
16
|
+
trackStoreVersion,
|
|
17
|
+
} from '../utilities/scheduler.ts';
|
|
19
18
|
import {
|
|
20
19
|
type ResolveSchema,
|
|
21
20
|
resolveProps,
|
|
@@ -284,21 +283,19 @@ class MenuComponent extends Component<ResolvedMenuProps, MenuState, MenuDOM> {
|
|
|
284
283
|
private bindState(): void {
|
|
285
284
|
if (this.cleanup.state) return;
|
|
286
285
|
|
|
287
|
-
this.cleanup.state =
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
return dispose;
|
|
295
|
-
});
|
|
286
|
+
this.cleanup.state = createStateSync(
|
|
287
|
+
() => ({
|
|
288
|
+
data: trackStoreVersion(this.state.data),
|
|
289
|
+
}),
|
|
290
|
+
(snapshot) => this.renderSnapshot(snapshot),
|
|
291
|
+
{ deferInitial: false, flushInitial: true }
|
|
292
|
+
);
|
|
296
293
|
}
|
|
297
294
|
|
|
298
295
|
private renderSnapshot(snapshot: MenuSnapshot): void {
|
|
299
296
|
if (!this.runtime.built || !this.dom.root || !this.dom.list) return;
|
|
300
297
|
this.validateData(snapshot.data);
|
|
301
|
-
const data = cloneMenuData(snapshot.data);
|
|
298
|
+
const data = cloneMenuData(stateSnapshot(snapshot.data));
|
|
302
299
|
|
|
303
300
|
this.dom.list.textContent = '';
|
|
304
301
|
this.dom.list.append(...data.map((item) => this.buildItem(item)));
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createDeepStore,
|
|
3
|
-
createEffect,
|
|
4
|
-
createRoot,
|
|
5
|
-
flushSync,
|
|
6
|
-
jsx,
|
|
7
|
-
untrack,
|
|
8
|
-
} from 'vanilla-signal';
|
|
1
|
+
import { createDeepStore, flushSync, jsx } from 'vanilla-signal';
|
|
9
2
|
|
|
10
3
|
import Component, {
|
|
11
4
|
type ComponentDOM,
|
|
@@ -17,6 +10,7 @@ import {
|
|
|
17
10
|
normalizeContentNodes,
|
|
18
11
|
} from '../utilities/dom.ts';
|
|
19
12
|
import { randomId } from '../utilities/id.ts';
|
|
13
|
+
import { createStateSync } from '../utilities/scheduler.ts';
|
|
20
14
|
import { timer } from '../utilities/timer.ts';
|
|
21
15
|
import {
|
|
22
16
|
type ResolveSchema,
|
|
@@ -298,18 +292,10 @@ class OffcanvasComponent extends Component<
|
|
|
298
292
|
private bindState(): void {
|
|
299
293
|
if (this.cleanup.state) return;
|
|
300
294
|
|
|
301
|
-
this.cleanup.state =
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if (!initialized) {
|
|
306
|
-
initialized = true;
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
void untrack(() => this.syncContent(content));
|
|
310
|
-
});
|
|
311
|
-
return dispose;
|
|
312
|
-
});
|
|
295
|
+
this.cleanup.state = createStateSync(
|
|
296
|
+
() => this.state.content,
|
|
297
|
+
(content) => this.syncContent(content)
|
|
298
|
+
);
|
|
313
299
|
}
|
|
314
300
|
|
|
315
301
|
private async syncContent(content: OffcanvasContent): Promise<void> {
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createDeepStore,
|
|
3
|
-
createEffect,
|
|
4
|
-
createRoot,
|
|
5
|
-
flushSync,
|
|
6
|
-
jsx,
|
|
7
|
-
untrack,
|
|
8
|
-
} from 'vanilla-signal';
|
|
1
|
+
import { createDeepStore, flushSync, jsx } from 'vanilla-signal';
|
|
9
2
|
|
|
10
3
|
import Component, {
|
|
11
4
|
type ComponentDOM,
|
|
@@ -13,6 +6,7 @@ import Component, {
|
|
|
13
6
|
} from '../core/Component.ts';
|
|
14
7
|
import { icon } from '../primitives/icons.ts';
|
|
15
8
|
import { isPlainObject } from '../utilities/object.ts';
|
|
9
|
+
import { createStateSync } from '../utilities/scheduler.ts';
|
|
16
10
|
import {
|
|
17
11
|
type ResolveSchema,
|
|
18
12
|
resolveProps,
|
|
@@ -400,20 +394,19 @@ class PaginationComponent extends Component<
|
|
|
400
394
|
private bindState(): void {
|
|
401
395
|
if (this.cleanup.state) return;
|
|
402
396
|
|
|
403
|
-
this.cleanup.state =
|
|
404
|
-
|
|
405
|
-
|
|
397
|
+
this.cleanup.state = createStateSync(
|
|
398
|
+
() =>
|
|
399
|
+
({
|
|
406
400
|
total: this.state.total,
|
|
407
401
|
size: this.state.page.size,
|
|
408
402
|
current: this.state.page.current,
|
|
409
403
|
sibling: this.state.count.sibling,
|
|
410
404
|
boundary: this.state.count.boundary,
|
|
411
405
|
locked: this.state.locked,
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
});
|
|
406
|
+
}) as PaginationSnapshot,
|
|
407
|
+
(snapshot) => this.syncState(snapshot),
|
|
408
|
+
{ deferInitial: false, flushInitial: true, flush: 'sync' }
|
|
409
|
+
);
|
|
417
410
|
}
|
|
418
411
|
|
|
419
412
|
private syncState(snapshot: PaginationSnapshot): void {
|
package/src/components/swiper.ts
CHANGED
|
@@ -2,10 +2,8 @@ import {
|
|
|
2
2
|
bindAttr,
|
|
3
3
|
bindClass,
|
|
4
4
|
createDeepStore,
|
|
5
|
-
createEffect,
|
|
6
5
|
createRoot,
|
|
7
6
|
h,
|
|
8
|
-
untrack,
|
|
9
7
|
} from 'vanilla-signal';
|
|
10
8
|
|
|
11
9
|
import Component, {
|
|
@@ -22,6 +20,11 @@ import {
|
|
|
22
20
|
type RenderableContent,
|
|
23
21
|
} from '../utilities/dom.ts';
|
|
24
22
|
import { randomId } from '../utilities/id.ts';
|
|
23
|
+
import {
|
|
24
|
+
createStateSync,
|
|
25
|
+
stateSnapshot,
|
|
26
|
+
trackStoreVersion,
|
|
27
|
+
} from '../utilities/scheduler.ts';
|
|
25
28
|
import {
|
|
26
29
|
type ResolveSchema,
|
|
27
30
|
resolveProps,
|
|
@@ -156,6 +159,7 @@ interface SwiperRuntime extends ComponentRuntime {
|
|
|
156
159
|
swiping: boolean;
|
|
157
160
|
clickPrevented: boolean;
|
|
158
161
|
timer: ReturnType<typeof setInterval> | null;
|
|
162
|
+
mountRefreshId: number | null;
|
|
159
163
|
imageCleanups: Set<() => void>;
|
|
160
164
|
realCount: number;
|
|
161
165
|
}
|
|
@@ -374,6 +378,7 @@ class SwiperComponent extends Component<
|
|
|
374
378
|
this.runtime.swiping = false;
|
|
375
379
|
this.runtime.clickPrevented = false;
|
|
376
380
|
this.runtime.timer = null;
|
|
381
|
+
this.runtime.mountRefreshId = null;
|
|
377
382
|
this.runtime.imageCleanups = new Set();
|
|
378
383
|
|
|
379
384
|
this.runtime.realCount = 0;
|
|
@@ -452,6 +457,7 @@ class SwiperComponent extends Component<
|
|
|
452
457
|
|
|
453
458
|
if (this.dom.slides.length > 0) this.onInit();
|
|
454
459
|
if (this.dom.createdRoot) this.bindStateData();
|
|
460
|
+
this.queueMountRefresh();
|
|
455
461
|
} catch (error) {
|
|
456
462
|
this.destroy();
|
|
457
463
|
throw error;
|
|
@@ -519,28 +525,21 @@ class SwiperComponent extends Component<
|
|
|
519
525
|
private bindStateData(): void {
|
|
520
526
|
if (this.cleanup.data) return;
|
|
521
527
|
|
|
522
|
-
this.cleanup.data =
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
if (!initialized) {
|
|
527
|
-
initialized = true;
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
untrack(() => this.syncStateData(data));
|
|
531
|
-
});
|
|
532
|
-
return dispose;
|
|
533
|
-
});
|
|
528
|
+
this.cleanup.data = createStateSync(
|
|
529
|
+
() => trackStoreVersion(this.state.data),
|
|
530
|
+
(data) => this.syncStateData(data)
|
|
531
|
+
);
|
|
534
532
|
}
|
|
535
533
|
|
|
536
534
|
private syncStateData(data: SwiperDataItem[]): void {
|
|
537
535
|
if (!this.runtime.built || !this.dom.createdRoot) return;
|
|
538
|
-
|
|
536
|
+
const nextData = cloneData(stateSnapshot(data));
|
|
537
|
+
validateParam('data', nextData, SWIPER_OPTIONS_SCHEMA.data, 'Swiper.state');
|
|
539
538
|
|
|
540
539
|
const realIndex = this.state.index;
|
|
541
540
|
let wrapper = this.dom.wrapper;
|
|
542
541
|
if (!wrapper && this.dom.root) {
|
|
543
|
-
wrapper = this.createDataView(this.dom.root,
|
|
542
|
+
wrapper = this.createDataView(this.dom.root, nextData);
|
|
544
543
|
this.dom.wrapper = wrapper;
|
|
545
544
|
}
|
|
546
545
|
if (!wrapper) return;
|
|
@@ -555,7 +554,7 @@ class SwiperComponent extends Component<
|
|
|
555
554
|
|
|
556
555
|
all('[data-clone]', wrapper).forEach((slide) => slide.remove());
|
|
557
556
|
wrapper.textContent = '';
|
|
558
|
-
this.normalizeData(
|
|
557
|
+
this.normalizeData(nextData).forEach((item, index) => {
|
|
559
558
|
wrapper.appendChild(this.createDataSlide(item, index));
|
|
560
559
|
});
|
|
561
560
|
this.refreshSlides();
|
|
@@ -658,6 +657,7 @@ class SwiperComponent extends Component<
|
|
|
658
657
|
|
|
659
658
|
protected onDestroy(): void {
|
|
660
659
|
this.pause();
|
|
660
|
+
this.cancelMountRefresh();
|
|
661
661
|
this.clearImageCleanups();
|
|
662
662
|
this.cleanup.events.clear();
|
|
663
663
|
this.cleanup.data?.();
|
|
@@ -680,7 +680,10 @@ class SwiperComponent extends Component<
|
|
|
680
680
|
private updateSize(): void {
|
|
681
681
|
this.assertBuilt('updateSize');
|
|
682
682
|
if (!this.dom.root) return;
|
|
683
|
-
|
|
683
|
+
const width = this.dom.root.clientWidth || this.dom.root.offsetWidth;
|
|
684
|
+
if (width > 0 || !this.dom.root.isConnected || this.state.width === 0) {
|
|
685
|
+
this.width = width;
|
|
686
|
+
}
|
|
684
687
|
}
|
|
685
688
|
|
|
686
689
|
refresh(): this {
|
|
@@ -689,11 +692,61 @@ class SwiperComponent extends Component<
|
|
|
689
692
|
this.setupStyles();
|
|
690
693
|
this.setTrackIndex(this.trackIndexForRealIndex(this.state.index), false);
|
|
691
694
|
if (this.props.lazyload) this.loadImages();
|
|
692
|
-
if (this.props.autoplay) this.resume();
|
|
695
|
+
if (this.props.autoplay && this.hasLayout()) this.resume();
|
|
693
696
|
else this.pause();
|
|
694
697
|
return this;
|
|
695
698
|
}
|
|
696
699
|
|
|
700
|
+
private hasLayout(): boolean {
|
|
701
|
+
return !!this.dom.root?.isConnected && this.state.width > 0;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
private syncLayout(): boolean {
|
|
705
|
+
this.updateSize();
|
|
706
|
+
if (!this.hasLayout()) return false;
|
|
707
|
+
this.setupStyles();
|
|
708
|
+
return true;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
private queueMountRefresh(): void {
|
|
712
|
+
if (!this.dom.createdRoot || !this.dom.root || this.runtime.mountRefreshId)
|
|
713
|
+
return;
|
|
714
|
+
if (typeof requestAnimationFrame !== 'function') {
|
|
715
|
+
queueMicrotask(() => {
|
|
716
|
+
if (
|
|
717
|
+
!this.runtime.destroyed &&
|
|
718
|
+
this.runtime.built &&
|
|
719
|
+
this.dom.root?.isConnected
|
|
720
|
+
) {
|
|
721
|
+
this.refresh();
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
this.runtime.mountRefreshId = requestAnimationFrame(() => {
|
|
727
|
+
this.runtime.mountRefreshId = null;
|
|
728
|
+
if (
|
|
729
|
+
!this.runtime.destroyed &&
|
|
730
|
+
this.runtime.built &&
|
|
731
|
+
this.dom.root?.isConnected
|
|
732
|
+
) {
|
|
733
|
+
this.refresh();
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
private cancelMountRefresh(): void {
|
|
739
|
+
if (
|
|
740
|
+
this.runtime.mountRefreshId == null ||
|
|
741
|
+
typeof cancelAnimationFrame !== 'function'
|
|
742
|
+
) {
|
|
743
|
+
this.runtime.mountRefreshId = null;
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
cancelAnimationFrame(this.runtime.mountRefreshId);
|
|
747
|
+
this.runtime.mountRefreshId = null;
|
|
748
|
+
}
|
|
749
|
+
|
|
697
750
|
private refreshSlides(): void {
|
|
698
751
|
if (!this.dom.wrapper) return;
|
|
699
752
|
this.dom.slides = all<HTMLElement>('[data-swiper-slide]', this.dom.wrapper);
|
|
@@ -893,7 +946,7 @@ class SwiperComponent extends Component<
|
|
|
893
946
|
}
|
|
894
947
|
|
|
895
948
|
private onStart(point: SwipePoint, target: EventTarget | null = null): void {
|
|
896
|
-
if (this.state.animating) return;
|
|
949
|
+
if (this.state.animating || !this.syncLayout()) return;
|
|
897
950
|
|
|
898
951
|
this.runtime.logs = [];
|
|
899
952
|
this.pushLog(point);
|
|
@@ -1082,6 +1135,7 @@ class SwiperComponent extends Component<
|
|
|
1082
1135
|
this.assertBuilt('slideToTrack');
|
|
1083
1136
|
if (this.state.animating) return;
|
|
1084
1137
|
if (this.dom.slides.length === 0) return;
|
|
1138
|
+
if (!this.syncLayout()) return;
|
|
1085
1139
|
|
|
1086
1140
|
let target = idx;
|
|
1087
1141
|
|
|
@@ -1113,7 +1167,14 @@ class SwiperComponent extends Component<
|
|
|
1113
1167
|
private render(animate: boolean): void {
|
|
1114
1168
|
this.assertBuilt('render');
|
|
1115
1169
|
if (!this.dom.wrapper) return;
|
|
1116
|
-
|
|
1170
|
+
const transform = `translate3d(${this.state.transform}px, 0, 0)`;
|
|
1171
|
+
const shouldAnimate =
|
|
1172
|
+
animate &&
|
|
1173
|
+
this.props.speed > 0 &&
|
|
1174
|
+
this.state.width > 0 &&
|
|
1175
|
+
this.dom.wrapper.style.transform !== transform;
|
|
1176
|
+
|
|
1177
|
+
if (shouldAnimate) {
|
|
1117
1178
|
this.dom.wrapper.style.transition = `transform ${this.props.speed}ms cubic-bezier(0.25, 0.46, 0.45, 0.94)`;
|
|
1118
1179
|
this.animating = true;
|
|
1119
1180
|
} else {
|
|
@@ -1121,7 +1182,7 @@ class SwiperComponent extends Component<
|
|
|
1121
1182
|
this.animating = false;
|
|
1122
1183
|
}
|
|
1123
1184
|
|
|
1124
|
-
this.dom.wrapper.style.transform =
|
|
1185
|
+
this.dom.wrapper.style.transform = transform;
|
|
1125
1186
|
}
|
|
1126
1187
|
|
|
1127
1188
|
private loadImages(): void {
|
|
@@ -1326,6 +1387,7 @@ class SwiperComponent extends Component<
|
|
|
1326
1387
|
this.assertBuilt('play');
|
|
1327
1388
|
if (this.runtime.destroyed || this.runtime.timer) return;
|
|
1328
1389
|
if (this.realCount <= 1) return;
|
|
1390
|
+
if (!this.syncLayout()) return;
|
|
1329
1391
|
const delay = Math.max(this.props.delay, AUTOPLAY_DELAY_FLOOR);
|
|
1330
1392
|
this.runtime.timer = setInterval(() => this.next(), delay);
|
|
1331
1393
|
}
|
package/src/components/tabs.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bindAttr,
|
|
3
3
|
createDeepStore,
|
|
4
|
-
createEffect,
|
|
5
4
|
createRoot,
|
|
6
5
|
flushSync,
|
|
7
6
|
jsx,
|
|
8
|
-
untrack,
|
|
9
7
|
} from 'vanilla-signal';
|
|
10
8
|
|
|
11
9
|
import Component, {
|
|
@@ -19,6 +17,11 @@ import {
|
|
|
19
17
|
q,
|
|
20
18
|
} from '../utilities/dom.ts';
|
|
21
19
|
import { randomId } from '../utilities/id.ts';
|
|
20
|
+
import {
|
|
21
|
+
createStateSync,
|
|
22
|
+
getStoreVersion,
|
|
23
|
+
stateSnapshot,
|
|
24
|
+
} from '../utilities/scheduler.ts';
|
|
22
25
|
import {
|
|
23
26
|
type ResolveSchema,
|
|
24
27
|
resolveProps,
|
|
@@ -334,7 +337,7 @@ class TabsComponent extends Component<ResolvedTabsProps, TabsState, TabsDOM> {
|
|
|
334
337
|
}) as HTMLElement;
|
|
335
338
|
}
|
|
336
339
|
|
|
337
|
-
private renderItems(): void {
|
|
340
|
+
private renderItems(data: TabItem[] = this.state.data): void {
|
|
338
341
|
if (!this.dom.root) return;
|
|
339
342
|
const tabList = q<HTMLElement>('[data-tabs-list]', this.dom.root);
|
|
340
343
|
const panelWrapper = q<HTMLElement>(
|
|
@@ -352,7 +355,7 @@ class TabsComponent extends Component<ResolvedTabsProps, TabsState, TabsDOM> {
|
|
|
352
355
|
const tabFragment = document.createDocumentFragment();
|
|
353
356
|
const panelFragment = document.createDocumentFragment();
|
|
354
357
|
|
|
355
|
-
|
|
358
|
+
data.forEach((item, index) => {
|
|
356
359
|
const name = item.name || randomId();
|
|
357
360
|
|
|
358
361
|
const tab = jsx('div', {
|
|
@@ -410,78 +413,84 @@ class TabsComponent extends Component<ResolvedTabsProps, TabsState, TabsDOM> {
|
|
|
410
413
|
private bindState(): void {
|
|
411
414
|
if (this.stateDispose) return;
|
|
412
415
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
416
|
+
let previousData = this.state.data;
|
|
417
|
+
let previousDataVersion = getStoreVersion(previousData);
|
|
418
|
+
let previousDirection = this.state.direction;
|
|
419
|
+
let previousActive = this.state.active;
|
|
420
|
+
|
|
421
|
+
this.stateDispose = createStateSync(
|
|
422
|
+
() => {
|
|
419
423
|
const data = this.state.data;
|
|
424
|
+
const dataVersion = getStoreVersion(data);
|
|
420
425
|
const direction = this.state.direction;
|
|
421
426
|
const disabled = this.state.disabled;
|
|
422
427
|
const active = this.state.active;
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
428
|
+
const snapshot = {
|
|
429
|
+
data,
|
|
430
|
+
direction,
|
|
431
|
+
disabled,
|
|
432
|
+
active,
|
|
433
|
+
dataChanged:
|
|
434
|
+
data !== previousData || dataVersion !== previousDataVersion,
|
|
435
|
+
directionChanged: direction !== previousDirection,
|
|
436
|
+
activeChanged: active !== previousActive,
|
|
437
|
+
};
|
|
438
|
+
|
|
431
439
|
previousData = data;
|
|
440
|
+
previousDataVersion = dataVersion;
|
|
432
441
|
previousDirection = direction;
|
|
433
442
|
previousActive = active;
|
|
434
443
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
});
|
|
440
|
-
return dispose;
|
|
441
|
-
});
|
|
444
|
+
return snapshot;
|
|
445
|
+
},
|
|
446
|
+
(snapshot) => this.syncStateView(snapshot)
|
|
447
|
+
);
|
|
442
448
|
}
|
|
443
449
|
|
|
444
450
|
private async syncStateView({
|
|
451
|
+
data,
|
|
452
|
+
direction,
|
|
453
|
+
disabled,
|
|
454
|
+
active,
|
|
445
455
|
dataChanged,
|
|
446
456
|
directionChanged,
|
|
447
457
|
activeChanged,
|
|
448
458
|
}: {
|
|
459
|
+
data: TabItem[];
|
|
460
|
+
direction: TabsDirection;
|
|
461
|
+
disabled: TabsDisabled;
|
|
462
|
+
active: TabsValue;
|
|
449
463
|
dataChanged: boolean;
|
|
450
464
|
directionChanged: boolean;
|
|
451
465
|
activeChanged: boolean;
|
|
452
466
|
}): Promise<void> {
|
|
453
467
|
if (!this.runtime.built || !this.dom.root) return;
|
|
454
|
-
|
|
468
|
+
const dataSnapshot = cloneTabItems(stateSnapshot(data));
|
|
469
|
+
this.validateData(dataSnapshot);
|
|
455
470
|
validateParam(
|
|
456
471
|
'direction',
|
|
457
|
-
|
|
472
|
+
direction,
|
|
458
473
|
TABS_STATE_SCHEMA.direction,
|
|
459
474
|
'Tabs.state'
|
|
460
475
|
);
|
|
461
476
|
validateParam(
|
|
462
477
|
'disabled',
|
|
463
|
-
|
|
478
|
+
disabled,
|
|
464
479
|
TABS_STATE_SCHEMA.disabled,
|
|
465
480
|
'Tabs.state'
|
|
466
481
|
);
|
|
467
|
-
validateParam(
|
|
468
|
-
'active',
|
|
469
|
-
this.state.active,
|
|
470
|
-
TABS_STATE_SCHEMA.active,
|
|
471
|
-
'Tabs.state'
|
|
472
|
-
);
|
|
482
|
+
validateParam('active', active, TABS_STATE_SCHEMA.active, 'Tabs.state');
|
|
473
483
|
|
|
474
484
|
if (directionChanged) {
|
|
475
|
-
this.dom.root.setAttribute('data-tabs-direction',
|
|
485
|
+
this.dom.root.setAttribute('data-tabs-direction', direction);
|
|
476
486
|
flushSync(() => {
|
|
477
|
-
this.state.isVertical =
|
|
478
|
-
this.state.direction === 'left' || this.state.direction === 'right';
|
|
487
|
+
this.state.isVertical = direction === 'left' || direction === 'right';
|
|
479
488
|
});
|
|
480
489
|
}
|
|
481
490
|
|
|
482
491
|
if (dataChanged) {
|
|
483
492
|
this.runtime.cache.panels.clear();
|
|
484
|
-
this.renderItems();
|
|
493
|
+
this.renderItems(dataSnapshot);
|
|
485
494
|
this.bindEvents();
|
|
486
495
|
flushSync(() => {
|
|
487
496
|
this.syncCurrent(-1);
|
|
@@ -489,7 +498,7 @@ class TabsComponent extends Component<ResolvedTabsProps, TabsState, TabsDOM> {
|
|
|
489
498
|
}
|
|
490
499
|
|
|
491
500
|
if (dataChanged || activeChanged) {
|
|
492
|
-
await this.activateInternal(
|
|
501
|
+
await this.activateInternal(active, activeChanged);
|
|
493
502
|
}
|
|
494
503
|
|
|
495
504
|
this.refreshDrag();
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from './utilities/object.ts';
|
|
|
11
11
|
export * from './utilities/timer.ts';
|
|
12
12
|
export * from './utilities/types.ts';
|
|
13
13
|
export * from './utilities/function.ts';
|
|
14
|
+
export * from './utilities/scheduler.ts';
|
|
14
15
|
|
|
15
16
|
// UI primitives
|
|
16
17
|
export * from './primitives/icons.ts';
|