react-native-terra-ui 0.11.16 → 0.11.17

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 (21) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/lib/module/components/page-indicator/hooks/use-page-indicator-window.js +60 -0
  3. package/lib/module/components/page-indicator/hooks/use-page-indicator-window.js.map +1 -0
  4. package/lib/module/components/page-indicator/utils.js +60 -0
  5. package/lib/module/components/page-indicator/utils.js.map +1 -1
  6. package/lib/module/components/page-indicator/variants/DotIndicator/index.js +13 -2
  7. package/lib/module/components/page-indicator/variants/DotIndicator/index.js.map +1 -1
  8. package/lib/module/components/page-indicator/variants/PillIndicator/index.js +29 -7
  9. package/lib/module/components/page-indicator/variants/PillIndicator/index.js.map +1 -1
  10. package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-window.d.ts +34 -0
  11. package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-window.d.ts.map +1 -0
  12. package/lib/typescript/src/components/page-indicator/utils.d.ts +41 -0
  13. package/lib/typescript/src/components/page-indicator/utils.d.ts.map +1 -1
  14. package/lib/typescript/src/components/page-indicator/variants/DotIndicator/index.d.ts.map +1 -1
  15. package/lib/typescript/src/components/page-indicator/variants/PillIndicator/index.d.ts +1 -1
  16. package/lib/typescript/src/components/page-indicator/variants/PillIndicator/index.d.ts.map +1 -1
  17. package/package.json +1 -1
  18. package/src/components/page-indicator/hooks/use-page-indicator-window.ts +84 -0
  19. package/src/components/page-indicator/utils.ts +70 -0
  20. package/src/components/page-indicator/variants/DotIndicator/index.tsx +12 -1
  21. package/src/components/page-indicator/variants/PillIndicator/index.tsx +32 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.11.17](https://github.com/earthlin9/react-native-terra-ui/compare/v0.11.16...v0.11.17) (2026-09-20)
4
+
5
+ ### Bug Fixes
6
+
7
+ * **ui:** [page-indicator] mount only the dots the window can show ([#28](https://github.com/earthlin9/react-native-terra-ui/issues/28)) ([a11ee28](https://github.com/earthlin9/react-native-terra-ui/commit/a11ee2830598b0cd75f6ad88cc27f54563a93d00))
8
+
9
+ ### Documentation
10
+
11
+ * [button] [chip] document the tertiary variant/color ([97cdcfc](https://github.com/earthlin9/react-native-terra-ui/commit/97cdcfc485bcfd18cc41535a4c67c0dd013557ad))
12
+
3
13
  ## [0.11.16](https://github.com/earthling-std/react-native-terra-ui/compare/v0.11.15...v0.11.16) (2026-09-20)
4
14
 
5
15
  ### Features
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ import { useMemo, useState } from "react";
4
+ import { runOnJS, useAnimatedReaction, useSharedValue } from "react-native-reanimated";
5
+ import { isPageIndicatorSharedValue, pageIndicatorMountedRange } from "../utils.js";
6
+ /**
7
+ * The slice of dots a variant should mount, kept in step with the active page.
8
+ *
9
+ * Both variants clip to a window of `maxVisible` slots and scroll a track
10
+ * behind it, so all but a handful of dots are drawn outside the viewport. This
11
+ * is what lets them skip building those: the range covers the visible window
12
+ * plus a generous overscan, and moves only when the active page leaves it.
13
+ *
14
+ * **Driven by `progress`, not by `current`.** `current` may be a shared value
15
+ * for scroll-linked motion, in which case React never re-renders on a page
16
+ * change and a range derived from props would sit still while the track slid
17
+ * out from under it. `progress` is the one place both spellings of `current`
18
+ * have already been resolved into a single number.
19
+ *
20
+ * The reaction only reaches JS when the page moves *outside* the mounted range
21
+ * — not on every frame, and not on every page. A one-page step normally costs
22
+ * no render at all, because the neighbour was already mounted; a long jump
23
+ * costs one render per window it crosses rather than one per page.
24
+ *
25
+ * Known limit: during a jump of many pages the track slides faster than the
26
+ * range is re-rendered, so dots in the middle of that flight may not be mounted
27
+ * as they pass. They are identical inactive dots moving too fast to read, and
28
+ * the alternative — mounting every page between the two — is the cost this
29
+ * exists to avoid.
30
+ */
31
+ export function usePageIndicatorWindow(count, current, progress, slot, dotSize, maxVisible, activeMainSize = dotSize) {
32
+ const virtualized = count > maxVisible;
33
+
34
+ /*
35
+ Seeded from `current` so an indicator mounted straight onto a late page
36
+ draws that page's dots on its first frame rather than the first page's.
37
+ Zero for a shared value, which cannot be read during render — the reaction
38
+ below corrects it on the first frame that matters.
39
+ */
40
+ const [anchor, setAnchor] = useState(() => current === undefined || isPageIndicatorSharedValue(current) ? 0 : Math.round(current));
41
+ const window = useMemo(() => pageIndicatorMountedRange(count, anchor, slot, dotSize, maxVisible, activeMainSize), [count, anchor, slot, dotSize, maxVisible, activeMainSize]);
42
+
43
+ /*
44
+ The live range, on the UI thread, so the reaction can decide for itself
45
+ whether a page change matters. Reading React state from a worklet is not an
46
+ option, and crossing to JS to ask would defeat the point of asking.
47
+ */
48
+ const mounted = useSharedValue(window);
49
+ mounted.value = window;
50
+ useAnimatedReaction(() => Math.round(progress.value), page => {
51
+ if (!virtualized) return;
52
+ const {
53
+ start,
54
+ end
55
+ } = mounted.value;
56
+ if (page < start || page > end) runOnJS(setAnchor)(page);
57
+ }, [virtualized]);
58
+ return window;
59
+ }
60
+ //# sourceMappingURL=use-page-indicator-window.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useMemo","useState","runOnJS","useAnimatedReaction","useSharedValue","isPageIndicatorSharedValue","pageIndicatorMountedRange","usePageIndicatorWindow","count","current","progress","slot","dotSize","maxVisible","activeMainSize","virtualized","anchor","setAnchor","undefined","Math","round","window","mounted","value","page","start","end"],"sourceRoot":"../../../../../src","sources":["components/page-indicator/hooks/use-page-indicator-window.ts"],"mappings":";;AAAA,SAASA,OAAO,EAAEC,QAAQ,QAAQ,OAAO;AAEzC,SAASC,OAAO,EAAoBC,mBAAmB,EAAEC,cAAc,QAAQ,yBAAyB;AAExG,SAASC,0BAA0B,EAAEC,yBAAyB;AAS9D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,sBAAsBA,CACpCC,KAAa,EACbC,OAAiD,EACjDC,QAA6B,EAC7BC,IAAY,EACZC,OAAe,EACfC,UAAkB,EAClBC,cAAc,GAAGF,OAAO,EACH;EACrB,MAAMG,WAAW,GAAGP,KAAK,GAAGK,UAAU;;EAEtC;AACF;AACA;AACA;AACA;AACA;EACE,MAAM,CAACG,MAAM,EAAEC,SAAS,CAAC,GAAGhB,QAAQ,CAAC,MACnCQ,OAAO,KAAKS,SAAS,IAAIb,0BAA0B,CAACI,OAAO,CAAC,GAAG,CAAC,GAAGU,IAAI,CAACC,KAAK,CAACX,OAAO,CACvF,CAAC;EAED,MAAMY,MAAM,GAAGrB,OAAO,CACpB,MAAMM,yBAAyB,CAACE,KAAK,EAAEQ,MAAM,EAAEL,IAAI,EAAEC,OAAO,EAAEC,UAAU,EAAEC,cAAc,CAAC,EACzF,CAACN,KAAK,EAAEQ,MAAM,EAAEL,IAAI,EAAEC,OAAO,EAAEC,UAAU,EAAEC,cAAc,CAC3D,CAAC;;EAED;AACF;AACA;AACA;AACA;EACE,MAAMQ,OAAO,GAAGlB,cAAc,CAACiB,MAAM,CAAC;EACtCC,OAAO,CAACC,KAAK,GAAGF,MAAM;EAEtBlB,mBAAmB,CACjB,MAAMgB,IAAI,CAACC,KAAK,CAACV,QAAQ,CAACa,KAAK,CAAC,EAC/BC,IAAI,IAAK;IACR,IAAI,CAACT,WAAW,EAAE;IAClB,MAAM;MAAEU,KAAK;MAAEC;IAAI,CAAC,GAAGJ,OAAO,CAACC,KAAK;IACpC,IAAIC,IAAI,GAAGC,KAAK,IAAID,IAAI,GAAGE,GAAG,EAAExB,OAAO,CAACe,SAAS,CAAC,CAACO,IAAI,CAAC;EAC1D,CAAC,EACD,CAACT,WAAW,CACd,CAAC;EAED,OAAOM,MAAM;AACf","ignoreList":[]}
@@ -66,6 +66,21 @@ export function pageIndicatorViewportClipStyle(vertical, viewportMainSize, cross
66
66
  overflow: "hidden"
67
67
  };
68
68
  }
69
+
70
+ /**
71
+ * Holds the room of dots a variant chose not to mount.
72
+ *
73
+ * The track keeps its full width and keeps scrolling by the same amount, so
74
+ * skipped dots still have to occupy their slots — otherwise the mounted ones
75
+ * slide up the track and the window shows the wrong pages.
76
+ */
77
+ export function pageIndicatorSpacerStyle(vertical, mainSize) {
78
+ return vertical ? {
79
+ height: mainSize
80
+ } : {
81
+ width: mainSize
82
+ };
83
+ }
69
84
  function scaleCurveValue(curve, distance, tailScale) {
70
85
  "worklet";
71
86
 
@@ -124,4 +139,49 @@ export function pageIndicatorWindowScale(index, displayProgress, count, slot, do
124
139
  const positionInWindow = Math.min(Math.max(Math.round(index - visibleStart), 0), maxVisible - 1);
125
140
  return pageIndicatorWindowSlotScale(positionInWindow, activeSlot, maxVisible);
126
141
  }
142
+
143
+ /**
144
+ * Dots kept mounted beyond each edge of the visible window.
145
+ *
146
+ * One full window's worth. The window itself is `maxVisible` slots wide, so
147
+ * this leaves a whole screenful of slack on either side — enough that ordinary
148
+ * navigation (a swipe, a few pages at a time) never outruns the mounted range
149
+ * between the page change and React re-rendering with a new one.
150
+ */
151
+ export const PAGE_INDICATOR_WINDOW_OVERSCAN = 5;
152
+
153
+ /**
154
+ * Which dot indices are worth mounting, inclusive, for an active page of
155
+ * `anchor`.
156
+ *
157
+ * An indicator draws `maxVisible` slots and clips the rest, and every dot more
158
+ * than one page from the active one is drawn identically — same size, same
159
+ * scale, same opacity, same colour. Mounting one view per page therefore costs
160
+ * an animated style per page to render something nobody can see: at 500 pages
161
+ * that is 500 worklets for five visible dots, and it lands on every page
162
+ * change. Bounding what is mounted is what stops the indicator's cost scaling
163
+ * with the length of the thing it indicates.
164
+ *
165
+ * Derived from `computePageIndicatorWindowTranslate`, the same function that
166
+ * positions the track, so the mounted range and the visible window cannot
167
+ * disagree about where the window is — including at the ends, where the
168
+ * translate clamps and the window stops following the active page.
169
+ *
170
+ * Below the overflow threshold every dot is visible, so the range is the whole
171
+ * track and nothing changes.
172
+ */
173
+ export function pageIndicatorMountedRange(count, anchor, slot, dotSize, maxVisible, activeMainSize = dotSize, overscan = PAGE_INDICATOR_WINDOW_OVERSCAN) {
174
+ const last = Math.max(0, count - 1);
175
+ if (count <= maxVisible) return {
176
+ start: 0,
177
+ end: last
178
+ };
179
+ const translate = computePageIndicatorWindowTranslate(count, anchor, slot, dotSize, maxVisible, activeMainSize);
180
+ // The same expression `pageIndicatorWindowScale` reads the window from.
181
+ const visibleStart = -translate / slot;
182
+ return {
183
+ start: Math.max(0, Math.floor(visibleStart) - overscan),
184
+ end: Math.min(last, Math.ceil(visibleStart) + maxVisible - 1 + overscan)
185
+ };
186
+ }
127
187
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["PAGE_INDICATOR_SINGLE_PAGE_DURATION","PAGE_INDICATOR_MULTI_PAGE_DURATION_STEP","PAGE_INDICATOR_MULTI_PAGE_DURATION_MAX","DEFAULT_PAGE_INDICATOR_MAX_VISIBLE","EDGE_OUTWARD_SCALE","CENTERED_SCALE","WINDOW_TAIL_SCALE","pageIndicatorPageJumpDistance","from","to","Math","abs","pageIndicatorPageJumpDuration","distance","min","isPageIndicatorSharedValue","value","pageIndicatorAxisTranslateStyle","vertical","translate","transform","translateY","translateX","pageIndicatorViewportClipStyle","viewportMainSize","crossSize","height","undefined","width","overflow","scaleCurveValue","curve","tailScale","length","pageIndicatorWindowSlotScale","positionInWindow","activeSlot","maxVisible","dist","centerSlot","floor","pageIndicatorTrackMainSize","count","slot","dotSize","activeMainSize","max","pageIndicatorViewportMainSize","visible","computePageIndicatorWindowTranslate","progress","trackMainSize","activeCenter","offset","pageIndicatorWindowScale","index","displayProgress","visibleStart","round"],"sourceRoot":"../../../../src","sources":["components/page-indicator/utils.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["PAGE_INDICATOR_SINGLE_PAGE_DURATION","PAGE_INDICATOR_MULTI_PAGE_DURATION_STEP","PAGE_INDICATOR_MULTI_PAGE_DURATION_MAX","DEFAULT_PAGE_INDICATOR_MAX_VISIBLE","EDGE_OUTWARD_SCALE","CENTERED_SCALE","WINDOW_TAIL_SCALE","pageIndicatorPageJumpDistance","from","to","Math","abs","pageIndicatorPageJumpDuration","distance","min","isPageIndicatorSharedValue","value","pageIndicatorAxisTranslateStyle","vertical","translate","transform","translateY","translateX","pageIndicatorViewportClipStyle","viewportMainSize","crossSize","height","undefined","width","overflow","pageIndicatorSpacerStyle","mainSize","scaleCurveValue","curve","tailScale","length","pageIndicatorWindowSlotScale","positionInWindow","activeSlot","maxVisible","dist","centerSlot","floor","pageIndicatorTrackMainSize","count","slot","dotSize","activeMainSize","max","pageIndicatorViewportMainSize","visible","computePageIndicatorWindowTranslate","progress","trackMainSize","activeCenter","offset","pageIndicatorWindowScale","index","displayProgress","visibleStart","round","PAGE_INDICATOR_WINDOW_OVERSCAN","pageIndicatorMountedRange","anchor","overscan","last","start","end","ceil"],"sourceRoot":"../../../../src","sources":["components/page-indicator/utils.ts"],"mappings":"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","ignoreList":[]}
@@ -3,6 +3,7 @@
3
3
  import { useMemo } from "react";
4
4
  import Animated, { useAnimatedStyle } from "react-native-reanimated";
5
5
  import { usePageIndicatorCore } from "../../hooks/use-page-indicator-core.js";
6
+ import { usePageIndicatorWindow } from "../../hooks/use-page-indicator-window.js";
6
7
  import { PageIndicatorRoot } from "../../PageIndicatorRoot.js";
7
8
  import { computePageIndicatorWindowTranslate, DEFAULT_PAGE_INDICATOR_MAX_VISIBLE, PAGE_INDICATOR_SINGLE_PAGE_DURATION, pageIndicatorAxisTranslateStyle, pageIndicatorTrackMainSize, pageIndicatorViewportMainSize } from "../../utils.js";
8
9
  import { InactiveDot } from "./InactiveDot.js";
@@ -39,9 +40,19 @@ export function DotIndicator({
39
40
  const viewportMainSize = pageIndicatorViewportMainSize(count, slot, dotSize, maxVisible);
40
41
  const fullTrackMainSize = pageIndicatorTrackMainSize(count, slot, dotSize);
41
42
  const overflows = count > maxVisible;
43
+
44
+ /*
45
+ No spacers here, unlike the pill variant: an `InactiveDot` is absolutely
46
+ positioned from its own index, so a dot that is not mounted leaves a gap
47
+ exactly where it would have been and the rest do not move.
48
+ */
49
+ const {
50
+ start,
51
+ end
52
+ } = usePageIndicatorWindow(count, current, progress, slot, dotSize, maxVisible);
42
53
  const indices = useMemo(() => Array.from({
43
- length: count
44
- }, (_, index) => index), [count]);
54
+ length: end - start + 1
55
+ }, (_, offset) => start + offset), [start, end]);
45
56
  const trackStyle = useAnimatedStyle(() => {
46
57
  const translate = computePageIndicatorWindowTranslate(count, progress.value, slot, dotSize, maxVisible);
47
58
  return pageIndicatorAxisTranslateStyle(vertical, translate);
@@ -1 +1 @@
1
- {"version":3,"names":["useMemo","Animated","useAnimatedStyle","usePageIndicatorCore","PageIndicatorRoot","computePageIndicatorWindowTranslate","DEFAULT_PAGE_INDICATOR_MAX_VISIBLE","PAGE_INDICATOR_SINGLE_PAGE_DURATION","pageIndicatorAxisTranslateStyle","pageIndicatorTrackMainSize","pageIndicatorViewportMainSize","InactiveDot","LoadingOrbit","TravelingPill","DOT_INDICATOR_GEOMETRY","jsx","_jsx","jsxs","_jsxs","DotIndicator","count","current","duration","activeColor","inactiveColor","loadingColor","loading","vertical","maxVisible","style","colors","progress","dotSize","slot","crossSize","inactiveOpacity","viewportMainSize","fullTrackMainSize","overflows","indices","Array","from","length","_","index","trackStyle","translate","value","children","View","width","height","map","color","opacity"],"sourceRoot":"../../../../../../src","sources":["components/page-indicator/variants/DotIndicator/index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useMemo","Animated","useAnimatedStyle","usePageIndicatorCore","usePageIndicatorWindow","PageIndicatorRoot","computePageIndicatorWindowTranslate","DEFAULT_PAGE_INDICATOR_MAX_VISIBLE","PAGE_INDICATOR_SINGLE_PAGE_DURATION","pageIndicatorAxisTranslateStyle","pageIndicatorTrackMainSize","pageIndicatorViewportMainSize","InactiveDot","LoadingOrbit","TravelingPill","DOT_INDICATOR_GEOMETRY","jsx","_jsx","jsxs","_jsxs","DotIndicator","count","current","duration","activeColor","inactiveColor","loadingColor","loading","vertical","maxVisible","style","colors","progress","dotSize","slot","crossSize","inactiveOpacity","viewportMainSize","fullTrackMainSize","overflows","start","end","indices","Array","from","length","_","offset","trackStyle","translate","value","children","View","width","height","map","index","color","opacity"],"sourceRoot":"../../../../../../src","sources":["components/page-indicator/variants/DotIndicator/index.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,13 +1,15 @@
1
1
  "use strict";
2
2
 
3
+ import { View } from "react-native-unistyles/components/native/View";
3
4
  import { useMemo } from "react";
4
5
  import Animated, { useAnimatedStyle } from "react-native-reanimated";
5
6
  import { usePageIndicatorCore } from "../../hooks/use-page-indicator-core.js";
7
+ import { usePageIndicatorWindow } from "../../hooks/use-page-indicator-window.js";
6
8
  import { PageIndicatorRoot } from "../../PageIndicatorRoot.js";
7
- import { computePageIndicatorWindowTranslate, DEFAULT_PAGE_INDICATOR_MAX_VISIBLE, PAGE_INDICATOR_SINGLE_PAGE_DURATION, pageIndicatorAxisTranslateStyle, pageIndicatorTrackMainSize, pageIndicatorViewportMainSize } from "../../utils.js";
9
+ import { computePageIndicatorWindowTranslate, DEFAULT_PAGE_INDICATOR_MAX_VISIBLE, PAGE_INDICATOR_SINGLE_PAGE_DURATION, pageIndicatorAxisTranslateStyle, pageIndicatorSpacerStyle, pageIndicatorTrackMainSize, pageIndicatorViewportMainSize } from "../../utils.js";
8
10
  import { PillIndicatorDot } from "./PillIndicatorDot.js";
9
11
  import { PILL_INDICATOR_GEOMETRY, pillIndicatorJumpDisplayProgress } from "./utils.js";
10
- import { jsx as _jsx } from "react/jsx-runtime";
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
13
  export function PillIndicator({
12
14
  count,
13
15
  current = 0,
@@ -35,9 +37,25 @@ export function PillIndicator({
35
37
  const viewportMainSize = pageIndicatorViewportMainSize(count, slot, dotSize, maxVisible, activeWidth);
36
38
  const fullTrackMainSize = pageIndicatorTrackMainSize(count, slot, dotSize, activeWidth);
37
39
  const overflows = count > maxVisible;
40
+ const {
41
+ start,
42
+ end
43
+ } = usePageIndicatorWindow(count, current, progress, slot, dotSize, maxVisible, activeWidth);
38
44
  const indices = useMemo(() => Array.from({
39
- length: count
40
- }, (_, index) => index), [count]);
45
+ length: end - start + 1
46
+ }, (_, offset) => start + offset), [start, end]);
47
+
48
+ /*
49
+ The track is still `count` slots wide and still scrolls the same way, so the
50
+ dots that are not mounted have to leave their room behind. Each unmounted
51
+ dot occupies exactly one slot — it is inactive, and only the active dot is
52
+ ever wider than `dotSize` — so the space before the window is `start` slots
53
+ and the space after it is whatever is left.
54
+ Less one gap apiece, because the row's own `gap` supplies the space between
55
+ a spacer and the first dot beside it.
56
+ */
57
+ const leadMainSize = start * slot - gap;
58
+ const tailMainSize = (count - 1 - end) * slot - gap;
41
59
  const trackStyle = useAnimatedStyle(() => {
42
60
  const displayProgress = pillIndicatorJumpDisplayProgress(progress.value, pillJump.active.value, pillJump.from.value, pillJump.to.value, pillJump.t.value);
43
61
  const translate = computePageIndicatorWindowTranslate(count, displayProgress, slot, dotSize, maxVisible, activeWidth);
@@ -48,7 +66,7 @@ export function PillIndicator({
48
66
  vertical: vertical,
49
67
  viewportMainSize: viewportMainSize,
50
68
  overflows: overflows,
51
- children: /*#__PURE__*/_jsx(Animated.View, {
69
+ children: /*#__PURE__*/_jsxs(Animated.View, {
52
70
  style: [{
53
71
  flexDirection: vertical ? "column" : "row",
54
72
  alignItems: "center",
@@ -59,7 +77,9 @@ export function PillIndicator({
59
77
  width: fullTrackMainSize
60
78
  } : undefined)
61
79
  }, overflows ? trackStyle : null],
62
- children: indices.map(index => /*#__PURE__*/_jsx(PillIndicatorDot, {
80
+ children: [leadMainSize > 0 && /*#__PURE__*/_jsx(View, {
81
+ style: pageIndicatorSpacerStyle(vertical, leadMainSize)
82
+ }), indices.map(index => /*#__PURE__*/_jsx(PillIndicatorDot, {
63
83
  activeColor: colors.activeColor,
64
84
  inactiveColor: colors.inactiveColor,
65
85
  count: count,
@@ -69,7 +89,9 @@ export function PillIndicator({
69
89
  vertical: vertical,
70
90
  overflows: overflows,
71
91
  maxVisible: maxVisible
72
- }, index))
92
+ }, index)), tailMainSize > 0 && /*#__PURE__*/_jsx(View, {
93
+ style: pageIndicatorSpacerStyle(vertical, tailMainSize)
94
+ })]
73
95
  })
74
96
  });
75
97
  }
@@ -1 +1 @@
1
- {"version":3,"names":["useMemo","Animated","useAnimatedStyle","usePageIndicatorCore","PageIndicatorRoot","computePageIndicatorWindowTranslate","DEFAULT_PAGE_INDICATOR_MAX_VISIBLE","PAGE_INDICATOR_SINGLE_PAGE_DURATION","pageIndicatorAxisTranslateStyle","pageIndicatorTrackMainSize","pageIndicatorViewportMainSize","PillIndicatorDot","PILL_INDICATOR_GEOMETRY","pillIndicatorJumpDisplayProgress","jsx","_jsx","PillIndicator","count","current","duration","activeColor","inactiveColor","vertical","maxVisible","style","colors","pillJump","progress","dotSize","gap","slot","activeWidth","viewportMainSize","fullTrackMainSize","overflows","indices","Array","from","length","_","index","trackStyle","displayProgress","value","active","to","t","translate","children","View","flexDirection","alignItems","height","width","undefined","map"],"sourceRoot":"../../../../../../src","sources":["components/page-indicator/variants/PillIndicator/index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useMemo","Animated","useAnimatedStyle","usePageIndicatorCore","usePageIndicatorWindow","PageIndicatorRoot","computePageIndicatorWindowTranslate","DEFAULT_PAGE_INDICATOR_MAX_VISIBLE","PAGE_INDICATOR_SINGLE_PAGE_DURATION","pageIndicatorAxisTranslateStyle","pageIndicatorSpacerStyle","pageIndicatorTrackMainSize","pageIndicatorViewportMainSize","PillIndicatorDot","PILL_INDICATOR_GEOMETRY","pillIndicatorJumpDisplayProgress","jsx","_jsx","jsxs","_jsxs","PillIndicator","count","current","duration","activeColor","inactiveColor","vertical","maxVisible","style","colors","pillJump","progress","dotSize","gap","slot","activeWidth","viewportMainSize","fullTrackMainSize","overflows","start","end","indices","Array","from","length","_","offset","leadMainSize","tailMainSize","trackStyle","displayProgress","value","active","to","t","translate","children","View","flexDirection","alignItems","height","width","undefined","map","index"],"sourceRoot":"../../../../../../src","sources":["components/page-indicator/variants/PillIndicator/index.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,34 @@
1
+ import { type SharedValue } from "react-native-reanimated";
2
+ export interface PageIndicatorWindow {
3
+ /** First dot index to mount, inclusive. */
4
+ start: number;
5
+ /** Last dot index to mount, inclusive. */
6
+ end: number;
7
+ }
8
+ /**
9
+ * The slice of dots a variant should mount, kept in step with the active page.
10
+ *
11
+ * Both variants clip to a window of `maxVisible` slots and scroll a track
12
+ * behind it, so all but a handful of dots are drawn outside the viewport. This
13
+ * is what lets them skip building those: the range covers the visible window
14
+ * plus a generous overscan, and moves only when the active page leaves it.
15
+ *
16
+ * **Driven by `progress`, not by `current`.** `current` may be a shared value
17
+ * for scroll-linked motion, in which case React never re-renders on a page
18
+ * change and a range derived from props would sit still while the track slid
19
+ * out from under it. `progress` is the one place both spellings of `current`
20
+ * have already been resolved into a single number.
21
+ *
22
+ * The reaction only reaches JS when the page moves *outside* the mounted range
23
+ * — not on every frame, and not on every page. A one-page step normally costs
24
+ * no render at all, because the neighbour was already mounted; a long jump
25
+ * costs one render per window it crosses rather than one per page.
26
+ *
27
+ * Known limit: during a jump of many pages the track slides faster than the
28
+ * range is re-rendered, so dots in the middle of that flight may not be mounted
29
+ * as they pass. They are identical inactive dots moving too fast to read, and
30
+ * the alternative — mounting every page between the two — is the cost this
31
+ * exists to avoid.
32
+ */
33
+ export declare function usePageIndicatorWindow(count: number, current: number | SharedValue<number> | undefined, progress: SharedValue<number>, slot: number, dotSize: number, maxVisible: number, activeMainSize?: number): PageIndicatorWindow;
34
+ //# sourceMappingURL=use-page-indicator-window.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-page-indicator-window.d.ts","sourceRoot":"","sources":["../../../../../../src/components/page-indicator/hooks/use-page-indicator-window.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,WAAW,EAAuC,MAAM,yBAAyB,CAAC;AAIzG,MAAM,WAAW,mBAAmB;IAClC,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,EACjD,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,EAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,cAAc,SAAU,GACvB,mBAAmB,CAqCrB"}
@@ -27,10 +27,51 @@ export declare function pageIndicatorAxisTranslateStyle(vertical: boolean, trans
27
27
  };
28
28
  /** Clips an overflowing track to the visible viewport along the main axis. */
29
29
  export declare function pageIndicatorViewportClipStyle(vertical: boolean, viewportMainSize: number, crossSize?: number): ViewStyle;
30
+ /**
31
+ * Holds the room of dots a variant chose not to mount.
32
+ *
33
+ * The track keeps its full width and keeps scrolling by the same amount, so
34
+ * skipped dots still have to occupy their slots — otherwise the mounted ones
35
+ * slide up the track and the window shows the wrong pages.
36
+ */
37
+ export declare function pageIndicatorSpacerStyle(vertical: boolean, mainSize: number): ViewStyle;
30
38
  /** Scale for a slot inside the window given the active slot. */
31
39
  export declare function pageIndicatorWindowSlotScale(positionInWindow: number, activeSlot: number, maxVisible: number): number;
32
40
  export declare function pageIndicatorTrackMainSize(count: number, slot: number, dotSize: number, activeMainSize?: number): number;
33
41
  export declare function pageIndicatorViewportMainSize(count: number, slot: number, dotSize: number, maxVisible: number, activeMainSize?: number): number;
34
42
  export declare function computePageIndicatorWindowTranslate(count: number, progress: number, slot: number, dotSize: number, maxVisible: number, activeMainSize?: number): number;
35
43
  export declare function pageIndicatorWindowScale(index: number, displayProgress: number, count: number, slot: number, dotSize: number, maxVisible: number, activeMainSize?: number): number;
44
+ /**
45
+ * Dots kept mounted beyond each edge of the visible window.
46
+ *
47
+ * One full window's worth. The window itself is `maxVisible` slots wide, so
48
+ * this leaves a whole screenful of slack on either side — enough that ordinary
49
+ * navigation (a swipe, a few pages at a time) never outruns the mounted range
50
+ * between the page change and React re-rendering with a new one.
51
+ */
52
+ export declare const PAGE_INDICATOR_WINDOW_OVERSCAN = 5;
53
+ /**
54
+ * Which dot indices are worth mounting, inclusive, for an active page of
55
+ * `anchor`.
56
+ *
57
+ * An indicator draws `maxVisible` slots and clips the rest, and every dot more
58
+ * than one page from the active one is drawn identically — same size, same
59
+ * scale, same opacity, same colour. Mounting one view per page therefore costs
60
+ * an animated style per page to render something nobody can see: at 500 pages
61
+ * that is 500 worklets for five visible dots, and it lands on every page
62
+ * change. Bounding what is mounted is what stops the indicator's cost scaling
63
+ * with the length of the thing it indicates.
64
+ *
65
+ * Derived from `computePageIndicatorWindowTranslate`, the same function that
66
+ * positions the track, so the mounted range and the visible window cannot
67
+ * disagree about where the window is — including at the ends, where the
68
+ * translate clamps and the window stops following the active page.
69
+ *
70
+ * Below the overflow threshold every dot is visible, so the range is the whole
71
+ * track and nothing changes.
72
+ */
73
+ export declare function pageIndicatorMountedRange(count: number, anchor: number, slot: number, dotSize: number, maxVisible: number, activeMainSize?: number, overscan?: number): {
74
+ start: number;
75
+ end: number;
76
+ };
36
77
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/components/page-indicator/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,uDAAuD;AACvD,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD,0EAA0E;AAC1E,eAAO,MAAM,uCAAuC,MAAM,CAAC;AAE3D,wDAAwD;AACxD,eAAO,MAAM,sCAAsC,MAAM,CAAC;AAE1D,0EAA0E;AAC1E,eAAO,MAAM,kCAAkC,IAAI,CAAC;AAMpD,+CAA+C;AAC/C,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAQ9E;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,GAClC,KAAK,IAAI,WAAW,CAAC,MAAM,CAAC,CAE9B;AAED,wEAAwE;AACxE,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM;;;;;;EAKnF;AAED,8EAA8E;AAC9E,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,OAAO,EACjB,gBAAgB,EAAE,MAAM,EACxB,SAAS,CAAC,EAAE,MAAM,GACjB,SAAS,CAcX;AAQD,gEAAgE;AAChE,wBAAgB,4BAA4B,CAC1C,gBAAgB,EAAE,MAAM,EACxB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,MAAM,CA2BR;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,cAAc,SAAU,GACvB,MAAM,CAER;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,cAAc,SAAU,GACvB,MAAM,CAGR;AAED,wBAAgB,mCAAmC,CACjD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,cAAc,SAAU,GACvB,MAAM,CAUR;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,cAAc,SAAU,GACvB,MAAM,CAiBR"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/components/page-indicator/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,uDAAuD;AACvD,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD,0EAA0E;AAC1E,eAAO,MAAM,uCAAuC,MAAM,CAAC;AAE3D,wDAAwD;AACxD,eAAO,MAAM,sCAAsC,MAAM,CAAC;AAE1D,0EAA0E;AAC1E,eAAO,MAAM,kCAAkC,IAAI,CAAC;AAMpD,+CAA+C;AAC/C,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAQ9E;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,GAClC,KAAK,IAAI,WAAW,CAAC,MAAM,CAAC,CAE9B;AAED,wEAAwE;AACxE,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM;;;;;;EAKnF;AAED,8EAA8E;AAC9E,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,OAAO,EACjB,gBAAgB,EAAE,MAAM,EACxB,SAAS,CAAC,EAAE,MAAM,GACjB,SAAS,CAcX;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,CAEvF;AAQD,gEAAgE;AAChE,wBAAgB,4BAA4B,CAC1C,gBAAgB,EAAE,MAAM,EACxB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,MAAM,CA2BR;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,cAAc,SAAU,GACvB,MAAM,CAER;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,cAAc,SAAU,GACvB,MAAM,CAGR;AAED,wBAAgB,mCAAmC,CACjD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,cAAc,SAAU,GACvB,MAAM,CAUR;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,cAAc,SAAU,GACvB,MAAM,CAiBR;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,cAAc,SAAU,EACxB,QAAQ,SAAiC,GACxC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAmBhC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/page-indicator/variants/DotIndicator/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAgB/C,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,iCAAiC;IACjC,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,gCAAgC;IAChC,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,OAAW,EACX,QAA8C,EAC9C,WAAW,EACX,aAAa,EACb,YAAY,EACZ,OAAe,EACf,QAAgB,EAChB,UAA+C,EAC/C,KAAK,GACN,EAAE,iBAAiB,+BAiEnB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/page-indicator/variants/DotIndicator/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAiB/C,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,iCAAiC;IACjC,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,gCAAgC;IAChC,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,OAAW,EACX,QAA8C,EAC9C,WAAW,EACX,aAAa,EACb,YAAY,EACZ,OAAe,EACf,QAAgB,EAChB,UAA+C,EAC/C,KAAK,GACN,EAAE,iBAAiB,+BA2EnB"}
@@ -1,4 +1,4 @@
1
- import type { StyleProp, ViewStyle } from "react-native";
1
+ import { type StyleProp, type ViewStyle } from "react-native";
2
2
  import type { SharedValue } from "react-native-reanimated";
3
3
  import type { ColorToken } from "../../../../theme/types";
4
4
  export interface PillIndicatorProps {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/page-indicator/variants/PillIndicator/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAc/C,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,iCAAiC;IACjC,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAW,EACX,QAA8C,EAC9C,WAAW,EACX,aAAa,EACb,QAAgB,EAChB,UAA+C,EAC/C,KAAK,GACN,EAAE,kBAAkB,+BAyEpB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/page-indicator/variants/PillIndicator/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAQ,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAgB/C,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,iCAAiC;IACjC,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAW,EACX,QAA8C,EAC9C,WAAW,EACX,aAAa,EACb,QAAgB,EAChB,UAA+C,EAC/C,KAAK,GACN,EAAE,kBAAkB,+BAqGpB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-terra-ui",
3
- "version": "0.11.16",
3
+ "version": "0.11.17",
4
4
  "description": "Themeable React Native UI components built on react-native-unistyles",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -0,0 +1,84 @@
1
+ import { useMemo, useState } from "react";
2
+
3
+ import { runOnJS, type SharedValue, useAnimatedReaction, useSharedValue } from "react-native-reanimated";
4
+
5
+ import { isPageIndicatorSharedValue, pageIndicatorMountedRange } from "../utils";
6
+
7
+ export interface PageIndicatorWindow {
8
+ /** First dot index to mount, inclusive. */
9
+ start: number;
10
+ /** Last dot index to mount, inclusive. */
11
+ end: number;
12
+ }
13
+
14
+ /**
15
+ * The slice of dots a variant should mount, kept in step with the active page.
16
+ *
17
+ * Both variants clip to a window of `maxVisible` slots and scroll a track
18
+ * behind it, so all but a handful of dots are drawn outside the viewport. This
19
+ * is what lets them skip building those: the range covers the visible window
20
+ * plus a generous overscan, and moves only when the active page leaves it.
21
+ *
22
+ * **Driven by `progress`, not by `current`.** `current` may be a shared value
23
+ * for scroll-linked motion, in which case React never re-renders on a page
24
+ * change and a range derived from props would sit still while the track slid
25
+ * out from under it. `progress` is the one place both spellings of `current`
26
+ * have already been resolved into a single number.
27
+ *
28
+ * The reaction only reaches JS when the page moves *outside* the mounted range
29
+ * — not on every frame, and not on every page. A one-page step normally costs
30
+ * no render at all, because the neighbour was already mounted; a long jump
31
+ * costs one render per window it crosses rather than one per page.
32
+ *
33
+ * Known limit: during a jump of many pages the track slides faster than the
34
+ * range is re-rendered, so dots in the middle of that flight may not be mounted
35
+ * as they pass. They are identical inactive dots moving too fast to read, and
36
+ * the alternative — mounting every page between the two — is the cost this
37
+ * exists to avoid.
38
+ */
39
+ export function usePageIndicatorWindow(
40
+ count: number,
41
+ current: number | SharedValue<number> | undefined,
42
+ progress: SharedValue<number>,
43
+ slot: number,
44
+ dotSize: number,
45
+ maxVisible: number,
46
+ activeMainSize = dotSize
47
+ ): PageIndicatorWindow {
48
+ const virtualized = count > maxVisible;
49
+
50
+ /*
51
+ Seeded from `current` so an indicator mounted straight onto a late page
52
+ draws that page's dots on its first frame rather than the first page's.
53
+ Zero for a shared value, which cannot be read during render — the reaction
54
+ below corrects it on the first frame that matters.
55
+ */
56
+ const [anchor, setAnchor] = useState(() =>
57
+ current === undefined || isPageIndicatorSharedValue(current) ? 0 : Math.round(current)
58
+ );
59
+
60
+ const window = useMemo(
61
+ () => pageIndicatorMountedRange(count, anchor, slot, dotSize, maxVisible, activeMainSize),
62
+ [count, anchor, slot, dotSize, maxVisible, activeMainSize]
63
+ );
64
+
65
+ /*
66
+ The live range, on the UI thread, so the reaction can decide for itself
67
+ whether a page change matters. Reading React state from a worklet is not an
68
+ option, and crossing to JS to ask would defeat the point of asking.
69
+ */
70
+ const mounted = useSharedValue(window);
71
+ mounted.value = window;
72
+
73
+ useAnimatedReaction(
74
+ () => Math.round(progress.value),
75
+ (page) => {
76
+ if (!virtualized) return;
77
+ const { start, end } = mounted.value;
78
+ if (page < start || page > end) runOnJS(setAnchor)(page);
79
+ },
80
+ [virtualized]
81
+ );
82
+
83
+ return window;
84
+ }
@@ -73,6 +73,17 @@ export function pageIndicatorViewportClipStyle(
73
73
  };
74
74
  }
75
75
 
76
+ /**
77
+ * Holds the room of dots a variant chose not to mount.
78
+ *
79
+ * The track keeps its full width and keeps scrolling by the same amount, so
80
+ * skipped dots still have to occupy their slots — otherwise the mounted ones
81
+ * slide up the track and the window shows the wrong pages.
82
+ */
83
+ export function pageIndicatorSpacerStyle(vertical: boolean, mainSize: number): ViewStyle {
84
+ return vertical ? { height: mainSize } : { width: mainSize };
85
+ }
86
+
76
87
  function scaleCurveValue(curve: readonly number[], distance: number, tailScale: number): number {
77
88
  "worklet";
78
89
  if (distance < curve.length) return curve[distance] ?? tailScale;
@@ -178,3 +189,62 @@ export function pageIndicatorWindowScale(
178
189
 
179
190
  return pageIndicatorWindowSlotScale(positionInWindow, activeSlot, maxVisible);
180
191
  }
192
+
193
+ /**
194
+ * Dots kept mounted beyond each edge of the visible window.
195
+ *
196
+ * One full window's worth. The window itself is `maxVisible` slots wide, so
197
+ * this leaves a whole screenful of slack on either side — enough that ordinary
198
+ * navigation (a swipe, a few pages at a time) never outruns the mounted range
199
+ * between the page change and React re-rendering with a new one.
200
+ */
201
+ export const PAGE_INDICATOR_WINDOW_OVERSCAN = 5;
202
+
203
+ /**
204
+ * Which dot indices are worth mounting, inclusive, for an active page of
205
+ * `anchor`.
206
+ *
207
+ * An indicator draws `maxVisible` slots and clips the rest, and every dot more
208
+ * than one page from the active one is drawn identically — same size, same
209
+ * scale, same opacity, same colour. Mounting one view per page therefore costs
210
+ * an animated style per page to render something nobody can see: at 500 pages
211
+ * that is 500 worklets for five visible dots, and it lands on every page
212
+ * change. Bounding what is mounted is what stops the indicator's cost scaling
213
+ * with the length of the thing it indicates.
214
+ *
215
+ * Derived from `computePageIndicatorWindowTranslate`, the same function that
216
+ * positions the track, so the mounted range and the visible window cannot
217
+ * disagree about where the window is — including at the ends, where the
218
+ * translate clamps and the window stops following the active page.
219
+ *
220
+ * Below the overflow threshold every dot is visible, so the range is the whole
221
+ * track and nothing changes.
222
+ */
223
+ export function pageIndicatorMountedRange(
224
+ count: number,
225
+ anchor: number,
226
+ slot: number,
227
+ dotSize: number,
228
+ maxVisible: number,
229
+ activeMainSize = dotSize,
230
+ overscan = PAGE_INDICATOR_WINDOW_OVERSCAN
231
+ ): { start: number; end: number } {
232
+ const last = Math.max(0, count - 1);
233
+ if (count <= maxVisible) return { start: 0, end: last };
234
+
235
+ const translate = computePageIndicatorWindowTranslate(
236
+ count,
237
+ anchor,
238
+ slot,
239
+ dotSize,
240
+ maxVisible,
241
+ activeMainSize
242
+ );
243
+ // The same expression `pageIndicatorWindowScale` reads the window from.
244
+ const visibleStart = -translate / slot;
245
+
246
+ return {
247
+ start: Math.max(0, Math.floor(visibleStart) - overscan),
248
+ end: Math.min(last, Math.ceil(visibleStart) + maxVisible - 1 + overscan),
249
+ };
250
+ }
@@ -6,6 +6,7 @@ import Animated, { useAnimatedStyle } from "react-native-reanimated";
6
6
 
7
7
  import type { ColorToken } from "#theme/types";
8
8
  import { usePageIndicatorCore } from "../../hooks/use-page-indicator-core";
9
+ import { usePageIndicatorWindow } from "../../hooks/use-page-indicator-window";
9
10
  import { PageIndicatorRoot } from "../../PageIndicatorRoot";
10
11
  import {
11
12
  computePageIndicatorWindowTranslate,
@@ -71,7 +72,17 @@ export function DotIndicator({
71
72
  const fullTrackMainSize = pageIndicatorTrackMainSize(count, slot, dotSize);
72
73
  const overflows = count > maxVisible;
73
74
 
74
- const indices = useMemo(() => Array.from({ length: count }, (_, index) => index), [count]);
75
+ /*
76
+ No spacers here, unlike the pill variant: an `InactiveDot` is absolutely
77
+ positioned from its own index, so a dot that is not mounted leaves a gap
78
+ exactly where it would have been and the rest do not move.
79
+ */
80
+ const { start, end } = usePageIndicatorWindow(count, current, progress, slot, dotSize, maxVisible);
81
+
82
+ const indices = useMemo(
83
+ () => Array.from({ length: end - start + 1 }, (_, offset) => start + offset),
84
+ [start, end]
85
+ );
75
86
 
76
87
  const trackStyle = useAnimatedStyle(() => {
77
88
  const translate = computePageIndicatorWindowTranslate(count, progress.value, slot, dotSize, maxVisible);
@@ -1,17 +1,19 @@
1
1
  import { useMemo } from "react";
2
- import type { StyleProp, ViewStyle } from "react-native";
2
+ import { type StyleProp, View, type ViewStyle } from "react-native";
3
3
 
4
4
  import type { SharedValue } from "react-native-reanimated";
5
5
  import Animated, { useAnimatedStyle } from "react-native-reanimated";
6
6
 
7
7
  import type { ColorToken } from "#theme/types";
8
8
  import { usePageIndicatorCore } from "../../hooks/use-page-indicator-core";
9
+ import { usePageIndicatorWindow } from "../../hooks/use-page-indicator-window";
9
10
  import { PageIndicatorRoot } from "../../PageIndicatorRoot";
10
11
  import {
11
12
  computePageIndicatorWindowTranslate,
12
13
  DEFAULT_PAGE_INDICATOR_MAX_VISIBLE,
13
14
  PAGE_INDICATOR_SINGLE_PAGE_DURATION,
14
15
  pageIndicatorAxisTranslateStyle,
16
+ pageIndicatorSpacerStyle,
15
17
  pageIndicatorTrackMainSize,
16
18
  pageIndicatorViewportMainSize,
17
19
  } from "../../utils";
@@ -62,7 +64,33 @@ export function PillIndicator({
62
64
  const fullTrackMainSize = pageIndicatorTrackMainSize(count, slot, dotSize, activeWidth);
63
65
  const overflows = count > maxVisible;
64
66
 
65
- const indices = useMemo(() => Array.from({ length: count }, (_, index) => index), [count]);
67
+ const { start, end } = usePageIndicatorWindow(
68
+ count,
69
+ current,
70
+ progress,
71
+ slot,
72
+ dotSize,
73
+ maxVisible,
74
+ activeWidth
75
+ );
76
+
77
+ const indices = useMemo(
78
+ () => Array.from({ length: end - start + 1 }, (_, offset) => start + offset),
79
+ [start, end]
80
+ );
81
+
82
+ /*
83
+ The track is still `count` slots wide and still scrolls the same way, so the
84
+ dots that are not mounted have to leave their room behind. Each unmounted
85
+ dot occupies exactly one slot — it is inactive, and only the active dot is
86
+ ever wider than `dotSize` — so the space before the window is `start` slots
87
+ and the space after it is whatever is left.
88
+
89
+ Less one gap apiece, because the row's own `gap` supplies the space between
90
+ a spacer and the first dot beside it.
91
+ */
92
+ const leadMainSize = start * slot - gap;
93
+ const tailMainSize = (count - 1 - end) * slot - gap;
66
94
 
67
95
  const trackStyle = useAnimatedStyle(() => {
68
96
  const displayProgress = pillIndicatorJumpDisplayProgress(
@@ -107,6 +135,7 @@ export function PillIndicator({
107
135
  overflows ? trackStyle : null,
108
136
  ]}
109
137
  >
138
+ {leadMainSize > 0 && <View style={pageIndicatorSpacerStyle(vertical, leadMainSize)} />}
110
139
  {indices.map((index) => (
111
140
  <PillIndicatorDot
112
141
  key={index}
@@ -121,6 +150,7 @@ export function PillIndicator({
121
150
  maxVisible={maxVisible}
122
151
  />
123
152
  ))}
153
+ {tailMainSize > 0 && <View style={pageIndicatorSpacerStyle(vertical, tailMainSize)} />}
124
154
  </Animated.View>
125
155
  </PageIndicatorRoot>
126
156
  );