related-ui-components 2.7.5 → 2.7.7

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 (35) hide show
  1. package/lib/module/app.js +17 -57
  2. package/lib/module/app.js.map +1 -1
  3. package/lib/module/components/CarouselCardStack/CarouselCardStack.js +99 -81
  4. package/lib/module/components/CarouselCardStack/CarouselCardStack.js.map +1 -1
  5. package/lib/module/components/TravelBooking/FlightSummary.js +219 -112
  6. package/lib/module/components/TravelBooking/FlightSummary.js.map +1 -1
  7. package/lib/module/components/TravelBooking/HotelSummary.js +168 -37
  8. package/lib/module/components/TravelBooking/HotelSummary.js.map +1 -1
  9. package/lib/module/components/TravelBooking/SummaryBar.js +239 -172
  10. package/lib/module/components/TravelBooking/SummaryBar.js.map +1 -1
  11. package/lib/module/components/TravelBooking/TravelBooking.js +407 -241
  12. package/lib/module/components/TravelBooking/TravelBooking.js.map +1 -1
  13. package/lib/module/components/TravelBooking/index.js +5 -4
  14. package/lib/module/components/TravelBooking/index.js.map +1 -1
  15. package/lib/typescript/src/app.d.ts.map +1 -1
  16. package/lib/typescript/src/components/CarouselCardStack/CarouselCardStack.d.ts.map +1 -1
  17. package/lib/typescript/src/components/TravelBooking/FlightSummary.d.ts +25 -11
  18. package/lib/typescript/src/components/TravelBooking/FlightSummary.d.ts.map +1 -1
  19. package/lib/typescript/src/components/TravelBooking/HotelSummary.d.ts +14 -0
  20. package/lib/typescript/src/components/TravelBooking/HotelSummary.d.ts.map +1 -1
  21. package/lib/typescript/src/components/TravelBooking/SummaryBar.d.ts +1 -16
  22. package/lib/typescript/src/components/TravelBooking/SummaryBar.d.ts.map +1 -1
  23. package/lib/typescript/src/components/TravelBooking/TravelBooking.d.ts +1 -83
  24. package/lib/typescript/src/components/TravelBooking/TravelBooking.d.ts.map +1 -1
  25. package/lib/typescript/src/components/TravelBooking/index.d.ts +0 -4
  26. package/lib/typescript/src/components/TravelBooking/index.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/src/app.tsx +18 -39
  29. package/src/components/CarouselCardStack/CarouselCardStack.tsx +130 -120
  30. package/src/components/TravelBooking/FlightSummary.tsx +264 -164
  31. package/src/components/TravelBooking/HotelSummary.tsx +243 -76
  32. package/src/components/TravelBooking/SummaryBar.tsx +239 -239
  33. package/src/components/TravelBooking/TravelBooking.tsx +407 -407
  34. package/src/components/TravelBooking/index.ts +4 -4
  35. package/src/index.ts +1 -1
@@ -25,9 +25,11 @@ const SIDE_CARD_TRANSLATE_X_FACTOR = Math.min(0.32, MAX_X_FACTOR);
25
25
  const SIDE_CARD_TRANSLATE_X = CARD_WIDTH * SIDE_CARD_TRANSLATE_X_FACTOR;
26
26
  const ACTIVE_CARD_SCALE = 1.0;
27
27
  const SPRING_CONFIG = { damping: 18, stiffness: 120, mass: 0.6 };
28
- const FADE_SPRING_CONFIG = { damping: 35, stiffness: 50, mass: 1.2 };
29
28
  const SIDE_CARD_ROTATION_DEGREES = 7;
30
29
 
