react-stately 3.47.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 (140) 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/color/Color.cjs.map +1 -1
  14. package/dist/private/color/Color.js.map +1 -1
  15. package/dist/private/color/Color.mjs.map +1 -1
  16. package/dist/private/combobox/useComboBoxState.cjs +2 -1
  17. package/dist/private/combobox/useComboBoxState.cjs.map +1 -1
  18. package/dist/private/combobox/useComboBoxState.js +2 -1
  19. package/dist/private/combobox/useComboBoxState.js.map +1 -1
  20. package/dist/private/combobox/useComboBoxState.mjs +2 -1
  21. package/dist/private/combobox/useComboBoxState.mjs.map +1 -1
  22. package/dist/private/data/useTreeData.cjs +1 -0
  23. package/dist/private/data/useTreeData.cjs.map +1 -1
  24. package/dist/private/data/useTreeData.js +1 -0
  25. package/dist/private/data/useTreeData.js.map +1 -1
  26. package/dist/private/data/useTreeData.mjs +1 -0
  27. package/dist/private/data/useTreeData.mjs.map +1 -1
  28. package/dist/private/datepicker/useDateRangePickerState.cjs +1 -0
  29. package/dist/private/datepicker/useDateRangePickerState.cjs.map +1 -1
  30. package/dist/private/datepicker/useDateRangePickerState.js +1 -0
  31. package/dist/private/datepicker/useDateRangePickerState.js.map +1 -1
  32. package/dist/private/datepicker/useDateRangePickerState.mjs +1 -0
  33. package/dist/private/datepicker/useDateRangePickerState.mjs.map +1 -1
  34. package/dist/private/datepicker/utils.cjs.map +1 -1
  35. package/dist/private/datepicker/utils.js.map +1 -1
  36. package/dist/private/datepicker/utils.mjs.map +1 -1
  37. package/dist/private/form/useFormValidationState.cjs +1 -1
  38. package/dist/private/form/useFormValidationState.cjs.map +1 -1
  39. package/dist/private/form/useFormValidationState.js +1 -1
  40. package/dist/private/form/useFormValidationState.js.map +1 -1
  41. package/dist/private/form/useFormValidationState.mjs +1 -1
  42. package/dist/private/form/useFormValidationState.mjs.map +1 -1
  43. package/dist/private/grid/useGridState.cjs +12 -10
  44. package/dist/private/grid/useGridState.cjs.map +1 -1
  45. package/dist/private/grid/useGridState.js +12 -10
  46. package/dist/private/grid/useGridState.js.map +1 -1
  47. package/dist/private/grid/useGridState.mjs +12 -10
  48. package/dist/private/grid/useGridState.mjs.map +1 -1
  49. package/dist/private/layout/ListLayout.cjs +0 -3
  50. package/dist/private/layout/ListLayout.cjs.map +1 -1
  51. package/dist/private/layout/ListLayout.js +0 -3
  52. package/dist/private/layout/ListLayout.js.map +1 -1
  53. package/dist/private/layout/ListLayout.mjs +0 -3
  54. package/dist/private/layout/ListLayout.mjs.map +1 -1
  55. package/dist/private/menu/useMenuTriggerState.cjs.map +1 -1
  56. package/dist/private/menu/useMenuTriggerState.js.map +1 -1
  57. package/dist/private/menu/useMenuTriggerState.mjs.map +1 -1
  58. package/dist/private/menu/useSubmenuTriggerState.cjs +3 -1
  59. package/dist/private/menu/useSubmenuTriggerState.cjs.map +1 -1
  60. package/dist/private/menu/useSubmenuTriggerState.js +3 -1
  61. package/dist/private/menu/useSubmenuTriggerState.js.map +1 -1
  62. package/dist/private/menu/useSubmenuTriggerState.mjs +3 -1
  63. package/dist/private/menu/useSubmenuTriggerState.mjs.map +1 -1
  64. package/dist/private/overlays/useOverlayTriggerState.cjs +4 -1
  65. package/dist/private/overlays/useOverlayTriggerState.cjs.map +1 -1
  66. package/dist/private/overlays/useOverlayTriggerState.js +5 -2
  67. package/dist/private/overlays/useOverlayTriggerState.js.map +1 -1
  68. package/dist/private/overlays/useOverlayTriggerState.mjs +5 -2
  69. package/dist/private/overlays/useOverlayTriggerState.mjs.map +1 -1
  70. package/dist/private/select/useSelectState.cjs +1 -0
  71. package/dist/private/select/useSelectState.cjs.map +1 -1
  72. package/dist/private/select/useSelectState.js +1 -0
  73. package/dist/private/select/useSelectState.js.map +1 -1
  74. package/dist/private/select/useSelectState.mjs +1 -0
  75. package/dist/private/select/useSelectState.mjs.map +1 -1
  76. package/dist/private/slider/useSliderState.cjs.map +1 -1
  77. package/dist/private/slider/useSliderState.js.map +1 -1
  78. package/dist/private/slider/useSliderState.mjs.map +1 -1
  79. package/dist/private/table/TableUtils.cjs +13 -1
  80. package/dist/private/table/TableUtils.cjs.map +1 -1
  81. package/dist/private/table/TableUtils.js +13 -1
  82. package/dist/private/table/TableUtils.js.map +1 -1
  83. package/dist/private/table/TableUtils.mjs +13 -1
  84. package/dist/private/table/TableUtils.mjs.map +1 -1
  85. package/dist/private/table/useTableState.cjs +1 -0
  86. package/dist/private/table/useTableState.cjs.map +1 -1
  87. package/dist/private/table/useTableState.js +1 -0
  88. package/dist/private/table/useTableState.js.map +1 -1
  89. package/dist/private/table/useTableState.mjs +1 -0
  90. package/dist/private/table/useTableState.mjs.map +1 -1
  91. package/dist/private/tokenfield/TokenFieldValue.cjs +287 -0
  92. package/dist/private/tokenfield/TokenFieldValue.cjs.map +1 -0
  93. package/dist/private/tokenfield/TokenFieldValue.js +285 -0
  94. package/dist/private/tokenfield/TokenFieldValue.js.map +1 -0
  95. package/dist/private/tokenfield/TokenFieldValue.mjs +282 -0
  96. package/dist/private/tokenfield/TokenFieldValue.mjs.map +1 -0
  97. package/dist/private/tokenfield/useTokenFieldState.cjs +37 -0
  98. package/dist/private/tokenfield/useTokenFieldState.cjs.map +1 -0
  99. package/dist/private/tokenfield/useTokenFieldState.js +32 -0
  100. package/dist/private/tokenfield/useTokenFieldState.js.map +1 -0
  101. package/dist/private/tokenfield/useTokenFieldState.mjs +32 -0
  102. package/dist/private/tokenfield/useTokenFieldState.mjs.map +1 -0
  103. package/dist/private/tooltip/useTooltipTriggerState.cjs +18 -6
  104. package/dist/private/tooltip/useTooltipTriggerState.cjs.map +1 -1
  105. package/dist/private/tooltip/useTooltipTriggerState.js +19 -7
  106. package/dist/private/tooltip/useTooltipTriggerState.js.map +1 -1
  107. package/dist/private/tooltip/useTooltipTriggerState.mjs +19 -7
  108. package/dist/private/tooltip/useTooltipTriggerState.mjs.map +1 -1
  109. package/dist/private/virtualizer/ScrollAnchor.cjs +136 -0
  110. package/dist/private/virtualizer/ScrollAnchor.cjs.map +1 -0
  111. package/dist/private/virtualizer/ScrollAnchor.js +132 -0
  112. package/dist/private/virtualizer/ScrollAnchor.js.map +1 -0
  113. package/dist/private/virtualizer/ScrollAnchor.mjs +131 -0
  114. package/dist/private/virtualizer/ScrollAnchor.mjs.map +1 -0
  115. package/dist/private/virtualizer/Virtualizer.cjs +50 -1
  116. package/dist/private/virtualizer/Virtualizer.cjs.map +1 -1
  117. package/dist/private/virtualizer/Virtualizer.js +54 -1
  118. package/dist/private/virtualizer/Virtualizer.js.map +1 -1
  119. package/dist/private/virtualizer/Virtualizer.mjs +50 -1
  120. package/dist/private/virtualizer/Virtualizer.mjs.map +1 -1
  121. package/dist/private/virtualizer/useVirtualizerState.cjs +2 -1
  122. package/dist/private/virtualizer/useVirtualizerState.cjs.map +1 -1
  123. package/dist/private/virtualizer/useVirtualizerState.js +2 -1
  124. package/dist/private/virtualizer/useVirtualizerState.js.map +1 -1
  125. package/dist/private/virtualizer/useVirtualizerState.mjs +2 -1
  126. package/dist/private/virtualizer/useVirtualizerState.mjs.map +1 -1
  127. package/dist/types/exports/index.d.ts +4 -0
  128. package/dist/types/exports/useTokenFieldState.d.ts +4 -0
  129. package/dist/types/src/combobox/useComboBoxState.d.ts +1 -1
  130. package/dist/types/src/menu/useMenuTriggerState.d.ts +1 -1
  131. package/dist/types/src/overlays/useOverlayTriggerState.d.ts +9 -0
  132. package/dist/types/src/table/useTableState.d.ts +7 -0
  133. package/dist/types/src/tokenfield/TokenFieldValue.d.ts +71 -0
  134. package/dist/types/src/tokenfield/useTokenFieldState.d.ts +19 -0
  135. package/dist/types/src/tooltip/useTooltipTriggerState.d.ts +5 -0
  136. package/dist/types/src/virtualizer/ScrollAnchor.d.ts +81 -0
  137. package/dist/types/src/virtualizer/Virtualizer.d.ts +1 -0
  138. package/dist/types/src/virtualizer/types.d.ts +2 -0
  139. package/package.json +5 -5
  140. package/useTokenFieldState/package.json +5 -0
