chat-layout 1.2.0-4 → 1.2.0-5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -1
- package/example/chat.ts +33 -14
- package/index.d.mts +47 -31
- package/index.mjs +1048 -487
- package/index.mjs.map +1 -1
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -3649,6 +3649,21 @@ function normalizeUpdateAnimation(animation) {
|
|
|
3649
3649
|
function normalizeDeleteAnimation(animation) {
|
|
3650
3650
|
return normalizeAnimationDuration(animation?.duration);
|
|
3651
3651
|
}
|
|
3652
|
+
const DEFAULT_INSERT_ALL_ANIMATION_DURATION = 220;
|
|
3653
|
+
function normalizeInsertAnimationDuration(duration, hasAnimationOptions) {
|
|
3654
|
+
if (!hasAnimationOptions) return;
|
|
3655
|
+
const resolvedDuration = duration == null ? DEFAULT_INSERT_ALL_ANIMATION_DURATION : duration;
|
|
3656
|
+
if (!Number.isFinite(resolvedDuration) || resolvedDuration <= 0) return;
|
|
3657
|
+
return resolvedDuration;
|
|
3658
|
+
}
|
|
3659
|
+
function normalizeInsertAnimation(animation) {
|
|
3660
|
+
const duration = normalizeInsertAnimationDuration(animation?.duration, animation != null);
|
|
3661
|
+
if (duration == null) return;
|
|
3662
|
+
const normalizedAnimation = { duration };
|
|
3663
|
+
if (typeof animation?.distance === "number" && Number.isFinite(animation.distance)) normalizedAnimation.distance = Math.max(0, animation.distance);
|
|
3664
|
+
if (animation?.followIfAtBoundary === true) normalizedAnimation.followIfAtBoundary = true;
|
|
3665
|
+
return normalizedAnimation;
|
|
3666
|
+
}
|
|
3652
3667
|
var ListState = class {
|
|
3653
3668
|
#items;
|
|
3654
3669
|
#pendingDeletes = /* @__PURE__ */ new Set();
|
|
@@ -3681,14 +3696,16 @@ var ListState = class {
|
|
|
3681
3696
|
this.unshiftAll(items);
|
|
3682
3697
|
}
|
|
3683
3698
|
/** Prepends an array of items. */
|
|
3684
|
-
unshiftAll(items) {
|
|
3699
|
+
unshiftAll(items, animation) {
|
|
3685
3700
|
if (items.length === 0) return;
|
|
3686
3701
|
assertUniqueItemReferences(items, this.#items);
|
|
3702
|
+
const normalizedAnimation = normalizeInsertAnimation(animation);
|
|
3687
3703
|
if (this.position != null) this.position += items.length;
|
|
3688
3704
|
this.#items = items.concat(this.#items);
|
|
3689
3705
|
emitListStateChange(this, {
|
|
3690
3706
|
type: "unshift",
|
|
3691
|
-
count: items.length
|
|
3707
|
+
count: items.length,
|
|
3708
|
+
animation: normalizedAnimation
|
|
3692
3709
|
});
|
|
3693
3710
|
}
|
|
3694
3711
|
/** Appends one or more items. */
|
|
@@ -3696,13 +3713,15 @@ var ListState = class {
|
|
|
3696
3713
|
this.pushAll(items);
|
|
3697
3714
|
}
|
|
3698
3715
|
/** Appends an array of items. */
|
|
3699
|
-
pushAll(items) {
|
|
3716
|
+
pushAll(items, animation) {
|
|
3700
3717
|
if (items.length === 0) return;
|
|
3701
3718
|
assertUniqueItemReferences(items, this.#items);
|
|
3719
|
+
const normalizedAnimation = normalizeInsertAnimation(animation);
|
|
3702
3720
|
this.#items.push(...items);
|
|
3703
3721
|
emitListStateChange(this, {
|
|
3704
3722
|
type: "push",
|
|
3705
|
-
count: items.length
|
|
3723
|
+
count: items.length,
|
|
3724
|
+
animation: normalizedAnimation
|
|
3706
3725
|
});
|
|
3707
3726
|
}
|
|
3708
3727
|
/**
|
|
@@ -3856,287 +3875,826 @@ function memoRenderItemBy(keyOf, renderItem, options = {}) {
|
|
|
3856
3875
|
}
|
|
3857
3876
|
//#endregion
|
|
3858
3877
|
//#region src/renderer/virtualized/base-animation.ts
|
|
3859
|
-
function clamp$
|
|
3878
|
+
function clamp$1(value, min, max) {
|
|
3860
3879
|
return Math.min(Math.max(value, min), max);
|
|
3861
3880
|
}
|
|
3862
3881
|
function sameState(state, position, offset) {
|
|
3863
3882
|
return Object.is(state.position, position) && Object.is(state.offset, offset);
|
|
3864
3883
|
}
|
|
3884
|
+
function resolveJumpSegmentIndex(anchor, direction, itemCount) {
|
|
3885
|
+
if (itemCount <= 0) return;
|
|
3886
|
+
if (direction > 0) {
|
|
3887
|
+
if (anchor >= itemCount) return;
|
|
3888
|
+
return clamp$1(Math.floor(anchor), 0, itemCount - 1);
|
|
3889
|
+
}
|
|
3890
|
+
if (anchor <= 0) return;
|
|
3891
|
+
return clamp$1(Math.ceil(anchor) - 1, 0, itemCount - 1);
|
|
3892
|
+
}
|
|
3893
|
+
function buildJumpPath(itemCount, readItemHeight, startAnchor, targetAnchor) {
|
|
3894
|
+
const clampedStartAnchor = clamp$1(startAnchor, 0, itemCount);
|
|
3895
|
+
const clampedTargetAnchor = clamp$1(targetAnchor, 0, itemCount);
|
|
3896
|
+
if (itemCount <= 0 || !Number.isFinite(clampedStartAnchor) || !Number.isFinite(clampedTargetAnchor) || Math.abs(clampedTargetAnchor - clampedStartAnchor) <= Number.EPSILON) return {
|
|
3897
|
+
startAnchor: clampedStartAnchor,
|
|
3898
|
+
targetAnchor: clampedTargetAnchor,
|
|
3899
|
+
totalDistance: 0,
|
|
3900
|
+
segments: []
|
|
3901
|
+
};
|
|
3902
|
+
const direction = clampedTargetAnchor > clampedStartAnchor ? 1 : -1;
|
|
3903
|
+
const segments = [];
|
|
3904
|
+
let cursor = clampedStartAnchor;
|
|
3905
|
+
let totalDistance = 0;
|
|
3906
|
+
while (direction > 0 ? cursor < clampedTargetAnchor : cursor > clampedTargetAnchor) {
|
|
3907
|
+
const index = resolveJumpSegmentIndex(cursor, direction, itemCount);
|
|
3908
|
+
if (index == null) break;
|
|
3909
|
+
const nextCursor = direction > 0 ? Math.min(clampedTargetAnchor, index + 1) : Math.max(clampedTargetAnchor, index);
|
|
3910
|
+
if (Math.abs(nextCursor - cursor) <= Number.EPSILON) {
|
|
3911
|
+
cursor = nextCursor;
|
|
3912
|
+
continue;
|
|
3913
|
+
}
|
|
3914
|
+
const height = readItemHeight(index);
|
|
3915
|
+
const distance = height > 0 ? Math.abs(nextCursor - cursor) * height : 0;
|
|
3916
|
+
if (distance > 0) {
|
|
3917
|
+
segments.push({
|
|
3918
|
+
anchorStart: cursor,
|
|
3919
|
+
anchorEnd: nextCursor,
|
|
3920
|
+
distanceStart: totalDistance,
|
|
3921
|
+
distanceEnd: totalDistance + distance
|
|
3922
|
+
});
|
|
3923
|
+
totalDistance += distance;
|
|
3924
|
+
}
|
|
3925
|
+
cursor = nextCursor;
|
|
3926
|
+
}
|
|
3927
|
+
return {
|
|
3928
|
+
startAnchor: clampedStartAnchor,
|
|
3929
|
+
targetAnchor: clampedTargetAnchor,
|
|
3930
|
+
totalDistance,
|
|
3931
|
+
segments
|
|
3932
|
+
};
|
|
3933
|
+
}
|
|
3865
3934
|
function smoothstep(value) {
|
|
3866
3935
|
return value * value * (3 - 2 * value);
|
|
3867
3936
|
}
|
|
3868
3937
|
function getProgress(startTime, duration, now) {
|
|
3869
3938
|
if (!(duration > 0)) return 1;
|
|
3870
|
-
return clamp$
|
|
3939
|
+
return clamp$1((now - startTime) / duration, 0, 1);
|
|
3871
3940
|
}
|
|
3872
3941
|
function interpolate(from, to, startTime, duration, now) {
|
|
3873
3942
|
const progress = getProgress(startTime, duration, now);
|
|
3874
3943
|
const eased = progress >= 1 ? 1 : smoothstep(progress);
|
|
3875
3944
|
return from + (to - from) * eased;
|
|
3876
3945
|
}
|
|
3946
|
+
function getAnchorAtDistance(path, distance) {
|
|
3947
|
+
if (!(path.totalDistance > 0) || path.segments.length === 0) return path.targetAnchor;
|
|
3948
|
+
const clampedDistance = clamp$1(distance, 0, path.totalDistance);
|
|
3949
|
+
if (clampedDistance <= 0) return path.startAnchor;
|
|
3950
|
+
if (clampedDistance >= path.totalDistance) return path.targetAnchor;
|
|
3951
|
+
for (const segment of path.segments) {
|
|
3952
|
+
if (clampedDistance >= segment.distanceEnd) continue;
|
|
3953
|
+
const span = segment.distanceEnd - segment.distanceStart;
|
|
3954
|
+
if (!(span > 0)) continue;
|
|
3955
|
+
const ratio = (clampedDistance - segment.distanceStart) / span;
|
|
3956
|
+
return segment.anchorStart + (segment.anchorEnd - segment.anchorStart) * ratio;
|
|
3957
|
+
}
|
|
3958
|
+
return path.targetAnchor;
|
|
3959
|
+
}
|
|
3877
3960
|
function getNow() {
|
|
3878
3961
|
return globalThis.performance?.now() ?? Date.now();
|
|
3879
3962
|
}
|
|
3880
3963
|
//#endregion
|
|
3881
|
-
//#region src/renderer/virtualized/
|
|
3882
|
-
|
|
3883
|
-
|
|
3964
|
+
//#region src/renderer/virtualized/frame-session.ts
|
|
3965
|
+
function prepareFrameSession(params) {
|
|
3966
|
+
let solution = params.resolveVisibleWindow(params.now);
|
|
3967
|
+
let viewportTranslateY = params.getViewportTranslateY(params.now);
|
|
3968
|
+
params.captureVisibleItemSnapshot(solution, viewportTranslateY);
|
|
3969
|
+
const requestSettleRedraw = params.pruneTransitionAnimations(solution.window, params.now);
|
|
3970
|
+
if (requestSettleRedraw) {
|
|
3971
|
+
solution = params.resolveVisibleWindow(params.now);
|
|
3972
|
+
viewportTranslateY = params.getViewportTranslateY(params.now);
|
|
3973
|
+
params.captureVisibleItemSnapshot(solution, viewportTranslateY);
|
|
3974
|
+
}
|
|
3975
|
+
return {
|
|
3976
|
+
solution,
|
|
3977
|
+
viewportTranslateY,
|
|
3978
|
+
requestSettleRedraw
|
|
3979
|
+
};
|
|
3980
|
+
}
|
|
3884
3981
|
//#endregion
|
|
3885
|
-
//#region src/renderer/virtualized/
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
#
|
|
3892
|
-
#
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
const
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3982
|
+
//#region src/renderer/virtualized/jump-controller.ts
|
|
3983
|
+
var JumpController = class {
|
|
3984
|
+
#autoFollowLatch;
|
|
3985
|
+
#controlledState;
|
|
3986
|
+
#jumpAnimation;
|
|
3987
|
+
#lastCommittedState;
|
|
3988
|
+
#hasPendingListChange = false;
|
|
3989
|
+
#options;
|
|
3990
|
+
constructor(options) {
|
|
3991
|
+
this.#options = options;
|
|
3992
|
+
}
|
|
3993
|
+
beforeFrame() {
|
|
3994
|
+
const currentState = this.#options.readListState();
|
|
3995
|
+
if (!this.#hasPendingListChange && this.#jumpAnimation == null && this.#lastCommittedState != null && !sameState(this.#lastCommittedState, currentState.position, currentState.offset)) this.#clearAutoFollowLatch();
|
|
3996
|
+
this.#hasPendingListChange = false;
|
|
3997
|
+
}
|
|
3998
|
+
prepare(now) {
|
|
3999
|
+
const animation = this.#jumpAnimation;
|
|
4000
|
+
if (animation == null) return false;
|
|
4001
|
+
if (this.#options.getItemCount() === 0) {
|
|
4002
|
+
this.#cancelJumpAnimation();
|
|
4003
|
+
return false;
|
|
3902
4004
|
}
|
|
3903
|
-
this.#
|
|
3904
|
-
|
|
3905
|
-
|
|
4005
|
+
if (this.#controlledState != null && !sameState(this.#controlledState, this.#options.readListState().position, this.#options.readListState().offset)) {
|
|
4006
|
+
this.#clearAutoFollowLatch();
|
|
4007
|
+
this.#cancelJumpAnimation();
|
|
4008
|
+
return false;
|
|
4009
|
+
}
|
|
4010
|
+
const progress = getProgress(animation.startTime, animation.duration, now);
|
|
4011
|
+
const eased = progress >= 1 ? 1 : smoothstep(progress);
|
|
4012
|
+
const anchor = getAnchorAtDistance(animation.path, animation.path.totalDistance * eased);
|
|
4013
|
+
this.#options.applyAnchor(anchor);
|
|
4014
|
+
animation.needsMoreFrames = progress < 1;
|
|
4015
|
+
return animation.needsMoreFrames;
|
|
3906
4016
|
}
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
for (const item of [...this.#activeReplacementItems]) {
|
|
3914
|
-
if (this.#visibleItems.has(item)) continue;
|
|
3915
|
-
const animation = this.#replacementAnimations.get(item);
|
|
3916
|
-
this.#replacementAnimations.delete(item);
|
|
3917
|
-
this.#activeReplacementItems.delete(item);
|
|
3918
|
-
if (animation?.kind === "delete") adapter.onDeleteComplete(item);
|
|
3919
|
-
changed = true;
|
|
4017
|
+
finishFrame(requestRedraw) {
|
|
4018
|
+
const animation = this.#jumpAnimation;
|
|
4019
|
+
if (animation == null) return requestRedraw;
|
|
4020
|
+
if (animation.needsMoreFrames) {
|
|
4021
|
+
this.#controlledState = this.#options.readListState();
|
|
4022
|
+
return true;
|
|
3920
4023
|
}
|
|
3921
|
-
|
|
4024
|
+
const onComplete = animation.onComplete;
|
|
4025
|
+
this.#cancelJumpAnimation();
|
|
4026
|
+
onComplete?.();
|
|
4027
|
+
return requestRedraw || this.#jumpAnimation != null;
|
|
3922
4028
|
}
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
4029
|
+
commit(state) {
|
|
4030
|
+
this.#lastCommittedState = {
|
|
4031
|
+
position: state.position,
|
|
4032
|
+
offset: state.offset
|
|
4033
|
+
};
|
|
3928
4034
|
}
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
readAnimation(item, now, adapter) {
|
|
3934
|
-
const animation = this.#replacementAnimations.get(item);
|
|
3935
|
-
if (animation == null) return;
|
|
3936
|
-
if (getProgress(animation.startTime, animation.duration, now) >= 1) {
|
|
3937
|
-
this.#replacementAnimations.delete(item);
|
|
3938
|
-
this.#activeReplacementItems.delete(item);
|
|
3939
|
-
if (animation.kind === "delete") adapter?.onDeleteComplete(item);
|
|
4035
|
+
jumpTo(index, options = {}) {
|
|
4036
|
+
this.#clearAutoFollowLatch();
|
|
4037
|
+
if (this.#options.getItemCount() === 0) {
|
|
4038
|
+
this.#cancelJumpAnimation();
|
|
3940
4039
|
return;
|
|
3941
4040
|
}
|
|
3942
|
-
|
|
3943
|
-
}
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
const
|
|
3947
|
-
|
|
3948
|
-
const
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
4041
|
+
this.#startJumpToIndex(index, options, { kind: "manual" });
|
|
4042
|
+
}
|
|
4043
|
+
handleListStateChange(change) {
|
|
4044
|
+
this.#hasPendingListChange = true;
|
|
4045
|
+
const followChange = this.#resolveAutoFollowChange(change);
|
|
4046
|
+
const canChainAutoFollow = followChange != null ? this.#shouldChainAutoFollow(followChange.direction, followChange.animation) : false;
|
|
4047
|
+
const canLatchAutoFollow = followChange != null ? this.#shouldLatchAutoFollow(followChange.direction, followChange.count, followChange.animation) : false;
|
|
4048
|
+
const canSnapshotAutoFollow = followChange != null ? this.#shouldAutoFollowFromSnapshot(followChange.direction, followChange.count, followChange.animation) : false;
|
|
4049
|
+
if (followChange != null && (canSnapshotAutoFollow || canChainAutoFollow || canLatchAutoFollow)) {
|
|
4050
|
+
if (canChainAutoFollow) this.#rebaseJumpAnchorForBoundaryInsert(followChange.direction, followChange.count, getNow());
|
|
4051
|
+
this.#autoFollowLatch = followChange.direction;
|
|
4052
|
+
this.#startJumpToIndex(followChange.direction === "push" ? this.#options.getItemCount() - 1 : 0, {
|
|
4053
|
+
block: followChange.direction === "push" ? "end" : "start",
|
|
4054
|
+
duration: followChange.animation?.duration
|
|
4055
|
+
}, {
|
|
4056
|
+
kind: "auto-follow",
|
|
4057
|
+
direction: followChange.direction
|
|
4058
|
+
});
|
|
3956
4059
|
return {
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
hittest: (test, y) => node.hittest(adapter.getRootContext(), {
|
|
3960
|
-
...test,
|
|
3961
|
-
y: test.y - y
|
|
3962
|
-
})
|
|
3963
|
-
},
|
|
3964
|
-
height: adapter.measureNode(node).height
|
|
4060
|
+
...followChange.change,
|
|
4061
|
+
animation: void 0
|
|
3965
4062
|
};
|
|
3966
4063
|
}
|
|
3967
|
-
|
|
3968
|
-
const layers = this.#readReplacementLayers(replacement, now, adapter.measureNode);
|
|
3969
|
-
return {
|
|
3970
|
-
value: {
|
|
3971
|
-
draw: (y) => this.#drawReplacementLayers(layers, slotHeight, y, adapter),
|
|
3972
|
-
hittest: () => false
|
|
3973
|
-
},
|
|
3974
|
-
height: slotHeight
|
|
3975
|
-
};
|
|
4064
|
+
return change;
|
|
3976
4065
|
}
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
this.handleUpdate(change.prevItem, change.nextItem, change.animation?.duration, ctx);
|
|
3981
|
-
break;
|
|
3982
|
-
case "delete":
|
|
3983
|
-
this.handleDelete(change.item, change.animation?.duration, ctx);
|
|
3984
|
-
break;
|
|
3985
|
-
case "delete-finalize":
|
|
3986
|
-
this.#replacementAnimations.delete(change.item);
|
|
3987
|
-
this.#activeReplacementItems.delete(change.item);
|
|
3988
|
-
break;
|
|
3989
|
-
case "unshift":
|
|
3990
|
-
case "push": break;
|
|
3991
|
-
case "reset":
|
|
3992
|
-
case "set":
|
|
3993
|
-
this.reset();
|
|
3994
|
-
break;
|
|
3995
|
-
}
|
|
4066
|
+
#cancelJumpAnimation() {
|
|
4067
|
+
this.#jumpAnimation = void 0;
|
|
4068
|
+
this.#controlledState = void 0;
|
|
3996
4069
|
}
|
|
3997
|
-
|
|
3998
|
-
const
|
|
3999
|
-
const
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4070
|
+
#startJumpToIndex(index, options, source) {
|
|
4071
|
+
const targetIndex = this.#options.clampItemIndex(index);
|
|
4072
|
+
const currentState = this.#options.normalizeListState(this.#options.readListState());
|
|
4073
|
+
const targetBlock = options.block ?? this.#options.getDefaultJumpBlock();
|
|
4074
|
+
const targetAnchor = this.#options.getTargetAnchor(targetIndex, targetBlock);
|
|
4075
|
+
if (!(options.animated ?? true)) {
|
|
4076
|
+
this.#cancelJumpAnimation();
|
|
4077
|
+
this.#options.applyAnchor(targetAnchor);
|
|
4078
|
+
options.onComplete?.();
|
|
4003
4079
|
return;
|
|
4004
4080
|
}
|
|
4005
|
-
const
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
let fromHeight;
|
|
4012
|
-
if (animation == null || animation.incoming == null) {
|
|
4013
|
-
currentNode = ctx.renderItem(prevItem);
|
|
4014
|
-
fromHeight = ctx.measureNode(currentNode).height;
|
|
4015
|
-
} else {
|
|
4016
|
-
currentNode = animation.incoming.node;
|
|
4017
|
-
currentAlpha = this.#sampleLayerAlpha(animation.incoming, now);
|
|
4018
|
-
fromHeight = this.#sampleReplacementHeight(animation, now);
|
|
4081
|
+
const startAnchor = this.#options.readAnchor(currentState);
|
|
4082
|
+
if (!Number.isFinite(startAnchor)) {
|
|
4083
|
+
this.#cancelJumpAnimation();
|
|
4084
|
+
this.#options.applyAnchor(targetAnchor);
|
|
4085
|
+
options.onComplete?.();
|
|
4086
|
+
return;
|
|
4019
4087
|
}
|
|
4020
|
-
const
|
|
4021
|
-
const
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
incoming,
|
|
4027
|
-
fromHeight,
|
|
4028
|
-
toHeight: nextHeight,
|
|
4029
|
-
startTime: now,
|
|
4030
|
-
duration: normalizedDuration
|
|
4031
|
-
});
|
|
4032
|
-
this.#activeReplacementItems.delete(prevItem);
|
|
4033
|
-
this.#activeReplacementItems.add(nextItem);
|
|
4034
|
-
}
|
|
4035
|
-
handleDelete(item, duration, ctx) {
|
|
4036
|
-
const normalizedDuration = Math.max(0, typeof duration === "number" && Number.isFinite(duration) ? duration : 0);
|
|
4037
|
-
const index = ctx.items.indexOf(item);
|
|
4038
|
-
if (normalizedDuration <= 0 || index < 0 || !this.#canAnimateUpdate(index, item, ctx)) {
|
|
4039
|
-
this.#replacementAnimations.delete(item);
|
|
4040
|
-
this.#activeReplacementItems.delete(item);
|
|
4041
|
-
ctx.onDeleteComplete(item);
|
|
4088
|
+
const path = buildJumpPath(this.#options.getItemCount(), this.#options.getItemHeight, startAnchor, targetAnchor);
|
|
4089
|
+
const duration = clamp$1(options.duration ?? this.#options.minJumpDuration + path.totalDistance * this.#options.jumpDurationPerPixel, 0, this.#options.maxJumpDuration);
|
|
4090
|
+
if (duration <= 0 || path.totalDistance <= Number.EPSILON) {
|
|
4091
|
+
this.#cancelJumpAnimation();
|
|
4092
|
+
this.#options.applyAnchor(targetAnchor);
|
|
4093
|
+
options.onComplete?.();
|
|
4042
4094
|
return;
|
|
4043
4095
|
}
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4096
|
+
this.#jumpAnimation = {
|
|
4097
|
+
path,
|
|
4098
|
+
startTime: getNow(),
|
|
4099
|
+
duration,
|
|
4100
|
+
needsMoreFrames: true,
|
|
4101
|
+
onComplete: options.onComplete,
|
|
4102
|
+
source
|
|
4103
|
+
};
|
|
4104
|
+
this.#controlledState = this.#options.readListState();
|
|
4105
|
+
}
|
|
4106
|
+
#resolveAutoFollowChange(change) {
|
|
4107
|
+
switch (change.type) {
|
|
4108
|
+
case "push":
|
|
4109
|
+
case "unshift": return change.animation?.followIfAtBoundary === true ? {
|
|
4110
|
+
change,
|
|
4111
|
+
direction: change.type,
|
|
4112
|
+
count: change.count,
|
|
4113
|
+
animation: change.animation
|
|
4114
|
+
} : void 0;
|
|
4115
|
+
default: return;
|
|
4063
4116
|
}
|
|
4064
|
-
const outgoing = currentAlpha > .001 ? this.#createLayer(currentNode, currentAlpha, 0, now, normalizedDuration) : void 0;
|
|
4065
|
-
this.#replacementAnimations.set(item, {
|
|
4066
|
-
kind: "delete",
|
|
4067
|
-
outgoing,
|
|
4068
|
-
incoming: void 0,
|
|
4069
|
-
fromHeight,
|
|
4070
|
-
toHeight: 0,
|
|
4071
|
-
startTime: now,
|
|
4072
|
-
duration: normalizedDuration
|
|
4073
|
-
});
|
|
4074
|
-
this.#activeReplacementItems.add(item);
|
|
4075
4117
|
}
|
|
4076
|
-
|
|
4118
|
+
#shouldAutoFollowFromSnapshot(direction, count, animation) {
|
|
4119
|
+
if (animation?.followIfAtBoundary !== true) return false;
|
|
4120
|
+
return this.#options.canAutoFollowBoundaryInsert(direction, count, this.#options.readListState().position, this.#options.readListState().offset);
|
|
4121
|
+
}
|
|
4122
|
+
#shouldLatchAutoFollow(direction, count, animation) {
|
|
4123
|
+
if (animation?.followIfAtBoundary !== true) return false;
|
|
4124
|
+
if (!this.#matchesLastCommittedStateAfterBoundaryInsert(direction, count)) {
|
|
4125
|
+
this.#clearAutoFollowLatch();
|
|
4126
|
+
return false;
|
|
4127
|
+
}
|
|
4128
|
+
return this.#autoFollowLatch === direction;
|
|
4129
|
+
}
|
|
4130
|
+
#shouldChainAutoFollow(direction, animation) {
|
|
4131
|
+
if (animation?.followIfAtBoundary !== true) return false;
|
|
4132
|
+
return this.#jumpAnimation?.source.kind === "auto-follow" ? this.#jumpAnimation.source.direction === direction : false;
|
|
4133
|
+
}
|
|
4134
|
+
#rebaseJumpAnchorForBoundaryInsert(direction, count, now) {
|
|
4135
|
+
const animation = this.#jumpAnimation;
|
|
4136
|
+
if (animation == null) return;
|
|
4137
|
+
const progress = getProgress(animation.startTime, animation.duration, now);
|
|
4138
|
+
const eased = progress >= 1 ? 1 : smoothstep(progress);
|
|
4139
|
+
const anchorAtNow = getAnchorAtDistance(animation.path, animation.path.totalDistance * eased);
|
|
4140
|
+
this.#cancelJumpAnimation();
|
|
4141
|
+
this.#options.applyAnchor(direction === "unshift" ? anchorAtNow + count : anchorAtNow);
|
|
4142
|
+
}
|
|
4143
|
+
#matchesLastCommittedStateAfterBoundaryInsert(direction, count) {
|
|
4144
|
+
const state = this.#lastCommittedState;
|
|
4145
|
+
if (state == null) return false;
|
|
4146
|
+
return sameState({
|
|
4147
|
+
position: direction === "unshift" && state.position != null ? state.position + count : state.position,
|
|
4148
|
+
offset: state.offset
|
|
4149
|
+
}, this.#options.readListState().position, this.#options.readListState().offset);
|
|
4150
|
+
}
|
|
4151
|
+
#clearAutoFollowLatch() {
|
|
4152
|
+
this.#autoFollowLatch = void 0;
|
|
4153
|
+
}
|
|
4154
|
+
};
|
|
4155
|
+
//#endregion
|
|
4156
|
+
//#region src/renderer/virtualized/transition-snapshot.ts
|
|
4157
|
+
var VisibilitySnapshot = class {
|
|
4158
|
+
#drawnItems = /* @__PURE__ */ new Set();
|
|
4159
|
+
#visibleItems = /* @__PURE__ */ new Set();
|
|
4160
|
+
#hasSnapshot = false;
|
|
4161
|
+
#snapshotState;
|
|
4162
|
+
#emptyState;
|
|
4163
|
+
#coversShortList = false;
|
|
4164
|
+
#topGap = 0;
|
|
4165
|
+
#bottomGap = 0;
|
|
4166
|
+
#atStartBoundary = false;
|
|
4167
|
+
#atEndBoundary = false;
|
|
4168
|
+
get coversShortList() {
|
|
4169
|
+
return this.#hasSnapshot && this.#snapshotState != null && this.#coversShortList;
|
|
4170
|
+
}
|
|
4171
|
+
get topGap() {
|
|
4172
|
+
return this.#topGap;
|
|
4173
|
+
}
|
|
4174
|
+
get bottomGap() {
|
|
4175
|
+
return this.#bottomGap;
|
|
4176
|
+
}
|
|
4177
|
+
capture(window, _resolutionPath, items, viewportHeight, snapshotState, extraShift, readVisibleRange) {
|
|
4178
|
+
const nextDrawnItems = /* @__PURE__ */ new Set();
|
|
4179
|
+
const nextVisibleItems = /* @__PURE__ */ new Set();
|
|
4180
|
+
let minVisibleIndex = Number.POSITIVE_INFINITY;
|
|
4181
|
+
let maxVisibleIndex = Number.NEGATIVE_INFINITY;
|
|
4182
|
+
let topMostY = Number.POSITIVE_INFINITY;
|
|
4183
|
+
let bottomMostY = Number.NEGATIVE_INFINITY;
|
|
4184
|
+
const effectiveShift = window.shift + extraShift;
|
|
4185
|
+
for (const { idx, offset, height } of window.drawList) {
|
|
4186
|
+
minVisibleIndex = Math.min(minVisibleIndex, idx);
|
|
4187
|
+
maxVisibleIndex = Math.max(maxVisibleIndex, idx);
|
|
4188
|
+
const y = offset + effectiveShift;
|
|
4189
|
+
topMostY = Math.min(topMostY, y);
|
|
4190
|
+
bottomMostY = Math.max(bottomMostY, y + height);
|
|
4191
|
+
const item = items[idx];
|
|
4192
|
+
if (item != null) nextDrawnItems.add(item);
|
|
4193
|
+
if (item == null || readVisibleRange(y, height) == null) continue;
|
|
4194
|
+
nextVisibleItems.add(item);
|
|
4195
|
+
}
|
|
4196
|
+
this.#drawnItems = nextDrawnItems;
|
|
4197
|
+
this.#visibleItems = nextVisibleItems;
|
|
4198
|
+
this.#hasSnapshot = true;
|
|
4199
|
+
this.#snapshotState = snapshotState;
|
|
4200
|
+
this.#emptyState = items.length === 0 && window.drawList.length === 0 ? snapshotState : void 0;
|
|
4201
|
+
const contentHeight = bottomMostY - topMostY;
|
|
4202
|
+
this.#coversShortList = window.drawList.length > 0 && items.length > 0 && window.drawList.length === items.length && minVisibleIndex === 0 && maxVisibleIndex === items.length - 1 && topMostY >= -Number.EPSILON && bottomMostY <= viewportHeight + Number.EPSILON && contentHeight < viewportHeight - Number.EPSILON;
|
|
4203
|
+
this.#topGap = this.#coversShortList ? Math.max(0, topMostY) : 0;
|
|
4204
|
+
this.#bottomGap = this.#coversShortList ? Math.max(0, viewportHeight - bottomMostY) : 0;
|
|
4205
|
+
this.#atStartBoundary = window.drawList.length > 0 && items.length > 0 && minVisibleIndex === 0 && topMostY >= -Number.EPSILON;
|
|
4206
|
+
this.#atEndBoundary = window.drawList.length > 0 && items.length > 0 && maxVisibleIndex === items.length - 1 && bottomMostY <= viewportHeight + Number.EPSILON;
|
|
4207
|
+
}
|
|
4208
|
+
matchesCurrentState(position, offset) {
|
|
4209
|
+
return this.#hasSnapshot && this.#snapshotState != null && sameState(this.#snapshotState, position, offset);
|
|
4210
|
+
}
|
|
4211
|
+
matchesBoundaryInsertState(direction, count, position, offset) {
|
|
4212
|
+
if (!this.coversShortList || this.#snapshotState == null) return false;
|
|
4213
|
+
return this.#matchesStateAfterBoundaryInsert(direction, count, position, offset);
|
|
4214
|
+
}
|
|
4215
|
+
matchesFollowBoundaryInsertState(direction, count, position, offset) {
|
|
4216
|
+
if (!this.#hasSnapshot || this.#snapshotState == null) return false;
|
|
4217
|
+
if (direction === "push" ? !this.#atEndBoundary : !this.#atStartBoundary) return false;
|
|
4218
|
+
return this.#matchesStateAfterBoundaryInsert(direction, count, position, offset);
|
|
4219
|
+
}
|
|
4220
|
+
matchesEmptyBoundaryInsertState(direction, count, position, offset) {
|
|
4221
|
+
const emptyState = this.#emptyState;
|
|
4222
|
+
if (!this.#hasSnapshot || emptyState == null) return false;
|
|
4223
|
+
return sameState({
|
|
4224
|
+
position: direction === "unshift" && emptyState.position != null ? emptyState.position + count : emptyState.position,
|
|
4225
|
+
offset: emptyState.offset
|
|
4226
|
+
}, position, offset);
|
|
4227
|
+
}
|
|
4228
|
+
isVisible(item) {
|
|
4229
|
+
return this.#visibleItems.has(item);
|
|
4230
|
+
}
|
|
4231
|
+
tracks(item, retention) {
|
|
4232
|
+
return retention === "drawn" ? this.#drawnItems.has(item) : this.#visibleItems.has(item);
|
|
4233
|
+
}
|
|
4077
4234
|
reset() {
|
|
4078
|
-
this.#
|
|
4079
|
-
this.#activeReplacementItems.clear();
|
|
4235
|
+
this.#drawnItems.clear();
|
|
4080
4236
|
this.#visibleItems.clear();
|
|
4081
|
-
this.#
|
|
4082
|
-
this.#
|
|
4237
|
+
this.#hasSnapshot = false;
|
|
4238
|
+
this.#snapshotState = void 0;
|
|
4239
|
+
this.#emptyState = void 0;
|
|
4240
|
+
this.#coversShortList = false;
|
|
4241
|
+
this.#topGap = 0;
|
|
4242
|
+
this.#bottomGap = 0;
|
|
4243
|
+
this.#atStartBoundary = false;
|
|
4244
|
+
this.#atEndBoundary = false;
|
|
4245
|
+
}
|
|
4246
|
+
#matchesStateAfterBoundaryInsert(direction, count, position, offset) {
|
|
4247
|
+
const snapshotState = this.#snapshotState;
|
|
4248
|
+
if (snapshotState == null) return false;
|
|
4249
|
+
return sameState({
|
|
4250
|
+
position: direction === "unshift" && snapshotState.position != null ? snapshotState.position + count : snapshotState.position,
|
|
4251
|
+
offset: snapshotState.offset
|
|
4252
|
+
}, position, offset);
|
|
4253
|
+
}
|
|
4254
|
+
};
|
|
4255
|
+
//#endregion
|
|
4256
|
+
//#region src/renderer/virtualized/transition-store.ts
|
|
4257
|
+
var TransitionStore = class {
|
|
4258
|
+
#transitions = /* @__PURE__ */ new Map();
|
|
4259
|
+
get size() {
|
|
4260
|
+
return this.#transitions.size;
|
|
4261
|
+
}
|
|
4262
|
+
has(item) {
|
|
4263
|
+
return this.#transitions.has(item);
|
|
4264
|
+
}
|
|
4265
|
+
set(item, transition) {
|
|
4266
|
+
this.#transitions.set(item, transition);
|
|
4267
|
+
}
|
|
4268
|
+
replace(prevItem, nextItem, transition) {
|
|
4269
|
+
this.#transitions.delete(prevItem);
|
|
4270
|
+
this.#transitions.set(nextItem, transition);
|
|
4271
|
+
}
|
|
4272
|
+
delete(item) {
|
|
4273
|
+
const transition = this.#transitions.get(item);
|
|
4274
|
+
if (transition != null) this.#transitions.delete(item);
|
|
4275
|
+
return transition;
|
|
4276
|
+
}
|
|
4277
|
+
readActive(item, now) {
|
|
4278
|
+
const transition = this.#transitions.get(item);
|
|
4279
|
+
if (transition == null) return;
|
|
4280
|
+
return this.#isComplete(transition, now) ? void 0 : transition;
|
|
4281
|
+
}
|
|
4282
|
+
prepare(now) {
|
|
4283
|
+
for (const transition of this.#transitions.values()) if (!this.#isComplete(transition, now)) return true;
|
|
4284
|
+
return false;
|
|
4285
|
+
}
|
|
4286
|
+
findCompleted(now) {
|
|
4287
|
+
return [...this.#transitions.entries()].filter(([, transition]) => this.#isComplete(transition, now)).map(([item, transition]) => ({
|
|
4288
|
+
item,
|
|
4289
|
+
transition
|
|
4290
|
+
}));
|
|
4291
|
+
}
|
|
4292
|
+
findInvisible(snapshot) {
|
|
4293
|
+
return [...this.#transitions.entries()].filter(([item, transition]) => !snapshot.tracks(item, transition.retention)).map(([item, transition]) => ({
|
|
4294
|
+
item,
|
|
4295
|
+
transition
|
|
4296
|
+
}));
|
|
4297
|
+
}
|
|
4298
|
+
reset() {
|
|
4299
|
+
this.#transitions.clear();
|
|
4300
|
+
}
|
|
4301
|
+
#isComplete(transition, now) {
|
|
4302
|
+
return getProgress(transition.height.startTime, transition.height.duration, now) >= 1;
|
|
4303
|
+
}
|
|
4304
|
+
};
|
|
4305
|
+
//#endregion
|
|
4306
|
+
//#region src/renderer/virtualized/transition-planner.ts
|
|
4307
|
+
function isFinitePositive(value) {
|
|
4308
|
+
return Number.isFinite(value) && value > 0;
|
|
4309
|
+
}
|
|
4310
|
+
function normalizeDuration(duration) {
|
|
4311
|
+
return Math.max(0, typeof duration === "number" && Number.isFinite(duration) ? duration : 0);
|
|
4312
|
+
}
|
|
4313
|
+
function createScalarAnimation(from, to, startTime, duration) {
|
|
4314
|
+
return {
|
|
4315
|
+
from,
|
|
4316
|
+
to,
|
|
4317
|
+
startTime,
|
|
4318
|
+
duration
|
|
4319
|
+
};
|
|
4320
|
+
}
|
|
4321
|
+
function createLayerAnimation(node, fromAlpha, toAlpha, startTime, duration, fromTranslateY, toTranslateY) {
|
|
4322
|
+
return {
|
|
4323
|
+
node,
|
|
4324
|
+
alpha: createScalarAnimation(fromAlpha, toAlpha, startTime, duration),
|
|
4325
|
+
translateY: createScalarAnimation(fromTranslateY, toTranslateY, startTime, duration)
|
|
4326
|
+
};
|
|
4327
|
+
}
|
|
4328
|
+
function findVisibleEntry(index, resolveVisibleWindow, readVisibleRange) {
|
|
4329
|
+
if (index < 0) return;
|
|
4330
|
+
const solution = resolveVisibleWindow();
|
|
4331
|
+
for (const entry of solution.window.drawList) {
|
|
4332
|
+
if (entry.idx !== index) continue;
|
|
4333
|
+
if (readVisibleRange(entry.offset + solution.window.shift, entry.height) != null) return entry;
|
|
4083
4334
|
}
|
|
4084
|
-
|
|
4335
|
+
}
|
|
4336
|
+
function isIndexVisible(index, resolveVisibleWindow, readVisibleRange) {
|
|
4337
|
+
return findVisibleEntry(index, resolveVisibleWindow, readVisibleRange) != null;
|
|
4338
|
+
}
|
|
4339
|
+
function resolveAnimationEligibility(params) {
|
|
4340
|
+
if (params.index < 0) return false;
|
|
4341
|
+
if (params.snapshot.matchesCurrentState(params.position, params.offset)) return params.snapshot.isVisible(params.item);
|
|
4342
|
+
return isIndexVisible(params.index, params.resolveVisibleWindow, params.readVisibleRange);
|
|
4343
|
+
}
|
|
4344
|
+
function resolveBoundaryInsertStrategy(direction, underflowAlign, coversShortListSnapshot) {
|
|
4345
|
+
if (!coversShortListSnapshot) return "hard-cut";
|
|
4346
|
+
if (direction === "push" && underflowAlign === "bottom" || direction === "unshift" && underflowAlign === "top") return "viewport-slide";
|
|
4347
|
+
return "item-enter";
|
|
4348
|
+
}
|
|
4349
|
+
function sampleScalarAnimation(animation, now) {
|
|
4350
|
+
return interpolate(animation.from, animation.to, animation.startTime, animation.duration, now);
|
|
4351
|
+
}
|
|
4352
|
+
function sampleLayerAnimation(layer, now) {
|
|
4353
|
+
const alpha = sampleScalarAnimation(layer.alpha, now);
|
|
4354
|
+
if (alpha <= .001) return;
|
|
4355
|
+
return {
|
|
4356
|
+
alpha,
|
|
4357
|
+
node: layer.node,
|
|
4358
|
+
translateY: sampleScalarAnimation(layer.translateY, now)
|
|
4359
|
+
};
|
|
4360
|
+
}
|
|
4361
|
+
function sampleTransition(transition, now) {
|
|
4362
|
+
return {
|
|
4363
|
+
kind: transition.kind,
|
|
4364
|
+
slotHeight: sampleScalarAnimation(transition.height, now),
|
|
4365
|
+
layers: transition.layers.map((layer) => sampleLayerAnimation(layer, now)).filter((layer) => layer != null),
|
|
4366
|
+
retention: transition.retention
|
|
4367
|
+
};
|
|
4368
|
+
}
|
|
4369
|
+
function planExistingItemTransition(params) {
|
|
4370
|
+
if (!params.canAnimate || params.duration <= 0) return;
|
|
4371
|
+
if (params.kind === "update" && !Number.isFinite(params.nextHeight)) return;
|
|
4372
|
+
const layers = [];
|
|
4373
|
+
if (params.currentVisualState.alpha > .001) layers.push(createLayerAnimation(params.currentVisualState.node, params.currentVisualState.alpha, 0, params.now, params.duration, params.currentVisualState.translateY, 0));
|
|
4374
|
+
if (params.kind === "update") {
|
|
4375
|
+
layers.push(createLayerAnimation(params.nextNode, 0, 1, params.now, params.duration, params.currentVisualState.translateY, 0));
|
|
4085
4376
|
return {
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
duration
|
|
4377
|
+
kind: "update",
|
|
4378
|
+
layers,
|
|
4379
|
+
height: createScalarAnimation(params.currentVisualState.height, params.nextHeight, params.now, params.duration),
|
|
4380
|
+
retention: "visible"
|
|
4091
4381
|
};
|
|
4092
4382
|
}
|
|
4093
|
-
|
|
4094
|
-
|
|
4383
|
+
return {
|
|
4384
|
+
kind: "delete",
|
|
4385
|
+
layers,
|
|
4386
|
+
height: createScalarAnimation(params.currentVisualState.height, 0, params.now, params.duration),
|
|
4387
|
+
retention: "visible"
|
|
4388
|
+
};
|
|
4389
|
+
}
|
|
4390
|
+
function planViewportShift(params) {
|
|
4391
|
+
if (!isFinitePositive(params.travel) || params.duration <= 0) return;
|
|
4392
|
+
return createScalarAnimation(params.direction === "positive" ? params.currentTranslateY + params.travel : params.currentTranslateY - params.travel, 0, params.now, params.duration);
|
|
4393
|
+
}
|
|
4394
|
+
function planBoundaryInsert(params) {
|
|
4395
|
+
switch (params.strategy) {
|
|
4396
|
+
case "hard-cut": return;
|
|
4397
|
+
case "item-enter": return planBoundaryInsertItems(params);
|
|
4398
|
+
case "viewport-slide": return planBoundaryInsertViewportShift(params);
|
|
4095
4399
|
}
|
|
4096
|
-
|
|
4097
|
-
|
|
4400
|
+
}
|
|
4401
|
+
function planBoundaryInsertItems(params) {
|
|
4402
|
+
const entries = [];
|
|
4403
|
+
const signedDistance = params.direction === "push" ? 1 : -1;
|
|
4404
|
+
for (const { item, node, height } of params.measuredItems) {
|
|
4405
|
+
if (!Number.isFinite(height) || height < 0) return;
|
|
4406
|
+
const resolvedDistance = typeof params.distance === "number" && Number.isFinite(params.distance) ? Math.max(0, params.distance) : Math.min(24, height);
|
|
4407
|
+
entries.push({
|
|
4408
|
+
item,
|
|
4409
|
+
transition: {
|
|
4410
|
+
kind: "insert",
|
|
4411
|
+
layers: [createLayerAnimation(node, 0, 1, params.now, params.duration, signedDistance * resolvedDistance, 0)],
|
|
4412
|
+
height: createScalarAnimation(height, height, params.now, params.duration),
|
|
4413
|
+
retention: "drawn"
|
|
4414
|
+
}
|
|
4415
|
+
});
|
|
4098
4416
|
}
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4417
|
+
return entries.length === 0 ? void 0 : {
|
|
4418
|
+
kind: "item-enter",
|
|
4419
|
+
entries
|
|
4420
|
+
};
|
|
4421
|
+
}
|
|
4422
|
+
function planBoundaryInsertViewportShift(params) {
|
|
4423
|
+
let insertedHeight = 0;
|
|
4424
|
+
for (const { height } of params.measuredItems) {
|
|
4425
|
+
if (!Number.isFinite(height) || height <= 0) return;
|
|
4426
|
+
insertedHeight += height;
|
|
4427
|
+
}
|
|
4428
|
+
if (!isFinitePositive(insertedHeight)) return;
|
|
4429
|
+
const gap = params.direction === "push" ? params.snapshot.topGap : params.snapshot.bottomGap;
|
|
4430
|
+
const travel = Math.min(insertedHeight, gap);
|
|
4431
|
+
const animation = planViewportShift({
|
|
4432
|
+
currentTranslateY: params.currentTranslateY,
|
|
4433
|
+
travel,
|
|
4434
|
+
direction: params.direction === "push" ? "positive" : "negative",
|
|
4435
|
+
now: params.now,
|
|
4436
|
+
duration: params.duration
|
|
4437
|
+
});
|
|
4438
|
+
return animation == null ? void 0 : {
|
|
4439
|
+
kind: "viewport-slide",
|
|
4440
|
+
animation
|
|
4441
|
+
};
|
|
4442
|
+
}
|
|
4443
|
+
function measureBoundaryInsertItems(direction, count, ctx) {
|
|
4444
|
+
const start = direction === "push" ? ctx.items.length - count : 0;
|
|
4445
|
+
const end = direction === "push" ? ctx.items.length : Math.min(count, ctx.items.length);
|
|
4446
|
+
if (start < 0 || end < start) return;
|
|
4447
|
+
const measured = [];
|
|
4448
|
+
for (let index = start; index < end; index += 1) {
|
|
4449
|
+
const item = ctx.items[index];
|
|
4450
|
+
if (item == null) continue;
|
|
4451
|
+
const node = ctx.renderItem(item);
|
|
4452
|
+
const height = ctx.measureNode(node).height;
|
|
4453
|
+
measured.push({
|
|
4454
|
+
item,
|
|
4455
|
+
node,
|
|
4456
|
+
height
|
|
4457
|
+
});
|
|
4105
4458
|
}
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4459
|
+
return measured;
|
|
4460
|
+
}
|
|
4461
|
+
function drawSampledLayers(sampled, y, adapter) {
|
|
4462
|
+
if (sampled.slotHeight <= 0) return false;
|
|
4463
|
+
let result = false;
|
|
4464
|
+
for (const layer of sampled.layers) {
|
|
4465
|
+
const alpha = clamp$1(layer.alpha, 0, 1);
|
|
4466
|
+
if (alpha <= .001) continue;
|
|
4467
|
+
adapter.graphics.save();
|
|
4468
|
+
try {
|
|
4469
|
+
if (typeof adapter.graphics.globalAlpha === "number") adapter.graphics.globalAlpha *= alpha;
|
|
4470
|
+
if (adapter.drawNode(layer.node, 0, y + layer.translateY)) result = true;
|
|
4471
|
+
} finally {
|
|
4472
|
+
adapter.graphics.restore();
|
|
4473
|
+
}
|
|
4474
|
+
}
|
|
4475
|
+
return result;
|
|
4476
|
+
}
|
|
4477
|
+
function planUpdateTransition(prevItem, nextItem, duration, now, currentVisualState, ctx, snapshot, store) {
|
|
4478
|
+
const nextIndex = ctx.items.indexOf(nextItem);
|
|
4479
|
+
const nextNode = ctx.renderItem(nextItem);
|
|
4480
|
+
const nextHeight = ctx.measureNode(nextNode).height;
|
|
4481
|
+
return planExistingItemTransition({
|
|
4482
|
+
kind: "update",
|
|
4483
|
+
duration: normalizeDuration(duration),
|
|
4484
|
+
canAnimate: resolveAnimationEligibility({
|
|
4485
|
+
index: nextIndex,
|
|
4486
|
+
item: prevItem,
|
|
4487
|
+
position: ctx.position,
|
|
4488
|
+
offset: ctx.offset,
|
|
4489
|
+
snapshot,
|
|
4490
|
+
hasActiveTransition: store.has(prevItem),
|
|
4491
|
+
resolveVisibleWindow: ctx.resolveVisibleWindow,
|
|
4492
|
+
readVisibleRange: ctx.readVisibleRange
|
|
4493
|
+
}),
|
|
4494
|
+
now,
|
|
4495
|
+
currentVisualState,
|
|
4496
|
+
nextNode,
|
|
4497
|
+
nextHeight
|
|
4498
|
+
});
|
|
4499
|
+
}
|
|
4500
|
+
function planDeleteTransition(item, duration, now, currentVisualState, ctx, snapshot, store) {
|
|
4501
|
+
const index = ctx.items.indexOf(item);
|
|
4502
|
+
return planExistingItemTransition({
|
|
4503
|
+
kind: "delete",
|
|
4504
|
+
duration: normalizeDuration(duration),
|
|
4505
|
+
canAnimate: resolveAnimationEligibility({
|
|
4506
|
+
index,
|
|
4507
|
+
item,
|
|
4508
|
+
position: ctx.position,
|
|
4509
|
+
offset: ctx.offset,
|
|
4510
|
+
snapshot,
|
|
4511
|
+
hasActiveTransition: store.has(item),
|
|
4512
|
+
resolveVisibleWindow: ctx.resolveVisibleWindow,
|
|
4513
|
+
readVisibleRange: ctx.readVisibleRange
|
|
4514
|
+
}),
|
|
4515
|
+
now,
|
|
4516
|
+
currentVisualState
|
|
4517
|
+
});
|
|
4518
|
+
}
|
|
4519
|
+
function planBoundaryInsertTransition(direction, count, duration, distance, now, currentTranslateY, ctx, snapshot) {
|
|
4520
|
+
const normalizedDuration = normalizeDuration(duration);
|
|
4521
|
+
if (count <= 0 || normalizedDuration <= 0) return;
|
|
4522
|
+
const strategy = snapshot.matchesBoundaryInsertState(direction, count, ctx.position, ctx.offset) ? resolveBoundaryInsertStrategy(direction, ctx.underflowAlign, true) : snapshot.matchesEmptyBoundaryInsertState(direction, count, ctx.position, ctx.offset) ? "item-enter" : "hard-cut";
|
|
4523
|
+
if (strategy === "hard-cut") return;
|
|
4524
|
+
const measuredItems = measureBoundaryInsertItems(direction, count, ctx);
|
|
4525
|
+
if (measuredItems == null) return;
|
|
4526
|
+
return planBoundaryInsert({
|
|
4527
|
+
direction,
|
|
4528
|
+
duration: normalizedDuration,
|
|
4529
|
+
distance,
|
|
4530
|
+
now,
|
|
4531
|
+
strategy,
|
|
4532
|
+
snapshot,
|
|
4533
|
+
currentTranslateY,
|
|
4534
|
+
measuredItems
|
|
4535
|
+
});
|
|
4536
|
+
}
|
|
4537
|
+
function getTransitionedItemHeight(item, now, store, adapter) {
|
|
4538
|
+
const transition = store.readActive(item, now);
|
|
4539
|
+
if (transition != null) return sampleTransition(transition, now).slotHeight;
|
|
4540
|
+
const node = adapter.renderItem(item);
|
|
4541
|
+
return adapter.measureNode(node).height;
|
|
4542
|
+
}
|
|
4543
|
+
function resolveTransitionedItem(item, now, store, adapter, lifecycle) {
|
|
4544
|
+
const transition = store.readActive(item, now);
|
|
4545
|
+
if (transition == null) {
|
|
4546
|
+
const node = adapter.renderItem(item);
|
|
4547
|
+
return {
|
|
4548
|
+
value: {
|
|
4549
|
+
draw: (y) => adapter.drawNode(node, 0, y),
|
|
4550
|
+
hittest: (test, y) => node.hittest(adapter.getRootContext(), {
|
|
4551
|
+
...test,
|
|
4552
|
+
y: test.y - y
|
|
4553
|
+
})
|
|
4554
|
+
},
|
|
4555
|
+
height: adapter.measureNode(node).height
|
|
4556
|
+
};
|
|
4557
|
+
}
|
|
4558
|
+
const sampled = sampleTransition(transition, now);
|
|
4559
|
+
return {
|
|
4560
|
+
value: {
|
|
4561
|
+
draw: (y) => drawSampledLayers(sampled, y, adapter),
|
|
4562
|
+
hittest: () => false
|
|
4563
|
+
},
|
|
4564
|
+
height: sampled.slotHeight
|
|
4565
|
+
};
|
|
4566
|
+
}
|
|
4567
|
+
function readCurrentVisualState(item, now, store, adapter) {
|
|
4568
|
+
const transition = store.readActive(item, now);
|
|
4569
|
+
if (transition != null && transition.layers.length > 0) {
|
|
4570
|
+
const primaryLayer = transition.layers[transition.layers.length - 1];
|
|
4571
|
+
return {
|
|
4572
|
+
node: primaryLayer.node,
|
|
4573
|
+
alpha: sampleScalarAnimation(primaryLayer.alpha, now),
|
|
4574
|
+
height: sampleScalarAnimation(transition.height, now),
|
|
4575
|
+
translateY: sampleScalarAnimation(primaryLayer.translateY, now)
|
|
4576
|
+
};
|
|
4577
|
+
}
|
|
4578
|
+
const node = adapter.renderItem(item);
|
|
4579
|
+
return {
|
|
4580
|
+
node,
|
|
4581
|
+
alpha: 1,
|
|
4582
|
+
height: adapter.measureNode(node).height,
|
|
4583
|
+
translateY: 0
|
|
4584
|
+
};
|
|
4585
|
+
}
|
|
4586
|
+
function handleTransitionStateChange(store, snapshot, currentViewportTranslateY, change, ctx, lifecycle) {
|
|
4587
|
+
switch (change.type) {
|
|
4588
|
+
case "update": {
|
|
4589
|
+
const now = getNow();
|
|
4590
|
+
const currentVisualState = readCurrentVisualState(change.prevItem, now, store, ctx);
|
|
4591
|
+
const transition = planUpdateTransition(change.prevItem, change.nextItem, change.animation?.duration, now, currentVisualState, ctx, snapshot, store);
|
|
4592
|
+
if (transition == null) {
|
|
4593
|
+
store.delete(change.prevItem);
|
|
4594
|
+
return {};
|
|
4123
4595
|
}
|
|
4596
|
+
store.replace(change.prevItem, change.nextItem, transition);
|
|
4597
|
+
return {};
|
|
4124
4598
|
}
|
|
4125
|
-
|
|
4599
|
+
case "delete": {
|
|
4600
|
+
const now = getNow();
|
|
4601
|
+
const currentVisualState = readCurrentVisualState(change.item, now, store, ctx);
|
|
4602
|
+
const transition = planDeleteTransition(change.item, change.animation?.duration, now, currentVisualState, ctx, snapshot, store);
|
|
4603
|
+
if (transition == null) {
|
|
4604
|
+
store.delete(change.item);
|
|
4605
|
+
lifecycle.onDeleteComplete(change.item);
|
|
4606
|
+
return {};
|
|
4607
|
+
}
|
|
4608
|
+
store.set(change.item, transition);
|
|
4609
|
+
return {};
|
|
4610
|
+
}
|
|
4611
|
+
case "delete-finalize":
|
|
4612
|
+
store.delete(change.item);
|
|
4613
|
+
return {};
|
|
4614
|
+
case "unshift":
|
|
4615
|
+
case "push": {
|
|
4616
|
+
const now = getNow();
|
|
4617
|
+
const plan = planBoundaryInsertTransition(change.type, change.count, change.animation?.duration, change.animation?.distance, now, currentViewportTranslateY, ctx, snapshot);
|
|
4618
|
+
if (plan == null) return {};
|
|
4619
|
+
if (plan.kind === "viewport-slide") return { viewportAnimation: plan.animation };
|
|
4620
|
+
for (const entry of plan.entries) store.set(entry.item, entry.transition);
|
|
4621
|
+
return {};
|
|
4622
|
+
}
|
|
4623
|
+
case "reset":
|
|
4624
|
+
case "set":
|
|
4625
|
+
store.reset();
|
|
4626
|
+
snapshot.reset();
|
|
4627
|
+
return {};
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
//#endregion
|
|
4631
|
+
//#region src/renderer/virtualized/base-transition.ts
|
|
4632
|
+
var TransitionController = class {
|
|
4633
|
+
#store = new TransitionStore();
|
|
4634
|
+
#snapshot = new VisibilitySnapshot();
|
|
4635
|
+
#viewportTranslateAnimation;
|
|
4636
|
+
captureVisibilitySnapshot(window, resolutionPath, items, viewportHeight, snapshotState, extraShift, readVisibleRange) {
|
|
4637
|
+
this.#snapshot.capture(window, resolutionPath, items, viewportHeight, snapshotState, extraShift, readVisibleRange);
|
|
4638
|
+
}
|
|
4639
|
+
pruneInvisible(lifecycle) {
|
|
4640
|
+
return this.pruneInvisibleAt(getNow(), lifecycle);
|
|
4641
|
+
}
|
|
4642
|
+
prepare(now, lifecycle) {
|
|
4643
|
+
this.settle(now, lifecycle);
|
|
4644
|
+
this.#cleanupViewportTranslateAnimation(now);
|
|
4645
|
+
const keepViewportAnimating = this.#viewportTranslateAnimation != null;
|
|
4646
|
+
return this.#store.prepare(now) || keepViewportAnimating;
|
|
4647
|
+
}
|
|
4648
|
+
getViewportTranslateY(now) {
|
|
4649
|
+
this.#cleanupViewportTranslateAnimation(now);
|
|
4650
|
+
return this.#viewportTranslateAnimation == null ? 0 : sampleScalarAnimation(this.#viewportTranslateAnimation, now);
|
|
4651
|
+
}
|
|
4652
|
+
canAutoFollowBoundaryInsert(direction, count, position, offset) {
|
|
4653
|
+
return this.#snapshot.matchesFollowBoundaryInsertState(direction, count, position, offset);
|
|
4126
4654
|
}
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4655
|
+
getItemHeight(item, now, adapter) {
|
|
4656
|
+
return getTransitionedItemHeight(item, now, this.#store, adapter);
|
|
4657
|
+
}
|
|
4658
|
+
resolveItem(item, now, adapter, lifecycle) {
|
|
4659
|
+
return resolveTransitionedItem(item, now, this.#store, adapter, lifecycle);
|
|
4660
|
+
}
|
|
4661
|
+
handleListStateChange(change, ctx, lifecycle) {
|
|
4662
|
+
const now = getNow();
|
|
4663
|
+
this.settle(now, lifecycle);
|
|
4664
|
+
const result = handleTransitionStateChange(this.#store, this.#snapshot, this.getViewportTranslateY(now), change, ctx, lifecycle);
|
|
4665
|
+
if (change.type === "reset" || change.type === "set") {
|
|
4666
|
+
this.#viewportTranslateAnimation = void 0;
|
|
4667
|
+
return;
|
|
4133
4668
|
}
|
|
4134
|
-
|
|
4669
|
+
if (result.viewportAnimation != null) this.#viewportTranslateAnimation = result.viewportAnimation;
|
|
4135
4670
|
}
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
return
|
|
4671
|
+
settle(now, lifecycle) {
|
|
4672
|
+
const changed = this.#settleTransitions(this.#store.findCompleted(now), now, lifecycle);
|
|
4673
|
+
this.#cleanupViewportTranslateAnimation(now);
|
|
4674
|
+
return changed;
|
|
4675
|
+
}
|
|
4676
|
+
pruneInvisibleAt(now, lifecycle) {
|
|
4677
|
+
return this.#settleTransitions(this.#store.findInvisible(this.#snapshot), now, lifecycle);
|
|
4678
|
+
}
|
|
4679
|
+
reset() {
|
|
4680
|
+
this.#store.reset();
|
|
4681
|
+
this.#snapshot.reset();
|
|
4682
|
+
this.#viewportTranslateAnimation = void 0;
|
|
4683
|
+
}
|
|
4684
|
+
#cleanupViewportTranslateAnimation(now) {
|
|
4685
|
+
const animation = this.#viewportTranslateAnimation;
|
|
4686
|
+
if (animation == null) return;
|
|
4687
|
+
if (getProgress(animation.startTime, animation.duration, now) >= 1) this.#viewportTranslateAnimation = void 0;
|
|
4688
|
+
}
|
|
4689
|
+
#settleTransitions(removals, now, lifecycle) {
|
|
4690
|
+
if (removals.length === 0) return false;
|
|
4691
|
+
const anchor = lifecycle.captureVisualAnchor(now);
|
|
4692
|
+
for (const { item, transition } of removals) {
|
|
4693
|
+
this.#store.delete(item);
|
|
4694
|
+
if (transition.kind === "delete") lifecycle.onDeleteComplete(item);
|
|
4695
|
+
}
|
|
4696
|
+
if (anchor != null && Number.isFinite(anchor)) lifecycle.restoreVisualAnchor(anchor);
|
|
4697
|
+
return true;
|
|
4140
4698
|
}
|
|
4141
4699
|
};
|
|
4142
4700
|
//#endregion
|
|
@@ -4147,12 +4705,26 @@ var ReplacementController = class {
|
|
|
4147
4705
|
var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
4148
4706
|
static MIN_JUMP_DURATION = 160;
|
|
4149
4707
|
static MAX_JUMP_DURATION = 420;
|
|
4150
|
-
static
|
|
4151
|
-
#
|
|
4152
|
-
#
|
|
4153
|
-
#replacementController = new ReplacementController();
|
|
4708
|
+
static JUMP_DURATION_PER_PIXEL = .7;
|
|
4709
|
+
#jumpController;
|
|
4710
|
+
#transitionController = new TransitionController();
|
|
4154
4711
|
constructor(graphics, options) {
|
|
4155
4712
|
super(graphics, options);
|
|
4713
|
+
this.#jumpController = new JumpController({
|
|
4714
|
+
minJumpDuration: VirtualizedRenderer.MIN_JUMP_DURATION,
|
|
4715
|
+
maxJumpDuration: VirtualizedRenderer.MAX_JUMP_DURATION,
|
|
4716
|
+
jumpDurationPerPixel: VirtualizedRenderer.JUMP_DURATION_PER_PIXEL,
|
|
4717
|
+
getItemCount: () => this.items.length,
|
|
4718
|
+
readListState: this._readListState.bind(this),
|
|
4719
|
+
normalizeListState: this._normalizeListState.bind(this),
|
|
4720
|
+
readAnchor: (state) => this._readAnchor(state, this._getItemHeight.bind(this)),
|
|
4721
|
+
applyAnchor: this._applyAnchor.bind(this),
|
|
4722
|
+
getDefaultJumpBlock: this._getDefaultJumpBlock.bind(this),
|
|
4723
|
+
getTargetAnchor: this._getTargetAnchor.bind(this),
|
|
4724
|
+
clampItemIndex: this._clampItemIndex.bind(this),
|
|
4725
|
+
getItemHeight: this._getItemHeight.bind(this),
|
|
4726
|
+
canAutoFollowBoundaryInsert: (direction, count, position, offset) => this.#transitionController.canAutoFollowBoundaryInsert(direction, count, position, offset)
|
|
4727
|
+
});
|
|
4156
4728
|
subscribeListState(options.list, this, (owner, change) => {
|
|
4157
4729
|
owner.#handleListStateChange(change);
|
|
4158
4730
|
});
|
|
@@ -4183,30 +4755,35 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
4183
4755
|
}
|
|
4184
4756
|
/** Renders the current visible window. */
|
|
4185
4757
|
render(feedback) {
|
|
4758
|
+
this.#jumpController.beforeFrame();
|
|
4186
4759
|
const now = getNow();
|
|
4187
4760
|
const keepAnimating = this._prepareRender(now);
|
|
4188
4761
|
const { clientWidth: viewportWidth, clientHeight: viewportHeight } = this.graphics.canvas;
|
|
4189
4762
|
this.graphics.clearRect(0, 0, viewportWidth, viewportHeight);
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
solution
|
|
4195
|
-
this.
|
|
4196
|
-
}
|
|
4197
|
-
const requestRedraw = this._renderVisibleWindow(solution.window, feedback);
|
|
4198
|
-
this._commitListState(solution.normalizedState);
|
|
4199
|
-
return this._finishRender(keepAnimating || requestRedraw || requestSettleRedraw);
|
|
4763
|
+
const frame = prepareFrameSession({
|
|
4764
|
+
now,
|
|
4765
|
+
resolveVisibleWindow: (frameNow) => this._resolveVisibleWindow(frameNow),
|
|
4766
|
+
getViewportTranslateY: (frameNow) => this.#transitionController.getViewportTranslateY(frameNow),
|
|
4767
|
+
captureVisibleItemSnapshot: (solution, extraShift) => this._captureVisibleItemSnapshot(solution, extraShift),
|
|
4768
|
+
pruneTransitionAnimations: (window, frameNow) => this._pruneTransitionAnimations(window, frameNow)
|
|
4769
|
+
});
|
|
4770
|
+
const requestRedraw = this._renderVisibleWindow(frame.solution.window, feedback, frame.viewportTranslateY);
|
|
4771
|
+
this._commitListState(frame.solution.normalizedState);
|
|
4772
|
+
return this._finishRender(keepAnimating || requestRedraw || frame.requestSettleRedraw);
|
|
4200
4773
|
}
|
|
4201
4774
|
/** Hit-tests the current visible window. */
|
|
4202
4775
|
hittest(test) {
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4776
|
+
this.#jumpController.beforeFrame();
|
|
4777
|
+
const now = getNow();
|
|
4778
|
+
this.#transitionController.settle(now, this.#getTransitionLifecycleAdapter());
|
|
4779
|
+
const frame = prepareFrameSession({
|
|
4780
|
+
now,
|
|
4781
|
+
resolveVisibleWindow: (frameNow) => this._resolveVisibleWindow(frameNow),
|
|
4782
|
+
getViewportTranslateY: (frameNow) => this.#transitionController.getViewportTranslateY(frameNow),
|
|
4783
|
+
captureVisibleItemSnapshot: (solution, extraShift) => this._captureVisibleItemSnapshot(solution, extraShift),
|
|
4784
|
+
pruneTransitionAnimations: (window, frameNow) => this._pruneTransitionAnimations(window, frameNow)
|
|
4785
|
+
});
|
|
4786
|
+
return this._hittestVisibleWindow(frame.solution.window, test, frame.viewportTranslateY);
|
|
4210
4787
|
}
|
|
4211
4788
|
_readListState() {
|
|
4212
4789
|
return {
|
|
@@ -4214,51 +4791,19 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
4214
4791
|
offset: this.offset
|
|
4215
4792
|
};
|
|
4216
4793
|
}
|
|
4794
|
+
_resolveVisibleWindow(now) {
|
|
4795
|
+
return this._resolveVisibleWindowForState(this._readListState(), now);
|
|
4796
|
+
}
|
|
4217
4797
|
_commitListState(state) {
|
|
4218
4798
|
this.position = state.position;
|
|
4219
4799
|
this.offset = state.offset;
|
|
4800
|
+
this.#jumpController.commit(state);
|
|
4220
4801
|
}
|
|
4221
4802
|
/**
|
|
4222
4803
|
* Scrolls the viewport to the requested item index.
|
|
4223
4804
|
*/
|
|
4224
4805
|
jumpTo(index, options = {}) {
|
|
4225
|
-
|
|
4226
|
-
this.#cancelJumpAnimation();
|
|
4227
|
-
return;
|
|
4228
|
-
}
|
|
4229
|
-
const targetIndex = this._clampItemIndex(index);
|
|
4230
|
-
const currentState = this._normalizeListState(this._readListState());
|
|
4231
|
-
const targetBlock = options.block ?? this._getDefaultJumpBlock();
|
|
4232
|
-
const targetAnchor = this._getTargetAnchor(targetIndex, targetBlock);
|
|
4233
|
-
if (!(options.animated ?? true)) {
|
|
4234
|
-
this.#cancelJumpAnimation();
|
|
4235
|
-
this._applyAnchor(targetAnchor);
|
|
4236
|
-
options.onComplete?.();
|
|
4237
|
-
return;
|
|
4238
|
-
}
|
|
4239
|
-
const startAnchor = this._readAnchor(currentState);
|
|
4240
|
-
if (!Number.isFinite(startAnchor)) {
|
|
4241
|
-
this.#cancelJumpAnimation();
|
|
4242
|
-
this._applyAnchor(targetAnchor);
|
|
4243
|
-
options.onComplete?.();
|
|
4244
|
-
return;
|
|
4245
|
-
}
|
|
4246
|
-
const duration = clamp$3(options.duration ?? VirtualizedRenderer.MIN_JUMP_DURATION + Math.abs(targetAnchor - startAnchor) * VirtualizedRenderer.JUMP_DURATION_PER_ITEM, 0, VirtualizedRenderer.MAX_JUMP_DURATION);
|
|
4247
|
-
if (duration <= 0 || Math.abs(targetAnchor - startAnchor) <= Number.EPSILON) {
|
|
4248
|
-
this.#cancelJumpAnimation();
|
|
4249
|
-
this._applyAnchor(targetAnchor);
|
|
4250
|
-
options.onComplete?.();
|
|
4251
|
-
return;
|
|
4252
|
-
}
|
|
4253
|
-
this.#jumpAnimation = {
|
|
4254
|
-
startAnchor,
|
|
4255
|
-
targetAnchor,
|
|
4256
|
-
startTime: getNow(),
|
|
4257
|
-
duration,
|
|
4258
|
-
needsMoreFrames: true,
|
|
4259
|
-
onComplete: options.onComplete
|
|
4260
|
-
};
|
|
4261
|
-
this.#controlledState = this._readListState();
|
|
4806
|
+
this.#jumpController.jumpTo(index, options);
|
|
4262
4807
|
}
|
|
4263
4808
|
_resetRenderFeedback(feedback) {
|
|
4264
4809
|
if (feedback == null) return;
|
|
@@ -4288,178 +4833,231 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
4288
4833
|
}
|
|
4289
4834
|
return result;
|
|
4290
4835
|
}
|
|
4291
|
-
_renderVisibleWindow(window, feedback) {
|
|
4836
|
+
_renderVisibleWindow(window, feedback, extraShift = 0) {
|
|
4292
4837
|
this._resetRenderFeedback(feedback);
|
|
4293
|
-
return this._renderDrawList(window.drawList, window.shift, feedback);
|
|
4838
|
+
return this._renderDrawList(window.drawList, window.shift + extraShift, feedback);
|
|
4294
4839
|
}
|
|
4295
4840
|
_readVisibleRange(top, height) {
|
|
4296
4841
|
if (!Number.isFinite(top) || !Number.isFinite(height) || height <= 0) return;
|
|
4297
4842
|
const viewportHeight = this.graphics.canvas.clientHeight;
|
|
4298
|
-
const visibleTop = clamp$
|
|
4299
|
-
const visibleBottom = clamp$
|
|
4843
|
+
const visibleTop = clamp$1(-top, 0, height);
|
|
4844
|
+
const visibleBottom = clamp$1(viewportHeight - top, 0, height);
|
|
4300
4845
|
if (visibleBottom <= visibleTop) return;
|
|
4301
4846
|
return {
|
|
4302
4847
|
top: visibleTop,
|
|
4303
4848
|
bottom: visibleBottom
|
|
4304
4849
|
};
|
|
4305
4850
|
}
|
|
4306
|
-
|
|
4307
|
-
return this.#
|
|
4851
|
+
_pruneTransitionAnimations(_window, now) {
|
|
4852
|
+
return this.#transitionController.pruneInvisibleAt(now, this.#getTransitionLifecycleAdapter());
|
|
4308
4853
|
}
|
|
4309
|
-
_hittestVisibleWindow(window, test) {
|
|
4854
|
+
_hittestVisibleWindow(window, test, extraShift = 0) {
|
|
4310
4855
|
for (const { value: item, offset, height } of window.drawList) {
|
|
4311
|
-
const y = offset + window.shift;
|
|
4856
|
+
const y = offset + window.shift + extraShift;
|
|
4312
4857
|
if (test.y < y || test.y >= y + height) continue;
|
|
4313
4858
|
return item.hittest(test, y);
|
|
4314
4859
|
}
|
|
4315
4860
|
return false;
|
|
4316
4861
|
}
|
|
4317
|
-
_captureVisibleItemSnapshot(
|
|
4318
|
-
|
|
4862
|
+
_captureVisibleItemSnapshot(solution, extraShift = 0) {
|
|
4863
|
+
const normalizedState = this._normalizeListState(this._readListState());
|
|
4864
|
+
this.#transitionController.captureVisibilitySnapshot(solution.window, solution.resolutionPath, this.items, this.graphics.canvas.clientHeight, normalizedState, extraShift, this._readVisibleRange.bind(this));
|
|
4319
4865
|
}
|
|
4320
4866
|
_prepareRender(now) {
|
|
4321
|
-
const
|
|
4322
|
-
const
|
|
4323
|
-
|
|
4324
|
-
if (this.items.length === 0) {
|
|
4325
|
-
this.#cancelJumpAnimation();
|
|
4326
|
-
return keepReplacing;
|
|
4327
|
-
}
|
|
4328
|
-
if (this.#controlledState != null && !sameState(this.#controlledState, this.position, this.offset)) {
|
|
4329
|
-
this.#cancelJumpAnimation();
|
|
4330
|
-
return keepReplacing;
|
|
4331
|
-
}
|
|
4332
|
-
const anchor = interpolate(animation.startAnchor, animation.targetAnchor, animation.startTime, animation.duration, now);
|
|
4333
|
-
const progress = getProgress(animation.startTime, animation.duration, now);
|
|
4334
|
-
this._applyAnchor(anchor);
|
|
4335
|
-
animation.needsMoreFrames = progress < 1;
|
|
4336
|
-
return keepReplacing || animation.needsMoreFrames;
|
|
4867
|
+
const keepTransitioning = this.#transitionController.prepare(now, this.#getTransitionLifecycleAdapter());
|
|
4868
|
+
const keepJumping = this.#jumpController.prepare(now);
|
|
4869
|
+
return keepTransitioning || keepJumping;
|
|
4337
4870
|
}
|
|
4338
4871
|
_finishRender(requestRedraw) {
|
|
4339
|
-
|
|
4340
|
-
if (animation == null) return requestRedraw;
|
|
4341
|
-
if (animation.needsMoreFrames) {
|
|
4342
|
-
this.#controlledState = this._readListState();
|
|
4343
|
-
return true;
|
|
4344
|
-
}
|
|
4345
|
-
const onComplete = animation.onComplete;
|
|
4346
|
-
this.#cancelJumpAnimation();
|
|
4347
|
-
onComplete?.();
|
|
4348
|
-
return requestRedraw || this.#jumpAnimation != null;
|
|
4872
|
+
return this.#jumpController.finishFrame(requestRedraw);
|
|
4349
4873
|
}
|
|
4350
4874
|
_clampItemIndex(index) {
|
|
4351
|
-
return clamp$
|
|
4875
|
+
return clamp$1(Number.isFinite(index) ? Math.trunc(index) : 0, 0, this.items.length - 1);
|
|
4352
4876
|
}
|
|
4353
4877
|
_getItemHeight(index) {
|
|
4878
|
+
return this._getItemHeightAt(index, getNow());
|
|
4879
|
+
}
|
|
4880
|
+
_getItemHeightAt(index, now) {
|
|
4354
4881
|
const item = this.items[index];
|
|
4355
|
-
return this.#
|
|
4882
|
+
return this.#transitionController.getItemHeight(item, now, {
|
|
4356
4883
|
renderItem: this.options.renderItem,
|
|
4357
4884
|
measureNode: this.measureRootNode.bind(this)
|
|
4358
4885
|
});
|
|
4359
4886
|
}
|
|
4360
|
-
|
|
4361
|
-
|
|
4887
|
+
_readAnchorAt(now) {
|
|
4888
|
+
if (this.items.length <= 0) return;
|
|
4889
|
+
const state = this._normalizeListState(this._readListState());
|
|
4890
|
+
return this._readAnchor(state, (index) => this._getItemHeightAt(index, now));
|
|
4362
4891
|
}
|
|
4363
|
-
|
|
4364
|
-
if (this.items.length
|
|
4365
|
-
|
|
4366
|
-
let remaining = Number.isFinite(offset) ? offset : 0;
|
|
4367
|
-
while (true) {
|
|
4368
|
-
if (remaining < 0) {
|
|
4369
|
-
if (currentIndex === 0) return 0;
|
|
4370
|
-
currentIndex -= 1;
|
|
4371
|
-
const height = this._getItemHeight(currentIndex);
|
|
4372
|
-
if (height > 0) remaining += height;
|
|
4373
|
-
continue;
|
|
4374
|
-
}
|
|
4375
|
-
const height = this._getItemHeight(currentIndex);
|
|
4376
|
-
if (height > 0) {
|
|
4377
|
-
if (remaining <= height) return currentIndex + remaining / height;
|
|
4378
|
-
remaining -= height;
|
|
4379
|
-
} else if (remaining === 0) return currentIndex;
|
|
4380
|
-
if (currentIndex === this.items.length - 1) return this.items.length;
|
|
4381
|
-
currentIndex += 1;
|
|
4382
|
-
}
|
|
4892
|
+
_restoreAnchor(anchor) {
|
|
4893
|
+
if (!Number.isFinite(anchor) || this.items.length <= 0) return;
|
|
4894
|
+
this._applyAnchor(anchor);
|
|
4383
4895
|
}
|
|
4384
|
-
|
|
4385
|
-
this.#
|
|
4386
|
-
this.#controlledState = void 0;
|
|
4896
|
+
_resolveItem(item, _index, now) {
|
|
4897
|
+
return this.#transitionController.resolveItem(item, now, this.#getTransitionRenderAdapter(), this.#getTransitionLifecycleAdapter());
|
|
4387
4898
|
}
|
|
4388
4899
|
#handleDeleteComplete(item) {
|
|
4389
4900
|
this.options.list.finalizeDelete(item);
|
|
4390
4901
|
}
|
|
4391
|
-
#
|
|
4902
|
+
#getTransitionLifecycleAdapter() {
|
|
4392
4903
|
return {
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
getRootContext: this.getRootContext.bind(this),
|
|
4397
|
-
graphics: this.graphics,
|
|
4398
|
-
getAnimatedLayerOffset: this._getAnimatedLayerOffset.bind(this),
|
|
4399
|
-
onDeleteComplete: this.#handleDeleteComplete.bind(this)
|
|
4904
|
+
onDeleteComplete: this.#handleDeleteComplete.bind(this),
|
|
4905
|
+
captureVisualAnchor: this._readAnchorAt.bind(this),
|
|
4906
|
+
restoreVisualAnchor: this._restoreAnchor.bind(this)
|
|
4400
4907
|
};
|
|
4401
4908
|
}
|
|
4402
|
-
#
|
|
4909
|
+
#getVirtualizedRuntime() {
|
|
4403
4910
|
return {
|
|
4404
|
-
...this.#getReplacementRendererAdapter(),
|
|
4405
4911
|
items: this.items,
|
|
4406
4912
|
position: this.position,
|
|
4407
4913
|
offset: this.offset,
|
|
4408
|
-
|
|
4914
|
+
renderItem: this.options.renderItem,
|
|
4915
|
+
measureNode: this.measureRootNode.bind(this),
|
|
4409
4916
|
readVisibleRange: this._readVisibleRange.bind(this),
|
|
4410
4917
|
resolveVisibleWindow: () => this._resolveVisibleWindow(getNow())
|
|
4411
4918
|
};
|
|
4412
4919
|
}
|
|
4920
|
+
#getTransitionRenderAdapter() {
|
|
4921
|
+
const runtime = this.#getVirtualizedRuntime();
|
|
4922
|
+
return {
|
|
4923
|
+
renderItem: runtime.renderItem,
|
|
4924
|
+
measureNode: runtime.measureNode,
|
|
4925
|
+
drawNode: this.drawRootNode.bind(this),
|
|
4926
|
+
getRootContext: this.getRootContext.bind(this),
|
|
4927
|
+
graphics: this.graphics
|
|
4928
|
+
};
|
|
4929
|
+
}
|
|
4930
|
+
#getTransitionPlanningAdapter() {
|
|
4931
|
+
return {
|
|
4932
|
+
...this.#getVirtualizedRuntime(),
|
|
4933
|
+
underflowAlign: this._getLayoutOptions().underflowAlign
|
|
4934
|
+
};
|
|
4935
|
+
}
|
|
4413
4936
|
#handleListStateChange(change) {
|
|
4414
|
-
this.#
|
|
4937
|
+
const nextChange = this.#jumpController.handleListStateChange(change);
|
|
4938
|
+
this.#transitionController.handleListStateChange(nextChange, this.#getTransitionPlanningAdapter(), this.#getTransitionLifecycleAdapter());
|
|
4415
4939
|
}
|
|
4416
4940
|
};
|
|
4417
4941
|
//#endregion
|
|
4942
|
+
//#region src/renderer/virtualized/anchor-model.ts
|
|
4943
|
+
function clampItemIndex(index, itemCount) {
|
|
4944
|
+
if (itemCount <= 0) return 0;
|
|
4945
|
+
return clamp$1(Number.isFinite(index) ? Math.trunc(index) : 0, 0, itemCount - 1);
|
|
4946
|
+
}
|
|
4947
|
+
function readAnchorFromState(itemCount, state, anchorMode, readItemHeight) {
|
|
4948
|
+
if (itemCount <= 0) return 0;
|
|
4949
|
+
const height = readItemHeight(state.position);
|
|
4950
|
+
if (anchorMode === "top") return height > 0 ? state.position - state.offset / height : state.position;
|
|
4951
|
+
return height > 0 ? state.position + 1 - state.offset / height : state.position + 1;
|
|
4952
|
+
}
|
|
4953
|
+
function applyAnchorToState(itemCount, anchor, anchorMode, readItemHeight) {
|
|
4954
|
+
if (itemCount <= 0) return;
|
|
4955
|
+
const clampedAnchor = clamp$1(anchor, 0, itemCount);
|
|
4956
|
+
if (anchorMode === "top") {
|
|
4957
|
+
const position = clamp$1(Math.floor(clampedAnchor), 0, itemCount - 1);
|
|
4958
|
+
const height = readItemHeight(position);
|
|
4959
|
+
const offset = height > 0 ? -(clampedAnchor - position) * height : 0;
|
|
4960
|
+
return {
|
|
4961
|
+
position,
|
|
4962
|
+
offset: Object.is(offset, -0) ? 0 : offset
|
|
4963
|
+
};
|
|
4964
|
+
}
|
|
4965
|
+
const position = clamp$1(Math.ceil(clampedAnchor) - 1, 0, itemCount - 1);
|
|
4966
|
+
const height = readItemHeight(position);
|
|
4967
|
+
const offset = height > 0 ? (position + 1 - clampedAnchor) * height : 0;
|
|
4968
|
+
return {
|
|
4969
|
+
position,
|
|
4970
|
+
offset: Object.is(offset, -0) ? 0 : offset
|
|
4971
|
+
};
|
|
4972
|
+
}
|
|
4973
|
+
function getAnchorAtOffset(itemCount, index, offset, readItemHeight) {
|
|
4974
|
+
if (itemCount <= 0) return 0;
|
|
4975
|
+
let currentIndex = clampItemIndex(index, itemCount);
|
|
4976
|
+
let remaining = Number.isFinite(offset) ? offset : 0;
|
|
4977
|
+
while (true) {
|
|
4978
|
+
if (remaining < 0) {
|
|
4979
|
+
if (currentIndex === 0) return 0;
|
|
4980
|
+
currentIndex -= 1;
|
|
4981
|
+
const height = readItemHeight(currentIndex);
|
|
4982
|
+
if (height > 0) remaining += height;
|
|
4983
|
+
continue;
|
|
4984
|
+
}
|
|
4985
|
+
const height = readItemHeight(currentIndex);
|
|
4986
|
+
if (height > 0) {
|
|
4987
|
+
if (remaining <= height) return currentIndex + remaining / height;
|
|
4988
|
+
remaining -= height;
|
|
4989
|
+
} else if (remaining === 0) return currentIndex;
|
|
4990
|
+
if (currentIndex === itemCount - 1) return itemCount;
|
|
4991
|
+
currentIndex += 1;
|
|
4992
|
+
}
|
|
4993
|
+
}
|
|
4994
|
+
function getTargetAnchorForItem(itemCount, index, block, anchorMode, viewportHeight, readItemHeight) {
|
|
4995
|
+
if (itemCount <= 0) return 0;
|
|
4996
|
+
const targetIndex = clampItemIndex(index, itemCount);
|
|
4997
|
+
const height = readItemHeight(targetIndex);
|
|
4998
|
+
if (anchorMode === "top") switch (block) {
|
|
4999
|
+
case "start": return getAnchorAtOffset(itemCount, targetIndex, 0, readItemHeight);
|
|
5000
|
+
case "center": return getAnchorAtOffset(itemCount, targetIndex, height / 2 - viewportHeight / 2, readItemHeight);
|
|
5001
|
+
case "end": return getAnchorAtOffset(itemCount, targetIndex, height - viewportHeight, readItemHeight);
|
|
5002
|
+
}
|
|
5003
|
+
switch (block) {
|
|
5004
|
+
case "start": return getAnchorAtOffset(itemCount, targetIndex, viewportHeight, readItemHeight);
|
|
5005
|
+
case "center": return getAnchorAtOffset(itemCount, targetIndex, height / 2 + viewportHeight / 2, readItemHeight);
|
|
5006
|
+
case "end": return getAnchorAtOffset(itemCount, targetIndex, height, readItemHeight);
|
|
5007
|
+
}
|
|
5008
|
+
}
|
|
5009
|
+
//#endregion
|
|
4418
5010
|
//#region src/renderer/virtualized/solver.ts
|
|
4419
|
-
function clamp
|
|
5011
|
+
function clamp(value, min, max) {
|
|
4420
5012
|
return Math.min(Math.max(value, min), max);
|
|
4421
5013
|
}
|
|
4422
5014
|
function normalizeOffset(offset) {
|
|
4423
5015
|
return Number.isFinite(offset) ? offset : 0;
|
|
4424
5016
|
}
|
|
4425
|
-
function
|
|
5017
|
+
function resolveListLayoutOptions(options = {}) {
|
|
5018
|
+
return {
|
|
5019
|
+
anchorMode: options.anchorMode ?? "top",
|
|
5020
|
+
underflowAlign: options.underflowAlign ?? "top"
|
|
5021
|
+
};
|
|
5022
|
+
}
|
|
5023
|
+
function normalizeVisibleState(itemCount, state, layout) {
|
|
4426
5024
|
if (itemCount <= 0) return {
|
|
4427
5025
|
position: 0,
|
|
4428
5026
|
offset: 0
|
|
4429
5027
|
};
|
|
4430
5028
|
const position = state.position;
|
|
4431
|
-
const fallbackPosition =
|
|
5029
|
+
const fallbackPosition = layout.anchorMode === "top" ? 0 : itemCount - 1;
|
|
4432
5030
|
if (typeof position !== "number" || !Number.isFinite(position)) return {
|
|
4433
5031
|
position: fallbackPosition,
|
|
4434
5032
|
offset: normalizeOffset(state.offset)
|
|
4435
5033
|
};
|
|
4436
5034
|
return {
|
|
4437
|
-
position: clamp
|
|
5035
|
+
position: clamp(Math.trunc(position), 0, itemCount - 1),
|
|
4438
5036
|
offset: normalizeOffset(state.offset)
|
|
4439
5037
|
};
|
|
4440
5038
|
}
|
|
4441
|
-
function
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
const normalizedState = normalizeVisibleState(items.length, state, direction);
|
|
5039
|
+
function resolveVisibleWindow(items, state, viewportHeight, resolveItem, layout) {
|
|
5040
|
+
const normalizedState = normalizeVisibleState(items.length, state, layout);
|
|
5041
|
+
const resolutionPath = /* @__PURE__ */ new Set();
|
|
5042
|
+
const readResolvedItem = (item, idx) => {
|
|
5043
|
+
resolutionPath.add(idx);
|
|
5044
|
+
return resolveItem(item, idx);
|
|
5045
|
+
};
|
|
4449
5046
|
if (items.length === 0) return {
|
|
4450
5047
|
normalizedState,
|
|
5048
|
+
resolutionPath: [],
|
|
4451
5049
|
window: {
|
|
4452
5050
|
drawList: [],
|
|
4453
5051
|
shift: 0
|
|
4454
5052
|
}
|
|
4455
5053
|
};
|
|
4456
|
-
if (
|
|
5054
|
+
if (layout.anchorMode === "top") {
|
|
4457
5055
|
let { position, offset } = normalizedState;
|
|
4458
5056
|
let drawLength = 0;
|
|
4459
5057
|
if (offset > 0) if (position === 0) offset = 0;
|
|
4460
5058
|
else {
|
|
4461
5059
|
for (let i = position - 1; i >= 0; i -= 1) {
|
|
4462
|
-
const { height } =
|
|
5060
|
+
const { height } = readResolvedItem(items[i], i);
|
|
4463
5061
|
position = i;
|
|
4464
5062
|
offset -= height;
|
|
4465
5063
|
if (offset <= 0) break;
|
|
@@ -4469,7 +5067,7 @@ function resolveVisibleWindow(items, state, viewportHeight, resolveItem, directi
|
|
|
4469
5067
|
let y = offset;
|
|
4470
5068
|
const drawList = [];
|
|
4471
5069
|
for (let i = position; i < items.length; i += 1) {
|
|
4472
|
-
const { value, height } =
|
|
5070
|
+
const { value, height } = readResolvedItem(items[i], i);
|
|
4473
5071
|
if (y + height > 0) {
|
|
4474
5072
|
drawList.push({
|
|
4475
5073
|
idx: i,
|
|
@@ -4494,7 +5092,7 @@ function resolveVisibleWindow(items, state, viewportHeight, resolveItem, directi
|
|
|
4494
5092
|
y = offset += shift;
|
|
4495
5093
|
let lastIdx = -1;
|
|
4496
5094
|
for (let i = position - 1; i >= 0; i -= 1) {
|
|
4497
|
-
const { value, height } =
|
|
5095
|
+
const { value, height } = readResolvedItem(items[i], i);
|
|
4498
5096
|
drawLength += height;
|
|
4499
5097
|
y -= height;
|
|
4500
5098
|
drawList.push({
|
|
@@ -4512,22 +5110,19 @@ function resolveVisibleWindow(items, state, viewportHeight, resolveItem, directi
|
|
|
4512
5110
|
offset = 0;
|
|
4513
5111
|
}
|
|
4514
5112
|
}
|
|
4515
|
-
return {
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
shift
|
|
4523
|
-
}
|
|
4524
|
-
};
|
|
5113
|
+
return finalizeVisibleWindowResult(items.length, viewportHeight, layout, {
|
|
5114
|
+
position,
|
|
5115
|
+
offset
|
|
5116
|
+
}, Array.from(resolutionPath), {
|
|
5117
|
+
drawList,
|
|
5118
|
+
shift
|
|
5119
|
+
});
|
|
4525
5120
|
}
|
|
4526
5121
|
let { position, offset } = normalizedState;
|
|
4527
5122
|
let drawLength = 0;
|
|
4528
5123
|
if (offset < 0) if (position === items.length - 1) offset = 0;
|
|
4529
5124
|
else for (let i = position + 1; i < items.length; i += 1) {
|
|
4530
|
-
const { height } =
|
|
5125
|
+
const { height } = readResolvedItem(items[i], i);
|
|
4531
5126
|
position = i;
|
|
4532
5127
|
offset += height;
|
|
4533
5128
|
if (offset > 0) break;
|
|
@@ -4535,7 +5130,7 @@ function resolveVisibleWindow(items, state, viewportHeight, resolveItem, directi
|
|
|
4535
5130
|
let y = viewportHeight + offset;
|
|
4536
5131
|
const drawList = [];
|
|
4537
5132
|
for (let i = position; i >= 0; i -= 1) {
|
|
4538
|
-
const { value, height } =
|
|
5133
|
+
const { value, height } = readResolvedItem(items[i], i);
|
|
4539
5134
|
y -= height;
|
|
4540
5135
|
if (y <= viewportHeight) {
|
|
4541
5136
|
drawList.push({
|
|
@@ -4557,7 +5152,7 @@ function resolveVisibleWindow(items, state, viewportHeight, resolveItem, directi
|
|
|
4557
5152
|
if (drawLength < viewportHeight) {
|
|
4558
5153
|
y = drawLength;
|
|
4559
5154
|
for (let i = position + 1; i < items.length; i += 1) {
|
|
4560
|
-
const { value, height } =
|
|
5155
|
+
const { value, height } = readResolvedItem(items[i], i);
|
|
4561
5156
|
drawList.push({
|
|
4562
5157
|
idx: i,
|
|
4563
5158
|
value,
|
|
@@ -4571,122 +5166,88 @@ function resolveVisibleWindow(items, state, viewportHeight, resolveItem, directi
|
|
|
4571
5166
|
offset = drawLength < viewportHeight ? 0 : drawLength - viewportHeight;
|
|
4572
5167
|
} else offset = drawLength - viewportHeight;
|
|
4573
5168
|
}
|
|
5169
|
+
return finalizeVisibleWindowResult(items.length, viewportHeight, layout, {
|
|
5170
|
+
position,
|
|
5171
|
+
offset
|
|
5172
|
+
}, Array.from(resolutionPath), {
|
|
5173
|
+
drawList,
|
|
5174
|
+
shift
|
|
5175
|
+
});
|
|
5176
|
+
}
|
|
5177
|
+
function finalizeVisibleWindowResult(itemCount, viewportHeight, layout, normalizedState, resolutionPath, window) {
|
|
5178
|
+
if (window.drawList.length !== itemCount || itemCount <= 0) return {
|
|
5179
|
+
normalizedState,
|
|
5180
|
+
resolutionPath,
|
|
5181
|
+
window
|
|
5182
|
+
};
|
|
5183
|
+
let minIndex = Number.POSITIVE_INFINITY;
|
|
5184
|
+
let maxIndex = Number.NEGATIVE_INFINITY;
|
|
5185
|
+
let minOffset = Number.POSITIVE_INFINITY;
|
|
5186
|
+
let maxBottom = Number.NEGATIVE_INFINITY;
|
|
5187
|
+
for (const entry of window.drawList) {
|
|
5188
|
+
minIndex = Math.min(minIndex, entry.idx);
|
|
5189
|
+
maxIndex = Math.max(maxIndex, entry.idx);
|
|
5190
|
+
minOffset = Math.min(minOffset, entry.offset);
|
|
5191
|
+
maxBottom = Math.max(maxBottom, entry.offset + entry.height);
|
|
5192
|
+
}
|
|
5193
|
+
const contentHeight = maxBottom - minOffset;
|
|
5194
|
+
if (minIndex !== 0 || maxIndex !== itemCount - 1 || !(contentHeight < viewportHeight - Number.EPSILON)) return {
|
|
5195
|
+
normalizedState,
|
|
5196
|
+
resolutionPath,
|
|
5197
|
+
window
|
|
5198
|
+
};
|
|
5199
|
+
const desiredTop = layout.underflowAlign === "bottom" ? viewportHeight - contentHeight : 0;
|
|
4574
5200
|
return {
|
|
4575
|
-
normalizedState: {
|
|
4576
|
-
position,
|
|
4577
|
-
offset
|
|
5201
|
+
normalizedState: layout.anchorMode === "top" ? {
|
|
5202
|
+
position: 0,
|
|
5203
|
+
offset: 0
|
|
5204
|
+
} : {
|
|
5205
|
+
position: itemCount - 1,
|
|
5206
|
+
offset: 0
|
|
4578
5207
|
},
|
|
5208
|
+
resolutionPath,
|
|
4579
5209
|
window: {
|
|
4580
|
-
drawList,
|
|
4581
|
-
shift
|
|
5210
|
+
drawList: window.drawList,
|
|
5211
|
+
shift: desiredTop - minOffset
|
|
4582
5212
|
}
|
|
4583
5213
|
};
|
|
4584
5214
|
}
|
|
4585
|
-
function resolveTimelineVisibleWindow(items, state, viewportHeight, resolveItem) {
|
|
4586
|
-
return resolveVisibleWindow(items, state, viewportHeight, resolveItem, "forward");
|
|
4587
|
-
}
|
|
4588
|
-
function resolveChatVisibleWindow(items, state, viewportHeight, resolveItem) {
|
|
4589
|
-
return resolveVisibleWindow(items, state, viewportHeight, resolveItem, "backward");
|
|
4590
|
-
}
|
|
4591
5215
|
//#endregion
|
|
4592
|
-
//#region src/renderer/virtualized/
|
|
4593
|
-
function clamp$1(value, min, max) {
|
|
4594
|
-
return Math.min(Math.max(value, min), max);
|
|
4595
|
-
}
|
|
5216
|
+
//#region src/renderer/virtualized/list.ts
|
|
4596
5217
|
/**
|
|
4597
|
-
* Virtualized renderer
|
|
5218
|
+
* Virtualized list renderer with configurable anchor semantics.
|
|
4598
5219
|
*/
|
|
4599
|
-
var
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
}
|
|
4605
|
-
_getDefaultJumpBlock() {
|
|
4606
|
-
return "end";
|
|
4607
|
-
}
|
|
4608
|
-
_normalizeListState(state) {
|
|
4609
|
-
return normalizeChatState(this.items.length, state);
|
|
4610
|
-
}
|
|
4611
|
-
_readAnchor(state) {
|
|
4612
|
-
if (this.items.length === 0) return 0;
|
|
4613
|
-
const height = this._getItemHeight(state.position);
|
|
4614
|
-
return height > 0 ? state.position + 1 - state.offset / height : state.position + 1;
|
|
4615
|
-
}
|
|
4616
|
-
_applyAnchor(anchor) {
|
|
4617
|
-
if (this.items.length === 0) return;
|
|
4618
|
-
const clampedAnchor = clamp$1(anchor, 0, this.items.length);
|
|
4619
|
-
const position = clamp$1(Math.ceil(clampedAnchor) - 1, 0, this.items.length - 1);
|
|
4620
|
-
const height = this._getItemHeight(position);
|
|
4621
|
-
const offset = height > 0 ? (position + 1 - clampedAnchor) * height : 0;
|
|
4622
|
-
this._commitListState({
|
|
4623
|
-
position,
|
|
4624
|
-
offset: Object.is(offset, -0) ? 0 : offset
|
|
4625
|
-
});
|
|
5220
|
+
var ListRenderer = class extends VirtualizedRenderer {
|
|
5221
|
+
#layout;
|
|
5222
|
+
constructor(graphics, options) {
|
|
5223
|
+
super(graphics, options);
|
|
5224
|
+
this.#layout = resolveListLayoutOptions(options);
|
|
4626
5225
|
}
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
const viewportHeight = this.graphics.canvas.clientHeight;
|
|
4630
|
-
switch (block) {
|
|
4631
|
-
case "start": return this._getAnchorAtOffset(index, viewportHeight);
|
|
4632
|
-
case "center": return this._getAnchorAtOffset(index, height / 2 + viewportHeight / 2);
|
|
4633
|
-
case "end": return this._getAnchorAtOffset(index, height);
|
|
4634
|
-
}
|
|
5226
|
+
_getLayoutOptions() {
|
|
5227
|
+
return this.#layout;
|
|
4635
5228
|
}
|
|
4636
|
-
|
|
4637
|
-
return
|
|
4638
|
-
}
|
|
4639
|
-
};
|
|
4640
|
-
//#endregion
|
|
4641
|
-
//#region src/renderer/virtualized/timeline.ts
|
|
4642
|
-
function clamp(value, min, max) {
|
|
4643
|
-
return Math.min(Math.max(value, min), max);
|
|
4644
|
-
}
|
|
4645
|
-
/**
|
|
4646
|
-
* Virtualized renderer anchored to the top, suitable for timeline-style UIs.
|
|
4647
|
-
*/
|
|
4648
|
-
var TimelineRenderer = class extends VirtualizedRenderer {
|
|
4649
|
-
_resolveVisibleWindow(now) {
|
|
4650
|
-
return resolveTimelineVisibleWindow(this.items, this._readListState(), this.graphics.canvas.clientHeight, (item, idx) => {
|
|
4651
|
-
return this._resolveItem(item, idx, now);
|
|
4652
|
-
});
|
|
5229
|
+
_resolveVisibleWindowForState(state, now) {
|
|
5230
|
+
return resolveVisibleWindow(this.items, state, this.graphics.canvas.clientHeight, (item, idx) => this._resolveItem(item, idx, now), this.#layout);
|
|
4653
5231
|
}
|
|
4654
5232
|
_getDefaultJumpBlock() {
|
|
4655
|
-
return "start";
|
|
5233
|
+
return this.#layout.anchorMode === "top" ? "start" : "end";
|
|
4656
5234
|
}
|
|
4657
5235
|
_normalizeListState(state) {
|
|
4658
|
-
return
|
|
5236
|
+
return normalizeVisibleState(this.items.length, state, this.#layout);
|
|
4659
5237
|
}
|
|
4660
|
-
_readAnchor(state) {
|
|
4661
|
-
|
|
4662
|
-
const height = this._getItemHeight(state.position);
|
|
4663
|
-
return height > 0 ? state.position - state.offset / height : state.position;
|
|
5238
|
+
_readAnchor(state, readItemHeight) {
|
|
5239
|
+
return readAnchorFromState(this.items.length, state, this.#layout.anchorMode, readItemHeight);
|
|
4664
5240
|
}
|
|
4665
5241
|
_applyAnchor(anchor) {
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
const height = this._getItemHeight(position);
|
|
4670
|
-
const offset = height > 0 ? -(clampedAnchor - position) * height : 0;
|
|
4671
|
-
this._commitListState({
|
|
4672
|
-
position,
|
|
4673
|
-
offset: Object.is(offset, -0) ? 0 : offset
|
|
4674
|
-
});
|
|
5242
|
+
const state = applyAnchorToState(this.items.length, anchor, this.#layout.anchorMode, this._getItemHeight.bind(this));
|
|
5243
|
+
if (state == null) return;
|
|
5244
|
+
this._commitListState(state);
|
|
4675
5245
|
}
|
|
4676
5246
|
_getTargetAnchor(index, block) {
|
|
4677
|
-
|
|
4678
|
-
const viewportHeight = this.graphics.canvas.clientHeight;
|
|
4679
|
-
switch (block) {
|
|
4680
|
-
case "start": return this._getAnchorAtOffset(index, 0);
|
|
4681
|
-
case "center": return this._getAnchorAtOffset(index, height / 2 - viewportHeight / 2);
|
|
4682
|
-
case "end": return this._getAnchorAtOffset(index, height - viewportHeight);
|
|
4683
|
-
}
|
|
4684
|
-
}
|
|
4685
|
-
_getAnimatedLayerOffset(_slotHeight, _nodeHeight) {
|
|
4686
|
-
return 0;
|
|
5247
|
+
return getTargetAnchorForItem(this.items.length, index, block, this.#layout.anchorMode, this.graphics.canvas.clientHeight, this._getItemHeight.bind(this));
|
|
4687
5248
|
}
|
|
4688
5249
|
};
|
|
4689
5250
|
//#endregion
|
|
4690
|
-
export { BaseRenderer,
|
|
5251
|
+
export { BaseRenderer, DebugRenderer, Fixed, Flex, FlexItem, Group, ListRenderer, ListState, MultilineText, PaddingBox, Place, ShrinkWrap, Text, VirtualizedRenderer, Wrapper, memoRenderItem, memoRenderItemBy };
|
|
4691
5252
|
|
|
4692
5253
|
//# sourceMappingURL=index.mjs.map
|