30
+ // Only render visible items + buffer
31
+ const VISIBLE_RANGE = 2; // Items on each side of active item
32
+
31
33
  interface CarouselItemOriginal {
32
34
  id: string;
33
35
  image: string;
@@ -42,7 +44,7 @@ interface CarouselCardStackProps {
42
44
  data: CarouselItemOriginal[];
43
45
  cardHeight?: number;
44
46
  cardWidth?: number;
45
- backgroundColor? : string;
47
+ backgroundColor?: string;
46
48
  }
47
49
 
48
50
  const createVirtualData = (
@@ -66,6 +68,91 @@ const createVirtualData = (
66
68
  return [...prevSegment, ...currSegment, ...nextSegment];
67
69
  };
68
70
 
71
+ // Memoized card component
72
+ const CarouselCard = React.memo<{
73
+ item: CarouselItemVirtual;
74
+ index: number;
75
+ activeIndex: Animated.SharedValue<number>;
76
+ gestureTranslateX: Animated.SharedValue<number>;
77
+ cardWidth: number;
78
+ cardHeight: number;
79
+ virtualDataLength: number;
80
+ }>(({ item, index, activeIndex, gestureTranslateX, cardWidth, cardHeight, virtualDataLength }) => {
81
+ const animatedStyle = useAnimatedStyle(() => {
82
+ const currentCardDragOffset = gestureTranslateX.value / cardWidth;
83
+ const displayOffset = index - (activeIndex.value - currentCardDragOffset);
84
+
85
+ // Early return for items that are too far away to optimize performance
86
+ const absDisplayOffset = Math.abs(displayOffset);
87
+ if (absDisplayOffset > 2.5) {
88
+ return {
89
+ opacity: 0,
90
+ transform: [{ translateX: 0 }, { scale: 0.8 }, { rotateZ: '0deg' }],
91
+ zIndex: 0,
92
+ };
93
+ }
94
+
95
+ const scale = interpolate(
96
+ displayOffset,
97
+ [-1, 0, 1],
98
+ [SIDE_CARD_SCALE_FACTOR, ACTIVE_CARD_SCALE, SIDE_CARD_SCALE_FACTOR],
99
+ Extrapolation.CLAMP
100
+ );
101
+
102
+ const translateX = interpolate(
103
+ displayOffset,
104
+ [-1, 0, 1],
105
+ [-SIDE_CARD_TRANSLATE_X, 0, SIDE_CARD_TRANSLATE_X],
106
+ Extrapolation.CLAMP
107
+ );
108
+
109
+ const rotation = interpolate(
110
+ displayOffset,
111
+ [-1, 0, 1],
112
+ [-SIDE_CARD_ROTATION_DEGREES, 0, SIDE_CARD_ROTATION_DEGREES],
113
+ Extrapolation.CLAMP
114
+ );
115
+
116
+ const opacity = interpolate(
117
+ absDisplayOffset,
118
+ [0, 1, 2, 2.75],
119
+ [1, 1, 0.6, 0],
120
+ Extrapolation.CLAMP
121
+ );
122
+
123
+ const snappedDisplayOffset = index - Math.round(activeIndex.value);
124
+ const zIndex = virtualDataLength - Math.abs(snappedDisplayOffset);
125
+
126
+ return {
127
+ transform: [{ translateX }, { scale }, { rotateZ: `${rotation}deg` }],
128
+ opacity,
129
+ zIndex,
130
+ };
131
+ }, [index, cardWidth, virtualDataLength]);
132
+
133
+ return (
134
+ <Animated.View
135
+ style={[
136
+ styles.card,
137
+ { width: cardWidth, height: cardHeight },
138
+ animatedStyle,
139
+ ]}
140
+ >
141
+ <Image
142
+ source={{ uri: item.image }}
143
+ style={styles.cardImage}
144
+ resizeMode="cover"
145
+ fadeDuration={0}
146
+ />
147
+ {item.title && (
148
+ <View style={styles.titleContainer}>
149
+ <Text style={styles.cardTitle}>{item.title}</Text>
150
+ </View>
151
+ )}
152
+ </Animated.View>
153
+ );
154
+ });
155
+
69
156
  const CarouselCardStack: React.FC<CarouselCardStackProps> = ({
70
157
  data: originalData,
71
158
  cardHeight = CARD_HEIGHT,
@@ -73,7 +160,6 @@ const CarouselCardStack: React.FC<CarouselCardStackProps> = ({
73
160
  backgroundColor = "transparent"
74
161
  }) => {
75
162
  const { theme } = useTheme();
76
-
77
163
  const N_original = originalData.length;
78
164
 
79
165
  const virtualData = useMemo(
@@ -90,35 +176,29 @@ const CarouselCardStack: React.FC<CarouselCardStackProps> = ({
90
176
  gestureTranslateX.value = 0;
91
177
  }, [N_original, activeIndex, gestureTranslateX]);
92
178
 
93
- // Worklet to handle the loop reset
94
- const handleLoopReset = () => {
95
- "worklet";
96
- if (N_original === 0) return;
97
-
98
- const currentValue = Math.round(activeIndex.value);
99
-
100
- // If in the "next" segment (indices >= 2*N_original)
101
- if (currentValue >= N_original * 2) {
102
- activeIndex.value = currentValue - N_original;
103
- }
104
- // If in the "prev" segment (indices < N_original)
105
- else if (currentValue < N_original) {
106
- activeIndex.value = currentValue + N_original;
107
- }
108
- };
179
+ // Calculate visible items - moved outside of render
180
+ const visibleItems = useDerivedValue(() => {
181
+ const center = Math.round(activeIndex.value);
182
+ const start = Math.max(0, center - VISIBLE_RANGE);
183
+ const end = Math.min(virtualData.length - 1, center + VISIBLE_RANGE);
184
+
185
+ return virtualData.slice(start, end + 1).map((item, sliceIndex) => ({
186
+ item,
187
+ originalIndex: start + sliceIndex
188
+ }));
189
+ });
109
190
 
110
191
  useDerivedValue(() => {
111
- const currentValue = Math.round(activeIndex.value);
112
-
113
- // Only reset if we're at the boundaries and not currently animating (gestureTranslateX is 0)
114
- if (gestureTranslateX.value === 0) { // i.e., spring animation for gesture has finished
115
- if (currentValue >= N_original * 2) {
116
- activeIndex.value = currentValue - N_original;
117
- } else if (currentValue < N_original) {
118
- activeIndex.value = currentValue + N_original;
192
+ const currentValue = Math.round(activeIndex.value);
193
+
194
+ if (gestureTranslateX.value === 0) {
195
+ if (currentValue >= N_original * 2) {
196
+ activeIndex.value = currentValue - N_original;
197
+ } else if (currentValue < N_original) {
198
+ activeIndex.value = currentValue + N_original;
199
+ }
119
200
  }
120
- }
121
- });
201
+ });
122
202
 
123
203
  const panGesture = Gesture.Pan()
124
204
  .activeOffsetX([-10, 10])
@@ -140,22 +220,12 @@ const CarouselCardStack: React.FC<CarouselCardStackProps> = ({
140
220
  newTargetVirtualIndex = activeIndex.value - 1;
141
221
  }
142
222
 
143
- // Clamp to virtual data bounds
144
223
  newTargetVirtualIndex = Math.max(
145
224
  0,
146
225
  Math.min(newTargetVirtualIndex, virtualData.length - 1)
147
226
  );
148
227
 
149
- activeIndex.value = withSpring(
150
- newTargetVirtualIndex,
151
- SPRING_CONFIG,
152
- (finished) => {
153
- "worklet";
154
- if (finished) {
155
- handleLoopReset();
156
- }
157
- }
158
- );
228
+ activeIndex.value = withSpring(newTargetVirtualIndex, SPRING_CONFIG);
159
229
  gestureTranslateX.value = withSpring(0, SPRING_CONFIG);
160
230
  contextX.value = 0;
161
231
  });
@@ -175,100 +245,40 @@ const CarouselCardStack: React.FC<CarouselCardStackProps> = ({
175
245
  }
176
246
 
177
247
  return (
178
- <View style={[styles.container, { height: cardHeight + 60, backgroundColor: backgroundColor}]}>
248
+ <View style={[styles.container, { height: cardHeight + 60, backgroundColor }]}>
179
249
  <GestureDetector gesture={panGesture}>
180
250
  <Animated.View style={[styles.cardContainer, { height: cardHeight }]}>
181
- {virtualData.map((item, index) => {
182
- const animatedStyle = useAnimatedStyle(() => {
183
- const currentCardDragOffset = gestureTranslateX.value / cardWidth;
184
- const displayOffset =
185
- index - (activeIndex.value - currentCardDragOffset);
186
-
187
- const scale = interpolate(
188
- displayOffset,
189
- [-1, 0, 1],
190
- [
191
- SIDE_CARD_SCALE_FACTOR,
192
- ACTIVE_CARD_SCALE,
193
- SIDE_CARD_SCALE_FACTOR,
194
- ],
195
- Extrapolation.CLAMP
196
- );
197
-
198
- const translateX = interpolate(
199
- displayOffset,
200
- [-1, 0, 1],
201
- [-SIDE_CARD_TRANSLATE_X, 0, SIDE_CARD_TRANSLATE_X],
202
- Extrapolation.CLAMP
203
- );
204
-
205
- const rotation = interpolate(
206
- displayOffset,
207
- [-1, 0, 1],
208
- [
209
- -SIDE_CARD_ROTATION_DEGREES,
210
- 0,
211
- SIDE_CARD_ROTATION_DEGREES,
212
- ],
213
- Extrapolation.CLAMP,
214
- );
215
-
216
- const opacity = interpolate(
217
- Math.abs(displayOffset),
218
- [0, 1, 2, 2.75],
219
- [1, 1, 0.6, 0],
220
- Extrapolation.CLAMP
221
- );
222
-
223
- const snappedDisplayOffset =
224
- index - Math.round(activeIndex.value);
225
- const zIndex =
226
- virtualData.length - Math.abs(snappedDisplayOffset);
227
-
228
-
229
- return {
230
- transform: [{ translateX }, { scale }, { rotateZ: `${rotation}deg` }],
231
- opacity,
232
- zIndex,
233
- };
234
- });
235
-
236
- return (
237
- <Animated.View
238
- key={item.uniqueId}
239
- style={[
240
- styles.card,
241
- { width: cardWidth, height: cardHeight },
242
- animatedStyle,
243
- ]}
244
- >
245
- <Image source={{ uri: item.image }} style={styles.cardImage} />
246
- {item.title && (
247
- <View style={styles.titleContainer}>
248
- <Text style={styles.cardTitle}>{item.title}</Text>
249
- </View>
250
- )}
251
- </Animated.View>
252
- );
253
- })}
251
+ {/* Render all items but optimize with early returns in animations */}
252
+ {virtualData.map((item, index) => (
253
+ <CarouselCard
254
+ key={item.uniqueId}
255
+ item={item}
256
+ index={index}
257
+ activeIndex={activeIndex}
258
+ gestureTranslateX={gestureTranslateX}
259
+ cardWidth={cardWidth}
260
+ cardHeight={cardHeight}
261
+ virtualDataLength={virtualData.length}
262
+ />
263
+ ))}
254
264
  </Animated.View>
255
265
  </GestureDetector>
256
266
 
257
267
  {N_original > 0 && (
258
268
  <View style={styles.paginationContainer}>
259
269
  {originalData.map((_, i) => {
260
- const isActiveDot = useDerivedValue(() => {
261
- return activeDotIndex.value === i;
262
- });
263
270
  const dotStyle = useAnimatedStyle(() => {
271
+ const isActive = activeDotIndex.value === i;
264
272
  return {
265
- width: withSpring(isActiveDot.value ? 24 : 8, SPRING_CONFIG),
273
+ width: withSpring(isActive ? 24 : 8, SPRING_CONFIG),
266
274
  height: 8,
267
275
  borderRadius: 4,
268
- backgroundColor: isActiveDot.value ? theme.primary : "#FFFFFF",
276
+ backgroundColor: isActive ? theme.primary : "#FFFFFF",
269
277
  marginHorizontal: 4,
278
+ opacity: isActive ? 1 : 0.5,
270
279
  };
271
280
  });
281
+
272
282
  return <Animated.View key={`dot-${i}`} style={dotStyle} />;
273
283
  })}
274
284
  </View>
@@ -325,4 +335,4 @@ const styles = StyleSheet.create({
325
335
  },
326
336
  });
327
337
 
328
- export default CarouselCardStack;
338
+ export default CarouselCardStack;