react-stately 3.48.0 → 3.49.0

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.
Files changed (134) hide show
  1. package/dist/exports/index.cjs +6 -0
  2. package/dist/exports/index.cjs.map +1 -1
  3. package/dist/exports/index.js +5 -1
  4. package/dist/exports/index.js.map +1 -1
  5. package/dist/exports/index.mjs +5 -1
  6. package/dist/exports/index.mjs.map +1 -1
  7. package/dist/exports/useTokenFieldState.cjs +25 -0
  8. package/dist/exports/useTokenFieldState.cjs.map +1 -0
  9. package/dist/exports/useTokenFieldState.js +19 -0
  10. package/dist/exports/useTokenFieldState.js.map +1 -0
  11. package/dist/exports/useTokenFieldState.mjs +19 -0
  12. package/dist/exports/useTokenFieldState.mjs.map +1 -0
  13. package/dist/private/combobox/useComboBoxState.cjs +2 -1
  14. package/dist/private/combobox/useComboBoxState.cjs.map +1 -1
  15. package/dist/private/combobox/useComboBoxState.js +2 -1
  16. package/dist/private/combobox/useComboBoxState.js.map +1 -1
  17. package/dist/private/combobox/useComboBoxState.mjs +2 -1
  18. package/dist/private/combobox/useComboBoxState.mjs.map +1 -1
  19. package/dist/private/data/useTreeData.cjs +1 -0
  20. package/dist/private/data/useTreeData.cjs.map +1 -1
  21. package/dist/private/data/useTreeData.js +1 -0
  22. package/dist/private/data/useTreeData.js.map +1 -1
  23. package/dist/private/data/useTreeData.mjs +1 -0
  24. package/dist/private/data/useTreeData.mjs.map +1 -1
  25. package/dist/private/datepicker/useDateRangePickerState.cjs +1 -0
  26. package/dist/private/datepicker/useDateRangePickerState.cjs.map +1 -1
  27. package/dist/private/datepicker/useDateRangePickerState.js +1 -0
  28. package/dist/private/datepicker/useDateRangePickerState.js.map +1 -1
  29. package/dist/private/datepicker/useDateRangePickerState.mjs +1 -0
  30. package/dist/private/datepicker/useDateRangePickerState.mjs.map +1 -1
  31. package/dist/private/form/useFormValidationState.cjs +1 -1
  32. package/dist/private/form/useFormValidationState.cjs.map +1 -1
  33. package/dist/private/form/useFormValidationState.js +1 -1
  34. package/dist/private/form/useFormValidationState.js.map +1 -1
  35. package/dist/private/form/useFormValidationState.mjs +1 -1
  36. package/dist/private/form/useFormValidationState.mjs.map +1 -1
  37. package/dist/private/grid/useGridState.cjs +12 -10
  38. package/dist/private/grid/useGridState.cjs.map +1 -1
  39. package/dist/private/grid/useGridState.js +12 -10
  40. package/dist/private/grid/useGridState.js.map +1 -1
  41. package/dist/private/grid/useGridState.mjs +12 -10
  42. package/dist/private/grid/useGridState.mjs.map +1 -1
  43. package/dist/private/layout/ListLayout.cjs +0 -3
  44. package/dist/private/layout/ListLayout.cjs.map +1 -1
  45. package/dist/private/layout/ListLayout.js +0 -3
  46. package/dist/private/layout/ListLayout.js.map +1 -1
  47. package/dist/private/layout/ListLayout.mjs +0 -3
  48. package/dist/private/layout/ListLayout.mjs.map +1 -1
  49. package/dist/private/menu/useMenuTriggerState.cjs.map +1 -1
  50. package/dist/private/menu/useMenuTriggerState.js.map +1 -1
  51. package/dist/private/menu/useMenuTriggerState.mjs.map +1 -1
  52. package/dist/private/menu/useSubmenuTriggerState.cjs +3 -1
  53. package/dist/private/menu/useSubmenuTriggerState.cjs.map +1 -1
  54. package/dist/private/menu/useSubmenuTriggerState.js +3 -1
  55. package/dist/private/menu/useSubmenuTriggerState.js.map +1 -1
  56. package/dist/private/menu/useSubmenuTriggerState.mjs +3 -1
  57. package/dist/private/menu/useSubmenuTriggerState.mjs.map +1 -1
  58. package/dist/private/overlays/useOverlayTriggerState.cjs +4 -1
  59. package/dist/private/overlays/useOverlayTriggerState.cjs.map +1 -1
  60. package/dist/private/overlays/useOverlayTriggerState.js +5 -2
  61. package/dist/private/overlays/useOverlayTriggerState.js.map +1 -1
  62. package/dist/private/overlays/useOverlayTriggerState.mjs +5 -2
  63. package/dist/private/overlays/useOverlayTriggerState.mjs.map +1 -1
  64. package/dist/private/select/useSelectState.cjs +1 -0
  65. package/dist/private/select/useSelectState.cjs.map +1 -1
  66. package/dist/private/select/useSelectState.js +1 -0
  67. package/dist/private/select/useSelectState.js.map +1 -1
  68. package/dist/private/select/useSelectState.mjs +1 -0
  69. package/dist/private/select/useSelectState.mjs.map +1 -1
  70. package/dist/private/slider/useSliderState.cjs.map +1 -1
  71. package/dist/private/slider/useSliderState.js.map +1 -1
  72. package/dist/private/slider/useSliderState.mjs.map +1 -1
  73. package/dist/private/table/TableUtils.cjs +13 -1
  74. package/dist/private/table/TableUtils.cjs.map +1 -1
  75. package/dist/private/table/TableUtils.js +13 -1
  76. package/dist/private/table/TableUtils.js.map +1 -1
  77. package/dist/private/table/TableUtils.mjs +13 -1
  78. package/dist/private/table/TableUtils.mjs.map +1 -1
  79. package/dist/private/table/useTableState.cjs +1 -0
  80. package/dist/private/table/useTableState.cjs.map +1 -1
  81. package/dist/private/table/useTableState.js +1 -0
  82. package/dist/private/table/useTableState.js.map +1 -1
  83. package/dist/private/table/useTableState.mjs +1 -0
  84. package/dist/private/table/useTableState.mjs.map +1 -1
  85. package/dist/private/tokenfield/TokenFieldValue.cjs +287 -0
  86. package/dist/private/tokenfield/TokenFieldValue.cjs.map +1 -0
  87. package/dist/private/tokenfield/TokenFieldValue.js +285 -0
  88. package/dist/private/tokenfield/TokenFieldValue.js.map +1 -0
  89. package/dist/private/tokenfield/TokenFieldValue.mjs +282 -0
  90. package/dist/private/tokenfield/TokenFieldValue.mjs.map +1 -0
  91. package/dist/private/tokenfield/useTokenFieldState.cjs +37 -0
  92. package/dist/private/tokenfield/useTokenFieldState.cjs.map +1 -0
  93. package/dist/private/tokenfield/useTokenFieldState.js +32 -0
  94. package/dist/private/tokenfield/useTokenFieldState.js.map +1 -0
  95. package/dist/private/tokenfield/useTokenFieldState.mjs +32 -0
  96. package/dist/private/tokenfield/useTokenFieldState.mjs.map +1 -0
  97. package/dist/private/tooltip/useTooltipTriggerState.cjs +18 -6
  98. package/dist/private/tooltip/useTooltipTriggerState.cjs.map +1 -1
  99. package/dist/private/tooltip/useTooltipTriggerState.js +19 -7
  100. package/dist/private/tooltip/useTooltipTriggerState.js.map +1 -1
  101. package/dist/private/tooltip/useTooltipTriggerState.mjs +19 -7
  102. package/dist/private/tooltip/useTooltipTriggerState.mjs.map +1 -1
  103. package/dist/private/virtualizer/ScrollAnchor.cjs +136 -0
  104. package/dist/private/virtualizer/ScrollAnchor.cjs.map +1 -0
  105. package/dist/private/virtualizer/ScrollAnchor.js +132 -0
  106. package/dist/private/virtualizer/ScrollAnchor.js.map +1 -0
  107. package/dist/private/virtualizer/ScrollAnchor.mjs +131 -0
  108. package/dist/private/virtualizer/ScrollAnchor.mjs.map +1 -0
  109. package/dist/private/virtualizer/Virtualizer.cjs +50 -1
  110. package/dist/private/virtualizer/Virtualizer.cjs.map +1 -1
  111. package/dist/private/virtualizer/Virtualizer.js +54 -1
  112. package/dist/private/virtualizer/Virtualizer.js.map +1 -1
  113. package/dist/private/virtualizer/Virtualizer.mjs +50 -1
  114. package/dist/private/virtualizer/Virtualizer.mjs.map +1 -1
  115. package/dist/private/virtualizer/useVirtualizerState.cjs +2 -1
  116. package/dist/private/virtualizer/useVirtualizerState.cjs.map +1 -1
  117. package/dist/private/virtualizer/useVirtualizerState.js +2 -1
  118. package/dist/private/virtualizer/useVirtualizerState.js.map +1 -1
  119. package/dist/private/virtualizer/useVirtualizerState.mjs +2 -1
  120. package/dist/private/virtualizer/useVirtualizerState.mjs.map +1 -1
  121. package/dist/types/exports/index.d.ts +4 -0
  122. package/dist/types/exports/useTokenFieldState.d.ts +4 -0
  123. package/dist/types/src/combobox/useComboBoxState.d.ts +1 -1
  124. package/dist/types/src/menu/useMenuTriggerState.d.ts +1 -1
  125. package/dist/types/src/overlays/useOverlayTriggerState.d.ts +9 -0
  126. package/dist/types/src/table/useTableState.d.ts +7 -0
  127. package/dist/types/src/tokenfield/TokenFieldValue.d.ts +71 -0
  128. package/dist/types/src/tokenfield/useTokenFieldState.d.ts +19 -0
  129. package/dist/types/src/tooltip/useTooltipTriggerState.d.ts +5 -0
  130. package/dist/types/src/virtualizer/ScrollAnchor.d.ts +81 -0
  131. package/dist/types/src/virtualizer/Virtualizer.d.ts +1 -0
  132. package/dist/types/src/virtualizer/types.d.ts +2 -0
  133. package/package.json +5 -5
  134. package/useTokenFieldState/package.json +5 -0
