chat-layout 1.2.0-7 → 1.2.0-9
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 +1 -1
- package/example/chat.ts +1 -1
- package/index.d.mts +14 -25
- package/index.mjs +1191 -1116
- package/index.mjs.map +1 -1
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -2041,6 +2041,154 @@ function createRichSourceItems(spans, defaultFont) {
|
|
|
2041
2041
|
}));
|
|
2042
2042
|
}
|
|
2043
2043
|
//#endregion
|
|
2044
|
+
//#region src/text/justify.ts
|
|
2045
|
+
let _justifySupported;
|
|
2046
|
+
function isJustifySupported(ctx) {
|
|
2047
|
+
if (_justifySupported !== void 0) return _justifySupported;
|
|
2048
|
+
_justifySupported = typeof ctx.wordSpacing === "string" && typeof ctx.letterSpacing === "string";
|
|
2049
|
+
return _justifySupported;
|
|
2050
|
+
}
|
|
2051
|
+
function resolveJustifyMode(justify) {
|
|
2052
|
+
if (justify === true) return "inter-word";
|
|
2053
|
+
if (justify === "inter-word" || justify === "inter-character") return justify;
|
|
2054
|
+
return null;
|
|
2055
|
+
}
|
|
2056
|
+
const HYBRID_WORD_SHARE_CANDIDATES = [
|
|
2057
|
+
.15,
|
|
2058
|
+
.2,
|
|
2059
|
+
.25,
|
|
2060
|
+
.3,
|
|
2061
|
+
.35,
|
|
2062
|
+
.4,
|
|
2063
|
+
.45,
|
|
2064
|
+
.5,
|
|
2065
|
+
.55,
|
|
2066
|
+
.6,
|
|
2067
|
+
.65,
|
|
2068
|
+
.7,
|
|
2069
|
+
.75,
|
|
2070
|
+
.8,
|
|
2071
|
+
.85,
|
|
2072
|
+
1,
|
|
2073
|
+
0
|
|
2074
|
+
];
|
|
2075
|
+
const PUNCTUATION_OR_SYMBOL_PATTERN = /^[\p{P}\p{S}]$/u;
|
|
2076
|
+
const JUSTIFY_SCORE_EPSILON = 1e-9;
|
|
2077
|
+
function analyzeLineForJustify(prepared, line) {
|
|
2078
|
+
let wordGapCount = 0;
|
|
2079
|
+
let wordCount = 0;
|
|
2080
|
+
let renderAtomCount = 0;
|
|
2081
|
+
let spaceCount = 0;
|
|
2082
|
+
let nonSpaceCount = 0;
|
|
2083
|
+
let cjkCount = 0;
|
|
2084
|
+
let latinLikeCount = 0;
|
|
2085
|
+
let punctuationCount = 0;
|
|
2086
|
+
let nonSpaceWidth = 0;
|
|
2087
|
+
let insideWord = false;
|
|
2088
|
+
forEachAtomInRange(prepared, line.start, line.end, (atom) => {
|
|
2089
|
+
if (atom.kind === "space" && !atom.preservesLineEnd && atom.atomicGroupId == null) wordGapCount++;
|
|
2090
|
+
renderAtomCount++;
|
|
2091
|
+
if (atom.kind === "space") {
|
|
2092
|
+
spaceCount++;
|
|
2093
|
+
insideWord = false;
|
|
2094
|
+
return;
|
|
2095
|
+
}
|
|
2096
|
+
nonSpaceCount++;
|
|
2097
|
+
nonSpaceWidth += atom.width + atom.extraWidthAfter;
|
|
2098
|
+
if (!insideWord) {
|
|
2099
|
+
wordCount++;
|
|
2100
|
+
insideWord = true;
|
|
2101
|
+
}
|
|
2102
|
+
if (isCJK(atom.text)) {
|
|
2103
|
+
cjkCount++;
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
if (PUNCTUATION_OR_SYMBOL_PATTERN.test(atom.text)) {
|
|
2107
|
+
punctuationCount++;
|
|
2108
|
+
return;
|
|
2109
|
+
}
|
|
2110
|
+
latinLikeCount++;
|
|
2111
|
+
});
|
|
2112
|
+
return {
|
|
2113
|
+
wordGapCount,
|
|
2114
|
+
wordCount,
|
|
2115
|
+
renderAtomCount,
|
|
2116
|
+
letterGapCount: Math.max(renderAtomCount - 1, 0),
|
|
2117
|
+
spaceCount,
|
|
2118
|
+
nonSpaceCount,
|
|
2119
|
+
cjkCount,
|
|
2120
|
+
latinLikeCount,
|
|
2121
|
+
punctuationCount,
|
|
2122
|
+
lineWidth: line.width,
|
|
2123
|
+
nonSpaceWidth
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
function getAverageWordWidth(info) {
|
|
2127
|
+
return info.wordCount > 0 ? info.nonSpaceWidth / info.wordCount : info.lineWidth;
|
|
2128
|
+
}
|
|
2129
|
+
function getAverageCharWidth(info) {
|
|
2130
|
+
return info.renderAtomCount > 0 ? info.lineWidth / info.renderAtomCount : info.lineWidth;
|
|
2131
|
+
}
|
|
2132
|
+
function resolvePerGapSpacing(totalSpace, gapCount) {
|
|
2133
|
+
if (totalSpace === 0) return 0;
|
|
2134
|
+
if (gapCount <= 0) return null;
|
|
2135
|
+
return totalSpace / gapCount;
|
|
2136
|
+
}
|
|
2137
|
+
function exceedsThreshold(perGap, averageWidth, threshold) {
|
|
2138
|
+
if (!Number.isFinite(threshold)) return false;
|
|
2139
|
+
return perGap > threshold * averageWidth;
|
|
2140
|
+
}
|
|
2141
|
+
function createJustifySpacing(wordSpacingPx, letterSpacingPx) {
|
|
2142
|
+
return {
|
|
2143
|
+
wordSpacing: `${wordSpacingPx}px`,
|
|
2144
|
+
letterSpacing: `${letterSpacingPx}px`,
|
|
2145
|
+
wordSpacingPx,
|
|
2146
|
+
letterSpacingPx
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
function computeJustifySpacing(lineWidth, maxWidth, info, mode, threshold = Number.POSITIVE_INFINITY) {
|
|
2150
|
+
const extraSpace = maxWidth - lineWidth;
|
|
2151
|
+
if (extraSpace <= 0 || mode == null) return null;
|
|
2152
|
+
if (mode === "inter-word" && info.wordGapCount > 0) {
|
|
2153
|
+
const perGap = extraSpace / info.wordGapCount;
|
|
2154
|
+
if (exceedsThreshold(perGap, Math.max(getAverageWordWidth(info), Number.EPSILON), threshold)) return null;
|
|
2155
|
+
return createJustifySpacing(perGap, 0);
|
|
2156
|
+
}
|
|
2157
|
+
if (mode !== "inter-character" || info.renderAtomCount === 0) return null;
|
|
2158
|
+
const avgCharWidth = Math.max(getAverageCharWidth(info), Number.EPSILON);
|
|
2159
|
+
if (info.wordGapCount === 0) {
|
|
2160
|
+
const perGap = resolvePerGapSpacing(extraSpace, info.letterGapCount);
|
|
2161
|
+
if (perGap == null) return null;
|
|
2162
|
+
if (exceedsThreshold(perGap, avgCharWidth, threshold)) return null;
|
|
2163
|
+
return createJustifySpacing(0, perGap);
|
|
2164
|
+
}
|
|
2165
|
+
const avgWordWidth = Math.max(getAverageWordWidth(info), Number.EPSILON);
|
|
2166
|
+
const nonSpaceCount = Math.max(info.nonSpaceCount, 1);
|
|
2167
|
+
const cjkRatio = info.cjkCount / nonSpaceCount;
|
|
2168
|
+
const latinLikeRatio = info.latinLikeCount / nonSpaceCount;
|
|
2169
|
+
const punctuationRatio = info.punctuationCount / nonSpaceCount;
|
|
2170
|
+
const wordPenalty = 1 + cjkRatio;
|
|
2171
|
+
const letterPenalty = 1 + latinLikeRatio + .5 * punctuationRatio;
|
|
2172
|
+
let bestCandidate = null;
|
|
2173
|
+
for (const wordShare of HYBRID_WORD_SHARE_CANDIDATES) {
|
|
2174
|
+
const wordExtraSpace = extraSpace * wordShare;
|
|
2175
|
+
const letterExtraSpace = extraSpace - wordExtraSpace;
|
|
2176
|
+
const wordSpacingPx = resolvePerGapSpacing(wordExtraSpace, info.wordGapCount);
|
|
2177
|
+
const letterSpacingPx = resolvePerGapSpacing(letterExtraSpace, info.letterGapCount);
|
|
2178
|
+
if (wordSpacingPx == null || letterSpacingPx == null) continue;
|
|
2179
|
+
if (exceedsThreshold(wordSpacingPx, avgWordWidth, threshold) || exceedsThreshold(letterSpacingPx, avgCharWidth, threshold)) continue;
|
|
2180
|
+
const wordRatio = wordSpacingPx / avgWordWidth;
|
|
2181
|
+
const letterRatio = letterSpacingPx / avgCharWidth;
|
|
2182
|
+
const score = wordPenalty * wordRatio ** 2 + letterPenalty * letterRatio ** 2;
|
|
2183
|
+
if (bestCandidate == null || score < bestCandidate.score - JUSTIFY_SCORE_EPSILON || Math.abs(score - bestCandidate.score) <= JUSTIFY_SCORE_EPSILON && wordShare > bestCandidate.wordShare) bestCandidate = {
|
|
2184
|
+
spacing: createJustifySpacing(wordSpacingPx, letterSpacingPx),
|
|
2185
|
+
score,
|
|
2186
|
+
wordShare
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
return bestCandidate?.spacing ?? null;
|
|
2190
|
+
}
|
|
2191
|
+
//#endregion
|
|
2044
2192
|
//#region src/text/plain-core.ts
|
|
2045
2193
|
function readPreparedText(text, font, whiteSpace, wordBreak) {
|
|
2046
2194
|
return readPreparedInlineLayout(getPlainPreparedKey(text, font, whiteSpace, wordBreak), createPlainSourceItems(text, font), whiteSpace, wordBreak);
|
|
@@ -2759,174 +2907,26 @@ function layoutRichTextWithOverflow(ctx, spans, maxWidth, defaultFont, defaultCo
|
|
|
2759
2907
|
};
|
|
2760
2908
|
}
|
|
2761
2909
|
//#endregion
|
|
2762
|
-
//#region src/text
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
if (
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
if (justify === "inter-word" || justify === "inter-character") return justify;
|
|
2772
|
-
return null;
|
|
2773
|
-
}
|
|
2774
|
-
const HYBRID_WORD_SHARE_CANDIDATES = [
|
|
2775
|
-
.15,
|
|
2776
|
-
.2,
|
|
2777
|
-
.25,
|
|
2778
|
-
.3,
|
|
2779
|
-
.35,
|
|
2780
|
-
.4,
|
|
2781
|
-
.45,
|
|
2782
|
-
.5,
|
|
2783
|
-
.55,
|
|
2784
|
-
.6,
|
|
2785
|
-
.65,
|
|
2786
|
-
.7,
|
|
2787
|
-
.75,
|
|
2788
|
-
.8,
|
|
2789
|
-
.85,
|
|
2790
|
-
1,
|
|
2791
|
-
0
|
|
2792
|
-
];
|
|
2793
|
-
const PUNCTUATION_OR_SYMBOL_PATTERN = /^[\p{P}\p{S}]$/u;
|
|
2794
|
-
const JUSTIFY_SCORE_EPSILON = 1e-9;
|
|
2795
|
-
function analyzeLineForJustify(prepared, line) {
|
|
2796
|
-
let wordGapCount = 0;
|
|
2797
|
-
let wordCount = 0;
|
|
2798
|
-
let renderAtomCount = 0;
|
|
2799
|
-
let spaceCount = 0;
|
|
2800
|
-
let nonSpaceCount = 0;
|
|
2801
|
-
let cjkCount = 0;
|
|
2802
|
-
let latinLikeCount = 0;
|
|
2803
|
-
let punctuationCount = 0;
|
|
2804
|
-
let nonSpaceWidth = 0;
|
|
2805
|
-
let insideWord = false;
|
|
2806
|
-
forEachAtomInRange(prepared, line.start, line.end, (atom) => {
|
|
2807
|
-
if (atom.kind === "space" && !atom.preservesLineEnd && atom.atomicGroupId == null) wordGapCount++;
|
|
2808
|
-
renderAtomCount++;
|
|
2809
|
-
if (atom.kind === "space") {
|
|
2810
|
-
spaceCount++;
|
|
2811
|
-
insideWord = false;
|
|
2812
|
-
return;
|
|
2813
|
-
}
|
|
2814
|
-
nonSpaceCount++;
|
|
2815
|
-
nonSpaceWidth += atom.width + atom.extraWidthAfter;
|
|
2816
|
-
if (!insideWord) {
|
|
2817
|
-
wordCount++;
|
|
2818
|
-
insideWord = true;
|
|
2819
|
-
}
|
|
2820
|
-
if (isCJK(atom.text)) {
|
|
2821
|
-
cjkCount++;
|
|
2822
|
-
return;
|
|
2823
|
-
}
|
|
2824
|
-
if (PUNCTUATION_OR_SYMBOL_PATTERN.test(atom.text)) {
|
|
2825
|
-
punctuationCount++;
|
|
2826
|
-
return;
|
|
2827
|
-
}
|
|
2828
|
-
latinLikeCount++;
|
|
2829
|
-
});
|
|
2830
|
-
return {
|
|
2831
|
-
wordGapCount,
|
|
2832
|
-
wordCount,
|
|
2833
|
-
renderAtomCount,
|
|
2834
|
-
letterGapCount: Math.max(renderAtomCount - 1, 0),
|
|
2835
|
-
spaceCount,
|
|
2836
|
-
nonSpaceCount,
|
|
2837
|
-
cjkCount,
|
|
2838
|
-
latinLikeCount,
|
|
2839
|
-
punctuationCount,
|
|
2840
|
-
lineWidth: line.width,
|
|
2841
|
-
nonSpaceWidth
|
|
2842
|
-
};
|
|
2843
|
-
}
|
|
2844
|
-
function getAverageWordWidth(info) {
|
|
2845
|
-
return info.wordCount > 0 ? info.nonSpaceWidth / info.wordCount : info.lineWidth;
|
|
2910
|
+
//#region src/nodes/text.ts
|
|
2911
|
+
function resolvePhysicalTextAlign(options) {
|
|
2912
|
+
if (options.physicalAlign != null) return options.physicalAlign;
|
|
2913
|
+
if (options.align != null) switch (options.align) {
|
|
2914
|
+
case "start": return "left";
|
|
2915
|
+
case "center": return "center";
|
|
2916
|
+
case "end": return "right";
|
|
2917
|
+
}
|
|
2918
|
+
return "left";
|
|
2846
2919
|
}
|
|
2847
|
-
function
|
|
2848
|
-
|
|
2920
|
+
function normalizeTextMaxWidth(maxWidth) {
|
|
2921
|
+
if (maxWidth == null) return;
|
|
2922
|
+
return Math.max(0, maxWidth);
|
|
2849
2923
|
}
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
if (!Number.isFinite(threshold)) return false;
|
|
2857
|
-
return perGap > threshold * averageWidth;
|
|
2858
|
-
}
|
|
2859
|
-
function createJustifySpacing(wordSpacingPx, letterSpacingPx) {
|
|
2860
|
-
return {
|
|
2861
|
-
wordSpacing: `${wordSpacingPx}px`,
|
|
2862
|
-
letterSpacing: `${letterSpacingPx}px`,
|
|
2863
|
-
wordSpacingPx,
|
|
2864
|
-
letterSpacingPx
|
|
2865
|
-
};
|
|
2866
|
-
}
|
|
2867
|
-
function computeJustifySpacing(lineWidth, maxWidth, info, mode, threshold = Number.POSITIVE_INFINITY) {
|
|
2868
|
-
const extraSpace = maxWidth - lineWidth;
|
|
2869
|
-
if (extraSpace <= 0 || mode == null) return null;
|
|
2870
|
-
if (mode === "inter-word" && info.wordGapCount > 0) {
|
|
2871
|
-
const perGap = extraSpace / info.wordGapCount;
|
|
2872
|
-
if (exceedsThreshold(perGap, Math.max(getAverageWordWidth(info), Number.EPSILON), threshold)) return null;
|
|
2873
|
-
return createJustifySpacing(perGap, 0);
|
|
2874
|
-
}
|
|
2875
|
-
if (mode !== "inter-character" || info.renderAtomCount === 0) return null;
|
|
2876
|
-
const avgCharWidth = Math.max(getAverageCharWidth(info), Number.EPSILON);
|
|
2877
|
-
if (info.wordGapCount === 0) {
|
|
2878
|
-
const perGap = resolvePerGapSpacing(extraSpace, info.letterGapCount);
|
|
2879
|
-
if (perGap == null) return null;
|
|
2880
|
-
if (exceedsThreshold(perGap, avgCharWidth, threshold)) return null;
|
|
2881
|
-
return createJustifySpacing(0, perGap);
|
|
2882
|
-
}
|
|
2883
|
-
const avgWordWidth = Math.max(getAverageWordWidth(info), Number.EPSILON);
|
|
2884
|
-
const nonSpaceCount = Math.max(info.nonSpaceCount, 1);
|
|
2885
|
-
const cjkRatio = info.cjkCount / nonSpaceCount;
|
|
2886
|
-
const latinLikeRatio = info.latinLikeCount / nonSpaceCount;
|
|
2887
|
-
const punctuationRatio = info.punctuationCount / nonSpaceCount;
|
|
2888
|
-
const wordPenalty = 1 + cjkRatio;
|
|
2889
|
-
const letterPenalty = 1 + latinLikeRatio + .5 * punctuationRatio;
|
|
2890
|
-
let bestCandidate = null;
|
|
2891
|
-
for (const wordShare of HYBRID_WORD_SHARE_CANDIDATES) {
|
|
2892
|
-
const wordExtraSpace = extraSpace * wordShare;
|
|
2893
|
-
const letterExtraSpace = extraSpace - wordExtraSpace;
|
|
2894
|
-
const wordSpacingPx = resolvePerGapSpacing(wordExtraSpace, info.wordGapCount);
|
|
2895
|
-
const letterSpacingPx = resolvePerGapSpacing(letterExtraSpace, info.letterGapCount);
|
|
2896
|
-
if (wordSpacingPx == null || letterSpacingPx == null) continue;
|
|
2897
|
-
if (exceedsThreshold(wordSpacingPx, avgWordWidth, threshold) || exceedsThreshold(letterSpacingPx, avgCharWidth, threshold)) continue;
|
|
2898
|
-
const wordRatio = wordSpacingPx / avgWordWidth;
|
|
2899
|
-
const letterRatio = letterSpacingPx / avgCharWidth;
|
|
2900
|
-
const score = wordPenalty * wordRatio ** 2 + letterPenalty * letterRatio ** 2;
|
|
2901
|
-
if (bestCandidate == null || score < bestCandidate.score - JUSTIFY_SCORE_EPSILON || Math.abs(score - bestCandidate.score) <= JUSTIFY_SCORE_EPSILON && wordShare > bestCandidate.wordShare) bestCandidate = {
|
|
2902
|
-
spacing: createJustifySpacing(wordSpacingPx, letterSpacingPx),
|
|
2903
|
-
score,
|
|
2904
|
-
wordShare
|
|
2905
|
-
};
|
|
2906
|
-
}
|
|
2907
|
-
return bestCandidate?.spacing ?? null;
|
|
2908
|
-
}
|
|
2909
|
-
//#endregion
|
|
2910
|
-
//#region src/nodes/text.ts
|
|
2911
|
-
function resolvePhysicalTextAlign(options) {
|
|
2912
|
-
if (options.physicalAlign != null) return options.physicalAlign;
|
|
2913
|
-
if (options.align != null) switch (options.align) {
|
|
2914
|
-
case "start": return "left";
|
|
2915
|
-
case "center": return "center";
|
|
2916
|
-
case "end": return "right";
|
|
2917
|
-
}
|
|
2918
|
-
return "left";
|
|
2919
|
-
}
|
|
2920
|
-
function normalizeTextMaxWidth(maxWidth) {
|
|
2921
|
-
if (maxWidth == null) return;
|
|
2922
|
-
return Math.max(0, maxWidth);
|
|
2923
|
-
}
|
|
2924
|
-
const DEFAULT_TEXT_SPACING = {
|
|
2925
|
-
wordSpacing: "0px",
|
|
2926
|
-
letterSpacing: "0px"
|
|
2927
|
-
};
|
|
2928
|
-
function supportsTextSpacing(g) {
|
|
2929
|
-
return typeof g.wordSpacing === "string" && typeof g.letterSpacing === "string";
|
|
2924
|
+
const DEFAULT_TEXT_SPACING = {
|
|
2925
|
+
wordSpacing: "0px",
|
|
2926
|
+
letterSpacing: "0px"
|
|
2927
|
+
};
|
|
2928
|
+
function supportsTextSpacing(g) {
|
|
2929
|
+
return typeof g.wordSpacing === "string" && typeof g.letterSpacing === "string";
|
|
2930
2930
|
}
|
|
2931
2931
|
function withTextSpacing(g, spacing, cb) {
|
|
2932
2932
|
if (!supportsTextSpacing(g)) return cb();
|
|
@@ -3574,30 +3574,13 @@ var DebugRenderer = class extends BaseRenderer {
|
|
|
3574
3574
|
}
|
|
3575
3575
|
};
|
|
3576
3576
|
//#endregion
|
|
3577
|
-
//#region src/renderer/weak-listeners.ts
|
|
3578
|
-
function pruneWeakListenerMap(listeners) {
|
|
3579
|
-
for (const [token, listener] of listeners) if (listener.ownerRef.deref() == null) listeners.delete(token);
|
|
3580
|
-
}
|
|
3581
|
-
function emitWeakListeners(listeners, event) {
|
|
3582
|
-
for (const [token, listener] of [...listeners]) {
|
|
3583
|
-
const owner = listener.ownerRef.deref();
|
|
3584
|
-
if (owner == null) {
|
|
3585
|
-
listeners.delete(token);
|
|
3586
|
-
continue;
|
|
3587
|
-
}
|
|
3588
|
-
listener.notify(owner, event);
|
|
3589
|
-
}
|
|
3590
|
-
}
|
|
3591
|
-
//#endregion
|
|
3592
3577
|
//#region src/renderer/list-state.ts
|
|
3593
|
-
const
|
|
3594
|
-
const listStateListenerRegistry = typeof FinalizationRegistry === "function" ? new FinalizationRegistry(({ listRef, token }) => {
|
|
3595
|
-
const list = listRef.deref();
|
|
3596
|
-
if (list == null) return;
|
|
3597
|
-
deleteListStateListener(list, token);
|
|
3598
|
-
}) : null;
|
|
3578
|
+
const listStateChangeQueues = /* @__PURE__ */ new WeakMap();
|
|
3599
3579
|
const listScrollMutations = /* @__PURE__ */ new WeakMap();
|
|
3600
3580
|
const WRITE_LIST_SCROLL_STATE = Symbol("writeListScrollState");
|
|
3581
|
+
const FINALIZE_LIST_DELETE = Symbol("finalizeListDelete");
|
|
3582
|
+
const LIST_STATE_CHANGE_TIME = Symbol("listStateChangeTime");
|
|
3583
|
+
const LIST_STATE_CHANGE_SNAPSHOT = Symbol("listStateChangeSnapshot");
|
|
3601
3584
|
function normalizePosition(value) {
|
|
3602
3585
|
return typeof value === "number" && Number.isFinite(value) ? Math.trunc(value) : void 0;
|
|
3603
3586
|
}
|
|
@@ -3630,34 +3613,43 @@ function readListScrollMutation(list) {
|
|
|
3630
3613
|
function writeInternalListScrollState(list, state) {
|
|
3631
3614
|
list[WRITE_LIST_SCROLL_STATE](state, "internal");
|
|
3632
3615
|
}
|
|
3633
|
-
function
|
|
3634
|
-
|
|
3635
|
-
if (listeners == null) return;
|
|
3636
|
-
listeners.delete(token);
|
|
3637
|
-
if (listeners.size === 0) listStateListeners.delete(list);
|
|
3638
|
-
}
|
|
3639
|
-
function emitListStateChange(list, change) {
|
|
3640
|
-
const listeners = listStateListeners.get(list);
|
|
3641
|
-
if (listeners == null) return;
|
|
3642
|
-
emitWeakListeners(listeners, change);
|
|
3643
|
-
if (listeners.size === 0) listStateListeners.delete(list);
|
|
3616
|
+
function finalizeInternalListDelete(list, item) {
|
|
3617
|
+
list[FINALIZE_LIST_DELETE](item);
|
|
3644
3618
|
}
|
|
3645
|
-
function
|
|
3619
|
+
function enqueueListStateChange(list, change) {
|
|
3646
3620
|
const key = list;
|
|
3647
|
-
let
|
|
3648
|
-
if (
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
}
|
|
3652
|
-
const
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3621
|
+
let queue = listStateChangeQueues.get(key);
|
|
3622
|
+
if (queue == null) {
|
|
3623
|
+
queue = [];
|
|
3624
|
+
listStateChangeQueues.set(key, queue);
|
|
3625
|
+
}
|
|
3626
|
+
const timestampedChange = change;
|
|
3627
|
+
Object.defineProperty(timestampedChange, LIST_STATE_CHANGE_TIME, {
|
|
3628
|
+
value: performance.now(),
|
|
3629
|
+
configurable: true
|
|
3656
3630
|
});
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3631
|
+
Object.defineProperty(timestampedChange, LIST_STATE_CHANGE_SNAPSHOT, {
|
|
3632
|
+
value: {
|
|
3633
|
+
items: [...list.items],
|
|
3634
|
+
position: list.position,
|
|
3635
|
+
offset: list.offset
|
|
3636
|
+
},
|
|
3637
|
+
configurable: true
|
|
3660
3638
|
});
|
|
3639
|
+
queue.push(timestampedChange);
|
|
3640
|
+
}
|
|
3641
|
+
function drainInternalListStateChanges(list) {
|
|
3642
|
+
const key = list;
|
|
3643
|
+
const queue = listStateChangeQueues.get(key);
|
|
3644
|
+
if (queue == null || queue.length === 0) return [];
|
|
3645
|
+
listStateChangeQueues.delete(key);
|
|
3646
|
+
return queue;
|
|
3647
|
+
}
|
|
3648
|
+
function readInternalListStateChangeTime(change) {
|
|
3649
|
+
return change[LIST_STATE_CHANGE_TIME];
|
|
3650
|
+
}
|
|
3651
|
+
function readInternalListStateChangeSnapshot(change) {
|
|
3652
|
+
return change[LIST_STATE_CHANGE_SNAPSHOT];
|
|
3661
3653
|
}
|
|
3662
3654
|
function isObjectIdentityCandidate(value) {
|
|
3663
3655
|
return typeof value === "object" && value !== null || typeof value === "function";
|
|
@@ -3706,16 +3698,10 @@ var ListState = class {
|
|
|
3706
3698
|
get offset() {
|
|
3707
3699
|
return this.#offset;
|
|
3708
3700
|
}
|
|
3709
|
-
set offset(value) {
|
|
3710
|
-
this.#writeScrollState({ offset: normalizeOffset$1(value) }, "external");
|
|
3711
|
-
}
|
|
3712
3701
|
/** Anchor item index, or `undefined` to use the renderer default. */
|
|
3713
3702
|
get position() {
|
|
3714
3703
|
return this.#position;
|
|
3715
3704
|
}
|
|
3716
|
-
set position(value) {
|
|
3717
|
-
this.#writeScrollState({ position: normalizePosition(value) }, "external");
|
|
3718
|
-
}
|
|
3719
3705
|
/** Items currently managed by the renderer. */
|
|
3720
3706
|
get items() {
|
|
3721
3707
|
return this.#items;
|
|
@@ -3726,7 +3712,7 @@ var ListState = class {
|
|
|
3726
3712
|
assertUniqueItemReferences(nextItems);
|
|
3727
3713
|
this.#items = nextItems;
|
|
3728
3714
|
this.#pendingDeletes.clear();
|
|
3729
|
-
|
|
3715
|
+
enqueueListStateChange(this, { type: "set" });
|
|
3730
3716
|
}
|
|
3731
3717
|
/**
|
|
3732
3718
|
* @param items Initial list items.
|
|
@@ -3747,7 +3733,7 @@ var ListState = class {
|
|
|
3747
3733
|
const normalizedAnimation = normalizeInsertAnimation(animation);
|
|
3748
3734
|
if (this.position != null) this.#writeScrollState({ position: this.position + items.length }, "internal");
|
|
3749
3735
|
this.#items = items.concat(this.#items);
|
|
3750
|
-
|
|
3736
|
+
enqueueListStateChange(this, {
|
|
3751
3737
|
type: "unshift",
|
|
3752
3738
|
count: items.length,
|
|
3753
3739
|
animation: normalizedAnimation
|
|
@@ -3763,7 +3749,7 @@ var ListState = class {
|
|
|
3763
3749
|
assertUniqueItemReferences(items, this.#items);
|
|
3764
3750
|
const normalizedAnimation = normalizeInsertAnimation(animation);
|
|
3765
3751
|
this.#items.push(...items);
|
|
3766
|
-
|
|
3752
|
+
enqueueListStateChange(this, {
|
|
3767
3753
|
type: "push",
|
|
3768
3754
|
count: items.length,
|
|
3769
3755
|
animation: normalizedAnimation
|
|
@@ -3781,7 +3767,7 @@ var ListState = class {
|
|
|
3781
3767
|
if (this.#items.includes(nextItem)) throw new Error("update() nextItem is already present in the list.");
|
|
3782
3768
|
const prevItem = this.#items[index];
|
|
3783
3769
|
this.#items[index] = nextItem;
|
|
3784
|
-
|
|
3770
|
+
enqueueListStateChange(this, {
|
|
3785
3771
|
type: "update",
|
|
3786
3772
|
prevItem,
|
|
3787
3773
|
nextItem,
|
|
@@ -3798,11 +3784,11 @@ var ListState = class {
|
|
|
3798
3784
|
const normalizedAnimation = normalizeDeleteAnimation(animation);
|
|
3799
3785
|
if (!((normalizedAnimation?.duration ?? 0) > 0)) {
|
|
3800
3786
|
this.#pendingDeletes.add(item);
|
|
3801
|
-
this
|
|
3787
|
+
this[FINALIZE_LIST_DELETE](item);
|
|
3802
3788
|
return;
|
|
3803
3789
|
}
|
|
3804
3790
|
this.#pendingDeletes.add(item);
|
|
3805
|
-
|
|
3791
|
+
enqueueListStateChange(this, {
|
|
3806
3792
|
type: "delete",
|
|
3807
3793
|
item,
|
|
3808
3794
|
animation: normalizedAnimation
|
|
@@ -3811,7 +3797,7 @@ var ListState = class {
|
|
|
3811
3797
|
/**
|
|
3812
3798
|
* Finalizes a pending delete by removing the item from the list.
|
|
3813
3799
|
*/
|
|
3814
|
-
|
|
3800
|
+
[FINALIZE_LIST_DELETE](item) {
|
|
3815
3801
|
if (!this.#pendingDeletes.has(item)) return;
|
|
3816
3802
|
const index = this.#items.indexOf(item);
|
|
3817
3803
|
this.#pendingDeletes.delete(item);
|
|
@@ -3825,21 +3811,12 @@ var ListState = class {
|
|
|
3825
3811
|
if (this.position > index) this.#writeScrollState({ position: this.position - 1 }, "internal");
|
|
3826
3812
|
else if (this.position === index) this.#writeScrollState({ position: Math.min(index, this.#items.length - 1) }, "internal");
|
|
3827
3813
|
}
|
|
3828
|
-
|
|
3814
|
+
enqueueListStateChange(this, {
|
|
3829
3815
|
type: "delete-finalize",
|
|
3830
3816
|
item
|
|
3831
3817
|
});
|
|
3832
3818
|
}
|
|
3833
3819
|
/**
|
|
3834
|
-
* Sets the current anchor item and pixel offset.
|
|
3835
|
-
*/
|
|
3836
|
-
setAnchor(position, offset = 0) {
|
|
3837
|
-
this.#writeScrollState({
|
|
3838
|
-
position: normalizePosition(position),
|
|
3839
|
-
offset: normalizeOffset$1(offset)
|
|
3840
|
-
}, "external");
|
|
3841
|
-
}
|
|
3842
|
-
/**
|
|
3843
3820
|
* Replaces all items and clears scroll state.
|
|
3844
3821
|
*/
|
|
3845
3822
|
reset(items = []) {
|
|
@@ -3851,14 +3828,7 @@ var ListState = class {
|
|
|
3851
3828
|
position: void 0,
|
|
3852
3829
|
offset: 0
|
|
3853
3830
|
}, "internal");
|
|
3854
|
-
|
|
3855
|
-
}
|
|
3856
|
-
/** Clears the current scroll anchor while keeping the items. */
|
|
3857
|
-
resetScroll() {
|
|
3858
|
-
this.#writeScrollState({
|
|
3859
|
-
position: void 0,
|
|
3860
|
-
offset: 0
|
|
3861
|
-
}, "external");
|
|
3831
|
+
enqueueListStateChange(this, { type: "reset" });
|
|
3862
3832
|
}
|
|
3863
3833
|
/** Applies a relative pixel scroll delta. */
|
|
3864
3834
|
applyScroll(delta) {
|
|
@@ -3948,7 +3918,22 @@ function memoRenderItemBy(keyOf, renderItem, options = {}) {
|
|
|
3948
3918
|
});
|
|
3949
3919
|
}
|
|
3950
3920
|
//#endregion
|
|
3951
|
-
//#region src/renderer/virtualized/
|
|
3921
|
+
//#region src/renderer/virtualized/frame-session.ts
|
|
3922
|
+
function prepareFrameSession(params) {
|
|
3923
|
+
let solution = params.resolveVisibleWindow(params.now);
|
|
3924
|
+
params.captureVisibleItemSnapshot(solution);
|
|
3925
|
+
const requestSettleRedraw = params.pruneTransitionAnimations(solution.window, params.now);
|
|
3926
|
+
if (requestSettleRedraw) {
|
|
3927
|
+
solution = params.resolveVisibleWindow(params.now);
|
|
3928
|
+
params.captureVisibleItemSnapshot(solution);
|
|
3929
|
+
}
|
|
3930
|
+
return {
|
|
3931
|
+
solution,
|
|
3932
|
+
requestSettleRedraw
|
|
3933
|
+
};
|
|
3934
|
+
}
|
|
3935
|
+
//#endregion
|
|
3936
|
+
//#region src/renderer/virtualized/virtualized-animation.ts
|
|
3952
3937
|
const CONTROLLED_STATE_OFFSET_EPSILON = 1e-9;
|
|
3953
3938
|
function clamp$1(value, min, max) {
|
|
3954
3939
|
return Math.min(Math.max(value, min), max);
|
|
@@ -4039,21 +4024,6 @@ function getNow() {
|
|
|
4039
4024
|
return globalThis.performance?.now() ?? Date.now();
|
|
4040
4025
|
}
|
|
4041
4026
|
//#endregion
|
|
4042
|
-
//#region src/renderer/virtualized/frame-session.ts
|
|
4043
|
-
function prepareFrameSession(params) {
|
|
4044
|
-
let solution = params.resolveVisibleWindow(params.now);
|
|
4045
|
-
params.captureVisibleItemSnapshot(solution);
|
|
4046
|
-
const requestSettleRedraw = params.pruneTransitionAnimations(solution.window, params.now);
|
|
4047
|
-
if (requestSettleRedraw) {
|
|
4048
|
-
solution = params.resolveVisibleWindow(params.now);
|
|
4049
|
-
params.captureVisibleItemSnapshot(solution);
|
|
4050
|
-
}
|
|
4051
|
-
return {
|
|
4052
|
-
solution,
|
|
4053
|
-
requestSettleRedraw
|
|
4054
|
-
};
|
|
4055
|
-
}
|
|
4056
|
-
//#endregion
|
|
4057
4027
|
//#region src/renderer/virtualized/jump-controller.ts
|
|
4058
4028
|
var JumpController = class JumpController {
|
|
4059
4029
|
static TRANSITION_SETTLE_SNAP_DURATION = 120;
|
|
@@ -4064,6 +4034,10 @@ var JumpController = class JumpController {
|
|
|
4064
4034
|
#pendingAutoFollowRecomputeReasonTop = "init";
|
|
4065
4035
|
#pendingAutoFollowRecomputeReasonBottom = "init";
|
|
4066
4036
|
#pendingTransitionSettleReconcile = false;
|
|
4037
|
+
#autoFollowObservationCountTop = 0;
|
|
4038
|
+
#autoFollowObservationCountBottom = 0;
|
|
4039
|
+
#pendingAutoFollowInvalidationTop = false;
|
|
4040
|
+
#pendingAutoFollowInvalidationBottom = false;
|
|
4067
4041
|
#lastArmedAutoFollowBoundary;
|
|
4068
4042
|
#lastObservedRenderedAutoFollowTop = false;
|
|
4069
4043
|
#lastObservedRenderedAutoFollowBottom = false;
|
|
@@ -4145,12 +4119,32 @@ var JumpController = class JumpController {
|
|
|
4145
4119
|
block: boundary === "bottom" ? "end" : "start"
|
|
4146
4120
|
});
|
|
4147
4121
|
}
|
|
4122
|
+
beginAutoFollowBoundaryObservation(boundary) {
|
|
4123
|
+
if (boundary === "top") {
|
|
4124
|
+
this.#autoFollowObservationCountTop += 1;
|
|
4125
|
+
return;
|
|
4126
|
+
}
|
|
4127
|
+
this.#autoFollowObservationCountBottom += 1;
|
|
4128
|
+
}
|
|
4129
|
+
endAutoFollowBoundaryObservation(boundary) {
|
|
4130
|
+
if (boundary === "top") {
|
|
4131
|
+
this.#autoFollowObservationCountTop = Math.max(0, this.#autoFollowObservationCountTop - 1);
|
|
4132
|
+
return;
|
|
4133
|
+
}
|
|
4134
|
+
this.#autoFollowObservationCountBottom = Math.max(0, this.#autoFollowObservationCountBottom - 1);
|
|
4135
|
+
}
|
|
4136
|
+
invalidateAutoFollowBoundary(boundary) {
|
|
4137
|
+
if (boundary == null || boundary === "top") this.#pendingAutoFollowInvalidationTop = true;
|
|
4138
|
+
if (boundary == null || boundary === "bottom") this.#pendingAutoFollowInvalidationBottom = true;
|
|
4139
|
+
}
|
|
4148
4140
|
recomputeAutoFollowCapabilities(capabilities) {
|
|
4149
4141
|
const previouslyObservedDualBoundary = this.#lastObservedRenderedAutoFollowTop && this.#lastObservedRenderedAutoFollowBottom;
|
|
4150
4142
|
if (capabilities.top && capabilities.bottom && !previouslyObservedDualBoundary) {
|
|
4151
4143
|
this.#setAutoFollowBoundary("top", true, "dual-boundary-promotion");
|
|
4152
4144
|
this.#setAutoFollowBoundary("bottom", true, "dual-boundary-promotion");
|
|
4153
4145
|
}
|
|
4146
|
+
this.#syncObservedOrInvalidatedBoundary("top", capabilities);
|
|
4147
|
+
this.#syncObservedOrInvalidatedBoundary("bottom", capabilities);
|
|
4154
4148
|
if (this.#pendingAutoFollowRecomputeTop) {
|
|
4155
4149
|
this.#setAutoFollowBoundary("top", capabilities.top, `strict-recompute:${this.#pendingAutoFollowRecomputeReasonTop}`);
|
|
4156
4150
|
this.#pendingAutoFollowRecomputeTop = false;
|
|
@@ -4177,22 +4171,23 @@ var JumpController = class JumpController {
|
|
|
4177
4171
|
reconcileAutoFollowAfterTransitionSettle() {
|
|
4178
4172
|
this.#pendingTransitionSettleReconcile = true;
|
|
4179
4173
|
}
|
|
4180
|
-
handleListStateChange(change) {
|
|
4174
|
+
handleListStateChange(change, now = getNow()) {
|
|
4181
4175
|
switch (change.type) {
|
|
4182
4176
|
case "reset":
|
|
4183
4177
|
case "set":
|
|
4184
4178
|
this.#cancelJumpAnimation();
|
|
4185
4179
|
this.#clearPendingPostJumpBoundary();
|
|
4186
4180
|
this.#clearPendingTransitionSettleReconcile();
|
|
4181
|
+
this.#clearAutoFollowObservationState();
|
|
4187
4182
|
this.#syncScrollMutationVersion();
|
|
4188
4183
|
this.#markAutoFollowRecompute(void 0, change.type);
|
|
4189
4184
|
return change;
|
|
4190
4185
|
case "push":
|
|
4191
|
-
case "unshift": return this.#handleBoundaryInsert(change);
|
|
4186
|
+
case "unshift": return this.#handleBoundaryInsert(change, now);
|
|
4192
4187
|
default: return change;
|
|
4193
4188
|
}
|
|
4194
4189
|
}
|
|
4195
|
-
#handleBoundaryInsert(change) {
|
|
4190
|
+
#handleBoundaryInsert(change, now) {
|
|
4196
4191
|
if (this.#handlePendingExternalScrollMutation()) return change;
|
|
4197
4192
|
this.#clearPendingTransitionSettleReconcile();
|
|
4198
4193
|
const followChange = this.#resolveAutoFollowChange(change);
|
|
@@ -4205,21 +4200,21 @@ var JumpController = class JumpController {
|
|
|
4205
4200
|
this.#lastArmedAutoFollowBoundary = followChange.boundary;
|
|
4206
4201
|
}
|
|
4207
4202
|
this.#clearPendingPostJumpBoundary();
|
|
4208
|
-
this.#materializeAnimatedAnchor(
|
|
4203
|
+
this.#materializeAnimatedAnchor(now, followChange.direction, followChange.count);
|
|
4209
4204
|
this.#startJumpToIndex(followChange.boundary === "bottom" ? this.#options.getItemCount() - 1 : 0, {
|
|
4210
4205
|
block: followChange.boundary === "bottom" ? "end" : "start",
|
|
4211
4206
|
duration: followChange.animation?.duration
|
|
4212
|
-
});
|
|
4207
|
+
}, now);
|
|
4213
4208
|
return change;
|
|
4214
4209
|
}
|
|
4215
4210
|
#cancelJumpAnimation() {
|
|
4216
4211
|
this.#jumpAnimation = void 0;
|
|
4217
4212
|
}
|
|
4218
|
-
#startJumpToIndex(index, options) {
|
|
4213
|
+
#startJumpToIndex(index, options, now = getNow()) {
|
|
4219
4214
|
const targetIndex = this.#options.clampItemIndex(index);
|
|
4220
4215
|
const targetBlock = options.block ?? this.#options.getDefaultJumpBlock();
|
|
4221
4216
|
const settleBoundary = this.#resolveBoundaryLatchTarget(targetIndex, targetBlock);
|
|
4222
|
-
this.#materializeAnimatedAnchor(
|
|
4217
|
+
this.#materializeAnimatedAnchor(now);
|
|
4223
4218
|
const currentState = this.#options.normalizeListState(this.#options.readListState());
|
|
4224
4219
|
const targetAnchor = this.#options.getTargetAnchor(targetIndex, targetBlock);
|
|
4225
4220
|
if (!(options.animated ?? true)) {
|
|
@@ -4252,7 +4247,7 @@ var JumpController = class JumpController {
|
|
|
4252
4247
|
}
|
|
4253
4248
|
this.#jumpAnimation = {
|
|
4254
4249
|
path,
|
|
4255
|
-
startTime:
|
|
4250
|
+
startTime: now,
|
|
4256
4251
|
duration,
|
|
4257
4252
|
needsMoreFrames: true,
|
|
4258
4253
|
onComplete: options.onComplete
|
|
@@ -4306,6 +4301,12 @@ var JumpController = class JumpController {
|
|
|
4306
4301
|
#clearPendingTransitionSettleReconcile() {
|
|
4307
4302
|
this.#pendingTransitionSettleReconcile = false;
|
|
4308
4303
|
}
|
|
4304
|
+
#clearAutoFollowObservationState() {
|
|
4305
|
+
this.#autoFollowObservationCountTop = 0;
|
|
4306
|
+
this.#autoFollowObservationCountBottom = 0;
|
|
4307
|
+
this.#pendingAutoFollowInvalidationTop = false;
|
|
4308
|
+
this.#pendingAutoFollowInvalidationBottom = false;
|
|
4309
|
+
}
|
|
4309
4310
|
#materializeAnimatedAnchor(now, direction, count = 0) {
|
|
4310
4311
|
const animation = this.#jumpAnimation;
|
|
4311
4312
|
if (animation == null) return;
|
|
@@ -4320,6 +4321,20 @@ var JumpController = class JumpController {
|
|
|
4320
4321
|
if (boundary === "top") this.#canAutoFollowTop = value;
|
|
4321
4322
|
else this.#canAutoFollowBottom = value;
|
|
4322
4323
|
}
|
|
4324
|
+
#syncObservedOrInvalidatedBoundary(boundary, capabilities) {
|
|
4325
|
+
const isObserved = this.#readAutoFollowObservationCount(boundary) > 0;
|
|
4326
|
+
const isInvalidated = boundary === "top" ? this.#pendingAutoFollowInvalidationTop : this.#pendingAutoFollowInvalidationBottom;
|
|
4327
|
+
if (!isObserved && !isInvalidated) return;
|
|
4328
|
+
this.#setAutoFollowBoundary(boundary, this.#readCapabilityForBoundary(capabilities, boundary), "transition-observation");
|
|
4329
|
+
if (boundary === "top") {
|
|
4330
|
+
this.#pendingAutoFollowInvalidationTop = false;
|
|
4331
|
+
return;
|
|
4332
|
+
}
|
|
4333
|
+
this.#pendingAutoFollowInvalidationBottom = false;
|
|
4334
|
+
}
|
|
4335
|
+
#readAutoFollowObservationCount(boundary) {
|
|
4336
|
+
return boundary === "top" ? this.#autoFollowObservationCountTop : this.#autoFollowObservationCountBottom;
|
|
4337
|
+
}
|
|
4323
4338
|
#syncLastArmedBoundaryFromLatchedState() {
|
|
4324
4339
|
if (this.#canAutoFollowTop === this.#canAutoFollowBottom) return;
|
|
4325
4340
|
this.#lastArmedAutoFollowBoundary = this.#canAutoFollowTop ? "top" : "bottom";
|
|
@@ -4369,777 +4384,176 @@ var JumpController = class JumpController {
|
|
|
4369
4384
|
}
|
|
4370
4385
|
};
|
|
4371
4386
|
//#endregion
|
|
4372
|
-
//#region src/renderer/virtualized/
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
#visibleItems = /* @__PURE__ */ new Set();
|
|
4376
|
-
#previousVisibleItems = /* @__PURE__ */ new Set();
|
|
4377
|
-
#hasSnapshot = false;
|
|
4378
|
-
#snapshotState;
|
|
4379
|
-
#previousSnapshotState;
|
|
4380
|
-
#emptyState;
|
|
4381
|
-
#coversShortList = false;
|
|
4382
|
-
#atStartBoundary = false;
|
|
4383
|
-
#atEndBoundary = false;
|
|
4384
|
-
#minDrawnIndex = Number.POSITIVE_INFINITY;
|
|
4385
|
-
#maxDrawnIndex = Number.NEGATIVE_INFINITY;
|
|
4386
|
-
#topBoundaryItem;
|
|
4387
|
-
#bottomBoundaryItem;
|
|
4388
|
-
get coversShortList() {
|
|
4389
|
-
return this.#hasSnapshot && this.#snapshotState != null && this.#coversShortList;
|
|
4390
|
-
}
|
|
4391
|
-
get hasSnapshot() {
|
|
4392
|
-
return this.#hasSnapshot;
|
|
4393
|
-
}
|
|
4394
|
-
get previousState() {
|
|
4395
|
-
return this.#previousSnapshotState;
|
|
4396
|
-
}
|
|
4397
|
-
readDrawnIndexRange() {
|
|
4398
|
-
if (!Number.isFinite(this.#minDrawnIndex) || !Number.isFinite(this.#maxDrawnIndex)) return;
|
|
4399
|
-
return {
|
|
4400
|
-
minIndex: this.#minDrawnIndex,
|
|
4401
|
-
maxIndex: this.#maxDrawnIndex
|
|
4402
|
-
};
|
|
4403
|
-
}
|
|
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;
|
|
4410
|
-
const nextDrawnItems = /* @__PURE__ */ new Set();
|
|
4411
|
-
const nextVisibleItems = /* @__PURE__ */ new Set();
|
|
4412
|
-
let minVisibleIndex = Number.POSITIVE_INFINITY;
|
|
4413
|
-
let maxVisibleIndex = Number.NEGATIVE_INFINITY;
|
|
4414
|
-
let topMostY = Number.POSITIVE_INFINITY;
|
|
4415
|
-
let bottomMostY = Number.NEGATIVE_INFINITY;
|
|
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;
|
|
4424
|
-
for (const { idx, offset, height } of window.drawList) {
|
|
4425
|
-
const y = offset + effectiveShift + contentOriginY;
|
|
4426
|
-
topMostY = Math.min(topMostY, y);
|
|
4427
|
-
bottomMostY = Math.max(bottomMostY, y + height);
|
|
4428
|
-
const item = items[idx];
|
|
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
|
-
}
|
|
4448
|
-
}
|
|
4449
|
-
this.#drawnItems = nextDrawnItems;
|
|
4450
|
-
this.#visibleItems = nextVisibleItems;
|
|
4451
|
-
this.#hasSnapshot = true;
|
|
4452
|
-
this.#snapshotState = snapshotState;
|
|
4453
|
-
this.#minDrawnIndex = nextMinDrawnIndex;
|
|
4454
|
-
this.#maxDrawnIndex = nextMaxDrawnIndex;
|
|
4455
|
-
this.#topBoundaryItem = nextTopBoundaryItem;
|
|
4456
|
-
this.#bottomBoundaryItem = nextBottomBoundaryItem;
|
|
4457
|
-
this.#emptyState = items.length === 0 && window.drawList.length === 0 ? snapshotState : void 0;
|
|
4458
|
-
const contentHeight = bottomMostY - topMostY;
|
|
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;
|
|
4462
|
-
}
|
|
4463
|
-
matchesCurrentState(position, offset) {
|
|
4464
|
-
return this.#hasSnapshot && this.#snapshotState != null && sameState(this.#snapshotState, position, offset);
|
|
4465
|
-
}
|
|
4466
|
-
matchesBoundaryInsertState(direction, count, position, offset) {
|
|
4467
|
-
if (!this.coversShortList || this.#snapshotState == null) return false;
|
|
4468
|
-
return this.#matchesStateAfterBoundaryInsert(direction, count, position, offset);
|
|
4469
|
-
}
|
|
4470
|
-
matchesFollowBoundaryInsertState(direction, count, position, offset) {
|
|
4471
|
-
if (!this.#hasSnapshot || this.#snapshotState == null) return false;
|
|
4472
|
-
if (direction === "push" ? !this.#atEndBoundary : !this.#atStartBoundary) return false;
|
|
4473
|
-
return this.#matchesStateAfterBoundaryInsert(direction, count, position, offset);
|
|
4474
|
-
}
|
|
4475
|
-
matchesEmptyBoundaryInsertState(direction, count, position, offset) {
|
|
4476
|
-
const emptyState = this.#emptyState;
|
|
4477
|
-
if (!this.#hasSnapshot || emptyState == null) return false;
|
|
4478
|
-
return sameState({
|
|
4479
|
-
position: direction === "unshift" && emptyState.position != null ? emptyState.position + count : emptyState.position,
|
|
4480
|
-
offset: emptyState.offset
|
|
4481
|
-
}, position, offset);
|
|
4482
|
-
}
|
|
4483
|
-
isVisible(item) {
|
|
4484
|
-
return this.#visibleItems.has(item);
|
|
4485
|
-
}
|
|
4486
|
-
wasVisible(item) {
|
|
4487
|
-
return this.#previousVisibleItems.has(item);
|
|
4488
|
-
}
|
|
4489
|
-
tracks(item, retention) {
|
|
4490
|
-
return retention === "drawn" ? this.#drawnItems.has(item) : this.#visibleItems.has(item);
|
|
4491
|
-
}
|
|
4492
|
-
reset() {
|
|
4493
|
-
this.#drawnItems.clear();
|
|
4494
|
-
this.#visibleItems.clear();
|
|
4495
|
-
this.#previousVisibleItems.clear();
|
|
4496
|
-
this.#hasSnapshot = false;
|
|
4497
|
-
this.#snapshotState = void 0;
|
|
4498
|
-
this.#previousSnapshotState = void 0;
|
|
4499
|
-
this.#emptyState = void 0;
|
|
4500
|
-
this.#coversShortList = false;
|
|
4501
|
-
this.#atStartBoundary = false;
|
|
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;
|
|
4507
|
-
}
|
|
4508
|
-
#matchesStateAfterBoundaryInsert(direction, count, position, offset) {
|
|
4509
|
-
const snapshotState = this.#snapshotState;
|
|
4510
|
-
if (snapshotState == null) return false;
|
|
4511
|
-
return sameState({
|
|
4512
|
-
position: direction === "unshift" && snapshotState.position != null ? snapshotState.position + count : snapshotState.position,
|
|
4513
|
-
offset: snapshotState.offset
|
|
4514
|
-
}, position, offset);
|
|
4515
|
-
}
|
|
4516
|
-
};
|
|
4517
|
-
//#endregion
|
|
4518
|
-
//#region src/renderer/virtualized/transition-store.ts
|
|
4519
|
-
var TransitionStore = class {
|
|
4520
|
-
#transitions = /* @__PURE__ */ new Map();
|
|
4521
|
-
get size() {
|
|
4522
|
-
return this.#transitions.size;
|
|
4523
|
-
}
|
|
4524
|
-
has(item) {
|
|
4525
|
-
return this.#transitions.has(item);
|
|
4526
|
-
}
|
|
4527
|
-
set(item, transition) {
|
|
4528
|
-
this.#transitions.set(item, transition);
|
|
4529
|
-
}
|
|
4530
|
-
replace(prevItem, nextItem, transition) {
|
|
4531
|
-
this.#transitions.delete(prevItem);
|
|
4532
|
-
this.#transitions.set(nextItem, transition);
|
|
4533
|
-
}
|
|
4534
|
-
delete(item) {
|
|
4535
|
-
const transition = this.#transitions.get(item);
|
|
4536
|
-
if (transition != null) this.#transitions.delete(item);
|
|
4537
|
-
return transition;
|
|
4538
|
-
}
|
|
4539
|
-
readActive(item, now) {
|
|
4540
|
-
const transition = this.#transitions.get(item);
|
|
4541
|
-
if (transition == null) return;
|
|
4542
|
-
return this.#isComplete(transition, now) ? void 0 : transition;
|
|
4543
|
-
}
|
|
4544
|
-
prepare(now) {
|
|
4545
|
-
for (const transition of this.#transitions.values()) if (!this.#isComplete(transition, now)) return true;
|
|
4546
|
-
return false;
|
|
4547
|
-
}
|
|
4548
|
-
findCompleted(now) {
|
|
4549
|
-
return [...this.#transitions.entries()].filter(([, transition]) => this.#isComplete(transition, now)).map(([item, transition]) => ({
|
|
4550
|
-
item,
|
|
4551
|
-
transition
|
|
4552
|
-
}));
|
|
4553
|
-
}
|
|
4554
|
-
findInvisible(snapshot) {
|
|
4555
|
-
return [...this.#transitions.entries()].filter(([item, transition]) => !snapshot.tracks(item, transition.retention) && !(transition.kind === "insert" && !snapshot.wasVisible(item))).map(([item, transition]) => ({
|
|
4556
|
-
item,
|
|
4557
|
-
transition
|
|
4558
|
-
}));
|
|
4559
|
-
}
|
|
4560
|
-
reset() {
|
|
4561
|
-
this.#transitions.clear();
|
|
4562
|
-
}
|
|
4563
|
-
#isComplete(transition, now) {
|
|
4564
|
-
return getProgress(transition.height.startTime, transition.height.duration, now) >= 1;
|
|
4565
|
-
}
|
|
4566
|
-
};
|
|
4567
|
-
//#endregion
|
|
4568
|
-
//#region src/renderer/virtualized/transition-planner.ts
|
|
4569
|
-
function normalizeDuration(duration) {
|
|
4570
|
-
return Math.max(0, typeof duration === "number" && Number.isFinite(duration) ? duration : 0);
|
|
4387
|
+
//#region src/renderer/virtualized/solver.ts
|
|
4388
|
+
function clamp(value, min, max) {
|
|
4389
|
+
return Math.min(Math.max(value, min), max);
|
|
4571
4390
|
}
|
|
4572
|
-
function
|
|
4573
|
-
return
|
|
4574
|
-
from,
|
|
4575
|
-
to,
|
|
4576
|
-
startTime,
|
|
4577
|
-
duration
|
|
4578
|
-
};
|
|
4391
|
+
function normalizeOffset(offset) {
|
|
4392
|
+
return Number.isFinite(offset) ? offset : 0;
|
|
4579
4393
|
}
|
|
4580
|
-
function
|
|
4394
|
+
function normalizeListPadding(padding) {
|
|
4581
4395
|
return {
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
translateY: createScalarAnimation(fromTranslateY, toTranslateY, startTime, duration)
|
|
4396
|
+
top: typeof padding?.top === "number" && Number.isFinite(padding.top) ? Math.max(0, padding.top) : 0,
|
|
4397
|
+
bottom: typeof padding?.bottom === "number" && Number.isFinite(padding.bottom) ? Math.max(0, padding.bottom) : 0
|
|
4585
4398
|
};
|
|
4586
4399
|
}
|
|
4587
|
-
function
|
|
4588
|
-
|
|
4589
|
-
const
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
if (readVisibleRange(entry.offset + solution.window.shift, entry.height) != null) return entry;
|
|
4593
|
-
}
|
|
4594
|
-
}
|
|
4595
|
-
function isIndexVisible(index, resolveVisibleWindow, readVisibleRange) {
|
|
4596
|
-
return findVisibleEntry(index, resolveVisibleWindow, readVisibleRange) != null;
|
|
4597
|
-
}
|
|
4598
|
-
function resolveAnimationEligibility(params) {
|
|
4599
|
-
if (params.index < 0) return false;
|
|
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);
|
|
4602
|
-
}
|
|
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);
|
|
4610
|
-
}
|
|
4611
|
-
function sampleScalarAnimation(animation, now) {
|
|
4612
|
-
return interpolate(animation.from, animation.to, animation.startTime, animation.duration, now);
|
|
4613
|
-
}
|
|
4614
|
-
function sampleLayerAnimation(layer, now) {
|
|
4615
|
-
const alpha = sampleScalarAnimation(layer.alpha, now);
|
|
4616
|
-
if (alpha <= .001) return;
|
|
4400
|
+
function resolveListViewport(outerHeight, padding) {
|
|
4401
|
+
const height = typeof outerHeight === "number" && Number.isFinite(outerHeight) ? Math.max(0, outerHeight) : 0;
|
|
4402
|
+
const resolvedPadding = normalizeListPadding(padding);
|
|
4403
|
+
const contentTop = resolvedPadding.top;
|
|
4404
|
+
const contentBottom = Math.max(contentTop, height - resolvedPadding.bottom);
|
|
4617
4405
|
return {
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4406
|
+
outerHeight: height,
|
|
4407
|
+
contentTop,
|
|
4408
|
+
contentBottom,
|
|
4409
|
+
contentHeight: contentBottom - contentTop,
|
|
4410
|
+
outerContentTop: -contentTop,
|
|
4411
|
+
outerContentBottom: height - contentTop
|
|
4621
4412
|
};
|
|
4622
4413
|
}
|
|
4623
|
-
function
|
|
4414
|
+
function resolveListLayoutOptions(options = {}) {
|
|
4624
4415
|
return {
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
retention: transition.retention
|
|
4416
|
+
anchorMode: options.anchorMode ?? "top",
|
|
4417
|
+
underflowAlign: options.underflowAlign ?? "top",
|
|
4418
|
+
padding: normalizeListPadding(options.padding)
|
|
4629
4419
|
};
|
|
4630
4420
|
}
|
|
4631
|
-
function
|
|
4632
|
-
if (
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
retention: "drawn"
|
|
4643
|
-
};
|
|
4644
|
-
}
|
|
4421
|
+
function normalizeVisibleState(itemCount, state, layout) {
|
|
4422
|
+
if (itemCount <= 0) return {
|
|
4423
|
+
position: 0,
|
|
4424
|
+
offset: 0
|
|
4425
|
+
};
|
|
4426
|
+
const position = state.position;
|
|
4427
|
+
const fallbackPosition = layout.anchorMode === "top" ? 0 : itemCount - 1;
|
|
4428
|
+
if (typeof position !== "number" || !Number.isFinite(position)) return {
|
|
4429
|
+
position: fallbackPosition,
|
|
4430
|
+
offset: normalizeOffset(state.offset)
|
|
4431
|
+
};
|
|
4645
4432
|
return {
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
height: createScalarAnimation(params.currentVisualState.height, 0, params.now, params.duration),
|
|
4649
|
-
retention: "drawn"
|
|
4433
|
+
position: clamp(Math.trunc(position), 0, itemCount - 1),
|
|
4434
|
+
offset: normalizeOffset(state.offset)
|
|
4650
4435
|
};
|
|
4651
4436
|
}
|
|
4652
|
-
function
|
|
4653
|
-
const
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
}
|
|
4668
|
-
function measureBoundaryInsertItems(direction, count, ctx) {
|
|
4669
|
-
const start = direction === "push" ? ctx.items.length - count : 0;
|
|
4670
|
-
const end = direction === "push" ? ctx.items.length : Math.min(count, ctx.items.length);
|
|
4671
|
-
if (start < 0 || end < start) return;
|
|
4672
|
-
const measured = [];
|
|
4673
|
-
for (let index = start; index < end; index += 1) {
|
|
4674
|
-
const item = ctx.items[index];
|
|
4675
|
-
if (item == null) continue;
|
|
4676
|
-
const node = ctx.renderItem(item);
|
|
4677
|
-
const height = ctx.measureNode(node).height;
|
|
4678
|
-
measured.push({
|
|
4679
|
-
item,
|
|
4680
|
-
node,
|
|
4681
|
-
height
|
|
4682
|
-
});
|
|
4683
|
-
}
|
|
4684
|
-
return measured;
|
|
4685
|
-
}
|
|
4686
|
-
function drawSampledLayers(sampled, y, adapter) {
|
|
4687
|
-
if (sampled.slotHeight <= 0) return false;
|
|
4688
|
-
let result = false;
|
|
4689
|
-
for (const layer of sampled.layers) {
|
|
4690
|
-
const alpha = clamp$1(layer.alpha, 0, 1);
|
|
4691
|
-
if (alpha <= .001) continue;
|
|
4692
|
-
adapter.graphics.save();
|
|
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
|
-
}
|
|
4699
|
-
if (typeof adapter.graphics.globalAlpha === "number") adapter.graphics.globalAlpha *= alpha;
|
|
4700
|
-
if (adapter.drawNode(layer.node, 0, y + layer.translateY)) result = true;
|
|
4701
|
-
} finally {
|
|
4702
|
-
adapter.graphics.restore();
|
|
4437
|
+
function resolveVisibleWindow(items, state, viewportHeight, resolveItem, layout) {
|
|
4438
|
+
const viewport = typeof viewportHeight === "number" ? resolveListViewport(viewportHeight, layout.padding) : viewportHeight;
|
|
4439
|
+
const contentHeight = viewport.contentHeight;
|
|
4440
|
+
const normalizedState = normalizeVisibleState(items.length, state, layout);
|
|
4441
|
+
const resolutionPath = /* @__PURE__ */ new Set();
|
|
4442
|
+
const readResolvedItem = (item, idx) => {
|
|
4443
|
+
resolutionPath.add(idx);
|
|
4444
|
+
return resolveItem(item, idx);
|
|
4445
|
+
};
|
|
4446
|
+
if (items.length === 0) return {
|
|
4447
|
+
normalizedState,
|
|
4448
|
+
resolutionPath: [],
|
|
4449
|
+
window: {
|
|
4450
|
+
drawList: [],
|
|
4451
|
+
shift: 0
|
|
4703
4452
|
}
|
|
4704
|
-
}
|
|
4705
|
-
return result;
|
|
4706
|
-
}
|
|
4707
|
-
function planUpdateTransition(prevItem, nextItem, duration, now, currentVisualState, ctx, snapshot, store) {
|
|
4708
|
-
const nextIndex = ctx.items.indexOf(nextItem);
|
|
4709
|
-
const nextNode = ctx.renderItem(nextItem);
|
|
4710
|
-
const nextHeight = ctx.measureNode(nextNode).height;
|
|
4711
|
-
return planExistingItemTransition({
|
|
4712
|
-
kind: "update",
|
|
4713
|
-
duration: normalizeDuration(duration),
|
|
4714
|
-
canAnimate: resolveAnimationEligibility({
|
|
4715
|
-
index: nextIndex,
|
|
4716
|
-
item: prevItem,
|
|
4717
|
-
position: ctx.position,
|
|
4718
|
-
offset: ctx.offset,
|
|
4719
|
-
snapshot,
|
|
4720
|
-
hasActiveTransition: store.has(prevItem),
|
|
4721
|
-
resolveVisibleWindow: ctx.resolveVisibleWindow,
|
|
4722
|
-
readOuterVisibleRange: ctx.readOuterVisibleRange
|
|
4723
|
-
}),
|
|
4724
|
-
now,
|
|
4725
|
-
currentVisualState,
|
|
4726
|
-
nextNode,
|
|
4727
|
-
nextHeight
|
|
4728
|
-
});
|
|
4729
|
-
}
|
|
4730
|
-
function planDeleteTransition(item, duration, now, currentVisualState, ctx, snapshot, store) {
|
|
4731
|
-
const index = ctx.items.indexOf(item);
|
|
4732
|
-
return planExistingItemTransition({
|
|
4733
|
-
kind: "delete",
|
|
4734
|
-
duration: normalizeDuration(duration),
|
|
4735
|
-
canAnimate: resolveAnimationEligibility({
|
|
4736
|
-
index,
|
|
4737
|
-
item,
|
|
4738
|
-
position: ctx.position,
|
|
4739
|
-
offset: ctx.offset,
|
|
4740
|
-
snapshot,
|
|
4741
|
-
hasActiveTransition: store.has(item),
|
|
4742
|
-
resolveVisibleWindow: ctx.resolveVisibleWindow,
|
|
4743
|
-
readOuterVisibleRange: ctx.readOuterVisibleRange
|
|
4744
|
-
}),
|
|
4745
|
-
now,
|
|
4746
|
-
currentVisualState
|
|
4747
|
-
});
|
|
4748
|
-
}
|
|
4749
|
-
function planBoundaryInsertTransition(direction, count, duration, now, ctx, snapshot) {
|
|
4750
|
-
const normalizedDuration = normalizeDuration(duration);
|
|
4751
|
-
if (count <= 0 || normalizedDuration <= 0) return;
|
|
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);
|
|
4757
|
-
const measuredItems = measureBoundaryInsertItems(direction, count, ctx);
|
|
4758
|
-
if (measuredItems == null) return;
|
|
4759
|
-
return planBoundaryInsertItems({
|
|
4760
|
-
duration: normalizedDuration,
|
|
4761
|
-
animateHeight,
|
|
4762
|
-
now,
|
|
4763
|
-
measuredItems
|
|
4764
|
-
});
|
|
4765
|
-
}
|
|
4766
|
-
function getTransitionedItemHeight(item, now, store, adapter) {
|
|
4767
|
-
const transition = store.readActive(item, now);
|
|
4768
|
-
if (transition != null) return sampleTransition(transition, now).slotHeight;
|
|
4769
|
-
const node = adapter.renderItem(item);
|
|
4770
|
-
return adapter.measureNode(node).height;
|
|
4771
|
-
}
|
|
4772
|
-
function resolveTransitionedItem(item, now, store, adapter, lifecycle) {
|
|
4773
|
-
const transition = store.readActive(item, now);
|
|
4774
|
-
if (transition == null) {
|
|
4775
|
-
const node = adapter.renderItem(item);
|
|
4776
|
-
return {
|
|
4777
|
-
value: {
|
|
4778
|
-
draw: (y) => adapter.drawNode(node, 0, y),
|
|
4779
|
-
hittest: (test, y) => node.hittest(adapter.getRootContext(), {
|
|
4780
|
-
...test,
|
|
4781
|
-
y: test.y - y
|
|
4782
|
-
})
|
|
4783
|
-
},
|
|
4784
|
-
height: adapter.measureNode(node).height
|
|
4785
|
-
};
|
|
4786
|
-
}
|
|
4787
|
-
const sampled = sampleTransition(transition, now);
|
|
4788
|
-
return {
|
|
4789
|
-
value: {
|
|
4790
|
-
draw: (y) => drawSampledLayers(sampled, y, adapter),
|
|
4791
|
-
hittest: () => false
|
|
4792
|
-
},
|
|
4793
|
-
height: sampled.slotHeight
|
|
4794
|
-
};
|
|
4795
|
-
}
|
|
4796
|
-
function readCurrentVisualState(item, now, store, adapter) {
|
|
4797
|
-
const transition = store.readActive(item, now);
|
|
4798
|
-
if (transition != null && transition.layers.length > 0) {
|
|
4799
|
-
const primaryLayer = transition.layers[transition.layers.length - 1];
|
|
4800
|
-
return {
|
|
4801
|
-
node: primaryLayer.node,
|
|
4802
|
-
alpha: sampleScalarAnimation(primaryLayer.alpha, now),
|
|
4803
|
-
height: sampleScalarAnimation(transition.height, now),
|
|
4804
|
-
translateY: sampleScalarAnimation(primaryLayer.translateY, now)
|
|
4805
|
-
};
|
|
4806
|
-
}
|
|
4807
|
-
const node = adapter.renderItem(item);
|
|
4808
|
-
return {
|
|
4809
|
-
node,
|
|
4810
|
-
alpha: 1,
|
|
4811
|
-
height: adapter.measureNode(node).height,
|
|
4812
|
-
translateY: 0
|
|
4813
4453
|
};
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4454
|
+
if (layout.anchorMode === "top") {
|
|
4455
|
+
let { position, offset } = normalizedState;
|
|
4456
|
+
let drawLength = 0;
|
|
4457
|
+
if (offset > 0) if (position === 0) offset = 0;
|
|
4458
|
+
else {
|
|
4459
|
+
for (let i = position - 1; i >= 0; i -= 1) {
|
|
4460
|
+
const { height } = readResolvedItem(items[i], i);
|
|
4461
|
+
position = i;
|
|
4462
|
+
offset -= height;
|
|
4463
|
+
if (offset <= 0) break;
|
|
4824
4464
|
}
|
|
4825
|
-
|
|
4826
|
-
return;
|
|
4465
|
+
if (position === 0 && offset > 0) offset = 0;
|
|
4827
4466
|
}
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
const
|
|
4832
|
-
if (
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4467
|
+
let y = offset;
|
|
4468
|
+
const drawList = [];
|
|
4469
|
+
for (let i = position; i < items.length; i += 1) {
|
|
4470
|
+
const { value, height } = readResolvedItem(items[i], i);
|
|
4471
|
+
if (y + height > 0) {
|
|
4472
|
+
drawList.push({
|
|
4473
|
+
index: i,
|
|
4474
|
+
value,
|
|
4475
|
+
offset: y,
|
|
4476
|
+
height
|
|
4477
|
+
});
|
|
4478
|
+
drawLength += height;
|
|
4479
|
+
} else {
|
|
4480
|
+
offset += height;
|
|
4481
|
+
position = i + 1;
|
|
4836
4482
|
}
|
|
4837
|
-
|
|
4838
|
-
|
|
4483
|
+
y += height;
|
|
4484
|
+
if (y >= contentHeight) break;
|
|
4839
4485
|
}
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
return
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
if (
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4486
|
+
let shift = 0;
|
|
4487
|
+
if (y < contentHeight) {
|
|
4488
|
+
if (drawList.length > 0 && drawList.at(-1)?.index === items.length - 1 && !(drawList.at(-1)?.height > Number.EPSILON)) return finalizeVisibleWindowResult(items.length, viewport, layout, {
|
|
4489
|
+
position,
|
|
4490
|
+
offset
|
|
4491
|
+
}, Array.from(resolutionPath), extendVisibleWindowToOuterBounds(items, {
|
|
4492
|
+
drawList,
|
|
4493
|
+
shift
|
|
4494
|
+
}, viewport, readResolvedItem));
|
|
4495
|
+
if (position === 0 && drawLength < contentHeight) {
|
|
4496
|
+
shift = -offset;
|
|
4497
|
+
offset = 0;
|
|
4498
|
+
} else {
|
|
4499
|
+
shift = contentHeight - y;
|
|
4500
|
+
y = offset += shift;
|
|
4501
|
+
let lastIdx = -1;
|
|
4502
|
+
for (let i = position - 1; i >= 0; i -= 1) {
|
|
4503
|
+
const { value, height } = readResolvedItem(items[i], i);
|
|
4504
|
+
drawLength += height;
|
|
4505
|
+
y -= height;
|
|
4506
|
+
drawList.push({
|
|
4507
|
+
index: i,
|
|
4508
|
+
value,
|
|
4509
|
+
offset: y - shift,
|
|
4510
|
+
height
|
|
4511
|
+
});
|
|
4512
|
+
lastIdx = i;
|
|
4513
|
+
if (y < 0) break;
|
|
4514
|
+
}
|
|
4515
|
+
if (lastIdx === 0 && drawLength < contentHeight) {
|
|
4516
|
+
shift = drawList.at(-1)?.offset == null ? 0 : -drawList.at(-1).offset;
|
|
4517
|
+
position = 0;
|
|
4518
|
+
offset = 0;
|
|
4519
|
+
}
|
|
4853
4520
|
}
|
|
4854
|
-
return;
|
|
4855
|
-
}
|
|
4856
|
-
case "reset":
|
|
4857
|
-
case "set":
|
|
4858
|
-
store.reset();
|
|
4859
|
-
snapshot.reset();
|
|
4860
|
-
return;
|
|
4861
|
-
}
|
|
4862
|
-
}
|
|
4863
|
-
//#endregion
|
|
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
4521
|
}
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
}
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
captureVisibilitySnapshot(window, resolutionPath, items, viewport, snapshotState, readVisibleRange, readOuterVisibleRange) {
|
|
4882
|
-
this.#snapshot.capture(window, resolutionPath, items, viewport, snapshotState, readVisibleRange, readOuterVisibleRange);
|
|
4883
|
-
}
|
|
4884
|
-
pruneInvisible(ctx, lifecycle) {
|
|
4885
|
-
return this.pruneInvisibleAt(getNow(), ctx, lifecycle);
|
|
4886
|
-
}
|
|
4887
|
-
prepare(now, lifecycle) {
|
|
4888
|
-
this.settle(now, lifecycle);
|
|
4889
|
-
return this.#store.prepare(now);
|
|
4522
|
+
return finalizeVisibleWindowResult(items.length, viewport, layout, {
|
|
4523
|
+
position,
|
|
4524
|
+
offset
|
|
4525
|
+
}, Array.from(resolutionPath), extendVisibleWindowToOuterBounds(items, {
|
|
4526
|
+
drawList,
|
|
4527
|
+
shift
|
|
4528
|
+
}, viewport, readResolvedItem));
|
|
4890
4529
|
}
|
|
4891
|
-
|
|
4892
|
-
|
|
4530
|
+
let { position, offset } = normalizedState;
|
|
4531
|
+
let drawLength = 0;
|
|
4532
|
+
if (offset < 0) if (position === items.length - 1) offset = 0;
|
|
4533
|
+
else for (let i = position + 1; i < items.length; i += 1) {
|
|
4534
|
+
const { height } = readResolvedItem(items[i], i);
|
|
4535
|
+
position = i;
|
|
4536
|
+
offset += height;
|
|
4537
|
+
if (offset > 0) break;
|
|
4893
4538
|
}
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
reset() {
|
|
4913
|
-
this.#store.reset();
|
|
4914
|
-
this.#snapshot.reset();
|
|
4915
|
-
}
|
|
4916
|
-
#settleTransitions(removals, now, lifecycle, boundarySnap) {
|
|
4917
|
-
if (removals.length === 0) return false;
|
|
4918
|
-
const anchor = lifecycle.captureVisualAnchor(now);
|
|
4919
|
-
const beforeState = lifecycle.readScrollState();
|
|
4920
|
-
const completedDeleteIndices = [];
|
|
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
|
-
}
|
|
4926
|
-
this.#store.delete(item);
|
|
4927
|
-
if (transition.kind === "delete") lifecycle.onDeleteComplete(item);
|
|
4928
|
-
}
|
|
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();
|
|
4933
|
-
return true;
|
|
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
|
-
}
|
|
4971
|
-
};
|
|
4972
|
-
//#endregion
|
|
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));
|
|
5115
|
-
}
|
|
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;
|
|
4539
|
+
let y = contentHeight + offset;
|
|
4540
|
+
const drawList = [];
|
|
4541
|
+
for (let i = position; i >= 0; i -= 1) {
|
|
4542
|
+
const { value, height } = readResolvedItem(items[i], i);
|
|
4543
|
+
y -= height;
|
|
4544
|
+
if (y <= contentHeight) {
|
|
4545
|
+
drawList.push({
|
|
4546
|
+
index: i,
|
|
4547
|
+
value,
|
|
4548
|
+
offset: y,
|
|
4549
|
+
height
|
|
4550
|
+
});
|
|
4551
|
+
drawLength += height;
|
|
4552
|
+
} else {
|
|
4553
|
+
offset -= height;
|
|
4554
|
+
position = i - 1;
|
|
4555
|
+
}
|
|
4556
|
+
if (y < 0) break;
|
|
5143
4557
|
}
|
|
5144
4558
|
let shift = 0;
|
|
5145
4559
|
if (y > 0) {
|
|
@@ -5149,7 +4563,7 @@ function resolveVisibleWindow(items, state, viewportHeight, resolveItem, layout)
|
|
|
5149
4563
|
for (let i = position + 1; i < items.length; i += 1) {
|
|
5150
4564
|
const { value, height } = readResolvedItem(items[i], i);
|
|
5151
4565
|
drawList.push({
|
|
5152
|
-
|
|
4566
|
+
index: i,
|
|
5153
4567
|
value,
|
|
5154
4568
|
offset: y - shift,
|
|
5155
4569
|
height
|
|
@@ -5187,96 +4601,745 @@ function finalizeVisibleWindowResult(itemCount, viewport, layout, normalizedStat
|
|
|
5187
4601
|
minOffset = Math.min(minOffset, entry.offset);
|
|
5188
4602
|
maxBottom = Math.max(maxBottom, entry.offset + entry.height);
|
|
5189
4603
|
}
|
|
5190
|
-
minIndex = Math.min(minIndex, entry.
|
|
5191
|
-
maxIndex = Math.max(maxIndex, entry.
|
|
4604
|
+
minIndex = Math.min(minIndex, entry.index);
|
|
4605
|
+
maxIndex = Math.max(maxIndex, entry.index);
|
|
4606
|
+
}
|
|
4607
|
+
if (!Number.isFinite(minOffset) || !Number.isFinite(maxBottom)) return {
|
|
4608
|
+
normalizedState,
|
|
4609
|
+
resolutionPath,
|
|
4610
|
+
window
|
|
4611
|
+
};
|
|
4612
|
+
const contentHeight = maxBottom - minOffset;
|
|
4613
|
+
if (minIndex !== 0 || maxIndex !== itemCount - 1 || !(contentHeight < viewportHeight - Number.EPSILON)) return {
|
|
4614
|
+
normalizedState,
|
|
4615
|
+
resolutionPath,
|
|
4616
|
+
window
|
|
4617
|
+
};
|
|
4618
|
+
const desiredTop = layout.underflowAlign === "bottom" ? viewportHeight - contentHeight : 0;
|
|
4619
|
+
return {
|
|
4620
|
+
normalizedState: hasDeferredSlots ? normalizedState : layout.anchorMode === "top" ? {
|
|
4621
|
+
position: 0,
|
|
4622
|
+
offset: 0
|
|
4623
|
+
} : {
|
|
4624
|
+
position: itemCount - 1,
|
|
4625
|
+
offset: 0
|
|
4626
|
+
},
|
|
4627
|
+
resolutionPath,
|
|
4628
|
+
window: {
|
|
4629
|
+
drawList: window.drawList,
|
|
4630
|
+
shift: desiredTop - minOffset
|
|
4631
|
+
}
|
|
4632
|
+
};
|
|
4633
|
+
}
|
|
4634
|
+
function extendVisibleWindowToOuterBounds(items, window, viewport, resolveItem) {
|
|
4635
|
+
if (window.drawList.length === 0 || items.length === 0) return window;
|
|
4636
|
+
const drawList = [...window.drawList];
|
|
4637
|
+
const existingIndices = new Set(drawList.map((entry) => entry.index));
|
|
4638
|
+
let topEntry = drawList[0];
|
|
4639
|
+
let bottomEntry = drawList[0];
|
|
4640
|
+
for (const entry of drawList) {
|
|
4641
|
+
if (entry.offset < topEntry.offset) topEntry = entry;
|
|
4642
|
+
if (entry.offset + entry.height > bottomEntry.offset + bottomEntry.height) bottomEntry = entry;
|
|
4643
|
+
}
|
|
4644
|
+
let topIdx = topEntry.index;
|
|
4645
|
+
let topY = topEntry.offset + window.shift;
|
|
4646
|
+
while (topIdx > 0) {
|
|
4647
|
+
const prevIdx = topIdx - 1;
|
|
4648
|
+
if (existingIndices.has(prevIdx)) {
|
|
4649
|
+
const existing = drawList.find((entry) => entry.index === prevIdx);
|
|
4650
|
+
topIdx = prevIdx;
|
|
4651
|
+
if (existing != null) topY = existing.offset + window.shift;
|
|
4652
|
+
continue;
|
|
4653
|
+
}
|
|
4654
|
+
const { value, height } = resolveItem(items[prevIdx], prevIdx);
|
|
4655
|
+
const prevY = topY - height;
|
|
4656
|
+
if (prevY + height <= viewport.outerContentTop) break;
|
|
4657
|
+
drawList.push({
|
|
4658
|
+
index: prevIdx,
|
|
4659
|
+
value,
|
|
4660
|
+
offset: prevY - window.shift,
|
|
4661
|
+
height
|
|
4662
|
+
});
|
|
4663
|
+
existingIndices.add(prevIdx);
|
|
4664
|
+
topIdx = prevIdx;
|
|
4665
|
+
topY = prevY;
|
|
4666
|
+
}
|
|
4667
|
+
let bottomIdx = bottomEntry.index;
|
|
4668
|
+
let bottomY = bottomEntry.offset + window.shift + bottomEntry.height;
|
|
4669
|
+
while (bottomIdx < items.length - 1) {
|
|
4670
|
+
const nextIdx = bottomIdx + 1;
|
|
4671
|
+
if (existingIndices.has(nextIdx)) {
|
|
4672
|
+
const existing = drawList.find((entry) => entry.index === nextIdx);
|
|
4673
|
+
bottomIdx = nextIdx;
|
|
4674
|
+
if (existing != null) bottomY = Math.max(bottomY, existing.offset + window.shift + existing.height);
|
|
4675
|
+
continue;
|
|
4676
|
+
}
|
|
4677
|
+
const { value, height } = resolveItem(items[nextIdx], nextIdx);
|
|
4678
|
+
if (bottomY >= viewport.outerContentBottom) break;
|
|
4679
|
+
drawList.push({
|
|
4680
|
+
index: nextIdx,
|
|
4681
|
+
value,
|
|
4682
|
+
offset: bottomY - window.shift,
|
|
4683
|
+
height
|
|
4684
|
+
});
|
|
4685
|
+
existingIndices.add(nextIdx);
|
|
4686
|
+
bottomIdx = nextIdx;
|
|
4687
|
+
bottomY += height;
|
|
4688
|
+
}
|
|
4689
|
+
return {
|
|
4690
|
+
drawList,
|
|
4691
|
+
shift: window.shift
|
|
4692
|
+
};
|
|
4693
|
+
}
|
|
4694
|
+
//#endregion
|
|
4695
|
+
//#region src/renderer/virtualized/transition-snapshot.ts
|
|
4696
|
+
var VisibilitySnapshot = class {
|
|
4697
|
+
#drawnItems = /* @__PURE__ */ new Set();
|
|
4698
|
+
#visibleItems = /* @__PURE__ */ new Set();
|
|
4699
|
+
#previousVisibleItems = /* @__PURE__ */ new Set();
|
|
4700
|
+
#hasSnapshot = false;
|
|
4701
|
+
#snapshotState;
|
|
4702
|
+
#previousSnapshotState;
|
|
4703
|
+
#emptyState;
|
|
4704
|
+
#coversShortList = false;
|
|
4705
|
+
#atStartBoundary = false;
|
|
4706
|
+
#atEndBoundary = false;
|
|
4707
|
+
#minDrawnIndex = Number.POSITIVE_INFINITY;
|
|
4708
|
+
#maxDrawnIndex = Number.NEGATIVE_INFINITY;
|
|
4709
|
+
#topBoundaryItem;
|
|
4710
|
+
#bottomBoundaryItem;
|
|
4711
|
+
get coversShortList() {
|
|
4712
|
+
return this.#hasSnapshot && this.#snapshotState != null && this.#coversShortList;
|
|
4713
|
+
}
|
|
4714
|
+
get hasSnapshot() {
|
|
4715
|
+
return this.#hasSnapshot;
|
|
4716
|
+
}
|
|
4717
|
+
get previousState() {
|
|
4718
|
+
return this.#previousSnapshotState;
|
|
4719
|
+
}
|
|
4720
|
+
readDrawnIndexRange() {
|
|
4721
|
+
if (!Number.isFinite(this.#minDrawnIndex) || !Number.isFinite(this.#maxDrawnIndex)) return;
|
|
4722
|
+
return {
|
|
4723
|
+
minIndex: this.#minDrawnIndex,
|
|
4724
|
+
maxIndex: this.#maxDrawnIndex
|
|
4725
|
+
};
|
|
4726
|
+
}
|
|
4727
|
+
readBoundaryItem(boundary) {
|
|
4728
|
+
return boundary === "top" ? this.#topBoundaryItem : this.#bottomBoundaryItem;
|
|
4729
|
+
}
|
|
4730
|
+
capture(window, _resolutionPath, items, viewport, snapshotState, readVisibleRange, readOuterVisibleRange) {
|
|
4731
|
+
this.#previousVisibleItems = this.#visibleItems;
|
|
4732
|
+
this.#previousSnapshotState = this.#snapshotState;
|
|
4733
|
+
const nextDrawnItems = /* @__PURE__ */ new Set();
|
|
4734
|
+
const nextVisibleItems = /* @__PURE__ */ new Set();
|
|
4735
|
+
let minVisibleIndex = Number.POSITIVE_INFINITY;
|
|
4736
|
+
let maxVisibleIndex = Number.NEGATIVE_INFINITY;
|
|
4737
|
+
let topMostY = Number.POSITIVE_INFINITY;
|
|
4738
|
+
let bottomMostY = Number.NEGATIVE_INFINITY;
|
|
4739
|
+
let nextMinDrawnIndex = Number.POSITIVE_INFINITY;
|
|
4740
|
+
let nextMaxDrawnIndex = Number.NEGATIVE_INFINITY;
|
|
4741
|
+
let nextTopBoundaryItem;
|
|
4742
|
+
let nextBottomBoundaryItem;
|
|
4743
|
+
let nextTopBoundaryY = Number.POSITIVE_INFINITY;
|
|
4744
|
+
let nextBottomBoundaryY = Number.NEGATIVE_INFINITY;
|
|
4745
|
+
const effectiveShift = window.shift;
|
|
4746
|
+
const contentOriginY = viewport.contentTop;
|
|
4747
|
+
for (const { index, offset, height } of window.drawList) {
|
|
4748
|
+
const y = offset + effectiveShift + contentOriginY;
|
|
4749
|
+
topMostY = Math.min(topMostY, y);
|
|
4750
|
+
bottomMostY = Math.max(bottomMostY, y + height);
|
|
4751
|
+
const item = items[index];
|
|
4752
|
+
if (item != null && readOuterVisibleRange(y, height) != null) {
|
|
4753
|
+
nextDrawnItems.add(item);
|
|
4754
|
+
nextMinDrawnIndex = Math.min(nextMinDrawnIndex, index);
|
|
4755
|
+
nextMaxDrawnIndex = Math.max(nextMaxDrawnIndex, index);
|
|
4756
|
+
}
|
|
4757
|
+
if (item == null) continue;
|
|
4758
|
+
if (readVisibleRange(y, height) != null) {
|
|
4759
|
+
minVisibleIndex = Math.min(minVisibleIndex, index);
|
|
4760
|
+
maxVisibleIndex = Math.max(maxVisibleIndex, index);
|
|
4761
|
+
nextVisibleItems.add(item);
|
|
4762
|
+
if (y < nextTopBoundaryY) {
|
|
4763
|
+
nextTopBoundaryY = y;
|
|
4764
|
+
nextTopBoundaryItem = item;
|
|
4765
|
+
}
|
|
4766
|
+
if (y + height > nextBottomBoundaryY) {
|
|
4767
|
+
nextBottomBoundaryY = y + height;
|
|
4768
|
+
nextBottomBoundaryItem = item;
|
|
4769
|
+
}
|
|
4770
|
+
}
|
|
4771
|
+
}
|
|
4772
|
+
this.#drawnItems = nextDrawnItems;
|
|
4773
|
+
this.#visibleItems = nextVisibleItems;
|
|
4774
|
+
this.#hasSnapshot = true;
|
|
4775
|
+
this.#snapshotState = snapshotState;
|
|
4776
|
+
this.#minDrawnIndex = nextMinDrawnIndex;
|
|
4777
|
+
this.#maxDrawnIndex = nextMaxDrawnIndex;
|
|
4778
|
+
this.#topBoundaryItem = nextTopBoundaryItem;
|
|
4779
|
+
this.#bottomBoundaryItem = nextBottomBoundaryItem;
|
|
4780
|
+
this.#emptyState = items.length === 0 && window.drawList.length === 0 ? snapshotState : void 0;
|
|
4781
|
+
const contentHeight = bottomMostY - topMostY;
|
|
4782
|
+
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;
|
|
4783
|
+
this.#atStartBoundary = window.drawList.length > 0 && items.length > 0 && minVisibleIndex === 0 && topMostY >= viewport.contentTop - 1e-6;
|
|
4784
|
+
this.#atEndBoundary = window.drawList.length > 0 && items.length > 0 && maxVisibleIndex === items.length - 1 && bottomMostY <= viewport.contentBottom + 1e-6;
|
|
4785
|
+
}
|
|
4786
|
+
matchesCurrentState(position, offset) {
|
|
4787
|
+
return this.#hasSnapshot && this.#snapshotState != null && sameState(this.#snapshotState, position, offset);
|
|
4788
|
+
}
|
|
4789
|
+
matchesBoundaryInsertState(direction, count, position, offset) {
|
|
4790
|
+
if (!this.coversShortList || this.#snapshotState == null) return false;
|
|
4791
|
+
return this.#matchesStateAfterBoundaryInsert(direction, count, position, offset);
|
|
4792
|
+
}
|
|
4793
|
+
matchesFollowBoundaryInsertState(direction, count, position, offset) {
|
|
4794
|
+
if (!this.#hasSnapshot || this.#snapshotState == null) return false;
|
|
4795
|
+
if (direction === "push" ? !this.#atEndBoundary : !this.#atStartBoundary) return false;
|
|
4796
|
+
return this.#matchesStateAfterBoundaryInsert(direction, count, position, offset);
|
|
4797
|
+
}
|
|
4798
|
+
matchesEmptyBoundaryInsertState(direction, count, position, offset) {
|
|
4799
|
+
const emptyState = this.#emptyState;
|
|
4800
|
+
if (!this.#hasSnapshot || emptyState == null) return false;
|
|
4801
|
+
return sameState({
|
|
4802
|
+
position: direction === "unshift" && emptyState.position != null ? emptyState.position + count : emptyState.position,
|
|
4803
|
+
offset: emptyState.offset
|
|
4804
|
+
}, position, offset);
|
|
4805
|
+
}
|
|
4806
|
+
isVisible(item) {
|
|
4807
|
+
return this.#visibleItems.has(item);
|
|
4808
|
+
}
|
|
4809
|
+
wasVisible(item) {
|
|
4810
|
+
return this.#previousVisibleItems.has(item);
|
|
4811
|
+
}
|
|
4812
|
+
tracks(item, retention) {
|
|
4813
|
+
return retention === "drawn" ? this.#drawnItems.has(item) : this.#visibleItems.has(item);
|
|
4814
|
+
}
|
|
4815
|
+
reset() {
|
|
4816
|
+
this.#drawnItems.clear();
|
|
4817
|
+
this.#visibleItems.clear();
|
|
4818
|
+
this.#previousVisibleItems.clear();
|
|
4819
|
+
this.#hasSnapshot = false;
|
|
4820
|
+
this.#snapshotState = void 0;
|
|
4821
|
+
this.#previousSnapshotState = void 0;
|
|
4822
|
+
this.#emptyState = void 0;
|
|
4823
|
+
this.#coversShortList = false;
|
|
4824
|
+
this.#atStartBoundary = false;
|
|
4825
|
+
this.#atEndBoundary = false;
|
|
4826
|
+
this.#minDrawnIndex = Number.POSITIVE_INFINITY;
|
|
4827
|
+
this.#maxDrawnIndex = Number.NEGATIVE_INFINITY;
|
|
4828
|
+
this.#topBoundaryItem = void 0;
|
|
4829
|
+
this.#bottomBoundaryItem = void 0;
|
|
4830
|
+
}
|
|
4831
|
+
#matchesStateAfterBoundaryInsert(direction, count, position, offset) {
|
|
4832
|
+
const snapshotState = this.#snapshotState;
|
|
4833
|
+
if (snapshotState == null) return false;
|
|
4834
|
+
return sameState({
|
|
4835
|
+
position: direction === "unshift" && snapshotState.position != null ? snapshotState.position + count : snapshotState.position,
|
|
4836
|
+
offset: snapshotState.offset
|
|
4837
|
+
}, position, offset);
|
|
4838
|
+
}
|
|
4839
|
+
};
|
|
4840
|
+
//#endregion
|
|
4841
|
+
//#region src/renderer/virtualized/transition-store.ts
|
|
4842
|
+
var TransitionStore = class {
|
|
4843
|
+
#transitions = /* @__PURE__ */ new Map();
|
|
4844
|
+
get size() {
|
|
4845
|
+
return this.#transitions.size;
|
|
4846
|
+
}
|
|
4847
|
+
has(item) {
|
|
4848
|
+
return this.#transitions.has(item);
|
|
4849
|
+
}
|
|
4850
|
+
set(item, transition) {
|
|
4851
|
+
const previous = this.#transitions.get(item);
|
|
4852
|
+
this.#transitions.set(item, transition);
|
|
4853
|
+
return previous;
|
|
4854
|
+
}
|
|
4855
|
+
replace(prevItem, nextItem, transition) {
|
|
4856
|
+
const previous = this.#transitions.get(prevItem);
|
|
4857
|
+
this.#transitions.delete(prevItem);
|
|
4858
|
+
this.#transitions.set(nextItem, transition);
|
|
4859
|
+
return previous;
|
|
4860
|
+
}
|
|
4861
|
+
delete(item) {
|
|
4862
|
+
const transition = this.#transitions.get(item);
|
|
4863
|
+
if (transition != null) this.#transitions.delete(item);
|
|
4864
|
+
return transition;
|
|
4865
|
+
}
|
|
4866
|
+
readActive(item, now) {
|
|
4867
|
+
const transition = this.#transitions.get(item);
|
|
4868
|
+
if (transition == null) return;
|
|
4869
|
+
return this.#isComplete(transition, now) ? void 0 : transition;
|
|
4870
|
+
}
|
|
4871
|
+
prepare(now) {
|
|
4872
|
+
for (const transition of this.#transitions.values()) if (!this.#isComplete(transition, now)) return true;
|
|
4873
|
+
return false;
|
|
4874
|
+
}
|
|
4875
|
+
findCompleted(now) {
|
|
4876
|
+
return [...this.#transitions.entries()].filter(([, transition]) => this.#isComplete(transition, now)).map(([item, transition]) => ({
|
|
4877
|
+
item,
|
|
4878
|
+
transition
|
|
4879
|
+
}));
|
|
4880
|
+
}
|
|
4881
|
+
findInvisible(snapshot) {
|
|
4882
|
+
return [...this.#transitions.entries()].filter(([item, transition]) => !snapshot.tracks(item, transition.retention) && !(transition.kind === "insert" && !snapshot.wasVisible(item))).map(([item, transition]) => ({
|
|
4883
|
+
item,
|
|
4884
|
+
transition
|
|
4885
|
+
}));
|
|
4886
|
+
}
|
|
4887
|
+
entries() {
|
|
4888
|
+
return [...this.#transitions.entries()].map(([item, transition]) => ({
|
|
4889
|
+
item,
|
|
4890
|
+
transition
|
|
4891
|
+
}));
|
|
4892
|
+
}
|
|
4893
|
+
reset() {
|
|
4894
|
+
this.#transitions.clear();
|
|
4895
|
+
}
|
|
4896
|
+
#isComplete(transition, now) {
|
|
4897
|
+
return getProgress(transition.height.startTime, transition.height.duration, now) >= 1;
|
|
4898
|
+
}
|
|
4899
|
+
};
|
|
4900
|
+
//#endregion
|
|
4901
|
+
//#region src/renderer/virtualized/transition-planner.ts
|
|
4902
|
+
function normalizeDuration(duration) {
|
|
4903
|
+
return Math.max(0, typeof duration === "number" && Number.isFinite(duration) ? duration : 0);
|
|
4904
|
+
}
|
|
4905
|
+
function createScalarAnimation(from, to, startTime, duration) {
|
|
4906
|
+
return {
|
|
4907
|
+
from,
|
|
4908
|
+
to,
|
|
4909
|
+
startTime,
|
|
4910
|
+
duration
|
|
4911
|
+
};
|
|
4912
|
+
}
|
|
4913
|
+
function createLayerAnimation(node, fromAlpha, toAlpha, startTime, duration, fromTranslateY, toTranslateY) {
|
|
4914
|
+
return {
|
|
4915
|
+
node,
|
|
4916
|
+
alpha: createScalarAnimation(fromAlpha, toAlpha, startTime, duration),
|
|
4917
|
+
translateY: createScalarAnimation(fromTranslateY, toTranslateY, startTime, duration)
|
|
4918
|
+
};
|
|
4919
|
+
}
|
|
4920
|
+
function findVisibleEntry(index, resolveVisibleWindow, readVisibleRange) {
|
|
4921
|
+
if (index < 0) return;
|
|
4922
|
+
const solution = resolveVisibleWindow();
|
|
4923
|
+
for (const entry of solution.window.drawList) {
|
|
4924
|
+
if (entry.index !== index) continue;
|
|
4925
|
+
if (readVisibleRange(entry.offset + solution.window.shift, entry.height) != null) return entry;
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
function isIndexVisible(index, resolveVisibleWindow, readVisibleRange) {
|
|
4929
|
+
return findVisibleEntry(index, resolveVisibleWindow, readVisibleRange) != null;
|
|
4930
|
+
}
|
|
4931
|
+
function resolveAnimationEligibility(params) {
|
|
4932
|
+
if (params.index < 0) return false;
|
|
4933
|
+
if (params.snapshot.matchesCurrentState(params.position, params.offset)) return params.snapshot.tracks(params.item, "drawn");
|
|
4934
|
+
return isIndexVisible(params.index, params.resolveVisibleWindow, params.readOuterVisibleRange);
|
|
4935
|
+
}
|
|
4936
|
+
function hasVisibleBoundaryInsertItems(direction, count, ctx) {
|
|
4937
|
+
if (count <= 0) return false;
|
|
4938
|
+
const start = direction === "push" ? ctx.items.length - count : 0;
|
|
4939
|
+
const end = direction === "push" ? ctx.items.length : Math.min(count, ctx.items.length);
|
|
4940
|
+
if (start < 0 || end <= start) return false;
|
|
4941
|
+
const solution = ctx.resolveVisibleWindow();
|
|
4942
|
+
return solution.window.drawList.some((entry) => entry.index >= start && entry.index < end && ctx.readOuterVisibleRange(entry.offset + solution.window.shift, entry.height) != null);
|
|
4943
|
+
}
|
|
4944
|
+
function sampleScalarAnimation(animation, now) {
|
|
4945
|
+
return interpolate(animation.from, animation.to, animation.startTime, animation.duration, now);
|
|
4946
|
+
}
|
|
4947
|
+
function sampleLayerAnimation(layer, now) {
|
|
4948
|
+
const alpha = sampleScalarAnimation(layer.alpha, now);
|
|
4949
|
+
if (alpha <= .001) return;
|
|
4950
|
+
return {
|
|
4951
|
+
alpha,
|
|
4952
|
+
node: layer.node,
|
|
4953
|
+
translateY: sampleScalarAnimation(layer.translateY, now)
|
|
4954
|
+
};
|
|
4955
|
+
}
|
|
4956
|
+
function sampleTransition(transition, now) {
|
|
4957
|
+
return {
|
|
4958
|
+
kind: transition.kind,
|
|
4959
|
+
slotHeight: sampleScalarAnimation(transition.height, now),
|
|
4960
|
+
layers: transition.layers.map((layer) => sampleLayerAnimation(layer, now)).filter((layer) => layer != null),
|
|
4961
|
+
retention: transition.retention
|
|
4962
|
+
};
|
|
4963
|
+
}
|
|
4964
|
+
function planExistingItemTransition(params) {
|
|
4965
|
+
if (!params.canAnimate || params.duration <= 0) return;
|
|
4966
|
+
if (params.kind === "update" && !Number.isFinite(params.nextHeight)) return;
|
|
4967
|
+
const layers = [];
|
|
4968
|
+
if (params.currentVisualState.alpha > .001) layers.push(createLayerAnimation(params.currentVisualState.node, params.currentVisualState.alpha, 0, params.now, params.duration, params.currentVisualState.translateY, 0));
|
|
4969
|
+
if (params.kind === "update") {
|
|
4970
|
+
layers.push(createLayerAnimation(params.nextNode, 0, 1, params.now, params.duration, params.currentVisualState.translateY, 0));
|
|
4971
|
+
return {
|
|
4972
|
+
kind: "update",
|
|
4973
|
+
layers,
|
|
4974
|
+
height: createScalarAnimation(params.currentVisualState.height, params.nextHeight, params.now, params.duration),
|
|
4975
|
+
retention: "drawn"
|
|
4976
|
+
};
|
|
4977
|
+
}
|
|
4978
|
+
return {
|
|
4979
|
+
kind: "delete",
|
|
4980
|
+
layers,
|
|
4981
|
+
height: createScalarAnimation(params.currentVisualState.height, 0, params.now, params.duration),
|
|
4982
|
+
retention: "drawn"
|
|
4983
|
+
};
|
|
4984
|
+
}
|
|
4985
|
+
function resolveAutoFollowBoundaryRisk(index, ctx, snapshot) {
|
|
4986
|
+
const drawnRange = snapshot.readDrawnIndexRange();
|
|
4987
|
+
if (index < 0 || !snapshot.hasSnapshot || drawnRange == null || !Number.isFinite(drawnRange.minIndex) || !Number.isFinite(drawnRange.maxIndex)) return;
|
|
4988
|
+
if (ctx.anchorMode === "bottom") return index <= drawnRange.minIndex ? "top" : void 0;
|
|
4989
|
+
return index >= drawnRange.maxIndex ? "bottom" : void 0;
|
|
4990
|
+
}
|
|
4991
|
+
function canClassifyAutoFollowBoundaryRisk(index, snapshot) {
|
|
4992
|
+
return index >= 0 && snapshot.hasSnapshot && snapshot.readDrawnIndexRange() != null;
|
|
4993
|
+
}
|
|
4994
|
+
function beginTransitionAutoFollowObservation(transition, lifecycle) {
|
|
4995
|
+
if (transition.observedAutoFollowBoundary == null) return;
|
|
4996
|
+
lifecycle.beginAutoFollowBoundaryObservation(transition.observedAutoFollowBoundary);
|
|
4997
|
+
}
|
|
4998
|
+
function endTransitionAutoFollowObservation(transition, lifecycle) {
|
|
4999
|
+
if (transition?.observedAutoFollowBoundary == null) return;
|
|
5000
|
+
lifecycle.endAutoFollowBoundaryObservation(transition.observedAutoFollowBoundary);
|
|
5001
|
+
}
|
|
5002
|
+
function invalidateAutoFollowBoundaryRisk(boundary, canClassify, lifecycle) {
|
|
5003
|
+
if (boundary != null) {
|
|
5004
|
+
lifecycle.invalidateAutoFollowBoundary(boundary);
|
|
5005
|
+
return;
|
|
5006
|
+
}
|
|
5007
|
+
if (!canClassify) lifecycle.invalidateAutoFollowBoundary(void 0);
|
|
5008
|
+
}
|
|
5009
|
+
function planBoundaryInsertItems(params) {
|
|
5010
|
+
const entries = [];
|
|
5011
|
+
for (const { item, node, height } of params.measuredItems) {
|
|
5012
|
+
if (!Number.isFinite(height) || height < 0) return;
|
|
5013
|
+
entries.push({
|
|
5014
|
+
item,
|
|
5015
|
+
transition: {
|
|
5016
|
+
kind: "insert",
|
|
5017
|
+
layers: [createLayerAnimation(node, 0, 1, params.now, params.duration, 0, 0)],
|
|
5018
|
+
height: createScalarAnimation(params.animateHeight ? 0 : height, height, params.now, params.duration),
|
|
5019
|
+
retention: "drawn"
|
|
5020
|
+
}
|
|
5021
|
+
});
|
|
5022
|
+
}
|
|
5023
|
+
return entries.length === 0 ? void 0 : { entries };
|
|
5024
|
+
}
|
|
5025
|
+
function measureBoundaryInsertItems(direction, count, ctx) {
|
|
5026
|
+
const start = direction === "push" ? ctx.items.length - count : 0;
|
|
5027
|
+
const end = direction === "push" ? ctx.items.length : Math.min(count, ctx.items.length);
|
|
5028
|
+
if (start < 0 || end < start) return;
|
|
5029
|
+
const measured = [];
|
|
5030
|
+
for (let index = start; index < end; index += 1) {
|
|
5031
|
+
const item = ctx.items[index];
|
|
5032
|
+
if (item == null) continue;
|
|
5033
|
+
const node = ctx.renderItem(item);
|
|
5034
|
+
const height = ctx.measureNode(node).height;
|
|
5035
|
+
measured.push({
|
|
5036
|
+
item,
|
|
5037
|
+
node,
|
|
5038
|
+
height
|
|
5039
|
+
});
|
|
5040
|
+
}
|
|
5041
|
+
return measured;
|
|
5042
|
+
}
|
|
5043
|
+
function drawSampledLayers(sampled, y, adapter) {
|
|
5044
|
+
if (sampled.slotHeight <= 0) return false;
|
|
5045
|
+
let result = false;
|
|
5046
|
+
for (const layer of sampled.layers) {
|
|
5047
|
+
const alpha = clamp$1(layer.alpha, 0, 1);
|
|
5048
|
+
if (alpha <= .001) continue;
|
|
5049
|
+
adapter.graphics.save();
|
|
5050
|
+
try {
|
|
5051
|
+
if (sampled.kind === "insert") {
|
|
5052
|
+
adapter.graphics.beginPath();
|
|
5053
|
+
adapter.graphics.rect(0, y, adapter.graphics.canvas.clientWidth, sampled.slotHeight);
|
|
5054
|
+
adapter.graphics.clip();
|
|
5055
|
+
}
|
|
5056
|
+
if (typeof adapter.graphics.globalAlpha === "number") adapter.graphics.globalAlpha *= alpha;
|
|
5057
|
+
if (adapter.drawNode(layer.node, 0, y + layer.translateY)) result = true;
|
|
5058
|
+
} finally {
|
|
5059
|
+
adapter.graphics.restore();
|
|
5060
|
+
}
|
|
5061
|
+
}
|
|
5062
|
+
return result;
|
|
5063
|
+
}
|
|
5064
|
+
function planUpdateTransition(prevItem, nextItem, duration, now, currentVisualState, ctx, snapshot, store) {
|
|
5065
|
+
const nextIndex = ctx.items.indexOf(nextItem);
|
|
5066
|
+
const nextNode = ctx.renderItem(nextItem);
|
|
5067
|
+
const nextHeight = ctx.measureNode(nextNode).height;
|
|
5068
|
+
return planExistingItemTransition({
|
|
5069
|
+
kind: "update",
|
|
5070
|
+
duration: normalizeDuration(duration),
|
|
5071
|
+
canAnimate: resolveAnimationEligibility({
|
|
5072
|
+
index: nextIndex,
|
|
5073
|
+
item: prevItem,
|
|
5074
|
+
position: ctx.position,
|
|
5075
|
+
offset: ctx.offset,
|
|
5076
|
+
snapshot,
|
|
5077
|
+
hasActiveTransition: store.has(prevItem),
|
|
5078
|
+
resolveVisibleWindow: ctx.resolveVisibleWindow,
|
|
5079
|
+
readOuterVisibleRange: ctx.readOuterVisibleRange
|
|
5080
|
+
}),
|
|
5081
|
+
now,
|
|
5082
|
+
currentVisualState,
|
|
5083
|
+
nextNode,
|
|
5084
|
+
nextHeight
|
|
5085
|
+
});
|
|
5086
|
+
}
|
|
5087
|
+
function planDeleteTransition(item, duration, now, currentVisualState, ctx, snapshot, store) {
|
|
5088
|
+
const index = ctx.items.indexOf(item);
|
|
5089
|
+
return planExistingItemTransition({
|
|
5090
|
+
kind: "delete",
|
|
5091
|
+
duration: normalizeDuration(duration),
|
|
5092
|
+
canAnimate: resolveAnimationEligibility({
|
|
5093
|
+
index,
|
|
5094
|
+
item,
|
|
5095
|
+
position: ctx.position,
|
|
5096
|
+
offset: ctx.offset,
|
|
5097
|
+
snapshot,
|
|
5098
|
+
hasActiveTransition: store.has(item),
|
|
5099
|
+
resolveVisibleWindow: ctx.resolveVisibleWindow,
|
|
5100
|
+
readOuterVisibleRange: ctx.readOuterVisibleRange
|
|
5101
|
+
}),
|
|
5102
|
+
now,
|
|
5103
|
+
currentVisualState
|
|
5104
|
+
});
|
|
5105
|
+
}
|
|
5106
|
+
function planBoundaryInsertTransition(direction, count, duration, now, ctx, snapshot) {
|
|
5107
|
+
const normalizedDuration = normalizeDuration(duration);
|
|
5108
|
+
if (count <= 0 || normalizedDuration <= 0) return;
|
|
5109
|
+
const matchesBoundaryState = snapshot.matchesBoundaryInsertState(direction, count, ctx.position, ctx.offset);
|
|
5110
|
+
const matchesFollowState = snapshot.matchesFollowBoundaryInsertState(direction, count, ctx.position, ctx.offset);
|
|
5111
|
+
const matchesEmptyState = snapshot.matchesEmptyBoundaryInsertState(direction, count, ctx.position, ctx.offset);
|
|
5112
|
+
if (!(matchesBoundaryState || matchesFollowState || matchesEmptyState || snapshot.hasSnapshot && hasVisibleBoundaryInsertItems(direction, count, ctx))) return;
|
|
5113
|
+
const animateHeight = !(direction === "unshift" && matchesFollowState && !matchesBoundaryState && !matchesEmptyState);
|
|
5114
|
+
const measuredItems = measureBoundaryInsertItems(direction, count, ctx);
|
|
5115
|
+
if (measuredItems == null) return;
|
|
5116
|
+
return planBoundaryInsertItems({
|
|
5117
|
+
duration: normalizedDuration,
|
|
5118
|
+
animateHeight,
|
|
5119
|
+
now,
|
|
5120
|
+
measuredItems
|
|
5121
|
+
});
|
|
5122
|
+
}
|
|
5123
|
+
function getTransitionedItemHeight(item, now, store, adapter) {
|
|
5124
|
+
const transition = store.readActive(item, now);
|
|
5125
|
+
if (transition != null) return sampleTransition(transition, now).slotHeight;
|
|
5126
|
+
const node = adapter.renderItem(item);
|
|
5127
|
+
return adapter.measureNode(node).height;
|
|
5128
|
+
}
|
|
5129
|
+
function resolveTransitionedItem(item, now, store, adapter, lifecycle) {
|
|
5130
|
+
const transition = store.readActive(item, now);
|
|
5131
|
+
if (transition == null) {
|
|
5132
|
+
const node = adapter.renderItem(item);
|
|
5133
|
+
return {
|
|
5134
|
+
value: {
|
|
5135
|
+
draw: (y) => adapter.drawNode(node, 0, y),
|
|
5136
|
+
hittest: (test, y) => node.hittest(adapter.getRootContext(), {
|
|
5137
|
+
...test,
|
|
5138
|
+
y: test.y - y
|
|
5139
|
+
})
|
|
5140
|
+
},
|
|
5141
|
+
height: adapter.measureNode(node).height
|
|
5142
|
+
};
|
|
5192
5143
|
}
|
|
5193
|
-
|
|
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;
|
|
5144
|
+
const sampled = sampleTransition(transition, now);
|
|
5205
5145
|
return {
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
} : {
|
|
5210
|
-
position: itemCount - 1,
|
|
5211
|
-
offset: 0
|
|
5146
|
+
value: {
|
|
5147
|
+
draw: (y) => drawSampledLayers(sampled, y, adapter),
|
|
5148
|
+
hittest: () => false
|
|
5212
5149
|
},
|
|
5213
|
-
|
|
5214
|
-
window: {
|
|
5215
|
-
drawList: window.drawList,
|
|
5216
|
-
shift: desiredTop - minOffset
|
|
5217
|
-
}
|
|
5150
|
+
height: sampled.slotHeight
|
|
5218
5151
|
};
|
|
5219
5152
|
}
|
|
5220
|
-
function
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5153
|
+
function readCurrentVisualState(item, now, store, adapter) {
|
|
5154
|
+
const transition = store.readActive(item, now);
|
|
5155
|
+
if (transition != null && transition.layers.length > 0) {
|
|
5156
|
+
const primaryLayer = transition.layers[transition.layers.length - 1];
|
|
5157
|
+
return {
|
|
5158
|
+
node: primaryLayer.node,
|
|
5159
|
+
alpha: sampleScalarAnimation(primaryLayer.alpha, now),
|
|
5160
|
+
height: sampleScalarAnimation(transition.height, now),
|
|
5161
|
+
translateY: sampleScalarAnimation(primaryLayer.translateY, now)
|
|
5162
|
+
};
|
|
5229
5163
|
}
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5164
|
+
const node = adapter.renderItem(item);
|
|
5165
|
+
return {
|
|
5166
|
+
node,
|
|
5167
|
+
alpha: 1,
|
|
5168
|
+
height: adapter.measureNode(node).height,
|
|
5169
|
+
translateY: 0
|
|
5170
|
+
};
|
|
5171
|
+
}
|
|
5172
|
+
function handleTransitionStateChange(store, snapshot, change, ctx, lifecycle, now = getNow()) {
|
|
5173
|
+
switch (change.type) {
|
|
5174
|
+
case "update": {
|
|
5175
|
+
const nextIndex = ctx.items.indexOf(change.nextItem);
|
|
5176
|
+
const canClassifyRisk = canClassifyAutoFollowBoundaryRisk(nextIndex, snapshot);
|
|
5177
|
+
const observedBoundary = resolveAutoFollowBoundaryRisk(nextIndex, ctx, snapshot);
|
|
5178
|
+
const currentVisualState = readCurrentVisualState(change.prevItem, now, store, ctx);
|
|
5179
|
+
const transition = planUpdateTransition(change.prevItem, change.nextItem, change.animation?.duration, now, currentVisualState, ctx, snapshot, store);
|
|
5180
|
+
if (transition == null) {
|
|
5181
|
+
endTransitionAutoFollowObservation(store.delete(change.prevItem), lifecycle);
|
|
5182
|
+
invalidateAutoFollowBoundaryRisk(observedBoundary, canClassifyRisk, lifecycle);
|
|
5183
|
+
return;
|
|
5184
|
+
}
|
|
5185
|
+
transition.observedAutoFollowBoundary = observedBoundary;
|
|
5186
|
+
endTransitionAutoFollowObservation(store.replace(change.prevItem, change.nextItem, transition), lifecycle);
|
|
5187
|
+
beginTransitionAutoFollowObservation(transition, lifecycle);
|
|
5188
|
+
return;
|
|
5239
5189
|
}
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5190
|
+
case "delete": {
|
|
5191
|
+
const index = ctx.items.indexOf(change.item);
|
|
5192
|
+
const canClassifyRisk = canClassifyAutoFollowBoundaryRisk(index, snapshot);
|
|
5193
|
+
const observedBoundary = resolveAutoFollowBoundaryRisk(index, ctx, snapshot);
|
|
5194
|
+
const currentVisualState = readCurrentVisualState(change.item, now, store, ctx);
|
|
5195
|
+
const transition = planDeleteTransition(change.item, change.animation?.duration, now, currentVisualState, ctx, snapshot, store);
|
|
5196
|
+
if (transition == null) {
|
|
5197
|
+
endTransitionAutoFollowObservation(store.delete(change.item), lifecycle);
|
|
5198
|
+
invalidateAutoFollowBoundaryRisk(observedBoundary, canClassifyRisk, lifecycle);
|
|
5199
|
+
lifecycle.onDeleteComplete(change.item);
|
|
5200
|
+
return;
|
|
5201
|
+
}
|
|
5202
|
+
transition.observedAutoFollowBoundary = observedBoundary;
|
|
5203
|
+
endTransitionAutoFollowObservation(store.set(change.item, transition), lifecycle);
|
|
5204
|
+
beginTransitionAutoFollowObservation(transition, lifecycle);
|
|
5205
|
+
return;
|
|
5206
|
+
}
|
|
5207
|
+
case "delete-finalize":
|
|
5208
|
+
endTransitionAutoFollowObservation(store.delete(change.item), lifecycle);
|
|
5209
|
+
lifecycle.invalidateAutoFollowBoundary(void 0);
|
|
5210
|
+
return;
|
|
5211
|
+
case "unshift":
|
|
5212
|
+
case "push": {
|
|
5213
|
+
const plan = planBoundaryInsertTransition(change.type, change.count, change.animation?.duration, now, ctx, snapshot);
|
|
5214
|
+
if (plan == null) return;
|
|
5215
|
+
for (const entry of plan.entries) endTransitionAutoFollowObservation(store.set(entry.item, entry.transition), lifecycle);
|
|
5216
|
+
if (ctx.position == null && snapshot.coversShortList && (change.type === "push" && ctx.anchorMode === "bottom" || change.type === "unshift" && ctx.anchorMode === "top")) {
|
|
5217
|
+
const boundary = change.type === "push" ? "bottom" : "top";
|
|
5218
|
+
const boundaryItem = snapshot.readBoundaryItem(boundary);
|
|
5219
|
+
if (boundaryItem != null) lifecycle.snapItemToViewportBoundary(boundaryItem, boundary);
|
|
5220
|
+
}
|
|
5221
|
+
return;
|
|
5222
|
+
}
|
|
5223
|
+
case "reset":
|
|
5224
|
+
case "set":
|
|
5225
|
+
for (const entry of store.entries()) endTransitionAutoFollowObservation(entry.transition, lifecycle);
|
|
5226
|
+
store.reset();
|
|
5227
|
+
snapshot.reset();
|
|
5228
|
+
return;
|
|
5252
5229
|
}
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5230
|
+
}
|
|
5231
|
+
//#endregion
|
|
5232
|
+
//#region src/renderer/virtualized/transition-controller.ts
|
|
5233
|
+
function remapAnchorAfterDeletes(anchor, deletedIndices) {
|
|
5234
|
+
if (!Number.isFinite(anchor) || deletedIndices.length === 0) return anchor;
|
|
5235
|
+
const sortedIndices = [...deletedIndices].filter((index) => Number.isFinite(index) && index >= 0).sort((a, b) => a - b);
|
|
5236
|
+
let removedBeforeAnchor = 0;
|
|
5237
|
+
for (const index of sortedIndices) {
|
|
5238
|
+
if (anchor > index + 1) {
|
|
5239
|
+
removedBeforeAnchor += 1;
|
|
5261
5240
|
continue;
|
|
5262
5241
|
}
|
|
5263
|
-
|
|
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;
|
|
5242
|
+
if (anchor >= index) return index - removedBeforeAnchor;
|
|
5274
5243
|
}
|
|
5275
|
-
return
|
|
5276
|
-
drawList,
|
|
5277
|
-
shift: window.shift
|
|
5278
|
-
};
|
|
5244
|
+
return anchor - removedBeforeAnchor;
|
|
5279
5245
|
}
|
|
5246
|
+
var TransitionController = class {
|
|
5247
|
+
#store = new TransitionStore();
|
|
5248
|
+
#snapshot = new VisibilitySnapshot();
|
|
5249
|
+
captureVisibilitySnapshot(window, resolutionPath, items, viewport, snapshotState, readVisibleRange, readOuterVisibleRange) {
|
|
5250
|
+
this.#snapshot.capture(window, resolutionPath, items, viewport, snapshotState, readVisibleRange, readOuterVisibleRange);
|
|
5251
|
+
}
|
|
5252
|
+
pruneInvisible(ctx, lifecycle) {
|
|
5253
|
+
return this.pruneInvisibleAt(getNow(), ctx, lifecycle);
|
|
5254
|
+
}
|
|
5255
|
+
prepare(now, lifecycle) {
|
|
5256
|
+
this.settle(now, lifecycle);
|
|
5257
|
+
return this.#store.prepare(now);
|
|
5258
|
+
}
|
|
5259
|
+
canAutoFollowBoundaryInsert(direction, count, position, offset) {
|
|
5260
|
+
return this.#snapshot.matchesFollowBoundaryInsertState(direction, count, position, offset);
|
|
5261
|
+
}
|
|
5262
|
+
getItemHeight(item, now, adapter) {
|
|
5263
|
+
return getTransitionedItemHeight(item, now, this.#store, adapter);
|
|
5264
|
+
}
|
|
5265
|
+
resolveItem(item, now, adapter, lifecycle) {
|
|
5266
|
+
return resolveTransitionedItem(item, now, this.#store, adapter, lifecycle);
|
|
5267
|
+
}
|
|
5268
|
+
handleListStateChange(change, ctx, lifecycle, now = getNow()) {
|
|
5269
|
+
this.settle(now, lifecycle);
|
|
5270
|
+
handleTransitionStateChange(this.#store, this.#snapshot, change, ctx, lifecycle, now);
|
|
5271
|
+
}
|
|
5272
|
+
settle(now, lifecycle) {
|
|
5273
|
+
return this.#settleTransitions(this.#store.findCompleted(now), now, lifecycle);
|
|
5274
|
+
}
|
|
5275
|
+
pruneInvisibleAt(now, ctx, lifecycle) {
|
|
5276
|
+
const removals = this.#store.findInvisible(this.#snapshot);
|
|
5277
|
+
return this.#settleTransitions(removals, now, lifecycle, this.#resolveNaturalBoundarySnap(removals, now, ctx, lifecycle));
|
|
5278
|
+
}
|
|
5279
|
+
reset() {
|
|
5280
|
+
this.#store.reset();
|
|
5281
|
+
this.#snapshot.reset();
|
|
5282
|
+
}
|
|
5283
|
+
#settleTransitions(removals, now, lifecycle, boundarySnap) {
|
|
5284
|
+
if (removals.length === 0) return false;
|
|
5285
|
+
const anchor = lifecycle.captureVisualAnchor(now);
|
|
5286
|
+
const beforeState = lifecycle.readScrollState();
|
|
5287
|
+
const completedDeleteIndices = [];
|
|
5288
|
+
for (const { item, transition } of removals) {
|
|
5289
|
+
if (transition.kind === "delete") {
|
|
5290
|
+
const index = lifecycle.readItemIndex(item);
|
|
5291
|
+
if (index >= 0) completedDeleteIndices.push(index);
|
|
5292
|
+
}
|
|
5293
|
+
const removedTransition = this.#store.delete(item);
|
|
5294
|
+
if (removedTransition?.observedAutoFollowBoundary != null) {
|
|
5295
|
+
lifecycle.endAutoFollowBoundaryObservation(removedTransition.observedAutoFollowBoundary);
|
|
5296
|
+
lifecycle.invalidateAutoFollowBoundary(removedTransition.observedAutoFollowBoundary);
|
|
5297
|
+
}
|
|
5298
|
+
if (transition.kind === "delete") lifecycle.onDeleteComplete(item);
|
|
5299
|
+
}
|
|
5300
|
+
if (anchor != null && Number.isFinite(anchor)) lifecycle.restoreVisualAnchor(remapAnchorAfterDeletes(anchor, completedDeleteIndices));
|
|
5301
|
+
if (boundarySnap != null) lifecycle.snapItemToViewportBoundary(boundarySnap.item, boundarySnap.boundary);
|
|
5302
|
+
const afterState = lifecycle.readScrollState();
|
|
5303
|
+
if (!sameState(beforeState, afterState.position, afterState.offset)) lifecycle.onTransitionSettleScrollAdjusted();
|
|
5304
|
+
return true;
|
|
5305
|
+
}
|
|
5306
|
+
#resolveNaturalBoundarySnap(removals, now, ctx, lifecycle) {
|
|
5307
|
+
const previousState = this.#snapshot.previousState;
|
|
5308
|
+
const drawnRange = this.#snapshot.readDrawnIndexRange();
|
|
5309
|
+
if (previousState == null || drawnRange == null) return;
|
|
5310
|
+
const naturalIndices = [];
|
|
5311
|
+
for (const { item, transition } of removals) {
|
|
5312
|
+
if (transition.kind !== "update" && transition.kind !== "delete") continue;
|
|
5313
|
+
const index = lifecycle.readItemIndex(item);
|
|
5314
|
+
if (index < 0 || !this.#snapshot.wasVisible(item)) return;
|
|
5315
|
+
if (this.#isTransitionVisibleInState(index, previousState, now, ctx)) return;
|
|
5316
|
+
naturalIndices.push(index);
|
|
5317
|
+
}
|
|
5318
|
+
if (naturalIndices.length === 0) return;
|
|
5319
|
+
if (naturalIndices.every((index) => index < drawnRange.minIndex)) {
|
|
5320
|
+
const item = this.#snapshot.readBoundaryItem("top");
|
|
5321
|
+
return item == null ? void 0 : {
|
|
5322
|
+
item,
|
|
5323
|
+
boundary: "top"
|
|
5324
|
+
};
|
|
5325
|
+
}
|
|
5326
|
+
if (naturalIndices.every((index) => index > drawnRange.maxIndex)) {
|
|
5327
|
+
const item = this.#snapshot.readBoundaryItem("bottom");
|
|
5328
|
+
return item == null ? void 0 : {
|
|
5329
|
+
item,
|
|
5330
|
+
boundary: "bottom"
|
|
5331
|
+
};
|
|
5332
|
+
}
|
|
5333
|
+
}
|
|
5334
|
+
#isTransitionVisibleInState(index, state, now, ctx) {
|
|
5335
|
+
const solution = ctx.resolveVisibleWindowForState(state, now);
|
|
5336
|
+
for (const entry of solution.window.drawList) {
|
|
5337
|
+
if (entry.index !== index) continue;
|
|
5338
|
+
return ctx.readOuterVisibleRange(entry.offset + solution.window.shift, entry.height) != null;
|
|
5339
|
+
}
|
|
5340
|
+
return false;
|
|
5341
|
+
}
|
|
5342
|
+
};
|
|
5280
5343
|
//#endregion
|
|
5281
5344
|
//#region src/renderer/virtualized/base.ts
|
|
5282
5345
|
/**
|
|
@@ -5288,6 +5351,7 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
5288
5351
|
static JUMP_DURATION_PER_PIXEL = .7;
|
|
5289
5352
|
#jumpController;
|
|
5290
5353
|
#transitionController = new TransitionController();
|
|
5354
|
+
#listStateOverride;
|
|
5291
5355
|
constructor(graphics, options) {
|
|
5292
5356
|
super(graphics, options);
|
|
5293
5357
|
this.#jumpController = new JumpController({
|
|
@@ -5305,29 +5369,18 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
5305
5369
|
clampItemIndex: this._clampItemIndex.bind(this),
|
|
5306
5370
|
getItemHeight: this._getItemHeight.bind(this)
|
|
5307
5371
|
});
|
|
5308
|
-
subscribeListState(options.list, this, (owner, change) => {
|
|
5309
|
-
owner.#handleListStateChange(change);
|
|
5310
|
-
});
|
|
5311
5372
|
}
|
|
5312
5373
|
/** Current anchor item index. */
|
|
5313
5374
|
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;
|
|
5375
|
+
return this.#listStateOverride?.position ?? this.options.list.position;
|
|
5319
5376
|
}
|
|
5320
5377
|
/** Pixel offset from the anchored item edge. */
|
|
5321
5378
|
get offset() {
|
|
5322
|
-
return this.options.list.offset;
|
|
5323
|
-
}
|
|
5324
|
-
/** Updates the pixel offset from the anchored item edge. */
|
|
5325
|
-
set offset(value) {
|
|
5326
|
-
this.options.list.offset = value;
|
|
5379
|
+
return this.#listStateOverride?.offset ?? this.options.list.offset;
|
|
5327
5380
|
}
|
|
5328
5381
|
/** Items currently available to the renderer. */
|
|
5329
5382
|
get items() {
|
|
5330
|
-
return this.options.list.items;
|
|
5383
|
+
return this.#listStateOverride?.items ?? this.options.list.items;
|
|
5331
5384
|
}
|
|
5332
5385
|
/** Replaces the current item collection. */
|
|
5333
5386
|
set items(value) {
|
|
@@ -5335,6 +5388,7 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
5335
5388
|
}
|
|
5336
5389
|
/** Renders the current visible window. */
|
|
5337
5390
|
render(feedback) {
|
|
5391
|
+
this.#drainPendingListStateChanges();
|
|
5338
5392
|
this.#jumpController.beforeFrame();
|
|
5339
5393
|
this.#jumpController.noteViewportWidth(this.graphics.canvas.clientWidth);
|
|
5340
5394
|
const now = getNow();
|
|
@@ -5358,6 +5412,7 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
5358
5412
|
}
|
|
5359
5413
|
/** Hit-tests the current visible window. */
|
|
5360
5414
|
hittest(test) {
|
|
5415
|
+
this.#drainPendingListStateChanges();
|
|
5361
5416
|
this.#jumpController.beforeFrame();
|
|
5362
5417
|
this.#jumpController.noteViewportWidth(this.graphics.canvas.clientWidth);
|
|
5363
5418
|
const now = getNow();
|
|
@@ -5424,9 +5479,9 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
5424
5479
|
_renderDrawList(list, shift, feedback) {
|
|
5425
5480
|
let result = false;
|
|
5426
5481
|
const viewport = this._getViewportMetrics();
|
|
5427
|
-
for (const {
|
|
5482
|
+
for (const { index, value: item, offset, height } of list) {
|
|
5428
5483
|
const y = offset + shift + viewport.contentTop;
|
|
5429
|
-
if (feedback != null) this._accumulateRenderFeedback(feedback,
|
|
5484
|
+
if (feedback != null) this._accumulateRenderFeedback(feedback, index, y, height);
|
|
5430
5485
|
if (y + height < 0 || y > viewport.outerHeight) continue;
|
|
5431
5486
|
if (item.draw(y)) result = true;
|
|
5432
5487
|
}
|
|
@@ -5446,9 +5501,9 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
5446
5501
|
let topMostY = Number.POSITIVE_INFINITY;
|
|
5447
5502
|
let bottomMostY = Number.NEGATIVE_INFINITY;
|
|
5448
5503
|
const viewport = this._getViewportMetrics();
|
|
5449
|
-
for (const {
|
|
5450
|
-
minIndex = Math.min(minIndex,
|
|
5451
|
-
maxIndex = Math.max(maxIndex,
|
|
5504
|
+
for (const { index, offset, height } of window.drawList) {
|
|
5505
|
+
minIndex = Math.min(minIndex, index);
|
|
5506
|
+
maxIndex = Math.max(maxIndex, index);
|
|
5452
5507
|
const y = offset + window.shift + viewport.contentTop;
|
|
5453
5508
|
topMostY = Math.min(topMostY, y);
|
|
5454
5509
|
bottomMostY = Math.max(bottomMostY, y + height);
|
|
@@ -5539,7 +5594,8 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
5539
5594
|
return resolveListViewport(this.graphics.canvas.clientHeight, this._getLayoutOptions().padding);
|
|
5540
5595
|
}
|
|
5541
5596
|
#handleDeleteComplete(item) {
|
|
5542
|
-
this.options.list
|
|
5597
|
+
finalizeInternalListDelete(this.options.list, item);
|
|
5598
|
+
this.#drainPendingListStateChanges();
|
|
5543
5599
|
}
|
|
5544
5600
|
#getTransitionLifecycleAdapter() {
|
|
5545
5601
|
return {
|
|
@@ -5549,7 +5605,10 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
5549
5605
|
readScrollState: this._readListState.bind(this),
|
|
5550
5606
|
readItemIndex: (item) => this.items.indexOf(item),
|
|
5551
5607
|
snapItemToViewportBoundary: this.#snapItemToViewportBoundary.bind(this),
|
|
5552
|
-
onTransitionSettleScrollAdjusted: () => this.#jumpController.reconcileAutoFollowAfterTransitionSettle()
|
|
5608
|
+
onTransitionSettleScrollAdjusted: () => this.#jumpController.reconcileAutoFollowAfterTransitionSettle(),
|
|
5609
|
+
beginAutoFollowBoundaryObservation: (boundary) => this.#jumpController.beginAutoFollowBoundaryObservation(boundary),
|
|
5610
|
+
endAutoFollowBoundaryObservation: (boundary) => this.#jumpController.endAutoFollowBoundaryObservation(boundary),
|
|
5611
|
+
invalidateAutoFollowBoundary: (boundary) => this.#jumpController.invalidateAutoFollowBoundary(boundary)
|
|
5553
5612
|
};
|
|
5554
5613
|
}
|
|
5555
5614
|
#getVirtualizedRuntime() {
|
|
@@ -5583,9 +5642,25 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
5583
5642
|
anchorMode: this._getLayoutOptions().anchorMode
|
|
5584
5643
|
};
|
|
5585
5644
|
}
|
|
5586
|
-
#handleListStateChange(change) {
|
|
5587
|
-
|
|
5588
|
-
|
|
5645
|
+
#handleListStateChange(change, now = getNow(), snapshot) {
|
|
5646
|
+
this.#listStateOverride = snapshot == null ? void 0 : {
|
|
5647
|
+
items: [...snapshot.items],
|
|
5648
|
+
position: snapshot.position,
|
|
5649
|
+
offset: snapshot.offset
|
|
5650
|
+
};
|
|
5651
|
+
try {
|
|
5652
|
+
const nextChange = this.#jumpController.handleListStateChange(change, now);
|
|
5653
|
+
this.#transitionController.handleListStateChange(nextChange, this.#getTransitionPlanningAdapter(), this.#getTransitionLifecycleAdapter(), now);
|
|
5654
|
+
} finally {
|
|
5655
|
+
this.#listStateOverride = void 0;
|
|
5656
|
+
}
|
|
5657
|
+
}
|
|
5658
|
+
#drainPendingListStateChanges() {
|
|
5659
|
+
while (true) {
|
|
5660
|
+
const changes = drainInternalListStateChanges(this.options.list);
|
|
5661
|
+
if (changes.length === 0) return;
|
|
5662
|
+
for (const change of changes) this.#handleListStateChange(change, readInternalListStateChangeTime(change), readInternalListStateChangeSnapshot(change));
|
|
5663
|
+
}
|
|
5589
5664
|
}
|
|
5590
5665
|
};
|
|
5591
5666
|
//#endregion
|