@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.
Files changed (43) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +4 -0
  3. package/dist/maps/ambient.d.ts +2 -0
  4. package/dist/maps/ambient.js +25 -0
  5. package/dist/maps/current-conditions.d.ts +2 -0
  6. package/dist/maps/current-conditions.js +23 -0
  7. package/dist/skin/index.d.ts +2 -0
  8. package/dist/skin/index.js +9 -0
  9. package/dist/skins/ambient/index.d.ts +2 -0
  10. package/dist/skins/ambient/index.js +9 -0
  11. package/dist/skins/ambient/maps.d.ts +2 -0
  12. package/dist/skins/ambient/maps.js +87 -0
  13. package/dist/skins/types.d.ts +4 -0
  14. package/dist/skins/types.js +1 -0
  15. package/dist/types/index.d.ts +4 -0
  16. package/dist/types/index.js +1 -0
  17. package/dist/utils/index.d.ts +3 -0
  18. package/dist/utils/index.js +3 -0
  19. package/dist/utils/time-of-day.d.ts +7 -0
  20. package/dist/utils/time-of-day.js +18 -0
  21. package/dist/utils/timeOfDay.d.ts +7 -0
  22. package/dist/utils/timeOfDay.js +18 -0
  23. package/dist/utils/weather-color.d.ts +2 -0
  24. package/dist/utils/weather-color.js +22 -0
  25. package/dist/utils/weatherColor.d.ts +2 -0
  26. package/dist/utils/weatherColor.js +8 -0
  27. package/dist/utils/wind.d.ts +1 -0
  28. package/dist/utils/wind.js +23 -0
  29. package/jest.config.ts +14 -0
  30. package/package.json +23 -0
  31. package/src/index.ts +6 -0
  32. package/src/maps/current-conditions.ts +45 -0
  33. package/src/skin/index.ts +11 -0
  34. package/src/types/index.ts +5 -0
  35. package/src/utils/index.ts +3 -0
  36. package/src/utils/time-of-day.ts +30 -0
  37. package/src/utils/weather-color.ts +24 -0
  38. package/src/utils/wind.ts +13 -0
  39. package/tests/maps/current-conditions.ts +80 -0
  40. package/tests/utils/time-of-day.test.ts +44 -0
  41. package/tests/utils/weather-color.test.ts +28 -0
  42. package/tests/utils/wind.test.ts +8 -0
  43. package/tsconfig.json +14 -0
@@ -0,0 +1,2 @@
1
+ import type { CircadianTokens } from "@bravotango/circadian-ui";
2
+ export declare const applyCircadianSkin: (tokens: CircadianTokens) => void;
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { currentConditionsSkin } from "./skin/";
2
+ export const applyCircadianSkin = (tokens) => {
3
+ currentConditionsSkin.apply(tokens);
4
+ };
@@ -0,0 +1,2 @@
1
+ import { CircadianTokens } from "@bravotango/circadian-ui";
2
+ export declare function mapAmbient(tokens: CircadianTokens): Record<string, string>;
@@ -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,2 @@
1
+ import { CircadianTokens } from "@bravotango/circadian-ui";
2
+ export declare const currentConditions: (tokens: CircadianTokens) => Record<string, string>;
@@ -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,2 @@
1
+ import type { CircadianSkin } from "../types";
2
+ export declare const currentConditionsSkin: CircadianSkin;
@@ -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,2 @@
1
+ import type { CircadianSkin } from "../../types";
2
+ export declare const ambientSkin: CircadianSkin;
@@ -0,0 +1,9 @@
1
+ import { mapAmbient } from "../../maps/ambient";
2
+ export const ambientSkin = {
3
+ apply(tokens) {
4
+ const vars = mapAmbient(tokens);
5
+ Object.entries(vars).forEach(([key, value]) => {
6
+ document.documentElement.style.setProperty(key, value);
7
+ });
8
+ },
9
+ };
@@ -0,0 +1,2 @@
1
+ import { CircadianTokens } from "@bravotango/circadian-ui";
2
+ export declare function mapAmbient(tokens: CircadianTokens): Record<string, string>;
@@ -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,4 @@
1
+ import { CircadianTokens } from "@bravotango/circadian-ui";
2
+ export type CircadianSkin = {
3
+ apply(tokens: CircadianTokens): void;
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import { CircadianTokens } from "@bravotango/circadian-ui";
2
+ export type CircadianSkin = {
3
+ apply(tokens: CircadianTokens): void;
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export * from "./time-of-day";
2
+ export * from "./weather-color";
3
+ export * from "./wind";
@@ -0,0 +1,3 @@
1
+ export * from "./time-of-day";
2
+ export * from "./weather-color";
3
+ export * from "./wind";
@@ -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,2 @@
1
+ import { Condition } from "@bravotango/circadian-ui";
2
+ export declare const getWeatherColor: (condition: Condition) => string;
@@ -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,2 @@
1
+ import type { Condition } from "../types";
2
+ export declare function getWeatherColor(condition: Condition): string;
@@ -0,0 +1,8 @@
1
+ export function getWeatherColor(condition) {
2
+ switch (condition.toLowerCase()) {
3
+ case "clouds":
4
+ return "gray";
5
+ default:
6
+ return "gray";
7
+ }
8
+ }
@@ -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,6 @@
1
+ // circadian-skins/src/index.ts
2
+ import type { CircadianTokens } from "@bravotango/circadian-ui";
3
+ import { currentConditionsSkin } from "./skin/";
4
+ export const applyCircadianSkin = (tokens: CircadianTokens) => {
5
+ currentConditionsSkin.apply(tokens);
6
+ };
@@ -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,5 @@
1
+ import { CircadianTokens } from "@bravotango/circadian-ui";
2
+
3
+ export type CircadianSkin = {
4
+ apply(tokens: CircadianTokens): void;
5
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./time-of-day";
2
+ export * from "./weather-color";
3
+ export * from "./wind";
@@ -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
+ }