@homefile/components-v2 2.15.1 → 2.16.0
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.
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import { WeatherWidgetI } from '../../../interfaces';
|
|
2
|
-
export declare const WeatherWidget: ({ current, details, forecast, header, }: WeatherWidgetI
|
|
2
|
+
export declare const WeatherWidget: ({ current, details, forecast, header, loading, }: WeatherWidgetI & {
|
|
3
|
+
loading?: boolean;
|
|
4
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Stack, Flex, Text, HStack, VStack, Divider,
|
|
2
|
+
import { Stack, Flex, Box, Text, HStack, VStack, Divider, } from '@chakra-ui/react';
|
|
3
3
|
import { extractCodeFromUrl } from '../../../utils';
|
|
4
4
|
import { LuSnowflake, LuSun, LuCloudSun, LuCloudy, LuCloud, LuCloudRain, LuCloudLightning, } from 'react-icons/lu';
|
|
5
5
|
import { colors } from '../../../theme/colors';
|
|
6
|
+
import { SkeletonBox } from '../../loaders';
|
|
6
7
|
const weatherIconMap = {
|
|
7
8
|
skc: LuSun,
|
|
8
9
|
few: LuCloudSun,
|
|
@@ -13,13 +14,18 @@ const weatherIconMap = {
|
|
|
13
14
|
tsra: LuCloudLightning,
|
|
14
15
|
snow: LuSnowflake,
|
|
15
16
|
};
|
|
16
|
-
export const WeatherWidget = ({ current, details, forecast, header, }) => {
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
export const WeatherWidget = ({ current, details, forecast, header, loading = false, }) => {
|
|
18
|
+
if (loading) {
|
|
19
|
+
return (_jsxs(Stack, { spacing: "base", p: "4", zIndex: "2", children: [_jsxs(Flex, { justify: "space-between", align: "center", children: [_jsx(SkeletonBox, { height: "12px", borderRadius: "md", width: "30%" }), _jsx(SkeletonBox, { height: "12px", borderRadius: "md", width: "30%" })] }), _jsxs(Stack, { align: "end", children: [_jsxs(Flex, { justify: "end", align: "start", gap: "base", mt: "4", children: [_jsx(SkeletonBox, { height: "50px", borderRadius: "md", width: "50px" }), _jsx(SkeletonBox, { height: "30px", width: "30px", borderRadius: "full" })] }), _jsx(SkeletonBox, { height: "12px", borderRadius: "md", width: "20%" })] }), _jsx(Divider, { borderColor: "gray.2", borderStyle: "dashed" }), _jsxs(Flex, { justify: "space-between", mt: "4", children: [_jsxs(Stack, { children: [_jsx(SkeletonBox, { height: "8px", borderRadius: "md", width: "70px" }), _jsx(SkeletonBox, { height: "8px", borderRadius: "md", width: "70px" })] }), _jsxs(Stack, { align: "end", children: [_jsx(SkeletonBox, { height: "8px", borderRadius: "md", width: "70px" }), _jsx(SkeletonBox, { height: "8px", borderRadius: "md", width: "70px" })] })] }), _jsx(Flex, { justify: "space-between", mt: "4", gap: "1", children: Array(5)
|
|
20
|
+
.fill(0)
|
|
21
|
+
.map((_, idx) => (_jsxs(VStack, { gap: "1", width: "46px", border: "1px solid", borderColor: "gray.2", py: "2", px: "1", children: [_jsx(SkeletonBox, { height: "6px", borderRadius: "md", width: "60%" }), _jsx(Divider, { borderColor: "gray.2", borderStyle: "solid", my: "0.5" }), _jsx(SkeletonBox, { height: "18px", width: "18px", borderRadius: "full" }), _jsx(SkeletonBox, { height: "8px", borderRadius: "md", width: "70%" }), _jsx(SkeletonBox, { height: "6px", borderRadius: "md", width: "70%" })] }, idx))) })] }));
|
|
22
|
+
}
|
|
23
|
+
return (_jsxs(Stack, { spacing: "base", p: "4", zIndex: "2", children: [header && (_jsxs(Flex, { justify: "space-between", align: "center", children: [header.date && (_jsx(Text, { fontSize: "sm", fontFamily: "secondary", textTransform: "uppercase", children: header.date })), header.location && (_jsx(Text, { fontSize: "sm", fontFamily: "secondary", textTransform: "uppercase", children: header.location }))] })), current && (_jsxs(Stack, { spacing: "0", align: "end", children: [_jsxs(HStack, { align: "start", spacing: "2", children: [current.temp != null && (_jsxs(Text, { fontSize: "6xl", fontWeight: "medium", lineHeight: 1, color: "gray.4", children: [current.temp, "\u00B0"] })), getWeatherIcon(current.icon, 50)] }), _jsxs(Text, { fontSize: "md", children: [_jsxs(Box, { as: "span", color: "gray.2", children: ["H", ' '] }), current.high, "\u00B0", ' ', _jsxs(Box, { as: "span", color: "gray.2", children: ["L", ' '] }), current.low, "\u00B0"] })] })), _jsx(Divider, { borderColor: "gray.2", borderStyle: "dashed" }), details && (_jsxs(Flex, { justify: "space-between", children: [_jsxs(VStack, { spacing: "0", align: "start", children: [_jsx(WeatherDetailText, { children: "Precipitation" }), details.humidity != null && (_jsx(WeatherDetailText, { children: "Humidity" })), _jsx(WeatherDetailText, { children: "Wind" })] }), _jsxs(VStack, { spacing: "0", align: "start", children: [details.precipitation != null && (_jsxs(WeatherDetailText, { children: [details.precipitation, "%"] })), details.humidity != null && (_jsxs(WeatherDetailText, { children: [details.humidity, "%"] })), details.wind && (_jsx(WeatherDetailText, { children: details.wind }))] })] })), forecast && (_jsx(Flex, { justify: "space-between", gap: "1", children: forecast.map((f) => (_jsxs(VStack, { spacing: "0", border: "1px solid", borderColor: "gray.2", borderRadius: "sm", children: [_jsx(Text, { fontFamily: "secondary", textTransform: "uppercase", fontSize: "xxs", color: "gray.2", children: f.day }), _jsx(Divider, { borderColor: "gray.2" }), _jsxs(Stack, { spacing: "0", py: "1", px: "2", align: "center", children: [getWeatherIcon(f.icon, 16, colors.gray[3]), _jsxs(Text, { fontSize: "sm", fontWeight: "medium", mb: "-1", children: [f.high, "\u00B0"] }), _jsxs(WeatherDetailText, { children: ["L ", f.low, "\u00B0"] })] })] }, f.day))) }))] }));
|
|
21
24
|
};
|
|
25
|
+
const WeatherDetailText = ({ children }) => (_jsx(Text, { fontFamily: "secondary", textTransform: "uppercase", fontSize: "xs", lineHeight: "1.2", children: children }));
|
|
22
26
|
const getWeatherIcon = (iconUrl, size = 48, color = colors.gray[4]) => {
|
|
27
|
+
if (!iconUrl)
|
|
28
|
+
return null;
|
|
23
29
|
const code = extractCodeFromUrl(iconUrl);
|
|
24
30
|
const IconComponent = weatherIconMap[code] || LuSun;
|
|
25
31
|
return _jsx(IconComponent, { size: size, color: color });
|
package/package.json
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
HStack,
|
|
9
9
|
VStack,
|
|
10
10
|
Divider,
|
|
11
|
-
chakra,
|
|
12
11
|
} from '@chakra-ui/react'
|
|
13
12
|
import { extractCodeFromUrl } from '@/utils'
|
|
14
13
|
import {
|
|
@@ -21,6 +20,7 @@ import {
|
|
|
21
20
|
LuCloudLightning,
|
|
22
21
|
} from 'react-icons/lu'
|
|
23
22
|
import { colors } from '@/theme/colors'
|
|
23
|
+
import { SkeletonBox } from '@/components/loaders'
|
|
24
24
|
|
|
25
25
|
const weatherIconMap: Record<
|
|
26
26
|
string,
|
|
@@ -41,100 +41,192 @@ export const WeatherWidget = ({
|
|
|
41
41
|
details,
|
|
42
42
|
forecast,
|
|
43
43
|
header,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
</
|
|
54
|
-
|
|
44
|
+
loading = false,
|
|
45
|
+
}: WeatherWidgetI & { loading?: boolean }) => {
|
|
46
|
+
if (loading) {
|
|
47
|
+
return (
|
|
48
|
+
<Stack spacing="base" p="4" zIndex="2">
|
|
49
|
+
{/* Header skeleton */}
|
|
50
|
+
<Flex justify="space-between" align="center">
|
|
51
|
+
<SkeletonBox height="12px" borderRadius="md" width="30%" />
|
|
52
|
+
<SkeletonBox height="12px" borderRadius="md" width="30%" />
|
|
53
|
+
</Flex>
|
|
54
|
+
{/* Current temp skeleton */}
|
|
55
|
+
<Stack align="end">
|
|
56
|
+
<Flex justify="end" align="start" gap="base" mt="4">
|
|
57
|
+
<SkeletonBox height="50px" borderRadius="md" width="50px" />
|
|
58
|
+
<SkeletonBox height="30px" width="30px" borderRadius="full" />
|
|
59
|
+
</Flex>
|
|
60
|
+
<SkeletonBox height="12px" borderRadius="md" width="20%" />
|
|
61
|
+
</Stack>
|
|
55
62
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
</
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
63
|
+
<Divider borderColor="gray.2" borderStyle="dashed" />
|
|
64
|
+
{/* Details skeleton */}
|
|
65
|
+
<Flex justify="space-between" mt="4">
|
|
66
|
+
<Stack>
|
|
67
|
+
<SkeletonBox height="8px" borderRadius="md" width="70px" />
|
|
68
|
+
<SkeletonBox height="8px" borderRadius="md" width="70px" />
|
|
69
|
+
</Stack>
|
|
70
|
+
<Stack align="end">
|
|
71
|
+
<SkeletonBox height="8px" borderRadius="md" width="70px" />
|
|
72
|
+
<SkeletonBox height="8px" borderRadius="md" width="70px" />
|
|
73
|
+
</Stack>
|
|
74
|
+
</Flex>
|
|
75
|
+
{/* Forecast skeleton */}
|
|
76
|
+
<Flex justify="space-between" mt="4" gap="1">
|
|
77
|
+
{Array(5)
|
|
78
|
+
.fill(0)
|
|
79
|
+
.map((_, idx) => (
|
|
80
|
+
<VStack
|
|
81
|
+
key={idx}
|
|
82
|
+
gap="1"
|
|
83
|
+
width="46px"
|
|
84
|
+
border="1px solid"
|
|
85
|
+
borderColor="gray.2"
|
|
86
|
+
py="2"
|
|
87
|
+
px="1"
|
|
88
|
+
>
|
|
89
|
+
<SkeletonBox height="6px" borderRadius="md" width="60%" />
|
|
90
|
+
<Divider borderColor="gray.2" borderStyle="solid" my="0.5" />
|
|
91
|
+
<SkeletonBox height="18px" width="18px" borderRadius="full" />
|
|
92
|
+
<SkeletonBox height="8px" borderRadius="md" width="70%" />
|
|
93
|
+
<SkeletonBox height="6px" borderRadius="md" width="70%" />
|
|
94
|
+
</VStack>
|
|
95
|
+
))}
|
|
96
|
+
</Flex>
|
|
73
97
|
</Stack>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
74
100
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
{
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
101
|
+
return (
|
|
102
|
+
<Stack spacing="base" p="4" zIndex="2">
|
|
103
|
+
{/* Header */}
|
|
104
|
+
{header && (
|
|
105
|
+
<Flex justify="space-between" align="center">
|
|
106
|
+
{header.date && (
|
|
107
|
+
<Text
|
|
108
|
+
fontSize="sm"
|
|
109
|
+
fontFamily="secondary"
|
|
110
|
+
textTransform="uppercase"
|
|
111
|
+
>
|
|
112
|
+
{header.date}
|
|
113
|
+
</Text>
|
|
87
114
|
)}
|
|
88
|
-
|
|
89
|
-
</VStack>
|
|
90
|
-
</Flex>
|
|
91
|
-
|
|
92
|
-
<Flex justify="space-between" gap="1">
|
|
93
|
-
{forecast.map((f) => (
|
|
94
|
-
<VStack
|
|
95
|
-
key={f.day}
|
|
96
|
-
spacing="0"
|
|
97
|
-
border="1px solid"
|
|
98
|
-
borderColor="gray.2"
|
|
99
|
-
borderRadius="sm"
|
|
100
|
-
>
|
|
115
|
+
{header.location && (
|
|
101
116
|
<Text
|
|
117
|
+
fontSize="sm"
|
|
102
118
|
fontFamily="secondary"
|
|
103
119
|
textTransform="uppercase"
|
|
104
|
-
fontSize="xxs"
|
|
105
|
-
color="gray.2"
|
|
106
120
|
>
|
|
107
|
-
{
|
|
121
|
+
{header.location}
|
|
108
122
|
</Text>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
)}
|
|
124
|
+
</Flex>
|
|
125
|
+
)}
|
|
126
|
+
|
|
127
|
+
{/* Current Temperature */}
|
|
128
|
+
{current && (
|
|
129
|
+
<Stack spacing="0" align="end">
|
|
130
|
+
<HStack align="start" spacing="2">
|
|
131
|
+
{current.temp != null && (
|
|
132
|
+
<Text
|
|
133
|
+
fontSize="6xl"
|
|
134
|
+
fontWeight="medium"
|
|
135
|
+
lineHeight={1}
|
|
136
|
+
color="gray.4"
|
|
137
|
+
>
|
|
138
|
+
{current.temp}°
|
|
114
139
|
</Text>
|
|
115
|
-
|
|
116
|
-
|
|
140
|
+
)}
|
|
141
|
+
{getWeatherIcon(current.icon, 50)}
|
|
142
|
+
</HStack>
|
|
143
|
+
<Text fontSize="md">
|
|
144
|
+
<Box as="span" color="gray.2">
|
|
145
|
+
H{' '}
|
|
146
|
+
</Box>
|
|
147
|
+
{current.high}°{' '}
|
|
148
|
+
<Box as="span" color="gray.2">
|
|
149
|
+
L{' '}
|
|
150
|
+
</Box>
|
|
151
|
+
{current.low}°
|
|
152
|
+
</Text>
|
|
153
|
+
</Stack>
|
|
154
|
+
)}
|
|
155
|
+
|
|
156
|
+
<Divider borderColor="gray.2" borderStyle="dashed" />
|
|
157
|
+
|
|
158
|
+
{/* Details */}
|
|
159
|
+
{details && (
|
|
160
|
+
<Flex justify="space-between">
|
|
161
|
+
<VStack spacing="0" align="start">
|
|
162
|
+
<WeatherDetailText>Precipitation</WeatherDetailText>
|
|
163
|
+
{details.humidity != null && (
|
|
164
|
+
<WeatherDetailText>Humidity</WeatherDetailText>
|
|
165
|
+
)}
|
|
166
|
+
<WeatherDetailText>Wind</WeatherDetailText>
|
|
167
|
+
</VStack>
|
|
168
|
+
<VStack spacing="0" align="start">
|
|
169
|
+
{details.precipitation != null && (
|
|
170
|
+
<WeatherDetailText>{details.precipitation}%</WeatherDetailText>
|
|
171
|
+
)}
|
|
172
|
+
{details.humidity != null && (
|
|
173
|
+
<WeatherDetailText>{details.humidity}%</WeatherDetailText>
|
|
174
|
+
)}
|
|
175
|
+
{details.wind && (
|
|
176
|
+
<WeatherDetailText>{details.wind}</WeatherDetailText>
|
|
177
|
+
)}
|
|
117
178
|
</VStack>
|
|
118
|
-
|
|
119
|
-
|
|
179
|
+
</Flex>
|
|
180
|
+
)}
|
|
181
|
+
|
|
182
|
+
{/* 5-Day Forecast */}
|
|
183
|
+
{forecast && (
|
|
184
|
+
<Flex justify="space-between" gap="1">
|
|
185
|
+
{forecast.map((f) => (
|
|
186
|
+
<VStack
|
|
187
|
+
key={f.day}
|
|
188
|
+
spacing="0"
|
|
189
|
+
border="1px solid"
|
|
190
|
+
borderColor="gray.2"
|
|
191
|
+
borderRadius="sm"
|
|
192
|
+
>
|
|
193
|
+
<Text
|
|
194
|
+
fontFamily="secondary"
|
|
195
|
+
textTransform="uppercase"
|
|
196
|
+
fontSize="xxs"
|
|
197
|
+
color="gray.2"
|
|
198
|
+
>
|
|
199
|
+
{f.day}
|
|
200
|
+
</Text>
|
|
201
|
+
<Divider borderColor="gray.2" />
|
|
202
|
+
<Stack spacing="0" py="1" px="2" align="center">
|
|
203
|
+
{getWeatherIcon(f.icon, 16, colors.gray[3])}
|
|
204
|
+
<Text fontSize="sm" fontWeight="medium" mb="-1">
|
|
205
|
+
{f.high}°
|
|
206
|
+
</Text>
|
|
207
|
+
<WeatherDetailText>L {f.low}°</WeatherDetailText>
|
|
208
|
+
</Stack>
|
|
209
|
+
</VStack>
|
|
210
|
+
))}
|
|
211
|
+
</Flex>
|
|
212
|
+
)}
|
|
120
213
|
</Stack>
|
|
121
214
|
)
|
|
122
215
|
}
|
|
123
216
|
|
|
124
|
-
const WeatherDetailText = ({ children }: PropsWithChildren) =>
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
)
|
|
135
|
-
}
|
|
217
|
+
const WeatherDetailText = ({ children }: PropsWithChildren) => (
|
|
218
|
+
<Text
|
|
219
|
+
fontFamily="secondary"
|
|
220
|
+
textTransform="uppercase"
|
|
221
|
+
fontSize="xs"
|
|
222
|
+
lineHeight="1.2"
|
|
223
|
+
>
|
|
224
|
+
{children}
|
|
225
|
+
</Text>
|
|
226
|
+
)
|
|
136
227
|
|
|
137
228
|
const getWeatherIcon = (iconUrl: string, size = 48, color = colors.gray[4]) => {
|
|
229
|
+
if (!iconUrl) return null
|
|
138
230
|
const code = extractCodeFromUrl(iconUrl)
|
|
139
231
|
const IconComponent = weatherIconMap[code] || LuSun
|
|
140
232
|
return <IconComponent size={size} color={color} />
|