|
1 | | -import React, { useState, useEffect } from 'react'; |
| 1 | +import React, { useState, useEffect, useCallback } from 'react'; |
2 | 2 | import { View, Text, StyleSheet, ScrollView, ActivityIndicator } from 'react-native'; |
3 | 3 | import { Ionicons } from '@expo/vector-icons'; |
4 | 4 | import { useTheme } from '../theme/ThemeProvider'; |
@@ -49,6 +49,8 @@ export default function WeatherForecastUI({ latitude, longitude, useImperialUnit |
49 | 49 | const [data, setData] = useState(null); |
50 | 50 | const [loading, setLoading] = useState(true); |
51 | 51 | const [error, setError] = useState(null); |
| 52 | + const [barWrapperWidth, setBarWrapperWidth] = useState(0); |
| 53 | + const onBarWrapperLayout = useCallback((e) => setBarWrapperWidth(e.nativeEvent.layout.width), []); |
52 | 54 |
|
53 | 55 | useEffect(() => { |
54 | 56 | if (!latitude || !longitude) return; |
@@ -151,29 +153,37 @@ export default function WeatherForecastUI({ latitude, longitude, useImperialUnit |
151 | 153 | </View> |
152 | 154 | <View style={styles.dailyList}> |
153 | 155 | {dailyData.map((day, i) => { |
154 | | - const leftOffset = ((day.min - absoluteMin) / rangeTotal) * 100; |
155 | | - const barWidth = ((day.max - day.min) / rangeTotal) * 100; |
156 | | - |
| 156 | + // Use pixel values — percentage strings crash React Native on web (StyleSheet.replace bug) |
| 157 | + const leftFraction = (day.min - absoluteMin) / rangeTotal; |
| 158 | + const widthFraction = (day.max - day.min) / rangeTotal; |
| 159 | + const pixelLeft = barWrapperWidth * leftFraction; |
| 160 | + const pixelWidth = Math.max(4, barWrapperWidth * widthFraction); |
| 161 | + |
157 | 162 | return ( |
158 | 163 | <View key={i} style={styles.dailyRow}> |
159 | 164 | <Text style={[styles.dailyDayName, { color: theme.colors.text }]}>{getDayName(day.time)}</Text> |
160 | 165 | <View style={styles.dailyIconBox}> |
161 | 166 | <Ionicons name={getWeatherIcon(day.weathercode)} size={20} color={theme.colors.primary} /> |
162 | 167 | </View> |
163 | 168 | <Text style={[styles.dailyMin, { color: theme.colors.muted }]}>{day.min}°</Text> |
164 | | - |
165 | | - {/* Visual Temperature Distribution Bar */} |
166 | | - <View style={[styles.dailyBarWrapper, { backgroundColor: 'rgba(156, 163, 175, 0.2)' }]}> |
167 | | - <View |
168 | | - style={[ |
169 | | - styles.dailyBar, |
170 | | - { |
171 | | - left: `${leftOffset}%`, |
172 | | - width: `${barWidth}%`, |
173 | | - backgroundColor: theme.colors.primary |
174 | | - } |
175 | | - ]} |
176 | | - /> |
| 169 | + |
| 170 | + {/* Visual Temperature Distribution Bar (pixel-based to avoid RN web % crash) */} |
| 171 | + <View |
| 172 | + style={[styles.dailyBarWrapper, { backgroundColor: 'rgba(156, 163, 175, 0.2)' }]} |
| 173 | + onLayout={i === 0 ? onBarWrapperLayout : undefined} |
| 174 | + > |
| 175 | + {barWrapperWidth > 0 && ( |
| 176 | + <View |
| 177 | + style={[ |
| 178 | + styles.dailyBar, |
| 179 | + { |
| 180 | + left: pixelLeft, |
| 181 | + width: pixelWidth, |
| 182 | + backgroundColor: theme.colors.primary, |
| 183 | + }, |
| 184 | + ]} |
| 185 | + /> |
| 186 | + )} |
177 | 187 | </View> |
178 | 188 |
|
179 | 189 | <Text style={[styles.dailyMax, { color: theme.colors.text }]}>{day.max}°</Text> |
|
0 commit comments