Merge 94aa3b845d
into cff54b73a4
This commit is contained in:
commit
d9edc442db
|
@ -2,6 +2,7 @@ import { mdiImagePlus } from "@mdi/js";
|
|||
import { LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { haStyle } from "../resources/styles";
|
||||
import { createImage, generateImageThumbnailUrl } from "../data/image_upload";
|
||||
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
|
||||
import {
|
||||
|
@ -60,13 +61,15 @@ export class HaPictureUpload extends LitElement {
|
|||
alt=${this.currentImageAltText ||
|
||||
this.hass.localize("ui.components.picture-upload.current_image_alt")}
|
||||
/>
|
||||
<ha-button
|
||||
@click=${this._handleChangeClick}
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.picture-upload.change_picture"
|
||||
)}
|
||||
>
|
||||
</ha-button>
|
||||
<div>
|
||||
<ha-button
|
||||
@click=${this._handleChangeClick}
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.picture-upload.change_picture"
|
||||
)}
|
||||
>
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
@ -134,32 +137,35 @@ export class HaPictureUpload extends LitElement {
|
|||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 240px;
|
||||
}
|
||||
ha-file-upload {
|
||||
height: 100%;
|
||||
}
|
||||
.center-vertical {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
.value {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 200px;
|
||||
margin-bottom: 4px;
|
||||
border-radius: var(--file-upload-image-border-radius);
|
||||
}
|
||||
`;
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 240px;
|
||||
}
|
||||
ha-file-upload {
|
||||
height: 100%;
|
||||
}
|
||||
.center-vertical {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
.value {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 200px;
|
||||
margin-bottom: 4px;
|
||||
border-radius: var(--file-upload-image-border-radius);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { ImageSelector } from "../../data/selector";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-textarea";
|
||||
import "../ha-textfield";
|
||||
import "../ha-picture-upload";
|
||||
import "../ha-radio";
|
||||
import type { HaPictureUpload } from "../ha-picture-upload";
|
||||
import { URL_PREFIX } from "../../data/image_upload";
|
||||
|
||||
@customElement("ha-selector-image")
|
||||
export class HaImageSelector extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public value?: any;
|
||||
|
||||
@property() public name?: string;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() public placeholder?: string;
|
||||
|
||||
@property() public helper?: string;
|
||||
|
||||
@property({ attribute: false }) public selector!: ImageSelector;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property({ type: Boolean }) public required = true;
|
||||
|
||||
@state() private showUpload = false;
|
||||
|
||||
protected firstUpdated(changedProps): void {
|
||||
super.firstUpdated(changedProps);
|
||||
|
||||
if (!this.value || this.value.startsWith(URL_PREFIX)) {
|
||||
this.showUpload = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<div>
|
||||
<label>
|
||||
${this.hass.localize("ui.components.selectors.image.select_image")}
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize("ui.components.selectors.image.upload")}
|
||||
>
|
||||
<ha-radio
|
||||
name="mode"
|
||||
value="upload"
|
||||
.checked=${this.showUpload}
|
||||
@change=${this._radioGroupPicked}
|
||||
></ha-radio>
|
||||
</ha-formfield>
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize("ui.components.selectors.image.url")}
|
||||
>
|
||||
<ha-radio
|
||||
name="mode"
|
||||
value="url"
|
||||
.checked=${!this.showUpload}
|
||||
@change=${this._radioGroupPicked}
|
||||
></ha-radio>
|
||||
</ha-formfield>
|
||||
</label>
|
||||
${!this.showUpload
|
||||
? html`
|
||||
<ha-textfield
|
||||
.name=${this.name}
|
||||
.value=${this.value || ""}
|
||||
.placeholder=${this.placeholder || ""}
|
||||
.helper=${this.helper}
|
||||
helperPersistent
|
||||
.disabled=${this.disabled}
|
||||
@input=${this._handleChange}
|
||||
.label=${this.label || ""}
|
||||
.required=${this.required}
|
||||
></ha-textfield>
|
||||
`
|
||||
: html`
|
||||
<ha-picture-upload
|
||||
.hass=${this.hass}
|
||||
.value=${this.value?.startsWith(URL_PREFIX) ? this.value : null}
|
||||
@change=${this._pictureChanged}
|
||||
></ha-picture-upload>
|
||||
`}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _radioGroupPicked(ev): void {
|
||||
this.showUpload = ev.target.value === "upload";
|
||||
}
|
||||
|
||||
private _pictureChanged(ev) {
|
||||
const value = (ev.target as HaPictureUpload).value;
|
||||
|
||||
fireEvent(this, "value-changed", { value: value ?? undefined });
|
||||
}
|
||||
|
||||
private _handleChange(ev) {
|
||||
let value = ev.target.value;
|
||||
if (this.value === value) {
|
||||
return;
|
||||
}
|
||||
if (value === "" && !this.required) {
|
||||
value = undefined;
|
||||
}
|
||||
|
||||
fireEvent(this, "value-changed", { value });
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
ha-textarea,
|
||||
ha-textfield {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-selector-image": HaImageSelector;
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ const LOAD_ELEMENTS = {
|
|||
statistic: () => import("./ha-selector-statistic"),
|
||||
file: () => import("./ha-selector-file"),
|
||||
floor: () => import("./ha-selector-floor"),
|
||||
image: () => import("./ha-selector-image"),
|
||||
label: () => import("./ha-selector-label"),
|
||||
language: () => import("./ha-selector-language"),
|
||||
navigation: () => import("./ha-selector-navigation"),
|
||||
|
|
|
@ -8,12 +8,26 @@ interface Image {
|
|||
id: string;
|
||||
}
|
||||
|
||||
export const URL_PREFIX = "/api/image/serve/";
|
||||
|
||||
export interface ImageMutableParams {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const getIdFromUrl = (url: string): string | undefined => {
|
||||
let id;
|
||||
if (url.startsWith(URL_PREFIX)) {
|
||||
id = url.substring(URL_PREFIX.length);
|
||||
const idx = id.indexOf("/");
|
||||
if (idx >= 0) {
|
||||
id = id.substring(0, idx);
|
||||
}
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
export const generateImageThumbnailUrl = (mediaId: string, size: number) =>
|
||||
`/api/image/serve/${mediaId}/${size}x${size}`;
|
||||
`${URL_PREFIX}${mediaId}/${size}x${size}`;
|
||||
|
||||
export const fetchImages = (hass: HomeAssistant) =>
|
||||
hass.callWS<Image[]>({ type: "image/list" });
|
||||
|
@ -50,5 +64,5 @@ export const updateImage = (
|
|||
export const deleteImage = (hass: HomeAssistant, id: string) =>
|
||||
hass.callWS({
|
||||
type: "image/delete",
|
||||
media_id: id,
|
||||
image_id: id,
|
||||
});
|
||||
|
|
|
@ -38,6 +38,7 @@ export type Selector =
|
|||
| LegacyEntitySelector
|
||||
| FileSelector
|
||||
| IconSelector
|
||||
| ImageSelector
|
||||
| LabelSelector
|
||||
| LanguageSelector
|
||||
| LocationSelector
|
||||
|
@ -255,6 +256,11 @@ export interface IconSelector {
|
|||
} | null;
|
||||
}
|
||||
|
||||
export interface ImageSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
image: {} | null;
|
||||
}
|
||||
|
||||
export interface LabelSelector {
|
||||
label: {
|
||||
multiple?: boolean;
|
||||
|
|
|
@ -24,7 +24,7 @@ const cardConfigStruct = assign(
|
|||
);
|
||||
|
||||
const SCHEMA = [
|
||||
{ name: "image", selector: { text: {} } },
|
||||
{ name: "image", selector: { image: {} } },
|
||||
{ name: "image_entity", selector: { entity: { domain: "image" } } },
|
||||
{ name: "alt_text", selector: { text: {} } },
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
|
|
|
@ -32,7 +32,7 @@ const cardConfigStruct = assign(
|
|||
const SCHEMA = [
|
||||
{ name: "entity", required: true, selector: { entity: {} } },
|
||||
{ name: "name", selector: { text: {} } },
|
||||
{ name: "image", selector: { text: {} } },
|
||||
{ name: "image", selector: { image: {} } },
|
||||
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
|
||||
{
|
||||
name: "",
|
||||
|
|
|
@ -35,7 +35,7 @@ const cardConfigStruct = assign(
|
|||
|
||||
const SCHEMA = [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{ name: "image", selector: { text: {} } },
|
||||
{ name: "image", selector: { image: {} } },
|
||||
{ name: "image_entity", selector: { entity: { domain: "image" } } },
|
||||
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
|
||||
{
|
||||
|
|
|
@ -82,6 +82,7 @@ export const haStyle = css`
|
|||
color: var(--error-color);
|
||||
}
|
||||
|
||||
ha-button.warning,
|
||||
mwc-button.warning {
|
||||
--mdc-theme-primary: var(--error-color);
|
||||
}
|
||||
|
|
|
@ -377,6 +377,11 @@
|
|||
"upload_failed": "Upload failed",
|
||||
"unknown_file": "Unknown file"
|
||||
},
|
||||
"image": {
|
||||
"select_image": "Select image",
|
||||
"upload": "Upload picture",
|
||||
"url": "Local path or web URL"
|
||||
},
|
||||
"location": {
|
||||
"latitude": "[%key:ui::panel::config::zone::detail::latitude%]",
|
||||
"longitude": "[%key:ui::panel::config::zone::detail::longitude%]",
|
||||
|
|
Loading…
Reference in New Issue