chat-layout 1.1.0 → 1.1.2
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 -0
- package/index.d.mts +3 -1
- package/index.mjs +155 -49
- package/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,6 +108,7 @@ Notes:
|
|
|
108
108
|
## Migration notes
|
|
109
109
|
|
|
110
110
|
- Use `memoRenderItemBy(keyOf, renderItem)` when list items are primitives.
|
|
111
|
+
- `memoRenderItemBy()` now uses a bounded LRU cache by default; pass `{ maxEntries: Infinity }` to keep the old unbounded behavior explicitly.
|
|
111
112
|
- `FlexItem` exposes `grow`, `shrink`, and `alignSelf`; `basis` is no longer public.
|
|
112
113
|
- `MultilineText` now uses `align` / `physicalAlign` instead of `alignment`.
|
|
113
114
|
- `ListState.position` uses `undefined` for the renderer default anchor.
|
package/index.d.mts
CHANGED
|
@@ -518,7 +518,9 @@ declare function memoRenderItem<C extends CanvasRenderingContext2D, T extends ob
|
|
|
518
518
|
/**
|
|
519
519
|
* Memoizes `renderItem` by a caller-provided cache key.
|
|
520
520
|
*/
|
|
521
|
-
declare function memoRenderItemBy<C extends CanvasRenderingContext2D, T, K>(keyOf: (item: T) => K, renderItem: (item: T) => Node<C
|
|
521
|
+
declare function memoRenderItemBy<C extends CanvasRenderingContext2D, T, K>(keyOf: (item: T) => K, renderItem: (item: T) => Node<C>, options?: {
|
|
522
|
+
maxEntries?: number;
|
|
523
|
+
}): ((item: T) => Node<C>) & {
|
|
522
524
|
reset: (item: T) => boolean;
|
|
523
525
|
resetKey: (key: K) => boolean;
|
|
524
526
|
};
|
package/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { layoutNextLine, layoutWithLines, measureLineStats, measureNaturalWidth, prepareWithSegments } from "@chenglou/pretext";
|
|
2
|
-
import { layoutNextRichInlineLineRange, materializeRichInlineLineRange, measureRichInlineStats, prepareRichInline
|
|
2
|
+
import { layoutNextRichInlineLineRange, materializeRichInlineLineRange, measureRichInlineStats, prepareRichInline } from "@chenglou/pretext/rich-inline";
|
|
3
3
|
//#region src/internal/node-registry.ts
|
|
4
4
|
const registry = /* @__PURE__ */ new WeakMap();
|
|
5
5
|
const revisions = /* @__PURE__ */ new WeakMap();
|
|
@@ -818,17 +818,15 @@ var Place = class extends Wrapper {
|
|
|
818
818
|
};
|
|
819
819
|
const INTRINSIC_MAX_WIDTH = Number.POSITIVE_INFINITY;
|
|
820
820
|
const MIN_CONTENT_WIDTH_EPSILON = .001;
|
|
821
|
-
const fontShiftCache = /* @__PURE__ */ new Map();
|
|
822
|
-
const ellipsisWidthCache = /* @__PURE__ */ new Map();
|
|
823
821
|
let sharedGraphemeSegmenter;
|
|
824
|
-
function readLruValue(cache, key) {
|
|
822
|
+
function readLruValue$1(cache, key) {
|
|
825
823
|
const cached = cache.get(key);
|
|
826
824
|
if (cached == null) return;
|
|
827
825
|
cache.delete(key);
|
|
828
826
|
cache.set(key, cached);
|
|
829
827
|
return cached;
|
|
830
828
|
}
|
|
831
|
-
function writeLruValue(cache, key, value, capacity) {
|
|
829
|
+
function writeLruValue$1(cache, key, value, capacity) {
|
|
832
830
|
if (cache.has(key)) cache.delete(key);
|
|
833
831
|
else if (cache.size >= capacity) {
|
|
834
832
|
const firstKey = cache.keys().next().value;
|
|
@@ -838,17 +836,11 @@ function writeLruValue(cache, key, value, capacity) {
|
|
|
838
836
|
return value;
|
|
839
837
|
}
|
|
840
838
|
function measureFontShift(ctx) {
|
|
841
|
-
const font = ctx.graphics.font;
|
|
842
|
-
const cached = readLruValue(fontShiftCache, font);
|
|
843
|
-
if (cached != null) return cached;
|
|
844
839
|
const { fontBoundingBoxAscent: ascent = 0, fontBoundingBoxDescent: descent = 0 } = ctx.graphics.measureText("M");
|
|
845
|
-
return
|
|
840
|
+
return ascent - descent;
|
|
846
841
|
}
|
|
847
842
|
function measureEllipsisWidth(ctx) {
|
|
848
|
-
|
|
849
|
-
const cached = readLruValue(ellipsisWidthCache, font);
|
|
850
|
-
if (cached != null) return cached;
|
|
851
|
-
return writeLruValue(ellipsisWidthCache, font, ctx.graphics.measureText("…").width, 64);
|
|
843
|
+
return ctx.graphics.measureText("…").width;
|
|
852
844
|
}
|
|
853
845
|
function getGraphemeSegmenter() {
|
|
854
846
|
if (sharedGraphemeSegmenter !== void 0) return sharedGraphemeSegmenter;
|
|
@@ -936,15 +928,14 @@ const LINE_START_CURSOR$1 = {
|
|
|
936
928
|
graphemeIndex: 0
|
|
937
929
|
};
|
|
938
930
|
const preparedTextCache = /* @__PURE__ */ new Map();
|
|
939
|
-
const preparedUnitCache = /* @__PURE__ */ new WeakMap();
|
|
940
931
|
function getPreparedTextCacheKey(text, font, whiteSpace, wordBreak) {
|
|
941
932
|
return `${font}\u0000${whiteSpace}\u0000${wordBreak}\u0000${text}`;
|
|
942
933
|
}
|
|
943
934
|
function readPreparedText(text, font, whiteSpace, wordBreak) {
|
|
944
935
|
const key = getPreparedTextCacheKey(text, font, whiteSpace, wordBreak);
|
|
945
|
-
const cached = readLruValue(preparedTextCache, key);
|
|
936
|
+
const cached = readLruValue$1(preparedTextCache, key);
|
|
946
937
|
if (cached != null) return cached;
|
|
947
|
-
return writeLruValue(preparedTextCache, key, prepareWithSegments(text, font, {
|
|
938
|
+
return writeLruValue$1(preparedTextCache, key, prepareWithSegments(text, font, {
|
|
948
939
|
whiteSpace,
|
|
949
940
|
wordBreak
|
|
950
941
|
}), 512);
|
|
@@ -973,8 +964,6 @@ function measurePreparedMinContentWidth(prepared, overflowWrap = "break-word") {
|
|
|
973
964
|
return maxWidth > 0 ? maxWidth : maxAnyWidth;
|
|
974
965
|
}
|
|
975
966
|
function getPreparedUnits(prepared) {
|
|
976
|
-
const cached = preparedUnitCache.get(prepared);
|
|
977
|
-
if (cached != null) return cached;
|
|
978
967
|
const units = [];
|
|
979
968
|
for (let index = 0; index < prepared.segments.length; index += 1) {
|
|
980
969
|
const segment = prepared.segments[index] ?? "";
|
|
@@ -995,7 +984,6 @@ function getPreparedUnits(prepared) {
|
|
|
995
984
|
width: segmentWidth
|
|
996
985
|
});
|
|
997
986
|
}
|
|
998
|
-
preparedUnitCache.set(prepared, units);
|
|
999
987
|
return units;
|
|
1000
988
|
}
|
|
1001
989
|
function joinUnitText(units, start, end) {
|
|
@@ -1231,6 +1219,8 @@ function layoutTextWithOverflow(ctx, text, maxWidth, options = {}) {
|
|
|
1231
1219
|
//#endregion
|
|
1232
1220
|
//#region src/text/rich.ts
|
|
1233
1221
|
const RICH_PREPARED_CACHE_CAPACITY = 256;
|
|
1222
|
+
const LEADING_COLLAPSIBLE_BOUNDARY_RE = /^[ \t\n\f\r]+/;
|
|
1223
|
+
const TRAILING_COLLAPSIBLE_BOUNDARY_RE = /[ \t\n\f\r]+$/;
|
|
1234
1224
|
const richPreparedCache = /* @__PURE__ */ new Map();
|
|
1235
1225
|
function withFont(ctx, font, cb) {
|
|
1236
1226
|
const previousFont = ctx.graphics.font;
|
|
@@ -1246,20 +1236,87 @@ function getRichPreparedCacheKey(spans, defaultFont) {
|
|
|
1246
1236
|
}
|
|
1247
1237
|
function readRichPrepared(spans, defaultFont) {
|
|
1248
1238
|
const key = getRichPreparedCacheKey(spans, defaultFont);
|
|
1249
|
-
const cached = readLruValue(richPreparedCache, key);
|
|
1239
|
+
const cached = readLruValue$1(richPreparedCache, key);
|
|
1250
1240
|
if (cached != null) return cached;
|
|
1251
|
-
|
|
1241
|
+
const items = spans.map((span) => ({
|
|
1252
1242
|
text: span.text,
|
|
1253
1243
|
font: span.font ?? defaultFont,
|
|
1254
1244
|
break: span.break,
|
|
1255
1245
|
extraWidth: span.extraWidth
|
|
1256
|
-
}))
|
|
1246
|
+
}));
|
|
1247
|
+
const preparedItemIndexBySourceItemIndex = buildPreparedItemIndexBySourceItemIndex(spans);
|
|
1248
|
+
return writeLruValue$1(richPreparedCache, key, {
|
|
1249
|
+
prepared: prepareRichInline(items),
|
|
1250
|
+
preparedItemIndexBySourceItemIndex
|
|
1251
|
+
}, RICH_PREPARED_CACHE_CAPACITY);
|
|
1252
|
+
}
|
|
1253
|
+
function trimRichInlineBoundaryWhitespace(text) {
|
|
1254
|
+
return text.replace(LEADING_COLLAPSIBLE_BOUNDARY_RE, "").replace(TRAILING_COLLAPSIBLE_BOUNDARY_RE, "");
|
|
1255
|
+
}
|
|
1256
|
+
function buildPreparedItemIndexBySourceItemIndex(spans) {
|
|
1257
|
+
const preparedItemIndexBySourceItemIndex = Array.from({ length: spans.length });
|
|
1258
|
+
let preparedItemIndex = 0;
|
|
1259
|
+
for (let index = 0; index < spans.length; index += 1) {
|
|
1260
|
+
if (trimRichInlineBoundaryWhitespace(spans[index].text).length === 0) continue;
|
|
1261
|
+
preparedItemIndexBySourceItemIndex[index] = preparedItemIndex;
|
|
1262
|
+
preparedItemIndex += 1;
|
|
1263
|
+
}
|
|
1264
|
+
return preparedItemIndexBySourceItemIndex;
|
|
1265
|
+
}
|
|
1266
|
+
function getRichFragmentStartCursor(prepared, fragment) {
|
|
1267
|
+
const itemIndex = prepared.preparedItemIndexBySourceItemIndex[fragment.itemIndex];
|
|
1268
|
+
if (itemIndex == null) return null;
|
|
1269
|
+
return {
|
|
1270
|
+
itemIndex,
|
|
1271
|
+
segmentIndex: fragment.start.segmentIndex,
|
|
1272
|
+
graphemeIndex: fragment.start.graphemeIndex
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
function splitOverflowingRichLineRange(prepared, lineRange, maxWidth) {
|
|
1276
|
+
if (lineRange.width <= maxWidth || lineRange.fragments.length <= 1) return lineRange;
|
|
1277
|
+
const trailingFragment = lineRange.fragments[lineRange.fragments.length - 1];
|
|
1278
|
+
const splitCursor = getRichFragmentStartCursor(prepared, trailingFragment);
|
|
1279
|
+
if (splitCursor == null) return lineRange;
|
|
1280
|
+
const fragments = lineRange.fragments.slice(0, -1);
|
|
1281
|
+
return {
|
|
1282
|
+
fragments,
|
|
1283
|
+
width: fragments.reduce((total, fragment) => total + fragment.gapBefore + fragment.occupiedWidth, 0),
|
|
1284
|
+
end: splitCursor
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
function layoutNextConstrainedRichInlineLineRange(prepared, maxWidth, start) {
|
|
1288
|
+
const lineRange = layoutNextRichInlineLineRange(prepared.prepared, maxWidth, start);
|
|
1289
|
+
if (lineRange == null) return null;
|
|
1290
|
+
return splitOverflowingRichLineRange(prepared, lineRange, maxWidth);
|
|
1291
|
+
}
|
|
1292
|
+
function walkConstrainedRichInlineLineRanges(prepared, maxWidth, onLine) {
|
|
1293
|
+
let lineCount = 0;
|
|
1294
|
+
let cursor;
|
|
1295
|
+
while (true) {
|
|
1296
|
+
const lineRange = layoutNextConstrainedRichInlineLineRange(prepared, maxWidth, cursor);
|
|
1297
|
+
if (lineRange == null) return lineCount;
|
|
1298
|
+
onLine(lineRange);
|
|
1299
|
+
lineCount += 1;
|
|
1300
|
+
cursor = lineRange.end;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
function measureConstrainedRichInlineStats(prepared, maxWidth) {
|
|
1304
|
+
let lineCount = 0;
|
|
1305
|
+
let maxLineWidth = 0;
|
|
1306
|
+
walkConstrainedRichInlineLineRanges(prepared, maxWidth, (lineRange) => {
|
|
1307
|
+
lineCount += 1;
|
|
1308
|
+
if (lineRange.width > maxLineWidth) maxLineWidth = lineRange.width;
|
|
1309
|
+
});
|
|
1310
|
+
return {
|
|
1311
|
+
lineCount,
|
|
1312
|
+
maxLineWidth
|
|
1313
|
+
};
|
|
1257
1314
|
}
|
|
1258
1315
|
function measureRichFragmentShift(ctx, font) {
|
|
1259
1316
|
return withFont(ctx, font, () => measureFontShift(ctx));
|
|
1260
1317
|
}
|
|
1261
1318
|
function materializeRichLine(ctx, spans, defaultFont, defaultColor, lineRange, overflowed) {
|
|
1262
|
-
const richLine = materializeRichInlineLineRange(readRichPrepared(spans, defaultFont), lineRange);
|
|
1319
|
+
const richLine = materializeRichInlineLineRange(readRichPrepared(spans, defaultFont).prepared, lineRange);
|
|
1263
1320
|
const fragments = richLine.fragments.map((fragment) => {
|
|
1264
1321
|
const span = spans[fragment.itemIndex];
|
|
1265
1322
|
const font = span?.font ?? defaultFont;
|
|
@@ -1412,7 +1469,7 @@ function layoutRichFirstLineIntrinsic(ctx, spans, defaultFont, defaultColor) {
|
|
|
1412
1469
|
fragments: [],
|
|
1413
1470
|
overflowed: false
|
|
1414
1471
|
};
|
|
1415
|
-
const lineRange = layoutNextRichInlineLineRange(readRichPrepared(spans, defaultFont), INTRINSIC_MAX_WIDTH);
|
|
1472
|
+
const lineRange = layoutNextRichInlineLineRange(readRichPrepared(spans, defaultFont).prepared, INTRINSIC_MAX_WIDTH);
|
|
1416
1473
|
if (lineRange == null) return {
|
|
1417
1474
|
width: 0,
|
|
1418
1475
|
fragments: [],
|
|
@@ -1427,7 +1484,7 @@ function layoutRichFirstLine(ctx, spans, maxWidth, defaultFont, defaultColor) {
|
|
|
1427
1484
|
fragments: [],
|
|
1428
1485
|
overflowed: false
|
|
1429
1486
|
};
|
|
1430
|
-
const lineRange =
|
|
1487
|
+
const lineRange = layoutNextConstrainedRichInlineLineRange(readRichPrepared(spans, defaultFont), clampedMaxWidth);
|
|
1431
1488
|
if (lineRange == null) return {
|
|
1432
1489
|
width: 0,
|
|
1433
1490
|
fragments: [],
|
|
@@ -1454,7 +1511,7 @@ function measureRichText(_ctx, spans, maxWidth, defaultFont) {
|
|
|
1454
1511
|
width: 0,
|
|
1455
1512
|
lineCount: 0
|
|
1456
1513
|
};
|
|
1457
|
-
const { maxLineWidth: width, lineCount } =
|
|
1514
|
+
const { maxLineWidth: width, lineCount } = measureConstrainedRichInlineStats(readRichPrepared(spans, defaultFont), maxWidth);
|
|
1458
1515
|
return {
|
|
1459
1516
|
width,
|
|
1460
1517
|
lineCount
|
|
@@ -1465,7 +1522,7 @@ function measureRichTextIntrinsic(_ctx, spans, defaultFont) {
|
|
|
1465
1522
|
width: 0,
|
|
1466
1523
|
lineCount: 0
|
|
1467
1524
|
};
|
|
1468
|
-
const { maxLineWidth: width, lineCount } = measureRichInlineStats(readRichPrepared(spans, defaultFont), INTRINSIC_MAX_WIDTH);
|
|
1525
|
+
const { maxLineWidth: width, lineCount } = measureRichInlineStats(readRichPrepared(spans, defaultFont).prepared, INTRINSIC_MAX_WIDTH);
|
|
1469
1526
|
return {
|
|
1470
1527
|
width,
|
|
1471
1528
|
lineCount
|
|
@@ -1487,7 +1544,7 @@ function measureRichTextMinContent(_ctx, spans, defaultFont, overflowWrap = "bre
|
|
|
1487
1544
|
width: 0,
|
|
1488
1545
|
lineCount: 0
|
|
1489
1546
|
};
|
|
1490
|
-
const { lineCount } =
|
|
1547
|
+
const { lineCount } = measureConstrainedRichInlineStats(readRichPrepared(spans, defaultFont), Math.max(maxWidth, MIN_CONTENT_WIDTH_EPSILON));
|
|
1491
1548
|
return {
|
|
1492
1549
|
width: maxWidth,
|
|
1493
1550
|
lineCount
|
|
@@ -1501,7 +1558,7 @@ function layoutRichText(ctx, spans, maxWidth, defaultFont, defaultColor) {
|
|
|
1501
1558
|
};
|
|
1502
1559
|
const prepared = readRichPrepared(spans, defaultFont);
|
|
1503
1560
|
const lineRanges = [];
|
|
1504
|
-
|
|
1561
|
+
walkConstrainedRichInlineLineRanges(prepared, maxWidth, (lineRange) => lineRanges.push(lineRange));
|
|
1505
1562
|
if (lineRanges.length === 0) return {
|
|
1506
1563
|
width: 0,
|
|
1507
1564
|
lines: [],
|
|
@@ -2086,27 +2143,56 @@ var DebugRenderer = class extends BaseRenderer {
|
|
|
2086
2143
|
}
|
|
2087
2144
|
};
|
|
2088
2145
|
//#endregion
|
|
2146
|
+
//#region src/renderer/weak-listeners.ts
|
|
2147
|
+
function pruneWeakListenerMap(listeners) {
|
|
2148
|
+
for (const [token, listener] of listeners) if (listener.ownerRef.deref() == null) listeners.delete(token);
|
|
2149
|
+
}
|
|
2150
|
+
function emitWeakListeners(listeners, event) {
|
|
2151
|
+
for (const [token, listener] of [...listeners]) {
|
|
2152
|
+
const owner = listener.ownerRef.deref();
|
|
2153
|
+
if (owner == null) {
|
|
2154
|
+
listeners.delete(token);
|
|
2155
|
+
continue;
|
|
2156
|
+
}
|
|
2157
|
+
listener.notify(owner, event);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
//#endregion
|
|
2089
2161
|
//#region src/renderer/list-state.ts
|
|
2090
2162
|
const listStateListeners = /* @__PURE__ */ new WeakMap();
|
|
2163
|
+
const listStateListenerRegistry = typeof FinalizationRegistry === "function" ? new FinalizationRegistry(({ listRef, token }) => {
|
|
2164
|
+
const list = listRef.deref();
|
|
2165
|
+
if (list == null) return;
|
|
2166
|
+
deleteListStateListener(list, token);
|
|
2167
|
+
}) : null;
|
|
2168
|
+
function deleteListStateListener(list, token) {
|
|
2169
|
+
const listeners = listStateListeners.get(list);
|
|
2170
|
+
if (listeners == null) return;
|
|
2171
|
+
listeners.delete(token);
|
|
2172
|
+
if (listeners.size === 0) listStateListeners.delete(list);
|
|
2173
|
+
}
|
|
2091
2174
|
function emitListStateChange(list, change) {
|
|
2092
2175
|
const listeners = listStateListeners.get(list);
|
|
2093
|
-
if (listeners == null
|
|
2094
|
-
|
|
2176
|
+
if (listeners == null) return;
|
|
2177
|
+
emitWeakListeners(listeners, change);
|
|
2178
|
+
if (listeners.size === 0) listStateListeners.delete(list);
|
|
2095
2179
|
}
|
|
2096
|
-
function subscribeListState(list, listener) {
|
|
2180
|
+
function subscribeListState(list, owner, listener) {
|
|
2097
2181
|
const key = list;
|
|
2098
2182
|
let listeners = listStateListeners.get(key);
|
|
2099
2183
|
if (listeners == null) {
|
|
2100
|
-
listeners = /* @__PURE__ */ new
|
|
2184
|
+
listeners = /* @__PURE__ */ new Map();
|
|
2101
2185
|
listStateListeners.set(key, listeners);
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2186
|
+
} else pruneWeakListenerMap(listeners);
|
|
2187
|
+
const token = Symbol();
|
|
2188
|
+
listeners.set(token, {
|
|
2189
|
+
ownerRef: new WeakRef(owner),
|
|
2190
|
+
notify: listener
|
|
2191
|
+
});
|
|
2192
|
+
listStateListenerRegistry?.register(owner, {
|
|
2193
|
+
listRef: new WeakRef(key),
|
|
2194
|
+
token
|
|
2195
|
+
});
|
|
2110
2196
|
}
|
|
2111
2197
|
var ListState = class {
|
|
2112
2198
|
#items;
|
|
@@ -2200,9 +2286,31 @@ var ListState = class {
|
|
|
2200
2286
|
};
|
|
2201
2287
|
//#endregion
|
|
2202
2288
|
//#region src/renderer/memo.ts
|
|
2289
|
+
const DEFAULT_MEMO_RENDER_ITEM_BY_MAX_ENTRIES = 512;
|
|
2203
2290
|
function isWeakMapKey(value) {
|
|
2204
2291
|
return typeof value === "object" && value !== null || typeof value === "function";
|
|
2205
2292
|
}
|
|
2293
|
+
function normalizeMaxEntries(maxEntries) {
|
|
2294
|
+
if (maxEntries === Number.POSITIVE_INFINITY) return Number.POSITIVE_INFINITY;
|
|
2295
|
+
if (maxEntries == null || !Number.isFinite(maxEntries)) return DEFAULT_MEMO_RENDER_ITEM_BY_MAX_ENTRIES;
|
|
2296
|
+
return Math.max(0, Math.trunc(maxEntries));
|
|
2297
|
+
}
|
|
2298
|
+
function readLruValue(cache, key) {
|
|
2299
|
+
const cached = cache.get(key);
|
|
2300
|
+
if (cached == null) return;
|
|
2301
|
+
cache.delete(key);
|
|
2302
|
+
cache.set(key, cached);
|
|
2303
|
+
return cached;
|
|
2304
|
+
}
|
|
2305
|
+
function writeLruValue(cache, key, value, maxEntries) {
|
|
2306
|
+
if (cache.has(key)) cache.delete(key);
|
|
2307
|
+
else if (Number.isFinite(maxEntries) && cache.size >= maxEntries) {
|
|
2308
|
+
const oldestKey = cache.keys().next().value;
|
|
2309
|
+
if (oldestKey != null) cache.delete(oldestKey);
|
|
2310
|
+
}
|
|
2311
|
+
if (maxEntries > 0) cache.set(key, value);
|
|
2312
|
+
return value;
|
|
2313
|
+
}
|
|
2206
2314
|
/**
|
|
2207
2315
|
* Memoizes `renderItem` by object identity.
|
|
2208
2316
|
*/
|
|
@@ -2222,15 +2330,14 @@ function memoRenderItem(renderItem) {
|
|
|
2222
2330
|
/**
|
|
2223
2331
|
* Memoizes `renderItem` by a caller-provided cache key.
|
|
2224
2332
|
*/
|
|
2225
|
-
function memoRenderItemBy(keyOf, renderItem) {
|
|
2333
|
+
function memoRenderItemBy(keyOf, renderItem, options = {}) {
|
|
2226
2334
|
const cache = /* @__PURE__ */ new Map();
|
|
2335
|
+
const maxEntries = normalizeMaxEntries(options.maxEntries);
|
|
2227
2336
|
function fn(item) {
|
|
2228
2337
|
const key = keyOf(item);
|
|
2229
|
-
const cached = cache
|
|
2338
|
+
const cached = readLruValue(cache, key);
|
|
2230
2339
|
if (cached != null) return cached;
|
|
2231
|
-
|
|
2232
|
-
cache.set(key, result);
|
|
2233
|
-
return result;
|
|
2340
|
+
return writeLruValue(cache, key, renderItem(item), maxEntries);
|
|
2234
2341
|
}
|
|
2235
2342
|
return Object.assign(fn, {
|
|
2236
2343
|
reset: (item) => cache.delete(keyOf(item)),
|
|
@@ -2272,11 +2379,10 @@ var VirtualizedRenderer = class VirtualizedRenderer extends BaseRenderer {
|
|
|
2272
2379
|
#jumpAnimation;
|
|
2273
2380
|
#replacementAnimations = /* @__PURE__ */ new Map();
|
|
2274
2381
|
#nextReplacementLayerKey = 0;
|
|
2275
|
-
#unsubscribeListState;
|
|
2276
2382
|
constructor(graphics, options) {
|
|
2277
2383
|
super(graphics, options);
|
|
2278
|
-
|
|
2279
|
-
|
|
2384
|
+
subscribeListState(options.list, this, (owner, change) => {
|
|
2385
|
+
owner.#handleListStateChange(change);
|
|
2280
2386
|
});
|
|
2281
2387
|
}
|
|
2282
2388
|
/** Current anchor item index. */
|