@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) => import("react/jsx-runtime").JSX.Element;
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, chakra, } from '@chakra-ui/react';
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
- return (_jsxs(Stack, { spacing: "base", p: "4", zIndex: "2", children: [_jsxs(Flex, { justify: "space-between", align: "center", children: [_jsx(Text, { fontSize: "sm", fontFamily: "secondary", textTransform: "uppercase", children: header.date }), _jsx(Text, { fontSize: "sm", fontFamily: "secondary", textTransform: "uppercase", children: header.location })] }), _jsxs(Stack, { spacing: "0", align: "end", children: [_jsxs(HStack, { align: "start", spacing: "2", children: [_jsxs(Text, { fontSize: "6xl", fontWeight: "medium", lineHeight: 1, color: "gray.4", children: [current.temp, "\u00B0"] }), getWeatherIcon(current.icon, 50)] }), _jsxs(Text, { fontSize: "md", children: [_jsx(chakra.span, { color: "gray.2", children: "H " }), " ", current.high, "\u00B0 \u00A0", _jsx(chakra.span, { color: "gray.2", children: "L " }), current.low, "\u00B0"] })] }), _jsx(Divider, { borderColor: "gray.2", borderStyle: "dashed" }), _jsxs(Flex, { justify: "space-between", children: [_jsxs(VStack, { spacing: "0", align: "start", children: [_jsx(WeatherDetailText, { children: "Precipitation" }), details.humidity && _jsx(WeatherDetailText, { children: "Humidity" }), _jsx(WeatherDetailText, { children: "Wind" })] }), _jsxs(VStack, { spacing: "0", align: "start", children: [_jsxs(WeatherDetailText, { children: [details.precipitation, "%"] }), details.humidity && (_jsxs(WeatherDetailText, { children: [details.humidity, "%"] })), _jsx(WeatherDetailText, { children: details.wind })] })] }), _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))) })] }));
18
- };
19
- const WeatherDetailText = ({ children }) => {
20
- return (_jsx(Text, { fontFamily: "secondary", textTransform: "uppercase", fontSize: "xs", lineHeight: "1.2", children: children }));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homefile/components-v2",
3
- "version": "2.15.1",
3
+ "version": "2.16.0",
4
4
  "author": "Homefile",
5
5
  "license": "UNLICENSED",
6
6
  "typings": "dist/index.d.ts",
@@ -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
- }: WeatherWidgetI) => {
45
- return (
46
- <Stack spacing="base" p="4" zIndex="2">
47
- <Flex justify="space-between" align="center">
48
- <Text fontSize="sm" fontFamily="secondary" textTransform="uppercase">
49
- {header.date}
50
- </Text>
51
- <Text fontSize="sm" fontFamily="secondary" textTransform="uppercase">
52
- {header.location}
53
- </Text>
54
- </Flex>
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
- <Stack spacing="0" align="end">
57
- <HStack align="start" spacing="2">
58
- <Text
59
- fontSize="6xl"
60
- fontWeight="medium"
61
- lineHeight={1}
62
- color="gray.4"
63
- >
64
- {current.temp}°
65
- </Text>
66
- {getWeatherIcon(current.icon, 50)}
67
- </HStack>
68
- <Text fontSize="md">
69
- <chakra.span color="gray.2">H </chakra.span> {current.high}° &nbsp;
70
- <chakra.span color="gray.2">L </chakra.span>
71
- {current.low}°
72
- </Text>
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
- <Divider borderColor="gray.2" borderStyle="dashed" />
76
-
77
- <Flex justify="space-between">
78
- <VStack spacing="0" align="start">
79
- <WeatherDetailText>Precipitation</WeatherDetailText>
80
- {details.humidity && <WeatherDetailText>Humidity</WeatherDetailText>}
81
- <WeatherDetailText>Wind</WeatherDetailText>
82
- </VStack>
83
- <VStack spacing="0" align="start">
84
- <WeatherDetailText>{details.precipitation}%</WeatherDetailText>
85
- {details.humidity && (
86
- <WeatherDetailText>{details.humidity}%</WeatherDetailText>
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
- <WeatherDetailText>{details.wind}</WeatherDetailText>
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
- {f.day}
121
+ {header.location}
108
122
  </Text>
109
- <Divider borderColor="gray.2" />
110
- <Stack spacing="0" py="1" px="2" align="center">
111
- {getWeatherIcon(f.icon, 16, colors.gray[3])}
112
- <Text fontSize="sm" fontWeight="medium" mb="-1">
113
- {f.high}°
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
- <WeatherDetailText>L {f.low}°</WeatherDetailText>
116
- </Stack>
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
- </Flex>
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
- return (
126
- <Text
127
- fontFamily="secondary"
128
- textTransform="uppercase"
129
- fontSize="xs"
130
- lineHeight="1.2"
131
- >
132
- {children}
133
- </Text>
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} />