@@ -0,0 +1,131 @@
1
+ import {Rect as $af827553fd3cf456$export$c79fc6492f3af13d} from "./Rect.mjs";
2
+
3
+ /*
4
+ * Copyright 2026 Adobe. All rights reserved.
5
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License. You may obtain a copy
7
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software distributed under
10
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
11
+ * OF ANY KIND, either express or implied. See the License for the specific language
12
+ * governing permissions and limitations under the License.
13
+ */
14
+ /**
15
+ * Minimum overlap an item must have with the viewport, along the scroll axis,
16
+ * to be eligible as a scroll anchor. Without this, an item that only overlaps the viewport
17
+ * by a sliver (e.g. 1px, essentially scrolled out of view) can still "win" the anchor
18
+ * tie-break over a substantially visible item.
19
+ */ const $78014f4785404d8e$var$MIN_ANCHOR_OVERLAP = 4;
20
+ function $78014f4785404d8e$var$dimensionForAxis(axis) {
21
+ return axis === 'x' ? 'width' : 'height';
22
+ }
23
+ function $78014f4785404d8e$export$413719533c5919c9(anchor, axis, getLayoutInfo, visibleRect, contentSize) {
24
+ let finalInfo = getLayoutInfo(anchor.key);
25
+ if (!finalInfo) return null;
26
+ let adjustment = finalInfo.rect[anchor.corner][axis] - visibleRect[axis] - anchor.offset;
27
+ if (adjustment === 0) return null;
28
+ let target = visibleRect[axis] + adjustment;
29
+ let dimension = $78014f4785404d8e$var$dimensionForAxis(axis);
30
+ let max = Math.max(0, contentSize[dimension] - visibleRect[dimension]);
31
+ let clamped = Math.max(0, Math.min(max, target));
32
+ return clamped !== visibleRect[axis] ? clamped : null;
33
+ }
34
+ function $78014f4785404d8e$export$69fb4c6de5f339b7(edge, axis, visibleRect, visibleLayoutInfos, isAnchorable = ()=>true) {
35
+ let dimension = $78014f4785404d8e$var$dimensionForAxis(axis);
36
+ let best = null;
37
+ for (let [key, layoutInfo] of visibleLayoutInfos){
38
+ if (!layoutInfo || !isAnchorable(layoutInfo)) continue;
39
+ let overlap = layoutInfo.rect.intersection(visibleRect)[dimension];
40
+ if (layoutInfo.rect.area > 0 && overlap >= $78014f4785404d8e$var$MIN_ANCHOR_OVERLAP) {
41
+ let corner = layoutInfo.rect.getCornerInRect(visibleRect) ?? 'topLeft';
42
+ let offset = layoutInfo.rect[corner][axis] - visibleRect[axis];
43
+ let isBetter = !best || (edge === 'end' ? offset < best.offset : offset > best.offset);
44
+ if (isBetter) best = {
45
+ key: key,
46
+ corner: corner,
47
+ offset: offset
48
+ };
49
+ }
50
+ }
51
+ return best;
52
+ }
53
+ function $78014f4785404d8e$export$c74597e403428eac(edge, axis, contentSize, previousVisibleRect) {
54
+ if (edge === 'start') return 0;
55
+ let dimension = $78014f4785404d8e$var$dimensionForAxis(axis);
56
+ return Math.max(0, contentSize[dimension] - previousVisibleRect[dimension]);
57
+ }
58
+ function $78014f4785404d8e$export$f644bffd4937530e(visibleRect, contentSize, edge, axis, threshold) {
59
+ if (edge === 'start') return visibleRect[axis] <= threshold;
60
+ let dimension = $78014f4785404d8e$var$dimensionForAxis(axis);
61
+ let distanceFromEnd = contentSize[dimension] - (visibleRect[axis] + visibleRect[dimension]);
62
+ return distanceFromEnd <= threshold;
63
+ }
64
+ function $78014f4785404d8e$export$dcb80b718ca193d7(edge, axis, anchor, wasNearAnchorEdge, isScrolling, itemSizeChanged, contentSizeDelta, getLayoutInfo, previousVisibleRect, contentSize) {
65
+ let withTarget = (target)=>axis === 'x' ? new (0, $af827553fd3cf456$export$c79fc6492f3af13d)(target, previousVisibleRect.y, previousVisibleRect.width, previousVisibleRect.height) : new (0, $af827553fd3cf456$export$c79fc6492f3af13d)(previousVisibleRect.x, target, previousVisibleRect.width, previousVisibleRect.height);
66
+ if (anchor) {
67
+ let target = $78014f4785404d8e$export$413719533c5919c9(anchor, axis, getLayoutInfo, previousVisibleRect, contentSize);
68
+ if (target != null) return withTarget(target);
69
+ }
70
+ if (wasNearAnchorEdge && !isScrolling && (!itemSizeChanged || contentSizeDelta > 0)) {
71
+ let target = withTarget($78014f4785404d8e$export$c74597e403428eac(edge, axis, contentSize, previousVisibleRect));
72
+ return target.equals(previousVisibleRect) ? null : target;
73
+ }
74
+ return null;
75
+ }
76
+ class $78014f4785404d8e$export$8ce0fdedc36cb6b {
77
+ /** Resets all tracked state, e.g. when the virtualizer's layout instance changes. */ reset() {
78
+ this.hasSnappedToEdge = false;
79
+ this.hadEstimatedVisibleItems = false;
80
+ this.wasNearAnchorEdge = false;
81
+ }
82
+ /**
83
+ * Captures the anchor from pre-layout view positions.
84
+ */ captureBeforeLayout(anchorInfo, preLayoutInfos, visibleRect) {
85
+ if (!anchorInfo) return null;
86
+ return $78014f4785404d8e$export$69fb4c6de5f339b7(anchorInfo.edge, anchorInfo.axis, visibleRect, preLayoutInfos, anchorInfo.isAnchorable);
87
+ }
88
+ /**
89
+ * Runs the full post-layout decision: updates the tracked state for the next pass, and
90
+ * returns the resolved scroll target, or null if nothing should change.
91
+ */ resolveAfterLayout(options) {
92
+ let { anchorInfo: anchorInfo, anchor: anchor, postLayoutInfos: postLayoutInfos, previousVisibleRect: previousVisibleRect, previousContentSize: previousContentSize, contentSize: contentSize, itemSizeChanged: itemSizeChanged, isScrolling: isScrolling, getLayoutInfo: getLayoutInfo } = options;
93
+ if (!anchorInfo) return null;
94
+ // Read the previous pass's state into locals before any writes below overwrite it.
95
+ let wasSettlingLastPass = this.hadEstimatedVisibleItems;
96
+ let wasNearAnchorEdgeLastPass = this.wasNearAnchorEdge;
97
+ let hasEstimated = false;
98
+ for (let layoutInfo of postLayoutInfos.values())if (layoutInfo.estimatedSize) {
99
+ hasEstimated = true;
100
+ break;
101
+ }
102
+ this.hadEstimatedVisibleItems = hasEstimated;
103
+ // Don't recheck "near edge?" mid-resize because it could look like a scroll that never happened.
104
+ // Reuse the answer from before the resizing started.
105
+ if (!wasSettlingLastPass) this.wasNearAnchorEdge = $78014f4785404d8e$export$f644bffd4937530e(previousVisibleRect, previousContentSize, anchorInfo.edge, anchorInfo.axis, anchorInfo.threshold);
106
+ if (previousVisibleRect.area === 0) return null;
107
+ let dimension = anchorInfo.axis === 'x' ? 'width' : 'height';
108
+ let contentSizeDelta = contentSize[dimension] - previousContentSize[dimension];
109
+ let isFirstAnchoredLayout = !this.hasSnappedToEdge;
110
+ this.hasSnappedToEdge = true;
111
+ // Only modify scroll when content actually changed (or this is the first layout, which always snaps)
112
+ if (!(isFirstAnchoredLayout || contentSizeDelta !== 0 || itemSizeChanged)) return null;
113
+ let wasNearAnchorEdge = isFirstAnchoredLayout || wasSettlingLastPass && wasNearAnchorEdgeLastPass || $78014f4785404d8e$export$f644bffd4937530e(previousVisibleRect, previousContentSize, anchorInfo.edge, anchorInfo.axis, anchorInfo.threshold);
114
+ // A first-ever layout always snaps to the edge, even if the raw distance check says
115
+ // otherwise. Save that real decision here so later passes in this cascade reuse it.
116
+ if (!wasSettlingLastPass) this.wasNearAnchorEdge = wasNearAnchorEdge;
117
+ // Skip restoring to the captured anchor while still resizing because items above it are also still growing,
118
+ // and following it would fall short of the edge.
119
+ let effectiveAnchor = isFirstAnchoredLayout || wasSettlingLastPass && wasNearAnchorEdgeLastPass ? null : anchor;
120
+ return $78014f4785404d8e$export$dcb80b718ca193d7(anchorInfo.edge, anchorInfo.axis, effectiveAnchor, wasNearAnchorEdge, isScrolling, itemSizeChanged, contentSizeDelta, getLayoutInfo, previousVisibleRect, contentSize);
121
+ }
122
+ constructor(){
123
+ this.hasSnappedToEdge = false;
124
+ this.hadEstimatedVisibleItems = false;
125
+ this.wasNearAnchorEdge = false;
126
+ }
127
+ }
128
+
129
+
130
+ export {$78014f4785404d8e$export$413719533c5919c9 as computeScrollAnchorTarget, $78014f4785404d8e$export$69fb4c6de5f339b7 as captureScrollAnchor, $78014f4785404d8e$export$c74597e403428eac as getEdgeSnapTarget, $78014f4785404d8e$export$f644bffd4937530e as isNearEdge, $78014f4785404d8e$export$dcb80b718ca193d7 as resolveScrollAdjustment, $78014f4785404d8e$export$8ce0fdedc36cb6b as ScrollAnchorTracker};
131
+ //# sourceMappingURL=ScrollAnchor.mjs.map
@@ -0,0 +1 @@
1
+ {"mappings":"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","sources":["packages/react-stately/src/virtualizer/ScrollAnchor.ts"],"sourcesContent":["/*\n * Copyright 2026 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {Key} from '@react-types/shared';\nimport {LayoutInfo} from './LayoutInfo';\nimport {Rect, RectCorner} from './Rect';\nimport {Size} from './Size';\n\nexport type ScrollAnchorAxis = 'x' | 'y';\nexport type ScrollAnchorEdge = 'start' | 'end';\nexport interface ScrollAnchor {\n key: Key;\n corner: RectCorner;\n offset: number;\n}\n\nexport interface ScrollAnchorInfo {\n /** Which edge of the content the viewport should stay anchored to. */\n edge: 'start' | 'end';\n /** Which axis `edge` refers to — 'y' for vertical lists, 'x' for horizontal. */\n axis: 'x' | 'y';\n /** Distance (px) from `edge` within which the viewport is considered \"following\" it. */\n threshold: number;\n /**\n * Optional classifier excluding structural/ephemeral layout infos (e.g. loaders) from being\n * selected as the anchor. Defaults to allowing any layoutInfo.\n */\n isAnchorable?: (layoutInfo: LayoutInfo) => boolean;\n}\n\n/**\n * Minimum overlap an item must have with the viewport, along the scroll axis,\n * to be eligible as a scroll anchor. Without this, an item that only overlaps the viewport\n * by a sliver (e.g. 1px, essentially scrolled out of view) can still \"win\" the anchor\n * tie-break over a substantially visible item.\n */\nconst MIN_ANCHOR_OVERLAP = 4;\n\nfunction dimensionForAxis(axis: ScrollAnchorAxis): 'width' | 'height' {\n return axis === 'x' ? 'width' : 'height';\n}\n\n/**\n * Given a previously-captured anchor, computes the new viewport coordinate (along `axis`) needed\n * to keep it at the same offset from the viewport's start.\n */\nexport function computeScrollAnchorTarget(\n anchor: ScrollAnchor,\n axis: ScrollAnchorAxis,\n getLayoutInfo: (key: Key) => LayoutInfo | null,\n visibleRect: Rect,\n contentSize: Size\n): number | null {\n let finalInfo = getLayoutInfo(anchor.key);\n if (!finalInfo) {\n return null;\n }\n let adjustment = finalInfo.rect[anchor.corner][axis] - visibleRect[axis] - anchor.offset;\n if (adjustment === 0) {\n return null;\n }\n let target = visibleRect[axis] + adjustment;\n let dimension = dimensionForAxis(axis);\n let max = Math.max(0, contentSize[dimension] - visibleRect[dimension]);\n let clamped = Math.max(0, Math.min(max, target));\n return clamped !== visibleRect[axis] ? clamped : null;\n}\n\n/**\n * Picks the item to anchor scroll to: the one nearest the top of the viewport when\n * anchoring to 'end', or nearest the bottom when anchoring to 'start'. Callers can\n * exclude certain items (like loaders) with `isAnchorable`.\n */\nexport function captureScrollAnchor(\n edge: ScrollAnchorEdge,\n axis: ScrollAnchorAxis,\n visibleRect: Rect,\n visibleLayoutInfos: Iterable<[Key, LayoutInfo]>,\n isAnchorable: (layoutInfo: LayoutInfo) => boolean = () => true\n): ScrollAnchor | null {\n let dimension = dimensionForAxis(axis);\n let best: ScrollAnchor | null = null;\n for (let [key, layoutInfo] of visibleLayoutInfos) {\n if (!layoutInfo || !isAnchorable(layoutInfo)) {\n continue;\n }\n let overlap = layoutInfo.rect.intersection(visibleRect)[dimension];\n if (layoutInfo.rect.area > 0 && overlap >= MIN_ANCHOR_OVERLAP) {\n let corner = layoutInfo.rect.getCornerInRect(visibleRect) ?? 'topLeft';\n let offset = layoutInfo.rect[corner][axis] - visibleRect[axis];\n let isBetter = !best || (edge === 'end' ? offset < best.offset : offset > best.offset);\n if (isBetter) {\n best = {key, corner, offset};\n }\n }\n }\n return best;\n}\n\n/** Returns the viewport coordinate (along `axis`) that pins the viewport to `edge` of the content. */\nexport function getEdgeSnapTarget(\n edge: ScrollAnchorEdge,\n axis: ScrollAnchorAxis,\n contentSize: Size,\n previousVisibleRect: Rect\n): number {\n if (edge === 'start') {\n return 0;\n }\n let dimension = dimensionForAxis(axis);\n return Math.max(0, contentSize[dimension] - previousVisibleRect[dimension]);\n}\n\n/**\n * Whether the viewport is currently within `threshold` px of the anchored edge — used by\n * Virtualizer to compute wasNearAnchorEdge generically, without any layout-specific state.\n */\nexport function isNearEdge(\n visibleRect: Rect,\n contentSize: Size,\n edge: ScrollAnchorEdge,\n axis: ScrollAnchorAxis,\n threshold: number\n): boolean {\n if (edge === 'start') {\n return visibleRect[axis] <= threshold;\n }\n let dimension = dimensionForAxis(axis);\n let distanceFromEnd = contentSize[dimension] - (visibleRect[axis] + visibleRect[dimension]);\n return distanceFromEnd <= threshold;\n}\n\n/**\n * Works out the new scroll position after content changes. Tries to keep the anchor\n * item where it was. If that doesn't apply, sticks the view to the edge instead, but\n * only if the user was already near the edge and didn't just scroll away on their own.\n */\nexport function resolveScrollAdjustment(\n edge: ScrollAnchorEdge,\n axis: ScrollAnchorAxis,\n anchor: ScrollAnchor | null,\n wasNearAnchorEdge: boolean,\n isScrolling: boolean,\n itemSizeChanged: boolean,\n contentSizeDelta: number,\n getLayoutInfo: (key: Key) => LayoutInfo | null,\n previousVisibleRect: Rect,\n contentSize: Size\n): Rect | null {\n let withTarget = (target: number): Rect =>\n axis === 'x'\n ? new Rect(\n target,\n previousVisibleRect.y,\n previousVisibleRect.width,\n previousVisibleRect.height\n )\n : new Rect(\n previousVisibleRect.x,\n target,\n previousVisibleRect.width,\n previousVisibleRect.height\n );\n\n if (anchor) {\n let target = computeScrollAnchorTarget(\n anchor,\n axis,\n getLayoutInfo,\n previousVisibleRect,\n contentSize\n );\n if (target != null) {\n return withTarget(target);\n }\n }\n\n if (wasNearAnchorEdge && !isScrolling && (!itemSizeChanged || contentSizeDelta > 0)) {\n let target = withTarget(getEdgeSnapTarget(edge, axis, contentSize, previousVisibleRect));\n return target.equals(previousVisibleRect) ? null : target;\n }\n\n return null;\n}\n\nexport interface ResolveAfterLayoutOptions {\n anchorInfo: ScrollAnchorInfo | null;\n /** The anchor captured by `captureBeforeLayout` before this pass's `layout.update()` ran. */\n anchor: ScrollAnchor | null;\n /** The full post-layout visible layout infos, i.e. `virtualizer.getVisibleLayoutInfos()`. */\n postLayoutInfos: Map<Key, LayoutInfo>;\n previousVisibleRect: Rect;\n previousContentSize: Size;\n contentSize: Size;\n itemSizeChanged: boolean;\n isScrolling: boolean;\n getLayoutInfo: (key: Key) => LayoutInfo | null;\n}\n\n/**\n * Tracks the cross-pass state needed to keep the viewport anchored to a layout's edge across\n * relayouts.\n */\nexport class ScrollAnchorTracker {\n private hasSnappedToEdge = false;\n private hadEstimatedVisibleItems = false;\n private wasNearAnchorEdge = false;\n\n /** Resets all tracked state, e.g. when the virtualizer's layout instance changes. */\n reset(): void {\n this.hasSnappedToEdge = false;\n this.hadEstimatedVisibleItems = false;\n this.wasNearAnchorEdge = false;\n }\n\n /**\n * Captures the anchor from pre-layout view positions.\n */\n captureBeforeLayout(\n anchorInfo: ScrollAnchorInfo | null,\n preLayoutInfos: Iterable<[Key, LayoutInfo]>,\n visibleRect: Rect\n ): ScrollAnchor | null {\n if (!anchorInfo) {\n return null;\n }\n return captureScrollAnchor(\n anchorInfo.edge,\n anchorInfo.axis,\n visibleRect,\n preLayoutInfos,\n anchorInfo.isAnchorable\n );\n }\n\n /**\n * Runs the full post-layout decision: updates the tracked state for the next pass, and\n * returns the resolved scroll target, or null if nothing should change.\n */\n resolveAfterLayout(options: ResolveAfterLayoutOptions): Rect | null {\n let {\n anchorInfo,\n anchor,\n postLayoutInfos,\n previousVisibleRect,\n previousContentSize,\n contentSize,\n itemSizeChanged,\n isScrolling,\n getLayoutInfo\n } = options;\n\n if (!anchorInfo) {\n return null;\n }\n\n // Read the previous pass's state into locals before any writes below overwrite it.\n let wasSettlingLastPass = this.hadEstimatedVisibleItems;\n let wasNearAnchorEdgeLastPass = this.wasNearAnchorEdge;\n\n let hasEstimated = false;\n for (let layoutInfo of postLayoutInfos.values()) {\n if (layoutInfo.estimatedSize) {\n hasEstimated = true;\n break;\n }\n }\n this.hadEstimatedVisibleItems = hasEstimated;\n // Don't recheck \"near edge?\" mid-resize because it could look like a scroll that never happened.\n // Reuse the answer from before the resizing started.\n if (!wasSettlingLastPass) {\n this.wasNearAnchorEdge = isNearEdge(\n previousVisibleRect,\n previousContentSize,\n anchorInfo.edge,\n anchorInfo.axis,\n anchorInfo.threshold\n );\n }\n\n if (previousVisibleRect.area === 0) {\n return null;\n }\n\n let dimension = anchorInfo.axis === 'x' ? 'width' : 'height';\n let contentSizeDelta = contentSize[dimension] - previousContentSize[dimension];\n let isFirstAnchoredLayout = !this.hasSnappedToEdge;\n this.hasSnappedToEdge = true;\n\n // Only modify scroll when content actually changed (or this is the first layout, which always snaps)\n if (!(isFirstAnchoredLayout || contentSizeDelta !== 0 || itemSizeChanged)) {\n return null;\n }\n\n let wasNearAnchorEdge =\n isFirstAnchoredLayout ||\n (wasSettlingLastPass && wasNearAnchorEdgeLastPass) ||\n isNearEdge(\n previousVisibleRect,\n previousContentSize,\n anchorInfo.edge,\n anchorInfo.axis,\n anchorInfo.threshold\n );\n // A first-ever layout always snaps to the edge, even if the raw distance check says\n // otherwise. Save that real decision here so later passes in this cascade reuse it.\n if (!wasSettlingLastPass) {\n this.wasNearAnchorEdge = wasNearAnchorEdge;\n }\n // Skip restoring to the captured anchor while still resizing because items above it are also still growing,\n // and following it would fall short of the edge.\n let effectiveAnchor =\n isFirstAnchoredLayout || (wasSettlingLastPass && wasNearAnchorEdgeLastPass) ? null : anchor;\n return resolveScrollAdjustment(\n anchorInfo.edge,\n anchorInfo.axis,\n effectiveAnchor,\n wasNearAnchorEdge,\n isScrolling,\n itemSizeChanged,\n contentSizeDelta,\n getLayoutInfo,\n previousVisibleRect,\n contentSize\n );\n }\n}\n"],"names":[],"version":3,"file":"ScrollAnchor.mjs.map"}
@@ -2,6 +2,7 @@ var $c7afcf057887556c$exports = require("./ReusableView.cjs");
2
2
  var $0ef96a3bce5c0b0e$exports = require("./utils.cjs");
