diff --git a/.gitignore b/.gitignore index 9b1358e5..669dd93a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ cypress/videos/ cypress/screenshots/ .nyc_output/ coverage/ +stats.html diff --git a/README.md b/README.md index 9fda8388..9aee0027 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,19 @@ label_spacing: | ## Color Options `colors` is specified as an object containing one or more of the keys listed below and values that are valid CSS -colors. Invalid color values will be discarded and will trigger a warning. +colors or objects of foreground and/or background valid CSS colors. Invalid color values will be discarded and will +trigger a warning. + +### Color value format + +Colors may be specified as a valid CSS color string or as an object with one or more of the following fields, each +containing a valid CSS color string: + +- `foreground`: The color of the condition label text or icon +- `background`: The color of the bar background during that span + +If color is specified as a plain string, it will be used for background. Foreground color defaults to the primary text +color in your Home Assistant theme. Some conditions will default to whatever the value is of some other condition. For example, `fog` will default to whatever `cloudy` is. @@ -152,6 +164,9 @@ colors: sunny: '#bbccee' # note that hex colors must be quoted snowy-rainy: rgba(255, 255, 255, 0.8) # rgba works (and hsla too) exceptional: red # as do valid CSS color names + windy: + background: lightgray + foreground: '#000' ``` ### Wind Options diff --git a/cypress/e2e/config.cy.ts b/cypress/e2e/config.cy.ts index 57997f21..fbd8d6e3 100644 --- a/cypress/e2e/config.cy.ts +++ b/cypress/e2e/config.cy.ts @@ -98,8 +98,22 @@ describe('Config', () => { partlycloudy: 'rgb(0, 255, 0, 0, 0)', // too many params sunny: 'foo(240, 100%, 50%)', // wrong function "clear-night": 'blahblah', // invalid color name - // @ts-expect-error This is a test, so we're intentionally passing the wrong thing - foobar: 'rgb(0, 255, 0)' // invalid condition + foobar: 'rgb(0, 255, 0)', // invalid condition + windy: {}, // empty object + rainy: { + // @ts-expect-error This is a test, so we're intentionally passing the wrong thing + blah: 'blue' + }, + fog: { + background: 'blahblah' // invalid color name + }, + exceptional: { + foreground: '#12345678' // too many digits + }, + hail: { + background: 'foo(240, 100%, 50%)', // wrong function + foreground: 'rgb(0, 255, 0, 0)' // too many params + } } }); cy.get('hui-warning') @@ -108,11 +122,16 @@ describe('Config', () => { .should('have.length', 1) .its(0) .its('textContent') - .should('contain', 'cloudy: #FF000011') - .and('contain', 'partlycloudy: rgb(0, 255, 0, 0, 0)') - .and('contain', 'sunny: foo(240, 100%, 50%)') - .and('contain', 'clear-night: blahblah') - .and('contain', 'foobar: rgb(0, 255, 0)'); + .should('contain', 'cloudy: "#FF000011"') + .and('contain', 'partlycloudy: "rgb(0, 255, 0, 0, 0)"') + .and('contain', 'sunny: "foo(240, 100%, 50%)"') + .and('contain', 'clear-night: "blahblah"') + .and('contain', 'foobar: "rgb(0, 255, 0)"') + .and('contain', 'windy: {}') + .and('contain', 'rainy: {\n "blah": "blue"\n}') + .and('contain', 'fog: {\n "background": "blahblah"\n}') + .and('contain', 'exceptional: {\n "foreground": "#12345678"\n}') + .and('contain', 'hail: {\n "background": "foo(240, 100%, 50%)",\n "foreground": "rgb(0, 255, 0, 0)"\n}'); }); describe('Templates', () => { it('supports templated name', () => { diff --git a/cypress/e2e/weather-bar.cy.ts b/cypress/e2e/weather-bar.cy.ts index 3e1f7274..c90cd0f2 100644 --- a/cypress/e2e/weather-bar.cy.ts +++ b/cypress/e2e/weather-bar.cy.ts @@ -89,6 +89,39 @@ describe('Weather bar', () => { expect(cs.backgroundColor).to.eq(expectedCustomColors[i]); }); }); + + const expectedCustomObjectColors = [ + { bg: 'rgb(18, 52, 86)', fg: 'rgb(0, 0, 0)' }, + { bg: 'rgb(179, 219, 255)', fg: 'rgb(123, 45, 6)' }, + { bg: 'rgb(0, 255, 0)', fg: 'rgb(255, 0, 255)' }, + { bg: 'rgb(50, 205, 50)', fg: 'rgb(0, 0, 0)' } + ]; + + it('uses custom colors when specified as color objects', () => { + cy.configure({ + colors: { + cloudy: { + background: '#123456' + }, + partlycloudy: { + foreground: 'rgb(123, 45, 6)' + }, + sunny: { + background: 'hsl(120, 100%, 50%)', + foreground: 'magenta' + }, + "clear-night": 'limegreen' + } + }); + cy.get('weather-bar') + .shadow() + .find('div.bar > div') + .each((el, i) => { + const cs = window.getComputedStyle(el.get(0)); + expect(cs.backgroundColor).to.eq(expectedCustomObjectColors[i].bg); + expect(cs.color).to.eq(expectedCustomObjectColors[i].fg); + }); + }); describe('Labels', () => { it('shows text descriptions on condition blocks', () => { cy.get('weather-bar') diff --git a/package-lock.json b/package-lock.json index bb6dd10d..81584655 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,6 +47,7 @@ "rollup-plugin-serve": "^2.0.1", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.34.1", + "rollup-plugin-visualizer": "^5.8.3", "start-server-and-test": "^1.14.0", "typescript": "^4.9.3" } @@ -2467,6 +2468,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -2806,6 +2818,15 @@ "node": ">=0.10.0" } }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/define-properties": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", @@ -3908,6 +3929,15 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", @@ -4507,6 +4537,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4718,6 +4763,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -5430,6 +5487,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -5896,6 +5970,15 @@ "throttleit": "^1.0.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -6069,6 +6152,40 @@ "node": ">=10" } }, + "node_modules/rollup-plugin-visualizer": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.8.3.tgz", + "integrity": "sha512-QGJk4Bqe4AOat5AjipOh8esZH1nck5X2KFpf4VytUdSUuuuSwvIQZjMGgjcxe/zXexltqaXp5Vx1V3LmnQH15Q==", + "dev": true, + "dependencies": { + "open": "^8.4.0", + "source-map": "^0.7.4", + "yargs": "^17.5.1" + }, + "bin": { + "rollup-plugin-visualizer": "dist/bin/cli.js" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "rollup": "2.x || 3.x" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6938,12 +7055,48 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", @@ -8824,6 +8977,17 @@ "string-width": "^4.2.0" } }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -9100,6 +9264,12 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, "define-properties": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", @@ -9928,6 +10098,12 @@ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "get-intrinsic": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", @@ -10353,6 +10529,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -10495,6 +10677,15 @@ "call-bind": "^1.0.2" } }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -11044,6 +11235,17 @@ "mimic-fn": "^2.1.0" } }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, "opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -11383,6 +11585,12 @@ "throttleit": "^1.0.0" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -11510,6 +11718,25 @@ } } }, + "rollup-plugin-visualizer": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.8.3.tgz", + "integrity": "sha512-QGJk4Bqe4AOat5AjipOh8esZH1nck5X2KFpf4VytUdSUuuuSwvIQZjMGgjcxe/zXexltqaXp5Vx1V3LmnQH15Q==", + "dev": true, + "requires": { + "open": "^8.4.0", + "source-map": "^0.7.4", + "yargs": "^17.5.1" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + } + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -12156,12 +12383,39 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "dev": true + }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/package.json b/package.json index a441a3f3..5c749a3d 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "rollup-plugin-serve": "^2.0.1", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.34.1", + "rollup-plugin-visualizer": "^5.8.3", "start-server-and-test": "^1.14.0", "typescript": "^4.9.3" }, diff --git a/rollup.config.js b/rollup.config.js index 97d02f4c..0b6012eb 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -8,6 +8,7 @@ import { terser } from 'rollup-plugin-terser'; import serve from 'rollup-plugin-serve'; import json from '@rollup/plugin-json'; import replace from '@rollup/plugin-replace'; +import visualizer from 'rollup-plugin-visualizer'; import ignore from './rollup-plugins/ignore'; import { ignoreTextfieldFiles } from './elements/ignore/textfield'; import { ignoreSelectFiles } from './elements/ignore/select'; @@ -19,7 +20,7 @@ if (dev) console.log('Development build'); const serveopts = { contentBase: ['./dist'], host: '0.0.0.0', - port: 5000, + port: '5000', allowCrossOrigin: true, headers: { 'Access-Control-Allow-Origin': '*', @@ -50,6 +51,7 @@ const plugins = [ ignore({ files: [...ignoreTextfieldFiles, ...ignoreSelectFiles, ...ignoreSwitchFiles].map((file) => require.resolve(file)), }), + visualizer() ]; export default [ diff --git a/src/hourly-weather.ts b/src/hourly-weather.ts index 81df2f96..1c396342 100644 --- a/src/hourly-weather.ts +++ b/src/hourly-weather.ts @@ -18,7 +18,9 @@ import { isValidColorName, isValidHSL, isValidRGB } from 'is-valid-css-color'; import type { ColorConfig, + ColorDefinition, ColorMap, + ColorObject, ColorSettings, ConditionSpan, ForecastSegment, @@ -297,7 +299,7 @@ export class HourlyWeatherCard extends LitElement { ${isForecastDaily ? this._showWarning(this.localize('errors.daily_forecasts')) : ''} ${colorSettings.warnings.length ? - this._showWarning(this.localize('errors.invalid_colors') + colorSettings.warnings.join(', ')) : ''} + this._showWarning(this.localize('errors.invalid_colors') + ' ' + colorSettings.warnings.join(', ')) : ''} { - if (this.isValidColor(k, v)) - validColors.set(k as keyof ColorConfig, v); + if (this.isValidColorDefinition(k, v)) + validColors.set(k as keyof ColorConfig, HourlyWeatherCard.toColorObject(v)); else - warnings.push(`${k}: ${v}`); + warnings.push(`${k}: ${JSON.stringify(v, null, 2)}`); }); return { validColors, @@ -438,10 +440,22 @@ export class HourlyWeatherCard extends LitElement { }; } - private isValidColor(key: string, color: string): boolean { + private isValidColorDefinition(key: string, color: ColorDefinition): boolean { if (!(key in ICONS)) { return false; } + if (typeof color === 'string') { + if (!HourlyWeatherCard.isValidColor(color)) return false; + } else { + if (!color.background && !color.foreground) return false; + if (color.background && !HourlyWeatherCard.isValidColor(color.background)) return false; + if (color.foreground && !HourlyWeatherCard.isValidColor(color.foreground)) return false; + } + + return true; + } + + private static isValidColor(color: string): boolean { if (!(isValidRGB(color) || isValidColorName(color) || isValidHSL(color))) { @@ -451,6 +465,15 @@ export class HourlyWeatherCard extends LitElement { return true; } + private static toColorObject(color: string | ColorObject): ColorObject { + if (typeof color === 'string') { + return { + background: color + }; + } + return color; + } + private _handleAction(ev: ActionHandlerEvent): void { if (this.hass && this.config && ev.detail.action) { handleAction(this, this.hass, this.config, ev.detail.action); diff --git a/src/types.ts b/src/types.ts index f8e7fc36..1d7e780b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -31,22 +31,29 @@ export interface HourlyWeatherCardConfig extends LovelaceCardConfig { language?: string; } +export interface ColorObject { + foreground?: string; + background?: string; +} + +export type ColorDefinition = string | ColorObject; + export interface ColorConfig { - 'clear-night'?: string; - 'cloudy'?: string; - 'fog'?: string; - 'hail'?: string; - 'lightning'?: string; - 'lightning-rainy'?: string; - 'partlycloudy'?: string; - 'pouring'?: string; - 'rainy'?: string; - 'snowy'?: string; - 'snowy-rainy'?: string; - 'sunny'?: string; - 'windy'?: string; - 'windy-variant'?: string; - 'exceptional'?: string; + 'clear-night'?: ColorDefinition; + 'cloudy'?: ColorDefinition; + 'fog'?: ColorDefinition; + 'hail'?: ColorDefinition; + 'lightning'?: ColorDefinition; + 'lightning-rainy'?: ColorDefinition; + 'partlycloudy'?: ColorDefinition; + 'pouring'?: ColorDefinition; + 'rainy'?: ColorDefinition; + 'snowy'?: ColorDefinition; + 'snowy-rainy'?: ColorDefinition; + 'sunny'?: ColorDefinition; + 'windy'?: ColorDefinition; + 'windy-variant'?: ColorDefinition; + 'exceptional'?: ColorDefinition; } export interface ForecastSegment { @@ -84,7 +91,7 @@ export interface SegmentPrecipitation { precipitationAmount: string } -export type ColorMap = Map +export type ColorMap = Map export interface ColorSettings { validColors?: ColorMap, diff --git a/src/weather-bar.ts b/src/weather-bar.ts index 7b7cdf6f..cbf986d1 100644 --- a/src/weather-bar.ts +++ b/src/weather-bar.ts @@ -129,7 +129,10 @@ export class WeatherBar extends LitElement { if (!colors || colors.size === 0) return null; const vars: string[] = []; for (const [key, color] of colors.entries()) { - vars.push(`--color-${key}: ${color};`); + if (color.background) + vars.push(`--color-${key}: ${color.background};`); + if (color.foreground) + vars.push(`--color-${key}-foreground: ${color.foreground};`); } return html`