@@ -1,5 +1,5 @@
1
1
  import {useOverlayTriggerState as $3b152ed05cfdb7e2$export$61c6a8c84e605fb6} from "../overlays/useOverlayTriggerState.js";
2
- import {useMemo as $9hT9S$useMemo, useRef as $9hT9S$useRef, useEffect as $9hT9S$useEffect} from "react";
2
+ import {useState as $9hT9S$useState, useMemo as $9hT9S$useMemo, useRef as $9hT9S$useRef, useEffect as $9hT9S$useEffect} from "react";
3
3
 
4
4
  /*
5
5
  * Copyright 2020 Adobe. All rights reserved.
@@ -23,6 +23,9 @@ let $d881726242c7a389$var$globalCooldownTimeout = null;
23
23
  function $d881726242c7a389$export$4d40659c25ecb50b(props = {}) {
24
24
  let { delay: delay = $d881726242c7a389$var$TOOLTIP_DELAY, closeDelay: closeDelay = $d881726242c7a389$var$TOOLTIP_COOLDOWN } = props;
25
25
  let { isOpen: isOpen, open: open, close: close } = (0, $3b152ed05cfdb7e2$export$61c6a8c84e605fb6)(props);
26
+ // Whether the current open/close transition should skip its animation. Set when swapping
27
+ // between tooltips during the global warmup period.
28
+ let [shouldSkipAnimation, setIsInstant] = (0, $9hT9S$useState)(false);
26
29
  let id = (0, $9hT9S$useMemo)(()=>`${++$d881726242c7a389$var$tooltipId}`, []);
27
30
  let closeTimeout = (0, $9hT9S$useRef)(null);
28
31
  let closeCallback = (0, $9hT9S$useRef)(close);
@@ -31,15 +34,18 @@ function $d881726242c7a389$export$4d40659c25ecb50b(props = {}) {
31
34
  };
32
35
  let closeOpenTooltips = ()=>{
33
36
  for(let hideTooltipId in $d881726242c7a389$var$tooltips)if (hideTooltipId !== id) {
34
- $d881726242c7a389$var$tooltips[hideTooltipId](true);
37
+ // Close other open tooltips instantly (no exit animation), since they are being
38
+ // replaced by this one during the warmup period.
39
+ $d881726242c7a389$var$tooltips[hideTooltipId](true, true);
35
40
  delete $d881726242c7a389$var$tooltips[hideTooltipId];
36
41
  }
37
42
  };
38
- let showTooltip = ()=>{
43
+ let showTooltip = (instant)=>{
39
44
  if (closeTimeout.current) clearTimeout(closeTimeout.current);
40
45
  closeTimeout.current = null;
41
46
  closeOpenTooltips();
42
47
  ensureTooltipEntry();
48
+ setIsInstant(!!instant);
43
49
  $d881726242c7a389$var$globalWarmedUp = true;
44
50
  open();
45
51
  if ($d881726242c7a389$var$globalWarmUpTimeout) {
@@ -51,7 +57,8 @@ function $d881726242c7a389$export$4d40659c25ecb50b(props = {}) {
51
57
  $d881726242c7a389$var$globalCooldownTimeout = null;
52
58
  }
53
59
  };
54
- let hideTooltip = (immediate)=>{
60
+ let hideTooltip = (immediate, instant)=>{
61
+ setIsInstant(!!instant);
55
62
  if (immediate || closeDelay <= 0) {
56
63
  if (closeTimeout.current) clearTimeout(closeTimeout.current);
57
64
  closeTimeout.current = null;
@@ -81,9 +88,11 @@ function $d881726242c7a389$export$4d40659c25ecb50b(props = {}) {
81
88
  $d881726242c7a389$var$globalWarmUpTimeout = setTimeout(()=>{
82
89
  $d881726242c7a389$var$globalWarmUpTimeout = null;
83
90
  $d881726242c7a389$var$globalWarmedUp = true;
84
- showTooltip();
91
+ // First tooltip in a sequence: animate in.
92
+ showTooltip(false);
85
93
  }, delay);
86
- } else if (!isOpen) showTooltip();
94
+ } else if (!isOpen) // Already warmed up: appear instantly without an animation.
95
+ showTooltip(true);
87
96
  };
88
97
  (0, $9hT9S$useEffect)(()=>{
89
98
  closeCallback.current = close;
@@ -101,9 +110,12 @@ function $d881726242c7a389$export$4d40659c25ecb50b(props = {}) {
101
110
  ]);
102
111
  return {
103
112
  isOpen: isOpen,
113
+ shouldSkipAnimation: shouldSkipAnimation,
104
114
  open: (immediate)=>{
105
115
  if (!immediate && delay > 0 && !closeTimeout.current) warmupTooltip();
106
- else showTooltip();
116
+ else // Immediate opens (e.g. focus, or delay of 0) appear instantly without an animation
117
+ // only if another tooltip is already warmed up.
118
+ showTooltip($d881726242c7a389$var$globalWarmedUp);
107
119
  },
108
120
  close: hideTooltip
109
121
  };
@@ -1 +1 @@
1
- {"mappings":";;;AAAA;;;;;;;;;;CAUC;;AA0CD,MAAM,sCAAgB,MAAM,yDAAyD;AACrF,MAAM,yCAAmB;AAezB,IAAI,iCAAW,CAAC;AAChB,IAAI,kCAAY;AAChB,IAAI,uCAAiB;AACrB,IAAI,4CAA4D;AAChE,IAAI,8CAA8D;AAO3D,SAAS,0CAAuB,QAA6B,CAAC,CAAC;IACpE,IAAI,SAAC,QAAQ,iDAAe,aAAa,wCAAiB,GAAG;IAC7D,IAAI,UAAC,MAAM,QAAE,IAAI,SAAE,KAAK,EAAC,GAAG,CAAA,GAAA,yCAAqB,EAAE;IACnD,IAAI,KAAK,CAAA,GAAA,cAAM,EAAE,IAAM,GAAG,EAAE,iCAAW,EAAE,EAAE;IAC3C,IAAI,eAAe,CAAA,GAAA,aAAK,EAAwC;IAChE,IAAI,gBAAgB,CAAA,GAAA,aAAK,EAAc;IAEvC,IAAI,qBAAqB;QACvB,8BAAQ,CAAC,GAAG,GAAG;IACjB;IAEA,IAAI,oBAAoB;QACtB,IAAK,IAAI,iBAAiB,+BACxB,IAAI,kBAAkB,IAAI;YACxB,8BAAQ,CAAC,cAAc,CAAC;YACxB,OAAO,8BAAQ,CAAC,cAAc;QAChC;IAEJ;IAEA,IAAI,cAAc;QAChB,IAAI,aAAa,OAAO,EACtB,aAAa,aAAa,OAAO;QAEnC,aAAa,OAAO,GAAG;QACvB;QACA;QACA,uCAAiB;QACjB;QACA,IAAI,2CAAqB;YACvB,aAAa;YACb,4CAAsB;QACxB;QACA,IAAI,6CAAuB;YACzB,aAAa;YACb,8CAAwB;QAC1B;IACF;IAEA,IAAI,cAAc,CAAC;QACjB,IAAI,aAAa,cAAc,GAAG;YAChC,IAAI,aAAa,OAAO,EACtB,aAAa,aAAa,OAAO;YAEnC,aAAa,OAAO,GAAG;YACvB,cAAc,OAAO;QACvB,OAAO,IAAI,CAAC,aAAa,OAAO,EAC9B,aAAa,OAAO,GAAG,WAAW;YAChC,aAAa,OAAO,GAAG;YACvB,cAAc,OAAO;QACvB,GAAG;QAGL,IAAI,2CAAqB;YACvB,aAAa;YACb,4CAAsB;QACxB;QACA,IAAI,sCAAgB;YAClB,IAAI,6CACF,aAAa;YAEf,8CAAwB,WACtB;gBACE,OAAO,8BAAQ,CAAC,GAAG;gBACnB,8CAAwB;gBACxB,uCAAiB;YACnB,GACA,KAAK,GAAG,CAAC,wCAAkB;QAE/B;IACF;IAEA,IAAI,gBAAgB;QAClB;QACA;QACA,IAAI,CAAC,UAAU,CAAC,sCAAgB;YAC9B,IAAI,2CACF,aAAa;YAGf,4CAAsB,WAAW;gBAC/B,4CAAsB;gBACtB,uCAAiB;gBACjB;YACF,GAAG;QACL,OAAO,IAAI,CAAC,QACV;IAEJ;IAEA,CAAA,GAAA,gBAAQ,EAAE;QACR,cAAc,OAAO,GAAG;IAC1B,GAAG;QAAC;KAAM;IAEV,CAAA,GAAA,gBAAQ,EAAE;QACR,OAAO;YACL,IAAI,aAAa,OAAO,EACtB,aAAa,aAAa,OAAO;YAEnC,IAAI,UAAU,8BAAQ,CAAC,GAAG;YAC1B,IAAI,SACF,OAAO,8BAAQ,CAAC,GAAG;QAEvB;IACF,GAAG;QAAC;KAAG;IAEP,OAAO;gBACL;QACA,MAAM,CAAA;YACJ,IAAI,CAAC,aAAa,QAAQ,KAAK,CAAC,aAAa,OAAO,EAClD;iBAEA;QAEJ;QACA,OAAO;IACT;AACF","sources":["packages/react-stately/src/tooltip/useTooltipTriggerState.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 {OverlayTriggerProps, useOverlayTriggerState} from '../overlays/useOverlayTriggerState';\nimport {useEffect, useMemo, useRef} from 'react';\n\nexport interface TooltipTriggerProps extends OverlayTriggerProps {\n /**\n * Whether the tooltip should be disabled, independent from the trigger.\n */\n isDisabled?: boolean;\n\n /**\n * The delay time for the tooltip to show up. [See\n * guidelines](https://spectrum.adobe.com/page/tooltip/#Immediate-or-delayed-appearance).\n *\n * @default 1500\n */\n delay?: number;\n\n /**\n * The delay time for the tooltip to close. [See\n * guidelines](https://spectrum.adobe.com/page/tooltip/#Warmup-and-cooldown).\n *\n * @default 500\n */\n closeDelay?: number;\n\n /**\n * By default, opens for both focus and hover. Can be made to open only for focus.\n *\n * @default 'hover'\n */\n trigger?: 'hover' | 'focus';\n\n /**\n * Whether the tooltip should close when the trigger is pressed.\n *\n * @default true\n */\n shouldCloseOnPress?: boolean;\n}\n\nconst TOOLTIP_DELAY = 1500; // this seems to be a 1.5 second delay, check with design\nconst TOOLTIP_COOLDOWN = 500;\n\nexport interface TooltipTriggerState {\n /** Whether the tooltip is currently showing. */\n isOpen: boolean;\n /**\n * Shows the tooltip. By default, the tooltip becomes visible after a delay\n * depending on a global warmup timer. The `immediate` option shows the\n * tooltip immediately instead.\n */\n open(immediate?: boolean): void;\n /** Hides the tooltip. */\n close(immediate?: boolean): void;\n}\n\nlet tooltips = {};\nlet tooltipId = 0;\nlet globalWarmedUp = false;\nlet globalWarmUpTimeout: ReturnType<typeof setTimeout> | null = null;\nlet globalCooldownTimeout: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * Manages state for a tooltip trigger. Tracks whether the tooltip is open, and provides\n * methods to toggle this state. Ensures only one tooltip is open at a time and controls\n * the delay for showing a tooltip.\n */\nexport function useTooltipTriggerState(props: TooltipTriggerProps = {}): TooltipTriggerState {\n let {delay = TOOLTIP_DELAY, closeDelay = TOOLTIP_COOLDOWN} = props;\n let {isOpen, open, close} = useOverlayTriggerState(props);\n let id = useMemo(() => `${++tooltipId}`, []);\n let closeTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n let closeCallback = useRef<() => void>(close);\n\n let ensureTooltipEntry = () => {\n tooltips[id] = hideTooltip;\n };\n\n let closeOpenTooltips = () => {\n for (let hideTooltipId in tooltips) {\n if (hideTooltipId !== id) {\n tooltips[hideTooltipId](true);\n delete tooltips[hideTooltipId];\n }\n }\n };\n\n let showTooltip = () => {\n if (closeTimeout.current) {\n clearTimeout(closeTimeout.current);\n }\n closeTimeout.current = null;\n closeOpenTooltips();\n ensureTooltipEntry();\n globalWarmedUp = true;\n open();\n if (globalWarmUpTimeout) {\n clearTimeout(globalWarmUpTimeout);\n globalWarmUpTimeout = null;\n }\n if (globalCooldownTimeout) {\n clearTimeout(globalCooldownTimeout);\n globalCooldownTimeout = null;\n }\n };\n\n let hideTooltip = (immediate?: boolean) => {\n if (immediate || closeDelay <= 0) {\n if (closeTimeout.current) {\n clearTimeout(closeTimeout.current);\n }\n closeTimeout.current = null;\n closeCallback.current();\n } else if (!closeTimeout.current) {\n closeTimeout.current = setTimeout(() => {\n closeTimeout.current = null;\n closeCallback.current();\n }, closeDelay);\n }\n\n if (globalWarmUpTimeout) {\n clearTimeout(globalWarmUpTimeout);\n globalWarmUpTimeout = null;\n }\n if (globalWarmedUp) {\n if (globalCooldownTimeout) {\n clearTimeout(globalCooldownTimeout);\n }\n globalCooldownTimeout = setTimeout(\n () => {\n delete tooltips[id];\n globalCooldownTimeout = null;\n globalWarmedUp = false;\n },\n Math.max(TOOLTIP_COOLDOWN, closeDelay)\n );\n }\n };\n\n let warmupTooltip = () => {\n closeOpenTooltips();\n ensureTooltipEntry();\n if (!isOpen && !globalWarmedUp) {\n if (globalWarmUpTimeout) {\n clearTimeout(globalWarmUpTimeout);\n }\n\n globalWarmUpTimeout = setTimeout(() => {\n globalWarmUpTimeout = null;\n globalWarmedUp = true;\n showTooltip();\n }, delay);\n } else if (!isOpen) {\n showTooltip();\n }\n };\n\n useEffect(() => {\n closeCallback.current = close;\n }, [close]);\n\n useEffect(() => {\n return () => {\n if (closeTimeout.current) {\n clearTimeout(closeTimeout.current);\n }\n let tooltip = tooltips[id];\n if (tooltip) {\n delete tooltips[id];\n }\n };\n }, [id]);\n\n return {\n isOpen,\n open: immediate => {\n if (!immediate && delay > 0 && !closeTimeout.current) {\n warmupTooltip();\n } else {\n showTooltip();\n }\n },\n close: hideTooltip\n };\n}\n"],"names":[],"version":3,"file":"useTooltipTriggerState.js.map"}
1
+ {"mappings":"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","sources":["packages/react-stately/src/tooltip/useTooltipTriggerState.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 {OverlayTriggerProps, useOverlayTriggerState} from '../overlays/useOverlayTriggerState';\nimport {useEffect, useMemo, useRef, useState} from 'react';\n\nexport interface TooltipTriggerProps extends OverlayTriggerProps {\n /**\n * Whether the tooltip should be disabled, independent from the trigger.\n */\n isDisabled?: boolean;\n\n /**\n * The delay time for the tooltip to show up. [See\n * guidelines](https://spectrum.adobe.com/page/tooltip/#Immediate-or-delayed-appearance).\n *\n * @default 1500\n */\n delay?: number;\n\n /**\n * The delay time for the tooltip to close. [See\n * guidelines](https://spectrum.adobe.com/page/tooltip/#Warmup-and-cooldown).\n *\n * @default 500\n */\n closeDelay?: number;\n\n /**\n * By default, opens for both focus and hover. Can be made to open only for focus.\n *\n * @default 'hover'\n */\n trigger?: 'hover' | 'focus';\n\n /**\n * Whether the tooltip should close when the trigger is pressed.\n *\n * @default true\n */\n shouldCloseOnPress?: boolean;\n}\n\nconst TOOLTIP_DELAY = 1500; // this seems to be a 1.5 second delay, check with design\nconst TOOLTIP_COOLDOWN = 500;\n\nexport interface TooltipTriggerState {\n /** Whether the tooltip is currently showing. */\n isOpen: boolean;\n /**\n * Whether the tooltip is being shown or hidden without an animation. This is true while the\n * global warmup timer is active, i.e. when quickly moving between tooltips.\n */\n shouldSkipAnimation: boolean;\n /**\n * Shows the tooltip. By default, the tooltip becomes visible after a delay\n * depending on a global warmup timer. The `immediate` option shows the\n * tooltip immediately instead.\n */\n open(immediate?: boolean): void;\n /** Hides the tooltip. */\n close(immediate?: boolean): void;\n}\n\nlet tooltips = {};\nlet tooltipId = 0;\nlet globalWarmedUp = false;\nlet globalWarmUpTimeout: ReturnType<typeof setTimeout> | null = null;\nlet globalCooldownTimeout: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * Manages state for a tooltip trigger. Tracks whether the tooltip is open, and provides\n * methods to toggle this state. Ensures only one tooltip is open at a time and controls\n * the delay for showing a tooltip.\n */\nexport function useTooltipTriggerState(props: TooltipTriggerProps = {}): TooltipTriggerState {\n let {delay = TOOLTIP_DELAY, closeDelay = TOOLTIP_COOLDOWN} = props;\n let {isOpen, open, close} = useOverlayTriggerState(props);\n // Whether the current open/close transition should skip its animation. Set when swapping\n // between tooltips during the global warmup period.\n let [shouldSkipAnimation, setIsInstant] = useState(false);\n let id = useMemo(() => `${++tooltipId}`, []);\n let closeTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n let closeCallback = useRef<() => void>(close);\n\n let ensureTooltipEntry = () => {\n tooltips[id] = hideTooltip;\n };\n\n let closeOpenTooltips = () => {\n for (let hideTooltipId in tooltips) {\n if (hideTooltipId !== id) {\n // Close other open tooltips instantly (no exit animation), since they are being\n // replaced by this one during the warmup period.\n tooltips[hideTooltipId](true, true);\n delete tooltips[hideTooltipId];\n }\n }\n };\n\n let showTooltip = (instant?: boolean) => {\n if (closeTimeout.current) {\n clearTimeout(closeTimeout.current);\n }\n closeTimeout.current = null;\n closeOpenTooltips();\n ensureTooltipEntry();\n setIsInstant(!!instant);\n globalWarmedUp = true;\n open();\n if (globalWarmUpTimeout) {\n clearTimeout(globalWarmUpTimeout);\n globalWarmUpTimeout = null;\n }\n if (globalCooldownTimeout) {\n clearTimeout(globalCooldownTimeout);\n globalCooldownTimeout = null;\n }\n };\n\n let hideTooltip = (immediate?: boolean, instant?: boolean) => {\n setIsInstant(!!instant);\n if (immediate || closeDelay <= 0) {\n if (closeTimeout.current) {\n clearTimeout(closeTimeout.current);\n }\n closeTimeout.current = null;\n closeCallback.current();\n } else if (!closeTimeout.current) {\n closeTimeout.current = setTimeout(() => {\n closeTimeout.current = null;\n closeCallback.current();\n }, closeDelay);\n }\n\n if (globalWarmUpTimeout) {\n clearTimeout(globalWarmUpTimeout);\n globalWarmUpTimeout = null;\n }\n if (globalWarmedUp) {\n if (globalCooldownTimeout) {\n clearTimeout(globalCooldownTimeout);\n }\n globalCooldownTimeout = setTimeout(\n () => {\n delete tooltips[id];\n globalCooldownTimeout = null;\n globalWarmedUp = false;\n },\n Math.max(TOOLTIP_COOLDOWN, closeDelay)\n );\n }\n };\n\n let warmupTooltip = () => {\n closeOpenTooltips();\n ensureTooltipEntry();\n if (!isOpen && !globalWarmedUp) {\n if (globalWarmUpTimeout) {\n clearTimeout(globalWarmUpTimeout);\n }\n\n globalWarmUpTimeout = setTimeout(() => {\n globalWarmUpTimeout = null;\n globalWarmedUp = true;\n // First tooltip in a sequence: animate in.\n showTooltip(false);\n }, delay);\n } else if (!isOpen) {\n // Already warmed up: appear instantly without an animation.\n showTooltip(true);\n }\n };\n\n useEffect(() => {\n closeCallback.current = close;\n }, [close]);\n\n useEffect(() => {\n return () => {\n if (closeTimeout.current) {\n clearTimeout(closeTimeout.current);\n }\n let tooltip = tooltips[id];\n if (tooltip) {\n delete tooltips[id];\n }\n };\n }, [id]);\n\n return {\n isOpen,\n shouldSkipAnimation,\n open: immediate => {\n if (!immediate && delay > 0 && !closeTimeout.current) {\n warmupTooltip();\n } else {\n // Immediate opens (e.g. focus, or delay of 0) appear instantly without an animation\n // only if another tooltip is already warmed up.\n showTooltip(globalWarmedUp);\n }\n },\n close: hideTooltip\n };\n}\n"],"names":[],"version":3,"file":"useTooltipTriggerState.js.map"}
@@ -1,5 +1,5 @@
1
1
  import {useOverlayTriggerState as $f11fb0bcf1b2687a$export$61c6a8c84e605fb6} from "../overlays/useOverlayTriggerState.mjs";
