mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
Image aspect ratio (#1665)
* Allow user to specify an aspect ratio for various images * added a comment on what is supported * fixed typo * Fixed lint and test errors
This commit is contained in:
parent
a32809e14b
commit
ce3b53a920
3
.gitignore
vendored
3
.gitignore
vendored
@ -21,6 +21,9 @@ lib
|
||||
bin
|
||||
dist
|
||||
|
||||
# vscode
|
||||
.vscode
|
||||
|
||||
# Secrets
|
||||
.lokalise_token
|
||||
yarn-error.log
|
||||
|
24
src/common/util/parse-aspect-ratio.js
Normal file
24
src/common/util/parse-aspect-ratio.js
Normal file
@ -0,0 +1,24 @@
|
||||
export default function parseAspectRatio(input) {
|
||||
// Handle 16x9, 16:9, 1.78x1, 1.78:1, 1.78
|
||||
// Ignore everything else
|
||||
function parseOrThrow(number) {
|
||||
const parsed = parseFloat(number);
|
||||
if (isNaN(parsed)) throw new Error(`${number} is not a number`);
|
||||
return parsed;
|
||||
}
|
||||
try {
|
||||
if (input) {
|
||||
const arr = input.replace(':', 'x').split('x');
|
||||
if (arr.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return arr.length === 1 ?
|
||||
{ w: parseOrThrow(arr[0]), h: 1 } :
|
||||
{ w: parseOrThrow(arr[0]), h: parseOrThrow(arr[1]) };
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignore the error
|
||||
}
|
||||
return null;
|
||||
}
|
@ -58,6 +58,7 @@ class HuiPictureEntityCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
state-image="[[_config.state_image]]"
|
||||
camera-image="[[_getCameraImage(_config)]]"
|
||||
entity="[[_config.entity]]"
|
||||
aspect-ratio="[[_config.aspect_ratio]]"
|
||||
></hui-image>
|
||||
<template is="dom-if" if="[[_showNameAndState(_config)]]">
|
||||
<div class="footer both">
|
||||
|
@ -78,6 +78,7 @@ class HuiPictureGlanceCard extends NavigateMixin(LocalizeMixin(EventsMixin(Polym
|
||||
state-image="[[_config.state_image]]"
|
||||
camera-image="[[_config.camera_image]]"
|
||||
entity="[[_config.entity]]"
|
||||
aspect-ratio="[[_config.aspect_ratio]]"
|
||||
></hui-image>
|
||||
<div class="box">
|
||||
<template is="dom-if" if="[[_config.title]]">
|
||||
|
@ -5,6 +5,8 @@ import '@polymer/paper-toggle-button/paper-toggle-button.js';
|
||||
import { STATES_OFF } from '../../../common/const.js';
|
||||
import LocalizeMixin from '../../../mixins/localize-mixin.js';
|
||||
|
||||
import parseAspectRatio from '../../../common/util/parse-aspect-ratio.js';
|
||||
|
||||
const UPDATE_INTERVAL = 10000;
|
||||
const DEFAULT_FILTER = 'grayscale(100%)';
|
||||
|
||||
@ -13,8 +15,23 @@ const DEFAULT_FILTER = 'grayscale(100%)';
|
||||
*/
|
||||
class HuiImage extends LocalizeMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
${this.styleTemplate}
|
||||
<div id="wrapper">
|
||||
<img
|
||||
id="image"
|
||||
src="[[_imageSrc]]"
|
||||
on-error="_onImageError"
|
||||
on-load="_onImageLoad" />
|
||||
<div id="brokenImage"></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styleTemplate() {
|
||||
return html`
|
||||
<style>
|
||||
|
||||
img {
|
||||
display: block;
|
||||
height: auto;
|
||||
@ -30,19 +47,26 @@ class HuiImage extends LocalizeMixin(PolymerElement) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ratio {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 0
|
||||
}
|
||||
|
||||
.ratio img, .ratio div {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#brokenImage {
|
||||
background: grey url('/static/images/image-broken.svg') center/36px no-repeat;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<img
|
||||
id="image"
|
||||
src="[[_imageSrc]]"
|
||||
on-error="_onImageError"
|
||||
on-load="_onImageLoad" />
|
||||
<div id="brokenImage"></div>
|
||||
`;
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
@ -55,6 +79,7 @@ class HuiImage extends LocalizeMixin(PolymerElement) {
|
||||
image: String,
|
||||
stateImage: Object,
|
||||
cameraImage: String,
|
||||
aspectRatio: String,
|
||||
filter: String,
|
||||
stateFilter: Object,
|
||||
_imageSrc: String
|
||||
@ -62,7 +87,7 @@ class HuiImage extends LocalizeMixin(PolymerElement) {
|
||||
}
|
||||
|
||||
static get observers() {
|
||||
return ['_configChanged(image, stateImage, cameraImage)'];
|
||||
return ['_configChanged(image, stateImage, cameraImage, aspectRatio)'];
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@ -77,7 +102,14 @@ class HuiImage extends LocalizeMixin(PolymerElement) {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
|
||||
_configChanged(image, stateImage, cameraImage) {
|
||||
_configChanged(image, stateImage, cameraImage, aspectRatio) {
|
||||
const ratio = parseAspectRatio(aspectRatio);
|
||||
|
||||
if (ratio && ratio.w > 0 && ratio.h > 0) {
|
||||
this.$.wrapper.style.paddingBottom = `${((100 * ratio.h) / ratio.w).toFixed(2)}%`;
|
||||
this.$.wrapper.classList.add('ratio');
|
||||
}
|
||||
|
||||
if (cameraImage) {
|
||||
this._updateCameraImageSrc();
|
||||
} else if (image && !stateImage) {
|
||||
@ -88,14 +120,18 @@ class HuiImage extends LocalizeMixin(PolymerElement) {
|
||||
_onImageError() {
|
||||
this._imageSrc = null;
|
||||
this.$.image.classList.add('hidden');
|
||||
this.$.brokenImage.style.setProperty('height', `${this._lastImageHeight || '100'}px`);
|
||||
if (!this.$.wrapper.classList.contains('ratio')) {
|
||||
this.$.brokenImage.style.setProperty('height', `${this._lastImageHeight || '100'}px`);
|
||||
}
|
||||
this.$.brokenImage.classList.remove('hidden');
|
||||
}
|
||||
|
||||
_onImageLoad() {
|
||||
this.$.image.classList.remove('hidden');
|
||||
this.$.brokenImage.classList.add('hidden');
|
||||
this._lastImageHeight = this.$.image.offsetHeight;
|
||||
if (!this.$.wrapper.classList.contains('ratio')) {
|
||||
this._lastImageHeight = this.$.image.offsetHeight;
|
||||
}
|
||||
}
|
||||
|
||||
_hassChanged(hass) {
|
||||
|
@ -13,11 +13,11 @@ class HuiImageElement extends ElementClickMixin(PolymerElement) {
|
||||
return html`
|
||||
<style>
|
||||
:host(.clickable) {
|
||||
cursor: pointer;
|
||||
}
|
||||
cursor: pointer;
|
||||
}
|
||||
hui-image {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<hui-image
|
||||
hass="[[hass]]"
|
||||
@ -28,6 +28,7 @@ class HuiImageElement extends ElementClickMixin(PolymerElement) {
|
||||
filter="[[_config.filter]]"
|
||||
state-filter="[[_config.state_filter]]"
|
||||
title$="[[computeTooltip(hass, _config)]]"
|
||||
aspect-ratio="[[_config.aspect_ratio]]"
|
||||
></hui-image>
|
||||
`;
|
||||
}
|
||||
|
53
test-mocha/common/util/parse_aspect_ratio_test.js
Normal file
53
test-mocha/common/util/parse_aspect_ratio_test.js
Normal file
@ -0,0 +1,53 @@
|
||||
import assert from 'assert';
|
||||
|
||||
import parseAspectRatio from '../../../src/common/util/parse-aspect-ratio.js';
|
||||
|
||||
describe('parseAspectRatio', () => {
|
||||
const ratio16by9 = { w: 16, h: 9 };
|
||||
const ratio178 = { w: 1.78, h: 1 };
|
||||
|
||||
it('Parses 16x9', () => {
|
||||
const r = parseAspectRatio('16x9');
|
||||
assert.deepEqual(r, ratio16by9);
|
||||
});
|
||||
|
||||
it('Parses 16:9', () => {
|
||||
const r = parseAspectRatio('16:9');
|
||||
assert.deepEqual(r, ratio16by9);
|
||||
});
|
||||
|
||||
it('Parses 1.78x1', () => {
|
||||
const r = parseAspectRatio('1.78x1');
|
||||
assert.deepEqual(r, ratio178);
|
||||
});
|
||||
|
||||
it('Parses 1.78:1', () => {
|
||||
const r = parseAspectRatio('1.78:1');
|
||||
assert.deepEqual(r, ratio178);
|
||||
});
|
||||
|
||||
it('Parses 1.78', () => {
|
||||
const r = parseAspectRatio('1.78');
|
||||
assert.deepEqual(r, ratio178);
|
||||
});
|
||||
|
||||
it('Skips null states', () => {
|
||||
const r = parseAspectRatio(null);
|
||||
assert.equal(r, null);
|
||||
});
|
||||
|
||||
it('Skips empty states', () => {
|
||||
const r = parseAspectRatio(' ');
|
||||
assert.equal(r, null);
|
||||
});
|
||||
|
||||
it('Skips invalid input', () => {
|
||||
const r = parseAspectRatio('mary had a little lamb');
|
||||
assert.equal(r, null);
|
||||
});
|
||||
|
||||
it('Skips invalid, but close input', () => {
|
||||
const r = parseAspectRatio('mary:lamb');
|
||||
assert.equal(r, null);
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user