mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-02 08:33:31 +01:00
Migrate copy-textfield to input-copy (#30276)
This commit is contained in:
@@ -10,7 +10,6 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { haStyleScrollbar } from "../resources/styles";
|
||||
import { supportsFeature } from "../common/entity/supports-feature";
|
||||
import {
|
||||
runAssistPipeline,
|
||||
@@ -21,13 +20,14 @@ import {
|
||||
} from "../data/assist_pipeline";
|
||||
import { ConversationEntityFeature } from "../data/conversation";
|
||||
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
|
||||
import { haStyleScrollbar } from "../resources/styles";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { AudioRecorder } from "../util/audio-recorder";
|
||||
import { documentationUrl } from "../util/documentation-url";
|
||||
import "./ha-alert";
|
||||
import "./ha-markdown";
|
||||
import "./ha-textfield";
|
||||
import type { HaTextField } from "./ha-textfield";
|
||||
import "./input/ha-input";
|
||||
import type { HaInput } from "./input/ha-input";
|
||||
|
||||
interface AssistMessage {
|
||||
who: string;
|
||||
@@ -57,7 +57,7 @@ export class HaAssistChat extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public startListening?: boolean;
|
||||
|
||||
@query("#message-input") private _messageInput!: HaTextField;
|
||||
@query("#message-input") private _messageInput!: HaInput;
|
||||
|
||||
@query(".message:last-child")
|
||||
private _lastChatMessage!: LitElement;
|
||||
@@ -247,14 +247,13 @@ ${JSON.stringify(toolCall.result, null, 2)}</pre
|
||||
)}
|
||||
</div>
|
||||
<div class="input" slot="primaryAction">
|
||||
<ha-textfield
|
||||
<ha-input
|
||||
id="message-input"
|
||||
@keyup=${this._handleKeyUp}
|
||||
@input=${this._handleInput}
|
||||
.label=${this.hass.localize(`ui.dialogs.voice_command.input_label`)}
|
||||
.iconTrailing=${true}
|
||||
>
|
||||
<div slot="trailingIcon">
|
||||
<div slot="end">
|
||||
${this._showSendButton || !supportsSTT
|
||||
? html`
|
||||
<ha-icon-button
|
||||
@@ -299,7 +298,7 @@ ${JSON.stringify(toolCall.result, null, 2)}</pre
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
</ha-textfield>
|
||||
</ha-input>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -329,7 +328,7 @@ ${JSON.stringify(toolCall.result, null, 2)}</pre
|
||||
}
|
||||
|
||||
private _handleKeyUp(ev: KeyboardEvent) {
|
||||
const input = ev.target as HaTextField;
|
||||
const input = ev.target as HaInput;
|
||||
if (!this._processing && ev.key === "Enter" && input.value) {
|
||||
this._processText(input.value);
|
||||
input.value = "";
|
||||
@@ -338,7 +337,7 @@ ${JSON.stringify(toolCall.result, null, 2)}</pre
|
||||
}
|
||||
|
||||
private _handleInput(ev: InputEvent) {
|
||||
const value = (ev.target as HaTextField).value;
|
||||
const value = (ev.target as HaInput).value;
|
||||
if (value && !this._showSendButton) {
|
||||
this._showSendButton = true;
|
||||
} else if (!value && this._showSendButton) {
|
||||
@@ -728,9 +727,10 @@ ${JSON.stringify(toolCall.result, null, 2)}</pre
|
||||
ha-alert {
|
||||
margin-bottom: var(--ha-space-2);
|
||||
}
|
||||
ha-textfield {
|
||||
display: block;
|
||||
#message-input::part(wa-base) {
|
||||
padding-right: var(--ha-space-1);
|
||||
}
|
||||
|
||||
.messages {
|
||||
flex: 1 1 400px;
|
||||
display: block;
|
||||
@@ -944,20 +944,6 @@ ${JSON.stringify(toolCall.result, null, 2)}</pre
|
||||
}
|
||||
}
|
||||
|
||||
.listening-icon {
|
||||
position: relative;
|
||||
color: var(--secondary-text-color);
|
||||
margin-right: -24px;
|
||||
margin-inline-end: -24px;
|
||||
margin-inline-start: initial;
|
||||
direction: var(--direction);
|
||||
transform: scaleX(var(--scale-direction));
|
||||
}
|
||||
|
||||
.listening-icon[active] {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.unsupported {
|
||||
color: var(--error-color);
|
||||
position: absolute;
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import { mdiContentCopy, mdiEye, mdiEyeOff } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
|
||||
import { copyToClipboard } from "../common/util/copy-clipboard";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { showToast } from "../util/toast";
|
||||
import "./ha-button";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-svg-icon";
|
||||
import "./ha-textfield";
|
||||
import type { HaTextField } from "./ha-textfield";
|
||||
|
||||
@customElement("ha-copy-textfield")
|
||||
export class HaCopyTextfield extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: "value" }) public value!: string;
|
||||
|
||||
@property({ attribute: "masked-value" }) public maskedValue?: string;
|
||||
|
||||
@property({ attribute: "label" }) public label?: string;
|
||||
|
||||
@state() private _showMasked = true;
|
||||
|
||||
public render() {
|
||||
return html`
|
||||
<div class="container">
|
||||
<div class="textfield-container">
|
||||
<ha-textfield
|
||||
.value=${this._showMasked && this.maskedValue
|
||||
? this.maskedValue
|
||||
: this.value}
|
||||
readonly
|
||||
.suffix=${this.maskedValue
|
||||
? html`<div style="width: 24px"></div>`
|
||||
: nothing}
|
||||
@click=${this._focusInput}
|
||||
></ha-textfield>
|
||||
${this.maskedValue
|
||||
? html`<ha-icon-button
|
||||
class="toggle-unmasked"
|
||||
.label=${this.hass.localize(
|
||||
`ui.common.${this._showMasked ? "show" : "hide"}`
|
||||
)}
|
||||
@click=${this._toggleMasked}
|
||||
.path=${this._showMasked ? mdiEye : mdiEyeOff}
|
||||
></ha-icon-button>`
|
||||
: nothing}
|
||||
</div>
|
||||
<ha-button @click=${this._copy} appearance="plain" size="small">
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||
${this.label || this.hass.localize("ui.common.copy")}
|
||||
</ha-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _focusInput(ev) {
|
||||
const inputElement = ev.currentTarget as HaTextField;
|
||||
inputElement.select();
|
||||
}
|
||||
|
||||
private _toggleMasked(): void {
|
||||
this._showMasked = !this._showMasked;
|
||||
}
|
||||
|
||||
private async _copy(): Promise<void> {
|
||||
await copyToClipboard(this.value);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ha-space-2);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.textfield-container {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.textfield-container ha-textfield {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.toggle-unmasked {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
inset-inline-start: initial;
|
||||
inset-inline-end: 8px;
|
||||
--ha-icon-button-size: 40px;
|
||||
--mdc-icon-size: 20px;
|
||||
color: var(--secondary-text-color);
|
||||
direction: var(--direction);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-copy-textfield": HaCopyTextfield;
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ import { fireEvent } from "../common/dom/fire_event";
|
||||
import { TimeZone } from "../data/translation";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-svg-icon";
|
||||
import "./ha-textfield";
|
||||
import type { HaTextField } from "./ha-textfield";
|
||||
import "./input/ha-input";
|
||||
import type { HaInput } from "./input/ha-input";
|
||||
|
||||
const loadDatePickerDialog = () =>
|
||||
import("./date-picker/ha-dialog-date-picker");
|
||||
@@ -54,19 +54,17 @@ export class HaDateInput extends LitElement {
|
||||
|
||||
@property({ attribute: "can-clear", type: Boolean }) public canClear = false;
|
||||
|
||||
@query("ha-textfield", true) private _input?: HaTextField;
|
||||
@query("ha-input", true) private _input?: HaInput;
|
||||
|
||||
public reportValidity(): boolean {
|
||||
return this._input?.reportValidity() ?? true;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<ha-textfield
|
||||
.label=${this.label}
|
||||
.helper=${this.helper}
|
||||
return html`<ha-input
|
||||
.label=${this.label ?? ""}
|
||||
.hint=${this.helper ?? ""}
|
||||
.disabled=${this.disabled}
|
||||
iconTrailing
|
||||
helperPersistent
|
||||
readonly
|
||||
@click=${this._openDialog}
|
||||
@keydown=${this._keyDown}
|
||||
@@ -82,8 +80,8 @@ export class HaDateInput extends LitElement {
|
||||
: ""}
|
||||
.required=${this.required}
|
||||
>
|
||||
<ha-svg-icon slot="trailingIcon" .path=${mdiCalendar}></ha-svg-icon>
|
||||
</ha-textfield>`;
|
||||
<ha-svg-icon slot="end" .path=${mdiCalendar}></ha-svg-icon>
|
||||
</ha-input>`;
|
||||
}
|
||||
|
||||
private _openDialog() {
|
||||
@@ -128,9 +126,6 @@ export class HaDateInput extends LitElement {
|
||||
ha-svg-icon {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
ha-textfield {
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
}
|
||||
declare global {
|
||||
|
||||
134
src/components/input/ha-input-copy.ts
Normal file
134
src/components/input/ha-input-copy.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { mdiContentCopy, mdiEye, mdiEyeOff } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { copyToClipboard } from "../../common/util/copy-clipboard";
|
||||
import { localizeContext } from "../../data/context";
|
||||
import { showToast } from "../../util/toast";
|
||||
import "../ha-button";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-svg-icon";
|
||||
import "./ha-input";
|
||||
import type { HaInput, InputType } from "./ha-input";
|
||||
|
||||
@customElement("ha-input-copy")
|
||||
export class HaInputCopy extends LitElement {
|
||||
@property({ attribute: "value" }) public value!: string;
|
||||
|
||||
@property({ attribute: "masked-value" }) public maskedValue?: string;
|
||||
|
||||
@property({ attribute: "label" }) public label?: string;
|
||||
|
||||
@property({ type: Boolean }) public readonly = false;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "masked-toggle" }) public maskedToggle =
|
||||
false;
|
||||
|
||||
@property() public type: InputType = "text";
|
||||
|
||||
@property()
|
||||
public placeholder = "";
|
||||
|
||||
@property({ attribute: "validation-message" })
|
||||
public validationMessage?: string;
|
||||
|
||||
@property({ type: Boolean, attribute: "auto-validate" }) public autoValidate =
|
||||
false;
|
||||
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: ContextType<typeof localizeContext>;
|
||||
|
||||
@state() private _showMasked = true;
|
||||
|
||||
@query("ha-input", true) private _inputElement?: HaInput;
|
||||
|
||||
public reportValidity(): boolean {
|
||||
return this._inputElement?.reportValidity() ?? false;
|
||||
}
|
||||
|
||||
public render() {
|
||||
return html`
|
||||
<div class="container">
|
||||
<div class="textfield-container">
|
||||
<ha-input
|
||||
.type=${this.type}
|
||||
.value=${this._showMasked && this.maskedValue
|
||||
? this.maskedValue
|
||||
: this.value}
|
||||
.readonly=${this.readonly}
|
||||
.disabled=${this.disabled}
|
||||
@click=${this._focusInput}
|
||||
.placeholder=${this.placeholder}
|
||||
.autoValidate=${this.autoValidate}
|
||||
.validationMessage=${this.validationMessage}
|
||||
>
|
||||
${this.maskedToggle && this.maskedValue
|
||||
? html`<ha-icon-button
|
||||
slot="end"
|
||||
class="toggle-unmasked"
|
||||
.label=${this.localize(
|
||||
`ui.common.${this._showMasked ? "show" : "hide"}`
|
||||
)}
|
||||
@click=${this._toggleMasked}
|
||||
.path=${this._showMasked ? mdiEye : mdiEyeOff}
|
||||
></ha-icon-button>`
|
||||
: nothing}
|
||||
</ha-input>
|
||||
</div>
|
||||
<ha-button @click=${this._copy} appearance="plain" size="small">
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||
${this.label || this.localize("ui.common.copy")}
|
||||
</ha-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _focusInput(ev: Event) {
|
||||
const inputElement = ev.currentTarget as HaInput;
|
||||
inputElement.select();
|
||||
}
|
||||
|
||||
private _toggleMasked(): void {
|
||||
this._showMasked = !this._showMasked;
|
||||
}
|
||||
|
||||
private async _copy(): Promise<void> {
|
||||
await copyToClipboard(this.value);
|
||||
showToast(this, {
|
||||
message: this.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ha-space-2);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.textfield-container {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.toggle-unmasked {
|
||||
--ha-icon-button-size: 40px;
|
||||
--mdc-icon-size: 20px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
ha-button {
|
||||
margin-bottom: var(--ha-space-2);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-input-copy": HaInputCopy;
|
||||
}
|
||||
}
|
||||
@@ -12,20 +12,22 @@ import { stopPropagation } from "../../common/dom/stop_propagation";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-tooltip";
|
||||
|
||||
export type InputType =
|
||||
| "date"
|
||||
| "datetime-local"
|
||||
| "email"
|
||||
| "number"
|
||||
| "password"
|
||||
| "search"
|
||||
| "tel"
|
||||
| "text"
|
||||
| "time"
|
||||
| "url";
|
||||
|
||||
@customElement("ha-input")
|
||||
export class HaInput extends LitElement {
|
||||
@property({ reflect: true })
|
||||
public type:
|
||||
| "date"
|
||||
| "datetime-local"
|
||||
| "email"
|
||||
| "number"
|
||||
| "password"
|
||||
| "search"
|
||||
| "tel"
|
||||
| "text"
|
||||
| "time"
|
||||
| "url" = "text";
|
||||
public type: InputType = "text";
|
||||
|
||||
@property()
|
||||
public value?: string;
|
||||
|
||||
@@ -10,8 +10,8 @@ import "../../../../components/ha-md-list-item";
|
||||
import "../../../../components/ha-switch";
|
||||
|
||||
import { formatDate } from "../../../../common/datetime/format_date";
|
||||
import "../../../../components/ha-copy-textfield";
|
||||
import type { HaSwitch } from "../../../../components/ha-switch";
|
||||
import "../../../../components/input/ha-input-copy";
|
||||
import type { CloudStatusLoggedIn } from "../../../../data/cloud";
|
||||
import {
|
||||
connectCloudRemote,
|
||||
@@ -130,12 +130,12 @@ export class CloudRemotePref extends LitElement {
|
||||
</p>
|
||||
`}
|
||||
|
||||
<ha-copy-textfield
|
||||
.hass=${this.hass}
|
||||
<ha-input-copy
|
||||
readonly
|
||||
.value=${`https://${remote_domain}`}
|
||||
.maskedValue=${obfuscateUrl(`https://${remote_domain}`)}
|
||||
.label=${this.hass!.localize("ui.panel.config.common.copy_link")}
|
||||
></ha-copy-textfield>
|
||||
></ha-input-copy>
|
||||
|
||||
<ha-expansion-panel
|
||||
outlined
|
||||
|
||||
@@ -11,8 +11,8 @@ import { documentationUrl } from "../../../../util/documentation-url";
|
||||
import type { WebhookDialogParams } from "./show-dialog-manage-cloudhook";
|
||||
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-copy-textfield";
|
||||
import "../../../../components/ha-dialog";
|
||||
import "../../../../components/input/ha-input-copy";
|
||||
|
||||
@customElement("dialog-manage-cloudhook")
|
||||
export class DialogManageCloudhook extends LitElement {
|
||||
@@ -79,11 +79,11 @@ export class DialogManageCloudhook extends LitElement {
|
||||
`}
|
||||
</p>
|
||||
|
||||
<ha-copy-textfield
|
||||
.hass=${this.hass}
|
||||
<ha-input-copy
|
||||
readonly
|
||||
.value=${cloudhook.cloudhook_url}
|
||||
.label=${this.hass!.localize("ui.panel.config.common.copy_link")}
|
||||
></ha-copy-textfield>
|
||||
></ha-input-copy>
|
||||
</div>
|
||||
|
||||
<ha-dialog-footer slot="footer">
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
import { mdiContentCopy, mdiEye, mdiEyeOff } from "@mdi/js";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { isIPAddress } from "../../../common/string/is_ip_address";
|
||||
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-md-list-item";
|
||||
import "../../../components/ha-switch";
|
||||
import type { HaSwitch } from "../../../components/ha-switch";
|
||||
import "../../../components/input/ha-input";
|
||||
import "../../../components/ha-textfield";
|
||||
import type { HaInput } from "../../../components/input/ha-input";
|
||||
import "../../../components/input/ha-input-copy";
|
||||
import type { HaInputCopy } from "../../../components/input/ha-input-copy";
|
||||
import type { CloudStatus } from "../../../data/cloud";
|
||||
import { fetchCloudStatus } from "../../../data/cloud";
|
||||
import { saveCoreConfig } from "../../../data/core";
|
||||
import { getNetworkUrls, type NetworkUrls } from "../../../data/network";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../../../types";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { obfuscateUrl } from "../../../util/url";
|
||||
|
||||
@customElement("ha-config-url-form")
|
||||
@@ -42,16 +41,12 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
|
||||
|
||||
@state() private _showCustomInternalUrl = false;
|
||||
|
||||
@state() private _unmaskedExternalUrl = false;
|
||||
|
||||
@state() private _unmaskedInternalUrl = false;
|
||||
|
||||
@state() private _cloudChecked = false;
|
||||
|
||||
@query('[name="external_url"]')
|
||||
@query('[data-name="external_url"]')
|
||||
private _externalUrlField?: HaInput;
|
||||
|
||||
@query('[name="internal_url"]')
|
||||
@query('[data-name="internal_url"]')
|
||||
private _internalUrlField?: HaInput;
|
||||
|
||||
protected hassSubscribe() {
|
||||
@@ -76,6 +71,7 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
|
||||
const internalUrl = this._showCustomInternalUrl
|
||||
? this._internal_url
|
||||
: this._urls?.internal || "";
|
||||
|
||||
const externalUrl = this._showCustomExternalUrl
|
||||
? this._external_url
|
||||
: (this._cloudChecked ? this._urls?.cloud : this._urls?.external) || "";
|
||||
@@ -148,45 +144,24 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
|
||||
`
|
||||
: nothing}
|
||||
<div class="url-container">
|
||||
<div class="textfield-container">
|
||||
<ha-input
|
||||
name="external_url"
|
||||
type="url"
|
||||
auto-validate
|
||||
.validationMessage=${this.hass.localize(
|
||||
"ui.panel.config.url.invalid_url"
|
||||
)}
|
||||
placeholder="https://example.duckdns.org:8123"
|
||||
.value=${this._unmaskedExternalUrl ||
|
||||
(this._showCustomExternalUrl && canEdit)
|
||||
? externalUrl
|
||||
: obfuscateUrl(externalUrl)}
|
||||
@change=${this._handleChange}
|
||||
.disabled=${disabled || !this._showCustomExternalUrl}
|
||||
>
|
||||
${!this._showCustomExternalUrl || !canEdit
|
||||
? html`
|
||||
<ha-icon-button
|
||||
slot="end"
|
||||
.label=${this.hass.localize(
|
||||
`ui.panel.config.common.${this._unmaskedExternalUrl ? "hide" : "show"}_url`
|
||||
)}
|
||||
@click=${this._toggleUnmaskedExternalUrl}
|
||||
.path=${this._unmaskedExternalUrl ? mdiEyeOff : mdiEye}
|
||||
></ha-icon-button>
|
||||
`
|
||||
: nothing}
|
||||
</ha-input>
|
||||
</div>
|
||||
<ha-button
|
||||
size="small"
|
||||
appearance="plain"
|
||||
.url=${externalUrl}
|
||||
@click=${this._copyURL}
|
||||
<ha-input-copy
|
||||
auto-validate
|
||||
.validationMessage=${this.hass.localize(
|
||||
"ui.panel.config.url.invalid_url"
|
||||
)}
|
||||
data-name="external_url"
|
||||
type="url"
|
||||
.maskedToggle=${!(this._showCustomExternalUrl && canEdit)}
|
||||
placeholder="https://example.duckdns.org:8123"
|
||||
.value=${externalUrl}
|
||||
.maskedValue=${this._showCustomExternalUrl && canEdit
|
||||
? undefined
|
||||
: obfuscateUrl(externalUrl)}
|
||||
@change=${this._handleChange}
|
||||
.readonly=${!this._showCustomExternalUrl}
|
||||
.disabled=${disabled}
|
||||
>
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||
${this.hass.localize("ui.panel.config.common.copy_link")}
|
||||
</ha-button>
|
||||
</ha-input-copy>
|
||||
</div>
|
||||
${hasCloud || !isComponentLoaded(this.hass, "cloud")
|
||||
? nothing
|
||||
@@ -264,47 +239,26 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
|
||||
</ha-md-list-item>
|
||||
|
||||
<div class="url-container">
|
||||
<div class="textfield-container">
|
||||
<ha-input
|
||||
name="internal_url"
|
||||
type="url"
|
||||
auto-validate
|
||||
.validationMessage=${this.hass.localize(
|
||||
"ui.panel.config.url.invalid_url"
|
||||
)}
|
||||
placeholder=${this.hass.localize(
|
||||
"ui.panel.config.url.internal_url_placeholder"
|
||||
)}
|
||||
.value=${this._unmaskedInternalUrl ||
|
||||
(this._showCustomInternalUrl && canEdit)
|
||||
? internalUrl
|
||||
: obfuscateUrl(internalUrl)}
|
||||
@change=${this._handleChange}
|
||||
.disabled=${disabled || !this._showCustomInternalUrl}
|
||||
>
|
||||
${!this._showCustomInternalUrl || !canEdit
|
||||
? html`
|
||||
<ha-icon-button
|
||||
slot="end"
|
||||
.label=${this.hass.localize(
|
||||
`ui.panel.config.common.${this._unmaskedInternalUrl ? "hide" : "show"}_url`
|
||||
)}
|
||||
@click=${this._toggleUnmaskedInternalUrl}
|
||||
.path=${this._unmaskedInternalUrl ? mdiEyeOff : mdiEye}
|
||||
></ha-icon-button>
|
||||
`
|
||||
: nothing}
|
||||
</ha-input>
|
||||
</div>
|
||||
<ha-button
|
||||
size="small"
|
||||
appearance="plain"
|
||||
.url=${internalUrl}
|
||||
@click=${this._copyURL}
|
||||
<ha-input-copy
|
||||
auto-validate
|
||||
.validationMessage=${this.hass.localize(
|
||||
"ui.panel.config.url.invalid_url"
|
||||
)}
|
||||
data-name="internal_url"
|
||||
.maskedToggle=${!(this._showCustomInternalUrl && canEdit)}
|
||||
type="url"
|
||||
placeholder=${this.hass.localize(
|
||||
"ui.panel.config.url.internal_url_placeholder"
|
||||
)}
|
||||
.value=${internalUrl}
|
||||
.maskedValue=${this._showCustomInternalUrl && canEdit
|
||||
? undefined
|
||||
: obfuscateUrl(internalUrl)}
|
||||
@change=${this._handleChange}
|
||||
.readonly=${!this._showCustomInternalUrl}
|
||||
.disabled=${disabled}
|
||||
>
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||
${this.hass.localize("ui.panel.config.common.copy_link")}
|
||||
</ha-button>
|
||||
</ha-input-copy>
|
||||
</div>
|
||||
${
|
||||
// If the user has configured a cert, show an error if
|
||||
@@ -367,25 +321,10 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
|
||||
this._showCustomInternalUrl = !(ev.currentTarget as HaSwitch).checked;
|
||||
}
|
||||
|
||||
private _toggleUnmaskedInternalUrl() {
|
||||
this._unmaskedInternalUrl = !this._unmaskedInternalUrl;
|
||||
}
|
||||
|
||||
private _toggleUnmaskedExternalUrl() {
|
||||
this._unmaskedExternalUrl = !this._unmaskedExternalUrl;
|
||||
}
|
||||
|
||||
private async _copyURL(ev) {
|
||||
const url = ev.currentTarget.url;
|
||||
await copyToClipboard(url);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
}
|
||||
|
||||
private _handleChange(ev: ValueChangedEvent<string>) {
|
||||
const target = ev.currentTarget as HaInput;
|
||||
this[`_${target.name}`] = target.value || "";
|
||||
private _handleChange(ev: InputEvent) {
|
||||
const target = ev.currentTarget as HaInputCopy;
|
||||
const input = ev.composedPath()[0] as HaInput;
|
||||
this[`_${target.dataset.name}`] = input.value || "";
|
||||
}
|
||||
|
||||
private async _save() {
|
||||
@@ -471,12 +410,10 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
|
||||
gap: var(--ha-space-2);
|
||||
margin-top: 8px;
|
||||
}
|
||||
.textfield-container {
|
||||
|
||||
ha-input-copy {
|
||||
flex: 1;
|
||||
}
|
||||
.textfield-container ha-input {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ha-md-list-item {
|
||||
--md-list-item-top-space: 0;
|
||||
|
||||
Reference in New Issue
Block a user