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.
- package/lib/module/app.js +17 -57
- package/lib/module/app.js.map +1 -1
- package/lib/module/components/CarouselCardStack/CarouselCardStack.js +99 -81
- package/lib/module/components/CarouselCardStack/CarouselCardStack.js.map +1 -1
- package/lib/module/components/TravelBooking/FlightSummary.js +219 -112
- package/lib/module/components/TravelBooking/FlightSummary.js.map +1 -1
- package/lib/module/components/TravelBooking/HotelSummary.js +168 -37
- package/lib/module/components/TravelBooking/HotelSummary.js.map +1 -1
- package/lib/module/components/TravelBooking/SummaryBar.js +239 -172
- package/lib/module/components/TravelBooking/SummaryBar.js.map +1 -1
- package/lib/module/components/TravelBooking/TravelBooking.js +407 -241
- package/lib/module/components/TravelBooking/TravelBooking.js.map +1 -1
- package/lib/module/components/TravelBooking/index.js +5 -4
- package/lib/module/components/TravelBooking/index.js.map +1 -1
- package/lib/typescript/src/app.d.ts.map +1 -1
- package/lib/typescript/src/components/CarouselCardStack/CarouselCardStack.d.ts.map +1 -1
- package/lib/typescript/src/components/TravelBooking/FlightSummary.d.ts +25 -11
- package/lib/typescript/src/components/TravelBooking/FlightSummary.d.ts.map +1 -1
- package/lib/typescript/src/components/TravelBooking/HotelSummary.d.ts +14 -0
- package/lib/typescript/src/components/TravelBooking/HotelSummary.d.ts.map +1 -1
- package/lib/typescript/src/components/TravelBooking/SummaryBar.d.ts +1 -16
- package/lib/typescript/src/components/TravelBooking/SummaryBar.d.ts.map +1 -1
- package/lib/typescript/src/components/TravelBooking/TravelBooking.d.ts +1 -83
- package/lib/typescript/src/components/TravelBooking/TravelBooking.d.ts.map +1 -1
- package/lib/typescript/src/components/TravelBooking/index.d.ts +0 -4
- package/lib/typescript/src/components/TravelBooking/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/app.tsx +18 -39
- package/src/components/CarouselCardStack/CarouselCardStack.tsx +130 -120
- package/src/components/TravelBooking/FlightSummary.tsx +264 -164
- package/src/components/TravelBooking/HotelSummary.tsx +243 -76
- package/src/components/TravelBooking/SummaryBar.tsx +239 -239
- package/src/components/TravelBooking/TravelBooking.tsx +407 -407
- package/src/components/TravelBooking/index.ts +4 -4
- 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
|
|
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
|
-
//
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
|
248
|
+
<View style={[styles.container, { height: cardHeight + 60, backgroundColor }]}>
|
|
179
249
|
<GestureDetector gesture={panGesture}>
|
|
180
250
|
<Animated.View style={[styles.cardContainer, { height: cardHeight }]}>
|
|
181
|
-
{
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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(
|
|
273
|
+
width: withSpring(isActive ? 24 : 8, SPRING_CONFIG),
|
|
266
274
|
height: 8,
|
|
267
275
|
borderRadius: 4,
|
|
268
|
-
backgroundColor:
|
|
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;
|