react-native-laminar 1.0.0 → 1.0.2

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 (2) hide show
  1. package/README.md +213 -2
  2. package/package.json +7 -2
package/README.md CHANGED
@@ -4,8 +4,219 @@ React Native morphing text and number animation.
4
4
 
5
5
  ```tsx
6
6
  import { Laminar } from "react-native-laminar";
7
+ ```
8
+
9
+ `Laminar` animates changes to a string or number value. Matching characters hold position across transitions. New characters enter. Removed characters exit. Numbers align from the right so place-value columns stay in stable lanes.
10
+
11
+ ---
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install react-native-laminar
17
+ ```
18
+
19
+ Laminar requires [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/). Follow its installation guide before using this package.
20
+
21
+ ---
22
+
23
+ ## Quick Start
24
+
25
+ ### Text
26
+
27
+ ```tsx
28
+ import React, { useState } from "react";
29
+ import { Button, View } from "react-native";
30
+ import { Laminar } from "react-native-laminar";
31
+
32
+ export default function Example() {
33
+ const [word, setWord] = useState("Laminar");
34
+
35
+ return (
36
+ <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
37
+ <Laminar
38
+ text={word}
39
+ fontSize={40}
40
+ style={{ color: "#000000", fontFamily: "System" }}
41
+ />
42
+ <Button
43
+ title="Morph"
44
+ onPress={() => setWord((w) => (w === "Laminar" ? "Linear" : "Laminar"))}
45
+ />
46
+ </View>
47
+ );
48
+ }
49
+ ```
50
+
51
+ ### Number
7
52
 
8
- <Laminar text="$1,234.56" variant="number" />;
53
+ ```tsx
54
+ import React, { useState } from "react";
55
+ import { Button, View } from "react-native";
56
+ import { Laminar } from "react-native-laminar";
57
+
58
+ export default function Counter() {
59
+ const [value, setValue] = useState("$1,234");
60
+
61
+ return (
62
+ <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
63
+ <Laminar
64
+ text={value}
65
+ variant="number"
66
+ fontSize={40}
67
+ animationPreset="snappy"
68
+ style={{ color: "#000000", fontVariant: ["tabular-nums"] }}
69
+ />
70
+ <Button title="Change" onPress={() => setValue("$12,345")} />
71
+ </View>
72
+ );
73
+ }
9
74
  ```
10
75
 
11
- The package source lives in `src`. The demo app in the repository imports from the package boundary instead of package internals.
76
+ ### Inside a button with auto-sizing
77
+
78
+ ```tsx
79
+ <Pressable style={{ paddingHorizontal: 24, paddingVertical: 12, borderRadius: 36, backgroundColor: "#007aff" }}>
80
+ <Laminar
81
+ text={label}
82
+ autoSize
83
+ fontSize={18}
84
+ style={{ color: "#ffffff" }}
85
+ />
86
+ </Pressable>
87
+ ```
88
+
89
+ `autoSize` (default `true`) animates the outer container width toward the measured final text width. The button grows and shrinks without layout feedback loops.
90
+
91
+ ### Standalone word without auto-sizing
92
+
93
+ ```tsx
94
+ <Laminar
95
+ text={word}
96
+ autoSize={false}
97
+ fontSize={40}
98
+ style={{ color: "#000000" }}
99
+ />
100
+ ```
101
+
102
+ Use `autoSize={false}` when the parent already defines the space and you only want the glyph animation.
103
+
104
+ ---
105
+
106
+ ## Props
107
+
108
+ ```ts
109
+ type LaminarProps = {
110
+ text: string | number;
111
+ variant?: "text" | "number";
112
+ fontSize?: number;
113
+ color?: string;
114
+ style?: StyleProp<TextStyle>;
115
+ containerStyle?: StyleProp<ViewStyle>;
116
+ fontStyle?: StyleProp<TextStyle>;
117
+ animationDuration?: number;
118
+ animationPreset?: "default" | "smooth" | "snappy" | "bouncy";
119
+ stagger?: number;
120
+ autoSize?: boolean;
121
+ clipToBounds?: boolean;
122
+ };
123
+ ```
124
+
125
+ | Prop | Default | Description |
126
+ | ------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------- |
127
+ | `text` | Required | The value to display. Numbers are converted to strings internally. |
128
+ | `variant` | `"text"` | `"text"` uses LCS glyph reconciliation. `"number"` uses right-aligned digit lanes. |
129
+ | `fontSize` | Undefined | Convenience prop merged into the text style. |
130
+ | `color` | Undefined | Convenience prop merged into the text style. |
131
+ | `style` | Undefined | Text style applied after `fontSize` and `color`. |
132
+ | `containerStyle` | Undefined | Style for the outer viewport shell. Use for placement and alignment. |
133
+ | `fontStyle` | Undefined | Additional text style merged before `style`. |
134
+ | `animationDuration` | Preset default | Duration override in milliseconds. |
135
+ | `animationPreset` | `"default"` for text, `"snappy"` for number | Named motion recipe. |
136
+ | `stagger` | `0.02` | Delay in seconds between numeric lane animations. |
137
+ | `autoSize` | `true` | Animate the outer width to the measured final text width. |
138
+ | `clipToBounds` | `false` | Clip animated overflow to the viewport bounds. |
139
+
140
+ ---
141
+
142
+ ## Animation Presets
143
+
144
+ | Preset | Character | Default Duration |
145
+ | --------- | -------------------------- | ---------------- |
146
+ | `default` | Smooth cubic-bezier timing | 380ms |
147
+ | `smooth` | Spring with no bounce | 400ms |
148
+ | `snappy` | Spring with light bounce | 350ms |
149
+ | `bouncy` | Spring with more bounce | 500ms |
150
+
151
+ ```tsx
152
+ <Laminar text={word} animationPreset="smooth" />
153
+ <Laminar text={count} variant="number" animationPreset="snappy" />
154
+
155
+ // Override duration
156
+ <Laminar text={word} animationPreset="default" animationDuration={520} />
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Best Practices
162
+
163
+ **Keep `text` stable — don't change `key`.**
164
+
165
+ ```tsx
166
+ // Wrong — forces remount, loses glyph identity
167
+ <Laminar key={label} text={label} />
168
+
169
+ // Correct
170
+ <Laminar text={label} />
171
+ ```
172
+
173
+ **Choose `autoSize` based on layout role.**
174
+
175
+ - Buttons, chips, badges → `autoSize={true}`
176
+ - Standalone centered words → `autoSize={false}`
177
+ - Fixed-width counters → `autoSize={false}`
178
+
179
+ **Match formatting across renders for numbers.**
180
+
181
+ Switching between `$1,234` and `1234` mid-session breaks lane alignment. Keep the format consistent.
182
+
183
+ ```tsx
184
+ // Consistent formatting keeps lanes stable
185
+ <Laminar text={`$${value.toLocaleString()}`} variant="number" />
186
+ ```
187
+
188
+ **Duration guidelines.**
189
+
190
+ | Range | Best for |
191
+ | ------------- | ------------------------------- |
192
+ | 180ms – 260ms | Rapid counters, live data |
193
+ | 300ms – 450ms | Normal labels, button text |
194
+ | 500ms – 700ms | Expressive or teaching moments |
195
+
196
+ ---
197
+
198
+ ## Troubleshooting
199
+
200
+ **Animations not playing.**
201
+ 1. Verify Reanimated is installed and configured.
202
+ 2. Check that the component is not remounting due to a changing `key`.
203
+ 3. Check that updates are not arriving faster than the animation duration.
204
+
205
+ **Glyphs clipped.**
206
+ 1. Set `clipToBounds={false}` if overflow should be visible.
207
+ 2. Verify the parent does not have `overflow: "hidden"` set.
208
+
209
+ **Numbers misaligned or jumpy.**
210
+ 1. Use `variant="number"`.
211
+ 2. Keep formatting consistent between renders.
212
+ 3. Add `fontVariant: ["tabular-nums"]` if your font supports it.
213
+
214
+ **Auto-sizing not working.**
215
+ 1. Check that `autoSize` is not explicitly set to `false`.
216
+ 2. Verify the parent allows its child to define the width — a fixed-width parent overrides the animated child width.
217
+
218
+ ---
219
+
220
+ ## License
221
+
222
+ See the repository root for license information.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-laminar",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "A React Native component for morphing text and numbers with character-level identity.",
5
5
  "keywords": [
6
6
  "react-native",
@@ -54,7 +54,12 @@
54
54
  "output": "lib",
55
55
  "targets": [
56
56
  "commonjs",
57
- ["module", { "esm": true }],
57
+ [
58
+ "module",
59
+ {
60
+ "esm": true
61
+ }
62
+ ],
58
63
  "typescript"
59
64
  ]
60
65
  }