chat-layout 1.2.0-5 → 1.2.0-7
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 +2 -2
- package/example/chat.ts +19 -6
- package/index.d.mts +58 -9
- package/index.mjs +945 -488
- package/index.mjs.map +1 -1
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -3596,6 +3596,40 @@ const listStateListenerRegistry = typeof FinalizationRegistry === "function" ? n
|
|
|
3596
3596
|
if (list == null) return;
|
|
3597
3597
|
deleteListStateListener(list, token);
|
|
3598
3598
|
}) : null;
|
|
3599
|
+
const listScrollMutations = /* @__PURE__ */ new WeakMap();
|
|
3600
|
+
const WRITE_LIST_SCROLL_STATE = Symbol("writeListScrollState");
|
|
3601
|
+
function normalizePosition(value) {
|
|
3602
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.trunc(value) : void 0;
|
|
3603
|
+
}
|
|
3604
|
+
function normalizeOffset$1(value) {
|
|
3605
|
+
return Number.isFinite(value) ? value : 0;
|
|
3606
|
+
}
|
|
3607
|
+
function getListScrollMutationRecord(list) {
|
|
3608
|
+
let record = listScrollMutations.get(list);
|
|
3609
|
+
if (record == null) {
|
|
3610
|
+
record = {
|
|
3611
|
+
version: 0,
|
|
3612
|
+
source: "internal"
|
|
3613
|
+
};
|
|
3614
|
+
listScrollMutations.set(list, record);
|
|
3615
|
+
}
|
|
3616
|
+
return record;
|
|
3617
|
+
}
|
|
3618
|
+
function markListScrollMutation(list, source) {
|
|
3619
|
+
const record = getListScrollMutationRecord(list);
|
|
3620
|
+
record.version += 1;
|
|
3621
|
+
record.source = source;
|
|
3622
|
+
}
|
|
3623
|
+
function readListScrollMutation(list) {
|
|
3624
|
+
const record = getListScrollMutationRecord(list);
|
|
3625
|
+
return {
|
|
3626
|
+
version: record.version,
|
|
3627
|
+
source: record.source
|
|
3628
|
+
};
|
|
3629
|
+
}
|
|
3630
|
+
function writeInternalListScrollState(list, state) {
|
|
3631
|
+
list[WRITE_LIST_SCROLL_STATE](state, "internal");
|
|
3632
|
+
}
|
|
3599
3633
|
function deleteListStateListener(list, token) {
|
|
3600
3634
|
const listeners = listStateListeners.get(list);
|
|
3601
3635
|
if (listeners == null) return;
|
|
@@ -3660,17 +3694,28 @@ function normalizeInsertAnimation(animation) {
|
|
|
3660
3694
|
const duration = normalizeInsertAnimationDuration(animation?.duration, animation != null);
|
|
3661
3695
|
if (duration == null) return;
|
|
3662
3696
|
const normalizedAnimation = { duration };
|
|
3663
|
-
if (
|
|
3664
|
-
if (animation?.followIfAtBoundary === true) normalizedAnimation.followIfAtBoundary = true;
|
|
3697
|
+
if (animation?.autoFollow === true) normalizedAnimation.autoFollow = true;
|
|
3665
3698
|
return normalizedAnimation;
|
|
3666
3699
|
}
|
|
3667
3700
|
var ListState = class {
|
|
3668
3701
|
#items;
|
|
3669
3702
|
#pendingDeletes = /* @__PURE__ */ new Set();
|
|
3703
|
+
#offset = 0;
|
|
3704
|
+
#position;
|
|
3670
3705
|
/** Pixel offset from the anchored item edge. */
|
|
3671
|
-
offset
|
|
3706
|
+
get offset() {
|
|
3707
|
+
return this.#offset;
|
|
3708
|
+
}
|
|
3709
|
+
set offset(value) {
|
|
3710
|
+
this.#writeScrollState({ offset: normalizeOffset$1(value) }, "external");
|
|
3711
|
+
}
|
|
3672
3712
|
/** Anchor item index, or `undefined` to use the renderer default. */
|
|
3673
|
-
position
|
|
3713
|
+
get position() {
|
|
3714
|
+
return this.#position;
|
|
3715
|
+
}
|
|
3716
|
+
set position(value) {
|
|
3717
|
+
this.#writeScrollState({ position: normalizePosition(value) }, "external");
|
|
3718
|
+
}
|
|
3674
3719
|
/** Items currently managed by the renderer. */
|
|
3675
3720
|
get items() {
|
|
3676
3721
|
return this.#items;
|
|
@@ -3700,7 +3745,7 @@ var ListState = class {
|
|
|
3700
3745
|
if (items.length === 0) return;
|
|
3701
3746
|
assertUniqueItemReferences(items, this.#items);
|
|
3702
3747
|
const normalizedAnimation = normalizeInsertAnimation(animation);
|
|
3703
|
-
if (this.position != null) this.position
|
|
3748
|
+
if (this.position != null) this.#writeScrollState({ position: this.position + items.length }, "internal");
|
|
3704
3749
|
this.#items = items.concat(this.#items);
|
|
3705
3750
|
emitListStateChange(this, {
|
|
3706
3751
|
type: "unshift",
|
|
@@ -3772,12 +3817,13 @@ var ListState = class {
|
|
|
3772
3817
|
this.#pendingDeletes.delete(item);
|
|
3773
3818
|
if (index < 0) return;
|
|
3774
3819
|
this.#items.splice(index, 1);
|
|
3775
|
-
if (this.#items.length === 0) {
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
}
|
|
3779
|
-
|
|
3780
|
-
|
|
3820
|
+
if (this.#items.length === 0) this.#writeScrollState({
|
|
3821
|
+
position: void 0,
|
|
3822
|
+
offset: 0
|
|
3823
|
+
}, "internal");
|
|
3824
|
+
else if (this.position != null) {
|
|
3825
|
+
if (this.position > index) this.#writeScrollState({ position: this.position - 1 }, "internal");
|
|
3826
|
+
else if (this.position === index) this.#writeScrollState({ position: Math.min(index, this.#items.length - 1) }, "internal");
|
|
3781
3827
|
}
|
|
3782
3828
|
emitListStateChange(this, {
|
|
3783
3829
|
type: "delete-finalize",
|
|
@@ -3788,8 +3834,10 @@ var ListState = class {
|
|
|
3788
3834
|
* Sets the current anchor item and pixel offset.
|
|
3789
3835
|
*/
|
|
3790
3836
|
setAnchor(position, offset = 0) {
|
|
3791
|
-
this
|
|
3792
|
-
|
|
3837
|
+
this.#writeScrollState({
|
|
3838
|
+
position: normalizePosition(position),
|
|
3839
|
+
offset: normalizeOffset$1(offset)
|
|
3840
|
+
}, "external");
|
|
3793
3841
|
}
|
|
3794
3842
|
/**
|
|
3795
3843
|
* Replaces all items and clears scroll state.
|
|
@@ -3799,18 +3847,44 @@ var ListState = class {
|
|
|
3799
3847
|
assertUniqueItemReferences(nextItems);
|
|
3800
3848
|
this.#items = nextItems;
|
|
3801
3849
|
this.#pendingDeletes.clear();
|
|
3802
|
-
this
|
|
3803
|
-
|
|
3850
|
+
this.#writeScrollState({
|
|
3851
|
+
position: void 0,
|
|
3852
|
+
offset: 0
|
|
3853
|
+
}, "internal");
|
|
3804
3854
|
emitListStateChange(this, { type: "reset" });
|
|
3805
3855
|
}
|
|
3806
3856
|
/** Clears the current scroll anchor while keeping the items. */
|
|
3807
3857
|
resetScroll() {
|
|
3808
|
-
this
|
|
3809
|
-
|
|
3858
|
+
this.#writeScrollState({
|
|
3859
|
+
position: void 0,
|
|
3860
|
+
offset: 0
|
|
3861
|
+
}, "external");
|
|
3810
3862
|
}
|
|
3811
3863
|
/** Applies a relative pixel scroll delta. */
|
|
3812
3864
|
applyScroll(delta) {
|
|
3813
|
-
this
|
|
3865
|
+
this.#writeScrollState({ offset: this.#offset + delta }, "external");
|
|
3866
|
+
}
|
|
3867
|
+
[WRITE_LIST_SCROLL_STATE](patch, source) {
|
|
3868
|
+
this.#writeScrollState(patch, source);
|
|
3869
|
+
}
|
|
3870
|
+
#writeScrollState(patch, source) {
|
|
3871
|
+
let changed = false;
|
|
3872
|
+
if ("position" in patch) {
|
|
3873
|
+
const nextPosition = normalizePosition(patch.position);
|
|
3874
|
+
if (!Object.is(this.#position, nextPosition)) {
|
|
3875
|
+
this.#position = nextPosition;
|
|
3876
|
+
changed = true;
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
if ("offset" in patch) {
|
|
3880
|
+
const nextOffset = normalizeOffset$1(patch.offset ?? 0);
|
|
3881
|
+
if (!Object.is(this.#offset, nextOffset)) {
|
|
3882
|
+
this.#offset = nextOffset;
|
|
3883
|
+
changed = true;
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
if (!changed) return;
|
|
3887
|
+
markListScrollMutation(this, source);
|
|
3814
3888
|
}
|
|
3815
3889
|
};
|
|
3816
3890
|
//#endregion
|
|
@@ -3875,11 +3949,15 @@ function memoRenderItemBy(keyOf, renderItem, options = {}) {
|
|
|
3875
3949
|
}
|
|
3876
3950
|
//#endregion
|
|
3877
3951
|
//#region src/renderer/virtualized/base-animation.ts
|
|
3952
|
+
const CONTROLLED_STATE_OFFSET_EPSILON = 1e-9;
|
|
3878
3953
|
function clamp$1(value, min, max) {
|
|
3879
3954
|
return Math.min(Math.max(value, min), max);
|
|
3880
3955
|
}
|
|
3881
3956
|
function sameState(state, position, offset) {
|
|
3882
|
-
|
|
3957
|
+
if (!Object.is(state.position, position)) return false;
|
|
3958
|
+
if (Object.is(state.offset, offset)) return true;
|
|
3959
|
+
if (!Number.isFinite(state.offset) || !Number.isFinite(offset)) return false;
|
|
3960
|
+
return Math.abs(state.offset - offset) <= CONTROLLED_STATE_OFFSET_EPSILON;
|
|
3883
3961
|
}
|
|
3884
3962
|
function resolveJumpSegmentIndex(anchor, direction, itemCount) {
|
|
3885
3963
|
if (itemCount <= 0) return;
|
|
@@ -3964,117 +4042,190 @@ function getNow() {
|
|
|
3964
4042
|
//#region src/renderer/virtualized/frame-session.ts
|
|
3965
4043
|
function prepareFrameSession(params) {
|
|
3966
4044
|
let solution = params.resolveVisibleWindow(params.now);
|
|
3967
|
-
|
|
3968
|
-
params.captureVisibleItemSnapshot(solution, viewportTranslateY);
|
|
4045
|
+
params.captureVisibleItemSnapshot(solution);
|
|
3969
4046
|
const requestSettleRedraw = params.pruneTransitionAnimations(solution.window, params.now);
|
|
3970
4047
|
if (requestSettleRedraw) {
|
|
3971
4048
|
solution = params.resolveVisibleWindow(params.now);
|
|
3972
|
-
|
|
3973
|
-
params.captureVisibleItemSnapshot(solution, viewportTranslateY);
|
|
4049
|
+
params.captureVisibleItemSnapshot(solution);
|
|
3974
4050
|
}
|
|
3975
4051
|
return {
|
|
3976
4052
|
solution,
|
|
3977
|
-
viewportTranslateY,
|
|
3978
4053
|
requestSettleRedraw
|
|
3979
4054
|
};
|
|
3980
4055
|
}
|
|
3981
4056
|
//#endregion
|
|
3982
4057
|
//#region src/renderer/virtualized/jump-controller.ts
|
|
3983
|
-
var JumpController = class {
|
|
3984
|
-
|
|
3985
|
-
#
|
|
4058
|
+
var JumpController = class JumpController {
|
|
4059
|
+
static TRANSITION_SETTLE_SNAP_DURATION = 120;
|
|
4060
|
+
#canAutoFollowTop = false;
|
|
4061
|
+
#canAutoFollowBottom = false;
|
|
4062
|
+
#pendingAutoFollowRecomputeTop = true;
|
|
4063
|
+
#pendingAutoFollowRecomputeBottom = true;
|
|
4064
|
+
#pendingAutoFollowRecomputeReasonTop = "init";
|
|
4065
|
+
#pendingAutoFollowRecomputeReasonBottom = "init";
|
|
4066
|
+
#pendingTransitionSettleReconcile = false;
|
|
4067
|
+
#lastArmedAutoFollowBoundary;
|
|
4068
|
+
#lastObservedRenderedAutoFollowTop = false;
|
|
4069
|
+
#lastObservedRenderedAutoFollowBottom = false;
|
|
4070
|
+
#lastViewportWidth;
|
|
4071
|
+
#lastHandledScrollMutationVersion;
|
|
3986
4072
|
#jumpAnimation;
|
|
3987
|
-
#
|
|
3988
|
-
#
|
|
4073
|
+
#pendingPostJumpBoundary;
|
|
4074
|
+
#pendingPostJumpBoundaryBlocked = false;
|
|
3989
4075
|
#options;
|
|
3990
4076
|
constructor(options) {
|
|
3991
4077
|
this.#options = options;
|
|
4078
|
+
this.#lastHandledScrollMutationVersion = this.#options.readScrollMutation().version;
|
|
3992
4079
|
}
|
|
3993
4080
|
beforeFrame() {
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
4081
|
+
this.#handlePendingExternalScrollMutation();
|
|
4082
|
+
}
|
|
4083
|
+
noteViewportWidth(width) {
|
|
4084
|
+
if (!Number.isFinite(width)) return;
|
|
4085
|
+
if (this.#lastViewportWidth == null) {
|
|
4086
|
+
this.#lastViewportWidth = width;
|
|
4087
|
+
return;
|
|
4088
|
+
}
|
|
4089
|
+
if (Object.is(this.#lastViewportWidth, width)) return;
|
|
4090
|
+
this.#lastViewportWidth = width;
|
|
4091
|
+
this.#clearPendingPostJumpBoundary();
|
|
4092
|
+
this.#clearPendingTransitionSettleReconcile();
|
|
4093
|
+
this.#markAutoFollowRecompute(void 0, "viewport-width-change");
|
|
3997
4094
|
}
|
|
3998
4095
|
prepare(now) {
|
|
4096
|
+
if (this.#handlePendingExternalScrollMutation()) return false;
|
|
3999
4097
|
const animation = this.#jumpAnimation;
|
|
4000
4098
|
if (animation == null) return false;
|
|
4001
4099
|
if (this.#options.getItemCount() === 0) {
|
|
4002
4100
|
this.#cancelJumpAnimation();
|
|
4003
4101
|
return false;
|
|
4004
4102
|
}
|
|
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
4103
|
const progress = getProgress(animation.startTime, animation.duration, now);
|
|
4011
4104
|
const eased = progress >= 1 ? 1 : smoothstep(progress);
|
|
4012
4105
|
const anchor = getAnchorAtDistance(animation.path, animation.path.totalDistance * eased);
|
|
4013
4106
|
this.#options.applyAnchor(anchor);
|
|
4014
4107
|
animation.needsMoreFrames = progress < 1;
|
|
4108
|
+
if (!animation.needsMoreFrames && this.#pendingPostJumpBoundary != null && !this.#pendingPostJumpBoundaryBlocked) this.#armAutoFollowBoundary(this.#pendingPostJumpBoundary, "jump-to-boundary-settle");
|
|
4015
4109
|
return animation.needsMoreFrames;
|
|
4016
4110
|
}
|
|
4017
4111
|
finishFrame(requestRedraw) {
|
|
4018
4112
|
const animation = this.#jumpAnimation;
|
|
4019
4113
|
if (animation == null) return requestRedraw;
|
|
4020
|
-
if (animation.needsMoreFrames)
|
|
4021
|
-
|
|
4022
|
-
return true;
|
|
4023
|
-
}
|
|
4114
|
+
if (animation.needsMoreFrames) return true;
|
|
4115
|
+
const boundary = this.#pendingPostJumpBoundaryBlocked === true ? void 0 : this.#pendingPostJumpBoundary;
|
|
4024
4116
|
const onComplete = animation.onComplete;
|
|
4025
4117
|
this.#cancelJumpAnimation();
|
|
4118
|
+
this.#clearPendingPostJumpBoundary();
|
|
4119
|
+
if (boundary != null) this.#armAutoFollowBoundary(boundary, "jump-to-boundary-settle");
|
|
4026
4120
|
onComplete?.();
|
|
4027
4121
|
return requestRedraw || this.#jumpAnimation != null;
|
|
4028
4122
|
}
|
|
4029
4123
|
commit(state) {
|
|
4030
|
-
this.#
|
|
4031
|
-
position: state.position,
|
|
4032
|
-
offset: state.offset
|
|
4033
|
-
};
|
|
4124
|
+
this.#lastHandledScrollMutationVersion = this.#options.readScrollMutation().version;
|
|
4034
4125
|
}
|
|
4035
4126
|
jumpTo(index, options = {}) {
|
|
4036
|
-
this.#
|
|
4127
|
+
this.#clearPendingTransitionSettleReconcile();
|
|
4128
|
+
this.#clearPendingPostJumpBoundary();
|
|
4037
4129
|
if (this.#options.getItemCount() === 0) {
|
|
4038
4130
|
this.#cancelJumpAnimation();
|
|
4039
4131
|
return;
|
|
4040
4132
|
}
|
|
4041
|
-
this.#startJumpToIndex(index, options
|
|
4133
|
+
this.#startJumpToIndex(index, options);
|
|
4134
|
+
}
|
|
4135
|
+
jumpToBoundary(boundary, options = {}) {
|
|
4136
|
+
this.#clearPendingTransitionSettleReconcile();
|
|
4137
|
+
this.#clearPendingPostJumpBoundary();
|
|
4138
|
+
this.#armAutoFollowBoundary(boundary, "jump-to-boundary");
|
|
4139
|
+
if (this.#options.getItemCount() === 0) {
|
|
4140
|
+
this.#cancelJumpAnimation();
|
|
4141
|
+
return;
|
|
4142
|
+
}
|
|
4143
|
+
this.#startJumpToIndex(boundary === "bottom" ? this.#options.getItemCount() - 1 : 0, {
|
|
4144
|
+
...options,
|
|
4145
|
+
block: boundary === "bottom" ? "end" : "start"
|
|
4146
|
+
});
|
|
4147
|
+
}
|
|
4148
|
+
recomputeAutoFollowCapabilities(capabilities) {
|
|
4149
|
+
const previouslyObservedDualBoundary = this.#lastObservedRenderedAutoFollowTop && this.#lastObservedRenderedAutoFollowBottom;
|
|
4150
|
+
if (capabilities.top && capabilities.bottom && !previouslyObservedDualBoundary) {
|
|
4151
|
+
this.#setAutoFollowBoundary("top", true, "dual-boundary-promotion");
|
|
4152
|
+
this.#setAutoFollowBoundary("bottom", true, "dual-boundary-promotion");
|
|
4153
|
+
}
|
|
4154
|
+
if (this.#pendingAutoFollowRecomputeTop) {
|
|
4155
|
+
this.#setAutoFollowBoundary("top", capabilities.top, `strict-recompute:${this.#pendingAutoFollowRecomputeReasonTop}`);
|
|
4156
|
+
this.#pendingAutoFollowRecomputeTop = false;
|
|
4157
|
+
}
|
|
4158
|
+
if (this.#pendingAutoFollowRecomputeBottom) {
|
|
4159
|
+
this.#setAutoFollowBoundary("bottom", capabilities.bottom, `strict-recompute:${this.#pendingAutoFollowRecomputeReasonBottom}`);
|
|
4160
|
+
this.#pendingAutoFollowRecomputeBottom = false;
|
|
4161
|
+
}
|
|
4162
|
+
this.#syncLastArmedBoundaryFromLatchedState();
|
|
4163
|
+
if (this.#pendingTransitionSettleReconcile) {
|
|
4164
|
+
this.#reconcileLatchedAutoFollowAfterTransitionSettle(capabilities);
|
|
4165
|
+
this.#pendingTransitionSettleReconcile = false;
|
|
4166
|
+
}
|
|
4167
|
+
this.#lastObservedRenderedAutoFollowTop = capabilities.top;
|
|
4168
|
+
this.#lastObservedRenderedAutoFollowBottom = capabilities.bottom;
|
|
4169
|
+
return this.getAutoFollowCapabilities();
|
|
4170
|
+
}
|
|
4171
|
+
getAutoFollowCapabilities() {
|
|
4172
|
+
return {
|
|
4173
|
+
top: this.#canAutoFollowTop,
|
|
4174
|
+
bottom: this.#canAutoFollowBottom
|
|
4175
|
+
};
|
|
4176
|
+
}
|
|
4177
|
+
reconcileAutoFollowAfterTransitionSettle() {
|
|
4178
|
+
this.#pendingTransitionSettleReconcile = true;
|
|
4042
4179
|
}
|
|
4043
4180
|
handleListStateChange(change) {
|
|
4044
|
-
|
|
4181
|
+
switch (change.type) {
|
|
4182
|
+
case "reset":
|
|
4183
|
+
case "set":
|
|
4184
|
+
this.#cancelJumpAnimation();
|
|
4185
|
+
this.#clearPendingPostJumpBoundary();
|
|
4186
|
+
this.#clearPendingTransitionSettleReconcile();
|
|
4187
|
+
this.#syncScrollMutationVersion();
|
|
4188
|
+
this.#markAutoFollowRecompute(void 0, change.type);
|
|
4189
|
+
return change;
|
|
4190
|
+
case "push":
|
|
4191
|
+
case "unshift": return this.#handleBoundaryInsert(change);
|
|
4192
|
+
default: return change;
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
#handleBoundaryInsert(change) {
|
|
4196
|
+
if (this.#handlePendingExternalScrollMutation()) return change;
|
|
4197
|
+
this.#clearPendingTransitionSettleReconcile();
|
|
4045
4198
|
const followChange = this.#resolveAutoFollowChange(change);
|
|
4046
|
-
const
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
if (
|
|
4050
|
-
|
|
4051
|
-
this.#
|
|
4052
|
-
this.#
|
|
4053
|
-
block: followChange.direction === "push" ? "end" : "start",
|
|
4054
|
-
duration: followChange.animation?.duration
|
|
4055
|
-
}, {
|
|
4056
|
-
kind: "auto-follow",
|
|
4057
|
-
direction: followChange.direction
|
|
4058
|
-
});
|
|
4059
|
-
return {
|
|
4060
|
-
...followChange.change,
|
|
4061
|
-
animation: void 0
|
|
4062
|
-
};
|
|
4199
|
+
const boundary = change.type === "push" ? "bottom" : "top";
|
|
4200
|
+
if (this.#pendingPostJumpBoundary === boundary) this.#pendingPostJumpBoundaryBlocked = true;
|
|
4201
|
+
if (followChange == null || !this.#hasAutoFollowCapability(followChange.boundary)) return change;
|
|
4202
|
+
if (this.#canAutoFollowTop && this.#canAutoFollowBottom && this.#lastObservedRenderedAutoFollowTop && this.#lastObservedRenderedAutoFollowBottom) {
|
|
4203
|
+
const otherBoundary = followChange.boundary === "top" ? "bottom" : "top";
|
|
4204
|
+
this.#setAutoFollowBoundary(otherBoundary, false, "boundary-insert-narrow");
|
|
4205
|
+
this.#lastArmedAutoFollowBoundary = followChange.boundary;
|
|
4063
4206
|
}
|
|
4207
|
+
this.#clearPendingPostJumpBoundary();
|
|
4208
|
+
this.#materializeAnimatedAnchor(getNow(), followChange.direction, followChange.count);
|
|
4209
|
+
this.#startJumpToIndex(followChange.boundary === "bottom" ? this.#options.getItemCount() - 1 : 0, {
|
|
4210
|
+
block: followChange.boundary === "bottom" ? "end" : "start",
|
|
4211
|
+
duration: followChange.animation?.duration
|
|
4212
|
+
});
|
|
4064
4213
|
return change;
|
|
4065
4214
|
}
|
|
4066
4215
|
#cancelJumpAnimation() {
|
|
4067
4216
|
this.#jumpAnimation = void 0;
|
|
4068
|
-
this.#controlledState = void 0;
|
|
4069
4217
|
}
|
|
4070
|
-
#startJumpToIndex(index, options
|
|
4218
|
+
#startJumpToIndex(index, options) {
|
|
4071
4219
|
const targetIndex = this.#options.clampItemIndex(index);
|
|
4072
|
-
const currentState = this.#options.normalizeListState(this.#options.readListState());
|
|
4073
4220
|
const targetBlock = options.block ?? this.#options.getDefaultJumpBlock();
|
|
4221
|
+
const settleBoundary = this.#resolveBoundaryLatchTarget(targetIndex, targetBlock);
|
|
4222
|
+
this.#materializeAnimatedAnchor(getNow());
|
|
4223
|
+
const currentState = this.#options.normalizeListState(this.#options.readListState());
|
|
4074
4224
|
const targetAnchor = this.#options.getTargetAnchor(targetIndex, targetBlock);
|
|
4075
4225
|
if (!(options.animated ?? true)) {
|
|
4076
4226
|
this.#cancelJumpAnimation();
|
|
4077
4227
|
this.#options.applyAnchor(targetAnchor);
|
|
4228
|
+
if (settleBoundary != null) this.#armAutoFollowBoundary(settleBoundary, "jump-to-boundary-instant");
|
|
4078
4229
|
options.onComplete?.();
|
|
4079
4230
|
return;
|
|
4080
4231
|
}
|
|
@@ -4082,6 +4233,7 @@ var JumpController = class {
|
|
|
4082
4233
|
if (!Number.isFinite(startAnchor)) {
|
|
4083
4234
|
this.#cancelJumpAnimation();
|
|
4084
4235
|
this.#options.applyAnchor(targetAnchor);
|
|
4236
|
+
if (settleBoundary != null) this.#armAutoFollowBoundary(settleBoundary, "jump-to-boundary-instant");
|
|
4085
4237
|
options.onComplete?.();
|
|
4086
4238
|
return;
|
|
4087
4239
|
}
|
|
@@ -4090,24 +4242,34 @@ var JumpController = class {
|
|
|
4090
4242
|
if (duration <= 0 || path.totalDistance <= Number.EPSILON) {
|
|
4091
4243
|
this.#cancelJumpAnimation();
|
|
4092
4244
|
this.#options.applyAnchor(targetAnchor);
|
|
4245
|
+
if (settleBoundary != null) this.#armAutoFollowBoundary(settleBoundary, "jump-to-boundary-instant");
|
|
4093
4246
|
options.onComplete?.();
|
|
4094
4247
|
return;
|
|
4095
4248
|
}
|
|
4249
|
+
if (settleBoundary != null) {
|
|
4250
|
+
this.#pendingPostJumpBoundary = settleBoundary;
|
|
4251
|
+
this.#pendingPostJumpBoundaryBlocked = false;
|
|
4252
|
+
}
|
|
4096
4253
|
this.#jumpAnimation = {
|
|
4097
4254
|
path,
|
|
4098
4255
|
startTime: getNow(),
|
|
4099
4256
|
duration,
|
|
4100
4257
|
needsMoreFrames: true,
|
|
4101
|
-
onComplete: options.onComplete
|
|
4102
|
-
source
|
|
4258
|
+
onComplete: options.onComplete
|
|
4103
4259
|
};
|
|
4104
|
-
|
|
4260
|
+
}
|
|
4261
|
+
#resolveBoundaryLatchTarget(index, block) {
|
|
4262
|
+
const itemCount = this.#options.getItemCount();
|
|
4263
|
+
if (itemCount <= 0) return;
|
|
4264
|
+
if (index === 0 && block === "start") return "top";
|
|
4265
|
+
if (index === itemCount - 1 && block === "end") return "bottom";
|
|
4105
4266
|
}
|
|
4106
4267
|
#resolveAutoFollowChange(change) {
|
|
4107
4268
|
switch (change.type) {
|
|
4108
4269
|
case "push":
|
|
4109
|
-
case "unshift": return change.animation?.
|
|
4270
|
+
case "unshift": return change.animation?.autoFollow === true ? {
|
|
4110
4271
|
change,
|
|
4272
|
+
boundary: change.type === "push" ? "bottom" : "top",
|
|
4111
4273
|
direction: change.type,
|
|
4112
4274
|
count: change.count,
|
|
4113
4275
|
animation: change.animation
|
|
@@ -4115,41 +4277,95 @@ var JumpController = class {
|
|
|
4115
4277
|
default: return;
|
|
4116
4278
|
}
|
|
4117
4279
|
}
|
|
4118
|
-
#
|
|
4119
|
-
|
|
4120
|
-
return this.#options.canAutoFollowBoundaryInsert(direction, count, this.#options.readListState().position, this.#options.readListState().offset);
|
|
4280
|
+
#hasAutoFollowCapability(boundary) {
|
|
4281
|
+
return boundary === "top" ? this.#canAutoFollowTop : this.#canAutoFollowBottom;
|
|
4121
4282
|
}
|
|
4122
|
-
#
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4283
|
+
#armAutoFollowBoundary(boundary, reason) {
|
|
4284
|
+
this.#setAutoFollowBoundary(boundary, true, reason);
|
|
4285
|
+
this.#lastArmedAutoFollowBoundary = boundary;
|
|
4286
|
+
if (boundary === "top") {
|
|
4287
|
+
this.#pendingAutoFollowRecomputeTop = false;
|
|
4288
|
+
return;
|
|
4289
|
+
}
|
|
4290
|
+
this.#pendingAutoFollowRecomputeBottom = false;
|
|
4291
|
+
}
|
|
4292
|
+
#markAutoFollowRecompute(boundary, reason) {
|
|
4293
|
+
if (boundary == null || boundary === "top") {
|
|
4294
|
+
this.#pendingAutoFollowRecomputeTop = true;
|
|
4295
|
+
this.#pendingAutoFollowRecomputeReasonTop = reason;
|
|
4127
4296
|
}
|
|
4128
|
-
|
|
4297
|
+
if (boundary == null || boundary === "bottom") {
|
|
4298
|
+
this.#pendingAutoFollowRecomputeBottom = true;
|
|
4299
|
+
this.#pendingAutoFollowRecomputeReasonBottom = reason;
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
#clearPendingPostJumpBoundary() {
|
|
4303
|
+
this.#pendingPostJumpBoundary = void 0;
|
|
4304
|
+
this.#pendingPostJumpBoundaryBlocked = false;
|
|
4129
4305
|
}
|
|
4130
|
-
#
|
|
4131
|
-
|
|
4132
|
-
return this.#jumpAnimation?.source.kind === "auto-follow" ? this.#jumpAnimation.source.direction === direction : false;
|
|
4306
|
+
#clearPendingTransitionSettleReconcile() {
|
|
4307
|
+
this.#pendingTransitionSettleReconcile = false;
|
|
4133
4308
|
}
|
|
4134
|
-
#
|
|
4309
|
+
#materializeAnimatedAnchor(now, direction, count = 0) {
|
|
4135
4310
|
const animation = this.#jumpAnimation;
|
|
4136
4311
|
if (animation == null) return;
|
|
4137
4312
|
const progress = getProgress(animation.startTime, animation.duration, now);
|
|
4138
4313
|
const eased = progress >= 1 ? 1 : smoothstep(progress);
|
|
4139
|
-
|
|
4314
|
+
let anchor = getAnchorAtDistance(animation.path, animation.path.totalDistance * eased);
|
|
4315
|
+
if (direction === "unshift") anchor += count;
|
|
4140
4316
|
this.#cancelJumpAnimation();
|
|
4141
|
-
this.#options.applyAnchor(
|
|
4317
|
+
this.#options.applyAnchor(anchor);
|
|
4142
4318
|
}
|
|
4143
|
-
#
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4319
|
+
#setAutoFollowBoundary(boundary, value, reason) {
|
|
4320
|
+
if (boundary === "top") this.#canAutoFollowTop = value;
|
|
4321
|
+
else this.#canAutoFollowBottom = value;
|
|
4322
|
+
}
|
|
4323
|
+
#syncLastArmedBoundaryFromLatchedState() {
|
|
4324
|
+
if (this.#canAutoFollowTop === this.#canAutoFollowBottom) return;
|
|
4325
|
+
this.#lastArmedAutoFollowBoundary = this.#canAutoFollowTop ? "top" : "bottom";
|
|
4326
|
+
}
|
|
4327
|
+
#reconcileLatchedAutoFollowAfterTransitionSettle(capabilities) {
|
|
4328
|
+
if (!this.#canAutoFollowTop && !this.#canAutoFollowBottom) return;
|
|
4329
|
+
const preferredBoundary = this.#resolvePreferredLatchedBoundary(capabilities);
|
|
4330
|
+
if (preferredBoundary == null) return;
|
|
4331
|
+
const otherBoundary = preferredBoundary === "top" ? "bottom" : "top";
|
|
4332
|
+
if (this.#hasAutoFollowCapability(otherBoundary)) this.#setAutoFollowBoundary(otherBoundary, false, "strict-recompute:set");
|
|
4333
|
+
if (!this.#readCapabilityForBoundary(capabilities, preferredBoundary)) this.#startTransitionSettleSnap(preferredBoundary);
|
|
4334
|
+
this.#syncLastArmedBoundaryFromLatchedState();
|
|
4335
|
+
}
|
|
4336
|
+
#resolvePreferredLatchedBoundary(capabilities) {
|
|
4337
|
+
if (this.#canAutoFollowTop && this.#canAutoFollowBottom) {
|
|
4338
|
+
if (capabilities.top && capabilities.bottom) return;
|
|
4339
|
+
if (this.#lastArmedAutoFollowBoundary != null) return this.#lastArmedAutoFollowBoundary;
|
|
4340
|
+
if (capabilities.top !== capabilities.bottom) return capabilities.top ? "top" : "bottom";
|
|
4341
|
+
return "bottom";
|
|
4342
|
+
}
|
|
4343
|
+
if (this.#canAutoFollowTop) return "top";
|
|
4344
|
+
if (this.#canAutoFollowBottom) return "bottom";
|
|
4345
|
+
}
|
|
4346
|
+
#readCapabilityForBoundary(capabilities, boundary) {
|
|
4347
|
+
return boundary === "top" ? capabilities.top : capabilities.bottom;
|
|
4348
|
+
}
|
|
4349
|
+
#startTransitionSettleSnap(boundary) {
|
|
4350
|
+
if (this.#options.getItemCount() <= 0) return;
|
|
4351
|
+
this.#startJumpToIndex(boundary === "bottom" ? this.#options.getItemCount() - 1 : 0, {
|
|
4352
|
+
block: boundary === "bottom" ? "end" : "start",
|
|
4353
|
+
duration: JumpController.TRANSITION_SETTLE_SNAP_DURATION
|
|
4354
|
+
});
|
|
4150
4355
|
}
|
|
4151
|
-
#
|
|
4152
|
-
this.#
|
|
4356
|
+
#syncScrollMutationVersion() {
|
|
4357
|
+
this.#lastHandledScrollMutationVersion = this.#options.readScrollMutation().version;
|
|
4358
|
+
}
|
|
4359
|
+
#handlePendingExternalScrollMutation() {
|
|
4360
|
+
const mutation = this.#options.readScrollMutation();
|
|
4361
|
+
if (mutation.version === this.#lastHandledScrollMutationVersion) return false;
|
|
4362
|
+
this.#lastHandledScrollMutationVersion = mutation.version;
|
|
4363
|
+
if (mutation.source !== "external") return false;
|
|
4364
|
+
this.#cancelJumpAnimation();
|
|
4365
|
+
this.#clearPendingPostJumpBoundary();
|
|
4366
|
+
this.#clearPendingTransitionSettleReconcile();
|
|
4367
|
+
this.#markAutoFollowRecompute(void 0, "manual-scroll");
|
|
4368
|
+
return true;
|
|
4153
4369
|
}
|
|
4154
4370
|
};
|
|
4155
4371
|
//#endregion
|
|
@@ -4157,53 +4373,92 @@ var JumpController = class {
|
|
|
4157
4373
|
var VisibilitySnapshot = class {
|
|
4158
4374
|
#drawnItems = /* @__PURE__ */ new Set();
|
|
4159
4375
|
#visibleItems = /* @__PURE__ */ new Set();
|
|
4376
|
+
#previousVisibleItems = /* @__PURE__ */ new Set();
|
|
4160
4377
|
#hasSnapshot = false;
|
|
4161
4378
|
#snapshotState;
|
|
4379
|
+
#previousSnapshotState;
|
|
4162
4380
|
#emptyState;
|
|
4163
4381
|
#coversShortList = false;
|
|
4164
|
-
#topGap = 0;
|
|
4165
|
-
#bottomGap = 0;
|
|
4166
4382
|
#atStartBoundary = false;
|
|
4167
4383
|
#atEndBoundary = false;
|
|
4384
|
+
#minDrawnIndex = Number.POSITIVE_INFINITY;
|
|
4385
|
+
#maxDrawnIndex = Number.NEGATIVE_INFINITY;
|
|
4386
|
+
#topBoundaryItem;
|
|
4387
|
+
#bottomBoundaryItem;
|
|
4168
4388
|
get coversShortList() {
|
|
4169
4389
|
return this.#hasSnapshot && this.#snapshotState != null && this.#coversShortList;
|
|
4170
4390
|
}
|
|
4171
|
-
get
|
|
4172
|
-
return this.#
|
|
4391
|
+
get hasSnapshot() {
|
|
4392
|
+
return this.#hasSnapshot;
|
|
4393
|
+
}
|
|
4394
|
+
get previousState() {
|
|
4395
|
+
return this.#previousSnapshotState;
|
|
4173
4396
|
}
|
|
4174
|
-
|
|
4175
|
-
|
|
4397
|
+
readDrawnIndexRange() {
|
|
4398
|
+
if (!Number.isFinite(this.#minDrawnIndex) || !Number.isFinite(this.#maxDrawnIndex)) return;
|
|
4399
|
+
return {
|
|
4400
|
+
minIndex: this.#minDrawnIndex,
|
|
4401
|
+
maxIndex: this.#maxDrawnIndex
|
|
4402
|
+
};
|
|
4176
4403
|
}
|
|
4177
|
-
|
|
4404
|
+
readBoundaryItem(boundary) {
|
|
4405
|
+
return boundary === "top" ? this.#topBoundaryItem : this.#bottomBoundaryItem;
|
|
4406
|
+
}
|
|
4407
|
+
capture(window, _resolutionPath, items, viewport, snapshotState, readVisibleRange, readOuterVisibleRange) {
|
|
4408
|
+
this.#previousVisibleItems = this.#visibleItems;
|
|
4409
|
+
this.#previousSnapshotState = this.#snapshotState;
|
|
4178
4410
|
const nextDrawnItems = /* @__PURE__ */ new Set();
|
|
4179
4411
|
const nextVisibleItems = /* @__PURE__ */ new Set();
|
|
4180
4412
|
let minVisibleIndex = Number.POSITIVE_INFINITY;
|
|
4181
4413
|
let maxVisibleIndex = Number.NEGATIVE_INFINITY;
|
|
4182
4414
|
let topMostY = Number.POSITIVE_INFINITY;
|
|
4183
4415
|
let bottomMostY = Number.NEGATIVE_INFINITY;
|
|
4184
|
-
|
|
4416
|
+
let nextMinDrawnIndex = Number.POSITIVE_INFINITY;
|
|
4417
|
+
let nextMaxDrawnIndex = Number.NEGATIVE_INFINITY;
|
|
4418
|
+
let nextTopBoundaryItem;
|
|
4419
|
+
let nextBottomBoundaryItem;
|
|
4420
|
+
let nextTopBoundaryY = Number.POSITIVE_INFINITY;
|
|
4421
|
+
let nextBottomBoundaryY = Number.NEGATIVE_INFINITY;
|
|
4422
|
+
const effectiveShift = window.shift;
|
|
4423
|
+
const contentOriginY = viewport.contentTop;
|
|
4185
4424
|
for (const { idx, offset, height } of window.drawList) {
|
|
4186
|
-
|
|
4187
|
-
maxVisibleIndex = Math.max(maxVisibleIndex, idx);
|
|
4188
|
-
const y = offset + effectiveShift;
|
|
4425
|
+
const y = offset + effectiveShift + contentOriginY;
|
|
4189
4426
|
topMostY = Math.min(topMostY, y);
|
|
4190
4427
|
bottomMostY = Math.max(bottomMostY, y + height);
|
|
4191
4428
|
const item = items[idx];
|
|
4192
|
-
if (item != null
|
|
4193
|
-
|
|
4194
|
-
|
|
4429
|
+
if (item != null && readOuterVisibleRange(y, height) != null) {
|
|
4430
|
+
nextDrawnItems.add(item);
|
|
4431
|
+
nextMinDrawnIndex = Math.min(nextMinDrawnIndex, idx);
|
|
4432
|
+
nextMaxDrawnIndex = Math.max(nextMaxDrawnIndex, idx);
|
|
4433
|
+
}
|
|
4434
|
+
if (item == null) continue;
|
|
4435
|
+
if (readVisibleRange(y, height) != null) {
|
|
4436
|
+
minVisibleIndex = Math.min(minVisibleIndex, idx);
|
|
4437
|
+
maxVisibleIndex = Math.max(maxVisibleIndex, idx);
|
|
4438
|
+
nextVisibleItems.add(item);
|
|
4439
|
+
if (y < nextTopBoundaryY) {
|
|
4440
|
+
nextTopBoundaryY = y;
|
|
4441
|
+
nextTopBoundaryItem = item;
|
|
4442
|
+
}
|
|
4443
|
+
if (y + height > nextBottomBoundaryY) {
|
|
4444
|
+
nextBottomBoundaryY = y + height;
|
|
4445
|
+
nextBottomBoundaryItem = item;
|
|
4446
|
+
}
|
|
4447
|
+
}
|
|
4195
4448
|
}
|
|
4196
4449
|
this.#drawnItems = nextDrawnItems;
|
|
4197
4450
|
this.#visibleItems = nextVisibleItems;
|
|
4198
4451
|
this.#hasSnapshot = true;
|
|
4199
4452
|
this.#snapshotState = snapshotState;
|
|
4453
|
+
this.#minDrawnIndex = nextMinDrawnIndex;
|
|
4454
|
+
this.#maxDrawnIndex = nextMaxDrawnIndex;
|
|
4455
|
+
this.#topBoundaryItem = nextTopBoundaryItem;
|
|
4456
|
+
this.#bottomBoundaryItem = nextBottomBoundaryItem;
|
|
4200
4457
|
this.#emptyState = items.length === 0 && window.drawList.length === 0 ? snapshotState : void 0;
|
|
4201
4458
|
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 >=
|
|
4203
|
-
this.#
|
|
4204
|
-
this.#
|
|
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;
|
|
4459
|
+
this.#coversShortList = window.drawList.length > 0 && items.length > 0 && window.drawList.length === items.length && minVisibleIndex === 0 && maxVisibleIndex === items.length - 1 && topMostY >= viewport.contentTop - 1e-6 && bottomMostY <= viewport.contentBottom + 1e-6 && contentHeight < viewport.contentHeight - 1e-6;
|
|
4460
|
+
this.#atStartBoundary = window.drawList.length > 0 && items.length > 0 && minVisibleIndex === 0 && topMostY >= viewport.contentTop - 1e-6;
|
|
4461
|
+
this.#atEndBoundary = window.drawList.length > 0 && items.length > 0 && maxVisibleIndex === items.length - 1 && bottomMostY <= viewport.contentBottom + 1e-6;
|
|
4207
4462
|
}
|
|
4208
4463
|
matchesCurrentState(position, offset) {
|
|
4209
4464
|
return this.#hasSnapshot && this.#snapshotState != null && sameState(this.#snapshotState, position, offset);
|
|
@@ -4228,20 +4483,27 @@ var VisibilitySnapshot = class {
|
|
|
4228
4483
|
isVisible(item) {
|
|
4229
4484
|
return this.#visibleItems.has(item);
|
|
4230
4485
|
}
|
|
4486
|
+
wasVisible(item) {
|
|
4487
|
+
return this.#previousVisibleItems.has(item);
|
|
4488
|
+
}
|
|
4231
4489
|
tracks(item, retention) {
|
|
4232
4490
|
return retention === "drawn" ? this.#drawnItems.has(item) : this.#visibleItems.has(item);
|
|
4233
4491
|
}
|
|
4234
4492
|
reset() {
|
|
4235
4493
|
this.#drawnItems.clear();
|
|
4236
4494
|
this.#visibleItems.clear();
|
|
4495
|
+
this.#previousVisibleItems.clear();
|
|
4237
4496
|
this.#hasSnapshot = false;
|
|
4238
4497
|
this.#snapshotState = void 0;
|
|
4498
|
+
this.#previousSnapshotState = void 0;
|
|
4239
4499
|
this.#emptyState = void 0;
|
|
4240
4500
|
this.#coversShortList = false;
|
|
4241
|
-
this.#topGap = 0;
|
|
4242
|
-
this.#bottomGap = 0;
|
|
4243
4501
|
this.#atStartBoundary = false;
|
|
4244
4502
|
this.#atEndBoundary = false;
|
|
4503
|
+
this.#minDrawnIndex = Number.POSITIVE_INFINITY;
|
|
4504
|
+
this.#maxDrawnIndex = Number.NEGATIVE_INFINITY;
|
|
4505
|
+
this.#topBoundaryItem = void 0;
|
|
4506
|
+
this.#bottomBoundaryItem = void 0;
|
|
4245
4507
|
}
|
|
4246
4508
|
#matchesStateAfterBoundaryInsert(direction, count, position, offset) {
|
|
4247
4509
|
const snapshotState = this.#snapshotState;
|
|
@@ -4290,7 +4552,7 @@ var TransitionStore = class {
|
|
|
4290
4552
|
}));
|
|
4291
4553
|
}
|
|
4292
4554
|
findInvisible(snapshot) {
|
|
4293
|
-
return [...this.#transitions.entries()].filter(([item, transition]) => !snapshot.tracks(item, transition.retention)).map(([item, transition]) => ({
|
|
4555
|
+
return [...this.#transitions.entries()].filter(([item, transition]) => !snapshot.tracks(item, transition.retention) && !(transition.kind === "insert" && !snapshot.wasVisible(item))).map(([item, transition]) => ({
|
|
4294
4556
|
item,
|
|
4295
4557
|
transition
|
|
4296
4558
|
}));
|
|
@@ -4304,9 +4566,6 @@ var TransitionStore = class {
|
|
|
4304
4566
|
};
|
|
4305
4567
|
//#endregion
|
|
4306
4568
|
//#region src/renderer/virtualized/transition-planner.ts
|
|
4307
|
-
function isFinitePositive(value) {
|
|
4308
|
-
return Number.isFinite(value) && value > 0;
|
|
4309
|
-
}
|
|
4310
4569
|
function normalizeDuration(duration) {
|
|
4311
4570
|
return Math.max(0, typeof duration === "number" && Number.isFinite(duration) ? duration : 0);
|
|
4312
4571
|
}
|
|
@@ -4338,13 +4597,16 @@ function isIndexVisible(index, resolveVisibleWindow, readVisibleRange) {
|
|
|
4338
4597
|
}
|
|
4339
4598
|
function resolveAnimationEligibility(params) {
|
|
4340
4599
|
if (params.index < 0) return false;
|
|
4341
|
-
if (params.snapshot.matchesCurrentState(params.position, params.offset)) return params.snapshot.
|
|
4342
|
-
return isIndexVisible(params.index, params.resolveVisibleWindow, params.
|
|
4600
|
+
if (params.snapshot.matchesCurrentState(params.position, params.offset)) return params.snapshot.tracks(params.item, "drawn");
|
|
4601
|
+
return isIndexVisible(params.index, params.resolveVisibleWindow, params.readOuterVisibleRange);
|
|
4343
4602
|
}
|
|
4344
|
-
function
|
|
4345
|
-
if (
|
|
4346
|
-
|
|
4347
|
-
|
|
4603
|
+
function hasVisibleBoundaryInsertItems(direction, count, ctx) {
|
|
4604
|
+
if (count <= 0) return false;
|
|
4605
|
+
const start = direction === "push" ? ctx.items.length - count : 0;
|
|
4606
|
+
const end = direction === "push" ? ctx.items.length : Math.min(count, ctx.items.length);
|
|
4607
|
+
if (start < 0 || end <= start) return false;
|
|
4608
|
+
const solution = ctx.resolveVisibleWindow();
|
|
4609
|
+
return solution.window.drawList.some((entry) => entry.idx >= start && entry.idx < end && ctx.readOuterVisibleRange(entry.offset + solution.window.shift, entry.height) != null);
|
|
4348
4610
|
}
|
|
4349
4611
|
function sampleScalarAnimation(animation, now) {
|
|
4350
4612
|
return interpolate(animation.from, animation.to, animation.startTime, animation.duration, now);
|
|
@@ -4377,68 +4639,31 @@ function planExistingItemTransition(params) {
|
|
|
4377
4639
|
kind: "update",
|
|
4378
4640
|
layers,
|
|
4379
4641
|
height: createScalarAnimation(params.currentVisualState.height, params.nextHeight, params.now, params.duration),
|
|
4380
|
-
retention: "
|
|
4642
|
+
retention: "drawn"
|
|
4381
4643
|
};
|
|
4382
4644
|
}
|
|
4383
4645
|
return {
|
|
4384
4646
|
kind: "delete",
|
|
4385
4647
|
layers,
|
|
4386
4648
|
height: createScalarAnimation(params.currentVisualState.height, 0, params.now, params.duration),
|
|
4387
|
-
retention: "
|
|
4649
|
+
retention: "drawn"
|
|
4388
4650
|
};
|
|
4389
4651
|
}
|
|
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);
|
|
4399
|
-
}
|
|
4400
|
-
}
|
|
4401
4652
|
function planBoundaryInsertItems(params) {
|
|
4402
4653
|
const entries = [];
|
|
4403
|
-
const signedDistance = params.direction === "push" ? 1 : -1;
|
|
4404
4654
|
for (const { item, node, height } of params.measuredItems) {
|
|
4405
4655
|
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
4656
|
entries.push({
|
|
4408
4657
|
item,
|
|
4409
4658
|
transition: {
|
|
4410
4659
|
kind: "insert",
|
|
4411
|
-
layers: [createLayerAnimation(node, 0, 1, params.now, params.duration,
|
|
4412
|
-
height: createScalarAnimation(height, height, params.now, params.duration),
|
|
4660
|
+
layers: [createLayerAnimation(node, 0, 1, params.now, params.duration, 0, 0)],
|
|
4661
|
+
height: createScalarAnimation(params.animateHeight ? 0 : height, height, params.now, params.duration),
|
|
4413
4662
|
retention: "drawn"
|
|
4414
4663
|
}
|
|
4415
4664
|
});
|
|
4416
4665
|
}
|
|
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
|
-
};
|
|
4666
|
+
return entries.length === 0 ? void 0 : { entries };
|
|
4442
4667
|
}
|
|
4443
4668
|
function measureBoundaryInsertItems(direction, count, ctx) {
|
|
4444
4669
|
const start = direction === "push" ? ctx.items.length - count : 0;
|
|
@@ -4466,6 +4691,11 @@ function drawSampledLayers(sampled, y, adapter) {
|
|
|
4466
4691
|
if (alpha <= .001) continue;
|
|
4467
4692
|
adapter.graphics.save();
|
|
4468
4693
|
try {
|
|
4694
|
+
if (sampled.kind === "insert") {
|
|
4695
|
+
adapter.graphics.beginPath();
|
|
4696
|
+
adapter.graphics.rect(0, y, adapter.graphics.canvas.clientWidth, sampled.slotHeight);
|
|
4697
|
+
adapter.graphics.clip();
|
|
4698
|
+
}
|
|
4469
4699
|
if (typeof adapter.graphics.globalAlpha === "number") adapter.graphics.globalAlpha *= alpha;
|
|
4470
4700
|
if (adapter.drawNode(layer.node, 0, y + layer.translateY)) result = true;
|
|
4471
4701
|
} finally {
|
|
@@ -4489,7 +4719,7 @@ function planUpdateTransition(prevItem, nextItem, duration, now, currentVisualSt
|
|
|
4489
4719
|
snapshot,
|
|
4490
4720
|
hasActiveTransition: store.has(prevItem),
|
|
4491
4721
|
resolveVisibleWindow: ctx.resolveVisibleWindow,
|
|
4492
|
-
|
|
4722
|
+
readOuterVisibleRange: ctx.readOuterVisibleRange
|
|
4493
4723
|
}),
|
|
4494
4724
|
now,
|
|
4495
4725
|
currentVisualState,
|
|
@@ -4510,27 +4740,26 @@ function planDeleteTransition(item, duration, now, currentVisualState, ctx, snap
|
|
|
4510
4740
|
snapshot,
|
|
4511
4741
|
hasActiveTransition: store.has(item),
|
|
4512
4742
|
resolveVisibleWindow: ctx.resolveVisibleWindow,
|
|
4513
|
-
|
|
4743
|
+
readOuterVisibleRange: ctx.readOuterVisibleRange
|
|
4514
4744
|
}),
|
|
4515
4745
|
now,
|
|
4516
4746
|
currentVisualState
|
|
4517
4747
|
});
|
|
4518
4748
|
}
|
|
4519
|
-
function planBoundaryInsertTransition(direction, count, duration,
|
|
4749
|
+
function planBoundaryInsertTransition(direction, count, duration, now, ctx, snapshot) {
|
|
4520
4750
|
const normalizedDuration = normalizeDuration(duration);
|
|
4521
4751
|
if (count <= 0 || normalizedDuration <= 0) return;
|
|
4522
|
-
const
|
|
4523
|
-
|
|
4752
|
+
const matchesBoundaryState = snapshot.matchesBoundaryInsertState(direction, count, ctx.position, ctx.offset);
|
|
4753
|
+
const matchesFollowState = snapshot.matchesFollowBoundaryInsertState(direction, count, ctx.position, ctx.offset);
|
|
4754
|
+
const matchesEmptyState = snapshot.matchesEmptyBoundaryInsertState(direction, count, ctx.position, ctx.offset);
|
|
4755
|
+
if (!(matchesBoundaryState || matchesFollowState || matchesEmptyState || snapshot.hasSnapshot && hasVisibleBoundaryInsertItems(direction, count, ctx))) return;
|
|
4756
|
+
const animateHeight = !(direction === "unshift" && matchesFollowState && !matchesBoundaryState && !matchesEmptyState);
|
|
4524
4757
|
const measuredItems = measureBoundaryInsertItems(direction, count, ctx);
|
|
4525
4758
|
if (measuredItems == null) return;
|
|
4526
|
-
return
|
|
4527
|
-
direction,
|
|
4759
|
+
return planBoundaryInsertItems({
|
|
4528
4760
|
duration: normalizedDuration,
|
|
4529
|
-
|
|
4761
|
+
animateHeight,
|
|
4530
4762
|
now,
|
|
4531
|
-
strategy,
|
|
4532
|
-
snapshot,
|
|
4533
|
-
currentTranslateY,
|
|
4534
4763
|
measuredItems
|
|
4535
4764
|
});
|
|
4536
4765
|
}
|
|
@@ -4583,7 +4812,7 @@ function readCurrentVisualState(item, now, store, adapter) {
|
|
|
4583
4812
|
translateY: 0
|
|
4584
4813
|
};
|
|
4585
4814
|
}
|
|
4586
|
-
function handleTransitionStateChange(store, snapshot,
|
|
4815
|
+
function handleTransitionStateChange(store, snapshot, change, ctx, lifecycle) {
|
|
4587
4816
|
switch (change.type) {
|
|
4588
4817
|
case "update": {
|
|
4589
4818
|
const now = getNow();
|
|
@@ -4591,10 +4820,10 @@ function handleTransitionStateChange(store, snapshot, currentViewportTranslateY,
|
|
|
4591
4820
|
const transition = planUpdateTransition(change.prevItem, change.nextItem, change.animation?.duration, now, currentVisualState, ctx, snapshot, store);
|
|
4592
4821
|
if (transition == null) {
|
|
4593
4822
|
store.delete(change.prevItem);
|
|
4594
|
-
return
|
|
4823
|
+
return;
|
|
4595
4824
|
}
|
|
4596
4825
|
store.replace(change.prevItem, change.nextItem, transition);
|
|
4597
|
-
return
|
|
4826
|
+
return;
|
|
4598
4827
|
}
|
|
4599
4828
|
case "delete": {
|
|
4600
4829
|
const now = getNow();
|
|
@@ -4603,51 +4832,61 @@ function handleTransitionStateChange(store, snapshot, currentViewportTranslateY,
|
|
|
4603
4832
|
if (transition == null) {
|
|
4604
4833
|
store.delete(change.item);
|
|
4605
4834
|
lifecycle.onDeleteComplete(change.item);
|
|
4606
|
-
return
|
|
4835
|
+
return;
|
|
4607
4836
|
}
|
|
4608
4837
|
store.set(change.item, transition);
|
|
4609
|
-
return
|
|
4838
|
+
return;
|
|
4610
4839
|
}
|
|
4611
4840
|
case "delete-finalize":
|
|
4612
4841
|
store.delete(change.item);
|
|
4613
|
-
return
|
|
4842
|
+
return;
|
|
4614
4843
|
case "unshift":
|
|
4615
4844
|
case "push": {
|
|
4616
4845
|
const now = getNow();
|
|
4617
|
-
const plan = planBoundaryInsertTransition(change.type, change.count, change.animation?.duration,
|
|
4618
|
-
if (plan == null) return
|
|
4619
|
-
if (plan.kind === "viewport-slide") return { viewportAnimation: plan.animation };
|
|
4846
|
+
const plan = planBoundaryInsertTransition(change.type, change.count, change.animation?.duration, now, ctx, snapshot);
|
|
4847
|
+
if (plan == null) return;
|
|
4620
4848
|
for (const entry of plan.entries) store.set(entry.item, entry.transition);
|
|
4621
|
-
|
|
4849
|
+
if (ctx.position == null && snapshot.coversShortList && (change.type === "push" && ctx.anchorMode === "bottom" || change.type === "unshift" && ctx.anchorMode === "top")) {
|
|
4850
|
+
const boundary = change.type === "push" ? "bottom" : "top";
|
|
4851
|
+
const boundaryItem = snapshot.readBoundaryItem(boundary);
|
|
4852
|
+
if (boundaryItem != null) lifecycle.snapItemToViewportBoundary(boundaryItem, boundary);
|
|
4853
|
+
}
|
|
4854
|
+
return;
|
|
4622
4855
|
}
|
|
4623
4856
|
case "reset":
|
|
4624
4857
|
case "set":
|
|
4625
4858
|
store.reset();
|
|
4626
4859
|
snapshot.reset();
|
|
4627
|
-
return
|
|
4860
|
+
return;
|
|
4628
4861
|
}
|
|
4629
4862
|
}
|
|
4630
4863
|
//#endregion
|
|
4631
4864
|
//#region src/renderer/virtualized/base-transition.ts
|
|
4865
|
+
function remapAnchorAfterDeletes(anchor, deletedIndices) {
|
|
4866
|
+
if (!Number.isFinite(anchor) || deletedIndices.length === 0) return anchor;
|
|
4867
|
+
const sortedIndices = [...deletedIndices].filter((index) => Number.isFinite(index) && index >= 0).sort((a, b) => a - b);
|
|
4868
|
+
let removedBeforeAnchor = 0;
|
|
4869
|
+
for (const index of sortedIndices) {
|
|
4870
|
+
if (anchor > index + 1) {
|
|
4871
|
+
removedBeforeAnchor += 1;
|
|
4872
|
+
continue;
|
|
4873
|
+
}
|
|
4874
|
+
if (anchor >= index) return index - removedBeforeAnchor;
|
|
4875
|
+
}
|
|
4876
|
+
return anchor - removedBeforeAnchor;
|
|
4877
|
+
}
|
|
4632
4878
|
var TransitionController = class {
|
|
4633
4879
|
#store = new TransitionStore();
|
|
4634
4880
|
#snapshot = new VisibilitySnapshot();
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
this.#snapshot.capture(window, resolutionPath, items, viewportHeight, snapshotState, extraShift, readVisibleRange);
|
|
4881
|
+
captureVisibilitySnapshot(window, resolutionPath, items, viewport, snapshotState, readVisibleRange, readOuterVisibleRange) {
|
|
4882
|
+
this.#snapshot.capture(window, resolutionPath, items, viewport, snapshotState, readVisibleRange, readOuterVisibleRange);
|
|
4638
4883
|
}
|
|
4639
|
-
pruneInvisible(lifecycle) {
|
|
4640
|
-
return this.pruneInvisibleAt(getNow(), lifecycle);
|
|
4884
|
+
pruneInvisible(ctx, lifecycle) {
|
|
4885
|
+
return this.pruneInvisibleAt(getNow(), ctx, lifecycle);
|
|
4641
4886
|
}
|
|
4642
4887
|
prepare(now, lifecycle) {
|
|
4643
4888
|
this.settle(now, lifecycle);
|
|
4644
|
-
this.#
|
|
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);
|
|
4889
|
+
return this.#store.prepare(now);
|
|
4651
4890
|
}
|
|
4652
4891
|
canAutoFollowBoundaryInsert(direction, count, position, offset) {
|
|
4653
4892
|
return this.#snapshot.matchesFollowBoundaryInsertState(direction, count, position, offset);
|
|
@@ -4661,81 +4900,422 @@ var TransitionController = class {
|
|
|
4661
4900
|
handleListStateChange(change, ctx, lifecycle) {
|
|
4662
4901
|
const now = getNow();
|
|
4663
4902
|
this.settle(now, lifecycle);
|
|
4664
|
-
|
|
4665
|
-
if (change.type === "reset" || change.type === "set") {
|
|
4666
|
-
this.#viewportTranslateAnimation = void 0;
|
|
4667
|
-
return;
|
|
4668
|
-
}
|
|
4669
|
-
if (result.viewportAnimation != null) this.#viewportTranslateAnimation = result.viewportAnimation;
|
|
4903
|
+
handleTransitionStateChange(this.#store, this.#snapshot, change, ctx, lifecycle);
|
|
4670
4904
|
}
|
|
4671
4905
|
settle(now, lifecycle) {
|
|
4672
|
-
|
|
4673
|
-
this.#cleanupViewportTranslateAnimation(now);
|
|
4674
|
-
return changed;
|
|
4906
|
+
return this.#settleTransitions(this.#store.findCompleted(now), now, lifecycle);
|
|
4675
4907
|
}
|
|
4676
|
-
pruneInvisibleAt(now, lifecycle) {
|
|
4677
|
-
|
|
4908
|
+
pruneInvisibleAt(now, ctx, lifecycle) {
|
|
4909
|
+
const removals = this.#store.findInvisible(this.#snapshot);
|
|
4910
|
+
return this.#settleTransitions(removals, now, lifecycle, this.#resolveNaturalBoundarySnap(removals, now, ctx, lifecycle));
|
|
4678
4911
|
}
|
|
4679
4912
|
reset() {
|
|
4680
4913
|
this.#store.reset();
|
|
4681
4914
|
this.#snapshot.reset();
|
|
4682
|
-
this.#viewportTranslateAnimation = void 0;
|
|
4683
4915
|
}
|
|
4684
|
-
#
|
|
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) {
|
|
4916
|
+
#settleTransitions(removals, now, lifecycle, boundarySnap) {
|
|
4690
4917
|
if (removals.length === 0) return false;
|
|
4691
4918
|
const anchor = lifecycle.captureVisualAnchor(now);
|
|
4919
|
+
const beforeState = lifecycle.readScrollState();
|
|
4920
|
+
const completedDeleteIndices = [];
|
|
4692
4921
|
for (const { item, transition } of removals) {
|
|
4922
|
+
if (transition.kind === "delete") {
|
|
4923
|
+
const index = lifecycle.readItemIndex(item);
|
|
4924
|
+
if (index >= 0) completedDeleteIndices.push(index);
|
|
4925
|
+
}
|
|
4693
4926
|
this.#store.delete(item);
|
|
4694
4927
|
if (transition.kind === "delete") lifecycle.onDeleteComplete(item);
|
|
4695
4928
|
}
|
|
4696
|
-
if (anchor != null && Number.isFinite(anchor)) lifecycle.restoreVisualAnchor(anchor);
|
|
4929
|
+
if (anchor != null && Number.isFinite(anchor)) lifecycle.restoreVisualAnchor(remapAnchorAfterDeletes(anchor, completedDeleteIndices));
|
|
4930
|
+
if (boundarySnap != null) lifecycle.snapItemToViewportBoundary(boundarySnap.item, boundarySnap.boundary);
|
|
4931
|
+
const afterState = lifecycle.readScrollState();
|
|
4932
|
+
if (!sameState(beforeState, afterState.position, afterState.offset)) lifecycle.onTransitionSettleScrollAdjusted();
|
|
4697
4933
|
return true;
|
|
4698
4934
|
}
|
|
4935
|
+
#resolveNaturalBoundarySnap(removals, now, ctx, lifecycle) {
|
|
4936
|
+
const previousState = this.#snapshot.previousState;
|
|
4937
|
+
const drawnRange = this.#snapshot.readDrawnIndexRange();
|
|
4938
|
+
if (previousState == null || drawnRange == null) return;
|
|
4939
|
+
const naturalIndices = [];
|
|
4940
|
+
for (const { item, transition } of removals) {
|
|
4941
|
+
if (transition.kind !== "update" && transition.kind !== "delete") continue;
|
|
4942
|
+
const index = lifecycle.readItemIndex(item);
|
|
4943
|
+
if (index < 0 || !this.#snapshot.wasVisible(item)) return;
|
|
4944
|
+
if (this.#isTransitionVisibleInState(index, previousState, now, ctx)) return;
|
|
4945
|
+
naturalIndices.push(index);
|
|
4946
|
+
}
|
|
4947
|
+
if (naturalIndices.length === 0) return;
|
|
4948
|
+
if (naturalIndices.every((index) => index < drawnRange.minIndex)) {
|
|
4949
|
+
const item = this.#snapshot.readBoundaryItem("top");
|
|
4950
|
+
return item == null ? void 0 : {
|
|
4951
|
+
item,
|
|
4952
|
+
boundary: "top"
|
|
4953
|
+
};
|
|
4954
|
+
}
|
|
4955
|
+
if (naturalIndices.every((index) => index > drawnRange.maxIndex)) {
|
|
4956
|
+
const item = this.#snapshot.readBoundaryItem("bottom");
|
|
4957
|
+
return item == null ? void 0 : {
|
|
4958
|
+
item,
|
|
4959
|
+
boundary: "bottom"
|
|
4960
|
+
};
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4963
|
+
#isTransitionVisibleInState(index, state, now, ctx) {
|
|
4964
|
+
const solution = ctx.resolveVisibleWindowForState(state, now);
|
|
4965
|
+
for (const entry of solution.window.drawList) {
|
|
4966
|
+
if (entry.idx !== index) continue;
|
|
4967
|
+
return ctx.readOuterVisibleRange(entry.offset + solution.window.shift, entry.height) != null;
|
|
4968
|
+
}
|
|
4969
|
+
return false;
|
|
4970
|
+
}
|
|
4699
4971
|
};
|
|
4700
4972
|
//#endregion
|
|
4701
|
-
//#region src/renderer/virtualized/
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4973
|
+
//#region src/renderer/virtualized/solver.ts
|
|
4974
|
+
function clamp(value, min, max) {
|
|
4975
|
+
return Math.min(Math.max(value, min), max);
|
|
4976
|
+
}
|
|
4977
|
+
function normalizeOffset(offset) {
|
|
4978
|
+
return Number.isFinite(offset) ? offset : 0;
|
|
4979
|
+
}
|
|
4980
|
+
function normalizeListPadding(padding) {
|
|
4981
|
+
return {
|
|
4982
|
+
top: typeof padding?.top === "number" && Number.isFinite(padding.top) ? Math.max(0, padding.top) : 0,
|
|
4983
|
+
bottom: typeof padding?.bottom === "number" && Number.isFinite(padding.bottom) ? Math.max(0, padding.bottom) : 0
|
|
4984
|
+
};
|
|
4985
|
+
}
|
|
4986
|
+
function resolveListViewport(outerHeight, padding) {
|
|
4987
|
+
const height = typeof outerHeight === "number" && Number.isFinite(outerHeight) ? Math.max(0, outerHeight) : 0;
|
|
4988
|
+
const resolvedPadding = normalizeListPadding(padding);
|
|
4989
|
+
const contentTop = resolvedPadding.top;
|
|
4990
|
+
const contentBottom = Math.max(contentTop, height - resolvedPadding.bottom);
|
|
4991
|
+
return {
|
|
4992
|
+
outerHeight: height,
|
|
4993
|
+
contentTop,
|
|
4994
|
+
contentBottom,
|
|
4995
|
+
contentHeight: contentBottom - contentTop,
|
|
4996
|
+
outerContentTop: -contentTop,
|
|
4997
|
+
outerContentBottom: height - contentTop
|
|
4998
|
+
};
|
|
4999
|
+
}
|
|
5000
|
+
function resolveListLayoutOptions(options = {}) {
|
|
5001
|
+
return {
|
|
5002
|
+
anchorMode: options.anchorMode ?? "top",
|
|
5003
|
+
underflowAlign: options.underflowAlign ?? "top",
|
|
5004
|
+
padding: normalizeListPadding(options.padding)
|
|
5005
|
+
};
|
|
5006
|
+
}
|
|
5007
|
+
function normalizeVisibleState(itemCount, state, layout) {
|
|
5008
|
+
if (itemCount <= 0) return {
|
|
5009
|
+
position: 0,
|
|
5010
|
+
offset: 0
|
|
5011
|
+
};
|
|
5012
|
+
const position = state.position;
|
|
5013
|
+
const fallbackPosition = layout.anchorMode === "top" ? 0 : itemCount - 1;
|
|
5014
|
+
if (typeof position !== "number" || !Number.isFinite(position)) return {
|
|
5015
|
+
position: fallbackPosition,
|
|
5016
|
+
offset: normalizeOffset(state.offset)
|
|
5017
|
+
};
|
|
5018
|
+
return {
|
|
5019
|
+
position: clamp(Math.trunc(position), 0, itemCount - 1),
|
|
5020
|
+
offset: normalizeOffset(state.offset)
|
|
5021
|
+
};
|
|
5022
|
+
}
|
|
5023
|
+
function resolveVisibleWindow(items, state, viewportHeight, resolveItem, layout) {
|
|
5024
|
+
const viewport = typeof viewportHeight === "number" ? resolveListViewport(viewportHeight, layout.padding) : viewportHeight;
|
|
5025
|
+
const contentHeight = viewport.contentHeight;
|
|
5026
|
+
const normalizedState = normalizeVisibleState(items.length, state, layout);
|
|
5027
|
+
const resolutionPath = /* @__PURE__ */ new Set();
|
|
5028
|
+
const readResolvedItem = (item, idx) => {
|
|
5029
|
+
resolutionPath.add(idx);
|
|
5030
|
+
return resolveItem(item, idx);
|
|
5031
|
+
};
|
|
5032
|
+
if (items.length === 0) return {
|
|
5033
|
+
normalizedState,
|
|
5034
|
+
resolutionPath: [],
|
|
5035
|
+
window: {
|
|
5036
|
+
drawList: [],
|
|
5037
|
+
shift: 0
|
|
5038
|
+
}
|
|
5039
|
+
};
|
|
5040
|
+
if (layout.anchorMode === "top") {
|
|
5041
|
+
let { position, offset } = normalizedState;
|
|
5042
|
+
let drawLength = 0;
|
|
5043
|
+
if (offset > 0) if (position === 0) offset = 0;
|
|
5044
|
+
else {
|
|
5045
|
+
for (let i = position - 1; i >= 0; i -= 1) {
|
|
5046
|
+
const { height } = readResolvedItem(items[i], i);
|
|
5047
|
+
position = i;
|
|
5048
|
+
offset -= height;
|
|
5049
|
+
if (offset <= 0) break;
|
|
5050
|
+
}
|
|
5051
|
+
if (position === 0 && offset > 0) offset = 0;
|
|
5052
|
+
}
|
|
5053
|
+
let y = offset;
|
|
5054
|
+
const drawList = [];
|
|
5055
|
+
for (let i = position; i < items.length; i += 1) {
|
|
5056
|
+
const { value, height } = readResolvedItem(items[i], i);
|
|
5057
|
+
if (y + height > 0) {
|
|
5058
|
+
drawList.push({
|
|
5059
|
+
idx: i,
|
|
5060
|
+
value,
|
|
5061
|
+
offset: y,
|
|
5062
|
+
height
|
|
5063
|
+
});
|
|
5064
|
+
drawLength += height;
|
|
5065
|
+
} else {
|
|
5066
|
+
offset += height;
|
|
5067
|
+
position = i + 1;
|
|
5068
|
+
}
|
|
5069
|
+
y += height;
|
|
5070
|
+
if (y >= contentHeight) break;
|
|
5071
|
+
}
|
|
5072
|
+
let shift = 0;
|
|
5073
|
+
if (y < contentHeight) {
|
|
5074
|
+
if (drawList.length > 0 && drawList.at(-1)?.idx === items.length - 1 && !(drawList.at(-1)?.height > Number.EPSILON)) return finalizeVisibleWindowResult(items.length, viewport, layout, {
|
|
5075
|
+
position,
|
|
5076
|
+
offset
|
|
5077
|
+
}, Array.from(resolutionPath), extendVisibleWindowToOuterBounds(items, {
|
|
5078
|
+
drawList,
|
|
5079
|
+
shift
|
|
5080
|
+
}, viewport, readResolvedItem));
|
|
5081
|
+
if (position === 0 && drawLength < contentHeight) {
|
|
5082
|
+
shift = -offset;
|
|
5083
|
+
offset = 0;
|
|
5084
|
+
} else {
|
|
5085
|
+
shift = contentHeight - y;
|
|
5086
|
+
y = offset += shift;
|
|
5087
|
+
let lastIdx = -1;
|
|
5088
|
+
for (let i = position - 1; i >= 0; i -= 1) {
|
|
5089
|
+
const { value, height } = readResolvedItem(items[i], i);
|
|
5090
|
+
drawLength += height;
|
|
5091
|
+
y -= height;
|
|
5092
|
+
drawList.push({
|
|
5093
|
+
idx: i,
|
|
5094
|
+
value,
|
|
5095
|
+
offset: y - shift,
|
|
5096
|
+
height
|
|
5097
|
+
});
|
|
5098
|
+
lastIdx = i;
|
|
5099
|
+
if (y < 0) break;
|
|
5100
|
+
}
|
|
5101
|
+
if (lastIdx === 0 && drawLength < contentHeight) {
|
|
5102
|
+
shift = drawList.at(-1)?.offset == null ? 0 : -drawList.at(-1).offset;
|
|
5103
|
+
position = 0;
|
|
5104
|
+
offset = 0;
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5107
|
+
}
|
|
5108
|
+
return finalizeVisibleWindowResult(items.length, viewport, layout, {
|
|
5109
|
+
position,
|
|
5110
|
+
offset
|
|
5111
|
+
}, Array.from(resolutionPath), extendVisibleWindowToOuterBounds(items, {
|
|
5112
|
+
drawList,
|
|
5113
|
+
shift
|
|
5114
|
+
}, viewport, readResolvedItem));
|
|
4735
5115
|
}
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
5116
|
+
let { position, offset } = normalizedState;
|
|
5117
|
+
let drawLength = 0;
|
|
5118
|
+
if (offset < 0) if (position === items.length - 1) offset = 0;
|
|
5119
|
+
else for (let i = position + 1; i < items.length; i += 1) {
|
|
5120
|
+
const { height } = readResolvedItem(items[i], i);
|
|
5121
|
+
position = i;
|
|
5122
|
+
offset += height;
|
|
5123
|
+
if (offset > 0) break;
|
|
5124
|
+
}
|
|
5125
|
+
let y = contentHeight + offset;
|
|
5126
|
+
const drawList = [];
|
|
5127
|
+
for (let i = position; i >= 0; i -= 1) {
|
|
5128
|
+
const { value, height } = readResolvedItem(items[i], i);
|
|
5129
|
+
y -= height;
|
|
5130
|
+
if (y <= contentHeight) {
|
|
5131
|
+
drawList.push({
|
|
5132
|
+
idx: i,
|
|
5133
|
+
value,
|
|
5134
|
+
offset: y,
|
|
5135
|
+
height
|
|
5136
|
+
});
|
|
5137
|
+
drawLength += height;
|
|
5138
|
+
} else {
|
|
5139
|
+
offset -= height;
|
|
5140
|
+
position = i - 1;
|
|
5141
|
+
}
|
|
5142
|
+
if (y < 0) break;
|
|
5143
|
+
}
|
|
5144
|
+
let shift = 0;
|
|
5145
|
+
if (y > 0) {
|
|
5146
|
+
shift = -y;
|
|
5147
|
+
if (drawLength < contentHeight) {
|
|
5148
|
+
y = drawLength;
|
|
5149
|
+
for (let i = position + 1; i < items.length; i += 1) {
|
|
5150
|
+
const { value, height } = readResolvedItem(items[i], i);
|
|
5151
|
+
drawList.push({
|
|
5152
|
+
idx: i,
|
|
5153
|
+
value,
|
|
5154
|
+
offset: y - shift,
|
|
5155
|
+
height
|
|
5156
|
+
});
|
|
5157
|
+
y = drawLength += height;
|
|
5158
|
+
if (height > Number.EPSILON) position = i;
|
|
5159
|
+
if (y >= contentHeight) break;
|
|
5160
|
+
}
|
|
5161
|
+
offset = drawLength < contentHeight ? 0 : drawLength - contentHeight;
|
|
5162
|
+
} else offset = drawLength - contentHeight;
|
|
5163
|
+
}
|
|
5164
|
+
return finalizeVisibleWindowResult(items.length, viewport, layout, {
|
|
5165
|
+
position,
|
|
5166
|
+
offset
|
|
5167
|
+
}, Array.from(resolutionPath), extendVisibleWindowToOuterBounds(items, {
|
|
5168
|
+
drawList,
|
|
5169
|
+
shift
|
|
5170
|
+
}, viewport, readResolvedItem));
|
|
5171
|
+
}
|
|
5172
|
+
function finalizeVisibleWindowResult(itemCount, viewport, layout, normalizedState, resolutionPath, window) {
|
|
5173
|
+
const viewportHeight = viewport.contentHeight;
|
|
5174
|
+
if (window.drawList.length !== itemCount || itemCount <= 0) return {
|
|
5175
|
+
normalizedState,
|
|
5176
|
+
resolutionPath,
|
|
5177
|
+
window
|
|
5178
|
+
};
|
|
5179
|
+
let minIndex = Number.POSITIVE_INFINITY;
|
|
5180
|
+
let maxIndex = Number.NEGATIVE_INFINITY;
|
|
5181
|
+
let minOffset = Number.POSITIVE_INFINITY;
|
|
5182
|
+
let maxBottom = Number.NEGATIVE_INFINITY;
|
|
5183
|
+
let hasDeferredSlots = false;
|
|
5184
|
+
for (const entry of window.drawList) {
|
|
5185
|
+
if (!(entry.height > Number.EPSILON)) hasDeferredSlots = true;
|
|
5186
|
+
else {
|
|
5187
|
+
minOffset = Math.min(minOffset, entry.offset);
|
|
5188
|
+
maxBottom = Math.max(maxBottom, entry.offset + entry.height);
|
|
5189
|
+
}
|
|
5190
|
+
minIndex = Math.min(minIndex, entry.idx);
|
|
5191
|
+
maxIndex = Math.max(maxIndex, entry.idx);
|
|
5192
|
+
}
|
|
5193
|
+
if (!Number.isFinite(minOffset) || !Number.isFinite(maxBottom)) return {
|
|
5194
|
+
normalizedState,
|
|
5195
|
+
resolutionPath,
|
|
5196
|
+
window
|
|
5197
|
+
};
|
|
5198
|
+
const contentHeight = maxBottom - minOffset;
|
|
5199
|
+
if (minIndex !== 0 || maxIndex !== itemCount - 1 || !(contentHeight < viewportHeight - Number.EPSILON)) return {
|
|
5200
|
+
normalizedState,
|
|
5201
|
+
resolutionPath,
|
|
5202
|
+
window
|
|
5203
|
+
};
|
|
5204
|
+
const desiredTop = layout.underflowAlign === "bottom" ? viewportHeight - contentHeight : 0;
|
|
5205
|
+
return {
|
|
5206
|
+
normalizedState: hasDeferredSlots ? normalizedState : layout.anchorMode === "top" ? {
|
|
5207
|
+
position: 0,
|
|
5208
|
+
offset: 0
|
|
5209
|
+
} : {
|
|
5210
|
+
position: itemCount - 1,
|
|
5211
|
+
offset: 0
|
|
5212
|
+
},
|
|
5213
|
+
resolutionPath,
|
|
5214
|
+
window: {
|
|
5215
|
+
drawList: window.drawList,
|
|
5216
|
+
shift: desiredTop - minOffset
|
|
5217
|
+
}
|
|
5218
|
+
};
|
|
5219
|
+
}
|
|
5220
|
+
function extendVisibleWindowToOuterBounds(items, window, viewport, resolveItem) {
|
|
5221
|
+
if (window.drawList.length === 0 || items.length === 0) return window;
|
|
5222
|
+
const drawList = [...window.drawList];
|
|
5223
|
+
const existingIndices = new Set(drawList.map((entry) => entry.idx));
|
|
5224
|
+
let topEntry = drawList[0];
|
|
5225
|
+
let bottomEntry = drawList[0];
|
|
5226
|
+
for (const entry of drawList) {
|
|
5227
|
+
if (entry.offset < topEntry.offset) topEntry = entry;
|
|
5228
|
+
if (entry.offset + entry.height > bottomEntry.offset + bottomEntry.height) bottomEntry = entry;
|
|
5229
|
+
}
|
|
5230
|
+
let topIdx = topEntry.idx;
|
|
5231
|
+
let topY = topEntry.offset + window.shift;
|
|
5232
|
+
while (topIdx > 0) {
|
|
5233
|
+
const prevIdx = topIdx - 1;
|
|
5234
|
+
if (existingIndices.has(prevIdx)) {
|
|
5235
|
+
const existing = drawList.find((entry) => entry.idx === prevIdx);
|
|
5236
|
+
topIdx = prevIdx;
|
|
5237
|
+
if (existing != null) topY = existing.offset + window.shift;
|
|
5238
|
+
continue;
|
|
5239
|
+
}
|
|
5240
|
+
const { value, height } = resolveItem(items[prevIdx], prevIdx);
|
|
5241
|
+
const prevY = topY - height;
|
|
5242
|
+
if (prevY + height <= viewport.outerContentTop) break;
|
|
5243
|
+
drawList.push({
|
|
5244
|
+
idx: prevIdx,
|
|
5245
|
+
value,
|
|
5246
|
+
offset: prevY - window.shift,
|
|
5247
|
+
height
|
|
5248
|
+
});
|
|
5249
|
+
existingIndices.add(prevIdx);
|
|
5250
|
+
topIdx = prevIdx;
|
|
5251
|
+
topY = prevY;
|
|
5252
|
+
}
|
|
5253
|
+
let bottomIdx = bottomEntry.idx;
|
|
5254
|
+
let bottomY = bottomEntry.offset + window.shift + bottomEntry.height;
|
|
5255
|
+
while (bottomIdx < items.length - 1) {
|
|
5256
|
+
const nextIdx = bottomIdx + 1;
|
|
5257
|
+
if (existingIndices.has(nextIdx)) {
|
|
5258
|
+
const existing = drawList.find((entry) => entry.idx === nextIdx);
|
|
5259
|
+
bottomIdx = nextIdx;
|
|
5260
|
+
if (existing != null) bottomY = Math.max(bottomY, existing.offset + window.shift + existing.height);
|
|
5261
|
+
continue;
|
|
5262
|
+
}
|
|
5263
|
+
const { value, height } = resolveItem(items[nextIdx], nextIdx);
|
|
5264
|
+
if (bottomY >= viewport.outerContentBottom) break;
|
|
5265
|
+
drawList.push({
|
|
5266
|
+
idx: nextIdx,
|
|
5267
|
+
value,
|
|
5268
|
+
offset: bottomY - window.shift,
|
|
5269
|
+
height
|
|
5270
|
+
});
|
|
5271
|
+
existingIndices.add(nextIdx);
|
|
5272
|
+
bottomIdx = nextIdx;
|
|
5273
|
+
bottomY += height;
|
|
5274
|
+
}
|
|
5275
|
+
return {
|
|
5276
|
+
drawList,
|
|
5277
|
+
shift: window.shift
|
|
5278
|
+
};
|
|
5279
|
+
}
|
|
5280
|
+
//#endregion
|
|
5281
|
+
//#region src/renderer/virtualized/base.ts
|
|
5282
|
+
/**
|
|
5283
|
+
* Shared base class for virtualized list renderers.
|
|
5284
|
+
*/
|
|
5285
|
+
var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
5286
|
+
static MIN_JUMP_DURATION = 160;
|
|
5287
|
+
static MAX_JUMP_DURATION = 420;
|
|
5288
|
+
static JUMP_DURATION_PER_PIXEL = .7;
|
|
5289
|
+
#jumpController;
|
|
5290
|
+
#transitionController = new TransitionController();
|
|
5291
|
+
constructor(graphics, options) {
|
|
5292
|
+
super(graphics, options);
|
|
5293
|
+
this.#jumpController = new JumpController({
|
|
5294
|
+
minJumpDuration: VirtualizedRenderer.MIN_JUMP_DURATION,
|
|
5295
|
+
maxJumpDuration: VirtualizedRenderer.MAX_JUMP_DURATION,
|
|
5296
|
+
jumpDurationPerPixel: VirtualizedRenderer.JUMP_DURATION_PER_PIXEL,
|
|
5297
|
+
getItemCount: () => this.items.length,
|
|
5298
|
+
readListState: this._readListState.bind(this),
|
|
5299
|
+
readScrollMutation: () => readListScrollMutation(this.options.list),
|
|
5300
|
+
normalizeListState: this._normalizeListState.bind(this),
|
|
5301
|
+
readAnchor: (state) => this._readAnchor(state, this._getItemHeight.bind(this)),
|
|
5302
|
+
applyAnchor: this._applyAnchor.bind(this),
|
|
5303
|
+
getDefaultJumpBlock: this._getDefaultJumpBlock.bind(this),
|
|
5304
|
+
getTargetAnchor: this._getTargetAnchor.bind(this),
|
|
5305
|
+
clampItemIndex: this._clampItemIndex.bind(this),
|
|
5306
|
+
getItemHeight: this._getItemHeight.bind(this)
|
|
5307
|
+
});
|
|
5308
|
+
subscribeListState(options.list, this, (owner, change) => {
|
|
5309
|
+
owner.#handleListStateChange(change);
|
|
5310
|
+
});
|
|
5311
|
+
}
|
|
5312
|
+
/** Current anchor item index. */
|
|
5313
|
+
get position() {
|
|
5314
|
+
return this.options.list.position;
|
|
5315
|
+
}
|
|
5316
|
+
/** Updates the current anchor item index. */
|
|
5317
|
+
set position(value) {
|
|
5318
|
+
this.options.list.position = value;
|
|
4739
5319
|
}
|
|
4740
5320
|
/** Pixel offset from the anchored item edge. */
|
|
4741
5321
|
get offset() {
|
|
@@ -4756,6 +5336,7 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
4756
5336
|
/** Renders the current visible window. */
|
|
4757
5337
|
render(feedback) {
|
|
4758
5338
|
this.#jumpController.beforeFrame();
|
|
5339
|
+
this.#jumpController.noteViewportWidth(this.graphics.canvas.clientWidth);
|
|
4759
5340
|
const now = getNow();
|
|
4760
5341
|
const keepAnimating = this._prepareRender(now);
|
|
4761
5342
|
const { clientWidth: viewportWidth, clientHeight: viewportHeight } = this.graphics.canvas;
|
|
@@ -4763,27 +5344,32 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
4763
5344
|
const frame = prepareFrameSession({
|
|
4764
5345
|
now,
|
|
4765
5346
|
resolveVisibleWindow: (frameNow) => this._resolveVisibleWindow(frameNow),
|
|
4766
|
-
|
|
4767
|
-
captureVisibleItemSnapshot: (solution, extraShift) => this._captureVisibleItemSnapshot(solution, extraShift),
|
|
5347
|
+
captureVisibleItemSnapshot: (solution) => this._captureVisibleItemSnapshot(solution),
|
|
4768
5348
|
pruneTransitionAnimations: (window, frameNow) => this._pruneTransitionAnimations(window, frameNow)
|
|
4769
5349
|
});
|
|
4770
|
-
const
|
|
5350
|
+
const autoFollowCapabilities = this.#jumpController.recomputeAutoFollowCapabilities(this._readAutoFollowCapabilities(frame.solution.window));
|
|
5351
|
+
const requestRedraw = this._renderVisibleWindow(frame.solution.window, feedback);
|
|
5352
|
+
if (feedback != null) {
|
|
5353
|
+
feedback.canAutoFollowTop = autoFollowCapabilities.top;
|
|
5354
|
+
feedback.canAutoFollowBottom = autoFollowCapabilities.bottom;
|
|
5355
|
+
}
|
|
4771
5356
|
this._commitListState(frame.solution.normalizedState);
|
|
4772
5357
|
return this._finishRender(keepAnimating || requestRedraw || frame.requestSettleRedraw);
|
|
4773
5358
|
}
|
|
4774
5359
|
/** Hit-tests the current visible window. */
|
|
4775
5360
|
hittest(test) {
|
|
4776
5361
|
this.#jumpController.beforeFrame();
|
|
5362
|
+
this.#jumpController.noteViewportWidth(this.graphics.canvas.clientWidth);
|
|
4777
5363
|
const now = getNow();
|
|
4778
5364
|
this.#transitionController.settle(now, this.#getTransitionLifecycleAdapter());
|
|
4779
5365
|
const frame = prepareFrameSession({
|
|
4780
5366
|
now,
|
|
4781
5367
|
resolveVisibleWindow: (frameNow) => this._resolveVisibleWindow(frameNow),
|
|
4782
|
-
|
|
4783
|
-
captureVisibleItemSnapshot: (solution, extraShift) => this._captureVisibleItemSnapshot(solution, extraShift),
|
|
5368
|
+
captureVisibleItemSnapshot: (solution) => this._captureVisibleItemSnapshot(solution),
|
|
4784
5369
|
pruneTransitionAnimations: (window, frameNow) => this._pruneTransitionAnimations(window, frameNow)
|
|
4785
5370
|
});
|
|
4786
|
-
|
|
5371
|
+
this.#jumpController.recomputeAutoFollowCapabilities(this._readAutoFollowCapabilities(frame.solution.window));
|
|
5372
|
+
return this._hittestVisibleWindow(frame.solution.window, test);
|
|
4787
5373
|
}
|
|
4788
5374
|
_readListState() {
|
|
4789
5375
|
return {
|
|
@@ -4795,8 +5381,7 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
4795
5381
|
return this._resolveVisibleWindowForState(this._readListState(), now);
|
|
4796
5382
|
}
|
|
4797
5383
|
_commitListState(state) {
|
|
4798
|
-
this.
|
|
4799
|
-
this.offset = state.offset;
|
|
5384
|
+
writeInternalListScrollState(this.options.list, state);
|
|
4800
5385
|
this.#jumpController.commit(state);
|
|
4801
5386
|
}
|
|
4802
5387
|
/**
|
|
@@ -4805,12 +5390,26 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
4805
5390
|
jumpTo(index, options = {}) {
|
|
4806
5391
|
this.#jumpController.jumpTo(index, options);
|
|
4807
5392
|
}
|
|
5393
|
+
/**
|
|
5394
|
+
* Scrolls the viewport to the visual top edge and arms top auto-follow immediately.
|
|
5395
|
+
*/
|
|
5396
|
+
jumpToTop(options = {}) {
|
|
5397
|
+
this.#jumpController.jumpToBoundary("top", options);
|
|
5398
|
+
}
|
|
5399
|
+
/**
|
|
5400
|
+
* Scrolls the viewport to the visual bottom edge and arms bottom auto-follow immediately.
|
|
5401
|
+
*/
|
|
5402
|
+
jumpToBottom(options = {}) {
|
|
5403
|
+
this.#jumpController.jumpToBoundary("bottom", options);
|
|
5404
|
+
}
|
|
4808
5405
|
_resetRenderFeedback(feedback) {
|
|
4809
5406
|
if (feedback == null) return;
|
|
4810
5407
|
feedback.minIdx = NaN;
|
|
4811
5408
|
feedback.maxIdx = NaN;
|
|
4812
5409
|
feedback.min = NaN;
|
|
4813
5410
|
feedback.max = NaN;
|
|
5411
|
+
feedback.canAutoFollowTop = false;
|
|
5412
|
+
feedback.canAutoFollowBottom = false;
|
|
4814
5413
|
}
|
|
4815
5414
|
_accumulateRenderFeedback(feedback, idx, top, height) {
|
|
4816
5415
|
const visibleRange = this._readVisibleRange(top, height);
|
|
@@ -4824,20 +5423,53 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
4824
5423
|
}
|
|
4825
5424
|
_renderDrawList(list, shift, feedback) {
|
|
4826
5425
|
let result = false;
|
|
4827
|
-
const
|
|
5426
|
+
const viewport = this._getViewportMetrics();
|
|
4828
5427
|
for (const { idx, value: item, offset, height } of list) {
|
|
4829
|
-
const y = offset + shift;
|
|
5428
|
+
const y = offset + shift + viewport.contentTop;
|
|
4830
5429
|
if (feedback != null) this._accumulateRenderFeedback(feedback, idx, y, height);
|
|
4831
|
-
if (y + height < 0 || y >
|
|
5430
|
+
if (y + height < 0 || y > viewport.outerHeight) continue;
|
|
4832
5431
|
if (item.draw(y)) result = true;
|
|
4833
5432
|
}
|
|
4834
5433
|
return result;
|
|
4835
5434
|
}
|
|
4836
|
-
_renderVisibleWindow(window, feedback
|
|
5435
|
+
_renderVisibleWindow(window, feedback) {
|
|
4837
5436
|
this._resetRenderFeedback(feedback);
|
|
4838
|
-
return this._renderDrawList(window.drawList, window.shift
|
|
5437
|
+
return this._renderDrawList(window.drawList, window.shift, feedback);
|
|
5438
|
+
}
|
|
5439
|
+
_readAutoFollowCapabilities(window) {
|
|
5440
|
+
if (window.drawList.length === 0 || this.items.length === 0) return {
|
|
5441
|
+
top: false,
|
|
5442
|
+
bottom: false
|
|
5443
|
+
};
|
|
5444
|
+
let minIndex = Number.POSITIVE_INFINITY;
|
|
5445
|
+
let maxIndex = Number.NEGATIVE_INFINITY;
|
|
5446
|
+
let topMostY = Number.POSITIVE_INFINITY;
|
|
5447
|
+
let bottomMostY = Number.NEGATIVE_INFINITY;
|
|
5448
|
+
const viewport = this._getViewportMetrics();
|
|
5449
|
+
for (const { idx, offset, height } of window.drawList) {
|
|
5450
|
+
minIndex = Math.min(minIndex, idx);
|
|
5451
|
+
maxIndex = Math.max(maxIndex, idx);
|
|
5452
|
+
const y = offset + window.shift + viewport.contentTop;
|
|
5453
|
+
topMostY = Math.min(topMostY, y);
|
|
5454
|
+
bottomMostY = Math.max(bottomMostY, y + height);
|
|
5455
|
+
}
|
|
5456
|
+
return {
|
|
5457
|
+
top: minIndex === 0 && topMostY >= viewport.contentTop - 1e-6,
|
|
5458
|
+
bottom: maxIndex === this.items.length - 1 && bottomMostY <= viewport.contentBottom + 1e-6
|
|
5459
|
+
};
|
|
4839
5460
|
}
|
|
4840
5461
|
_readVisibleRange(top, height) {
|
|
5462
|
+
if (!Number.isFinite(top) || !Number.isFinite(height) || height <= 0) return;
|
|
5463
|
+
const viewport = this._getViewportMetrics();
|
|
5464
|
+
const visibleTop = clamp$1(viewport.contentTop - top, 0, height);
|
|
5465
|
+
const visibleBottom = clamp$1(viewport.contentBottom - top, 0, height);
|
|
5466
|
+
if (visibleBottom <= visibleTop) return;
|
|
5467
|
+
return {
|
|
5468
|
+
top: visibleTop,
|
|
5469
|
+
bottom: visibleBottom
|
|
5470
|
+
};
|
|
5471
|
+
}
|
|
5472
|
+
_readOuterVisibleRange(top, height) {
|
|
4841
5473
|
if (!Number.isFinite(top) || !Number.isFinite(height) || height <= 0) return;
|
|
4842
5474
|
const viewportHeight = this.graphics.canvas.clientHeight;
|
|
4843
5475
|
const visibleTop = clamp$1(-top, 0, height);
|
|
@@ -4849,19 +5481,21 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
4849
5481
|
};
|
|
4850
5482
|
}
|
|
4851
5483
|
_pruneTransitionAnimations(_window, now) {
|
|
4852
|
-
return this.#transitionController.pruneInvisibleAt(now, this.#getTransitionLifecycleAdapter());
|
|
5484
|
+
return this.#transitionController.pruneInvisibleAt(now, this.#getTransitionPlanningAdapter(), this.#getTransitionLifecycleAdapter());
|
|
4853
5485
|
}
|
|
4854
|
-
_hittestVisibleWindow(window, test
|
|
5486
|
+
_hittestVisibleWindow(window, test) {
|
|
5487
|
+
const viewport = this._getViewportMetrics();
|
|
4855
5488
|
for (const { value: item, offset, height } of window.drawList) {
|
|
4856
|
-
const y = offset + window.shift +
|
|
5489
|
+
const y = offset + window.shift + viewport.contentTop;
|
|
4857
5490
|
if (test.y < y || test.y >= y + height) continue;
|
|
4858
5491
|
return item.hittest(test, y);
|
|
4859
5492
|
}
|
|
4860
5493
|
return false;
|
|
4861
5494
|
}
|
|
4862
|
-
_captureVisibleItemSnapshot(solution
|
|
5495
|
+
_captureVisibleItemSnapshot(solution) {
|
|
4863
5496
|
const normalizedState = this._normalizeListState(this._readListState());
|
|
4864
|
-
|
|
5497
|
+
const viewport = this._getViewportMetrics();
|
|
5498
|
+
this.#transitionController.captureVisibilitySnapshot(solution.window, solution.resolutionPath, this.items, viewport, normalizedState, this._readVisibleRange.bind(this), this._readOuterVisibleRange.bind(this));
|
|
4865
5499
|
}
|
|
4866
5500
|
_prepareRender(now) {
|
|
4867
5501
|
const keepTransitioning = this.#transitionController.prepare(now, this.#getTransitionLifecycleAdapter());
|
|
@@ -4893,9 +5527,17 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
4893
5527
|
if (!Number.isFinite(anchor) || this.items.length <= 0) return;
|
|
4894
5528
|
this._applyAnchor(anchor);
|
|
4895
5529
|
}
|
|
5530
|
+
#snapItemToViewportBoundary(item, boundary) {
|
|
5531
|
+
const index = this.items.indexOf(item);
|
|
5532
|
+
if (index < 0) return;
|
|
5533
|
+
this._applyAnchor(this._getTargetAnchor(index, boundary === "top" ? "start" : "end"));
|
|
5534
|
+
}
|
|
4896
5535
|
_resolveItem(item, _index, now) {
|
|
4897
5536
|
return this.#transitionController.resolveItem(item, now, this.#getTransitionRenderAdapter(), this.#getTransitionLifecycleAdapter());
|
|
4898
5537
|
}
|
|
5538
|
+
_getViewportMetrics() {
|
|
5539
|
+
return resolveListViewport(this.graphics.canvas.clientHeight, this._getLayoutOptions().padding);
|
|
5540
|
+
}
|
|
4899
5541
|
#handleDeleteComplete(item) {
|
|
4900
5542
|
this.options.list.finalizeDelete(item);
|
|
4901
5543
|
}
|
|
@@ -4903,18 +5545,26 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
4903
5545
|
return {
|
|
4904
5546
|
onDeleteComplete: this.#handleDeleteComplete.bind(this),
|
|
4905
5547
|
captureVisualAnchor: this._readAnchorAt.bind(this),
|
|
4906
|
-
restoreVisualAnchor: this._restoreAnchor.bind(this)
|
|
5548
|
+
restoreVisualAnchor: this._restoreAnchor.bind(this),
|
|
5549
|
+
readScrollState: this._readListState.bind(this),
|
|
5550
|
+
readItemIndex: (item) => this.items.indexOf(item),
|
|
5551
|
+
snapItemToViewportBoundary: this.#snapItemToViewportBoundary.bind(this),
|
|
5552
|
+
onTransitionSettleScrollAdjusted: () => this.#jumpController.reconcileAutoFollowAfterTransitionSettle()
|
|
4907
5553
|
};
|
|
4908
5554
|
}
|
|
4909
5555
|
#getVirtualizedRuntime() {
|
|
5556
|
+
const viewport = this._getViewportMetrics();
|
|
4910
5557
|
return {
|
|
4911
5558
|
items: this.items,
|
|
4912
5559
|
position: this.position,
|
|
4913
5560
|
offset: this.offset,
|
|
4914
5561
|
renderItem: this.options.renderItem,
|
|
4915
5562
|
measureNode: this.measureRootNode.bind(this),
|
|
5563
|
+
viewport,
|
|
4916
5564
|
readVisibleRange: this._readVisibleRange.bind(this),
|
|
4917
|
-
|
|
5565
|
+
readOuterVisibleRange: this._readOuterVisibleRange.bind(this),
|
|
5566
|
+
resolveVisibleWindow: () => this._resolveVisibleWindow(getNow()),
|
|
5567
|
+
resolveVisibleWindowForState: (state, now) => this._resolveVisibleWindowForState(state, now)
|
|
4918
5568
|
};
|
|
4919
5569
|
}
|
|
4920
5570
|
#getTransitionRenderAdapter() {
|
|
@@ -4930,7 +5580,7 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
4930
5580
|
#getTransitionPlanningAdapter() {
|
|
4931
5581
|
return {
|
|
4932
5582
|
...this.#getVirtualizedRuntime(),
|
|
4933
|
-
|
|
5583
|
+
anchorMode: this._getLayoutOptions().anchorMode
|
|
4934
5584
|
};
|
|
4935
5585
|
}
|
|
4936
5586
|
#handleListStateChange(change) {
|
|
@@ -5007,212 +5657,6 @@ function getTargetAnchorForItem(itemCount, index, block, anchorMode, viewportHei
|
|
|
5007
5657
|
}
|
|
5008
5658
|
}
|
|
5009
5659
|
//#endregion
|
|
5010
|
-
//#region src/renderer/virtualized/solver.ts
|
|
5011
|
-
function clamp(value, min, max) {
|
|
5012
|
-
return Math.min(Math.max(value, min), max);
|
|
5013
|
-
}
|
|
5014
|
-
function normalizeOffset(offset) {
|
|
5015
|
-
return Number.isFinite(offset) ? offset : 0;
|
|
5016
|
-
}
|
|
5017
|
-
function resolveListLayoutOptions(options = {}) {
|
|
5018
|
-
return {
|
|
5019
|
-
anchorMode: options.anchorMode ?? "top",
|
|
5020
|
-
underflowAlign: options.underflowAlign ?? "top"
|
|
5021
|
-
};
|
|
5022
|
-
}
|
|
5023
|
-
function normalizeVisibleState(itemCount, state, layout) {
|
|
5024
|
-
if (itemCount <= 0) return {
|
|
5025
|
-
position: 0,
|
|
5026
|
-
offset: 0
|
|
5027
|
-
};
|
|
5028
|
-
const position = state.position;
|
|
5029
|
-
const fallbackPosition = layout.anchorMode === "top" ? 0 : itemCount - 1;
|
|
5030
|
-
if (typeof position !== "number" || !Number.isFinite(position)) return {
|
|
5031
|
-
position: fallbackPosition,
|
|
5032
|
-
offset: normalizeOffset(state.offset)
|
|
5033
|
-
};
|
|
5034
|
-
return {
|
|
5035
|
-
position: clamp(Math.trunc(position), 0, itemCount - 1),
|
|
5036
|
-
offset: normalizeOffset(state.offset)
|
|
5037
|
-
};
|
|
5038
|
-
}
|
|
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
|
-
};
|
|
5046
|
-
if (items.length === 0) return {
|
|
5047
|
-
normalizedState,
|
|
5048
|
-
resolutionPath: [],
|
|
5049
|
-
window: {
|
|
5050
|
-
drawList: [],
|
|
5051
|
-
shift: 0
|
|
5052
|
-
}
|
|
5053
|
-
};
|
|
5054
|
-
if (layout.anchorMode === "top") {
|
|
5055
|
-
let { position, offset } = normalizedState;
|
|
5056
|
-
let drawLength = 0;
|
|
5057
|
-
if (offset > 0) if (position === 0) offset = 0;
|
|
5058
|
-
else {
|
|
5059
|
-
for (let i = position - 1; i >= 0; i -= 1) {
|
|
5060
|
-
const { height } = readResolvedItem(items[i], i);
|
|
5061
|
-
position = i;
|
|
5062
|
-
offset -= height;
|
|
5063
|
-
if (offset <= 0) break;
|
|
5064
|
-
}
|
|
5065
|
-
if (position === 0 && offset > 0) offset = 0;
|
|
5066
|
-
}
|
|
5067
|
-
let y = offset;
|
|
5068
|
-
const drawList = [];
|
|
5069
|
-
for (let i = position; i < items.length; i += 1) {
|
|
5070
|
-
const { value, height } = readResolvedItem(items[i], i);
|
|
5071
|
-
if (y + height > 0) {
|
|
5072
|
-
drawList.push({
|
|
5073
|
-
idx: i,
|
|
5074
|
-
value,
|
|
5075
|
-
offset: y,
|
|
5076
|
-
height
|
|
5077
|
-
});
|
|
5078
|
-
drawLength += height;
|
|
5079
|
-
} else {
|
|
5080
|
-
offset += height;
|
|
5081
|
-
position = i + 1;
|
|
5082
|
-
}
|
|
5083
|
-
y += height;
|
|
5084
|
-
if (y >= viewportHeight) break;
|
|
5085
|
-
}
|
|
5086
|
-
let shift = 0;
|
|
5087
|
-
if (y < viewportHeight) if (position === 0 && drawLength < viewportHeight) {
|
|
5088
|
-
shift = -offset;
|
|
5089
|
-
offset = 0;
|
|
5090
|
-
} else {
|
|
5091
|
-
shift = viewportHeight - y;
|
|
5092
|
-
y = offset += shift;
|
|
5093
|
-
let lastIdx = -1;
|
|
5094
|
-
for (let i = position - 1; i >= 0; i -= 1) {
|
|
5095
|
-
const { value, height } = readResolvedItem(items[i], i);
|
|
5096
|
-
drawLength += height;
|
|
5097
|
-
y -= height;
|
|
5098
|
-
drawList.push({
|
|
5099
|
-
idx: i,
|
|
5100
|
-
value,
|
|
5101
|
-
offset: y - shift,
|
|
5102
|
-
height
|
|
5103
|
-
});
|
|
5104
|
-
lastIdx = i;
|
|
5105
|
-
if (y < 0) break;
|
|
5106
|
-
}
|
|
5107
|
-
if (lastIdx === 0 && drawLength < viewportHeight) {
|
|
5108
|
-
shift = drawList.at(-1)?.offset == null ? 0 : -drawList.at(-1).offset;
|
|
5109
|
-
position = 0;
|
|
5110
|
-
offset = 0;
|
|
5111
|
-
}
|
|
5112
|
-
}
|
|
5113
|
-
return finalizeVisibleWindowResult(items.length, viewportHeight, layout, {
|
|
5114
|
-
position,
|
|
5115
|
-
offset
|
|
5116
|
-
}, Array.from(resolutionPath), {
|
|
5117
|
-
drawList,
|
|
5118
|
-
shift
|
|
5119
|
-
});
|
|
5120
|
-
}
|
|
5121
|
-
let { position, offset } = normalizedState;
|
|
5122
|
-
let drawLength = 0;
|
|
5123
|
-
if (offset < 0) if (position === items.length - 1) offset = 0;
|
|
5124
|
-
else for (let i = position + 1; i < items.length; i += 1) {
|
|
5125
|
-
const { height } = readResolvedItem(items[i], i);
|
|
5126
|
-
position = i;
|
|
5127
|
-
offset += height;
|
|
5128
|
-
if (offset > 0) break;
|
|
5129
|
-
}
|
|
5130
|
-
let y = viewportHeight + offset;
|
|
5131
|
-
const drawList = [];
|
|
5132
|
-
for (let i = position; i >= 0; i -= 1) {
|
|
5133
|
-
const { value, height } = readResolvedItem(items[i], i);
|
|
5134
|
-
y -= height;
|
|
5135
|
-
if (y <= viewportHeight) {
|
|
5136
|
-
drawList.push({
|
|
5137
|
-
idx: i,
|
|
5138
|
-
value,
|
|
5139
|
-
offset: y,
|
|
5140
|
-
height
|
|
5141
|
-
});
|
|
5142
|
-
drawLength += height;
|
|
5143
|
-
} else {
|
|
5144
|
-
offset -= height;
|
|
5145
|
-
position = i - 1;
|
|
5146
|
-
}
|
|
5147
|
-
if (y < 0) break;
|
|
5148
|
-
}
|
|
5149
|
-
let shift = 0;
|
|
5150
|
-
if (y > 0) {
|
|
5151
|
-
shift = -y;
|
|
5152
|
-
if (drawLength < viewportHeight) {
|
|
5153
|
-
y = drawLength;
|
|
5154
|
-
for (let i = position + 1; i < items.length; i += 1) {
|
|
5155
|
-
const { value, height } = readResolvedItem(items[i], i);
|
|
5156
|
-
drawList.push({
|
|
5157
|
-
idx: i,
|
|
5158
|
-
value,
|
|
5159
|
-
offset: y - shift,
|
|
5160
|
-
height
|
|
5161
|
-
});
|
|
5162
|
-
y = drawLength += height;
|
|
5163
|
-
position = i;
|
|
5164
|
-
if (y >= viewportHeight) break;
|
|
5165
|
-
}
|
|
5166
|
-
offset = drawLength < viewportHeight ? 0 : drawLength - viewportHeight;
|
|
5167
|
-
} else offset = drawLength - viewportHeight;
|
|
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;
|
|
5200
|
-
return {
|
|
5201
|
-
normalizedState: layout.anchorMode === "top" ? {
|
|
5202
|
-
position: 0,
|
|
5203
|
-
offset: 0
|
|
5204
|
-
} : {
|
|
5205
|
-
position: itemCount - 1,
|
|
5206
|
-
offset: 0
|
|
5207
|
-
},
|
|
5208
|
-
resolutionPath,
|
|
5209
|
-
window: {
|
|
5210
|
-
drawList: window.drawList,
|
|
5211
|
-
shift: desiredTop - minOffset
|
|
5212
|
-
}
|
|
5213
|
-
};
|
|
5214
|
-
}
|
|
5215
|
-
//#endregion
|
|
5216
5660
|
//#region src/renderer/virtualized/list.ts
|
|
5217
5661
|
/**
|
|
5218
5662
|
* Virtualized list renderer with configurable anchor semantics.
|
|
@@ -5223,11 +5667,24 @@ var ListRenderer = class extends VirtualizedRenderer {
|
|
|
5223
5667
|
super(graphics, options);
|
|
5224
5668
|
this.#layout = resolveListLayoutOptions(options);
|
|
5225
5669
|
}
|
|
5670
|
+
get padding() {
|
|
5671
|
+
return { ...this.#layout.padding };
|
|
5672
|
+
}
|
|
5673
|
+
set padding(value) {
|
|
5674
|
+
const nextPadding = normalizeListPadding(value);
|
|
5675
|
+
if (nextPadding.top === this.#layout.padding.top && nextPadding.bottom === this.#layout.padding.bottom) return;
|
|
5676
|
+
const anchor = this._readAnchorAt(performance.now());
|
|
5677
|
+
this.#layout = {
|
|
5678
|
+
...this.#layout,
|
|
5679
|
+
padding: nextPadding
|
|
5680
|
+
};
|
|
5681
|
+
if (anchor != null) this._restoreAnchor(anchor);
|
|
5682
|
+
}
|
|
5226
5683
|
_getLayoutOptions() {
|
|
5227
5684
|
return this.#layout;
|
|
5228
5685
|
}
|
|
5229
5686
|
_resolveVisibleWindowForState(state, now) {
|
|
5230
|
-
return resolveVisibleWindow(this.items, state, this.graphics.canvas.clientHeight, (item, idx) => this._resolveItem(item, idx, now), this.#layout);
|
|
5687
|
+
return resolveVisibleWindow(this.items, state, resolveListViewport(this.graphics.canvas.clientHeight, this.#layout.padding), (item, idx) => this._resolveItem(item, idx, now), this.#layout);
|
|
5231
5688
|
}
|
|
5232
5689
|
_getDefaultJumpBlock() {
|
|
5233
5690
|
return this.#layout.anchorMode === "top" ? "start" : "end";
|
|
@@ -5244,7 +5701,7 @@ var ListRenderer = class extends VirtualizedRenderer {
|
|
|
5244
5701
|
this._commitListState(state);
|
|
5245
5702
|
}
|
|
5246
5703
|
_getTargetAnchor(index, block) {
|
|
5247
|
-
return getTargetAnchorForItem(this.items.length, index, block, this.#layout.anchorMode, this.graphics.canvas.clientHeight, this._getItemHeight.bind(this));
|
|
5704
|
+
return getTargetAnchorForItem(this.items.length, index, block, this.#layout.anchorMode, resolveListViewport(this.graphics.canvas.clientHeight, this.#layout.padding).contentHeight, this._getItemHeight.bind(this));
|
|
5248
5705
|
}
|
|
5249
5706
|
};
|
|
5250
5707
|
//#endregion
|