mirror of
https://github.com/home-assistant/frontend.git
synced 2026-02-04 01:10:33 -06:00
Refactor color handling to use CSS variables (#28021)
This commit is contained in:
parent
7fe9ae22f0
commit
eb6191ab3a
@ -1,64 +1,16 @@
|
||||
import memoizeOne from "memoize-one";
|
||||
import { theme2hex } from "./convert-color";
|
||||
|
||||
export const COLORS = [
|
||||
"#4269d0",
|
||||
"#f4bd4a",
|
||||
"#ff725c",
|
||||
"#6cc5b0",
|
||||
"#a463f2",
|
||||
"#ff8ab7",
|
||||
"#9c6b4e",
|
||||
"#97bbf5",
|
||||
"#01ab63",
|
||||
"#094bad",
|
||||
"#c99000",
|
||||
"#d84f3e",
|
||||
"#49a28f",
|
||||
"#048732",
|
||||
"#d96895",
|
||||
"#8043ce",
|
||||
"#7599d1",
|
||||
"#7a4c31",
|
||||
"#6989f4",
|
||||
"#ffd444",
|
||||
"#ff957c",
|
||||
"#8fe9d3",
|
||||
"#62cc71",
|
||||
"#ffadda",
|
||||
"#c884ff",
|
||||
"#badeff",
|
||||
"#bf8b6d",
|
||||
"#927acc",
|
||||
"#97ee3f",
|
||||
"#bf3947",
|
||||
"#9f5b00",
|
||||
"#f48758",
|
||||
"#8caed6",
|
||||
"#f2b94f",
|
||||
"#eff26e",
|
||||
"#e43872",
|
||||
"#d9b100",
|
||||
"#9d7a00",
|
||||
"#698cff",
|
||||
"#00d27e",
|
||||
"#d06800",
|
||||
"#009f82",
|
||||
"#c49200",
|
||||
"#cbe8ff",
|
||||
"#fecddf",
|
||||
"#c27eb6",
|
||||
"#8cd2ce",
|
||||
"#c4b8d9",
|
||||
"#f883b0",
|
||||
"#a49100",
|
||||
"#f48800",
|
||||
"#27d0df",
|
||||
"#a04a9b",
|
||||
];
|
||||
// Total number of colors defined in CSS variables (--color-1 through --color-54)
|
||||
export const COLORS_COUNT = 54;
|
||||
|
||||
export function getColorByIndex(index: number) {
|
||||
return COLORS[index % COLORS.length];
|
||||
export function getColorByIndex(
|
||||
index: number,
|
||||
style: CSSStyleDeclaration
|
||||
): string {
|
||||
// Wrap around using modulo to support unlimited indices
|
||||
const colorIndex = (index % COLORS_COUNT) + 1;
|
||||
return style.getPropertyValue(`--color-${colorIndex}`);
|
||||
}
|
||||
|
||||
export function getGraphColorByIndex(
|
||||
@ -68,15 +20,19 @@ export function getGraphColorByIndex(
|
||||
// The CSS vars for the colors use range 1..n, so we need to adjust the index from the internal 0..n color index range.
|
||||
const themeColor =
|
||||
style.getPropertyValue(`--graph-color-${index + 1}`) ||
|
||||
getColorByIndex(index);
|
||||
getColorByIndex(index, style);
|
||||
return theme2hex(themeColor);
|
||||
}
|
||||
|
||||
export const getAllGraphColors = memoizeOne(
|
||||
(style: CSSStyleDeclaration) =>
|
||||
COLORS.map((_color, index) => getGraphColorByIndex(index, style)),
|
||||
Array.from({ length: COLORS_COUNT }, (_, index) =>
|
||||
getGraphColorByIndex(index, style)
|
||||
),
|
||||
(newArgs: [CSSStyleDeclaration], lastArgs: [CSSStyleDeclaration]) =>
|
||||
// this is not ideal, but we need to memoize the colors
|
||||
newArgs[0].getPropertyValue("--graph-color-1") ===
|
||||
lastArgs[0].getPropertyValue("--graph-color-1")
|
||||
lastArgs[0].getPropertyValue("--graph-color-1") &&
|
||||
newArgs[0].getPropertyValue("--color-1") ===
|
||||
lastArgs[0].getPropertyValue("--color-1")
|
||||
);
|
||||
|
||||
@ -137,8 +137,12 @@ const getCalendarDate = (dateObj: any): string | undefined => {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getCalendars = (hass: HomeAssistant): Calendar[] =>
|
||||
Object.keys(hass.states)
|
||||
export const getCalendars = (
|
||||
hass: HomeAssistant,
|
||||
element: Element
|
||||
): Calendar[] => {
|
||||
const computedStyles = getComputedStyle(element);
|
||||
return Object.keys(hass.states)
|
||||
.filter(
|
||||
(eid) =>
|
||||
computeDomain(eid) === "calendar" &&
|
||||
@ -149,8 +153,9 @@ export const getCalendars = (hass: HomeAssistant): Calendar[] =>
|
||||
.map((eid, idx) => ({
|
||||
...hass.states[eid],
|
||||
name: computeStateName(hass.states[eid]),
|
||||
backgroundColor: getColorByIndex(idx),
|
||||
backgroundColor: getColorByIndex(idx, computedStyles),
|
||||
}));
|
||||
};
|
||||
|
||||
export const createCalendarEvent = (
|
||||
hass: HomeAssistant,
|
||||
|
||||
@ -87,7 +87,7 @@ class PanelCalendar extends LitElement {
|
||||
public willUpdate(changedProps: PropertyValues): void {
|
||||
super.willUpdate(changedProps);
|
||||
if (!this.hasUpdated) {
|
||||
this._calendars = getCalendars(this.hass);
|
||||
this._calendars = getCalendars(this.hass, this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,7 +243,7 @@ class PanelCalendar extends LitElement {
|
||||
manifest: await fetchIntegrationManifest(this.hass, "local_calendar"),
|
||||
dialogClosedCallback: ({ flowFinished }) => {
|
||||
if (flowFinished) {
|
||||
this._calendars = getCalendars(this.hass);
|
||||
this._calendars = getCalendars(this.hass, this);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@ -80,9 +80,10 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard {
|
||||
throw new Error("Entities need to be an array");
|
||||
}
|
||||
|
||||
const computedStyles = getComputedStyle(this);
|
||||
this._calendars = config!.entities.map((entity, idx) => ({
|
||||
entity_id: entity,
|
||||
backgroundColor: getColorByIndex(idx),
|
||||
backgroundColor: getColorByIndex(idx, computedStyles),
|
||||
}));
|
||||
|
||||
if (this._config?.entities !== config.entities) {
|
||||
|
||||
@ -394,7 +394,8 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
||||
if (color) {
|
||||
return color;
|
||||
}
|
||||
color = getColorByIndex(this._colorIndex);
|
||||
const computedStyles = getComputedStyle(this);
|
||||
color = getColorByIndex(this._colorIndex, computedStyles);
|
||||
this._colorIndex++;
|
||||
this._colorDict[entityId] = color;
|
||||
return color;
|
||||
|
||||
@ -92,6 +92,62 @@ export const colorStyles = css`
|
||||
--black-color: #000000;
|
||||
--white-color: #ffffff;
|
||||
|
||||
/* colors - used for graphs, calendars, maps, etc */
|
||||
--color-1: #4269d0;
|
||||
--color-2: #f4bd4a;
|
||||
--color-3: #ff725c;
|
||||
--color-4: #6cc5b0;
|
||||
--color-5: #a463f2;
|
||||
--color-6: #ff8ab7;
|
||||
--color-7: #9c6b4e;
|
||||
--color-8: #97bbf5;
|
||||
--color-9: #01ab63;
|
||||
--color-10: #094bad;
|
||||
--color-11: #c99000;
|
||||
--color-12: #d84f3e;
|
||||
--color-13: #49a28f;
|
||||
--color-14: #048732;
|
||||
--color-15: #d96895;
|
||||
--color-16: #8043ce;
|
||||
--color-17: #7599d1;
|
||||
--color-18: #7a4c31;
|
||||
--color-19: #6989f4;
|
||||
--color-20: #ffd444;
|
||||
--color-21: #ff957c;
|
||||
--color-22: #8fe9d3;
|
||||
--color-23: #62cc71;
|
||||
--color-24: #ffadda;
|
||||
--color-25: #c884ff;
|
||||
--color-26: #badeff;
|
||||
--color-27: #bf8b6d;
|
||||
--color-28: #927acc;
|
||||
--color-29: #97ee3f;
|
||||
--color-30: #bf3947;
|
||||
--color-31: #9f5b00;
|
||||
--color-32: #f48758;
|
||||
--color-33: #8caed6;
|
||||
--color-34: #f2b94f;
|
||||
--color-35: #eff26e;
|
||||
--color-36: #e43872;
|
||||
--color-37: #d9b100;
|
||||
--color-38: #9d7a00;
|
||||
--color-39: #698cff;
|
||||
--color-40: #00d27e;
|
||||
--color-41: #d06800;
|
||||
--color-42: #009f82;
|
||||
--color-43: #c49200;
|
||||
--color-44: #cbe8ff;
|
||||
--color-45: #fecddf;
|
||||
--color-46: #c27eb6;
|
||||
--color-47: #8cd2ce;
|
||||
--color-48: #c4b8d9;
|
||||
--color-49: #f883b0;
|
||||
--color-50: #a49100;
|
||||
--color-51: #f48800;
|
||||
--color-52: #27d0df;
|
||||
--color-53: #a04a9b;
|
||||
--color-54: #4269d0;
|
||||
|
||||
/* history colors */
|
||||
--history-unavailable-color: transparent;
|
||||
|
||||
|
||||
@ -2,34 +2,63 @@ import { describe, test, expect } from "vitest";
|
||||
import {
|
||||
getColorByIndex,
|
||||
getGraphColorByIndex,
|
||||
COLORS,
|
||||
COLORS_COUNT,
|
||||
} from "../../../src/common/color/colors";
|
||||
import { theme2hex } from "../../../src/common/color/convert-color";
|
||||
|
||||
describe("getColorByIndex", () => {
|
||||
test("return the correct color for a given index", () => {
|
||||
expect(getColorByIndex(0)).toBe(COLORS[0]);
|
||||
expect(getColorByIndex(10)).toBe(COLORS[10]);
|
||||
test("return the correct color from CSS variable", () => {
|
||||
const style = {
|
||||
getPropertyValue: (prop) => {
|
||||
if (prop === "--color-1") return "#4269d0";
|
||||
if (prop === "--color-11") return "#c99000";
|
||||
return "";
|
||||
},
|
||||
} as CSSStyleDeclaration;
|
||||
expect(getColorByIndex(0, style)).toBe(theme2hex("#4269d0"));
|
||||
expect(getColorByIndex(10, style)).toBe(theme2hex("#c99000"));
|
||||
});
|
||||
|
||||
test("wrap around if the index is greater than the length of COLORS", () => {
|
||||
expect(getColorByIndex(COLORS.length)).toBe(COLORS[0]);
|
||||
expect(getColorByIndex(COLORS.length + 4)).toBe(COLORS[4]);
|
||||
test("wrap around if the index is greater than the total count", () => {
|
||||
const style = {
|
||||
getPropertyValue: (prop) => {
|
||||
if (prop === "--color-1") return "#4269d0";
|
||||
if (prop === "--color-5") return "#a463f2";
|
||||
return "";
|
||||
},
|
||||
} as CSSStyleDeclaration;
|
||||
// Index 54 should wrap to color 1
|
||||
expect(getColorByIndex(COLORS_COUNT, style)).toBe(theme2hex("#4269d0"));
|
||||
// Index 58 should wrap to color 5
|
||||
expect(getColorByIndex(COLORS_COUNT + 4, style)).toBe(theme2hex("#a463f2"));
|
||||
});
|
||||
});
|
||||
|
||||
describe("getGraphColorByIndex", () => {
|
||||
test("return the correct theme color if it exists", () => {
|
||||
test("return color from --graph-color variable when it exists", () => {
|
||||
const style = {
|
||||
getPropertyValue: (prop) => (prop === "--graph-color-1" ? "#123456" : ""),
|
||||
} as CSSStyleDeclaration;
|
||||
expect(getGraphColorByIndex(0, style)).toBe(theme2hex("#123456"));
|
||||
});
|
||||
|
||||
test("return the default color if the theme color does not exist", () => {
|
||||
test("fallback to --color variable when --graph-color does not exist", () => {
|
||||
const style = {
|
||||
getPropertyValue: () => "",
|
||||
} as unknown as CSSStyleDeclaration;
|
||||
expect(getGraphColorByIndex(0, style)).toBe(theme2hex(COLORS[0]));
|
||||
getPropertyValue: (prop) => (prop === "--color-5" ? "#abcdef" : ""),
|
||||
} as CSSStyleDeclaration;
|
||||
// Index 4 should try --graph-color-5, then fallback to --color-5
|
||||
expect(getGraphColorByIndex(4, style)).toBe(theme2hex("#abcdef"));
|
||||
});
|
||||
|
||||
test("prefer --graph-color over --color when both exist", () => {
|
||||
const style = {
|
||||
getPropertyValue: (prop) => {
|
||||
if (prop === "--graph-color-1") return "#111111";
|
||||
if (prop === "--color-1") return "#222222";
|
||||
return "";
|
||||
},
|
||||
} as CSSStyleDeclaration;
|
||||
// Should prefer --graph-color-1
|
||||
expect(getGraphColorByIndex(0, style)).toBe(theme2hex("#111111"));
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user