@uniai-fe/uds-templates 0.5.9 → 0.5.11
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/README.md +7 -0
- package/dist/styles.css +139 -79
- package/package.json +1 -1
- package/src/cctv/components/pagination/list/Item.tsx +9 -0
- package/src/weather/apis/korea/client.ts +50 -15
- package/src/weather/apis/korea/server.ts +2 -2
- package/src/weather/apis/open-weather-map/client.ts +45 -15
- package/src/weather/apis/open-weather-map/server.ts +8 -2
- package/src/weather/components/icon/Address.tsx +7 -6
- package/src/weather/components/icon/Weather.tsx +4 -5
- package/src/weather/components/page-header/Address.tsx +36 -2
- package/src/weather/components/page-header/Alert.tsx +43 -16
- package/src/weather/components/page-header/Container.tsx +33 -5
- package/src/weather/components/page-header/Forecast.tsx +48 -7
- package/src/weather/components/page-header/NextDays.tsx +25 -22
- package/src/weather/components/page-header/Today.tsx +134 -91
- package/src/weather/hooks/useOpenWeatherMap.ts +22 -3
- package/src/weather/hooks/useWeatherKorea.ts +16 -4
- package/src/weather/hooks/useWeatherKoreaAlert.ts +13 -4
- package/src/weather/img/marker.svg +4 -0
- package/src/weather/index.scss +1 -0
- package/src/weather/styles/variables.scss +30 -0
- package/src/weather/styles/weather.scss +116 -109
- package/src/weather/types/base.ts +20 -0
- package/src/weather/types/index.ts +2 -0
- package/src/weather/types/page-header.ts +277 -0
- package/src/weather/types/provider.ts +34 -0
- package/src/weather/utils/index.ts +1 -0
- package/src/weather/utils/locale.ts +110 -0
- package/src/weather/utils/weather.ts +112 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
WeatherPageHeaderTexts,
|
|
3
|
+
WeatherProviderCapabilityRegistry,
|
|
4
|
+
WeatherProviderKey,
|
|
5
|
+
} from "../types";
|
|
6
|
+
|
|
7
|
+
const WEATHER_PAGE_HEADER_DEFAULT_TEXTS: WeatherPageHeaderTexts = {
|
|
8
|
+
currentWeatherLoading: "현재 날씨 불러오는 중...",
|
|
9
|
+
forecastWeatherLoading: "예보 날씨 불러오는 중...",
|
|
10
|
+
alertLoading: "특보 불러오는 중...",
|
|
11
|
+
currentLabel: "현재",
|
|
12
|
+
maxTemperatureLabel: "최고:",
|
|
13
|
+
minTemperatureLabel: "최저:",
|
|
14
|
+
humidityLabel: "습도",
|
|
15
|
+
tomorrowLabel: "내일",
|
|
16
|
+
dayAfterTomorrowLabel: "모레",
|
|
17
|
+
weatherIconAlt: "날씨 아이콘",
|
|
18
|
+
addressIconAlt: "날씨 위치",
|
|
19
|
+
conditionFallbackLabel: "-",
|
|
20
|
+
alertPreliminaryLevelLabel: "예비특보",
|
|
21
|
+
alertWatchLevelLabel: "주의보",
|
|
22
|
+
alertCancelCommandLabel: "해제",
|
|
23
|
+
alertIssueCommandLabel: "발령",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const OPEN_WEATHER_MAP_LANG_BY_LOCALE: Record<string, string> = {
|
|
27
|
+
ko: "kr",
|
|
28
|
+
en: "en",
|
|
29
|
+
ja: "ja",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const WEATHER_DEFAULT_PROVIDER: WeatherProviderKey = "openWeatherMap";
|
|
33
|
+
|
|
34
|
+
const WEATHER_PROVIDER_BY_LOCALE_KEY: Record<string, WeatherProviderKey> = {
|
|
35
|
+
ko: "kma",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const WEATHER_PROVIDER_CAPABILITIES: WeatherProviderCapabilityRegistry =
|
|
39
|
+
{
|
|
40
|
+
kma: {
|
|
41
|
+
now: true,
|
|
42
|
+
forecast: true,
|
|
43
|
+
alert: true,
|
|
44
|
+
},
|
|
45
|
+
openWeatherMap: {
|
|
46
|
+
now: true,
|
|
47
|
+
forecast: true,
|
|
48
|
+
alert: false,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Weather Locale; locale 기준 키 정규화
|
|
54
|
+
* @param {string} [locale] locale 코드
|
|
55
|
+
* @return {string} 전달된 locale의 기본 언어 코드
|
|
56
|
+
* @example
|
|
57
|
+
* getWeatherLocaleKey("ko-KR")
|
|
58
|
+
*/
|
|
59
|
+
export const getWeatherLocaleKey = (locale?: string): string =>
|
|
60
|
+
locale?.trim().toLowerCase().split(/[-_]/)[0] || "ko";
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Weather Locale; locale 기준 provider 선택
|
|
64
|
+
* @param {string} [locale] locale 코드
|
|
65
|
+
* @return {WeatherProviderKey} 현재 locale에 사용할 weather provider
|
|
66
|
+
* @example
|
|
67
|
+
* resolveWeatherProvider("ko-KR")
|
|
68
|
+
*/
|
|
69
|
+
export const resolveWeatherProvider = (locale?: string): WeatherProviderKey => {
|
|
70
|
+
const localeKey = getWeatherLocaleKey(locale);
|
|
71
|
+
|
|
72
|
+
return WEATHER_PROVIDER_BY_LOCALE_KEY[localeKey] ?? WEATHER_DEFAULT_PROVIDER;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Weather Locale; KMA provider 사용 여부
|
|
77
|
+
* @param {string} [locale] locale 코드
|
|
78
|
+
* @return {boolean} KMA provider 대상 locale 여부
|
|
79
|
+
* @example
|
|
80
|
+
* isKoreaWeatherLocale("ko-KR")
|
|
81
|
+
*/
|
|
82
|
+
export const isKoreaWeatherLocale = (locale?: string): boolean =>
|
|
83
|
+
resolveWeatherProvider(locale) === "kma";
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Weather Locale; page-header 텍스트 병합
|
|
87
|
+
* @param {Partial<WeatherPageHeaderTexts>} [texts] 서비스에서 주입한 텍스트
|
|
88
|
+
* @return {WeatherPageHeaderTexts} 기본 fallback과 서비스 주입 텍스트를 병합한 텍스트
|
|
89
|
+
* @example
|
|
90
|
+
* resolveWeatherPageHeaderTexts({ humidityLabel: "습도" })
|
|
91
|
+
*/
|
|
92
|
+
export const resolveWeatherPageHeaderTexts = (
|
|
93
|
+
texts?: Partial<WeatherPageHeaderTexts>,
|
|
94
|
+
): WeatherPageHeaderTexts => ({
|
|
95
|
+
...WEATHER_PAGE_HEADER_DEFAULT_TEXTS,
|
|
96
|
+
...texts,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Weather Locale; OpenWeatherMap API lang 값 변환
|
|
101
|
+
* @param {string} [locale] locale 코드
|
|
102
|
+
* @return {string} OpenWeatherMap lang 파라미터
|
|
103
|
+
* @example
|
|
104
|
+
* getOpenWeatherMapLang("ko")
|
|
105
|
+
*/
|
|
106
|
+
export const getOpenWeatherMapLang = (locale?: string): string => {
|
|
107
|
+
const localeKey = getWeatherLocaleKey(locale);
|
|
108
|
+
|
|
109
|
+
return OPEN_WEATHER_MAP_LANG_BY_LOCALE[localeKey] ?? localeKey;
|
|
110
|
+
};
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
API_Res_WeatherKoreaBase,
|
|
3
3
|
API_Res_WeatherKoreaForecast,
|
|
4
|
+
API_Res_WeatherKoreaNextDays,
|
|
4
5
|
API_Res_WeatherKoreaNow,
|
|
5
6
|
KMA_Res_WeatherItem,
|
|
6
7
|
KMA_Res_WeatherForecast,
|
|
7
8
|
KMA_Res_WeatherNow,
|
|
9
|
+
OWM_Res_Weather_Forecast,
|
|
10
|
+
OWM_Res_Weather_Forecast_Item,
|
|
8
11
|
} from "../types";
|
|
9
12
|
import { getMoments } from "./date-time";
|
|
10
13
|
|
|
@@ -217,6 +220,115 @@ const getWeatherCondition = ({
|
|
|
217
220
|
};
|
|
218
221
|
};
|
|
219
222
|
|
|
223
|
+
/**
|
|
224
|
+
* 날씨 도구; OpenWeatherMap 아이콘 코드를 weather icon 코드로 변환
|
|
225
|
+
* @param {string} [icon] OpenWeatherMap icon 코드
|
|
226
|
+
* @return {string} weather icon 코드
|
|
227
|
+
*/
|
|
228
|
+
export const getOpenWeatherMapConditionCode = (icon?: string): string => {
|
|
229
|
+
switch (icon) {
|
|
230
|
+
case "01d":
|
|
231
|
+
case "01n":
|
|
232
|
+
return "sky-1";
|
|
233
|
+
case "02d":
|
|
234
|
+
case "02n":
|
|
235
|
+
case "03d":
|
|
236
|
+
case "03n":
|
|
237
|
+
case "04d":
|
|
238
|
+
case "04n":
|
|
239
|
+
return "sky-2";
|
|
240
|
+
case "09d":
|
|
241
|
+
case "09n":
|
|
242
|
+
return "drop-rain-3";
|
|
243
|
+
case "10d":
|
|
244
|
+
case "10n":
|
|
245
|
+
return "drop-rain-1";
|
|
246
|
+
case "11d":
|
|
247
|
+
case "11n":
|
|
248
|
+
return "drop-rain-thunder";
|
|
249
|
+
case "13d":
|
|
250
|
+
case "13n":
|
|
251
|
+
return "drop-snow";
|
|
252
|
+
case "50d":
|
|
253
|
+
case "50n":
|
|
254
|
+
return "sky-4";
|
|
255
|
+
default:
|
|
256
|
+
return "sky-1";
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const getLocalDateKey = (date: Date): string => {
|
|
261
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
262
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
263
|
+
|
|
264
|
+
return `${date.getFullYear()}-${month}-${day}`;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const getForecastItemDateKey = (item: OWM_Res_Weather_Forecast_Item): string =>
|
|
268
|
+
item.dt_txt?.slice(0, 10) || getLocalDateKey(new Date(item.dt * 1000));
|
|
269
|
+
|
|
270
|
+
const toOpenWeatherMapNextDay = (
|
|
271
|
+
items: OWM_Res_Weather_Forecast_Item[],
|
|
272
|
+
): API_Res_WeatherKoreaNextDays | undefined => {
|
|
273
|
+
if (items.length === 0) return undefined;
|
|
274
|
+
|
|
275
|
+
const conditionItem =
|
|
276
|
+
items.find(item => item.dt_txt?.includes("12:00")) ||
|
|
277
|
+
items[Math.floor(items.length / 2)];
|
|
278
|
+
const temperatures = items.flatMap(item => [
|
|
279
|
+
item.main.temp_min,
|
|
280
|
+
item.main.temp_max,
|
|
281
|
+
item.main.temp,
|
|
282
|
+
]);
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
sky: null,
|
|
286
|
+
drop: null,
|
|
287
|
+
rainAmount: null,
|
|
288
|
+
snowAmount: null,
|
|
289
|
+
windSpeed: null,
|
|
290
|
+
condition: getOpenWeatherMapConditionCode(
|
|
291
|
+
conditionItem?.weather?.[0]?.icon,
|
|
292
|
+
),
|
|
293
|
+
conditionName: conditionItem?.weather?.[0]?.description || null,
|
|
294
|
+
max_temperature:
|
|
295
|
+
temperatures.length > 0 ? Math.round(Math.max(...temperatures)) : null,
|
|
296
|
+
min_temperature:
|
|
297
|
+
temperatures.length > 0 ? Math.round(Math.min(...temperatures)) : null,
|
|
298
|
+
};
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 날씨 도구; OpenWeatherMap 예보 응답을 page-header next days 형태로 변환
|
|
303
|
+
* @param {OWM_Res_Weather_Forecast} [forecast] OpenWeatherMap 예보 응답
|
|
304
|
+
* @return {{ day_1?: API_Res_WeatherKoreaNextDays; day_2?: API_Res_WeatherKoreaNextDays }} 내일/모레 예보
|
|
305
|
+
*/
|
|
306
|
+
export const getOpenWeatherMapNextDays = (
|
|
307
|
+
forecast?: OWM_Res_Weather_Forecast,
|
|
308
|
+
): {
|
|
309
|
+
day_1?: API_Res_WeatherKoreaNextDays;
|
|
310
|
+
day_2?: API_Res_WeatherKoreaNextDays;
|
|
311
|
+
} => {
|
|
312
|
+
if (!Array.isArray(forecast?.list)) return {};
|
|
313
|
+
|
|
314
|
+
const todayKey = getLocalDateKey(new Date());
|
|
315
|
+
const grouped = forecast.list.reduce<
|
|
316
|
+
Record<string, OWM_Res_Weather_Forecast_Item[]>
|
|
317
|
+
>((days, item) => {
|
|
318
|
+
const dateKey = getForecastItemDateKey(item);
|
|
319
|
+
if (dateKey === todayKey) return days;
|
|
320
|
+
|
|
321
|
+
days[dateKey] = [...(days[dateKey] || []), item];
|
|
322
|
+
return days;
|
|
323
|
+
}, {});
|
|
324
|
+
const [day1Key, day2Key] = Object.keys(grouped).sort();
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
day_1: day1Key ? toOpenWeatherMapNextDay(grouped[day1Key]) : undefined,
|
|
328
|
+
day_2: day2Key ? toOpenWeatherMapNextDay(grouped[day2Key]) : undefined,
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
|
|
220
332
|
export function now(
|
|
221
333
|
outputResponse: API_Res_WeatherKoreaNow,
|
|
222
334
|
apiResponse: KMA_Res_WeatherNow,
|