rnnovaui 0.9.6 → 0.9.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rnnovaui",
3
- "version": "0.9.6",
3
+ "version": "0.9.8",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "react-native": "src/index.js",
@@ -12,24 +12,19 @@ export const UIAccordionContent = memo(function UIAccordionContent({
12
12
  testID,
13
13
  }) {
14
14
  const accordion = useUIAccordion();
15
-
16
15
  const item = useUIAccordionItem();
17
16
 
18
17
  const [contentHeight, setContentHeight] = useState(0);
19
18
 
20
- const heightAnimation = useRef(new Animated.Value(item.open ? 1 : 0)).current;
21
-
22
- const opacityAnimation = useRef(
23
- new Animated.Value(item.open ? 1 : 0),
24
- ).current;
19
+ const animation = useRef(new Animated.Value(item.open ? 1 : 0)).current;
25
20
 
26
21
  /*
27
22
  * ------------------------------------------------
28
- * MEASURE REAL CONTENT HEIGHT
23
+ * MEASURE REAL CONTENT
29
24
  * ------------------------------------------------
30
25
  */
31
26
 
32
- const handleLayout = (event) => {
27
+ const handleMeasureLayout = (event) => {
33
28
  const height = Math.ceil(event.nativeEvent.layout.height);
34
29
 
35
30
  if (height > 0 && height !== contentHeight) {
@@ -39,51 +34,36 @@ export const UIAccordionContent = memo(function UIAccordionContent({
39
34
 
40
35
  /*
41
36
  * ------------------------------------------------
42
- * ANIMATION
37
+ * OPEN / CLOSE
43
38
  * ------------------------------------------------
44
39
  */
45
40
 
46
41
  useEffect(() => {
47
- /*
48
- * If content has not been measured yet,
49
- * don't try to animate to an unknown height.
50
- */
51
42
  if (contentHeight <= 0) {
52
43
  return;
53
44
  }
54
45
 
55
46
  if (!accordion.animated) {
56
- heightAnimation.setValue(item.open ? 1 : 0);
57
-
58
- opacityAnimation.setValue(item.open ? 1 : 0);
47
+ animation.setValue(item.open ? 1 : 0);
59
48
 
60
49
  return;
61
50
  }
62
51
 
63
- Animated.parallel([
64
- Animated.timing(heightAnimation, {
65
- toValue: item.open ? 1 : 0,
66
-
67
- duration: accordion.animationDuration,
52
+ Animated.timing(animation, {
53
+ toValue: item.open ? 1 : 0,
68
54
 
69
- useNativeDriver: false,
70
- }),
55
+ duration: accordion.animationDuration,
71
56
 
72
- Animated.timing(opacityAnimation, {
73
- toValue: item.open ? 1 : 0,
57
+ easing: undefined,
74
58
 
75
- duration: Math.min(accordion.animationDuration, 160),
76
-
77
- useNativeDriver: true,
78
- }),
79
- ]).start();
59
+ useNativeDriver: false,
60
+ }).start();
80
61
  }, [
81
62
  item.open,
82
63
  contentHeight,
83
64
  accordion.animated,
84
65
  accordion.animationDuration,
85
- heightAnimation,
86
- opacityAnimation,
66
+ animation,
87
67
  ]);
88
68
 
89
69
  /*
@@ -92,51 +72,94 @@ export const UIAccordionContent = memo(function UIAccordionContent({
92
72
  * ------------------------------------------------
93
73
  */
94
74
 
95
- const animatedHeight = heightAnimation.interpolate({
75
+ const animatedHeight = animation.interpolate({
96
76
  inputRange: [0, 1],
97
77
 
98
78
  outputRange: [0, contentHeight],
79
+
80
+ extrapolate: "clamp",
99
81
  });
100
82
 
101
83
  /*
102
84
  * ------------------------------------------------
103
- * CONTENT
104
- * ------------------------------------------------
85
+ * MEASUREMENT CONTENT
105
86
  *
106
- * The inner content remains mounted all the time.
107
- * Therefore backend content can have any height.
87
+ * This is always rendered.
88
+ *
89
+ * It does NOT participate in normal layout.
90
+ * Therefore it cannot cause accordion shaking.
91
+ * ------------------------------------------------
108
92
  */
109
93
 
110
94
  return (
111
- <Animated.View
112
- testID={testID}
113
- style={[
114
- styles.container,
115
-
116
- {
117
- height: animatedHeight,
118
-
119
- opacity: opacityAnimation,
120
- },
121
-
122
- style,
123
- ]}
124
- >
125
- <View style={[styles.inner, innerStyle]} onLayout={handleLayout}>
126
- {children}
95
+ <View style={styles.root}>
96
+ <View pointerEvents="none" style={styles.measureContainer}>
97
+ <View
98
+ style={[styles.measureContent, innerStyle]}
99
+ onLayout={handleMeasureLayout}
100
+ >
101
+ {children}
102
+ </View>
127
103
  </View>
128
- </Animated.View>
104
+
105
+ <Animated.View
106
+ testID={testID}
107
+ style={[
108
+ styles.animatedContainer,
109
+
110
+ {
111
+ height: animatedHeight,
112
+ },
113
+
114
+ style,
115
+ ]}
116
+ >
117
+ <View style={[styles.visibleContent, innerStyle]}>{children}</View>
118
+ </Animated.View>
119
+ </View>
129
120
  );
130
121
  });
131
122
 
132
123
  const styles = StyleSheet.create({
133
- container: {
124
+ root: {
125
+ width: "100%",
126
+ },
127
+
128
+ /*
129
+ * Hidden measurement layer.
130
+ *
131
+ * It remains available so onLayout can always
132
+ * calculate the real content height.
133
+ */
134
+ measureContainer: {
135
+ position: "absolute",
136
+
137
+ left: 0,
138
+ right: 0,
139
+
140
+ top: 0,
141
+
142
+ opacity: 0,
143
+
144
+ zIndex: -1,
145
+
146
+ pointerEvents: "none",
147
+ },
148
+
149
+ measureContent: {
150
+ width: "100%",
151
+ },
152
+
153
+ /*
154
+ * Visible animated layer.
155
+ */
156
+ animatedContainer: {
134
157
  width: "100%",
135
158
 
136
159
  overflow: "hidden",
137
160
  },
138
161
 
139
- inner: {
162
+ visibleContent: {
140
163
  width: "100%",
141
164
  },
142
165
  });
@@ -35,26 +35,28 @@ export const UITabsSizes = {
35
35
  export const UITabsAnimations = {
36
36
  none: "none",
37
37
 
38
+ /* Heading / indicator animations */
39
+ indicatorSlide: "indicatorSlide",
40
+ indicatorSpring: "indicatorSpring",
41
+ indicatorFade: "indicatorFade",
42
+ indicatorScale: "indicatorScale",
43
+ indicatorFadeScale: "indicatorFadeScale",
44
+
45
+ /* Heading press animations */
46
+ pressScale: "pressScale",
47
+ pressOpacity: "pressOpacity",
48
+
49
+ /* These remain available for future explicit use,
50
+ * but are NOT used by UITabs.Content.
51
+ */
38
52
  fade: "fade",
39
53
  scale: "scale",
40
54
  fadeScale: "fadeScale",
41
-
42
55
  slide: "slide",
43
56
  slideFade: "slideFade",
44
57
  slideScale: "slideScale",
45
-
46
- spring: "spring",
47
- springScale: "springScale",
48
- springSlide: "springSlide",
49
-
50
58
  bounce: "bounce",
51
59
  pulse: "pulse",
52
-
53
- indicatorSlide: "indicatorSlide",
54
- indicatorSpring: "indicatorSpring",
55
- indicatorFade: "indicatorFade",
56
- indicatorScale: "indicatorScale",
57
- indicatorFadeScale: "indicatorFadeScale",
58
60
  };
59
61
 
60
62
  export const UITabsAnimationPresets = {
@@ -63,18 +65,13 @@ export const UITabsAnimationPresets = {
63
65
  duration: 0,
64
66
  },
65
67
 
66
- subtle: {
67
- animation: "fade",
68
- duration: 160,
69
- },
70
-
71
68
  smooth: {
72
- animation: "fade",
69
+ animation: "indicatorSlide",
73
70
  duration: 220,
74
71
  },
75
72
 
76
73
  snappy: {
77
- animation: "fade",
74
+ animation: "indicatorSlide",
78
75
  duration: 160,
79
76
  },
80
77
 
@@ -84,7 +81,7 @@ export const UITabsAnimationPresets = {
84
81
  },
85
82
 
86
83
  bouncy: {
87
- animation: "bounce",
84
+ animation: "indicatorSpring",
88
85
  duration: 420,
89
86
  },
90
87
  };
@@ -165,7 +162,7 @@ function getAnimationConfig(animation, animationPreset, animationDuration) {
165
162
  return {
166
163
  animation: animation || "indicatorSlide",
167
164
 
168
- duration: animationDuration ?? 240,
165
+ duration: animationDuration ?? 220,
169
166
  };
170
167
  }
171
168
 
@@ -186,25 +183,39 @@ const UITabsRoot = forwardRef(function UITabs(
186
183
 
187
184
  variant = "default",
188
185
 
186
+ /*
187
+ * IMPORTANT:
188
+ *
189
+ * animated controls ONLY:
190
+ * - tab heading press
191
+ * - active indicator
192
+ *
193
+ * It does NOT animate Content.
194
+ */
189
195
  animated = true,
190
196
 
191
197
  animation = "indicatorSlide",
192
198
 
193
199
  animationPreset,
194
200
 
195
- animationDuration = 240,
201
+ animationDuration = 220,
196
202
 
197
203
  animationEasing = "easeOut",
198
204
 
199
205
  indicatorAnimation,
200
206
 
201
207
  /*
202
- * IMPORTANT:
203
- * Content defaults to fade.
204
- * It does NOT slide by default.
208
+ * Kept for API compatibility.
209
+ *
210
+ * Content animation is intentionally
211
+ * disabled in this component.
205
212
  */
206
- contentAnimation = "fade",
213
+ contentAnimation = "none",
207
214
 
215
+ /*
216
+ * Kept for compatibility.
217
+ * Content never slides.
218
+ */
208
219
  directionAware = false,
209
220
 
210
221
  spring = {
@@ -349,6 +360,10 @@ const UITabsRoot = forwardRef(function UITabs(
349
360
  scrollToTab(currentValue);
350
361
  }, [currentValue, scrollToTab]);
351
362
 
363
+ /*
364
+ * Direction is kept only for API compatibility.
365
+ * Content never uses it.
366
+ */
352
367
  const getDirection = useCallback(
353
368
  (tabValue) => {
354
369
  if (!directionAware) {
@@ -388,6 +403,9 @@ const UITabsRoot = forwardRef(function UITabs(
388
403
 
389
404
  animated,
390
405
 
406
+ /*
407
+ * Heading animation only.
408
+ */
391
409
  animation: resolvedAnimation,
392
410
 
393
411
  animationDuration: resolvedDuration,
@@ -396,9 +414,12 @@ const UITabsRoot = forwardRef(function UITabs(
396
414
 
397
415
  indicatorAnimation: indicatorAnimation || resolvedAnimation,
398
416
 
399
- contentAnimation,
417
+ /*
418
+ * Always none for Content.
419
+ */
420
+ contentAnimation: "none",
400
421
 
401
- directionAware,
422
+ directionAware: false,
402
423
 
403
424
  spring,
404
425
 
@@ -448,8 +469,6 @@ const UITabsRoot = forwardRef(function UITabs(
448
469
  resolvedDuration,
449
470
  animationEasing,
450
471
  indicatorAnimation,
451
- contentAnimation,
452
- directionAware,
453
472
  spring,
454
473
  resolvedActiveColor,
455
474
  resolvedInactiveColor,
@@ -582,7 +601,7 @@ const UITabsList = memo(function UITabsList({
582
601
  showsHorizontalScrollIndicator={showsHorizontalScrollIndicator}
583
602
  keyboardShouldPersistTaps={keyboardShouldPersistTaps}
584
603
  bounces
585
- contentContainerStyle={[styles.scrollContent]}
604
+ contentContainerStyle={styles.scrollContent}
586
605
  >
587
606
  {content}
588
607
  </ScrollView>
@@ -662,7 +681,7 @@ const UITabsTrigger = memo(function UITabsTrigger({
662
681
  return;
663
682
  }
664
683
 
665
- if (animation === "opacity") {
684
+ if (animation === "opacity" || animation === "pressOpacity") {
666
685
  Animated.timing(opacity, {
667
686
  toValue: 0.65,
668
687
 
@@ -782,9 +801,11 @@ const UITabsTrigger = memo(function UITabsTrigger({
782
801
  <View
783
802
  style={[
784
803
  styles.icon,
804
+
785
805
  {
786
806
  marginRight: 6,
787
807
  },
808
+
788
809
  iconStyle,
789
810
  ]}
790
811
  >
@@ -815,9 +836,11 @@ const UITabsTrigger = memo(function UITabsTrigger({
815
836
  <View
816
837
  style={[
817
838
  styles.icon,
839
+
818
840
  {
819
841
  marginLeft: 6,
820
842
  },
843
+
821
844
  iconStyle,
822
845
  ]}
823
846
  >
@@ -1067,6 +1090,19 @@ const UITabsIndicator = memo(function UITabsIndicator({
1067
1090
 
1068
1091
  /* =========================================================
1069
1092
  * CONTENT
1093
+ *
1094
+ * NO ANIMATION.
1095
+ *
1096
+ * This component intentionally does not use:
1097
+ * - Animated.Value
1098
+ * - opacity animation
1099
+ * - translateX
1100
+ * - translateY
1101
+ * - scale
1102
+ * - spring
1103
+ * - timing
1104
+ *
1105
+ * React simply mounts/unmounts the active content.
1070
1106
  * ======================================================= */
1071
1107
 
1072
1108
  const UITabsContent = memo(function UITabsContent({
@@ -1078,16 +1114,8 @@ const UITabsContent = memo(function UITabsContent({
1078
1114
  contentContainerStyle,
1079
1115
 
1080
1116
  /*
1081
- * Default is fade.
1082
- *
1083
- * Supported:
1084
- * none
1085
- * fade
1086
- * scale
1087
- * fadeScale
1088
- * slide
1089
- * slideFade
1090
- * slideScale
1117
+ * Kept for API compatibility.
1118
+ * Ignored intentionally.
1091
1119
  */
1092
1120
  animation,
1093
1121
 
@@ -1109,164 +1137,36 @@ const UITabsContent = memo(function UITabsContent({
1109
1137
 
1110
1138
  const active = tabs.isActive(value);
1111
1139
 
1112
- const [mounted, setMounted] = useState(active || !lazy);
1113
-
1114
- const progress = useRef(new Animated.Value(active ? 1 : 0)).current;
1115
-
1116
- const previousActive = useRef(active);
1117
-
1118
- const resolvedAnimation = animation ?? tabs.contentAnimation ?? "fade";
1140
+ const [hasMounted, setHasMounted] = useState(active || !lazy);
1119
1141
 
1120
1142
  useEffect(() => {
1121
1143
  if (active) {
1122
- setMounted(true);
1123
-
1124
- if (!tabs.animated || resolvedAnimation === "none") {
1125
- progress.setValue(1);
1126
-
1127
- previousActive.current = true;
1128
-
1129
- return;
1130
- }
1131
-
1132
- progress.setValue(0);
1133
-
1134
- Animated.timing(progress, {
1135
- toValue: 1,
1136
-
1137
- duration: animationDuration ?? tabs.animationDuration,
1138
-
1139
- easing: getEasing(tabs.animationEasing),
1140
-
1141
- useNativeDriver: true,
1142
- }).start();
1143
-
1144
- previousActive.current = true;
1145
-
1146
- return;
1144
+ setHasMounted(true);
1145
+ } else if (unmountOnExit) {
1146
+ setHasMounted(false);
1147
1147
  }
1148
+ }, [active, unmountOnExit]);
1148
1149
 
1149
- if (!previousActive.current) {
1150
- return;
1151
- }
1152
-
1153
- if (!tabs.animated || resolvedAnimation === "none") {
1154
- progress.setValue(0);
1155
-
1156
- if (unmountOnExit) {
1157
- setMounted(false);
1158
- }
1159
-
1160
- previousActive.current = false;
1161
-
1162
- return;
1163
- }
1164
-
1165
- Animated.timing(progress, {
1166
- toValue: 0,
1167
-
1168
- duration: animationDuration ?? tabs.animationDuration,
1169
-
1170
- easing: getEasing(tabs.animationEasing),
1171
-
1172
- useNativeDriver: true,
1173
- }).start(() => {
1174
- if (unmountOnExit) {
1175
- setMounted(false);
1176
- }
1177
- });
1178
-
1179
- previousActive.current = false;
1180
- }, [
1181
- active,
1182
- tabs.animated,
1183
- tabs.animationDuration,
1184
- tabs.animationEasing,
1185
- resolvedAnimation,
1186
- animationDuration,
1187
- progress,
1188
- unmountOnExit,
1189
- ]);
1190
-
1191
- if (!mounted) {
1150
+ if (!hasMounted) {
1192
1151
  return null;
1193
1152
  }
1194
1153
 
1195
1154
  /*
1196
1155
  * IMPORTANT:
1197
1156
  *
1198
- * Default fade has:
1199
- * - opacity animation
1200
- * - NO translateX
1201
- * - NO translateY
1202
- *
1203
- * Therefore it cannot behave
1204
- * like a carousel.
1157
+ * This is a normal View.
1158
+ * There is ZERO Animated.View here.
1205
1159
  */
1206
-
1207
- const isFade =
1208
- resolvedAnimation === "fade" ||
1209
- resolvedAnimation === "fadeScale" ||
1210
- resolvedAnimation === "slideFade";
1211
-
1212
- const isScale =
1213
- resolvedAnimation === "scale" ||
1214
- resolvedAnimation === "fadeScale" ||
1215
- resolvedAnimation === "slideScale";
1216
-
1217
- const isSlide =
1218
- resolvedAnimation === "slide" ||
1219
- resolvedAnimation === "slideFade" ||
1220
- resolvedAnimation === "slideScale";
1221
-
1222
- const translateX = isSlide && tabs.directionAware ? 16 : 0;
1223
-
1224
- const animatedTranslateX = isSlide
1225
- ? progress.interpolate({
1226
- inputRange: [0, 1],
1227
- outputRange: [translateX, 0],
1228
- })
1229
- : 0;
1230
-
1231
- const animatedScale = isScale
1232
- ? progress.interpolate({
1233
- inputRange: [0, 1],
1234
- outputRange: [0.98, 1],
1235
- })
1236
- : 1;
1237
-
1238
- const animatedOpacity = isFade ? progress : 1;
1239
-
1240
1160
  return (
1241
- <Animated.View
1161
+ <View
1242
1162
  testID={testID}
1243
1163
  accessible={accessible}
1244
1164
  accessibilityLabel={accessibilityLabel}
1245
- style={[
1246
- styles.content,
1247
-
1248
- {
1249
- opacity: animatedOpacity,
1250
-
1251
- transform: [
1252
- {
1253
- translateX: animatedTranslateX,
1254
- },
1255
-
1256
- {
1257
- scale: animatedScale,
1258
- },
1259
- ],
1260
- },
1261
-
1262
- style,
1263
-
1264
- contentContainerStyle,
1265
- ]}
1165
+ style={[styles.content, style, contentContainerStyle]}
1266
1166
  {...props}
1267
1167
  >
1268
1168
  {children}
1269
- </Animated.View>
1169
+ </View>
1270
1170
  );
1271
1171
  });
1272
1172
 
@@ -1289,7 +1189,7 @@ export function useUITabs() {
1289
1189
  }
1290
1190
 
1291
1191
  /* =========================================================
1292
- * COMPOUND COMPONENT API
1192
+ * COMPOUND COMPONENT
1293
1193
  * ======================================================= */
1294
1194
 
1295
1195
  const UITabs = memo(UITabsRoot);