react-native-laminar 1.4.2 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/hooks/use-inline-auto-width.js +105 -18
- package/lib/commonjs/hooks/use-inline-auto-width.js.map +1 -1
- package/lib/commonjs/index.js +5 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/view/morph-viewport.js +2 -0
- package/lib/commonjs/view/morph-viewport.js.map +1 -1
- package/lib/commonjs/view/text-run.js +30 -12
- package/lib/commonjs/view/text-run.js.map +1 -1
- package/lib/module/hooks/use-inline-auto-width.js +106 -19
- package/lib/module/hooks/use-inline-auto-width.js.map +1 -1
- package/lib/module/index.js +5 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/view/morph-viewport.js +2 -0
- package/lib/module/view/morph-viewport.js.map +1 -1
- package/lib/module/view/text-run.js +30 -12
- package/lib/module/view/text-run.js.map +1 -1
- package/lib/typescript/commonjs/hooks/use-inline-auto-width.d.ts +2 -0
- package/lib/typescript/commonjs/hooks/use-inline-auto-width.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/view/morph-viewport.d.ts +3 -2
- package/lib/typescript/commonjs/view/morph-viewport.d.ts.map +1 -1
- package/lib/typescript/commonjs/view/text-run.d.ts +2 -1
- package/lib/typescript/commonjs/view/text-run.d.ts.map +1 -1
- package/lib/typescript/module/hooks/use-inline-auto-width.d.ts +2 -0
- package/lib/typescript/module/hooks/use-inline-auto-width.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/view/morph-viewport.d.ts +3 -2
- package/lib/typescript/module/view/morph-viewport.d.ts.map +1 -1
- package/lib/typescript/module/view/text-run.d.ts +2 -1
- package/lib/typescript/module/view/text-run.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/hooks/use-inline-auto-width.ts +160 -18
- package/src/index.tsx +10 -1
- package/src/view/morph-viewport.tsx +7 -2
- package/src/view/text-run.tsx +49 -15
package/src/index.tsx
CHANGED
|
@@ -79,7 +79,13 @@ export const Laminar = React.memo(function Laminar({
|
|
|
79
79
|
textStyle,
|
|
80
80
|
variant,
|
|
81
81
|
]);
|
|
82
|
-
const {
|
|
82
|
+
const {
|
|
83
|
+
captureLayout,
|
|
84
|
+
captureVisibleLayout,
|
|
85
|
+
animatedWidthStyle,
|
|
86
|
+
isReady: isAutoSizeReady,
|
|
87
|
+
shouldMeasure,
|
|
88
|
+
} =
|
|
83
89
|
useInlineAutoWidth({
|
|
84
90
|
enabled: autoSize,
|
|
85
91
|
driveToWidth: motionRecipe.driveNumber,
|
|
@@ -103,9 +109,11 @@ export const Laminar = React.memo(function Laminar({
|
|
|
103
109
|
align={align}
|
|
104
110
|
containerStyle={containerStyle}
|
|
105
111
|
animatedWidthStyle={animatedWidthStyle}
|
|
112
|
+
onVisibleLayout={autoSize ? captureVisibleLayout : undefined}
|
|
106
113
|
measurement={
|
|
107
114
|
shouldMeasure ? (
|
|
108
115
|
<View
|
|
116
|
+
key={measurementKey}
|
|
109
117
|
onLayout={captureLayout}
|
|
110
118
|
style={{ flexDirection: "row", alignSelf: "flex-start" }}
|
|
111
119
|
>
|
|
@@ -157,6 +165,7 @@ export const Laminar = React.memo(function Laminar({
|
|
|
157
165
|
leading={resolvedLeading}
|
|
158
166
|
leadingKey={resolvedLeadingKey}
|
|
159
167
|
leadingGap={leadingGap}
|
|
168
|
+
ready={!autoSize || isAutoSizeReady}
|
|
160
169
|
textStyle={textStyle}
|
|
161
170
|
className={className}
|
|
162
171
|
/>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
|
-
import type { StyleProp, ViewStyle } from "react-native";
|
|
2
|
+
import type { LayoutChangeEvent, StyleProp, ViewStyle } from "react-native";
|
|
3
3
|
import { View } from "react-native";
|
|
4
4
|
import Animated from "react-native-reanimated";
|
|
5
5
|
import type { LaminarAlign } from "../types";
|
|
@@ -52,6 +52,7 @@ type MorphViewportProps = {
|
|
|
52
52
|
readonly align: LaminarAlign;
|
|
53
53
|
readonly containerStyle?: StyleProp<ViewStyle>;
|
|
54
54
|
readonly animatedWidthStyle?: React.ComponentProps<typeof Animated.View>["style"];
|
|
55
|
+
readonly onVisibleLayout?: (event: LayoutChangeEvent) => void;
|
|
55
56
|
readonly measurement?: React.ReactNode;
|
|
56
57
|
readonly children: React.ReactNode;
|
|
57
58
|
};
|
|
@@ -64,6 +65,7 @@ export const MorphViewport = React.memo(
|
|
|
64
65
|
align,
|
|
65
66
|
containerStyle,
|
|
66
67
|
animatedWidthStyle,
|
|
68
|
+
onVisibleLayout,
|
|
67
69
|
measurement,
|
|
68
70
|
children,
|
|
69
71
|
}: MorphViewportProps) => {
|
|
@@ -91,7 +93,10 @@ export const MorphViewport = React.memo(
|
|
|
91
93
|
>
|
|
92
94
|
{measurement}
|
|
93
95
|
</View>
|
|
94
|
-
<Animated.View
|
|
96
|
+
<Animated.View
|
|
97
|
+
onLayout={onVisibleLayout}
|
|
98
|
+
style={[resolvedViewportStyle, animatedWidthStyle]}
|
|
99
|
+
>
|
|
95
100
|
{children}
|
|
96
101
|
</Animated.View>
|
|
97
102
|
</>
|
package/src/view/text-run.tsx
CHANGED
|
@@ -17,6 +17,7 @@ type TextRunProps = {
|
|
|
17
17
|
readonly leading?: ReactNode;
|
|
18
18
|
readonly leadingKey?: string | number;
|
|
19
19
|
readonly leadingGap?: number;
|
|
20
|
+
readonly ready?: boolean;
|
|
20
21
|
readonly textStyle?: StyleProp<TextStyle>;
|
|
21
22
|
readonly className?: string;
|
|
22
23
|
};
|
|
@@ -30,25 +31,58 @@ export const TextRun = React.memo(
|
|
|
30
31
|
leading,
|
|
31
32
|
leadingKey,
|
|
32
33
|
leadingGap = 0,
|
|
34
|
+
ready = true,
|
|
33
35
|
textStyle,
|
|
34
36
|
className,
|
|
35
37
|
}: TextRunProps) => {
|
|
36
38
|
// namespace ids per instance so repeated strings do not collide
|
|
37
39
|
const scopeId = useId();
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
40
|
+
const hasDisplayedRef = useRef(false);
|
|
41
|
+
const displayedValueRef = useRef(value);
|
|
42
|
+
const displayedLeadingRef = useRef(leading);
|
|
43
|
+
const displayedLeadingKeyRef = useRef(leadingKey);
|
|
44
|
+
const displayedLeadingGapRef = useRef(leadingGap);
|
|
45
|
+
const shouldDisplayTarget = ready || !hasDisplayedRef.current;
|
|
46
|
+
const visibleValue = shouldDisplayTarget
|
|
47
|
+
? value
|
|
48
|
+
: displayedValueRef.current;
|
|
49
|
+
const visibleLeading = shouldDisplayTarget
|
|
50
|
+
? leading
|
|
51
|
+
: displayedLeadingRef.current;
|
|
52
|
+
const visibleLeadingKey = shouldDisplayTarget
|
|
53
|
+
? leadingKey
|
|
54
|
+
: displayedLeadingKeyRef.current;
|
|
55
|
+
const visibleLeadingGap = shouldDisplayTarget
|
|
56
|
+
? leadingGap
|
|
57
|
+
: displayedLeadingGapRef.current;
|
|
58
|
+
|
|
59
|
+
if (shouldDisplayTarget) {
|
|
60
|
+
hasDisplayedRef.current = true;
|
|
61
|
+
displayedValueRef.current = value;
|
|
62
|
+
displayedLeadingRef.current = leading;
|
|
63
|
+
displayedLeadingKeyRef.current = leadingKey;
|
|
64
|
+
displayedLeadingGapRef.current = leadingGap;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const lastValueRef = useRef(visibleValue);
|
|
68
|
+
const lastLeadingPresenceRef = useRef(Boolean(visibleLeading));
|
|
69
|
+
const lastLeadingKeyRef = useRef(visibleLeadingKey);
|
|
41
70
|
const hasAnimatedRef = useRef(false);
|
|
42
71
|
const isLeadingSwap =
|
|
43
|
-
Boolean(
|
|
72
|
+
Boolean(visibleLeading) &&
|
|
44
73
|
lastLeadingPresenceRef.current &&
|
|
45
|
-
|
|
74
|
+
visibleLeadingKey !== lastLeadingKeyRef.current;
|
|
46
75
|
// update the worklet flag after commit so render never writes a shared value
|
|
47
76
|
const leadingSwapProgress = useSharedValue(0);
|
|
48
77
|
useLayoutEffect(() => {
|
|
49
78
|
leadingSwapProgress.value = isLeadingSwap ? 1 : 0;
|
|
50
79
|
}, [isLeadingSwap, leadingSwapProgress]);
|
|
51
|
-
const glyphs = useTextGlyphs(
|
|
80
|
+
const glyphs = useTextGlyphs(
|
|
81
|
+
visibleValue,
|
|
82
|
+
scopeId,
|
|
83
|
+
visibleLeading,
|
|
84
|
+
visibleLeadingKey
|
|
85
|
+
);
|
|
52
86
|
const elementEnterTransition = useMemo(
|
|
53
87
|
() =>
|
|
54
88
|
createLeadingEnterTransition({
|
|
@@ -62,11 +96,11 @@ export const TextRun = React.memo(
|
|
|
62
96
|
createLeadingExitTransition({
|
|
63
97
|
durationMs: motionRecipe.durationMs,
|
|
64
98
|
easing: motionRecipe.easing,
|
|
65
|
-
leadingGap,
|
|
99
|
+
leadingGap: visibleLeadingGap,
|
|
66
100
|
scale: leadingSwapProgress,
|
|
67
101
|
}),
|
|
68
102
|
[
|
|
69
|
-
|
|
103
|
+
visibleLeadingGap,
|
|
70
104
|
leadingSwapProgress,
|
|
71
105
|
motionRecipe.durationMs,
|
|
72
106
|
motionRecipe.easing,
|
|
@@ -75,14 +109,14 @@ export const TextRun = React.memo(
|
|
|
75
109
|
|
|
76
110
|
// mark the first value as settled and later changes as eligible for motion
|
|
77
111
|
if (
|
|
78
|
-
|
|
79
|
-
Boolean(
|
|
80
|
-
|
|
112
|
+
visibleValue !== lastValueRef.current ||
|
|
113
|
+
Boolean(visibleLeading) !== lastLeadingPresenceRef.current ||
|
|
114
|
+
visibleLeadingKey !== lastLeadingKeyRef.current
|
|
81
115
|
) {
|
|
82
116
|
hasAnimatedRef.current = true;
|
|
83
|
-
lastValueRef.current =
|
|
84
|
-
lastLeadingPresenceRef.current = Boolean(
|
|
85
|
-
lastLeadingKeyRef.current =
|
|
117
|
+
lastValueRef.current = visibleValue;
|
|
118
|
+
lastLeadingPresenceRef.current = Boolean(visibleLeading);
|
|
119
|
+
lastLeadingKeyRef.current = visibleLeadingKey;
|
|
86
120
|
}
|
|
87
121
|
|
|
88
122
|
const hasAnimated = hasAnimatedRef.current;
|
|
@@ -103,7 +137,7 @@ export const TextRun = React.memo(
|
|
|
103
137
|
}
|
|
104
138
|
exitTransition={motionRecipe.exitTransition}
|
|
105
139
|
elementExitTransition={elementExitTransition}
|
|
106
|
-
leadingGap={
|
|
140
|
+
leadingGap={visibleLeadingGap}
|
|
107
141
|
align={align}
|
|
108
142
|
textStyle={textStyle}
|
|
109
143
|
className={className}
|