react-native-laminar 1.4.4 → 1.5.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 (109) hide show
  1. package/README.md +36 -6
  2. package/lib/commonjs/hooks/use-inline-auto-width.js +24 -1
  3. package/lib/commonjs/hooks/use-inline-auto-width.js.map +1 -1
  4. package/lib/commonjs/hooks/use-leading-animation.js +49 -0
  5. package/lib/commonjs/hooks/use-leading-animation.js.map +1 -0
  6. package/lib/commonjs/hooks/use-morph-motion.js +3 -1
  7. package/lib/commonjs/hooks/use-morph-motion.js.map +1 -1
  8. package/lib/commonjs/index.js +35 -6
  9. package/lib/commonjs/index.js.map +1 -1
  10. package/lib/commonjs/motion/preset-map.js +5 -0
  11. package/lib/commonjs/motion/preset-map.js.map +1 -1
  12. package/lib/commonjs/view/leading-element.js +37 -0
  13. package/lib/commonjs/view/leading-element.js.map +1 -0
  14. package/lib/commonjs/view/morph-viewport.js +26 -0
  15. package/lib/commonjs/view/morph-viewport.js.map +1 -1
  16. package/lib/commonjs/view/number-run.js +70 -22
  17. package/lib/commonjs/view/number-run.js.map +1 -1
  18. package/lib/commonjs/view/slot-shadow.js +77 -0
  19. package/lib/commonjs/view/slot-shadow.js.map +1 -0
  20. package/lib/commonjs/view/slots-run.js +111 -42
  21. package/lib/commonjs/view/slots-run.js.map +1 -1
  22. package/lib/commonjs/view/text-run.js +13 -24
  23. package/lib/commonjs/view/text-run.js.map +1 -1
  24. package/lib/commonjs/view/ticker-run.js +127 -0
  25. package/lib/commonjs/view/ticker-run.js.map +1 -0
  26. package/lib/module/hooks/use-inline-auto-width.js +24 -1
  27. package/lib/module/hooks/use-inline-auto-width.js.map +1 -1
  28. package/lib/module/hooks/use-leading-animation.js +44 -0
  29. package/lib/module/hooks/use-leading-animation.js.map +1 -0
  30. package/lib/module/hooks/use-morph-motion.js +3 -1
  31. package/lib/module/hooks/use-morph-motion.js.map +1 -1
  32. package/lib/module/index.js +35 -6
  33. package/lib/module/index.js.map +1 -1
  34. package/lib/module/motion/preset-map.js +5 -0
  35. package/lib/module/motion/preset-map.js.map +1 -1
  36. package/lib/module/view/leading-element.js +32 -0
  37. package/lib/module/view/leading-element.js.map +1 -0
  38. package/lib/module/view/morph-viewport.js +28 -2
  39. package/lib/module/view/morph-viewport.js.map +1 -1
  40. package/lib/module/view/number-run.js +70 -23
  41. package/lib/module/view/number-run.js.map +1 -1
  42. package/lib/module/view/slot-shadow.js +72 -0
  43. package/lib/module/view/slot-shadow.js.map +1 -0
  44. package/lib/module/view/slots-run.js +111 -42
  45. package/lib/module/view/slots-run.js.map +1 -1
  46. package/lib/module/view/text-run.js +14 -25
  47. package/lib/module/view/text-run.js.map +1 -1
  48. package/lib/module/view/ticker-run.js +121 -0
  49. package/lib/module/view/ticker-run.js.map +1 -0
  50. package/lib/typescript/commonjs/hooks/use-inline-auto-width.d.ts.map +1 -1
  51. package/lib/typescript/commonjs/hooks/use-leading-animation.d.ts +16 -0
  52. package/lib/typescript/commonjs/hooks/use-leading-animation.d.ts.map +1 -0
  53. package/lib/typescript/commonjs/hooks/use-morph-motion.d.ts +3 -2
  54. package/lib/typescript/commonjs/hooks/use-morph-motion.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/index.d.ts +1 -1
  56. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/motion/preset-map.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/types.d.ts +14 -2
  59. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/view/leading-element.d.ts +14 -0
  61. package/lib/typescript/commonjs/view/leading-element.d.ts.map +1 -0
  62. package/lib/typescript/commonjs/view/morph-viewport.d.ts +4 -1
  63. package/lib/typescript/commonjs/view/morph-viewport.d.ts.map +1 -1
  64. package/lib/typescript/commonjs/view/number-run.d.ts +6 -2
  65. package/lib/typescript/commonjs/view/number-run.d.ts.map +1 -1
  66. package/lib/typescript/commonjs/view/slot-shadow.d.ts +11 -0
  67. package/lib/typescript/commonjs/view/slot-shadow.d.ts.map +1 -0
  68. package/lib/typescript/commonjs/view/slots-run.d.ts +8 -3
  69. package/lib/typescript/commonjs/view/slots-run.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/view/text-run.d.ts.map +1 -1
  71. package/lib/typescript/commonjs/view/ticker-run.d.ts +21 -0
  72. package/lib/typescript/commonjs/view/ticker-run.d.ts.map +1 -0
  73. package/lib/typescript/module/hooks/use-inline-auto-width.d.ts.map +1 -1
  74. package/lib/typescript/module/hooks/use-leading-animation.d.ts +16 -0
  75. package/lib/typescript/module/hooks/use-leading-animation.d.ts.map +1 -0
  76. package/lib/typescript/module/hooks/use-morph-motion.d.ts +3 -2
  77. package/lib/typescript/module/hooks/use-morph-motion.d.ts.map +1 -1
  78. package/lib/typescript/module/index.d.ts +1 -1
  79. package/lib/typescript/module/index.d.ts.map +1 -1
  80. package/lib/typescript/module/motion/preset-map.d.ts.map +1 -1
  81. package/lib/typescript/module/types.d.ts +14 -2
  82. package/lib/typescript/module/types.d.ts.map +1 -1
  83. package/lib/typescript/module/view/leading-element.d.ts +14 -0
  84. package/lib/typescript/module/view/leading-element.d.ts.map +1 -0
  85. package/lib/typescript/module/view/morph-viewport.d.ts +4 -1
  86. package/lib/typescript/module/view/morph-viewport.d.ts.map +1 -1
  87. package/lib/typescript/module/view/number-run.d.ts +6 -2
  88. package/lib/typescript/module/view/number-run.d.ts.map +1 -1
  89. package/lib/typescript/module/view/slot-shadow.d.ts +11 -0
  90. package/lib/typescript/module/view/slot-shadow.d.ts.map +1 -0
  91. package/lib/typescript/module/view/slots-run.d.ts +8 -3
  92. package/lib/typescript/module/view/slots-run.d.ts.map +1 -1
  93. package/lib/typescript/module/view/text-run.d.ts.map +1 -1
  94. package/lib/typescript/module/view/ticker-run.d.ts +21 -0
  95. package/lib/typescript/module/view/ticker-run.d.ts.map +1 -0
  96. package/package.json +3 -1
  97. package/src/hooks/use-inline-auto-width.ts +33 -1
  98. package/src/hooks/use-leading-animation.ts +74 -0
  99. package/src/hooks/use-morph-motion.ts +10 -1
  100. package/src/index.tsx +44 -6
  101. package/src/motion/preset-map.ts +5 -0
  102. package/src/types.ts +17 -2
  103. package/src/view/leading-element.tsx +45 -0
  104. package/src/view/morph-viewport.tsx +39 -1
  105. package/src/view/number-run.tsx +94 -26
  106. package/src/view/slot-shadow.tsx +99 -0
  107. package/src/view/slots-run.tsx +163 -58
  108. package/src/view/text-run.tsx +14 -51
  109. package/src/view/ticker-run.tsx +167 -0