3
3
  var $2e81872088c084c8$exports = require("./OverscanManager.cjs");
4
4
  var $2bdd626d0eea82c5$exports = require("./Rect.cjs");
5
+ var $e06c1e621ca9099b$exports = require("./ScrollAnchor.cjs");
5
6
  var $f9c934dfa703bb0a$exports = require("./Size.cjs");
6
7
 
7
8
 
@@ -25,6 +26,7 @@ $parcel$export(module.exports, "Virtualizer", function () { return $c3448b396b9a
25
26
 
26
27
 
27
28
 
29
+
28
30
  class $c3448b396b9ae7a2$export$89be5a243e59c4b2 {
29
31
  constructor(options){
30
32
  this.delegate = options.delegate;
@@ -40,6 +42,7 @@ class $c3448b396b9ae7a2$export$89be5a243e59c4b2 {
40
42
  this._isScrolling = false;
41
43
  this._invalidationContext = {};
42
44
  this._overscanManager = new (0, $2e81872088c084c8$exports.OverscanManager)();
45
+ this._scrollAnchor = new (0, $e06c1e621ca9099b$exports.ScrollAnchorTracker)();
43
46
  }
44
47
  /** Returns whether the given key, or an ancestor, is persisted. */ isPersistedKey(key) {
45
48
  // Quick check if the key is directly in the set of persisted keys.
@@ -90,9 +93,46 @@ class $c3448b396b9ae7a2$export$89be5a243e59c4b2 {
90
93
  return null;
91
94
  }
92
95
  relayout(context = {}) {
96
+ // @ts-ignore
97
+ let anchorInfo = this.layout.UNSTABLE_getScrollAnchorInfo?.(context.layoutOptions) ?? null;
98
+ // Capture scroll anchor from current (pre-layout) view positions.
99
+ // On first render _visibleViews is empty so no anchor will be found.
100
+ let anchor = null;
101
+ if (anchorInfo) {
102
+ let preLayoutInfos = [];
103
+ for (let [key, view] of this._visibleViews){
104
+ let layoutInfo = this.layout.getLayoutInfo(key) ?? view.layoutInfo;
105
+ if (layoutInfo) preLayoutInfos.push([
106
+ key,
107
+ layoutInfo
108
+ ]);
109
+ }
110
+ anchor = this._scrollAnchor.captureBeforeLayout(anchorInfo, preLayoutInfos, this.visibleRect);
111
+ }
112
+ let previousContentSize = this.contentSize;
113
+ let previousVisibleRect = this.visibleRect;
93
114
  // Update the layout
94
115
  this.layout.update(context);
95
- this.contentSize = this.layout.getContentSize();
116
+ let rawContentSize = this.layout.getContentSize();
117
+ this.contentSize = new (0, $f9c934dfa703bb0a$exports.Size)(rawContentSize.width, rawContentSize.height);
118
+ let target = this._scrollAnchor.resolveAfterLayout({
119
+ anchorInfo: anchorInfo,
120
+ anchor: anchor,
121
+ postLayoutInfos: anchorInfo ? this.getVisibleLayoutInfos() : new Map(),
122
+ previousVisibleRect: previousVisibleRect,
123
+ previousContentSize: previousContentSize,
124
+ contentSize: this.contentSize,
125
+ itemSizeChanged: context.itemSizeChanged ?? false,
126
+ isScrolling: this._isScrolling,
127
+ getLayoutInfo: (key)=>this.layout.getLayoutInfo(key)
128
+ });
129
+ if (target) {
130
+ // Queues a new render cycle. Return early to skip updateSubviews — running it now
131
+ // would position views against the old visibleRect, causing a flash before the
132
+ // incoming relayout corrects them.
133
+ this.delegate.setVisibleRect(target);
134
+ return;
135
+ }
96
136
  // Constrain scroll position.
97
137
  // If the content changed, scroll to the top.
98
138
  let visibleRect = this.visibleRect;
@@ -169,6 +209,8 @@ class $c3448b396b9ae7a2$export$89be5a243e59c4b2 {
169
209
  let needsLayout = false;
170
210
  let offsetChanged = false;
171
211
  let sizeChanged = false;
212
+ let widthChanged = false;
213
+ let heightChanged = false;
172
214
  let itemSizeChanged = false;
173
215
  let layoutOptionsChanged = false;
174
216
  let needsUpdate = false;
@@ -180,6 +222,7 @@ class $c3448b396b9ae7a2$export$89be5a243e59c4b2 {
180
222
  if (this.layout) this.layout.virtualizer = null;
181
223
  opts.layout.virtualizer = this;
182
224
  mutableThis.layout = opts.layout;
225
+ this._scrollAnchor.reset();
183
226
  needsLayout = true;
184
227
  }
185
228
  if (opts.persistedKeys && !(0, $0ef96a3bce5c0b0e$exports.isSetEqual)(opts.persistedKeys, this.persistedKeys)) {
@@ -196,6 +239,8 @@ class $c3448b396b9ae7a2$export$89be5a243e59c4b2 {
196
239
  if (shouldInvalidate) {
197
240
  offsetChanged = !opts.visibleRect.pointEquals(this.visibleRect);
198
241
  sizeChanged = !this.size.equals(opts.size);
242
+ widthChanged = this.size.width !== opts.size.width;
243
+ heightChanged = this.size.height !== opts.size.height;
199
244
  needsLayout = true;
200
245
  } else needsUpdate = true;
201
246
  mutableThis.visibleRect = opts.visibleRect;
@@ -204,6 +249,8 @@ class $c3448b396b9ae7a2$export$89be5a243e59c4b2 {
204
249
  if (opts.invalidationContext !== this._invalidationContext) {
205
250
  if (opts.invalidationContext) {
206
251
  sizeChanged ||= opts.invalidationContext.sizeChanged || false;
252
+ widthChanged ||= opts.invalidationContext.widthChanged || false;
253
+ heightChanged ||= opts.invalidationContext.heightChanged || false;
207
254
  offsetChanged ||= opts.invalidationContext.offsetChanged || false;
208
255
  itemSizeChanged ||= opts.invalidationContext.itemSizeChanged || false;
209
256
  layoutOptionsChanged ||= opts.invalidationContext.layoutOptions != null && this._invalidationContext.layoutOptions != null && opts.invalidationContext.layoutOptions !== this._invalidationContext.layoutOptions && this.layout.shouldInvalidateLayoutOptions(opts.invalidationContext.layoutOptions, this._invalidationContext.layoutOptions);
@@ -219,6 +266,8 @@ class $c3448b396b9ae7a2$export$89be5a243e59c4b2 {
219
266
  if (needsLayout) this.relayout({
220
267
  offsetChanged: offsetChanged,
221
268
  sizeChanged: sizeChanged,
269
+ widthChanged: widthChanged,
270
+ heightChanged: heightChanged,
222
271
  itemSizeChanged: itemSizeChanged,
223
272
  layoutOptionsChanged: layoutOptionsChanged,
224
273
  layoutOptions: this._invalidationContext.layoutOptions
@@ -1 +1 @@
1
- {"mappings":"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","sources":["packages/react-stately/src/virtualizer/Virtualizer.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {ChildView, ReusableView, RootView} from './ReusableView';\nimport {Collection, Key} from '@react-types/shared';\nimport {InvalidationContext, Mutable, VirtualizerDelegate, VirtualizerRenderOptions} from './types';\nimport {isSetEqual} from './utils';\nimport {Layout} from './Layout';\nimport {LayoutInfo} from './LayoutInfo';\nimport {OverscanManager} from './OverscanManager';\nimport {Point} from './Point';\nimport {Rect} from './Rect';\nimport {Size} from './Size';\n\ninterface VirtualizerOptions<T extends object, V> {\n delegate: VirtualizerDelegate<T, V>;\n collection: Collection<T>;\n layout: Layout<T>;\n}\n\n/**\n * The Virtualizer class renders a scrollable collection of data using customizable layouts.\n * It supports very large collections by only rendering visible views to the DOM, reusing\n * them as you scroll. Virtualizer can present any type of view, including non-item views\n * such as section headers and footers.\n *\n * Virtualizer uses `Layout` objects to compute what views should be visible, and how\n * to position and style them. This means that virtualizer can have its items arranged in\n * a stack, a grid, a circle, or any other layout you can think of. The layout can be changed\n * dynamically at runtime as well.\n *\n * Layouts produce information on what views should appear in the virtualizer, but do not create the\n * views themselves directly. It is the responsibility of the `VirtualizerDelegate` object to render\n * elements for each layout info. The virtualizer manages a set of `ReusableView` objects, which are\n * reused as the user scrolls by swapping their content with cached elements returned by the\n * delegate.\n */\nexport class Virtualizer<T extends object, V> {\n /**\n * The virtualizer delegate. The delegate is used by the virtualizer\n * to create and configure views.\n */\n delegate: VirtualizerDelegate<T, V>;\n\n /** The current content of the virtualizer. */\n readonly collection: Collection<T>;\n /** The layout object that determines the visible views. */\n readonly layout: Layout<T>;\n /** The size of the scrollable content. */\n readonly contentSize: Size;\n /** The currently visible rectangle. */\n readonly visibleRect: Rect;\n /** The size of the virtualizer scroll view. */\n readonly size: Size;\n /** The set of persisted keys that are always present in the DOM, even if not currently in view. */\n readonly persistedKeys: Set<Key>;\n\n private _visibleViews: Map<Key, ChildView<T, V>>;\n private _renderedContent: WeakMap<T, V>;\n private _rootView: RootView<T, V>;\n private _isScrolling: boolean;\n private _invalidationContext: InvalidationContext;\n private _overscanManager: OverscanManager;\n\n constructor(options: VirtualizerOptions<T, V>) {\n this.delegate = options.delegate;\n this.collection = options.collection;\n this.layout = options.layout;\n this.contentSize = new Size();\n this.visibleRect = new Rect();\n this.size = new Size();\n this.persistedKeys = new Set();\n this._visibleViews = new Map();\n this._renderedContent = new WeakMap();\n this._rootView = new RootView(this);\n this._isScrolling = false;\n this._invalidationContext = {};\n this._overscanManager = new OverscanManager();\n }\n\n /** Returns whether the given key, or an ancestor, is persisted. */\n isPersistedKey(key: Key): boolean {\n // Quick check if the key is directly in the set of persisted keys.\n if (this.persistedKeys.has(key)) {\n return true;\n }\n\n // If not, check if the key is an ancestor of any of the persisted keys.\n for (let k of this.persistedKeys) {\n while (k != null) {\n let layoutInfo = this.layout.getLayoutInfo(k);\n if (!layoutInfo || layoutInfo.parentKey == null) {\n break;\n }\n\n k = layoutInfo.parentKey;\n\n if (k === key) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n private getParentView(layoutInfo: LayoutInfo): ReusableView<T, V> | undefined {\n return layoutInfo.parentKey != null\n ? this._visibleViews.get(layoutInfo.parentKey)\n : this._rootView;\n }\n\n private getReusableView(layoutInfo: LayoutInfo): ChildView<T, V> {\n let parentView = this.getParentView(layoutInfo)!;\n let view = parentView.getReusableView(layoutInfo.type);\n view.layoutInfo = layoutInfo;\n this._renderView(view);\n return view;\n }\n\n private _renderView(reusableView: ReusableView<T, V>) {\n if (reusableView.layoutInfo) {\n let {type, key, content} = reusableView.layoutInfo;\n reusableView.content = content || this.collection.getItem(key);\n reusableView.rendered = this._renderContent(type, reusableView.content);\n }\n }\n\n private _renderContent(type: string, content: T | null) {\n let cached = content != null ? this._renderedContent.get(content) : null;\n if (cached != null) {\n return cached;\n }\n\n let rendered = this.delegate.renderView(type, content);\n if (content) {\n this._renderedContent.set(content, rendered);\n }\n return rendered;\n }\n\n /**\n * Returns the key for the item view currently at the given point.\n */\n keyAtPoint(point: Point): Key | null {\n let rect = new Rect(point.x, point.y, 1, 1);\n let layoutInfos = rect.area === 0 ? [] : this.layout.getVisibleLayoutInfos(rect);\n\n // Layout may return multiple layout infos in the case of\n // persisted keys, so find the first one that actually intersects.\n for (let layoutInfo of layoutInfos) {\n if (layoutInfo.rect.intersects(rect)) {\n return layoutInfo.key;\n }\n }\n\n return null;\n }\n\n private relayout(context: InvalidationContext = {}) {\n // Update the layout\n this.layout.update(context);\n (this as Mutable<this>).contentSize = this.layout.getContentSize();\n\n // Constrain scroll position.\n // If the content changed, scroll to the top.\n let visibleRect = this.visibleRect;\n let contentOffsetX = context.contentChanged ? 0 : visibleRect.x;\n let contentOffsetY = context.contentChanged ? 0 : visibleRect.y;\n contentOffsetX = Math.max(\n 0,\n Math.min(this.contentSize.width - visibleRect.width, contentOffsetX)\n );\n contentOffsetY = Math.max(\n 0,\n Math.min(this.contentSize.height - visibleRect.height, contentOffsetY)\n );\n\n if (contentOffsetX !== visibleRect.x || contentOffsetY !== visibleRect.y) {\n // If the offset changed, trigger a new re-render.\n let rect = new Rect(contentOffsetX, contentOffsetY, visibleRect.width, visibleRect.height);\n this.delegate.setVisibleRect(rect);\n } else {\n this.updateSubviews();\n }\n }\n\n getVisibleLayoutInfos(): Map<Key, LayoutInfo> {\n let isTestEnv = process.env.NODE_ENV === 'test' && !process.env.VIRT_ON;\n let isClientWidthMocked =\n isTestEnv &&\n typeof HTMLElement !== 'undefined' &&\n Object.getOwnPropertyNames(HTMLElement.prototype).includes('clientWidth');\n let isClientHeightMocked =\n isTestEnv &&\n typeof HTMLElement !== 'undefined' &&\n Object.getOwnPropertyNames(HTMLElement.prototype).includes('clientHeight');\n\n let rect: Rect;\n if (isTestEnv && !(isClientWidthMocked && isClientHeightMocked)) {\n rect = new Rect(0, 0, this.contentSize.width, this.contentSize.height);\n } else {\n rect = this._overscanManager.getOverscannedRect();\n }\n let layoutInfos = this.layout.getVisibleLayoutInfos(rect);\n let map = new Map();\n for (let layoutInfo of layoutInfos) {\n map.set(layoutInfo.key, layoutInfo);\n }\n\n return map;\n }\n\n private updateSubviews() {\n let visibleLayoutInfos = this.getVisibleLayoutInfos();\n\n let removed = new Set<ChildView<T, V>>();\n for (let [key, view] of this._visibleViews) {\n let layoutInfo = visibleLayoutInfos.get(key);\n // If a view's parent changed, treat it as a delete and re-create in the new parent.\n if (!layoutInfo || view.parent !== this.getParentView(layoutInfo)) {\n this._visibleViews.delete(key);\n view.parent.reuseChild(view);\n removed.add(view); // Defer removing in case we reuse this view.\n }\n }\n\n for (let [key, layoutInfo] of visibleLayoutInfos) {\n let view = this._visibleViews.get(key);\n if (!view) {\n view = this.getReusableView(layoutInfo);\n view.parent.children.add(view);\n this._visibleViews.set(key, view);\n removed.delete(view);\n } else {\n view.layoutInfo = layoutInfo;\n\n let item = this.collection.getItem(layoutInfo.key);\n if (view.content !== item) {\n if (view.content != null) {\n this._renderedContent.delete(view.content);\n }\n this._renderView(view);\n }\n }\n }\n\n // The remaining views in `removed` were not reused to render new items.\n // They should be removed from the DOM. We also clear the reusable view queue\n // here since there's no point holding onto views that have been removed.\n // Doing so hurts performance in the future when reusing elements due to FIFO order.\n for (let view of removed) {\n view.parent.children.delete(view);\n view.parent.reusableViews.clear();\n }\n\n // Reordering DOM nodes is costly, so we defer this until scrolling stops.\n // DOM order does not affect visual order (due to absolute positioning),\n // but does matter for assistive technology users.\n if (!this._isScrolling) {\n // Layout infos must be in topological order (parents before children).\n for (let key of visibleLayoutInfos.keys()) {\n let view = this._visibleViews.get(key)!;\n view.parent.children.delete(view);\n view.parent.children.add(view);\n }\n }\n }\n\n /** Performs layout and updates visible views as needed. */\n render(opts: VirtualizerRenderOptions<T>): ReusableView<T, V>[] {\n let mutableThis: Mutable<this> = this;\n let needsLayout = false;\n let offsetChanged = false;\n let sizeChanged = false;\n let itemSizeChanged = false;\n let layoutOptionsChanged = false;\n let needsUpdate = false;\n\n if (opts.collection !== this.collection) {\n mutableThis.collection = opts.collection;\n needsLayout = true;\n }\n\n if (opts.layout !== this.layout || this.layout.virtualizer !== this) {\n if (this.layout) {\n this.layout.virtualizer = null;\n }\n\n opts.layout.virtualizer = this;\n mutableThis.layout = opts.layout;\n needsLayout = true;\n }\n\n if (opts.persistedKeys && !isSetEqual(opts.persistedKeys, this.persistedKeys)) {\n mutableThis.persistedKeys = opts.persistedKeys;\n needsUpdate = true;\n }\n\n if (!this.visibleRect.equals(opts.visibleRect) || !this.size.equals(opts.size)) {\n this._overscanManager.setVisibleRect(opts.visibleRect);\n\n // Create a rectangle using the scroll position and layout size of the scroll view. This is not the same\n // as the visibleRect, whose width and height may change during window scrolling.\n let oldRect = new Rect(\n this.visibleRect.x,\n this.visibleRect.y,\n this.size.width,\n this.size.height\n );\n let newRect = new Rect(\n opts.visibleRect.x,\n opts.visibleRect.y,\n opts.size.width,\n opts.size.height\n );\n let shouldInvalidate = this.layout.shouldInvalidate(newRect, oldRect);\n\n if (shouldInvalidate) {\n offsetChanged = !opts.visibleRect.pointEquals(this.visibleRect);\n sizeChanged = !this.size.equals(opts.size);\n needsLayout = true;\n } else {\n needsUpdate = true;\n }\n\n mutableThis.visibleRect = opts.visibleRect;\n mutableThis.size = opts.size;\n }\n\n if (opts.invalidationContext !== this._invalidationContext) {\n if (opts.invalidationContext) {\n sizeChanged ||= opts.invalidationContext.sizeChanged || false;\n offsetChanged ||= opts.invalidationContext.offsetChanged || false;\n itemSizeChanged ||= opts.invalidationContext.itemSizeChanged || false;\n layoutOptionsChanged ||=\n opts.invalidationContext.layoutOptions != null &&\n this._invalidationContext.layoutOptions != null &&\n opts.invalidationContext.layoutOptions !== this._invalidationContext.layoutOptions &&\n this.layout.shouldInvalidateLayoutOptions(\n opts.invalidationContext.layoutOptions,\n this._invalidationContext.layoutOptions\n );\n needsLayout ||= itemSizeChanged || sizeChanged || offsetChanged || layoutOptionsChanged;\n }\n this._invalidationContext = opts.invalidationContext;\n }\n\n if (opts.isScrolling !== this._isScrolling) {\n this._isScrolling = opts.isScrolling;\n if (!opts.isScrolling) {\n // Update to fix the DOM order after scrolling.\n needsUpdate = true;\n }\n }\n\n if (needsLayout) {\n this.relayout({\n offsetChanged,\n sizeChanged,\n itemSizeChanged,\n layoutOptionsChanged,\n layoutOptions: this._invalidationContext.layoutOptions\n });\n } else if (needsUpdate) {\n this.updateSubviews();\n }\n\n return Array.from(this._rootView.children);\n }\n\n getVisibleView(key: Key): ReusableView<T, V> | undefined {\n return this._visibleViews.get(key);\n }\n\n invalidate(context: InvalidationContext): void {\n this.delegate.invalidate(context);\n }\n\n updateItemSize(key: Key, size: Size): void {\n if (!this.layout.updateItemSize) {\n return;\n }\n\n let changed = this.layout.updateItemSize(key, size);\n if (changed) {\n this.invalidate({\n itemSizeChanged: true\n });\n }\n }\n}\n"],"names":[],"version":3,"file":"Virtualizer.cjs.map"}
1
+ {"mappings":"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","sources":["packages/react-stately/src/virtualizer/Virtualizer.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {ChildView, ReusableView, RootView} from './ReusableView';\nimport {Collection, Key} from '@react-types/shared';\nimport {InvalidationContext, Mutable, VirtualizerDelegate, VirtualizerRenderOptions} from './types';\nimport {isSetEqual} from './utils';\nimport {Layout} from './Layout';\nimport {LayoutInfo} from './LayoutInfo';\nimport {OverscanManager} from './OverscanManager';\nimport {Point} from './Point';\nimport {Rect} from './Rect';\nimport {ScrollAnchor, ScrollAnchorTracker} from './ScrollAnchor';\nimport {Size} from './Size';\n\ninterface VirtualizerOptions<T extends object, V> {\n delegate: VirtualizerDelegate<T, V>;\n collection: Collection<T>;\n layout: Layout<T>;\n}\n\n/**\n * The Virtualizer class renders a scrollable collection of data using customizable layouts.\n * It supports very large collections by only rendering visible views to the DOM, reusing\n * them as you scroll. Virtualizer can present any type of view, including non-item views\n * such as section headers and footers.\n *\n * Virtualizer uses `Layout` objects to compute what views should be visible, and how\n * to position and style them. This means that virtualizer can have its items arranged in\n * a stack, a grid, a circle, or any other layout you can think of. The layout can be changed\n * dynamically at runtime as well.\n *\n * Layouts produce information on what views should appear in the virtualizer, but do not create the\n * views themselves directly. It is the responsibility of the `VirtualizerDelegate` object to render\n * elements for each layout info. The virtualizer manages a set of `ReusableView` objects, which are\n * reused as the user scrolls by swapping their content with cached elements returned by the\n * delegate.\n */\nexport class Virtualizer<T extends object, V> {\n /**\n * The virtualizer delegate. The delegate is used by the virtualizer\n * to create and configure views.\n */\n delegate: VirtualizerDelegate<T, V>;\n\n /** The current content of the virtualizer. */\n readonly collection: Collection<T>;\n /** The layout object that determines the visible views. */\n readonly layout: Layout<T>;\n /** The size of the scrollable content. */\n readonly contentSize: Size;\n /** The currently visible rectangle. */\n readonly visibleRect: Rect;\n /** The size of the virtualizer scroll view. */\n readonly size: Size;\n /** The set of persisted keys that are always present in the DOM, even if not currently in view. */\n readonly persistedKeys: Set<Key>;\n\n private _visibleViews: Map<Key, ChildView<T, V>>;\n private _renderedContent: WeakMap<T, V>;\n private _rootView: RootView<T, V>;\n private _isScrolling: boolean;\n private _invalidationContext: InvalidationContext;\n private _overscanManager: OverscanManager;\n private _scrollAnchor: ScrollAnchorTracker;\n\n constructor(options: VirtualizerOptions<T, V>) {\n this.delegate = options.delegate;\n this.collection = options.collection;\n this.layout = options.layout;\n this.contentSize = new Size();\n this.visibleRect = new Rect();\n this.size = new Size();\n this.persistedKeys = new Set();\n this._visibleViews = new Map();\n this._renderedContent = new WeakMap();\n this._rootView = new RootView(this);\n this._isScrolling = false;\n this._invalidationContext = {};\n this._overscanManager = new OverscanManager();\n this._scrollAnchor = new ScrollAnchorTracker();\n }\n\n /** Returns whether the given key, or an ancestor, is persisted. */\n isPersistedKey(key: Key): boolean {\n // Quick check if the key is directly in the set of persisted keys.\n if (this.persistedKeys.has(key)) {\n return true;\n }\n\n // If not, check if the key is an ancestor of any of the persisted keys.\n for (let k of this.persistedKeys) {\n while (k != null) {\n let layoutInfo = this.layout.getLayoutInfo(k);\n if (!layoutInfo || layoutInfo.parentKey == null) {\n break;\n }\n\n k = layoutInfo.parentKey;\n\n if (k === key) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n private getParentView(layoutInfo: LayoutInfo): ReusableView<T, V> | undefined {\n return layoutInfo.parentKey != null\n ? this._visibleViews.get(layoutInfo.parentKey)\n : this._rootView;\n }\n\n private getReusableView(layoutInfo: LayoutInfo): ChildView<T, V> {\n let parentView = this.getParentView(layoutInfo)!;\n let view = parentView.getReusableView(layoutInfo.type);\n view.layoutInfo = layoutInfo;\n this._renderView(view);\n return view;\n }\n\n private _renderView(reusableView: ReusableView<T, V>) {\n if (reusableView.layoutInfo) {\n let {type, key, content} = reusableView.layoutInfo;\n reusableView.content = content || this.collection.getItem(key);\n reusableView.rendered = this._renderContent(type, reusableView.content);\n }\n }\n\n private _renderContent(type: string, content: T | null) {\n let cached = content != null ? this._renderedContent.get(content) : null;\n if (cached != null) {\n return cached;\n }\n\n let rendered = this.delegate.renderView(type, content);\n if (content) {\n this._renderedContent.set(content, rendered);\n }\n return rendered;\n }\n\n /**\n * Returns the key for the item view currently at the given point.\n */\n keyAtPoint(point: Point): Key | null {\n let rect = new Rect(point.x, point.y, 1, 1);\n let layoutInfos = rect.area === 0 ? [] : this.layout.getVisibleLayoutInfos(rect);\n\n // Layout may return multiple layout infos in the case of\n // persisted keys, so find the first one that actually intersects.\n for (let layoutInfo of layoutInfos) {\n if (layoutInfo.rect.intersects(rect)) {\n return layoutInfo.key;\n }\n }\n\n return null;\n }\n\n private relayout(context: InvalidationContext = {}) {\n // @ts-ignore\n let anchorInfo = this.layout.UNSTABLE_getScrollAnchorInfo?.(context.layoutOptions) ?? null;\n\n // Capture scroll anchor from current (pre-layout) view positions.\n // On first render _visibleViews is empty so no anchor will be found.\n let anchor: ScrollAnchor | null = null;\n if (anchorInfo) {\n let preLayoutInfos: [Key, LayoutInfo][] = [];\n for (let [key, view] of this._visibleViews) {\n let layoutInfo = this.layout.getLayoutInfo(key) ?? view.layoutInfo;\n if (layoutInfo) {\n preLayoutInfos.push([key, layoutInfo]);\n }\n }\n anchor = this._scrollAnchor.captureBeforeLayout(anchorInfo, preLayoutInfos, this.visibleRect);\n }\n\n let previousContentSize = this.contentSize;\n let previousVisibleRect = this.visibleRect;\n\n // Update the layout\n this.layout.update(context);\n\n let rawContentSize = this.layout.getContentSize();\n (this as Mutable<this>).contentSize = new Size(rawContentSize.width, rawContentSize.height);\n\n let target = this._scrollAnchor.resolveAfterLayout({\n anchorInfo,\n anchor,\n postLayoutInfos: anchorInfo ? this.getVisibleLayoutInfos() : new Map(),\n previousVisibleRect,\n previousContentSize,\n contentSize: this.contentSize,\n itemSizeChanged: context.itemSizeChanged ?? false,\n isScrolling: this._isScrolling,\n getLayoutInfo: (key: Key) => this.layout.getLayoutInfo(key)\n });\n\n if (target) {\n // Queues a new render cycle. Return early to skip updateSubviews — running it now\n // would position views against the old visibleRect, causing a flash before the\n // incoming relayout corrects them.\n this.delegate.setVisibleRect(target);\n return;\n }\n\n // Constrain scroll position.\n // If the content changed, scroll to the top.\n let visibleRect = this.visibleRect;\n let contentOffsetX = context.contentChanged ? 0 : visibleRect.x;\n let contentOffsetY = context.contentChanged ? 0 : visibleRect.y;\n contentOffsetX = Math.max(\n 0,\n Math.min(this.contentSize.width - visibleRect.width, contentOffsetX)\n );\n contentOffsetY = Math.max(\n 0,\n Math.min(this.contentSize.height - visibleRect.height, contentOffsetY)\n );\n\n if (contentOffsetX !== visibleRect.x || contentOffsetY !== visibleRect.y) {\n // If the offset changed, trigger a new re-render.\n let rect = new Rect(contentOffsetX, contentOffsetY, visibleRect.width, visibleRect.height);\n this.delegate.setVisibleRect(rect);\n } else {\n this.updateSubviews();\n }\n }\n\n getVisibleLayoutInfos(): Map<Key, LayoutInfo> {\n let isTestEnv = process.env.NODE_ENV === 'test' && !process.env.VIRT_ON;\n let isClientWidthMocked =\n isTestEnv &&\n typeof HTMLElement !== 'undefined' &&\n Object.getOwnPropertyNames(HTMLElement.prototype).includes('clientWidth');\n let isClientHeightMocked =\n isTestEnv &&\n typeof HTMLElement !== 'undefined' &&\n Object.getOwnPropertyNames(HTMLElement.prototype).includes('clientHeight');\n\n let rect: Rect;\n if (isTestEnv && !(isClientWidthMocked && isClientHeightMocked)) {\n rect = new Rect(0, 0, this.contentSize.width, this.contentSize.height);\n } else {\n rect = this._overscanManager.getOverscannedRect();\n }\n let layoutInfos = this.layout.getVisibleLayoutInfos(rect);\n let map = new Map();\n for (let layoutInfo of layoutInfos) {\n map.set(layoutInfo.key, layoutInfo);\n }\n\n return map;\n }\n\n private updateSubviews() {\n let visibleLayoutInfos = this.getVisibleLayoutInfos();\n\n let removed = new Set<ChildView<T, V>>();\n for (let [key, view] of this._visibleViews) {\n let layoutInfo = visibleLayoutInfos.get(key);\n // If a view's parent changed, treat it as a delete and re-create in the new parent.\n if (!layoutInfo || view.parent !== this.getParentView(layoutInfo)) {\n this._visibleViews.delete(key);\n view.parent.reuseChild(view);\n removed.add(view); // Defer removing in case we reuse this view.\n }\n }\n\n for (let [key, layoutInfo] of visibleLayoutInfos) {\n let view = this._visibleViews.get(key);\n if (!view) {\n view = this.getReusableView(layoutInfo);\n view.parent.children.add(view);\n this._visibleViews.set(key, view);\n removed.delete(view);\n } else {\n view.layoutInfo = layoutInfo;\n\n let item = this.collection.getItem(layoutInfo.key);\n if (view.content !== item) {\n if (view.content != null) {\n this._renderedContent.delete(view.content);\n }\n this._renderView(view);\n }\n }\n }\n\n // The remaining views in `removed` were not reused to render new items.\n // They should be removed from the DOM. We also clear the reusable view queue\n // here since there's no point holding onto views that have been removed.\n // Doing so hurts performance in the future when reusing elements due to FIFO order.\n for (let view of removed) {\n view.parent.children.delete(view);\n view.parent.reusableViews.clear();\n }\n\n // Reordering DOM nodes is costly, so we defer this until scrolling stops.\n // DOM order does not affect visual order (due to absolute positioning),\n // but does matter for assistive technology users.\n if (!this._isScrolling) {\n // Layout infos must be in topological order (parents before children).\n for (let key of visibleLayoutInfos.keys()) {\n let view = this._visibleViews.get(key)!;\n view.parent.children.delete(view);\n view.parent.children.add(view);\n }\n }\n }\n\n /** Performs layout and updates visible views as needed. */\n render(opts: VirtualizerRenderOptions<T>): ReusableView<T, V>[] {\n let mutableThis: Mutable<this> = this;\n let needsLayout = false;\n let offsetChanged = false;\n let sizeChanged = false;\n let widthChanged = false;\n let heightChanged = false;\n let itemSizeChanged = false;\n let layoutOptionsChanged = false;\n let needsUpdate = false;\n\n if (opts.collection !== this.collection) {\n mutableThis.collection = opts.collection;\n needsLayout = true;\n }\n\n if (opts.layout !== this.layout || this.layout.virtualizer !== this) {\n if (this.layout) {\n this.layout.virtualizer = null;\n }\n\n opts.layout.virtualizer = this;\n mutableThis.layout = opts.layout;\n this._scrollAnchor.reset();\n needsLayout = true;\n }\n\n if (opts.persistedKeys && !isSetEqual(opts.persistedKeys, this.persistedKeys)) {\n mutableThis.persistedKeys = opts.persistedKeys;\n needsUpdate = true;\n }\n\n if (!this.visibleRect.equals(opts.visibleRect) || !this.size.equals(opts.size)) {\n this._overscanManager.setVisibleRect(opts.visibleRect);\n\n // Create a rectangle using the scroll position and layout size of the scroll view. This is not the same\n // as the visibleRect, whose width and height may change during window scrolling.\n let oldRect = new Rect(\n this.visibleRect.x,\n this.visibleRect.y,\n this.size.width,\n this.size.height\n );\n let newRect = new Rect(\n opts.visibleRect.x,\n opts.visibleRect.y,\n opts.size.width,\n opts.size.height\n );\n let shouldInvalidate = this.layout.shouldInvalidate(newRect, oldRect);\n\n if (shouldInvalidate) {\n offsetChanged = !opts.visibleRect.pointEquals(this.visibleRect);\n sizeChanged = !this.size.equals(opts.size);\n widthChanged = this.size.width !== opts.size.width;\n heightChanged = this.size.height !== opts.size.height;\n needsLayout = true;\n } else {\n needsUpdate = true;\n }\n\n mutableThis.visibleRect = opts.visibleRect;\n mutableThis.size = opts.size;\n }\n\n if (opts.invalidationContext !== this._invalidationContext) {\n if (opts.invalidationContext) {\n sizeChanged ||= opts.invalidationContext.sizeChanged || false;\n widthChanged ||= opts.invalidationContext.widthChanged || false;\n heightChanged ||= opts.invalidationContext.heightChanged || false;\n offsetChanged ||= opts.invalidationContext.offsetChanged || false;\n itemSizeChanged ||= opts.invalidationContext.itemSizeChanged || false;\n layoutOptionsChanged ||=\n opts.invalidationContext.layoutOptions != null &&\n this._invalidationContext.layoutOptions != null &&\n opts.invalidationContext.layoutOptions !== this._invalidationContext.layoutOptions &&\n this.layout.shouldInvalidateLayoutOptions(\n opts.invalidationContext.layoutOptions,\n this._invalidationContext.layoutOptions\n );\n needsLayout ||= itemSizeChanged || sizeChanged || offsetChanged || layoutOptionsChanged;\n }\n this._invalidationContext = opts.invalidationContext;\n }\n\n if (opts.isScrolling !== this._isScrolling) {\n this._isScrolling = opts.isScrolling;\n if (!opts.isScrolling) {\n // Update to fix the DOM order after scrolling.\n needsUpdate = true;\n }\n }\n\n if (needsLayout) {\n this.relayout({\n offsetChanged,\n sizeChanged,\n widthChanged,\n heightChanged,\n itemSizeChanged,\n layoutOptionsChanged,\n layoutOptions: this._invalidationContext.layoutOptions\n });\n } else if (needsUpdate) {\n this.updateSubviews();\n }\n\n return Array.from(this._rootView.children);\n }\n\n getVisibleView(key: Key): ReusableView<T, V> | undefined {\n return this._visibleViews.get(key);\n }\n\n invalidate(context: InvalidationContext): void {\n this.delegate.invalidate(context);\n }\n\n updateItemSize(key: Key, size: Size): void {\n if (!this.layout.updateItemSize) {\n return;\n }\n\n let changed = this.layout.updateItemSize(key, size);\n if (changed) {\n this.invalidate({\n itemSizeChanged: true\n });\n }\n }\n}\n"],"names":[],"version":3,"file":"Virtualizer.cjs.map"}
@@ -2,6 +2,7 @@ import {RootView as $c7620442287ad52a$export$e21886a4eef6b29a} from "./ReusableV
2
2
  import {isSetEqual as $94e3e3694aff09c6$export$a8d0d0c8d1c5df64} from "./utils.js";
3
3
  import {OverscanManager as $44cbaca532f3db86$export$4455ee6afb38dcbb} from "./OverscanManager.js";
4
4
  import {Rect as $546350db2c358941$export$c79fc6492f3af13d} from "./Rect.js";
5
+ import {ScrollAnchorTracker as $20fe89c1b1ab405a$export$8ce0fdedc36cb6b} from "./ScrollAnchor.js";
5
6
  import {Size as $266cbd2785be3a2c$export$cb6da89c6af1a8ec} from "./Size.js";
6
7
 
7
8
  /*
@@ -19,6 +20,7 @@ import {Size as $266cbd2785be3a2c$export$cb6da89c6af1a8ec} from "./Size.js";
19
20
 
20
21
 
21
22
 
23
+
22
24
  class $55865ccb920012bc$export$89be5a243e59c4b2 {
23
25
  /** Returns whether the given key, or an ancestor, is persisted. */ isPersistedKey(key) {
24
26
  // Quick check if the key is directly in the set of persisted keys.
@@ -69,9 +71,50 @@ class $55865ccb920012bc$export$89be5a243e59c4b2 {
69
71
  return null;
70
72
  }
71
73
  relayout(context = {}) {
74
+ var _this_layout_UNSTABLE_getScrollAnchorInfo, _this_layout;
75
+ var _this_layout_UNSTABLE_getScrollAnchorInfo1;
76
+ // @ts-ignore
77
+ let anchorInfo = (_this_layout_UNSTABLE_getScrollAnchorInfo1 = (_this_layout_UNSTABLE_getScrollAnchorInfo = (_this_layout = this.layout).UNSTABLE_getScrollAnchorInfo) === null || _this_layout_UNSTABLE_getScrollAnchorInfo === void 0 ? void 0 : _this_layout_UNSTABLE_getScrollAnchorInfo.call(_this_layout, context.layoutOptions)) !== null && _this_layout_UNSTABLE_getScrollAnchorInfo1 !== void 0 ? _this_layout_UNSTABLE_getScrollAnchorInfo1 : null;
78
+ // Capture scroll anchor from current (pre-layout) view positions.
79
+ // On first render _visibleViews is empty so no anchor will be found.
80
+ let anchor = null;
81
+ if (anchorInfo) {
82
+ let preLayoutInfos = [];
83
+ for (let [key, view] of this._visibleViews){
84
+ var _this_layout_getLayoutInfo;
85
+ let layoutInfo = (_this_layout_getLayoutInfo = this.layout.getLayoutInfo(key)) !== null && _this_layout_getLayoutInfo !== void 0 ? _this_layout_getLayoutInfo : view.layoutInfo;
86
+ if (layoutInfo) preLayoutInfos.push([
87
+ key,
88
+ layoutInfo
89
+ ]);
90
+ }
91
+ anchor = this._scrollAnchor.captureBeforeLayout(anchorInfo, preLayoutInfos, this.visibleRect);
92
+ }
93
+ let previousContentSize = this.contentSize;
94
+ let previousVisibleRect = this.visibleRect;
72
95
  // Update the layout
73
96
  this.layout.update(context);
74
- this.contentSize = this.layout.getContentSize();
97
+ let rawContentSize = this.layout.getContentSize();
98
+ this.contentSize = new (0, $266cbd2785be3a2c$export$cb6da89c6af1a8ec)(rawContentSize.width, rawContentSize.height);
99
+ var _context_itemSizeChanged;
100
+ let target = this._scrollAnchor.resolveAfterLayout({
101
+ anchorInfo: anchorInfo,
102
+ anchor: anchor,
103
+ postLayoutInfos: anchorInfo ? this.getVisibleLayoutInfos() : new Map(),
104
+ previousVisibleRect: previousVisibleRect,
105
+ previousContentSize: previousContentSize,
106
+ contentSize: this.contentSize,
107
+ itemSizeChanged: (_context_itemSizeChanged = context.itemSizeChanged) !== null && _context_itemSizeChanged !== void 0 ? _context_itemSizeChanged : false,
108
+ isScrolling: this._isScrolling,
109
+ getLayoutInfo: (key)=>this.layout.getLayoutInfo(key)
110
+ });
111
+ if (target) {
112
+ // Queues a new render cycle. Return early to skip updateSubviews — running it now
113
+ // would position views against the old visibleRect, causing a flash before the
114
+ // incoming relayout corrects them.
115
+ this.delegate.setVisibleRect(target);
116
+ return;
117
+ }
75
118
  // Constrain scroll position.
76
119
  // If the content changed, scroll to the top.
77
120
  let visibleRect = this.visibleRect;
@@ -148,6 +191,8 @@ class $55865ccb920012bc$export$89be5a243e59c4b2 {
148
191
  let needsLayout = false;
149
192
  let offsetChanged = false;
150
193
  let sizeChanged = false;
194
+ let widthChanged = false;
195
+ let heightChanged = false;
151
196
  let itemSizeChanged = false;
152
197
  let layoutOptionsChanged = false;
153
198
  let needsUpdate = false;
@@ -159,6 +204,7 @@ class $55865ccb920012bc$export$89be5a243e59c4b2 {
159
204
  if (this.layout) this.layout.virtualizer = null;
160
205
  opts.layout.virtualizer = this;
161
206
  mutableThis.layout = opts.layout;
207
+ this._scrollAnchor.reset();
162
208
  needsLayout = true;
163
209
  }
164
210
  if (opts.persistedKeys && !(0, $94e3e3694aff09c6$export$a8d0d0c8d1c5df64)(opts.persistedKeys, this.persistedKeys)) {
@@ -175,6 +221,8 @@ class $55865ccb920012bc$export$89be5a243e59c4b2 {
175
221
  if (shouldInvalidate) {
176
222
  offsetChanged = !opts.visibleRect.pointEquals(this.visibleRect);
177
223
  sizeChanged = !this.size.equals(opts.size);
224
+ widthChanged = this.size.width !== opts.size.width;
225
+ heightChanged = this.size.height !== opts.size.height;
178
226
  needsLayout = true;
179
227
  } else needsUpdate = true;
180
228
  mutableThis.visibleRect = opts.visibleRect;
@@ -183,6 +231,8 @@ class $55865ccb920012bc$export$89be5a243e59c4b2 {
183
231
  if (opts.invalidationContext !== this._invalidationContext) {
184
232
  if (opts.invalidationContext) {
185
233
  sizeChanged || (sizeChanged = opts.invalidationContext.sizeChanged || false);
234
+ widthChanged || (widthChanged = opts.invalidationContext.widthChanged || false);
235
+ heightChanged || (heightChanged = opts.invalidationContext.heightChanged || false);
186
236
  offsetChanged || (offsetChanged = opts.invalidationContext.offsetChanged || false);
187
237
  itemSizeChanged || (itemSizeChanged = opts.invalidationContext.itemSizeChanged || false);
188
238
  layoutOptionsChanged || (layoutOptionsChanged = opts.invalidationContext.layoutOptions != null && this._invalidationContext.layoutOptions != null && opts.invalidationContext.layoutOptions !== this._invalidationContext.layoutOptions && this.layout.shouldInvalidateLayoutOptions(opts.invalidationContext.layoutOptions, this._invalidationContext.layoutOptions));
@@ -198,6 +248,8 @@ class $55865ccb920012bc$export$89be5a243e59c4b2 {
198
248
  if (needsLayout) this.relayout({
199
249
  offsetChanged: offsetChanged,
200
250
  sizeChanged: sizeChanged,
251
+ widthChanged: widthChanged,
252
+ heightChanged: heightChanged,
201
253
  itemSizeChanged: itemSizeChanged,
202
254
  layoutOptionsChanged: layoutOptionsChanged,
203
255
  layoutOptions: this._invalidationContext.layoutOptions
@@ -232,6 +284,7 @@ class $55865ccb920012bc$export$89be5a243e59c4b2 {
232
284
  this._isScrolling = false;
233
285
  this._invalidationContext = {};
234
286
  this._overscanManager = new (0, $44cbaca532f3db86$export$4455ee6afb38dcbb)();
287
+ this._scrollAnchor = new (0, $20fe89c1b1ab405a$export$8ce0fdedc36cb6b)();
235
288
  }
236
289
  }
237
290