-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a picture uploader to picture-card-editor #18695
Changes from all commits
50bf1d1
9afeadc
9c1ac5b
2de16e7
c41257b
893d2ef
84ec7be
2a88f52
aaaf3af
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -8,10 +8,24 @@ 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; | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+17
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding explicit type annotations to enhance code clarity and maintainability. For instance, - let id;
+ let id: string | undefined; Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
export const generateImageThumbnailUrl = ( | ||||||||||||||||||||||||||||||||||||||||||
mediaId: string, | ||||||||||||||||||||||||||||||||||||||||||
size?: number, | ||||||||||||||||||||||||||||||||||||||||||
|
@@ -61,5 +75,5 @@ export const updateImage = ( | |||||||||||||||||||||||||||||||||||||||||
export const deleteImage = (hass: HomeAssistant, id: string) => | ||||||||||||||||||||||||||||||||||||||||||
hass.callWS({ | ||||||||||||||||||||||||||||||||||||||||||
type: "image/delete", | ||||||||||||||||||||||||||||||||||||||||||
media_id: id, | ||||||||||||||||||||||||||||||||||||||||||
image_id: id, | ||||||||||||||||||||||||||||||||||||||||||
}); |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -40,6 +40,7 @@ export type Selector = | |||||||||||||||||||||
| FileSelector | ||||||||||||||||||||||
| IconSelector | ||||||||||||||||||||||
| LabelSelector | ||||||||||||||||||||||
| ImageSelector | ||||||||||||||||||||||
| LanguageSelector | ||||||||||||||||||||||
| LocationSelector | ||||||||||||||||||||||
| MediaSelector | ||||||||||||||||||||||
|
@@ -256,6 +257,11 @@ export interface IconSelector { | |||||||||||||||||||||
} | null; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
export interface ImageSelector { | ||||||||||||||||||||||
// eslint-disable-next-line @typescript-eslint/ban-types | ||||||||||||||||||||||
image: {} | null; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
Comment on lines
+260
to
+263
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Define a more specific type for - export interface ImageSelector {
- image: {} | null;
- }
+ export interface ImageSelector {
+ image: {
+ url?: string;
+ altText?: string;
+ } | null;
+ } Using Committable suggestion
Suggested change
|
||||||||||||||||||||||
|
||||||||||||||||||||||
export interface LabelSelector { | ||||||||||||||||||||||
label: { | ||||||||||||||||||||||
multiple?: boolean; | ||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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%]", | ||
|
@@ -412,6 +417,11 @@ | |
"manual": "Manual Entry" | ||
} | ||
}, | ||
"image": { | ||
"select_image": "Select image", | ||
"upload": "Upload picture", | ||
"url": "Local path or web URL" | ||
}, | ||
Comment on lines
+420
to
+424
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This section appears to be a duplicate of the earlier translation keys for image handling. Please verify and consider removing if it's unintentional. |
||
"text": { | ||
"show_password": "Show password", | ||
"hide_password": "Hide password" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new
HaImageSelector
component is well-implemented, providing a flexible UI component for image selection. It supports both upload and URL modes, which aligns with the PR's objectives. Consider specifying a more precise type thanany
for thevalue
property to enhance type safety.Committable suggestion