@@ -0,0 +1,167 @@
1
+ import React, { useRef, type ReactNode } from "react";
2
+ import { type StyleProp, type TextStyle, View } from "react-native";
3
+ import Animated from "react-native-reanimated";
4
+ import { useLeadingAnimation } from "../hooks/use-leading-animation";
5
+ import { useNumericLanes } from "../hooks/use-numeric-lanes";
6
+ import { isAsciiDigit } from "../model/display-units";
7
+ import type {
8
+ LaminarAlign,
9
+ LaminarShadow,
10
+ MotionRecipe,
11
+ } from "../types";
12
+ import { LeadingElement } from "./leading-element";
13
+
14
+ const rowStyle = {
15
+ flexDirection: "row",
16
+ alignItems: "center",
17
+ alignSelf: "flex-start",
18
+ } as const;
19
+
20
+ // The wrapper owns the changing layout box. Keeping the Text itself out of
21
+ // the layout transition prevents a newly rendered glyph from being clipped by
22
+ // the previous glyph's narrower bounds.
23
+ const glyphSlotStyle = {
24
+ alignSelf: "flex-start",
25
+ } as const;
26
+
27
+ const rowAlignStyles = {
28
+ left: { alignSelf: "flex-start" },
29
+ center: { alignSelf: "center" },
30
+ right: { alignSelf: "flex-end" },
31
+ } as const;
32
+
33
+ type TickerRunProps = {
34
+ readonly value: string;
35
+ readonly motionRecipe: MotionRecipe;
36
+ readonly align: LaminarAlign;
37
+ readonly fontSize?: number;
38
+ readonly textStyle?: StyleProp<TextStyle>;
39
+ readonly staggerMs: number;
40
+ readonly className?: string;
41
+ readonly leading?: ReactNode;
42
+ readonly leadingKey?: string | number;
43
+ readonly leadingGap?: number;
44
+ readonly ready?: boolean;
45
+ /** accepted for API compatibility; flow values stay crisp and unmasked */
46
+ readonly shadow?: LaminarShadow;
47
+ };
48
+
49
+ // Render the data-ticker behavior from the reference: stable inline lanes and
50
+ // crisp in-place replacements. There is deliberately no reel, vertical travel,
51
+ // opacity fade, or viewport mask in this renderer.
52
+ export const TickerRun = React.memo(
53
+ ({
54
+ value,
55
+ motionRecipe,
56
+ align,
57
+ textStyle,
58
+ className,
59
+ leading,
60
+ leadingKey,
61
+ leadingGap = 0,
62
+ ready = true,
63
+ }: Readonly<TickerRunProps>) => {
64
+ const hasDisplayedRef = useRef(false);
65
+ const displayedValueRef = useRef(value);
66
+ const displayedLeadingRef = useRef(leading);
67
+ const displayedLeadingKeyRef = useRef(leadingKey);
68
+ const displayedLeadingGapRef = useRef(leadingGap);
69
+ const shouldDisplayTarget = ready || !hasDisplayedRef.current;
70
+ const visibleValue = shouldDisplayTarget
71
+ ? value
72
+ : displayedValueRef.current;
73
+ const visibleLeading = shouldDisplayTarget
74
+ ? leading
75
+ : displayedLeadingRef.current;
76
+ const visibleLeadingKey = shouldDisplayTarget
77
+ ? leadingKey
78
+ : displayedLeadingKeyRef.current;
79
+ const visibleLeadingGap = shouldDisplayTarget
80
+ ? leadingGap
81
+ : displayedLeadingGapRef.current;
82
+
83
+ if (shouldDisplayTarget) {
84
+ hasDisplayedRef.current = true;
85
+ displayedValueRef.current = value;
86
+ displayedLeadingRef.current = leading;
87
+ displayedLeadingKeyRef.current = leadingKey;
88
+ displayedLeadingGapRef.current = leadingGap;
89
+ }
90
+
91
+ const {
92
+ elementEnterTransition,
93
+ elementExitTransition,
94
+ isLeadingChange,
95
+ isLeadingSwap,
96
+ } = useLeadingAnimation({
97
+ leading: visibleLeading,
98
+ leadingKey: visibleLeadingKey,
99
+ leadingGap: visibleLeadingGap,
100
+ motionRecipe,
101
+ });
102
+ const { units, leadLength } = useNumericLanes(visibleValue);
103
+ const lastValueRef = useRef(visibleValue);
104
+ const hasAnimatedRef = useRef(false);
105
+
106
+ if (visibleValue !== lastValueRef.current || isLeadingChange) {
107
+ hasAnimatedRef.current = true;
108
+ lastValueRef.current = visibleValue;
109
+ }
110
+
111
+ const hasAnimated = hasAnimatedRef.current;
112
+ const bodyLength = Math.max(units.length - leadLength, 0);
113
+
114
+ return (
115
+ <View style={[rowStyle, rowAlignStyles[align]]}>
116
+ <LeadingElement
117
+ leading={visibleLeading}
118
+ leadingKey={visibleLeadingKey}
119
+ leadingGap={visibleLeadingGap}
120
+ enterTransition={
121
+ hasAnimated ? motionRecipe.enterTransition : undefined
122
+ }
123
+ elementEnterTransition={
124
+ isLeadingSwap ? elementEnterTransition : undefined
125
+ }
126
+ exitTransition={motionRecipe.exitTransition}
127
+ elementExitTransition={elementExitTransition}
128
+ />
129
+ <Animated.View
130
+ layout={isLeadingChange ? motionRecipe.layoutTransition : undefined}
131
+ style={rowStyle}
132
+ >
133
+ {units.map((unit, index) => {
134
+ const inLead = index < leadLength;
135
+ const bodyIndex = index - leadLength;
136
+ const isDigit = !inLead && isAsciiDigit(unit);
137
+ const laneKey = inLead
138
+ ? `flow:lead:${index}`
139
+ : `flow:${bodyLength - 1 - bodyIndex}`;
140
+ const unitKey = isDigit ? laneKey : `${laneKey}:${unit}`;
141
+
142
+ return (
143
+ <Animated.View
144
+ // Keep each digit lane mounted across updates. Replacing the
145
+ // node here makes the glyph snap to its new width; a stable
146
+ // lane lets Reanimated interpolate that width and the
147
+ // positions of the following glyphs, which is the subtle
148
+ // horizontal movement in the reference ticker.
149
+ key={unitKey}
150
+ layout={
151
+ hasAnimated ? motionRecipe.layoutTransition : undefined
152
+ }
153
+ style={glyphSlotStyle}
154
+ >
155
+ <Animated.Text style={textStyle} className={className}>
156
+ {unit}
157
+ </Animated.Text>
158
+ </Animated.View>
159
+ );
160
+ })}
161
+ </Animated.View>
162
+ </View>
163
+ );
164
+ }
165
+ );
166
+
167
+ TickerRun.displayName = "TickerRun";