@bravotango/circadian-current-conditions 0.1.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.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/maps/ambient.d.ts +2 -0
- package/dist/maps/ambient.js +25 -0
- package/dist/maps/current-conditions.d.ts +2 -0
- package/dist/maps/current-conditions.js +23 -0
- package/dist/skin/index.d.ts +2 -0
- package/dist/skin/index.js +9 -0
- package/dist/skins/ambient/index.d.ts +2 -0
- package/dist/skins/ambient/index.js +9 -0
- package/dist/skins/ambient/maps.d.ts +2 -0
- package/dist/skins/ambient/maps.js +87 -0
- package/dist/skins/types.d.ts +4 -0
- package/dist/skins/types.js +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/time-of-day.d.ts +7 -0
- package/dist/utils/time-of-day.js +18 -0
- package/dist/utils/timeOfDay.d.ts +7 -0
- package/dist/utils/timeOfDay.js +18 -0
- package/dist/utils/weather-color.d.ts +2 -0
- package/dist/utils/weather-color.js +22 -0
- package/dist/utils/weatherColor.d.ts +2 -0
- package/dist/utils/weatherColor.js +8 -0
- package/dist/utils/wind.d.ts +1 -0
- package/dist/utils/wind.js +23 -0
- package/jest.config.ts +14 -0
- package/package.json +23 -0
- package/src/index.ts +6 -0
- package/src/maps/current-conditions.ts +45 -0
- package/src/skin/index.ts +11 -0
- package/src/types/index.ts +5 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/time-of-day.ts +30 -0
- package/src/utils/weather-color.ts +24 -0
- package/src/utils/wind.ts +13 -0
- package/tests/maps/current-conditions.ts +80 -0
- package/tests/utils/time-of-day.test.ts +44 -0
- package/tests/utils/weather-color.test.ts +28 -0
- package/tests/utils/wind.test.ts +8 -0
- package/tsconfig.json +14 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { windSpeedToDuration } from "../utils/wind";
|
|
2
|
+
import { getTimeOfDayColor, getConditionImage } from "../utils/timeOfDay";
|
|
3
|
+
import { getWeatherColor } from "../utils/weatherColor";
|
|
4
|
+
export function mapAmbient(tokens) {
|
|
5
|
+
const cssVars = {};
|
|
6
|
+
// Base weather image
|
|
7
|
+
cssVars["--circadian-condition-img"] =
|
|
8
|
+
`url("/circadian-assets/weather/ambient/${tokens.current.condition}.png")`;
|
|
9
|
+
// Time-of-day color
|
|
10
|
+
cssVars["--circadian-current-condition-time-of-day-color"] =
|
|
11
|
+
getTimeOfDayColor(tokens.timeOfDay, tokens.current.condition);
|
|
12
|
+
// Night clear special image
|
|
13
|
+
const nightImg = getConditionImage(tokens.timeOfDay, tokens.current.condition);
|
|
14
|
+
if (nightImg)
|
|
15
|
+
cssVars["--circadian-condition-img"] = `url("${nightImg}")`;
|
|
16
|
+
// Weather color
|
|
17
|
+
cssVars["--circadian-current-condition-color"] = getWeatherColor(tokens.current.condition);
|
|
18
|
+
// OpenWeather icon
|
|
19
|
+
cssVars["--circadian-current-condition-icon"] =
|
|
20
|
+
`url('https://openweathermap.org/img/wn/${tokens.current.icon}@2x.png')`;
|
|
21
|
+
// Wind
|
|
22
|
+
cssVars["--circadian-wind-speed"] = windSpeedToDuration(tokens.wind.speed);
|
|
23
|
+
cssVars["--circadian-wind-speed-text"] = tokens.wind.speed.toString();
|
|
24
|
+
return cssVars;
|
|
25
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getTimeOfDayColor, getConditionImage, getWeatherColor, windSpeedToDuration, } from "../utils";
|
|
2
|
+
export const currentConditions = (tokens) => {
|
|
3
|
+
const cssVars = {};
|
|
4
|
+
const cssPrefix = "--circadian-current-condition";
|
|
5
|
+
// Base weather image
|
|
6
|
+
cssVars[`${cssPrefix}-img`] =
|
|
7
|
+
`url("/circadian-assets/weather/${tokens.current.condition}.png")`;
|
|
8
|
+
// Time-of-day color
|
|
9
|
+
cssVars[`${cssPrefix}-time-of-day-color`] = getTimeOfDayColor(tokens.timeOfDay, tokens.current.condition);
|
|
10
|
+
// Current condition image
|
|
11
|
+
const currentConditionImage = getConditionImage(tokens.timeOfDay, tokens.current.condition);
|
|
12
|
+
if (currentConditionImage)
|
|
13
|
+
cssVars[`${cssPrefix}-img`] = `url("${currentConditionImage}")`;
|
|
14
|
+
// Weather color
|
|
15
|
+
cssVars[`${cssPrefix}-color`] = getWeatherColor(tokens.current.condition);
|
|
16
|
+
// OpenWeather icon
|
|
17
|
+
cssVars[`${cssPrefix}-icon`] =
|
|
18
|
+
`url('https://openweathermap.org/img/wn/${tokens.current.icon}@2x.png')`;
|
|
19
|
+
// Wind
|
|
20
|
+
cssVars[`${cssPrefix}-wind-speed`] = windSpeedToDuration(tokens.wind.speed);
|
|
21
|
+
cssVars[`${cssPrefix}-wind-speed-text`] = tokens.wind.speed.toString();
|
|
22
|
+
return cssVars;
|
|
23
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { currentConditions } from "../maps/current-conditions";
|
|
2
|
+
export const currentConditionsSkin = {
|
|
3
|
+
apply(tokens) {
|
|
4
|
+
const vars = currentConditions(tokens);
|
|
5
|
+
Object.entries(vars).forEach(([key, value]) => {
|
|
6
|
+
document.documentElement.style.setProperty(key, value);
|
|
7
|
+
});
|
|
8
|
+
},
|
|
9
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// circadian-skins/src/skins/ambient/maps.ts
|
|
2
|
+
export function mapAmbient(tokens) {
|
|
3
|
+
const cssVars = {};
|
|
4
|
+
cssVars["--circadian-condition-img"] =
|
|
5
|
+
`url("/circadian-assets/weather/ambient/${tokens.current.condition}.png")`;
|
|
6
|
+
// Time of day mapping
|
|
7
|
+
switch (tokens.timeOfDay) {
|
|
8
|
+
case "day":
|
|
9
|
+
if (tokens.current.condition === "clear") {
|
|
10
|
+
cssVars["--circadian-current-condition-time-of-day-color"] = "#3987fc";
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
cssVars["--circadian-current-condition-time-of-day-color"] = "#a5a5a5";
|
|
14
|
+
break;
|
|
15
|
+
case "night":
|
|
16
|
+
if (tokens.current.condition === "clear") {
|
|
17
|
+
cssVars["--circadian-current-condition-time-of-day-color"] = "#0a2b4d";
|
|
18
|
+
cssVars["--circadian-condition-img"] =
|
|
19
|
+
`url("/circadian-assets/weather/ambient/${tokens.current.condition}-night.png")`;
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
cssVars["--circadian-current-condition-time-of-day-color"] = "#141414";
|
|
23
|
+
break;
|
|
24
|
+
case "sunrise":
|
|
25
|
+
if (tokens.current.condition === "clear") {
|
|
26
|
+
cssVars["--circadian-current-condition-time-of-day-color"] = "#d64400";
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
cssVars["--circadian-current-condition-time-of-day-color"] = "#818181";
|
|
30
|
+
break;
|
|
31
|
+
case "sunset":
|
|
32
|
+
if (tokens.current.condition === "clear") {
|
|
33
|
+
cssVars["--circadian-current-condition-time-of-day-color"] = "#6f00d6";
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
cssVars["--circadian-current-condition-time-of-day-color"] = "#222222";
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
// Weather color
|
|
40
|
+
switch (tokens.current.condition.toLowerCase()) {
|
|
41
|
+
case "clouds":
|
|
42
|
+
cssVars["--circadian-current-condition-color"] = "gray";
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
cssVars["--circadian-current-condition-color"] = "gray";
|
|
46
|
+
}
|
|
47
|
+
// In your JS/TS mapping function
|
|
48
|
+
cssVars["--circadian-current-condition-icon"] =
|
|
49
|
+
`url('https://openweathermap.org/img/wn/${tokens.current.icon}@2x.png')`;
|
|
50
|
+
// Wind speed category using switch(true)
|
|
51
|
+
switch (true) {
|
|
52
|
+
case tokens.wind.speed > 50:
|
|
53
|
+
cssVars["--circadian-wind-speed"] = "2s";
|
|
54
|
+
break;
|
|
55
|
+
case tokens.wind.speed > 30:
|
|
56
|
+
cssVars["--circadian-wind-speed"] = "10s";
|
|
57
|
+
break;
|
|
58
|
+
case tokens.wind.speed > 20:
|
|
59
|
+
cssVars["--circadian-wind-speed"] = "15s";
|
|
60
|
+
break;
|
|
61
|
+
case tokens.wind.speed > 15:
|
|
62
|
+
cssVars["--circadian-wind-speed"] = "30s";
|
|
63
|
+
break;
|
|
64
|
+
case tokens.wind.speed > 10:
|
|
65
|
+
cssVars["--circadian-wind-speed"] = "100s";
|
|
66
|
+
break;
|
|
67
|
+
case tokens.wind.speed > 7:
|
|
68
|
+
cssVars["--circadian-wind-speed"] = "120s";
|
|
69
|
+
break;
|
|
70
|
+
case tokens.wind.speed > 5.5:
|
|
71
|
+
cssVars["--circadian-wind-speed"] = "175s";
|
|
72
|
+
break;
|
|
73
|
+
case tokens.wind.speed > 4:
|
|
74
|
+
cssVars["--circadian-wind-speed"] = "200s";
|
|
75
|
+
break;
|
|
76
|
+
case tokens.wind.speed > 2:
|
|
77
|
+
cssVars["--circadian-wind-speed"] = "250s";
|
|
78
|
+
break;
|
|
79
|
+
case tokens.wind.speed > 1:
|
|
80
|
+
cssVars["--circadian-wind-speed"] = "300s";
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
cssVars["--circadian-wind-speed"] = "0s";
|
|
84
|
+
}
|
|
85
|
+
cssVars["--circadian-wind-speed-text"] = tokens.wind.speed.toString();
|
|
86
|
+
return cssVars;
|
|
87
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Condition, TimeOfDay } from "@bravotango/circadian-ui";
|
|
2
|
+
export declare const TIME_OF_DAY_COLOR_MAP: Record<TimeOfDay, {
|
|
3
|
+
clear: string;
|
|
4
|
+
default: string;
|
|
5
|
+
}>;
|
|
6
|
+
export declare const getTimeOfDayColor: (timeOfDay: TimeOfDay, condition: Condition) => string;
|
|
7
|
+
export declare const getConditionImage: (timeOfDay: TimeOfDay, condition: Condition) => string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const TIME_OF_DAY_COLOR_MAP = {
|
|
2
|
+
day: { clear: "#3987fc", default: "#a5a5a5" },
|
|
3
|
+
night: { clear: "#0a2b4d", default: "#141414" },
|
|
4
|
+
sunrise: { clear: "#d64400", default: "#818181" },
|
|
5
|
+
sunset: { clear: "#6f00d6", default: "#222222" },
|
|
6
|
+
};
|
|
7
|
+
export const getTimeOfDayColor = (timeOfDay, condition) => {
|
|
8
|
+
const entry = TIME_OF_DAY_COLOR_MAP[timeOfDay];
|
|
9
|
+
if (!entry)
|
|
10
|
+
return "#000000"; // defensive fallback
|
|
11
|
+
return condition === "clear" ? entry.clear : entry.default;
|
|
12
|
+
};
|
|
13
|
+
export const getConditionImage = (timeOfDay, condition) => {
|
|
14
|
+
if (timeOfDay === "night") {
|
|
15
|
+
return `/circadian-assets/weather/${condition}-night.png`;
|
|
16
|
+
}
|
|
17
|
+
return `/circadian-assets/weather/${condition}.png`;
|
|
18
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TimeOfDay, Condition } from "../types";
|
|
2
|
+
export declare const TIME_OF_DAY_COLOR_MAP: Record<TimeOfDay, {
|
|
3
|
+
clear: string;
|
|
4
|
+
default: string;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function getTimeOfDayColor(timeOfDay: TimeOfDay, condition: Condition): string;
|
|
7
|
+
export declare function getConditionImage(timeOfDay: TimeOfDay, condition: Condition): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const TIME_OF_DAY_COLOR_MAP = {
|
|
2
|
+
day: { clear: "#3987fc", default: "#a5a5a5" },
|
|
3
|
+
night: { clear: "#0a2b4d", default: "#141414" },
|
|
4
|
+
sunrise: { clear: "#d64400", default: "#818181" },
|
|
5
|
+
sunset: { clear: "#6f00d6", default: "#222222" },
|
|
6
|
+
};
|
|
7
|
+
export function getTimeOfDayColor(timeOfDay, condition) {
|
|
8
|
+
const entry = TIME_OF_DAY_COLOR_MAP[timeOfDay];
|
|
9
|
+
if (!entry)
|
|
10
|
+
return "#000000"; // defensive fallback
|
|
11
|
+
return condition === "clear" ? entry.clear : entry.default;
|
|
12
|
+
}
|
|
13
|
+
export function getConditionImage(timeOfDay, condition) {
|
|
14
|
+
if (timeOfDay === "night") {
|
|
15
|
+
return `/circadian-assets/weather/ambient/${condition}-night.png`;
|
|
16
|
+
}
|
|
17
|
+
return `/circadian-assets/weather/ambient/${condition}.png`;
|
|
18
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const weatherColors = {
|
|
2
|
+
clear: "#FFD93D",
|
|
3
|
+
clouds: "#B0C4DE",
|
|
4
|
+
rain: "#4A90E2",
|
|
5
|
+
snow: "#E0F7FA",
|
|
6
|
+
thunderstorm: "#616161",
|
|
7
|
+
drizzle: "#76B5C5",
|
|
8
|
+
mist: "#CFCFCF",
|
|
9
|
+
smoke: "#A9A9A9",
|
|
10
|
+
haze: "#D3D3D3",
|
|
11
|
+
dust: "#D2B48C",
|
|
12
|
+
fog: "#BEBEBE",
|
|
13
|
+
sand: "#F4E1A1",
|
|
14
|
+
ash: "#B2B2B2",
|
|
15
|
+
squall: "#5A5A5A",
|
|
16
|
+
tornado: "#4B4B4B",
|
|
17
|
+
};
|
|
18
|
+
export const getWeatherColor = (condition) => {
|
|
19
|
+
var _a;
|
|
20
|
+
const key = condition.toLowerCase();
|
|
21
|
+
return (_a = weatherColors[key]) !== null && _a !== void 0 ? _a : "#B0C4DE"; // fallback gray for unknown
|
|
22
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const windSpeedToDuration: (speed: number) => string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const windSpeedToDuration = (speed) => {
|
|
2
|
+
if (speed >= 50)
|
|
3
|
+
return ".5s";
|
|
4
|
+
if (speed >= 30)
|
|
5
|
+
return "1s";
|
|
6
|
+
if (speed >= 20)
|
|
7
|
+
return "1.5s";
|
|
8
|
+
if (speed >= 15)
|
|
9
|
+
return "3s";
|
|
10
|
+
if (speed >= 10)
|
|
11
|
+
return "10s";
|
|
12
|
+
if (speed >= 7)
|
|
13
|
+
return "12s";
|
|
14
|
+
if (speed >= 5.5)
|
|
15
|
+
return "17.5s";
|
|
16
|
+
if (speed >= 4)
|
|
17
|
+
return "20s";
|
|
18
|
+
if (speed >= 2)
|
|
19
|
+
return "25s";
|
|
20
|
+
if (speed >= 1)
|
|
21
|
+
return "30s";
|
|
22
|
+
return "0s";
|
|
23
|
+
};
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Config } from "jest";
|
|
2
|
+
|
|
3
|
+
const config: Config = {
|
|
4
|
+
preset: "ts-jest",
|
|
5
|
+
testEnvironment: "node",
|
|
6
|
+
roots: ["<rootDir>/src", "<rootDir>/tests"],
|
|
7
|
+
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
|
|
8
|
+
testRegex: "(/tests/.*|(\\.|/)(test|spec))\\.(ts|tsx|js)$",
|
|
9
|
+
collectCoverage: true,
|
|
10
|
+
coverageDirectory: "coverage",
|
|
11
|
+
verbose: true
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default config;
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bravotango/circadian-current-conditions",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"description": "",
|
|
7
|
+
"keywords": [],
|
|
8
|
+
"author": "",
|
|
9
|
+
"license": "ISC",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@bravotango/circadian-ui": "0.1.1"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@types/jest": "^30.0.0",
|
|
15
|
+
"jest": "^30.2.0",
|
|
16
|
+
"ts-jest": "^29.4.6"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "jest",
|
|
20
|
+
"test:watch": "jest --watch",
|
|
21
|
+
"build": "tsc"
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { CircadianTokens } from "@bravotango/circadian-ui";
|
|
2
|
+
import {
|
|
3
|
+
getTimeOfDayColor,
|
|
4
|
+
getConditionImage,
|
|
5
|
+
getWeatherColor,
|
|
6
|
+
windSpeedToDuration,
|
|
7
|
+
} from "../utils";
|
|
8
|
+
|
|
9
|
+
export const currentConditions = (
|
|
10
|
+
tokens: CircadianTokens,
|
|
11
|
+
): Record<string, string> => {
|
|
12
|
+
const cssVars: Record<string, string> = {};
|
|
13
|
+
const cssPrefix = "--circadian-current-condition";
|
|
14
|
+
|
|
15
|
+
// Base weather image
|
|
16
|
+
cssVars[`${cssPrefix}-img`] =
|
|
17
|
+
`url("/circadian-assets/weather/${tokens.current.condition}.png")`;
|
|
18
|
+
|
|
19
|
+
// Time-of-day color
|
|
20
|
+
cssVars[`${cssPrefix}-time-of-day-color`] = getTimeOfDayColor(
|
|
21
|
+
tokens.timeOfDay,
|
|
22
|
+
tokens.current.condition,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// Current condition image
|
|
26
|
+
const currentConditionImage = getConditionImage(
|
|
27
|
+
tokens.timeOfDay,
|
|
28
|
+
tokens.current.condition,
|
|
29
|
+
);
|
|
30
|
+
if (currentConditionImage)
|
|
31
|
+
cssVars[`${cssPrefix}-img`] = `url("${currentConditionImage}")`;
|
|
32
|
+
|
|
33
|
+
// Weather color
|
|
34
|
+
cssVars[`${cssPrefix}-color`] = getWeatherColor(tokens.current.condition);
|
|
35
|
+
|
|
36
|
+
// OpenWeather icon
|
|
37
|
+
cssVars[`${cssPrefix}-icon`] =
|
|
38
|
+
`url('https://openweathermap.org/img/wn/${tokens.current.icon}@2x.png')`;
|
|
39
|
+
|
|
40
|
+
// Wind
|
|
41
|
+
cssVars[`${cssPrefix}-wind-speed`] = windSpeedToDuration(tokens.wind.speed);
|
|
42
|
+
cssVars[`${cssPrefix}-wind-speed-text`] = tokens.wind.speed.toString();
|
|
43
|
+
|
|
44
|
+
return cssVars;
|
|
45
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { currentConditions } from "../maps/current-conditions";
|
|
2
|
+
import type { CircadianSkin } from "../types";
|
|
3
|
+
|
|
4
|
+
export const currentConditionsSkin: CircadianSkin = {
|
|
5
|
+
apply(tokens) {
|
|
6
|
+
const vars = currentConditions(tokens);
|
|
7
|
+
Object.entries(vars).forEach(([key, value]) => {
|
|
8
|
+
document.documentElement.style.setProperty(key, value);
|
|
9
|
+
});
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Condition, TimeOfDay } from "@bravotango/circadian-ui";
|
|
2
|
+
|
|
3
|
+
export const TIME_OF_DAY_COLOR_MAP: Record<
|
|
4
|
+
TimeOfDay,
|
|
5
|
+
{ clear: string; default: string }
|
|
6
|
+
> = {
|
|
7
|
+
day: { clear: "#3987fc", default: "#a5a5a5" },
|
|
8
|
+
night: { clear: "#0a2b4d", default: "#141414" },
|
|
9
|
+
sunrise: { clear: "#d64400", default: "#818181" },
|
|
10
|
+
sunset: { clear: "#6f00d6", default: "#222222" },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const getTimeOfDayColor = (
|
|
14
|
+
timeOfDay: TimeOfDay,
|
|
15
|
+
condition: Condition,
|
|
16
|
+
): string => {
|
|
17
|
+
const entry = TIME_OF_DAY_COLOR_MAP[timeOfDay];
|
|
18
|
+
if (!entry) return "#000000"; // defensive fallback
|
|
19
|
+
return condition === "clear" ? entry.clear : entry.default;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const getConditionImage = (
|
|
23
|
+
timeOfDay: TimeOfDay,
|
|
24
|
+
condition: Condition,
|
|
25
|
+
): string => {
|
|
26
|
+
if (timeOfDay === "night") {
|
|
27
|
+
return `/circadian-assets/weather/${condition}-night.png`;
|
|
28
|
+
}
|
|
29
|
+
return `/circadian-assets/weather/${condition}.png`;
|
|
30
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Condition } from "@bravotango/circadian-ui";
|
|
2
|
+
|
|
3
|
+
const weatherColors: Record<string, string> = {
|
|
4
|
+
clear: "#FFD93D",
|
|
5
|
+
clouds: "#B0C4DE",
|
|
6
|
+
rain: "#4A90E2",
|
|
7
|
+
snow: "#E0F7FA",
|
|
8
|
+
thunderstorm: "#616161",
|
|
9
|
+
drizzle: "#76B5C5",
|
|
10
|
+
mist: "#CFCFCF",
|
|
11
|
+
smoke: "#A9A9A9",
|
|
12
|
+
haze: "#D3D3D3",
|
|
13
|
+
dust: "#D2B48C",
|
|
14
|
+
fog: "#BEBEBE",
|
|
15
|
+
sand: "#F4E1A1",
|
|
16
|
+
ash: "#B2B2B2",
|
|
17
|
+
squall: "#5A5A5A",
|
|
18
|
+
tornado: "#4B4B4B",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const getWeatherColor = (condition: Condition): string => {
|
|
22
|
+
const key = condition.toLowerCase();
|
|
23
|
+
return weatherColors[key] ?? "#B0C4DE"; // fallback gray for unknown
|
|
24
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const windSpeedToDuration = (speed: number): string => {
|
|
2
|
+
if (speed >= 50) return ".5s";
|
|
3
|
+
if (speed >= 30) return "1s";
|
|
4
|
+
if (speed >= 20) return "1.5s";
|
|
5
|
+
if (speed >= 15) return "3s";
|
|
6
|
+
if (speed >= 10) return "10s";
|
|
7
|
+
if (speed >= 7) return "12s";
|
|
8
|
+
if (speed >= 5.5) return "17.5s";
|
|
9
|
+
if (speed >= 4) return "20s";
|
|
10
|
+
if (speed >= 2) return "25s";
|
|
11
|
+
if (speed >= 1) return "30s";
|
|
12
|
+
return "0s";
|
|
13
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { CircadianTokens } from "@bravotango/circadian-ui";
|
|
2
|
+
import { currentConditions } from "../../src/maps/current-conditions";
|
|
3
|
+
|
|
4
|
+
const baseTokens: CircadianTokens = {
|
|
5
|
+
timeOfDay: "night",
|
|
6
|
+
season: "summer",
|
|
7
|
+
location: {
|
|
8
|
+
locationId: 1,
|
|
9
|
+
name: "Seattle",
|
|
10
|
+
coordinates: {
|
|
11
|
+
lat: 47.6062,
|
|
12
|
+
lon: -122.3321,
|
|
13
|
+
},
|
|
14
|
+
timezone: {
|
|
15
|
+
offsetSeconds: -28800,
|
|
16
|
+
offsetHours: -8,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
current: {
|
|
20
|
+
id: 800,
|
|
21
|
+
condition: "clear",
|
|
22
|
+
description: "clear sky",
|
|
23
|
+
icon: "01n",
|
|
24
|
+
temperature: 55,
|
|
25
|
+
},
|
|
26
|
+
wind: {
|
|
27
|
+
speed: 12,
|
|
28
|
+
directionFrom: "N",
|
|
29
|
+
degrees: 0,
|
|
30
|
+
precipitationDegree: 0,
|
|
31
|
+
gusts: 15,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe("currentConditions", () => {
|
|
36
|
+
it("produces correct CSS vars for night clear", () => {
|
|
37
|
+
const cssVars = currentConditions(baseTokens);
|
|
38
|
+
|
|
39
|
+
expect(cssVars["--circadian-current-condition-time-of-day-color"]).toBe(
|
|
40
|
+
"#0a2b4d",
|
|
41
|
+
);
|
|
42
|
+
expect(cssVars["--circadian-current-condition-img"]).toBe(
|
|
43
|
+
'url("/circadian-assets/weather/clear-night.png")',
|
|
44
|
+
);
|
|
45
|
+
expect(cssVars["--circadian-current-condition-color"]).toBe("#FFD93D");
|
|
46
|
+
expect(cssVars["--circadian-current-condition-icon"]).toBe(
|
|
47
|
+
"url('https://openweathermap.org/img/wn/01n@2x.png')",
|
|
48
|
+
);
|
|
49
|
+
expect(cssVars["--circadian-current-condition-wind-speed"]).toBe("10s"); // 12 mph → 100s
|
|
50
|
+
expect(cssVars["--circadian-current-condition-wind-speed-text"]).toBe("12");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("produces default image for day clear", () => {
|
|
54
|
+
const tokens: CircadianTokens = { ...baseTokens, timeOfDay: "day" };
|
|
55
|
+
const cssVars = currentConditions(tokens);
|
|
56
|
+
|
|
57
|
+
expect(cssVars["--circadian-current-condition-img"]).toBe(
|
|
58
|
+
'url("/circadian-assets/weather/clear.png")',
|
|
59
|
+
);
|
|
60
|
+
expect(cssVars["--circadian-current-condition-time-of-day-color"]).toBe(
|
|
61
|
+
"#3987fc",
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("produces default color for non-clear conditions", () => {
|
|
66
|
+
const tokens: CircadianTokens = {
|
|
67
|
+
...baseTokens,
|
|
68
|
+
current: { ...baseTokens.current, condition: "clouds" },
|
|
69
|
+
};
|
|
70
|
+
const cssVars = currentConditions(tokens);
|
|
71
|
+
|
|
72
|
+
expect(cssVars["--circadian-current-condition-time-of-day-color"]).toBe(
|
|
73
|
+
"#141414",
|
|
74
|
+
); // night default
|
|
75
|
+
expect(cssVars["--circadian-current-condition-color"]).toBe("#B0C4DE");
|
|
76
|
+
expect(cssVars["--circadian-current-condition-img"]).toBe(
|
|
77
|
+
'url("/circadian-assets/weather/clouds-night.png")',
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getTimeOfDayColor,
|
|
3
|
+
getConditionImage,
|
|
4
|
+
TIME_OF_DAY_COLOR_MAP,
|
|
5
|
+
} from "../../src/utils";
|
|
6
|
+
|
|
7
|
+
describe("getTimeOfDayColor", () => {
|
|
8
|
+
it("returns correct clear color for each time of day", () => {
|
|
9
|
+
expect(getTimeOfDayColor("day", "clear")).toBe(
|
|
10
|
+
TIME_OF_DAY_COLOR_MAP.day.clear,
|
|
11
|
+
);
|
|
12
|
+
expect(getTimeOfDayColor("night", "clear")).toBe(
|
|
13
|
+
TIME_OF_DAY_COLOR_MAP.night.clear,
|
|
14
|
+
);
|
|
15
|
+
expect(getTimeOfDayColor("sunrise", "clear")).toBe(
|
|
16
|
+
TIME_OF_DAY_COLOR_MAP.sunrise.clear,
|
|
17
|
+
);
|
|
18
|
+
expect(getTimeOfDayColor("sunset", "clear")).toBe(
|
|
19
|
+
TIME_OF_DAY_COLOR_MAP.sunset.clear,
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("returns default color for non-clear conditions", () => {
|
|
24
|
+
expect(getTimeOfDayColor("day", "clouds")).toBe(
|
|
25
|
+
TIME_OF_DAY_COLOR_MAP.day.default,
|
|
26
|
+
);
|
|
27
|
+
expect(getTimeOfDayColor("night", "rain")).toBe(
|
|
28
|
+
TIME_OF_DAY_COLOR_MAP.night.default,
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("returns fallback for invalid timeOfDay", () => {
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
expect(getTimeOfDayColor("midnight", "clear")).toBe("#000000");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("getConditionImage", () => {
|
|
39
|
+
it("returns night clear image", () => {
|
|
40
|
+
expect(getConditionImage("night", "clear")).toBe(
|
|
41
|
+
"/circadian-assets/weather/clear-night.png",
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Condition } from "@bravotango/circadian-ui";
|
|
2
|
+
import { getWeatherColor } from "../../src/utils";
|
|
3
|
+
|
|
4
|
+
describe("getWeatherColor", () => {
|
|
5
|
+
const WEATHER_COLOR_MAP: Record<Condition, string> = {
|
|
6
|
+
clear: "#FFD93D",
|
|
7
|
+
clouds: "#B0C4DE",
|
|
8
|
+
rain: "#4A90E2",
|
|
9
|
+
snow: "#E0F7FA",
|
|
10
|
+
thunderstorm: "#616161",
|
|
11
|
+
drizzle: "#76B5C5",
|
|
12
|
+
mist: "#CFCFCF",
|
|
13
|
+
smoke: "#A9A9A9",
|
|
14
|
+
haze: "#D3D3D3",
|
|
15
|
+
dust: "#D2B48C",
|
|
16
|
+
fog: "#BEBEBE",
|
|
17
|
+
sand: "#F4E1A1",
|
|
18
|
+
ash: "#B2B2B2",
|
|
19
|
+
squall: "#5A5A5A",
|
|
20
|
+
tornado: "#4B4B4B",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
it("returns correct color for each condition", () => {
|
|
24
|
+
Object.entries(WEATHER_COLOR_MAP).forEach(([condition, expectedColor]) => {
|
|
25
|
+
expect(getWeatherColor(condition as Condition)).toBe(expectedColor);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { windSpeedToDuration } from "../../src/utils";
|
|
2
|
+
|
|
3
|
+
describe("windSpeedToDuration", () => {
|
|
4
|
+
it("returns 0s for calm wind", () =>
|
|
5
|
+
expect(windSpeedToDuration(0)).toBe("0s"));
|
|
6
|
+
it("returns correct duration for 12 mph", () =>
|
|
7
|
+
expect(windSpeedToDuration(12)).toBe("10s"));
|
|
8
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2019",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"moduleResolution": "Node",
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|