Add show/hide button for password inputs #566 (#664)

* Added visibility toggle for password cell

* Formatted code

* Moved password toggle to separate component

* Adjusted to review

* Added password toggle for add filesystem component

* Update lib/livebook_web/helpers.ex

Co-authored-by: Jonatan Kłosko <jonatanklosko@gmail.com>
This commit is contained in:
Jakub Perżyło 2021-11-01 16:04:11 +01:00 committed by GitHub
parent 5f58fb902c
commit ace64eab37
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 95 additions and 5 deletions

View file

@ -25,6 +25,7 @@ import MarkdownRenderer from "./markdown_renderer";
import Highlight from "./highlight";
import ClipCopy from "./clip_copy";
import DragAndDrop from "./darg_and_drop";
import PasswordToggle from "./password_toggle";
import morphdomCallbacks from "./morphdom_callbacks";
import { loadUserData } from "./lib/user";
@ -43,6 +44,7 @@ const hooks = {
Highlight,
ClipCopy,
DragAndDrop,
PasswordToggle,
};
const csrfToken = document

View file

@ -0,0 +1,38 @@
/**
* A hook used to toggle password's input visibility via icon button.
*/
const VISIBLE_ICON = "ri-eye-off-line";
const OBSCURED_ICON = "ri-eye-line";
const PasswordToggle = {
mounted() {
this.visible = false;
this.input = this.el.querySelector("input");
this.iconButton = this.el.querySelector("i");
this.iconButton.addEventListener("click", () => {
this.visible = !this.visible;
this._updateDOM();
});
},
updated() {
this._updateDOM();
},
_updateDOM() {
if (this.visible) {
this.input.type = "text";
this.iconButton.classList.remove(OBSCURED_ICON);
this.iconButton.classList.add(VISIBLE_ICON);
} else {
this.input.type = "password";
this.iconButton.classList.remove(VISIBLE_ICON);
this.iconButton.classList.add(OBSCURED_ICON);
}
},
};
export default PasswordToggle;

View file

@ -246,6 +246,35 @@ defmodule LivebookWeb.Helpers do
"""
end
@doc """
Renders a wrapper around password input
with an added visibility toggle button.
The toggle switches the input's type between `password`
and `text`.
## Examples
<.with_password_toggle id="input-id">
<input type="password" ...>
</.with_password_toggle>
"""
def with_password_toggle(assigns) do
~H"""
<div id={"password-toggle-#{@id}"} class="relative inline w-min" phx-hook="PasswordToggle">
<!-- render password input -->
<%= render_block(@inner_block) %>
<button
class="bg-gray-50 p-1 icon-button absolute inset-y-0 right-1"
type="button"
aria-label="toggle password visibility"
phx-change="ignore">
<.remix_icon icon="eye-line" class="text-xl" />
</button>
</div>
"""
end
defdelegate ansi_string_to_html(string), to: LivebookWeb.Helpers.ANSI
defdelegate ansi_string_to_html_lines(string), to: LivebookWeb.Helpers.ANSI

View file

@ -1,6 +1,7 @@
defmodule LivebookWeb.SessionLive.CellComponent do
use LivebookWeb, :live_component
@impl true
def render(assigns) do
~H"""
<div class="flex flex-col relative"
@ -15,7 +16,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
"""
end
def render_cell(%{cell_view: %{type: :markdown}} = assigns) do
defp render_cell(%{cell_view: %{type: :markdown}} = assigns) do
~H"""
<div class="mb-1 flex items-center justify-end">
<div class="relative z-20 flex items-center justify-end space-x-2" data-element="actions">
@ -51,7 +52,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
"""
end
def render_cell(%{cell_view: %{type: :elixir}} = assigns) do
defp render_cell(%{cell_view: %{type: :elixir}} = assigns) do
~H"""
<div class="mb-1 flex items-center justify-between">
<div class="relative z-20 flex items-center justify-end space-x-2" data-element="actions" data-primary>
@ -106,7 +107,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
"""
end
def render_cell(%{cell_view: %{type: :input}} = assigns) do
defp render_cell(%{cell_view: %{type: :input}} = assigns) do
~H"""
<div class="mb-1 flex items-center justify-end">
<div class="relative z-20 flex items-center justify-end space-x-2" data-element="actions">
@ -200,6 +201,22 @@ defmodule LivebookWeb.SessionLive.CellComponent do
"""
end
defp cell_input(%{cell_view: %{input_type: :password}} = assigns) do
~H"""
<.with_password_toggle id={@cell_view.id}>
<input type="password"
data-element="input"
class={"input w-auto bg-gray-50 #{if(@cell_view.error, do: "input--error")}"}
name="value"
value={@cell_view.value}
phx-debounce="300"
spellcheck="false"
autocomplete="off"
tabindex="-1" />
</.with_password_toggle>
"""
end
defp cell_input(assigns) do
~H"""
<input type={html_input_type(@cell_view.input_type)}

View file

@ -46,11 +46,15 @@ defmodule LivebookWeb.SettingsLive.AddFileSystemComponent do
</div>
<div>
<div class="input-label">Access Key ID</div>
<%= text_input f, :access_key_id, value: @data["access_key_id"], class: "input", type: "password" %>
<.with_password_toggle id="access-key">
<%= text_input f, :access_key_id, value: @data["access_key_id"], class: "input", type: "password" %>
</.with_password_toggle>
</div>
<div>
<div class="input-label">Secret Access Key</div>
<%= text_input f, :secret_access_key, value: @data["secret_access_key"], class: "input", type: "password" %>
<.with_password_toggle id="secret-access-key">
<%= text_input f, :secret_access_key, value: @data["secret_access_key"], class: "input", type: "password" %>
</.with_password_toggle>
</div>
</div>
<div class="mt-5 flex justify-end space-x-2">