2
- import {useMemo as $auOuY$useMemo, useRef as $auOuY$useRef, useEffect as $auOuY$useEffect} from "react";
2
+ import {useState as $auOuY$useState, useMemo as $auOuY$useMemo, useRef as $auOuY$useRef, useEffect as $auOuY$useEffect} from "react";
3
3
 
4
4
  /*
5
5
  * Copyright 2020 Adobe. All rights reserved.
@@ -23,6 +23,9 @@ let $3834487504f4fc00$var$globalCooldownTimeout = null;
23
23
  function $3834487504f4fc00$export$4d40659c25ecb50b(props = {}) {
24
24
  let { delay: delay = $3834487504f4fc00$var$TOOLTIP_DELAY, closeDelay: closeDelay = $3834487504f4fc00$var$TOOLTIP_COOLDOWN } = props;
25
25
  let { isOpen: isOpen, open: open, close: close } = (0, $f11fb0bcf1b2687a$export$61c6a8c84e605fb6)(props);
26
+ // Whether the current open/close transition should skip its animation. Set when swapping
27
+ // between tooltips during the global warmup period.
28
+ let [shouldSkipAnimation, setIsInstant] = (0, $auOuY$useState)(false);
26
29
  let id = (0, $auOuY$useMemo)(()=>`${++$3834487504f4fc00$var$tooltipId}`, []);
27
30
  let closeTimeout = (0, $auOuY$useRef)(null);
28
31
  let closeCallback = (0, $auOuY$useRef)(close);
@@ -31,15 +34,18 @@ function $3834487504f4fc00$export$4d40659c25ecb50b(props = {}) {
31
34
  };
32
35
  let closeOpenTooltips = ()=>{
33
36
  for(let hideTooltipId in $3834487504f4fc00$var$tooltips)if (hideTooltipId !== id) {
34
- $3834487504f4fc00$var$tooltips[hideTooltipId](true);
37
+ // Close other open tooltips instantly (no exit animation), since they are being
38
+ // replaced by this one during the warmup period.
39
+ $3834487504f4fc00$var$tooltips[hideTooltipId](true, true);
35
40
  delete $3834487504f4fc00$var$tooltips[hideTooltipId];
36
41
  }
37
42
  };
38
- let showTooltip = ()=>{
43
+ let showTooltip = (instant)=>{
39
44
  if (closeTimeout.current) clearTimeout(closeTimeout.current);
40
45
  closeTimeout.current = null;
41
46
  closeOpenTooltips();
42
47
  ensureTooltipEntry();
48
+ setIsInstant(!!instant);
43
49
  $3834487504f4fc00$var$globalWarmedUp = true;
44
50
  open();
45
51
  if ($3834487504f4fc00$var$globalWarmUpTimeout) {
@@ -51,7 +57,8 @@ function $3834487504f4fc00$export$4d40659c25ecb50b(props = {}) {
51
57
  $3834487504f4fc00$var$globalCooldownTimeout = null;
52
58
  }
53
59
  };
54
- let hideTooltip = (immediate)=>{
60
+ let hideTooltip = (immediate, instant)=>{
61
+ setIsInstant(!!instant);
55
62
  if (immediate || closeDelay <= 0) {
56
63
  if (closeTimeout.current) clearTimeout(closeTimeout.current);
57
64
  closeTimeout.current = null;
@@ -81,9 +88,11 @@ function $3834487504f4fc00$export$4d40659c25ecb50b(props = {}) {
81
88
  $3834487504f4fc00$var$globalWarmUpTimeout = setTimeout(()=>{
82
89
  $3834487504f4fc00$var$globalWarmUpTimeout = null;
83
90
  $3834487504f4fc00$var$globalWarmedUp = true;
84
- showTooltip();
91
+ // First tooltip in a sequence: animate in.
92
+ showTooltip(false);
85
93
  }, delay);
86
- } else if (!isOpen) showTooltip();
94
+ } else if (!isOpen) // Already warmed up: appear instantly without an animation.
95
+ showTooltip(true);
87
96
  };
88
97
  (0, $auOuY$useEffect)(()=>{
89
98
  closeCallback.current = close;
@@ -101,9 +110,12 @@ function $3834487504f4fc00$export$4d40659c25ecb50b(props = {}) {
101
110
  ]);
102
111
  return {
103
112
  isOpen: isOpen,
113
+ shouldSkipAnimation: shouldSkipAnimation,
104
114
  open: (immediate)=>{
105
115
  if (!immediate && delay > 0 && !closeTimeout.current) warmupTooltip();
106
- else showTooltip();
116
+ else // Immediate opens (e.g. focus, or delay of 0) appear instantly without an animation
117
+ // only if another tooltip is already warmed up.
118
+ showTooltip($3834487504f4fc00$var$globalWarmedUp);
107
119
  },
108
120
  close: hideTooltip
109
121
  };
@@ -1 +1 @@
1
- {"mappings":";;;AAAA;;;;;;;;;;CAUC;;AA0CD,MAAM,sCAAgB,MAAM,yDAAyD;AACrF,MAAM,yCAAmB;AAezB,IAAI,iCAAW,CAAC;AAChB,IAAI,kCAAY;AAChB,IAAI,uCAAiB;AACrB,IAAI,4CAA4D;AAChE,IAAI,8CAA8D;AAO3D,SAAS,0CAAuB,QAA6B,CAAC,CAAC;IACpE,IAAI,SAAC,QAAQ,iDAAe,aAAa,wCAAiB,GAAG;IAC7D,IAAI,UAAC,MAAM,QAAE,IAAI,SAAE,KAAK,EAAC,GAAG,CAAA,GAAA,yCAAqB,EAAE;IACnD,IAAI,KAAK,CAAA,GAAA,cAAM,EAAE,IAAM,GAAG,EAAE,iCAAW,EAAE,EAAE;IAC3C,IAAI,eAAe,CAAA,GAAA,aAAK,EAAwC;IAChE,IAAI,gBAAgB,CAAA,GAAA,aAAK,EAAc;IAEvC,IAAI,qBAAqB;QACvB,8BAAQ,CAAC,GAAG,GAAG;IACjB;IAEA,IAAI,oBAAoB;QACtB,IAAK,IAAI,iBAAiB,+BACxB,IAAI,kBAAkB,IAAI;YACxB,8BAAQ,CAAC,cAAc,CAAC;YACxB,OAAO,8BAAQ,CAAC,cAAc;QAChC;IAEJ;IAEA,IAAI,cAAc;QAChB,IAAI,aAAa,OAAO,EACtB,aAAa,aAAa,OAAO;QAEnC,aAAa,OAAO,GAAG;QACvB;QACA;QACA,uCAAiB;QACjB;QACA,IAAI,2CAAqB;YACvB,aAAa;YACb,4CAAsB;QACxB;QACA,IAAI,6CAAuB;YACzB,aAAa;YACb,8CAAwB;QAC1B;IACF;IAEA,IAAI,cAAc,CAAC;QACjB,IAAI,aAAa,cAAc,GAAG;YAChC,IAAI,aAAa,OAAO,EACtB,aAAa,aAAa,OAAO;YAEnC,aAAa,OAAO,GAAG;YACvB,cAAc,OAAO;QACvB,OAAO,IAAI,CAAC,aAAa,OAAO,EAC9B,aAAa,OAAO,GAAG,WAAW;YAChC,aAAa,OAAO,GAAG;YACvB,cAAc,OAAO;QACvB,GAAG;QAGL,IAAI,2CAAqB;YACvB,aAAa;YACb,4CAAsB;QACxB;QACA,IAAI,sCAAgB;YAClB,IAAI,6CACF,aAAa;YAEf,8CAAwB,WACtB;gBACE,OAAO,8BAAQ,CAAC,GAAG;gBACnB,8CAAwB;gBACxB,uCAAiB;YACnB,GACA,KAAK,GAAG,CAAC,wCAAkB;QAE/B;IACF;IAEA,IAAI,gBAAgB;QAClB;QACA;QACA,IAAI,CAAC,UAAU,CAAC,sCAAgB;YAC9B,IAAI,2CACF,aAAa;YAGf,4CAAsB,WAAW;gBAC/B,4CAAsB;gBACtB,uCAAiB;gBACjB;YACF,GAAG;QACL,OAAO,IAAI,CAAC,QACV;IAEJ;IAEA,CAAA,GAAA,gBAAQ,EAAE;QACR,cAAc,OAAO,GAAG;IAC1B,GAAG;QAAC;KAAM;IAEV,CAAA,GAAA,gBAAQ,EAAE;QACR,OAAO;YACL,IAAI,aAAa,OAAO,EACtB,aAAa,aAAa,OAAO;YAEnC,IAAI,UAAU,8BAAQ,CAAC,GAAG;YAC1B,IAAI,SACF,OAAO,8BAAQ,CAAC,GAAG;QAEvB;IACF,GAAG;QAAC;KAAG;IAEP,OAAO;gBACL;QACA,MAAM,CAAA;YACJ,IAAI,CAAC,aAAa,QAAQ,KAAK,CAAC,aAAa,OAAO,EAClD;iBAEA;QAEJ;QACA,OAAO;IACT;AACF","sources":["packages/react-stately/src/tooltip/useTooltipTriggerState.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 {OverlayTriggerProps, useOverlayTriggerState} from '../overlays/useOverlayTriggerState';\nimport {useEffect, useMemo, useRef} from 'react';\n\nexport interface TooltipTriggerProps extends OverlayTriggerProps {\n /**\n * Whether the tooltip should be disabled, independent from the trigger.\n */\n isDisabled?: boolean;\n\n /**\n * The delay time for the tooltip to show up. [See\n * guidelines](https://spectrum.adobe.com/page/tooltip/#Immediate-or-delayed-appearance).\n *\n * @default 1500\n */\n delay?: number;\n\n /**\n * The delay time for the tooltip to close. [See\n * guidelines](https://spectrum.adobe.com/page/tooltip/#Warmup-and-cooldown).\n *\n * @default 500\n */\n closeDelay?: number;\n\n /**\n * By default, opens for both focus and hover. Can be made to open only for focus.\n *\n * @default 'hover'\n */\n trigger?: 'hover' | 'focus';\n\n /**\n * Whether the tooltip should close when the trigger is pressed.\n *\n * @default true\n */\n shouldCloseOnPress?: boolean;\n}\n\nconst TOOLTIP_DELAY = 1500; // this seems to be a 1.5 second delay, check with design\nconst TOOLTIP_COOLDOWN = 500;\n\nexport interface TooltipTriggerState {\n /** Whether the tooltip is currently showing. */\n isOpen: boolean;\n /**\n * Shows the tooltip. By default, the tooltip becomes visible after a delay\n * depending on a global warmup timer. The `immediate` option shows the\n * tooltip immediately instead.\n */\n open(immediate?: boolean): void;\n /** Hides the tooltip. */\n close(immediate?: boolean): void;\n}\n\nlet tooltips = {};\nlet tooltipId = 0;\nlet globalWarmedUp = false;\nlet globalWarmUpTimeout: ReturnType<typeof setTimeout> | null = null;\nlet globalCooldownTimeout: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * Manages state for a tooltip trigger. Tracks whether the tooltip is open, and provides\n * methods to toggle this state. Ensures only one tooltip is open at a time and controls\n * the delay for showing a tooltip.\n */\nexport function useTooltipTriggerState(props: TooltipTriggerProps = {}): TooltipTriggerState {\n let {delay = TOOLTIP_DELAY, closeDelay = TOOLTIP_COOLDOWN} = props;\n let {isOpen, open, close} = useOverlayTriggerState(props);\n let id = useMemo(() => `${++tooltipId}`, []);\n let closeTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n let closeCallback = useRef<() => void>(close);\n\n let ensureTooltipEntry = () => {\n tooltips[id] = hideTooltip;\n };\n\n let closeOpenTooltips = () => {\n for (let hideTooltipId in tooltips) {\n if (hideTooltipId !== id) {\n tooltips[hideTooltipId](true);\n delete tooltips[hideTooltipId];\n }\n }\n };\n\n let showTooltip = () => {\n if (closeTimeout.current) {\n clearTimeout(closeTimeout.current);\n }\n closeTimeout.current = null;\n closeOpenTooltips();\n ensureTooltipEntry();\n globalWarmedUp = true;\n open();\n if (globalWarmUpTimeout) {\n clearTimeout(globalWarmUpTimeout);\n globalWarmUpTimeout = null;\n }\n if (globalCooldownTimeout) {\n clearTimeout(globalCooldownTimeout);\n globalCooldownTimeout = null;\n }\n };\n\n let hideTooltip = (immediate?: boolean) => {\n if (immediate || closeDelay <= 0) {\n if (closeTimeout.current) {\n clearTimeout(closeTimeout.current);\n }\n closeTimeout.current = null;\n closeCallback.current();\n } else if (!closeTimeout.current) {\n closeTimeout.current = setTimeout(() => {\n closeTimeout.current = null;\n closeCallback.current();\n }, closeDelay);\n }\n\n if (globalWarmUpTimeout) {\n clearTimeout(globalWarmUpTimeout);\n globalWarmUpTimeout = null;\n }\n if (globalWarmedUp) {\n if (globalCooldownTimeout) {\n clearTimeout(globalCooldownTimeout);\n }\n globalCooldownTimeout = setTimeout(\n () => {\n delete tooltips[id];\n globalCooldownTimeout = null;\n globalWarmedUp = false;\n },\n Math.max(TOOLTIP_COOLDOWN, closeDelay)\n );\n }\n };\n\n let warmupTooltip = () => {\n closeOpenTooltips();\n ensureTooltipEntry();\n if (!isOpen && !globalWarmedUp) {\n if (globalWarmUpTimeout) {\n clearTimeout(globalWarmUpTimeout);\n }\n\n globalWarmUpTimeout = setTimeout(() => {\n globalWarmUpTimeout = null;\n globalWarmedUp = true;\n showTooltip();\n }, delay);\n } else if (!isOpen) {\n showTooltip();\n }\n };\n\n useEffect(() => {\n closeCallback.current = close;\n }, [close]);\n\n useEffect(() => {\n return () => {\n if (closeTimeout.current) {\n clearTimeout(closeTimeout.current);\n }\n let tooltip = tooltips[id];\n if (tooltip) {\n delete tooltips[id];\n }\n };\n }, [id]);\n\n return {\n isOpen,\n open: immediate => {\n if (!immediate && delay > 0 && !closeTimeout.current) {\n warmupTooltip();\n } else {\n showTooltip();\n }\n },\n close: hideTooltip\n };\n}\n"],"names":[],"version":3,"file":"useTooltipTriggerState.mjs.map"}
1
+ {"mappings":"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","sources":["packages/react-stately/src/tooltip/useTooltipTriggerState.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 {OverlayTriggerProps, useOverlayTriggerState} from '../overlays/useOverlayTriggerState';\nimport {useEffect, useMemo, useRef, useState} from 'react';\n\nexport interface TooltipTriggerProps extends OverlayTriggerProps {\n /**\n * Whether the tooltip should be disabled, independent from the trigger.\n */\n isDisabled?: boolean;\n\n /**\n * The delay time for the tooltip to show up. [See\n * guidelines](https://spectrum.adobe.com/page/tooltip/#Immediate-or-delayed-appearance).\n *\n * @default 1500\n */\n delay?: number;\n\n /**\n * The delay time for the tooltip to close. [See\n * guidelines](https://spectrum.adobe.com/page/tooltip/#Warmup-and-cooldown).\n *\n * @default 500\n */\n closeDelay?: number;\n\n /**\n * By default, opens for both focus and hover. Can be made to open only for focus.\n *\n * @default 'hover'\n */\n trigger?: 'hover' | 'focus';\n\n /**\n * Whether the tooltip should close when the trigger is pressed.\n *\n * @default true\n */\n shouldCloseOnPress?: boolean;\n}\n\nconst TOOLTIP_DELAY = 1500; // this seems to be a 1.5 second delay, check with design\nconst TOOLTIP_COOLDOWN = 500;\n\nexport interface TooltipTriggerState {\n /** Whether the tooltip is currently showing. */\n isOpen: boolean;\n /**\n * Whether the tooltip is being shown or hidden without an animation. This is true while the\n * global warmup timer is active, i.e. when quickly moving between tooltips.\n */\n shouldSkipAnimation: boolean;\n /**\n * Shows the tooltip. By default, the tooltip becomes visible after a delay\n * depending on a global warmup timer. The `immediate` option shows the\n * tooltip immediately instead.\n */\n open(immediate?: boolean): void;\n /** Hides the tooltip. */\n close(immediate?: boolean): void;\n}\n\nlet tooltips = {};\nlet tooltipId = 0;\nlet globalWarmedUp = false;\nlet globalWarmUpTimeout: ReturnType<typeof setTimeout> | null = null;\nlet globalCooldownTimeout: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * Manages state for a tooltip trigger. Tracks whether the tooltip is open, and provides\n * methods to toggle this state. Ensures only one tooltip is open at a time and controls\n * the delay for showing a tooltip.\n */\nexport function useTooltipTriggerState(props: TooltipTriggerProps = {}): TooltipTriggerState {\n let {delay = TOOLTIP_DELAY, closeDelay = TOOLTIP_COOLDOWN} = props;\n let {isOpen, open, close} = useOverlayTriggerState(props);\n // Whether the current open/close transition should skip its animation. Set when swapping\n // between tooltips during the global warmup period.\n let [shouldSkipAnimation, setIsInstant] = useState(false);\n let id = useMemo(() => `${++tooltipId}`, []);\n let closeTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n let closeCallback = useRef<() => void>(close);\n\n let ensureTooltipEntry = () => {\n tooltips[id] = hideTooltip;\n };\n\n let closeOpenTooltips = () => {\n for (let hideTooltipId in tooltips) {\n if (hideTooltipId !== id) {\n // Close other open tooltips instantly (no exit animation), since they are being\n // replaced by this one during the warmup period.\n tooltips[hideTooltipId](true, true);\n delete tooltips[hideTooltipId];\n }\n }\n };\n\n let showTooltip = (instant?: boolean) => {\n if (closeTimeout.current) {\n clearTimeout(closeTimeout.current);\n }\n closeTimeout.current = null;\n closeOpenTooltips();\n ensureTooltipEntry();\n setIsInstant(!!instant);\n globalWarmedUp = true;\n open();\n if (globalWarmUpTimeout) {\n clearTimeout(globalWarmUpTimeout);\n globalWarmUpTimeout = null;\n }\n if (globalCooldownTimeout) {\n clearTimeout(globalCooldownTimeout);\n globalCooldownTimeout = null;\n }\n };\n\n let hideTooltip = (immediate?: boolean, instant?: boolean) => {\n setIsInstant(!!instant);\n if (immediate || closeDelay <= 0) {\n if (closeTimeout.current) {\n clearTimeout(closeTimeout.current);\n }\n closeTimeout.current = null;\n closeCallback.current();\n } else if (!closeTimeout.current) {\n closeTimeout.current = setTimeout(() => {\n closeTimeout.current = null;\n closeCallback.current();\n }, closeDelay);\n }\n\n if (globalWarmUpTimeout) {\n clearTimeout(globalWarmUpTimeout);\n globalWarmUpTimeout = null;\n }\n if (globalWarmedUp) {\n if (globalCooldownTimeout) {\n clearTimeout(globalCooldownTimeout);\n }\n globalCooldownTimeout = setTimeout(\n () => {\n delete tooltips[id];\n globalCooldownTimeout = null;\n globalWarmedUp = false;\n },\n Math.max(TOOLTIP_COOLDOWN, closeDelay)\n );\n }\n };\n\n let warmupTooltip = () => {\n closeOpenTooltips();\n ensureTooltipEntry();\n if (!isOpen && !globalWarmedUp) {\n if (globalWarmUpTimeout) {\n clearTimeout(globalWarmUpTimeout);\n }\n\n globalWarmUpTimeout = setTimeout(() => {\n globalWarmUpTimeout = null;\n globalWarmedUp = true;\n // First tooltip in a sequence: animate in.\n showTooltip(false);\n }, delay);\n } else if (!isOpen) {\n // Already warmed up: appear instantly without an animation.\n showTooltip(true);\n }\n };\n\n useEffect(() => {\n closeCallback.current = close;\n }, [close]);\n\n useEffect(() => {\n return () => {\n if (closeTimeout.current) {\n clearTimeout(closeTimeout.current);\n }\n let tooltip = tooltips[id];\n if (tooltip) {\n delete tooltips[id];\n }\n };\n }, [id]);\n\n return {\n isOpen,\n shouldSkipAnimation,\n open: immediate => {\n if (!immediate && delay > 0 && !closeTimeout.current) {\n warmupTooltip();\n } else {\n // Immediate opens (e.g. focus, or delay of 0) appear instantly without an animation\n // only if another tooltip is already warmed up.\n showTooltip(globalWarmedUp);\n }\n },\n close: hideTooltip\n };\n}\n"],"names":[],"version":3,"file":"useTooltipTriggerState.mjs.map"}
@@ -0,0 +1,136 @@
1
+ var $2bdd626d0eea82c5$exports = require("./Rect.cjs");
2
+
3
+
4
+ function $parcel$export(e, n, v, s) {
5
+ Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
6
+ }
7
+
8
+ $parcel$export(module.exports, "ScrollAnchorTracker", function () { return $e06c1e621ca9099b$export$8ce0fdedc36cb6b; });
9
+ /*
10
+ * Copyright 2026 Adobe. All rights reserved.
11
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License. You may obtain a copy
13
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
14
+ *
15
+ * Unless required by applicable law or agreed to in writing, software distributed under
16
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
17
+ * OF ANY KIND, either express or implied. See the License for the specific language
18
+ * governing permissions and limitations under the License.
19
+ */
20
+ /**
21
+ * Minimum overlap an item must have with the viewport, along the scroll axis,
22
+ * to be eligible as a scroll anchor. Without this, an item that only overlaps the viewport
23
+ * by a sliver (e.g. 1px, essentially scrolled out of view) can still "win" the anchor
24
+ * tie-break over a substantially visible item.
25
+ */ const $e06c1e621ca9099b$var$MIN_ANCHOR_OVERLAP = 4;
26
+ function $e06c1e621ca9099b$var$dimensionForAxis(axis) {
27
+ return axis === 'x' ? 'width' : 'height';
28
+ }
29
+ function $e06c1e621ca9099b$export$413719533c5919c9(anchor, axis, getLayoutInfo, visibleRect, contentSize) {
30
+ let finalInfo = getLayoutInfo(anchor.key);
31
+ if (!finalInfo) return null;
32
+ let adjustment = finalInfo.rect[anchor.corner][axis] - visibleRect[axis] - anchor.offset;
33
+ if (adjustment === 0) return null;
34
+ let target = visibleRect[axis] + adjustment;
35
+ let dimension = $e06c1e621ca9099b$var$dimensionForAxis(axis);
36
+ let max = Math.max(0, contentSize[dimension] - visibleRect[dimension]);
37
+ let clamped = Math.max(0, Math.min(max, target));
38
+ return clamped !== visibleRect[axis] ? clamped : null;
39
+ }
40
+ function $e06c1e621ca9099b$export$69fb4c6de5f339b7(edge, axis, visibleRect, visibleLayoutInfos, isAnchorable = ()=>true) {
41
+ let dimension = $e06c1e621ca9099b$var$dimensionForAxis(axis);
42
+ let best = null;
43
+ for (let [key, layoutInfo] of visibleLayoutInfos){
44
+ if (!layoutInfo || !isAnchorable(layoutInfo)) continue;
45
+ let overlap = layoutInfo.rect.intersection(visibleRect)[dimension];
46
+ if (layoutInfo.rect.area > 0 && overlap >= $e06c1e621ca9099b$var$MIN_ANCHOR_OVERLAP) {
47
+ let corner = layoutInfo.rect.getCornerInRect(visibleRect) ?? 'topLeft';
48
+ let offset = layoutInfo.rect[corner][axis] - visibleRect[axis];
49
+ let isBetter = !best || (edge === 'end' ? offset < best.offset : offset > best.offset);
50
+ if (isBetter) best = {
51
+ key: key,
52
+ corner: corner,
53
+ offset: offset
54
+ };
55
+ }
56
+ }
57
+ return best;
58
+ }
59
+ function $e06c1e621ca9099b$export$c74597e403428eac(edge, axis, contentSize, previousVisibleRect) {
60
+ if (edge === 'start') return 0;
61
+ let dimension = $e06c1e621ca9099b$var$dimensionForAxis(axis);
62
+ return Math.max(0, contentSize[dimension] - previousVisibleRect[dimension]);
63
+ }
64
+ function $e06c1e621ca9099b$export$f644bffd4937530e(visibleRect, contentSize, edge, axis, threshold) {
65
+ if (edge === 'start') return visibleRect[axis] <= threshold;
66
+ let dimension = $e06c1e621ca9099b$var$dimensionForAxis(axis);
67
+ let distanceFromEnd = contentSize[dimension] - (visibleRect[axis] + visibleRect[dimension]);
68
+ return distanceFromEnd <= threshold;
69
+ }
70
+ function $e06c1e621ca9099b$export$dcb80b718ca193d7(edge, axis, anchor, wasNearAnchorEdge, isScrolling, itemSizeChanged, contentSizeDelta, getLayoutInfo, previousVisibleRect, contentSize) {
71
+ let withTarget = (target)=>axis === 'x' ? new (0, $2bdd626d0eea82c5$exports.Rect)(target, previousVisibleRect.y, previousVisibleRect.width, previousVisibleRect.height) : new (0, $2bdd626d0eea82c5$exports.Rect)(previousVisibleRect.x, target, previousVisibleRect.width, previousVisibleRect.height);
72
+ if (anchor) {
73
+ let target = $e06c1e621ca9099b$export$413719533c5919c9(anchor, axis, getLayoutInfo, previousVisibleRect, contentSize);
74
+ if (target != null) return withTarget(target);
75
+ }
76
+ if (wasNearAnchorEdge && !isScrolling && (!itemSizeChanged || contentSizeDelta > 0)) {
77
+ let target = withTarget($e06c1e621ca9099b$export$c74597e403428eac(edge, axis, contentSize, previousVisibleRect));
78
+ return target.equals(previousVisibleRect) ? null : target;
79
+ }
80
+ return null;
81
+ }
82
+ class $e06c1e621ca9099b$export$8ce0fdedc36cb6b {
83
+ /** Resets all tracked state, e.g. when the virtualizer's layout instance changes. */ reset() {
84
+ this.hasSnappedToEdge = false;
85
+ this.hadEstimatedVisibleItems = false;
86
+ this.wasNearAnchorEdge = false;
87
+ }
88
+ /**
89
+ * Captures the anchor from pre-layout view positions.
90
+ */ captureBeforeLayout(anchorInfo, preLayoutInfos, visibleRect) {
91
+ if (!anchorInfo) return null;
92
+ return $e06c1e621ca9099b$export$69fb4c6de5f339b7(anchorInfo.edge, anchorInfo.axis, visibleRect, preLayoutInfos, anchorInfo.isAnchorable);
93
+ }
94
+ /**
95
+ * Runs the full post-layout decision: updates the tracked state for the next pass, and
96
+ * returns the resolved scroll target, or null if nothing should change.
97
+ */ resolveAfterLayout(options) {
98
+ let { anchorInfo: anchorInfo, anchor: anchor, postLayoutInfos: postLayoutInfos, previousVisibleRect: previousVisibleRect, previousContentSize: previousContentSize, contentSize: contentSize, itemSizeChanged: itemSizeChanged, isScrolling: isScrolling, getLayoutInfo: getLayoutInfo } = options;
99
+ if (!anchorInfo) return null;
100
+ // Read the previous pass's state into locals before any writes below overwrite it.
101
+ let wasSettlingLastPass = this.hadEstimatedVisibleItems;
102
+ let wasNearAnchorEdgeLastPass = this.wasNearAnchorEdge;
103
+ let hasEstimated = false;
104
+ for (let layoutInfo of postLayoutInfos.values())if (layoutInfo.estimatedSize) {
105
+ hasEstimated = true;
106
+ break;
107
+ }
108
+ this.hadEstimatedVisibleItems = hasEstimated;
109
+ // Don't recheck "near edge?" mid-resize because it could look like a scroll that never happened.
110
+ // Reuse the answer from before the resizing started.
111
+ if (!wasSettlingLastPass) this.wasNearAnchorEdge = $e06c1e621ca9099b$export$f644bffd4937530e(previousVisibleRect, previousContentSize, anchorInfo.edge, anchorInfo.axis, anchorInfo.threshold);
112
+ if (previousVisibleRect.area === 0) return null;
113
+ let dimension = anchorInfo.axis === 'x' ? 'width' : 'height';
114
+ let contentSizeDelta = contentSize[dimension] - previousContentSize[dimension];
115
+ let isFirstAnchoredLayout = !this.hasSnappedToEdge;
116
+ this.hasSnappedToEdge = true;
117
+ // Only modify scroll when content actually changed (or this is the first layout, which always snaps)
118
+ if (!(isFirstAnchoredLayout || contentSizeDelta !== 0 || itemSizeChanged)) return null;
119
+ let wasNearAnchorEdge = isFirstAnchoredLayout || wasSettlingLastPass && wasNearAnchorEdgeLastPass || $e06c1e621ca9099b$export$f644bffd4937530e(previousVisibleRect, previousContentSize, anchorInfo.edge, anchorInfo.axis, anchorInfo.threshold);
120
+ // A first-ever layout always snaps to the edge, even if the raw distance check says
121
+ // otherwise. Save that real decision here so later passes in this cascade reuse it.
122
+ if (!wasSettlingLastPass) this.wasNearAnchorEdge = wasNearAnchorEdge;
123
+ // Skip restoring to the captured anchor while still resizing because items above it are also still growing,
124
+ // and following it would fall short of the edge.
125
+ let effectiveAnchor = isFirstAnchoredLayout || wasSettlingLastPass && wasNearAnchorEdgeLastPass ? null : anchor;
126
+ return $e06c1e621ca9099b$export$dcb80b718ca193d7(anchorInfo.edge, anchorInfo.axis, effectiveAnchor, wasNearAnchorEdge, isScrolling, itemSizeChanged, contentSizeDelta, getLayoutInfo, previousVisibleRect, contentSize);
127
+ }
128
+ constructor(){
129
+ this.hasSnappedToEdge = false;
130
+ this.hadEstimatedVisibleItems = false;
131
+ this.wasNearAnchorEdge = false;
132
+ }
133
+ }
134
+
135
+
136
+ //# sourceMappingURL=ScrollAnchor.cjs.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.cjs.map"}
@@ -0,0 +1,132 @@
1
+ import {Rect as $546350db2c358941$export$c79fc6492f3af13d} from "./Rect.js";
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 $20fe89c1b1ab405a$var$MIN_ANCHOR_OVERLAP = 4;
20
+ function $20fe89c1b1ab405a$var$dimensionForAxis(axis) {
21
+ return axis === 'x' ? 'width' : 'height';
22
+ }
23
+ function $20fe89c1b1ab405a$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 = $20fe89c1b1ab405a$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 $20fe89c1b1ab405a$export$69fb4c6de5f339b7(edge, axis, visibleRect, visibleLayoutInfos, isAnchorable = ()=>true) {
35
+ let dimension = $20fe89c1b1ab405a$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 >= $20fe89c1b1ab405a$var$MIN_ANCHOR_OVERLAP) {
41
+ var _layoutInfo_rect_getCornerInRect;
42
+ let corner = (_layoutInfo_rect_getCornerInRect = layoutInfo.rect.getCornerInRect(visibleRect)) !== null && _layoutInfo_rect_getCornerInRect !== void 0 ? _layoutInfo_rect_getCornerInRect : 'topLeft';
43
+ let offset = layoutInfo.rect[corner][axis] - visibleRect[axis];
44
+ let isBetter = !best || (edge === 'end' ? offset < best.offset : offset > best.offset);
45
+ if (isBetter) best = {
46
+ key: key,
47
+ corner: corner,
48
+ offset: offset
49
+ };
50
+ }
51
+ }
52
+ return best;
53
+ }
54
+ function $20fe89c1b1ab405a$export$c74597e403428eac(edge, axis, contentSize, previousVisibleRect) {
55
+ if (edge === 'start') return 0;
56
+ let dimension = $20fe89c1b1ab405a$var$dimensionForAxis(axis);
57
+ return Math.max(0, contentSize[dimension] - previousVisibleRect[dimension]);
58
+ }
59
+ function $20fe89c1b1ab405a$export$f644bffd4937530e(visibleRect, contentSize, edge, axis, threshold) {
60
+ if (edge === 'start') return visibleRect[axis] <= threshold;
61
+ let dimension = $20fe89c1b1ab405a$var$dimensionForAxis(axis);
62
+ let distanceFromEnd = contentSize[dimension] - (visibleRect[axis] + visibleRect[dimension]);
63
+ return distanceFromEnd <= threshold;
64
+ }
65
+ function $20fe89c1b1ab405a$export$dcb80b718ca193d7(edge, axis, anchor, wasNearAnchorEdge, isScrolling, itemSizeChanged, contentSizeDelta, getLayoutInfo, previousVisibleRect, contentSize) {
66
+ let withTarget = (target)=>axis === 'x' ? new (0, $546350db2c358941$export$c79fc6492f3af13d)(target, previousVisibleRect.y, previousVisibleRect.width, previousVisibleRect.height) : new (0, $546350db2c358941$export$c79fc6492f3af13d)(previousVisibleRect.x, target, previousVisibleRect.width, previousVisibleRect.height);
67
+ if (anchor) {
68
+ let target = $20fe89c1b1ab405a$export$413719533c5919c9(anchor, axis, getLayoutInfo, previousVisibleRect, contentSize);
69
+ if (target != null) return withTarget(target);
70
+ }
71
+ if (wasNearAnchorEdge && !isScrolling && (!itemSizeChanged || contentSizeDelta > 0)) {
72
+ let target = withTarget($20fe89c1b1ab405a$export$c74597e403428eac(edge, axis, contentSize, previousVisibleRect));
73
+ return target.equals(previousVisibleRect) ? null : target;
74
+ }
75
+ return null;
76
+ }
77
+ class $20fe89c1b1ab405a$export$8ce0fdedc36cb6b {
78
+ /** Resets all tracked state, e.g. when the virtualizer's layout instance changes. */ reset() {
79
+ this.hasSnappedToEdge = false;
80
+ this.hadEstimatedVisibleItems = false;
81
+ this.wasNearAnchorEdge = false;
82
+ }
83
+ /**
84
+ * Captures the anchor from pre-layout view positions.
85
+ */ captureBeforeLayout(anchorInfo, preLayoutInfos, visibleRect) {
86
+ if (!anchorInfo) return null;
87
+ return $20fe89c1b1ab405a$export$69fb4c6de5f339b7(anchorInfo.edge, anchorInfo.axis, visibleRect, preLayoutInfos, anchorInfo.isAnchorable);
88
+ }
89
+ /**
90
+ * Runs the full post-layout decision: updates the tracked state for the next pass, and
91
+ * returns the resolved scroll target, or null if nothing should change.
92
+ */ resolveAfterLayout(options) {
93
+ let { anchorInfo: anchorInfo, anchor: anchor, postLayoutInfos: postLayoutInfos, previousVisibleRect: previousVisibleRect, previousContentSize: previousContentSize, contentSize: contentSize, itemSizeChanged: itemSizeChanged, isScrolling: isScrolling, getLayoutInfo: getLayoutInfo } = options;
94
+ if (!anchorInfo) return null;
95
+ // Read the previous pass's state into locals before any writes below overwrite it.
96
+ let wasSettlingLastPass = this.hadEstimatedVisibleItems;
97
+ let wasNearAnchorEdgeLastPass = this.wasNearAnchorEdge;
98
+ let hasEstimated = false;
99
+ for (let layoutInfo of postLayoutInfos.values())if (layoutInfo.estimatedSize) {
100
+ hasEstimated = true;
101
+ break;
102
+ }
103
+ this.hadEstimatedVisibleItems = hasEstimated;
104
+ // Don't recheck "near edge?" mid-resize because it could look like a scroll that never happened.
105
+ // Reuse the answer from before the resizing started.
106
+ if (!wasSettlingLastPass) this.wasNearAnchorEdge = $20fe89c1b1ab405a$export$f644bffd4937530e(previousVisibleRect, previousContentSize, anchorInfo.edge, anchorInfo.axis, anchorInfo.threshold);
107
+ if (previousVisibleRect.area === 0) return null;
108
+ let dimension = anchorInfo.axis === 'x' ? 'width' : 'height';
109
+ let contentSizeDelta = contentSize[dimension] - previousContentSize[dimension];
110
+ let isFirstAnchoredLayout = !this.hasSnappedToEdge;
111
+ this.hasSnappedToEdge = true;
112
+ // Only modify scroll when content actually changed (or this is the first layout, which always snaps)
113
+ if (!(isFirstAnchoredLayout || contentSizeDelta !== 0 || itemSizeChanged)) return null;
114
+ let wasNearAnchorEdge = isFirstAnchoredLayout || wasSettlingLastPass && wasNearAnchorEdgeLastPass || $20fe89c1b1ab405a$export$f644bffd4937530e(previousVisibleRect, previousContentSize, anchorInfo.edge, anchorInfo.axis, anchorInfo.threshold);
115
+ // A first-ever layout always snaps to the edge, even if the raw distance check says
116
+ // otherwise. Save that real decision here so later passes in this cascade reuse it.
117
+ if (!wasSettlingLastPass) this.wasNearAnchorEdge = wasNearAnchorEdge;
118
+ // Skip restoring to the captured anchor while still resizing because items above it are also still growing,
119
+ // and following it would fall short of the edge.
120
+ let effectiveAnchor = isFirstAnchoredLayout || wasSettlingLastPass && wasNearAnchorEdgeLastPass ? null : anchor;
121
+ return $20fe89c1b1ab405a$export$dcb80b718ca193d7(anchorInfo.edge, anchorInfo.axis, effectiveAnchor, wasNearAnchorEdge, isScrolling, itemSizeChanged, contentSizeDelta, getLayoutInfo, previousVisibleRect, contentSize);
122
+ }
123
+ constructor(){
124
+ this.hasSnappedToEdge = false;
125
+ this.hadEstimatedVisibleItems = false;
126
+ this.wasNearAnchorEdge = false;
127
+ }
128
+ }
129
+
130
+
131
+ export {$20fe89c1b1ab405a$export$413719533c5919c9 as computeScrollAnchorTarget, $20fe89c1b1ab405a$export$69fb4c6de5f339b7 as captureScrollAnchor, $20fe89c1b1ab405a$export$c74597e403428eac as getEdgeSnapTarget, $20fe89c1b1ab405a$export$f644bffd4937530e as isNearEdge, $20fe89c1b1ab405a$export$dcb80b718ca193d7 as resolveScrollAdjustment, $20fe89c1b1ab405a$export$8ce0fdedc36cb6b as ScrollAnchorTracker};
132
+ //# sourceMappingURL=ScrollAnchor.js.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.js.map"}