diff --git a/src/components/ha-copy-textfield.ts b/src/components/ha-copy-textfield.ts new file mode 100644 index 0000000000..46bbb3cf72 --- /dev/null +++ b/src/components/ha-copy-textfield.ts @@ -0,0 +1,111 @@ +import { customElement, property, state } from "lit/decorators"; +import { css, html, LitElement, nothing } from "lit"; +import { mdiContentCopy, mdiEye, mdiEyeOff } from "@mdi/js"; + +import "./ha-button"; +import "./ha-icon-button"; +import "./ha-svg-icon"; +import "./ha-textfield"; +import type { HomeAssistant } from "../types"; +import { copyToClipboard } from "../common/util/copy-clipboard"; +import { showToast } from "../util/toast"; +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` +
+
+
` + : nothing} + @click=${this._focusInput} + > + ${this.maskedValue + ? html`` + : nothing} +
+ + + ${this.label || this.hass.localize("ui.common.copy")} + + + `; + } + + private _focusInput(ev) { + const inputElement = ev.currentTarget as HaTextField; + inputElement.select(); + } + + private _toggleMasked(): void { + this._showMasked = !this._showMasked; + } + + private async _copy(): Promise { + await copyToClipboard(this.value); + showToast(this, { + message: this.hass.localize("ui.common.copied_clipboard"), + }); + } + + static styles = css` + .container { + display: flex; + align-items: center; + gap: 8px; + 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; + --mdc-icon-button-size: 40px; + --mdc-icon-size: 20px; + color: var(--secondary-text-color); + direction: var(--direction); + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-copy-textfield": HaCopyTextfield; + } +} diff --git a/src/panels/config/cloud/account/cloud-remote-pref.ts b/src/panels/config/cloud/account/cloud-remote-pref.ts index 60ddbd50a0..d7ff6a8049 100644 --- a/src/panels/config/cloud/account/cloud-remote-pref.ts +++ b/src/panels/config/cloud/account/cloud-remote-pref.ts @@ -1,17 +1,13 @@ -import { mdiContentCopy, mdiEye, mdiEyeOff, mdiHelpCircle } from "@mdi/js"; +import { mdiHelpCircle } from "@mdi/js"; import { LitElement, css, html, nothing } from "lit"; -import { customElement, property, state } from "lit/decorators"; +import { customElement, property } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { copyToClipboard } from "../../../../common/util/copy-clipboard"; import "../../../../components/ha-alert"; import "../../../../components/ha-button"; import "../../../../components/ha-card"; import "../../../../components/ha-expansion-panel"; -import "../../../../components/ha-formfield"; -import "../../../../components/ha-radio"; import "../../../../components/ha-settings-row"; import "../../../../components/ha-switch"; -import "../../../../components/ha-textfield"; import { formatDate } from "../../../../common/datetime/format_date"; import type { HaSwitch } from "../../../../components/ha-switch"; @@ -25,6 +21,7 @@ import type { HomeAssistant } from "../../../../types"; import { showToast } from "../../../../util/toast"; import { showCloudCertificateDialog } from "../dialog-cloud-certificate/show-dialog-cloud-certificate"; import { obfuscateUrl } from "../../../../util/url"; +import "../../../../components/ha-copy-textfield"; @customElement("cloud-remote-pref") export class CloudRemotePref extends LitElement { @@ -34,8 +31,6 @@ export class CloudRemotePref extends LitElement { @property({ type: Boolean }) public narrow = false; - @state() private _unmaskedUrl = false; - protected render() { if (!this.cloudStatus) { return nothing; @@ -139,37 +134,13 @@ export class CloudRemotePref extends LitElement { )}

`} -
-
-
` - } - > - -
- - - ${this.hass.localize("ui.panel.config.common.copy_link")} - - + + { - const url = ev.currentTarget.url; - await copyToClipboard(url); - showToast(this, { - message: this.hass.localize("ui.common.copied_clipboard"), - }); - } - static styles = css` .preparing { padding: 0 16px 16px; @@ -335,30 +294,6 @@ export class CloudRemotePref extends LitElement { display: block; margin-bottom: 16px; } - .url-container { - display: flex; - align-items: center; - gap: 8px; - margin-top: 8px; - } - .textfield-container { - position: relative; - flex: 1; - } - .textfield-container ha-textfield { - display: block; - } - .toggle-unmasked-url { - position: absolute; - top: 8px; - right: 8px; - inset-inline-start: initial; - inset-inline-end: 8px; - --mdc-icon-button-size: 40px; - --mdc-icon-size: 20px; - color: var(--secondary-text-color); - direction: var(--direction); - } hr { border: none; height: 1px; diff --git a/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts b/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts index b1c313fe6b..909866095e 100644 --- a/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts +++ b/src/panels/config/cloud/dialog-manage-cloudhook/dialog-manage-cloudhook.ts @@ -1,27 +1,23 @@ import "@material/mwc-button"; -import { mdiContentCopy, mdiOpenInNew } from "@mdi/js"; +import { mdiOpenInNew } from "@mdi/js"; import type { CSSResultGroup } from "lit"; import { css, html, LitElement, nothing } from "lit"; -import { query, state } from "lit/decorators"; +import { state } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; -import { copyToClipboard } from "../../../../common/util/copy-clipboard"; import { createCloseHeading } from "../../../../components/ha-dialog"; -import "../../../../components/ha-textfield"; -import type { HaTextField } from "../../../../components/ha-textfield"; import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; import { haStyle, haStyleDialog } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; import { documentationUrl } from "../../../../util/documentation-url"; -import { showToast } from "../../../../util/toast"; import type { WebhookDialogParams } from "./show-dialog-manage-cloudhook"; +import "../../../../components/ha-copy-textfield"; + export class DialogManageCloudhook extends LitElement { protected hass?: HomeAssistant; @state() private _params?: WebhookDialogParams; - @query("ha-textfield") _input!: HaTextField; - public showDialog(params: WebhookDialogParams) { this._params = params; } @@ -82,21 +78,12 @@ export class DialogManageCloudhook extends LitElement {

- - - + .label=${this.hass!.localize("ui.panel.config.common.copy_link")} + > { - if (!this.hass) return; - ev.stopPropagation(); - const inputElement = ev.target.parentElement as HaTextField; - inputElement.select(); - const url = this.hass.hassUrl(inputElement.value); - - await copyToClipboard(url); - showToast(this, { - message: this.hass.localize("ui.common.copied_clipboard"), - }); - } - static get styles(): CSSResultGroup { return [ haStyle, @@ -163,13 +132,6 @@ export class DialogManageCloudhook extends LitElement { ha-dialog { width: 650px; } - ha-textfield { - display: block; - } - ha-textfield > ha-icon-button { - --mdc-icon-button-size: 24px; - --mdc-icon-size: 18px; - } button.link { color: var(--primary-color); text-decoration: none; diff --git a/src/translations/en.json b/src/translations/en.json index fd3146e78a..e04de83107 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -370,7 +370,10 @@ "name": "Name", "optional": "optional", "default": "Default", - "dont_save": "Don't save" + "dont_save": "Don't save", + "copy": "Copy", + "show": "Show", + "hide": "Hide" }, "components": { "selectors": {